From a0186bdc7b393c10855861e414d4f0160902ece3 Mon Sep 17 00:00:00 2001 From: Michael Goderbauer Date: Wed, 18 Dec 2024 21:45:43 -0800 Subject: [PATCH] formatted --- ci/bin/format.dart | 419 +- ci/test/format_test.dart | 138 +- examples/glfw/main.dart | 17 +- examples/glfw_drm/main.dart | 8 +- .../test/to_string_test.dart | 69 +- impeller/fixtures/dart_tests.dart | 41 +- .../tessellator/dart/lib/tessellator.dart | 119 +- lib/gpu/lib/src/buffer.dart | 137 +- lib/gpu/lib/src/command_buffer.dart | 6 +- lib/gpu/lib/src/context.dart | 78 +- lib/gpu/lib/src/formats.dart | 62 +- lib/gpu/lib/src/render_pass.dart | 429 +- lib/gpu/lib/src/render_pipeline.dart | 16 +- lib/gpu/lib/src/shader.dart | 10 +- lib/gpu/lib/src/shader_library.dart | 10 +- lib/gpu/lib/src/texture.dart | 101 +- lib/ui/channel_buffers.dart | 92 +- lib/ui/compositing.dart | 238 +- lib/ui/fixtures/ui_test.dart | 241 +- lib/ui/geometry.dart | 346 +- lib/ui/hooks.dart | 94 +- lib/ui/key.dart | 92 +- lib/ui/natives.dart | 20 +- lib/ui/painting.dart | 1402 +- lib/ui/platform_dispatcher.dart | 380 +- lib/ui/platform_isolate.dart | 46 +- lib/ui/plugins.dart | 12 +- lib/ui/pointer.dart | 78 +- lib/ui/semantics.dart | 301 +- lib/ui/setup_hooks.dart | 10 +- lib/ui/text.dart | 656 +- lib/ui/ui.dart | 8 +- lib/ui/window.dart | 72 +- lib/web_ui/dev/analyze.dart | 23 +- lib/web_ui/dev/browser.dart | 5 +- lib/web_ui/dev/browser_process.dart | 122 +- lib/web_ui/dev/build.dart | 60 +- lib/web_ui/dev/chrome.dart | 329 +- lib/web_ui/dev/chrome_installer.dart | 96 +- lib/web_ui/dev/cipd.dart | 12 +- lib/web_ui/dev/clean.dart | 10 +- lib/web_ui/dev/common.dart | 41 +- lib/web_ui/dev/edge.dart | 44 +- lib/web_ui/dev/edge_installation.dart | 34 +- lib/web_ui/dev/environment.dart | 102 +- lib/web_ui/dev/exceptions.dart | 2 +- lib/web_ui/dev/felt.dart | 30 +- lib/web_ui/dev/felt_config.dart | 74 +- lib/web_ui/dev/firefox.dart | 90 +- lib/web_ui/dev/firefox_installer.dart | 128 +- lib/web_ui/dev/generate_builder_json.dart | 118 +- lib/web_ui/dev/licenses.dart | 36 +- lib/web_ui/dev/package_lock.dart | 22 +- lib/web_ui/dev/package_roller.dart | 119 +- lib/web_ui/dev/pipeline.dart | 9 +- lib/web_ui/dev/roll_fallback_fonts.dart | 132 +- lib/web_ui/dev/safari_macos.dart | 17 +- lib/web_ui/dev/steps/compile_bundle_step.dart | 155 +- lib/web_ui/dev/steps/copy_artifacts_step.dart | 217 +- lib/web_ui/dev/steps/run_suite_step.dart | 43 +- lib/web_ui/dev/suite_filter.dart | 43 +- lib/web_ui/dev/test_platform.dart | 386 +- lib/web_ui/dev/test_runner.dart | 226 +- lib/web_ui/dev/utils.dart | 74 +- lib/web_ui/dev/webdriver_browser.dart | 28 +- lib/web_ui/lib/canvas.dart | 57 +- lib/web_ui/lib/channel_buffers.dart | 60 +- lib/web_ui/lib/compositing.dart | 19 +- lib/web_ui/lib/geometry.dart | 371 +- lib/web_ui/lib/initialization.dart | 10 +- lib/web_ui/lib/key.dart | 84 +- lib/web_ui/lib/natives.dart | 3 +- lib/web_ui/lib/painting.dart | 294 +- lib/web_ui/lib/path.dart | 2 +- lib/web_ui/lib/platform_dispatcher.dart | 138 +- lib/web_ui/lib/platform_isolate.dart | 3 +- lib/web_ui/lib/pointer.dart | 89 +- lib/web_ui/lib/semantics.dart | 190 +- lib/web_ui/lib/src/engine/alarm_clock.dart | 10 +- lib/web_ui/lib/src/engine/app_bootstrap.dart | 19 +- .../lib/src/engine/browser_detection.dart | 11 +- lib/web_ui/lib/src/engine/canvas_pool.dart | 147 +- .../lib/src/engine/canvaskit/canvas.dart | 110 +- .../src/engine/canvaskit/canvaskit_api.dart | 847 +- .../engine/canvaskit/canvaskit_canvas.dart | 100 +- .../src/engine/canvaskit/color_filter.dart | 52 +- .../canvaskit/display_canvas_factory.dart | 7 +- .../src/engine/canvaskit/embedded_views.dart | 210 +- .../lib/src/engine/canvaskit/fonts.dart | 40 +- .../lib/src/engine/canvaskit/image.dart | 145 +- .../src/engine/canvaskit/image_filter.dart | 119 +- .../engine/canvaskit/image_wasm_codecs.dart | 18 +- .../engine/canvaskit/image_web_codecs.dart | 37 +- .../lib/src/engine/canvaskit/layer.dart | 45 +- .../engine/canvaskit/layer_scene_builder.dart | 41 +- .../lib/src/engine/canvaskit/layer_tree.dart | 19 +- .../src/engine/canvaskit/layer_visitor.dart | 89 +- .../lib/src/engine/canvaskit/mask_filter.dart | 6 +- .../canvaskit/multi_surface_rasterizer.dart | 18 +- .../src/engine/canvaskit/n_way_canvas.dart | 3 +- .../src/engine/canvaskit/native_memory.dart | 22 +- .../offscreen_canvas_rasterizer.dart | 14 +- .../canvaskit/overlay_scene_optimizer.dart | 37 +- .../lib/src/engine/canvaskit/painting.dart | 49 +- lib/web_ui/lib/src/engine/canvaskit/path.dart | 74 +- .../src/engine/canvaskit/path_metrics.dart | 30 +- .../lib/src/engine/canvaskit/picture.dart | 9 +- .../src/engine/canvaskit/raster_cache.dart | 6 +- .../lib/src/engine/canvaskit/rasterizer.dart | 16 +- .../src/engine/canvaskit/render_canvas.dart | 9 +- .../lib/src/engine/canvaskit/renderer.dart | 491 +- .../lib/src/engine/canvaskit/shader.dart | 49 +- .../lib/src/engine/canvaskit/surface.dart | 132 +- lib/web_ui/lib/src/engine/canvaskit/text.dart | 257 +- .../src/engine/canvaskit/text_fragmenter.dart | 48 +- lib/web_ui/lib/src/engine/canvaskit/util.dart | 30 +- .../lib/src/engine/canvaskit/vertices.dart | 32 +- lib/web_ui/lib/src/engine/clipboard.dart | 124 +- lib/web_ui/lib/src/engine/color_filter.dart | 29 +- lib/web_ui/lib/src/engine/configuration.dart | 69 +- lib/web_ui/lib/src/engine/display.dart | 12 +- lib/web_ui/lib/src/engine/dom.dart | 528 +- lib/web_ui/lib/src/engine/engine_canvas.dart | 63 +- .../lib/src/engine/font_change_util.dart | 6 +- .../lib/src/engine/font_fallback_data.dart | 62566 ++++++++-------- lib/web_ui/lib/src/engine/font_fallbacks.dart | 79 +- lib/web_ui/lib/src/engine/fonts.dart | 64 +- .../lib/src/engine/frame_timing_recorder.dart | 7 +- .../lib/src/engine/html/backdrop_filter.dart | 19 +- .../lib/src/engine/html/bitmap_canvas.dart | 395 +- lib/web_ui/lib/src/engine/html/canvas.dart | 68 +- lib/web_ui/lib/src/engine/html/clip.dart | 15 +- .../lib/src/engine/html/color_filter.dart | 144 +- .../html/debug_canvas_reuse_overlay.dart | 15 +- .../lib/src/engine/html/dom_canvas.dart | 64 +- lib/web_ui/lib/src/engine/html/image.dart | 16 +- .../lib/src/engine/html/image_filter.dart | 10 +- lib/web_ui/lib/src/engine/html/offset.dart | 3 +- lib/web_ui/lib/src/engine/html/opacity.dart | 7 +- .../lib/src/engine/html/path/conic.dart | 55 +- .../lib/src/engine/html/path/cubic.dart | 41 +- lib/web_ui/lib/src/engine/html/path/path.dart | 212 +- .../src/engine/html/path/path_iterator.dart | 12 +- .../src/engine/html/path/path_metrics.dart | 335 +- .../lib/src/engine/html/path/path_ref.dart | 115 +- .../lib/src/engine/html/path/path_to_svg.dart | 29 +- .../lib/src/engine/html/path/path_utils.dart | 51 +- .../src/engine/html/path/path_windings.dart | 47 +- .../lib/src/engine/html/path/tangent.dart | 56 +- .../lib/src/engine/html/path_to_svg_clip.dart | 26 +- lib/web_ui/lib/src/engine/html/picture.dart | 163 +- .../lib/src/engine/html/recording_canvas.dart | 366 +- .../lib/src/engine/html/render_vertices.dart | 265 +- lib/web_ui/lib/src/engine/html/renderer.dart | 393 +- lib/web_ui/lib/src/engine/html/scene.dart | 7 +- .../lib/src/engine/html/scene_builder.dart | 103 +- .../lib/src/engine/html/shader_mask.dart | 138 +- .../src/engine/html/shaders/image_shader.dart | 100 +- .../html/shaders/normalized_gradient.dart | 80 +- .../lib/src/engine/html/shaders/shader.dart | 433 +- .../engine/html/shaders/shader_builder.dart | 84 +- .../engine/html/shaders/vertex_shaders.dart | 30 +- lib/web_ui/lib/src/engine/html/surface.dart | 228 +- .../lib/src/engine/html/surface_stats.dart | 39 +- lib/web_ui/lib/src/engine/html/transform.dart | 3 +- .../src/engine/html_image_element_codec.dart | 21 +- lib/web_ui/lib/src/engine/image_decoder.dart | 98 +- .../lib/src/engine/image_format_detector.dart | 83 +- lib/web_ui/lib/src/engine/initialization.dart | 41 +- .../lib/src/engine/js_interop/js_app.dart | 16 +- .../lib/src/engine/js_interop/js_loader.dart | 18 +- .../lib/src/engine/js_interop/js_promise.dart | 36 +- .../src/engine/js_interop/js_typed_data.dart | 21 +- lib/web_ui/lib/src/engine/key_map.g.dart | 28 +- .../lib/src/engine/keyboard_binding.dart | 127 +- lib/web_ui/lib/src/engine/layers.dart | 142 +- .../lib/src/engine/navigation/history.dart | 52 +- .../lib/src/engine/onscreen_logging.dart | 17 +- .../lib/src/engine/platform_dispatcher.dart | 258 +- .../app_lifecycle_state.dart | 17 +- .../view_focus_binding.dart | 9 +- .../platform_views/content_manager.dart | 45 +- .../platform_views/message_handler.dart | 41 +- .../lib/src/engine/platform_views/slots.dart | 8 +- .../lib/src/engine/pointer_binding.dart | 204 +- .../event_position_helper.dart | 13 +- .../lib/src/engine/pointer_converter.dart | 45 +- lib/web_ui/lib/src/engine/profiler.dart | 40 +- lib/web_ui/lib/src/engine/raw_keyboard.dart | 21 +- lib/web_ui/lib/src/engine/rrect_renderer.dart | 93 +- .../lib/src/engine/safe_browser_api.dart | 393 +- lib/web_ui/lib/src/engine/scene_builder.dart | 124 +- lib/web_ui/lib/src/engine/scene_view.dart | 34 +- .../src/engine/semantics/accessibility.dart | 17 +- .../lib/src/engine/semantics/checkable.dart | 15 +- .../lib/src/engine/semantics/focusable.dart | 29 +- .../lib/src/engine/semantics/header.dart | 35 +- .../lib/src/engine/semantics/heading.dart | 3 +- .../lib/src/engine/semantics/image.dart | 2 +- .../src/engine/semantics/incrementable.dart | 50 +- .../src/engine/semantics/label_and_value.dart | 66 +- lib/web_ui/lib/src/engine/semantics/link.dart | 11 +- .../lib/src/engine/semantics/live_region.dart | 8 +- .../src/engine/semantics/platform_view.dart | 15 +- .../lib/src/engine/semantics/route.dart | 10 +- .../lib/src/engine/semantics/scrollable.dart | 43 +- .../lib/src/engine/semantics/semantics.dart | 164 +- .../engine/semantics/semantics_helper.dart | 34 +- .../lib/src/engine/semantics/tappable.dart | 21 +- .../lib/src/engine/semantics/text_field.dart | 78 +- .../lib/src/engine/services/buffers.dart | 7 +- .../src/engine/services/message_codec.dart | 9 +- .../src/engine/services/message_codecs.dart | 9 +- .../src/engine/services/serialization.dart | 21 +- lib/web_ui/lib/src/engine/shader_data.dart | 25 +- lib/web_ui/lib/src/engine/shadow.dart | 17 +- .../src/engine/skwasm/skwasm_impl/canvas.dart | 185 +- .../src/engine/skwasm/skwasm_impl/codecs.dart | 9 +- .../engine/skwasm/skwasm_impl/filters.dart | 99 +- .../skwasm/skwasm_impl/font_collection.dart | 64 +- .../src/engine/skwasm/skwasm_impl/image.dart | 22 +- .../src/engine/skwasm/skwasm_impl/memory.dart | 11 +- .../src/engine/skwasm/skwasm_impl/paint.dart | 15 +- .../engine/skwasm/skwasm_impl/paragraph.dart | 280 +- .../src/engine/skwasm/skwasm_impl/path.dart | 111 +- .../skwasm/skwasm_impl/path_metrics.dart | 28 +- .../engine/skwasm/skwasm_impl/picture.dart | 13 +- .../skwasm/skwasm_impl/raw/raw_canvas.dart | 238 +- .../skwasm/skwasm_impl/raw/raw_filters.dart | 119 +- .../skwasm/skwasm_impl/raw/raw_fonts.dart | 25 +- .../skwasm/skwasm_impl/raw/raw_image.dart | 33 +- .../skwasm/skwasm_impl/raw/raw_memory.dart | 8 +- .../skwasm/skwasm_impl/raw/raw_paint.dart | 12 +- .../skwasm/skwasm_impl/raw/raw_path.dart | 120 +- .../skwasm_impl/raw/raw_path_metrics.dart | 42 +- .../skwasm/skwasm_impl/raw/raw_picture.dart | 32 +- .../skwasm/skwasm_impl/raw/raw_shaders.dart | 98 +- .../skwasm/skwasm_impl/raw/raw_skdata.dart | 1 + .../skwasm/skwasm_impl/raw/raw_skstring.dart | 2 + .../skwasm/skwasm_impl/raw/raw_surface.dart | 38 +- .../skwasm/skwasm_impl/raw/raw_vertices.dart | 21 +- .../raw/text/raw_line_metrics.dart | 17 +- .../skwasm_impl/raw/text/raw_paragraph.dart | 94 +- .../raw/text/raw_paragraph_builder.dart | 96 +- .../raw/text/raw_paragraph_style.dart | 55 +- .../skwasm_impl/raw/text/raw_strut_style.dart | 28 +- .../skwasm_impl/raw/text/raw_text_style.dart | 68 +- .../engine/skwasm/skwasm_impl/renderer.dart | 202 +- .../engine/skwasm/skwasm_impl/shaders.dart | 109 +- .../engine/skwasm/skwasm_impl/surface.dart | 47 +- .../engine/skwasm/skwasm_impl/vertices.dart | 72 +- .../engine/skwasm/skwasm_stub/renderer.dart | 169 +- lib/web_ui/lib/src/engine/svg.dart | 24 +- lib/web_ui/lib/src/engine/test_embedding.dart | 4 +- .../lib/src/engine/text/canvas_paragraph.dart | 81 +- .../lib/src/engine/text/font_collection.dart | 22 +- .../src/engine/text/layout_fragmenter.dart | 187 +- .../lib/src/engine/text/layout_service.dart | 129 +- .../engine/text/line_break_properties.dart | 13 +- .../lib/src/engine/text/line_breaker.dart | 78 +- .../lib/src/engine/text/measurement.dart | 8 +- .../lib/src/engine/text/paint_service.dart | 11 +- lib/web_ui/lib/src/engine/text/paragraph.dart | 256 +- lib/web_ui/lib/src/engine/text/ruler.dart | 9 +- .../lib/src/engine/text/text_direction.dart | 79 +- .../lib/src/engine/text/unicode_range.dart | 19 +- .../engine/text/word_break_properties.dart | 11 +- .../lib/src/engine/text/word_breaker.dart | 19 +- .../engine/text_editing/autofill_hint.dart | 115 +- .../text_editing/composition_aware_mixin.dart | 15 +- .../src/engine/text_editing/input_action.dart | 1 - .../text_editing/text_capitalization.dart | 21 +- .../src/engine/text_editing/text_editing.dart | 616 +- lib/web_ui/lib/src/engine/util.dart | 120 +- lib/web_ui/lib/src/engine/validators.dart | 28 +- lib/web_ui/lib/src/engine/vector_math.dart | 430 +- .../custom_element_dimensions_provider.dart | 22 +- .../dimensions_provider.dart | 5 +- .../full_page_dimensions_provider.dart | 22 +- .../view_embedder/display_dpr_stream.dart | 18 +- .../src/engine/view_embedder/dom_manager.dart | 11 +- .../full_page_embedding_strategy.dart | 17 +- .../view_embedder/flutter_view_manager.dart | 31 +- .../view_embedder/global_html_attributes.dart | 4 +- .../engine/view_embedder/style_manager.dart | 34 +- lib/web_ui/lib/src/engine/window.dart | 88 +- lib/web_ui/lib/text.dart | 248 +- lib/web_ui/lib/tile_mode.dart | 7 +- .../lib/ui_web/src/ui_web/asset_manager.dart | 26 +- .../ui_web/src/ui_web/browser_detection.dart | 20 +- .../src/ui_web/flutter_views_proxy.dart | 4 +- lib/web_ui/lib/ui_web/src/ui_web/images.dart | 31 +- .../lib/ui_web/src/ui_web/initialization.dart | 8 +- .../src/ui_web/navigation/url_strategy.dart | 14 +- .../src/ui_web/platform_view_registry.dart | 11 +- lib/web_ui/lib/ui_web/src/ui_web/testing.dart | 3 +- lib/web_ui/lib/window.dart | 28 +- .../backdrop_filter_golden_test.dart | 55 +- .../test/canvaskit/canvas_golden_test.dart | 252 +- .../test/canvaskit/canvaskit_api_test.dart | 930 +- .../canvaskit/canvaskit_api_tt_on_test.dart | 7 +- .../canvaskit/color_filter_golden_test.dart | 76 +- lib/web_ui/test/canvaskit/common.dart | 57 +- .../configuration_canvaskit_variant_test.dart | 4 +- .../display_canvas_factory_test.dart | 28 +- .../test/canvaskit/embedded_views_test.dart | 599 +- lib/web_ui/test/canvaskit/filter_test.dart | 135 +- .../flutter_tester_emulation_golden_test.dart | 44 +- .../test/canvaskit/fragment_program_test.dart | 18 +- .../test/canvaskit/image_golden_test.dart | 159 +- lib/web_ui/test/canvaskit/image_test.dart | 32 +- .../initialization/services_vs_ui_test.dart | 2 +- lib/web_ui/test/canvaskit/layer_test.dart | 46 +- .../linear_gradient_golden_test.dart | 62 +- .../test/canvaskit/multi_view_test.dart | 73 +- .../test/canvaskit/native_memory_test.dart | 102 +- .../no_create_image_bitmap_test.dart | 34 +- lib/web_ui/test/canvaskit/picture_test.dart | 33 +- .../canvaskit/platform_dispatcher_test.dart | 12 +- .../test/canvaskit/render_canvas_test.dart | 16 +- lib/web_ui/test/canvaskit/renderer_test.dart | 55 +- .../canvaskit/shader_mask_golden_test.dart | 94 +- lib/web_ui/test/canvaskit/shader_test.dart | 82 +- .../canvaskit/skia_font_collection_test.dart | 71 +- lib/web_ui/test/canvaskit/surface_test.dart | 62 +- .../canvaskit/sweep_gradient_golden_test.dart | 33 +- lib/web_ui/test/canvaskit/test_data.dart | 471 +- .../test/canvaskit/text_fragmenter_test.dart | 27 +- lib/web_ui/test/canvaskit/text_test.dart | 46 +- .../test/common/fake_asset_manager.dart | 19 +- .../test/common/keyboard_test_common.dart | 17 +- lib/web_ui/test/common/matchers.dart | 76 +- .../test/common/mock_engine_canvas.dart | 134 +- lib/web_ui/test/common/rendering.dart | 13 +- lib/web_ui/test/common/spy.dart | 8 +- .../test/common/test_initialization.dart | 14 +- lib/web_ui/test/engine/alarm_clock_test.dart | 15 +- .../test/engine/app_bootstrap_test.dart | 112 +- lib/web_ui/test/engine/assets_test.dart | 21 +- .../test/engine/browser_detect_test.dart | 110 +- .../test/engine/channel_buffers_test.dart | 166 +- lib/web_ui/test/engine/clipboard_test.dart | 38 +- lib/web_ui/test/engine/composition_test.dart | 290 +- .../test/engine/configuration_test.dart | 33 +- .../test/engine/dom_http_fetch_test.dart | 69 +- .../test/engine/frame_reference_test.dart | 8 +- lib/web_ui/test/engine/geometry_test.dart | 91 +- .../test/engine/gesture_settings_test.dart | 44 +- .../test/engine/global_styles_test.dart | 95 +- lib/web_ui/test/engine/history_test.dart | 44 +- lib/web_ui/test/engine/image/image_test.dart | 21 +- .../engine/image_format_detector_test.dart | 17 +- .../test/engine/image_to_byte_data_test.dart | 27 +- .../test/engine/initialization_test.dart | 134 +- .../test/engine/keyboard_converter_test.dart | 321 +- lib/web_ui/test/engine/lerp_test.dart | 11 +- lib/web_ui/test/engine/locale_test.dart | 38 +- lib/web_ui/test/engine/lru_cache_test.dart | 4 +- lib/web_ui/test/engine/matchers_test.dart | 126 +- lib/web_ui/test/engine/navigation_test.dart | 17 +- ...application_switcher_description_test.dart | 34 +- .../platform_dispatcher_test.dart | 211 +- .../system_ui_overlay_style_test.dart | 9 +- .../view_focus_binding_test.dart | 11 +- .../platform_views/content_manager_test.dart | 83 +- .../platform_views/message_handler_test.dart | 74 +- .../engine/platform_views/slots_test.dart | 20 +- .../event_position_helper_test.dart | 44 +- .../test/engine/pointer_binding_test.dart | 4521 +- lib/web_ui/test/engine/profiler_test.dart | 21 +- lib/web_ui/test/engine/raw_keyboard_test.dart | 725 +- lib/web_ui/test/engine/routing_test.dart | 384 +- .../test/engine/scene_builder_test.dart | 138 +- .../test/engine/scene_builder_utils.dart | 48 +- lib/web_ui/test/engine/scene_view_test.dart | 109 +- .../semantics_announcement_test.dart | 17 +- .../engine/semantics/semantics_api_test.dart | 13 +- .../semantics/semantics_auto_enable_test.dart | 10 +- .../semantics/semantics_helper_test.dart | 71 +- .../semantics/semantics_multi_view_test.dart | 33 +- .../semantics_placeholder_enable_test.dart | 10 +- .../test/engine/semantics/semantics_test.dart | 679 +- .../engine/semantics/semantics_tester.dart | 19 +- .../engine/semantics/semantics_text_test.dart | 30 +- .../engine/semantics/text_field_test.dart | 251 +- .../engine/services/serialization_test.dart | 3 +- .../surface/filters/image_filter_test.dart | 18 +- lib/web_ui/test/engine/text_editing_test.dart | 2407 +- lib/web_ui/test/engine/util_test.dart | 42 +- lib/web_ui/test/engine/vector_math_test.dart | 17 +- .../engine/view/view_constraints_test.dart | 173 +- ...stom_element_dimensions_provider_test.dart | 24 +- .../dimensions_provider_test.dart | 7 +- .../full_page_dimensions_provider_test.dart | 24 +- .../display_dpr_stream_test.dart | 14 +- .../view_embedder/dom_manager_test.dart | 77 +- ...ustom_element_embedding_strategy_test.dart | 40 +- .../embedding_strategy_test.dart | 7 +- .../full_page_embedding_strategy_test.dart | 52 +- .../flutter_view_manager_test.dart | 65 +- .../flutter_views_proxy_test.dart | 22 +- .../hot_restart_cache_handler_test.dart | 9 +- .../view_embedder/style_manager_test.dart | 18 +- lib/web_ui/test/engine/window_test.dart | 240 +- lib/web_ui/test/fallbacks/fallbacks_test.dart | 8 +- .../test/html/bitmap_canvas_golden_test.dart | 161 +- .../html/canvas_clip_path_golden_test.dart | 118 +- .../test/html/canvas_context_golden_test.dart | 31 +- .../test/html/canvas_reuse_golden_test.dart | 40 +- lib/web_ui/test/html/canvas_test.dart | 83 +- .../html/canvas_winding_rule_golden_test.dart | 69 +- lib/web_ui/test/html/clip_op_golden_test.dart | 47 +- .../backdrop_filter_golden_test.dart | 120 +- .../compositing/canvas_blend_golden_test.dart | 112 +- .../canvas_image_blend_mode_golden_test.dart | 165 +- .../canvas_image_filter_golden_test.dart | 28 +- .../canvas_mask_filter_golden_test.dart | 48 +- .../compositing/color_filter_golden_test.dart | 77 +- .../compositing/compositing_golden_test.dart | 454 +- .../dom_mask_filter_golden_test.dart | 14 +- lib/web_ui/test/html/deprecation_test.dart | 7 +- lib/web_ui/test/html/dom_canvas_test.dart | 54 +- .../html/drawing/canvas_arc_golden_test.dart | 115 +- .../canvas_draw_color_golden_test.dart | 31 +- .../canvas_draw_image_golden_test.dart | 520 +- .../canvas_draw_picture_golden_test.dart | 72 +- .../drawing/canvas_lines_golden_test.dart | 121 +- .../html/drawing/canvas_rect_golden_test.dart | 56 +- .../drawing/canvas_rrect_golden_test.dart | 78 +- .../canvas_stroke_joins_golden_test.dart | 38 +- .../canvas_stroke_rects_golden_test.dart | 66 +- .../test/html/drawing/conic_golden_test.dart | 19 +- .../drawing/dom_clip_stroke_golden_test.dart | 9 +- .../drawing/draw_vertices_golden_test.dart | 484 +- lib/web_ui/test/html/image_test.dart | 111 +- .../html/paragraph/general_golden_test.dart | 402 +- lib/web_ui/test/html/paragraph/helper.dart | 29 +- .../html/paragraph/justify_golden_test.dart | 117 +- .../html/paragraph/overflow_golden_test.dart | 7 +- .../paragraph/placeholders_golden_test.dart | 44 +- .../html/paragraph/shadows_golden_test.dart | 35 +- .../test/html/paragraph/text_goldens.dart | 35 +- .../text_multiline_clipping_golden_test.dart | 188 +- .../paragraph/text_overflow_golden_test.dart | 3 +- .../text_placeholders_golden_test.dart | 14 +- .../test/html/path_metrics_golden_test.dart | 58 +- lib/web_ui/test/html/path_ref_test.dart | 16 +- lib/web_ui/test/html/path_test.dart | 254 +- .../test/html/path_to_svg_golden_test.dart | 112 +- .../test/html/path_transform_golden_test.dart | 206 +- lib/web_ui/test/html/picture_golden_test.dart | 7 +- .../html/recording_canvas_golden_test.dart | 398 +- .../test/html/recording_canvas_test.dart | 47 +- .../test/html/resource_manager_test.dart | 10 +- lib/web_ui/test/html/screenshot.dart | 17 +- .../html/shaders/gradient_golden_test.dart | 712 +- .../shaders/image_shader_golden_test.dart | 73 +- .../shaders/linear_gradient_golden_test.dart | 132 +- .../shaders/radial_gradient_golden_test.dart | 120 +- .../html/shaders/shader_mask_golden_test.dart | 119 +- lib/web_ui/test/html/shadow_golden_test.dart | 91 +- .../html/surface/path/path_winding_test.dart | 260 +- .../test/html/surface/platform_view_test.dart | 14 +- .../test/html/surface/scene_builder_test.dart | 402 +- .../shaders/normalized_gradient_test.dart | 45 +- .../surface/shaders/shader_builder_test.dart | 159 +- .../test/html/surface/surface_test.dart | 171 +- lib/web_ui/test/html/testimage.dart | 3 +- .../text/canvas_paragraph_builder_test.dart | 106 +- .../test/html/text/canvas_paragraph_test.dart | 67 +- .../test/html/text/font_collection_test.dart | 308 +- .../test/html/text/font_loading_test.dart | 155 +- .../html/text/layout_fragmenter_test.dart | 187 +- .../test/html/text/layout_service_helper.dart | 7 +- .../html/text/layout_service_plain_test.dart | 183 +- .../html/text/layout_service_rich_test.dart | 18 +- .../test/html/text/line_breaker_test.dart | 243 +- .../html/text/line_breaker_test_helper.dart | 43 +- .../html/text/line_breaker_test_raw_data.dart | 1 - .../test/html/text/text_direction_test.dart | 10 +- .../test/html/text/word_breaker_test.dart | 59 +- lib/web_ui/test/html/text_test.dart | 425 +- .../test/ui/canvas_curves_golden_test.dart | 11 +- .../ui/canvas_draw_points_golden_test.dart | 65 +- .../test/ui/canvas_lines_golden_test.dart | 113 +- lib/web_ui/test/ui/canvas_test.dart | 29 +- lib/web_ui/test/ui/color_test.dart | 4 +- .../test/ui/draw_atlas_golden_test.dart | 73 +- .../test/ui/fallback_fonts_golden_test.dart | 1119 +- lib/web_ui/test/ui/filters_test.dart | 340 +- lib/web_ui/test/ui/font_collection_test.dart | 101 +- lib/web_ui/test/ui/fragment_shader_test.dart | 10 +- lib/web_ui/test/ui/frame_timings_test.dart | 3 +- lib/web_ui/test/ui/gradient_golden_test.dart | 45 +- lib/web_ui/test/ui/gradient_test.dart | 101 +- .../image/html_image_element_codec_test.dart | 41 +- lib/web_ui/test/ui/image_golden_test.dart | 95 +- lib/web_ui/test/ui/line_metrics_test.dart | 117 +- lib/web_ui/test/ui/paint_test.dart | 27 +- .../test/ui/paragraph_builder_test.dart | 66 +- lib/web_ui/test/ui/paragraph_style_test.dart | 113 +- lib/web_ui/test/ui/path_metrics_test.dart | 55 +- lib/web_ui/test/ui/path_test.dart | 61 +- lib/web_ui/test/ui/picture_test.dart | 3 +- lib/web_ui/test/ui/platform_view_test.dart | 120 +- lib/web_ui/test/ui/renderer_test.dart | 1 - lib/web_ui/test/ui/rrect_test.dart | 31 +- lib/web_ui/test/ui/scene_builder_test.dart | 666 +- lib/web_ui/test/ui/shadow_test.dart | 12 +- lib/web_ui/test/ui/strut_style_test.dart | 93 +- lib/web_ui/test/ui/text_golden_test.dart | 368 +- lib/web_ui/test/ui/text_style_test.dart | 173 +- .../test/ui/text_style_test_env_test.dart | 8 +- lib/web_ui/test/ui/url_strategy_test.dart | 18 +- lib/web_ui/test/ui/utils.dart | 3 +- lib/web_ui/test/ui/vertices_test.dart | 25 +- .../flutter_build/dart_plugin_registrant.dart | 34 +- runtime/fixtures/runtime_test.dart | 52 +- shell/common/fixtures/shell_test.dart | 115 +- .../Source/fixtures/flutter_desktop_test.dart | 3 +- shell/platform/embedder/fixtures/main.dart | 724 +- .../fuchsia/dart-pkg/fuchsia/lib/fuchsia.dart | 12 +- .../dart-pkg/zircon/lib/src/handle.dart | 3 +- .../dart-pkg/zircon/lib/src/system.dart | 30 +- .../dart-pkg/zircon/lib/src/zd_channel.dart | 17 +- .../dart-pkg/zircon/lib/src/zd_handle.dart | 14 +- .../dart-pkg/zircon/test/zircon_tests.dart | 123 +- .../dart-pkg/zircon_ffi/lib/zircon_ffi.dart | 428 +- shell/platform/fuchsia/dart/compiler.dart | 16 +- .../embedder/child-view/lib/child_view.dart | 9 +- .../embedder/parent-view/lib/parent_view.dart | 78 +- .../lib/mouse-input-view.dart | 64 +- .../text-input-view/lib/text_input_view.dart | 31 +- .../lib/embedding-flutter-view.dart | 133 +- .../lib/touch-input-view.dart | 41 +- .../dart_profiler_symbols.dart | 23 +- shell/platform/windows/fixtures/main.dart | 112 +- shell/testing/observatory/empty_main.dart | 3 +- shell/testing/observatory/launcher.dart | 10 +- shell/testing/observatory/service_client.dart | 12 +- shell/testing/observatory/test.dart | 17 +- testing/benchmark/bin/parse_and_send.dart | 59 +- testing/dart/assets_test.dart | 26 +- testing/dart/canvas_test.dart | 992 +- testing/dart/channel_buffers_test.dart | 189 +- testing/dart/codec_test.dart | 158 +- testing/dart/color_filter_test.dart | 69 +- testing/dart/color_test.dart | 127 +- testing/dart/compositing_test.dart | 304 +- testing/dart/dart_test.dart | 1 + testing/dart/encoding_test.dart | 105 +- testing/dart/fragment_shader_test.dart | 259 +- testing/dart/geometry_test.dart | 117 +- testing/dart/gesture_settings_test.dart | 39 +- testing/dart/goldens.dart | 45 +- testing/dart/gpu_test.dart | 521 +- testing/dart/gradient_test.dart | 85 +- .../http_allow_http_connections_test.dart | 7 +- .../http_disallow_http_connections_test.dart | 59 +- testing/dart/image_descriptor_test.dart | 12 +- testing/dart/image_dispose_test.dart | 17 +- testing/dart/image_events_test.dart | 8 +- testing/dart/image_filter_test.dart | 228 +- testing/dart/image_resize_test.dart | 24 +- testing/dart/image_shader_test.dart | 2 +- testing/dart/isolate_name_server_test.dart | 58 +- testing/dart/isolate_test.dart | 7 +- testing/dart/lerp_test.dart | 11 +- testing/dart/locale_test.dart | 35 +- testing/dart/mask_filter_test.dart | 7 +- .../dart/observatory/shader_reload_test.dart | 12 +- testing/dart/observatory/skp_test.dart | 1 - testing/dart/observatory/tracing_test.dart | 3 +- .../observatory/vmservice_methods_test.dart | 25 +- testing/dart/painting_test.dart | 80 +- testing/dart/paragraph_builder_test.dart | 7 +- testing/dart/paragraph_test.dart | 203 +- testing/dart/path_test.dart | 71 +- testing/dart/picture_test.dart | 3 +- testing/dart/platform_dispatcher_test.dart | 46 +- testing/dart/platform_isolate_test.dart | 20 +- testing/dart/semantics_test.dart | 13 +- testing/dart/shader_test_file_utils.dart | 12 +- testing/dart/spawn_test.dart | 31 +- testing/dart/stringification_test.dart | 25 +- testing/dart/text_test.dart | 218 +- testing/dart/window_test.dart | 62 +- .../scenario_app/bin/run_android_tests.dart | 296 +- testing/scenario_app/bin/run_ios_tests.dart | 235 +- .../bin/utils/adb_logcat_filtering.dart | 20 +- .../scenario_app/bin/utils/environment.dart | 6 +- testing/scenario_app/bin/utils/logs.dart | 2 +- testing/scenario_app/bin/utils/options.dart | 181 +- .../bin/utils/process_manager_extension.dart | 23 +- testing/scenario_app/lib/main.dart | 8 +- .../lib/src/animated_color_square.dart | 10 +- .../scenario_app/lib/src/bogus_font_text.dart | 10 +- .../scenario_app/lib/src/channel_util.dart | 9 +- .../src/darwin_app_extension_scenario.dart | 6 +- .../lib/src/darwin_system_font.dart | 10 +- .../lib/src/get_bitmap_scenario.dart | 12 +- .../lib/src/locale_initialization.dart | 166 +- .../scenario_app/lib/src/platform_view.dart | 1163 +- .../scenario_app/lib/src/poppable_screen.dart | 8 +- testing/scenario_app/lib/src/scenarios.dart | 179 +- testing/scenario_app/lib/src/solid_blue.dart | 6 +- testing/scenario_app/lib/src/texture.dart | 15 +- .../lib/src/touches_scenario.dart | 6 +- .../test/adb_log_filter_test.dart | 10 +- .../test/src/fake_adb_logcat.dart | 10 +- testing/scenario_app/tool/logcat_reader.dart | 20 +- .../lib/skia_gold_client.dart | 172 +- .../lib/src/release_version.dart | 29 +- .../test/release_version_test.dart | 8 +- .../test/skia_gold_client_test.dart | 40 +- testing/skia_gold_client/tool/e2e_test.dart | 26 +- testing/skia_gold_client/tool/generate.dart | 137 +- testing/symbols/verify_exported.dart | 60 +- tools/android_lint/bin/main.dart | 58 +- tools/api_check/lib/apicheck.dart | 49 +- tools/api_check/test/apicheck_test.dart | 123 +- .../lib/build_bucket_golden_scraper.dart | 43 +- .../build_bucket_golden_scraper_test.dart | 16 +- tools/clang_tidy/lib/clang_tidy.dart | 122 +- tools/clang_tidy/lib/src/command.dart | 72 +- tools/clang_tidy/lib/src/options.dart | 136 +- tools/clang_tidy/test/clang_tidy_test.dart | 315 +- tools/clangd_check/bin/main.dart | 45 +- .../compare_goldens/lib/compare_goldens.dart | 22 +- tools/const_finder/bin/main.dart | 91 +- .../const_finder/test/const_finder_test.dart | 247 +- .../test/fixtures/lib/consts.dart | 1 - .../lib/dir_contents_diff.dart | 13 +- .../test/dir_contents_diff_test.dart | 27 +- tools/engine_tool/lib/main.dart | 25 +- tools/engine_tool/lib/src/build_plan.dart | 99 +- tools/engine_tool/lib/src/build_utils.dart | 18 +- .../lib/src/commands/build_command.dart | 24 +- .../lib/src/commands/cleanup_command.dart | 19 +- .../engine_tool/lib/src/commands/command.dart | 7 +- .../lib/src/commands/command_runner.dart | 54 +- .../lib/src/commands/fetch_command.dart | 5 +- .../lib/src/commands/format_command.dart | 65 +- .../lib/src/commands/lint_command.dart | 45 +- .../lib/src/commands/query_command.dart | 61 +- .../lib/src/commands/run_command.dart | 86 +- .../lib/src/commands/test_command.dart | 47 +- tools/engine_tool/lib/src/dependencies.dart | 16 +- tools/engine_tool/lib/src/environment.dart | 7 +- .../lib/src/flutter_tool_interop/device.dart | 12 +- .../flutter_tool_interop/flutter_tool.dart | 21 +- tools/engine_tool/lib/src/gn.dart | 102 +- tools/engine_tool/lib/src/label.dart | 33 +- tools/engine_tool/lib/src/logger.dart | 102 +- tools/engine_tool/lib/src/phone_home.dart | 4 +- tools/engine_tool/lib/src/proc_utils.dart | 81 +- tools/engine_tool/lib/src/typed_json.dart | 43 +- tools/engine_tool/test/build_plan_test.dart | 330 +- .../test/commands/build_command_test.dart | 390 +- .../test/commands/cleanup_command_test.dart | 88 +- .../test/commands/fetch_command_test.dart | 46 +- .../test/commands/format_command_test.dart | 116 +- .../test/commands/lint_command_test.dart | 27 +- .../test/commands/query_command_test.dart | 194 +- .../test/commands/run_command_test.dart | 177 +- .../test/commands/test_command_test.dart | 24 +- tools/engine_tool/test/entry_point_test.dart | 12 +- .../external_tools/flutter_tools_test.dart | 47 +- .../test/external_tools/gn_test.dart | 37 +- tools/engine_tool/test/proc_utils_test.dart | 55 +- tools/engine_tool/test/run_target_test.dart | 89 +- tools/engine_tool/test/src/matchers.dart | 12 +- .../test/src/test_build_configs.dart | 19 +- tools/engine_tool/test/src/utils.dart | 81 +- tools/engine_tool/test/typed_json_test.dart | 251 +- tools/engine_tool/test/utils_test.dart | 55 +- tools/engine_tool/test/worker_pool_test.dart | 47 +- tools/gen_locale.dart | 22 +- .../bin/gen_web_locale_keymap.dart | 67 +- .../lib/benchmark_planner.dart | 16 +- tools/gen_web_locale_keymap/lib/common.dart | 26 +- tools/gen_web_locale_keymap/lib/github.dart | 60 +- tools/gen_web_locale_keymap/lib/json_get.dart | 9 +- .../lib/layout_types.dart | 8 +- tools/githooks/lib/githooks.dart | 25 +- tools/githooks/lib/src/messages.dart | 4 +- tools/githooks/lib/src/pre_push_command.dart | 35 +- tools/githooks/test/githooks_test.dart | 36 +- .../bin/golden_tests_harvester.dart | 30 +- .../lib/golden_tests_harvester.dart | 102 +- .../lib/src/digests_json_format.dart | 40 +- .../test/golden_tests_harvester_test.dart | 99 +- .../lib/header_guard_check.dart | 72 +- .../lib/src/header_file.dart | 44 +- .../test/header_file_test.dart | 99 +- tools/path_ops/dart/lib/path_ops.dart | 81 +- tools/path_ops/dart/test/path_ops_test.dart | 128 +- tools/pkg/engine_build_configs/bin/check.dart | 32 +- tools/pkg/engine_build_configs/bin/run.dart | 25 +- .../lib/src/build_config.dart | 257 +- .../lib/src/build_config_loader.dart | 19 +- .../lib/src/build_config_runner.dart | 264 +- .../engine_build_configs/lib/src/ci_yaml.dart | 53 +- .../lib/src/merge_gn_args.dart | 5 +- .../test/build_config_loader_test.dart | 18 +- .../test/build_config_runner_test.dart | 405 +- .../test/build_config_test.dart | 76 +- .../test/ci_yaml_test.dart | 32 +- .../test/merge_gn_args_test.dart | 30 +- .../lib/engine_repo_tools.dart | 27 +- .../test/engine_repo_tools_test.dart | 36 +- .../git_repo_tools/lib/git_repo_tools.dart | 70 +- .../test/git_repo_tools_test.dart | 24 +- .../pkg/process_fakes/lib/process_fakes.dart | 17 +- web_sdk/sdk_rewriter.dart | 110 +- web_sdk/test/api_conform_test.dart | 235 +- web_sdk/test/js_access_test.dart | 61 +- web_sdk/test/sdk_rewriter_test.dart | 25 +- .../web_engine_tester/lib/golden_tester.dart | 22 +- .../web_engine_tester/lib/static/host.dart | 145 +- web_sdk/web_test_utils/lib/environment.dart | 89 +- 721 files changed, 68400 insertions(+), 68047 deletions(-) diff --git a/ci/bin/format.dart b/ci/bin/format.dart index 7132549086271..2f2998f1a8037 100644 --- a/ci/bin/format.dart +++ b/ci/bin/format.dart @@ -33,11 +33,7 @@ class FormattingException implements Exception { } } -enum MessageType { - message, - error, - warning, -} +enum MessageType { message, error, warning } enum FormatCheck { dart, @@ -91,9 +87,7 @@ String formatCheckToName(FormatCheck check) { } List formatCheckNames() { - return FormatCheck.values - .map((FormatCheck check) => check.name) - .toList(); + return FormatCheck.values.map((FormatCheck check) => check.name).toList(); } Future _runGit( @@ -101,10 +95,10 @@ Future _runGit( ProcessRunner processRunner, { bool failOk = false, }) async { - final ProcessRunnerResult result = await processRunner.runProcess( - ['git', ...args], - failOk: failOk, - ); + final ProcessRunnerResult result = await processRunner.runProcess([ + 'git', + ...args, + ], failOk: failOk); return result.stdout; } @@ -121,9 +115,9 @@ abstract class FormatChecker { this.allFiles = false, this.messageCallback, }) : _processRunner = ProcessRunner( - defaultWorkingDirectory: repoDir, - processManager: processManager, - ); + defaultWorkingDirectory: repoDir, + processManager: processManager, + ); /// Factory method that creates subclass format checkers based on the type of check. factory FormatChecker.ofType( @@ -236,16 +230,20 @@ abstract class FormatChecker { processRunner: _processRunner, printReport: namedReport('patch'), ); - final List jobs = patches.map((String patch) { - return WorkerJob( - ['git', 'apply', '--ignore-space-change'], - stdinRaw: codeUnitsAsStream(patch.codeUnits), - ); - }).toList(); + final List jobs = + patches.map((String patch) { + return WorkerJob([ + 'git', + 'apply', + '--ignore-space-change', + ], stdinRaw: codeUnitsAsStream(patch.codeUnits)); + }).toList(); final List completedJobs = await patchPool.runToCompletion(jobs); if (patchPool.failedJobs != 0) { - error('${patchPool.failedJobs} patch${patchPool.failedJobs > 1 ? 'es' : ''} ' - 'failed to apply.'); + error( + '${patchPool.failedJobs} patch${patchPool.failedJobs > 1 ? 'es' : ''} ' + 'failed to apply.', + ); completedJobs .where((WorkerJob job) => job.result.exitCode != 0) .map((WorkerJob job) => job.result.output) @@ -265,11 +263,7 @@ abstract class FormatChecker { Future> getFileList(List types) async { String output; if (allFiles) { - output = await runGit([ - 'ls-files', - '--', - ...types, - ]); + output = await runGit(['ls-files', '--', ...types]); } else { output = await runGit([ 'diff', @@ -282,9 +276,10 @@ abstract class FormatChecker { ...types, ]); } - return output.split('\n').where( - (String line) => line.isNotEmpty && !line.contains('third_party') - ).toList(); + return output + .split('\n') + .where((String line) => line.isNotEmpty && !line.contains('third_party')) + .toList(); } /// Generates a reporting function to supply to ProcessRunner to use instead @@ -300,11 +295,13 @@ abstract class FormatChecker { final String pendingStr = pending.toString().padLeft(3); final String failedStr = failed.toString().padLeft(3); - stdout.write('$name Jobs: $percent% done, ' - '$completedStr/$totalStr completed, ' - '$inProgressStr in progress, ' - '$pendingStr pending, ' - '$failedStr failed.${' ' * 20}\r'); + stdout.write( + '$name Jobs: $percent% done, ' + '$completedStr/$totalStr completed, ' + '$inProgressStr in progress, ' + '$pendingStr pending, ' + '$failedStr failed.${' ' * 20}\r', + ); }; } @@ -331,8 +328,10 @@ class ClangFormatChecker extends FormatChecker { Abi.macosArm64 => 'mac-arm64', Abi.macosX64 => 'mac-x64', Abi.windowsX64 => 'windows-x64', - (_) => throw FormattingException( - "Unknown operating system: don't know how to run clang-format here.") + (_) => + throw FormattingException( + "Unknown operating system: don't know how to run clang-format here.", + ), }; clangFormat = File( path.join( @@ -367,8 +366,10 @@ class ClangFormatChecker extends FormatChecker { } Future _getClangFormatVersion() async { - final ProcessRunnerResult result = - await _processRunner.runProcess([clangFormat.path, '--version']); + final ProcessRunnerResult result = await _processRunner.runProcess([ + clangFormat.path, + '--version', + ]); return result.stdout.trim(); } @@ -423,14 +424,15 @@ class ClangFormatChecker extends FormatChecker { '--', completedJob.command.last, '-', - ], - stdinRaw: codeUnitsAsStream(completedJob.result.stdoutRaw)), + ], stdinRaw: codeUnitsAsStream(completedJob.result.stdoutRaw)), ); } else { final String formatterCommand = completedJob.command.join(' '); - error("Formatter command '$formatterCommand' failed with exit code " - '${completedJob.result.exitCode}. Command output follows:\n\n' - '${completedJob.result.output}'); + error( + "Formatter command '$formatterCommand' failed with exit code " + '${completedJob.result.exitCode}. Command output follows:\n\n' + '${completedJob.result.output}', + ); } } final ProcessPool diffPool = ProcessPool( @@ -445,24 +447,32 @@ class ClangFormatChecker extends FormatChecker { if (failed.isNotEmpty) { final bool plural = failed.length > 1; if (fixing) { - message('Fixing ${failed.length} C++/ObjC/Shader file${plural ? 's' : ''}' - ' which ${plural ? 'were' : 'was'} formatted incorrectly.'); + message( + 'Fixing ${failed.length} C++/ObjC/Shader file${plural ? 's' : ''}' + ' which ${plural ? 'were' : 'was'} formatted incorrectly.', + ); } else { - error('Found ${failed.length} C++/ObjC/Shader file${plural ? 's' : ''}' - ' which ${plural ? 'were' : 'was'} formatted incorrectly.'); + error( + 'Found ${failed.length} C++/ObjC/Shader file${plural ? 's' : ''}' + ' which ${plural ? 'were' : 'was'} formatted incorrectly.', + ); stdout.writeln('To fix, run `et format` or:'); stdout.writeln(); stdout.writeln('git apply <((WorkerJob job) { return job.result.stdout @@ -525,8 +535,12 @@ class JavaFormatChecker extends FormatChecker { static const String _javaFormatErrorString = 'Java Formatting Error'; Future _getGoogleJavaFormatVersion() async { - final ProcessRunnerResult result = await _processRunner - .runProcess([javaExe, '-jar', googleJavaFormatJar.path, '--version']); + final ProcessRunnerResult result = await _processRunner.runProcess([ + javaExe, + '-jar', + googleJavaFormatJar.path, + '--version', + ]); return result.stderr.trim(); } @@ -554,8 +568,10 @@ class JavaFormatChecker extends FormatChecker { } Future _getJavaVersion() async { - final ProcessRunnerResult result = - await _processRunner.runProcess([javaExe, '-version']); + final ProcessRunnerResult result = await _processRunner.runProcess([ + javaExe, + '-version', + ]); return result.stderr.trim().split('\n')[0]; } @@ -573,8 +589,10 @@ class JavaFormatChecker extends FormatChecker { javaVersion = await _getJavaVersion(); } on ProcessRunnerException { if (!_processRunner.processManager.canRun(javaExe)) { - error('Cannot find Java ($javaExe). ' - 'Skipping Java format check.'); + error( + 'Cannot find Java ($javaExe). ' + 'Skipping Java format check.', + ); return const [_javaFormatErrorString]; } error('Cannot run Java ($javaExe), skipping Java file formatting!'); @@ -593,11 +611,7 @@ class JavaFormatChecker extends FormatChecker { if (file.trim().isEmpty) { continue; } - formatJobs.add( - WorkerJob( - [javaExe, '-jar', googleJavaFormatJar.path, file.trim()], - ), - ); + formatJobs.add(WorkerJob([javaExe, '-jar', googleJavaFormatJar.path, file.trim()])); } final ProcessPool formatPool = ProcessPool( processRunner: _processRunner, @@ -608,25 +622,24 @@ class JavaFormatChecker extends FormatChecker { await for (final WorkerJob completedJob in completedJavaFormats) { if (completedJob.result.exitCode == 0) { diffJobs.add( - WorkerJob( - [ - 'git', - 'diff', - '--no-index', - '--no-color', - '--ignore-cr-at-eol', - '--', - completedJob.command.last, - '-', - ], - stdinRaw: codeUnitsAsStream(completedJob.result.stdoutRaw), - ), + WorkerJob([ + 'git', + 'diff', + '--no-index', + '--no-color', + '--ignore-cr-at-eol', + '--', + completedJob.command.last, + '-', + ], stdinRaw: codeUnitsAsStream(completedJob.result.stdoutRaw)), ); } else { final String formatterCommand = completedJob.command.join(' '); - error("Formatter command '$formatterCommand' failed with exit code " - '${completedJob.result.exitCode}. Command output follows:\n\n' - '${completedJob.result.output}'); + error( + "Formatter command '$formatterCommand' failed with exit code " + '${completedJob.result.exitCode}. Command output follows:\n\n' + '${completedJob.result.output}', + ); } } final ProcessPool diffPool = ProcessPool( @@ -641,18 +654,24 @@ class JavaFormatChecker extends FormatChecker { if (failed.isNotEmpty) { final bool plural = failed.length > 1; if (fixing) { - message('Fixing ${failed.length} Java file${plural ? 's' : ''}' - ' which ${plural ? 'were' : 'was'} formatted incorrectly.'); + message( + 'Fixing ${failed.length} Java file${plural ? 's' : ''}' + ' which ${plural ? 'were' : 'was'} formatted incorrectly.', + ); } else { - error('Found ${failed.length} Java file${plural ? 's' : ''}' - ' which ${plural ? 'were' : 'was'} formatted incorrectly.'); + error( + 'Found ${failed.length} Java file${plural ? 's' : ''}' + ' which ${plural ? 'were' : 'was'} formatted incorrectly.', + ); stdout.writeln('To fix, run `et format` or:'); stdout.writeln(); stdout.writeln('git apply < _runGnCheck({required bool fixing}) async { final List filesToCheck = await getFileList(['*.gn', '*.gni']); - final List cmd = [ - gnBinary.path, - 'format', - if (!fixing) '--stdin', - ]; + final List cmd = [gnBinary.path, 'format', if (!fixing) '--stdin']; final List jobs = []; for (final String file in filesToCheck) { if (fixing) { - jobs.add(WorkerJob( - [...cmd, file], - name: [...cmd, file].join(' '), - )); + jobs.add(WorkerJob([...cmd, file], name: [...cmd, file].join(' '))); } else { final WorkerJob job = WorkerJob( cmd, @@ -740,33 +745,31 @@ class GnFormatChecker extends FormatChecker { await for (final WorkerJob completedJob in completedJobs) { if (completedJob.result.exitCode == 0) { diffJobs.add( - WorkerJob( - [ - 'git', - 'diff', - '--no-index', - '--no-color', - '--ignore-cr-at-eol', - '--', - completedJob.name.split(' ').last, - '-' - ], - stdinRaw: codeUnitsAsStream(completedJob.result.stdoutRaw), - ), + WorkerJob([ + 'git', + 'diff', + '--no-index', + '--no-color', + '--ignore-cr-at-eol', + '--', + completedJob.name.split(' ').last, + '-', + ], stdinRaw: codeUnitsAsStream(completedJob.result.stdoutRaw)), ); } else { final String formatterCommand = completedJob.command.join(' '); - error("Formatter command '$formatterCommand' failed with exit code " - '${completedJob.result.exitCode}. Command output follows:\n\n' - '${completedJob.result.output}'); + error( + "Formatter command '$formatterCommand' failed with exit code " + '${completedJob.result.exitCode}. Command output follows:\n\n' + '${completedJob.result.output}', + ); } } final ProcessPool diffPool = ProcessPool( processRunner: _processRunner, printReport: namedReport('diff'), ); - final List completedDiffs = - await diffPool.runToCompletion(diffJobs); + final List completedDiffs = await diffPool.runToCompletion(diffJobs); final Iterable failed = completedDiffs.where((WorkerJob job) { return job.result.exitCode != 0; }); @@ -774,25 +777,33 @@ class GnFormatChecker extends FormatChecker { if (failed.isNotEmpty) { final bool plural = failed.length > 1; if (fixing) { - message('Fixed ${failed.length} GN file${plural ? 's' : ''}' - ' which ${plural ? 'were' : 'was'} formatted incorrectly.'); + message( + 'Fixed ${failed.length} GN file${plural ? 's' : ''}' + ' which ${plural ? 'were' : 'was'} formatted incorrectly.', + ); } else { - error('Found ${failed.length} GN file${plural ? 's' : ''}' - ' which ${plural ? 'were' : 'was'} formatted incorrectly.'); + error( + 'Found ${failed.length} GN file${plural ? 's' : ''}' + ' which ${plural ? 'were' : 'was'} formatted incorrectly.', + ); stdout.writeln('To fix, run `et format` or:'); stdout.writeln(); stdout.writeln('git apply < 1; if (fixing) { - message('Fixing ${incorrect.length} dart file${plural ? 's' : ''}' - ' which ${plural ? 'were' : 'was'} formatted incorrectly.'); + message( + 'Fixing ${incorrect.length} dart file${plural ? 's' : ''}' + ' which ${plural ? 'were' : 'was'} formatted incorrectly.', + ); } else { - error('Found ${incorrect.length} Dart file${plural ? 's' : ''}' - ' which ${plural ? 'were' : 'was'} formatted incorrectly.'); + error( + 'Found ${incorrect.length} Dart file${plural ? 's' : ''}' + ' which ${plural ? 'were' : 'was'} formatted incorrectly.', + ); stdout.writeln(); stdout.writeln('To fix, run `et format` or:'); stdout.writeln(); stdout.writeln('git apply < cmd = [ yapfBin.path, - '--style', _yapfStyle.path, + '--style', + _yapfStyle.path, if (!fixing) '--diff', if (fixing) '--in-place', ]; @@ -1020,11 +1031,15 @@ class PythonFormatChecker extends FormatChecker { if (incorrect.isNotEmpty) { final bool plural = incorrect.length > 1; if (fixing) { - message('Fixed ${incorrect.length} python file${plural ? 's' : ''}' - ' which ${plural ? 'were' : 'was'} formatted incorrectly.'); + message( + 'Fixed ${incorrect.length} python file${plural ? 's' : ''}' + ' which ${plural ? 'were' : 'was'} formatted incorrectly.', + ); } else { - error('Found ${incorrect.length} python file${plural ? 's' : ''}' - ' which ${plural ? 'were' : 'was'} formatted incorrectly:'); + error( + 'Found ${incorrect.length} python file${plural ? 's' : ''}' + ' which ${plural ? 'were' : 'was'} formatted incorrectly:', + ); stdout.writeln('To fix, run `et format` or:'); stdout.writeln(); stdout.writeln('git apply <[]; } - message('Checking for trailing whitespace on ${files.length} source ' - 'file${files.length > 1 ? 's' : ''}...'); + message( + 'Checking for trailing whitespace on ${files.length} source ' + 'file${files.length > 1 ? 's' : ''}...', + ); final ProcessPoolProgressReporter reporter = namedReport('whitespace'); final List<_GrepResult> found = <_GrepResult>[]; @@ -1135,11 +1152,7 @@ class WhitespaceFormatChecker extends FormatChecker { int pending = total; int failed = 0; for (final _GrepResult result in _whereHasTrailingWhitespace( - files.map( - (String file) => File( - path.join(repoDir.absolute.path, file), - ), - ), + files.map((String file) => File(path.join(repoDir.absolute.path, file))), )) { if (result.isEmpty) { completed++; @@ -1201,9 +1214,7 @@ final class HeaderFormatChecker extends FormatChecker { Future checkFormatting() async { final List include = []; if (!allFiles) { - include.addAll(await getFileList([ - '*.h', - ])); + include.addAll(await getFileList(['*.h'])); if (include.isEmpty) { message('No header files with changes, skipping header guard check.'); return true; @@ -1229,9 +1240,7 @@ final class HeaderFormatChecker extends FormatChecker { Future fixFormatting() async { final List include = []; if (!allFiles) { - include.addAll(await getFileList([ - '*.h', - ])); + include.addAll(await getFileList(['*.h'])); if (include.isEmpty) { message('No header files with changes, skipping header guard fix.'); return true; @@ -1275,10 +1284,12 @@ Future _getDiffBaseRevision(ProcessManager processManager, Directory rep // This is the preferred command to use, but developer checkouts often do // not have a clear fork point, so we fall back to just the regular // merge-base in that case. - result = await _runGit( - ['merge-base', '--fork-point', 'FETCH_HEAD', 'HEAD'], - processRunner, - ); + result = await _runGit([ + 'merge-base', + '--fork-point', + 'FETCH_HEAD', + 'HEAD', + ], processRunner); } on ProcessRunnerException { result = await _runGit(['merge-base', 'FETCH_HEAD', 'HEAD'], processRunner); } @@ -1286,8 +1297,10 @@ Future _getDiffBaseRevision(ProcessManager processManager, Directory rep } void _usage(ArgParser parser, {int exitCode = 1}) { - stderr.writeln('format.dart [--help] [--fix] [--all-files] ' - '[--check <${formatCheckNames().join('|')}>]'); + stderr.writeln( + 'format.dart [--help] [--fix] [--all-files] ' + '[--check <${formatCheckNames().join('|')}>]', + ); stderr.writeln(parser.usage); exit(exitCode); } @@ -1297,19 +1310,27 @@ bool verbose = false; Future main(List arguments) async { final ArgParser parser = ArgParser(); parser.addFlag('help', help: 'Print help.', abbr: 'h'); - parser.addFlag('fix', - abbr: 'f', - help: 'Instead of just checking for formatting errors, fix them in place.'); - parser.addFlag('all-files', - abbr: 'a', - help: 'Instead of just checking for formatting errors in changed files, ' - 'check for them in all files.'); - parser.addMultiOption('check', - abbr: 'c', - allowed: formatCheckNames(), - defaultsTo: formatCheckNames(), - help: 'Specifies which checks will be performed. Defaults to all checks. ' - 'May be specified more than once to perform multiple types of checks. '); + parser.addFlag( + 'fix', + abbr: 'f', + help: 'Instead of just checking for formatting errors, fix them in place.', + ); + parser.addFlag( + 'all-files', + abbr: 'a', + help: + 'Instead of just checking for formatting errors in changed files, ' + 'check for them in all files.', + ); + parser.addMultiOption( + 'check', + abbr: 'c', + allowed: formatCheckNames(), + defaultsTo: formatCheckNames(), + help: + 'Specifies which checks will be performed. Defaults to all checks. ' + 'May be specified more than once to perform multiple types of checks. ', + ); parser.addFlag('verbose', help: 'Print verbose output.', defaultsTo: verbose); late final ArgResults options; @@ -1355,12 +1376,14 @@ Future main(List arguments) async { for (final String checkName in checks) { final FormatCheck check = nameToFormatCheck(checkName); final String humanCheckName = formatCheckToName(check); - final FormatChecker checker = FormatChecker.ofType(check, - baseGitRef: baseGitRef, - repoDir: repoDir, - srcDir: srcDir, - allFiles: options['all-files'] as bool, - messageCallback: message); + final FormatChecker checker = FormatChecker.ofType( + check, + baseGitRef: baseGitRef, + repoDir: repoDir, + srcDir: srcDir, + allFiles: options['all-files'] as bool, + messageCallback: message, + ); bool stepResult; if (options['fix'] as bool) { message('Fixing any $humanCheckName format problems'); diff --git a/ci/test/format_test.dart b/ci/test/format_test.dart index a63e129d43b18..082fe0f0a5c65 100644 --- a/ci/test/format_test.dart +++ b/ci/test/format_test.dart @@ -20,28 +20,32 @@ class FileContentPair { } final FileContentPair ccContentPair = FileContentPair( - 'int main(){return 0;}\n', 'int main() {\n return 0;\n}\n'); -final FileContentPair hContentPair = - FileContentPair('int\nmain\n()\n;\n', 'int main();\n'); + 'int main(){return 0;}\n', + 'int main() {\n return 0;\n}\n', +); +final FileContentPair hContentPair = FileContentPair('int\nmain\n()\n;\n', 'int main();\n'); final FileContentPair dartContentPair = FileContentPair( - 'enum \n\nfoo {\n entry1,\n entry2,\n}', 'enum foo { entry1, entry2 }\n'); + 'enum \n\nfoo {\n entry1,\n entry2,\n}', + 'enum foo { entry1, entry2 }\n', +); final FileContentPair gnContentPair = FileContentPair( - 'test\n(){testvar=true}\n', 'test() {\n testvar = true\n}\n'); + 'test\n(){testvar=true}\n', + 'test() {\n testvar = true\n}\n', +); final FileContentPair javaContentPair = FileContentPair( - 'class Test{public static void main(String args[]){System.out.println("Test");}}\n', - 'class Test {\n public static void main(String args[]) {\n System.out.println("Test");\n }\n}\n'); + 'class Test{public static void main(String args[]){System.out.println("Test");}}\n', + 'class Test {\n public static void main(String args[]) {\n System.out.println("Test");\n }\n}\n', +); final FileContentPair pythonContentPair = FileContentPair( - "if __name__=='__main__':\n sys.exit(\nMain(sys.argv)\n)\n", - "if __name__ == '__main__':\n sys.exit(Main(sys.argv))\n"); + "if __name__=='__main__':\n sys.exit(\nMain(sys.argv)\n)\n", + "if __name__ == '__main__':\n sys.exit(Main(sys.argv))\n", +); final FileContentPair whitespaceContentPair = FileContentPair( - 'int main() {\n return 0; \n}\n', 'int main() {\n return 0;\n}\n'); + 'int main() {\n return 0; \n}\n', + 'int main() {\n return 0;\n}\n', +); final FileContentPair headerContentPair = FileContentPair( - [ - '#ifndef FOO_H_', - '#define FOO_H_', - '', - '#endif // FOO_H_', - ].join('\n'), + ['#ifndef FOO_H_', '#define FOO_H_', '', '#endif // FOO_H_'].join('\n'), [ '#ifndef FLUTTER_FORMAT_TEST_H_', '#define FLUTTER_FORMAT_TEST_H_', @@ -118,55 +122,37 @@ class TestFileFixture { case target.FormatCheck.clang: return FileContentPair( content, - path.extension(file.path) == '.cc' - ? ccContentPair.formatted - : hContentPair.formatted, + path.extension(file.path) == '.cc' ? ccContentPair.formatted : hContentPair.formatted, ); case target.FormatCheck.dart: - return FileContentPair( - content, - dartContentPair.formatted, - ); + return FileContentPair(content, dartContentPair.formatted); case target.FormatCheck.gn: - return FileContentPair( - content, - gnContentPair.formatted, - ); + return FileContentPair(content, gnContentPair.formatted); case target.FormatCheck.java: - return FileContentPair( - content, - javaContentPair.formatted, - ); + return FileContentPair(content, javaContentPair.formatted); case target.FormatCheck.python: - return FileContentPair( - content, - pythonContentPair.formatted, - ); + return FileContentPair(content, pythonContentPair.formatted); case target.FormatCheck.whitespace: - return FileContentPair( - content, - whitespaceContentPair.formatted, - ); + return FileContentPair(content, whitespaceContentPair.formatted); case target.FormatCheck.header: - return FileContentPair( - content, - headerContentPair.formatted, - ); + return FileContentPair(content, headerContentPair.formatted); } }); } } void main() { - final String formatterPath = - '${repoDir.path}/ci/format.${io.Platform.isWindows ? 'bat' : 'sh'}'; + final String formatterPath = '${repoDir.path}/ci/format.${io.Platform.isWindows ? 'bat' : 'sh'}'; test('Can fix C++ formatting errors', () { final TestFileFixture fixture = TestFileFixture(target.FormatCheck.clang); try { fixture.gitAdd(); - io.Process.runSync(formatterPath, ['--check', 'clang', '--fix'], - workingDirectory: repoDir.path); + io.Process.runSync(formatterPath, [ + '--check', + 'clang', + '--fix', + ], workingDirectory: repoDir.path); final Iterable files = fixture.getFileContents(); for (final FileContentPair pair in files) { @@ -181,8 +167,11 @@ void main() { final TestFileFixture fixture = TestFileFixture(target.FormatCheck.dart); try { fixture.gitAdd(); - io.Process.runSync(formatterPath, ['--check', 'dart', '--fix'], - workingDirectory: repoDir.path); + io.Process.runSync(formatterPath, [ + '--check', + 'dart', + '--fix', + ], workingDirectory: repoDir.path); final Iterable files = fixture.getFileContents(); for (final FileContentPair pair in files) { @@ -201,10 +190,11 @@ void main() { try { fixture.gitAdd(); - final io.ProcessResult result = io.Process.runSync( - formatterPath, ['--check', 'dart', '--fix'], - workingDirectory: repoDir.path, - ); + final io.ProcessResult result = io.Process.runSync(formatterPath, [ + '--check', + 'dart', + '--fix', + ], workingDirectory: repoDir.path); expect(result.stdout, contains('format_test2.dart produced the following error')); expect(result.exitCode, isNot(0)); } finally { @@ -216,8 +206,11 @@ void main() { final TestFileFixture fixture = TestFileFixture(target.FormatCheck.gn); try { fixture.gitAdd(); - io.Process.runSync(formatterPath, ['--check', 'gn', '--fix'], - workingDirectory: repoDir.path); + io.Process.runSync(formatterPath, [ + '--check', + 'gn', + '--fix', + ], workingDirectory: repoDir.path); final Iterable files = fixture.getFileContents(); for (final FileContentPair pair in files) { @@ -232,8 +225,11 @@ void main() { final TestFileFixture fixture = TestFileFixture(target.FormatCheck.java); try { fixture.gitAdd(); - io.Process.runSync(formatterPath, ['--check', 'java', '--fix'], - workingDirectory: repoDir.path); + io.Process.runSync(formatterPath, [ + '--check', + 'java', + '--fix', + ], workingDirectory: repoDir.path); final Iterable files = fixture.getFileContents(); for (final FileContentPair pair in files) { @@ -248,8 +244,11 @@ void main() { final TestFileFixture fixture = TestFileFixture(target.FormatCheck.python); try { fixture.gitAdd(); - io.Process.runSync(formatterPath, ['--check', 'python', '--fix'], - workingDirectory: repoDir.path); + io.Process.runSync(formatterPath, [ + '--check', + 'python', + '--fix', + ], workingDirectory: repoDir.path); final Iterable files = fixture.getFileContents(); for (final FileContentPair pair in files) { @@ -261,13 +260,14 @@ void main() { }); test('Can fix whitespace formatting errors', () { - final TestFileFixture fixture = - TestFileFixture(target.FormatCheck.whitespace); + final TestFileFixture fixture = TestFileFixture(target.FormatCheck.whitespace); try { fixture.gitAdd(); - io.Process.runSync( - formatterPath, ['--check', 'whitespace', '--fix'], - workingDirectory: repoDir.path); + io.Process.runSync(formatterPath, [ + '--check', + 'whitespace', + '--fix', + ], workingDirectory: repoDir.path); final Iterable files = fixture.getFileContents(); for (final FileContentPair pair in files) { @@ -282,11 +282,11 @@ void main() { final TestFileFixture fixture = TestFileFixture(target.FormatCheck.header); try { fixture.gitAdd(); - io.Process.runSync( - formatterPath, - ['--check', 'header', '--fix'], - workingDirectory: repoDir.path, - ); + io.Process.runSync(formatterPath, [ + '--check', + 'header', + '--fix', + ], workingDirectory: repoDir.path); final Iterable files = fixture.getFileContents(); for (final FileContentPair pair in files) { expect(pair.original, equals(pair.formatted)); diff --git a/examples/glfw/main.dart b/examples/glfw/main.dart index ceb5eda137751..1edbdc8013c29 100644 --- a/examples/glfw/main.dart +++ b/examples/glfw/main.dart @@ -4,8 +4,7 @@ import 'package:flutter_gpu/gpu.dart' as gpu; import 'package:flutter/material.dart'; -import 'package:flutter/foundation.dart' - show debugDefaultTargetPlatformOverride; +import 'package:flutter/foundation.dart' show debugDefaultTargetPlatformOverride; void main() { // Ensure Flutter GPU symbols are available by forcing the GPU context to instantiate. @@ -14,8 +13,9 @@ void main() { gpu.gpuContext; // Force the context to instantiate. } catch (e) { // If impeller is not enabled, make sure the exception isn't about symbols missing. - assert(e.toString().contains( - 'Flutter GPU requires the Impeller rendering backend to be enabled.')); + assert( + e.toString().contains('Flutter GPU requires the Impeller rendering backend to be enabled.'), + ); } // This is a hack to make Flutter think you are running on Google Fuchsia, @@ -113,13 +113,8 @@ class _MyHomePageState extends State { // horizontal). mainAxisAlignment: MainAxisAlignment.center, children: [ - Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headlineMedium, - ), + Text('You have pushed the button this many times:'), + Text('$_counter', style: Theme.of(context).textTheme.headlineMedium), ], ), ), diff --git a/examples/glfw_drm/main.dart b/examples/glfw_drm/main.dart index d8ad9221f8e4c..3de623b571fb2 100644 --- a/examples/glfw_drm/main.dart +++ b/examples/glfw_drm/main.dart @@ -3,11 +3,9 @@ // found in the LICENSE file. import 'package:flutter/material.dart'; -import 'package:flutter/foundation.dart' - show debugDefaultTargetPlatformOverride; +import 'package:flutter/foundation.dart' show debugDefaultTargetPlatformOverride; import 'package:flutter_spinkit/flutter_spinkit.dart'; - void main() { // This is a hack to make Flutter think you are running on Google Fuchsia, // otherwise you will get an error about running from an unsupported platform. @@ -71,9 +69,7 @@ class _MyHomePageState extends State { body: Center( // Center is a layout widget. It takes a single child and positions it // in the middle of the parent. - child: RepaintBoundary( - child: SpinKitRotatingCircle(color: Colors.blue, size: 50.0), - ), + child: RepaintBoundary(child: SpinKitRotatingCircle(color: Colors.blue, size: 50.0)), ), ); } diff --git a/flutter_frontend_server/test/to_string_test.dart b/flutter_frontend_server/test/to_string_test.dart index 770f5866ac669..03c4a461abe10 100644 --- a/flutter_frontend_server/test/to_string_test.dart +++ b/flutter_frontend_server/test/to_string_test.dart @@ -15,15 +15,8 @@ void main() { if (buildDir == null) { fail('No build directory found. Set FLUTTER_BUILD_DIRECTORY'); } - final frontendServer = path.join( - buildDir, - 'gen', - 'frontend_server_aot.dart.snapshot', - ); - final sdkRoot = path.join( - buildDir, - 'flutter_patched_sdk', - ); + final frontendServer = path.join(buildDir, 'gen', 'frontend_server_aot.dart.snapshot'); + final sdkRoot = path.join(buildDir, 'flutter_patched_sdk'); final String dart = io.Platform.resolvedExecutable; final String dartaotruntime = path.join( @@ -35,11 +28,7 @@ void main() { final basePath = path.join(engineDir, 'flutter_frontend_server'); final fixtures = path.join(basePath, 'test', 'fixtures'); final mainDart = path.join(fixtures, 'lib', 'main.dart'); - final packageConfig = path.join( - fixtures, - '.dart_tool', - 'package_config.json', - ); + final packageConfig = path.join(fixtures, '.dart_tool', 'package_config.json'); final regularDill = path.join(fixtures, 'toString.dill'); final transformedDill = path.join(fixtures, 'toStringTransformed.dill'); @@ -50,23 +39,26 @@ void main() { } test('Without flag', () { - checkProcessResult(io.Process.runSync(dartaotruntime, [ - frontendServer, - '--sdk-root=$sdkRoot', - '--target=flutter', - '--packages=$packageConfig', - '--output-dill=$regularDill', - mainDart, - ])); + checkProcessResult( + io.Process.runSync(dartaotruntime, [ + frontendServer, + '--sdk-root=$sdkRoot', + '--target=flutter', + '--packages=$packageConfig', + '--output-dill=$regularDill', + mainDart, + ]), + ); final runResult = io.Process.runSync(dart, [regularDill]); checkProcessResult(runResult); var paintString = - '"Paint.toString":"Paint(Color(alpha: 1.0000, red: 1.0000, green: 1.0000, blue: 1.0000, colorSpace: ColorSpace.sRGB))"'; + '"Paint.toString":"Paint(Color(alpha: 1.0000, red: 1.0000, green: 1.0000, blue: 1.0000, colorSpace: ColorSpace.sRGB))"'; if (buildDir.contains('release')) { paintString = '"Paint.toString":"Instance of \'Paint\'"'; } - final String expectedStdout = '{$paintString,' + final String expectedStdout = + '{$paintString,' '"Brightness.toString":"Brightness.dark",' '"Foo.toString":"I am a Foo",' '"Keep.toString":"I am a Keep"}'; @@ -75,22 +67,25 @@ void main() { }); test('With flag', () { - checkProcessResult(io.Process.runSync(dartaotruntime, [ - frontendServer, - '--sdk-root=$sdkRoot', - '--target=flutter', - '--packages=$packageConfig', - '--output-dill=$transformedDill', - '--delete-tostring-package-uri', - 'dart:ui', - '--delete-tostring-package-uri', - 'package:flutter_frontend_fixtures', - mainDart, - ])); + checkProcessResult( + io.Process.runSync(dartaotruntime, [ + frontendServer, + '--sdk-root=$sdkRoot', + '--target=flutter', + '--packages=$packageConfig', + '--output-dill=$transformedDill', + '--delete-tostring-package-uri', + 'dart:ui', + '--delete-tostring-package-uri', + 'package:flutter_frontend_fixtures', + mainDart, + ]), + ); final runResult = io.Process.runSync(dart, [transformedDill]); checkProcessResult(runResult); - const expectedStdout = '{"Paint.toString":"Instance of \'Paint\'",' + const expectedStdout = + '{"Paint.toString":"Instance of \'Paint\'",' '"Brightness.toString":"Brightness.dark",' '"Foo.toString":"Instance of \'Foo\'",' '"Keep.toString":"I am a Keep"}'; diff --git a/impeller/fixtures/dart_tests.dart b/impeller/fixtures/dart_tests.dart index c585b256f8d80..f1e8a952a863a 100644 --- a/impeller/fixtures/dart_tests.dart +++ b/impeller/fixtures/dart_tests.dart @@ -37,8 +37,7 @@ void canCreateShaderLibrary() { void canReflectUniformStructs() { final gpu.RenderPipeline pipeline = createUnlitRenderPipeline(); - final gpu.UniformSlot vertInfo = - pipeline.vertexShader.getUniformSlot('VertInfo'); + final gpu.UniformSlot vertInfo = pipeline.vertexShader.getUniformSlot('VertInfo'); assert(vertInfo.uniformName == 'VertInfo'); final int? totalSize = vertInfo.sizeInBytes; assert(totalSize != null); @@ -67,8 +66,11 @@ ByteData float32(List values) { @pragma('vm:entry-point') void canCreateRenderPassAndSubmit(int width, int height) { - final gpu.Texture? renderTexture = gpu.gpuContext - .createTexture(gpu.StorageMode.devicePrivate, width, height); + final gpu.Texture? renderTexture = gpu.gpuContext.createTexture( + gpu.StorageMode.devicePrivate, + width, + height, + ); assert(renderTexture != null); final gpu.CommandBuffer commandBuffer = gpu.gpuContext.createCommandBuffer(); @@ -86,22 +88,25 @@ void canCreateRenderPassAndSubmit(int width, int height) { encoder.setColorBlendEquation(gpu.ColorBlendEquation()); final gpu.HostBuffer transients = gpu.gpuContext.createHostBuffer(); - final gpu.BufferView vertices = transients.emplace(float32([ - -0.5, 0.5, // - 0.0, -0.5, // - 0.5, 0.5, // - ])); - final gpu.BufferView vertInfoData = transients.emplace(float32([ - 1, 0, 0, 0, // mvp - 0, 1, 0, 0, // mvp - 0, 0, 1, 0, // mvp - 0, 0, 0, 1, // mvp - 0, 1, 0, 1, // color - ])); + final gpu.BufferView vertices = transients.emplace( + float32([ + -0.5, 0.5, // + 0.0, -0.5, // + 0.5, 0.5, // + ]), + ); + final gpu.BufferView vertInfoData = transients.emplace( + float32([ + 1, 0, 0, 0, // mvp + 0, 1, 0, 0, // mvp + 0, 0, 1, 0, // mvp + 0, 0, 0, 1, // mvp + 0, 1, 0, 1, // color + ]), + ); encoder.bindVertexBuffer(vertices, 3); - final gpu.UniformSlot vertInfo = - pipeline.vertexShader.getUniformSlot('VertInfo'); + final gpu.UniformSlot vertInfo = pipeline.vertexShader.getUniformSlot('VertInfo'); encoder.bindUniform(vertInfo, vertInfoData); encoder.draw(); diff --git a/impeller/tessellator/dart/lib/tessellator.dart b/impeller/tessellator/dart/lib/tessellator.dart index f899b2f630eb7..e228620fe9b7a 100644 --- a/impeller/tessellator/dart/lib/tessellator.dart +++ b/impeller/tessellator/dart/lib/tessellator.dart @@ -29,11 +29,7 @@ enum FillType { /// Used by [VerticesBuilder.tessellate]. class SmoothingApproximation { /// Creates a new smoothing approximation instance with default values. - const SmoothingApproximation({ - this.scale = 1.0, - this.angleTolerance = 0.0, - this.cuspLimit = 0.0, - }); + const SmoothingApproximation({this.scale = 1.0, this.angleTolerance = 0.0, this.cuspLimit = 0.0}); /// The scaling coefficient to use when translating to screen coordinates. /// @@ -85,14 +81,7 @@ class VerticesBuilder { /// Adds a cubic Bezier curve with x1,y1 as the first control point, x2,y2 as /// the second control point, and end point x3,y3. - void cubicTo( - double x1, - double y1, - double x2, - double y2, - double x3, - double y3, - ) { + void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3) { assert(_builder != null); _cubicToFn(_builder!, x1, y1, x2, y2, x3, y3); } @@ -159,91 +148,63 @@ final class _PathBuilder extends ffi.Opaque {} typedef _CreatePathBuilderType = ffi.Pointer<_PathBuilder> Function(); typedef _create_path_builder_type = ffi.Pointer<_PathBuilder> Function(); -final _CreatePathBuilderType _createPathFn = - _dylib.lookupFunction<_create_path_builder_type, _CreatePathBuilderType>( - 'CreatePathBuilder', -); +final _CreatePathBuilderType _createPathFn = _dylib + .lookupFunction<_create_path_builder_type, _CreatePathBuilderType>('CreatePathBuilder'); typedef _MoveToType = void Function(ffi.Pointer<_PathBuilder>, double, double); -typedef _move_to_type = ffi.Void Function( - ffi.Pointer<_PathBuilder>, - ffi.Float, - ffi.Float, -); +typedef _move_to_type = ffi.Void Function(ffi.Pointer<_PathBuilder>, ffi.Float, ffi.Float); -final _MoveToType _moveToFn = _dylib.lookupFunction<_move_to_type, _MoveToType>( - 'MoveTo', -); +final _MoveToType _moveToFn = _dylib.lookupFunction<_move_to_type, _MoveToType>('MoveTo'); typedef _LineToType = void Function(ffi.Pointer<_PathBuilder>, double, double); -typedef _line_to_type = ffi.Void Function( - ffi.Pointer<_PathBuilder>, - ffi.Float, - ffi.Float, -); - -final _LineToType _lineToFn = _dylib.lookupFunction<_line_to_type, _LineToType>( - 'LineTo', -); - -typedef _CubicToType = void Function( - ffi.Pointer<_PathBuilder>, - double, - double, - double, - double, - double, - double, -); -typedef _cubic_to_type = ffi.Void Function( - ffi.Pointer<_PathBuilder>, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, -); +typedef _line_to_type = ffi.Void Function(ffi.Pointer<_PathBuilder>, ffi.Float, ffi.Float); + +final _LineToType _lineToFn = _dylib.lookupFunction<_line_to_type, _LineToType>('LineTo'); + +typedef _CubicToType = + void Function(ffi.Pointer<_PathBuilder>, double, double, double, double, double, double); +typedef _cubic_to_type = + ffi.Void Function( + ffi.Pointer<_PathBuilder>, + ffi.Float, + ffi.Float, + ffi.Float, + ffi.Float, + ffi.Float, + ffi.Float, + ); -final _CubicToType _cubicToFn = - _dylib.lookupFunction<_cubic_to_type, _CubicToType>('CubicTo'); +final _CubicToType _cubicToFn = _dylib.lookupFunction<_cubic_to_type, _CubicToType>('CubicTo'); typedef _CloseType = void Function(ffi.Pointer<_PathBuilder>, bool); typedef _close_type = ffi.Void Function(ffi.Pointer<_PathBuilder>, ffi.Bool); -final _CloseType _closeFn = - _dylib.lookupFunction<_close_type, _CloseType>('Close'); +final _CloseType _closeFn = _dylib.lookupFunction<_close_type, _CloseType>('Close'); + +typedef _TessellateType = + ffi.Pointer<_Vertices> Function(ffi.Pointer<_PathBuilder>, int, double, double, double); +typedef _tessellate_type = + ffi.Pointer<_Vertices> Function( + ffi.Pointer<_PathBuilder>, + ffi.Int, + ffi.Float, + ffi.Float, + ffi.Float, + ); -typedef _TessellateType = ffi.Pointer<_Vertices> Function( - ffi.Pointer<_PathBuilder>, - int, - double, - double, - double, +final _TessellateType _tessellateFn = _dylib.lookupFunction<_tessellate_type, _TessellateType>( + 'Tessellate', ); -typedef _tessellate_type = ffi.Pointer<_Vertices> Function( - ffi.Pointer<_PathBuilder>, - ffi.Int, - ffi.Float, - ffi.Float, - ffi.Float, -); - -final _TessellateType _tessellateFn = - _dylib.lookupFunction<_tessellate_type, _TessellateType>('Tessellate'); typedef _DestroyType = void Function(ffi.Pointer<_PathBuilder>); typedef _destroy_type = ffi.Void Function(ffi.Pointer<_PathBuilder>); -final _DestroyType _destroyFn = - _dylib.lookupFunction<_destroy_type, _DestroyType>( +final _DestroyType _destroyFn = _dylib.lookupFunction<_destroy_type, _DestroyType>( 'DestroyPathBuilder', ); typedef _DestroyVerticesType = void Function(ffi.Pointer<_Vertices>); typedef _destroy_vertices_type = ffi.Void Function(ffi.Pointer<_Vertices>); -final _DestroyVerticesType _destroyVerticesFn = - _dylib.lookupFunction<_destroy_vertices_type, _DestroyVerticesType>( - 'DestroyVertices', -); +final _DestroyVerticesType _destroyVerticesFn = _dylib + .lookupFunction<_destroy_vertices_type, _DestroyVerticesType>('DestroyVertices'); diff --git a/lib/gpu/lib/src/buffer.dart b/lib/gpu/lib/src/buffer.dart index fd3ae464f88be..aed4cff4e8149 100644 --- a/lib/gpu/lib/src/buffer.dart +++ b/lib/gpu/lib/src/buffer.dart @@ -19,8 +19,11 @@ class BufferView { final int lengthInBytes; /// Create a new view into a buffer on the GPU. - const BufferView(this.buffer, - {required this.offsetInBytes, required this.lengthInBytes}); + const BufferView( + this.buffer, { + required this.offsetInBytes, + required this.lengthInBytes, + }); } /// [DeviceBuffer] is a region of memory allocated on the device heap @@ -33,49 +36,83 @@ base class DeviceBuffer extends NativeFieldWrapperClass1 { /// Creates a new DeviceBuffer. DeviceBuffer._initialize( - GpuContext gpuContext, StorageMode storageMode, int sizeInBytes) - : storageMode = storageMode, - sizeInBytes = sizeInBytes { + GpuContext gpuContext, + StorageMode storageMode, + int sizeInBytes, + ) : storageMode = storageMode, + sizeInBytes = sizeInBytes { _valid = _initialize(gpuContext, storageMode.index, sizeInBytes); } /// Creates a new host visible DeviceBuffer with data copied from the host. DeviceBuffer._initializeWithHostData(GpuContext gpuContext, ByteData data) - : storageMode = StorageMode.hostVisible, - sizeInBytes = data.lengthInBytes { + : storageMode = StorageMode.hostVisible, + sizeInBytes = data.lengthInBytes { _valid = _initializeWithHostData(gpuContext, data); } final StorageMode storageMode; final int sizeInBytes; - void _bindAsVertexBuffer(RenderPass renderPass, int offsetInBytes, - int lengthInBytes, int vertexCount) { + void _bindAsVertexBuffer( + RenderPass renderPass, + int offsetInBytes, + int lengthInBytes, + int vertexCount, + ) { renderPass._bindVertexBufferDevice( - this, offsetInBytes, lengthInBytes, vertexCount); + this, + offsetInBytes, + lengthInBytes, + vertexCount, + ); } - void _bindAsIndexBuffer(RenderPass renderPass, int offsetInBytes, - int lengthInBytes, IndexType indexType, int indexCount) { + void _bindAsIndexBuffer( + RenderPass renderPass, + int offsetInBytes, + int lengthInBytes, + IndexType indexType, + int indexCount, + ) { renderPass._bindIndexBufferDevice( - this, offsetInBytes, lengthInBytes, indexType.index, indexCount); + this, + offsetInBytes, + lengthInBytes, + indexType.index, + indexCount, + ); } - bool _bindAsUniform(RenderPass renderPass, UniformSlot slot, - int offsetInBytes, int lengthInBytes) { + bool _bindAsUniform( + RenderPass renderPass, + UniformSlot slot, + int offsetInBytes, + int lengthInBytes, + ) { return renderPass._bindUniformDevice( - slot.shader, slot.uniformName, this, offsetInBytes, lengthInBytes); + slot.shader, + slot.uniformName, + this, + offsetInBytes, + lengthInBytes, + ); } /// Wrap with native counterpart. @Native, Int, Int)>( - symbol: 'InternalFlutterGpu_DeviceBuffer_Initialize') + symbol: 'InternalFlutterGpu_DeviceBuffer_Initialize', + ) external bool _initialize( - GpuContext gpuContext, int storageMode, int sizeInBytes); + GpuContext gpuContext, + int storageMode, + int sizeInBytes, + ); /// Wrap with native counterpart. @Native, Handle)>( - symbol: 'InternalFlutterGpu_DeviceBuffer_InitializeWithHostData') + symbol: 'InternalFlutterGpu_DeviceBuffer_InitializeWithHostData', + ) external bool _initializeWithHostData(GpuContext gpuContext, ByteData data); /// Overwrite a range of bytes within an existing [DeviceBuffer]. @@ -97,7 +134,8 @@ base class DeviceBuffer extends NativeFieldWrapperClass1 { bool overwrite(ByteData sourceBytes, {int destinationOffsetInBytes = 0}) { if (storageMode != StorageMode.hostVisible) { throw Exception( - 'DeviceBuffer.overwrite can only be used with DeviceBuffers that are host visible'); + 'DeviceBuffer.overwrite can only be used with DeviceBuffers that are host visible', + ); } if (destinationOffsetInBytes < 0) { throw Exception('destinationOffsetInBytes must be positive'); @@ -106,7 +144,8 @@ base class DeviceBuffer extends NativeFieldWrapperClass1 { } @Native, Handle, Int)>( - symbol: 'InternalFlutterGpu_DeviceBuffer_Overwrite') + symbol: 'InternalFlutterGpu_DeviceBuffer_Overwrite', + ) external bool _overwrite(ByteData bytes, int destinationOffsetInBytes); /// Flush the contents of the [DeviceBuffer] to the GPU. @@ -121,7 +160,8 @@ base class DeviceBuffer extends NativeFieldWrapperClass1 { void flush({int offsetInBytes = 0, int lengthInBytes = -1}) { if (storageMode != StorageMode.hostVisible) { throw Exception( - 'DeviceBuffer.flush can only be used with DeviceBuffers that are host visible'); + 'DeviceBuffer.flush can only be used with DeviceBuffers that are host visible', + ); } if (offsetInBytes < 0 || offsetInBytes >= sizeInBytes) { throw Exception('offsetInBytes must be within the bounds of the buffer'); @@ -131,13 +171,15 @@ base class DeviceBuffer extends NativeFieldWrapperClass1 { } if (lengthInBytes != -1 && offsetInBytes + lengthInBytes > sizeInBytes) { throw Exception( - 'The provided range must not be too large to fit within the buffer'); + 'The provided range must not be too large to fit within the buffer', + ); } _flush(offsetInBytes, lengthInBytes); } @Native, Int, Int)>( - symbol: 'InternalFlutterGpu_DeviceBuffer_Flush') + symbol: 'InternalFlutterGpu_DeviceBuffer_Flush', + ) external void _flush(int offsetInBytes, int lengthInBytes); } @@ -186,8 +228,10 @@ base class HostBuffer { final List> _buffers = []; /// Creates a new HostBuffer. - HostBuffer._initialize(this._gpuContext, - {this.blockLengthInBytes = HostBuffer.kDefaultBlockLengthInBytes}) { + HostBuffer._initialize( + this._gpuContext, { + this.blockLengthInBytes = HostBuffer.kDefaultBlockLengthInBytes, + }) { for (int i = 0; i < frameCount; i++) { List frame = []; _buffers.add(frame); @@ -196,8 +240,10 @@ base class HostBuffer { } DeviceBuffer _allocateNewBlock(length) { - final buffer = - _gpuContext.createDeviceBuffer(StorageMode.hostVisible, length); + final buffer = _gpuContext.createDeviceBuffer( + StorageMode.hostVisible, + length, + ); if (buffer == null) { throw Exception('Failed to allocate DeviceBuffer of length $length'); } @@ -208,11 +254,15 @@ base class HostBuffer { /// Allocates a new block if necessary. BufferView _allocateEmplacement(ByteData bytes) { if (bytes.lengthInBytes > blockLengthInBytes) { - return BufferView(_allocateNewBlock(bytes.lengthInBytes), - offsetInBytes: 0, lengthInBytes: bytes.lengthInBytes); + return BufferView( + _allocateNewBlock(bytes.lengthInBytes), + offsetInBytes: 0, + lengthInBytes: bytes.lengthInBytes, + ); } - int padding = _gpuContext.minimumUniformByteAlignment - + int padding = + _gpuContext.minimumUniformByteAlignment - (_offsetCursor % _gpuContext.minimumUniformByteAlignment); // If the padding is the full alignment size, then we're already aligned. // So reset the padding to zero. @@ -223,13 +273,19 @@ base class HostBuffer { _bufferCursor++; _offsetCursor = bytes.lengthInBytes; - return BufferView(buffer, - offsetInBytes: 0, lengthInBytes: blockLengthInBytes); + return BufferView( + buffer, + offsetInBytes: 0, + lengthInBytes: blockLengthInBytes, + ); } _offsetCursor += padding; - final view = BufferView(_buffers[_frameCursor][_bufferCursor], - offsetInBytes: _offsetCursor, lengthInBytes: bytes.lengthInBytes); + final view = BufferView( + _buffers[_frameCursor][_bufferCursor], + offsetInBytes: _offsetCursor, + lengthInBytes: bytes.lengthInBytes, + ); _offsetCursor += bytes.lengthInBytes; return view; } @@ -246,11 +302,14 @@ base class HostBuffer { /// referencing it in a command. BufferView emplace(ByteData bytes) { BufferView view = _allocateEmplacement(bytes); - if (!view.buffer - .overwrite(bytes, destinationOffsetInBytes: view.offsetInBytes)) { + if (!view.buffer.overwrite( + bytes, + destinationOffsetInBytes: view.offsetInBytes, + )) { throw Exception( - 'Failed to write range (offset=${view.offsetInBytes}, length=${view.lengthInBytes}) ' - 'to HostBuffer-managed DeviceBuffer (frame=$_frameCursor, buffer=$_bufferCursor, offset=$_offsetCursor).'); + 'Failed to write range (offset=${view.offsetInBytes}, length=${view.lengthInBytes}) ' + 'to HostBuffer-managed DeviceBuffer (frame=$_frameCursor, buffer=$_bufferCursor, offset=$_offsetCursor).', + ); } return view; diff --git a/lib/gpu/lib/src/command_buffer.dart b/lib/gpu/lib/src/command_buffer.dart index 20c0137ce69aa..4d4a24ee70ea3 100644 --- a/lib/gpu/lib/src/command_buffer.dart +++ b/lib/gpu/lib/src/command_buffer.dart @@ -29,10 +29,12 @@ base class CommandBuffer extends NativeFieldWrapperClass1 { /// Wrap with native counterpart. @Native)>( - symbol: 'InternalFlutterGpu_CommandBuffer_Initialize') + symbol: 'InternalFlutterGpu_CommandBuffer_Initialize', + ) external bool _initialize(GpuContext gpuContext); @Native, Handle)>( - symbol: 'InternalFlutterGpu_CommandBuffer_Submit') + symbol: 'InternalFlutterGpu_CommandBuffer_Submit', + ) external String? _submit(CompletionCallback? completionCallback); } diff --git a/lib/gpu/lib/src/context.dart b/lib/gpu/lib/src/context.dart index 5b62f6685ce59..6574b215e646f 100644 --- a/lib/gpu/lib/src/context.dart +++ b/lib/gpu/lib/src/context.dart @@ -67,10 +67,14 @@ base class GpuContext extends NativeFieldWrapperClass1 { DeviceBuffer? createDeviceBuffer(StorageMode storageMode, int sizeInBytes) { if (storageMode == StorageMode.deviceTransient) { throw Exception( - 'DeviceBuffers cannot be set to StorageMode.deviceTransient'); + 'DeviceBuffers cannot be set to StorageMode.deviceTransient', + ); } - DeviceBuffer result = - DeviceBuffer._initialize(this, storageMode, sizeInBytes); + DeviceBuffer result = DeviceBuffer._initialize( + this, + storageMode, + sizeInBytes, + ); return result.isValid ? result : null; } @@ -90,33 +94,39 @@ base class GpuContext extends NativeFieldWrapperClass1 { /// Creates a bump allocator that managed a [DeviceBuffer] block list. /// /// See also [HostBuffer]. - HostBuffer createHostBuffer( - {int blockLengthInBytes = HostBuffer.kDefaultBlockLengthInBytes}) { + HostBuffer createHostBuffer({ + int blockLengthInBytes = HostBuffer.kDefaultBlockLengthInBytes, + }) { return HostBuffer._initialize(this, blockLengthInBytes: blockLengthInBytes); } /// Allocates a new texture in GPU-resident memory. /// /// Returns [null] if the [Texture] creation failed. - Texture? createTexture(StorageMode storageMode, int width, int height, - {PixelFormat format = PixelFormat.r8g8b8a8UNormInt, - sampleCount = 1, - TextureCoordinateSystem coordinateSystem = - TextureCoordinateSystem.renderToTexture, - bool enableRenderTargetUsage = true, - bool enableShaderReadUsage = true, - bool enableShaderWriteUsage = false}) { + Texture? createTexture( + StorageMode storageMode, + int width, + int height, { + PixelFormat format = PixelFormat.r8g8b8a8UNormInt, + sampleCount = 1, + TextureCoordinateSystem coordinateSystem = + TextureCoordinateSystem.renderToTexture, + bool enableRenderTargetUsage = true, + bool enableShaderReadUsage = true, + bool enableShaderWriteUsage = false, + }) { Texture result = Texture._initialize( - this, - storageMode, - format, - width, - height, - sampleCount, - coordinateSystem, - enableRenderTargetUsage, - enableShaderReadUsage, - enableShaderWriteUsage); + this, + storageMode, + format, + width, + height, + sampleCount, + coordinateSystem, + enableRenderTargetUsage, + enableShaderReadUsage, + enableShaderWriteUsage, + ); return result.isValid ? result : null; } @@ -126,33 +136,41 @@ base class GpuContext extends NativeFieldWrapperClass1 { } RenderPipeline createRenderPipeline( - Shader vertexShader, Shader fragmentShader) { + Shader vertexShader, + Shader fragmentShader, + ) { return RenderPipeline._(this, vertexShader, fragmentShader); } /// Associates the default Impeller context with this Context. @Native( - symbol: 'InternalFlutterGpu_Context_InitializeDefault') + symbol: 'InternalFlutterGpu_Context_InitializeDefault', + ) external String? _initializeDefault(); @Native)>( - symbol: 'InternalFlutterGpu_Context_GetDefaultColorFormat') + symbol: 'InternalFlutterGpu_Context_GetDefaultColorFormat', + ) external int _getDefaultColorFormat(); @Native)>( - symbol: 'InternalFlutterGpu_Context_GetDefaultStencilFormat') + symbol: 'InternalFlutterGpu_Context_GetDefaultStencilFormat', + ) external int _getDefaultStencilFormat(); @Native)>( - symbol: 'InternalFlutterGpu_Context_GetDefaultDepthStencilFormat') + symbol: 'InternalFlutterGpu_Context_GetDefaultDepthStencilFormat', + ) external int _getDefaultDepthStencilFormat(); @Native)>( - symbol: 'InternalFlutterGpu_Context_GetMinimumUniformByteAlignment') + symbol: 'InternalFlutterGpu_Context_GetMinimumUniformByteAlignment', + ) external int _getMinimumUniformByteAlignment(); @Native)>( - symbol: 'InternalFlutterGpu_Context_GetSupportsOffscreenMSAA') + symbol: 'InternalFlutterGpu_Context_GetSupportsOffscreenMSAA', + ) external bool _getSupportsOffscreenMSAA(); } diff --git a/lib/gpu/lib/src/formats.dart b/lib/gpu/lib/src/formats.dart index a7a5f51b88e82..2bfa6eb18043e 100644 --- a/lib/gpu/lib/src/formats.dart +++ b/lib/gpu/lib/src/formats.dart @@ -82,17 +82,9 @@ enum BlendFactor { oneMinusBlendAlpha, } -enum BlendOperation { - add, - subtract, - reverseSubtract, -} +enum BlendOperation { add, subtract, reverseSubtract } -enum LoadAction { - dontCare, - load, - clear, -} +enum LoadAction { dontCare, load, clear } enum StoreAction { dontCare, @@ -101,55 +93,23 @@ enum StoreAction { storeAndMultisampleResolve, } -enum ShaderStage { - vertex, - fragment, -} +enum ShaderStage { vertex, fragment } -enum MinMagFilter { - nearest, - linear, -} +enum MinMagFilter { nearest, linear } -enum MipFilter { - nearest, - linear, -} +enum MipFilter { nearest, linear } -enum SamplerAddressMode { - clampToEdge, - repeat, - mirror, -} +enum SamplerAddressMode { clampToEdge, repeat, mirror } -enum IndexType { - int16, - int32, -} +enum IndexType { int16, int32 } -enum PrimitiveType { - triangle, - triangleStrip, - line, - lineStrip, - point, -} +enum PrimitiveType { triangle, triangleStrip, line, lineStrip, point } -enum CullMode { - none, - frontFace, - backFace, -} +enum CullMode { none, frontFace, backFace } -enum WindingOrder { - clockwise, - counterClockwise, -} +enum WindingOrder { clockwise, counterClockwise } -enum PolygonMode { - fill, - line -} +enum PolygonMode { fill, line } enum CompareFunction { /// Comparison test never passes. diff --git a/lib/gpu/lib/src/render_pass.dart b/lib/gpu/lib/src/render_pass.dart index 6dd60ab548a85..8ef52556d0e50 100644 --- a/lib/gpu/lib/src/render_pass.dart +++ b/lib/gpu/lib/src/render_pass.dart @@ -26,29 +26,35 @@ base class ColorAttachment { if (resolveTexture != null) { if (resolveTexture!.format != texture.format) { throw Exception( - "ColorAttachment MSAA resolve texture must have the same format as the texture"); + "ColorAttachment MSAA resolve texture must have the same format as the texture", + ); } if (resolveTexture!.width != texture.width || resolveTexture!.height != texture.height) { throw Exception( - "ColorAttachment MSAA resolve texture must have the same dimensions as the texture"); + "ColorAttachment MSAA resolve texture must have the same dimensions as the texture", + ); } if (resolveTexture!.sampleCount != 1) { throw Exception( - "ColorAttachment MSAA resolve texture must have a sample count of 1"); + "ColorAttachment MSAA resolve texture must have a sample count of 1", + ); } if (texture.sampleCount <= 1) { throw Exception( - "ColorAttachment must have a sample count greater than 1 when a MSAA resolve texture is set"); + "ColorAttachment must have a sample count greater than 1 when a MSAA resolve texture is set", + ); } if (storeAction != StoreAction.multisampleResolve && storeAction != StoreAction.storeAndMultisampleResolve) { throw Exception( - "ColorAttachment StoreAction must be multisampleResolve or storeAndMultisampleResolve when a resolve texture is set"); + "ColorAttachment StoreAction must be multisampleResolve or storeAndMultisampleResolve when a resolve texture is set", + ); } if (resolveTexture!.storageMode == StorageMode.deviceTransient) { throw Exception( - "ColorAttachment MSAA resolve texture must not have a storage mode of deviceTransient"); + "ColorAttachment MSAA resolve texture must not have a storage mode of deviceTransient", + ); } } } @@ -79,11 +85,13 @@ base class DepthStencilAttachment { if (texture.storageMode == StorageMode.deviceTransient) { if (depthLoadAction == LoadAction.load) { throw Exception( - "DepthStencilAttachment depthLoadAction must not be load when the texture has a storage mode of deviceTransient"); + "DepthStencilAttachment depthLoadAction must not be load when the texture has a storage mode of deviceTransient", + ); } if (stencilLoadAction == LoadAction.load) { throw Exception( - "DepthStencilAttachment stencilLoadAction must not be load when the texture has a storage mode of deviceTransient"); + "DepthStencilAttachment stencilLoadAction must not be load when the texture has a storage mode of deviceTransient", + ); } } } @@ -109,11 +117,7 @@ base class StencilConfig { // Note: When modifying this enum, also update // `InternalFlutterGpu_RenderPass_SetStencilConfig` in `gpu/render_pass.cc`. -enum StencilFace { - both, - front, - back, -} +enum StencilFace { both, front, back } base class ColorBlendEquation { ColorBlendEquation({ @@ -157,21 +161,26 @@ base class Scissor { void _validate() { if (x < 0 || y < 0 || width < 0 || height < 0) { - throw Exception("Invalid values for scissor. All values should be positive."); + throw Exception( + "Invalid values for scissor. All values should be positive.", + ); } } } base class RenderTarget { - const RenderTarget( - {this.colorAttachments = const [], - this.depthStencilAttachment}); + const RenderTarget({ + this.colorAttachments = const [], + this.depthStencilAttachment, + }); - RenderTarget.singleColor(ColorAttachment colorAttachment, - {DepthStencilAttachment? depthStencilAttachment}) - : this( - colorAttachments: [colorAttachment], - depthStencilAttachment: depthStencilAttachment); + RenderTarget.singleColor( + ColorAttachment colorAttachment, { + DepthStencilAttachment? depthStencilAttachment, + }) : this( + colorAttachments: [colorAttachment], + depthStencilAttachment: depthStencilAttachment, + ); _validate() { for (final color in colorAttachments) { @@ -188,8 +197,11 @@ base class RenderTarget { base class RenderPass extends NativeFieldWrapperClass1 { /// Creates a new RenderPass. - RenderPass._(GpuContext gpuContext, CommandBuffer commandBuffer, - RenderTarget renderTarget) { + RenderPass._( + GpuContext gpuContext, + CommandBuffer commandBuffer, + RenderTarget renderTarget, + ) { assert(() { renderTarget._validate(); return true; @@ -199,16 +211,17 @@ base class RenderPass extends NativeFieldWrapperClass1 { String? error; for (final (index, color) in renderTarget.colorAttachments.indexed) { error = _setColorAttachment( - gpuContext, - index, - color.loadAction.index, - color.storeAction.index, - color.clearValue.r, - color.clearValue.g, - color.clearValue.b, - color.clearValue.a, - color.texture, - color.resolveTexture); + gpuContext, + index, + color.loadAction.index, + color.storeAction.index, + color.clearValue.r, + color.clearValue.g, + color.clearValue.b, + color.clearValue.a, + color.texture, + color.resolveTexture, + ); if (error != null) { throw Exception(error); } @@ -216,13 +229,14 @@ base class RenderPass extends NativeFieldWrapperClass1 { if (renderTarget.depthStencilAttachment != null) { final ds = renderTarget.depthStencilAttachment!; error = _setDepthStencilAttachment( - ds.depthLoadAction.index, - ds.depthStoreAction.index, - ds.depthClearValue, - ds.stencilLoadAction.index, - ds.stencilStoreAction.index, - ds.stencilClearValue, - ds.texture); + ds.depthLoadAction.index, + ds.depthStoreAction.index, + ds.depthClearValue, + ds.stencilLoadAction.index, + ds.stencilStoreAction.index, + ds.stencilClearValue, + ds.texture, + ); if (error != null) { throw Exception(error); } @@ -239,25 +253,44 @@ base class RenderPass extends NativeFieldWrapperClass1 { void bindVertexBuffer(BufferView bufferView, int vertexCount) { bufferView.buffer._bindAsVertexBuffer( - this, bufferView.offsetInBytes, bufferView.lengthInBytes, vertexCount); + this, + bufferView.offsetInBytes, + bufferView.lengthInBytes, + vertexCount, + ); } void bindIndexBuffer( - BufferView bufferView, IndexType indexType, int indexCount) { - bufferView.buffer._bindAsIndexBuffer(this, bufferView.offsetInBytes, - bufferView.lengthInBytes, indexType, indexCount); + BufferView bufferView, + IndexType indexType, + int indexCount, + ) { + bufferView.buffer._bindAsIndexBuffer( + this, + bufferView.offsetInBytes, + bufferView.lengthInBytes, + indexType, + indexCount, + ); } void bindUniform(UniformSlot slot, BufferView bufferView) { bool success = bufferView.buffer._bindAsUniform( - this, slot, bufferView.offsetInBytes, bufferView.lengthInBytes); + this, + slot, + bufferView.offsetInBytes, + bufferView.lengthInBytes, + ); if (!success) { throw Exception("Failed to bind uniform"); } } - void bindTexture(UniformSlot slot, Texture texture, - {SamplerOptions? sampler}) { + void bindTexture( + UniformSlot slot, + Texture texture, { + SamplerOptions? sampler, + }) { if (sampler == null) { sampler = SamplerOptions(); } @@ -265,20 +298,22 @@ base class RenderPass extends NativeFieldWrapperClass1 { assert(() { if (texture.storageMode == StorageMode.deviceTransient) { throw Exception( - "Textures with StorageMode.deviceTransient cannot be bound to a RenderPass"); + "Textures with StorageMode.deviceTransient cannot be bound to a RenderPass", + ); } return true; }()); bool success = _bindTexture( - slot.shader, - slot.uniformName, - texture, - sampler.minFilter.index, - sampler.magFilter.index, - sampler.mipFilter.index, - sampler.widthAddressMode.index, - sampler.heightAddressMode.index); + slot.shader, + slot.uniformName, + texture, + sampler.minFilter.index, + sampler.magFilter.index, + sampler.mipFilter.index, + sampler.widthAddressMode.index, + sampler.heightAddressMode.index, + ); if (!success) { throw Exception("Failed to bind texture"); } @@ -292,16 +327,19 @@ base class RenderPass extends NativeFieldWrapperClass1 { _setColorBlendEnable(colorAttachmentIndex, enable); } - void setColorBlendEquation(ColorBlendEquation equation, - {int colorAttachmentIndex = 0}) { + void setColorBlendEquation( + ColorBlendEquation equation, { + int colorAttachmentIndex = 0, + }) { _setColorBlendEquation( - colorAttachmentIndex, - equation.colorBlendOperation.index, - equation.sourceColorBlendFactor.index, - equation.destinationColorBlendFactor.index, - equation.alphaBlendOperation.index, - equation.sourceAlphaBlendFactor.index, - equation.destinationAlphaBlendFactor.index); + colorAttachmentIndex, + equation.colorBlendOperation.index, + equation.sourceColorBlendFactor.index, + equation.destinationColorBlendFactor.index, + equation.alphaBlendOperation.index, + equation.sourceAlphaBlendFactor.index, + equation.destinationAlphaBlendFactor.index, + ); } void setDepthWriteEnable(bool enable) { @@ -315,13 +353,16 @@ base class RenderPass extends NativeFieldWrapperClass1 { void setStencilReference(int referenceValue) { if (referenceValue < 0 || referenceValue > 0xFFFFFFFF) { throw Exception( - "The stencil reference value must be in the range [0, 2^32 - 1]"); + "The stencil reference value must be in the range [0, 2^32 - 1]", + ); } _setStencilReference(referenceValue); } - void setStencilConfig(StencilConfig configuration, - {StencilFace targetFace = StencilFace.both}) { + void setStencilConfig( + StencilConfig configuration, { + StencilFace targetFace = StencilFace.both, + }) { if (configuration.readMask < 0 || configuration.readMask > 0xFFFFFFFF) { throw Exception("The stencil read mask must be in the range [0, 255]"); } @@ -329,13 +370,14 @@ base class RenderPass extends NativeFieldWrapperClass1 { throw Exception("The stencil write mask must be in the range [0, 255]"); } _setStencilConfig( - configuration.compareFunction.index, - configuration.stencilFailureOperation.index, - configuration.depthFailureOperation.index, - configuration.depthStencilPassOperation.index, - configuration.readMask, - configuration.writeMask, - targetFace.index); + configuration.compareFunction.index, + configuration.stencilFailureOperation.index, + configuration.depthFailureOperation.index, + configuration.depthStencilPassOperation.index, + configuration.readMask, + configuration.writeMask, + targetFace.index, + ); } void setScissor(Scissor scissor) { @@ -370,159 +412,204 @@ base class RenderPass extends NativeFieldWrapperClass1 { /// Wrap with native counterpart. @Native( - symbol: 'InternalFlutterGpu_RenderPass_Initialize') + symbol: 'InternalFlutterGpu_RenderPass_Initialize', + ) external void _initialize(); @Native< - Handle Function( - Pointer, - Pointer, - Int, - Int, - Int, - Float, - Float, - Float, - Float, - Pointer, - Handle)>(symbol: 'InternalFlutterGpu_RenderPass_SetColorAttachment') + Handle Function( + Pointer, + Pointer, + Int, + Int, + Int, + Float, + Float, + Float, + Float, + Pointer, + Handle, + ) + >(symbol: 'InternalFlutterGpu_RenderPass_SetColorAttachment') external String? _setColorAttachment( - GpuContext context, - int colorAttachmentIndex, - int loadAction, - int storeAction, - double clearColorR, - double clearColorG, - double clearColorB, - double clearColorA, - Texture texture, - Texture? resolveTexture); + GpuContext context, + int colorAttachmentIndex, + int loadAction, + int storeAction, + double clearColorR, + double clearColorG, + double clearColorB, + double clearColorA, + Texture texture, + Texture? resolveTexture, + ); @Native< - Handle Function( - Pointer, Int, Int, Float, Int, Int, Int, Pointer)>( - symbol: 'InternalFlutterGpu_RenderPass_SetDepthStencilAttachment') + Handle Function( + Pointer, + Int, + Int, + Float, + Int, + Int, + Int, + Pointer, + ) + >(symbol: 'InternalFlutterGpu_RenderPass_SetDepthStencilAttachment') external String? _setDepthStencilAttachment( - int depthLoadAction, - int depthStoreAction, - double depthClearValue, - int stencilLoadAction, - int stencilStoreAction, - int stencilClearValue, - Texture texture); + int depthLoadAction, + int depthStoreAction, + double depthClearValue, + int stencilLoadAction, + int stencilStoreAction, + int stencilClearValue, + Texture texture, + ); @Native, Pointer)>( - symbol: 'InternalFlutterGpu_RenderPass_Begin') + symbol: 'InternalFlutterGpu_RenderPass_Begin', + ) external String? _begin(CommandBuffer commandBuffer); @Native, Pointer)>( - symbol: 'InternalFlutterGpu_RenderPass_BindPipeline') + symbol: 'InternalFlutterGpu_RenderPass_BindPipeline', + ) external void _bindPipeline(RenderPipeline pipeline); @Native, Pointer, Int, Int, Int)>( - symbol: 'InternalFlutterGpu_RenderPass_BindVertexBufferDevice') - external void _bindVertexBufferDevice(DeviceBuffer buffer, int offsetInBytes, - int lengthInBytes, int vertexCount); + symbol: 'InternalFlutterGpu_RenderPass_BindVertexBufferDevice', + ) + external void _bindVertexBufferDevice( + DeviceBuffer buffer, + int offsetInBytes, + int lengthInBytes, + int vertexCount, + ); @Native, Pointer, Int, Int, Int, Int)>( - symbol: 'InternalFlutterGpu_RenderPass_BindIndexBufferDevice') - external void _bindIndexBufferDevice(DeviceBuffer buffer, int offsetInBytes, - int lengthInBytes, int indexType, int indexCount); + symbol: 'InternalFlutterGpu_RenderPass_BindIndexBufferDevice', + ) + external void _bindIndexBufferDevice( + DeviceBuffer buffer, + int offsetInBytes, + int lengthInBytes, + int indexType, + int indexCount, + ); @Native< - Bool Function(Pointer, Pointer, Handle, Pointer, Int, - Int)>(symbol: 'InternalFlutterGpu_RenderPass_BindUniformDevice') - external bool _bindUniformDevice(Shader shader, String uniformName, - DeviceBuffer buffer, int offsetInBytes, int lengthInBytes); + Bool Function(Pointer, Pointer, Handle, Pointer, Int, Int) + >(symbol: 'InternalFlutterGpu_RenderPass_BindUniformDevice') + external bool _bindUniformDevice( + Shader shader, + String uniformName, + DeviceBuffer buffer, + int offsetInBytes, + int lengthInBytes, + ); @Native< - Bool Function( - Pointer, - Pointer, - Handle, - Pointer, - Int, - Int, - Int, - Int, - Int)>(symbol: 'InternalFlutterGpu_RenderPass_BindTexture') + Bool Function( + Pointer, + Pointer, + Handle, + Pointer, + Int, + Int, + Int, + Int, + Int, + ) + >(symbol: 'InternalFlutterGpu_RenderPass_BindTexture') external bool _bindTexture( - Shader shader, - String uniformName, - Texture texture, - int minFilter, - int magFilter, - int mipFilter, - int widthAddressMode, - int heightAddressMode); + Shader shader, + String uniformName, + Texture texture, + int minFilter, + int magFilter, + int mipFilter, + int widthAddressMode, + int heightAddressMode, + ); @Native)>( - symbol: 'InternalFlutterGpu_RenderPass_ClearBindings') + symbol: 'InternalFlutterGpu_RenderPass_ClearBindings', + ) external void _clearBindings(); @Native, Int, Bool)>( - symbol: 'InternalFlutterGpu_RenderPass_SetColorBlendEnable') + symbol: 'InternalFlutterGpu_RenderPass_SetColorBlendEnable', + ) external void _setColorBlendEnable(int colorAttachmentIndex, bool enable); @Native, Int, Int, Int, Int, Int, Int, Int)>( - symbol: 'InternalFlutterGpu_RenderPass_SetColorBlendEquation') + symbol: 'InternalFlutterGpu_RenderPass_SetColorBlendEquation', + ) external void _setColorBlendEquation( - int colorAttachmentIndex, - int colorBlendOperation, - int sourceColorBlendFactor, - int destinationColorBlendFactor, - int alphaBlendOperation, - int sourceAlphaBlendFactor, - int destinationAlphaBlendFactor); + int colorAttachmentIndex, + int colorBlendOperation, + int sourceColorBlendFactor, + int destinationColorBlendFactor, + int alphaBlendOperation, + int sourceAlphaBlendFactor, + int destinationAlphaBlendFactor, + ); @Native, Bool)>( - symbol: 'InternalFlutterGpu_RenderPass_SetDepthWriteEnable') + symbol: 'InternalFlutterGpu_RenderPass_SetDepthWriteEnable', + ) external void _setDepthWriteEnable(bool enable); @Native, Int)>( - symbol: 'InternalFlutterGpu_RenderPass_SetDepthCompareOperation') + symbol: 'InternalFlutterGpu_RenderPass_SetDepthCompareOperation', + ) external void _setDepthCompareOperation(int compareOperation); @Native, Int)>( - symbol: 'InternalFlutterGpu_RenderPass_SetStencilReference') + symbol: 'InternalFlutterGpu_RenderPass_SetStencilReference', + ) external void _setStencilReference(int referenceValue); @Native, Int, Int, Int, Int, Int, Int, Int)>( - symbol: 'InternalFlutterGpu_RenderPass_SetStencilConfig') + symbol: 'InternalFlutterGpu_RenderPass_SetStencilConfig', + ) external void _setStencilConfig( - int compareFunction, - int stencilFailureOperation, - int depthFailureOperation, - int depthStencilPassOperation, - int readMask, - int writeMask, - int target_face); + int compareFunction, + int stencilFailureOperation, + int depthFailureOperation, + int depthStencilPassOperation, + int readMask, + int writeMask, + int target_face, + ); @Native, Int, Int, Int, Int)>( - symbol: 'InternalFlutterGpu_RenderPass_SetScissor') - external void _setScissor( - int x, - int y, - int width, - int height); + symbol: 'InternalFlutterGpu_RenderPass_SetScissor', + ) + external void _setScissor(int x, int y, int width, int height); @Native, Int)>( - symbol: 'InternalFlutterGpu_RenderPass_SetCullMode') + symbol: 'InternalFlutterGpu_RenderPass_SetCullMode', + ) external void _setCullMode(int cullMode); @Native, Int)>( - symbol: 'InternalFlutterGpu_RenderPass_SetPrimitiveType') + symbol: 'InternalFlutterGpu_RenderPass_SetPrimitiveType', + ) external void _setPrimitiveType(int primitiveType); @Native, Int)>( - symbol: 'InternalFlutterGpu_RenderPass_SetWindingOrder') + symbol: 'InternalFlutterGpu_RenderPass_SetWindingOrder', + ) external void _setWindingOrder(int windingOrder); @Native, Int)>( - symbol: 'InternalFlutterGpu_RenderPass_SetPolygonMode') + symbol: 'InternalFlutterGpu_RenderPass_SetPolygonMode', + ) external void _setPolygonMode(int polygonMode); @Native)>( - symbol: 'InternalFlutterGpu_RenderPass_Draw') + symbol: 'InternalFlutterGpu_RenderPass_Draw', + ) external bool _draw(); } diff --git a/lib/gpu/lib/src/render_pipeline.dart b/lib/gpu/lib/src/render_pipeline.dart index a8d110e29a2fd..0086d0de98494 100644 --- a/lib/gpu/lib/src/render_pipeline.dart +++ b/lib/gpu/lib/src/render_pipeline.dart @@ -9,9 +9,11 @@ part of flutter_gpu; base class RenderPipeline extends NativeFieldWrapperClass1 { /// Creates a new RenderPipeline. RenderPipeline._( - GpuContext gpuContext, Shader vertexShader, Shader fragmentShader) - : vertexShader = vertexShader, - fragmentShader = fragmentShader { + GpuContext gpuContext, + Shader vertexShader, + Shader fragmentShader, + ) : vertexShader = vertexShader, + fragmentShader = fragmentShader { String? error = _initialize(gpuContext, vertexShader, fragmentShader); if (error != null) { throw Exception(error); @@ -23,7 +25,11 @@ base class RenderPipeline extends NativeFieldWrapperClass1 { /// Wrap with native counterpart. @Native, Pointer, Pointer)>( - symbol: 'InternalFlutterGpu_RenderPipeline_Initialize') + symbol: 'InternalFlutterGpu_RenderPipeline_Initialize', + ) external String? _initialize( - GpuContext gpuContext, Shader vertexShader, Shader fragmentShader); + GpuContext gpuContext, + Shader vertexShader, + Shader fragmentShader, + ); } diff --git a/lib/gpu/lib/src/shader.dart b/lib/gpu/lib/src/shader.dart index ad34346dd36ea..526bf927536ab 100644 --- a/lib/gpu/lib/src/shader.dart +++ b/lib/gpu/lib/src/shader.dart @@ -40,11 +40,15 @@ base class Shader extends NativeFieldWrapperClass1 { } @Native, Handle)>( - symbol: 'InternalFlutterGpu_Shader_GetUniformStructSize') + symbol: 'InternalFlutterGpu_Shader_GetUniformStructSize', + ) external int _getUniformStructSize(String uniformStructName); @Native, Handle, Handle)>( - symbol: 'InternalFlutterGpu_Shader_GetUniformMemberOffset') + symbol: 'InternalFlutterGpu_Shader_GetUniformMemberOffset', + ) external int _getUniformMemberOffset( - String uniformStructName, String memberName); + String uniformStructName, + String memberName, + ); } diff --git a/lib/gpu/lib/src/shader_library.dart b/lib/gpu/lib/src/shader_library.dart index 859ba02d38f29..b4c41f08bf70e 100644 --- a/lib/gpu/lib/src/shader_library.dart +++ b/lib/gpu/lib/src/shader_library.dart @@ -31,14 +31,18 @@ base class ShaderLibrary extends NativeFieldWrapperClass1 { // counterpart (if it hasn't been wrapped already) is a hack to work around // this. return shaders_.putIfAbsent( - shaderName, () => _getShader(shaderName, Shader._())); + shaderName, + () => _getShader(shaderName, Shader._()), + ); } @Native( - symbol: 'InternalFlutterGpu_ShaderLibrary_InitializeWithAsset') + symbol: 'InternalFlutterGpu_ShaderLibrary_InitializeWithAsset', + ) external String? _initializeWithAsset(String assetName); @Native, Handle, Handle)>( - symbol: 'InternalFlutterGpu_ShaderLibrary_GetShader') + symbol: 'InternalFlutterGpu_ShaderLibrary_GetShader', + ) external Shader? _getShader(String shaderName, Shader shaderWrapper); } diff --git a/lib/gpu/lib/src/texture.dart b/lib/gpu/lib/src/texture.dart index 12455ccaf3b84..02ba6198a7f27 100644 --- a/lib/gpu/lib/src/texture.dart +++ b/lib/gpu/lib/src/texture.dart @@ -14,31 +14,32 @@ base class Texture extends NativeFieldWrapperClass1 { /// Creates a new Texture. Texture._initialize( - GpuContext gpuContext, - this.storageMode, - this.format, - this.width, - this.height, - this.sampleCount, - TextureCoordinateSystem coordinateSystem, - this.enableRenderTargetUsage, - this.enableShaderReadUsage, - this.enableShaderWriteUsage) - : _coordinateSystem = coordinateSystem { + GpuContext gpuContext, + this.storageMode, + this.format, + this.width, + this.height, + this.sampleCount, + TextureCoordinateSystem coordinateSystem, + this.enableRenderTargetUsage, + this.enableShaderReadUsage, + this.enableShaderWriteUsage, + ) : _coordinateSystem = coordinateSystem { if (sampleCount != 1 && sampleCount != 4) { throw Exception("Only a sample count of 1 or 4 is currently supported"); } _valid = _initialize( - gpuContext, - storageMode.index, - format.index, - width, - height, - sampleCount, - coordinateSystem.index, - enableRenderTargetUsage, - enableShaderReadUsage, - enableShaderWriteUsage); + gpuContext, + storageMode.index, + format.index, + width, + height, + sampleCount, + coordinateSystem.index, + enableRenderTargetUsage, + enableShaderReadUsage, + enableShaderWriteUsage, + ); } final StorageMode storageMode; @@ -90,12 +91,14 @@ base class Texture extends NativeFieldWrapperClass1 { bool overwrite(ByteData sourceBytes) { if (storageMode != StorageMode.hostVisible) { throw Exception( - 'Texture.overwrite can only be used with Textures that are host visible'); + 'Texture.overwrite can only be used with Textures that are host visible', + ); } int baseMipSize = GetBaseMipLevelSizeInBytes(); if (sourceBytes.lengthInBytes != baseMipSize) { throw Exception( - 'The length of sourceBytes (bytes: ${sourceBytes.lengthInBytes}) must exactly match the size of the base mip level (bytes: ${baseMipSize})'); + 'The length of sourceBytes (bytes: ${sourceBytes.lengthInBytes}) must exactly match the size of the base mip level (bytes: ${baseMipSize})', + ); } return _overwrite(sourceBytes); } @@ -103,40 +106,58 @@ base class Texture extends NativeFieldWrapperClass1 { ui.Image asImage() { if (!enableShaderReadUsage) { throw Exception( - 'Only shader readable Flutter GPU textures can be used as UI Images'); + 'Only shader readable Flutter GPU textures can be used as UI Images', + ); } return _asImage(); } /// Wrap with native counterpart. @Native< - Bool Function(Handle, Pointer, Int, Int, Int, Int, Int, Int, Bool, - Bool, Bool)>(symbol: 'InternalFlutterGpu_Texture_Initialize') + Bool Function( + Handle, + Pointer, + Int, + Int, + Int, + Int, + Int, + Int, + Bool, + Bool, + Bool, + ) + >(symbol: 'InternalFlutterGpu_Texture_Initialize') external bool _initialize( - GpuContext gpuContext, - int storageMode, - int format, - int width, - int height, - int sampleCount, - int coordinateSystem, - bool enableRenderTargetUsage, - bool enableShaderReadUsage, - bool enableShaderWriteUsage); + GpuContext gpuContext, + int storageMode, + int format, + int width, + int height, + int sampleCount, + int coordinateSystem, + bool enableRenderTargetUsage, + bool enableShaderReadUsage, + bool enableShaderWriteUsage, + ); @Native( - symbol: 'InternalFlutterGpu_Texture_SetCoordinateSystem') + symbol: 'InternalFlutterGpu_Texture_SetCoordinateSystem', + ) external void _setCoordinateSystem(int coordinateSystem); @Native)>( - symbol: 'InternalFlutterGpu_Texture_BytesPerTexel') + symbol: 'InternalFlutterGpu_Texture_BytesPerTexel', + ) external int _bytesPerTexel(); @Native, Handle)>( - symbol: 'InternalFlutterGpu_Texture_Overwrite') + symbol: 'InternalFlutterGpu_Texture_Overwrite', + ) external bool _overwrite(ByteData bytes); @Native)>( - symbol: 'InternalFlutterGpu_Texture_AsImage') + symbol: 'InternalFlutterGpu_Texture_AsImage', + ) external ui.Image _asImage(); } diff --git a/lib/ui/channel_buffers.dart b/lib/ui/channel_buffers.dart index 2ff1758e82e4c..bd57b61efecd5 100644 --- a/lib/ui/channel_buffers.dart +++ b/lib/ui/channel_buffers.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - // KEEP THIS SYNCHRONIZED WITH ../web_ui/lib/channel_buffers.dart part of dart.ui; @@ -18,7 +17,8 @@ part of dart.ui; 'Migrate to ChannelCallback instead. ' 'This feature was deprecated after v3.11.0-20.0.pre.', ) -typedef DrainChannelCallback = Future Function(ByteData? data, PlatformMessageResponseCallback callback); +typedef DrainChannelCallback = + Future Function(ByteData? data, PlatformMessageResponseCallback callback); /// Signature for [ChannelBuffers.setListener]'s `callback` argument. /// @@ -75,7 +75,7 @@ class _StoredMessage { /// This consists of a fixed-size circular queue of [_StoredMessage]s, /// and the channel's callback, if any has been registered. class _Channel { - _Channel([ this._capacity = ChannelBuffers.kDefaultBufferSize ]) + _Channel([this._capacity = ChannelBuffers.kDefaultBufferSize]) : _queue = collection.ListQueue<_StoredMessage>(_capacity); /// The underlying data for the buffered messages. @@ -98,6 +98,7 @@ class _Channel { /// in a first-in-first-out fashion. int get capacity => _capacity; int _capacity; + /// Set the [capacity] of the channel to the given size. /// /// If the new size is smaller than the [length], the oldest @@ -402,7 +403,8 @@ class ChannelBuffers { // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. // ignore: public_member_api_docs - void sendChannelUpdate(String name, {required bool listening}) => _sendChannelUpdate(name, listening); + void sendChannelUpdate(String name, {required bool listening}) => + _sendChannelUpdate(name, listening); /// Deprecated. Migrate to [setListener] instead. /// @@ -469,9 +471,11 @@ class ChannelBuffers { // We hard-code the deserialization here because the StandardMethodCodec class // is part of the framework, not dart:ui. final Uint8List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - if (bytes[0] == 0x07) { // 7 = value code for string + if (bytes[0] == 0x07) { + // 7 = value code for string final int methodNameLength = bytes[1]; - if (methodNameLength >= 254) { // lengths greater than 253 have more elaborate encoding + if (methodNameLength >= 254) { + // lengths greater than 253 have more elaborate encoding throw Exception('Unrecognized message sent to $kControlChannelName (method name too long)'); } int index = 2; // where we are in reading the bytes @@ -479,55 +483,87 @@ class ChannelBuffers { index += methodNameLength; switch (methodName) { case 'resize': - if (bytes[index] != 0x0C) { // 12 = value code for list - throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (arguments must be a two-element list, channel name and new capacity)"); + if (bytes[index] != 0x0C) { + // 12 = value code for list + throw Exception( + "Invalid arguments for 'resize' method sent to $kControlChannelName (arguments must be a two-element list, channel name and new capacity)", + ); } index += 1; - if (bytes[index] < 0x02) { // We ignore extra arguments, in case we need to support them in the future, hence <2 rather than !=2. - throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (arguments must be a two-element list, channel name and new capacity)"); + if (bytes[index] < 0x02) { + // We ignore extra arguments, in case we need to support them in the future, hence <2 rather than !=2. + throw Exception( + "Invalid arguments for 'resize' method sent to $kControlChannelName (arguments must be a two-element list, channel name and new capacity)", + ); } index += 1; - if (bytes[index] != 0x07) { // 7 = value code for string - throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (first argument must be a string)"); + if (bytes[index] != 0x07) { + // 7 = value code for string + throw Exception( + "Invalid arguments for 'resize' method sent to $kControlChannelName (first argument must be a string)", + ); } index += 1; final int channelNameLength = bytes[index]; - if (channelNameLength >= 254) { // lengths greater than 253 have more elaborate encoding - throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (channel name must be less than 254 characters long)"); + if (channelNameLength >= 254) { + // lengths greater than 253 have more elaborate encoding + throw Exception( + "Invalid arguments for 'resize' method sent to $kControlChannelName (channel name must be less than 254 characters long)", + ); } index += 1; final String channelName = utf8.decode(bytes.sublist(index, index + channelNameLength)); if (channelName.contains('\u0000')) { - throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (channel name must not contain any null bytes)"); + throw Exception( + "Invalid arguments for 'resize' method sent to $kControlChannelName (channel name must not contain any null bytes)", + ); } index += channelNameLength; - if (bytes[index] != 0x03) { // 3 = value code for uint32 - throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (second argument must be an integer in the range 0 to 2147483647)"); + if (bytes[index] != 0x03) { + // 3 = value code for uint32 + throw Exception( + "Invalid arguments for 'resize' method sent to $kControlChannelName (second argument must be an integer in the range 0 to 2147483647)", + ); } index += 1; resize(channelName, data.getUint32(index, Endian.host)); case 'overflow': - if (bytes[index] != 0x0C) { // 12 = value code for list - throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (arguments must be a two-element list, channel name and flag state)"); + if (bytes[index] != 0x0C) { + // 12 = value code for list + throw Exception( + "Invalid arguments for 'overflow' method sent to $kControlChannelName (arguments must be a two-element list, channel name and flag state)", + ); } index += 1; - if (bytes[index] < 0x02) { // We ignore extra arguments, in case we need to support them in the future, hence <2 rather than !=2. - throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (arguments must be a two-element list, channel name and flag state)"); + if (bytes[index] < 0x02) { + // We ignore extra arguments, in case we need to support them in the future, hence <2 rather than !=2. + throw Exception( + "Invalid arguments for 'overflow' method sent to $kControlChannelName (arguments must be a two-element list, channel name and flag state)", + ); } index += 1; - if (bytes[index] != 0x07) { // 7 = value code for string - throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (first argument must be a string)"); + if (bytes[index] != 0x07) { + // 7 = value code for string + throw Exception( + "Invalid arguments for 'overflow' method sent to $kControlChannelName (first argument must be a string)", + ); } index += 1; final int channelNameLength = bytes[index]; - if (channelNameLength >= 254) { // lengths greater than 253 have more elaborate encoding - throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (channel name must be less than 254 characters long)"); + if (channelNameLength >= 254) { + // lengths greater than 253 have more elaborate encoding + throw Exception( + "Invalid arguments for 'overflow' method sent to $kControlChannelName (channel name must be less than 254 characters long)", + ); } index += 1; final String channelName = utf8.decode(bytes.sublist(index, index + channelNameLength)); index += channelNameLength; - if (bytes[index] != 0x01 && bytes[index] != 0x02) { // 1 = value code for true, 2 = value code for false - throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (second argument must be a boolean)"); + if (bytes[index] != 0x01 && bytes[index] != 0x02) { + // 1 = value code for true, 2 = value code for false + throw Exception( + "Invalid arguments for 'overflow' method sent to $kControlChannelName (second argument must be a boolean)", + ); } allowOverflow(channelName, bytes[index] == 0x01); default: @@ -535,7 +571,7 @@ class ChannelBuffers { } } else { final List parts = utf8.decode(bytes).split('\r'); - if (parts.length == 1 + /*arity=*/2 && parts[0] == 'resize') { + if (parts.length == 1 + /*arity=*/ 2 && parts[0] == 'resize') { resize(parts[1], int.parse(parts[2])); } else { // If the message couldn't be decoded as UTF-8, a FormatException will diff --git a/lib/ui/compositing.dart b/lib/ui/compositing.dart index d8684270486e6..196291448de5d 100644 --- a/lib/ui/compositing.dart +++ b/lib/ui/compositing.dart @@ -49,7 +49,7 @@ base class _NativeScene extends NativeFieldWrapperClass1 implements Scene { } final _Image image = _Image._(); - final String? result = _toImageSync(width, height, image); + final String? result = _toImageSync(width, height, image); if (result != null) { throw PictureRasterizationException._(result); } @@ -64,7 +64,8 @@ base class _NativeScene extends NativeFieldWrapperClass1 implements Scene { if (width <= 0 || height <= 0) { throw Exception('Invalid image dimensions.'); } - return _futurize((_Callback callback) => _toImage(width, height, (_Image? image) { + return _futurize( + (_Callback callback) => _toImage(width, height, (_Image? image) { if (image == null) { callback(null); } else { @@ -122,12 +123,13 @@ abstract class _EngineLayerWrapper implements EngineLayer { bool _debugCheckNotUsedAsOldLayer() { // The hashCode formatting should match shortHash in the framework assert( - !_debugWasUsedAsOldLayer, - 'Layer $runtimeType#${hashCode.toUnsigned(20).toRadixString(16).padLeft(5, '0')} was previously used as oldLayer.\n' - 'Once a layer is used as oldLayer, it may not be used again. Instead, ' - 'after calling one of the SceneBuilder.push* methods and passing an oldLayer ' - 'to it, use the layer returned by the method as oldLayer in subsequent ' - 'frames.'); + !_debugWasUsedAsOldLayer, + 'Layer $runtimeType#${hashCode.toUnsigned(20).toRadixString(16).padLeft(5, '0')} was previously used as oldLayer.\n' + 'Once a layer is used as oldLayer, it may not be used again. Instead, ' + 'after calling one of the SceneBuilder.push* methods and passing an oldLayer ' + 'to it, use the layer returned by the method as oldLayer in subsequent ' + 'frames.', + ); return true; } } @@ -278,10 +280,7 @@ abstract class SceneBuilder { /// {@endtemplate} /// /// See [pop] for details about the operation stack. - TransformEngineLayer pushTransform( - Float64List matrix4, { - TransformEngineLayer? oldLayer, - }); + TransformEngineLayer pushTransform(Float64List matrix4, {TransformEngineLayer? oldLayer}); /// Pushes an offset operation onto the operation stack. /// @@ -292,11 +291,7 @@ abstract class SceneBuilder { /// {@macro dart.ui.sceneBuilder.oldLayerVsRetained} /// /// See [pop] for details about the operation stack. - OffsetEngineLayer pushOffset( - double dx, - double dy, { - OffsetEngineLayer? oldLayer, - }); + OffsetEngineLayer pushOffset(double dx, double dy, {OffsetEngineLayer? oldLayer}); /// Pushes a rectangular clip operation onto the operation stack. /// @@ -374,10 +369,7 @@ abstract class SceneBuilder { /// {@macro dart.ui.sceneBuilder.oldLayerVsRetained} /// /// See [pop] for details about the operation stack. - ColorFilterEngineLayer pushColorFilter( - ColorFilter filter, { - ColorFilterEngineLayer? oldLayer, - }); + ColorFilterEngineLayer pushColorFilter(ColorFilter filter, {ColorFilterEngineLayer? oldLayer}); /// Pushes an image filter operation onto the operation stack. /// @@ -592,10 +584,11 @@ base class _NativeSceneBuilder extends NativeFieldWrapperClass1 implements Scene bool _debugCheckUsedOnce(EngineLayer layer, String usage) { assert(() { assert( - !_usedLayers.containsKey(layer), - 'Layer ${layer.runtimeType} already used.\n' - 'The layer is already being used as ${_usedLayers[layer]} in this scene.\n' - 'A layer may only be used once in a given scene.'); + !_usedLayers.containsKey(layer), + 'Layer ${layer.runtimeType} already used.\n' + 'The layer is already being used as ${_usedLayers[layer]} in this scene.\n' + 'A layer may only be used once in a given scene.', + ); _usedLayers[layer] = usage; return true; @@ -636,10 +629,7 @@ base class _NativeSceneBuilder extends NativeFieldWrapperClass1 implements Scene } @override - TransformEngineLayer pushTransform( - Float64List matrix4, { - TransformEngineLayer? oldLayer, - }) { + TransformEngineLayer pushTransform(Float64List matrix4, {TransformEngineLayer? oldLayer}) { assert(_matrix4IsValid(matrix4)); assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushTransform')); final EngineLayer engineLayer = _NativeEngineLayer._(); @@ -649,15 +639,13 @@ base class _NativeSceneBuilder extends NativeFieldWrapperClass1 implements Scene return layer; } - @Native, Handle, Handle, Handle)>(symbol: 'SceneBuilder::pushTransformHandle') + @Native, Handle, Handle, Handle)>( + symbol: 'SceneBuilder::pushTransformHandle', + ) external void _pushTransform(EngineLayer layer, Float64List matrix4, EngineLayer? oldLayer); @override - OffsetEngineLayer pushOffset( - double dx, - double dy, { - OffsetEngineLayer? oldLayer, - }) { + OffsetEngineLayer pushOffset(double dx, double dy, {OffsetEngineLayer? oldLayer}) { assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushOffset')); final EngineLayer engineLayer = _NativeEngineLayer._(); _pushOffset(engineLayer, dx, dy, oldLayer?._nativeLayer); @@ -666,7 +654,9 @@ base class _NativeSceneBuilder extends NativeFieldWrapperClass1 implements Scene return layer; } - @Native, Handle, Double, Double, Handle)>(symbol: 'SceneBuilder::pushOffset') + @Native, Handle, Double, Double, Handle)>( + symbol: 'SceneBuilder::pushOffset', + ) external void _pushOffset(EngineLayer layer, double dx, double dy, EngineLayer? oldLayer); @override @@ -678,22 +668,32 @@ base class _NativeSceneBuilder extends NativeFieldWrapperClass1 implements Scene assert(clipBehavior != Clip.none); assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushClipRect')); final EngineLayer engineLayer = _NativeEngineLayer._(); - _pushClipRect(engineLayer, rect.left, rect.right, rect.top, rect.bottom, clipBehavior.index, - oldLayer?._nativeLayer); + _pushClipRect( + engineLayer, + rect.left, + rect.right, + rect.top, + rect.bottom, + clipBehavior.index, + oldLayer?._nativeLayer, + ); final ClipRectEngineLayer layer = ClipRectEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } - @Native, Handle, Double, Double, Double, Double, Int32, Handle)>(symbol: 'SceneBuilder::pushClipRect') + @Native, Handle, Double, Double, Double, Double, Int32, Handle)>( + symbol: 'SceneBuilder::pushClipRect', + ) external void _pushClipRect( - EngineLayer outEngineLayer, - double left, - double right, - double top, - double bottom, - int clipBehavior, - EngineLayer? oldLayer); + EngineLayer outEngineLayer, + double left, + double right, + double top, + double bottom, + int clipBehavior, + EngineLayer? oldLayer, + ); @override ClipRRectEngineLayer pushClipRRect( @@ -710,8 +710,15 @@ base class _NativeSceneBuilder extends NativeFieldWrapperClass1 implements Scene return layer; } - @Native, Handle, Handle, Int32, Handle)>(symbol: 'SceneBuilder::pushClipRRect') - external void _pushClipRRect(EngineLayer layer, Float32List rrect, int clipBehavior, EngineLayer? oldLayer); + @Native, Handle, Handle, Int32, Handle)>( + symbol: 'SceneBuilder::pushClipRRect', + ) + external void _pushClipRRect( + EngineLayer layer, + Float32List rrect, + int clipBehavior, + EngineLayer? oldLayer, + ); @override ClipPathEngineLayer pushClipPath( @@ -728,8 +735,15 @@ base class _NativeSceneBuilder extends NativeFieldWrapperClass1 implements Scene return layer; } - @Native, Handle, Pointer, Int32, Handle)>(symbol: 'SceneBuilder::pushClipPath') - external void _pushClipPath(EngineLayer layer, _NativePath path, int clipBehavior, EngineLayer? oldLayer); + @Native, Handle, Pointer, Int32, Handle)>( + symbol: 'SceneBuilder::pushClipPath', + ) + external void _pushClipPath( + EngineLayer layer, + _NativePath path, + int clipBehavior, + EngineLayer? oldLayer, + ); @override OpacityEngineLayer pushOpacity( @@ -745,14 +759,19 @@ base class _NativeSceneBuilder extends NativeFieldWrapperClass1 implements Scene return layer; } - @Native, Handle, Int32, Double, Double, Handle)>(symbol: 'SceneBuilder::pushOpacity') - external void _pushOpacity(EngineLayer layer, int alpha, double dx, double dy, EngineLayer? oldLayer); + @Native, Handle, Int32, Double, Double, Handle)>( + symbol: 'SceneBuilder::pushOpacity', + ) + external void _pushOpacity( + EngineLayer layer, + int alpha, + double dx, + double dy, + EngineLayer? oldLayer, + ); @override - ColorFilterEngineLayer pushColorFilter( - ColorFilter filter, { - ColorFilterEngineLayer? oldLayer, - }) { + ColorFilterEngineLayer pushColorFilter(ColorFilter filter, {ColorFilterEngineLayer? oldLayer}) { assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushColorFilter')); final _ColorFilter nativeFilter = filter._toNativeColorFilter()!; final EngineLayer engineLayer = _NativeEngineLayer._(); @@ -762,7 +781,9 @@ base class _NativeSceneBuilder extends NativeFieldWrapperClass1 implements Scene return layer; } - @Native, Handle, Pointer, Handle)>(symbol: 'SceneBuilder::pushColorFilter') + @Native, Handle, Pointer, Handle)>( + symbol: 'SceneBuilder::pushColorFilter', + ) external void _pushColorFilter(EngineLayer layer, _ColorFilter filter, EngineLayer? oldLayer); @override @@ -780,8 +801,16 @@ base class _NativeSceneBuilder extends NativeFieldWrapperClass1 implements Scene return layer; } - @Native, Handle, Pointer, Double, Double, Handle)>(symbol: 'SceneBuilder::pushImageFilter') - external void _pushImageFilter(EngineLayer outEngineLayer, _ImageFilter filter, double dx, double dy, EngineLayer? oldLayer); + @Native, Handle, Pointer, Double, Double, Handle)>( + symbol: 'SceneBuilder::pushImageFilter', + ) + external void _pushImageFilter( + EngineLayer outEngineLayer, + _ImageFilter filter, + double dx, + double dy, + EngineLayer? oldLayer, + ); @override BackdropFilterEngineLayer pushBackdropFilter( @@ -792,14 +821,28 @@ base class _NativeSceneBuilder extends NativeFieldWrapperClass1 implements Scene }) { assert(_debugCheckCanBeUsedAsOldLayer(oldLayer, 'pushBackdropFilter')); final EngineLayer engineLayer = _NativeEngineLayer._(); - _pushBackdropFilter(engineLayer, filter._toNativeImageFilter(), blendMode.index, backdropId, oldLayer?._nativeLayer); + _pushBackdropFilter( + engineLayer, + filter._toNativeImageFilter(), + blendMode.index, + backdropId, + oldLayer?._nativeLayer, + ); final BackdropFilterEngineLayer layer = BackdropFilterEngineLayer._(engineLayer); assert(_debugPushLayer(layer)); return layer; } - @Native, Handle, Pointer, Int32, Handle, Handle)>(symbol: 'SceneBuilder::pushBackdropFilter') - external void _pushBackdropFilter(EngineLayer outEngineLayer, _ImageFilter filter, int blendMode, int? backdropId, EngineLayer? oldLayer); + @Native, Handle, Pointer, Int32, Handle, Handle)>( + symbol: 'SceneBuilder::pushBackdropFilter', + ) + external void _pushBackdropFilter( + EngineLayer outEngineLayer, + _ImageFilter filter, + int blendMode, + int? backdropId, + EngineLayer? oldLayer, + ); @override ShaderMaskEngineLayer pushShaderMask( @@ -827,17 +870,31 @@ base class _NativeSceneBuilder extends NativeFieldWrapperClass1 implements Scene return layer; } - @Native, Handle, Pointer, Double, Double, Double, Double, Int32, Int32, Handle)>(symbol: 'SceneBuilder::pushShaderMask') + @Native< + Void Function( + Pointer, + Handle, + Pointer, + Double, + Double, + Double, + Double, + Int32, + Int32, + Handle, + ) + >(symbol: 'SceneBuilder::pushShaderMask') external void _pushShaderMask( - EngineLayer engineLayer, - Shader shader, - double maskRectLeft, - double maskRectRight, - double maskRectTop, - double maskRectBottom, - int blendMode, - int filterQualityIndex, - EngineLayer? oldLayer); + EngineLayer engineLayer, + Shader shader, + double maskRectLeft, + double maskRectRight, + double maskRectTop, + double maskRectBottom, + int blendMode, + int filterQualityIndex, + EngineLayer? oldLayer, + ); @override void pop() { @@ -886,8 +943,17 @@ base class _NativeSceneBuilder extends NativeFieldWrapperClass1 implements Scene _addPerformanceOverlay(enabledOptions, bounds.left, bounds.right, bounds.top, bounds.bottom); } - @Native, Uint64, Double, Double, Double, Double)>(symbol: 'SceneBuilder::addPerformanceOverlay', isLeaf: true) - external void _addPerformanceOverlay(int enabledOptions, double left, double right, double top, double bottom); + @Native, Uint64, Double, Double, Double, Double)>( + symbol: 'SceneBuilder::addPerformanceOverlay', + isLeaf: true, + ) + external void _addPerformanceOverlay( + int enabledOptions, + double left, + double right, + double top, + double bottom, + ); @override void addPicture( @@ -901,7 +967,9 @@ base class _NativeSceneBuilder extends NativeFieldWrapperClass1 implements Scene _addPicture(offset.dx, offset.dy, picture as _NativePicture, hints); } - @Native, Double, Double, Pointer, Int32)>(symbol: 'SceneBuilder::addPicture') + @Native, Double, Double, Pointer, Int32)>( + symbol: 'SceneBuilder::addPicture', + ) external void _addPicture(double dx, double dy, _NativePicture picture, int hints); @override @@ -916,8 +984,19 @@ base class _NativeSceneBuilder extends NativeFieldWrapperClass1 implements Scene _addTexture(offset.dx, offset.dy, width, height, textureId, freeze, filterQuality.index); } - @Native, Double, Double, Double, Double, Int64, Bool, Int32)>(symbol: 'SceneBuilder::addTexture', isLeaf: true) - external void _addTexture(double dx, double dy, double width, double height, int textureId, bool freeze, int filterQuality); + @Native, Double, Double, Double, Double, Int64, Bool, Int32)>( + symbol: 'SceneBuilder::addTexture', + isLeaf: true, + ) + external void _addTexture( + double dx, + double dy, + double width, + double height, + int textureId, + bool freeze, + int filterQuality, + ); @override void addPlatformView( @@ -929,7 +1008,10 @@ base class _NativeSceneBuilder extends NativeFieldWrapperClass1 implements Scene _addPlatformView(offset.dx, offset.dy, width, height, viewId); } - @Native, Double, Double, Double, Double, Int64)>(symbol: 'SceneBuilder::addPlatformView', isLeaf: true) + @Native, Double, Double, Double, Double, Int64)>( + symbol: 'SceneBuilder::addPlatformView', + isLeaf: true, + ) external void _addPlatformView(double dx, double dy, double width, double height, int viewId); @override diff --git a/lib/ui/fixtures/ui_test.dart b/lib/ui/fixtures/ui_test.dart index c7b0967d6a4dd..5ea28f6c57ea0 100644 --- a/lib/ui/fixtures/ui_test.dart +++ b/lib/ui/fixtures/ui_test.dart @@ -90,7 +90,9 @@ external _validateEngineLayerDispose(); @pragma('vm:entry-point') Future createSingleFrameCodec() async { - final ImmutableBuffer buffer = await ImmutableBuffer.fromUint8List(Uint8List.fromList(List.filled(4, 100))); + final ImmutableBuffer buffer = await ImmutableBuffer.fromUint8List( + Uint8List.fromList(List.filled(4, 100)), + ); final ImageDescriptor descriptor = ImageDescriptor.raw( buffer, width: 1, @@ -147,30 +149,28 @@ void sendSemanticsUpdate() { final SemanticsUpdateBuilder builder = SemanticsUpdateBuilder(); final String identifier = "identifier"; final String label = "label"; - final List labelAttributes = [ + final List labelAttributes = [ SpellOutStringAttribute(range: TextRange(start: 1, end: 2)), ]; final String value = "value"; - final List valueAttributes = [ + final List valueAttributes = [ SpellOutStringAttribute(range: TextRange(start: 2, end: 3)), ]; final String increasedValue = "increasedValue"; - final List increasedValueAttributes = [ + final List increasedValueAttributes = [ SpellOutStringAttribute(range: TextRange(start: 4, end: 5)), ]; final String decreasedValue = "decreasedValue"; - final List decreasedValueAttributes = [ + final List decreasedValueAttributes = [ SpellOutStringAttribute(range: TextRange(start: 5, end: 6)), ]; final String hint = "hint"; - final List hintAttributes = [ - LocaleStringAttribute( - locale: Locale('en', 'MX'), range: TextRange(start: 0, end: 1), - ), + final List hintAttributes = [ + LocaleStringAttribute(locale: Locale('en', 'MX'), range: TextRange(start: 0, end: 1)), ]; String tooltip = "tooltip"; @@ -318,9 +318,10 @@ external void validateConfiguration(); Future encodeImageProducesExternalUint8List() async { final PictureRecorder pictureRecorder = PictureRecorder(); final Canvas canvas = Canvas(pictureRecorder); - final Paint paint = Paint() - ..color = Color.fromRGBO(255, 255, 255, 1.0) - ..style = PaintingStyle.fill; + final Paint paint = + Paint() + ..color = Color.fromRGBO(255, 255, 255, 1.0) + ..style = PaintingStyle.fill; final Offset c = Offset(50.0, 50.0); canvas.drawCircle(c, 25.0, paint); final Picture picture = pictureRecorder.endRecording(); @@ -350,9 +351,10 @@ external void _validateNotNull(Object? object); Future toByteDataWithoutGPU() async { final PictureRecorder pictureRecorder = PictureRecorder(); final Canvas canvas = Canvas(pictureRecorder); - final Paint paint = Paint() - ..color = Color.fromRGBO(255, 255, 255, 1.0) - ..style = PaintingStyle.fill; + final Paint paint = + Paint() + ..color = Color.fromRGBO(255, 255, 255, 1.0) + ..style = PaintingStyle.fill; final Offset c = Offset(50.0, 50.0); canvas.drawCircle(c, 25.0, paint); final Picture picture = pictureRecorder.endRecording(); @@ -375,9 +377,10 @@ Future toByteDataWithoutGPU() async { Future toByteDataRetries() async { final PictureRecorder pictureRecorder = PictureRecorder(); final Canvas canvas = Canvas(pictureRecorder); - final Paint paint = Paint() - ..color = Color.fromRGBO(255, 255, 255, 1.0) - ..style = PaintingStyle.fill; + final Paint paint = + Paint() + ..color = Color.fromRGBO(255, 255, 255, 1.0) + ..style = PaintingStyle.fill; final Offset c = Offset(50.0, 50.0); canvas.drawCircle(c, 25.0, paint); final Picture picture = pictureRecorder.endRecording(); @@ -398,9 +401,10 @@ Future toByteDataRetries() async { Future toByteDataRetryOverflows() async { final PictureRecorder pictureRecorder = PictureRecorder(); final Canvas canvas = Canvas(pictureRecorder); - final Paint paint = Paint() - ..color = Color.fromRGBO(255, 255, 255, 1.0) - ..style = PaintingStyle.fill; + final Paint paint = + Paint() + ..color = Color.fromRGBO(255, 255, 255, 1.0) + ..style = PaintingStyle.fill; final Offset c = Offset(50.0, 50.0); canvas.drawCircle(c, 25.0, paint); final Picture picture = pictureRecorder.endRecording(); @@ -437,9 +441,10 @@ Future toByteDataRetryOverflows() async { Future toImageRetries() async { final PictureRecorder pictureRecorder = PictureRecorder(); final Canvas canvas = Canvas(pictureRecorder); - final Paint paint = Paint() - ..color = Color.fromRGBO(255, 255, 255, 1.0) - ..style = PaintingStyle.fill; + final Paint paint = + Paint() + ..color = Color.fromRGBO(255, 255, 255, 1.0) + ..style = PaintingStyle.fill; final Offset c = Offset(50.0, 50.0); canvas.drawCircle(c, 25.0, paint); final Picture picture = pictureRecorder.endRecording(); @@ -459,9 +464,10 @@ Future toImageRetries() async { Future toImageRetryOverflows() async { final PictureRecorder pictureRecorder = PictureRecorder(); final Canvas canvas = Canvas(pictureRecorder); - final Paint paint = Paint() - ..color = Color.fromRGBO(255, 255, 255, 1.0) - ..style = PaintingStyle.fill; + final Paint paint = + Paint() + ..color = Color.fromRGBO(255, 255, 255, 1.0) + ..style = PaintingStyle.fill; final Offset c = Offset(50.0, 50.0); canvas.drawCircle(c, 25.0, paint); final Picture picture = pictureRecorder.endRecording(); @@ -562,7 +568,8 @@ void convertPaintToDlPaint() { paint.style = PaintingStyle.stroke; _convertPaintToDlPaint(paint); } -@pragma('vm:external-name', 'ConvertPaintToDlPaint') + +@pragma('vm:external-name', 'ConvertPaintToDlPaint') external void _convertPaintToDlPaint(Paint paint); /// Hooks for platform_configuration_unittests.cc @@ -623,24 +630,24 @@ void hooksTests() async { 21, 0, // window Id 0.1234, // device pixel ratio - 0.0, // width - 0.0, // height - 0.0, // padding top - 0.0, // padding right - 0.0, // padding bottom - 0.0, // padding left - 0.0, // inset top - 0.0, // inset right - 0.0, // inset bottom - 0.0, // inset left - 0.0, // system gesture inset top - 0.0, // system gesture inset right - 0.0, // system gesture inset bottom - 0.0, // system gesture inset left - 22.0, // physicalTouchSlop - [], // display features bounds - [], // display features types - [], // display features states + 0.0, // width + 0.0, // height + 0.0, // padding top + 0.0, // padding right + 0.0, // padding bottom + 0.0, // padding left + 0.0, // inset top + 0.0, // inset right + 0.0, // inset bottom + 0.0, // inset left + 0.0, // system gesture inset top + 0.0, // system gesture inset right + 0.0, // system gesture inset bottom + 0.0, // system gesture inset left + 22.0, // physicalTouchSlop + [], // display features bounds + [], // display features types + [], // display features states 0, // Display ID ); @@ -675,36 +682,48 @@ void hooksTests() async { _callHook('_updateUserSettingsData', 1, '{}'); }); - await test('PlatformDispatcher.locale returns unknown locale when locales is set to empty list', () { - late Locale locale; - int callCount = 0; - runZoned(() { - window.onLocaleChanged = () { - locale = PlatformDispatcher.instance.locale; - callCount += 1; - }; - }); + await test( + 'PlatformDispatcher.locale returns unknown locale when locales is set to empty list', + () { + late Locale locale; + int callCount = 0; + runZoned(() { + window.onLocaleChanged = () { + locale = PlatformDispatcher.instance.locale; + callCount += 1; + }; + }); - const Locale fakeLocale = Locale.fromSubtags(languageCode: '1', countryCode: '2', scriptCode: '3'); - _callHook('_updateLocales', 1, [fakeLocale.languageCode, fakeLocale.countryCode!, fakeLocale.scriptCode!, '']); - if (callCount != 1) { - throw 'Expected 1 call, have $callCount'; - } - if (locale != fakeLocale) { - throw 'Expected $locale to match $fakeLocale'; - } - _callHook('_updateLocales', 1, []); - if (callCount != 2) { - throw 'Expected 2 calls, have $callCount'; - } + const Locale fakeLocale = Locale.fromSubtags( + languageCode: '1', + countryCode: '2', + scriptCode: '3', + ); + _callHook('_updateLocales', 1, [ + fakeLocale.languageCode, + fakeLocale.countryCode!, + fakeLocale.scriptCode!, + '', + ]); + if (callCount != 1) { + throw 'Expected 1 call, have $callCount'; + } + if (locale != fakeLocale) { + throw 'Expected $locale to match $fakeLocale'; + } + _callHook('_updateLocales', 1, []); + if (callCount != 2) { + throw 'Expected 2 calls, have $callCount'; + } - if (locale != const Locale.fromSubtags()) { - throw '$locale did not equal ${Locale.fromSubtags()}'; - } - if (locale.languageCode != 'und') { - throw '${locale.languageCode} did not equal "und"'; - } - }); + if (locale != const Locale.fromSubtags()) { + throw '$locale did not equal ${Locale.fromSubtags()}'; + } + if (locale.languageCode != 'und') { + throw '${locale.languageCode} did not equal "und"'; + } + }, + ); await test('deprecated region equals', () { // These are equal because ZR is deprecated and was mapped to CD. @@ -740,9 +759,9 @@ void hooksTests() async { 0.0, // systemGestureInsetBottom 0.0, // systemGestureInsetLeft 22.0, // physicalTouchSlop - [], // display features bounds - [], // display features types - [], // display features states + [], // display features bounds + [], // display features types + [], // display features states 0, // Display ID ); @@ -771,9 +790,9 @@ void hooksTests() async { 44.0, // systemGestureInsetBottom 0.0, // systemGestureInsetLeft 22.0, // physicalTouchSlop - [], // display features bounds - [], // display features types - [], // display features states + [], // display features bounds + [], // display features types + [], // display features states 0, // Display ID ); @@ -804,14 +823,13 @@ void hooksTests() async { 0.0, // systemGestureInsetBottom 0.0, // systemGestureInsetLeft 11.0, // physicalTouchSlop - [], // display features bounds - [], // display features types - [], // display features states + [], // display features bounds + [], // display features types + [], // display features states 0, // Display ID ); - expectEquals(window.gestureSettings, - GestureSettings(physicalTouchSlop: 11.0)); + expectEquals(window.gestureSettings, GestureSettings(physicalTouchSlop: 11.0)); _callHook( '_updateWindowMetrics', @@ -833,14 +851,13 @@ void hooksTests() async { 44.0, // systemGestureInsetBottom 0.0, // systemGestureInsetLeft -1.0, // physicalTouchSlop - [], // display features bounds - [], // display features types - [], // display features states + [], // display features bounds + [], // display features types + [], // display features states 0, // Display ID ); - expectEquals(window.gestureSettings, - GestureSettings(physicalTouchSlop: null)); + expectEquals(window.gestureSettings, GestureSettings(physicalTouchSlop: null)); _callHook( '_updateWindowMetrics', @@ -862,14 +879,13 @@ void hooksTests() async { 44.0, // systemGestureInsetBottom 0.0, // systemGestureInsetLeft 22.0, // physicalTouchSlop - [], // display features bounds - [], // display features types - [], // display features states + [], // display features bounds + [], // display features types + [], // display features states 0, // Display ID ); - expectEquals(window.gestureSettings, - GestureSettings(physicalTouchSlop: 22.0)); + expectEquals(window.gestureSettings, GestureSettings(physicalTouchSlop: 22.0)); }); await test('onLocaleChanged preserves callback zone', () { @@ -1034,7 +1050,11 @@ void hooksTests() async { window.onTextScaleFactorChanged!(); - _callHook('_updateUserSettingsData', 1, '{"textScaleFactor": 0.5, "platformBrightness": "light", "alwaysUse24HourFormat": true}'); + _callHook( + '_updateUserSettingsData', + 1, + '{"textScaleFactor": 0.5, "platformBrightness": "light", "alwaysUse24HourFormat": true}', + ); expectIdentical(runZoneTextScaleFactor, innerZone); expectEquals(textScaleFactor, 0.5); @@ -1042,7 +1062,11 @@ void hooksTests() async { platformBrightness = null; window.onPlatformBrightnessChanged!(); - _callHook('_updateUserSettingsData', 1, '{"textScaleFactor": 0.5, "platformBrightness": "dark", "alwaysUse24HourFormat": true}'); + _callHook( + '_updateUserSettingsData', + 1, + '{"textScaleFactor": 0.5, "platformBrightness": "dark", "alwaysUse24HourFormat": true}', + ); expectIdentical(runZonePlatformBrightness, innerZone); expectEquals(platformBrightness, Brightness.dark); }); @@ -1079,7 +1103,9 @@ void hooksTests() async { }; }); - _callHook('_updateDisplays', 5, [0], [800], [600], [1.5], [65]); + _callHook('_updateDisplays', 5, [0], [800], [600], [1.5], [ + 65, + ]); expectNotEquals(runZone, null); expectIdentical(runZone, innerZone); expectEquals(display.id, 0); @@ -1092,6 +1118,7 @@ void hooksTests() async { String? callbacker(void Function(Object? arg) cb) { return 'failure'; } + Object? error; try { await _futurize(callbacker); @@ -1105,6 +1132,7 @@ void hooksTests() async { String? callbacker(void Function(Object? arg) cb) { cb(null); // indicates failure } + Object? error; try { await _futurize(callbacker); @@ -1120,6 +1148,7 @@ void hooksTests() async { cb(null); // indicates failure }); } + Object? error; try { await _futurize(callbacker); @@ -1133,6 +1162,7 @@ void hooksTests() async { String? callbacker(void Function(Object? arg) cb) { cb(true); } + final Object? result = await _futurize(callbacker); expectEquals(result, true); @@ -1144,6 +1174,7 @@ void hooksTests() async { cb(true); }); } + final Object? result = await _futurize(callbacker); expectEquals(result, true); @@ -1166,7 +1197,9 @@ void hooksTests() async { Isolate.spawn(_backgroundIsolateSendWithoutRegistering, receivePort.sendPort); bool didError = await receivePort.first as bool; if (!didError) { - throw Exception('Expected an error when not registering a root isolate and sending port messages.'); + throw Exception( + 'Expected an error when not registering a root isolate and sending port messages.', + ); } }); @@ -1186,12 +1219,7 @@ void _backgroundIsolateSendWithoutRegistering(SendPort port) { bool didError = false; ReceivePort messagePort = ReceivePort(); try { - PlatformDispatcher.instance.sendPortPlatformMessage( - 'foo', - null, - 1, - messagePort.sendPort, - ); + PlatformDispatcher.instance.sendPortPlatformMessage('foo', null, 1, messagePort.sendPort); } catch (_) { didError = true; } @@ -1221,8 +1249,7 @@ Future _futurize(_Callbacker callbacker) { } }); sync = false; - if (error != null) - throw Exception(error); + if (error != null) throw Exception(error); return completer.future; } diff --git a/lib/ui/geometry.dart b/lib/ui/geometry.dart index 6f00295aaa40a..f2756bf9434a6 100644 --- a/lib/ui/geometry.dart +++ b/lib/ui/geometry.dart @@ -81,9 +81,7 @@ abstract class OffsetBase { /// the right-hand-side operand respectively. Returns false otherwise. @override bool operator ==(Object other) { - return other is OffsetBase - && other._dx == _dx - && other._dy == _dy; + return other is OffsetBase && other._dx == _dx && other._dy == _dy; } @override @@ -124,7 +122,7 @@ class Offset extends OffsetBase { /// The direction is in radians clockwise from the positive x-axis. /// /// The distance can be omitted, to create a unit vector (distance = 1.0). - factory Offset.fromDirection(double direction, [ double distance = 1.0 ]) { + factory Offset.fromDirection(double direction, [double distance = 1.0]) { return Offset(distance * math.cos(direction), distance * math.sin(direction)); } @@ -224,7 +222,8 @@ class Offset extends OffsetBase { /// Offset c = a + b; // same as: a.translate(b.dx, b.dy) /// Offset d = a - b; // same as: a.translate(-b.dx, -b.dy) /// ``` - Offset translate(double translateX, double translateY) => Offset(dx + translateX, dy + translateY); + Offset translate(double translateX, double translateY) => + Offset(dx + translateX, dy + translateY); /// Unary negation operator. /// @@ -275,7 +274,8 @@ class Offset extends OffsetBase { /// Returns an offset whose coordinates are the coordinates of the /// left-hand-side operand (an Offset) divided by the scalar right-hand-side /// operand (a double), rounded towards zero. - Offset operator ~/(double operand) => Offset((dx ~/ operand).toDouble(), (dy ~/ operand).toDouble()); + Offset operator ~/(double operand) => + Offset((dx ~/ operand).toDouble(), (dy ~/ operand).toDouble()); /// Modulo (remainder) operator. /// @@ -330,9 +330,7 @@ class Offset extends OffsetBase { /// Compares two Offsets for equality. @override bool operator ==(Object other) { - return other is Offset - && other.dx == dx - && other.dy == dy; + return other is Offset && other.dx == dx && other.dy == dy; } @override @@ -480,7 +478,8 @@ class Size extends OffsetBase { /// Returns a [Size] whose dimensions are the dimensions of the left-hand-side /// operand (a [Size]) divided by the scalar right-hand-side operand (a /// [double]), rounded towards zero. - Size operator ~/(double operand) => Size((width ~/ operand).toDouble(), (height ~/ operand).toDouble()); + Size operator ~/(double operand) => + Size((width ~/ operand).toDouble(), (height ~/ operand).toDouble()); /// Modulo (remainder) operator. /// @@ -606,9 +605,7 @@ class Size extends OffsetBase { // We don't compare the runtimeType because of _DebugSize in the framework. @override bool operator ==(Object other) { - return other is Size - && other._dx == _dx - && other._dy == _dy; + return other is Size && other._dx == _dx && other._dy == _dy; } @override @@ -642,7 +639,8 @@ class Rect { /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/rect_from_ltwh.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/rect_from_ltwh_dark.png#gh-dark-mode-only) - const Rect.fromLTWH(double left, double top, double width, double height) : this.fromLTRB(left, top, left + width, top + height); + const Rect.fromLTWH(double left, double top, double width, double height) + : this.fromLTRB(left, top, left + width, top + height); /// Construct a rectangle that bounds the given circle. /// @@ -650,11 +648,8 @@ class Rect { /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/rect_from_circle.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/rect_from_circle_dark.png#gh-dark-mode-only) - Rect.fromCircle({ required Offset center, required double radius }) : this.fromCenter( - center: center, - width: radius * 2, - height: radius * 2, - ); + Rect.fromCircle({required Offset center, required double radius}) + : this.fromCenter(center: center, width: radius * 2, height: radius * 2); /// Constructs a rectangle from its center point, width, and height. /// @@ -662,24 +657,26 @@ class Rect { /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/rect_from_center.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/rect_from_center_dark.png#gh-dark-mode-only) - Rect.fromCenter({ required Offset center, required double width, required double height }) : this.fromLTRB( - center.dx - width / 2, - center.dy - height / 2, - center.dx + width / 2, - center.dy + height / 2, - ); + Rect.fromCenter({required Offset center, required double width, required double height}) + : this.fromLTRB( + center.dx - width / 2, + center.dy - height / 2, + center.dx + width / 2, + center.dy + height / 2, + ); /// Construct the smallest rectangle that encloses the given offsets, treating /// them as vectors from the origin. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/rect_from_points.png#gh-light-mode-only) /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/rect_from_points_dark.png#gh-dark-mode-only) - Rect.fromPoints(Offset a, Offset b) : this.fromLTRB( - math.min(a.dx, b.dx), - math.min(a.dy, b.dy), - math.max(a.dx, b.dx), - math.max(a.dy, b.dy), - ); + Rect.fromPoints(Offset a, Offset b) + : this.fromLTRB( + math.min(a.dx, b.dx), + math.min(a.dy, b.dy), + math.max(a.dx, b.dx), + math.max(a.dy, b.dy), + ); Float32List _getValue32() { final Float32List result = Float32List(4); @@ -724,15 +721,20 @@ class Rect { /// /// This covers the space from -1e9,-1e9 to 1e9,1e9. /// This is the space over which graphics operations are valid. - static const Rect largest = Rect.fromLTRB(-_giantScalar, -_giantScalar, _giantScalar, _giantScalar); + static const Rect largest = Rect.fromLTRB( + -_giantScalar, + -_giantScalar, + _giantScalar, + _giantScalar, + ); /// Whether any of the coordinates of this rectangle are equal to positive infinity. // included for consistency with Offset and Size bool get isInfinite { - return left >= double.infinity - || top >= double.infinity - || right >= double.infinity - || bottom >= double.infinity; + return left >= double.infinity || + top >= double.infinity || + right >= double.infinity || + bottom >= double.infinity; } /// Whether all coordinates of this rectangle are finite. @@ -756,7 +758,12 @@ class Rect { /// To translate a rectangle by an [Offset] rather than by separate x and y /// components, consider [shift]. Rect translate(double translateX, double translateY) { - return Rect.fromLTRB(left + translateX, top + translateY, right + translateX, bottom + translateY); + return Rect.fromLTRB( + left + translateX, + top + translateY, + right + translateX, + bottom + translateY, + ); } /// Returns a new rectangle with edges moved outwards by the given delta. @@ -776,7 +783,7 @@ class Rect { math.max(left, other.left), math.max(top, other.top), math.min(right, other.right), - math.min(bottom, other.bottom) + math.min(bottom, other.bottom), ); } @@ -784,10 +791,10 @@ class Rect { /// rectangle and the given rectangle. Rect expandToInclude(Rect other) { return Rect.fromLTRB( - math.min(left, other.left), - math.min(top, other.top), - math.max(right, other.right), - math.max(bottom, other.bottom), + math.min(left, other.left), + math.min(top, other.top), + math.max(right, other.right), + math.max(bottom, other.bottom), ); } @@ -911,18 +918,19 @@ class Rect { if (runtimeType != other.runtimeType) { return false; } - return other is Rect - && other.left == left - && other.top == top - && other.right == right - && other.bottom == bottom; + return other is Rect && + other.left == left && + other.top == top && + other.right == right && + other.bottom == bottom; } @override int get hashCode => Object.hash(left, top, right, bottom); @override - String toString() => 'Rect.fromLTRB(${left.toStringAsFixed(1)}, ${top.toStringAsFixed(1)}, ${right.toStringAsFixed(1)}, ${bottom.toStringAsFixed(1)})'; + String toString() => + 'Rect.fromLTRB(${left.toStringAsFixed(1)}, ${top.toStringAsFixed(1)}, ${right.toStringAsFixed(1)}, ${bottom.toStringAsFixed(1)})'; } /// A radius for either circular or elliptical shapes. @@ -969,12 +977,7 @@ class Radius { /// /// The `minimumX` and `minimumY` values default to `-double.infinity`, and /// the `maximumX` and `maximumY` values default to `double.infinity`. - Radius clampValues({ - double? minimumX, - double? minimumY, - double? maximumX, - double? maximumY, - }) { + Radius clampValues({double? minimumX, double? minimumY, double? maximumX, double? maximumY}) { return Radius.elliptical( clampDouble(x, minimumX ?? -double.infinity, maximumX ?? double.infinity), clampDouble(y, minimumY ?? -double.infinity, maximumY ?? double.infinity), @@ -1024,7 +1027,8 @@ class Radius { /// Returns a radius whose coordinates are the coordinates of the /// left-hand-side operand (a radius) divided by the scalar right-hand-side /// operand (a double), rounded towards zero. - Radius operator ~/(double operand) => Radius.elliptical((x ~/ operand).toDouble(), (y ~/ operand).toDouble()); + Radius operator ~/(double operand) => + Radius.elliptical((x ~/ operand).toDouble(), (y ~/ operand).toDouble()); /// Modulo (remainder) operator. /// @@ -1060,10 +1064,7 @@ class Radius { if (a == null) { return Radius.elliptical(b.x * t, b.y * t); } else { - return Radius.elliptical( - _lerpDouble(a.x, b.x, t), - _lerpDouble(a.y, b.y, t), - ); + return Radius.elliptical(_lerpDouble(a.x, b.x, t), _lerpDouble(a.y, b.y, t)); } } } @@ -1077,9 +1078,7 @@ class Radius { return false; } - return other is Radius - && other.x == x - && other.y == y; + return other is Radius && other.x == x && other.y == y; } @override @@ -1087,9 +1086,10 @@ class Radius { @override String toString() { - return x == y ? 'Radius.circular(${x.toStringAsFixed(1)})' : - 'Radius.elliptical(${x.toStringAsFixed(1)}, ' - '${y.toStringAsFixed(1)})'; + return x == y + ? 'Radius.circular(${x.toStringAsFixed(1)})' + : 'Radius.elliptical(${x.toStringAsFixed(1)}, ' + '${y.toStringAsFixed(1)})'; } } @@ -1107,31 +1107,25 @@ class RRect { double radiusX, double radiusY, ) : this._raw( - top: top, - left: left, - right: right, - bottom: bottom, - tlRadiusX: radiusX, - tlRadiusY: radiusY, - trRadiusX: radiusX, - trRadiusY: radiusY, - blRadiusX: radiusX, - blRadiusY: radiusY, - brRadiusX: radiusX, - brRadiusY: radiusY, - ); + top: top, + left: left, + right: right, + bottom: bottom, + tlRadiusX: radiusX, + tlRadiusY: radiusY, + trRadiusX: radiusX, + trRadiusY: radiusY, + blRadiusX: radiusX, + blRadiusY: radiusY, + brRadiusX: radiusX, + brRadiusY: radiusY, + ); /// Construct a rounded rectangle from its left, top, right, and bottom edges, /// and the same radius in each corner. /// /// Will assert in debug mode if the `radius` is negative in either x or y. - RRect.fromLTRBR( - double left, - double top, - double right, - double bottom, - Radius radius, - ) + RRect.fromLTRBR(double left, double top, double right, double bottom, Radius radius) : this._raw( top: top, left: left, @@ -1222,27 +1216,25 @@ class RRect { /// The corner radii default to [Radius.zero], i.e. right-angled corners. Will /// assert in debug mode if any of the radii are negative in either x or y. RRect.fromRectAndCorners( - Rect rect, - { - Radius topLeft = Radius.zero, - Radius topRight = Radius.zero, - Radius bottomRight = Radius.zero, - Radius bottomLeft = Radius.zero - } - ) : this._raw( - top: rect.top, - left: rect.left, - right: rect.right, - bottom: rect.bottom, - tlRadiusX: topLeft.x, - tlRadiusY: topLeft.y, - trRadiusX: topRight.x, - trRadiusY: topRight.y, - blRadiusX: bottomLeft.x, - blRadiusY: bottomLeft.y, - brRadiusX: bottomRight.x, - brRadiusY: bottomRight.y, - ); + Rect rect, { + Radius topLeft = Radius.zero, + Radius topRight = Radius.zero, + Radius bottomRight = Radius.zero, + Radius bottomLeft = Radius.zero, + }) : this._raw( + top: rect.top, + left: rect.left, + right: rect.right, + bottom: rect.bottom, + tlRadiusX: topLeft.x, + tlRadiusY: topLeft.y, + trRadiusX: topRight.x, + trRadiusY: topRight.y, + blRadiusX: bottomLeft.x, + blRadiusY: bottomLeft.y, + brRadiusX: bottomRight.x, + brRadiusY: bottomRight.y, + ); const RRect._raw({ this.left = 0.0, @@ -1266,7 +1258,7 @@ class RRect { assert(blRadiusX >= 0), assert(blRadiusY >= 0); - Float32List _getValue32() { + Float32List _getValue32() { final Float32List result = Float32List(12); result[0] = left; result[1] = top; @@ -1399,7 +1391,7 @@ class RRect { left + leftRadius * kInsetFactor, top + topRadius * kInsetFactor, right - rightRadius * kInsetFactor, - bottom - bottomRadius * kInsetFactor + bottom - bottomRadius * kInsetFactor, ); } @@ -1418,7 +1410,7 @@ class RRect { left + leftRadius, top + topRadius, right - rightRadius, - bottom - bottomRadius + bottom - bottomRadius, ); } @@ -1429,12 +1421,7 @@ class RRect { Rect get wideMiddleRect { final double topRadius = math.max(tlRadiusY, trRadiusY); final double bottomRadius = math.max(brRadiusY, blRadiusY); - return Rect.fromLTRB( - left, - top + topRadius, - right, - bottom - bottomRadius - ); + return Rect.fromLTRB(left, top + topRadius, right, bottom - bottomRadius); } /// The biggest rectangle that is entirely inside the rounded rectangle and @@ -1444,12 +1431,7 @@ class RRect { Rect get tallMiddleRect { final double leftRadius = math.max(blRadiusX, tlRadiusX); final double rightRadius = math.max(trRadiusX, brRadiusX); - return Rect.fromLTRB( - left + leftRadius, - top, - right - rightRadius, - bottom - ); + return Rect.fromLTRB(left + leftRadius, top, right - rightRadius, bottom); } /// Whether this rounded rectangle encloses a non-zero area. @@ -1463,26 +1445,26 @@ class RRect { /// corner radii. bool get isRect { return (tlRadiusX == 0.0 || tlRadiusY == 0.0) && - (trRadiusX == 0.0 || trRadiusY == 0.0) && - (blRadiusX == 0.0 || blRadiusY == 0.0) && - (brRadiusX == 0.0 || brRadiusY == 0.0); + (trRadiusX == 0.0 || trRadiusY == 0.0) && + (blRadiusX == 0.0 || blRadiusY == 0.0) && + (brRadiusX == 0.0 || brRadiusY == 0.0); } /// Whether this rounded rectangle has a side with no straight section. bool get isStadium { - return tlRadius == trRadius - && trRadius == brRadius - && brRadius == blRadius - && (width <= 2.0 * tlRadiusX || height <= 2.0 * tlRadiusY); + return tlRadius == trRadius && + trRadius == brRadius && + brRadius == blRadius && + (width <= 2.0 * tlRadiusX || height <= 2.0 * tlRadiusY); } /// Whether this rounded rectangle has no side with a straight section. bool get isEllipse { - return tlRadius == trRadius - && trRadius == brRadius - && brRadius == blRadius - && width <= 2.0 * tlRadiusX - && height <= 2.0 * tlRadiusY; + return tlRadius == trRadius && + trRadius == brRadius && + brRadius == blRadius && + width <= 2.0 * tlRadiusX && + height <= 2.0 * tlRadiusY; } /// Whether this rounded rectangle would draw as a circle. @@ -1497,9 +1479,19 @@ class RRect { double get longestSide => math.max(width.abs(), height.abs()); /// Whether any of the dimensions are `NaN`. - bool get hasNaN => left.isNaN || top.isNaN || right.isNaN || bottom.isNaN || - trRadiusX.isNaN || trRadiusY.isNaN || tlRadiusX.isNaN || tlRadiusY.isNaN || - brRadiusX.isNaN || brRadiusY.isNaN || blRadiusX.isNaN || blRadiusY.isNaN; + bool get hasNaN => + left.isNaN || + top.isNaN || + right.isNaN || + bottom.isNaN || + trRadiusX.isNaN || + trRadiusY.isNaN || + tlRadiusX.isNaN || + tlRadiusY.isNaN || + brRadiusX.isNaN || + brRadiusY.isNaN || + blRadiusX.isNaN || + blRadiusY.isNaN; /// The offset to the point halfway between the left and right and the top and /// bottom edges of this rectangle. @@ -1585,26 +1577,22 @@ class RRect { double radiusY; // check whether point is in one of the rounded corner areas // x, y -> translate to ellipse center - if (point.dx < left + scaled.tlRadiusX && - point.dy < top + scaled.tlRadiusY) { + if (point.dx < left + scaled.tlRadiusX && point.dy < top + scaled.tlRadiusY) { x = point.dx - left - scaled.tlRadiusX; y = point.dy - top - scaled.tlRadiusY; radiusX = scaled.tlRadiusX; radiusY = scaled.tlRadiusY; - } else if (point.dx > right - scaled.trRadiusX && - point.dy < top + scaled.trRadiusY) { + } else if (point.dx > right - scaled.trRadiusX && point.dy < top + scaled.trRadiusY) { x = point.dx - right + scaled.trRadiusX; y = point.dy - top - scaled.trRadiusY; radiusX = scaled.trRadiusX; radiusY = scaled.trRadiusY; - } else if (point.dx > right - scaled.brRadiusX && - point.dy > bottom - scaled.brRadiusY) { + } else if (point.dx > right - scaled.brRadiusX && point.dy > bottom - scaled.brRadiusY) { x = point.dx - right + scaled.brRadiusX; y = point.dy - bottom + scaled.brRadiusY; radiusX = scaled.brRadiusX; radiusY = scaled.brRadiusY; - } else if (point.dx < left + scaled.blRadiusX && - point.dy > bottom - scaled.blRadiusY) { + } else if (point.dx < left + scaled.blRadiusX && point.dy > bottom - scaled.blRadiusY) { x = point.dx - left - scaled.blRadiusX; y = point.dy - bottom + scaled.blRadiusY; radiusX = scaled.blRadiusX; @@ -1701,47 +1689,57 @@ class RRect { if (runtimeType != other.runtimeType) { return false; } - return other is RRect - && other.left == left - && other.top == top - && other.right == right - && other.bottom == bottom - && other.tlRadiusX == tlRadiusX - && other.tlRadiusY == tlRadiusY - && other.trRadiusX == trRadiusX - && other.trRadiusY == trRadiusY - && other.blRadiusX == blRadiusX - && other.blRadiusY == blRadiusY - && other.brRadiusX == brRadiusX - && other.brRadiusY == brRadiusY; + return other is RRect && + other.left == left && + other.top == top && + other.right == right && + other.bottom == bottom && + other.tlRadiusX == tlRadiusX && + other.tlRadiusY == tlRadiusY && + other.trRadiusX == trRadiusX && + other.trRadiusY == trRadiusY && + other.blRadiusX == blRadiusX && + other.blRadiusY == blRadiusY && + other.brRadiusX == brRadiusX && + other.brRadiusY == brRadiusY; } @override - int get hashCode => Object.hash(left, top, right, bottom, - tlRadiusX, tlRadiusY, trRadiusX, trRadiusY, - blRadiusX, blRadiusY, brRadiusX, brRadiusY); + int get hashCode => Object.hash( + left, + top, + right, + bottom, + tlRadiusX, + tlRadiusY, + trRadiusX, + trRadiusY, + blRadiusX, + blRadiusY, + brRadiusX, + brRadiusY, + ); @override String toString() { - final String rect = '${left.toStringAsFixed(1)}, ' - '${top.toStringAsFixed(1)}, ' - '${right.toStringAsFixed(1)}, ' - '${bottom.toStringAsFixed(1)}'; - if (tlRadius == trRadius && - trRadius == brRadius && - brRadius == blRadius) { + final String rect = + '${left.toStringAsFixed(1)}, ' + '${top.toStringAsFixed(1)}, ' + '${right.toStringAsFixed(1)}, ' + '${bottom.toStringAsFixed(1)}'; + if (tlRadius == trRadius && trRadius == brRadius && brRadius == blRadius) { if (tlRadius.x == tlRadius.y) { return 'RRect.fromLTRBR($rect, ${tlRadius.x.toStringAsFixed(1)})'; } return 'RRect.fromLTRBXY($rect, ${tlRadius.x.toStringAsFixed(1)}, ${tlRadius.y.toStringAsFixed(1)})'; } return 'RRect.fromLTRBAndCorners(' - '$rect, ' - 'topLeft: $tlRadius, ' - 'topRight: $trRadius, ' - 'bottomRight: $brRadius, ' - 'bottomLeft: $blRadius' - ')'; + '$rect, ' + 'topLeft: $tlRadius, ' + 'topRight: $trRadius, ' + 'bottomRight: $brRadius, ' + 'bottomLeft: $blRadius' + ')'; } } @@ -1808,7 +1806,7 @@ class RSTransform { required double anchorX, required double anchorY, required double translateX, - required double translateY + required double translateY, }) { final double scos = math.cos(rotation) * scale; final double ssin = math.sin(rotation) * scale; diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index b685f7280eaca..60db8e21833ba 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -73,12 +73,14 @@ void _updateDisplays( final List displays = []; for (int index = 0; index < ids.length; index += 1) { final int displayId = ids[index]; - displays.add(Display._( - id: displayId, - size: Size(widths[index], heights[index]), - devicePixelRatio: devicePixelRatios[index], - refreshRate: refreshRates[index], - )); + displays.add( + Display._( + id: displayId, + size: Size(widths[index], heights[index]), + devicePixelRatio: devicePixelRatios[index], + refreshRate: refreshRates[index], + ), + ); } PlatformDispatcher.instance._updateDisplays(displays); @@ -93,20 +95,23 @@ List _decodeDisplayFeatures({ assert(bounds.length / 4 == type.length, 'Bounds are rectangles, requiring 4 measurements each'); assert(type.length == state.length); final List result = []; - for(int i = 0; i < type.length; i++) { + for (int i = 0; i < type.length; i++) { final int rectOffset = i * 4; - result.add(DisplayFeature( - bounds: Rect.fromLTRB( - bounds[rectOffset] / devicePixelRatio, - bounds[rectOffset + 1] / devicePixelRatio, - bounds[rectOffset + 2] / devicePixelRatio, - bounds[rectOffset + 3] / devicePixelRatio, + result.add( + DisplayFeature( + bounds: Rect.fromLTRB( + bounds[rectOffset] / devicePixelRatio, + bounds[rectOffset + 1] / devicePixelRatio, + bounds[rectOffset + 2] / devicePixelRatio, + bounds[rectOffset + 3] / devicePixelRatio, + ), + type: DisplayFeatureType.values[type[i]], + state: + state[i] < DisplayFeatureState.values.length + ? DisplayFeatureState.values[state[i]] + : DisplayFeatureState.unknown, ), - type: DisplayFeatureType.values[type[i]], - state: state[i] < DisplayFeatureState.values.length - ? DisplayFeatureState.values[state[i]] - : DisplayFeatureState.unknown, - )); + ); } return result; } @@ -291,9 +296,7 @@ bool _onError(Object error, StackTrace? stackTrace) { typedef _ListStringArgFunction = Object? Function(List args); @pragma('vm:entry-point') -void _runMain(Function startMainIsolateFunction, - Function userMainFunction, - List args) { +void _runMain(Function startMainIsolateFunction, Function userMainFunction, List args) { // ignore: avoid_dynamic_calls startMainIsolateFunction(() { if (userMainFunction is _ListStringArgFunction) { @@ -355,7 +358,13 @@ void _invoke2(void Function(A1 a1, A2 a2)? callback, Zone zone, A1 arg1, /// The 3 in the name refers to the number of arguments expected by /// the callback (and thus passed to this function, in addition to the /// callback itself and the zone in which the callback is executed). -void _invoke3(void Function(A1 a1, A2 a2, A3 a3)? callback, Zone zone, A1 arg1, A2 arg2, A3 arg3) { +void _invoke3( + void Function(A1 a1, A2 a2, A3 a3)? callback, + Zone zone, + A1 arg1, + A2 arg2, + A3 arg3, +) { if (callback == null) { return; } @@ -388,24 +397,25 @@ bool _isLoopback(String host) { @pragma('vm:entry-point') void Function(Uri) _getHttpConnectionHookClosure(bool mayInsecurelyConnectToAllDomains) { return (Uri uri) { - final Object? zoneOverride = Zone.current[#flutter.io.allow_http]; - if (zoneOverride == true) { - return; - } - if (zoneOverride == false && uri.isScheme('http')) { - // Going to _isLoopback check before throwing - } else if (mayInsecurelyConnectToAllDomains || uri.isScheme('https')) { - // In absence of zone override, if engine setting allows the connection - // or if connection is to `https`, allow the connection. - return; - } - // Loopback connections are always allowed - // Check at last resort to avoid debug annoyance of try/on ArgumentError - if (_isLoopback(uri.host)) { - return; - } - throw UnsupportedError( - 'Non-https connection "$uri" is not supported by the platform. ' - 'Refer to https://flutter.dev/docs/release/breaking-changes/network-policy-ios-android.'); - }; + final Object? zoneOverride = Zone.current[#flutter.io.allow_http]; + if (zoneOverride == true) { + return; + } + if (zoneOverride == false && uri.isScheme('http')) { + // Going to _isLoopback check before throwing + } else if (mayInsecurelyConnectToAllDomains || uri.isScheme('https')) { + // In absence of zone override, if engine setting allows the connection + // or if connection is to `https`, allow the connection. + return; + } + // Loopback connections are always allowed + // Check at last resort to avoid debug annoyance of try/on ArgumentError + if (_isLoopback(uri.host)) { + return; + } + throw UnsupportedError( + 'Non-https connection "$uri" is not supported by the platform. ' + 'Refer to https://flutter.dev/docs/release/breaking-changes/network-policy-ios-android.', + ); + }; } diff --git a/lib/ui/key.dart b/lib/ui/key.dart index 1ea37200849a2..045dde1d3cf92 100644 --- a/lib/ui/key.dart +++ b/lib/ui/key.dart @@ -16,7 +16,7 @@ enum KeyEventType { /// The key is held, causing a repeated key input. repeat; - // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. + // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. // ignore: public_member_api_docs String get label { return switch (this) { @@ -144,15 +144,17 @@ class KeyData { // Equivalent to assert(divisorForValueMask == (1 << valueMaskWidth)). const int firstDivisorWidth = 28; - assert(divisorForValueMask == - (1 << firstDivisorWidth) * (1 << (valueMaskWidth - firstDivisorWidth))); + assert( + divisorForValueMask == (1 << firstDivisorWidth) * (1 << (valueMaskWidth - firstDivisorWidth)), + ); // JS only supports up to 2^53 - 1, therefore non-value bits can only // contain (maxSafeIntegerWidth - valueMaskWidth) bits. const int maxSafeIntegerWidth = 52; const int nonValueMask = (1 << (maxSafeIntegerWidth - valueMaskWidth)) - 1; - if (identical(0, 0.0)) { // Detects if we are on the web. + if (identical(0, 0.0)) { + // Detects if we are on the web. return (n / divisorForValueMask).floor() & nonValueMask; } else { return (n >> valueMaskWidth) & nonValueMask; @@ -162,33 +164,34 @@ class KeyData { String _logicalToString() { final String result = '0x${logical.toRadixString(16)}'; final int planeNum = _nonValueBits(logical) & 0x0FF; - final String planeDescription = (() { - switch (planeNum) { - case 0x000: - return ' (Unicode)'; - case 0x001: - return ' (Unprintable)'; - case 0x002: - return ' (Flutter)'; - case 0x011: - return ' (Android)'; - case 0x012: - return ' (Fuchsia)'; - case 0x013: - return ' (iOS)'; - case 0x014: - return ' (macOS)'; - case 0x015: - return ' (GTK)'; - case 0x016: - return ' (Windows)'; - case 0x017: - return ' (Web)'; - case 0x018: - return ' (GLFW)'; - } - return ''; - })(); + final String planeDescription = + (() { + switch (planeNum) { + case 0x000: + return ' (Unicode)'; + case 0x001: + return ' (Unprintable)'; + case 0x002: + return ' (Flutter)'; + case 0x011: + return ' (Android)'; + case 0x012: + return ' (Fuchsia)'; + case 0x013: + return ' (iOS)'; + case 0x014: + return ' (macOS)'; + case 0x015: + return ' (GTK)'; + case 0x016: + return ' (Windows)'; + case 0x017: + return ' (Web)'; + case 0x018: + return ' (GLFW)'; + } + return ''; + })(); return '$result$planeDescription'; } @@ -216,29 +219,30 @@ class KeyData { if (character == null) { return ''; } - final Iterable hexChars = character!.codeUnits - .map((int code) => code.toRadixString(16).padLeft(2, '0')); + final Iterable hexChars = character!.codeUnits.map( + (int code) => code.toRadixString(16).padLeft(2, '0'), + ); return ' (0x${hexChars.join(' ')})'; } @override String toString() { return 'KeyData(${type.label}, ' - 'physical: 0x${physical.toRadixString(16)}, ' - 'logical: ${_logicalToString()}, ' - 'character: ${_escapeCharacter()}${_quotedCharCode()}' - '${synthesized ? ', synthesized' : ''}'; + 'physical: 0x${physical.toRadixString(16)}, ' + 'logical: ${_logicalToString()}, ' + 'character: ${_escapeCharacter()}${_quotedCharCode()}' + '${synthesized ? ', synthesized' : ''}'; } /// Returns a complete textual description of the information in this object. String toStringFull() { return '$runtimeType(type: ${type.label}, ' - 'deviceType: ${deviceType.label}, ' - 'timeStamp: $timeStamp, ' - 'physical: 0x${physical.toRadixString(16)}, ' - 'logical: 0x${logical.toRadixString(16)}, ' - 'character: ${_escapeCharacter()}, ' - 'synthesized: $synthesized' - ')'; + 'deviceType: ${deviceType.label}, ' + 'timeStamp: $timeStamp, ' + 'physical: 0x${physical.toRadixString(16)}, ' + 'logical: 0x${logical.toRadixString(16)}, ' + 'character: ${_escapeCharacter()}, ' + 'synthesized: $synthesized' + ')'; } } diff --git a/lib/ui/natives.dart b/lib/ui/natives.dart index 830827b3dc772..65d758977e0b9 100644 --- a/lib/ui/natives.dart +++ b/lib/ui/natives.dart @@ -18,6 +18,7 @@ abstract final class DartPluginRegistrant { _ensureInitialized(); } } + @Native(symbol: 'DartPluginRegistrant_EnsureInitialized') external static void _ensureInitialized(); } @@ -53,9 +54,9 @@ Future _scheduleFrame( // Schedule the frame. PlatformDispatcher.instance.scheduleFrame(); // Always succeed. - return developer.ServiceExtensionResponse.result(json.encode({ - 'type': 'Success', - })); + return developer.ServiceExtensionResponse.result( + json.encode({'type': 'Success'}), + ); } Future _reinitializeShader( @@ -68,19 +69,18 @@ Future _reinitializeShader( } // Always succeed. - return developer.ServiceExtensionResponse.result(json.encode({ - 'type': 'Success', - })); + return developer.ServiceExtensionResponse.result( + json.encode({'type': 'Success'}), + ); } Future _getImpellerEnabled( String method, Map parameters, ) async { - return developer.ServiceExtensionResponse.result(json.encode({ - 'type': 'Success', - 'enabled': _impellerEnabled, - })); + return developer.ServiceExtensionResponse.result( + json.encode({'type': 'Success', 'enabled': _impellerEnabled}), + ); } const bool _kReleaseMode = bool.fromEnvironment('dart.vm.product'); diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index 2cfec8d08af2c..68e21ae847d3e 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -107,24 +107,23 @@ class Color { /// Color(0xFFFF9000)` (`FF` for the alpha, `FF` for the red, `90` for the /// green, and `00` for the blue). const Color(int value) - : this._fromARGBC( - value >> 24, value >> 16, value >> 8, value, ColorSpace.sRGB); + : this._fromARGBC(value >> 24, value >> 16, value >> 8, value, ColorSpace.sRGB); /// Construct a color with normalized color components. /// /// Normalized color components allows arbitrary bit depths for color /// components to be be supported. The values will be normalized relative to /// the [ColorSpace] argument. - const Color.from( - {required double alpha, - required double red, - required double green, - required double blue, - this.colorSpace = ColorSpace.sRGB}) - : a = alpha, - r = red, - g = green, - b = blue; + const Color.from({ + required double alpha, + required double red, + required double green, + required double blue, + this.colorSpace = ColorSpace.sRGB, + }) : a = alpha, + r = red, + g = green, + b = blue; /// Construct an sRGB color from the lower 8 bits of four integers. /// @@ -138,13 +137,10 @@ class Color { /// /// See also [fromRGBO], which takes the alpha value as a floating point /// value. - const Color.fromARGB(int a, int r, int g, int b) - : this._fromARGBC(a, r, g, b, ColorSpace.sRGB); + const Color.fromARGB(int a, int r, int g, int b) : this._fromARGBC(a, r, g, b, ColorSpace.sRGB); - const Color._fromARGBC( - int alpha, int red, int green, int blue, ColorSpace colorSpace) - : this._fromRGBOC( - red, green, blue, (alpha & 0xff) / 255, colorSpace); + const Color._fromARGBC(int alpha, int red, int green, int blue, ColorSpace colorSpace) + : this._fromRGBOC(red, green, blue, (alpha & 0xff) / 255, colorSpace); /// Create an sRGB color from red, green, blue, and opacity, similar to /// `rgba()` in CSS. @@ -159,13 +155,13 @@ class Color { /// /// See also [fromARGB], which takes the opacity as an integer value. const Color.fromRGBO(int r, int g, int b, double opacity) - : this._fromRGBOC(r, g, b, opacity, ColorSpace.sRGB); + : this._fromRGBOC(r, g, b, opacity, ColorSpace.sRGB); const Color._fromRGBOC(int r, int g, int b, double opacity, this.colorSpace) - : a = opacity, - r = (r & 0xff) / 255, - g = (g & 0xff) / 255, - b = (b & 0xff) / 255; + : a = opacity, + r = (r & 0xff) / 255, + g = (g & 0xff) / 255, + b = (b & 0xff) / 255; /// The alpha channel of this color. /// @@ -254,24 +250,25 @@ class Color { /// /// Changes to color components will be applied before applying changes to the /// color space. - Color withValues( - {double? alpha, - double? red, - double? green, - double? blue, - ColorSpace? colorSpace}) { + Color withValues({ + double? alpha, + double? red, + double? green, + double? blue, + ColorSpace? colorSpace, + }) { Color? updatedComponents; if (alpha != null || red != null || green != null || blue != null) { updatedComponents = Color.from( - alpha: alpha ?? a, - red: red ?? r, - green: green ?? g, - blue: blue ?? b, - colorSpace: this.colorSpace); + alpha: alpha ?? a, + red: red ?? r, + green: green ?? g, + blue: blue ?? b, + colorSpace: this.colorSpace, + ); } if (colorSpace != null && colorSpace != this.colorSpace) { - final _ColorTransform transform = - _getColorTransform(this.colorSpace, colorSpace); + final _ColorTransform transform = _getColorTransform(this.colorSpace, colorSpace); return transform.transform(updatedComponents ?? this, colorSpace); } else { return updatedComponents ?? this; @@ -402,12 +399,14 @@ class Color { assert(foreground.colorSpace == background.colorSpace); assert(foreground.colorSpace != ColorSpace.extendedSRGB); final double alpha = foreground.a; - if (alpha == 0) { // Foreground completely transparent. + if (alpha == 0) { + // Foreground completely transparent. return background; } final double invAlpha = 1 - alpha; double backAlpha = background.a; - if (backAlpha == 1) { // Opaque background case + if (backAlpha == 1) { + // Opaque background case return Color.from( alpha: 1, red: alpha * foreground.r + invAlpha * background.r, @@ -415,7 +414,8 @@ class Color { blue: alpha * foreground.b + invAlpha * background.b, colorSpace: foreground.colorSpace, ); - } else { // General case + } else { + // General case backAlpha = backAlpha * invAlpha; final double outAlpha = alpha + backAlpha; assert(outAlpha != 0); @@ -729,8 +729,7 @@ enum BlendMode { /// destination image. /// * [hardLight], which combines [modulate] and [screen] to favor the /// source image. - screen, // The last coeff mode. - + screen, // The last coeff mode. /// Multiply the components of the source and destination images after /// adjusting them to favor the destination. /// @@ -864,8 +863,7 @@ enum BlendMode { /// channel, consider [modulate]. /// /// ![](https://flutter.github.io/assets-for-api-docs/assets/dart-ui/blend_mode_multiply.png) - multiply, // The last separable mode. - + multiply, // The last separable mode. /// Take the hue of the source image, and the saturation and luminosity of the /// destination image. /// @@ -1320,6 +1318,7 @@ final class Paint { bool get isAntiAlias { return _data.getInt32(_kIsAntiAliasOffset, _kFakeHostEndian) == 0; } + set isAntiAlias(bool value) { // We encode true as zero and false as one because the default value, which // we always encode as zero, is true. @@ -1346,16 +1345,11 @@ final class Paint { final double red = _data.getFloat32(_kColorRedOffset, _kFakeHostEndian); final double green = _data.getFloat32(_kColorGreenOffset, _kFakeHostEndian); final double blue = _data.getFloat32(_kColorBlueOffset, _kFakeHostEndian); - final double alpha = - 1.0 - _data.getFloat32(_kColorAlphaOffset, _kFakeHostEndian); + final double alpha = 1.0 - _data.getFloat32(_kColorAlphaOffset, _kFakeHostEndian); final ColorSpace colorSpace = _indexToColorSpace( - _data.getInt32(_kColorSpaceOffset, _kFakeHostEndian)); - return Color.from( - alpha: alpha, - red: red, - green: green, - blue: blue, - colorSpace: colorSpace); + _data.getInt32(_kColorSpaceOffset, _kFakeHostEndian), + ); + return Color.from(alpha: alpha, red: red, green: green, blue: blue, colorSpace: colorSpace); } set color(Color value) { @@ -1363,8 +1357,7 @@ final class Paint { _data.setFloat32(_kColorGreenOffset, value.g, _kFakeHostEndian); _data.setFloat32(_kColorBlueOffset, value.b, _kFakeHostEndian); _data.setFloat32(_kColorAlphaOffset, 1.0 - value.a, _kFakeHostEndian); - _data.setInt32(_kColorSpaceOffset, _colorSpaceToIndex(value.colorSpace), - _kFakeHostEndian); + _data.setInt32(_kColorSpaceOffset, _colorSpaceToIndex(value.colorSpace), _kFakeHostEndian); } // Must be kept in sync with the default in paint.cc. @@ -1392,6 +1385,7 @@ final class Paint { final int encoded = _data.getInt32(_kBlendModeOffset, _kFakeHostEndian); return BlendMode.values[encoded ^ _kBlendModeDefault]; } + set blendMode(BlendMode value) { final int encoded = value.index ^ _kBlendModeDefault; _data.setInt32(_kBlendModeOffset, encoded, _kFakeHostEndian); @@ -1403,6 +1397,7 @@ final class Paint { PaintingStyle get style { return PaintingStyle.values[_data.getInt32(_kStyleOffset, _kFakeHostEndian)]; } + set style(PaintingStyle value) { final int encoded = value.index; _data.setInt32(_kStyleOffset, encoded, _kFakeHostEndian); @@ -1416,6 +1411,7 @@ final class Paint { double get strokeWidth { return _data.getFloat32(_kStrokeWidthOffset, _kFakeHostEndian); } + set strokeWidth(double value) { final double encoded = value; _data.setFloat32(_kStrokeWidthOffset, encoded, _kFakeHostEndian); @@ -1428,6 +1424,7 @@ final class Paint { StrokeCap get strokeCap { return StrokeCap.values[_data.getInt32(_kStrokeCapOffset, _kFakeHostEndian)]; } + set strokeCap(StrokeCap value) { final int encoded = value.index; _data.setInt32(_kStrokeCapOffset, encoded, _kFakeHostEndian); @@ -1461,6 +1458,7 @@ final class Paint { StrokeJoin get strokeJoin { return StrokeJoin.values[_data.getInt32(_kStrokeJoinOffset, _kFakeHostEndian)]; } + set strokeJoin(StrokeJoin value) { final int encoded = value.index; _data.setInt32(_kStrokeJoinOffset, encoded, _kFakeHostEndian); @@ -1498,6 +1496,7 @@ final class Paint { double get strokeMiterLimit { return _data.getFloat32(_kStrokeMiterLimitOffset, _kFakeHostEndian); } + set strokeMiterLimit(double value) { final double encoded = value - _kStrokeMiterLimitDefault; _data.setFloat32(_kStrokeMiterLimitOffset, encoded, _kFakeHostEndian); @@ -1519,6 +1518,7 @@ final class Paint { } return null; } + set maskFilter(MaskFilter? value) { if (value == null) { _data.setInt32(_kMaskFilterOffset, MaskFilter._TypeNone, _kFakeHostEndian); @@ -1542,6 +1542,7 @@ final class Paint { FilterQuality get filterQuality { return FilterQuality.values[_data.getInt32(_kFilterQualityOffset, _kFakeHostEndian)]; } + set filterQuality(FilterQuality value) { final int encoded = value.index; _data.setInt32(_kFilterQualityOffset, encoded, _kFakeHostEndian); @@ -1560,12 +1561,10 @@ final class Paint { Shader? get shader { return _objects?[_kShaderIndex] as Shader?; } + set shader(Shader? value) { assert(() { - assert( - value == null || !value.debugDisposed, - 'Attempted to set a disposed shader to $this', - ); + assert(value == null || !value.debugDisposed, 'Attempted to set a disposed shader to $this'); return true; }()); assert(() { @@ -1589,6 +1588,7 @@ final class Paint { final _ColorFilter? nativeFilter = _objects?[_kColorFilterIndex] as _ColorFilter?; return nativeFilter?.creator; } + set colorFilter(ColorFilter? value) { final _ColorFilter? nativeFilter = value?._toNativeColorFilter(); if (nativeFilter == null) { @@ -1622,6 +1622,7 @@ final class Paint { final _ImageFilter? nativeFilter = _objects?[_kImageFilterIndex] as _ImageFilter?; return nativeFilter?.creator; } + set imageFilter(ImageFilter? value) { if (value == null) { if (_objects != null) { @@ -1644,6 +1645,7 @@ final class Paint { bool get invertColors { return _data.getInt32(_kInvertColorOffset, _kFakeHostEndian) == 1; } + set invertColors(bool value) { _data.setInt32(_kInvertColorOffset, value ? 1 : 0, _kFakeHostEndian); } @@ -1733,11 +1735,13 @@ enum ColorSpace { /// /// See also: https://en.wikipedia.org/wiki/SRGB sRGB, + /// A color space that is backwards compatible with sRGB but can represent /// colors outside of that gamut with values outside of [0..1]. In order to /// see the extended values an [ImageByteFormat] like /// [ImageByteFormat.rawExtendedRgba128] must be used. extendedSRGB, + /// The Display P3 color space. /// /// This is a wide gamut color space that has broad hardware support. It's @@ -1760,7 +1764,7 @@ int _colorSpaceToIndex(ColorSpace colorSpace) { } ColorSpace _indexToColorSpace(int index) { - switch(index) { + switch (index) { case 0: return ColorSpace.sRGB; case 1: @@ -1921,6 +1925,7 @@ class Image { final int height; bool _disposed = false; + /// Release this handle's claim on the underlying Image. This handle is no /// longer usable after this method is called. /// @@ -1953,7 +1958,8 @@ class Image { disposed = _disposed; return true; }()); - return disposed ?? (throw StateError('Image.debugDisposed is only available when asserts are enabled.')); + return disposed ?? + (throw StateError('Image.debugDisposed is only available when asserts are enabled.')); } /// Converts the [Image] object into a byte array. @@ -2094,7 +2100,7 @@ class Image { 'Cannot clone a disposed image.\n' 'The clone() method of a previously-disposed Image was called. Once an ' 'Image object has been disposed, it can no longer be used to create ' - 'handles, as the underlying data may have been released.' + 'handles, as the underlying data may have been released.', ); } assert(!_image._disposed); @@ -2311,17 +2317,23 @@ base class _NativeCodec extends NativeFieldWrapperClass1 implements Codec { @override Future getNextFrame() async { final Completer completer = Completer.sync(); - final String? error = _getNextFrame((_Image? image, int durationMilliseconds, String decodeError) { + final String? error = _getNextFrame(( + _Image? image, + int durationMilliseconds, + String decodeError, + ) { if (image == null) { if (decodeError.isEmpty) { decodeError = 'Codec failed to produce an image, possibly due to invalid image data.'; } completer.completeError(Exception(decodeError)); } else { - completer.complete(FrameInfo._( - image: Image._(image, image.width, image.height), - duration: Duration(milliseconds: durationMilliseconds), - )); + completer.complete( + FrameInfo._( + image: Image._(image, image.width, image.height), + duration: Duration(milliseconds: durationMilliseconds), + ), + ); } }); if (error != null) { @@ -2518,10 +2530,7 @@ TargetImageSize _getDefaultImageSize(int intrinsicWidth, int intrinsicHeight) { /// /// * [instantiateImageCodecWithSize], which used this signature for its /// `getTargetSize` argument. -typedef TargetImageSizeCallback = TargetImageSize Function( - int intrinsicWidth, - int intrinsicHeight, -); +typedef TargetImageSizeCallback = TargetImageSize Function(int intrinsicWidth, int intrinsicHeight); /// A specification of the size to which an image should be decoded. /// @@ -2536,8 +2545,8 @@ class TargetImageSize { /// The `width` and `height` may both be null, but if they're non-null, they /// must be positive. const TargetImageSize({this.width, this.height}) - : assert(width == null || width > 0), - assert(height == null || height > 0); + : assert(width == null || width > 0), + assert(height == null || height > 0); /// The width into which to load the image. /// @@ -2622,30 +2631,26 @@ void decodeImageFromPixels( assert(allowUpscaling || targetHeight <= height); } - ImmutableBuffer.fromUint8List(pixels) - .then((ImmutableBuffer buffer) { - final ImageDescriptor descriptor = ImageDescriptor.raw( - buffer, - width: width, - height: height, - rowBytes: rowBytes, - pixelFormat: format, - ); + ImmutableBuffer.fromUint8List(pixels).then((ImmutableBuffer buffer) { + final ImageDescriptor descriptor = ImageDescriptor.raw( + buffer, + width: width, + height: height, + rowBytes: rowBytes, + pixelFormat: format, + ); - if (!allowUpscaling) { - if (targetWidth != null && targetWidth! > descriptor.width) { - targetWidth = descriptor.width; - } - if (targetHeight != null && targetHeight! > descriptor.height) { - targetHeight = descriptor.height; - } + if (!allowUpscaling) { + if (targetWidth != null && targetWidth! > descriptor.width) { + targetWidth = descriptor.width; } + if (targetHeight != null && targetHeight! > descriptor.height) { + targetHeight = descriptor.height; + } + } - descriptor - .instantiateCodec( - targetWidth: targetWidth, - targetHeight: targetHeight, - ) + descriptor + .instantiateCodec(targetWidth: targetWidth, targetHeight: targetHeight) .then((Codec codec) { final Future frameInfo = codec.getNextFrame(); codec.dispose(); @@ -2702,6 +2707,7 @@ enum PathOperation { /// * [reverseDifference], which is the same but subtracting the first path /// from the second. difference, + /// Create a new path that is the intersection of the two paths, leaving the /// overlapping pieces of the path. /// @@ -2712,12 +2718,14 @@ enum PathOperation { /// See also: /// * [xor], which is the inverse of this operation intersect, + /// Create a new path that is the union (inclusive-or) of the two paths. /// /// For example, if the two paths are overlapping circles of equal diameter /// but differing centers, the result would be a figure-eight like shape /// matching the outer boundaries of both circles. union, + /// Create a new path that is the exclusive-or of the two paths, leaving /// everything but the overlapping pieces of the path. /// @@ -2727,6 +2735,7 @@ enum PathOperation { /// See also: /// * [intersect], which is the inverse of this operation xor, + /// Subtract the first path from the second path. /// /// For example, if the two paths are overlapping circles of equal diameter @@ -2898,7 +2907,8 @@ abstract class Path { /// point in the path is `arcEnd`. The radii are scaled to fit the last path /// point if both are greater than zero but too small to describe an arc. /// - void arcToPoint(Offset arcEnd, { + void arcToPoint( + Offset arcEnd, { Radius radius = Radius.zero, double rotation = 0.0, bool largeArc = false, @@ -3038,7 +3048,9 @@ abstract class Path { if (path._op(path1 as _NativePath, path2 as _NativePath, operation.index)) { return path; } - throw StateError('Path.combine() failed. This may be due an invalid path; in particular, check for NaN values.'); + throw StateError( + 'Path.combine() failed. This may be due an invalid path; in particular, check for NaN values.', + ); } /// Creates a [PathMetrics] object for this path, which can describe various @@ -3077,7 +3089,9 @@ abstract class Path { base class _NativePath extends NativeFieldWrapperClass1 implements Path { /// Create a new empty [Path] object. - _NativePath() { _constructor(); } + _NativePath() { + _constructor(); + } /// Avoids creating a new native backing for the path for methods that will /// create it later, such as [Path.from], [shift] and [transform]. @@ -3105,7 +3119,10 @@ base class _NativePath extends NativeFieldWrapperClass1 implements Path { external void moveTo(double x, double y); @override - @Native, Double, Double)>(symbol: 'Path::relativeMoveTo', isLeaf: true) + @Native, Double, Double)>( + symbol: 'Path::relativeMoveTo', + isLeaf: true, + ) external void relativeMoveTo(double dx, double dy); @override @@ -3113,32 +3130,52 @@ base class _NativePath extends NativeFieldWrapperClass1 implements Path { external void lineTo(double x, double y); @override - @Native, Double, Double)>(symbol: 'Path::relativeLineTo', isLeaf: true) + @Native, Double, Double)>( + symbol: 'Path::relativeLineTo', + isLeaf: true, + ) external void relativeLineTo(double dx, double dy); @override - @Native, Double, Double, Double, Double)>(symbol: 'Path::quadraticBezierTo', isLeaf: true) + @Native, Double, Double, Double, Double)>( + symbol: 'Path::quadraticBezierTo', + isLeaf: true, + ) external void quadraticBezierTo(double x1, double y1, double x2, double y2); @override - @Native, Double, Double, Double, Double)>(symbol: 'Path::relativeQuadraticBezierTo', isLeaf: true) - external void relativeQuadraticBezierTo( - double x1, double y1, double x2, double y2); + @Native, Double, Double, Double, Double)>( + symbol: 'Path::relativeQuadraticBezierTo', + isLeaf: true, + ) + external void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2); @override - @Native, Double, Double, Double, Double, Double, Double)>(symbol: 'Path::cubicTo', isLeaf: true) + @Native, Double, Double, Double, Double, Double, Double)>( + symbol: 'Path::cubicTo', + isLeaf: true, + ) external void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3); @override - @Native, Double, Double, Double, Double, Double, Double)>(symbol: 'Path::relativeCubicTo', isLeaf: true) + @Native, Double, Double, Double, Double, Double, Double)>( + symbol: 'Path::relativeCubicTo', + isLeaf: true, + ) external void relativeCubicTo(double x1, double y1, double x2, double y2, double x3, double y3); @override - @Native, Double, Double, Double, Double, Double)>(symbol: 'Path::conicTo', isLeaf: true) + @Native, Double, Double, Double, Double, Double)>( + symbol: 'Path::conicTo', + isLeaf: true, + ) external void conicTo(double x1, double y1, double x2, double y2, double w); @override - @Native, Double, Double, Double, Double, Double)>(symbol: 'Path::relativeConicTo', isLeaf: true) + @Native, Double, Double, Double, Double, Double)>( + symbol: 'Path::relativeConicTo', + isLeaf: true, + ) external void relativeConicTo(double x1, double y1, double x2, double y2, double w); @override @@ -3147,11 +3184,23 @@ base class _NativePath extends NativeFieldWrapperClass1 implements Path { _arcTo(rect.left, rect.top, rect.right, rect.bottom, startAngle, sweepAngle, forceMoveTo); } - @Native, Double, Double, Double, Double, Double, Double, Bool)>(symbol: 'Path::arcTo', isLeaf: true) - external void _arcTo(double left, double top, double right, double bottom, double startAngle, double sweepAngle, bool forceMoveTo); + @Native, Double, Double, Double, Double, Double, Double, Bool)>( + symbol: 'Path::arcTo', + isLeaf: true, + ) + external void _arcTo( + double left, + double top, + double right, + double bottom, + double startAngle, + double sweepAngle, + bool forceMoveTo, + ); @override - void arcToPoint(Offset arcEnd, { + void arcToPoint( + Offset arcEnd, { Radius radius = Radius.zero, double rotation = 0.0, bool largeArc = false, @@ -3162,8 +3211,19 @@ base class _NativePath extends NativeFieldWrapperClass1 implements Path { _arcToPoint(arcEnd.dx, arcEnd.dy, radius.x, radius.y, rotation, largeArc, clockwise); } - @Native, Double, Double, Double, Double, Double, Bool, Bool)>(symbol: 'Path::arcToPoint', isLeaf: true) - external void _arcToPoint(double arcEndX, double arcEndY, double radiusX, double radiusY, double rotation, bool largeArc, bool clockwise); + @Native, Double, Double, Double, Double, Double, Bool, Bool)>( + symbol: 'Path::arcToPoint', + isLeaf: true, + ) + external void _arcToPoint( + double arcEndX, + double arcEndY, + double radiusX, + double radiusY, + double rotation, + bool largeArc, + bool clockwise, + ); @override void relativeArcToPoint( @@ -3175,18 +3235,30 @@ base class _NativePath extends NativeFieldWrapperClass1 implements Path { }) { assert(_offsetIsValid(arcEndDelta)); assert(_radiusIsValid(radius)); - _relativeArcToPoint(arcEndDelta.dx, arcEndDelta.dy, radius.x, radius.y, rotation, largeArc, clockwise); + _relativeArcToPoint( + arcEndDelta.dx, + arcEndDelta.dy, + radius.x, + radius.y, + rotation, + largeArc, + clockwise, + ); } - @Native, Double, Double, Double, Double, Double, Bool, Bool)>(symbol: 'Path::relativeArcToPoint', isLeaf: true) + @Native, Double, Double, Double, Double, Double, Bool, Bool)>( + symbol: 'Path::relativeArcToPoint', + isLeaf: true, + ) external void _relativeArcToPoint( - double arcEndX, - double arcEndY, - double radiusX, - double radiusY, - double rotation, - bool largeArc, - bool clockwise); + double arcEndX, + double arcEndY, + double radiusX, + double radiusY, + double rotation, + bool largeArc, + bool clockwise, + ); @override void addRect(Rect rect) { @@ -3194,7 +3266,10 @@ base class _NativePath extends NativeFieldWrapperClass1 implements Path { _addRect(rect.left, rect.top, rect.right, rect.bottom); } - @Native, Double, Double, Double, Double)>(symbol: 'Path::addRect', isLeaf: true) + @Native, Double, Double, Double, Double)>( + symbol: 'Path::addRect', + isLeaf: true, + ) external void _addRect(double left, double top, double right, double bottom); @override @@ -3203,7 +3278,10 @@ base class _NativePath extends NativeFieldWrapperClass1 implements Path { _addOval(oval.left, oval.top, oval.right, oval.bottom); } - @Native, Double, Double, Double, Double)>(symbol: 'Path::addOval', isLeaf: true) + @Native, Double, Double, Double, Double)>( + symbol: 'Path::addOval', + isLeaf: true, + ) external void _addOval(double left, double top, double right, double bottom); @override @@ -3212,8 +3290,18 @@ base class _NativePath extends NativeFieldWrapperClass1 implements Path { _addArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle); } - @Native, Double, Double, Double, Double, Double, Double)>(symbol: 'Path::addArc', isLeaf: true) - external void _addArc(double left, double top, double right, double bottom, double startAngle, double sweepAngle); + @Native, Double, Double, Double, Double, Double, Double)>( + symbol: 'Path::addArc', + isLeaf: true, + ) + external void _addArc( + double left, + double top, + double right, + double bottom, + double startAngle, + double sweepAngle, + ); @override void addPolygon(List points, bool close) { @@ -3246,7 +3334,9 @@ base class _NativePath extends NativeFieldWrapperClass1 implements Path { @Native, Pointer, Double, Double)>(symbol: 'Path::addPath') external void _addPath(_NativePath path, double dx, double dy); - @Native, Pointer, Double, Double, Handle)>(symbol: 'Path::addPathWithMatrix') + @Native, Pointer, Double, Double, Handle)>( + symbol: 'Path::addPathWithMatrix', + ) external void _addPathWithMatrix(_NativePath path, double dx, double dy, Float64List matrix); @override @@ -3260,11 +3350,20 @@ base class _NativePath extends NativeFieldWrapperClass1 implements Path { } } - @Native, Pointer, Double, Double)>(symbol: 'Path::extendWithPath') + @Native, Pointer, Double, Double)>( + symbol: 'Path::extendWithPath', + ) external void _extendWithPath(_NativePath path, double dx, double dy); - @Native, Pointer, Double, Double, Handle)>(symbol: 'Path::extendWithPathAndMatrix') - external void _extendWithPathAndMatrix(_NativePath path, double dx, double dy, Float64List matrix); + @Native, Pointer, Double, Double, Handle)>( + symbol: 'Path::extendWithPathAndMatrix', + ) + external void _extendWithPathAndMatrix( + _NativePath path, + double dx, + double dy, + Float64List matrix, + ); @override @Native)>(symbol: 'Path::close', isLeaf: true) @@ -3388,8 +3487,8 @@ class Tangent { /// multiple times, or who need to randomly access elements of the list, should /// use [toList] on this object. class PathMetrics extends collection.IterableBase { - PathMetrics._(Path path, bool forceClosed) : - _iterator = PathMetricIterator._(_PathMeasure(path as _NativePath, forceClosed)); + PathMetrics._(Path path, bool forceClosed) + : _iterator = PathMetricIterator._(_PathMeasure(path as _NativePath, forceClosed)); final Iterator _iterator; @@ -3412,7 +3511,7 @@ class PathMetricIterator implements Iterator { throw RangeError( 'PathMetricIterator is not pointing to a PathMetric. This can happen in two situations:\n' '- The iteration has not started yet. If so, call "moveNext" to start iteration.\n' - '- The iterator ran out of elements. If so, check that "moveNext" returns true prior to calling "current".' + '- The iterator ran out of elements. If so, check that "moveNext" returns true prior to calling "current".', ); } return currentMetric; @@ -3502,7 +3601,8 @@ class PathMetric { } @override - String toString() => 'PathMetric(length: $length, isClosed: $isClosed, contourIndex: $contourIndex)'; + String toString() => + 'PathMetric(length: $length, isClosed: $isClosed, contourIndex: $contourIndex)'; } base class _PathMeasure extends NativeFieldWrapperClass1 { @@ -3514,7 +3614,10 @@ base class _PathMeasure extends NativeFieldWrapperClass1 { external void _constructor(_NativePath path, bool forceClosed); double length(int contourIndex) { - assert(contourIndex <= currentContourIndex, 'Iterator must be advanced before index $contourIndex can be used.'); + assert( + contourIndex <= currentContourIndex, + 'Iterator must be advanced before index $contourIndex can be used.', + ); return _length(contourIndex); } @@ -3522,36 +3625,48 @@ base class _PathMeasure extends NativeFieldWrapperClass1 { external double _length(int contourIndex); Tangent? getTangentForOffset(int contourIndex, double distance) { - assert(contourIndex <= currentContourIndex, - 'Iterator must be advanced before index $contourIndex can be used.'); + assert( + contourIndex <= currentContourIndex, + 'Iterator must be advanced before index $contourIndex can be used.', + ); final Float32List posTan = _getPosTan(contourIndex, distance); // first entry == 0 indicates that Skia returned false if (posTan[0] == 0.0) { return null; } else { - return Tangent( - Offset(posTan[1], posTan[2]), - Offset(posTan[3], posTan[4]) - ); + return Tangent(Offset(posTan[1], posTan[2]), Offset(posTan[3], posTan[4])); } } @Native, Int32, Double)>(symbol: 'PathMeasure::getPosTan') external Float32List _getPosTan(int contourIndex, double distance); - Path extractPath(int contourIndex, double start, double end, - {bool startWithMoveTo = true}) { - assert(contourIndex <= currentContourIndex, 'Iterator must be advanced before index $contourIndex can be used.'); + Path extractPath(int contourIndex, double start, double end, {bool startWithMoveTo = true}) { + assert( + contourIndex <= currentContourIndex, + 'Iterator must be advanced before index $contourIndex can be used.', + ); final _NativePath path = _NativePath._(); _extractPath(path, contourIndex, start, end, startWithMoveTo); return path; } - @Native, Handle, Int32, Double, Double, Bool)>(symbol: 'PathMeasure::getSegment') - external void _extractPath(Path outPath, int contourIndex, double start, double end, bool startWithMoveTo); + @Native, Handle, Int32, Double, Double, Bool)>( + symbol: 'PathMeasure::getSegment', + ) + external void _extractPath( + Path outPath, + int contourIndex, + double start, + double end, + bool startWithMoveTo, + ); bool isClosed(int contourIndex) { - assert(contourIndex <= currentContourIndex, 'Iterator must be advanced before index $contourIndex can be used.'); + assert( + contourIndex <= currentContourIndex, + 'Iterator must be advanced before index $contourIndex can be used.', + ); return _isClosed(contourIndex); } @@ -3627,10 +3742,7 @@ class MaskFilter { /// See also: /// /// * [Canvas.drawShadow], which is a more efficient way to draw shadows. - const MaskFilter.blur( - this._style, - this._sigma, - ); + const MaskFilter.blur(this._style, this._sigma); final BlurStyle _style; final double _sigma; @@ -3642,9 +3754,7 @@ class MaskFilter { @override bool operator ==(Object other) { - return other is MaskFilter - && other._style == _style - && other._sigma == _sigma; + return other is MaskFilter && other._style == _style && other._sigma == _sigma; } @override @@ -3674,7 +3784,8 @@ class _ClampTransform implements _ColorTransform { red: clampDouble(color.r, 0, 1), green: clampDouble(color.g, 0, 1), blue: clampDouble(color.b, 0, 1), - colorSpace: resultColorSpace); + colorSpace: resultColorSpace, + ); } } @@ -3687,20 +3798,12 @@ class _MatrixColorTransform implements _ColorTransform { @override Color transform(Color color, ColorSpace resultColorSpace) { return Color.from( - alpha: color.a, - red: values[0] * color.r + - values[1] * color.g + - values[2] * color.b + - values[3], - green: values[4] * color.r + - values[5] * color.g + - values[6] * color.b + - values[7], - blue: values[8] * color.r + - values[9] * color.g + - values[10] * color.b + - values[11], - colorSpace: resultColorSpace); + alpha: color.a, + red: values[0] * color.r + values[1] * color.g + values[2] * color.b + values[3], + green: values[4] * color.r + values[5] * color.g + values[6] * color.b + values[7], + blue: values[8] * color.r + values[9] * color.g + values[10] * color.b + values[11], + colorSpace: resultColorSpace, + ); } } @@ -3729,14 +3832,14 @@ _ColorTransform _getColorTransform(ColorSpace source, ColorSpace destination) { 0.145738111193222, // 0.096480880462996, 0.916386732581291, -0.086093928394828, 0.089490172325882, // - -0.127099563510240, -0.068983484963878, 0.735426667591299, 0.233655661600230 + -0.127099563510240, -0.068983484963878, 0.735426667591299, 0.233655661600230, ]); const _ColorTransform p3ToSrgb = _MatrixColorTransform([ 1.306671048092539, -0.298061942172353, 0.213228303487995, -0.213580156254466, // -0.117390025596251, 1.127722006101976, 0.109727644608938, -0.109450321455370, // - 0.214813187718391, 0.054268702864647, 1.406898424029350, -0.364892765879631 + 0.214813187718391, 0.054268702864647, 1.406898424029350, -0.364892765879631, ]); switch (source) { case ColorSpace.sRGB: @@ -3786,10 +3889,10 @@ class ColorFilter implements ImageFilter { /// to the [Paint.blendMode], using the output of this filter as the source /// and the background as the destination. const ColorFilter.mode(Color color, BlendMode blendMode) - : _color = color, - _blendMode = blendMode, - _matrix = null, - _type = _kTypeMode; + : _color = color, + _blendMode = blendMode, + _matrix = null, + _type = _kTypeMode; /// Construct a color filter from a 4x5 row-major matrix. The matrix is /// interpreted as a 5x5 matrix, where the fifth row is the identity @@ -3851,26 +3954,26 @@ class ColorFilter implements ImageFilter { /// ]); /// ``` const ColorFilter.matrix(List matrix) - : _color = null, - _blendMode = null, - _matrix = matrix, - _type = _kTypeMatrix; + : _color = null, + _blendMode = null, + _matrix = matrix, + _type = _kTypeMatrix; /// Construct a color filter that applies the sRGB gamma curve to the RGB /// channels. const ColorFilter.linearToSrgbGamma() - : _color = null, - _blendMode = null, - _matrix = null, - _type = _kTypeLinearToSrgbGamma; + : _color = null, + _blendMode = null, + _matrix = null, + _type = _kTypeLinearToSrgbGamma; /// Creates a color filter that applies the inverse of the sRGB gamma curve /// to the RGB channels. const ColorFilter.srgbToLinearGamma() - : _color = null, - _blendMode = null, - _matrix = null, - _type = _kTypeSrgbToLinearGamma; + : _color = null, + _blendMode = null, + _matrix = null, + _type = _kTypeSrgbToLinearGamma; final Color? _color; final BlendMode? _blendMode; @@ -3915,11 +4018,11 @@ class ColorFilter implements ImageFilter { if (other.runtimeType != runtimeType) { return false; } - return other is ColorFilter - && other._type == _type - && _listEquals(other._matrix, _matrix) - && other._color == _color - && other._blendMode == _blendMode; + return other is ColorFilter && + other._type == _type && + _listEquals(other._matrix, _matrix) && + other._color == _color && + other._blendMode == _blendMode; } @override @@ -3968,14 +4071,12 @@ class ColorFilter implements ImageFilter { /// efficiently comparable, so that widgets can check for ColorFilter equality to /// avoid repainting. base class _ColorFilter extends NativeFieldWrapperClass1 { - _ColorFilter.mode(this.creator) - : assert(creator._type == ColorFilter._kTypeMode) { + _ColorFilter.mode(this.creator) : assert(creator._type == ColorFilter._kTypeMode) { _constructor(); _initMode(creator._color!.value, creator._blendMode!.index); } - _ColorFilter.matrix(this.creator) - : assert(creator._type == ColorFilter._kTypeMatrix) { + _ColorFilter.matrix(this.creator) : assert(creator._type == ColorFilter._kTypeMatrix) { _constructor(); _initMatrix(Float32List.fromList(creator._matrix!)); } @@ -4026,19 +4127,19 @@ abstract class ImageFilter { ImageFilter._(); // ignore: unused_element /// Creates an image filter that applies a Gaussian blur. - factory ImageFilter.blur({ double sigmaX = 0.0, double sigmaY = 0.0, TileMode? tileMode }) { + factory ImageFilter.blur({double sigmaX = 0.0, double sigmaY = 0.0, TileMode? tileMode}) { return _GaussianBlurImageFilter(sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode); } /// Creates an image filter that dilates each input pixel's channel values /// to the max value within the given radii along the x and y axes. - factory ImageFilter.dilate({ double radiusX = 0.0, double radiusY = 0.0 }) { + factory ImageFilter.dilate({double radiusX = 0.0, double radiusY = 0.0}) { return _DilateImageFilter(radiusX: radiusX, radiusY: radiusY); } /// Create a filter that erodes each input pixel's channel values /// to the minimum channel value within the given radii along the x and y axes. - factory ImageFilter.erode({ double radiusX = 0.0, double radiusY = 0.0 }) { + factory ImageFilter.erode({double radiusX = 0.0, double radiusY = 0.0}) { return _ErodeImageFilter(radiusX: radiusX, radiusY: radiusY); } @@ -4046,8 +4147,10 @@ abstract class ImageFilter { /// /// For example, applying a positive scale matrix (see [Matrix4.diagonal3]) /// when used with [BackdropFilter] would magnify the background image. - factory ImageFilter.matrix(Float64List matrix4, - { FilterQuality filterQuality = FilterQuality.medium }) { + factory ImageFilter.matrix( + Float64List matrix4, { + FilterQuality filterQuality = FilterQuality.medium, + }) { if (matrix4.length != 16) { throw ArgumentError('"matrix4" must have 16 entries.'); } @@ -4059,7 +4162,7 @@ abstract class ImageFilter { /// Creates a single [ImageFilter] that when applied, has the same effect as /// subsequently applying `inner` and `outer`, i.e., /// result = outer(inner(source)). - factory ImageFilter.compose({ required ImageFilter outer, required ImageFilter inner }) { + factory ImageFilter.compose({required ImageFilter outer, required ImageFilter inner}) { return _ComposeImageFilter(innerFilter: inner, outerFilter: outer); } @@ -4104,7 +4207,8 @@ abstract class ImageFilter { if (invalidFloats || invalidSampler) { final StringBuffer buffer = StringBuffer( 'ImageFilter.shader requires that the first uniform is a vec2 and at ' - 'least one sampler uniform is present.\n'); + 'least one sampler uniform is present.\n', + ); if (invalidFloats) { buffer.write('The shader has fewer than two float uniforms.\n'); } @@ -4128,7 +4232,7 @@ abstract class ImageFilter { } class _MatrixImageFilter implements ImageFilter { - _MatrixImageFilter({ required this.data, required this.filterQuality }); + _MatrixImageFilter({required this.data, required this.filterQuality}); final Float64List data; final FilterQuality filterQuality; @@ -4149,9 +4253,9 @@ class _MatrixImageFilter implements ImageFilter { if (other.runtimeType != runtimeType) { return false; } - return other is _MatrixImageFilter - && other.filterQuality == filterQuality - && _listEquals(other.data, data); + return other is _MatrixImageFilter && + other.filterQuality == filterQuality && + _listEquals(other.data, data); } @override @@ -4159,7 +4263,7 @@ class _MatrixImageFilter implements ImageFilter { } class _GaussianBlurImageFilter implements ImageFilter { - _GaussianBlurImageFilter({ required this.sigmaX, required this.sigmaY, required this.tileMode }); + _GaussianBlurImageFilter({required this.sigmaX, required this.sigmaY, required this.tileMode}); final double sigmaX; final double sigmaY; @@ -4172,11 +4276,16 @@ class _GaussianBlurImageFilter implements ImageFilter { String get _modeString { switch (tileMode) { - case TileMode.clamp: return 'clamp'; - case TileMode.mirror: return 'mirror'; - case TileMode.repeated: return 'repeated'; - case TileMode.decal: return 'decal'; - case null: return 'unspecified'; + case TileMode.clamp: + return 'clamp'; + case TileMode.mirror: + return 'mirror'; + case TileMode.repeated: + return 'repeated'; + case TileMode.decal: + return 'decal'; + case null: + return 'unspecified'; } } @@ -4191,10 +4300,10 @@ class _GaussianBlurImageFilter implements ImageFilter { if (other.runtimeType != runtimeType) { return false; } - return other is _GaussianBlurImageFilter - && other.sigmaX == sigmaX - && other.sigmaY == sigmaY - && other.tileMode == tileMode; + return other is _GaussianBlurImageFilter && + other.sigmaX == sigmaX && + other.sigmaY == sigmaY && + other.tileMode == tileMode; } @override @@ -4202,7 +4311,7 @@ class _GaussianBlurImageFilter implements ImageFilter { } class _DilateImageFilter implements ImageFilter { - _DilateImageFilter({ required this.radiusX, required this.radiusY }); + _DilateImageFilter({required this.radiusX, required this.radiusY}); final double radiusX; final double radiusY; @@ -4222,9 +4331,7 @@ class _DilateImageFilter implements ImageFilter { if (other.runtimeType != runtimeType) { return false; } - return other is _DilateImageFilter - && other.radiusX == radiusX - && other.radiusY == radiusY; + return other is _DilateImageFilter && other.radiusX == radiusX && other.radiusY == radiusY; } @override @@ -4232,7 +4339,7 @@ class _DilateImageFilter implements ImageFilter { } class _ErodeImageFilter implements ImageFilter { - _ErodeImageFilter({ required this.radiusX, required this.radiusY }); + _ErodeImageFilter({required this.radiusX, required this.radiusY}); final double radiusX; final double radiusY; @@ -4252,9 +4359,7 @@ class _ErodeImageFilter implements ImageFilter { if (other.runtimeType != runtimeType) { return false; } - return other is _ErodeImageFilter - && other.radiusX == radiusX - && other.radiusY == radiusY; + return other is _ErodeImageFilter && other.radiusX == radiusX && other.radiusY == radiusY; } @override @@ -4262,7 +4367,7 @@ class _ErodeImageFilter implements ImageFilter { } class _ComposeImageFilter implements ImageFilter { - _ComposeImageFilter({ required this.innerFilter, required this.outerFilter }); + _ComposeImageFilter({required this.innerFilter, required this.outerFilter}); final ImageFilter innerFilter; final ImageFilter outerFilter; @@ -4273,7 +4378,8 @@ class _ComposeImageFilter implements ImageFilter { _ImageFilter _toNativeImageFilter() => nativeFilter; @override - String get _shortDescription => '${innerFilter._shortDescription} -> ${outerFilter._shortDescription}'; + String get _shortDescription => + '${innerFilter._shortDescription} -> ${outerFilter._shortDescription}'; @override String toString() => 'ImageFilter.compose(source -> $_shortDescription -> result)'; @@ -4283,9 +4389,9 @@ class _ComposeImageFilter implements ImageFilter { if (other.runtimeType != runtimeType) { return false; } - return other is _ComposeImageFilter - && other.innerFilter == innerFilter - && other.outerFilter == outerFilter; + return other is _ComposeImageFilter && + other.innerFilter == innerFilter && + other.outerFilter == outerFilter; } @override @@ -4313,8 +4419,7 @@ class _FragmentShaderImageFilter implements ImageFilter { if (other.runtimeType != runtimeType) { return false; } - return other is _FragmentShaderImageFilter - && other.shader == shader; + return other is _FragmentShaderImageFilter && other.shader == shader; } @override @@ -4328,24 +4433,21 @@ class _FragmentShaderImageFilter implements ImageFilter { /// widgets can check for ImageFilter equality to avoid repainting. base class _ImageFilter extends NativeFieldWrapperClass1 { /// Creates an image filter that applies a Gaussian blur. - _ImageFilter.blur(_GaussianBlurImageFilter filter) - : creator = filter { + _ImageFilter.blur(_GaussianBlurImageFilter filter) : creator = filter { _constructor(); _initBlur(filter.sigmaX, filter.sigmaY, filter.tileMode?.index ?? -1); } /// Creates an image filter that dilates each input pixel's channel values /// to the max value within the given radii along the x and y axes. - _ImageFilter.dilate(_DilateImageFilter filter) - : creator = filter { + _ImageFilter.dilate(_DilateImageFilter filter) : creator = filter { _constructor(); _initDilate(filter.radiusX, filter.radiusY); } /// Create a filter that erodes each input pixel's channel values /// to the minimum channel value within the given radii along the x and y axes. - _ImageFilter.erode(_ErodeImageFilter filter) - : creator = filter { + _ImageFilter.erode(_ErodeImageFilter filter) : creator = filter { _constructor(); _initErode(filter.radiusX, filter.radiusY); } @@ -4354,8 +4456,7 @@ base class _ImageFilter extends NativeFieldWrapperClass1 { /// /// For example, applying a positive scale matrix (see [Matrix4.diagonal3]) /// when used with [BackdropFilter] would magnify the background image. - _ImageFilter.matrix(_MatrixImageFilter filter) - : creator = filter { + _ImageFilter.matrix(_MatrixImageFilter filter) : creator = filter { if (filter.data.length != 16) { throw ArgumentError('"matrix4" must have 16 entries.'); } @@ -4364,38 +4465,44 @@ base class _ImageFilter extends NativeFieldWrapperClass1 { } /// Converts a color filter to an image filter. - _ImageFilter.fromColorFilter(ColorFilter filter) - : creator = filter { + _ImageFilter.fromColorFilter(ColorFilter filter) : creator = filter { _constructor(); final _ColorFilter? nativeFilter = filter._toNativeColorFilter(); _initColorFilter(nativeFilter); } /// Composes `_innerFilter` with `_outerFilter`. - _ImageFilter.composed(_ComposeImageFilter filter) - : creator = filter { + _ImageFilter.composed(_ComposeImageFilter filter) : creator = filter { _constructor(); final _ImageFilter nativeFilterInner = filter.innerFilter._toNativeImageFilter(); final _ImageFilter nativeFilterOuter = filter.outerFilter._toNativeImageFilter(); _initComposed(nativeFilterOuter, nativeFilterInner); } - _ImageFilter.shader(_FragmentShaderImageFilter filter) - : creator = filter { - _constructor(); - _initShader(filter.shader); - } + _ImageFilter.shader(_FragmentShaderImageFilter filter) : creator = filter { + _constructor(); + _initShader(filter.shader); + } @Native(symbol: 'ImageFilter::Create') external void _constructor(); - @Native, Double, Double, Int32)>(symbol: 'ImageFilter::initBlur', isLeaf: true) + @Native, Double, Double, Int32)>( + symbol: 'ImageFilter::initBlur', + isLeaf: true, + ) external void _initBlur(double sigmaX, double sigmaY, int tileMode); - @Native, Double, Double)>(symbol: 'ImageFilter::initDilate', isLeaf: true) + @Native, Double, Double)>( + symbol: 'ImageFilter::initDilate', + isLeaf: true, + ) external void _initDilate(double radiusX, double radiusY); - @Native, Double, Double)>(symbol: 'ImageFilter::initErode', isLeaf: true) + @Native, Double, Double)>( + symbol: 'ImageFilter::initErode', + isLeaf: true, + ) external void _initErode(double radiusX, double radiusY); @Native, Handle, Int32)>(symbol: 'ImageFilter::initMatrix') @@ -4404,7 +4511,9 @@ base class _ImageFilter extends NativeFieldWrapperClass1 { @Native, Pointer)>(symbol: 'ImageFilter::initColorFilter') external void _initColorFilter(_ColorFilter? colorFilter); - @Native, Pointer, Pointer)>(symbol: 'ImageFilter::initComposeFilter') + @Native, Pointer, Pointer)>( + symbol: 'ImageFilter::initComposeFilter', + ) external void _initComposed(_ImageFilter outerFilter, _ImageFilter innerFilter); @Native, Pointer)>(symbol: 'ImageFilter::initShader') @@ -4549,17 +4658,15 @@ Float32List _encodeWideColorList(List colors) { final int colorCount = colors.length; final Float32List result = Float32List(colorCount * 4); for (int i = 0; i < colorCount; i++) { - final Color colorXr = - colors[i].withValues(colorSpace: ColorSpace.extendedSRGB); - result[i*4+0] = colorXr.a; - result[i*4+1] = colorXr.r; - result[i*4+2] = colorXr.g; - result[i*4+3] = colorXr.b; + final Color colorXr = colors[i].withValues(colorSpace: ColorSpace.extendedSRGB); + result[i * 4 + 0] = colorXr.a; + result[i * 4 + 1] = colorXr.r; + result[i * 4 + 2] = colorXr.g; + result[i * 4 + 3] = colorXr.b; } return result; } - Int32List _encodeColorList(List colors) { final int colorCount = colors.length; final Int32List result = Int32List(colorCount); @@ -4644,7 +4751,8 @@ base class Gradient extends Shader { _validateColorStops(colors, colorStops); final Float32List endPointsBuffer = _encodeTwoPoints(from, to); final Float32List colorsBuffer = _encodeWideColorList(colors); - final Float32List? colorStopsBuffer = colorStops == null ? null : Float32List.fromList(colorStops); + final Float32List? colorStopsBuffer = + colorStops == null ? null : Float32List.fromList(colorStops); _constructor(); _initLinear(endPointsBuffer, colorsBuffer, colorStopsBuffer, tileMode.index, matrix4); } @@ -4691,23 +4799,45 @@ base class Gradient extends Shader { TileMode tileMode = TileMode.clamp, Float64List? matrix4, Offset? focal, - double focalRadius = 0.0 + double focalRadius = 0.0, ]) : assert(_offsetIsValid(center)), assert(matrix4 == null || _matrix4IsValid(matrix4)), super._() { _validateColorStops(colors, colorStops); - final Float32List? colorStopsBuffer = colorStops == null ? null : Float32List.fromList(colorStops); + final Float32List? colorStopsBuffer = + colorStops == null ? null : Float32List.fromList(colorStops); final Float32List colorsBuffer = _encodeWideColorList(colors); // If focal is null or focal radius is null, this should be treated as a regular radial gradient // If focal == center and the focal radius is 0.0, it's still a regular radial gradient if (focal == null || (focal == center && focalRadius == 0.0)) { _constructor(); - _initRadial(center.dx, center.dy, radius, colorsBuffer, colorStopsBuffer, tileMode.index, matrix4); + _initRadial( + center.dx, + center.dy, + radius, + colorsBuffer, + colorStopsBuffer, + tileMode.index, + matrix4, + ); } else { - assert(center != Offset.zero || focal != Offset.zero); // will result in exception(s) in Skia side + assert( + center != Offset.zero || focal != Offset.zero, + ); // will result in exception(s) in Skia side _constructor(); - _initConical(focal.dx, focal.dy, focalRadius, center.dx, center.dy, radius, colorsBuffer, colorStopsBuffer, tileMode.index, matrix4); + _initConical( + focal.dx, + focal.dy, + focalRadius, + center.dx, + center.dy, + radius, + colorsBuffer, + colorStopsBuffer, + tileMode.index, + matrix4, + ); } } @@ -4756,53 +4886,91 @@ base class Gradient extends Shader { super._() { _validateColorStops(colors, colorStops); final Float32List colorsBuffer = _encodeWideColorList(colors); - final Float32List? colorStopsBuffer = colorStops == null ? null : Float32List.fromList(colorStops); + final Float32List? colorStopsBuffer = + colorStops == null ? null : Float32List.fromList(colorStops); _constructor(); - _initSweep(center.dx, center.dy, colorsBuffer, colorStopsBuffer, tileMode.index, startAngle, endAngle, matrix4); + _initSweep( + center.dx, + center.dy, + colorsBuffer, + colorStopsBuffer, + tileMode.index, + startAngle, + endAngle, + matrix4, + ); } @Native(symbol: 'Gradient::Create') external void _constructor(); - @Native, Handle, Handle, Handle, Int32, Handle)>(symbol: 'Gradient::initLinear') - external void _initLinear(Float32List endPoints, Float32List colors, Float32List? colorStops, int tileMode, Float64List? matrix4); + @Native, Handle, Handle, Handle, Int32, Handle)>( + symbol: 'Gradient::initLinear', + ) + external void _initLinear( + Float32List endPoints, + Float32List colors, + Float32List? colorStops, + int tileMode, + Float64List? matrix4, + ); - @Native, Double, Double, Double, Handle, Handle, Int32, Handle)>(symbol: 'Gradient::initRadial') + @Native, Double, Double, Double, Handle, Handle, Int32, Handle)>( + symbol: 'Gradient::initRadial', + ) external void _initRadial( - double centerX, - double centerY, - double radius, - Float32List colors, - Float32List? colorStops, - int tileMode, - Float64List? matrix4); - - @Native, Double, Double, Double, Double, Double, Double, Handle, Handle, Int32, Handle)>(symbol: 'Gradient::initTwoPointConical') + double centerX, + double centerY, + double radius, + Float32List colors, + Float32List? colorStops, + int tileMode, + Float64List? matrix4, + ); + + @Native< + Void Function( + Pointer, + Double, + Double, + Double, + Double, + Double, + Double, + Handle, + Handle, + Int32, + Handle, + ) + >(symbol: 'Gradient::initTwoPointConical') external void _initConical( - double startX, - double startY, - double startRadius, - double endX, - double endY, - double endRadius, - Float32List colors, - Float32List? colorStops, - int tileMode, - Float64List? matrix4); - - @Native, Double, Double, Handle, Handle, Int32, Double, Double, Handle)>(symbol: 'Gradient::initSweep') + double startX, + double startY, + double startRadius, + double endX, + double endY, + double endRadius, + Float32List colors, + Float32List? colorStops, + int tileMode, + Float64List? matrix4, + ); + + @Native< + Void Function(Pointer, Double, Double, Handle, Handle, Int32, Double, Double, Handle) + >(symbol: 'Gradient::initSweep') external void _initSweep( - double centerX, - double centerY, - Float32List colors, - Float32List? colorStops, - int tileMode, - double startAngle, - double endAngle, - Float64List? matrix); - - static void _validateColorStops( - List colors, List? colorStops) { + double centerX, + double centerY, + Float32List colors, + Float32List? colorStops, + int tileMode, + double startAngle, + double endAngle, + Float64List? matrix, + ); + + static void _validateColorStops(List colors, List? colorStops) { if (colorStops == null) { if (colors.length != 2) { throw ArgumentError('"colors" must have length 2 if "colorStops" is omitted.'); @@ -4837,16 +5005,25 @@ base class ImageShader extends Shader { /// it will be deduced from the environment where it is used, such as from /// [Paint.filterQuality]. @pragma('vm:entry-point') - ImageShader(Image image, TileMode tmx, TileMode tmy, Float64List matrix4, { + ImageShader( + Image image, + TileMode tmx, + TileMode tmy, + Float64List matrix4, { FilterQuality? filterQuality, - }) : - assert(!image.debugDisposed), - super._() { + }) : assert(!image.debugDisposed), + super._() { if (matrix4.length != 16) { throw ArgumentError('"matrix4" must have 16 entries.'); } _constructor(); - final String? error = _initWithImage(image._image, tmx.index, tmy.index, filterQuality?.index ?? -1, matrix4); + final String? error = _initWithImage( + image._image, + tmx.index, + tmy.index, + filterQuality?.index ?? -1, + matrix4, + ); if (error != null) { throw Exception(error); } @@ -4861,8 +5038,16 @@ base class ImageShader extends Shader { @Native(symbol: 'ImageShader::Create') external void _constructor(); - @Native, Pointer, Int32, Int32, Int32, Handle)>(symbol: 'ImageShader::initWithImage') - external String? _initWithImage(_Image image, int tmx, int tmy, int filterQualityIndex, Float64List matrix4); + @Native, Pointer, Int32, Int32, Int32, Handle)>( + symbol: 'ImageShader::initWithImage', + ) + external String? _initWithImage( + _Image image, + int tmx, + int tmy, + int filterQualityIndex, + Float64List matrix4, + ); /// This can't be a leaf call because the native function calls Dart API /// (Dart_SetNativeInstanceField). @@ -4920,8 +5105,7 @@ base class FragmentProgram extends NativeFieldWrapperClass1 { // program (PSO variants) until shutdown, so maintaining a strong reference // here ensures we do not perform extra work if the dart object is continually // re-initialized. - static final Map _shaderRegistry = - {}; + static final Map _shaderRegistry = {}; static void _reinitializeShader(String assetKey) { // If a shader for the asset isn't already registered, then there's no @@ -4968,12 +5152,10 @@ base class FragmentProgram extends NativeFieldWrapperClass1 { /// are required to exist simultaneously, they must be obtained from two /// different calls to [FragmentProgram.fragmentShader]. base class FragmentShader extends Shader { - FragmentShader._(FragmentProgram program, { String? debugName }) : _debugName = debugName, super._() { - _floats = _constructor( - program, - program._uniformFloatCount, - program._samplerCount, - ); + FragmentShader._(FragmentProgram program, {String? debugName}) + : _debugName = debugName, + super._() { + _floats = _constructor(program, program._uniformFloatCount, program._samplerCount); } final String? _debugName; @@ -5054,9 +5236,15 @@ base class FragmentShader extends Shader { } @Native(symbol: 'ReusableFragmentShader::Create') - external Float32List _constructor(FragmentProgram program, int floatUniforms, int samplerUniforms); + external Float32List _constructor( + FragmentProgram program, + int floatUniforms, + int samplerUniforms, + ); - @Native, Handle, Handle)>(symbol: 'ReusableFragmentShader::SetImageSampler') + @Native, Handle, Handle)>( + symbol: 'ReusableFragmentShader::SetImageSampler', + ) external void _setImageSampler(int index, _Image sampler); @Native)>(symbol: 'ReusableFragmentShader::ValidateSamplers') @@ -5196,17 +5384,19 @@ base class Vertices extends NativeFieldWrapperClass1 { return true; }()); final Float32List encodedPositions = _encodePointList(positions); - final Float32List? encodedTextureCoordinates = (textureCoordinates != null) - ? _encodePointList(textureCoordinates) - : null; - final Int32List? encodedColors = colors != null - ? _encodeColorList(colors) - : null; - final Uint16List? encodedIndices = indices != null - ? Uint16List.fromList(indices) - : null; - - if (!_init(this, mode.index, encodedPositions, encodedTextureCoordinates, encodedColors, encodedIndices)) { + final Float32List? encodedTextureCoordinates = + (textureCoordinates != null) ? _encodePointList(textureCoordinates) : null; + final Int32List? encodedColors = colors != null ? _encodeColorList(colors) : null; + final Uint16List? encodedIndices = indices != null ? Uint16List.fromList(indices) : null; + + if (!_init( + this, + mode.index, + encodedPositions, + encodedTextureCoordinates, + encodedColors, + encodedIndices, + )) { throw ArgumentError('Invalid configuration for vertices.'); } } @@ -5263,7 +5453,9 @@ base class Vertices extends NativeFieldWrapperClass1 { Uint16List? indices, }) { if (positions.length % 2 != 0) { - throw ArgumentError('"positions" must have an even number of entries (each coordinate is an x,y pair).'); + throw ArgumentError( + '"positions" must have an even number of entries (each coordinate is an x,y pair).', + ); } if (colors != null && colors.length * 2 != positions.length) { throw ArgumentError('"positions" and "colors" lengths must match.'); @@ -5291,12 +5483,14 @@ base class Vertices extends NativeFieldWrapperClass1 { } @Native(symbol: 'Vertices::init') - external static bool _init(Vertices outVertices, - int mode, - Float32List positions, - Float32List? textureCoordinates, - Int32List? colors, - Uint16List? indices); + external static bool _init( + Vertices outVertices, + int mode, + Float32List positions, + Float32List? textureCoordinates, + Int32List? colors, + Uint16List? indices, + ); /// Release the resources used by this object. The object is no longer usable /// after this method is called. @@ -5315,6 +5509,7 @@ base class Vertices extends NativeFieldWrapperClass1 { external void _dispose(); bool _disposed = false; + /// Whether this reference to the underlying vertex data is [dispose]d. /// /// This only returns a valid value if asserts are enabled, and must not be @@ -5325,7 +5520,8 @@ base class Vertices extends NativeFieldWrapperClass1 { disposed = _disposed; return true; }()); - return disposed ?? (throw StateError('Vertices.debugDisposed is only available when asserts are enabled.')); + return disposed ?? + (throw StateError('Vertices.debugDisposed is only available when asserts are enabled.')); } } @@ -5414,7 +5610,7 @@ abstract class Canvas { /// /// To end the recording, call [PictureRecorder.endRecording] on the /// given recorder. - factory Canvas(PictureRecorder recorder, [ Rect? cullRect ]) = _NativeCanvas; + factory Canvas(PictureRecorder recorder, [Rect? cullRect]) = _NativeCanvas; /// Saves a copy of the current transform and clip on the save stack. /// @@ -5613,7 +5809,7 @@ abstract class Canvas { /// /// Use [ClipOp.difference] to subtract the provided rectangle from the /// current clip. - void clipRect(Rect rect, { ClipOp clipOp = ClipOp.intersect, bool doAntiAlias = true }); + void clipRect(Rect rect, {ClipOp clipOp = ClipOp.intersect, bool doAntiAlias = true}); /// Reduces the clip region to the intersection of the current clip and the /// given rounded rectangle. @@ -6034,13 +6230,15 @@ abstract class Canvas { /// /// * [drawRawAtlas], which takes its arguments as typed data lists rather /// than objects. - void drawAtlas(Image atlas, - List transforms, - List rects, - List? colors, - BlendMode? blendMode, - Rect? cullRect, - Paint paint); + void drawAtlas( + Image atlas, + List transforms, + List rects, + List? colors, + BlendMode? blendMode, + Rect? cullRect, + Paint paint, + ); /// Draws many parts of an image - the [atlas] - onto the canvas. /// @@ -6189,13 +6387,15 @@ abstract class Canvas { /// /// * [drawAtlas], which takes its arguments as objects rather than typed /// data lists. - void drawRawAtlas(Image atlas, - Float32List rstTransforms, - Float32List rects, - Int32List? colors, - BlendMode? blendMode, - Rect? cullRect, - Paint paint); + void drawRawAtlas( + Image atlas, + Float32List rstTransforms, + Float32List rects, + Int32List? colors, + BlendMode? blendMode, + Rect? cullRect, + Paint paint, + ); /// Draws a shadow for a [Path] representing the given material elevation. /// @@ -6207,7 +6407,7 @@ abstract class Canvas { } base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas { - _NativeCanvas(PictureRecorder recorder, [ Rect? cullRect ]) { + _NativeCanvas(PictureRecorder recorder, [Rect? cullRect]) { if (recorder.isRecording) { throw ArgumentError('"recorder" must not already be associated with another Canvas.'); } @@ -6217,8 +6417,16 @@ base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas { _constructor(_recorder!, cullRect.left, cullRect.top, cullRect.right, cullRect.bottom); } - @Native, Double, Double, Double, Double)>(symbol: 'Canvas::Create') - external void _constructor(_NativePictureRecorder recorder, double left, double top, double right, double bottom); + @Native, Double, Double, Double, Double)>( + symbol: 'Canvas::Create', + ) + external void _constructor( + _NativePictureRecorder recorder, + double left, + double top, + double right, + double bottom, + ); // The underlying DlCanvas is owned by the DisplayListBuilder used to create this Canvas. // The Canvas holds a reference to the PictureRecorder to prevent the recorder from being @@ -6254,8 +6462,17 @@ base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas { @Native, Handle, Handle)>(symbol: 'Canvas::saveLayerWithoutBounds') external void _saveLayerWithoutBounds(List? paintObjects, ByteData paintData); - @Native, Double, Double, Double, Double, Handle, Handle)>(symbol: 'Canvas::saveLayer') - external void _saveLayer(double left, double top, double right, double bottom, List? paintObjects, ByteData paintData); + @Native, Double, Double, Double, Double, Handle, Handle)>( + symbol: 'Canvas::saveLayer', + ) + external void _saveLayer( + double left, + double top, + double right, + double bottom, + List? paintObjects, + ByteData paintData, + ); @override @Native)>(symbol: 'Canvas::restore', isLeaf: true) @@ -6309,7 +6526,7 @@ base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas { external void _getTransform(Float64List matrix4); @override - void clipRect(Rect rect, { ClipOp clipOp = ClipOp.intersect, bool doAntiAlias = true }) { + void clipRect(Rect rect, {ClipOp clipOp = ClipOp.intersect, bool doAntiAlias = true}) { assert(_rectIsValid(rect)); rect = _sorted(rect); // Even if rect is still empty - which implies it has a zero dimension - @@ -6318,8 +6535,18 @@ base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas { _clipRect(rect.left, rect.top, rect.right, rect.bottom, clipOp.index, doAntiAlias); } - @Native, Double, Double, Double, Double, Int32, Bool)>(symbol: 'Canvas::clipRect', isLeaf: true) - external void _clipRect(double left, double top, double right, double bottom, int clipOp, bool doAntiAlias); + @Native, Double, Double, Double, Double, Int32, Bool)>( + symbol: 'Canvas::clipRect', + isLeaf: true, + ) + external void _clipRect( + double left, + double top, + double right, + double bottom, + int clipOp, + bool doAntiAlias, + ); @override void clipRRect(RRect rrect, {bool doAntiAlias = true}) { @@ -6373,8 +6600,17 @@ base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas { _drawLine(p1.dx, p1.dy, p2.dx, p2.dy, paint._objects, paint._data); } - @Native, Double, Double, Double, Double, Handle, Handle)>(symbol: 'Canvas::drawLine') - external void _drawLine(double x1, double y1, double x2, double y2, List? paintObjects, ByteData paintData); + @Native, Double, Double, Double, Double, Handle, Handle)>( + symbol: 'Canvas::drawLine', + ) + external void _drawLine( + double x1, + double y1, + double x2, + double y2, + List? paintObjects, + ByteData paintData, + ); @override void drawPaint(Paint paint) { @@ -6393,8 +6629,17 @@ base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas { } } - @Native, Double, Double, Double, Double, Handle, Handle)>(symbol: 'Canvas::drawRect') - external void _drawRect(double left, double top, double right, double bottom, List? paintObjects, ByteData paintData); + @Native, Double, Double, Double, Double, Handle, Handle)>( + symbol: 'Canvas::drawRect', + ) + external void _drawRect( + double left, + double top, + double right, + double bottom, + List? paintObjects, + ByteData paintData, + ); @override void drawRRect(RRect rrect, Paint paint) { @@ -6412,8 +6657,15 @@ base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas { _drawDRRect(outer._getValue32(), inner._getValue32(), paint._objects, paint._data); } - @Native, Handle, Handle, Handle, Handle)>(symbol: 'Canvas::drawDRRect') - external void _drawDRRect(Float32List outer, Float32List inner, List? paintObjects, ByteData paintData); + @Native, Handle, Handle, Handle, Handle)>( + symbol: 'Canvas::drawDRRect', + ) + external void _drawDRRect( + Float32List outer, + Float32List inner, + List? paintObjects, + ByteData paintData, + ); @override void drawOval(Rect rect, Paint paint) { @@ -6424,8 +6676,17 @@ base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas { } } - @Native, Double, Double, Double, Double, Handle, Handle)>(symbol: 'Canvas::drawOval') - external void _drawOval(double left, double top, double right, double bottom, List? paintObjects, ByteData paintData); + @Native, Double, Double, Double, Double, Handle, Handle)>( + symbol: 'Canvas::drawOval', + ) + external void _drawOval( + double left, + double top, + double right, + double bottom, + List? paintObjects, + ByteData paintData, + ); @override void drawCircle(Offset c, double radius, Paint paint) { @@ -6433,26 +6694,58 @@ base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas { _drawCircle(c.dx, c.dy, radius, paint._objects, paint._data); } - @Native, Double, Double, Double, Handle, Handle)>(symbol: 'Canvas::drawCircle') - external void _drawCircle(double x, double y, double radius, List? paintObjects, ByteData paintData); + @Native, Double, Double, Double, Handle, Handle)>( + symbol: 'Canvas::drawCircle', + ) + external void _drawCircle( + double x, + double y, + double radius, + List? paintObjects, + ByteData paintData, + ); @override void drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint) { assert(_rectIsValid(rect)); - _drawArc(rect.left, rect.top, rect.right, rect.bottom, startAngle, sweepAngle, useCenter, paint._objects, paint._data); + _drawArc( + rect.left, + rect.top, + rect.right, + rect.bottom, + startAngle, + sweepAngle, + useCenter, + paint._objects, + paint._data, + ); } - @Native, Double, Double, Double, Double, Double, Double, Bool, Handle, Handle)>(symbol: 'Canvas::drawArc') + @Native< + Void Function( + Pointer, + Double, + Double, + Double, + Double, + Double, + Double, + Bool, + Handle, + Handle, + ) + >(symbol: 'Canvas::drawArc') external void _drawArc( - double left, - double top, - double right, - double bottom, - double startAngle, - double sweepAngle, - bool useCenter, - List? paintObjects, - ByteData paintData); + double left, + double top, + double right, + double bottom, + double startAngle, + double sweepAngle, + bool useCenter, + List? paintObjects, + ByteData paintData, + ); @override void drawPath(Path path, Paint paint) { @@ -6466,88 +6759,142 @@ base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas { void drawImage(Image image, Offset offset, Paint paint) { assert(!image.debugDisposed); assert(_offsetIsValid(offset)); - final String? error = _drawImage(image._image, offset.dx, offset.dy, paint._objects, paint._data, paint.filterQuality.index); + final String? error = _drawImage( + image._image, + offset.dx, + offset.dy, + paint._objects, + paint._data, + paint.filterQuality.index, + ); if (error != null) { throw PictureRasterizationException._(error, stack: image._debugStack); } } - @Native, Pointer, Double, Double, Handle, Handle, Int32)>(symbol: 'Canvas::drawImage') - external String? _drawImage(_Image image, double x, double y, List? paintObjects, ByteData paintData, int filterQualityIndex); + @Native, Pointer, Double, Double, Handle, Handle, Int32)>( + symbol: 'Canvas::drawImage', + ) + external String? _drawImage( + _Image image, + double x, + double y, + List? paintObjects, + ByteData paintData, + int filterQualityIndex, + ); @override void drawImageRect(Image image, Rect src, Rect dst, Paint paint) { assert(!image.debugDisposed); assert(_rectIsValid(src)); assert(_rectIsValid(dst)); - final String? error = _drawImageRect(image._image, - src.left, - src.top, - src.right, - src.bottom, - dst.left, - dst.top, - dst.right, - dst.bottom, - paint._objects, - paint._data, - paint.filterQuality.index); + final String? error = _drawImageRect( + image._image, + src.left, + src.top, + src.right, + src.bottom, + dst.left, + dst.top, + dst.right, + dst.bottom, + paint._objects, + paint._data, + paint.filterQuality.index, + ); if (error != null) { throw PictureRasterizationException._(error, stack: image._debugStack); } } - @Native, Pointer, Double, Double, Double, Double, Double, Double, Double, Double, Handle, Handle, Int32)>(symbol: 'Canvas::drawImageRect') + @Native< + Handle Function( + Pointer, + Pointer, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Handle, + Handle, + Int32, + ) + >(symbol: 'Canvas::drawImageRect') external String? _drawImageRect( - _Image image, - double srcLeft, - double srcTop, - double srcRight, - double srcBottom, - double dstLeft, - double dstTop, - double dstRight, - double dstBottom, - List? paintObjects, - ByteData paintData, - int filterQualityIndex); + _Image image, + double srcLeft, + double srcTop, + double srcRight, + double srcBottom, + double dstLeft, + double dstTop, + double dstRight, + double dstBottom, + List? paintObjects, + ByteData paintData, + int filterQualityIndex, + ); @override void drawImageNine(Image image, Rect center, Rect dst, Paint paint) { assert(!image.debugDisposed); assert(_rectIsValid(center)); assert(_rectIsValid(dst)); - final String? error = _drawImageNine(image._image, - center.left, - center.top, - center.right, - center.bottom, - dst.left, - dst.top, - dst.right, - dst.bottom, - paint._objects, - paint._data, - paint.filterQuality.index); + final String? error = _drawImageNine( + image._image, + center.left, + center.top, + center.right, + center.bottom, + dst.left, + dst.top, + dst.right, + dst.bottom, + paint._objects, + paint._data, + paint.filterQuality.index, + ); if (error != null) { throw PictureRasterizationException._(error, stack: image._debugStack); } } - @Native, Pointer, Double, Double, Double, Double, Double, Double, Double, Double, Handle, Handle, Int32)>(symbol: 'Canvas::drawImageNine') + @Native< + Handle Function( + Pointer, + Pointer, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Handle, + Handle, + Int32, + ) + >(symbol: 'Canvas::drawImageNine') external String? _drawImageNine( - _Image image, - double centerLeft, - double centerTop, - double centerRight, - double centerBottom, - double dstLeft, - double dstTop, - double dstRight, - double dstBottom, - List? paintObjects, - ByteData paintData, - int filterQualityIndex); + _Image image, + double centerLeft, + double centerTop, + double centerRight, + double centerBottom, + double dstLeft, + double dstTop, + double dstRight, + double dstBottom, + List? paintObjects, + ByteData paintData, + int filterQualityIndex, + ); @override void drawPicture(Picture picture) { @@ -6581,7 +6928,12 @@ base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas { } @Native, Handle, Handle, Int32, Handle)>(symbol: 'Canvas::drawPoints') - external void _drawPoints(List? paintObjects, ByteData paintData, int pointMode, Float32List points); + external void _drawPoints( + List? paintObjects, + ByteData paintData, + int pointMode, + Float32List points, + ); @override void drawVertices(Vertices vertices, BlendMode blendMode, Paint paint) { @@ -6589,17 +6941,26 @@ base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas { _drawVertices(vertices, blendMode.index, paint._objects, paint._data); } - @Native, Pointer, Int32, Handle, Handle)>(symbol: 'Canvas::drawVertices') - external void _drawVertices(Vertices vertices, int blendMode, List? paintObjects, ByteData paintData); + @Native, Pointer, Int32, Handle, Handle)>( + symbol: 'Canvas::drawVertices', + ) + external void _drawVertices( + Vertices vertices, + int blendMode, + List? paintObjects, + ByteData paintData, + ); @override - void drawAtlas(Image atlas, - List transforms, - List rects, - List? colors, - BlendMode? blendMode, - Rect? cullRect, - Paint paint) { + void drawAtlas( + Image atlas, + List transforms, + List rects, + List? colors, + BlendMode? blendMode, + Rect? cullRect, + Paint paint, + ) { assert(!atlas.debugDisposed); assert(colors == null || colors.isEmpty || blendMode != null); @@ -6608,7 +6969,9 @@ base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas { throw ArgumentError('"transforms" and "rects" lengths must match.'); } if (colors != null && colors.isNotEmpty && colors.length != rectCount) { - throw ArgumentError('If non-null, "colors" length must match that of "transforms" and "rects".'); + throw ArgumentError( + 'If non-null, "colors" length must match that of "transforms" and "rects".', + ); } final Float32List rstTransformBuffer = Float32List(rectCount * 4); @@ -6632,13 +6995,21 @@ base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas { rectBuffer[index3] = rect.bottom; } - final Int32List? colorBuffer = (colors == null || colors.isEmpty) ? null : _encodeColorList(colors); + final Int32List? colorBuffer = + (colors == null || colors.isEmpty) ? null : _encodeColorList(colors); final Float32List? cullRectBuffer = cullRect?._getValue32(); final int qualityIndex = paint.filterQuality.index; final String? error = _drawAtlas( - paint._objects, paint._data, qualityIndex, atlas._image, rstTransformBuffer, rectBuffer, - colorBuffer, (blendMode ?? BlendMode.src).index, cullRectBuffer + paint._objects, + paint._data, + qualityIndex, + atlas._image, + rstTransformBuffer, + rectBuffer, + colorBuffer, + (blendMode ?? BlendMode.src).index, + cullRectBuffer, ); if (error != null) { @@ -6647,13 +7018,15 @@ base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas { } @override - void drawRawAtlas(Image atlas, - Float32List rstTransforms, - Float32List rects, - Int32List? colors, - BlendMode? blendMode, - Rect? cullRect, - Paint paint) { + void drawRawAtlas( + Image atlas, + Float32List rstTransforms, + Float32List rects, + Int32List? colors, + BlendMode? blendMode, + Rect? cullRect, + Paint paint, + ) { assert(colors == null || blendMode != null); final int rectCount = rects.length; @@ -6664,13 +7037,22 @@ base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas { throw ArgumentError('"rstTransforms" and "rects" lengths must be a multiple of four.'); } if (colors != null && colors.length * 4 != rectCount) { - throw ArgumentError('If non-null, "colors" length must be one fourth the length of "rstTransforms" and "rects".'); + throw ArgumentError( + 'If non-null, "colors" length must be one fourth the length of "rstTransforms" and "rects".', + ); } final int qualityIndex = paint.filterQuality.index; final String? error = _drawAtlas( - paint._objects, paint._data, qualityIndex, atlas._image, rstTransforms, rects, - colors, (blendMode ?? BlendMode.src).index, cullRect?._getValue32() + paint._objects, + paint._data, + qualityIndex, + atlas._image, + rstTransforms, + rects, + colors, + (blendMode ?? BlendMode.src).index, + cullRect?._getValue32(), ); if (error != null) { @@ -6678,25 +7060,46 @@ base class _NativeCanvas extends NativeFieldWrapperClass1 implements Canvas { } } - @Native, Handle, Handle, Int32, Pointer, Handle, Handle, Handle, Int32, Handle)>(symbol: 'Canvas::drawAtlas') + @Native< + Handle Function( + Pointer, + Handle, + Handle, + Int32, + Pointer, + Handle, + Handle, + Handle, + Int32, + Handle, + ) + >(symbol: 'Canvas::drawAtlas') external String? _drawAtlas( - List? paintObjects, - ByteData paintData, - int filterQualityIndex, - _Image atlas, - Float32List rstTransforms, - Float32List rects, - Int32List? colors, - int blendMode, - Float32List? cullRect); + List? paintObjects, + ByteData paintData, + int filterQualityIndex, + _Image atlas, + Float32List rstTransforms, + Float32List rects, + Int32List? colors, + int blendMode, + Float32List? cullRect, + ); @override void drawShadow(Path path, Color color, double elevation, bool transparentOccluder) { _drawShadow(path as _NativePath, color.value, elevation, transparentOccluder); } - @Native, Pointer, Uint32, Double, Bool)>(symbol: 'Canvas::drawShadow') - external void _drawShadow(_NativePath path, int color, double elevation, bool transparentOccluder); + @Native, Pointer, Uint32, Double, Bool)>( + symbol: 'Canvas::drawShadow', + ) + external void _drawShadow( + _NativePath path, + int color, + double elevation, + bool transparentOccluder, + ); @override String toString() => 'Canvas(recording: ${_recorder != null})'; @@ -6837,7 +7240,8 @@ base class _NativePicture extends NativeFieldWrapperClass1 implements Picture { disposed = _disposed; return true; }()); - return disposed ?? (throw StateError('Picture.debugDisposed is only available when asserts are enabled.')); + return disposed ?? + (throw StateError('Picture.debugDisposed is only available when asserts are enabled.')); } @override @@ -6889,7 +7293,9 @@ abstract class PictureRecorder { } base class _NativePictureRecorder extends NativeFieldWrapperClass1 implements PictureRecorder { - _NativePictureRecorder() { _constructor(); } + _NativePictureRecorder() { + _constructor(); + } @Native(symbol: 'PictureRecorder::Create') external void _constructor(); @@ -6999,11 +7405,7 @@ class Shadow { /// Returns a new shadow with its [offset] and [blurRadius] scaled by the given /// factor. Shadow scale(double factor) { - return Shadow( - color: color, - offset: offset * factor, - blurRadius: blurRadius * factor, - ); + return Shadow(color: color, offset: offset * factor, blurRadius: blurRadius * factor); } /// Linearly interpolate between two shadows. @@ -7075,10 +7477,10 @@ class Shadow { if (identical(this, other)) { return true; } - return other is Shadow - && other.color == color - && other.offset == offset - && other.blurRadius == blurRadius; + return other is Shadow && + other.color == color && + other.offset == offset && + other.blurRadius == blurRadius; } @override @@ -7100,18 +7502,18 @@ class Shadow { final Shadow shadow = shadows[shadowIndex]; shadowOffset = shadowIndex * _kBytesPerShadow; - shadowsData.setInt32(_kColorOffset + shadowOffset, - shadow.color.value ^ Shadow._kColorDefault, _kFakeHostEndian); + shadowsData.setInt32( + _kColorOffset + shadowOffset, + shadow.color.value ^ Shadow._kColorDefault, + _kFakeHostEndian, + ); - shadowsData.setFloat32(_kXOffset + shadowOffset, - shadow.offset.dx, _kFakeHostEndian); + shadowsData.setFloat32(_kXOffset + shadowOffset, shadow.offset.dx, _kFakeHostEndian); - shadowsData.setFloat32(_kYOffset + shadowOffset, - shadow.offset.dy, _kFakeHostEndian); + shadowsData.setFloat32(_kYOffset + shadowOffset, shadow.offset.dy, _kFakeHostEndian); final double blurSigma = Shadow.convertRadiusToSigma(shadow.blurRadius); - shadowsData.setFloat32(_kBlurOffset + shadowOffset, - blurSigma, _kFakeHostEndian); + shadowsData.setFloat32(_kBlurOffset + shadowOffset, blurSigma, _kFakeHostEndian); } return shadowsData; @@ -7318,8 +7720,17 @@ base class _NativeImageDescriptor extends NativeFieldWrapperClass1 implements Im @Native, Handle)>(symbol: 'ImageDescriptor::initEncoded') external String? _initEncoded(ImmutableBuffer buffer, _Callback callback); - @Native(symbol: 'ImageDescriptor::initRaw') - external static void _initRaw(ImageDescriptor outDescriptor, ImmutableBuffer buffer, int width, int height, int rowBytes, int pixelFormat); + @Native( + symbol: 'ImageDescriptor::initRaw', + ) + external static void _initRaw( + ImageDescriptor outDescriptor, + ImmutableBuffer buffer, + int width, + int height, + int rowBytes, + int pixelFormat, + ); int? _width; @@ -7374,11 +7785,14 @@ base class _NativeImageDescriptor extends NativeFieldWrapperClass1 implements Im return codec; } - @Native, Handle, Int32, Int32)>(symbol: 'ImageDescriptor::instantiateCodec') + @Native, Handle, Int32, Int32)>( + symbol: 'ImageDescriptor::instantiateCodec', + ) external void _instantiateCodec(Codec outCodec, int targetWidth, int targetHeight); @override - String toString() => 'ImageDescriptor(width: ${_width ?? '?'}, height: ${_height ?? '?'}, bytes per pixel: ${_bytesPerPixel ?? '?'})'; + String toString() => + 'ImageDescriptor(width: ${_width ?? '?'}, height: ${_height ?? '?'}, bytes per pixel: ${_bytesPerPixel ?? '?'})'; } /// Generic callback signature, used by [_futurize]. diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index db59d220f1fc1..01225704b34b4 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -56,7 +56,8 @@ typedef PlatformMessageResponseCallback = void Function(ByteData? data); 'Migrate to ChannelBuffers.setListener instead. ' 'This feature was deprecated after v3.11.0-20.0.pre.', ) -typedef PlatformMessageCallback = void Function(String name, ByteData? data, PlatformMessageResponseCallback? callback); +typedef PlatformMessageCallback = + void Function(String name, ByteData? data, PlatformMessageResponseCallback? callback); // Signature for _setNeedsReportTimings. typedef _SetNeedsReportTimingsFunc = void Function(bool value); @@ -80,8 +81,7 @@ const double _kUnsetGestureSetting = -1.0; const String _kFlutterKeyDataChannel = 'flutter/keydata'; @pragma('vm:entry-point') -ByteData? _wrapUnmodifiableByteData(ByteData? byteData) => - byteData?.asUnmodifiableView(); +ByteData? _wrapUnmodifiableByteData(ByteData? byteData) => byteData?.asUnmodifiableView(); /// A token that represents a root isolate. class RootIsolateToken { @@ -221,16 +221,20 @@ class PlatformDispatcher { FlutterView? get implicitView { final FlutterView? result = _views[_implicitViewId]; // Make sure [implicitView] agrees with `_implicitViewId`. - assert((result != null) == (_implicitViewId != null), - (_implicitViewId != null) ? - 'The implicit view ID is $_implicitViewId, but the implicit view does not exist.' : - 'The implicit view ID is null, but the implicit view exists.'); + assert( + (result != null) == (_implicitViewId != null), + (_implicitViewId != null) + ? 'The implicit view ID is $_implicitViewId, but the implicit view does not exist.' + : 'The implicit view ID is null, but the implicit view exists.', + ); // Make sure [implicitView] never chages. assert(() { if (_debugRecordedLastImplicitView) { - assert(identical(_debugLastImplicitView, result), + assert( + identical(_debugLastImplicitView, result), 'The implicitView has changed:\n' - 'Last: $_debugLastImplicitView\nCurrent: $result'); + 'Last: $_debugLastImplicitView\nCurrent: $result', + ); } else { _debugLastImplicitView = result; _debugRecordedLastImplicitView = true; @@ -239,6 +243,7 @@ class PlatformDispatcher { }()); return result; } + FlutterView? _debugLastImplicitView; bool _debugRecordedLastImplicitView = false; @@ -402,11 +407,7 @@ class PlatformDispatcher { // Called from the engine, via hooks.dart void _beginFrame(int microseconds) { - _invoke1( - onBeginFrame, - _onBeginFrameZone, - Duration(microseconds: microseconds), - ); + _invoke1(onBeginFrame, _onBeginFrameZone, Duration(microseconds: microseconds)); } /// A callback that is invoked for each frame after [onBeginFrame] has @@ -467,63 +468,68 @@ class PlatformDispatcher { final List data = []; for (int i = 0; i < length; ++i) { int offset = i * _kPointerDataFieldCount; - data.add(PointerData( - // The unpacking code must match the struct in pointer_data.h. - embedderId: packet.getInt64(kStride * offset++, _kFakeHostEndian), - timeStamp: Duration(microseconds: packet.getInt64(kStride * offset++, _kFakeHostEndian)), - change: PointerChange.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], - kind: PointerDeviceKind.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], - signalKind: PointerSignalKind.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], - device: packet.getInt64(kStride * offset++, _kFakeHostEndian), - pointerIdentifier: packet.getInt64(kStride * offset++, _kFakeHostEndian), - physicalX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - physicalY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - physicalDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - physicalDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - buttons: packet.getInt64(kStride * offset++, _kFakeHostEndian), - obscured: packet.getInt64(kStride * offset++, _kFakeHostEndian) != 0, - synthesized: packet.getInt64(kStride * offset++, _kFakeHostEndian) != 0, - pressure: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - pressureMin: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - pressureMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - distance: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - distanceMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - size: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - radiusMajor: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - radiusMinor: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - radiusMin: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - radiusMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - orientation: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - tilt: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - platformData: packet.getInt64(kStride * offset++, _kFakeHostEndian), - scrollDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - scrollDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - panX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - panY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - panDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - panDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - scale: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - rotation: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - viewId: packet.getInt64(kStride * offset++, _kFakeHostEndian), - )); + data.add( + PointerData( + // The unpacking code must match the struct in pointer_data.h. + embedderId: packet.getInt64(kStride * offset++, _kFakeHostEndian), + timeStamp: Duration(microseconds: packet.getInt64(kStride * offset++, _kFakeHostEndian)), + change: PointerChange.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], + kind: PointerDeviceKind.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], + signalKind: + PointerSignalKind.values[packet.getInt64(kStride * offset++, _kFakeHostEndian)], + device: packet.getInt64(kStride * offset++, _kFakeHostEndian), + pointerIdentifier: packet.getInt64(kStride * offset++, _kFakeHostEndian), + physicalX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + physicalY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + physicalDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + physicalDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + buttons: packet.getInt64(kStride * offset++, _kFakeHostEndian), + obscured: packet.getInt64(kStride * offset++, _kFakeHostEndian) != 0, + synthesized: packet.getInt64(kStride * offset++, _kFakeHostEndian) != 0, + pressure: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + pressureMin: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + pressureMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + distance: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + distanceMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + size: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + radiusMajor: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + radiusMinor: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + radiusMin: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + radiusMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + orientation: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + tilt: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + platformData: packet.getInt64(kStride * offset++, _kFakeHostEndian), + scrollDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + scrollDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + panX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + panY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + panDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + panDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + scale: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + rotation: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + viewId: packet.getInt64(kStride * offset++, _kFakeHostEndian), + ), + ); assert(offset == (i + 1) * _kPointerDataFieldCount); } return PointerDataPacket(data: data); } - static ChannelCallback _keyDataListener(KeyDataCallback onKeyData, Zone zone) => - (ByteData? packet, PlatformMessageResponseCallback callback) { - _invoke1( - (KeyData keyData) { - final bool handled = onKeyData(keyData); - final Uint8List response = Uint8List(1); - response[0] = handled ? 1 : 0; - callback(response.buffer.asByteData()); - }, - zone, - _unpackKeyData(packet!), - ); - }; + static ChannelCallback _keyDataListener(KeyDataCallback onKeyData, Zone zone) => ( + ByteData? packet, + PlatformMessageResponseCallback callback, + ) { + _invoke1( + (KeyData keyData) { + final bool handled = onKeyData(keyData); + final Uint8List response = Uint8List(1); + response[0] = handled ? 1 : 0; + callback(response.buffer.asByteData()); + }, + zone, + _unpackKeyData(packet!), + ); + }; /// A callback that is invoked when key data is available. /// @@ -555,8 +561,12 @@ class PlatformDispatcher { int offset = 0; final int charDataSize = packet.getUint64(kStride * offset++, _kFakeHostEndian); - final String? character = charDataSize == 0 ? null : utf8.decoder.convert( - packet.buffer.asUint8List(kStride * (offset + _kKeyDataFieldCount), charDataSize)); + final String? character = + charDataSize == 0 + ? null + : utf8.decoder.convert( + packet.buffer.asUint8List(kStride * (offset + _kKeyDataFieldCount), charDataSize), + ); final KeyData keyData = KeyData( timeStamp: Duration(microseconds: packet.getUint64(kStride * offset++, _kFakeHostEndian)), @@ -629,18 +639,30 @@ class PlatformDispatcher { /// The framework invokes [callback] in the same zone in which this method was /// called. void sendPlatformMessage(String name, ByteData? data, PlatformMessageResponseCallback? callback) { - final String? error = - _sendPlatformMessage(name, _zonedPlatformMessageResponseCallback(callback), data); + final String? error = _sendPlatformMessage( + name, + _zonedPlatformMessageResponseCallback(callback), + data, + ); if (error != null) { throw Exception(error); } } - String? _sendPlatformMessage(String name, PlatformMessageResponseCallback? callback, ByteData? data) => - __sendPlatformMessage(name, callback, data); + String? _sendPlatformMessage( + String name, + PlatformMessageResponseCallback? callback, + ByteData? data, + ) => __sendPlatformMessage(name, callback, data); - @Native(symbol: 'PlatformConfigurationNativeApi::SendPlatformMessage') - external static String? __sendPlatformMessage(String name, PlatformMessageResponseCallback? callback, ByteData? data); + @Native( + symbol: 'PlatformConfigurationNativeApi::SendPlatformMessage', + ) + external static String? __sendPlatformMessage( + String name, + PlatformMessageResponseCallback? callback, + ByteData? data, + ); /// Sends a message to a platform-specific plugin via a [SendPort]. /// @@ -650,13 +672,8 @@ class PlatformDispatcher { /// of the channel communication will happen on. The [data] parameter is the /// payload of the message. The [identifier] parameter is a unique integer /// assigned to the message. - void sendPortPlatformMessage( - String name, - ByteData? data, - int identifier, - SendPort port) { - final String? error = - _sendPortPlatformMessage(name, identifier, port.nativePort, data); + void sendPortPlatformMessage(String name, ByteData? data, int identifier, SendPort port) { + final String? error = _sendPortPlatformMessage(name, identifier, port.nativePort, data); if (error != null) { throw Exception(error); } @@ -665,8 +682,15 @@ class PlatformDispatcher { String? _sendPortPlatformMessage(String name, int identifier, int port, ByteData? data) => __sendPortPlatformMessage(name, identifier, port, data); - @Native(symbol: 'PlatformConfigurationNativeApi::SendPortPlatformMessage') - external static String? __sendPortPlatformMessage(String name, int identifier, int port, ByteData? data); + @Native( + symbol: 'PlatformConfigurationNativeApi::SendPortPlatformMessage', + ) + external static String? __sendPortPlatformMessage( + String name, + int identifier, + int port, + ByteData? data, + ); /// Registers the current isolate with the isolate identified with by the /// [token]. This is required if platform channels are to be used on a @@ -675,6 +699,7 @@ class PlatformDispatcher { DartPluginRegistrant.ensureInitialized(); __registerBackgroundIsolate(token._token); } + @Native(symbol: 'PlatformConfigurationNativeApi::RegisterBackgroundIsolate') external static void __registerBackgroundIsolate(int rootIsolateId); @@ -710,9 +735,12 @@ class PlatformDispatcher { } /// Called by [_dispatchPlatformMessage]. - void _respondToPlatformMessage(int responseId, ByteData? data) => __respondToPlatformMessage(responseId, data); + void _respondToPlatformMessage(int responseId, ByteData? data) => + __respondToPlatformMessage(responseId, data); - @Native(symbol: 'PlatformConfigurationNativeApi::RespondToPlatformMessage') + @Native( + symbol: 'PlatformConfigurationNativeApi::RespondToPlatformMessage', + ) external static void __respondToPlatformMessage(int responseId, ByteData? data); /// Wraps the given [callback] in another callback that ensures that the @@ -873,11 +901,9 @@ class PlatformDispatcher { if (newFeatures == previousConfiguration.accessibilityFeatures) { return; } - _configuration = previousConfiguration.copyWith( - accessibilityFeatures: newFeatures, - ); - _invoke(onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone,); - _invoke(onAccessibilityFeaturesChanged, _onAccessibilityFeaturesChangedZone,); + _configuration = previousConfiguration.copyWith(accessibilityFeatures: newFeatures); + _invoke(onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone); + _invoke(onAccessibilityFeaturesChanged, _onAccessibilityFeaturesChangedZone); } /// Change the retained semantics data about this platform dispatcher. @@ -896,7 +922,8 @@ class PlatformDispatcher { semantics, use PlatformDispatcher.instance.views to get a [FlutterView] and call `updateSemantics`. ''') - void updateSemantics(SemanticsUpdate update) => _updateSemantics(update as _NativeSemanticsUpdate); + void updateSemantics(SemanticsUpdate update) => + _updateSemantics(update as _NativeSemanticsUpdate); @Native)>(symbol: 'PlatformConfigurationNativeApi::UpdateSemantics') external static void _updateSemantics(_NativeSemanticsUpdate update); @@ -953,14 +980,18 @@ class PlatformDispatcher { return Locale.fromSubtags( languageCode: result[0], countryCode: result[1] == '' ? null : result[1], - scriptCode: result[2] == '' ? null : result[2]); + scriptCode: result[2] == '' ? null : result[2], + ); } return null; } - List _computePlatformResolvedLocale(List supportedLocalesData) => __computePlatformResolvedLocale(supportedLocalesData); + List _computePlatformResolvedLocale(List supportedLocalesData) => + __computePlatformResolvedLocale(supportedLocalesData); - @Native(symbol: 'PlatformConfigurationNativeApi::ComputePlatformResolvedLocale') + @Native( + symbol: 'PlatformConfigurationNativeApi::ComputePlatformResolvedLocale', + ) external static List __computePlatformResolvedLocale(List supportedLocalesData); /// A callback that is invoked whenever [locale] changes value. @@ -991,11 +1022,13 @@ class PlatformDispatcher { final String countryCode = locales[localeIndex * stringsPerLocale + 1]; final String scriptCode = locales[localeIndex * stringsPerLocale + 2]; - newLocales.add(Locale.fromSubtags( - languageCode: locales[localeIndex * stringsPerLocale], - countryCode: countryCode.isEmpty ? null : countryCode, - scriptCode: scriptCode.isEmpty ? null : scriptCode, - )); + newLocales.add( + Locale.fromSubtags( + languageCode: locales[localeIndex * stringsPerLocale], + countryCode: countryCode.isEmpty ? null : countryCode, + scriptCode: scriptCode.isEmpty ? null : scriptCode, + ), + ); if (!localesDiffer && newLocales[localeIndex] != previousConfiguration.locales[localeIndex]) { localesDiffer = true; } @@ -1159,7 +1192,8 @@ class PlatformDispatcher { _nativeSpellCheckServiceDefined = false; } - final bool? supportsShowingSystemContextMenu = data['supportsShowingSystemContextMenu'] as bool?; + final bool? supportsShowingSystemContextMenu = + data['supportsShowingSystemContextMenu'] as bool?; if (supportsShowingSystemContextMenu != null) { _supportsShowingSystemContextMenu = supportsShowingSystemContextMenu; } else { @@ -1172,18 +1206,24 @@ class PlatformDispatcher { _brieflyShowPassword = brieflyShowPassword; } final Brightness platformBrightness = switch (data['platformBrightness']) { - 'dark' => Brightness.dark, - 'light' => Brightness.light, + 'dark' => Brightness.dark, + 'light' => Brightness.light, final Object? value => throw StateError('$value is not a valid platformBrightness.'), }; final String? systemFontFamily = data['systemFontFamily'] as String?; final int? configurationId = data['configurationId'] as int?; final _PlatformConfiguration previousConfiguration = _configuration; - final bool platformBrightnessChanged = previousConfiguration.platformBrightness != platformBrightness; + final bool platformBrightnessChanged = + previousConfiguration.platformBrightness != platformBrightness; final bool textScaleFactorChanged = previousConfiguration.textScaleFactor != textScaleFactor; - final bool alwaysUse24HourFormatChanged = previousConfiguration.alwaysUse24HourFormat != alwaysUse24HourFormat; + final bool alwaysUse24HourFormatChanged = + previousConfiguration.alwaysUse24HourFormat != alwaysUse24HourFormat; final bool systemFontFamilyChanged = previousConfiguration.systemFontFamily != systemFontFamily; - if (!platformBrightnessChanged && !textScaleFactorChanged && !alwaysUse24HourFormatChanged && !systemFontFamilyChanged && configurationId == null) { + if (!platformBrightnessChanged && + !textScaleFactorChanged && + !alwaysUse24HourFormatChanged && + !systemFontFamilyChanged && + configurationId == null) { return; } _configuration = previousConfiguration.copyWith( @@ -1231,9 +1271,7 @@ class PlatformDispatcher { if (previousConfiguration.semanticsEnabled == enabled) { return; } - _configuration = previousConfiguration.copyWith( - semanticsEnabled: enabled, - ); + _configuration = previousConfiguration.copyWith(semanticsEnabled: enabled); _invoke(onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone); _invoke(onSemanticsEnabledChanged, _onSemanticsEnabledChangedZone); } @@ -1400,11 +1438,15 @@ class PlatformDispatcher { // No need to interpolate if the input value is an integer. return _scaleAndMemoize(unscaledFloor) ?? unscaledFontSize * textScaleFactor; } - assert(unscaledCeil - unscaledFloor == 1, 'Unexpected interpolation range: $unscaledFloor - $unscaledCeil.'); + assert( + unscaledCeil - unscaledFloor == 1, + 'Unexpected interpolation range: $unscaledFloor - $unscaledCeil.', + ); return switch ((_scaleAndMemoize(unscaledFloor), _scaleAndMemoize(unscaledCeil))) { - (null, _) || (_, null) => unscaledFontSize * textScaleFactor, - (final double lower, final double upper) => lower + (upper - lower) * (unscaledFontSize - unscaledFloor), + (null, _) || (_, null) => unscaledFontSize * textScaleFactor, + (final double lower, final double upper) => + lower + (upper - lower) * (unscaledFontSize - unscaledFloor), }; } @@ -1425,7 +1467,10 @@ class PlatformDispatcher { } final double unscaledFontSizeDouble = unscaledFontSize.toDouble(); - final double fontSize = PlatformDispatcher._getScaledFontSize(unscaledFontSizeDouble, configurationId); + final double fontSize = PlatformDispatcher._getScaledFontSize( + unscaledFontSizeDouble, + configurationId, + ); if (fontSize >= 0) { return (_cachedFontSizes ??= {})[unscaledFontSize] = fontSize; } @@ -1781,17 +1826,20 @@ class FrameTiming { /// {@template dart.ui.FrameTiming.fps_milliseconds} /// That's about 16ms for 60fps, and 8ms for 120fps. /// {@endtemplate} - Duration get buildDuration => _rawDuration(FramePhase.buildFinish) - _rawDuration(FramePhase.buildStart); + Duration get buildDuration => + _rawDuration(FramePhase.buildFinish) - _rawDuration(FramePhase.buildStart); /// The duration to rasterize the frame on the raster thread. /// /// {@macro dart.ui.FrameTiming.fps_smoothness_milliseconds} /// {@macro dart.ui.FrameTiming.fps_milliseconds} - Duration get rasterDuration => _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.rasterStart); + Duration get rasterDuration => + _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.rasterStart); /// The duration between receiving the vsync signal and starting building the /// frame. - Duration get vsyncOverhead => _rawDuration(FramePhase.buildStart) - _rawDuration(FramePhase.vsyncStart); + Duration get vsyncOverhead => + _rawDuration(FramePhase.buildStart) - _rawDuration(FramePhase.vsyncStart); /// The timespan between vsync start and raster finish. /// @@ -1800,7 +1848,8 @@ class FrameTiming { /// {@macro dart.ui.FrameTiming.fps_milliseconds} /// /// See also [vsyncOverhead], [buildDuration] and [rasterDuration]. - Duration get totalSpan => _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.vsyncStart); + Duration get totalSpan => + _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.vsyncStart); /// The number of layers stored in the raster cache during the frame. /// @@ -1998,6 +2047,7 @@ enum AppLifecycleState { enum AppExitResponse { /// Exiting the application can proceed. exit, + /// Cancel the exit: do not exit the application. cancel, } @@ -2040,7 +2090,12 @@ enum AppExitType { /// * [Scaffold], which automatically applies the padding in material design /// applications. class ViewPadding { - const ViewPadding._({ required this.left, required this.top, required this.right, required this.bottom }); + const ViewPadding._({ + required this.left, + required this.top, + required this.right, + required this.bottom, + }); /// The distance from the left edge to the first unpadded pixel, in physical pixels. final double left; @@ -2123,15 +2178,17 @@ class ViewConstraints { /// Whether the given size satisfies the constraints. bool isSatisfiedBy(Size size) { - return (minWidth <= size.width) && (size.width <= maxWidth) && - (minHeight <= size.height) && (size.height <= maxHeight); + return (minWidth <= size.width) && + (size.width <= maxWidth) && + (minHeight <= size.height) && + (size.height <= maxHeight); } /// Whether there is exactly one size that satisfies the constraints. bool get isTight => minWidth >= maxWidth && minHeight >= maxHeight; /// Scales each constraint parameter by the inverse of the given factor. - ViewConstraints operator/(double factor) { + ViewConstraints operator /(double factor) { return ViewConstraints( minWidth: minWidth / factor, maxWidth: maxWidth / factor, @@ -2148,11 +2205,11 @@ class ViewConstraints { if (other.runtimeType != runtimeType) { return false; } - return other is ViewConstraints - && other.minWidth == minWidth - && other.maxWidth == maxWidth - && other.minHeight == minHeight - && other.maxHeight == maxHeight; + return other is ViewConstraints && + other.minWidth == minWidth && + other.maxWidth == maxWidth && + other.minHeight == minHeight && + other.maxHeight == maxHeight; } @override @@ -2163,8 +2220,10 @@ class ViewConstraints { if (minWidth == double.infinity && minHeight == double.infinity) { return 'ViewConstraints(biggest)'; } - if (minWidth == 0 && maxWidth == double.infinity && - minHeight == 0 && maxHeight == double.infinity) { + if (minWidth == 0 && + maxWidth == double.infinity && + minHeight == 0 && + maxHeight == double.infinity) { return 'ViewConstraints(unconstrained)'; } String describe(double min, double max, String dim) { @@ -2173,6 +2232,7 @@ class ViewConstraints { } return '${min.toStringAsFixed(1)}<=$dim<=${max.toStringAsFixed(1)}'; } + final String width = describe(minWidth, maxWidth, 'w'); final String height = describe(minHeight, maxHeight, 'h'); return 'ViewConstraints($width, $height)'; @@ -2205,11 +2265,11 @@ class ViewConstraints { class DisplayFeature { // TODO(matanlurey): have original authors document; see https://github.com/flutter/flutter/issues/151917. // ignore: public_member_api_docs - const DisplayFeature({ - required this.bounds, - required this.type, - required this.state, - }) : assert(!identical(type, DisplayFeatureType.cutout) || identical(state, DisplayFeatureState.unknown)); + const DisplayFeature({required this.bounds, required this.type, required this.state}) + : assert( + !identical(type, DisplayFeatureType.cutout) || + identical(state, DisplayFeatureState.unknown), + ); /// The area of the flutter view occupied by this display feature, measured in logical pixels. /// @@ -2240,10 +2300,10 @@ class DisplayFeature { if (other.runtimeType != runtimeType) { return false; } - return other is DisplayFeature - && bounds == other.bounds - && type == other.type - && state == other.state; + return other is DisplayFeature && + bounds == other.bounds && + type == other.type && + state == other.state; } @override @@ -2276,12 +2336,15 @@ class DisplayFeature { enum DisplayFeatureType { /// [DisplayFeature] type is new and not yet known to Flutter. unknown, + /// A fold in the flexible screen without a physical gap. /// /// The bounds for this display feature type indicate where the display makes a crease. fold, + /// A physical separation with a hinge that allows two display panels to fold. hinge, + /// A non-displaying area of the screen, usually housing cameras or sensors. cutout, } @@ -2301,10 +2364,12 @@ enum DisplayFeatureState { /// The display feature is a [DisplayFeatureType.cutout] or this state is new /// and not yet known to Flutter. unknown, + /// The foldable device is completely open. /// /// The screen space that is presented to the user is flat. postureFlat, + /// Fold angle is in an intermediate position between opened and closed state. /// /// There is a non-flat angle between parts of the flexible screen or between @@ -2362,11 +2427,9 @@ class Locale { /// /// * [Locale.fromSubtags], which also allows a [scriptCode] to be /// specified. - const Locale( - this._languageCode, [ - this._countryCode, - ]) : assert(_languageCode != ''), - scriptCode = null; + const Locale(this._languageCode, [this._countryCode]) + : assert(_languageCode != ''), + scriptCode = null; /// Creates a new Locale object. /// @@ -2388,15 +2451,12 @@ class Locale { /// /// Validity is not checked by default, but some methods may throw away /// invalid data. - const Locale.fromSubtags({ - String languageCode = 'und', - this.scriptCode, - String? countryCode, - }) : assert(languageCode != ''), - _languageCode = languageCode, - assert(scriptCode != ''), - assert(countryCode != ''), - _countryCode = countryCode; + const Locale.fromSubtags({String languageCode = 'und', this.scriptCode, String? countryCode}) + : assert(languageCode != ''), + _languageCode = languageCode, + assert(scriptCode != ''), + assert(countryCode != ''), + _countryCode = countryCode; /// The primary language subtag for the locale. /// @@ -2564,11 +2624,15 @@ class Locale { } final String? thisCountryCode = countryCode; final String? otherCountryCode = other.countryCode; - return other.languageCode == languageCode - && other.scriptCode == scriptCode // scriptCode cannot be '' - && (other.countryCode == thisCountryCode // Treat '' as equal to null. - || otherCountryCode != null && otherCountryCode.isEmpty && thisCountryCode == null - || thisCountryCode != null && thisCountryCode.isEmpty && other.countryCode == null); + return other.languageCode == languageCode && + other.scriptCode == + scriptCode // scriptCode cannot be '' + && + (other.countryCode == + thisCountryCode // Treat '' as equal to null. + || + otherCountryCode != null && otherCountryCode.isEmpty && thisCountryCode == null || + thisCountryCode != null && thisCountryCode.isEmpty && other.countryCode == null); } @override @@ -2690,11 +2754,7 @@ typedef ViewFocusChangeCallback = void Function(ViewFocusEvent viewFocusEvent); /// callback. final class ViewFocusEvent { /// Creates a [ViewFocusChange]. - const ViewFocusEvent({ - required this.viewId, - required this.state, - required this.direction, - }); + const ViewFocusEvent({required this.viewId, required this.state, required this.direction}); /// The ID of the [FlutterView] that experienced a focus change. final int viewId; diff --git a/lib/ui/platform_isolate.dart b/lib/ui/platform_isolate.dart index a55e56401d644..d738c80c8805c 100644 --- a/lib/ui/platform_isolate.dart +++ b/lib/ui/platform_isolate.dart @@ -28,8 +28,7 @@ part of dart.ui; /// This API is currently experimental. Future runOnPlatformThread(FutureOr Function() computation) { if (!_platformIsolatesEnabled) { - throw UnsupportedError( - 'Platform thread isolates are not supported by this platform.'); + throw UnsupportedError('Platform thread isolates are not supported by this platform.'); } if (isRunningOnPlatformThread) { return Future(computation); @@ -38,8 +37,9 @@ Future runOnPlatformThread(FutureOr Function() computation) { if (sendPort != null) { return _sendComputation(sendPort, computation); } else { - return (_platformRunnerSendPortFuture ??= _spawnPlatformIsolate()) - .then((SendPort port) => _sendComputation(port, computation)); + return (_platformRunnerSendPortFuture ??= _spawnPlatformIsolate()).then( + (SendPort port) => _sendComputation(port, computation), + ); } } @@ -59,9 +59,9 @@ Future _spawnPlatformIsolate() { // isolate, and errors are fatal is false. But if the isolate does // shutdown unexpectedly, clear the singleton so we can create another. for (final Completer completer in _pending.values) { - completer.completeError(RemoteError( - 'PlatformIsolate shutdown unexpectedly', - StackTrace.empty.toString())); + completer.completeError( + RemoteError('PlatformIsolate shutdown unexpectedly', StackTrace.empty.toString()), + ); } _pending.clear(); _platformRunnerSendPort = null; @@ -80,8 +80,7 @@ Future _spawnPlatformIsolate() { } else { // onError handler message, uncaught async error. // Both values are strings, so calling `toString` is efficient. - final RemoteError error = - RemoteError(remoteError!.toString(), remoteStack.toString()); + final RemoteError error = RemoteError(remoteError!.toString(), remoteStack.toString()); resultCompleter.completeError(error, error.stackTrace); } } else { @@ -90,13 +89,11 @@ Future _spawnPlatformIsolate() { } else { // We encountered an error while starting the new isolate. if (!sendPortCompleter.isCompleted) { - sendPortCompleter.completeError( - IsolateSpawnException('Unable to spawn isolate: $message')); + sendPortCompleter.completeError(IsolateSpawnException('Unable to spawn isolate: $message')); return; } // This shouldn't happen. - throw IsolateSpawnException( - "Internal error: unexpected message: '$message'"); + throw IsolateSpawnException("Internal error: unexpected message: '$message'"); } }; final Isolate parentIsolate = Isolate.current; @@ -110,8 +107,7 @@ Future _spawnPlatformIsolate() { return sendPortCompleter.future; } -Future _sendComputation( - SendPort port, FutureOr Function() computation) { +Future _sendComputation(SendPort port, FutureOr Function() computation) { final int id = ++_nextId; final Completer resultCompleter = Completer(); _pending[id] = resultCompleter; @@ -119,8 +115,7 @@ Future _sendComputation( return resultCompleter.future; } -void _safeSend(SendPort sendPort, int id, Object? result, Object? error, - Object? stackTrace) { +void _safeSend(SendPort sendPort, int id, Object? result, Object? error, Object? stackTrace) { try { sendPort.send(_ComputationResult(id, result, error, stackTrace)); } catch (sendError, sendStack) { @@ -146,11 +141,14 @@ void _platformIsolateMain(Isolate parentIsolate, SendPort sendPort) { } if (potentiallyAsyncResult is Future) { - potentiallyAsyncResult.then((Object? result) { - _safeSend(sendPort, message.id, result, null, null); - }, onError: (Object? e, Object? s) { - _safeSend(sendPort, message.id, null, e, s ?? StackTrace.empty); - }); + potentiallyAsyncResult.then( + (Object? result) { + _safeSend(sendPort, message.id, result, null, null); + }, + onError: (Object? e, Object? s) { + _safeSend(sendPort, message.id, null, e, s ?? StackTrace.empty); + }, + ); } else { _safeSend(sendPort, message.id, potentiallyAsyncResult, null, null); } @@ -167,7 +165,9 @@ external void _nativeSpawn(Function entryPoint); final bool isRunningOnPlatformThread = _isRunningOnPlatformThread(); @Native( - symbol: 'PlatformIsolateNativeApi::IsRunningOnPlatformThread', isLeaf: true) + symbol: 'PlatformIsolateNativeApi::IsRunningOnPlatformThread', + isLeaf: true, +) external bool _isRunningOnPlatformThread(); class _PlatformIsolateReadyMessage { diff --git a/lib/ui/plugins.dart b/lib/ui/plugins.dart index b517530c6f19c..aec1f12598821 100644 --- a/lib/ui/plugins.dart +++ b/lib/ui/plugins.dart @@ -25,8 +25,7 @@ class CallbackHandle { if (runtimeType != other.runtimeType) { return false; } - return other is CallbackHandle - && other._handle == _handle; + return other is CallbackHandle && other._handle == _handle; } @override @@ -40,10 +39,8 @@ class CallbackHandle { /// * [IsolateNameServer], which provides utilities for dealing with /// [Isolate]s. abstract final class PluginUtilities { - static final Map _forwardCache = - {}; - static final Map _backwardCache = - {}; + static final Map _forwardCache = {}; + static final Map _backwardCache = {}; /// Get a handle to a named top-level or static callback function which can /// be easily passed between isolates. @@ -70,7 +67,6 @@ abstract final class PluginUtilities { /// [PluginUtilities.getCallbackHandle], null is returned. Otherwise, a /// tear-off of the callback associated with `handle` is returned. static Function? getCallbackFromHandle(CallbackHandle handle) { - return _backwardCache.putIfAbsent( - handle, () => _getCallbackFromHandle(handle.toRawHandle())); + return _backwardCache.putIfAbsent(handle, () => _getCallbackFromHandle(handle.toRawHandle())); } } diff --git a/lib/ui/pointer.dart b/lib/ui/pointer.dart index ba3ba5a275d58..b3835ceb87d7e 100644 --- a/lib/ui/pointer.dart +++ b/lib/ui/pointer.dart @@ -116,7 +116,7 @@ enum PointerDeviceKind { trackpad, /// An unknown pointer device. - unknown + unknown, } /// The kind of pointer signal event. @@ -134,7 +134,7 @@ enum PointerSignalKind { scale, /// An unknown pointer signal kind. - unknown + unknown, } /// A function that implements the [PointerData.respond] method. @@ -420,49 +420,49 @@ class PointerData { /// Returns a complete textual description of the information in this object. String toStringFull() { return '$runtimeType(' - 'embedderId: $embedderId, ' - 'timeStamp: $timeStamp, ' - 'change: $change, ' - 'kind: $kind, ' - 'signalKind: $signalKind, ' - 'device: $device, ' - 'pointerIdentifier: $pointerIdentifier, ' - 'physicalX: $physicalX, ' - 'physicalY: $physicalY, ' - 'physicalDeltaX: $physicalDeltaX, ' - 'physicalDeltaY: $physicalDeltaY, ' - 'buttons: $buttons, ' - 'synthesized: $synthesized, ' - 'pressure: $pressure, ' - 'pressureMin: $pressureMin, ' - 'pressureMax: $pressureMax, ' - 'distance: $distance, ' - 'distanceMax: $distanceMax, ' - 'size: $size, ' - 'radiusMajor: $radiusMajor, ' - 'radiusMinor: $radiusMinor, ' - 'radiusMin: $radiusMin, ' - 'radiusMax: $radiusMax, ' - 'orientation: $orientation, ' - 'tilt: $tilt, ' - 'platformData: $platformData, ' - 'scrollDeltaX: $scrollDeltaX, ' - 'scrollDeltaY: $scrollDeltaY, ' - 'panX: $panX, ' - 'panY: $panY, ' - 'panDeltaX: $panDeltaX, ' - 'panDeltaY: $panDeltaY, ' - 'scale: $scale, ' - 'rotation: $rotation, ' - 'viewId: $viewId' - ')'; + 'embedderId: $embedderId, ' + 'timeStamp: $timeStamp, ' + 'change: $change, ' + 'kind: $kind, ' + 'signalKind: $signalKind, ' + 'device: $device, ' + 'pointerIdentifier: $pointerIdentifier, ' + 'physicalX: $physicalX, ' + 'physicalY: $physicalY, ' + 'physicalDeltaX: $physicalDeltaX, ' + 'physicalDeltaY: $physicalDeltaY, ' + 'buttons: $buttons, ' + 'synthesized: $synthesized, ' + 'pressure: $pressure, ' + 'pressureMin: $pressureMin, ' + 'pressureMax: $pressureMax, ' + 'distance: $distance, ' + 'distanceMax: $distanceMax, ' + 'size: $size, ' + 'radiusMajor: $radiusMajor, ' + 'radiusMinor: $radiusMinor, ' + 'radiusMin: $radiusMin, ' + 'radiusMax: $radiusMax, ' + 'orientation: $orientation, ' + 'tilt: $tilt, ' + 'platformData: $platformData, ' + 'scrollDeltaX: $scrollDeltaX, ' + 'scrollDeltaY: $scrollDeltaY, ' + 'panX: $panX, ' + 'panY: $panY, ' + 'panDeltaX: $panDeltaX, ' + 'panDeltaY: $panDeltaY, ' + 'scale: $scale, ' + 'rotation: $rotation, ' + 'viewId: $viewId' + ')'; } } /// A sequence of reports about the state of pointers. class PointerDataPacket { /// Creates a packet of pointer data reports. - const PointerDataPacket({ this.data = const [] }); + const PointerDataPacket({this.data = const []}); /// Data about the individual pointers in this packet. /// diff --git a/lib/ui/semantics.dart b/lib/ui/semantics.dart index 59052b157b687..1844b4c689d62 100644 --- a/lib/ui/semantics.dart +++ b/lib/ui/semantics.dart @@ -96,7 +96,10 @@ class SemanticsAction { /// /// This action is used by iOS Full Keyboard Access to reveal contents that /// are currently not visible in the viewport. - static const SemanticsAction scrollToOffset = SemanticsAction._(_kScrollToOffsetIndex, 'scrollToOffset'); + static const SemanticsAction scrollToOffset = SemanticsAction._( + _kScrollToOffsetIndex, + 'scrollToOffset', + ); /// A request to increase the value represented by the semantics node. /// @@ -112,7 +115,10 @@ class SemanticsAction { /// /// For example, this action might be send to a node in a scrollable list that /// is partially off screen to bring it on screen. - static const SemanticsAction showOnScreen = SemanticsAction._(_kShowOnScreenIndex, 'showOnScreen'); + static const SemanticsAction showOnScreen = SemanticsAction._( + _kShowOnScreenIndex, + 'showOnScreen', + ); /// Move the cursor forward by one character. /// @@ -120,7 +126,10 @@ class SemanticsAction { /// /// The action includes a boolean argument, which indicates whether the cursor /// movement should extend (or start) a selection. - static const SemanticsAction moveCursorForwardByCharacter = SemanticsAction._(_kMoveCursorForwardByCharacterIndex, 'moveCursorForwardByCharacter'); + static const SemanticsAction moveCursorForwardByCharacter = SemanticsAction._( + _kMoveCursorForwardByCharacterIndex, + 'moveCursorForwardByCharacter', + ); /// Move the cursor backward by one character. /// @@ -128,7 +137,10 @@ class SemanticsAction { /// /// The action includes a boolean argument, which indicates whether the cursor /// movement should extend (or start) a selection. - static const SemanticsAction moveCursorBackwardByCharacter = SemanticsAction._(_kMoveCursorBackwardByCharacterIndex, 'moveCursorBackwardByCharacter'); + static const SemanticsAction moveCursorBackwardByCharacter = SemanticsAction._( + _kMoveCursorBackwardByCharacterIndex, + 'moveCursorBackwardByCharacter', + ); /// Replaces the current text in the text field. /// @@ -147,7 +159,10 @@ class SemanticsAction { /// /// Setting `base` and `extent` to the same value will move the cursor to /// that position (without selecting anything). - static const SemanticsAction setSelection = SemanticsAction._(_kSetSelectionIndex, 'setSelection'); + static const SemanticsAction setSelection = SemanticsAction._( + _kSetSelectionIndex, + 'setSelection', + ); /// Copy the current selection to the clipboard. static const SemanticsAction copy = SemanticsAction._(_kCopyIndex, 'copy'); @@ -173,7 +188,10 @@ class SemanticsAction { /// See also: /// /// * [focus], which controls the input focus. - static const SemanticsAction didGainAccessibilityFocus = SemanticsAction._(_kDidGainAccessibilityFocusIndex, 'didGainAccessibilityFocus'); + static const SemanticsAction didGainAccessibilityFocus = SemanticsAction._( + _kDidGainAccessibilityFocusIndex, + 'didGainAccessibilityFocus', + ); /// Indicates that the node has lost accessibility focus. /// @@ -186,13 +204,19 @@ class SemanticsAction { /// The accessibility focus is different from the input focus. The input focus /// is usually held by the element that currently responds to keyboard inputs. /// Accessibility focus and input focus can be held by two different nodes! - static const SemanticsAction didLoseAccessibilityFocus = SemanticsAction._(_kDidLoseAccessibilityFocusIndex, 'didLoseAccessibilityFocus'); + static const SemanticsAction didLoseAccessibilityFocus = SemanticsAction._( + _kDidLoseAccessibilityFocusIndex, + 'didLoseAccessibilityFocus', + ); /// Indicates that the user has invoked a custom accessibility action. /// /// This handler is added automatically whenever a custom accessibility /// action is added to a semantics node. - static const SemanticsAction customAction = SemanticsAction._(_kCustomActionIndex, 'customAction'); + static const SemanticsAction customAction = SemanticsAction._( + _kCustomActionIndex, + 'customAction', + ); /// A request that the node should be dismissed. /// @@ -209,7 +233,10 @@ class SemanticsAction { /// /// The action includes a boolean argument, which indicates whether the cursor /// movement should extend (or start) a selection. - static const SemanticsAction moveCursorForwardByWord = SemanticsAction._(_kMoveCursorForwardByWordIndex, 'moveCursorForwardByWord'); + static const SemanticsAction moveCursorForwardByWord = SemanticsAction._( + _kMoveCursorForwardByWordIndex, + 'moveCursorForwardByWord', + ); /// Move the cursor backward by one word. /// @@ -217,7 +244,10 @@ class SemanticsAction { /// /// The action includes a boolean argument, which indicates whether the cursor /// movement should extend (or start) a selection. - static const SemanticsAction moveCursorBackwardByWord = SemanticsAction._(_kMoveCursorBackwardByWordIndex, 'moveCursorBackwardByWord'); + static const SemanticsAction moveCursorBackwardByWord = SemanticsAction._( + _kMoveCursorBackwardByWordIndex, + 'moveCursorBackwardByWord', + ); /// Move the input focus to the respective widget. /// @@ -386,7 +416,10 @@ class SemanticsFlag { /// See also: /// /// * [SemanticsFlag.isChecked], which controls whether the node is "checked" or "unchecked". - static const SemanticsFlag hasCheckedState = SemanticsFlag._(_kHasCheckedStateIndex, 'hasCheckedState'); + static const SemanticsFlag hasCheckedState = SemanticsFlag._( + _kHasCheckedStateIndex, + 'hasCheckedState', + ); /// Whether a semantics node that [hasCheckedState] is checked. /// @@ -409,7 +442,10 @@ class SemanticsFlag { /// can have checked, unchecked, or mixed state. /// /// Must be false when the checkbox is either checked or unchecked. - static const SemanticsFlag isCheckStateMixed = SemanticsFlag._(_kIsCheckStateMixedIndex, 'isCheckStateMixed'); + static const SemanticsFlag isCheckStateMixed = SemanticsFlag._( + _kIsCheckStateMixedIndex, + 'isCheckStateMixed', + ); /// The semantics node has the quality of either being "selected" or "unselected". /// @@ -419,7 +455,10 @@ class SemanticsFlag { /// When this flag is not set, the corresponding widget cannot be selected by /// the user, and the presence or the lack of [isSelected] does not carry any /// meaning. - static const SemanticsFlag hasSelectedState = SemanticsFlag._(_kHasSelectedStateIndex, 'hasSelectedState'); + static const SemanticsFlag hasSelectedState = SemanticsFlag._( + _kHasSelectedStateIndex, + 'hasSelectedState', + ); /// Whether a semantics node is selected. /// @@ -478,7 +517,10 @@ class SemanticsFlag { /// For example, a button can be enabled or disabled and therefore has an /// "enabled" state. Static text is usually neither enabled nor disabled and /// therefore does not have an "enabled" state. - static const SemanticsFlag hasEnabledState = SemanticsFlag._(_kHasEnabledStateIndex, 'hasEnabledState'); + static const SemanticsFlag hasEnabledState = SemanticsFlag._( + _kHasEnabledStateIndex, + 'hasEnabledState', + ); /// Whether a semantic node that [hasEnabledState] is currently enabled. /// @@ -491,7 +533,10 @@ class SemanticsFlag { /// /// For example, a radio button is in a mutually exclusive group because /// only one radio button in that group can be marked as [isChecked]. - static const SemanticsFlag isInMutuallyExclusiveGroup = SemanticsFlag._(_kIsInMutuallyExclusiveGroupIndex, 'isInMutuallyExclusiveGroup'); + static const SemanticsFlag isInMutuallyExclusiveGroup = SemanticsFlag._( + _kIsInMutuallyExclusiveGroupIndex, + 'isInMutuallyExclusiveGroup', + ); /// Whether a semantic node is a header that divides content into sections. /// @@ -602,7 +647,10 @@ class SemanticsFlag { /// See also: /// /// * [SemanticsFlag.isToggled], which controls whether the node is "on" or "off". - static const SemanticsFlag hasToggledState = SemanticsFlag._(_kHasToggledStateIndex, 'hasToggledState'); + static const SemanticsFlag hasToggledState = SemanticsFlag._( + _kHasToggledStateIndex, + 'hasToggledState', + ); /// If true, the semantics node is "on". If false, the semantics node is /// "off". @@ -621,7 +669,10 @@ class SemanticsFlag { /// easily move the accessibility focus to the next set of children. A /// [PageView] widget does not have implicit scrolling, so that users don't /// navigate to the next page when reaching the end of the current one. - static const SemanticsFlag hasImplicitScrolling = SemanticsFlag._(_kHasImplicitScrollingIndex, 'hasImplicitScrolling'); + static const SemanticsFlag hasImplicitScrolling = SemanticsFlag._( + _kHasImplicitScrollingIndex, + 'hasImplicitScrolling', + ); /// The semantics node has the quality of either being "expanded" or "collapsed". /// @@ -630,7 +681,10 @@ class SemanticsFlag { /// See also: /// /// * [SemanticsFlag.isExpanded], which controls whether the node is "expanded" or "collapsed". - static const SemanticsFlag hasExpandedState = SemanticsFlag._(_kHasExpandedStateIndex, 'hasExpandedState'); + static const SemanticsFlag hasExpandedState = SemanticsFlag._( + _kHasExpandedStateIndex, + 'hasExpandedState', + ); /// Whether a semantics node is expanded. /// @@ -710,9 +764,7 @@ class SemanticsFlag { /// * [LocaleStringAttribute], which causes the assistive technologies to /// treat the string in the specific language. abstract base class StringAttribute extends NativeFieldWrapperClass1 { - StringAttribute._({ - required this.range, - }); + StringAttribute._({required this.range}); /// The range of the text to which this attribute applies. final TextRange range; @@ -738,14 +790,18 @@ abstract base class StringAttribute extends NativeFieldWrapperClass1 { base class SpellOutStringAttribute extends StringAttribute { /// Creates a string attribute that denotes the text in [range] must be /// spell out when the assistive technologies announce the string. - SpellOutStringAttribute({ - required TextRange range, - }) : super._(range: range) { + SpellOutStringAttribute({required TextRange range}) : super._(range: range) { _initSpellOutStringAttribute(this, range.start, range.end); } - @Native(symbol: 'NativeStringAttribute::initSpellOutStringAttribute') - external static void _initSpellOutStringAttribute(SpellOutStringAttribute instance, int start, int end); + @Native( + symbol: 'NativeStringAttribute::initSpellOutStringAttribute', + ) + external static void _initSpellOutStringAttribute( + SpellOutStringAttribute instance, + int start, + int end, + ); @override StringAttribute copy({required TextRange range}) { @@ -770,18 +826,22 @@ base class LocaleStringAttribute extends StringAttribute { /// Creates a string attribute that denotes the text in [range] must be /// treated as the language specified by the [locale] when the assistive /// technologies announce the string. - LocaleStringAttribute({ - required TextRange range, - required this.locale, - }) : super._(range: range) { + LocaleStringAttribute({required TextRange range, required this.locale}) : super._(range: range) { _initLocaleStringAttribute(this, range.start, range.end, locale.toLanguageTag()); } /// The language of this attribute. final Locale locale; - @Native(symbol: 'NativeStringAttribute::initLocaleStringAttribute') - external static void _initLocaleStringAttribute(LocaleStringAttribute instance, int start, int end, String locale); + @Native( + symbol: 'NativeStringAttribute::initLocaleStringAttribute', + ) + external static void _initLocaleStringAttribute( + LocaleStringAttribute instance, + int start, + int end, + String locale, + ); @override StringAttribute copy({required TextRange range}) { @@ -969,8 +1029,11 @@ abstract class SemanticsUpdateBuilder { SemanticsUpdate build(); } -base class _NativeSemanticsUpdateBuilder extends NativeFieldWrapperClass1 implements SemanticsUpdateBuilder { - _NativeSemanticsUpdateBuilder() { _constructor(); } +base class _NativeSemanticsUpdateBuilder extends NativeFieldWrapperClass1 + implements SemanticsUpdateBuilder { + _NativeSemanticsUpdateBuilder() { + _constructor(); + } @Native(symbol: 'SemanticsUpdateBuilder::Create') external void _constructor(); @@ -1014,9 +1077,9 @@ base class _NativeSemanticsUpdateBuilder extends NativeFieldWrapperClass1 implem String linkUrl = '', }) { assert(_matrix4IsValid(transform)); - assert ( + assert( headingLevel >= 0 && headingLevel <= 6, - 'Heading level must be between 1 and 6, or 0 to indicate that this node is not a heading.' + 'Heading level must be between 1 and 6, or 0 to indicate that this node is not a heading.', ); _updateNode( id, @@ -1059,92 +1122,99 @@ base class _NativeSemanticsUpdateBuilder extends NativeFieldWrapperClass1 implem linkUrl, ); } + @Native< - Void Function( - Pointer, - Int32, - Int32, - Int32, - Int32, - Int32, - Int32, - Int32, - Int32, - Int32, - Int32, - Double, - Double, - Double, - Double, - Double, - Double, - Double, - Double, - Double, - Handle, - Handle, - Handle, - Handle, - Handle, - Handle, - Handle, - Handle, - Handle, - Handle, - Handle, - Handle, - Int32, - Handle, - Handle, - Handle, - Handle, - Int32, - Handle)>(symbol: 'SemanticsUpdateBuilder::updateNode') + Void Function( + Pointer, + Int32, + Int32, + Int32, + Int32, + Int32, + Int32, + Int32, + Int32, + Int32, + Int32, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Double, + Handle, + Handle, + Handle, + Handle, + Handle, + Handle, + Handle, + Handle, + Handle, + Handle, + Handle, + Handle, + Int32, + Handle, + Handle, + Handle, + Handle, + Int32, + Handle, + ) + >(symbol: 'SemanticsUpdateBuilder::updateNode') external void _updateNode( - int id, - int flags, - int actions, - int maxValueLength, - int currentValueLength, - int textSelectionBase, - int textSelectionExtent, - int platformViewId, - int scrollChildren, - int scrollIndex, - double scrollPosition, - double scrollExtentMax, - double scrollExtentMin, - double left, - double top, - double right, - double bottom, - double elevation, - double thickness, - String? identifier, - String label, - List labelAttributes, - String value, - List valueAttributes, - String increasedValue, - List increasedValueAttributes, - String decreasedValue, - List decreasedValueAttributes, - String hint, - List hintAttributes, - String tooltip, - int textDirection, - Float64List transform, - Int32List childrenInTraversalOrder, - Int32List childrenInHitTestOrder, - Int32List additionalActions, - int headingLevel, - String linkUrl); + int id, + int flags, + int actions, + int maxValueLength, + int currentValueLength, + int textSelectionBase, + int textSelectionExtent, + int platformViewId, + int scrollChildren, + int scrollIndex, + double scrollPosition, + double scrollExtentMax, + double scrollExtentMin, + double left, + double top, + double right, + double bottom, + double elevation, + double thickness, + String? identifier, + String label, + List labelAttributes, + String value, + List valueAttributes, + String increasedValue, + List increasedValueAttributes, + String decreasedValue, + List decreasedValueAttributes, + String hint, + List hintAttributes, + String tooltip, + int textDirection, + Float64List transform, + Int32List childrenInTraversalOrder, + Int32List childrenInHitTestOrder, + Int32List additionalActions, + int headingLevel, + String linkUrl, + ); @override void updateCustomAction({required int id, String? label, String? hint, int overrideId = -1}) { _updateCustomAction(id, label ?? '', hint ?? '', overrideId); } - @Native, Int32, Handle, Handle, Int32)>(symbol: 'SemanticsUpdateBuilder::updateCustomAction') + + @Native, Int32, Handle, Handle, Int32)>( + symbol: 'SemanticsUpdateBuilder::updateCustomAction', + ) external void _updateCustomAction(int id, String label, String hint, int overrideId); @override @@ -1153,6 +1223,7 @@ base class _NativeSemanticsUpdateBuilder extends NativeFieldWrapperClass1 implem _build(semanticsUpdate); return semanticsUpdate; } + @Native, Handle)>(symbol: 'SemanticsUpdateBuilder::build') external void _build(_NativeSemanticsUpdate outSemanticsUpdate); diff --git a/lib/ui/setup_hooks.dart b/lib/ui/setup_hooks.dart index cb79a052c25bf..241a9760c86e2 100644 --- a/lib/ui/setup_hooks.dart +++ b/lib/ui/setup_hooks.dart @@ -11,19 +11,13 @@ void _setupHooks() { developer.registerExtension('ext.ui.window.scheduleFrame', _scheduleFrame); // In debug mode, allow shaders to be reinitialized. - developer.registerExtension( - 'ext.ui.window.reinitializeShader', - _reinitializeShader, - ); + developer.registerExtension('ext.ui.window.reinitializeShader', _reinitializeShader); return true; }()); // In debug and profile mode, allow tools to display the current rendering backend. if (!_kReleaseMode) { - developer.registerExtension( - 'ext.ui.window.impellerEnabled', - _getImpellerEnabled, - ); + developer.registerExtension('ext.ui.window.impellerEnabled', _getImpellerEnabled); } } diff --git a/lib/ui/text.dart b/lib/ui/text.dart index 84813a58bc6cb..f2cea271038b3 100644 --- a/lib/ui/text.dart +++ b/lib/ui/text.dart @@ -98,7 +98,15 @@ class FontWeight { /// A list of all the font weights. static const List values = [ - w100, w200, w300, w400, w500, w600, w700, w800, w900 + w100, + w200, + w300, + w400, + w500, + w600, + w700, + w800, + w900, ]; /// Linearly interpolates between two font weights. @@ -184,10 +192,8 @@ class FontFeature { /// flags whose value can be 1 (when enabled) or 0 (when disabled). /// /// See - const FontFeature( - this.feature, - [ this.value = 1 ] - ) : assert(feature.length == 4, 'Feature tag must be exactly four characters long.'), + const FontFeature(this.feature, [this.value = 1]) + : assert(feature.length == 4, 'Feature tag must be exactly four characters long.'), assert(value >= 0, 'Feature value must be zero or a positive integer.'); /// Create a [FontFeature] object that enables the feature with the given tag. @@ -547,7 +553,7 @@ class FontFeature { /// * /// * /// * - const FontFeature.localeAware({ bool enable = true }) : feature = 'locl', value = enable ? 1 : 0; + const FontFeature.localeAware({bool enable = true}) : feature = 'locl', value = enable ? 1 : 0; /// Display alternative glyphs for numerals (alternate annotation forms). (`nalt`) /// @@ -968,9 +974,7 @@ class FontFeature { if (other.runtimeType != runtimeType) { return false; } - return other is FontFeature - && other.feature == feature - && other.value == value; + return other is FontFeature && other.feature == feature && other.value == value; } @override @@ -1012,11 +1016,12 @@ class FontVariation { /// /// `value` is the value that the axis will be set to. The behavior /// depends on how the font implements the axis. - const FontVariation( - this.axis, - this.value, - ) : assert(axis.length == 4, 'Axis tag must be exactly four characters long.'), - assert(value >= -32768.0 && value < 32768.0, 'Value must be representable as a signed 16.16 fixed-point number, i.e. it must be in this range: -32768.0 ≤ value < 32768.0'); + const FontVariation(this.axis, this.value) + : assert(axis.length == 4, 'Axis tag must be exactly four characters long.'), + assert( + value >= -32768.0 && value < 32768.0, + 'Value must be representable as a signed 16.16 fixed-point number, i.e. it must be in this range: -32768.0 ≤ value < 32768.0', + ); // Constructors below should be alphabetic by axis tag. This makes it easier // to determine when an axis is missing so that we avoid adding duplicates. @@ -1038,7 +1043,10 @@ class FontVariation { /// See also: /// /// * - const FontVariation.italic(this.value) : assert(value >= 0.0), assert(value <= 1.0), axis = 'ital'; + const FontVariation.italic(this.value) + : assert(value >= 0.0), + assert(value <= 1.0), + axis = 'ital'; /// Optical size optimization. (`opzs`) /// @@ -1082,7 +1090,10 @@ class FontVariation { /// See also: /// /// * - const FontVariation.slant(this.value) : assert(value > -90.0), assert(value < 90.0), axis = 'slnt'; + const FontVariation.slant(this.value) + : assert(value > -90.0), + assert(value < 90.0), + axis = 'slnt'; /// Variable font width. (`wdth`) /// @@ -1154,9 +1165,7 @@ class FontVariation { if (other.runtimeType != runtimeType) { return false; } - return other is FontVariation - && other.axis == axis - && other.value == value; + return other is FontVariation && other.axis == axis && other.value == value; } @override @@ -1189,7 +1198,7 @@ class FontVariation { } return FontVariation( a!.axis, - clampDouble(lerpDouble(a.value, b!.value, t)!, -32768.0, 32768.0 - 1.0/65536.0), + clampDouble(lerpDouble(a.value, b!.value, t)!, -32768.0, 32768.0 - 1.0 / 65536.0), ); } @@ -1208,10 +1217,21 @@ class FontVariation { /// the glyph(s) onscreen that's closest to the given [Offset]. final class GlyphInfo { /// Creates a [GlyphInfo] with the specified values. - GlyphInfo(this.graphemeClusterLayoutBounds, this.graphemeClusterCodeUnitRange, this.writingDirection); + GlyphInfo( + this.graphemeClusterLayoutBounds, + this.graphemeClusterCodeUnitRange, + this.writingDirection, + ); - GlyphInfo._(double left, double top, double right, double bottom, int graphemeStart, int graphemeEnd, bool isLTR) - : graphemeClusterLayoutBounds = Rect.fromLTRB(left, top, right, bottom), + GlyphInfo._( + double left, + double top, + double right, + double bottom, + int graphemeStart, + int graphemeEnd, + bool isLTR, + ) : graphemeClusterLayoutBounds = Rect.fromLTRB(left, top, right, bottom), graphemeClusterCodeUnitRange = TextRange(start: graphemeStart, end: graphemeEnd), writingDirection = isLTR ? TextDirection.ltr : TextDirection.rtl; @@ -1235,17 +1255,19 @@ final class GlyphInfo { if (identical(this, other)) { return true; } - return other is GlyphInfo - && graphemeClusterLayoutBounds == other.graphemeClusterLayoutBounds - && graphemeClusterCodeUnitRange == other.graphemeClusterCodeUnitRange - && writingDirection == other.writingDirection; + return other is GlyphInfo && + graphemeClusterLayoutBounds == other.graphemeClusterLayoutBounds && + graphemeClusterCodeUnitRange == other.graphemeClusterCodeUnitRange && + writingDirection == other.writingDirection; } @override - int get hashCode => Object.hash(graphemeClusterLayoutBounds, graphemeClusterCodeUnitRange, writingDirection); + int get hashCode => + Object.hash(graphemeClusterLayoutBounds, graphemeClusterCodeUnitRange, writingDirection); @override - String toString() => 'Glyph($graphemeClusterLayoutBounds, textRange: $graphemeClusterCodeUnitRange, direction: $writingDirection)'; + String toString() => + 'Glyph($graphemeClusterLayoutBounds, textRange: $graphemeClusterCodeUnitRange, direction: $writingDirection)'; } /// Whether and how to align text horizontally. @@ -1324,8 +1346,7 @@ class TextDecoration { @override bool operator ==(Object other) { - return other is TextDecoration - && other._mask == _mask; + return other is TextDecoration && other._mask == _mask; } @override @@ -1368,7 +1389,7 @@ enum TextDecorationStyle { dashed, /// Draw a sinusoidal line - wavy + wavy, } /// {@macro dart.ui.textLeadingDistribution} @@ -1415,7 +1436,6 @@ enum TextLeadingDistribution { /// /// {@endtemplate} class TextHeightBehavior { - /// Creates a new TextHeightBehavior object. /// /// * applyHeightToFirstAscent: When true, the [TextStyle.height] modifier @@ -1480,8 +1500,7 @@ class TextHeightBehavior { /// Returns an encoded int representation of this object (excluding /// [leadingDistribution]). int _encode() { - return (applyHeightToFirstAscent ? 0 : 1 << 0) - | (applyHeightToLastDescent ? 0 : 1 << 1); + return (applyHeightToFirstAscent ? 0 : 1 << 0) | (applyHeightToLastDescent ? 0 : 1 << 1); } @override @@ -1489,10 +1508,10 @@ class TextHeightBehavior { if (other.runtimeType != runtimeType) { return false; } - return other is TextHeightBehavior - && other.applyHeightToFirstAscent == applyHeightToFirstAscent - && other.applyHeightToLastDescent == applyHeightToLastDescent - && other.leadingDistribution == leadingDistribution; + return other is TextHeightBehavior && + other.applyHeightToFirstAscent == applyHeightToFirstAscent && + other.applyHeightToLastDescent == applyHeightToLastDescent && + other.leadingDistribution == leadingDistribution; } @override @@ -1507,10 +1526,10 @@ class TextHeightBehavior { @override String toString() { return 'TextHeightBehavior(' - 'applyHeightToFirstAscent: $applyHeightToFirstAscent, ' - 'applyHeightToLastDescent: $applyHeightToLastDescent, ' - 'leadingDistribution: $leadingDistribution' - ')'; + 'applyHeightToFirstAscent: $applyHeightToFirstAscent, ' + 'applyHeightToLastDescent: $applyHeightToLastDescent, ' + 'leadingDistribution: $leadingDistribution' + ')'; } } @@ -1724,9 +1743,10 @@ class TextStyle { List? shadows, List? fontFeatures, List? fontVariations, - }) : assert(color == null || foreground == null, + }) : assert( + color == null || foreground == null, 'Cannot provide both a color and a foreground\n' - 'The color argument is just a shorthand for "foreground: Paint()..color = color".' + 'The color argument is just a shorthand for "foreground: Paint()..color = color".', ), _encoded = _encodeTextStyle( color, @@ -1786,22 +1806,22 @@ class TextStyle { if (identical(this, other)) { return true; } - return other is TextStyle - && other._leadingDistribution == _leadingDistribution - && other._fontFamily == _fontFamily - && other._fontSize == _fontSize - && other._letterSpacing == _letterSpacing - && other._wordSpacing == _wordSpacing - && other._height == _height - && other._decorationThickness == _decorationThickness - && other._locale == _locale - && other._background == _background - && other._foreground == _foreground - && _listEquals(other._encoded, _encoded) - && _listEquals(other._shadows, _shadows) - && _listEquals(other._fontFamilyFallback, _fontFamilyFallback) - && _listEquals(other._fontFeatures, _fontFeatures) - && _listEquals(other._fontVariations, _fontVariations); + return other is TextStyle && + other._leadingDistribution == _leadingDistribution && + other._fontFamily == _fontFamily && + other._fontSize == _fontSize && + other._letterSpacing == _letterSpacing && + other._wordSpacing == _wordSpacing && + other._height == _height && + other._decorationThickness == _decorationThickness && + other._locale == _locale && + other._background == _background && + other._foreground == _foreground && + _listEquals(other._encoded, _encoded) && + _listEquals(other._shadows, _shadows) && + _listEquals(other._fontFamilyFallback, _fontFamilyFallback) && + _listEquals(other._fontFeatures, _fontFeatures) && + _listEquals(other._fontVariations, _fontVariations); } @override @@ -1831,34 +1851,34 @@ class TextStyle { @override String toString() { final List? fontFamilyFallback = _fontFamilyFallback; - final String heightText = _encoded[0] & 0x02000 == 0x02000 ? (_height == kTextHeightNone ? 'kTextHeightNone' : '${_height}x') : 'unspecified'; + final String heightText = + _encoded[0] & 0x02000 == 0x02000 + ? (_height == kTextHeightNone ? 'kTextHeightNone' : '${_height}x') + : 'unspecified'; return 'TextStyle(' - 'color: ${ _encoded[0] & 0x00002 == 0x00002 ? Color(_encoded[1]) : "unspecified"}, ' - 'decoration: ${ _encoded[0] & 0x00004 == 0x00004 ? TextDecoration._(_encoded[2]) : "unspecified"}, ' - 'decorationColor: ${ _encoded[0] & 0x00008 == 0x00008 ? Color(_encoded[3]) : "unspecified"}, ' - 'decorationStyle: ${ _encoded[0] & 0x00010 == 0x00010 ? TextDecorationStyle.values[_encoded[4]] : "unspecified"}, ' - // The decorationThickness is not in encoded order in order to keep it near the other decoration properties. - 'decorationThickness: ${_encoded[0] & 0x00100 == 0x00100 ? _decorationThickness : "unspecified"}, ' - 'fontWeight: ${ _encoded[0] & 0x00020 == 0x00020 ? FontWeight.values[_encoded[5]] : "unspecified"}, ' - 'fontStyle: ${ _encoded[0] & 0x00040 == 0x00040 ? FontStyle.values[_encoded[6]] : "unspecified"}, ' - 'textBaseline: ${ _encoded[0] & 0x00080 == 0x00080 ? TextBaseline.values[_encoded[7]] : "unspecified"}, ' - 'fontFamily: ${ _encoded[0] & 0x00200 == 0x00200 - && _fontFamily != '' ? _fontFamily : "unspecified"}, ' - 'fontFamilyFallback: ${ _encoded[0] & 0x00200 == 0x00200 - && fontFamilyFallback != null - && fontFamilyFallback.isNotEmpty ? fontFamilyFallback : "unspecified"}, ' - 'fontSize: ${ _encoded[0] & 0x00400 == 0x00400 ? _fontSize : "unspecified"}, ' - 'letterSpacing: ${ _encoded[0] & 0x00800 == 0x00800 ? "${_letterSpacing}x" : "unspecified"}, ' - 'wordSpacing: ${ _encoded[0] & 0x01000 == 0x01000 ? "${_wordSpacing}x" : "unspecified"}, ' - 'height: $heightText, ' - 'leadingDistribution: ${_leadingDistribution ?? "unspecified"}, ' - 'locale: ${ _encoded[0] & 0x04000 == 0x04000 ? _locale : "unspecified"}, ' - 'background: ${ _encoded[0] & 0x08000 == 0x08000 ? _background : "unspecified"}, ' - 'foreground: ${ _encoded[0] & 0x10000 == 0x10000 ? _foreground : "unspecified"}, ' - 'shadows: ${ _encoded[0] & 0x20000 == 0x20000 ? _shadows : "unspecified"}, ' - 'fontFeatures: ${ _encoded[0] & 0x40000 == 0x40000 ? _fontFeatures : "unspecified"}, ' - 'fontVariations: ${ _encoded[0] & 0x80000 == 0x80000 ? _fontVariations : "unspecified"}' - ')'; + 'color: ${_encoded[0] & 0x00002 == 0x00002 ? Color(_encoded[1]) : "unspecified"}, ' + 'decoration: ${_encoded[0] & 0x00004 == 0x00004 ? TextDecoration._(_encoded[2]) : "unspecified"}, ' + 'decorationColor: ${_encoded[0] & 0x00008 == 0x00008 ? Color(_encoded[3]) : "unspecified"}, ' + 'decorationStyle: ${_encoded[0] & 0x00010 == 0x00010 ? TextDecorationStyle.values[_encoded[4]] : "unspecified"}, ' + // The decorationThickness is not in encoded order in order to keep it near the other decoration properties. + 'decorationThickness: ${_encoded[0] & 0x00100 == 0x00100 ? _decorationThickness : "unspecified"}, ' + 'fontWeight: ${_encoded[0] & 0x00020 == 0x00020 ? FontWeight.values[_encoded[5]] : "unspecified"}, ' + 'fontStyle: ${_encoded[0] & 0x00040 == 0x00040 ? FontStyle.values[_encoded[6]] : "unspecified"}, ' + 'textBaseline: ${_encoded[0] & 0x00080 == 0x00080 ? TextBaseline.values[_encoded[7]] : "unspecified"}, ' + 'fontFamily: ${_encoded[0] & 0x00200 == 0x00200 && _fontFamily != '' ? _fontFamily : "unspecified"}, ' + 'fontFamilyFallback: ${_encoded[0] & 0x00200 == 0x00200 && fontFamilyFallback != null && fontFamilyFallback.isNotEmpty ? fontFamilyFallback : "unspecified"}, ' + 'fontSize: ${_encoded[0] & 0x00400 == 0x00400 ? _fontSize : "unspecified"}, ' + 'letterSpacing: ${_encoded[0] & 0x00800 == 0x00800 ? "${_letterSpacing}x" : "unspecified"}, ' + 'wordSpacing: ${_encoded[0] & 0x01000 == 0x01000 ? "${_wordSpacing}x" : "unspecified"}, ' + 'height: $heightText, ' + 'leadingDistribution: ${_leadingDistribution ?? "unspecified"}, ' + 'locale: ${_encoded[0] & 0x04000 == 0x04000 ? _locale : "unspecified"}, ' + 'background: ${_encoded[0] & 0x08000 == 0x08000 ? _background : "unspecified"}, ' + 'foreground: ${_encoded[0] & 0x10000 == 0x10000 ? _foreground : "unspecified"}, ' + 'shadows: ${_encoded[0] & 0x20000 == 0x20000 ? _shadows : "unspecified"}, ' + 'fontFeatures: ${_encoded[0] & 0x40000 == 0x40000 ? _fontFeatures : "unspecified"}, ' + 'fontVariations: ${_encoded[0] & 0x80000 == 0x80000 ? _fontVariations : "unspecified"}' + ')'; } } @@ -2047,7 +2067,8 @@ class ParagraphStyle { _strutStyle = strutStyle, _ellipsis = ellipsis, _locale = locale, - _leadingDistribution = textHeightBehavior?.leadingDistribution ?? TextLeadingDistribution.proportional; + _leadingDistribution = + textHeightBehavior?.leadingDistribution ?? TextLeadingDistribution.proportional; final Int32List _encoded; final String? _fontFamily; @@ -2066,38 +2087,44 @@ class ParagraphStyle { if (other.runtimeType != runtimeType) { return false; } - return other is ParagraphStyle - && other._fontFamily == _fontFamily - && other._fontSize == _fontSize - && other._height == _height - && other._strutStyle == _strutStyle - && other._ellipsis == _ellipsis - && other._locale == _locale - && other._leadingDistribution == _leadingDistribution - && _listEquals(other._encoded, _encoded); + return other is ParagraphStyle && + other._fontFamily == _fontFamily && + other._fontSize == _fontSize && + other._height == _height && + other._strutStyle == _strutStyle && + other._ellipsis == _ellipsis && + other._locale == _locale && + other._leadingDistribution == _leadingDistribution && + _listEquals(other._encoded, _encoded); } @override - int get hashCode => Object.hash(Object.hashAll(_encoded), _fontFamily, _fontSize, _height, _ellipsis, _locale, _leadingDistribution); + int get hashCode => Object.hash( + Object.hashAll(_encoded), + _fontFamily, + _fontSize, + _height, + _ellipsis, + _locale, + _leadingDistribution, + ); @override String toString() { return 'ParagraphStyle(' - 'textAlign: ${ _encoded[0] & 0x002 == 0x002 ? TextAlign.values[_encoded[1]] : "unspecified"}, ' - 'textDirection: ${ _encoded[0] & 0x004 == 0x004 ? TextDirection.values[_encoded[2]] : "unspecified"}, ' - 'fontWeight: ${ _encoded[0] & 0x008 == 0x008 ? FontWeight.values[_encoded[3]] : "unspecified"}, ' - 'fontStyle: ${ _encoded[0] & 0x010 == 0x010 ? FontStyle.values[_encoded[4]] : "unspecified"}, ' - 'maxLines: ${ _encoded[0] & 0x020 == 0x020 ? _encoded[5] : "unspecified"}, ' - 'textHeightBehavior: ${ - _encoded[0] & 0x040 == 0x040 ? - TextHeightBehavior._fromEncoded(_encoded[6], _leadingDistribution).toString() : "unspecified"}, ' - 'fontFamily: ${ _encoded[0] & 0x080 == 0x080 ? _fontFamily : "unspecified"}, ' - 'fontSize: ${ _encoded[0] & 0x100 == 0x100 ? _fontSize : "unspecified"}, ' - 'height: ${ _encoded[0] & 0x200 == 0x200 ? "${_height}x" : "unspecified"}, ' - 'strutStyle: ${ _encoded[0] & 0x400 == 0x400 ? _strutStyle : "unspecified"}, ' - 'ellipsis: ${ _encoded[0] & 0x800 == 0x800 ? '"$_ellipsis"' : "unspecified"}, ' - 'locale: ${ _encoded[0] & 0x1000 == 0x1000 ? _locale : "unspecified"}' - ')'; + 'textAlign: ${_encoded[0] & 0x002 == 0x002 ? TextAlign.values[_encoded[1]] : "unspecified"}, ' + 'textDirection: ${_encoded[0] & 0x004 == 0x004 ? TextDirection.values[_encoded[2]] : "unspecified"}, ' + 'fontWeight: ${_encoded[0] & 0x008 == 0x008 ? FontWeight.values[_encoded[3]] : "unspecified"}, ' + 'fontStyle: ${_encoded[0] & 0x010 == 0x010 ? FontStyle.values[_encoded[4]] : "unspecified"}, ' + 'maxLines: ${_encoded[0] & 0x020 == 0x020 ? _encoded[5] : "unspecified"}, ' + 'textHeightBehavior: ${_encoded[0] & 0x040 == 0x040 ? TextHeightBehavior._fromEncoded(_encoded[6], _leadingDistribution).toString() : "unspecified"}, ' + 'fontFamily: ${_encoded[0] & 0x080 == 0x080 ? _fontFamily : "unspecified"}, ' + 'fontSize: ${_encoded[0] & 0x100 == 0x100 ? _fontSize : "unspecified"}, ' + 'height: ${_encoded[0] & 0x200 == 0x200 ? "${_height}x" : "unspecified"}, ' + 'strutStyle: ${_encoded[0] & 0x400 == 0x400 ? _strutStyle : "unspecified"}, ' + 'ellipsis: ${_encoded[0] & 0x800 == 0x800 ? '"$_ellipsis"' : "unspecified"}, ' + 'locale: ${_encoded[0] & 0x1000 == 0x1000 ? _locale : "unspecified"}' + ')'; } } @@ -2119,18 +2146,19 @@ ByteData _encodeStrut( double? leading, FontWeight? fontWeight, FontStyle? fontStyle, - bool? forceStrutHeight) { + bool? forceStrutHeight, +) { // Strut styles are unique in a paragraph, there is no inheriting so // height == null and height == kTextHeightNone are semantically equivalent. final bool hasHeightOverride = height != null && height != kTextHeightNone; if (fontFamily == null && - fontSize == null && - !hasHeightOverride && - leadingDistribution == null && - leading == null && - fontWeight == null && - fontStyle == null && - forceStrutHeight == null) { + fontSize == null && + !hasHeightOverride && + leadingDistribution == null && + leading == null && + fontWeight == null && + fontStyle == null && + forceStrutHeight == null) { return ByteData(0); } @@ -2271,16 +2299,16 @@ class StrutStyle { if (other.runtimeType != runtimeType) { return false; } - return other is StrutStyle - && other._fontFamily == _fontFamily - && other._leadingDistribution == _leadingDistribution - && _listEquals(other._fontFamilyFallback, _fontFamilyFallback) - && _listEquals(other._encoded.buffer.asInt8List(), _encoded.buffer.asInt8List()); + return other is StrutStyle && + other._fontFamily == _fontFamily && + other._leadingDistribution == _leadingDistribution && + _listEquals(other._fontFamilyFallback, _fontFamilyFallback) && + _listEquals(other._encoded.buffer.asInt8List(), _encoded.buffer.asInt8List()); } @override - int get hashCode => Object.hash(Object.hashAll(_encoded.buffer.asInt8List()), _fontFamily, _leadingDistribution); - + int get hashCode => + Object.hash(Object.hashAll(_encoded.buffer.asInt8List()), _fontFamily, _leadingDistribution); } /// A direction in which text flows. @@ -2381,13 +2409,7 @@ enum TextDirection { /// This is similar to [Rect] but includes an inherent [TextDirection]. class TextBox { /// Creates an object that describes a box containing text. - const TextBox.fromLTRBD( - this.left, - this.top, - this.right, - this.bottom, - this.direction, - ); + const TextBox.fromLTRBD(this.left, this.top, this.right, this.bottom, this.direction); /// The left edge of the text box, irrespective of direction. /// @@ -2437,19 +2459,20 @@ class TextBox { if (other.runtimeType != runtimeType) { return false; } - return other is TextBox - && other.left == left - && other.top == top - && other.right == right - && other.bottom == bottom - && other.direction == direction; + return other is TextBox && + other.left == left && + other.top == top && + other.right == right && + other.bottom == bottom && + other.direction == direction; } @override int get hashCode => Object.hash(left, top, right, bottom, direction); @override - String toString() => 'TextBox.fromLTRBD(${left.toStringAsFixed(1)}, ${top.toStringAsFixed(1)}, ${right.toStringAsFixed(1)}, ${bottom.toStringAsFixed(1)}, $direction)'; + String toString() => + 'TextBox.fromLTRBD(${left.toStringAsFixed(1)}, ${top.toStringAsFixed(1)}, ${right.toStringAsFixed(1)}, ${bottom.toStringAsFixed(1)}, $direction)'; } /// A way to disambiguate a [TextPosition] when its offset could match two @@ -2519,10 +2542,7 @@ class TextPosition { /// Creates an object representing a particular position in a string. /// /// The arguments must not be null (so the [offset] argument is required). - const TextPosition({ - required this.offset, - this.affinity = TextAffinity.downstream, - }); + const TextPosition({required this.offset, this.affinity = TextAffinity.downstream}); /// The index of the character that immediately follows the position in the /// string representation of the text. @@ -2546,9 +2566,7 @@ class TextPosition { if (other.runtimeType != runtimeType) { return false; } - return other is TextPosition - && other.offset == offset - && other.affinity == affinity; + return other is TextPosition && other.offset == offset && other.affinity == affinity; } @override @@ -2572,19 +2590,14 @@ class TextRange { /// /// Instead of creating an empty text range, consider using the [empty] /// constant. - const TextRange({ - required this.start, - required this.end, - }) : assert(start >= -1), - assert(end >= -1); + const TextRange({required this.start, required this.end}) + : assert(start >= -1), + assert(end >= -1); /// A text range that starts and ends at offset. /// /// The [offset] argument must be non-null and greater than or equal to -1. - const TextRange.collapsed(int offset) - : assert(offset >= -1), - start = offset, - end = offset; + const TextRange.collapsed(int offset) : assert(offset >= -1), start = offset, end = offset; /// A text range that contains nothing and is not in the text. static const TextRange empty = TextRange(start: -1, end: -1); @@ -2631,16 +2644,11 @@ class TextRange { if (identical(this, other)) { return true; } - return other is TextRange - && other.start == start - && other.end == end; + return other is TextRange && other.start == start && other.end == end; } @override - int get hashCode => Object.hash( - start.hashCode, - end.hashCode, - ); + int get hashCode => Object.hash(start.hashCode, end.hashCode); @override String toString() => 'TextRange(start: $start, end: $end)'; @@ -2656,9 +2664,7 @@ class ParagraphConstraints { /// Creates constraints for laying out a paragraph. /// /// The [width] argument must not be null. - const ParagraphConstraints({ - required this.width, - }); + const ParagraphConstraints({required this.width}); /// The width the paragraph should use whey computing the positions of glyphs. /// @@ -2684,8 +2690,7 @@ class ParagraphConstraints { if (other.runtimeType != runtimeType) { return false; } - return other is ParagraphConstraints - && other.width == width; + return other is ParagraphConstraints && other.width == width; } @override @@ -2920,32 +2925,42 @@ class LineMetrics { if (other.runtimeType != runtimeType) { return false; } - return other is LineMetrics - && other.hardBreak == hardBreak - && other.ascent == ascent - && other.descent == descent - && other.unscaledAscent == unscaledAscent - && other.height == height - && other.width == width - && other.left == left - && other.baseline == baseline - && other.lineNumber == lineNumber; + return other is LineMetrics && + other.hardBreak == hardBreak && + other.ascent == ascent && + other.descent == descent && + other.unscaledAscent == unscaledAscent && + other.height == height && + other.width == width && + other.left == left && + other.baseline == baseline && + other.lineNumber == lineNumber; } @override - int get hashCode => Object.hash(hardBreak, ascent, descent, unscaledAscent, height, width, left, baseline, lineNumber); + int get hashCode => Object.hash( + hardBreak, + ascent, + descent, + unscaledAscent, + height, + width, + left, + baseline, + lineNumber, + ); @override String toString() { return 'LineMetrics(hardBreak: $hardBreak, ' - 'ascent: $ascent, ' - 'descent: $descent, ' - 'unscaledAscent: $unscaledAscent, ' - 'height: $height, ' - 'width: $width, ' - 'left: $left, ' - 'baseline: $baseline, ' - 'lineNumber: $lineNumber)'; + 'ascent: $ascent, ' + 'descent: $descent, ' + 'unscaledAscent: $unscaledAscent, ' + 'height: $height, ' + 'width: $width, ' + 'left: $left, ' + 'baseline: $baseline, ' + 'lineNumber: $lineNumber)'; } } @@ -3022,7 +3037,12 @@ abstract class Paragraph { /// The [boxHeightStyle] and [boxWidthStyle] parameters must not be null. /// /// See [BoxHeightStyle] and [BoxWidthStyle] for full descriptions of each option. - List getBoxesForRange(int start, int end, {BoxHeightStyle boxHeightStyle = BoxHeightStyle.tight, BoxWidthStyle boxWidthStyle = BoxWidthStyle.tight}); + List getBoxesForRange( + int start, + int end, { + BoxHeightStyle boxHeightStyle = BoxHeightStyle.tight, + BoxWidthStyle boxWidthStyle = BoxWidthStyle.tight, + }); /// Returns a list of text boxes that enclose all placeholders in the paragraph. /// @@ -3178,6 +3198,7 @@ base class _NativeParagraph extends NativeFieldWrapperClass1 implements Paragrap return true; }()); } + @Native, Double)>(symbol: 'Paragraph::layout', isLeaf: true) external void _layout(double width); @@ -3186,24 +3207,35 @@ base class _NativeParagraph extends NativeFieldWrapperClass1 implements Paragrap final List boxes = []; int position = 0; for (int index = 0; index < count; index += 1) { - boxes.add(TextBox.fromLTRBD( - encoded[position++], - encoded[position++], - encoded[position++], - encoded[position++], - TextDirection.values[encoded[position++].toInt()], - )); + boxes.add( + TextBox.fromLTRBD( + encoded[position++], + encoded[position++], + encoded[position++], + encoded[position++], + TextDirection.values[encoded[position++].toInt()], + ), + ); } return boxes; } @override - List getBoxesForRange(int start, int end, {BoxHeightStyle boxHeightStyle = BoxHeightStyle.tight, BoxWidthStyle boxWidthStyle = BoxWidthStyle.tight}) { - return _decodeTextBoxes(_getBoxesForRange(start, end, boxHeightStyle.index, boxWidthStyle.index)); + List getBoxesForRange( + int start, + int end, { + BoxHeightStyle boxHeightStyle = BoxHeightStyle.tight, + BoxWidthStyle boxWidthStyle = BoxWidthStyle.tight, + }) { + return _decodeTextBoxes( + _getBoxesForRange(start, end, boxHeightStyle.index, boxWidthStyle.index), + ); } // See paragraph.cc for the layout of this return value. - @Native, Uint32, Uint32, Uint32, Uint32)>(symbol: 'Paragraph::getRectsForRange') + @Native, Uint32, Uint32, Uint32, Uint32)>( + symbol: 'Paragraph::getRectsForRange', + ) external Float32List _getBoxesForRange(int start, int end, int boxHeightStyle, int boxWidthStyle); @override @@ -3229,8 +3261,11 @@ base class _NativeParagraph extends NativeFieldWrapperClass1 implements Paragrap external GlyphInfo? _getGlyphInfoAt(int codeUnitOffset, Function constructor); @override - GlyphInfo? getClosestGlyphInfoForOffset(Offset offset) => _getClosestGlyphInfoForOffset(offset.dx, offset.dy, GlyphInfo._); - @Native, Double, Double, Handle)>(symbol: 'Paragraph::getClosestGlyphInfo') + GlyphInfo? getClosestGlyphInfoForOffset(Offset offset) => + _getClosestGlyphInfoForOffset(offset.dx, offset.dy, GlyphInfo._); + @Native, Double, Double, Handle)>( + symbol: 'Paragraph::getClosestGlyphInfo', + ) external GlyphInfo? _getClosestGlyphInfoForOffset(double dx, double dy, Function constructor); @override @@ -3264,8 +3299,10 @@ base class _NativeParagraph extends NativeFieldWrapperClass1 implements Paragrap // _getLineBoundary only considers the offset and assumes that the // TextAffinity is upstream. In the case that TextPosition is just after a // word wrap (downstream), we need to return the line for the next offset. - if (position.affinity == TextAffinity.downstream && line != nextLine - && position.offset == line.end && line.end == nextLine.start) { + if (position.affinity == TextAffinity.downstream && + line != nextLine && + position.offset == line.end && + line.end == nextLine.start) { return TextRange(start: nextBoundary[0], end: nextBoundary[1]); } return line; @@ -3288,16 +3325,16 @@ base class _NativeParagraph extends NativeFieldWrapperClass1 implements Paragrap final List metrics = [ for (int index = 0; index < count; index += 1) LineMetrics( - hardBreak: encoded[position++] != 0, - ascent: encoded[position++], - descent: encoded[position++], + hardBreak: encoded[position++] != 0, + ascent: encoded[position++], + descent: encoded[position++], unscaledAscent: encoded[position++], - height: encoded[position++], - width: encoded[position++], - left: encoded[position++], - baseline: encoded[position++], - lineNumber: encoded[position++].toInt(), - ) + height: encoded[position++], + width: encoded[position++], + left: encoded[position++], + baseline: encoded[position++], + lineNumber: encoded[position++].toInt(), + ), ]; return metrics; } @@ -3319,6 +3356,7 @@ base class _NativeParagraph extends NativeFieldWrapperClass1 implements Paragrap final int lineNumber = _getLineNumber(codeUnitOffset); return lineNumber < 0 ? null : lineNumber; } + @Native, Uint32)>(symbol: 'Paragraph::getLineNumberAt') external int _getLineNumber(int codeUnitOffset); @@ -3346,7 +3384,10 @@ base class _NativeParagraph extends NativeFieldWrapperClass1 implements Paragrap disposed = _disposed; return true; }()); - return disposed ?? (throw StateError('$runtimeType.debugDisposed is only available when asserts are enabled.')); + return disposed ?? + (throw StateError( + '$runtimeType.debugDisposed is only available when asserts are enabled.', + )); } @override @@ -3461,7 +3502,10 @@ abstract class ParagraphBuilder { /// The `scale` parameter will scale the `width` and `height` by the specified amount, /// and keep track of the scale. The scales of placeholders added can be accessed /// through [placeholderScales]. This is primarily used for accessibility scaling. - void addPlaceholder(double width, double height, PlaceholderAlignment alignment, { + void addPlaceholder( + double width, + double height, + PlaceholderAlignment alignment, { double scale = 1.0, double? baselineOffset, TextBaseline? baseline, @@ -3478,48 +3522,50 @@ abstract class ParagraphBuilder { base class _NativeParagraphBuilder extends NativeFieldWrapperClass1 implements ParagraphBuilder { _NativeParagraphBuilder(ParagraphStyle style) : _defaultLeadingDistribution = style._leadingDistribution { - List? strutFontFamilies; - final StrutStyle? strutStyle = style._strutStyle; - final ByteData? encodedStrutStyle; - if (strutStyle != null && strutStyle._enabled) { - final String? fontFamily = strutStyle._fontFamily; - strutFontFamilies = [ - if (fontFamily != null) fontFamily, - ...?strutStyle._fontFamilyFallback, - ]; - - assert(TextLeadingDistribution.values.length <= 2); - final TextLeadingDistribution leadingDistribution = strutStyle._leadingDistribution - ?? style._leadingDistribution; - encodedStrutStyle = strutStyle._encoded; - int bitmask = encodedStrutStyle.getInt8(0); - bitmask |= (leadingDistribution.index) << 3; - encodedStrutStyle.setInt8(0, bitmask); - } else { - encodedStrutStyle = null; - } - _constructor( - style._encoded, - encodedStrutStyle, - style._fontFamily ?? '', - strutFontFamilies, - style._fontSize ?? 0, - style._height ?? 0, - style._ellipsis ?? '', - _encodeLocale(style._locale), - ); + List? strutFontFamilies; + final StrutStyle? strutStyle = style._strutStyle; + final ByteData? encodedStrutStyle; + if (strutStyle != null && strutStyle._enabled) { + final String? fontFamily = strutStyle._fontFamily; + strutFontFamilies = [ + if (fontFamily != null) fontFamily, + ...?strutStyle._fontFamilyFallback, + ]; + + assert(TextLeadingDistribution.values.length <= 2); + final TextLeadingDistribution leadingDistribution = + strutStyle._leadingDistribution ?? style._leadingDistribution; + encodedStrutStyle = strutStyle._encoded; + int bitmask = encodedStrutStyle.getInt8(0); + bitmask |= (leadingDistribution.index) << 3; + encodedStrutStyle.setInt8(0, bitmask); + } else { + encodedStrutStyle = null; + } + _constructor( + style._encoded, + encodedStrutStyle, + style._fontFamily ?? '', + strutFontFamilies, + style._fontSize ?? 0, + style._height ?? 0, + style._ellipsis ?? '', + _encodeLocale(style._locale), + ); } - @Native(symbol: 'ParagraphBuilder::Create') + @Native( + symbol: 'ParagraphBuilder::Create', + ) external void _constructor( - Int32List encoded, - ByteData? strutData, - String fontFamily, - List? strutFontFamily, - double fontSize, - double height, - String ellipsis, - String locale, + Int32List encoded, + ByteData? strutData, + String fontFamily, + List? strutFontFamily, + double fontSize, + double height, + String ellipsis, + String locale, ); @override @@ -3542,7 +3588,8 @@ base class _NativeParagraphBuilder extends NativeFieldWrapperClass1 implements P } final Int32List encoded = style._encoded; - final TextLeadingDistribution finalLeadingDistribution = style._leadingDistribution ?? _defaultLeadingDistribution; + final TextLeadingDistribution finalLeadingDistribution = + style._leadingDistribution ?? _defaultLeadingDistribution; // ensure the enum can be represented using 1 bit. assert(TextLeadingDistribution.values.length <= 2); @@ -3556,7 +3603,9 @@ base class _NativeParagraphBuilder extends NativeFieldWrapperClass1 implements P encodedFontFeatures = ByteData(fontFeatures.length * FontFeature._kEncodedSize); int byteOffset = 0; for (final FontFeature feature in fontFeatures) { - feature._encode(ByteData.view(encodedFontFeatures.buffer, byteOffset, FontFeature._kEncodedSize)); + feature._encode( + ByteData.view(encodedFontFeatures.buffer, byteOffset, FontFeature._kEncodedSize), + ); byteOffset += FontFeature._kEncodedSize; } } @@ -3567,7 +3616,9 @@ base class _NativeParagraphBuilder extends NativeFieldWrapperClass1 implements P encodedFontVariations = ByteData(fontVariations.length * FontVariation._kEncodedSize); int byteOffset = 0; for (final FontVariation variation in fontVariations) { - variation._encode(ByteData.view(encodedFontVariations.buffer, byteOffset, FontVariation._kEncodedSize)); + variation._encode( + ByteData.view(encodedFontVariations.buffer, byteOffset, FontVariation._kEncodedSize), + ); byteOffset += FontVariation._kEncodedSize; } } @@ -3592,23 +3643,25 @@ base class _NativeParagraphBuilder extends NativeFieldWrapperClass1 implements P } @Native< - Void Function( - Pointer, - Handle, - Handle, - Double, - Double, - Double, - Double, - Double, - Handle, - Handle, - Handle, - Handle, - Handle, - Handle, - Handle, - Handle)>(symbol: 'ParagraphBuilder::pushStyle') + Void Function( + Pointer, + Handle, + Handle, + Double, + Double, + Double, + Double, + Double, + Handle, + Handle, + Handle, + Handle, + Handle, + Handle, + Handle, + Handle, + ) + >(symbol: 'ParagraphBuilder::pushStyle') external void _pushStyle( Int32List encoded, List fontFamilies, @@ -3645,25 +3698,45 @@ base class _NativeParagraphBuilder extends NativeFieldWrapperClass1 implements P external String? _addText(String text); @override - void addPlaceholder(double width, double height, PlaceholderAlignment alignment, { + void addPlaceholder( + double width, + double height, + PlaceholderAlignment alignment, { double scale = 1.0, double? baselineOffset, TextBaseline? baseline, }) { // Require a baseline to be specified if using a baseline-based alignment. - assert(!(alignment == PlaceholderAlignment.aboveBaseline || - alignment == PlaceholderAlignment.belowBaseline || - alignment == PlaceholderAlignment.baseline) || baseline != null); + assert( + !(alignment == PlaceholderAlignment.aboveBaseline || + alignment == PlaceholderAlignment.belowBaseline || + alignment == PlaceholderAlignment.baseline) || + baseline != null, + ); // Default the baselineOffset to height if null. This will place the placeholder // fully above the baseline, similar to [PlaceholderAlignment.aboveBaseline]. baselineOffset = baselineOffset ?? height; - _addPlaceholder(width * scale, height * scale, alignment.index, baselineOffset * scale, (baseline ?? TextBaseline.alphabetic).index); + _addPlaceholder( + width * scale, + height * scale, + alignment.index, + baselineOffset * scale, + (baseline ?? TextBaseline.alphabetic).index, + ); _placeholderCount++; _placeholderScales.add(scale); } - @Native, Double, Double, Uint32, Double, Uint32)>(symbol: 'ParagraphBuilder::addPlaceholder') - external void _addPlaceholder(double width, double height, int alignment, double baselineOffset, int baseline); + @Native, Double, Double, Uint32, Double, Uint32)>( + symbol: 'ParagraphBuilder::addPlaceholder', + ) + external void _addPlaceholder( + double width, + double height, + int alignment, + double baselineOffset, + int baseline, + ); @override Paragraph build() { @@ -3685,17 +3758,14 @@ base class _NativeParagraphBuilder extends NativeFieldWrapperClass1 implements P /// * `fontFamily`: The family name used to identify the font in text styles. /// If this is not provided, then the family name will be extracted from the font file. Future loadFontFromList(Uint8List list, {String? fontFamily}) { - return _futurize( - (_Callback callback) { - _loadFontFromList(list, callback, fontFamily ?? ''); - return null; - } - ).then((_) => _sendFontChangeMessage()); + return _futurize((_Callback callback) { + _loadFontFromList(list, callback, fontFamily ?? ''); + return null; + }).then((_) => _sendFontChangeMessage()); } -final ByteData _fontChangeMessage = utf8.encode( - json.encode({'type': 'fontsChange'}) -).buffer.asByteData(); +final ByteData _fontChangeMessage = + utf8.encode(json.encode({'type': 'fontsChange'})).buffer.asByteData(); FutureOr _sendFontChangeMessage() async { const String kSystemChannelName = 'flutter/system'; @@ -3705,10 +3775,10 @@ FutureOr _sendFontChangeMessage() async { PlatformDispatcher.instance._onPlatformMessageZone, kSystemChannelName, _fontChangeMessage, - (ByteData? responseData) { }, + (ByteData? responseData) {}, ); } else { - channelBuffers.push(kSystemChannelName, _fontChangeMessage, (ByteData? responseData) { }); + channelBuffers.push(kSystemChannelName, _fontChangeMessage, (ByteData? responseData) {}); } } diff --git a/lib/ui/ui.dart b/lib/ui/ui.dart index 411d36005b3fd..b370493013c4a 100644 --- a/lib/ui/ui.dart +++ b/lib/ui/ui.dart @@ -17,13 +17,7 @@ import 'dart:convert'; import 'dart:developer' as developer; import 'dart:ffi'; import 'dart:io'; -import 'dart:isolate' - show - Isolate, - IsolateSpawnException, - RawReceivePort, - RemoteError, - SendPort; +import 'dart:isolate' show Isolate, IsolateSpawnException, RawReceivePort, RemoteError, SendPort; import 'dart:math' as math; import 'dart:nativewrappers'; import 'dart:typed_data'; diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 229022b0fb017..3f7aecfbf07dc 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -34,7 +34,8 @@ class Display { final double refreshRate; @override - String toString() => 'Display(id: $id, size: $size, devicePixelRatio: $devicePixelRatio, refreshRate: $refreshRate)'; + String toString() => + 'Display(id: $id, size: $size, devicePixelRatio: $devicePixelRatio, refreshRate: $refreshRate)'; } /// A view into which a Flutter [Scene] is drawn. @@ -374,10 +375,17 @@ class FlutterView { /// * [RendererBinding], the Flutter framework class which manages layout and /// painting. void render(Scene scene, {Size? size}) { - _render(viewId, scene as _NativeScene, size?.width ?? physicalSize.width, size?.height ?? physicalSize.height); + _render( + viewId, + scene as _NativeScene, + size?.width ?? physicalSize.width, + size?.height ?? physicalSize.height, + ); } - @Native, Double, Double)>(symbol: 'PlatformConfigurationNativeApi::Render') + @Native, Double, Double)>( + symbol: 'PlatformConfigurationNativeApi::Render', + ) external static void _render(int viewId, _NativeScene scene, double width, double height); /// Change the retained semantics data about this [FlutterView]. @@ -388,7 +396,8 @@ class FlutterView { /// /// This function disposes the given update, which means the semantics update /// cannot be used further. - void updateSemantics(SemanticsUpdate update) => _updateSemantics(update as _NativeSemanticsUpdate); + void updateSemantics(SemanticsUpdate update) => + _updateSemantics(update as _NativeSemanticsUpdate); @Native)>(symbol: 'PlatformConfigurationNativeApi::UpdateSemantics') external static void _updateSemantics(_NativeSemanticsUpdate update); @@ -419,27 +428,29 @@ class FlutterView { @Deprecated( 'Use FlutterView or PlatformDispatcher instead. ' 'Deprecated to prepare for the upcoming multi-window support. ' - 'This feature was deprecated after v3.7.0-32.0.pre.' + 'This feature was deprecated after v3.7.0-32.0.pre.', ) class SingletonFlutterWindow extends FlutterView { @Deprecated( 'Use FlutterView or PlatformDispatcher instead. ' 'Deprecated to prepare for the upcoming multi-window support. ' - 'This feature was deprecated after v3.7.0-32.0.pre.' + 'This feature was deprecated after v3.7.0-32.0.pre.', ) - SingletonFlutterWindow._() : super._( - // TODO(dkwingsmt): This crashes if the implicit view is disabled. We need - // to resolve this by the time embedders are allowed to disable the implicit - // view. - // https://github.com/flutter/flutter/issues/131651 - _implicitViewId!, - PlatformDispatcher.instance, - const _ViewConfiguration(), - ); + SingletonFlutterWindow._() + : super._( + // TODO(dkwingsmt): This crashes if the implicit view is disabled. We need + // to resolve this by the time embedders are allowed to disable the implicit + // view. + // https://github.com/flutter/flutter/issues/131651 + _implicitViewId!, + PlatformDispatcher.instance, + const _ViewConfiguration(), + ); // Gets its configuration from the FlutterView with the same ID if it exists. @override - _ViewConfiguration get _viewConfiguration => platformDispatcher._views[viewId]?._viewConfiguration ?? super._viewConfiguration; + _ViewConfiguration get _viewConfiguration => + platformDispatcher._views[viewId]?._viewConfiguration ?? super._viewConfiguration; /// A callback that is invoked whenever the [devicePixelRatio], /// [physicalSize], [padding], [viewInsets], [PlatformDispatcher.views], or @@ -835,7 +846,8 @@ class SingletonFlutterWindow extends FlutterView { /// /// The framework invokes this callback in the same zone in which the /// callback was set. - VoidCallback? get onAccessibilityFeaturesChanged => platformDispatcher.onAccessibilityFeaturesChanged; + VoidCallback? get onAccessibilityFeaturesChanged => + platformDispatcher.onAccessibilityFeaturesChanged; set onAccessibilityFeaturesChanged(VoidCallback? callback) { platformDispatcher.onAccessibilityFeaturesChanged = callback; } @@ -851,9 +863,7 @@ class SingletonFlutterWindow extends FlutterView { /// /// The framework invokes [callback] in the same zone in which this method /// was called. - void sendPlatformMessage(String name, - ByteData? data, - PlatformMessageResponseCallback? callback) { + void sendPlatformMessage(String name, ByteData? data, PlatformMessageResponseCallback? callback) { platformDispatcher.sendPlatformMessage(name, data, callback); } @@ -988,8 +998,7 @@ class AccessibilityFeatures { if (other.runtimeType != runtimeType) { return false; } - return other is AccessibilityFeatures - && other._index == _index; + return other is AccessibilityFeatures && other._index == _index; } @override @@ -1054,7 +1063,7 @@ enum Brightness { @Deprecated( 'Look up the current FlutterView from the context via View.of(context) or consult the PlatformDispatcher directly instead. ' 'Deprecated to prepare for the upcoming multi-window support. ' - 'This feature was deprecated after v3.7.0-32.0.pre.' + 'This feature was deprecated after v3.7.0-32.0.pre.', ) final SingletonFlutterWindow window = SingletonFlutterWindow._(); @@ -1091,10 +1100,7 @@ class GestureSettings { /// /// Consider using [GestureSettings.copyWith] on an existing settings object /// to ensure that newly added fields are correctly set. - const GestureSettings({ - this.physicalTouchSlop, - this.physicalDoubleTapSlop, - }); + const GestureSettings({this.physicalTouchSlop, this.physicalDoubleTapSlop}); /// The number of physical pixels a pointer is allowed to drift before it is /// considered an intentional movement. @@ -1112,10 +1118,7 @@ class GestureSettings { /// Create a new [GestureSettings] object from an existing value, overwriting /// all of the provided fields. - GestureSettings copyWith({ - double? physicalTouchSlop, - double? physicalDoubleTapSlop, - }) { + GestureSettings copyWith({double? physicalTouchSlop, double? physicalDoubleTapSlop}) { return GestureSettings( physicalTouchSlop: physicalTouchSlop ?? this.physicalTouchSlop, physicalDoubleTapSlop: physicalDoubleTapSlop ?? this.physicalDoubleTapSlop, @@ -1128,13 +1131,14 @@ class GestureSettings { return false; } return other is GestureSettings && - other.physicalTouchSlop == physicalTouchSlop && - other.physicalDoubleTapSlop == physicalDoubleTapSlop; + other.physicalTouchSlop == physicalTouchSlop && + other.physicalDoubleTapSlop == physicalDoubleTapSlop; } @override int get hashCode => Object.hash(physicalTouchSlop, physicalDoubleTapSlop); @override - String toString() => 'GestureSettings(physicalTouchSlop: $physicalTouchSlop, physicalDoubleTapSlop: $physicalDoubleTapSlop)'; + String toString() => + 'GestureSettings(physicalTouchSlop: $physicalTouchSlop, physicalDoubleTapSlop: $physicalDoubleTapSlop)'; } diff --git a/lib/web_ui/dev/analyze.dart b/lib/web_ui/dev/analyze.dart index b7038480720ed..efb943543bc02 100644 --- a/lib/web_ui/dev/analyze.dart +++ b/lib/web_ui/dev/analyze.dart @@ -19,10 +19,7 @@ class AnalyzeCommand extends Command with ArgUtils { @override FutureOr run() async { - final Pipeline buildPipeline = Pipeline(steps: [ - PubGetStep(), - AnalyzeStep(), - ]); + final Pipeline buildPipeline = Pipeline(steps: [PubGetStep(), AnalyzeStep()]); await buildPipeline.run(); return true; } @@ -39,11 +36,10 @@ class PubGetStep extends ProcessStep { @override Future createProcess() { print('Running `dart pub get`...'); - return startProcess( - environment.dartExecutable, - ['pub', 'get'], - workingDirectory: environment.webUiRootDir.path, - ); + return startProcess(environment.dartExecutable, [ + 'pub', + 'get', + ], workingDirectory: environment.webUiRootDir.path); } } @@ -58,10 +54,9 @@ class AnalyzeStep extends ProcessStep { @override Future createProcess() { print('Running `dart analyze`...'); - return startProcess( - environment.dartExecutable, - ['analyze', '--fatal-infos'], - workingDirectory: environment.webUiRootDir.path, - ); + return startProcess(environment.dartExecutable, [ + 'analyze', + '--fatal-infos', + ], workingDirectory: environment.webUiRootDir.path); } } diff --git a/lib/web_ui/dev/browser.dart b/lib/web_ui/dev/browser.dart index 79c328661ce01..c5b3e80e4dbca 100644 --- a/lib/web_ui/dev/browser.dart +++ b/lib/web_ui/dev/browser.dart @@ -42,10 +42,7 @@ abstract class BrowserEnvironment { /// browser in debug mode by pausing test execution after the code is loaded /// but before calling the `main()` function of the test, giving the /// developer a chance to set breakpoints. - Future launchBrowserInstance( - Uri url, { - bool debug = false, - }); + Future launchBrowserInstance(Uri url, {bool debug = false}); } /// An interface for running browser instances. diff --git a/lib/web_ui/dev/browser_process.dart b/lib/web_ui/dev/browser_process.dart index 104dafc51c893..af794ba71e6e8 100644 --- a/lib/web_ui/dev/browser_process.dart +++ b/lib/web_ui/dev/browser_process.dart @@ -20,68 +20,70 @@ class BrowserProcess { // Don't return a Future here because there's no need for the caller to wait // for the process to actually start. They should just wait for the HTTP // request instead. - runZonedGuarded(() async { - final Process process = await startBrowser(); - _processCompleter.complete(process); - - final Uint8Buffer output = Uint8Buffer(); - void drainOutput(Stream> stream) { - try { - _ioSubscriptions - .add(stream.listen(output.addAll, cancelOnError: true)); - } on StateError catch (_) {} - } - - // If we don't drain the stdout and stderr the process can hang. - drainOutput(process.stdout); - drainOutput(process.stderr); - - final int exitCode = await process.exitCode; - - // This hack dodges an otherwise intractable race condition. When the user - // presses Control-C, the signal is sent to the browser and the test - // runner at the same time. It's possible for the browser to exit before - // the [Browser.close] is called, which would trigger the error below. - // - // A negative exit code signals that the process exited due to a signal. - // However, it's possible that this signal didn't come from the user's - // Control-C, in which case we do want to throw the error. The only way to - // resolve the ambiguity is to wait a brief amount of time and see if this - // browser is actually closed. - if (!_closed && exitCode < 0) { - await Future.delayed(const Duration(milliseconds: 200)); - } - - if (!_closed && exitCode != 0) { - final String outputString = utf8.decode(output); - String message = 'Browser process failed with exit code $exitCode.'; - if (outputString.isNotEmpty) { - message += '\nStandard output:\n$outputString'; + runZonedGuarded( + () async { + final Process process = await startBrowser(); + _processCompleter.complete(process); + + final Uint8Buffer output = Uint8Buffer(); + void drainOutput(Stream> stream) { + try { + _ioSubscriptions.add(stream.listen(output.addAll, cancelOnError: true)); + } on StateError catch (_) {} } - throw Exception(message); - } - - _onExitCompleter.complete(); - }, (dynamic error, StackTrace? stackTrace) { - // Ignore any errors after the browser has been closed. - if (_closed) { - return; - } - - // Make sure the process dies even if the error wasn't fatal. - _process.then((Process process) => process.kill()); - - stackTrace ??= Trace.current(); - - if (_onExitCompleter.isCompleted) { - return; - } - _onExitCompleter.completeError( - Exception('Failed to run browser process: $error.'), - stackTrace, - ); - }); + // If we don't drain the stdout and stderr the process can hang. + drainOutput(process.stdout); + drainOutput(process.stderr); + + final int exitCode = await process.exitCode; + + // This hack dodges an otherwise intractable race condition. When the user + // presses Control-C, the signal is sent to the browser and the test + // runner at the same time. It's possible for the browser to exit before + // the [Browser.close] is called, which would trigger the error below. + // + // A negative exit code signals that the process exited due to a signal. + // However, it's possible that this signal didn't come from the user's + // Control-C, in which case we do want to throw the error. The only way to + // resolve the ambiguity is to wait a brief amount of time and see if this + // browser is actually closed. + if (!_closed && exitCode < 0) { + await Future.delayed(const Duration(milliseconds: 200)); + } + + if (!_closed && exitCode != 0) { + final String outputString = utf8.decode(output); + String message = 'Browser process failed with exit code $exitCode.'; + if (outputString.isNotEmpty) { + message += '\nStandard output:\n$outputString'; + } + + throw Exception(message); + } + + _onExitCompleter.complete(); + }, + (dynamic error, StackTrace? stackTrace) { + // Ignore any errors after the browser has been closed. + if (_closed) { + return; + } + + // Make sure the process dies even if the error wasn't fatal. + _process.then((Process process) => process.kill()); + + stackTrace ??= Trace.current(); + + if (_onExitCompleter.isCompleted) { + return; + } + _onExitCompleter.completeError( + Exception('Failed to run browser process: $error.'), + stackTrace, + ); + }, + ); } /// The underlying process. diff --git a/lib/web_ui/dev/build.dart b/lib/web_ui/dev/build.dart index 58ca394dc9bb3..df1fe19123c88 100644 --- a/lib/web_ui/dev/build.dart +++ b/lib/web_ui/dev/build.dart @@ -28,29 +28,34 @@ class BuildCommand extends Command with ArgUtils { argParser.addFlag( 'watch', abbr: 'w', - help: 'Run the build in watch mode so it rebuilds whenever a change is ' + help: + 'Run the build in watch mode so it rebuilds whenever a change is ' 'made. Disabled by default.', ); argParser.addFlag( 'host', - help: 'Build the host build instead of the wasm build, which is ' - 'currently needed for `flutter run --local-engine` to work.' + help: + 'Build the host build instead of the wasm build, which is ' + 'currently needed for `flutter run --local-engine` to work.', ); argParser.addFlag( 'profile', - help: 'Build in profile mode instead of release mode. In this mode, the ' + help: + 'Build in profile mode instead of release mode. In this mode, the ' 'output will be located at "out/wasm_profile".\nThis only applies to ' 'the wasm build. The host build is always built in release mode.', ); argParser.addFlag( 'debug', - help: 'Build in debug mode instead of release mode. In this mode, the ' + help: + 'Build in debug mode instead of release mode. In this mode, the ' 'output will be located at "out/wasm_debug".\nThis only applies to ' 'the wasm build. The host build is always built in release mode.', ); argParser.addFlag( 'dwarf', - help: 'Embed DWARF debugging info into the output wasm modules. This is ' + help: + 'Embed DWARF debugging info into the output wasm modules. This is ' 'only valid in debug mode.', ); } @@ -75,11 +80,7 @@ class BuildCommand extends Command with ArgUtils { } final FilePath libPath = FilePath.fromWebUi('lib'); final List steps = [ - GnPipelineStep( - host: host, - runtimeMode: runtimeMode, - embedDwarf: embedDwarf, - ), + GnPipelineStep(host: host, runtimeMode: runtimeMode, embedDwarf: embedDwarf), NinjaPipelineStep( host: host, runtimeMode: runtimeMode, @@ -108,11 +109,7 @@ class BuildCommand extends Command with ArgUtils { /// Not safe to interrupt as it may leave the `out/` directory in a corrupted /// state. GN is pretty quick though, so it's OK to not support interruption. class GnPipelineStep extends ProcessStep { - GnPipelineStep({ - required this.host, - required this.runtimeMode, - required this.embedDwarf, - }); + GnPipelineStep({required this.host, required this.runtimeMode, required this.embedDwarf}); final bool host; final RuntimeMode runtimeMode; @@ -126,18 +123,13 @@ class GnPipelineStep extends ProcessStep { List get _gnArgs { if (host) { - return [ - '--unoptimized', - '--full-dart-sdk', - ]; + return ['--unoptimized', '--full-dart-sdk']; } else { return [ '--web', '--runtime-mode=${runtimeMode.name}', - if (runtimeMode == RuntimeMode.debug) - '--unoptimized', - if (embedDwarf) - '--wasm-use-dwarf', + if (runtimeMode == RuntimeMode.debug) '--unoptimized', + if (embedDwarf) '--wasm-use-dwarf', ]; } } @@ -145,10 +137,7 @@ class GnPipelineStep extends ProcessStep { @override Future createProcess() { print('Running gn...'); - return startProcess( - path.join(environment.flutterDirectory.path, 'tools', 'gn'), - _gnArgs, - ); + return startProcess(path.join(environment.flutterDirectory.path, 'tools', 'gn'), _gnArgs); } } @@ -156,11 +145,7 @@ class GnPipelineStep extends ProcessStep { /// /// Can be safely interrupted. class NinjaPipelineStep extends ProcessStep { - NinjaPipelineStep({ - required this.host, - required this.runtimeMode, - required this.targets, - }); + NinjaPipelineStep({required this.host, required this.runtimeMode, required this.targets}); @override String get description => 'ninja'; @@ -182,13 +167,6 @@ class NinjaPipelineStep extends ProcessStep { @override Future createProcess() { print('Running autoninja...'); - return startProcess( - 'autoninja', - [ - '-C', - buildDirectory, - ...targets, - ], - ); + return startProcess('autoninja', ['-C', buildDirectory, ...targets]); } } diff --git a/lib/web_ui/dev/chrome.dart b/lib/web_ui/dev/chrome.dart index c3b5d922d65e7..33b2e03e697f2 100644 --- a/lib/web_ui/dev/chrome.dart +++ b/lib/web_ui/dev/chrome.dart @@ -10,8 +10,7 @@ import 'dart:math' as math; import 'package:image/image.dart'; import 'package:path/path.dart' as path; import 'package:test_api/backend.dart'; -import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart' - as wip; +import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart' as wip; import 'browser.dart'; import 'browser_process.dart'; @@ -24,25 +23,15 @@ const String kBlankPageUrl = 'about:blank'; /// Provides an environment for desktop Chrome. class ChromeEnvironment implements BrowserEnvironment { - ChromeEnvironment({ - required bool useDwarf, - }) : _useDwarf = useDwarf; + ChromeEnvironment({required bool useDwarf}) : _useDwarf = useDwarf; late final BrowserInstallation _installation; final bool _useDwarf; @override - Future launchBrowserInstance( - Uri url, { - bool debug = false, - }) async { - return Chrome( - url, - _installation, - debug: debug, - useDwarf: _useDwarf - ); + Future launchBrowserInstance(Uri url, {bool debug = false}) async { + return Chrome(url, _installation, debug: debug, useDwarf: _useDwarf); } @override @@ -51,10 +40,7 @@ class ChromeEnvironment implements BrowserEnvironment { @override Future prepare() async { final String version = packageLock.chromeLock.version; - _installation = await getOrInstallChrome( - version, - infoLog: isCi ? stdout : DevNull(), - ); + _installation = await getOrInstallChrome(version, infoLog: isCi ? stdout : DevNull()); } @override @@ -85,85 +71,80 @@ class Chrome extends Browser { }) { final Completer remoteDebuggerCompleter = Completer.sync(); final Completer exceptionCompleter = Completer(); - return Chrome._(BrowserProcess(() async { - // A good source of various Chrome CLI options: - // https://peter.sh/experiments/chromium-command-line-switches/ - // - // Things to try: - // --font-render-hinting - // --enable-font-antialiasing - // --gpu-rasterization-msaa-sample-count - // --disable-gpu - // --disallow-non-exact-resource-reuse - // --disable-font-subpixel-positioning - final bool isChromeNoSandbox = - Platform.environment['CHROME_NO_SANDBOX'] == 'true'; - final String dir = await generateUserDirectory(installation, useDwarf); - final List args = [ - '--user-data-dir=$dir', - kBlankPageUrl, - if (!debug) - '--headless', - if (isChromeNoSandbox) - '--no-sandbox', - // When headless, this is the actual size of the viewport. - if (!debug) - '--window-size=$kMaxScreenshotWidth,$kMaxScreenshotHeight', - // When debugging, run in maximized mode so there's enough room for DevTools. - if (debug) - '--start-maximized', - if (debug) - '--auto-open-devtools-for-tabs', - if (useDwarf) - '--devtools-flags=enabledExperiments=wasmDWARFDebugging', - // Always run unit tests at a 1x scale factor - '--force-device-scale-factor=1', - if (!useDwarf) - // DWARF debugging requires a Chrome extension. - '--disable-extensions', - '--disable-popup-blocking', - // Indicates that the browser is in "browse without sign-in" (Guest session) mode. - '--bwsi', - '--no-first-run', - '--no-default-browser-check', - '--disable-default-apps', - '--disable-translate', - '--remote-debugging-port=$kDevtoolsPort', + return Chrome._( + BrowserProcess(() async { + // A good source of various Chrome CLI options: + // https://peter.sh/experiments/chromium-command-line-switches/ + // + // Things to try: + // --font-render-hinting + // --enable-font-antialiasing + // --gpu-rasterization-msaa-sample-count + // --disable-gpu + // --disallow-non-exact-resource-reuse + // --disable-font-subpixel-positioning + final bool isChromeNoSandbox = Platform.environment['CHROME_NO_SANDBOX'] == 'true'; + final String dir = await generateUserDirectory(installation, useDwarf); + final List args = [ + '--user-data-dir=$dir', + kBlankPageUrl, + if (!debug) '--headless', + if (isChromeNoSandbox) '--no-sandbox', + // When headless, this is the actual size of the viewport. + if (!debug) '--window-size=$kMaxScreenshotWidth,$kMaxScreenshotHeight', + // When debugging, run in maximized mode so there's enough room for DevTools. + if (debug) '--start-maximized', + if (debug) '--auto-open-devtools-for-tabs', + if (useDwarf) '--devtools-flags=enabledExperiments=wasmDWARFDebugging', + // Always run unit tests at a 1x scale factor + '--force-device-scale-factor=1', + if (!useDwarf) + // DWARF debugging requires a Chrome extension. + '--disable-extensions', + '--disable-popup-blocking', + // Indicates that the browser is in "browse without sign-in" (Guest session) mode. + '--bwsi', + '--no-first-run', + '--no-default-browser-check', + '--disable-default-apps', + '--disable-translate', + '--remote-debugging-port=$kDevtoolsPort', - // SwiftShader support on ARM macs is disabled until they upgrade to a newer - // version of LLVM, see https://issuetracker.google.com/issues/165000222. In - // headless Chrome, the default is to use SwiftShader as a software renderer - // for WebGL contexts. In order to work around this limitation, we can force - // GPU rendering with this flag. - if (environment.isMacosArm) - '--use-angle=metal', - ]; + // SwiftShader support on ARM macs is disabled until they upgrade to a newer + // version of LLVM, see https://issuetracker.google.com/issues/165000222. In + // headless Chrome, the default is to use SwiftShader as a software renderer + // for WebGL contexts. In order to work around this limitation, we can force + // GPU rendering with this flag. + if (environment.isMacosArm) '--use-angle=metal', + ]; - final Process process = - await _spawnChromiumProcess(installation.executable, args); + final Process process = await _spawnChromiumProcess(installation.executable, args); - await setupChromiumTab(url, exceptionCompleter); + await setupChromiumTab(url, exceptionCompleter); - remoteDebuggerCompleter.complete( - getRemoteDebuggerUrl(Uri.parse('http://localhost:$kDevtoolsPort'))); + remoteDebuggerCompleter.complete( + getRemoteDebuggerUrl(Uri.parse('http://localhost:$kDevtoolsPort')), + ); - unawaited(process.exitCode - .then((_) => Directory(dir).deleteSync(recursive: true))); + unawaited(process.exitCode.then((_) => Directory(dir).deleteSync(recursive: true))); - return process; - }), remoteDebuggerCompleter.future, exceptionCompleter.future); + return process; + }), + remoteDebuggerCompleter.future, + exceptionCompleter.future, + ); } Chrome._(this._process, this.remoteDebuggerUrl, this._onUncaughtException); static Future generateUserDirectory( BrowserInstallation installation, - bool useDwarf + bool useDwarf, ) async { - final String userDirectoryPath = environment - .webUiDartToolDir - .createTempSync('test_chrome_user_data_') - .resolveSymbolicLinksSync(); + final String userDirectoryPath = + environment.webUiDartToolDir + .createTempSync('test_chrome_user_data_') + .resolveSymbolicLinksSync(); if (!useDwarf) { return userDirectoryPath; } @@ -171,35 +152,35 @@ class Chrome extends Browser { // Using DWARF debugging info requires installation of a Chrome extension. // We can prompt for this, but in order to avoid prompting on every single // browser launch, we cache the user directory after it has been installed. - final Directory baselineUserDirectory = Directory(path.join( - environment.webUiDartToolDir.path, - 'chrome_user_data_base', - )); - final Directory dwarfExtensionInstallDirectory = Directory(path.join( - baselineUserDirectory.path, - 'Default', - 'Extensions', - // This is the ID of the dwarf debugging extension. - 'pdcpmagijalfljmkmjngeonclgbbannb', - )); + final Directory baselineUserDirectory = Directory( + path.join(environment.webUiDartToolDir.path, 'chrome_user_data_base'), + ); + final Directory dwarfExtensionInstallDirectory = Directory( + path.join( + baselineUserDirectory.path, + 'Default', + 'Extensions', + // This is the ID of the dwarf debugging extension. + 'pdcpmagijalfljmkmjngeonclgbbannb', + ), + ); if (!baselineUserDirectory.existsSync()) { baselineUserDirectory.createSync(recursive: true); } if (!dwarfExtensionInstallDirectory.existsSync()) { - print('DWARF debugging requested. Launching Chrome. Please install the ' - 'extension and then exit Chrome when the installation is complete...'); - final Process addExtension = await Process.start( - installation.executable, - [ - '--user-data-dir=${baselineUserDirectory.path}', - 'https://goo.gle/wasm-debugging-extension', - '--bwsi', - '--no-first-run', - '--no-default-browser-check', - '--disable-default-apps', - '--disable-translate', - ] + print( + 'DWARF debugging requested. Launching Chrome. Please install the ' + 'extension and then exit Chrome when the installation is complete...', ); + final Process addExtension = await Process.start(installation.executable, [ + '--user-data-dir=${baselineUserDirectory.path}', + 'https://goo.gle/wasm-debugging-extension', + '--bwsi', + '--no-first-run', + '--no-default-browser-check', + '--disable-default-apps', + '--disable-translate', + ]); await addExtension.exitCode; } for (final FileSystemEntity input in baselineUserDirectory.listSync(recursive: true)) { @@ -247,14 +228,12 @@ class Chrome extends Browser { // TODO(yjbanov): extends tests to Window, https://github.com/flutter/flutter/issues/65673 @override Future captureScreenshot(math.Rectangle? region) async { - final wip.ChromeConnection chromeConnection = - wip.ChromeConnection('localhost', kDevtoolsPort); + final wip.ChromeConnection chromeConnection = wip.ChromeConnection('localhost', kDevtoolsPort); final wip.ChromeTab? chromeTab = await chromeConnection.getTab( - (wip.ChromeTab chromeTab) => chromeTab.url.contains('localhost')); + (wip.ChromeTab chromeTab) => chromeTab.url.contains('localhost'), + ); if (chromeTab == null) { - throw StateError( - 'Failed locate Chrome tab with the test page', - ); + throw StateError('Failed locate Chrome tab with the test page'); } final wip.WipConnection wipConnection = await chromeTab.connect(); @@ -276,22 +255,21 @@ class Chrome extends Browser { // Setting hardware-independent screen parameters: // https://chromedevtools.github.io/devtools-protocol/tot/Emulation - await wipConnection - .sendCommand('Emulation.setDeviceMetricsOverride', { + await wipConnection.sendCommand('Emulation.setDeviceMetricsOverride', { 'width': kMaxScreenshotWidth, 'height': kMaxScreenshotHeight, 'deviceScaleFactor': 1, 'mobile': false, }); final wip.WipResponse response = await wipConnection.sendCommand( - 'Page.captureScreenshot', captureScreenshotParameters); + 'Page.captureScreenshot', + captureScreenshotParameters, + ); - final Image screenshot = - decodePng(base64.decode(response.result!['data'] as String))!; + final Image screenshot = decodePng(base64.decode(response.result!['data'] as String))!; return screenshot; } - } /// Used by [Chrome] to detect a glibc bug and retry launching the @@ -306,48 +284,57 @@ class Chrome extends Browser { /// Inconsistency detected by ld.so: ../elf/dl-tls.c: 493: _dl_allocate_tls_init: Assertion `listp->slotinfo[cnt].gen <= GL(dl_tls_generation)' failed! const String _kGlibcError = 'Inconsistency detected by ld.so'; -Future _spawnChromiumProcess(String executable, List args, { String? workingDirectory }) async { +Future _spawnChromiumProcess( + String executable, + List args, { + String? workingDirectory, +}) async { // Keep attempting to launch the browser until one of: // - Chrome launched successfully, in which case we just return from the loop. // - The tool detected an unretriable Chrome error, in which case we throw ToolExit. while (true) { - final Process process = await Process.start(executable, args, workingDirectory: workingDirectory); + final Process process = await Process.start( + executable, + args, + workingDirectory: workingDirectory, + ); - process.stdout - .transform(utf8.decoder) - .transform(const LineSplitter()) - .listen((String line) { - print('[CHROME STDOUT]: $line'); - }); + process.stdout.transform(utf8.decoder).transform(const LineSplitter()).listen((String line) { + print('[CHROME STDOUT]: $line'); + }); // Wait until the DevTools are listening before trying to connect. This is // only required for flutter_test --platform=chrome and not flutter run. bool hitGlibcBug = false; await process.stderr - .transform(utf8.decoder) - .transform(const LineSplitter()) - .map((String line) { - print('[CHROME STDERR]:$line'); - if (line.contains(_kGlibcError)) { - hitGlibcBug = true; - } - return line; - }) - .firstWhere((String line) => line.startsWith('DevTools listening'), orElse: () { - if (hitGlibcBug) { - const String message = 'Encountered glibc bug ' - 'https://sourceware.org/bugzilla/show_bug.cgi?id=19329. ' - 'Will try launching browser again.'; - print(message); - return message; - } - print('Failed to launch browser. Command used to launch it: ${args.join(' ')}'); - throw Exception( - 'Failed to launch browser. Make sure you are using an up-to-date ' - 'Chrome or Edge. Otherwise, consider using -d web-server instead ' - 'and filing an issue at https://github.com/flutter/flutter/issues.', + .transform(utf8.decoder) + .transform(const LineSplitter()) + .map((String line) { + print('[CHROME STDERR]:$line'); + if (line.contains(_kGlibcError)) { + hitGlibcBug = true; + } + return line; + }) + .firstWhere( + (String line) => line.startsWith('DevTools listening'), + orElse: () { + if (hitGlibcBug) { + const String message = + 'Encountered glibc bug ' + 'https://sourceware.org/bugzilla/show_bug.cgi?id=19329. ' + 'Will try launching browser again.'; + print(message); + return message; + } + print('Failed to launch browser. Command used to launch it: ${args.join(' ')}'); + throw Exception( + 'Failed to launch browser. Make sure you are using an up-to-date ' + 'Chrome or Edge. Otherwise, consider using -d web-server instead ' + 'and filing an issue at https://github.com/flutter/flutter/issues.', + ); + }, ); - }); if (!hitGlibcBug) { return process; @@ -362,10 +349,13 @@ Future _spawnChromiumProcess(String executable, List args, { St // the rails already due to the glibc bug, and we're just scrambling to keep // the system stable. // ignore: unawaited_futures - process.exitCode.timeout(const Duration(seconds: 1), onTimeout: () { - process.kill(); - return -1; - }); + process.exitCode.timeout( + const Duration(seconds: 1), + onTimeout: () { + process.kill(); + return -1; + }, + ); } } @@ -381,7 +371,9 @@ Future getRemoteDebuggerUrl(Uri base) async { final HttpClientResponse response = await request.close(); final List? jsonObject = await json.fuse(utf8).decoder.bind(response).single as List?; - return base.resolve((jsonObject!.first as Map)['devtoolsFrontendUrl'] as String); + return base.resolve( + (jsonObject!.first as Map)['devtoolsFrontendUrl'] as String, + ); } catch (_) { // If we fail to talk to the remote debugger protocol, give up and return // the raw URL rather than crashing. @@ -389,25 +381,22 @@ Future getRemoteDebuggerUrl(Uri base) async { } } -Future setupChromiumTab( - Uri url, Completer exceptionCompleter) async { - final wip.ChromeConnection chromeConnection = - wip.ChromeConnection('localhost', kDevtoolsPort); +Future setupChromiumTab(Uri url, Completer exceptionCompleter) async { + final wip.ChromeConnection chromeConnection = wip.ChromeConnection('localhost', kDevtoolsPort); final wip.ChromeTab? chromeTab = await chromeConnection.getTab( - (wip.ChromeTab chromeTab) => chromeTab.url == kBlankPageUrl); + (wip.ChromeTab chromeTab) => chromeTab.url == kBlankPageUrl, + ); final wip.WipConnection wipConnection = await chromeTab!.connect(); await wipConnection.runtime.enable(); - wipConnection.runtime.onExceptionThrown.listen( - (wip.ExceptionThrownEvent event) { - if (!exceptionCompleter.isCompleted) { - final String text = event.exceptionDetails.text; - final String? description = event.exceptionDetails.exception?.description; - exceptionCompleter.complete('$text: $description'); - } + wipConnection.runtime.onExceptionThrown.listen((wip.ExceptionThrownEvent event) { + if (!exceptionCompleter.isCompleted) { + final String text = event.exceptionDetails.text; + final String? description = event.exceptionDetails.exception?.description; + exceptionCompleter.complete('$text: $description'); } - ); + }); await wipConnection.page.enable(); diff --git a/lib/web_ui/dev/chrome_installer.dart b/lib/web_ui/dev/chrome_installer.dart index a044ba462f975..01c9c3cd20c04 100644 --- a/lib/web_ui/dev/chrome_installer.dart +++ b/lib/web_ui/dev/chrome_installer.dart @@ -39,8 +39,10 @@ Future getOrInstallChrome( // then the bot will download Chrome from CIPD and place it in a cache and // set the environment variable CHROME_EXECUTABLE. if (io.Platform.environment.containsKey(_chromeExecutableVar)) { - infoLog.writeln('Using Chrome from $_chromeExecutableVar variable: ' - '${io.Platform.environment[_chromeExecutableVar]}'); + infoLog.writeln( + 'Using Chrome from $_chromeExecutableVar variable: ' + '${io.Platform.environment[_chromeExecutableVar]}', + ); return BrowserInstallation( version: 'cipd', executable: io.Platform.environment[_chromeExecutableVar]!, @@ -48,27 +50,25 @@ Future getOrInstallChrome( } if (requestedVersion == 'system') { - return BrowserInstallation( - version: 'system', - executable: await _findSystemChromeExecutable(), - ); + return BrowserInstallation(version: 'system', executable: await _findSystemChromeExecutable()); } ChromeInstaller? installer; try { - installer = requestedVersion == 'latest' - ? await ChromeInstaller.latest() - : ChromeInstaller(version: requestedVersion); + installer = + requestedVersion == 'latest' + ? await ChromeInstaller.latest() + : ChromeInstaller(version: requestedVersion); if (installer.isInstalled) { infoLog.writeln( - 'Installation was skipped because Chrome version ${installer.version} is already installed.'); + 'Installation was skipped because Chrome version ${installer.version} is already installed.', + ); } else { infoLog.writeln('Installing Chrome version: ${installer.version}'); await installer.install(); final BrowserInstallation installation = installer.getInstallation()!; - infoLog.writeln( - 'Installations complete. To launch it run ${installation.executable}'); + infoLog.writeln('Installations complete. To launch it run ${installation.executable}'); } return installer.getInstallation()!; } finally { @@ -77,12 +77,10 @@ Future getOrInstallChrome( } Future _findSystemChromeExecutable() async { - final io.ProcessResult which = - await io.Process.run('which', ['google-chrome']); + final io.ProcessResult which = await io.Process.run('which', ['google-chrome']); if (which.exitCode != 0) { - throw BrowserInstallerException( - 'Failed to locate system Chrome installation.'); + throw BrowserInstallerException('Failed to locate system Chrome installation.'); } return which.stdout as String; @@ -90,23 +88,21 @@ Future _findSystemChromeExecutable() async { /// Manages the installation of a particular [version] of Chrome. class ChromeInstaller { - factory ChromeInstaller({ - required String version, - }) { + factory ChromeInstaller({required String version}) { if (version == 'system') { throw BrowserInstallerException( - 'Cannot install system version of Chrome. System Chrome must be installed manually.'); + 'Cannot install system version of Chrome. System Chrome must be installed manually.', + ); } if (version == 'latest') { throw BrowserInstallerException( - 'Expected a concrete Chromer version, but got $version. Maybe use ChromeInstaller.latest()?'); + 'Expected a concrete Chromer version, but got $version. Maybe use ChromeInstaller.latest()?', + ); } final io.Directory chromeInstallationDir = io.Directory( path.join(environment.webUiDartToolDir.path, 'chrome'), ); - final io.Directory versionDir = io.Directory( - path.join(chromeInstallationDir.path, version), - ); + final io.Directory versionDir = io.Directory(path.join(chromeInstallationDir.path, version)); return ChromeInstaller._( version: version, chromeInstallationDir: chromeInstallationDir, @@ -168,13 +164,9 @@ class ChromeInstaller { final String url = PlatformBinding.instance.getChromeDownloadUrl(version); print('Downloading Chrome from $url'); - final StreamedResponse download = await client.send(Request( - 'GET', - Uri.parse(url), - )); + final StreamedResponse download = await client.send(Request('GET', Uri.parse(url))); - final io.File downloadedFile = - io.File(path.join(versionDir.path, 'chrome.zip')); + final io.File downloadedFile = io.File(path.join(versionDir.path, 'chrome.zip')); await download.stream.pipe(downloadedFile.openWrite()); /// Windows LUCI bots does not have a `unzip`. Instead we are @@ -197,21 +189,21 @@ class ChromeInstaller { final String filename = file.name; if (file.isFile) { final List data = file.content as List; - io.File(path.joinAll([ - versionDir.path, - // Remove the "chrome-win/" path prefix, which is the Windows - // convention for Chromium directory structure. - ...path.split(filename).skip(1), - ])) + io.File( + path.joinAll([ + versionDir.path, + // Remove the "chrome-win/" path prefix, which is the Windows + // convention for Chromium directory structure. + ...path.split(filename).skip(1), + ]), + ) ..createSync(recursive: true) ..writeAsBytesSync(data); } } stopwatch.stop(); - print( - 'The unzip took ${stopwatch.elapsedMilliseconds ~/ 1000} seconds.' - ); + print('The unzip took ${stopwatch.elapsedMilliseconds ~/ 1000} seconds.'); } else { // We have to unzip into a temporary directory and then copy the files // out because our tests expect the files to be direct children of the @@ -220,24 +212,22 @@ class ChromeInstaller { // directory and into the version directory. final io.Directory tmpDir = await chromeInstallationDir.createTemp(); final io.Directory unzipDir = tmpDir; - final io.ProcessResult unzipResult = - await io.Process.run('unzip', [ + final io.ProcessResult unzipResult = await io.Process.run('unzip', [ downloadedFile.path, '-d', unzipDir.path, ]); if (unzipResult.exitCode != 0) { throw BrowserInstallerException( - 'Failed to unzip the downloaded Chrome archive ${downloadedFile.path}.\n' - 'With the version path ${versionDir.path}\n' - 'The unzip process exited with code ${unzipResult.exitCode}.'); + 'Failed to unzip the downloaded Chrome archive ${downloadedFile.path}.\n' + 'With the version path ${versionDir.path}\n' + 'The unzip process exited with code ${unzipResult.exitCode}.', + ); } - final io.Directory topLevelDir = - await tmpDir.list().single as io.Directory; + final io.Directory topLevelDir = await tmpDir.list().single as io.Directory; await for (final io.FileSystemEntity entity in topLevelDir.list()) { - await entity - .rename(path.join(versionDir.path, path.basename(entity.path))); + await entity.rename(path.join(versionDir.path, path.basename(entity.path))); } await tmpDir.delete(recursive: true); } @@ -254,11 +244,15 @@ class ChromeInstaller { Future fetchLatestChromeVersion() async { final Client client = Client(); try { - final Response response = await client.get(Uri.parse( - 'https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2FLAST_CHANGE?alt=media')); + final Response response = await client.get( + Uri.parse( + 'https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2FLAST_CHANGE?alt=media', + ), + ); if (response.statusCode != 200) { throw BrowserInstallerException( - 'Failed to fetch latest Chrome version. Server returned status code ${response.statusCode}'); + 'Failed to fetch latest Chrome version. Server returned status code ${response.statusCode}', + ); } return response.body; } finally { diff --git a/lib/web_ui/dev/cipd.dart b/lib/web_ui/dev/cipd.dart index 70a17b170acef..5fb541a0c285f 100644 --- a/lib/web_ui/dev/cipd.dart +++ b/lib/web_ui/dev/cipd.dart @@ -67,15 +67,7 @@ data: '${path.basenameWithoutExtension(configFile.path)}.json', '--log-level', logLevel, - if (!isDryRun) ...[ - '--tag', - 'version:$version', - '--ref', - version, - ], - if (isDryRun) ...[ - '--out', - '${path.basenameWithoutExtension(configFile.path)}.zip', - ], + if (!isDryRun) ...['--tag', 'version:$version', '--ref', version], + if (isDryRun) ...['--out', '${path.basenameWithoutExtension(configFile.path)}.zip'], ], workingDirectory: directory.path); } diff --git a/lib/web_ui/dev/clean.dart b/lib/web_ui/dev/clean.dart index 48584c7b81be2..d6d99dfee322c 100644 --- a/lib/web_ui/dev/clean.dart +++ b/lib/web_ui/dev/clean.dart @@ -45,16 +45,14 @@ class CleanCommand extends Command with ArgUtils { io.Directory(legacyBuildPath), io.File(path.join(environment.webUiRootDir.path, '.dart_tool', 'package_config.json')), io.File(path.join(environment.webUiRootDir.path, 'pubspec.lock')), - if (_alsoCleanNinja) - environment.outDir, - if(_alsoCleanFlutterRepo) - environment.engineDartToolDir, + if (_alsoCleanNinja) environment.outDir, + if (_alsoCleanFlutterRepo) environment.engineDartToolDir, ]; await Future.wait( thingsToBeCleaned - .where((io.FileSystemEntity entity) => entity.existsSync()) - .map((io.FileSystemEntity entity) => entity.delete(recursive: true)) + .where((io.FileSystemEntity entity) => entity.existsSync()) + .map((io.FileSystemEntity entity) => entity.delete(recursive: true)), ); return true; } diff --git a/lib/web_ui/dev/common.dart b/lib/web_ui/dev/common.dart index ab6c9ea32ee85..c2c085dd889d7 100644 --- a/lib/web_ui/dev/common.dart +++ b/lib/web_ui/dev/common.dart @@ -103,8 +103,7 @@ class LinuxPlatformBinding extends PlatformBinding { String get chromePlatformString => 'linux64'; @override - String getChromeExecutablePath(io.Directory versionDir) => - path.join(versionDir.path, 'chrome'); + String getChromeExecutablePath(io.Directory versionDir) => path.join(versionDir.path, 'chrome'); @override String getFirefoxDownloadUrl(String version) => @@ -112,8 +111,7 @@ class LinuxPlatformBinding extends PlatformBinding { '${getFirefoxDownloadFilename(version)}'; @override - String getFirefoxDownloadFilename(String version) => - 'firefox-$version.tar.bz2'; + String getFirefoxDownloadFilename(String version) => 'firefox-$version.tar.bz2'; @override String getFirefoxExecutablePath(io.Directory versionDir) => @@ -124,12 +122,10 @@ class LinuxPlatformBinding extends PlatformBinding { 'https://download.mozilla.org/?product=firefox-latest&os=linux64&lang=en-US'; @override - String getMacApplicationLauncher() => - throw UnsupportedError('Safari is not supported on Linux'); + String getMacApplicationLauncher() => throw UnsupportedError('Safari is not supported on Linux'); @override - String getCommandToRunEdge() => - throw UnsupportedError('Edge is not supported on Linux'); + String getCommandToRunEdge() => throw UnsupportedError('Edge is not supported on Linux'); @override String get esbuildPlatformName => 'linux-x64'; @@ -138,12 +134,12 @@ class LinuxPlatformBinding extends PlatformBinding { abstract class MacPlatformBinding extends PlatformBinding { @override String getChromeExecutablePath(io.Directory versionDir) => path.join( - versionDir.path, - 'Google Chrome for Testing.app', - 'Contents', - 'MacOS', - 'Google Chrome for Testing', - ); + versionDir.path, + 'Google Chrome for Testing.app', + 'Contents', + 'MacOS', + 'Google Chrome for Testing', + ); @override String getFirefoxDownloadUrl(String version) => @@ -186,10 +182,7 @@ class Macx64PlatformBinding extends MacPlatformBinding { } class BrowserInstallation { - const BrowserInstallation({ - required this.version, - required this.executable, - }); + const BrowserInstallation({required this.version, required this.executable}); /// Browser version. final String version; @@ -225,20 +218,12 @@ const String kEdge = 'edge'; const String kFirefox = 'firefox'; const String kSafari = 'safari'; -const List kAllBrowserNames = [ - kChrome, - kEdge, - kFirefox, - kSafari, -]; +const List kAllBrowserNames = [kChrome, kEdge, kFirefox, kSafari]; /// Creates an environment for a browser. /// /// The [browserName] matches the browser name passed as the `--browser` option. -BrowserEnvironment getBrowserEnvironment( - BrowserName browserName, { - required bool useDwarf, -}) { +BrowserEnvironment getBrowserEnvironment(BrowserName browserName, {required bool useDwarf}) { switch (browserName) { case BrowserName.chrome: return ChromeEnvironment(useDwarf: useDwarf); diff --git a/lib/web_ui/dev/edge.dart b/lib/web_ui/dev/edge.dart index b27a401b42594..d65c0d3347441 100644 --- a/lib/web_ui/dev/edge.dart +++ b/lib/web_ui/dev/edge.dart @@ -49,26 +49,28 @@ class Edge extends Browser { /// Starts a new instance of Safari open to the given [url], which may be a /// [Uri] or a [String]. factory Edge(Uri url) { - return Edge._(BrowserProcess(() async { - final BrowserInstallation installation = await getEdgeInstallation( - packageLock.edgeLock.launcherVersion, - infoLog: DevNull(), - ); - - // Debug is not a valid option for Edge. Remove it. - String pathToOpen = url.toString(); - if(pathToOpen.contains('debug')) { - final int index = pathToOpen.indexOf('debug'); - pathToOpen = pathToOpen.substring(0, index-1); - } - - final Process process = await Process.start( - installation.executable, - [pathToOpen,'-k'], - ); - - return process; - })); + return Edge._( + BrowserProcess(() async { + final BrowserInstallation installation = await getEdgeInstallation( + packageLock.edgeLock.launcherVersion, + infoLog: DevNull(), + ); + + // Debug is not a valid option for Edge. Remove it. + String pathToOpen = url.toString(); + if (pathToOpen.contains('debug')) { + final int index = pathToOpen.indexOf('debug'); + pathToOpen = pathToOpen.substring(0, index - 1); + } + + final Process process = await Process.start(installation.executable, [ + pathToOpen, + '-k', + ]); + + return process; + }), + ); } Edge._(this._process); @@ -79,5 +81,5 @@ class Edge extends Browser { Future get onExit => _process.onExit; @override - Future close() => _process.close(); + Future close() => _process.close(); } diff --git a/lib/web_ui/dev/edge_installation.dart b/lib/web_ui/dev/edge_installation.dart index 2f6f893777270..a2b7165591d44 100644 --- a/lib/web_ui/dev/edge_installation.dart +++ b/lib/web_ui/dev/edge_installation.dart @@ -30,8 +30,9 @@ Future getEdgeInstallation( // In the future we can investigate to run them on Android or on MacOS. if (!io.Platform.isWindows) { throw UnimplementedError( - 'Tests for Edge on ${io.Platform.operatingSystem} is' - ' not supported.'); + 'Tests for Edge on ${io.Platform.operatingSystem} is' + ' not supported.', + ); } infoLog ??= io.stdout; @@ -46,16 +47,18 @@ Future getEdgeInstallation( } else { infoLog.writeln('Installing MicrosoftEdgeLauncher'); await edgeLauncher.install(); - infoLog.writeln( - 'Installations complete. To launch it run ${edgeLauncher.executable}'); + infoLog.writeln('Installations complete. To launch it run ${edgeLauncher.executable}'); } return BrowserInstallation( version: 'system', - executable: io.Directory(path.join( + executable: + io.Directory( + path.join( edgeLauncher.launcherInstallationDir.path, - PlatformBinding.instance.getCommandToRunEdge())) - .path, + PlatformBinding.instance.getCommandToRunEdge(), + ), + ).path, ); } else { infoLog.writeln('Unsupported version $requestedVersion.'); @@ -73,13 +76,11 @@ class EdgeLauncher { EdgeLauncher(); /// Path to the directory that contains `MicrosoftEdgeLauncher.exe`. - io.Directory get launcherInstallationDir => io.Directory( - path.join(environment.webUiDartToolDir.path, 'microsoftedgelauncher', - version), - ); + io.Directory get launcherInstallationDir => + io.Directory(path.join(environment.webUiDartToolDir.path, 'microsoftedgelauncher', version)); - io.File get executable => io.File( - path.join(launcherInstallationDir.path, 'MicrosoftEdgeLauncher.exe')); + io.File get executable => + io.File(path.join(launcherInstallationDir.path, 'MicrosoftEdgeLauncher.exe')); bool get isInstalled => executable.existsSync(); @@ -108,10 +109,9 @@ class EdgeLauncher { try { // Download executable from Github. - final StreamedResponse download = await client.send(Request( - 'GET', - Uri.parse(windowsEdgeLauncherDownloadUrl), - )); + final StreamedResponse download = await client.send( + Request('GET', Uri.parse(windowsEdgeLauncherDownloadUrl)), + ); await download.stream.pipe(executable.openWrite()); } finally { diff --git a/lib/web_ui/dev/environment.dart b/lib/web_ui/dev/environment.dart index 061994dfa3cb7..47a7255f8af34 100644 --- a/lib/web_ui/dev/environment.dart +++ b/lib/web_ui/dev/environment.dart @@ -24,32 +24,29 @@ class Environment { final io.File self = io.File.fromUri(io.Platform.script); final io.Directory engineSrcDir = self.parent.parent.parent.parent.parent; - final io.Directory engineToolsDir = - io.Directory(pathlib.join(engineSrcDir.path, 'flutter', 'tools')); - final io.Directory outDir = - io.Directory(pathlib.join(engineSrcDir.path, 'out')); - final io.Directory wasmReleaseOutDir = - io.Directory(pathlib.join(outDir.path, 'wasm_release')); - final io.Directory wasmProfileOutDir = - io.Directory(pathlib.join(outDir.path, 'wasm_profile')); - final io.Directory wasmDebugUnoptOutDir = - io.Directory(pathlib.join(outDir.path, 'wasm_debug_unopt')); - final io.Directory hostDebugUnoptDir = - io.Directory(pathlib.join(outDir.path, 'host_debug_unopt')); + final io.Directory engineToolsDir = io.Directory( + pathlib.join(engineSrcDir.path, 'flutter', 'tools'), + ); + final io.Directory outDir = io.Directory(pathlib.join(engineSrcDir.path, 'out')); + final io.Directory wasmReleaseOutDir = io.Directory(pathlib.join(outDir.path, 'wasm_release')); + final io.Directory wasmProfileOutDir = io.Directory(pathlib.join(outDir.path, 'wasm_profile')); + final io.Directory wasmDebugUnoptOutDir = io.Directory( + pathlib.join(outDir.path, 'wasm_debug_unopt'), + ); + final io.Directory hostDebugUnoptDir = io.Directory( + pathlib.join(outDir.path, 'host_debug_unopt'), + ); final io.Directory dartSdkDir = dartExecutable.parent.parent; final io.Directory webUiRootDir = io.Directory( - pathlib.join(engineSrcDir.path, 'flutter', 'lib', 'web_ui')); + pathlib.join(engineSrcDir.path, 'flutter', 'lib', 'web_ui'), + ); - for (final io.Directory expectedDirectory in [ - engineSrcDir, - webUiRootDir - ]) { + for (final io.Directory expectedDirectory in [engineSrcDir, webUiRootDir]) { if (!expectedDirectory.existsSync()) { throw ToolExit('$expectedDirectory does not exist.'); } } - return Environment._( self: self, isMacosArm: isMacosArm, @@ -126,7 +123,8 @@ class Environment { String get pubExecutable => pathlib.join(dartSdkDir.path, 'bin', 'pub'); /// The path to dart2wasm pre-compiled snapshot - String get dart2wasmSnapshotPath => pathlib.join(dartSdkDir.path, 'bin', 'snapshots', 'dart2wasm_product.snapshot'); + String get dart2wasmSnapshotPath => + pathlib.join(dartSdkDir.path, 'bin', 'snapshots', 'dart2wasm_product.snapshot'); /// The path to dart2wasm.dart file String get dart2wasmScriptPath => pathlib.join( @@ -136,81 +134,51 @@ class Environment { 'pkg', 'dart2wasm', 'bin', - 'dart2wasm.dart' + 'dart2wasm.dart', ); /// Path to where github.com/flutter/engine is checked out inside the engine workspace. - io.Directory get flutterDirectory => - io.Directory(pathlib.join(engineSrcDir.path, 'flutter')); - io.Directory get webSdkRootDir => io.Directory(pathlib.join( - flutterDirectory.path, - 'web_sdk', - )); + io.Directory get flutterDirectory => io.Directory(pathlib.join(engineSrcDir.path, 'flutter')); + io.Directory get webSdkRootDir => io.Directory(pathlib.join(flutterDirectory.path, 'web_sdk')); /// Path to the "web_engine_tester" package. - io.Directory get webEngineTesterRootDir => io.Directory(pathlib.join( - webSdkRootDir.path, - 'web_engine_tester', - )); + io.Directory get webEngineTesterRootDir => + io.Directory(pathlib.join(webSdkRootDir.path, 'web_engine_tester')); /// Path to the "build" directory, generated by "package:build_runner". /// /// This is where compiled test output goes. - io.Directory get webUiBuildDir => io.Directory(pathlib.join( - outDir.path, - 'web_tests', - )); + io.Directory get webUiBuildDir => io.Directory(pathlib.join(outDir.path, 'web_tests')); - io.Directory get webTestsArtifactsDir => io.Directory(pathlib.join( - webUiBuildDir.path, - 'artifacts', - )); + io.Directory get webTestsArtifactsDir => + io.Directory(pathlib.join(webUiBuildDir.path, 'artifacts')); /// Path to the ".dart_tool" directory, generated by various Dart tools. - io.Directory get webUiDartToolDir => io.Directory(pathlib.join( - webUiRootDir.path, - '.dart_tool', - )); + io.Directory get webUiDartToolDir => io.Directory(pathlib.join(webUiRootDir.path, '.dart_tool')); /// Path to the ".dart_tool" directory living under `engine/src/flutter`. /// /// This is a designated area for tool downloads which can be used by /// multiple platforms. For exampe: Flutter repo for e2e tests. - io.Directory get engineDartToolDir => io.Directory(pathlib.join( - engineSrcDir.path, - 'flutter', - '.dart_tool', - )); + io.Directory get engineDartToolDir => + io.Directory(pathlib.join(engineSrcDir.path, 'flutter', '.dart_tool')); /// Path to the "dev" directory containing engine developer tools and /// configuration files. - io.Directory get webUiDevDir => io.Directory(pathlib.join( - webUiRootDir.path, - 'dev', - )); + io.Directory get webUiDevDir => io.Directory(pathlib.join(webUiRootDir.path, 'dev')); /// Path to the "test" directory containing web engine tests. - io.Directory get webUiTestDir => io.Directory(pathlib.join( - webUiRootDir.path, - 'test', - )); + io.Directory get webUiTestDir => io.Directory(pathlib.join(webUiRootDir.path, 'test')); /// Path to the "lib" directory containing web engine code. - io.Directory get webUiLibDir => io.Directory(pathlib.join( - webUiRootDir.path, - 'lib', - )); + io.Directory get webUiLibDir => io.Directory(pathlib.join(webUiRootDir.path, 'lib')); /// Path to the base directory to be used by Skia Gold. - io.Directory get webUiSkiaGoldDirectory => io.Directory(pathlib.join( - webUiDartToolDir.path, - 'skia_gold', - )); + io.Directory get webUiSkiaGoldDirectory => + io.Directory(pathlib.join(webUiDartToolDir.path, 'skia_gold')); /// Directory to add test results which would later be uploaded to a gcs /// bucket by LUCI. - io.Directory get webUiTestResultsDirectory => io.Directory(pathlib.join( - webUiDartToolDir.path, - 'test_results', - )); + io.Directory get webUiTestResultsDirectory => + io.Directory(pathlib.join(webUiDartToolDir.path, 'test_results')); } diff --git a/lib/web_ui/dev/exceptions.dart b/lib/web_ui/dev/exceptions.dart index 319eae17c261d..df81222660d8e 100644 --- a/lib/web_ui/dev/exceptions.dart +++ b/lib/web_ui/dev/exceptions.dart @@ -14,7 +14,7 @@ class BrowserInstallerException implements Exception { /// Throw this exception in felt command to exit felt with a message and a /// non-zero exit code. class ToolExit implements Exception { - ToolExit(this.message, { this.exitCode = 1 }); + ToolExit(this.message, {this.exitCode = 1}); final String message; final int exitCode; diff --git a/lib/web_ui/dev/felt.dart b/lib/web_ui/dev/felt.dart index 1e89cdb5fe165..9126355789718 100644 --- a/lib/web_ui/dev/felt.dart +++ b/lib/web_ui/dev/felt.dart @@ -16,17 +16,15 @@ import 'roll_fallback_fonts.dart'; import 'test_runner.dart'; import 'utils.dart'; -CommandRunner runner = CommandRunner( - 'felt', - 'Command-line utility for building and testing Flutter web engine.', -) - ..addCommand(AnalyzeCommand()) - ..addCommand(BuildCommand()) - ..addCommand(CleanCommand()) - ..addCommand(RollFallbackFontsCommand()) - ..addCommand(GenerateBuilderJsonCommand()) - ..addCommand(LicensesCommand()) - ..addCommand(TestCommand()); +CommandRunner runner = + CommandRunner('felt', 'Command-line utility for building and testing Flutter web engine.') + ..addCommand(AnalyzeCommand()) + ..addCommand(BuildCommand()) + ..addCommand(CleanCommand()) + ..addCommand(RollFallbackFontsCommand()) + ..addCommand(GenerateBuilderJsonCommand()) + ..addCommand(LicensesCommand()) + ..addCommand(TestCommand()); Future main(List rawArgs) async { // Remove --clean from the list as that's processed by the wrapper script. @@ -54,10 +52,12 @@ Future main(List rawArgs) async { io.stderr.writeln(exception.message); exitCode = exception.exitCode; } on ProcessException catch (e) { - io.stderr.writeln('description: ${e.description}' - 'executable: ${e.executable} ' - 'arguments: ${e.arguments} ' - 'exit code: ${e.exitCode}'); + io.stderr.writeln( + 'description: ${e.description}' + 'executable: ${e.executable} ' + 'arguments: ${e.arguments} ' + 'exit code: ${e.exitCode}', + ); exitCode = e.exitCode ?? 1; } catch (e) { rethrow; diff --git a/lib/web_ui/dev/felt_config.dart b/lib/web_ui/dev/felt_config.dart index 047c6612c1dd8..8cb3cb2862c7b 100644 --- a/lib/web_ui/dev/felt_config.dart +++ b/lib/web_ui/dev/felt_config.dart @@ -5,16 +5,9 @@ import 'dart:io' as io; import 'package:yaml/yaml.dart'; -enum Compiler { - dart2js, - dart2wasm -} +enum Compiler { dart2js, dart2wasm } -enum Renderer { - html, - canvaskit, - skwasm, -} +enum Renderer { html, canvaskit, skwasm } class CompileConfiguration { CompileConfiguration(this.name, this.compiler, this.renderer); @@ -39,17 +32,9 @@ class TestBundle { final List compileConfigs; } -enum CanvasKitVariant { - full, - chromium, -} +enum CanvasKitVariant { full, chromium } -enum BrowserName { - chrome, - edge, - firefox, - safari, -} +enum BrowserName { chrome, edge, firefox, safari } class RunConfiguration { RunConfiguration( @@ -57,7 +42,7 @@ class RunConfiguration { this.browser, this.variant, this.crossOriginIsolated, - this.forceSingleThreadedSkwasm + this.forceSingleThreadedSkwasm, ); final String name; @@ -71,18 +56,15 @@ class ArtifactDependencies { ArtifactDependencies({ required this.canvasKit, required this.canvasKitChromium, - required this.skwasm + required this.skwasm, }); - ArtifactDependencies.none() : - canvasKit = false, - canvasKitChromium = false, - skwasm = false; + ArtifactDependencies.none() : canvasKit = false, canvasKitChromium = false, skwasm = false; final bool canvasKit; final bool canvasKitChromium; final bool skwasm; - ArtifactDependencies operator|(ArtifactDependencies other) { + ArtifactDependencies operator |(ArtifactDependencies other) { return ArtifactDependencies( canvasKit: canvasKit || other.canvasKit, canvasKitChromium: canvasKitChromium || other.canvasKitChromium, @@ -90,7 +72,7 @@ class ArtifactDependencies { ); } - ArtifactDependencies operator&(ArtifactDependencies other) { + ArtifactDependencies operator &(ArtifactDependencies other) { return ArtifactDependencies( canvasKit: canvasKit && other.canvasKit, canvasKitChromium: canvasKitChromium && other.canvasKitChromium, @@ -100,12 +82,7 @@ class ArtifactDependencies { } class TestSuite { - TestSuite( - this.name, - this.testBundle, - this.runConfig, - this.artifactDependencies - ); + TestSuite(this.name, this.testBundle, this.runConfig, this.artifactDependencies); String name; TestBundle testBundle; @@ -163,16 +140,19 @@ class FeltConfig { final String testSetName = testBundleYaml['test-set'] as String; final TestSet? testSet = testSetsByName[testSetName]; if (testSet == null) { - throw AssertionError('Test set not found with name: `$testSetName` (referenced by test bundle: `$name`)'); + throw AssertionError( + 'Test set not found with name: `$testSetName` (referenced by test bundle: `$name`)', + ); } final dynamic compileConfigsValue = testBundleYaml['compile-configs']; final List compileConfigs; if (compileConfigsValue is String) { compileConfigs = [compileConfigsByName[compileConfigsValue]!]; } else { - compileConfigs = (compileConfigsValue as List).map( - (dynamic configName) => compileConfigsByName[configName as String]! - ).toList(); + compileConfigs = + (compileConfigsValue as List) + .map((dynamic configName) => compileConfigsByName[configName as String]!) + .toList(); } final TestBundle bundle = TestBundle(name, testSet, compileConfigs); testBundles.add(bundle); @@ -189,17 +169,17 @@ class FeltConfig { final String name = runConfigYaml['name'] as String; final BrowserName browser = BrowserName.values.byName(runConfigYaml['browser'] as String); final dynamic variantNode = runConfigYaml['canvaskit-variant']; - final CanvasKitVariant? variant = variantNode == null - ? null - : CanvasKitVariant.values.byName(variantNode as String); + final CanvasKitVariant? variant = + variantNode == null ? null : CanvasKitVariant.values.byName(variantNode as String); final bool crossOriginIsolated = runConfigYaml['cross-origin-isolated'] as bool? ?? false; - final bool forceSingleThreadedSkwasm = runConfigYaml['force-single-threaded-skwasm'] as bool? ?? false; + final bool forceSingleThreadedSkwasm = + runConfigYaml['force-single-threaded-skwasm'] as bool? ?? false; final RunConfiguration runConfig = RunConfiguration( name, browser, variant, crossOriginIsolated, - forceSingleThreadedSkwasm + forceSingleThreadedSkwasm, ); runConfigs.add(runConfig); if (runConfigsByName.containsKey(name)) { @@ -215,12 +195,16 @@ class FeltConfig { final String testBundleName = testSuiteYaml['test-bundle'] as String; final TestBundle? bundle = testBundlesByName[testBundleName]; if (bundle == null) { - throw AssertionError('Test bundle not found with name: `$testBundleName` (referenced by test suite: `$name`)'); + throw AssertionError( + 'Test bundle not found with name: `$testBundleName` (referenced by test suite: `$name`)', + ); } final String runConfigName = testSuiteYaml['run-config'] as String; final RunConfiguration? runConfig = runConfigsByName[runConfigName]; if (runConfig == null) { - throw AssertionError('Run config not found with name: `$runConfigName` (referenced by test suite: `$name`)'); + throw AssertionError( + 'Run config not found with name: `$runConfigName` (referenced by test suite: `$name`)', + ); } bool canvasKit = false; bool canvasKitChromium = false; @@ -252,7 +236,7 @@ class FeltConfig { final ArtifactDependencies artifactDeps = ArtifactDependencies( canvasKit: canvasKit, canvasKitChromium: canvasKitChromium, - skwasm: skwasm + skwasm: skwasm, ); final TestSuite suite = TestSuite(name, bundle, runConfig, artifactDeps); testSuites.add(suite); diff --git a/lib/web_ui/dev/firefox.dart b/lib/web_ui/dev/firefox.dart index 2495456019d39..17dc298dc8bdf 100644 --- a/lib/web_ui/dev/firefox.dart +++ b/lib/web_ui/dev/firefox.dart @@ -59,54 +59,58 @@ class Firefox extends Browser { /// [Uri] or a [String]. factory Firefox(Uri url, BrowserInstallation installation, {bool debug = false}) { final Completer remoteDebuggerCompleter = Completer.sync(); - return Firefox._(BrowserProcess(() async { - // Using a profile on opening will prevent popups related to profiles. - const String profile = ''' + return Firefox._( + BrowserProcess(() async { + // Using a profile on opening will prevent popups related to profiles. + const String profile = ''' user_pref("browser.shell.checkDefaultBrowser", false); user_pref("dom.disable_open_during_load", false); user_pref("dom.max_script_run_time", 0); '''; - final Directory temporaryProfileDirectory = Directory( - path.join(environment.webUiDartToolDir.path, 'firefox_profile')); - - // A good source of various Firefox Command Line options: - // https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#Browser - // - if (temporaryProfileDirectory.existsSync()) { - temporaryProfileDirectory.deleteSync(recursive: true); - } - temporaryProfileDirectory.createSync(recursive: true); - File(path.join(temporaryProfileDirectory.path, 'prefs.js')) - .writeAsStringSync(profile); - - final bool isMac = Platform.isMacOS; - final List args = [ - url.toString(), - '--profile', - temporaryProfileDirectory.path, - if (!debug) - '--headless', - '-width $kMaxScreenshotWidth', - '-height $kMaxScreenshotHeight', - // On Mac Firefox uses the -- option prefix, while elsewhere it uses the - prefix. - '${isMac ? '-' : ''}-new-window', - '${isMac ? '-' : ''}-new-instance', - '--start-debugger-server $kDevtoolsPort', - ]; - - final Process process = - await Process.start(installation.executable, args); - - remoteDebuggerCompleter.complete( - getRemoteDebuggerUrl(Uri.parse('http://localhost:$kDevtoolsPort'))); - - unawaited(process.exitCode.then((_) { - temporaryProfileDirectory.deleteSync(recursive: true); - })); - - return process; - }), remoteDebuggerCompleter.future); + final Directory temporaryProfileDirectory = Directory( + path.join(environment.webUiDartToolDir.path, 'firefox_profile'), + ); + + // A good source of various Firefox Command Line options: + // https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#Browser + // + if (temporaryProfileDirectory.existsSync()) { + temporaryProfileDirectory.deleteSync(recursive: true); + } + temporaryProfileDirectory.createSync(recursive: true); + File(path.join(temporaryProfileDirectory.path, 'prefs.js')).writeAsStringSync(profile); + + final bool isMac = Platform.isMacOS; + final List args = [ + url.toString(), + '--profile', + temporaryProfileDirectory.path, + if (!debug) '--headless', + '-width $kMaxScreenshotWidth', + '-height $kMaxScreenshotHeight', + // On Mac Firefox uses the -- option prefix, while elsewhere it uses the - prefix. + '${isMac ? '-' : ''}-new-window', + '${isMac ? '-' : ''}-new-instance', + '--start-debugger-server $kDevtoolsPort', + ]; + + final Process process = await Process.start(installation.executable, args); + + remoteDebuggerCompleter.complete( + getRemoteDebuggerUrl(Uri.parse('http://localhost:$kDevtoolsPort')), + ); + + unawaited( + process.exitCode.then((_) { + temporaryProfileDirectory.deleteSync(recursive: true); + }), + ); + + return process; + }), + remoteDebuggerCompleter.future, + ); } Firefox._(this._process, this.remoteDebuggerUrl); diff --git a/lib/web_ui/dev/firefox_installer.dart b/lib/web_ui/dev/firefox_installer.dart index e86433f93d3b1..d4fa0e5d4d3ea 100644 --- a/lib/web_ui/dev/firefox_installer.dart +++ b/lib/web_ui/dev/firefox_installer.dart @@ -32,8 +32,10 @@ Future getOrInstallFirefox( // These tests are aimed to run only on the Linux containers in Cirrus. // Therefore Firefox installation is implemented only for Linux now. if (!io.Platform.isLinux && !io.Platform.isMacOS) { - throw UnimplementedError('Firefox Installer is only supported on Linux ' - 'and Mac operating systems'); + throw UnimplementedError( + 'Firefox Installer is only supported on Linux ' + 'and Mac operating systems', + ); } infoLog ??= io.stdout; @@ -42,8 +44,10 @@ Future getOrInstallFirefox( // bot will download Firefox from CIPD and place it in a cache and set the // environment variable FIREFOX_EXECUTABLE. if (io.Platform.environment.containsKey(_firefoxExecutableVar)) { - infoLog.writeln('Using Firefox from $_firefoxExecutableVar variable: ' - '${io.Platform.environment[_firefoxExecutableVar]}'); + infoLog.writeln( + 'Using Firefox from $_firefoxExecutableVar variable: ' + '${io.Platform.environment[_firefoxExecutableVar]}', + ); return BrowserInstallation( version: 'cipd', executable: io.Platform.environment[_firefoxExecutableVar]!, @@ -51,27 +55,25 @@ Future getOrInstallFirefox( } if (requestedVersion == 'system') { - return BrowserInstallation( - version: 'system', - executable: await _findSystemFirefoxExecutable(), - ); + return BrowserInstallation(version: 'system', executable: await _findSystemFirefoxExecutable()); } FirefoxInstaller? installer; try { - installer = requestedVersion == 'latest' - ? await FirefoxInstaller.latest() - : FirefoxInstaller(version: requestedVersion); + installer = + requestedVersion == 'latest' + ? await FirefoxInstaller.latest() + : FirefoxInstaller(version: requestedVersion); if (installer.isInstalled) { infoLog.writeln( - 'Installation was skipped because Firefox version ${installer.version} is already installed.'); + 'Installation was skipped because Firefox version ${installer.version} is already installed.', + ); } else { infoLog.writeln('Installing Firefox version: ${installer.version}'); await installer.install(); final BrowserInstallation installation = installer.getInstallation()!; - infoLog.writeln( - 'Installations complete. To launch it run ${installation.executable}'); + infoLog.writeln('Installations complete. To launch it run ${installation.executable}'); } return installer.getInstallation()!; } finally { @@ -81,23 +83,21 @@ Future getOrInstallFirefox( /// Manages the installation of a particular [version] of Firefox. class FirefoxInstaller { - factory FirefoxInstaller({ - required String version, - }) { + factory FirefoxInstaller({required String version}) { if (version == 'system') { throw BrowserInstallerException( - 'Cannot install system version of Firefox. System Firefox must be installed manually.'); + 'Cannot install system version of Firefox. System Firefox must be installed manually.', + ); } if (version == 'latest') { throw BrowserInstallerException( - 'Expected a concrete Firefox version, but got $version. Maybe use FirefoxInstaller.latest()?'); + 'Expected a concrete Firefox version, but got $version. Maybe use FirefoxInstaller.latest()?', + ); } final io.Directory firefoxInstallationDir = io.Directory( path.join(environment.webUiDartToolDir.path, 'firefox'), ); - final io.Directory versionDir = io.Directory( - path.join(firefoxInstallationDir.path, version), - ); + final io.Directory versionDir = io.Directory(path.join(firefoxInstallationDir.path, version)); return FirefoxInstaller._( version: version, firefoxInstallationDir: firefoxInstallationDir, @@ -112,9 +112,10 @@ class FirefoxInstaller { }); static Future latest() async { - final String latestVersion = io.Platform.isLinux - ? await fetchLatestFirefoxVersionLinux() - : await fetchLatestFirefoxVersionMacOS(); + final String latestVersion = + io.Platform.isLinux + ? await fetchLatestFirefoxVersionLinux() + : await fetchLatestFirefoxVersionMacOS(); return FirefoxInstaller(version: latestVersion); } @@ -165,13 +166,11 @@ class FirefoxInstaller { versionDir.createSync(recursive: true); final String url = PlatformBinding.instance.getFirefoxDownloadUrl(version); - final StreamedResponse download = await client.send(Request( - 'GET', - Uri.parse(url), - )); + final StreamedResponse download = await client.send(Request('GET', Uri.parse(url))); - final io.File downloadedFile = - io.File(path.join(versionDir.path, PlatformBinding.instance.getFirefoxDownloadFilename(version))); + final io.File downloadedFile = io.File( + path.join(versionDir.path, PlatformBinding.instance.getFirefoxDownloadFilename(version)), + ); final io.IOSink sink = downloadedFile.openWrite(); await download.stream.pipe(sink); await sink.flush(); @@ -194,8 +193,9 @@ class FirefoxInstaller { if (unzipResult.exitCode != 0) { throw BrowserInstallerException( - 'Failed to unzip the downloaded Firefox archive ${downloadedFile.path}.\n' - 'The unzip process exited with code ${unzipResult.exitCode}.'); + 'Failed to unzip the downloaded Firefox archive ${downloadedFile.path}.\n' + 'The unzip process exited with code ${unzipResult.exitCode}.', + ); } } @@ -214,10 +214,11 @@ class FirefoxInstaller { ]); if (installResult.exitCode != 0) { throw BrowserInstallerException( - 'Failed to copy Firefox disk image contents from ' - '$sourcePath to $targetPath.\n' - 'Exit code ${installResult.exitCode}.\n' - '${installResult.stderr}'); + 'Failed to copy Firefox disk image contents from ' + '$sourcePath to $targetPath.\n' + 'Exit code ${installResult.exitCode}.\n' + '${installResult.stderr}', + ); } } finally { await _hdiUtilUnmount(volumeName); @@ -232,16 +233,18 @@ class FirefoxInstaller { ]); if (mountResult.exitCode != 0) { throw BrowserInstallerException( - 'Failed to mount Firefox disk image ${dmgFile.path}.\n' - 'Exit code ${mountResult.exitCode}.\n${mountResult.stderr}'); + 'Failed to mount Firefox disk image ${dmgFile.path}.\n' + 'Exit code ${mountResult.exitCode}.\n${mountResult.stderr}', + ); } final List processOutput = (mountResult.stdout as String).split('\n'); final String? volumePath = _volumeFromMountResult(processOutput); if (volumePath == null) { throw BrowserInstallerException( - 'Failed to parse mount dmg result ${processOutput.join('\n')}.\n' - 'Expected /Volumes/{volume name}'); + 'Failed to parse mount dmg result ${processOutput.join('\n')}.\n' + 'Expected /Volumes/{volume name}', + ); } return volumePath; } @@ -265,8 +268,9 @@ class FirefoxInstaller { ]); if (unmountResult.exitCode != 0) { throw BrowserInstallerException( - 'Failed to unmount Firefox disk image $volumeName.\n' - 'Exit code ${unmountResult.exitCode}. ${unmountResult.stderr}'); + 'Failed to unmount Firefox disk image $volumeName.\n' + 'Exit code ${unmountResult.exitCode}. ${unmountResult.stderr}', + ); } } @@ -276,18 +280,14 @@ class FirefoxInstaller { } Future _findSystemFirefoxExecutable() async { - final io.ProcessResult which = - await io.Process.run('which', ['firefox']); + final io.ProcessResult which = await io.Process.run('which', ['firefox']); final bool found = which.exitCode != 0; - const String fireFoxDefaultInstallPath = - '/Applications/Firefox.app/Contents/MacOS/firefox'; + const String fireFoxDefaultInstallPath = '/Applications/Firefox.app/Contents/MacOS/firefox'; if (!found) { - if (io.Platform.isMacOS && - io.File(fireFoxDefaultInstallPath).existsSync()) { + if (io.Platform.isMacOS && io.File(fireFoxDefaultInstallPath).existsSync()) { return Future.value(fireFoxDefaultInstallPath); } - throw BrowserInstallerException( - 'Failed to locate system Firefox installation.'); + throw BrowserInstallerException('Failed to locate system Firefox installation.'); } return which.stdout as String; } @@ -295,8 +295,9 @@ Future _findSystemFirefoxExecutable() async { /// Fetches the latest available Firefox build version on Linux. Future fetchLatestFirefoxVersionLinux() async { final RegExp forFirefoxVersion = RegExp('firefox-[0-9.]+[0-9]'); - final io.HttpClientRequest request = await io.HttpClient() - .getUrl(Uri.parse(PlatformBinding.instance.getFirefoxLatestVersionUrl())); + final io.HttpClientRequest request = await io.HttpClient().getUrl( + Uri.parse(PlatformBinding.instance.getFirefoxLatestVersionUrl()), + ); request.followRedirects = false; // We will parse the HttpHeaders to find the redirect location. final io.HttpClientResponse response = await request.close(); @@ -310,8 +311,9 @@ Future fetchLatestFirefoxVersionLinux() async { /// Fetches the latest available Firefox build version on Mac OS. Future fetchLatestFirefoxVersionMacOS() async { final RegExp forFirefoxVersion = RegExp('firefox/releases/[0-9.]+[0-9]'); - final io.HttpClientRequest request = await io.HttpClient() - .getUrl(Uri.parse(PlatformBinding.instance.getFirefoxLatestVersionUrl())); + final io.HttpClientRequest request = await io.HttpClient().getUrl( + Uri.parse(PlatformBinding.instance.getFirefoxLatestVersionUrl()), + ); request.followRedirects = false; // We will parse the HttpHeaders to find the redirect location. final io.HttpClientResponse response = await request.close(); @@ -324,19 +326,21 @@ Future fetchLatestFirefoxVersionMacOS() async { Future getInstaller({String requestedVersion = 'latest'}) async { FirefoxInstaller? installer; try { - installer = requestedVersion == 'latest' - ? await FirefoxInstaller.latest() - : FirefoxInstaller(version: requestedVersion); + installer = + requestedVersion == 'latest' + ? await FirefoxInstaller.latest() + : FirefoxInstaller(version: requestedVersion); if (installer.isInstalled) { - print('Installation was skipped because Firefox version ' - '${installer.version} is already installed.'); + print( + 'Installation was skipped because Firefox version ' + '${installer.version} is already installed.', + ); } else { print('Installing Firefox version: ${installer.version}'); await installer.install(); final BrowserInstallation installation = installer.getInstallation()!; - print( - 'Installations complete. To launch it run ${installation.executable}'); + print('Installations complete. To launch it run ${installation.executable}'); } return installer.getInstallation()!; } finally { diff --git a/lib/web_ui/dev/generate_builder_json.dart b/lib/web_ui/dev/generate_builder_json.dart index d4263e423b357..05a2e33847c16 100644 --- a/lib/web_ui/dev/generate_builder_json.dart +++ b/lib/web_ui/dev/generate_builder_json.dart @@ -16,8 +16,8 @@ import 'package_lock.dart'; class GenerateBuilderJsonCommand extends Command { @override String get description => - 'Generates JSON for the engine_v2 builders to build and copy all artifacts, ' - 'compile all test bundles, and run all test suites on all platforms.'; + 'Generates JSON for the engine_v2 builders to build and copy all artifacts, ' + 'compile all test bundles, and run all test suites on all platforms.'; @override String get name => 'generate-builder-json'; @@ -26,15 +26,12 @@ class GenerateBuilderJsonCommand extends Command { FutureOr? run() { final PackageLock packageLock = PackageLock(); final FeltConfig config = FeltConfig.fromFile( - path.join(environment.webUiTestDir.path, 'felt_config.yaml') + path.join(environment.webUiTestDir.path, 'felt_config.yaml'), ); final String configString = generate(config, packageLock); - final io.File configFile = io.File(path.join( - environment.flutterDirectory.path, - 'ci', - 'builders', - 'linux_web_engine.json', - )); + final io.File configFile = io.File( + path.join(environment.flutterDirectory.path, 'ci', 'builders', 'linux_web_engine.json'), + ); configFile.writeAsStringSync('$configString\n'); return true; } @@ -45,10 +42,9 @@ class GenerateBuilderJsonCommand extends Command { '_comment2': 'See `generate_builder_json.dart` for the generator code', 'builds': [ _getArtifactBuildStep(), - for (final TestBundle bundle in config.testBundles) - _getBundleBuildStep(bundle), + for (final TestBundle bundle in config.testBundles) _getBundleBuildStep(bundle), ], - 'tests': _getAllTestSteps(config.testSuites, packageLock) + 'tests': _getAllTestSteps(config.testSuites, packageLock), }; return const JsonEncoder.withIndent(' ').convert(outputJson); } @@ -56,64 +52,44 @@ class GenerateBuilderJsonCommand extends Command { Map _getArtifactBuildStep() { return { 'name': 'web_tests/artifacts', - 'drone_dimensions': [ - 'device_type=none', - 'os=Linux', - 'cores=32' - ], + 'drone_dimensions': ['device_type=none', 'os=Linux', 'cores=32'], 'gclient_variables': { 'download_android_deps': false, 'download_jdk': false, 'download_emsdk': true, }, - 'gn': [ - '--web', - '--runtime-mode=release', - '--no-goma', - ], + 'gn': ['--web', '--runtime-mode=release', '--no-goma'], 'ninja': { 'config': 'wasm_release', - 'targets': [ - 'flutter/web_sdk:flutter_web_sdk_archive' - ] + 'targets': ['flutter/web_sdk:flutter_web_sdk_archive'], }, 'archives': [ { 'name': 'wasm_release', 'base_path': 'out/wasm_release/zip_archives/', 'type': 'gcs', - 'include_paths': [ - 'out/wasm_release/zip_archives/flutter-web-sdk.zip' - ], + 'include_paths': ['out/wasm_release/zip_archives/flutter-web-sdk.zip'], 'realm': 'production', - } + }, ], 'generators': { 'tasks': [ { 'name': 'check licenses', - 'parameters': [ - 'check-licenses' - ], - 'scripts': [ 'flutter/lib/web_ui/dev/felt' ], - + 'parameters': ['check-licenses'], + 'scripts': ['flutter/lib/web_ui/dev/felt'], }, { 'name': 'web engine analysis', - 'parameters': [ - 'analyze' - ], - 'scripts': [ 'flutter/lib/web_ui/dev/felt' ], + 'parameters': ['analyze'], + 'scripts': ['flutter/lib/web_ui/dev/felt'], }, { 'name': 'copy artifacts for web tests', - 'parameters': [ - 'test', - '--copy-artifacts', - ], - 'scripts': [ 'flutter/lib/web_ui/dev/felt' ], + 'parameters': ['test', '--copy-artifacts'], + 'scripts': ['flutter/lib/web_ui/dev/felt'], }, - ] + ], }, }; } @@ -121,22 +97,15 @@ class GenerateBuilderJsonCommand extends Command { Map _getBundleBuildStep(TestBundle bundle) { return { 'name': 'web_tests/test_bundles/${bundle.name}', - 'drone_dimensions': [ - 'device_type=none', - 'os=Linux', - ], + 'drone_dimensions': ['device_type=none', 'os=Linux'], 'generators': { 'tasks': [ { 'name': 'compile bundle ${bundle.name}', - 'parameters': [ - 'test', - '--compile', - '--bundle=${bundle.name}', - ], - 'scripts': [ 'flutter/lib/web_ui/dev/felt' ], - } - ] + 'parameters': ['test', '--compile', '--bundle=${bundle.name}'], + 'scripts': ['flutter/lib/web_ui/dev/felt'], + }, + ], }, }; } @@ -145,8 +114,15 @@ class GenerateBuilderJsonCommand extends Command { return [ _getTestStepForPlatformAndBrowser(suites, packageLock, 'Linux', BrowserName.chrome), _getTestStepForPlatformAndBrowser(suites, packageLock, 'Linux', BrowserName.firefox), - _getTestStepForPlatformAndBrowser(suites, packageLock, 'Mac', BrowserName.safari, specificOS: 'Mac-13', cpu: 'arm64'), - _getTestStepForPlatformAndBrowser(suites, packageLock, 'Windows', BrowserName.chrome) + _getTestStepForPlatformAndBrowser( + suites, + packageLock, + 'Mac', + BrowserName.safari, + specificOS: 'Mac-13', + cpu: 'arm64', + ), + _getTestStepForPlatformAndBrowser(suites, packageLock, 'Windows', BrowserName.chrome), ]; } @@ -168,10 +144,7 @@ class GenerateBuilderJsonCommand extends Command { 'os=${specificOS ?? platform}', if (cpu != null) 'cpu=$cpu', ], - 'gclient_variables': { - 'download_android_deps': false, - 'download_jdk': false, - }, + 'gclient_variables': {'download_android_deps': false, 'download_jdk': false}, 'dependencies': [ 'web_tests/artifacts', ...bundles.map((bundle) => 'web_tests/test_bundles/${bundle.name}'), @@ -190,17 +163,18 @@ class GenerateBuilderJsonCommand extends Command { { 'dependency': 'firefox', 'version': 'version:${packageLock.firefoxLock.version}', - } + }, ], - 'tasks': filteredSuites.map((suite) => { - 'name': 'run suite ${suite.name}', - 'parameters': [ - 'test', - '--run', - '--suite=${suite.name}', - ], - 'script': 'flutter/lib/web_ui/dev/felt', - }).toList(), + 'tasks': + filteredSuites + .map( + (suite) => { + 'name': 'run suite ${suite.name}', + 'parameters': ['test', '--run', '--suite=${suite.name}'], + 'script': 'flutter/lib/web_ui/dev/felt', + }, + ) + .toList(), }; } } diff --git a/lib/web_ui/dev/licenses.dart b/lib/web_ui/dev/licenses.dart index 8ce8090d628cd..91afec1b14556 100644 --- a/lib/web_ui/dev/licenses.dart +++ b/lib/web_ui/dev/licenses.dart @@ -23,25 +23,21 @@ class LicensesCommand extends Command { } void _checkLicenseHeaders() { - final List allSourceFiles = - _flatListSourceFiles(environment.webUiRootDir); - _expect(allSourceFiles.isNotEmpty, - 'Dart source listing of ${environment.webUiRootDir.path} must not be empty.'); + final List allSourceFiles = _flatListSourceFiles(environment.webUiRootDir); + _expect( + allSourceFiles.isNotEmpty, + 'Dart source listing of ${environment.webUiRootDir.path} must not be empty.', + ); - final List allDartPaths = - allSourceFiles.map((io.File f) => f.path).toList(); + final List allDartPaths = allSourceFiles.map((io.File f) => f.path).toList(); - for (final String expectedDirectory in const [ - 'lib', - 'test', - 'dev', - ]) { - final String expectedAbsoluteDirectory = - path.join(environment.webUiRootDir.path, expectedDirectory); + for (final String expectedDirectory in const ['lib', 'test', 'dev']) { + final String expectedAbsoluteDirectory = path.join( + environment.webUiRootDir.path, + expectedDirectory, + ); _expect( - allDartPaths - .where((String p) => p.startsWith(expectedAbsoluteDirectory)) - .isNotEmpty, + allDartPaths.where((String p) => p.startsWith(expectedAbsoluteDirectory)).isNotEmpty, 'Must include the $expectedDirectory/ directory', ); } @@ -50,8 +46,9 @@ class LicensesCommand extends Command { print('License headers OK!'); } - final RegExp _copyRegex = - RegExp(r'// Copyright 2013 The Flutter Authors\. All rights reserved\.'); + final RegExp _copyRegex = RegExp( + r'// Copyright 2013 The Flutter Authors\. All rights reserved\.', + ); void _expectLicenseHeader(io.File file) { final List head = file.readAsStringSync().split('\n').take(3).toList(); @@ -62,8 +59,7 @@ class LicensesCommand extends Command { 'Invalid first line of license header in file ${file.path}', ); _expect( - head[1] == - '// Use of this source code is governed by a BSD-style license that can be', + head[1] == '// Use of this source code is governed by a BSD-style license that can be', 'Invalid second line of license header in file ${file.path}', ); _expect( diff --git a/lib/web_ui/dev/package_lock.dart b/lib/web_ui/dev/package_lock.dart index 9eb517f83175e..78efc36357cb4 100644 --- a/lib/web_ui/dev/package_lock.dart +++ b/lib/web_ui/dev/package_lock.dart @@ -23,11 +23,11 @@ class PackageLock { return PackageLock._fromYaml(yaml); } - PackageLock._fromYaml(YamlMap yaml) : - chromeLock = ChromeLock._fromYaml(yaml['chrome'] as YamlMap), - firefoxLock = FirefoxLock._fromYaml(yaml['firefox'] as YamlMap), - edgeLock = EdgeLock._fromYaml(yaml['edge'] as YamlMap), - esbuildLock = EsbuildLock._fromYaml(yaml['esbuild'] as YamlMap); + PackageLock._fromYaml(YamlMap yaml) + : chromeLock = ChromeLock._fromYaml(yaml['chrome'] as YamlMap), + firefoxLock = FirefoxLock._fromYaml(yaml['firefox'] as YamlMap), + edgeLock = EdgeLock._fromYaml(yaml['edge'] as YamlMap), + esbuildLock = EsbuildLock._fromYaml(yaml['esbuild'] as YamlMap); final ChromeLock chromeLock; final FirefoxLock firefoxLock; @@ -36,30 +36,26 @@ class PackageLock { } class ChromeLock { - ChromeLock._fromYaml(YamlMap yaml) : - version = yaml['version'] as String; + ChromeLock._fromYaml(YamlMap yaml) : version = yaml['version'] as String; /// The full version of Chromium represented by this lock. E.g: '119.0.6045.9' final String version; } class FirefoxLock { - FirefoxLock._fromYaml(YamlMap yaml) : - version = yaml['version'] as String; + FirefoxLock._fromYaml(YamlMap yaml) : version = yaml['version'] as String; final String version; } class EdgeLock { - EdgeLock._fromYaml(YamlMap yaml) : - launcherVersion = yaml['launcher_version'] as String; + EdgeLock._fromYaml(YamlMap yaml) : launcherVersion = yaml['launcher_version'] as String; final String launcherVersion; } class EsbuildLock { - EsbuildLock._fromYaml(YamlMap yaml) : - version = yaml['version'] as String; + EsbuildLock._fromYaml(YamlMap yaml) : version = yaml['version'] as String; final String version; } diff --git a/lib/web_ui/dev/package_roller.dart b/lib/web_ui/dev/package_roller.dart index 4e0f19e47127b..fc438a92b77ec 100644 --- a/lib/web_ui/dev/package_roller.dart +++ b/lib/web_ui/dev/package_roller.dart @@ -13,20 +13,18 @@ import 'common.dart'; import 'package_lock.dart'; import 'utils.dart'; -final ArgParser _argParser = ArgParser(allowTrailingOptions: false) - ..addFlag( - 'dry-run', - help: 'Whether or not to push changes to CIPD. When --dry-run is set, the ' - 'script will download everything and attempt to prepare the bundle ' - 'but will stop before publishing. When not set, the bundle will be ' - 'published.', - negatable: false, - )..addFlag( - 'verbose', - abbr: 'v', - help: 'Enable verbose output.', - negatable: false, - ); +final ArgParser _argParser = + ArgParser(allowTrailingOptions: false) + ..addFlag( + 'dry-run', + help: + 'Whether or not to push changes to CIPD. When --dry-run is set, the ' + 'script will download everything and attempt to prepare the bundle ' + 'but will stop before publishing. When not set, the bundle will be ' + 'published.', + negatable: false, + ) + ..addFlag('verbose', abbr: 'v', help: 'Enable verbose output.', negatable: false); late final bool dryRun; late final bool verbose; @@ -138,14 +136,11 @@ class _PackageRoller { // Download a file from the internet, and put it in a temporary location. Future _downloadTemporaryFile(String url) async { // Use the hash of the Url to temporarily store a file under tmp - final io.File downloadedFile = io.File(path.join( - io.Directory.systemTemp.path, - 'download_${url.hashCode.toRadixString(16)}', - )); - vprint(' Downloading [$url] into [${downloadedFile.path}]'); - final StreamedResponse download = await _client.send( - Request('GET', Uri.parse(url)), + final io.File downloadedFile = io.File( + path.join(io.Directory.systemTemp.path, 'download_${url.hashCode.toRadixString(16)}'), ); + vprint(' Downloading [$url] into [${downloadedFile.path}]'); + final StreamedResponse download = await _client.send(Request('GET', Uri.parse(url))); await download.stream.pipe(downloadedFile.openWrite()); return downloadedFile; } @@ -154,9 +149,7 @@ class _PackageRoller { Future _unzipAndDeleteFile(io.File zipFile, io.Directory destination) async { vprint(' Unzipping [${zipFile.path}] into [$destination]'); await runProcess('unzip', [ - if (!verbose) ...[ - '-q', - ], + if (!verbose) ...['-q'], zipFile.path, '-d', destination.path, @@ -178,8 +171,9 @@ class _PackageRoller { if (unzipResult.exitCode != 0) { throw StateError( - 'Failed to unzip the downloaded archive ${tarFile.path}.\n' - 'The unzip process exited with code ${unzipResult.exitCode}.'); + 'Failed to unzip the downloaded archive ${tarFile.path}.\n' + 'The unzip process exited with code ${unzipResult.exitCode}.', + ); } vprint(' Deleting [${tarFile.path}]'); await tarFile.delete(); @@ -216,11 +210,12 @@ class _PackageRoller { final io.Directory platformDir = io.Directory(path.join(_rollDir.path, platform.name)); print('\nRolling Chromium for ${platform.name} (version:$version)'); // Bail out if CIPD already has version:$majorVersion for this package! - if (!dryRun && await cipdKnowsPackageVersion( - package: cipdPackageName, - versionTag: version, - isVerbose: verbose - )) { + if (!dryRun && + await cipdKnowsPackageVersion( + package: cipdPackageName, + versionTag: version, + isVerbose: verbose, + )) { print(' Skipping $cipdPackageName version:$version. Already uploaded to CIPD!'); vprint(' Update package_lock.yaml and use a different version value.'); return; @@ -235,7 +230,10 @@ class _PackageRoller { final io.Directory? actualContentRoot = await _locateContentRoot(platformDir); assert(actualContentRoot != null); - final String relativePlatformDirPath = path.relative(actualContentRoot!.path, from: _rollDir.path); + final String relativePlatformDirPath = path.relative( + actualContentRoot!.path, + from: _rollDir.path, + ); vprint(' Uploading Chromium (${platform.name}) to CIPD...'); await uploadDirectoryToCipd( @@ -256,14 +254,17 @@ class _PackageRoller { final String version = _lock.chromeLock.version; final String url = platform.binding.getChromeDriverDownloadUrl(version); final String cipdPackageName = 'flutter_internal/browser-drivers/chrome/${platform.name}'; - final io.Directory platformDir = io.Directory(path.join(_rollDir.path, '${platform.name}_driver')); + final io.Directory platformDir = io.Directory( + path.join(_rollDir.path, '${platform.name}_driver'), + ); print('\nRolling Chromedriver for ${platform.os}-${platform.arch} (version:$version)'); // Bail out if CIPD already has version:$majorVersion for this package! - if (!dryRun && await cipdKnowsPackageVersion( - package: cipdPackageName, - versionTag: version, - isVerbose: verbose - )) { + if (!dryRun && + await cipdKnowsPackageVersion( + package: cipdPackageName, + versionTag: version, + isVerbose: verbose, + )) { print(' Skipping $cipdPackageName version:$version. Already uploaded to CIPD!'); vprint(' Update package_lock.yaml and use a different version value.'); return; @@ -279,7 +280,10 @@ class _PackageRoller { // Ensure the chromedriver executable is placed in the root of the bundle. final io.Directory? actualContentRoot = await _locateContentRoot(platformDir); assert(actualContentRoot != null); - final String relativePlatformDirPath = path.relative(actualContentRoot!.path, from: _rollDir.path); + final String relativePlatformDirPath = path.relative( + actualContentRoot!.path, + from: _rollDir.path, + ); vprint(' Uploading Chromedriver (${platform.name}) to CIPD...'); await uploadDirectoryToCipd( @@ -294,7 +298,6 @@ class _PackageRoller { ); } - // Downloads Firefox from the internet, packs it in the directory structure // that the LUCI script wants. The result of this will be then uploaded to CIPD. Future _rollFirefox(_Platform platform) async { @@ -304,11 +307,12 @@ class _PackageRoller { final io.Directory platformDir = io.Directory(path.join(_rollDir.path, platform.name)); print('\nRolling Firefox for ${platform.name} (version:$version)'); // Bail out if CIPD already has version:$majorVersion for this package! - if (!dryRun && await cipdKnowsPackageVersion( - package: cipdPackageName, - versionTag: version, - isVerbose: verbose - )) { + if (!dryRun && + await cipdKnowsPackageVersion( + package: cipdPackageName, + versionTag: version, + isVerbose: verbose, + )) { print(' Skipping $cipdPackageName version:$version. Already uploaded to CIPD!'); vprint(' Update package_lock.yaml and use a different version value.'); return; @@ -323,7 +327,10 @@ class _PackageRoller { final io.Directory? actualContentRoot = await _locateContentRoot(platformDir); assert(actualContentRoot != null); - final String relativePlatformDirPath = path.relative(actualContentRoot!.path, from: _rollDir.path); + final String relativePlatformDirPath = path.relative( + actualContentRoot!.path, + from: _rollDir.path, + ); vprint(' Uploading Firefox (${platform.name}) to CIPD...'); await uploadDirectoryToCipd( @@ -345,11 +352,12 @@ class _PackageRoller { final io.Directory platformDir = io.Directory(path.join(_rollDir.path, platform.name)); print('\nRolling esbuild for ${platform.name} (version:$version)'); // Bail out if CIPD already has version:$majorVersion for this package! - if (!dryRun && await cipdKnowsPackageVersion( - package: cipdPackageName, - versionTag: version, - isVerbose: verbose - )) { + if (!dryRun && + await cipdKnowsPackageVersion( + package: cipdPackageName, + versionTag: version, + isVerbose: verbose, + )) { print(' Skipping $cipdPackageName version:$version. Already uploaded to CIPD!'); vprint(' Update package_lock.yaml and use a different version value.'); return; @@ -365,11 +373,10 @@ class _PackageRoller { // Write out the license file from the github repo. // Copied from https://github.com/evanw/esbuild/blob/main/LICENSE.md - final io.File licenseFile = io.File(path.join( - packageDir, - 'LICENSE.md', - )); - licenseFile..createSync()..writeAsStringSync(''' + final io.File licenseFile = io.File(path.join(packageDir, 'LICENSE.md')); + licenseFile + ..createSync() + ..writeAsStringSync(''' MIT License Copyright (c) 2020 Evan Wallace diff --git a/lib/web_ui/dev/pipeline.dart b/lib/web_ui/dev/pipeline.dart index e8e8a4d2e7f7b..54cb5aa3b5be5 100644 --- a/lib/web_ui/dev/pipeline.dart +++ b/lib/web_ui/dev/pipeline.dart @@ -184,11 +184,8 @@ typedef WatchEventPredicate = bool Function(WatchEvent event); /// The [ignore] callback can be used to customize the watching behavior to /// ignore certain files. class PipelineWatcher { - PipelineWatcher({ - required this.dir, - required this.pipeline, - this.ignore, - }) : watcher = DirectoryWatcher(dir); + PipelineWatcher({required this.dir, required this.pipeline, this.ignore}) + : watcher = DirectoryWatcher(dir); /// The path of the directory to watch for changes. final String dir; @@ -265,7 +262,7 @@ class PipelineWatcher { try { await pipeline.run(); _pipelineSucceeded(runCount); - } catch(error, stackTrace) { + } catch (error, stackTrace) { // The error is printed but not rethrown. This is because in watch mode // failures are expected. The idea is that the developer corrects the // error, saves the file, and the pipeline reruns. diff --git a/lib/web_ui/dev/roll_fallback_fonts.dart b/lib/web_ui/dev/roll_fallback_fonts.dart index d4d0d321ee3e3..ff3845363f6a7 100644 --- a/lib/web_ui/dev/roll_fallback_fonts.dart +++ b/lib/web_ui/dev/roll_fallback_fonts.dart @@ -22,21 +22,22 @@ import 'utils.dart'; const String expectedUrlPrefix = 'https://fonts.gstatic.com/s/'; -class RollFallbackFontsCommand extends Command - with ArgUtils { +class RollFallbackFontsCommand extends Command with ArgUtils { RollFallbackFontsCommand() { argParser.addOption( 'key', defaultsTo: '', - help: 'The Google Fonts API key. Used to get data about fonts hosted on ' + help: + 'The Google Fonts API key. Used to get data about fonts hosted on ' 'Google Fonts.', ); argParser.addFlag( 'dry-run', - help: 'Whether or not to push changes to CIPD. When --dry-run is set, the ' - 'script will download everything and attempt to prepare the bundle ' - 'but will stop before publishing. When not set, the bundle will be ' - 'published.', + help: + 'Whether or not to push changes to CIPD. When --dry-run is set, the ' + 'script will download everything and attempt to prepare the bundle ' + 'but will stop before publishing. When not set, the bundle will be ' + 'published.', negatable: false, ); } @@ -45,8 +46,9 @@ class RollFallbackFontsCommand extends Command final String name = 'roll-fallback-fonts'; @override - final String description = 'Generate fallback font data from GoogleFonts and ' - 'upload fonts to cipd.'; + final String description = + 'Generate fallback font data from GoogleFonts and ' + 'upload fonts to cipd.'; String get apiKey => stringArg('key'); bool get isDryRun => boolArg('dry-run'); @@ -84,8 +86,7 @@ class RollFallbackFontsCommand extends Command throw ToolExit('Failed to download font for $family'); } final String urlSuffix = getUrlSuffix(uri); - final io.File fontFile = - io.File(path.join(fontDir.path, urlSuffix)); + final io.File fontFile = io.File(path.join(fontDir.path, urlSuffix)); final Uint8List bodyBytes = fontResponse.bodyBytes; hasher.add(utf8.encode(urlSuffix)); @@ -93,8 +94,7 @@ class RollFallbackFontsCommand extends Command await fontFile.create(recursive: true); await fontFile.writeAsBytes(bodyBytes, flush: true); - final io.ProcessResult fcQueryResult = - await io.Process.run('fc-query', [ + final io.ProcessResult fcQueryResult = await io.Process.run('fc-query', [ '--format=%{charset}', '--', fontFile.path, @@ -128,8 +128,10 @@ class RollFallbackFontsCommand extends Command final String fontSetsCode = _computeEncodedFontSets(fallbackFontData); sb.writeln('// Copyright 2013 The Flutter Authors. All rights reserved.'); - sb.writeln('// Use of this source code is governed by a BSD-style license ' - 'that can be'); + sb.writeln( + '// Use of this source code is governed by a BSD-style license ' + 'that can be', + ); sb.writeln('// found in the LICENSE file.'); sb.writeln(); sb.writeln('// DO NOT EDIT! This file is generated. See:'); @@ -147,19 +149,12 @@ class RollFallbackFontsCommand extends Command sb.writeln(); sb.write(fontSetsCode); - final io.File fontDataFile = io.File(path.join( - environment.webUiRootDir.path, - 'lib', - 'src', - 'engine', - 'font_fallback_data.dart', - )); + final io.File fontDataFile = io.File( + path.join(environment.webUiRootDir.path, 'lib', 'src', 'engine', 'font_fallback_data.dart'), + ); await fontDataFile.writeAsString(sb.toString()); - final io.File licenseFile = io.File(path.join( - fontDir.path, - 'LICENSE.txt', - )); + final io.File licenseFile = io.File(path.join(fontDir.path, 'LICENSE.txt')); const String licenseString = r''' © Copyright 2015-2021 Google LLC. All Rights Reserved. @@ -264,10 +259,8 @@ OTHER DEALINGS IN THE FONT SOFTWARE. final crypto.Digest digest = hashSink.events.single; final String versionString = digest.toString(); const String packageName = 'flutter/flutter_font_fallbacks'; - if (await cipdKnowsPackageVersion( - package: packageName, - versionTag: versionString)) { - print('Package already exists with hash $versionString. Skipping upload'); + if (await cipdKnowsPackageVersion(package: packageName, versionTag: versionString)) { + print('Package already exists with hash $versionString. Skipping upload'); } else { print('Uploading fallback fonts to CIPD with hash $versionString'); await uploadDirectoryToCipd( @@ -282,15 +275,11 @@ OTHER DEALINGS IN THE FONT SOFTWARE. } print('Setting new fallback fonts deps version to $versionString'); - final String depFilePath = path.join( - environment.engineSrcDir.path, - 'flutter', - 'DEPS', - ); + final String depFilePath = path.join(environment.engineSrcDir.path, 'flutter', 'DEPS'); await runProcess('gclient', [ 'setdep', '--revision=src/flutter/third_party/google_fonts_for_unit_tests:$packageName@$versionString', - '--deps-file=$depFilePath' + '--deps-file=$depFilePath', ]); } @@ -302,26 +291,22 @@ OTHER DEALINGS IN THE FONT SOFTWARE. throw UsageException('No Google Fonts API key provided', argParser.usage); } final List<_FontInfo> processedFonts = <_FontInfo>[]; - final http.Response response = await client.get(Uri.parse( - 'https://www.googleapis.com/webfonts/v1/webfonts?capability=WOFF2&key=$apiKey')); + final http.Response response = await client.get( + Uri.parse('https://www.googleapis.com/webfonts/v1/webfonts?capability=WOFF2&key=$apiKey'), + ); if (response.statusCode != 200) { throw ToolExit('Failed to download Google Fonts list.'); } final Map googleFontsResult = jsonDecode(response.body) as Map; final List> fontDatas = - (googleFontsResult['items'] as List) - .cast>(); + (googleFontsResult['items'] as List).cast>(); for (final Map fontData in fontDatas) { final String family = fontData['family']! as String; if (requestedFonts.contains(family)) { final files = fontData['files']! as Map; - final Uri uri = Uri.parse(files['regular']! as String) - .replace(scheme: 'https'); - processedFonts.add(( - family: family, - uri: uri, - )); + final Uri uri = Uri.parse(files['regular']! as String).replace(scheme: 'https'); + processedFonts.add((family: family, uri: uri)); } } return processedFonts; @@ -334,15 +319,16 @@ OTHER DEALINGS IN THE FONT SOFTWARE. final List<_FontInfo> processedFonts = <_FontInfo>[]; for (final String font in requestedFonts) { final String modifiedFontName = font.replaceAll(' ', '+'); - final Uri cssUri = Uri.parse( - 'https://fonts.googleapis.com/css2?family=$modifiedFontName'); - final http.Response response = - await client.get(cssUri, headers: { - // Spoof the User-Agent so Google Fonts serves WOFF2 fonts - 'User-Agent': - 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) ' - 'Chrome/112.0.0.0 Safari/537.36' - }); + final Uri cssUri = Uri.parse('https://fonts.googleapis.com/css2?family=$modifiedFontName'); + final http.Response response = await client.get( + cssUri, + headers: { + // Spoof the User-Agent so Google Fonts serves WOFF2 fonts + 'User-Agent': + 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) ' + 'Chrome/112.0.0.0 Safari/537.36', + }, + ); final String cssString = response.body; // Match the patterns that look like: // `src: url(...some url...)` @@ -352,10 +338,7 @@ OTHER DEALINGS IN THE FONT SOFTWARE. for (final match in r.allMatches(cssString)) { final String family = '$font $familyCount'; final Uri uri = Uri.parse(match.group(1)!); - processedFonts.add(( - family: family, - uri: uri, - )); + processedFonts.add((family: family, uri: uri)); familyCount += 1; } } @@ -532,8 +515,7 @@ Future> _checkForLicenseAttributions( http.Client client, List<_FontInfo> fallbackFontInfo, ) async { - const String googleFontsUpstream = - 'https://github.com/google/fonts/tree/main/ofl'; + const String googleFontsUpstream = 'https://github.com/google/fonts/tree/main/ofl'; const String attributionString = 'This Font Software is licensed under the SIL Open Font License, Version 1.1.'; @@ -576,13 +558,12 @@ class _Font { static int compare(_Font a, _Font b) => a.index.compareTo(b.index); String get shortName => - _shortName + - String.fromCharCodes( - '$index'.codeUnits.map((int ch) => ch - 48 + 0x2080)); + _shortName + String.fromCharCodes('$index'.codeUnits.map((int ch) => ch - 48 + 0x2080)); - String get _shortName => info.family.startsWith('Noto Sans ') - ? info.family.substring('Noto Sans '.length) - : info.family; + String get _shortName => + info.family.startsWith('Noto Sans ') + ? info.family.substring('Noto Sans '.length) + : info.family; } /// The boundary of a range of a font. @@ -792,8 +773,7 @@ String _computeEncodedFontSets(List<_Font> fonts) { void newRange(int start, int end) { // Ensure we are using the canonical font order. - final List<_Font> fonts = List<_Font>.of(currentElements) - ..sort(_Font.compare); + final List<_Font> fonts = List<_Font>.of(currentElements)..sort(_Font.compare); final _TrieNode node = trieRoot.insertSequenceAtRoot(fonts); final _FontSet fontSet = node.fontSet ??= _FontSet(fonts); if (fontSet.rangeCount == 0) { @@ -880,12 +860,13 @@ String _computeEncodedFontSets(List<_Font> fonts) { final StringBuffer declarations = StringBuffer(); - final int references = - allSets.fold(0, (int sum, _FontSet set) => sum + set.length); + final int references = allSets.fold(0, (int sum, _FontSet set) => sum + set.length); declarations - ..writeln('// ${allSets.length} unique sets of fonts' - ' containing $references font references' - ' encoded in $totalEncodedLength characters') + ..writeln( + '// ${allSets.length} unique sets of fonts' + ' containing $references font references' + ' encoded in $totalEncodedLength characters', + ) ..writeln('const String encodedFontSets =') ..write(code) ..writeln(' ;'); @@ -923,8 +904,7 @@ String _computeEncodedFontSets(List<_Font> fonts) { declarations ..writeln() - ..writeln( - '// ${ranges.length} ranges encoded in $totalEncodedLength characters') + ..writeln('// ${ranges.length} ranges encoded in $totalEncodedLength characters') ..writeln('const String encodedFontSetRanges =') ..write(code) ..writeln(' ;'); diff --git a/lib/web_ui/dev/safari_macos.dart b/lib/web_ui/dev/safari_macos.dart index fe726b3f7b7b6..a7405f54b70f6 100644 --- a/lib/web_ui/dev/safari_macos.dart +++ b/lib/web_ui/dev/safari_macos.dart @@ -30,7 +30,8 @@ class SafariMacOsEnvironment extends WebDriverBrowserEnvironment { static const int _maxRetryCount = 10; @override - Future spawnDriverProcess() => Process.start('safaridriver', ['-p', portNumber.toString()]); + Future spawnDriverProcess() => + Process.start('safaridriver', ['-p', portNumber.toString()]); @override Future prepare() async { @@ -54,10 +55,9 @@ class SafariMacOsEnvironment extends WebDriverBrowserEnvironment { _driverProcess = await spawnDriverProcess(); - _driverProcess.stderr - .transform(utf8.decoder) - .transform(const LineSplitter()) - .listen((String error) { + _driverProcess.stderr.transform(utf8.decoder).transform(const LineSplitter()).listen(( + String error, + ) { print('[Webdriver][Error] $error'); if (_retryCount > _maxRetryCount) { print('[Webdriver][Error] Failed to start after $_maxRetryCount tries.'); @@ -66,10 +66,9 @@ class SafariMacOsEnvironment extends WebDriverBrowserEnvironment { _startDriverProcess(); } }); - _driverProcess.stdout - .transform(utf8.decoder) - .transform(const LineSplitter()) - .listen((String log) { + _driverProcess.stdout.transform(utf8.decoder).transform(const LineSplitter()).listen(( + String log, + ) { print('[Webdriver] $log'); }); } diff --git a/lib/web_ui/dev/steps/compile_bundle_step.dart b/lib/web_ui/dev/steps/compile_bundle_step.dart index b87495020cf53..a7355637cc5ff 100644 --- a/lib/web_ui/dev/steps/compile_bundle_step.dart +++ b/lib/web_ui/dev/steps/compile_bundle_step.dart @@ -12,22 +12,21 @@ import '../environment.dart'; import '../exceptions.dart'; import '../felt_config.dart'; import '../pipeline.dart'; -import '../utils.dart' show AnsiColors, FilePath, ProcessManager, cleanup, getBundleBuildDirectory, startProcess; +import '../utils.dart' + show AnsiColors, FilePath, ProcessManager, cleanup, getBundleBuildDirectory, startProcess; /// Compiles a web test bundle into web_ui/build/test_bundles/. class CompileBundleStep implements PipelineStep { - CompileBundleStep({ - required this.bundle, - required this.isVerbose, - this.testFiles, - }); + CompileBundleStep({required this.bundle, required this.isVerbose, this.testFiles}); final TestBundle bundle; final bool isVerbose; final Set? testFiles; // Maximum number of concurrent compile processes to use. - static final int _compileConcurrency = int.parse(io.Platform.environment['FELT_COMPILE_CONCURRENCY'] ?? '8'); + static final int _compileConcurrency = int.parse( + io.Platform.environment['FELT_COMPILE_CONCURRENCY'] ?? '8', + ); final Pool compilePool = Pool(_compileConcurrency); @override @@ -41,24 +40,27 @@ class CompileBundleStep implements PipelineStep { await cleanup(); } - io.Directory get testSetDirectory => io.Directory( - pathlib.join(environment.webUiTestDir.path, bundle.testSet.directory) - ); + io.Directory get testSetDirectory => + io.Directory(pathlib.join(environment.webUiTestDir.path, bundle.testSet.directory)); io.Directory get outputBundleDirectory => getBundleBuildDirectory(bundle); List _findTestFiles() { final io.Directory testDirectory = testSetDirectory; if (!testDirectory.existsSync()) { - throw ToolExit('Test directory "${testDirectory.path}" for bundle ${bundle.name.ansiMagenta} does not exist.'); + throw ToolExit( + 'Test directory "${testDirectory.path}" for bundle ${bundle.name.ansiMagenta} does not exist.', + ); } return testDirectory - .listSync(recursive: true) - .whereType() - .where((io.File f) => f.path.endsWith('_test.dart')) - .map((io.File f) => FilePath.fromWebUi( - pathlib.relative(f.path, from: environment.webUiRootDir.path))) - .toList(); + .listSync(recursive: true) + .whereType() + .where((io.File f) => f.path.endsWith('_test.dart')) + .map( + (io.File f) => + FilePath.fromWebUi(pathlib.relative(f.path, from: environment.webUiRootDir.path)), + ) + .toList(); } TestCompiler _createCompiler(CompileConfiguration config) { @@ -84,59 +86,65 @@ class CompileBundleStep implements PipelineStep { Future run() async { print('Compiling test bundle ${bundle.name.ansiMagenta}...'); final List allTests = _findTestFiles(); - final List compilers = bundle.compileConfigs.map( - (CompileConfiguration config) => _createCompiler(config) - ).toList(); + final List compilers = + bundle.compileConfigs + .map((CompileConfiguration config) => _createCompiler(config)) + .toList(); final Stopwatch stopwatch = Stopwatch()..start(); final String testSetDirectoryPath = testSetDirectory.path; // Clear out old bundle compilations, if they exist if (outputBundleDirectory.existsSync()) { - outputBundleDirectory.deleteSync(recursive: true ); + outputBundleDirectory.deleteSync(recursive: true); } - final List>> pendingResults = >>[]; + final List>> pendingResults = + >>[]; for (final TestCompiler compiler in compilers) { for (final FilePath testFile in allTests) { - final String relativePath = pathlib.relative( - testFile.absolute, - from: testSetDirectoryPath); + final String relativePath = pathlib.relative(testFile.absolute, from: testSetDirectoryPath); final Future> result = compilePool.withResource(() async { if (testFiles != null && !testFiles!.contains(testFile)) { return MapEntry(relativePath, CompileResult.filtered); } final bool success = await compiler.compileTest(testFile); const int maxTestNameLength = 80; - final String truncatedPath = relativePath.length > maxTestNameLength - ? relativePath.replaceRange(maxTestNameLength - 3, relativePath.length, '...') - : relativePath; + final String truncatedPath = + relativePath.length > maxTestNameLength + ? relativePath.replaceRange(maxTestNameLength - 3, relativePath.length, '...') + : relativePath; final String expandedPath = truncatedPath.padRight(maxTestNameLength); io.stdout.write('\r ${success ? expandedPath.ansiGreen : expandedPath.ansiRed}'); return success - ? MapEntry(relativePath, CompileResult.success) - : MapEntry(relativePath, CompileResult.compilationFailure); + ? MapEntry(relativePath, CompileResult.success) + : MapEntry(relativePath, CompileResult.compilationFailure); }); pendingResults.add(result); } } - final Map results = Map.fromEntries(await Future.wait(pendingResults)); + final Map results = Map.fromEntries( + await Future.wait(pendingResults), + ); stopwatch.stop(); final String resultsJson = const JsonEncoder.withIndent(' ').convert({ 'name': bundle.name, 'directory': bundle.testSet.directory, - 'builds': bundle.compileConfigs.map( - (CompileConfiguration config) => { - 'compiler': config.compiler.name, - 'renderer': config.renderer.name, - }).toList(), + 'builds': + bundle.compileConfigs + .map( + (CompileConfiguration config) => { + 'compiler': config.compiler.name, + 'renderer': config.renderer.name, + }, + ) + .toList(), 'compileTimeInMs': stopwatch.elapsedMilliseconds, 'results': results.map((String k, CompileResult v) => MapEntry(k, v.name)), }); - final io.File outputResultsFile = io.File(pathlib.join( - outputBundleDirectory.path, - 'results.json', - )); + final io.File outputResultsFile = io.File( + pathlib.join(outputBundleDirectory.path, 'results.json'), + ); outputResultsFile.writeAsStringSync(resultsJson); final List failedFiles = []; results.forEach((String fileName, CompileResult result) { @@ -145,9 +153,14 @@ class CompileBundleStep implements PipelineStep { } }); if (failedFiles.isEmpty) { - print('\rCompleted compilation of ${bundle.name.ansiMagenta} in ${stopwatch.elapsedMilliseconds}ms.'.padRight(82)); + print( + '\rCompleted compilation of ${bundle.name.ansiMagenta} in ${stopwatch.elapsedMilliseconds}ms.' + .padRight(82), + ); } else { - print('\rThe bundle ${bundle.name.ansiMagenta} compiled with some failures in ${stopwatch.elapsedMilliseconds}ms.'); + print( + '\rThe bundle ${bundle.name.ansiMagenta} compiled with some failures in ${stopwatch.elapsedMilliseconds}ms.', + ); print('Compilation failures:'); for (final String fileName in failedFiles) { print(' $fileName'); @@ -157,21 +170,15 @@ class CompileBundleStep implements PipelineStep { } } -enum CompileResult { - success, - compilationFailure, - filtered, -} +enum CompileResult { success, compilationFailure, filtered } abstract class TestCompiler { TestCompiler( this.inputTestSetDirectory, - this.outputTestBundleDirectory, - { - required this.renderer, - required this.isVerbose, - } - ); + this.outputTestBundleDirectory, { + required this.renderer, + required this.isVerbose, + }); final io.Directory inputTestSetDirectory; final io.Directory outputTestBundleDirectory; @@ -184,19 +191,14 @@ abstract class TestCompiler { class Dart2JSCompiler extends TestCompiler { Dart2JSCompiler( super.inputTestSetDirectory, - super.outputTestBundleDirectory, - { - required super.renderer, - required super.isVerbose, - } - ); + super.outputTestBundleDirectory, { + required super.renderer, + required super.isVerbose, + }); @override Future compileTest(FilePath input) async { - final String relativePath = pathlib.relative( - input.absolute, - from: inputTestSetDirectory.path - ); + final String relativePath = pathlib.relative(input.absolute, from: inputTestSetDirectory.path); final String targetFileName = pathlib.join( outputTestBundleDirectory.path, @@ -237,8 +239,10 @@ class Dart2JSCompiler extends TestCompiler { ); final int exitCode = await process.wait(); if (exitCode != 0) { - io.stderr.writeln('ERROR: Failed to compile test $input. ' - 'Dart2js exited with exit code $exitCode'); + io.stderr.writeln( + 'ERROR: Failed to compile test $input. ' + 'Dart2js exited with exit code $exitCode', + ); return false; } else { return true; @@ -249,19 +253,14 @@ class Dart2JSCompiler extends TestCompiler { class Dart2WasmCompiler extends TestCompiler { Dart2WasmCompiler( super.inputTestSetDirectory, - super.outputTestBundleDirectory, - { - required super.renderer, - required super.isVerbose, - } - ); + super.outputTestBundleDirectory, { + required super.renderer, + required super.isVerbose, + }); @override Future compileTest(FilePath input) async { - final String relativePath = pathlib.relative( - input.absolute, - from: inputTestSetDirectory.path - ); + final String relativePath = pathlib.relative(input.absolute, from: inputTestSetDirectory.path); final String targetFileName = pathlib.join( outputTestBundleDirectory.path, @@ -306,8 +305,10 @@ class Dart2WasmCompiler extends TestCompiler { final int exitCode = await process.wait(); if (exitCode != 0) { - io.stderr.writeln('ERROR: Failed to compile test $input. ' - 'dart2wasm exited with exit code $exitCode'); + io.stderr.writeln( + 'ERROR: Failed to compile test $input. ' + 'dart2wasm exited with exit code $exitCode', + ); return false; } else { return true; diff --git a/lib/web_ui/dev/steps/copy_artifacts_step.dart b/lib/web_ui/dev/steps/copy_artifacts_step.dart index 930b30ac9f894..285e242353715 100644 --- a/lib/web_ui/dev/steps/copy_artifacts_step.dart +++ b/lib/web_ui/dev/steps/copy_artifacts_step.dart @@ -14,7 +14,7 @@ import '../pipeline.dart'; import '../utils.dart'; class CopyArtifactsStep implements PipelineStep { - CopyArtifactsStep(this.artifactDeps, { required this.runtimeMode }); + CopyArtifactsStep(this.artifactDeps, {required this.runtimeMode}); final ArtifactDependencies artifactDeps; final RuntimeMode runtimeMode; @@ -76,49 +76,42 @@ class CopyArtifactsStep implements PipelineStep { fontManifest.add({ 'family': family, 'fonts': [ - { - 'asset': 'fonts/$fontFile', - }, + {'asset': 'fonts/$fontFile'}, ], }); final io.File sourceTtf = io.File(pathlib.join(fontsPath, fontFile)); - final io.File destinationTtf = io.File(pathlib.join( - environment.webTestsArtifactsDir.path, - 'assets', - 'fonts', - fontFile, - )); + final io.File destinationTtf = io.File( + pathlib.join(environment.webTestsArtifactsDir.path, 'assets', 'fonts', fontFile), + ); await destinationTtf.create(recursive: true); await sourceTtf.copy(destinationTtf.path); } - final io.File fontManifestFile = io.File(pathlib.join( - environment.webTestsArtifactsDir.path, - 'assets', - 'FontManifest.json', - )); + final io.File fontManifestFile = io.File( + pathlib.join(environment.webTestsArtifactsDir.path, 'assets', 'FontManifest.json'), + ); await fontManifestFile.create(recursive: true); - await fontManifestFile.writeAsString( - const JsonEncoder.withIndent(' ').convert(fontManifest), + await fontManifestFile.writeAsString(const JsonEncoder.withIndent(' ').convert(fontManifest)); + + final io.Directory fallbackFontsSource = io.Directory( + pathlib.join( + environment.engineSrcDir.path, + 'flutter', + 'third_party', + 'google_fonts_for_unit_tests', + ), ); - - final io.Directory fallbackFontsSource = io.Directory(pathlib.join( - environment.engineSrcDir.path, - 'flutter', - 'third_party', - 'google_fonts_for_unit_tests', - )); final String fallbackFontsDestinationPath = pathlib.join( environment.webTestsArtifactsDir.path, 'assets', 'fallback_fonts', ); - for (final io.File file in - fallbackFontsSource.listSync(recursive: true).whereType() - ) { + for (final io.File file in fallbackFontsSource.listSync(recursive: true).whereType()) { final String relativePath = pathlib.relative(file.path, from: fallbackFontsSource.path); - final io.File destinationFile = io.File(pathlib.join(fallbackFontsDestinationPath, relativePath)); + final io.File destinationFile = io.File( + pathlib.join(fallbackFontsDestinationPath, relativePath), + ); if (!destinationFile.parent.existsSync()) { destinationFile.parent.createSync(recursive: true); } @@ -127,53 +120,50 @@ class CopyArtifactsStep implements PipelineStep { } Future copySkiaTestImages() async { - final io.Directory testImagesDir = io.Directory(pathlib.join( - environment.engineSrcDir.path, - 'flutter', - 'third_party', - 'skia', - 'resources', - 'images', - )); + final io.Directory testImagesDir = io.Directory( + pathlib.join( + environment.engineSrcDir.path, + 'flutter', + 'third_party', + 'skia', + 'resources', + 'images', + ), + ); for (final io.File imageFile in testImagesDir.listSync(recursive: true).whereType()) { // Skip files that are used by Skia to test handling of invalid input. if (pathlib.basename(imageFile.path).contains('invalid')) { continue; } - final io.File destination = io.File(pathlib.join( - environment.webTestsArtifactsDir.path, - 'test_images', - pathlib.relative(imageFile.path, from: testImagesDir.path), - )); + final io.File destination = io.File( + pathlib.join( + environment.webTestsArtifactsDir.path, + 'test_images', + pathlib.relative(imageFile.path, from: testImagesDir.path), + ), + ); destination.createSync(recursive: true); await imageFile.copy(destination.path); } } Future copyFlutterJsFiles() async { - final io.Directory flutterJsInputDirectory = io.Directory(pathlib.join( - outBuildPath, - 'flutter_web_sdk', - 'flutter_js', - )); + final io.Directory flutterJsInputDirectory = io.Directory( + pathlib.join(outBuildPath, 'flutter_web_sdk', 'flutter_js'), + ); final String targetDirectoryPath = pathlib.join( environment.webTestsArtifactsDir.path, 'flutter_js', ); - for (final io.File sourceFile in flutterJsInputDirectory - .listSync(recursive: true) - .whereType() - ) { + for (final io.File sourceFile + in flutterJsInputDirectory.listSync(recursive: true).whereType()) { final String relativePath = pathlib.relative( sourceFile.path, - from: flutterJsInputDirectory.path - ); - final String targetPath = pathlib.join( - targetDirectoryPath, - relativePath, + from: flutterJsInputDirectory.path, ); + final String targetPath = pathlib.join(targetDirectoryPath, relativePath); final io.File targetFile = io.File(targetPath); if (!targetFile.parent.existsSync()) { targetFile.parent.createSync(recursive: true); @@ -183,10 +173,7 @@ class CopyArtifactsStep implements PipelineStep { } Future copyCanvasKitFiles(String sourcePath, String destinationPath) async { - final String sourceDirectoryPath = pathlib.join( - outBuildPath, - sourcePath, - ); + final String sourceDirectoryPath = pathlib.join(outBuildPath, sourcePath); final String targetDirectoryPath = pathlib.join( environment.webTestsArtifactsDir.path, @@ -198,20 +185,15 @@ class CopyArtifactsStep implements PipelineStep { 'canvaskit.wasm', 'canvaskit.wasm.map', ]) { - final io.File sourceFile = io.File(pathlib.join( - sourceDirectoryPath, - filename, - )); - final io.File targetFile = io.File(pathlib.join( - targetDirectoryPath, - filename, - )); + final io.File sourceFile = io.File(pathlib.join(sourceDirectoryPath, filename)); + final io.File targetFile = io.File(pathlib.join(targetDirectoryPath, filename)); if (!sourceFile.existsSync()) { if (filename.endsWith('.map')) { // Sourcemaps are only generated under certain build conditions, so // they are optional. continue; - } { + } + { throw ToolExit('Built CanvasKit artifact not found at path "$sourceFile".'); } } @@ -223,10 +205,9 @@ class CopyArtifactsStep implements PipelineStep { String get outBuildPath => getBuildDirectoryForRuntimeMode(runtimeMode).path; Future copySkwasm() async { - final io.Directory targetDir = io.Directory(pathlib.join( - environment.webTestsArtifactsDir.path, - 'canvaskit', - )); + final io.Directory targetDir = io.Directory( + pathlib.join(environment.webTestsArtifactsDir.path, 'canvaskit'), + ); await targetDir.create(recursive: true); @@ -238,71 +219,47 @@ class CopyArtifactsStep implements PipelineStep { 'skwasm_st.wasm.map', 'skwasm_st.js', ]) { - final io.File sourceFile = io.File(pathlib.join( - outBuildPath, - 'flutter_web_sdk', - 'canvaskit', - fileName, - )); + final io.File sourceFile = io.File( + pathlib.join(outBuildPath, 'flutter_web_sdk', 'canvaskit', fileName), + ); if (!sourceFile.existsSync()) { if (fileName.endsWith('.map')) { // Sourcemaps are only generated under certain build conditions, so // they are optional. continue; - } { + } + { throw ToolExit('Built Skwasm artifact not found at path "$sourceFile".'); } } - final io.File targetFile = io.File(pathlib.join( - targetDir.path, - fileName, - )); + final io.File targetFile = io.File(pathlib.join(targetDir.path, fileName)); await sourceFile.copy(targetFile.path); } } Future buildHostPage() async { final String hostDartPath = pathlib.join('lib', 'static', 'host.dart'); - final io.File hostDartFile = io.File(pathlib.join( - environment.webEngineTesterRootDir.path, - hostDartPath, - )); - final String targetDirectoryPath = pathlib.join( - environment.webTestsArtifactsDir.path, - 'host', + final io.File hostDartFile = io.File( + pathlib.join(environment.webEngineTesterRootDir.path, hostDartPath), ); + final String targetDirectoryPath = pathlib.join(environment.webTestsArtifactsDir.path, 'host'); io.Directory(targetDirectoryPath).createSync(recursive: true); - final String targetFilePath = pathlib.join( - targetDirectoryPath, - 'host.dart', - ); + final String targetFilePath = pathlib.join(targetDirectoryPath, 'host.dart'); - const List staticFiles = [ - 'favicon.ico', - 'host.css', - 'index.html', - ]; + const List staticFiles = ['favicon.ico', 'host.css', 'index.html']; for (final String staticFilePath in staticFiles) { - final io.File source = io.File(pathlib.join( - environment.webEngineTesterRootDir.path, - 'lib', - 'static', - staticFilePath, - )); - final io.File destination = io.File(pathlib.join( - targetDirectoryPath, - staticFilePath, - )); + final io.File source = io.File( + pathlib.join(environment.webEngineTesterRootDir.path, 'lib', 'static', staticFilePath), + ); + final io.File destination = io.File(pathlib.join(targetDirectoryPath, staticFilePath)); await source.copy(destination.path); } - final io.File timestampFile = io.File(pathlib.join( - environment.webEngineTesterRootDir.path, - '$targetFilePath.js.timestamp', - )); + final io.File timestampFile = io.File( + pathlib.join(environment.webEngineTesterRootDir.path, '$targetFilePath.js.timestamp'), + ); - final String timestamp = - hostDartFile.statSync().modified.millisecondsSinceEpoch.toString(); + final String timestamp = hostDartFile.statSync().modified.millisecondsSinceEpoch.toString(); if (timestampFile.existsSync()) { final String lastBuildTimestamp = timestampFile.readAsStringSync(); if (lastBuildTimestamp == timestamp) { @@ -316,14 +273,10 @@ class CopyArtifactsStep implements PipelineStep { print('Building ${hostDartFile.path}.'); } - int exitCode = await runProcess( - environment.dartExecutable, - [ - 'pub', - 'get', - ], - workingDirectory: environment.webEngineTesterRootDir.path - ); + int exitCode = await runProcess(environment.dartExecutable, [ + 'pub', + 'get', + ], workingDirectory: environment.webEngineTesterRootDir.path); if (exitCode != 0) { throw ToolExit( @@ -332,17 +285,13 @@ class CopyArtifactsStep implements PipelineStep { ); } - exitCode = await runProcess( - environment.dartExecutable, - [ - 'compile', - 'js', - hostDartPath, - '-o', - '$targetFilePath.js', - ], - workingDirectory: environment.webEngineTesterRootDir.path, - ); + exitCode = await runProcess(environment.dartExecutable, [ + 'compile', + 'js', + hostDartPath, + '-o', + '$targetFilePath.js', + ], workingDirectory: environment.webEngineTesterRootDir.path); if (exitCode != 0) { throw ToolExit( diff --git a/lib/web_ui/dev/steps/run_suite_step.dart b/lib/web_ui/dev/steps/run_suite_step.dart index 50f8336571edf..7eda336f1a7b3 100644 --- a/lib/web_ui/dev/steps/run_suite_step.dart +++ b/lib/web_ui/dev/steps/run_suite_step.dart @@ -12,7 +12,8 @@ import 'package:skia_gold_client/skia_gold_client.dart'; import 'package:test_api/backend.dart' as hack; // TODO(ditman): Fix ignores when https://github.com/flutter/flutter/issues/143599 is resolved. import 'package:test_core/src/executable.dart' as test; // ignore: implementation_imports -import 'package:test_core/src/runner/hack_register_platform.dart' as hack; // ignore: implementation_imports +import 'package:test_core/src/runner/hack_register_platform.dart' + as hack; // ignore: implementation_imports import '../browser.dart'; import '../common.dart'; @@ -29,7 +30,8 @@ import '../utils.dart'; /// running them prior to this step locally, or by having the build graph copy /// them from another bot. class RunSuiteStep implements PipelineStep { - RunSuiteStep(this.suite, { + RunSuiteStep( + this.suite, { required this.startPaused, required this.isVerbose, required this.doUpdateScreenshotGoldens, @@ -90,9 +92,7 @@ class RunSuiteStep implements PipelineStep { ..._collectTestPaths(), ]; - hack.registerPlatformPlugin([ - browserEnvironment.packageTestRuntime, - ], () { + hack.registerPlatformPlugin([browserEnvironment.packageTestRuntime], () { return BrowserPlatform.start( suite, browserEnvironment: browserEnvironment, @@ -106,10 +106,9 @@ class RunSuiteStep implements PipelineStep { print('[${suite.name.ansiCyan}] Running...'); // We want to run tests with the test set's directory as a working directory. - final io.Directory testSetDirectory = io.Directory(pathlib.join( - environment.webUiTestDir.path, - suite.testBundle.testSet.directory, - )); + final io.Directory testSetDirectory = io.Directory( + pathlib.join(environment.webUiTestDir.path, suite.testBundle.testSet.directory), + ); final dynamic originalCwd = io.Directory.current; io.Directory.current = testSetDirectory; try { @@ -135,10 +134,9 @@ class RunSuiteStep implements PipelineStep { } io.Directory _prepareTestResultsDirectory() { - final io.Directory resultsDirectory = io.Directory(pathlib.join( - environment.webUiTestResultsDirectory.path, - suite.name, - )); + final io.Directory resultsDirectory = io.Directory( + pathlib.join(environment.webUiTestResultsDirectory.path, suite.name), + ); if (resultsDirectory.existsSync()) { resultsDirectory.deleteSync(recursive: true); } @@ -148,12 +146,11 @@ class RunSuiteStep implements PipelineStep { List _collectTestPaths() { final io.Directory bundleBuild = getBundleBuildDirectory(suite.testBundle); - final io.File resultsJsonFile = io.File(pathlib.join( - bundleBuild.path, - 'results.json', - )); + final io.File resultsJsonFile = io.File(pathlib.join(bundleBuild.path, 'results.json')); if (!resultsJsonFile.existsSync()) { - throw ToolExit('Could not find built bundle ${suite.testBundle.name.ansiMagenta} for suite ${suite.name.ansiCyan}.'); + throw ToolExit( + 'Could not find built bundle ${suite.testBundle.name.ansiMagenta} for suite ${suite.name.ansiCyan}.', + ); } final String jsonString = resultsJsonFile.readAsStringSync(); final jsonContents = const JsonDecoder().convert(jsonString) as Map; @@ -194,23 +191,21 @@ class RunSuiteStep implements PipelineStep { workDirectory.deleteSync(recursive: true); } final bool isWasm = suite.testBundle.compileConfigs.first.compiler == Compiler.dart2wasm; - final bool singleThreaded = suite.runConfig.forceSingleThreadedSkwasm || !suite.runConfig.crossOriginIsolated; + final bool singleThreaded = + suite.runConfig.forceSingleThreadedSkwasm || !suite.runConfig.crossOriginIsolated; final String rendererName = switch (renderer) { Renderer.skwasm => singleThreaded ? 'skwasm_st' : 'skwasm', _ => renderer.name, }; - final dimensions = { + final dimensions = { 'Browser': suite.runConfig.browser.name, if (isWasm) 'Wasm': 'true', 'Renderer': rendererName, if (variant != null) 'CanvasKitVariant': variant.name, }; print('Created Skia Gold Client. dimensions: $dimensions'); - final SkiaGoldClient skiaClient = SkiaGoldClient( - workDirectory, - dimensions: dimensions, - ); + final SkiaGoldClient skiaClient = SkiaGoldClient(workDirectory, dimensions: dimensions); if (await _checkSkiaClient(skiaClient)) { print('Successfully checked Skia Gold Client'); diff --git a/lib/web_ui/dev/suite_filter.dart b/lib/web_ui/dev/suite_filter.dart index 91f26610bbb87..d566b4f5e6784 100644 --- a/lib/web_ui/dev/suite_filter.dart +++ b/lib/web_ui/dev/suite_filter.dart @@ -20,7 +20,7 @@ abstract class SuiteFilter { } abstract class AllowListSuiteFilter implements SuiteFilter { - AllowListSuiteFilter({ required this.allowList }); + AllowListSuiteFilter({required this.allowList}); final Set allowList; @@ -76,10 +76,12 @@ class CompilerFilter extends SuiteFilter { final Set allowList; @override - SuiteFilterResult filterSuite(TestSuite suite) => suite.testBundle.compileConfigs.any( - (CompileConfiguration config) => allowList.contains(config.compiler) - ) ? SuiteFilterResult.accepted() - : SuiteFilterResult.rejected('Selected compilers not used in suite.'); + SuiteFilterResult filterSuite(TestSuite suite) => + suite.testBundle.compileConfigs.any( + (CompileConfiguration config) => allowList.contains(config.compiler), + ) + ? SuiteFilterResult.accepted() + : SuiteFilterResult.rejected('Selected compilers not used in suite.'); } class RendererFilter extends SuiteFilter { @@ -88,10 +90,12 @@ class RendererFilter extends SuiteFilter { final Set allowList; @override - SuiteFilterResult filterSuite(TestSuite suite) => suite.testBundle.compileConfigs.any( - (CompileConfiguration config) => allowList.contains(config.renderer) - ) ? SuiteFilterResult.accepted() - : SuiteFilterResult.rejected('Selected renderers not used in suite.'); + SuiteFilterResult filterSuite(TestSuite suite) => + suite.testBundle.compileConfigs.any( + (CompileConfiguration config) => allowList.contains(config.renderer), + ) + ? SuiteFilterResult.accepted() + : SuiteFilterResult.rejected('Selected renderers not used in suite.'); } class CanvasKitVariantFilter extends AllowListSuiteFilter { @@ -99,26 +103,17 @@ class CanvasKitVariantFilter extends AllowListSuiteFilter { @override // TODO(jackson): Is this the right default? - CanvasKitVariant getAttributeForSuite(TestSuite suite) => suite.runConfig.variant ?? CanvasKitVariant.full; + CanvasKitVariant getAttributeForSuite(TestSuite suite) => + suite.runConfig.variant ?? CanvasKitVariant.full; } Set get _supportedPlatformBrowsers { if (io.Platform.isLinux) { - return { - BrowserName.chrome, - BrowserName.firefox - }; + return {BrowserName.chrome, BrowserName.firefox}; } else if (io.Platform.isMacOS) { - return { - BrowserName.chrome, - BrowserName.firefox, - BrowserName.safari, - }; + return {BrowserName.chrome, BrowserName.firefox, BrowserName.safari}; } else if (io.Platform.isWindows) { - return { - BrowserName.chrome, - BrowserName.edge, - }; + return {BrowserName.chrome, BrowserName.edge}; } else { throw AssertionError('Unsupported OS: ${io.Platform.operatingSystem}'); } @@ -129,5 +124,5 @@ class PlatformBrowserFilter extends BrowserSuiteFilter { @override String rejectReason(TestSuite suite) => - 'Current platform (${io.Platform.operatingSystem}) does not support browser ${suite.runConfig.browser}'; + 'Current platform (${io.Platform.operatingSystem}) does not support browser ${suite.runConfig.browser}'; } diff --git a/lib/web_ui/dev/test_platform.dart b/lib/web_ui/dev/test_platform.dart index d1e4396648ee7..3c75dc034856a 100644 --- a/lib/web_ui/dev/test_platform.dart +++ b/lib/web_ui/dev/test_platform.dart @@ -46,7 +46,8 @@ const Map coopCoepHeaders = { /// Custom test platform that serves web engine unit tests. class BrowserPlatform extends PlatformPlugin { - BrowserPlatform._(this.suite, { + BrowserPlatform._( + this.suite, { required this.browserEnvironment, required this.server, required this.isDebug, @@ -62,7 +63,6 @@ class BrowserPlatform extends PlatformPlugin { // reporting restuls. See [_browserManagerFor] and [BrowserManager.start] // for details on how the channels are established. .add(_webSocketHandler.handler) - // Serves /packages/* requests; fetches files and sources from // pubspec dependencies. // @@ -71,36 +71,27 @@ class BrowserPlatform extends PlatformPlugin { // * Assets that are part of the engine sources, such as Ahem.ttf .add(_packageUrlHandler) .add(_canvasKitOverrideHandler) - // Serves files from the bundle's output build directory .add(createSimpleDirectoryHandler(getBundleBuildDirectory(suite.testBundle))) - // Serves files from the out/web_tests/artifacts directory at the root (/) URL path. .add(createSimpleDirectoryHandler(env.environment.webTestsArtifactsDir)) - // Serves files from the test set directory .add(createSimpleDirectoryHandler(getTestSetDirectory(suite.testBundle.testSet))) .add(_testImageListingHandler) - // Serves the initial HTML for the test. .add(_testBootstrapHandler) - // Serves source files from the engine src root for devtools debugging. .add(_createSourceHandler()) - // Serves files from the root of web_ui. Some tests download assets that are embedded // directly in the test folder, such as test/html/image/sample_image1.png etc .add(createStaticHandler(env.environment.webUiRootDir.path)) - // Serves absolute package URLs (i.e. not /packages/* but /Users/user/*/hosted/pub.dartlang.org/*). // This handler goes last, after all more specific handlers failed to handle the request. .add(_createAbsolutePackageUrlHandler()) .add(_screenshotHandler) - // Generates and serves a test payload of given length, split into chunks // of given size. Reponds to requests to /long_test_payload. .add(_testPayloadGenerator) - // If none of the handlers above handled the request, return 404. .add(_fileNotFoundCatcher); @@ -113,15 +104,15 @@ class BrowserPlatform extends PlatformPlugin { /// /// If [doUpdateScreenshotGoldens] is true updates screenshot golden files /// instead of failing the test on screenshot mismatches. - static Future start(TestSuite suite, { + static Future start( + TestSuite suite, { required BrowserEnvironment browserEnvironment, required bool doUpdateScreenshotGoldens, required SkiaGoldClient? skiaClient, required String? overridePathToCanvasKit, required bool isVerbose, }) async { - final shelf_io.IOServer server = - shelf_io.IOServer(await HttpMultiServer.loopback(0)); + final shelf_io.IOServer server = shelf_io.IOServer(await HttpMultiServer.loopback(0)); return BrowserPlatform._( suite, browserEnvironment: browserEnvironment, @@ -178,18 +169,14 @@ class BrowserPlatform extends PlatformPlugin { /// If a path to a custom local build of CanvasKit was specified, serve from /// there instead of serving the default CanvasKit in the build/ directory. - Future _canvasKitOverrideHandler( - shelf.Request request) async { + Future _canvasKitOverrideHandler(shelf.Request request) async { final String? pathOverride = overridePathToCanvasKit; if (pathOverride == null || !request.url.path.startsWith('canvaskit/')) { return shelf.Response.notFound('Not a request for CanvasKit.'); } - final File file = File(p.joinAll([ - pathOverride, - ...p.split(request.url.path).skip(1), - ])); + final File file = File(p.joinAll([pathOverride, ...p.split(request.url.path).skip(1)])); if (!file.existsSync()) { return shelf.Response.notFound('File not found: ${request.url.path}'); @@ -199,17 +186,14 @@ class BrowserPlatform extends PlatformPlugin { final String? contentType = contentTypes[extension]; if (contentType == null) { - final String error = - 'Failed to determine Content-Type for "${request.url.path}".'; + final String error = 'Failed to determine Content-Type for "${request.url.path}".'; stderr.writeln(error); return shelf.Response.internalServerError(body: error); } return shelf.Response.ok( file.readAsBytesSync(), - headers: { - HttpHeaders.contentTypeHeader: contentType, - }, + headers: {HttpHeaders.contentTypeHeader: contentType}, ); } @@ -228,25 +212,21 @@ class BrowserPlatform extends PlatformPlugin { return shelf.Response.notFound('Not found.'); } - final Directory testImageDirectory = Directory(p.join( - env.environment.webTestsArtifactsDir.path, - 'test_images', - )); + final Directory testImageDirectory = Directory( + p.join(env.environment.webTestsArtifactsDir.path, 'test_images'), + ); - final List testImageFiles = testImageDirectory - .listSync(recursive: true) - .whereType() - .map( - (File file) => p.relative(file.path, from: testImageDirectory.path)) - .where( - (String path) => supportedImageTypes.containsKey(p.extension(path))) - .toList(); + final List testImageFiles = + testImageDirectory + .listSync(recursive: true) + .whereType() + .map((File file) => p.relative(file.path, from: testImageDirectory.path)) + .where((String path) => supportedImageTypes.containsKey(p.extension(path))) + .toList(); return shelf.Response.ok( json.encode(testImageFiles), - headers: { - HttpHeaders.contentTypeHeader: 'application/json', - }, + headers: {HttpHeaders.contentTypeHeader: 'application/json'}, ); } @@ -261,11 +241,11 @@ class BrowserPlatform extends PlatformPlugin { final String path = p.fromUri(request.url); final String extension = p.extension(path); final bool isSource = - extension == '.dart' || - extension == '.c' || - extension == '.cc' || - extension == '.cpp' || - extension == '.h'; + extension == '.dart' || + extension == '.c' || + extension == '.cc' || + extension == '.cpp' || + extension == '.h'; if (isSource && p.isRelative(path)) { final String fullPath = p.join(env.environment.engineSrcDir.path, path); final File file = File(fullPath); @@ -318,8 +298,7 @@ class BrowserPlatform extends PlatformPlugin { // // C:\Users\user\AppData => Users\user\AppData // /home/user/path.dart => home/user/path.dart - final String rootRelativePath = - p.relative(configPath, from: p.rootPrefix(configPath)); + final String rootRelativePath = p.relative(configPath, from: p.rootPrefix(configPath)); urlToPackage[p.toUri(rootRelativePath).path] = package; } return (shelf.Request request) async { @@ -327,9 +306,9 @@ class BrowserPlatform extends PlatformPlugin { // The cast is needed because keys are non-null String, so there's no way // to return null for a mismatch. final String? packagePath = urlToPackage.keys.cast().firstWhere( - (String? packageUrl) => requestedPath.startsWith(packageUrl!), - orElse: () => null, - ); + (String? packageUrl) => requestedPath.startsWith(packageUrl!), + orElse: () => null, + ); if (packagePath == null) { return shelf.Response.notFound('Not a pub.dartlang.org request'); } @@ -354,8 +333,7 @@ class BrowserPlatform extends PlatformPlugin { Future _testPayloadGenerator(shelf.Request request) async { if (!request.requestedUri.path.endsWith('/long_test_payload')) { - return shelf.Response.notFound( - 'This request is not handled by the test payload generator'); + return shelf.Response.notFound('This request is not handled by the test payload generator'); } final int payloadLength = int.parse(request.requestedUri.queryParameters['length']!); @@ -394,13 +372,11 @@ class BrowserPlatform extends PlatformPlugin { Future _screenshotHandler(shelf.Request request) async { if (!request.requestedUri.path.endsWith('/screenshot')) { - return shelf.Response.notFound( - 'This request is not handled by the screenshot handler'); + return shelf.Response.notFound('This request is not handled by the screenshot handler'); } final String payload = await request.readAsString(); - final Map requestData = - json.decode(payload) as Map; + final Map requestData = json.decode(payload) as Map; final String filename = requestData['filename'] as String; if (!(await browserManager).supportsScreenshots) { @@ -413,8 +389,7 @@ class BrowserPlatform extends PlatformPlugin { return shelf.Response.ok(json.encode('OK')); } - final Map region = - requestData['region'] as Map; + final Map region = requestData['region'] as Map; final bool isCanvaskitTest = requestData['isCanvaskitTest'] as bool; final String result = await _diffScreenshot(filename, region, isCanvaskitTest); return shelf.Response.ok(json.encode(result)); @@ -433,8 +408,7 @@ class BrowserPlatform extends PlatformPlugin { ); // Take screenshot. - final Image screenshot = - await (await browserManager).captureScreenshot(regionAsRectange); + final Image screenshot = await (await browserManager).captureScreenshot(regionAsRectange); return compareImage( screenshot, @@ -476,10 +450,7 @@ class BrowserPlatform extends PlatformPlugin { /// This is used for trivial use-cases, such as `favicon.ico`, host pages, etc. shelf.Handler createSimpleDirectoryHandler(Directory directory) { return (shelf.Request request) { - final File fileInDirectory = File(p.join( - directory.path, - request.url.path, - )); + final File fileInDirectory = File(p.join(directory.path, request.url.path)); if (request.url.path.contains('//') || !fileInDirectory.existsSync()) { return shelf.Response.notFound('File not found: ${request.url.path}'); @@ -489,22 +460,17 @@ class BrowserPlatform extends PlatformPlugin { final String? contentType = contentTypes[extension]; if (contentType == null) { - final String error = - 'Failed to determine Content-Type for "${request.url.path}".'; + final String error = 'Failed to determine Content-Type for "${request.url.path}".'; stderr.writeln(error); return shelf.Response.internalServerError(body: error); } - final bool isScript = - extension == '.js' || - extension == '.mjs' || - extension == '.html'; + final bool isScript = extension == '.js' || extension == '.mjs' || extension == '.html'; return shelf.Response.ok( fileInDirectory.readAsBytesSync(), headers: { HttpHeaders.contentTypeHeader: contentType, - if (isScript && suite.runConfig.crossOriginIsolated) - ...coopCoepHeaders, + if (isScript && suite.runConfig.crossOriginIsolated) ...coopCoepHeaders, }, ); }; @@ -522,14 +488,16 @@ class BrowserPlatform extends PlatformPlugin { } String _makeBuildConfigString(String scriptBase, CompileConfiguration config) { - return config.compiler == Compiler.dart2wasm ? ''' + return config.compiler == Compiler.dart2wasm + ? ''' { compileTarget: "${config.compiler.name}", renderer: "${config.renderer.name}", mainWasmPath: "$scriptBase.browser_test.dart.wasm", jsSupportRuntimePath: "$scriptBase.browser_test.dart.mjs", } -''' : ''' +''' + : ''' { compileTarget: "${config.compiler.name}", renderer: "${config.renderer.name}", @@ -546,9 +514,9 @@ class BrowserPlatform extends PlatformPlugin { final String test = '${p.withoutExtension(path)}.dart'; final String scriptBase = htmlEscape.convert(p.basename(test)); - final String buildConfigsString = suite.testBundle.compileConfigs.map( - (CompileConfiguration config) => _makeBuildConfigString(scriptBase, config) - ).join(',\n'); + final String buildConfigsString = suite.testBundle.compileConfigs + .map((CompileConfiguration config) => _makeBuildConfigString(scriptBase, config)) + .join(',\n'); final String bootstrapScript = ''' '''; - return shelf.Response.ok(''' + return shelf.Response.ok( + ''' @@ -584,11 +553,12 @@ class BrowserPlatform extends PlatformPlugin { $bootstrapScript - ''', headers: { - 'Content-Type': 'text/html', - if (suite.runConfig.crossOriginIsolated) - ...coopCoepHeaders - }); + ''', + headers: { + 'Content-Type': 'text/html', + if (suite.runConfig.crossOriginIsolated) ...coopCoepHeaders, + }, + ); } return shelf.Response.notFound('Not found.'); @@ -605,8 +575,12 @@ class BrowserPlatform extends PlatformPlugin { /// This will start a browser to load the suite if one isn't already running. /// Throws an [ArgumentError] if `platform.platform` isn't a browser. @override - Future load(String path, SuitePlatform platform, - SuiteConfiguration suiteConfig, Object message) async { + Future load( + String path, + SuitePlatform platform, + SuiteConfiguration suiteConfig, + Object message, + ) async { _checkNotClosed(); if (suiteConfig.precompiledPath == null) { throw Exception('This test platform only supports precompiled JS.'); @@ -624,13 +598,11 @@ class BrowserPlatform extends PlatformPlugin { final BrowserManager? browserManager = await _startBrowserManager(); if (browserManager == null) { - throw StateError( - 'Failed to initialize browser manager for ${browserEnvironment.name}'); + throw StateError('Failed to initialize browser manager for ${browserEnvironment.name}'); } _checkNotClosed(); - final RunnerSuite runnerSuite = - await browserManager.load(path, suiteUrl, suiteConfig, message); + final RunnerSuite runnerSuite = await browserManager.load(path, suiteUrl, suiteConfig, message); _checkNotClosed(); return runnerSuite; } @@ -646,19 +618,20 @@ class BrowserPlatform extends PlatformPlugin { return _browserManager!; } - final Completer completer = - Completer.sync(); - final String path = - _webSocketHandler.create(webSocketHandler(completer.complete)); + final Completer completer = Completer.sync(); + final String path = _webSocketHandler.create(webSocketHandler(completer.complete)); final Uri webSocketUrl = url.replace(scheme: 'ws').resolve(path); - final Uri hostUrl = url.resolve('host/index.html').replace( - queryParameters: { - 'managerUrl': webSocketUrl.toString(), - 'debug': isDebug.toString() - }); + final Uri hostUrl = url + .resolve('host/index.html') + .replace( + queryParameters: { + 'managerUrl': webSocketUrl.toString(), + 'debug': isDebug.toString(), + }, + ); final bool hasSourceMaps = suite.testBundle.compileConfigs.any( - (CompileConfiguration config) => config.compiler == Compiler.dart2js + (CompileConfiguration config) => config.compiler == Compiler.dart2js, ); final Future future = BrowserManager.start( browserEnvironment: browserEnvironment, @@ -696,12 +669,14 @@ class BrowserPlatform extends PlatformPlugin { Future close() { return _closeMemo.runOnce(() async { final List> futures = >[]; - futures.add(Future.microtask(() async { - if (_browserManager != null) { - final BrowserManager? result = await _browserManager; - await result?.close(); - } - })); + futures.add( + Future.microtask(() async { + if (_browserManager != null) { + final BrowserManager? result = await _browserManager; + await result?.close(); + } + }), + ); futures.add(server.close()); await Future.wait(futures); @@ -782,26 +757,26 @@ class BrowserManager { // Whenever we get a message, no matter which child channel it's for, we the // know browser is still running code which means the user isn't debugging. - _channel = MultiChannel(webSocket - .cast() - .transform(jsonDocument) - .changeStream((Stream stream) { - return stream.map((Object? message) { - if (!_closed) { - _timer.reset(); - } - for (final RunnerSuiteController controller in _controllers) { - controller.setDebugging(false); - } - - return message; - }); - })); + _channel = MultiChannel( + webSocket.cast().transform(jsonDocument).changeStream((Stream stream) { + return stream.map((Object? message) { + if (!_closed) { + _timer.reset(); + } + for (final RunnerSuiteController controller in _controllers) { + controller.setDebugging(false); + } + + return message; + }); + }), + ); _environment = _loadBrowserEnvironment(); _channel.stream.listen( - (dynamic message) => _onMessage(message as Map), - onDone: close); + (dynamic message) => _onMessage(message as Map), + onDone: close, + ); } final PackageConfig packageConfig; @@ -845,8 +820,7 @@ class BrowserManager { CancelableCompleter? _pauseCompleter; /// The controller for [_BrowserEnvironment.onRestart]. - final StreamController _onRestartController = - StreamController.broadcast(); + final StreamController _onRestartController = StreamController.broadcast(); /// The environment to attach to each suite. late final Future<_BrowserEnvironment> _environment; @@ -882,11 +856,7 @@ class BrowserManager { Directory? sourceMapDirectory, bool debug = false, }) async { - final Browser browser = await _newBrowser( - url, - browserEnvironment, - debug: debug, - ); + final Browser browser = await _newBrowser(url, browserEnvironment, debug: debug); return _startBrowserManager( browserEnvironment: browserEnvironment, url: url, @@ -921,24 +891,28 @@ class BrowserManager { completer.completeError(error, stackTrace); }); - future.then((WebSocketChannel webSocket) { - if (completer.isCompleted) { - return; - } - completer.complete(BrowserManager._( - packageConfig, - browser, - browserEnvironment, - sourceMapDirectory, - webSocket, - )); - }).catchError((Object error, StackTrace stackTrace) { - browser.close(); - if (completer.isCompleted) { - return null; - } - completer.completeError(error, stackTrace); - }); + future + .then((WebSocketChannel webSocket) { + if (completer.isCompleted) { + return; + } + completer.complete( + BrowserManager._( + packageConfig, + browser, + browserEnvironment, + sourceMapDirectory, + webSocket, + ), + ); + }) + .catchError((Object error, StackTrace stackTrace) { + browser.close(); + if (completer.isCompleted) { + return null; + } + completer.completeError(error, stackTrace); + }); return completer.future; } @@ -951,10 +925,7 @@ class BrowserManager { BrowserEnvironment browserEnvironment, { bool debug = false, }) { - return browserEnvironment.launchBrowserInstance( - url, - debug: debug, - ); + return browserEnvironment.launchBrowserInstance(url, debug: debug); } /// Loads [_BrowserEnvironment]. @@ -963,7 +934,7 @@ class BrowserManager { this, await _browser.vmServiceUrl, await _browser.remoteDebuggerUrl, - _onRestartController.stream + _onRestartController.stream, ); } @@ -972,13 +943,20 @@ class BrowserManager { /// [url] should be an HTML page with a reference to the JS-compiled test /// suite. [path] is the path of the original test suite file, which is used /// for reporting. [suiteConfig] is the configuration for the test suite. - Future load(String path, Uri url, SuiteConfiguration suiteConfig, - Object message) async { + Future load( + String path, + Uri url, + SuiteConfiguration suiteConfig, + Object message, + ) async { url = url.replace( - fragment: Uri.encodeFull(jsonEncode({ - 'metadata': suiteConfig.metadata.serialize(), - 'browser': _browserEnvironment.packageTestRuntime.identifier - }))); + fragment: Uri.encodeFull( + jsonEncode({ + 'metadata': suiteConfig.metadata.serialize(), + 'browser': _browserEnvironment.packageTestRuntime.identifier, + }), + ), + ); final int suiteID = _suiteID++; RunnerSuiteController? controller; @@ -987,8 +965,7 @@ class BrowserManager { return; } _controllers.remove(controller); - _channel.sink - .add({'command': 'closeSuite', 'id': suiteID}); + _channel.sink.add({'command': 'closeSuite', 'id': suiteID}); } // The virtual channel will be closed when the suite is closed, in which @@ -996,11 +973,13 @@ class BrowserManager { final VirtualChannel virtualChannel = _channel.virtualChannel(); final int suiteChannelID = virtualChannel.id; final StreamChannel suiteChannel = virtualChannel.transformStream( - StreamTransformer.fromHandlers( - handleDone: (EventSink sink) { - closeIframe(); - sink.close(); - })); + StreamTransformer.fromHandlers( + handleDone: (EventSink sink) { + closeIframe(); + sink.close(); + }, + ), + ); if (Configuration.current.pauseAfterLoad) { print('Browser loaded. Press enter to start tests...'); @@ -1012,36 +991,31 @@ class BrowserManager { 'command': 'loadSuite', 'url': url.toString(), 'id': suiteID, - 'channel': suiteChannelID + 'channel': suiteChannelID, }); try { controller = deserializeSuite( - path, - currentPlatform(_browserEnvironment.packageTestRuntime), - suiteConfig, - await _environment, - suiteChannel, - message); + path, + currentPlatform(_browserEnvironment.packageTestRuntime), + suiteConfig, + await _environment, + suiteChannel, + message, + ); if (_sourceMapDirectory == null) { // We don't have mapping for wasm yet. But we should send a message // to let the host page move forward. controller!.channel('test.browser.mapper').sink.add(null); } else { - final String sourceMapFileName = - '${p.basename(path)}.browser_test.dart.js.map'; + final String sourceMapFileName = '${p.basename(path)}.browser_test.dart.js.map'; final String pathToTest = p.dirname(path); - final String mapPath = p.join( - _sourceMapDirectory.path, - pathToTest, - sourceMapFileName - ); + final String mapPath = p.join(_sourceMapDirectory.path, pathToTest, sourceMapFileName); final Map packageMap = { - for (final Package p in packageConfig.packages) - p.name: p.packageUriRoot + for (final Package p in packageConfig.packages) p.name: p.packageUriRoot, }; final JSStackTraceMapper mapper = JSStackTraceMapper( await File(mapPath).readAsString(), @@ -1055,13 +1029,13 @@ class BrowserManager { _controllers.add(controller!); - final List> futures = >[ - controller!.suite - ]; + final List> futures = >[controller!.suite]; if (_browser.onUncaughtException != null) { - futures.add(_browser.onUncaughtException!.then( - (String error) => - throw Exception('Exception while loading suite: $error'))); + futures.add( + _browser.onUncaughtException!.then( + (String error) => throw Exception('Exception while loading suite: $error'), + ), + ); } final RunnerSuite suite = await Future.any(futures); @@ -1080,10 +1054,12 @@ class BrowserManager { return pauseCompleter.operation; } - pauseCompleter = CancelableCompleter(onCancel: () { - _channel.sink.add({'command': 'resume'}); - _pauseCompleter = null; - }); + pauseCompleter = CancelableCompleter( + onCancel: () { + _channel.sink.add({'command': 'resume'}); + _pauseCompleter = null; + }, + ); _pauseCompleter = pauseCompleter; pauseCompleter.operation.value.whenComplete(() { @@ -1115,23 +1091,22 @@ class BrowserManager { bool get supportsScreenshots => _browser.supportsScreenshots; - Future captureScreenshot(Rectangle region) => - _browser.captureScreenshot(region); + Future captureScreenshot(Rectangle region) => _browser.captureScreenshot(region); /// Closes the manager and releases any resources it owns, including closing /// the browser. Future close() => _closeMemoizer.runOnce(() { - if (Configuration.current.pauseAfterLoad) { - print('Test run finished. Press enter to close browser...'); - stdin.readLineSync(); - } - _closed = true; - _timer.cancel(); - _pauseCompleter?.complete(); - _pauseCompleter = null; - _controllers.clear(); - return _browser.close(); - }); + if (Configuration.current.pauseAfterLoad) { + print('Test run finished. Press enter to close browser...'); + stdin.readLineSync(); + } + _closed = true; + _timer.cancel(); + _pauseCompleter?.complete(); + _pauseCompleter = null; + _controllers.clear(); + return _browser.close(); + }); final AsyncMemoizer _closeMemoizer = AsyncMemoizer(); } @@ -1139,8 +1114,7 @@ class BrowserManager { /// /// All methods forward directly to [BrowserManager]. class _BrowserEnvironment implements Environment { - _BrowserEnvironment(this._manager, this.observatoryUrl, - this.remoteDebuggerUrl, this.onRestart); + _BrowserEnvironment(this._manager, this.observatoryUrl, this.remoteDebuggerUrl, this.onRestart); final BrowserManager _manager; diff --git a/lib/web_ui/dev/test_runner.dart b/lib/web_ui/dev/test_runner.dart index 672c70f837f72..c9321190624e2 100644 --- a/lib/web_ui/dev/test_runner.dart +++ b/lib/web_ui/dev/test_runner.dart @@ -26,19 +26,17 @@ class TestCommand extends Command with ArgUtils { argParser ..addFlag( 'start-paused', - help: 'Pauses the browser before running a test, giving you an ' + help: + 'Pauses the browser before running a test, giving you an ' 'opportunity to add breakpoints or inspect loaded code before ' 'running the code.', ) - ..addFlag( - 'verbose', - abbr: 'v', - help: 'Enable verbose output.' - ) + ..addFlag('verbose', abbr: 'v', help: 'Enable verbose output.') ..addFlag( 'watch', abbr: 'w', - help: 'Run in watch mode so the tests re-run whenever a change is ' + help: + 'Run in watch mode so the tests re-run whenever a change is ' 'made.', ) ..addFlag( @@ -46,40 +44,30 @@ class TestCommand extends Command with ArgUtils { help: 'Lists the bundles that would be compiled and the suites that ' 'will be run as part of this invocation, without actually ' - 'compiling or running them.' + 'compiling or running them.', ) ..addFlag( 'compile', help: 'Compile test bundles. If this is specified on its own, we will ' - 'only compile and not run the suites.' + 'only compile and not run the suites.', ) ..addFlag( 'run', help: 'Run test suites. If this is specified on its own, we will only ' - 'run the suites and not compile the bundles.' + 'run the suites and not compile the bundles.', ) ..addFlag( 'copy-artifacts', help: 'Copy artifacts needed for test suites. If this is specified on ' 'its own, we will only copy the artifacts and not compile or run' - 'the tests bundles or suites.' - ) - ..addFlag( - 'profile', - help: - 'Use artifacts from the profile build instead of release.' - ) - ..addFlag( - 'debug', - help: 'Use artifacts from the debug build instead of release.' - ) - ..addFlag( - 'dwarf', - help: 'Debug wasm modules using embedded DWARF data.' + 'the tests bundles or suites.', ) + ..addFlag('profile', help: 'Use artifacts from the profile build instead of release.') + ..addFlag('debug', help: 'Use artifacts from the debug build instead of release.') + ..addFlag('dwarf', help: 'Debug wasm modules using embedded DWARF data.') ..addFlag( 'require-skia-gold', help: @@ -93,47 +81,28 @@ class TestCommand extends Command with ArgUtils { '.dart_tool/goldens. Use this option to bulk-update all screenshots, ' 'for example, when a new browser version affects pixels.', ) - ..addMultiOption( - 'browser', - help: 'Filter test suites by browser.', - ) - ..addMultiOption( - 'compiler', - help: 'Filter test suites by compiler.', - ) - ..addMultiOption( - 'renderer', - help: 'Filter test suites by renderer.', - ) - ..addMultiOption( - 'canvaskit-variant', - help: 'Filter test suites by CanvasKit variant.', - ) - ..addMultiOption( - 'suite', - help: 'Filter test suites by suite name.', - ) - ..addMultiOption( - 'bundle', - help: 'Filter test suites by bundle name.', - ) + ..addMultiOption('browser', help: 'Filter test suites by browser.') + ..addMultiOption('compiler', help: 'Filter test suites by compiler.') + ..addMultiOption('renderer', help: 'Filter test suites by renderer.') + ..addMultiOption('canvaskit-variant', help: 'Filter test suites by CanvasKit variant.') + ..addMultiOption('suite', help: 'Filter test suites by suite name.') + ..addMultiOption('bundle', help: 'Filter test suites by bundle name.') ..addFlag( 'fail-early', - help: 'If set, causes the test runner to exit upon the first test ' - 'failure. If not set, the test runner will continue running ' - 'test despite failures and will report them after all tests ' - 'finish.', + help: + 'If set, causes the test runner to exit upon the first test ' + 'failure. If not set, the test runner will continue running ' + 'test despite failures and will report them after all tests ' + 'finish.', ) ..addOption( 'canvaskit-path', - help: 'Optional. The path to a local build of CanvasKit to use in ' - 'tests. If omitted, the test runner uses the default CanvasKit ' - 'build.', + help: + 'Optional. The path to a local build of CanvasKit to use in ' + 'tests. If omitted, the test runner uses the default CanvasKit ' + 'build.', ) - ..addFlag( - 'wasm', - help: 'Whether the test we are running are compiled to webassembly.' - ); + ..addFlag('wasm', help: 'Whether the test we are running are compiled to webassembly.'); } @override @@ -157,7 +126,8 @@ class TestCommand extends Command with ArgUtils { bool get isVerbose => boolArg('verbose'); /// The target test files to run. - List get targetFiles => argResults!.rest.map((String t) => FilePath.fromCwd(t)).toList(); + List get targetFiles => + argResults!.rest.map((String t) => FilePath.fromCwd(t)).toList(); /// When running screenshot tests, require Skia Gold to be available and /// reachable. @@ -171,7 +141,7 @@ class TestCommand extends Command with ArgUtils { String? get overridePathToCanvasKit => argResults!['canvaskit-path'] as String?; final FeltConfig config = FeltConfig.fromFile( - path.join(environment.webUiTestDir.path, 'felt_config.yaml') + path.join(environment.webUiTestDir.path, 'felt_config.yaml'), ); BrowserSuiteFilter? makeBrowserFilter() { @@ -179,7 +149,9 @@ class TestCommand extends Command with ArgUtils { if (browserArgs == null || browserArgs.isEmpty) { return null; } - final Set browserNames = Set.from(browserArgs.map((String arg) => BrowserName.values.byName(arg))); + final Set browserNames = Set.from( + browserArgs.map((String arg) => BrowserName.values.byName(arg)), + ); return BrowserSuiteFilter(allowList: browserNames); } @@ -188,7 +160,9 @@ class TestCommand extends Command with ArgUtils { if (compilerArgs == null || compilerArgs.isEmpty) { return null; } - final Set compilers = Set.from(compilerArgs.map((String arg) => Compiler.values.byName(arg))); + final Set compilers = Set.from( + compilerArgs.map((String arg) => Compiler.values.byName(arg)), + ); return CompilerFilter(allowList: compilers); } @@ -197,7 +171,9 @@ class TestCommand extends Command with ArgUtils { if (rendererArgs == null || rendererArgs.isEmpty) { return null; } - final Set renderers = Set.from(rendererArgs.map((String arg) => Renderer.values.byName(arg))); + final Set renderers = Set.from( + rendererArgs.map((String arg) => Renderer.values.byName(arg)), + ); return RendererFilter(allowList: renderers); } @@ -206,7 +182,9 @@ class TestCommand extends Command with ArgUtils { if (variantArgs == null || variantArgs.isEmpty) { return null; } - final Set variants = Set.from(variantArgs.map((String arg) => CanvasKitVariant.values.byName(arg))); + final Set variants = Set.from( + variantArgs.map((String arg) => CanvasKitVariant.values.byName(arg)), + ); return CanvasKitVariantFilter(allowList: variants); } @@ -232,7 +210,7 @@ class TestCommand extends Command with ArgUtils { } final Iterable allBundleNames = config.testSuites.map( - (TestSuite suite) => suite.testBundle.name + (TestSuite suite) => suite.testBundle.name, ); for (final String bundleName in bundleNameArgs) { if (!allBundleNames.contains(bundleName)) { @@ -292,30 +270,34 @@ class TestCommand extends Command with ArgUtils { print('Filtering suites...'); } final List filters = suiteFilters; - final List filteredSuites = config.testSuites.where((TestSuite suite) { - for (final SuiteFilter filter in filters) { - final SuiteFilterResult result = filter.filterSuite(suite); - if (!result.isAccepted) { - if (isVerbose) { - print(' ${suite.name.ansiCyan} rejected for reason: ${result.rejectReason}'); + final List filteredSuites = + config.testSuites.where((TestSuite suite) { + for (final SuiteFilter filter in filters) { + final SuiteFilterResult result = filter.filterSuite(suite); + if (!result.isAccepted) { + if (isVerbose) { + print(' ${suite.name.ansiCyan} rejected for reason: ${result.rejectReason}'); + } + return false; + } } - return false; - } - } - return true; - }).toList(); + return true; + }).toList(); return filteredSuites; } List _filterBundlesForSuites(List suites) { - final Set seenBundles = - Set.from(suites.map((TestSuite suite) => suite.testBundle)); + final Set seenBundles = Set.from( + suites.map((TestSuite suite) => suite.testBundle), + ); return config.testBundles.where((TestBundle bundle) => seenBundles.contains(bundle)).toList(); } ArtifactDependencies _artifactsForSuites(List suites) { - return suites.fold(ArtifactDependencies.none(), - (ArtifactDependencies deps, TestSuite suite) => deps | suite.artifactDependencies); + return suites.fold( + ArtifactDependencies.none(), + (ArtifactDependencies deps, TestSuite suite) => deps | suite.artifactDependencies, + ); } @override @@ -358,29 +340,27 @@ class TestCommand extends Command with ArgUtils { } final Set? testFiles = targetFiles.isEmpty ? null : Set.from(targetFiles); - final Pipeline testPipeline = Pipeline(steps: [ - if (isWatchMode) ClearTerminalScreenStep(), - if (shouldCopyArtifacts) CopyArtifactsStep(artifacts, runtimeMode: runtimeMode), - if (shouldCompile) - for (final TestBundle bundle in bundles) - CompileBundleStep( - bundle: bundle, - isVerbose: isVerbose, - testFiles: testFiles, - ), - if (shouldRun) - for (final TestSuite suite in filteredSuites) - RunSuiteStep( - suite, - startPaused: startPaused, - isVerbose: isVerbose, - doUpdateScreenshotGoldens: doUpdateScreenshotGoldens, - requireSkiaGold: requireSkiaGold, - overridePathToCanvasKit: overridePathToCanvasKit, - testFiles: testFiles, - useDwarf: boolArg('dwarf'), - ), - ]); + final Pipeline testPipeline = Pipeline( + steps: [ + if (isWatchMode) ClearTerminalScreenStep(), + if (shouldCopyArtifacts) CopyArtifactsStep(artifacts, runtimeMode: runtimeMode), + if (shouldCompile) + for (final TestBundle bundle in bundles) + CompileBundleStep(bundle: bundle, isVerbose: isVerbose, testFiles: testFiles), + if (shouldRun) + for (final TestSuite suite in filteredSuites) + RunSuiteStep( + suite, + startPaused: startPaused, + isVerbose: isVerbose, + doUpdateScreenshotGoldens: doUpdateScreenshotGoldens, + requireSkiaGold: requireSkiaGold, + overridePathToCanvasKit: overridePathToCanvasKit, + testFiles: testFiles, + useDwarf: boolArg('dwarf'), + ), + ], + ); try { await testPipeline.run(); @@ -388,7 +368,7 @@ class TestCommand extends Command with ArgUtils { print(''); print('Initial test succeeded!'); } - } catch(error, stackTrace) { + } catch (error, stackTrace) { if (isWatchMode) { // The error is printed but not rethrown in watch mode because // failures are expected. The idea is that the developer corrects the @@ -405,29 +385,27 @@ class TestCommand extends Command with ArgUtils { if (isWatchMode) { final FilePath dir = FilePath.fromWebUi(''); print(''); - print( - 'Watching ${dir.relativeToCwd}/lib and ${dir.relativeToCwd}/test to re-run tests'); + print('Watching ${dir.relativeToCwd}/lib and ${dir.relativeToCwd}/test to re-run tests'); print(''); await PipelineWatcher( - dir: dir.absolute, - pipeline: testPipeline, - ignore: (WatchEvent event) { - // Ignore font files that are copied whenever tests run. - if (event.path.endsWith('.ttf')) { - return true; - } + dir: dir.absolute, + pipeline: testPipeline, + ignore: (WatchEvent event) { + // Ignore font files that are copied whenever tests run. + if (event.path.endsWith('.ttf')) { + return true; + } - // React to changes in lib/ and test/ folders. - final String relativePath = - path.relative(event.path, from: dir.absolute); - if (path.isWithin('lib', relativePath) || - path.isWithin('test', relativePath)) { - return false; - } + // React to changes in lib/ and test/ folders. + final String relativePath = path.relative(event.path, from: dir.absolute); + if (path.isWithin('lib', relativePath) || path.isWithin('test', relativePath)) { + return false; + } - // Ignore anything else. - return true; - }).start(); + // Ignore anything else. + return true; + }, + ).start(); } return true; } diff --git a/lib/web_ui/dev/utils.dart b/lib/web_ui/dev/utils.dart index 78a79a96dbc91..23fd23b1bbed9 100644 --- a/lib/web_ui/dev/utils.dart +++ b/lib/web_ui/dev/utils.dart @@ -15,26 +15,20 @@ import 'environment.dart'; import 'exceptions.dart'; import 'felt_config.dart'; -enum RuntimeMode { - debug, - profile, - release, -} +enum RuntimeMode { debug, profile, release } class FilePath { - FilePath.fromCwd(String relativePath) - : _absolutePath = path.absolute(relativePath); + FilePath.fromCwd(String relativePath) : _absolutePath = path.absolute(relativePath); FilePath.fromWebUi(String relativePath) - : _absolutePath = path.join(environment.webUiRootDir.path, relativePath); + : _absolutePath = path.join(environment.webUiRootDir.path, relativePath); FilePath.fromTestSet(TestSet testSet, String relativePath) - : _absolutePath = path.join(getTestSetDirectory(testSet).path, relativePath); + : _absolutePath = path.join(getTestSetDirectory(testSet).path, relativePath); final String _absolutePath; String get absolute => _absolutePath; String get relativeToCwd => path.relative(_absolutePath); - String get relativeToWebUi => - path.relative(_absolutePath, from: environment.webUiRootDir.path); + String get relativeToWebUi => path.relative(_absolutePath, from: environment.webUiRootDir.path); @override bool operator ==(Object other) { @@ -146,7 +140,8 @@ class ProcessManager { required this.process, required bool evalOutput, required bool failureIsSuccess, - }) : _evalOutput = evalOutput, _failureIsSuccess = failureIsSuccess { + }) : _evalOutput = evalOutput, + _failureIsSuccess = failureIsSuccess { if (_evalOutput) { _forwardStream(process.stdout, _stdout); _forwardStream(process.stderr, _stderr); @@ -181,10 +176,7 @@ class ProcessManager { final StringBuffer _stderr = StringBuffer(); void _forwardStream(Stream> stream, StringSink buffer) { - stream - .transform(utf8.decoder) - .transform(const LineSplitter()) - .listen(buffer.writeln); + stream.transform(utf8.decoder).transform(const LineSplitter()).listen(buffer.writeln); } /// Waits for the [process] to exit. Returns the exit code. @@ -198,10 +190,7 @@ class ProcessManager { Future wait() async { final int exitCode = await process.exitCode; if (!_failureIsSuccess && exitCode != 0) { - _throwProcessException( - description: 'Sub-process failed.', - exitCode: exitCode, - ); + _throwProcessException(description: 'Sub-process failed.', exitCode: exitCode); } return exitCode; } @@ -212,7 +201,8 @@ class ProcessManager { if (!_evalOutput) { kill(); _throwProcessException( - description: 'Cannot eval process output. The process was launched ' + description: + 'Cannot eval process output. The process was launched ' 'with `evalOutput` set to false.', ); } @@ -252,11 +242,7 @@ class ProcessManager { /// Stringified standard output and standard error streams from a process. class ProcessOutput { - ProcessOutput({ - required this.exitCode, - required this.stdout, - required this.stderr, - }); + ProcessOutput({required this.exitCode, required this.stdout, required this.stderr}); /// The exit code of the process. final int exitCode; @@ -329,12 +315,11 @@ mixin ArgUtils on Command { } } -io.Directory getBuildDirectoryForRuntimeMode(RuntimeMode runtimeMode) => - switch (runtimeMode) { - RuntimeMode.debug => environment.wasmDebugUnoptOutDir, - RuntimeMode.profile => environment.wasmProfileOutDir, - RuntimeMode.release => environment.wasmReleaseOutDir, - }; +io.Directory getBuildDirectoryForRuntimeMode(RuntimeMode runtimeMode) => switch (runtimeMode) { + RuntimeMode.debug => environment.wasmDebugUnoptOutDir, + RuntimeMode.profile => environment.wasmProfileOutDir, + RuntimeMode.release => environment.wasmReleaseOutDir, +}; /// There might be proccesses started during the tests. /// @@ -378,31 +363,15 @@ Future cleanup() async { } io.Directory getTestSetDirectory(TestSet testSet) { - return io.Directory( - path.join( - environment.webUiTestDir.path, - testSet.directory, - ) - ); + return io.Directory(path.join(environment.webUiTestDir.path, testSet.directory)); } io.Directory getBundleBuildDirectory(TestBundle bundle) { - return io.Directory( - path.join( - environment.webUiBuildDir.path, - 'test_bundles', - bundle.name, - ) - ); + return io.Directory(path.join(environment.webUiBuildDir.path, 'test_bundles', bundle.name)); } io.Directory getSkiaGoldDirectoryForSuite(TestSuite suite) { - return io.Directory( - path.join( - environment.webUiSkiaGoldDirectory.path, - suite.name, - ) - ); + return io.Directory(path.join(environment.webUiSkiaGoldDirectory.path, suite.name)); } extension AnsiColors on String { @@ -416,8 +385,7 @@ extension AnsiColors on String { static const String _noColorCode = '\u001b[39m'; - String _wrapText(String prefix, String suffix) => shouldEscape - ? '$prefix$this$suffix' : this; + String _wrapText(String prefix, String suffix) => shouldEscape ? '$prefix$this$suffix' : this; String _colorText(String colorCode) => _wrapText(colorCode, _noColorCode); diff --git a/lib/web_ui/dev/webdriver_browser.dart b/lib/web_ui/dev/webdriver_browser.dart index 253d1244308fb..efe0e023f6fd1 100644 --- a/lib/web_ui/dev/webdriver_browser.dart +++ b/lib/web_ui/dev/webdriver_browser.dart @@ -30,24 +30,21 @@ abstract class WebDriverBrowserEnvironment extends BrowserEnvironment { return port; } - @override Future prepare() async { portNumber = await pickUnusedPort(); _driverProcess = await spawnDriverProcess(); - _driverProcess.stderr - .transform(utf8.decoder) - .transform(const LineSplitter()) - .listen((String error) { + _driverProcess.stderr.transform(utf8.decoder).transform(const LineSplitter()).listen(( + String error, + ) { print('[Webdriver][Error] $error'); }); - _driverProcess.stdout - .transform(utf8.decoder) - .transform(const LineSplitter()) - .listen((String log) { + _driverProcess.stdout.transform(utf8.decoder).transform(const LineSplitter()).listen(( + String log, + ) { print('[Webdriver] $log'); }); } @@ -62,7 +59,9 @@ abstract class WebDriverBrowserEnvironment extends BrowserEnvironment { while (true) { try { final WebDriver driver = await createDriver( - uri: driverUri, desired: {'browserName': packageTestRuntime.identifier}); + uri: driverUri, + desired: {'browserName': packageTestRuntime.identifier}, + ); return WebDriverBrowser(driver, url); } on SocketException { // Sometimes we may try to connect before the web driver port is ready. @@ -120,7 +119,12 @@ class WebDriverBrowser extends Browser { @override Future captureScreenshot(Rectangle region) async { final Image image = decodePng(await _driver.captureScreenshotAsList())!; - return copyCrop(image, region.left.round(), region.top.round(), - region.width.round(), region.height.round()); + return copyCrop( + image, + region.left.round(), + region.top.round(), + region.width.round(), + region.height.round(), + ); } } diff --git a/lib/web_ui/lib/canvas.dart b/lib/web_ui/lib/canvas.dart index 6e845799be22a..f9be002cdd5d1 100644 --- a/lib/web_ui/lib/canvas.dart +++ b/lib/web_ui/lib/canvas.dart @@ -4,22 +4,11 @@ part of ui; -enum PointMode { - points, - lines, - polygon, -} +enum PointMode { points, lines, polygon } -enum ClipOp { - difference, - intersect, -} +enum ClipOp { difference, intersect } -enum VertexMode { - triangles, - triangleStrip, - triangleFan, -} +enum VertexMode { triangles, triangleStrip, triangleFan } abstract class Vertices { factory Vertices( @@ -29,11 +18,13 @@ abstract class Vertices { List? textureCoordinates, List? indices, }) { - return engine.renderer.createVertices(mode, + return engine.renderer.createVertices( + mode, positions, textureCoordinates: textureCoordinates, colors: colors, - indices: indices); + indices: indices, + ); } factory Vertices.raw( VertexMode mode, @@ -42,11 +33,13 @@ abstract class Vertices { Float32List? textureCoordinates, Uint16List? indices, }) { - return engine.renderer.createVerticesRaw(mode, + return engine.renderer.createVerticesRaw( + mode, positions, textureCoordinates: textureCoordinates, colors: colors, - indices: indices); + indices: indices, + ); } void dispose(); @@ -61,7 +54,7 @@ abstract class PictureRecorder { abstract class Canvas { factory Canvas(PictureRecorder recorder, [Rect? cullRect]) => - engine.renderer.createCanvas(recorder, cullRect); + engine.renderer.createCanvas(recorder, cullRect); void save(); void saveLayer(Rect? bounds, Paint paint); void restore(); @@ -73,8 +66,7 @@ abstract class Canvas { void skew(double sx, double sy); void transform(Float64List matrix4); Float64List getTransform(); - void clipRect(Rect rect, - {ClipOp clipOp = ClipOp.intersect, bool doAntiAlias = true}); + void clipRect(Rect rect, {ClipOp clipOp = ClipOp.intersect, bool doAntiAlias = true}); void clipRRect(RRect rrect, {bool doAntiAlias = true}); void clipPath(Path path, {bool doAntiAlias = true}); Rect getLocalClipBounds(); @@ -87,8 +79,7 @@ abstract class Canvas { void drawDRRect(RRect outer, RRect inner, Paint paint); void drawOval(Rect rect, Paint paint); void drawCircle(Offset c, double radius, Paint paint); - void drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, - Paint paint); + void drawArc(Rect rect, double startAngle, double sweepAngle, bool useCenter, Paint paint); void drawPath(Path path, Paint paint); void drawImage(Image image, Offset offset, Paint paint); void drawImageRect(Image image, Rect src, Rect dst, Paint paint); @@ -117,12 +108,7 @@ abstract class Canvas { Rect? cullRect, Paint paint, ); - void drawShadow( - Path path, - Color color, - double elevation, - bool transparentOccluder, - ); + void drawShadow(Path path, Color color, double elevation, bool transparentOccluder); } typedef PictureEventCallback = void Function(Picture picture); @@ -137,19 +123,10 @@ abstract class Picture { int get approximateBytesUsed; } -enum PathFillType { - nonZero, - evenOdd, -} +enum PathFillType { nonZero, evenOdd } // Must be kept in sync with SkPathOp -enum PathOperation { - difference, - intersect, - union, - xor, - reverseDifference, -} +enum PathOperation { difference, intersect, union, xor, reverseDifference } abstract class PictureRasterizationException implements Exception { String get message; diff --git a/lib/web_ui/lib/channel_buffers.dart b/lib/web_ui/lib/channel_buffers.dart index 48d777ce2d539..0bc4857c6b55d 100644 --- a/lib/web_ui/lib/channel_buffers.dart +++ b/lib/web_ui/lib/channel_buffers.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - // This is identical to ../../../../ui/channel_buffers.dart with the // following exceptions: // @@ -12,7 +11,8 @@ part of ui; -typedef DrainChannelCallback = Future Function(ByteData? data, PlatformMessageResponseCallback callback); +typedef DrainChannelCallback = + Future Function(ByteData? data, PlatformMessageResponseCallback callback); typedef ChannelCallback = void Function(ByteData? data, PlatformMessageResponseCallback callback); @@ -22,7 +22,12 @@ class _ChannelCallbackRecord { final Zone _zone; void invoke(ByteData? dataArg, PlatformMessageResponseCallback callbackArg) { - engine.invoke2(_callback, _zone, dataArg, callbackArg); + engine.invoke2( + _callback, + _zone, + dataArg, + callbackArg, + ); } } @@ -41,7 +46,7 @@ class _StoredMessage { } class _Channel { - _Channel([ this._capacity = ChannelBuffers.kDefaultBufferSize ]) + _Channel([this._capacity = ChannelBuffers.kDefaultBufferSize]) : _queue = collection.ListQueue<_StoredMessage>(_capacity); final collection.ListQueue<_StoredMessage> _queue; @@ -136,7 +141,7 @@ class ChannelBuffers { 'framework has had an opportunity to register a listener. See the ChannelBuffers ' 'API documentation for details on how to configure the channel to expect more ' 'messages, or to expect messages to get discarded:\n' - ' https://api.flutter.dev/flutter/dart-ui/ChannelBuffers-class.html' + ' https://api.flutter.dev/flutter/dart-ui/ChannelBuffers-class.html', ); return true; }()); @@ -165,7 +170,8 @@ class ChannelBuffers { void handleMessage(ByteData data) { final Uint8List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - if (bytes[0] == 0x07) { // 7 = value code for string + if (bytes[0] == 0x07) { + // 7 = value code for string final int methodNameLength = bytes[1]; if (methodNameLength >= 254) { throw Exception('Unrecognized message sent to $kControlChannelName (method name too long)'); @@ -176,51 +182,71 @@ class ChannelBuffers { switch (methodName) { case 'resize': if (bytes[index] != 0x0C) { - throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (arguments must be a two-element list, channel name and new capacity)"); + throw Exception( + "Invalid arguments for 'resize' method sent to $kControlChannelName (arguments must be a two-element list, channel name and new capacity)", + ); } index += 1; if (bytes[index] < 0x02) { - throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (arguments must be a two-element list, channel name and new capacity)"); + throw Exception( + "Invalid arguments for 'resize' method sent to $kControlChannelName (arguments must be a two-element list, channel name and new capacity)", + ); } index += 1; if (bytes[index] != 0x07) { - throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (first argument must be a string)"); + throw Exception( + "Invalid arguments for 'resize' method sent to $kControlChannelName (first argument must be a string)", + ); } index += 1; final int channelNameLength = bytes[index]; if (channelNameLength >= 254) { - throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (channel name must be less than 254 characters long)"); + throw Exception( + "Invalid arguments for 'resize' method sent to $kControlChannelName (channel name must be less than 254 characters long)", + ); } index += 1; final String channelName = utf8.decode(bytes.sublist(index, index + channelNameLength)); index += channelNameLength; if (bytes[index] != 0x03) { - throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (second argument must be an integer in the range 0 to 2147483647)"); + throw Exception( + "Invalid arguments for 'resize' method sent to $kControlChannelName (second argument must be an integer in the range 0 to 2147483647)", + ); } index += 1; resize(channelName, data.getUint32(index, Endian.host)); case 'overflow': if (bytes[index] != 0x0C) { - throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (arguments must be a two-element list, channel name and flag state)"); + throw Exception( + "Invalid arguments for 'overflow' method sent to $kControlChannelName (arguments must be a two-element list, channel name and flag state)", + ); } index += 1; if (bytes[index] < 0x02) { - throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (arguments must be a two-element list, channel name and flag state)"); + throw Exception( + "Invalid arguments for 'overflow' method sent to $kControlChannelName (arguments must be a two-element list, channel name and flag state)", + ); } index += 1; if (bytes[index] != 0x07) { - throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (first argument must be a string)"); + throw Exception( + "Invalid arguments for 'overflow' method sent to $kControlChannelName (first argument must be a string)", + ); } index += 1; final int channelNameLength = bytes[index]; if (channelNameLength >= 254) { - throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (channel name must be less than 254 characters long)"); + throw Exception( + "Invalid arguments for 'overflow' method sent to $kControlChannelName (channel name must be less than 254 characters long)", + ); } index += 1; final String channelName = utf8.decode(bytes.sublist(index, index + channelNameLength)); index += channelNameLength; if (bytes[index] != 0x01 && bytes[index] != 0x02) { - throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (second argument must be a boolean)"); + throw Exception( + "Invalid arguments for 'overflow' method sent to $kControlChannelName (second argument must be a boolean)", + ); } allowOverflow(channelName, bytes[index] == 0x01); default: @@ -228,7 +254,7 @@ class ChannelBuffers { } } else { final List parts = utf8.decode(bytes).split('\r'); - if (parts.length == 1 + /*arity=*/2 && parts[0] == 'resize') { + if (parts.length == 1 + /*arity=*/ 2 && parts[0] == 'resize') { resize(parts[1], int.parse(parts[2])); } else { throw Exception('Unrecognized message $parts sent to $kControlChannelName.'); diff --git a/lib/web_ui/lib/compositing.dart b/lib/web_ui/lib/compositing.dart index 2f0d558f5bb34..27713aad31ddf 100644 --- a/lib/web_ui/lib/compositing.dart +++ b/lib/web_ui/lib/compositing.dart @@ -31,18 +31,10 @@ abstract class BackdropFilterEngineLayer implements EngineLayer {} abstract class ShaderMaskEngineLayer implements EngineLayer {} abstract class SceneBuilder { - factory SceneBuilder() => - engine.renderer.createSceneBuilder(); + factory SceneBuilder() => engine.renderer.createSceneBuilder(); - OffsetEngineLayer pushOffset( - double dx, - double dy, { - OffsetEngineLayer? oldLayer, - }); - TransformEngineLayer pushTransform( - Float64List matrix4, { - TransformEngineLayer? oldLayer, - }); + OffsetEngineLayer pushOffset(double dx, double dy, {OffsetEngineLayer? oldLayer}); + TransformEngineLayer pushTransform(Float64List matrix4, {TransformEngineLayer? oldLayer}); ClipRectEngineLayer pushClipRect( Rect rect, { Clip clipBehavior = Clip.antiAlias, @@ -63,10 +55,7 @@ abstract class SceneBuilder { Offset offset = Offset.zero, OpacityEngineLayer? oldLayer, }); - ColorFilterEngineLayer pushColorFilter( - ColorFilter filter, { - ColorFilterEngineLayer? oldLayer, - }); + ColorFilterEngineLayer pushColorFilter(ColorFilter filter, {ColorFilterEngineLayer? oldLayer}); ImageFilterEngineLayer pushImageFilter( ImageFilter filter, { Offset offset = Offset.zero, diff --git a/lib/web_ui/lib/geometry.dart b/lib/web_ui/lib/geometry.dart index ff5abf31bf51d..31dd463c2ba0b 100644 --- a/lib/web_ui/lib/geometry.dart +++ b/lib/web_ui/lib/geometry.dart @@ -23,9 +23,7 @@ abstract class OffsetBase { bool operator >=(OffsetBase other) => _dx >= other._dx && _dy >= other._dy; @override bool operator ==(Object other) { - return other is OffsetBase - && other._dx == _dx - && other._dy == _dy; + return other is OffsetBase && other._dx == _dx && other._dy == _dy; } @override @@ -37,7 +35,7 @@ abstract class OffsetBase { class Offset extends OffsetBase { const Offset(super.dx, super.dy); - factory Offset.fromDirection(double direction, [ double distance = 1.0 ]) { + factory Offset.fromDirection(double direction, [double distance = 1.0]) { return Offset(distance * math.cos(direction), distance * math.sin(direction)); } double get dx => _dx; @@ -49,13 +47,15 @@ class Offset extends OffsetBase { // This is included for completeness, because [Size.infinite] exists. static const Offset infinite = Offset(double.infinity, double.infinity); Offset scale(double scaleX, double scaleY) => Offset(dx * scaleX, dy * scaleY); - Offset translate(double translateX, double translateY) => Offset(dx + translateX, dy + translateY); + Offset translate(double translateX, double translateY) => + Offset(dx + translateX, dy + translateY); Offset operator -() => Offset(-dx, -dy); Offset operator -(Offset other) => Offset(dx - other.dx, dy - other.dy); Offset operator +(Offset other) => Offset(dx + other.dx, dy + other.dy); Offset operator *(double operand) => Offset(dx * operand, dy * operand); Offset operator /(double operand) => Offset(dx / operand, dy / operand); - Offset operator ~/(double operand) => Offset((dx ~/ operand).toDouble(), (dy ~/ operand).toDouble()); + Offset operator ~/(double operand) => + Offset((dx ~/ operand).toDouble(), (dy ~/ operand).toDouble()); Offset operator %(double operand) => Offset(dx % operand, dy % operand); Rect operator &(Size other) => Rect.fromLTWH(dx, dy, other.width, other.height); static Offset? lerp(Offset? a, Offset? b, double t) { @@ -76,9 +76,7 @@ class Offset extends OffsetBase { @override bool operator ==(Object other) { - return other is Offset - && other.dx == dx - && other.dy == dy; + return other is Offset && other.dx == dx && other.dy == dy; } @override @@ -127,7 +125,8 @@ class Size extends OffsetBase { Size operator +(Offset other) => Size(width + other.dx, height + other.dy); Size operator *(double operand) => Size(width * operand, height * operand); Size operator /(double operand) => Size(width / operand, height / operand); - Size operator ~/(double operand) => Size((width ~/ operand).toDouble(), (height ~/ operand).toDouble()); + Size operator ~/(double operand) => + Size((width ~/ operand).toDouble(), (height ~/ operand).toDouble()); Size operator %(double operand) => Size(width % operand, height % operand); double get shortestSide => math.min(width.abs(), height.abs()); double get longestSide => math.max(width.abs(), height.abs()); @@ -167,9 +166,7 @@ class Size extends OffsetBase { // We don't compare the runtimeType because of _DebugSize in the framework. @override bool operator ==(Object other) { - return other is Size - && other._dx == _dx - && other._dy == _dy; + return other is Size && other._dx == _dx && other._dy == _dy; } @override @@ -183,30 +180,26 @@ class Rect { const Rect.fromLTRB(this.left, this.top, this.right, this.bottom); const Rect.fromLTWH(double left, double top, double width, double height) - : this.fromLTRB(left, top, left + width, top + height); + : this.fromLTRB(left, top, left + width, top + height); - Rect.fromCircle({ required Offset center, required double radius }) - : this.fromCenter( - center: center, - width: radius * 2, - height: radius * 2, - ); + Rect.fromCircle({required Offset center, required double radius}) + : this.fromCenter(center: center, width: radius * 2, height: radius * 2); - Rect.fromCenter({ required Offset center, required double width, required double height }) - : this.fromLTRB( - center.dx - width / 2, - center.dy - height / 2, - center.dx + width / 2, - center.dy + height / 2, - ); + Rect.fromCenter({required Offset center, required double width, required double height}) + : this.fromLTRB( + center.dx - width / 2, + center.dy - height / 2, + center.dx + width / 2, + center.dy + height / 2, + ); Rect.fromPoints(Offset a, Offset b) - : this.fromLTRB( - math.min(a.dx, b.dx), - math.min(a.dy, b.dy), - math.max(a.dx, b.dx), - math.max(a.dy, b.dy), - ); + : this.fromLTRB( + math.min(a.dx, b.dx), + math.min(a.dy, b.dy), + math.max(a.dx, b.dx), + math.max(a.dy, b.dy), + ); final double left; final double top; @@ -219,13 +212,18 @@ class Rect { static const Rect zero = Rect.fromLTRB(0.0, 0.0, 0.0, 0.0); static const double _giantScalar = 1.0E+9; // matches kGiantRect from layer.h - static const Rect largest = Rect.fromLTRB(-_giantScalar, -_giantScalar, _giantScalar, _giantScalar); + static const Rect largest = Rect.fromLTRB( + -_giantScalar, + -_giantScalar, + _giantScalar, + _giantScalar, + ); // included for consistency with Offset and Size bool get isInfinite { - return left >= double.infinity - || top >= double.infinity - || right >= double.infinity - || bottom >= double.infinity; + return left >= double.infinity || + top >= double.infinity || + right >= double.infinity || + bottom >= double.infinity; } bool get isFinite => left.isFinite && top.isFinite && right.isFinite && bottom.isFinite; @@ -235,7 +233,12 @@ class Rect { } Rect translate(double translateX, double translateY) { - return Rect.fromLTRB(left + translateX, top + translateY, right + translateX, bottom + translateY); + return Rect.fromLTRB( + left + translateX, + top + translateY, + right + translateX, + bottom + translateY, + ); } Rect inflate(double delta) { @@ -316,18 +319,19 @@ class Rect { if (runtimeType != other.runtimeType) { return false; } - return other is Rect - && other.left == left - && other.top == top - && other.right == right - && other.bottom == bottom; + return other is Rect && + other.left == left && + other.top == top && + other.right == right && + other.bottom == bottom; } @override int get hashCode => Object.hash(left, top, right, bottom); @override - String toString() => 'Rect.fromLTRB(${left.toStringAsFixed(1)}, ${top.toStringAsFixed(1)}, ${right.toStringAsFixed(1)}, ${bottom.toStringAsFixed(1)})'; + String toString() => + 'Rect.fromLTRB(${left.toStringAsFixed(1)}, ${top.toStringAsFixed(1)}, ${right.toStringAsFixed(1)}, ${bottom.toStringAsFixed(1)})'; } class Radius { @@ -344,23 +348,21 @@ class Radius { clampDouble(y, minimum.y, maximum.y), ); } - Radius clampValues({ - double? minimumX, - double? minimumY, - double? maximumX, - double? maximumY, - }) { + + Radius clampValues({double? minimumX, double? minimumY, double? maximumX, double? maximumY}) { return Radius.elliptical( clampDouble(x, minimumX ?? -double.infinity, maximumX ?? double.infinity), clampDouble(y, minimumY ?? -double.infinity, maximumY ?? double.infinity), ); } + Radius operator -() => Radius.elliptical(-x, -y); Radius operator -(Radius other) => Radius.elliptical(x - other.x, y - other.y); Radius operator +(Radius other) => Radius.elliptical(x + other.x, y + other.y); Radius operator *(double operand) => Radius.elliptical(x * operand, y * operand); Radius operator /(double operand) => Radius.elliptical(x / operand, y / operand); - Radius operator ~/(double operand) => Radius.elliptical((x ~/ operand).toDouble(), (y ~/ operand).toDouble()); + Radius operator ~/(double operand) => + Radius.elliptical((x ~/ operand).toDouble(), (y ~/ operand).toDouble()); Radius operator %(double operand) => Radius.elliptical(x % operand, y % operand); static Radius? lerp(Radius? a, Radius? b, double t) { if (b == null) { @@ -374,10 +376,7 @@ class Radius { if (a == null) { return Radius.elliptical(b.x * t, b.y * t); } else { - return Radius.elliptical( - _lerpDouble(a.x, b.x, t), - _lerpDouble(a.y, b.y, t), - ); + return Radius.elliptical(_lerpDouble(a.x, b.x, t), _lerpDouble(a.y, b.y, t)); } } } @@ -391,9 +390,7 @@ class Radius { return false; } - return other is Radius - && other.x == x - && other.y == y; + return other is Radius && other.x == x && other.y == y; } @override @@ -401,9 +398,10 @@ class Radius { @override String toString() { - return x == y ? 'Radius.circular(${x.toStringAsFixed(1)})' : - 'Radius.elliptical(${x.toStringAsFixed(1)}, ' - '${y.toStringAsFixed(1)})'; + return x == y + ? 'Radius.circular(${x.toStringAsFixed(1)})' + : 'Radius.elliptical(${x.toStringAsFixed(1)}, ' + '${y.toStringAsFixed(1)})'; } } @@ -431,13 +429,8 @@ class RRect { uniformRadii: radiusX == radiusY, ); - RRect.fromLTRBR( - double left, - double top, - double right, - double bottom, - Radius radius, - ) : this._raw( + RRect.fromLTRBR(double left, double top, double right, double bottom, Radius radius) + : this._raw( top: top, left: left, right: right, @@ -497,26 +490,27 @@ class RRect { Radius bottomRight = Radius.zero, Radius bottomLeft = Radius.zero, }) : this._raw( - top: top, - left: left, - right: right, - bottom: bottom, - tlRadiusX: topLeft.x, - tlRadiusY: topLeft.y, - trRadiusX: topRight.x, - trRadiusY: topRight.y, - blRadiusX: bottomLeft.x, - blRadiusY: bottomLeft.y, - brRadiusX: bottomRight.x, - brRadiusY: bottomRight.y, - uniformRadii: topLeft.x == topLeft.y && - topLeft.x == topRight.x && - topLeft.x == topRight.y && - topLeft.x == bottomLeft.x && - topLeft.x == bottomLeft.y && - topLeft.x == bottomRight.x && - topLeft.x == bottomRight.y, - ); + top: top, + left: left, + right: right, + bottom: bottom, + tlRadiusX: topLeft.x, + tlRadiusY: topLeft.y, + trRadiusX: topRight.x, + trRadiusY: topRight.y, + blRadiusX: bottomLeft.x, + blRadiusY: bottomLeft.y, + brRadiusX: bottomRight.x, + brRadiusY: bottomRight.y, + uniformRadii: + topLeft.x == topLeft.y && + topLeft.x == topRight.x && + topLeft.x == topRight.y && + topLeft.x == bottomLeft.x && + topLeft.x == bottomLeft.y && + topLeft.x == bottomRight.x && + topLeft.x == bottomRight.y, + ); RRect.fromRectAndCorners( Rect rect, { @@ -525,26 +519,27 @@ class RRect { Radius bottomRight = Radius.zero, Radius bottomLeft = Radius.zero, }) : this._raw( - top: rect.top, - left: rect.left, - right: rect.right, - bottom: rect.bottom, - tlRadiusX: topLeft.x, - tlRadiusY: topLeft.y, - trRadiusX: topRight.x, - trRadiusY: topRight.y, - blRadiusX: bottomLeft.x, - blRadiusY: bottomLeft.y, - brRadiusX: bottomRight.x, - brRadiusY: bottomRight.y, - uniformRadii: topLeft.x == topLeft.y && - topLeft.x == topRight.x && - topLeft.x == topRight.y && - topLeft.x == bottomLeft.x && - topLeft.x == bottomLeft.y && - topLeft.x == bottomRight.x && - topLeft.x == bottomRight.y, - ); + top: rect.top, + left: rect.left, + right: rect.right, + bottom: rect.bottom, + tlRadiusX: topLeft.x, + tlRadiusY: topLeft.y, + trRadiusX: topRight.x, + trRadiusY: topRight.y, + blRadiusX: bottomLeft.x, + blRadiusY: bottomLeft.y, + brRadiusX: bottomRight.x, + brRadiusY: bottomRight.y, + uniformRadii: + topLeft.x == topLeft.y && + topLeft.x == topRight.x && + topLeft.x == topRight.y && + topLeft.x == bottomLeft.x && + topLeft.x == bottomLeft.y && + topLeft.x == bottomRight.x && + topLeft.x == bottomRight.y, + ); const RRect._raw({ this.left = 0.0, @@ -560,15 +555,15 @@ class RRect { this.blRadiusX = 0.0, this.blRadiusY = 0.0, bool uniformRadii = false, - }) : assert(tlRadiusX >= 0), - assert(tlRadiusY >= 0), - assert(trRadiusX >= 0), - assert(trRadiusY >= 0), - assert(brRadiusX >= 0), - assert(brRadiusY >= 0), - assert(blRadiusX >= 0), - assert(blRadiusY >= 0), - webOnlyUniformRadii = uniformRadii; + }) : assert(tlRadiusX >= 0), + assert(tlRadiusY >= 0), + assert(trRadiusX >= 0), + assert(trRadiusY >= 0), + assert(brRadiusX >= 0), + assert(brRadiusY >= 0), + assert(blRadiusX >= 0), + assert(blRadiusY >= 0), + webOnlyUniformRadii = uniformRadii; final double left; final double top; @@ -640,7 +635,7 @@ class RRect { left + leftRadius * kInsetFactor, top + topRadius * kInsetFactor, right - rightRadius * kInsetFactor, - bottom - bottomRadius * kInsetFactor + bottom - bottomRadius * kInsetFactor, ); } @@ -653,62 +648,62 @@ class RRect { left + leftRadius, top + topRadius, right - rightRadius, - bottom - bottomRadius + bottom - bottomRadius, ); } Rect get wideMiddleRect { final double topRadius = math.max(tlRadiusY, trRadiusY); final double bottomRadius = math.max(brRadiusY, blRadiusY); - return Rect.fromLTRB( - left, - top + topRadius, - right, - bottom - bottomRadius - ); + return Rect.fromLTRB(left, top + topRadius, right, bottom - bottomRadius); } Rect get tallMiddleRect { final double leftRadius = math.max(blRadiusX, tlRadiusX); final double rightRadius = math.max(trRadiusX, brRadiusX); - return Rect.fromLTRB( - left + leftRadius, - top, - right - rightRadius, - bottom - ); + return Rect.fromLTRB(left + leftRadius, top, right - rightRadius, bottom); } bool get isEmpty => left >= right || top >= bottom; bool get isFinite => left.isFinite && top.isFinite && right.isFinite && bottom.isFinite; bool get isRect { - return (tlRadiusX == 0.0 || tlRadiusY == 0.0) - && (trRadiusX == 0.0 || trRadiusY == 0.0) - && (blRadiusX == 0.0 || blRadiusY == 0.0) - && (brRadiusX == 0.0 || brRadiusY == 0.0); + return (tlRadiusX == 0.0 || tlRadiusY == 0.0) && + (trRadiusX == 0.0 || trRadiusY == 0.0) && + (blRadiusX == 0.0 || blRadiusY == 0.0) && + (brRadiusX == 0.0 || brRadiusY == 0.0); } bool get isStadium { - return tlRadius == trRadius - && trRadius == brRadius - && brRadius == blRadius - && (width <= 2.0 * tlRadiusX || height <= 2.0 * tlRadiusY); + return tlRadius == trRadius && + trRadius == brRadius && + brRadius == blRadius && + (width <= 2.0 * tlRadiusX || height <= 2.0 * tlRadiusY); } bool get isEllipse { - return tlRadius == trRadius - && trRadius == brRadius - && brRadius == blRadius - && width <= 2.0 * tlRadiusX - && height <= 2.0 * tlRadiusY; + return tlRadius == trRadius && + trRadius == brRadius && + brRadius == blRadius && + width <= 2.0 * tlRadiusX && + height <= 2.0 * tlRadiusY; } bool get isCircle => width == height && isEllipse; double get shortestSide => math.min(width.abs(), height.abs()); double get longestSide => math.max(width.abs(), height.abs()); - bool get hasNaN => left.isNaN || top.isNaN || right.isNaN || bottom.isNaN || - trRadiusX.isNaN || trRadiusY.isNaN || tlRadiusX.isNaN || tlRadiusY.isNaN || - brRadiusX.isNaN || brRadiusY.isNaN || blRadiusX.isNaN || blRadiusY.isNaN; + bool get hasNaN => + left.isNaN || + top.isNaN || + right.isNaN || + bottom.isNaN || + trRadiusX.isNaN || + trRadiusY.isNaN || + tlRadiusX.isNaN || + tlRadiusY.isNaN || + brRadiusX.isNaN || + brRadiusY.isNaN || + blRadiusX.isNaN || + blRadiusY.isNaN; Offset get center => Offset(left + width / 2.0, top + height / 2.0); // Returns the minimum between min and scale to which radius1 and radius2 @@ -776,26 +771,22 @@ class RRect { double radiusY; // check whether point is in one of the rounded corner areas // x, y -> translate to ellipse center - if (point.dx < left + scaled.tlRadiusX && - point.dy < top + scaled.tlRadiusY) { + if (point.dx < left + scaled.tlRadiusX && point.dy < top + scaled.tlRadiusY) { x = point.dx - left - scaled.tlRadiusX; y = point.dy - top - scaled.tlRadiusY; radiusX = scaled.tlRadiusX; radiusY = scaled.tlRadiusY; - } else if (point.dx > right - scaled.trRadiusX && - point.dy < top + scaled.trRadiusY) { + } else if (point.dx > right - scaled.trRadiusX && point.dy < top + scaled.trRadiusY) { x = point.dx - right + scaled.trRadiusX; y = point.dy - top - scaled.trRadiusY; radiusX = scaled.trRadiusX; radiusY = scaled.trRadiusY; - } else if (point.dx > right - scaled.brRadiusX && - point.dy > bottom - scaled.brRadiusY) { + } else if (point.dx > right - scaled.brRadiusX && point.dy > bottom - scaled.brRadiusY) { x = point.dx - right + scaled.brRadiusX; y = point.dy - bottom + scaled.brRadiusY; radiusX = scaled.brRadiusX; radiusY = scaled.brRadiusY; - } else if (point.dx < left + scaled.blRadiusX && - point.dy > bottom - scaled.blRadiusY) { + } else if (point.dx < left + scaled.blRadiusX && point.dy > bottom - scaled.blRadiusY) { x = point.dx - left - scaled.blRadiusX; y = point.dy - bottom + scaled.blRadiusY; radiusX = scaled.blRadiusX; @@ -877,47 +868,57 @@ class RRect { if (runtimeType != other.runtimeType) { return false; } - return other is RRect - && other.left == left - && other.top == top - && other.right == right - && other.bottom == bottom - && other.tlRadiusX == tlRadiusX - && other.tlRadiusY == tlRadiusY - && other.trRadiusX == trRadiusX - && other.trRadiusY == trRadiusY - && other.blRadiusX == blRadiusX - && other.blRadiusY == blRadiusY - && other.brRadiusX == brRadiusX - && other.brRadiusY == brRadiusY; + return other is RRect && + other.left == left && + other.top == top && + other.right == right && + other.bottom == bottom && + other.tlRadiusX == tlRadiusX && + other.tlRadiusY == tlRadiusY && + other.trRadiusX == trRadiusX && + other.trRadiusY == trRadiusY && + other.blRadiusX == blRadiusX && + other.blRadiusY == blRadiusY && + other.brRadiusX == brRadiusX && + other.brRadiusY == brRadiusY; } @override - int get hashCode => Object.hash(left, top, right, bottom, - tlRadiusX, tlRadiusY, trRadiusX, trRadiusY, - blRadiusX, blRadiusY, brRadiusX, brRadiusY); + int get hashCode => Object.hash( + left, + top, + right, + bottom, + tlRadiusX, + tlRadiusY, + trRadiusX, + trRadiusY, + blRadiusX, + blRadiusY, + brRadiusX, + brRadiusY, + ); @override String toString() { - final String rect = '${left.toStringAsFixed(1)}, ' - '${top.toStringAsFixed(1)}, ' - '${right.toStringAsFixed(1)}, ' - '${bottom.toStringAsFixed(1)}'; - if (tlRadius == trRadius && - trRadius == brRadius && - brRadius == blRadius) { + final String rect = + '${left.toStringAsFixed(1)}, ' + '${top.toStringAsFixed(1)}, ' + '${right.toStringAsFixed(1)}, ' + '${bottom.toStringAsFixed(1)}'; + if (tlRadius == trRadius && trRadius == brRadius && brRadius == blRadius) { if (tlRadius.x == tlRadius.y) { return 'RRect.fromLTRBR($rect, ${tlRadius.x.toStringAsFixed(1)})'; } return 'RRect.fromLTRBXY($rect, ${tlRadius.x.toStringAsFixed(1)}, ${tlRadius.y.toStringAsFixed(1)})'; } return 'RRect.fromLTRBAndCorners(' - '$rect, ' - 'topLeft: $tlRadius, ' - 'topRight: $trRadius, ' - 'bottomRight: $brRadius, ' - 'bottomLeft: $blRadius' - ')'; + '$rect, ' + 'topLeft: $tlRadius, ' + 'topRight: $trRadius, ' + 'bottomRight: $brRadius, ' + 'bottomLeft: $blRadius' + ')'; } } // Modeled after Skia's SkRSXform. diff --git a/lib/web_ui/lib/initialization.dart b/lib/web_ui/lib/initialization.dart index 43dc09e7bd0ca..15b69a61b8621 100644 --- a/lib/web_ui/lib/initialization.dart +++ b/lib/web_ui/lib/initialization.dart @@ -25,10 +25,7 @@ part of ui; // TODO(mdebbar): Deprecate this and remove it. // https://github.com/flutter/flutter/issues/127395 -Future webOnlyWarmupEngine({ - VoidCallback? registerPlugins, - VoidCallback? runApp, -}) { +Future webOnlyWarmupEngine({VoidCallback? registerPlugins, VoidCallback? runApp}) { assert(() { engine.printWarning( 'The webOnlyWarmupEngine API is deprecated and will be removed in a ' @@ -36,10 +33,7 @@ Future webOnlyWarmupEngine({ ); return true; }()); - return ui_web.bootstrapEngine( - registerPlugins: registerPlugins, - runApp: runApp, - ); + return ui_web.bootstrapEngine(registerPlugins: registerPlugins, runApp: runApp); } // TODO(mdebbar): Deprecate this and remove it. diff --git a/lib/web_ui/lib/key.dart b/lib/web_ui/lib/key.dart index 35fbf42d9a5a0..9af819928b835 100644 --- a/lib/web_ui/lib/key.dart +++ b/lib/web_ui/lib/key.dart @@ -133,33 +133,34 @@ class KeyData { // JavaScript only support 32-bit bitwise operations and needs to use // division instead. final int planeNum = (logical / 0x100000000).floor(); - final String planeDescription = (() { - switch (planeNum) { - case 0x000: - return ' (Unicode)'; - case 0x001: - return ' (Unprintable)'; - case 0x002: - return ' (Flutter)'; - case 0x011: - return ' (Android)'; - case 0x012: - return ' (Fuchsia)'; - case 0x013: - return ' (iOS)'; - case 0x014: - return ' (macOS)'; - case 0x015: - return ' (GTK)'; - case 0x016: - return ' (Windows)'; - case 0x017: - return ' (Web)'; - case 0x018: - return ' (GLFW)'; - } - return ''; - })(); + final String planeDescription = + (() { + switch (planeNum) { + case 0x000: + return ' (Unicode)'; + case 0x001: + return ' (Unprintable)'; + case 0x002: + return ' (Flutter)'; + case 0x011: + return ' (Android)'; + case 0x012: + return ' (Fuchsia)'; + case 0x013: + return ' (iOS)'; + case 0x014: + return ' (macOS)'; + case 0x015: + return ' (GTK)'; + case 0x016: + return ' (Windows)'; + case 0x017: + return ' (Web)'; + case 0x018: + return ' (GLFW)'; + } + return ''; + })(); return '$result$planeDescription'; } @@ -187,30 +188,31 @@ class KeyData { if (character == null) { return ''; } - final Iterable hexChars = character!.codeUnits - .map((int code) => code.toRadixString(16).padLeft(2, '0')); + final Iterable hexChars = character!.codeUnits.map( + (int code) => code.toRadixString(16).padLeft(2, '0'), + ); return ' (0x${hexChars.join(' ')})'; } @override String toString() { return 'KeyData(${type.label}, ' - 'physical: 0x${physical.toRadixString(16)}, ' - 'logical: ${_logicalToString()}, ' - 'character: ${_escapeCharacter()}${_quotedCharCode()}' - '${synthesized ? ', synthesized' : ''})'; + 'physical: 0x${physical.toRadixString(16)}, ' + 'logical: ${_logicalToString()}, ' + 'character: ${_escapeCharacter()}${_quotedCharCode()}' + '${synthesized ? ', synthesized' : ''})'; } /// Returns a complete textual description of the information in this object. String toStringFull() { return '$runtimeType(' - 'type: ${type.label}, ' - 'deviceType: ${deviceType.label}, ' - 'timeStamp: $timeStamp, ' - 'physical: 0x${physical.toRadixString(16)}, ' - 'logical: 0x${logical.toRadixString(16)}, ' - 'character: ${_escapeCharacter()}, ' - 'synthesized: $synthesized' - ')'; + 'type: ${type.label}, ' + 'deviceType: ${deviceType.label}, ' + 'timeStamp: $timeStamp, ' + 'physical: 0x${physical.toRadixString(16)}, ' + 'logical: 0x${logical.toRadixString(16)}, ' + 'character: ${_escapeCharacter()}, ' + 'synthesized: $synthesized' + ')'; } } diff --git a/lib/web_ui/lib/natives.dart b/lib/web_ui/lib/natives.dart index 37ba2b7efcae9..5e61e40b61682 100644 --- a/lib/web_ui/lib/natives.dart +++ b/lib/web_ui/lib/natives.dart @@ -12,7 +12,6 @@ class DartPluginRegistrant { /// isolate. This can safely be executed multiple times on the same isolate, /// but should not be called on the Root isolate. static void ensureInitialized() { - throw UnimplementedError( - '`ensureInitialized` is not implemented on the web.'); + throw UnimplementedError('`ensureInitialized` is not implemented on the web.'); } } diff --git a/lib/web_ui/lib/painting.dart b/lib/web_ui/lib/painting.dart index bb007afc84f02..94d8c11f61d37 100644 --- a/lib/web_ui/lib/painting.dart +++ b/lib/web_ui/lib/painting.dart @@ -23,36 +23,32 @@ Color _scaleAlpha(Color x, double factor) { class Color { const Color(int value) - : this._fromARGBC( - value >> 24, value >> 16, value >> 8, value, ColorSpace.sRGB); - - const Color.from( - {required double alpha, - required double red, - required double green, - required double blue, - this.colorSpace = ColorSpace.sRGB}) - : a = alpha, - r = red, - g = green, - b = blue; - - const Color.fromARGB(int a, int r, int g, int b) - : this._fromARGBC(a, r, g, b, ColorSpace.sRGB); - - const Color._fromARGBC( - int alpha, int red, int green, int blue, ColorSpace colorSpace) - : this._fromRGBOC( - red, green, blue, (alpha & 0xff) / 255, colorSpace); + : this._fromARGBC(value >> 24, value >> 16, value >> 8, value, ColorSpace.sRGB); + + const Color.from({ + required double alpha, + required double red, + required double green, + required double blue, + this.colorSpace = ColorSpace.sRGB, + }) : a = alpha, + r = red, + g = green, + b = blue; + + const Color.fromARGB(int a, int r, int g, int b) : this._fromARGBC(a, r, g, b, ColorSpace.sRGB); + + const Color._fromARGBC(int alpha, int red, int green, int blue, ColorSpace colorSpace) + : this._fromRGBOC(red, green, blue, (alpha & 0xff) / 255, colorSpace); const Color.fromRGBO(int r, int g, int b, double opacity) - : this._fromRGBOC(r, g, b, opacity, ColorSpace.sRGB); + : this._fromRGBOC(r, g, b, opacity, ColorSpace.sRGB); const Color._fromRGBOC(int r, int g, int b, double opacity, this.colorSpace) - : a = opacity, - r = (r & 0xff) / 255, - g = (g & 0xff) / 255, - b = (b & 0xff) / 255; + : a = opacity, + r = (r & 0xff) / 255, + g = (g & 0xff) / 255, + b = (b & 0xff) / 255; final double a; @@ -87,24 +83,25 @@ class Color { int get blue => (0x000000ff & value) >> 0; - Color withValues( - {double? alpha, - double? red, - double? green, - double? blue, - ColorSpace? colorSpace}) { + Color withValues({ + double? alpha, + double? red, + double? green, + double? blue, + ColorSpace? colorSpace, + }) { Color? updatedComponents; if (alpha != null || red != null || green != null || blue != null) { updatedComponents = Color.from( - alpha: alpha ?? a, - red: red ?? r, - green: green ?? g, - blue: blue ?? b, - colorSpace: this.colorSpace); + alpha: alpha ?? a, + red: red ?? r, + green: green ?? g, + blue: blue ?? b, + colorSpace: this.colorSpace, + ); } if (colorSpace != null && colorSpace != this.colorSpace) { - final _ColorTransform transform = - _getColorTransform(this.colorSpace, colorSpace); + final _ColorTransform transform = _getColorTransform(this.colorSpace, colorSpace); return transform.transform(updatedComponents ?? this, colorSpace); } else { return updatedComponents ?? this; @@ -177,12 +174,14 @@ class Color { assert(foreground.colorSpace == background.colorSpace); assert(foreground.colorSpace != ColorSpace.extendedSRGB); final double alpha = foreground.a; - if (alpha == 0) { // Foreground completely transparent. + if (alpha == 0) { + // Foreground completely transparent. return background; } final double invAlpha = 1 - alpha; double backAlpha = background.a; - if (backAlpha == 1) { // Opaque background case + if (backAlpha == 1) { + // Opaque background case return Color.from( alpha: 1, red: alpha * foreground.r + invAlpha * background.r, @@ -190,7 +189,8 @@ class Color { blue: alpha * foreground.b + invAlpha * background.b, colorSpace: foreground.colorSpace, ); - } else { // General case + } else { + // General case backAlpha = backAlpha * invAlpha; final double outAlpha = alpha + backAlpha; assert(outAlpha != 0); @@ -232,23 +232,12 @@ class Color { 'Color(alpha: ${a.toStringAsFixed(4)}, red: ${r.toStringAsFixed(4)}, green: ${g.toStringAsFixed(4)}, blue: ${b.toStringAsFixed(4)}, colorSpace: $colorSpace)'; } -enum StrokeCap { - butt, - round, - square, -} +enum StrokeCap { butt, round, square } // These enum values must be kept in sync with SkPaint::Join. -enum StrokeJoin { - miter, - round, - bevel, -} +enum StrokeJoin { miter, round, bevel } -enum PaintingStyle { - fill, - stroke, -} +enum PaintingStyle { fill, stroke } enum BlendMode { // This list comes from Skia's SkXfermode.h and the values (order) should be @@ -287,12 +276,7 @@ enum BlendMode { luminosity, } -enum Clip { - none, - hardEdge, - antiAlias, - antiAliasWithSaveLayer, -} +enum Clip { none, hardEdge, antiAlias, antiAliasWithSaveLayer } abstract class Paint { factory Paint() => engine.renderer.createPaint(); @@ -369,13 +353,7 @@ abstract class Gradient implements Shader { Float64List? matrix4, ]) { final Float32List? matrix = matrix4 == null ? null : engine.toMatrix32(matrix4); - return engine.renderer.createLinearGradient( - from, - to, - colors, - colorStops, - tileMode, - matrix); + return engine.renderer.createLinearGradient(from, to, colors, colorStops, tileMode, matrix); } factory Gradient.radial( @@ -394,12 +372,27 @@ abstract class Gradient implements Shader { final Float32List? matrix32 = matrix4 != null ? engine.toMatrix32(matrix4) : null; if (focal == null || (focal == center && focalRadius == 0.0)) { return engine.renderer.createRadialGradient( - center, radius, colors, colorStops, tileMode, matrix32); + center, + radius, + colors, + colorStops, + tileMode, + matrix32, + ); } else { - assert(center != Offset.zero || - focal != Offset.zero); // will result in exception(s) in Skia side + assert( + center != Offset.zero || focal != Offset.zero, + ); // will result in exception(s) in Skia side return engine.renderer.createConicalGradient( - focal, focalRadius, center, radius, colors, colorStops, tileMode, matrix32); + focal, + focalRadius, + center, + radius, + colors, + colorStops, + tileMode, + matrix32, + ); } } factory Gradient.sweep( @@ -417,7 +410,8 @@ abstract class Gradient implements Shader { tileMode, startAngle, endAngle, - matrix4 != null ? engine.toMatrix32(matrix4) : null); + matrix4 != null ? engine.toMatrix32(matrix4) : null, + ); } typedef ImageEventCallback = void Function(Image image); @@ -461,10 +455,7 @@ enum BlurStyle { } class MaskFilter { - const MaskFilter.blur( - this._style, - this._sigma, - ); + const MaskFilter.blur(this._style, this._sigma); final BlurStyle _style; final double _sigma; @@ -473,9 +464,7 @@ class MaskFilter { @override bool operator ==(Object other) { - return other is MaskFilter - && other._style == _style - && other._sigma == _sigma; + return other is MaskFilter && other._style == _style && other._sigma == _sigma; } @override @@ -505,7 +494,8 @@ class _ClampTransform implements _ColorTransform { red: clampDouble(color.r, 0, 1), green: clampDouble(color.g, 0, 1), blue: clampDouble(color.b, 0, 1), - colorSpace: resultColorSpace); + colorSpace: resultColorSpace, + ); } } @@ -517,20 +507,12 @@ class _MatrixColorTransform implements _ColorTransform { @override Color transform(Color color, ColorSpace resultColorSpace) { return Color.from( - alpha: color.a, - red: values[0] * color.r + - values[1] * color.g + - values[2] * color.b + - values[3], - green: values[4] * color.r + - values[5] * color.g + - values[6] * color.b + - values[7], - blue: values[8] * color.r + - values[9] * color.g + - values[10] * color.b + - values[11], - colorSpace: resultColorSpace); + alpha: color.a, + red: values[0] * color.r + values[1] * color.g + values[2] * color.b + values[3], + green: values[4] * color.r + values[5] * color.g + values[6] * color.b + values[7], + blue: values[8] * color.r + values[9] * color.g + values[10] * color.b + values[11], + colorSpace: resultColorSpace, + ); } } @@ -540,14 +522,14 @@ _ColorTransform _getColorTransform(ColorSpace source, ColorSpace destination) { 0.145738111193222, // 0.096480880462996, 0.916386732581291, -0.086093928394828, 0.089490172325882, // - -0.127099563510240, -0.068983484963878, 0.735426667591299, 0.233655661600230 + -0.127099563510240, -0.068983484963878, 0.735426667591299, 0.233655661600230, ]); const _ColorTransform p3ToSrgb = _MatrixColorTransform([ 1.306671048092539, -0.298061942172353, 0.213228303487995, -0.213580156254466, // -0.117390025596251, 1.127722006101976, 0.109727644608938, -0.109450321455370, // - 0.214813187718391, 0.054268702864647, 1.406898424029350, -0.364892765879631 + 0.214813187718391, 0.054268702864647, 1.406898424029350, -0.364892765879631, ]); switch (source) { case ColorSpace.sRGB: @@ -581,31 +563,22 @@ _ColorTransform _getColorTransform(ColorSpace source, ColorSpace destination) { } // This needs to be kept in sync with the "_FilterQuality" enum in skwasm's canvas.cpp -enum FilterQuality { - none, - low, - medium, - high, -} +enum FilterQuality { none, low, medium, high } class ImageFilter { - factory ImageFilter.blur({ - double sigmaX = 0.0, - double sigmaY = 0.0, - TileMode? tileMode - }) => engine.renderer.createBlurImageFilter( - sigmaX: sigmaX, - sigmaY: sigmaY, - tileMode: tileMode - ); + factory ImageFilter.blur({double sigmaX = 0.0, double sigmaY = 0.0, TileMode? tileMode}) => + engine.renderer.createBlurImageFilter(sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode); - factory ImageFilter.dilate({ double radiusX = 0.0, double radiusY = 0.0 }) => - engine.renderer.createDilateImageFilter(radiusX: radiusX, radiusY: radiusY); + factory ImageFilter.dilate({double radiusX = 0.0, double radiusY = 0.0}) => + engine.renderer.createDilateImageFilter(radiusX: radiusX, radiusY: radiusY); - factory ImageFilter.erode({ double radiusX = 0.0, double radiusY = 0.0 }) => - engine.renderer.createErodeImageFilter(radiusX: radiusX, radiusY: radiusY); + factory ImageFilter.erode({double radiusX = 0.0, double radiusY = 0.0}) => + engine.renderer.createErodeImageFilter(radiusX: radiusX, radiusY: radiusY); - factory ImageFilter.matrix(Float64List matrix4, {FilterQuality filterQuality = FilterQuality.medium}) { + factory ImageFilter.matrix( + Float64List matrix4, { + FilterQuality filterQuality = FilterQuality.medium, + }) { if (matrix4.length != 16) { throw ArgumentError('"matrix4" must have 16 entries.'); } @@ -613,7 +586,7 @@ class ImageFilter { } factory ImageFilter.compose({required ImageFilter outer, required ImageFilter inner}) => - engine.renderer.composeImageFilters(outer: outer, inner: inner); + engine.renderer.composeImageFilters(outer: outer, inner: inner); // ignore: avoid_unused_constructor_parameters factory ImageFilter.shader(FragmentShader shader) { @@ -623,26 +596,13 @@ class ImageFilter { static bool get isShaderFilterSupported => false; } -enum ColorSpace { - sRGB, - extendedSRGB, - displayP3, -} +enum ColorSpace { sRGB, extendedSRGB, displayP3 } // This must be kept in sync with the `ImageByteFormat` enum in Skwasm's surface.cpp. -enum ImageByteFormat { - rawRgba, - rawStraightRgba, - rawUnmodified, - png, -} +enum ImageByteFormat { rawRgba, rawStraightRgba, rawUnmodified, png } // This must be kept in sync with the `PixelFormat` enum in Skwasm's image.cpp. -enum PixelFormat { - rgba8888, - bgra8888, - rgbaFloat32, -} +enum PixelFormat { rgba8888, bgra8888, rgbaFloat32 } typedef ImageDecoderCallback = void Function(Image result); @@ -674,7 +634,8 @@ Future instantiateImageCodec( list, targetWidth: targetWidth, targetHeight: targetHeight, - allowUpscaling: allowUpscaling); + allowUpscaling: allowUpscaling, +); Future instantiateImageCodecFromBuffer( ImmutableBuffer buffer, { @@ -685,7 +646,8 @@ Future instantiateImageCodecFromBuffer( buffer._list!, targetWidth: targetWidth, targetHeight: targetHeight, - allowUpscaling: allowUpscaling); + allowUpscaling: allowUpscaling, +); Future instantiateImageCodecWithSize( ImmutableBuffer buffer, { @@ -701,8 +663,12 @@ Future instantiateImageCodecWithSize( final int width = info.image.width; final int height = info.image.height; final TargetImageSize targetSize = getTargetSize(width, height); - return engine.renderer.instantiateImageCodec(buffer._list!, - targetWidth: targetSize.width, targetHeight: targetSize.height, allowUpscaling: false); + return engine.renderer.instantiateImageCodec( + buffer._list!, + targetWidth: targetSize.width, + targetHeight: targetSize.height, + allowUpscaling: false, + ); } finally { info.image.dispose(); } @@ -716,8 +682,8 @@ typedef TargetImageSizeCallback = TargetImageSize Function(int intrinsicWidth, i class TargetImageSize { const TargetImageSize({this.width, this.height}) - : assert(width == null || width > 0), - assert(height == null || height > 0); + : assert(width == null || width > 0), + assert(height == null || height > 0); final int? width; final int? height; @@ -755,13 +721,7 @@ Future _decodeImageFromListAsync(Uint8List list, ImageDecoderCallback call // The `pixels` should be the scanlined raw pixels, 4 bytes per pixel, from left // to right, then from top to down. The order of the 4 bytes of pixels is // decided by `format`. -Future createBmp( - Uint8List pixels, - int width, - int height, - int rowBytes, - PixelFormat format, -) { +Future createBmp(Uint8List pixels, int width, int height, int rowBytes, PixelFormat format) { late bool swapRedBlue; switch (format) { case PixelFormat.bgra8888: @@ -829,9 +789,7 @@ Future createBmp( } } - return instantiateImageCodec( - bmpData.buffer.asUint8List(), - ); + return instantiateImageCodec(bmpData.buffer.asUint8List()); } void decodeImageFromPixels( @@ -853,14 +811,15 @@ void decodeImageFromPixels( rowBytes: rowBytes, targetWidth: targetWidth, targetHeight: targetHeight, - allowUpscaling: allowUpscaling); + allowUpscaling: allowUpscaling, +); class Shadow { const Shadow({ this.color = const Color(_kColorDefault), this.offset = Offset.zero, this.blurRadius = 0.0, - }) : assert(blurRadius >= 0.0, 'Text shadow blur radius should be non-negative.'); + }) : assert(blurRadius >= 0.0, 'Text shadow blur radius should be non-negative.'); static const int _kColorDefault = 0xFF000000; final Color color; @@ -880,11 +839,7 @@ class Shadow { } Shadow scale(double factor) { - return Shadow( - color: color, - offset: offset * factor, - blurRadius: blurRadius * factor, - ); + return Shadow(color: color, offset: offset * factor, blurRadius: blurRadius * factor); } static Shadow? lerp(Shadow? a, Shadow? b, double t) { @@ -952,13 +907,7 @@ abstract class ImageShader implements Shader { TileMode tmy, Float64List matrix4, { FilterQuality? filterQuality, - }) => engine.renderer.createImageShader( - image, - tmx, - tmy, - matrix4, - filterQuality - ); + }) => engine.renderer.createImageShader(image, tmx, tmy, matrix4, filterQuality); @override void dispose(); @@ -996,6 +945,7 @@ class ImmutableBuffer { }()); return disposed; } + void dispose() => _list = null; } @@ -1007,18 +957,14 @@ class ImageDescriptor { required int height, int? rowBytes, required PixelFormat pixelFormat, - }) : _width = width, - _height = height, - _rowBytes = rowBytes, - _format = pixelFormat { + }) : _width = width, + _height = height, + _rowBytes = rowBytes, + _format = pixelFormat { _data = buffer._list; } - ImageDescriptor._() - : _width = null, - _height = null, - _rowBytes = null, - _format = null; + ImageDescriptor._() : _width = null, _height = null, _rowBytes = null, _format = null; static Future encoded(ImmutableBuffer buffer) async { final ImageDescriptor descriptor = ImageDescriptor._(); diff --git a/lib/web_ui/lib/path.dart b/lib/web_ui/lib/path.dart index 47b80ecd084cc..6cc47f222de6d 100644 --- a/lib/web_ui/lib/path.dart +++ b/lib/web_ui/lib/path.dart @@ -51,7 +51,7 @@ abstract class Path { // see https://skia.org/user/api/SkPath_Reference#SkPath_getBounds Rect getBounds(); static Path combine(PathOperation operation, Path path1, Path path2) => - engine.renderer.combinePaths(operation, path1, path2); + engine.renderer.combinePaths(operation, path1, path2); PathMetrics computeMetrics({bool forceClosed = false}); } diff --git a/lib/web_ui/lib/platform_dispatcher.dart b/lib/web_ui/lib/platform_dispatcher.dart index d6995d7ee24e0..c9bbdcbfebb78 100644 --- a/lib/web_ui/lib/platform_dispatcher.dart +++ b/lib/web_ui/lib/platform_dispatcher.dart @@ -12,8 +12,8 @@ typedef PointerDataPacketCallback = void Function(PointerDataPacket packet); typedef KeyDataCallback = bool Function(KeyData data); typedef SemanticsActionEventCallback = void Function(SemanticsActionEvent action); typedef PlatformMessageResponseCallback = void Function(ByteData? data); -typedef PlatformMessageCallback = void Function( - String name, ByteData? data, PlatformMessageResponseCallback? callback); +typedef PlatformMessageCallback = + void Function(String name, ByteData? data, PlatformMessageResponseCallback? callback); typedef ErrorCallback = bool Function(Object exception, StackTrace stackTrace); // ignore: avoid_classes_with_only_static_members @@ -65,17 +65,9 @@ abstract class PlatformDispatcher { TimingsCallback? get onReportTimings; set onReportTimings(TimingsCallback? callback); - void sendPlatformMessage( - String name, - ByteData? data, - PlatformMessageResponseCallback? callback, - ); + void sendPlatformMessage(String name, ByteData? data, PlatformMessageResponseCallback? callback); - void sendPortPlatformMessage( - String name, - ByteData? data, - int identifier, - Object port); + void sendPortPlatformMessage(String name, ByteData? data, int identifier, Object port); void registerBackgroundIsolate(RootIsolateToken token); @@ -208,8 +200,7 @@ class FrameTiming { ]); } - FrameTiming._(this._data) - : assert(_data.length == _dataLength); + FrameTiming._(this._data) : assert(_data.length == _dataLength); static final int _dataLength = FramePhase.values.length + _FrameTimingInfo.values.length; @@ -225,7 +216,8 @@ class FrameTiming { Duration get rasterDuration => _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.rasterStart); - Duration get vsyncOverhead => _rawDuration(FramePhase.buildStart) - _rawDuration(FramePhase.vsyncStart); + Duration get vsyncOverhead => + _rawDuration(FramePhase.buildStart) - _rawDuration(FramePhase.vsyncStart); Duration get totalSpan => _rawDuration(FramePhase.rasterFinish) - _rawDuration(FramePhase.vsyncStart); @@ -244,7 +236,7 @@ class FrameTiming { int get frameNumber => _data.last; - final List _data; // some elements in microseconds, some in bytes, some are counts + final List _data; // some elements in microseconds, some in bytes, some are counts String _formatMS(Duration duration) => '${duration.inMicroseconds * 0.001}ms'; @@ -262,30 +254,19 @@ class FrameTiming { } } -enum AppLifecycleState { - detached, - resumed, - inactive, - hidden, - paused, -} +enum AppLifecycleState { detached, resumed, inactive, hidden, paused } -enum AppExitResponse { - exit, - cancel, -} +enum AppExitResponse { exit, cancel } -enum AppExitType { - cancelable, - required, -} +enum AppExitType { cancelable, required } abstract class ViewPadding { - const factory ViewPadding._( - {required double left, - required double top, - required double right, - required double bottom}) = engine.ViewPadding; + const factory ViewPadding._({ + required double left, + required double top, + required double right, + required double bottom, + }) = engine.ViewPadding; double get left; double get top; @@ -316,7 +297,7 @@ abstract class ViewConstraints { double get maxHeight; bool isSatisfiedBy(Size size); bool get isTight; - ViewConstraints operator/(double factor); + ViewConstraints operator /(double factor); } @Deprecated( @@ -326,11 +307,7 @@ abstract class ViewConstraints { typedef WindowPadding = ViewPadding; class DisplayFeature { - const DisplayFeature({ - required this.bounds, - required this.type, - required this.state, - }); + const DisplayFeature({required this.bounds, required this.type, required this.state}); final Rect bounds; final DisplayFeatureType type; @@ -344,8 +321,10 @@ class DisplayFeature { if (other.runtimeType != runtimeType) { return false; } - return other is DisplayFeature && bounds == other.bounds && - type == other.type && state == other.state; + return other is DisplayFeature && + bounds == other.bounds && + type == other.type && + state == other.state; } @override @@ -357,36 +336,21 @@ class DisplayFeature { } } -enum DisplayFeatureType { - unknown, - fold, - hinge, - cutout, -} +enum DisplayFeatureType { unknown, fold, hinge, cutout } -enum DisplayFeatureState { - unknown, - postureFlat, - postureHalfOpened, - postureFlipped, -} +enum DisplayFeatureState { unknown, postureFlat, postureHalfOpened, postureFlipped } class Locale { - const Locale( - this._languageCode, [ - this._countryCode, - ]) : assert(_languageCode != ''), - scriptCode = null; - - const Locale.fromSubtags({ - String languageCode = 'und', - this.scriptCode, - String? countryCode, - }) : assert(languageCode != ''), - _languageCode = languageCode, - assert(scriptCode != ''), - assert(countryCode != ''), - _countryCode = countryCode; + const Locale(this._languageCode, [this._countryCode]) + : assert(_languageCode != ''), + scriptCode = null; + + const Locale.fromSubtags({String languageCode = 'und', this.scriptCode, String? countryCode}) + : assert(languageCode != ''), + _languageCode = languageCode, + assert(scriptCode != ''), + assert(countryCode != ''), + _countryCode = countryCode; String get languageCode => _deprecatedLanguageSubtagMap[_languageCode] ?? _languageCode; final String _languageCode; @@ -495,10 +459,10 @@ class Locale { if (identical(this, other)) { return true; } - return other is Locale - && other.languageCode == languageCode - && other.scriptCode == scriptCode - && other.countryCode == countryCode; + return other is Locale && + other.languageCode == languageCode && + other.scriptCode == scriptCode && + other.countryCode == countryCode; } @override @@ -523,12 +487,7 @@ class Locale { } } -enum DartPerformanceMode { - balanced, - latency, - throughput, - memory, -} +enum DartPerformanceMode { balanced, latency, throughput, memory } class SemanticsActionEvent { const SemanticsActionEvent({ @@ -564,11 +523,7 @@ class SemanticsActionEvent { } final class ViewFocusEvent { - const ViewFocusEvent({ - required this.viewId, - required this.state, - required this.direction, - }); + const ViewFocusEvent({required this.viewId, required this.state, required this.direction}); final int viewId; @@ -582,13 +537,6 @@ final class ViewFocusEvent { } } -enum ViewFocusState { - unfocused, - focused, -} +enum ViewFocusState { unfocused, focused } -enum ViewFocusDirection { - undefined, - forward, - backward, -} +enum ViewFocusDirection { undefined, forward, backward } diff --git a/lib/web_ui/lib/platform_isolate.dart b/lib/web_ui/lib/platform_isolate.dart index 067d462b42654..497f56c98c8ce 100644 --- a/lib/web_ui/lib/platform_isolate.dart +++ b/lib/web_ui/lib/platform_isolate.dart @@ -27,8 +27,7 @@ part of ui; /// This method can only be invoked from the main isolate. /// /// This API is currently experimental. -Future runOnPlatformThread(FutureOr Function() computation) => - Future(computation); +Future runOnPlatformThread(FutureOr Function() computation) => Future(computation); /// Returns whether the current isolate is running on the platform thread. bool isRunningOnPlatformThread = true; diff --git a/lib/web_ui/lib/pointer.dart b/lib/web_ui/lib/pointer.dart index 6b32bde05449f..7bf1d9c3c1e75 100644 --- a/lib/web_ui/lib/pointer.dart +++ b/lib/web_ui/lib/pointer.dart @@ -17,22 +17,9 @@ enum PointerChange { panZoomEnd, } -enum PointerDeviceKind { - touch, - mouse, - stylus, - invertedStylus, - trackpad, - unknown -} +enum PointerDeviceKind { touch, mouse, stylus, invertedStylus, trackpad, unknown } -enum PointerSignalKind { - none, - scroll, - scrollInertiaCancel, - scale, - unknown -} +enum PointerSignalKind { none, scroll, scrollInertiaCancel, scale, unknown } typedef PointerDataRespondCallback = void Function({bool allowPlatformDefault}); @@ -124,42 +111,42 @@ class PointerData { String toString() => 'PointerData(viewId: $viewId, x: $physicalX, y: $physicalY)'; String toStringFull() { return '$runtimeType(' - 'embedderId: $embedderId, ' - 'timeStamp: $timeStamp, ' - 'change: $change, ' - 'kind: $kind, ' - 'signalKind: $signalKind, ' - 'device: $device, ' - 'pointerIdentifier: $pointerIdentifier, ' - 'physicalX: $physicalX, ' - 'physicalY: $physicalY, ' - 'physicalDeltaX: $physicalDeltaX, ' - 'physicalDeltaY: $physicalDeltaY, ' - 'buttons: $buttons, ' - 'synthesized: $synthesized, ' - 'pressure: $pressure, ' - 'pressureMin: $pressureMin, ' - 'pressureMax: $pressureMax, ' - 'distance: $distance, ' - 'distanceMax: $distanceMax, ' - 'size: $size, ' - 'radiusMajor: $radiusMajor, ' - 'radiusMinor: $radiusMinor, ' - 'radiusMin: $radiusMin, ' - 'radiusMax: $radiusMax, ' - 'orientation: $orientation, ' - 'tilt: $tilt, ' - 'platformData: $platformData, ' - 'scrollDeltaX: $scrollDeltaX, ' - 'scrollDeltaY: $scrollDeltaY, ' - 'panX: $panX, ' - 'panY: $panY, ' - 'panDeltaX: $panDeltaX, ' - 'panDeltaY: $panDeltaY, ' - 'scale: $scale, ' - 'rotation: $rotation, ' - 'viewId: $viewId' - ')'; + 'embedderId: $embedderId, ' + 'timeStamp: $timeStamp, ' + 'change: $change, ' + 'kind: $kind, ' + 'signalKind: $signalKind, ' + 'device: $device, ' + 'pointerIdentifier: $pointerIdentifier, ' + 'physicalX: $physicalX, ' + 'physicalY: $physicalY, ' + 'physicalDeltaX: $physicalDeltaX, ' + 'physicalDeltaY: $physicalDeltaY, ' + 'buttons: $buttons, ' + 'synthesized: $synthesized, ' + 'pressure: $pressure, ' + 'pressureMin: $pressureMin, ' + 'pressureMax: $pressureMax, ' + 'distance: $distance, ' + 'distanceMax: $distanceMax, ' + 'size: $size, ' + 'radiusMajor: $radiusMajor, ' + 'radiusMinor: $radiusMinor, ' + 'radiusMin: $radiusMin, ' + 'radiusMax: $radiusMax, ' + 'orientation: $orientation, ' + 'tilt: $tilt, ' + 'platformData: $platformData, ' + 'scrollDeltaX: $scrollDeltaX, ' + 'scrollDeltaY: $scrollDeltaY, ' + 'panX: $panX, ' + 'panY: $panY, ' + 'panDeltaX: $panDeltaX, ' + 'panDeltaY: $panDeltaY, ' + 'scale: $scale, ' + 'rotation: $rotation, ' + 'viewId: $viewId' + ')'; } } diff --git a/lib/web_ui/lib/semantics.dart b/lib/web_ui/lib/semantics.dart index 786eaacc5454f..0bb27d8bdc0f5 100644 --- a/lib/web_ui/lib/semantics.dart +++ b/lib/web_ui/lib/semantics.dart @@ -41,23 +41,53 @@ class SemanticsAction { static const SemanticsAction scrollRight = SemanticsAction._(_kScrollRightIndex, 'scrollRight'); static const SemanticsAction scrollUp = SemanticsAction._(_kScrollUpIndex, 'scrollUp'); static const SemanticsAction scrollDown = SemanticsAction._(_kScrollDownIndex, 'scrollDown'); - static const SemanticsAction scrollToOffset = SemanticsAction._(_kScrollToOffsetIndex, 'scrollToOffset'); + static const SemanticsAction scrollToOffset = SemanticsAction._( + _kScrollToOffsetIndex, + 'scrollToOffset', + ); static const SemanticsAction increase = SemanticsAction._(_kIncreaseIndex, 'increase'); static const SemanticsAction decrease = SemanticsAction._(_kDecreaseIndex, 'decrease'); - static const SemanticsAction showOnScreen = SemanticsAction._(_kShowOnScreenIndex, 'showOnScreen'); - static const SemanticsAction moveCursorForwardByCharacter = SemanticsAction._(_kMoveCursorForwardByCharacterIndex, 'moveCursorForwardByCharacter'); - static const SemanticsAction moveCursorBackwardByCharacter = SemanticsAction._(_kMoveCursorBackwardByCharacterIndex, 'moveCursorBackwardByCharacter'); + static const SemanticsAction showOnScreen = SemanticsAction._( + _kShowOnScreenIndex, + 'showOnScreen', + ); + static const SemanticsAction moveCursorForwardByCharacter = SemanticsAction._( + _kMoveCursorForwardByCharacterIndex, + 'moveCursorForwardByCharacter', + ); + static const SemanticsAction moveCursorBackwardByCharacter = SemanticsAction._( + _kMoveCursorBackwardByCharacterIndex, + 'moveCursorBackwardByCharacter', + ); static const SemanticsAction setText = SemanticsAction._(_kSetTextIndex, 'setText'); - static const SemanticsAction setSelection = SemanticsAction._(_kSetSelectionIndex, 'setSelection'); + static const SemanticsAction setSelection = SemanticsAction._( + _kSetSelectionIndex, + 'setSelection', + ); static const SemanticsAction copy = SemanticsAction._(_kCopyIndex, 'copy'); static const SemanticsAction cut = SemanticsAction._(_kCutIndex, 'cut'); static const SemanticsAction paste = SemanticsAction._(_kPasteIndex, 'paste'); - static const SemanticsAction didGainAccessibilityFocus = SemanticsAction._(_kDidGainAccessibilityFocusIndex, 'didGainAccessibilityFocus'); - static const SemanticsAction didLoseAccessibilityFocus = SemanticsAction._(_kDidLoseAccessibilityFocusIndex, 'didLoseAccessibilityFocus'); - static const SemanticsAction customAction = SemanticsAction._(_kCustomActionIndex, 'customAction'); + static const SemanticsAction didGainAccessibilityFocus = SemanticsAction._( + _kDidGainAccessibilityFocusIndex, + 'didGainAccessibilityFocus', + ); + static const SemanticsAction didLoseAccessibilityFocus = SemanticsAction._( + _kDidLoseAccessibilityFocusIndex, + 'didLoseAccessibilityFocus', + ); + static const SemanticsAction customAction = SemanticsAction._( + _kCustomActionIndex, + 'customAction', + ); static const SemanticsAction dismiss = SemanticsAction._(_kDismissIndex, 'dismiss'); - static const SemanticsAction moveCursorForwardByWord = SemanticsAction._(_kMoveCursorForwardByWordIndex, 'moveCursorForwardByWord'); - static const SemanticsAction moveCursorBackwardByWord = SemanticsAction._(_kMoveCursorBackwardByWordIndex, 'moveCursorBackwardByWord'); + static const SemanticsAction moveCursorForwardByWord = SemanticsAction._( + _kMoveCursorForwardByWordIndex, + 'moveCursorForwardByWord', + ); + static const SemanticsAction moveCursorBackwardByWord = SemanticsAction._( + _kMoveCursorBackwardByWordIndex, + 'moveCursorBackwardByWord', + ); static const SemanticsAction focus = SemanticsAction._(_kFocusIndex, 'focus'); static const Map _kActionById = { @@ -131,9 +161,15 @@ class SemanticsFlag { static const int _kIsExpandedIndex = 1 << 27; static const int _kHasSelectedStateIndex = 1 << 28; - static const SemanticsFlag hasCheckedState = SemanticsFlag._(_kHasCheckedStateIndex, 'hasCheckedState'); + static const SemanticsFlag hasCheckedState = SemanticsFlag._( + _kHasCheckedStateIndex, + 'hasCheckedState', + ); static const SemanticsFlag isChecked = SemanticsFlag._(_kIsCheckedIndex, 'isChecked'); - static const SemanticsFlag hasSelectedState = SemanticsFlag._(_kHasSelectedStateIndex, 'hasSelectedState'); + static const SemanticsFlag hasSelectedState = SemanticsFlag._( + _kHasSelectedStateIndex, + 'hasSelectedState', + ); static const SemanticsFlag isSelected = SemanticsFlag._(_kIsSelectedIndex, 'isSelected'); static const SemanticsFlag isButton = SemanticsFlag._(_kIsButtonIndex, 'isButton'); static const SemanticsFlag isTextField = SemanticsFlag._(_kIsTextFieldIndex, 'isTextField'); @@ -143,9 +179,15 @@ class SemanticsFlag { static const SemanticsFlag isLink = SemanticsFlag._(_kIsLinkIndex, 'isLink'); static const SemanticsFlag isFocusable = SemanticsFlag._(_kIsFocusableIndex, 'isFocusable'); static const SemanticsFlag isFocused = SemanticsFlag._(_kIsFocusedIndex, 'isFocused'); - static const SemanticsFlag hasEnabledState = SemanticsFlag._(_kHasEnabledStateIndex, 'hasEnabledState'); + static const SemanticsFlag hasEnabledState = SemanticsFlag._( + _kHasEnabledStateIndex, + 'hasEnabledState', + ); static const SemanticsFlag isEnabled = SemanticsFlag._(_kIsEnabledIndex, 'isEnabled'); - static const SemanticsFlag isInMutuallyExclusiveGroup = SemanticsFlag._(_kIsInMutuallyExclusiveGroupIndex, 'isInMutuallyExclusiveGroup'); + static const SemanticsFlag isInMutuallyExclusiveGroup = SemanticsFlag._( + _kIsInMutuallyExclusiveGroupIndex, + 'isInMutuallyExclusiveGroup', + ); static const SemanticsFlag isHeader = SemanticsFlag._(_kIsHeaderIndex, 'isHeader'); static const SemanticsFlag isObscured = SemanticsFlag._(_kIsObscuredIndex, 'isObscured'); static const SemanticsFlag isMultiline = SemanticsFlag._(_kIsMultilineIndex, 'isMultiline'); @@ -154,11 +196,23 @@ class SemanticsFlag { static const SemanticsFlag isHidden = SemanticsFlag._(_kIsHiddenIndex, 'isHidden'); static const SemanticsFlag isImage = SemanticsFlag._(_kIsImageIndex, 'isImage'); static const SemanticsFlag isLiveRegion = SemanticsFlag._(_kIsLiveRegionIndex, 'isLiveRegion'); - static const SemanticsFlag hasToggledState = SemanticsFlag._(_kHasToggledStateIndex, 'hasToggledState'); + static const SemanticsFlag hasToggledState = SemanticsFlag._( + _kHasToggledStateIndex, + 'hasToggledState', + ); static const SemanticsFlag isToggled = SemanticsFlag._(_kIsToggledIndex, 'isToggled'); - static const SemanticsFlag hasImplicitScrolling = SemanticsFlag._(_kHasImplicitScrollingIndex, 'hasImplicitScrolling'); - static const SemanticsFlag isCheckStateMixed = SemanticsFlag._(_kIsCheckStateMixedIndex, 'isCheckStateMixed'); - static const SemanticsFlag hasExpandedState = SemanticsFlag._(_kHasExpandedStateIndex, 'hasExpandedState'); + static const SemanticsFlag hasImplicitScrolling = SemanticsFlag._( + _kHasImplicitScrollingIndex, + 'hasImplicitScrolling', + ); + static const SemanticsFlag isCheckStateMixed = SemanticsFlag._( + _kIsCheckStateMixedIndex, + 'isCheckStateMixed', + ); + static const SemanticsFlag hasExpandedState = SemanticsFlag._( + _kHasExpandedStateIndex, + 'hasExpandedState', + ); static const SemanticsFlag isExpanded = SemanticsFlag._(_kIsExpandedIndex, 'isExpanded'); static const Map _kFlagById = { @@ -210,9 +264,7 @@ class SemanticsFlag { // * engine/src/flutter/testing/dart/semantics_test.dart abstract class StringAttribute { - StringAttribute._({ - required this.range, - }); + StringAttribute._({required this.range}); final TextRange range; @@ -220,9 +272,7 @@ abstract class StringAttribute { } class SpellOutStringAttribute extends StringAttribute { - SpellOutStringAttribute({ - required super.range, - }) : super._(); + SpellOutStringAttribute({required super.range}) : super._(); @override StringAttribute copy({required TextRange range}) { @@ -236,10 +286,7 @@ class SpellOutStringAttribute extends StringAttribute { } class LocaleStringAttribute extends StringAttribute { - LocaleStringAttribute({ - required super.range, - required this.locale, - }) : super._(); + LocaleStringAttribute({required super.range, required this.locale}) : super._(); final Locale locale; @@ -298,57 +345,52 @@ class SemanticsUpdateBuilder { if (transform.length != 16) { throw ArgumentError('transform argument must have 16 entries.'); } - _nodeUpdates.add(engine.SemanticsNodeUpdate( - id: id, - flags: flags, - actions: actions, - maxValueLength: maxValueLength, - currentValueLength: currentValueLength, - textSelectionBase: textSelectionBase, - textSelectionExtent: textSelectionExtent, - scrollChildren: scrollChildren, - scrollIndex: scrollIndex, - scrollPosition: scrollPosition, - scrollExtentMax: scrollExtentMax, - scrollExtentMin: scrollExtentMin, - rect: rect, - identifier: identifier, - label: label, - labelAttributes: labelAttributes, - value: value, - valueAttributes: valueAttributes, - increasedValue: increasedValue, - increasedValueAttributes: increasedValueAttributes, - decreasedValue: decreasedValue, - decreasedValueAttributes: decreasedValueAttributes, - hint: hint, - hintAttributes: hintAttributes, - tooltip: tooltip, - textDirection: textDirection, - transform: engine.toMatrix32(transform), - elevation: elevation, - thickness: thickness, - childrenInTraversalOrder: childrenInTraversalOrder, - childrenInHitTestOrder: childrenInHitTestOrder, - additionalActions: additionalActions, - platformViewId: platformViewId, - headingLevel: headingLevel, - linkUrl: linkUrl, - )); + _nodeUpdates.add( + engine.SemanticsNodeUpdate( + id: id, + flags: flags, + actions: actions, + maxValueLength: maxValueLength, + currentValueLength: currentValueLength, + textSelectionBase: textSelectionBase, + textSelectionExtent: textSelectionExtent, + scrollChildren: scrollChildren, + scrollIndex: scrollIndex, + scrollPosition: scrollPosition, + scrollExtentMax: scrollExtentMax, + scrollExtentMin: scrollExtentMin, + rect: rect, + identifier: identifier, + label: label, + labelAttributes: labelAttributes, + value: value, + valueAttributes: valueAttributes, + increasedValue: increasedValue, + increasedValueAttributes: increasedValueAttributes, + decreasedValue: decreasedValue, + decreasedValueAttributes: decreasedValueAttributes, + hint: hint, + hintAttributes: hintAttributes, + tooltip: tooltip, + textDirection: textDirection, + transform: engine.toMatrix32(transform), + elevation: elevation, + thickness: thickness, + childrenInTraversalOrder: childrenInTraversalOrder, + childrenInHitTestOrder: childrenInHitTestOrder, + additionalActions: additionalActions, + platformViewId: platformViewId, + headingLevel: headingLevel, + linkUrl: linkUrl, + ), + ); } - void updateCustomAction({ - required int id, - String? label, - String? hint, - int overrideId = -1, - }) { + void updateCustomAction({required int id, String? label, String? hint, int overrideId = -1}) { // TODO(yjbanov): implement. } SemanticsUpdate build() { - return SemanticsUpdate._( - nodeUpdates: _nodeUpdates, - ); + return SemanticsUpdate._(nodeUpdates: _nodeUpdates); } } diff --git a/lib/web_ui/lib/src/engine/alarm_clock.dart b/lib/web_ui/lib/src/engine/alarm_clock.dart index 8a03bccec16bc..f3367b6985383 100644 --- a/lib/web_ui/lib/src/engine/alarm_clock.dart +++ b/lib/web_ui/lib/src/engine/alarm_clock.dart @@ -18,10 +18,8 @@ typedef TimestampFunction = DateTime Function(); /// /// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Notes class AlarmClock { - /// Initializes Alarmclock with a closure that gets called with a timestamp. - AlarmClock(TimestampFunction timestampFunction) - : _timestampFunction = timestampFunction; + AlarmClock(TimestampFunction timestampFunction) : _timestampFunction = timestampFunction; /// The function used to get current time. final TimestampFunction _timestampFunction; @@ -77,8 +75,7 @@ class AlarmClock { // We didn't have an existing timer, so create a new one. _timer = Timer(value.difference(now), _timerDidFire); } else { - assert(_datetime != null, - 'We can only have a timer if there is a non-null datetime'); + assert(_datetime != null, 'We can only have a timer if there is a non-null datetime'); if (_datetime!.isAfter(value)) { // This is the case when the value moves the target time to an earlier // point. Because there is no way to reconfigure an existing timer, we @@ -100,8 +97,7 @@ class AlarmClock { } void _timerDidFire() { - assert(_datetime != null, - 'If _datetime is null, the timer would have been cancelled'); + assert(_datetime != null, 'If _datetime is null, the timer would have been cancelled'); final DateTime now = _timestampFunction(); // We use the "not before" logic instead of "is after" because we may have // zero difference between now and _datetime. diff --git a/lib/web_ui/lib/src/engine/app_bootstrap.dart b/lib/web_ui/lib/src/engine/app_bootstrap.dart index 3c101f95717cb..5f4f89368f81e 100644 --- a/lib/web_ui/lib/src/engine/app_bootstrap.dart +++ b/lib/web_ui/lib/src/engine/app_bootstrap.dart @@ -18,8 +18,9 @@ typedef AppBootstrapRunAppFn = Future Function(); /// A class that controls the coarse lifecycle of a Flutter app. class AppBootstrap { /// Construct an AppBootstrap. - AppBootstrap({required InitEngineFn initializeEngine, required AppBootstrapRunAppFn runApp}) : - _initializeEngine = initializeEngine, _runApp = runApp; + AppBootstrap({required InitEngineFn initializeEngine, required AppBootstrapRunAppFn runApp}) + : _initializeEngine = initializeEngine, + _runApp = runApp; // A function to initialize the engine. final InitEngineFn _initializeEngine; @@ -51,16 +52,18 @@ class AppBootstrap { initializeEngine: ([JsFlutterConfiguration? configuration]) async { await _initializeEngine(configuration); return _prepareAppRunner(); - } + }, ); } /// Creates an appRunner that runs our encapsulated runApp function. FlutterAppRunner _prepareAppRunner() { - return FlutterAppRunner(runApp: ([RunAppFnParameters? params]) async { - await _runApp(); - return _prepareFlutterApp(); - }); + return FlutterAppRunner( + runApp: ([RunAppFnParameters? params]) async { + await _runApp(); + return _prepareFlutterApp(); + }, + ); } FlutterViewManager get viewManager => EnginePlatformDispatcher.instance.viewManager; @@ -75,7 +78,7 @@ class AppBootstrap { removeView: (int viewId) { assert(configuration.multiViewEnabled, 'Cannot removeView when multiView is not enabled'); return viewManager.disposeAndUnregisterView(viewId); - } + }, ); } } diff --git a/lib/web_ui/lib/src/engine/browser_detection.dart b/lib/web_ui/lib/src/engine/browser_detection.dart index 00ddbb707756c..bb76f73916f60 100644 --- a/lib/web_ui/lib/src/engine/browser_detection.dart +++ b/lib/web_ui/lib/src/engine/browser_detection.dart @@ -63,8 +63,7 @@ bool get isChrome110OrOlder { return _cachedIsChrome110OrOlder!; } final RegExp chromeRegexp = RegExp(r'Chrom(e|ium)\/([0-9]+)\.'); - final RegExpMatch? match = - chromeRegexp.firstMatch(ui_web.browser.userAgent); + final RegExpMatch? match = chromeRegexp.firstMatch(ui_web.browser.userAgent); if (match != null) { final int chromeVersion = int.parse(match.group(2)!); return _cachedIsChrome110OrOlder = chromeVersion <= 110; @@ -103,8 +102,7 @@ abstract class WebGLVersion { /// The highest WebGL version supported by the current browser, or -1 if WebGL /// is not supported. -int get webGLVersion => - _cachedWebGLVersion ?? (_cachedWebGLVersion = _detectWebGLVersion()); +int get webGLVersion => _cachedWebGLVersion ?? (_cachedWebGLVersion = _detectWebGLVersion()); int? _cachedWebGLVersion; @@ -118,10 +116,7 @@ int? _cachedWebGLVersion; /// /// Our CanvasKit backend is affected due to: https://github.com/emscripten-core/emscripten/issues/11819 int _detectWebGLVersion() { - final DomCanvasElement canvas = createDomCanvasElement( - width: 1, - height: 1, - ); + final DomCanvasElement canvas = createDomCanvasElement(width: 1, height: 1); if (canvas.getContext('webgl2') != null) { if (_workAroundBug91333) { return WebGLVersion.webgl1; diff --git a/lib/web_ui/lib/src/engine/canvas_pool.dart b/lib/web_ui/lib/src/engine/canvas_pool.dart index 2fb4ad478b16c..f6496f44e1851 100644 --- a/lib/web_ui/lib/src/engine/canvas_pool.dart +++ b/lib/web_ui/lib/src/engine/canvas_pool.dart @@ -45,8 +45,7 @@ import 'vector_math.dart'; /// to prevent reallocation. class CanvasPool extends _SaveStackTracking { /// Initializes canvas pool for target size and dpi. - CanvasPool(this._widthInBitmapPixels, this._heightInBitmapPixels, - this._density); + CanvasPool(this._widthInBitmapPixels, this._heightInBitmapPixels, this._density); DomCanvasRenderingContext2D? _context; ContextStateHandle? _contextHandle; @@ -215,10 +214,7 @@ class CanvasPool extends _SaveStackTracking { DomCanvasElement? _allocCanvas(int width, int height) { // The dartdocs for `tryCreateCanvasElement` on why we don't use the // `DomCanvasElement` constructor. - return tryCreateCanvasElement( - (width * _density).ceil(), - (height * _density).ceil(), - ); + return tryCreateCanvasElement((width * _density).ceil(), (height * _density).ceil()); } @override @@ -243,13 +239,15 @@ class CanvasPool extends _SaveStackTracking { reuse(); } - int _replaySingleSaveEntry(int clipDepth, Matrix4 prevTransform, - Matrix4 transform, List? clipStack) { + int _replaySingleSaveEntry( + int clipDepth, + Matrix4 prevTransform, + Matrix4 transform, + List? clipStack, + ) { final DomCanvasRenderingContext2D ctx = context; if (clipStack != null) { - for (final int clipCount = clipStack.length; - clipDepth < clipCount; - clipDepth++) { + for (final int clipCount = clipStack.length; clipDepth < clipCount; clipDepth++) { final SaveClipEntry clipEntry = clipStack[clipDepth]; final Matrix4 clipTimeTransform = clipEntry.currentTransform; // If transform for entry recording change since last element, update. @@ -263,12 +261,13 @@ class CanvasPool extends _SaveStackTracking { final double ratio = dpi; ctx.setTransform(ratio, 0, 0, ratio, 0, 0); ctx.transform( - clipTimeTransform[0], - clipTimeTransform[1], - clipTimeTransform[4], - clipTimeTransform[5], - clipTimeTransform[12], - clipTimeTransform[13]); + clipTimeTransform[0], + clipTimeTransform[1], + clipTimeTransform[4], + clipTimeTransform[5], + clipTimeTransform[12], + clipTimeTransform[13], + ); prevTransform = clipTimeTransform; } if (clipEntry.rect != null) { @@ -296,8 +295,14 @@ class CanvasPool extends _SaveStackTracking { transform[13] != prevTransform[13]) { final double ratio = dpi; ctx.setTransform(ratio, 0, 0, ratio, 0, 0); - ctx.transform(transform[0], transform[1], transform[4], transform[5], - transform[12], transform[13]); + ctx.transform( + transform[0], + transform[1], + transform[4], + transform[5], + transform[12], + transform[13], + ); } return clipDepth; } @@ -311,13 +316,16 @@ class CanvasPool extends _SaveStackTracking { for (int saveStackIndex = 0; saveStackIndex < len; saveStackIndex++) { final SaveStackEntry saveEntry = _saveStack[saveStackIndex]; clipDepth = _replaySingleSaveEntry( - clipDepth, prevTransform, saveEntry.transform, saveEntry.clipStack); + clipDepth, + prevTransform, + saveEntry.transform, + saveEntry.clipStack, + ); prevTransform = saveEntry.transform; ctx.save(); ++_saveContextCount; } - _replaySingleSaveEntry( - clipDepth, prevTransform, _currentTransform, clipStack); + _replaySingleSaveEntry(clipDepth, prevTransform, _currentTransform, clipStack); } /// Marks this pool for reuse. @@ -374,8 +382,7 @@ class CanvasPool extends _SaveStackTracking { // is applied on the DOM elements. ctx.setTransform(1, 0, 0, 1, 0, 0); if (clearCanvas) { - ctx.clearRect(0, 0, _widthInBitmapPixels * _density, - _heightInBitmapPixels * _density); + ctx.clearRect(0, 0, _widthInBitmapPixels * _density, _heightInBitmapPixels * _density); } // This scale makes sure that 1 CSS pixel is translated to the correct @@ -403,7 +410,6 @@ class CanvasPool extends _SaveStackTracking { return _canvas!.toDataURL(); } - @override void save() { super.save(); @@ -488,8 +494,7 @@ class CanvasPool extends _SaveStackTracking { // This matrix is sufficient to represent 2D rotates, translates, scales, // and skews. if (_canvas != null) { - context.transform(matrix4[0], matrix4[1], matrix4[4], matrix4[5], - matrix4[12], matrix4[13]); + context.transform(matrix4[0], matrix4[1], matrix4[4], matrix4[5], matrix4[12], matrix4[13]); } } @@ -653,8 +658,12 @@ class CanvasPool extends _SaveStackTracking { if (shaderBounds == null) { context.rect(rect.left, rect.top, rect.width, rect.height); } else { - context.rect(rect.left - shaderBounds.left, rect.top - shaderBounds.top, - rect.width, rect.height); + context.rect( + rect.left - shaderBounds.left, + rect.top - shaderBounds.top, + rect.width, + rect.height, + ); } contextHandle.paint(style); } @@ -662,8 +671,12 @@ class CanvasPool extends _SaveStackTracking { /// Applies path to drawing context, preparing for fill and other operations. /// /// WARNING: Don't refactor _runPath/_runPathWithOffset. Latency sensitive - void _runPathWithOffset(DomCanvasRenderingContext2D ctx, SurfacePath path, - double offsetX, double offsetY) { + void _runPathWithOffset( + DomCanvasRenderingContext2D ctx, + SurfacePath path, + double offsetX, + double offsetY, + ) { ctx.beginPath(); final Float32List p = _runBuffer; final PathRefIterator iter = PathRefIterator(path.pathRef); @@ -675,11 +688,16 @@ class CanvasPool extends _SaveStackTracking { case SPath.kLineVerb: ctx.lineTo(p[2] + offsetX, p[3] + offsetY); case SPath.kCubicVerb: - ctx.bezierCurveTo(p[2] + offsetX, p[3] + offsetY, - p[4] + offsetX, p[5] + offsetY, p[6] + offsetX, p[7] + offsetY); + ctx.bezierCurveTo( + p[2] + offsetX, + p[3] + offsetY, + p[4] + offsetX, + p[5] + offsetY, + p[6] + offsetX, + p[7] + offsetY, + ); case SPath.kQuadVerb: - ctx.quadraticCurveTo(p[2] + offsetX, p[3] + offsetY, - p[4] + offsetX, p[5] + offsetY); + ctx.quadraticCurveTo(p[2] + offsetX, p[3] + offsetY, p[4] + offsetX, p[5] + offsetY); case SPath.kConicVerb: final double w = iter.conicWeight; final Conic conic = Conic(p[0], p[1], p[2], p[3], p[4], p[5], w); @@ -690,8 +708,7 @@ class CanvasPool extends _SaveStackTracking { final double p1y = points[i].dy; final double p2x = points[i + 1].dx; final double p2y = points[i + 1].dy; - ctx.quadraticCurveTo(p1x + offsetX, p1y + offsetY, - p2x + offsetX, p2y + offsetY); + ctx.quadraticCurveTo(p1x + offsetX, p1y + offsetY, p2x + offsetX, p2y + offsetY); } case SPath.kCloseVerb: ctx.closePath(); @@ -705,8 +722,10 @@ class CanvasPool extends _SaveStackTracking { void drawRRect(ui.RRect roundRect, ui.PaintingStyle? style) { final ui.Rect? shaderBounds = contextHandle._shaderBounds; RRectToCanvasRenderer(context).render( - shaderBounds == null ? roundRect - : roundRect.shift(ui.Offset(-shaderBounds.left, -shaderBounds.top))); + shaderBounds == null + ? roundRect + : roundRect.shift(ui.Offset(-shaderBounds.left, -shaderBounds.top)), + ); contextHandle.paint(style); } @@ -731,12 +750,9 @@ class CanvasPool extends _SaveStackTracking { void drawOval(ui.Rect rect, ui.PaintingStyle? style) { context.beginPath(); final ui.Rect? shaderBounds = contextHandle._shaderBounds; - final double cx = shaderBounds == null ? rect.center.dx : - rect.center.dx - shaderBounds.left; - final double cy = shaderBounds == null ? rect.center.dy : - rect.center.dy - shaderBounds.top; - drawEllipse(context, cx, cy, rect.width / 2, - rect.height / 2, 0, 0, 2.0 * math.pi, false); + final double cx = shaderBounds == null ? rect.center.dx : rect.center.dx - shaderBounds.left; + final double cy = shaderBounds == null ? rect.center.dy : rect.center.dy - shaderBounds.top; + drawEllipse(context, cx, cy, rect.width / 2, rect.height / 2, 0, 0, 2.0 * math.pi, false); contextHandle.paint(style); } @@ -756,8 +772,7 @@ class CanvasPool extends _SaveStackTracking { if (shaderBounds == null) { _runPath(context, path as SurfacePath); } else { - _runPathWithOffset(context, path as SurfacePath, - -shaderBounds.left, -shaderBounds.top); + _runPathWithOffset(context, path as SurfacePath, -shaderBounds.left, -shaderBounds.top); } contextHandle.paintPath(style, path.fillType); } @@ -767,8 +782,7 @@ class CanvasPool extends _SaveStackTracking { } /// Draws a shadow for a Path representing the given material elevation. - void drawShadow(ui.Path path, ui.Color color, double elevation, - bool transparentOccluder) { + void drawShadow(ui.Path path, ui.Color color, double elevation, bool transparentOccluder) { final SurfaceShadowData? shadow = computeShadow(path.getBounds(), elevation); if (shadow != null) { // On April 2020 Web canvas 2D did not support shadow color alpha. So @@ -796,7 +810,8 @@ class CanvasPool extends _SaveStackTracking { // which is undesirable. context.translate(shadow.offset.dx, shadow.offset.dy); context.filter = maskFilterToCanvasFilter( - ui.MaskFilter.blur(ui.BlurStyle.normal, shadow.blurWidth)); + ui.MaskFilter.blur(ui.BlurStyle.normal, shadow.blurWidth), + ); context.strokeStyle = ''; context.fillStyle = solidColor; } else { @@ -871,6 +886,7 @@ class ContextStateHandle { /// Associated canvas element context tracked by this context state. final DomCanvasRenderingContext2D context; final CanvasPool _canvasPool; + /// Dpi of context. final double density; ui.BlendMode? _currentBlendMode = ui.BlendMode.srcOver; @@ -886,8 +902,7 @@ class ContextStateHandle { set blendMode(ui.BlendMode? blendMode) { if (blendMode != _currentBlendMode) { _currentBlendMode = blendMode; - context.globalCompositeOperation = - blendModeToCssMixBlendMode(blendMode) ?? 'source-over'; + context.globalCompositeOperation = blendModeToCssMixBlendMode(blendMode) ?? 'source-over'; } } @@ -955,7 +970,8 @@ class ContextStateHandle { /// This is used in screenshot tests to test Safari codepaths. static bool debugEmulateWebKitMaskFilter = false; - bool get _renderMaskFilterForWebkit => ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit || debugEmulateWebKitMaskFilter; + bool get _renderMaskFilterForWebkit => + ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit || debugEmulateWebKitMaskFilter; /// Sets paint properties on the current canvas. /// @@ -977,8 +993,11 @@ class ContextStateHandle { if (paint.shader != null) { if (paint.shader is EngineGradient) { final EngineGradient engineShader = paint.shader! as EngineGradient; - final Object paintStyle = - engineShader.createPaintStyle(_canvasPool.context, shaderBounds, density); + final Object paintStyle = engineShader.createPaintStyle( + _canvasPool.context, + shaderBounds, + density, + ); fillStyle = paintStyle; strokeStyle = paintStyle; _shaderBounds = shaderBounds; @@ -986,8 +1005,11 @@ class ContextStateHandle { context.translate(shaderBounds!.left, shaderBounds.top); } else if (paint.shader is EngineImageShader) { final EngineImageShader imageShader = paint.shader! as EngineImageShader; - final Object paintStyle = - imageShader.createPaintStyle(_canvasPool.context, shaderBounds, density); + final Object paintStyle = imageShader.createPaintStyle( + _canvasPool.context, + shaderBounds, + density, + ); fillStyle = paintStyle; strokeStyle = paintStyle; if (imageShader.requiresTileOffset) { @@ -1155,11 +1177,12 @@ class _SaveStackTracking { /// Saves current clip and transform on the save stack. @mustCallSuper void save() { - _saveStack.add(SaveStackEntry( - transform: _currentTransform.clone(), - clipStack: - clipStack == null ? null : List.from(clipStack!), - )); + _saveStack.add( + SaveStackEntry( + transform: _currentTransform.clone(), + clipStack: clipStack == null ? null : List.from(clipStack!), + ), + ); } /// Restores current clip and transform from the save stack. diff --git a/lib/web_ui/lib/src/engine/canvaskit/canvas.dart b/lib/web_ui/lib/src/engine/canvaskit/canvas.dart index dd7cb93f39853..b3b0810de2035 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/canvas.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/canvas.dart @@ -45,40 +45,22 @@ class CkCanvas { } void clipPath(CkPath path, bool doAntiAlias) { - skCanvas.clipPath( - path.skiaObject, - _clipOpIntersect, - doAntiAlias, - ); + skCanvas.clipPath(path.skiaObject, _clipOpIntersect, doAntiAlias); } void clipRRect(ui.RRect rrect, bool doAntiAlias) { - skCanvas.clipRRect( - toSkRRect(rrect), - _clipOpIntersect, - doAntiAlias, - ); + skCanvas.clipRRect(toSkRRect(rrect), _clipOpIntersect, doAntiAlias); } void clipRect(ui.Rect rect, ui.ClipOp clipOp, bool doAntiAlias) { - skCanvas.clipRect( - toSkRect(rect), - toSkClipOp(clipOp), - doAntiAlias, - ); + skCanvas.clipRect(toSkRect(rect), toSkClipOp(clipOp), doAntiAlias); } ui.Rect getDeviceClipBounds() { return rectFromSkIRect(skCanvas.getDeviceClipBounds()); } - void drawArc( - ui.Rect oval, - double startAngle, - double sweepAngle, - bool useCenter, - CkPaint paint, - ) { + void drawArc(ui.Rect oval, double startAngle, double sweepAngle, bool useCenter, CkPaint paint) { const double toDegrees = 180 / math.pi; final skPaint = paint.toSkPaint(); @@ -115,29 +97,17 @@ class CkCanvas { void drawCircle(ui.Offset c, double radius, CkPaint paint) { final skPaint = paint.toSkPaint(); - skCanvas.drawCircle( - c.dx, - c.dy, - radius, - skPaint, - ); + skCanvas.drawCircle(c.dx, c.dy, radius, skPaint); skPaint.delete(); } void drawColor(ui.Color color, ui.BlendMode blendMode) { - skCanvas.drawColorInt( - color.value.toDouble(), - toSkBlendMode(blendMode), - ); + skCanvas.drawColorInt(color.value.toDouble(), toSkBlendMode(blendMode)); } void drawDRRect(ui.RRect outer, ui.RRect inner, CkPaint paint) { final skPaint = paint.toSkPaint(); - skCanvas.drawDRRect( - toSkRRect(outer), - toSkRRect(inner), - skPaint, - ); + skCanvas.drawDRRect(toSkRRect(outer), toSkRRect(inner), skPaint); skPaint.delete(); } @@ -191,8 +161,7 @@ class CkCanvas { skPaint.delete(); } - void drawImageNine( - CkImage image, ui.Rect center, ui.Rect dst, CkPaint paint) { + void drawImageNine(CkImage image, ui.Rect center, ui.Rect dst, CkPaint paint) { final skPaint = paint.toSkPaint(defaultBlurTileMode: ui.TileMode.clamp); skCanvas.drawImageNine( image.skImage, @@ -206,22 +175,13 @@ class CkCanvas { void drawLine(ui.Offset p1, ui.Offset p2, CkPaint paint) { final skPaint = paint.toSkPaint(); - skCanvas.drawLine( - p1.dx, - p1.dy, - p2.dx, - p2.dy, - skPaint, - ); + skCanvas.drawLine(p1.dx, p1.dy, p2.dx, p2.dy, skPaint); skPaint.delete(); } void drawOval(ui.Rect rect, CkPaint paint) { final skPaint = paint.toSkPaint(); - skCanvas.drawOval( - toSkRect(rect), - skPaint, - ); + skCanvas.drawOval(toSkRect(rect), skPaint); skPaint.delete(); } @@ -232,11 +192,7 @@ class CkCanvas { } void drawParagraph(CkParagraph paragraph, ui.Offset offset) { - skCanvas.drawParagraph( - paragraph.skiaObject, - offset.dx, - offset.dy, - ); + skCanvas.drawParagraph(paragraph.skiaObject, offset.dx, offset.dy); } void drawPath(CkPath path, CkPaint paint) { @@ -252,20 +208,13 @@ class CkCanvas { void drawPoints(CkPaint paint, ui.PointMode pointMode, Float32List points) { final skPaint = paint.toSkPaint(); - skCanvas.drawPoints( - toSkPointMode(pointMode), - points, - skPaint, - ); + skCanvas.drawPoints(toSkPointMode(pointMode), points, skPaint); skPaint.delete(); } void drawRRect(ui.RRect rrect, CkPaint paint) { final skPaint = paint.toSkPaint(); - skCanvas.drawRRect( - toSkRRect(rrect), - skPaint, - ); + skCanvas.drawRRect(toSkRRect(rrect), skPaint); skPaint.delete(); } @@ -275,20 +224,20 @@ class CkCanvas { skPaint.delete(); } - void drawShadow( - CkPath path, ui.Color color, double elevation, bool transparentOccluder) { - drawSkShadow(skCanvas, path, color, elevation, transparentOccluder, - EngineFlutterDisplay.instance.devicePixelRatio); + void drawShadow(CkPath path, ui.Color color, double elevation, bool transparentOccluder) { + drawSkShadow( + skCanvas, + path, + color, + elevation, + transparentOccluder, + EngineFlutterDisplay.instance.devicePixelRatio, + ); } - void drawVertices( - CkVertices vertices, ui.BlendMode blendMode, CkPaint paint) { + void drawVertices(CkVertices vertices, ui.BlendMode blendMode, CkPaint paint) { final skPaint = paint.toSkPaint(); - skCanvas.drawVertices( - vertices.skiaObject, - toSkBlendMode(blendMode), - skPaint, - ); + skCanvas.drawVertices(vertices.skiaObject, toSkBlendMode(blendMode), skPaint); skPaint.delete(); } @@ -310,13 +259,7 @@ class CkCanvas { void saveLayer(ui.Rect bounds, CkPaint? paint) { final skPaint = paint?.toSkPaint(); - skCanvas.saveLayer( - skPaint, - toSkRect(bounds), - null, - null, - canvasKit.TileMode.Clamp, - ); + skCanvas.saveLayer(skPaint, toSkRect(bounds), null, null, canvasKit.TileMode.Clamp); skPaint?.delete(); } @@ -326,8 +269,7 @@ class CkCanvas { skPaint?.delete(); } - void saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter, - [CkPaint? paint]) { + void saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter, [CkPaint? paint]) { final CkManagedSkImageFilterConvertible convertible; if (filter is ui.ColorFilter) { convertible = createCkColorFilter(filter as EngineColorFilter)!; diff --git a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart index b9fdcfce7bf52..1e69dcef72e69 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart @@ -77,8 +77,7 @@ extension CanvasKitExtension on CanvasKit { external SkFontSlantEnum get FontSlant; @JS('MakeAnimatedImageFromEncoded') - external SkAnimatedImage? _MakeAnimatedImageFromEncoded( - JSUint8Array imageData); + external SkAnimatedImage? _MakeAnimatedImageFromEncoded(JSUint8Array imageData); SkAnimatedImage? MakeAnimatedImageFromEncoded(Uint8List imageData) => _MakeAnimatedImageFromEncoded(imageData.toJS); @@ -103,32 +102,20 @@ extension CanvasKitExtension on CanvasKit { Float32List? textureCoordinates, Uint32List? colors, Uint16List? indices, - ) => _MakeVertices(mode, positions.toJS, textureCoordinates?.toJS, - colors?.toJS, indices?.toJS); + ) => _MakeVertices(mode, positions.toJS, textureCoordinates?.toJS, colors?.toJS, indices?.toJS); external SkParagraphBuilderNamespace get ParagraphBuilder; - external SkParagraphStyle ParagraphStyle( - SkParagraphStyleProperties properties); + external SkParagraphStyle ParagraphStyle(SkParagraphStyleProperties properties); external SkTextStyle TextStyle(SkTextStyleProperties properties); external SkSurface MakeWebGLCanvasSurface(DomCanvasElement canvas); @JS('MakeSurface') - external SkSurface _MakeSurface( - JSNumber width, - JSNumber height, - ); - SkSurface MakeSurface( - double width, - double height, - ) => _MakeSurface(width.toJS, height.toJS); + external SkSurface _MakeSurface(JSNumber width, JSNumber height); + SkSurface MakeSurface(double width, double height) => _MakeSurface(width.toJS, height.toJS); @JS('getDataBytes') - external JSUint8Array _getDataBytes( - SkData skData, - ); - Uint8List getDataBytes( - SkData skData, - ) => _getDataBytes(skData).toDart; + external JSUint8Array _getDataBytes(SkData skData); + Uint8List getDataBytes(SkData skData) => _getDataBytes(skData).toDart; // Text decoration enum is embedded in the CanvasKit object itself. @JS('NoDecoration') @@ -158,23 +145,21 @@ extension CanvasKitExtension on CanvasKit { external SkTypefaceFactory get Typeface; @JS('GetWebGLContext') - external JSNumber _GetWebGLContext( - DomCanvasElement canvas, SkWebGLContextOptions options); - double GetWebGLContext( - DomCanvasElement canvas, SkWebGLContextOptions options) => - _GetWebGLContext(canvas, options).toDartDouble; + external JSNumber _GetWebGLContext(DomCanvasElement canvas, SkWebGLContextOptions options); + double GetWebGLContext(DomCanvasElement canvas, SkWebGLContextOptions options) => + _GetWebGLContext(canvas, options).toDartDouble; @JS('GetWebGLContext') external JSNumber _GetOffscreenWebGLContext( - DomOffscreenCanvas canvas, SkWebGLContextOptions options); - double GetOffscreenWebGLContext( - DomOffscreenCanvas canvas, SkWebGLContextOptions options) => + DomOffscreenCanvas canvas, + SkWebGLContextOptions options, + ); + double GetOffscreenWebGLContext(DomOffscreenCanvas canvas, SkWebGLContextOptions options) => _GetOffscreenWebGLContext(canvas, options).toDartDouble; @JS('MakeGrContext') external SkGrContext _MakeGrContext(JSNumber glContext); - SkGrContext MakeGrContext(double glContext) => - _MakeGrContext(glContext.toJS); + SkGrContext MakeGrContext(double glContext) => _MakeGrContext(glContext.toJS); @JS('MakeOnScreenGLSurface') external SkSurface? _MakeOnScreenGLSurface( @@ -192,20 +177,19 @@ extension CanvasKitExtension on CanvasKit { ColorSpace colorSpace, int sampleCount, int stencil, - ) => _MakeOnScreenGLSurface(grContext, width.toJS, height.toJS, colorSpace, - sampleCount.toJS, stencil.toJS); + ) => _MakeOnScreenGLSurface( + grContext, + width.toJS, + height.toJS, + colorSpace, + sampleCount.toJS, + stencil.toJS, + ); @JS('MakeRenderTarget') - external SkSurface? _MakeRenderTarget( - SkGrContext grContext, - JSNumber width, - JSNumber height, - ); - SkSurface? MakeRenderTarget( - SkGrContext grContext, - int width, - int height, - ) => _MakeRenderTarget(grContext, width.toJS, height.toJS); + external SkSurface? _MakeRenderTarget(SkGrContext grContext, JSNumber width, JSNumber height); + SkSurface? MakeRenderTarget(SkGrContext grContext, int width, int height) => + _MakeRenderTarget(grContext, width.toJS, height.toJS); external SkSurface MakeSWCanvasSurface(DomCanvasElement canvas); @@ -220,22 +204,12 @@ extension CanvasKitExtension on CanvasKit { /// parameters specified in [SkImageInfo] passed [SkImage.readPixels] must /// match [info]. @JS('MakeImage') - external SkImage? _MakeImage( - SkImageInfo info, - JSUint8Array pixels, - JSNumber bytesPerRow, - ); - SkImage? MakeImage( - SkImageInfo info, - Uint8List pixels, - double bytesPerRow, - ) => _MakeImage(info, pixels.toJS, bytesPerRow.toJS); + external SkImage? _MakeImage(SkImageInfo info, JSUint8Array pixels, JSNumber bytesPerRow); + SkImage? MakeImage(SkImageInfo info, Uint8List pixels, double bytesPerRow) => + _MakeImage(info, pixels.toJS, bytesPerRow.toJS); @JS('MakeLazyImageFromTextureSource') - external SkImage? _MakeLazyImageFromTextureSource2( - JSAny src, - SkPartialImageInfo info, - ); + external SkImage? _MakeLazyImageFromTextureSource2(JSAny src, SkPartialImageInfo info); @JS('MakeLazyImageFromTextureSource') external SkImage? _MakeLazyImageFromTextureSource3( @@ -244,19 +218,11 @@ extension CanvasKitExtension on CanvasKit { JSBoolean srcIsPremultiplied, ); - SkImage? MakeLazyImageFromTextureSourceWithInfo( - Object src, - SkPartialImageInfo info, - ) => _MakeLazyImageFromTextureSource2(src.toJSAnyShallow, info); - - SkImage? MakeLazyImageFromImageBitmap( - DomImageBitmap imageBitmap, - bool hasPremultipliedAlpha, - ) => _MakeLazyImageFromTextureSource3( - imageBitmap as JSObject, - 0.toJS, - hasPremultipliedAlpha.toJS, - ); + SkImage? MakeLazyImageFromTextureSourceWithInfo(Object src, SkPartialImageInfo info) => + _MakeLazyImageFromTextureSource2(src.toJSAnyShallow, info); + + SkImage? MakeLazyImageFromImageBitmap(DomImageBitmap imageBitmap, bool hasPremultipliedAlpha) => + _MakeLazyImageFromTextureSource3(imageBitmap as JSObject, 0.toJS, hasPremultipliedAlpha.toJS); } @JS() @@ -270,16 +236,13 @@ extension CanvasKitModuleExtension on CanvasKitModule { typedef LocateFileCallback = String Function(String file, String unusedBase); -JSFunction createLocateFileCallback(LocateFileCallback callback) => - callback.toJS; +JSFunction createLocateFileCallback(LocateFileCallback callback) => callback.toJS; @JS() @anonymous @staticInterop class CanvasKitInitOptions { - external factory CanvasKitInitOptions({ - required JSFunction locateFile, - }); + external factory CanvasKitInitOptions({required JSFunction locateFile}); } @JS('window.flutterCanvasKit.ColorSpace.SRGB') @@ -297,9 +260,7 @@ class SkWebGLContextOptions { required double antialias, // WebGL version: 1 or 2. required double majorVersion, - }) => SkWebGLContextOptions._( - antialias: antialias.toJS, majorVersion: majorVersion.toJS - ); + }) => SkWebGLContextOptions._(antialias: antialias.toJS, majorVersion: majorVersion.toJS); external factory SkWebGLContextOptions._({ required JSNumber antialias, // WebGL version: 1 or 2. @@ -334,8 +295,7 @@ class SkGrContext {} extension SkGrContextExtension on SkGrContext { @JS('setResourceCacheLimitBytes') external JSVoid _setResourceCacheLimitBytes(JSNumber limit); - void setResourceCacheLimitBytes(double limit) => - _setResourceCacheLimitBytes(limit.toJS); + void setResourceCacheLimitBytes(double limit) => _setResourceCacheLimitBytes(limit.toJS); external JSVoid releaseResourcesAndAbandonContext(); external JSVoid delete(); @@ -528,8 +488,7 @@ extension SkTextHeightBehaviorExtension on SkTextHeightBehavior { double get value => _value.toDartDouble; } -final List _skTextHeightBehaviors = - [ +final List _skTextHeightBehaviors = [ canvasKit.TextHeightBehavior.All, canvasKit.TextHeightBehavior.DisableFirstAscent, canvasKit.TextHeightBehavior.DisableLastDescent, @@ -537,7 +496,8 @@ final List _skTextHeightBehaviors = ]; SkTextHeightBehavior toSkTextHeightBehavior(ui.TextHeightBehavior behavior) { - final int index = (behavior.applyHeightToFirstAscent ? 0 : 1 << 0) | + final int index = + (behavior.applyHeightToFirstAscent ? 0 : 1 << 0) | (behavior.applyHeightToLastDescent ? 0 : 1 << 1); return _skTextHeightBehaviors[index]; } @@ -1180,12 +1140,10 @@ extension SkImageExtension on SkImage { SkFilterMode filterMode, SkMipmapMode mipmapMode, Float32List? matrix, // 3x3 matrix - ) => _makeShaderOptions(tileModeX, tileModeY, filterMode, mipmapMode, - matrix?.toJS); + ) => _makeShaderOptions(tileModeX, tileModeY, filterMode, mipmapMode, matrix?.toJS); @JS('readPixels') - external JSUint8Array? _readPixels( - JSNumber srcX, JSNumber srcY, SkImageInfo imageInfo); + external JSUint8Array? _readPixels(JSNumber srcX, JSNumber srcY, SkImageInfo imageInfo); Uint8List? readPixels(double srcX, double srcY, SkImageInfo imageInfo) => _readPixels(srcX.toJS, srcY.toJS, imageInfo)?.toDart; @@ -1223,8 +1181,8 @@ extension SkShaderNamespaceExtension on SkShaderNamespace { Float32List colorStops, SkTileMode tileMode, Float32List? matrix, - ) => _MakeLinearGradient(from.toJS, to.toJS, colors.toJS, colorStops.toJS, - tileMode, matrix?.toJS); + ) => + _MakeLinearGradient(from.toJS, to.toJS, colors.toJS, colorStops.toJS, tileMode, matrix?.toJS); @JS('MakeRadialGradient') external SkShader _MakeRadialGradient( @@ -1244,9 +1202,15 @@ extension SkShaderNamespaceExtension on SkShaderNamespace { SkTileMode tileMode, Float32List? matrix, // 3x3 matrix double flags, - ) => _MakeRadialGradient(center.toJS, radius.toJS, colors.toJS, - colorStops.toJS, tileMode, matrix?.toJS, - flags.toJS); + ) => _MakeRadialGradient( + center.toJS, + radius.toJS, + colors.toJS, + colorStops.toJS, + tileMode, + matrix?.toJS, + flags.toJS, + ); @JS('MakeTwoPointConicalGradient') external SkShader _MakeTwoPointConicalGradient( @@ -1270,9 +1234,17 @@ extension SkShaderNamespaceExtension on SkShaderNamespace { SkTileMode tileMode, Float32List? matrix, // 3x3 matrix double flags, - ) => _MakeTwoPointConicalGradient(focal.toJS, focalRadius.toJS, center.toJS, - radius.toJS, colors.toJS, colorStops.toJS, - tileMode, matrix?.toJS, flags.toJS); + ) => _MakeTwoPointConicalGradient( + focal.toJS, + focalRadius.toJS, + center.toJS, + radius.toJS, + colors.toJS, + colorStops.toJS, + tileMode, + matrix?.toJS, + flags.toJS, + ); @JS('MakeSweepGradient') external SkShader _MakeSweepGradient( @@ -1296,9 +1268,17 @@ extension SkShaderNamespaceExtension on SkShaderNamespace { double flags, double startAngle, double endAngle, - ) => _MakeSweepGradient(cx.toJS, cy.toJS, colors.toJS, colorStops.toJS, - tileMode, matrix?.toJS, flags.toJS, startAngle.toJS, - endAngle.toJS); + ) => _MakeSweepGradient( + cx.toJS, + cy.toJS, + colors.toJS, + colorStops.toJS, + tileMode, + matrix?.toJS, + flags.toJS, + startAngle.toJS, + endAngle.toJS, + ); } @JS() @@ -1319,10 +1299,8 @@ extension SkMaskFilterNamespaceExtension on SkMaskFilterNamespace { // // Returns `null` if [sigma] is 0 or infinite. @JS('MakeBlur') - external SkMaskFilter? _MakeBlur( - SkBlurStyle blurStyle, JSNumber sigma, JSBoolean respectCTM); - SkMaskFilter? MakeBlur( - SkBlurStyle blurStyle, double sigma, bool respectCTM) => + external SkMaskFilter? _MakeBlur(SkBlurStyle blurStyle, JSNumber sigma, JSBoolean respectCTM); + SkMaskFilter? MakeBlur(SkBlurStyle blurStyle, double sigma, bool respectCTM) => _MakeBlur(blurStyle, sigma.toJS, respectCTM.toJS); } @@ -1376,20 +1354,17 @@ abstract class CkFilterOptions {} @anonymous @staticInterop class _CkCubicFilterOptions extends CkFilterOptions { - external factory _CkCubicFilterOptions( - {required JSNumber B, required JSNumber C}); + external factory _CkCubicFilterOptions({required JSNumber B, required JSNumber C}); } @JS() @anonymous @staticInterop class _CkTransformFilterOptions extends CkFilterOptions { - external factory _CkTransformFilterOptions( - {SkFilterMode filter, SkMipmapMode mipmap}); + external factory _CkTransformFilterOptions({SkFilterMode filter, SkMipmapMode mipmap}); } -final Map _filterOptions = - { +final Map _filterOptions = { ui.FilterQuality.none: _CkTransformFilterOptions( filter: canvasKit.FilterMode.Nearest, mipmap: canvasKit.MipmapMode.None, @@ -1402,10 +1377,7 @@ final Map _filterOptions = filter: canvasKit.FilterMode.Linear, mipmap: canvasKit.MipmapMode.Linear, ), - ui.FilterQuality.high: _CkCubicFilterOptions( - B: (1.0 / 3).toJS, - C: (1.0 / 3).toJS, - ), + ui.FilterQuality.high: _CkCubicFilterOptions(B: (1.0 / 3).toJS, C: (1.0 / 3).toJS), }; CkFilterOptions toSkFilterOptions(ui.FilterQuality filterQuality) { @@ -1427,8 +1399,7 @@ class SkColorFilterNamespace {} extension SkColorFilterNamespaceExtension on SkColorFilterNamespace { @JS('MakeBlend') - external SkColorFilter? _MakeBlend( - JSFloat32Array color, SkBlendMode blendMode); + external SkColorFilter? _MakeBlend(JSFloat32Array color, SkBlendMode blendMode); SkColorFilter? MakeBlend(Float32List color, SkBlendMode blendMode) => _MakeBlend(color.toJS, blendMode); @@ -1490,10 +1461,7 @@ extension SkImageFilterNamespaceExtension on SkImageFilterNamespace { JSVoid input, // we don't use this yet ); - external SkImageFilter MakeCompose( - SkImageFilter outer, - SkImageFilter inner, - ); + external SkImageFilter MakeCompose(SkImageFilter outer, SkImageFilter inner); external SkImageFilter MakeDilate( double radiusX, @@ -1522,8 +1490,7 @@ extension SkImageFilterExtension on SkImageFilter { @JS('getOutputBounds') external JSInt32Array _getOutputBounds(JSFloat32Array bounds); - Int32List getOutputBounds(Float32List bounds) => - _getOutputBounds(bounds.toJS).toDart; + Int32List getOutputBounds(Float32List bounds) => _getOutputBounds(bounds.toJS).toDart; } @JS() @@ -1534,8 +1501,7 @@ extension SkPathNamespaceExtension on SkPathNamespace { /// Creates an [SkPath] using commands obtained from [SkPath.toCmds]. @JS('MakeFromCmds') external SkPath _MakeFromCmds(JSAny pathCommands); - SkPath MakeFromCmds(List pathCommands) => - _MakeFromCmds(pathCommands.toJSAnyShallow); + SkPath MakeFromCmds(List pathCommands) => _MakeFromCmds(pathCommands.toJSAnyShallow); /// Creates an [SkPath] by combining [path1] and [path2] using [pathOp]. external SkPath MakeFromOp(SkPath path1, SkPath path2, SkPathOp pathOp); @@ -1602,9 +1568,10 @@ Float32List toSkPoint(ui.Offset offset) { } /// Color stops used when the framework specifies `null`. -final Float32List _kDefaultSkColorStops = Float32List(2) - ..[0] = 0 - ..[1] = 1; +final Float32List _kDefaultSkColorStops = + Float32List(2) + ..[0] = 0 + ..[1] = 1; /// Converts a list of color stops into a Skia-compatible JS array or color stops. /// @@ -1784,23 +1751,13 @@ extension SkPathExtension on SkPath { JSNumber startAngleDegrees, JSNumber sweepAngleDegrees, ); - void addArc( - Float32List oval, - double startAngleDegrees, - double sweepAngleDegrees, - ) => _addArc(oval.toJS, startAngleDegrees.toJS, sweepAngleDegrees.toJS); + void addArc(Float32List oval, double startAngleDegrees, double sweepAngleDegrees) => + _addArc(oval.toJS, startAngleDegrees.toJS, sweepAngleDegrees.toJS); @JS('addOval') - external JSVoid _addOval( - JSFloat32Array oval, - JSBoolean counterClockWise, - JSNumber startIndex, - ); - void addOval( - Float32List oval, - bool counterClockWise, - double startIndex, - ) => _addOval(oval.toJS, counterClockWise.toJS, startIndex.toJS); + external JSVoid _addOval(JSFloat32Array oval, JSBoolean counterClockWise, JSNumber startIndex); + void addOval(Float32List oval, bool counterClockWise, double startIndex) => + _addOval(oval.toJS, counterClockWise.toJS, startIndex.toJS); @JS('addPath') external JSVoid _addPath( @@ -1828,37 +1785,32 @@ extension SkPathExtension on SkPath { double pers1, double pers2, bool extendPath, - ) => _addPath(other, scaleX.toJS, skewX.toJS, transX.toJS, skewY.toJS, - scaleY.toJS, transY.toJS, pers0.toJS, pers1.toJS, pers2.toJS, - extendPath.toJS); + ) => _addPath( + other, + scaleX.toJS, + skewX.toJS, + transX.toJS, + skewY.toJS, + scaleY.toJS, + transY.toJS, + pers0.toJS, + pers1.toJS, + pers2.toJS, + extendPath.toJS, + ); @JS('addPoly') - external JSVoid _addPoly( - JSFloat32Array points, - JSBoolean close, - ); - void addPoly( - Float32List points, - bool close, - ) => _addPoly(points.toJS, close.toJS); + external JSVoid _addPoly(JSFloat32Array points, JSBoolean close); + void addPoly(Float32List points, bool close) => _addPoly(points.toJS, close.toJS); @JS('addRRect') - external JSVoid _addRRect( - JSFloat32Array rrect, - JSBoolean counterClockWise, - ); - void addRRect( - Float32List rrect, - bool counterClockWise, - ) => _addRRect(rrect.toJS, counterClockWise.toJS); + external JSVoid _addRRect(JSFloat32Array rrect, JSBoolean counterClockWise); + void addRRect(Float32List rrect, bool counterClockWise) => + _addRRect(rrect.toJS, counterClockWise.toJS); @JS('addRect') - external JSVoid _addRect( - JSFloat32Array rect, - ); - void addRect( - Float32List rect, - ) => _addRect(rect.toJS); + external JSVoid _addRect(JSFloat32Array rect); + void addRect(Float32List rect) => _addRect(rect.toJS); @JS('arcToOval') external JSVoid _arcToOval( @@ -1872,8 +1824,7 @@ extension SkPathExtension on SkPath { double startAngleDegrees, double sweepAngleDegrees, bool forceMoveTo, - ) => _arcToOval(oval.toJS, startAngleDegrees.toJS, sweepAngleDegrees.toJS, - forceMoveTo.toJS); + ) => _arcToOval(oval.toJS, startAngleDegrees.toJS, sweepAngleDegrees.toJS, forceMoveTo.toJS); @JS('arcToRotated') external JSVoid _arcToRotated( @@ -1893,37 +1844,26 @@ extension SkPathExtension on SkPath { bool counterClockWise, double x, double y, - ) => _arcToRotated(radiusX.toJS, radiusY.toJS, rotation.toJS, - useSmallArc.toJS, counterClockWise.toJS, - x.toJS, y.toJS); + ) => _arcToRotated( + radiusX.toJS, + radiusY.toJS, + rotation.toJS, + useSmallArc.toJS, + counterClockWise.toJS, + x.toJS, + y.toJS, + ); external JSVoid close(); @JS('conicTo') - external JSVoid _conicTo( - JSNumber x1, - JSNumber y1, - JSNumber x2, - JSNumber y2, - JSNumber w, - ); - void conicTo( - double x1, - double y1, - double x2, - double y2, - double w, - ) => _conicTo(x1.toJS, y1.toJS, x2.toJS, y2.toJS, w.toJS); + external JSVoid _conicTo(JSNumber x1, JSNumber y1, JSNumber x2, JSNumber y2, JSNumber w); + void conicTo(double x1, double y1, double x2, double y2, double w) => + _conicTo(x1.toJS, y1.toJS, x2.toJS, y2.toJS, w.toJS); @JS('contains') - external JSBoolean _contains( - JSNumber x, - JSNumber y, - ); - bool contains( - double x, - double y, - ) => _contains(x.toJS, y.toJS).toDart; + external JSBoolean _contains(JSNumber x, JSNumber y); + bool contains(double x, double y) => _contains(x.toJS, y.toJS).toDart; @JS('cubicTo') external JSVoid _cubicTo( @@ -1934,14 +1874,8 @@ extension SkPathExtension on SkPath { JSNumber x3, JSNumber y3, ); - void cubicTo( - double x1, - double y1, - double x2, - double y2, - double x3, - double y3, - ) => _cubicTo(x1.toJS, y1.toJS, x2.toJS, y2.toJS, x3.toJS, y3.toJS); + void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3) => + _cubicTo(x1.toJS, y1.toJS, x2.toJS, y2.toJS, x3.toJS, y3.toJS); @JS('getBounds') external JSFloat32Array _getBounds(); @@ -1956,18 +1890,9 @@ extension SkPathExtension on SkPath { void moveTo(double x, double y) => _moveTo(x.toJS, y.toJS); @JS('quadTo') - external JSVoid _quadTo( - JSNumber x1, - JSNumber y1, - JSNumber x2, - JSNumber y2, - ); - void quadTo( - double x1, - double y1, - double x2, - double y2, - ) => _quadTo(x1.toJS, y1.toJS, x2.toJS, y2.toJS); + external JSVoid _quadTo(JSNumber x1, JSNumber y1, JSNumber x2, JSNumber y2); + void quadTo(double x1, double y1, double x2, double y2) => + _quadTo(x1.toJS, y1.toJS, x2.toJS, y2.toJS); @JS('rArcTo') external JSVoid _rArcTo( @@ -1987,24 +1912,20 @@ extension SkPathExtension on SkPath { bool counterClockWise, double deltaX, double deltaY, - ) => _rArcTo(x.toJS, y.toJS, rotation.toJS, useSmallArc.toJS, - counterClockWise.toJS, deltaX.toJS, deltaY.toJS); + ) => _rArcTo( + x.toJS, + y.toJS, + rotation.toJS, + useSmallArc.toJS, + counterClockWise.toJS, + deltaX.toJS, + deltaY.toJS, + ); @JS('rConicTo') - external JSVoid _rConicTo( - JSNumber x1, - JSNumber y1, - JSNumber x2, - JSNumber y2, - JSNumber w, - ); - void rConicTo( - double x1, - double y1, - double x2, - double y2, - double w, - ) => _rConicTo(x1.toJS, y1.toJS, x2.toJS, y2.toJS, w.toJS); + external JSVoid _rConicTo(JSNumber x1, JSNumber y1, JSNumber x2, JSNumber y2, JSNumber w); + void rConicTo(double x1, double y1, double x2, double y2, double w) => + _rConicTo(x1.toJS, y1.toJS, x2.toJS, y2.toJS, w.toJS); @JS('rCubicTo') external JSVoid _rCubicTo( @@ -2015,14 +1936,8 @@ extension SkPathExtension on SkPath { JSNumber x3, JSNumber y3, ); - void rCubicTo( - double x1, - double y1, - double x2, - double y2, - double x3, - double y3, - ) => _rCubicTo(x1.toJS, y1.toJS, x2.toJS, y2.toJS, x3.toJS, y3.toJS); + void rCubicTo(double x1, double y1, double x2, double y2, double x3, double y3) => + _rCubicTo(x1.toJS, y1.toJS, x2.toJS, y2.toJS, x3.toJS, y3.toJS); @JS('rLineTo') external JSVoid _rLineTo(JSNumber x, JSNumber y); @@ -2033,18 +1948,9 @@ extension SkPathExtension on SkPath { void rMoveTo(double x, double y) => _rMoveTo(x.toJS, y.toJS); @JS('rQuadTo') - external JSVoid _rQuadTo( - JSNumber x1, - JSNumber y1, - JSNumber x2, - JSNumber y2, - ); - void rQuadTo( - double x1, - double y1, - double x2, - double y2, - ) => _rQuadTo(x1.toJS, y1.toJS, x2.toJS, y2.toJS); + external JSVoid _rQuadTo(JSNumber x1, JSNumber y1, JSNumber x2, JSNumber y2); + void rQuadTo(double x1, double y1, double x2, double y2) => + _rQuadTo(x1.toJS, y1.toJS, x2.toJS, y2.toJS); external JSVoid reset(); @@ -2080,9 +1986,17 @@ extension SkPathExtension on SkPath { double pers0, double pers1, double pers2, - ) => _transform(scaleX.toJS, skewX.toJS, transX.toJS, - skewY.toJS, scaleY.toJS, transY.toJS, - pers0.toJS, pers1.toJS, pers2.toJS); + ) => _transform( + scaleX.toJS, + skewX.toJS, + transX.toJS, + skewY.toJS, + scaleY.toJS, + transY.toJS, + pers0.toJS, + pers1.toJS, + pers2.toJS, + ); /// Serializes the path into a list of commands. /// @@ -2098,15 +2012,9 @@ extension SkPathExtension on SkPath { @JS('window.flutterCanvasKit.ContourMeasureIter') @staticInterop class SkContourMeasureIter { - factory SkContourMeasureIter( - SkPath path, - bool forceClosed, - double resScale) => SkContourMeasureIter._(path, forceClosed.toJS, - resScale.toJS); - external factory SkContourMeasureIter._( - SkPath path, - JSBoolean forceClosed, - JSNumber resScale); + factory SkContourMeasureIter(SkPath path, bool forceClosed, double resScale) => + SkContourMeasureIter._(path, forceClosed.toJS, resScale.toJS); + external factory SkContourMeasureIter._(SkPath path, JSBoolean forceClosed, JSNumber resScale); } extension SkContourMeasureIterExtension on SkContourMeasureIter { @@ -2120,15 +2028,13 @@ class SkContourMeasure {} extension SkContourMeasureExtension on SkContourMeasure { @JS('getSegment') - external SkPath _getSegment( - JSNumber start, JSNumber end, JSBoolean startWithMoveTo); + external SkPath _getSegment(JSNumber start, JSNumber end, JSBoolean startWithMoveTo); SkPath getSegment(double start, double end, bool startWithMoveTo) => _getSegment(start.toJS, end.toJS, startWithMoveTo.toJS); @JS('getPosTan') external JSFloat32Array _getPosTan(JSNumber distance); - Float32List getPosTan(double distance) => - _getPosTan(distance.toJS).toDart; + Float32List getPosTan(double distance) => _getPosTan(distance.toJS).toDart; @JS('isClosed') external JSBoolean _isClosed(); @@ -2247,10 +2153,8 @@ class SkPictureRecorder { extension SkPictureRecorderExtension on SkPictureRecorder { @JS('beginRecording') - external SkCanvas _beginRecording( - JSFloat32Array bounds, JSBoolean computeBounds); - SkCanvas beginRecording(Float32List bounds) => - _beginRecording(bounds.toJS, true.toJS); + external SkCanvas _beginRecording(JSFloat32Array bounds, JSBoolean computeBounds); + SkCanvas beginRecording(Float32List bounds) => _beginRecording(bounds.toJS, true.toJS); external SkPicture finishRecordingAsPicture(); external JSVoid delete(); @@ -2272,40 +2176,19 @@ extension SkCanvasExtension on SkCanvas { void clear(Float32List color) => _clear(color.toJS); @JS('clipPath') - external JSVoid _clipPath( - SkPath path, - SkClipOp clipOp, - JSBoolean doAntiAlias, - ); - void clipPath( - SkPath path, - SkClipOp clipOp, - bool doAntiAlias, - ) => _clipPath(path, clipOp, doAntiAlias.toJS); + external JSVoid _clipPath(SkPath path, SkClipOp clipOp, JSBoolean doAntiAlias); + void clipPath(SkPath path, SkClipOp clipOp, bool doAntiAlias) => + _clipPath(path, clipOp, doAntiAlias.toJS); @JS('clipRRect') - external JSVoid _clipRRect( - JSFloat32Array rrect, - SkClipOp clipOp, - JSBoolean doAntiAlias, - ); - void clipRRect( - Float32List rrect, - SkClipOp clipOp, - bool doAntiAlias, - ) => _clipRRect(rrect.toJS, clipOp, doAntiAlias.toJS); + external JSVoid _clipRRect(JSFloat32Array rrect, SkClipOp clipOp, JSBoolean doAntiAlias); + void clipRRect(Float32List rrect, SkClipOp clipOp, bool doAntiAlias) => + _clipRRect(rrect.toJS, clipOp, doAntiAlias.toJS); @JS('clipRect') - external JSVoid _clipRect( - JSFloat32Array rect, - SkClipOp clipOp, - JSBoolean doAntiAlias, - ); - void clipRect( - Float32List rect, - SkClipOp clipOp, - bool doAntiAlias, - ) => _clipRect(rect.toJS, clipOp, doAntiAlias.toJS); + external JSVoid _clipRect(JSFloat32Array rect, SkClipOp clipOp, JSBoolean doAntiAlias); + void clipRect(Float32List rect, SkClipOp clipOp, bool doAntiAlias) => + _clipRect(rect.toJS, clipOp, doAntiAlias.toJS); @JS('getDeviceClipBounds') external JSInt32Array _getDeviceClipBounds(); @@ -2325,8 +2208,7 @@ extension SkCanvasExtension on SkCanvas { double sweepAngleDegrees, bool useCenter, SkPaint paint, - ) => _drawArc(oval.toJS, startAngleDegrees.toJS, sweepAngleDegrees.toJS, - useCenter.toJS, paint); + ) => _drawArc(oval.toJS, startAngleDegrees.toJS, sweepAngleDegrees.toJS, useCenter.toJS, paint); @JS('drawAtlas') external JSVoid _drawAtlas( @@ -2344,44 +2226,21 @@ extension SkCanvasExtension on SkCanvas { SkPaint paint, SkBlendMode blendMode, Uint32List? colors, - ) => _drawAtlas(image, rects.toJS, rstTransforms.toJS, paint, - blendMode, colors?.toJS); + ) => _drawAtlas(image, rects.toJS, rstTransforms.toJS, paint, blendMode, colors?.toJS); @JS('drawCircle') - external JSVoid _drawCircle( - JSNumber x, - JSNumber y, - JSNumber radius, - SkPaint paint, - ); - void drawCircle( - double x, - double y, - double radius, - SkPaint paint, - ) => _drawCircle(x.toJS, y.toJS, radius.toJS, paint); + external JSVoid _drawCircle(JSNumber x, JSNumber y, JSNumber radius, SkPaint paint); + void drawCircle(double x, double y, double radius, SkPaint paint) => + _drawCircle(x.toJS, y.toJS, radius.toJS, paint); @JS('drawColorInt') - external JSVoid _drawColorInt( - JSNumber color, - SkBlendMode blendMode, - ); - void drawColorInt( - double color, - SkBlendMode blendMode, - ) => _drawColorInt(color.toJS, blendMode); + external JSVoid _drawColorInt(JSNumber color, SkBlendMode blendMode); + void drawColorInt(double color, SkBlendMode blendMode) => _drawColorInt(color.toJS, blendMode); @JS('drawDRRect') - external JSVoid _drawDRRect( - JSFloat32Array outer, - JSFloat32Array inner, - SkPaint paint, - ); - void drawDRRect( - Float32List outer, - Float32List inner, - SkPaint paint, - ) => _drawDRRect(outer.toJS, inner.toJS, paint); + external JSVoid _drawDRRect(JSFloat32Array outer, JSFloat32Array inner, SkPaint paint); + void drawDRRect(Float32List outer, Float32List inner, SkPaint paint) => + _drawDRRect(outer.toJS, inner.toJS, paint); @JS('drawImageCubic') external JSVoid _drawImageCubic( @@ -2392,14 +2251,8 @@ extension SkCanvasExtension on SkCanvas { JSNumber C, SkPaint paint, ); - void drawImageCubic( - SkImage image, - double x, - double y, - double B, - double C, - SkPaint paint, - ) => _drawImageCubic(image, x.toJS, y.toJS, B.toJS, C.toJS, paint); + void drawImageCubic(SkImage image, double x, double y, double B, double C, SkPaint paint) => + _drawImageCubic(image, x.toJS, y.toJS, B.toJS, C.toJS, paint); @JS('drawImageOptions') external JSVoid _drawImageOptions( @@ -2453,8 +2306,7 @@ extension SkCanvasExtension on SkCanvas { SkFilterMode filterMode, SkMipmapMode mipmapMode, SkPaint paint, - ) => _drawImageRectOptions(image, src.toJS, dst.toJS, filterMode, mipmapMode, - paint); + ) => _drawImageRectOptions(image, src.toJS, dst.toJS, filterMode, mipmapMode, paint); @JS('drawImageNine') external JSVoid _drawImageNine( @@ -2473,70 +2325,29 @@ extension SkCanvasExtension on SkCanvas { ) => _drawImageNine(image, center.toJS, dst.toJS, filterMode, paint); @JS('drawLine') - external JSVoid _drawLine( - JSNumber x1, - JSNumber y1, - JSNumber x2, - JSNumber y2, - SkPaint paint, - ); - void drawLine( - double x1, - double y1, - double x2, - double y2, - SkPaint paint, - ) => _drawLine(x1.toJS, y1.toJS, x2.toJS, y2.toJS, paint); + external JSVoid _drawLine(JSNumber x1, JSNumber y1, JSNumber x2, JSNumber y2, SkPaint paint); + void drawLine(double x1, double y1, double x2, double y2, SkPaint paint) => + _drawLine(x1.toJS, y1.toJS, x2.toJS, y2.toJS, paint); @JS('drawOval') - external JSVoid _drawOval( - JSFloat32Array rect, - SkPaint paint, - ); - void drawOval( - Float32List rect, - SkPaint paint, - ) => _drawOval(rect.toJS, paint); + external JSVoid _drawOval(JSFloat32Array rect, SkPaint paint); + void drawOval(Float32List rect, SkPaint paint) => _drawOval(rect.toJS, paint); - external JSVoid drawPaint( - SkPaint paint, - ); - external JSVoid drawPath( - SkPath path, - SkPaint paint, - ); + external JSVoid drawPaint(SkPaint paint); + external JSVoid drawPath(SkPath path, SkPaint paint); @JS('drawPoints') - external JSVoid _drawPoints( - SkPointMode pointMode, - JSFloat32Array points, - SkPaint paint, - ); - void drawPoints( - SkPointMode pointMode, - Float32List points, - SkPaint paint, - ) => _drawPoints(pointMode, points.toJS, paint); + external JSVoid _drawPoints(SkPointMode pointMode, JSFloat32Array points, SkPaint paint); + void drawPoints(SkPointMode pointMode, Float32List points, SkPaint paint) => + _drawPoints(pointMode, points.toJS, paint); @JS('drawRRect') - external JSVoid _drawRRect( - JSFloat32Array rrect, - SkPaint paint, - ); - void drawRRect( - Float32List rrect, - SkPaint paint, - ) => _drawRRect(rrect.toJS, paint); + external JSVoid _drawRRect(JSFloat32Array rrect, SkPaint paint); + void drawRRect(Float32List rrect, SkPaint paint) => _drawRRect(rrect.toJS, paint); @JS('drawRect') - external JSVoid _drawRect( - JSFloat32Array rect, - SkPaint paint, - ); - void drawRect( - Float32List rect, - SkPaint paint, - ) => _drawRect(rect.toJS, paint); + external JSVoid _drawRect(JSFloat32Array rect, SkPaint paint); + void drawRect(Float32List rect, SkPaint paint) => _drawRect(rect.toJS, paint); @JS('drawShadow') external JSVoid _drawShadow( @@ -2556,15 +2367,18 @@ extension SkCanvasExtension on SkCanvas { Float32List ambientColor, Float32List spotColor, double flags, - ) => _drawShadow(path, zPlaneParams.toJS, lightPos.toJS, lightRadius.toJS, - ambientColor.toJS, spotColor.toJS, flags.toJS); - - external JSVoid drawVertices( - SkVertices vertices, - SkBlendMode blendMode, - SkPaint paint, + ) => _drawShadow( + path, + zPlaneParams.toJS, + lightPos.toJS, + lightRadius.toJS, + ambientColor.toJS, + spotColor.toJS, + flags.toJS, ); + external JSVoid drawVertices(SkVertices vertices, SkBlendMode blendMode, SkPaint paint); + @JS('save') external JSNumber _save(); double save() => _save().toDartDouble; @@ -2596,16 +2410,9 @@ extension SkCanvasExtension on SkCanvas { void restoreToCount(double count) => _restoreToCount(count.toJS); @JS('rotate') - external JSVoid _rotate( - JSNumber angleDegrees, - JSNumber px, - JSNumber py, - ); - void rotate( - double angleDegrees, - double px, - double py, - ) => _rotate(angleDegrees.toJS, px.toJS, py.toJS); + external JSVoid _rotate(JSNumber angleDegrees, JSNumber px, JSNumber py); + void rotate(double angleDegrees, double px, double py) => + _rotate(angleDegrees.toJS, px.toJS, py.toJS); @JS('scale') external JSVoid _scale(JSNumber x, JSNumber y); @@ -2625,8 +2432,7 @@ extension SkCanvasExtension on SkCanvas { @JS('getLocalToDevice') external JSAny _getLocalToDevice(); - List getLocalToDevice() => _getLocalToDevice().toObjectShallow as - List; + List getLocalToDevice() => _getLocalToDevice().toObjectShallow as List; @JS('quickReject') external JSBoolean _quickReject(JSFloat32Array rect); @@ -2635,16 +2441,9 @@ extension SkCanvasExtension on SkCanvas { external JSVoid drawPicture(SkPicture picture); @JS('drawParagraph') - external JSVoid _drawParagraph( - SkParagraph paragraph, - JSNumber x, - JSNumber y, - ); - void drawParagraph( - SkParagraph paragraph, - double x, - double y, - ) => _drawParagraph(paragraph, x.toJS, y.toJS); + external JSVoid _drawParagraph(SkParagraph paragraph, JSNumber x, JSNumber y); + void drawParagraph(SkParagraph paragraph, double x, double y) => + _drawParagraph(paragraph, x.toJS, y.toJS); } @JS() @@ -2679,7 +2478,7 @@ extension SkParagraphBuilderNamespaceExtension on SkParagraphBuilderNamespace { if (!js_util.hasProperty(this, 'RequiresClientICU')) { return false; } - return js_util.callMethod(this, 'RequiresClientICU', const [],) as bool; + return js_util.callMethod(this, 'RequiresClientICU', const []) as bool; } } @@ -2694,8 +2493,7 @@ extension SkParagraphBuilderExtension on SkParagraphBuilder { void addText(String text) => _addText(text.toJS); external JSVoid pushStyle(SkTextStyle textStyle); - external JSVoid pushPaintStyle( - SkTextStyle textStyle, SkPaint foreground, SkPaint background); + external JSVoid pushPaintStyle(SkTextStyle textStyle, SkPaint foreground, SkPaint background); external JSVoid pop(); @JS('addPlaceholder') @@ -2712,8 +2510,7 @@ extension SkParagraphBuilderExtension on SkParagraphBuilder { SkPlaceholderAlignment alignment, SkTextBaseline baseline, double offset, - ) => _addPlaceholder(width.toJS, height.toJS, alignment, - baseline, offset.toJS); + ) => _addPlaceholder(width.toJS, height.toJS, alignment, baseline, offset.toJS); @JS('getText') external JSString _getTextUtf8(); @@ -2732,23 +2529,19 @@ extension SkParagraphBuilderExtension on SkParagraphBuilder { @JS('setGraphemeBreaksUtf8') external JSVoid _setGraphemeBreaksUtf8(JSUint32Array graphemes); - void setGraphemeBreaksUtf8(Uint32List graphemes) => - _setGraphemeBreaksUtf8(graphemes.toJS); + void setGraphemeBreaksUtf8(Uint32List graphemes) => _setGraphemeBreaksUtf8(graphemes.toJS); @JS('setGraphemeBreaksUtf16') external JSVoid _setGraphemeBreaksUtf16(JSUint32Array graphemes); - void setGraphemeBreaksUtf16(Uint32List graphemes) => - _setGraphemeBreaksUtf16(graphemes.toJS); + void setGraphemeBreaksUtf16(Uint32List graphemes) => _setGraphemeBreaksUtf16(graphemes.toJS); @JS('setLineBreaksUtf8') external JSVoid _setLineBreaksUtf8(JSUint32Array lineBreaks); - void setLineBreaksUtf8(Uint32List lineBreaks) => - _setLineBreaksUtf8(lineBreaks.toJS); + void setLineBreaksUtf8(Uint32List lineBreaks) => _setLineBreaksUtf8(lineBreaks.toJS); @JS('setLineBreaksUtf16') external JSVoid _setLineBreaksUtf16(JSUint32Array lineBreaks); - void setLineBreaksUtf16(Uint32List lineBreaks) => - _setLineBreaksUtf16(lineBreaks.toJS); + void setLineBreaksUtf16(Uint32List lineBreaks) => _setLineBreaksUtf16(lineBreaks.toJS); external SkParagraph build(); external JSVoid delete(); @@ -2820,8 +2613,7 @@ extension SkTextDecorationStyleExtension on SkTextDecorationStyle { double get value => _value.toDartDouble; } -final List _skTextDecorationStyles = - [ +final List _skTextDecorationStyles = [ canvasKit.DecorationStyle.Solid, canvasKit.DecorationStyle.Double, canvasKit.DecorationStyle.Dotted, @@ -2884,8 +2676,7 @@ extension SkPlaceholderAlignmentExtension on SkPlaceholderAlignment { double get value => _value.toDartDouble; } -final List _skPlaceholderAlignments = - [ +final List _skPlaceholderAlignments = [ canvasKit.PlaceholderAlignment.Baseline, canvasKit.PlaceholderAlignment.AboveBaseline, canvasKit.PlaceholderAlignment.BelowBaseline, @@ -2894,8 +2685,7 @@ final List _skPlaceholderAlignments = canvasKit.PlaceholderAlignment.Middle, ]; -SkPlaceholderAlignment toSkPlaceholderAlignment( - ui.PlaceholderAlignment alignment) { +SkPlaceholderAlignment toSkPlaceholderAlignment(ui.PlaceholderAlignment alignment) { return _skPlaceholderAlignments[alignment.index]; } @@ -2909,8 +2699,7 @@ class SkTextStyleProperties { extension SkTextStylePropertiesExtension on SkTextStyleProperties { @JS('backgroundColor') external set _backgroundColor(JSFloat32Array? value); - set backgroundColor(Float32List? value) => - _backgroundColor = value?.toJS; + set backgroundColor(Float32List? value) => _backgroundColor = value?.toJS; @JS('color') external set _color(JSFloat32Array? value); @@ -2926,8 +2715,7 @@ extension SkTextStylePropertiesExtension on SkTextStyleProperties { @JS('decorationThickness') external set _decorationThickness(JSNumber? value); - set decorationThickness(double? value) => - _decorationThickness = value?.toJS; + set decorationThickness(double? value) => _decorationThickness = value?.toJS; @JS('decorationColor') external set _decorationColor(JSFloat32Array? value); @@ -2969,26 +2757,29 @@ extension SkTextStylePropertiesExtension on SkTextStyleProperties { @JS('shadows') external set _shadows(JSArray? value); set shadows(List? value) => - // TODO(joshualitt): remove this cast when we reify JS types on JS - // backends. - // ignore: unnecessary_cast - _shadows = (value as List?)?.toJS; + // TODO(joshualitt): remove this cast when we reify JS types on JS + // backends. + // ignore: unnecessary_cast + _shadows = + (value as List?)?.toJS; @JS('fontFeatures') external set _fontFeatures(JSArray? value); set fontFeatures(List? value) => - // TODO(joshualitt): remove this cast when we reify JS types on JS - // backends. - // ignore: unnecessary_cast - _fontFeatures = (value as List?)?.toJS; + // TODO(joshualitt): remove this cast when we reify JS types on JS + // backends. + // ignore: unnecessary_cast + _fontFeatures = + (value as List?)?.toJS; @JS('fontVariations') external set _fontVariations(JSArray? value); set fontVariations(List? value) => - // TODO(joshualitt): remove this cast when we reify JS types on JS - // backends. - // ignore: unnecessary_cast - _fontVariations = (value as List?)?.toJS; + // TODO(joshualitt): remove this cast when we reify JS types on JS + // backends. + // ignore: unnecessary_cast + _fontVariations = + (value as List?)?.toJS; } @JS() @@ -3001,8 +2792,7 @@ class SkStrutStyleProperties { extension SkStrutStylePropertiesExtension on SkStrutStyleProperties { @JS('fontFamilies') external set _fontFamilies(JSAny? value); - set fontFamilies(List? value) => - _fontFamilies = value?.toJSAnyShallow; + set fontFamilies(List? value) => _fontFamilies = value?.toJSAnyShallow; external set fontStyle(SkFontStyle? value); @@ -3115,10 +2905,8 @@ extension SkFontExtension on SkFont { Uint16List getGlyphIDs(String text) => _getGlyphIDs(text.toJS).toDart; @JS('getGlyphBounds') - external JSVoid _getGlyphBounds( - JSAny glyphs, SkPaint? paint, JSUint8Array? output); - void getGlyphBounds( - List glyphs, SkPaint? paint, Uint8List? output) => + external JSVoid _getGlyphBounds(JSAny glyphs, SkPaint? paint, JSUint8Array? output); + void getGlyphBounds(List glyphs, SkPaint? paint, Uint8List? output) => _getGlyphBounds(glyphs.toJSAnyShallow, paint, output?.toJS); } @@ -3136,20 +2924,17 @@ extension SkFontMgrExtension on SkFontMgr { @JS('MakeTypefaceFromData') external SkTypeface? _MakeTypefaceFromData(JSUint8Array font); - SkTypeface? MakeTypefaceFromData(Uint8List font) => - _MakeTypefaceFromData(font.toJS); + SkTypeface? MakeTypefaceFromData(Uint8List font) => _MakeTypefaceFromData(font.toJS); } @JS('window.flutterCanvasKit.TypefaceFontProvider') @staticInterop -class TypefaceFontProvider extends SkFontMgr { -} +class TypefaceFontProvider extends SkFontMgr {} extension TypefaceFontProviderExtension on TypefaceFontProvider { @JS('registerFont') external JSVoid _registerFont(JSUint8Array font, JSString family); - void registerFont(Uint8List font, String family) => - _registerFont(font.toJS, family.toJS); + void registerFont(Uint8List font, String family) => _registerFont(font.toJS, family.toJS); } @JS() @@ -3235,8 +3020,16 @@ extension SkGlyphClusterInfoExtension on SkGlyphClusterInfo { ui.GlyphInfo get _glyphInfo { final List list = _bounds.toDart.cast(); - final ui.Rect bounds = ui.Rect.fromLTRB(list[0].toDartDouble, list[1].toDartDouble, list[2].toDartDouble, list[3].toDartDouble); - final ui.TextRange textRange = ui.TextRange(start: _textRange.start.toInt(), end: _textRange.end.toInt()); + final ui.Rect bounds = ui.Rect.fromLTRB( + list[0].toDartDouble, + list[1].toDartDouble, + list[2].toDartDouble, + list[3].toDartDouble, + ); + final ui.TextRange textRange = ui.TextRange( + start: _textRange.start.toInt(), + end: _textRange.end.toInt(), + ); return ui.GlyphInfo(bounds, textRange, ui.TextDirection.values[_direction.value.toInt()]); } } @@ -3282,8 +3075,7 @@ extension SkParagraphExtension on SkParagraph { @JS('getLineMetrics') external JSArray _getLineMetrics(); - List getLineMetrics() => - _getLineMetrics().toDart.cast(); + List getLineMetrics() => _getLineMetrics().toDart.cast(); @JS('getLineMetricsAt') external SkLineMetrics? _getLineMetricsAt(JSNumber index); @@ -3325,8 +3117,13 @@ extension SkParagraphExtension on SkParagraph { double end, SkRectHeightStyle heightStyle, SkRectWidthStyle widthStyle, - ) => _getRectsForRange(start.toJS, end.toJS, heightStyle, - widthStyle).toDart.cast(); + ) => + _getRectsForRange( + start.toJS, + end.toJS, + heightStyle, + widthStyle, + ).toDart.cast(); @JS('getRectsForPlaceholders') external JSArray _getRectsForPlaceholders(); @@ -3334,14 +3131,9 @@ extension SkParagraphExtension on SkParagraph { _getRectsForPlaceholders().toDart.cast(); @JS('getGlyphPositionAtCoordinate') - external SkTextPosition _getGlyphPositionAtCoordinate( - JSNumber x, - JSNumber y, - ); - SkTextPosition getGlyphPositionAtCoordinate( - double x, - double y, - ) => _getGlyphPositionAtCoordinate(x.toJS, y.toJS); + external SkTextPosition _getGlyphPositionAtCoordinate(JSNumber x, JSNumber y); + SkTextPosition getGlyphPositionAtCoordinate(double x, double y) => + _getGlyphPositionAtCoordinate(x.toJS, y.toJS); @JS('getGlyphInfoAt') external SkGlyphClusterInfo? _getGlyphInfoAt(JSNumber position); @@ -3349,12 +3141,12 @@ extension SkParagraphExtension on SkParagraph { @JS('getClosestGlyphInfoAtCoordinate') external SkGlyphClusterInfo? _getClosestGlyphInfoAtCoordinate(JSNumber x, JSNumber y); - ui.GlyphInfo? getClosestGlyphInfoAt(double x, double y) => _getClosestGlyphInfoAtCoordinate(x.toJS, y.toJS)?._glyphInfo; + ui.GlyphInfo? getClosestGlyphInfoAt(double x, double y) => + _getClosestGlyphInfoAtCoordinate(x.toJS, y.toJS)?._glyphInfo; @JS('getWordBoundary') external SkTextRange _getWordBoundary(JSNumber position); - SkTextRange getWordBoundary(double position) => - _getWordBoundary(position.toJS); + SkTextRange getWordBoundary(double position) => _getWordBoundary(position.toJS); @JS('layout') external JSVoid _layout(JSNumber width); @@ -3402,14 +3194,9 @@ extension SkVerticesExtension on SkVertices { @anonymous @staticInterop class SkTonalColors { - factory SkTonalColors({ - required Float32List ambient, - required Float32List spot, - }) => SkTonalColors._(ambient: ambient.toJS, spot: spot.toJS); - external factory SkTonalColors._({ - required JSFloat32Array ambient, - required JSFloat32Array spot, - }); + factory SkTonalColors({required Float32List ambient, required Float32List spot}) => + SkTonalColors._(ambient: ambient.toJS, spot: spot.toJS); + external factory SkTonalColors._({required JSFloat32Array ambient, required JSFloat32Array spot}); } extension SkTonalColorsExtension on SkTonalColors { @@ -3443,7 +3230,7 @@ extension TypefaceFontProviderNamespaceExtension on TypefaceFontProviderNamespac @JS() @staticInterop -class FontCollectionNamespace{} +class FontCollectionNamespace {} extension FontCollectionNamespaceExtension on FontCollectionNamespace { external SkFontCollection Make(); @@ -3527,11 +3314,13 @@ class SkImageInfo { required SkColorType colorType, required SkAlphaType alphaType, required ColorSpace colorSpace, - }) => SkImageInfo._(width: width.toJS, - height: height.toJS, - colorType: colorType, - alphaType: alphaType, - colorSpace: colorSpace); + }) => SkImageInfo._( + width: width.toJS, + height: height.toJS, + colorType: colorType, + alphaType: alphaType, + colorSpace: colorSpace, + ); external factory SkImageInfo._({ required JSNumber width, required JSNumber height, @@ -3572,8 +3361,7 @@ extension SkImageInfoExtension on SkImageInfo { @JS('makeWH') external SkImageInfo _makeWH(JSNumber width, JSNumber height); - SkImageInfo makeWH(double width, double height) => - _makeWH(width.toJS, height.toJS); + SkImageInfo makeWH(double width, double height) => _makeWH(width.toJS, height.toJS); } @JS() @@ -3586,11 +3374,13 @@ class SkPartialImageInfo { required SkColorType colorType, required SkAlphaType alphaType, required ColorSpace colorSpace, - }) => SkPartialImageInfo._(width: width.toJS, - height: height.toJS, - colorType: colorType, - alphaType: alphaType, - colorSpace: colorSpace); + }) => SkPartialImageInfo._( + width: width.toJS, + height: height.toJS, + colorType: colorType, + alphaType: alphaType, + colorSpace: colorSpace, + ); external factory SkPartialImageInfo._({ required JSNumber width, required JSNumber height, @@ -3621,21 +3411,17 @@ class SkRuntimeEffect {} @JS('window.flutterCanvasKit.RuntimeEffect.Make') external SkRuntimeEffect? _MakeRuntimeEffect(JSString program); -SkRuntimeEffect? MakeRuntimeEffect(String program) => - _MakeRuntimeEffect(program.toJS); +SkRuntimeEffect? MakeRuntimeEffect(String program) => _MakeRuntimeEffect(program.toJS); extension SkSkRuntimeEffectExtension on SkRuntimeEffect { @JS('makeShader') external SkShader? _makeShader(JSAny uniforms); - SkShader? makeShader(SkFloat32List uniforms) => - _makeShader(uniforms.toJSAnyShallow); + SkShader? makeShader(SkFloat32List uniforms) => _makeShader(uniforms.toJSAnyShallow); @JS('makeShaderWithChildren') external SkShader? _makeShaderWithChildren(JSAny uniforms, JSAny children); - SkShader? makeShaderWithChildren( - SkFloat32List uniforms, List children) => - _makeShaderWithChildren(uniforms.toJSAnyShallow, - children.toJSAnyShallow); + SkShader? makeShaderWithChildren(SkFloat32List uniforms, List children) => + _makeShaderWithChildren(uniforms.toJSAnyShallow, children.toJSAnyShallow); } const String _kFullCanvasKitJsFileName = 'canvaskit.js'; @@ -3657,14 +3443,15 @@ List getCanvasKitJsFileNames(CanvasKitVariant variant) { return [_kChromiumCanvasKitJsFileName]; } } + Iterable get _canvasKitJsUrls { - return getCanvasKitJsFileNames(configuration.canvasKitVariant).map( - (String filename) => '$_canvasKitBaseUrl$filename', - ); + return getCanvasKitJsFileNames( + configuration.canvasKitVariant, + ).map((String filename) => '$_canvasKitBaseUrl$filename'); } + @visibleForTesting -String canvasKitWasmModuleUrl(String file, String canvasKitBase) => - canvasKitBase + file; +String canvasKitWasmModuleUrl(String file, String canvasKitBase) => canvasKitBase + file; /// Download and initialize the CanvasKit module. /// @@ -3673,9 +3460,13 @@ String canvasKitWasmModuleUrl(String file, String canvasKitBase) => Future downloadCanvasKit() async { final CanvasKitModule canvasKitModule = await _downloadOneOf(_canvasKitJsUrls); - final CanvasKit canvasKit = (await canvasKitModule.defaultExport(CanvasKitInitOptions( - locateFile: createLocateFileCallback(canvasKitWasmModuleUrl), - )).toDart) as CanvasKit; + final CanvasKit canvasKit = + (await canvasKitModule + .defaultExport( + CanvasKitInitOptions(locateFile: createLocateFileCallback(canvasKitWasmModuleUrl)), + ) + .toDart) + as CanvasKit; if (canvasKit.ParagraphBuilder.RequiresClientICU() && !browserSupportsCanvaskitChromium) { throw Exception( @@ -3701,9 +3492,7 @@ Future _downloadOneOf(Iterable urls) async { } // Reaching this point means that all URLs failed to download. - throw Exception( - 'Failed to download any of the following CanvasKit URLs: $urls', - ); + throw Exception('Failed to download any of the following CanvasKit URLs: $urls'); } String _resolveUrl(String url) { diff --git a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_canvas.dart b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_canvas.dart index 70fb9a79a366f..d283d3d11abcd 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_canvas.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_canvas.dart @@ -22,8 +22,7 @@ import 'vertices.dart'; class CanvasKitCanvas implements ui.Canvas { factory CanvasKitCanvas(ui.PictureRecorder recorder, [ui.Rect? cullRect]) { if (recorder.isRecording) { - throw ArgumentError( - '"recorder" must not already be associated with another Canvas.'); + throw ArgumentError('"recorder" must not already be associated with another Canvas.'); } cullRect ??= ui.Rect.largest; final CkPictureRecorder ckRecorder = recorder as CkPictureRecorder; @@ -112,8 +111,7 @@ class CanvasKitCanvas implements ui.Canvas { } @override - void clipRect(ui.Rect rect, - {ui.ClipOp clipOp = ui.ClipOp.intersect, bool doAntiAlias = true}) { + void clipRect(ui.Rect rect, {ui.ClipOp clipOp = ui.ClipOp.intersect, bool doAntiAlias = true}) { assert(rectIsValid(rect)); _clipRect(rect, clipOp, doAntiAlias); } @@ -233,14 +231,18 @@ class CanvasKitCanvas implements ui.Canvas { } @override - void drawArc(ui.Rect rect, double startAngle, double sweepAngle, - bool useCenter, ui.Paint paint) { + void drawArc(ui.Rect rect, double startAngle, double sweepAngle, bool useCenter, ui.Paint paint) { assert(rectIsValid(rect)); _drawArc(rect, startAngle, sweepAngle, useCenter, paint); } - void _drawArc(ui.Rect rect, double startAngle, double sweepAngle, - bool useCenter, ui.Paint paint) { + void _drawArc( + ui.Rect rect, + double startAngle, + double sweepAngle, + bool useCenter, + ui.Paint paint, + ) { _canvas.drawArc(rect, startAngle, sweepAngle, useCenter, paint as CkPaint); } @@ -263,8 +265,7 @@ class CanvasKitCanvas implements ui.Canvas { } @override - void drawImageNine( - ui.Image image, ui.Rect center, ui.Rect dst, ui.Paint paint) { + void drawImageNine(ui.Image image, ui.Rect center, ui.Rect dst, ui.Paint paint) { assert(rectIsValid(center)); assert(rectIsValid(dst)); _canvas.drawImageNine(image as CkImage, center, dst, paint as CkPaint); @@ -286,45 +287,35 @@ class CanvasKitCanvas implements ui.Canvas { } @override - void drawPoints( - ui.PointMode pointMode, List points, ui.Paint paint) { + void drawPoints(ui.PointMode pointMode, List points, ui.Paint paint) { final SkFloat32List skPoints = toMallocedSkPoints(points); - _canvas.drawPoints( - paint as CkPaint, - pointMode, - skPoints.toTypedArray(), - ); + _canvas.drawPoints(paint as CkPaint, pointMode, skPoints.toTypedArray()); free(skPoints); } @override - void drawRawPoints( - ui.PointMode pointMode, Float32List points, ui.Paint paint) { + void drawRawPoints(ui.PointMode pointMode, Float32List points, ui.Paint paint) { if (points.length % 2 != 0) { throw ArgumentError('"points" must have an even number of values.'); } - _canvas.drawPoints( - paint as CkPaint, - pointMode, - points, - ); + _canvas.drawPoints(paint as CkPaint, pointMode, points); } @override - void drawVertices( - ui.Vertices vertices, ui.BlendMode blendMode, ui.Paint paint) { + void drawVertices(ui.Vertices vertices, ui.BlendMode blendMode, ui.Paint paint) { _canvas.drawVertices(vertices as CkVertices, blendMode, paint as CkPaint); } @override void drawAtlas( - ui.Image atlas, - List transforms, - List rects, - List? colors, - ui.BlendMode? blendMode, - ui.Rect? cullRect, - ui.Paint paint) { + ui.Image atlas, + List transforms, + List rects, + List? colors, + ui.BlendMode? blendMode, + ui.Rect? cullRect, + ui.Paint paint, + ) { assert(colors == null || colors.isEmpty || blendMode != null); final int rectCount = rects.length; @@ -333,7 +324,8 @@ class CanvasKitCanvas implements ui.Canvas { } if (colors != null && colors.isNotEmpty && colors.length != rectCount) { throw ArgumentError( - 'If non-null, "colors" length must match that of "transforms" and "rects".'); + 'If non-null, "colors" length must match that of "transforms" and "rects".', + ); } final Float32List rstTransformBuffer = Float32List(rectCount * 4); @@ -360,19 +352,26 @@ class CanvasKitCanvas implements ui.Canvas { final Uint32List? colorBuffer = (colors == null || colors.isEmpty) ? null : toFlatColors(colors); - _drawAtlas(paint, atlas, rstTransformBuffer, rectBuffer, colorBuffer, - blendMode ?? ui.BlendMode.src); + _drawAtlas( + paint, + atlas, + rstTransformBuffer, + rectBuffer, + colorBuffer, + blendMode ?? ui.BlendMode.src, + ); } @override void drawRawAtlas( - ui.Image atlas, - Float32List rstTransforms, - Float32List rects, - Int32List? colors, - ui.BlendMode? blendMode, - ui.Rect? cullRect, - ui.Paint paint) { + ui.Image atlas, + Float32List rstTransforms, + Float32List rects, + Int32List? colors, + ui.BlendMode? blendMode, + ui.Rect? cullRect, + ui.Paint paint, + ) { assert(colors == null || blendMode != null); final int rectCount = rects.length; @@ -380,12 +379,12 @@ class CanvasKitCanvas implements ui.Canvas { throw ArgumentError('"rstTransforms" and "rects" lengths must match.'); } if (rectCount % 4 != 0) { - throw ArgumentError( - '"rstTransforms" and "rects" lengths must be a multiple of four.'); + throw ArgumentError('"rstTransforms" and "rects" lengths must be a multiple of four.'); } if (colors != null && colors.length * 4 != rectCount) { throw ArgumentError( - 'If non-null, "colors" length must be one fourth the length of "rstTransforms" and "rects".'); + 'If non-null, "colors" length must be one fourth the length of "rstTransforms" and "rects".', + ); } Uint32List? unsignedColors; @@ -393,8 +392,7 @@ class CanvasKitCanvas implements ui.Canvas { unsignedColors = colors.buffer.asUint32List(colors.offsetInBytes, colors.length); } - _drawAtlas(paint, atlas, rstTransforms, rects, - unsignedColors, blendMode ?? ui.BlendMode.src); + _drawAtlas(paint, atlas, rstTransforms, rects, unsignedColors, blendMode ?? ui.BlendMode.src); } // TODO(hterkelsen): Pass a cull_rect once CanvasKit supports that. @@ -417,13 +415,11 @@ class CanvasKitCanvas implements ui.Canvas { } @override - void drawShadow(ui.Path path, ui.Color color, double elevation, - bool transparentOccluder) { + void drawShadow(ui.Path path, ui.Color color, double elevation, bool transparentOccluder) { _drawShadow(path, color, elevation, transparentOccluder); } - void _drawShadow(ui.Path path, ui.Color color, double elevation, - bool transparentOccluder) { + void _drawShadow(ui.Path path, ui.Color color, double elevation, bool transparentOccluder) { _canvas.drawShadow(path as CkPath, color, elevation, transparentOccluder); } } diff --git a/lib/web_ui/lib/src/engine/canvaskit/color_filter.dart b/lib/web_ui/lib/src/engine/canvaskit/color_filter.dart index 51e5fcbcd205f..734803a5f58b2 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/color_filter.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/color_filter.dart @@ -20,8 +20,7 @@ import 'native_memory.dart'; /// * [CkPaint.colorFilter], which uses a [ManagedSkColorFilter] to manage /// the lifecycle of its [SkColorFilter]. class ManagedSkColorFilter { - ManagedSkColorFilter(CkColorFilter ckColorFilter) - : colorFilter = ckColorFilter { + ManagedSkColorFilter(CkColorFilter ckColorFilter) : colorFilter = ckColorFilter { _ref = UniqueRef(this, colorFilter._initRawColorFilter(), 'ColorFilter'); } @@ -71,7 +70,8 @@ abstract class CkColorFilter implements CkManagedSkImageFilterConvertible { SkColorFilter _initRawColorFilter(); @override - void withSkImageFilter(SkImageFilterBorrow borrow, { + void withSkImageFilter( + SkImageFilterBorrow borrow, { ui.TileMode defaultBlurTileMode = ui.TileMode.clamp, }) { // Since ColorFilter has a const constructor it cannot store dynamically @@ -109,8 +109,7 @@ Float32List _computeIdentityTransform() { return result; } -SkColorFilter createSkColorFilterFromColorAndBlendMode( - ui.Color color, ui.BlendMode blendMode) { +SkColorFilter createSkColorFilterFromColorAndBlendMode(ui.Color color, ui.BlendMode blendMode) { final SkColorFilter? filter = canvasKit.ColorFilter.MakeBlend( toSharedSkColor1(color), toSkBlendMode(blendMode), @@ -124,7 +123,6 @@ SkColorFilter createSkColorFilterFromColorAndBlendMode( return filter; } - class CkBlendModeColorFilter extends CkColorFilter { const CkBlendModeColorFilter(this.color, this.blendMode); @@ -144,9 +142,7 @@ class CkBlendModeColorFilter extends CkColorFilter { if (runtimeType != other.runtimeType) { return false; } - return other is CkBlendModeColorFilter && - other.color == color && - other.blendMode == blendMode; + return other is CkBlendModeColorFilter && other.color == color && other.blendMode == blendMode; } @override @@ -199,8 +195,7 @@ class CkMatrixColorFilter extends CkColorFilter { class CkLinearToSrgbGammaColorFilter extends CkColorFilter { const CkLinearToSrgbGammaColorFilter(); @override - SkColorFilter _initRawColorFilter() => - canvasKit.ColorFilter.MakeLinearToSRGBGamma(); + SkColorFilter _initRawColorFilter() => canvasKit.ColorFilter.MakeLinearToSRGBGamma(); @override bool operator ==(Object other) => runtimeType == other.runtimeType; @@ -215,8 +210,7 @@ class CkLinearToSrgbGammaColorFilter extends CkColorFilter { class CkSrgbToLinearGammaColorFilter extends CkColorFilter { const CkSrgbToLinearGammaColorFilter(); @override - SkColorFilter _initRawColorFilter() => - canvasKit.ColorFilter.MakeSRGBToLinearGamma(); + SkColorFilter _initRawColorFilter() => canvasKit.ColorFilter.MakeSRGBToLinearGamma(); @override bool operator ==(Object other) => runtimeType == other.runtimeType; @@ -260,20 +254,20 @@ class CkComposeColorFilter extends CkColorFilter { /// avoid repainting. CkColorFilter? createCkColorFilter(EngineColorFilter colorFilter) { switch (colorFilter.type) { - case ColorFilterType.mode: - if (colorFilter.color == null || colorFilter.blendMode == null) { - return null; - } - return CkBlendModeColorFilter(colorFilter.color!, colorFilter.blendMode!); - case ColorFilterType.matrix: - if (colorFilter.matrix == null) { - return null; - } - assert(colorFilter.matrix!.length == 20, 'Color Matrix must have 20 entries.'); - return CkMatrixColorFilter(colorFilter.matrix!); - case ColorFilterType.linearToSrgbGamma: - return const CkLinearToSrgbGammaColorFilter(); - case ColorFilterType.srgbToLinearGamma: - return const CkSrgbToLinearGammaColorFilter(); - } + case ColorFilterType.mode: + if (colorFilter.color == null || colorFilter.blendMode == null) { + return null; + } + return CkBlendModeColorFilter(colorFilter.color!, colorFilter.blendMode!); + case ColorFilterType.matrix: + if (colorFilter.matrix == null) { + return null; + } + assert(colorFilter.matrix!.length == 20, 'Color Matrix must have 20 entries.'); + return CkMatrixColorFilter(colorFilter.matrix!); + case ColorFilterType.linearToSrgbGamma: + return const CkLinearToSrgbGammaColorFilter(); + case ColorFilterType.srgbToLinearGamma: + return const CkSrgbToLinearGammaColorFilter(); + } } diff --git a/lib/web_ui/lib/src/engine/canvaskit/display_canvas_factory.dart b/lib/web_ui/lib/src/engine/canvaskit/display_canvas_factory.dart index 93807b60bb596..71578adfd71b6 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/display_canvas_factory.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/display_canvas_factory.dart @@ -93,9 +93,10 @@ class DisplayCanvasFactory { void releaseCanvas(T canvas) { assert(canvas != baseCanvas, 'Attempting to release the base canvas'); assert( - _liveCanvases.contains(canvas), - 'Attempting to release a Canvas which ' - 'was not created by this factory'); + _liveCanvases.contains(canvas), + 'Attempting to release a Canvas which ' + 'was not created by this factory', + ); canvas.hostElement.remove(); _liveCanvases.remove(canvas); _cache.add(canvas); diff --git a/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart b/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart index 26af188fd047b..6aa70fb9d3674 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart @@ -37,8 +37,7 @@ class HtmlViewEmbedder { /// If we receive a request to composite a view, but the composition /// parameters haven't changed, we can avoid having to recompute the /// element stack that correctly composites the view into the scene. - final Map _currentCompositionParams = - {}; + final Map _currentCompositionParams = {}; /// The clip chain for a view Id. /// @@ -82,8 +81,7 @@ class HtmlViewEmbedder { /// Returns a list of canvases for the optimized rendering. These are used in /// the paint step. Iterable getOptimizedCanvases() { - return _context.optimizedCanvasRecorders! - .map((CkPictureRecorder r) => r.recordingCanvas!); + return _context.optimizedCanvasRecorders!.map((CkPictureRecorder r) => r.recordingCanvas!); } void prerollCompositeEmbeddedView(int viewId, EmbeddedViewParams params) { @@ -122,8 +120,7 @@ class HtmlViewEmbedder { void _compositeWithParams(int platformViewId, EmbeddedViewParams params) { // If we haven't seen this viewId yet, cache it for clips/transforms. - final ViewClipChain clipChain = - _viewClipChains.putIfAbsent(platformViewId, () { + final ViewClipChain clipChain = _viewClipChains.putIfAbsent(platformViewId, () { return ViewClipChain(view: createPlatformViewSlot(platformViewId)); }); @@ -147,10 +144,7 @@ class HtmlViewEmbedder { oldPlatformViewRoot, ); // Store the updated root element, and clip count - clipChain.updateClipChain( - root: newPlatformViewRoot, - clipCount: currentClippingCount, - ); + clipChain.updateClipChain(root: newPlatformViewRoot, clipCount: currentClippingCount); } // Apply mutators to the slot @@ -221,13 +215,13 @@ class HtmlViewEmbedder { } } - void _applyMutators( - EmbeddedViewParams params, DomElement embeddedView, int viewId) { + void _applyMutators(EmbeddedViewParams params, DomElement embeddedView, int viewId) { final MutatorsStack mutators = params.mutators; DomElement head = embeddedView; - Matrix4 headTransform = params.offset == ui.Offset.zero - ? Matrix4.identity() - : Matrix4.translationValues(params.offset.dx, params.offset.dy, 0); + Matrix4 headTransform = + params.offset == ui.Offset.zero + ? Matrix4.identity() + : Matrix4.translationValues(params.offset.dx, params.offset.dy, 0); double embeddedOpacity = 1.0; _resetAnchor(head); _cleanUpClipDefs(viewId); @@ -236,8 +230,7 @@ class HtmlViewEmbedder { switch (mutator.type) { case MutatorType.transform: headTransform = mutator.matrix!.multiplied(headTransform); - head.style.transform = - float64ListToCssTransform(headTransform.storage); + head.style.transform = float64ListToCssTransform(headTransform.storage); case MutatorType.clipRect: case MutatorType.clipRRect: case MutatorType.clipPath: @@ -253,20 +246,19 @@ class HtmlViewEmbedder { clipView.style.height = '100%'; if (mutator.rect != null) { final ui.Rect rect = mutator.rect!; - clipView.style.clip = 'rect(${rect.top}px, ${rect.right}px, ' + clipView.style.clip = + 'rect(${rect.top}px, ${rect.right}px, ' '${rect.bottom}px, ${rect.left}px)'; } else if (mutator.rrect != null) { final CkPath path = CkPath(); path.addRRect(mutator.rrect!); _ensureSvgPathDefs(); - final DomElement pathDefs = - _svgPathDefs!.querySelector('#sk_path_defs')!; + final DomElement pathDefs = _svgPathDefs!.querySelector('#sk_path_defs')!; _clipPathCount += 1; final String clipId = 'svgClip$_clipPathCount'; final SVGClipPathElement newClipPath = createSVGClipPathElement(); newClipPath.id = clipId; - newClipPath.append( - createSVGPathElement()..setAttribute('d', path.toSvgString())); + newClipPath.append(createSVGPathElement()..setAttribute('d', path.toSvgString())); pathDefs.append(newClipPath); // Store the id of the node instead of [newClipPath] directly. For @@ -277,14 +269,12 @@ class HtmlViewEmbedder { } else if (mutator.path != null) { final CkPath path = mutator.path! as CkPath; _ensureSvgPathDefs(); - final DomElement pathDefs = - _svgPathDefs!.querySelector('#sk_path_defs')!; + final DomElement pathDefs = _svgPathDefs!.querySelector('#sk_path_defs')!; _clipPathCount += 1; final String clipId = 'svgClip$_clipPathCount'; final SVGClipPathElement newClipPath = createSVGClipPathElement(); newClipPath.id = clipId; - newClipPath.append( - createSVGPathElement()..setAttribute('d', path.toSvgString())); + newClipPath.append(createSVGPathElement()..setAttribute('d', path.toSvgString())); pathDefs.append(newClipPath); // Store the id of the node instead of [newClipPath] directly. For // some reason, calling `newClipPath.remove()` doesn't remove it @@ -307,8 +297,7 @@ class HtmlViewEmbedder { // pixels, so scale down the head element to match the logical resolution. final double scale = EngineFlutterDisplay.instance.devicePixelRatio; final double inverseScale = 1 / scale; - final Matrix4 scaleMatrix = - Matrix4.diagonal3Values(inverseScale, inverseScale, 1); + final Matrix4 scaleMatrix = Matrix4.diagonal3Values(inverseScale, inverseScale, 1); headTransform = scaleMatrix.multiplied(headTransform); head.style.transform = float64ListToCssTransform(headTransform.storage); } @@ -344,13 +333,14 @@ class HtmlViewEmbedder { /// the final paint pass to paint the pictures into the optimized canvases. void optimizeRendering() { Rendering rendering = createOptimizedRendering( - _context.sceneElements, _currentCompositionParams); + _context.sceneElements, + _currentCompositionParams, + ); rendering = _modifyRenderingForMaxCanvases(rendering); _context.optimizedRendering = rendering; // Create new picture recorders for the optimized render canvases and record // which pictures go in which canvas. - final List optimizedCanvasRecorders = - []; + final List optimizedCanvasRecorders = []; final Map pictureToOptimizedCanvasMap = {}; for (final RenderingRenderCanvas renderCanvas in rendering.canvases) { @@ -358,8 +348,7 @@ class HtmlViewEmbedder { pictureRecorder.beginRecording(ui.Offset.zero & _frameSize.toSize()); optimizedCanvasRecorders.add(pictureRecorder); for (final PictureLayer picture in renderCanvas.pictures) { - pictureToOptimizedCanvasMap[picture] = - pictureRecorder; + pictureToOptimizedCanvasMap[picture] = pictureRecorder; } } _context.optimizedCanvasRecorders = optimizedCanvasRecorders; @@ -379,8 +368,7 @@ class HtmlViewEmbedder { if (rendering.equalsForRendering(_activeRendering)) { // Copy the display canvases to the new rendering. for (int i = 0; i < rendering.canvases.length; i++) { - rendering.canvases[i].displayCanvas = - _activeRendering.canvases[i].displayCanvas; + rendering.canvases[i].displayCanvas = _activeRendering.canvases[i].displayCanvas; _activeRendering.canvases[i].displayCanvas = null; } } @@ -389,16 +377,13 @@ class HtmlViewEmbedder { final List renderCanvases = rendering.canvases; int renderCanvasIndex = 0; for (final RenderingRenderCanvas renderCanvas in renderCanvases) { - final CkPicture renderPicture = _context - .optimizedCanvasRecorders![renderCanvasIndex++] - .endRecording(); - await rasterizer.rasterizeToCanvas( - renderCanvas.displayCanvas!, [renderPicture]); + final CkPicture renderPicture = + _context.optimizedCanvasRecorders![renderCanvasIndex++].endRecording(); + await rasterizer.rasterizeToCanvas(renderCanvas.displayCanvas!, [renderPicture]); renderPicture.dispose(); } - for (final CkPictureRecorder recorder - in _context.measuringPictureRecorders.values) { + for (final CkPictureRecorder recorder in _context.measuringPictureRecorders.values) { if (recorder.isRecording) { recorder.endRecording(); } @@ -410,22 +395,15 @@ class HtmlViewEmbedder { debugBoundsCanvas ??= rasterizer.displayFactory.getCanvas(); final CkPictureRecorder boundsRecorder = CkPictureRecorder(); final CkCanvas boundsCanvas = boundsRecorder.beginRecording( - ui.Rect.fromLTWH( - 0, - 0, - _frameSize.width.toDouble(), - _frameSize.height.toDouble(), - ), + ui.Rect.fromLTWH(0, 0, _frameSize.width.toDouble(), _frameSize.height.toDouble()), ); - final CkPaint platformViewBoundsPaint = CkPaint() - ..color = const ui.Color.fromARGB(100, 0, 255, 0); - final CkPaint pictureBoundsPaint = CkPaint() - ..color = const ui.Color.fromARGB(100, 0, 0, 255); + final CkPaint platformViewBoundsPaint = + CkPaint()..color = const ui.Color.fromARGB(100, 0, 255, 0); + final CkPaint pictureBoundsPaint = CkPaint()..color = const ui.Color.fromARGB(100, 0, 0, 255); for (final RenderingEntity entity in _activeRendering.entities) { if (entity is RenderingPlatformView) { if (entity.debugComputedBounds != null) { - boundsCanvas.drawRect( - entity.debugComputedBounds!, platformViewBoundsPaint); + boundsCanvas.drawRect(entity.debugComputedBounds!, platformViewBoundsPaint); } } else if (entity is RenderingRenderCanvas) { for (final PictureLayer picture in entity.pictures) { @@ -433,8 +411,9 @@ class HtmlViewEmbedder { } } } - await rasterizer.rasterizeToCanvas( - debugBoundsCanvas!, [boundsRecorder.endRecording()]); + await rasterizer.rasterizeToCanvas(debugBoundsCanvas!, [ + boundsRecorder.endRecording(), + ]); sceneHost.append(debugBoundsCanvas!.hostElement); } @@ -501,8 +480,7 @@ class HtmlViewEmbedder { } int numCanvasesToDelete = numCanvases - maximumCanvases; final List picturesForLastCanvas = []; - final List modifiedEntities = - List.from(rendering.entities); + final List modifiedEntities = List.from(rendering.entities); bool sawLastCanvas = false; for (int i = rendering.entities.length - 1; i >= 0; i--) { final RenderingEntity entity = modifiedEntities[i]; @@ -543,13 +521,10 @@ class HtmlViewEmbedder { // The rendering has not changed, so no DOM manipulation is needed. return; } - final List indexMap = - _getIndexMapFromPreviousRendering(_activeRendering, rendering); - final List existingIndexMap = - indexMap.where((int index) => index != -1).toList(); + final List indexMap = _getIndexMapFromPreviousRendering(_activeRendering, rendering); + final List existingIndexMap = indexMap.where((int index) => index != -1).toList(); - final List staticElements = - longestIncreasingSubsequence(existingIndexMap); + final List staticElements = longestIncreasingSubsequence(existingIndexMap); // Convert longest increasing subsequence from subsequence of indices of // `existingIndexMap` to a subsequence of indices in previous rendering. for (int i = 0; i < staticElements.length; i++) { @@ -567,9 +542,10 @@ class HtmlViewEmbedder { disposeView(entity.viewId); } else if (entity is RenderingRenderCanvas) { assert( - entity.displayCanvas != null, - 'RenderCanvas in previous rendering was ' - 'not assigned a DisplayCanvas'); + entity.displayCanvas != null, + 'RenderCanvas in previous rendering was ' + 'not assigned a DisplayCanvas', + ); rasterizer.releaseOverlay(entity.displayCanvas!); entity.displayCanvas = null; } @@ -578,19 +554,15 @@ class HtmlViewEmbedder { // Updates [renderCanvas] (located in [index] in the next rendering) to have // a display canvas, either taken from the associated render canvas in the // previous rendering, or newly created. - void updateRenderCanvasWithDisplay( - RenderingRenderCanvas renderCanvas, int index) { + void updateRenderCanvasWithDisplay(RenderingRenderCanvas renderCanvas, int index) { // Does [nextEntity] correspond with a render canvas in the previous // rendering? If so, then the render canvas in the previous rendering // had an associated display canvas. Use this display canvas for // [nextEntity]. if (indexMap[index] != -1) { - final RenderingEntity previousEntity = - _activeRendering.entities[indexMap[index]]; - assert(previousEntity is RenderingRenderCanvas && - previousEntity.displayCanvas != null); - renderCanvas.displayCanvas = - (previousEntity as RenderingRenderCanvas).displayCanvas; + final RenderingEntity previousEntity = _activeRendering.entities[indexMap[index]]; + assert(previousEntity is RenderingRenderCanvas && previousEntity.displayCanvas != null); + renderCanvas.displayCanvas = (previousEntity as RenderingRenderCanvas).displayCanvas; previousEntity.displayCanvas = null; } else { // There is no corresponding render canvas in the previous @@ -605,15 +577,13 @@ class HtmlViewEmbedder { int staticElementIndex = 0; int nextRenderingIndex = 0; while (staticElementIndex < staticElements.length) { - final int staticElementIndexInActiveRendering = - staticElements[staticElementIndex]; + final int staticElementIndexInActiveRendering = staticElements[staticElementIndex]; final DomElement staticDomElement = _getElement( - _activeRendering.entities[staticElementIndexInActiveRendering]); + _activeRendering.entities[staticElementIndexInActiveRendering], + ); // Go through next rendering elements until we reach the static element. - while ( - indexMap[nextRenderingIndex] != staticElementIndexInActiveRendering) { - final RenderingEntity nextEntity = - rendering.entities[nextRenderingIndex]; + while (indexMap[nextRenderingIndex] != staticElementIndexInActiveRendering) { + final RenderingEntity nextEntity = rendering.entities[nextRenderingIndex]; if (nextEntity is RenderingRenderCanvas) { updateRenderCanvasWithDisplay(nextEntity, nextRenderingIndex); } @@ -622,8 +592,9 @@ class HtmlViewEmbedder { } if (rendering.entities[nextRenderingIndex] is RenderingRenderCanvas) { updateRenderCanvasWithDisplay( - rendering.entities[nextRenderingIndex] as RenderingRenderCanvas, - nextRenderingIndex); + rendering.entities[nextRenderingIndex] as RenderingRenderCanvas, + nextRenderingIndex, + ); } // Also increment the next rendering index because this is the static // element. @@ -654,15 +625,15 @@ class HtmlViewEmbedder { /// Returns a [List] of ints mapping elements from the [next] rendering to /// elements of the [previous] rendering. If there is no matching element in /// the previous rendering, then the index map for that element is `-1`. - List _getIndexMapFromPreviousRendering( - Rendering previous, Rendering next) { - assert(!previous.equalsForRendering(next), - 'Should not be in this method if the Renderings are equal'); + List _getIndexMapFromPreviousRendering(Rendering previous, Rendering next) { + assert( + !previous.equalsForRendering(next), + 'Should not be in this method if the Renderings are equal', + ); final List result = []; int index = 0; - final int maxUnchangedLength = - math.min(previous.entities.length, next.entities.length); + final int maxUnchangedLength = math.min(previous.entities.length, next.entities.length); // A canvas in the previous rendering can only be used once in the next // rendering. So if it is matched with one in the next rendering, mark it @@ -681,11 +652,8 @@ class HtmlViewEmbedder { while (index < next.entities.length) { bool foundForIndex = false; - for (int oldIndex = 0; - oldIndex < previous.entities.length; - oldIndex += 1) { - if (previous.entities[oldIndex] - .equalsForRendering(next.entities[index]) && + for (int oldIndex = 0; oldIndex < previous.entities.length; oldIndex += 1) { + if (previous.entities[oldIndex].equalsForRendering(next.entities[index]) && !alreadyClaimedCanvases.contains(oldIndex)) { result.add(oldIndex); if (previous.entities[oldIndex] is RenderingRenderCanvas) { @@ -709,9 +677,7 @@ class HtmlViewEmbedder { void debugCleanupSvgClipPaths() { final DomElement? parent = _svgPathDefs?.children.single; if (parent != null) { - for (DomNode? child = parent.lastChild; - child != null; - child = parent.lastChild) { + for (DomNode? child = parent.lastChild; child != null; child = parent.lastChild) { parent.removeChild(child); } } @@ -750,9 +716,7 @@ class HtmlViewEmbedder { /// * The slot view in the stack (the actual contents of the platform view). /// * The number of clipping elements used last time the view was composited. class ViewClipChain { - ViewClipChain({required DomElement view}) - : _root = view, - _slot = view; + ViewClipChain({required DomElement view}) : _root = view, _slot = view; DomElement _root; final DomElement _slot; @@ -771,7 +735,7 @@ class ViewClipChain { /// The parameters passed to the view embedder. class EmbeddedViewParams { EmbeddedViewParams(this.offset, this.size, MutatorsStack mutators) - : mutators = MutatorsStack._copy(mutators); + : mutators = MutatorsStack._copy(mutators); final ui.Offset offset; final ui.Size size; @@ -792,35 +756,19 @@ class EmbeddedViewParams { int get hashCode => Object.hash(offset, size, mutators); } -enum MutatorType { - clipRect, - clipRRect, - clipPath, - transform, - opacity, -} +enum MutatorType { clipRect, clipRRect, clipPath, transform, opacity } /// Stores mutation information like clipping or transform. class Mutator { - const Mutator.clipRect(ui.Rect rect) - : this._(MutatorType.clipRect, rect, null, null, null, null); + const Mutator.clipRect(ui.Rect rect) : this._(MutatorType.clipRect, rect, null, null, null, null); const Mutator.clipRRect(ui.RRect rrect) - : this._(MutatorType.clipRRect, null, rrect, null, null, null); - const Mutator.clipPath(ui.Path path) - : this._(MutatorType.clipPath, null, null, path, null, null); + : this._(MutatorType.clipRRect, null, rrect, null, null, null); + const Mutator.clipPath(ui.Path path) : this._(MutatorType.clipPath, null, null, path, null, null); const Mutator.transform(Matrix4 matrix) - : this._(MutatorType.transform, null, null, null, matrix, null); - const Mutator.opacity(int alpha) - : this._(MutatorType.opacity, null, null, null, null, alpha); - - const Mutator._( - this.type, - this.rect, - this.rrect, - this.path, - this.matrix, - this.alpha, - ); + : this._(MutatorType.transform, null, null, null, matrix, null); + const Mutator.opacity(int alpha) : this._(MutatorType.opacity, null, null, null, null, alpha); + + const Mutator._(this.type, this.rect, this.rrect, this.path, this.matrix, this.alpha); final MutatorType type; final ui.Rect? rect; @@ -830,9 +778,7 @@ class Mutator { final int? alpha; bool get isClipType => - type == MutatorType.clipRect || - type == MutatorType.clipRRect || - type == MutatorType.clipPath; + type == MutatorType.clipRect || type == MutatorType.clipRRect || type == MutatorType.clipPath; double get alphaFloat => alpha! / 255.0; @@ -872,8 +818,7 @@ class Mutator { class MutatorsStack extends Iterable { MutatorsStack() : _mutators = []; - MutatorsStack._copy(MutatorsStack original) - : _mutators = List.from(original._mutators); + MutatorsStack._copy(MutatorsStack original) : _mutators = List.from(original._mutators); final List _mutators; @@ -906,8 +851,7 @@ class MutatorsStack extends Iterable { if (identical(other, this)) { return true; } - return other is MutatorsStack && - listEquals(other._mutators, _mutators); + return other is MutatorsStack && listEquals(other._mutators, _mutators); } @override diff --git a/lib/web_ui/lib/src/engine/canvaskit/fonts.dart b/lib/web_ui/lib/src/engine/canvaskit/fonts.dart index 66588299fd9fe..aad46a95cd08b 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/fonts.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/fonts.dart @@ -21,8 +21,7 @@ class SkiaFontCollection implements FlutterFontCollection { final Set _downloadedFontFamilies = {}; @override - late FontFallbackManager fontFallbackManager = - FontFallbackManager(SkiaFallbackRegistry(this)); + late FontFallbackManager fontFallbackManager = FontFallbackManager(SkiaFallbackRegistry(this)); /// Fonts that started the download process, but are not yet registered. /// @@ -62,16 +61,12 @@ class SkiaFontCollection implements FlutterFontCollection { for (final RegisteredFont font in _registeredFonts) { _fontProvider!.registerFont(font.bytes, font.family); - familyToFontMap - .putIfAbsent(font.family, () => []) - .add(SkFont(font.typeface)); + familyToFontMap.putIfAbsent(font.family, () => []).add(SkFont(font.typeface)); } for (final RegisteredFont font in registeredFallbackFonts) { _fontProvider!.registerFont(font.bytes, font.family); - familyToFontMap - .putIfAbsent(font.family, () => []) - .add(SkFont(font.typeface)); + familyToFontMap.putIfAbsent(font.family, () => []).add(SkFont(font.typeface)); } } @@ -88,8 +83,7 @@ class SkiaFontCollection implements FlutterFontCollection { // Make sure CanvasKit is actually loaded await renderer.initialize(); - final SkTypeface? typeface = - canvasKit.Typeface.MakeFreeTypeFaceFromData(list.buffer); + final SkTypeface? typeface = canvasKit.Typeface.MakeFreeTypeFaceFromData(list.buffer); if (typeface != null) { _registeredFonts.add(RegisteredFont(list, fontFamily, typeface)); _registerWithFontProvider(); @@ -139,8 +133,7 @@ class SkiaFontCollection implements FlutterFontCollection { final List loadedFonts = []; for (final (String assetName, UnregisteredFont unregisteredFont) in downloadedFonts) { final Uint8List bytes = unregisteredFont.bytes.asUint8List(); - final SkTypeface? typeface = - canvasKit.Typeface.MakeFreeTypeFaceFromData(bytes.buffer); + final SkTypeface? typeface = canvasKit.Typeface.MakeFreeTypeFaceFromData(bytes.buffer); if (typeface != null) { loadedFonts.add(assetName); _registeredFonts.add(RegisteredFont(bytes, unregisteredFont.family, typeface)); @@ -157,8 +150,7 @@ class SkiaFontCollection implements FlutterFontCollection { void registerDownloadedFonts() { RegisteredFont? makeRegisterFont(ByteBuffer buffer, String url, String family) { final Uint8List bytes = buffer.asUint8List(); - final SkTypeface? typeface = - canvasKit.Typeface.MakeFreeTypeFaceFromData(bytes.buffer); + final SkTypeface? typeface = canvasKit.Typeface.MakeFreeTypeFaceFromData(bytes.buffer); if (typeface != null) { return RegisteredFont(bytes, family, typeface); } else { @@ -172,7 +164,7 @@ class SkiaFontCollection implements FlutterFontCollection { final RegisteredFont? registeredFont = makeRegisterFont( unregisteredFont.bytes, unregisteredFont.url, - unregisteredFont.family + unregisteredFont.family, ); if (registeredFont != null) { _registeredFonts.add(registeredFont); @@ -183,11 +175,7 @@ class SkiaFontCollection implements FlutterFontCollection { _registerWithFontProvider(); } - Future _downloadFont( - String assetName, - String url, - String fontFamily - ) async { + Future _downloadFont(String assetName, String url, String fontFamily) async { final ByteBuffer fontData; // Try to get the font leniently. Do not crash the app when failing to @@ -209,10 +197,8 @@ class SkiaFontCollection implements FlutterFontCollection { return FontDownloadResult.fromFont(assetName, UnregisteredFont(fontData, url, fontFamily)); } - String? _readActualFamilyName(Uint8List bytes) { - final SkFontMgr tmpFontMgr = - canvasKit.FontMgr.FromData([bytes])!; + final SkFontMgr tmpFontMgr = canvasKit.FontMgr.FromData([bytes])!; final String? actualFamily = tmpFontMgr.getFamilyName(0); tmpFontMgr.delete(); return actualFamily; @@ -282,8 +268,7 @@ class SkiaFallbackRegistry implements FallbackFontRegistry { fonts.addAll(typefacesForFamily); } } - final List codePointsSupported = - List.filled(codeUnits.length, false); + final List codePointsSupported = List.filled(codeUnits.length, false); final String testString = String.fromCharCodes(codeUnits); for (final SkFont font in fonts) { final Uint16List glyphs = font.getGlyphIDs(testString); @@ -305,14 +290,13 @@ class SkiaFallbackRegistry implements FallbackFontRegistry { @override Future loadFallbackFont(String familyName, String url) async { final ByteBuffer buffer = await httpFetchByteBuffer(url); - final SkTypeface? typeface = - canvasKit.Typeface.MakeFreeTypeFaceFromData(buffer); + final SkTypeface? typeface = canvasKit.Typeface.MakeFreeTypeFaceFromData(buffer); if (typeface == null) { printWarning('Failed to parse fallback font $familyName as a font.'); return; } fontCollection.registeredFallbackFonts.add( - RegisteredFont(buffer.asUint8List(), familyName, typeface) + RegisteredFont(buffer.asUint8List(), familyName, typeface), ); } diff --git a/lib/web_ui/lib/src/engine/canvaskit/image.dart b/lib/web_ui/lib/src/engine/canvaskit/image.dart index 306ff752c2563..57f51c57b6ec6 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/image.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/image.dart @@ -13,8 +13,12 @@ import 'package:ui/ui.dart' as ui; import 'package:ui/ui_web/src/ui_web.dart' as ui_web; /// Instantiates a [ui.Codec] backed by an `SkAnimatedImage` from Skia. -Future skiaInstantiateImageCodec(Uint8List list, - [int? targetWidth, int? targetHeight, bool allowUpscaling = true]) async { +Future skiaInstantiateImageCodec( + Uint8List list, [ + int? targetWidth, + int? targetHeight, + bool allowUpscaling = true, +]) async { ui.Codec codec; // ImageDecoder does not detect image type automatically. It requires us to // tell it what the image type is. @@ -28,8 +32,12 @@ Future skiaInstantiateImageCodec(Uint8List list, ); } else { if (imageType.isAnimated) { - codec = CkAnimatedImage.decodeFromBytes(list, 'encoded image bytes', - targetWidth: targetWidth, targetHeight: targetHeight); + codec = CkAnimatedImage.decodeFromBytes( + list, + 'encoded image bytes', + targetWidth: targetWidth, + targetHeight: targetHeight, + ); } else { final DomBlob blob = createDomBlob([list.buffer]); codec = await decodeBlobToCkImage(blob); @@ -46,12 +54,7 @@ Future skiaInstantiateImageCodec(Uint8List list, /// A resizing codec which uses an HTML element to scale the image if /// it is backed by an HTML Image element. class CkResizingCodec extends ResizingCodec { - CkResizingCodec( - super.delegate, { - super.targetWidth, - super.targetHeight, - super.allowUpscaling, - }); + CkResizingCodec(super.delegate, {super.targetWidth, super.targetHeight, super.allowUpscaling}); @override ui.Image scaleImage( @@ -87,23 +90,18 @@ class CkResizingCodec extends ResizingCodec { assert(image.imageSource != null); final int width = image.width; final int height = image.height; - final BitmapSize? scaledSize = - scaledImageSize(width, height, targetWidth, targetHeight); + final BitmapSize? scaledSize = scaledImageSize(width, height, targetWidth, targetHeight); if (scaledSize == null) { return image; } - if (!allowUpscaling && - (scaledSize.width > width || scaledSize.height > height)) { + if (!allowUpscaling && (scaledSize.width > width || scaledSize.height > height)) { return image; } final int scaledWidth = scaledSize.width; final int scaledHeight = scaledSize.height; - final DomOffscreenCanvas offscreenCanvas = createDomOffscreenCanvas( - scaledWidth, - scaledHeight, - ); + final DomOffscreenCanvas offscreenCanvas = createDomOffscreenCanvas(scaledWidth, scaledHeight); final DomCanvasRenderingContext2D ctx = offscreenCanvas.getContext('2d')! as DomCanvasRenderingContext2D; ctx.drawImage( @@ -118,8 +116,7 @@ class CkResizingCodec extends ResizingCodec { scaledHeight, ); final DomImageBitmap bitmap = offscreenCanvas.transferToImageBitmap(); - final SkImage? skImage = - canvasKit.MakeLazyImageFromImageBitmap(bitmap, true); + final SkImage? skImage = canvasKit.MakeLazyImageFromImageBitmap(bitmap, true); // Resize the canvas to 0x0 to cause the browser to eagerly reclaim its // memory. @@ -152,9 +149,7 @@ ui.Image createCkImageFromImageElement( ), ); if (skImage == null) { - throw ImageCodecException( - 'Failed to create image from Image.decode', - ); + throw ImageCodecException('Failed to create image from Image.decode'); } return CkImage(skImage, imageSource: ImageElementImageSource(image)); @@ -165,8 +160,10 @@ class CkImageElementCodec extends HtmlImageElementCodec { @override ui.Image createImageFromHTMLImageElement( - DomHTMLImageElement image, int naturalWidth, int naturalHeight) => - createCkImageFromImageElement(image, naturalWidth, naturalHeight); + DomHTMLImageElement image, + int naturalWidth, + int naturalHeight, + ) => createCkImageFromImageElement(image, naturalWidth, naturalHeight); } class CkImageBlobCodec extends HtmlBlobCodec { @@ -174,8 +171,10 @@ class CkImageBlobCodec extends HtmlBlobCodec { @override ui.Image createImageFromHTMLImageElement( - DomHTMLImageElement image, int naturalWidth, int naturalHeight) => - createCkImageFromImageElement(image, naturalWidth, naturalHeight); + DomHTMLImageElement image, + int naturalWidth, + int naturalHeight, + ) => createCkImageFromImageElement(image, naturalWidth, naturalHeight); } /// Creates and decodes an image using HtmlImageElement. @@ -216,9 +215,10 @@ void skiaDecodeImageFromPixels( SkImageInfo( width: width.toDouble(), height: height.toDouble(), - colorType: format == ui.PixelFormat.rgba8888 - ? canvasKit.ColorType.RGBA_8888 - : canvasKit.ColorType.BGRA_8888, + colorType: + format == ui.PixelFormat.rgba8888 + ? canvasKit.ColorType.RGBA_8888 + : canvasKit.ColorType.BGRA_8888, alphaType: canvasKit.AlphaType.Premul, colorSpace: SkColorSpaceSRGB, ), @@ -232,8 +232,7 @@ void skiaDecodeImageFromPixels( } if (targetWidth != null || targetHeight != null) { - if (validUpscale( - allowUpscaling, targetWidth, targetHeight, width, height)) { + if (validUpscale(allowUpscaling, targetWidth, targetHeight, width, height)) { return callback(scaleImage(skImage, targetWidth, targetHeight)); } } @@ -243,8 +242,13 @@ void skiaDecodeImageFromPixels( // An invalid upscale happens when allowUpscaling is false AND either the given // targetWidth is larger than the originalWidth OR the targetHeight is larger than originalHeight. -bool validUpscale(bool allowUpscaling, int? targetWidth, int? targetHeight, - int originalWidth, int originalHeight) { +bool validUpscale( + bool allowUpscaling, + int? targetWidth, + int? targetHeight, + int originalWidth, + int originalHeight, +) { if (allowUpscaling) { return true; } @@ -325,8 +329,13 @@ const String _kNetworkImageMessage = 'Failed to load network image.'; /// Instantiates a [ui.Codec] backed by an `SkAnimatedImage` from Skia after /// requesting from URI. Future skiaInstantiateWebImageCodec( - String url, ui_web.ImageCodecChunkCallback? chunkCallback) async { - final CkImageElementCodec imageElementCodec = CkImageElementCodec(url, chunkCallback: chunkCallback); + String url, + ui_web.ImageCodecChunkCallback? chunkCallback, +) async { + final CkImageElementCodec imageElementCodec = CkImageElementCodec( + url, + chunkCallback: chunkCallback, + ); try { await imageElementCodec.decode(); return imageElementCodec; @@ -336,7 +345,10 @@ Future skiaInstantiateWebImageCodec( final ImageType imageType = tryDetectImageType(list, url); if (browserSupportsImageDecoder) { return CkBrowserImageDecoder.create( - data: list, contentType: imageType.mimeType, debugSource: url); + data: list, + contentType: imageType.mimeType, + debugSource: url, + ); } else { final DomBlob blob = createDomBlob([list.buffer]); final CkImageBlobCodec codec = CkImageBlobCodec(blob, chunkCallback: chunkCallback); @@ -353,8 +365,7 @@ Future skiaInstantiateWebImageCodec( } /// Sends a request to fetch image data. -Future fetchImage( - String url, ui_web.ImageCodecChunkCallback? chunkCallback) async { +Future fetchImage(String url, ui_web.ImageCodecChunkCallback? chunkCallback) async { try { final HttpFetchResponse response = await httpFetch(url); final int? contentLength = response.contentLength; @@ -385,8 +396,11 @@ Future fetchImage( /// Reads the [payload] in chunks using the browser's Streams API /// /// See: https://developer.mozilla.org/en-US/docs/Web/API/Streams_API -Future readChunked(HttpFetchPayload payload, int contentLength, - ui_web.ImageCodecChunkCallback chunkCallback) async { +Future readChunked( + HttpFetchPayload payload, + int contentLength, + ui_web.ImageCodecChunkCallback chunkCallback, +) async { final JSUint8Array result = createUint8ArrayFromLength(contentLength); int position = 0; int cumulativeBytesLoaded = 0; @@ -450,10 +464,7 @@ class CkImage implements ui.Image, StackTraceDebugger { @override void dispose() { - assert( - !_disposed, - 'Cannot dispose an image that has already been disposed.', - ); + assert(!_disposed, 'Cannot dispose an image that has already been disposed.'); ui.Image.onDispose?.call(this); _disposed = true; box.unref(this); @@ -474,17 +485,13 @@ class CkImage implements ui.Image, StackTraceDebugger { return result!; } - throw StateError( - 'Image.debugDisposed is only available when asserts are enabled.'); + throw StateError('Image.debugDisposed is only available when asserts are enabled.'); } @override CkImage clone() { assert(_debugCheckIsNotDisposed()); - return CkImage.cloneOf( - box, - imageSource: imageSource, - ); + return CkImage.cloneOf(box, imageSource: imageSource); } @override @@ -494,8 +501,7 @@ class CkImage implements ui.Image, StackTraceDebugger { } @override - List? debugGetOpenHandleStackTraces() => - box.debugGetStackTraces(); + List? debugGetOpenHandleStackTraces() => box.debugGetStackTraces(); @override int get width { @@ -510,9 +516,7 @@ class CkImage implements ui.Image, StackTraceDebugger { } @override - Future toByteData({ - ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba, - }) { + Future toByteData({ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba}) { assert(_debugCheckIsNotDisposed()); switch (imageSource) { case ImageElementImageSource(): @@ -525,8 +529,7 @@ class CkImage implements ui.Image, StackTraceDebugger { imageElement.naturalHeight.toInt(), ); case ImageBitmapImageSource(): - final DomImageBitmap imageBitmap = - (imageSource! as ImageBitmapImageSource).imageBitmap; + final DomImageBitmap imageBitmap = (imageSource! as ImageBitmapImageSource).imageBitmap; return readPixelsFromDomImageSource( imageBitmap, format, @@ -534,8 +537,7 @@ class CkImage implements ui.Image, StackTraceDebugger { imageBitmap.height.toDartInt, ); case VideoFrameImageSource(): - final VideoFrame videoFrame = - (imageSource! as VideoFrameImageSource).videoFrame; + final VideoFrame videoFrame = (imageSource! as VideoFrameImageSource).videoFrame; if (videoFrame.format != 'I420' && videoFrame.format != 'I444' && videoFrame.format != 'I422') { @@ -556,9 +558,10 @@ class CkImage implements ui.Image, StackTraceDebugger { ui.ColorSpace get colorSpace => ui.ColorSpace.sRGB; ByteData? _readPixelsFromSkImage(ui.ImageByteFormat format) { - final SkAlphaType alphaType = format == ui.ImageByteFormat.rawStraightRgba - ? canvasKit.AlphaType.Unpremul - : canvasKit.AlphaType.Premul; + final SkAlphaType alphaType = + format == ui.ImageByteFormat.rawStraightRgba + ? canvasKit.AlphaType.Unpremul + : canvasKit.AlphaType.Premul; final ByteData? data = _encodeImage( skImage: skImage, format: format, @@ -571,8 +574,7 @@ class CkImage implements ui.Image, StackTraceDebugger { ByteData? _readPixelsFromImageViaSurface(ui.ImageByteFormat format) { final Surface surface = CanvasKitRenderer.instance.pictureToImageSurface; - final CkSurface ckSurface = - surface.createOrUpdateSurface(BitmapSize(width, height)); + final CkSurface ckSurface = surface.createOrUpdateSurface(BitmapSize(width, height)); final CkCanvas ckCanvas = ckSurface.getCanvas(); ckCanvas.clear(const ui.Color(0x00000000)); ckCanvas.drawImage(this, ui.Offset.zero, CkPaint()); @@ -600,8 +602,7 @@ class CkImage implements ui.Image, StackTraceDebugger { }) { Uint8List? bytes; - if (format == ui.ImageByteFormat.rawRgba || - format == ui.ImageByteFormat.rawStraightRgba) { + if (format == ui.ImageByteFormat.rawRgba || format == ui.ImageByteFormat.rawStraightRgba) { final SkImageInfo imageInfo = SkImageInfo( alphaType: alphaType, colorType: colorType, @@ -633,15 +634,15 @@ ImageType tryDetectImageType(Uint8List data, String debugSource) { if (imageType == null) { final String fileHeader; if (data.isNotEmpty) { - fileHeader = - '[${bytesToHexString(data.sublist(0, math.min(10, data.length)))}]'; + fileHeader = '[${bytesToHexString(data.sublist(0, math.min(10, data.length)))}]'; } else { fileHeader = 'empty'; } throw ImageCodecException( - 'Failed to detect image file format using the file header.\n' - 'File header was $fileHeader.\n' - 'Image source: $debugSource'); + 'Failed to detect image file format using the file header.\n' + 'File header was $fileHeader.\n' + 'Image source: $debugSource', + ); } return imageType; } diff --git a/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart b/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart index e97327a43ebfd..260f3f3b7dc69 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart @@ -29,7 +29,8 @@ abstract class CkManagedSkImageFilterConvertible implements ui.ImageFilter { /// /// [SkImageFilter] objects are not kept around so that their memory is /// reclaimed immediately, rather than waiting for the GC cycle. - void withSkImageFilter(SkImageFilterBorrow borrow, { + void withSkImageFilter( + SkImageFilterBorrow borrow, { ui.TileMode defaultBlurTileMode = ui.TileMode.clamp, }); @@ -42,23 +43,22 @@ abstract class CkManagedSkImageFilterConvertible implements ui.ImageFilter { /// /// Currently only supports `blur`, `matrix`, and ColorFilters. abstract class CkImageFilter implements CkManagedSkImageFilterConvertible { - factory CkImageFilter.blur( - {required double sigmaX, - required double sigmaY, - required ui.TileMode? tileMode}) = _CkBlurImageFilter; - factory CkImageFilter.color({required CkColorFilter colorFilter}) = - CkColorFilterImageFilter; - factory CkImageFilter.matrix( - {required Float64List matrix, - required ui.FilterQuality filterQuality}) = _CkMatrixImageFilter; - factory CkImageFilter.dilate( - {required double radiusX, - required double radiusY}) = _CkDilateImageFilter; - factory CkImageFilter.erode( - {required double radiusX, required double radiusY}) = _CkErodeImageFilter; - factory CkImageFilter.compose( - {required CkImageFilter outer, - required CkImageFilter inner}) = _CkComposeImageFilter; + factory CkImageFilter.blur({ + required double sigmaX, + required double sigmaY, + required ui.TileMode? tileMode, + }) = _CkBlurImageFilter; + factory CkImageFilter.color({required CkColorFilter colorFilter}) = CkColorFilterImageFilter; + factory CkImageFilter.matrix({ + required Float64List matrix, + required ui.FilterQuality filterQuality, + }) = _CkMatrixImageFilter; + factory CkImageFilter.dilate({required double radiusX, required double radiusY}) = + _CkDilateImageFilter; + factory CkImageFilter.erode({required double radiusX, required double radiusY}) = + _CkErodeImageFilter; + factory CkImageFilter.compose({required CkImageFilter outer, required CkImageFilter inner}) = + _CkComposeImageFilter; CkImageFilter._(); @@ -79,7 +79,8 @@ class CkColorFilterImageFilter extends CkImageFilter { final CkColorFilter colorFilter; @override - void withSkImageFilter(SkImageFilterBorrow borrow, { + void withSkImageFilter( + SkImageFilterBorrow borrow, { ui.TileMode defaultBlurTileMode = ui.TileMode.clamp, }) { final skImageFilter = colorFilter.initRawImageFilter(); @@ -95,8 +96,7 @@ class CkColorFilterImageFilter extends CkImageFilter { if (runtimeType != other.runtimeType) { return false; } - return other is CkColorFilterImageFilter && - other.colorFilter == colorFilter; + return other is CkColorFilterImageFilter && other.colorFilter == colorFilter; } @override @@ -104,9 +104,8 @@ class CkColorFilterImageFilter extends CkImageFilter { } class _CkBlurImageFilter extends CkImageFilter { - _CkBlurImageFilter( - {required this.sigmaX, required this.sigmaY, required this.tileMode}) - : super._(); + _CkBlurImageFilter({required this.sigmaX, required this.sigmaY, required this.tileMode}) + : super._(); final double sigmaX; final double sigmaY; @@ -116,7 +115,8 @@ class _CkBlurImageFilter extends CkImageFilter { ui.TileMode? get backdropTileMode => tileMode; @override - void withSkImageFilter(SkImageFilterBorrow borrow, { + void withSkImageFilter( + SkImageFilterBorrow borrow, { ui.TileMode defaultBlurTileMode = ui.TileMode.clamp, }) { /// Return the identity matrix when both sigmaX and sigmaY are 0. Replicates @@ -124,9 +124,10 @@ class _CkBlurImageFilter extends CkImageFilter { final SkImageFilter skImageFilter; if (sigmaX == 0 && sigmaY == 0) { skImageFilter = canvasKit.ImageFilter.MakeMatrixTransform( - toSkMatrixFromFloat32(Matrix4.identity().storage), - toSkFilterOptions(ui.FilterQuality.none), - null); + toSkMatrixFromFloat32(Matrix4.identity().storage), + toSkFilterOptions(ui.FilterQuality.none), + null, + ); } else { skImageFilter = canvasKit.ImageFilter.MakeBlur( sigmaX, @@ -161,22 +162,21 @@ class _CkBlurImageFilter extends CkImageFilter { } class _CkMatrixImageFilter extends CkImageFilter { - _CkMatrixImageFilter( - {required Float64List matrix, required this.filterQuality}) - : matrix = Float64List.fromList(matrix), - _transform = Matrix4.fromFloat32List(toMatrix32(matrix)), - super._(); + _CkMatrixImageFilter({required Float64List matrix, required this.filterQuality}) + : matrix = Float64List.fromList(matrix), + _transform = Matrix4.fromFloat32List(toMatrix32(matrix)), + super._(); final Float64List matrix; final ui.FilterQuality filterQuality; final Matrix4 _transform; @override - void withSkImageFilter(SkImageFilterBorrow borrow, { + void withSkImageFilter( + SkImageFilterBorrow borrow, { ui.TileMode defaultBlurTileMode = ui.TileMode.clamp, }) { - final skImageFilter = - canvasKit.ImageFilter.MakeMatrixTransform( + final skImageFilter = canvasKit.ImageFilter.MakeMatrixTransform( toSkMatrixFromFloat64(matrix), toSkFilterOptions(filterQuality), null, @@ -206,21 +206,17 @@ class _CkMatrixImageFilter extends CkImageFilter { } class _CkDilateImageFilter extends CkImageFilter { - _CkDilateImageFilter({required this.radiusX, required this.radiusY}) - : super._(); + _CkDilateImageFilter({required this.radiusX, required this.radiusY}) : super._(); final double radiusX; final double radiusY; @override - void withSkImageFilter(SkImageFilterBorrow borrow, { + void withSkImageFilter( + SkImageFilterBorrow borrow, { ui.TileMode defaultBlurTileMode = ui.TileMode.clamp, }) { - final skImageFilter = canvasKit.ImageFilter.MakeDilate( - radiusX, - radiusY, - null, - ); + final skImageFilter = canvasKit.ImageFilter.MakeDilate(radiusX, radiusY, null); borrow(skImageFilter); skImageFilter.delete(); } @@ -230,9 +226,7 @@ class _CkDilateImageFilter extends CkImageFilter { if (runtimeType != other.runtimeType) { return false; } - return other is _CkDilateImageFilter && - other.radiusX == radiusX && - other.radiusY == radiusY; + return other is _CkDilateImageFilter && other.radiusX == radiusX && other.radiusY == radiusY; } @override @@ -245,21 +239,17 @@ class _CkDilateImageFilter extends CkImageFilter { } class _CkErodeImageFilter extends CkImageFilter { - _CkErodeImageFilter({required this.radiusX, required this.radiusY}) - : super._(); + _CkErodeImageFilter({required this.radiusX, required this.radiusY}) : super._(); final double radiusX; final double radiusY; @override - void withSkImageFilter(SkImageFilterBorrow borrow, { + void withSkImageFilter( + SkImageFilterBorrow borrow, { ui.TileMode defaultBlurTileMode = ui.TileMode.clamp, }) { - final skImageFilter = canvasKit.ImageFilter.MakeErode( - radiusX, - radiusY, - null, - ); + final skImageFilter = canvasKit.ImageFilter.MakeErode(radiusX, radiusY, null); borrow(skImageFilter); skImageFilter.delete(); } @@ -269,9 +259,7 @@ class _CkErodeImageFilter extends CkImageFilter { if (runtimeType != other.runtimeType) { return false; } - return other is _CkErodeImageFilter && - other.radiusX == radiusX && - other.radiusY == radiusY; + return other is _CkErodeImageFilter && other.radiusX == radiusX && other.radiusY == radiusY; } @override @@ -284,22 +272,19 @@ class _CkErodeImageFilter extends CkImageFilter { } class _CkComposeImageFilter extends CkImageFilter { - _CkComposeImageFilter({required this.outer, required this.inner}) - : super._(); + _CkComposeImageFilter({required this.outer, required this.inner}) : super._(); final CkImageFilter outer; final CkImageFilter inner; @override - void withSkImageFilter(SkImageFilterBorrow borrow, { + void withSkImageFilter( + SkImageFilterBorrow borrow, { ui.TileMode defaultBlurTileMode = ui.TileMode.clamp, }) { outer.withSkImageFilter((skOuter) { inner.withSkImageFilter((skInner) { - final skImageFilter = canvasKit.ImageFilter.MakeCompose( - skOuter, - skInner, - ); + final skImageFilter = canvasKit.ImageFilter.MakeCompose(skOuter, skInner); borrow(skImageFilter); skImageFilter.delete(); }, defaultBlurTileMode: defaultBlurTileMode); @@ -311,9 +296,7 @@ class _CkComposeImageFilter extends CkImageFilter { if (runtimeType != other.runtimeType) { return false; } - return other is _CkComposeImageFilter && - other.outer == outer && - other.inner == inner; + return other is _CkComposeImageFilter && other.outer == outer && other.inner == inner; } @override diff --git a/lib/web_ui/lib/src/engine/canvaskit/image_wasm_codecs.dart b/lib/web_ui/lib/src/engine/canvaskit/image_wasm_codecs.dart index 337eac8679fdf..26f79cfe820ef 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/image_wasm_codecs.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/image_wasm_codecs.dart @@ -35,8 +35,7 @@ class CkAnimatedImage implements ui.Codec { final int? targetHeight; SkAnimatedImage createSkAnimatedImage() { - SkAnimatedImage? animatedImage = - canvasKit.MakeAnimatedImageFromEncoded(_bytes); + SkAnimatedImage? animatedImage = canvasKit.MakeAnimatedImageFromEncoded(_bytes); if (animatedImage == null) { throw ImageCodecException( 'Failed to decode image data.\n' @@ -64,7 +63,11 @@ class CkAnimatedImage implements ui.Codec { return animatedImage; } - SkAnimatedImage? _resizeAnimatedImage(SkAnimatedImage animatedImage, int? targetWidth, int? targetHeight) { + SkAnimatedImage? _resizeAnimatedImage( + SkAnimatedImage animatedImage, + int? targetWidth, + int? targetHeight, + ) { final SkImage image = animatedImage.makeImageAtCurrentFrame(); final CkImage ckImage = scaleImage(image, targetWidth, targetHeight); final Uint8List? resizedBytes = ckImage.skImage.encodeToBytes(); @@ -73,7 +76,9 @@ class CkAnimatedImage implements ui.Codec { throw ImageCodecException('Failed to re-size image'); } - final SkAnimatedImage? resizedAnimatedImage = canvasKit.MakeAnimatedImageFromEncoded(resizedBytes); + final SkAnimatedImage? resizedAnimatedImage = canvasKit.MakeAnimatedImageFromEncoded( + resizedBytes, + ); return resizedAnimatedImage; } @@ -87,10 +92,7 @@ class CkAnimatedImage implements ui.Codec { @override void dispose() { - assert( - !_disposed, - 'Cannot dispose a codec that has already been disposed.', - ); + assert(!_disposed, 'Cannot dispose a codec that has already been disposed.'); _disposed = true; _ref.dispose(); } diff --git a/lib/web_ui/lib/src/engine/canvaskit/image_web_codecs.dart b/lib/web_ui/lib/src/engine/canvaskit/image_web_codecs.dart index f9c583aa9b3b3..dadb7958411c5 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/image_web_codecs.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/image_web_codecs.dart @@ -62,8 +62,7 @@ class CkBrowserImageDecoder extends BrowserImageDecoder { } } -Future readPixelsFromVideoFrame( - VideoFrame videoFrame, ui.ImageByteFormat format) async { +Future readPixelsFromVideoFrame(VideoFrame videoFrame, ui.ImageByteFormat format) async { if (format == ui.ImageByteFormat.png) { final Uint8List png = await encodeDomImageSourceAsPng( videoFrame, @@ -106,16 +105,11 @@ Future readPixelsFromDomImageSource( int height, ) async { if (format == ui.ImageByteFormat.png) { - final Uint8List png = await encodeDomImageSourceAsPng( - imageSource, - width, - height, - ); + final Uint8List png = await encodeDomImageSourceAsPng(imageSource, width, height); return png.buffer.asByteData(); } - final ByteBuffer pixels = - readDomImageSourcePixelsUnmodified(imageSource, width, height); + final ByteBuffer pixels = readDomImageSourcePixelsUnmodified(imageSource, width, height); return pixels.asByteData(); } @@ -166,16 +160,14 @@ void _bgrToRawRgba(ByteBuffer pixels) { } } -bool _shouldReadPixelsUnmodified( - VideoFrame videoFrame, ui.ImageByteFormat format) { +bool _shouldReadPixelsUnmodified(VideoFrame videoFrame, ui.ImageByteFormat format) { if (format == ui.ImageByteFormat.rawUnmodified) { return true; } // Do not convert if the requested format is RGBA and the video frame is // encoded as either RGBA or RGBX. - final bool isRgbFrame = - videoFrame.format == 'RGBA' || videoFrame.format == 'RGBX'; + final bool isRgbFrame = videoFrame.format == 'RGBA' || videoFrame.format == 'RGBX'; return format == ui.ImageByteFormat.rawStraightRgba && isRgbFrame; } @@ -194,9 +186,11 @@ Future readVideoFramePixelsUnmodified(VideoFrame videoFrame) async { } ByteBuffer readDomImageSourcePixelsUnmodified( - DomCanvasImageSource imageSource, int width, int height) { - final DomCanvasElement htmlCanvas = - createDomCanvasElement(width: width, height: height); + DomCanvasImageSource imageSource, + int width, + int height, +) { + final DomCanvasElement htmlCanvas = createDomCanvasElement(width: width, height: height); final DomCanvasRenderingContext2D ctx = htmlCanvas.getContext('2d')! as DomCanvasRenderingContext2D; ctx.drawImage(imageSource, 0, 0); @@ -209,13 +203,14 @@ ByteBuffer readDomImageSourcePixelsUnmodified( } Future encodeDomImageSourceAsPng( - DomCanvasImageSource imageSource, int width, int height) async { - final DomCanvasElement canvas = - createDomCanvasElement(width: width, height: height); + DomCanvasImageSource imageSource, + int width, + int height, +) async { + final DomCanvasElement canvas = createDomCanvasElement(width: width, height: height); final DomCanvasRenderingContext2D ctx = canvas.context2D; ctx.drawImage(imageSource, 0, 0); - final String pngBase64 = - canvas.toDataURL().substring('data:image/png;base64,'.length); + final String pngBase64 = canvas.toDataURL().substring('data:image/png;base64,'.length); // Resize the canvas to 0x0 to cause the browser to reclaim its memory // eagerly. canvas.width = 0; diff --git a/lib/web_ui/lib/src/engine/canvaskit/layer.dart b/lib/web_ui/lib/src/engine/canvaskit/layer.dart index 7026dad562d85..a331bfb30b652 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/layer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/layer.dart @@ -59,8 +59,7 @@ class RootLayer extends ContainerLayer { } } -class BackdropFilterEngineLayer extends ContainerLayer - implements ui.BackdropFilterEngineLayer { +class BackdropFilterEngineLayer extends ContainerLayer implements ui.BackdropFilterEngineLayer { BackdropFilterEngineLayer(this.filter, this.blendMode); final ui.ImageFilter filter; @@ -76,10 +75,8 @@ class BackdropFilterEngineLayer extends ContainerLayer } /// A layer that clips its child layers by a given [Path]. -class ClipPathEngineLayer extends ContainerLayer - implements ui.ClipPathEngineLayer { - ClipPathEngineLayer(this.clipPath, this.clipBehavior) - : assert(clipBehavior != ui.Clip.none); +class ClipPathEngineLayer extends ContainerLayer implements ui.ClipPathEngineLayer { + ClipPathEngineLayer(this.clipPath, this.clipBehavior) : assert(clipBehavior != ui.Clip.none); /// The path used to clip child layers. final CkPath clipPath; @@ -92,10 +89,8 @@ class ClipPathEngineLayer extends ContainerLayer } /// A layer that clips its child layers by a given [Rect]. -class ClipRectEngineLayer extends ContainerLayer - implements ui.ClipRectEngineLayer { - ClipRectEngineLayer(this.clipRect, this.clipBehavior) - : assert(clipBehavior != ui.Clip.none); +class ClipRectEngineLayer extends ContainerLayer implements ui.ClipRectEngineLayer { + ClipRectEngineLayer(this.clipRect, this.clipBehavior) : assert(clipBehavior != ui.Clip.none); /// The rectangle used to clip child layers. final ui.Rect clipRect; @@ -108,10 +103,8 @@ class ClipRectEngineLayer extends ContainerLayer } /// A layer that clips its child layers by a given [RRect]. -class ClipRRectEngineLayer extends ContainerLayer - implements ui.ClipRRectEngineLayer { - ClipRRectEngineLayer(this.clipRRect, this.clipBehavior) - : assert(clipBehavior != ui.Clip.none); +class ClipRRectEngineLayer extends ContainerLayer implements ui.ClipRRectEngineLayer { + ClipRRectEngineLayer(this.clipRRect, this.clipBehavior) : assert(clipBehavior != ui.Clip.none); /// The rounded rectangle used to clip child layers. final ui.RRect clipRRect; @@ -124,8 +117,7 @@ class ClipRRectEngineLayer extends ContainerLayer } /// A layer that paints its children with the given opacity. -class OpacityEngineLayer extends ContainerLayer - implements ui.OpacityEngineLayer { +class OpacityEngineLayer extends ContainerLayer implements ui.OpacityEngineLayer { OpacityEngineLayer(this.alpha, this.offset); final int alpha; @@ -138,8 +130,7 @@ class OpacityEngineLayer extends ContainerLayer } /// A layer that transforms its child layers by the given transform matrix. -class TransformEngineLayer extends ContainerLayer - implements ui.TransformEngineLayer { +class TransformEngineLayer extends ContainerLayer implements ui.TransformEngineLayer { TransformEngineLayer(this.transform); /// The matrix with which to transform the child layers. @@ -156,10 +147,8 @@ class TransformEngineLayer extends ContainerLayer /// This is a thin wrapper over [TransformEngineLayer] just so the framework /// gets the "OffsetEngineLayer" when calling `runtimeType.toString()`. This is /// better for debugging. -class OffsetEngineLayer extends TransformEngineLayer - implements ui.OffsetEngineLayer { - OffsetEngineLayer(double dx, double dy) - : super(Matrix4.translationValues(dx, dy, 0.0)); +class OffsetEngineLayer extends TransformEngineLayer implements ui.OffsetEngineLayer { + OffsetEngineLayer(double dx, double dy) : super(Matrix4.translationValues(dx, dy, 0.0)); @override void accept(LayerVisitor visitor) { @@ -168,8 +157,7 @@ class OffsetEngineLayer extends TransformEngineLayer } /// A layer that applies an [ui.ImageFilter] to its children. -class ImageFilterEngineLayer extends ContainerLayer - implements ui.ImageFilterEngineLayer { +class ImageFilterEngineLayer extends ContainerLayer implements ui.ImageFilterEngineLayer { ImageFilterEngineLayer(this.filter, this.offset); final ui.Offset offset; @@ -184,10 +172,8 @@ class ImageFilterEngineLayer extends ContainerLayer // https://github.com/flutter/flutter/issues/82832 } -class ShaderMaskEngineLayer extends ContainerLayer - implements ui.ShaderMaskEngineLayer { - ShaderMaskEngineLayer( - this.shader, this.maskRect, this.blendMode, this.filterQuality); +class ShaderMaskEngineLayer extends ContainerLayer implements ui.ShaderMaskEngineLayer { + ShaderMaskEngineLayer(this.shader, this.maskRect, this.blendMode, this.filterQuality); final ui.Shader shader; final ui.Rect maskRect; @@ -233,8 +219,7 @@ class PictureLayer extends Layer { } /// A layer which contains a [ui.ColorFilter]. -class ColorFilterEngineLayer extends ContainerLayer - implements ui.ColorFilterEngineLayer { +class ColorFilterEngineLayer extends ContainerLayer implements ui.ColorFilterEngineLayer { ColorFilterEngineLayer(this.filter); final ui.ColorFilter filter; diff --git a/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart b/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart index d2f34bc6a01c8..78c52c340fac7 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/layer_scene_builder.dart @@ -22,19 +22,13 @@ class LayerScene implements ui.Scene { @override Future toImage(int width, int height) { - final ui.Picture picture = layerTree.flatten(ui.Size( - width.toDouble(), - height.toDouble(), - )); + final ui.Picture picture = layerTree.flatten(ui.Size(width.toDouble(), height.toDouble())); return picture.toImage(width, height); } @override ui.Image toImageSync(int width, int height) { - final ui.Picture picture = layerTree.flatten(ui.Size( - width.toDouble(), - height.toDouble(), - )); + final ui.Picture picture = layerTree.flatten(ui.Size(width.toDouble(), height.toDouble())); return picture.toImageSync(width, height); } } @@ -60,8 +54,7 @@ class LayerSceneBuilder implements ui.SceneBuilder { bool isComplexHint = false, bool willChangeHint = false, }) { - currentLayer.add(PictureLayer( - picture as CkPicture, offset, isComplexHint, willChangeHint)); + currentLayer.add(PictureLayer(picture as CkPicture, offset, isComplexHint, willChangeHint)); } @override @@ -112,10 +105,7 @@ class LayerSceneBuilder implements ui.SceneBuilder { ui.EngineLayer? oldLayer, int? backdropId, }) { - return pushLayer(BackdropFilterEngineLayer( - filter, - blendMode, - )); + return pushLayer(BackdropFilterEngineLayer(filter, blendMode)); } @override @@ -124,8 +114,7 @@ class LayerSceneBuilder implements ui.SceneBuilder { ui.Clip clipBehavior = ui.Clip.antiAlias, ui.EngineLayer? oldLayer, }) { - return pushLayer( - ClipPathEngineLayer(path as CkPath, clipBehavior)); + return pushLayer(ClipPathEngineLayer(path as CkPath, clipBehavior)); } @override @@ -134,8 +123,7 @@ class LayerSceneBuilder implements ui.SceneBuilder { ui.Clip? clipBehavior, ui.EngineLayer? oldLayer, }) { - return pushLayer( - ClipRRectEngineLayer(rrect, clipBehavior)); + return pushLayer(ClipRRectEngineLayer(rrect, clipBehavior)); } @override @@ -144,8 +132,7 @@ class LayerSceneBuilder implements ui.SceneBuilder { ui.Clip clipBehavior = ui.Clip.antiAlias, ui.EngineLayer? oldLayer, }) { - return pushLayer( - ClipRectEngineLayer(rect, clipBehavior)); + return pushLayer(ClipRectEngineLayer(rect, clipBehavior)); } @override @@ -166,11 +153,7 @@ class LayerSceneBuilder implements ui.SceneBuilder { } @override - OffsetEngineLayer pushOffset( - double dx, - double dy, { - ui.EngineLayer? oldLayer, - }) { + OffsetEngineLayer pushOffset(double dx, double dy, {ui.EngineLayer? oldLayer}) { return pushLayer(OffsetEngineLayer(dx, dy)); } @@ -192,14 +175,12 @@ class LayerSceneBuilder implements ui.SceneBuilder { ui.FilterQuality filterQuality = ui.FilterQuality.low, }) { return pushLayer( - ShaderMaskEngineLayer(shader, maskRect, blendMode, filterQuality)); + ShaderMaskEngineLayer(shader, maskRect, blendMode, filterQuality), + ); } @override - TransformEngineLayer pushTransform( - Float64List matrix4, { - ui.EngineLayer? oldLayer, - }) { + TransformEngineLayer pushTransform(Float64List matrix4, {ui.EngineLayer? oldLayer}) { final Matrix4 matrix = Matrix4.fromFloat32List(toMatrix32(matrix4)); return pushLayer(TransformEngineLayer(matrix)); } diff --git a/lib/web_ui/lib/src/engine/canvaskit/layer_tree.dart b/lib/web_ui/lib/src/engine/canvaskit/layer_tree.dart index 8fa2c5efc56a5..56f7df3536ba9 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/layer_tree.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/layer_tree.dart @@ -39,10 +39,7 @@ class LayerTree { /// tree. This paint pass is just used to measure the bounds for each picture /// so we can optimize the total number of canvases required. void measure(Frame frame, BitmapSize size, {bool ignoreRasterCache = false}) { - final MeasureVisitor measureVisitor = MeasureVisitor( - size, - frame.viewEmbedder!, - ); + final MeasureVisitor measureVisitor = MeasureVisitor(size, frame.viewEmbedder!); if (rootLayer.needsPainting) { rootLayer.accept(measureVisitor); } @@ -55,13 +52,9 @@ class LayerTree { /// not be used. void paint(Frame frame, {bool ignoreRasterCache = false}) { final CkNWayCanvas internalNodesCanvas = CkNWayCanvas(); - final Iterable overlayCanvases = - frame.viewEmbedder!.getOptimizedCanvases(); + final Iterable overlayCanvases = frame.viewEmbedder!.getOptimizedCanvases(); overlayCanvases.forEach(internalNodesCanvas.addCanvas); - final PaintVisitor paintVisitor = PaintVisitor( - internalNodesCanvas, - frame.viewEmbedder!, - ); + final PaintVisitor paintVisitor = PaintVisitor(internalNodesCanvas, frame.viewEmbedder!); if (rootLayer.needsPainting) { rootLayer.accept(paintVisitor); } @@ -78,8 +71,7 @@ class LayerTree { final CkNWayCanvas internalNodesCanvas = CkNWayCanvas(); internalNodesCanvas.addCanvas(canvas); - final PaintVisitor paintVisitor = - PaintVisitor.forToImage(internalNodesCanvas, canvas); + final PaintVisitor paintVisitor = PaintVisitor.forToImage(internalNodesCanvas, canvas); if (rootLayer.needsPainting) { rootLayer.accept(paintVisitor); } @@ -98,8 +90,7 @@ class Frame { final HtmlViewEmbedder? viewEmbedder; /// Rasterize the given layer tree into this frame. - bool raster(LayerTree layerTree, BitmapSize size, - {bool ignoreRasterCache = false}) { + bool raster(LayerTree layerTree, BitmapSize size, {bool ignoreRasterCache = false}) { timeAction(kProfilePrerollFrame, () { layerTree.preroll(this, ignoreRasterCache: ignoreRasterCache); layerTree.measure(this, size, ignoreRasterCache: ignoreRasterCache); diff --git a/lib/web_ui/lib/src/engine/canvaskit/layer_visitor.dart b/lib/web_ui/lib/src/engine/canvaskit/layer_visitor.dart index 9850c75c99f39..9516b466d5d74 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/layer_visitor.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/layer_visitor.dart @@ -104,8 +104,7 @@ class PrerollVisitor extends LayerVisitor { mutatorsStack.pushClipRRect(clipRRect.clipRRect); final ui.Rect childPaintBounds = prerollChildren(clipRRect); if (childPaintBounds.overlaps(clipRRect.clipRRect.outerRect)) { - clipRRect.paintBounds = - childPaintBounds.intersect(clipRRect.clipRRect.outerRect); + clipRRect.paintBounds = childPaintBounds.intersect(clipRRect.clipRRect.outerRect); } mutatorsStack.pop(); } @@ -127,18 +126,17 @@ class PrerollVisitor extends LayerVisitor { @override void visitImageFilter(ImageFilterEngineLayer imageFilter) { - mutatorsStack.pushTransform(Matrix4.translationValues( - imageFilter.offset.dx, imageFilter.offset.dy, 0.0)); + mutatorsStack.pushTransform( + Matrix4.translationValues(imageFilter.offset.dx, imageFilter.offset.dy, 0.0), + ); final CkManagedSkImageFilterConvertible convertible; if (imageFilter.filter is ui.ColorFilter) { - convertible = - createCkColorFilter(imageFilter.filter as EngineColorFilter)!; + convertible = createCkColorFilter(imageFilter.filter as EngineColorFilter)!; } else { convertible = imageFilter.filter as CkManagedSkImageFilterConvertible; } ui.Rect childPaintBounds = prerollChildren(imageFilter); - childPaintBounds = childPaintBounds.translate( - imageFilter.offset.dx, imageFilter.offset.dy); + childPaintBounds = childPaintBounds.translate(imageFilter.offset.dx, imageFilter.offset.dy); if (imageFilter.filter is ui.ColorFilter) { // If the filter is a ColorFilter, the extended paint bounds will be the // entire screen, which is not what we want. @@ -161,13 +159,13 @@ class PrerollVisitor extends LayerVisitor { @override void visitOpacity(OpacityEngineLayer opacity) { mutatorsStack.pushTransform( - Matrix4.translationValues(opacity.offset.dx, opacity.offset.dy, 0.0)); + Matrix4.translationValues(opacity.offset.dx, opacity.offset.dy, 0.0), + ); mutatorsStack.pushOpacity(opacity.alpha); prerollContainerLayer(opacity); mutatorsStack.pop(); mutatorsStack.pop(); - opacity.paintBounds = - opacity.paintBounds.translate(opacity.offset.dx, opacity.offset.dy); + opacity.paintBounds = opacity.paintBounds.translate(opacity.offset.dx, opacity.offset.dy); } @override @@ -216,17 +214,12 @@ class PrerollVisitor extends LayerVisitor { /// A layer visitor which measures the pictures that make up the scene and /// prepares for them to be optimized into few canvases. class MeasureVisitor extends LayerVisitor { - MeasureVisitor( - BitmapSize size, - this.viewEmbedder, - ) : measuringRecorder = CkPictureRecorder() { - measuringCanvas = - measuringRecorder.beginRecording(ui.Offset.zero & size.toSize()); + MeasureVisitor(BitmapSize size, this.viewEmbedder) : measuringRecorder = CkPictureRecorder() { + measuringCanvas = measuringRecorder.beginRecording(ui.Offset.zero & size.toSize()); } /// A stack of image filters which apply their transforms to measured bounds. - List imageFilterStack = - []; + List imageFilterStack = []; final CkPictureRecorder measuringRecorder; @@ -269,8 +262,7 @@ class MeasureVisitor extends LayerVisitor { assert(clipPath.needsPainting); measuringCanvas.save(); - measuringCanvas.clipPath( - clipPath.clipPath, clipPath.clipBehavior != ui.Clip.hardEdge); + measuringCanvas.clipPath(clipPath.clipPath, clipPath.clipBehavior != ui.Clip.hardEdge); if (clipPath.clipBehavior == ui.Clip.antiAliasWithSaveLayer) { measuringCanvas.saveLayer(clipPath.paintBounds, null); @@ -307,8 +299,7 @@ class MeasureVisitor extends LayerVisitor { assert(clipRRect.needsPainting); measuringCanvas.save(); - measuringCanvas.clipRRect( - clipRRect.clipRRect, clipRRect.clipBehavior != ui.Clip.hardEdge); + measuringCanvas.clipRRect(clipRRect.clipRRect, clipRRect.clipBehavior != ui.Clip.hardEdge); if (clipRRect.clipBehavior == ui.Clip.antiAliasWithSaveLayer) { measuringCanvas.saveLayer(clipRRect.paintBounds, null); } @@ -354,8 +345,7 @@ class MeasureVisitor extends LayerVisitor { @override void visitImageFilter(ImageFilterEngineLayer imageFilter) { assert(imageFilter.needsPainting); - final ui.Rect offsetPaintBounds = - imageFilter.paintBounds.shift(-imageFilter.offset); + final ui.Rect offsetPaintBounds = imageFilter.paintBounds.shift(-imageFilter.offset); measuringCanvas.save(); measuringCanvas.translate(imageFilter.offset.dx, imageFilter.offset.dy); measuringCanvas.clipRect(offsetPaintBounds, ui.ClipOp.intersect, false); @@ -363,8 +353,7 @@ class MeasureVisitor extends LayerVisitor { paint.imageFilter = imageFilter.filter; measuringCanvas.saveLayer(offsetPaintBounds, paint); if (imageFilter.filter is! ui.ColorFilter) { - imageFilterStack - .add(imageFilter.filter as CkManagedSkImageFilterConvertible); + imageFilterStack.add(imageFilter.filter as CkManagedSkImageFilterConvertible); } measureChildren(imageFilter); if (imageFilter.filter is! ui.ColorFilter) { @@ -393,15 +382,13 @@ class MeasureVisitor extends LayerVisitor { // Get the picture bounds using the measuring canvas. final Float32List localTransform = measuringCanvas.getLocalToDevice(); - ui.Rect transformedBounds = Matrix4.fromFloat32List(localTransform) - .transformRect(picture.picture.cullRect); + ui.Rect transformedBounds = Matrix4.fromFloat32List( + localTransform, + ).transformRect(picture.picture.cullRect); // Modify the bounds with the image filters. - for (final CkManagedSkImageFilterConvertible convertible - in imageFilterStack.reversed) { + for (final CkManagedSkImageFilterConvertible convertible in imageFilterStack.reversed) { convertible.withSkImageFilter((SkImageFilter skFilter) { - transformedBounds = rectFromSkIRect( - skFilter.getOutputBounds(toSkRect(transformedBounds)), - ); + transformedBounds = rectFromSkIRect(skFilter.getOutputBounds(toSkRect(transformedBounds))); }, defaultBlurTileMode: ui.TileMode.decal); } picture.sceneBounds = transformedBounds; @@ -427,8 +414,7 @@ class MeasureVisitor extends LayerVisitor { measuringCanvas.save(); // TODO(hterkelsen): Only clip if the ColorFilter affects transparent black. - measuringCanvas.clipRect( - colorFilter.paintBounds, ui.ClipOp.intersect, false); + measuringCanvas.clipRect(colorFilter.paintBounds, ui.ClipOp.intersect, false); measuringCanvas.saveLayer(colorFilter.paintBounds, paint); measureChildren(colorFilter); @@ -449,15 +435,9 @@ class MeasureVisitor extends LayerVisitor { /// The canvases are the optimized canvases that were created when the view /// embedder optimized the canvases after the measure step. class PaintVisitor extends LayerVisitor { - PaintVisitor( - this.nWayCanvas, - HtmlViewEmbedder this.viewEmbedder, - ) : toImageCanvas = null; + PaintVisitor(this.nWayCanvas, HtmlViewEmbedder this.viewEmbedder) : toImageCanvas = null; - PaintVisitor.forToImage( - this.nWayCanvas, - this.toImageCanvas, - ) : viewEmbedder = null; + PaintVisitor.forToImage(this.nWayCanvas, this.toImageCanvas) : viewEmbedder = null; /// A multi-canvas that applies clips, transforms, and opacity /// operations to all canvases (root canvas and overlay canvases for the @@ -494,8 +474,7 @@ class PaintVisitor extends LayerVisitor { void visitBackdropFilter(BackdropFilterEngineLayer backdropFilter) { final CkPaint paint = CkPaint()..blendMode = backdropFilter.blendMode; - nWayCanvas.saveLayerWithFilter( - backdropFilter.paintBounds, backdropFilter.filter, paint); + nWayCanvas.saveLayerWithFilter(backdropFilter.paintBounds, backdropFilter.filter, paint); paintChildren(backdropFilter); nWayCanvas.restore(); } @@ -505,8 +484,7 @@ class PaintVisitor extends LayerVisitor { assert(clipPath.needsPainting); nWayCanvas.save(); - nWayCanvas.clipPath( - clipPath.clipPath, clipPath.clipBehavior != ui.Clip.hardEdge); + nWayCanvas.clipPath(clipPath.clipPath, clipPath.clipBehavior != ui.Clip.hardEdge); if (clipPath.clipBehavior == ui.Clip.antiAliasWithSaveLayer) { nWayCanvas.saveLayer(clipPath.paintBounds, null); @@ -543,8 +521,7 @@ class PaintVisitor extends LayerVisitor { assert(clipRRect.needsPainting); nWayCanvas.save(); - nWayCanvas.clipRRect( - clipRRect.clipRRect, clipRRect.clipBehavior != ui.Clip.hardEdge); + nWayCanvas.clipRRect(clipRRect.clipRRect, clipRRect.clipBehavior != ui.Clip.hardEdge); if (clipRRect.clipBehavior == ui.Clip.antiAliasWithSaveLayer) { nWayCanvas.saveLayer(clipRRect.paintBounds, null); } @@ -590,8 +567,7 @@ class PaintVisitor extends LayerVisitor { @override void visitImageFilter(ImageFilterEngineLayer imageFilter) { assert(imageFilter.needsPainting); - final ui.Rect offsetPaintBounds = - imageFilter.paintBounds.shift(-imageFilter.offset); + final ui.Rect offsetPaintBounds = imageFilter.paintBounds.shift(-imageFilter.offset); nWayCanvas.save(); nWayCanvas.translate(imageFilter.offset.dx, imageFilter.offset.dy); nWayCanvas.clipRect(offsetPaintBounds, ui.ClipOp.intersect, false); @@ -619,8 +595,7 @@ class PaintVisitor extends LayerVisitor { late List canvasesToApplyShaderMask; if (viewEmbedder != null) { final Set canvases = {}; - final List? pictureChildren = - picturesUnderShaderMask[shaderMask]; + final List? pictureChildren = picturesUnderShaderMask[shaderMask]; if (pictureChildren != null) { for (final PictureLayer picture in pictureChildren) { canvases.add(viewEmbedder!.getOptimizedCanvasFor(picture)); @@ -636,9 +611,9 @@ class PaintVisitor extends LayerVisitor { canvas.translate(shaderMask.maskRect.left, shaderMask.maskRect.top); canvas.drawRect( - ui.Rect.fromLTWH( - 0, 0, shaderMask.maskRect.width, shaderMask.maskRect.height), - paint); + ui.Rect.fromLTWH(0, 0, shaderMask.maskRect.width, shaderMask.maskRect.height), + paint, + ); canvas.restore(); } nWayCanvas.restore(); diff --git a/lib/web_ui/lib/src/engine/canvaskit/mask_filter.dart b/lib/web_ui/lib/src/engine/canvaskit/mask_filter.dart index 55f35541dc6a2..d99142fa574e4 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/mask_filter.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/mask_filter.dart @@ -10,9 +10,5 @@ import 'canvaskit_api.dart'; /// /// It is the responsibility of the caller to delete the returned Skia object. SkMaskFilter createBlurSkMaskFilter(ui.BlurStyle blurStyle, double sigma) { - return canvasKit.MaskFilter.MakeBlur( - toSkBlurStyle(blurStyle), - sigma, - true, - )!; + return canvasKit.MaskFilter.MakeBlur(toSkBlurStyle(blurStyle), sigma, true)!; } diff --git a/lib/web_ui/lib/src/engine/canvaskit/multi_surface_rasterizer.dart b/lib/web_ui/lib/src/engine/canvaskit/multi_surface_rasterizer.dart index f3b2d78405bf1..ef5d37463bd18 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/multi_surface_rasterizer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/multi_surface_rasterizer.dart @@ -14,8 +14,7 @@ import 'package:ui/ui.dart' as ui; class MultiSurfaceRasterizer extends Rasterizer { @override MultiSurfaceViewRasterizer createViewRasterizer(EngineFlutterView view) { - return _viewRasterizers.putIfAbsent( - view, () => MultiSurfaceViewRasterizer(view, this)); + return _viewRasterizers.putIfAbsent(view, () => MultiSurfaceViewRasterizer(view, this)); } final Map _viewRasterizers = @@ -23,8 +22,7 @@ class MultiSurfaceRasterizer extends Rasterizer { @override void dispose() { - for (final MultiSurfaceViewRasterizer viewRasterizer - in _viewRasterizers.values) { + for (final MultiSurfaceViewRasterizer viewRasterizer in _viewRasterizers.values) { viewRasterizer.dispose(); } _viewRasterizers.clear(); @@ -32,8 +30,7 @@ class MultiSurfaceRasterizer extends Rasterizer { @override void setResourceCacheMaxBytes(int bytes) { - for (final MultiSurfaceViewRasterizer viewRasterizer - in _viewRasterizers.values) { + for (final MultiSurfaceViewRasterizer viewRasterizer in _viewRasterizers.values) { viewRasterizer.displayFactory.forEachCanvas((Surface surface) { surface.setSkiaResourceCacheMaxBytes(bytes); }); @@ -47,9 +44,9 @@ class MultiSurfaceViewRasterizer extends ViewRasterizer { final MultiSurfaceRasterizer rasterizer; @override - final DisplayCanvasFactory displayFactory = - DisplayCanvasFactory( - createCanvas: () => Surface(isDisplayCanvas: true)); + final DisplayCanvasFactory displayFactory = DisplayCanvasFactory( + createCanvas: () => Surface(isDisplayCanvas: true), + ); @override void prepareToDraw() { @@ -57,8 +54,7 @@ class MultiSurfaceViewRasterizer extends ViewRasterizer { } @override - Future rasterizeToCanvas( - DisplayCanvas canvas, List pictures) { + Future rasterizeToCanvas(DisplayCanvas canvas, List pictures) { final Surface surface = canvas as Surface; surface.createOrUpdateSurface(currentFrameSize); surface.positionToShowFrame(currentFrameSize); diff --git a/lib/web_ui/lib/src/engine/canvaskit/n_way_canvas.dart b/lib/web_ui/lib/src/engine/canvaskit/n_way_canvas.dart index 529d74908e271..d494ba167c608 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/n_way_canvas.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/n_way_canvas.dart @@ -35,8 +35,7 @@ class CkNWayCanvas { } /// Calls [saveLayerWithFilter] on all canvases. - void saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter, - [CkPaint? paint]) { + void saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter, [CkPaint? paint]) { for (int i = 0; i < _canvases.length; i++) { _canvases[i].saveLayerWithFilter(bounds, filter, paint); } diff --git a/lib/web_ui/lib/src/engine/canvaskit/native_memory.dart b/lib/web_ui/lib/src/engine/canvaskit/native_memory.dart index c638f1f8a1664..93104c1f1fee6 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/native_memory.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/native_memory.dart @@ -21,11 +21,11 @@ import 'package:ui/src/engine.dart'; /// 5. The finalizer function is called with the SkPaint as the sole argument. /// 6. We call `delete` on SkPaint. DomFinalizationRegistry _finalizationRegistry = DomFinalizationRegistry( - ((ExternalDartReference> boxedUniq) => - boxedUniq.toDartObject.collect() - ).toJS); + ((ExternalDartReference> boxedUniq) => boxedUniq.toDartObject.collect()).toJS, +); -NativeMemoryFinalizationRegistry nativeMemoryFinalizationRegistry = NativeMemoryFinalizationRegistry(); +NativeMemoryFinalizationRegistry nativeMemoryFinalizationRegistry = + NativeMemoryFinalizationRegistry(); /// An indirection to [DomFinalizationRegistry] to enable tests provide a /// mock implementation of a finalization registry. @@ -181,9 +181,7 @@ class CountedRef { List debugGetStackTraces() { List? result; assert(() { - result = debugReferrers - .map((R referrer) => referrer.debugStackTrace) - .toList(); + result = debugReferrers.map((R referrer) => referrer.debugStackTrace).toList(); return true; }()); @@ -197,10 +195,7 @@ class CountedRef { /// Increases the reference count of this box because a new object began /// sharing ownership of the underlying [nativeObject]. void ref(R debugReferrer) { - assert( - !_ref.isDisposed, - 'Cannot increment ref count on a deleted handle.', - ); + assert(!_ref.isDisposed, 'Cannot increment ref count on a deleted handle.'); assert(_refCount > 0); assert( debugReferrers.add(debugReferrer), @@ -217,10 +212,7 @@ class CountedRef { /// If this causes the reference count to drop to zero, deletes the /// [nativeObject]. void unref(R debugReferrer) { - assert( - !_ref.isDisposed, - 'Attempted to unref an already deleted native object.', - ); + assert(!_ref.isDisposed, 'Attempted to unref an already deleted native object.'); assert( debugReferrers.remove(debugReferrer), 'Attempted to decrement ref count by the same referrer more than once.', diff --git a/lib/web_ui/lib/src/engine/canvaskit/offscreen_canvas_rasterizer.dart b/lib/web_ui/lib/src/engine/canvaskit/offscreen_canvas_rasterizer.dart index 585575391a1cb..57505cf6c7a83 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/offscreen_canvas_rasterizer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/offscreen_canvas_rasterizer.dart @@ -14,8 +14,7 @@ class OffscreenCanvasRasterizer extends Rasterizer { @override OffscreenCanvasViewRasterizer createViewRasterizer(EngineFlutterView view) { - return _viewRasterizers.putIfAbsent( - view, () => OffscreenCanvasViewRasterizer(view, this)); + return _viewRasterizers.putIfAbsent(view, () => OffscreenCanvasViewRasterizer(view, this)); } final Map _viewRasterizers = @@ -29,8 +28,7 @@ class OffscreenCanvasRasterizer extends Rasterizer { @override void dispose() { offscreenSurface.dispose(); - for (final OffscreenCanvasViewRasterizer viewRasterizer - in _viewRasterizers.values) { + for (final OffscreenCanvasViewRasterizer viewRasterizer in _viewRasterizers.values) { viewRasterizer.dispose(); } } @@ -42,13 +40,13 @@ class OffscreenCanvasViewRasterizer extends ViewRasterizer { final OffscreenCanvasRasterizer rasterizer; @override - final DisplayCanvasFactory displayFactory = - DisplayCanvasFactory(createCanvas: () => RenderCanvas()); + final DisplayCanvasFactory displayFactory = DisplayCanvasFactory( + createCanvas: () => RenderCanvas(), + ); /// Render the given [pictures] so it is displayed by the given [canvas]. @override - Future rasterizeToCanvas( - DisplayCanvas canvas, List pictures) async { + Future rasterizeToCanvas(DisplayCanvas canvas, List pictures) async { await rasterizer.offscreenSurface.rasterizeToCanvas( currentFrameSize, canvas as RenderCanvas, diff --git a/lib/web_ui/lib/src/engine/canvaskit/overlay_scene_optimizer.dart b/lib/web_ui/lib/src/engine/canvaskit/overlay_scene_optimizer.dart index 0d7dba2c28f8e..4a079e9b4e060 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/overlay_scene_optimizer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/overlay_scene_optimizer.dart @@ -43,8 +43,7 @@ class Rendering { } /// A list of just the canvases in the rendering. - List get canvases => - entities.whereType().toList(); + List get canvases => entities.whereType().toList(); @override String toString() => entities.toString(); @@ -119,16 +118,22 @@ ui.Rect computePlatformViewBounds(EmbeddedViewParams params) { for (final Mutator mutator in params.mutators.reversed) { switch (mutator.type) { case MutatorType.clipRect: - final ui.Rect transformedClipBounds = - transformRectWithMatrix(currentTransform, mutator.rect!); + final ui.Rect transformedClipBounds = transformRectWithMatrix( + currentTransform, + mutator.rect!, + ); currentClipBounds = currentClipBounds.intersect(transformedClipBounds); case MutatorType.clipRRect: - final ui.Rect transformedClipBounds = - transformRectWithMatrix(currentTransform, mutator.rrect!.outerRect); + final ui.Rect transformedClipBounds = transformRectWithMatrix( + currentTransform, + mutator.rrect!.outerRect, + ); currentClipBounds = currentClipBounds.intersect(transformedClipBounds); case MutatorType.clipPath: final ui.Rect transformedClipBounds = transformRectWithMatrix( - currentTransform, mutator.path!.getBounds()); + currentTransform, + mutator.path!.getBounds(), + ); currentClipBounds.intersect(transformedClipBounds); case MutatorType.transform: currentTransform = currentTransform.multiplied(mutator.matrix!); @@ -146,8 +151,7 @@ ui.Rect computePlatformViewBounds(EmbeddedViewParams params) { params.size.width, params.size.height, ); - final ui.Rect transformedBounds = - transformRectWithMatrix(currentTransform, rawBounds); + final ui.Rect transformedBounds = transformRectWithMatrix(currentTransform, rawBounds); return transformedBounds.intersect(currentClipBounds); } @@ -171,8 +175,8 @@ Rendering createOptimizedRendering( final int viewId = renderObject.viewId; final RenderingPlatformView platformView = RenderingPlatformView(viewId); if (PlatformViewManager.instance.isVisible(viewId)) { - final ui.Rect platformViewBounds = cachedComputedRects[viewId] = - computePlatformViewBounds(paramsForViews[viewId]!); + final ui.Rect platformViewBounds = + cachedComputedRects[viewId] = computePlatformViewBounds(paramsForViews[viewId]!); if (debugOverlayOptimizationBounds) { platformView.debugComputedBounds = platformViewBounds; @@ -204,9 +208,7 @@ Rendering createOptimizedRendering( // when it is eventually added. bool addedToTentativeCanvas = false; for (final PictureLayer canvasPicture in tentativeCanvas.pictures) { - if (!canvasPicture.sceneBounds! - .intersect(picture.sceneBounds!) - .isEmpty) { + if (!canvasPicture.sceneBounds!.intersect(picture.sceneBounds!).isEmpty) { tentativeCanvas.add(picture); addedToTentativeCanvas = true; break; @@ -221,8 +223,7 @@ Rendering createOptimizedRendering( for (final RenderingEntity entity in result.entities.reversed) { if (entity is RenderingPlatformView) { if (PlatformViewManager.instance.isVisible(entity.viewId)) { - final ui.Rect platformViewBounds = - cachedComputedRects[entity.viewId]!; + final ui.Rect platformViewBounds = cachedComputedRects[entity.viewId]!; if (!platformViewBounds.intersect(picture.sceneBounds!).isEmpty) { // The next picture intersects with a platform view already in the // result. Add this picture to the first render canvas which comes @@ -240,9 +241,7 @@ Rendering createOptimizedRendering( lastCanvasSeen = entity; // Check if we intersect with any pictures in this render canvas. for (final PictureLayer canvasPicture in entity.pictures) { - if (!canvasPicture.sceneBounds! - .intersect(picture.sceneBounds!) - .isEmpty) { + if (!canvasPicture.sceneBounds!.intersect(picture.sceneBounds!).isEmpty) { lastCanvasSeen.add(picture); addedPictureToRendering = true; break; diff --git a/lib/web_ui/lib/src/engine/canvaskit/painting.dart b/lib/web_ui/lib/src/engine/canvaskit/painting.dart index c56ddce6c8fcd..210c1e9236001 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/painting.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/painting.dart @@ -54,10 +54,9 @@ class CkPaint implements ui.Paint { if (localMaskFilter != null) { // CanvasKit returns `null` if the sigma is `0` or infinite. if (localMaskFilter.webOnlySigma.isFinite && localMaskFilter.webOnlySigma > 0) { - skPaint.setMaskFilter(createBlurSkMaskFilter( - localMaskFilter.webOnlyBlurStyle, - localMaskFilter.webOnlySigma, - )); + skPaint.setMaskFilter( + createBlurSkMaskFilter(localMaskFilter.webOnlyBlurStyle, localMaskFilter.webOnlySigma), + ); } } @@ -115,7 +114,7 @@ class CkPaint implements ui.Paint { _effectiveColorFilter = _invertColorFilter; } else { _effectiveColorFilter = ManagedSkColorFilter( - CkComposeColorFilter(_invertColorFilter, _effectiveColorFilter!) + CkComposeColorFilter(_invertColorFilter, _effectiveColorFilter!), ); } } @@ -169,7 +168,7 @@ class CkPaint implements ui.Paint { _effectiveColorFilter = _invertColorFilter; } else { _effectiveColorFilter = ManagedSkColorFilter( - CkComposeColorFilter(_invertColorFilter, _effectiveColorFilter!) + CkComposeColorFilter(_invertColorFilter, _effectiveColorFilter!), ); } } @@ -287,14 +286,15 @@ final Float32List _invertColorMatrix = Float32List.fromList(const [ -1.0, 0, 0, 1.0, 0, // row 0, -1.0, 0, 1.0, 0, // row 0, 0, -1.0, 1.0, 0, // row - 1.0, 1.0, 1.0, 1.0, 0 + 1.0, 1.0, 1.0, 1.0, 0, ]); -final ManagedSkColorFilter _invertColorFilter = ManagedSkColorFilter(CkMatrixColorFilter(_invertColorMatrix)); +final ManagedSkColorFilter _invertColorFilter = ManagedSkColorFilter( + CkMatrixColorFilter(_invertColorMatrix), +); class CkFragmentProgram implements ui.FragmentProgram { - CkFragmentProgram(this.name, this.effect, this.uniforms, this.floatCount, - this.textureCount); + CkFragmentProgram(this.name, this.effect, this.uniforms, this.floatCount, this.textureCount); factory CkFragmentProgram.fromBytes(String name, Uint8List data) { final ShaderData shaderData = ShaderData.fromBytes(data); @@ -326,9 +326,9 @@ class CkFragmentProgram implements ui.FragmentProgram { class CkFragmentShader implements ui.FragmentShader, CkShader { CkFragmentShader(this.name, this.effect, int floatCount, int textureCount) - : floats = mallocFloat32List(floatCount + textureCount * 2), - samplers = List.filled(textureCount, null), - lastFloatIndex = floatCount; + : floats = mallocFloat32List(floatCount + textureCount * 2), + samplers = List.filled(textureCount, null), + lastFloatIndex = floatCount; final String name; final SkRuntimeEffect effect; @@ -344,13 +344,16 @@ class CkFragmentShader implements ui.FragmentShader, CkShader { assert(!_debugDisposed, 'FragmentShader has been disposed of.'); ref?.dispose(); - final SkShader? result = samplers.isEmpty - ? effect.makeShader(floats) - : effect.makeShaderWithChildren(floats, samplers); + final SkShader? result = + samplers.isEmpty + ? effect.makeShader(floats) + : effect.makeShaderWithChildren(floats, samplers); if (result == null) { - throw Exception('Invalid uniform data for shader $name:' - ' floatUniforms: $floats \n' - ' samplerUniforms: $samplers \n'); + throw Exception( + 'Invalid uniform data for shader $name:' + ' floatUniforms: $floats \n' + ' samplerUniforms: $samplers \n', + ); } ref = UniqueRef(this, result, 'FragmentShader'); @@ -366,8 +369,12 @@ class CkFragmentShader implements ui.FragmentShader, CkShader { @override void setImageSampler(int index, ui.Image image) { assert(!_debugDisposed, 'FragmentShader has been disposed of.'); - final ui.ImageShader sampler = ui.ImageShader(image, ui.TileMode.clamp, - ui.TileMode.clamp, toMatrix64(Matrix4.identity().storage)); + final ui.ImageShader sampler = ui.ImageShader( + image, + ui.TileMode.clamp, + ui.TileMode.clamp, + toMatrix64(Matrix4.identity().storage), + ); samplers[index] = (sampler as CkShader).getSkShader(ui.FilterQuality.none); setFloat(lastFloatIndex + 2 * index, (sampler as CkImageShader).imageWidth.toDouble()); setFloat(lastFloatIndex + 2 * index + 1, sampler.imageHeight.toDouble()); diff --git a/lib/web_ui/lib/src/engine/canvaskit/path.dart b/lib/web_ui/lib/src/engine/canvaskit/path.dart index 06d5d7d19b929..cab184d80264d 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/path.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/path.dart @@ -59,11 +59,7 @@ class CkPath implements ScenePath { @override void addArc(ui.Rect oval, double startAngle, double sweepAngle) { const double toDegrees = 180.0 / math.pi; - skiaObject.addArc( - toSkRect(oval), - startAngle * toDegrees, - sweepAngle * toDegrees, - ); + skiaObject.addArc(toSkRect(oval), startAngle * toDegrees, sweepAngle * toDegrees); } @override @@ -76,7 +72,8 @@ class CkPath implements ScenePath { List skMatrix; if (matrix4 == null) { skMatrix = toSkMatrixFromFloat32( - Matrix4.translationValues(offset.dx, offset.dy, 0.0).storage); + Matrix4.translationValues(offset.dx, offset.dy, 0.0).storage, + ); } else { skMatrix = toSkMatrixFromFloat64(matrix4); skMatrix[2] += offset.dx; @@ -107,10 +104,7 @@ class CkPath implements ScenePath { @override void addRRect(ui.RRect rrect) { - skiaObject.addRRect( - toSkRRect(rrect), - false, - ); + skiaObject.addRRect(toSkRRect(rrect), false); } @override @@ -119,8 +113,7 @@ class CkPath implements ScenePath { } @override - void arcTo( - ui.Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) { + void arcTo(ui.Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) { const double toDegrees = 180.0 / math.pi; skiaObject.arcToOval( toSkRect(rect), @@ -131,11 +124,13 @@ class CkPath implements ScenePath { } @override - void arcToPoint(ui.Offset arcEnd, - {ui.Radius radius = ui.Radius.zero, - double rotation = 0.0, - bool largeArc = false, - bool clockwise = true}) { + void arcToPoint( + ui.Offset arcEnd, { + ui.Radius radius = ui.Radius.zero, + double rotation = 0.0, + bool largeArc = false, + bool clockwise = true, + }) { skiaObject.arcToRotated( radius.x, radius.y, @@ -168,8 +163,7 @@ class CkPath implements ScenePath { } @override - void cubicTo( - double x1, double y1, double x2, double y2, double x3, double y3) { + void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3) { skiaObject.cubicTo(x1, y1, x2, y2, x3, y3); } @@ -178,7 +172,8 @@ class CkPath implements ScenePath { List skMatrix; if (matrix4 == null) { skMatrix = toSkMatrixFromFloat32( - Matrix4.translationValues(offset.dx, offset.dy, 0.0).storage); + Matrix4.translationValues(offset.dx, offset.dy, 0.0).storage, + ); } else { skMatrix = toSkMatrixFromFloat64(matrix4); skMatrix[2] += offset.dx; @@ -219,11 +214,13 @@ class CkPath implements ScenePath { } @override - void relativeArcToPoint(ui.Offset arcEndDelta, - {ui.Radius radius = ui.Radius.zero, - double rotation = 0.0, - bool largeArc = false, - bool clockwise = true}) { + void relativeArcToPoint( + ui.Offset arcEndDelta, { + ui.Radius radius = ui.Radius.zero, + double rotation = 0.0, + bool largeArc = false, + bool clockwise = true, + }) { skiaObject.rArcTo( radius.x, radius.y, @@ -241,8 +238,7 @@ class CkPath implements ScenePath { } @override - void relativeCubicTo( - double x1, double y1, double x2, double y2, double x3, double y3) { + void relativeCubicTo(double x1, double y1, double x2, double y2, double x3, double y3) { skiaObject.rCubicTo(x1, y1, x2, y2, x3, y3); } @@ -274,19 +270,11 @@ class CkPath implements ScenePath { // `SkPath.transform` mutates the existing path, so create a copy and call // `transform` on the copy. final SkPath shiftedPath = skiaObject.copy(); - shiftedPath.transform( - 1.0, 0.0, offset.dx, - 0.0, 1.0, offset.dy, - 0.0, 0.0, 1.0, - ); + shiftedPath.transform(1.0, 0.0, offset.dx, 0.0, 1.0, offset.dy, 0.0, 0.0, 1.0); return CkPath.fromSkPath(shiftedPath, _fillType); } - static CkPath combine( - ui.PathOperation operation, - ui.Path uiPath1, - ui.Path uiPath2, - ) { + static CkPath combine(ui.PathOperation operation, ui.Path uiPath1, ui.Path uiPath2) { final CkPath path1 = uiPath1 as CkPath; final CkPath path2 = uiPath2 as CkPath; final SkPath newPath = canvasKit.Path.MakeFromOp( @@ -301,17 +289,7 @@ class CkPath implements ScenePath { ui.Path transform(Float64List matrix4) { final SkPath newPath = skiaObject.copy(); final Float32List m = toSkMatrixFromFloat64(matrix4); - newPath.transform( - m[0], - m[1], - m[2], - m[3], - m[4], - m[5], - m[6], - m[7], - m[8], - ); + newPath.transform(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]); return CkPath.fromSkPath(newPath, _fillType); } diff --git a/lib/web_ui/lib/src/engine/canvaskit/path_metrics.dart b/lib/web_ui/lib/src/engine/canvaskit/path_metrics.dart index 344fabad329ff..72da6df96e05d 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/path_metrics.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/path_metrics.dart @@ -19,18 +19,17 @@ class CkPathMetrics extends IterableBase implements ui.PathMetric /// The [CkPath.isEmpty] case is special-cased to avoid booting the WASM machinery just to find out there are no contours. @override - late final Iterator iterator = _path.isEmpty - ? const CkPathMetricIteratorEmpty._() - : CkContourMeasureIter(this); + late final Iterator iterator = + _path.isEmpty ? const CkPathMetricIteratorEmpty._() : CkContourMeasureIter(this); } class CkContourMeasureIter implements Iterator { CkContourMeasureIter(this._metrics) { - _ref = UniqueRef(this, SkContourMeasureIter( - _metrics._path.skiaObject, - _metrics._forceClosed, - 1.0, - ), 'Iterator'); + _ref = UniqueRef( + this, + SkContourMeasureIter(_metrics._path.skiaObject, _metrics._forceClosed, 1.0), + 'Iterator', + ); } final CkPathMetrics _metrics; @@ -48,9 +47,10 @@ class CkContourMeasureIter implements Iterator { final ui.PathMetric? currentMetric = _current; if (currentMetric == null) { throw RangeError( - 'PathMetricIterator is not pointing to a PathMetric. This can happen in two situations:\n' - '- The iteration has not started yet. If so, call "moveNext" to start iteration.\n' - '- The iterator ran out of elements. If so, check that "moveNext" returns true prior to calling "current".'); + 'PathMetricIterator is not pointing to a PathMetric. This can happen in two situations:\n' + '- The iteration has not started yet. If so, call "moveNext" to start iteration.\n' + '- The iterator ran out of elements. If so, check that "moveNext" returns true prior to calling "current".', + ); } return currentMetric; } @@ -65,8 +65,7 @@ class CkContourMeasureIter implements Iterator { return false; } - _current = - CkContourMeasure(_metrics, skContourMeasure, _contourIndexCounter); + _current = CkContourMeasure(_metrics, skContourMeasure, _contourIndexCounter); _contourIndexCounter += 1; return true; } @@ -98,10 +97,7 @@ class CkContourMeasure implements ui.PathMetric { @override ui.Tangent getTangentForOffset(double distance) { final Float32List posTan = skiaObject.getPosTan(distance); - return ui.Tangent( - ui.Offset(posTan[0], posTan[1]), - ui.Offset(posTan[2], posTan[3]), - ); + return ui.Tangent(ui.Offset(posTan[0], posTan[1]), ui.Offset(posTan[2], posTan[3])); } @override diff --git a/lib/web_ui/lib/src/engine/canvaskit/picture.dart b/lib/web_ui/lib/src/engine/canvaskit/picture.dart index addb864c441ee..5ba5cd911548f 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/picture.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/picture.dart @@ -43,8 +43,7 @@ class CkPicture implements ScenePicture { return result!; } - throw StateError( - 'Picture.debugDisposed is only available when asserts are enabled.'); + throw StateError('Picture.debugDisposed is only available when asserts are enabled.'); } /// This is set to true when [dispose] is called and is never reset back to @@ -101,8 +100,7 @@ class CkPicture implements ScenePicture { assert(debugCheckNotDisposed('Cannot convert picture to image.')); final Surface surface = CanvasKitRenderer.instance.pictureToImageSurface; - final CkSurface ckSurface = - surface.createOrUpdateSurface(BitmapSize(width, height)); + final CkSurface ckSurface = surface.createOrUpdateSurface(BitmapSize(width, height)); final CkCanvas ckCanvas = ckSurface.getCanvas(); ckCanvas.clear(const ui.Color(0x00000000)); ckCanvas.drawPicture(this); @@ -118,8 +116,7 @@ class CkPicture implements ScenePicture { if (pixels == null) { throw StateError('Unable to read pixels from SkImage.'); } - final SkImage? rasterImage = - canvasKit.MakeImage(imageInfo, pixels, (4 * width).toDouble()); + final SkImage? rasterImage = canvasKit.MakeImage(imageInfo, pixels, (4 * width).toDouble()); if (rasterImage == null) { throw StateError('Unable to convert image pixels into SkImage.'); } diff --git a/lib/web_ui/lib/src/engine/canvaskit/raster_cache.dart b/lib/web_ui/lib/src/engine/canvaskit/raster_cache.dart index bfb96257e8e72..192dd42c03de8 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/raster_cache.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/raster_cache.dart @@ -35,12 +35,10 @@ class RasterCache { /// /// The flag [willChange] is a hint to the raster cache that this picture /// will change, and so should be less likely to be cached. - void prepare( - ui.Picture picture, Matrix4 matrix, bool isComplex, bool willChange) {} + void prepare(ui.Picture picture, Matrix4 matrix, bool isComplex, bool willChange) {} /// Gets a raster cache result for the [picture] at transform [matrix]. - RasterCacheResult get(ui.Picture picture, Matrix4 matrix) => - RasterCacheResult(); + RasterCacheResult get(ui.Picture picture, Matrix4 matrix) => RasterCacheResult(); } /// A cache entry for a given picture and matrix. diff --git a/lib/web_ui/lib/src/engine/canvaskit/rasterizer.dart b/lib/web_ui/lib/src/engine/canvaskit/rasterizer.dart index f43bebcbb3b56..89b87b5555ede 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/rasterizer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/rasterizer.dart @@ -63,11 +63,7 @@ abstract class ViewRasterizer { viewEmbedder.frameSize = currentFrameSize; final Frame compositorFrame = context.acquireFrame(viewEmbedder); - compositorFrame.raster( - layerTree, - currentFrameSize, - ignoreRasterCache: true, - ); + compositorFrame.raster(layerTree, currentFrameSize, ignoreRasterCache: true); await viewEmbedder.submitFrame(); } @@ -79,8 +75,7 @@ abstract class ViewRasterizer { void prepareToDraw(); /// Rasterize the [pictures] to the given [canvas]. - Future rasterizeToCanvas( - DisplayCanvas canvas, List pictures); + Future rasterizeToCanvas(DisplayCanvas canvas, List pictures); /// Get a [DisplayCanvas] to use as an overlay. DisplayCanvas getOverlay() { @@ -137,11 +132,8 @@ abstract class DisplayCanvas { /// Encapsulates a request to render a [ui.Scene]. Contains the scene to render /// and a [Completer] which completes when the scene has been rendered. -typedef RenderRequest = ({ - ui.Scene scene, - Completer completer, - FrameTimingRecorder? recorder, -}); +typedef RenderRequest = + ({ui.Scene scene, Completer completer, FrameTimingRecorder? recorder}); /// A per-view queue of render requests. Only contains the current render /// request and the next render request. If a new render request is made before diff --git a/lib/web_ui/lib/src/engine/canvaskit/render_canvas.dart b/lib/web_ui/lib/src/engine/canvaskit/render_canvas.dart index 0c45c36609ebd..3c24811936138 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/render_canvas.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/render_canvas.dart @@ -54,16 +54,14 @@ class RenderCanvas extends DisplayCanvas { late final DomCanvasRenderingContextBitmapRenderer renderContext = canvasElement.contextBitmapRenderer; - late final DomCanvasRenderingContext2D renderContext2d = - canvasElement.context2D; + late final DomCanvasRenderingContext2D renderContext2d = canvasElement.context2D; double _currentDevicePixelRatio = -1; /// Sets the CSS size of the canvas so that canvas pixels are 1:1 with device /// pixels. void _updateLogicalHtmlCanvasSize() { - final double devicePixelRatio = - EngineFlutterDisplay.instance.devicePixelRatio; + final double devicePixelRatio = EngineFlutterDisplay.instance.devicePixelRatio; final double logicalWidth = _pixelWidth / devicePixelRatio; final double logicalHeight = _pixelHeight / devicePixelRatio; final DomCSSStyleDeclaration style = canvasElement.style; @@ -107,8 +105,7 @@ class RenderCanvas extends DisplayCanvas { if (size.width == _pixelWidth && size.height == _pixelHeight) { // The existing canvas doesn't need to be resized (unless the device pixel // ratio changed). - if (EngineFlutterDisplay.instance.devicePixelRatio != - _currentDevicePixelRatio) { + if (EngineFlutterDisplay.instance.devicePixelRatio != _currentDevicePixelRatio) { _updateLogicalHtmlCanvasSize(); } return; diff --git a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart index 6676ab4544693..2e9bedde91063 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/renderer.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/renderer.dart @@ -59,8 +59,7 @@ class CanvasKitRenderer implements Renderer { _rasterizer = testRasterizer; } - set resourceCacheMaxBytes(int bytes) => - _rasterizer.setResourceCacheMaxBytes(bytes); + set resourceCacheMaxBytes(int bytes) => _rasterizer.setResourceCacheMaxBytes(bytes); /// A surface used specifically for `Picture.toImage` when software rendering /// is supported. @@ -78,8 +77,7 @@ class CanvasKitRenderer implements Renderer { canvasKit = windowFlutterCanvasKit!; } else if (windowFlutterCanvasKitLoaded != null) { // CanvasKit is being preloaded by flutter.js. Wait for it to complete. - canvasKit = - await promiseToFuture(windowFlutterCanvasKitLoaded!); + canvasKit = await promiseToFuture(windowFlutterCanvasKitLoaded!); } else { canvasKit = await downloadCanvasKit(); windowFlutterCanvasKit = canvasKit; @@ -87,17 +85,14 @@ class CanvasKitRenderer implements Renderer { // Views may have been registered before this renderer was initialized. // Create rasterizers for them and then start listening for new view // creation/disposal events. - final FlutterViewManager viewManager = - EnginePlatformDispatcher.instance.viewManager; + final FlutterViewManager viewManager = EnginePlatformDispatcher.instance.viewManager; if (_onViewCreatedListener == null) { for (final EngineFlutterView view in viewManager.views) { _onViewCreated(view.viewId); } } - _onViewCreatedListener ??= - viewManager.onViewCreated.listen(_onViewCreated); - _onViewDisposedListener ??= - viewManager.onViewDisposed.listen(_onViewDisposed); + _onViewCreatedListener ??= viewManager.onViewCreated.listen(_onViewCreated); + _onViewDisposedListener ??= viewManager.onViewDisposed.listen(_onViewDisposed); _instance = this; }(); return _initialized; @@ -113,11 +108,13 @@ class CanvasKitRenderer implements Renderer { List? textureCoordinates, List? colors, List? indices, - }) => - CkVertices(mode, positions, - textureCoordinates: textureCoordinates, - colors: colors, - indices: indices); + }) => CkVertices( + mode, + positions, + textureCoordinates: textureCoordinates, + colors: colors, + indices: indices, + ); @override ui.Vertices createVerticesRaw( @@ -126,11 +123,13 @@ class CanvasKitRenderer implements Renderer { Float32List? textureCoordinates, Int32List? colors, Uint16List? indices, - }) => - CkVertices.raw(mode, positions, - textureCoordinates: textureCoordinates, - colors: colors, - indices: indices); + }) => CkVertices.raw( + mode, + positions, + textureCoordinates: textureCoordinates, + colors: colors, + indices: indices, + ); @override ui.Canvas createCanvas(ui.PictureRecorder recorder, [ui.Rect? cullRect]) => @@ -138,11 +137,13 @@ class CanvasKitRenderer implements Renderer { @override ui.Gradient createLinearGradient( - ui.Offset from, ui.Offset to, List colors, - [List? colorStops, - ui.TileMode tileMode = ui.TileMode.clamp, - Float32List? matrix4]) => - CkGradientLinear(from, to, colors, colorStops, tileMode, matrix4); + ui.Offset from, + ui.Offset to, + List colors, [ + List? colorStops, + ui.TileMode tileMode = ui.TileMode.clamp, + Float32List? matrix4, + ]) => CkGradientLinear(from, to, colors, colorStops, tileMode, matrix4); @override ui.Gradient createRadialGradient( @@ -152,27 +153,30 @@ class CanvasKitRenderer implements Renderer { List? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, Float32List? matrix4, - ]) => - CkGradientRadial(center, radius, colors, colorStops, tileMode, matrix4); + ]) => CkGradientRadial(center, radius, colors, colorStops, tileMode, matrix4); @override - ui.Gradient createConicalGradient(ui.Offset focal, double focalRadius, - ui.Offset center, double radius, List colors, - [List? colorStops, - ui.TileMode tileMode = ui.TileMode.clamp, - Float32List? matrix]) => - CkGradientConical(focal, focalRadius, center, radius, colors, colorStops, - tileMode, matrix); + ui.Gradient createConicalGradient( + ui.Offset focal, + double focalRadius, + ui.Offset center, + double radius, + List colors, [ + List? colorStops, + ui.TileMode tileMode = ui.TileMode.clamp, + Float32List? matrix, + ]) => CkGradientConical(focal, focalRadius, center, radius, colors, colorStops, tileMode, matrix); @override - ui.Gradient createSweepGradient(ui.Offset center, List colors, - [List? colorStops, - ui.TileMode tileMode = ui.TileMode.clamp, - double startAngle = 0.0, - double endAngle = math.pi * 2, - Float32List? matrix4]) => - CkGradientSweep( - center, colors, colorStops, tileMode, startAngle, endAngle, matrix4); + ui.Gradient createSweepGradient( + ui.Offset center, + List colors, [ + List? colorStops, + ui.TileMode tileMode = ui.TileMode.clamp, + double startAngle = 0.0, + double endAngle = math.pi * 2, + Float32List? matrix4, + ]) => CkGradientSweep(center, colors, colorStops, tileMode, startAngle, endAngle, matrix4); @override ui.PictureRecorder createPictureRecorder() => CkPictureRecorder(); @@ -181,30 +185,31 @@ class CanvasKitRenderer implements Renderer { ui.SceneBuilder createSceneBuilder() => LayerSceneBuilder(); @override - ui.ImageFilter createBlurImageFilter( - {double sigmaX = 0.0, - double sigmaY = 0.0, - ui.TileMode? tileMode}) => - CkImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode); + ui.ImageFilter createBlurImageFilter({ + double sigmaX = 0.0, + double sigmaY = 0.0, + ui.TileMode? tileMode, + }) => CkImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode); @override - ui.ImageFilter createDilateImageFilter( - {double radiusX = 0.0, double radiusY = 0.0}) => + ui.ImageFilter createDilateImageFilter({double radiusX = 0.0, double radiusY = 0.0}) => CkImageFilter.dilate(radiusX: radiusX, radiusY: radiusY); @override - ui.ImageFilter createErodeImageFilter( - {double radiusX = 0.0, double radiusY = 0.0}) => + ui.ImageFilter createErodeImageFilter({double radiusX = 0.0, double radiusY = 0.0}) => CkImageFilter.erode(radiusX: radiusX, radiusY: radiusY); @override - ui.ImageFilter createMatrixImageFilter(Float64List matrix4, - {ui.FilterQuality filterQuality = ui.FilterQuality.low}) => - CkImageFilter.matrix(matrix: matrix4, filterQuality: filterQuality); + ui.ImageFilter createMatrixImageFilter( + Float64List matrix4, { + ui.FilterQuality filterQuality = ui.FilterQuality.low, + }) => CkImageFilter.matrix(matrix: matrix4, filterQuality: filterQuality); @override - ui.ImageFilter composeImageFilters( - {required ui.ImageFilter outer, required ui.ImageFilter inner}) { + ui.ImageFilter composeImageFilters({ + required ui.ImageFilter outer, + required ui.ImageFilter inner, + }) { if (outer is EngineColorFilter) { final CkColorFilter colorFilter = createCkColorFilter(outer)!; outer = CkColorFilterImageFilter(colorFilter: colorFilter); @@ -213,27 +218,26 @@ class CanvasKitRenderer implements Renderer { final CkColorFilter colorFilter = createCkColorFilter(inner)!; inner = CkColorFilterImageFilter(colorFilter: colorFilter); } - return CkImageFilter.compose( - outer: outer as CkImageFilter, inner: inner as CkImageFilter); + return CkImageFilter.compose(outer: outer as CkImageFilter, inner: inner as CkImageFilter); } @override - Future instantiateImageCodec(Uint8List list, - {int? targetWidth, - int? targetHeight, - bool allowUpscaling = true}) async => - skiaInstantiateImageCodec( - list, targetWidth, targetHeight, allowUpscaling); + Future instantiateImageCodec( + Uint8List list, { + int? targetWidth, + int? targetHeight, + bool allowUpscaling = true, + }) async => skiaInstantiateImageCodec(list, targetWidth, targetHeight, allowUpscaling); @override - Future instantiateImageCodecFromUrl(Uri uri, - {ui_web.ImageCodecChunkCallback? chunkCallback}) => - skiaInstantiateWebImageCodec(uri.toString(), chunkCallback); + Future instantiateImageCodecFromUrl( + Uri uri, { + ui_web.ImageCodecChunkCallback? chunkCallback, + }) => skiaInstantiateWebImageCodec(uri.toString(), chunkCallback); @override ui.Image createImageFromImageBitmap(DomImageBitmap imageBitmap) { - final SkImage? skImage = - canvasKit.MakeLazyImageFromImageBitmap(imageBitmap, true); + final SkImage? skImage = canvasKit.MakeLazyImageFromImageBitmap(imageBitmap, true); if (skImage == null) { throw Exception('Failed to convert image bitmap to an SkImage.'); } @@ -241,21 +245,31 @@ class CanvasKitRenderer implements Renderer { } @override - FutureOr createImageFromTextureSource(JSAny object, - {required int width, required int height, required bool transferOwnership}) async { - if (!transferOwnership) { - final DomImageBitmap bitmap = await createImageBitmap(object, (x:0, y: 0, width: width, height: height)); - return createImageFromImageBitmap(bitmap); - } + FutureOr createImageFromTextureSource( + JSAny object, { + required int width, + required int height, + required bool transferOwnership, + }) async { + if (!transferOwnership) { + final DomImageBitmap bitmap = await createImageBitmap(object, ( + x: 0, + y: 0, + width: width, + height: height, + )); + return createImageFromImageBitmap(bitmap); + } final SkImage? skImage = canvasKit.MakeLazyImageFromTextureSourceWithInfo( - object, - SkPartialImageInfo( - width: width.toDouble(), - height: height.toDouble(), - alphaType: canvasKit.AlphaType.Premul, - colorType: canvasKit.ColorType.RGBA_8888, - colorSpace: SkColorSpaceSRGB, - )); + object, + SkPartialImageInfo( + width: width.toDouble(), + height: height.toDouble(), + alphaType: canvasKit.AlphaType.Premul, + colorType: canvasKit.ColorType.RGBA_8888, + colorSpace: SkColorSpaceSRGB, + ), + ); if (skImage == null) { throw Exception('Failed to convert image bitmap to an SkImage.'); @@ -264,26 +278,36 @@ class CanvasKitRenderer implements Renderer { } @override - void decodeImageFromPixels(Uint8List pixels, int width, int height, - ui.PixelFormat format, ui.ImageDecoderCallback callback, - {int? rowBytes, - int? targetWidth, - int? targetHeight, - bool allowUpscaling = true}) => - skiaDecodeImageFromPixels(pixels, width, height, format, callback, - rowBytes: rowBytes, - targetWidth: targetWidth, - targetHeight: targetHeight, - allowUpscaling: allowUpscaling); + void decodeImageFromPixels( + Uint8List pixels, + int width, + int height, + ui.PixelFormat format, + ui.ImageDecoderCallback callback, { + int? rowBytes, + int? targetWidth, + int? targetHeight, + bool allowUpscaling = true, + }) => skiaDecodeImageFromPixels( + pixels, + width, + height, + format, + callback, + rowBytes: rowBytes, + targetWidth: targetWidth, + targetHeight: targetHeight, + allowUpscaling: allowUpscaling, + ); @override ui.ImageShader createImageShader( - ui.Image image, - ui.TileMode tmx, - ui.TileMode tmy, - Float64List matrix4, - ui.FilterQuality? filterQuality) => - CkImageShader(image, tmx, tmy, matrix4, filterQuality); + ui.Image image, + ui.TileMode tmx, + ui.TileMode tmy, + Float64List matrix4, + ui.FilterQuality? filterQuality, + ) => CkImageShader(image, tmx, tmy, matrix4, filterQuality); @override ui.Path createPath() => CkPath(); @@ -296,114 +320,115 @@ class CanvasKitRenderer implements Renderer { CkPath.combine(op, path1, path2); @override - ui.TextStyle createTextStyle( - {ui.Color? color, - ui.TextDecoration? decoration, - ui.Color? decorationColor, - ui.TextDecorationStyle? decorationStyle, - double? decorationThickness, - ui.FontWeight? fontWeight, - ui.FontStyle? fontStyle, - ui.TextBaseline? textBaseline, - String? fontFamily, - List? fontFamilyFallback, - double? fontSize, - double? letterSpacing, - double? wordSpacing, - double? height, - ui.TextLeadingDistribution? leadingDistribution, - ui.Locale? locale, - ui.Paint? background, - ui.Paint? foreground, - List? shadows, - List? fontFeatures, - List? fontVariations}) => - CkTextStyle( - color: color, - decoration: decoration, - decorationColor: decorationColor, - decorationStyle: decorationStyle, - decorationThickness: decorationThickness, - fontWeight: fontWeight, - fontStyle: fontStyle, - textBaseline: textBaseline, - fontFamily: fontFamily, - fontFamilyFallback: fontFamilyFallback, - fontSize: fontSize, - letterSpacing: letterSpacing, - wordSpacing: wordSpacing, - height: height, - leadingDistribution: leadingDistribution, - locale: locale, - background: background as CkPaint?, - foreground: foreground as CkPaint?, - shadows: shadows, - fontFeatures: fontFeatures, - fontVariations: fontVariations, - ); - - @override - ui.ParagraphStyle createParagraphStyle( - {ui.TextAlign? textAlign, - ui.TextDirection? textDirection, - int? maxLines, - String? fontFamily, - double? fontSize, - double? height, - ui.TextHeightBehavior? textHeightBehavior, - ui.FontWeight? fontWeight, - ui.FontStyle? fontStyle, - ui.StrutStyle? strutStyle, - String? ellipsis, - ui.Locale? locale}) => - CkParagraphStyle( - textAlign: textAlign, - textDirection: textDirection, - maxLines: maxLines, - fontFamily: fontFamily, - fontSize: fontSize, - height: height, - textHeightBehavior: textHeightBehavior, - fontWeight: fontWeight, - fontStyle: fontStyle, - strutStyle: strutStyle, - ellipsis: ellipsis, - locale: locale, - ); - - @override - ui.StrutStyle createStrutStyle( - {String? fontFamily, - List? fontFamilyFallback, - double? fontSize, - double? height, - ui.TextLeadingDistribution? leadingDistribution, - double? leading, - ui.FontWeight? fontWeight, - ui.FontStyle? fontStyle, - bool? forceStrutHeight}) => - CkStrutStyle( - fontFamily: fontFamily, - fontFamilyFallback: fontFamilyFallback, - fontSize: fontSize, - height: height, - leadingDistribution: leadingDistribution, - leading: leading, - fontWeight: fontWeight, - fontStyle: fontStyle, - forceStrutHeight: forceStrutHeight, - ); - - @override - ui.ParagraphBuilder createParagraphBuilder(ui.ParagraphStyle style) => - CkParagraphBuilder(style); + ui.TextStyle createTextStyle({ + ui.Color? color, + ui.TextDecoration? decoration, + ui.Color? decorationColor, + ui.TextDecorationStyle? decorationStyle, + double? decorationThickness, + ui.FontWeight? fontWeight, + ui.FontStyle? fontStyle, + ui.TextBaseline? textBaseline, + String? fontFamily, + List? fontFamilyFallback, + double? fontSize, + double? letterSpacing, + double? wordSpacing, + double? height, + ui.TextLeadingDistribution? leadingDistribution, + ui.Locale? locale, + ui.Paint? background, + ui.Paint? foreground, + List? shadows, + List? fontFeatures, + List? fontVariations, + }) => CkTextStyle( + color: color, + decoration: decoration, + decorationColor: decorationColor, + decorationStyle: decorationStyle, + decorationThickness: decorationThickness, + fontWeight: fontWeight, + fontStyle: fontStyle, + textBaseline: textBaseline, + fontFamily: fontFamily, + fontFamilyFallback: fontFamilyFallback, + fontSize: fontSize, + letterSpacing: letterSpacing, + wordSpacing: wordSpacing, + height: height, + leadingDistribution: leadingDistribution, + locale: locale, + background: background as CkPaint?, + foreground: foreground as CkPaint?, + shadows: shadows, + fontFeatures: fontFeatures, + fontVariations: fontVariations, + ); + + @override + ui.ParagraphStyle createParagraphStyle({ + ui.TextAlign? textAlign, + ui.TextDirection? textDirection, + int? maxLines, + String? fontFamily, + double? fontSize, + double? height, + ui.TextHeightBehavior? textHeightBehavior, + ui.FontWeight? fontWeight, + ui.FontStyle? fontStyle, + ui.StrutStyle? strutStyle, + String? ellipsis, + ui.Locale? locale, + }) => CkParagraphStyle( + textAlign: textAlign, + textDirection: textDirection, + maxLines: maxLines, + fontFamily: fontFamily, + fontSize: fontSize, + height: height, + textHeightBehavior: textHeightBehavior, + fontWeight: fontWeight, + fontStyle: fontStyle, + strutStyle: strutStyle, + ellipsis: ellipsis, + locale: locale, + ); + + @override + ui.StrutStyle createStrutStyle({ + String? fontFamily, + List? fontFamilyFallback, + double? fontSize, + double? height, + ui.TextLeadingDistribution? leadingDistribution, + double? leading, + ui.FontWeight? fontWeight, + ui.FontStyle? fontStyle, + bool? forceStrutHeight, + }) => CkStrutStyle( + fontFamily: fontFamily, + fontFamilyFallback: fontFamilyFallback, + fontSize: fontSize, + height: height, + leadingDistribution: leadingDistribution, + leading: leading, + fontWeight: fontWeight, + fontStyle: fontStyle, + forceStrutHeight: forceStrutHeight, + ); + + @override + ui.ParagraphBuilder createParagraphBuilder(ui.ParagraphStyle style) => CkParagraphBuilder(style); // TODO(harryterkelsen): Merge this logic with the async logic in // [EngineScene], https://github.com/flutter/flutter/issues/142072. @override Future renderScene(ui.Scene scene, EngineFlutterView view) async { - assert(_rasterizers.containsKey(view.viewId), - "Unable to render to a view which hasn't been registered"); + assert( + _rasterizers.containsKey(view.viewId), + "Unable to render to a view which hasn't been registered", + ); final ViewRasterizer rasterizer = _rasterizers[view.viewId]!; final RenderQueue renderQueue = rasterizer.queue; final FrameTimingRecorder? recorder = @@ -413,13 +438,11 @@ class CanvasKitRenderer implements Renderer { // so that the scene view always displays the most recently requested scene. renderQueue.next?.completer.complete(); final Completer completer = Completer(); - renderQueue.next = - (scene: scene, completer: completer, recorder: recorder); + renderQueue.next = (scene: scene, completer: completer, recorder: recorder); return completer.future; } final Completer completer = Completer(); - renderQueue.current = - (scene: scene, completer: completer, recorder: recorder); + renderQueue.current = (scene: scene, completer: completer, recorder: recorder); unawaited(_kickRenderLoop(rasterizer)); return completer.future; } @@ -442,8 +465,11 @@ class CanvasKitRenderer implements Renderer { } } - Future _renderScene(ui.Scene scene, ViewRasterizer rasterizer, - FrameTimingRecorder? recorder) async { + Future _renderScene( + ui.Scene scene, + ViewRasterizer rasterizer, + FrameTimingRecorder? recorder, + ) async { // "Build finish" and "raster start" happen back-to-back because we // render on the same thread, so there's no overhead from hopping to // another thread. @@ -463,8 +489,7 @@ class CanvasKitRenderer implements Renderer { final Map _rasterizers = {}; void _onViewCreated(int viewId) { - final EngineFlutterView view = - EnginePlatformDispatcher.instance.viewManager[viewId]!; + final EngineFlutterView view = EnginePlatformDispatcher.instance.viewManager[viewId]!; _rasterizers[view.viewId] = _rasterizer.createViewRasterizer(view); } @@ -511,31 +536,31 @@ class CanvasKitRenderer implements Renderer { if (_programs.containsKey(assetKey)) { return _programs[assetKey]!; } - return _programs[assetKey] = - ui_web.assetManager.load(assetKey).then((ByteData data) { + return _programs[assetKey] = ui_web.assetManager.load(assetKey).then((ByteData data) { return CkFragmentProgram.fromBytes(assetKey, data.buffer.asUint8List()); }); } @override - ui.LineMetrics createLineMetrics( - {required bool hardBreak, - required double ascent, - required double descent, - required double unscaledAscent, - required double height, - required double width, - required double left, - required double baseline, - required int lineNumber}) => - EngineLineMetrics( - hardBreak: hardBreak, - ascent: ascent, - descent: descent, - unscaledAscent: unscaledAscent, - height: height, - width: width, - left: left, - baseline: baseline, - lineNumber: lineNumber); + ui.LineMetrics createLineMetrics({ + required bool hardBreak, + required double ascent, + required double descent, + required double unscaledAscent, + required double height, + required double width, + required double left, + required double baseline, + required int lineNumber, + }) => EngineLineMetrics( + hardBreak: hardBreak, + ascent: ascent, + descent: descent, + unscaledAscent: unscaledAscent, + height: height, + width: width, + left: left, + baseline: baseline, + lineNumber: lineNumber, + ); } diff --git a/lib/web_ui/lib/src/engine/canvaskit/shader.dart b/lib/web_ui/lib/src/engine/canvaskit/shader.dart index 5c83adc4e32b7..fd3da770416b1 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/shader.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/shader.dart @@ -60,11 +60,17 @@ abstract class SimpleCkShader implements CkShader { } class CkGradientSweep extends SimpleCkShader implements ui.Gradient { - CkGradientSweep(this.center, this.colors, this.colorStops, this.tileMode, - this.startAngle, this.endAngle, this.matrix4) - : assert(offsetIsValid(center)), - assert(startAngle < endAngle), - assert(matrix4 == null || matrix4IsValid(matrix4)) { + CkGradientSweep( + this.center, + this.colors, + this.colorStops, + this.tileMode, + this.startAngle, + this.endAngle, + this.matrix4, + ) : assert(offsetIsValid(center)), + assert(startAngle < endAngle), + assert(matrix4 == null || matrix4IsValid(matrix4)) { validateColorStops(colors, colorStops); } @@ -104,9 +110,9 @@ class CkGradientLinear extends SimpleCkShader implements ui.Gradient { this.colorStops, this.tileMode, Float32List? matrix, - ) : assert(offsetIsValid(from)), - assert(offsetIsValid(to)), - matrix4 = matrix { + ) : assert(offsetIsValid(from)), + assert(offsetIsValid(to)), + matrix4 = matrix { assert(matrix4 == null || matrix4IsValid(matrix4!)); // ignore: prefer_asserts_in_initializer_lists assert(() { @@ -142,8 +148,14 @@ class CkGradientLinear extends SimpleCkShader implements ui.Gradient { } class CkGradientRadial extends SimpleCkShader implements ui.Gradient { - CkGradientRadial(this.center, this.radius, this.colors, this.colorStops, - this.tileMode, this.matrix4); + CkGradientRadial( + this.center, + this.radius, + this.colors, + this.colorStops, + this.tileMode, + this.matrix4, + ); final ui.Offset center; final double radius; @@ -173,8 +185,16 @@ class CkGradientRadial extends SimpleCkShader implements ui.Gradient { } class CkGradientConical extends SimpleCkShader implements ui.Gradient { - CkGradientConical(this.focal, this.focalRadius, this.center, this.radius, - this.colors, this.colorStops, this.tileMode, this.matrix4); + CkGradientConical( + this.focal, + this.focalRadius, + this.center, + this.radius, + this.colors, + this.colorStops, + this.tileMode, + this.matrix4, + ); final ui.Offset focal; final double focalRadius; @@ -216,9 +236,8 @@ class CkGradientConical extends SimpleCkShader implements ui.Gradient { /// generated depending on the _usage_ of this object in [ui.Paint] and other /// scenarios that want a shader at different filter quality levels. class CkImageShader implements ui.ImageShader, CkShader { - CkImageShader(ui.Image image, this.tileModeX, this.tileModeY, this.matrix4, - this.filterQuality) - : _image = image as CkImage { + CkImageShader(ui.Image image, this.tileModeX, this.tileModeY, this.matrix4, this.filterQuality) + : _image = image as CkImage { _initializeSkImageShader(filterQuality ?? ui.FilterQuality.none); } diff --git a/lib/web_ui/lib/src/engine/canvaskit/surface.dart b/lib/web_ui/lib/src/engine/canvaskit/surface.dart index 169fc4cc14369..69041aee75be0 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/surface.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/surface.dart @@ -51,8 +51,7 @@ class SurfaceFrame { /// created. class Surface extends DisplayCanvas { Surface({this.isDisplayCanvas = false}) - : useOffscreenCanvas = - Surface.offscreenCanvasSupported && !isDisplayCanvas; + : useOffscreenCanvas = Surface.offscreenCanvasSupported && !isDisplayCanvas; CkSurface? _surface; @@ -136,8 +135,11 @@ class Surface extends DisplayCanvas { _surface!.flush(); } - Future rasterizeToCanvas(BitmapSize bitmapSize, RenderCanvas canvas, - List pictures) async { + Future rasterizeToCanvas( + BitmapSize bitmapSize, + RenderCanvas canvas, + List pictures, + ) async { final CkCanvas skCanvas = getCanvas(); skCanvas.clear(const ui.Color(0x00000000)); pictures.forEach(skCanvas.drawPicture); @@ -177,8 +179,7 @@ class Surface extends DisplayCanvas { final CkSurface surface = createOrUpdateSurface(BitmapSize.fromSize(size)); // ignore: prefer_function_declarations_over_variables - final SubmitCallback submitCallback = - (SurfaceFrame surfaceFrame, CkCanvas canvas) { + final SubmitCallback submitCallback = (SurfaceFrame surfaceFrame, CkCanvas canvas) { return _presentSurface(); }; @@ -190,8 +191,7 @@ class Surface extends DisplayCanvas { /// Sets the CSS size of the canvas so that canvas pixels are 1:1 with device /// pixels. void _updateLogicalHtmlCanvasSize() { - final double devicePixelRatio = - EngineFlutterDisplay.instance.devicePixelRatio; + final double devicePixelRatio = EngineFlutterDisplay.instance.devicePixelRatio; final double logicalWidth = _pixelWidth / devicePixelRatio; final double logicalHeight = _pixelHeight / devicePixelRatio; final DomCSSStyleDeclaration style = _canvasElement!.style; @@ -206,16 +206,13 @@ class Surface extends DisplayCanvas { /// the top left of the window. We need to shift the canvas down so that the /// bottom left of the is the the bottom left corner of the window. void positionToShowFrame(BitmapSize frameSize) { - assert(isDisplayCanvas, - 'Should not position Surface if not used as a render canvas'); - final double devicePixelRatio = - EngineFlutterDisplay.instance.devicePixelRatio; + assert(isDisplayCanvas, 'Should not position Surface if not used as a render canvas'); + final double devicePixelRatio = EngineFlutterDisplay.instance.devicePixelRatio; final double logicalHeight = _pixelHeight / devicePixelRatio; final double logicalFrameHeight = frameSize.height / devicePixelRatio; // Shift the canvas up so the bottom left is in the window. - _canvasElement!.style.transform = - 'translate(0px, ${logicalFrameHeight - logicalHeight}px)'; + _canvasElement!.style.transform = 'translate(0px, ${logicalFrameHeight - logicalHeight}px)'; } /// This is only valid after the first frame or if [ensureSurface] has been @@ -257,8 +254,7 @@ class Surface extends DisplayCanvas { if (previousSurfaceSize != null && size.width == previousSurfaceSize.width && size.height == previousSurfaceSize.height) { - final double devicePixelRatio = - EngineFlutterDisplay.instance.devicePixelRatio; + final double devicePixelRatio = EngineFlutterDisplay.instance.devicePixelRatio; if (isDisplayCanvas && devicePixelRatio != _currentDevicePixelRatio) { _updateLogicalHtmlCanvasSize(); } @@ -269,8 +265,7 @@ class Surface extends DisplayCanvas { // Initialize a new, larger, canvas. If the size is growing, then make the // new canvas larger than required to avoid many canvas creations. if (previousCanvasSize != null && - (size.width > previousCanvasSize.width || - size.height > previousCanvasSize.height)) { + (size.width > previousCanvasSize.width || size.height > previousCanvasSize.height)) { final BitmapSize newSize = BitmapSize.fromSize(size.toSize() * 1.4); _surface?.dispose(); _surface = null; @@ -308,9 +303,10 @@ class Surface extends DisplayCanvas { JSVoid _contextRestoredListener(DomEvent event) { assert( - _contextLost, - 'Received "webglcontextrestored" event but never received ' - 'a "webglcontextlost" event.'); + _contextLost, + 'Received "webglcontextrestored" event but never received ' + 'a "webglcontextlost" event.', + ); _contextLost = false; // Force the framework to rerender the frame. EnginePlatformDispatcher.instance.invokeOnMetricsChanged(); @@ -319,8 +315,10 @@ class Surface extends DisplayCanvas { } JSVoid _contextLostListener(DomEvent event) { - assert(event.target == _offscreenCanvas || event.target == _canvasElement, - 'Received a context lost event for a disposed canvas'); + assert( + event.target == _offscreenCanvas || event.target == _canvasElement, + 'Received a context lost event for a disposed canvas', + ); _contextLost = true; _forceNewContext = true; event.preventDefault(); @@ -337,11 +335,7 @@ class Surface extends DisplayCanvas { _cachedContextRestoredListener, false, ); - _offscreenCanvas!.removeEventListener( - 'webglcontextlost', - _cachedContextLostListener, - false, - ); + _offscreenCanvas!.removeEventListener('webglcontextlost', _cachedContextLostListener, false); _offscreenCanvas = null; _cachedContextRestoredListener = null; _cachedContextLostListener = null; @@ -351,11 +345,7 @@ class Surface extends DisplayCanvas { _cachedContextRestoredListener, false, ); - _canvasElement!.removeEventListener( - 'webglcontextlost', - _cachedContextLostListener, - false, - ); + _canvasElement!.removeEventListener('webglcontextlost', _cachedContextLostListener, false); _canvasElement!.remove(); _canvasElement = null; _cachedContextRestoredListener = null; @@ -376,8 +366,10 @@ class Surface extends DisplayCanvas { _offscreenCanvas = offscreenCanvas; _canvasElement = null; } else { - final DomCanvasElement canvas = - createDomCanvasElement(width: _pixelWidth, height: _pixelHeight); + final DomCanvasElement canvas = createDomCanvasElement( + width: _pixelWidth, + height: _pixelHeight, + ); htmlCanvas = canvas; _canvasElement = canvas; _offscreenCanvas = null; @@ -395,19 +387,10 @@ class Surface extends DisplayCanvas { // notification. When we receive this notification we force a new context. // // See also: https://www.khronos.org/webgl/wiki/HandlingContextLost - _cachedContextRestoredListener = - createDomEventListener(_contextRestoredListener); + _cachedContextRestoredListener = createDomEventListener(_contextRestoredListener); _cachedContextLostListener = createDomEventListener(_contextLostListener); - htmlCanvas.addEventListener( - 'webglcontextlost', - _cachedContextLostListener, - false, - ); - htmlCanvas.addEventListener( - 'webglcontextrestored', - _cachedContextRestoredListener, - false, - ); + htmlCanvas.addEventListener('webglcontextlost', _cachedContextLostListener, false); + htmlCanvas.addEventListener('webglcontextrestored', _cachedContextRestoredListener, false); _forceNewContext = false; _contextLost = false; @@ -420,15 +403,9 @@ class Surface extends DisplayCanvas { majorVersion: webGLVersion.toDouble(), ); if (useOffscreenCanvas) { - glContext = canvasKit.GetOffscreenWebGLContext( - _offscreenCanvas!, - options, - ).toInt(); + glContext = canvasKit.GetOffscreenWebGLContext(_offscreenCanvas!, options).toInt(); } else { - glContext = canvasKit.GetWebGLContext( - _canvasElement!, - options, - ).toInt(); + glContext = canvasKit.GetWebGLContext(_canvasElement!, options).toInt(); } _glContext = glContext; @@ -436,8 +413,10 @@ class Surface extends DisplayCanvas { if (_glContext != 0) { _grContext = canvasKit.MakeGrContext(glContext.toDouble()); if (_grContext == null) { - throw CanvasKitError('Failed to initialize CanvasKit. ' - 'CanvasKit.MakeGrContext returned null.'); + throw CanvasKitError( + 'Failed to initialize CanvasKit. ' + 'CanvasKit.MakeGrContext returned null.', + ); } if (_sampleCount == -1 || _stencilBits == -1) { _initWebglParams(); @@ -465,29 +444,21 @@ class Surface extends DisplayCanvas { if (webGLVersion == -1) { return _makeSoftwareCanvasSurface('WebGL support not detected', size); } else if (configuration.canvasKitForceCpuOnly) { - return _makeSoftwareCanvasSurface( - 'CPU rendering forced by application', - size, - ); + return _makeSoftwareCanvasSurface('CPU rendering forced by application', size); } else if (_glContext == 0) { - return _makeSoftwareCanvasSurface( - 'Failed to initialize WebGL context', - size, - ); + return _makeSoftwareCanvasSurface('Failed to initialize WebGL context', size); } else { final SkSurface? skSurface = canvasKit.MakeOnScreenGLSurface( - _grContext!, - size.width.toDouble(), - size.height.toDouble(), - SkColorSpaceSRGB, - _sampleCount, - _stencilBits); + _grContext!, + size.width.toDouble(), + size.height.toDouble(), + SkColorSpaceSRGB, + _sampleCount, + _stencilBits, + ); if (skSurface == null) { - return _makeSoftwareCanvasSurface( - 'Failed to initialize WebGL surface', - size, - ); + return _makeSoftwareCanvasSurface('Failed to initialize WebGL surface', size); } return CkSurface(skSurface, _glContext, size); @@ -532,10 +503,12 @@ class Surface extends DisplayCanvas { @override void dispose() { + _offscreenCanvas?.removeEventListener('webglcontextlost', _cachedContextLostListener, false); _offscreenCanvas?.removeEventListener( - 'webglcontextlost', _cachedContextLostListener, false); - _offscreenCanvas?.removeEventListener( - 'webglcontextrestored', _cachedContextRestoredListener, false); + 'webglcontextrestored', + _cachedContextRestoredListener, + false, + ); _cachedContextLostListener = null; _cachedContextRestoredListener = null; _surface?.dispose(); @@ -543,8 +516,7 @@ class Surface extends DisplayCanvas { /// Safari 15 doesn't support OffscreenCanvas at all. Safari 16 supports /// OffscreenCanvas, but only with the context2d API, not WebGL. - static bool get offscreenCanvasSupported => - browserSupportsOffscreenCanvas && !isSafari; + static bool get offscreenCanvasSupported => browserSupportsOffscreenCanvas && !isSafari; } /// A Dart wrapper around Skia's SkSurface. diff --git a/lib/web_ui/lib/src/engine/canvaskit/text.dart b/lib/web_ui/lib/src/engine/canvaskit/text.dart index e012f6eebe5ef..3514ec01a5a8b 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/text.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/text.dart @@ -14,8 +14,8 @@ final bool _ckRequiresClientICU = canvasKit.ParagraphBuilder.RequiresClientICU() final List _testFonts = ['FlutterTest', 'Ahem']; String? _computeEffectiveFontFamily(String? fontFamily) { return ui_web.debugEmulateFlutterTesterEnvironment && !_testFonts.contains(fontFamily) - ? _testFonts.first - : fontFamily; + ? _testFonts.first + : fontFamily; } @immutable @@ -33,34 +33,33 @@ class CkParagraphStyle implements ui.ParagraphStyle { ui.StrutStyle? strutStyle, String? ellipsis, ui.Locale? locale, - }) : skParagraphStyle = toSkParagraphStyle( - textAlign, - textDirection, - maxLines, - _computeEffectiveFontFamily(fontFamily), - fontSize, - height == ui.kTextHeightNone ? null : height, - textHeightBehavior, - fontWeight, - fontStyle, - strutStyle, - ellipsis, - locale, - ), - _textAlign = textAlign, - _textDirection = textDirection, - _fontWeight = fontWeight, - _fontStyle = fontStyle, - _maxLines = maxLines, - _originalFontFamily = fontFamily, - _effectiveFontFamily = _computeEffectiveFontFamily(fontFamily), - _fontSize = fontSize, - _height = height == ui.kTextHeightNone ? null : height, - _textHeightBehavior = textHeightBehavior, - _strutStyle = strutStyle, - _ellipsis = ellipsis, - _locale = locale; - + }) : skParagraphStyle = toSkParagraphStyle( + textAlign, + textDirection, + maxLines, + _computeEffectiveFontFamily(fontFamily), + fontSize, + height == ui.kTextHeightNone ? null : height, + textHeightBehavior, + fontWeight, + fontStyle, + strutStyle, + ellipsis, + locale, + ), + _textAlign = textAlign, + _textDirection = textDirection, + _fontWeight = fontWeight, + _fontStyle = fontStyle, + _maxLines = maxLines, + _originalFontFamily = fontFamily, + _effectiveFontFamily = _computeEffectiveFontFamily(fontFamily), + _fontSize = fontSize, + _height = height == ui.kTextHeightNone ? null : height, + _textHeightBehavior = textHeightBehavior, + _strutStyle = strutStyle, + _ellipsis = ellipsis, + _locale = locale; final SkParagraphStyle skParagraphStyle; @@ -104,11 +103,15 @@ class CkParagraphStyle implements ui.ParagraphStyle { } static SkStrutStyleProperties toSkStrutStyleProperties( - ui.StrutStyle value, ui.TextHeightBehavior? paragraphHeightBehavior) { + ui.StrutStyle value, + ui.TextHeightBehavior? paragraphHeightBehavior, + ) { final CkStrutStyle style = value as CkStrutStyle; final SkStrutStyleProperties skStrutStyle = SkStrutStyleProperties(); - skStrutStyle.fontFamilies = - _computeCombinedFontFamilies(style._fontFamily, style._fontFamilyFallback); + skStrutStyle.fontFamilies = _computeCombinedFontFamilies( + style._fontFamily, + style._fontFamilyFallback, + ); if (style._fontSize != null) { skStrutStyle.fontSize = style._fontSize; @@ -119,8 +122,7 @@ class CkParagraphStyle implements ui.ParagraphStyle { } final ui.TextLeadingDistribution? effectiveLeadingDistribution = - style._leadingDistribution ?? - paragraphHeightBehavior?.leadingDistribution; + style._leadingDistribution ?? paragraphHeightBehavior?.leadingDistribution; switch (effectiveLeadingDistribution) { case null: break; @@ -135,8 +137,7 @@ class CkParagraphStyle implements ui.ParagraphStyle { } if (style._fontWeight != null || style._fontStyle != null) { - skStrutStyle.fontStyle = - toSkFontStyle(style._fontWeight, style._fontStyle); + skStrutStyle.fontStyle = toSkFontStyle(style._fontWeight, style._fontStyle); } if (style._forceStrutHeight != null) { @@ -181,8 +182,7 @@ class CkParagraphStyle implements ui.ParagraphStyle { } if (textHeightBehavior != null) { - properties.textHeightBehavior = - toSkTextHeightBehavior(textHeightBehavior); + properties.textHeightBehavior = toSkTextHeightBehavior(textHeightBehavior); } if (ellipsis != null) { @@ -190,13 +190,17 @@ class CkParagraphStyle implements ui.ParagraphStyle { } if (strutStyle != null) { - properties.strutStyle = - toSkStrutStyleProperties(strutStyle, textHeightBehavior); + properties.strutStyle = toSkStrutStyleProperties(strutStyle, textHeightBehavior); } properties.replaceTabCharacters = true; properties.textStyle = toSkTextStyleProperties( - fontFamily, fontSize, height, fontWeight, fontStyle); + fontFamily, + fontSize, + height, + fontWeight, + fontStyle, + ); properties.applyRoundingHack = false; return canvasKit.ParagraphStyle(properties); @@ -281,7 +285,8 @@ class CkParagraphStyle implements ui.ParagraphStyle { assert(() { final double? fontSize = _fontSize; final double? height = _height; - result = 'ParagraphStyle(' + result = + 'ParagraphStyle(' 'textAlign: ${_textAlign ?? "unspecified"}, ' 'textDirection: ${_textDirection ?? "unspecified"}, ' 'fontWeight: ${_fontWeight ?? "unspecified"}, ' @@ -343,7 +348,8 @@ class CkTextStyle implements ui.TextStyle { originalFontFamily: fontFamily, effectiveFontFamily: _computeEffectiveFontFamily(fontFamily), originalFontFamilyFallback: fontFamilyFallback, - effectiveFontFamilyFallback: ui_web.debugEmulateFlutterTesterEnvironment ? null : fontFamilyFallback, + effectiveFontFamilyFallback: + ui_web.debugEmulateFlutterTesterEnvironment ? null : fontFamilyFallback, fontSize: fontSize, letterSpacing: letterSpacing, wordSpacing: wordSpacing, @@ -413,9 +419,7 @@ class CkTextStyle implements ui.TextStyle { /// The values in this text style are used unless [other] specifically /// overrides it. CkTextStyle mergeWith(CkTextStyle other) { - final double? textHeight = other.height == ui.kTextHeightNone - ? null - : (other.height ?? height); + final double? textHeight = other.height == ui.kTextHeightNone ? null : (other.height ?? height); return CkTextStyle._( color: other.color ?? color, decoration: other.decoration ?? decoration, @@ -444,8 +448,10 @@ class CkTextStyle implements ui.TextStyle { } /// Lazy-initialized combination of font family and font family fallback sent to Skia. - late final List combinedFontFamilies = - _computeCombinedFontFamilies(effectiveFontFamily, effectiveFontFamilyFallback); + late final List combinedFontFamilies = _computeCombinedFontFamilies( + effectiveFontFamily, + effectiveFontFamilyFallback, + ); /// Lazy-initialized Skia style used to pass the style to Skia. /// @@ -593,28 +599,28 @@ class CkTextStyle implements ui.TextStyle { if (identical(this, other)) { return true; } - return other is CkTextStyle - && other.color == color - && other.decoration == decoration - && other.decorationColor == decorationColor - && other.decorationStyle == decorationStyle - && other.fontWeight == fontWeight - && other.fontStyle == fontStyle - && other.textBaseline == textBaseline - && other.leadingDistribution == leadingDistribution - && other.originalFontFamily == originalFontFamily - && other.fontSize == fontSize - && other.letterSpacing == letterSpacing - && other.wordSpacing == wordSpacing - && other.height == height - && other.decorationThickness == decorationThickness - && other.locale == locale - && other.background == background - && other.foreground == foreground - && listEquals(other.shadows, shadows) - && listEquals(other.originalFontFamilyFallback, originalFontFamilyFallback) - && listEquals(other.fontFeatures, fontFeatures) - && listEquals(other.fontVariations, fontVariations); + return other is CkTextStyle && + other.color == color && + other.decoration == decoration && + other.decorationColor == decorationColor && + other.decorationStyle == decorationStyle && + other.fontWeight == fontWeight && + other.fontStyle == fontStyle && + other.textBaseline == textBaseline && + other.leadingDistribution == leadingDistribution && + other.originalFontFamily == originalFontFamily && + other.fontSize == fontSize && + other.letterSpacing == letterSpacing && + other.wordSpacing == wordSpacing && + other.height == height && + other.decorationThickness == decorationThickness && + other.locale == locale && + other.background == background && + other.foreground == foreground && + listEquals(other.shadows, shadows) && + listEquals(other.originalFontFamilyFallback, originalFontFamilyFallback) && + listEquals(other.fontFeatures, fontFeatures) && + listEquals(other.fontVariations, fontVariations); } @override @@ -647,7 +653,7 @@ class CkTextStyle implements ui.TextStyle { Object.hash( fontFeatures == null ? null : Object.hashAll(fontFeatures), fontVariations == null ? null : Object.hashAll(fontVariations), - ) + ), ); } @@ -658,7 +664,8 @@ class CkTextStyle implements ui.TextStyle { final List? fontFamilyFallback = originalFontFamilyFallback; final double? fontSize = this.fontSize; final double? height = this.height; - result = 'TextStyle(' + result = + 'TextStyle(' 'color: ${color ?? "unspecified"}, ' 'decoration: ${decoration ?? "unspecified"}, ' 'decorationColor: ${decorationColor ?? "unspecified"}, ' @@ -699,15 +706,16 @@ class CkStrutStyle implements ui.StrutStyle { ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, bool? forceStrutHeight, - }) : _fontFamily = _computeEffectiveFontFamily(fontFamily), - _fontFamilyFallback = ui_web.debugEmulateFlutterTesterEnvironment ? null : fontFamilyFallback, - _fontSize = fontSize, - _height = height == ui.kTextHeightNone ? null : height, - _leadingDistribution = leadingDistribution, - _leading = leading, - _fontWeight = fontWeight, - _fontStyle = fontStyle, - _forceStrutHeight = forceStrutHeight; + }) : _fontFamily = _computeEffectiveFontFamily(fontFamily), + _fontFamilyFallback = + ui_web.debugEmulateFlutterTesterEnvironment ? null : fontFamilyFallback, + _fontSize = fontSize, + _height = height == ui.kTextHeightNone ? null : height, + _leadingDistribution = leadingDistribution, + _leading = leading, + _fontWeight = fontWeight, + _fontStyle = fontStyle, + _forceStrutHeight = forceStrutHeight; final String? _fontFamily; final List? _fontFamilyFallback; @@ -740,16 +748,16 @@ class CkStrutStyle implements ui.StrutStyle { int get hashCode { final List? fontFamilyFallback = _fontFamilyFallback; return Object.hash( - _fontFamily, - fontFamilyFallback != null ? Object.hashAll(fontFamilyFallback) : null, - _fontSize, - _height, - _leading, - _leadingDistribution, - _fontWeight, - _fontStyle, - _forceStrutHeight, - ); + _fontFamily, + fontFamilyFallback != null ? Object.hashAll(fontFamilyFallback) : null, + _fontSize, + _height, + _leading, + _leadingDistribution, + _fontWeight, + _fontStyle, + _forceStrutHeight, + ); } } @@ -786,7 +794,6 @@ class CkParagraph implements ui.Paragraph { /// is deleted. final CkParagraphStyle _paragraphStyle; - @override double get alphabeticBaseline => _alphabeticBaseline; double _alphabeticBaseline = 0; @@ -853,13 +860,15 @@ class CkParagraph implements ui.Paragraph { final SkRectWithDirection skRect = skRects[i]; final Float32List rect = skRect.rect; final int skTextDirection = skRect.dir.value.toInt(); - result.add(ui.TextBox.fromLTRBD( - rect[0], - rect[1], - rect[2], - rect[3], - ui.TextDirection.values[skTextDirection], - )); + result.add( + ui.TextBox.fromLTRBD( + rect[0], + rect[1], + rect[2], + rect[3], + ui.TextDirection.values[skTextDirection], + ), + ); } return result; @@ -923,8 +932,7 @@ class CkParagraph implements ui.Paragraph { _maxIntrinsicWidth = paragraph.getMaxIntrinsicWidth(); _minIntrinsicWidth = paragraph.getMinIntrinsicWidth(); _width = paragraph.getMaxWidth(); - _boxesForPlaceholders = - skRectsToTextBoxes(paragraph.getRectsForPlaceholders()); + _boxesForPlaceholders = skRectsToTextBoxes(paragraph.getRectsForPlaceholders()); } catch (e) { printWarning( 'CanvasKit threw an exception while laying ' @@ -1026,8 +1034,7 @@ class CkLineMetrics implements ui.LineMetrics { double get baseline => skLineMetrics.baseline; @override - double get height => - (skLineMetrics.ascent + skLineMetrics.descent).round().toDouble(); + double get height => (skLineMetrics.ascent + skLineMetrics.descent).round().toDouble(); @override double get left => skLineMetrics.left; @@ -1041,14 +1048,14 @@ class CkLineMetrics implements ui.LineMetrics { class CkParagraphBuilder implements ui.ParagraphBuilder { CkParagraphBuilder(ui.ParagraphStyle style) - : _style = style as CkParagraphStyle, - _placeholderCount = 0, - _placeholderScales = [], - _styleStack = [], - _paragraphBuilder = canvasKit.ParagraphBuilder.MakeFromFontCollection( - style.skParagraphStyle, - CanvasKitRenderer.instance.fontCollection.skFontCollection, - ) { + : _style = style as CkParagraphStyle, + _placeholderCount = 0, + _placeholderScales = [], + _styleStack = [], + _paragraphBuilder = canvasKit.ParagraphBuilder.MakeFromFontCollection( + style.skParagraphStyle, + CanvasKitRenderer.instance.fontCollection.skFontCollection, + ) { _styleStack.add(_style.getTextStyle()); } @@ -1068,9 +1075,12 @@ class CkParagraphBuilder implements ui.ParagraphBuilder { ui.TextBaseline? baseline, }) { // Require a baseline to be specified if using a baseline-based alignment. - assert(!(alignment == ui.PlaceholderAlignment.aboveBaseline || - alignment == ui.PlaceholderAlignment.belowBaseline || - alignment == ui.PlaceholderAlignment.baseline) || baseline != null); + assert( + !(alignment == ui.PlaceholderAlignment.aboveBaseline || + alignment == ui.PlaceholderAlignment.belowBaseline || + alignment == ui.PlaceholderAlignment.baseline) || + baseline != null, + ); _placeholderCount++; _placeholderScales.add(scale); @@ -1175,9 +1185,7 @@ class CkParagraphBuilder implements ui.ParagraphBuilder { foreground = style.foreground!.toSkPaint(); } else { foreground = SkPaint(); - foreground.setColorInt( - style.color?.value ?? 0xFF000000, - ); + foreground.setColorInt(style.color?.value ?? 0xFF000000); } return foreground; } @@ -1187,8 +1195,7 @@ class CkParagraphBuilder implements ui.ParagraphBuilder { if (style.background != null) { background = style.background!.toSkPaint(); } else { - background = SkPaint() - ..setColorInt(0x00000000); + background = SkPaint()..setColorInt(0x00000000); } return background; } @@ -1204,8 +1211,7 @@ class CkParagraphBuilder implements ui.ParagraphBuilder { if (mergedStyle.foreground != null || mergedStyle.background != null) { final foreground = createForegroundPaint(mergedStyle); final background = createBackgroundPaint(mergedStyle); - _paragraphBuilder.pushPaintStyle( - mergedStyle.skTextStyle, foreground, background); + _paragraphBuilder.pushPaintStyle(mergedStyle.skTextStyle, foreground, background); foreground.delete(); background.delete(); } else { @@ -1230,8 +1236,7 @@ class _CkParagraphPlaceholder { final double offset; } -List _computeCombinedFontFamilies(String? fontFamily, - [List? fontFamilyFallback]) { +List _computeCombinedFontFamilies(String? fontFamily, [List? fontFamilyFallback]) { final List fontFamilies = []; if (fontFamily != null) { fontFamilies.add(fontFamily); @@ -1240,8 +1245,6 @@ List _computeCombinedFontFamilies(String? fontFamily, !fontFamilyFallback.every((String font) => fontFamily == font)) { fontFamilies.addAll(fontFamilyFallback); } - fontFamilies.addAll( - renderer.fontCollection.fontFallbackManager!.globalFontFallbacks - ); + fontFamilies.addAll(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks); return fontFamilies; } diff --git a/lib/web_ui/lib/src/engine/canvaskit/text_fragmenter.dart b/lib/web_ui/lib/src/engine/canvaskit/text_fragmenter.dart index a762d769cc61b..6fc251a85d2ee 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/text_fragmenter.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/text_fragmenter.dart @@ -10,11 +10,7 @@ import '../text/line_breaker.dart'; import '../util.dart'; import 'canvaskit_api.dart'; -typedef SegmentationResult = ({ - Uint32List words, - Uint32List graphemes, - Uint32List breaks, -}); +typedef SegmentationResult = ({Uint32List words, Uint32List graphemes, Uint32List breaks}); // The cache numbers below were picked based on the following logic. // @@ -38,11 +34,12 @@ const SegmentationCacheSpec kSmallParagraphCacheSpec = (cacheSize: 100000, maxTe const SegmentationCacheSpec kMediumParagraphCacheSpec = (cacheSize: 10000, maxTextLength: 100); const SegmentationCacheSpec kLargeParagraphCacheSpec = (cacheSize: 20, maxTextLength: 50000); -typedef SegmentationCache = ({ - LruCache small, - LruCache medium, - LruCache large, -}); +typedef SegmentationCache = + ({ + LruCache small, + LruCache medium, + LruCache large, + }); /// Caches segmentation results for small, medium and large paragraphts. /// @@ -121,20 +118,15 @@ SegmentationResult segmentText(String text) { /// /// To find all supported granularities, see: /// - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter/Segmenter -enum IntlSegmenterGranularity { - grapheme, - word, -} +enum IntlSegmenterGranularity { grapheme, word } -final Map _intlSegmenters = { - IntlSegmenterGranularity.grapheme: createIntlSegmenter(granularity: 'grapheme'), - IntlSegmenterGranularity.word: createIntlSegmenter(granularity: 'word'), -}; +final Map _intlSegmenters = + { + IntlSegmenterGranularity.grapheme: createIntlSegmenter(granularity: 'grapheme'), + IntlSegmenterGranularity.word: createIntlSegmenter(granularity: 'word'), + }; -Uint32List fragmentUsingIntlSegmenter( - String text, - IntlSegmenterGranularity granularity, -) { +Uint32List fragmentUsingIntlSegmenter(String text, IntlSegmenterGranularity granularity) { final DomSegmenter segmenter = _intlSegmenters[granularity]!; final DomIteratorWrapper iterator = segmenter.segment(text).iterator(); @@ -153,8 +145,11 @@ const int _kHardLineBreak = 1; final DomV8BreakIterator _v8LineBreaker = createV8BreakIterator(); Uint32List fragmentUsingV8LineBreaker(String text) { - final List fragments = - breakLinesUsingV8BreakIterator(text, text.toJS, _v8LineBreaker); + final List fragments = breakLinesUsingV8BreakIterator( + text, + text.toJS, + _v8LineBreaker, + ); final int size = (fragments.length + 1) * 2; final Uint32List typedArray = Uint32List(size); @@ -166,9 +161,8 @@ Uint32List fragmentUsingV8LineBreaker(String text) { final LineBreakFragment fragment = fragments[i]; final int uint32Index = 2 + i * 2; typedArray[uint32Index] = fragment.end; - typedArray[uint32Index + 1] = fragment.type == LineBreakType.mandatory - ? _kHardLineBreak - : _kSoftLineBreak; + typedArray[uint32Index + 1] = + fragment.type == LineBreakType.mandatory ? _kHardLineBreak : _kSoftLineBreak; } return typedArray; diff --git a/lib/web_ui/lib/src/engine/canvaskit/util.dart b/lib/web_ui/lib/src/engine/canvaskit/util.dart index 2b2e646dd117b..ae0a91524f2c9 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/util.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/util.dart @@ -35,10 +35,7 @@ Float32List makeFreshSkColor(ui.Color color) { ui.TextPosition fromPositionWithAffinity(SkTextPosition positionWithAffinity) { final ui.TextAffinity affinity = ui.TextAffinity.values[positionWithAffinity.affinity.value.toInt()]; - return ui.TextPosition( - offset: positionWithAffinity.pos.toInt(), - affinity: affinity, - ); + return ui.TextPosition(offset: positionWithAffinity.pos.toInt(), affinity: affinity); } /// Shadow flag constants derived from Skia's SkShadowFlags.h. @@ -61,8 +58,7 @@ class SkiaShadowFlags { static const int kDirectionalLight_ShadowFlag = 0x04; /// Complete value for the `flags` argument for opaque occluder. - static const int kDefaultShadowFlags = - kDirectionalLight_ShadowFlag | kNone_ShadowFlag; + static const int kDefaultShadowFlags = kDirectionalLight_ShadowFlag | kNone_ShadowFlag; /// Complete value for the `flags` argument for transparent occluder. static const int kTransparentOccluderShadowFlags = @@ -125,8 +121,7 @@ ui.Rect computeSkShadowBounds( left = left - 1 + (spotOffsetX - ambientBlur - spotBlur) * devicePixelRatio; top = top - 1 + (spotOffsetY - ambientBlur - spotBlur) * devicePixelRatio; right = right + 1 + (spotOffsetX + ambientBlur + spotBlur) * devicePixelRatio; - bottom = - bottom + 1 + (spotOffsetY + ambientBlur + spotBlur) * devicePixelRatio; + bottom = bottom + 1 + (spotOffsetY + ambientBlur + spotBlur) * devicePixelRatio; final ui.Rect shadowBounds = ui.Rect.fromLTRB(left, top, right, bottom); @@ -145,12 +140,10 @@ ui.Rect computeSkShadowBounds( const double kAmbientHeightFactor = 1.0 / 128.0; const double kAmbientGeomFactor = 64.0; -const double kMaxAmbientRadius = - 300 * kAmbientHeightFactor * kAmbientGeomFactor; +const double kMaxAmbientRadius = 300 * kAmbientHeightFactor * kAmbientGeomFactor; double ambientBlurRadius(double height) { - return math.min( - height * kAmbientHeightFactor * kAmbientGeomFactor, kMaxAmbientRadius); + return math.min(height * kAmbientHeightFactor * kAmbientGeomFactor, kMaxAmbientRadius); } void drawSkShadow( @@ -161,15 +154,14 @@ void drawSkShadow( bool transparentOccluder, double devicePixelRatio, ) { - int flags = transparentOccluder - ? SkiaShadowFlags.kTransparentOccluderShadowFlags - : SkiaShadowFlags.kDefaultShadowFlags; + int flags = + transparentOccluder + ? SkiaShadowFlags.kTransparentOccluderShadowFlags + : SkiaShadowFlags.kDefaultShadowFlags; flags |= SkiaShadowFlags.kDirectionalLight_ShadowFlag; - final ui.Color inAmbient = - color.withAlpha((color.alpha * ckShadowAmbientAlpha).round()); - final ui.Color inSpot = - color.withAlpha((color.alpha * ckShadowSpotAlpha).round()); + final ui.Color inAmbient = color.withAlpha((color.alpha * ckShadowAmbientAlpha).round()); + final ui.Color inSpot = color.withAlpha((color.alpha * ckShadowSpotAlpha).round()); final SkTonalColors inTonalColors = SkTonalColors( ambient: makeFreshSkColor(inAmbient), diff --git a/lib/web_ui/lib/src/engine/canvaskit/vertices.dart b/lib/web_ui/lib/src/engine/canvaskit/vertices.dart index 8c53334e37d09..2011de5216a6c 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/vertices.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/vertices.dart @@ -17,18 +17,14 @@ class CkVertices implements ui.Vertices { List? colors, List? indices, }) { - if (textureCoordinates != null && - textureCoordinates.length != positions.length) { - throw ArgumentError( - '"positions" and "textureCoordinates" lengths must match.'); + if (textureCoordinates != null && textureCoordinates.length != positions.length) { + throw ArgumentError('"positions" and "textureCoordinates" lengths must match.'); } if (colors != null && colors.length != positions.length) { throw ArgumentError('"positions" and "colors" lengths must match.'); } - if (indices != null && - indices.any((int i) => i < 0 || i >= positions.length)) { - throw ArgumentError( - '"indices" values must be valid indices in the positions list.'); + if (indices != null && indices.any((int i) => i < 0 || i >= positions.length)) { + throw ArgumentError('"indices" values must be valid indices in the positions list.'); } return CkVertices._( @@ -47,18 +43,14 @@ class CkVertices implements ui.Vertices { Int32List? colors, Uint16List? indices, }) { - if (textureCoordinates != null && - textureCoordinates.length != positions.length) { - throw ArgumentError( - '"positions" and "textureCoordinates" lengths must match.'); + if (textureCoordinates != null && textureCoordinates.length != positions.length) { + throw ArgumentError('"positions" and "textureCoordinates" lengths must match.'); } if (colors != null && colors.length * 2 != positions.length) { throw ArgumentError('"positions" and "colors" lengths must match.'); } - if (indices != null && - indices.any((int i) => i < 0 || i >= positions.length)) { - throw ArgumentError( - '"indices" values must be valid indices in the positions list.'); + if (indices != null && indices.any((int i) => i < 0 || i >= positions.length)) { + throw ArgumentError('"indices" values must be valid indices in the positions list.'); } Uint32List? unsignedColors; @@ -75,13 +67,7 @@ class CkVertices implements ui.Vertices { ); } - CkVertices._( - this._mode, - this._positions, - this._textureCoordinates, - this._colors, - this._indices, - ) { + CkVertices._(this._mode, this._positions, this._textureCoordinates, this._colors, this._indices) { final SkVertices skVertices = canvasKit.MakeVertices( _mode, _positions, diff --git a/lib/web_ui/lib/src/engine/clipboard.dart b/lib/web_ui/lib/src/engine/clipboard.dart index 1d17b39d40c19..c63194709e85d 100644 --- a/lib/web_ui/lib/src/engine/clipboard.dart +++ b/lib/web_ui/lib/src/engine/clipboard.dart @@ -14,87 +14,96 @@ class ClipboardMessageHandler { CopyToClipboardStrategy _copyToClipboardStrategy = CopyToClipboardStrategy(); /// Helper to handle copy to clipboard functionality. - PasteFromClipboardStrategy _pasteFromClipboardStrategy = - PasteFromClipboardStrategy(); + PasteFromClipboardStrategy _pasteFromClipboardStrategy = PasteFromClipboardStrategy(); /// Handles the platform message which stores the given text to the clipboard. - void setDataMethodCall( - MethodCall methodCall, ui.PlatformMessageResponseCallback? callback) { + void setDataMethodCall(MethodCall methodCall, ui.PlatformMessageResponseCallback? callback) { const MethodCodec codec = JSONMethodCodec(); bool errorEnvelopeEncoded = false; _copyToClipboardStrategy .setData((methodCall.arguments as Map)['text'] as String?) .then((bool success) { - if (success) { - callback!(codec.encodeSuccessEnvelope(true)); - } else { - callback!(codec.encodeErrorEnvelope( - code: 'copy_fail', message: 'Clipboard.setData failed')); - errorEnvelopeEncoded = true; - } - }).catchError((dynamic _) { - // Don't encode a duplicate reply if we already failed and an error - // was already encoded. - if (!errorEnvelopeEncoded) { - callback!(codec.encodeErrorEnvelope( - code: 'copy_fail', message: 'Clipboard.setData failed')); - } - }); + if (success) { + callback!(codec.encodeSuccessEnvelope(true)); + } else { + callback!( + codec.encodeErrorEnvelope(code: 'copy_fail', message: 'Clipboard.setData failed'), + ); + errorEnvelopeEncoded = true; + } + }) + .catchError((dynamic _) { + // Don't encode a duplicate reply if we already failed and an error + // was already encoded. + if (!errorEnvelopeEncoded) { + callback!( + codec.encodeErrorEnvelope(code: 'copy_fail', message: 'Clipboard.setData failed'), + ); + } + }); } /// Handles the platform message which retrieves text data from the clipboard. void getDataMethodCall(ui.PlatformMessageResponseCallback? callback) { const MethodCodec codec = JSONMethodCodec(); - _pasteFromClipboardStrategy.getData().then((String data) { - final Map map = {'text': data}; - callback!(codec.encodeSuccessEnvelope(map)); - }).catchError((dynamic error) { - if (error is UnimplementedError) { - // Clipboard.getData not supported. - // Passing [null] to [callback] indicates that the platform message isn't - // implemented. Look at [MethodChannel.invokeMethod] to see how [null] is - // handled. - Future.delayed(Duration.zero).then((_) { - if (callback != null) { - callback(null); + _pasteFromClipboardStrategy + .getData() + .then((String data) { + final Map map = {'text': data}; + callback!(codec.encodeSuccessEnvelope(map)); + }) + .catchError((dynamic error) { + if (error is UnimplementedError) { + // Clipboard.getData not supported. + // Passing [null] to [callback] indicates that the platform message isn't + // implemented. Look at [MethodChannel.invokeMethod] to see how [null] is + // handled. + Future.delayed(Duration.zero).then((_) { + if (callback != null) { + callback(null); + } + }); + return; } + _reportGetDataFailure(callback, codec, error); }); - return; - } - _reportGetDataFailure(callback, codec, error); - }); } /// Handles the platform message which asks if the clipboard contains /// pasteable strings. void hasStringsMethodCall(ui.PlatformMessageResponseCallback? callback) { const MethodCodec codec = JSONMethodCodec(); - _pasteFromClipboardStrategy.getData().then((String data) { - final Map map = {'value': data.isNotEmpty}; - callback!(codec.encodeSuccessEnvelope(map)); - }).catchError((dynamic error) { - if (error is UnimplementedError) { - // Clipboard.hasStrings not supported. - // Passing [null] to [callback] indicates that the platform message isn't - // implemented. Look at [MethodChannel.invokeMethod] to see how [null] is - // handled. - Future.delayed(Duration.zero).then((_) { - if (callback != null) { - callback(null); + _pasteFromClipboardStrategy + .getData() + .then((String data) { + final Map map = {'value': data.isNotEmpty}; + callback!(codec.encodeSuccessEnvelope(map)); + }) + .catchError((dynamic error) { + if (error is UnimplementedError) { + // Clipboard.hasStrings not supported. + // Passing [null] to [callback] indicates that the platform message isn't + // implemented. Look at [MethodChannel.invokeMethod] to see how [null] is + // handled. + Future.delayed(Duration.zero).then((_) { + if (callback != null) { + callback(null); + } + }); + return; } + final Map map = {'value': false}; + callback!(codec.encodeSuccessEnvelope(map)); }); - return; - } - final Map map = {'value': false}; - callback!(codec.encodeSuccessEnvelope(map)); - }); } - void _reportGetDataFailure(ui.PlatformMessageResponseCallback? callback, - MethodCodec codec, dynamic error) { + void _reportGetDataFailure( + ui.PlatformMessageResponseCallback? callback, + MethodCodec codec, + dynamic error, + ) { print('Could not get text from clipboard: $error'); - callback!(codec.encodeErrorEnvelope( - code: 'paste_fail', message: 'Clipboard.getData failed')); + callback!(codec.encodeErrorEnvelope(code: 'paste_fail', message: 'Clipboard.getData failed')); } /// Methods used by tests. @@ -227,7 +236,6 @@ class ExecCommandPasteStrategy implements PasteFromClipboardStrategy { @override Future getData() { // TODO(mdebbar): https://github.com/flutter/flutter/issues/48581 - return Future.error( - UnimplementedError('Paste is not implemented for this browser.')); + return Future.error(UnimplementedError('Paste is not implemented for this browser.')); } } diff --git a/lib/web_ui/lib/src/engine/color_filter.dart b/lib/web_ui/lib/src/engine/color_filter.dart index a74145c08bd78..c3572f88604fb 100644 --- a/lib/web_ui/lib/src/engine/color_filter.dart +++ b/lib/web_ui/lib/src/engine/color_filter.dart @@ -5,12 +5,7 @@ import 'package:ui/src/engine.dart'; import 'package:ui/ui.dart' as ui; -enum ColorFilterType { - mode, - matrix, - linearToSrgbGamma, - srgbToLinearGamma, -} +enum ColorFilterType { mode, matrix, linearToSrgbGamma, srgbToLinearGamma } /// A description of a color filter to apply when drawing a shape or compositing /// a layer with a particular [Paint]. A color filter is a function that takes @@ -93,25 +88,25 @@ class EngineColorFilter implements SceneImageFilter, ui.ColorFilter { /// ]); /// ``` const EngineColorFilter.matrix(List this.matrix) - : color = null, - blendMode = null, - type = ColorFilterType.matrix; + : color = null, + blendMode = null, + type = ColorFilterType.matrix; /// Construct a color filter that applies the sRGB gamma curve to the RGB /// channels. const EngineColorFilter.linearToSrgbGamma() - : color = null, - blendMode = null, - matrix = null, - type = ColorFilterType.linearToSrgbGamma; + : color = null, + blendMode = null, + matrix = null, + type = ColorFilterType.linearToSrgbGamma; /// Creates a color filter that applies the inverse of the sRGB gamma curve /// to the RGB channels. const EngineColorFilter.srgbToLinearGamma() - : color = null, - blendMode = null, - matrix = null, - type = ColorFilterType.srgbToLinearGamma; + : color = null, + blendMode = null, + matrix = null, + type = ColorFilterType.srgbToLinearGamma; final ui.Color? color; final ui.BlendMode? blendMode; diff --git a/lib/web_ui/lib/src/engine/configuration.dart b/lib/web_ui/lib/src/engine/configuration.dart index 50695b991875f..6b417805e5deb 100644 --- a/lib/web_ui/lib/src/engine/configuration.dart +++ b/lib/web_ui/lib/src/engine/configuration.dart @@ -108,25 +108,30 @@ class FlutterConfiguration { assert(() { if (config != null) { domWindow.console.warn( - 'window.flutterConfiguration is now deprecated.\n' - 'Use engineInitializer.initializeEngine(config) instead.\n' - 'See: https://docs.flutter.dev/development/platform-integration/web/initialization'); + 'window.flutterConfiguration is now deprecated.\n' + 'Use engineInitializer.initializeEngine(config) instead.\n' + 'See: https://docs.flutter.dev/development/platform-integration/web/initialization', + ); } if (_requestedRendererType != null) { - domWindow.console.warn('window.flutterWebRenderer is now deprecated.\n' - 'Use engineInitializer.initializeEngine(config) instead.\n' - 'See: https://docs.flutter.dev/development/platform-integration/web/initialization'); + domWindow.console.warn( + 'window.flutterWebRenderer is now deprecated.\n' + 'Use engineInitializer.initializeEngine(config) instead.\n' + 'See: https://docs.flutter.dev/development/platform-integration/web/initialization', + ); } return true; }()); } FlutterConfiguration withOverrides(JsFlutterConfiguration? overrides) { - final JsFlutterConfiguration newJsConfig = objectConstructor.assign( - {}.jsify(), - _configuration.jsify(), - overrides.jsify(), - ) as JsFlutterConfiguration; + final JsFlutterConfiguration newJsConfig = + objectConstructor.assign( + {}.jsify(), + _configuration.jsify(), + overrides.jsify(), + ) + as JsFlutterConfiguration; final FlutterConfiguration newConfig = FlutterConfiguration(); newConfig._configuration = newJsConfig; return newConfig; @@ -146,15 +151,17 @@ class FlutterConfiguration { void setUserConfiguration(JsFlutterConfiguration? configuration) { if (configuration != null) { assert( - !_usedLegacyConfigStyle, - 'Use engineInitializer.initializeEngine(config) only. ' - 'Using the (deprecated) window.flutterConfiguration and initializeEngine ' - 'configuration simultaneously is not supported.'); + !_usedLegacyConfigStyle, + 'Use engineInitializer.initializeEngine(config) only. ' + 'Using the (deprecated) window.flutterConfiguration and initializeEngine ' + 'configuration simultaneously is not supported.', + ); assert( - _requestedRendererType == null || configuration.renderer == null, - 'Use engineInitializer.initializeEngine(config) only. ' - 'Using the (deprecated) window.flutterWebRenderer and initializeEngine ' - 'configuration simultaneously is not supported.'); + _requestedRendererType == null || configuration.renderer == null, + 'Use engineInitializer.initializeEngine(config) only. ' + 'Using the (deprecated) window.flutterWebRenderer and initializeEngine ' + 'configuration simultaneously is not supported.', + ); _configuration = configuration; } } @@ -169,11 +176,12 @@ class FlutterConfiguration { /// /// Using flutter tools option "--web-renderer=auto" or not specifying one /// would set the value to true. Otherwise, it would be false. - static const bool flutterWebAutoDetect = - bool.fromEnvironment('FLUTTER_WEB_AUTO_DETECT', defaultValue: true); + static const bool flutterWebAutoDetect = bool.fromEnvironment( + 'FLUTTER_WEB_AUTO_DETECT', + defaultValue: true, + ); - static const bool flutterWebUseSkwasm = - bool.fromEnvironment('FLUTTER_WEB_USE_SKWASM'); + static const bool flutterWebUseSkwasm = bool.fromEnvironment('FLUTTER_WEB_USE_SKWASM'); /// Enable the Skia-based rendering backend. /// @@ -237,8 +245,7 @@ class FlutterConfiguration { /// --web-renderer=canvaskit \ /// --dart-define=FLUTTER_WEB_CANVASKIT_URL=https://example.com/custom-canvaskit-build/ /// ``` - String get canvasKitBaseUrl => - _configuration?.canvasKitBaseUrl ?? _defaultCanvasKitBaseUrl; + String get canvasKitBaseUrl => _configuration?.canvasKitBaseUrl ?? _defaultCanvasKitBaseUrl; static const String _defaultCanvasKitBaseUrl = String.fromEnvironment( 'FLUTTER_WEB_CANVASKIT_URL', defaultValue: 'canvaskit/', @@ -275,8 +282,7 @@ class FlutterConfiguration { /// /// Limits the amount of overlays that can be created. int get canvasKitMaximumSurfaces { - final int maxSurfaces = - _configuration?.canvasKitMaximumSurfaces?.toInt() ?? 8; + final int maxSurfaces = _configuration?.canvasKitMaximumSurfaces?.toInt() ?? 8; if (maxSurfaces < 1) { return 1; } @@ -295,8 +301,7 @@ class FlutterConfiguration { /// flutter run -d chrome --profile --dart-define=FLUTTER_WEB_DEBUG_SHOW_SEMANTICS=true /// ``` bool get debugShowSemanticsNodes => - _configuration?.debugShowSemanticsNodes ?? - _defaultDebugShowSemanticsNodes; + _configuration?.debugShowSemanticsNodes ?? _defaultDebugShowSemanticsNodes; static const bool _defaultDebugShowSemanticsNodes = bool.fromEnvironment( 'FLUTTER_WEB_DEBUG_SHOW_SEMANTICS', ); @@ -327,8 +332,7 @@ class FlutterConfiguration { /// `window.flutterWebRenderer`. /// /// This is used by the Renderer class to decide how to initialize the engine. - String? get requestedRendererType => - _configuration?.renderer ?? _requestedRendererType; + String? get requestedRendererType => _configuration?.renderer ?? _requestedRendererType; /// Returns the base URL to load fallback fonts from. Fallback fonts are /// downloaded automatically when there is no font bundled with the app that @@ -371,8 +375,7 @@ extension JsFlutterConfigurationExtension on JsFlutterConfiguration { @JS('canvasKitMaximumSurfaces') external JSNumber? get _canvasKitMaximumSurfaces; - double? get canvasKitMaximumSurfaces => - _canvasKitMaximumSurfaces?.toDartDouble; + double? get canvasKitMaximumSurfaces => _canvasKitMaximumSurfaces?.toDartDouble; @JS('debugShowSemanticsNodes') external JSBoolean? get _debugShowSemanticsNodes; diff --git a/lib/web_ui/lib/src/engine/display.dart b/lib/web_ui/lib/src/engine/display.dart index 1264b49865b09..017825284aae2 100644 --- a/lib/web_ui/lib/src/engine/display.dart +++ b/lib/web_ui/lib/src/engine/display.dart @@ -9,11 +9,7 @@ import 'package:ui/ui.dart' as ui; import '../engine.dart'; class EngineFlutterDisplay extends ui.Display { - EngineFlutterDisplay({ - required this.id, - required this.size, - required this.refreshRate, - }); + EngineFlutterDisplay({required this.id, required this.size, required this.refreshRate}); /// The single [EngineFlutterDisplay] that the web page is rendered on. static EngineFlutterDisplay get instance => _instance; @@ -38,8 +34,7 @@ class EngineFlutterDisplay extends ui.Display { final double refreshRate; @override - double get devicePixelRatio => - _debugDevicePixelRatioOverride ?? browserDevicePixelRatio; + double get devicePixelRatio => _debugDevicePixelRatioOverride ?? browserDevicePixelRatio; /// The real device pixel ratio of the browser. /// @@ -97,8 +92,7 @@ class ScreenOrientation { screenOrientation.unlock(); return true; } else { - final String? lockType = - _deviceOrientationToLockType(orientations.first as String?); + final String? lockType = _deviceOrientationToLockType(orientations.first as String?); if (lockType != null) { try { await screenOrientation.lock(lockType); diff --git a/lib/web_ui/lib/src/engine/dom.dart b/lib/web_ui/lib/src/engine/dom.dart index 336ddadbab602..689408af69d4c 100644 --- a/lib/web_ui/lib/src/engine/dom.dart +++ b/lib/web_ui/lib/src/engine/dom.dart @@ -132,8 +132,7 @@ extension DomWindowExtension on DomWindow { DomMediaQueryList matchMedia(String? query) => _matchMedia(query?.toJS); @JS('getComputedStyle') - external DomCSSStyleDeclaration _getComputedStyle( - DomElement elt, [JSString pseudoElt]); + external DomCSSStyleDeclaration _getComputedStyle(DomElement elt, [JSString pseudoElt]); DomCSSStyleDeclaration getComputedStyle(DomElement elt, [String? pseudoElt]) { if (pseudoElt == null) { return _getComputedStyle(elt); @@ -150,19 +149,18 @@ extension DomWindowExtension on DomWindow { _requestAnimationFrame(callback.toJS).toDartDouble; @JS('postMessage') - external void _postMessage( - JSAny message, JSString targetOrigin, [JSArray messagePorts]); - void postMessage(Object message, String targetOrigin, - [List? messagePorts]) { + external void _postMessage(JSAny message, JSString targetOrigin, [JSArray messagePorts]); + void postMessage(Object message, String targetOrigin, [List? messagePorts]) { if (messagePorts == null) { _postMessage(message.toJSAnyShallow, targetOrigin.toJS); } else { _postMessage( - message.toJSAnyShallow, - targetOrigin.toJS, - // Cast is necessary so we can call `.toJS` on the right extension. - // ignore: unnecessary_cast - (messagePorts as List).toJS); + message.toJSAnyShallow, + targetOrigin.toJS, + // Cast is necessary so we can call `.toJS` on the right extension. + // ignore: unnecessary_cast + (messagePorts as List).toJS, + ); } } @@ -214,12 +212,19 @@ external JSPromise _createImageBitmap( JSNumber width, JSNumber height, ]); -Future createImageBitmap(JSAny source, - [({int x, int y, int width, int height})? bounds]) { +Future createImageBitmap( + JSAny source, [ + ({int x, int y, int width, int height})? bounds, +]) { JSPromise jsPromise; if (bounds != null) { - jsPromise = _createImageBitmap(source, bounds.x.toJS, bounds.y.toJS, - bounds.width.toJS, bounds.height.toJS); + jsPromise = _createImageBitmap( + source, + bounds.x.toJS, + bounds.y.toJS, + bounds.width.toJS, + bounds.height.toJS, + ); } else { jsPromise = _createImageBitmap(source); } @@ -255,9 +260,8 @@ extension DomNavigatorExtension on DomNavigator { @JS('languages') external JSArray? get _languages; - List? get languages => _languages?.toDart - .map((JSAny? any) => (any! as JSString).toDart) - .toList(); + List? get languages => + _languages?.toDart.map((JSAny? any) => (any! as JSString).toDart).toList(); } @JS() @@ -293,8 +297,7 @@ extension DomDocumentExtension on DomDocument { external DomHTMLScriptElement? get currentScript; @JS('createElementNS') - external DomElement _createElementNS( - JSString namespaceURI, JSString qualifiedName); + external DomElement _createElementNS(JSString namespaceURI, JSString qualifiedName); DomElement createElementNS(String namespaceURI, String qualifiedName) => _createElementNS(namespaceURI.toJS, qualifiedName.toJS); @@ -310,8 +313,7 @@ extension DomDocumentExtension on DomDocument { @JS('elementFromPoint') external DomElement? _elementFromPoint(JSNumber x, JSNumber y); - DomElement? elementFromPoint(int x, int y) => - _elementFromPoint(x.toJS, y.toJS); + DomElement? elementFromPoint(int x, int y) => _elementFromPoint(x.toJS, y.toJS); } @JS() @@ -360,10 +362,8 @@ class DomEventTarget {} extension DomEventTargetExtension on DomEventTarget { @JS('addEventListener') - external void _addEventListener( - JSString type, DomEventListener listener, [JSBoolean useCapture]); - void addEventListener(String type, DomEventListener? listener, - [bool? useCapture]) { + external void _addEventListener(JSString type, DomEventListener listener, [JSBoolean useCapture]); + void addEventListener(String type, DomEventListener? listener, [bool? useCapture]) { if (listener != null) { if (useCapture == null) { _addEventListener(type.toJS, listener); @@ -375,16 +375,23 @@ extension DomEventTargetExtension on DomEventTarget { @JS('addEventListener') external void _addEventListenerWithOptions( - JSString type, DomEventListener listener, JSAny options); - void addEventListenerWithOptions(String type, DomEventListener listener, - Map options) => - _addEventListenerWithOptions(type.toJS, listener, options.toJSAnyDeep); + JSString type, + DomEventListener listener, + JSAny options, + ); + void addEventListenerWithOptions( + String type, + DomEventListener listener, + Map options, + ) => _addEventListenerWithOptions(type.toJS, listener, options.toJSAnyDeep); @JS('removeEventListener') external void _removeEventListener( - JSString type, DomEventListener listener, [JSBoolean useCapture]); - void removeEventListener(String type, DomEventListener? listener, - [bool? useCapture]) { + JSString type, + DomEventListener listener, [ + JSBoolean useCapture, + ]); + void removeEventListener(String type, DomEventListener? listener, [bool? useCapture]) { if (listener != null) { if (useCapture == null) { _removeEventListener(type.toJS, listener); @@ -431,8 +438,7 @@ extension DomEventExtension on DomEvent { external void stopPropagation(); @JS('initEvent') - external void _initEvent( - JSString type, [JSBoolean bubbles, JSBoolean cancelable]); + external void _initEvent(JSString type, [JSBoolean bubbles, JSBoolean cancelable]); void initEvent(String type, [bool? bubbles, bool? cancelable]) { if (bubbles == null) { _initEvent(type.toJS); @@ -533,8 +539,7 @@ extension DomNodeExtension on DomNode { @JS('childNodes') external _DomList get _childNodes; - Iterable get childNodes => - createDomListWrapper(_childNodes); + Iterable get childNodes => createDomListWrapper(_childNodes); external DomDocument? get ownerDocument; void clearChildren() { @@ -553,8 +558,7 @@ DomElement createDomElement(String tag) => domDocument.createElement(tag); extension DomElementExtension on DomElement { @JS('children') external _DomList get _children; - Iterable get children => - createDomListWrapper(_children); + Iterable get children => createDomListWrapper(_children); external DomElement? get firstElementChild; external DomElement? get lastElementChild; @@ -606,8 +610,7 @@ extension DomElementExtension on DomElement { @JS('getAttribute') external JSString? _getAttribute(JSString attributeName); - String? getAttribute(String attributeName) => - _getAttribute(attributeName.toJS)?.toDart; + String? getAttribute(String attributeName) => _getAttribute(attributeName.toJS)?.toDart; external DomRect getBoundingClientRect(); external void prepend(DomNode node); @@ -633,8 +636,7 @@ extension DomElementExtension on DomElement { @JS('setAttribute') external void _setAttribute(JSString name, JSAny value); - void setAttribute(String name, Object value) => - _setAttribute(name.toJS, value.toJSAnyDeep); + void setAttribute(String name, Object value) => _setAttribute(name.toJS, value.toJSAnyDeep); void appendText(String text) => append(createDomText(text)); @@ -658,7 +660,7 @@ extension DomElementExtension on DomElement { @JS('focus') external void _focus(JSAny options); - static final JSAny _preventScrollOptions = { 'preventScroll': true }.toJSAnyDeep; + static final JSAny _preventScrollOptions = {'preventScroll': true}.toJSAnyDeep; /// Calls DOM `Element.focus` with `preventScroll` set to true. /// @@ -718,13 +720,11 @@ extension DomElementExtension on DomElement { @JS('childNodes') external _DomList get _childNodes; - Iterable get childNodes => - createDomListWrapper(_childNodes); + Iterable get childNodes => createDomListWrapper(_childNodes); @JS('attachShadow') external DomShadowRoot _attachShadow(JSAny initDict); - DomShadowRoot attachShadow(Map initDict) => - _attachShadow(initDict.toJSAnyDeep); + DomShadowRoot attachShadow(Map initDict) => _attachShadow(initDict.toJSAnyDeep); external DomShadowRoot? get shadowRoot; void clearChildren() { @@ -770,23 +770,16 @@ extension DomCSSStyleDeclarationExtension on DomCSSStyleDeclaration { set wordSpacing(String value) => setProperty('word-spacing', value); set textShadow(String value) => setProperty('text-shadow', value); set textDecoration(String value) => setProperty('text-decoration', value); - set textDecorationColor(String value) => - setProperty('text-decoration-color', value); - set fontFeatureSettings(String value) => - setProperty('font-feature-settings', value); - set fontVariationSettings(String value) => - setProperty('font-variation-settings', value); + set textDecorationColor(String value) => setProperty('text-decoration-color', value); + set fontFeatureSettings(String value) => setProperty('font-feature-settings', value); + set fontVariationSettings(String value) => setProperty('font-variation-settings', value); set visibility(String value) => setProperty('visibility', value); set overflow(String value) => setProperty('overflow', value); set boxShadow(String value) => setProperty('box-shadow', value); - set borderTopLeftRadius(String value) => - setProperty('border-top-left-radius', value); - set borderTopRightRadius(String value) => - setProperty('border-top-right-radius', value); - set borderBottomLeftRadius(String value) => - setProperty('border-bottom-left-radius', value); - set borderBottomRightRadius(String value) => - setProperty('border-bottom-right-radius', value); + set borderTopLeftRadius(String value) => setProperty('border-top-left-radius', value); + set borderTopRightRadius(String value) => setProperty('border-top-right-radius', value); + set borderBottomLeftRadius(String value) => setProperty('border-bottom-left-radius', value); + set borderBottomRightRadius(String value) => setProperty('border-bottom-right-radius', value); set borderRadius(String value) => setProperty('border-radius', value); set perspective(String value) => setProperty('perspective', value); set padding(String value) => setProperty('padding', value); @@ -794,8 +787,7 @@ extension DomCSSStyleDeclarationExtension on DomCSSStyleDeclaration { set border(String value) => setProperty('border', value); set mixBlendMode(String value) => setProperty('mix-blend-mode', value); set backgroundSize(String value) => setProperty('background-size', value); - set backgroundBlendMode(String value) => - setProperty('background-blend-mode', value); + set backgroundBlendMode(String value) => setProperty('background-blend-mode', value); set transformStyle(String value) => setProperty('transform-style', value); set display(String value) => setProperty('display', value); set flexDirection(String value) => setProperty('flex-direction', value); @@ -841,18 +833,14 @@ extension DomCSSStyleDeclarationExtension on DomCSSStyleDeclaration { String get textShadow => getPropertyValue('text-shadow'); String get textDecorationColor => getPropertyValue('text-decoration-color'); String get fontFeatureSettings => getPropertyValue('font-feature-settings'); - String get fontVariationSettings => - getPropertyValue('font-variation-settings'); + String get fontVariationSettings => getPropertyValue('font-variation-settings'); String get visibility => getPropertyValue('visibility'); String get overflow => getPropertyValue('overflow'); String get boxShadow => getPropertyValue('box-shadow'); String get borderTopLeftRadius => getPropertyValue('border-top-left-radius'); - String get borderTopRightRadius => - getPropertyValue('border-top-right-radius'); - String get borderBottomLeftRadius => - getPropertyValue('border-bottom-left-radius'); - String get borderBottomRightRadius => - getPropertyValue('border-bottom-right-radius'); + String get borderTopRightRadius => getPropertyValue('border-top-right-radius'); + String get borderBottomLeftRadius => getPropertyValue('border-bottom-left-radius'); + String get borderBottomRightRadius => getPropertyValue('border-bottom-right-radius'); String get borderRadius => getPropertyValue('border-radius'); String get perspective => getPropertyValue('perspective'); String get padding => getPropertyValue('padding'); @@ -879,12 +867,10 @@ extension DomCSSStyleDeclarationExtension on DomCSSStyleDeclaration { @JS('getPropertyValue') external JSString _getPropertyValue(JSString property); - String getPropertyValue(String property) => - _getPropertyValue(property.toJS).toDart; + String getPropertyValue(String property) => _getPropertyValue(property.toJS).toDart; @JS('setProperty') - external void _setProperty( - JSString propertyName, JSString value, JSString priority); + external void _setProperty(JSString propertyName, JSString value, JSString priority); void setProperty(String propertyName, String value, [String? priority]) { priority ??= ''; _setProperty(propertyName.toJS, value.toJS, priority.toJS); @@ -892,8 +878,7 @@ extension DomCSSStyleDeclarationExtension on DomCSSStyleDeclaration { @JS('removeProperty') external JSString _removeProperty(JSString property); - String removeProperty(String property) => - _removeProperty(property.toJS).toDart; + String removeProperty(String property) => _removeProperty(property.toJS).toDart; } @JS() @@ -951,8 +936,7 @@ class DomHTMLBodyElement extends DomHTMLElement {} @JS() @staticInterop -class DomHTMLImageElement extends DomHTMLElement - implements DomCanvasImageSource {} +class DomHTMLImageElement extends DomHTMLElement implements DomCanvasImageSource {} DomHTMLImageElement createDomHTMLImageElement() => domDocument.createElement('img') as DomHTMLImageElement; @@ -1018,8 +1002,7 @@ class DomHTMLScriptElement extends DomHTMLElement {} extension DomHTMLScriptElementExtension on DomHTMLScriptElement { @JS('src') external set _src(JSAny value); - set src(Object /* String|TrustedScriptURL */ value) => - _src = value.toJSAnyShallow; + set src(Object /* String|TrustedScriptURL */ value) => _src = value.toJSAnyShallow; @JS('nonce') external set _nonce(JSString? value); @@ -1027,8 +1010,7 @@ extension DomHTMLScriptElementExtension on DomHTMLScriptElement { } DomHTMLScriptElement createDomHTMLScriptElement(String? nonce) { - final DomHTMLScriptElement script = - domDocument.createElement('script') as DomHTMLScriptElement; + final DomHTMLScriptElement script = domDocument.createElement('script') as DomHTMLScriptElement; if (nonce != null) { script.nonce = nonce; } @@ -1084,8 +1066,7 @@ extension DomHTMLStyleElementExtension on DomHTMLStyleElement { } DomHTMLStyleElement createDomHTMLStyleElement(String? nonce) { - final DomHTMLStyleElement style = - domDocument.createElement('style') as DomHTMLStyleElement; + final DomHTMLStyleElement style = domDocument.createElement('style') as DomHTMLStyleElement; if (nonce != null) { style.nonce = nonce; } @@ -1103,9 +1084,11 @@ extension DomPerformanceExtension on DomPerformance { @JS('measure') external DomPerformanceMeasure? _measure( - JSString measureName, JSString? startMark, JSString? endMark); - DomPerformanceMeasure? measure( - String measureName, String? startMark, String? endMark) => + JSString measureName, + JSString? startMark, + JSString? endMark, + ); + DomPerformanceMeasure? measure(String measureName, String? startMark, String? endMark) => _measure(measureName.toJS, startMark?.toJS, endMark?.toJS); @JS('now') @@ -1135,8 +1118,7 @@ void debugResetCanvasCount() { DomCanvasElement createDomCanvasElement({int? width, int? height}) { debugCanvasCount++; - final DomCanvasElement canvas = - domWindow.document.createElement('canvas') as DomCanvasElement; + final DomCanvasElement canvas = domWindow.document.createElement('canvas') as DomCanvasElement; if (width != null) { canvas.width = width.toDouble(); } @@ -1181,8 +1163,7 @@ extension DomCanvasElementExtension on DomCanvasElement { } } - DomCanvasRenderingContext2D get context2D => - getContext('2d')! as DomCanvasRenderingContext2D; + DomCanvasRenderingContext2D get context2D => getContext('2d')! as DomCanvasRenderingContext2D; WebGLContext getGlContext(int majorVersion) { if (majorVersion == 1) { @@ -1265,23 +1246,30 @@ extension DomCanvasRenderingContext2DExtension on DomCanvasRenderingContext2D { @JS('createLinearGradient') external DomCanvasGradient _createLinearGradient( - JSNumber x0, JSNumber y0, JSNumber x1, JSNumber y1); + JSNumber x0, + JSNumber y0, + JSNumber x1, + JSNumber y1, + ); DomCanvasGradient createLinearGradient(num x0, num y0, num x1, num y1) => _createLinearGradient(x0.toJS, y0.toJS, x1.toJS, y1.toJS); @JS('createPattern') - external DomCanvasPattern? _createPattern( - JSAny image, JSString reptitionType); + external DomCanvasPattern? _createPattern(JSAny image, JSString reptitionType); DomCanvasPattern? createPattern(Object image, String reptitionType) => _createPattern(image.toJSAnyShallow, reptitionType.toJS); @JS('createRadialGradient') - external DomCanvasGradient _createRadialGradient(JSNumber x0, JSNumber y0, - JSNumber r0, JSNumber x1, JSNumber y1, JSNumber r1); - DomCanvasGradient createRadialGradient( - num x0, num y0, num r0, num x1, num y1, num r1) => - _createRadialGradient( - x0.toJS, y0.toJS, r0.toJS, x1.toJS, y1.toJS, r1.toJS); + external DomCanvasGradient _createRadialGradient( + JSNumber x0, + JSNumber y0, + JSNumber r0, + JSNumber x1, + JSNumber y1, + JSNumber r1, + ); + DomCanvasGradient createRadialGradient(num x0, num y0, num r0, num x1, num y1, num r1) => + _createRadialGradient(x0.toJS, y0.toJS, r0.toJS, x1.toJS, y1.toJS, r1.toJS); @JS('drawImage') external void _drawImage( @@ -1310,11 +1298,9 @@ extension DomCanvasRenderingContext2DExtension on DomCanvasRenderingContext2D { // In this case the numbers provided are the destination x and y offset. return _drawImage(source, srcxOrDstX.toJS, srcyOrDstY.toJS); } else { - assert(srcHeight != null && - dstX != null && - dstY != null && - dstWidth != null && - dstHeight != null); + assert( + srcHeight != null && dstX != null && dstY != null && dstWidth != null && dstHeight != null, + ); return _drawImage( source, srcxOrDstX.toJS, @@ -1340,14 +1326,12 @@ extension DomCanvasRenderingContext2DExtension on DomCanvasRenderingContext2D { } @JS('fillRect') - external void _fillRect( - JSNumber x, JSNumber y, JSNumber width, JSNumber height); + external void _fillRect(JSNumber x, JSNumber y, JSNumber width, JSNumber height); void fillRect(num x, num y, num width, num height) => _fillRect(x.toJS, y.toJS, width.toJS, height.toJS); @JS('fillText') - external void _fillText( - JSString text, JSNumber x, JSNumber y, [JSNumber maxWidth]); + external void _fillText(JSString text, JSNumber x, JSNumber y, [JSNumber maxWidth]); void fillText(String text, num x, num y, [num? maxWidth]) { if (maxWidth == null) { _fillText(text.toJS, x.toJS, y.toJS); @@ -1357,8 +1341,7 @@ extension DomCanvasRenderingContext2DExtension on DomCanvasRenderingContext2D { } @JS('getImageData') - external DomImageData _getImageData( - JSNumber x, JSNumber y, JSNumber sw, JSNumber sh); + external DomImageData _getImageData(JSNumber x, JSNumber y, JSNumber sw, JSNumber sh); DomImageData getImageData(int x, int y, int sw, int sh) => _getImageData(x.toJS, y.toJS, sw.toJS, sh.toJS); @@ -1378,23 +1361,26 @@ extension DomCanvasRenderingContext2DExtension on DomCanvasRenderingContext2D { external void stroke(); @JS('rect') - external void _rect( - JSNumber x, JSNumber y, JSNumber width, JSNumber height); - void rect(num x, num y, num width, num height) => - _rect(x.toJS, y.toJS, width.toJS, height.toJS); + external void _rect(JSNumber x, JSNumber y, JSNumber width, JSNumber height); + void rect(num x, num y, num width, num height) => _rect(x.toJS, y.toJS, width.toJS, height.toJS); external void resetTransform(); external void restore(); @JS('setTransform') external void _setTransform( - JSNumber a, JSNumber b, JSNumber c, JSNumber d, JSNumber e, JSNumber f); + JSNumber a, + JSNumber b, + JSNumber c, + JSNumber d, + JSNumber e, + JSNumber f, + ); void setTransform(num a, num b, num c, num d, num e, num f) => _setTransform(a.toJS, b.toJS, c.toJS, d.toJS, e.toJS, f.toJS); @JS('transform') - external void _transform( - JSNumber a, JSNumber b, JSNumber c, JSNumber d, JSNumber e, JSNumber f); + external void _transform(JSNumber a, JSNumber b, JSNumber c, JSNumber d, JSNumber e, JSNumber f); void transform(num a, num b, num c, num d, num e, num f) => _transform(a.toJS, b.toJS, c.toJS, d.toJS, e.toJS, f.toJS); @@ -1413,8 +1399,7 @@ extension DomCanvasRenderingContext2DExtension on DomCanvasRenderingContext2D { void scale(num x, num y) => _scale(x.toJS, y.toJS); @JS('clearRect') - external void _clearRect( - JSNumber x, JSNumber y, JSNumber width, JSNumber height); + external void _clearRect(JSNumber x, JSNumber y, JSNumber width, JSNumber height); void clearRect(num x, num y, num width, num height) => _clearRect(x.toJS, y.toJS, width.toJS, height.toJS); @@ -1427,22 +1412,25 @@ extension DomCanvasRenderingContext2DExtension on DomCanvasRenderingContext2D { void rotate(num angle) => _rotate(angle.toJS); @JS('bezierCurveTo') - external void _bezierCurveTo(JSNumber cp1x, JSNumber cp1y, JSNumber cp2x, - JSNumber cp2y, JSNumber x, JSNumber y); + external void _bezierCurveTo( + JSNumber cp1x, + JSNumber cp1y, + JSNumber cp2x, + JSNumber cp2y, + JSNumber x, + JSNumber y, + ); void bezierCurveTo(num cp1x, num cp1y, num cp2x, num cp2y, num x, num y) => - _bezierCurveTo( - cp1x.toJS, cp1y.toJS, cp2x.toJS, cp2y.toJS, x.toJS, y.toJS); + _bezierCurveTo(cp1x.toJS, cp1y.toJS, cp2x.toJS, cp2y.toJS, x.toJS, y.toJS); @JS('quadraticCurveTo') - external void _quadraticCurveTo( - JSNumber cpx, JSNumber cpy, JSNumber x, JSNumber y); + external void _quadraticCurveTo(JSNumber cpx, JSNumber cpy, JSNumber x, JSNumber y); void quadraticCurveTo(num cpx, num cpy, num x, num y) => _quadraticCurveTo(cpx.toJS, cpy.toJS, x.toJS, y.toJS); @JS('globalCompositeOperation') external set _globalCompositeOperation(JSString value); - set globalCompositeOperation(String value) => - _globalCompositeOperation = value.toJS; + set globalCompositeOperation(String value) => _globalCompositeOperation = value.toJS; @JS('lineCap') external set _lineCap(JSString value); @@ -1457,12 +1445,16 @@ extension DomCanvasRenderingContext2DExtension on DomCanvasRenderingContext2D { set shadowBlur(num value) => _shadowBlur = value.toJS; @JS('arc') - external void _arc(JSNumber x, JSNumber y, JSNumber radius, - JSNumber startAngle, JSNumber endAngle, JSBoolean antiClockwise); - void arc(num x, num y, num radius, num startAngle, num endAngle, - [bool antiClockwise = false]) => - _arc(x.toJS, y.toJS, radius.toJS, startAngle.toJS, endAngle.toJS, - antiClockwise.toJS); + external void _arc( + JSNumber x, + JSNumber y, + JSNumber radius, + JSNumber startAngle, + JSNumber endAngle, + JSBoolean antiClockwise, + ); + void arc(num x, num y, num radius, num startAngle, num endAngle, [bool antiClockwise = false]) => + _arc(x.toJS, y.toJS, radius.toJS, startAngle.toJS, endAngle.toJS, antiClockwise.toJS); @JS('filter') external set _filter(JSString? value); @@ -1482,23 +1474,38 @@ extension DomCanvasRenderingContext2DExtension on DomCanvasRenderingContext2D { @JS('ellipse') external void _ellipse( - JSNumber x, - JSNumber y, - JSNumber radiusX, - JSNumber radiusY, - JSNumber rotation, - JSNumber startAngle, - JSNumber endAngle, - JSBoolean? antiClockwise); - void ellipse(num x, num y, num radiusX, num radiusY, num rotation, - num startAngle, num endAngle, bool? antiClockwise) => - _ellipse(x.toJS, y.toJS, radiusX.toJS, radiusY.toJS, rotation.toJS, - startAngle.toJS, endAngle.toJS, antiClockwise?.toJS); + JSNumber x, + JSNumber y, + JSNumber radiusX, + JSNumber radiusY, + JSNumber rotation, + JSNumber startAngle, + JSNumber endAngle, + JSBoolean? antiClockwise, + ); + void ellipse( + num x, + num y, + num radiusX, + num radiusY, + num rotation, + num startAngle, + num endAngle, + bool? antiClockwise, + ) => _ellipse( + x.toJS, + y.toJS, + radiusX.toJS, + radiusY.toJS, + rotation.toJS, + startAngle.toJS, + endAngle.toJS, + antiClockwise?.toJS, + ); @JS('strokeText') external void _strokeText(JSString text, JSNumber x, JSNumber y); - void strokeText(String text, num x, num y) => - _strokeText(text.toJS, x.toJS, y.toJS); + void strokeText(String text, num x, num y) => _strokeText(text.toJS, x.toJS, y.toJS); @JS('globalAlpha') external set _globalAlpha(JSNumber? value); @@ -1509,8 +1516,7 @@ extension DomCanvasRenderingContext2DExtension on DomCanvasRenderingContext2D { @staticInterop class DomCanvasRenderingContextWebGl {} -extension DomCanvasRenderingContextWebGlExtension - on DomCanvasRenderingContextWebGl { +extension DomCanvasRenderingContextWebGlExtension on DomCanvasRenderingContextWebGl { @JS('isContextLost') external JSBoolean _isContextLost(); bool isContextLost() => _isContextLost().toDart; @@ -1534,8 +1540,7 @@ class DomImageData { DomImageData createDomImageData(Object data, int sw, int sh) => DomImageData._(data.toJSAnyShallow, sw.toJS, sh.toJS); -DomImageData createBlankDomImageData(int sw, int sh) => - DomImageData._empty(sw.toJS, sh.toJS); +DomImageData createBlankDomImageData(int sw, int sh) => DomImageData._empty(sw.toJS, sh.toJS); extension DomImageDataExtension on DomImageData { @JS('data') @@ -1564,8 +1569,7 @@ class DomCanvasGradient {} extension DomCanvasGradientExtension on DomCanvasGradient { @JS('addColorStop') external void _addColorStop(JSNumber offset, JSString color); - void addColorStop(num offset, String color) => - _addColorStop(offset.toJS, color.toJS); + void addColorStop(num offset, String color) => _addColorStop(offset.toJS, color.toJS); } @JS() @@ -1575,8 +1579,7 @@ class DomXMLHttpRequestEventTarget extends DomEventTarget {} Future rawHttpGet(String url) => js_util.promiseToFuture(domWindow._fetch(url.toJS)); -typedef MockHttpFetchResponseFactory = Future Function( - String url); +typedef MockHttpFetchResponseFactory = Future Function(String url); MockHttpFetchResponseFactory? mockHttpFetchResponseFactory; @@ -1595,8 +1598,7 @@ MockHttpFetchResponseFactory? mockHttpFetchResponseFactory; /// [httpFetchText] instead. Future httpFetch(String url) async { if (mockHttpFetchResponseFactory != null) { - final MockHttpFetchResponse? response = - await mockHttpFetchResponseFactory!(url); + final MockHttpFetchResponse? response = await mockHttpFetchResponseFactory!(url); if (response != null) { return response; } @@ -1609,16 +1611,16 @@ Future httpFetch(String url) async { } } -Future _rawHttpPost(String url, String data) => - js_util.promiseToFuture(domWindow._fetch( - url.toJS, - { - 'method': 'POST', - 'headers': { - 'Content-Type': 'text/plain', - }, - 'body': data, - }.toJSAnyDeep)); +Future _rawHttpPost(String url, String data) => js_util.promiseToFuture( + domWindow._fetch( + url.toJS, + { + 'method': 'POST', + 'headers': {'Content-Type': 'text/plain'}, + 'body': data, + }.toJSAnyDeep, + ), +); /// Sends a [data] string as HTTP POST request to [url]. /// @@ -1837,11 +1839,9 @@ class HttpFetchPayloadImpl implements HttpFetchPayload { typedef MockOnRead = Future Function(HttpFetchReader callback); class MockHttpFetchPayload implements HttpFetchPayload { - MockHttpFetchPayload({ - required ByteBuffer byteBuffer, - int? chunkSize, - }) : _byteBuffer = byteBuffer, - _chunkSize = chunkSize ?? 64; + MockHttpFetchPayload({required ByteBuffer byteBuffer, int? chunkSize}) + : _byteBuffer = byteBuffer, + _chunkSize = chunkSize ?? 64; final ByteBuffer _byteBuffer; final int _chunkSize; @@ -1853,7 +1853,10 @@ class MockHttpFetchPayload implements HttpFetchPayload { while (currentIndex < totalLength) { final int chunkSize = math.min(_chunkSize, totalLength - currentIndex); final Uint8List chunk = Uint8List.sublistView( - _byteBuffer.asByteData(), currentIndex, currentIndex + chunkSize); + _byteBuffer.asByteData(), + currentIndex, + currentIndex + chunkSize, + ); callback(chunk.toJS as T); currentIndex += chunkSize; } @@ -1863,12 +1866,10 @@ class MockHttpFetchPayload implements HttpFetchPayload { Future asByteBuffer() async => _byteBuffer; @override - Future json() async => - throw AssertionError('json not supported by mock'); + Future json() async => throw AssertionError('json not supported by mock'); @override - Future text() async => - throw AssertionError('text not supported by mock'); + Future text() async => throw AssertionError('text not supported by mock'); } /// Indicates a missing HTTP payload when one was expected, such as when @@ -1936,8 +1937,7 @@ extension DomResponseExtension on DomResponse { @JS('arrayBuffer') external JSPromise _arrayBuffer(); - Future arrayBuffer() => - js_util.promiseToFuture(_arrayBuffer()); + Future arrayBuffer() => js_util.promiseToFuture(_arrayBuffer()); @JS('json') external JSPromise _json(); @@ -1973,8 +1973,7 @@ class _DomStreamReader {} extension _DomStreamReaderExtension on _DomStreamReader { @JS('read') external JSPromise _read(); - Future<_DomStreamChunk> read() => - js_util.promiseToFuture<_DomStreamChunk>(_read()); + Future<_DomStreamChunk> read() => js_util.promiseToFuture<_DomStreamChunk>(_read()); } @JS() @@ -2070,25 +2069,21 @@ DomRect createDomRectFromPoints(DomPoint a, DomPoint b) { @JS('DOMRect') @staticInterop class DomRect extends DomRectReadOnly { - external factory DomRect( - JSNumber left, JSNumber top, JSNumber width, JSNumber height); + external factory DomRect(JSNumber left, JSNumber top, JSNumber width, JSNumber height); } @JS('FontFace') @staticInterop class DomFontFace { external factory DomFontFace._args2(JSString family, JSAny source); - external factory DomFontFace._args3( - JSString family, JSAny source, JSAny descriptors); + external factory DomFontFace._args3(JSString family, JSAny source, JSAny descriptors); } -DomFontFace createDomFontFace(String family, Object source, - [Map? descriptors]) { +DomFontFace createDomFontFace(String family, Object source, [Map? descriptors]) { if (descriptors == null) { return DomFontFace._args2(family.toJS, source.toJSAnyShallow); } else { - return DomFontFace._args3( - family.toJS, source.toJSAnyShallow, descriptors.toJSAnyDeep); + return DomFontFace._args3(family.toJS, source.toJSAnyShallow, descriptors.toJSAnyDeep); } } @@ -2120,12 +2115,11 @@ extension DomFontFaceSetExtension on DomFontFaceSet { @JS('forEach') external void _forEach(JSFunction callback); - void forEach(DomFontFaceSetForEachCallback callback) => - _forEach(callback.toJS); + void forEach(DomFontFaceSetForEachCallback callback) => _forEach(callback.toJS); } -typedef DomFontFaceSetForEachCallback = void Function( - DomFontFace fontFace, DomFontFace fontFaceAgain, DomFontFaceSet set); +typedef DomFontFaceSetForEachCallback = + void Function(DomFontFace fontFace, DomFontFace fontFaceAgain, DomFontFaceSet set); @JS() @staticInterop @@ -2188,8 +2182,7 @@ extension DomHTMLTextAreaElementExtension on DomHTMLTextAreaElement { String? get value => _value?.toDart; @JS('setSelectionRange') - external void _setSelectionRange( - JSNumber start, JSNumber end, [JSString direction]); + external void _setSelectionRange(JSNumber start, JSNumber end, [JSString direction]); void setSelectionRange(int start, int end, [String? direction]) { if (direction == null) { _setSelectionRange(start.toJS, end.toJS); @@ -2218,8 +2211,7 @@ extension DomClipboardExtension on DomClipboard { @JS('writeText') external JSPromise _writeText(JSString data); - Future writeText(String data) => - js_util.promiseToFuture(_writeText(data.toJS)); + Future writeText(String data) => js_util.promiseToFuture(_writeText(data.toJS)); } @JS() @@ -2280,8 +2272,7 @@ extension DomKeyboardEventExtension on DomKeyboardEvent { bool getModifierState(String keyArg) => _getModifierState(keyArg.toJS).toDart; } -DomKeyboardEvent createDomKeyboardEvent(String type, - [Map? init]) { +DomKeyboardEvent createDomKeyboardEvent(String type, [Map? init]) { if (init == null) { return DomKeyboardEvent(type.toJS); } else { @@ -2352,8 +2343,7 @@ class DomPopStateEvent extends DomEvent { external factory DomPopStateEvent(JSString type, [JSAny initDict]); } -DomPopStateEvent createDomPopStateEvent( - String type, Map? eventInitDict) { +DomPopStateEvent createDomPopStateEvent(String type, Map? eventInitDict) { if (eventInitDict == null) { return DomPopStateEvent(type.toJS); } else { @@ -2379,8 +2369,7 @@ DomURL createDomURL(String url, [String? base]) => extension DomURLExtension on DomURL { @JS('createObjectURL') external JSString _createObjectURL(JSAny object); - String createObjectURL(Object object) => - _createObjectURL(object.toJSAnyShallow).toDart; + String createObjectURL(Object object) => _createObjectURL(object.toJSAnyShallow).toDart; @JS('revokeObjectURL') external void _revokeObjectURL(JSString url); @@ -2406,13 +2395,11 @@ DomBlob createDomBlob(List parts, [Map? options]) { if (options == null) { return DomBlob(parts.toJSAnyShallow as JSArray); } else { - return DomBlob.withOptions( - parts.toJSAnyShallow as JSArray, options.toJSAnyDeep); + return DomBlob.withOptions(parts.toJSAnyShallow as JSArray, options.toJSAnyDeep); } } -typedef DomMutationCallback = void Function( - JSArray mutation, DomMutationObserver observer); +typedef DomMutationCallback = void Function(JSArray mutation, DomMutationObserver observer); @JS('MutationObserver') @staticInterop @@ -2428,12 +2415,11 @@ extension DomMutationObserverExtension on DomMutationObserver { @JS('observe') external void _observe(DomNode target, JSAny options); - void observe(DomNode target, - {bool? childList, bool? attributes, List? attributeFilter}) { + void observe(DomNode target, {bool? childList, bool? attributes, List? attributeFilter}) { final Map options = { if (childList != null) 'childList': childList, if (attributes != null) 'attributes': attributes, - if (attributeFilter != null) 'attributeFilter': attributeFilter + if (attributeFilter != null) 'attributeFilter': attributeFilter, }; return _observe(target, options.toJSAnyDeep); } @@ -2627,8 +2613,7 @@ extension DomPointerEventExtension on DomPointerEvent { _getCoalescedEvents().toDart.cast(); } -DomPointerEvent createDomPointerEvent(String type, - [Map? init]) { +DomPointerEvent createDomPointerEvent(String type, [Map? init]) { if (init == null) { return DomPointerEvent(type.toJS); } else { @@ -2697,8 +2682,7 @@ extension DomTouchEventExtension on DomTouchEvent { @JS('changedTouches') external _DomTouchList get _changedTouches; - Iterable get changedTouches => - createDomTouchListWrapper(_changedTouches); + Iterable get changedTouches => createDomTouchListWrapper(_changedTouches); } @JS('Touch') @@ -2743,8 +2727,7 @@ extension DomCompositionEventExtension on DomCompositionEvent { String? get data => _data?.toDart; } -DomCompositionEvent createDomCompositionEvent(String type, - [Map? options]) { +DomCompositionEvent createDomCompositionEvent(String type, [Map? options]) { if (options == null) { return DomCompositionEvent(type.toJS); } else { @@ -2842,8 +2825,7 @@ extension DomHTMLInputElementExtension on DomHTMLInputElement { set selectionEnd(double? value) => _selectionEnd = value?.toJS; @JS('setSelectionRange') - external void _setSelectionRange( - JSNumber start, JSNumber end, [JSString direction]); + external void _setSelectionRange(JSNumber start, JSNumber end, [JSString direction]); void setSelectionRange(int start, int end, [String? direction]) { if (direction == null) { _setSelectionRange(start.toJS, end.toJS); @@ -2972,8 +2954,7 @@ extension DomOffscreenCanvasExtension on DomOffscreenCanvas { @JS('transferToImageBitmap') external JSAny? _transferToImageBitmap(); - DomImageBitmap transferToImageBitmap() => - _transferToImageBitmap()! as DomImageBitmap; + DomImageBitmap transferToImageBitmap() => _transferToImageBitmap()! as DomImageBitmap; } DomOffscreenCanvas createDomOffscreenCanvas(int width, int height) => @@ -3029,8 +3010,7 @@ extension DomShadowRootExtension on DomShadowRoot { @JS('elementFromPoint') external DomElement? _elementFromPoint(JSNumber x, JSNumber y); - DomElement? elementFromPoint(int x, int y) => - _elementFromPoint(x.toJS, y.toJS); + DomElement? elementFromPoint(int x, int y) => _elementFromPoint(x.toJS, y.toJS); } @JS() @@ -3044,8 +3024,7 @@ class DomCSSStyleSheet extends DomStyleSheet {} extension DomCSSStyleSheetExtension on DomCSSStyleSheet { @JS('cssRules') external _DomList get _cssRules; - Iterable get cssRules => - createDomListWrapper(_cssRules); + Iterable get cssRules => createDomListWrapper(_cssRules); @JS('insertRule') external JSNumber _insertRule(JSString rule, [JSNumber index]); @@ -3086,8 +3065,7 @@ class DomScreenOrientation extends DomEventTarget {} extension DomScreenOrientationExtension on DomScreenOrientation { @JS('lock') external JSPromise _lock(JSString orientation); - Future lock(String orientation) => - js_util.promiseToFuture(_lock(orientation.toJS)); + Future lock(String orientation) => js_util.promiseToFuture(_lock(orientation.toJS)); external void unlock(); } @@ -3096,10 +3074,9 @@ extension DomScreenOrientationExtension on DomScreenOrientation { // event listener of the requested type to the target. Calling [cancel] will // remove the listener. class DomSubscription { - DomSubscription( - this.target, String typeString, DartDomEventListener dartListener) - : type = typeString.toJS, - listener = createDomEventListener(dartListener) { + DomSubscription(this.target, String typeString, DartDomEventListener dartListener) + : type = typeString.toJS, + listener = createDomEventListener(dartListener) { target._addEventListener(type, listener); } @@ -3233,18 +3210,18 @@ abstract class DomResizeObserver { /// /// Internally converts the `List` of entries into the expected /// `List` -DomResizeObserver? createDomResizeObserver(DomResizeObserverCallbackFn fn) => - DomResizeObserver((JSArray entries, DomResizeObserver observer) { - fn(entries.toDart.cast(), observer); - }.toJS); +DomResizeObserver? createDomResizeObserver(DomResizeObserverCallbackFn fn) => DomResizeObserver( + (JSArray entries, DomResizeObserver observer) { + fn(entries.toDart.cast(), observer); + }.toJS, +); /// ResizeObserver instance methods. /// /// See: https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver#instance_methods extension DomResizeObserverExtension on DomResizeObserver { external void disconnect(); - external void observe(DomElement target, - [DomResizeObserverObserveOptions options]); + external void observe(DomElement target, [DomResizeObserverObserveOptions options]); external void unobserve(DomElement target); } @@ -3255,14 +3232,12 @@ extension DomResizeObserverExtension on DomResizeObserver { @staticInterop @anonymous abstract class DomResizeObserverObserveOptions { - external factory DomResizeObserverObserveOptions({ - JSString box, - }); + external factory DomResizeObserverObserveOptions({JSString box}); } /// Type of the function used to create a Resize Observer. -typedef DomResizeObserverCallbackFn = void Function( - List entries, DomResizeObserver observer); +typedef DomResizeObserverCallbackFn = + void Function(List entries, DomResizeObserver observer); /// The object passed to the [DomResizeObserverCallbackFn], which allows access to the new dimensions of the observed element. /// @@ -3320,9 +3295,7 @@ abstract class DomTrustedTypePolicyOptions { /// /// `createScriptURL` is a callback function that contains code to run when /// creating a TrustedScriptURL object. - external factory DomTrustedTypePolicyOptions({ - JSFunction? createScriptURL, - }); + external factory DomTrustedTypePolicyOptions({JSFunction? createScriptURL}); } /// Type of the function used to configure createScriptURL. @@ -3346,8 +3319,7 @@ extension DomTrustedTypePolicyExtension on DomTrustedTypePolicy { /// `input` is a string containing the data to be _sanitized_ by the policy. @JS('createScriptURL') external DomTrustedScriptURL _createScriptURL(JSString input); - DomTrustedScriptURL createScriptURL(String input) => - _createScriptURL(input.toJS); + DomTrustedScriptURL createScriptURL(String input) => _createScriptURL(input.toJS); } /// Represents a string that a developer can insert into an _injection sink_ @@ -3371,27 +3343,28 @@ extension DomTrustedScriptUrlExtension on DomTrustedScriptURL { // The expected set of files that the flutter-engine TrustedType policy is going // to accept as valid. -const Set _expectedFilesForTT = { - 'canvaskit.js', -}; +const Set _expectedFilesForTT = {'canvaskit.js'}; // The definition of the `flutter-engine` TrustedType policy. // Only accessible if the Trusted Types API is available. final DomTrustedTypePolicy _ttPolicy = domWindow.trustedTypes!.createPolicy( 'flutter-engine'.toJS, DomTrustedTypePolicyOptions( - // Validates the given [url]. - createScriptURL: (JSString url) { - final Uri uri = Uri.parse(url.toDart); - if (_expectedFilesForTT.contains(uri.pathSegments.last)) { - return uri.toString().toJS; - } - domWindow.console - .error('URL rejected by TrustedTypes policy flutter-engine: $url' - '(download prevented)'); - - return null; - }.toJS), + // Validates the given [url]. + createScriptURL: + (JSString url) { + final Uri uri = Uri.parse(url.toDart); + if (_expectedFilesForTT.contains(uri.pathSegments.last)) { + return uri.toString().toJS; + } + domWindow.console.error( + 'URL rejected by TrustedTypes policy flutter-engine: $url' + '(download prevented)', + ); + + return null; + }.toJS, + ), ); /// Converts a String `url` into a [DomTrustedScriptURL] object when the @@ -3465,8 +3438,7 @@ class _DomListWrapper extends Iterable { /// This is a work around for a `TypeError` which can be triggered by calling /// `toList` on the `Iterable`. -Iterable createDomListWrapper(_DomList list) => - _DomListWrapper._(list).cast(); +Iterable createDomListWrapper(_DomList list) => _DomListWrapper._(list).cast(); // https://developer.mozilla.org/en-US/docs/Web/API/TouchList @JS() @@ -3564,8 +3536,8 @@ class DomSegments {} extension DomSegmentsExtension on DomSegments { DomIteratorWrapper iterator() { - final DomIterator segmentIterator = js_util - .callMethod(this, domSymbol.iterator, const []) as DomIterator; + final DomIterator segmentIterator = + js_util.callMethod(this, domSymbol.iterator, const []) as DomIterator; return DomIteratorWrapper(segmentIterator); } } @@ -3639,10 +3611,7 @@ DomSegmenter createIntlSegmenter({required String granularity}) { throw UnimplementedError('Intl.Segmenter() is not supported.'); } - return DomSegmenter( - [].toJS, - {'granularity': granularity}.toJSAnyDeep, - ); + return DomSegmenter([].toJS, {'granularity': granularity}.toJSAnyDeep); } @JS('Intl.v8BreakIterator') @@ -3677,8 +3646,7 @@ DomV8BreakIterator createV8BreakIterator() { throw UnimplementedError('v8BreakIterator is not supported.'); } - return DomV8BreakIterator( - [].toJS, const {'type': 'line'}.toJSAnyDeep); + return DomV8BreakIterator([].toJS, const {'type': 'line'}.toJSAnyDeep); } @JS('TextDecoder') @@ -3699,11 +3667,14 @@ class DomFinalizationRegistry { extension DomFinalizationRegistryExtension on DomFinalizationRegistry { @JS('register') - external void register( - ExternalDartReference target, ExternalDartReference value); + external void register(ExternalDartReference target, ExternalDartReference value); @JS('register') - external void registerWithToken(ExternalDartReference target, ExternalDartReference value, ExternalDartReference token); + external void registerWithToken( + ExternalDartReference target, + ExternalDartReference value, + ExternalDartReference token, + ); @JS('unregister') external void unregister(ExternalDartReference token); @@ -3713,8 +3684,7 @@ extension DomFinalizationRegistryExtension on DomFinalizationRegistry { external JSAny? get _finalizationRegistryConstructor; /// Whether the current browser supports `FinalizationRegistry`. -bool browserSupportsFinalizationRegistry = - _finalizationRegistryConstructor != null; +bool browserSupportsFinalizationRegistry = _finalizationRegistryConstructor != null; @JS('window.OffscreenCanvas') external JSAny? get _offscreenCanvasConstructor; diff --git a/lib/web_ui/lib/src/engine/engine_canvas.dart b/lib/web_ui/lib/src/engine/engine_canvas.dart index 7819ba4da17ec..f49bc3a156eac 100644 --- a/lib/web_ui/lib/src/engine/engine_canvas.dart +++ b/lib/web_ui/lib/src/engine/engine_canvas.dart @@ -67,18 +67,15 @@ abstract class EngineCanvas { void drawPath(ui.Path path, SurfacePaintData paint); - void drawShadow( - ui.Path path, ui.Color color, double elevation, bool transparentOccluder); + void drawShadow(ui.Path path, ui.Color color, double elevation, bool transparentOccluder); void drawImage(ui.Image image, ui.Offset p, SurfacePaintData paint); - void drawImageRect( - ui.Image image, ui.Rect src, ui.Rect dst, SurfacePaintData paint); + void drawImageRect(ui.Image image, ui.Rect src, ui.Rect dst, SurfacePaintData paint); void drawParagraph(CanvasParagraph paragraph, ui.Offset offset); - void drawVertices( - SurfaceVertices vertices, ui.BlendMode blendMode, SurfacePaintData paint); + void drawVertices(SurfaceVertices vertices, ui.BlendMode blendMode, SurfacePaintData paint); void drawPoints(ui.PointMode pointMode, Float32List points, SurfacePaintData paint); @@ -104,10 +101,7 @@ Matrix4 transformWithOffset(Matrix4 transform, ui.Offset offset) { } class SaveStackEntry { - SaveStackEntry({ - required this.transform, - required this.clipStack, - }); + SaveStackEntry({required this.transform, required this.clipStack}); final Matrix4 transform; final List? clipStack; @@ -115,15 +109,9 @@ class SaveStackEntry { /// Tagged union of clipping parameters used for canvas. class SaveClipEntry { - SaveClipEntry.rect(this.rect, this.currentTransform) - : rrect = null, - path = null; - SaveClipEntry.rrect(this.rrect, this.currentTransform) - : rect = null, - path = null; - SaveClipEntry.path(this.path, this.currentTransform) - : rect = null, - rrect = null; + SaveClipEntry.rect(this.rect, this.currentTransform) : rrect = null, path = null; + SaveClipEntry.rrect(this.rrect, this.currentTransform) : rect = null, path = null; + SaveClipEntry.path(this.path, this.currentTransform) : rect = null, rrect = null; final ui.Rect? rect; final ui.RRect? rrect; @@ -163,11 +151,12 @@ mixin SaveStackTracking on EngineCanvas { /// Classes that override this method must call `super.save()`. @override void save() { - _saveStack.add(SaveStackEntry( - transform: _currentTransform.clone(), - clipStack: - _clipStack == null ? null : List.from(_clipStack!), - )); + _saveStack.add( + SaveStackEntry( + transform: _currentTransform.clone(), + clipStack: _clipStack == null ? null : List.from(_clipStack!), + ), + ); } /// Restores current clip and transform from the save stack. @@ -255,29 +244,19 @@ mixin SaveStackTracking on EngineCanvas { } } -DomElement drawParagraphElement( - CanvasParagraph paragraph, - ui.Offset offset, { - Matrix4? transform, -}) { +DomElement drawParagraphElement(CanvasParagraph paragraph, ui.Offset offset, {Matrix4? transform}) { assert(paragraph.isLaidOut); final DomElement paragraphElement = paragraph.toDomElement(); if (transform != null) { - setElementTransform( - paragraphElement, - transformWithOffset(transform, offset).storage, - ); + setElementTransform(paragraphElement, transformWithOffset(transform, offset).storage); } return paragraphElement; } class _SaveElementStackEntry { - _SaveElementStackEntry({ - required this.savedElement, - required this.transform, - }); + _SaveElementStackEntry({required this.savedElement, required this.transform}); final DomElement savedElement; final Matrix4 transform; @@ -290,8 +269,7 @@ mixin SaveElementStackTracking on EngineCanvas { /// The element at the top of the element stack, or [rootElement] if the stack /// is empty. - DomElement get currentElement => - _elementStack.isEmpty ? rootElement : _elementStack.last; + DomElement get currentElement => _elementStack.isEmpty ? rootElement : _elementStack.last; /// The stack that maintains the DOM elements used to express certain paint /// operations, such as clips. @@ -325,10 +303,9 @@ mixin SaveElementStackTracking on EngineCanvas { /// Classes that override this method must call `super.save()`. @override void save() { - _saveStack.add(_SaveElementStackEntry( - savedElement: currentElement, - transform: _currentTransform.clone(), - )); + _saveStack.add( + _SaveElementStackEntry(savedElement: currentElement, transform: _currentTransform.clone()), + ); } /// Restores current clip and transform from the save stack. diff --git a/lib/web_ui/lib/src/engine/font_change_util.dart b/lib/web_ui/lib/src/engine/font_change_util.dart index 12cbe8f94c717..3444f3bca3651 100644 --- a/lib/web_ui/lib/src/engine/font_change_util.dart +++ b/lib/web_ui/lib/src/engine/font_change_util.dart @@ -9,9 +9,9 @@ import 'dom.dart'; import 'platform_dispatcher.dart'; import 'services.dart'; -final ByteData? _fontChangeMessage = - const JSONMessageCodec().encodeMessage( - {'type': 'fontsChange'}); +final ByteData? _fontChangeMessage = const JSONMessageCodec().encodeMessage({ + 'type': 'fontsChange', +}); // Font load callbacks will typically arrive in sequence, we want to prevent // sendFontChangeMessage of causing multiple synchronous rebuilds. diff --git a/lib/web_ui/lib/src/engine/font_fallback_data.dart b/lib/web_ui/lib/src/engine/font_fallback_data.dart index a6dcfb334ddc0..91590c601f985 100644 --- a/lib/web_ui/lib/src/engine/font_fallback_data.dart +++ b/lib/web_ui/lib/src/engine/font_fallback_data.dart @@ -7,730 +7,2809 @@ import 'noto_font.dart'; List getFallbackFontList() => [ - NotoFont('Noto Color Emoji 0', 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.0.woff2'), - NotoFont('Noto Color Emoji 1', 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.1.woff2'), - NotoFont('Noto Color Emoji 2', 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.2.woff2'), - NotoFont('Noto Color Emoji 3', 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.3.woff2'), - NotoFont('Noto Color Emoji 4', 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.4.woff2'), - NotoFont('Noto Color Emoji 5', 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.5.woff2'), - NotoFont('Noto Color Emoji 6', 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.6.woff2'), - NotoFont('Noto Color Emoji 7', 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.7.woff2'), - NotoFont('Noto Color Emoji 8', 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.8.woff2'), - NotoFont('Noto Color Emoji 9', 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.9.woff2'), - NotoFont('Noto Color Emoji 10', 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.10.woff2'), - NotoFont('Noto Color Emoji 11', 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.11.woff2'), - NotoFont('Noto Sans Symbols 2 0', 'notosanssymbols2/v24/I_uyMoGduATTei9eI8daxVHDyfisHr71-jrBWXPM4Q.woff2'), - NotoFont('Noto Sans Symbols 2 1', 'notosanssymbols2/v24/I_uyMoGduATTei9eI8daxVHDyfisHr71-ujgfE71.woff2'), - NotoFont('Noto Sans Symbols 2 2', 'notosanssymbols2/v24/I_uyMoGduATTei9eI8daxVHDyfisHr71-gTBWXPM4Q.woff2'), - NotoFont('Noto Sans Symbols 2 3', 'notosanssymbols2/v24/I_uyMoGduATTei9eI8daxVHDyfisHr71-vrgfE71.woff2'), - NotoFont('Noto Sans Symbols 2 4', 'notosanssymbols2/v24/I_uyMoGduATTei9eI8daxVHDyfisHr71-prgfE71.woff2'), - NotoFont('Noto Sans Symbols 2 5', 'notosanssymbols2/v24/I_uyMoGduATTei9eI8daxVHDyfisHr71-pTgfA.woff2'), - NotoFont('Noto Sans Cuneiform 0', 'notosanscuneiform/v17/bMrrmTWK7YY-MF22aHGGd7H8PhJtvBDWse5DlCQu.woff2'), - NotoFont('Noto Sans Cuneiform 1', 'notosanscuneiform/v17/bMrrmTWK7YY-MF22aHGGd7H8PhJtvBDWsbZDlCQu.woff2'), - NotoFont('Noto Sans Cuneiform 2', 'notosanscuneiform/v17/bMrrmTWK7YY-MF22aHGGd7H8PhJtvBDWsbhDlA.woff2'), - NotoFont('Noto Sans Duployan 0', 'notosansduployan/v18/gokzH7nwAEdtF9N8-mdTDx_X9JM5wsvbi-kD5F8a.woff2'), - NotoFont('Noto Sans Duployan 1', 'notosansduployan/v18/gokzH7nwAEdtF9N8-mdTDx_X9JM5wsvbH8gm2WY.woff2'), - NotoFont('Noto Sans Duployan 2', 'notosansduployan/v18/gokzH7nwAEdtF9N8-mdTDx_X9JM5wsvbEcgm.woff2'), - NotoFont('Noto Sans Egyptian Hieroglyphs 0', 'notosansegyptianhieroglyphs/v29/vEF42-tODB8RrNDvZSUmRhcQHzx1s7y_F9-j3qSzEcbEYintdVi99Rg.woff2'), - NotoFont('Noto Sans Egyptian Hieroglyphs 1', 'notosansegyptianhieroglyphs/v29/vEF42-tODB8RrNDvZSUmRhcQHzx1s7y_F9-j3qSzEcbEYintQFi99Rg.woff2'), - NotoFont('Noto Sans Egyptian Hieroglyphs 2', 'notosansegyptianhieroglyphs/v29/vEF42-tODB8RrNDvZSUmRhcQHzx1s7y_F9-j3qSzEcbEYintTli9.woff2'), - NotoFont('Noto Sans HK 0', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.0.woff2'), - NotoFont('Noto Sans HK 1', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.1.woff2'), - NotoFont('Noto Sans HK 2', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.2.woff2'), - NotoFont('Noto Sans HK 3', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.3.woff2'), - NotoFont('Noto Sans HK 4', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.4.woff2'), - NotoFont('Noto Sans HK 5', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.5.woff2'), - NotoFont('Noto Sans HK 6', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.6.woff2'), - NotoFont('Noto Sans HK 7', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.7.woff2'), - NotoFont('Noto Sans HK 8', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.8.woff2'), - NotoFont('Noto Sans HK 9', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.9.woff2'), - NotoFont('Noto Sans HK 10', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.10.woff2'), - NotoFont('Noto Sans HK 11', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.15.woff2'), - NotoFont('Noto Sans HK 12', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.16.woff2'), - NotoFont('Noto Sans HK 13', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.17.woff2'), - NotoFont('Noto Sans HK 14', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.25.woff2'), - NotoFont('Noto Sans HK 15', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.26.woff2'), - NotoFont('Noto Sans HK 16', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.27.woff2'), - NotoFont('Noto Sans HK 17', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.28.woff2'), - NotoFont('Noto Sans HK 18', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.29.woff2'), - NotoFont('Noto Sans HK 19', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.30.woff2'), - NotoFont('Noto Sans HK 20', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.31.woff2'), - NotoFont('Noto Sans HK 21', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.32.woff2'), - NotoFont('Noto Sans HK 22', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.33.woff2'), - NotoFont('Noto Sans HK 23', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.34.woff2'), - NotoFont('Noto Sans HK 24', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.35.woff2'), - NotoFont('Noto Sans HK 25', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.36.woff2'), - NotoFont('Noto Sans HK 26', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.37.woff2'), - NotoFont('Noto Sans HK 27', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.38.woff2'), - NotoFont('Noto Sans HK 28', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.39.woff2'), - NotoFont('Noto Sans HK 29', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.40.woff2'), - NotoFont('Noto Sans HK 30', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.41.woff2'), - NotoFont('Noto Sans HK 31', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.42.woff2'), - NotoFont('Noto Sans HK 32', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.43.woff2'), - NotoFont('Noto Sans HK 33', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.44.woff2'), - NotoFont('Noto Sans HK 34', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.45.woff2'), - NotoFont('Noto Sans HK 35', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.46.woff2'), - NotoFont('Noto Sans HK 36', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.47.woff2'), - NotoFont('Noto Sans HK 37', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.48.woff2'), - NotoFont('Noto Sans HK 38', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.49.woff2'), - NotoFont('Noto Sans HK 39', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.50.woff2'), - NotoFont('Noto Sans HK 40', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.51.woff2'), - NotoFont('Noto Sans HK 41', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.52.woff2'), - NotoFont('Noto Sans HK 42', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.53.woff2'), - NotoFont('Noto Sans HK 43', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.54.woff2'), - NotoFont('Noto Sans HK 44', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.55.woff2'), - NotoFont('Noto Sans HK 45', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.56.woff2'), - NotoFont('Noto Sans HK 46', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.57.woff2'), - NotoFont('Noto Sans HK 47', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.58.woff2'), - NotoFont('Noto Sans HK 48', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.59.woff2'), - NotoFont('Noto Sans HK 49', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.60.woff2'), - NotoFont('Noto Sans HK 50', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.61.woff2'), - NotoFont('Noto Sans HK 51', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.62.woff2'), - NotoFont('Noto Sans HK 52', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.63.woff2'), - NotoFont('Noto Sans HK 53', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.64.woff2'), - NotoFont('Noto Sans HK 54', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.65.woff2'), - NotoFont('Noto Sans HK 55', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.66.woff2'), - NotoFont('Noto Sans HK 56', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.67.woff2'), - NotoFont('Noto Sans HK 57', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.68.woff2'), - NotoFont('Noto Sans HK 58', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.69.woff2'), - NotoFont('Noto Sans HK 59', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.70.woff2'), - NotoFont('Noto Sans HK 60', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.71.woff2'), - NotoFont('Noto Sans HK 61', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.72.woff2'), - NotoFont('Noto Sans HK 62', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.73.woff2'), - NotoFont('Noto Sans HK 63', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.74.woff2'), - NotoFont('Noto Sans HK 64', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.75.woff2'), - NotoFont('Noto Sans HK 65', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.76.woff2'), - NotoFont('Noto Sans HK 66', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.77.woff2'), - NotoFont('Noto Sans HK 67', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.78.woff2'), - NotoFont('Noto Sans HK 68', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.79.woff2'), - NotoFont('Noto Sans HK 69', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.80.woff2'), - NotoFont('Noto Sans HK 70', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.81.woff2'), - NotoFont('Noto Sans HK 71', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.82.woff2'), - NotoFont('Noto Sans HK 72', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.83.woff2'), - NotoFont('Noto Sans HK 73', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.84.woff2'), - NotoFont('Noto Sans HK 74', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.85.woff2'), - NotoFont('Noto Sans HK 75', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.86.woff2'), - NotoFont('Noto Sans HK 76', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.87.woff2'), - NotoFont('Noto Sans HK 77', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.88.woff2'), - NotoFont('Noto Sans HK 78', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.89.woff2'), - NotoFont('Noto Sans HK 79', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.90.woff2'), - NotoFont('Noto Sans HK 80', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.91.woff2'), - NotoFont('Noto Sans HK 81', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.92.woff2'), - NotoFont('Noto Sans HK 82', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.93.woff2'), - NotoFont('Noto Sans HK 83', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.98.woff2'), - NotoFont('Noto Sans HK 84', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.99.woff2'), - NotoFont('Noto Sans HK 85', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.100.woff2'), - NotoFont('Noto Sans HK 86', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.101.woff2'), - NotoFont('Noto Sans HK 87', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.102.woff2'), - NotoFont('Noto Sans HK 88', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.103.woff2'), - NotoFont('Noto Sans HK 89', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.104.woff2'), - NotoFont('Noto Sans HK 90', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.105.woff2'), - NotoFont('Noto Sans HK 91', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.106.woff2'), - NotoFont('Noto Sans HK 92', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.107.woff2'), - NotoFont('Noto Sans HK 93', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.108.woff2'), - NotoFont('Noto Sans HK 94', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.109.woff2'), - NotoFont('Noto Sans HK 95', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.110.woff2'), - NotoFont('Noto Sans HK 96', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.111.woff2'), - NotoFont('Noto Sans HK 97', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.112.woff2'), - NotoFont('Noto Sans HK 98', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.113.woff2'), - NotoFont('Noto Sans HK 99', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.114.woff2'), - NotoFont('Noto Sans HK 100', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.115.woff2'), - NotoFont('Noto Sans HK 101', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.116.woff2'), - NotoFont('Noto Sans HK 102', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.117.woff2'), - NotoFont('Noto Sans HK 103', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.118.woff2'), - NotoFont('Noto Sans HK 104', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.119.woff2'), - NotoFont('Noto Sans HK 105', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB-yoaZiLjN.woff2'), - NotoFont('Noto Sans HK 106', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB-yo2ZiLjN.woff2'), - NotoFont('Noto Sans HK 107', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB-yoyZiLjN.woff2'), - NotoFont('Noto Sans HK 108', 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB-yoKZiA.woff2'), - NotoFont('Noto Sans JP 0', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.0.woff2'), - NotoFont('Noto Sans JP 1', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.1.woff2'), - NotoFont('Noto Sans JP 2', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.2.woff2'), - NotoFont('Noto Sans JP 3', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.3.woff2'), - NotoFont('Noto Sans JP 4', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.4.woff2'), - NotoFont('Noto Sans JP 5', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.5.woff2'), - NotoFont('Noto Sans JP 6', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.6.woff2'), - NotoFont('Noto Sans JP 7', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.7.woff2'), - NotoFont('Noto Sans JP 8', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.8.woff2'), - NotoFont('Noto Sans JP 9', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.9.woff2'), - NotoFont('Noto Sans JP 10', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.10.woff2'), - NotoFont('Noto Sans JP 11', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.11.woff2'), - NotoFont('Noto Sans JP 12', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.12.woff2'), - NotoFont('Noto Sans JP 13', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.13.woff2'), - NotoFont('Noto Sans JP 14', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.14.woff2'), - NotoFont('Noto Sans JP 15', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.15.woff2'), - NotoFont('Noto Sans JP 16', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.16.woff2'), - NotoFont('Noto Sans JP 17', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.17.woff2'), - NotoFont('Noto Sans JP 18', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.18.woff2'), - NotoFont('Noto Sans JP 19', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.19.woff2'), - NotoFont('Noto Sans JP 20', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.20.woff2'), - NotoFont('Noto Sans JP 21', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.21.woff2'), - NotoFont('Noto Sans JP 22', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.22.woff2'), - NotoFont('Noto Sans JP 23', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.23.woff2'), - NotoFont('Noto Sans JP 24', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.24.woff2'), - NotoFont('Noto Sans JP 25', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.25.woff2'), - NotoFont('Noto Sans JP 26', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.26.woff2'), - NotoFont('Noto Sans JP 27', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.27.woff2'), - NotoFont('Noto Sans JP 28', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.28.woff2'), - NotoFont('Noto Sans JP 29', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.29.woff2'), - NotoFont('Noto Sans JP 30', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.30.woff2'), - NotoFont('Noto Sans JP 31', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.31.woff2'), - NotoFont('Noto Sans JP 32', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.32.woff2'), - NotoFont('Noto Sans JP 33', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.33.woff2'), - NotoFont('Noto Sans JP 34', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.34.woff2'), - NotoFont('Noto Sans JP 35', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.35.woff2'), - NotoFont('Noto Sans JP 36', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.36.woff2'), - NotoFont('Noto Sans JP 37', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.37.woff2'), - NotoFont('Noto Sans JP 38', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.38.woff2'), - NotoFont('Noto Sans JP 39', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.39.woff2'), - NotoFont('Noto Sans JP 40', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.40.woff2'), - NotoFont('Noto Sans JP 41', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.41.woff2'), - NotoFont('Noto Sans JP 42', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.42.woff2'), - NotoFont('Noto Sans JP 43', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.43.woff2'), - NotoFont('Noto Sans JP 44', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.44.woff2'), - NotoFont('Noto Sans JP 45', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.45.woff2'), - NotoFont('Noto Sans JP 46', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.46.woff2'), - NotoFont('Noto Sans JP 47', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.47.woff2'), - NotoFont('Noto Sans JP 48', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.48.woff2'), - NotoFont('Noto Sans JP 49', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.49.woff2'), - NotoFont('Noto Sans JP 50', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.50.woff2'), - NotoFont('Noto Sans JP 51', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.51.woff2'), - NotoFont('Noto Sans JP 52', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.52.woff2'), - NotoFont('Noto Sans JP 53', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.53.woff2'), - NotoFont('Noto Sans JP 54', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.54.woff2'), - NotoFont('Noto Sans JP 55', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.55.woff2'), - NotoFont('Noto Sans JP 56', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.56.woff2'), - NotoFont('Noto Sans JP 57', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.57.woff2'), - NotoFont('Noto Sans JP 58', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.58.woff2'), - NotoFont('Noto Sans JP 59', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.59.woff2'), - NotoFont('Noto Sans JP 60', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.60.woff2'), - NotoFont('Noto Sans JP 61', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.61.woff2'), - NotoFont('Noto Sans JP 62', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.62.woff2'), - NotoFont('Noto Sans JP 63', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.63.woff2'), - NotoFont('Noto Sans JP 64', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.64.woff2'), - NotoFont('Noto Sans JP 65', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.65.woff2'), - NotoFont('Noto Sans JP 66', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.66.woff2'), - NotoFont('Noto Sans JP 67', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.67.woff2'), - NotoFont('Noto Sans JP 68', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.68.woff2'), - NotoFont('Noto Sans JP 69', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.69.woff2'), - NotoFont('Noto Sans JP 70', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.70.woff2'), - NotoFont('Noto Sans JP 71', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.71.woff2'), - NotoFont('Noto Sans JP 72', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.72.woff2'), - NotoFont('Noto Sans JP 73', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.73.woff2'), - NotoFont('Noto Sans JP 74', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.74.woff2'), - NotoFont('Noto Sans JP 75', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.75.woff2'), - NotoFont('Noto Sans JP 76', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.76.woff2'), - NotoFont('Noto Sans JP 77', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.77.woff2'), - NotoFont('Noto Sans JP 78', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.78.woff2'), - NotoFont('Noto Sans JP 79', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.79.woff2'), - NotoFont('Noto Sans JP 80', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.80.woff2'), - NotoFont('Noto Sans JP 81', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.81.woff2'), - NotoFont('Noto Sans JP 82', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.82.woff2'), - NotoFont('Noto Sans JP 83', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.83.woff2'), - NotoFont('Noto Sans JP 84', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.84.woff2'), - NotoFont('Noto Sans JP 85', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.85.woff2'), - NotoFont('Noto Sans JP 86', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.86.woff2'), - NotoFont('Noto Sans JP 87', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.87.woff2'), - NotoFont('Noto Sans JP 88', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.88.woff2'), - NotoFont('Noto Sans JP 89', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.89.woff2'), - NotoFont('Noto Sans JP 90', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.90.woff2'), - NotoFont('Noto Sans JP 91', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.91.woff2'), - NotoFont('Noto Sans JP 92', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.92.woff2'), - NotoFont('Noto Sans JP 93', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.93.woff2'), - NotoFont('Noto Sans JP 94', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.94.woff2'), - NotoFont('Noto Sans JP 95', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.95.woff2'), - NotoFont('Noto Sans JP 96', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.96.woff2'), - NotoFont('Noto Sans JP 97', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.97.woff2'), - NotoFont('Noto Sans JP 98', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.98.woff2'), - NotoFont('Noto Sans JP 99', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.99.woff2'), - NotoFont('Noto Sans JP 100', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.100.woff2'), - NotoFont('Noto Sans JP 101', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.101.woff2'), - NotoFont('Noto Sans JP 102', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.102.woff2'), - NotoFont('Noto Sans JP 103', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.103.woff2'), - NotoFont('Noto Sans JP 104', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.104.woff2'), - NotoFont('Noto Sans JP 105', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.105.woff2'), - NotoFont('Noto Sans JP 106', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.106.woff2'), - NotoFont('Noto Sans JP 107', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.107.woff2'), - NotoFont('Noto Sans JP 108', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.108.woff2'), - NotoFont('Noto Sans JP 109', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.109.woff2'), - NotoFont('Noto Sans JP 110', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.110.woff2'), - NotoFont('Noto Sans JP 111', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.111.woff2'), - NotoFont('Noto Sans JP 112', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.112.woff2'), - NotoFont('Noto Sans JP 113', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.113.woff2'), - NotoFont('Noto Sans JP 114', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.114.woff2'), - NotoFont('Noto Sans JP 115', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.115.woff2'), - NotoFont('Noto Sans JP 116', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.116.woff2'), - NotoFont('Noto Sans JP 117', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.117.woff2'), - NotoFont('Noto Sans JP 118', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.118.woff2'), - NotoFont('Noto Sans JP 119', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.119.woff2'), - NotoFont('Noto Sans JP 120', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj35jS04w-.woff2'), - NotoFont('Noto Sans JP 121', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj35PS04w-.woff2'), - NotoFont('Noto Sans JP 122', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj35LS04w-.woff2'), - NotoFont('Noto Sans JP 123', 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj35zS0w.woff2'), - NotoFont('Noto Sans KR 0', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.0.woff2'), - NotoFont('Noto Sans KR 1', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.1.woff2'), - NotoFont('Noto Sans KR 2', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.2.woff2'), - NotoFont('Noto Sans KR 3', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.3.woff2'), - NotoFont('Noto Sans KR 4', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.4.woff2'), - NotoFont('Noto Sans KR 5', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.5.woff2'), - NotoFont('Noto Sans KR 6', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.6.woff2'), - NotoFont('Noto Sans KR 7', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.7.woff2'), - NotoFont('Noto Sans KR 8', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.8.woff2'), - NotoFont('Noto Sans KR 9', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.9.woff2'), - NotoFont('Noto Sans KR 10', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.10.woff2'), - NotoFont('Noto Sans KR 11', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.11.woff2'), - NotoFont('Noto Sans KR 12', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.12.woff2'), - NotoFont('Noto Sans KR 13', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.13.woff2'), - NotoFont('Noto Sans KR 14', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.14.woff2'), - NotoFont('Noto Sans KR 15', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.15.woff2'), - NotoFont('Noto Sans KR 16', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.16.woff2'), - NotoFont('Noto Sans KR 17', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.17.woff2'), - NotoFont('Noto Sans KR 18', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.18.woff2'), - NotoFont('Noto Sans KR 19', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.19.woff2'), - NotoFont('Noto Sans KR 20', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.20.woff2'), - NotoFont('Noto Sans KR 21', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.21.woff2'), - NotoFont('Noto Sans KR 22', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.22.woff2'), - NotoFont('Noto Sans KR 23', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.23.woff2'), - NotoFont('Noto Sans KR 24', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.24.woff2'), - NotoFont('Noto Sans KR 25', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.25.woff2'), - NotoFont('Noto Sans KR 26', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.26.woff2'), - NotoFont('Noto Sans KR 27', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.27.woff2'), - NotoFont('Noto Sans KR 28', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.28.woff2'), - NotoFont('Noto Sans KR 29', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.29.woff2'), - NotoFont('Noto Sans KR 30', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.30.woff2'), - NotoFont('Noto Sans KR 31', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.31.woff2'), - NotoFont('Noto Sans KR 32', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.32.woff2'), - NotoFont('Noto Sans KR 33', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.33.woff2'), - NotoFont('Noto Sans KR 34', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.34.woff2'), - NotoFont('Noto Sans KR 35', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.35.woff2'), - NotoFont('Noto Sans KR 36', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.36.woff2'), - NotoFont('Noto Sans KR 37', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.37.woff2'), - NotoFont('Noto Sans KR 38', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.38.woff2'), - NotoFont('Noto Sans KR 39', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.39.woff2'), - NotoFont('Noto Sans KR 40', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.40.woff2'), - NotoFont('Noto Sans KR 41', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.41.woff2'), - NotoFont('Noto Sans KR 42', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.42.woff2'), - NotoFont('Noto Sans KR 43', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.43.woff2'), - NotoFont('Noto Sans KR 44', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.44.woff2'), - NotoFont('Noto Sans KR 45', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.45.woff2'), - NotoFont('Noto Sans KR 46', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.46.woff2'), - NotoFont('Noto Sans KR 47', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.47.woff2'), - NotoFont('Noto Sans KR 48', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.48.woff2'), - NotoFont('Noto Sans KR 49', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.49.woff2'), - NotoFont('Noto Sans KR 50', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.50.woff2'), - NotoFont('Noto Sans KR 51', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.51.woff2'), - NotoFont('Noto Sans KR 52', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.52.woff2'), - NotoFont('Noto Sans KR 53', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.53.woff2'), - NotoFont('Noto Sans KR 54', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.54.woff2'), - NotoFont('Noto Sans KR 55', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.55.woff2'), - NotoFont('Noto Sans KR 56', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.56.woff2'), - NotoFont('Noto Sans KR 57', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.57.woff2'), - NotoFont('Noto Sans KR 58', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.58.woff2'), - NotoFont('Noto Sans KR 59', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.59.woff2'), - NotoFont('Noto Sans KR 60', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.60.woff2'), - NotoFont('Noto Sans KR 61', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.61.woff2'), - NotoFont('Noto Sans KR 62', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.62.woff2'), - NotoFont('Noto Sans KR 63', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.63.woff2'), - NotoFont('Noto Sans KR 64', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.64.woff2'), - NotoFont('Noto Sans KR 65', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.65.woff2'), - NotoFont('Noto Sans KR 66', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.66.woff2'), - NotoFont('Noto Sans KR 67', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.67.woff2'), - NotoFont('Noto Sans KR 68', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.68.woff2'), - NotoFont('Noto Sans KR 69', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.69.woff2'), - NotoFont('Noto Sans KR 70', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.70.woff2'), - NotoFont('Noto Sans KR 71', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.71.woff2'), - NotoFont('Noto Sans KR 72', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.72.woff2'), - NotoFont('Noto Sans KR 73', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.73.woff2'), - NotoFont('Noto Sans KR 74', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.74.woff2'), - NotoFont('Noto Sans KR 75', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.75.woff2'), - NotoFont('Noto Sans KR 76', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.76.woff2'), - NotoFont('Noto Sans KR 77', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.77.woff2'), - NotoFont('Noto Sans KR 78', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.78.woff2'), - NotoFont('Noto Sans KR 79', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.79.woff2'), - NotoFont('Noto Sans KR 80', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.80.woff2'), - NotoFont('Noto Sans KR 81', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.81.woff2'), - NotoFont('Noto Sans KR 82', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.82.woff2'), - NotoFont('Noto Sans KR 83', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.83.woff2'), - NotoFont('Noto Sans KR 84', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.84.woff2'), - NotoFont('Noto Sans KR 85', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.85.woff2'), - NotoFont('Noto Sans KR 86', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.86.woff2'), - NotoFont('Noto Sans KR 87', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.87.woff2'), - NotoFont('Noto Sans KR 88', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.88.woff2'), - NotoFont('Noto Sans KR 89', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.89.woff2'), - NotoFont('Noto Sans KR 90', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.90.woff2'), - NotoFont('Noto Sans KR 91', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.91.woff2'), - NotoFont('Noto Sans KR 92', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.92.woff2'), - NotoFont('Noto Sans KR 93', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.93.woff2'), - NotoFont('Noto Sans KR 94', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.94.woff2'), - NotoFont('Noto Sans KR 95', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.95.woff2'), - NotoFont('Noto Sans KR 96', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.96.woff2'), - NotoFont('Noto Sans KR 97', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.97.woff2'), - NotoFont('Noto Sans KR 98', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.98.woff2'), - NotoFont('Noto Sans KR 99', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.99.woff2'), - NotoFont('Noto Sans KR 100', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.100.woff2'), - NotoFont('Noto Sans KR 101', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.101.woff2'), - NotoFont('Noto Sans KR 102', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.102.woff2'), - NotoFont('Noto Sans KR 103', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.103.woff2'), - NotoFont('Noto Sans KR 104', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.104.woff2'), - NotoFont('Noto Sans KR 105', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.105.woff2'), - NotoFont('Noto Sans KR 106', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.106.woff2'), - NotoFont('Noto Sans KR 107', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.107.woff2'), - NotoFont('Noto Sans KR 108', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.108.woff2'), - NotoFont('Noto Sans KR 109', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.109.woff2'), - NotoFont('Noto Sans KR 110', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.110.woff2'), - NotoFont('Noto Sans KR 111', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.111.woff2'), - NotoFont('Noto Sans KR 112', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.112.woff2'), - NotoFont('Noto Sans KR 113', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.113.woff2'), - NotoFont('Noto Sans KR 114', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.114.woff2'), - NotoFont('Noto Sans KR 115', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.115.woff2'), - NotoFont('Noto Sans KR 116', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.116.woff2'), - NotoFont('Noto Sans KR 117', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.117.woff2'), - NotoFont('Noto Sans KR 118', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.118.woff2'), - NotoFont('Noto Sans KR 119', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.119.woff2'), - NotoFont('Noto Sans KR 120', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoySLfg8U4h.woff2'), - NotoFont('Noto Sans KR 121', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoySLzg8U4h.woff2'), - NotoFont('Noto Sans KR 122', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoySL3g8U4h.woff2'), - NotoFont('Noto Sans KR 123', 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoySLPg8Q.woff2'), - NotoFont('Noto Sans SC 0', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.4.woff2'), - NotoFont('Noto Sans SC 1', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.5.woff2'), - NotoFont('Noto Sans SC 2', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.6.woff2'), - NotoFont('Noto Sans SC 3', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.21.woff2'), - NotoFont('Noto Sans SC 4', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.22.woff2'), - NotoFont('Noto Sans SC 5', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.23.woff2'), - NotoFont('Noto Sans SC 6', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.24.woff2'), - NotoFont('Noto Sans SC 7', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.25.woff2'), - NotoFont('Noto Sans SC 8', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.26.woff2'), - NotoFont('Noto Sans SC 9', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.27.woff2'), - NotoFont('Noto Sans SC 10', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.28.woff2'), - NotoFont('Noto Sans SC 11', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.29.woff2'), - NotoFont('Noto Sans SC 12', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.30.woff2'), - NotoFont('Noto Sans SC 13', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.31.woff2'), - NotoFont('Noto Sans SC 14', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.32.woff2'), - NotoFont('Noto Sans SC 15', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.33.woff2'), - NotoFont('Noto Sans SC 16', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.34.woff2'), - NotoFont('Noto Sans SC 17', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.35.woff2'), - NotoFont('Noto Sans SC 18', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.36.woff2'), - NotoFont('Noto Sans SC 19', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.37.woff2'), - NotoFont('Noto Sans SC 20', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.38.woff2'), - NotoFont('Noto Sans SC 21', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.39.woff2'), - NotoFont('Noto Sans SC 22', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.40.woff2'), - NotoFont('Noto Sans SC 23', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.41.woff2'), - NotoFont('Noto Sans SC 24', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.42.woff2'), - NotoFont('Noto Sans SC 25', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.43.woff2'), - NotoFont('Noto Sans SC 26', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.44.woff2'), - NotoFont('Noto Sans SC 27', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.45.woff2'), - NotoFont('Noto Sans SC 28', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.46.woff2'), - NotoFont('Noto Sans SC 29', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.47.woff2'), - NotoFont('Noto Sans SC 30', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.48.woff2'), - NotoFont('Noto Sans SC 31', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.49.woff2'), - NotoFont('Noto Sans SC 32', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.50.woff2'), - NotoFont('Noto Sans SC 33', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.51.woff2'), - NotoFont('Noto Sans SC 34', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.52.woff2'), - NotoFont('Noto Sans SC 35', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.53.woff2'), - NotoFont('Noto Sans SC 36', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.54.woff2'), - NotoFont('Noto Sans SC 37', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.55.woff2'), - NotoFont('Noto Sans SC 38', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.56.woff2'), - NotoFont('Noto Sans SC 39', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.57.woff2'), - NotoFont('Noto Sans SC 40', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.58.woff2'), - NotoFont('Noto Sans SC 41', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.59.woff2'), - NotoFont('Noto Sans SC 42', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.60.woff2'), - NotoFont('Noto Sans SC 43', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.61.woff2'), - NotoFont('Noto Sans SC 44', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.62.woff2'), - NotoFont('Noto Sans SC 45', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.63.woff2'), - NotoFont('Noto Sans SC 46', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.64.woff2'), - NotoFont('Noto Sans SC 47', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.65.woff2'), - NotoFont('Noto Sans SC 48', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.66.woff2'), - NotoFont('Noto Sans SC 49', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.67.woff2'), - NotoFont('Noto Sans SC 50', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.68.woff2'), - NotoFont('Noto Sans SC 51', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.69.woff2'), - NotoFont('Noto Sans SC 52', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.70.woff2'), - NotoFont('Noto Sans SC 53', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.71.woff2'), - NotoFont('Noto Sans SC 54', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.72.woff2'), - NotoFont('Noto Sans SC 55', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.73.woff2'), - NotoFont('Noto Sans SC 56', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.74.woff2'), - NotoFont('Noto Sans SC 57', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.75.woff2'), - NotoFont('Noto Sans SC 58', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.76.woff2'), - NotoFont('Noto Sans SC 59', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.77.woff2'), - NotoFont('Noto Sans SC 60', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.78.woff2'), - NotoFont('Noto Sans SC 61', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.79.woff2'), - NotoFont('Noto Sans SC 62', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.80.woff2'), - NotoFont('Noto Sans SC 63', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.81.woff2'), - NotoFont('Noto Sans SC 64', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.82.woff2'), - NotoFont('Noto Sans SC 65', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.83.woff2'), - NotoFont('Noto Sans SC 66', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.84.woff2'), - NotoFont('Noto Sans SC 67', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.85.woff2'), - NotoFont('Noto Sans SC 68', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.86.woff2'), - NotoFont('Noto Sans SC 69', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.87.woff2'), - NotoFont('Noto Sans SC 70', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.88.woff2'), - NotoFont('Noto Sans SC 71', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.89.woff2'), - NotoFont('Noto Sans SC 72', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.90.woff2'), - NotoFont('Noto Sans SC 73', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.91.woff2'), - NotoFont('Noto Sans SC 74', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.97.woff2'), - NotoFont('Noto Sans SC 75', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.98.woff2'), - NotoFont('Noto Sans SC 76', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.99.woff2'), - NotoFont('Noto Sans SC 77', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.100.woff2'), - NotoFont('Noto Sans SC 78', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.101.woff2'), - NotoFont('Noto Sans SC 79', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.102.woff2'), - NotoFont('Noto Sans SC 80', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.103.woff2'), - NotoFont('Noto Sans SC 81', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.104.woff2'), - NotoFont('Noto Sans SC 82', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.105.woff2'), - NotoFont('Noto Sans SC 83', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.106.woff2'), - NotoFont('Noto Sans SC 84', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.107.woff2'), - NotoFont('Noto Sans SC 85', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.108.woff2'), - NotoFont('Noto Sans SC 86', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.109.woff2'), - NotoFont('Noto Sans SC 87', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.110.woff2'), - NotoFont('Noto Sans SC 88', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.111.woff2'), - NotoFont('Noto Sans SC 89', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.112.woff2'), - NotoFont('Noto Sans SC 90', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.113.woff2'), - NotoFont('Noto Sans SC 91', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.114.woff2'), - NotoFont('Noto Sans SC 92', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.115.woff2'), - NotoFont('Noto Sans SC 93', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.116.woff2'), - NotoFont('Noto Sans SC 94', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.117.woff2'), - NotoFont('Noto Sans SC 95', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.118.woff2'), - NotoFont('Noto Sans SC 96', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.119.woff2'), - NotoFont('Noto Sans SC 97', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FrY9HbczS.woff2'), - NotoFont('Noto Sans SC 98', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FrYRHbczS.woff2'), - NotoFont('Noto Sans SC 99', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FrYVHbczS.woff2'), - NotoFont('Noto Sans SC 100', 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FrYtHbQ.woff2'), - NotoFont('Noto Sans TC 0', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.0.woff2'), - NotoFont('Noto Sans TC 1', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.6.woff2'), - NotoFont('Noto Sans TC 2', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.7.woff2'), - NotoFont('Noto Sans TC 3', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.8.woff2'), - NotoFont('Noto Sans TC 4', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.19.woff2'), - NotoFont('Noto Sans TC 5', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.20.woff2'), - NotoFont('Noto Sans TC 6', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.21.woff2'), - NotoFont('Noto Sans TC 7', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.22.woff2'), - NotoFont('Noto Sans TC 8', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.23.woff2'), - NotoFont('Noto Sans TC 9', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.24.woff2'), - NotoFont('Noto Sans TC 10', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.25.woff2'), - NotoFont('Noto Sans TC 11', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.26.woff2'), - NotoFont('Noto Sans TC 12', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.27.woff2'), - NotoFont('Noto Sans TC 13', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.28.woff2'), - NotoFont('Noto Sans TC 14', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.29.woff2'), - NotoFont('Noto Sans TC 15', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.30.woff2'), - NotoFont('Noto Sans TC 16', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.31.woff2'), - NotoFont('Noto Sans TC 17', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.32.woff2'), - NotoFont('Noto Sans TC 18', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.33.woff2'), - NotoFont('Noto Sans TC 19', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.34.woff2'), - NotoFont('Noto Sans TC 20', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.35.woff2'), - NotoFont('Noto Sans TC 21', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.36.woff2'), - NotoFont('Noto Sans TC 22', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.37.woff2'), - NotoFont('Noto Sans TC 23', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.38.woff2'), - NotoFont('Noto Sans TC 24', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.39.woff2'), - NotoFont('Noto Sans TC 25', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.40.woff2'), - NotoFont('Noto Sans TC 26', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.41.woff2'), - NotoFont('Noto Sans TC 27', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.42.woff2'), - NotoFont('Noto Sans TC 28', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.43.woff2'), - NotoFont('Noto Sans TC 29', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.44.woff2'), - NotoFont('Noto Sans TC 30', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.45.woff2'), - NotoFont('Noto Sans TC 31', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.46.woff2'), - NotoFont('Noto Sans TC 32', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.47.woff2'), - NotoFont('Noto Sans TC 33', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.48.woff2'), - NotoFont('Noto Sans TC 34', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.49.woff2'), - NotoFont('Noto Sans TC 35', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.50.woff2'), - NotoFont('Noto Sans TC 36', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.51.woff2'), - NotoFont('Noto Sans TC 37', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.52.woff2'), - NotoFont('Noto Sans TC 38', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.53.woff2'), - NotoFont('Noto Sans TC 39', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.54.woff2'), - NotoFont('Noto Sans TC 40', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.55.woff2'), - NotoFont('Noto Sans TC 41', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.56.woff2'), - NotoFont('Noto Sans TC 42', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.57.woff2'), - NotoFont('Noto Sans TC 43', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.58.woff2'), - NotoFont('Noto Sans TC 44', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.59.woff2'), - NotoFont('Noto Sans TC 45', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.60.woff2'), - NotoFont('Noto Sans TC 46', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.61.woff2'), - NotoFont('Noto Sans TC 47', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.62.woff2'), - NotoFont('Noto Sans TC 48', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.63.woff2'), - NotoFont('Noto Sans TC 49', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.64.woff2'), - NotoFont('Noto Sans TC 50', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.65.woff2'), - NotoFont('Noto Sans TC 51', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.66.woff2'), - NotoFont('Noto Sans TC 52', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.67.woff2'), - NotoFont('Noto Sans TC 53', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.68.woff2'), - NotoFont('Noto Sans TC 54', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.69.woff2'), - NotoFont('Noto Sans TC 55', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.70.woff2'), - NotoFont('Noto Sans TC 56', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.71.woff2'), - NotoFont('Noto Sans TC 57', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.72.woff2'), - NotoFont('Noto Sans TC 58', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.73.woff2'), - NotoFont('Noto Sans TC 59', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.74.woff2'), - NotoFont('Noto Sans TC 60', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.75.woff2'), - NotoFont('Noto Sans TC 61', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.76.woff2'), - NotoFont('Noto Sans TC 62', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.77.woff2'), - NotoFont('Noto Sans TC 63', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.78.woff2'), - NotoFont('Noto Sans TC 64', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.79.woff2'), - NotoFont('Noto Sans TC 65', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.80.woff2'), - NotoFont('Noto Sans TC 66', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.81.woff2'), - NotoFont('Noto Sans TC 67', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.82.woff2'), - NotoFont('Noto Sans TC 68', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.83.woff2'), - NotoFont('Noto Sans TC 69', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.84.woff2'), - NotoFont('Noto Sans TC 70', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.85.woff2'), - NotoFont('Noto Sans TC 71', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.86.woff2'), - NotoFont('Noto Sans TC 72', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.87.woff2'), - NotoFont('Noto Sans TC 73', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.88.woff2'), - NotoFont('Noto Sans TC 74', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.89.woff2'), - NotoFont('Noto Sans TC 75', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.90.woff2'), - NotoFont('Noto Sans TC 76', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.91.woff2'), - NotoFont('Noto Sans TC 77', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.92.woff2'), - NotoFont('Noto Sans TC 78', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.97.woff2'), - NotoFont('Noto Sans TC 79', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.98.woff2'), - NotoFont('Noto Sans TC 80', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.99.woff2'), - NotoFont('Noto Sans TC 81', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.100.woff2'), - NotoFont('Noto Sans TC 82', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.101.woff2'), - NotoFont('Noto Sans TC 83', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.102.woff2'), - NotoFont('Noto Sans TC 84', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.103.woff2'), - NotoFont('Noto Sans TC 85', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.104.woff2'), - NotoFont('Noto Sans TC 86', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.105.woff2'), - NotoFont('Noto Sans TC 87', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.106.woff2'), - NotoFont('Noto Sans TC 88', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.107.woff2'), - NotoFont('Noto Sans TC 89', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.108.woff2'), - NotoFont('Noto Sans TC 90', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.109.woff2'), - NotoFont('Noto Sans TC 91', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.110.woff2'), - NotoFont('Noto Sans TC 92', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.111.woff2'), - NotoFont('Noto Sans TC 93', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.112.woff2'), - NotoFont('Noto Sans TC 94', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.113.woff2'), - NotoFont('Noto Sans TC 95', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.114.woff2'), - NotoFont('Noto Sans TC 96', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.115.woff2'), - NotoFont('Noto Sans TC 97', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.116.woff2'), - NotoFont('Noto Sans TC 98', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.117.woff2'), - NotoFont('Noto Sans TC 99', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.118.woff2'), - NotoFont('Noto Sans TC 100', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.119.woff2'), - NotoFont('Noto Sans TC 101', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76CyzClEt1a3.woff2'), - NotoFont('Noto Sans TC 102', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76CyzCJEt1a3.woff2'), - NotoFont('Noto Sans TC 103', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76CyzCNEt1a3.woff2'), - NotoFont('Noto Sans TC 104', 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76CyzC1Etw.woff2'), - NotoFont('Noto Music', 'notomusic/v20/pe0rMIiSN5pO63htf1sxItKQB9Zra1U.woff2'), - NotoFont('Noto Sans', 'notosans/v37/o-0mIpQlx3QUlC5A4PNB6Ryti20_6n1iPHjcz6L1SoM-jCpoiyD9A99Y41P6zHtY.woff2'), - NotoFont('Noto Sans Adlam', 'notosansadlam/v22/neIczCCpqp0s5pPusPamd81eMfjPonvqdbYxxpgufnv0TGzBZLwhuvk.woff2'), - NotoFont('Noto Sans Anatolian Hieroglyphs', 'notosansanatolianhieroglyphs/v16/ijw9s4roRME5LLRxjsRb8A0gKPSWq4BbDmHHu6j2pEtUJzZWXyPIymc5QYo.woff2'), - NotoFont('Noto Sans Arabic', 'notosansarabic/v28/nwpxtLGrOAZMl5nJ_wfgRg3DrWFZWsnVBJ_sS6tlqHHFlhQ5l3sQWIHPqzCfyGyvvnCBFQLaig.woff2'), - NotoFont('Noto Sans Armenian', 'notosansarmenian/v43/ZgN0jOZKPa7CHqq0h37c7ReDUubm2SEdFXp7ig73qtTY5idb74R9UdM3y2nZLorxb60nYy6zF3Eg.woff2'), - NotoFont('Noto Sans Avestan', 'notosansavestan/v21/bWti7ejKfBziStx7lIzKOLQZKhIJkyu4SASLji8U.woff2'), - NotoFont('Noto Sans Balinese', 'notosansbalinese/v24/NaPwcYvSBuhTirw6IaFn6UrRDaqje-lpbbRtYf-Fwu2Ov7fdhEtVd222PPY.woff2'), - NotoFont('Noto Sans Bamum', 'notosansbamum/v27/uk-0EGK3o6EruUbnwovcbBTkkklK_Ya_PBHfNGTPEddO-_0LykxEkxA.woff2'), - NotoFont('Noto Sans Bassa Vah', 'notosansbassavah/v17/PN_bRee-r3f7LnqsD5sax12gjZn7mBpL5YwUpA2MBdcFn4MaAc6s34gH-GD7.woff2'), - NotoFont('Noto Sans Batak', 'notosansbatak/v20/gok2H6TwAEdtF9N8-mdTCQvT-Zdgpo_PHuk74A.woff2'), - NotoFont('Noto Sans Bengali', 'notosansbengali/v26/Cn-SJsCGWQxOjaGwMQ6fIiMywrNJIky6nvd8BjzVMvJx2mcSPVFpVEqE-6KmsolLudWk8izI0lc.woff2'), - NotoFont('Noto Sans Bhaiksuki', 'notosansbhaiksuki/v17/UcC63EosKniBH4iELXATsSBWdvUHXxhj8rfUdU4wh9U.woff2'), - NotoFont('Noto Sans Brahmi', 'notosansbrahmi/v19/vEFK2-VODB8RrNDvZSUmQQIIByV18te1W77HtMo.woff2'), - NotoFont('Noto Sans Buginese', 'notosansbuginese/v18/esDM30ldNv-KYGGJpKGk18phe_7Da6_gsPuEXLmNtw.woff2'), - NotoFont('Noto Sans Buhid', 'notosansbuhid/v22/Dxxy8jiXMW75w3OmoDXVWJD7YwzAfqtgnaFoGA.woff2'), - NotoFont('Noto Sans Canadian Aboriginal', 'notosanscanadianaboriginal/v26/4C_TLjTuEqPj-8J01CwaGkiZ9os0iGVkezM1mUT-j_Lmlzda6uH_nnX1bzigWLn_zQsg0q0uhQ.woff2'), - NotoFont('Noto Sans Carian', 'notosanscarian/v16/LDIpaoiONgYwA9Yc6f0gUILeMIOgs78b9yGLmfI.woff2'), - NotoFont('Noto Sans Caucasian Albanian', 'notosanscaucasianalbanian/v18/nKKA-HM_FYFRJvXzVXaANsU0VzsAc46QGOkWytlTs-TXrYXmoVmRSZo.woff2'), - NotoFont('Noto Sans Chakma', 'notosanschakma/v17/Y4GQYbJ8VTEp4t3MKJSMjg5OIzhi4J3TQhYBeYo.woff2'), - NotoFont('Noto Sans Cham', 'notosanscham/v31/pe06MIySN5pO62Z5YkFyQb_bbuRhe6D4yip43qfcERwcurGykboaLg.woff2'), - NotoFont('Noto Sans Cherokee', 'notosanscherokee/v20/KFOPCm6Yu8uF-29fiz9vQF9YWK6Z8O10cHNA0cSkZCHYWi5PDky5rAffjl0.woff2'), - NotoFont('Noto Sans Coptic', 'notosanscoptic/v21/iJWfBWmUZi_OHPqn4wq6kgqumOEd786_VG0xR4Y.woff2'), - NotoFont('Noto Sans Cypriot', 'notosanscypriot/v19/8AtzGta9PYqQDjyp79a6f8Cj-3a3cxIpK5MPpahF.woff2'), - NotoFont('Noto Sans Deseret', 'notosansdeseret/v17/MwQsbgPp1eKH6QsAVuFb9AZM6MMr2Vq4ZnJSZtQG.woff2'), - NotoFont('Noto Sans Devanagari', 'notosansdevanagari/v26/TuGoUUFzXI5FBtUq5a8bjKYTZjtRU6Sgv3NaV_SNmI0b8QQCQmHn6B2OHjbL_08AlXQly-UzoFoW4Ow.woff2'), - NotoFont('Noto Sans Elbasan', 'notosanselbasan/v16/-F6rfiZqLzI2JPCgQBnw400qp1trvHdgre4dFcFh.woff2'), - NotoFont('Noto Sans Elymaic', 'notosanselymaic/v17/UqyKK9YTJW5liNMhTMqe9vUFP65ZD4AmWOT0zi2V.woff2'), - NotoFont('Noto Sans Ethiopic', 'notosansethiopic/v47/7cHPv50vjIepfJVOZZgcpQ5B9FBTH9KGNfhSTgtoow1KVnIvyBoMSzUMacb-T35OK6DmwmfeaY9u.woff2'), - NotoFont('Noto Sans Georgian', 'notosansgeorgian/v44/PlIaFke5O6RzLfvNNVSitxkr76PRHBC4Ytyq-Gof7PUs4S7zWn-8YDB09HFNdpvnzFj7f5WK0OQV.woff2'), - NotoFont('Noto Sans Glagolitic', 'notosansglagolitic/v18/1q2ZY4-BBFBst88SU_tOj4J-4yuNF_HI4ERP4Amu7nM1.woff2'), - NotoFont('Noto Sans Gothic', 'notosansgothic/v16/TuGKUUVzXI5FBtUq5a8bj6wRbzxTFMD40kFQRx0.woff2'), - NotoFont('Noto Sans Grantha', 'notosansgrantha/v19/3y976akwcCjmsU8NDyrKo3IQfQ4o-r8ZFeulHc6N.woff2'), - NotoFont('Noto Sans Gujarati', 'notosansgujarati/v25/wlpWgx_HC1ti5ViekvcxnhMlCVo3f5pv17ivlzsUB14gg1TMR2Gw4VceEl7MA_ypFwPJ_OdiEH0s.woff2'), - NotoFont('Noto Sans Gunjala Gondi', 'notosansgunjalagondi/v19/bWtX7e7KfBziStx7lIzKPrcSMwcEnCv6DW7n5g0ef3PLtymzNxYL4YDE5Z4vCTxEJQ.woff2'), - NotoFont('Noto Sans Gurmukhi', 'notosansgurmukhi/v26/w8g9H3EvQP81sInb43inmyN9zZ7hb7ATbSWo4q8dJ74a3cVrYFQ_bogT0-gPeG1Oenb0Z_trdp7h.woff2'), - NotoFont('Noto Sans Hanunoo', 'notosanshanunoo/v21/f0Xs0fCv8dxkDWlZSoXOj6CphMloFsEpEpgL_ix2.woff2'), - NotoFont('Noto Sans Hatran', 'notosanshatran/v16/A2BBn4Ne0RgnVF3Lnko-0sOBIfL_mMo3r1nwzDs.woff2'), - NotoFont('Noto Sans Hebrew', 'notosanshebrew/v46/or3HQ7v33eiDljA1IufXTtVf7V6RvEEdhQlk0LlGxCyaeNKYZC0sqk3xXGiXd4qtpyJltutR2g.woff2'), - NotoFont('Noto Sans Imperial Aramaic', 'notosansimperialaramaic/v17/a8IMNpjwKmHXpgXbMIsbTc_kvks91LlLetBr5itQrtdjl3YfPNno.woff2'), - NotoFont('Noto Sans Indic Siyaq Numbers', 'notosansindicsiyaqnumbers/v16/6xK5dTJFKcWIu4bpRBjRZRpsIYHabOeZ8UZLubTzpXNHKx2TPOpVd5Iu.woff2'), - NotoFont('Noto Sans Inscriptional Pahlavi', 'notosansinscriptionalpahlavi/v17/ll8UK3GaVDuxR-TEqFPIbsR79Xxz9WEKbwsjpz7VklYlC7FCVt-VOAYK0QA.woff2'), - NotoFont('Noto Sans Inscriptional Parthian', 'notosansinscriptionalparthian/v17/k3k7o-IMPvpLmixcA63oYi-yStDkgXuXncL7dzfW3P4TAJ2yklBM2jNkLlLr.woff2'), - NotoFont('Noto Sans Javanese', 'notosansjavanese/v23/2V01KJkDAIA6Hp4zoSScDjV0Y-eoHAHT-Z3MngEefiidxJnkFFxiZYWj4O8.woff2'), - NotoFont('Noto Sans Kaithi', 'notosanskaithi/v22/buEtppS9f8_vkXadMBJJu0tWjLwjQigKdoZIKlo.woff2'), - NotoFont('Noto Sans Kannada', 'notosanskannada/v27/8vIs7xs32H97qzQKnzfeXycxXZyUmySvZWItmf1fe6TVmgop9ndpS-BqHEyGrDvNzScMLsPKrkY.woff2'), - NotoFont('Noto Sans Kayah Li', 'notosanskayahli/v21/B50nF61OpWTRcGrhOVJJwOMXdca6Yecki3E06x2jVTX3WCc3CZT4EXLuKVM.woff2'), - NotoFont('Noto Sans Kharoshthi', 'notosanskharoshthi/v16/Fh4qPiLjKS30-P4-pGMMXCCfvkc5Vd7KE5z9rFyx5mR1.woff2'), - NotoFont('Noto Sans Khmer', 'notosanskhmer/v24/ijw3s5roRME5LLRxjsRb-gssOenAyendxrgV2c-Zw-9vbVUti_Z_dWgtWYuNAJz9kAbrddiA.woff2'), - NotoFont('Noto Sans Khojki', 'notosanskhojki/v19/-nFnOHM29Oofr2wohFbTuPPKVWpmK_J709jy92k.woff2'), - NotoFont('Noto Sans Khudawadi', 'notosanskhudawadi/v22/fdNi9t6ZsWBZ2k5ltHN73zZ5hc8HANlHIjFnVVXz9MY.woff2'), - NotoFont('Noto Sans Lao', 'notosanslao/v30/bx6lNx2Ol_ixgdYWLm9BwxM3NW6BOkuf763Clj73CiQ_J1Djx9pidOt4ccbdepMK3riB2w.woff2'), - NotoFont('Noto Sans Lepcha', 'notosanslepcha/v19/0QI7MWlB_JWgA166SKhu05TekNS32AdstqBXgd4.woff2'), - NotoFont('Noto Sans Limbu', 'notosanslimbu/v24/3JnlSDv90Gmq2mrzckOBBRRoNJVj1cF3OHRDnA.woff2'), - NotoFont('Noto Sans Linear A', 'notosanslineara/v18/oPWS_l16kP4jCuhpgEGmwJOiA18FZj22y2HQAGQicw.woff2'), - NotoFont('Noto Sans Linear B', 'notosanslinearb/v17/HhyJU4wt9vSgfHoORYOiXOckKNB737IV2RkFTq4EPw.woff2'), - NotoFont('Noto Sans Lisu', 'notosanslisu/v25/uk-3EGO3o6EruUbnwovcYhz6kh57_nqbcTdjJnHP2Vwt3tIlxkVdig.woff2'), - NotoFont('Noto Sans Lycian', 'notosanslycian/v15/QldVNSNMqAsHtsJ7UmqxBQA9r8wA5_zaCJwn00E.woff2'), - NotoFont('Noto Sans Lydian', 'notosanslydian/v18/c4m71mVzGN7s8FmIukZJ1v4ZlcPReUbXMoIjEQI.woff2'), - NotoFont('Noto Sans Mahajani', 'notosansmahajani/v19/-F6sfiVqLzI2JPCgQBnw60Agp0JrvD5FgsARHNh4zg.woff2'), - NotoFont('Noto Sans Malayalam', 'notosansmalayalam/v26/sJoi3K5XjsSdcnzn071rL37lpAOsUThnDZIfPdbeSNzVakglNM-Qw8EaeB8Nss-_RuD9AVzEr6HxEA.woff2'), - NotoFont('Noto Sans Mandaic', 'notosansmandaic/v17/cIfnMbdWt1w_HgCcilqhKQBo_OsMI5_F_gMk0izH.woff2'), - NotoFont('Noto Sans Manichaean', 'notosansmanichaean/v18/taiVGntiC4--qtsfi4Jp9-_GkPZZCcrfekqHNTtFCtdX.woff2'), - NotoFont('Noto Sans Marchen', 'notosansmarchen/v20/aFTO7OZ_Y282EP-WyG6QTOX_C8WZMHhKk652ZaHk.woff2'), - NotoFont('Noto Sans Masaram Gondi', 'notosansmasaramgondi/v17/6xK_dThFKcWIu4bpRBjRYRV7KZCbUq6n_1kPnuGb7RI9WSWX.woff2'), - NotoFont('Noto Sans Math', 'notosansmath/v15/7Aump_cpkSecTWaHRlH2hyV5UHkD-V048PW0.woff2'), - NotoFont('Noto Sans Mayan Numerals', 'notosansmayannumerals/v16/PlIuFk25O6RzLfvNNVSivR09_KqYMwvvDKYjfIiE7soo6eepYQ.woff2'), - NotoFont('Noto Sans Medefaidrin', 'notosansmedefaidrin/v23/WwkzxOq6Dk-wranENynkfeVsNbRZtbOIdLb1exeM4ZeuabBfmErWlTj18e5A3rw.woff2'), - NotoFont('Noto Sans Meetei Mayek', 'notosansmeeteimayek/v15/HTxAL3QyKieByqY9eZPFweO0be7M21uSphSdhqILnmrRfJ8t_1TJ_vTT5PgeFYVa.woff2'), - NotoFont('Noto Sans Meroitic', 'notosansmeroitic/v18/IFS5HfRJndhE3P4b5jnZ3ITPvC6i00UDhThTiKY9KQ.woff2'), - NotoFont('Noto Sans Miao', 'notosansmiao/v17/Dxxz8jmXMW75w3OmoDXVV4zyZUjlUYVslLhx.woff2'), - NotoFont('Noto Sans Modi', 'notosansmodi/v23/pe03MIySN5pO62Z5YkFyT7jeav5vWVAgVol-.woff2'), - NotoFont('Noto Sans Mongolian', 'notosansmongolian/v22/VdGCAYADGIwE0EopZx8xQfHlgEAMsrToxL4g6-av1x0.woff2'), - NotoFont('Noto Sans Mro', 'notosansmro/v18/qWcsB6--pZv9TqnUQMhe9b39WDnRtjkho4M.woff2'), - NotoFont('Noto Sans Multani', 'notosansmultani/v20/9Bty3ClF38_RfOpe1gCaZ8p30BOFO1AxpfCs5Kos.woff2'), - NotoFont('Noto Sans Myanmar', 'notosansmyanmar/v20/AlZq_y1ZtY3ymOryg38hOCSdOnFq0Enz3OU4o1AC.woff2'), - NotoFont('Noto Sans NKo', 'notosansnko/v6/esDX31ZdNv-KYGGJpKGk2_RpMpWMHMLBrdA.woff2'), - NotoFont('Noto Sans Nabataean', 'notosansnabataean/v16/IFS4HfVJndhE3P4b5jnZ34DfsjO330dNoBd9hK8kMK4.woff2'), - NotoFont('Noto Sans New Tai Lue', 'notosansnewtailue/v22/H4cKBW-Pl9DZ0Xe_nHUapt7PovLXAhAnY7wqaLy-OJgU3p_pdeXAYUPghFPKzeY.woff2'), - NotoFont('Noto Sans Newa', 'notosansnewa/v16/7r3fqXp6utEsO9pI4f8ok8sWg8n6qN4R5lNU.woff2'), - NotoFont('Noto Sans Nushu', 'notosansnushu/v19/rnCw-xRQ3B7652emAbAe_Ai1IYaFXVAMArZKqQ.woff2'), - NotoFont('Noto Sans Ogham', 'notosansogham/v17/kmKlZqk1GBDGN0mY6k5lmEmww4hrsplaQxcoCA.woff2'), - NotoFont('Noto Sans Ol Chiki', 'notosansolchiki/v29/N0b92TJNOPt-eHmFZCdQbrL32r-4CvhzDzRwlxOQYuVALWk267c6gVrz5gQ.woff2'), - NotoFont('Noto Sans Old Hungarian', 'notosansoldhungarian/v18/E213_cD6hP3GwCJPEUssHEM0KqLaHJXg2PiIgRfmbg5nCYXt.woff2'), - NotoFont('Noto Sans Old Italic', 'notosansolditalic/v17/TuGOUUFzXI5FBtUq5a8bh68BJxxEVam7tWlUdRhtCC4d.woff2'), - NotoFont('Noto Sans Old North Arabian', 'notosansoldnortharabian/v16/esDF30BdNv-KYGGJpKGk2tNiMt7Jar6olZDyNdr81zBQnEo_xw4ABw.woff2'), - NotoFont('Noto Sans Old Permic', 'notosansoldpermic/v17/snf1s1q1-dF8pli1TesqcbUY4Mr-ElrwKLdSgv_dKYB5.woff2'), - NotoFont('Noto Sans Old Persian', 'notosansoldpersian/v16/wEOjEAbNnc5caQTFG18FHrZr9Bp6-8CmIJ_trelQfx9CjA.woff2'), - NotoFont('Noto Sans Old Sogdian', 'notosansoldsogdian/v17/3JnjSCH90Gmq2mrzckOBBhFhdrMst48aURt7mOIqM-9uyg.woff2'), - NotoFont('Noto Sans Old South Arabian', 'notosansoldsoutharabian/v16/3qT5oiOhnSyU8TNFIdhZTice3hB_HWKsEnF--0XCHiKx0etDT9HwTA.woff2'), - NotoFont('Noto Sans Old Turkic', 'notosansoldturkic/v18/yMJNMJVya43H0SUF_WmcGEQVqoEMKDKbsE2UjEw-Vyws.woff2'), - NotoFont('Noto Sans Oriya', 'notosansoriya/v31/AYCppXfzfccDCstK_hrjDyADv5e9748vhj3CJBLHIARtgD6TJQS0dJT5Ivj0f6_Z6LhHBRe-.woff2'), - NotoFont('Noto Sans Osage', 'notosansosage/v18/oPWX_kB6kP4jCuhpgEGmw4mtAVtXQ1aSxkrMCQ.woff2'), - NotoFont('Noto Sans Osmanya', 'notosansosmanya/v18/8vIS7xs32H97qzQKnzfeWzUyUpOJmz6hR47NCV5Z.woff2'), - NotoFont('Noto Sans Pahawh Hmong', 'notosanspahawhhmong/v18/bWtp7e_KfBziStx7lIzKKaMUOBEA3UPQDW7krzI_c48aMpM.woff2'), - NotoFont('Noto Sans Palmyrene', 'notosanspalmyrene/v16/ZgNPjOdKPa7CHqq0h37c_ASCWvH93SFCPne5ZpdNtcA.woff2'), - NotoFont('Noto Sans Pau Cin Hau', 'notosanspaucinhau/v20/x3d-cl3IZKmUqiMg_9wBLLtzl22EayN7ehIdiUWqKMxsKw.woff2'), - NotoFont('Noto Sans Phags Pa', 'notosansphagspa/v15/pxiZyoo6v8ZYyWh5WuPeJzMkd4SrGChkr0SsrvNXiA.woff2'), - NotoFont('Noto Sans Phoenician', 'notosansphoenician/v17/jizFRF9Ksm4Bt9PvcTaEkIHiTVtxmFtS5X7Mot-p5561.woff2'), - NotoFont('Noto Sans Psalter Pahlavi', 'notosanspsalterpahlavi/v17/rP2Vp3K65FkAtHfwd-eISGznYihzggmsicPfud3w1GjKsUQBct4.woff2'), - NotoFont('Noto Sans Rejang', 'notosansrejang/v21/Ktk2AKuMeZjqPnXgyqrib7DIogqwN4a3WYZB_sU.woff2'), - NotoFont('Noto Sans Runic', 'notosansrunic/v17/H4c_BXWPl9DZ0Xe_nHUaus7W68WWbhpvHtgIYg.woff2'), - NotoFont('Noto Sans Saurashtra', 'notosanssaurashtra/v23/ea8GacQ0Wfz_XKWXe6OtoA8w8zvmYwTef9nYjhPTSIx9.woff2'), - NotoFont('Noto Sans Sharada', 'notosanssharada/v16/gok0H7rwAEdtF9N8-mdTGALG6p0kwoXOPOwr4H8a.woff2'), - NotoFont('Noto Sans Shavian', 'notosansshavian/v17/CHy5V_HZE0jxJBQlqAeCKjJvQBNF4EFVSplv2Cwg.woff2'), - NotoFont('Noto Sans Siddham', 'notosanssiddham/v20/OZpZg-FwqiNLe9PELUikxTWDoCCeGqnYk3Ic92ZH.woff2'), - NotoFont('Noto Sans Sinhala', 'notosanssinhala/v32/yMJ2MJBya43H0SUF_WmcBEEf4rQVO2P524V5N_MxQzQtb-tf5dJbC30Fu9zUwg2a5l0LpJwbQRM.woff2'), - NotoFont('Noto Sans Sogdian', 'notosanssogdian/v16/taiQGn5iC4--qtsfi4Jp6eHPnfxQBo-7Pm6KHidM.woff2'), - NotoFont('Noto Sans Sora Sompeng', 'notosanssorasompeng/v24/PlIRFkO5O6RzLfvNNVSioxM2_OTrEhPyDLolKvCsHzCxWuGkYHR818DsZXJQd4Mu.woff2'), - NotoFont('Noto Sans Soyombo', 'notosanssoyombo/v17/RWmSoL-Y6-8q5LTtXs6MF6q7xsxgY0FuIFOcK25W.woff2'), - NotoFont('Noto Sans Sundanese', 'notosanssundanese/v26/FwZw7_84xUkosG2xJo2gm7nFwSLQkdymq2mkz3Gz1_b6ctxpNNHHizv7fQES.woff2'), - NotoFont('Noto Sans Syloti Nagri', 'notosanssylotinagri/v23/uU9eCAQZ75uhfF9UoWDRiY3q7Sf_VFV3m4dGFVLxN87gsj0.woff2'), - NotoFont('Noto Sans Symbols', 'notosanssymbols/v43/rP2up3q65FkAtHfwd-eIS2brbDN6gxP34F9jRRCe4W3gfQ8gb_VFRkzrbQ.woff2'), - NotoFont('Noto Sans Syriac', 'notosanssyriac/v16/Ktk7AKuMeZjqPnXgyqribqzQqgW0LYiVqV7dXcP0C-VD9MaMyZfUL_FC.woff2'), - NotoFont('Noto Sans Tagalog', 'notosanstagalog/v22/J7aFnoNzCnFcV9ZI-sUYuvote1R0wwEFA8jHexnL.woff2'), - NotoFont('Noto Sans Tagbanwa', 'notosanstagbanwa/v18/Y4GWYbB8VTEp4t3MKJSMmQdIKjRtt_nZQzQEaYpGoQ.woff2'), - NotoFont('Noto Sans Tai Le', 'notosanstaile/v17/vEFK2-VODB8RrNDvZSUmVxEATwR58te1W77HtMo.woff2'), - NotoFont('Noto Sans Tai Tham', 'notosanstaitham/v20/kJEbBv0U4hgtwxDUw2x9q7tbjLIfbPGHBoaVSAZ3MdLJBCUbPg-uyaRGKMw.woff2'), - NotoFont('Noto Sans Tai Viet', 'notosanstaiviet/v19/8QIUdj3HhN_lv4jf9vsE-9GMOLsaSPZr7o4fWsRO9w.woff2'), - NotoFont('Noto Sans Takri', 'notosanstakri/v24/TuGJUVpzXI5FBtUq5a8bnKIOdTwQMe_W3khJXg.woff2'), - NotoFont('Noto Sans Tamil', 'notosanstamil/v27/ieVc2YdFI3GCY6SyQy1KfStzYKZgzN1z4LKDbeZce-0429tBManUktuex7vGo70UqKDt_EvT.woff2'), - NotoFont('Noto Sans Tamil Supplement', 'notosanstamilsupplement/v21/DdTz78kEtnooLS5rXF1DaruiCd_bFp_Ph4sGcn7ax_vpAeMkeq1x.woff2'), - NotoFont('Noto Sans Telugu', 'notosanstelugu/v26/0FlxVOGZlE2Rrtr-HmgkMWJNjJ5_RyT8o8c7fHkeg-esVC5dzHkHIJQqrEntezbqREbf-3v37w.woff2'), - NotoFont('Noto Sans Thaana', 'notosansthaana/v24/C8c14dM-vnz-s-3jaEsxlxHkBH-WZOETXfoQrfQ9Y4XrbhLknu4-tbNu.woff2'), - NotoFont('Noto Sans Thai', 'notosansthai/v25/iJWnBXeUZi_OHPqn4wq6hQ2_hbJ1xyN9wd43SofNWcd1MKVQt_So_9CdU5RtpzR-QRvzzXg.woff2'), - NotoFont('Noto Sans Tifinagh', 'notosanstifinagh/v20/I_uzMoCduATTei9eI8dawkHIwvmhCvbn77nEcXfs4Q.woff2'), - NotoFont('Noto Sans Tirhuta', 'notosanstirhuta/v16/t5t6IQYRNJ6TWjahPR6X-M-apUyby7uDUBsTrn5P.woff2'), - NotoFont('Noto Sans Ugaritic', 'notosansugaritic/v16/3qTwoiqhnSyU8TNFIdhZVCwbjCpkAXXkNxoIkiazfg.woff2'), - NotoFont('Noto Sans Vai', 'notosansvai/v17/NaPecZTSBuhTirw6IaFn_UrURMHsDIRSfr0.woff2'), - NotoFont('Noto Sans Wancho', 'notosanswancho/v17/zrf-0GXXyfn6Fs0lH9P4cUubP0GBqAbopiRfKp8.woff2'), - NotoFont('Noto Sans Warang Citi', 'notosanswarangciti/v17/EYqtmb9SzL1YtsZSScyKDXIeOv3w-zgsNvKRoOVCCXzdgA.woff2'), - NotoFont('Noto Sans Yi', 'notosansyi/v19/sJoD3LFXjsSdcnzn071rO3apwFDJNVgSNg.woff2'), - NotoFont('Noto Sans Zanabazar Square', 'notosanszanabazarsquare/v19/Cn-jJsuGWQxOjaGwMQ6fOicyxLBEMRfDtkzl4uagQtJ0OCEgN0Gc.woff2'), - NotoFont('Noto Serif Tibetan', 'notoseriftibetan/v22/gokGH7nwAEdtF9N45n0Vaz7O-pk0wsvxHeDXMfqguoCmIrYcPSvrdSy_32c.woff2'), + NotoFont( + 'Noto Color Emoji 0', + 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.0.woff2', + ), + NotoFont( + 'Noto Color Emoji 1', + 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.1.woff2', + ), + NotoFont( + 'Noto Color Emoji 2', + 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.2.woff2', + ), + NotoFont( + 'Noto Color Emoji 3', + 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.3.woff2', + ), + NotoFont( + 'Noto Color Emoji 4', + 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.4.woff2', + ), + NotoFont( + 'Noto Color Emoji 5', + 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.5.woff2', + ), + NotoFont( + 'Noto Color Emoji 6', + 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.6.woff2', + ), + NotoFont( + 'Noto Color Emoji 7', + 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.7.woff2', + ), + NotoFont( + 'Noto Color Emoji 8', + 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.8.woff2', + ), + NotoFont( + 'Noto Color Emoji 9', + 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.9.woff2', + ), + NotoFont( + 'Noto Color Emoji 10', + 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.10.woff2', + ), + NotoFont( + 'Noto Color Emoji 11', + 'notocoloremoji/v32/Yq6P-KqIXTD0t4D9z1ESnKM3-HpFabsE4tq3luCC7p-aXxcn.11.woff2', + ), + NotoFont( + 'Noto Sans Symbols 2 0', + 'notosanssymbols2/v24/I_uyMoGduATTei9eI8daxVHDyfisHr71-jrBWXPM4Q.woff2', + ), + NotoFont( + 'Noto Sans Symbols 2 1', + 'notosanssymbols2/v24/I_uyMoGduATTei9eI8daxVHDyfisHr71-ujgfE71.woff2', + ), + NotoFont( + 'Noto Sans Symbols 2 2', + 'notosanssymbols2/v24/I_uyMoGduATTei9eI8daxVHDyfisHr71-gTBWXPM4Q.woff2', + ), + NotoFont( + 'Noto Sans Symbols 2 3', + 'notosanssymbols2/v24/I_uyMoGduATTei9eI8daxVHDyfisHr71-vrgfE71.woff2', + ), + NotoFont( + 'Noto Sans Symbols 2 4', + 'notosanssymbols2/v24/I_uyMoGduATTei9eI8daxVHDyfisHr71-prgfE71.woff2', + ), + NotoFont( + 'Noto Sans Symbols 2 5', + 'notosanssymbols2/v24/I_uyMoGduATTei9eI8daxVHDyfisHr71-pTgfA.woff2', + ), + NotoFont( + 'Noto Sans Cuneiform 0', + 'notosanscuneiform/v17/bMrrmTWK7YY-MF22aHGGd7H8PhJtvBDWse5DlCQu.woff2', + ), + NotoFont( + 'Noto Sans Cuneiform 1', + 'notosanscuneiform/v17/bMrrmTWK7YY-MF22aHGGd7H8PhJtvBDWsbZDlCQu.woff2', + ), + NotoFont( + 'Noto Sans Cuneiform 2', + 'notosanscuneiform/v17/bMrrmTWK7YY-MF22aHGGd7H8PhJtvBDWsbhDlA.woff2', + ), + NotoFont( + 'Noto Sans Duployan 0', + 'notosansduployan/v18/gokzH7nwAEdtF9N8-mdTDx_X9JM5wsvbi-kD5F8a.woff2', + ), + NotoFont( + 'Noto Sans Duployan 1', + 'notosansduployan/v18/gokzH7nwAEdtF9N8-mdTDx_X9JM5wsvbH8gm2WY.woff2', + ), + NotoFont( + 'Noto Sans Duployan 2', + 'notosansduployan/v18/gokzH7nwAEdtF9N8-mdTDx_X9JM5wsvbEcgm.woff2', + ), + NotoFont( + 'Noto Sans Egyptian Hieroglyphs 0', + 'notosansegyptianhieroglyphs/v29/vEF42-tODB8RrNDvZSUmRhcQHzx1s7y_F9-j3qSzEcbEYintdVi99Rg.woff2', + ), + NotoFont( + 'Noto Sans Egyptian Hieroglyphs 1', + 'notosansegyptianhieroglyphs/v29/vEF42-tODB8RrNDvZSUmRhcQHzx1s7y_F9-j3qSzEcbEYintQFi99Rg.woff2', + ), + NotoFont( + 'Noto Sans Egyptian Hieroglyphs 2', + 'notosansegyptianhieroglyphs/v29/vEF42-tODB8RrNDvZSUmRhcQHzx1s7y_F9-j3qSzEcbEYintTli9.woff2', + ), + NotoFont( + 'Noto Sans HK 0', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.0.woff2', + ), + NotoFont( + 'Noto Sans HK 1', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.1.woff2', + ), + NotoFont( + 'Noto Sans HK 2', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.2.woff2', + ), + NotoFont( + 'Noto Sans HK 3', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.3.woff2', + ), + NotoFont( + 'Noto Sans HK 4', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.4.woff2', + ), + NotoFont( + 'Noto Sans HK 5', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.5.woff2', + ), + NotoFont( + 'Noto Sans HK 6', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.6.woff2', + ), + NotoFont( + 'Noto Sans HK 7', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.7.woff2', + ), + NotoFont( + 'Noto Sans HK 8', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.8.woff2', + ), + NotoFont( + 'Noto Sans HK 9', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.9.woff2', + ), + NotoFont( + 'Noto Sans HK 10', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.10.woff2', + ), + NotoFont( + 'Noto Sans HK 11', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.15.woff2', + ), + NotoFont( + 'Noto Sans HK 12', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.16.woff2', + ), + NotoFont( + 'Noto Sans HK 13', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.17.woff2', + ), + NotoFont( + 'Noto Sans HK 14', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.25.woff2', + ), + NotoFont( + 'Noto Sans HK 15', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.26.woff2', + ), + NotoFont( + 'Noto Sans HK 16', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.27.woff2', + ), + NotoFont( + 'Noto Sans HK 17', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.28.woff2', + ), + NotoFont( + 'Noto Sans HK 18', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.29.woff2', + ), + NotoFont( + 'Noto Sans HK 19', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.30.woff2', + ), + NotoFont( + 'Noto Sans HK 20', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.31.woff2', + ), + NotoFont( + 'Noto Sans HK 21', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.32.woff2', + ), + NotoFont( + 'Noto Sans HK 22', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.33.woff2', + ), + NotoFont( + 'Noto Sans HK 23', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.34.woff2', + ), + NotoFont( + 'Noto Sans HK 24', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.35.woff2', + ), + NotoFont( + 'Noto Sans HK 25', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.36.woff2', + ), + NotoFont( + 'Noto Sans HK 26', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.37.woff2', + ), + NotoFont( + 'Noto Sans HK 27', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.38.woff2', + ), + NotoFont( + 'Noto Sans HK 28', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.39.woff2', + ), + NotoFont( + 'Noto Sans HK 29', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.40.woff2', + ), + NotoFont( + 'Noto Sans HK 30', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.41.woff2', + ), + NotoFont( + 'Noto Sans HK 31', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.42.woff2', + ), + NotoFont( + 'Noto Sans HK 32', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.43.woff2', + ), + NotoFont( + 'Noto Sans HK 33', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.44.woff2', + ), + NotoFont( + 'Noto Sans HK 34', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.45.woff2', + ), + NotoFont( + 'Noto Sans HK 35', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.46.woff2', + ), + NotoFont( + 'Noto Sans HK 36', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.47.woff2', + ), + NotoFont( + 'Noto Sans HK 37', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.48.woff2', + ), + NotoFont( + 'Noto Sans HK 38', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.49.woff2', + ), + NotoFont( + 'Noto Sans HK 39', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.50.woff2', + ), + NotoFont( + 'Noto Sans HK 40', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.51.woff2', + ), + NotoFont( + 'Noto Sans HK 41', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.52.woff2', + ), + NotoFont( + 'Noto Sans HK 42', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.53.woff2', + ), + NotoFont( + 'Noto Sans HK 43', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.54.woff2', + ), + NotoFont( + 'Noto Sans HK 44', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.55.woff2', + ), + NotoFont( + 'Noto Sans HK 45', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.56.woff2', + ), + NotoFont( + 'Noto Sans HK 46', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.57.woff2', + ), + NotoFont( + 'Noto Sans HK 47', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.58.woff2', + ), + NotoFont( + 'Noto Sans HK 48', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.59.woff2', + ), + NotoFont( + 'Noto Sans HK 49', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.60.woff2', + ), + NotoFont( + 'Noto Sans HK 50', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.61.woff2', + ), + NotoFont( + 'Noto Sans HK 51', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.62.woff2', + ), + NotoFont( + 'Noto Sans HK 52', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.63.woff2', + ), + NotoFont( + 'Noto Sans HK 53', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.64.woff2', + ), + NotoFont( + 'Noto Sans HK 54', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.65.woff2', + ), + NotoFont( + 'Noto Sans HK 55', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.66.woff2', + ), + NotoFont( + 'Noto Sans HK 56', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.67.woff2', + ), + NotoFont( + 'Noto Sans HK 57', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.68.woff2', + ), + NotoFont( + 'Noto Sans HK 58', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.69.woff2', + ), + NotoFont( + 'Noto Sans HK 59', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.70.woff2', + ), + NotoFont( + 'Noto Sans HK 60', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.71.woff2', + ), + NotoFont( + 'Noto Sans HK 61', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.72.woff2', + ), + NotoFont( + 'Noto Sans HK 62', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.73.woff2', + ), + NotoFont( + 'Noto Sans HK 63', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.74.woff2', + ), + NotoFont( + 'Noto Sans HK 64', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.75.woff2', + ), + NotoFont( + 'Noto Sans HK 65', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.76.woff2', + ), + NotoFont( + 'Noto Sans HK 66', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.77.woff2', + ), + NotoFont( + 'Noto Sans HK 67', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.78.woff2', + ), + NotoFont( + 'Noto Sans HK 68', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.79.woff2', + ), + NotoFont( + 'Noto Sans HK 69', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.80.woff2', + ), + NotoFont( + 'Noto Sans HK 70', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.81.woff2', + ), + NotoFont( + 'Noto Sans HK 71', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.82.woff2', + ), + NotoFont( + 'Noto Sans HK 72', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.83.woff2', + ), + NotoFont( + 'Noto Sans HK 73', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.84.woff2', + ), + NotoFont( + 'Noto Sans HK 74', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.85.woff2', + ), + NotoFont( + 'Noto Sans HK 75', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.86.woff2', + ), + NotoFont( + 'Noto Sans HK 76', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.87.woff2', + ), + NotoFont( + 'Noto Sans HK 77', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.88.woff2', + ), + NotoFont( + 'Noto Sans HK 78', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.89.woff2', + ), + NotoFont( + 'Noto Sans HK 79', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.90.woff2', + ), + NotoFont( + 'Noto Sans HK 80', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.91.woff2', + ), + NotoFont( + 'Noto Sans HK 81', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.92.woff2', + ), + NotoFont( + 'Noto Sans HK 82', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.93.woff2', + ), + NotoFont( + 'Noto Sans HK 83', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.98.woff2', + ), + NotoFont( + 'Noto Sans HK 84', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.99.woff2', + ), + NotoFont( + 'Noto Sans HK 85', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.100.woff2', + ), + NotoFont( + 'Noto Sans HK 86', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.101.woff2', + ), + NotoFont( + 'Noto Sans HK 87', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.102.woff2', + ), + NotoFont( + 'Noto Sans HK 88', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.103.woff2', + ), + NotoFont( + 'Noto Sans HK 89', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.104.woff2', + ), + NotoFont( + 'Noto Sans HK 90', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.105.woff2', + ), + NotoFont( + 'Noto Sans HK 91', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.106.woff2', + ), + NotoFont( + 'Noto Sans HK 92', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.107.woff2', + ), + NotoFont( + 'Noto Sans HK 93', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.108.woff2', + ), + NotoFont( + 'Noto Sans HK 94', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.109.woff2', + ), + NotoFont( + 'Noto Sans HK 95', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.110.woff2', + ), + NotoFont( + 'Noto Sans HK 96', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.111.woff2', + ), + NotoFont( + 'Noto Sans HK 97', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.112.woff2', + ), + NotoFont( + 'Noto Sans HK 98', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.113.woff2', + ), + NotoFont( + 'Noto Sans HK 99', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.114.woff2', + ), + NotoFont( + 'Noto Sans HK 100', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.115.woff2', + ), + NotoFont( + 'Noto Sans HK 101', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.116.woff2', + ), + NotoFont( + 'Noto Sans HK 102', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.117.woff2', + ), + NotoFont( + 'Noto Sans HK 103', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.118.woff2', + ), + NotoFont( + 'Noto Sans HK 104', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB--oD7kYrUzT7-NvA3pTohjc3XVtNXX8A7gG1LO2KAPAw.119.woff2', + ), + NotoFont( + 'Noto Sans HK 105', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB-yoaZiLjN.woff2', + ), + NotoFont( + 'Noto Sans HK 106', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB-yo2ZiLjN.woff2', + ), + NotoFont( + 'Noto Sans HK 107', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB-yoyZiLjN.woff2', + ), + NotoFont( + 'Noto Sans HK 108', + 'notosanshk/v32/nKKF-GM_FYFRJvXzVXaAPe97P1KHynJFP716qHB-yoKZiA.woff2', + ), + NotoFont( + 'Noto Sans JP 0', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.0.woff2', + ), + NotoFont( + 'Noto Sans JP 1', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.1.woff2', + ), + NotoFont( + 'Noto Sans JP 2', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.2.woff2', + ), + NotoFont( + 'Noto Sans JP 3', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.3.woff2', + ), + NotoFont( + 'Noto Sans JP 4', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.4.woff2', + ), + NotoFont( + 'Noto Sans JP 5', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.5.woff2', + ), + NotoFont( + 'Noto Sans JP 6', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.6.woff2', + ), + NotoFont( + 'Noto Sans JP 7', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.7.woff2', + ), + NotoFont( + 'Noto Sans JP 8', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.8.woff2', + ), + NotoFont( + 'Noto Sans JP 9', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.9.woff2', + ), + NotoFont( + 'Noto Sans JP 10', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.10.woff2', + ), + NotoFont( + 'Noto Sans JP 11', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.11.woff2', + ), + NotoFont( + 'Noto Sans JP 12', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.12.woff2', + ), + NotoFont( + 'Noto Sans JP 13', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.13.woff2', + ), + NotoFont( + 'Noto Sans JP 14', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.14.woff2', + ), + NotoFont( + 'Noto Sans JP 15', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.15.woff2', + ), + NotoFont( + 'Noto Sans JP 16', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.16.woff2', + ), + NotoFont( + 'Noto Sans JP 17', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.17.woff2', + ), + NotoFont( + 'Noto Sans JP 18', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.18.woff2', + ), + NotoFont( + 'Noto Sans JP 19', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.19.woff2', + ), + NotoFont( + 'Noto Sans JP 20', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.20.woff2', + ), + NotoFont( + 'Noto Sans JP 21', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.21.woff2', + ), + NotoFont( + 'Noto Sans JP 22', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.22.woff2', + ), + NotoFont( + 'Noto Sans JP 23', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.23.woff2', + ), + NotoFont( + 'Noto Sans JP 24', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.24.woff2', + ), + NotoFont( + 'Noto Sans JP 25', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.25.woff2', + ), + NotoFont( + 'Noto Sans JP 26', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.26.woff2', + ), + NotoFont( + 'Noto Sans JP 27', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.27.woff2', + ), + NotoFont( + 'Noto Sans JP 28', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.28.woff2', + ), + NotoFont( + 'Noto Sans JP 29', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.29.woff2', + ), + NotoFont( + 'Noto Sans JP 30', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.30.woff2', + ), + NotoFont( + 'Noto Sans JP 31', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.31.woff2', + ), + NotoFont( + 'Noto Sans JP 32', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.32.woff2', + ), + NotoFont( + 'Noto Sans JP 33', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.33.woff2', + ), + NotoFont( + 'Noto Sans JP 34', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.34.woff2', + ), + NotoFont( + 'Noto Sans JP 35', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.35.woff2', + ), + NotoFont( + 'Noto Sans JP 36', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.36.woff2', + ), + NotoFont( + 'Noto Sans JP 37', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.37.woff2', + ), + NotoFont( + 'Noto Sans JP 38', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.38.woff2', + ), + NotoFont( + 'Noto Sans JP 39', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.39.woff2', + ), + NotoFont( + 'Noto Sans JP 40', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.40.woff2', + ), + NotoFont( + 'Noto Sans JP 41', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.41.woff2', + ), + NotoFont( + 'Noto Sans JP 42', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.42.woff2', + ), + NotoFont( + 'Noto Sans JP 43', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.43.woff2', + ), + NotoFont( + 'Noto Sans JP 44', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.44.woff2', + ), + NotoFont( + 'Noto Sans JP 45', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.45.woff2', + ), + NotoFont( + 'Noto Sans JP 46', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.46.woff2', + ), + NotoFont( + 'Noto Sans JP 47', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.47.woff2', + ), + NotoFont( + 'Noto Sans JP 48', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.48.woff2', + ), + NotoFont( + 'Noto Sans JP 49', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.49.woff2', + ), + NotoFont( + 'Noto Sans JP 50', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.50.woff2', + ), + NotoFont( + 'Noto Sans JP 51', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.51.woff2', + ), + NotoFont( + 'Noto Sans JP 52', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.52.woff2', + ), + NotoFont( + 'Noto Sans JP 53', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.53.woff2', + ), + NotoFont( + 'Noto Sans JP 54', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.54.woff2', + ), + NotoFont( + 'Noto Sans JP 55', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.55.woff2', + ), + NotoFont( + 'Noto Sans JP 56', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.56.woff2', + ), + NotoFont( + 'Noto Sans JP 57', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.57.woff2', + ), + NotoFont( + 'Noto Sans JP 58', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.58.woff2', + ), + NotoFont( + 'Noto Sans JP 59', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.59.woff2', + ), + NotoFont( + 'Noto Sans JP 60', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.60.woff2', + ), + NotoFont( + 'Noto Sans JP 61', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.61.woff2', + ), + NotoFont( + 'Noto Sans JP 62', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.62.woff2', + ), + NotoFont( + 'Noto Sans JP 63', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.63.woff2', + ), + NotoFont( + 'Noto Sans JP 64', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.64.woff2', + ), + NotoFont( + 'Noto Sans JP 65', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.65.woff2', + ), + NotoFont( + 'Noto Sans JP 66', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.66.woff2', + ), + NotoFont( + 'Noto Sans JP 67', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.67.woff2', + ), + NotoFont( + 'Noto Sans JP 68', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.68.woff2', + ), + NotoFont( + 'Noto Sans JP 69', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.69.woff2', + ), + NotoFont( + 'Noto Sans JP 70', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.70.woff2', + ), + NotoFont( + 'Noto Sans JP 71', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.71.woff2', + ), + NotoFont( + 'Noto Sans JP 72', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.72.woff2', + ), + NotoFont( + 'Noto Sans JP 73', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.73.woff2', + ), + NotoFont( + 'Noto Sans JP 74', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.74.woff2', + ), + NotoFont( + 'Noto Sans JP 75', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.75.woff2', + ), + NotoFont( + 'Noto Sans JP 76', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.76.woff2', + ), + NotoFont( + 'Noto Sans JP 77', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.77.woff2', + ), + NotoFont( + 'Noto Sans JP 78', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.78.woff2', + ), + NotoFont( + 'Noto Sans JP 79', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.79.woff2', + ), + NotoFont( + 'Noto Sans JP 80', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.80.woff2', + ), + NotoFont( + 'Noto Sans JP 81', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.81.woff2', + ), + NotoFont( + 'Noto Sans JP 82', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.82.woff2', + ), + NotoFont( + 'Noto Sans JP 83', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.83.woff2', + ), + NotoFont( + 'Noto Sans JP 84', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.84.woff2', + ), + NotoFont( + 'Noto Sans JP 85', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.85.woff2', + ), + NotoFont( + 'Noto Sans JP 86', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.86.woff2', + ), + NotoFont( + 'Noto Sans JP 87', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.87.woff2', + ), + NotoFont( + 'Noto Sans JP 88', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.88.woff2', + ), + NotoFont( + 'Noto Sans JP 89', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.89.woff2', + ), + NotoFont( + 'Noto Sans JP 90', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.90.woff2', + ), + NotoFont( + 'Noto Sans JP 91', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.91.woff2', + ), + NotoFont( + 'Noto Sans JP 92', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.92.woff2', + ), + NotoFont( + 'Noto Sans JP 93', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.93.woff2', + ), + NotoFont( + 'Noto Sans JP 94', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.94.woff2', + ), + NotoFont( + 'Noto Sans JP 95', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.95.woff2', + ), + NotoFont( + 'Noto Sans JP 96', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.96.woff2', + ), + NotoFont( + 'Noto Sans JP 97', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.97.woff2', + ), + NotoFont( + 'Noto Sans JP 98', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.98.woff2', + ), + NotoFont( + 'Noto Sans JP 99', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.99.woff2', + ), + NotoFont( + 'Noto Sans JP 100', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.100.woff2', + ), + NotoFont( + 'Noto Sans JP 101', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.101.woff2', + ), + NotoFont( + 'Noto Sans JP 102', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.102.woff2', + ), + NotoFont( + 'Noto Sans JP 103', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.103.woff2', + ), + NotoFont( + 'Noto Sans JP 104', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.104.woff2', + ), + NotoFont( + 'Noto Sans JP 105', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.105.woff2', + ), + NotoFont( + 'Noto Sans JP 106', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.106.woff2', + ), + NotoFont( + 'Noto Sans JP 107', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.107.woff2', + ), + NotoFont( + 'Noto Sans JP 108', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.108.woff2', + ), + NotoFont( + 'Noto Sans JP 109', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.109.woff2', + ), + NotoFont( + 'Noto Sans JP 110', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.110.woff2', + ), + NotoFont( + 'Noto Sans JP 111', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.111.woff2', + ), + NotoFont( + 'Noto Sans JP 112', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.112.woff2', + ), + NotoFont( + 'Noto Sans JP 113', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.113.woff2', + ), + NotoFont( + 'Noto Sans JP 114', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.114.woff2', + ), + NotoFont( + 'Noto Sans JP 115', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.115.woff2', + ), + NotoFont( + 'Noto Sans JP 116', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.116.woff2', + ), + NotoFont( + 'Noto Sans JP 117', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.117.woff2', + ), + NotoFont( + 'Noto Sans JP 118', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.118.woff2', + ), + NotoFont( + 'Noto Sans JP 119', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj756wwr4v0qHnANADNsISRDl2PRkiiWsg.119.woff2', + ), + NotoFont( + 'Noto Sans JP 120', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj35jS04w-.woff2', + ), + NotoFont( + 'Noto Sans JP 121', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj35PS04w-.woff2', + ), + NotoFont( + 'Noto Sans JP 122', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj35LS04w-.woff2', + ), + NotoFont( + 'Noto Sans JP 123', + 'notosansjp/v53/-F6jfjtqLzI2JPCgQBnw7HFyzSD-AsregP8VFBEj35zS0w.woff2', + ), + NotoFont( + 'Noto Sans KR 0', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.0.woff2', + ), + NotoFont( + 'Noto Sans KR 1', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.1.woff2', + ), + NotoFont( + 'Noto Sans KR 2', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.2.woff2', + ), + NotoFont( + 'Noto Sans KR 3', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.3.woff2', + ), + NotoFont( + 'Noto Sans KR 4', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.4.woff2', + ), + NotoFont( + 'Noto Sans KR 5', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.5.woff2', + ), + NotoFont( + 'Noto Sans KR 6', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.6.woff2', + ), + NotoFont( + 'Noto Sans KR 7', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.7.woff2', + ), + NotoFont( + 'Noto Sans KR 8', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.8.woff2', + ), + NotoFont( + 'Noto Sans KR 9', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.9.woff2', + ), + NotoFont( + 'Noto Sans KR 10', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.10.woff2', + ), + NotoFont( + 'Noto Sans KR 11', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.11.woff2', + ), + NotoFont( + 'Noto Sans KR 12', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.12.woff2', + ), + NotoFont( + 'Noto Sans KR 13', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.13.woff2', + ), + NotoFont( + 'Noto Sans KR 14', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.14.woff2', + ), + NotoFont( + 'Noto Sans KR 15', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.15.woff2', + ), + NotoFont( + 'Noto Sans KR 16', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.16.woff2', + ), + NotoFont( + 'Noto Sans KR 17', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.17.woff2', + ), + NotoFont( + 'Noto Sans KR 18', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.18.woff2', + ), + NotoFont( + 'Noto Sans KR 19', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.19.woff2', + ), + NotoFont( + 'Noto Sans KR 20', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.20.woff2', + ), + NotoFont( + 'Noto Sans KR 21', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.21.woff2', + ), + NotoFont( + 'Noto Sans KR 22', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.22.woff2', + ), + NotoFont( + 'Noto Sans KR 23', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.23.woff2', + ), + NotoFont( + 'Noto Sans KR 24', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.24.woff2', + ), + NotoFont( + 'Noto Sans KR 25', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.25.woff2', + ), + NotoFont( + 'Noto Sans KR 26', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.26.woff2', + ), + NotoFont( + 'Noto Sans KR 27', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.27.woff2', + ), + NotoFont( + 'Noto Sans KR 28', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.28.woff2', + ), + NotoFont( + 'Noto Sans KR 29', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.29.woff2', + ), + NotoFont( + 'Noto Sans KR 30', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.30.woff2', + ), + NotoFont( + 'Noto Sans KR 31', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.31.woff2', + ), + NotoFont( + 'Noto Sans KR 32', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.32.woff2', + ), + NotoFont( + 'Noto Sans KR 33', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.33.woff2', + ), + NotoFont( + 'Noto Sans KR 34', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.34.woff2', + ), + NotoFont( + 'Noto Sans KR 35', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.35.woff2', + ), + NotoFont( + 'Noto Sans KR 36', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.36.woff2', + ), + NotoFont( + 'Noto Sans KR 37', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.37.woff2', + ), + NotoFont( + 'Noto Sans KR 38', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.38.woff2', + ), + NotoFont( + 'Noto Sans KR 39', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.39.woff2', + ), + NotoFont( + 'Noto Sans KR 40', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.40.woff2', + ), + NotoFont( + 'Noto Sans KR 41', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.41.woff2', + ), + NotoFont( + 'Noto Sans KR 42', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.42.woff2', + ), + NotoFont( + 'Noto Sans KR 43', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.43.woff2', + ), + NotoFont( + 'Noto Sans KR 44', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.44.woff2', + ), + NotoFont( + 'Noto Sans KR 45', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.45.woff2', + ), + NotoFont( + 'Noto Sans KR 46', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.46.woff2', + ), + NotoFont( + 'Noto Sans KR 47', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.47.woff2', + ), + NotoFont( + 'Noto Sans KR 48', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.48.woff2', + ), + NotoFont( + 'Noto Sans KR 49', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.49.woff2', + ), + NotoFont( + 'Noto Sans KR 50', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.50.woff2', + ), + NotoFont( + 'Noto Sans KR 51', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.51.woff2', + ), + NotoFont( + 'Noto Sans KR 52', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.52.woff2', + ), + NotoFont( + 'Noto Sans KR 53', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.53.woff2', + ), + NotoFont( + 'Noto Sans KR 54', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.54.woff2', + ), + NotoFont( + 'Noto Sans KR 55', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.55.woff2', + ), + NotoFont( + 'Noto Sans KR 56', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.56.woff2', + ), + NotoFont( + 'Noto Sans KR 57', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.57.woff2', + ), + NotoFont( + 'Noto Sans KR 58', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.58.woff2', + ), + NotoFont( + 'Noto Sans KR 59', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.59.woff2', + ), + NotoFont( + 'Noto Sans KR 60', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.60.woff2', + ), + NotoFont( + 'Noto Sans KR 61', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.61.woff2', + ), + NotoFont( + 'Noto Sans KR 62', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.62.woff2', + ), + NotoFont( + 'Noto Sans KR 63', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.63.woff2', + ), + NotoFont( + 'Noto Sans KR 64', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.64.woff2', + ), + NotoFont( + 'Noto Sans KR 65', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.65.woff2', + ), + NotoFont( + 'Noto Sans KR 66', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.66.woff2', + ), + NotoFont( + 'Noto Sans KR 67', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.67.woff2', + ), + NotoFont( + 'Noto Sans KR 68', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.68.woff2', + ), + NotoFont( + 'Noto Sans KR 69', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.69.woff2', + ), + NotoFont( + 'Noto Sans KR 70', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.70.woff2', + ), + NotoFont( + 'Noto Sans KR 71', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.71.woff2', + ), + NotoFont( + 'Noto Sans KR 72', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.72.woff2', + ), + NotoFont( + 'Noto Sans KR 73', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.73.woff2', + ), + NotoFont( + 'Noto Sans KR 74', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.74.woff2', + ), + NotoFont( + 'Noto Sans KR 75', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.75.woff2', + ), + NotoFont( + 'Noto Sans KR 76', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.76.woff2', + ), + NotoFont( + 'Noto Sans KR 77', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.77.woff2', + ), + NotoFont( + 'Noto Sans KR 78', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.78.woff2', + ), + NotoFont( + 'Noto Sans KR 79', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.79.woff2', + ), + NotoFont( + 'Noto Sans KR 80', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.80.woff2', + ), + NotoFont( + 'Noto Sans KR 81', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.81.woff2', + ), + NotoFont( + 'Noto Sans KR 82', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.82.woff2', + ), + NotoFont( + 'Noto Sans KR 83', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.83.woff2', + ), + NotoFont( + 'Noto Sans KR 84', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.84.woff2', + ), + NotoFont( + 'Noto Sans KR 85', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.85.woff2', + ), + NotoFont( + 'Noto Sans KR 86', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.86.woff2', + ), + NotoFont( + 'Noto Sans KR 87', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.87.woff2', + ), + NotoFont( + 'Noto Sans KR 88', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.88.woff2', + ), + NotoFont( + 'Noto Sans KR 89', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.89.woff2', + ), + NotoFont( + 'Noto Sans KR 90', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.90.woff2', + ), + NotoFont( + 'Noto Sans KR 91', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.91.woff2', + ), + NotoFont( + 'Noto Sans KR 92', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.92.woff2', + ), + NotoFont( + 'Noto Sans KR 93', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.93.woff2', + ), + NotoFont( + 'Noto Sans KR 94', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.94.woff2', + ), + NotoFont( + 'Noto Sans KR 95', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.95.woff2', + ), + NotoFont( + 'Noto Sans KR 96', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.96.woff2', + ), + NotoFont( + 'Noto Sans KR 97', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.97.woff2', + ), + NotoFont( + 'Noto Sans KR 98', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.98.woff2', + ), + NotoFont( + 'Noto Sans KR 99', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.99.woff2', + ), + NotoFont( + 'Noto Sans KR 100', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.100.woff2', + ), + NotoFont( + 'Noto Sans KR 101', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.101.woff2', + ), + NotoFont( + 'Noto Sans KR 102', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.102.woff2', + ), + NotoFont( + 'Noto Sans KR 103', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.103.woff2', + ), + NotoFont( + 'Noto Sans KR 104', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.104.woff2', + ), + NotoFont( + 'Noto Sans KR 105', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.105.woff2', + ), + NotoFont( + 'Noto Sans KR 106', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.106.woff2', + ), + NotoFont( + 'Noto Sans KR 107', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.107.woff2', + ), + NotoFont( + 'Noto Sans KR 108', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.108.woff2', + ), + NotoFont( + 'Noto Sans KR 109', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.109.woff2', + ), + NotoFont( + 'Noto Sans KR 110', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.110.woff2', + ), + NotoFont( + 'Noto Sans KR 111', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.111.woff2', + ), + NotoFont( + 'Noto Sans KR 112', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.112.woff2', + ), + NotoFont( + 'Noto Sans KR 113', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.113.woff2', + ), + NotoFont( + 'Noto Sans KR 114', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.114.woff2', + ), + NotoFont( + 'Noto Sans KR 115', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.115.woff2', + ), + NotoFont( + 'Noto Sans KR 116', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.116.woff2', + ), + NotoFont( + 'Noto Sans KR 117', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.117.woff2', + ), + NotoFont( + 'Noto Sans KR 118', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.118.woff2', + ), + NotoFont( + 'Noto Sans KR 119', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoyeLGC5nwuDo-KBTUm6CryotyJROlrnQ.119.woff2', + ), + NotoFont( + 'Noto Sans KR 120', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoySLfg8U4h.woff2', + ), + NotoFont( + 'Noto Sans KR 121', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoySLzg8U4h.woff2', + ), + NotoFont( + 'Noto Sans KR 122', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoySL3g8U4h.woff2', + ), + NotoFont( + 'Noto Sans KR 123', + 'notosanskr/v36/PbyxFmXiEBPT4ITbgNA5Cgms3VYcOA-vvnIzzuoySLPg8Q.woff2', + ), + NotoFont( + 'Noto Sans SC 0', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.4.woff2', + ), + NotoFont( + 'Noto Sans SC 1', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.5.woff2', + ), + NotoFont( + 'Noto Sans SC 2', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.6.woff2', + ), + NotoFont( + 'Noto Sans SC 3', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.21.woff2', + ), + NotoFont( + 'Noto Sans SC 4', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.22.woff2', + ), + NotoFont( + 'Noto Sans SC 5', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.23.woff2', + ), + NotoFont( + 'Noto Sans SC 6', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.24.woff2', + ), + NotoFont( + 'Noto Sans SC 7', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.25.woff2', + ), + NotoFont( + 'Noto Sans SC 8', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.26.woff2', + ), + NotoFont( + 'Noto Sans SC 9', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.27.woff2', + ), + NotoFont( + 'Noto Sans SC 10', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.28.woff2', + ), + NotoFont( + 'Noto Sans SC 11', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.29.woff2', + ), + NotoFont( + 'Noto Sans SC 12', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.30.woff2', + ), + NotoFont( + 'Noto Sans SC 13', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.31.woff2', + ), + NotoFont( + 'Noto Sans SC 14', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.32.woff2', + ), + NotoFont( + 'Noto Sans SC 15', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.33.woff2', + ), + NotoFont( + 'Noto Sans SC 16', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.34.woff2', + ), + NotoFont( + 'Noto Sans SC 17', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.35.woff2', + ), + NotoFont( + 'Noto Sans SC 18', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.36.woff2', + ), + NotoFont( + 'Noto Sans SC 19', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.37.woff2', + ), + NotoFont( + 'Noto Sans SC 20', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.38.woff2', + ), + NotoFont( + 'Noto Sans SC 21', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.39.woff2', + ), + NotoFont( + 'Noto Sans SC 22', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.40.woff2', + ), + NotoFont( + 'Noto Sans SC 23', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.41.woff2', + ), + NotoFont( + 'Noto Sans SC 24', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.42.woff2', + ), + NotoFont( + 'Noto Sans SC 25', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.43.woff2', + ), + NotoFont( + 'Noto Sans SC 26', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.44.woff2', + ), + NotoFont( + 'Noto Sans SC 27', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.45.woff2', + ), + NotoFont( + 'Noto Sans SC 28', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.46.woff2', + ), + NotoFont( + 'Noto Sans SC 29', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.47.woff2', + ), + NotoFont( + 'Noto Sans SC 30', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.48.woff2', + ), + NotoFont( + 'Noto Sans SC 31', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.49.woff2', + ), + NotoFont( + 'Noto Sans SC 32', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.50.woff2', + ), + NotoFont( + 'Noto Sans SC 33', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.51.woff2', + ), + NotoFont( + 'Noto Sans SC 34', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.52.woff2', + ), + NotoFont( + 'Noto Sans SC 35', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.53.woff2', + ), + NotoFont( + 'Noto Sans SC 36', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.54.woff2', + ), + NotoFont( + 'Noto Sans SC 37', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.55.woff2', + ), + NotoFont( + 'Noto Sans SC 38', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.56.woff2', + ), + NotoFont( + 'Noto Sans SC 39', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.57.woff2', + ), + NotoFont( + 'Noto Sans SC 40', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.58.woff2', + ), + NotoFont( + 'Noto Sans SC 41', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.59.woff2', + ), + NotoFont( + 'Noto Sans SC 42', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.60.woff2', + ), + NotoFont( + 'Noto Sans SC 43', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.61.woff2', + ), + NotoFont( + 'Noto Sans SC 44', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.62.woff2', + ), + NotoFont( + 'Noto Sans SC 45', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.63.woff2', + ), + NotoFont( + 'Noto Sans SC 46', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.64.woff2', + ), + NotoFont( + 'Noto Sans SC 47', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.65.woff2', + ), + NotoFont( + 'Noto Sans SC 48', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.66.woff2', + ), + NotoFont( + 'Noto Sans SC 49', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.67.woff2', + ), + NotoFont( + 'Noto Sans SC 50', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.68.woff2', + ), + NotoFont( + 'Noto Sans SC 51', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.69.woff2', + ), + NotoFont( + 'Noto Sans SC 52', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.70.woff2', + ), + NotoFont( + 'Noto Sans SC 53', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.71.woff2', + ), + NotoFont( + 'Noto Sans SC 54', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.72.woff2', + ), + NotoFont( + 'Noto Sans SC 55', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.73.woff2', + ), + NotoFont( + 'Noto Sans SC 56', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.74.woff2', + ), + NotoFont( + 'Noto Sans SC 57', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.75.woff2', + ), + NotoFont( + 'Noto Sans SC 58', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.76.woff2', + ), + NotoFont( + 'Noto Sans SC 59', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.77.woff2', + ), + NotoFont( + 'Noto Sans SC 60', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.78.woff2', + ), + NotoFont( + 'Noto Sans SC 61', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.79.woff2', + ), + NotoFont( + 'Noto Sans SC 62', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.80.woff2', + ), + NotoFont( + 'Noto Sans SC 63', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.81.woff2', + ), + NotoFont( + 'Noto Sans SC 64', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.82.woff2', + ), + NotoFont( + 'Noto Sans SC 65', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.83.woff2', + ), + NotoFont( + 'Noto Sans SC 66', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.84.woff2', + ), + NotoFont( + 'Noto Sans SC 67', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.85.woff2', + ), + NotoFont( + 'Noto Sans SC 68', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.86.woff2', + ), + NotoFont( + 'Noto Sans SC 69', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.87.woff2', + ), + NotoFont( + 'Noto Sans SC 70', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.88.woff2', + ), + NotoFont( + 'Noto Sans SC 71', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.89.woff2', + ), + NotoFont( + 'Noto Sans SC 72', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.90.woff2', + ), + NotoFont( + 'Noto Sans SC 73', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.91.woff2', + ), + NotoFont( + 'Noto Sans SC 74', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.97.woff2', + ), + NotoFont( + 'Noto Sans SC 75', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.98.woff2', + ), + NotoFont( + 'Noto Sans SC 76', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.99.woff2', + ), + NotoFont( + 'Noto Sans SC 77', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.100.woff2', + ), + NotoFont( + 'Noto Sans SC 78', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.101.woff2', + ), + NotoFont( + 'Noto Sans SC 79', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.102.woff2', + ), + NotoFont( + 'Noto Sans SC 80', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.103.woff2', + ), + NotoFont( + 'Noto Sans SC 81', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.104.woff2', + ), + NotoFont( + 'Noto Sans SC 82', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.105.woff2', + ), + NotoFont( + 'Noto Sans SC 83', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.106.woff2', + ), + NotoFont( + 'Noto Sans SC 84', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.107.woff2', + ), + NotoFont( + 'Noto Sans SC 85', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.108.woff2', + ), + NotoFont( + 'Noto Sans SC 86', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.109.woff2', + ), + NotoFont( + 'Noto Sans SC 87', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.110.woff2', + ), + NotoFont( + 'Noto Sans SC 88', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.111.woff2', + ), + NotoFont( + 'Noto Sans SC 89', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.112.woff2', + ), + NotoFont( + 'Noto Sans SC 90', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.113.woff2', + ), + NotoFont( + 'Noto Sans SC 91', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.114.woff2', + ), + NotoFont( + 'Noto Sans SC 92', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.115.woff2', + ), + NotoFont( + 'Noto Sans SC 93', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.116.woff2', + ), + NotoFont( + 'Noto Sans SC 94', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.117.woff2', + ), + NotoFont( + 'Noto Sans SC 95', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.118.woff2', + ), + NotoFont( + 'Noto Sans SC 96', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FnYkldv7JjxkkgFsFSSOPMOkySAZ73y9ViAt3acb8NexQ2w.119.woff2', + ), + NotoFont( + 'Noto Sans SC 97', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FrY9HbczS.woff2', + ), + NotoFont( + 'Noto Sans SC 98', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FrYRHbczS.woff2', + ), + NotoFont( + 'Noto Sans SC 99', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FrYVHbczS.woff2', + ), + NotoFont( + 'Noto Sans SC 100', + 'notosanssc/v37/k3kCo84MPvpLmixcA63oeAL7Iqp5IZJF9bmaG9_FrYtHbQ.woff2', + ), + NotoFont( + 'Noto Sans TC 0', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.0.woff2', + ), + NotoFont( + 'Noto Sans TC 1', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.6.woff2', + ), + NotoFont( + 'Noto Sans TC 2', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.7.woff2', + ), + NotoFont( + 'Noto Sans TC 3', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.8.woff2', + ), + NotoFont( + 'Noto Sans TC 4', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.19.woff2', + ), + NotoFont( + 'Noto Sans TC 5', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.20.woff2', + ), + NotoFont( + 'Noto Sans TC 6', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.21.woff2', + ), + NotoFont( + 'Noto Sans TC 7', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.22.woff2', + ), + NotoFont( + 'Noto Sans TC 8', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.23.woff2', + ), + NotoFont( + 'Noto Sans TC 9', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.24.woff2', + ), + NotoFont( + 'Noto Sans TC 10', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.25.woff2', + ), + NotoFont( + 'Noto Sans TC 11', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.26.woff2', + ), + NotoFont( + 'Noto Sans TC 12', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.27.woff2', + ), + NotoFont( + 'Noto Sans TC 13', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.28.woff2', + ), + NotoFont( + 'Noto Sans TC 14', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.29.woff2', + ), + NotoFont( + 'Noto Sans TC 15', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.30.woff2', + ), + NotoFont( + 'Noto Sans TC 16', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.31.woff2', + ), + NotoFont( + 'Noto Sans TC 17', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.32.woff2', + ), + NotoFont( + 'Noto Sans TC 18', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.33.woff2', + ), + NotoFont( + 'Noto Sans TC 19', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.34.woff2', + ), + NotoFont( + 'Noto Sans TC 20', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.35.woff2', + ), + NotoFont( + 'Noto Sans TC 21', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.36.woff2', + ), + NotoFont( + 'Noto Sans TC 22', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.37.woff2', + ), + NotoFont( + 'Noto Sans TC 23', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.38.woff2', + ), + NotoFont( + 'Noto Sans TC 24', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.39.woff2', + ), + NotoFont( + 'Noto Sans TC 25', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.40.woff2', + ), + NotoFont( + 'Noto Sans TC 26', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.41.woff2', + ), + NotoFont( + 'Noto Sans TC 27', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.42.woff2', + ), + NotoFont( + 'Noto Sans TC 28', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.43.woff2', + ), + NotoFont( + 'Noto Sans TC 29', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.44.woff2', + ), + NotoFont( + 'Noto Sans TC 30', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.45.woff2', + ), + NotoFont( + 'Noto Sans TC 31', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.46.woff2', + ), + NotoFont( + 'Noto Sans TC 32', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.47.woff2', + ), + NotoFont( + 'Noto Sans TC 33', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.48.woff2', + ), + NotoFont( + 'Noto Sans TC 34', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.49.woff2', + ), + NotoFont( + 'Noto Sans TC 35', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.50.woff2', + ), + NotoFont( + 'Noto Sans TC 36', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.51.woff2', + ), + NotoFont( + 'Noto Sans TC 37', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.52.woff2', + ), + NotoFont( + 'Noto Sans TC 38', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.53.woff2', + ), + NotoFont( + 'Noto Sans TC 39', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.54.woff2', + ), + NotoFont( + 'Noto Sans TC 40', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.55.woff2', + ), + NotoFont( + 'Noto Sans TC 41', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.56.woff2', + ), + NotoFont( + 'Noto Sans TC 42', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.57.woff2', + ), + NotoFont( + 'Noto Sans TC 43', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.58.woff2', + ), + NotoFont( + 'Noto Sans TC 44', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.59.woff2', + ), + NotoFont( + 'Noto Sans TC 45', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.60.woff2', + ), + NotoFont( + 'Noto Sans TC 46', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.61.woff2', + ), + NotoFont( + 'Noto Sans TC 47', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.62.woff2', + ), + NotoFont( + 'Noto Sans TC 48', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.63.woff2', + ), + NotoFont( + 'Noto Sans TC 49', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.64.woff2', + ), + NotoFont( + 'Noto Sans TC 50', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.65.woff2', + ), + NotoFont( + 'Noto Sans TC 51', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.66.woff2', + ), + NotoFont( + 'Noto Sans TC 52', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.67.woff2', + ), + NotoFont( + 'Noto Sans TC 53', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.68.woff2', + ), + NotoFont( + 'Noto Sans TC 54', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.69.woff2', + ), + NotoFont( + 'Noto Sans TC 55', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.70.woff2', + ), + NotoFont( + 'Noto Sans TC 56', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.71.woff2', + ), + NotoFont( + 'Noto Sans TC 57', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.72.woff2', + ), + NotoFont( + 'Noto Sans TC 58', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.73.woff2', + ), + NotoFont( + 'Noto Sans TC 59', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.74.woff2', + ), + NotoFont( + 'Noto Sans TC 60', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.75.woff2', + ), + NotoFont( + 'Noto Sans TC 61', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.76.woff2', + ), + NotoFont( + 'Noto Sans TC 62', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.77.woff2', + ), + NotoFont( + 'Noto Sans TC 63', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.78.woff2', + ), + NotoFont( + 'Noto Sans TC 64', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.79.woff2', + ), + NotoFont( + 'Noto Sans TC 65', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.80.woff2', + ), + NotoFont( + 'Noto Sans TC 66', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.81.woff2', + ), + NotoFont( + 'Noto Sans TC 67', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.82.woff2', + ), + NotoFont( + 'Noto Sans TC 68', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.83.woff2', + ), + NotoFont( + 'Noto Sans TC 69', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.84.woff2', + ), + NotoFont( + 'Noto Sans TC 70', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.85.woff2', + ), + NotoFont( + 'Noto Sans TC 71', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.86.woff2', + ), + NotoFont( + 'Noto Sans TC 72', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.87.woff2', + ), + NotoFont( + 'Noto Sans TC 73', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.88.woff2', + ), + NotoFont( + 'Noto Sans TC 74', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.89.woff2', + ), + NotoFont( + 'Noto Sans TC 75', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.90.woff2', + ), + NotoFont( + 'Noto Sans TC 76', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.91.woff2', + ), + NotoFont( + 'Noto Sans TC 77', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.92.woff2', + ), + NotoFont( + 'Noto Sans TC 78', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.97.woff2', + ), + NotoFont( + 'Noto Sans TC 79', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.98.woff2', + ), + NotoFont( + 'Noto Sans TC 80', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.99.woff2', + ), + NotoFont( + 'Noto Sans TC 81', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.100.woff2', + ), + NotoFont( + 'Noto Sans TC 82', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.101.woff2', + ), + NotoFont( + 'Noto Sans TC 83', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.102.woff2', + ), + NotoFont( + 'Noto Sans TC 84', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.103.woff2', + ), + NotoFont( + 'Noto Sans TC 85', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.104.woff2', + ), + NotoFont( + 'Noto Sans TC 86', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.105.woff2', + ), + NotoFont( + 'Noto Sans TC 87', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.106.woff2', + ), + NotoFont( + 'Noto Sans TC 88', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.107.woff2', + ), + NotoFont( + 'Noto Sans TC 89', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.108.woff2', + ), + NotoFont( + 'Noto Sans TC 90', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.109.woff2', + ), + NotoFont( + 'Noto Sans TC 91', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.110.woff2', + ), + NotoFont( + 'Noto Sans TC 92', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.111.woff2', + ), + NotoFont( + 'Noto Sans TC 93', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.112.woff2', + ), + NotoFont( + 'Noto Sans TC 94', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.113.woff2', + ), + NotoFont( + 'Noto Sans TC 95', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.114.woff2', + ), + NotoFont( + 'Noto Sans TC 96', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.115.woff2', + ), + NotoFont( + 'Noto Sans TC 97', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.116.woff2', + ), + NotoFont( + 'Noto Sans TC 98', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.117.woff2', + ), + NotoFont( + 'Noto Sans TC 99', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.118.woff2', + ), + NotoFont( + 'Noto Sans TC 100', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76Cy_C8mrWSt1KeqzFVoizG-KdWhyhvKuGOf8EUcrq3YKp7nxxk.119.woff2', + ), + NotoFont( + 'Noto Sans TC 101', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76CyzClEt1a3.woff2', + ), + NotoFont( + 'Noto Sans TC 102', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76CyzCJEt1a3.woff2', + ), + NotoFont( + 'Noto Sans TC 103', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76CyzCNEt1a3.woff2', + ), + NotoFont( + 'Noto Sans TC 104', + 'notosanstc/v36/-nFuOG829Oofr2wohFbTp9ifNAn722rq0MXz76CyzC1Etw.woff2', + ), + NotoFont('Noto Music', 'notomusic/v20/pe0rMIiSN5pO63htf1sxItKQB9Zra1U.woff2'), + NotoFont( + 'Noto Sans', + 'notosans/v37/o-0mIpQlx3QUlC5A4PNB6Ryti20_6n1iPHjcz6L1SoM-jCpoiyD9A99Y41P6zHtY.woff2', + ), + NotoFont( + 'Noto Sans Adlam', + 'notosansadlam/v22/neIczCCpqp0s5pPusPamd81eMfjPonvqdbYxxpgufnv0TGzBZLwhuvk.woff2', + ), + NotoFont( + 'Noto Sans Anatolian Hieroglyphs', + 'notosansanatolianhieroglyphs/v16/ijw9s4roRME5LLRxjsRb8A0gKPSWq4BbDmHHu6j2pEtUJzZWXyPIymc5QYo.woff2', + ), + NotoFont( + 'Noto Sans Arabic', + 'notosansarabic/v28/nwpxtLGrOAZMl5nJ_wfgRg3DrWFZWsnVBJ_sS6tlqHHFlhQ5l3sQWIHPqzCfyGyvvnCBFQLaig.woff2', + ), + NotoFont( + 'Noto Sans Armenian', + 'notosansarmenian/v43/ZgN0jOZKPa7CHqq0h37c7ReDUubm2SEdFXp7ig73qtTY5idb74R9UdM3y2nZLorxb60nYy6zF3Eg.woff2', + ), + NotoFont( + 'Noto Sans Avestan', + 'notosansavestan/v21/bWti7ejKfBziStx7lIzKOLQZKhIJkyu4SASLji8U.woff2', + ), + NotoFont( + 'Noto Sans Balinese', + 'notosansbalinese/v24/NaPwcYvSBuhTirw6IaFn6UrRDaqje-lpbbRtYf-Fwu2Ov7fdhEtVd222PPY.woff2', + ), + NotoFont( + 'Noto Sans Bamum', + 'notosansbamum/v27/uk-0EGK3o6EruUbnwovcbBTkkklK_Ya_PBHfNGTPEddO-_0LykxEkxA.woff2', + ), + NotoFont( + 'Noto Sans Bassa Vah', + 'notosansbassavah/v17/PN_bRee-r3f7LnqsD5sax12gjZn7mBpL5YwUpA2MBdcFn4MaAc6s34gH-GD7.woff2', + ), + NotoFont('Noto Sans Batak', 'notosansbatak/v20/gok2H6TwAEdtF9N8-mdTCQvT-Zdgpo_PHuk74A.woff2'), + NotoFont( + 'Noto Sans Bengali', + 'notosansbengali/v26/Cn-SJsCGWQxOjaGwMQ6fIiMywrNJIky6nvd8BjzVMvJx2mcSPVFpVEqE-6KmsolLudWk8izI0lc.woff2', + ), + NotoFont( + 'Noto Sans Bhaiksuki', + 'notosansbhaiksuki/v17/UcC63EosKniBH4iELXATsSBWdvUHXxhj8rfUdU4wh9U.woff2', + ), + NotoFont('Noto Sans Brahmi', 'notosansbrahmi/v19/vEFK2-VODB8RrNDvZSUmQQIIByV18te1W77HtMo.woff2'), + NotoFont( + 'Noto Sans Buginese', + 'notosansbuginese/v18/esDM30ldNv-KYGGJpKGk18phe_7Da6_gsPuEXLmNtw.woff2', + ), + NotoFont('Noto Sans Buhid', 'notosansbuhid/v22/Dxxy8jiXMW75w3OmoDXVWJD7YwzAfqtgnaFoGA.woff2'), + NotoFont( + 'Noto Sans Canadian Aboriginal', + 'notosanscanadianaboriginal/v26/4C_TLjTuEqPj-8J01CwaGkiZ9os0iGVkezM1mUT-j_Lmlzda6uH_nnX1bzigWLn_zQsg0q0uhQ.woff2', + ), + NotoFont('Noto Sans Carian', 'notosanscarian/v16/LDIpaoiONgYwA9Yc6f0gUILeMIOgs78b9yGLmfI.woff2'), + NotoFont( + 'Noto Sans Caucasian Albanian', + 'notosanscaucasianalbanian/v18/nKKA-HM_FYFRJvXzVXaANsU0VzsAc46QGOkWytlTs-TXrYXmoVmRSZo.woff2', + ), + NotoFont('Noto Sans Chakma', 'notosanschakma/v17/Y4GQYbJ8VTEp4t3MKJSMjg5OIzhi4J3TQhYBeYo.woff2'), + NotoFont( + 'Noto Sans Cham', + 'notosanscham/v31/pe06MIySN5pO62Z5YkFyQb_bbuRhe6D4yip43qfcERwcurGykboaLg.woff2', + ), + NotoFont( + 'Noto Sans Cherokee', + 'notosanscherokee/v20/KFOPCm6Yu8uF-29fiz9vQF9YWK6Z8O10cHNA0cSkZCHYWi5PDky5rAffjl0.woff2', + ), + NotoFont('Noto Sans Coptic', 'notosanscoptic/v21/iJWfBWmUZi_OHPqn4wq6kgqumOEd786_VG0xR4Y.woff2'), + NotoFont( + 'Noto Sans Cypriot', + 'notosanscypriot/v19/8AtzGta9PYqQDjyp79a6f8Cj-3a3cxIpK5MPpahF.woff2', + ), + NotoFont( + 'Noto Sans Deseret', + 'notosansdeseret/v17/MwQsbgPp1eKH6QsAVuFb9AZM6MMr2Vq4ZnJSZtQG.woff2', + ), + NotoFont( + 'Noto Sans Devanagari', + 'notosansdevanagari/v26/TuGoUUFzXI5FBtUq5a8bjKYTZjtRU6Sgv3NaV_SNmI0b8QQCQmHn6B2OHjbL_08AlXQly-UzoFoW4Ow.woff2', + ), + NotoFont( + 'Noto Sans Elbasan', + 'notosanselbasan/v16/-F6rfiZqLzI2JPCgQBnw400qp1trvHdgre4dFcFh.woff2', + ), + NotoFont( + 'Noto Sans Elymaic', + 'notosanselymaic/v17/UqyKK9YTJW5liNMhTMqe9vUFP65ZD4AmWOT0zi2V.woff2', + ), + NotoFont( + 'Noto Sans Ethiopic', + 'notosansethiopic/v47/7cHPv50vjIepfJVOZZgcpQ5B9FBTH9KGNfhSTgtoow1KVnIvyBoMSzUMacb-T35OK6DmwmfeaY9u.woff2', + ), + NotoFont( + 'Noto Sans Georgian', + 'notosansgeorgian/v44/PlIaFke5O6RzLfvNNVSitxkr76PRHBC4Ytyq-Gof7PUs4S7zWn-8YDB09HFNdpvnzFj7f5WK0OQV.woff2', + ), + NotoFont( + 'Noto Sans Glagolitic', + 'notosansglagolitic/v18/1q2ZY4-BBFBst88SU_tOj4J-4yuNF_HI4ERP4Amu7nM1.woff2', + ), + NotoFont('Noto Sans Gothic', 'notosansgothic/v16/TuGKUUVzXI5FBtUq5a8bj6wRbzxTFMD40kFQRx0.woff2'), + NotoFont( + 'Noto Sans Grantha', + 'notosansgrantha/v19/3y976akwcCjmsU8NDyrKo3IQfQ4o-r8ZFeulHc6N.woff2', + ), + NotoFont( + 'Noto Sans Gujarati', + 'notosansgujarati/v25/wlpWgx_HC1ti5ViekvcxnhMlCVo3f5pv17ivlzsUB14gg1TMR2Gw4VceEl7MA_ypFwPJ_OdiEH0s.woff2', + ), + NotoFont( + 'Noto Sans Gunjala Gondi', + 'notosansgunjalagondi/v19/bWtX7e7KfBziStx7lIzKPrcSMwcEnCv6DW7n5g0ef3PLtymzNxYL4YDE5Z4vCTxEJQ.woff2', + ), + NotoFont( + 'Noto Sans Gurmukhi', + 'notosansgurmukhi/v26/w8g9H3EvQP81sInb43inmyN9zZ7hb7ATbSWo4q8dJ74a3cVrYFQ_bogT0-gPeG1Oenb0Z_trdp7h.woff2', + ), + NotoFont( + 'Noto Sans Hanunoo', + 'notosanshanunoo/v21/f0Xs0fCv8dxkDWlZSoXOj6CphMloFsEpEpgL_ix2.woff2', + ), + NotoFont('Noto Sans Hatran', 'notosanshatran/v16/A2BBn4Ne0RgnVF3Lnko-0sOBIfL_mMo3r1nwzDs.woff2'), + NotoFont( + 'Noto Sans Hebrew', + 'notosanshebrew/v46/or3HQ7v33eiDljA1IufXTtVf7V6RvEEdhQlk0LlGxCyaeNKYZC0sqk3xXGiXd4qtpyJltutR2g.woff2', + ), + NotoFont( + 'Noto Sans Imperial Aramaic', + 'notosansimperialaramaic/v17/a8IMNpjwKmHXpgXbMIsbTc_kvks91LlLetBr5itQrtdjl3YfPNno.woff2', + ), + NotoFont( + 'Noto Sans Indic Siyaq Numbers', + 'notosansindicsiyaqnumbers/v16/6xK5dTJFKcWIu4bpRBjRZRpsIYHabOeZ8UZLubTzpXNHKx2TPOpVd5Iu.woff2', + ), + NotoFont( + 'Noto Sans Inscriptional Pahlavi', + 'notosansinscriptionalpahlavi/v17/ll8UK3GaVDuxR-TEqFPIbsR79Xxz9WEKbwsjpz7VklYlC7FCVt-VOAYK0QA.woff2', + ), + NotoFont( + 'Noto Sans Inscriptional Parthian', + 'notosansinscriptionalparthian/v17/k3k7o-IMPvpLmixcA63oYi-yStDkgXuXncL7dzfW3P4TAJ2yklBM2jNkLlLr.woff2', + ), + NotoFont( + 'Noto Sans Javanese', + 'notosansjavanese/v23/2V01KJkDAIA6Hp4zoSScDjV0Y-eoHAHT-Z3MngEefiidxJnkFFxiZYWj4O8.woff2', + ), + NotoFont('Noto Sans Kaithi', 'notosanskaithi/v22/buEtppS9f8_vkXadMBJJu0tWjLwjQigKdoZIKlo.woff2'), + NotoFont( + 'Noto Sans Kannada', + 'notosanskannada/v27/8vIs7xs32H97qzQKnzfeXycxXZyUmySvZWItmf1fe6TVmgop9ndpS-BqHEyGrDvNzScMLsPKrkY.woff2', + ), + NotoFont( + 'Noto Sans Kayah Li', + 'notosanskayahli/v21/B50nF61OpWTRcGrhOVJJwOMXdca6Yecki3E06x2jVTX3WCc3CZT4EXLuKVM.woff2', + ), + NotoFont( + 'Noto Sans Kharoshthi', + 'notosanskharoshthi/v16/Fh4qPiLjKS30-P4-pGMMXCCfvkc5Vd7KE5z9rFyx5mR1.woff2', + ), + NotoFont( + 'Noto Sans Khmer', + 'notosanskhmer/v24/ijw3s5roRME5LLRxjsRb-gssOenAyendxrgV2c-Zw-9vbVUti_Z_dWgtWYuNAJz9kAbrddiA.woff2', + ), + NotoFont('Noto Sans Khojki', 'notosanskhojki/v19/-nFnOHM29Oofr2wohFbTuPPKVWpmK_J709jy92k.woff2'), + NotoFont( + 'Noto Sans Khudawadi', + 'notosanskhudawadi/v22/fdNi9t6ZsWBZ2k5ltHN73zZ5hc8HANlHIjFnVVXz9MY.woff2', + ), + NotoFont( + 'Noto Sans Lao', + 'notosanslao/v30/bx6lNx2Ol_ixgdYWLm9BwxM3NW6BOkuf763Clj73CiQ_J1Djx9pidOt4ccbdepMK3riB2w.woff2', + ), + NotoFont('Noto Sans Lepcha', 'notosanslepcha/v19/0QI7MWlB_JWgA166SKhu05TekNS32AdstqBXgd4.woff2'), + NotoFont('Noto Sans Limbu', 'notosanslimbu/v24/3JnlSDv90Gmq2mrzckOBBRRoNJVj1cF3OHRDnA.woff2'), + NotoFont( + 'Noto Sans Linear A', + 'notosanslineara/v18/oPWS_l16kP4jCuhpgEGmwJOiA18FZj22y2HQAGQicw.woff2', + ), + NotoFont( + 'Noto Sans Linear B', + 'notosanslinearb/v17/HhyJU4wt9vSgfHoORYOiXOckKNB737IV2RkFTq4EPw.woff2', + ), + NotoFont( + 'Noto Sans Lisu', + 'notosanslisu/v25/uk-3EGO3o6EruUbnwovcYhz6kh57_nqbcTdjJnHP2Vwt3tIlxkVdig.woff2', + ), + NotoFont('Noto Sans Lycian', 'notosanslycian/v15/QldVNSNMqAsHtsJ7UmqxBQA9r8wA5_zaCJwn00E.woff2'), + NotoFont('Noto Sans Lydian', 'notosanslydian/v18/c4m71mVzGN7s8FmIukZJ1v4ZlcPReUbXMoIjEQI.woff2'), + NotoFont( + 'Noto Sans Mahajani', + 'notosansmahajani/v19/-F6sfiVqLzI2JPCgQBnw60Agp0JrvD5FgsARHNh4zg.woff2', + ), + NotoFont( + 'Noto Sans Malayalam', + 'notosansmalayalam/v26/sJoi3K5XjsSdcnzn071rL37lpAOsUThnDZIfPdbeSNzVakglNM-Qw8EaeB8Nss-_RuD9AVzEr6HxEA.woff2', + ), + NotoFont( + 'Noto Sans Mandaic', + 'notosansmandaic/v17/cIfnMbdWt1w_HgCcilqhKQBo_OsMI5_F_gMk0izH.woff2', + ), + NotoFont( + 'Noto Sans Manichaean', + 'notosansmanichaean/v18/taiVGntiC4--qtsfi4Jp9-_GkPZZCcrfekqHNTtFCtdX.woff2', + ), + NotoFont( + 'Noto Sans Marchen', + 'notosansmarchen/v20/aFTO7OZ_Y282EP-WyG6QTOX_C8WZMHhKk652ZaHk.woff2', + ), + NotoFont( + 'Noto Sans Masaram Gondi', + 'notosansmasaramgondi/v17/6xK_dThFKcWIu4bpRBjRYRV7KZCbUq6n_1kPnuGb7RI9WSWX.woff2', + ), + NotoFont('Noto Sans Math', 'notosansmath/v15/7Aump_cpkSecTWaHRlH2hyV5UHkD-V048PW0.woff2'), + NotoFont( + 'Noto Sans Mayan Numerals', + 'notosansmayannumerals/v16/PlIuFk25O6RzLfvNNVSivR09_KqYMwvvDKYjfIiE7soo6eepYQ.woff2', + ), + NotoFont( + 'Noto Sans Medefaidrin', + 'notosansmedefaidrin/v23/WwkzxOq6Dk-wranENynkfeVsNbRZtbOIdLb1exeM4ZeuabBfmErWlTj18e5A3rw.woff2', + ), + NotoFont( + 'Noto Sans Meetei Mayek', + 'notosansmeeteimayek/v15/HTxAL3QyKieByqY9eZPFweO0be7M21uSphSdhqILnmrRfJ8t_1TJ_vTT5PgeFYVa.woff2', + ), + NotoFont( + 'Noto Sans Meroitic', + 'notosansmeroitic/v18/IFS5HfRJndhE3P4b5jnZ3ITPvC6i00UDhThTiKY9KQ.woff2', + ), + NotoFont('Noto Sans Miao', 'notosansmiao/v17/Dxxz8jmXMW75w3OmoDXVV4zyZUjlUYVslLhx.woff2'), + NotoFont('Noto Sans Modi', 'notosansmodi/v23/pe03MIySN5pO62Z5YkFyT7jeav5vWVAgVol-.woff2'), + NotoFont( + 'Noto Sans Mongolian', + 'notosansmongolian/v22/VdGCAYADGIwE0EopZx8xQfHlgEAMsrToxL4g6-av1x0.woff2', + ), + NotoFont('Noto Sans Mro', 'notosansmro/v18/qWcsB6--pZv9TqnUQMhe9b39WDnRtjkho4M.woff2'), + NotoFont( + 'Noto Sans Multani', + 'notosansmultani/v20/9Bty3ClF38_RfOpe1gCaZ8p30BOFO1AxpfCs5Kos.woff2', + ), + NotoFont( + 'Noto Sans Myanmar', + 'notosansmyanmar/v20/AlZq_y1ZtY3ymOryg38hOCSdOnFq0Enz3OU4o1AC.woff2', + ), + NotoFont('Noto Sans NKo', 'notosansnko/v6/esDX31ZdNv-KYGGJpKGk2_RpMpWMHMLBrdA.woff2'), + NotoFont( + 'Noto Sans Nabataean', + 'notosansnabataean/v16/IFS4HfVJndhE3P4b5jnZ34DfsjO330dNoBd9hK8kMK4.woff2', + ), + NotoFont( + 'Noto Sans New Tai Lue', + 'notosansnewtailue/v22/H4cKBW-Pl9DZ0Xe_nHUapt7PovLXAhAnY7wqaLy-OJgU3p_pdeXAYUPghFPKzeY.woff2', + ), + NotoFont('Noto Sans Newa', 'notosansnewa/v16/7r3fqXp6utEsO9pI4f8ok8sWg8n6qN4R5lNU.woff2'), + NotoFont('Noto Sans Nushu', 'notosansnushu/v19/rnCw-xRQ3B7652emAbAe_Ai1IYaFXVAMArZKqQ.woff2'), + NotoFont('Noto Sans Ogham', 'notosansogham/v17/kmKlZqk1GBDGN0mY6k5lmEmww4hrsplaQxcoCA.woff2'), + NotoFont( + 'Noto Sans Ol Chiki', + 'notosansolchiki/v29/N0b92TJNOPt-eHmFZCdQbrL32r-4CvhzDzRwlxOQYuVALWk267c6gVrz5gQ.woff2', + ), + NotoFont( + 'Noto Sans Old Hungarian', + 'notosansoldhungarian/v18/E213_cD6hP3GwCJPEUssHEM0KqLaHJXg2PiIgRfmbg5nCYXt.woff2', + ), + NotoFont( + 'Noto Sans Old Italic', + 'notosansolditalic/v17/TuGOUUFzXI5FBtUq5a8bh68BJxxEVam7tWlUdRhtCC4d.woff2', + ), + NotoFont( + 'Noto Sans Old North Arabian', + 'notosansoldnortharabian/v16/esDF30BdNv-KYGGJpKGk2tNiMt7Jar6olZDyNdr81zBQnEo_xw4ABw.woff2', + ), + NotoFont( + 'Noto Sans Old Permic', + 'notosansoldpermic/v17/snf1s1q1-dF8pli1TesqcbUY4Mr-ElrwKLdSgv_dKYB5.woff2', + ), + NotoFont( + 'Noto Sans Old Persian', + 'notosansoldpersian/v16/wEOjEAbNnc5caQTFG18FHrZr9Bp6-8CmIJ_trelQfx9CjA.woff2', + ), + NotoFont( + 'Noto Sans Old Sogdian', + 'notosansoldsogdian/v17/3JnjSCH90Gmq2mrzckOBBhFhdrMst48aURt7mOIqM-9uyg.woff2', + ), + NotoFont( + 'Noto Sans Old South Arabian', + 'notosansoldsoutharabian/v16/3qT5oiOhnSyU8TNFIdhZTice3hB_HWKsEnF--0XCHiKx0etDT9HwTA.woff2', + ), + NotoFont( + 'Noto Sans Old Turkic', + 'notosansoldturkic/v18/yMJNMJVya43H0SUF_WmcGEQVqoEMKDKbsE2UjEw-Vyws.woff2', + ), + NotoFont( + 'Noto Sans Oriya', + 'notosansoriya/v31/AYCppXfzfccDCstK_hrjDyADv5e9748vhj3CJBLHIARtgD6TJQS0dJT5Ivj0f6_Z6LhHBRe-.woff2', + ), + NotoFont('Noto Sans Osage', 'notosansosage/v18/oPWX_kB6kP4jCuhpgEGmw4mtAVtXQ1aSxkrMCQ.woff2'), + NotoFont( + 'Noto Sans Osmanya', + 'notosansosmanya/v18/8vIS7xs32H97qzQKnzfeWzUyUpOJmz6hR47NCV5Z.woff2', + ), + NotoFont( + 'Noto Sans Pahawh Hmong', + 'notosanspahawhhmong/v18/bWtp7e_KfBziStx7lIzKKaMUOBEA3UPQDW7krzI_c48aMpM.woff2', + ), + NotoFont( + 'Noto Sans Palmyrene', + 'notosanspalmyrene/v16/ZgNPjOdKPa7CHqq0h37c_ASCWvH93SFCPne5ZpdNtcA.woff2', + ), + NotoFont( + 'Noto Sans Pau Cin Hau', + 'notosanspaucinhau/v20/x3d-cl3IZKmUqiMg_9wBLLtzl22EayN7ehIdiUWqKMxsKw.woff2', + ), + NotoFont( + 'Noto Sans Phags Pa', + 'notosansphagspa/v15/pxiZyoo6v8ZYyWh5WuPeJzMkd4SrGChkr0SsrvNXiA.woff2', + ), + NotoFont( + 'Noto Sans Phoenician', + 'notosansphoenician/v17/jizFRF9Ksm4Bt9PvcTaEkIHiTVtxmFtS5X7Mot-p5561.woff2', + ), + NotoFont( + 'Noto Sans Psalter Pahlavi', + 'notosanspsalterpahlavi/v17/rP2Vp3K65FkAtHfwd-eISGznYihzggmsicPfud3w1GjKsUQBct4.woff2', + ), + NotoFont('Noto Sans Rejang', 'notosansrejang/v21/Ktk2AKuMeZjqPnXgyqrib7DIogqwN4a3WYZB_sU.woff2'), + NotoFont('Noto Sans Runic', 'notosansrunic/v17/H4c_BXWPl9DZ0Xe_nHUaus7W68WWbhpvHtgIYg.woff2'), + NotoFont( + 'Noto Sans Saurashtra', + 'notosanssaurashtra/v23/ea8GacQ0Wfz_XKWXe6OtoA8w8zvmYwTef9nYjhPTSIx9.woff2', + ), + NotoFont( + 'Noto Sans Sharada', + 'notosanssharada/v16/gok0H7rwAEdtF9N8-mdTGALG6p0kwoXOPOwr4H8a.woff2', + ), + NotoFont( + 'Noto Sans Shavian', + 'notosansshavian/v17/CHy5V_HZE0jxJBQlqAeCKjJvQBNF4EFVSplv2Cwg.woff2', + ), + NotoFont( + 'Noto Sans Siddham', + 'notosanssiddham/v20/OZpZg-FwqiNLe9PELUikxTWDoCCeGqnYk3Ic92ZH.woff2', + ), + NotoFont( + 'Noto Sans Sinhala', + 'notosanssinhala/v32/yMJ2MJBya43H0SUF_WmcBEEf4rQVO2P524V5N_MxQzQtb-tf5dJbC30Fu9zUwg2a5l0LpJwbQRM.woff2', + ), + NotoFont( + 'Noto Sans Sogdian', + 'notosanssogdian/v16/taiQGn5iC4--qtsfi4Jp6eHPnfxQBo-7Pm6KHidM.woff2', + ), + NotoFont( + 'Noto Sans Sora Sompeng', + 'notosanssorasompeng/v24/PlIRFkO5O6RzLfvNNVSioxM2_OTrEhPyDLolKvCsHzCxWuGkYHR818DsZXJQd4Mu.woff2', + ), + NotoFont( + 'Noto Sans Soyombo', + 'notosanssoyombo/v17/RWmSoL-Y6-8q5LTtXs6MF6q7xsxgY0FuIFOcK25W.woff2', + ), + NotoFont( + 'Noto Sans Sundanese', + 'notosanssundanese/v26/FwZw7_84xUkosG2xJo2gm7nFwSLQkdymq2mkz3Gz1_b6ctxpNNHHizv7fQES.woff2', + ), + NotoFont( + 'Noto Sans Syloti Nagri', + 'notosanssylotinagri/v23/uU9eCAQZ75uhfF9UoWDRiY3q7Sf_VFV3m4dGFVLxN87gsj0.woff2', + ), + NotoFont( + 'Noto Sans Symbols', + 'notosanssymbols/v43/rP2up3q65FkAtHfwd-eIS2brbDN6gxP34F9jRRCe4W3gfQ8gb_VFRkzrbQ.woff2', + ), + NotoFont( + 'Noto Sans Syriac', + 'notosanssyriac/v16/Ktk7AKuMeZjqPnXgyqribqzQqgW0LYiVqV7dXcP0C-VD9MaMyZfUL_FC.woff2', + ), + NotoFont( + 'Noto Sans Tagalog', + 'notosanstagalog/v22/J7aFnoNzCnFcV9ZI-sUYuvote1R0wwEFA8jHexnL.woff2', + ), + NotoFont( + 'Noto Sans Tagbanwa', + 'notosanstagbanwa/v18/Y4GWYbB8VTEp4t3MKJSMmQdIKjRtt_nZQzQEaYpGoQ.woff2', + ), + NotoFont('Noto Sans Tai Le', 'notosanstaile/v17/vEFK2-VODB8RrNDvZSUmVxEATwR58te1W77HtMo.woff2'), + NotoFont( + 'Noto Sans Tai Tham', + 'notosanstaitham/v20/kJEbBv0U4hgtwxDUw2x9q7tbjLIfbPGHBoaVSAZ3MdLJBCUbPg-uyaRGKMw.woff2', + ), + NotoFont( + 'Noto Sans Tai Viet', + 'notosanstaiviet/v19/8QIUdj3HhN_lv4jf9vsE-9GMOLsaSPZr7o4fWsRO9w.woff2', + ), + NotoFont('Noto Sans Takri', 'notosanstakri/v24/TuGJUVpzXI5FBtUq5a8bnKIOdTwQMe_W3khJXg.woff2'), + NotoFont( + 'Noto Sans Tamil', + 'notosanstamil/v27/ieVc2YdFI3GCY6SyQy1KfStzYKZgzN1z4LKDbeZce-0429tBManUktuex7vGo70UqKDt_EvT.woff2', + ), + NotoFont( + 'Noto Sans Tamil Supplement', + 'notosanstamilsupplement/v21/DdTz78kEtnooLS5rXF1DaruiCd_bFp_Ph4sGcn7ax_vpAeMkeq1x.woff2', + ), + NotoFont( + 'Noto Sans Telugu', + 'notosanstelugu/v26/0FlxVOGZlE2Rrtr-HmgkMWJNjJ5_RyT8o8c7fHkeg-esVC5dzHkHIJQqrEntezbqREbf-3v37w.woff2', + ), + NotoFont( + 'Noto Sans Thaana', + 'notosansthaana/v24/C8c14dM-vnz-s-3jaEsxlxHkBH-WZOETXfoQrfQ9Y4XrbhLknu4-tbNu.woff2', + ), + NotoFont( + 'Noto Sans Thai', + 'notosansthai/v25/iJWnBXeUZi_OHPqn4wq6hQ2_hbJ1xyN9wd43SofNWcd1MKVQt_So_9CdU5RtpzR-QRvzzXg.woff2', + ), + NotoFont( + 'Noto Sans Tifinagh', + 'notosanstifinagh/v20/I_uzMoCduATTei9eI8dawkHIwvmhCvbn77nEcXfs4Q.woff2', + ), + NotoFont( + 'Noto Sans Tirhuta', + 'notosanstirhuta/v16/t5t6IQYRNJ6TWjahPR6X-M-apUyby7uDUBsTrn5P.woff2', + ), + NotoFont( + 'Noto Sans Ugaritic', + 'notosansugaritic/v16/3qTwoiqhnSyU8TNFIdhZVCwbjCpkAXXkNxoIkiazfg.woff2', + ), + NotoFont('Noto Sans Vai', 'notosansvai/v17/NaPecZTSBuhTirw6IaFn_UrURMHsDIRSfr0.woff2'), + NotoFont('Noto Sans Wancho', 'notosanswancho/v17/zrf-0GXXyfn6Fs0lH9P4cUubP0GBqAbopiRfKp8.woff2'), + NotoFont( + 'Noto Sans Warang Citi', + 'notosanswarangciti/v17/EYqtmb9SzL1YtsZSScyKDXIeOv3w-zgsNvKRoOVCCXzdgA.woff2', + ), + NotoFont('Noto Sans Yi', 'notosansyi/v19/sJoD3LFXjsSdcnzn071rO3apwFDJNVgSNg.woff2'), + NotoFont( + 'Noto Sans Zanabazar Square', + 'notosanszanabazarsquare/v19/Cn-jJsuGWQxOjaGwMQ6fOicyxLBEMRfDtkzl4uagQtJ0OCEgN0Gc.woff2', + ), + NotoFont( + 'Noto Serif Tibetan', + 'notoseriftibetan/v22/gokGH7nwAEdtF9N45n0Vaz7O-pk0wsvxHeDXMfqguoCmIrYcPSvrdSy_32c.woff2', + ), ]; // 6892 unique sets of fonts containing 44697 font references encoded in 80682 characters @@ -14518,29527 +16597,29526 @@ const String encodedFontSets = // #6890: 1 font: Vai₇₁₈. '27q,' // #6891: 1 font: Zanabazar Square₇₂₂. - '27u' - ; + '27u'; // 29518 ranges encoded in 76152 characters const String encodedFontSetRanges = - '1eA' // 0-1f - '75Z' // 20 #1975 - '76N' // 21 #1989 - '76P' // 22 #1991 - '73Z' // 23 #1923 - '76O' // 24 #1990 - '76Y' // 25 #2000 - '77B' // 26 #2003 - 'b24K' // 27-29 #634 - '51J' // 2a #1335 - '77E' // 2b #2006 - '24K' // 2c #634 - '77H' // 2d #2009 - 'a24K' // 2e-2f #634 - 'h51J' // 30-38 #1335 - '74A' // 39 #1924 - 'a24K' // 3a-3b #634 - '76V' // 3c #1997 - '76X' // 3d #1999 - '77D' // 3e #2005 - '77G' // 3f #2008 - '76Z' // 40 #2001 - 'c27P' // 41-44 #717 - '42N' // 45 #1105 - 'a27P' // 46-47 #717 - '76D' // 48 #1979 - '76F' // 49 #1981 - 'b27P' // 4a-4c #717 - '76E' // 4d #1980 - '76C' // 4e #1978 - '42N' // 4f #1105 - 'd27P' // 50-54 #717 - '42N' // 55 #1105 - 'd27P' // 56-5a #717 - 'b24K' // 5b-5d #634 - '76U' // 5e #1996 - '77A' // 5f #2002 - '76T' // 60 #1995 - 'y27P' // 61-7a #717 - '24K' // 7b #634 - '77F' // 7c #2007 - '24K' // 7d #634 - '77C' // 7e #2004 - 'F' // 7f #5 - '1eA' // 80-9f - '76A' // a0 #1976 - '79G' // a1 #2060 - '9A' // a2 #234 - '79J' // a3 #2063 - '48U' // a4 #1268 - '79M' // a5 #2066 - '62Y' // a6 #1636 - '76R' // a7 #1993 - '79H' // a8 #2061 - '74K' // a9 #1934 - '9A' // aa #234 - '42U' // ab #1112 - '121E' // ac #3150 - '247O' // ad #6436 - '51L' // ae #1337 - '79I' // af #2062 - '79N' // b0 #2067 - '121F' // b1 #3151 - '245W' // b2 #6392 - '245V' // b3 #6391 - '79L' // b4 #2065 - '62Y' // b5 #1636 - '9A' // b6 #234 - '79P' // b7 #2069 - '9A' // b8 #234 - '48U' // b9 #1268 - '9A' // ba #234 - '42U' // bb #1112 - 'b48U' // bc-be #1268 - '79S' // bf #2072 - '9A' // c0 #234 - '21S' // c1 #564 - '9A' // c2 #234 - 'b21S' // c3-c5 #564 - 'b9A' // c6-c8 #234 - '42T' // c9 #1111 - 'a9A' // ca-cb #234 - '21S' // cc #564 - '42V' // cd #1113 - 'b9A' // ce-d0 #234 - 'b21S' // d1-d3 #564 - '9A' // d4 #234 - '21S' // d5 #564 - '42V' // d6 #1113 - '76W' // d7 #1998 - '42S' // d8 #1110 - '9A' // d9 #234 - '21S' // da #564 - '9A' // db #234 - '42S' // dc #1110 - '21S' // dd #564 - '9A' // de #234 - '79F' // df #2059 - '52O' // e0 #1366 - '27Q' // e1 #718 - '52O' // e2 #1366 - 'b27Q' // e3-e5 #718 - '52N' // e6 #1365 - '79X' // e7 #2077 - '79U' // e8 #2074 - '79W' // e9 #2076 - '52P' // ea #1367 - '42T' // eb #1111 - 'a27Q' // ec-ed #718 - '52N' // ee #1365 - '42T' // ef #1111 - '9A' // f0 #234 - 'b27Q' // f1-f3 #718 - '79T' // f4 #2073 - '21S' // f5 #564 - '27Q' // f6 #718 - '76S' // f7 #1994 - '42S' // f8 #1110 - '79R' // f9 #2071 - '27Q' // fa #718 - '9A' // fb #234 - '52P' // fc #1367 - '42V' // fd #1113 - 'a9A' // fe-ff #234 - '32D' // 100 #835 - '36V' // 101 #957 - '78T' // 102 #2047 - '78X' // 103 #2051 - 'c8C' // 104-107 #210 - 'aE' // 108-109 #4 - 'e8C' // 10a-10f #210 - '78W' // 110 #2050 - '78V' // 111 #2049 - '32D' // 112 #835 - '36V' // 113 #957 - 'aE' // 114-115 #4 - 'c8C' // 116-119 #210 - '42R' // 11a #1109 - '32D' // 11b #835 - 'aE' // 11c-11d #4 - 'e8C' // 11e-123 #210 - 'aE' // 124-125 #4 - 'a8C' // 126-127 #210 - '49J' // 128 #1283 - '62X' // 129 #1635 - '78Z' // 12a #2053 - '36V' // 12b #957 - 'aE' // 12c-12d #4 - 'b8C' // 12e-130 #210 - '42W' // 131 #1114 - 'a80C' // 132-133 #2082 - 'aE' // 134-135 #4 - 'a8C' // 136-137 #210 - 'E' // 138 #4 - 'e8C' // 139-13e #210 - 'aE' // 13f-140 #4 - 'a8C' // 141-142 #210 - '42R' // 143 #1109 - '32D' // 144 #835 - 'a8C' // 145-146 #210 - '42R' // 147 #1109 - '78U' // 148 #2048 - 'E' // 149 #4 - 'a52L' // 14a-14b #1363 - '52K' // 14c #1362 - '79A' // 14d #2054 - '21F' // 14e #551 - '121C' // 14f #3148 - 'a8C' // 150-151 #210 - 'a9A' // 152-153 #234 - 'a8C' // 154-155 #210 - 'a52L' // 156-157 #1363 - 'c8C' // 158-15b #210 - 'aE' // 15c-15d #4 - 'c8C' // 15e-161 #210 - 'aE' // 162-163 #4 - 'a8C' // 164-165 #210 - 'aE' // 166-167 #4 - '247N' // 168 #6435 - '62X' // 169 #1635 - '32D' // 16a #835 - '36V' // 16b #957 - '79B' // 16c #2055 - '52K' // 16d #1362 - 'p8C' // 16e-17e #210 - 'rE' // 17f-191 #4 - '121B' // 192 #3147 - 'lE' // 193-19f #4 - '121A' // 1a0 #3146 - '245T' // 1a1 #6389 - 'lE' // 1a2-1ae #4 - '120Z' // 1af #3145 - '245U' // 1b0 #6390 - '1aE' // 1b1-1cc #4 - '80B' // 1cd #2081 - '80A' // 1ce #2080 - '21F' // 1cf #551 - '121D' // 1d0 #3149 - '21F' // 1d1 #551 - '35S' // 1d2 #928 - '21F' // 1d3 #551 - '35S' // 1d4 #928 - '21F' // 1d5 #551 - '49K' // 1d6 #1284 - '21F' // 1d7 #551 - '49K' // 1d8 #1284 - '21F' // 1d9 #551 - '35S' // 1da #928 - '21F' // 1db #551 - '35S' // 1dc #928 - 'zE' // 1dd-1f7 #4 - '49K' // 1f8 #1284 - '35S' // 1f9 #928 - '1cE' // 1fa-217 #4 - 'c8C' // 218-21b #210 - 'zE' // 21c-236 #4 - '8C' // 237 #210 - 'xE' // 238-250 #4 - '62W' // 251 #1634 - 'nE' // 252-260 #4 - '62W' // 261 #1634 - '3hE' // 262-2b8 #4 - '17K' // 2b9 #452 - 'E' // 2ba #4 - '120W' // 2bb #3142 - '261T' // 2bc #6805 - 'hE' // 2bd-2c5 #4 - '42W' // 2c6 #1114 - '79C' // 2c7 #2056 - 'E' // 2c8 #4 - '79D' // 2c9 #2057 - '124Y' // 2ca #3248 - '135O' // 2cb #3524 - 'E' // 2cc #4 - '262J' // 2cd #6821 - 'hE' // 2ce-2d6 #4 - '262W' // 2d7 #6834 - '41V' // 2d8 #1087 - '129Z' // 2d9 #3379 - '79Z' // 2da #2079 - '261J' // 2db #6795 - '42W' // 2dc #1114 - '79E' // 2dd #2058 - 'kE' // 2de-2e9 #4 - '41T' // 2ea #1085 - '120Y' // 2eb #3144 - 'sE' // 2ec-2ff #4 - '78R' // 300 #2045 - '78Q' // 301 #2044 - '77J' // 302 #2011 - '52C' // 303 #1354 - '78P' // 304 #2043 - '262A' // 305 #6812 - '41V' // 306 #1087 - '77I' // 307 #2010 - '52C' // 308 #1354 - '71L' // 309 #1857 - 'a41V' // 30a-30b #1087 - '120X' // 30c #3143 - 'E' // 30d #4 - '262E' // 30e #6816 - 'aE' // 30f-310 #4 - '17K' // 311 #452 - '77L' // 312 #2013 - '262Q' // 313 #6828 - 'kE' // 314-31f #4 - '50K' // 320 #1310 - 'aE' // 321-322 #4 - '80E' // 323 #2084 - '261Z' // 324 #6811 - '50K' // 325 #1310 - 'a77K' // 326-327 #2012 - '41V' // 328 #1087 - 'cE' // 329-32c #4 - 'a50K' // 32d-32e #1310 - 'E' // 32f #4 - '261Y' // 330 #6810 - '261V' // 331 #6807 - 'lE' // 332-33e #4 - '17K' // 33f #452 - 'nE' // 340-34e #4 - '261M' // 34f #6798 - 'gE' // 350-357 #4 - '262S' // 358 #6830 - 'E' // 359 #4 - '262R' // 35a #6829 - 'bE' // 35b-35d #4 - '261W' // 35e #6808 - 'aE' // 35f-360 #4 - '17K' // 361 #452 - 'qE' // 362-373 #4 - 'a17K' // 374-375 #452 - 'aE' // 376-377 #4 - 'aA' // 378-379 - 'eE' // 37a-37f #4 - 'cA' // 380-383 - 'fE' // 384-38a #4 - 'A' // 38b - 'E' // 38c #4 - 'A' // 38d - 'bE' // 38e-390 #4 - 'c50G' // 391-394 #1306 - '261I' // 395 #6794 - 'k50G' // 396-3a1 #1306 - 'A' // 3a2 - 'f50G' // 3a3-3a9 #1306 - 'fE' // 3aa-3b0 #4 - '261G' // 3b1 #6792 - 'd36N' // 3b2-3b6 #949 - '71G' // 3b7 #1852 - '36N' // 3b8 #949 - '50H' // 3b9 #1307 - '71G' // 3ba #1852 - '50H' // 3bb #1307 - '36N' // 3bc #949 - '50H' // 3bd #1307 - 'c36N' // 3be-3c1 #949 - '8B' // 3c2 #209 - 'f36N' // 3c3-3c9 #949 - 'fE' // 3ca-3d0 #4 - '8B' // 3d1 #209 - 'bE' // 3d2-3d4 #4 - 'a8B' // 3d5-3d6 #209 - 'bE' // 3d7-3d9 #4 - '41W' // 3da #1088 - 'E' // 3db #4 - '41W' // 3dc #1088 - 'E' // 3dd #4 - '41W' // 3de #1088 - 'E' // 3df #4 - '41W' // 3e0 #1088 - 'E' // 3e1 #4 - 'm50R' // 3e2-3ef #1317 - 'a8B' // 3f0-3f1 #209 - 'aE' // 3f2-3f3 #4 - 'a8B' // 3f4-3f5 #209 - 'jE' // 3f6-400 #4 - '49I' // 401 #1282 - 'mE' // 402-40f #4 - '2k49I' // 410-44f #1282 - 'E' // 450 #4 - '49I' // 451 #1282 - '1vE' // 452-482 #4 - '262F' // 483 #6817 - '41X' // 484 #1089 - 'aE' // 485-486 #4 - '41X' // 487 #1089 - '6kE' // 488-52f #4 - 'A' // 530 - '1k36P' // 531-556 #951 - 'aA' // 557-558 - '1u36P' // 559-588 #951 - '263E' // 589 #6842 - '36P' // 58a #951 - 'aA' // 58b-58c - 'b36P' // 58d-58f #951 - 'A' // 590 - '2b21R' // 591-5c7 #563 - 'gA' // 5c8-5cf - 'z21R' // 5d0-5ea #563 - 'cA' // 5eb-5ee - 'e21R' // 5ef-5f4 #563 - 'jA' // 5f5-5ff - 'd4W' // 600-604 #126 - '263A' // 605 #6838 - 'e4W' // 606-60b #126 - '50M' // 60c #1312 - 'm4W' // 60d-61a #126 - '50M' // 61b #1312 - 'b4W' // 61c-61e #126 - '262Y' // 61f #6836 - '4W' // 620 #126 - '50N' // 621 #1313 - 'd4W' // 622-626 #126 - '71M' // 627 #1858 - 'w4W' // 628-63f #126 - '262X' // 640 #6835 - 'i4W' // 641-64a #126 - 'j50N' // 64b-655 #1313 - 'i4W' // 656-65f #126 - 'i263B' // 660-669 #6839 - '50M' // 66a #1312 - 'a263D' // 66b-66c #6841 - '41Y' // 66d #1090 - 'a4W' // 66e-66f #126 - '50N' // 670 #1313 - '3t4W' // 671-6d3 #126 - '41Y' // 6d4 #1090 - 'z4W' // 6d5-6ef #126 - 'i71M' // 6f0-6f9 #1858 - 'e4W' // 6fa-6ff #126 - 'm51E' // 700-70d #1330 - 'A' // 70e - '2g51E' // 70f-74a #1330 - 'aA' // 74b-74c - 'b51E' // 74d-74f #1330 - '1u4W' // 750-77f #126 - '1w264Z' // 780-7b1 #6889 - 'mA' // 7b2-7bf - '2f72K' // 7c0-7fa #1882 - 'aA' // 7fb-7fc - 'b72K' // 7fd-7ff #1882 - '2kA' // 800-83f - '1a72G' // 840-85b #1878 - 'aA' // 85c-85d - '72G' // 85e #1878 - 'pA' // 85f-86f - '1d4W' // 870-88e #126 - 'A' // 88f - 'a4W' // 890-891 #126 - 'eA' // 892-897 - '3y4W' // 898-8ff #126 - '2h36O' // 900-93c #950 - '262D' // 93d #6815 - 'r36O' // 93e-950 #950 - 'a261R' // 951-952 #6803 - 'p36O' // 953-963 #950 - 'a261Q' // 964-965 #6802 - 'i262C' // 966-96f #6814 - 'o36O' // 970-97f #950 - 'c11N' // 980-983 #299 - 'A' // 984 - 'g11N' // 985-98c #299 - 'aA' // 98d-98e - 'a11N' // 98f-990 #299 - 'aA' // 991-992 - 'u11N' // 993-9a8 #299 - 'A' // 9a9 - 'f11N' // 9aa-9b0 #299 - 'A' // 9b1 - '11N' // 9b2 #299 - 'bA' // 9b3-9b5 - 'c11N' // 9b6-9b9 #299 - 'aA' // 9ba-9bb - 'h11N' // 9bc-9c4 #299 - 'aA' // 9c5-9c6 - 'a11N' // 9c7-9c8 #299 - 'aA' // 9c9-9ca - 'c11N' // 9cb-9ce #299 - 'gA' // 9cf-9d6 - '11N' // 9d7 #299 - 'cA' // 9d8-9db - 'a11N' // 9dc-9dd #299 - 'A' // 9de - 'd11N' // 9df-9e3 #299 - 'aA' // 9e4-9e5 - 'i263F' // 9e6-9ef #6843 - 'c11N' // 9f0-9f3 #299 - 'c263I' // 9f4-9f7 #6846 - 'f11N' // 9f8-9fe #299 - 'aA' // 9ff-a00 - 'b11P' // a01-a03 #301 - 'A' // a04 - 'e11P' // a05-a0a #301 - 'cA' // a0b-a0e - 'a11P' // a0f-a10 #301 - 'aA' // a11-a12 - 'u11P' // a13-a28 #301 - 'A' // a29 - 'f11P' // a2a-a30 #301 - 'A' // a31 - 'a11P' // a32-a33 #301 - 'A' // a34 - 'a11P' // a35-a36 #301 - 'A' // a37 - 'a11P' // a38-a39 #301 - 'aA' // a3a-a3b - '11P' // a3c #301 - 'A' // a3d - 'd11P' // a3e-a42 #301 - 'cA' // a43-a46 - 'a11P' // a47-a48 #301 - 'aA' // a49-a4a - 'b11P' // a4b-a4d #301 - 'bA' // a4e-a50 - '11P' // a51 #301 - 'fA' // a52-a58 - 'c11P' // a59-a5c #301 - 'A' // a5d - '11P' // a5e #301 - 'fA' // a5f-a65 - 'i264A' // a66-a6f #6864 - 'f11P' // a70-a76 #301 - 'iA' // a77-a80 - 'b13P' // a81-a83 #353 - 'A' // a84 - 'h13P' // a85-a8d #353 - 'A' // a8e - 'b13P' // a8f-a91 #353 - 'A' // a92 - 'u13P' // a93-aa8 #353 - 'A' // aa9 - 'f13P' // aaa-ab0 #353 - 'A' // ab1 - 'a13P' // ab2-ab3 #353 - 'A' // ab4 - 'd13P' // ab5-ab9 #353 - 'aA' // aba-abb - 'i13P' // abc-ac5 #353 - 'A' // ac6 - 'b13P' // ac7-ac9 #353 - 'A' // aca - 'b13P' // acb-acd #353 - 'aA' // ace-acf - '13P' // ad0 #353 - 'nA' // ad1-adf - 'c13P' // ae0-ae3 #353 - 'aA' // ae4-ae5 - 'i263Z' // ae6-aef #6863 - 'a13P' // af0-af1 #353 - 'fA' // af2-af8 - 'f13P' // af9-aff #353 - 'A' // b00 - 'b13Q' // b01-b03 #354 - 'A' // b04 - 'g13Q' // b05-b0c #354 - 'aA' // b0d-b0e - 'a13Q' // b0f-b10 #354 - 'aA' // b11-b12 - 'u13Q' // b13-b28 #354 - 'A' // b29 - 'f13Q' // b2a-b30 #354 - 'A' // b31 - 'a13Q' // b32-b33 #354 - 'A' // b34 - 'd13Q' // b35-b39 #354 - 'aA' // b3a-b3b - 'h13Q' // b3c-b44 #354 - 'aA' // b45-b46 - 'a13Q' // b47-b48 #354 - 'aA' // b49-b4a - 'b13Q' // b4b-b4d #354 - 'fA' // b4e-b54 - 'b13Q' // b55-b57 #354 - 'cA' // b58-b5b - 'a13Q' // b5c-b5d #354 - 'A' // b5e - 'd13Q' // b5f-b63 #354 - 'aA' // b64-b65 - 'q13Q' // b66-b77 #354 - 'iA' // b78-b81 - 'a10O' // b82-b83 #274 - 'A' // b84 - 'e10O' // b85-b8a #274 - 'bA' // b8b-b8d - 'b10O' // b8e-b90 #274 - 'A' // b91 - 'c10O' // b92-b95 #274 - 'bA' // b96-b98 - 'a10O' // b99-b9a #274 - 'A' // b9b - '10O' // b9c #274 - 'A' // b9d - 'a10O' // b9e-b9f #274 - 'bA' // ba0-ba2 - 'a10O' // ba3-ba4 #274 - 'bA' // ba5-ba7 - 'a10O' // ba8-ba9 #274 - '31U' // baa #826 - 'bA' // bab-bad - 'f10O' // bae-bb4 #274 - '31U' // bb5 #826 - 'c10O' // bb6-bb9 #274 - 'cA' // bba-bbd - 'd10O' // bbe-bc2 #274 - 'bA' // bc3-bc5 - 'b10O' // bc6-bc8 #274 - 'A' // bc9 - 'c10O' // bca-bcd #274 - 'aA' // bce-bcf - '10O' // bd0 #274 - 'eA' // bd1-bd6 - '10O' // bd7 #274 - 'mA' // bd8-be5 - 'l31U' // be6-bf2 #826 - 'g10O' // bf3-bfa #274 - 'dA' // bfb-bff - 'l15D' // c00-c0c #393 - 'A' // c0d - 'b15D' // c0e-c10 #393 - 'A' // c11 - 'v15D' // c12-c28 #393 - 'A' // c29 - 'o15D' // c2a-c39 #393 - 'aA' // c3a-c3b - 'h15D' // c3c-c44 #393 - 'A' // c45 - 'b15D' // c46-c48 #393 - 'A' // c49 - 'c15D' // c4a-c4d #393 - 'fA' // c4e-c54 - 'a15D' // c55-c56 #393 - 'A' // c57 - 'b15D' // c58-c5a #393 - 'aA' // c5b-c5c - '15D' // c5d #393 - 'aA' // c5e-c5f - 'c15D' // c60-c63 #393 - 'aA' // c64-c65 - 'i15D' // c66-c6f #393 - 'fA' // c70-c76 - 'h15D' // c77-c7f #393 - 'l15B' // c80-c8c #391 - 'A' // c8d - 'b15B' // c8e-c90 #391 - 'A' // c91 - 'v15B' // c92-ca8 #391 - 'A' // ca9 - 'i15B' // caa-cb3 #391 - 'A' // cb4 - 'd15B' // cb5-cb9 #391 - 'aA' // cba-cbb - 'h15B' // cbc-cc4 #391 - 'A' // cc5 - 'b15B' // cc6-cc8 #391 - 'A' // cc9 - 'c15B' // cca-ccd #391 - 'fA' // cce-cd4 - 'a15B' // cd5-cd6 #391 - 'eA' // cd7-cdc - 'a15B' // cdd-cde #391 - 'A' // cdf - 'c15B' // ce0-ce3 #391 - 'aA' // ce4-ce5 - 'i15B' // ce6-cef #391 - 'A' // cf0 - 'b15B' // cf1-cf3 #391 - 'kA' // cf4-cff - 'l27J' // d00-d0c #711 - 'A' // d0d - 'b27J' // d0e-d10 #711 - 'A' // d11 - '1x27J' // d12-d44 #711 - 'A' // d45 - 'b27J' // d46-d48 #711 - 'A' // d49 - 'e27J' // d4a-d4f #711 - 'cA' // d50-d53 - 'o27J' // d54-d63 #711 - 'aA' // d64-d65 - 'y27J' // d66-d7f #711 - 'A' // d80 - 'b15C' // d81-d83 #392 - 'A' // d84 - 'q15C' // d85-d96 #392 - 'bA' // d97-d99 - 'w15C' // d9a-db1 #392 - 'A' // db2 - 'h15C' // db3-dbb #392 - 'A' // dbc - '15C' // dbd #392 - 'aA' // dbe-dbf - 'f15C' // dc0-dc6 #392 - 'bA' // dc7-dc9 - '15C' // dca #392 - 'cA' // dcb-dce - 'e15C' // dcf-dd4 #392 - 'A' // dd5 - '15C' // dd6 #392 - 'A' // dd7 - 'g15C' // dd8-ddf #392 - 'eA' // de0-de5 - 'i15C' // de6-def #392 - 'aA' // df0-df1 - 'b15C' // df2-df4 #392 - 'kA' // df5-e00 - '2e73D' // e01-e3a #1901 - 'cA' // e3b-e3e - '1b73D' // e3f-e5b #1901 - '1jA' // e5c-e80 - 'a17M' // e81-e82 #454 - 'A' // e83 - '17M' // e84 #454 - 'A' // e85 - 'd17M' // e86-e8a #454 - 'A' // e8b - 'w17M' // e8c-ea3 #454 - 'A' // ea4 - '17M' // ea5 #454 - 'A' // ea6 - 'v17M' // ea7-ebd #454 - 'aA' // ebe-ebf - 'd17M' // ec0-ec4 #454 - 'A' // ec5 - '17M' // ec6 #454 - 'A' // ec7 - 'f17M' // ec8-ece #454 - 'A' // ecf - 'i17M' // ed0-ed9 #454 - 'aA' // eda-edb - 'c17M' // edc-edf #454 - '1eA' // ee0-eff - '2s31Y' // f00-f47 #830 - 'A' // f48 - '1i31Y' // f49-f6c #830 - 'cA' // f6d-f70 - '1l31Y' // f71-f97 #830 - 'A' // f98 - '1i31Y' // f99-fbc #830 - 'A' // fbd - 'n31Y' // fbe-fcc #830 - 'A' // fcd - 'l31Y' // fce-fda #830 - '1jA' // fdb-fff - '2k42D' // 1000-103f #1095 - 'i263N' // 1040-1049 #6851 - '3g42D' // 104a-109f #1095 - '1k19P' // 10a0-10c5 #509 - 'A' // 10c6 - '19P' // 10c7 #509 - 'dA' // 10c8-10cc - '19P' // 10cd #509 - 'aA' // 10ce-10cf - '1p19P' // 10d0-10fa #509 - '71K' // 10fb #1856 - 'c19P' // 10fc-10ff #509 - '9uA' // 1100-11ff - '2t3R' // 1200-1248 #95 - 'A' // 1249 - 'c3R' // 124a-124d #95 - 'aA' // 124e-124f - 'f3R' // 1250-1256 #95 - 'A' // 1257 - '3R' // 1258 #95 - 'A' // 1259 - 'c3R' // 125a-125d #95 - 'aA' // 125e-125f - '1n3R' // 1260-1288 #95 - 'A' // 1289 - 'c3R' // 128a-128d #95 - 'aA' // 128e-128f - '1f3R' // 1290-12b0 #95 - 'A' // 12b1 - 'c3R' // 12b2-12b5 #95 - 'aA' // 12b6-12b7 - 'f3R' // 12b8-12be #95 - 'A' // 12bf - '3R' // 12c0 #95 - 'A' // 12c1 - 'c3R' // 12c2-12c5 #95 - 'aA' // 12c6-12c7 - 'n3R' // 12c8-12d6 #95 - 'A' // 12d7 - '2d3R' // 12d8-1310 #95 - 'A' // 1311 - 'c3R' // 1312-1315 #95 - 'aA' // 1316-1317 - '2n3R' // 1318-135a #95 - 'aA' // 135b-135c - '1e3R' // 135d-137c #95 - 'bA' // 137d-137f - 'y3R' // 1380-1399 #95 - 'eA' // 139a-139f - '3g50Q' // 13a0-13f5 #1316 - 'aA' // 13f6-13f7 - 'e50Q' // 13f8-13fd #1316 - 'aA' // 13fe-13ff - '24o50P' // 1400-167f #1315 - '1b264I' // 1680-169c #6872 - 'bA' // 169d-169f - '3j264S' // 16a0-16f8 #6882 - 'fA' // 16f9-16ff - 'u72Y' // 1700-1715 #1896 - 'hA' // 1716-171e - '72Y' // 171f #1896 - 't264C' // 1720-1734 #6866 - 'a263L' // 1735-1736 #6849 - 'hA' // 1737-173f - 's263K' // 1740-1753 #6848 - 'kA' // 1754-175f - 'l51F' // 1760-176c #1331 - 'A' // 176d - 'b51F' // 176e-1770 #1331 - 'A' // 1771 - 'a51F' // 1772-1773 #1331 - 'kA' // 1774-177f - '3o42C' // 1780-17dd #1094 - 'aA' // 17de-17df - 'i42C' // 17e0-17e9 #1094 - 'eA' // 17ea-17ef - 'i42C' // 17f0-17f9 #1094 - 'eA' // 17fa-17ff - '31X' // 1800 #829 - 'b72J' // 1801-1803 #1881 - '31X' // 1804 #829 - '72J' // 1805 #1881 - 's31X' // 1806-1819 #829 - 'eA' // 181a-181f - '3j31X' // 1820-1878 #829 - 'fA' // 1879-187f - '1p31X' // 1880-18aa #829 - 'dA' // 18ab-18af - '2q50P' // 18b0-18f5 #1315 - 'iA' // 18f6-18ff - '1d36Q' // 1900-191e #952 - 'A' // 191f - 'k36Q' // 1920-192b #952 - 'cA' // 192c-192f - 'k36Q' // 1930-193b #952 - 'cA' // 193c-193f - '36Q' // 1940 #952 - 'bA' // 1941-1943 - 'k36Q' // 1944-194f #952 - '1c72Z' // 1950-196d #1897 - 'aA' // 196e-196f - 'd72Z' // 1970-1974 #1897 - 'jA' // 1975-197f - '1q42E' // 1980-19ab #1096 - 'cA' // 19ac-19af - 'y42E' // 19b0-19c9 #1096 - 'eA' // 19ca-19cf - 'j42E' // 19d0-19da #1096 - 'bA' // 19db-19dd - 'a42E' // 19de-19df #1096 - '1e42C' // 19e0-19ff #1094 - '1a71T' // 1a00-1a1b #1865 - 'aA' // 1a1c-1a1d - 'a71T' // 1a1e-1a1f #1865 - '2j36T' // 1a20-1a5e #955 - 'A' // 1a5f - '1b36T' // 1a60-1a7c #955 - 'aA' // 1a7d-1a7e - 'j36T' // 1a7f-1a89 #955 - 'eA' // 1a8a-1a8f - 'i36T' // 1a90-1a99 #955 - 'eA' // 1a9a-1a9f - 'm36T' // 1aa0-1aad #955 - 'aA' // 1aae-1aaf - 'pE' // 1ab0-1ac0 #4 - 'cA' // 1ac1-1ac4 - 'E' // 1ac5 #4 - 'A' // 1ac6 - 'gE' // 1ac7-1ace #4 - '1vA' // 1acf-1aff - '2w71O' // 1b00-1b4b #1860 - 'cA' // 1b4c-1b4f - '1r71O' // 1b50-1b7c #1860 - 'bA' // 1b7d-1b7f - '2k72X' // 1b80-1bbf #1895 - '1y71R' // 1bc0-1bf3 #1863 - 'gA' // 1bf4-1bfb - 'c71R' // 1bfc-1bff #1863 - '2c50V' // 1c00-1c37 #1321 - 'bA' // 1c38-1c3a - 'n50V' // 1c3b-1c49 #1321 - 'bA' // 1c4a-1c4c - 'b50V' // 1c4d-1c4f #1321 - '1u264J' // 1c50-1c7f #6873 - 'hE' // 1c80-1c88 #4 - 'fA' // 1c89-1c8f - '1p19P' // 1c90-1cba #509 - 'aA' // 1cbb-1cbc - 'b19P' // 1cbd-1cbf #509 - 'g72X' // 1cc0-1cc7 #1895 - 'gA' // 1cc8-1ccf - '71S' // 1cd0 #1864 - '17L' // 1cd1 #453 - '71S' // 1cd2 #1864 - '50S' // 1cd3 #1318 - '17L' // 1cd4 #453 - 'a27H' // 1cd5-1cd6 #709 - '42B' // 1cd7 #1093 - '27H' // 1cd8 #709 - '42B' // 1cd9 #1093 - '263T' // 1cda #6857 - '17L' // 1cdb #453 - 'a42B' // 1cdc-1cdd #1093 - 'a17L' // 1cde-1cdf #453 - '42B' // 1ce0 #1093 - '27H' // 1ce1 #709 - 'g17L' // 1ce2-1ce9 #453 - '27H' // 1cea #709 - 'a17L' // 1ceb-1cec #453 - '27H' // 1ced #709 - 'c17L' // 1cee-1cf1 #453 - '263G' // 1cf2 #6844 - '50S' // 1cf3 #1318 - '263P' // 1cf4 #6853 - '263H' // 1cf5 #6845 - '27H' // 1cf6 #709 - '11N' // 1cf7 #299 - 'a50S' // 1cf8-1cf9 #1318 - 'eA' // 1cfa-1cff - '7vE' // 1d00-1dcc #4 - '17K' // 1dcd #452 - '1qE' // 1dce-1df9 #4 - 'A' // 1dfa - '262N' // 1dfb #6825 - '2mE' // 1dfc-1e3d #4 - 'a21F' // 1e3e-1e3f #551 - '2kE' // 1e40-1e7f #4 - 'e8C' // 1e80-1e85 #210 - 'wE' // 1e86-1e9d #4 - '8C' // 1e9e #210 - 'E' // 1e9f #4 - '15Z' // 1ea0 #415 - '69E' // 1ea1 #1798 - '15Z' // 1ea2 #415 - '69E' // 1ea3 #1798 - 'e15Z' // 1ea4-1ea9 #415 - '11B' // 1eaa #287 - 'b15Z' // 1eab-1ead #415 - '120H' // 1eae #3127 - '15Z' // 1eaf #415 - '11B' // 1eb0 #287 - '15Z' // 1eb1 #415 - '11B' // 1eb2 #287 - '15Z' // 1eb3 #415 - '11B' // 1eb4 #287 - '15Z' // 1eb5 #415 - '11B' // 1eb6 #287 - '15Z' // 1eb7 #415 - '11B' // 1eb8 #287 - '15Z' // 1eb9 #415 - '11B' // 1eba #287 - '15Z' // 1ebb #415 - '11B' // 1ebc #287 - 'a15Z' // 1ebd-1ebe #415 - '69F' // 1ebf #1799 - 'c11A' // 1ec0-1ec3 #286 - '11B' // 1ec4 #287 - 'a11A' // 1ec5-1ec6 #286 - '69F' // 1ec7 #1799 - '11B' // 1ec8 #287 - '11A' // 1ec9 #286 - '35W' // 1eca #932 - 'b11A' // 1ecb-1ecd #286 - '11B' // 1ece #287 - 'b11A' // 1ecf-1ed1 #286 - '35W' // 1ed2 #932 - '11A' // 1ed3 #286 - '62U' // 1ed4 #1632 - '11A' // 1ed5 #286 - '11B' // 1ed6 #287 - 'f11A' // 1ed7-1edd #286 - '35W' // 1ede #932 - '11A' // 1edf #286 - '11B' // 1ee0 #287 - '11A' // 1ee1 #286 - '11B' // 1ee2 #287 - '11A' // 1ee3 #286 - '62U' // 1ee4 #1632 - 'b11A' // 1ee5-1ee7 #286 - '35W' // 1ee8 #932 - '11A' // 1ee9 #286 - '35W' // 1eea #932 - '11A' // 1eeb #286 - '11B' // 1eec #287 - '11A' // 1eed #286 - '11B' // 1eee #287 - 'b11A' // 1eef-1ef1 #286 - '78Y' // 1ef2 #2052 - '78S' // 1ef3 #2046 - 'b49J' // 1ef4-1ef6 #1283 - '62T' // 1ef7 #1631 - '49J' // 1ef8 #1283 - '62T' // 1ef9 #1631 - '1aE' // 1efa-1f15 #4 - 'aA' // 1f16-1f17 - 'eE' // 1f18-1f1d #4 - 'aA' // 1f1e-1f1f - '1kE' // 1f20-1f45 #4 - 'aA' // 1f46-1f47 - 'eE' // 1f48-1f4d #4 - 'aA' // 1f4e-1f4f - 'gE' // 1f50-1f57 #4 - 'A' // 1f58 - 'E' // 1f59 #4 - 'A' // 1f5a - 'E' // 1f5b #4 - 'A' // 1f5c - 'E' // 1f5d #4 - 'A' // 1f5e - '1dE' // 1f5f-1f7d #4 - 'aA' // 1f7e-1f7f - '1zE' // 1f80-1fb4 #4 - 'A' // 1fb5 - 'nE' // 1fb6-1fc4 #4 - 'A' // 1fc5 - 'mE' // 1fc6-1fd3 #4 - 'aA' // 1fd4-1fd5 - 'eE' // 1fd6-1fdb #4 - 'A' // 1fdc - 'rE' // 1fdd-1fef #4 - 'aA' // 1ff0-1ff1 - 'bE' // 1ff2-1ff4 #4 - 'A' // 1ff5 - 'hE' // 1ff6-1ffe #4 - 'A' // 1fff - 'aE' // 2000-2001 #4 - '120M' // 2002 #3132 - '120S' // 2003 #3138 - 'dE' // 2004-2008 #4 - '71J' // 2009 #1855 - 'E' // 200a #4 - '261K' // 200b #6796 - '80D' // 200c #2083 - '73K' // 200d #1908 - 'a261N' // 200e-200f #6799 - '120U' // 2010 #3140 - '120R' // 2011 #3137 - '120Q' // 2012 #3136 - 'a42U' // 2013-2014 #1112 - '120V' // 2015 #3141 - '120N' // 2016 #3133 - 'E' // 2017 #4 - 'a79Y' // 2018-2019 #2078 - '52M' // 201a #1364 - '71H' // 201b #1853 - 'a79O' // 201c-201d #2068 - '52M' // 201e #1364 - 'E' // 201f #4 - '120I' // 2020 #3128 - '62V' // 2021 #1633 - '77N' // 2022 #2015 - 'E' // 2023 #4 - '261O' // 2024 #6800 - '120T' // 2025 #3139 - '79Q' // 2026 #2070 - '216U' // 2027 #5636 - 'eE' // 2028-202d #4 - '71L' // 202e #1857 - '261L' // 202f #6797 - '62V' // 2030 #1633 - 'E' // 2031 #4 - '120L' // 2032 #3131 - '120J' // 2033 #3129 - '8B' // 2034 #209 - '120O' // 2035 #3134 - 'a8B' // 2036-2037 #209 - 'E' // 2038 #4 - 'a79V' // 2039-203a #2075 - '206L' // 203b #5367 - '74W' // 203c #1946 - 'dE' // 203d-2041 #4 - '120P' // 2042 #3135 - 'E' // 2043 #4 - '80F' // 2044 #2085 - 'aE' // 2045-2046 #4 - '41T' // 2047 #1085 - '260L' // 2048 #6771 - '74U' // 2049 #1944 - 'dE' // 204a-204e #4 - '71I' // 204f #1854 - 'E' // 2050 #4 - '41T' // 2051 #1085 - 'E' // 2052 #4 - '17K' // 2053 #452 - 'E' // 2054 #4 - '262T' // 2055 #6831 - '17K' // 2056 #452 - '8B' // 2057 #209 - 'a17K' // 2058-2059 #452 - '262P' // 205a #6827 - 'aE' // 205b-205c #4 - '262L' // 205d #6823 - '262O' // 205e #6826 - 'eE' // 205f-2064 #4 - 'A' // 2065 - 'kE' // 2066-2071 #4 - 'aA' // 2072-2073 - '120K' // 2074 #3130 - 'lE' // 2075-2081 #4 - 'b262V' // 2082-2084 #6833 - 'iE' // 2085-208e #4 - 'A' // 208f - 'lE' // 2090-209c #4 - 'bA' // 209d-209f - 'hE' // 20a0-20a8 #4 - '119B' // 20a9 #3095 - '262G' // 20aa #6818 - '119A' // 20ab #3094 - '79K' // 20ac #2064 - '262I' // 20ad #6820 - 'jE' // 20ae-20b8 #4 - '261S' // 20b9 #6804 - 'cE' // 20ba-20bd #4 - '71K' // 20be #1856 - 'aE' // 20bf-20c0 #4 - 'nA' // 20c1-20cf - 'jM' // 20d0-20da #12 - '264G' // 20db #6870 - 'M' // 20dc #12 - '119O' // 20dd #3108 - '251T' // 20de #6545 - 'a2M' // 20df-20e0 #64 - 'M' // 20e1 #12 - '52J' // 20e2 #1361 - '74J' // 20e3 #1933 - '2M' // 20e4 #64 - 'jM' // 20e5-20ef #12 - '262B' // 20f0 #6813 - 'nA' // 20f1-20ff - '41T' // 2100 #1085 - 'E' // 2101 #4 - '8B' // 2102 #209 - '154U' // 2103 #4024 - 'E' // 2104 #4 - '119N' // 2105 #3107 - 'bE' // 2106-2108 #4 - '40Y' // 2109 #1064 - '70T' // 210a #1839 - 'c8B' // 210b-210e #209 - '41R' // 210f #1083 - 'b8B' // 2110-2112 #209 - '119C' // 2113 #3096 - 'E' // 2114 #4 - '8B' // 2115 #209 - '118Z' // 2116 #3093 - 'aE' // 2117-2118 #4 - 'd8B' // 2119-211d #209 - 'bE' // 211e-2120 #4 - '40Y' // 2121 #1064 - '51L' // 2122 #1337 - 'E' // 2123 #4 - '8B' // 2124 #209 - 'E' // 2125 #4 - '40Y' // 2126 #1064 - '41R' // 2127 #1083 - '8B' // 2128 #209 - 'aE' // 2129-212a #4 - '40Y' // 212b #1064 - 'a8B' // 212c-212d #209 - '41R' // 212e #1083 - 'b8B' // 212f-2131 #209 - 'E' // 2132 #4 - 'a8B' // 2133-2134 #209 - '70T' // 2135 #1839 - 'b8B' // 2136-2138 #209 - '74Z' // 2139 #1949 - 'E' // 213a #4 - '41R' // 213b #1083 - 'd8B' // 213c-2140 #209 - 'cE' // 2141-2144 #4 - 'd8B' // 2145-2149 #209 - 'uE' // 214a-215f #4 - '261F' // 2160 #6791 - '261H' // 2161 #6793 - 'g71F' // 2162-2169 #1851 - 'e2M' // 216a-216f #64 - 'i71F' // 2170-2179 #1851 - 'h2M' // 217a-2182 #64 - '262U' // 2183 #6832 - 'E' // 2184 #4 - 'c2M' // 2185-2188 #64 - 'E' // 2189 #4 - 'a2M' // 218a-218b #64 - 'cA' // 218c-218f - '161Z' // 2190 #4211 - '155V' // 2191 #4051 - '206E' // 2192 #5360 - '155U' // 2193 #4050 - '73Y' // 2194 #1922 - '73X' // 2195 #1921 - 'a51P' // 2196-2197 #1341 - '74V' // 2198 #1945 - '51P' // 2199 #1341 - 'nM' // 219a-21a8 #12 - 'a75A' // 21a9-21aa #1950 - 'cM' // 21ab-21ae #12 - 'F' // 21af #5 - 'gM' // 21b0-21b7 #12 - 'a21N' // 21b8-21b9 #559 - 'iM' // 21ba-21c3 #12 - '8I' // 21c4 #216 - '12N' // 21c5 #325 - '21N' // 21c6 #559 - 'cM' // 21c7-21ca #12 - '21N' // 21cb #559 - '48T' // 21cc #1267 - 'bM' // 21cd-21cf #12 - '8I' // 21d0 #216 - 'M' // 21d1 #12 - '120G' // 21d2 #3126 - 'M' // 21d3 #12 - '119Q' // 21d4 #3110 - 'pM' // 21d5-21e5 #12 - 'c77Z' // 21e6-21e9 #2027 - 'fF' // 21ea-21f0 #5 - 'aM' // 21f1-21f2 #12 - 'F' // 21f3 #5 - 'M' // 21f4 #12 - '21N' // 21f5 #559 - 'iM' // 21f6-21ff #12 - '62S' // 2200 #1630 - 'M' // 2201 #12 - '8I' // 2202 #216 - '12N' // 2203 #325 - 'M' // 2204 #12 - '48T' // 2205 #1267 - '8I' // 2206 #216 - '119Z' // 2207 #3119 - '8I' // 2208 #216 - '12N' // 2209 #325 - '21N' // 220a #559 - '12N' // 220b #325 - 'bM' // 220c-220e #12 - '8I' // 220f #216 - 'M' // 2210 #12 - '8I' // 2211 #216 - '76Q' // 2212 #1992 - '12N' // 2213 #325 - 'M' // 2214 #12 - '119D' // 2215 #3097 - 'aM' // 2216-2217 #12 - '32C' // 2218 #834 - '76M' // 2219 #1988 - '119I' // 221a #3102 - 'aM' // 221b-221c #12 - '8I' // 221d #216 - '119X' // 221e #3117 - '120B' // 221f #3121 - '8I' // 2220 #216 - 'aM' // 2221-2222 #12 - '124N' // 2223 #3237 - 'M' // 2224 #12 - '8I' // 2225 #216 - '12N' // 2226 #325 - '119P' // 2227 #3109 - '119M' // 2228 #3106 - '119S' // 2229 #3112 - 'a8I' // 222a-222b #216 - '12N' // 222c #325 - '21N' // 222d #559 - '8I' // 222e #216 - 'dM' // 222f-2233 #12 - 'c8I' // 2234-2237 #216 - 'dM' // 2238-223c #12 - '8I' // 223d #216 - 'dM' // 223e-2242 #12 - '12N' // 2243 #325 - 'M' // 2244 #12 - '12N' // 2245 #325 - 'aM' // 2246-2247 #12 - '8I' // 2248 #216 - 'bM' // 2249-224b #12 - '251R' // 224c #6543 - 'rM' // 224d-225f #12 - '8I' // 2260 #216 - '62R' // 2261 #1629 - '12N' // 2262 #325 - 'M' // 2263 #12 - '119J' // 2264 #3103 - '119K' // 2265 #3104 - '130P' // 2266 #3395 - '136Q' // 2267 #3552 - 'aM' // 2268-2269 #12 - '120C' // 226a #3122 - '62S' // 226b #1630 - 'aM' // 226c-226d #12 - 'a8I' // 226e-226f #216 - 'aM' // 2270-2271 #12 - 'a12N' // 2272-2273 #325 - 'aM' // 2274-2275 #12 - 'a12N' // 2276-2277 #325 - 'iM' // 2278-2281 #12 - '62R' // 2282 #1629 - '8I' // 2283 #216 - 'c12N' // 2284-2287 #325 - 'aM' // 2288-2289 #12 - 'a12N' // 228a-228b #325 - 'hM' // 228c-2294 #12 - '8I' // 2295 #216 - '119L' // 2296 #3105 - '251P' // 2297 #6541 - '21N' // 2298 #559 - '76K' // 2299 #1986 - 'eM' // 229a-229f #12 - '21N' // 22a0 #559 - 'cM' // 22a1-22a4 #12 - '8I' // 22a5 #216 - 'xM' // 22a6-22be #12 - '48T' // 22bf #1267 - 'cM' // 22c0-22c3 #12 - 'b32C' // 22c4-22c6 #834 - 'rM' // 22c7-22d9 #12 - 'a12N' // 22da-22db #325 - 'qM' // 22dc-22ed #12 - '263X' // 22ee #6861 - '172M' // 22ef #4484 - 'oM' // 22f0-22ff #12 - 'd2M' // 2300-2304 #64 - '41S' // 2305 #1084 - '70S' // 2306 #1838 - '251S' // 2307 #6544 - 'c31W' // 2308-230b #828 - 'c2M' // 230c-230f #64 - 'M' // 2310 #12 - '2M' // 2311 #64 - '119V' // 2312 #3115 - 'b2M' // 2313-2315 #64 - 'F' // 2316 #5 - '2M' // 2317 #64 - '78A' // 2318 #2028 - 'M' // 2319 #12 - 'a2E' // 231a-231b #56 - 'c31W' // 231c-231f #828 - 'aM' // 2320-2321 #12 - 'a2M' // 2322-2323 #64 - 'cF' // 2324-2327 #5 - '2E' // 2328 #56 - 'a41S' // 2329-232a #1084 - 'F' // 232b #5 - 'i2M' // 232c-2335 #64 - '2pM' // 2336-237a #12 - 'F' // 237b #5 - '31W' // 237c #828 - 'bF' // 237d-237f #5 - 's2M' // 2380-2393 #64 - '52J' // 2394 #1361 - 'M' // 2395 #12 - 'd2M' // 2396-239a #64 - 'sM' // 239b-23ae #12 - '31W' // 23af #828 - 'a21N' // 23b0-23b1 #559 - 'dM' // 23b2-23b6 #12 - 'fA' // 23b7-23bd - 'n41S' // 23be-23cc #1084 - '2M' // 23cd #64 - '52I' // 23ce #1360 - '4M' // 23cf #116 - '31W' // 23d0 #828 - 'h2M' // 23d1-23d9 #64 - 'a41S' // 23da-23db #1084 - 'eM' // 23dc-23e1 #12 - 'f2M' // 23e2-23e8 #64 - 'a4M' // 23e9-23ea #116 - 'a3S' // 23eb-23ec #96 - 'b4M' // 23ed-23ef #116 - '2S' // 23f0 #70 - 'b2E' // 23f1-23f3 #56 - 'cF' // 23f4-23f7 #5 - 'b4M' // 23f8-23fa #116 - '1mF' // 23fb-2422 #5 - '52I' // 2423 #1360 - 'bF' // 2424-2426 #5 - 'xA' // 2427-243f - 'jF' // 2440-244a #5 - 'tA' // 244b-245f - '120F' // 2460 #3125 - '120E' // 2461 #3124 - '120D' // 2462 #3123 - '120A' // 2463 #3120 - '119Y' // 2464 #3118 - '119W' // 2465 #3116 - '119U' // 2466 #3114 - '119T' // 2467 #3113 - '119R' // 2468 #3111 - 'h119H' // 2469-2471 #3101 - 'a70S' // 2472-2473 #1838 - '119G' // 2474 #3100 - '119E' // 2475 #3098 - '119F' // 2476 #3099 - 'a118U' // 2477-2478 #3088 - 'h35R' // 2479-2481 #927 - 'e251O' // 2482-2487 #6540 - 'i118V' // 2488-2491 #3089 - 'i251Q' // 2492-249b #6542 - 'y36L' // 249c-24b5 #947 - 'a26W' // 24b6-24b7 #698 - '35R' // 24b8 #927 - 'h26W' // 24b9-24c1 #698 - '74T' // 24c2 #1943 - 'b26W' // 24c3-24c5 #698 - '36L' // 24c6 #947 - '35R' // 24c7 #927 - '26W' // 24c8 #698 - '35R' // 24c9 #927 - '26W' // 24ca #698 - '36L' // 24cb #947 - '26W' // 24cc #698 - '36L' // 24cd #947 - '26W' // 24ce #698 - '36L' // 24cf #947 - '35R' // 24d0 #927 - '21D' // 24d1 #549 - '118S' // 24d2 #3086 - '21D' // 24d3 #549 - '35Q' // 24d4 #926 - 'b21D' // 24d5-24d7 #549 - '118R' // 24d8 #3085 - 'c21D' // 24d9-24dc #549 - 'a35Q' // 24dd-24de #926 - '21D' // 24df #549 - '70R' // 24e0 #1837 - 'a21D' // 24e1-24e2 #549 - '35Q' // 24e3 #926 - 'a21D' // 24e4-24e5 #549 - '35Q' // 24e6 #926 - '21D' // 24e7 #549 - '35Q' // 24e8 #926 - '21D' // 24e9 #549 - '48S' // 24ea #1266 - 'i70R' // 24eb-24f4 #1837 - '48S' // 24f5 #1266 - 'h251N' // 24f6-24fe #6539 - '48S' // 24ff #1266 - '189E' // 2500 #4918 - '118Y' // 2501 #3092 - '196F' // 2502 #5101 - '10Z' // 2503 #285 - 'e62P' // 2504-2509 #1627 - '118T' // 250a #3087 - '62P' // 250b #1627 - 'a10Z' // 250c-250d #285 - '16D' // 250e #419 - 'b10Z' // 250f-2511 #285 - '16D' // 2512 #419 - '10Z' // 2513 #285 - '118X' // 2514 #3091 - '10Z' // 2515 #285 - '16D' // 2516 #419 - '118W' // 2517 #3090 - '10Z' // 2518 #285 - 'a16D' // 2519-251a #419 - '10Z' // 251b #285 - '62Q' // 251c #1628 - '10Z' // 251d #285 - 'a16D' // 251e-251f #419 - '10Z' // 2520 #285 - 'a16D' // 2521-2522 #419 - '62Q' // 2523 #1628 - '10Z' // 2524 #285 - 'a16D' // 2525-2526 #419 - '50D' // 2527 #1303 - '10Z' // 2528 #285 - 'a16D' // 2529-252a #419 - 'a10Z' // 252b-252c #285 - 'a16D' // 252d-252e #419 - '10Z' // 252f #285 - 'b16D' // 2530-2532 #419 - 'a10Z' // 2533-2534 #285 - '50D' // 2535 #1303 - '16D' // 2536 #419 - '10Z' // 2537 #285 - 'b50D' // 2538-253a #1303 - 'a10Z' // 253b-253c #285 - 'c16D' // 253d-2540 #419 - '48R' // 2541 #1265 - '50C' // 2542 #1302 - 'b48R' // 2543-2545 #1265 - 'b50C' // 2546-2548 #1302 - '251M' // 2549 #6538 - '50C' // 254a #1302 - '48R' // 254b #1265 - 'c36K' // 254c-254f #946 - 'a23X' // 2550-2551 #621 - 'a26V' // 2552-2553 #697 - '23X' // 2554 #621 - 'a26V' // 2555-2556 #697 - '23X' // 2557 #621 - 'a26V' // 2558-2559 #697 - 'a23X' // 255a-255b #621 - '26V' // 255c #697 - '23X' // 255d #621 - '26V' // 255e #697 - 'a23X' // 255f-2560 #621 - '26V' // 2561 #697 - 'a23X' // 2562-2563 #621 - '26V' // 2564 #697 - '23X' // 2565 #621 - 'a23W' // 2566-2567 #620 - '40X' // 2568 #1063 - 'a23W' // 2569-256a #620 - '40X' // 256b #1063 - 'd23W' // 256c-2570 #620 - '64X' // 2571 #1687 - '23W' // 2572 #620 - '135H' // 2573 #3517 - '40X' // 2574 #1063 - 'c36K' // 2575-2578 #946 - '23W' // 2579 #620 - 'e36K' // 257a-257f #946 - 'j23W' // 2580-258a #620 - '64X' // 258b #1687 - 'e23W' // 258c-2591 #620 - '118Q' // 2592 #3084 - 'b23W' // 2593-2595 #620 - '36K' // 2596 #946 - '40X' // 2597 #1063 - 'g36K' // 2598-259f #946 - '78E' // 25a0 #2032 - '77X' // 25a1 #2025 - '78N' // 25a2 #2041 - '77T' // 25a3 #2021 - '52G' // 25a4 #1358 - '52H' // 25a5 #1359 - 'b52G' // 25a6-25a8 #1358 - '42P' // 25a9 #1107 - 'a74G' // 25aa-25ab #1930 - 'bF' // 25ac-25ae #5 - '32C' // 25af #834 - 'F' // 25b0 #5 - '52H' // 25b1 #1359 - '78M' // 25b2 #2040 - '76L' // 25b3 #1987 - 'aF' // 25b4-25b5 #5 - '74I' // 25b6 #1932 - '76I' // 25b7 #1984 - 'cF' // 25b8-25bb #5 - '78K' // 25bc #2038 - '76J' // 25bd #1985 - 'aF' // 25be-25bf #5 - '74F' // 25c0 #1929 - '52B' // 25c1 #1353 - 'cF' // 25c2-25c5 #5 - '78H' // 25c6 #2035 - '77Y' // 25c7 #2026 - 'F' // 25c8 #5 - '42P' // 25c9 #1107 - '52B' // 25ca #1353 - '78F' // 25cb #2033 - '76G' // 25cc #1982 - 'F' // 25cd #5 - '78G' // 25ce #2034 - '78I' // 25cf #2036 - 'a42P' // 25d0-25d1 #1107 - 'a77V' // 25d2-25d3 #2023 - 'mF' // 25d4-25e1 #5 - 'a77U' // 25e2-25e3 #2022 - 'a52F' // 25e4-25e5 #1357 - '42O' // 25e6 #1106 - 'gF' // 25e7-25ee #5 - '77W' // 25ef #2024 - 'jF' // 25f0-25fa #5 - '74B' // 25fb #1925 - 'b4M' // 25fc-25fe #116 - 'F' // 25ff #5 - '75V' // 2600 #1971 - '51W' // 2601 #1348 - '75M' // 2602 #1962 - '51W' // 2603 #1348 - '5Z' // 2604 #155 - '78L' // 2605 #2039 - '78J' // 2606 #2037 - 'aF' // 2607-2608 #5 - '52F' // 2609 #1357 - 'c2M' // 260a-260d #64 - '75L' // 260e #1961 - '42O' // 260f #1106 - 'F' // 2610 #5 - '4M' // 2611 #116 - 'F' // 2612 #5 - '2M' // 2613 #64 - '5Z' // 2614 #155 - '32A' // 2615 #832 - 'a42Q' // 2616-2617 #1108 - '5Z' // 2618 #155 - 'bF' // 2619-261b #5 - '77Q' // 261c #2018 - '75Y' // 261d #1974 - '77R' // 261e #2019 - '42O' // 261f #1106 - '73N' // 2620 #1911 - 'F' // 2621 #5 - 'a4M' // 2622-2623 #116 - 'a2M' // 2624-2625 #64 - '17N' // 2626 #455 - 'b2M' // 2627-2629 #64 - '17N' // 262a #455 - '2M' // 262b #64 - '264B' // 262c #6865 - '2M' // 262d #64 - '17N' // 262e #455 - '74R' // 262f #1941 - 'gF' // 2630-2637 #5 - '75B' // 2638 #1951 - 'a52A' // 2639-263a #1352 - '2M' // 263b #64 - 'F' // 263c #5 - 'b2M' // 263d-263f #64 - '73S' // 2640 #1916 - '118L' // 2641 #3079 - '73R' // 2642 #1915 - 'd2M' // 2643-2647 #64 - 'k17N' // 2648-2653 #455 - 'jF' // 2654-265e #5 - '6J' // 265f #165 - '51K' // 2660 #1336 - '78D' // 2661 #2031 - '77S' // 2662 #2020 - '74D' // 2663 #1927 - '52E' // 2664 #1356 - '73V' // 2665 #1919 - '74E' // 2666 #1928 - '52E' // 2667 #1356 - '51K' // 2668 #1336 - '118N' // 2669 #3081 - '155N' // 266a #4043 - '118P' // 266b #3083 - '118O' // 266c #3082 - '118J' // 266d #3077 - '118K' // 266e #3078 - '118M' // 266f #3080 - 'a264Y' // 2670-2671 #6888 - 'h50B' // 2672-267a #1301 - '74S' // 267b #1942 - 'a50B' // 267c-267d #1301 - '17N' // 267e #455 - '4M' // 267f #116 - 'oF' // 2680-268f #5 - 'a2M' // 2690-2691 #64 - '31Z' // 2692 #831 - '27M' // 2693 #714 - '31Z' // 2694 #831 - '73T' // 2695 #1917 - '75G' // 2696 #1956 - '31Z' // 2697 #831 - '2M' // 2698 #64 - '31Z' // 2699 #831 - '2M' // 269a #64 - 'a17N' // 269b-269c #455 - '2M' // 269d #64 - 'aF' // 269e-269f #5 - '74H' // 26a0 #1931 - '5Z' // 26a1 #155 - 'd2M' // 26a2-26a6 #64 - '73L' // 26a7 #1909 - 'a2M' // 26a8-26a9 #64 - 'a4M' // 26aa-26ab #116 - 'F' // 26ac #5 - 'b2M' // 26ad-26af #64 - 'a31Z' // 26b0-26b1 #831 - 'j2M' // 26b2-26bc #64 - 'a75O' // 26bd-26be #1964 - 'dF' // 26bf-26c3 #5 - 'a5Z' // 26c4-26c5 #155 - 'aF' // 26c6-26c7 #5 - '5Z' // 26c8 #155 - 'dF' // 26c9-26cd #5 - '17N' // 26ce #455 - '2E' // 26cf #56 - 'F' // 26d0 #5 - '2E' // 26d1 #56 - 'F' // 26d2 #5 - '75J' // 26d3 #1959 - '4M' // 26d4 #116 - 'lF' // 26d5-26e1 #5 - 'f2M' // 26e2-26e8 #64 - 'a27M' // 26e9-26ea #714 - 'd2M' // 26eb-26ef #64 - '75W' // 26f0 #1972 - 'a27M' // 26f1-26f2 #714 - '51S' // 26f3 #1344 - 'a27M' // 26f4-26f5 #714 - '2M' // 26f6 #64 - '51Y' // 26f7 #1350 - '51S' // 26f8 #1344 - '51Y' // 26f9 #1350 - '27M' // 26fa #714 - 'a2M' // 26fb-26fc #64 - '27M' // 26fd #714 - 'a2M' // 26fe-26ff #64 - 'aF' // 2700-2701 #5 - '75K' // 2702 #1960 - 'aF' // 2703-2704 #5 - '3S' // 2705 #96 - 'aF' // 2706-2707 #5 - '51T' // 2708 #1345 - '2E' // 2709 #56 - '3O' // 270a #92 - 'b17O' // 270b-270d #456 - 'F' // 270e #5 - '2E' // 270f #56 - 'aF' // 2710-2711 #5 - '2E' // 2712 #56 - '78C' // 2713 #2030 - '4M' // 2714 #116 - 'F' // 2715 #5 - '4M' // 2716 #116 - 'bF' // 2717-2719 #5 - '52D' // 271a #1355 - 'aF' // 271b-271c #5 - '17N' // 271d #455 - 'b2M' // 271e-2720 #64 - '17N' // 2721 #455 - 'eF' // 2722-2727 #5 - '27N' // 2728 #715 - 'iF' // 2729-2732 #5 - 'a4M' // 2733-2734 #116 - 'gF' // 2735-273c #5 - '52D' // 273d #1355 - 'F' // 273e #5 - '78B' // 273f #2029 - '77P' // 2740 #2017 - 'bF' // 2741-2743 #5 - '5Z' // 2744 #155 - 'aF' // 2745-2746 #5 - '4M' // 2747 #116 - 'cF' // 2748-274b #5 - '3S' // 274c #96 - 'F' // 274d #5 - '3S' // 274e #96 - 'cF' // 274f-2752 #5 - '4M' // 2753 #116 - 'a3S' // 2754-2755 #96 - '77O' // 2756 #2016 - '4M' // 2757 #116 - 'jF' // 2758-2762 #5 - '17O' // 2763 #456 - '73Q' // 2764 #1914 - 'pF' // 2765-2775 #5 - 'h62O' // 2776-277e #1626 - '50A' // 277f #1300 - 'b118I' // 2780-2782 #3076 - 'f50B' // 2783-2789 #1301 - 'b62O' // 278a-278c #1626 - '50A' // 278d #1300 - '251I' // 278e #6534 - 'd50A' // 278f-2793 #1300 - 'F' // 2794 #5 - 'b3S' // 2795-2797 #96 - 'hF' // 2798-27a0 #5 - '73W' // 27a1 #1920 - 'mF' // 27a2-27af #5 - '3S' // 27b0 #96 - 'mF' // 27b1-27be #5 - '3S' // 27bf #96 - '2kM' // 27c0-27ff #12 - '9u76B' // 2800-28ff #1977 - '1fM' // 2900-2920 #12 - 'a31W' // 2921-2922 #828 - 'pM' // 2923-2933 #12 - 'a74Q' // 2934-2935 #1940 - '2vM' // 2936-2980 #12 - '32C' // 2981 #834 - '2hM' // 2982-29be #12 - '76H' // 29bf #1983 - '1pM' // 29c0-29ea #12 - '32C' // 29eb #834 - 'mM' // 29ec-29f9 #12 - 'a251L' // 29fa-29fb #6537 - '9yM' // 29fc-2aff #12 - 'dF' // 2b00-2b04 #5 - 'b74C' // 2b05-2b07 #1926 - 'eF' // 2b08-2b0d #5 - 'cM' // 2b0e-2b11 #12 - 'gF' // 2b12-2b19 #5 - '42Q' // 2b1a #1108 - '73P' // 2b1b #1913 - '4M' // 2b1c #116 - 'rF' // 2b1d-2b2f #5 - '1bM' // 2b30-2b4c #12 - 'bF' // 2b4d-2b4f #5 - '36U' // 2b50 #956 - 'cF' // 2b51-2b54 #5 - '4M' // 2b55 #116 - '1cF' // 2b56-2b73 #5 - 'aA' // 2b74-2b75 - '1dF' // 2b76-2b94 #5 - '42Q' // 2b95 #1108 - 'A' // 2b96 - '3xF' // 2b97-2bfd #5 - 'M' // 2bfe #12 - 'F' // 2bff #5 - '1t27I' // 2c00-2c2e #710 - 'A' // 2c2f - '1t27I' // 2c30-2c5e #710 - 'A' // 2c5f - '1eE' // 2c60-2c7f #4 - '4k50R' // 2c80-2cf3 #1317 - 'dA' // 2cf4-2cf8 - 'f50R' // 2cf9-2cff #1317 - '1k19P' // 2d00-2d25 #509 - 'A' // 2d26 - '19P' // 2d27 #509 - 'dA' // 2d28-2d2c - '19P' // 2d2d #509 - 'aA' // 2d2e-2d2f - '2c51G' // 2d30-2d67 #1332 - 'fA' // 2d68-2d6e - 'a51G' // 2d6f-2d70 #1332 - 'mA' // 2d71-2d7e - '51G' // 2d7f #1332 - 'v3R' // 2d80-2d96 #95 - 'hA' // 2d97-2d9f - 'f3R' // 2da0-2da6 #95 - 'A' // 2da7 - 'f3R' // 2da8-2dae #95 - 'A' // 2daf - 'f3R' // 2db0-2db6 #95 - 'A' // 2db7 - 'f3R' // 2db8-2dbe #95 - 'A' // 2dbf - 'f3R' // 2dc0-2dc6 #95 - 'A' // 2dc7 - 'f3R' // 2dc8-2dce #95 - 'A' // 2dcf - 'f3R' // 2dd0-2dd6 #95 - 'A' // 2dd7 - 'f3R' // 2dd8-2dde #95 - 'A' // 2ddf - '2bE' // 2de0-2e16 #4 - '17K' // 2e17 #452 - 'cE' // 2e18-2e1b #4 - 'a262M' // 2e1c-2e1d #6824 - 'iE' // 2e1e-2e27 #4 - 'a71H' // 2e28-2e29 #1853 - 'eE' // 2e2a-2e2f #4 - 'a261P' // 2e30-2e31 #6801 - 'E' // 2e32 #4 - 'a17K' // 2e33-2e34 #452 - 'dE' // 2e35-2e39 #4 - 'a251K' // 2e3a-2e3b #6536 - 'dE' // 2e3c-2e40 #4 - '71I' // 2e41 #1854 - '1aE' // 2e42-2e5d #4 - '1gA' // 2e5e-2e7f - 'y21M' // 2e80-2e99 #558 - 'A' // 2e9a - '1e21M' // 2e9b-2eba #558 - '251J' // 2ebb #6535 - '2c21M' // 2ebc-2ef3 #558 - 'kA' // 2ef4-2eff - '62N' // 2f00 #1625 - 'b21M' // 2f01-2f03 #558 - '41Q' // 2f04 #1082 - '21M' // 2f05 #558 - '41Q' // 2f06 #1082 - '21M' // 2f07 #558 - '62N' // 2f08 #1625 - '21M' // 2f09 #558 - 'a41Q' // 2f0a-2f0b #1082 - 'd21M' // 2f0c-2f10 #558 - '41Q' // 2f11 #1082 - '118H' // 2f12 #3075 - '21M' // 2f13 #558 - '6O' // 2f14 #170 - 'a4I' // 2f15-2f16 #112 - 'a6O' // 2f17-2f18 #170 - 'b4I' // 2f19-2f1b #112 - 'a6O' // 2f1c-2f1d #170 - '4I' // 2f1e #112 - 'a6O' // 2f1f-2f20 #170 - 'a4I' // 2f21-2f22 #112 - '6O' // 2f23 #170 - '48Q' // 2f24 #1264 - 'a6O' // 2f25-2f26 #170 - '4I' // 2f27 #112 - '6O' // 2f28 #170 - '118G' // 2f29 #3074 - '4I' // 2f2a #112 - '6O' // 2f2b #170 - '4I' // 2f2c #112 - '49Z' // 2f2d #1299 - '4I' // 2f2e #112 - '48Q' // 2f2f #1264 - '49Z' // 2f30 #1299 - 'a6O' // 2f31-2f32 #170 - 'd4I' // 2f33-2f37 #112 - '6O' // 2f38 #170 - 'b4I' // 2f39-2f3b #112 - '48Q' // 2f3c #1264 - 'a6O' // 2f3d-2f3e #170 - '62J' // 2f3f #1621 - '6O' // 2f40 #170 - '4I' // 2f41 #112 - '62J' // 2f42 #1621 - 'a6O' // 2f43-2f44 #170 - '62I' // 2f45 #1620 - 'f6O' // 2f46-2f4c #170 - 'a4I' // 2f4d-2f4e #112 - 'c6O' // 2f4f-2f52 #170 - '4I' // 2f53 #112 - 'd6O' // 2f54-2f58 #170 - '4I' // 2f59 #112 - 'h6O' // 2f5a-2f62 #170 - 'a62I' // 2f63-2f64 #1620 - 'a6O' // 2f65-2f66 #170 - 'a4I' // 2f67-2f68 #112 - 'g6O' // 2f69-2f70 #170 - '4I' // 2f71 #112 - 'a6O' // 2f72-2f73 #170 - '49Z' // 2f74 #1299 - 'a6O' // 2f75-2f76 #170 - '4I' // 2f77 #112 - '6O' // 2f78 #170 - '4I' // 2f79 #112 - 'b6O' // 2f7a-2f7c #170 - '14S' // 2f7d #382 - '4I' // 2f7e #112 - 'c14S' // 2f7f-2f82 #382 - '48N' // 2f83 #1261 - 'g14S' // 2f84-2f8b #382 - 'a4I' // 2f8c-2f8d #112 - '14S' // 2f8e #382 - '48N' // 2f8f #1261 - '14S' // 2f90 #382 - '4I' // 2f91 #112 - 'e14S' // 2f92-2f97 #382 - '4I' // 2f98 #112 - 'g14S' // 2f99-2fa0 #382 - '4I' // 2fa1 #112 - 'a14S' // 2fa2-2fa3 #382 - '4I' // 2fa4 #112 - 'd14S' // 2fa5-2fa9 #382 - 'a4I' // 2faa-2fab #112 - 'e14S' // 2fac-2fb1 #382 - '4I' // 2fb2 #112 - 'h14S' // 2fb3-2fbb #382 - '251G' // 2fbc #6532 - 'c4I' // 2fbd-2fc0 #112 - 'i14S' // 2fc1-2fca #382 - 'a4I' // 2fcb-2fcc #112 - 'g14S' // 2fcd-2fd4 #382 - '4I' // 2fd5 #112 - 'yA' // 2fd6-2fef - 'k4I' // 2ff0-2ffb #112 - 'cA' // 2ffc-2fff - '239K' // 3000 #6224 - '247L' // 3001 #6433 - '247M' // 3002 #6434 - '117T' // 3003 #3061 - '48O' // 3004 #1262 - '118C' // 3005 #3070 - '117U' // 3006 #3062 - '117Z' // 3007 #3067 - '182B' // 3008 #4733 - '182C' // 3009 #4734 - '233T' // 300a #6077 - '233U' // 300b #6078 - 'a245R' // 300c-300d #6387 - '214B' // 300e #5565 - '206I' // 300f #5364 - 'a239J' // 3010-3011 #6223 - '118A' // 3012 #3068 - '48N' // 3013 #1261 - 'a154W' // 3014-3015 #4026 - 'a117N' // 3016-3017 #3055 - 'a117P' // 3018-3019 #3057 - 'a251H' // 301a-301b #6533 - '126G' // 301c #3282 - '124U' // 301d #3244 - '129S' // 301e #3372 - '251W' // 301f #6548 - '117O' // 3020 #3056 - '62K' // 3021 #1622 - '48O' // 3022 #1262 - 'a62K' // 3023-3024 #1622 - '48O' // 3025 #1262 - 'a4I' // 3026-3027 #112 - 'g13J' // 3028-302f #347 - '74O' // 3030 #1938 - 'b13J' // 3031-3033 #347 - 'a251F' // 3034-3035 #6531 - '49X' // 3036 #1297 - 'e13J' // 3037-303c #347 - '74P' // 303d #1939 - 'a13J' // 303e-303f #347 - 'A' // 3040 - '35P' // 3041 #925 - '7C' // 3042 #184 - '35P' // 3043 #925 - '49E' // 3044 #1278 - '35P' // 3045 #925 - '17H' // 3046 #449 - '35P' // 3047 #925 - '21E' // 3048 #550 - '35P' // 3049 #925 - '7C' // 304a #184 - '14P' // 304b #379 - 'a17H' // 304c-304d #449 - '7B' // 304e #183 - '17H' // 304f #449 - '7B' // 3050 #183 - '143R' // 3051 #3735 - '118E' // 3052 #3072 - '7C' // 3053 #184 - '26X' // 3054 #699 - '17H' // 3055 #449 - '17G' // 3056 #448 - '14P' // 3057 #379 - '131I' // 3058 #3414 - '17H' // 3059 #449 - '7B' // 305a #183 - '26Y' // 305b #700 - '15Y' // 305c #414 - '26Y' // 305d #700 - '17G' // 305e #448 - '14P' // 305f #379 - '16A' // 3060 #416 - '7C' // 3061 #184 - '15Y' // 3062 #414 - '17H' // 3063 #449 - '16A' // 3064 #416 - '7B' // 3065 #183 - 'b17H' // 3066-3068 #449 - '26Y' // 3069 #700 - 'a14P' // 306a-306b #379 - '17G' // 306c #448 - '118F' // 306d #3073 - '68A' // 306e #1768 - '17H' // 306f #449 - 'c7B' // 3070-3073 #183 - '17G' // 3074 #448 - 'a7B' // 3075-3076 #183 - '17G' // 3077 #448 - 'a7B' // 3078-3079 #183 - '15Y' // 307a #414 - '7B' // 307b #183 - '15Y' // 307c #414 - '17G' // 307d #448 - '14P' // 307e #379 - '7C' // 307f #184 - '118D' // 3080 #3071 - '149Q' // 3081 #3890 - '7C' // 3082 #184 - 'a21E' // 3083-3084 #550 - '7B' // 3085 #183 - '26X' // 3086 #699 - '7B' // 3087 #183 - '16A' // 3088 #416 - '17H' // 3089 #449 - 'a14P' // 308a-308b #379 - '7C' // 308c #184 - '26X' // 308d #699 - '15Y' // 308e #414 - '21E' // 308f #550 - '62L' // 3090 #1623 - '15Y' // 3091 #414 - '7C' // 3092 #184 - '14P' // 3093 #379 - 'b13J' // 3094-3096 #347 - 'aA' // 3097-3098 - 'a48P' // 3099-309a #1263 - '117R' // 309b #3059 - '117X' // 309c #3065 - 'a48P' // 309d-309e #1263 - 'a13J' // 309f-30a0 #347 - '7B' // 30a1 #183 - '14P' // 30a2 #379 - '16A' // 30a3 #416 - '49E' // 30a4 #1278 - '15Y' // 30a5 #414 - '16A' // 30a6 #416 - '26Y' // 30a7 #700 - '7C' // 30a8 #184 - '7B' // 30a9 #183 - '16A' // 30aa #416 - '7C' // 30ab #184 - '26X' // 30ac #699 - '7C' // 30ad #184 - '7B' // 30ae #183 - '14P' // 30af #379 - '7C' // 30b0 #184 - 'a7B' // 30b1-30b2 #183 - '7C' // 30b3 #184 - '7B' // 30b4 #183 - '16A' // 30b5 #416 - '7B' // 30b6 #183 - '7C' // 30b7 #184 - '17H' // 30b8 #449 - '14P' // 30b9 #379 - '26Y' // 30ba #700 - '26X' // 30bb #699 - '17G' // 30bc #448 - '7B' // 30bd #183 - '17G' // 30be #448 - '17H' // 30bf #449 - '21E' // 30c0 #550 - '7C' // 30c1 #184 - '15Y' // 30c2 #414 - '14P' // 30c3 #379 - '26X' // 30c4 #699 - '15Y' // 30c5 #414 - '7C' // 30c6 #184 - '16A' // 30c7 #416 - '14P' // 30c8 #379 - '7C' // 30c9 #184 - 'a16A' // 30ca-30cb #416 - '17G' // 30cc #448 - 'a26X' // 30cd-30ce #699 - '26Y' // 30cf #700 - '16A' // 30d0 #416 - '21E' // 30d1 #550 - '7B' // 30d2 #183 - '21E' // 30d3 #550 - '7B' // 30d4 #183 - '7C' // 30d5 #184 - '21E' // 30d6 #550 - '7C' // 30d7 #184 - '17G' // 30d8 #448 - 'd7B' // 30d9-30dd #183 - '7C' // 30de #184 - '21E' // 30df #550 - 'a7C' // 30e0-30e1 #184 - '137L' // 30e2 #3573 - '16A' // 30e3 #416 - '7B' // 30e4 #183 - '16A' // 30e5 #416 - '7B' // 30e6 #183 - '21E' // 30e7 #550 - '17G' // 30e8 #448 - 'b14P' // 30e9-30eb #379 - 'a7C' // 30ec-30ed #184 - '117G' // 30ee #3048 - '26Y' // 30ef #700 - '15Y' // 30f0 #414 - '62L' // 30f1 #1623 - '15Y' // 30f2 #414 - '68A' // 30f3 #1768 - '15Y' // 30f4 #414 - '117Y' // 30f5 #3066 - '118B' // 30f6 #3069 - 'c13J' // 30f7-30fa #347 - '216T' // 30fb #5635 - '49E' // 30fc #1278 - 'a48P' // 30fd-30fe #1263 - '13J' // 30ff #347 - 'dA' // 3100-3104 - 'a31I' // 3105-3106 #814 - '63K' // 3107 #1648 - '31I' // 3108 #814 - '141M' // 3109 #3678 - 'a31I' // 310a-310b #814 - '63K' // 310c #1648 - 'e31I' // 310d-3112 #814 - 'a41E' // 3113-3114 #1070 - 'b31I' // 3115-3117 #814 - '41E' // 3118 #1070 - '31I' // 3119 #814 - 'l41E' // 311a-3126 #1070 - '245Z' // 3127 #6395 - '41E' // 3128 #1070 - '245Y' // 3129 #6394 - 'e13J' // 312a-312f #347 - 'A' // 3130 - '26U' // 3131 #696 - '49Y' // 3132 #1298 - '49X' // 3133 #1297 - '26U' // 3134 #696 - '49X' // 3135 #1297 - '13K' // 3136 #348 - '48L' // 3137 #1259 - '13K' // 3138 #348 - '26U' // 3139 #696 - 'f13K' // 313a-3140 #348 - 'a26U' // 3141-3142 #696 - 'a13K' // 3143-3144 #348 - '48L' // 3145 #1259 - '49Y' // 3146 #1298 - '62G' // 3147 #1618 - '26U' // 3148 #696 - '49Y' // 3149 #1298 - '70Q' // 314a #1836 - '117M' // 314b #3054 - '70Q' // 314c #1836 - '117J' // 314d #3051 - '117L' // 314e #3053 - '117I' // 314f #3050 - '13K' // 3150 #348 - '70P' // 3151 #1835 - '13K' // 3152 #348 - '117H' // 3153 #3049 - 'b13K' // 3154-3156 #348 - '70P' // 3157 #1835 - 'c13K' // 3158-315b #348 - '48L' // 315c #1259 - 'b13K' // 315d-315f #348 - '117K' // 3160 #3052 - '26U' // 3161 #696 - '13K' // 3162 #348 - '26U' // 3163 #696 - '261E' // 3164 #6790 - '1a13K' // 3165-3180 #348 - '117F' // 3181 #3047 - 'c13K' // 3182-3185 #348 - '251E' // 3186 #6530 - 'e13K' // 3187-318c #348 - '62G' // 318d #1618 - '13K' // 318e #348 - 'A' // 318f - 'a13J' // 3190-3191 #347 - '48M' // 3192 #1260 - '62H' // 3193 #1619 - 'a13J' // 3194-3195 #347 - 'b48M' // 3196-3198 #1260 - 'c13J' // 3199-319c #347 - '62H' // 319d #1619 - 'a48M' // 319e-319f #1260 - '1a13J' // 31a0-31bb #347 - 'cA' // 31bc-31bf - 'g13J' // 31c0-31c7 #347 - '1a12M' // 31c8-31e3 #324 - 'kA' // 31e4-31ef - 'o12M' // 31f0-31ff #324 - '1a17J' // 3200-321b #451 - '251C' // 321c #6528 - 'a12M' // 321d-321e #324 - 'A' // 321f - 'f62F' // 3220-3226 #1617 - 'b251D' // 3227-3229 #6529 - 'f12M' // 322a-3230 #324 - '117S' // 3231 #3060 - 'f12M' // 3232-3238 #324 - '17J' // 3239 #451 - 'v12M' // 323a-3250 #324 - 'i17J' // 3251-325a #451 - 'd12M' // 325b-325f #324 - 'g17J' // 3260-3267 #451 - '117D' // 3268 #3045 - 'r17J' // 3269-327b #451 - 'a12M' // 327c-327d #324 - 'a17J' // 327e-327f #451 - '12M' // 3280 #324 - '31H' // 3281 #813 - 'g12M' // 3282-3289 #324 - '17J' // 328a #451 - '117E' // 328b #3046 - 'd17J' // 328c-3290 #451 - 'a31H' // 3291-3292 #813 - '12M' // 3293 #324 - '17J' // 3294 #451 - 'a31H' // 3295-3296 #813 - '51O' // 3297 #1340 - '12M' // 3298 #324 - '51O' // 3299 #1340 - 'b12M' // 329a-329c #324 - '31H' // 329d #813 - '17J' // 329e #451 - '31H' // 329f #813 - 'b12M' // 32a0-32a2 #324 - '62F' // 32a3 #1617 - '31H' // 32a4 #813 - '17J' // 32a5 #451 - 'n12M' // 32a6-32b4 #324 - '1f27D' // 32b5-32d5 #705 - '35O' // 32d6 #924 - 'i27D' // 32d7-32e0 #705 - '35O' // 32e1 #924 - '1w27D' // 32e2-3313 #705 - '35O' // 3314 #924 - 'l27D' // 3315-3321 #705 - '35O' // 3322 #924 - 'h27D' // 3323-332b #705 - 'A' // 332c - '3c27D' // 332d-337e #705 - '35O' // 337f #924 - 'd70O' // 3380-3384 #1834 - 'b27D' // 3385-3387 #705 - 'c70O' // 3388-338b #1834 - 'a70N' // 338c-338d #1833 - 'a117C' // 338e-338f #3044 - 'd70N' // 3390-3394 #1833 - '251B' // 3395 #6527 - 'e21L' // 3396-339b #557 - '117Q' // 339c #3058 - '117W' // 339d #3064 - '116Z' // 339e #3041 - 'a21L' // 339f-33a0 #557 - '117V' // 33a1 #3063 - '1g21L' // 33a2-33c3 #557 - '116Y' // 33c4 #3040 - 'f21L' // 33c5-33cb #557 - 'bT' // 33cc-33ce #19 - 'a21L' // 33cf-33d0 #557 - 'a117B' // 33d1-33d2 #3043 - '21L' // 33d3 #557 - 'T' // 33d4 #19 - '70M' // 33d5 #1832 - '21L' // 33d6 #557 - 'T' // 33d7 #19 - '21L' // 33d8 #557 - 'aT' // 33d9-33da #19 - 'b21L' // 33db-33dd #557 - '1gT' // 33de-33ff #19 - 'aA' // 3400-3401 - 'T' // 3402 #19 - 'aA' // 3403-3404 - 'aT' // 3405-3406 #19 - '1eA' // 3407-3426 - 'T' // 3427 #19 - 'cA' // 3428-342b - 'T' // 342c #19 - 'A' // 342d - 'T' // 342e #19 - 'dA' // 342f-3433 - '7J' // 3434 #191 - '7A' // 3435 #182 - 'iA' // 3436-343f - '62M' // 3440 #1624 - 'fA' // 3441-3447 - 'a3G' // 3448-3449 #84 - '7A' // 344a #182 - 'A' // 344b - '7A' // 344c #182 - 'vA' // 344d-3463 - '7A' // 3464 #182 - 'bA' // 3465-3467 - 'T' // 3468 #19 - 'A' // 3469 - 'T' // 346a #19 - 'gA' // 346b-3472 - '7A' // 3473 #182 - 'eA' // 3474-3479 - '7A' // 347a #182 - 'aA' // 347b-347c - 'a7A' // 347d-347e #182 - 'hA' // 347f-3487 - 'T' // 3488 #19 - 'hA' // 3489-3491 - 'T' // 3492 #19 - '7A' // 3493 #182 - 'aA' // 3494-3495 - '7A' // 3496 #182 - 'mA' // 3497-34a4 - '7A' // 34a5 #182 - 'hA' // 34a6-34ae - '7A' // 34af #182 - 'dA' // 34b0-34b4 - 'T' // 34b5 #19 - 'eA' // 34b6-34bb - '62E' // 34bc #1616 - 'cA' // 34bd-34c0 - '62E' // 34c1 #1616 - 'dA' // 34c2-34c6 - 'T' // 34c7 #19 - '7A' // 34c8 #182 - 'qA' // 34c9-34da - 'T' // 34db #19 - '7J' // 34dc #191 - 'aA' // 34dd-34de - '7A' // 34df #182 - 'cA' // 34e0-34e3 - '7A' // 34e4 #182 - 'A' // 34e5 - '7A' // 34e6 #182 - 'fA' // 34e7-34ed - '7J' // 34ee #191 - 'kA' // 34ef-34fa - '7A' // 34fb #182 - 'iA' // 34fc-3505 - '7A' // 3506 #182 - 'wA' // 3507-351e - 'T' // 351f #19 - '1cA' // 3520-353d - '117A' // 353e #3042 - 'qA' // 353f-3550 - '62M' // 3551 #1624 - 'A' // 3552 - '7A' // 3553 #182 - 'dA' // 3554-3558 - '7A' // 3559 #182 - 'bA' // 355a-355c - '70M' // 355d #1832 - 'T' // 355e #19 - 'aA' // 355f-3560 - '7A' // 3561 #182 - 'A' // 3562 - 'T' // 3563 #19 - 'aA' // 3564-3565 - '7J' // 3566 #191 - 'eA' // 3567-356c - '7A' // 356d #182 - 'T' // 356e #19 - 'A' // 356f - '7A' // 3570 #182 - 'A' // 3571 - 'I' // 3572 #8 - 'aA' // 3573-3574 - '7J' // 3575 #191 - 'A' // 3576 - 'aI' // 3577-3578 #8 - 'jA' // 3579-3583 - 'I' // 3584 #8 - 'lA' // 3585-3591 - '7J' // 3592 #191 - 'cA' // 3593-3596 - 'aI' // 3597-3598 #8 - 'fA' // 3599-359f - '7J' // 35a0 #191 - '116X' // 35a1 #3039 - 'bA' // 35a2-35a4 - 'I' // 35a5 #8 - 'T' // 35a6 #19 - 'A' // 35a7 - 'T' // 35a8 #19 - 'cA' // 35a9-35ac - '62D' // 35ad #1615 - 'pA' // 35ae-35be - 'I' // 35bf #8 - 'A' // 35c0 - 'I' // 35c1 #8 - 'bA' // 35c2-35c4 - '26T' // 35c5 #695 - 'A' // 35c6 - 'I' // 35c7 #8 - 'aA' // 35c8-35c9 - 'I' // 35ca #8 - 'bA' // 35cb-35cd - '62D' // 35ce #1615 - 'bA' // 35cf-35d1 - 'I' // 35d2 #8 - 'bA' // 35d3-35d5 - 'I' // 35d6 #8 - 'bA' // 35d7-35d9 - 'T' // 35da #19 - 'I' // 35db #8 - 'A' // 35dc - 'I' // 35dd #8 - 'T' // 35de #19 - 'qA' // 35df-35f0 - 'bI' // 35f1-35f3 #8 - 'T' // 35f4 #19 - 'eA' // 35f5-35fa - 'I' // 35fb #8 - 'aA' // 35fc-35fd - 'I' // 35fe #8 - 'eA' // 35ff-3604 - 'T' // 3605 #19 - 'bA' // 3606-3608 - 'I' // 3609 #8 - 'iA' // 360a-3613 - 'T' // 3614 #19 - 'bA' // 3615-3617 - 'I' // 3618 #8 - 'A' // 3619 - 'I' // 361a #8 - 'gA' // 361b-3622 - 'I' // 3623 #8 - 'A' // 3624 - 'I' // 3625 #8 - 'fA' // 3626-362c - 'I' // 362d #8 - 'fA' // 362e-3634 - 'I' // 3635 #8 - 'bA' // 3636-3638 - 'I' // 3639 #8 - 'cA' // 363a-363d - 'I' // 363e #8 - 'gA' // 363f-3646 - 'bI' // 3647-3649 #8 - 'T' // 364a #19 - 'bA' // 364b-364d - 'I' // 364e #8 - 'oA' // 364f-365e - 'I' // 365f #8 - 'A' // 3660 - 'I' // 3661 #8 - 'wA' // 3662-3679 - 'I' // 367a #8 - 'eA' // 367b-3680 - 'I' // 3681 #8 - 'nA' // 3682-3690 - 'T' // 3691 #19 - 'cA' // 3692-3695 - 'T' // 3696 #19 - 'aA' // 3697-3698 - 'T' // 3699 #19 - 'I' // 369a #8 - 'fA' // 369b-36a1 - '7J' // 36a2 #191 - 'aA' // 36a3-36a4 - 'I' // 36a5 #8 - 'cA' // 36a6-36a9 - 'I' // 36aa #8 - '7J' // 36ab #191 - '35N' // 36ac #923 - 'bA' // 36ad-36af - 'aI' // 36b0-36b1 #8 - 'bA' // 36b2-36b4 - 'I' // 36b5 #8 - 'bA' // 36b6-36b8 - 'I' // 36b9 #8 - 'aA' // 36ba-36bb - 'I' // 36bc #8 - 'cA' // 36bd-36c0 - 'I' // 36c1 #8 - 'A' // 36c2 - 'bI' // 36c3-36c5 #8 - 'A' // 36c6 - 'aI' // 36c7-36c8 #8 - 'eA' // 36c9-36ce - 'T' // 36cf #19 - 'bA' // 36d0-36d2 - 'aI' // 36d3-36d4 #8 - 'A' // 36d5 - 'I' // 36d6 #8 - 'eA' // 36d7-36dc - 'I' // 36dd #8 - 'bA' // 36de-36e0 - 'aI' // 36e1-36e2 #8 - 'aA' // 36e3-36e4 - 'aI' // 36e5-36e6 #8 - 'mA' // 36e7-36f4 - 'I' // 36f5 #8 - 'jA' // 36f6-3700 - 'I' // 3701 #8 - 'A' // 3702 - 'I' // 3703 #8 - 'cA' // 3704-3707 - 'I' // 3708 #8 - 'A' // 3709 - 'I' // 370a #8 - 'aA' // 370b-370c - 'I' // 370d #8 - 'mA' // 370e-371b - 'I' // 371c #8 - 'dA' // 371d-3721 - 'aI' // 3722-3723 #8 - 'A' // 3724 - 'I' // 3725 #8 - 'eA' // 3726-372b - 'aI' // 372c-372d #8 - 'aA' // 372e-372f - 'I' // 3730 #8 - 'A' // 3731 - 'aI' // 3732-3733 #8 - 'eA' // 3734-3739 - '35N' // 373a #923 - 'dA' // 373b-373f - 'I' // 3740 #8 - 'aA' // 3741-3742 - 'I' // 3743 #8 - '1bA' // 3744-3760 - 'T' // 3761 #19 - '26T' // 3762 #695 - 'gA' // 3763-376a - 'aT' // 376b-376c #19 - 'aA' // 376d-376e - 'I' // 376f #8 - 'dA' // 3770-3774 - 'T' // 3775 #19 - 'vA' // 3776-378c - 'T' // 378d #19 - 'hA' // 378e-3796 - 'I' // 3797 #8 - 'gA' // 3798-379f - 'I' // 37a0 #8 - 'wA' // 37a1-37b8 - 'I' // 37b9 #8 - 'cA' // 37ba-37bd - 'I' // 37be #8 - 'aA' // 37bf-37c0 - 'T' // 37c1 #19 - 'sA' // 37c2-37d5 - 'I' // 37d6 #8 - 'jA' // 37d7-37e1 - 'T' // 37e2 #19 - 'dA' // 37e3-37e7 - 'T' // 37e8 #19 - 'hA' // 37e9-37f1 - 'I' // 37f2 #8 - 'A' // 37f3 - 'T' // 37f4 #19 - 'bA' // 37f5-37f7 - 'I' // 37f8 #8 - 'aA' // 37f9-37fa - 'I' // 37fb #8 - 'A' // 37fc - 'T' // 37fd #19 - 'aA' // 37fe-37ff - 'T' // 3800 #19 - 'mA' // 3801-380e - 'I' // 380f #8 - 'hA' // 3810-3818 - 'I' // 3819 #8 - 'eA' // 381a-381f - 'I' // 3820 #8 - 'kA' // 3821-382c - 'I' // 382d #8 - 'A' // 382e - 'T' // 382f #19 - 'eA' // 3830-3835 - '26T' // 3836 #695 - 'A' // 3837 - 'I' // 3838 #8 - 'fA' // 3839-383f - 'T' // 3840 #19 - 'zA' // 3841-385b - 'T' // 385c #19 - 'cA' // 385d-3860 - 'T' // 3861 #19 - 'A' // 3862 - '35N' // 3863 #923 - 'pA' // 3864-3874 - 'I' // 3875 #8 - '1oA' // 3876-389f - 'I' // 38a0 #8 - 'T' // 38a1 #19 - 'eA' // 38a2-38a7 - '7J' // 38a8 #191 - 'cA' // 38a9-38ac - 'T' // 38ad #19 - 'tA' // 38ae-38c2 - 'I' // 38c3 #8 - 'gA' // 38c4-38cb - 'I' // 38cc #8 - 'cA' // 38cd-38d0 - 'I' // 38d1 #8 - 'aA' // 38d2-38d3 - 'I' // 38d4 #8 - '1jA' // 38d5-38f9 - '26T' // 38fa #695 - 'lA' // 38fb-3907 - 'I' // 3908 #8 - 'jA' // 3909-3913 - 'I' // 3914 #8 - 'aA' // 3915-3916 - 'T' // 3917 #19 - 'aA' // 3918-3919 - 'T' // 391a #19 - 'kA' // 391b-3926 - 'I' // 3927 #8 - 'iA' // 3928-3931 - 'I' // 3932 #8 - 'kA' // 3933-393e - 'I' // 393f #8 - 'lA' // 3940-394c - 'I' // 394d #8 - 'tA' // 394e-3962 - 'I' // 3963 #8 - 'jA' // 3964-396e - 'T' // 396f #19 - 'gA' // 3970-3977 - 'I' // 3978 #8 - 'fA' // 3979-397f - 'I' // 3980 #8 - 'gA' // 3981-3988 - 'aI' // 3989-398a #8 - 'fA' // 398b-3991 - 'I' // 3992 #8 - 'eA' // 3993-3998 - 'I' // 3999 #8 - 'A' // 399a - 'I' // 399b #8 - 'dA' // 399c-39a0 - 'I' // 39a1 #8 - 'aA' // 39a2-39a3 - '26T' // 39a4 #695 - 'rA' // 39a5-39b7 - '62C' // 39b8 #1614 - '1hA' // 39b9-39db - 'I' // 39dc #8 - 'dA' // 39dd-39e1 - 'I' // 39e2 #8 - 'aA' // 39e3-39e4 - 'I' // 39e5 #8 - 'eA' // 39e6-39eb - 'I' // 39ec #8 - 'jA' // 39ed-39f7 - 'I' // 39f8 #8 - 'aA' // 39f9-39fa - 'I' // 39fb #8 - 'aA' // 39fc-39fd - 'I' // 39fe #8 - 'aA' // 39ff-3a00 - 'I' // 3a01 #8 - 'A' // 3a02 - 'I' // 3a03 #8 - 'aA' // 3a04-3a05 - 'I' // 3a06 #8 - 'oA' // 3a07-3a16 - '35N' // 3a17 #923 - 'I' // 3a18 #8 - 'oA' // 3a19-3a28 - 'aI' // 3a29-3a2a #8 - 'hA' // 3a2b-3a33 - 'I' // 3a34 #8 - 'uA' // 3a35-3a4a - 'I' // 3a4b #8 - 'eA' // 3a4c-3a51 - '35N' // 3a52 #923 - 'cA' // 3a53-3a56 - 'I' // 3a57 #8 - 'cA' // 3a58-3a5b - '26T' // 3a5c #695 - 'A' // 3a5d - 'I' // 3a5e #8 - 'fA' // 3a5f-3a65 - 'aI' // 3a66-3a67 #8 - 'eA' // 3a68-3a6d - 'T' // 3a6e #19 - 'cA' // 3a6f-3a72 - 'T' // 3a73 #19 - 'pA' // 3a74-3a84 - 'T' // 3a85 #19 - 'pA' // 3a86-3a96 - 'I' // 3a97 #8 - 'rA' // 3a98-3aaa - 'I' // 3aab #8 - 'pA' // 3aac-3abc - 'I' // 3abd #8 - 'eA' // 3abe-3ac3 - 'T' // 3ac4 #19 - 'eA' // 3ac5-3aca - 'T' // 3acb #19 - 'iA' // 3acc-3ad5 - 'aT' // 3ad6-3ad7 #19 - 'eA' // 3ad8-3add - 'I' // 3ade #8 - 'A' // 3adf - 'I' // 3ae0 #8 - 'hA' // 3ae1-3ae9 - 'T' // 3aea #19 - 'dA' // 3aeb-3aef - 'I' // 3af0 #8 - 'A' // 3af1 - 'I' // 3af2 #8 - 'T' // 3af3 #19 - 'A' // 3af4 - 'I' // 3af5 #8 - 'dA' // 3af6-3afa - 'I' // 3afb #8 - 'qA' // 3afc-3b0d - '26T' // 3b0e #695 - 'iA' // 3b0f-3b18 - 'I' // 3b19 #8 - 'T' // 3b1a #19 - 'A' // 3b1b - 'T' // 3b1c #19 - 'dA' // 3b1d-3b21 - '62C' // 3b22 #1614 - 'gA' // 3b23-3b2a - 'I' // 3b2b #8 - 'hA' // 3b2c-3b34 - 'T' // 3b35 #19 - 'bA' // 3b36-3b38 - 'I' // 3b39 #8 - 'gA' // 3b3a-3b41 - 'I' // 3b42 #8 - 'tA' // 3b43-3b57 - 'I' // 3b58 #8 - 'fA' // 3b59-3b5f - 'I' // 3b60 #8 - 'kA' // 3b61-3b6c - 'T' // 3b6d #19 - 'bA' // 3b6e-3b70 - 'aI' // 3b71-3b72 #8 - 'cA' // 3b73-3b76 - 'T' // 3b77 #19 - 'bA' // 3b78-3b7a - 'aI' // 3b7b-3b7c #8 - 'bA' // 3b7d-3b7f - 'I' // 3b80 #8 - 'eA' // 3b81-3b86 - 'aT' // 3b87-3b88 #19 - 'cA' // 3b89-3b8c - 'T' // 3b8d #19 - 'fA' // 3b8e-3b94 - 'aL' // 3b95-3b96 #11 - 'aA' // 3b97-3b98 - 'L' // 3b99 #11 - 'fA' // 3b9a-3ba0 - 'L' // 3ba1 #11 - 'aA' // 3ba2-3ba3 - 'T' // 3ba4 #19 - 'pA' // 3ba5-3bb5 - 'T' // 3bb6 #19 - 'dA' // 3bb7-3bbb - 'L' // 3bbc #11 - 'A' // 3bbd - 'L' // 3bbe #11 - 'bA' // 3bbf-3bc1 - 'L' // 3bc2 #11 - 'T' // 3bc3 #19 - 'L' // 3bc4 #11 - 'gA' // 3bc5-3bcc - 'T' // 3bcd #19 - 'hA' // 3bce-3bd6 - '40W' // 3bd7 #1062 - 'dA' // 3bd8-3bdc - 'L' // 3bdd #11 - 'mA' // 3bde-3beb - 'L' // 3bec #11 - 'bA' // 3bed-3bef - 'T' // 3bf0 #19 - 'A' // 3bf1 - 'L' // 3bf2 #11 - '23V' // 3bf3 #619 - 'L' // 3bf4 #11 - 'wA' // 3bf5-3c0c - 'L' // 3c0d #11 - 'A' // 3c0e - 'T' // 3c0f #19 - 'A' // 3c10 - 'L' // 3c11 #11 - 'bA' // 3c12-3c14 - 'L' // 3c15 #11 - 'aA' // 3c16-3c17 - 'L' // 3c18 #11 - 'lA' // 3c19-3c25 - 'T' // 3c26 #19 - '1rA' // 3c27-3c53 - 'L' // 3c54 #11 - '2aA' // 3c55-3c8a - 'L' // 3c8b #11 - '2bA' // 3c8c-3cc2 - 'T' // 3cc3 #19 - 'fA' // 3cc4-3cca - 'L' // 3ccb #11 - 'A' // 3ccc - 'L' // 3ccd #11 - 'bA' // 3cce-3cd0 - 'L' // 3cd1 #11 - 'T' // 3cd2 #19 - 'bA' // 3cd3-3cd5 - 'L' // 3cd6 #11 - 'dA' // 3cd7-3cdb - 'L' // 3cdc #11 - 'mA' // 3cdd-3cea - 'L' // 3ceb #11 - 'bA' // 3cec-3cee - 'L' // 3cef #11 - '1fA' // 3cf0-3d10 - 'T' // 3d11 #19 - 'aL' // 3d12-3d13 #11 - 'hA' // 3d14-3d1c - 'L' // 3d1d #11 - 'T' // 3d1e #19 - 'qA' // 3d1f-3d30 - 'T' // 3d31 #19 - 'L' // 3d32 #11 - 'gA' // 3d33-3d3a - 'L' // 3d3b #11 - 'iA' // 3d3c-3d45 - 'L' // 3d46 #11 - 'dA' // 3d47-3d4b - 'L' // 3d4c #11 - 'A' // 3d4d - '23V' // 3d4e #619 - 'aA' // 3d4f-3d50 - 'L' // 3d51 #11 - 'lA' // 3d52-3d5e - 'L' // 3d5f #11 - 'aA' // 3d60-3d61 - 'L' // 3d62 #11 - 'A' // 3d63 - 'T' // 3d64 #19 - 'cA' // 3d65-3d68 - 'aL' // 3d69-3d6a #11 - 'cA' // 3d6b-3d6e - 'L' // 3d6f #11 - 'dA' // 3d70-3d74 - 'L' // 3d75 #11 - 'fA' // 3d76-3d7c - 'L' // 3d7d #11 - 'fA' // 3d7e-3d84 - 'L' // 3d85 #11 - 'aA' // 3d86-3d87 - 'L' // 3d88 #11 - 'A' // 3d89 - 'L' // 3d8a #11 - 'cA' // 3d8b-3d8e - 'L' // 3d8f #11 - 'A' // 3d90 - 'L' // 3d91 #11 - 'gA' // 3d92-3d99 - 'T' // 3d9a #19 - 'iA' // 3d9b-3da4 - 'L' // 3da5 #11 - 'dA' // 3da6-3daa - '7J' // 3dab #191 - 'A' // 3dac - 'L' // 3dad #11 - 'eA' // 3dae-3db3 - 'L' // 3db4 #11 - 'iA' // 3db5-3dbe - 'L' // 3dbf #11 - 'T' // 3dc0 #19 - 'dA' // 3dc1-3dc5 - 'aL' // 3dc6-3dc7 #11 - 'A' // 3dc8 - 'L' // 3dc9 #11 - 'aA' // 3dca-3dcb - '23V' // 3dcc #619 - 'L' // 3dcd #11 - 'dA' // 3dce-3dd2 - 'L' // 3dd3 #11 - 'T' // 3dd4 #19 - 'eA' // 3dd5-3dda - '40W' // 3ddb #1062 - 'jA' // 3ddc-3de6 - '62A' // 3de7 #1612 - 'L' // 3de8 #11 - 'aA' // 3de9-3dea - '62A' // 3deb #1612 - 'fA' // 3dec-3df2 - 'aL' // 3df3-3df4 #11 - 'aA' // 3df5-3df6 - 'L' // 3df7 #11 - 'cA' // 3df8-3dfb - 'aL' // 3dfc-3dfd #11 - 'fA' // 3dfe-3e04 - 'T' // 3e05 #19 - 'L' // 3e06 #11 - 'rA' // 3e07-3e19 - '7J' // 3e1a #191 - '1iA' // 3e1b-3e3e - 'T' // 3e3f #19 - '23V' // 3e40 #619 - 'aA' // 3e41-3e42 - 'L' // 3e43 #11 - 'cA' // 3e44-3e47 - 'L' // 3e48 #11 - 'kA' // 3e49-3e54 - 'L' // 3e55 #11 - 'iA' // 3e56-3e5f - 'T' // 3e60 #19 - 'dA' // 3e61-3e65 - 'T' // 3e66 #19 - 'A' // 3e67 - 'T' // 3e68 #19 - 'jA' // 3e69-3e73 - '40W' // 3e74 #1062 - 'mA' // 3e75-3e82 - 'T' // 3e83 #19 - 'eA' // 3e84-3e89 - 'T' // 3e8a #19 - 'hA' // 3e8b-3e93 - 'T' // 3e94 #19 - 'rA' // 3e95-3ea7 - 'bL' // 3ea8-3eaa #11 - 'aA' // 3eab-3eac - 'L' // 3ead #11 - 'bA' // 3eae-3eb0 - 'L' // 3eb1 #11 - 'eA' // 3eb2-3eb7 - 'L' // 3eb8 #11 - 'eA' // 3eb9-3ebe - 'L' // 3ebf #11 - 'aA' // 3ec0-3ec1 - 'L' // 3ec2 #11 - 'cA' // 3ec3-3ec6 - 'L' // 3ec7 #11 - 'aA' // 3ec8-3ec9 - 'L' // 3eca #11 - 'A' // 3ecb - 'L' // 3ecc #11 - 'bA' // 3ecd-3ecf - 'aL' // 3ed0-3ed1 #11 - 'cA' // 3ed2-3ed5 - 'aL' // 3ed6-3ed7 #11 - 'aA' // 3ed8-3ed9 - '23V' // 3eda #619 - 'L' // 3edb #11 - 'aA' // 3edc-3edd - 'L' // 3ede #11 - 'aA' // 3edf-3ee0 - 'aL' // 3ee1-3ee2 #11 - 'cA' // 3ee3-3ee6 - 'L' // 3ee7 #11 - 'A' // 3ee8 - 'L' // 3ee9 #11 - 'A' // 3eea - 'aL' // 3eeb-3eec #11 - 'bA' // 3eed-3eef - 'L' // 3ef0 #11 - 'aA' // 3ef1-3ef2 - 'aL' // 3ef3-3ef4 #11 - 'dA' // 3ef5-3ef9 - 'L' // 3efa #11 - 'A' // 3efb - 'L' // 3efc #11 - 'aA' // 3efd-3efe - 'aL' // 3eff-3f00 #11 - 'bA' // 3f01-3f03 - 'L' // 3f04 #11 - 'A' // 3f05 - 'aL' // 3f06-3f07 #11 - 'eA' // 3f08-3f0d - '40W' // 3f0e #1062 - 'kA' // 3f0f-3f1a - '7J' // 3f1b #191 - '2bA' // 3f1c-3f52 - 'L' // 3f53 #11 - 'bA' // 3f54-3f56 - 'T' // 3f57 #19 - 'aL' // 3f58-3f59 #11 - 'hA' // 3f5a-3f62 - 'L' // 3f63 #11 - 'hA' // 3f64-3f6c - '7J' // 3f6d #191 - 'cA' // 3f6e-3f71 - 'T' // 3f72 #19 - 'aA' // 3f73-3f74 - 'T' // 3f75 #19 - 'A' // 3f76 - 'T' // 3f77 #19 - 'cA' // 3f78-3f7b - 'L' // 3f7c #11 - 'uA' // 3f7d-3f92 - 'L' // 3f93 #11 - 'yA' // 3f94-3fad - 'T' // 3fae #19 - 'aA' // 3faf-3fb0 - 'T' // 3fb1 #19 - 'mA' // 3fb2-3fbf - 'L' // 3fc0 #11 - 'fA' // 3fc1-3fc7 - 'L' // 3fc8 #11 - 'T' // 3fc9 #19 - 'lA' // 3fca-3fd6 - '23V' // 3fd7 #619 - 'cA' // 3fd8-3fdb - '23V' // 3fdc #619 - 'gA' // 3fdd-3fe4 - 'L' // 3fe5 #11 - 'fA' // 3fe6-3fec - 'L' // 3fed #11 - 'jA' // 3fee-3ff8 - 'aL' // 3ff9-3ffa #11 - 'hA' // 3ffb-4003 - 'L' // 4004 #11 - 'cA' // 4005-4008 - 'L' // 4009 #11 - 'rA' // 400a-401c - 'L' // 401d #11 - 'zA' // 401e-4038 - '23V' // 4039 #619 - 'jA' // 403a-4044 - 'L' // 4045 #11 - 'lA' // 4046-4052 - 'L' // 4053 #11 - 'bA' // 4054-4056 - 'L' // 4057 #11 - 'T' // 4058 #19 - 'hA' // 4059-4061 - 'L' // 4062 #11 - 'aA' // 4063-4064 - 'L' // 4065 #11 - 'cA' // 4066-4069 - 'L' // 406a #11 - 'cA' // 406b-406e - 'L' // 406f #11 - 'A' // 4070 - 'L' // 4071 #11 - '1fA' // 4072-4092 - '1B' // 4093 #27 - 'sA' // 4094-40a7 - 'L' // 40a8 #11 - 'jA' // 40a9-40b3 - 'L' // 40b4 #11 - 'eA' // 40b5-40ba - 'L' // 40bb #11 - 'bA' // 40bc-40be - 'L' // 40bf #11 - 'gA' // 40c0-40c7 - 'L' // 40c8 #11 - 'nA' // 40c9-40d7 - 'L' // 40d8 #11 - 'eA' // 40d9-40de - 'L' // 40df #11 - 'wA' // 40e0-40f7 - 'L' // 40f8 #11 - 'A' // 40f9 - 'L' // 40fa #11 - 'fA' // 40fb-4101 - '62B' // 4102 #1613 - '116W' // 4103 #3038 - 'L' // 4104 #11 - '1B' // 4105 #27 - 'bA' // 4106-4108 - 'L' // 4109 #11 - 'cA' // 410a-410d - 'L' // 410e #11 - '1gA' // 410f-4130 - 'aL' // 4131-4132 #11 - 'tA' // 4133-4147 - '1B' // 4148 #27 - 'eA' // 4149-414e - '1B' // 414f #27 - 'rA' // 4150-4162 - '1B' // 4163 #27 - 'bA' // 4164-4166 - 'L' // 4167 #11 - 'cA' // 4168-416b - 'L' // 416c #11 - 'A' // 416d - 'L' // 416e #11 - 'lA' // 416f-417b - 'L' // 417c #11 - 'aA' // 417d-417e - 'L' // 417f #11 - 'A' // 4180 - '62B' // 4181 #1613 - 'mA' // 4182-418f - 'L' // 4190 #11 - '1fA' // 4191-41b1 - 'L' // 41b2 #11 - 'A' // 41b3 - '1B' // 41b4 #27 - 'iA' // 41b5-41be - '1B' // 41bf #27 - 'cA' // 41c0-41c3 - 'L' // 41c4 #11 - 'dA' // 41c5-41c9 - 'L' // 41ca #11 - 'cA' // 41cb-41ce - 'L' // 41cf #11 - 'jA' // 41d0-41da - 'G' // 41db #6 - 'iA' // 41dc-41e5 - '1B' // 41e6 #27 - 'eA' // 41e7-41ec - 'G' // 41ed #6 - '1B' // 41ee #27 - 'G' // 41ef #6 - 'bA' // 41f0-41f2 - '1B' // 41f3 #27 - 'dA' // 41f4-41f8 - 'G' // 41f9 #6 - 'lA' // 41fa-4206 - '1B' // 4207 #27 - 'eA' // 4208-420d - '1B' // 420e #27 - 'aA' // 420f-4210 - 'G' // 4211 #6 - 'pA' // 4212-4222 - 'G' // 4223 #6 - '1aA' // 4224-423f - 'G' // 4240 #6 - '1dA' // 4241-425f - 'G' // 4260 #6 - 'bA' // 4261-4263 - '1B' // 4264 #27 - 'dA' // 4265-4269 - 'G' // 426a #6 - 'jA' // 426b-4275 - 'G' // 4276 #6 - 'bA' // 4277-4279 - 'G' // 427a #6 - 'pA' // 427b-428b - 'G' // 428c #6 - 'eA' // 428d-4292 - '1B' // 4293 #27 - 'G' // 4294 #6 - 'lA' // 4295-42a1 - 'G' // 42a2 #6 - 'qA' // 42a3-42b4 - 'G' // 42b5 #6 - 'bA' // 42b6-42b8 - 'G' // 42b9 #6 - 'aA' // 42ba-42bb - 'G' // 42bc #6 - 'hA' // 42bd-42c5 - '1B' // 42c6 #27 - 'nA' // 42c7-42d5 - '1B' // 42d6 #27 - 'eA' // 42d7-42dc - '1B' // 42dd #27 - 'uA' // 42de-42f3 - 'G' // 42f4 #6 - 'eA' // 42f5-42fa - 'aG' // 42fb-42fc #6 - 'dA' // 42fd-4301 - '1B' // 4302 #27 - 'fA' // 4303-4309 - 'G' // 430a #6 - '1eA' // 430b-432a - '19E' // 432b #498 - 'vA' // 432c-4342 - '1B' // 4343 #27 - '1oA' // 4344-436d - 'G' // 436e #6 - '1mA' // 436f-4396 - 'G' // 4397 #6 - 'aA' // 4398-4399 - 'G' // 439a #6 - '1dA' // 439b-43b9 - 'G' // 43ba #6 - 'eA' // 43bb-43c0 - 'G' // 43c1 #6 - 'vA' // 43c2-43d8 - 'G' // 43d9 #6 - 'dA' // 43da-43de - 'G' // 43df #6 - 'lA' // 43e0-43ec - 'G' // 43ed #6 - '1B' // 43ee #27 - 'A' // 43ef - '19E' // 43f0 #498 - 'A' // 43f1 - 'G' // 43f2 #6 - 'mA' // 43f3-4400 - 'aG' // 4401-4402 #6 - 'dA' // 4403-4407 - '1B' // 4408 #27 - 'bA' // 4409-440b - '1B' // 440c #27 - 'eA' // 440d-4412 - 'G' // 4413 #6 - 'bA' // 4414-4416 - '1B' // 4417 #27 - 'cA' // 4418-441b - '1B' // 441c #27 - 'dA' // 441d-4421 - '1B' // 4422 #27 - 'aA' // 4423-4424 - 'G' // 4425 #6 - 'fA' // 4426-442c - 'G' // 442d #6 - '1jA' // 442e-4452 - '1B' // 4453 #27 - 'fA' // 4454-445a - '1B' // 445b #27 - 'yA' // 445c-4475 - '1B' // 4476 #27 - 'bA' // 4477-4479 - '19E' // 447a #498 - 'sA' // 447b-448e - 'G' // 448f #6 - 'A' // 4490 - '19E' // 4491 #498 - 'bA' // 4492-4494 - '7J' // 4495 #191 - 'hA' // 4496-449e - 'aG' // 449f-44a0 #6 - 'A' // 44a1 - 'G' // 44a2 #6 - 'lA' // 44a3-44af - 'G' // 44b0 #6 - 'aA' // 44b1-44b2 - '1B' // 44b3 #27 - 'bA' // 44b4-44b6 - 'G' // 44b7 #6 - 'dA' // 44b8-44bc - 'G' // 44bd #6 - '1B' // 44be #27 - 'A' // 44bf - 'G' // 44c0 #6 - 'aA' // 44c1-44c2 - 'G' // 44c3 #6 - 'A' // 44c4 - 'G' // 44c5 #6 - 'gA' // 44c6-44cd - 'G' // 44ce #6 - 'dA' // 44cf-44d3 - '1B' // 44d4 #27 - 'gA' // 44d5-44dc - 'bG' // 44dd-44df #6 - 'A' // 44e0 - 'G' // 44e1 #6 - 'aA' // 44e2-44e3 - 'G' // 44e4 #6 - 'cA' // 44e5-44e8 - 'cG' // 44e9-44ec #6 - 'fA' // 44ed-44f3 - 'G' // 44f4 #6 - 'mA' // 44f5-4502 - 'aG' // 4503-4504 #6 - 'bA' // 4505-4507 - '1B' // 4508 #27 - 'G' // 4509 #6 - 'A' // 450a - 'G' // 450b #6 - 'A' // 450c - '1B' // 450d #27 - 'gA' // 450e-4515 - 'G' // 4516 #6 - 'cA' // 4517-451a - 'G' // 451b #6 - 'A' // 451c - 'G' // 451d #6 - 'fA' // 451e-4524 - '1B' // 4525 #27 - 'A' // 4526 - 'G' // 4527 #6 - 'eA' // 4528-452d - 'G' // 452e #6 - 'cA' // 452f-4532 - 'G' // 4533 #6 - 'aA' // 4534-4535 - 'G' // 4536 #6 - 'cA' // 4537-453a - 'G' // 453b #6 - 'A' // 453c - 'G' // 453d #6 - 'A' // 453e - 'G' // 453f #6 - 'bA' // 4540-4542 - '19E' // 4543 #498 - 'lA' // 4544-4550 - 'G' // 4551 #6 - '61Z' // 4552 #1611 - 'aA' // 4553-4554 - 'G' // 4555 #6 - 'aA' // 4556-4557 - 'G' // 4558 #6 - 'bA' // 4559-455b - 'G' // 455c #6 - 'cA' // 455d-4560 - 'aG' // 4561-4562 #6 - 'fA' // 4563-4569 - 'G' // 456a #6 - 'aA' // 456b-456c - 'G' // 456d #6 - 'hA' // 456e-4576 - 'aG' // 4577-4578 #6 - 'A' // 4579 - '1B' // 457a #27 - 'iA' // 457b-4584 - 'G' // 4585 #6 - 'vA' // 4586-459c - '1B' // 459d #27 - 'gA' // 459e-45a5 - 'G' // 45a6 #6 - 'kA' // 45a7-45b2 - 'G' // 45b3 #6 - 'cA' // 45b4-45b7 - '1B' // 45b8 #27 - 'dA' // 45b9-45bd - '1B' // 45be #27 - 'zA' // 45bf-45d9 - 'G' // 45da #6 - 'iA' // 45db-45e4 - '1B' // 45e5 #27 - 'bA' // 45e6-45e8 - 'G' // 45e9 #6 - '19E' // 45ea #498 - 'wA' // 45eb-4602 - 'G' // 4603 #6 - 'aA' // 4604-4605 - 'G' // 4606 #6 - 'gA' // 4607-460e - '19E' // 460f #498 - '1B' // 4610 #27 - 'cA' // 4611-4614 - 'G' // 4615 #6 - 'A' // 4616 - 'G' // 4617 #6 - '1nA' // 4618-4640 - '1B' // 4641 #27 - 'xA' // 4642-465a - 'G' // 465b #6 - 'hA' // 465c-4664 - '1B' // 4665 #27 - 'sA' // 4666-4679 - 'G' // 467a #6 - 'dA' // 467b-467f - 'G' // 4680 #6 - '1eA' // 4681-46a0 - '19E' // 46a1 #498 - 'kA' // 46a2-46ad - '19E' // 46ae #498 - '1B' // 46af #27 - 'jA' // 46b0-46ba - 'G' // 46bb #6 - 'rA' // 46bc-46ce - 'aG' // 46cf-46d0 #6 - '1iA' // 46d1-46f4 - 'G' // 46f5 #6 - 'A' // 46f6 - 'G' // 46f7 #6 - 'sA' // 46f8-470b - '1B' // 470c #27 - 'eA' // 470d-4712 - 'G' // 4713 #6 - 'cA' // 4714-4717 - 'G' // 4718 #6 - 'eA' // 4719-471e - '1B' // 471f #27 - 'bA' // 4720-4722 - '7J' // 4723 #191 - 'qA' // 4724-4735 - 'G' // 4736 #6 - 'lA' // 4737-4743 - 'G' // 4744 #6 - 'hA' // 4745-474d - 'aG' // 474e-474f #6 - 'sA' // 4750-4763 - '1B' // 4764 #27 - 'vA' // 4765-477b - 'G' // 477c #6 - 'zA' // 477d-4797 - 'G' // 4798 #6 - 'lA' // 4799-47a5 - 'G' // 47a6 #6 - '1sA' // 47a7-47d4 - 'G' // 47d5 #6 - 'oA' // 47d6-47e5 - '1B' // 47e6 #27 - 'eA' // 47e7-47ec - 'G' // 47ed #6 - 'eA' // 47ee-47f3 - 'G' // 47f4 #6 - 'gA' // 47f5-47fc - '1B' // 47fd #27 - 'aA' // 47fe-47ff - 'G' // 4800 #6 - 'iA' // 4801-480a - 'G' // 480b #6 - 'iA' // 480c-4815 - '1B' // 4816 #27 - 'fA' // 4817-481d - '1B' // 481e #27 - 'wA' // 481f-4836 - '61Z' // 4837 #1611 - 'kA' // 4838-4843 - '1B' // 4844 #27 - 'hA' // 4845-484d - '1B' // 484e #27 - 'mA' // 484f-485c - 'G' // 485d #6 - 'rA' // 485e-4870 - 'G' // 4871 #6 - '1nA' // 4872-489a - 'G' // 489b #6 - 'pA' // 489c-48ac - 'aG' // 48ad-48ae #6 - 'eA' // 48af-48b4 - '1B' // 48b5 #27 - 'yA' // 48b6-48cf - 'G' // 48d0 #6 - 'kA' // 48d1-48dc - 'G' // 48dd #6 - 'nA' // 48de-48ec - 'G' // 48ed #6 - 'dA' // 48ee-48f2 - 'G' // 48f3 #6 - 'eA' // 48f4-48f9 - '116V' // 48fa #3037 - 'jA' // 48fb-4905 - 'G' // 4906 #6 - 'iA' // 4907-4910 - 'G' // 4911 #6 - 'kA' // 4912-491d - 'G' // 491e #6 - 'eA' // 491f-4924 - 'G' // 4925 #6 - 'cA' // 4926-4929 - 'G' // 492a #6 - 'aA' // 492b-492c - 'G' // 492d #6 - 'A' // 492e - 'aG' // 492f-4930 #6 - 'cA' // 4931-4934 - 'G' // 4935 #6 - 'eA' // 4936-493b - 'G' // 493c #6 - 'A' // 493d - 'G' // 493e #6 - 'eA' // 493f-4944 - 'G' // 4945 #6 - 'jA' // 4946-4950 - 'G' // 4951 #6 - 'A' // 4952 - 'G' // 4953 #6 - 'pA' // 4954-4964 - 'G' // 4965 #6 - 'cA' // 4966-4969 - 'G' // 496a #6 - 'fA' // 496b-4971 - 'G' // 4972 #6 - 'uA' // 4973-4988 - 'G' // 4989 #6 - 'vA' // 498a-49a0 - 'G' // 49a1 #6 - 'dA' // 49a2-49a6 - 'G' // 49a7 #6 - 'gA' // 49a8-49af - '1B' // 49b0 #27 - '1sA' // 49b1-49de - 'G' // 49df #6 - 'dA' // 49e0-49e4 - 'G' // 49e5 #6 - 'A' // 49e6 - '19E' // 49e7 #498 - 'qA' // 49e8-49f9 - '1B' // 49fa #27 - 'hA' // 49fb-4a03 - '1B' // 4a04 #27 - 'iA' // 4a05-4a0e - 'G' // 4a0f #6 - 'lA' // 4a10-4a1c - 'G' // 4a1d #6 - 'eA' // 4a1e-4a23 - 'G' // 4a24 #6 - 'cA' // 4a25-4a28 - '1B' // 4a29 #27 - 'jA' // 4a2a-4a34 - 'G' // 4a35 #6 - '3qA' // 4a36-4a95 - 'G' // 4a96 #6 - 'lA' // 4a97-4aa3 - 'G' // 4aa4 #6 - 'nA' // 4aa5-4ab3 - 'G' // 4ab4 #6 - 'bA' // 4ab5-4ab7 - 'G' // 4ab8 #6 - 'bA' // 4ab9-4abb - '1B' // 4abc #27 - 'sA' // 4abd-4ad0 - 'G' // 4ad1 #6 - 'qA' // 4ad2-4ae3 - '1Y' // 4ae4 #50 - 'yA' // 4ae5-4afe - '1Y' // 4aff #50 - 'oA' // 4b00-4b0f - '1Y' // 4b10 #50 - 'gA' // 4b11-4b18 - '1Y' // 4b19 #50 - 'eA' // 4b1a-4b1f - '1Y' // 4b20 #50 - 'jA' // 4b21-4b2b - '1Y' // 4b2c #50 - 'iA' // 4b2d-4b36 - '1Y' // 4b37 #50 - '1B' // 4b38 #27 - 'aA' // 4b39-4b3a - '1B' // 4b3b #27 - '1xA' // 4b3c-4b6e - 'a1Y' // 4b6f-4b70 #50 - 'A' // 4b71 - '1Y' // 4b72 #50 - 'gA' // 4b73-4b7a - '1Y' // 4b7b #50 - 'aA' // 4b7c-4b7d - '40S' // 4b7e #1058 - 'nA' // 4b7f-4b8d - '1Y' // 4b8e #50 - 'A' // 4b8f - '1Y' // 4b90 #50 - 'aA' // 4b91-4b92 - '1Y' // 4b93 #50 - 'aA' // 4b94-4b95 - 'a1Y' // 4b96-4b97 #50 - 'dA' // 4b98-4b9c - '1Y' // 4b9d #50 - '1dA' // 4b9e-4bbc - 'a1Y' // 4bbd-4bbe #50 - 'A' // 4bbf - '1Y' // 4bc0 #50 - 'A' // 4bc1 - '1B' // 4bc2 #27 - 'fA' // 4bc3-4bc9 - '1B' // 4bca #27 - 'fA' // 4bcb-4bd1 - '1B' // 4bd2 #27 - 'tA' // 4bd3-4be7 - '1B' // 4be8 #27 - 'zA' // 4be9-4c03 - '1Y' // 4c04 #50 - 'aA' // 4c05-4c06 - '1Y' // 4c07 #50 - 'eA' // 4c08-4c0d - '1Y' // 4c0e #50 - 'gA' // 4c0f-4c16 - '1B' // 4c17 #27 - 'gA' // 4c18-4c1f - '1B' // 4c20 #27 - 'pA' // 4c21-4c31 - '1Y' // 4c32 #50 - 'dA' // 4c33-4c37 - '1B' // 4c38 #27 - 'aA' // 4c39-4c3a - '1Y' // 4c3b #50 - 'aA' // 4c3c-4c3d - '1Y' // 4c3e #50 - 'A' // 4c3f - '1Y' // 4c40 #50 - 'eA' // 4c41-4c46 - '1Y' // 4c47 #50 - 'nA' // 4c48-4c56 - '1Y' // 4c57 #50 - 'bA' // 4c58-4c5a - '1Y' // 4c5b #50 - 'pA' // 4c5c-4c6c - '1Y' // 4c6d #50 - 'hA' // 4c6e-4c76 - '1Y' // 4c77 #50 - 'bA' // 4c78-4c7a - '1Y' // 4c7b #50 - 'A' // 4c7c - '1Y' // 4c7d #50 - 'bA' // 4c7e-4c80 - '1Y' // 4c81 #50 - 'bA' // 4c82-4c84 - '1Y' // 4c85 #50 - '1bA' // 4c86-4ca2 - '7J' // 4ca3 #191 - '1Y' // 4ca4 #50 - 'hA' // 4ca5-4cad - '1Y' // 4cae #50 - 'A' // 4caf - '1Y' // 4cb0 #50 - 'eA' // 4cb1-4cb6 - '1Y' // 4cb7 #50 - 'kA' // 4cb8-4cc3 - '1B' // 4cc4 #27 - 'gA' // 4cc5-4ccc - '1Y' // 4ccd #50 - 'bA' // 4cce-4cd0 - '1B' // 4cd1 #27 - 'nA' // 4cd2-4ce0 - '40S' // 4ce1 #1058 - '1Y' // 4ce2 #50 - 'iA' // 4ce3-4cec - '1Y' // 4ced #50 - 'xA' // 4cee-4d06 - '40S' // 4d07 #1058 - 'A' // 4d08 - '48K' // 4d09 #1258 - 'eA' // 4d0a-4d0f - '1Y' // 4d10 #50 - '1hA' // 4d11-4d33 - '1Y' // 4d34 #50 - '2lA' // 4d35-4d75 - '1Y' // 4d76 #50 - '40S' // 4d77 #1058 - 'pA' // 4d78-4d88 - '1Y' // 4d89 #50 - 'fA' // 4d8a-4d90 - '1Y' // 4d91 #50 - 'iA' // 4d92-4d9b - '1Y' // 4d9c #50 - '1hA' // 4d9d-4dbf - '2kF' // 4dc0-4dff #5 - '247B' // 4e00 #6423 - '205Y' // 4e01 #5354 - '19J' // 4e02 #503 - '220E' // 4e03 #5724 - '14O' // 4e04 #378 - '19J' // 4e05 #503 - '7J' // 4e06 #191 - '131G' // 4e07 #3412 - '169H' // 4e08 #4401 - '243R' // 4e09 #6335 - '69L' // 4e0a #1805 - '69J' // 4e0b #1803 - '14O' // 4e0c #378 - '246N' // 4e0d #6409 - '162E' // 4e0e #4216 - '14O' // 4e0f #378 - '116J' // 4e10 #3025 - '141I' // 4e11 #3674 - '1B' // 4e12 #27 - '7K' // 4e13 #192 - '226O' // 4e14 #5890 - '40T' // 4e15 #1059 - '238A' // 4e16 #6188 - '19J' // 4e17 #503 - '161X' // 4e18 #4209 - '65L' // 4e19 #1701 - '35T' // 4e1a #929 - '2R' // 4e1b #69 - '64Z' // 4e1c #1689 - '2D' // 4e1d #55 - '147O' // 4e1e #3836 - '179T' // 4e1f #4673 - '7J' // 4e20 #191 - '116P' // 4e21 #3031 - '116S' // 4e22 #3034 - '1B' // 4e23 #27 - '141L' // 4e24 #3677 - '2C' // 4e25 #54 - '235L' // 4e26 #6121 - '2Y' // 4e27 #76 - '135G' // 4e28 #3516 - '19J' // 4e29 #503 - '166C' // 4e2a #4318 - '141K' // 4e2b #3676 - '61V' // 4e2c #1607 - '247G' // 4e2d #6428 - '1B' // 4e2e #27 - '61V' // 4e2f #1607 - '124M' // 4e30 #3236 - '14O' // 4e31 #378 - '195V' // 4e32 #5091 - 'A' // 4e33 - '2C' // 4e34 #54 - '19J' // 4e35 #503 - '116L' // 4e36 #3027 - '14O' // 4e37 #378 - '183Z' // 4e38 #4783 - '203G' // 4e39 #5284 - '7K' // 4e3a #192 - '243B' // 4e3b #6319 - '136V' // 4e3c #3557 - '61Y' // 4e3d #1610 - '3Q' // 4e3e #94 - '14O' // 4e3f #378 - 'a19J' // 4e40-4e41 #503 - '40T' // 4e42 #1059 - '183M' // 4e43 #4770 - '19J' // 4e44 #503 - '222Y' // 4e45 #5796 - '7J' // 4e46 #191 - '14O' // 4e47 #378 - '159D' // 4e48 #4137 - '61Y' // 4e49 #1610 - 'A' // 4e4a - '241F' // 4e4b #6271 - '3N' // 4e4c #91 - '135F' // 4e4d #3515 - '208H' // 4e4e #5415 - '174R' // 4e4f #4541 - '3Q' // 4e50 #94 - '1B' // 4e51 #27 - '116R' // 4e52 #3033 - '116Q' // 4e53 #3032 - '1R' // 4e54 #43 - '1B' // 4e55 #27 - '166A' // 4e56 #4316 - '259A' // 4e57 #6734 - '194G' // 4e58 #5050 - '176A' // 4e59 #4576 - 'a14O' // 4e5a-4e5b #378 - '147P' // 4e5c #3837 - '221T' // 4e5d #5765 - '125C' // 4e5e #3252 - '241B' // 4e5f #6267 - '2D' // 4e60 #55 - '3I' // 4e61 #86 - 'a1B' // 4e62-4e63 #27 - 'a7J' // 4e64-4e65 #191 - '1Z' // 4e66 #51 - '7J' // 4e67 #191 - '1B' // 4e68 #27 - '14O' // 4e69 #378 - '1Y' // 4e6a #50 - '50F' // 4e6b #1305 - 'A' // 4e6c - '261D' // 4e6d #6789 - 'aA' // 4e6e-4e6f - '1Z' // 4e70 #51 - '257I' // 4e71 #6690 - 'A' // 4e72 - '205Q' // 4e73 #5346 - 'a1B' // 4e74-4e75 #27 - 'a50F' // 4e76-4e77 #1305 - '40V' // 4e78 #1061 - '1B' // 4e79 #27 - 'cA' // 4e7a-4e7d - '215H' // 4e7e #5597 - '19J' // 4e7f #503 - '116M' // 4e80 #3028 - '40V' // 4e81 #1061 - '208F' // 4e82 #5413 - 'aA' // 4e83-4e84 - '14O' // 4e85 #378 - '242G' // 4e86 #6298 - '40V' // 4e87 #1061 - '206M' // 4e88 #5368 - '116N' // 4e89 #3029 - '19J' // 4e8a #503 - '245L' // 4e8b #6381 - '68T' // 4e8c #1787 - '14O' // 4e8d #378 - '200Z' // 4e8e #5225 - '2Y' // 4e8f #76 - '50F' // 4e90 #1305 - '160G' // 4e91 #4166 - '211M' // 4e92 #5498 - '40V' // 4e93 #1061 - '234W' // 4e94 #6106 - '191T' // 4e95 #4985 - '19J' // 4e96 #503 - '1B' // 4e97 #27 - '40T' // 4e98 #1059 - '14O' // 4e99 #378 - '169V' // 4e9a #4415 - '233D' // 4e9b #6061 - '256P' // 4e9c #6671 - '1B' // 4e9d #27 - '226D' // 4e9e #5879 - 'a14O' // 4e9f-4ea0 #378 - '198B' // 4ea1 #5149 - '40T' // 4ea2 #1059 - '61X' // 4ea3 #1609 - '237Y' // 4ea4 #6186 - '35M' // 4ea5 #922 - '214W' // 4ea6 #5586 - '7K' // 4ea7 #192 - '165Z' // 4ea8 #4315 - '3H' // 4ea9 #85 - 'A' // 4eaa - '240R' // 4eab #6257 - '216R' // 4eac #5633 - '168F' // 4ead #4373 - '218S' // 4eae #5686 - '1B' // 4eaf #27 - '16C' // 4eb0 #418 - '8A' // 4eb1 #208 - '2D' // 4eb2 #55 - '13F' // 4eb3 #343 - 'a8A' // 4eb4-4eb5 #208 - '35M' // 4eb6 #922 - '48K' // 4eb7 #1258 - '8A' // 4eb8 #208 - '13F' // 4eb9 #343 - '247E' // 4eba #6426 - 'a13F' // 4ebb-4ebc #343 - 'a8A' // 4ebd-4ebe #208 - '116U' // 4ebf #3036 - '226K' // 4ec0 #5886 - '209Z' // 4ec1 #5459 - 'a13F' // 4ec2-4ec3 #343 - '35M' // 4ec4 #922 - '2D' // 4ec5 #55 - '116K' // 4ec6 #3026 - '166B' // 4ec7 #4317 - '13F' // 4ec8 #343 - '40U' // 4ec9 #1060 - '239I' // 4eca #6222 - '238U' // 4ecb #6208 - '8A' // 4ecc #208 - '208G' // 4ecd #5414 - '130D' // 4ece #3383 - '255D' // 4ecf #6633 - '16C' // 4ed0 #418 - '2K' // 4ed1 #62 - '8A' // 4ed2 #208 - '3N' // 4ed3 #91 - '218D' // 4ed4 #5671 - '184I' // 4ed5 #4792 - '245A' // 4ed6 #6370 - '65L' // 4ed7 #1701 - '224E' // 4ed8 #5828 - '204E' // 4ed9 #5308 - 'a16C' // 4eda-4edb #418 - '48K' // 4edc #1258 - '35M' // 4edd #922 - '13F' // 4ede #343 - '35M' // 4edf #922 - '16C' // 4ee0 #418 - '13F' // 4ee1 #343 - '1B' // 4ee2 #27 - '243Z' // 4ee3 #6343 - '242Z' // 4ee4 #6317 - '69G' // 4ee5 #1800 - 'a8A' // 4ee6-4ee7 #208 - '13F' // 4ee8 #343 - '40U' // 4ee9 #1060 - '116T' // 4eea #3035 - '13F' // 4eeb #343 - '7K' // 4eec #192 - '1B' // 4eed #27 - '116O' // 4eee #3030 - '16C' // 4eef #418 - '181R' // 4ef0 #4723 - '61W' // 4ef1 #1608 - '198Y' // 4ef2 #5172 - '13F' // 4ef3 #343 - '40U' // 4ef4 #1060 - '13F' // 4ef5 #343 - '243X' // 4ef6 #6341 - '141J' // 4ef7 #3675 - '61X' // 4ef8 #1609 - 'a8A' // 4ef9-4efa #208 - '235W' // 4efb #6132 - '16C' // 4efc #418 - '233B' // 4efd #6059 - '1B' // 4efe #27 - '179U' // 4eff #4674 - '13F' // 4f00 #343 - '223P' // 4f01 #5813 - '61W' // 4f02 #1608 - '13F' // 4f03 #343 - '8A' // 4f04 #208 - '40U' // 4f05 #1060 - 'aA' // 4f06-4f07 - '13F' // 4f08 #343 - '61S' // 4f09 #1604 - '205L' // 4f0a #5341 - '61S' // 4f0b #1604 - '1B' // 4f0c #27 - '172K' // 4f0d #4482 - '116B' // 4f0e #3017 - '175L' // 4f0f #4561 - '153O' // 4f10 #3992 - '223K' // 4f11 #5808 - '16C' // 4f12 #418 - '8A' // 4f13 #208 - 'A' // 4f14 - '115V' // 4f15 #3011 - '1B' // 4f16 #27 - '115W' // 4f17 #3012 - '143W' // 4f18 #3740 - '172L' // 4f19 #4483 - '245S' // 4f1a #6388 - '8A' // 4f1b #208 - '1B' // 4f1c #27 - '116E' // 4f1d #3020 - '2R' // 4f1e #69 - '3Y' // 4f1f #102 - '3Q' // 4f20 #94 - '8A' // 4f21 #208 - '61U' // 4f22 #1606 - 'A' // 4f23 - '2C' // 4f24 #54 - '8A' // 4f25 #208 - '3I' // 4f26 #86 - '8A' // 4f27 #208 - '116G' // 4f28 #3022 - '61U' // 4f29 #1606 - '2L' // 4f2a #63 - '16C' // 4f2b #418 - '8A' // 4f2c #208 - '21C' // 4f2d #548 - '16C' // 4f2e #418 - '195L' // 4f2f #5081 - '194H' // 4f30 #5051 - '16C' // 4f31 #418 - '21C' // 4f32 #548 - '16C' // 4f33 #418 - '215F' // 4f34 #5595 - '1B' // 4f35 #27 - '141H' // 4f36 #3673 - '40Q' // 4f37 #1056 - '204U' // 4f38 #5324 - '40Q' // 4f39 #1056 - '167Y' // 4f3a #4366 - '40R' // 4f3b #1057 - '221U' // 4f3c #5766 - '159S' // 4f3d #4152 - '40Q' // 4f3e #1056 - 'A' // 4f3f - '16C' // 4f40 #418 - '21C' // 4f41 #548 - '40R' // 4f42 #1057 - '115U' // 4f43 #3010 - 'A' // 4f44 - '40N' // 4f45 #1053 - '233H' // 4f46 #6065 - '115Z' // 4f47 #3015 - '208E' // 4f48 #5412 - '40Q' // 4f49 #1056 - 'A' // 4f4a - '115T' // 4f4b #3009 - '40R' // 4f4c #1057 - '242Y' // 4f4d #6316 - '229N' // 4f4e #5967 - '231H' // 4f4f #6013 - '191S' // 4f50 #4984 - '167Q' // 4f51 #4358 - '40R' // 4f52 #1057 - '214E' // 4f53 #5568 - '187B' // 4f54 #4863 - '237X' // 4f55 #6185 - '1B' // 4f56 #27 - 'a9H' // 4f57-4f58 #241 - '176P' // 4f59 #4591 - '115M' // 4f5a #3002 - '200X' // 4f5b #5223 - '246S' // 4f5c #6414 - 'a9H' // 4f5d-4f5e #241 - '115Q' // 4f5f #3006 - '240M' // 4f60 #6252 - '21C' // 4f61 #548 - '169S' // 4f62 #4412 - '115R' // 4f63 #3007 - '9H' // 4f64 #241 - '8A' // 4f65 #208 - 'A' // 4f66 - '21C' // 4f67 #548 - 'A' // 4f68 - '186Y' // 4f69 #4860 - '9H' // 4f6a #241 - 'A' // 4f6b - '65W' // 4f6c #1712 - 'A' // 4f6d - '9H' // 4f6e #241 - '17F' // 4f6f #447 - '152L' // 4f70 #3963 - '3F' // 4f71 #83 - '21C' // 4f72 #548 - '227E' // 4f73 #5906 - '21C' // 4f74 #548 - '197G' // 4f75 #5128 - '17F' // 4f76 #447 - 'b9H' // 4f77-4f79 #241 - '17F' // 4f7a #447 - '9H' // 4f7b #241 - '124L' // 4f7c #3235 - '9H' // 4f7d #241 - '17F' // 4f7e #447 - '244Z' // 4f7f #6369 - '21C' // 4f80 #548 - '17F' // 4f81 #447 - '9H' // 4f82 #241 - '124K' // 4f83 #3234 - '17F' // 4f84 #447 - '3F' // 4f85 #83 - '240T' // 4f86 #6259 - 'A' // 4f87 - '135D' // 4f88 #3513 - '9H' // 4f89 #241 - '17F' // 4f8a #447 - '222X' // 4f8b #5795 - '3F' // 4f8c #83 - '148Q' // 4f8d #3864 - '49W' // 4f8e #1296 - '17F' // 4f8f #447 - '9H' // 4f90 #241 - '115Y' // 4f91 #3014 - '9H' // 4f92 #241 - '49W' // 4f93 #1296 - '9H' // 4f94 #241 - '8A' // 4f95 #208 - '17F' // 4f96 #447 - '9H' // 4f97 #241 - '17F' // 4f98 #447 - '3F' // 4f99 #83 - '21B' // 4f9a #547 - '237W' // 4f9b #6184 - 'A' // 4f9c - '234M' // 4f9d #6096 - '9H' // 4f9e #241 - '3F' // 4f9f #83 - '251A' // 4fa0 #6526 - '259Z' // 4fa1 #6759 - '23U' // 4fa2 #618 - '1R' // 4fa3 #43 - 'A' // 4fa4 - '8A' // 4fa5 #208 - '2R' // 4fa6 #69 - '3N' // 4fa7 #91 - '116H' // 4fa8 #3023 - 'a8A' // 4fa9-4faa #208 - '21B' // 4fab #547 - '8A' // 4fac #208 - '3F' // 4fad #83 - '173A' // 4fae #4498 - '165Y' // 4faf #4314 - '40N' // 4fb0 #1053 - 'A' // 4fb1 - '21B' // 4fb2 #547 - '21C' // 4fb3 #548 - '40N' // 4fb4 #1053 - '210D' // 4fb5 #5463 - '180C' // 4fb6 #4682 - '9H' // 4fb7 #241 - '8A' // 4fb8 #208 - '21B' // 4fb9 #547 - '23U' // 4fba #618 - 'a3F' // 4fbb-4fbc #83 - '115O' // 4fbd #3004 - '3F' // 4fbe #83 - '236Y' // 4fbf #6160 - 'a21B' // 4fc0-4fc1 #547 - '222W' // 4fc2 #5794 - '203F' // 4fc3 #5283 - '186Z' // 4fc4 #4861 - '9H' // 4fc5 #241 - '49W' // 4fc6 #1296 - '23U' // 4fc7 #618 - '40O' // 4fc8 #1054 - '70L' // 4fc9 #1831 - '189D' // 4fca #4917 - '21B' // 4fcb #547 - '40O' // 4fcc #1054 - '9H' // 4fcd #241 - '17F' // 4fce #447 - '65W' // 4fcf #1712 - '141F' // 4fd0 #3671 - '19D' // 4fd1 #497 - '3F' // 4fd2 #83 - '19D' // 4fd3 #497 - '115N' // 4fd4 #3003 - 'aA' // 4fd5-4fd6 - '197B' // 4fd7 #5123 - '115P' // 4fd8 #3005 - '23U' // 4fd9 #618 - '19D' // 4fda #497 - 'a15X' // 4fdb-4fdc #413 - '68V' // 4fdd #1789 - '131L' // 4fde #3417 - '19D' // 4fdf #497 - '179Q' // 4fe0 #4670 - '49G' // 4fe1 #1280 - '13I' // 4fe2 #346 - '252T' // 4fe3 #6571 - '15X' // 4fe4 #413 - '21B' // 4fe5 #547 - '13I' // 4fe6 #346 - 'A' // 4fe7 - '16H' // 4fe8 #423 - '2Y' // 4fe9 #76 - '3G' // 4fea #84 - '16H' // 4feb #423 - '61T' // 4fec #1605 - '16H' // 4fed #423 - '230K' // 4fee #5990 - '135C' // 4fef #3512 - '40O' // 4ff0 #1054 - '194F' // 4ff1 #5049 - '40O' // 4ff2 #1054 - '116C' // 4ff3 #3018 - '23U' // 4ff4 #618 - '116A' // 4ff5 #3016 - '15X' // 4ff6 #413 - 'A' // 4ff7 - '115X' // 4ff8 #3013 - '23U' // 4ff9 #618 - '116D' // 4ffa #3019 - 'A' // 4ffb - '3F' // 4ffc #83 - '21B' // 4ffd #547 - '147N' // 4ffe #3835 - '3F' // 4fff #83 - '21B' // 5000 #547 - '13I' // 5001 #346 - '70L' // 5002 #1831 - '40N' // 5003 #1053 - '3F' // 5004 #83 - '15X' // 5005 #413 - '65V' // 5006 #1711 - '3F' // 5007 #83 - '23U' // 5008 #618 - '191L' // 5009 #4977 - '3F' // 500a #83 - '246L' // 500b #6407 - '15X' // 500c #413 - '205V' // 500d #5351 - '3F' // 500e #83 - '15X' // 500f #413 - '3F' // 5010 #83 - '240H' // 5011 #6247 - '212G' // 5012 #5518 - 'a15X' // 5013-5014 #413 - '61T' // 5015 #1605 - '129R' // 5016 #3371 - '13I' // 5017 #346 - '135E' // 5018 #3514 - '219U' // 5019 #5714 - '135A' // 501a #3510 - '15X' // 501b #413 - '19D' // 501c #497 - '13I' // 501d #346 - '19D' // 501e #497 - '197V' // 501f #5143 - 'A' // 5020 - '147M' // 5021 #3834 - '15X' // 5022 #413 - '19D' // 5023 #497 - '258Q' // 5024 #6724 - '15X' // 5025 #413 - '135B' // 5026 #3511 - 'a19D' // 5027-5028 #497 - '141G' // 5029 #3672 - '141E' // 502a #3670 - '210N' // 502b #5473 - 'a19D' // 502c-502d #497 - '15X' // 502e #413 - 'A' // 502f - '15X' // 5030 #413 - '23U' // 5031 #618 - '3F' // 5032 #83 - '13I' // 5033 #346 - '116F' // 5034 #3021 - '3F' // 5035 #83 - '255A' // 5036 #6630 - 'a16H' // 5037-5038 #423 - '13I' // 5039 #346 - '2Y' // 503a #76 - '19D' // 503b #497 - '231T' // 503c #6025 - 'A' // 503d - '1R' // 503e #43 - 'A' // 503f - '115S' // 5040 #3008 - '40P' // 5041 #1055 - '13I' // 5042 #346 - '61R' // 5043 #1603 - 'A' // 5044 - 'a40P' // 5045-5046 #1055 - '226C' // 5047 #5878 - '61R' // 5048 #1603 - '196I' // 5049 #5104 - '40P' // 504a #1055 - '116I' // 504b #3024 - '40P' // 504c #1055 - '40M' // 504d #1052 - '12I' // 504e #320 - '202V' // 504f #5273 - '3F' // 5050 #83 - '12I' // 5051 #320 - '3F' // 5052 #83 - '12I' // 5053 #320 - 'A' // 5054 - '64W' // 5055 #1686 - '61Q' // 5056 #1602 - '13I' // 5057 #346 - '48F' // 5058 #1253 - '3F' // 5059 #83 - '233A' // 505a #6058 - '16H' // 505b #423 - '220U' // 505c #5740 - '19C' // 505d #496 - '40M' // 505e #1052 - '21A' // 505f #546 - '12I' // 5060 #320 - 'A' // 5061 - '13I' // 5062 #346 - '12I' // 5063 #320 - 'A' // 5064 - '236B' // 5065 #6137 - '40L' // 5066 #1051 - '13I' // 5067 #346 - 'aA' // 5068-5069 - '12I' // 506a #320 - 'A' // 506b - '61Q' // 506c #1602 - '3F' // 506d #83 - 'A' // 506e - '19C' // 506f #496 - '12I' // 5070 #320 - '13I' // 5071 #346 - '12I' // 5072 #320 - 'A' // 5073 - '199B' // 5074 #5175 - '175N' // 5075 #4563 - '202F' // 5076 #5257 - '200Y' // 5077 #5224 - '250Z' // 5078 #6525 - 'A' // 5079 - '19C' // 507a #496 - '16H' // 507b #423 - 'A' // 507c - '168O' // 507d #4382 - '16H' // 507e #423 - '2L' // 507f #63 - '48H' // 5080 #1255 - '40L' // 5081 #1051 - 'A' // 5082 - 'a3F' // 5083-5084 #83 - '179R' // 5085 #4671 - '3F' // 5086 #83 - 'A' // 5087 - '12I' // 5088 #320 - '16H' // 5089 #423 - '3F' // 508a #83 - 'a19C' // 508b-508c #496 - '142L' // 508d #3703 - '21A' // 508e #546 - '3F' // 508f #83 - '40L' // 5090 #1051 - '195I' // 5091 #5078 - '12I' // 5092 #320 - 'a3F' // 5093-5094 #83 - '12I' // 5095 #320 - '21A' // 5096 #546 - '16H' // 5097 #423 - '167S' // 5098 #4360 - '236X' // 5099 #6159 - 'b12I' // 509a-509c #320 - '40M' // 509d #1052 - '13I' // 509e #346 - 'b3F' // 509f-50a1 #83 - '179S' // 50a2 #4672 - '12I' // 50a3 #320 - 'A' // 50a4 - '16H' // 50a5 #423 - '115H' // 50a6 #2997 - '16H' // 50a7 #423 - '3Y' // 50a8 #102 - '16H' // 50a9 #423 - '3F' // 50aa #83 - 'A' // 50ab - '169L' // 50ac #4405 - '64W' // 50ad #1686 - 'A' // 50ae - '21A' // 50af #546 - '3F' // 50b0 #83 - '21A' // 50b1 #546 - '172J' // 50b2 #4481 - '240G' // 50b3 #6246 - '21A' // 50b4 #546 - '174G' // 50b5 #4530 - 'A' // 50b6 - '215O' // 50b7 #5604 - 'A' // 50b8 - '3F' // 50b9 #83 - '12I' // 50ba #320 - '187A' // 50bb #4862 - '48F' // 50bc #1253 - '3F' // 50bd #83 - '182N' // 50be #4745 - 'A' // 50bf - '3F' // 50c0 #83 - 'A' // 50c1 - '21A' // 50c2 #546 - '3F' // 50c3 #83 - '13I' // 50c4 #346 - '218J' // 50c5 #5677 - '19C' // 50c6 #496 - '12I' // 50c7 #320 - '19C' // 50c8 #496 - '114Y' // 50c9 #2988 - '48H' // 50ca #1255 - 'A' // 50cb - '3F' // 50cc #83 - '115F' // 50cd #2995 - '21A' // 50ce #546 - '238W' // 50cf #6210 - '40L' // 50d0 #1051 - '65V' // 50d1 #1711 - 'A' // 50d2 - 'a3F' // 50d3-50d4 #83 - '143M' // 50d5 #3730 - '48H' // 50d6 #1255 - 'A' // 50d7 - '3F' // 50d8 #83 - '21A' // 50d9 #546 - '125T' // 50da #3269 - 'A' // 50db - '3F' // 50dc #83 - '14N' // 50dd #377 - '250U' // 50de #6520 - '14N' // 50df #377 - 'A' // 50e0 - '14N' // 50e1 #377 - '2Q' // 50e2 #68 - '14N' // 50e3 #377 - '2Q' // 50e4 #68 - '40K' // 50e5 #1050 - '31G' // 50e6 #812 - '142M' // 50e7 #3704 - 'a14N' // 50e8-50e9 #377 - '19C' // 50ea #496 - 'A' // 50eb - '40M' // 50ec #1052 - '40K' // 50ed #1050 - '31G' // 50ee #812 - '14N' // 50ef #377 - '31G' // 50f0 #812 - '141C' // 50f1 #3668 - '2Q' // 50f2 #68 - '31G' // 50f3 #812 - '48G' // 50f4 #1254 - '141D' // 50f5 #3669 - '2Q' // 50f6 #68 - 'aA' // 50f7-50f8 - '232Y' // 50f9 #6056 - '250V' // 50fa #6521 - '40K' // 50fb #1050 - '48F' // 50fc #1253 - 'A' // 50fd - '31G' // 50fe #812 - '261C' // 50ff #6788 - '203B' // 5100 #5279 - '40K' // 5101 #1050 - '31G' // 5102 #812 - '2Q' // 5103 #68 - '203T' // 5104 #5297 - '19C' // 5105 #496 - '31F' // 5106 #811 - '10E' // 5107 #264 - '14N' // 5108 #377 - '31F' // 5109 #811 - '15A' // 510a #390 - '10E' // 510b #264 - '14N' // 510c #377 - '10E' // 510d #264 - '14N' // 510e #377 - '15A' // 510f #390 - '10E' // 5110 #264 - 'A' // 5111 - '159C' // 5112 #4136 - '48J' // 5113 #1257 - '14N' // 5114 #377 - '10E' // 5115 #264 - '2Q' // 5116 #68 - '10E' // 5117 #264 - '179O' // 5118 #4668 - '2Q' // 5119 #68 - '10E' // 511a #264 - '7R' // 511b #199 - '10E' // 511c #264 - '2Q' // 511d #68 - '7R' // 511e #199 - '176B' // 511f #4577 - '19C' // 5120 #496 - '31F' // 5121 #811 - 'A' // 5122 - '2Q' // 5123 #68 - 'A' // 5124 - '19C' // 5125 #496 - 'A' // 5126 - '2Q' // 5127 #68 - '7R' // 5128 #199 - 'A' // 5129 - '237M' // 512a #6174 - '48J' // 512b #1257 - 'a7R' // 512c-512d #199 - 'A' // 512e - '2Q' // 512f #68 - 'A' // 5130 - '10E' // 5131 #264 - '201S' // 5132 #5244 - '7R' // 5133 #199 - 'a10E' // 5134-5135 #264 - 'A' // 5136 - '134X' // 5137 #3507 - 'a10E' // 5138-5139 #264 - '61N' // 513a #1599 - '14N' // 513b #377 - '31F' // 513c #811 - 'aA' // 513d-513e - '134Z' // 513f #3509 - '31F' // 5140 #811 - '172I' // 5141 #4480 - '7R' // 5142 #199 - '238M' // 5143 #6200 - '198P' // 5144 #5163 - '221S' // 5145 #5764 - '174Q' // 5146 #4540 - '165X' // 5147 #4313 - '239C' // 5148 #6216 - '68T' // 5149 #1787 - '2Q' // 514a #68 - '226Y' // 514b #5900 - '172H' // 514c #4479 - '234N' // 514d #6097 - '253I' // 514e #6586 - '7R' // 514f #199 - '258T' // 5150 #6727 - '115J' // 5151 #2999 - '232Z' // 5152 #6057 - '2Q' // 5153 #68 - '186X' // 5154 #4859 - '10E' // 5155 #264 - '48J' // 5156 #1257 - '10E' // 5157 #264 - '7R' // 5158 #199 - 'A' // 5159 - '115D' // 515a #2993 - 'A' // 515b - '147W' // 515c #3844 - 'aA' // 515d-515e - '14N' // 515f #377 - '48G' // 5160 #1254 - 'A' // 5161 - '31F' // 5162 #811 - 'A' // 5163 - '2Q' // 5164 #68 - '246X' // 5165 #6419 - '2Q' // 5166 #68 - '240F' // 5167 #6245 - '41F' // 5168 #1071 - '232X' // 5169 #6055 - '61N' // 516a #1599 - '228O' // 516b #5942 - '69H' // 516c #1801 - '227S' // 516d #5920 - '114T' // 516e #2983 - 'A' // 516f - '2C' // 5170 #54 - '236Z' // 5171 #6161 - '15A' // 5172 #390 - '250Y' // 5173 #6524 - '114X' // 5174 #2987 - '205U' // 5175 #5350 - '240V' // 5176 #6261 - '236W' // 5177 #6158 - '229W' // 5178 #5976 - '114V' // 5179 #2985 - 'A' // 517a - '250X' // 517b #6523 - '197Q' // 517c #5138 - '3N' // 517d #91 - '2Q' // 517e #68 - '15A' // 517f #390 - '129Q' // 5180 #3370 - '15A' // 5181 #390 - '10E' // 5182 #264 - 'a7R' // 5183-5184 #199 - '260K' // 5185 #6770 - '115G' // 5186 #2996 - '169R' // 5187 #4411 - '115I' // 5188 #2998 - '114U' // 5189 #2984 - '233P' // 518a #6073 - '7R' // 518b #199 - '70K' // 518c #1830 - '242P' // 518d #6307 - '2Q' // 518e #68 - '10E' // 518f #264 - '2Q' // 5190 #68 - '10E' // 5191 #264 - '203O' // 5192 #5292 - '14N' // 5193 #377 - 'A' // 5194 - '114S' // 5195 #2982 - '10E' // 5196 #264 - '115A' // 5197 #2990 - '2Q' // 5198 #68 - '260A' // 5199 #6760 - '48I' // 519a #1256 - '2D' // 519b #55 - '115K' // 519c #3000 - '2Q' // 519d #68 - '115L' // 519e #3001 - 'A' // 519f - '202U' // 51a0 #5272 - '2Q' // 51a1 #68 - '35L' // 51a2 #921 - '7R' // 51a3 #199 - '147K' // 51a4 #3832 - '142A' // 51a5 #3692 - '7R' // 51a6 #199 - '48I' // 51a7 #1256 - '115B' // 51a8 #2991 - '7R' // 51a9 #199 - '129P' // 51aa #3369 - '35L' // 51ab #921 - '205P' // 51ac #5345 - '7R' // 51ad #199 - '48I' // 51ae #1256 - '2Y' // 51af #76 - '214V' // 51b0 #5585 - '35L' // 51b1 #921 - '114W' // 51b2 #2986 - '61O' // 51b3 #1600 - '115C' // 51b4 #2992 - '61O' // 51b5 #1600 - '114Z' // 51b6 #2989 - '221D' // 51b7 #5749 - '48G' // 51b8 #1254 - 'A' // 51b9 - '2Q' // 51ba #68 - '2L' // 51bb #63 - '35L' // 51bc #921 - '114R' // 51bd #2981 - '61P' // 51be #1601 - '7R' // 51bf #199 - '3Y' // 51c0 #102 - 'A' // 51c1 - '2Q' // 51c2 #68 - '35L' // 51c3 #921 - '115E' // 51c4 #2994 - '61P' // 51c5 #1601 - '195A' // 51c6 #5070 - '40J' // 51c7 #1049 - '141B' // 51c8 #3667 - '114A' // 51c9 #2964 - '10D' // 51ca #263 - '31E' // 51cb #810 - '188K' // 51cc #4898 - '196Y' // 51cd #5120 - '40J' // 51ce #1049 - '114E' // 51cf #2968 - '48E' // 51d0 #1252 - '114C' // 51d1 #2966 - 'a31D' // 51d2-51d3 #809 - '10D' // 51d4 #263 - '2Q' // 51d5 #68 - '7R' // 51d6 #199 - 'A' // 51d7 - '2Q' // 51d8 #68 - 'aA' // 51d9-51da - '114I' // 51db #2972 - '114G' // 51dc #2970 - '174P' // 51dd #4539 - '250T' // 51de #6519 - '15W' // 51df #412 - '147L' // 51e0 #3833 - '201Q' // 51e1 #5242 - '31D' // 51e2 #809 - 'A' // 51e3 - '114P' // 51e4 #2979 - '2Q' // 51e5 #68 - '258N' // 51e6 #6721 - '2Q' // 51e7 #68 - '15A' // 51e8 #390 - '7R' // 51e9 #199 - '253A' // 51ea #6578 - '15A' // 51eb #390 - '2Q' // 51ec #68 - '114D' // 51ed #2967 - '2Q' // 51ee #68 - '3N' // 51ef #91 - '153B' // 51f0 #3979 - '201I' // 51f1 #5234 - '2Q' // 51f2 #68 - '134Y' // 51f3 #3508 - '15W' // 51f4 #412 - '10D' // 51f5 #263 - '153U' // 51f6 #3998 - '2Q' // 51f7 #68 - '167B' // 51f8 #4343 - '153Y' // 51f9 #4002 - '41F' // 51fa #1071 - '3Q' // 51fb #94 - '40J' // 51fc #1049 - '175A' // 51fd #4550 - '31D' // 51fe #809 - '15A' // 51ff #390 - '203E' // 5200 #5282 - '114B' // 5201 #2965 - '10D' // 5202 #263 - '142W' // 5203 #3714 - '2Q' // 5204 #68 - '10D' // 5205 #263 - '69K' // 5206 #1804 - '223Y' // 5207 #5822 - '114H' // 5208 #2971 - 'A' // 5209 - '228A' // 520a #5928 - '10D' // 520b #263 - 'A' // 520c - '15A' // 520d #390 - '31E' // 520e #810 - 'aA' // 520f-5210 - '182M' // 5211 #4744 - '179P' // 5212 #4669 - '15W' // 5213 #412 - '2Q' // 5214 #68 - '7R' // 5215 #199 - '10D' // 5216 #263 - '234D' // 5217 #6087 - '250W' // 5218 #6522 - '2D' // 5219 #55 - '3I' // 521a #86 - '3Q' // 521b #94 - 'A' // 521c - '224C' // 521d #5826 - 'A' // 521e - '40F' // 521f #1045 - '114Q' // 5220 #2980 - 'A' // 5221 - '2Q' // 5222 #68 - 'A' // 5223 - '206B' // 5224 #5357 - '239B' // 5225 #6215 - 'a15W' // 5226-5227 #412 - '10D' // 5228 #263 - '245I' // 5229 #6378 - '208D' // 522a #5411 - '70K' // 522b #1830 - 'a15A' // 522c-522d #390 - '172G' // 522e #4478 - 'A' // 522f - '246H' // 5230 #6403 - '2Q' // 5231 #68 - '15W' // 5232 #412 - '10D' // 5233 #263 - '48E' // 5234 #1252 - '2Q' // 5235 #68 - '231E' // 5236 #6010 - '205B' // 5237 #5331 - '205O' // 5238 #5344 - '251V' // 5239 #6547 - '205D' // 523a #5333 - '215S' // 523b #5608 - '10D' // 523c #263 - '15A' // 523d #390 - 'A' // 523e - 'a15A' // 523f-5240 #390 - '114O' // 5241 #2978 - '1R' // 5242 #43 - '125H' // 5243 #3257 - '15W' // 5244 #412 - '7R' // 5245 #199 - 'A' // 5246 - '233X' // 5247 #6081 - 'A' // 5248 - '10D' // 5249 #263 - '162D' // 524a #4215 - '134V' // 524b #3505 - '31E' // 524c #810 - '245Q' // 524d #6386 - '143U' // 524e #3738 - '31D' // 524f #809 - '15A' // 5250 #390 - '3N' // 5251 #91 - '48E' // 5252 #1252 - 'A' // 5253 - '134W' // 5254 #3506 - '15W' // 5255 #412 - '153J' // 5256 #3987 - '15W' // 5257 #412 - '2Q' // 5258 #68 - '40F' // 5259 #1045 - '2Q' // 525a #68 - '219E' // 525b #5698 - '10D' // 525c #263 - '152K' // 525d #3962 - '10D' // 525e #263 - '7R' // 525f #199 - '31D' // 5260 #809 - '31E' // 5261 #810 - 'A' // 5262 - '256O' // 5263 #6670 - '257A' // 5264 #6682 - '254N' // 5265 #6617 - '2Q' // 5266 #68 - '2C' // 5267 #54 - '40F' // 5268 #1045 - '194E' // 5269 #5048 - '194L' // 526a #5055 - 'A' // 526b - '2Q' // 526c #68 - 'A' // 526d - '15W' // 526e #412 - '203W' // 526f #5300 - '253P' // 5270 #6593 - '2Q' // 5271 #68 - '184D' // 5272 #4787 - '10D' // 5273 #263 - '15W' // 5274 #412 - '234L' // 5275 #6095 - 'A' // 5276 - '10D' // 5277 #263 - '2Q' // 5278 #68 - '31D' // 5279 #809 - 'bA' // 527a-527c - '31E' // 527d #810 - 'A' // 527e - '31E' // 527f #810 - '2Q' // 5280 #68 - '40J' // 5281 #1049 - '10D' // 5282 #263 - '214U' // 5283 #5584 - '10D' // 5284 #263 - '7R' // 5285 #199 - 'A' // 5286 - '220G' // 5287 #5726 - '141A' // 5288 #3666 - '200W' // 5289 #5222 - '15W' // 528a #412 - '12T' // 528b #331 - '15W' // 528c #412 - '67B' // 528d #1743 - 'A' // 528e - '114N' // 528f #2977 - '40F' // 5290 #1045 - '67B' // 5291 #1743 - '250Q' // 5292 #6516 - '23T' // 5293 #617 - '40I' // 5294 #1048 - 'c3L' // 5295-5298 #89 - 'A' // 5299 - '113Y' // 529a #2962 - '68Y' // 529b #1792 - '3L' // 529c #89 - '3H' // 529d #85 - '1Z' // 529e #51 - '234A' // 529f #6084 - '41D' // 52a0 #1069 - '66M' // 52a1 #1728 - '12T' // 52a2 #331 - '161R' // 52a3 #4203 - '113W' // 52a4 #2960 - '3L' // 52a5 #89 - '23T' // 52a6 #617 - '9J' // 52a7 #243 - '66M' // 52a8 #1728 - '235E' // 52a9 #6114 - '197F' // 52aa #5127 - '159B' // 52ab #4135 - 'a23T' // 52ac-52ad #617 - 'A' // 52ae - '9J' // 52af #243 - '3L' // 52b0 #89 - '254T' // 52b1 #6623 - '1R' // 52b2 #43 - '3Y' // 52b3 #102 - '257E' // 52b4 #6686 - '23T' // 52b5 #617 - 'b3L' // 52b6-52b8 #89 - '114K' // 52b9 #2974 - '3L' // 52ba #89 - 'a23T' // 52bb-52bc #617 - '3L' // 52bd #89 - '31C' // 52be #808 - '2D' // 52bf #55 - '40I' // 52c0 #1048 - '194C' // 52c1 #5046 - 'A' // 52c2 - '161H' // 52c3 #4193 - '3L' // 52c4 #89 - '31C' // 52c5 #808 - '3L' // 52c6 #89 - '197L' // 52c7 #5133 - '3L' // 52c8 #89 - '155G' // 52c9 #4036 - '9J' // 52ca #243 - '2R' // 52cb #69 - '26S' // 52cc #694 - '31C' // 52cd #808 - 'A' // 52ce - '3L' // 52cf #89 - '23T' // 52d0 #617 - '40I' // 52d1 #1048 - '186V' // 52d2 #4857 - 'A' // 52d3 - '3L' // 52d4 #89 - '246Y' // 52d5 #6420 - '31C' // 52d6 #808 - '23T' // 52d7 #617 - '136W' // 52d8 #3558 - '242O' // 52d9 #6306 - 'A' // 52da - '31C' // 52db #808 - '3L' // 52dc #89 - '213J' // 52dd #5547 - '67O' // 52de #1756 - '199G' // 52df #5180 - '23T' // 52e0 #617 - '26S' // 52e1 #694 - '221X' // 52e2 #5769 - '31C' // 52e3 #808 - '191C' // 52e4 #4968 - '9J' // 52e5 #243 - '40I' // 52e6 #1048 - '256V' // 52e7 #6677 - 'b9J' // 52e8-52ea #243 - '61M' // 52eb #1598 - '9J' // 52ec #243 - 'b12T' // 52ed-52ef #331 - '15V' // 52f0 #411 - '40H' // 52f1 #1047 - '253F' // 52f2 #6583 - '66D' // 52f3 #1719 - '3L' // 52f4 #89 - '67O' // 52f5 #1756 - '9J' // 52f6 #243 - '15V' // 52f7 #411 - '165U' // 52f8 #4310 - '15V' // 52f9 #411 - '113X' // 52fa #2961 - '66D' // 52fb #1719 - '12T' // 52fc #331 - 'A' // 52fd - '180A' // 52fe #4680 - '209C' // 52ff #5436 - '250R' // 5300 #6517 - '40H' // 5301 #1047 - '254K' // 5302 #6614 - '3L' // 5303 #89 - '12T' // 5304 #331 - '234H' // 5305 #6091 - '129N' // 5306 #3367 - '3L' // 5307 #89 - '134U' // 5308 #3504 - 'A' // 5309 - '40H' // 530a #1047 - '15V' // 530b #411 - '3L' // 530c #89 - '40G' // 530d #1046 - 'A' // 530e - 'a40G' // 530f-5310 #1046 - '3L' // 5311 #89 - 'A' // 5312 - '3L' // 5313 #89 - 'A' // 5314 - '40G' // 5315 #1046 - '68X' // 5316 #1791 - '243H' // 5317 #6325 - '3L' // 5318 #89 - '172F' // 5319 #4477 - '15V' // 531a #411 - '3L' // 531b #89 - 'a15V' // 531c-531d #411 - 'a3L' // 531e-531f #89 - '161K' // 5320 #4196 - '124J' // 5321 #3233 - 'A' // 5322 - '140Z' // 5323 #3665 - '26S' // 5324 #694 - '3L' // 5325 #89 - '12T' // 5326 #331 - '26S' // 5327 #694 - 'a3L' // 5328-5329 #89 - '65K' // 532a #1700 - '3L' // 532b #89 - '26S' // 532c #694 - '40H' // 532d #1047 - '12T' // 532e #331 - '208C' // 532f #5410 - '9J' // 5330 #243 - '15V' // 5331 #411 - 'a26S' // 5332-5333 #694 - 'A' // 5334 - '3L' // 5335 #89 - 'aA' // 5336-5337 - '15V' // 5338 #411 - '161I' // 5339 #4194 - '259I' // 533a #6742 - '114J' // 533b #2973 - 'b15V' // 533c-533e #411 - '161O' // 533f #4200 - '240E' // 5340 #6244 - '235H' // 5341 #6117 - '26S' // 5342 #694 - '68L' // 5343 #1779 - '114L' // 5344 #2975 - '15V' // 5345 #411 - '3L' // 5346 #89 - '226J' // 5347 #5885 - '215N' // 5348 #5603 - '152I' // 5349 #3960 - '68L' // 534a #1779 - '9J' // 534b #243 - '15V' // 534c #411 - '114F' // 534d #2969 - '65M' // 534e #1702 - '1Z' // 534f #51 - '12T' // 5350 #331 - '154I' // 5351 #4012 - '126C' // 5352 #3278 - '190J' // 5353 #4949 - '216E' // 5354 #5620 - '3Q' // 5355 #94 - '2C' // 5356 #54 - '236F' // 5357 #6141 - '259U' // 5358 #6754 - '3L' // 5359 #89 - '220T' // 535a #5739 - '3L' // 535b #89 - '166D' // 535c #4319 - '114M' // 535d #2976 - '40G' // 535e #1046 - '61M' // 535f #1598 - '190V' // 5360 #4961 - '232W' // 5361 #6054 - '2L' // 5362 #63 - '15V' // 5363 #411 - '113Z' // 5364 #2963 - '3L' // 5365 #89 - '179M' // 5366 #4666 - '113G' // 5367 #2944 - '261B' // 5368 #6787 - '10C' // 5369 #262 - 'A' // 536a - '2D' // 536b #55 - '10C' // 536c #262 - '48B' // 536d #1249 - '10C' // 536e #262 - '113N' // 536f #2951 - '230F' // 5370 #5985 - '204T' // 5371 #5323 - '10C' // 5372 #262 - '234S' // 5373 #6102 - '113T' // 5374 #2957 - '155B' // 5375 #4031 - 'A' // 5376 - '194D' // 5377 #5047 - '175V' // 5378 #4571 - '10C' // 5379 #262 - '9J' // 537a #243 - '218A' // 537b #5668 - '31B' // 537c #807 - '61J' // 537d #1595 - '48B' // 537e #1249 - '65K' // 537f #1700 - 'aA' // 5380-5381 - '61K' // 5382 #1596 - '3L' // 5383 #89 - '136K' // 5384 #3546 - '3I' // 5385 #86 - '1Z' // 5386 #51 - 'a3L' // 5387-5388 #89 - '250S' // 5389 #6518 - 'A' // 538a - '2C' // 538b #54 - '2Y' // 538c #76 - '12T' // 538d #331 - '10C' // 538e #262 - 'A' // 538f - '12T' // 5390 #331 - 'A' // 5391 - '31B' // 5392 #807 - '61J' // 5393 #1595 - '10C' // 5394 #262 - '2R' // 5395 #69 - '10C' // 5396 #262 - 'A' // 5397 - '147I' // 5398 #3830 - '48C' // 5399 #1250 - '67T' // 539a #1761 - 'a12T' // 539b-539c #331 - '147J' // 539d #3831 - 'A' // 539e - '243M' // 539f #6330 - '61I' // 53a0 #1594 - '9J' // 53a1 #243 - '113V' // 53a2 #2959 - '12T' // 53a3 #331 - '10C' // 53a4 #262 - '61I' // 53a5 #1594 - '113B' // 53a6 #2939 - 'A' // 53a7 - '113Q' // 53a8 #2954 - '10C' // 53a9 #262 - 'a48B' // 53aa-53ab #1249 - 'A' // 53ac - '172E' // 53ad #4476 - '10C' // 53ae #262 - '3L' // 53af #89 - '10C' // 53b0 #262 - 'A' // 53b1 - '179N' // 53b2 #4667 - '257U' // 53b3 #6702 - '48C' // 53b4 #1250 - '9J' // 53b5 #243 - '10C' // 53b6 #262 - 'a9J' // 53b7-53b8 #243 - '31B' // 53b9 #807 - '3L' // 53ba #89 - '68Q' // 53bb #1784 - '12T' // 53bc #331 - '3L' // 53bd #89 - '12T' // 53be #331 - '3I' // 53bf #86 - '3L' // 53c0 #89 - '10C' // 53c1 #262 - '143Q' // 53c2 #3734 - '232V' // 53c3 #6053 - '9J' // 53c4 #243 - '113C' // 53c5 #2940 - 'a12T' // 53c6-53c7 #331 - '233Q' // 53c8 #6074 - '172Z' // 53c9 #4497 - '241I' // 53ca #6274 - '242F' // 53cb #6297 - '137B' // 53cc #3563 - '230R' // 53cd #5997 - '259M' // 53ce #6746 - '9J' // 53cf #243 - '48A' // 53d0 #1248 - '177D' // 53d1 #4605 - '10C' // 53d2 #262 - '9J' // 53d3 #243 - '173I' // 53d4 #4506 - '9J' // 53d5 #243 - '239G' // 53d6 #6220 - '238T' // 53d7 #6207 - '131N' // 53d8 #3419 - '113J' // 53d9 #2947 - '10C' // 53da #262 - '152H' // 53db #3959 - 'A' // 53dc - '3L' // 53dd #89 - '9J' // 53de #243 - '10C' // 53df #262 - '113F' // 53e0 #2943 - '113L' // 53e1 #2949 - '159A' // 53e2 #4134 - '238L' // 53e3 #6199 - '231I' // 53e4 #6014 - '203S' // 53e5 #5296 - '226A' // 53e6 #5876 - '36J' // 53e7 #945 - '23S' // 53e8 #616 - '113R' // 53e9 #2955 - '233G' // 53ea #6064 - '209L' // 53eb #5445 - '181V' // 53ec #4727 - '165T' // 53ed #4309 - '152J' // 53ee #3961 - '69G' // 53ef #1800 - '246K' // 53f0 #6406 - '113M' // 53f1 #2950 - '229B' // 53f2 #5955 - '212O' // 53f3 #5526 - '36J' // 53f4 #945 - '23S' // 53f5 #616 - '113S' // 53f6 #2956 - '155P' // 53f7 #4045 - '234K' // 53f8 #6094 - '2Y' // 53f9 #76 - '36J' // 53fa #945 - 'a31A' // 53fb-53fc #806 - '7Z' // 53fd #207 - '48D' // 53fe #1251 - 'a7Z' // 53ff-5400 #207 - '113D' // 5401 #2941 - '36J' // 5402 #945 - '225Z' // 5403 #5875 - '236O' // 5404 #6150 - '7Z' // 5405 #207 - 'a31A' // 5406-5407 #806 - '49H' // 5408 #1281 - '212V' // 5409 #5533 - '188O' // 540a #4902 - '186W' // 540b #4858 - '49G' // 540c #1280 - '68Z' // 540d #1793 - '201C' // 540e #5228 - '61H' // 540f #1593 - '189M' // 5410 #4926 - '231P' // 5411 #6021 - '23S' // 5412 #616 - '129O' // 5413 #3368 - '31A' // 5414 #806 - '2Y' // 5415 #76 - '31A' // 5416 #806 - '1Z' // 5417 #51 - 'a7Z' // 5418-5419 #207 - '23S' // 541a #616 - '198S' // 541b #5166 - 'A' // 541c - '129L' // 541d #3365 - '165W' // 541e #4312 - '148F' // 541f #3853 - '113K' // 5420 #2948 - '23S' // 5421 #616 - '7Z' // 5422 #207 - '48D' // 5423 #1251 - '48C' // 5424 #1250 - '31A' // 5425 #806 - '219S' // 5426 #5712 - '226B' // 5427 #5877 - '113E' // 5428 #2942 - '61H' // 5429 #1593 - '36J' // 542a #945 - '222V' // 542b #5793 - '113I' // 542c #2946 - 'a23S' // 542d-542e #616 - '61K' // 542f #1596 - 'A' // 5430 - '23S' // 5431 #616 - '31A' // 5432 #806 - '208B' // 5433 #5409 - '113H' // 5434 #2945 - '165V' // 5435 #4311 - '129M' // 5436 #3366 - '31B' // 5437 #807 - '219T' // 5438 #5713 - '198R' // 5439 #5165 - 'A' // 543a - '172X' // 543b #4495 - '147H' // 543c #3829 - '23S' // 543d #616 - '161M' // 543e #4198 - '113A' // 543f #2938 - '194A' // 5440 #5044 - '31B' // 5441 #807 - '176M' // 5442 #4588 - '64J' // 5443 #1673 - '4E' // 5444 #108 - '31B' // 5445 #807 - '173T' // 5446 #4517 - '26R' // 5447 #693 - '196C' // 5448 #5098 - '253O' // 5449 #6592 - '243W' // 544a #6340 - '17E' // 544b #446 - '15U' // 544c #410 - '26R' // 544d #693 - '147F' // 544e #3827 - '4E' // 544f #108 - '3G' // 5450 #84 - '253U' // 5451 #6598 - 'a7Z' // 5452-5453 #207 - '17E' // 5454 #446 - '250P' // 5455 #6515 - '7Z' // 5456 #207 - '3H' // 5457 #85 - '3Q' // 5458 #94 - '7Z' // 5459 #207 - 'A' // 545a - 'a7Z' // 545b-545c #207 - 'A' // 545d - '4E' // 545e #108 - '252U' // 545f #6572 - '61L' // 5460 #1597 - '7Z' // 5461 #207 - '68D' // 5462 #1771 - '17E' // 5463 #446 - '15U' // 5464 #410 - 'A' // 5465 - '134T' // 5466 #3503 - '4E' // 5467 #108 - '231D' // 5468 #6009 - '112Y' // 5469 #2936 - '113P' // 546a #2953 - 'b15U' // 546b-546d #410 - '31Q' // 546e #822 - '17E' // 546f #446 - '15U' // 5470 #410 - '30Z' // 5471 #805 - '17E' // 5472 #446 - '231O' // 5473 #6020 - '15U' // 5474 #410 - '165P' // 5475 #4305 - '15U' // 5476 #410 - '48W' // 5477 #1270 - '17E' // 5478 #446 - 'A' // 5479 - '7Z' // 547a #207 - '112Z' // 547b #2937 - '205N' // 547c #5343 - '229G' // 547d #5960 - '17E' // 547e #446 - '15U' // 547f #410 - '158W' // 5480 #4130 - '172C' // 5481 #4474 - '17E' // 5482 #446 - '31Q' // 5483 #822 - '15U' // 5484 #410 - '26R' // 5485 #693 - '30Z' // 5486 #805 - '61L' // 5487 #1597 - '26R' // 5488 #693 - '4E' // 5489 #108 - '31Q' // 548a #822 - '124H' // 548b #3231 - '243G' // 548c #6324 - '15U' // 548d #410 - '30Z' // 548e #805 - '61G' // 548f #1592 - '30Z' // 5490 #805 - '15U' // 5491 #410 - '152G' // 5492 #3958 - '48A' // 5493 #1248 - '17E' // 5494 #446 - '158Y' // 5495 #4132 - '207Z' // 5496 #5407 - '162O' // 5497 #4226 - '17E' // 5498 #446 - '7Z' // 5499 #207 - '113U' // 549a #2958 - '7Z' // 549b #207 - '26R' // 549c #693 - '7Z' // 549d #207 - '48A' // 549e #1248 - 'a4E' // 549f-54a0 #108 - 'a15U' // 54a1-54a2 #410 - '17E' // 54a3 #446 - '30Z' // 54a4 #805 - '113O' // 54a5 #2952 - '48W' // 54a6 #1270 - '64J' // 54a7 #1673 - '134R' // 54a8 #3501 - '165R' // 54a9 #4307 - '193Z' // 54aa #5043 - '30Z' // 54ab #805 - '179L' // 54ac #4665 - '15U' // 54ad #410 - '26R' // 54ae #693 - '61G' // 54af #1592 - 'A' // 54b0 - '140X' // 54b1 #3663 - '125V' // 54b2 #3271 - '141W' // 54b3 #3688 - '48D' // 54b4 #1251 - '7Z' // 54b5 #207 - 'A' // 54b6 - '26R' // 54b7 #693 - '140V' // 54b8 #3661 - '61E' // 54b9 #1590 - '4E' // 54ba #108 - '20Y' // 54bb #544 - '40E' // 54bc #1044 - '136D' // 54bd #3539 - 'a20Y' // 54be-54bf #544 - '167X' // 54c0 #4365 - '35V' // 54c1 #931 - '48W' // 54c2 #1270 - '31Q' // 54c3 #822 - '124C' // 54c4 #3226 - 'A' // 54c5 - '124F' // 54c6 #3229 - '165S' // 54c7 #4308 - '207W' // 54c8 #5404 - '136T' // 54c9 #3555 - '31Q' // 54ca #822 - '137P' // 54cb #3577 - '30Y' // 54cc #804 - '124I' // 54cd #3232 - '64A' // 54ce #1664 - 'a30Y' // 54cf-54d0 #804 - '2K' // 54d1 #62 - '2R' // 54d2 #69 - '7Z' // 54d3 #207 - '3H' // 54d4 #85 - '7Z' // 54d5 #207 - '30Y' // 54d6 #804 - '2W' // 54d7 #74 - '4E' // 54d8 #108 - '7Z' // 54d9 #207 - '30Y' // 54da #804 - 'A' // 54db - 'a7Z' // 54dc-54dd #207 - '30Y' // 54de #804 - '2K' // 54df #62 - '4E' // 54e0 #108 - '243V' // 54e1 #6339 - '20Y' // 54e2 #544 - '30X' // 54e3 #803 - '26Q' // 54e4 #692 - '214T' // 54e5 #5583 - '194B' // 54e6 #5045 - '30Y' // 54e7 #804 - '129K' // 54e8 #3364 - '172D' // 54e9 #4475 - '217Z' // 54ea #5667 - '26Q' // 54eb #692 - '4E' // 54ec #108 - '193Y' // 54ed #5042 - '63J' // 54ee #1647 - '61B' // 54ef #1587 - 'A' // 54f0 - '4E' // 54f1 #108 - '189U' // 54f2 #4934 - '20Y' // 54f3 #544 - '7Z' // 54f4 #207 - 'A' // 54f5 - '4E' // 54f6 #108 - 'A' // 54f7 - 'a7Z' // 54f8-54f9 #207 - '152P' // 54fa #3967 - 'A' // 54fb - '64A' // 54fc #1664 - '20Y' // 54fd #544 - '31Q' // 54fe #822 - '20Y' // 54ff #544 - '4E' // 5500 #108 - '20Y' // 5501 #544 - '26Q' // 5502 #692 - 'A' // 5503 - '112S' // 5504 #2930 - '20Y' // 5505 #544 - '112O' // 5506 #2926 - '174A' // 5507 #4524 - '4E' // 5508 #108 - '147G' // 5509 #3828 - '26Q' // 550a #692 - 'A' // 550b - '20Y' // 550c #544 - '61E' // 550d #1590 - '112M' // 550e #2924 - '17D' // 550f #445 - '189N' // 5510 #4927 - 'b20Z' // 5511-5513 #545 - '186U' // 5514 #4856 - '4E' // 5515 #108 - '31P' // 5516 #821 - '20Z' // 5517 #545 - '30X' // 5518 #803 - '10N' // 5519 #273 - '26Q' // 551a #692 - '10N' // 551b #273 - '261A' // 551c #6786 - '10N' // 551d #273 - '20Z' // 551e #545 - 'A' // 551f - '10N' // 5520 #273 - 'A' // 5521 - '10N' // 5522 #273 - '61F' // 5523 #1591 - '2L' // 5524 #63 - '30X' // 5525 #803 - '20Z' // 5526 #545 - '17D' // 5527 #445 - '30X' // 5528 #803 - 'A' // 5529 - '17D' // 552a #445 - '61D' // 552b #1589 - '20Z' // 552c #545 - '26Q' // 552d #692 - '225Y' // 552e #5874 - '203K' // 552f #5288 - '20Z' // 5530 #545 - '202S' // 5531 #5270 - 'a17D' // 5532-5533 #445 - '10N' // 5534 #273 - '47Y' // 5535 #1246 - '40E' // 5536 #1044 - '191V' // 5537 #4987 - '152F' // 5538 #3957 - '4E' // 5539 #108 - 'A' // 553a - 'a17D' // 553b-553c #445 - '31P' // 553d #821 - '112R' // 553e #2929 - '20Z' // 553f #545 - '31P' // 5540 #821 - '17D' // 5541 #445 - 'A' // 5542 - '63M' // 5543 #1650 - '47Y' // 5544 #1246 - '40E' // 5545 #1044 - '243K' // 5546 #6328 - '17D' // 5547 #445 - '10N' // 5548 #273 - '17D' // 5549 #445 - '208A' // 554a #5408 - '20Z' // 554b #545 - '4E' // 554c #108 - '17D' // 554d #445 - '26Q' // 554e #692 - '245E' // 554f #6374 - '17D' // 5550 #445 - '40E' // 5551 #1044 - 'A' // 5552 - '112V' // 5553 #2933 - 'A' // 5554 - '20Z' // 5555 #545 - '47Y' // 5556 #1246 - '17D' // 5557 #445 - '31P' // 5558 #821 - 'A' // 5559 - '4E' // 555a #108 - '31P' // 555b #821 - '8H' // 555c #215 - '61D' // 555d #1589 - '134P' // 555e #3499 - '224H' // 555f #5831 - '4E' // 5560 #108 - '207Y' // 5561 #5406 - '35K' // 5562 #920 - '112N' // 5563 #2925 - '158Z' // 5564 #4133 - '66P' // 5565 #1731 - '68D' // 5566 #1771 - 'a10N' // 5567-5568 #273 - '10Y' // 5569 #284 - '137S' // 556a #3580 - '10Y' // 556b #284 - 'd10N' // 556c-5570 #273 - '131K' // 5571 #3416 - '162N' // 5572 #4225 - '30X' // 5573 #803 - '10N' // 5574 #273 - 'b10Y' // 5575-5577 #284 - '3G' // 5578 #84 - '35K' // 5579 #920 - 'A' // 557a - '8H' // 557b #215 - '23R' // 557c #615 - '4E' // 557d #108 - '63Z' // 557e #1663 - '8H' // 557f #215 - '134Q' // 5580 #3500 - '8H' // 5581 #215 - '140Y' // 5582 #3664 - '8H' // 5583 #215 - '221C' // 5584 #5748 - '10N' // 5585 #273 - '23R' // 5586 #615 - '179K' // 5587 #4664 - '8H' // 5588 #215 - '167E' // 5589 #4346 - '186T' // 558a #4855 - '112T' // 558b #2931 - '10N' // 558c #273 - 'A' // 558d - '63Z' // 558e #1663 - '8H' // 558f #215 - '61F' // 5590 #1591 - '8H' // 5591 #215 - '4E' // 5592 #108 - '31P' // 5593 #821 - '207X' // 5594 #5405 - 'aA' // 5595-5596 - '4E' // 5597 #108 - '148P' // 5598 #3863 - '23R' // 5599 #615 - '175R' // 559a #4567 - 'A' // 559b - '234Q' // 559c #6100 - '208O' // 559d #5422 - '4E' // 559e #108 - '8H' // 559f #215 - 'A' // 55a0 - '35K' // 55a1 #920 - 'A' // 55a2 - '35J' // 55a3 #919 - '4E' // 55a4 #108 - 'a10Y' // 55a5-55a6 #284 - '130S' // 55a7 #3398 - '8H' // 55a8 #215 - '23R' // 55a9 #615 - '174F' // 55aa #4529 - '112W' // 55ab #2934 - '186S' // 55ac #4854 - '35J' // 55ad #919 - '240D' // 55ae #6243 - 'A' // 55af - '112U' // 55b0 #2932 - '10Y' // 55b1 #284 - '140W' // 55b2 #3662 - '10Y' // 55b3 #284 - '30X' // 55b4 #803 - '184J' // 55b5 #4793 - '259Y' // 55b6 #6758 - '3N' // 55b7 #91 - 'A' // 55b8 - '10Y' // 55b9 #284 - '143T' // 55ba #3737 - '65X' // 55bb #1713 - '10Y' // 55bc #284 - 'a10N' // 55bd-55be #273 - '4E' // 55bf #108 - 'A' // 55c0 - '61B' // 55c1 #1587 - 'A' // 55c2 - '4E' // 55c3 #108 - '8H' // 55c4 #215 - '112P' // 55c5 #2927 - '158X' // 55c6 #4131 - '23R' // 55c7 #615 - 'A' // 55c8 - '8H' // 55c9 #215 - 'A' // 55ca - '4E' // 55cb #108 - '8H' // 55cc #215 - '10Y' // 55cd #284 - '225X' // 55ce #5873 - '10N' // 55cf #273 - '10Y' // 55d0 #284 - '124E' // 55d1 #3228 - '8H' // 55d2 #215 - '124G' // 55d3 #3230 - '23R' // 55d4 #615 - 'a10Y' // 55d5-55d6 #284 - 'a8H' // 55d7-55d8 #215 - '10Y' // 55d9 #284 - '140U' // 55da #3660 - '8H' // 55db #215 - '142D' // 55dc #3695 - 'a8H' // 55dd-55de #215 - '23R' // 55df #615 - 'A' // 55e0 - '10Y' // 55e1 #284 - '35J' // 55e2 #919 - '112Q' // 55e3 #2928 - '23R' // 55e4 #615 - 'a10Y' // 55e5-55e6 #284 - 'A' // 55e7 - '65X' // 55e8 #1713 - '35J' // 55e9 #919 - '10Y' // 55ea #284 - '10N' // 55eb #273 - '8H' // 55ec #215 - '10N' // 55ed #273 - '8H' // 55ee #215 - '162P' // 55ef #4227 - '63M' // 55f0 #1650 - '8H' // 55f1 #215 - '10Y' // 55f2 #284 - '10N' // 55f3 #273 - 'A' // 55f4 - '47Z' // 55f5 #1247 - 'a61C' // 55f6-55f7 #1588 - '4E' // 55f8 #108 - '35J' // 55f9 #919 - '35K' // 55fa #920 - '112X' // 55fb #2935 - 'A' // 55fc - '63J' // 55fd #1647 - '112L' // 55fe #2923 - '4E' // 55ff #108 - 'a47Z' // 5600-5601 #1247 - '35K' // 5602 #920 - 'aA' // 5603-5604 - '165Q' // 5605 #4306 - '173P' // 5606 #4513 - '4E' // 5607 #108 - '61C' // 5608 #1588 - '218Q' // 5609 #5684 - '4E' // 560a #108 - 'A' // 560b - '47Z' // 560c #1247 - '124D' // 560d #3227 - '134S' // 560e #3502 - '112C' // 560f #2914 - '4E' // 5610 #108 - '112B' // 5611 #2913 - '112D' // 5612 #2915 - '35I' // 5613 #918 - '136J' // 5614 #3545 - '35I' // 5615 #918 - '13E' // 5616 #342 - '186R' // 5617 #4853 - '255L' // 5618 #6641 - '4B' // 5619 #105 - '12S' // 561a #330 - '179J' // 561b #4663 - '23Q' // 561c #614 - 'A' // 561d - '23Q' // 561e #614 - '137R' // 561f #3579 - '35H' // 5620 #917 - '112J' // 5621 #2921 - '149S' // 5622 #3892 - '23Q' // 5623 #614 - '12S' // 5624 #330 - '23Q' // 5625 #614 - 'A' // 5626 - '23Q' // 5627 #614 - '4B' // 5628 #105 - '125M' // 5629 #3262 - '35I' // 562a #918 - '12S' // 562b #330 - '13E' // 562c #342 - 'a23Q' // 562d-562e #614 - '64I' // 562f #1672 - '13E' // 5630 #342 - '250O' // 5631 #6514 - '147C' // 5632 #3824 - '4B' // 5633 #105 - '200U' // 5634 #5220 - '35H' // 5635 #917 - '61A' // 5636 #1586 - '23P' // 5637 #613 - 'a13E' // 5638-5639 #342 - '23Q' // 563a #614 - '158V' // 563b #4129 - '27C' // 563c #704 - '4B' // 563d #105 - 'A' // 563e - '147D' // 563f #3825 - '13E' // 5640 #342 - '49A' // 5641 #1274 - '112G' // 5642 #2918 - '35H' // 5643 #917 - '4B' // 5644 #105 - 'A' // 5645 - 'a4B' // 5646-5647 #105 - 'A' // 5648 - '13E' // 5649 #342 - 'A' // 564a - '4B' // 564b #105 - '112F' // 564c #2917 - 'c13E' // 564d-5650 #342 - 'A' // 5651 - '35G' // 5652 #916 - '158T' // 5653 #4127 - '13E' // 5654 #342 - '12S' // 5655 #330 - 'A' // 5656 - '162M' // 5657 #4224 - 'b23Q' // 5658-565a #614 - '254J' // 565b #6613 - '3W' // 565c #100 - '35I' // 565d #918 - '4B' // 565e #105 - 'A' // 565f - '124A' // 5660 #3224 - '23P' // 5661 #613 - '140T' // 5662 #3659 - '27C' // 5663 #704 - '13E' // 5664 #342 - '35I' // 5665 #918 - '13E' // 5666 #342 - 'A' // 5667 - '236V' // 5668 #6157 - '111Y' // 5669 #2910 - '147E' // 566a #3826 - '61A' // 566b #1586 - '134M' // 566c #3496 - '35H' // 566d #917 - 'A' // 566e - '13E' // 566f #342 - '12S' // 5670 #330 - '13E' // 5671 #342 - '35H' // 5672 #917 - '12S' // 5673 #330 - '195U' // 5674 #5090 - '27C' // 5675 #704 - '13E' // 5676 #342 - '12S' // 5677 #330 - '64V' // 5678 #1685 - '137O' // 5679 #3576 - '13E' // 567a #342 - 'a14M' // 567b-567c #376 - 'A' // 567d - '12S' // 567e #330 - 'A' // 567f - '17C' // 5680 #444 - 'bA' // 5681-5683 - 'a10B' // 5684-5685 #261 - '40C' // 5686 #1042 - '193X' // 5687 #5041 - '4B' // 5688 #105 - '35G' // 5689 #916 - 'a23P' // 568a-568b #613 - '10B' // 568c #261 - 'A' // 568d - '14M' // 568e #376 - '10B' // 568f #261 - '169Q' // 5690 #4410 - '12S' // 5691 #330 - 'a14M' // 5692-5693 #376 - '4B' // 5694 #105 - '49A' // 5695 #1274 - 'A' // 5696 - '14M' // 5697 #376 - '40D' // 5698 #1043 - '17C' // 5699 #444 - '4B' // 569a #105 - '12S' // 569b #330 - '14M' // 569c #376 - '4B' // 569d #105 - '10B' // 569e #261 - '49A' // 569f #1274 - '4B' // 56a0 #105 - '14M' // 56a1 #376 - '251Y' // 56a2 #6550 - '2K' // 56a3 #62 - '14M' // 56a4 #376 - '40C' // 56a5 #1042 - 'a10B' // 56a6-56a7 #261 - '129J' // 56a8 #3363 - '27C' // 56a9 #704 - 'A' // 56aa - '17C' // 56ab #444 - '40C' // 56ac #1042 - '10B' // 56ad #261 - '147B' // 56ae #3823 - '14M' // 56af #376 - 'A' // 56b0 - '111X' // 56b1 #2909 - '4B' // 56b2 #105 - '17C' // 56b3 #444 - '207V' // 56b4 #5403 - '40D' // 56b5 #1043 - '10B' // 56b6 #261 - '158U' // 56b7 #4128 - '12S' // 56b8 #330 - '35G' // 56b9 #916 - 'aA' // 56ba-56bb - '134L' // 56bc #3495 - 'A' // 56bd - '4B' // 56be #105 - '14M' // 56bf #376 - '17C' // 56c0 #444 - '112E' // 56c1 #2916 - '64V' // 56c2 #1685 - '10B' // 56c3 #261 - 'A' // 56c4 - '17C' // 56c5 #444 - 'aA' // 56c6-56c7 - '17C' // 56c8 #444 - '200V' // 56c9 #5221 - '172B' // 56ca #4473 - 'a17C' // 56cb-56cc #444 - '64I' // 56cd #1672 - 'b4B' // 56ce-56d0 #105 - '40C' // 56d1 #1042 - 'A' // 56d2 - '17C' // 56d3 #444 - '14M' // 56d4 #376 - 'A' // 56d5 - '14M' // 56d6 #376 - '10B' // 56d7 #261 - 'a27C' // 56d8-56d9 #704 - '148D' // 56da #3851 - '68Q' // 56db #1784 - '4B' // 56dc #105 - '10B' // 56dd #261 - '244J' // 56de #6353 - '10B' // 56df #261 - '234G' // 56e0 #6090 - '10B' // 56e1 #261 - '112K' // 56e2 #2922 - '258S' // 56e3 #6726 - 'a10B' // 56e4-56e5 #261 - '27C' // 56e6 #704 - '134N' // 56e7 #3497 - '27C' // 56e8 #704 - '12S' // 56e9 #330 - '14M' // 56ea #376 - '10B' // 56eb #261 - '12S' // 56ec #330 - '134O' // 56ed #3498 - '10B' // 56ee #261 - '14M' // 56ef #376 - '67T' // 56f0 #1761 - '10B' // 56f1 #261 - '258I' // 56f2 #6716 - '259G' // 56f3 #6740 - '2D' // 56f4 #55 - '12S' // 56f5 #330 - '4B' // 56f6 #105 - '10B' // 56f7 #261 - '12S' // 56f8 #330 - '111W' // 56f9 #2908 - '212U' // 56fa #5532 - '35G' // 56fb #916 - '7I' // 56fc #190 - '191U' // 56fd #4986 - '7K' // 56fe #192 - 'a20X' // 56ff-5700 #543 - 'a4B' // 5701-5702 #105 - 'a47X' // 5703-5704 #1245 - 'A' // 5705 - '3I' // 5706 #86 - '17C' // 5707 #444 - '214S' // 5708 #5582 - 'a20X' // 5709-570a #543 - '246E' // 570b #6400 - '20X' // 570c #543 - '217Y' // 570d #5666 - 'A' // 570e - '255Y' // 570f #6654 - 'A' // 5710 - '4B' // 5711 #105 - '236U' // 5712 #6156 - '207U' // 5713 #5402 - 'A' // 5714 - '23P' // 5715 #613 - '240C' // 5716 #6242 - 'A' // 5717 - '232U' // 5718 #6052 - '7I' // 5719 #190 - 'a4B' // 571a-571b #105 - '20X' // 571c #543 - '23P' // 571d #613 - '7I' // 571e #190 - '223V' // 571f #5819 - 'b4B' // 5720-5722 #105 - '112A' // 5723 #2912 - '4B' // 5724 #105 - '24A' // 5725 #624 - '4B' // 5726 #105 - '257T' // 5727 #6701 - '246M' // 5728 #6408 - 'a20X' // 5729-572a #543 - 'A' // 572b - '20X' // 572c #543 - '136U' // 572d #3556 - 'a20X' // 572e-572f #543 - '246U' // 5730 #6416 - 'A' // 5731 - '35G' // 5732 #916 - '165O' // 5733 #4304 - '20X' // 5734 #543 - '7I' // 5735 #190 - 'A' // 5736 - 'a4B' // 5737-5738 #105 - '7I' // 5739 #190 - '3Q' // 573a #94 - '47X' // 573b #1245 - 'A' // 573c - '23P' // 573d #613 - '67A' // 573e #1742 - '23P' // 573f #613 - '226G' // 5740 #5882 - '112I' // 5741 #2920 - '143N' // 5742 #3731 - '112H' // 5743 #2919 - 'A' // 5744 - '24A' // 5745 #624 - '23P' // 5746 #613 - '220L' // 5747 #5731 - 'A' // 5748 - '7I' // 5749 #190 - '203A' // 574a #5278 - '40D' // 574b #1043 - '20X' // 574c #543 - '47X' // 574d #1245 - '147A' // 574e #3822 - '111Z' // 574f #2911 - '208M' // 5750 #5420 - '179I' // 5751 #4662 - '17C' // 5752 #444 - 'A' // 5753 - '40D' // 5754 #1043 - 'aA' // 5755-5756 - '155S' // 5757 #4048 - 'A' // 5758 - '4B' // 5759 #105 - '3I' // 575a #86 - '111V' // 575b #2907 - '7I' // 575c #190 - 'a2K' // 575d-575e #62 - '111L' // 575f #2897 - '2Y' // 5760 #76 - '200T' // 5761 #5219 - '4B' // 5762 #105 - '7I' // 5763 #190 - '158S' // 5764 #4126 - '4B' // 5765 #105 - '187G' // 5766 #4868 - '47U' // 5767 #1242 - 'a15T' // 5768-5769 #409 - '181C' // 576a #4708 - '15T' // 576b #409 - 'A' // 576c - '15T' // 576d #409 - '250N' // 576e #6513 - '111K' // 576f #2896 - '30V' // 5770 #801 - '4B' // 5771 #105 - '47W' // 5772 #1244 - '15T' // 5773 #409 - '111M' // 5774 #2898 - '30V' // 5775 #801 - 'A' // 5776 - '15T' // 5777 #409 - 'A' // 5778 - '4B' // 5779 #105 - '47U' // 577a #1242 - '15T' // 577b #409 - '30V' // 577c #801 - '47W' // 577d #1244 - 'a47U' // 577e-577f #1242 - '47W' // 5780 #1244 - '4B' // 5781 #105 - '168N' // 5782 #4381 - '67A' // 5783 #1742 - '2W' // 5784 #74 - 'a7I' // 5785-5786 #190 - 'A' // 5787 - '30V' // 5788 #801 - '4B' // 5789 #105 - '23O' // 578a #612 - '237L' // 578b #6173 - '15T' // 578c #409 - '23O' // 578d #612 - 'a7I' // 578e-578f #190 - '23O' // 5790 #612 - 'A' // 5791 - '3X' // 5792 #101 - '30V' // 5793 #801 - '24A' // 5794 #624 - '15T' // 5795 #409 - 'A' // 5796 - '4B' // 5797 #105 - 'A' // 5798 - '24A' // 5799 #624 - '15T' // 579a #409 - '111T' // 579b #2905 - '47V' // 579c #1243 - 'a24A' // 579d-579e #624 - '15T' // 579f #409 - '30V' // 57a0 #801 - '15T' // 57a1 #409 - '130W' // 57a2 #3402 - '130Z' // 57a3 #3405 - '15T' // 57a4 #409 - 'A' // 57a5 - '7I' // 57a6 #190 - '47V' // 57a7 #1243 - 'a24A' // 57a8-57a9 #624 - '47V' // 57aa #1243 - '4C' // 57ab #106 - '24A' // 57ac #624 - '7I' // 57ad #190 - '124B' // 57ae #3225 - '7I' // 57af #190 - '24A' // 57b0 #624 - 'a7I' // 57b1-57b2 #190 - '35F' // 57b3 #915 - '40B' // 57b4 #1041 - '20W' // 57b5 #542 - '7I' // 57b6 #190 - 'A' // 57b7 - '10A' // 57b8 #260 - '20W' // 57b9 #542 - 'a30W' // 57ba-57bb #802 - 'A' // 57bc - '36I' // 57bd #944 - '40B' // 57be #1041 - '7I' // 57bf #190 - '2P' // 57c0 #67 - 'A' // 57c1 - '20W' // 57c2 #542 - '165M' // 57c3 #4302 - '40B' // 57c4 #1041 - '7I' // 57c5 #190 - '10A' // 57c6 #260 - 'a39Z' // 57c7-57c8 #1039 - 'aA' // 57c9-57ca - '182T' // 57cb #4751 - '10A' // 57cc #260 - '7I' // 57cd #190 - '230G' // 57ce #5986 - '10A' // 57cf #260 - 'aA' // 57d0-57d1 - '10A' // 57d2 #260 - '2P' // 57d3 #67 - '186Q' // 57d4 #4852 - '140R' // 57d5 #3657 - '36I' // 57d6 #944 - '134K' // 57d7 #3494 - 'c7I' // 57d8-57db #190 - 'b10A' // 57dc-57de #260 - '213R' // 57df #5555 - '123Y' // 57e0 #3222 - '10A' // 57e1 #260 - '20W' // 57e2 #542 - '2P' // 57e3 #67 - '123Z' // 57e4 #3223 - '20W' // 57e5 #542 - '35F' // 57e6 #915 - '10A' // 57e7 #260 - 'A' // 57e8 - '2P' // 57e9 #67 - 'A' // 57ea - '7I' // 57eb #190 - 'A' // 57ec - '10A' // 57ed #260 - '30W' // 57ee #802 - '40B' // 57ef #1041 - '30U' // 57f0 #800 - 'aA' // 57f1-57f2 - '30W' // 57f3 #802 - '39Z' // 57f4 #1039 - 'a10A' // 57f5-57f6 #260 - '204K' // 57f7 #5314 - '10A' // 57f8 #260 - '202B' // 57f9 #5253 - '237K' // 57fa #6172 - '26P' // 57fb #691 - '111S' // 57fc #2904 - '10A' // 57fd #260 - '35F' // 57fe #915 - '2P' // 57ff #67 - '111P' // 5800 #2901 - '20W' // 5801 #542 - '221R' // 5802 #5763 - '10A' // 5803 #260 - '26P' // 5804 #691 - '202A' // 5805 #5252 - '187F' // 5806 #4867 - '20W' // 5807 #542 - '36H' // 5808 #943 - '39Z' // 5809 #1039 - '30U' // 580a #800 - '10A' // 580b #260 - '36I' // 580c #944 - '10A' // 580d #260 - '20W' // 580e #542 - 'A' // 580f - '30W' // 5810 #802 - '7I' // 5811 #190 - '23O' // 5812 #612 - 'A' // 5813 - '20W' // 5814 #542 - '253Z' // 5815 #6603 - 'aA' // 5816-5817 - '23O' // 5818 #612 - '10A' // 5819 #260 - 'A' // 581a - '36I' // 581b #944 - '7I' // 581c #190 - '10A' // 581d #260 - '39Z' // 581e #1039 - '36I' // 581f #944 - '9Z' // 5820 #259 - '186P' // 5821 #4851 - '40A' // 5822 #1040 - '17B' // 5823 #443 - '160M' // 5824 #4172 - '10M' // 5825 #272 - '9Z' // 5826 #259 - '36H' // 5827 #943 - 'aA' // 5828-5829 - '175Q' // 582a #4566 - 'A' // 582b - '17B' // 582c #443 - '26P' // 582d #691 - 'A' // 582e - '134J' // 582f #3493 - '111J' // 5830 #2895 - '245D' // 5831 #6373 - '2P' // 5832 #67 - 'A' // 5833 - '245F' // 5834 #6375 - '65J' // 5835 #1699 - 'bA' // 5836-5838 - '2P' // 5839 #67 - '111N' // 583a #2899 - 'aA' // 583b-583c - '12L' // 583d #323 - 'A' // 583e - 'a9Z' // 583f-5840 #259 - '252Z' // 5841 #6577 - '10M' // 5842 #272 - 'A' // 5843 - '40A' // 5844 #1040 - 'a10M' // 5845-5846 #272 - '23O' // 5847 #612 - '17B' // 5848 #443 - '2P' // 5849 #67 - '208T' // 584a #5427 - '30U' // 584b #800 - '146Z' // 584c #3821 - '9Z' // 584d #259 - 'A' // 584e - '30U' // 584f #800 - '2P' // 5850 #67 - '200S' // 5851 #5218 - '9Z' // 5852 #259 - 'A' // 5853 - '202I' // 5854 #5260 - '2P' // 5855 #67 - 'A' // 5856 - '190U' // 5857 #4960 - '165L' // 5858 #4301 - '9Z' // 5859 #259 - '111R' // 585a #2903 - 'A' // 585b - '17B' // 585c #443 - '10M' // 585d #272 - '195H' // 585e #5077 - '35F' // 585f #915 - 'A' // 5860 - '36H' // 5861 #943 - '140O' // 5862 #3654 - 'A' // 5863 - '30U' // 5864 #800 - 'aA' // 5865-5866 - '2P' // 5867 #67 - '9Z' // 5868 #259 - '111Q' // 5869 #2902 - 'A' // 586a - '201F' // 586b #5231 - '17B' // 586c #443 - '9Z' // 586d #259 - 'A' // 586e - '10M' // 586f #272 - '12L' // 5870 #323 - '17B' // 5871 #443 - '9Z' // 5872 #259 - '40A' // 5873 #1040 - 'A' // 5874 - '187Y' // 5875 #4886 - 'aA' // 5876-5877 - '2P' // 5878 #67 - '39Y' // 5879 #1038 - 'aA' // 587a-587b - '30U' // 587c #800 - '17B' // 587d #443 - '111O' // 587e #2900 - '26P' // 587f #691 - 'a9Z' // 5880-5881 #259 - 'A' // 5882 - '229V' // 5883 #5975 - 'A' // 5884 - '65J' // 5885 #1699 - 'A' // 5886 - '26P' // 5887 #691 - '9Z' // 5888 #259 - '39Y' // 5889 #1038 - '67J' // 588a #1751 - 'a2P' // 588b-588c #67 - '12L' // 588d #323 - '30W' // 588e #802 - '2P' // 588f #67 - '26P' // 5890 #691 - '30W' // 5891 #802 - '17B' // 5892 #443 - '167R' // 5893 #4359 - '2P' // 5894 #67 - 'A' // 5895 - '9Z' // 5896 #259 - '258M' // 5897 #6720 - '9Z' // 5898 #259 - '111U' // 5899 #2906 - '40A' // 589a #1040 - 'A' // 589b - '166M' // 589c #4328 - '9Z' // 589d #259 - '225W' // 589e #5872 - '135L' // 589f #3521 - '12L' // 58a0 #323 - '9Z' // 58a1 #259 - '2P' // 58a2 #67 - '17B' // 58a3 #443 - 'aA' // 58a4-58a5 - '9Z' // 58a6 #259 - '23O' // 58a7 #612 - '196B' // 58a8 #5097 - '39Y' // 58a9 #1038 - '35F' // 58aa #915 - '12L' // 58ab #323 - '17B' // 58ac #443 - '10M' // 58ad #272 - '146Y' // 58ae #3820 - 'A' // 58af - '17B' // 58b0 #443 - '26P' // 58b1 #691 - '2P' // 58b2 #67 - '129H' // 58b3 #3361 - 'A' // 58b4 - 'a23O' // 58b5-58b6 #612 - 'A' // 58b7 - 'a2P' // 58b8-58b9 #67 - '36H' // 58ba #943 - '39Y' // 58bb #1038 - '9Z' // 58bc #259 - '10M' // 58bd #272 - '152E' // 58be #3956 - '10M' // 58bf #272 - 'A' // 58c0 - '198O' // 58c1 #5162 - '30T' // 58c2 #799 - '12L' // 58c3 #323 - '2P' // 58c4 #67 - '26O' // 58c5 #690 - '60Z' // 58c6 #1585 - '209J' // 58c7 #5443 - '12L' // 58c8 #323 - 'A' // 58c9 - '256Z' // 58ca #6681 - '23M' // 58cb #610 - '252K' // 58cc #6562 - '2P' // 58cd #67 - '26O' // 58ce #690 - 'A' // 58cf - '39X' // 58d0 #1037 - '26O' // 58d1 #690 - '12L' // 58d2 #323 - '217V' // 58d3 #5663 - '2P' // 58d4 #67 - '26O' // 58d5 #690 - 'a2P' // 58d6-58d7 #67 - '158Q' // 58d8 #4124 - '26O' // 58d9 #690 - '39X' // 58da #1037 - 'A' // 58db - '60V' // 58dc #1581 - '2P' // 58dd #67 - '67W' // 58de #1764 - '26O' // 58df #690 - '39X' // 58e0 #1037 - '2P' // 58e1 #67 - '165N' // 58e2 #4303 - 'A' // 58e3 - '140P' // 58e4 #3655 - '2P' // 58e5 #67 - 'bA' // 58e6-58e8 - '30T' // 58e9 #799 - 'A' // 58ea - '236D' // 58eb #6139 - '26O' // 58ec #690 - 'A' // 58ed - '254L' // 58ee #6615 - '179G' // 58ef #4660 - '143P' // 58f0 #3733 - '253S' // 58f1 #6596 - '111H' // 58f2 #2893 - '110X' // 58f3 #2883 - '2P' // 58f4 #67 - '10M' // 58f5 #272 - '2R' // 58f6 #69 - '12L' // 58f7 #323 - '10M' // 58f8 #272 - '186O' // 58f9 #4850 - '173G' // 58fa #4504 - '110U' // 58fb #2880 - '39X' // 58fc #1037 - '67N' // 58fd #1755 - 'aA' // 58fe-58ff - '10M' // 5900 #272 - 'A' // 5901 - '30T' // 5902 #799 - 'A' // 5903 - '63N' // 5904 #1651 - '60V' // 5905 #1581 - '30T' // 5906 #799 - '143X' // 5907 #3741 - '10M' // 5908 #272 - '260E' // 5909 #6764 - '30T' // 590a #799 - '12L' // 590b #323 - '30T' // 590c #799 - '140S' // 590d #3658 - '60Z' // 590e #1585 - '216K' // 590f #5626 - '12L' // 5910 #323 - '19B' // 5911 #495 - 'a2P' // 5912-5913 #67 - '39W' // 5914 #1036 - '176L' // 5915 #4587 - '69B' // 5916 #1795 - 'A' // 5917 - '2P' // 5918 #67 - '39W' // 5919 #1036 - '69H' // 591a #1801 - '12L' // 591b #323 - '223F' // 591c #5803 - '110V' // 591d #2881 - '10M' // 591e #272 - '110Y' // 591f #2884 - '224G' // 5920 #5830 - '2P' // 5921 #67 - '221Q' // 5922 #5762 - '12L' // 5923 #323 - '30S' // 5924 #798 - '67J' // 5925 #1751 - 'A' // 5926 - '247C' // 5927 #6424 - '12L' // 5928 #323 - '243S' // 5929 #6336 - '236C' // 592a #6138 - '216P' // 592b #5631 - '30S' // 592c #798 - '123X' // 592d #3221 - '205X' // 592e #5353 - '179H' // 592f #4661 - '2P' // 5930 #67 - '229F' // 5931 #5959 - '30S' // 5932 #798 - '2P' // 5933 #67 - '64Z' // 5934 #1689 - '2P' // 5935 #67 - '12L' // 5936 #323 - '152O' // 5937 #3966 - '110W' // 5938 #2882 - '250M' // 5939 #6512 - '1R' // 593a #43 - 'A' // 593b - '10M' // 593c #272 - '2P' // 593d #67 - '67W' // 593e #1764 - '2P' // 593f #67 - '111I' // 5940 #2894 - 'a10M' // 5941-5942 #272 - '2P' // 5943 #67 - '110Z' // 5944 #2885 - 'A' // 5945 - '2P' // 5946 #67 - '227M' // 5947 #5914 - '199F' // 5948 #5179 - '174Z' // 5949 #4549 - 'A' // 594a - '1R' // 594b #43 - 'a10M' // 594c-594d #272 - '110T' // 594e #2879 - '182X' // 594f #4755 - '39W' // 5950 #1036 - '183Q' // 5951 #4774 - '2P' // 5952 #67 - '30S' // 5953 #798 - '179Z' // 5954 #4679 - '158R' // 5955 #4125 - '2D' // 5956 #55 - '217W' // 5957 #5664 - '30S' // 5958 #798 - '2P' // 5959 #67 - '39W' // 595a #1036 - '2P' // 595b #67 - 'A' // 595c - 'b2P' // 595d-595f #67 - '129I' // 5960 #3362 - '30S' // 5961 #798 - '173H' // 5962 #4505 - '2P' // 5963 #67 - 'A' // 5964 - '126B' // 5965 #3277 - '10M' // 5966 #272 - '67N' // 5967 #1755 - '256K' // 5968 #6666 - '60X' // 5969 #1583 - '189T' // 596a #4933 - '36H' // 596b #943 - 'a110S' // 596c-596d #2878 - '182S' // 596e #4750 - '2P' // 596f #67 - 'aA' // 5970-5971 - '2P' // 5972 #67 - '244C' // 5973 #6346 - '169B' // 5974 #4395 - '60X' // 5975 #1583 - '207T' // 5976 #5401 - '9Y' // 5977 #258 - '140Q' // 5978 #3656 - '217X' // 5979 #5665 - 'A' // 597a - 'a60W' // 597b-597c #1582 - '243P' // 597d #6333 - '19B' // 597e #495 - 'A' // 597f - '19B' // 5980 #495 - '60W' // 5981 #1582 - '241A' // 5982 #6266 - '160P' // 5983 #4175 - '143E' // 5984 #3722 - 'A' // 5985 - '3Y' // 5986 #102 - 'a3I' // 5987-5988 #86 - '23M' // 5989 #610 - '111E' // 598a #2890 - 'a5S' // 598b-598c #148 - '158P' // 598d #4123 - '5S' // 598e #148 - '9Y' // 598f #258 - 'aA' // 5990-5991 - '129G' // 5992 #3360 - '129U' // 5993 #3374 - '23M' // 5994 #610 - '5S' // 5995 #148 - '175U' // 5996 #4570 - '110R' // 5997 #2877 - '9Y' // 5998 #258 - '198A' // 5999 #5148 - '60Y' // 599a #1584 - '5S' // 599b #148 - '7Y' // 599c #206 - '214R' // 599d #5581 - '169T' // 599e #4413 - '23N' // 599f #611 - 'a9Y' // 59a0-59a1 #258 - '19B' // 59a2 #495 - '8Y' // 59a3 #232 - '63Y' // 59a4 #1662 - '166Y' // 59a5 #4340 - '19B' // 59a6 #495 - '8Y' // 59a7 #232 - '160F' // 59a8 #4165 - '3W' // 59a9 #100 - 'a7Y' // 59aa-59ab #206 - '111C' // 59ac #2888 - '49V' // 59ad #1295 - '186N' // 59ae #4849 - '8Y' // 59af #232 - '39V' // 59b0 #1035 - '19B' // 59b1 #495 - '8Y' // 59b2 #232 - '193W' // 59b3 #5040 - 'A' // 59b4 - '7Y' // 59b5 #206 - '9Y' // 59b6 #258 - '39V' // 59b7 #1035 - '9Y' // 59b8 #258 - '215M' // 59b9 #5602 - '8Y' // 59ba #232 - '205I' // 59bb #5338 - '5S' // 59bc #148 - '7Y' // 59bd #206 - '26N' // 59be #689 - '23M' // 59bf #610 - '7Y' // 59c0 #206 - '8Y' // 59c1 #232 - 'A' // 59c2 - '26N' // 59c3 #689 - '47T' // 59c4 #1241 - 'A' // 59c5 - '186L' // 59c6 #4847 - '9Y' // 59c7 #258 - '8Y' // 59c8 #232 - '111G' // 59c9 #2892 - '186M' // 59ca #4848 - '231G' // 59cb #6012 - '7Y' // 59cc #206 - '8Y' // 59cd #232 - '19B' // 59ce #495 - '7Y' // 59cf #206 - '208J' // 59d0 #5417 - '180H' // 59d1 #4687 - '8Y' // 59d2 #232 - '201N' // 59d3 #5239 - '211I' // 59d4 #5494 - '7Y' // 59d5 #206 - '9Y' // 59d6 #258 - '3X' // 59d7 #101 - '9Y' // 59d8 #258 - '26N' // 59d9 #689 - '158O' // 59da #4122 - '7Y' // 59db #206 - '153I' // 59dc #3986 - '26N' // 59dd #689 - '8Y' // 59de #232 - '49V' // 59df #1295 - '9Y' // 59e0 #258 - 'A' // 59e1 - '7Y' // 59e2 #206 - 'b8Y' // 59e3-59e5 #232 - '161N' // 59e6 #4199 - '49V' // 59e7 #1295 - '152C' // 59e8 #3954 - '9Y' // 59e9 #258 - '111A' // 59ea #2886 - '111F' // 59eb #2891 - '165K' // 59ec #4300 - 'A' // 59ed - '26N' // 59ee #689 - '39V' // 59ef #1035 - '23M' // 59f0 #610 - '8Y' // 59f1 #232 - '23N' // 59f2 #611 - '9Y' // 59f3 #258 - '5S' // 59f4 #148 - '9Y' // 59f5 #258 - '23N' // 59f6 #611 - '8Y' // 59f7 #232 - '26N' // 59f8 #689 - '9Y' // 59f9 #258 - 'A' // 59fa - '172Q' // 59fb #4488 - '19B' // 59fc #495 - '9Y' // 59fd #258 - 'A' // 59fe - '191J' // 59ff #4975 - '23N' // 5a00 #611 - '214Z' // 5a01 #5589 - '23M' // 5a02 #610 - '66Z' // 5a03 #1741 - '250L' // 5a04 #6511 - '2K' // 5a05 #62 - '7Y' // 5a06 #206 - '4C' // 5a07 #106 - '7Y' // 5a08 #206 - '8Y' // 5a09 #232 - '7Y' // 5a0a #206 - '23M' // 5a0b #610 - '8Y' // 5a0c #232 - '47T' // 5a0d #1241 - '5S' // 5a0e #148 - 'aA' // 5a0f-5a10 - '26N' // 5a11 #689 - '47T' // 5a12 #1241 - '8Y' // 5a13 #232 - 'A' // 5a14 - '7Y' // 5a15 #206 - '9Y' // 5a16 #258 - '23N' // 5a17 #611 - '205T' // 5a18 #5349 - 'A' // 5a19 - '8Y' // 5a1a #232 - '225V' // 5a1b #5871 - '193V' // 5a1c #5039 - 'A' // 5a1d - '23N' // 5a1e #611 - '65I' // 5a1f #1698 - '111D' // 5a20 #2889 - '60Y' // 5a21 #1584 - '7Y' // 5a22 #206 - '8Y' // 5a23 #232 - '23N' // 5a24 #611 - '140N' // 5a25 #3653 - 'A' // 5a26 - '39V' // 5a27 #1035 - '5S' // 5a28 #148 - '111B' // 5a29 #2887 - '23N' // 5a2a #611 - '19B' // 5a2b #495 - '23M' // 5a2c #610 - '8Y' // 5a2d #232 - '9Y' // 5a2e #258 - '253E' // 5a2f #6582 - '5S' // 5a30 #148 - '1Z' // 5a31 #51 - '7Y' // 5a32 #206 - '19B' // 5a33 #495 - '2W' // 5a34 #74 - '5S' // 5a35 #148 - '158N' // 5a36 #4121 - 'A' // 5a37 - '26M' // 5a38 #688 - 'bA' // 5a39-5a3b - '110O' // 5a3c #2874 - '9G' // 5a3d #240 - 'a7Y' // 5a3e-5a3f #206 - '35E' // 5a40 #914 - '110I' // 5a41 #2868 - 'a26M' // 5a42-5a43 #688 - '35E' // 5a44 #914 - '110K' // 5a45 #2870 - '195D' // 5a46 #5073 - '35E' // 5a47 #914 - '110N' // 5a48 #2873 - '152B' // 5a49 #3953 - '26M' // 5a4a #688 - 'A' // 5a4b - '35E' // 5a4c #914 - '26M' // 5a4d #688 - 'A' // 5a4e - '7Y' // 5a4f #206 - '35E' // 5a50 #914 - '26M' // 5a51 #688 - 'A' // 5a52 - '26M' // 5a53 #688 - '9G' // 5a54 #240 - '63Y' // 5a55 #1662 - '26M' // 5a56 #688 - '30R' // 5a57 #797 - 'A' // 5a58 - '9G' // 5a59 #240 - '216J' // 5a5a #5625 - 'aA' // 5a5b-5a5c - '20V' // 5a5d #541 - '16Z' // 5a5e #441 - 'A' // 5a5f - '30R' // 5a60 #797 - '20V' // 5a61 #541 - '110H' // 5a62 #2867 - '35D' // 5a63 #913 - 'A' // 5a64 - '16Z' // 5a65 #441 - '212N' // 5a66 #5525 - '16Z' // 5a67 #441 - '9G' // 5a68 #240 - 'A' // 5a69 - '16Z' // 5a6a #441 - '9G' // 5a6b #240 - 'a16Z' // 5a6c-5a6d #441 - '9G' // 5a6e #240 - 'aA' // 5a6f-5a70 - '9G' // 5a71 #240 - 'a7Y' // 5a72-5a73 #206 - '3Y' // 5a74 #102 - 'a7Y' // 5a75-5a76 #206 - '172A' // 5a77 #4472 - 'A' // 5a78 - '110Q' // 5a79 #2876 - 'a16Z' // 5a7a-5a7b #441 - '30R' // 5a7c #797 - 'A' // 5a7d - '35D' // 5a7e #913 - '124S' // 5a7f #3242 - 'A' // 5a80 - 'b30R' // 5a81-5a83 #797 - '16Z' // 5a84 #441 - 'A' // 5a85 - '9G' // 5a86 #240 - 'A' // 5a87 - '9G' // 5a88 #240 - 'aA' // 5a89-5a8a - '5S' // 5a8b #148 - '30R' // 5a8c #797 - 'A' // 5a8d - '7Y' // 5a8e #206 - 'A' // 5a8f - '17A' // 5a90 #442 - '9G' // 5a91 #240 - '218P' // 5a92 #5683 - '16Z' // 5a93 #441 - 'aA' // 5a94-5a95 - '16Z' // 5a96 #441 - '30R' // 5a97 #797 - 'A' // 5a98 - '35D' // 5a99 #913 - '154G' // 5a9a #4010 - '155F' // 5a9b #4035 - '16Z' // 5a9c #441 - 'A' // 5a9d - '16Z' // 5a9e #441 - '17A' // 5a9f #442 - '4D' // 5aa0 #107 - '9G' // 5aa1 #240 - '5S' // 5aa2 #148 - '8T' // 5aa3 #227 - '60U' // 5aa4 #1580 - 'aA' // 5aa5-5aa6 - '17A' // 5aa7 #442 - 'aA' // 5aa8-5aa9 - '47S' // 5aaa #1240 - '20V' // 5aab #541 - '17A' // 5aac #442 - 'A' // 5aad - 'a9X' // 5aae-5aaf #257 - 'A' // 5ab0 - '4D' // 5ab1 #107 - '48Y' // 5ab2 #1272 - '152D' // 5ab3 #3955 - '9X' // 5ab4 #257 - '4D' // 5ab5 #107 - 'A' // 5ab6 - '8T' // 5ab7 #227 - '4D' // 5ab8 #107 - '8T' // 5ab9 #227 - '4D' // 5aba #107 - 'a17A' // 5abb-5abc #442 - '217T' // 5abd #5661 - 'a4D' // 5abe-5abf #107 - 'A' // 5ac0 - '183G' // 5ac1 #4764 - '65I' // 5ac2 #1698 - '9X' // 5ac3 #257 - '19A' // 5ac4 #494 - 'A' // 5ac5 - '4D' // 5ac6 #107 - '9X' // 5ac7 #257 - '4D' // 5ac8 #107 - '110P' // 5ac9 #2875 - '9X' // 5aca #257 - '4D' // 5acb #107 - '183S' // 5acc #4776 - '8T' // 5acd #227 - '9G' // 5ace #240 - '4D' // 5acf #107 - '27B' // 5ad0 #703 - '9X' // 5ad1 #257 - '8T' // 5ad2 #227 - '9G' // 5ad3 #240 - '8T' // 5ad4 #227 - '20V' // 5ad5 #541 - '110L' // 5ad6 #2871 - '17A' // 5ad7 #442 - 'a9X' // 5ad8-5ad9 #257 - '4D' // 5ada #107 - '20V' // 5adb #541 - '4D' // 5adc #107 - 'aA' // 5add-5ade - '8T' // 5adf #227 - '4D' // 5ae0 #107 - '19A' // 5ae1 #494 - '8T' // 5ae2 #227 - '110M' // 5ae3 #2872 - '47S' // 5ae4 #1240 - '17A' // 5ae5 #442 - '19A' // 5ae6 #494 - 'A' // 5ae7 - '9X' // 5ae8 #257 - '66Z' // 5ae9 #1741 - '4D' // 5aea #107 - '9X' // 5aeb #257 - 'a8T' // 5aec-5aed #227 - '17A' // 5aee #442 - 'A' // 5aef - '4D' // 5af0 #107 - '8T' // 5af1 #227 - '9X' // 5af2 #257 - '8T' // 5af3 #227 - 'A' // 5af4 - '4D' // 5af5 #107 - '5S' // 5af6 #148 - 'aA' // 5af7-5af8 - '8T' // 5af9 #227 - '4D' // 5afa #107 - '48Y' // 5afb #1272 - 'A' // 5afc - '27B' // 5afd #703 - '9G' // 5afe #240 - '20V' // 5aff #541 - '5S' // 5b00 #148 - '4D' // 5b01 #107 - 'bA' // 5b02-5b04 - '60U' // 5b05 #1580 - 'aA' // 5b06-5b07 - '4D' // 5b08 #107 - '137G' // 5b09 #3568 - 'A' // 5b0a - '19A' // 5b0b #494 - '172W' // 5b0c #4494 - '9G' // 5b0d #240 - 'bA' // 5b0e-5b10 - '9X' // 5b11 #257 - 'bA' // 5b12-5b14 - '9G' // 5b15 #240 - '19A' // 5b16 #494 - '4D' // 5b17 #107 - 'A' // 5b18 - '17A' // 5b19 #442 - 'A' // 5b1a - '4D' // 5b1b #107 - 'A' // 5b1c - '17A' // 5b1d #442 - 'A' // 5b1e - '9G' // 5b1f #240 - 'A' // 5b20 - '4D' // 5b21 #107 - '255K' // 5b22 #6640 - '20V' // 5b23 #541 - '149R' // 5b24 #3891 - '5S' // 5b25 #148 - 'A' // 5b26 - '8T' // 5b27 #227 - '9X' // 5b28 #257 - '8T' // 5b29 #227 - '19A' // 5b2a #494 - '9X' // 5b2b #257 - 'a4D' // 5b2c-5b2d #107 - '8T' // 5b2e #227 - 'A' // 5b2f - '200R' // 5b30 #5217 - 'A' // 5b31 - '4D' // 5b32 #107 - 'A' // 5b33 - '4D' // 5b34 #107 - 'A' // 5b35 - '27B' // 5b36 #703 - '8T' // 5b37 #227 - '4D' // 5b38 #107 - 'dA' // 5b39-5b3d - '4D' // 5b3e #107 - '9X' // 5b3f #257 - '19A' // 5b40 #494 - '35D' // 5b41 #913 - 'A' // 5b42 - '19A' // 5b43 #494 - '20V' // 5b44 #541 - '4D' // 5b45 #107 - '20V' // 5b46 #541 - 'bA' // 5b47-5b49 - '47S' // 5b4a #1240 - '4D' // 5b4b #107 - '17A' // 5b4c #442 - 'aA' // 5b4d-5b4e - '9G' // 5b4f #240 - '246V' // 5b50 #6417 - '19A' // 5b51 #494 - '27B' // 5b52 #703 - '9X' // 5b53 #257 - '194Z' // 5b54 #5069 - '201J' // 5b55 #5235 - '48Y' // 5b56 #1272 - '242B' // 5b57 #6293 - '230Q' // 5b58 #5996 - '3Y' // 5b59 #102 - '63I' // 5b5a #1646 - '4D' // 5b5b #107 - '110J' // 5b5c #2869 - '196H' // 5b5d #5103 - '5S' // 5b5e #148 - '171Z' // 5b5f #4471 - 'aA' // 5b60-5b61 - '9X' // 5b62 #257 - '222I' // 5b63 #5780 - '188T' // 5b64 #4907 - '4D' // 5b65 #107 - '169P' // 5b66 #4409 - '8T' // 5b67 #227 - '35D' // 5b68 #913 - '217S' // 5b69 #5660 - '8T' // 5b6a #227 - '196R' // 5b6b #5113 - 'a9X' // 5b6c-5b6d #257 - '4D' // 5b6e #107 - '5S' // 5b6f #148 - '19A' // 5b70 #494 - '60Q' // 5b71 #1576 - '110G' // 5b72 #2866 - '60R' // 5b73 #1577 - '35C' // 5b74 #912 - '109Q' // 5b75 #2850 - '47Q' // 5b76 #1238 - 'A' // 5b77 - '240N' // 5b78 #6253 - 'A' // 5b79 - '60Q' // 5b7a #1576 - '110C' // 5b7b #2862 - '109P' // 5b7c #2849 - '109T' // 5b7d #2853 - '5S' // 5b7e #148 - 'a60R' // 5b7f-5b80 #1577 - '109U' // 5b81 #2854 - '109V' // 5b82 #2855 - '217U' // 5b83 #5662 - '109S' // 5b84 #2852 - '213Q' // 5b85 #5554 - '5S' // 5b86 #148 - '205H' // 5b87 #5337 - '212T' // 5b88 #5531 - '244A' // 5b89 #6344 - '5S' // 5b8a #148 - '179F' // 5b8b #4659 - '242Q' // 5b8c #6308 - '27B' // 5b8d #703 - '5S' // 5b8e #148 - '196E' // 5b8f #5100 - '47Q' // 5b90 #1238 - '27B' // 5b91 #703 - '15S' // 5b92 #408 - '47R' // 5b93 #1239 - '27B' // 5b94 #703 - '109W' // 5b95 #2856 - '109R' // 5b96 #2851 - '67R' // 5b97 #1759 - '227V' // 5b98 #5923 - '169A' // 5b99 #4394 - '69C' // 5b9a #1796 - '154F' // 5b9b #4009 - '219D' // 5b9c #5697 - '169I' // 5b9d #4402 - '35T' // 5b9e #929 - '110B' // 5b9f #2861 - '3Y' // 5ba0 #102 - '2C' // 5ba1 #54 - '242N' // 5ba2 #6305 - '210M' // 5ba3 #5472 - '230Y' // 5ba4 #6004 - '134I' // 5ba5 #3492 - '47R' // 5ba6 #1239 - '15S' // 5ba7 #408 - '60S' // 5ba8 #1578 - '5S' // 5ba9 #148 - '110F' // 5baa #2865 - '3Y' // 5bab #102 - '47R' // 5bac #1239 - '60S' // 5bad #1578 - '213V' // 5bae #5559 - '5S' // 5baf #148 - '148I' // 5bb0 #3856 - 'a5S' // 5bb1-5bb2 #148 - '222S' // 5bb3 #5790 - '189S' // 5bb4 #4932 - '159V' // 5bb5 #4155 - '246Q' // 5bb6 #6412 - '47Q' // 5bb7 #1238 - '63I' // 5bb8 #1646 - '243J' // 5bb9 #6327 - '5S' // 5bba #148 - 'A' // 5bbb - '1S' // 5bbc #44 - 'a3Y' // 5bbd-5bbe #102 - '213G' // 5bbf #5544 - '30Q' // 5bc0 #796 - '23L' // 5bc1 #609 - '161J' // 5bc2 #4195 - '30Q' // 5bc3 #796 - '222U' // 5bc4 #5792 - '130J' // 5bc5 #3389 - '228N' // 5bc6 #5941 - '129F' // 5bc7 #3359 - 'A' // 5bc8 - '1S' // 5bc9 #44 - 'A' // 5bca - '8T' // 5bcb #227 - '230E' // 5bcc #5984 - 'b1S' // 5bcd-5bcf #44 - '30Q' // 5bd0 #796 - '60T' // 5bd1 #1579 - '198F' // 5bd2 #5153 - '171Y' // 5bd3 #4470 - '30Q' // 5bd4 #796 - '60T' // 5bd5 #1579 - '60P' // 5bd6 #1575 - '30Q' // 5bd7 #796 - '60P' // 5bd8 #1575 - '1S' // 5bd9 #44 - '250J' // 5bda #6509 - '109Y' // 5bdb #2858 - '8T' // 5bdc #227 - '258A' // 5bdd #6708 - '146X' // 5bde #3819 - '211V' // 5bdf #5507 - '1S' // 5be0 #44 - '146V' // 5be1 #3817 - '171X' // 5be2 #4469 - 'A' // 5be3 - 'a30Q' // 5be4-5be5 #796 - '240A' // 5be6 #6240 - '204D' // 5be7 #5307 - '152A' // 5be8 #3952 - '197U' // 5be9 #5142 - 'A' // 5bea - '225U' // 5beb #5870 - '200P' // 5bec #5215 - 'A' // 5bed - '160S' // 5bee #4178 - '47O' // 5bef #1236 - '63X' // 5bf0 #1661 - '1S' // 5bf1 #44 - '7X' // 5bf2 #205 - '8G' // 5bf3 #214 - '1S' // 5bf4 #44 - '217R' // 5bf5 #5659 - '225T' // 5bf6 #5869 - 'A' // 5bf7 - '67R' // 5bf8 #1759 - '7K' // 5bf9 #192 - '176T' // 5bfa #4595 - '3I' // 5bfb #86 - '3Q' // 5bfc #94 - '1S' // 5bfd #44 - '260D' // 5bfe #6763 - '109Z' // 5bff #2859 - 'A' // 5c00 - '210G' // 5c01 #5466 - '259R' // 5c02 #6751 - '8G' // 5c03 #214 - '211L' // 5c04 #5497 - '8G' // 5c05 #214 - '257F' // 5c06 #6687 - 'a239Z' // 5c07-5c08 #6239 - '47O' // 5c09 #1236 - '196V' // 5c0a #5117 - '233K' // 5c0b #6068 - '8G' // 5c0c #214 - '240B' // 5c0d #6241 - '234V' // 5c0e #6105 - '69J' // 5c0f #1803 - '12H' // 5c10 #319 - '238N' // 5c11 #6201 - 'a8G' // 5c12-5c13 #214 - '123W' // 5c14 #3220 - '12H' // 5c15 #319 - '194V' // 5c16 #5065 - '31O' // 5c17 #820 - '1R' // 5c18 #43 - '250I' // 5c19 #6508 - '233O' // 5c1a #6072 - '7X' // 5c1b #205 - '39U' // 5c1c #1034 - '1R' // 5c1d #43 - 'a8G' // 5c1e-5c1f #214 - '60M' // 5c20 #1572 - 'A' // 5c21 - '8G' // 5c22 #214 - '60M' // 5c23 #1572 - '200Q' // 5c24 #5216 - '12H' // 5c25 #319 - '1S' // 5c26 #44 - '2W' // 5c27 #74 - '47O' // 5c28 #1236 - '1S' // 5c29 #44 - '8G' // 5c2a #214 - '31O' // 5c2b #820 - '165J' // 5c2c #4299 - 'a1S' // 5c2d-5c2e #44 - '7X' // 5c2f #205 - '31O' // 5c30 #820 - '241G' // 5c31 #6272 - '1S' // 5c32 #44 - 'A' // 5c33 - '2Y' // 5c34 #76 - 'a1S' // 5c35-5c36 #44 - '155Q' // 5c37 #4046 - '109N' // 5c38 #2847 - '146W' // 5c39 #3818 - '208V' // 5c3a #5429 - '125X' // 5c3b #3273 - '218L' // 5c3c #5679 - '255R' // 5c3d #6647 - '212F' // 5c3e #5517 - '189L' // 5c3f #4925 - '222H' // 5c40 #5779 - '172R' // 5c41 #4489 - '2C' // 5c42 #54 - '7X' // 5c43 #205 - '12H' // 5c44 #319 - '229M' // 5c45 #5966 - '193U' // 5c46 #5038 - '12H' // 5c47 #319 - '168M' // 5c48 #4380 - '110D' // 5c49 #2863 - '110A' // 5c4a #2860 - '223X' // 5c4b #5821 - '149V' // 5c4c #3895 - '179D' // 5c4d #4657 - '151Y' // 5c4e #3950 - '207S' // 5c4f #5400 - '8G' // 5c50 #214 - '135R' // 5c51 #3527 - 'A' // 5c52 - '23L' // 5c53 #609 - 'A' // 5c54 - '235I' // 5c55 #6118 - 'aA' // 5c56-5c57 - '15S' // 5c58 #408 - '8G' // 5c59 #214 - '1S' // 5c5a #44 - '70J' // 5c5b #1829 - '63H' // 5c5c #1645 - '15S' // 5c5d #408 - '126E' // 5c5e #3280 - '1S' // 5c5f #44 - '146U' // 5c60 #3816 - '250K' // 5c61 #6510 - '48Z' // 5c62 #1273 - '8G' // 5c63 #214 - '219N' // 5c64 #5707 - '169K' // 5c65 #4404 - '7X' // 5c66 #205 - '1S' // 5c67 #44 - '23L' // 5c68 #609 - '1S' // 5c69 #44 - 'aA' // 5c6a-5c6b - '225S' // 5c6c #5868 - '23L' // 5c6d #609 - '8G' // 5c6e #214 - '179E' // 5c6f #4658 - '1S' // 5c70 #44 - '238Y' // 5c71 #6212 - 'aA' // 5c72-5c73 - '8G' // 5c74 #214 - 'a1S' // 5c75-5c76 #44 - 'A' // 5c77 - '39U' // 5c78 #1034 - '30P' // 5c79 #795 - 'b8G' // 5c7a-5c7c #214 - '31O' // 5c7d #820 - '12H' // 5c7e #319 - '3G' // 5c7f #84 - '7X' // 5c80 #205 - '2C' // 5c81 #54 - '3G' // 5c82 #84 - '39U' // 5c83 #1034 - '7X' // 5c84 #205 - '35C' // 5c85 #912 - '15S' // 5c86 #408 - '1S' // 5c87 #44 - '8G' // 5c88 #214 - 'A' // 5c89 - '23L' // 5c8a #609 - '12H' // 5c8b #319 - '8G' // 5c8c #214 - '12H' // 5c8d #319 - 'A' // 5c8e - '1S' // 5c8f #44 - '137F' // 5c90 #3567 - '48Z' // 5c91 #1273 - '23L' // 5c92 #609 - '15S' // 5c93 #408 - '8G' // 5c94 #214 - '12H' // 5c95 #319 - '7X' // 5c96 #205 - '1R' // 5c97 #43 - '7X' // 5c98 #205 - '12H' // 5c99 #319 - '110E' // 5c9a #2864 - '3I' // 5c9b #86 - '12H' // 5c9c #319 - '1S' // 5c9d #44 - '39U' // 5c9e #1034 - '1S' // 5c9f #44 - '23L' // 5ca0 #609 - '184G' // 5ca1 #4790 - 'a8G' // 5ca2-5ca3 #214 - 'A' // 5ca4 - '15S' // 5ca5 #408 - 'a23L' // 5ca6-5ca7 #609 - '1S' // 5ca8 #44 - '176U' // 5ca9 #4596 - '1S' // 5caa #44 - '30P' // 5cab #795 - '109X' // 5cac #2857 - '109O' // 5cad #2848 - 'bA' // 5cae-5cb0 - '129E' // 5cb1 #3358 - '1S' // 5cb2 #44 - '161G' // 5cb3 #4192 - '1S' // 5cb4 #44 - '30P' // 5cb5 #795 - '1S' // 5cb6 #44 - '30P' // 5cb7 #795 - '204C' // 5cb8 #5306 - 'A' // 5cb9 - '30P' // 5cba #795 - 'a1S' // 5cbb-5cbc #44 - '7X' // 5cbd #205 - '70J' // 5cbe #1829 - '7X' // 5cbf #205 - '71E' // 5cc0 #1850 - '12H' // 5cc1 #319 - '35C' // 5cc2 #912 - 'a7X' // 5cc3-5cc4 #205 - '1S' // 5cc5 #44 - 'A' // 5cc6 - '63X' // 5cc7 #1661 - '15S' // 5cc8 #408 - '1S' // 5cc9 #44 - '15S' // 5cca #408 - '8G' // 5ccb #214 - 'aA' // 5ccc-5ccd - '15S' // 5cce #408 - 'A' // 5ccf - '1S' // 5cd0 #44 - '35C' // 5cd1 #912 - '8G' // 5cd2 #214 - 'aA' // 5cd3-5cd4 - '35C' // 5cd5 #912 - '15S' // 5cd6 #408 - '1S' // 5cd7 #44 - '12H' // 5cd8 #319 - '30P' // 5cd9 #795 - '15S' // 5cda #408 - 'aA' // 5cdb-5cdc - '1S' // 5cdd #44 - 'A' // 5cde - '12H' // 5cdf #319 - '253C' // 5ce0 #6580 - '252W' // 5ce1 #6574 - 'A' // 5ce2 - 'a7X' // 5ce3-5ce4 #205 - '12H' // 5ce5 #319 - '31O' // 5ce6 #820 - 'A' // 5ce7 - '60N' // 5ce8 #1573 - 'a60O' // 5ce9-5cea #1574 - 'aA' // 5ceb-5cec - '60O' // 5ced #1574 - '47P' // 5cee #1237 - '135Z' // 5cef #3535 - '196M' // 5cf0 #5108 - '47P' // 5cf1 #1237 - '1S' // 5cf2 #44 - '7X' // 5cf3 #205 - '60N' // 5cf4 #1573 - '47P' // 5cf5 #1237 - '223U' // 5cf6 #5818 - 'A' // 5cf7 - '47N' // 5cf8 #1235 - 'A' // 5cf9 - '1S' // 5cfa #44 - '140K' // 5cfb #3650 - '30M' // 5cfc #792 - '165I' // 5cfd #4298 - 'A' // 5cfe - '15R' // 5cff #407 - '47N' // 5d00 #1235 - '140L' // 5d01 #3651 - 'b7X' // 5d02-5d04 #205 - 'A' // 5d05 - '20U' // 5d06 #540 - '173V' // 5d07 #4519 - '7X' // 5d08 #205 - 'aA' // 5d09-5d0a - '20U' // 5d0b #540 - '47N' // 5d0c #1235 - '109F' // 5d0d #2839 - '169M' // 5d0e #4406 - '7X' // 5d0f #205 - '63H' // 5d10 #1645 - '48Z' // 5d11 #1273 - '31O' // 5d12 #820 - '7X' // 5d13 #205 - '151Z' // 5d14 #3951 - '20U' // 5d15 #540 - '136A' // 5d16 #3536 - '158M' // 5d17 #4120 - '20U' // 5d18 #540 - '151X' // 5d19 #3949 - '20U' // 5d1a #540 - '140M' // 5d1b #3652 - 'A' // 5d1c - '20U' // 5d1d #540 - '7X' // 5d1e #205 - 'a20U' // 5d1f-5d20 #540 - '7X' // 5d21 #205 - '20U' // 5d22 #540 - '1S' // 5d23 #44 - '20U' // 5d24 #540 - '7X' // 5d25 #205 - '14L' // 5d26 #375 - '30N' // 5d27 #793 - '15R' // 5d28 #407 - '183B' // 5d29 #4759 - 'A' // 5d2a - '1S' // 5d2b #44 - '15R' // 5d2c #407 - '2W' // 5d2d #74 - '16Y' // 5d2e #440 - '35B' // 5d2f #911 - '5E' // 5d30 #134 - '31N' // 5d31 #819 - '5E' // 5d32 #134 - '16Y' // 5d33 #440 - '14L' // 5d34 #375 - '15R' // 5d35 #407 - 'bA' // 5d36-5d38 - '1S' // 5d39 #44 - 'aA' // 5d3a-5d3b - '5E' // 5d3c #134 - '14L' // 5d3d #375 - '35B' // 5d3e #911 - '30O' // 5d3f #794 - 'A' // 5d40 - '5E' // 5d41 #134 - 'a14L' // 5d42-5d43 #375 - '5E' // 5d44 #134 - 'A' // 5d45 - 'a14L' // 5d46-5d47 #375 - '109G' // 5d48 #2840 - '15R' // 5d49 #407 - '14L' // 5d4a #375 - '30N' // 5d4b #793 - '152T' // 5d4c #3971 - 'A' // 5d4d - '14L' // 5d4e #375 - 'A' // 5d4f - '155A' // 5d50 #4030 - '1S' // 5d51 #44 - '14L' // 5d52 #375 - '1S' // 5d53 #44 - 'A' // 5d54 - '31N' // 5d55 #819 - '35B' // 5d56 #911 - '16Y' // 5d57 #440 - '5E' // 5d58 #134 - '30O' // 5d59 #794 - '5E' // 5d5a #134 - '16Y' // 5d5b #440 - '31N' // 5d5c #819 - '5E' // 5d5d #134 - 'A' // 5d5e - 'c1S' // 5d5f-5d62 #44 - 'A' // 5d63 - '1S' // 5d64 #44 - '15R' // 5d65 #407 - 'aA' // 5d66-5d67 - '16Y' // 5d68 #440 - '30N' // 5d69 #793 - '1S' // 5d6a #44 - '16Y' // 5d6b #440 - '30N' // 5d6c #793 - '1S' // 5d6d #44 - 'A' // 5d6e - '30N' // 5d6f #793 - '109E' // 5d70 #2838 - 'aA' // 5d71-5d72 - '1S' // 5d73 #44 - '16Y' // 5d74 #440 - 'A' // 5d75 - '1S' // 5d76 #44 - 'A' // 5d77 - '30M' // 5d78 #792 - 'a1S' // 5d79-5d7a #44 - '30M' // 5d7b #792 - 'aA' // 5d7c-5d7d - '30O' // 5d7e #794 - '14L' // 5d7f #375 - 'A' // 5d80 - '30O' // 5d81 #794 - '14L' // 5d82 #375 - '31N' // 5d83 #819 - '134H' // 5d84 #3491 - 'a16Y' // 5d85-5d86 #440 - '30N' // 5d87 #793 - '14L' // 5d88 #375 - '5E' // 5d89 #134 - '1S' // 5d8a #44 - '109J' // 5d8b #2843 - '31N' // 5d8c #819 - 'A' // 5d8d - '30M' // 5d8e #792 - '5E' // 5d8f #134 - '1S' // 5d90 #44 - 'A' // 5d91 - '14L' // 5d92 #375 - '31N' // 5d93 #819 - '30O' // 5d94 #794 - '1S' // 5d95 #44 - 'A' // 5d96 - '30O' // 5d97 #794 - 'A' // 5d98 - '14L' // 5d99 #375 - 'A' // 5d9a - '1X' // 5d9b #49 - 'A' // 5d9c - '23I' // 5d9d #606 - 'A' // 5d9e - '1X' // 5d9f #49 - '47M' // 5da0 #1234 - '15R' // 5da1 #407 - '47M' // 5da2 #1234 - 'A' // 5da3 - '35A' // 5da4 #910 - 'aA' // 5da5-5da6 - '23K' // 5da7 #608 - 'A' // 5da8 - '15R' // 5da9 #407 - '109K' // 5daa #2844 - '35A' // 5dab #910 - '1X' // 5dac #49 - 'A' // 5dad - '23K' // 5dae #608 - 'A' // 5daf - '1X' // 5db0 #49 - 'A' // 5db1 - '20T' // 5db2 #539 - 'A' // 5db3 - '23K' // 5db4 #608 - 'A' // 5db5 - '35B' // 5db6 #911 - '20T' // 5db7 #539 - '47M' // 5db8 #1234 - '35A' // 5db9 #910 - '166Q' // 5dba #4332 - 'A' // 5dbb - '158K' // 5dbc #4118 - '23I' // 5dbd #606 - 'bA' // 5dbe-5dc0 - '30M' // 5dc1 #792 - '16Y' // 5dc2 #440 - '20T' // 5dc3 #539 - 'A' // 5dc4 - '2R' // 5dc5 #69 - '5E' // 5dc6 #134 - '36G' // 5dc7 #942 - 'A' // 5dc8 - '20T' // 5dc9 #539 - 'A' // 5dca - '23K' // 5dcb #608 - '36G' // 5dcc #942 - '108Z' // 5dcd #2833 - '1X' // 5dce #49 - 'A' // 5dcf - 'a1X' // 5dd0-5dd1 #49 - '23I' // 5dd2 #606 - '1X' // 5dd3 #49 - '137N' // 5dd4 #3575 - 'A' // 5dd5 - '23I' // 5dd6 #606 - '109B' // 5dd7 #2835 - '20T' // 5dd8 #539 - '1X' // 5dd9 #49 - 'A' // 5dda - '20T' // 5ddb #539 - '5E' // 5ddc #134 - '214D' // 5ddd #5567 - '212Z' // 5dde #5537 - 'A' // 5ddf - '23K' // 5de0 #608 - '182W' // 5de1 #4754 - '165G' // 5de2 #4296 - '254R' // 5de3 #6621 - '1X' // 5de4 #49 - '242M' // 5de5 #6304 - '221P' // 5de6 #5761 - '214Y' // 5de7 #5588 - '211U' // 5de8 #5506 - '250E' // 5de9 #6504 - 'A' // 5dea - '153M' // 5deb #3990 - 'A' // 5dec - '5E' // 5ded #134 - '229L' // 5dee #5965 - '5E' // 5def #134 - '15R' // 5df0 #407 - '234F' // 5df1 #6089 - '239Y' // 5df2 #6238 - '109H' // 5df3 #2841 - '226N' // 5df4 #5889 - '23K' // 5df5 #608 - '5E' // 5df6 #134 - '201E' // 5df7 #5230 - '1X' // 5df8 #49 - '23K' // 5df9 #608 - '5E' // 5dfa #134 - '257S' // 5dfb #6700 - '5E' // 5dfc #134 - '23I' // 5dfd #606 - '180Z' // 5dfe #4705 - '129D' // 5dff #3357 - '36G' // 5e00 #942 - '3I' // 5e01 #86 - '68X' // 5e02 #1791 - '229U' // 5e03 #5974 - '15R' // 5e04 #407 - '3N' // 5e05 #91 - '167P' // 5e06 #4357 - '36G' // 5e07 #942 - '1Z' // 5e08 #51 - '30M' // 5e09 #792 - '15R' // 5e0a #407 - '35A' // 5e0b #910 - '223L' // 5e0c #5809 - '1X' // 5e0d #49 - 'A' // 5e0e - '5E' // 5e0f #134 - '2C' // 5e10 #54 - '23I' // 5e11 #606 - '35A' // 5e12 #910 - '5E' // 5e13 #134 - '20T' // 5e14 #539 - '165H' // 5e15 #4297 - '188E' // 5e16 #4892 - 'A' // 5e17 - '109D' // 5e18 #2837 - '23I' // 5e19 #606 - '20T' // 5e1a #539 - '23I' // 5e1b #606 - '3W' // 5e1c #100 - '202Z' // 5e1d #5277 - 'A' // 5e1e - 'a23K' // 5e1f-5e20 #608 - '15R' // 5e21 #407 - '16Y' // 5e22 #440 - 'aA' // 5e23-5e24 - '193T' // 5e25 #5037 - '1Z' // 5e26 #51 - '3W' // 5e27 #100 - '20T' // 5e28 #539 - 'A' // 5e29 - '5E' // 5e2a #134 - '235R' // 5e2b #6127 - 'A' // 5e2c - '205G' // 5e2d #5336 - '158L' // 5e2e #4119 - '259F' // 5e2f #6739 - '258L' // 5e30 #6719 - '5E' // 5e31 #134 - '1X' // 5e32 #49 - '228G' // 5e33 #5934 - '16Y' // 5e34 #440 - '1X' // 5e35 #49 - '232T' // 5e36 #6051 - '109C' // 5e37 #2836 - '242X' // 5e38 #6315 - 'c5E' // 5e39-5e3c #134 - '197E' // 5e3d #5126 - '36G' // 5e3e #942 - '71E' // 5e3f #1850 - '47L' // 5e40 #1233 - 'A' // 5e41 - '109M' // 5e42 #2846 - '39T' // 5e43 #1033 - '47L' // 5e44 #1233 - '191I' // 5e45 #4974 - '5E' // 5e46 #134 - '70I' // 5e47 #1828 - '35B' // 5e48 #911 - '1X' // 5e49 #49 - 'A' // 5e4a - '1X' // 5e4b #49 - '125U' // 5e4c #3270 - 'A' // 5e4d - '1X' // 5e4e #49 - '5E' // 5e4f #134 - 'a1X' // 5e50-5e51 #49 - 'A' // 5e52 - '5E' // 5e53 #134 - '23J' // 5e54 #607 - '219R' // 5e55 #5711 - '1X' // 5e56 #49 - '23J' // 5e57 #607 - '39T' // 5e58 #1033 - '109L' // 5e59 #2845 - 'A' // 5e5a - '23J' // 5e5b #607 - '1X' // 5e5c #49 - 'A' // 5e5d - '23J' // 5e5e #607 - '47L' // 5e5f #1233 - 'A' // 5e60 - '109I' // 5e61 #2842 - '109A' // 5e62 #2834 - '208R' // 5e63 #5425 - '1X' // 5e64 #49 - 'bA' // 5e65-5e67 - '1X' // 5e68 #49 - 'A' // 5e69 - '23J' // 5e6a #607 - '225R' // 5e6b #5867 - '39T' // 5e6c #1033 - '1X' // 5e6d #49 - '39T' // 5e6e #1033 - 'A' // 5e6f - '1X' // 5e70 #49 - 'A' // 5e71 - '190P' // 5e72 #4955 - '244L' // 5e73 #6355 - '245M' // 5e74 #6382 - '23J' // 5e75 #607 - '146S' // 5e76 #3814 - '70I' // 5e77 #1828 - '216F' // 5e78 #5621 - '197Z' // 5e79 #5147 - '23J' // 5e7a #607 - '203J' // 5e7b #5287 - '211T' // 5e7c #5505 - '180X' // 5e7d #4703 - '226V' // 5e7e #5897 - '146T' // 5e7f #3815 - '23J' // 5e80 #607 - '256G' // 5e81 #6662 - 'A' // 5e82 - '108T' // 5e83 #2827 - '154R' // 5e84 #4021 - '5E' // 5e85 #134 - '108Y' // 5e86 #2832 - '140H' // 5e87 #3647 - '30L' // 5e88 #791 - 'A' // 5e89 - '211B' // 5e8a #5487 - '60I' // 5e8b #1568 - 'aA' // 5e8c-5e8d - '1X' // 5e8e #49 - '227A' // 5e8f #5902 - '3W' // 5e90 #100 - '5E' // 5e91 #134 - '108W' // 5e92 #2830 - '1Z' // 5e93 #51 - '3Q' // 5e94 #94 - '227X' // 5e95 #5925 - '60I' // 5e96 #1568 - '238S' // 5e97 #6206 - '14Z' // 5e98 #389 - '108I' // 5e99 #2816 - '65H' // 5e9a #1697 - '30K' // 5e9b #790 - '216H' // 5e9c #5623 - '14Z' // 5e9d #389 - '2R' // 5e9e #69 - '1R' // 5e9f #43 - '39S' // 5ea0 #1032 - '14Z' // 5ea1 #389 - '4P' // 5ea2 #119 - '14Z' // 5ea3 #389 - 'a4P' // 5ea4-5ea5 #119 - '41D' // 5ea6 #1069 - '230J' // 5ea7 #5989 - '27A' // 5ea8 #702 - 'A' // 5ea9 - '1X' // 5eaa #49 - '223J' // 5eab #5807 - '1X' // 5eac #49 - '221K' // 5ead #5756 - 'A' // 5eae - '14Z' // 5eaf #389 - 'A' // 5eb0 - '1X' // 5eb1 #49 - 'A' // 5eb2 - '4P' // 5eb3 #119 - '30L' // 5eb4 #791 - '108P' // 5eb5 #2823 - '108M' // 5eb6 #2820 - '235S' // 5eb7 #6128 - '65H' // 5eb8 #1697 - '4P' // 5eb9 #119 - 'bA' // 5eba-5ebc - '60G' // 5ebd #1566 - '39S' // 5ebe #1032 - '1X' // 5ebf #49 - 'A' // 5ec0 - '66L' // 5ec1 #1727 - '151W' // 5ec2 #3948 - '256J' // 5ec3 #6665 - '30L' // 5ec4 #791 - 'A' // 5ec5 - '4P' // 5ec6 #119 - 'A' // 5ec7 - '171W' // 5ec8 #4468 - '187Q' // 5ec9 #4878 - '173Z' // 5eca #4523 - 'a4P' // 5ecb-5ecc #119 - '30L' // 5ecd #791 - 'a1X' // 5ece-5ecf #49 - '108G' // 5ed0 #2814 - 'a4P' // 5ed1-5ed2 #119 - '129C' // 5ed3 #3356 - '4P' // 5ed4 #119 - '30J' // 5ed5 #789 - '165F' // 5ed6 #4295 - 'A' // 5ed7 - '30L' // 5ed8 #791 - '4P' // 5ed9 #119 - '67V' // 5eda #1763 - '39S' // 5edb #1032 - '1X' // 5edc #49 - '4P' // 5edd #119 - '1X' // 5ede #49 - '179C' // 5edf #4656 - '217Q' // 5ee0 #5658 - '30J' // 5ee1 #789 - '193S' // 5ee2 #5036 - '233F' // 5ee3 #6063 - 'A' // 5ee4 - '1X' // 5ee5 #49 - 'aA' // 5ee6-5ee7 - '4P' // 5ee8 #119 - '30J' // 5ee9 #789 - '14Z' // 5eea #389 - '1X' // 5eeb #49 - '39S' // 5eec #1032 - 'aA' // 5eed-5eee - '30K' // 5eef #790 - '27A' // 5ef0 #702 - '1X' // 5ef1 #49 - 'A' // 5ef2 - '67V' // 5ef3 #1763 - '4P' // 5ef4 #119 - 'A' // 5ef5 - '211C' // 5ef6 #5488 - '180G' // 5ef7 #4686 - '4P' // 5ef8 #119 - '60G' // 5ef9 #1566 - '235D' // 5efa #6113 - '108O' // 5efb #2822 - '4P' // 5efc #119 - '1X' // 5efd #49 - '4P' // 5efe #119 - '140I' // 5eff #3648 - '250H' // 5f00 #6507 - '108S' // 5f01 #2826 - '108J' // 5f02 #2817 - '250G' // 5f03 #6506 - '195P' // 5f04 #5085 - '30L' // 5f05 #791 - '1X' // 5f06 #49 - '4P' // 5f07 #119 - '108H' // 5f08 #2815 - '1X' // 5f09 #49 - '136Y' // 5f0a #3560 - 'c4P' // 5f0b-5f0e #119 - '69C' // 5f0f #1796 - '252S' // 5f10 #6570 - '70H' // 5f11 #1827 - '30K' // 5f12 #790 - '148V' // 5f13 #3869 - '108K' // 5f14 #2818 - '231R' // 5f15 #6023 - '1X' // 5f16 #49 - '151V' // 5f17 #3947 - '176D' // 5f18 #4579 - '1X' // 5f19 #49 - '14Z' // 5f1a #389 - '108F' // 5f1b #2813 - '1X' // 5f1c #49 - '30J' // 5f1d #789 - '1X' // 5f1e #49 - '204B' // 5f1f #5305 - '1Z' // 5f20 #51 - '1X' // 5f21 #49 - '4P' // 5f22 #119 - 'a1X' // 5f23-5f24 #49 - '108Q' // 5f25 #2824 - '154O' // 5f26 #4018 - '140F' // 5f27 #3645 - '4P' // 5f28 #119 - '34Z' // 5f29 #909 - 'A' // 5f2a - '1X' // 5f2b #49 - '27A' // 5f2c #702 - '4P' // 5f2d #119 - '1X' // 5f2e #49 - '250F' // 5f2f #6505 - '30J' // 5f30 #789 - '198N' // 5f31 #5161 - 'aA' // 5f32-5f33 - '1X' // 5f34 #49 - '236N' // 5f35 #6149 - '4P' // 5f36 #119 - '237Q' // 5f37 #6178 - '4P' // 5f38 #119 - '3I' // 5f39 #86 - '140G' // 5f3a #3646 - '60H' // 5f3b #1567 - '34Z' // 5f3c #909 - '27A' // 5f3d #702 - '257N' // 5f3e #6695 - '27A' // 5f3f #702 - '4P' // 5f40 #119 - '27A' // 5f41 #702 - 'a14Z' // 5f42-5f43 #389 - '1X' // 5f44 #49 - '4P' // 5f45 #119 - '30K' // 5f46 #790 - '27A' // 5f47 #702 - '207R' // 5f48 #5399 - '14Z' // 5f49 #389 - '34Z' // 5f4a #909 - 'A' // 5f4b - '165E' // 5f4c #4294 - '60H' // 5f4d #1567 - '171V' // 5f4e #4467 - 'A' // 5f4f - '4P' // 5f50 #119 - '30J' // 5f51 #789 - '2C' // 5f52 #54 - '260F' // 5f53 #6765 - '4P' // 5f54 #119 - '7K' // 5f55 #192 - 'a34Z' // 5f56-5f57 #909 - '4P' // 5f58 #119 - '173N' // 5f59 #4511 - 'A' // 5f5a - '70H' // 5f5b #1827 - 'a4P' // 5f5c-5f5d #119 - '30K' // 5f5e #790 - '14Z' // 5f5f #389 - '1X' // 5f60 #49 - '108N' // 5f61 #2821 - '231C' // 5f62 #6008 - '4P' // 5f63 #119 - '140J' // 5f64 #3649 - '66L' // 5f65 #1727 - '255S' // 5f66 #6648 - '34Z' // 5f67 #909 - '14Z' // 5f68 #389 - '227Z' // 5f69 #5927 - '129B' // 5f6a #3355 - '108R' // 5f6b #2825 - '146R' // 5f6c #3813 - '171S' // 5f6d #4464 - '14Z' // 5f6e #389 - '31M' // 5f6f #818 - '201Z' // 5f70 #5251 - '241Y' // 5f71 #6290 - 'b60E' // 5f72-5f74 #1564 - '31M' // 5f75 #818 - '30K' // 5f76 #790 - '152U' // 5f77 #3972 - '60E' // 5f78 #1564 - '176V' // 5f79 #4597 - '31M' // 5f7a #818 - '108X' // 5f7b #2831 - '184A' // 5f7c #4784 - '108E' // 5f7d #2812 - '31M' // 5f7e #818 - '151Q' // 5f7f #3942 - '226Z' // 5f80 #5901 - '181Q' // 5f81 #4722 - 'a30I' // 5f82-5f83 #788 - '255E' // 5f84 #6634 - '223O' // 5f85 #5812 - 'A' // 5f86 - '39R' // 5f87 #1031 - '232R' // 5f88 #6049 - '30I' // 5f89 #788 - '60J' // 5f8a #1569 - '220D' // 5f8b #5723 - '245B' // 5f8c #6371 - '31M' // 5f8d #818 - 'A' // 5f8e - '3C' // 5f8f #80 - '188S' // 5f90 #4906 - '186I' // 5f91 #4844 - '191N' // 5f92 #4979 - '257D' // 5f93 #6685 - 'A' // 5f94 - '14Z' // 5f95 #389 - '3C' // 5f96 #80 - '243O' // 5f97 #6332 - '60J' // 5f98 #1569 - '39R' // 5f99 #1031 - 'A' // 5f9a - '60L' // 5f9b #1571 - '30I' // 5f9c #788 - '3C' // 5f9d #80 - '233C' // 5f9e #6060 - 'A' // 5f9f - '39R' // 5fa0 #1031 - '183Y' // 5fa1 #4782 - '3C' // 5fa2 #80 - 'A' // 5fa3 - '60F' // 5fa4 #1565 - 'A' // 5fa5 - '108V' // 5fa6 #2829 - '30I' // 5fa7 #788 - '39R' // 5fa8 #1031 - '221W' // 5fa9 #5768 - '181B' // 5faa #4707 - '60F' // 5fab #1565 - 'a30I' // 5fac-5fad #788 - '227L' // 5fae #5913 - '31M' // 5faf #818 - '3C' // 5fb0 #80 - '108D' // 5fb1 #2811 - 'A' // 5fb2 - '257R' // 5fb3 #6699 - '257P' // 5fb4 #6697 - '214Q' // 5fb5 #5580 - 'A' // 5fb6 - '225Q' // 5fb7 #5866 - '3C' // 5fb8 #80 - '176I' // 5fb9 #4584 - '60D' // 5fba #1563 - 'A' // 5fbb - '30I' // 5fbc #788 - '151U' // 5fbd #3946 - 'cA' // 5fbe-5fc1 - '60D' // 5fc2 #1563 - '246O' // 5fc3 #6410 - '16X' // 5fc4 #439 - '238K' // 5fc5 #6198 - '3N' // 5fc6 #91 - 'a3C' // 5fc7-5fc8 #80 - '16X' // 5fc9 #439 - 'A' // 5fca - '17I' // 5fcb #450 - '180T' // 5fcc #4699 - '197K' // 5fcd #5132 - '6U' // 5fce #176 - '60K' // 5fcf #1570 - 'b16X' // 5fd0-5fd2 #439 - '17I' // 5fd3 #450 - '16X' // 5fd4 #439 - '60K' // 5fd5 #1570 - '108L' // 5fd6 #2819 - '221B' // 5fd7 #5747 - '221O' // 5fd8 #5760 - '203N' // 5fd9 #5291 - '6U' // 5fda #176 - '108U' // 5fdb #2828 - '260B' // 5fdc #6761 - 'a16X' // 5fdd-5fde #439 - '60L' // 5fdf #1571 - '196L' // 5fe0 #5107 - '16X' // 5fe1 #439 - '17I' // 5fe2 #450 - 'A' // 5fe3 - '16X' // 5fe4 #439 - '6U' // 5fe5 #176 - 'A' // 5fe6 - '3N' // 5fe7 #91 - 'a3C' // 5fe8-5fe9 #80 - '16X' // 5fea #439 - '241L' // 5feb #6277 - '3C' // 5fec #80 - 'a16X' // 5fed-5fee #439 - 'a3C' // 5fef-5ff0 #80 - '16X' // 5ff1 #439 - '17I' // 5ff2 #450 - '16X' // 5ff3 #439 - 'A' // 5ff4 - '223E' // 5ff5 #5802 - '17I' // 5ff6 #450 - 'A' // 5ff7 - '5J' // 5ff8 #139 - 'A' // 5ff9 - '18Z' // 5ffa #493 - '5J' // 5ffb #139 - '3C' // 5ffc #80 - '179A' // 5ffd #4654 - '6U' // 5ffe #176 - '16V' // 5fff #437 - '108C' // 6000 #2810 - '3Q' // 6001 #94 - 'd6U' // 6002-6006 #176 - '3C' // 6007 #80 - 'aA' // 6008-6009 - '5J' // 600a #139 - 'aA' // 600b-600c - '5J' // 600d #139 - '217P' // 600e #5657 - '16V' // 600f #437 - '18Z' // 6010 #493 - 'A' // 6011 - '190T' // 6012 #4959 - '3C' // 6013 #80 - '5J' // 6014 #139 - '207Q' // 6015 #5398 - '190R' // 6016 #4957 - '18Z' // 6017 #493 - '3C' // 6018 #80 - '5J' // 6019 #139 - '18Z' // 601a #493 - '5J' // 601b #139 - '107Q' // 601c #2798 - '231S' // 601d #6024 - '26L' // 601e #687 - '3C' // 601f #80 - '107S' // 6020 #2800 - '178Z' // 6021 #4653 - '18Z' // 6022 #493 - '108A' // 6023 #2808 - '3C' // 6024 #80 - '213P' // 6025 #5553 - '5J' // 6026 #139 - '244Q' // 6027 #6360 - '172P' // 6028 #4487 - '5J' // 6029 #139 - '210S' // 602a #5478 - '5J' // 602b #139 - '26L' // 602c #687 - '3C' // 602d #80 - '39Q' // 602e #1030 - '107P' // 602f #2797 - 'A' // 6030 - '5J' // 6031 #139 - 'A' // 6032 - '5J' // 6033 #139 - '26L' // 6034 #687 - '5J' // 6035 #139 - 'bA' // 6036-6038 - '39Q' // 6039 #1030 - '3C' // 603a #80 - '126K' // 603b #3286 - '2K' // 603c #62 - 'aA' // 603d-603e - '6U' // 603f #176 - '18Z' // 6040 #493 - 'b16V' // 6041-6043 #437 - 'A' // 6044 - '26L' // 6045 #687 - '179B' // 6046 #4655 - '18Z' // 6047 #493 - 'a3C' // 6048-6049 #80 - '18Z' // 604a #493 - '107Z' // 604b #2807 - '18Z' // 604c #493 - '123V' // 604d #3219 - 'A' // 604e - '6U' // 604f #176 - '211S' // 6050 #5504 - '3C' // 6051 #80 - '154P' // 6052 #4019 - '39Q' // 6053 #1030 - '17I' // 6054 #450 - '165D' // 6055 #4293 - 'a3C' // 6056-6057 #80 - '6U' // 6058 #176 - '16V' // 6059 #437 - '5J' // 605a #139 - '39Q' // 605b #1030 - 'A' // 605c - '16V' // 605d #437 - '6U' // 605e #176 - '3C' // 605f #80 - '17I' // 6060 #450 - '3C' // 6061 #80 - '171U' // 6062 #4466 - '16V' // 6063 #437 - '151T' // 6064 #3945 - '155D' // 6065 #4033 - 'A' // 6066 - '5J' // 6067 #139 - '173O' // 6068 #4512 - '209O' // 6069 #5448 - '16V' // 606a #437 - '5J' // 606b #139 - '134G' // 606c #3490 - '173Y' // 606d #4522 - '26L' // 606e #687 - '234J' // 606f #6093 - '158J' // 6070 #4117 - '3C' // 6071 #80 - '26L' // 6072 #687 - '2W' // 6073 #74 - 'A' // 6074 - '107Y' // 6075 #2806 - '3I' // 6076 #86 - '26K' // 6077 #686 - 'a6U' // 6078-6079 #176 - '3G' // 607a #84 - '6U' // 607b #176 - '2Y' // 607c #76 - '6U' // 607d #176 - '26K' // 607e #686 - '5J' // 607f #139 - '26L' // 6080 #687 - '18Z' // 6081 #493 - '3C' // 6082 #80 - '5J' // 6083 #139 - '186K' // 6084 #4846 - '186H' // 6085 #4843 - '5J' // 6086 #139 - '6U' // 6087 #176 - '3C' // 6088 #80 - '186J' // 6089 #4845 - '5J' // 608a #139 - '3C' // 608b #80 - '16V' // 608c #437 - '151S' // 608d #3944 - '5J' // 608e #139 - '6U' // 608f #176 - 'A' // 6090 - '3C' // 6091 #80 - '5J' // 6092 #139 - '17I' // 6093 #450 - '175Z' // 6094 #4575 - '5J' // 6095 #139 - '16V' // 6096 #437 - '5J' // 6097 #139 - '3C' // 6098 #80 - 'A' // 6099 - '151R' // 609a #3943 - '16V' // 609b #437 - '6U' // 609c #176 - '5J' // 609d #139 - '26K' // 609e #686 - '168R' // 609f #4385 - '189K' // 60a0 #4924 - 'A' // 60a1 - '5J' // 60a2 #139 - '189R' // 60a3 #4931 - '107L' // 60a4 #2793 - '3C' // 60a5 #80 - '107T' // 60a6 #2801 - '16V' // 60a7 #437 - '232S' // 60a8 #6050 - '257Z' // 60a9 #6707 - '258R' // 60aa #6725 - '6U' // 60ab #176 - '1R' // 60ac #43 - '6U' // 60ad #176 - 'A' // 60ae - '6U' // 60af #176 - '34Y' // 60b0 #908 - '16W' // 60b1 #438 - '190S' // 60b2 #4958 - 'a34Y' // 60b3-60b4 #908 - '16W' // 60b5 #438 - '167D' // 60b6 #4345 - '3C' // 60b7 #80 - '34Y' // 60b8 #908 - 'aA' // 60b9-60ba - '16W' // 60bb #438 - '130I' // 60bc #3388 - '34Y' // 60bd #908 - '16W' // 60be #438 - 'A' // 60bf - '47K' // 60c0 #1232 - '6U' // 60c1 #176 - '17I' // 60c2 #450 - '6U' // 60c3 #176 - '3C' // 60c4 #80 - '69D' // 60c5 #1797 - '16W' // 60c6 #438 - '34Y' // 60c7 #908 - 'a17I' // 60c8-60c9 #450 - '107O' // 60ca #2796 - '16W' // 60cb #438 - 'aA' // 60cc-60cd - '17I' // 60ce #450 - '3C' // 60cf #80 - 'A' // 60d0 - '190Z' // 60d1 #4965 - 'A' // 60d2 - 'a16W' // 60d3-60d4 #438 - '107N' // 60d5 #2795 - 'A' // 60d6 - '47K' // 60d7 #1232 - 'a16W' // 60d8-60d9 #438 - '107X' // 60da #2805 - '16W' // 60db #438 - '195O' // 60dc #5084 - '16W' // 60dd #438 - '26K' // 60de #686 - '158I' // 60df #4116 - '226I' // 60e0 #5884 - '207P' // 60e1 #5397 - '16W' // 60e2 #438 - '107U' // 60e3 #2802 - '6U' // 60e4 #176 - '3C' // 60e5 #80 - '47K' // 60e6 #1232 - '107R' // 60e7 #2799 - '107W' // 60e8 #2804 - '108B' // 60e9 #2809 - '6U' // 60ea #176 - '2W' // 60eb #74 - '6U' // 60ec #176 - '10L' // 60ed #271 - '250D' // 60ee #6503 - '1R' // 60ef #43 - '34X' // 60f0 #907 - '171R' // 60f1 #4463 - '30H' // 60f2 #787 - '242I' // 60f3 #6300 - '39P' // 60f4 #1029 - '3C' // 60f5 #80 - '34X' // 60f6 #907 - 'a3C' // 60f7-60f8 #80 - '188H' // 60f9 #4895 - '34X' // 60fa #907 - '107M' // 60fb #2794 - '3C' // 60fc #80 - '26K' // 60fd #686 - 'A' // 60fe - '60C' // 60ff #1562 - '39P' // 6100 #1029 - '152V' // 6101 #3973 - '3C' // 6102 #80 - '30H' // 6103 #787 - 'aA' // 6104-6105 - '34X' // 6106 #907 - '26K' // 6107 #686 - '171T' // 6108 #4465 - '174M' // 6109 #4536 - '30H' // 610a #787 - '60C' // 610b #1562 - '26K' // 610c #686 - 'a34X' // 610d-610e #907 - '68Y' // 610f #1792 - '30H' // 6110 #787 - '3C' // 6111 #80 - '39P' // 6112 #1029 - '30H' // 6113 #787 - '39P' // 6114 #1029 - '107V' // 6115 #2803 - '30H' // 6116 #787 - '3E' // 6117 #82 - 'A' // 6118 - '60B' // 6119 #1561 - '154L' // 611a #4015 - '243Y' // 611b #6342 - '123U' // 611c #3218 - 'A' // 611d - '3E' // 611e #82 - '68W' // 611f #1790 - '34W' // 6120 #906 - '3E' // 6121 #82 - '60B' // 6122 #1561 - '47J' // 6123 #1231 - '2Y' // 6124 #76 - 'A' // 6125 - '10L' // 6126 #271 - '146P' // 6127 #3811 - '34W' // 6128 #906 - '47J' // 6129 #1231 - '36F' // 612a #941 - 'a34W' // 612b-612c #906 - '10L' // 612d #271 - 'a47J' // 612e-612f #1231 - '60A' // 6130 #1560 - '3E' // 6131 #82 - 'aA' // 6132-6133 - '107K' // 6134 #2792 - '3E' // 6135 #82 - '34W' // 6136 #906 - '123T' // 6137 #3217 - 'A' // 6138 - '3E' // 6139 #82 - '36F' // 613a #941 - 'A' // 613b - '250B' // 613c #6501 - '34W' // 613d #906 - '60A' // 613e #1560 - '107J' // 613f #2791 - 'A' // 6140 - '3E' // 6141 #82 - '106Y' // 6142 #2780 - 'A' // 6143 - '47G' // 6144 #1228 - '3E' // 6145 #82 - '15P' // 6146 #405 - '47G' // 6147 #1228 - '188D' // 6148 #4891 - '18Y' // 6149 #492 - '47G' // 614a #1228 - '230P' // 614b #5995 - '153X' // 614c #4001 - '18Y' // 614d #492 - '175O' // 614e #4564 - 'A' // 614f - '59X' // 6150 #1557 - '10L' // 6151 #271 - '30G' // 6152 #786 - '14K' // 6153 #374 - 'A' // 6154 - '180B' // 6155 #4681 - 'aA' // 6156-6157 - '186G' // 6158 #4842 - '59Y' // 6159 #1558 - '15P' // 615a #405 - 'A' // 615b - '47I' // 615c #1230 - '14K' // 615d #374 - '18Y' // 615e #492 - '14K' // 615f #374 - '20S' // 6160 #538 - '10L' // 6161 #271 - '211A' // 6162 #5486 - '197Y' // 6163 #5146 - '14K' // 6164 #374 - '15P' // 6165 #405 - 'A' // 6166 - '214X' // 6167 #5587 - '141V' // 6168 #3687 - 'A' // 6169 - '30G' // 616a #786 - '14K' // 616b #374 - '15P' // 616c #405 - 'A' // 616d - '197T' // 616e #5141 - '18Y' // 616f #492 - '174Y' // 6170 #4548 - '15P' // 6171 #405 - '18Y' // 6172 #492 - 'b15P' // 6173-6175 #405 - '209Q' // 6176 #5450 - '14K' // 6177 #374 - '3E' // 6178 #82 - 'A' // 6179 - '30G' // 617a #786 - '3E' // 617b #82 - '18Y' // 617c #492 - '47F' // 617d #1227 - '171P' // 617e #4461 - '3E' // 617f #82 - '18Y' // 6180 #492 - '47F' // 6181 #1227 - '195Y' // 6182 #5094 - 'a3E' // 6183-6184 #82 - 'aA' // 6185-6186 - '15P' // 6187 #405 - 'aA' // 6188-6189 - '14K' // 618a #374 - '107A' // 618b #2782 - '30G' // 618c #786 - '15P' // 618d #405 - '107D' // 618e #2785 - 'A' // 618f - '159U' // 6190 #4154 - '187P' // 6191 #4877 - 'a18Y' // 6192-6193 #492 - '14K' // 6194 #374 - '59X' // 6195 #1557 - 'a3E' // 6196-6197 #82 - '47F' // 6198 #1227 - 'a14K' // 6199-619a #374 - '30G' // 619b #786 - '106Z' // 619c #2781 - '3E' // 619d #82 - 'A' // 619e - '15P' // 619f #405 - '3E' // 61a0 #82 - '47I' // 61a1 #1230 - 'A' // 61a2 - '10L' // 61a3 #271 - '159N' // 61a4 #4147 - '3E' // 61a5 #82 - 'A' // 61a6 - '107F' // 61a7 #2787 - '15P' // 61a8 #405 - '143C' // 61a9 #3720 - '18Y' // 61aa #492 - 'a14K' // 61ab-61ac #374 - '15P' // 61ad #405 - '59Y' // 61ae #1558 - '30G' // 61af #786 - 'aA' // 61b0-61b1 - '175K' // 61b2 #4560 - '10L' // 61b3 #271 - 'A' // 61b4 - '10L' // 61b5 #271 - '204M' // 61b6 #5316 - '47H' // 61b7 #1229 - '18Y' // 61b8 #492 - '20S' // 61b9 #538 - '14K' // 61ba #374 - '10L' // 61bb #271 - '3E' // 61bc #82 - 'A' // 61bd - '158G' // 61be #4114 - '47I' // 61bf #1230 - '20S' // 61c0 #538 - '3E' // 61c1 #82 - '207N' // 61c2 #5395 - '14K' // 61c3 #374 - '10L' // 61c4 #271 - 'A' // 61c5 - '15P' // 61c6 #405 - '136S' // 61c7 #3554 - '123S' // 61c8 #3216 - '240P' // 61c9 #6255 - 'a14K' // 61ca-61cb #374 - 'a15Q' // 61cc-61cd #406 - '3E' // 61ce #82 - '20S' // 61cf #538 - '107G' // 61d0 #2788 - '10L' // 61d1 #271 - '2L' // 61d2 #63 - '47H' // 61d3 #1229 - '10L' // 61d4 #271 - '3E' // 61d5 #82 - 'A' // 61d6 - '10L' // 61d7 #271 - 'aA' // 61d8-61d9 - '47H' // 61da #1229 - 'A' // 61db - 'a3E' // 61dc-61dd #82 - '15Q' // 61de #406 - '26J' // 61df #685 - '39O' // 61e0 #1028 - '36F' // 61e1 #941 - '20S' // 61e2 #538 - '15Q' // 61e3 #406 - 'A' // 61e4 - '3E' // 61e5 #82 - '34U' // 61e6 #904 - '3E' // 61e7 #82 - '15Q' // 61e8 #406 - '3E' // 61e9 #82 - 'aA' // 61ea-61eb - '3E' // 61ec #82 - '15Q' // 61ed #406 - '39O' // 61ee #1028 - '3E' // 61ef #82 - '10L' // 61f0 #271 - 'A' // 61f1 - '148H' // 61f2 #3855 - 'A' // 61f3 - '3E' // 61f4 #82 - '59Z' // 61f5 #1559 - '193N' // 61f6 #5031 - '214P' // 61f7 #5579 - '175T' // 61f8 #4569 - '34V' // 61f9 #905 - '34U' // 61fa #904 - 'A' // 61fb - '158F' // 61fc #4113 - 'a26J' // 61fd-61fe #685 - '128Z' // 61ff #3353 - '207M' // 6200 #5394 - '3E' // 6201 #82 - 'A' // 6202 - 'a3E' // 6203-6204 #82 - 'A' // 6205 - '10L' // 6206 #271 - '34U' // 6207 #904 - '146O' // 6208 #3810 - '26J' // 6209 #685 - '146N' // 620a #3809 - '10L' // 620b #271 - '147V' // 620c #3843 - 'a34U' // 620d-620e #904 - '1Z' // 620f #51 - '41D' // 6210 #1069 - '246I' // 6211 #6404 - '181G' // 6212 #4712 - '36F' // 6213 #941 - 'a26J' // 6214-6215 #685 - '240Q' // 6216 #6256 - '10L' // 6217 #271 - '1Z' // 6218 #51 - '39O' // 6219 #1028 - '153T' // 621a #3997 - '26J' // 621b #685 - 'b36F' // 621c-621e #941 - '107C' // 621f #2784 - '26J' // 6220 #685 - '34U' // 6221 #904 - 'a26J' // 6222-6223 #685 - 'A' // 6224 - '39O' // 6225 #1028 - '259E' // 6226 #6738 - '15Q' // 6227 #406 - 'A' // 6228 - '15Q' // 6229 #406 - '193O' // 622a #5032 - '15Q' // 622b #406 - '39N' // 622c #1027 - 'A' // 622d - '34T' // 622e #903 - '70X' // 622f #1843 - '225O' // 6230 #5864 - '249Z' // 6231 #6499 - '232P' // 6232 #6047 - '140E' // 6233 #3644 - '201M' // 6234 #5238 - 'A' // 6235 - '225P' // 6236 #5865 - '35T' // 6237 #929 - '70Y' // 6238 #1844 - '20S' // 6239 #538 - 'A' // 623a - '259Q' // 623b #6750 - 'A' // 623c - '16U' // 623d #436 - '34T' // 623e #903 - '229A' // 623f #5954 - '41D' // 6240 #1069 - '165A' // 6241 #4290 - '15Q' // 6242 #406 - '16U' // 6243 #436 - '3E' // 6244 #82 - 'A' // 6245 - '16U' // 6246 #436 - '175C' // 6247 #4552 - '34T' // 6248 #903 - '107E' // 6249 #2786 - 'A' // 624a - '35V' // 624b #931 - '16U' // 624c #436 - '233S' // 624d #6076 - '165C' // 624e #4292 - 'A' // 624f - '3E' // 6250 #82 - '107B' // 6251 #2783 - '146Q' // 6252 #3812 - '234P' // 6253 #6099 - '129A' // 6254 #3354 - '258H' // 6255 #6715 - '3E' // 6256 #82 - 'A' // 6257 - '193P' // 6258 #5033 - '39N' // 6259 #1027 - '15Q' // 625a #406 - '134F' // 625b #3489 - '3E' // 625c #82 - 'A' // 625d - '16U' // 625e #436 - 'A' // 625f - 'a16U' // 6260-6261 #436 - '34V' // 6262 #905 - '207O' // 6263 #5396 - '3E' // 6264 #82 - 'a39N' // 6265-6266 #1027 - '3I' // 6267 #86 - '20S' // 6268 #538 - '3N' // 6269 #91 - '11M' // 626a #298 - '2D' // 626b #55 - '3N' // 626c #91 - '171Q' // 626d #4462 - '187C' // 626e #4864 - '165B' // 626f #4291 - '107I' // 6270 #2790 - '107H' // 6271 #2789 - '34V' // 6272 #905 - '59Z' // 6273 #1559 - 'aA' // 6274-6275 - '173U' // 6276 #4518 - 'aA' // 6277-6278 - '209S' // 6279 #5452 - '16U' // 627a #436 - '15Q' // 627b #406 - '34T' // 627c #903 - '16U' // 627d #436 - '232Q' // 627e #6048 - '216G' // 627f #5622 - '235V' // 6280 #6131 - 'A' // 6281 - '20S' // 6282 #538 - '16U' // 6283 #436 - '152X' // 6284 #3975 - '15Q' // 6285 #406 - '39N' // 6286 #1027 - 'aA' // 6287-6288 - '34T' // 6289 #903 - '226X' // 628a #5899 - 'A' // 628b - '34V' // 628c #905 - '3E' // 628d #82 - '16U' // 628e #436 - '3E' // 628f #82 - '20S' // 6290 #538 - '168Z' // 6291 #4393 - '123R' // 6292 #3215 - '193R' // 6293 #5035 - '16U' // 6294 #436 - '231N' // 6295 #6019 - '151P' // 6296 #3941 - '210K' // 6297 #5470 - '220S' // 6298 #5738 - '3E' // 6299 #82 - '2Y' // 629a #76 - '250A' // 629b #6500 - '70Y' // 629c #1844 - '34V' // 629d #905 - '259D' // 629e #6737 - '11M' // 629f #298 - '2K' // 62a0 #62 - '11M' // 62a1 #298 - '3I' // 62a2 #86 - 'A' // 62a3 - '64L' // 62a4 #1675 - '7K' // 62a5 #192 - '39L' // 62a6 #1025 - 'A' // 62a7 - '34S' // 62a8 #902 - 'aA' // 62a9-62aa - '176C' // 62ab #4578 - '158H' // 62ac #4115 - 'aA' // 62ad-62ae - '47E' // 62af #1226 - 'A' // 62b0 - '211E' // 62b1 #5490 - 'A' // 62b2 - '39L' // 62b3 #1025 - 'A' // 62b4 - '202R' // 62b5 #5269 - '39L' // 62b6 #1025 - '3E' // 62b7 #82 - 'A' // 62b8 - '180Y' // 62b9 #4704 - '250C' // 62ba #6502 - '34S' // 62bb #902 - '162F' // 62bc #4217 - '209R' // 62bd #5451 - '39L' // 62be #1025 - '34S' // 62bf #902 - 'A' // 62c0 - '30F' // 62c1 #785 - '59W' // 62c2 #1556 - '30F' // 62c3 #785 - '34S' // 62c4 #902 - '106T' // 62c5 #2775 - '193Q' // 62c6 #5034 - '123Q' // 62c7 #3214 - '59W' // 62c8 #1556 - '226P' // 62c9 #5891 - '34S' // 62ca #902 - '177A' // 62cb #4602 - '167J' // 62cc #4351 - '233J' // 62cd #6067 - '140D' // 62ce #3643 - '59V' // 62cf #1555 - '135N' // 62d0 #3523 - '34R' // 62d1 #901 - '196Q' // 62d2 #5112 - '168I' // 62d3 #4376 - '186D' // 62d4 #4839 - '106K' // 62d5 #2766 - '67M' // 62d6 #1754 - '106N' // 62d7 #2769 - '168E' // 62d8 #4372 - '106M' // 62d9 #2768 - '164X' // 62da #4287 - '219H' // 62db #5701 - '67M' // 62dc #1754 - '70X' // 62dd #1843 - 'A' // 62de - '106W' // 62df #2778 - '256I' // 62e0 #6664 - '257O' // 62e1 #6696 - '3G' // 62e2 #84 - 'a11M' // 62e3-62e4 #298 - '106X' // 62e5 #2779 - '3H' // 62e6 #85 - '11M' // 62e7 #298 - '2L' // 62e8 #63 - '1Z' // 62e9 #51 - '4A' // 62ea #104 - '11M' // 62eb #298 - '210L' // 62ec #5471 - '142T' // 62ed #3711 - '59V' // 62ee #1555 - '146L' // 62ef #3807 - '11M' // 62f0 #298 - '134C' // 62f1 #3486 - '4A' // 62f2 #104 - '174V' // 62f3 #4545 - 'a34R' // 62f4-62f5 #901 - '106R' // 62f6 #2773 - '106P' // 62f7 #2771 - 'c47E' // 62f8-62fb #1226 - '200N' // 62fc #5213 - '106I' // 62fd #2764 - '167O' // 62fe #4356 - '217O' // 62ff #5656 - '47E' // 6300 #1226 - '238J' // 6301 #6197 - '106J' // 6302 #2765 - 'a4A' // 6303-6304 #104 - 'aA' // 6305-6306 - '238I' // 6307 #6196 - '34R' // 6308 #901 - '225N' // 6309 #5863 - 'a31L' // 630a-630b #817 - 'a34R' // 630c-630d #901 - '106U' // 630e #2776 - 'A' // 630f - '34R' // 6310 #901 - '211F' // 6311 #5491 - '11M' // 6312 #298 - '8X' // 6313 #231 - 'aA' // 6314-6315 - '178Y' // 6316 #4652 - '11M' // 6317 #298 - '59U' // 6318 #1554 - '256U' // 6319 #6676 - '3X' // 631a #101 - '31L' // 631b #817 - 'A' // 631c - 'a11M' // 631d-631e #298 - '70W' // 631f #1842 - '3W' // 6320 #100 - '2L' // 6321 #63 - '11M' // 6322 #298 - '3H' // 6323 #85 - '2Y' // 6324 #76 - '3N' // 6325 #91 - '11M' // 6326 #298 - '4A' // 6327 #104 - '149E' // 6328 #3878 - '31L' // 6329 #817 - '151O' // 632a #3940 - '153H' // 632b #3985 - 'A' // 632c - '18X' // 632d #491 - '26I' // 632e #684 - '191Q' // 632f #4982 - 'A' // 6330 - '47D' // 6331 #1225 - '8X' // 6332 #231 - 'A' // 6333 - '26H' // 6334 #683 - 'a8X' // 6335-6336 #231 - '26H' // 6337 #683 - '26I' // 6338 #684 - '8X' // 6339 #231 - '186E' // 633a #4840 - '39K' // 633b #1024 - '8X' // 633c #231 - '148C' // 633d #3850 - '39K' // 633e #1024 - '255X' // 633f #6653 - '26H' // 6340 #683 - '4A' // 6341 #104 - '106G' // 6342 #2762 - '8X' // 6343 #231 - '18X' // 6344 #491 - '106V' // 6345 #2777 - '106H' // 6346 #2763 - '11M' // 6347 #298 - 'A' // 6348 - '167C' // 6349 #4344 - '4A' // 634a #104 - '8X' // 634b #231 - '39K' // 634c #1024 - '128X' // 634d #3351 - '8X' // 634e #231 - '153A' // 634f #3978 - '178W' // 6350 #4650 - 'A' // 6351 - 'a4A' // 6352-6353 #104 - '31L' // 6354 #817 - '190I' // 6355 #4948 - 'A' // 6356 - '106Q' // 6357 #2772 - 'a4A' // 6358-6359 #104 - '26I' // 635a #684 - '4A' // 635b #104 - '70W' // 635c #1842 - '47D' // 635d #1225 - '2K' // 635e #62 - '3Y' // 635f #102 - 'A' // 6360 - '3H' // 6361 #85 - '1Z' // 6362 #51 - '3G' // 6363 #84 - '30F' // 6364 #785 - '18X' // 6365 #491 - '4A' // 6366 #104 - '166P' // 6367 #4331 - '176F' // 6368 #4581 - '8X' // 6369 #231 - 'A' // 636a - 'a18X' // 636b-636c #491 - '8X' // 636d #231 - '160O' // 636e #4174 - 'a26H' // 636f-6370 #683 - '63G' // 6371 #1644 - '193M' // 6372 #5030 - 'A' // 6373 - '4A' // 6374 #104 - '18X' // 6375 #491 - '8X' // 6376 #231 - '67U' // 6377 #1762 - '4A' // 6378 #104 - '30F' // 6379 #785 - '39K' // 637a #1024 - '106O' // 637b #2770 - '4A' // 637c #104 - '8X' // 637d #231 - 'A' // 637e - '106E' // 637f #2760 - '171O' // 6380 #4460 - '26I' // 6381 #684 - '63G' // 6382 #1644 - '205F' // 6383 #5335 - '18X' // 6384 #491 - 'aA' // 6385-6386 - '8X' // 6387 #231 - '211H' // 6388 #5493 - '67U' // 6389 #1762 - '8X' // 638a #231 - '47D' // 638b #1225 - '208Q' // 638c #5424 - '26H' // 638d #683 - '8X' // 638e #231 - '140C' // 638f #3642 - '106F' // 6390 #2761 - '11M' // 6391 #298 - '233R' // 6392 #6075 - '11M' // 6393 #298 - '8X' // 6394 #231 - '31L' // 6395 #817 - '106D' // 6396 #2759 - '26H' // 6397 #683 - '161T' // 6398 #4205 - '140B' // 6399 #3641 - '4A' // 639a #104 - '221J' // 639b #5755 - 'aA' // 639c-639d - '8X' // 639e #231 - '106L' // 639f #2767 - '134B' // 63a0 #3485 - '216N' // 63a1 #5629 - '214C' // 63a2 #5566 - 'a8X' // 63a3-63a4 #231 - '236A' // 63a5 #6136 - '4A' // 63a6 #104 - '219G' // 63a7 #5700 - '241K' // 63a8 #6276 - '158D' // 63a9 #4111 - '188R' // 63aa #4905 - '4A' // 63ab #104 - 'b8X' // 63ac-63ae #231 - '18X' // 63af #491 - '149T' // 63b0 #3893 - '26H' // 63b1 #683 - '259T' // 63b2 #6753 - '11M' // 63b3 #298 - '254D' // 63b4 #6607 - '31L' // 63b5 #817 - 'A' // 63b6 - '2W' // 63b7 #74 - '14Y' // 63b8 #388 - '39M' // 63b9 #1026 - '14Y' // 63ba #388 - '252R' // 63bb #6569 - '14Y' // 63bc #388 - '18X' // 63bd #491 - '18W' // 63be #490 - 'A' // 63bf - '140A' // 63c0 #3640 - '59U' // 63c1 #1554 - 'A' // 63c2 - '106S' // 63c3 #2774 - '47C' // 63c4 #1224 - '4A' // 63c5 #104 - '64H' // 63c6 #1671 - 'A' // 63c7 - '18W' // 63c8 #490 - '148W' // 63c9 #3870 - 'bA' // 63ca-63cc - '39M' // 63cd #1026 - '18W' // 63ce #490 - '205C' // 63cf #5332 - '242R' // 63d0 #6309 - '18W' // 63d1 #490 - '200O' // 63d2 #5214 - 'b4A' // 63d3-63d5 #104 - '47C' // 63d6 #1224 - 'bA' // 63d7-63d9 - '197J' // 63da #5131 - '230I' // 63db #5988 - '18X' // 63dc #491 - 'A' // 63dd - '39M' // 63de #1026 - 'A' // 63df - '18W' // 63e0 #490 - '67Q' // 63e1 #1758 - '30F' // 63e2 #785 - '18W' // 63e3 #490 - '26I' // 63e4 #684 - '4A' // 63e5 #104 - '26I' // 63e6 #684 - 'aA' // 63e7-63e8 - '18W' // 63e9 #490 - '164W' // 63ea #4286 - 'a4A' // 63eb-63ec #104 - '193L' // 63ed #5029 - '196X' // 63ee #5119 - 'A' // 63ef - '26I' // 63f0 #684 - 'A' // 63f1 - '18W' // 63f2 #490 - '18X' // 63f3 #491 - '213I' // 63f4 #5546 - '18W' // 63f5 #490 - '47C' // 63f6 #1224 - '249Y' // 63f7 #6498 - '18W' // 63f8 #490 - '134D' // 63f9 #3487 - '255J' // 63fa #6639 - '30F' // 63fb #785 - '39M' // 63fc #1026 - '2K' // 63fd #62 - '30E' // 63fe #784 - 'a14Y' // 63ff-6400 #388 - 'a3W' // 6401-6402 #100 - 'aA' // 6403-6404 - '3H' // 6405 #85 - '30D' // 6406 #783 - '26G' // 6407 #682 - 'A' // 6408 - 'a59S' // 6409-640a #1552 - 'a30E' // 640b-640c #784 - '205A' // 640d #5330 - 'A' // 640e - '146K' // 640f #3806 - '30D' // 6410 #783 - 'A' // 6411 - '4A' // 6412 #104 - '134E' // 6413 #3488 - '59R' // 6414 #1551 - '59T' // 6415 #1553 - '193J' // 6416 #5027 - '64H' // 6417 #1671 - '59S' // 6418 #1552 - 'aA' // 6419-641a - '30E' // 641b #784 - '240L' // 641c #6251 - 'A' // 641d - '207L' // 641e #5393 - '59T' // 641f #1553 - '30D' // 6420 #783 - '30E' // 6421 #784 - '59R' // 6422 #1551 - '30E' // 6423 #784 - '4A' // 6424 #104 - 'a30D' // 6425-6426 #783 - '30E' // 6427 #784 - '30D' // 6428 #783 - '4A' // 6429 #104 - '30D' // 642a #783 - '13D' // 642b #341 - '188N' // 642c #4901 - '219M' // 642d #5706 - 'A' // 642e - 'a20R' // 642f-6430 #537 - '14Y' // 6431 #388 - '10X' // 6432 #283 - 'A' // 6433 - '10W' // 6434 #282 - '49B' // 6435 #1275 - '207K' // 6436 #5392 - '10X' // 6437 #283 - '26G' // 6438 #682 - 'A' // 6439 - '106C' // 643a #2758 - '26G' // 643b #682 - 'A' // 643c - '10W' // 643d #282 - '106B' // 643e #2757 - '10W' // 643f #282 - 'a10X' // 6440-6441 #283 - '254X' // 6442 #6627 - '13D' // 6443 #341 - '2C' // 6444 #54 - '14Y' // 6445 #388 - '3N' // 6446 #91 - '1R' // 6447 #43 - '14Y' // 6448 #388 - 'A' // 6449 - '2R' // 644a #69 - '20R' // 644b #537 - 'aA' // 644c-644d - '20R' // 644e #537 - '4A' // 644f #104 - '10X' // 6450 #283 - 'a10W' // 6451-6452 #282 - '20R' // 6453 #537 - '164Y' // 6454 #4288 - 'bA' // 6455-6457 - '197D' // 6458 #5125 - '13D' // 6459 #341 - '105W' // 645a #2752 - 'a10W' // 645b-645c #282 - '70G' // 645d #1826 - '10X' // 645e #283 - '123O' // 645f #3212 - '105Y' // 6460 #2754 - '20R' // 6461 #537 - '14Y' // 6462 #388 - '4A' // 6463 #104 - 'A' // 6464 - '10X' // 6465 #283 - 'A' // 6466 - '128Y' // 6467 #3352 - '10X' // 6468 #283 - '219F' // 6469 #5699 - 'aA' // 646a-646b - '13D' // 646c #341 - '10W' // 646d #282 - '13D' // 646e #341 - '141O' // 646f #3680 - '10X' // 6470 #283 - '47B' // 6471 #1223 - '13D' // 6472 #341 - '10W' // 6473 #282 - '20R' // 6474 #537 - '13D' // 6475 #341 - '20R' // 6476 #537 - '10X' // 6477 #283 - '178V' // 6478 #4649 - '34Q' // 6479 #900 - '179W' // 647a #4676 - '10W' // 647b #282 - '47B' // 647c #1223 - '10W' // 647d #282 - 'cA' // 647e-6481 - '10X' // 6482 #283 - '257Y' // 6483 #6706 - '14Y' // 6484 #388 - '10W' // 6485 #282 - 'A' // 6486 - '49B' // 6487 #1275 - '151N' // 6488 #3939 - 'aA' // 6489-648a - '13D' // 648b #341 - '10X' // 648c #283 - '26G' // 648d #682 - 'A' // 648e - '4A' // 648f #104 - '178X' // 6490 #4651 - '105X' // 6491 #2753 - '173F' // 6492 #4503 - '34Q' // 6493 #900 - 'A' // 6494 - '158E' // 6495 #4112 - 'a10X' // 6496-6497 #283 - 'a10W' // 6498-6499 #282 - '34Q' // 649a #900 - '4A' // 649b #104 - 'A' // 649c - '10W' // 649d #282 - '193K' // 649e #5028 - '20R' // 649f #537 - '10X' // 64a0 #283 - '4A' // 64a1 #104 - '13D' // 64a2 #341 - '20R' // 64a3 #537 - '167N' // 64a4 #4355 - '187O' // 64a5 #4876 - '4A' // 64a6 #104 - 'A' // 64a7 - '4A' // 64a8 #104 - '146M' // 64a9 #3808 - 'A' // 64aa - '160L' // 64ab #4171 - '10W' // 64ac #282 - '218G' // 64ad #5674 - '126F' // 64ae #3281 - '26G' // 64af #682 - '172O' // 64b0 #4486 - '10X' // 64b1 #283 - '168D' // 64b2 #4371 - '10W' // 64b3 #282 - '10X' // 64b4 #283 - '14Y' // 64b5 #388 - '47B' // 64b6 #1223 - '14Y' // 64b7 #388 - '2R' // 64b8 #69 - '254G' // 64b9 #6610 - '14Y' // 64ba #388 - '34Q' // 64bb #900 - '164Z' // 64bc #4289 - '4A' // 64bd #104 - '10W' // 64be #282 - '49B' // 64bf #1275 - '10X' // 64c0 #283 - '208S' // 64c1 #5426 - '123P' // 64c2 #3213 - '13D' // 64c3 #341 - '34Q' // 64c4 #900 - '164V' // 64c5 #4285 - 'A' // 64c6 - '68I' // 64c7 #1776 - 'A' // 64c8 - '70G' // 64c9 #1826 - '68I' // 64ca #1776 - '186F' // 64cb #4841 - '4A' // 64cc #104 - '204S' // 64cd #5322 - '164U' // 64ce #4284 - 'A' // 64cf - '10W' // 64d0 #282 - '4A' // 64d1 #104 - '105V' // 64d2 #2751 - '26G' // 64d3 #682 - '214O' // 64d4 #5578 - '3K' // 64d5 #88 - 'A' // 64d6 - '39I' // 64d7 #1022 - '59P' // 64d8 #1549 - 'A' // 64d9 - '225L' // 64da #5861 - 'aA' // 64db-64dc - '26G' // 64dd #682 - '14Y' // 64de #388 - 'A' // 64df - '171N' // 64e0 #4459 - '59P' // 64e1 #1549 - '105Z' // 64e2 #2755 - '18V' // 64e3 #489 - '39I' // 64e4 #1022 - '105U' // 64e5 #2750 - '181J' // 64e6 #4715 - '59Q' // 64e7 #1550 - 'A' // 64e8 - '39I' // 64e9 #1022 - '46Z' // 64ea #1221 - 'A' // 64eb - '195F' // 64ec #5075 - '18V' // 64ed #489 - 'A' // 64ee - '18V' // 64ef #489 - '39I' // 64f0 #1022 - '128W' // 64f1 #3350 - '48X' // 64f2 #1271 - '13D' // 64f3 #341 - '193H' // 64f4 #5025 - 'a3K' // 64f5-64f6 #88 - '64G' // 64f7 #1670 - '39J' // 64f8 #1023 - 'A' // 64f9 - '67L' // 64fa #1753 - '18V' // 64fb #489 - '39J' // 64fc #1023 - '3K' // 64fd #88 - '67L' // 64fe #1753 - '23H' // 64ff #605 - '151L' // 6500 #3937 - '3K' // 6501 #88 - 'A' // 6502 - '8S' // 6503 #226 - '59Q' // 6504 #1550 - '3K' // 6505 #88 - '13D' // 6506 #341 - 'A' // 6507 - '3K' // 6508 #88 - '23H' // 6509 #605 - '46Z' // 650a #1221 - 'cA' // 650b-650e - '64G' // 650f #1670 - 'A' // 6510 - '13D' // 6511 #341 - '3W' // 6512 #100 - '3K' // 6513 #88 - '146J' // 6514 #3805 - 'A' // 6515 - '18V' // 6516 #489 - 'A' // 6517 - '47A' // 6518 #1222 - '18V' // 6519 #489 - 'A' // 651a - '18V' // 651b #489 - '193I' // 651c #5026 - '68C' // 651d #1770 - '123N' // 651e #3211 - '46Z' // 651f #1221 - 'a13D' // 6520-6521 #341 - '23H' // 6522 #605 - '106A' // 6523 #2756 - '178U' // 6524 #4648 - '39J' // 6525 #1023 - '18V' // 6526 #489 - 'aA' // 6527-6528 - '18V' // 6529 #489 - '151J' // 652a #3935 - '47A' // 652b #1222 - '65G' // 652c #1696 - 'A' // 652d - '23H' // 652e #605 - '231B' // 652f #6007 - '39J' // 6530 #1023 - '14R' // 6531 #381 - '23H' // 6532 #605 - 'A' // 6533 - 'a23H' // 6534-6535 #605 - '239X' // 6536 #6237 - 'a47A' // 6537-6538 #1222 - '235U' // 6539 #6130 - '14R' // 653a #381 - '220R' // 653b #5737 - '14R' // 653c #381 - '23H' // 653d #605 - '236T' // 653e #6155 - '234Z' // 653f #6109 - 'aA' // 6540-6541 - '8S' // 6542 #226 - '18V' // 6543 #489 - '3K' // 6544 #88 - '228Q' // 6545 #5944 - 'A' // 6546 - '3K' // 6547 #88 - '225M' // 6548 #5862 - '23H' // 6549 #605 - 'a8S' // 654a-654b #226 - '1R' // 654c #43 - '105E' // 654d #2734 - '249X' // 654e #6497 - '203I' // 654f #5286 - '3K' // 6550 #88 - '210Z' // 6551 #5485 - '3K' // 6552 #88 - '8S' // 6553 #226 - 'a59O' // 6554-6555 #1548 - '151K' // 6556 #3936 - '211K' // 6557 #5496 - '164S' // 6558 #4282 - '238D' // 6559 #6191 - '34P' // 655a #899 - '3W' // 655b #100 - 'A' // 655c - '59O' // 655d #1548 - '146H' // 655e #3803 - '39F' // 655f #1019 - '3K' // 6560 #88 - 'A' // 6561 - '201P' // 6562 #5241 - '212E' // 6563 #5516 - '105Q' // 6564 #2746 - '39H' // 6565 #1021 - '188J' // 6566 #4897 - '16T' // 6567 #435 - 'A' // 6568 - '8S' // 6569 #226 - 'A' // 656a - '6Z' // 656b #181 - '67Q' // 656c #1758 - '105M' // 656d #2742 - 'a8S' // 656e-656f #226 - '260I' // 6570 #6768 - '8S' // 6571 #226 - '66K' // 6572 #1726 - '23G' // 6573 #604 - '235Q' // 6574 #6126 - '191H' // 6575 #4973 - '23G' // 6576 #604 - '162A' // 6577 #4212 - '239W' // 6578 #6236 - '23G' // 6579 #604 - '6Z' // 657a #181 - '23G' // 657b #604 - '8S' // 657c #226 - '14R' // 657d #381 - '260Z' // 657e #6785 - 'aA' // 657f-6580 - '6Z' // 6581 #181 - '48X' // 6582 #1271 - '65G' // 6583 #1696 - '3K' // 6584 #88 - '59N' // 6585 #1547 - '23G' // 6586 #604 - '246W' // 6587 #6418 - '16T' // 6588 #435 - '105K' // 6589 #2740 - '3K' // 658a #88 - '105R' // 658b #2747 - '146I' // 658c #3804 - 'A' // 658d - '255I' // 658e #6638 - '8S' // 658f #226 - '142R' // 6590 #3709 - '180R' // 6591 #4697 - '14R' // 6592 #381 - '8S' // 6593 #226 - 'A' // 6594 - '6Z' // 6595 #181 - '8S' // 6596 #226 - '189J' // 6597 #4923 - '3K' // 6598 #88 - '244Y' // 6599 #6368 - 'A' // 659a - '20Q' // 659b #536 - '182A' // 659c #4732 - '6Z' // 659d #181 - 'A' // 659e - '20Q' // 659f #536 - '6Z' // 65a0 #181 - '20Q' // 65a1 #536 - 'A' // 65a2 - '14R' // 65a3 #381 - '178T' // 65a4 #4647 - '66K' // 65a5 #1726 - '3K' // 65a6 #88 - '135K' // 65a7 #3520 - 'A' // 65a8 - '2R' // 65a9 #69 - 'A' // 65aa - '20Q' // 65ab #536 - '161F' // 65ac #4191 - '259N' // 65ad #6747 - '3K' // 65ae #88 - '226H' // 65af #5883 - '247D' // 65b0 #6425 - 'A' // 65b1 - '6Z' // 65b2 #181 - '16T' // 65b3 #435 - '3K' // 65b4 #88 - '16T' // 65b5 #435 - '8S' // 65b6 #226 - '68C' // 65b7 #1770 - '3K' // 65b8 #88 - '49H' // 65b9 #1281 - '8S' // 65ba #226 - '23G' // 65bb #604 - '246C' // 65bc #6398 - '216O' // 65bd #5630 - 'a6Z' // 65be-65bf #181 - '8S' // 65c0 #226 - '200M' // 65c1 #5212 - 'b6Z' // 65c2-65c4 #181 - '236M' // 65c5 #6148 - '6Z' // 65c6 #181 - '8S' // 65c7 #226 - '14R' // 65c8 #381 - '3K' // 65c9 #88 - 'A' // 65ca - '195T' // 65cb #5089 - '20Q' // 65cc #536 - 'A' // 65cd - '6Z' // 65ce #181 - '222O' // 65cf #5786 - '14R' // 65d0 #381 - '34P' // 65d1 #899 - '20Q' // 65d2 #536 - '8S' // 65d3 #226 - '39F' // 65d4 #1019 - 'A' // 65d5 - '6Z' // 65d6 #181 - '201Y' // 65d7 #5250 - 'a3K' // 65d8-65d9 #88 - 'A' // 65da - '6Z' // 65db #181 - 'A' // 65dc - '8S' // 65dd #226 - 'A' // 65de - '3K' // 65df #88 - '151M' // 65e0 #3938 - '6Z' // 65e1 #181 - '198H' // 65e2 #5155 - '20Q' // 65e3 #536 - 'A' // 65e4 - '247J' // 65e5 #6431 - '183F' // 65e6 #4763 - '257B' // 65e7 #6683 - '168U' // 65e8 #4388 - '230X' // 65e9 #6003 - 'aA' // 65ea-65eb - '155E' // 65ec #4034 - '168L' // 65ed #4379 - 'a23G' // 65ee-65ef #604 - '6Z' // 65f0 #181 - '123M' // 65f1 #3210 - '16T' // 65f2 #435 - '23G' // 65f3 #604 - '20Q' // 65f4 #536 - '14R' // 65f5 #381 - '7K' // 65f6 #192 - '3W' // 65f7 #100 - '8S' // 65f8 #226 - '3K' // 65f9 #88 - '194U' // 65fa #5064 - '48X' // 65fb #1271 - '20Q' // 65fc #536 - '105N' // 65fd #2743 - '14R' // 65fe #381 - '59N' // 65ff #1547 - '6Z' // 6600 #181 - 'A' // 6601 - '159J' // 6602 #4143 - '6Z' // 6603 #181 - '16T' // 6604 #435 - '39H' // 6605 #1021 - '167I' // 6606 #4350 - '183L' // 6607 #4769 - '3K' // 6608 #88 - '39G' // 6609 #1020 - '123L' // 660a #3209 - '8S' // 660b #226 - '189I' // 660c #4922 - '16T' // 660d #435 - '244N' // 660e #6357 - '173E' // 660f #4502 - '105L' // 6610 #2741 - '39G' // 6611 #1020 - '3K' // 6612 #88 - '233Z' // 6613 #6083 - '169G' // 6614 #4400 - '128V' // 6615 #3349 - '3K' // 6616 #88 - 'A' // 6617 - '34P' // 6618 #899 - 'a8S' // 6619-661a #226 - 'A' // 661b - 'a6Z' // 661c-661d #181 - '39G' // 661e #1020 - '241Q' // 661f #6282 - '206H' // 6620 #5363 - '6Z' // 6621 #181 - '16T' // 6622 #435 - '39F' // 6623 #1019 - '6Z' // 6624 #181 - '68M' // 6625 #1780 - '6Z' // 6626 #181 - '148U' // 6627 #3868 - '198X' // 6628 #5171 - '3K' // 6629 #88 - '14R' // 662a #381 - '6Z' // 662b #181 - '14R' // 662c #381 - '242W' // 662d #6314 - '14R' // 662e #381 - '246G' // 662f #6402 - '39G' // 6630 #1020 - '64U' // 6631 #1684 - 'A' // 6632 - '26F' // 6633 #681 - '15O' // 6634 #404 - '105H' // 6635 #2737 - '15O' // 6636 #404 - '3K' // 6637 #88 - 'A' // 6638 - '16T' // 6639 #435 - '15O' // 663a #404 - '249W' // 663b #6496 - '257H' // 663c #6689 - '10K' // 663d #270 - '1Z' // 663e #51 - 'a3K' // 663f-6640 #88 - '15O' // 6641 #404 - '246Z' // 6642 #6421 - '154E' // 6643 #4008 - '15O' // 6644 #404 - '26F' // 6645 #681 - '3K' // 6646 #88 - 'A' // 6647 - '39F' // 6648 #1019 - '171M' // 6649 #4458 - '16T' // 664a #435 - '105J' // 664b #2739 - '26F' // 664c #681 - '34P' // 664d #899 - '3K' // 664e #88 - '64U' // 664f #1684 - 'A' // 6650 - '3K' // 6651 #88 - '160V' // 6652 #4181 - '105S' // 6653 #2748 - '10K' // 6654 #270 - '2L' // 6655 #63 - '2K' // 6656 #62 - '105G' // 6657 #2736 - '3K' // 6658 #88 - '15O' // 6659 #404 - '217N' // 665a #5655 - '15O' // 665b #404 - '3K' // 665c #88 - 'a15O' // 665d-665e #404 - '134A' // 665f #3484 - '105F' // 6660 #2735 - 'b26F' // 6661-6663 #681 - 'a15O' // 6664-6665 #404 - '105I' // 6666 #2738 - '15O' // 6667 #404 - '193G' // 6668 #5024 - '255F' // 6669 #6635 - '3K' // 666a #88 - '105D' // 666b #2733 - '26F' // 666c #681 - '3K' // 666d #88 - '222N' // 666e #5785 - '228Z' // 666f #5953 - '164T' // 6670 #4283 - '10K' // 6671 #270 - '39H' // 6672 #1021 - '15O' // 6673 #404 - '183T' // 6674 #4777 - '3K' // 6675 #88 - '202Y' // 6676 #5276 - 'a15O' // 6677-6678 #404 - '26F' // 6679 #681 - '227H' // 667a #5909 - '16T' // 667b #435 - '26F' // 667c #681 - '39H' // 667d #1021 - '105C' // 667e #2732 - '3U' // 667f #98 - '49U' // 6680 #1294 - '253N' // 6681 #6591 - '3I' // 6682 #86 - '3U' // 6683 #98 - '26E' // 6684 #680 - '105O' // 6685 #2744 - 'A' // 6686 - '131A' // 6687 #3406 - '164Q' // 6688 #4280 - '141X' // 6689 #3689 - 'A' // 668a - 'b46Y' // 668b-668d #1220 - '26E' // 668e #680 - 'A' // 668f - '26E' // 6690 #680 - '175S' // 6691 #4568 - '46Y' // 6692 #1220 - 'aA' // 6693-6694 - '10K' // 6695 #270 - '210F' // 6696 #5465 - '205E' // 6697 #5334 - '26E' // 6698 #680 - '3U' // 6699 #98 - '105B' // 669a #2731 - 'a3U' // 669b-669c #98 - '26E' // 669d #680 - 'A' // 669e - '46Y' // 669f #1220 - '26E' // 66a0 #680 - 'A' // 66a1 - '67I' // 66a2 #1750 - '10K' // 66a3 #270 - '59L' // 66a4 #1545 - 'A' // 66a5 - '70V' // 66a6 #1841 - '3W' // 66a7 #100 - '177B' // 66a8 #4603 - 'A' // 66a9 - '105P' // 66aa #2745 - '201L' // 66ab #5237 - 'A' // 66ac - '59L' // 66ad #1545 - '137J' // 66ae #3571 - 'aA' // 66af-66b0 - '66Y' // 66b1 #1740 - '26E' // 66b2 #680 - '105A' // 66b3 #2730 - '210Y' // 66b4 #5484 - '59M' // 66b5 #1546 - '34P' // 66b6 #899 - 'A' // 66b7 - '59M' // 66b8 #1546 - '46X' // 66b9 #1219 - '105T' // 66ba #2749 - '46X' // 66bb #1219 - '3U' // 66bc #98 - 'A' // 66bd - '46X' // 66be #1219 - '26C' // 66bf #678 - '3U' // 66c0 #98 - '49U' // 66c1 #1294 - 'a3U' // 66c2-66c3 #98 - '104N' // 66c4 #2717 - 'A' // 66c5 - '200K' // 66c6 #5210 - '104V' // 66c7 #2725 - '7P' // 66c8 #197 - '67I' // 66c9 #1750 - 'aA' // 66ca-66cb - '7P' // 66cc #197 - '39E' // 66cd #1018 - '26C' // 66ce #678 - '9W' // 66cf #256 - 'cA' // 66d0-66d3 - '3U' // 66d4 #98 - '10K' // 66d5 #270 - '135X' // 66d6 #3533 - '10K' // 66d7 #270 - '23F' // 66d8 #603 - '104T' // 66d9 #2723 - 'a7P' // 66da-66db #197 - '143O' // 66dc #3732 - '200L' // 66dd #5211 - '10K' // 66de #270 - '49U' // 66df #1294 - '133Y' // 66e0 #3482 - 'a10K' // 66e1-66e2 #270 - 'bA' // 66e3-66e5 - '128T' // 66e6 #3347 - '39E' // 66e7 #1018 - 'a7P' // 66e8-66e9 #197 - '10K' // 66ea #270 - '3U' // 66eb #98 - '66Y' // 66ec #1740 - 'A' // 66ed - '3U' // 66ee #98 - 'A' // 66ef - '130H' // 66f0 #3387 - '23F' // 66f1 #603 - '212S' // 66f2 #5530 - '147Y' // 66f3 #3846 - '244I' // 66f4 #6352 - '3U' // 66f5 #98 - 'A' // 66f6 - '18U' // 66f7 #488 - '239D' // 66f8 #6217 - '160E' // 66f9 #4164 - '249T' // 66fa #6493 - '3U' // 66fb #98 - '193F' // 66fc #5023 - '70V' // 66fd #1841 - '218B' // 66fe #5669 - '206C' // 66ff #5358 - '41F' // 6700 #1071 - '3U' // 6701 #98 - '46W' // 6702 #1218 - '246B' // 6703 #6397 - 'A' // 6704 - '7P' // 6705 #197 - 'A' // 6706 - '3U' // 6707 #98 - '247I' // 6708 #6430 - '69I' // 6709 #1802 - '23F' // 670a #603 - '226R' // 670b #5893 - '26C' // 670c #678 - '241X' // 670d #6289 - '26C' // 670e #678 - 'a7P' // 670f-6710 #197 - 'A' // 6711 - '3U' // 6712 #98 - '7P' // 6713 #197 - '59I' // 6714 #1542 - '18U' // 6715 #488 - '26C' // 6716 #678 - '197S' // 6717 #5140 - '34O' // 6718 #898 - '7P' // 6719 #197 - 'A' // 671a - '231M' // 671b #6018 - '3U' // 671c #98 - '206G' // 671d #5362 - '104L' // 671e #2715 - '243U' // 671f #6338 - '9W' // 6720 #256 - 'A' // 6721 - '7P' // 6722 #197 - '23F' // 6723 #603 - 'A' // 6724 - '7P' // 6725 #197 - '18U' // 6726 #488 - '104R' // 6727 #2721 - '224A' // 6728 #5824 - '10K' // 6729 #270 - '237J' // 672a #6171 - '206A' // 672b #5356 - '247F' // 672c #6427 - '162B' // 672d #4213 - '18U' // 672e #488 - '3Q' // 672f #94 - 'A' // 6730 - '188P' // 6731 #4903 - '10K' // 6732 #270 - '7P' // 6733 #197 - '148L' // 6734 #3859 - '186B' // 6735 #4837 - '18U' // 6736 #488 - '3U' // 6737 #98 - '9W' // 6738 #256 - '23F' // 6739 #603 - '167M' // 673a #4354 - '10K' // 673b #270 - 'A' // 673c - '125B' // 673d #3251 - '3U' // 673e #98 - '7P' // 673f #197 - '3I' // 6740 #86 - '3U' // 6741 #98 - '2C' // 6742 #54 - '249V' // 6743 #6495 - '46W' // 6744 #1218 - '9W' // 6745 #256 - '133Z' // 6746 #3483 - '9W' // 6747 #256 - '7P' // 6748 #197 - '176H' // 6749 #4583 - 'A' // 674a - '34O' // 674b #898 - 'a7P' // 674c-674d #197 - '218N' // 674e #5681 - '168H' // 674f #4375 - '230W' // 6750 #6002 - '213U' // 6751 #5558 - 'A' // 6752 - '18U' // 6753 #488 - '3U' // 6754 #98 - '7P' // 6755 #197 - '135Y' // 6756 #3534 - 'aA' // 6757-6758 - '9W' // 6759 #256 - 'aA' // 675a-675b - '188C' // 675c #4890 - '9W' // 675d #256 - '59I' // 675e #1542 - '210X' // 675f #5483 - '104P' // 6760 #2719 - '162I' // 6761 #4220 - '7P' // 6762 #197 - 'a3U' // 6763-6764 #98 - '260C' // 6765 #6762 - '3U' // 6766 #98 - '23F' // 6767 #603 - '3I' // 6768 #86 - '10K' // 6769 #270 - '7P' // 676a #197 - '46W' // 676b #1218 - '7P' // 676c #197 - '147U' // 676d #3842 - '7P' // 676e #197 - '210J' // 676f #5469 - '164R' // 6770 #4281 - '244O' // 6771 #6358 - '7P' // 6772 #197 - '18U' // 6773 #488 - '9W' // 6774 #256 - '104S' // 6775 #2722 - '9W' // 6776 #256 - '18U' // 6777 #488 - 'aA' // 6778-6779 - '23F' // 677a #603 - '18U' // 677b #488 - '7P' // 677c #197 - '10K' // 677d #270 - '206D' // 677e #5359 - '230D' // 677f #5983 - '3U' // 6780 #98 - '128U' // 6781 #3348 - '23F' // 6782 #603 - '10K' // 6783 #270 - '123K' // 6784 #3208 - '3U' // 6785 #98 - '34O' // 6786 #898 - '18U' // 6787 #488 - 'A' // 6788 - '123J' // 6789 #3207 - '10K' // 678a #270 - '26D' // 678b #679 - '15N' // 678c #403 - '7H' // 678d #189 - '9W' // 678e #256 - '26D' // 678f #679 - '211D' // 6790 #5489 - '9W' // 6791 #256 - '15N' // 6792 #403 - '26D' // 6793 #679 - 'A' // 6794 - '168T' // 6795 #4387 - '23Z' // 6796 #623 - '234X' // 6797 #6107 - '15N' // 6798 #403 - '9W' // 6799 #256 - '155M' // 679a #4042 - '3U' // 679b #98 - '243D' // 679c #6321 - '182L' // 679d #4743 - '7H' // 679e #189 - '59K' // 679f #1544 - '104W' // 67a0 #2726 - '23Z' // 67a1 #623 - '252B' // 67a2 #6553 - '3H' // 67a3 #85 - '26C' // 67a4 #678 - '7H' // 67a5 #189 - '3U' // 67a6 #98 - 'a7H' // 67a7-67a8 #189 - '23Z' // 67a9 #623 - '3N' // 67aa #91 - '2R' // 67ab #69 - '59K' // 67ac #1544 - '7H' // 67ad #189 - '34O' // 67ae #898 - '142O' // 67af #3706 - '26D' // 67b0 #679 - '15N' // 67b1 #403 - '9W' // 67b2 #256 - '26D' // 67b3 #679 - '9W' // 67b4 #256 - '15N' // 67b5 #403 - '218W' // 67b6 #5690 - '26D' // 67b7 #679 - '104M' // 67b8 #2716 - '15N' // 67b9 #403 - '34O' // 67ba #898 - '15N' // 67bb #403 - '23Z' // 67bc #623 - '3U' // 67bd #98 - '249S' // 67be #6492 - '39E' // 67bf #1018 - 'a15N' // 67c0-67c1 #403 - '9W' // 67c2 #256 - '15N' // 67c3 #403 - '149K' // 67c4 #3884 - 'a15N' // 67c5-67c6 #403 - '7H' // 67c7 #189 - 'a15N' // 67c8-67c9 #403 - '104U' // 67ca #2724 - 'bA' // 67cb-67cd - '9W' // 67ce #256 - '196P' // 67cf #5111 - '202J' // 67d0 #5261 - '136L' // 67d1 #3547 - '26D' // 67d2 #679 - '204Z' // 67d3 #5329 - '198E' // 67d4 #5152 - 'A' // 67d5 - '39E' // 67d6 #1018 - '104O' // 67d7 #2718 - 'a15N' // 67d8-67d9 #403 - '148O' // 67da #3862 - '9W' // 67db #256 - '104Q' // 67dc #2720 - '59H' // 67dd #1541 - '30C' // 67de #782 - '46V' // 67df #1217 - '2K' // 67e0 #62 - '3U' // 67e1 #98 - '30C' // 67e2 #782 - '7H' // 67e3 #189 - '30C' // 67e4 #782 - '239L' // 67e5 #6225 - 'a23Z' // 67e6-67e7 #623 - '7H' // 67e8 #189 - '59H' // 67e9 #1541 - '104Z' // 67ea #2729 - 'A' // 67eb - '139Z' // 67ec #3639 - 'A' // 67ed - '3U' // 67ee #98 - '186A' // 67ef #4836 - '59G' // 67f0 #1540 - '175J' // 67f1 #4559 - '59J' // 67f2 #1543 - '175Y' // 67f3 #4574 - '175I' // 67f4 #4558 - '129Y' // 67f5 #3378 - '104K' // 67f6 #2714 - '30C' // 67f7 #782 - '46V' // 67f8 #1217 - '26C' // 67f9 #678 - '30C' // 67fa #782 - '258D' // 67fb #6711 - '3U' // 67fc #98 - '7H' // 67fd #189 - '59G' // 67fe #1540 - '125K' // 67ff #3260 - '46V' // 6800 #1217 - '30C' // 6801 #782 - '59J' // 6802 #1543 - '104X' // 6803 #2727 - '104Y' // 6804 #2728 - '249U' // 6805 #6494 - '7H' // 6806 #189 - '3Q' // 6807 #94 - '3H' // 6808 #85 - 'a7H' // 6809-680a #189 - '2Y' // 680b #76 - '7H' // 680c #189 - '34L' // 680d #895 - '7H' // 680e #189 - '2C' // 680f #54 - '59C' // 6810 #1536 - '3I' // 6811 #86 - '104G' // 6812 #2710 - '125J' // 6813 #3259 - '59D' // 6814 #1537 - 'A' // 6815 - '104C' // 6816 #2706 - '196U' // 6817 #5116 - '59D' // 6818 #1537 - '3U' // 6819 #98 - 'A' // 681a - '34L' // 681b #895 - '26B' // 681c #677 - '23E' // 681d #602 - '104A' // 681e #2704 - '23Z' // 681f #623 - '26B' // 6820 #677 - '223I' // 6821 #5806 - '39D' // 6822 #1017 - 'A' // 6823 - '7H' // 6824 #189 - '59F' // 6825 #1539 - '7H' // 6826 #189 - '23Z' // 6827 #623 - 'a23E' // 6828-6829 #602 - '69D' // 682a #1797 - '23E' // 682b #602 - 'a23Z' // 682c-682d #623 - '26B' // 682e #677 - '103Y' // 682f #2702 - '3U' // 6830 #98 - 'c23E' // 6831-6834 #602 - '7H' // 6835 #189 - '34L' // 6836 #895 - '137T' // 6837 #3581 - '209F' // 6838 #5439 - '222T' // 6839 #5791 - '26B' // 683a #677 - '23E' // 683b #602 - '68V' // 683c #1789 - '161A' // 683d #4186 - '23E' // 683e #602 - '3U' // 683f #98 - '39D' // 6840 #1017 - '104E' // 6841 #2708 - '174E' // 6842 #4528 - '219K' // 6843 #5704 - 'a23E' // 6844-6845 #602 - '186C' // 6846 #4838 - '34L' // 6847 #895 - '231A' // 6848 #6006 - '23E' // 6849 #602 - '59C' // 684a #1536 - 'A' // 684b - '207J' // 684c #5391 - '3U' // 684d #98 - '39D' // 684e #1017 - 'A' // 684f - '154Y' // 6850 #4028 - '174U' // 6851 #4544 - '3U' // 6852 #98 - '39D' // 6853 #1017 - '123I' // 6854 #3206 - '9V' // 6855 #255 - '59F' // 6856 #1539 - 'b2U' // 6857-6859 #72 - 'A' // 685a - '2U' // 685b #72 - '257V' // 685c #6703 - '9V' // 685d #255 - 'A' // 685e - '36E' // 685f #940 - 'b7H' // 6860-6862 #189 - '249R' // 6863 #6491 - '7H' // 6864 #189 - '104J' // 6865 #2713 - '7H' // 6866 #189 - '36E' // 6867 #940 - '7H' // 6868 #189 - '3X' // 6869 #101 - 'A' // 686a - '9V' // 686b #255 - 'A' // 686c - '104H' // 686d #2711 - '2U' // 686e #72 - '9V' // 686f #255 - '2U' // 6870 #72 - '14J' // 6871 #373 - '9V' // 6872 #255 - 'A' // 6873 - '9V' // 6874 #255 - '14J' // 6875 #373 - '187L' // 6876 #4873 - '9V' // 6877 #255 - 'A' // 6878 - '14J' // 6879 #373 - '2U' // 687a #72 - 'a14J' // 687b-687c #373 - 'A' // 687d - '14J' // 687e #373 - '171L' // 687f #4457 - '26B' // 6880 #677 - '187V' // 6881 #4883 - '14J' // 6882 #373 - '9V' // 6883 #255 - '30A' // 6884 #780 - '198Q' // 6885 #5164 - '9V' // 6886 #255 - 'A' // 6887 - '30A' // 6888 #780 - 'cA' // 6889-688c - 'a2U' // 688d-688e #72 - '46S' // 688f #1214 - '2U' // 6890 #72 - 'a26B' // 6891-6892 #677 - '152Z' // 6893 #3977 - '103T' // 6894 #2697 - 'A' // 6895 - '14J' // 6896 #373 - '153F' // 6897 #3983 - '14J' // 6898 #373 - 'a2U' // 6899-689a #72 - '9V' // 689b #255 - '14J' // 689c #373 - '240Z' // 689d #6265 - 'A' // 689e - '46S' // 689f #1214 - '9V' // 68a0 #255 - '249Q' // 68a1 #6490 - '46S' // 68a2 #1214 - '9V' // 68a3 #255 - 'A' // 68a4 - '2U' // 68a5 #72 - '103X' // 68a6 #2701 - '128S' // 68a7 #3346 - '183U' // 68a8 #4778 - '14J' // 68a9 #373 - 'a2U' // 68aa-68ab #72 - 'A' // 68ac - '158B' // 68ad #4109 - '2U' // 68ae #72 - '66X' // 68af #1739 - '190H' // 68b0 #4947 - '104F' // 68b1 #2709 - '14J' // 68b2 #373 - '65U' // 68b3 #1710 - '14J' // 68b4 #373 - '158C' // 68b5 #4110 - '104B' // 68b6 #2705 - 'aA' // 68b7-68b8 - '103V' // 68b9 #2699 - '36E' // 68ba #940 - '2U' // 68bb #72 - '36E' // 68bc #940 - '59E' // 68bd #1538 - 'a7H' // 68be-68bf #189 - '2C' // 68c0 #54 - '59E' // 68c1 #1538 - '7H' // 68c2 #189 - '9V' // 68c3 #255 - '202P' // 68c4 #5267 - '103Q' // 68c5 #2694 - '9V' // 68c6 #255 - 'A' // 68c7 - '9V' // 68c8 #255 - '66X' // 68c9 #1739 - '9V' // 68ca #255 - '167L' // 68cb #4353 - '36E' // 68cc #940 - '146G' // 68cd #3802 - 'A' // 68ce - '2U' // 68cf #72 - 'a9V' // 68d0-68d1 #255 - '210C' // 68d2 #5462 - '26A' // 68d3 #676 - '2U' // 68d4 #72 - '65U' // 68d5 #1710 - '14J' // 68d6 #373 - '64F' // 68d7 #1669 - '123H' // 68d8 #3205 - '2U' // 68d9 #72 - '161W' // 68da #4208 - 'A' // 68db - '2U' // 68dc #72 - '19I' // 68dd #502 - 'A' // 68de - '174T' // 68df #4543 - '133S' // 68e0 #3476 - '26A' // 68e1 #676 - 'A' // 68e2 - '26A' // 68e3 #676 - '19I' // 68e4 #502 - '2U' // 68e5 #72 - '46T' // 68e6 #1215 - '158A' // 68e7 #4108 - '46R' // 68e8 #1213 - '46T' // 68e9 #1215 - 'b26A' // 68ea-68ec #676 - '19I' // 68ed #502 - '216M' // 68ee #5628 - 'a26A' // 68ef-68f0 #676 - '103W' // 68f1 #2700 - '148N' // 68f2 #3861 - '26B' // 68f3 #677 - '13O' // 68f4 #352 - '133X' // 68f5 #3481 - 'a26A' // 68f6-68f7 #676 - 'A' // 68f8 - '46R' // 68f9 #1213 - '124R' // 68fa #3241 - 'b26A' // 68fb-68fd #676 - 'A' // 68fe - '13O' // 68ff #352 - '46R' // 6900 #1213 - '103U' // 6901 #2698 - '46T' // 6902 #1215 - '30A' // 6903 #780 - '30B' // 6904 #781 - '189H' // 6905 #4921 - 'a16S' // 6906-6907 #434 - '19I' // 6908 #502 - '30B' // 6909 #781 - '2U' // 690a #72 - '16S' // 690b #434 - '2U' // 690c #72 - '204Y' // 690d #5328 - '149B' // 690e #3875 - '30B' // 690f #781 - '16S' // 6910 #434 - '2U' // 6911 #72 - '166J' // 6912 #4325 - '2U' // 6913 #72 - 'aA' // 6914-6915 - '2U' // 6916 #72 - '30B' // 6917 #781 - '34L' // 6918 #895 - '30A' // 6919 #780 - 'a16S' // 691a-691b #434 - '260H' // 691c #6767 - 'aA' // 691d-691e - 'a13O' // 691f-6920 #352 - 'b2U' // 6921-6923 #72 - '13O' // 6924 #352 - '16S' // 6925 #434 - '2U' // 6926 #72 - '260Y' // 6927 #6784 - '2U' // 6928 #72 - 'A' // 6929 - '16S' // 692a #434 - 'A' // 692b - '46U' // 692c #1216 - '13O' // 692d #352 - 'aA' // 692e-692f - '151I' // 6930 #3934 - '2U' // 6931 #72 - '46U' // 6932 #1216 - '2U' // 6933 #72 - '16S' // 6934 #434 - '2U' // 6935 #72 - '30A' // 6936 #780 - 'A' // 6937 - '2U' // 6938 #72 - '16S' // 6939 #434 - 'A' // 693a - '2U' // 693b #72 - '34N' // 693c #897 - '34M' // 693d #896 - '34N' // 693e #897 - '104D' // 693f #2707 - '34N' // 6940 #897 - '13O' // 6941 #352 - '16S' // 6942 #434 - '104I' // 6943 #2712 - '13O' // 6944 #352 - '19I' // 6945 #502 - '30A' // 6946 #780 - 'aA' // 6947-6948 - '30B' // 6949 #781 - '201B' // 694a #5227 - '13O' // 694b #352 - 'aA' // 694c-694d - '2U' // 694e #72 - 'bA' // 694f-6951 - '34N' // 6952 #897 - '166V' // 6953 #4337 - '34M' // 6954 #896 - '103Z' // 6955 #2703 - '46U' // 6956 #1216 - '34M' // 6957 #896 - 'A' // 6958 - '34M' // 6959 #896 - '201U' // 695a #5246 - '16S' // 695b #434 - '30B' // 695c #781 - '16S' // 695d #434 - '103R' // 695e #2695 - '34N' // 695f #897 - '153N' // 6960 #3991 - '103S' // 6961 #2696 - 'a34M' // 6962-6963 #896 - '8F' // 6964 #213 - '29Z' // 6965 #779 - '8F' // 6966 #213 - '25Z' // 6967 #675 - '20O' // 6968 #534 - 'a8F' // 6969-696a #213 - '20O' // 696b #534 - '8F' // 696c #213 - '244X' // 696d #6367 - 'a20O' // 696e-696f #534 - '29Z' // 6970 #779 - '19I' // 6971 #502 - '39C' // 6972 #1016 - 'a8F' // 6973-6974 #213 - '229K' // 6975 #5964 - '25Z' // 6976 #675 - '133T' // 6977 #3477 - 'a20O' // 6978-6979 #534 - '29Z' // 697a #779 - '2U' // 697b #72 - '253B' // 697c #6579 - '71A' // 697d #1846 - '2U' // 697e #72 - '19I' // 697f #502 - '8F' // 6980 #213 - '2U' // 6981 #72 - '213S' // 6982 #5556 - 'A' // 6983 - '3X' // 6984 #101 - '20P' // 6985 #535 - '103D' // 6986 #2681 - 'b13O' // 6987-6989 #352 - '103H' // 698a #2685 - 'aA' // 698b-698c - '8F' // 698d #213 - '103J' // 698e #2687 - 'A' // 698f - '25Z' // 6990 #675 - '29Z' // 6991 #779 - '2U' // 6992 #72 - 'A' // 6993 - '8F' // 6994 #213 - '64F' // 6995 #1669 - '8F' // 6996 #213 - '20P' // 6997 #535 - '8F' // 6998 #213 - '20P' // 6999 #535 - '25Z' // 699a #675 - '103G' // 699b #2684 - '207I' // 699c #5390 - 'A' // 699d - '25Z' // 699e #675 - '12G' // 699f #318 - 'a2U' // 69a0-69a1 #72 - '12G' // 69a2 #318 - 'a20P' // 69a3-69a4 #535 - '103C' // 69a5 #2680 - '8F' // 69a6 #213 - '20O' // 69a7 #534 - '133W' // 69a8 #3480 - 'aA' // 69a9-69aa - '8F' // 69ab #213 - 'A' // 69ac - '8F' // 69ad #213 - '208I' // 69ae #5416 - '29Z' // 69af #779 - '2U' // 69b0 #72 - '8F' // 69b1 #213 - '39C' // 69b2 #1016 - '25Z' // 69b3 #675 - '133U' // 69b4 #3478 - 'A' // 69b5 - '25Z' // 69b6 #675 - '8F' // 69b7 #213 - '2U' // 69b8 #72 - 'A' // 69b9 - '2U' // 69ba #72 - '20O' // 69bb #534 - '8F' // 69bc #213 - 'A' // 69bd - '2U' // 69be #72 - '29Z' // 69bf #779 - '39C' // 69c0 #1016 - '20O' // 69c1 #534 - 'A' // 69c2 - '20O' // 69c3 #534 - '20P' // 69c4 #535 - '19I' // 69c5 #502 - 'A' // 69c6 - '19I' // 69c7 #502 - '2U' // 69c8 #72 - 'A' // 69c9 - '8F' // 69ca #213 - '213O' // 69cb #5552 - '103F' // 69cc #2683 - '194Y' // 69cd #5068 - '8F' // 69ce #213 - '2U' // 69cf #72 - '20O' // 69d0 #534 - '8F' // 69d1 #213 - 'A' // 69d2 - '133V' // 69d3 #3479 - '20P' // 69d4 #535 - '12G' // 69d5 #318 - '39C' // 69d6 #1016 - '19I' // 69d7 #502 - '71A' // 69d8 #1846 - '8F' // 69d9 #213 - '13O' // 69da #352 - '3X' // 69db #101 - 'A' // 69dc - 'a1Q' // 69dd-69de #42 - '3G' // 69df #84 - '13O' // 69e0 #352 - '12G' // 69e1 #318 - 'a1Q' // 69e2-69e3 #42 - '20P' // 69e4 #535 - '1Q' // 69e5 #42 - '13O' // 69e6 #352 - '1Q' // 69e7 #42 - '103A' // 69e8 #2678 - '29Y' // 69e9 #778 - '249P' // 69ea #6489 - '1Q' // 69eb #42 - '13O' // 69ec #352 - '59A' // 69ed #1534 - '29Y' // 69ee #778 - '1Q' // 69ef #42 - 'A' // 69f0 - 'b59A' // 69f1-69f3 #1534 - '29Y' // 69f4 #778 - '1Q' // 69f5 #42 - '29Y' // 69f6 #778 - 'A' // 69f7 - '20P' // 69f8 #535 - '23D' // 69f9 #601 - '20P' // 69fa #535 - '103L' // 69fb #2689 - '13O' // 69fc #352 - '174L' // 69fd #4535 - '12K' // 69fe #322 - '58Z' // 69ff #1533 - '46O' // 6a00 #1210 - '123G' // 6a01 #3204 - '240K' // 6a02 #6250 - '23D' // 6a03 #601 - 'A' // 6a04 - '29Y' // 6a05 #778 - '11L' // 6a06 #297 - 'aA' // 6a07-6a08 - '11L' // 6a09 #297 - '102Z' // 6a0a #2677 - '103K' // 6a0b #2688 - '23D' // 6a0c #601 - 'aA' // 6a0d-6a0e - '1Q' // 6a0f #42 - 'A' // 6a10 - '65T' // 6a11 #1709 - '1Q' // 6a12 #42 - '225J' // 6a13 #5859 - 'a1Q' // 6a14-6a15 #42 - 'A' // 6a16 - '58Z' // 6a17 #1533 - '103P' // 6a18 #2693 - '235C' // 6a19 #6112 - '23D' // 6a1a #601 - '46O' // 6a1b #1210 - '12G' // 6a1c #318 - '1Q' // 6a1d #42 - '123F' // 6a1e #3203 - '128Q' // 6a1f #3344 - '1Q' // 6a20 #42 - '228Y' // 6a21 #5952 - '1Q' // 6a22 #42 - '232O' // 6a23 #6046 - '1Q' // 6a24 #42 - 'bA' // 6a25-6a27 - '46O' // 6a28 #1210 - '103M' // 6a29 #2690 - '258O' // 6a2a #6722 - '103I' // 6a2b #2686 - 'a12G' // 6a2c-6a2d #318 - '1Q' // 6a2e #42 - '11L' // 6a2f #297 - '12K' // 6a30 #322 - '4C' // 6a31 #106 - '29Y' // 6a32 #778 - '23D' // 6a33 #601 - '1Q' // 6a34 #42 - '34K' // 6a35 #894 - 'a1Q' // 6a36-6a37 #42 - '65T' // 6a38 #1709 - '211Z' // 6a39 #5511 - '152R' // 6a3a #3969 - '16R' // 6a3b #433 - '11L' // 6a3c #297 - '136N' // 6a3d #3549 - 'a16R' // 6a3e-6a3f #433 - '46P' // 6a40 #1211 - 'aA' // 6a41-6a42 - '12G' // 6a43 #318 - '139Y' // 6a44 #3638 - '23D' // 6a45 #601 - '12K' // 6a46 #322 - '16R' // 6a47 #433 - '34K' // 6a48 #894 - '1Q' // 6a49 #42 - '12K' // 6a4a #322 - '213F' // 6a4b #5543 - '12G' // 6a4c #318 - 'A' // 6a4d - '12K' // 6a4e #322 - 'A' // 6a4f - '16R' // 6a50 #433 - '1Q' // 6a51 #42 - '34K' // 6a52 #894 - '103N' // 6a53 #2691 - '1Q' // 6a54 #42 - 'a25Y' // 6a55-6a56 #674 - '46Q' // 6a57 #1212 - '174D' // 6a58 #4527 - '159M' // 6a59 #4146 - '46Q' // 6a5a #1212 - '16R' // 6a5b #433 - 'aA' // 6a5c-6a5d - '46P' // 6a5e #1211 - '244H' // 6a5f #6351 - 'A' // 6a60 - '157Z' // 6a61 #4107 - '16R' // 6a62 #433 - '12G' // 6a63 #318 - '1Q' // 6a64 #42 - '103O' // 6a65 #2692 - '16R' // 6a66 #433 - '12K' // 6a67 #322 - 'aA' // 6a68-6a69 - '25Y' // 6a6a #674 - '193D' // 6a6b #5021 - 'dA' // 6a6c-6a70 - '103B' // 6a71 #2679 - 'a1Q' // 6a72-6a73 #42 - '12G' // 6a74 #318 - 'bA' // 6a75-6a77 - '1Q' // 6a78 #42 - '11L' // 6a79 #297 - '23D' // 6a7a #601 - 'A' // 6a7b - '11L' // 6a7c #297 - 'A' // 6a7d - 'a16R' // 6a7e-6a7f #433 - '103E' // 6a80 #2682 - '25Y' // 6a81 #674 - '12G' // 6a82 #318 - '1Q' // 6a83 #42 - '34K' // 6a84 #894 - 'A' // 6a85 - '12K' // 6a86 #322 - '25Y' // 6a87 #674 - 'A' // 6a88 - '46N' // 6a89 #1209 - '12G' // 6a8a #318 - '1Q' // 6a8b #42 - 'A' // 6a8c - '46N' // 6a8d #1209 - '59B' // 6a8e #1535 - '12G' // 6a8f #318 - 'a16R' // 6a90-6a91 #433 - '46Q' // 6a92 #1212 - 'A' // 6a93 - '217M' // 6a94 #5654 - 'aA' // 6a95-6a96 - '34K' // 6a97 #894 - 'A' // 6a98 - '12G' // 6a99 #318 - 'A' // 6a9a - '1Q' // 6a9b #42 - '59B' // 6a9c #1535 - '23D' // 6a9d #601 - '16R' // 6a9e #433 - '25Y' // 6a9f #674 - '16R' // 6aa0 #433 - '25Y' // 6aa1 #674 - '225K' // 6aa2 #5860 - '46N' // 6aa3 #1209 - '46P' // 6aa4 #1211 - '25Y' // 6aa5 #674 - 'A' // 6aa6 - '12G' // 6aa7 #318 - '29X' // 6aa8 #777 - '11L' // 6aa9 #297 - '1Q' // 6aaa #42 - '8E' // 6aab #212 - '164P' // 6aac #4279 - 'A' // 6aad - '8E' // 6aae #212 - '66J' // 6aaf #1725 - '12K' // 6ab0 #322 - '46L' // 6ab1 #1207 - '58Y' // 6ab2 #1532 - '146E' // 6ab3 #3800 - '1Q' // 6ab4 #42 - '39A' // 6ab5 #1014 - 'A' // 6ab6 - '11L' // 6ab7 #297 - '66J' // 6ab8 #1725 - 'A' // 6ab9 - '39A' // 6aba #1014 - '133R' // 6abb #3475 - 'A' // 6abc - '12K' // 6abd #322 - '14H' // 6abe #371 - '12K' // 6abf #322 - 'A' // 6ac0 - '1Q' // 6ac1 #42 - '58X' // 6ac2 #1531 - '200J' // 6ac3 #5209 - '11L' // 6ac4 #297 - '29X' // 6ac5 #777 - '8E' // 6ac6 #212 - 'A' // 6ac7 - '8E' // 6ac8 #212 - '14H' // 6ac9 #371 - '29W' // 6aca #776 - 'A' // 6acb - '8E' // 6acc #212 - 'A' // 6acd - '11L' // 6ace #297 - 'A' // 6acf - 'a1Q' // 6ad0-6ad1 #42 - '11L' // 6ad2 #297 - '58X' // 6ad3 #1531 - '46L' // 6ad4 #1207 - 'a1Q' // 6ad5-6ad6 #42 - 'A' // 6ad7 - '58Y' // 6ad8 #1532 - '11L' // 6ad9 #297 - 'a46M' // 6ada-6adb #1208 - '12K' // 6adc #322 - 'a14H' // 6add-6ade #371 - '8E' // 6adf #212 - '11L' // 6ae0 #297 - 'A' // 6ae1 - '1Q' // 6ae2 #42 - 'A' // 6ae3 - '12K' // 6ae4 #322 - '162L' // 6ae5 #4223 - 'A' // 6ae6 - 'a8E' // 6ae7-6ae8 #212 - 'A' // 6ae9 - '14H' // 6aea #371 - '29X' // 6aeb #777 - '14H' // 6aec #371 - 'bA' // 6aed-6aef - '1Q' // 6af0 #42 - '14H' // 6af1 #371 - '1Q' // 6af2 #42 - '14H' // 6af3 #371 - 'aA' // 6af4-6af5 - '102U' // 6af6 #2672 - 'A' // 6af7 - '14H' // 6af8 #371 - 'A' // 6af9 - '14H' // 6afa #371 - '195S' // 6afb #5088 - '14H' // 6afc #371 - '1Q' // 6afd #42 - 'cA' // 6afe-6b01 - 'a1Q' // 6b02-6b03 #42 - '204A' // 6b04 #5304 - '102O' // 6b05 #2666 - 'a1Q' // 6b06-6b07 #42 - 'A' // 6b08 - '14H' // 6b09 #371 - '239V' // 6b0a #6235 - '1Q' // 6b0b #42 - '260X' // 6b0c #6783 - 'a11L' // 6b0d-6b0e #297 - 'b8E' // 6b0f-6b11 #212 - '46M' // 6b12 #1208 - '39A' // 6b13 #1014 - 'aA' // 6b14-6b15 - '65F' // 6b16 #1695 - '8E' // 6b17 #212 - 'A' // 6b18 - '11L' // 6b19 #297 - 'A' // 6b1a - '1Q' // 6b1b #42 - 'A' // 6b1c - 'a14H' // 6b1d-6b1e #371 - '1Q' // 6b1f #42 - '169D' // 6b20 #4397 - '68U' // 6b21 #1788 - '3Q' // 6b22 #94 - '200I' // 6b23 #5208 - '12K' // 6b24 #322 - '39A' // 6b25 #1014 - 'A' // 6b26 - '256B' // 6b27 #6657 - '1Q' // 6b28 #42 - 'aA' // 6b29-6b2a - '1Q' // 6b2b #42 - '8E' // 6b2c #212 - 'aA' // 6b2d-6b2e - '1Q' // 6b2f #42 - 'A' // 6b30 - '29X' // 6b31 #777 - '199E' // 6b32 #5178 - 'aA' // 6b33-6b34 - 'a14H' // 6b35-6b36 #371 - '8E' // 6b37 #212 - '146F' // 6b38 #3801 - '8E' // 6b39 #212 - '182K' // 6b3a #4742 - '8E' // 6b3b #212 - 'A' // 6b3c - '65F' // 6b3d #1695 - '240X' // 6b3e #6263 - '1Q' // 6b3f #42 - 'bA' // 6b40-6b42 - '8E' // 6b43 #212 - 'aA' // 6b44-6b45 - '46M' // 6b46 #1208 - '157Y' // 6b47 #4106 - '29X' // 6b48 #777 - '193E' // 6b49 #5022 - '1Q' // 6b4a #42 - 'A' // 6b4b - '216L' // 6b4c #5627 - '1Q' // 6b4d #42 - '128P' // 6b4e #3343 - 'A' // 6b4f - '217K' // 6b50 #5652 - 'A' // 6b51 - '46L' // 6b52 #1207 - '256T' // 6b53 #6675 - '8E' // 6b54 #212 - '29X' // 6b55 #777 - '1Q' // 6b56 #42 - '29W' // 6b57 #776 - '12K' // 6b58 #322 - '8E' // 6b59 #212 - 'A' // 6b5a - '8E' // 6b5b #212 - 'A' // 6b5c - '1Q' // 6b5d #42 - 'A' // 6b5e - '58W' // 6b5f #1530 - '8E' // 6b60 #212 - '232N' // 6b61 #6045 - '223N' // 6b62 #5811 - '41F' // 6b63 #1071 - '240U' // 6b64 #6260 - '225I' // 6b65 #5858 - '212R' // 6b66 #5529 - '151H' // 6b67 #3933 - 'A' // 6b68 - '259C' // 6b69 #6736 - '154B' // 6b6a #4005 - 'a1Q' // 6b6b-6b6c #42 - '11L' // 6b6d #297 - '1Q' // 6b6e #42 - '102S' // 6b6f #2670 - '12K' // 6b70 #322 - 'A' // 6b71 - '217L' // 6b72 #5653 - '258Z' // 6b73 #6733 - '102T' // 6b74 #2671 - '1Q' // 6b75 #42 - 'A' // 6b76 - '225H' // 6b77 #5857 - '200H' // 6b78 #5207 - '128R' // 6b79 #3345 - '8E' // 6b7a #212 - '68E' // 6b7b #1772 - '3W' // 6b7c #100 - 'a1Q' // 6b7d-6b7e #42 - '58W' // 6b7f #1530 - 'b8E' // 6b80-6b82 #212 - 'a102N' // 6b83-6b84 #2665 - '21K' // 6b85 #556 - '102P' // 6b86 #2667 - '2W' // 6b87 #74 - 'A' // 6b88 - '46J' // 6b89 #1205 - '203H' // 6b8a #5285 - '259H' // 6b8b #6741 - 'A' // 6b8c - '23C' // 6b8d #600 - 'bA' // 6b8e-6b90 - '102W' // 6b91 #2674 - 'a8R' // 6b92-6b93 #225 - 'A' // 6b94 - '21K' // 6b95 #556 - '167H' // 6b96 #4349 - '21K' // 6b97 #556 - '185V' // 6b98 #4831 - 'A' // 6b99 - '8R' // 6b9a #225 - '23C' // 6b9b #600 - 'aA' // 6b9c-6b9d - '46J' // 6b9e #1205 - 'a5L' // 6b9f-6ba0 #141 - '8R' // 6ba1 #225 - '23C' // 6ba2 #600 - '21K' // 6ba3 #556 - '23C' // 6ba4 #600 - 'bA' // 6ba5-6ba7 - '5L' // 6ba8 #141 - '21K' // 6ba9 #556 - '23C' // 6baa #600 - '102M' // 6bab #2664 - '5L' // 6bac #141 - '23C' // 6bad #600 - '102I' // 6bae #2660 - '63F' // 6baf #1643 - '21K' // 6bb0 #556 - '5L' // 6bb1 #141 - '46J' // 6bb2 #1205 - '23C' // 6bb3 #600 - '254A' // 6bb4 #6604 - '230C' // 6bb5 #5982 - 'A' // 6bb6 - '133Q' // 6bb7 #3474 - 'a5L' // 6bb8-6bb9 #141 - '215Q' // 6bba #5606 - '253R' // 6bbb #6595 - '193B' // 6bbc #5019 - '23C' // 6bbd #600 - '5L' // 6bbe #141 - '168Y' // 6bbf #4392 - '185Y' // 6bc0 #4834 - '102V' // 6bc1 #2673 - '8R' // 6bc2 #225 - 'a5L' // 6bc3-6bc4 #141 - '166K' // 6bc5 #4326 - '146C' // 6bc6 #3798 - 'b5L' // 6bc7-6bc9 #141 - '8R' // 6bca #225 - '128N' // 6bcb #3341 - '9U' // 6bcc #254 - '221Y' // 6bcd #5770 - '259B' // 6bce #6735 - '232M' // 6bcf #6044 - '14I' // 6bd0 #372 - '8R' // 6bd1 #225 - '209Y' // 6bd2 #5458 - '133P' // 6bd3 #3473 - '236E' // 6bd4 #6140 - '3Y' // 6bd5 #102 - 'b46I' // 6bd6-6bd8 #1204 - '3W' // 6bd9 #100 - '21K' // 6bda #556 - '221I' // 6bdb #5754 - '29W' // 6bdc #776 - 'A' // 6bdd - '39B' // 6bde #1015 - '5L' // 6bdf #141 - 'A' // 6be0 - '9U' // 6be1 #254 - 'A' // 6be2 - '5L' // 6be3 #141 - 'aA' // 6be4-6be5 - '21K' // 6be6 #556 - '5L' // 6be7 #141 - 'aA' // 6be8-6be9 - '29W' // 6bea #776 - '185X' // 6beb #4833 - '46I' // 6bec #1204 - 'A' // 6bed - '5L' // 6bee #141 - '159E' // 6bef #4138 - 'A' // 6bf0 - '21K' // 6bf1 #556 - 'A' // 6bf2 - '9U' // 6bf3 #254 - 'a8R' // 6bf4-6bf5 #225 - 'A' // 6bf6 - '5L' // 6bf7 #141 - 'A' // 6bf8 - '9U' // 6bf9 #254 - '29W' // 6bfa #776 - 'aA' // 6bfb-6bfc - '14I' // 6bfd #372 - 'A' // 6bfe - '46K' // 6bff #1206 - '39B' // 6c00 #1015 - 'A' // 6c01 - '46K' // 6c02 #1206 - 'A' // 6c03 - '5L' // 6c04 #141 - '9U' // 6c05 #254 - '14I' // 6c06 #372 - '8R' // 6c07 #225 - '63F' // 6c08 #1643 - 'a5L' // 6c09-6c0a #141 - 'A' // 6c0b - '39B' // 6c0c #1015 - '9U' // 6c0d #254 - '5L' // 6c0e #141 - '191P' // 6c0f #4981 - '9U' // 6c10 #254 - '235F' // 6c11 #6115 - '5L' // 6c12 #141 - '102H' // 6c13 #2659 - '139X' // 6c14 #3637 - '8R' // 6c15 #225 - '14I' // 6c16 #372 - '260J' // 6c17 #6769 - '14I' // 6c18 #372 - '9U' // 6c19 #254 - '14I' // 6c1a #372 - '185Z' // 6c1b #4835 - '29W' // 6c1c #776 - 'aA' // 6c1d-6c1e - '102K' // 6c1f #2662 - 'A' // 6c20 - '14I' // 6c21 #372 - '3G' // 6c22 #84 - '240J' // 6c23 #6249 - '9U' // 6c24 #254 - '8R' // 6c25 #225 - '9U' // 6c26 #254 - '171K' // 6c27 #4456 - '102L' // 6c28 #2663 - '8R' // 6c29 #225 - '14I' // 6c2a #372 - '126H' // 6c2b #3283 - '9U' // 6c2c #254 - '8R' // 6c2d #225 - '102J' // 6c2e #2661 - '102X' // 6c2f #2675 - 'b14I' // 6c30-6c32 #372 - '9U' // 6c33 #254 - '244P' // 6c34 #6359 - 'a9U' // 6c35-6c36 #254 - '102R' // 6c37 #2669 - '220K' // 6c38 #5730 - '14I' // 6c39 #372 - '9U' // 6c3a #254 - '5L' // 6c3b #141 - '8R' // 6c3c #225 - '14I' // 6c3d #372 - '46I' // 6c3e #1204 - '9U' // 6c3f #254 - '139W' // 6c40 #3636 - '190C' // 6c41 #4942 - '236L' // 6c42 #6147 - '39B' // 6c43 #1015 - 'aA' // 6c44-6c45 - '14I' // 6c46 #372 - '2C' // 6c47 #54 - 'A' // 6c48 - '102Y' // 6c49 #2676 - 'a9U' // 6c4a-6c4b #254 - '14I' // 6c4c #372 - '46K' // 6c4d #1206 - '102Q' // 6c4e #2668 - '9U' // 6c4f #254 - '159Z' // 6c50 #4159 - 'A' // 6c51 - '5L' // 6c52 #141 - 'A' // 6c53 - '18T' // 6c54 #487 - '58R' // 6c55 #1525 - '8R' // 6c56 #225 - '182R' // 6c57 #4749 - '29V' // 6c58 #775 - '151G' // 6c59 #3932 - '102D' // 6c5a #2655 - 'a18T' // 6c5b-6c5c #487 - '128O' // 6c5d #3342 - '22Z' // 6c5e #597 - '216B' // 6c5f #5617 - '205M' // 6c60 #5342 - '184K' // 6c61 #4794 - '5L' // 6c62 #141 - '8R' // 6c63 #225 - '3N' // 6c64 #91 - 'a29V' // 6c65-6c66 #775 - '18T' // 6c67 #487 - '22Z' // 6c68 #597 - '46H' // 6c69 #1203 - '164N' // 6c6a #4277 - '18T' // 6c6b #487 - 'A' // 6c6c - '22Z' // 6c6d #597 - '29V' // 6c6e #775 - '18T' // 6c6f #487 - '142Q' // 6c70 #3708 - '29V' // 6c71 #775 - '101Z' // 6c72 #2651 - 'a18T' // 6c73-6c74 #487 - '29V' // 6c75 #775 - '139V' // 6c76 #3635 - 'A' // 6c77 - 'a18T' // 6c78-6c79 #487 - '223T' // 6c7a #5817 - '5L' // 6c7b #141 - '8R' // 6c7c #225 - '218E' // 6c7d #5672 - '101V' // 6c7e #2647 - '34J' // 6c7f #893 - 'A' // 6c80 - '123E' // 6c81 #3202 - '58R' // 6c82 #1525 - '151F' // 6c83 #3931 - '18T' // 6c84 #487 - 'b22Z' // 6c85-6c87 #597 - '175H' // 6c88 #4557 - '193C' // 6c89 #5020 - 'A' // 6c8a - '8R' // 6c8b #225 - '22Z' // 6c8c #597 - '46G' // 6c8d #1202 - 'A' // 6c8e - '46H' // 6c8f #1203 - '178S' // 6c90 #4646 - '8R' // 6c91 #225 - '239U' // 6c92 #6234 - 'a22Z' // 6c93-6c94 #597 - '249N' // 6c95 #6487 - '199D' // 6c96 #5177 - '5L' // 6c97 #141 - '18T' // 6c98 #487 - '219J' // 6c99 #5703 - '22Z' // 6c9a #597 - '157X' // 6c9b #4105 - '5L' // 6c9c #141 - '46H' // 6c9d #1203 - 'A' // 6c9e - '58T' // 6c9f #1527 - 'A' // 6ca0 - '254U' // 6ca1 #6624 - '102F' // 6ca2 #2657 - 'a8R' // 6ca3-6ca4 #225 - '3G' // 6ca5 #84 - '2K' // 6ca6 #62 - '3H' // 6ca7 #85 - 'a8R' // 6ca8-6ca9 #225 - '58T' // 6caa #1527 - '146D' // 6cab #3799 - 'a18T' // 6cac-6cad #487 - '22Z' // 6cae #597 - '29V' // 6caf #775 - '46G' // 6cb0 #1202 - 'a23A' // 6cb1-6cb2 #598 - '67X' // 6cb3 #1765 - '23A' // 6cb4 #598 - '5D' // 6cb5 #133 - 'A' // 6cb6 - '5D' // 6cb7 #133 - '143D' // 6cb8 #3721 - '219Z' // 6cb9 #5719 - '23A' // 6cba #598 - '244G' // 6cbb #6350 - '102C' // 6cbc #2654 - '123D' // 6cbd #3201 - '164M' // 6cbe #4276 - '183I' // 6cbf #4766 - 'A' // 6cc0 - '229T' // 6cc1 #5973 - '58Q' // 6cc2 #1524 - '58V' // 6cc3 #1529 - '135Q' // 6cc4 #3526 - 'a23A' // 6cc5-6cc6 #598 - '58V' // 6cc7 #1529 - 'A' // 6cc8 - '198Z' // 6cc9 #5173 - '176R' // 6cca #4593 - '34J' // 6ccb #893 - '153S' // 6ccc #3996 - '5L' // 6ccd #141 - '34J' // 6cce #893 - '5L' // 6ccf #141 - 'b23A' // 6cd0-6cd2 #598 - '133O' // 6cd3 #3472 - '23A' // 6cd4 #598 - '69A' // 6cd5 #1794 - '23A' // 6cd6 #598 - '58Q' // 6cd7 #1524 - 'A' // 6cd8 - 'a23A' // 6cd9-6cda #598 - '171J' // 6cdb #4455 - '46G' // 6cdc #1202 - '25W' // 6cdd #672 - '23B' // 6cde #599 - '58U' // 6cdf #1528 - '6Y' // 6ce0 #180 - '209X' // 6ce1 #5457 - '68E' // 6ce2 #1772 - '143G' // 6ce3 #3724 - '5D' // 6ce4 #133 - '189C' // 6ce5 #4916 - '5D' // 6ce6 #133 - '25W' // 6ce7 #672 - '231Q' // 6ce8 #6022 - '6Y' // 6ce9 #180 - '101X' // 6cea #2649 - '22Y' // 6ceb #596 - '6Y' // 6cec #180 - '49T' // 6ced #1293 - 'a22Y' // 6cee-6cef #596 - '218T' // 6cf0 #5687 - '6Y' // 6cf1 #180 - '49T' // 6cf2 #1293 - '189G' // 6cf3 #4920 - '5L' // 6cf4 #141 - '64C' // 6cf5 #1666 - 'a5D' // 6cf6-6cf7 #133 - '3W' // 6cf8 #100 - 'A' // 6cf9 - '5D' // 6cfa #133 - '249O' // 6cfb #6488 - '2R' // 6cfc #69 - '3N' // 6cfd #91 - '5D' // 6cfe #133 - '34J' // 6cff #893 - '25W' // 6d00 #672 - '101Y' // 6d01 #2650 - '25X' // 6d02 #673 - '5D' // 6d03 #133 - '6Y' // 6d04 #180 - '58U' // 6d05 #1528 - '25X' // 6d06 #673 - '6Y' // 6d07 #180 - '5D' // 6d08 #133 - '25X' // 6d09 #673 - '6Y' // 6d0a #180 - '216I' // 6d0b #5624 - '22Y' // 6d0c #596 - 'A' // 6d0d - 'a6Y' // 6d0e-6d0f #180 - '23B' // 6d10 #599 - '22Y' // 6d11 #596 - '102B' // 6d12 #2653 - '49T' // 6d13 #1293 - '5D' // 6d14 #133 - 'A' // 6d15 - '5D' // 6d16 #133 - '221N' // 6d17 #5759 - '23B' // 6d18 #599 - '22Y' // 6d19 #596 - '6Y' // 6d1a #180 - '194O' // 6d1b #5058 - '5D' // 6d1c #133 - 'A' // 6d1d - '195X' // 6d1e #5093 - '6Y' // 6d1f #180 - 'aA' // 6d20-6d21 - '23B' // 6d22 #599 - '5D' // 6d23 #133 - '25W' // 6d24 #672 - '191G' // 6d25 #4972 - '58S' // 6d26 #1526 - '22Y' // 6d27 #596 - '6Y' // 6d28 #180 - '172V' // 6d29 #4493 - '187W' // 6d2a #4884 - '6Y' // 6d2b #180 - 'A' // 6d2c - '25X' // 6d2d #673 - 'a6Y' // 6d2e-6d2f #180 - '23B' // 6d30 #599 - '101W' // 6d31 #2648 - '218U' // 6d32 #5688 - '6Y' // 6d33 #180 - '25W' // 6d34 #672 - 'a22Y' // 6d35-6d36 #596 - '25X' // 6d37 #673 - 'a22Y' // 6d38-6d39 #596 - '23B' // 6d3a #599 - '68U' // 6d3b #1788 - '6Y' // 6d3c #180 - '185W' // 6d3d #4832 - '67X' // 6d3e #1765 - '6Y' // 6d3f #180 - 'A' // 6d40 - '237I' // 6d41 #6170 - 'a5D' // 6d42-6d43 #133 - '255W' // 6d44 #6652 - '256Q' // 6d45 #6672 - '2Y' // 6d46 #76 - '3X' // 6d47 #101 - 'a5D' // 6d48-6d49 #133 - '3W' // 6d4a #100 - '2D' // 6d4b #55 - 'A' // 6d4c - '5D' // 6d4d #133 - '102G' // 6d4e #2658 - '2C' // 6d4f #54 - '5D' // 6d50 #133 - '3H' // 6d51 #85 - '5D' // 6d52 #133 - '1R' // 6d53 #43 - '5D' // 6d54 #133 - 'A' // 6d55 - '5D' // 6d56 #133 - 'a6Y' // 6d57-6d58 #180 - '164O' // 6d59 #4278 - '58P' // 6d5a #1523 - '58S' // 6d5b #1526 - '102E' // 6d5c #2656 - 'A' // 6d5d - '6Y' // 6d5e #180 - '25W' // 6d5f #672 - 'a6Y' // 6d60-6d61 #180 - '25X' // 6d62 #673 - '102A' // 6d63 #2652 - 'a6Y' // 6d64-6d65 #180 - '176N' // 6d66 #4589 - '6Y' // 6d67 #180 - 'A' // 6d68 - '182F' // 6d69 #4737 - '215A' // 6d6a #5590 - 'A' // 6d6b - '58P' // 6d6c #1523 - '23B' // 6d6d #599 - '190Y' // 6d6e #4964 - '6Y' // 6d6f #180 - '25W' // 6d70 #672 - '34J' // 6d71 #893 - '25X' // 6d72 #673 - 'A' // 6d73 - '205K' // 6d74 #5340 - '23B' // 6d75 #599 - 'A' // 6d76 - '238X' // 6d77 #6211 - '168A' // 6d78 #4368 - '101M' // 6d79 #2638 - 'A' // 6d7a - '5D' // 6d7b #133 - '20N' // 6d7c #533 - '5D' // 6d7d #133 - 'A' // 6d7e - '101R' // 6d7f #2643 - '7D' // 6d80 #185 - '46E' // 6d81 #1200 - '101L' // 6d82 #2637 - 'aA' // 6d83-6d84 - '101F' // 6d85 #2631 - 'A' // 6d86 - '34H' // 6d87 #891 - '235Z' // 6d88 #6135 - '200F' // 6d89 #5205 - '70F' // 6d8a #1825 - 'A' // 6d8b - '146B' // 6d8c #3797 - '70D' // 6d8d #1823 - '34H' // 6d8e #891 - '46F' // 6d8f #1201 - '5D' // 6d90 #133 - '34H' // 6d91 #891 - '20N' // 6d92 #533 - '34H' // 6d93 #891 - '20N' // 6d94 #533 - '34H' // 6d95 #891 - '101I' // 6d96 #2634 - 'a20N' // 6d97-6d98 #533 - '256F' // 6d99 #6661 - '101S' // 6d9a #2644 - '252C' // 6d9b #6554 - '70F' // 6d9c #1825 - 'c5D' // 6d9d-6da0 #133 - '3X' // 6da1 #101 - 'a5D' // 6da2-6da3 #133 - '101U' // 6da4 #2646 - '25V' // 6da5 #671 - '3Y' // 6da6 #102 - '5D' // 6da7 #133 - '1R' // 6da8 #43 - '3H' // 6da9 #85 - 'b20N' // 6daa-6dac #533 - 'A' // 6dad - '133N' // 6dae #3471 - '174K' // 6daf #4534 - 'A' // 6db0 - '25V' // 6db1 #671 - '67S' // 6db2 #1760 - '101T' // 6db3 #2645 - '20N' // 6db4 #533 - '178R' // 6db5 #4645 - '5D' // 6db6 #133 - '34I' // 6db7 #892 - '20N' // 6db8 #533 - '46E' // 6db9 #1200 - 'aA' // 6dba-6dbb - '203P' // 6dbc #5293 - '7D' // 6dbd #185 - '46F' // 6dbe #1201 - '20N' // 6dbf #533 - '101P' // 6dc0 #2641 - 'A' // 6dc1 - '20N' // 6dc2 #533 - '260W' // 6dc3 #6782 - '101G' // 6dc4 #2632 - '58M' // 6dc5 #1520 - '123A' // 6dc6 #3198 - '164L' // 6dc7 #4275 - '34I' // 6dc8 #892 - '58O' // 6dc9 #1522 - '34I' // 6dca #892 - '179Y' // 6dcb #4678 - '58N' // 6dcc #1521 - '46F' // 6dcd #1201 - '7D' // 6dce #185 - '58M' // 6dcf #1520 - '34I' // 6dd0 #892 - '180F' // 6dd1 #4685 - '58N' // 6dd2 #1521 - '58O' // 6dd3 #1522 - 'A' // 6dd4 - '34I' // 6dd5 #892 - '5I' // 6dd6 #138 - 'A' // 6dd7 - '185U' // 6dd8 #4830 - '10V' // 6dd9 #281 - '66W' // 6dda #1738 - '5I' // 6ddb #138 - '18S' // 6ddc #486 - '5I' // 6ddd #138 - '10V' // 6dde #281 - '18R' // 6ddf #485 - '5I' // 6de0 #138 - '202O' // 6de1 #5266 - '18R' // 6de2 #485 - '14G' // 6de3 #370 - '5I' // 6de4 #138 - '18R' // 6de5 #485 - '5I' // 6de6 #138 - 'A' // 6de7 - '207H' // 6de8 #5389 - '5I' // 6de9 #138 - '145Z' // 6dea #3795 - '168K' // 6deb #4378 - '5I' // 6dec #138 - 'A' // 6ded - '128M' // 6dee #3340 - 'a5I' // 6def-6df0 #138 - '229J' // 6df1 #5963 - '18R' // 6df2 #485 - '142S' // 6df3 #3710 - '18R' // 6df4 #485 - '153R' // 6df5 #3995 - '5I' // 6df6 #138 - '67S' // 6df7 #1760 - '249L' // 6df8 #6485 - '123B' // 6df9 #3199 - '66W' // 6dfa #1738 - '197X' // 6dfb #5145 - '5I' // 6dfc #138 - '14G' // 6dfd #370 - '25V' // 6dfe #671 - 'A' // 6dff - '18R' // 6e00 #485 - 'A' // 6e01 - '14G' // 6e02 #370 - '18S' // 6e03 #486 - '101H' // 6e04 #2633 - '235G' // 6e05 #6116 - 'A' // 6e06 - '252G' // 6e07 #6558 - '70Z' // 6e08 #1845 - '255H' // 6e09 #6637 - '101J' // 6e0a #2635 - '256Y' // 6e0b #6680 - '11K' // 6e0c #296 - '2W' // 6e0d #74 - '11K' // 6e0e #296 - '25V' // 6e0f #671 - '3N' // 6e10 #91 - '11K' // 6e11 #296 - 'A' // 6e12 - '253J' // 6e13 #6587 - '2Y' // 6e14 #76 - '101O' // 6e15 #2640 - '11K' // 6e16 #296 - '249M' // 6e17 #6486 - '25V' // 6e18 #671 - '10V' // 6e19 #281 - '101N' // 6e1a #2639 - '215V' // 6e1b #5611 - 'A' // 6e1c - '101K' // 6e1d #2636 - '36D' // 6e1e #939 - '10V' // 6e1f #281 - '139S' // 6e20 #3632 - '183V' // 6e21 #4779 - '5I' // 6e22 #138 - '157W' // 6e23 #4104 - '101E' // 6e24 #2630 - '10V' // 6e25 #281 - '135W' // 6e26 #3532 - '5I' // 6e27 #138 - 'A' // 6e28 - '137K' // 6e29 #3572 - '25V' // 6e2a #671 - '10V' // 6e2b #281 - '220J' // 6e2c #5729 - '101D' // 6e2d #2629 - '18R' // 6e2e #485 - '234O' // 6e2f #6098 - 'a14G' // 6e30-6e31 #370 - '146A' // 6e32 #3796 - 'A' // 6e33 - '157V' // 6e34 #4103 - 'A' // 6e35 - '10V' // 6e36 #281 - '11K' // 6e37 #296 - '200G' // 6e38 #5206 - '5I' // 6e39 #138 - '10V' // 6e3a #281 - '7D' // 6e3b #185 - '10V' // 6e3c #281 - '101Q' // 6e3d #2642 - '147X' // 6e3e #3845 - '11K' // 6e3f #296 - 'a18S' // 6e40-6e41 #486 - '7D' // 6e42 #185 - '139R' // 6e43 #3631 - '10V' // 6e44 #281 - '5I' // 6e45 #138 - 'A' // 6e46 - '18S' // 6e47 #486 - '7D' // 6e48 #185 - '5I' // 6e49 #138 - '160K' // 6e4a #4170 - '5I' // 6e4b #138 - '7D' // 6e4c #185 - '10V' // 6e4d #281 - '5I' // 6e4e #138 - '18R' // 6e4f #485 - '25V' // 6e50 #671 - '5I' // 6e51 #138 - '7D' // 6e52 #185 - 'a5I' // 6e53-6e54 #138 - '11K' // 6e55 #296 - '215D' // 6e56 #5593 - '46E' // 6e57 #1200 - '148X' // 6e58 #3871 - '14G' // 6e59 #370 - 'A' // 6e5a - '133M' // 6e5b #3470 - '10V' // 6e5c #281 - '36D' // 6e5d #939 - 'a10V' // 6e5e-6e5f #281 - '14G' // 6e60 #370 - '18S' // 6e61 #486 - '36D' // 6e62 #939 - '5I' // 6e63 #138 - '14G' // 6e64 #370 - 'a18S' // 6e65-6e66 #486 - '160X' // 6e67 #4183 - '7D' // 6e68 #185 - '14G' // 6e69 #370 - '11K' // 6e6a #296 - '10V' // 6e6b #281 - 'A' // 6e6c - '11K' // 6e6d #296 - '10V' // 6e6e #281 - '204X' // 6e6f #5327 - '11K' // 6e70 #296 - '14G' // 6e71 #370 - 'a10V' // 6e72-6e73 #281 - '18S' // 6e74 #486 - 'A' // 6e75 - '5I' // 6e76 #138 - '11K' // 6e77 #296 - '18S' // 6e78 #486 - 'A' // 6e79 - '260V' // 6e7a #6781 - '7D' // 6e7b #185 - '18S' // 6e7c #486 - '7D' // 6e7d #185 - '256R' // 6e7e #6673 - '255Q' // 6e7f #6646 - '70Z' // 6e80 #1845 - '11K' // 6e81 #296 - '36D' // 6e82 #939 - '2R' // 6e83 #69 - 'A' // 6e84 - '3G' // 6e85 #84 - '18S' // 6e86 #486 - '11K' // 6e87 #296 - '14G' // 6e88 #370 - '5I' // 6e89 #138 - 'A' // 6e8a - '14G' // 6e8b #370 - '7D' // 6e8c #185 - '5I' // 6e8d #138 - '14G' // 6e8e #370 - '5I' // 6e8f #138 - '228F' // 6e90 #5933 - 'aA' // 6e91-6e92 - '18R' // 6e93 #485 - 'aA' // 6e94-6e95 - '230A' // 6e96 #5980 - 'A' // 6e97 - '5I' // 6e98 #138 - '18R' // 6e99 #485 - '14G' // 6e9a #370 - 'A' // 6e9b - '167Z' // 6e9c #4367 - '188Q' // 6e9d #4904 - '11K' // 6e9e #296 - '29U' // 6e9f #774 - '7D' // 6ea0 #185 - '38Z' // 6ea1 #1013 - '161Q' // 6ea2 #4202 - 'A' // 6ea3 - '46D' // 6ea4 #1199 - '29U' // 6ea5 #774 - '38Z' // 6ea6 #1013 - '20M' // 6ea7 #532 - 'aA' // 6ea8-6ea9 - '185T' // 6eaa #4829 - '225G' // 6eab #5856 - 'A' // 6eac - '7D' // 6ead #185 - '58L' // 6eae #1519 - '133L' // 6eaf #3469 - 'A' // 6eb0 - '29U' // 6eb1 #774 - '20M' // 6eb2 #532 - '7D' // 6eb3 #185 - '20M' // 6eb4 #532 - '46D' // 6eb5 #1199 - '161S' // 6eb6 #4204 - '20M' // 6eb7 #532 - '29T' // 6eb8 #773 - 'A' // 6eb9 - '136I' // 6eba #3544 - '100R' // 6ebb #2617 - '139U' // 6ebc #3634 - '20M' // 6ebd #532 - 'A' // 6ebe - 'a36D' // 6ebf-6ec0 #939 - '20M' // 6ec1 #532 - '29U' // 6ec2 #774 - '20M' // 6ec3 #532 - '63W' // 6ec4 #1660 - '190M' // 6ec5 #4952 - '11K' // 6ec6 #296 - '100S' // 6ec7 #2618 - '20M' // 6ec8 #532 - '29U' // 6ec9 #774 - '7D' // 6eca #185 - '190X' // 6ecb #4963 - '63W' // 6ecc #1660 - '58L' // 6ecd #1519 - '100P' // 6ece #2615 - '20M' // 6ecf #532 - '38Z' // 6ed0 #1013 - '209W' // 6ed1 #5456 - 'A' // 6ed2 - 'a29U' // 6ed3-6ed4 #774 - '100T' // 6ed5 #2619 - '46D' // 6ed6 #1199 - '11K' // 6ed7 #296 - '38Z' // 6ed8 #1013 - '123C' // 6ed9 #3200 - '100U' // 6eda #2620 - '18P' // 6edb #483 - 'A' // 6edc - '100Y' // 6edd #2624 - '255P' // 6ede #6645 - 'a11J' // 6edf-6ee0 #295 - '2D' // 6ee1 #55 - '14F' // 6ee2 #369 - 'A' // 6ee3 - '2L' // 6ee4 #63 - '2K' // 6ee5 #62 - '70E' // 6ee6 #1824 - 'A' // 6ee7 - '101C' // 6ee8 #2628 - '101B' // 6ee9 #2627 - '11J' // 6eea #295 - '18Q' // 6eeb #484 - '139T' // 6eec #3633 - '7D' // 6eed #185 - '18P' // 6eee #483 - '145Y' // 6eef #3794 - 'aA' // 6ef0-6ef1 - '147S' // 6ef2 #3840 - '11J' // 6ef3 #295 - '188G' // 6ef4 #4894 - 'aA' // 6ef5-6ef6 - '151D' // 6ef7 #3929 - '46B' // 6ef8 #1197 - '18P' // 6ef9 #483 - '14F' // 6efa #369 - '18Q' // 6efb #484 - 'A' // 6efc - '7D' // 6efd #185 - '67H' // 6efe #1749 - '232L' // 6eff #6043 - '9T' // 6f00 #253 - '174X' // 6f01 #4547 - '202H' // 6f02 #5259 - '11J' // 6f03 #295 - '58J' // 6f04 #1517 - 'A' // 6f05 - '173S' // 6f06 #4516 - 'A' // 6f07 - 'a18P' // 6f08-6f09 #483 - '18Q' // 6f0a #484 - '29T' // 6f0b #773 - '58J' // 6f0c #1517 - '18Q' // 6f0d #484 - '9T' // 6f0e #253 - '189Y' // 6f0f #4938 - '7D' // 6f10 #185 - '70D' // 6f11 #1823 - '9T' // 6f12 #253 - '58K' // 6f13 #1518 - '230B' // 6f14 #5981 - '46B' // 6f15 #1197 - '100Q' // 6f16 #2616 - '29T' // 6f17 #773 - '7D' // 6f18 #185 - '14F' // 6f19 #369 - '18Q' // 6f1a #484 - '7D' // 6f1b #185 - 'cA' // 6f1c-6f1f - '160D' // 6f20 #4163 - 'A' // 6f21 - '210E' // 6f22 #5464 - '100V' // 6f23 #2621 - '46C' // 6f24 #1198 - '18P' // 6f25 #483 - '18Q' // 6f26 #484 - '9T' // 6f27 #253 - '11J' // 6f28 #295 - 'a18P' // 6f29-6f2a #483 - '228E' // 6f2b #5932 - '143B' // 6f2c #3719 - '18P' // 6f2d #483 - '9T' // 6f2e #253 - '18P' // 6f2f #483 - '18Q' // 6f30 #484 - '100N' // 6f31 #2613 - '185S' // 6f32 #4828 - '58K' // 6f33 #1518 - '9T' // 6f34 #253 - '18Q' // 6f35 #484 - '18P' // 6f36 #483 - '11J' // 6f37 #295 - '67H' // 6f38 #1749 - 'A' // 6f39 - '9T' // 6f3a #253 - 'a18Q' // 6f3b-6f3c #484 - '29T' // 6f3d #773 - '151E' // 6f3e #3930 - '66I' // 6f3f #1724 - '9T' // 6f40 #253 - '100O' // 6f41 #2614 - 'A' // 6f42 - 'a14F' // 6f43-6f44 #369 - '70E' // 6f45 #1824 - '11J' // 6f46 #295 - '3H' // 6f47 #85 - '11J' // 6f48 #295 - 'aA' // 6f49-6f4a - '11J' // 6f4b #295 - 'A' // 6f4c - '2K' // 6f4d #62 - '14F' // 6f4e #369 - '18P' // 6f4f #483 - 'A' // 6f50 - '66I' // 6f51 #1724 - '7D' // 6f52 #185 - '18Q' // 6f53 #484 - '209I' // 6f54 #5442 - '11J' // 6f55 #295 - '46C' // 6f56 #1198 - '46B' // 6f57 #1197 - '171G' // 6f58 #4452 - '41P' // 6f59 #1081 - '38Y' // 6f5a #1012 - '192Z' // 6f5b #5017 - '100Z' // 6f5c #2625 - '5K' // 6f5d #140 - '38Y' // 6f5e #1012 - '101A' // 6f5f #2626 - '25U' // 6f60 #670 - '15M' // 6f61 #402 - '171E' // 6f62 #4450 - '9T' // 6f63 #253 - '196O' // 6f64 #5110 - '11J' // 6f65 #295 - '15M' // 6f66 #402 - '14F' // 6f67 #369 - '5K' // 6f68 #140 - 'b14F' // 6f69-6f6b #369 - '15M' // 6f6c #402 - '171F' // 6f6d #4451 - '215G' // 6f6e #5596 - '15M' // 6f6f #402 - '167W' // 6f70 #4364 - '11J' // 6f71 #295 - '14F' // 6f72 #369 - '9T' // 6f73 #253 - '15M' // 6f74 #402 - '11J' // 6f75 #295 - '14F' // 6f76 #369 - '9T' // 6f77 #253 - '15M' // 6f78 #402 - '46C' // 6f79 #1198 - '38Y' // 6f7a #1012 - '14F' // 6f7b #369 - 'b38Y' // 6f7c-6f7e #1012 - '9T' // 6f7f #253 - '151C' // 6f80 #3928 - '100X' // 6f81 #2623 - '15M' // 6f82 #402 - '41P' // 6f83 #1081 - '167G' // 6f84 #4348 - 'A' // 6f85 - '63E' // 6f86 #1642 - '25U' // 6f87 #670 - '128L' // 6f88 #3339 - '14F' // 6f89 #369 - '29T' // 6f8a #773 - 'a15M' // 6f8b-6f8c #402 - '18O' // 6f8d #482 - '192Y' // 6f8e #5016 - 'A' // 6f8f - '18O' // 6f90 #482 - '5K' // 6f91 #140 - '15M' // 6f92 #402 - '5K' // 6f93 #140 - '18O' // 6f94 #482 - '9T' // 6f95 #253 - '25U' // 6f96 #670 - '18O' // 6f97 #482 - '41P' // 6f98 #1081 - 'A' // 6f99 - '5K' // 6f9a #140 - '11J' // 6f9b #295 - '2K' // 6f9c #62 - '58I' // 6f9d #1516 - 'A' // 6f9e - 'a25U' // 6f9f-6fa0 #670 - '171H' // 6fa1 #4453 - '9T' // 6fa2 #253 - '18O' // 6fa3 #482 - '197P' // 6fa4 #5137 - '15M' // 6fa5 #402 - '25U' // 6fa6 #670 - '18O' // 6fa7 #482 - '25U' // 6fa8 #670 - 'A' // 6fa9 - '100W' // 6faa #2622 - '9T' // 6fab #253 - 'aA' // 6fac-6fad - '100M' // 6fae #2612 - '18O' // 6faf #482 - '5K' // 6fb0 #140 - '64T' // 6fb1 #1683 - 'A' // 6fb2 - '217J' // 6fb3 #5651 - '14F' // 6fb4 #369 - '58I' // 6fb5 #1516 - '15M' // 6fb6 #402 - '5K' // 6fb7 #140 - 'A' // 6fb8 - '18O' // 6fb9 #482 - '9T' // 6fba #253 - '29T' // 6fbb #773 - '25U' // 6fbc #670 - 'A' // 6fbd - '100L' // 6fbe #2611 - 'A' // 6fbf - '213E' // 6fc0 #5542 - '125G' // 6fc1 #3256 - '18O' // 6fc2 #482 - '204R' // 6fc3 #5321 - '11J' // 6fc4 #295 - '41P' // 6fc5 #1081 - 'c15M' // 6fc6-6fc9 #402 - '18O' // 6fca #482 - '14F' // 6fcb #369 - 'aA' // 6fcc-6fcd - '9T' // 6fce #253 - 'aA' // 6fcf-6fd0 - 'a11J' // 6fd1-6fd2 #295 - '100F' // 6fd3 #2605 - '46A' // 6fd4 #1196 - '200E' // 6fd5 #5204 - 'aA' // 6fd6-6fd7 - '46A' // 6fd8 #1196 - '13C' // 6fd9 #340 - '99P' // 6fda #2589 - '63E' // 6fdb #1642 - 'aA' // 6fdc-6fdd - '99Q' // 6fde #2590 - '207G' // 6fdf #5388 - '58G' // 6fe0 #1514 - '99Z' // 6fe1 #2599 - '100K' // 6fe2 #2610 - 'A' // 6fe3 - '145W' // 6fe4 #3792 - 'bA' // 6fe5-6fe7 - '46A' // 6fe8 #1196 - '58G' // 6fe9 #1514 - 'A' // 6fea - '157T' // 6feb #4101 - '58F' // 6fec #1513 - 'A' // 6fed - '45Z' // 6fee #1195 - '131C' // 6fef #3408 - '45Z' // 6ff0 #1195 - '174C' // 6ff1 #4526 - 'A' // 6ff2 - '5K' // 6ff3 #140 - 'A' // 6ff4 - '5K' // 6ff5 #140 - '14D' // 6ff6 #367 - 'A' // 6ff7 - '13C' // 6ff8 #340 - '5K' // 6ff9 #140 - '45Z' // 6ffa #1195 - 'A' // 6ffb - '14D' // 6ffc #367 - '5K' // 6ffd #140 - '171D' // 6ffe #4449 - '9S' // 6fff #252 - '14D' // 7000 #367 - '58H' // 7001 #1515 - 'A' // 7002 - '9S' // 7003 #252 - 'A' // 7004 - 'a58F' // 7005-7006 #1513 - '14D' // 7007 #367 - 'A' // 7008 - '64T' // 7009 #1683 - '5K' // 700a #140 - '8D' // 700b #211 - 'A' // 700c - '7O' // 700d #196 - 'A' // 700e - '225F' // 700f #5855 - 'A' // 7010 - '139Q' // 7011 #3630 - 'bA' // 7012-7014 - '8D' // 7015 #211 - 'A' // 7016 - '5K' // 7017 #140 - '8D' // 7018 #211 - 'A' // 7019 - '139P' // 701a #3629 - '8D' // 701b #211 - '50E' // 701c #1304 - '63V' // 701d #1659 - '8D' // 701e #211 - '63D' // 701f #1641 - '14D' // 7020 #367 - '9S' // 7021 #252 - 'A' // 7022 - '8D' // 7023 #211 - 'aA' // 7024-7025 - '7O' // 7026 #196 - '99W' // 7027 #2596 - '133J' // 7028 #3467 - 'bA' // 7029-702b - '100B' // 702c #2601 - 'A' // 702d - '6T' // 702e #175 - '8D' // 702f #211 - '7O' // 7030 #196 - '9S' // 7031 #252 - '14D' // 7032 #367 - 'A' // 7033 - '14D' // 7034 #367 - '14E' // 7035 #368 - 'A' // 7036 - '8D' // 7037 #211 - '9S' // 7038 #252 - 'a7O' // 7039-703a #196 - '9S' // 703b #252 - '7O' // 703c #196 - '6T' // 703d #175 - '63V' // 703e #1659 - 'A' // 703f - '9S' // 7040 #252 - 'A' // 7041 - '9S' // 7042 #252 - '14D' // 7043 #367 - '7O' // 7044 #196 - 'A' // 7045 - '9S' // 7046 #252 - 'a5K' // 7047-7048 #140 - '7O' // 7049 #196 - '36C' // 704a #938 - '7O' // 704b #196 - '164K' // 704c #4274 - '13C' // 704d #340 - '5K' // 704e #140 - '6T' // 704f #175 - '100C' // 7050 #2602 - '164H' // 7051 #4271 - '9S' // 7052 #252 - 'A' // 7053 - 'a14D' // 7054-7055 #367 - 'aA' // 7056-7057 - '173J' // 7058 #4507 - 'A' // 7059 - '6T' // 705a #175 - 'A' // 705b - '14E' // 705c #368 - '8D' // 705d #211 - '7O' // 705e #196 - 'a9S' // 705f-7060 #252 - '14E' // 7061 #368 - 'A' // 7062 - '240S' // 7063 #6258 - '7O' // 7064 #196 - '14D' // 7065 #367 - '14E' // 7066 #368 - 'a9S' // 7067-7068 #252 - '14D' // 7069 #367 - 'A' // 706a - '68M' // 706b #1780 - '7O' // 706c #196 - '1R' // 706d #43 - '45Y' // 706e #1194 - '100A' // 706f #2600 - '196A' // 7070 #5096 - 'bA' // 7071-7073 - '9S' // 7074 #252 - '99U' // 7075 #2594 - '122Z' // 7076 #3197 - '13C' // 7077 #340 - '99X' // 7078 #2597 - '13C' // 7079 #340 - '9S' // 707a #252 - 'A' // 707b - '129X' // 707c #3377 - '191F' // 707d #4971 - '99T' // 707e #2593 - '100I' // 707f #2608 - '6T' // 7080 #175 - '7O' // 7081 #196 - 'bA' // 7082-7084 - '8D' // 7085 #211 - '7O' // 7086 #196 - 'aA' // 7087-7088 - '99Y' // 7089 #2598 - '149A' // 708a #3874 - '13C' // 708b #340 - 'aA' // 708c-708d - '190O' // 708e #4954 - '100E' // 708f #2604 - 'A' // 7090 - '14E' // 7091 #368 - '188F' // 7092 #4893 - 'A' // 7093 - 'a7O' // 7094-7095 #196 - '99S' // 7096 #2592 - '5K' // 7097 #140 - '8D' // 7098 #211 - '130C' // 7099 #3382 - '50E' // 709a #1304 - '5K' // 709b #140 - 'a6T' // 709c-709d #175 - 'A' // 709e - '7O' // 709f #196 - '13C' // 70a0 #340 - '100D' // 70a1 #2603 - 'A' // 70a2 - '13C' // 70a3 #340 - '8D' // 70a4 #211 - 'b13C' // 70a5-70a7 #340 - 'A' // 70a8 - '14E' // 70a9 #368 - '6T' // 70aa #175 - '164J' // 70ab #4273 - '99N' // 70ac #2587 - '168V' // 70ad #4389 - '171I' // 70ae #4454 - '8D' // 70af #211 - 'a7O' // 70b0-70b1 #196 - '6T' // 70b2 #175 - '133K' // 70b3 #3468 - '7O' // 70b4 #196 - '14E' // 70b5 #368 - '6T' // 70b6 #175 - '8D' // 70b7 #211 - '194R' // 70b8 #5061 - '247H' // 70b9 #6429 - '246J' // 70ba #6405 - '7O' // 70bb #196 - '100J' // 70bc #2609 - '100G' // 70bd #2606 - '9S' // 70be #252 - 'A' // 70bf - '14E' // 70c0 #368 - '3W' // 70c1 #100 - '1R' // 70c2 #43 - '6T' // 70c3 #175 - '13C' // 70c4 #340 - 'aA' // 70c5-70c6 - '6T' // 70c7 #175 - '202N' // 70c8 #5265 - 'A' // 70c9 - '99R' // 70ca #2591 - '8D' // 70cb #211 - '13C' // 70cc #340 - 'A' // 70cd - '6T' // 70ce #175 - '195K' // 70cf #5080 - '13C' // 70d0 #340 - '5K' // 70d1 #140 - '9S' // 70d2 #252 - '5K' // 70d3 #140 - '7O' // 70d4 #196 - '14D' // 70d5 #367 - '45Y' // 70d6 #1194 - 'A' // 70d7 - '178Q' // 70d8 #4644 - '8D' // 70d9 #211 - '14E' // 70da #368 - '3X' // 70db #101 - '7O' // 70dc #196 - '8D' // 70dd #211 - 'A' // 70de - '99O' // 70df #2588 - '6T' // 70e0 #175 - 'bA' // 70e1-70e3 - '193A' // 70e4 #5018 - 'A' // 70e5 - '1R' // 70e6 #43 - '3Y' // 70e7 #102 - '2W' // 70e8 #74 - '6T' // 70e9 #175 - 'A' // 70ea - '2R' // 70eb #69 - '36C' // 70ec #938 - '7K' // 70ed #192 - 'A' // 70ee - '100H' // 70ef #2607 - 'A' // 70f0 - '58H' // 70f1 #1515 - 'aA' // 70f2-70f3 - '9S' // 70f4 #252 - '13C' // 70f5 #340 - 'A' // 70f6 - '14E' // 70f7 #368 - 'A' // 70f8 - '166N' // 70f9 #4329 - '7O' // 70fa #196 - 'aA' // 70fb-70fc - '8D' // 70fd #211 - '13C' // 70fe #340 - '14E' // 70ff #368 - 'bA' // 7100-7102 - '5K' // 7103 #140 - '8D' // 7104 #211 - '45Y' // 7105 #1194 - '14D' // 7106 #367 - '5K' // 7107 #140 - '36C' // 7108 #938 - '99V' // 7109 #2595 - '64C' // 710a #1666 - '5K' // 710b #140 - '8D' // 710c #211 - 'aA' // 710d-710e - '5K' // 710f #140 - '14E' // 7110 #368 - 'aA' // 7111-7112 - '14E' // 7113 #368 - '36C' // 7114 #938 - '2K' // 7115 #62 - '6T' // 7116 #175 - '143S' // 7117 #3736 - '6T' // 7118 #175 - '164I' // 7119 #4272 - '136C' // 711a #3538 - 'A' // 711b - '22X' // 711c #595 - '6N' // 711d #169 - '34G' // 711e #890 - 'A' // 711f - '22X' // 7120 #595 - '245P' // 7121 #6385 - '12F' // 7122 #317 - 'bA' // 7123-7125 - '202X' // 7126 #5275 - 'aA' // 7127-7128 - '6N' // 7129 #169 - 'A' // 712a - '58D' // 712b #1511 - '6N' // 712c #169 - '5K' // 712d #140 - 'a22X' // 712e-712f #595 - '145X' // 7130 #3793 - '22X' // 7131 #595 - 'A' // 7132 - '6N' // 7133 #169 - '12F' // 7134 #317 - '6N' // 7135 #169 - '237H' // 7136 #6169 - 'A' // 7137 - '5K' // 7138 #140 - 'aA' // 7139-713a - '6N' // 713b #169 - '258K' // 713c #6718 - 'A' // 713d - '6N' // 713e #169 - 'A' // 713f - '6N' // 7140 #169 - '5K' // 7141 #140 - '6T' // 7142 #175 - '12F' // 7143 #317 - '6T' // 7144 #175 - 'a22X' // 7145-7146 #595 - '34G' // 7147 #890 - 'A' // 7148 - '157S' // 7149 #4100 - '34G' // 714a #890 - '22X' // 714b #595 - '159R' // 714c #4151 - 'A' // 714d - '166O' // 714e #4330 - '6N' // 714f #169 - '34G' // 7150 #890 - '38X' // 7151 #1011 - '22X' // 7152 #595 - '38X' // 7153 #1011 - 'A' // 7154 - '36C' // 7155 #938 - '34G' // 7156 #890 - '58D' // 7157 #1511 - 'A' // 7158 - '197I' // 7159 #5130 - '22X' // 715a #595 - 'A' // 715b - '63D' // 715c #1641 - 'A' // 715d - '157U' // 715e #4102 - 'A' // 715f - '38X' // 7160 #1011 - '6T' // 7161 #175 - '38X' // 7162 #1011 - 'A' // 7163 - '151B' // 7164 #3927 - '145V' // 7165 #3791 - '58B' // 7166 #1509 - '234U' // 7167 #6104 - '38W' // 7168 #1010 - '194T' // 7169 #5063 - 'A' // 716a - '6N' // 716b #169 - '58C' // 716c #1510 - '6T' // 716d #175 - '189F' // 716e #4919 - 'aA' // 716f-7170 - '12F' // 7171 #317 - '149U' // 7172 #3894 - '29S' // 7173 #772 - '58E' // 7174 #1512 - '6N' // 7175 #169 - '58E' // 7176 #1512 - '6N' // 7177 #169 - '29S' // 7178 #772 - '2Z' // 7179 #77 - '29S' // 717a #772 - '12F' // 717b #317 - '6N' // 717c #169 - '99M' // 717d #2586 - '6N' // 717e #169 - '6T' // 717f #175 - '38W' // 7180 #1010 - '12F' // 7181 #317 - 'aA' // 7182-7183 - '139O' // 7184 #3628 - '16Q' // 7185 #432 - '6T' // 7186 #175 - 'a38W' // 7187-7188 #1010 - '50E' // 7189 #1304 - '205J' // 718a #5339 - 'A' // 718b - '34E' // 718c #888 - 'A' // 718d - '6N' // 718e #169 - '99J' // 718f #2583 - '12F' // 7190 #317 - '6N' // 7191 #169 - '58B' // 7192 #1509 - 'A' // 7193 - '122X' // 7194 #3195 - '2Z' // 7195 #77 - '16Q' // 7196 #432 - '12F' // 7197 #317 - '29S' // 7198 #772 - '157R' // 7199 #4099 - 'a16Q' // 719a-719b #432 - '29S' // 719c #772 - 'aA' // 719d-719e - '210W' // 719f #5482 - '38W' // 71a0 #1010 - 'A' // 71a1 - '99I' // 71a2 #2582 - '6N' // 71a3 #169 - '29S' // 71a4 #772 - '6T' // 71a5 #175 - 'aA' // 71a6-71a7 - '7N' // 71a8 #195 - 'bA' // 71a9-71ab - '157Q' // 71ac #4098 - '6N' // 71ad #169 - '2Z' // 71ae #77 - '7N' // 71af #195 - '2Z' // 71b0 #77 - '241O' // 71b1 #6280 - 'a7N' // 71b2-71b3 #195 - '45X' // 71b4 #1193 - '18N' // 71b5 #481 - '11I' // 71b6 #294 - 'a18N' // 71b7-71b8 #481 - 'a18M' // 71b9-71ba #480 - 'a11I' // 71bb-71bc #294 - 'A' // 71bd - '18M' // 71be #480 - 'a2Z' // 71bf-71c0 #77 - '18M' // 71c1 #480 - '11I' // 71c2 #294 - '183A' // 71c3 #4758 - '7N' // 71c4 #195 - 'bA' // 71c5-71c7 - '208L' // 71c8 #5419 - '65E' // 71c9 #1694 - '18N' // 71ca #481 - '7N' // 71cb #195 - '2Z' // 71cc #77 - 'A' // 71cd - '18M' // 71ce #480 - '18N' // 71cf #481 - '18M' // 71d0 #480 - '6N' // 71d1 #169 - '67Z' // 71d2 #1767 - '2Z' // 71d3 #77 - '18M' // 71d4 #480 - '180J' // 71d5 #4689 - 'a2Z' // 71d6-71d7 #77 - '12F' // 71d8 #317 - '164G' // 71d9 #4270 - '7N' // 71da #195 - 'A' // 71db - '7N' // 71dc #195 - '6N' // 71dd #169 - 'A' // 71de - '232K' // 71df #6042 - '7N' // 71e0 #195 - '12F' // 71e1 #317 - 'bA' // 71e2-71e4 - '168Q' // 71e5 #4384 - '164F' // 71e6 #4269 - '18M' // 71e7 #480 - '11I' // 71e8 #294 - 'A' // 71e9 - '11I' // 71ea #294 - '6N' // 71eb #169 - '16Q' // 71ec #432 - '65E' // 71ed #1694 - '18M' // 71ee #480 - 'dA' // 71ef-71f3 - '7N' // 71f4 #195 - '16Q' // 71f5 #432 - '18N' // 71f6 #481 - 'A' // 71f7 - '2Z' // 71f8 #77 - '7N' // 71f9 #195 - 'A' // 71fa - '124Q' // 71fb #3240 - '18M' // 71fc #480 - 'A' // 71fd - '58C' // 71fe #1510 - 'a18M' // 71ff-7200 #480 - '12F' // 7201 #317 - 'A' // 7202 - '12F' // 7203 #317 - 'aA' // 7204-7205 - '220Q' // 7206 #5736 - '7N' // 7207 #195 - '2Z' // 7208 #77 - '16Q' // 7209 #432 - 'aA' // 720a-720b - '18N' // 720c #481 - '122Y' // 720d #3196 - 'a6N' // 720e-720f #169 - '192W' // 7210 #5014 - 'aA' // 7211-7212 - '16Q' // 7213 #432 - '12F' // 7214 #317 - '16Q' // 7215 #432 - '45X' // 7216 #1193 - '16Q' // 7217 #432 - 'aA' // 7218-7219 - '7N' // 721a #195 - '194W' // 721b #5066 - '11I' // 721c #294 - '7N' // 721d #195 - '11I' // 721e #294 - '2Z' // 721f #77 - 'aA' // 7220-7221 - '12F' // 7222 #317 - '18N' // 7223 #481 - '34E' // 7224 #888 - '6N' // 7225 #169 - 'aA' // 7226-7227 - '7N' // 7228 #195 - 'A' // 7229 - '154N' // 722a #4017 - '7N' // 722b #195 - '180Q' // 722c #4696 - '67Z' // 722d #1767 - '45X' // 722e #1193 - '2Z' // 722f #77 - '34F' // 7230 #889 - '3Q' // 7231 #94 - '249J' // 7232 #6483 - 'A' // 7233 - '2Z' // 7234 #77 - '173C' // 7235 #4500 - '212A' // 7236 #5512 - '1R' // 7237 #43 - '192X' // 7238 #5015 - '145U' // 7239 #3790 - '188B' // 723a #4889 - '34F' // 723b #889 - '21J' // 723c #555 - '202M' // 723d #5264 - '217H' // 723e #5649 - '7N' // 723f #195 - '34F' // 7240 #889 - 'a7N' // 7241-7242 #195 - '2Z' // 7243 #77 - 'A' // 7244 - '2Z' // 7245 #77 - '200D' // 7246 #5203 - '241H' // 7247 #6273 - '242E' // 7248 #6296 - 'aA' // 7249-724a - '7N' // 724b #195 - '226F' // 724c #5881 - '11I' // 724d #294 - '21J' // 724e #555 - '2Z' // 724f #77 - '34E' // 7250 #888 - 'A' // 7251 - '34F' // 7252 #889 - '16Q' // 7253 #432 - 'A' // 7254 - '34E' // 7255 #888 - '7N' // 7256 #195 - '34E' // 7257 #888 - '34F' // 7258 #889 - '208U' // 7259 #5428 - '21J' // 725a #555 - '219Q' // 725b #5710 - '99K' // 725c #2584 - '99L' // 725d #2585 - '2Z' // 725e #77 - '130G' // 725f #3386 - '151A' // 7260 #3926 - '148M' // 7261 #3860 - '159G' // 7262 #4140 - '16Q' // 7263 #432 - 'a11I' // 7264-7265 #294 - '18N' // 7266 #481 - '175X' // 7267 #4573 - '2Z' // 7268 #77 - '35V' // 7269 #931 - '18N' // 726a #481 - '2Z' // 726b #77 - '11I' // 726c #294 - 'A' // 726d - 'a7N' // 726e-726f #195 - '12F' // 7270 #317 - '21J' // 7271 #555 - '153L' // 7272 #3989 - '18N' // 7273 #481 - '7N' // 7274 #195 - '4C' // 7275 #106 - 'A' // 7276 - '58A' // 7277 #1508 - '21J' // 7278 #555 - '35V' // 7279 #931 - '3X' // 727a #101 - '57Z' // 727b #1507 - '2Z' // 727c #77 - '180L' // 727d #4691 - '57Z' // 727e #1507 - '58A' // 727f #1508 - '145T' // 7280 #3789 - '45V' // 7281 #1191 - '10U' // 7282 #280 - 'A' // 7283 - '10U' // 7284 #280 - 'aA' // 7285-7286 - '10U' // 7287 #280 - 'A' // 7288 - '2Z' // 7289 #77 - '11I' // 728a #294 - 'aA' // 728b-728c - '10U' // 728d #280 - '2Z' // 728e #77 - '99H' // 728f #2581 - 'aA' // 7290-7291 - '10U' // 7292 #280 - '2Z' // 7293 #77 - '38U' // 7294 #1008 - 'A' // 7295 - '25T' // 7296 #669 - 'A' // 7297 - '20L' // 7298 #531 - 'aA' // 7299-729a - '10U' // 729b #280 - 'bA' // 729c-729e - '34D' // 729f #887 - '253D' // 72a0 #6581 - '20L' // 72a1 #531 - '57X' // 72a2 #1505 - 'cA' // 72a3-72a6 - '145S' // 72a7 #3788 - '2Z' // 72a8 #77 - 'bA' // 72a9-72ab - '183P' // 72ac #4773 - 'a10U' // 72ad-72ae #280 - '204W' // 72af #5326 - '10U' // 72b0 #280 - '21J' // 72b1 #555 - '10U' // 72b2 #280 - '11I' // 72b3 #294 - '10U' // 72b4 #280 - '34D' // 72b5 #887 - '259V' // 72b6 #6755 - 'a11I' // 72b7-72b8 #294 - '249K' // 72b9 #6484 - 'b11I' // 72ba-72bc #294 - '34D' // 72bd #887 - '21J' // 72be #555 - 'A' // 72bf - '225E' // 72c0 #5854 - '10U' // 72c1 #280 - '215E' // 72c2 #5594 - '10U' // 72c3 #280 - '164E' // 72c4 #4268 - '34D' // 72c5 #887 - '10U' // 72c6 #280 - '2Z' // 72c7 #77 - '11I' // 72c8 #294 - '2Z' // 72c9 #77 - 'aA' // 72ca-72cb - '10U' // 72cc #280 - '34D' // 72cd #887 - '45V' // 72ce #1191 - 'A' // 72cf - '166T' // 72d0 #4335 - 'A' // 72d1 - '10U' // 72d2 #280 - 'A' // 72d3 - '20L' // 72d4 #531 - '2Z' // 72d5 #77 - '21J' // 72d6 #555 - '208N' // 72d7 #5421 - '2Z' // 72d8 #77 - '131B' // 72d9 #3407 - 'A' // 72da - '252J' // 72db #6561 - 'A' // 72dc - 'a11I' // 72dd-72de #294 - '10U' // 72df #280 - '66H' // 72e0 #1723 - '45V' // 72e1 #1191 - '98Z' // 72e2 #2573 - 'aA' // 72e3-72e4 - '21J' // 72e5 #555 - '7G' // 72e6 #188 - 'A' // 72e7 - '14C' // 72e8 #366 - '130X' // 72e9 #3403 - 'aA' // 72ea-72eb - '258U' // 72ec #6728 - '255Z' // 72ed #6655 - '2L' // 72ee #63 - 'a7G' // 72ef-72f0 #188 - '2L' // 72f1 #63 - '7G' // 72f2 #188 - 'a12E' // 72f3-72f4 #316 - 'A' // 72f5 - '7G' // 72f6 #188 - '12E' // 72f7 #316 - '148B' // 72f8 #3849 - '139N' // 72f9 #3627 - 'a12E' // 72fa-72fb #316 - '181F' // 72fc #4711 - '20K' // 72fd #530 - '2Z' // 72fe #77 - 'A' // 72ff - 'a14C' // 7300-7301 #366 - '57Y' // 7302 #1506 - '7G' // 7303 #188 - '12E' // 7304 #316 - '2Z' // 7305 #77 - 'A' // 7306 - '12E' // 7307 #316 - 'aA' // 7308-7309 - '20K' // 730a #530 - '12E' // 730b #316 - '7G' // 730c #188 - '2Z' // 730d #77 - '1R' // 730e #43 - 'A' // 730f - '38U' // 7310 #1008 - 'A' // 7311 - '2Z' // 7312 #77 - '12E' // 7313 #316 - 'a7G' // 7314-7315 #188 - '20K' // 7316 #530 - '12E' // 7317 #316 - '2Z' // 7318 #77 - '25T' // 7319 #669 - 'A' // 731a - '189B' // 731b #4915 - '178P' // 731c #4643 - '128K' // 731d #3338 - '12E' // 731e #316 - '252Q' // 731f #6568 - 'a7G' // 7320-7321 #188 - '12E' // 7322 #316 - 'A' // 7323 - '2Z' // 7324 #77 - '130O' // 7325 #3394 - 'A' // 7326 - '36B' // 7327 #937 - '25T' // 7328 #669 - '20K' // 7329 #530 - '99B' // 732a #2575 - '99D' // 732b #2577 - '12E' // 732c #316 - '14C' // 732d #366 - '99C' // 732e #2576 - '2Z' // 732f #77 - '14C' // 7330 #366 - '12E' // 7331 #316 - '2Z' // 7332 #77 - '36B' // 7333 #937 - '66H' // 7334 #1723 - '36B' // 7335 #937 - '178O' // 7336 #4642 - '20K' // 7337 #530 - '38U' // 7338 #1008 - '57Y' // 7339 #1506 - 'a25T' // 733a-733b #669 - '20L' // 733c #531 - '36B' // 733d #937 - '20K' // 733e #530 - '136R' // 733f #3553 - '14C' // 7340 #366 - '20L' // 7341 #531 - '7G' // 7342 #188 - '25T' // 7343 #669 - '182V' // 7344 #4753 - '187U' // 7345 #4882 - 'aA' // 7346-7347 - '38U' // 7348 #1008 - 'A' // 7349 - '7G' // 734a #188 - 'A' // 734b - '20L' // 734c #531 - '12E' // 734d #316 - '217I' // 734e #5650 - '12E' // 734f #316 - '20K' // 7350 #530 - 'A' // 7351 - '20K' // 7352 #530 - 'aA' // 7353-7354 - '7G' // 7355 #188 - '2Z' // 7356 #77 - '20K' // 7357 #530 - '2Z' // 7358 #77 - '14C' // 7359 #366 - '20L' // 735a #531 - 'aA' // 735b-735c - 'a2Z' // 735d-735e #77 - '36B' // 735f #937 - '12E' // 7360 #316 - '20L' // 7361 #531 - '14C' // 7362 #366 - '255O' // 7363 #6644 - 'A' // 7364 - '14C' // 7365 #366 - 'a2Z' // 7366-7367 #77 - '225D' // 7368 #5853 - '25T' // 7369 #669 - '57X' // 736a #1505 - '25T' // 736b #669 - '20J' // 736c #529 - '7G' // 736d #188 - '34C' // 736e #886 - '20J' // 736f #529 - '25S' // 7370 #668 - '98W' // 7371 #2570 - '227R' // 7372 #5919 - '20L' // 7373 #531 - '14C' // 7374 #366 - '66V' // 7375 #1737 - '7G' // 7376 #188 - '34C' // 7377 #886 - '66V' // 7378 #1737 - '9I' // 7379 #242 - '25S' // 737a #668 - '192U' // 737b #5012 - '34C' // 737c #886 - '7G' // 737d #188 - '14C' // 737e #366 - 'A' // 737f - '34C' // 7380 #886 - '9I' // 7381 #242 - '7G' // 7382 #188 - '70C' // 7383 #1822 - '182Z' // 7384 #4757 - '34C' // 7385 #886 - '25S' // 7386 #668 - '222G' // 7387 #5778 - '7G' // 7388 #188 - '213N' // 7389 #5551 - '20J' // 738a #529 - '235J' // 738b #6119 - '99F' // 738c #2579 - '7G' // 738d #188 - '25S' // 738e #668 - '14C' // 738f #366 - '70C' // 7390 #1822 - '7G' // 7391 #188 - '14C' // 7392 #366 - '20J' // 7393 #529 - '98X' // 7394 #2571 - '20J' // 7395 #529 - '122W' // 7396 #3194 - 'a25S' // 7397-7398 #668 - 'a7G' // 7399-739a #188 - '1R' // 739b #43 - '45U' // 739c #1190 - 'A' // 739d - '45U' // 739e #1190 - '128H' // 739f #3335 - '20J' // 73a0 #529 - '14C' // 73a1 #366 - '20J' // 73a2 #529 - '7G' // 73a3 #188 - 'A' // 73a4 - 'a20J' // 73a5-73a6 #529 - '99E' // 73a7 #2578 - '20J' // 73a8 #529 - '233M' // 73a9 #6070 - '45U' // 73aa #1190 - '66T' // 73ab #1735 - 'A' // 73ac - '25S' // 73ad #668 - '3X' // 73ae #101 - '1Z' // 73af #51 - '7K' // 73b0 #192 - '7G' // 73b1 #188 - '189A' // 73b2 #4914 - '25S' // 73b3 #668 - '45W' // 73b4 #1192 - '38V' // 73b5 #1009 - '99G' // 73b6 #2580 - '57W' // 73b7 #1504 - '45W' // 73b8 #1192 - '57V' // 73b9 #1503 - '98Y' // 73ba #2572 - '192V' // 73bb #5013 - '38V' // 73bc #1009 - '9I' // 73bd #242 - 'A' // 73be - '38V' // 73bf #1009 - '135M' // 73c0 #3522 - 'A' // 73c1 - '99A' // 73c2 #2574 - 'A' // 73c3 - '45W' // 73c4 #1192 - 'a57W' // 73c5-73c6 #1504 - '7G' // 73c7 #188 - '148R' // 73c8 #3865 - '57V' // 73c9 #1503 - '171B' // 73ca #4447 - '38V' // 73cb #1009 - '22W' // 73cc #594 - '203R' // 73cd #5295 - '45T' // 73ce #1189 - '22W' // 73cf #594 - '38T' // 73d0 #1007 - '3G' // 73d1 #84 - '45T' // 73d2 #1189 - '9I' // 73d3 #242 - 'A' // 73d4 - '12B' // 73d5 #313 - '22W' // 73d6 #594 - '12D' // 73d7 #315 - 'A' // 73d8 - '22W' // 73d9 #594 - 'A' // 73da - 'a38T' // 73db-73dc #1007 - 'a22W' // 73dd-73de #594 - 'A' // 73df - '209K' // 73e0 #5444 - '25R' // 73e1 #667 - '12B' // 73e2 #313 - '22W' // 73e3 #594 - '98J' // 73e4 #2557 - 'a22W' // 73e5-73e6 #594 - '45T' // 73e7 #1189 - '38T' // 73e8 #1007 - 'a22W' // 73e9-73ea #594 - '12D' // 73eb #315 - 'A' // 73ec - '218I' // 73ed #5676 - '133I' // 73ee #3466 - '38T' // 73ef #1007 - '9N' // 73f0 #247 - '9I' // 73f1 #242 - '9N' // 73f2 #247 - '12B' // 73f3 #313 - '31K' // 73f4 #816 - '7M' // 73f5 #194 - '12D' // 73f6 #315 - '12C' // 73f7 #314 - '9I' // 73f8 #242 - '12C' // 73f9 #314 - '7M' // 73fa #194 - '25R' // 73fb #667 - '18L' // 73fc #479 - '12C' // 73fd #314 - '244F' // 73fe #6349 - '31K' // 73ff #816 - '7M' // 7400 #194 - '12C' // 7401 #314 - '57T' // 7402 #1501 - '235B' // 7403 #6111 - '7M' // 7404 #194 - '98M' // 7405 #2560 - '35U' // 7406 #930 - '12C' // 7407 #314 - '18L' // 7408 #479 - '160J' // 7409 #4169 - '7M' // 740a #194 - 'b18L' // 740b-740d #479 - 'a9N' // 740e-740f #247 - '3X' // 7410 #101 - '25R' // 7411 #667 - '12B' // 7412 #313 - '70B' // 7413 #1821 - 'a12B' // 7414-7415 #313 - '12D' // 7416 #315 - '12B' // 7417 #313 - '9N' // 7418 #247 - '12B' // 7419 #313 - '7M' // 741a #194 - '12C' // 741b #314 - '57T' // 741c #1501 - '12D' // 741d #315 - '18L' // 741e #479 - '12B' // 741f #313 - '98U' // 7420 #2568 - '98N' // 7421 #2561 - '98P' // 7422 #2563 - '12D' // 7423 #315 - '7M' // 7424 #194 - '57R' // 7425 #1499 - '139M' // 7426 #3626 - 'A' // 7427 - '12C' // 7428 #314 - '7M' // 7429 #194 - '171C' // 742a #4448 - '70B' // 742b #1821 - '12C' // 742c #314 - '7M' // 742d #194 - 'b12C' // 742e-7430 #314 - '29R' // 7431 #771 - '98Q' // 7432 #2564 - '178N' // 7433 #4641 - '181U' // 7434 #4726 - 'a57R' // 7435-7436 #1499 - '12B' // 7437 #313 - '57S' // 7438 #1500 - '7M' // 7439 #194 - '12C' // 743a #314 - '9N' // 743b #247 - '98V' // 743c #2569 - 'A' // 743d - '9N' // 743e #247 - 'b12C' // 743f-7441 #314 - '12D' // 7442 #315 - '98K' // 7443 #2558 - '133G' // 7444 #3464 - '18L' // 7445 #479 - '7M' // 7446 #194 - '25R' // 7447 #667 - '18L' // 7448 #479 - '12D' // 7449 #315 - '18L' // 744a #479 - '164C' // 744b #4266 - '12B' // 744c #313 - '9I' // 744d #242 - 'bA' // 744e-7450 - '29R' // 7451 #771 - '7M' // 7452 #194 - '25R' // 7453 #667 - '12D' // 7454 #315 - '164D' // 7455 #4267 - '12B' // 7456 #313 - '12C' // 7457 #314 - 'A' // 7458 - '98L' // 7459 #2559 - '133H' // 745a #3465 - '125D' // 745b #3253 - '178M' // 745c #4640 - '7M' // 745d #194 - '209D' // 745e #5437 - '157P' // 745f #4097 - '98R' // 7460 #2565 - '12D' // 7461 #315 - '12C' // 7462 #314 - '128I' // 7463 #3336 - '145R' // 7464 #3787 - '57S' // 7465 #1500 - '9I' // 7466 #242 - '7M' // 7467 #194 - '12C' // 7468 #314 - '157O' // 7469 #4096 - '192T' // 746a #5011 - '25R' // 746b #667 - '12D' // 746c #315 - '7M' // 746d #194 - '29R' // 746e #771 - '122V' // 746f #3193 - '66T' // 7470 #1735 - '7M' // 7471 #194 - '29R' // 7472 #771 - '7M' // 7473 #194 - '18L' // 7474 #479 - '12D' // 7475 #315 - '57Q' // 7476 #1498 - '9N' // 7477 #247 - 'A' // 7478 - '12D' // 7479 #315 - '18L' // 747a #479 - 'A' // 747b - 'a12D' // 747c-747d #315 - '12C' // 747e #314 - '12D' // 747f #315 - '128J' // 7480 #3337 - '7M' // 7481 #194 - '98T' // 7482 #2567 - '195E' // 7483 #5074 - '9N' // 7484 #247 - '29R' // 7485 #771 - '7M' // 7486 #194 - '133F' // 7487 #3463 - '7M' // 7488 #194 - '57P' // 7489 #1497 - '18L' // 748a #479 - '122U' // 748b #3192 - 'a12B' // 748c-748d #313 - '9N' // 748e #247 - '31K' // 748f #816 - '57Q' // 7490 #1498 - '9I' // 7491 #242 - '29R' // 7492 #771 - '9N' // 7493 #247 - 'bA' // 7494-7496 - '9I' // 7497 #242 - '57P' // 7498 #1497 - '25R' // 7499 #667 - '7M' // 749a #194 - '12B' // 749b #313 - '34A' // 749c #884 - '57U' // 749d #1502 - 'a34A' // 749e-749f #884 - '34B' // 74a0 #885 - '45S' // 74a1 #1188 - '9I' // 74a2 #242 - '34A' // 74a3 #884 - '12B' // 74a4 #313 - '45S' // 74a5 #1188 - '34B' // 74a6 #885 - '98S' // 74a7 #2566 - '128G' // 74a8 #3334 - '34B' // 74a9 #885 - '34A' // 74aa #884 - '98O' // 74ab #2562 - 'aA' // 74ac-74ad - '31K' // 74ae #816 - '9I' // 74af #242 - '229S' // 74b0 #5972 - '34B' // 74b1 #885 - '34A' // 74b2 #884 - 'A' // 74b3 - '12B' // 74b4 #313 - '45S' // 74b5 #1188 - '9N' // 74b6 #247 - 'A' // 74b7 - '57U' // 74b8 #1502 - '98I' // 74b9 #2556 - '34B' // 74ba #885 - '9I' // 74bb #242 - 'A' // 74bc - '64S' // 74bd #1682 - 'A' // 74be - '29Q' // 74bf #770 - 'A' // 74c0 - '9N' // 74c1 #247 - 'A' // 74c2 - '9N' // 74c3 #247 - 'A' // 74c4 - '98H' // 74c5 #2555 - '98E' // 74c6 #2552 - 'A' // 74c7 - '22U' // 74c8 #592 - '9I' // 74c9 #242 - '150Y' // 74ca #3924 - 'A' // 74cb - '22U' // 74cc #592 - 'aA' // 74cd-74ce - '64S' // 74cf #1682 - '33Y' // 74d0 #882 - 'a9N' // 74d1-74d2 #247 - '33Y' // 74d3 #882 - '29Q' // 74d4 #770 - '9N' // 74d5 #247 - '45Q' // 74d6 #1186 - 'A' // 74d7 - '29Q' // 74d8 #770 - '9N' // 74d9 #247 - '29Q' // 74da #770 - '31K' // 74db #816 - '194S' // 74dc #5062 - 'A' // 74dd - 'a22U' // 74de-74df #592 - '29Q' // 74e0 #770 - 'A' // 74e1 - '97T' // 74e2 #2541 - '150Z' // 74e3 #3925 - '22U' // 74e4 #592 - '9N' // 74e5 #247 - '188A' // 74e6 #4888 - '57O' // 74e7 #1496 - 'a22U' // 74e8-74e9 #592 - 'a9I' // 74ea-74eb #242 - 'aA' // 74ec-74ed - '29Q' // 74ee #770 - '31K' // 74ef #816 - 'b33Y' // 74f0-74f2 #882 - 'A' // 74f3 - '22U' // 74f4 #592 - 'A' // 74f5 - '196J' // 74f6 #5105 - '164B' // 74f7 #4265 - '33Y' // 74f8 #882 - 'A' // 74f9 - '9I' // 74fa #242 - '45Q' // 74fb #1186 - '9I' // 74fc #242 - 'aA' // 74fd-74fe - '22U' // 74ff #592 - '22V' // 7500 #593 - '249I' // 7501 #6482 - 'A' // 7502 - '22U' // 7503 #592 - '157N' // 7504 #4095 - '57O' // 7505 #1496 - '9I' // 7506 #242 - '22V' // 7507 #593 - '9N' // 7508 #247 - 'bA' // 7509-750b - '45Q' // 750c #1186 - '97S' // 750d #2540 - '33Y' // 750e #882 - '13N' // 750f #351 - 'A' // 7510 - '9F' // 7511 #239 - '3P' // 7512 #93 - '5A' // 7513 #130 - 'A' // 7514 - '9F' // 7515 #239 - '25Q' // 7516 #666 - '5A' // 7517 #130 - '198M' // 7518 #5160 - '33Z' // 7519 #883 - '208P' // 751a #5423 - '260U' // 751b #6780 - '214N' // 751c #5577 - 'A' // 751d - '25Q' // 751e #666 - '69L' // 751f #1805 - '3P' // 7520 #93 - '5A' // 7521 #130 - '239T' // 7522 #6233 - '259S' // 7523 #6752 - '11C' // 7524 #288 - '97U' // 7525 #2542 - '128D' // 7526 #3331 - '11C' // 7527 #288 - '69K' // 7528 #1804 - '164A' // 7529 #4264 - '5A' // 752a #130 - '139K' // 752b #3624 - '9F' // 752c #239 - '33Z' // 752d #883 - '22V' // 752e #593 - '5A' // 752f #130 - '216S' // 7530 #5634 - '236S' // 7531 #6154 - '211W' // 7532 #5508 - '213Y' // 7533 #5562 - '33Z' // 7534 #883 - '49C' // 7535 #1276 - '11C' // 7536 #288 - '237P' // 7537 #6177 - '150U' // 7538 #3920 - '3P' // 7539 #93 - '162H' // 753a #4219 - '149P' // 753b #3889 - 'a3P' // 753c-753d #93 - '5A' // 753e #130 - '3P' // 753f #93 - '133D' // 7540 #3461 - 'A' // 7541 - '22V' // 7542 #593 - '3P' // 7543 #93 - '11C' // 7544 #288 - '3Y' // 7545 #102 - '45P' // 7546 #1185 - '9F' // 7547 #239 - '5A' // 7548 #130 - '11C' // 7549 #288 - 'a5A' // 754a-754b #130 - '237G' // 754c #6168 - '97R' // 754d #2539 - '5A' // 754e #130 - '65D' // 754f #1693 - '11C' // 7550 #288 - '98D' // 7551 #2551 - '11C' // 7552 #288 - '98F' // 7553 #2553 - '141S' // 7554 #3684 - '57M' // 7555 #1494 - '13N' // 7556 #351 - '11C' // 7557 #288 - 'A' // 7558 - '234E' // 7559 #6088 - '5A' // 755a #130 - '9F' // 755b #239 - '148Z' // 755c #3873 - '9F' // 755d #239 - '11C' // 755e #288 - '3P' // 755f #93 - '97W' // 7560 #2544 - '3P' // 7561 #93 - '207F' // 7562 #5387 - '22V' // 7563 #593 - '5A' // 7564 #130 - '220P' // 7565 #5735 - '9F' // 7566 #239 - '5A' // 7567 #130 - '13N' // 7568 #351 - '3P' // 7569 #93 - '199I' // 756a #5182 - '225C' // 756b #5852 - '5A' // 756c #130 - '25Q' // 756d #666 - '22V' // 756e #593 - '9F' // 756f #239 - '223D' // 7570 #5801 - '11C' // 7571 #288 - '5A' // 7572 #130 - '255B' // 7573 #6631 - '249H' // 7574 #6481 - '249G' // 7575 #6480 - '239S' // 7576 #6232 - '25Q' // 7577 #666 - '97P' // 7578 #2537 - '5A' // 7579 #130 - '9F' // 757a #239 - 'a11C' // 757b-757c #288 - '3P' // 757d #93 - '5A' // 757e #130 - '98C' // 757f #2550 - 'A' // 7580 - 'a11C' // 7581-7582 #288 - 'a33Z' // 7583-7584 #883 - '3P' // 7585 #93 - '150W' // 7586 #3922 - '9F' // 7587 #239 - 'A' // 7588 - '11C' // 7589 #288 - '178L' // 758a #4639 - '9F' // 758b #239 - '5A' // 758c #130 - '33Z' // 758d #883 - '98B' // 758e #2549 - '171A' // 758f #4446 - '5A' // 7590 #130 - '211Y' // 7591 #5510 - '5A' // 7592 #130 - '3P' // 7593 #93 - '5A' // 7594 #130 - '25Q' // 7595 #666 - '13N' // 7596 #351 - '2C' // 7597 #54 - 'A' // 7598 - 'a5A' // 7599-759a #130 - 'A' // 759b - '3P' // 759c #93 - '9F' // 759d #239 - '57M' // 759e #1494 - 'a13N' // 759f-75a0 #351 - '2W' // 75a1 #74 - '25Q' // 75a2 #666 - '5A' // 75a3 #130 - '133E' // 75a4 #3462 - '9F' // 75a5 #239 - 'A' // 75a6 - '22V' // 75a7 #593 - '13N' // 75a8 #351 - 'A' // 75a9 - '22V' // 75aa #593 - '166U' // 75ab #4336 - 'a13N' // 75ac-75ad #351 - '3X' // 75ae #101 - '3N' // 75af #91 - '5A' // 75b0 #130 - '9F' // 75b1 #239 - '169C' // 75b2 #4396 - '9F' // 75b3 #239 - '5A' // 75b4 #130 - '150T' // 75b5 #3919 - 'A' // 75b6 - '3P' // 75b7 #93 - '9F' // 75b8 #239 - '142C' // 75b9 #3694 - '3P' // 75ba #93 - 'A' // 75bb - '172T' // 75bc #4491 - '9F' // 75bd #239 - '188X' // 75be #4911 - '3P' // 75bf #93 - '25Q' // 75c0 #666 - '11C' // 75c1 #288 - '9F' // 75c2 #239 - 'a5A' // 75c3-75c4 #130 - '212Y' // 75c5 #5536 - '3P' // 75c6 #93 - '198U' // 75c7 #5168 - '98G' // 75c8 #2554 - '13N' // 75c9 #351 - '5A' // 75ca #130 - 'A' // 75cb - '5A' // 75cc #130 - '33X' // 75cd #881 - 'a3P' // 75ce-75cf #93 - 'aA' // 75d0-75d1 - '97V' // 75d2 #2543 - '3P' // 75d3 #93 - '33X' // 75d4 #881 - '180P' // 75d5 #4695 - '13N' // 75d6 #351 - '3P' // 75d7 #93 - '65D' // 75d8 #1693 - '98A' // 75d9 #2548 - 'A' // 75da - '211X' // 75db #5509 - '45P' // 75dc #1185 - '3P' // 75dd #93 - '185R' // 75de #4827 - '57N' // 75df #1495 - '128F' // 75e0 #3333 - '3P' // 75e1 #93 - '97X' // 75e2 #2545 - 'a20I' // 75e3-75e4 #528 - 'A' // 75e5 - '45R' // 75e6 #1187 - '20I' // 75e7 #528 - '13N' // 75e8 #351 - '254Q' // 75e9 #6620 - '13N' // 75ea #351 - '3G' // 75eb #84 - '3P' // 75ec #93 - 'A' // 75ed - 'a3P' // 75ee-75ef #93 - '97O' // 75f0 #2536 - '20I' // 75f1 #528 - '33X' // 75f2 #881 - '20I' // 75f3 #528 - '161V' // 75f4 #4207 - 'aA' // 75f5-75f6 - '45R' // 75f7 #1187 - 'A' // 75f8 - '20I' // 75f9 #528 - '97Y' // 75fa #2546 - 'A' // 75fb - '33X' // 75fc #881 - 'A' // 75fd - 'a20I' // 75fe-75ff #528 - '33X' // 7600 #881 - '20I' // 7601 #528 - '57N' // 7602 #1495 - '11C' // 7603 #288 - '3P' // 7604 #93 - 'a13N' // 7605-7606 #351 - '45P' // 7607 #1185 - 'b20I' // 7608-760a #528 - '200C' // 760b #5202 - '20I' // 760c #528 - '97Z' // 760d #2547 - '13N' // 760e #351 - '3P' // 760f #93 - '45R' // 7610 #1187 - '13N' // 7611 #351 - '11C' // 7612 #288 - '128E' // 7613 #3332 - '6I' // 7614 #164 - '97Q' // 7615 #2538 - '6X' // 7616 #179 - '6I' // 7617 #164 - '41O' // 7618 #1080 - '25O' // 7619 #664 - '38S' // 761a #1006 - 'b6X' // 761b-761d #179 - '29O' // 761e #768 - 'a25O' // 761f-7620 #664 - '64R' // 7621 #1681 - '25O' // 7622 #664 - '29O' // 7623 #768 - '150V' // 7624 #3921 - '6X' // 7625 #179 - '200B' // 7626 #5201 - '6X' // 7627 #179 - '41O' // 7628 #1080 - '6X' // 7629 #179 - '6I' // 762a #164 - '3X' // 762b #101 - '33V' // 762c #879 - '3P' // 762d #93 - 'a6I' // 762e-762f #164 - '6X' // 7630 #179 - 'A' // 7631 - 'c6X' // 7632-7635 #179 - 'aA' // 7636-7637 - '6X' // 7638 #179 - '41O' // 7639 #1080 - '6X' // 763a #179 - '45O' // 763b #1184 - '6X' // 763c #179 - '6I' // 763d #164 - '2R' // 763e #69 - '6I' // 763f #164 - '6X' // 7640 #179 - '3P' // 7641 #93 - '216A' // 7642 #5616 - '6X' // 7643 #179 - 'a3P' // 7644-7645 #93 - '29O' // 7646 #768 - '122T' // 7647 #3191 - '6X' // 7648 #179 - '29O' // 7649 #768 - 'a3P' // 764a-764b #93 - '187T' // 764c #4881 - '25P' // 764d #665 - '25O' // 764e #664 - '33V' // 764f #879 - 'A' // 7650 - '33V' // 7651 #879 - '183E' // 7652 #4762 - 'A' // 7653 - '25P' // 7654 #665 - '3P' // 7655 #93 - '130V' // 7656 #3401 - 'A' // 7657 - '6X' // 7658 #179 - '3P' // 7659 #93 - '6I' // 765a #164 - 'A' // 765b - '6X' // 765c #179 - 'A' // 765d - '6I' // 765e #164 - '6X' // 765f #179 - 'A' // 7660 - '150S' // 7661 #3918 - '150X' // 7662 #3923 - '6I' // 7663 #164 - '45O' // 7664 #1184 - '6X' // 7665 #179 - '38S' // 7666 #1006 - '29O' // 7667 #768 - '3P' // 7668 #93 - '25O' // 7669 #664 - '3P' // 766a #93 - '3X' // 766b #101 - '25O' // 766c #664 - '29O' // 766d #768 - '163Z' // 766e #4263 - '6X' // 766f #179 - '45O' // 7670 #1184 - '139L' // 7671 #3625 - '64R' // 7672 #1681 - '97M' // 7673 #2534 - '97G' // 7674 #2528 - '6I' // 7675 #164 - '6X' // 7676 #179 - 'A' // 7677 - '25O' // 7678 #664 - '25P' // 7679 #665 - '97L' // 767a #2533 - '35U' // 767b #930 - '246A' // 767c #6396 - '238B' // 767d #6189 - '234R' // 767e #6101 - '25P' // 767f #665 - '3P' // 7680 #93 - '6X' // 7681 #179 - '157M' // 7682 #4094 - '41O' // 7683 #1080 - '69I' // 7684 #1802 - '3P' // 7685 #93 - '213D' // 7686 #5541 - '202W' // 7687 #5274 - '6X' // 7688 #179 - 'A' // 7689 - '25P' // 768a #665 - '6X' // 768b #179 - '21I' // 768c #554 - '3J' // 768d #87 - '45N' // 768e #1183 - 'A' // 768f - '45N' // 7690 #1183 - 'a6I' // 7691-7692 #164 - '139H' // 7693 #3621 - 'A' // 7694 - '14B' // 7695 #365 - '97F' // 7696 #2527 - 'A' // 7697 - '6I' // 7698 #164 - '122S' // 7699 #3190 - 'a14B' // 769a-769b #365 - '38R' // 769c #1005 - 'a14B' // 769d-769e #365 - 'a21I' // 769f-76a0 #554 - '45M' // 76a1 #1182 - '21I' // 76a2 #554 - '3J' // 76a3 #87 - '14B' // 76a4 #365 - '57J' // 76a5 #1491 - 'a21I' // 76a6-76a7 #554 - '3J' // 76a8 #87 - 'A' // 76a9 - '38R' // 76aa #1005 - 'a6I' // 76ab-76ac #164 - '3J' // 76ad #87 - '227Q' // 76ae #5918 - '21I' // 76af #554 - '14B' // 76b0 #365 - '3H' // 76b1 #85 - '6I' // 76b2 #164 - 'A' // 76b3 - '14B' // 76b4 #365 - '6I' // 76b5 #164 - '3J' // 76b6 #87 - 'a38R' // 76b7-76b8 #1005 - '3J' // 76b9 #87 - '157K' // 76ba #4092 - '6I' // 76bb #164 - 'A' // 76bc - '3J' // 76bd #87 - 'A' // 76be - '125S' // 76bf #3268 - 'A' // 76c0 - '3J' // 76c1 #87 - '45N' // 76c2 #1183 - '163X' // 76c3 #4261 - 'A' // 76c4 - '14B' // 76c5 #365 - '174W' // 76c6 #4546 - 'A' // 76c7 - '178J' // 76c8 #4637 - '14B' // 76c9 #365 - '219L' // 76ca #5705 - '3J' // 76cb #87 - 'a14B' // 76cc-76cd #365 - '122Q' // 76ce #3188 - '2W' // 76cf #74 - '4C' // 76d0 #106 - '2C' // 76d1 #54 - '207E' // 76d2 #5386 - 'A' // 76d3 - '63U' // 76d4 #1658 - 'A' // 76d5 - '97E' // 76d6 #2526 - '256L' // 76d7 #6667 - '2D' // 76d8 #55 - '45M' // 76d9 #1182 - 'A' // 76da - '212M' // 76db #5524 - '185Q' // 76dc #4826 - '6I' // 76dd #164 - '122P' // 76de #3187 - '210B' // 76df #5461 - '3J' // 76e0 #87 - '214M' // 76e1 #5576 - '6I' // 76e2 #164 - '211Q' // 76e3 #5502 - '220I' // 76e4 #5728 - 'a14B' // 76e5-76e6 #365 - '178I' // 76e7 #4636 - '21I' // 76e8 #554 - '25P' // 76e9 #665 - '145O' // 76ea #3784 - '3J' // 76eb #87 - '38R' // 76ec #1005 - 'A' // 76ed - '245G' // 76ee #6376 - '143V' // 76ef #3739 - '3J' // 76f0 #87 - '14B' // 76f1 #365 - '159T' // 76f2 #4153 - 'A' // 76f3 - '237N' // 76f4 #6175 - 'A' // 76f5 - '3J' // 76f6 #87 - '38S' // 76f7 #1006 - '243N' // 76f8 #6331 - '14B' // 76f9 #365 - '25P' // 76fa #665 - '14B' // 76fb #365 - '163Y' // 76fc #4262 - '6I' // 76fd #164 - '167A' // 76fe #4342 - '6I' // 76ff #164 - '21I' // 7700 #554 - '211R' // 7701 #5503 - 'A' // 7702 - '6I' // 7703 #164 - '97C' // 7704 #2524 - '38S' // 7705 #1006 - '3J' // 7706 #87 - '57K' // 7707 #1492 - '97B' // 7708 #2523 - '180W' // 7709 #4702 - '38Q' // 770a #1004 - '241D' // 770b #6269 - '97K' // 770c #2532 - '6I' // 770d #164 - '57J' // 770e #1491 - '97N' // 770f #2535 - 'aA' // 7710-7711 - '21I' // 7712 #554 - 'A' // 7713 - '21I' // 7714 #554 - '38Q' // 7715 #1004 - '6I' // 7716 #164 - '3J' // 7717 #87 - 'A' // 7718 - 'b57K' // 7719-771b #1492 - '3J' // 771c #87 - '29P' // 771d #769 - '97J' // 771e #2531 - '244S' // 771f #6362 - '183J' // 7720 #4767 - '6I' // 7721 #164 - '9R' // 7722 #251 - 'A' // 7723 - '45M' // 7724 #1182 - '38Q' // 7725 #1004 - '9R' // 7726 #251 - 'A' // 7727 - '9R' // 7728 #251 - '135V' // 7729 #3531 - 'A' // 772a - '57L' // 772b #1493 - '8Q' // 772c #224 - '9R' // 772d #251 - '23Y' // 772e #622 - '9R' // 772f #251 - '8Q' // 7730 #224 - 'A' // 7731 - '8Q' // 7732 #224 - '33W' // 7733 #880 - 'b9R' // 7734-7736 #251 - '139E' // 7737 #3618 - '97D' // 7738 #2525 - '23Y' // 7739 #622 - '130U' // 773a #3400 - '29P' // 773b #769 - '227U' // 773c #5922 - '9R' // 773d #251 - '217G' // 773e #5648 - '8Q' // 773f #224 - '176Z' // 7740 #4601 - '3G' // 7741 #84 - '3J' // 7742 #87 - '57L' // 7743 #1493 - '29P' // 7744 #769 - '3J' // 7745 #87 - '9R' // 7746 #251 - '178K' // 7747 #4638 - 'aA' // 7748-7749 - '3J' // 774a #87 - 'A' // 774b - '33W' // 774c #880 - '249E' // 774d #6478 - 'a9R' // 774e-774f #251 - '3X' // 7750 #101 - '8Q' // 7751 #224 - '9R' // 7752 #251 - 'aA' // 7753-7754 - '29P' // 7755 #769 - 'a3J' // 7756-7757 #87 - '57I' // 7758 #1490 - '33W' // 7759 #880 - '9R' // 775a #251 - '192S' // 775b #5010 - '63T' // 775c #1657 - '8Q' // 775d #224 - '63T' // 775e #1657 - 'a38Q' // 775f-7760 #1004 - '209N' // 7761 #5447 - '9R' // 7762 #251 - '183H' // 7763 #4765 - '3J' // 7764 #87 - '9R' // 7765 #251 - '97I' // 7766 #2530 - '3J' // 7767 #87 - '97H' // 7768 #2529 - '33W' // 7769 #880 - '9R' // 776a #251 - '145M' // 776b #3782 - '9R' // 776c #251 - '33W' // 776d #880 - '29P' // 776e #769 - 'A' // 776f - '3J' // 7770 #87 - '8Q' // 7771 #224 - '57I' // 7772 #1490 - 'a3J' // 7773-7774 #87 - 'aA' // 7775-7776 - '33V' // 7777 #879 - '29P' // 7778 #769 - '65C' // 7779 #1692 - '9R' // 777a #251 - '33V' // 777b #879 - '23Y' // 777c #622 - '9R' // 777d #251 - '22S' // 777e #590 - '145N' // 777f #3783 - '6W' // 7780 #178 - 'bA' // 7781-7783 - '145P' // 7784 #3785 - '12A' // 7785 #312 - 'A' // 7786 - '12A' // 7787 #312 - 'A' // 7788 - '29N' // 7789 #767 - 'A' // 778a - '22S' // 778b #590 - 'a6W' // 778c-778d #178 - '145Q' // 778e #3786 - 'a8Q' // 778f-7790 #224 - '57H' // 7791 #1489 - '3G' // 7792 #84 - '12A' // 7793 #312 - 'b3J' // 7794-7796 #87 - 'A' // 7797 - '38O' // 7798 #1002 - 'A' // 7799 - '3J' // 779a #87 - 'A' // 779b - '29N' // 779c #767 - 'A' // 779d - '133C' // 779e #3460 - 'a6W' // 779f-77a0 #178 - 'A' // 77a1 - '6W' // 77a2 #178 - 'A' // 77a3 - '3J' // 77a4 #87 - '22S' // 77a5 #590 - 'A' // 77a6 - '139J' // 77a7 #3623 - 'A' // 77a8 - '249F' // 77a9 #6479 - '122R' // 77aa #3189 - 'A' // 77ab - '183K' // 77ac #4768 - '180E' // 77ad #4684 - '3J' // 77ae #87 - '57F' // 77af #1487 - '57D' // 77b0 #1485 - '29M' // 77b1 #766 - 'A' // 77b2 - '142V' // 77b3 #3713 - '12A' // 77b4 #312 - '23Y' // 77b5 #622 - 'a6W' // 77b6-77b7 #178 - 'A' // 77b8 - '29M' // 77b9 #766 - 'A' // 77ba - '139F' // 77bb #3619 - '96X' // 77bc #2519 - 'a6W' // 77bd-77be #178 - '22S' // 77bf #590 - '8Q' // 77c0 #224 - 'A' // 77c1 - '8Q' // 77c2 #224 - '45K' // 77c3 #1180 - 'A' // 77c4 - '12A' // 77c5 #312 - 'A' // 77c6 - '6W' // 77c7 #178 - 'A' // 77c8 - '3J' // 77c9 #87 - 'A' // 77ca - '38O' // 77cb #1002 - '29N' // 77cc #767 - '6W' // 77cd #178 - 'bA' // 77ce-77d0 - 'a3J' // 77d1-77d2 #87 - '29N' // 77d3 #767 - 'A' // 77d4 - '3J' // 77d5 #87 - '8Q' // 77d6 #224 - '22S' // 77d7 #590 - 'A' // 77d8 - '23Y' // 77d9 #622 - '139I' // 77da #3622 - '148E' // 77db #3852 - '96V' // 77dc #2517 - '38P' // 77dd #1003 - '6W' // 77de #178 - 'a3J' // 77df-77e0 #87 - 'A' // 77e1 - '143I' // 77e2 #3726 - '57D' // 77e3 #1485 - '3J' // 77e4 #87 - '35U' // 77e5 #930 - '45K' // 77e6 #1180 - '6W' // 77e7 #178 - 'A' // 77e8 - '139G' // 77e9 #3620 - '23Y' // 77ea #622 - '3X' // 77eb #101 - '6W' // 77ec #178 - '222F' // 77ed #5777 - '65C' // 77ee #1692 - '143A' // 77ef #3718 - '29M' // 77f0 #766 - '3J' // 77f1 #87 - '29N' // 77f2 #767 - '223Q' // 77f3 #5814 - '45K' // 77f4 #1180 - 'A' // 77f5 - '2K' // 77f6 #62 - 'A' // 77f7 - '6W' // 77f8 #178 - 'A' // 77f9 - '29N' // 77fa #767 - '6W' // 77fb #178 - '29M' // 77fc #766 - '162K' // 77fd #4222 - '12A' // 77fe #312 - '1R' // 77ff #43 - '8Q' // 7800 #224 - '3Q' // 7801 #94 - '183D' // 7802 #4761 - '12A' // 7803 #312 - 'A' // 7804 - '29M' // 7805 #766 - '6W' // 7806 #178 - 'A' // 7807 - '38O' // 7808 #1002 - '6W' // 7809 #178 - 'aA' // 780a-780b - '63U' // 780c #1658 - '157L' // 780d #4093 - '3J' // 780e #87 - '8Q' // 780f #224 - '12A' // 7810 #312 - '6W' // 7811 #178 - '22S' // 7812 #590 - 'A' // 7813 - '222Z' // 7814 #5797 - '253T' // 7815 #6597 - '2L' // 7816 #63 - '8Q' // 7817 #224 - '38P' // 7818 #1003 - '3J' // 7819 #87 - 'a8Q' // 781a-781b #224 - '12A' // 781c #312 - '6W' // 781d #178 - '38P' // 781e #1003 - '12A' // 781f #312 - '29M' // 7820 #766 - 'b6W' // 7821-7823 #178 - 'A' // 7824 - '57H' // 7825 #1489 - 'a22S' // 7826-7827 #590 - 'A' // 7828 - '12A' // 7829 #312 - 'A' // 782a - '8Q' // 782b #224 - '22S' // 782c #590 - 'a6W' // 782d-782e #178 - '12A' // 782f #312 - '6W' // 7830 #178 - 'A' // 7831 - '161D' // 7832 #4189 - '12A' // 7833 #312 - '221H' // 7834 #5753 - '6W' // 7835 #178 - 'A' // 7836 - '6W' // 7837 #178 - '155R' // 7838 #4047 - '12A' // 7839 #312 - '23Y' // 783a #622 - '8Q' // 783b #224 - '12A' // 783c #312 - '38O' // 783d #1002 - '8Q' // 783e #224 - '3J' // 783f #87 - '3I' // 7840 #86 - '8Q' // 7841 #224 - '97A' // 7842 #2522 - '57G' // 7843 #1488 - '57F' // 7844 #1487 - '96W' // 7845 #2518 - 'A' // 7846 - '57G' // 7847 #1488 - '23Y' // 7848 #622 - '8Q' // 7849 #224 - '25N' // 784a #663 - '38P' // 784b #1003 - '57E' // 784c #1486 - '25N' // 784d #663 - '57E' // 784e #1486 - '249D' // 784f #6477 - '22T' // 7850 #591 - '57C' // 7851 #1484 - '15L' // 7852 #401 - '22T' // 7853 #591 - '45L' // 7854 #1181 - '4C' // 7855 #106 - 'a6H' // 7856-7857 #163 - 'A' // 7858 - 'a6H' // 7859-785a #163 - 'A' // 785b - '22R' // 785c #589 - '96Y' // 785d #2520 - '3T' // 785e #97 - 'A' // 785f - 'a3T' // 7860-7861 #97 - 'A' // 7862 - '3T' // 7863 #97 - '22R' // 7864 #589 - '6H' // 7865 #163 - '25N' // 7866 #663 - 'A' // 7867 - '22R' // 7868 #589 - '6H' // 7869 #163 - '15L' // 786a #401 - '124X' // 786b #3247 - '209V' // 786c #5455 - '6H' // 786d #163 - '150R' // 786e #3917 - '33U' // 786f #878 - 'aA' // 7870-7871 - '3T' // 7872 #97 - 'A' // 7873 - '3T' // 7874 #97 - 'A' // 7875 - 'a6H' // 7876-7877 #163 - 'aA' // 7878-7879 - '33T' // 787a #877 - 'A' // 787b - '33U' // 787c #878 - 'A' // 787d - '16B' // 787e #417 - '22T' // 787f #591 - 'A' // 7880 - '125L' // 7881 #3261 - 'cA' // 7882-7885 - '22R' // 7886 #589 - '33U' // 7887 #878 - '45L' // 7888 #1181 - '22T' // 7889 #591 - '3T' // 788a #97 - 'A' // 788b - '150Q' // 788c #3916 - '96Q' // 788d #2512 - '185N' // 788e #4823 - '22R' // 788f #589 - 'A' // 7890 - '166I' // 7891 #4324 - 'A' // 7892 - '15L' // 7893 #401 - '16B' // 7894 #417 - '22R' // 7895 #589 - '25N' // 7896 #663 - '187K' // 7897 #4872 - '15L' // 7898 #401 - '6H' // 7899 #163 - '15L' // 789a #401 - 'a6H' // 789b-789c #163 - '16B' // 789d #417 - '15L' // 789e #401 - '185O' // 789f #4824 - 'A' // 78a0 - '15L' // 78a1 #401 - 'A' // 78a2 - '33U' // 78a3 #878 - '3T' // 78a4 #97 - '22T' // 78a5 #591 - 'A' // 78a6 - '180V' // 78a7 #4701 - '3T' // 78a8 #97 - '178G' // 78a9 #4634 - '22R' // 78aa #589 - 'A' // 78ab - '3T' // 78ac #97 - '15L' // 78ad #401 - 'A' // 78ae - '33T' // 78af #877 - '185P' // 78b0 #4825 - '96U' // 78b1 #2516 - '15L' // 78b2 #401 - '178H' // 78b3 #4635 - '22T' // 78b4 #591 - '3T' // 78b5 #97 - '22T' // 78b6 #591 - 'A' // 78b7 - '25N' // 78b8 #663 - '45L' // 78b9 #1181 - '231L' // 78ba #6017 - '249B' // 78bb #6475 - '225B' // 78bc #5851 - '3T' // 78bd #97 - '96T' // 78be #2515 - '16B' // 78bf #417 - 'A' // 78c0 - '181P' // 78c1 #4721 - 'A' // 78c2 - '6H' // 78c3 #163 - 'A' // 78c4 - '157J' // 78c5 #4091 - '3T' // 78c6 #97 - '33T' // 78c7 #877 - '22R' // 78c8 #589 - '15L' // 78c9 #401 - '122N' // 78ca #3185 - '33U' // 78cb #878 - '3T' // 78cc #97 - 'A' // 78cd - '96R' // 78ce #2513 - 'A' // 78cf - '96Z' // 78d0 #2521 - '15L' // 78d1 #401 - '57C' // 78d2 #1484 - '33T' // 78d3 #877 - '15L' // 78d4 #401 - '96S' // 78d5 #2514 - '3T' // 78d6 #97 - 'a25N' // 78d7-78d8 #663 - '6H' // 78d9 #163 - '66S' // 78da #1734 - '3T' // 78db #97 - '6H' // 78dc #163 - 'A' // 78dd - '22T' // 78de #591 - 'a3T' // 78df-78e0 #97 - '128B' // 78e1 #3329 - 'A' // 78e2 - '25N' // 78e3 #663 - '33T' // 78e4 #877 - '6H' // 78e5 #163 - '16B' // 78e6 #417 - '18K' // 78e7 #478 - '197O' // 78e8 #5136 - 'A' // 78e9 - '5R' // 78ea #147 - 'A' // 78eb - '29L' // 78ec #765 - 'A' // 78ed - '20H' // 78ee #527 - '160Z' // 78ef #4185 - '20H' // 78f0 #527 - '57B' // 78f1 #1483 - '5R' // 78f2 #147 - '18K' // 78f3 #478 - '5R' // 78f4 #147 - '96P' // 78f5 #2511 - '3T' // 78f6 #97 - '128C' // 78f7 #3330 - 'A' // 78f8 - '3T' // 78f9 #97 - '5R' // 78fa #147 - '29L' // 78fb #765 - 'A' // 78fc - '18K' // 78fd #478 - '5R' // 78fe #147 - '18K' // 78ff #478 - '3T' // 7900 #97 - '157G' // 7901 #4088 - '6H' // 7902 #163 - 'A' // 7903 - '33S' // 7904 #876 - '25M' // 7905 #662 - '18K' // 7906 #478 - '16B' // 7907 #417 - 'A' // 7908 - '6H' // 7909 #163 - 'A' // 790a - '6H' // 790b #163 - '5R' // 790c #147 - 'A' // 790d - '190G' // 790e #4946 - 'A' // 790f - '5R' // 7910 #147 - 'a18K' // 7911-7912 #478 - '6H' // 7913 #163 - 'aA' // 7914-7915 - '260S' // 7916 #6778 - 'aA' // 7917-7918 - '192R' // 7919 #5009 - 'a16B' // 791a-791b #417 - '18K' // 791c #478 - 'A' // 791d - '5R' // 791e #147 - '16B' // 791f #417 - '3T' // 7920 #97 - '25M' // 7921 #662 - 'aA' // 7922-7923 - '6H' // 7924 #163 - '3T' // 7925 #97 - '66S' // 7926 #1734 - 'b3T' // 7927-7929 #97 - 'a29L' // 792a-792b #765 - '96H' // 792c #2503 - '3T' // 792d #97 - '18K' // 792e #478 - 'A' // 792f - '3T' // 7930 #97 - '18K' // 7931 #478 - 'a20H' // 7932-7933 #527 - '5R' // 7934 #147 - '3T' // 7935 #97 - '57B' // 7936 #1483 - 'A' // 7937 - '33S' // 7938 #876 - '6H' // 7939 #163 - '239A' // 793a #6214 - '5R' // 793b #147 - '125Z' // 793c #3275 - '5R' // 793d #147 - '49H' // 793e #1281 - '18K' // 793f #478 - '141Q' // 7940 #3682 - '96F' // 7941 #2501 - '5R' // 7942 #147 - '6H' // 7943 #163 - '3T' // 7944 #97 - 'a5R' // 7945-7946 #147 - '96K' // 7947 #2506 - '168C' // 7948 #4370 - '96L' // 7949 #2507 - '16B' // 794a #417 - '3T' // 794b #97 - '25M' // 794c #662 - 'A' // 794d - '6H' // 794e #163 - '16B' // 794f #417 - '154X' // 7950 #4027 - '16B' // 7951 #417 - 'A' // 7952 - '96J' // 7953 #2505 - '5R' // 7954 #147 - '170Z' // 7955 #4445 - '196T' // 7956 #5115 - '29L' // 7957 #765 - '5R' // 7958 #147 - '25M' // 7959 #662 - '29L' // 795a #765 - '96G' // 795b #2502 - '29L' // 795c #765 - '199C' // 795d #5176 - '238O' // 795e #6202 - '5R' // 795f #147 - '128A' // 7960 #3328 - '33S' // 7961 #876 - '5R' // 7962 #147 - 'A' // 7963 - '25M' // 7964 #662 - '189P' // 7965 #4929 - '6H' // 7966 #163 - '5R' // 7967 #147 - '220C' // 7968 #5722 - '5R' // 7969 #147 - 'A' // 796a - '5R' // 796b #147 - 'A' // 796c - '183W' // 796d #4780 - 'A' // 796e - '6H' // 796f #163 - 'A' // 7970 - '20H' // 7971 #527 - '5R' // 7972 #147 - '33S' // 7973 #876 - '6H' // 7974 #163 - 'aA' // 7975-7976 - '249C' // 7977 #6476 - '2Y' // 7978 #76 - '5R' // 7979 #147 - '122L' // 797a #3183 - '16B' // 797b #417 - '5R' // 797c #147 - 'A' // 797d - '5R' // 797e #147 - '139B' // 797f #3615 - '5R' // 7980 #147 - '213H' // 7981 #5545 - '25M' // 7982 #662 - '20H' // 7983 #527 - '252H' // 7984 #6559 - '253G' // 7985 #6584 - 'a25M' // 7986-7987 #662 - '33S' // 7988 #876 - '6H' // 7989 #163 - '96I' // 798a #2504 - '9E' // 798b #238 - '31J' // 798c #815 - '178F' // 798d #4633 - '133A' // 798e #3458 - '230Z' // 798f #6005 - 'A' // 7990 - '56Y' // 7991 #1480 - '9M' // 7992 #246 - '25K' // 7993 #660 - 'a9E' // 7994-7995 #238 - '25K' // 7996 #660 - '9M' // 7997 #246 - '9E' // 7998 #238 - '20H' // 7999 #527 - '25L' // 799a #661 - '9E' // 799b #238 - '31J' // 799c #815 - '29K' // 799d #764 - 'A' // 799e - '25L' // 799f #661 - '33R' // 79a0 #875 - '25K' // 79a1 #660 - '33R' // 79a2 #875 - '9M' // 79a3 #246 - '25L' // 79a4 #661 - '57A' // 79a5 #1482 - '65S' // 79a6 #1708 - '139C' // 79a7 #3616 - 'a9E' // 79a8-79a9 #238 - '65S' // 79aa #1708 - '31J' // 79ab #815 - '9M' // 79ac #246 - 'A' // 79ad - '225A' // 79ae #5850 - '31J' // 79af #815 - '9E' // 79b0 #238 - '139A' // 79b1 #3614 - 'A' // 79b2 - '45J' // 79b3 #1179 - '31J' // 79b4 #815 - '9M' // 79b5 #246 - 'aA' // 79b6-79b7 - '9E' // 79b8 #238 - '122M' // 79b9 #3184 - '9E' // 79ba #238 - '122O' // 79bb #3186 - 'A' // 79bc - '139D' // 79bd #3617 - '157H' // 79be #4089 - '124P' // 79bf #3239 - '215I' // 79c0 #5598 - '237F' // 79c1 #6167 - '31J' // 79c2 #815 - '2W' // 79c3 #74 - '29K' // 79c4 #764 - 'A' // 79c5 - '25L' // 79c6 #661 - '3T' // 79c7 #97 - '9E' // 79c8 #238 - '157I' // 79c9 #4090 - '96E' // 79ca #2500 - '213L' // 79cb #5549 - '29K' // 79cc #764 - '133B' // 79cd #3459 - 'A' // 79ce - '9E' // 79cf #238 - '20H' // 79d0 #527 - '235X' // 79d1 #6133 - '210V' // 79d2 #5481 - 'A' // 79d3 - '29K' // 79d4 #764 - '45J' // 79d5 #1179 - '9E' // 79d6 #238 - 'A' // 79d7 - '221A' // 79d8 #5746 - 'A' // 79d9 - '3T' // 79da #97 - 'A' // 79db - '33R' // 79dc #875 - 'a9E' // 79dd-79de #238 - '200A' // 79df #5200 - 'a3T' // 79e0-79e1 #97 - '29K' // 79e2 #764 - '9E' // 79e3 #238 - '148Y' // 79e4 #3872 - '3T' // 79e5 #97 - '160B' // 79e6 #4161 - '45J' // 79e7 #1179 - '9M' // 79e8 #246 - '136H' // 79e9 #3543 - '25K' // 79ea #660 - '9E' // 79eb #238 - '25K' // 79ec #660 - '9E' // 79ed #238 - 'A' // 79ee - '2D' // 79ef #55 - '257J' // 79f0 #6691 - '29K' // 79f1 #764 - 'aA' // 79f2-79f3 - '25L' // 79f4 #661 - 'A' // 79f5 - '33R' // 79f6 #875 - '25L' // 79f7 #661 - '9E' // 79f8 #238 - 'A' // 79f9 - '9M' // 79fa #246 - '213M' // 79fb #5550 - '3T' // 79fc #97 - '2Y' // 79fd #76 - '9M' // 79fe #246 - 'A' // 79ff - '174J' // 7a00 #4533 - 'A' // 7a01 - 'a9E' // 7a02-7a03 #238 - 'A' // 7a04 - '199Z' // 7a05 #5199 - '20H' // 7a06 #527 - '3T' // 7a07 #97 - '56Y' // 7a08 #1480 - '3T' // 7a09 #97 - '9E' // 7a0a #238 - '241V' // 7a0b #6287 - '25K' // 7a0c #660 - '192Q' // 7a0d #5008 - '96O' // 7a0e #2510 - 'A' // 7a0f - '33R' // 7a10 #875 - '25K' // 7a11 #660 - 'aA' // 7a12-7a13 - '56Z' // 7a14 #1481 - '3T' // 7a15 #97 - 'A' // 7a16 - '38N' // 7a17 #1001 - '56X' // 7a18 #1479 - '38N' // 7a19 #1001 - '161Y' // 7a1a #4210 - '2O' // 7a1b #66 - '56Z' // 7a1c #1481 - 'A' // 7a1d - '56W' // 7a1e #1478 - '38N' // 7a1f #1001 - '127X' // 7a20 #3325 - '2O' // 7a21 #66 - 'A' // 7a22 - '9M' // 7a23 #246 - 'A' // 7a24 - '9M' // 7a25 #246 - '25L' // 7a26 #661 - '2O' // 7a27 #66 - 'aA' // 7a28-7a29 - '20H' // 7a2a #527 - '2O' // 7a2b #66 - '57A' // 7a2c #1482 - '56X' // 7a2d #1479 - '237V' // 7a2e #6183 - '2O' // 7a2f #66 - '19H' // 7a30 #501 - '232J' // 7a31 #6041 - '96N' // 7a32 #2509 - '3I' // 7a33 #86 - 'a2O' // 7a34-7a35 #66 - '260T' // 7a36 #6779 - '38N' // 7a37 #1001 - '2O' // 7a38 #66 - '56W' // 7a39 #1478 - '56V' // 7a3a #1477 - '170Y' // 7a3b #4444 - '96M' // 7a3c #2508 - '136G' // 7a3d #3542 - '56V' // 7a3e #1477 - '206K' // 7a3f #5366 - '153Q' // 7a40 #3994 - 'A' // 7a41 - '255N' // 7a42 #6643 - '18H' // 7a43 #475 - '2O' // 7a44 #66 - '18J' // 7a45 #477 - '145L' // 7a46 #3781 - '19H' // 7a47 #501 - '2O' // 7a48 #66 - '29J' // 7a49 #763 - 'aA' // 7a4a-7a4b - '132Y' // 7a4c #3456 - '228X' // 7a4d #5951 - '66U' // 7a4e #1736 - '253Y' // 7a4f #6602 - '2O' // 7a50 #66 - '9M' // 7a51 #246 - 'bA' // 7a52-7a54 - '19H' // 7a55 #501 - '18J' // 7a56 #477 - '145J' // 7a57 #3779 - 'A' // 7a58 - '2O' // 7a59 #66 - '38M' // 7a5a #1000 - '9M' // 7a5b #246 - '18J' // 7a5c #477 - '19H' // 7a5d #501 - '9M' // 7a5e #246 - '2O' // 7a5f #66 - '18J' // 7a60 #477 - '56T' // 7a61 #1475 - '150P' // 7a62 #3915 - '19H' // 7a63 #501 - 'A' // 7a64 - '18H' // 7a65 #475 - '9M' // 7a66 #246 - '2O' // 7a67 #66 - '38L' // 7a68 #999 - '199Y' // 7a69 #5198 - '2O' // 7a6a #66 - '142N' // 7a6b #3705 - 'A' // 7a6c - '18J' // 7a6d #477 - '38M' // 7a6e #1000 - 'A' // 7a6f - '29J' // 7a70 #763 - '38M' // 7a71 #1000 - '45G' // 7a72 #1176 - 'A' // 7a73 - '169F' // 7a74 #4399 - '2O' // 7a75 #66 - '222M' // 7a76 #5784 - '4C' // 7a77 #106 - '11Z' // 7a78 #311 - '95P' // 7a79 #2485 - '237Z' // 7a7a #6187 - 'aA' // 7a7b-7a7c - '95M' // 7a7d #2482 - '2O' // 7a7e #66 - '218F' // 7a7f #5673 - '11Z' // 7a80 #311 - '213C' // 7a81 #5540 - '2O' // 7a82 #66 - '95V' // 7a83 #2491 - '157F' // 7a84 #4087 - 'a11Z' // 7a85-7a86 #311 - '9M' // 7a87 #246 - '29J' // 7a88 #763 - 'A' // 7a89 - '19H' // 7a8a #501 - '2O' // 7a8b #66 - 'A' // 7a8c - '2K' // 7a8d #62 - 'aA' // 7a8e-7a8f - '11Z' // 7a90 #311 - '95R' // 7a91 #2487 - '125A' // 7a92 #3250 - '96D' // 7a93 #2499 - '11Z' // 7a94 #311 - '29J' // 7a95 #763 - '11Z' // 7a96 #311 - '207D' // 7a97 #5385 - '29J' // 7a98 #763 - 'aA' // 7a99-7a9a - 'a9M' // 7a9b-7a9c #246 - '2L' // 7a9d #63 - '19H' // 7a9e #501 - '136F' // 7a9f #3541 - '11Z' // 7aa0 #311 - '9M' // 7aa1 #246 - 'A' // 7aa2 - '11Z' // 7aa3 #311 - 'A' // 7aa4 - '3H' // 7aa5 #85 - '3G' // 7aa6 #84 - 'A' // 7aa7 - '38L' // 7aa8 #999 - '66U' // 7aa9 #1736 - '95Z' // 7aaa #2495 - 'A' // 7aab - '11Z' // 7aac #311 - '9M' // 7aad #246 - '180O' // 7aae #4694 - '130L' // 7aaf #3391 - '11Z' // 7ab0 #311 - 'aA' // 7ab1-7ab2 - '11Z' // 7ab3 #311 - 'A' // 7ab4 - '2O' // 7ab5 #66 - '18J' // 7ab6 #477 - 'A' // 7ab7 - '38L' // 7ab8 #999 - '2O' // 7ab9 #66 - '145I' // 7aba #3778 - '18J' // 7abb #477 - '18H' // 7abc #475 - '19H' // 7abd #501 - 'a11Z' // 7abe-7abf #311 - 'aA' // 7ac0-7ac1 - '38M' // 7ac2 #1000 - '18H' // 7ac3 #475 - '135J' // 7ac4 #3519 - '138Z' // 7ac5 #3613 - '2O' // 7ac6 #66 - '122K' // 7ac7 #3182 - '11Z' // 7ac8 #311 - '18J' // 7ac9 #477 - '157E' // 7aca #4086 - '68W' // 7acb #1790 - 'b2O' // 7acc-7ace #66 - '18H' // 7acf #475 - 'A' // 7ad0 - '11Z' // 7ad1 #311 - '19H' // 7ad2 #501 - '18H' // 7ad3 #475 - 'A' // 7ad4 - '2O' // 7ad5 #66 - '3H' // 7ad6 #85 - '71D' // 7ad7 #1849 - 'A' // 7ad8 - '240W' // 7ad9 #6262 - '11Z' // 7ada #311 - '95Q' // 7adb #2486 - '96C' // 7adc #2498 - '29J' // 7add #763 - '3I' // 7ade #86 - '214L' // 7adf #5575 - '234C' // 7ae0 #6086 - '19H' // 7ae1 #501 - '18H' // 7ae2 #475 - '124W' // 7ae3 #3246 - '38L' // 7ae4 #999 - '227W' // 7ae5 #5924 - '56U' // 7ae6 #1476 - '18H' // 7ae7 #475 - '2O' // 7ae8 #66 - '18J' // 7ae9 #477 - '56T' // 7aea #1475 - '18J' // 7aeb #477 - '2O' // 7aec #66 - '145K' // 7aed #3780 - 'A' // 7aee - '212Q' // 7aef #5528 - 'a2O' // 7af0-7af1 #66 - 'aA' // 7af2-7af3 - '14Q' // 7af4 #380 - '7W' // 7af5 #204 - '204V' // 7af6 #5325 - '7W' // 7af7 #204 - '2O' // 7af8 #66 - '215K' // 7af9 #5600 - '95O' // 7afa #2484 - '56U' // 7afb #1476 - '45G' // 7afc #1176 - 'a5H' // 7afd-7afe #137 - '135U' // 7aff #3530 - '7W' // 7b00 #204 - '14A' // 7b01 #364 - '14Q' // 7b02 #380 - '7W' // 7b03 #204 - '5H' // 7b04 #137 - '14A' // 7b05 #364 - '5H' // 7b06 #137 - '14Q' // 7b07 #380 - '127Y' // 7b08 #3326 - '14A' // 7b09 #364 - '5H' // 7b0a #137 - '95S' // 7b0b #2488 - '38K' // 7b0c #998 - 'A' // 7b0d - '14A' // 7b0e #364 - '18I' // 7b0f #476 - '45I' // 7b10 #1178 - '223C' // 7b11 #5800 - '2O' // 7b12 #66 - '7W' // 7b13 #204 - '95T' // 7b14 #2489 - 'a7W' // 7b15-7b16 #204 - 'A' // 7b17 - '5H' // 7b18 #137 - '122J' // 7b19 #3181 - '45I' // 7b1a #1178 - '142J' // 7b1b #3701 - 'aA' // 7b1c-7b1d - '18I' // 7b1e #476 - '5H' // 7b1f #137 - '125Q' // 7b20 #3266 - 'A' // 7b21 - '14A' // 7b22 #364 - '5H' // 7b23 #137 - '14A' // 7b24 #364 - '5H' // 7b25 #137 - '209A' // 7b26 #5434 - '18H' // 7b27 #475 - '163W' // 7b28 #4260 - 'b5H' // 7b29-7b2b #137 - '243A' // 7b2c #6318 - '18I' // 7b2d #476 - '5H' // 7b2e #137 - '2O' // 7b2f #66 - '14Q' // 7b30 #380 - '5H' // 7b31 #137 - '14A' // 7b32 #364 - 'b5H' // 7b33-7b35 #137 - '14Q' // 7b36 #380 - '7W' // 7b37 #204 - '14A' // 7b38 #364 - '96A' // 7b39 #2496 - '7W' // 7b3a #204 - '5H' // 7b3b #137 - '2R' // 7b3c #69 - '2O' // 7b3d #66 - '7W' // 7b3e #204 - '14Q' // 7b3f #380 - '2O' // 7b40 #66 - '14Q' // 7b41 #380 - '38K' // 7b42 #998 - '45G' // 7b43 #1176 - '7W' // 7b44 #204 - '5H' // 7b45 #137 - '221G' // 7b46 #5752 - '5H' // 7b47 #137 - '95X' // 7b48 #2493 - '242V' // 7b49 #6313 - '14A' // 7b4a #364 - '176Q' // 7b4b #4592 - '18I' // 7b4c #476 - '145H' // 7b4d #3777 - '5H' // 7b4e #137 - 'a18I' // 7b4f-7b50 #476 - '125P' // 7b51 #3265 - '182E' // 7b52 #4736 - '2O' // 7b53 #66 - '222E' // 7b54 #5776 - '18H' // 7b55 #475 - '68J' // 7b56 #1777 - 'A' // 7b57 - '14A' // 7b58 #364 - 'A' // 7b59 - '7W' // 7b5a #204 - '2Y' // 7b5b #76 - '7W' // 7b5c #204 - '248Z' // 7b5d #6473 - 'aA' // 7b5e-7b5f - '18I' // 7b60 #476 - 'A' // 7b61 - '45I' // 7b62 #1178 - 'A' // 7b63 - '2O' // 7b64 #66 - '45H' // 7b65 #1177 - '5H' // 7b66 #137 - '95U' // 7b67 #2490 - 'A' // 7b68 - '5H' // 7b69 #137 - '2O' // 7b6a #66 - 'A' // 7b6b - '95N' // 7b6c #2483 - '5H' // 7b6d #137 - '18I' // 7b6e #476 - '5H' // 7b6f #137 - '2O' // 7b70 #66 - '127Z' // 7b71 #3327 - 'b5H' // 7b72-7b74 #137 - '18I' // 7b75 #476 - '14A' // 7b76 #364 - '132Z' // 7b77 #3457 - 'A' // 7b78 - '249A' // 7b79 #6474 - '2O' // 7b7a #66 - '38K' // 7b7b #998 - '7W' // 7b7c #204 - '260R' // 7b7d #6777 - '1Z' // 7b7e #51 - '14Q' // 7b7f #380 - '3Q' // 7b80 #94 - 'A' // 7b81 - '14A' // 7b82 #364 - 'A' // 7b83 - '45H' // 7b84 #1177 - '14A' // 7b85 #364 - '2O' // 7b86 #66 - '96B' // 7b87 #2497 - 'A' // 7b88 - '2O' // 7b89 #66 - 'A' // 7b8a - '95W' // 7b8b #2492 - '7W' // 7b8c #204 - 'a5H' // 7b8d-7b8e #137 - '18I' // 7b8f #476 - 'b5H' // 7b90-7b92 #137 - '7W' // 7b93 #204 - '124V' // 7b94 #3245 - '95Y' // 7b95 #2494 - '5H' // 7b96 #137 - '68J' // 7b97 #1777 - '5H' // 7b98 #137 - '14Q' // 7b99 #380 - '248Y' // 7b9a #6472 - '14Q' // 7b9b #380 - '5H' // 7b9c #137 - '18I' // 7b9d #476 - 'a14Q' // 7b9e-7b9f #380 - '45H' // 7ba0 #1177 - '237E' // 7ba1 #6166 - '38K' // 7ba2 #998 - '56R' // 7ba3 #1473 - '7W' // 7ba4 #204 - '2O' // 7ba5 #66 - 'c7W' // 7ba6-7ba9 #204 - '252F' // 7baa #6557 - '7W' // 7bab #204 - '95D' // 7bac #2473 - '170X' // 7bad #4443 - '56R' // 7bae #1473 - 'a14Q' // 7baf-7bb0 #380 - '228D' // 7bb1 #5931 - '56P' // 7bb2 #1471 - 'A' // 7bb3 - '45D' // 7bb4 #1173 - 'a2O' // 7bb5-7bb6 #66 - '7W' // 7bb7 #204 - '95H' // 7bb8 #2477 - '95K' // 7bb9 #2480 - '2O' // 7bba #66 - '14Q' // 7bbb #380 - 'a2O' // 7bbc-7bbd #66 - 'aA' // 7bbe-7bbf - '235O' // 7bc0 #6124 - '45D' // 7bc1 #1173 - '2O' // 7bc2 #66 - '7W' // 7bc3 #204 - '219P' // 7bc4 #5709 - '56P' // 7bc5 #1471 - '45D' // 7bc6 #1173 - '226U' // 7bc7 #5896 - '70A' // 7bc8 #1820 - '198W' // 7bc9 #5170 - '70A' // 7bca #1820 - 'a95C' // 7bcb-7bcc #2472 - 'aA' // 7bcd-7bce - '33P' // 7bcf #873 - '29I' // 7bd0 #762 - '7W' // 7bd1 #204 - '71D' // 7bd2 #1849 - '5Y' // 7bd3 #154 - '13Z' // 7bd4 #363 - 'A' // 7bd5 - 'a3B' // 7bd6-7bd7 #79 - 'A' // 7bd8 - 'a13Z' // 7bd9-7bda #363 - '33P' // 7bdb #873 - 'A' // 7bdc - '13Z' // 7bdd #363 - 'aA' // 7bde-7bdf - '95I' // 7be0 #2478 - '45E' // 7be1 #1174 - 'aA' // 7be2-7be3 - '130N' // 7be4 #3393 - 'a13Z' // 7be5-7be6 #363 - 'A' // 7be7 - '3B' // 7be8 #79 - '66N' // 7be9 #1729 - '13Z' // 7bea #363 - 'A' // 7beb - '29I' // 7bec #762 - '251X' // 7bed #6549 - '1R' // 7bee #43 - '5Y' // 7bef #154 - '3B' // 7bf0 #79 - '95L' // 7bf1 #2481 - 'a33P' // 7bf2-7bf3 #873 - '10F' // 7bf4 #265 - '3B' // 7bf5 #79 - '10F' // 7bf6 #265 - '150O' // 7bf7 #3914 - 'a33P' // 7bf8-7bf9 #873 - '18G' // 7bfa #474 - 'A' // 7bfb - '13Z' // 7bfc #363 - '56S' // 7bfd #1474 - '13Z' // 7bfe #363 - '56S' // 7bff #1474 - '33P' // 7c00 #873 - '13Z' // 7c01 #363 - '3B' // 7c02 #79 - '13Z' // 7c03 #363 - '3B' // 7c04 #79 - 'A' // 7c05 - '3B' // 7c06 #79 - '56N' // 7c07 #1469 - '5Y' // 7c08 #154 - '3B' // 7c09 #79 - '45E' // 7c0a #1174 - 'b13Z' // 7c0b-7c0d #363 - '3B' // 7c0e #79 - '13Z' // 7c0f #363 - 'A' // 7c10 - '13Z' // 7c11 #363 - '95A' // 7c12 #2470 - 'a3B' // 7c13-7c14 #79 - '45E' // 7c15 #1174 - '5Y' // 7c16 #154 - '3B' // 7c17 #79 - 'A' // 7c18 - '10F' // 7c19 #265 - 'A' // 7c1a - '18G' // 7c1b #474 - 'aA' // 7c1c-7c1d - '56N' // 7c1e #1469 - 'a13Z' // 7c1f-7c20 #363 - '236J' // 7c21 #6145 - 'A' // 7c22 - '5G' // 7c23 #136 - '5Y' // 7c24 #154 - '13B' // 7c25 #339 - '5G' // 7c26 #136 - '132W' // 7c27 #3454 - '10F' // 7c28 #265 - '5Y' // 7c29 #154 - 'a38I' // 7c2a-7c2b #996 - '10F' // 7c2c #265 - 'a5Y' // 7c2d-7c2e #154 - '10F' // 7c2f #265 - '5Y' // 7c30 #154 - '10F' // 7c31 #265 - '5Y' // 7c32 #154 - '10F' // 7c33 #265 - '3B' // 7c34 #79 - '38J' // 7c35 #997 - '3B' // 7c36 #79 - 'b5G' // 7c37-7c39 #136 - '10F' // 7c3a #265 - '5Y' // 7c3b #154 - 'A' // 7c3c - '199X' // 7c3d #5197 - '150M' // 7c3e #3912 - '201X' // 7c3f #5249 - '5G' // 7c40 #136 - '5Y' // 7c41 #154 - '18G' // 7c42 #474 - '185M' // 7c43 #4822 - '29I' // 7c44 #762 - 'a3B' // 7c45-7c46 #79 - '5Y' // 7c47 #154 - '33Q' // 7c48 #874 - '45F' // 7c49 #1175 - '3B' // 7c4a #79 - 'A' // 7c4b - '66N' // 7c4c #1729 - '205S' // 7c4d #5348 - 'A' // 7c4e - '3B' // 7c4f #79 - '5G' // 7c50 #136 - '18G' // 7c51 #474 - '3B' // 7c52 #79 - 'a5G' // 7c53-7c54 #136 - '3B' // 7c55 #79 - 'a13B' // 7c56-7c57 #339 - '3B' // 7c58 #79 - '5G' // 7c59 #136 - 'b13B' // 7c5a-7c5c #339 - '18G' // 7c5d #474 - '3B' // 7c5e #79 - '5G' // 7c5f #136 - '167V' // 7c60 #4363 - '3B' // 7c61 #79 - 'A' // 7c62 - '5G' // 7c63 #136 - '207C' // 7c64 #5384 - '5G' // 7c65 #136 - 'A' // 7c66 - '3B' // 7c67 #79 - 'A' // 7c68 - '13B' // 7c69 #339 - 'aA' // 7c6a-7c6b - '38I' // 7c6c #996 - '13B' // 7c6d #339 - '5G' // 7c6e #136 - '3B' // 7c6f #79 - '18G' // 7c70 #474 - 'A' // 7c71 - '163V' // 7c72 #4259 - '229I' // 7c73 #5962 - '33Q' // 7c74 #874 - '13B' // 7c75 #339 - 'bA' // 7c76-7c78 - '5G' // 7c79 #136 - '5Y' // 7c7a #154 - '145G' // 7c7b #3776 - '5G' // 7c7c #136 - '145F' // 7c7d #3775 - '13B' // 7c7e #339 - 'aA' // 7c7f-7c80 - 'a10F' // 7c81-7c82 #265 - '94X' // 7c83 #2467 - '33Q' // 7c84 #874 - '5Y' // 7c85 #154 - '18G' // 7c86 #474 - '3B' // 7c87 #79 - '5Y' // 7c88 #154 - '227K' // 7c89 #5912 - '5Y' // 7c8a #154 - '254P' // 7c8b #6619 - '5Y' // 7c8c #154 - '5G' // 7c8d #136 - '38J' // 7c8e #997 - 'a10F' // 7c8f-7c90 #265 - '33Q' // 7c91 #874 - '181Z' // 7c92 #4731 - '5Y' // 7c93 #154 - '5G' // 7c94 #136 - '95E' // 7c95 #2474 - '5Y' // 7c96 #154 - '195N' // 7c97 #5083 - '154Q' // 7c98 #4020 - '5Y' // 7c99 #154 - 'A' // 7c9a - '252I' // 7c9b #6560 - '38J' // 7c9c #997 - '5Y' // 7c9d #154 - '10F' // 7c9e #265 - '95G' // 7c9f #2476 - 'a10F' // 7ca0-7ca1 #265 - '5G' // 7ca2 #136 - 'A' // 7ca3 - '248X' // 7ca4 #6471 - '150N' // 7ca5 #3913 - '5G' // 7ca6 #136 - '136Z' // 7ca7 #3561 - '5G' // 7ca8 #136 - '5Y' // 7ca9 #154 - '3G' // 7caa #84 - '3B' // 7cab #79 - '38J' // 7cac #997 - '3B' // 7cad #79 - '94Y' // 7cae #2468 - '5Y' // 7caf #154 - '10F' // 7cb0 #265 - 'b38I' // 7cb1-7cb3 #996 - 'A' // 7cb4 - '162J' // 7cb5 #4221 - 'a10F' // 7cb6-7cb7 #265 - '29I' // 7cb8 #762 - '66C' // 7cb9 #1718 - '5G' // 7cba #136 - '10F' // 7cbb #265 - '5G' // 7cbc #136 - '122H' // 7cbd #3179 - '241N' // 7cbe #6279 - '5G' // 7cbf #136 - '10F' // 7cc0 #265 - '5Y' // 7cc1 #154 - '56O' // 7cc2 #1470 - '29I' // 7cc3 #762 - '3B' // 7cc4 #79 - '5G' // 7cc5 #136 - 'A' // 7cc6 - '56O' // 7cc7 #1470 - 'a5G' // 7cc8-7cc9 #136 - '163U' // 7cca #4258 - 'A' // 7ccb - '33Q' // 7ccc #874 - '5G' // 7ccd #136 - '13B' // 7cce #339 - '3B' // 7ccf #79 - 'aA' // 7cd0-7cd1 - '3B' // 7cd2 #79 - '18G' // 7cd3 #474 - '3B' // 7cd4 #79 - '192P' // 7cd5 #5007 - '203D' // 7cd6 #5281 - '5G' // 7cd7 #136 - '3B' // 7cd8 #79 - '132X' // 7cd9 #3455 - '18G' // 7cda #474 - 'A' // 7cdb - '5G' // 7cdc #136 - '13B' // 7cdd #339 - '125O' // 7cde #3264 - '66C' // 7cdf #1718 - '38I' // 7ce0 #996 - 'A' // 7ce1 - '13B' // 7ce2 #339 - 'A' // 7ce3 - 'a5Y' // 7ce4-7ce5 #154 - '18G' // 7ce6 #474 - '159Q' // 7ce7 #4150 - '56Q' // 7ce8 #1472 - '3B' // 7ce9 #79 - '45F' // 7cea #1175 - '3B' // 7ceb #79 - '64B' // 7cec #1665 - '45F' // 7ced #1175 - 'A' // 7cee - '122I' // 7cef #3180 - '64B' // 7cf0 #1665 - 'A' // 7cf1 - '13B' // 7cf2 #339 - '29I' // 7cf3 #762 - '13B' // 7cf4 #339 - '95B' // 7cf5 #2471 - '33O' // 7cf6 #872 - '11H' // 7cf7 #293 - '95J' // 7cf8 #2479 - '33O' // 7cf9 #872 - '3B' // 7cfa #79 - '236K' // 7cfb #6146 - '56Q' // 7cfc #1472 - 'A' // 7cfd - '170W' // 7cfe #4442 - 'A' // 7cff - '227Y' // 7d00 #5926 - 'A' // 7d01 - '94Z' // 7d02 #2469 - '3B' // 7d03 #79 - '239F' // 7d04 #6219 - '233W' // 7d05 #6080 - 'b56M' // 7d06-7d08 #1468 - '33O' // 7d09 #872 - '56M' // 7d0a #1468 - '202E' // 7d0b #5256 - 'A' // 7d0c - '213K' // 7d0d #5548 - 'A' // 7d0e - '13B' // 7d0f #339 - '195R' // 7d10 #5087 - 'a33O' // 7d11-7d12 #872 - '145E' // 7d13 #3774 - '212D' // 7d14 #5515 - '13B' // 7d15 #339 - '3B' // 7d16 #79 - '189O' // 7d17 #4928 - '95F' // 7d18 #2475 - '222D' // 7d19 #5775 - '235P' // 7d1a #6125 - '196D' // 7d1b #5099 - '33O' // 7d1c #872 - 'a33M' // 7d1d-7d1e #870 - '11H' // 7d1f #293 - '223S' // 7d20 #5816 - '153E' // 7d21 #3982 - '224F' // 7d22 #5829 - '3B' // 7d23 #79 - '11H' // 7d24 #293 - '33N' // 7d25 #871 - '36A' // 7d26 #936 - '2C' // 7d27 #54 - '11H' // 7d28 #293 - '33N' // 7d29 #871 - '36A' // 7d2a #936 - '203V' // 7d2b #5299 - '94P' // 7d2c #2459 - '36A' // 7d2d #936 - '150L' // 7d2e #3911 - '208Z' // 7d2f #5433 - '68N' // 7d30 #1781 - '33M' // 7d31 #870 - '29G' // 7d32 #760 - '148T' // 7d33 #3867 - 'A' // 7d34 - '56K' // 7d35 #1466 - '11H' // 7d36 #293 - 'A' // 7d37 - '33N' // 7d38 #871 - '68N' // 7d39 #1781 - '94S' // 7d3a #2462 - 'A' // 7d3b - '29G' // 7d3c #760 - '3B' // 7d3d #79 - 'a29G' // 7d3e-7d3f #760 - '33M' // 7d40 #870 - '29G' // 7d41 #760 - '223R' // 7d42 #5815 - '56K' // 7d43 #1466 - '237D' // 7d44 #6165 - '94O' // 7d45 #2458 - '125N' // 7d46 #3263 - 'a3B' // 7d47-7d48 #79 - 'aA' // 7d49-7d4a - '36A' // 7d4b #936 - '259P' // 7d4c #6749 - '94N' // 7d4d #2457 - '29G' // 7d4e #760 - '33M' // 7d4f #870 - '243T' // 7d50 #6337 - '36A' // 7d51 #936 - 'A' // 7d52 - '29G' // 7d53 #760 - '33N' // 7d54 #871 - '217F' // 7d55 #5647 - '33M' // 7d56 #870 - '4H' // 7d57 #111 - '11H' // 7d58 #293 - '4H' // 7d59 #111 - '38H' // 7d5a #995 - 'a29E' // 7d5b-7d5c #758 - '29F' // 7d5d #759 - '131D' // 7d5e #3409 - '45C' // 7d5f #1172 - 'A' // 7d60 - '241U' // 7d61 #6286 - '130M' // 7d62 #3392 - '29E' // 7d63 #758 - 'A' // 7d64 - '4H' // 7d65 #111 - '68P' // 7d66 #1783 - '29F' // 7d67 #759 - '179X' // 7d68 #4677 - 'A' // 7d69 - '56J' // 7d6a #1465 - 'a11H' // 7d6b-7d6c #293 - '45C' // 7d6d #1172 - '66B' // 7d6e #1717 - '11H' // 7d6f #293 - '29F' // 7d70 #759 - '68P' // 7d71 #1783 - '224Z' // 7d72 #5849 - '56J' // 7d73 #1465 - 'A' // 7d74 - '258G' // 7d75 #6714 - '258W' // 7d76 #6730 - '11H' // 7d77 #293 - '4H' // 7d78 #111 - '94R' // 7d79 #2461 - '29E' // 7d7a #758 - '29F' // 7d7b #759 - 'A' // 7d7c - '29F' // 7d7d #759 - '11H' // 7d7e #293 - '69Z' // 7d7f #1819 - '45C' // 7d80 #1172 - '178E' // 7d81 #4632 - '4H' // 7d82 #111 - '29F' // 7d83 #759 - '33N' // 7d84 #871 - '49S' // 7d85 #1292 - '29E' // 7d86 #758 - '11H' // 7d87 #293 - 'a29E' // 7d88-7d89 #758 - '11H' // 7d8a #293 - '29E' // 7d8b #758 - '9D' // 7d8c #237 - '18F' // 7d8d #473 - '94V' // 7d8e #2465 - '29D' // 7d8f #757 - 'A' // 7d90 - '18F' // 7d91 #473 - 'A' // 7d92 - '239R' // 7d93 #6231 - '11H' // 7d94 #293 - '22Q' // 7d95 #588 - '18F' // 7d96 #473 - '38H' // 7d97 #995 - '11H' // 7d98 #293 - '257M' // 7d99 #6694 - '259X' // 7d9a #6757 - '4H' // 7d9b #111 - '67Y' // 7d9c #1766 - 'a18F' // 7d9d-7d9e #473 - '49S' // 7d9f #1292 - '67Y' // 7da0 #1766 - 'A' // 7da1 - '29D' // 7da2 #757 - '9D' // 7da3 #237 - '29B' // 7da4 #755 - 'A' // 7da5 - '18F' // 7da6 #473 - '4H' // 7da7 #111 - '29B' // 7da8 #755 - 'A' // 7da9 - '18F' // 7daa #473 - '138Y' // 7dab #3612 - '29D' // 7dac #757 - '227G' // 7dad #5908 - 'b18F' // 7dae-7db0 #473 - '160W' // 7db1 #4182 - '246F' // 7db2 #6401 - '9D' // 7db3 #237 - '142U' // 7db4 #3712 - '29D' // 7db5 #757 - '49S' // 7db6 #1292 - '18F' // 7db7 #473 - '138X' // 7db8 #3611 - '9D' // 7db9 #237 - '155H' // 7dba #4037 - '148A' // 7dbb #3848 - '11H' // 7dbc #293 - '29D' // 7dbd #757 - '130Y' // 7dbe #3404 - '175M' // 7dbf #4562 - '4H' // 7dc0 #111 - '94W' // 7dc1 #2466 - 'a4H' // 7dc2-7dc3 #111 - '18F' // 7dc4 #473 - 'a9D' // 7dc5-7dc6 #237 - '29D' // 7dc7 #757 - '11H' // 7dc8 #293 - 'A' // 7dc9 - '210U' // 7dca #5480 - '141Z' // 7dcb #3691 - 'a18F' // 7dcc-7dcd #473 - '9D' // 7dce #237 - '94U' // 7dcf #2464 - '38H' // 7dd0 #995 - '256X' // 7dd1 #6679 - '191R' // 7dd2 #4983 - '29B' // 7dd3 #755 - '56L' // 7dd4 #1467 - '4H' // 7dd5 #111 - '248W' // 7dd6 #6470 - '13Y' // 7dd7 #362 - '29C' // 7dd8 #756 - '9D' // 7dd9 #237 - '242L' // 7dda #6303 - '29H' // 7ddb #761 - '13Y' // 7ddc #362 - '64Q' // 7ddd #1680 - '122G' // 7dde #3178 - '5C' // 7ddf #132 - '143L' // 7de0 #3729 - '33L' // 7de1 #869 - '19G' // 7de2 #500 - '199W' // 7de3 #5196 - 'a45B' // 7de4-7de5 #1171 - '13Y' // 7de6 #362 - 'A' // 7de7 - '237C' // 7de8 #6164 - '196N' // 7de9 #5109 - 'a19G' // 7dea-7deb #500 - '65R' // 7dec #1707 - '19G' // 7ded #500 - 'A' // 7dee - '160R' // 7def #4177 - '22Q' // 7df0 #588 - 'a13Y' // 7df1-7df2 #362 - '29H' // 7df3 #761 - '212C' // 7df4 #5514 - '45B' // 7df5 #1171 - '9D' // 7df6 #237 - '5C' // 7df7 #132 - 'A' // 7df8 - '145D' // 7df9 #3773 - '4H' // 7dfa #111 - '194N' // 7dfb #5057 - '56L' // 7dfc #1467 - '29B' // 7dfd #755 - '29H' // 7dfe #761 - '5C' // 7dff #132 - '19G' // 7e00 #500 - '256N' // 7e01 #6669 - '5C' // 7e02 #132 - 'A' // 7e03 - '257X' // 7e04 #6705 - '4H' // 7e05 #111 - 'A' // 7e06 - '29B' // 7e07 #755 - '13Y' // 7e08 #362 - 'a29C' // 7e09-7e0a #756 - '13Y' // 7e0b #362 - 'cA' // 7e0c-7e0f - 'a9D' // 7e10-7e11 #237 - '19G' // 7e12 #500 - '22Q' // 7e13 #588 - 'A' // 7e14 - '33L' // 7e15 #869 - 'A' // 7e16 - '4H' // 7e17 #111 - 'bA' // 7e18-7e1a - '130T' // 7e1b #3399 - '4H' // 7e1c #111 - '33L' // 7e1d #869 - '94Q' // 7e1e #2460 - '33L' // 7e1f #869 - '13Y' // 7e20 #362 - '69Z' // 7e21 #1819 - '13Y' // 7e22 #362 - '214K' // 7e23 #5574 - 'A' // 7e24 - '22Q' // 7e25 #588 - '255V' // 7e26 #6651 - '9D' // 7e27 #237 - '4H' // 7e28 #111 - '29H' // 7e29 #761 - 'A' // 7e2a - '175B' // 7e2b #4551 - '4H' // 7e2c #111 - '9D' // 7e2d #237 - '204H' // 7e2e #5311 - '29C' // 7e2f #756 - '22Q' // 7e30 #588 - '170V' // 7e31 #4441 - 'a9D' // 7e32-7e33 #237 - '22Q' // 7e34 #588 - 'a9D' // 7e35-7e36 #237 - '29C' // 7e37 #756 - 'A' // 7e38 - '13Y' // 7e39 #362 - '19G' // 7e3a #500 - '13Y' // 7e3b #362 - '5C' // 7e3c #132 - '232I' // 7e3d #6040 - '198K' // 7e3e #5158 - '4H' // 7e3f #111 - '5C' // 7e40 #132 - '215W' // 7e41 #5612 - 'A' // 7e42 - '132V' // 7e43 #3453 - '13Y' // 7e44 #362 - '9D' // 7e45 #237 - '145C' // 7e46 #3772 - '29C' // 7e47 #756 - '9D' // 7e48 #237 - 'A' // 7e49 - '254W' // 7e4a #6626 - '256E' // 7e4b #6660 - 'A' // 7e4c - '254C' // 7e4d #6606 - '19G' // 7e4e #500 - 'A' // 7e4f - '9D' // 7e50 #237 - '29H' // 7e51 #761 - '33L' // 7e52 #869 - 'A' // 7e53 - '211P' // 7e54 #5501 - '142B' // 7e55 #3693 - '13Y' // 7e56 #362 - 'A' // 7e57 - 'b13Y' // 7e58-7e5a #362 - '29H' // 7e5b #761 - 'A' // 7e5c - '19G' // 7e5d #500 - '66R' // 7e5e #1733 - '4H' // 7e5f #111 - 'A' // 7e60 - '157C' // 7e61 #4084 - '9D' // 7e62 #237 - 'aA' // 7e63-7e64 - '38H' // 7e65 #995 - '19G' // 7e66 #500 - '45B' // 7e67 #1171 - '22Q' // 7e68 #588 - '66R' // 7e69 #1733 - '192O' // 7e6a #5006 - '207A' // 7e6b #5382 - '29B' // 7e6c #755 - '29C' // 7e6d #756 - 'a9D' // 7e6e-7e6f #237 - '94T' // 7e70 #2463 - 'aA' // 7e71-7e72 - '178D' // 7e73 #4631 - 'A' // 7e74 - '4H' // 7e75 #111 - '22Q' // 7e76 #588 - '5C' // 7e77 #132 - '33K' // 7e78 #868 - '65R' // 7e79 #1707 - 'A' // 7e7a - '29A' // 7e7b #754 - '217E' // 7e7c #5646 - '157D' // 7e7d #4085 - '33K' // 7e7e #868 - '56H' // 7e7f #1463 - 'A' // 7e80 - '29A' // 7e81 #754 - '38G' // 7e82 #994 - '4H' // 7e83 #111 - 'aA' // 7e84-7e85 - 'b33K' // 7e86-7e88 #868 - '4H' // 7e89 #111 - '33K' // 7e8a #868 - 'A' // 7e8b - '224Y' // 7e8c #5848 - '29A' // 7e8d #754 - '33K' // 7e8e #868 - '159Y' // 7e8f #4158 - 'a4H' // 7e90-7e91 #111 - '29A' // 7e92 #754 - '38G' // 7e93 #994 - '29A' // 7e94 #754 - '4H' // 7e95 #111 - '185K' // 7e96 #4820 - 'A' // 7e97 - '38G' // 7e98 #994 - '94M' // 7e99 #2456 - '29A' // 7e9a #754 - '38G' // 7e9b #994 - '64Q' // 7e9c #1680 - '4H' // 7e9d #111 - '19G' // 7e9e #500 - '94E' // 7e9f #2448 - '3Y' // 7ea0 #102 - '5C' // 7ea1 #132 - '1Z' // 7ea2 #51 - '5C' // 7ea3 #132 - '94K' // 7ea4 #2454 - '5C' // 7ea5 #132 - '1Z' // 7ea6 #51 - '3Q' // 7ea7 #94 - 'a5C' // 7ea8-7ea9 #132 - '2D' // 7eaa #55 - '5C' // 7eab #132 - '94H' // 7eac #2451 - 'a5C' // 7ead-7eae #132 - '2C' // 7eaf #54 - '5C' // 7eb0 #132 - '1R' // 7eb1 #43 - '2R' // 7eb2 #69 - '2C' // 7eb3 #54 - 'A' // 7eb4 - '4C' // 7eb5 #106 - '2W' // 7eb6 #74 - '1R' // 7eb7 #43 - '2C' // 7eb8 #54 - '3Y' // 7eb9 #102 - '94J' // 7eba #2453 - '5C' // 7ebb #132 - 'A' // 7ebc - '4C' // 7ebd #106 - '5C' // 7ebe #132 - '7K' // 7ebf #192 - 'b5C' // 7ec0-7ec2 #132 - '3I' // 7ec3 #86 - '1Z' // 7ec4 #51 - '3X' // 7ec5 #101 - '1Z' // 7ec6 #51 - '94L' // 7ec7 #2455 - '2D' // 7ec8 #55 - '5C' // 7ec9 #132 - '2W' // 7eca #74 - 'a5C' // 7ecb-7ecc #132 - '1Z' // 7ecd #51 - '2Y' // 7ece #76 - '35T' // 7ecf #929 - '5C' // 7ed0 #132 - '1R' // 7ed1 #43 - '2Y' // 7ed2 #76 - '3Q' // 7ed3 #94 - '5C' // 7ed4 #132 - '1R' // 7ed5 #43 - 'A' // 7ed6 - '5C' // 7ed7 #132 - '3Y' // 7ed8 #102 - '3Q' // 7ed9 #94 - '3G' // 7eda #84 - '5C' // 7edb #132 - '1Z' // 7edc #51 - '2D' // 7edd #55 - '3G' // 7ede #84 - '155T' // 7edf #4049 - 'b5C' // 7ee0-7ee2 #132 - '2Y' // 7ee3 #76 - 'A' // 7ee4 - 'a5C' // 7ee5-7ee6 #132 - '2C' // 7ee7 #54 - '5C' // 7ee8 #132 - '3N' // 7ee9 #91 - '2L' // 7eea #63 - '5C' // 7eeb #132 - 'A' // 7eec - '2D' // 7eed #55 - '3X' // 7eee #101 - '2K' // 7eef #62 - 'b5X' // 7ef0-7ef2 #153 - '2Y' // 7ef3 #76 - '1Z' // 7ef4 #51 - '2L' // 7ef5 #63 - '5X' // 7ef6 #153 - '2W' // 7ef7 #74 - '2K' // 7ef8 #62 - 'A' // 7ef9 - 'a5X' // 7efa-7efb #153 - '2D' // 7efc #55 - '2K' // 7efd #62 - '5X' // 7efe #153 - '2C' // 7eff #54 - '2K' // 7f00 #62 - 'c5X' // 7f01-7f04 #153 - '2R' // 7f05 #69 - '94I' // 7f06 #2452 - '2W' // 7f07 #74 - '5X' // 7f08 #153 - '2W' // 7f09 #74 - 'h5X' // 7f0a-7f12 #153 - '3N' // 7f13 #91 - '3X' // 7f14 #101 - '2W' // 7f15 #74 - '3Q' // 7f16 #94 - '5X' // 7f17 #153 - '3Y' // 7f18 #102 - '5X' // 7f19 #153 - '3X' // 7f1a #101 - 'a5X' // 7f1b-7f1c #153 - '2L' // 7f1d #63 - 'A' // 7f1e - '5X' // 7f1f #153 - '2R' // 7f20 #69 - 'b5X' // 7f21-7f23 #153 - '3G' // 7f24 #84 - 'c5X' // 7f25-7f28 #153 - '3Y' // 7f29 #102 - 'i5X' // 7f2a-7f33 #153 - '2R' // 7f34 #69 - '5X' // 7f35 #153 - '94G' // 7f36 #2450 - '44Z' // 7f37 #1169 - '66B' // 7f38 #1717 - 'A' // 7f39 - '207B' // 7f3a #5383 - 'a4H' // 7f3b-7f3c #111 - '16P' // 7f3d #431 - 'a4H' // 7f3e-7f3f #111 - 'a93Z' // 7f40-7f41 #2443 - '5X' // 7f42 #153 - '44Y' // 7f43 #1168 - 'a16P' // 7f44-7f45 #431 - 'A' // 7f46 - '44Y' // 7f47 #1168 - 'c45A' // 7f48-7f4b #1170 - '56I' // 7f4c #1464 - '16P' // 7f4d #431 - '44Y' // 7f4e #1168 - '4H' // 7f4f #111 - '178C' // 7f50 #4630 - '187Z' // 7f51 #4887 - '16P' // 7f52 #431 - '56H' // 7f53 #1463 - '56I' // 7f54 #1464 - '163T' // 7f55 #4257 - 'A' // 7f56 - '2C' // 7f57 #54 - '16P' // 7f58 #431 - '5X' // 7f59 #153 - '4C' // 7f5a #106 - 'a4H' // 7f5b-7f5c #111 - '16P' // 7f5d #431 - 'A' // 7f5e - '16P' // 7f5f #431 - '94F' // 7f60 #2449 - '16P' // 7f61 #431 - '2R' // 7f62 #69 - '16P' // 7f63 #431 - '4H' // 7f64 #111 - '16P' // 7f65 #431 - 'a4H' // 7f66-7f67 #111 - '16P' // 7f68 #431 - '185L' // 7f69 #4821 - '197R' // 7f6a #5139 - '94C' // 7f6b #2446 - 'A' // 7f6c - '4H' // 7f6d #111 - '230V' // 7f6e #6001 - 'A' // 7f6f - '181N' // 7f70 #4719 - '94D' // 7f71 #2447 - '188Z' // 7f72 #4913 - 'a5X' // 7f73-7f74 #153 - '180N' // 7f75 #4693 - 'A' // 7f76 - '66A' // 7f77 #1716 - '28Z' // 7f78 #753 - '150K' // 7f79 #3910 - 'bA' // 7f7a-7f7c - 'a25J' // 7f7d-7f7e #659 - 'a1V' // 7f7f-7f80 #47 - '2W' // 7f81 #74 - '1V' // 7f82 #47 - '28Z' // 7f83 #753 - 'A' // 7f84 - '219C' // 7f85 #5696 - '25J' // 7f86 #659 - '28Z' // 7f87 #753 - '122F' // 7f88 #3177 - '5X' // 7f89 #153 - '196S' // 7f8a #5114 - '25J' // 7f8b #659 - '44X' // 7f8c #1167 - '28Z' // 7f8d #753 - '244R' // 7f8e #6361 - '94A' // 7f8f #2444 - '41N' // 7f90 #1079 - '25J' // 7f91 #659 - 'A' // 7f92 - '45A' // 7f93 #1170 - '44X' // 7f94 #1167 - '44Z' // 7f95 #1169 - '41N' // 7f96 #1079 - '28Z' // 7f97 #753 - 'a5X' // 7f98-7f99 #153 - '44X' // 7f9a #1167 - '5X' // 7f9b #153 - '1V' // 7f9c #47 - '25J' // 7f9d #659 - '173M' // 7f9e #4510 - '5X' // 7f9f #153 - 'A' // 7fa0 - '94B' // 7fa1 #2445 - '28Z' // 7fa2 #753 - '25J' // 7fa3 #659 - '229E' // 7fa4 #5958 - '45A' // 7fa5 #1170 - '1V' // 7fa6 #47 - '44Z' // 7fa7 #1169 - '160Q' // 7fa8 #4176 - '228W' // 7fa9 #5950 - '1V' // 7faa #47 - 'A' // 7fab - '5X' // 7fac #153 - 'b25J' // 7fad-7faf #659 - 'a93X' // 7fb0-7fb1 #2441 - '93J' // 7fb2 #2427 - 'A' // 7fb3 - '56F' // 7fb4 #1461 - 'A' // 7fb5 - '56F' // 7fb6 #1461 - 'A' // 7fb7 - '25H' // 7fb8 #657 - '127W' // 7fb9 #3324 - 'aA' // 7fba-7fbb - '11Y' // 7fbc #310 - '198T' // 7fbd #5167 - 'A' // 7fbe - 'a11Y' // 7fbf-7fc0 #310 - '172U' // 7fc1 #4492 - 'A' // 7fc2 - '11Y' // 7fc3 #310 - 'A' // 7fc4 - '157A' // 7fc5 #4082 - '1V' // 7fc6 #47 - 'A' // 7fc7 - '1V' // 7fc8 #47 - 'A' // 7fc9 - '127V' // 7fca #3323 - '25I' // 7fcb #658 - '93U' // 7fcc #2438 - 'A' // 7fcd - '25H' // 7fce #657 - '13X' // 7fcf #361 - 'A' // 7fd0 - '25I' // 7fd1 #658 - '229R' // 7fd2 #5971 - 'A' // 7fd3 - '190B' // 7fd4 #4941 - '25H' // 7fd5 #657 - 'aA' // 7fd6-7fd7 - '2R' // 7fd8 #69 - 'a5X' // 7fd9-7fda #153 - '11Y' // 7fdb #310 - 'A' // 7fdc - '33J' // 7fdd #867 - '25I' // 7fde #658 - '25H' // 7fdf #657 - '187X' // 7fe0 #4885 - '156Z' // 7fe1 #4081 - 'A' // 7fe2 - '41N' // 7fe3 #1079 - 'A' // 7fe4 - 'a11Y' // 7fe5-7fe6 #310 - '93W' // 7fe7 #2440 - '1V' // 7fe8 #47 - '25H' // 7fe9 #657 - 'A' // 7fea - '25H' // 7feb #657 - '11Y' // 7fec #310 - 'A' // 7fed - '11Y' // 7fee #310 - '41N' // 7fef #1079 - '163S' // 7ff0 #4256 - '137M' // 7ff1 #3574 - '13X' // 7ff2 #361 - '11Y' // 7ff3 #310 - '5X' // 7ff4 #153 - 'cA' // 7ff5-7ff8 - '66A' // 7ff9 #1716 - '11Y' // 7ffa #310 - '218Z' // 7ffb #5693 - '175P' // 7ffc #4565 - '13X' // 7ffd #361 - '11Y' // 7ffe #310 - '1V' // 7fff #47 - '194K' // 8000 #5054 - '234T' // 8001 #6103 - '13X' // 8002 #361 - '231F' // 8003 #6011 - '11Y' // 8004 #310 - '35V' // 8005 #931 - '25H' // 8006 #657 - '1V' // 8007 #47 - '22P' // 8008 #587 - '71C' // 8009 #1848 - '1V' // 800a #47 - '11Y' // 800b #310 - '232H' // 800c #6039 - '157B' // 800d #4083 - '11Y' // 800e #310 - '1V' // 800f #47 - '204I' // 8010 #5312 - 'a11Y' // 8011-8012 #310 - '1V' // 8013 #47 - '11Y' // 8014 #310 - '160I' // 8015 #4168 - '4Z' // 8016 #129 - '188I' // 8017 #4896 - '93I' // 8018 #2426 - '4Z' // 8019 #129 - 'aA' // 801a-801b - '4Z' // 801c #129 - '22P' // 801d #587 - '26Z' // 801e #701 - '1V' // 801f #47 - '22P' // 8020 #587 - '1V' // 8021 #47 - 'aA' // 8022-8023 - '13X' // 8024 #361 - '33J' // 8025 #867 - '4Z' // 8026 #129 - '6G' // 8027 #162 - '4Z' // 8028 #129 - 'a6G' // 8029-802a #162 - 'A' // 802b - '13X' // 802c #361 - '71C' // 802d #1848 - '22P' // 802e #587 - '33J' // 802f #867 - '13X' // 8030 #361 - '28Y' // 8031 #752 - 'A' // 8032 - '210R' // 8033 #5477 - '26Z' // 8034 #701 - '4Z' // 8035 #129 - '195G' // 8036 #5076 - '4Z' // 8037 #129 - '2W' // 8038 #74 - '13X' // 8039 #361 - '1V' // 803a #47 - '93L' // 803b #2429 - '22P' // 803c #587 - '127U' // 803d #3322 - '1V' // 803e #47 - '64P' // 803f #1679 - '1V' // 8040 #47 - 'A' // 8041 - '3W' // 8042 #100 - '18E' // 8043 #472 - '1V' // 8044 #47 - 'A' // 8045 - '64P' // 8046 #1679 - 'bA' // 8047-8049 - '206Y' // 804a #5380 - '6G' // 804b #162 - '2D' // 804c #55 - '6G' // 804d #162 - 'cA' // 804e-8051 - '4Z' // 8052 #129 - 'A' // 8053 - '49C' // 8054 #1276 - 'A' // 8055 - '215J' // 8056 #5599 - 'A' // 8057 - '178B' // 8058 #4629 - '6G' // 8059 #162 - '206Z' // 805a #5381 - '33J' // 805b #867 - 'aA' // 805c-805d - '236I' // 805e #6144 - 'a1V' // 805f-8060 #47 - '93R' // 8061 #2435 - '22P' // 8062 #587 - '33J' // 8063 #867 - '1V' // 8064 #47 - 'A' // 8065 - '22P' // 8066 #587 - 'A' // 8067 - '26Z' // 8068 #701 - '6G' // 8069 #162 - '2L' // 806a #63 - 'aA' // 806b-806c - '1V' // 806d #47 - '6G' // 806e #162 - '239Q' // 806f #6230 - '178A' // 8070 #4628 - '4Z' // 8071 #129 - '232G' // 8072 #6038 - '18E' // 8073 #472 - '257Q' // 8074 #6698 - '13X' // 8075 #361 - '4Z' // 8076 #129 - '223B' // 8077 #5799 - '6G' // 8078 #162 - '13X' // 8079 #361 - 'A' // 807a - '1V' // 807b #47 - '6G' // 807c #162 - '224X' // 807d #5847 - 'a18E' // 807e-807f #472 - '93K' // 8080 #2428 - '1V' // 8081 #47 - '6G' // 8082 #162 - '4C' // 8083 #106 - '18E' // 8084 #472 - '145B' // 8085 #3771 - '150I' // 8086 #3908 - '138V' // 8087 #3609 - '26Z' // 8088 #701 - '222B' // 8089 #5773 - 'A' // 808a - '132T' // 808b #3451 - '204Q' // 808c #5320 - 'A' // 808d - '1V' // 808e #47 - '20G' // 808f #526 - 'bA' // 8090-8092 - '4Z' // 8093 #129 - 'A' // 8094 - '25I' // 8095 #658 - '173R' // 8096 #4515 - 'A' // 8097 - '93O' // 8098 #2432 - '1V' // 8099 #47 - '192N' // 809a #5005 - '136M' // 809b #3548 - '4Z' // 809c #129 - '175G' // 809d #4556 - '1V' // 809e #47 - '20G' // 809f #526 - '4C' // 80a0 #106 - '227D' // 80a1 #5905 - '161P' // 80a2 #4201 - 'A' // 80a3 - '248V' // 80a4 #6469 - '202G' // 80a5 #5258 - '1V' // 80a6 #47 - '13X' // 80a7 #361 - 'A' // 80a8 - '190L' // 80a9 #4951 - '161C' // 80aa #4188 - '4Z' // 80ab #129 - '1V' // 80ac #47 - '4Z' // 80ad #129 - '20G' // 80ae #526 - '201H' // 80af #5233 - 'A' // 80b0 - '18E' // 80b1 #472 - '230O' // 80b2 #5994 - 'A' // 80b3 - '93Q' // 80b4 #2434 - '25I' // 80b5 #658 - '20G' // 80b6 #526 - '28Y' // 80b7 #752 - '4Z' // 80b8 #129 - '1V' // 80b9 #47 - '160A' // 80ba #4160 - 'A' // 80bb - 'a20G' // 80bc-80bd #526 - '2R' // 80be #69 - '2L' // 80bf #63 - 'a2R' // 80c0-80c1 #69 - '20G' // 80c2 #526 - '182G' // 80c3 #4738 - '18E' // 80c4 #472 - '1V' // 80c5 #47 - '93S' // 80c6 #2436 - '25I' // 80c7 #658 - '1V' // 80c8 #47 - 'A' // 80c9 - '26Z' // 80ca #701 - 'A' // 80cb - '220Z' // 80cc #5745 - '4Z' // 80cd #129 - '194X' // 80ce #5067 - '13X' // 80cf #361 - 'A' // 80d0 - '6G' // 80d1 #162 - '1V' // 80d2 #47 - 'A' // 80d3 - '4Z' // 80d4 #129 - '1V' // 80d5 #47 - '185J' // 80d6 #4819 - '4Z' // 80d7 #129 - '1V' // 80d8 #47 - '4Z' // 80d9 #129 - '122E' // 80da #3176 - '18E' // 80db #472 - '93Y' // 80dc #2442 - '4Z' // 80dd #129 - '174S' // 80de #4542 - 'A' // 80df - '4Z' // 80e0 #129 - '195J' // 80e1 #5079 - 'A' // 80e2 - '25I' // 80e3 #658 - 'a18E' // 80e4-80e5 #472 - '1V' // 80e6 #47 - 'a6G' // 80e7-80e8 #162 - '28Y' // 80e9 #752 - 'a6G' // 80ea-80eb #162 - '20G' // 80ec #526 - '4Z' // 80ed #129 - '1V' // 80ee #47 - 'a4Z' // 80ef-80f0 #129 - '18E' // 80f1 #472 - '1V' // 80f2 #47 - '4Z' // 80f3 #129 - '93P' // 80f4 #2433 - '1V' // 80f5 #47 - '93N' // 80f6 #2431 - '1V' // 80f7 #47 - '198D' // 80f8 #5151 - '1V' // 80f9 #47 - '132U' // 80fa #3452 - '1V' // 80fb #47 - '4Z' // 80fc #129 - '244W' // 80fd #6366 - '13X' // 80fe #361 - 'aA' // 80ff-8100 - '4Z' // 8101 #129 - '190F' // 8102 #4945 - '22P' // 8103 #587 - '6G' // 8104 #162 - '181M' // 8105 #4718 - '180S' // 8106 #4698 - '93T' // 8107 #2437 - '175F' // 8108 #4555 - '93M' // 8109 #2430 - '152Y' // 810a #3976 - '1V' // 810b #47 - '28Y' // 810c #752 - '26Z' // 810d #701 - '28Y' // 810e #752 - '4C' // 810f #106 - '2W' // 8110 #74 - '2D' // 8111 #55 - '20G' // 8112 #526 - '6G' // 8113 #162 - '28Y' // 8114 #752 - '20G' // 8115 #526 - '138W' // 8116 #3610 - '4Z' // 8117 #129 - '18E' // 8118 #472 - '6G' // 8119 #162 - '93V' // 811a #2439 - '56D' // 811b #1459 - '26Z' // 811c #701 - '6G' // 811d #162 - '56E' // 811e #1460 - '6G' // 811f #162 - '1V' // 8120 #47 - 'a56G' // 8121-8122 #1462 - '56D' // 8123 #1459 - '56E' // 8124 #1460 - '56G' // 8125 #1462 - '6G' // 8126 #162 - '38E' // 8127 #992 - '6G' // 8128 #162 - '56A' // 8129 #1456 - '44W' // 812a #1166 - '199V' // 812b #5195 - '38E' // 812c #992 - 'a6G' // 812d-812e #162 - '56A' // 812f #1456 - '38E' // 8130 #992 - '93F' // 8131 #2423 - '28X' // 8132 #751 - '256H' // 8133 #6663 - '28X' // 8134 #751 - '1V' // 8135 #47 - 'A' // 8136 - '28X' // 8137 #751 - '3I' // 8138 #86 - '150H' // 8139 #3907 - '56B' // 813a #1457 - 'A' // 813b - '1V' // 813c #47 - '56B' // 813d #1457 - '150J' // 813e #3909 - 'a6G' // 813f-8140 #162 - '1V' // 8141 #47 - '44W' // 8142 #1166 - 'A' // 8143 - '25G' // 8144 #656 - '1V' // 8145 #47 - '38E' // 8146 #992 - '1V' // 8147 #47 - '28X' // 8148 #751 - 'A' // 8149 - '92X' // 814a #2415 - '124O' // 814b #3238 - '92W' // 814c #2414 - '28X' // 814d #751 - '159X' // 814e #4157 - 'A' // 814f - '190A' // 8150 #4940 - '92T' // 8151 #2411 - '2J' // 8152 #61 - '55Z' // 8153 #1455 - '166S' // 8154 #4334 - '176S' // 8155 #4594 - '25G' // 8156 #656 - '2J' // 8157 #61 - '6G' // 8158 #162 - '44W' // 8159 #1166 - '28X' // 815a #751 - 'bA' // 815b-815d - '6G' // 815e #162 - '2J' // 815f #61 - '55Z' // 8160 #1455 - '2J' // 8161 #61 - 'bA' // 8162-8164 - '144Z' // 8165 #3769 - '224V' // 8166 #5845 - '4O' // 8167 #118 - '21H' // 8168 #553 - '4O' // 8169 #118 - 'A' // 816a - '174O' // 816b #4538 - '38D' // 816c #991 - '4O' // 816d #118 - '127T' // 816e #3321 - '11X' // 816f #309 - '197N' // 8170 #5135 - '16O' // 8171 #430 - 'A' // 8172 - '214J' // 8173 #5573 - '4O' // 8174 #118 - 'aA' // 8175-8176 - '2J' // 8177 #61 - '189W' // 8178 #4936 - '183O' // 8179 #4772 - '153W' // 817a #4000 - '2Y' // 817b #76 - '28W' // 817c #750 - '38F' // 817d #993 - '2D' // 817e #55 - '201D' // 817f #5229 - '156X' // 8180 #4079 - '2J' // 8181 #61 - '4O' // 8182 #118 - '2J' // 8183 #61 - '20F' // 8184 #525 - 'a2J' // 8185-8186 #61 - 'A' // 8187 - '16O' // 8188 #430 - 'A' // 8189 - '55Y' // 818a #1454 - '2J' // 818b #61 - 'aA' // 818c-818d - '2J' // 818e #61 - '180K' // 818f #4690 - '2J' // 8190 #61 - '16G' // 8191 #422 - 'A' // 8192 - '20F' // 8193 #525 - 'A' // 8194 - '11X' // 8195 #309 - '2J' // 8196 #61 - 'A' // 8197 - '4O' // 8198 #118 - '16G' // 8199 #422 - '202L' // 819a #5263 - '4O' // 819b #118 - '195Q' // 819c #5086 - '161B' // 819d #4187 - '11X' // 819e #309 - 'A' // 819f - '206W' // 81a0 #5378 - 'A' // 81a1 - '2J' // 81a2 #61 - '93C' // 81a3 #2420 - '2J' // 81a4 #61 - '38F' // 81a5 #993 - '28W' // 81a6 #750 - '25G' // 81a7 #656 - '142Z' // 81a8 #3717 - '177Z' // 81a9 #4627 - '38D' // 81aa #991 - '25G' // 81ab #656 - 'aA' // 81ac-81ad - '2J' // 81ae #61 - 'A' // 81af - '11X' // 81b0 #309 - 'A' // 81b1 - '2J' // 81b2 #61 - '154A' // 81b3 #4004 - '2J' // 81b4 #61 - '33I' // 81b5 #866 - '28W' // 81b6 #750 - 'A' // 81b7 - '2J' // 81b8 #61 - 'A' // 81b9 - '16O' // 81ba #430 - '4O' // 81bb #118 - 'A' // 81bc - '185I' // 81bd #4818 - '33I' // 81be #866 - '92Z' // 81bf #2417 - '156Y' // 81c0 #4080 - '92V' // 81c1 #2413 - '163Q' // 81c2 #4254 - '4O' // 81c3 #118 - 'A' // 81c4 - '2J' // 81c5 #61 - '16O' // 81c6 #430 - 'A' // 81c7 - '20F' // 81c8 #525 - '217D' // 81c9 #5645 - '4O' // 81ca #118 - '2J' // 81cb #61 - '28W' // 81cc #750 - '122B' // 81cd #3173 - '2J' // 81ce #61 - '11X' // 81cf #309 - 'A' // 81d0 - '4O' // 81d1 #118 - '25G' // 81d2 #656 - '254Z' // 81d3 #6629 - '16G' // 81d4 #422 - 'a2J' // 81d5-81d6 #61 - '11X' // 81d7 #309 - '66G' // 81d8 #1722 - 'a11X' // 81d9-81da #309 - '21H' // 81db #553 - '16G' // 81dc #422 - '11X' // 81dd #309 - '4O' // 81de #118 - '170T' // 81df #4439 - 'a11X' // 81e0-81e1 #309 - '25G' // 81e2 #656 - '167U' // 81e3 #4362 - '20F' // 81e4 #525 - '66G' // 81e5 #1722 - 'A' // 81e6 - '16O' // 81e7 #430 - '210I' // 81e8 #5468 - '16G' // 81e9 #422 - '68Z' // 81ea #1793 - '21H' // 81eb #553 - '4O' // 81ec #118 - '182Y' // 81ed #4756 - '16G' // 81ee #422 - '4O' // 81ef #118 - 'b2J' // 81f0-81f2 #61 - '233V' // 81f3 #6079 - '222C' // 81f4 #5774 - '21H' // 81f5 #553 - '20F' // 81f6 #525 - 'A' // 81f7 - '21H' // 81f8 #553 - '2J' // 81f9 #61 - '68B' // 81fa #1769 - '144Y' // 81fb #3768 - '56C' // 81fc #1458 - '2J' // 81fd #61 - '16O' // 81fe #430 - '2J' // 81ff #61 - 'b4O' // 8200-8202 #118 - '2J' // 8203 #61 - '4O' // 8204 #118 - '55Y' // 8205 #1454 - '2R' // 8206 #69 - '240I' // 8207 #6248 - '229D' // 8208 #5957 - '224W' // 8209 #5846 - '68B' // 820a #1769 - '11X' // 820b #309 - '181T' // 820c #4725 - '177Y' // 820d #4626 - '256D' // 820e #6659 - '21H' // 820f #553 - '93E' // 8210 #2422 - 'A' // 8211 - '206X' // 8212 #5379 - '2J' // 8213 #61 - '138U' // 8214 #3608 - '25G' // 8215 #656 - '192M' // 8216 #5004 - '258Y' // 8217 #6732 - '93A' // 8218 #2418 - '2J' // 8219 #61 - '20F' // 821a #525 - '16O' // 821b #430 - '138T' // 821c #3607 - '11X' // 821d #309 - '212L' // 821e #5523 - '154D' // 821f #4007 - 'A' // 8220 - '16O' // 8221 #430 - '4O' // 8222 #118 - 'a16G' // 8223-8224 #422 - 'A' // 8225 - '38F' // 8226 #993 - '16G' // 8227 #422 - '4O' // 8228 #118 - '11X' // 8229 #309 - '215P' // 822a #5605 - '16O' // 822b #430 - '222R' // 822c #5789 - '38F' // 822d #993 - '2J' // 822e #61 - '28W' // 822f #750 - '1R' // 8230 #43 - '3X' // 8231 #101 - 'b4O' // 8232-8234 #118 - '122C' // 8235 #3174 - '93B' // 8236 #2419 - '16O' // 8237 #430 - '4O' // 8238 #118 - '198J' // 8239 #5157 - '4O' // 823a #118 - '16G' // 823b #422 - '2J' // 823c #61 - 'A' // 823d - '28W' // 823e #750 - 'A' // 823f - '33I' // 8240 #866 - 'aA' // 8241-8242 - '2J' // 8243 #61 - '4O' // 8244 #118 - '33I' // 8245 #866 - '2J' // 8246 #61 - '153G' // 8247 #3984 - 'A' // 8248 - '4O' // 8249 #118 - 'A' // 824a - '4O' // 824b #118 - 'aA' // 824c-824d - '11X' // 824e #309 - '4O' // 824f #118 - 'A' // 8250 - '2J' // 8251 #61 - 'aA' // 8252-8253 - '38D' // 8254 #991 - 'A' // 8255 - '2J' // 8256 #61 - '11X' // 8257 #309 - '122D' // 8258 #3175 - '65Q' // 8259 #1706 - '4O' // 825a #118 - 'A' // 825b - 'a2J' // 825c-825d #61 - 'A' // 825e - '4O' // 825f #118 - '2J' // 8260 #61 - 'A' // 8261 - '20F' // 8262 #525 - '2J' // 8263 #61 - '33I' // 8264 #866 - '38D' // 8265 #991 - '196W' // 8266 #5118 - '2J' // 8267 #61 - '4O' // 8268 #118 - 'A' // 8269 - '2J' // 826a #61 - '11X' // 826b #309 - 'A' // 826c - '2J' // 826d #61 - '16O' // 826e #430 - '223Z' // 826f #5823 - '2R' // 8270 #69 - '65Q' // 8271 #1706 - '243F' // 8272 #6323 - '3N' // 8273 #91 - '2J' // 8274 #61 - 'A' // 8275 - '93D' // 8276 #2421 - '170U' // 8277 #4440 - '56C' // 8278 #1458 - '4O' // 8279 #118 - '64L' // 827a #1675 - '20F' // 827b #525 - 'A' // 827c - '25F' // 827d #655 - '192L' // 827e #5003 - '25F' // 827f #655 - 'a2J' // 8280-8281 #61 - '3Q' // 8282 #94 - 'a25F' // 8283-8284 #655 - 'aA' // 8285-8286 - '20F' // 8287 #525 - '16G' // 8288 #422 - '21H' // 8289 #553 - '25F' // 828a #655 - '160U' // 828b #4180 - 'A' // 828c - 'a92S' // 828d-828e #2410 - 'a93G' // 828f-8290 #2424 - '25F' // 8291 #655 - '163R' // 8292 #4255 - 'a25F' // 8293-8294 #655 - 'A' // 8295 - '21H' // 8296 #553 - '16G' // 8297 #422 - '25F' // 8298 #655 - '163P' // 8299 #4253 - '92U' // 829a #2412 - '92Y' // 829b #2416 - '2K' // 829c #62 - '189X' // 829d #4937 - '93H' // 829e #2425 - '92H' // 829f #2399 - 'a28U' // 82a0-82a1 #748 - '28V' // 82a2 #749 - 'a28U' // 82a3-82a4 #748 - '129W' // 82a5 #3376 - '92O' // 82a6 #2406 - 'a28U' // 82a7-82a8 #748 - '55U' // 82a9 #1450 - 'a28U' // 82aa-82ab #748 - '185H' // 82ac #4817 - '159L' // 82ad #4145 - '55U' // 82ae #1450 - '160N' // 82af #4173 - '28U' // 82b0 #748 - '68S' // 82b1 #1786 - '21H' // 82b2 #553 - '196G' // 82b3 #5102 - '28U' // 82b4 #748 - '16G' // 82b5 #422 - '92Q' // 82b6 #2408 - '127S' // 82b7 #3320 - '149N' // 82b8 #3887 - '125F' // 82b9 #3255 - '92J' // 82ba #2401 - '92G' // 82bb #2398 - '28S' // 82bc #746 - '175E' // 82bd #4554 - '28T' // 82be #747 - '92E' // 82bf #2396 - 'b5B' // 82c0-82c2 #131 - 'A' // 82c3 - '18D' // 82c4 #471 - 'a248U' // 82c5-82c6 #6468 - 'a5B' // 82c7-82c8 #131 - 'A' // 82c9 - '33H' // 82ca #865 - 'a5B' // 82cb-82cc #131 - '2L' // 82cd #63 - '5B' // 82ce #131 - '55X' // 82cf #1453 - '28T' // 82d0 #747 - '181E' // 82d1 #4710 - '28S' // 82d2 #746 - '145A' // 82d3 #3770 - '142G' // 82d4 #3698 - '28S' // 82d5 #746 - '18D' // 82d6 #471 - '202D' // 82d7 #5255 - '33H' // 82d8 #865 - '28T' // 82d9 #747 - '2J' // 82da #61 - '92K' // 82db #2402 - '28T' // 82dc #747 - '5B' // 82dd #131 - '28S' // 82de #746 - '92F' // 82df #2397 - '28T' // 82e0 #747 - '28S' // 82e1 #746 - '92I' // 82e2 #2400 - 'a28T' // 82e3-82e4 #747 - '229H' // 82e5 #5961 - '212H' // 82e6 #5519 - '28S' // 82e7 #746 - '3A' // 82e8 #78 - '5B' // 82e9 #131 - '8W' // 82ea #230 - '92L' // 82eb #2403 - 'A' // 82ec - '41M' // 82ed #1078 - '33H' // 82ee #865 - '92B' // 82ef #2393 - '28V' // 82f0 #749 - '235K' // 82f1 #6120 - 'A' // 82f2 - 'a8W' // 82f3-82f4 #230 - 'A' // 82f5 - '22O' // 82f6 #586 - '8W' // 82f7 #230 - '33H' // 82f8 #865 - '92C' // 82f9 #2394 - '92M' // 82fa #2404 - '8W' // 82fb #230 - '18D' // 82fc #471 - '91Z' // 82fd #2391 - '18C' // 82fe #470 - '18D' // 82ff #471 - '8W' // 8300 #230 - '18C' // 8301 #470 - '168G' // 8302 #4374 - '185F' // 8303 #4815 - '166F' // 8304 #4321 - '142P' // 8305 #3707 - 'b8W' // 8306-8308 #230 - '136O' // 8309 #3550 - '3A' // 830a #78 - '22O' // 830b #586 - '8W' // 830c #230 - '18D' // 830d #471 - '253Q' // 830e #6594 - '5B' // 830f #131 - 'A' // 8310 - '5B' // 8311 #131 - 'A' // 8312 - 'b5B' // 8313-8315 #131 - '22O' // 8316 #586 - '127Q' // 8317 #3318 - '8W' // 8318 #230 - 'A' // 8319 - '18D' // 831a #471 - '8W' // 831b #230 - '142H' // 831c #3699 - '8W' // 831d #230 - '22O' // 831e #586 - '3A' // 831f #78 - 'A' // 8320 - 'b3A' // 8321-8323 #78 - '5B' // 8324 #131 - 'aA' // 8325-8326 - '18D' // 8327 #471 - '137E' // 8328 #3566 - 'A' // 8329 - '18D' // 832a #471 - '132R' // 832b #3449 - 'a8W' // 832c-832d #230 - '3A' // 832e #78 - '18C' // 832f #470 - '3A' // 8330 #78 - '18C' // 8331 #470 - '163N' // 8332 #4251 - '8W' // 8333 #230 - '18C' // 8334 #470 - '138Q' // 8335 #3604 - '212X' // 8336 #5535 - '22O' // 8337 #586 - '130K' // 8338 #3390 - '141Y' // 8339 #3690 - '8W' // 833a #230 - '18D' // 833b #471 - '8W' // 833c #230 - '44T' // 833d #1163 - 'A' // 833e - '28V' // 833f #749 - '18C' // 8340 #470 - 'A' // 8341 - '22O' // 8342 #586 - '144X' // 8343 #3767 - 'a8W' // 8344-8345 #230 - '248R' // 8346 #6465 - '18C' // 8347 #470 - '5B' // 8348 #131 - '215X' // 8349 #5613 - '127P' // 834a #3317 - '28V' // 834b #749 - '18D' // 834c #471 - 'a3A' // 834d-834e #78 - '18C' // 834f #470 - '138S' // 8350 #3606 - '18C' // 8351 #470 - '176J' // 8352 #4585 - '3A' // 8353 #78 - '138R' // 8354 #3605 - '3A' // 8355 #78 - '8W' // 8356 #230 - '22O' // 8357 #586 - '254V' // 8358 #6625 - '5B' // 8359 #131 - '41M' // 835a #1078 - 'a5B' // 835b-835c #131 - 'A' // 835d - '5B' // 835e #131 - '3X' // 835f #101 - '5B' // 8360 #131 - '4C' // 8361 #106 - '44T' // 8362 #1163 - '92D' // 8363 #2395 - 'a5B' // 8364-8365 #131 - '33H' // 8366 #865 - '2K' // 8367 #62 - 'b5B' // 8368-836a #131 - '3W' // 836b #100 - 'b5B' // 836c-836e #131 - '55X' // 836f #1453 - '3A' // 8370 #78 - 'aA' // 8371-8372 - '18C' // 8373 #470 - 'A' // 8374 - '22O' // 8375 #586 - 'A' // 8376 - '199A' // 8377 #5174 - '8W' // 8378 #230 - 'A' // 8379 - '28V' // 837a #749 - '92N' // 837b #2405 - 'a8W' // 837c-837d #230 - '28V' // 837e #749 - '8W' // 837f #230 - '41M' // 8380 #1078 - 'A' // 8381 - '41M' // 8382 #1078 - '44V' // 8383 #1165 - '3A' // 8384 #78 - 'a92A' // 8385-8386 #2392 - '3A' // 8387 #78 - '5B' // 8388 #131 - '202Q' // 8389 #5268 - '199T' // 838a #5193 - '5B' // 838b #131 - 'A' // 838c - '3A' // 838d #78 - '177W' // 838e #4624 - 'aA' // 838f-8390 - '55T' // 8391 #1449 - '25E' // 8392 #654 - '170S' // 8393 #4438 - '25E' // 8394 #654 - '44U' // 8395 #1164 - '132Q' // 8396 #3448 - 'A' // 8397 - '38C' // 8398 #990 - '25E' // 8399 #654 - '3A' // 839a #78 - 'a25E' // 839b-839c #654 - '3A' // 839d #78 - '144W' // 839e #3766 - '3A' // 839f #78 - '25E' // 83a0 #654 - 'A' // 83a1 - '38C' // 83a2 #990 - '5B' // 83a3 #131 - '44V' // 83a4 #1165 - 'A' // 83a5 - '3A' // 83a6 #78 - '44U' // 83a7 #1164 - '25E' // 83a8 #654 - 'a38C' // 83a9-83aa #990 - '192K' // 83ab #5002 - '44T' // 83ac #1163 - '3A' // 83ad #78 - '5B' // 83ae #131 - 'a55W' // 83af-83b0 #1452 - '248T' // 83b1 #6467 - '1R' // 83b2 #43 - 'a5B' // 83b3-83b4 #131 - '3A' // 83b5 #78 - '5B' // 83b6 #131 - '1Z' // 83b7 #51 - '5B' // 83b8 #131 - '92R' // 83b9 #2409 - '5B' // 83ba #131 - 'A' // 83bb - '5B' // 83bc #131 - '38C' // 83bd #990 - '44U' // 83be #1164 - 'a25E' // 83bf-83c0 #654 - '163M' // 83c1 #4250 - '55W' // 83c2 #1452 - 'A' // 83c3 - '44V' // 83c4 #1165 - '92P' // 83c5 #2407 - 'A' // 83c6 - '163O' // 83c7 #4252 - '55V' // 83c8 #1451 - '91Y' // 83c9 #2390 - '168J' // 83ca #4377 - '55V' // 83cb #1451 - '182J' // 83cc #4741 - '55T' // 83cd #1449 - '18B' // 83ce #469 - '91S' // 83cf #2384 - '3A' // 83d0 #78 - '3Z' // 83d1 #103 - 'A' // 83d2 - '131F' // 83d3 #3411 - '3Z' // 83d4 #103 - '25D' // 83d5 #653 - '16N' // 83d6 #429 - 'A' // 83d7 - '3Z' // 83d8 #103 - 'aA' // 83d9-83da - '11G' // 83db #292 - '216D' // 83dc #5619 - '3Z' // 83dd #103 - 'A' // 83de - '3Z' // 83df #103 - '132S' // 83e0 #3450 - '3Z' // 83e1 #103 - '15K' // 83e2 #400 - 'aA' // 83e3-83e4 - '3Z' // 83e5 #103 - 'aA' // 83e6-83e7 - '3A' // 83e8 #78 - '147R' // 83e9 #3839 - '3Z' // 83ea #103 - '16N' // 83eb #429 - 'A' // 83ec - '10T' // 83ed #279 - 'A' // 83ee - '234Y' // 83ef #6108 - '16N' // 83f0 #429 - '161U' // 83f1 #4206 - '199U' // 83f2 #5194 - '15K' // 83f3 #400 - '16N' // 83f4 #429 - 'A' // 83f5 - 'a3A' // 83f6-83f7 #78 - '156W' // 83f8 #4078 - '16N' // 83f9 #429 - 'A' // 83fa - '3Z' // 83fb #103 - '18B' // 83fc #469 - '16N' // 83fd #429 - '15K' // 83fe #400 - '11G' // 83ff #292 - 'A' // 8400 - '63S' // 8401 #1656 - 'A' // 8402 - '156V' // 8403 #4077 - '170R' // 8404 #4437 - '10T' // 8405 #279 - '3Z' // 8406 #103 - '18B' // 8407 #469 - 'aA' // 8408-8409 - '199S' // 840a #5192 - '3Z' // 840b #103 - '189Q' // 840c #4930 - '144V' // 840d #3765 - '135T' // 840e #3529 - '3Z' // 840f #103 - 'A' // 8410 - '3Z' // 8411 #103 - 'A' // 8412 - '18B' // 8413 #469 - '10T' // 8414 #279 - '3A' // 8415 #78 - '10T' // 8416 #279 - '3A' // 8417 #78 - '25D' // 8418 #653 - '3A' // 8419 #78 - 'A' // 841a - '15K' // 841b #400 - '25D' // 841c #653 - '4C' // 841d #106 - 'aA' // 841e-841f - '3Z' // 8420 #103 - '15K' // 8421 #400 - '49R' // 8422 #1291 - 'a25D' // 8423-8424 #653 - '1Z' // 8425 #51 - '25D' // 8426 #653 - '2R' // 8427 #69 - '1R' // 8428 #43 - '91W' // 8429 #2388 - '3A' // 842a #78 - '15K' // 842b #400 - '233E' // 842c #6062 - 'a15K' // 842d-842e #400 - '3A' // 842f #78 - 'A' // 8430 - '150F' // 8431 #3905 - 'a15K' // 8432-8433 #400 - 'A' // 8434 - '18B' // 8435 #469 - 'A' // 8436 - '15K' // 8437 #400 - '16N' // 8438 #429 - '3Z' // 8439 #103 - 'A' // 843a - '25D' // 843b #653 - '3Z' // 843c #103 - '230N' // 843d #5993 - '15K' // 843e #400 - '11G' // 843f #292 - 'dA' // 8440-8444 - '18B' // 8445 #469 - 'a3Z' // 8446-8447 #103 - '28R' // 8448 #745 - '213X' // 8449 #5561 - '28R' // 844a #745 - 'aA' // 844b-844c - '3A' // 844d #78 - '3Z' // 844e #103 - '3A' // 844f #78 - 'A' // 8450 - 'a3Z' // 8451-8452 #103 - '10T' // 8453 #279 - 'A' // 8454 - '10T' // 8455 #279 - '3Z' // 8456 #103 - '235A' // 8457 #6110 - '28R' // 8458 #745 - 'a3Z' // 8459-845a #103 - '168S' // 845b #4386 - '3Z' // 845c #103 - 'aA' // 845d-845e - '18B' // 845f #469 - '3A' // 8460 #78 - '177X' // 8461 #4625 - '3Z' // 8462 #103 - '201G' // 8463 #5232 - '28R' // 8464 #745 - '3A' // 8465 #78 - '16N' // 8466 #429 - '18B' // 8467 #469 - 'A' // 8468 - '127R' // 8469 #3319 - '3A' // 846a #78 - '122A' // 846b #3172 - '154V' // 846c #4025 - '3Z' // 846d #103 - '3A' // 846e #78 - '16N' // 846f #429 - '3Z' // 8470 #103 - '91T' // 8471 #2385 - '10T' // 8472 #279 - '3Z' // 8473 #103 - '18B' // 8474 #469 - '161E' // 8475 #4190 - 'b3Z' // 8476-8478 #103 - '3A' // 8479 #78 - '16N' // 847a #429 - 'A' // 847b - '3A' // 847c #78 - '49R' // 847d #1291 - 'A' // 847e - 'a10T' // 847f-8480 #279 - '3A' // 8481 #78 - '185G' // 8482 #4816 - 'A' // 8483 - '3Z' // 8484 #103 - '49R' // 8485 #1291 - 'A' // 8486 - '11G' // 8487 #292 - '10T' // 8488 #279 - '11G' // 8489 #292 - 'A' // 848a - '248S' // 848b #6466 - '11G' // 848c #292 - '15K' // 848d #400 - '25D' // 848e #653 - 'A' // 848f - '163L' // 8490 #4249 - 'A' // 8491 - '28R' // 8492 #745 - '3Z' // 8493 #103 - '91U' // 8494 #2386 - '3A' // 8495 #78 - '10T' // 8496 #279 - '3Z' // 8497 #103 - 'A' // 8498 - '187J' // 8499 #4871 - 'A' // 849a - '11G' // 849b #292 - '150G' // 849c #3906 - '15K' // 849d #400 - '63S' // 849e #1656 - '3Z' // 849f #103 - 'A' // 84a0 - '16N' // 84a1 #429 - 'A' // 84a2 - '10T' // 84a3 #279 - 'A' // 84a4 - '11G' // 84a5 #292 - '3A' // 84a6 #78 - 'A' // 84a7 - '3Z' // 84a8 #103 - 'a3A' // 84a9-84aa #78 - 'aA' // 84ab-84ac - '28R' // 84ad #745 - 'A' // 84ae - '3Z' // 84af #103 - 'A' // 84b0 - '18B' // 84b1 #469 - '154K' // 84b2 #4014 - 'A' // 84b3 - '44R' // 84b4 #1161 - 'bA' // 84b5-84b7 - '181Y' // 84b8 #4730 - 'a44R' // 84b9-84ba #1161 - '55R' // 84bb #1447 - '161L' // 84bc #4197 - 'a44R' // 84bd-84be #1161 - '55R' // 84bf #1447 - '38B' // 84c0 #989 - '63C' // 84c1 #1640 - '28Q' // 84c2 #744 - 'A' // 84c3 - '154T' // 84c4 #4023 - '11G' // 84c5 #292 - '28Q' // 84c6 #744 - '15J' // 84c7 #399 - '2H' // 84c8 #59 - '156U' // 84c9 #4076 - '15J' // 84ca #399 - '208X' // 84cb #5431 - '2H' // 84cc #59 - '28Q' // 84cd #744 - 'a25C' // 84ce-84cf #652 - '15J' // 84d0 #399 - '28Q' // 84d1 #744 - '44S' // 84d2 #1162 - '63C' // 84d3 #1640 - 'aA' // 84d4-84d5 - '15J' // 84d6 #399 - 'aA' // 84d7-84d8 - '2H' // 84d9 #59 - '91R' // 84da #2383 - 'A' // 84db - '2H' // 84dc #59 - '2C' // 84dd #54 - '10T' // 84de #279 - 'a11G' // 84df-84e0 #292 - '10T' // 84e1 #279 - '55S' // 84e2 #1448 - '11G' // 84e3 #292 - '10T' // 84e4 #279 - '91X' // 84e5 #2389 - '11G' // 84e6 #292 - '15J' // 84e7 #399 - '44S' // 84e8 #1162 - 'A' // 84e9 - '25C' // 84ea #652 - 'A' // 84eb - '156T' // 84ec #4075 - 'A' // 84ed - '209M' // 84ee #5446 - 'a25C' // 84ef-84f0 #652 - 'a2H' // 84f1-84f2 #59 - '55S' // 84f3 #1448 - '38B' // 84f4 #989 - 'A' // 84f5 - '11G' // 84f6 #292 - '25C' // 84f7 #652 - '10T' // 84f8 #279 - 'A' // 84f9 - '15J' // 84fa #399 - '2H' // 84fb #59 - '28Q' // 84fc #744 - '25C' // 84fd #652 - 'A' // 84fe - 'a15J' // 84ff-8500 #399 - 'A' // 8501 - '2H' // 8502 #59 - '44Q' // 8503 #1160 - 'a10T' // 8504-8505 #279 - '25C' // 8506 #652 - '2H' // 8507 #59 - 'cA' // 8508-850b - '15J' // 850c #399 - 'A' // 850d - '2H' // 850e #59 - 'A' // 850f - '44Q' // 8510 #1160 - '91V' // 8511 #2387 - 'A' // 8512 - '152N' // 8513 #3965 - '150E' // 8514 #3904 - '15J' // 8515 #399 - 'A' // 8516 - '28Q' // 8517 #744 - '121Y' // 8518 #3170 - 'A' // 8519 - '144U' // 851a #3764 - 'a2H' // 851b-851c #59 - '44S' // 851d #1162 - '38B' // 851e #989 - '15J' // 851f #399 - 'A' // 8520 - '199Q' // 8521 #5190 - '2H' // 8522 #59 - '156S' // 8523 #4074 - '25C' // 8524 #652 - '170Q' // 8525 #4436 - '141R' // 8526 #3683 - '2H' // 8527 #59 - 'A' // 8528 - '11G' // 8529 #292 - '2H' // 852a #59 - '15J' // 852b #399 - '177U' // 852c #4622 - '135S' // 852d #3528 - 'A' // 852e - '38B' // 852f #989 - 'aA' // 8530-8531 - '248Q' // 8532 #6464 - '44Q' // 8533 #1160 - '15J' // 8534 #399 - '257W' // 8535 #6704 - '2H' // 8536 #59 - '2W' // 8537 #74 - '55Q' // 8538 #1446 - 'a11G' // 8539-853a #292 - '137Q' // 853b #3578 - '11G' // 853c #292 - '147T' // 853d #3841 - '6M' // 853e #168 - '248P' // 853f #6463 - '2H' // 8540 #59 - '55P' // 8541 #1445 - '28P' // 8542 #743 - '144T' // 8543 #3763 - 'A' // 8544 - '55Q' // 8545 #1446 - '2H' // 8546 #59 - 'A' // 8547 - '6B' // 8548 #157 - '152S' // 8549 #3970 - '127O' // 854a #3316 - '6M' // 854b #168 - '16M' // 854c #428 - '28P' // 854d #743 - '91M' // 854e #2378 - 'b2H' // 854f-8551 #59 - '6M' // 8552 #168 - '55O' // 8553 #1444 - 'A' // 8554 - '6M' // 8555 #168 - 'a6B' // 8556-8557 #157 - '6M' // 8558 #168 - '127N' // 8559 #3315 - '6M' // 855a #168 - 'A' // 855b - 'a2H' // 855c-855d #59 - '6B' // 855e #157 - '28L' // 855f #739 - '2H' // 8560 #59 - '6B' // 8561 #157 - '6M' // 8562 #168 - '55P' // 8563 #1445 - '6B' // 8564 #157 - '91P' // 8565 #2381 - 'aA' // 8566-8567 - '91J' // 8568 #2375 - '156R' // 8569 #4073 - '55O' // 856a #1444 - '6M' // 856b #168 - '28P' // 856c #743 - '170P' // 856d #4435 - 'A' // 856e - '6B' // 856f #157 - '91O' // 8570 #2380 - 'A' // 8571 - '16F' // 8572 #421 - '16M' // 8573 #428 - '2K' // 8574 #62 - 'A' // 8575 - '16F' // 8576 #421 - '6M' // 8577 #168 - '28P' // 8578 #743 - 'b6B' // 8579-857b #157 - 'A' // 857c - '2H' // 857d #59 - '177V' // 857e #4623 - '2H' // 857f #59 - '6B' // 8580 #157 - '6M' // 8581 #168 - 'aA' // 8582-8583 - '204P' // 8584 #5319 - 'a6B' // 8585-8586 #157 - '173Q' // 8587 #4514 - '132P' // 8588 #3447 - '2H' // 8589 #59 - '6B' // 858a #157 - '2H' // 858b #59 - '6M' // 858c #168 - 'aA' // 858d-858e - '20E' // 858f #524 - '6M' // 8590 #168 - '156P' // 8591 #4071 - 'A' // 8592 - '28L' // 8593 #739 - '91L' // 8594 #2377 - 'aA' // 8595-8596 - 'a6M' // 8597-8598 #168 - '91H' // 8599 #2373 - 'A' // 859a - '144S' // 859b #3762 - '6B' // 859c #157 - '2H' // 859d #59 - 'A' // 859e - '6M' // 859f #168 - '2H' // 85a0 #59 - '28P' // 85a1 #743 - '6B' // 85a2 #157 - 'A' // 85a3 - '6B' // 85a4 #157 - '2H' // 85a5 #59 - '233N' // 85a6 #6071 - '2H' // 85a7 #59 - '20E' // 85a8 #524 - '187S' // 85a9 #4880 - '187N' // 85aa #4875 - '253M' // 85ab #6590 - '258F' // 85ac #6713 - '2H' // 85ad #59 - '35Z' // 85ae #935 - '163K' // 85af #4248 - '156Q' // 85b0 #4072 - 'aA' // 85b1-85b2 - '28P' // 85b3 #743 - '6M' // 85b4 #168 - 'A' // 85b5 - '2H' // 85b6 #59 - '6B' // 85b7 #157 - '35Z' // 85b8 #935 - '6B' // 85b9 #157 - '33G' // 85ba #864 - 'A' // 85bb - '2H' // 85bc #59 - '6M' // 85bd #168 - '6B' // 85be #157 - '2H' // 85bf #59 - 'A' // 85c0 - '20E' // 85c1 #524 - '6M' // 85c2 #168 - 'cA' // 85c3-85c6 - '35Z' // 85c7 #935 - 'A' // 85c8 - '192J' // 85c9 #5001 - '2H' // 85ca #59 - '6M' // 85cb #168 - 'A' // 85cc - '218O' // 85cd #5682 - '33G' // 85ce #864 - '224U' // 85cf #5844 - '6B' // 85d0 #157 - 'aA' // 85d1-85d2 - '16F' // 85d3 #421 - 'A' // 85d4 - '20E' // 85d5 #524 - '16M' // 85d6 #428 - 'A' // 85d7 - 'b2H' // 85d8-85da #59 - 'A' // 85db - '20E' // 85dc #524 - '226M' // 85dd #5888 - 'A' // 85de - '35Z' // 85df #935 - '6B' // 85e0 #157 - '2H' // 85e1 #59 - 'aA' // 85e2-85e3 - '184C' // 85e4 #4786 - '206V' // 85e5 #5377 - '6B' // 85e6 #157 - 'A' // 85e7 - '6B' // 85e8 #157 - '135P' // 85e9 #3525 - '91I' // 85ea #2374 - 'aA' // 85eb-85ec - '6M' // 85ed #168 - '16M' // 85ee #428 - 'cA' // 85ef-85f2 - '2H' // 85f3 #59 - '6B' // 85f4 #157 - 'A' // 85f5 - '6M' // 85f6 #168 - '33G' // 85f7 #864 - 'A' // 85f8 - '6B' // 85f9 #157 - '33G' // 85fa #864 - '148G' // 85fb #3854 - '28L' // 85fc #739 - 'A' // 85fd - '35Z' // 85fe #935 - '20E' // 85ff #524 - '2H' // 8600 #59 - 'A' // 8601 - '20E' // 8602 #524 - 'A' // 8603 - '6M' // 8604 #168 - '6B' // 8605 #157 - '170O' // 8606 #4434 - '208W' // 8607 #5430 - 'aA' // 8608-8609 - '138P' // 860a #3603 - '199R' // 860b #5191 - 'A' // 860c - '28L' // 860d #739 - '2H' // 860e #59 - '16M' // 860f #428 - '6M' // 8610 #168 - '121Z' // 8611 #3171 - '2H' // 8612 #59 - '28L' // 8613 #739 - '16M' // 8614 #428 - 'A' // 8615 - '20E' // 8616 #524 - '33G' // 8617 #864 - '6B' // 8618 #157 - '2H' // 8619 #59 - '20E' // 861a #524 - '2H' // 861b #59 - 'aA' // 861c-861d - '6M' // 861e #168 - 'aA' // 861f-8620 - 'a28N' // 8621-8622 #741 - 'A' // 8623 - '2H' // 8624 #59 - 'aA' // 8625-8626 - '28M' // 8627 #740 - '16M' // 8628 #428 - '28M' // 8629 #740 - '28O' // 862a #742 - 'aA' // 862b-862c - '226W' // 862d #5898 - 'A' // 862e - '28L' // 862f #739 - '2H' // 8630 #59 - 'bA' // 8631-8633 - 'a28O' // 8634-8635 #742 - '28N' // 8636 #741 - 'A' // 8637 - '28M' // 8638 #740 - '2H' // 8639 #59 - '28N' // 863a #741 - 'A' // 863b - '28M' // 863c #740 - '2H' // 863d #59 - 'A' // 863e - '177T' // 863f #4621 - '28N' // 8640 #741 - '2H' // 8641 #59 - '28N' // 8642 #741 - 'aA' // 8643-8644 - '16M' // 8645 #428 - '28N' // 8646 #741 - 'dA' // 8647-864b - '28O' // 864c #742 - '28M' // 864d #740 - '202K' // 864e #5262 - '16F' // 864f #421 - '167T' // 8650 #4361 - '3Y' // 8651 #102 - 'a28M' // 8652-8653 #740 - '91G' // 8654 #2372 - '232F' // 8655 #6037 - 'b1O' // 8656-8658 #40 - '33F' // 8659 #863 - '254H' // 865a #6611 - '192I' // 865b #5000 - '91K' // 865c #2376 - '1O' // 865d #40 - '127K' // 865e #3312 - '239P' // 865f #6229 - 'a1O' // 8660-8661 #40 - '20D' // 8662 #523 - 'a1O' // 8663-8664 #40 - 'aA' // 8665-8666 - '163J' // 8667 #4247 - 'A' // 8668 - '1O' // 8669 #40 - 'A' // 866a - '91N' // 866b #2379 - '20D' // 866c #523 - 'A' // 866d - '16F' // 866e #421 - '33F' // 866f #863 - '28O' // 8670 #742 - '20D' // 8671 #523 - '16M' // 8672 #428 - '28O' // 8673 #742 - 'A' // 8674 - '41L' // 8675 #1077 - '1O' // 8676 #40 - '33F' // 8677 #863 - 'A' // 8678 - '181L' // 8679 #4717 - 'a20D' // 867a-867b #523 - '16F' // 867c #421 - '248O' // 867d #6462 - '91Q' // 867e #2382 - '16F' // 867f #421 - '3H' // 8680 #85 - 'a2R' // 8681-8682 #69 - 'cA' // 8683-8686 - 'b1O' // 8687-8689 #40 - '172Y' // 868a #4496 - '20D' // 868b #523 - '91F' // 868c #2371 - '20D' // 868d #523 - 'bA' // 868e-8690 - '1O' // 8691 #40 - '16M' // 8692 #428 - '44P' // 8693 #1159 - '28O' // 8694 #742 - '55N' // 8695 #1443 - '33F' // 8696 #863 - 'A' // 8697 - '1O' // 8698 #40 - 'A' // 8699 - '33F' // 869a #863 - 'A' // 869b - 'a20D' // 869c-869d #523 - 'aA' // 869e-869f - '16M' // 86a0 #428 - '20D' // 86a1 #523 - 'A' // 86a2 - 'a44P' // 86a3-86a4 #1159 - 'A' // 86a5 - '1O' // 86a6 #40 - 'a20D' // 86a7-86a8 #523 - '44P' // 86a9 #1159 - '55N' // 86aa #1443 - '1O' // 86ab #40 - '16F' // 86ac #421 - '91E' // 86ad #2370 - 'A' // 86ae - 'b18A' // 86af-86b1 #468 - '44M' // 86b2 #1156 - '22N' // 86b3 #585 - '18A' // 86b4 #468 - '132O' // 86b5 #3446 - '18A' // 86b6 #468 - 'a1O' // 86b7-86b8 #40 - '22N' // 86b9 #585 - '55M' // 86ba #1442 - 'cA' // 86bb-86be - '1O' // 86bf #40 - '18A' // 86c0 #468 - '22N' // 86c1 #585 - '38A' // 86c2 #988 - '1O' // 86c3 #40 - '18A' // 86c4 #468 - '1O' // 86c5 #40 - '18A' // 86c6 #468 - '181D' // 86c7 #4709 - 'A' // 86c8 - '18A' // 86c9 #468 - '16F' // 86ca #421 - '217C' // 86cb #5644 - '38A' // 86cc #988 - '253X' // 86cd #6601 - '41L' // 86ce #1077 - '16F' // 86cf #421 - '55M' // 86d0 #1442 - '41L' // 86d1 #1077 - '1O' // 86d2 #40 - '38A' // 86d3 #988 - '44O' // 86d4 #1158 - '1O' // 86d5 #40 - 'A' // 86d6 - '1O' // 86d7 #40 - '16F' // 86d8 #421 - '159F' // 86d9 #4139 - '1O' // 86da #40 - '141N' // 86db #3679 - '1O' // 86dc #40 - 'A' // 86dd - '18A' // 86de #468 - '44O' // 86df #1158 - '1O' // 86e0 #40 - 'aA' // 86e1-86e2 - '1O' // 86e3 #40 - '127J' // 86e4 #3311 - '1O' // 86e5 #40 - '41L' // 86e6 #1077 - '1O' // 86e7 #40 - 'A' // 86e8 - '18A' // 86e9 #468 - 'aA' // 86ea-86eb - '1O' // 86ec #40 - '44O' // 86ed #1158 - '252L' // 86ee #6563 - '18A' // 86ef #468 - 'c16F' // 86f0-86f3 #421 - '24G' // 86f4 #630 - 'bA' // 86f5-86f7 - 'a8V' // 86f8-86f9 #229 - '22N' // 86fa #585 - '8V' // 86fb #229 - 'a1O' // 86fc-86fd #40 - '28K' // 86fe #738 - 'A' // 86ff - '127L' // 8700 #3313 - 'A' // 8701 - '180U' // 8702 #4700 - '28K' // 8703 #738 - 'a1O' // 8704-8705 #40 - 'a8V' // 8706-8707 #229 - '28K' // 8708 #738 - 'a8V' // 8709-870a #229 - '1O' // 870b #40 - 'A' // 870c - '8V' // 870d #229 - '22N' // 870e #585 - 'a1O' // 870f-8710 #40 - 'a8V' // 8711-8712 #229 - '64O' // 8713 #1678 - '1O' // 8714 #40 - '91D' // 8715 #2369 - 'A' // 8716 - '3G' // 8717 #84 - '135I' // 8718 #3518 - '22N' // 8719 #585 - '28K' // 871a #738 - 'A' // 871b - '201W' // 871c #5248 - 'A' // 871d - '8V' // 871e #229 - '1O' // 871f #40 - 'A' // 8720 - '91C' // 8721 #2368 - 'a8V' // 8722-8723 #229 - 'A' // 8724 - '8V' // 8725 #229 - 'aA' // 8726-8727 - '22N' // 8728 #585 - '8V' // 8729 #229 - 'cA' // 872a-872d - '8V' // 872e #229 - '1O' // 872f #40 - 'A' // 8730 - '8V' // 8731 #229 - '1O' // 8732 #40 - 'A' // 8733 - '8V' // 8734 #229 - 'aA' // 8735-8736 - '8V' // 8737 #229 - 'A' // 8738 - '1O' // 8739 #40 - '8V' // 873a #229 - '64O' // 873b #1678 - 'a1O' // 873c-873d #40 - 'b8V' // 873e-8740 #229 - 'A' // 8741 - '55L' // 8742 #1441 - '1O' // 8743 #40 - 'A' // 8744 - '1O' // 8745 #40 - 'A' // 8746 - 'a24G' // 8747-8748 #630 - '248N' // 8749 #6461 - 'A' // 874a - '1O' // 874b #40 - '91B' // 874c #2367 - '1O' // 874d #40 - '91A' // 874e #2366 - '38A' // 874f #988 - 'A' // 8750 - '1O' // 8751 #40 - 'A' // 8752 - '8V' // 8753 #229 - 'A' // 8754 - '150D' // 8755 #3903 - 'A' // 8756 - '28K' // 8757 #738 - '8V' // 8758 #229 - '63R' // 8759 #1655 - 'bA' // 875a-875c - '8V' // 875d #229 - 'A' // 875e - '28K' // 875f #738 - '63R' // 8760 #1655 - '22N' // 8761 #585 - '55L' // 8762 #1441 - 'b9C' // 8763-8765 #236 - '67G' // 8766 #1748 - 'A' // 8767 - '33D' // 8768 #861 - 'A' // 8769 - '22M' // 876a #584 - 'A' // 876b - 'a16L' // 876c-876d #427 - '9C' // 876e #236 - '22M' // 876f #584 - '33E' // 8770 #862 - '25B' // 8771 #651 - '49Q' // 8772 #1290 - 'A' // 8773 - '156O' // 8774 #4070 - '24G' // 8775 #630 - '174N' // 8776 #4537 - '16L' // 8777 #427 - '63Q' // 8778 #1654 - 'A' // 8779 - '16L' // 877a #427 - '9C' // 877b #236 - '49Q' // 877c #1290 - '33E' // 877d #862 - '24G' // 877e #630 - '1O' // 877f #40 - 'A' // 8780 - '16L' // 8781 #427 - '63Q' // 8782 #1654 - '121X' // 8783 #3169 - '22M' // 8784 #584 - '9C' // 8785 #236 - '25B' // 8786 #651 - '1O' // 8787 #40 - '9C' // 8788 #236 - '1O' // 8789 #40 - 'A' // 878a - '9C' // 878b #236 - '25B' // 878c #651 - '203Z' // 878d #5303 - '1O' // 878e #40 - 'A' // 878f - '1O' // 8790 #40 - 'aA' // 8791-8792 - '9C' // 8793 #236 - 'A' // 8794 - '1O' // 8795 #40 - 'A' // 8796 - '9C' // 8797 #236 - '22M' // 8798 #584 - '1O' // 8799 #40 - '24G' // 879a #630 - 'bA' // 879b-879d - '127M' // 879e #3314 - '33D' // 879f #861 - '49Q' // 87a0 #1290 - 'A' // 87a1 - '67G' // 87a2 #1748 - '9C' // 87a3 #236 - 'A' // 87a4 - '44M' // 87a5 #1156 - 'A' // 87a6 - '1O' // 87a7 #40 - '24G' // 87a8 #630 - '44M' // 87a9 #1156 - 'A' // 87aa - 'b9C' // 87ab-87ad #236 - '1O' // 87ae #40 - '9C' // 87af #236 - 'A' // 87b0 - '25B' // 87b1 #651 - 'A' // 87b2 - '33D' // 87b3 #861 - 'A' // 87b4 - '9C' // 87b5 #236 - 'bA' // 87b6-87b8 - '16L' // 87b9 #427 - '172S' // 87ba #4490 - '22M' // 87bb #584 - 'A' // 87bc - '9C' // 87bd #236 - 'a22M' // 87be-87bf #584 - '9C' // 87c0 #236 - '25B' // 87c1 #651 - 'aA' // 87c2-87c3 - '33D' // 87c4 #861 - '16L' // 87c5 #427 - '9C' // 87c6 #236 - '1O' // 87c7 #40 - '22M' // 87c8 #584 - '1O' // 87c9 #40 - 'a9C' // 87ca-87cb #236 - '16L' // 87cc #427 - 'A' // 87cd - '22M' // 87ce #584 - 'A' // 87cf - '1O' // 87d0 #40 - '131J' // 87d1 #3415 - '9C' // 87d2 #236 - 'aA' // 87d3-87d4 - '1O' // 87d5 #40 - '25B' // 87d6 #651 - 'aA' // 87d7-87d8 - '1O' // 87d9 #40 - '25B' // 87da #651 - '33E' // 87db #862 - '9C' // 87dc #236 - 'A' // 87dd - '24G' // 87de #630 - '1O' // 87df #40 - '33D' // 87e0 #861 - '16L' // 87e1 #427 - '1L' // 87e2 #37 - '33C' // 87e3 #860 - '1L' // 87e4 #37 - '25A' // 87e5 #650 - '1L' // 87e6 #37 - '16L' // 87e7 #427 - 'aA' // 87e8-87e9 - '25A' // 87ea #650 - '33C' // 87eb #860 - '132N' // 87ec #3445 - '1L' // 87ed #37 - '33E' // 87ee #862 - '90Z' // 87ef #2365 - 'A' // 87f0 - '1L' // 87f1 #37 - '194J' // 87f2 #5053 - '25A' // 87f3 #650 - '16L' // 87f4 #427 - '44N' // 87f5 #1157 - 'a33C' // 87f6-87f7 #860 - '1L' // 87f8 #37 - '173L' // 87f9 #4509 - '1L' // 87fa #37 - '144Q' // 87fb #3760 - 'A' // 87fc - '24G' // 87fd #630 - '55K' // 87fe #1440 - '1L' // 87ff #37 - 'A' // 8800 - '1L' // 8801 #37 - '33E' // 8802 #862 - '25A' // 8803 #650 - '16L' // 8804 #427 - '55K' // 8805 #1440 - '33C' // 8806 #860 - '1L' // 8807 #37 - 'A' // 8808 - '1L' // 8809 #37 - 'a25A' // 880a-880b #650 - 'A' // 880c - '127I' // 880d #3310 - '1L' // 880e #37 - '44N' // 880f #1157 - 'a33C' // 8810-8811 #860 - '1L' // 8812 #37 - '25A' // 8813 #650 - '121W' // 8814 #3168 - 'a25A' // 8815-8816 #650 - 'A' // 8817 - '44N' // 8818 #1157 - '69Y' // 8819 #1818 - '1L' // 881a #37 - '10S' // 881b #278 - '1L' // 881c #37 - 'A' // 881d - '1L' // 881e #37 - '156N' // 881f #4069 - 'A' // 8820 - '10S' // 8821 #278 - '138O' // 8822 #3602 - '90O' // 8823 #2354 - 'bA' // 8824-8826 - '24Z' // 8827 #649 - '1L' // 8828 #37 - 'cA' // 8829-882c - '28I' // 882d #736 - '1L' // 882e #37 - 'A' // 882f - '1L' // 8830 #37 - '28J' // 8831 #737 - '10S' // 8832 #278 - 'aA' // 8833-8834 - '10S' // 8835 #278 - '127H' // 8836 #3309 - 'aA' // 8837-8838 - '10S' // 8839 #278 - '1L' // 883a #37 - '185D' // 883b #4813 - '10S' // 883c #278 - 'bA' // 883d-883f - '215R' // 8840 #5607 - '1L' // 8841 #37 - '28I' // 8842 #736 - '1L' // 8843 #37 - '10S' // 8844 #278 - '90K' // 8845 #2350 - '90V' // 8846 #2361 - 'A' // 8847 - 'a1L' // 8848-8849 #37 - '10S' // 884a #278 - '1L' // 884b #37 - '247A' // 884c #6422 - '65Z' // 884d #1715 - '10S' // 884e #278 - '22L' // 884f #583 - 'A' // 8850 - '1L' // 8851 #37 - '28J' // 8852 #737 - '236H' // 8853 #6143 - '2R' // 8854 #69 - '10S' // 8855 #278 - '24Z' // 8856 #649 - '228U' // 8857 #5948 - '1L' // 8858 #37 - '28J' // 8859 #737 - '24Z' // 885a #649 - '215T' // 885b #5609 - '1L' // 885c #37 - '204J' // 885d #5313 - '10S' // 885e #278 - '1L' // 885f #37 - '28I' // 8860 #736 - '187E' // 8861 #4866 - '28J' // 8862 #737 - '228V' // 8863 #5949 - '10S' // 8864 #278 - '90Y' // 8865 #2364 - 'aA' // 8866-8867 - '244V' // 8868 #6365 - '10S' // 8869 #278 - '90X' // 886a #2363 - '170M' // 886b #4432 - '2L' // 886c #63 - '55J' // 886d #1439 - '10S' // 886e #278 - '69Y' // 886f #1818 - '173X' // 8870 #4521 - '24Z' // 8871 #649 - '28J' // 8872 #737 - 'aA' // 8873-8874 - '24Z' // 8875 #649 - 'A' // 8876 - '150A' // 8877 #3900 - 'A' // 8878 - '10S' // 8879 #278 - 'A' // 887a - '1L' // 887b #37 - 'A' // 887c - '10S' // 887d #278 - '28J' // 887e #737 - '90N' // 887f #2353 - '1L' // 8880 #37 - '150B' // 8881 #3901 - '44L' // 8882 #1155 - 'A' // 8883 - '55I' // 8884 #1438 - 'a10J' // 8885-8886 #269 - '22L' // 8887 #583 - '44L' // 8888 #1155 - 'aA' // 8889-888a - '213B' // 888b #5539 - 'A' // 888c - '144R' // 888d #3761 - 'A' // 888e - '90W' // 888f #2362 - '55J' // 8890 #1439 - 'A' // 8891 - '44L' // 8892 #1155 - '55I' // 8893 #1438 - 'aA' // 8894-8895 - '182P' // 8896 #4747 - '55H' // 8897 #1437 - '24Z' // 8898 #649 - '1L' // 8899 #37 - '24Z' // 889a #649 - '24Y' // 889b #648 - '90L' // 889c #2351 - '22L' // 889d #583 - '55H' // 889e #1437 - '1L' // 889f #37 - '28I' // 88a0 #736 - 'A' // 88a1 - '24Y' // 88a2 #648 - 'A' // 88a3 - '24Y' // 88a4 #648 - 'A' // 88a5 - '10J' // 88a6 #269 - 'A' // 88a7 - '24Y' // 88a8 #648 - 'A' // 88a9 - '24Y' // 88aa #648 - '233Y' // 88ab #6082 - 'A' // 88ac - '3Y' // 88ad #102 - '24Y' // 88ae #648 - 'A' // 88af - '1L' // 88b0 #37 - '24Y' // 88b1 #648 - 'aA' // 88b2-88b3 - '90P' // 88b4 #2355 - '28I' // 88b5 #736 - 'A' // 88b6 - '6L' // 88b7 #167 - '17Z' // 88b8 #467 - 'A' // 88b9 - '1L' // 88ba #37 - 'A' // 88bb - '6L' // 88bc #167 - 'a20C' // 88bd-88be #522 - '28I' // 88bf #736 - '6L' // 88c0 #167 - '190E' // 88c1 #4944 - '181X' // 88c2 #4729 - 'a1L' // 88c3-88c4 #37 - '149O' // 88c5 #3888 - '41K' // 88c6 #1076 - '17Y' // 88c7 #466 - '10J' // 88c8 #269 - '17Y' // 88c9 #466 - 'b20C' // 88ca-88cc #522 - '1L' // 88cd #37 - '6L' // 88ce #167 - '191M' // 88cf #4978 - 'A' // 88d0 - '41K' // 88d1 #1076 - '6L' // 88d2 #167 - '41K' // 88d3 #1076 - '65Z' // 88d4 #1715 - '176K' // 88d5 #4586 - '17Z' // 88d6 #467 - 'A' // 88d7 - '6L' // 88d8 #167 - '185E' // 88d9 #4814 - 'A' // 88da - '6L' // 88db #167 - '221V' // 88dc #5767 - '232E' // 88dd #6036 - '1L' // 88de #37 - '24X' // 88df #647 - '1L' // 88e0 #37 - '224T' // 88e1 #5843 - 'a10J' // 88e2-88e3 #269 - '3N' // 88e4 #91 - '10J' // 88e5 #269 - '22L' // 88e6 #583 - '1L' // 88e7 #37 - '24X' // 88e8 #647 - 'bA' // 88e9-88eb - '17Y' // 88ec #466 - 'aA' // 88ed-88ee - '20C' // 88ef #522 - 'a6L' // 88f0-88f1 #167 - '1L' // 88f2 #37 - '90M' // 88f3 #2352 - '24X' // 88f4 #647 - '55G' // 88f5 #1436 - 'A' // 88f6 - '1L' // 88f7 #37 - '183C' // 88f8 #4760 - '150C' // 88f9 #3902 - 'aA' // 88fa-88fb - '6L' // 88fc #167 - '236R' // 88fd #6153 - '90R' // 88fe #2357 - '22L' // 88ff #583 - '17Y' // 8900 #466 - '1L' // 8901 #37 - '6L' // 8902 #167 - 'A' // 8903 - '1L' // 8904 #37 - 'A' // 8905 - '6L' // 8906 #167 - '216C' // 8907 #5618 - 'A' // 8908 - '10J' // 8909 #269 - '6L' // 890a #167 - '17Y' // 890b #466 - '6L' // 890c #167 - 'b1L' // 890d-890f #37 - '136B' // 8910 #3537 - 'A' // 8911 - '90T' // 8912 #2359 - '24X' // 8913 #647 - '17Y' // 8914 #466 - '6L' // 8915 #167 - '1L' // 8916 #37 - 'A' // 8917 - 'a24X' // 8918-8919 #647 - '6L' // 891a #167 - '10J' // 891b #269 - 'b1L' // 891c-891e #37 - '17Z' // 891f #467 - '1L' // 8920 #37 - '17Y' // 8921 #466 - 'A' // 8922 - '17Z' // 8923 #467 - '22L' // 8924 #583 - '24X' // 8925 #647 - 'b1L' // 8926-8928 #37 - 'A' // 8929 - '90J' // 892a #2349 - '6L' // 892b #167 - 'A' // 892c - '17Z' // 892d #467 - 'aA' // 892e-892f - '6L' // 8930 #167 - '1L' // 8931 #37 - '199P' // 8932 #5189 - '17Z' // 8933 #467 - '10J' // 8934 #269 - '20C' // 8935 #522 - '24X' // 8936 #647 - '1L' // 8937 #37 - '55G' // 8938 #1436 - 'a1L' // 8939-893a #37 - '132M' // 893b #3444 - 'A' // 893c - '17Y' // 893d #466 - '1L' // 893e #37 - 'A' // 893f - '1L' // 8940 #37 - '90H' // 8941 #2347 - 'a20C' // 8942-8943 #522 - '127G' // 8944 #3308 - '1L' // 8945 #37 - '20C' // 8946 #522 - '17Z' // 8947 #467 - 'A' // 8948 - '20C' // 8949 #522 - 'aA' // 894a-894b - '6L' // 894c #167 - '20C' // 894d #522 - 'A' // 894e - '1L' // 894f #37 - 'aA' // 8950-8951 - '1L' // 8952 #37 - 'A' // 8953 - '22L' // 8954 #583 - '10J' // 8955 #269 - '6L' // 8956 #167 - '20C' // 8957 #522 - 'A' // 8958 - '17Y' // 8959 #466 - 'a1L' // 895a-895b #37 - '6L' // 895c #167 - 'A' // 895d - '6L' // 895e #167 - '90Q' // 895f #2356 - '6L' // 8960 #167 - 'b1L' // 8961-8963 #37 - '90I' // 8964 #2348 - '22L' // 8965 #583 - '6L' // 8966 #167 - 'bA' // 8967-8969 - '170L' // 896a #4431 - '1L' // 896b #37 - '17Y' // 896c #466 - 'a1L' // 896d-896e #37 - '170N' // 896f #4433 - '41K' // 8970 #1076 - '17Z' // 8971 #467 - '197A' // 8972 #5122 - '1T' // 8973 #45 - '33B' // 8974 #859 - '1T' // 8975 #45 - 'A' // 8976 - '55F' // 8977 #1435 - 'aA' // 8978-8979 - '1T' // 897a #45 - '37Z' // 897b #987 - 'a1T' // 897c-897d #45 - '37Z' // 897e #987 - '68S' // 897f #1786 - '37Z' // 8980 #987 - '244U' // 8981 #6364 - '17Z' // 8982 #467 - '90G' // 8983 #2346 - 'A' // 8984 - '10J' // 8985 #269 - '218R' // 8986 #5685 - '90U' // 8987 #2360 - '37Z' // 8988 #987 - '55F' // 8989 #1435 - '33B' // 898a #859 - '245H' // 898b #6377 - '10J' // 898c #269 - '1T' // 898d #45 - 'A' // 898e - '238Z' // 898f #6213 - '1T' // 8990 #45 - '17Z' // 8991 #467 - 'A' // 8992 - '138M' // 8993 #3600 - 'a33B' // 8994-8995 #859 - '241T' // 8996 #6285 - '90S' // 8997 #2358 - '33B' // 8998 #859 - 'A' // 8999 - '258E' // 899a #6712 - '1T' // 899b #45 - '33B' // 899c #859 - 'aA' // 899d-899e - 'a1T' // 899f-89a0 #45 - '37Y' // 89a1 #986 - 'aA' // 89a2-89a3 - '90F' // 89a4 #2345 - 'a20B' // 89a5-89a6 #521 - '90B' // 89a7 #2341 - 'A' // 89a8 - '37Y' // 89a9 #986 - '235N' // 89aa #6123 - 'A' // 89ab - '20B' // 89ac #521 - 'aA' // 89ad-89ae - '20B' // 89af #521 - '1T' // 89b0 #45 - 'A' // 89b1 - '37Y' // 89b2 #986 - '259L' // 89b3 #6745 - 'b1T' // 89b4-89b6 #45 - '17X' // 89b7 #465 - 'aA' // 89b8-89b9 - '224S' // 89ba #5842 - 'A' // 89bb - '28H' // 89bc #735 - '232C' // 89bd #6034 - 'A' // 89be - '20B' // 89bf #521 - '232B' // 89c0 #6033 - '132L' // 89c1 #3443 - '1Z' // 89c2 #51 - 'A' // 89c3 - '3Q' // 89c4 #94 - '3G' // 89c5 #84 - '49C' // 89c6 #1276 - '10J' // 89c7 #269 - '1Z' // 89c8 #51 - '2D' // 89c9 #55 - 'b10J' // 89ca-89cc #269 - 'A' // 89cd - 'c10J' // 89ce-89d1 #269 - '228T' // 89d2 #5947 - 'A' // 89d3 - 'a20B' // 89d4-89d5 #521 - '49P' // 89d6 #1289 - 'a1T' // 89d7-89d8 #45 - 'A' // 89d9 - '17X' // 89da #465 - 'A' // 89db - '17X' // 89dc #465 - '20B' // 89dd #521 - '10J' // 89de #269 - 'cA' // 89df-89e2 - '238H' // 89e3 #6195 - 'A' // 89e4 - '17X' // 89e5 #465 - '90A' // 89e6 #2340 - '17X' // 89e7 #465 - 'A' // 89e8 - '1T' // 89e9 #45 - 'A' // 89ea - '49P' // 89eb #1289 - 'A' // 89ec - '20B' // 89ed #521 - 'A' // 89ee - '10J' // 89ef #269 - 'A' // 89f0 - '17X' // 89f1 #465 - 'A' // 89f2 - '17X' // 89f3 #465 - '37Y' // 89f4 #986 - 'A' // 89f5 - '20B' // 89f6 #521 - 'A' // 89f7 - '199O' // 89f8 #5188 - '1T' // 89f9 #45 - 'bA' // 89fa-89fc - '1T' // 89fd #45 - 'A' // 89fe - '17X' // 89ff #465 - '243L' // 8a00 #6329 - '49P' // 8a01 #1289 - '233L' // 8a02 #6069 - '89U' // 8a03 #2334 - 'a1T' // 8a04-8a05 #45 - 'A' // 8a06 - '17X' // 8a07 #465 - '242U' // 8a08 #6312 - '10J' // 8a09 #269 - '240O' // 8a0a #6254 - 'A' // 8a0b - '89Q' // 8a0c #2330 - 'A' // 8a0d - '220B' // 8a0e #5721 - '17X' // 8a0f #465 - 'b20B' // 8a10-8a12 #521 - '209P' // 8a13 #5449 - '1T' // 8a14 #45 - '121V' // 8a15 #3167 - '89P' // 8a16 #2329 - '197M' // 8a17 #5134 - '245J' // 8a18 #6379 - 'A' // 8a19 - '27G' // 8a1a #708 - '8U' // 8a1b #228 - '33A' // 8a1c #858 - '41B' // 8a1d #1067 - '1T' // 8a1e #45 - '142F' // 8a1f #3697 - 'a1T' // 8a20-8a21 #45 - '10R' // 8a22 #277 - '66O' // 8a23 #1730 - '1T' // 8a24 #45 - '8U' // 8a25 #228 - '1T' // 8a26 #45 - '24W' // 8a27 #646 - 'A' // 8a28 - '33A' // 8a29 #858 - '221F' // 8a2a #5751 - '10R' // 8a2b #277 - '1T' // 8a2c #45 - '244E' // 8a2d #6348 - 'A' // 8a2e - '1T' // 8a2f #45 - 'A' // 8a30 - '68K' // 8a31 #1778 - 'A' // 8a32 - '258C' // 8a33 #6710 - '215C' // 8a34 #5592 - '1T' // 8a35 #45 - '8U' // 8a36 #228 - '41J' // 8a37 #1075 - '90E' // 8a38 #2344 - 'A' // 8a39 - '67C' // 8a3a #1744 - '232D' // 8a3b #6035 - '155O' // 8a3c #4044 - '4G' // 8a3d #110 - '10R' // 8a3e #277 - 'A' // 8a3f - '41J' // 8a40 #1075 - '10R' // 8a41 #277 - 'A' // 8a42 - '1T' // 8a43 #45 - 'A' // 8a44 - '4G' // 8a45 #110 - '10R' // 8a46 #277 - '1T' // 8a47 #45 - '10R' // 8a48 #277 - '28H' // 8a49 #735 - 'bA' // 8a4a-8a4c - '1T' // 8a4d #45 - '4G' // 8a4e #110 - 'A' // 8a4f - '196Z' // 8a50 #5121 - '4G' // 8a51 #110 - '10R' // 8a52 #277 - '1T' // 8a53 #45 - '8U' // 8a54 #228 - '68R' // 8a55 #1785 - 'a4G' // 8a56-8a57 #110 - '10R' // 8a58 #277 - 'aA' // 8a59-8a5a - '8U' // 8a5b #228 - '1T' // 8a5c #45 - '41J' // 8a5d #1075 - '210H' // 8a5e #5467 - '27G' // 8a5f #708 - '159P' // 8a60 #4149 - '10R' // 8a61 #277 - '224R' // 8a62 #5841 - '89T' // 8a63 #2333 - 'A' // 8a64 - '1T' // 8a65 #45 - '230M' // 8a66 #5992 - '4G' // 8a67 #110 - 'A' // 8a68 - '202T' // 8a69 #5271 - '27G' // 8a6a #708 - '89X' // 8a6b #2337 - '4G' // 8a6c #110 - '41B' // 8a6d #1067 - '152W' // 8a6e #3974 - 'A' // 8a6f - '89Z' // 8a70 #2339 - '244K' // 8a71 #6354 - '227T' // 8a72 #5921 - '223W' // 8a73 #5820 - '24W' // 8a74 #646 - '17W' // 8a75 #464 - '4G' // 8a76 #110 - '1T' // 8a77 #45 - 'A' // 8a78 - '156M' // 8a79 #4068 - '10R' // 8a7a #277 - 'a4G' // 8a7b-8a7c #110 - 'A' // 8a7d - '28H' // 8a7e #735 - 'a1T' // 8a7f-8a80 #45 - 'A' // 8a81 - '4G' // 8a82 #110 - '1T' // 8a83 #45 - '4G' // 8a84 #110 - '8U' // 8a85 #228 - '4G' // 8a86 #110 - '175W' // 8a87 #4572 - 'A' // 8a88 - '254E' // 8a89 #6608 - '27G' // 8a8a #708 - '1T' // 8a8b #45 - '222L' // 8a8c #5783 - '238G' // 8a8d #6194 - 'A' // 8a8e - '4G' // 8a8f #110 - 'b10R' // 8a90-8a92 #277 - '153V' // 8a93 #3999 - '24W' // 8a94 #646 - '198V' // 8a95 #5169 - 'a1T' // 8a96-8a97 #45 - '67C' // 8a98 #1744 - '1T' // 8a99 #45 - '4G' // 8a9a #110 - 'A' // 8a9b - '33A' // 8a9c #858 - 'A' // 8a9d - '238P' // 8a9e #6203 - '1T' // 8a9f #45 - '210A' // 8aa0 #5460 - '8U' // 8aa1 #228 - 'A' // 8aa2 - '8U' // 8aa3 #228 - '210T' // 8aa4 #5479 - '8U' // 8aa5 #228 - '63B' // 8aa6 #1639 - '4G' // 8aa7 #110 - '8U' // 8aa8 #228 - '89R' // 8aa9 #2331 - '239O' // 8aaa #6228 - 'A' // 8aab - '131H' // 8aac #3413 - '259W' // 8aad #6756 - '41J' // 8aae #1075 - '28H' // 8aaf #735 - '213A' // 8ab0 #5538 - 'A' // 8ab1 - '212B' // 8ab2 #5513 - '1T' // 8ab3 #45 - '33A' // 8ab4 #858 - 'A' // 8ab5 - '4G' // 8ab6 #110 - '1T' // 8ab7 #45 - '27G' // 8ab8 #708 - '142K' // 8ab9 #3702 - 'A' // 8aba - '1T' // 8abb #45 - '163I' // 8abc #4246 - 'A' // 8abd - '8U' // 8abe #228 - '231K' // 8abf #6016 - 'aA' // 8ac0-8ac1 - '17W' // 8ac2 #464 - '1T' // 8ac3 #45 - '17W' // 8ac4 #464 - 'A' // 8ac5 - '4G' // 8ac6 #110 - '213W' // 8ac7 #5560 - '1T' // 8ac8 #45 - '4G' // 8ac9 #110 - '1T' // 8aca #45 - '241M' // 8acb #6278 - '4G' // 8acc #110 - '17W' // 8acd #464 - 'A' // 8ace - '89V' // 8acf #2335 - '1T' // 8ad0 #45 - '4G' // 8ad1 #110 - '172N' // 8ad2 #4485 - 'b1T' // 8ad3-8ad5 #45 - '234I' // 8ad6 #6092 - '138N' // 8ad7 #3601 - 'aA' // 8ad8-8ad9 - '28H' // 8ada #735 - '8U' // 8adb #228 - '144O' // 8adc #3758 - 'a4G' // 8add-8ade #110 - '10R' // 8adf #277 - '4G' // 8ae0 #110 - '8U' // 8ae1 #228 - '4G' // 8ae2 #110 - 'A' // 8ae3 - '4G' // 8ae4 #110 - 'A' // 8ae5 - '89Y' // 8ae6 #2338 - '41B' // 8ae7 #1067 - '27G' // 8ae8 #708 - 'A' // 8ae9 - '90C' // 8aea #2342 - '8U' // 8aeb #228 - '1T' // 8aec #45 - '89W' // 8aed #2336 - '185C' // 8aee #4812 - 'A' // 8aef - '1T' // 8af0 #45 - '8U' // 8af1 #228 - '24W' // 8af2 #646 - 'a10R' // 8af3-8af4 #277 - '4G' // 8af5 #110 - '17W' // 8af6 #464 - '41B' // 8af7 #1067 - '182Q' // 8af8 #4748 - '33A' // 8af9 #858 - '8U' // 8afa #228 - 'A' // 8afb - '4G' // 8afc #110 - 'A' // 8afd - '203U' // 8afe #5298 - '1T' // 8aff #45 - '66O' // 8b00 #1730 - '8U' // 8b01 #228 - '192H' // 8b02 #4999 - 'A' // 8b03 - '17W' // 8b04 #464 - '4G' // 8b05 #110 - '1T' // 8b06 #45 - '10R' // 8b07 #277 - 'aA' // 8b08-8b09 - '144P' // 8b0a #3759 - '4G' // 8b0b #110 - '10R' // 8b0c #277 - '4G' // 8b0d #110 - '168X' // 8b0e #4391 - '4G' // 8b0f #110 - '8U' // 8b10 #228 - '1T' // 8b11 #45 - 'A' // 8b12 - '24W' // 8b13 #646 - '17W' // 8b14 #464 - 'A' // 8b15 - '17W' // 8b16 #464 - '153P' // 8b17 #3993 - 'A' // 8b18 - '160H' // 8b19 #4167 - '17W' // 8b1a #464 - '221M' // 8b1b #5758 - '4G' // 8b1c #110 - '228C' // 8b1d #5930 - '1T' // 8b1e #45 - '28H' // 8b1f #735 - '170K' // 8b20 #4430 - '253L' // 8b21 #6589 - '24W' // 8b22 #646 - 'bA' // 8b23-8b25 - '10R' // 8b26 #277 - 'A' // 8b27 - '8U' // 8b28 #228 - 'aA' // 8b29-8b2a - '17W' // 8b2b #464 - '63B' // 8b2c #1639 - '55D' // 8b2d #1433 - '24W' // 8b2e #646 - 'A' // 8b2f - '1K' // 8b30 #36 - 'aA' // 8b31-8b32 - '89S' // 8b33 #2332 - 'bA' // 8b34-8b36 - '1K' // 8b37 #36 - 'A' // 8b38 - '166L' // 8b39 #4327 - 'aA' // 8b3a-8b3b - '1K' // 8b3c #36 - 'A' // 8b3d - '132K' // 8b3e #3442 - '90D' // 8b3f #2343 - 'A' // 8b40 - '89M' // 8b41 #2326 - '1K' // 8b42 #36 - '55D' // 8b43 #1433 - '1K' // 8b44 #36 - '248M' // 8b45 #6460 - '55E' // 8b46 #1434 - 'A' // 8b47 - '1K' // 8b48 #36 - '232A' // 8b49 #6032 - 'aA' // 8b4a-8b4b - '89O' // 8b4c #2328 - '55E' // 8b4d #1434 - '89N' // 8b4e #2327 - '54Z' // 8b4f #1429 - 'A' // 8b50 - 'a1K' // 8b51-8b52 #36 - 'a20A' // 8b53-8b54 #520 - 'A' // 8b55 - '20A' // 8b56 #520 - '27G' // 8b57 #708 - '68K' // 8b58 #1778 - '20A' // 8b59 #520 - '153D' // 8b5a #3981 - '1K' // 8b5b #36 - '202C' // 8b5c #5254 - 'A' // 8b5d - '89D' // 8b5e #2317 - '20A' // 8b5f #520 - '89K' // 8b60 #2324 - 'A' // 8b61 - '54Y' // 8b62 #1428 - '1K' // 8b63 #36 - 'aA' // 8b64-8b65 - '211G' // 8b66 #5492 - 'aA' // 8b67-8b68 - '28G' // 8b69 #734 - '89L' // 8b6a #2325 - '20A' // 8b6b #520 - '54Z' // 8b6c #1429 - '20A' // 8b6d #520 - 'A' // 8b6e - '192F' // 8b6f #4997 - '228S' // 8b70 #5946 - '1K' // 8b71 #36 - '255U' // 8b72 #6650 - 'A' // 8b73 - '127E' // 8b74 #3306 - 'A' // 8b75 - '1K' // 8b76 #36 - '68R' // 8b77 #1785 - 'a1K' // 8b78-8b79 #36 - 'aA' // 8b7a-8b7b - '1K' // 8b7c #36 - '177S' // 8b7d #4620 - '20A' // 8b7e #520 - '1K' // 8b7f #36 - '224Q' // 8b80 #5840 - '28G' // 8b81 #734 - 'A' // 8b82 - '89F' // 8b83 #2319 - 'a1K' // 8b84-8b85 #36 - 'cA' // 8b86-8b89 - '68O' // 8b8a #1782 - '1K' // 8b8b #36 - '55B' // 8b8c #1431 - '1K' // 8b8d #36 - '20A' // 8b8e #520 - '28G' // 8b8f #734 - '89E' // 8b90 #2318 - 'A' // 8b91 - '55A' // 8b92 #1430 - '68O' // 8b93 #1782 - '1K' // 8b94 #36 - '20A' // 8b95 #520 - '55A' // 8b96 #1430 - 'aA' // 8b97-8b98 - '55B' // 8b99 #1431 - '217A' // 8b9a #5642 - '54Y' // 8b9b #1428 - '11W' // 8b9c #308 - '1K' // 8b9d #36 - 'a11W' // 8b9e-8b9f #308 - '89C' // 8ba0 #2316 - '3Q' // 8ba1 #94 - '2D' // 8ba2 #55 - '3D' // 8ba3 #81 - '3Q' // 8ba4 #94 - 'b3D' // 8ba5-8ba7 #81 - '3I' // 8ba8 #86 - '1Z' // 8ba9 #51 - 'b3D' // 8baa-8bac #81 - '3I' // 8bad #86 - 'b3Q' // 8bae-8bb0 #94 - 'A' // 8bb1 - '2C' // 8bb2 #54 - '2W' // 8bb3 #74 - 'a3D' // 8bb4-8bb5 #81 - '3X' // 8bb6 #101 - '3D' // 8bb7 #81 - '1Z' // 8bb8 #51 - '3D' // 8bb9 #81 - '7K' // 8bba #192 - 'A' // 8bbb - '3X' // 8bbc #101 - '2K' // 8bbd #62 - '65M' // 8bbe #1702 - '2D' // 8bbf #55 - '2R' // 8bc0 #69 - '3Q' // 8bc1 #94 - 'a3D' // 8bc2-8bc3 #81 - '3Q' // 8bc4 #94 - '3D' // 8bc5 #81 - '1Z' // 8bc6 #51 - 'A' // 8bc7 - '4C' // 8bc8 #106 - '2D' // 8bc9 #55 - '1R' // 8bca #43 - 'a3D' // 8bcb-8bcc #81 - '2D' // 8bcd #55 - 'b3D' // 8bce-8bd0 #81 - '3N' // 8bd1 #91 - 'b3D' // 8bd2-8bd4 #81 - '1Z' // 8bd5 #51 - '3D' // 8bd6 #81 - '3Y' // 8bd7 #102 - 'a3D' // 8bd8-8bd9 #81 - '2C' // 8bda #54 - '3G' // 8bdb #84 - '3D' // 8bdc #81 - '7K' // 8bdd #192 - '1R' // 8bde #43 - '3D' // 8bdf #81 - 'a2K' // 8be0-8be1 #62 - '63N' // 8be2 #1651 - 'a3D' // 8be3-8be4 #81 - '1Z' // 8be5 #51 - '2D' // 8be6 #55 - 'b3D' // 8be7-8be9 #81 - 'A' // 8bea - 'a3D' // 8beb-8bec #81 - '1Z' // 8bed #51 - '3D' // 8bee #81 - '3I' // 8bef #86 - '3D' // 8bf0 #81 - '3N' // 8bf1 #91 - 'a3D' // 8bf2-8bf3 #81 - '7K' // 8bf4 #192 - '3G' // 8bf5 #84 - '3D' // 8bf6 #81 - '7K' // 8bf7 #192 - '4C' // 8bf8 #106 - '3D' // 8bf9 #81 - '3Y' // 8bfa #102 - '3Q' // 8bfb #94 - 'a3D' // 8bfc-8bfd #81 - '3I' // 8bfe #86 - 'a3D' // 8bff-8c00 #81 - '2C' // 8c01 #54 - '3D' // 8c02 #81 - '1Z' // 8c03 #51 - '3D' // 8c04 #81 - '3H' // 8c05 #85 - 'a3D' // 8c06-8c07 #81 - '2C' // 8c08 #54 - 'A' // 8c09 - '2R' // 8c0a #69 - '4C' // 8c0b #106 - '3D' // 8c0c #81 - '3H' // 8c0d #85 - '2K' // 8c0e #62 - '3D' // 8c0f #81 - '2L' // 8c10 #63 - 'a3D' // 8c11-8c12 #81 - '1R' // 8c13 #43 - 'g3D' // 8c14-8c1b #81 - '2Y' // 8c1c #76 - 'd3D' // 8c1d-8c21 #81 - '2D' // 8c22 #55 - '2L' // 8c23 #63 - 'a3D' // 8c24-8c25 #81 - '3H' // 8c26 #85 - '3D' // 8c27 #81 - '4C' // 8c28 #106 - '2R' // 8c29 #69 - 'b3D' // 8c2a-8c2c #81 - '2R' // 8c2d #69 - 'b3D' // 8c2e-8c30 #81 - '3N' // 8c31 #91 - 'd3D' // 8c32-8c36 #81 - '206F' // 8c37 #5361 - '1K' // 8c38 #36 - '11W' // 8c39 #308 - '1K' // 8c3a #36 - 'aA' // 8c3b-8c3c - 'a1K' // 8c3d-8c3e #36 - '44K' // 8c3f #1154 - 'A' // 8c40 - '132J' // 8c41 #3441 - 'bA' // 8c42-8c44 - '1K' // 8c45 #36 - '204O' // 8c46 #5318 - '11V' // 8c47 #307 - '132I' // 8c48 #3440 - '11V' // 8c49 #307 - '89J' // 8c4a #2323 - '11V' // 8c4b #307 - '44K' // 8c4c #1154 - 'A' // 8c4d - '127F' // 8c4e #3307 - '11V' // 8c4f #307 - '217B' // 8c50 #5643 - '11W' // 8c51 #308 - 'A' // 8c52 - '1K' // 8c53 #36 - '163G' // 8c54 #4244 - '44K' // 8c55 #1154 - '3D' // 8c56 #81 - 'b1K' // 8c57-8c59 #36 - '149D' // 8c5a #3877 - '1K' // 8c5b #36 - 'A' // 8c5c - '1K' // 8c5d #36 - 'bA' // 8c5e-8c60 - '223A' // 8c61 #5798 - '11V' // 8c62 #307 - 'a1K' // 8c63-8c64 #36 - 'A' // 8c65 - '1K' // 8c66 #36 - 'A' // 8c67 - '11V' // 8c68 #307 - '1K' // 8c69 #36 - '210Q' // 8c6a #5476 - '163F' // 8c6b #4243 - '192G' // 8c6c #4998 - '1K' // 8c6d #36 - 'dA' // 8c6e-8c72 - '11V' // 8c73 #307 - 'A' // 8c74 - 'a1K' // 8c75-8c76 #36 - 'A' // 8c77 - '11V' // 8c78 #307 - '159O' // 8c79 #4148 - '44J' // 8c7a #1153 - '11W' // 8c7b #308 - '1K' // 8c7c #36 - 'A' // 8c7d - '1K' // 8c7e #36 - 'bA' // 8c7f-8c81 - '44J' // 8c82 #1153 - 'aA' // 8c83-8c84 - '11V' // 8c85 #307 - 'a1K' // 8c86-8c87 #36 - 'A' // 8c88 - '11V' // 8c89 #307 - '44J' // 8c8a #1153 - '1K' // 8c8b #36 - '188L' // 8c8c #4899 - '11V' // 8c8d #307 - '248L' // 8c8e #6459 - 'A' // 8c8f - '11V' // 8c90 #307 - 'A' // 8c91 - '1K' // 8c92 #36 - '206U' // 8c93 #5376 - '11V' // 8c94 #307 - 'bA' // 8c95-8c97 - '11V' // 8c98 #307 - '11W' // 8c99 #308 - 'A' // 8c9a - '28G' // 8c9b #734 - '1K' // 8c9c #36 - '209B' // 8c9d #5435 - '168B' // 8c9e #4369 - '28G' // 8c9f #734 - '222A' // 8ca0 #5772 - '228H' // 8ca1 #5935 - '182H' // 8ca2 #4739 - '55C' // 8ca3 #1432 - '11W' // 8ca4 #308 - 'aA' // 8ca5-8ca6 - '168W' // 8ca7 #4390 - '229C' // 8ca8 #5956 - '199H' // 8ca9 #5181 - '166G' // 8caa #4322 - '168P' // 8cab #4383 - '229Q' // 8cac #5970 - '11W' // 8cad #308 - '1K' // 8cae #36 - '89I' // 8caf #2322 - '89H' // 8cb0 #2321 - 'A' // 8cb1 - '11W' // 8cb2 #308 - '63P' // 8cb3 #1653 - '215Z' // 8cb4 #5615 - 'A' // 8cb5 - '63P' // 8cb6 #1653 - '237U' // 8cb7 #6182 - '176O' // 8cb8 #4590 - '11V' // 8cb9 #307 - '11W' // 8cba #308 - '241R' // 8cbb #6283 - '227P' // 8cbc #5917 - '44I' // 8cbd #1152 - 'A' // 8cbe - '201O' // 8cbf #5240 - '183X' // 8cc0 #4781 - 'a44I' // 8cc1-8cc2 #1152 - '149G' // 8cc3 #3880 - '44I' // 8cc4 #1152 - '11W' // 8cc5 #308 - '1K' // 8cc6 #36 - '241Z' // 8cc7 #6291 - '156K' // 8cc8 #4066 - '1K' // 8cc9 #36 - '174I' // 8cca #4532 - '1K' // 8ccb #36 - 'A' // 8ccc - '89B' // 8ccd #2315 - '1K' // 8cce #36 - '11W' // 8ccf #308 - 'A' // 8cd0 - '89G' // 8cd1 #2320 - '11W' // 8cd2 #308 - '201A' // 8cd3 #5226 - '55C' // 8cd4 #1432 - '11W' // 8cd5 #308 - '28G' // 8cd6 #734 - 'aA' // 8cd7-8cd8 - '11W' // 8cd9 #308 - '89A' // 8cda #2314 - '88W' // 8cdb #2310 - '153C' // 8cdc #3980 - '1K' // 8cdd #36 - '221L' // 8cde #5757 - 'A' // 8cdf - '174B' // 8ce0 #4525 - '13A' // 8ce1 #338 - '189V' // 8ce2 #4935 - '231Z' // 8ce3 #6031 - '144N' // 8ce4 #3757 - 'A' // 8ce5 - '163E' // 8ce6 #4242 - '4R' // 8ce7 #121 - '16K' // 8ce8 #426 - '24U' // 8ce9 #644 - '238F' // 8cea #6193 - '19Z' // 8ceb #519 - '163H' // 8cec #4245 - '166R' // 8ced #4333 - 'A' // 8cee - '1K' // 8cef #36 - '13A' // 8cf0 #338 - '1K' // 8cf1 #36 - '32Z' // 8cf2 #857 - 'A' // 8cf3 - '67K' // 8cf4 #1752 - '1K' // 8cf5 #36 - 'A' // 8cf6 - '32Z' // 8cf7 #857 - '16K' // 8cf8 #426 - 'A' // 8cf9 - '67F' // 8cfa #1747 - '88Q' // 8cfb #2304 - '237B' // 8cfc #6163 - '216Z' // 8cfd #5641 - '13A' // 8cfe #338 - '1K' // 8cff #36 - 'A' // 8d00 - '1K' // 8d01 #36 - 'A' // 8d02 - '32Z' // 8d03 #857 - '28F' // 8d04 #733 - '125R' // 8d05 #3267 - 'A' // 8d06 - '28F' // 8d07 #733 - '203Y' // 8d08 #5302 - '1K' // 8d09 #36 - '67K' // 8d0a #1752 - '13A' // 8d0b #338 - '24U' // 8d0c #644 - '28F' // 8d0d #733 - '1K' // 8d0e #36 - '185B' // 8d0f #4811 - '13A' // 8d10 #338 - '24U' // 8d11 #644 - '13A' // 8d12 #338 - '28F' // 8d13 #733 - '16K' // 8d14 #426 - 'A' // 8d15 - '127D' // 8d16 #3305 - '13A' // 8d17 #338 - '24U' // 8d18 #644 - 'aA' // 8d19-8d1a - '16K' // 8d1b #426 - '13A' // 8d1c #338 - '88S' // 8d1d #2306 - '3H' // 8d1e #85 - '2C' // 8d1f #54 - '4R' // 8d20 #121 - '1R' // 8d21 #43 - '2D' // 8d22 #55 - '1Z' // 8d23 #51 - '1R' // 8d24 #43 - '3Y' // 8d25 #102 - 'a2D' // 8d26-8d27 #55 - '3Q' // 8d28 #94 - '2K' // 8d29 #62 - '2Y' // 8d2a #76 - '4C' // 8d2b #106 - '3W' // 8d2c #100 - '1Z' // 8d2d #51 - '2W' // 8d2e #74 - '4C' // 8d2f #106 - '4R' // 8d30 #121 - '3H' // 8d31 #85 - 'a4R' // 8d32-8d33 #121 - '1Z' // 8d34 #51 - '2C' // 8d35 #54 - '4R' // 8d36 #121 - '4C' // 8d37 #106 - '3Y' // 8d38 #102 - '3Q' // 8d39 #94 - '4C' // 8d3a #106 - '4R' // 8d3b #121 - '2Y' // 8d3c #76 - '4R' // 8d3d #121 - '2L' // 8d3e #63 - '3G' // 8d3f #84 - '4R' // 8d40 #121 - '2R' // 8d41 #69 - 'a4R' // 8d42-8d43 #121 - '7K' // 8d44 #192 - 'a4R' // 8d45-8d46 #121 - 'A' // 8d47 - 'b4R' // 8d48-8d4a #121 - 'a2L' // 8d4b-8d4c #63 - '4R' // 8d4d #121 - '3W' // 8d4e #100 - '3I' // 8d4f #86 - '2K' // 8d50 #62 - '4R' // 8d51 #121 - 'A' // 8d52 - '4R' // 8d53 #121 - '4C' // 8d54 #106 - '4R' // 8d55 #121 - '1R' // 8d56 #43 - 'A' // 8d57 - '2W' // 8d58 #74 - '4R' // 8d59 #121 - '3N' // 8d5a #91 - '2C' // 8d5b #54 - 'a4R' // 8d5c-8d5d #121 - '2C' // 8d5e #54 - '4R' // 8d5f #121 - '1R' // 8d60 #43 - '4R' // 8d61 #121 - '3I' // 8d62 #86 - '2K' // 8d63 #62 - '184B' // 8d64 #4785 - '1K' // 8d65 #36 - '88U' // 8d66 #2308 - '16K' // 8d67 #426 - '19Z' // 8d68 #519 - '13A' // 8d69 #338 - '4R' // 8d6a #121 - '170I' // 8d6b #4428 - '13A' // 8d6c #338 - '16K' // 8d6d #426 - '13A' // 8d6e #338 - 'A' // 8d6f - '228M' // 8d70 #5940 - '248K' // 8d71 #6458 - 'A' // 8d72 - '28F' // 8d73 #733 - '180M' // 8d74 #4692 - '3N' // 8d75 #91 - '88R' // 8d76 #2305 - '241S' // 8d77 #6284 - 'aA' // 8d78-8d79 - '24U' // 8d7a #644 - '19Z' // 8d7b #519 - 'A' // 8d7c - '19Z' // 8d7d #519 - 'A' // 8d7e - '1K' // 8d7f #36 - 'A' // 8d80 - '170J' // 8d81 #4429 - '32Z' // 8d82 #857 - 'A' // 8d83 - '16K' // 8d84 #426 - '242A' // 8d85 #6292 - 'aA' // 8d86-8d87 - '1K' // 8d88 #36 - 'A' // 8d89 - '223H' // 8d8a #5805 - '1R' // 8d8b #43 - 'A' // 8d8c - '1K' // 8d8d #36 - 'aA' // 8d8e-8d8f - 'a16K' // 8d90-8d91 #426 - 'aA' // 8d92-8d93 - '24V' // 8d94 #645 - '67F' // 8d95 #1747 - '19Z' // 8d96 #519 - 'aA' // 8d97-8d98 - '177R' // 8d99 #4619 - 'aA' // 8d9a-8d9b - '19Z' // 8d9c #519 - 'A' // 8d9d - '1K' // 8d9e #36 - '156L' // 8d9f #4067 - '1K' // 8da0 #36 - 'aA' // 8da1-8da2 - '228L' // 8da3 #5939 - 'aA' // 8da4-8da5 - '32Z' // 8da6 #857 - 'A' // 8da7 - '185A' // 8da8 #4810 - '24U' // 8da9 #644 - 'A' // 8daa - '13A' // 8dab #338 - '1K' // 8dac #36 - 'aA' // 8dad-8dae - '16K' // 8daf #426 - 'A' // 8db0 - '4R' // 8db1 #121 - '13A' // 8db2 #338 - '230U' // 8db3 #6000 - '66P' // 8db4 #1731 - '16K' // 8db5 #426 - 'A' // 8db6 - '13A' // 8db7 #338 - '4R' // 8db8 #121 - '1K' // 8db9 #36 - '28F' // 8dba #733 - '1K' // 8dbb #36 - '16K' // 8dbc #426 - 'A' // 8dbd - '121U' // 8dbe #3166 - '24V' // 8dbf #645 - '44F' // 8dc0 #1149 - 'A' // 8dc1 - '11U' // 8dc2 #306 - '88Y' // 8dc3 #2312 - '4R' // 8dc4 #121 - '1N' // 8dc5 #39 - '28E' // 8dc6 #732 - 'a1N' // 8dc7-8dc8 #39 - 'A' // 8dc9 - '1N' // 8dca #39 - '28E' // 8dcb #732 - '184Y' // 8dcc #4808 - 'A' // 8dcd - '11U' // 8dce #306 - '28E' // 8dcf #732 - '19Z' // 8dd0 #519 - '214I' // 8dd1 #5572 - 'aA' // 8dd2-8dd3 - '44F' // 8dd4 #1149 - '1N' // 8dd5 #39 - 'a11U' // 8dd6-8dd7 #306 - 'A' // 8dd8 - '1N' // 8dd9 #39 - '11U' // 8dda #306 - '28E' // 8ddb #732 - 'A' // 8ddc - '204L' // 8ddd #5315 - '4R' // 8dde #121 - '224P' // 8ddf #5839 - 'A' // 8de0 - '198L' // 8de1 #5159 - 'A' // 8de2 - '28E' // 8de3 #732 - '11U' // 8de4 #306 - '1N' // 8de5 #39 - 'A' // 8de6 - '1N' // 8de7 #39 - '208K' // 8de8 #5418 - '24V' // 8de9 #645 - '149Z' // 8dea #3899 - 'a11U' // 8deb-8dec #306 - 'aA' // 8ded-8dee - '241W' // 8def #6288 - '69X' // 8df0 #1817 - '11U' // 8df1 #306 - '1N' // 8df2 #39 - '218M' // 8df3 #5680 - '1N' // 8df4 #39 - '255C' // 8df5 #6632 - 'c4R' // 8df6-8df9 #121 - '24V' // 8dfa #645 - '4R' // 8dfb #121 - 'a11U' // 8dfc-8dfd #306 - 'A' // 8dfe - '1N' // 8dff #39 - 'A' // 8e00 - '44F' // 8e01 #1149 - 'aA' // 8e02-8e03 - '1N' // 8e04 #39 - '11U' // 8e05 #306 - '1N' // 8e06 #39 - '4R' // 8e07 #121 - '1N' // 8e08 #39 - '11U' // 8e09 #306 - '88V' // 8e0a #2309 - '1N' // 8e0b #39 - '69X' // 8e0c #1817 - 'A' // 8e0d - '24V' // 8e0e #645 - '190D' // 8e0f #4943 - '156I' // 8e10 #4064 - '1N' // 8e11 #39 - 'aA' // 8e12-8e13 - '11U' // 8e14 #306 - 'A' // 8e15 - '44H' // 8e16 #1151 - 'A' // 8e17 - '19Z' // 8e18 #519 - 'cA' // 8e19-8e1c - '11U' // 8e1d #306 - '28E' // 8e1e #732 - '11U' // 8e1f #306 - 'a44H' // 8e20-8e21 #1151 - '184Z' // 8e22 #4809 - '11U' // 8e23 #306 - 'aA' // 8e24-8e25 - '11U' // 8e26 #306 - '44H' // 8e27 #1151 - '19Z' // 8e28 #519 - '177C' // 8e29 #4604 - '88T' // 8e2a #2307 - '24V' // 8e2b #645 - '4R' // 8e2c #121 - 'a24V' // 8e2d-8e2e #645 - '4R' // 8e2f #121 - '54W' // 8e30 #1426 - '37X' // 8e31 #985 - 'A' // 8e32 - '1N' // 8e33 #39 - '121T' // 8e34 #3165 - '54W' // 8e35 #1426 - 'b1N' // 8e36-8e38 #39 - '37X' // 8e39 #985 - '88X' // 8e3a #2311 - 'A' // 8e3b - '88Z' // 8e3c #2313 - '37X' // 8e3d #985 - 'aA' // 8e3e-8e3f - '37X' // 8e40 #985 - '54X' // 8e41 #1427 - '44G' // 8e42 #1150 - 'A' // 8e43 - '127C' // 8e44 #3304 - 'A' // 8e45 - '24U' // 8e46 #644 - '44G' // 8e47 #1150 - '156J' // 8e48 #4065 - 'a44G' // 8e49-8e4a #1150 - '54X' // 8e4b #1427 - 'b88P' // 8e4c-8e4e #2303 - '22K' // 8e4f #582 - '13W' // 8e50 #360 - 'a8P' // 8e51-8e52 #223 - '37W' // 8e53 #984 - 'a5Q' // 8e54-8e55 #146 - 'bA' // 8e56-8e58 - '12Z' // 8e59 #337 - '37W' // 8e5a #984 - '5Q' // 8e5b #146 - 'b1N' // 8e5c-8e5e #39 - '177Q' // 8e5f #4618 - '12Z' // 8e60 #337 - '1N' // 8e61 #39 - 'a5Q' // 8e62-8e63 #146 - '216Y' // 8e64 #5640 - 'A' // 8e65 - '126I' // 8e66 #3284 - '19Y' // 8e67 #518 - '32X' // 8e68 #855 - '13W' // 8e69 #360 - 'aA' // 8e6a-8e6b - '13W' // 8e6c #360 - '88B' // 8e6d #2289 - 'A' // 8e6e - '5Q' // 8e6f #146 - '49O' // 8e70 #1288 - '22K' // 8e71 #582 - '138L' // 8e72 #3599 - 'A' // 8e73 - '88F' // 8e74 #2293 - '22K' // 8e75 #582 - '12Z' // 8e76 #337 - '22K' // 8e77 #582 - 'A' // 8e78 - '1N' // 8e79 #39 - 'b13W' // 8e7a-8e7c #360 - 'A' // 8e7d - '32X' // 8e7e #855 - '8P' // 8e7f #223 - '32X' // 8e80 #855 - '144M' // 8e81 #3756 - '5Q' // 8e82 #146 - '1N' // 8e83 #39 - 'a13W' // 8e84-8e85 #360 - 'A' // 8e86 - '88C' // 8e87 #2290 - 'A' // 8e88 - '13W' // 8e89 #360 - '88D' // 8e8a #2291 - '13W' // 8e8b #360 - 'A' // 8e8c - '183R' // 8e8d #4775 - 'A' // 8e8e - '8P' // 8e8f #223 - '13W' // 8e90 #360 - 'b5Q' // 8e91-8e93 #146 - '13W' // 8e94 #360 - '5Q' // 8e95 #146 - 'aA' // 8e96-8e97 - '1N' // 8e98 #39 - '49O' // 8e99 #1288 - '5Q' // 8e9a #146 - '1N' // 8e9b #39 - '8P' // 8e9c #223 - '5Q' // 8e9d #146 - '13W' // 8e9e #360 - 'aA' // 8e9f-8ea0 - '5Q' // 8ea1 #146 - '1N' // 8ea2 #39 - '19Y' // 8ea3 #518 - 'A' // 8ea4 - 'a19Y' // 8ea5-8ea6 #518 - '22K' // 8ea7 #582 - 'A' // 8ea8 - '1N' // 8ea9 #39 - '12Z' // 8eaa #337 - '242T' // 8eab #6311 - '12Z' // 8eac #337 - '5Q' // 8ead #146 - '1N' // 8eae #39 - '248J' // 8eaf #6457 - '13W' // 8eb0 #360 - '1N' // 8eb1 #39 - '169U' // 8eb2 #4414 - '1N' // 8eb3 #39 - 'A' // 8eb4 - '1N' // 8eb5 #39 - '88A' // 8eb6 #2288 - 'aA' // 8eb7-8eb8 - '19Y' // 8eb9 #518 - '163D' // 8eba #4241 - '1N' // 8ebb #39 - '19Y' // 8ebc #518 - 'A' // 8ebd - '1N' // 8ebe #39 - 'A' // 8ebf - '121S' // 8ec0 #3164 - '1N' // 8ec1 #39 - 'A' // 8ec2 - '22K' // 8ec3 #582 - 'a1N' // 8ec4-8ec5 #39 - '49O' // 8ec6 #1288 - 'a1N' // 8ec7-8ec8 #39 - 'A' // 8ec9 - '243Q' // 8eca #6334 - '12Z' // 8ecb #337 - '181I' // 8ecc #4714 - '220A' // 8ecd #5720 - '88K' // 8ece #2298 - '5Q' // 8ecf #146 - 'A' // 8ed0 - '5Q' // 8ed1 #146 - '182O' // 8ed2 #4746 - 'A' // 8ed3 - '5Q' // 8ed4 #146 - 'aA' // 8ed5-8ed6 - '19Y' // 8ed7 #518 - 'aA' // 8ed8-8ed9 - '37W' // 8eda #984 - '5Q' // 8edb #146 - '1N' // 8edc #39 - 'aA' // 8edd-8ede - '218Y' // 8edf #5692 - 'aA' // 8ee0-8ee1 - '88I' // 8ee2 #2296 - '1N' // 8ee3 #39 - '32X' // 8ee4 #855 - 'bA' // 8ee5-8ee7 - '5Q' // 8ee8 #146 - 'aA' // 8ee9-8eea - '12Z' // 8eeb #337 - 'A' // 8eec - '22K' // 8eed #582 - '1N' // 8eee #39 - 'A' // 8eef - 'a1N' // 8ef0-8ef1 #39 - '19Y' // 8ef2 #518 - 'cA' // 8ef3-8ef6 - '1N' // 8ef7 #39 - '175D' // 8ef8 #4553 - 'a5Q' // 8ef9-8efa #146 - '12Z' // 8efb #337 - '13W' // 8efc #360 - '259K' // 8efd #6744 - '12Z' // 8efe #337 - 'A' // 8eff - '1N' // 8f00 #39 - 'A' // 8f01 - '1N' // 8f02 #39 - '228R' // 8f03 #5945 - 'A' // 8f04 - '32Y' // 8f05 #856 - 'A' // 8f06 - 'a5Q' // 8f07-8f08 #146 - '238R' // 8f09 #6205 - '5Q' // 8f0a #146 - '37W' // 8f0b #984 - '251Z' // 8f0c #6551 - 'aA' // 8f0d-8f0e - 'a1N' // 8f0f-8f10 #39 - 'A' // 8f11 - 'a12Z' // 8f12-8f13 #337 - '195Z' // 8f14 #5095 - '68H' // 8f15 #1775 - '1N' // 8f16 #39 - '5Q' // 8f17 #146 - '1N' // 8f18 #39 - '5Q' // 8f19 #146 - '19Y' // 8f1a #518 - '170H' // 8f1b #4427 - '32Y' // 8f1c #856 - '190N' // 8f1d #4953 - '32Y' // 8f1e #856 - '12Z' // 8f1f #337 - 'a1N' // 8f20-8f21 #39 - 'A' // 8f22 - '1N' // 8f23 #39 - 'A' // 8f24 - '5Q' // 8f25 #146 - '32Y' // 8f26 #856 - '69W' // 8f27 #1816 - '1N' // 8f28 #39 - '190W' // 8f29 #4962 - '212K' // 8f2a #5522 - 'a1N' // 8f2b-8f2c #39 - '5Q' // 8f2d #146 - '1N' // 8f2e #39 - '68H' // 8f2f #1775 - '19Y' // 8f30 #518 - 'aA' // 8f31-8f32 - '32Y' // 8f33 #856 - 'a1N' // 8f34-8f35 #39 - '13W' // 8f36 #360 - '1N' // 8f37 #39 - '219Y' // 8f38 #5718 - '69W' // 8f39 #1816 - '1N' // 8f3a #39 - '127B' // 8f3b #3303 - '32X' // 8f3c #855 - 'A' // 8f3d - '12Z' // 8f3e #337 - '129V' // 8f3f #3375 - 'b5Q' // 8f40-8f42 #146 - '1N' // 8f43 #39 - '147Z' // 8f44 #3847 - '12Z' // 8f45 #337 - 'a5Q' // 8f46-8f47 #146 - 'A' // 8f48 - '239N' // 8f49 #6227 - '22K' // 8f4a #582 - 'A' // 8f4b - '1N' // 8f4c #39 - '12Z' // 8f4d #337 - '138K' // 8f4e #3598 - '1N' // 8f4f #39 - 'A' // 8f50 - '1N' // 8f51 #39 - 'a2G' // 8f52-8f53 #58 - 'a44E' // 8f54-8f55 #1148 - 'A' // 8f56 - 'a2G' // 8f57-8f58 #58 - 'bA' // 8f59-8f5b - '37V' // 8f5c #983 - '54U' // 8f5d #1424 - '2G' // 8f5e #58 - '173B' // 8f5f #4499 - 'A' // 8f60 - '44E' // 8f61 #1148 - '54U' // 8f62 #1424 - '2G' // 8f63 #58 - '44E' // 8f64 #1148 - '2G' // 8f65 #58 - '144L' // 8f66 #3755 - '88L' // 8f67 #2299 - '1R' // 8f68 #43 - '2L' // 8f69 #63 - 'A' // 8f6a - '8P' // 8f6b #223 - '3Q' // 8f6c #94 - '8P' // 8f6d #223 - '88M' // 8f6e #2300 - '2D' // 8f6f #55 - '2Y' // 8f70 #76 - 'b8P' // 8f71-8f73 #223 - '2L' // 8f74 #63 - 'a8P' // 8f75-8f76 #223 - 'A' // 8f77 - 'b8P' // 8f78-8f7a #223 - '2D' // 8f7b #55 - '8P' // 8f7c #223 - '3Q' // 8f7d #94 - '8P' // 8f7e #223 - '2K' // 8f7f #62 - 'A' // 8f80 - 'a8P' // 8f81-8f82 #223 - '2D' // 8f83 #55 - '8P' // 8f84 #223 - '3N' // 8f85 #91 - '4C' // 8f86 #106 - '8P' // 8f87 #223 - '4C' // 8f88 #106 - '1R' // 8f89 #43 - 'a8P' // 8f8a-8f8b #223 - 'A' // 8f8c - 'b8P' // 8f8d-8f8f #223 - '3H' // 8f90 #85 - '2D' // 8f91 #55 - 'A' // 8f92 - '88N' // 8f93 #2301 - 'a8P' // 8f94-8f95 #223 - '3H' // 8f96 #85 - 'c8P' // 8f97-8f9a #223 - '197W' // 8f9b #5144 - '138I' // 8f9c #3596 - '2G' // 8f9d #58 - '256W' // 8f9e #6678 - '87X' // 8f9f #2285 - '37V' // 8fa0 #983 - 'a2G' // 8fa1-8fa2 #58 - '194I' // 8fa3 #5052 - '2G' // 8fa4 #58 - '37V' // 8fa5 #983 - '224O' // 8fa6 #5838 - '87Z' // 8fa7 #2287 - '170G' // 8fa8 #4426 - '2Y' // 8fa9 #76 - 'A' // 8faa - '2W' // 8fab #74 - 'A' // 8fac - '184V' // 8fad #4805 - '54V' // 8fae #1425 - '156H' // 8faf #4063 - '166X' // 8fb0 #4339 - '181O' // 8fb1 #4720 - '210O' // 8fb2 #5474 - '88O' // 8fb3 #2302 - '2G' // 8fb4 #58 - 'a54V' // 8fb5-8fb6 #1425 - '87W' // 8fb7 #2284 - '37V' // 8fb8 #983 - '131M' // 8fb9 #3418 - '88H' // 8fba #2295 - '88G' // 8fbb #2294 - '88J' // 8fbc #2297 - '1R' // 8fbd #43 - '132H' // 8fbe #3439 - '88E' // 8fbf #2292 - '2G' // 8fc0 #58 - '87Y' // 8fc1 #2286 - '87V' // 8fc2 #2283 - 'A' // 8fc3 - '130Q' // 8fc4 #3396 - '181K' // 8fc5 #4716 - '22J' // 8fc6 #581 - '7K' // 8fc7 #192 - '248I' // 8fc8 #6456 - 'A' // 8fc9 - '28C' // 8fca #730 - '69V' // 8fcb #1815 - '32W' // 8fcc #854 - '22I' // 8fcd #580 - '228K' // 8fce #5938 - '44B' // 8fcf #1145 - '138J' // 8fd0 #3597 - '239E' // 8fd1 #6218 - '22J' // 8fd2 #581 - '22I' // 8fd3 #580 - '216Q' // 8fd4 #5632 - '22I' // 8fd5 #580 - 'A' // 8fd6 - '8P' // 8fd7 #223 - 'a7K' // 8fd8-8fd9 #192 - '87N' // 8fda #2275 - '7K' // 8fdb #192 - '2D' // 8fdc #55 - '2C' // 8fdd #54 - '1Z' // 8fde #51 - '2L' // 8fdf #63 - '2G' // 8fe0 #58 - 'A' // 8fe1 - 'c22I' // 8fe2-8fe5 #580 - '141P' // 8fe6 #3681 - 'A' // 8fe7 - '22I' // 8fe8 #580 - '69V' // 8fe9 #1815 - '199N' // 8fea #5187 - '198G' // 8feb #5154 - 'A' // 8fec - '54S' // 8fed #1422 - '22I' // 8fee #580 - '2G' // 8fef #58 - '215B' // 8ff0 #5591 - '2G' // 8ff1 #58 - '260Q' // 8ff2 #6776 - '8P' // 8ff3 #223 - '192D' // 8ff4 #4995 - '22J' // 8ff5 #581 - '2G' // 8ff6 #58 - '220O' // 8ff7 #5734 - '22I' // 8ff8 #580 - '126Z' // 8ff9 #3301 - '22I' // 8ffa #580 - '22J' // 8ffb #581 - '32W' // 8ffc #854 - '231J' // 8ffd #6015 - '22J' // 8ffe #581 - 'A' // 8fff - '220Y' // 9000 #5744 - '238Q' // 9001 #6204 - '87L' // 9002 #2273 - '191B' // 9003 #4967 - '4Y' // 9004 #128 - '63A' // 9005 #1638 - '191E' // 9006 #4970 - 'A' // 9007 - '87M' // 9008 #2274 - '3Q' // 9009 #94 - '2Y' // 900a #76 - '12Y' // 900b #336 - '2G' // 900c #58 - '144K' // 900d #3754 - '2G' // 900e #58 - '227O' // 900f #5916 - '194Q' // 9010 #5060 - '12Y' // 9011 #336 - '87U' // 9012 #2282 - '2G' // 9013 #58 - '205W' // 9014 #5352 - '64E' // 9015 #1668 - '4Y' // 9016 #128 - '159K' // 9017 #4144 - '2G' // 9018 #58 - '240Y' // 9019 #6264 - '35U' // 901a #930 - '192E' // 901b #4996 - 'A' // 901c - '166W' // 901d #4338 - '12Y' // 901e #336 - '235Y' // 901f #6134 - '235M' // 9020 #6122 - '12Y' // 9021 #336 - '181H' // 9022 #4713 - '244T' // 9023 #6363 - '32W' // 9024 #854 - 'A' // 9025 - '14X' // 9026 #387 - 'c2G' // 9027-902a #58 - 'A' // 902b - '2G' // 902c #58 - '4Y' // 902d #128 - '154S' // 902e #4022 - '4Y' // 902f #128 - 'A' // 9030 - '230T' // 9031 #5999 - '242D' // 9032 #6295 - '28C' // 9033 #730 - '22J' // 9034 #581 - '12Y' // 9035 #336 - '4Y' // 9036 #128 - '28C' // 9037 #730 - '182D' // 9038 #4735 - '69U' // 9039 #1814 - '14X' // 903a #387 - '2R' // 903b #69 - '184W' // 903c #4806 - 'A' // 903d - '177P' // 903e #4617 - '2G' // 903f #58 - 'A' // 9040 - '12Y' // 9041 #336 - '143F' // 9042 #3723 - '2G' // 9043 #58 - '4Y' // 9044 #128 - '257L' // 9045 #6693 - '87S' // 9046 #2280 - '219B' // 9047 #5695 - 'A' // 9048 - '2G' // 9049 #58 - '241P' // 904a #6281 - '243I' // 904b #6326 - '28C' // 904c #730 - '187M' // 904d #4874 - '242K' // 904e #6302 - '4Y' // 904f #128 - 'a12Y' // 9050-9051 #336 - '4Y' // 9052 #128 - '244B' // 9053 #6345 - '237S' // 9054 #6180 - '206J' // 9055 #5365 - '28C' // 9056 #730 - '3I' // 9057 #86 - '4Y' // 9058 #128 - '187D' // 9059 #4865 - 'A' // 905a - '4Y' // 905b #128 - '49D' // 905c #1277 - '12Y' // 905d #336 - '67E' // 905e #1746 - 'A' // 905f - '222K' // 9060 #5782 - '87P' // 9061 #2277 - '4Y' // 9062 #128 - '149I' // 9063 #3882 - '44B' // 9064 #1145 - '254S' // 9065 #6622 - 'a2G' // 9066-9067 #58 - '4Y' // 9068 #128 - '230L' // 9069 #5991 - 'aA' // 906a-906b - '28C' // 906c #730 - '209E' // 906d #5438 - '173W' // 906e #4520 - '12Y' // 906f #336 - '2G' // 9070 #58 - 'A' // 9071 - '49D' // 9072 #1277 - 'A' // 9073 - '4Y' // 9074 #128 - '187H' // 9075 #4869 - '127A' // 9076 #3302 - '174H' // 9077 #4531 - '244D' // 9078 #6347 - '4Y' // 9079 #128 - '203X' // 907a #5301 - 'A' // 907b - '141U' // 907c #3686 - '87Q' // 907d #2278 - 'A' // 907e - '210P' // 907f #5475 - '192C' // 9080 #4994 - '49D' // 9081 #1277 - '63A' // 9082 #1638 - '12Y' // 9083 #336 - '241C' // 9084 #6268 - '4Y' // 9085 #128 - 'A' // 9086 - 'a12Y' // 9087-9088 #336 - '252N' // 9089 #6565 - '226L' // 908a #5887 - '4Y' // 908b #128 - '2G' // 908c #58 - 'A' // 908d - '2G' // 908e #58 - '66F' // 908f #1721 - '22J' // 9090 #581 - '124T' // 9091 #3243 - 'A' // 9092 - '2L' // 9093 #63 - 'A' // 9094 - '12Y' // 9095 #336 - 'A' // 9096 - '4Y' // 9097 #128 - '2G' // 9098 #58 - '12Y' // 9099 #336 - '14X' // 909a #387 - '4Y' // 909b #128 - 'A' // 909c - '14X' // 909d #387 - '32W' // 909e #854 - 'A' // 909f - 'a4Y' // 90a0-90a1 #128 - '87J' // 90a2 #2271 - '234B' // 90a3 #6085 - 'A' // 90a4 - '2G' // 90a5 #58 - '209U' // 90a6 #5454 - 'A' // 90a7 - '64E' // 90a8 #1668 - '14X' // 90a9 #387 - '176E' // 90aa #4580 - 'A' // 90ab - '14X' // 90ac #387 - 'A' // 90ad - '131O' // 90ae #3420 - '54S' // 90af #1422 - '12Y' // 90b0 #336 - '170F' // 90b1 #4425 - 'b4Y' // 90b2-90b4 #128 - '132G' // 90b5 #3438 - '4Y' // 90b6 #128 - 'A' // 90b7 - '130R' // 90b8 #3397 - '3X' // 90b9 #101 - '14X' // 90ba #387 - '87T' // 90bb #2281 - 'A' // 90bc - 'a4Y' // 90bd-90be #128 - 'aA' // 90bf-90c0 - '173K' // 90c1 #4508 - 'A' // 90c2 - 'b4Y' // 90c3-90c5 #128 - 'A' // 90c6 - '4Y' // 90c7 #128 - '69U' // 90c8 #1814 - '2G' // 90c9 #58 - '154C' // 90ca #4006 - 'A' // 90cb - '2G' // 90cc #58 - 'A' // 90cd - '191O' // 90ce #4980 - 'a14X' // 90cf-90d0 #387 - '3Y' // 90d1 #102 - '2G' // 90d2 #58 - '14X' // 90d3 #387 - '32W' // 90d4 #854 - '4Y' // 90d5 #128 - 'A' // 90d6 - '4Y' // 90d7 #128 - 'a2G' // 90d8-90d9 #58 - '14X' // 90da #387 - 'a4Y' // 90db-90dc #128 - '87O' // 90dd #2276 - '69T' // 90de #1813 - '22J' // 90df #581 - 'A' // 90e0 - '137A' // 90e1 #3562 - '10Q' // 90e2 #276 - '44D' // 90e3 #1147 - '10Q' // 90e4 #276 - '2G' // 90e5 #58 - 'a14X' // 90e6-90e7 #387 - '35U' // 90e8 #930 - 'A' // 90e9 - '54T' // 90ea #1423 - '10Q' // 90eb #276 - 'A' // 90ec - '187R' // 90ed #4879 - 'A' // 90ee - '10Q' // 90ef #276 - '2G' // 90f0 #58 - 'A' // 90f1 - '2G' // 90f2 #58 - 'A' // 90f3 - '10Q' // 90f4 #276 - '219X' // 90f5 #5717 - '2G' // 90f6 #58 - '256M' // 90f7 #6668 - '3G' // 90f8 #84 - 'bA' // 90f9-90fb - '44D' // 90fc #1147 - '243E' // 90fd #6322 - 'b10Q' // 90fe-9100 #276 - 'A' // 9101 - '87K' // 9102 #2272 - 'A' // 9103 - '10Q' // 9104 #276 - '2G' // 9105 #58 - '28D' // 9106 #731 - 'A' // 9107 - '2G' // 9108 #58 - '214G' // 9109 #5570 - 'aA' // 910a-910b - '14X' // 910c #387 - '2G' // 910d #58 - 'aA' // 910e-910f - '2G' // 9110 #58 - 'A' // 9111 - '54R' // 9112 #1421 - 'A' // 9113 - '10Q' // 9114 #276 - '69T' // 9115 #1813 - 'a28D' // 9116-9117 #731 - '10Q' // 9118 #276 - '87I' // 9119 #2270 - '28D' // 911a #731 - 'A' // 911b - '10Q' // 911c #276 - 'A' // 911d - '10Q' // 911e #276 - 'A' // 911f - '10Q' // 9120 #276 - 'A' // 9121 - 'a10Q' // 9122-9123 #276 - 'A' // 9124 - '2G' // 9125 #58 - 'A' // 9126 - '66F' // 9127 #1721 - 'A' // 9128 - '28D' // 9129 #731 - 'A' // 912a - '44D' // 912b #1147 - 'A' // 912c - '67E' // 912d #1746 - '2G' // 912e #58 - '10Q' // 912f #276 - '184X' // 9130 #4807 - '10Q' // 9131 #276 - '54R' // 9132 #1421 - 'A' // 9133 - '28D' // 9134 #731 - 'A' // 9135 - '28D' // 9136 #731 - '2G' // 9137 #58 - 'A' // 9138 - 'a10Q' // 9139-913a #276 - 'A' // 913b - 'a2G' // 913c-913d #58 - 'dA' // 913e-9142 - '32V' // 9143 #853 - 'aA' // 9144-9145 - '32V' // 9146 #853 - '2T' // 9147 #71 - '44C' // 9148 #1146 - 'a87H' // 9149-914a #2269 - '121R' // 914b #3163 - '144J' // 914c #3753 - '237T' // 914d #6181 - '87R' // 914e #2279 - '32V' // 914f #853 - '54T' // 9150 #1423 - '44B' // 9151 #1145 - '222Q' // 9152 #5788 - '2T' // 9153 #71 - '255G' // 9154 #6636 - 'A' // 9155 - '44C' // 9156 #1146 - '32V' // 9157 #853 - '2T' // 9158 #71 - '44C' // 9159 #1146 - '32V' // 915a #853 - '2T' // 915b #71 - '24T' // 915c #643 - '14X' // 915d #387 - '87F' // 915e #2267 - 'aA' // 915f-9160 - '86X' // 9161 #2259 - '87C' // 9162 #2264 - 'a9Q' // 9163-9164 #250 - '170E' // 9165 #4424 - 'A' // 9166 - '13V' // 9167 #359 - 'A' // 9168 - '64D' // 9169 #1667 - '152Q' // 916a #3968 - 'A' // 916b - '154M' // 916c #4016 - '2T' // 916d #71 - '54Q' // 916e #1420 - '126J' // 916f #3285 - '32U' // 9170 #852 - '2L' // 9171 #63 - '9Q' // 9172 #250 - '2T' // 9173 #71 - '9Q' // 9174 #250 - '160T' // 9175 #4179 - '87G' // 9176 #2268 - '197C' // 9177 #5124 - '203Q' // 9178 #5294 - 'a9Q' // 9179-917a #250 - '2T' // 917b #71 - '24T' // 917c #643 - 'a50J' // 917d-917e #1309 - '3H' // 917f #85 - 'A' // 9180 - '7L' // 9181 #193 - '9Q' // 9182 #250 - '132F' // 9183 #3437 - '22H' // 9184 #579 - '9Q' // 9185 #250 - '7L' // 9186 #193 - '65Y' // 9187 #1714 - 'A' // 9188 - '177O' // 9189 #4616 - '2T' // 918a #71 - '65Y' // 918b #1714 - '32U' // 918c #852 - '86Y' // 918d #2260 - '7L' // 918e #193 - 'A' // 918f - '86Z' // 9190 #2261 - '9Q' // 9191 #250 - '208Y' // 9192 #5432 - 'b2T' // 9193-9195 #71 - '22H' // 9196 #579 - 'a2T' // 9197-9198 #71 - 'A' // 9199 - '32U' // 919a #852 - '54Q' // 919b #1420 - '173D' // 919c #4501 - 'A' // 919d - '7L' // 919e #193 - 'aA' // 919f-91a0 - 'a9Q' // 91a1-91a2 #250 - '32U' // 91a3 #852 - '254B' // 91a4 #6605 - 'A' // 91a5 - '2T' // 91a6 #71 - '22H' // 91a7 #579 - '35Y' // 91a8 #934 - '24T' // 91a9 #643 - '9Q' // 91aa #250 - '224N' // 91ab #5837 - '184U' // 91ac #4804 - '35Y' // 91ad #934 - 'a37U' // 91ae-91af #982 - '7L' // 91b0 #193 - '86V' // 91b1 #2257 - '7L' // 91b2 #193 - '2T' // 91b3 #71 - 'a37U' // 91b4-91b5 #982 - '13V' // 91b6 #359 - 'A' // 91b7 - '253K' // 91b8 #6588 - 'A' // 91b9 - '138H' // 91ba #3595 - '13V' // 91bb #359 - '2T' // 91bc #71 - '7L' // 91bd #193 - '32U' // 91be #852 - '2T' // 91bf #71 - '163B' // 91c0 #4239 - '9Q' // 91c1 #250 - '2T' // 91c2 #71 - '7L' // 91c3 #193 - '13V' // 91c4 #359 - '7L' // 91c5 #193 - '9Q' // 91c6 #250 - '194P' // 91c7 #5059 - '254M' // 91c8 #6616 - '86U' // 91c9 #2256 - '3Y' // 91ca #102 - '206T' // 91cb #5375 - '228P' // 91cc #5943 - '243C' // 91cd #6320 - '224B' // 91ce #5825 - '236G' // 91cf #6142 - '64D' // 91d0 #1667 - '69B' // 91d1 #1795 - '50J' // 91d2 #1309 - 'a7L' // 91d3-91d4 #193 - '22H' // 91d5 #579 - '13V' // 91d6 #359 - '37U' // 91d7 #982 - '160C' // 91d8 #4162 - '7L' // 91d9 #193 - 'a2T' // 91da-91db #71 - '136P' // 91dc #3551 - '212P' // 91dd #5527 - '2T' // 91de #71 - '13V' // 91df #359 - 'A' // 91e0 - '2T' // 91e1 #71 - 'A' // 91e2 - '190Q' // 91e3 #4956 - '7L' // 91e4 #193 - '13V' // 91e5 #359 - '9Q' // 91e6 #250 - '87A' // 91e7 #2262 - '22H' // 91e8 #579 - '7L' // 91e9 #193 - '248H' // 91ea #6455 - 'A' // 91eb - '7L' // 91ec #193 - '9Q' // 91ed #250 - 'a2T' // 91ee-91ef #71 - '35Y' // 91f0 #934 - '7L' // 91f1 #193 - 'bA' // 91f2-91f4 - '37U' // 91f5 #982 - 'a7L' // 91f6-91f7 #193 - '22H' // 91f8 #579 - '9Q' // 91f9 #250 - '22H' // 91fa #579 - 'a2T' // 91fb-91fc #71 - '7L' // 91fd #193 - '24T' // 91fe #643 - '7L' // 91ff #193 - '9Q' // 9200 #250 - '7L' // 9201 #193 - 'A' // 9202 - '22H' // 9203 #579 - '7L' // 9204 #193 - '9Q' // 9205 #250 - '7L' // 9206 #193 - '9Q' // 9207 #250 - '22F' // 9208 #577 - 'a12X' // 9209-920a #335 - 'A' // 920b - '2T' // 920c #71 - '87B' // 920d #2263 - '12X' // 920e #335 - 'A' // 920f - '32T' // 9210 #851 - '54P' // 9211 #1419 - '248G' // 9212 #6454 - '13V' // 9213 #359 - '156G' // 9214 #4062 - '163C' // 9215 #4240 - '2T' // 9216 #71 - '54P' // 9217 #1419 - '13V' // 9218 #359 - '22G' // 9219 #578 - 'aA' // 921a-921b - '12X' // 921c #335 - '2T' // 921d #71 - '65P' // 921e #1705 - 'aA' // 921f-9220 - '22F' // 9221 #577 - 'A' // 9222 - '65B' // 9223 #1691 - 'a12X' // 9224-9225 #335 - '65B' // 9226 #1691 - '22F' // 9227 #577 - '13V' // 9228 #359 - '35Y' // 9229 #934 - '22F' // 922a #577 - '24T' // 922b #643 - '2T' // 922c #71 - '22F' // 922d #577 - '11T' // 922e #305 - '2T' // 922f #71 - '11T' // 9230 #305 - '22G' // 9231 #578 - 'A' // 9232 - '11T' // 9233 #305 - '183N' // 9234 #4771 - '12X' // 9235 #335 - '11T' // 9236 #305 - '12X' // 9237 #335 - '11T' // 9238 #305 - '12X' // 9239 #335 - '41A' // 923a #1066 - 'A' // 923b - '86W' // 923c #2258 - '22F' // 923d #577 - '12X' // 923e #335 - 'a32T' // 923f-9240 #851 - '22G' // 9241 #578 - 'a2T' // 9242-9243 #71 - '87E' // 9244 #2266 - '65P' // 9245 #1705 - '12X' // 9246 #335 - '2T' // 9247 #71 - '11T' // 9248 #305 - '32T' // 9249 #851 - '2T' // 924a #71 - '11T' // 924b #305 - '22G' // 924c #578 - '11T' // 924d #305 - 'a12X' // 924e-924f #335 - '11T' // 9250 #305 - '12X' // 9251 #335 - 'A' // 9252 - '22F' // 9253 #577 - 'A' // 9254 - '24T' // 9255 #643 - '2T' // 9256 #71 - '126Y' // 9257 #3300 - '13V' // 9258 #359 - '2T' // 9259 #71 - '11T' // 925a #305 - '148S' // 925b #3866 - '2T' // 925c #71 - '11T' // 925d #305 - '32T' // 925e #851 - '24T' // 925f #643 - 'a2T' // 9260-9261 #71 - '87D' // 9262 #2265 - 'A' // 9263 - '41A' // 9264 #1066 - 'a32T' // 9265-9266 #851 - '11T' // 9267 #305 - '35Y' // 9268 #934 - '2T' // 9269 #71 - 'A' // 926a - '22G' // 926b #578 - '22F' // 926c #577 - '22G' // 926d #578 - '13V' // 926e #359 - '12X' // 926f #335 - '2T' // 9270 #71 - '253W' // 9271 #6600 - '22G' // 9272 #578 - 'A' // 9273 - '3N' // 9274 #91 - '2T' // 9275 #71 - '11T' // 9276 #305 - '13V' // 9277 #359 - '11T' // 9278 #305 - '2T' // 9279 #71 - '22G' // 927a #578 - '12X' // 927b #335 - '11T' // 927c #305 - '2T' // 927d #71 - '12X' // 927e #335 - '17V' // 927f #463 - '222P' // 9280 #5787 - '9P' // 9281 #249 - '15I' // 9282 #398 - '86T' // 9283 #2255 - '9P' // 9284 #249 - '188M' // 9285 #4900 - '37T' // 9286 #981 - '50J' // 9287 #1309 - '32S' // 9288 #850 - '86L' // 9289 #2247 - '32S' // 928a #850 - 'aA' // 928b-928c - '17V' // 928d #463 - '32S' // 928e #850 - '9P' // 928f #249 - 'A' // 9290 - '44A' // 9291 #1144 - '2T' // 9292 #71 - '132E' // 9293 #3436 - 'A' // 9294 - '17V' // 9295 #463 - '44A' // 9296 #1144 - '2T' // 9297 #71 - '188V' // 9298 #4909 - '17V' // 9299 #463 - '86M' // 929a #2248 - '17V' // 929b #463 - '41A' // 929c #1066 - '37T' // 929d #981 - 'A' // 929e - '2T' // 929f #71 - '17V' // 92a0 #463 - 'b15I' // 92a1-92a3 #398 - 'a17V' // 92a4-92a5 #463 - '15I' // 92a6 #398 - '17V' // 92a7 #463 - '32S' // 92a8 #850 - 'a15I' // 92a9-92aa #398 - '32S' // 92ab #850 - '37T' // 92ac #981 - '256A' // 92ad #6656 - '37T' // 92ae #981 - '2T' // 92af #71 - 'A' // 92b0 - '9P' // 92b1 #249 - '17V' // 92b2 #463 - '163A' // 92b3 #4238 - 'A' // 92b4 - '15I' // 92b5 #398 - '44A' // 92b6 #1144 - '224M' // 92b7 #5836 - '2T' // 92b8 #71 - '41A' // 92b9 #1066 - '86K' // 92ba #2246 - '17V' // 92bb #463 - '9B' // 92bc #235 - '2X' // 92bd #75 - '9P' // 92be #249 - '28A' // 92bf #728 - '2X' // 92c0 #75 - '170D' // 92c1 #4423 - 'a9B' // 92c2-92c3 #235 - 'A' // 92c4 - '121Q' // 92c5 #3162 - '43Z' // 92c6 #1143 - '9B' // 92c7 #235 - '43Z' // 92c8 #1143 - 'a15I' // 92c9-92ca #398 - '9B' // 92cb #235 - '54M' // 92cc #1416 - '9B' // 92cd #235 - '2X' // 92ce #75 - '86J' // 92cf #2245 - '43Z' // 92d0 #1143 - '15I' // 92d1 #398 - '177N' // 92d2 #4615 - '2X' // 92d3 #75 - '9P' // 92d4 #249 - '9B' // 92d5 #235 - 'A' // 92d6 - '9B' // 92d7 #235 - '2X' // 92d8 #75 - '9B' // 92d9 #235 - 'A' // 92da - '9P' // 92db #249 - '2X' // 92dc #75 - '9B' // 92dd #235 - 'A' // 92de - '9B' // 92df #235 - 'a2X' // 92e0-92e1 #75 - 'A' // 92e2 - '28A' // 92e3 #728 - '54M' // 92e4 #1416 - '9B' // 92e5 #235 - '15I' // 92e6 #398 - '2X' // 92e7 #75 - 'a9B' // 92e8-92e9 #235 - '184S' // 92ea #4802 - '9P' // 92eb #249 - '54N' // 92ec #1417 - '86S' // 92ed #2254 - '9B' // 92ee #235 - '15I' // 92ef #398 - '40Z' // 92f0 #1065 - '15I' // 92f1 #398 - '9B' // 92f2 #235 - '86O' // 92f3 #2250 - '9P' // 92f4 #249 - 'A' // 92f5 - '15I' // 92f6 #398 - '2X' // 92f7 #75 - '132D' // 92f8 #3435 - '9B' // 92f9 #235 - '2X' // 92fa #75 - '9B' // 92fb #235 - '67P' // 92fc #1757 - '9P' // 92fd #249 - 'A' // 92fe - '2X' // 92ff #75 - '9B' // 9300 #235 - '15I' // 9301 #398 - '9B' // 9302 #235 - '9P' // 9303 #249 - '231Y' // 9304 #6030 - 'A' // 9305 - '86P' // 9306 #2251 - '9P' // 9307 #249 - '2X' // 9308 #75 - 'aA' // 9309-930a - '19X' // 930b #517 - 'A' // 930c - '2X' // 930d #75 - 'A' // 930e - '11S' // 930f #304 - '126W' // 9310 #3298 - '2X' // 9311 #75 - '22E' // 9312 #576 - 'A' // 9313 - '2X' // 9314 #75 - '13U' // 9315 #358 - 'aA' // 9316-9317 - '121P' // 9318 #3161 - '11S' // 9319 #304 - '28B' // 931a #729 - '19X' // 931b #517 - '2X' // 931c #75 - '11S' // 931d #304 - '28B' // 931e #729 - '43Y' // 931f #1142 - '148K' // 9320 #3858 - '28B' // 9321 #729 - '224L' // 9322 #5835 - '11S' // 9323 #304 - '28B' // 9324 #729 - '11S' // 9325 #304 - '188Y' // 9326 #4912 - '13U' // 9327 #358 - '28B' // 9328 #729 - '13U' // 9329 #358 - '11S' // 932a #304 - '162Y' // 932b #4236 - '86R' // 932c #2253 - '19X' // 932d #517 - '43Y' // 932e #1142 - '226S' // 932f #5894 - '9P' // 9330 #249 - '22E' // 9331 #576 - '260G' // 9332 #6766 - '11S' // 9333 #304 - '2X' // 9334 #75 - '13U' // 9335 #358 - '192B' // 9336 #4993 - '2X' // 9337 #75 - '19X' // 9338 #517 - 'A' // 9339 - 'a2X' // 933a-933b #75 - '19X' // 933c #517 - 'A' // 933d - '19O' // 933e #508 - 'A' // 933f - 'a22E' // 9340-9341 #576 - 'a9P' // 9342-9343 #249 - '28A' // 9344 #728 - 'a19X' // 9345-9346 #517 - '11S' // 9347 #304 - '43Y' // 9348 #1142 - '11S' // 9349 #304 - '177M' // 934a #4614 - '203M' // 934b #5290 - '19O' // 934c #508 - '144I' // 934d #3752 - '19O' // 934e #508 - '22E' // 934f #576 - 'b13U' // 9350-9352 #358 - '19O' // 9353 #508 - '28B' // 9354 #729 - '21G' // 9355 #552 - 'b13U' // 9356-9358 #358 - '22E' // 9359 #576 - '13U' // 935a #358 - '149C' // 935b #3876 - '13U' // 935c #358 - '19O' // 935d #508 - '13U' // 935e #358 - '22E' // 935f #576 - '13U' // 9360 #358 - 'A' // 9361 - '54O' // 9362 #1418 - '22E' // 9363 #576 - '11S' // 9364 #304 - '13U' // 9365 #358 - '22E' // 9366 #576 - '13U' // 9367 #358 - '54O' // 9368 #1418 - 'a13U' // 9369-936a #358 - '54N' // 936b #1417 - '11S' // 936c #304 - '2X' // 936d #75 - '86N' // 936e #2249 - '2X' // 936f #75 - 'a11S' // 9370-9371 #304 - 'A' // 9372 - '11S' // 9373 #304 - '28A' // 9374 #728 - '227B' // 9375 #5903 - '11S' // 9376 #304 - 'A' // 9377 - '9P' // 9378 #249 - '19X' // 9379 #517 - '11S' // 937a #304 - 'A' // 937b - '86Q' // 937c #2252 - '28A' // 937d #728 - '184R' // 937e #4801 - 'a2X' // 937f-9380 #75 - '28A' // 9381 #728 - '40Z' // 9382 #1065 - 'A' // 9383 - '9P' // 9384 #249 - '19X' // 9385 #517 - '9P' // 9386 #249 - '19X' // 9387 #517 - '2X' // 9388 #75 - 'A' // 9389 - '40Z' // 938a #1065 - '2X' // 938b #75 - '86E' // 938c #2240 - '2X' // 938d #75 - 'A' // 938e - '11R' // 938f #303 - '16J' // 9390 #425 - 'A' // 9391 - '2X' // 9392 #75 - '19O' // 9393 #508 - '22D' // 9394 #575 - '2X' // 9395 #75 - '67P' // 9396 #1757 - '11R' // 9397 #303 - '13T' // 9398 #357 - 'A' // 9399 - '22D' // 939a #575 - '13T' // 939b #357 - '16J' // 939c #425 - '32R' // 939d #849 - '13T' // 939e #357 - 'A' // 939f - '16J' // 93a0 #425 - '13T' // 93a1 #357 - '24S' // 93a2 #642 - '86B' // 93a3 #2237 - '2X' // 93a4 #75 - 'A' // 93a5 - '13T' // 93a6 #357 - '86D' // 93a7 #2239 - '2X' // 93a8 #75 - '13T' // 93a9 #357 - '32R' // 93aa #849 - '2X' // 93ab #75 - 'a22D' // 93ac-93ad #575 - '201T' // 93ae #5245 - '24S' // 93af #642 - '22D' // 93b0 #575 - 'a19O' // 93b1-93b2 #508 - '24S' // 93b3 #642 - 'a11R' // 93b4-93b5 #303 - '21G' // 93b6 #552 - 'a24S' // 93b7-93b8 #642 - 'a21G' // 93b9-93ba #552 - '11R' // 93bb #303 - '19O' // 93bc #508 - '24S' // 93bd #642 - '19O' // 93be #508 - '86G' // 93bf #2242 - '24S' // 93c0 #642 - '21G' // 93c1 #552 - '24S' // 93c2 #642 - '22D' // 93c3 #575 - '11R' // 93c4 #303 - '21G' // 93c5 #552 - '86C' // 93c6 #2238 - '11R' // 93c7 #303 - '184T' // 93c8 #4803 - '21G' // 93c9 #552 - 'c11R' // 93ca-93cd #303 - 'aA' // 93ce-93cf - '11R' // 93d0 #303 - '22D' // 93d1 #575 - 'A' // 93d2 - '27Y' // 93d3 #726 - 'aA' // 93d4-93d5 - 'b11R' // 93d6-93d8 #303 - '2X' // 93d9 #75 - 'A' // 93da - '16J' // 93db #425 - 'a13T' // 93dc-93dd #357 - '22D' // 93de #575 - '126X' // 93df #3299 - '32R' // 93e0 #849 - '215L' // 93e1 #5601 - '40Z' // 93e2 #1065 - 'A' // 93e3 - '22D' // 93e4 #575 - 'b2X' // 93e5-93e7 #75 - '13T' // 93e8 #357 - 'dA' // 93e9-93ed - '32R' // 93ee #849 - 'A' // 93ef - '32R' // 93f0 #849 - '27Y' // 93f1 #726 - 'A' // 93f2 - 'a16J' // 93f3-93f4 #425 - '13T' // 93f5 #357 - '260P' // 93f6 #6775 - '13T' // 93f7 #357 - '11R' // 93f8 #303 - '13T' // 93f9 #357 - '2X' // 93fa #75 - '11R' // 93fb #303 - 'A' // 93fc - '162Z' // 93fd #4237 - 'bA' // 93fe-9400 - '27Y' // 9401 #726 - '2X' // 9402 #75 - '13T' // 9403 #357 - '86A' // 9404 #2236 - 'aA' // 9405-9406 - '13T' // 9407 #357 - '27Y' // 9408 #726 - '2X' // 9409 #75 - 'bA' // 940a-940c - '2X' // 940d #75 - '21G' // 940e #552 - 'a11R' // 940f-9410 #303 - 'a19O' // 9411-9412 #508 - 'a11R' // 9413-9414 #303 - 'a21G' // 9415-9416 #552 - '11R' // 9417 #303 - '218H' // 9418 #5675 - '11R' // 9419 #303 - '21G' // 941a #552 - '16J' // 941b #425 - '5W' // 941c #152 - '27Z' // 941d #727 - '5W' // 941e #152 - '69S' // 941f #1812 - '24R' // 9420 #641 - '69S' // 9421 #1812 - 'a5W' // 9422-9423 #152 - '24R' // 9424 #641 - '86F' // 9425 #2241 - '27Z' // 9426 #727 - 'c24R' // 9427-942a #641 - '54K' // 942b #1414 - 'A' // 942c - '27Z' // 942d #727 - '43X' // 942e #1141 - '27Y' // 942f #726 - 'A' // 9430 - '2X' // 9431 #75 - 'a43X' // 9432-9433 #1141 - '2X' // 9434 #75 - '218C' // 9435 #5670 - '37S' // 9436 #980 - 'A' // 9437 - '54K' // 9438 #1414 - 'A' // 9439 - '43X' // 943a #1141 - '2X' // 943b #75 - 'A' // 943c - '37S' // 943d #980 - '27Z' // 943e #727 - '37S' // 943f #980 - '54L' // 9440 #1415 - '2X' // 9441 #75 - '16J' // 9442 #425 - '27Y' // 9443 #726 - '144H' // 9444 #3751 - '37S' // 9445 #980 - 'aA' // 9446-9447 - '2X' // 9448 #75 - 'A' // 9449 - '24Q' // 944a #640 - 'A' // 944b - '22C' // 944c #574 - '16J' // 944d #425 - 'bA' // 944e-9450 - '198C' // 9451 #5150 - '138D' // 9452 #3591 - '2N' // 9453 #65 - '54L' // 9454 #1415 - '22C' // 9455 #574 - 'aA' // 9456-9457 - '16J' // 9458 #425 - '2N' // 9459 #65 - '69R' // 945a #1811 - '85Y' // 945b #2234 - '2N' // 945c #65 - 'A' // 945d - '22C' // 945e #574 - '2N' // 945f #65 - '22C' // 9460 #574 - '2N' // 9461 #65 - 'a24Q' // 9462-9463 #640 - 'A' // 9464 - '27Z' // 9465 #727 - 'A' // 9466 - '16J' // 9467 #425 - '22C' // 9468 #574 - 'A' // 9469 - '22C' // 946a #574 - '156F' // 946b #4061 - '16J' // 946c #425 - '22C' // 946d #574 - '2N' // 946e #65 - '22C' // 946f #574 - '156E' // 9470 #4060 - '24Q' // 9471 #640 - '132C' // 9472 #3434 - '5W' // 9473 #152 - '24R' // 9474 #641 - '24Q' // 9475 #640 - '24R' // 9476 #641 - '24Q' // 9477 #640 - '5W' // 9478 #152 - '24R' // 9479 #641 - '5W' // 947a #152 - '27Z' // 947b #727 - '149Y' // 947c #3898 - '191Z' // 947d #4991 - '24Q' // 947e #640 - '85X' // 947f #2233 - '24R' // 9480 #641 - '24Q' // 9481 #640 - '5W' // 9482 #152 - 'a69R' // 9483-9484 #1811 - '85Z' // 9485 #2235 - 'a5W' // 9486-9487 #152 - '3I' // 9488 #86 - '2L' // 9489 #63 - 'h5W' // 948a-9492 #152 - '2R' // 9493 #69 - 'd5W' // 9494-9498 #152 - '3H' // 9499 #85 - '5W' // 949a #152 - '3W' // 949b #100 - 'a5W' // 949c-949d #152 - '3G' // 949e #84 - '86I' // 949f #2244 - '3W' // 94a0 #100 - '5W' // 94a1 #152 - '86H' // 94a2 #2243 - 'a5W' // 94a3-94a4 #152 - '2R' // 94a5 #69 - '2K' // 94a6 #62 - '3G' // 94a7 #84 - '5W' // 94a8 #152 - '3H' // 94a9 #85 - 'c5W' // 94aa-94ad #152 - '2L' // 94ae #63 - '5W' // 94af #152 - '3W' // 94b0 #100 - '2D' // 94b1 #55 - '5W' // 94b2 #152 - '2W' // 94b3 #74 - 'f5W' // 94b4-94ba #152 - '1R' // 94bb #43 - 'd5W' // 94bc-94c0 #152 - '54J' // 94c1 #1413 - '3G' // 94c2 #84 - '85T' // 94c3 #2229 - '5W' // 94c4 #152 - '2K' // 94c5 #62 - 'u5W' // 94c6-94db #152 - '85U' // 94dc #2230 - '2Y' // 94dd #76 - 'n5W' // 94de-94ec #152 - '2Y' // 94ed #76 - 'c5W' // 94ee-94f1 #152 - '3X' // 94f2 #101 - '5W' // 94f3 #152 - 'A' // 94f4 - '5V' // 94f5 #151 - '54J' // 94f6 #1413 - '5V' // 94f7 #151 - '2R' // 94f8 #69 - '5V' // 94f9 #151 - '3Y' // 94fa #102 - 'b5V' // 94fb-94fd #151 - '1Z' // 94fe #51 - '5V' // 94ff #151 - '1Z' // 9500 #51 - '3Y' // 9501 #102 - '2W' // 9502 #74 - 'a5V' // 9503-9504 #151 - '1R' // 9505 #43 - 'a5V' // 9506-9507 #151 - '2L' // 9508 #63 - 'a5V' // 9509-950a #151 - '3N' // 950b #91 - '2K' // 950c #62 - 'b5V' // 950d-950f #151 - '2L' // 9510 #63 - 'g5V' // 9511-9518 #151 - '2D' // 9519 #55 - 'f5V' // 951a-9520 #151 - '4C' // 9521 #106 - '5V' // 9522 #151 - '2W' // 9523 #74 - '3H' // 9524 #85 - '3G' // 9525 #84 - '3N' // 9526 #91 - 'A' // 9527 - 'd5V' // 9528-952c #151 - '85S' // 952d #2228 - '1Z' // 952e #51 - '3X' // 952f #101 - 'j5V' // 9530-953a #151 - '2Y' // 953b #76 - 'c5V' // 953c-953f #151 - '3H' // 9540 #85 - 'a3W' // 9541-9542 #100 - 'c5V' // 9543-9546 #151 - '85V' // 9547 #2231 - 'h5V' // 9548-9550 #151 - '2W' // 9551 #74 - 'c5V' // 9552-9555 #151 - '3W' // 9556 #100 - 'd5V' // 9557-955b #151 - '3I' // 955c #86 - 'k5V' // 955d-9568 #151 - 'A' // 9569 - 'c5V' // 956a-956d #151 - 'A' // 956e - '2W' // 956f #74 - 'd5V' // 9570-9574 #151 - 'A' // 9575 - '3X' // 9576 #101 - '244M' // 9577 #6356 - '13S' // 9578 #356 - '2N' // 9579 #65 - 'cA' // 957a-957d - '2N' // 957e #65 - '144G' // 957f #3750 - '242H' // 9580 #6299 - 'A' // 9581 - '13S' // 9582 #356 - '201K' // 9583 #5236 - '2N' // 9584 #65 - '54I' // 9585 #1412 - '19W' // 9586 #516 - '2N' // 9587 #65 - '13S' // 9588 #356 - '223M' // 9589 #5810 - '2N' // 958a #65 - '246R' // 958b #6413 - 'a2N' // 958c-958d #65 - '19W' // 958e #516 - '37R' // 958f #979 - 'A' // 9590 - '142I' // 9591 #3700 - '216X' // 9592 #5639 - '245K' // 9593 #6380 - '65O' // 9594 #1704 - 'A' // 9595 - '85M' // 9596 #2222 - '54I' // 9597 #1412 - '132B' // 9598 #3433 - '19W' // 9599 #516 - 'aA' // 959a-959b - '85W' // 959c #2232 - '2N' // 959d #65 - 'a19W' // 959e-959f #516 - '85N' // 95a0 #2223 - '13S' // 95a1 #356 - '85R' // 95a2 #2227 - '182I' // 95a3 #4740 - '37R' // 95a4 #979 - '129T' // 95a5 #3373 - '19W' // 95a6 #516 - '13S' // 95a7 #356 - '65O' // 95a8 #1704 - '126U' // 95a9 #3296 - '43W' // 95aa #1140 - 'a19W' // 95ab-95ac #516 - '37R' // 95ad #979 - '43W' // 95ae #1140 - '5V' // 95af #151 - '43W' // 95b0 #1140 - '231X' // 95b1 #6029 - '85Q' // 95b2 #2226 - 'A' // 95b3 - '248E' // 95b4 #6452 - 'A' // 95b5 - '13S' // 95b6 #356 - 'A' // 95b7 - '5V' // 95b8 #151 - 'a19W' // 95b9-95ba #516 - 'a37R' // 95bb-95bc #979 - 'b19W' // 95bd-95bf #516 - 'b5V' // 95c0-95c2 #151 - '19W' // 95c3 #516 - 'a8O' // 95c4-95c5 #222 - '184Q' // 95c6 #4800 - '85O' // 95c7 #2224 - 'a43V' // 95c8-95c9 #1139 - '162V' // 95ca #4233 - '13S' // 95cb #356 - 'a43V' // 95cc-95cd #1139 - 'aA' // 95ce-95cf - '13S' // 95d0 #356 - 'a2N' // 95d1-95d2 #65 - '13S' // 95d3 #356 - 'a43U' // 95d4-95d5 #1138 - '170B' // 95d6 #4421 - 'A' // 95d7 - '257C' // 95d8 #6684 - '2N' // 95d9 #65 - '13S' // 95da #356 - 'A' // 95db - '245X' // 95dc #6393 - '2N' // 95dd #65 - '13S' // 95de #356 - '2N' // 95df #65 - '13S' // 95e0 #356 - '43U' // 95e1 #1138 - '138C' // 95e2 #3590 - 'A' // 95e3 - 'a13S' // 95e4-95e5 #356 - '2N' // 95e6 #65 - 'A' // 95e7 - '170C' // 95e8 #4422 - '8O' // 95e9 #222 - '3N' // 95ea #91 - '3W' // 95eb #100 - 'A' // 95ec - '2D' // 95ed #55 - '7K' // 95ee #192 - '2Y' // 95ef #76 - 'a8O' // 95f0-95f1 #222 - '3I' // 95f2 #86 - '8O' // 95f3 #222 - '35T' // 95f4 #929 - '3W' // 95f5 #100 - '8O' // 95f6 #222 - '2R' // 95f7 #69 - '3X' // 95f8 #101 - '1R' // 95f9 #43 - '2Y' // 95fa #76 - '3Q' // 95fb #94 - '8O' // 95fc #222 - '2Y' // 95fd #76 - 'a8O' // 95fe-95ff #222 - '3H' // 9600 #85 - '2L' // 9601 #63 - 'b8O' // 9602-9604 #222 - '1Z' // 9605 #51 - 'g8O' // 9606-960d #222 - '2W' // 960e #74 - '8O' // 960f #222 - '2K' // 9610 #62 - 'b8O' // 9611-9613 #222 - '2L' // 9614 #63 - 'b8O' // 9615-9617 #222 - 'A' // 9618 - 'b8O' // 9619-961b #222 - '85P' // 961c #2225 - '43V' // 961d #1139 - '2N' // 961e #65 - '2D' // 961f #55 - 'A' // 9620 - '43U' // 9621 #1138 - '2N' // 9622 #65 - 'A' // 9623 - '43S' // 9624 #1136 - 'a2N' // 9625-9626 #65 - 'A' // 9627 - '24O' // 9628 #638 - 'A' // 9629 - '184F' // 962a #4789 - 'A' // 962b - '2N' // 962c #65 - 'A' // 962d - '126T' // 962e #3295 - '24O' // 962f #638 - 'A' // 9630 - '138F' // 9631 #3593 - '229Z' // 9632 #5979 - '85A' // 9633 #2210 - '248F' // 9634 #6453 - '3Y' // 9635 #102 - '3N' // 9636 #91 - '2N' // 9637 #65 - '22B' // 9638 #573 - 'a2N' // 9639-963a #65 - '195C' // 963b #5072 - '24O' // 963c #638 - '69Q' // 963d #1810 - '8O' // 963e #222 - '227F' // 963f #5907 - '159I' // 9640 #4142 - '24O' // 9641 #638 - '54G' // 9642 #1410 - '37Q' // 9643 #978 - '226T' // 9644 #5895 - '85L' // 9645 #2221 - '2C' // 9646 #54 - '2W' // 9647 #74 - '2C' // 9648 #54 - '8O' // 9649 #222 - 'A' // 964a - '121O' // 964b #3160 - '162W' // 964c #4234 - '212W' // 964d #5534 - '37Q' // 964e #978 - '43S' // 964f #1136 - '238E' // 9650 #6192 - '37Q' // 9651 #978 - '2N' // 9652 #65 - '37Q' // 9653 #978 - '24O' // 9654 #638 - '1R' // 9655 #43 - '22B' // 9656 #573 - '2N' // 9657 #65 - '43S' // 9658 #1136 - 'aA' // 9659-965a - '85D' // 965b #2213 - 'c54G' // 965c-965f #1410 - 'A' // 9660 - '24O' // 9661 #638 - '229P' // 9662 #5969 - '203C' // 9663 #5280 - '237A' // 9664 #6162 - '254I' // 9665 #6612 - '2N' // 9666 #65 - 'a8O' // 9667-9668 #222 - '85K' // 9669 #2220 - '192A' // 966a #4992 - 'A' // 966b - '24O' // 966c #638 - 'A' // 966d - '2N' // 966e #65 - 'A' // 966f - '197H' // 9670 #5129 - 'A' // 9671 - '12W' // 9672 #334 - '226Q' // 9673 #5892 - '12W' // 9674 #334 - '160Y' // 9675 #4184 - '181W' // 9676 #4728 - '184P' // 9677 #4799 - '220X' // 9678 #5743 - 'A' // 9679 - '258J' // 967a #6717 - '22B' // 967b #573 - '2N' // 967c #65 - '221E' // 967d #5750 - 'a2N' // 967e-967f #65 - 'A' // 9680 - '22B' // 9681 #573 - '69Q' // 9682 #1810 - 'a24P' // 9683-9684 #639 - '85H' // 9685 #2217 - '209T' // 9686 #5453 - 'A' // 9687 - '85G' // 9688 #2216 - '24P' // 9689 #639 - '219W' // 968a #5716 - '84Y' // 968b #2208 - 'A' // 968c - '43R' // 968d #1135 - '212J' // 968e #5521 - '131E' // 968f #3410 - '2D' // 9690 #55 - '2N' // 9691 #65 - 'aA' // 9692-9693 - '209H' // 9694 #5441 - '43R' // 9695 #1135 - '22B' // 9696 #573 - '12W' // 9697 #334 - '43R' // 9698 #1135 - '142X' // 9699 #3715 - '2N' // 969a #65 - '237R' // 969b #6179 - '211O' // 969c #5500 - '2N' // 969d #65 - '54H' // 969e #1411 - '2N' // 969f #65 - '257K' // 96a0 #6692 - 'a17U' // 96a1-96a2 #462 - '85J' // 96a3 #2219 - '12W' // 96a4 #334 - '22B' // 96a5 #573 - '2N' // 96a6 #65 - '144F' // 96a7 #3749 - '68G' // 96a8 #1774 - '24P' // 96a9 #639 - '49F' // 96aa #1279 - 'A' // 96ab - '17U' // 96ac #462 - 'A' // 96ad - '24P' // 96ae #639 - '2N' // 96af #65 - '12W' // 96b0 #334 - '231W' // 96b1 #6028 - '2N' // 96b2 #65 - 'a12W' // 96b3-96b4 #334 - 'A' // 96b5 - '84Z' // 96b6 #2209 - '254F' // 96b7 #6609 - '138E' // 96b8 #3592 - '12W' // 96b9 #334 - '2N' // 96ba #65 - '199M' // 96bb #5186 - '85E' // 96bc #2214 - '12W' // 96bd #334 - '2D' // 96be #55 - 'A' // 96bf - '167K' // 96c0 #4352 - '121N' // 96c1 #3159 - 'A' // 96c2 - '17U' // 96c3 #462 - '227N' // 96c4 #5915 - '219I' // 96c5 #5702 - '239H' // 96c6 #6221 - '136X' // 96c7 #3559 - 'A' // 96c8 - '24N' // 96c9 #637 - '24P' // 96ca #639 - '24N' // 96cb #637 - '125E' // 96cc #3254 - '121M' // 96cd #3158 - '24N' // 96ce #637 - '3W' // 96cf #100 - 'A' // 96d0 - '259J' // 96d1 #6743 - '12W' // 96d2 #334 - 'aA' // 96d3-96d4 - '177L' // 96d5 #4613 - '49F' // 96d6 #1279 - 'A' // 96d7 - '24P' // 96d8 #639 - '68G' // 96d9 #1774 - '2N' // 96da #65 - '125I' // 96db #3258 - '49F' // 96dc #1279 - '24P' // 96dd #639 - '206S' // 96de #5374 - '2N' // 96df #65 - '8O' // 96e0 #222 - 'A' // 96e1 - '229Y' // 96e2 #5978 - '229O' // 96e3 #5968 - 'cA' // 96e4-96e7 - '211N' // 96e8 #5499 - '24N' // 96e9 #637 - '211J' // 96ea #5495 - '252P' // 96eb #6567 - 'bA' // 96ec-96ee - '156D' // 96ef #4059 - '85I' // 96f0 #2218 - '12W' // 96f1 #334 - '227J' // 96f2 #5911 - '2W' // 96f3 #74 - '17U' // 96f4 #462 - 'A' // 96f5 - '218K' // 96f6 #5678 - '209G' // 96f7 #5440 - 'A' // 96f8 - '24N' // 96f9 #637 - '2N' // 96fa #65 - '246P' // 96fb #6411 - 'aA' // 96fc-96fd - '1R' // 96fe #43 - '54H' // 96ff #1411 - '233I' // 9700 #6066 - '8O' // 9701 #222 - '12W' // 9702 #334 - '22B' // 9703 #573 - '126V' // 9704 #3297 - '12W' // 9705 #334 - '149X' // 9706 #3897 - '204G' // 9707 #5310 - '12W' // 9708 #334 - '138G' // 9709 #3594 - '255M' // 970a #6642 - 'aA' // 970b-970c - '162X' // 970d #4235 - 'a12W' // 970e-970f #334 - '17U' // 9710 #462 - '24N' // 9711 #637 - 'A' // 9712 - '126S' // 9713 #3294 - '2N' // 9714 #65 - 'A' // 9715 - '156C' // 9716 #4058 - 'aA' // 9717-9718 - '24N' // 9719 #637 - '2N' // 971a #65 - '22B' // 971b #573 - '180I' // 971c #4688 - '22A' // 971d #572 - '153Z' // 971e #4003 - 'a17U' // 971f-9720 #462 - '17T' // 9721 #461 - 'b1U' // 9722-9724 #46 - 'aA' // 9725-9726 - '188W' // 9727 #4910 - '22A' // 9728 #572 - 'A' // 9729 - '54F' // 972a #1409 - 'aA' // 972b-972c - '8O' // 972d #222 - 'aA' // 972e-972f - '43Q' // 9730 #1134 - '17T' // 9731 #461 - '220N' // 9732 #5733 - '1U' // 9733 #46 - '32Q' // 9734 #848 - 'A' // 9735 - '17T' // 9736 #461 - 'A' // 9737 - '199L' // 9738 #5185 - '131Z' // 9739 #3431 - '17U' // 973a #462 - '1U' // 973b #46 - 'A' // 973c - '43Q' // 973d #1134 - '121L' // 973e #3157 - 'A' // 973f - '32Q' // 9740 #848 - '17T' // 9741 #461 - '131Y' // 9742 #3430 - '1U' // 9743 #46 - '43Q' // 9744 #1134 - 'A' // 9745 - 'a22A' // 9746-9747 #572 - '216W' // 9748 #5638 - '54F' // 9749 #1409 - '17T' // 974a #461 - 'aA' // 974b-974c - 'b1U' // 974d-974f #46 - '8O' // 9750 #222 - '84X' // 9751 #2207 - '230S' // 9752 #5998 - '2Y' // 9753 #76 - 'A' // 9754 - '17T' // 9755 #461 - '153K' // 9756 #3988 - '17T' // 9757 #461 - '22A' // 9758 #572 - '258P' // 9759 #6723 - '170A' // 975a #4420 - '16I' // 975b #424 - '206P' // 975c #5371 - '17U' // 975d #462 - '236P' // 975e #6151 - '32Q' // 975f #848 - '206R' // 9760 #5373 - '132A' // 9761 #3432 - '49G' // 9762 #1280 - '41I' // 9763 #1074 - '1U' // 9764 #46 - '8N' // 9765 #221 - '16I' // 9766 #424 - '1U' // 9767 #46 - '22A' // 9768 #572 - '198I' // 9769 #5156 - 'a1U' // 976a-976b #46 - '43T' // 976c #1137 - '85C' // 976d #2212 - '1U' // 976e #46 - 'aA' // 976f-9770 - '22A' // 9771 #572 - 'A' // 9772 - '16I' // 9773 #424 - '169E' // 9774 #4398 - 'A' // 9775 - '16I' // 9776 #424 - '248C' // 9777 #6450 - 'a1U' // 9778-9779 #46 - '37P' // 977a #977 - '1U' // 977b #46 - '16I' // 977c #424 - '1U' // 977d #46 - 'A' // 977e - '1U' // 977f #46 - '22A' // 9780 #572 - '1U' // 9781 #46 - 'aA' // 9782-9783 - '85F' // 9784 #2215 - '16I' // 9785 #424 - '1U' // 9786 #46 - '32Q' // 9787 #848 - '17U' // 9788 #462 - '17T' // 9789 #461 - 'A' // 978a - '206Q' // 978b #5372 - 'A' // 978c - '141T' // 978d #3685 - '43T' // 978e #1137 - '37P' // 978f #977 - '1U' // 9790 #46 - 'a8N' // 9791-9792 #221 - 'A' // 9793 - '8N' // 9794 #221 - '41I' // 9795 #1074 - 'a1U' // 9796-9797 #46 - '85B' // 9798 #2211 - '1U' // 9799 #46 - '41I' // 979a #1074 - '32Q' // 979b #848 - '1U' // 979c #46 - '17U' // 979d #462 - '16I' // 979e #424 - '17T' // 979f #461 - '121K' // 97a0 #3156 - 'A' // 97a1 - '1U' // 97a2 #46 - '16I' // 97a3 #424 - 'A' // 97a4 - '43T' // 97a5 #1137 - '16I' // 97a6 #424 - 'A' // 97a7 - '37P' // 97a8 #977 - 'aA' // 97a9-97aa - '37P' // 97ab #977 - '16I' // 97ac #424 - '147Q' // 97ad #3838 - '16I' // 97ae #424 - '8N' // 97af #221 - 'A' // 97b0 - '17T' // 97b1 #461 - '54E' // 97b2 #1408 - '1U' // 97b3 #46 - '54E' // 97b4 #1408 - '1U' // 97b5 #46 - '22A' // 97b6 #572 - '43P' // 97b7 #1133 - '12V' // 97b8 #333 - '19V' // 97b9 #515 - '12V' // 97ba #333 - 'A' // 97bb - '1U' // 97bc #46 - '17S' // 97bd #460 - '12V' // 97be #333 - '19V' // 97bf #515 - '17S' // 97c0 #460 - '19V' // 97c1 #515 - '17S' // 97c2 #460 - '19V' // 97c3 #515 - 'a1U' // 97c4-97c5 #46 - '54B' // 97c6 #1405 - '1U' // 97c7 #46 - '12V' // 97c8 #333 - '19V' // 97c9 #515 - '1U' // 97ca #46 - '156A' // 97cb #4056 - '144E' // 97cc #3748 - '19V' // 97cd #515 - '1U' // 97ce #46 - 'A' // 97cf - 'a1U' // 97d0-97d1 #46 - '17S' // 97d2 #460 - '228I' // 97d3 #5936 - '1U' // 97d4 #46 - 'A' // 97d5 - '43P' // 97d6 #1133 - '1U' // 97d7 #46 - '43N' // 97d8 #1131 - '19V' // 97d9 #515 - 'A' // 97da - '1U' // 97db #46 - '54B' // 97dc #1405 - 'a19V' // 97dd-97de #515 - 'A' // 97df - '12V' // 97e0 #333 - '43N' // 97e1 #1131 - 'aA' // 97e2-97e3 - '1U' // 97e4 #46 - 'A' // 97e5 - '84P' // 97e6 #2199 - '3X' // 97e7 #101 - 'A' // 97e8 - '2C' // 97e9 #54 - 'a8N' // 97ea-97eb #221 - '2K' // 97ec #62 - '84O' // 97ed #2198 - '43N' // 97ee #1131 - '1U' // 97ef #46 - '43P' // 97f0 #1133 - '19V' // 97f1 #515 - '12V' // 97f2 #333 - '238C' // 97f3 #6190 - '1U' // 97f4 #46 - '84Q' // 97f5 #2200 - '126Q' // 97f6 #3292 - 'a1U' // 97f7-97f8 #46 - '27X' // 97f9 #725 - '6V' // 97fa #177 - '166H' // 97fb #4323 - 'aA' // 97fc-97fd - '43O' // 97fe #1132 - '228J' // 97ff #5937 - '260O' // 9800 #6774 - '246D' // 9801 #6399 - '68F' // 9802 #1773 - '126D' // 9803 #3279 - '6V' // 9804 #177 - '229X' // 9805 #5977 - '68F' // 9806 #1773 - '6V' // 9807 #177 - '220W' // 9808 #5742 - 'A' // 9809 - '54C' // 980a #1406 - 'A' // 980b - '65N' // 980c #1703 - '1U' // 980d #46 - 'a6V' // 980e-980f #177 - '227C' // 9810 #5904 - '143K' // 9811 #3728 - '166Z' // 9812 #4341 - '195B' // 9813 #5071 - '15H' // 9814 #397 - '17S' // 9815 #460 - '6V' // 9816 #177 - '162U' // 9817 #4232 - '219O' // 9818 #5708 - 'a1U' // 9819-981a #46 - 'A' // 981b - '6V' // 981c #177 - 'A' // 981d - '15H' // 981e #397 - '17S' // 981f #460 - 'a15H' // 9820-9821 #397 - 'A' // 9822 - '6V' // 9823 #177 - '126R' // 9824 #3293 - '1U' // 9825 #46 - '6V' // 9826 #177 - 'A' // 9827 - '43O' // 9828 #1132 - 'aA' // 9829-982a - '15H' // 982b #397 - '253H' // 982c #6585 - '242J' // 982d #6301 - '12V' // 982e #333 - '1U' // 982f #46 - '138A' // 9830 #3588 - 'A' // 9831 - '6V' // 9832 #177 - '12V' // 9833 #333 - '15H' // 9834 #397 - '6V' // 9835 #177 - 'A' // 9836 - '6V' // 9837 #177 - '180D' // 9838 #4683 - '43M' // 9839 #1130 - 'A' // 983a - '218V' // 983b #5689 - '258B' // 983c #6709 - 'a1U' // 983d-983e #46 - 'dA' // 983f-9843 - '1U' // 9844 #46 - '27X' // 9845 #725 - '199K' // 9846 #5184 - '12V' // 9847 #333 - 'a27X' // 9848-9849 #725 - '1U' // 984a #46 - '12V' // 984b #333 - '242S' // 984c #6310 - '222J' // 984d #5781 - '84U' // 984e #2204 - '214H' // 984f #5571 - 'A' // 9850 - '41I' // 9851 #1074 - '15H' // 9852 #397 - '6V' // 9853 #177 - '258V' // 9854 #6729 - '253V' // 9855 #6599 - '6V' // 9856 #177 - '15H' // 9857 #397 - '213Z' // 9858 #5563 - '6V' // 9859 #177 - '248D' // 985a #6451 - '156B' // 985b #4057 - 'aA' // 985c-985d - '242C' // 985e #6294 - 'bA' // 985f-9861 - 'a15H' // 9862-9863 #397 - 'A' // 9864 - '43M' // 9865 #1130 - '12V' // 9866 #333 - '218X' // 9867 #5691 - '17S' // 9868 #460 - 'A' // 9869 - '1U' // 986a #46 - '126P' // 986b #3291 - '12V' // 986c #333 - 'aA' // 986d-986e - '224K' // 986f #5834 - 'a15H' // 9870-9871 #397 - 'A' // 9872 - 'a6V' // 9873-9874 #177 - '177K' // 9875 #4612 - '2D' // 9876 #55 - 'a8N' // 9877-9878 #221 - '1Z' // 9879 #51 - '3I' // 987a #86 - '2C' // 987b #54 - '8N' // 987c #221 - '2K' // 987d #62 - '2C' // 987e #54 - '3N' // 987f #91 - '8N' // 9880 #221 - '2Y' // 9881 #76 - '2K' // 9882 #62 - '8N' // 9883 #221 - '2D' // 9884 #55 - '8N' // 9885 #221 - '1Z' // 9886 #51 - '2Y' // 9887 #76 - '2L' // 9888 #63 - '8N' // 9889 #221 - '2W' // 988a #74 - 'd8N' // 988b-988f #221 - '3W' // 9890 #100 - '3Q' // 9891 #94 - 'A' // 9892 - 'b8N' // 9893-9895 #221 - '1R' // 9896 #43 - '4C' // 9897 #106 - '7K' // 9898 #192 - 'b8N' // 9899-989b #221 - '3I' // 989c #86 - '2C' // 989d #54 - 'a8N' // 989e-989f #221 - '2R' // 98a0 #69 - 'a8N' // 98a1-98a2 #221 - 'A' // 98a3 - '3X' // 98a4 #101 - 'b8N' // 98a5-98a7 #221 - '237O' // 98a8 #6176 - '43O' // 98a9 #1132 - 'a1U' // 98aa-98ab #46 - 'A' // 98ac - '1U' // 98ad #46 - '6V' // 98ae #177 - '84S' // 98af #2202 - '1U' // 98b0 #46 - '65N' // 98b1 #1703 - 'a27X' // 98b2-98b3 #725 - '12V' // 98b4 #333 - 'A' // 98b5 - '15H' // 98b6 #397 - 'a6V' // 98b7-98b8 #177 - '17S' // 98b9 #460 - '15H' // 98ba #397 - 'a6V' // 98bb-98bc #177 - '27X' // 98bd #725 - '8N' // 98be #221 - '6V' // 98bf #177 - 'aA' // 98c0-98c1 - '1U' // 98c2 #46 - '84N' // 98c3 #2197 - '177J' // 98c4 #4611 - '1U' // 98c5 #46 - '169Z' // 98c6 #4419 - '43M' // 98c7 #1130 - '15H' // 98c8 #397 - 'A' // 98c9 - '27X' // 98ca #725 - 'a1U' // 98cb-98cc #46 - 'A' // 98cd - '138B' // 98ce #3589 - 'g8N' // 98cf-98d6 #221 - 'A' // 98d7 - '2L' // 98d8 #63 - '3H' // 98d9 #85 - '8N' // 98da #221 - '221Z' // 98db #5771 - '54C' // 98dc #1406 - '7V' // 98dd #203 - '84R' // 98de #2201 - '238V' // 98df #6209 - '6V' // 98e0 #177 - '84M' // 98e1 #2196 - '130A' // 98e2 #3380 - '6V' // 98e3 #177 - 'A' // 98e4 - '6V' // 98e5 #177 - '12V' // 98e6 #333 - '54D' // 98e7 #1407 - '7V' // 98e8 #203 - '54D' // 98e9 #1407 - '144D' // 98ea #3747 - '37O' // 98eb #976 - '84W' // 98ec #2206 - '53Z' // 98ed #1403 - '248B' // 98ee #6449 - '215U' // 98ef #5610 - '1M' // 98f0 #38 - '37O' // 98f1 #976 - '223G' // 98f2 #5804 - '1M' // 98f3 #38 - '84T' // 98f4 #2203 - '17S' // 98f5 #460 - '1M' // 98f6 #38 - 'dA' // 98f7-98fb - '154Z' // 98fc #4029 - '196K' // 98fd #5106 - '219V' // 98fe #5715 - 'bA' // 98ff-9901 - '1M' // 9902 #38 - '154H' // 9903 #4011 - 'A' // 9904 - '201V' // 9905 #5247 - 'A' // 9906 - '1M' // 9907 #38 - '54A' // 9908 #1404 - '53Z' // 9909 #1403 - '228B' // 990a #5929 - 'A' // 990b - '84V' // 990c #2205 - '7V' // 990d #203 - '17S' // 990e #460 - 'A' // 990f - '226E' // 9910 #5880 - 'a37O' // 9911-9912 #976 - '166E' // 9913 #4320 - '54A' // 9914 #1404 - '1M' // 9915 #38 - 'a37O' // 9916-9917 #976 - '206O' // 9918 #5370 - '12U' // 9919 #332 - '126O' // 991a #3290 - '43L' // 991b #1129 - '32P' // 991c #847 - '1M' // 991d #38 - '84G' // 991e #2190 - '1M' // 991f #38 - '69O' // 9920 #1808 - '144C' // 9921 #3746 - '1M' // 9922 #38 - 'A' // 9923 - '1M' // 9924 #38 - 'A' // 9925 - '1M' // 9926 #38 - '32P' // 9927 #847 - '230H' // 9928 #5987 - 'aA' // 9929-992a - '32P' // 992b #847 - '43L' // 992c #1129 - 'A' // 992d - '43L' // 992e #1129 - 'aA' // 992f-9930 - 'b32P' // 9931-9933 #847 - '1M' // 9934 #38 - '162T' // 9935 #4231 - 'A' // 9936 - '12U' // 9937 #332 - '84L' // 9938 #2195 - '43K' // 9939 #1128 - '32P' // 993a #847 - '27W' // 993b #724 - '4K' // 993c #114 - 'a17R' // 993d-993e #459 - '13R' // 993f #355 - '4K' // 9940 #114 - '1M' // 9941 #38 - '27W' // 9942 #724 - '13R' // 9943 #355 - 'A' // 9944 - '137Z' // 9945 #3587 - 'a1M' // 9946-9947 #38 - '4K' // 9948 #114 - '32O' // 9949 #846 - '13R' // 994a #355 - '184N' // 994b #4797 - '37N' // 994c #975 - '32O' // 994d #846 - '4K' // 994e #114 - 'A' // 994f - '1M' // 9950 #38 - '37N' // 9951 #975 - '144B' // 9952 #3745 - 'A' // 9953 - '37N' // 9954 #975 - '121I' // 9955 #3154 - 'A' // 9956 - '152M' // 9957 #3964 - 'a1M' // 9958-9959 #38 - 'A' // 995a - '1M' // 995b #38 - '4K' // 995c #114 - '12U' // 995d #332 - '17R' // 995e #459 - '4K' // 995f #114 - '1M' // 9960 #38 - 'a13R' // 9961-9962 #355 - '43K' // 9963 #1128 - 'A' // 9964 - '2K' // 9965 #62 - 'c7V' // 9966-9969 #203 - '2K' // 996a #62 - 'a7V' // 996b-996c #203 - 'a3I' // 996d-996e #86 - '7V' // 996f #203 - '2C' // 9970 #54 - '2L' // 9971 #63 - '2K' // 9972 #62 - 'A' // 9973 - 'a7V' // 9974-9975 #203 - '3H' // 9976 #85 - 'b7V' // 9977-9979 #203 - '3X' // 997a #101 - 'A' // 997b - '4C' // 997c #106 - 'a7V' // 997d-997e #203 - '3H' // 997f #85 - 'a7V' // 9980-9981 #203 - 'A' // 9982 - 'a7V' // 9983-9984 #203 - '3W' // 9985 #100 - '2C' // 9986 #54 - '7V' // 9987 #203 - '2D' // 9988 #55 - 'A' // 9989 - 'a7V' // 998a-998b #203 - 'A' // 998c - 'd7V' // 998d-9991 #203 - '3W' // 9992 #100 - 'b7V' // 9993-9995 #203 - '241J' // 9996 #6275 - 'a17R' // 9997-9998 #459 - '236Q' // 9999 #6152 - 'A' // 999a - '27W' // 999b #724 - '13R' // 999c #355 - '69O' // 999d #1808 - '4K' // 999e #114 - '1M' // 999f #38 - 'A' // 99a0 - '13R' // 99a1 #355 - 'A' // 99a2 - '1M' // 99a3 #38 - '12U' // 99a4 #332 - '63O' // 99a5 #1652 - '1M' // 99a6 #38 - 'A' // 99a7 - '184O' // 99a8 #4798 - 'A' // 99a9 - '12U' // 99aa #332 - '53Y' // 99ab #1402 - '235T' // 99ac #6129 - '131X' // 99ad #3429 - '149W' // 99ae #3896 - '13R' // 99af #355 - '1M' // 99b0 #38 - '37N' // 99b1 #975 - '1M' // 99b2 #38 - '159W' // 99b3 #4156 - '84I' // 99b4 #2192 - '1M' // 99b5 #38 - 'aA' // 99b6-99b7 - '12U' // 99b8 #332 - '32O' // 99b9 #846 - '1M' // 99ba #38 - 'A' // 99bb - '43K' // 99bc #1128 - '1M' // 99bd #38 - 'A' // 99be - '69P' // 99bf #1809 - 'A' // 99c0 - '162S' // 99c1 #4230 - 'A' // 99c2 - '69P' // 99c3 #1809 - '84J' // 99c4 #2193 - '84K' // 99c5 #2194 - '256S' // 99c6 #6674 - 'A' // 99c7 - 'a1M' // 99c8-99c9 #38 - 'aA' // 99ca-99cb - '7V' // 99cc #203 - 'aA' // 99cd-99ce - '13R' // 99cf #355 - '191A' // 99d0 #4966 - '32O' // 99d1 #846 - '84H' // 99d2 #2191 - '1M' // 99d3 #38 - '4K' // 99d4 #114 - '191Y' // 99d5 #4990 - '13R' // 99d6 #355 - 'A' // 99d7 - '4K' // 99d8 #114 - '32O' // 99d9 #846 - '27W' // 99da #724 - '177H' // 99db #4609 - '1M' // 99dc #38 - '63O' // 99dd #1652 - '1M' // 99de #38 - '37M' // 99df #974 - '12U' // 99e0 #332 - '17R' // 99e1 #459 - '4K' // 99e2 #114 - 'bA' // 99e3-99e5 - '12U' // 99e6 #332 - '1M' // 99e7 #38 - 'A' // 99e8 - '13R' // 99e9 #355 - 'b1M' // 99ea-99ec #38 - '137X' // 99ed #3585 - '4K' // 99ee #114 - 'A' // 99ef - '4K' // 99f0 #114 - '62Z' // 99f1 #1637 - '1M' // 99f2 #38 - 'A' // 99f3 - '1M' // 99f4 #38 - '27W' // 99f5 #724 - 'aA' // 99f6-99f7 - '4K' // 99f8 #114 - '1M' // 99f9 #38 - 'A' // 99fa - '4K' // 99fb #114 - 'b1M' // 99fc-99fe #38 - '154J' // 99ff #4013 - 'A' // 9a00 - '21Z' // 9a01 #571 - '4K' // 9a02 #114 - 'a17R' // 9a03-9a04 #459 - '4K' // 9a05 #114 - 'aA' // 9a06-9a07 - '248A' // 9a08 #6448 - 'A' // 9a09 - 'a1M' // 9a0a-9a0b #38 - '4K' // 9a0c #114 - 'A' // 9a0d - '203L' // 9a0e #5289 - '21Z' // 9a0f #571 - '4K' // 9a10 #114 - '17R' // 9a11 #459 - '255T' // 9a12 #6649 - '259O' // 9a13 #6748 - 'aA' // 9a14-9a15 - '4K' // 9a16 #114 - 'aA' // 9a17-9a18 - '195W' // 9a19 #5092 - '1M' // 9a1a #38 - '53Y' // 9a1b #1402 - '13R' // 9a1c #355 - 'A' // 9a1d - '1M' // 9a1e #38 - '12U' // 9a1f #332 - '4K' // 9a20 #114 - '12U' // 9a21 #332 - 'a1M' // 9a22-9a23 #38 - '4K' // 9a24 #114 - 'A' // 9a25 - '12U' // 9a26 #332 - '1M' // 9a27 #38 - '252E' // 9a28 #6556 - 'aA' // 9a29-9a2a - '21Z' // 9a2b #571 - 'A' // 9a2c - 'a4K' // 9a2d-9a2e #114 - '12U' // 9a2f #332 - '188U' // 9a30 #4908 - '1M' // 9a31 #38 - '7V' // 9a32 #203 - '1M' // 9a33 #38 - '13R' // 9a34 #355 - '4K' // 9a35 #114 - '37M' // 9a36 #974 - '184M' // 9a37 #4796 - '4K' // 9a38 #114 - 'aA' // 9a39-9a3a - 'a12U' // 9a3b-9a3c #332 - 'A' // 9a3d - '4K' // 9a3e #114 - 'A' // 9a3f - '21Z' // 9a40 #571 - 'a4K' // 9a41-9a42 #114 - '37M' // 9a43 #974 - '4K' // 9a44 #114 - '66Q' // 9a45 #1732 - 'A' // 9a46 - '1M' // 9a47 #38 - 'aA' // 9a48-9a49 - '17R' // 9a4a #459 - '1M' // 9a4b #38 - '4K' // 9a4c #114 - '21Z' // 9a4d #571 - '17R' // 9a4e #459 - 'aA' // 9a4f-9a50 - '1M' // 9a51 #38 - '17R' // 9a52 #459 - 'A' // 9a53 - '1M' // 9a54 #38 - '144A' // 9a55 #3744 - '4K' // 9a56 #114 - '231V' // 9a57 #6027 - '27W' // 9a58 #724 - 'A' // 9a59 - '220V' // 9a5a #5741 - '62Z' // 9a5b #1637 - '12U' // 9a5c #332 - '1M' // 9a5d #38 - 'A' // 9a5e - '66Q' // 9a5f #1732 - 'aA' // 9a60-9a61 - '21Z' // 9a62 #571 - '13R' // 9a63 #355 - '4K' // 9a64 #114 - '21Z' // 9a65 #571 - 'bA' // 9a66-9a68 - '21Z' // 9a69 #571 - '37M' // 9a6a #974 - '17R' // 9a6b #459 - '177I' // 9a6c #4610 - '3W' // 9a6d #100 - '7V' // 9a6e #203 - '2W' // 9a6f #74 - '4C' // 9a70 #106 - '1R' // 9a71 #43 - 'A' // 9a72 - '3X' // 9a73 #101 - '3H' // 9a74 #85 - '7V' // 9a75 #203 - '4C' // 9a76 #106 - 'c7V' // 9a77-9a7a #203 - '3Y' // 9a7b #102 - '3G' // 9a7c #84 - '7V' // 9a7d #203 - '3N' // 9a7e #91 - '3G' // 9a7f #84 - '8M' // 9a80 #220 - '3W' // 9a81 #100 - '1R' // 9a82 #43 - '8M' // 9a83 #220 - '3H' // 9a84 #85 - '8M' // 9a85 #220 - '3W' // 9a86 #100 - '2W' // 9a87 #74 - 'b8M' // 9a88-9a8a #220 - '2W' // 9a8b #74 - '1Z' // 9a8c #51 - 'a8M' // 9a8d-9a8e #220 - '84F' // 9a8f #2189 - '8M' // 9a90 #220 - '1R' // 9a91 #43 - 'a8M' // 9a92-9a93 #220 - 'A' // 9a94 - 'a8M' // 9a95-9a96 #220 - '3Y' // 9a97 #102 - 'a8M' // 9a98-9a99 #220 - '4C' // 9a9a #106 - 'g8M' // 9a9b-9aa2 #220 - 'A' // 9aa3 - '2L' // 9aa4 #63 - '8M' // 9aa5 #220 - 'A' // 9aa6 - '8M' // 9aa7 #220 - '204N' // 9aa8 #5317 - 'A' // 9aa9 - '1M' // 9aaa #38 - 'A' // 9aab - 'b1M' // 9aac-9aae #38 - 'a21X' // 9aaf-9ab0 #569 - '21Y' // 9ab1 #570 - '37L' // 9ab2 #973 - 'A' // 9ab3 - '1M' // 9ab4 #38 - '41H' // 9ab5 #1073 - '21X' // 9ab6 #569 - '83X' // 9ab7 #2181 - '130E' // 9ab8 #3384 - '21X' // 9ab9 #569 - '21Y' // 9aba #570 - '1M' // 9abb #38 - '121J' // 9abc #3155 - '37L' // 9abd #973 - '53X' // 9abe #1401 - '1M' // 9abf #38 - 'a21X' // 9ac0-9ac1 #569 - '21Y' // 9ac2 #570 - '41H' // 9ac3 #1073 - '252V' // 9ac4 #6573 - '2W' // 9ac5 #74 - '1M' // 9ac6 #38 - 'A' // 9ac7 - '41H' // 9ac8 #1073 - 'aA' // 9ac9-9aca - 'a8M' // 9acb-9acc #220 - 'A' // 9acd - '41H' // 9ace #1073 - '21X' // 9acf #569 - '1M' // 9ad0 #38 - '21X' // 9ad1 #569 - '177G' // 9ad2 #4608 - '137Y' // 9ad3 #3586 - '241E' // 9ad4 #6270 - 'b53X' // 9ad5-9ad7 #1401 - '69A' // 9ad8 #1794 - '252M' // 9ad9 #6564 - 'A' // 9ada - 'a1M' // 9adb-9adc #38 - 'A' // 9add - '1M' // 9ade #38 - '21X' // 9adf #569 - '37L' // 9ae0 #973 - '21Y' // 9ae1 #570 - '37L' // 9ae2 #973 - '21X' // 9ae3 #569 - '1M' // 9ae4 #38 - '247Z' // 9ae5 #6447 - '131W' // 9ae6 #3428 - '1H' // 9ae7 #33 - 'A' // 9ae8 - '1H' // 9ae9 #33 - '257G' // 9aea #6688 - '6K' // 9aeb #166 - '1H' // 9aec #33 - '84B' // 9aed #2185 - '206N' // 9aee #5369 - '6K' // 9aef #166 - 'A' // 9af0 - '1H' // 9af1 #33 - '6A' // 9af2 #156 - '1H' // 9af3 #33 - '6K' // 9af4 #166 - '1H' // 9af5 #33 - 'A' // 9af6 - '1H' // 9af7 #33 - 'A' // 9af8 - '6K' // 9af9 #166 - '1H' // 9afa #33 - '6K' // 9afb #166 - 'A' // 9afc - '6A' // 9afd #156 - 'A' // 9afe - '19U' // 9aff #514 - 'a1H' // 9b00-9b01 #33 - '19U' // 9b02 #514 - 'a6K' // 9b03-9b04 #166 - '1H' // 9b05 #33 - '216V' // 9b06 #5637 - 'A' // 9b07 - '6K' // 9b08 #166 - '19U' // 9b09 #514 - 'A' // 9b0a - 'a1H' // 9b0b-9b0c #33 - '65A' // 9b0d #1690 - '1H' // 9b0e #33 - '84E' // 9b0f #2188 - '41G' // 9b10 #1072 - 'A' // 9b11 - '1H' // 9b12 #33 - '8M' // 9b13 #220 - '32M' // 9b14 #844 - 'A' // 9b15 - '1H' // 9b16 #33 - 'A' // 9b17 - '6K' // 9b18 #166 - '1H' // 9b19 #33 - '131U' // 9b1a #3426 - 'b1H' // 9b1b-9b1d #33 - 'A' // 9b1e - '6K' // 9b1f #166 - '1H' // 9b20 #33 - 'A' // 9b21 - 'a6K' // 9b22-9b23 #166 - 'A' // 9b24 - '199J' // 9b25 #5183 - '1H' // 9b26 #33 - '67D' // 9b27 #1745 - '6K' // 9b28 #166 - '6A' // 9b29 #156 - '43J' // 9b2a #1127 - 'a1H' // 9b2b-9b2c #33 - 'a6A' // 9b2d-9b2e #156 - '6K' // 9b2f #166 - 'A' // 9b30 - '167F' // 9b31 #4347 - '6K' // 9b32 #166 - '1H' // 9b33 #33 - '19U' // 9b34 #514 - '1H' // 9b35 #33 - 'A' // 9b36 - '1H' // 9b37 #33 - 'A' // 9b38 - '19U' // 9b39 #514 - '1H' // 9b3a #33 - '6K' // 9b3b #166 - '204F' // 9b3c #5309 - '1H' // 9b3d #33 - 'aA' // 9b3e-9b3f - '32M' // 9b40 #844 - '130B' // 9b41 #3381 - '189Z' // 9b42 #4939 - '43J' // 9b43 #1127 - '131V' // 9b44 #3427 - '191D' // 9b45 #4969 - '21Y' // 9b46 #570 - '8M' // 9b47 #220 - '6K' // 9b48 #166 - '8M' // 9b49 #220 - 'A' // 9b4a - 'a6A' // 9b4b-9b4c #156 - 'a6K' // 9b4d-9b4e #166 - '155Z' // 9b4f #4055 - '32M' // 9b50 #844 - '6K' // 9b51 #166 - 'aA' // 9b52-9b53 - '215Y' // 9b54 #5614 - '6A' // 9b55 #156 - '41G' // 9b56 #1072 - '1H' // 9b57 #33 - '6K' // 9b58 #166 - 'A' // 9b59 - '220H' // 9b5a #5727 - '6A' // 9b5b #156 - '8M' // 9b5c #220 - 'A' // 9b5d - '1H' // 9b5e #33 - '21Y' // 9b5f #570 - '17Q' // 9b60 #458 - '41G' // 9b61 #1072 - '8M' // 9b62 #220 - '1H' // 9b63 #33 - 'A' // 9b64 - 'a1H' // 9b65-9b66 #33 - 'A' // 9b67 - '6A' // 9b68 #156 - '17Q' // 9b69 #458 - 'd1H' // 9b6a-9b6e #33 - '67D' // 9b6f #1745 - 'aA' // 9b70-9b71 - 'a1H' // 9b72-9b73 #33 - '6A' // 9b74 #156 - 'a1H' // 9b75-9b76 #33 - '126N' // 9b77 #3289 - 'a1H' // 9b78-9b79 #33 - 'bA' // 9b7a-9b7c - '17Q' // 9b7d #458 - 'A' // 9b7e - '19U' // 9b7f #514 - '6K' // 9b80 #166 - '17Q' // 9b81 #458 - 'A' // 9b82 - '6A' // 9b83 #156 - 'b1H' // 9b84-9b86 #33 - '6A' // 9b87 #156 - '21Y' // 9b88 #570 - 'a1H' // 9b89-9b8a #33 - '6K' // 9b8b #166 - 'A' // 9b8c - '6A' // 9b8d #156 - '84A' // 9b8e #2184 - '19U' // 9b8f #514 - '6A' // 9b90 #156 - '143Z' // 9b91 #3743 - 'a6A' // 9b92-9b93 #156 - '1H' // 9b94 #33 - '17Q' // 9b95 #458 - '1H' // 9b96 #33 - '6A' // 9b97 #156 - 'aA' // 9b98-9b99 - '1H' // 9b9a #33 - 'aA' // 9b9b-9b9c - '19U' // 9b9d #514 - '1H' // 9b9e #33 - '43J' // 9b9f #1127 - '6K' // 9ba0 #166 - 'A' // 9ba1 - '17Q' // 9ba2 #458 - 'bA' // 9ba3-9ba5 - 'a1H' // 9ba6-9ba7 #33 - '83Z' // 9ba8 #2183 - '1H' // 9ba9 #33 - '137W' // 9baa #3584 - '83Y' // 9bab #2182 - '1H' // 9bac #33 - '130F' // 9bad #3385 - '220F' // 9bae #5725 - 'A' // 9baf - '6K' // 9bb0 #166 - '41G' // 9bb1 #1072 - '1H' // 9bb2 #33 - 'A' // 9bb3 - '1H' // 9bb4 #33 - 'aA' // 9bb5-9bb6 - '1H' // 9bb7 #33 - '6K' // 9bb8 #166 - '1H' // 9bb9 #33 - 'A' // 9bba - 'a1H' // 9bbb-9bbc #33 - 'A' // 9bbd - 'a1H' // 9bbe-9bbf #33 - 'a6A' // 9bc0-9bc1 #156 - 'A' // 9bc2 - '17Q' // 9bc3 #458 - 'aA' // 9bc4-9bc5 - 'b6A' // 9bc6-9bc8 #156 - '136E' // 9bc9 #3540 - '65A' // 9bca #1690 - 'bA' // 9bcb-9bcd - '1H' // 9bce #33 - '19U' // 9bcf #514 - 'b1H' // 9bd0-9bd2 #33 - '21Y' // 9bd3 #570 - '6A' // 9bd4 #156 - '17Q' // 9bd5 #458 - '84D' // 9bd6 #2187 - '6A' // 9bd7 #156 - '1H' // 9bd8 #33 - '17Q' // 9bd9 #458 - 'A' // 9bda - '84C' // 9bdb #2186 - 'A' // 9bdc - '6A' // 9bdd #156 - 'A' // 9bde - '1H' // 9bdf #33 - 'A' // 9be0 - 'a6A' // 9be1-9be2 #156 - '1H' // 9be3 #33 - '83W' // 9be4 #2180 - '6A' // 9be5 #156 - 'A' // 9be6 - '6A' // 9be7 #156 - '142E' // 9be8 #3696 - '32M' // 9be9 #844 - '6A' // 9bea #156 - '1H' // 9beb #33 - 'A' // 9bec - '17Q' // 9bed #458 - 'a1H' // 9bee-9bef #33 - 'a53W' // 9bf0-9bf1 #1400 - 'a1H' // 9bf2-9bf3 #33 - '32M' // 9bf4 #844 - '1H' // 9bf5 #33 - 'A' // 9bf6 - '32N' // 9bf7 #845 - 'b1H' // 9bf8-9bfa #33 - 'aA' // 9bfb-9bfc - '32N' // 9bfd #845 - 'A' // 9bfe - '32N' // 9bff #845 - '1H' // 9c00 #33 - 'A' // 9c01 - '53W' // 9c02 #1400 - 'A' // 9c03 - '1H' // 9c04 #33 - 'A' // 9c05 - '32N' // 9c06 #845 - 'A' // 9c07 - 'b32N' // 9c08-9c0a #845 - '1H' // 9c0b #33 - '37J' // 9c0c #971 - '43H' // 9c0d #1125 - 'A' // 9c0e - '1H' // 9c0f #33 - '43G' // 9c10 #1124 - '1H' // 9c11 #33 - '43H' // 9c12 #1125 - '19T' // 9c13 #513 - '1H' // 9c14 #33 - '43G' // 9c15 #1124 - '1H' // 9c16 #33 - 'A' // 9c17 - 'b1H' // 9c18-9c1a #33 - '37J' // 9c1b #971 - '19T' // 9c1c #513 - 'a1H' // 9c1d-9c1e #33 - '37K' // 9c1f #972 - '21W' // 9c20 #568 - '19T' // 9c21 #513 - '1H' // 9c22 #33 - '19T' // 9c23 #513 - '43I' // 9c24 #1126 - '43H' // 9c25 #1125 - '37J' // 9c26 #971 - '1H' // 9c27 #33 - 'a19T' // 9c28-9c29 #513 - '1H' // 9c2a #33 - 'aA' // 9c2b-9c2c - '43I' // 9c2d #1126 - '1H' // 9c2e #33 - '37J' // 9c2f #971 - '1H' // 9c30 #33 - '19T' // 9c31 #513 - '43G' // 9c32 #1124 - '37K' // 9c33 #972 - 'A' // 9c34 - 'b19T' // 9c35-9c37 #513 - 'A' // 9c38 - '43I' // 9c39 #1126 - '19T' // 9c3a #513 - '83S' // 9c3b #2176 - 'A' // 9c3c - '19T' // 9c3d #513 - '4N' // 9c3e #117 - 'A' // 9c3f - '83V' // 9c40 #2179 - '1I' // 9c41 #34 - 'A' // 9c42 - 'a1I' // 9c43-9c44 #34 - 'a4N' // 9c45-9c46 #117 - '53T' // 9c47 #1397 - '37I' // 9c48 #970 - '53T' // 9c49 #1397 - '1I' // 9c4a #34 - 'bA' // 9c4b-9c4d - '1I' // 9c4e #34 - '32L' // 9c4f #843 - '1I' // 9c50 #34 - 'A' // 9c51 - '4N' // 9c52 #117 - '83Q' // 9c53 #2174 - '4N' // 9c54 #117 - 'A' // 9c55 - '4N' // 9c56 #117 - '124Z' // 9c57 #3249 - '4N' // 9c58 #117 - '37K' // 9c59 #972 - 'b1I' // 9c5a-9c5c #34 - '4N' // 9c5d #117 - '1I' // 9c5e #34 - 'a4N' // 9c5f-9c60 #117 - '1I' // 9c61 #34 - 'A' // 9c62 - '4N' // 9c63 #117 - '8M' // 9c64 #220 - '1I' // 9c65 #34 - 'A' // 9c66 - 'a4N' // 9c67-9c68 #117 - 'b1I' // 9c69-9c6b #34 - 'A' // 9c6c - 'a1I' // 9c6d-9c6e #34 - 'A' // 9c6f - '1I' // 9c70 #34 - 'A' // 9c71 - '37I' // 9c72 #970 - 'aA' // 9c73-9c74 - '4N' // 9c75 #117 - '1I' // 9c76 #34 - '137U' // 9c77 #3582 - '37I' // 9c78 #970 - 'A' // 9c79 - '4N' // 9c7a #117 - '37I' // 9c7b #970 - '83R' // 9c7c #2175 - 'aA' // 9c7d-9c7e - 'a8M' // 9c7f-9c80 #220 - '3Y' // 9c81 #102 - 'a6F' // 9c82-9c83 #161 - 'A' // 9c84 - 'g6F' // 9c85-9c8c #161 - '3H' // 9c8d #85 - 'd6F' // 9c8e-9c92 #161 - 'A' // 9c93 - 'g6F' // 9c94-9c9b #161 - '2C' // 9c9c #54 - 'A' // 9c9d - 'e6F' // 9c9e-9ca3 #161 - '3W' // 9ca4 #100 - 'b6F' // 9ca5-9ca7 #161 - '3G' // 9ca8 #84 - '6F' // 9ca9 #161 - 'A' // 9caa - '6F' // 9cab #161 - 'A' // 9cac - 'a6F' // 9cad-9cae #161 - 'aA' // 9caf-9cb0 - 'f6F' // 9cb1-9cb7 #161 - '2K' // 9cb8 #62 - 'd6F' // 9cb9-9cbd #161 - 'A' // 9cbe - 'a6F' // 9cbf-9cc0 #161 - 'aA' // 9cc1-9cc2 - '6F' // 9cc3 #161 - '3G' // 9cc4 #84 - 'b6F' // 9cc5-9cc7 #161 - 'A' // 9cc8 - 'h6F' // 9cc9-9cd1 #161 - 'A' // 9cd2 - 'g6F' // 9cd3-9cda #161 - 'A' // 9cdb - 'a6F' // 9cdc-9cdd #161 - '3G' // 9cde #84 - '6F' // 9cdf #161 - 'A' // 9ce0 - 'b6F' // 9ce1-9ce3 #161 - 'A' // 9ce4 - '205Z' // 9ce5 #5355 - '1I' // 9ce6 #34 - '83O' // 9ce7 #2172 - 'A' // 9ce8 - '83U' // 9ce9 #2178 - 'A' // 9cea - 'a1I' // 9ceb-9cec #34 - 'A' // 9ced - 'a6F' // 9cee-9cef #161 - '1I' // 9cf0 #34 - 'A' // 9cf1 - '4N' // 9cf2 #117 - '201R' // 9cf3 #5243 - '182U' // 9cf4 #4752 - 'A' // 9cf5 - '64N' // 9cf6 #1677 - '1I' // 9cf7 #34 - 'A' // 9cf8 - '1I' // 9cf9 #34 - 'aA' // 9cfa-9cfb - 'a53U' // 9cfc-9cfd #1398 - 'cA' // 9cfe-9d01 - '83P' // 9d02 #2173 - '4N' // 9d03 #117 - 'aA' // 9d04-9d05 - 'a4N' // 9d06-9d07 #117 - '27V' // 9d08 #723 - '155Y' // 9d09 #4054 - 'A' // 9d0a - '1I' // 9d0b #34 - '21W' // 9d0c #568 - 'A' // 9d0d - '1I' // 9d0e #34 - 'A' // 9d0f - '37K' // 9d10 #972 - '1I' // 9d11 #34 - '27V' // 9d12 #723 - 'aA' // 9d13-9d14 - '4N' // 9d15 #117 - '21W' // 9d16 #568 - '4N' // 9d17 #117 - '1I' // 9d18 #34 - 'aA' // 9d19-9d1a - '48V' // 9d1b #1269 - '1I' // 9d1c #34 - '4N' // 9d1d #117 - '27V' // 9d1e #723 - '4N' // 9d1f #117 - 'A' // 9d20 - '21W' // 9d21 #568 - 'A' // 9d22 - '4N' // 9d23 #117 - 'aA' // 9d24-9d25 - '48V' // 9d26 #1269 - 'A' // 9d27 - '181A' // 9d28 #4706 - 'A' // 9d29 - 'b1I' // 9d2a-9d2c #34 - 'aA' // 9d2d-9d2e - 'a4N' // 9d2f-9d30 #117 - 'A' // 9d31 - 'a1I' // 9d32-9d33 #34 - '4N' // 9d34 #117 - 'aA' // 9d35-9d36 - '53U' // 9d37 #1398 - 'A' // 9d38 - '21W' // 9d39 #568 - '1I' // 9d3a #34 - '187I' // 9d3b #4870 - '1I' // 9d3c #34 - '4N' // 9d3d #117 - '1I' // 9d3e #34 - '131T' // 9d3f #3425 - 'A' // 9d40 - '1I' // 9d41 #34 - '4N' // 9d42 #117 - '1I' // 9d43 #34 - '32L' // 9d44 #843 - 'c1I' // 9d45-9d48 #34 - '21W' // 9d49 #568 - '1I' // 9d4a #34 - 'bA' // 9d4b-9d4d - '21W' // 9d4e #568 - 'A' // 9d4f - '4N' // 9d50 #117 - '48V' // 9d51 #1269 - 'a4N' // 9d52-9d53 #117 - '1I' // 9d54 #34 - 'cA' // 9d55-9d58 - '27V' // 9d59 #723 - 'aA' // 9d5a-9d5b - '83T' // 9d5c #2177 - '66E' // 9d5d #1720 - '4N' // 9d5e #117 - '27V' // 9d5f #723 - 'a83M' // 9d60-9d61 #2170 - 'c1I' // 9d62-9d65 #34 - 'bA' // 9d66-9d68 - '1I' // 9d69 #34 - '4N' // 9d6a #117 - '1I' // 9d6b #34 - '66E' // 9d6c #1720 - 'a21W' // 9d6d-9d6e #568 - '4N' // 9d6f #117 - '27V' // 9d70 #723 - 'A' // 9d71 - '64N' // 9d72 #1677 - '1I' // 9d73 #34 - 'aA' // 9d74-9d75 - '1I' // 9d76 #34 - '15G' // 9d77 #396 - 'aA' // 9d78-9d79 - '49N' // 9d7a #1287 - '1I' // 9d7b #34 - '32L' // 9d7c #843 - 'A' // 9d7d - '37H' // 9d7e #969 - 'cA' // 9d7f-9d82 - '32L' // 9d83 #843 - '49N' // 9d84 #1287 - 'A' // 9d85 - '1I' // 9d86 #34 - '15G' // 9d87 #396 - 'A' // 9d88 - '37H' // 9d89 #969 - '1I' // 9d8a #34 - 'aA' // 9d8b-9d8c - 'a1I' // 9d8d-9d8e #34 - '254Y' // 9d8f #6628 - 'aA' // 9d90-9d91 - '37H' // 9d92 #969 - '15G' // 9d93 #396 - 'A' // 9d94 - '1I' // 9d95 #34 - '15G' // 9d96 #396 - '1I' // 9d97 #34 - '15G' // 9d98 #396 - '1I' // 9d99 #34 - '15G' // 9d9a #396 - 'eA' // 9d9b-9da0 - '15G' // 9da1 #396 - '53V' // 9da2 #1399 - 'A' // 9da3 - '1I' // 9da4 #34 - '53V' // 9da5 #1399 - 'bA' // 9da6-9da8 - '83N' // 9da9 #2171 - '1I' // 9daa #34 - 'a15G' // 9dab-9dac #396 - 'A' // 9dad - '1I' // 9dae #34 - '179V' // 9daf #4675 - 'A' // 9db0 - 'a15G' // 9db1-9db2 #396 - 'A' // 9db3 - '155C' // 9db4 #4032 - '15G' // 9db5 #396 - 'aA' // 9db6-9db7 - '49N' // 9db8 #1287 - 'b15G' // 9db9-9dbb #396 - '37H' // 9dbc #969 - '32L' // 9dbd #843 - 'A' // 9dbe - '15G' // 9dbf #396 - 'b10P' // 9dc0-9dc2 #275 - '1I' // 9dc3 #34 - '43F' // 9dc4 #1123 - 'A' // 9dc5 - '1I' // 9dc6 #34 - '32K' // 9dc7 #842 - '19S' // 9dc8 #512 - '32K' // 9dc9 #842 - '1I' // 9dca #34 - 'cA' // 9dcb-9dce - '1I' // 9dcf #34 - 'bA' // 9dd0-9dd2 - '10P' // 9dd3 #275 - '32J' // 9dd4 #841 - '1I' // 9dd5 #34 - '10P' // 9dd6 #275 - '131R' // 9dd7 #3423 - 'A' // 9dd8 - 'a10P' // 9dd9-9dda #275 - 'bA' // 9ddb-9ddd - '10P' // 9dde #275 - '32K' // 9ddf #842 - '1I' // 9de0 #34 - 'A' // 9de1 - '19S' // 9de2 #512 - '1I' // 9de3 #34 - 'A' // 9de4 - 'a10P' // 9de5-9de6 #275 - '1I' // 9de7 #34 - '19S' // 9de8 #512 - '1I' // 9de9 #34 - 'A' // 9dea - '1I' // 9deb #34 - 'A' // 9dec - 'a1I' // 9ded-9dee #34 - 'a10P' // 9def-9df0 #275 - 'A' // 9df1 - '83J' // 9df2 #2167 - 'a10P' // 9df3-9df4 #275 - 'bA' // 9df5-9df7 - '83G' // 9df8 #2164 - '181S' // 9df9 #4724 - '83I' // 9dfa #2166 - 'A' // 9dfb - '24L' // 9dfc #635 - '10P' // 9dfd #275 - '1I' // 9dfe #34 - '19S' // 9dff #512 - 'aA' // 9e00-9e01 - '10P' // 9e02 #275 - 'cA' // 9e03-9e06 - '10P' // 9e07 #275 - 'aA' // 9e08-9e09 - '32K' // 9e0a #842 - 'A' // 9e0b - '24L' // 9e0c #635 - '1I' // 9e0d #34 - '32J' // 9e0e #841 - 'A' // 9e0f - '1I' // 9e10 #34 - '32K' // 9e11 #842 - '1I' // 9e12 #34 - 'aA' // 9e13-9e14 - '10P' // 9e15 #275 - '1I' // 9e16 #34 - 'A' // 9e17 - '19S' // 9e18 #512 - '1I' // 9e19 #34 - '43F' // 9e1a #1123 - '10P' // 9e1b #275 - '32J' // 9e1c #841 - '10P' // 9e1d #275 - '43F' // 9e1e #1123 - '137V' // 9e1f #3583 - '6F' // 9e20 #161 - '3I' // 9e21 #86 - '6F' // 9e22 #161 - '4C' // 9e23 #106 - 'A' // 9e24 - '2W' // 9e25 #74 - '3H' // 9e26 #85 - 'A' // 9e27 - 'd6F' // 9e28-9e2c #161 - '2Y' // 9e2d #76 - 'a6F' // 9e2e-9e2f #161 - 'c6S' // 9e30-9e33 #174 - 'A' // 9e34 - 'f6S' // 9e35-9e3b #174 - 'A' // 9e3c - '3X' // 9e3d #101 - '6S' // 9e3e #174 - '2L' // 9e3f #63 - 'd6S' // 9e40-9e44 #174 - '2L' // 9e45 #63 - 'h6S' // 9e46-9e4e #174 - '2L' // 9e4f #63 - 'A' // 9e50 - '6S' // 9e51 #174 - 'A' // 9e52 - '6S' // 9e53 #174 - 'A' // 9e54 - 'c6S' // 9e55-9e58 #174 - 'A' // 9e59 - 'b6S' // 9e5a-9e5c #174 - 'A' // 9e5d - 'e6S' // 9e5e-9e63 #174 - '2R' // 9e64 #69 - 'A' // 9e65 - 'h6S' // 9e66-9e6e #174 - 'A' // 9e6f - '2L' // 9e70 #63 - '6S' // 9e71 #174 - 'A' // 9e72 - '6S' // 9e73 #174 - 'A' // 9e74 - '83F' // 9e75 #2163 - 'aA' // 9e76-9e77 - '252Y' // 9e78 #6576 - '162R' // 9e79 #4229 - '1I' // 9e7a #34 - '32J' // 9e7b #841 - '131S' // 9e7c #3424 - '177F' // 9e7d #4607 - '6S' // 9e7e #174 - '191K' // 9e7f #4976 - 'a10P' // 9e80-9e81 #275 - '53Q' // 9e82 #1394 - '10P' // 9e83 #275 - 'a32J' // 9e84-9e85 #841 - '6S' // 9e86 #174 - 'a53Q' // 9e87-9e88 #1394 - 'aA' // 9e89-9e8a - 'a21V' // 9e8b-9e8c #567 - 'A' // 9e8d - 'a2F' // 9e8e-9e8f #57 - '53S' // 9e90 #1396 - '21V' // 9e91 #567 - '131Q' // 9e92 #3422 - '83H' // 9e93 #2165 - 'A' // 9e94 - '37F' // 9e95 #967 - '37G' // 9e96 #968 - '220M' // 9e97 #5732 - '19R' // 9e98 #511 - 'aA' // 9e99-9e9a - '2F' // 9e9b #57 - 'A' // 9e9c - '32I' // 9e9d #840 - '19R' // 9e9e #511 - '159H' // 9e9f #4141 - 'A' // 9ea0 - '19S' // 9ea1 #512 - '24L' // 9ea2 #635 - 'A' // 9ea3 - '21V' // 9ea4 #567 - '41C' // 9ea5 #1068 - '83K' // 9ea6 #2168 - 'A' // 9ea7 - '19R' // 9ea8 #511 - 'a21V' // 9ea9-9eaa #567 - '19S' // 9eab #512 - '19R' // 9eac #511 - '37G' // 9ead #968 - '2F' // 9eae #57 - '37G' // 9eaf #968 - '2F' // 9eb0 #57 - '24L' // 9eb1 #635 - 'A' // 9eb2 - '2F' // 9eb3 #57 - '32I' // 9eb4 #840 - '41C' // 9eb5 #1068 - 'A' // 9eb6 - '19S' // 9eb7 #512 - '49M' // 9eb8 #1286 - '252D' // 9eb9 #6555 - '256C' // 9eba #6658 - '212I' // 9ebb #5520 - '231U' // 9ebc #6026 - '143Y' // 9ebd #3742 - '32I' // 9ebe #840 - '21V' // 9ebf #567 - 'A' // 9ec0 - '24L' // 9ec1 #635 - 'A' // 9ec2 - '224I' // 9ec3 #5832 - '143H' // 9ec4 #3725 - 'A' // 9ec5 - '19R' // 9ec6 #511 - '24L' // 9ec7 #635 - '2F' // 9ec8 #57 - '6S' // 9ec9 #174 - 'A' // 9eca - '2F' // 9ecb #57 - '37G' // 9ecc #968 - '32I' // 9ecd #840 - '194M' // 9ece #5056 - '169Y' // 9ecf #4418 - '21V' // 9ed0 #567 - '224J' // 9ed1 #5833 - '258X' // 9ed2 #6731 - '6S' // 9ed3 #174 - '83E' // 9ed4 #2162 - '49M' // 9ed5 #1286 - '6S' // 9ed6 #174 - 'A' // 9ed7 - '191X' // 9ed8 #4989 - '254O' // 9ed9 #6618 - '19S' // 9eda #512 - '155X' // 9edb #4053 - '32I' // 9edc #840 - '21V' // 9edd #567 - '239M' // 9ede #6226 - 'a21V' // 9edf-9ee0 #567 - 'A' // 9ee1 - '53S' // 9ee2 #1396 - 'A' // 9ee3 - '2F' // 9ee4 #57 - '11Q' // 9ee5 #302 - 'A' // 9ee6 - '11Q' // 9ee7 #302 - '184L' // 9ee8 #4795 - 'a6S' // 9ee9-9eea #174 - 'A' // 9eeb - 'a2F' // 9eec-9eed #57 - '24M' // 9eee #636 - '121G' // 9eef #3152 - '2F' // 9ef0 #57 - '37F' // 9ef1 #967 - '2F' // 9ef2 #57 - '53R' // 9ef3 #1395 - '83D' // 9ef4 #2161 - '2F' // 9ef5 #57 - '11Q' // 9ef6 #302 - '24M' // 9ef7 #636 - '19R' // 9ef8 #511 - '11Q' // 9ef9 #302 - 'A' // 9efa - 'a11Q' // 9efb-9efc #302 - '24M' // 9efd #636 - '37F' // 9efe #967 - '24M' // 9eff #636 - 'aA' // 9f00-9f01 - '19R' // 9f02 #511 - '2F' // 9f03 #57 - 'bA' // 9f04-9f06 - 'a53P' // 9f07-9f08 #1393 - '24M' // 9f09 #636 - 'A' // 9f0a - '6S' // 9f0b #174 - 'A' // 9f0c - '6S' // 9f0d #174 - '169X' // 9f0e #4417 - '2F' // 9f0f #57 - '11Q' // 9f10 #302 - 'a2F' // 9f11-9f12 #57 - '195M' // 9f13 #5082 - '49M' // 9f14 #1286 - '24M' // 9f15 #636 - '2F' // 9f16 #57 - '37F' // 9f17 #967 - 'A' // 9f18 - '11Q' // 9f19 #302 - 'a2F' // 9f1a-9f1b #57 - 'bA' // 9f1c-9f1e - '2F' // 9f1f #57 - '191W' // 9f20 #4988 - '2F' // 9f21 #57 - '11Q' // 9f22 #302 - 'bA' // 9f23-9f25 - '19R' // 9f26 #511 - '24L' // 9f27 #635 - 'A' // 9f28 - '53R' // 9f29 #1395 - 'a2F' // 9f2a-9f2b #57 - '11Q' // 9f2c #302 - 'aA' // 9f2d-9f2e - '11Q' // 9f2f #302 - 'A' // 9f30 - '11Q' // 9f31 #302 - '2F' // 9f32 #57 - 'A' // 9f33 - '24M' // 9f34 #636 - 'aA' // 9f35-9f36 - '11Q' // 9f37 #302 - 'A' // 9f38 - '11Q' // 9f39 #302 - '2F' // 9f3a #57 - '190K' // 9f3b #4950 - '2F' // 9f3c #57 - 'a11Q' // 9f3d-9f3e #302 - '2F' // 9f3f #57 - 'A' // 9f40 - '11Q' // 9f41 #302 - 'A' // 9f42 - '2F' // 9f43 #57 - 'a19R' // 9f44-9f45 #511 - 'a2F' // 9f46-9f47 #57 - 'aA' // 9f48-9f49 - '41C' // 9f4a #1068 - '148J' // 9f4b #3857 - 'a83L' // 9f4c-9f4d #2169 - '53P' // 9f4e #1393 - '27U' // 9f4f #722 - '83A' // 9f50 #2158 - '6S' // 9f51 #174 - '169W' // 9f52 #4416 - '32G' // 9f53 #838 - '27U' // 9f54 #722 - 'a2F' // 9f55-9f56 #57 - '27U' // 9f57 #722 - '2F' // 9f58 #57 - '37E' // 9f59 #966 - '32G' // 9f5a #838 - 'A' // 9f5b - '37E' // 9f5c #966 - 'a2F' // 9f5d-9f5e #57 - '37D' // 9f5f #965 - '27U' // 9f60 #722 - '41C' // 9f61 #1068 - '83B' // 9f62 #2159 - '53O' // 9f63 #1392 - 'aA' // 9f64-9f65 - '53O' // 9f66 #1392 - '82X' // 9f67 #2155 - '2F' // 9f68 #57 - '32G' // 9f69 #838 - '37D' // 9f6a #965 - 'A' // 9f6b - '37D' // 9f6c #965 - 'd2F' // 9f6d-9f71 #57 - '27U' // 9f72 #722 - '2F' // 9f73 #57 - 'A' // 9f74 - '2F' // 9f75 #57 - '27U' // 9f76 #722 - '37D' // 9f77 #965 - 'aA' // 9f78-9f79 - '2F' // 9f7a #57 - 'aA' // 9f7b-9f7c - '2F' // 9f7d #57 - 'A' // 9f7e - '82Z' // 9f7f #2157 - 'a6S' // 9f80-9f81 #174 - 'A' // 9f82 - '50I' // 9f83 #1308 - '3N' // 9f84 #91 - 'g50I' // 9f85-9f8c #1308 - '227I' // 9f8d #5910 - '37E' // 9f8e #966 - '2F' // 9f8f #57 - '155W' // 9f90 #4052 - '32H' // 9f91 #839 - '2F' // 9f92 #57 - 'A' // 9f93 - '32H' // 9f94 #839 - '82W' // 9f95 #2154 - '32H' // 9f96 #839 - '32G' // 9f97 #838 - '83C' // 9f98 #2160 - '121H' // 9f99 #3153 - '3G' // 9f9a #84 - '50I' // 9f9b #1308 - '177E' // 9f9c #4606 - '2F' // 9f9d #57 - '247Y' // 9f9e #6446 - '82Y' // 9f9f #2156 - '32H' // 9fa0 #839 - '2F' // 9fa1 #57 - '32H' // 9fa2 #839 - '2F' // 9fa3 #57 - '37E' // 9fa4 #966 - '32G' // 9fa5 #838 - 'm43E' // 9fa6-9fb3 #1122 - '2F' // 9fb4 #57 - 'fA' // 9fb5-9fbb - 'f2F' // 9fbc-9fc2 #57 - 'A' // 9fc3 - '2F' // 9fc4 #57 - 'A' // 9fc5 - '2F' // 9fc6 #57 - 'd43E' // 9fc7-9fcb #1122 - '2F' // 9fcc #57 - 'bA' // 9fcd-9fcf - '43E' // 9fd0 #1122 - '1tA' // 9fd1-9fff - '44t73I' // a000-a48c #1906 - 'bA' // a48d-a48f - '2b73I' // a490-a4c6 #1906 - 'hA' // a4c7-a4cf - '1u72E' // a4d0-a4ff #1876 - '11m265A' // a500-a62b #6890 - 'sA' // a62c-a63f - '1tE' // a640-a66e #4 - '41X' // a66f #1089 - '1uE' // a670-a69f #4 - '3i71P' // a6a0-a6f7 #1861 - 'gA' // a6f8-a6ff - '7tE' // a700-a7ca #4 - 'dA' // a7cb-a7cf - 'aE' // a7d0-a7d1 #4 - 'A' // a7d2 - 'E' // a7d3 #4 - 'A' // a7d4 - 'dE' // a7d5-a7d9 #4 - 'wA' // a7da-a7f1 - 'mE' // a7f2-a7ff #4 - '1r264X' // a800-a82c #6887 - 'bA' // a82d-a82f - 'b263Q' // a830-a832 #6854 - 'b263R' // a833-a835 #6855 - 'c263S' // a836-a839 #6856 - 'eA' // a83a-a83f - '2c264R' // a840-a877 #6881 - 'gA' // a878-a87f - '2q72U' // a880-a8c5 #1892 - 'gA' // a8c6-a8cd - 'k72U' // a8ce-a8d9 #1892 - 'eA' // a8da-a8df - 'p17L' // a8e0-a8f0 #453 - '27H' // a8f1 #709 - '17L' // a8f2 #453 - '263U' // a8f3 #6858 - 'j17L' // a8f4-a8fe #453 - '36O' // a8ff #950 - '1s72B' // a900-a92d #1873 - '262H' // a92e #6819 - '72B' // a92f #1873 - '1i72T' // a930-a953 #1891 - 'jA' // a954-a95e - '72T' // a95f #1891 - '1eA' // a960-a97f - '2y50U' // a980-a9cd #1320 - 'A' // a9ce - '263J' // a9cf #6847 - 'i50U' // a9d0-a9d9 #1320 - 'cA' // a9da-a9dd - 'a50U' // a9de-a9df #1320 - '1d42D' // a9e0-a9fe #1095 - 'A' // a9ff - '2b42A' // aa00-aa36 #1092 - 'hA' // aa37-aa3f - 'm42A' // aa40-aa4d #1092 - 'aA' // aa4e-aa4f - 'i42A' // aa50-aa59 #1092 - 'aA' // aa5a-aa5b - 'c42A' // aa5c-aa5f #1092 - '1e42D' // aa60-aa7f #1095 - '2n73A' // aa80-aac2 #1898 - 'wA' // aac3-aada - 'd73A' // aadb-aadf #1898 - 'v50Y' // aae0-aaf6 #1324 - 'iA' // aaf7-ab00 - 'e3R' // ab01-ab06 #95 - 'aA' // ab07-ab08 - 'e3R' // ab09-ab0e #95 - 'aA' // ab0f-ab10 - 'e3R' // ab11-ab16 #95 - 'hA' // ab17-ab1f - 'f3R' // ab20-ab26 #95 - 'A' // ab27 - 'f3R' // ab28-ab2e #95 - 'A' // ab2f - '2gE' // ab30-ab6b #4 - 'cA' // ab6c-ab6f - '3a50Q' // ab70-abbf #1316 - '1s50Y' // abc0-abed #1324 - 'aA' // abee-abef - 'i50Y' // abf0-abf9 #1324 - 'eA' // abfa-abff - '14W' // ac00 #386 - '1A' // ac01 #26 - 'a27F' // ac02-ac03 #707 - 'X' // ac04 #23 - 'a27F' // ac05-ac06 #707 - '1W' // ac07 #48 - 'W' // ac08 #22 - 'f27F' // ac09-ac0f #707 - 'Z' // ac10 #25 - 'V' // ac11 #21 - 'U' // ac12 #20 - '1C' // ac13 #28 - 'U' // ac14 #20 - 'Z' // ac15 #25 - 'U' // ac16 #20 - 'a27F' // ac17-ac18 #707 - '1A' // ac19 #26 - '1W' // ac1a #48 - '27F' // ac1b #707 - 'X' // ac1c #23 - '1A' // ac1d #26 - 'a27F' // ac1e-ac1f #707 - '2B' // ac20 #53 - 'b27F' // ac21-ac23 #707 - 'Y' // ac24 #24 - 'g5U' // ac25-ac2c #150 - '3M' // ac2d #90 - '5U' // ac2e #150 - '1P' // ac2f #41 - '5U' // ac30 #150 - '1G' // ac31 #32 - 'e5U' // ac32-ac37 #150 - '2I' // ac38 #60 - 'f5U' // ac39-ac3f #150 - '1W' // ac40 #48 - 'k5U' // ac41-ac4c #150 - '1P' // ac4d #41 - '1g5U' // ac4e-ac6f #150 - 'X' // ac70 #23 - '1C' // ac71 #28 - 'a5U' // ac72-ac73 #150 - 'Z' // ac74 #25 - 'a5U' // ac75-ac76 #150 - '1G' // ac77 #32 - 'W' // ac78 #22 - 'f5U' // ac79-ac7f #150 - 'Z' // ac80 #25 - 'U' // ac81 #20 - '5U' // ac82 #150 - 'Z' // ac83 #25 - 'd5U' // ac84-ac88 #150 - '1E' // ac89 #30 - 'a5U' // ac8a-ac8b #150 - 'X' // ac8c #23 - 'b5U' // ac8d-ac8f #150 - '1C' // ac90 #28 - 'b5U' // ac91-ac93 #150 - '1J' // ac94 #35 - 'f5U' // ac95-ac9b #150 - '1E' // ac9c #30 - 'a5U' // ac9d-ac9e #150 - '1G' // ac9f #32 - '1A' // aca0 #26 - 'f5U' // aca1-aca7 #150 - 'W' // aca8 #22 - '1A' // aca9 #26 - '1D' // acaa #29 - '5U' // acab #150 - 'W' // acac #22 - 'b5U' // acad-acaf #150 - '1A' // acb0 #26 - 'f5U' // acb1-acb7 #150 - '1C' // acb8 #28 - '1G' // acb9 #32 - 'a5U' // acba-acbb #150 - '1D' // acbc #29 - 'X' // acbd #23 - 'b5U' // acbe-acc0 #150 - '1E' // acc1 #30 - 'a5U' // acc2-acc3 #150 - 'Z' // acc4 #25 - 'z5U' // acc5-acdf #150 - '14W' // ace0 #386 - 'Y' // ace1 #24 - 'a6E' // ace2-ace3 #160 - 'Y' // ace4 #24 - 'a6E' // ace5-ace6 #160 - '1C' // ace7 #28 - 'W' // ace8 #22 - 'f6E' // ace9-acef #160 - 'a1G' // acf0-acf1 #32 - '6E' // acf2 #160 - 'V' // acf3 #21 - '6E' // acf4 #160 - 'X' // acf5 #23 - '2B' // acf6 #53 - 'd6E' // acf7-acfb #160 - 'X' // acfc #23 - '1G' // acfd #32 - 'a6E' // acfe-acff #160 - 'X' // ad00 #23 - 'b6E' // ad01-ad03 #160 - '1E' // ad04 #30 - 'f6E' // ad05-ad0b #160 - '1W' // ad0c #48 - 'c6E' // ad0d-ad10 #160 - 'Z' // ad11 #25 - 'i6E' // ad12-ad1b #160 - '1D' // ad1c #29 - 'v6E' // ad1d-ad33 #160 - 'U' // ad34 #20 - 's6E' // ad35-ad48 #160 - '1F' // ad49 #31 - 'e6E' // ad4a-ad4f #160 - 'Z' // ad50 #25 - 'z6E' // ad51-ad6b #160 - 'X' // ad6c #23 - 'Z' // ad6d #25 - 'a6E' // ad6e-ad6f #160 - 'W' // ad70 #22 - 'a6E' // ad71-ad72 #160 - '1G' // ad73 #32 - 'V' // ad74 #21 - '1E' // ad75 #30 - '2I' // ad76 #60 - 'e6E' // ad77-ad7c #160 - '1E' // ad7d #30 - '6E' // ad7e #160 - '1C' // ad7f #28 - '6E' // ad80 #160 - 'V' // ad81 #21 - 'e6E' // ad82-ad87 #160 - '1W' // ad88 #48 - 'b6E' // ad89-ad8b #160 - '1A' // ad8c #26 - 'b6E' // ad8d-ad8f #160 - '2B' // ad90 #53 - 'j6E' // ad91-ad9b #160 - 'g5O' // ad9c-ada3 #144 - '1W' // ada4 #48 - 'z5O' // ada5-adbf #144 - 'W' // adc0 #22 - 'b5O' // adc1-adc3 #144 - '1P' // adc4 #41 - 'b5O' // adc5-adc7 #144 - '3M' // adc8 #90 - 'i5O' // adc9-add2 #144 - '3M' // add3 #90 - 'g5O' // add4-addb #144 - 'W' // addc #22 - 'b5O' // addd-addf #144 - 'U' // ade0 #20 - 'b5O' // ade1-ade3 #144 - '1P' // ade4 #41 - 'r5O' // ade5-adf7 #144 - 'X' // adf8 #23 - 'V' // adf9 #21 - 'a5O' // adfa-adfb #144 - 'Z' // adfc #25 - 'b5O' // adfd-adff #144 - 'Z' // ae00 #25 - '1W' // ae01 #48 - 'e5O' // ae02-ae07 #144 - 'Z' // ae08 #25 - '1A' // ae09 #26 - '5O' // ae0a #144 - '1F' // ae0b #31 - '5O' // ae0c #144 - '1F' // ae0d #31 - 'e5O' // ae0e-ae13 #144 - '3V' // ae14 #99 - 'z5O' // ae15-ae2f #144 - '14W' // ae30 #386 - 'b5O' // ae31-ae33 #144 - 'V' // ae34 #21 - 'b5O' // ae35-ae37 #144 - '1A' // ae38 #26 - 'f5O' // ae39-ae3f #144 - '1A' // ae40 #26 - '1P' // ae41 #41 - '5O' // ae42 #144 - '1F' // ae43 #31 - '5O' // ae44 #144 - '1E' // ae45 #30 - 'c5O' // ae46-ae49 #144 - '1D' // ae4a #29 - '5O' // ae4b #144 - 'Z' // ae4c #25 - 'a1P' // ae4d-ae4e #41 - '5O' // ae4f #144 - '1C' // ae50 #28 - 'b5O' // ae51-ae53 #144 - 'U' // ae54 #20 - '5O' // ae55 #144 - 'e12R' // ae56-ae5b #329 - '1D' // ae5c #29 - '1F' // ae5d #31 - 'b12R' // ae5e-ae60 #329 - '1E' // ae61 #30 - 'b12R' // ae62-ae64 #329 - '1W' // ae65 #48 - 'a12R' // ae66-ae67 #329 - 'Y' // ae68 #24 - 'b12R' // ae69-ae6b #329 - '3V' // ae6c #99 - 'v12R' // ae6d-ae83 #329 - '2I' // ae84 #60 - '2b12R' // ae85-aebb #329 - 'U' // aebc #20 - '1W' // aebd #48 - '1J' // aebe #35 - '12R' // aebf #329 - '3V' // aec0 #99 - 'b12R' // aec1-aec3 #329 - '1W' // aec4 #48 - 'f12R' // aec5-aecb #329 - '2I' // aecc #60 - '1J' // aecd #35 - '12R' // aece #329 - '1F' // aecf #31 - '12R' // aed0 #329 - '1P' // aed1 #41 - 'e12R' // aed2-aed7 #329 - '1A' // aed8 #26 - 'z12R' // aed9-aef3 #329 - '1C' // aef4 #28 - 'm12R' // aef5-af02 #329 - 'd19N' // af03-af07 #507 - '1J' // af08 #35 - '1h19N' // af09-af2b #507 - 'U' // af2c #20 - 'Y' // af2d #24 - 'e19N' // af2e-af33 #507 - '1C' // af34 #28 - 'f19N' // af35-af3b #507 - '1F' // af3c #31 - '1G' // af3d #32 - 'b19N' // af3e-af40 #507 - '1J' // af41 #35 - '1E' // af42 #30 - 'V' // af43 #21 - 'c19N' // af44-af47 #507 - '3M' // af48 #90 - '1J' // af49 #35 - 'r19N' // af4a-af5c #507 - '2B' // af5d #53 - 'e19N' // af5e-af63 #507 - '1F' // af64 #31 - 'z19N' // af65-af7f #507 - '2B' // af80 #53 - '1p19N' // af81-afab #507 - 'k7U' // afac-afb7 #202 - 'V' // afb8 #21 - '1P' // afb9 #41 - 'a7U' // afba-afbb #202 - '1C' // afbc #28 - 'b7U' // afbd-afbf #202 - 'U' // afc0 #20 - 'e7U' // afc1-afc6 #202 - '3V' // afc7 #99 - 'U' // afc8 #20 - '3V' // afc9 #99 - 'b7U' // afca-afcc #202 - '2B' // afcd #53 - 'e7U' // afce-afd3 #202 - '1F' // afd4 #31 - 'r7U' // afd5-afe7 #202 - '1W' // afe8 #48 - 'f7U' // afe9-afef #202 - '2B' // aff0 #53 - 'z7U' // aff1-b00b #202 - '1G' // b00c #32 - 'b7U' // b00d-b00f #202 - '1J' // b010 #35 - 'b7U' // b011-b013 #202 - '3M' // b014 #90 - 'f7U' // b015-b01b #202 - '3V' // b01c #99 - 'j7U' // b01d-b027 #202 - '3V' // b028 #99 - 'z7U' // b029-b043 #202 - '1D' // b044 #29 - 'b7U' // b045-b047 #202 - '1C' // b048 #28 - '7U' // b049 #202 - '1G' // b04a #32 - '7U' // b04b #202 - 'U' // b04c #20 - 'e7U' // b04d-b052 #202 - '1J' // b053 #35 - '1D' // b054 #29 - 'a7U' // b055-b056 #202 - '1G' // b057 #32 - 'd7U' // b058-b05c #202 - 'V' // b05d #21 - '7U' // b05e #202 - '1b5T' // b05f-b07b #149 - 'V' // b07c #21 - '2I' // b07d #60 - 'a5T' // b07e-b07f #149 - '1F' // b080 #31 - 'b5T' // b081-b083 #149 - '1F' // b084 #31 - 'f5T' // b085-b08b #149 - 'U' // b08c #20 - 'j5T' // b08d-b097 #149 - 'X' // b098 #23 - 'U' // b099 #20 - '1C' // b09a #28 - '5T' // b09b #149 - '1A' // b09c #26 - 'b5T' // b09d-b09f #149 - '1A' // b0a0 #26 - '2B' // b0a1 #53 - 'e5T' // b0a2-b0a7 #149 - 'Z' // b0a8 #25 - 'U' // b0a9 #20 - '5T' // b0aa #149 - '1E' // b0ab #30 - 'U' // b0ac #20 - '1C' // b0ad #28 - 'U' // b0ae #20 - '1E' // b0af #30 - '5T' // b0b0 #149 - '2I' // b0b1 #60 - '5T' // b0b2 #149 - '1E' // b0b3 #30 - 'X' // b0b4 #23 - '2B' // b0b5 #53 - 'a5T' // b0b6-b0b7 #149 - 'U' // b0b8 #20 - 'b5T' // b0b9-b0bb #149 - '1C' // b0bc #28 - 'f5T' // b0bd-b0c3 #149 - '1G' // b0c4 #32 - '1F' // b0c5 #31 - 'a5T' // b0c6-b0c7 #149 - 'a1D' // b0c8-b0c9 #29 - 'e5T' // b0ca-b0cf #149 - 'Y' // b0d0 #24 - 'n5T' // b0d1-b0df #149 - '1P' // b0e0 #41 - 'c5T' // b0e1-b0e4 #149 - 'Y' // b0e5 #24 - '1g5T' // b0e6-b107 #149 - '1A' // b108 #26 - '1J' // b109 #35 - 'a5T' // b10a-b10b #149 - '1F' // b10c #31 - 'b5T' // b10d-b10f #149 - 'V' // b110 #21 - 'a5T' // b111-b112 #149 - '1C' // b113 #28 - 'c5T' // b114-b117 #149 - 'V' // b118 #21 - 'a5T' // b119-b11a #149 - '1J' // b11b #35 - 'c5T' // b11c-b11f #149 - 'b8L' // b120-b122 #219 - 'U' // b123 #20 - 'Z' // b124 #25 - '1C' // b125 #28 - 'a8L' // b126-b127 #219 - '1J' // b128 #35 - 'b8L' // b129-b12b #219 - '1E' // b12c #30 - 'f8L' // b12d-b133 #219 - 'a3M' // b134-b135 #90 - '8L' // b136 #219 - 'W' // b137 #22 - '8L' // b138 #219 - '1W' // b139 #48 - 'e8L' // b13a-b13f #219 - 'W' // b140 #22 - '1C' // b141 #28 - 'a8L' // b142-b143 #219 - 'Z' // b144 #25 - 'j8L' // b145-b14f #219 - 'Y' // b150 #24 - 'b8L' // b151-b153 #219 - '1J' // b154 #35 - 'Y' // b155 #24 - 'a8L' // b156-b157 #219 - '3M' // b158 #90 - '1d8L' // b159-b177 #219 - 'Z' // b178 #25 - 'U' // b179 #20 - 'a8L' // b17a-b17b #219 - 'V' // b17c #21 - 'b8L' // b17d-b17f #219 - 'V' // b180 #21 - 'f8L' // b181-b187 #219 - '1G' // b188 #32 - 'c8L' // b189-b18c #219 - 'V' // b18d #21 - 'c8L' // b18e-b191 #219 - 'V' // b192 #21 - 'Y' // b193 #24 - '1E' // b194 #30 - 'r8L' // b195-b1a7 #219 - '1E' // b1a8 #30 - '1h8L' // b1a9-b1cb #219 - '1G' // b1cc #32 - 'h8L' // b1cd-b1d5 #219 - 'q19M' // b1d6-b1e7 #506 - '1F' // b1e8 #31 - 's19M' // b1e9-b1fc #506 - '2B' // b1fd #53 - 'e19M' // b1fe-b203 #506 - '1A' // b204 #26 - '1W' // b205 #48 - 'a19M' // b206-b207 #506 - 'W' // b208 #22 - 'b19M' // b209-b20b #506 - '1C' // b20c #28 - 'f19M' // b20d-b213 #506 - '1G' // b214 #32 - '2I' // b215 #60 - 'i19M' // b216-b21f #506 - '1E' // b220 #30 - '2b19M' // b221-b257 #506 - '1E' // b258 #30 - 'z19M' // b259-b273 #506 - 'Z' // b274 #25 - 'f19M' // b275-b27b #506 - 'g4V' // b27c-b283 #125 - '1E' // b284 #30 - 'j4V' // b285-b28f #125 - 'W' // b290 #22 - '1J' // b291 #35 - 'a4V' // b292-b293 #125 - 'X' // b294 #23 - 'b4V' // b295-b297 #125 - '1A' // b298 #26 - '1P' // b299 #41 - 'e4V' // b29a-b29f #125 - '2I' // b2a0 #60 - 'c4V' // b2a1-b2a4 #125 - 'Z' // b2a5 #25 - '1D' // b2a6 #29 - 'b4V' // b2a7-b2a9 #125 - '3M' // b2aa #90 - '4V' // b2ab #125 - '1F' // b2ac #31 - 'z4V' // b2ad-b2c7 #125 - 'X' // b2c8 #23 - 'Y' // b2c9 #24 - 'a4V' // b2ca-b2cb #125 - 'V' // b2cc #21 - 'b4V' // b2cd-b2cf #125 - '1D' // b2d0 #29 - 'f4V' // b2d1-b2d7 #125 - '1A' // b2d8 #26 - '1C' // b2d9 #28 - '4V' // b2da #125 - '1G' // b2db #32 - '4V' // b2dc #125 - 'Y' // b2dd #24 - 'e4V' // b2de-b2e3 #125 - '14W' // b2e4 #386 - 'U' // b2e5 #20 - '1J' // b2e6 #35 - '4V' // b2e7 #125 - 'Z' // b2e8 #25 - 'a4V' // b2e9-b2ea #125 - 'a1A' // b2eb-b2ec #26 - '1C' // b2ed #28 - '1G' // b2ee #32 - 'd4V' // b2ef-b2f3 #125 - '1A' // b2f4 #26 - 'W' // b2f5 #22 - '4V' // b2f6 #125 - 'U' // b2f7 #20 - '4V' // b2f8 #125 - 'Z' // b2f9 #25 - 'a4V' // b2fa-b2fb #125 - '2B' // b2fc #53 - 'a4V' // b2fd-b2fe #125 - '1E' // b2ff #30 - 'X' // b300 #23 - '1J' // b301 #35 - 'a4V' // b302-b303 #125 - '1G' // b304 #32 - 'b4V' // b305-b307 #125 - '3V' // b308 #99 - 'f4V' // b309-b30f #125 - '2I' // b310 #60 - '3V' // b311 #99 - '4V' // b312 #125 - '1A' // b313 #26 - '3V' // b314 #99 - '1E' // b315 #30 - 'e4V' // b316-b31b #125 - '3V' // b31c #99 - '1j4V' // b31d-b341 #125 - 'q7F' // b342-b353 #187 - 'Z' // b354 #25 - 'V' // b355 #21 - 'a7F' // b356-b357 #187 - '1A' // b358 #26 - 'b7F' // b359-b35b #187 - '1D' // b35c #29 - 'a7F' // b35d-b35e #187 - '3M' // b35f #90 - 'c7F' // b360-b363 #187 - '1D' // b364 #29 - '1P' // b365 #41 - '7F' // b366 #187 - '1C' // b367 #28 - '7F' // b368 #187 - '1C' // b369 #28 - 'c7F' // b36a-b36d #187 - '1F' // b36e #31 - '7F' // b36f #187 - 'Z' // b370 #25 - '1E' // b371 #30 - 'a7F' // b372-b373 #187 - '1C' // b374 #28 - 'b7F' // b375-b377 #187 - 'V' // b378 #21 - 'f7F' // b379-b37f #187 - '2B' // b380 #53 - 'a7F' // b381-b382 #187 - '1W' // b383 #48 - '7F' // b384 #187 - '1J' // b385 #35 - 'e7F' // b386-b38b #187 - '1W' // b38c #48 - '2b7F' // b38d-b3c3 #187 - 'X' // b3c4 #23 - 'W' // b3c5 #22 - 'a7F' // b3c6-b3c7 #187 - 'Y' // b3c8 #24 - 'a7F' // b3c9-b3ca #187 - '1C' // b3cb #28 - 'W' // b3cc #22 - 'f7F' // b3cd-b3d3 #187 - 'a1E' // b3d4-b3d5 #30 - '7F' // b3d6 #187 - '2I' // b3d7 #60 - '7F' // b3d8 #187 - 'X' // b3d9 #23 - '1c7F' // b3da-b3f7 #187 - 'c12Q' // b3f8-b3fb #328 - 'Y' // b3fc #24 - 'r12Q' // b3fd-b40f #328 - 'Y' // b410 #24 - 'f12Q' // b411-b417 #328 - 'Z' // b418 #25 - 'b12Q' // b419-b41b #328 - 'Z' // b41c #25 - 'b12Q' // b41d-b41f #328 - 'W' // b420 #22 - 'f12Q' // b421-b427 #328 - '1D' // b428 #29 - 'W' // b429 #22 - 'a12Q' // b42a-b42b #328 - '1W' // b42c #48 - '1h12Q' // b42d-b44f #328 - '1A' // b450 #26 - '1F' // b451 #31 - 'a12Q' // b452-b453 #328 - '1D' // b454 #29 - 'b12Q' // b455-b457 #328 - 'Y' // b458 #24 - 'f12Q' // b459-b45f #328 - '1E' // b460 #30 - '1W' // b461 #48 - 'b12Q' // b462-b464 #328 - '1D' // b465 #29 - 'e12Q' // b466-b46b #328 - '1P' // b46c #41 - 'r12Q' // b46d-b47f #328 - '1P' // b480 #41 - '1h12Q' // b481-b4a3 #328 - 'V' // b4a4 #21 - 'q6D' // b4a5-b4b6 #159 - '1D' // b4b7 #29 - 'g6D' // b4b8-b4bf #159 - 'U' // b4c0 #20 - 'f6D' // b4c1-b4c7 #159 - '1P' // b4c8 #41 - 'r6D' // b4c9-b4db #159 - 'X' // b4dc #23 - 'Y' // b4dd #24 - 'a6D' // b4de-b4df #159 - '1A' // b4e0 #26 - 'a6D' // b4e1-b4e2 #159 - '1D' // b4e3 #29 - 'X' // b4e4 #23 - 'f6D' // b4e5-b4eb #159 - '1C' // b4ec #28 - '1G' // b4ed #32 - '6D' // b4ee #159 - 'V' // b4ef #21 - '6D' // b4f0 #159 - 'Z' // b4f1 #25 - '1g6D' // b4f2-b513 #159 - 'Z' // b514 #25 - '1F' // b515 #31 - 'a6D' // b516-b517 #159 - '1G' // b518 #32 - 'a6D' // b519-b51a #159 - '2B' // b51b #53 - '1D' // b51c #29 - 'f6D' // b51d-b523 #159 - '3V' // b524 #99 - '1J' // b525 #35 - '6D' // b526 #159 - '1E' // b527 #30 - '6D' // b528 #159 - 'W' // b529 #22 - '2I' // b52a #60 - 'd6D' // b52b-b52f #159 - 'W' // b530 #22 - 'U' // b531 #20 - 'a6D' // b532-b533 #159 - '1E' // b534 #30 - 'b6D' // b535-b537 #159 - 'U' // b538 #20 - 'f6D' // b539-b53f #159 - '1F' // b540 #31 - 'c6D' // b541-b544 #159 - 'U' // b545 #20 - 'e6D' // b546-b54b #159 - '1A' // b54c #26 - 'b6D' // b54d-b54f #159 - 'U' // b550 #20 - 'j6D' // b551-b55b #159 - '1W' // b55c #48 - 'a6D' // b55d-b55e #159 - '24F' // b55f #629 - '3V' // b560 #99 - '1F' // b561 #31 - '1g24F' // b562-b583 #629 - '2B' // b584 #53 - 'z24F' // b585-b59f #629 - 'Y' // b5a0 #24 - '1D' // b5a1 #29 - 'a24F' // b5a2-b5a3 #629 - 'Y' // b5a4 #24 - 'b24F' // b5a5-b5a7 #629 - 'U' // b5a8 #20 - 'i24F' // b5a9-b5b2 #629 - '2B' // b5b3 #53 - '1J' // b5b4 #35 - 'e24F' // b5b5-b5ba #629 - 'Y' // b5bb #24 - '1G' // b5bc #32 - '2s24F' // b5bd-b604 #629 - 'j31S' // b605-b60f #824 - '1A' // b610 #26 - '1C' // b611 #28 - 'e31S' // b612-b617 #824 - '2I' // b618 #60 - 'k31S' // b619-b624 #824 - '1G' // b625 #32 - '4m31S' // b626-b69b #824 - 'a1G' // b69c-b69d #32 - 'e31S' // b69e-b6a3 #824 - '3M' // b6a4 #90 - 'a31S' // b6a5-b6a6 #824 - 'c19L' // b6a7-b6aa #505 - '1F' // b6ab #31 - 'd19L' // b6ac-b6b0 #505 - '1F' // b6b1 #31 - '2i19L' // b6b2-b6ef #505 - 'U' // b6f0 #20 - '2b19L' // b6f1-b727 #505 - 'U' // b728 #20 - '1J' // b729 #35 - 'a19L' // b72a-b72b #505 - '1F' // b72c #31 - 'a19L' // b72d-b72e #505 - '1J' // b72f #35 - '1F' // b730 #31 - 'f19L' // b731-b737 #505 - '1J' // b738 #35 - 'a19L' // b739-b73a #505 - 'U' // b73b #20 - 'g19L' // b73c-b743 #505 - '1F' // b744 #31 - 'g19L' // b745-b74c #505 - 'r7T' // b74d-b75f #201 - '1C' // b760 #28 - 'b7T' // b761-b763 #201 - '3V' // b764 #99 - 'o7T' // b765-b774 #201 - '2I' // b775 #60 - 'e7T' // b776-b77b #201 - 'X' // b77c #23 - 'W' // b77d #22 - 'a7T' // b77e-b77f #201 - '1A' // b780 #26 - 'b7T' // b781-b783 #201 - '1D' // b784 #29 - 'f7T' // b785-b78b #201 - '1A' // b78c #26 - 'V' // b78d #21 - '7T' // b78e #201 - '1W' // b78f #48 - '1D' // b790 #29 - '1A' // b791 #26 - 'd7T' // b792-b796 #201 - '2I' // b797 #60 - 'Z' // b798 #25 - 'V' // b799 #21 - 'a7T' // b79a-b79b #201 - 'W' // b79c #22 - 'j7T' // b79d-b7a7 #201 - 'W' // b7a8 #22 - 'V' // b7a9 #21 - '7T' // b7aa #201 - '1D' // b7ab #29 - '1G' // b7ac #32 - 'U' // b7ad #20 - 'e7T' // b7ae-b7b3 #201 - '1P' // b7b4 #41 - 'Y' // b7b5 #24 - 'r7T' // b7b6-b7c8 #201 - 'W' // b7c9 #22 - '1g7T' // b7ca-b7eb #201 - 'X' // b7ec #23 - 'U' // b7ed #20 - 'a7T' // b7ee-b7ef #201 - 'W' // b7f0 #22 - 'b7T' // b7f1-b7f3 #201 - '1D' // b7f4 #29 - 'f7T' // b7f5-b7fb #201 - 'W' // b7fc #22 - 'V' // b7fd #21 - '7T' // b7fe #201 - '1J' // b7ff #35 - '1F' // b800 #31 - '1G' // b801 #32 - 'd7T' // b802-b806 #201 - 'V' // b807 #21 - 'Z' // b808 #25 - 'U' // b809 #20 - 'a7T' // b80a-b80b #201 - 'V' // b80c #21 - 'b10I' // b80d-b80f #268 - '1E' // b810 #30 - 'f10I' // b811-b817 #268 - '1E' // b818 #30 - '1P' // b819 #41 - '10I' // b81a #268 - '1C' // b81b #28 - 'g10I' // b81c-b823 #268 - 'aZ' // b824-b825 #25 - 'a10I' // b826-b827 #268 - 'W' // b828 #22 - 'b10I' // b829-b82b #268 - '1D' // b82c #29 - 'f10I' // b82d-b833 #268 - '1C' // b834 #28 - '1D' // b835 #29 - '10I' // b836 #268 - '1J' // b837 #35 - 'Y' // b838 #24 - 'V' // b839 #21 - 'e10I' // b83a-b83f #268 - 'Y' // b840 #24 - 'z10I' // b841-b85b #268 - '14W' // b85c #386 - 'X' // b85d #23 - 'a10I' // b85e-b85f #268 - 'W' // b860 #22 - 'b10I' // b861-b863 #268 - 'U' // b864 #20 - 'f10I' // b865-b86b #268 - '1G' // b86c #32 - 'U' // b86d #20 - '10I' // b86e #268 - 'Y' // b86f #24 - '10I' // b870 #268 - 'U' // b871 #20 - '2i10I' // b872-b8af #268 - '1C' // b8b0 #28 - 'm10I' // b8b1-b8be #268 - 'l10H' // b8bf-b8cb #267 - 'Z' // b8cc #25 - 's10H' // b8cd-b8e0 #267 - '1C' // b8e1 #28 - 'e10H' // b8e2-b8e7 #267 - '1A' // b8e8 #26 - '1D' // b8e9 #29 - 'a10H' // b8ea-b8eb #267 - '1F' // b8ec #31 - 'b10H' // b8ed-b8ef #267 - '1F' // b8f0 #31 - 'f10H' // b8f1-b8f7 #267 - '1D' // b8f8 #29 - 'Y' // b8f9 #24 - '10H' // b8fa #267 - '1W' // b8fb #48 - '10H' // b8fc #267 - '2B' // b8fd #53 - 'e10H' // b8fe-b903 #267 - '1F' // b904 #31 - 'r10H' // b905-b917 #267 - '1P' // b918 #41 - 'f10H' // b919-b91f #267 - '3V' // b920 #99 - 'z10H' // b921-b93b #267 - '2B' // b93c #53 - 'z10H' // b93d-b957 #267 - '1A' // b958 #26 - '1G' // b959 #32 - 'a10H' // b95a-b95b #267 - '1G' // b95c #32 - 'b10H' // b95d-b95f #267 - 'Y' // b960 #24 - 'f10H' // b961-b967 #267 - '1F' // b968 #31 - 'c10H' // b969-b96c #267 - '1F' // b96d #31 - 'e5N' // b96e-b973 #143 - '1A' // b974 #26 - '1J' // b975 #35 - 'a5N' // b976-b977 #143 - 'Z' // b978 #25 - 'b5N' // b979-b97b #143 - 'X' // b97c #23 - 'f5N' // b97d-b983 #143 - '1A' // b984 #26 - '1G' // b985 #32 - '5N' // b986 #143 - '1F' // b987 #31 - '5N' // b988 #143 - '1C' // b989 #28 - 'c5N' // b98a-b98d #143 - '1E' // b98e #30 - '1b5N' // b98f-b9ab #143 - '14W' // b9ac #386 - 'W' // b9ad #22 - 'a5N' // b9ae-b9af #143 - '1A' // b9b0 #26 - 'b5N' // b9b1-b9b3 #143 - 'V' // b9b4 #21 - 'f5N' // b9b5-b9bb #143 - 'a1A' // b9bc-b9bd #26 - '5N' // b9be #143 - 'U' // b9bf #20 - '5N' // b9c0 #143 - 'W' // b9c1 #22 - 'e5N' // b9c2-b9c7 #143 - 'X' // b9c8 #23 - 'W' // b9c9 #22 - 'a5N' // b9ca-b9cb #143 - 'X' // b9cc #23 - '5N' // b9cd #143 - '1A' // b9ce #26 - '3M' // b9cf #90 - 'Z' // b9d0 #25 - '1C' // b9d1 #28 - 'e5N' // b9d2-b9d7 #143 - '1D' // b9d8 #29 - '1F' // b9d9 #31 - '5N' // b9da #143 - 'V' // b9db #21 - '5N' // b9dc #143 - 'aW' // b9dd-b9de #22 - 'a5N' // b9df-b9e0 #143 - '1C' // b9e1 #28 - '5N' // b9e2 #143 - '3V' // b9e3 #99 - 'Z' // b9e4 #25 - 'Y' // b9e5 #24 - 'a5N' // b9e6-b9e7 #143 - 'W' // b9e8 #22 - 'j5N' // b9e9-b9f3 #143 - '2B' // b9f4 #53 - 'U' // b9f5 #20 - '5N' // b9f6 #143 - '1W' // b9f7 #48 - '5N' // b9f8 #143 - '1D' // b9f9 #29 - '1C' // b9fa #28 - '1z5N' // b9fb-ba2f #143 - 'g6R' // ba30-ba37 #173 - 'aW' // ba38-ba39 #22 - 'a6R' // ba3a-ba3b #173 - 'V' // ba3c #21 - 'b6R' // ba3d-ba3f #173 - 'U' // ba40 #20 - 'f6R' // ba41-ba47 #173 - '1G' // ba48 #32 - 'a6R' // ba49-ba4a #173 - 'U' // ba4b #20 - '6R' // ba4c #173 - '1C' // ba4d #28 - 'e6R' // ba4e-ba53 #173 - 'X' // ba54 #23 - '1E' // ba55 #30 - 'a6R' // ba56-ba57 #173 - 'U' // ba58 #20 - 'b6R' // ba59-ba5b #173 - '1D' // ba5c #29 - 'f6R' // ba5d-ba63 #173 - '1D' // ba64 #29 - 'a6R' // ba65-ba66 #173 - '1J' // ba67 #35 - 'g6R' // ba68-ba6f #173 - '1A' // ba70 #26 - 'b6R' // ba71-ba73 #173 - 'X' // ba74 #23 - 'b6R' // ba75-ba77 #173 - '1C' // ba78 #28 - 'k6R' // ba79-ba84 #173 - 'X' // ba85 #23 - '6R' // ba86 #173 - 'U' // ba87 #20 - '1e6R' // ba88-baa7 #173 - 'X' // baa8 #23 - 'Z' // baa9 #25 - '6R' // baaa #173 - '1P' // baab #41 - 'U' // baac #20 - 'b6R' // baad-baaf #173 - 'V' // bab0 #21 - 'f6R' // bab1-bab7 #173 - 'Y' // bab8 #24 - '1P' // bab9 #41 - '6R' // baba #173 - 'W' // babb #22 - '6R' // babc #173 - '1D' // babd #29 - '1m6R' // babe-bae5 #173 - 'u14V' // bae6-bafb #385 - '1W' // bafc #48 - 'z14V' // bafd-bb17 #385 - '1D' // bb18 #29 - 'z14V' // bb19-bb33 #385 - 'X' // bb34 #23 - '1C' // bb35 #28 - '1F' // bb36 #31 - '14V' // bb37 #385 - 'X' // bb38 #23 - 'a14V' // bb39-bb3a #385 - '1D' // bb3b #29 - 'Z' // bb3c #25 - 'f14V' // bb3d-bb43 #385 - '2B' // bb44 #53 - 'a14V' // bb45-bb46 #385 - '1J' // bb47 #35 - '14V' // bb48 #385 - '1F' // bb49 #31 - 'e14V' // bb4a-bb4f #385 - 'Y' // bb50 #24 - 'b14V' // bb51-bb53 #385 - '1C' // bb54 #28 - 'b14V' // bb55-bb57 #385 - '1G' // bb58 #32 - 'i14V' // bb59-bb62 #385 - '3M' // bb63 #90 - '1q14V' // bb64-bb8f #385 - 's5M' // bb90-bba3 #142 - 'V' // bba4 #21 - 'f5M' // bba5-bbab #142 - '1E' // bbac #30 - 'r5M' // bbad-bbbf #142 - 'U' // bbc0 #20 - '2b5M' // bbc1-bbf7 #142 - 'X' // bbf8 #23 - '1C' // bbf9 #28 - 'a5M' // bbfa-bbfb #142 - '1A' // bbfc #26 - 'a5M' // bbfd-bbfe #142 - '1D' // bbff #29 - 'W' // bc00 #22 - 'f5M' // bc01-bc07 #142 - '2B' // bc08 #53 - 'a5M' // bc09-bc0a #142 - '1E' // bc0b #30 - '1G' // bc0c #32 - '1D' // bc0d #29 - '5M' // bc0e #142 - '1A' // bc0f #26 - '5M' // bc10 #142 - '1C' // bc11 #28 - 'a5M' // bc12-bc13 #142 - 'X' // bc14 #23 - '1A' // bc15 #26 - 'U' // bc16 #20 - '5M' // bc17 #142 - 'Z' // bc18 #25 - 'a5M' // bc19-bc1a #142 - '1A' // bc1b #26 - 'Z' // bc1c #25 - 'Y' // bc1d #24 - '5M' // bc1e #142 - '1J' // bc1f #35 - 'c5M' // bc20-bc23 #142 - 'aY' // bc24-bc25 #24 - '5M' // bc26 #142 - '2B' // bc27 #53 - '5M' // bc28 #142 - 'X' // bc29 #23 - 'b5M' // bc2a-bc2c #142 - '1F' // bc2d #31 - 'a5M' // bc2e-bc2f #142 - 'Z' // bc30 #25 - '1A' // bc31 #26 - 'a5M' // bc32-bc33 #142 - 'U' // bc34 #20 - 'b5M' // bc35-bc37 #142 - '1G' // bc38 #32 - 'f5M' // bc39-bc3f #142 - '1E' // bc40 #30 - 'a5M' // bc41-bc42 #142 - '1F' // bc43 #31 - '5M' // bc44 #142 - '1D' // bc45 #29 - 'b5M' // bc46-bc48 #142 - '2B' // bc49 #53 - 'c5M' // bc4a-bc4d #142 - '2a8K' // bc4e-bc83 #218 - 'Z' // bc84 #25 - '1G' // bc85 #32 - 'a8K' // bc86-bc87 #218 - 'X' // bc88 #23 - 'b8K' // bc89-bc8b #218 - 'W' // bc8c #22 - 'f8K' // bc8d-bc93 #218 - 'V' // bc94 #21 - '1A' // bc95 #26 - '8K' // bc96 #218 - '1D' // bc97 #29 - '8K' // bc98 #218 - '1W' // bc99 #48 - '1G' // bc9a #32 - 'd8K' // bc9b-bc9f #218 - 'W' // bca0 #22 - '1E' // bca1 #30 - 'a8K' // bca2-bca3 #218 - 'W' // bca4 #22 - 'b8K' // bca5-bca7 #218 - 'Y' // bca8 #24 - 'i8K' // bca9-bcb2 #218 - '1C' // bcb3 #28 - 'g8K' // bcb4-bcbb #218 - '1D' // bcbc #29 - 'Y' // bcbd #24 - 'a8K' // bcbe-bcbf #218 - '1A' // bcc0 #26 - 'b8K' // bcc1-bcc3 #218 - '1A' // bcc4 #26 - 'g8K' // bcc5-bccc #218 - '1E' // bccd #30 - 'b8K' // bcce-bcd0 #218 - 'W' // bcd1 #22 - 'b8K' // bcd2-bcd4 #218 - '2I' // bcd5 #60 - '1c8K' // bcd6-bcf3 #218 - 'X' // bcf4 #23 - 'Z' // bcf5 #25 - '1F' // bcf6 #31 - '8K' // bcf7 #218 - 'Z' // bcf8 #25 - 'b8K' // bcf9-bcfb #218 - 'W' // bcfc #22 - 'e8K' // bcfd-bd02 #218 - '12P' // bd03 #327 - 'Y' // bd04 #24 - '1C' // bd05 #28 - '12P' // bd06 #327 - 'U' // bd07 #20 - '12P' // bd08 #327 - 'W' // bd09 #22 - 'e12P' // bd0a-bd0f #327 - 'Y' // bd10 #24 - 'q12P' // bd11-bd22 #327 - '3M' // bd23 #90 - 'Y' // bd24 #24 - '1h12P' // bd25-bd47 #327 - '3V' // bd48 #99 - 'o12P' // bd49-bd58 #327 - '2I' // bd59 #60 - '1k12P' // bd5a-bd7f #327 - 'X' // bd80 #23 - '1A' // bd81 #26 - 'a12P' // bd82-bd83 #327 - 'Z' // bd84 #25 - 'b12P' // bd85-bd87 #327 - '1A' // bd88 #26 - '1F' // bd89 #31 - 'e12P' // bd8a-bd8f #327 - '1J' // bd90 #35 - 'a12P' // bd91-bd92 #327 - '1E' // bd93 #30 - '12P' // bd94 #327 - '1C' // bd95 #28 - 'b12P' // bd96-bd98 #327 - 'U' // bd99 #20 - 'u12P' // bd9a-bdaf #327 - '1i21Q' // bdb0-bdd3 #562 - '1C' // bdd4 #28 - 'z21Q' // bdd5-bdef #562 - 'W' // bdf0 #22 - 'z21Q' // bdf1-be0b #562 - 'Z' // be0c #25 - 'b21Q' // be0d-be0f #562 - '1C' // be10 #28 - 'b21Q' // be11-be13 #562 - 'Z' // be14 #25 - '1t21Q' // be15-be43 #562 - 'X' // be44 #23 - '1D' // be45 #29 - 'a21Q' // be46-be47 #562 - 'Y' // be48 #24 - 'b21Q' // be49-be4b #562 - 'V' // be4c #21 - 'f21Q' // be4d-be53 #562 - '1E' // be54 #30 - '2B' // be55 #53 - '11F' // be56 #291 - '1F' // be57 #31 - '11F' // be58 #291 - 'U' // be59 #20 - '1E' // be5a #30 - 'Y' // be5b #24 - 'c11F' // be5c-be5f #291 - 'W' // be60 #22 - '1F' // be61 #31 - 'a11F' // be62-be63 #291 - '2B' // be64 #53 - 'b11F' // be65-be67 #291 - 'Y' // be68 #24 - 'k11F' // be69-be74 #291 - '1D' // be75 #29 - 'e11F' // be76-be7b #291 - '1D' // be7c #29 - '3V' // be7d #99 - 'a11F' // be7e-be7f #291 - '2I' // be80 #60 - 'm11F' // be81-be8e #291 - '1P' // be8f #41 - '11F' // be90 #291 - '3M' // be91 #90 - 'u11F' // be92-bea7 #291 - '2I' // bea8 #60 - '1l11F' // bea9-becf #291 - '1C' // bed0 #28 - '2I' // bed1 #60 - 'a11F' // bed2-bed3 #291 - '1F' // bed4 #31 - 'a11F' // bed5-bed6 #291 - '1P' // bed7 #41 - '1W' // bed8 #48 - 'j11F' // bed9-bee3 #291 - 'a2I' // bee4-bee5 #60 - '1f11F' // bee6-bf06 #291 - '36M' // bf07 #948 - '1E' // bf08 #30 - '2b36M' // bf09-bf3f #948 - '1G' // bf40 #32 - 'n36M' // bf41-bf4f #948 - '1E' // bf50 #30 - '1C' // bf51 #28 - 'b36M' // bf52-bf54 #948 - '1E' // bf55 #30 - '3b36M' // bf56-bfa6 #948 - 'h27E' // bfa7-bfaf #706 - '2B' // bfb0 #53 - 's27E' // bfb1-bfc4 #706 - '2B' // bfc5 #53 - 'e27E' // bfc6-bfcb #706 - '1D' // bfcc #29 - '1W' // bfcd #48 - 'a27E' // bfce-bfcf #706 - 'U' // bfd0 #20 - 'b27E' // bfd1-bfd3 #706 - '1J' // bfd4 #35 - 'f27E' // bfd5-bfdb #706 - '1E' // bfdc #30 - '4c27E' // bfdd-c048 #706 - 'n6Q' // c049-c057 #172 - 'U' // c058 #20 - 'b6Q' // c059-c05b #172 - 'Y' // c05c #24 - 'b6Q' // c05d-c05f #172 - '2I' // c060 #60 - 'f6Q' // c061-c067 #172 - '1G' // c068 #32 - '1l6Q' // c069-c08f #172 - '1F' // c090 #31 - 'z6Q' // c091-c0ab #172 - '14W' // c0ac #386 - 'W' // c0ad #22 - 'a6Q' // c0ae-c0af #172 - 'Z' // c0b0 #25 - 'b6Q' // c0b1-c0b3 #172 - 'W' // c0b4 #22 - '6Q' // c0b5 #172 - '1D' // c0b6 #29 - 'd6Q' // c0b7-c0bb #172 - 'W' // c0bc #22 - '1G' // c0bd #32 - '6Q' // c0be #172 - '1W' // c0bf #48 - '1E' // c0c0 #30 - 'X' // c0c1 #23 - 'e6Q' // c0c2-c0c7 #172 - '1A' // c0c8 #26 - 'Z' // c0c9 #25 - 'a6Q' // c0ca-c0cb #172 - '1G' // c0cc #32 - 'b6Q' // c0cd-c0cf #172 - '1E' // c0d0 #30 - 'f6Q' // c0d1-c0d7 #172 - '1C' // c0d8 #28 - 'a6Q' // c0d9-c0da #172 - '3V' // c0db #99 - '6Q' // c0dc #172 - 'Z' // c0dd #25 - 'e6Q' // c0de-c0e3 #172 - 'U' // c0e4 #20 - 'f6Q' // c0e5-c0eb #172 - '1P' // c0ec #41 - 'f6Q' // c0ed-c0f3 #172 - '1E' // c0f4 #30 - 'U' // c0f5 #20 - '6Q' // c0f6 #172 - '1D' // c0f7 #29 - '6Q' // c0f8 #172 - '1W' // c0f9 #48 - 'e6Q' // c0fa-c0ff #172 - '1E' // c100 #30 - 'z6C' // c101-c11b #158 - 'X' // c11c #23 - '1A' // c11d #26 - '1E' // c11e #30 - '6C' // c11f #158 - 'X' // c120 #23 - 'b6C' // c121-c123 #158 - 'Z' // c124 #25 - 'f6C' // c125-c12b #158 - 'Y' // c12c #24 - 'U' // c12d #20 - '6C' // c12e #158 - '1G' // c12f #32 - '1F' // c130 #31 - 'X' // c131 #23 - 'e6C' // c132-c137 #158 - 'X' // c138 #23 - 'U' // c139 #20 - 'a6C' // c13a-c13b #158 - '1A' // c13c #26 - 'b6C' // c13d-c13f #158 - 'Y' // c140 #24 - 'f6C' // c141-c147 #158 - '1F' // c148 #31 - '1G' // c149 #32 - '6C' // c14a #158 - '1D' // c14b #29 - 'g6C' // c14c-c153 #158 - 'V' // c154 #21 - 'b6C' // c155-c157 #158 - '1A' // c158 #26 - 'b6C' // c159-c15b #158 - 'Y' // c15c #24 - 'i6C' // c15d-c166 #158 - '1W' // c167 #48 - 'Y' // c168 #24 - 'f6C' // c169-c16f #158 - '1E' // c170 #30 - 'f6C' // c171-c177 #158 - '2B' // c178 #53 - 'r6C' // c179-c18b #158 - 'X' // c18c #23 - '1A' // c18d #26 - 'a6C' // c18e-c18f #158 - 'W' // c190 #22 - 'b6C' // c191-c193 #158 - 'Y' // c194 #24 - 'f6C' // c195-c19b #158 - '1F' // c19c #31 - 'a6C' // c19d-c19e #158 - '1P' // c19f #41 - '6C' // c1a0 #158 - '1A' // c1a1 #26 - 'b6C' // c1a2-c1a4 #158 - '1P' // c1a5 #41 - 'u6C' // c1a6-c1bb #158 - 'g10G' // c1bc-c1c3 #266 - 'V' // c1c4 #21 - 'z10G' // c1c5-c1df #266 - '1G' // c1e0 #32 - 'z10G' // c1e1-c1fb #266 - 'W' // c1fc #22 - 'f10G' // c1fd-c203 #266 - '1W' // c204 #48 - 'g10G' // c205-c20c #266 - '1E' // c20d #30 - '10G' // c20e #266 - '1W' // c20f #48 - 'g10G' // c210-c217 #266 - 'X' // c218 #23 - 'Y' // c219 #24 - 'a10G' // c21a-c21b #266 - '1A' // c21c #26 - 'a10G' // c21d-c21e #266 - '2B' // c21f #53 - 'W' // c220 #22 - 'f10G' // c221-c227 #266 - 'U' // c228 #20 - 'a10G' // c229-c22a #266 - '1G' // c22b #32 - '10G' // c22c #266 - '1G' // c22d #32 - '10G' // c22e #266 - '1W' // c22f #48 - '10G' // c230 #266 - '3V' // c231 #99 - '1C' // c232 #28 - '1b10G' // c233-c24f #266 - '1G' // c250 #32 - 'f10G' // c251-c257 #266 - '1W' // c258 #48 - 'p10G' // c259-c269 #266 - 'a4J' // c26a-c26b #113 - 'V' // c26c #21 - 'b4J' // c26d-c26f #113 - '3V' // c270 #99 - 'b4J' // c271-c273 #113 - '1E' // c274 #30 - 'f4J' // c275-c27b #113 - '1J' // c27c #35 - 'Y' // c27d #24 - 'i4J' // c27e-c287 #113 - 'W' // c288 #22 - 'f4J' // c289-c28f #113 - '1E' // c290 #30 - 'f4J' // c291-c297 #113 - '1W' // c298 #48 - 'a4J' // c299-c29a #113 - '3M' // c29b #90 - 'g4J' // c29c-c2a3 #113 - '14W' // c2a4 #386 - 'b4J' // c2a5-c2a7 #113 - 'Y' // c2a8 #24 - 'b4J' // c2a9-c2ab #113 - 'V' // c2ac #21 - 'f4J' // c2ad-c2b3 #113 - '1D' // c2b4 #29 - 'X' // c2b5 #23 - '4J' // c2b6 #113 - '1C' // c2b7 #28 - '4J' // c2b8 #113 - 'W' // c2b9 #22 - 'a4J' // c2ba-c2bb #113 - '1W' // c2bc #48 - '1d4J' // c2bd-c2db #113 - '14W' // c2dc #386 - 'Z' // c2dd #25 - 'a4J' // c2de-c2df #113 - 'X' // c2e0 #23 - 'a4J' // c2e1-c2e2 #113 - '3V' // c2e3 #99 - 'Z' // c2e4 #25 - 'e4J' // c2e5-c2ea #113 - '1C' // c2eb #28 - 'Z' // c2ec #25 - 'V' // c2ed #21 - '4J' // c2ee #113 - '2I' // c2ef #60 - '4J' // c2f0 #113 - 'Y' // c2f1 #24 - 'c4J' // c2f2-c2f5 #113 - 'W' // c2f6 #22 - '4J' // c2f7 #113 - 'Y' // c2f8 #24 - '1F' // c2f9 #31 - 'a4J' // c2fa-c2fb #113 - '1G' // c2fc #32 - 'b4J' // c2fd-c2ff #113 - '1G' // c300 #32 - 'f4J' // c301-c307 #113 - '1E' // c308 #30 - 'c4J' // c309-c30c #113 - '1D' // c30d #29 - 'd4J' // c30e-c312 #113 - '1G' // c313 #32 - '2B' // c314 #53 - 'n4J' // c315-c323 #113 - '1J' // c324 #35 - 'c4J' // c325-c328 #113 - '2I' // c329 #60 - '4J' // c32a #113 - '2h24E' // c32b-c367 #628 - 'Y' // c368 #24 - '1E' // c369 #30 - 'a24E' // c36a-c36b #628 - '1F' // c36c #31 - 'b24E' // c36d-c36f #628 - '1G' // c370 #32 - 'f24E' // c371-c377 #628 - '1C' // c378 #28 - '1J' // c379 #35 - 'a24E' // c37a-c37b #628 - '1F' // c37c #31 - '3V' // c37d #99 - 'e24E' // c37e-c383 #628 - '1F' // c384 #31 - 'b24E' // c385-c387 #628 - '1W' // c388 #48 - '2r24E' // c389-c3cf #628 - 'g24D' // c3d0-c3d7 #627 - '1G' // c3d8 #32 - '1P' // c3d9 #41 - 'a24D' // c3da-c3db #627 - '2I' // c3dc #60 - 'a24D' // c3dd-c3de #627 - '1F' // c3df #31 - '1J' // c3e0 #35 - 'k24D' // c3e1-c3ec #627 - '3V' // c3ed #99 - 'e24D' // c3ee-c3f3 #627 - '3M' // c3f4 #90 - '2b24D' // c3f5-c42b #627 - '3M' // c42c #90 - '2b24D' // c42d-c463 #627 - '1P' // c464 #41 - '1J' // c465 #35 - 'n24D' // c466-c474 #627 - '4r31R' // c475-c4ef #823 - '1A' // c4f0 #26 - '2B' // c4f1 #53 - 'a31R' // c4f2-c4f3 #823 - 'V' // c4f4 #21 - 'b31R' // c4f5-c4f7 #823 - '1D' // c4f8 #29 - 'f31R' // c4f9-c4ff #823 - '1C' // c500 #28 - '1W' // c501 #48 - 'i31R' // c502-c50b #823 - '1W' // c50c #48 - 'i31R' // c50d-c516 #823 - 'p2V' // c517-c527 #73 - 'W' // c528 #22 - 'U' // c529 #20 - 'a2V' // c52a-c52b #73 - '1C' // c52c #28 - 'b2V' // c52d-c52f #73 - '3V' // c530 #99 - 'g2V' // c531-c538 #73 - '1E' // c539 #30 - '2V' // c53a #73 - '1J' // c53b #35 - '2V' // c53c #73 - '1P' // c53d #41 - 'e2V' // c53e-c543 #73 - 'X' // c544 #23 - 'W' // c545 #22 - 'a2V' // c546-c547 #73 - 'X' // c548 #23 - '1C' // c549 #28 - '1A' // c54a #26 - '2V' // c54b #73 - 'Z' // c54c #25 - 'e2V' // c54d-c552 #73 - '1P' // c553 #41 - 'aY' // c554-c555 #24 - '2V' // c556 #73 - '1G' // c557 #32 - 'W' // c558 #22 - 'Y' // c559 #24 - 'a2V' // c55a-c55b #73 - '3M' // c55c #90 - '2V' // c55d #73 - 'V' // c55e #21 - '2V' // c55f #73 - '1A' // c560 #26 - 'V' // c561 #21 - 'a2V' // c562-c563 #73 - 'Y' // c564 #24 - 'b2V' // c565-c567 #73 - '1C' // c568 #28 - 'f2V' // c569-c56f #73 - '1P' // c570 #41 - 'V' // c571 #21 - '2V' // c572 #73 - '3M' // c573 #90 - '2V' // c574 #73 - '1F' // c575 #31 - 'e2V' // c576-c57b #73 - 'aZ' // c57c-c57d #25 - 'a2V' // c57e-c57f #73 - '1G' // c580 #32 - 'b2V' // c581-c583 #73 - '1F' // c584 #31 - 'a2V' // c585-c586 #73 - '1E' // c587 #30 - 'c2V' // c588-c58b #73 - '1J' // c58c #35 - '2B' // c58d #53 - '2V' // c58e #73 - '3M' // c58f #90 - '2V' // c590 #73 - 'Z' // c591 #25 - 'd2V' // c592-c596 #73 - '2I' // c597 #60 - '1C' // c598 #28 - 'z2V' // c599-c5b3 #73 - 'X' // c5b4 #23 - 'V' // c5b5 #21 - 'a2V' // c5b6-c5b7 #73 - '1A' // c5b8 #26 - '1W' // c5b9 #48 - '2V' // c5ba #73 - '1D' // c5bb #29 - 'W' // c5bc #22 - '2I' // c5bd #60 - 'e2V' // c5be-c5c3 #73 - 'V' // c5c4 #21 - 'aZ' // c5c5-c5c6 #25 - 'U' // c5c7 #20 - '1A' // c5c8 #26 - '1C' // c5c9 #28 - 'a2V' // c5ca-c5cb #73 - '1P' // c5cc #41 - '2V' // c5cd #73 - '1W' // c5ce #48 - '2V' // c5cf #73 - 'X' // c5d0 #23 - '1D' // c5d1 #29 - 'a2V' // c5d2-c5d3 #73 - 'W' // c5d4 #22 - 'b2V' // c5d5-c5d7 #73 - 'Y' // c5d8 #24 - 'f2V' // c5d9-c5df #73 - '1D' // c5e0 #29 - 'a2V' // c5e1-c5e2 #73 - '1G' // c5e3 #32 - '2V' // c5e4 #73 - '2I' // c5e5 #60 - 'e2V' // c5e6-c5eb #73 - 'X' // c5ec #23 - 'Z' // c5ed #25 - '1D' // c5ee #29 - '2V' // c5ef #73 - 'X' // c5f0 #23 - 'b2V' // c5f1-c5f3 #73 - '1A' // c5f4 #26 - 'f4F' // c5f5-c5fb #109 - 'Y' // c5fc #24 - '1D' // c5fd #29 - '4F' // c5fe #109 - '1E' // c5ff #30 - 'W' // c600 #22 - 'X' // c601 #23 - 'c4F' // c602-c605 #109 - '1D' // c606 #29 - '4F' // c607 #109 - 'Z' // c608 #25 - 'f4F' // c609-c60f #109 - '1J' // c610 #35 - 'i4F' // c611-c61a #109 - '1C' // c61b #28 - 'g4F' // c61c-c623 #109 - 'X' // c624 #23 - 'Y' // c625 #24 - 'a4F' // c626-c627 #109 - '1A' // c628 #26 - 'b4F' // c629-c62b #109 - '1A' // c62c #26 - '4F' // c62d #109 - '1G' // c62e #32 - 'c4F' // c62f-c632 #109 - '1P' // c633 #41 - '1G' // c634 #32 - 'U' // c635 #20 - '4F' // c636 #109 - 'U' // c637 #20 - '4F' // c638 #109 - '1C' // c639 #28 - 'e4F' // c63a-c63f #109 - 'Z' // c640 #25 - '1W' // c641 #48 - 'a4F' // c642-c643 #109 - 'W' // c644 #22 - 'b4F' // c645-c647 #109 - '1W' // c648 #48 - 'i4F' // c649-c652 #109 - '1J' // c653 #35 - 'aV' // c654-c655 #21 - 'e4F' // c656-c65b #109 - 'Y' // c65c #24 - 'b4F' // c65d-c65f #109 - '1F' // c660 #31 - 'b4F' // c661-c663 #109 - '2B' // c664 #53 - 'r4F' // c665-c677 #109 - '1A' // c678 #26 - 'b4F' // c679-c67b #109 - '1C' // c67c #28 - 'v4F' // c67d-c693 #109 - 'X' // c694 #23 - 'V' // c695 #21 - 'a4F' // c696-c697 #109 - '2B' // c698 #53 - 'b4F' // c699-c69b #109 - '2I' // c69c #60 - 'f4F' // c69d-c6a3 #109 - '1P' // c6a4 #41 - '3V' // c6a5 #99 - '4F' // c6a6 #109 - '2B' // c6a7 #53 - '4F' // c6a8 #109 - 'X' // c6a9 #23 - 'e4F' // c6aa-c6af #109 - 'X' // c6b0 #23 - 'Y' // c6b1 #24 - 'a4F' // c6b2-c6b3 #109 - 'X' // c6b4 #23 - 'b4F' // c6b5-c6b7 #109 - 'Z' // c6b8 #25 - 'a4F' // c6b9-c6ba #109 - 'd4Q' // c6bb-c6bf #120 - 'W' // c6c0 #22 - '2B' // c6c1 #53 - '4Q' // c6c2 #120 - 'W' // c6c3 #22 - '4Q' // c6c4 #120 - '1D' // c6c5 #29 - 'e4Q' // c6c6-c6cb #120 - '1A' // c6cc #26 - '1J' // c6cd #35 - 'a4Q' // c6ce-c6cf #120 - 'X' // c6d0 #23 - 'b4Q' // c6d1-c6d3 #120 - 'Z' // c6d4 #25 - 'f4Q' // c6d5-c6db #120 - '3V' // c6dc #99 - 'b4Q' // c6dd-c6df #120 - '1D' // c6e0 #29 - 'f4Q' // c6e1-c6e7 #120 - 'V' // c6e8 #21 - 'b4Q' // c6e9-c6eb #120 - '1E' // c6ec #30 - 'b4Q' // c6ed-c6ef #120 - '1G' // c6f0 #32 - 'g4Q' // c6f1-c6f8 #120 - 'V' // c6f9 #21 - 'i4Q' // c6fa-c703 #120 - 'X' // c704 #23 - 'b4Q' // c705-c707 #120 - '1D' // c708 #29 - 'b4Q' // c709-c70b #120 - '1E' // c70c #30 - 'i4Q' // c70d-c716 #120 - '1C' // c717 #28 - '4Q' // c718 #120 - '1F' // c719 #31 - 'e4Q' // c71a-c71f #120 - 'X' // c720 #23 - 'W' // c721 #22 - 'a4Q' // c722-c723 #120 - 'V' // c724 #21 - 'b4Q' // c725-c727 #120 - 'Y' // c728 #24 - 'k4Q' // c729-c734 #120 - 'U' // c735 #20 - 'e4Q' // c736-c73b #120 - 'X' // c73c #23 - '1W' // c73d #48 - 'a4Q' // c73e-c73f #120 - 'X' // c740 #23 - 'b4Q' // c741-c743 #120 - 'X' // c744 #23 - 'f4Q' // c745-c74b #120 - 'X' // c74c #23 - '1G' // c74d #32 - 'b4Q' // c74e-c750 #120 - 'V' // c751 #21 - 'e4Q' // c752-c757 #120 - 'X' // c758 #23 - 'z4Q' // c759-c773 #120 - '14W' // c774 #386 - 'V' // c775 #21 - 'a4Q' // c776-c777 #120 - '14W' // c778 #386 - 'b4L' // c779-c77b #115 - 'X' // c77c #23 - 'U' // c77d #20 - 'd4L' // c77e-c782 #115 - '1G' // c783 #32 - 'Z' // c784 #25 - 'X' // c785 #23 - '4L' // c786 #115 - 'U' // c787 #20 - 'X' // c788 #23 - 'U' // c789 #20 - '1C' // c78a #28 - '4L' // c78b #115 - '3M' // c78c #90 - '4L' // c78d #115 - '1G' // c78e #32 - '4L' // c78f #115 - 'aX' // c790-c791 #23 - 'a4L' // c792-c793 #115 - 'Y' // c794 #24 - '4L' // c795 #115 - '1G' // c796 #32 - '4L' // c797 #115 - 'W' // c798 #22 - 'f4L' // c799-c79f #115 - 'V' // c7a0 #21 - 'W' // c7a1 #22 - '4L' // c7a2 #115 - '1P' // c7a3 #41 - '3V' // c7a4 #99 - 'X' // c7a5 #23 - '1P' // c7a6 #41 - 'd4L' // c7a7-c7ab #115 - 'Z' // c7ac #25 - '1E' // c7ad #30 - 'm4L' // c7ae-c7bb #115 - '1G' // c7bc #32 - 'c4L' // c7bd-c7c0 #115 - 'V' // c7c1 #21 - 'e4L' // c7c2-c7c7 #115 - '1E' // c7c8 #30 - 's4L' // c7c9-c7dc #115 - '3M' // c7dd #90 - '1g4L' // c7de-c7ff #115 - 'aZ' // c800-c801 #25 - 'a4L' // c802-c803 #115 - 'X' // c804 #23 - 'b4L' // c805-c807 #115 - '1A' // c808 #26 - '4L' // c809 #115 - '1C' // c80a #28 - 'd4L' // c80b-c80f #115 - '1A' // c810 #26 - 'W' // c811 #22 - '4L' // c812 #115 - '1J' // c813 #35 - '4L' // c814 #115 - 'X' // c815 #23 - '1F' // c816 #31 - 'd4L' // c817-c81b #115 - 'X' // c81c #23 - '1D' // c81d #29 - 'a4L' // c81e-c81f #115 - '1D' // c820 #29 - 'b4L' // c821-c823 #115 - '1D' // c824 #29 - 'i4L' // c825-c82e #115 - '1J' // c82f #35 - 'g4L' // c830-c837 #115 - 'W' // c838 #22 - 'b4L' // c839-c83b #115 - '1E' // c83c #30 - 'c4L' // c83d-c840 #115 - 'j19K' // c841-c84b #504 - 'Y' // c84c #24 - '1h19K' // c84d-c86f #504 - 'X' // c870 #23 - 'W' // c871 #22 - 'a19K' // c872-c873 #504 - 'V' // c874 #21 - 'b19K' // c875-c877 #504 - '1D' // c878 #29 - 'f19K' // c879-c87f #504 - 'V' // c880 #21 - '1E' // c881 #30 - 'b19K' // c882-c884 #504 - '1A' // c885 #26 - '1P' // c886 #41 - 'c19K' // c887-c88a #504 - '1A' // c88b #26 - 'Y' // c88c #24 - '2b19K' // c88d-c8c3 #504 - 'U' // c8c4 #20 - 'z19K' // c8c5-c8df #504 - 'Y' // c8e0 #24 - 'g19K' // c8e1-c8e8 #504 - 'k11E' // c8e9-c8f4 #290 - '3M' // c8f5 #90 - 'e11E' // c8f6-c8fb #290 - 'X' // c8fc #23 - 'Y' // c8fd #24 - 'a11E' // c8fe-c8ff #290 - '1A' // c900 #26 - 'b11E' // c901-c903 #290 - 'W' // c904 #22 - 'f11E' // c905-c90b #290 - 'a1C' // c90c-c90d #28 - 'b11E' // c90e-c910 #290 - 'X' // c911 #23 - 'e11E' // c912-c917 #290 - '1D' // c918 #29 - 'r11E' // c919-c92b #290 - '1G' // c92c #32 - '1h11E' // c92d-c94f #290 - '1C' // c950 #28 - 'b11E' // c951-c953 #290 - '3V' // c954 #99 - 'v11E' // c955-c96b #290 - '1D' // c96c #29 - 'f11E' // c96d-c973 #290 - '3V' // c974 #99 - 'r11E' // c975-c987 #290 - '1A' // c988 #26 - 'U' // c989 #20 - 'a11E' // c98a-c98b #290 - 'U' // c98c #20 - 'b11E' // c98d-c98f #290 - 'W' // c990 #22 - 'd11E' // c991-c995 #290 - 'a8Z' // c996-c997 #233 - 'Y' // c998 #24 - '1J' // c999 #35 - 'b8Z' // c99a-c99c #233 - '1A' // c99d #26 - '1g8Z' // c99e-c9bf #233 - '14W' // c9c0 #386 - 'Z' // c9c1 #25 - 'a8Z' // c9c2-c9c3 #233 - 'X' // c9c4 #23 - 'b8Z' // c9c5-c9c7 #233 - '1A' // c9c8 #26 - 'f8Z' // c9c9-c9cf #233 - 'U' // c9d0 #20 - 'Z' // c9d1 #25 - '8Z' // c9d2 #233 - 'U' // c9d3 #20 - '8Z' // c9d4 #233 - 'V' // c9d5 #21 - '2I' // c9d6 #60 - 'a8Z' // c9d7-c9d8 #233 - 'a1P' // c9d9-c9da #41 - '8Z' // c9db #233 - 'W' // c9dc #22 - 'Y' // c9dd #24 - 'a8Z' // c9de-c9df #233 - '1J' // c9e0 #35 - 'b8Z' // c9e1-c9e3 #233 - '1D' // c9e4 #29 - 'a8Z' // c9e5-c9e6 #233 - '1C' // c9e7 #28 - 'c8Z' // c9e8-c9eb #233 - '1P' // c9ec #41 - '2B' // c9ed #53 - 'b8Z' // c9ee-c9f0 #233 - '1D' // c9f1 #29 - 'e8Z' // c9f2-c9f7 #233 - 'V' // c9f8 #21 - 'r8Z' // c9f9-ca0b #233 - '1P' // ca0c #41 - '1a8Z' // ca0d-ca28 #233 - '2I' // ca29 #60 - '1e8Z' // ca2a-ca49 #233 - 'a13M' // ca4a-ca4b #350 - '1G' // ca4c #32 - '1F' // ca4d #31 - 'a13M' // ca4e-ca4f #350 - '1W' // ca50 #48 - 'b13M' // ca51-ca53 #350 - '1F' // ca54 #31 - 'f13M' // ca55-ca5b #350 - '2I' // ca5c #60 - 'c13M' // ca5d-ca60 #350 - '1W' // ca61 #48 - '1g13M' // ca62-ca83 #350 - '2I' // ca84 #60 - '2b13M' // ca85-cabb #350 - '1F' // cabc #31 - 'V' // cabd #21 - 'a13M' // cabe-cabf #350 - '2B' // cac0 #53 - 'b13M' // cac1-cac3 #350 - '1J' // cac4 #35 - 'f13M' // cac5-cacb #350 - '2B' // cacc #53 - 'c13M' // cacd-cad0 #350 - '3M' // cad1 #90 - '13M' // cad2 #350 - '1J' // cad3 #35 - 'd13M' // cad4-cad8 #350 - '2B' // cad9 #53 - 'y13M' // cada-caf3 #350 - '3e71B' // caf4-cb47 #1847 - '1E' // cb48 #30 - '1F' // cb49 #31 - '2r71B' // cb4a-cb90 #1847 - '2n13L' // cb91-cbd3 #349 - '1J' // cbd4 #35 - 'n13L' // cbd5-cbe3 #349 - '1C' // cbe4 #28 - '1l13L' // cbe5-cc0b #349 - 'U' // cc0c #20 - 'Y' // cc0d #24 - 'a13L' // cc0e-cc0f #349 - '1J' // cc10 #35 - 'b13L' // cc11-cc13 #349 - '1F' // cc14 #31 - 'f13L' // cc15-cc1b #349 - '1G' // cc1c #32 - 'c13L' // cc1d-cc20 #349 - '1P' // cc21 #41 - '1J' // cc22 #35 - 'd13L' // cc23-cc27 #349 - 'Z' // cc28 #25 - 'V' // cc29 #21 - 'a13L' // cc2a-cc2b #349 - 'V' // cc2c #21 - '13L' // cc2d #349 - '1D' // cc2e #29 - '13L' // cc2f #349 - 'Y' // cc30 #24 - 'f13L' // cc31-cc37 #349 - 'W' // cc38 #22 - '2I' // cc39 #60 - '13L' // cc3a #349 - '2I' // cc3b #60 - '13L' // cc3c #349 - 'a1A' // cc3d-cc3e #26 - 'd9L' // cc3f-cc43 #245 - 'W' // cc44 #22 - '1A' // cc45 #26 - 'a9L' // cc46-cc47 #245 - '3M' // cc48 #90 - 'b9L' // cc49-cc4b #245 - '1W' // cc4c #48 - 'f9L' // cc4d-cc53 #245 - '1G' // cc54 #32 - 'c9L' // cc55-cc58 #245 - '1G' // cc59 #32 - 'e9L' // cc5a-cc5f #245 - '1E' // cc60 #30 - '2b9L' // cc61-cc97 #245 - 'Z' // cc98 #25 - 'U' // cc99 #20 - 'a9L' // cc9a-cc9b #245 - 'Z' // cc9c #25 - 'b9L' // cc9d-cc9f #245 - 'W' // cca0 #22 - 'f9L' // cca1-cca7 #245 - 'V' // cca8 #21 - '1C' // cca9 #28 - '9L' // ccaa #245 - 'V' // ccab #21 - '9L' // ccac #245 - 'Z' // ccad #25 - 'e9L' // ccae-ccb3 #245 - 'X' // ccb4 #23 - '2B' // ccb5 #53 - 'a9L' // ccb6-ccb7 #245 - '1J' // ccb8 #35 - 'b9L' // ccb9-ccbb #245 - '1J' // ccbc #35 - 'r9L' // ccbd-cccf #245 - 'V' // ccd0 #21 - 'r9L' // ccd1-cce3 #245 - '1C' // cce4 #28 - 'i9L' // cce5-ccee #245 - 'x24C' // ccef-cd07 #626 - '1A' // cd08 #26 - '1D' // cd09 #29 - 'a24C' // cd0a-cd0b #626 - 'U' // cd0c #20 - 'm24C' // cd0d-cd1a #626 - '1P' // cd1b #41 - '24C' // cd1c #626 - 'W' // cd1d #22 - 'm24C' // cd1e-cd2b #626 - 'U' // cd2c #20 - '1t24C' // cd2d-cd5b #626 - 'Z' // cd5c #25 - 'z24C' // cd5d-cd77 #626 - '1P' // cd78 #41 - 'x24C' // cd79-cd91 #626 - 'a14U' // cd92-cd93 #384 - 'Z' // cd94 #25 - 'W' // cd95 #22 - 'a14U' // cd96-cd97 #384 - 'U' // cd98 #20 - 'b14U' // cd99-cd9b #384 - 'Z' // cd9c #25 - 'f14U' // cd9d-cda3 #384 - 'U' // cda4 #20 - '1J' // cda5 #35 - 'b14U' // cda6-cda8 #384 - 'W' // cda9 #22 - 'e14U' // cdaa-cdaf #384 - '1C' // cdb0 #28 - 'r14U' // cdb1-cdc3 #384 - '1P' // cdc4 #41 - 'f14U' // cdc5-cdcb #384 - '2B' // cdcc #53 - 'z14U' // cdcd-cde7 #384 - 'Z' // cde8 #25 - 'z14U' // cde9-ce03 #384 - '1G' // ce04 #32 - 'z14U' // ce05-ce1f #384 - '1A' // ce20 #26 - 'V' // ce21 #21 - 'r14U' // ce22-ce34 #384 - 'V' // ce35 #21 - 'e14U' // ce36-ce3b #384 - '1a7S' // ce3c-ce57 #200 - 'X' // ce58 #23 - 'U' // ce59 #20 - 'a7S' // ce5a-ce5b #200 - 'W' // ce5c #22 - 'b7S' // ce5d-ce5f #200 - 'U' // ce60 #20 - 'f7S' // ce61-ce67 #200 - 'Z' // ce68 #25 - '1E' // ce69 #30 - '7S' // ce6a #200 - '1E' // ce6b #30 - '7S' // ce6c #200 - 'U' // ce6d #20 - 'e7S' // ce6e-ce73 #200 - 'X' // ce74 #23 - '1E' // ce75 #30 - 'a7S' // ce76-ce77 #200 - '1C' // ce78 #28 - 'b7S' // ce79-ce7b #200 - 'V' // ce7c #21 - 'f7S' // ce7d-ce83 #200 - '1J' // ce84 #35 - 'c7S' // ce85-ce88 #200 - '2I' // ce89 #60 - 'e7S' // ce8a-ce8f #200 - 'V' // ce90 #21 - 'b7S' // ce91-ce93 #200 - '1C' // ce94 #28 - 'b7S' // ce95-ce97 #200 - '1C' // ce98 #28 - 'f7S' // ce99-ce9f #200 - 'U' // cea0 #20 - '1D' // cea1 #29 - '7S' // cea2 #200 - '1J' // cea3 #35 - 'g7S' // cea4-ceab #200 - '1W' // ceac #48 - '2b7S' // cead-cee3 #200 - '1A' // cee4 #26 - '2I' // cee5 #60 - 'a7S' // cee6-cee7 #200 - 'V' // cee8 #21 - 'b7S' // cee9-ceeb #200 - 'Y' // ceec #24 - 'b7S' // ceed-ceef #200 - 'c8J' // cef0-cef3 #217 - 'V' // cef4 #21 - '1D' // cef5 #29 - '8J' // cef6 #217 - '1D' // cef7 #29 - '1P' // cef8 #41 - 'f8J' // cef9-ceff #217 - 'W' // cf00 #22 - 'b8J' // cf01-cf03 #217 - '1E' // cf04 #30 - 'b8J' // cf05-cf07 #217 - '1E' // cf08 #30 - 'f8J' // cf09-cf0f #217 - '3M' // cf10 #90 - 'a8J' // cf11-cf12 #217 - 'Y' // cf13 #24 - 'g8J' // cf14-cf1b #217 - 'Y' // cf1c #24 - 'b8J' // cf1d-cf1f #217 - '3M' // cf20 #90 - 'n8J' // cf21-cf2f #217 - '1G' // cf30 #32 - '1h8J' // cf31-cf53 #217 - 'Z' // cf54 #25 - '1E' // cf55 #30 - 'a8J' // cf56-cf57 #217 - 'W' // cf58 #22 - 'b8J' // cf59-cf5b #217 - 'Y' // cf5c #24 - 'f8J' // cf5d-cf63 #217 - '1D' // cf64 #29 - '2I' // cf65 #60 - '8J' // cf66 #217 - '1J' // cf67 #35 - '8J' // cf68 #217 - '1D' // cf69 #29 - 'e8J' // cf6a-cf6f #217 - '1E' // cf70 #30 - 's8J' // cf71-cf84 #217 - '2I' // cf85 #60 - 'e8J' // cf86-cf8b #217 - '1C' // cf8c #28 - 't8J' // cf8d-cfa1 #217 - '1g14T' // cfa2-cfc3 #383 - '1G' // cfc4 #32 - 'z14T' // cfc5-cfdf #383 - 'V' // cfe0 #21 - '1J' // cfe1 #35 - 'a14T' // cfe2-cfe3 #383 - '1P' // cfe4 #41 - 'b14T' // cfe5-cfe7 #383 - '1D' // cfe8 #29 - 'k14T' // cfe9-cff4 #383 - '1F' // cff5 #31 - 'e14T' // cff6-cffb #383 - '1E' // cffc #30 - 'b14T' // cffd-cfff #383 - '3V' // d000 #99 - 'b14T' // d001-d003 #383 - '1F' // d004 #31 - 'r14T' // d005-d017 #383 - '1C' // d018 #28 - 'z14T' // d019-d033 #383 - '1C' // d034 #28 - '1D' // d035 #29 - 'a14T' // d036-d037 #383 - '1F' // d038 #31 - 'b14T' // d039-d03b #383 - '3M' // d03c #90 - 'm14T' // d03d-d04a #383 - 'd6P' // d04b-d04f #171 - 'Y' // d050 #24 - 'f6P' // d051-d057 #171 - '2I' // d058 #60 - 'r6P' // d059-d06b #171 - 'X' // d06c #23 - 'b6P' // d06d-d06f #171 - 'V' // d070 #21 - 'b6P' // d071-d073 #171 - '1A' // d074 #26 - 'f6P' // d075-d07b #171 - 'Y' // d07c #24 - '3M' // d07d #90 - '1k6P' // d07e-d0a3 #171 - '1A' // d0a4 #26 - '1P' // d0a5 #41 - 'a6P' // d0a6-d0a7 #171 - 'Y' // d0a8 #24 - 'b6P' // d0a9-d0ab #171 - '1D' // d0ac #29 - 'f6P' // d0ad-d0b3 #171 - '1F' // d0b4 #31 - '1P' // d0b5 #41 - '6P' // d0b6 #171 - '1E' // d0b7 #30 - '6P' // d0b8 #171 - 'V' // d0b9 #21 - 'e6P' // d0ba-d0bf #171 - 'X' // d0c0 #23 - 'V' // d0c1 #21 - 'a6P' // d0c2-d0c3 #171 - 'V' // d0c4 #21 - 'b6P' // d0c5-d0c7 #171 - 'V' // d0c8 #21 - 'f6P' // d0c9-d0cf #171 - 'aU' // d0d0-d0d1 #20 - '6P' // d0d2 #171 - '1F' // d0d3 #31 - '1W' // d0d4 #48 - 'U' // d0d5 #20 - 'e6P' // d0d6-d0db #171 - 'Z' // d0dc #25 - '1A' // d0dd #26 - 'a6P' // d0de-d0df #171 - '1F' // d0e0 #31 - 'b6P' // d0e1-d0e3 #171 - '2B' // d0e4 #53 - 'f6P' // d0e5-d0eb #171 - '1P' // d0ec #41 - '1F' // d0ed #31 - 'b6P' // d0ee-d0f0 #171 - '1C' // d0f1 #28 - 'r6P' // d0f2-d104 #171 - '1p11D' // d105-d12f #289 - 'X' // d130 #23 - '1F' // d131 #31 - 'a11D' // d132-d133 #289 - 'Y' // d134 #24 - 'b11D' // d135-d137 #289 - 'Y' // d138 #24 - 'f11D' // d139-d13f #289 - '1G' // d140 #32 - 'a11D' // d141-d142 #289 - '2B' // d143 #53 - '11D' // d144 #289 - '2I' // d145 #60 - 'e11D' // d146-d14b #289 - 'Z' // d14c #25 - 'U' // d14d #20 - 'a11D' // d14e-d14f #289 - 'W' // d150 #22 - 'b11D' // d151-d153 #289 - 'Y' // d154 #24 - 'f11D' // d155-d15b #289 - 'V' // d15c #21 - '1P' // d15d #41 - '1o11D' // d15e-d187 #289 - '1P' // d188 #41 - 'v11D' // d189-d19f #289 - 'Z' // d1a0 #25 - 'V' // d1a1 #21 - 'a11D' // d1a2-d1a3 #289 - 'U' // d1a4 #20 - 'b11D' // d1a5-d1a7 #289 - '1E' // d1a8 #30 - 'f11D' // d1a9-d1af #289 - '1F' // d1b0 #31 - '1C' // d1b1 #28 - 'a11D' // d1b2-d1b3 #289 - '24B' // d1b4 #625 - 'Z' // d1b5 #25 - '2i24B' // d1b6-d1f3 #625 - 'Y' // d1f4 #24 - '2b24B' // d1f5-d22b #625 - 'W' // d22c #22 - '2I' // d22d #60 - 'a24B' // d22e-d22f #625 - '1D' // d230 #29 - 'b24B' // d231-d233 #625 - '1D' // d234 #29 - 'f24B' // d235-d23b #625 - '1P' // d23c #41 - 'c24B' // d23d-d240 #625 - '1J' // d241 #35 - 't24B' // d242-d256 #625 - '1n16E' // d257-d27f #420 - '1C' // d280 #28 - 'z16E' // d281-d29b #420 - 'U' // d29c #20 - 'b16E' // d29d-d29f #420 - '3M' // d2a0 #90 - 'j16E' // d2a1-d2ab #420 - '1P' // d2ac #41 - 'j16E' // d2ad-d2b7 #420 - 'X' // d2b8 #23 - '1A' // d2b9 #26 - 'a16E' // d2ba-d2bb #420 - 'Y' // d2bc #24 - 'b16E' // d2bd-d2bf #420 - 'V' // d2c0 #21 - 'f16E' // d2c1-d2c7 #420 - '1E' // d2c8 #30 - '1l16E' // d2c9-d2ef #420 - 'Z' // d2f0 #25 - 'U' // d2f1 #20 - 'a16E' // d2f2-d2f3 #420 - '1D' // d2f4 #29 - 'b16E' // d2f5-d2f7 #420 - '1C' // d2f8 #28 - 'e16E' // d2f9-d2fe #420 - '4U' // d2ff #124 - 'V' // d300 #21 - 'U' // d301 #20 - 'b4U' // d302-d304 #124 - 'W' // d305 #22 - 'e4U' // d306-d30b #124 - 'Z' // d30c #25 - '1J' // d30d #35 - '3M' // d30e #90 - '4U' // d30f #124 - 'Z' // d310 #25 - 'b4U' // d311-d313 #124 - 'V' // d314 #21 - 'f4U' // d315-d31b #124 - '1J' // d31c #35 - '1D' // d31d #29 - '4U' // d31e #124 - '1F' // d31f #31 - '2B' // d320 #53 - '1G' // d321 #32 - 'b4U' // d322-d324 #124 - '2I' // d325 #60 - 'a4U' // d326-d327 #124 - 'W' // d328 #22 - 'Y' // d329 #24 - 'a4U' // d32a-d32b #124 - 'Y' // d32c #24 - 'b4U' // d32d-d32f #124 - '2B' // d330 #53 - 'f4U' // d331-d337 #124 - '1C' // d338 #28 - 'c4U' // d339-d33c #124 - '1F' // d33d #31 - '2i4U' // d33e-d37b #124 - 'W' // d37c #22 - '2I' // d37d #60 - 'a4U' // d37e-d37f #124 - '1C' // d380 #28 - 'b4U' // d381-d383 #124 - '1E' // d384 #30 - 'f4U' // d385-d38b #124 - '1G' // d38c #32 - '3M' // d38d #90 - 'b4U' // d38e-d390 #124 - '1J' // d391 #35 - 'e4U' // d392-d397 #124 - 'Z' // d398 #25 - '1G' // d399 #32 - 'a4U' // d39a-d39b #124 - 'U' // d39c #20 - 'b4U' // d39d-d39f #124 - '1F' // d3a0 #31 - 'f4U' // d3a1-d3a7 #124 - '3M' // d3a8 #90 - 'a4U' // d3a9-d3aa #124 - '1F' // d3ab #31 - '4U' // d3ac #124 - '2I' // d3ad #60 - 'e4U' // d3ae-d3b3 #124 - '1C' // d3b4 #28 - 'b4U' // d3b5-d3b7 #124 - '1A' // d3b8 #26 - 'b4U' // d3b9-d3bb #124 - 'U' // d3bc #20 - 'a4U' // d3bd-d3be #124 - 'h21P' // d3bf-d3c7 #561 - '3M' // d3c8 #90 - '1A' // d3c9 #26 - 'e21P' // d3ca-d3cf #561 - 'Y' // d3d0 #24 - 'z21P' // d3d1-d3eb #561 - 'X' // d3ec #23 - 'V' // d3ed #21 - 'a21P' // d3ee-d3ef #561 - 'W' // d3f0 #22 - 'b21P' // d3f1-d3f3 #561 - 'Y' // d3f4 #24 - 'f21P' // d3f5-d3fb #561 - 'Y' // d3fc #24 - 'c21P' // d3fd-d400 #561 - '1J' // d401 #35 - '3k21P' // d402-d45b #561 - 'Z' // d45c #25 - 'f21P' // d45d-d463 #561 - 's21O' // d464-d477 #560 - 'V' // d478 #21 - '1J' // d479 #35 - 'a21O' // d47a-d47b #560 - '1E' // d47c #30 - 'b21O' // d47d-d47f #560 - 'V' // d480 #21 - 'f21O' // d481-d487 #560 - '1A' // d488 #26 - 'a21O' // d489-d48a #560 - '1E' // d48b #30 - '21O' // d48c #560 - 'V' // d48d #21 - '3k21O' // d48e-d4e7 #560 - 'U' // d4e8 #20 - 'z21O' // d4e9-d503 #560 - 'X' // d504 #23 - 'a21O' // d505-d506 #560 - '7E' // d507 #186 - 'V' // d508 #21 - 'b7E' // d509-d50b #186 - 'Z' // d50c #25 - 'f7E' // d50d-d513 #186 - '1F' // d514 #31 - '1l7E' // d515-d53b #186 - 'Z' // d53c #25 - 'V' // d53d #21 - 'a7E' // d53e-d53f #186 - 'U' // d540 #20 - 'b7E' // d541-d543 #186 - '1A' // d544 #26 - 'g7E' // d545-d54c #186 - '2B' // d54d #53 - '7E' // d54e #186 - '1G' // d54f #32 - '7E' // d550 #186 - 'W' // d551 #22 - 'e7E' // d552-d557 #186 - '14W' // d558 #386 - '1A' // d559 #26 - 'a7E' // d55a-d55b #186 - 'X' // d55c #23 - 'b7E' // d55d-d55f #186 - 'Z' // d560 #25 - 'c7E' // d561-d564 #186 - '3V' // d565 #99 - 'a7E' // d566-d567 #186 - 'Z' // d568 #25 - 'X' // d569 #23 - '7E' // d56a #186 - 'Y' // d56b #24 - '7E' // d56c #186 - '1A' // d56d #26 - 'e7E' // d56e-d573 #186 - 'X' // d574 #23 - '1D' // d575 #29 - 'a7E' // d576-d577 #186 - '1D' // d578 #29 - 'j7E' // d579-d583 #186 - '1F' // d584 #31 - 'a7E' // d585-d586 #186 - '1G' // d587 #32 - '1A' // d588 #26 - 'Z' // d589 #25 - 'z7E' // d58a-d5a4 #186 - 'W' // d5a5 #22 - 'u7E' // d5a6-d5bb #186 - 'k4T' // d5bc-d5c7 #123 - 'W' // d5c8 #22 - '1J' // d5c9 #35 - 'a4T' // d5ca-d5cb #123 - 'Y' // d5cc #24 - 'b4T' // d5cd-d5cf #123 - '1G' // d5d0 #32 - 'f4T' // d5d1-d5d7 #123 - 'W' // d5d8 #22 - 'a4T' // d5d9-d5da #123 - '1J' // d5db #35 - '4T' // d5dc #123 - '1E' // d5dd #30 - 'e4T' // d5de-d5e3 #123 - 'V' // d5e4 #21 - '3M' // d5e5 #90 - 'a4T' // d5e6-d5e7 #123 - '1E' // d5e8 #30 - 'b4T' // d5e9-d5eb #123 - 'U' // d5ec #20 - 'i4T' // d5ed-d5f6 #123 - '1W' // d5f7 #48 - '4T' // d5f8 #123 - '3M' // d5f9 #90 - 'e4T' // d5fa-d5ff #123 - 'aY' // d600-d601 #24 - 'a4T' // d602-d603 #123 - 'Z' // d604 #25 - 'b4T' // d605-d607 #123 - '1D' // d608 #29 - 'f4T' // d609-d60f #123 - '1C' // d610 #28 - 'V' // d611 #21 - 'a4T' // d612-d613 #123 - '1D' // d614 #29 - '1A' // d615 #26 - 'e4T' // d616-d61b #123 - 'V' // d61c #21 - 'z4T' // d61d-d637 #123 - 'X' // d638 #23 - 'Y' // d639 #24 - 'a4T' // d63a-d63b #123 - 'W' // d63c #22 - 'b4T' // d63d-d63f #123 - 'U' // d640 #20 - 'f4T' // d641-d647 #123 - '1A' // d648 #26 - '1J' // d649 #35 - 'b4T' // d64a-d64c #123 - 'W' // d64d #22 - 'e4T' // d64e-d653 #123 - 'X' // d654 #23 - '1A' // d655 #26 - 'a4T' // d656-d657 #123 - '1A' // d658 #26 - 'b4T' // d659-d65b #123 - '1A' // d65c #26 - 'i4T' // d65d-d666 #123 - '3V' // d667 #99 - '4T' // d668 #123 - 'W' // d669 #22 - 'n4T' // d66a-d678 #123 - 'r12O' // d679-d68b #326 - 'X' // d68c #23 - 'V' // d68d #21 - 'p12O' // d68e-d69e #326 - '1C' // d69f #28 - '12O' // d6a0 #326 - '1F' // d6a1 #31 - 'e12O' // d6a2-d6a7 #326 - 'V' // d6a8 #21 - 'z12O' // d6a9-d6c3 #326 - 'Z' // d6c4 #25 - '3V' // d6c5 #99 - 'a12O' // d6c6-d6c7 #326 - 'Y' // d6c8 #24 - 'b12O' // d6c9-d6cb #326 - '1F' // d6cc #31 - 'f12O' // d6cd-d6d3 #326 - '1J' // d6d4 #35 - 'a12O' // d6d5-d6d6 #326 - '1W' // d6d7 #48 - 'k12O' // d6d8-d6e3 #326 - '3M' // d6e4 #90 - 'b12O' // d6e5-d6e7 #326 - '1G' // d6e8 #32 - 'r12O' // d6e9-d6fb #326 - '1C' // d6fc #28 - 'z12O' // d6fd-d717 #326 - '1D' // d718 #29 - 'f12O' // d719-d71f #326 - '1E' // d720 #30 - 'a12O' // d721-d722 #326 - 'e9K' // d723-d728 #244 - '1P' // d729 #41 - 'i9K' // d72a-d733 #244 - 'W' // d734 #22 - 's9K' // d735-d748 #244 - '1F' // d749 #31 - 'e9K' // d74a-d74f #244 - 'U' // d750 #20 - '1D' // d751 #29 - 'a9K' // d752-d753 #244 - 'Y' // d754 #24 - 'b9K' // d755-d757 #244 - '1C' // d758 #28 - '1E' // d759 #30 - 'e9K' // d75a-d75f #244 - '1F' // d760 #31 - '1D' // d761 #29 - 'b9K' // d762-d764 #244 - 'Y' // d765 #24 - 'b9K' // d766-d768 #244 - '2I' // d769 #60 - 'a9K' // d76a-d76b #244 - 'W' // d76c #22 - 'b9K' // d76d-d76f #244 - '1G' // d770 #32 - 'v9K' // d771-d787 #244 - 'Z' // d788 #25 - 'b9K' // d789-d78b #244 - '1D' // d78c #29 - 'b9K' // d78d-d78f #244 - '1D' // d790 #29 - 'f9K' // d791-d797 #244 - 'V' // d798 #21 - '1F' // d799 #31 - '9K' // d79a #244 - '1P' // d79b #41 - '9K' // d79c #244 - '3V' // d79d #99 - 'e9K' // d79e-d7a3 #244 - '328kA' // d7a4-f8ff - '35X' // f900 #933 - '82V' // f901 #2153 - '53N' // f902 #1391 - '35X' // f903 #933 - '53M' // f904 #1390 - '35X' // f905 #933 - '53N' // f906 #1391 - '53M' // f907 #1390 - 'a35X' // f908-f909 #933 - '247X' // f90a #6445 - '1f35X' // f90b-f92b #933 - '247W' // f92c #6444 - '82U' // f92d #2152 - '247U' // f92e #6442 - 'd7Q' // f92f-f933 #198 - '82S' // f934 #2150 - 'a7Q' // f935-f936 #198 - '82T' // f937 #2151 - '1g7Q' // f938-f959 #198 - '260N' // f95a #6773 - 'b7Q' // f95b-f95d #198 - '21U' // f95e #566 - '49L' // f95f #1285 - 'a7Q' // f960-f961 #198 - '21U' // f962 #566 - 'a7Q' // f963-f964 #198 - '27T' // f965 #721 - '7Q' // f966 #198 - '27T' // f967 #721 - 'd7Q' // f968-f96c #198 - '82L' // f96d #2143 - 'e7Q' // f96e-f973 #198 - '247T' // f974 #6441 - '7Q' // f975 #198 - '21U' // f976 #566 - '7Q' // f977 #198 - '21U' // f978 #566 - '27T' // f979 #721 - 'c7Q' // f97a-f97d #198 - '27T' // f97e #721 - 'a7Q' // f97f-f980 #198 - '247V' // f981 #6443 - 'g7Q' // f982-f989 #198 - '27T' // f98a #721 - 'b7Q' // f98b-f98d #198 - '27T' // f98e #721 - 'l7Q' // f98f-f99b #198 - '21U' // f99c #566 - 'a7Q' // f99d-f99e #198 - '21U' // f99f #566 - 'i7Q' // f9a0-f9a9 #198 - '49L' // f9aa #1285 - 'i7Q' // f9ab-f9b4 #198 - '21U' // f9b5 #566 - 'f7Q' // f9b6-f9bc #198 - '21U' // f9bd #566 - '82M' // f9be #2144 - 'j7Q' // f9bf-f9c9 #198 - 'e13H' // f9ca-f9cf #345 - '82J' // f9d0 #2141 - 'f13H' // f9d1-f9d7 #345 - '21T' // f9d8 #565 - 'b13H' // f9d9-f9db #345 - '21T' // f9dc #565 - '37C' // f9dd #964 - 'a13H' // f9de-f9df #345 - '37C' // f9e0 #964 - 'b13H' // f9e1-f9e3 #345 - '37C' // f9e4 #964 - 'c13H' // f9e5-f9e8 #345 - '21T' // f9e9 #565 - 'h13H' // f9ea-f9f2 #345 - '260M' // f9f3 #6772 - '21T' // f9f4 #565 - 'd13H' // f9f5-f9f9 #345 - '21T' // f9fa #565 - 'a13H' // f9fb-f9fc #345 - '21T' // f9fd #565 - '13H' // f9fe #345 - '21T' // f9ff #565 - 'e13H' // fa00-fa05 #345 - '21T' // fa06 #565 - '13H' // fa07 #345 - '37C' // fa08 #964 - '13H' // fa09 #345 - '82K' // fa0a #2142 - '13H' // fa0b #345 - '82R' // fa0c #2149 - 'A' // fa0d - 'a49L' // fa0e-fa0f #1285 - '13G' // fa10 #344 - '252O' // fa11 #6566 - '3m13G' // fa12-fa6d #344 - '5oA' // fa6e-faff - '69N' // fb00 #1807 - '82I' // fb01 #2140 - '247S' // fb02 #6440 - 'a69N' // fb03-fb04 #1807 - 'aE' // fb05-fb06 #4 - 'kA' // fb07-fb12 - 'd36P' // fb13-fb17 #951 - 'dA' // fb18-fb1c - 'y21R' // fb1d-fb36 #563 - 'A' // fb37 - 'd21R' // fb38-fb3c #563 - 'A' // fb3d - '21R' // fb3e #563 - 'A' // fb3f - 'a21R' // fb40-fb41 #563 - 'A' // fb42 - 'a21R' // fb43-fb44 #563 - 'A' // fb45 - 'i21R' // fb46-fb4f #563 - '4j4W' // fb50-fbc2 #126 - 'oA' // fbc3-fbd2 - '13x4W' // fbd3-fd3d #126 - 'a263C' // fd3e-fd3f #6840 - '3a4W' // fd40-fd8f #126 - 'aA' // fd90-fd91 - '2a4W' // fd92-fdc7 #126 - 'fA' // fdc8-fdce - '4W' // fdcf #126 - '1eA' // fdd0-fdef - 'a4W' // fdf0-fdf1 #126 - '41Y' // fdf2 #1090 - 'i4W' // fdf3-fdfc #126 - '41Y' // fdfd #1090 - 'a4W' // fdfe-fdff #126 - '262K' // fe00 #6822 - 'nA' // fe01-fe0f - 'i13G' // fe10-fe19 #344 - 'eA' // fe1a-fe1f - 'cE' // fe20-fe23 #4 - 'b261X' // fe24-fe26 #6809 - 'cE' // fe27-fe2a #4 - 'b261U' // fe2b-fe2d #6806 - 'a41X' // fe2e-fe2f #1089 - '162Q' // fe30 #4228 - '64M' // fe31 #1676 - '13G' // fe32 #344 - 'c19Q' // fe33-fe36 #510 - '69M' // fe37 #1806 - 'j19Q' // fe38-fe42 #510 - '82N' // fe43 #2145 - '19Q' // fe44 #510 - 'c13G' // fe45-fe48 #344 - 'h19Q' // fe49-fe51 #510 - '126M' // fe52 #3288 - 'A' // fe53 - '19Q' // fe54 #510 - '64M' // fe55 #1676 - 'a19Q' // fe56-fe57 #510 - '13G' // fe58 #344 - 'c19Q' // fe59-fe5c #510 - 'a131P' // fe5d-fe5e #3421 - 'g19Q' // fe5f-fe66 #510 - 'A' // fe67 - 'b19Q' // fe68-fe6a #510 - '69M' // fe6b #1806 - 'cA' // fe6c-fe6f - 'd4W' // fe70-fe74 #126 - 'A' // fe75 - '5d4W' // fe76-fefc #126 - 'aA' // fefd-fefe - '71J' // feff #1855 - 'A' // ff00 - '245O' // ff01 #6384 - '126L' // ff02 #3287 - '142Y' // ff03 #3716 - '53J' // ff04 #1387 - '162C' // ff05 #4214 - '176X' // ff06 #4599 - '53J' // ff07 #1387 - 'a245N' // ff08-ff09 #6383 - '176G' // ff0a #4582 - '169J' // ff0b #4403 - '247K' // ff0c #6432 - '205R' // ff0d #5347 - '214F' // ff0e #5569 - '213T' // ff0f #5557 - '169O' // ff10 #4408 - '184H' // ff11 #4791 - '176Y' // ff12 #4600 - '176W' // ff13 #4598 - '162G' // ff14 #4218 - '169N' // ff15 #4407 - '155L' // ff16 #4041 - '155J' // ff17 #4039 - '155K' // ff18 #4040 - '155I' // ff19 #4038 - '246T' // ff1a #6415 - '219A' // ff1b #5694 - '149L' // ff1c #3885 - '149H' // ff1d #3881 - '184E' // ff1e #4788 - '245C' // ff1f #6372 - '143J' // ff20 #3727 - '149M' // ff21 #3886 - '137C' // ff22 #3564 - '64K' // ff23 #1674 - '137D' // ff24 #3565 - '63L' // ff25 #1649 - '53K' // ff26 #1388 - '43D' // ff27 #1121 - '53K' // ff28 #1388 - '53L' // ff29 #1389 - '43D' // ff2a #1121 - 'a53L' // ff2b-ff2c #1389 - '63L' // ff2d #1649 - '125Y' // ff2e #3274 - '137H' // ff2f #3569 - '64K' // ff30 #1674 - '82O' // ff31 #2146 - '82Q' // ff32 #2148 - '137I' // ff33 #3570 - '126A' // ff34 #3276 - '82P' // ff35 #2147 - '43D' // ff36 #1121 - '53I' // ff37 #1386 - '149F' // ff38 #3879 - '81Y' // ff39 #2130 - '81L' // ff3a #2117 - '64Y' // ff3b #1688 - '81X' // ff3c #2129 - '64Y' // ff3d #1688 - '82B' // ff3e #2133 - '52Y' // ff3f #1376 - '53I' // ff40 #1386 - '32F' // ff41 #837 - '81F' // ff42 #2111 - '32F' // ff43 #837 - '52Z' // ff44 #1377 - '32F' // ff45 #837 - '80V' // ff46 #2101 - '53E' // ff47 #1382 - '53C' // ff48 #1380 - '32F' // ff49 #837 - '52V' // ff4a #1373 - '53E' // ff4b #1382 - '53C' // ff4c #1380 - '82D' // ff4d #2135 - '81R' // ff4e #2123 - '32F' // ff4f #837 - '53A' // ff50 #1378 - '80S' // ff51 #2098 - 'b81T' // ff52-ff54 #2125 - '52Z' // ff55 #1377 - '81A' // ff56 #2106 - '82G' // ff57 #2138 - '52Y' // ff58 #1376 - '53A' // ff59 #1378 - '52V' // ff5a #1373 - '52U' // ff5b #1372 - '214A' // ff5c #5564 - '52U' // ff5d #1372 - '224D' // ff5e #5827 - 'a13G' // ff5f-ff60 #344 - '81W' // ff61 #2128 - 'a82A' // ff62-ff63 #2132 - '81O' // ff64 #2120 - '82H' // ff65 #2139 - '13G' // ff66 #344 - '80X' // ff67 #2103 - '81C' // ff68 #2108 - '247R' // ff69 #6439 - '80Z' // ff6a #2105 - '252X' // ff6b #6575 - '43C' // ff6c #1120 - '81E' // ff6d #2110 - '81B' // ff6e #2107 - '53H' // ff6f #1385 - '82E' // ff70 #2136 - '81S' // ff71 #2124 - '53H' // ff72 #1385 - '43C' // ff73 #1120 - '81G' // ff74 #2112 - '43C' // ff75 #1120 - '81Q' // ff76 #2122 - '81V' // ff77 #2127 - '37B' // ff78 #963 - '52W' // ff79 #1374 - '53D' // ff7a #1381 - '53B' // ff7b #1379 - '53G' // ff7c #1384 - '37B' // ff7d #963 - '52X' // ff7e #1375 - '70U' // ff7f #1840 - '81Z' // ff80 #2131 - '81N' // ff81 #2119 - '80W' // ff82 #2102 - '53D' // ff83 #1381 - '37B' // ff84 #963 - '52X' // ff85 #1375 - '43B' // ff86 #1119 - '13G' // ff87 #344 - '70U' // ff88 #1840 - '53F' // ff89 #1383 - '53G' // ff8a #1384 - '81J' // ff8b #2115 - '37B' // ff8c #963 - '43B' // ff8d #1119 - '53B' // ff8e #1379 - '81K' // ff8f #2116 - '52W' // ff90 #1374 - '81H' // ff91 #2113 - '43B' // ff92 #1119 - '80Y' // ff93 #2104 - '80U' // ff94 #2100 - '251U' // ff95 #6546 - '252A' // ff96 #6552 - '53F' // ff97 #1383 - 'a81U' // ff98-ff99 #2126 - '81M' // ff9a #2118 - '81P' // ff9b #2121 - '81D' // ff9c #2109 - '82C' // ff9d #2134 - '82F' // ff9e #2137 - '149J' // ff9f #3883 - 'A' // ffa0 - 'w13G' // ffa1-ffb8 #344 - '80T' // ffb9 #2099 - 'd13G' // ffba-ffbe #344 - 'bA' // ffbf-ffc1 - 'e13G' // ffc2-ffc7 #344 - 'aA' // ffc8-ffc9 - 'e13G' // ffca-ffcf #344 - 'aA' // ffd0-ffd1 - 'd13G' // ffd2-ffd6 #344 - '19F' // ffd7 #499 - 'aA' // ffd8-ffd9 - 'b19F' // ffda-ffdc #499 - 'bA' // ffdd-ffdf - 'b80R' // ffe0-ffe2 #2097 - '81I' // ffe3 #2114 - '247Q' // ffe4 #6438 - '125W' // ffe5 #3272 - '247P' // ffe6 #6437 - 'A' // ffe7 - '19F' // ffe8 #499 - '43A' // ffe9 #1118 - '19F' // ffea #499 - '43A' // ffeb #1118 - '19F' // ffec #499 - '43A' // ffed #1118 - '19F' // ffee #499 - 'iA' // ffef-fff8 - 'bF' // fff9-fffb #5 - 'aE' // fffc-fffd #4 - 'aA' // fffe-ffff - 'k24I' // 10000-1000b #632 - 'A' // 1000c - 'y24I' // 1000d-10026 #632 - 'A' // 10027 - 'r24I' // 10028-1003a #632 - 'A' // 1003b - 'a24I' // 1003c-1003d #632 - 'A' // 1003e - 'n24I' // 1003f-1004d #632 - 'aA' // 1004e-1004f - 'm24I' // 10050-1005d #632 - '1gA' // 1005e-1007f - '4r24I' // 10080-100fa #632 - 'dA' // 100fb-100ff - 'b71W' // 10100-10102 #1868 - 'cA' // 10103-10106 - '1r71W' // 10107-10133 #1868 - 'bA' // 10134-10136 - 'h24I' // 10137-1013f #632 - '2zF' // 10140-1018e #5 - 'A' // 1018f - 'lF' // 10190-1019c #5 - 'bA' // 1019d-1019f - 'F' // 101a0 #5 - '1tA' // 101a1-101cf - '1sF' // 101d0-101fd #5 - '4yA' // 101fe-1027f - '1b264E' // 10280-1029c #6868 - 'bA' // 1029d-1029f - '1v263M' // 102a0-102d0 #6850 - 'nA' // 102d1-102df - '1a78O' // 102e0-102fb #2042 - 'cA' // 102fc-102ff - '1i72O' // 10300-10323 #1886 - 'hA' // 10324-1032c - 'b72O' // 1032d-1032f #1886 - 'z263Y' // 10330-1034a #6862 - 'dA' // 1034b-1034f - '1p264L' // 10350-1037a #6875 - 'dA' // 1037b-1037f - '1c73F' // 10380-1039d #1903 - 'A' // 1039e - '73F' // 1039f #1903 - '1i72P' // 103a0-103c3 #1887 - 'cA' // 103c4-103c7 - 'm72P' // 103c8-103d5 #1887 - '1oA' // 103d6-103ff - '3a263O' // 10400-1044f #6852 - '1u264U' // 10450-1047f #6884 - '1c72R' // 10480-1049d #1889 - 'aA' // 1049e-1049f - 'i72R' // 104a0-104a9 #1889 - 'eA' // 104aa-104af - '1i72Q' // 104b0-104d3 #1888 - 'cA' // 104d4-104d7 - '1i72Q' // 104d8-104fb #1888 - 'cA' // 104fc-104ff - '1m263V' // 10500-10527 #6859 - 'gA' // 10528-1052f - '1y71U' // 10530-10563 #1866 - 'jA' // 10564-1056e - '71U' // 1056f #1866 - '5mA' // 10570-105ff - '11x50W' // 10600-10736 #1322 - 'hA' // 10737-1073f - 'u50W' // 10740-10755 #1322 - 'iA' // 10756-1075f - 'g50W' // 10760-10767 #1322 - 'wA' // 10768-1077f - 'eE' // 10780-10785 #4 - 'A' // 10786 - '1oE' // 10787-107b0 #4 - 'A' // 107b1 - 'hE' // 107b2-107ba #4 - '2pA' // 107bb-107ff - 'e31T' // 10800-10805 #825 - 'aA' // 10806-10807 - '31T' // 10808 #825 - 'A' // 10809 - '1q31T' // 1080a-10835 #825 - 'A' // 10836 - 'a31T' // 10837-10838 #825 - 'bA' // 10839-1083b - '31T' // 1083c #825 - 'aA' // 1083d-1083e - '31T' // 1083f #825 - 'u71X' // 10840-10855 #1869 - 'A' // 10856 - 'h71X' // 10857-1085f #1869 - '1e264P' // 10860-1087f #6879 - '1d72L' // 10880-1089e #1883 - 'gA' // 1089f-108a6 - 'h72L' // 108a7-108af #1883 - '1uA' // 108b0-108df - 'r50T' // 108e0-108f2 #1319 - 'A' // 108f3 - 'a50T' // 108f4-108f5 #1319 - 'dA' // 108f6-108fa - 'd50T' // 108fb-108ff #1319 - '1a72S' // 10900-1091b #1890 - 'bA' // 1091c-1091e - '72S' // 1091f #1890 - 'y72F' // 10920-10939 #1877 - 'dA' // 1093a-1093e - '72F' // 1093f #1877 - '2kA' // 10940-1097f - '2c50Z' // 10980-109b7 #1325 - 'cA' // 109b8-109bb - 's50Z' // 109bc-109cf #1325 - 'aA' // 109d0-109d1 - '1s50Z' // 109d2-109ff #1325 - 'c24H' // 10a00-10a03 #631 - 'A' // 10a04 - 'a24H' // 10a05-10a06 #631 - 'dA' // 10a07-10a0b - 'g24H' // 10a0c-10a13 #631 - 'A' // 10a14 - 'b24H' // 10a15-10a17 #631 - 'A' // 10a18 - '1b24H' // 10a19-10a35 #631 - 'aA' // 10a36-10a37 - 'b24H' // 10a38-10a3a #631 - 'cA' // 10a3b-10a3e - 'i24H' // 10a3f-10a48 #631 - 'fA' // 10a49-10a4f - 'h24H' // 10a50-10a58 #631 - 'fA' // 10a59-10a5f - '1e264N' // 10a60-10a7f #6877 - '1e264K' // 10a80-10a9f #6874 - '1eA' // 10aa0-10abf - '1l72H' // 10ac0-10ae6 #1879 - 'cA' // 10ae7-10aea - 'k72H' // 10aeb-10af6 #1879 - 'hA' // 10af7-10aff - '2a71N' // 10b00-10b35 #1859 - 'bA' // 10b36-10b38 - 'f71N' // 10b39-10b3f #1859 - 'u71Z' // 10b40-10b55 #1871 - 'aA' // 10b56-10b57 - 'g71Z' // 10b58-10b5f #1871 - 'r71Y' // 10b60-10b72 #1870 - 'dA' // 10b73-10b77 - 'g71Y' // 10b78-10b7f #1870 - 'q51D' // 10b80-10b91 #1329 - 'fA' // 10b92-10b98 - 'c51D' // 10b99-10b9c #1329 - 'kA' // 10b9d-10ba8 - 'f51D' // 10ba9-10baf #1329 - '3aA' // 10bb0-10bff - '2t264O' // 10c00-10c48 #6878 - '2bA' // 10c49-10c7f - '1x51C' // 10c80-10cb2 #1328 - 'lA' // 10cb3-10cbf - '1x51C' // 10cc0-10cf2 #1328 - 'fA' // 10cf3-10cf9 - 'e51C' // 10cfa-10cff #1328 - '13mA' // 10d00-10e5f - '1dF' // 10e60-10e7e #5 - '4uA' // 10e7f-10efc - 'b4W' // 10efd-10eff #126 - '1m264M' // 10f00-10f27 #6876 - 'gA' // 10f28-10f2f - '1o264V' // 10f30-10f59 #6885 - '5cA' // 10f5a-10fdf - 'v263W' // 10fe0-10ff6 #6860 - 'hA' // 10ff7-10fff - '2y50O' // 11000-1104d #1314 - 'cA' // 1104e-11051 - '1i50O' // 11052-11075 #1314 - 'hA' // 11076-1107e - '50O' // 1107f #1314 - '2n72A' // 11080-110c2 #1872 - 'iA' // 110c3-110cc - '72A' // 110cd #1872 - 'aA' // 110ce-110cf - 'x72W' // 110d0-110e8 #1894 - 'fA' // 110e9-110ef - 'i72W' // 110f0-110f9 #1894 - 'eA' // 110fa-110ff - '1z71V' // 11100-11134 #1867 - 'A' // 11135 - 'q71V' // 11136-11147 #1867 - 'gA' // 11148-1114f - '1l264F' // 11150-11176 #6869 - 'hA' // 11177-1117f - '3q264T' // 11180-111df #6883 - 'A' // 111e0 - 's15C' // 111e1-111f4 #392 - 'jA' // 111f5-111ff - 'q72C' // 11200-11211 #1874 - 'A' // 11212 - '1s72C' // 11213-11240 #1874 - '2jA' // 11241-1127f - 'f36R' // 11280-11286 #953 - 'A' // 11287 - '36R' // 11288 #953 - 'A' // 11289 - 'c36R' // 1128a-1128d #953 - 'A' // 1128e - 'n36R' // 1128f-1129d #953 - 'A' // 1129e - 'j36R' // 1129f-112a9 #953 - 'eA' // 112aa-112af - '2f72D' // 112b0-112ea #1875 - 'dA' // 112eb-112ef - 'i72D' // 112f0-112f9 #1875 - 'eA' // 112fa-112ff - '11O' // 11300 #300 - '31U' // 11301 #826 - '11O' // 11302 #300 - '31U' // 11303 #826 - 'A' // 11304 - 'g11O' // 11305-1130c #300 - 'aA' // 1130d-1130e - 'a11O' // 1130f-11310 #300 - 'aA' // 11311-11312 - 'u11O' // 11313-11328 #300 - 'A' // 11329 - 'f11O' // 1132a-11330 #300 - 'A' // 11331 - 'a11O' // 11332-11333 #300 - 'A' // 11334 - 'd11O' // 11335-11339 #300 - 'A' // 1133a - 'a31U' // 1133b-1133c #826 - 'g11O' // 1133d-11344 #300 - 'aA' // 11345-11346 - 'a11O' // 11347-11348 #300 - 'aA' // 11349-1134a - 'b11O' // 1134b-1134d #300 - 'aA' // 1134e-1134f - '11O' // 11350 #300 - 'eA' // 11351-11356 - '11O' // 11357 #300 - 'dA' // 11358-1135c - 'f11O' // 1135d-11363 #300 - 'aA' // 11364-11365 - 'f11O' // 11366-1136c #300 - 'bA' // 1136d-1136f - 'd11O' // 11370-11374 #300 - '5hA' // 11375-113ff - '3m72M' // 11400-1145b #1884 - 'A' // 1145c - 'd72M' // 1145d-11461 #1884 - '1cA' // 11462-1147f - '2s73E' // 11480-114c7 #1902 - 'gA' // 114c8-114cf - 'i73E' // 114d0-114d9 #1902 - '6iA' // 114da-1157f - '2a72V' // 11580-115b5 #1893 - 'aA' // 115b6-115b7 - '1k72V' // 115b8-115dd #1893 - '1gA' // 115de-115ff - '2p72I' // 11600-11644 #1880 - 'jA' // 11645-1164f - 'i72I' // 11650-11659 #1880 - 'eA' // 1165a-1165f - 'l31X' // 11660-1166c #829 - 'rA' // 1166d-1167f - '2e73B' // 11680-116b9 #1899 - 'eA' // 116ba-116bf - 'i73B' // 116c0-116c9 #1899 - '18aA' // 116ca-1189f - '3d73H' // 118a0-118f2 #1905 - 'kA' // 118f3-118fe - '73H' // 118ff #1905 - '9uA' // 11900-119ff - '2s265B' // 11a00-11a47 #6891 - 'gA' // 11a48-11a4f - '3d264W' // 11a50-11aa2 #6886 - 'lA' // 11aa3-11aaf - 'o50P' // 11ab0-11abf #1315 - '2d264Q' // 11ac0-11af8 #6880 - 'fA' // 11af9-11aff - 'i17L' // 11b00-11b09 #453 - '9kA' // 11b0a-11bff - 'h41Z' // 11c00-11c08 #1091 - 'A' // 11c09 - '1r41Z' // 11c0a-11c36 #1091 - 'A' // 11c37 - 'm41Z' // 11c38-11c45 #1091 - 'iA' // 11c46-11c4f - '1b41Z' // 11c50-11c6c #1091 - 'bA' // 11c6d-11c6f - '1e50X' // 11c70-11c8f #1323 - 'aA' // 11c90-11c91 - 'u50X' // 11c92-11ca7 #1323 - 'A' // 11ca8 - 'm50X' // 11ca9-11cb6 #1323 - '2tA' // 11cb7-11cff - 'f27K' // 11d00-11d06 #712 - 'A' // 11d07 - 'a27K' // 11d08-11d09 #712 - 'A' // 11d0a - '1q27K' // 11d0b-11d36 #712 - 'bA' // 11d37-11d39 - '27K' // 11d3a #712 - 'A' // 11d3b - 'a27K' // 11d3c-11d3d #712 - 'A' // 11d3e - 'h27K' // 11d3f-11d47 #712 - 'gA' // 11d48-11d4f - 'i27K' // 11d50-11d59 #712 - 'eA' // 11d5a-11d5f - 'e31V' // 11d60-11d65 #827 - 'A' // 11d66 - 'a31V' // 11d67-11d68 #827 - 'A' // 11d69 - '1j31V' // 11d6a-11d8e #827 - 'A' // 11d8f - 'a31V' // 11d90-11d91 #827 - 'A' // 11d92 - 'e31V' // 11d93-11d98 #827 - 'fA' // 11d99-11d9f - 'i31V' // 11da0-11da9 #827 - '19wA' // 11daa-11faf - '72E' // 11fb0 #1876 - 'nA' // 11fb1-11fbf - '1w73C' // 11fc0-11ff1 #1900 - 'lA' // 11ff2-11ffe - '73C' // 11fff #1900 - '35k36W' // 12000-12399 #958 - '3wA' // 1239a-123ff - '4f36W' // 12400-1246e #958 - 'A' // 1246f - 'd36W' // 12470-12474 #958 - 'jA' // 12475-1247f - '7m36W' // 12480-12543 #958 - '105qA' // 12544-12fff - '33s42X' // 13000-1336d #1115 - '80G' // 1336e #2086 - '7j42X' // 1336f-1342f #1115 - 'pA' // 13430-13440 - 'e42X' // 13441-13446 #1115 - '154tA' // 13447-143ff - '22j262Z' // 14400-14646 #6837 - '331zA' // 14647-167ff - '21v71P' // 16800-16a38 #1861 - 'fA' // 16a39-16a3f - '1d51B' // 16a40-16a5e #1327 - 'A' // 16a5f - 'i51B' // 16a60-16a69 #1327 - 'cA' // 16a6a-16a6d - 'a51B' // 16a6e-16a6f #1327 - '3qA' // 16a70-16acf - '1c71Q' // 16ad0-16aed #1862 - 'aA' // 16aee-16aef - 'e71Q' // 16af0-16af5 #1862 - 'iA' // 16af6-16aff - '2q36S' // 16b00-16b45 #954 - 'iA' // 16b46-16b4f - 'i36S' // 16b50-16b59 #954 - 'A' // 16b5a - 'f36S' // 16b5b-16b61 #954 - 'A' // 16b62 - 't36S' // 16b63-16b77 #954 - 'dA' // 16b78-16b7c - 'r36S' // 16b7d-16b8f #954 - '26kA' // 16b90-16e3f - '3l264H' // 16e40-16e9a #6871 - '3vA' // 16e9b-16eff - '2v51A' // 16f00-16f4a #1326 - 'cA' // 16f4b-16f4e - '2d51A' // 16f4f-16f87 #1326 - 'fA' // 16f88-16f8e - 'p51A' // 16f8f-16f9f #1326 - '2lA' // 16fa0-16fe0 - '72N' // 16fe1 #1885 - '645kA' // 16fe2-1b16f - '15e72N' // 1b170-1b2fb #1885 - '88sA' // 1b2fc-1bbff - '4b32E' // 1bc00-1bc6a #836 - 'dA' // 1bc6b-1bc6f - 'l32E' // 1bc70-1bc7c #836 - 'bA' // 1bc7d-1bc7f - 'h32E' // 1bc80-1bc88 #836 - 'fA' // 1bc89-1bc8f - 'i32E' // 1bc90-1bc99 #836 - 'aA' // 1bc9a-1bc9b - 'g32E' // 1bc9c-1bca3 #836 - '190oA' // 1bca4-1cfff - '9k41U' // 1d000-1d0f5 #1086 - 'iA' // 1d0f6-1d0ff - '1l41U' // 1d100-1d126 #1086 - 'aA' // 1d127-1d128 - '7k41U' // 1d129-1d1ea #1086 - 'tA' // 1d1eb-1d1ff - '2q41U' // 1d200-1d245 #1086 - '4qA' // 1d246-1d2bf - 'sF' // 1d2c0-1d2d3 #5 - 'kA' // 1d2d4-1d2df - 's77M' // 1d2e0-1d2f3 #2014 - 'kA' // 1d2f4-1d2ff - '3hF' // 1d300-1d356 #5 - 'hA' // 1d357-1d35f - 'xF' // 1d360-1d378 #5 - '5dA' // 1d379-1d3ff - '3fM' // 1d400-1d454 #12 - 'A' // 1d455 - '2rM' // 1d456-1d49c #12 - 'A' // 1d49d - 'aM' // 1d49e-1d49f #12 - 'aA' // 1d4a0-1d4a1 - 'M' // 1d4a2 #12 - 'aA' // 1d4a3-1d4a4 - 'aM' // 1d4a5-1d4a6 #12 - 'aA' // 1d4a7-1d4a8 - 'cM' // 1d4a9-1d4ac #12 - 'A' // 1d4ad - 'kM' // 1d4ae-1d4b9 #12 - 'A' // 1d4ba - 'M' // 1d4bb #12 - 'A' // 1d4bc - 'fM' // 1d4bd-1d4c3 #12 - 'A' // 1d4c4 - '2lM' // 1d4c5-1d505 #12 - 'A' // 1d506 - 'cM' // 1d507-1d50a #12 - 'aA' // 1d50b-1d50c - 'gM' // 1d50d-1d514 #12 - 'A' // 1d515 - 'fM' // 1d516-1d51c #12 - 'A' // 1d51d - '1aM' // 1d51e-1d539 #12 - 'A' // 1d53a - 'cM' // 1d53b-1d53e #12 - 'A' // 1d53f - 'dM' // 1d540-1d544 #12 - 'A' // 1d545 - 'M' // 1d546 #12 - 'bA' // 1d547-1d549 - 'fM' // 1d54a-1d550 #12 - 'A' // 1d551 - '13aM' // 1d552-1d6a5 #12 - 'aA' // 1d6a6-1d6a7 - '11eM' // 1d6a8-1d7cb #12 - 'aA' // 1d7cc-1d7cd - '1wM' // 1d7ce-1d7ff #12 - '68wA' // 1d800-1deff - '1dE' // 1df00-1df1e #4 - '8pA' // 1df1f-1dfff - 'f27I' // 1e000-1e006 #710 - 'A' // 1e007 - 'p27I' // 1e008-1e018 #710 - 'aA' // 1e019-1e01a - 'f27I' // 1e01b-1e021 #710 - 'A' // 1e022 - 'a27I' // 1e023-1e024 #710 - 'A' // 1e025 - 'd27I' // 1e026-1e02a #710 - '25jA' // 1e02b-1e2bf - '2e73G' // 1e2c0-1e2f9 #1904 - 'dA' // 1e2fa-1e2fe - '73G' // 1e2ff #1904 - '47yA' // 1e300-1e7df - 'f3R' // 1e7e0-1e7e6 #95 - 'A' // 1e7e7 - 'c3R' // 1e7e8-1e7eb #95 - 'A' // 1e7ec - 'a3R' // 1e7ed-1e7ee #95 - 'A' // 1e7ef - 'n3R' // 1e7f0-1e7fe #95 - '9vA' // 1e7ff-1e8ff - '2w50L' // 1e900-1e94b #1311 - 'cA' // 1e94c-1e94f - 'i50L' // 1e950-1e959 #1311 - 'cA' // 1e95a-1e95d - 'a50L' // 1e95e-1e95f #1311 - '30dA' // 1e960-1ec70 - '2o264D' // 1ec71-1ecb4 #6867 - '12rA' // 1ecb5-1edff - 'cM' // 1ee00-1ee03 #12 - 'A' // 1ee04 - 'zM' // 1ee05-1ee1f #12 - 'A' // 1ee20 - 'aM' // 1ee21-1ee22 #12 - 'A' // 1ee23 - 'M' // 1ee24 #12 - 'aA' // 1ee25-1ee26 - 'M' // 1ee27 #12 - 'A' // 1ee28 - 'iM' // 1ee29-1ee32 #12 - 'A' // 1ee33 - 'cM' // 1ee34-1ee37 #12 - 'A' // 1ee38 - 'M' // 1ee39 #12 - 'A' // 1ee3a - 'M' // 1ee3b #12 - 'eA' // 1ee3c-1ee41 - 'M' // 1ee42 #12 - 'cA' // 1ee43-1ee46 - 'M' // 1ee47 #12 - 'A' // 1ee48 - 'M' // 1ee49 #12 - 'A' // 1ee4a - 'M' // 1ee4b #12 - 'A' // 1ee4c - 'bM' // 1ee4d-1ee4f #12 - 'A' // 1ee50 - 'aM' // 1ee51-1ee52 #12 - 'A' // 1ee53 - 'M' // 1ee54 #12 - 'aA' // 1ee55-1ee56 - 'M' // 1ee57 #12 - 'A' // 1ee58 - 'M' // 1ee59 #12 - 'A' // 1ee5a - 'M' // 1ee5b #12 - 'A' // 1ee5c - 'M' // 1ee5d #12 - 'A' // 1ee5e - 'M' // 1ee5f #12 - 'A' // 1ee60 - 'aM' // 1ee61-1ee62 #12 - 'A' // 1ee63 - 'M' // 1ee64 #12 - 'aA' // 1ee65-1ee66 - 'cM' // 1ee67-1ee6a #12 - 'A' // 1ee6b - 'fM' // 1ee6c-1ee72 #12 - 'A' // 1ee73 - 'cM' // 1ee74-1ee77 #12 - 'A' // 1ee78 - 'cM' // 1ee79-1ee7c #12 - 'A' // 1ee7d - 'M' // 1ee7e #12 - 'A' // 1ee7f - 'iM' // 1ee80-1ee89 #12 - 'A' // 1ee8a - 'pM' // 1ee8b-1ee9b #12 - 'dA' // 1ee9c-1eea0 - 'bM' // 1eea1-1eea3 #12 - 'A' // 1eea4 - 'dM' // 1eea5-1eea9 #12 - 'A' // 1eeaa - 'pM' // 1eeab-1eebb #12 - '1yA' // 1eebc-1eeef - 'aM' // 1eef0-1eef1 #12 - '10iA' // 1eef2-1efff - 'cF' // 1f000-1f003 #5 - '6J' // 1f004 #165 - '1lF' // 1f005-1f02b #5 - 'cA' // 1f02c-1f02f - '3uF' // 1f030-1f093 #5 - 'kA' // 1f094-1f09f - 'nF' // 1f0a0-1f0ae #5 - 'aA' // 1f0af-1f0b0 - 'nF' // 1f0b1-1f0bf #5 - 'A' // 1f0c0 - 'mF' // 1f0c1-1f0ce #5 - '6J' // 1f0cf #165 - 'A' // 1f0d0 - '1jF' // 1f0d1-1f0f5 #5 - 'iA' // 1f0f6-1f0ff - 'l12J' // 1f100-1f10c #321 - 'bF' // 1f10d-1f10f #5 - '1v12J' // 1f110-1f140 #321 - 'a17P' // 1f141-1f142 #457 - 'l12J' // 1f143-1f14f #321 - '17P' // 1f150 #457 - 'b12J' // 1f151-1f153 #321 - '17P' // 1f154 #457 - 'b12J' // 1f155-1f157 #321 - '17P' // 1f158 #457 - 'a12J' // 1f159-1f15a #321 - '17P' // 1f15b #457 - '12J' // 1f15c #321 - 'a17P' // 1f15d-1f15e #457 - 'b12J' // 1f15f-1f161 #321 - 'a17P' // 1f162-1f163 #457 - 'h12J' // 1f164-1f16c #321 - 'bF' // 1f16d-1f16f #5 - 'a74M' // 1f170-1f171 #1936 - 'a12J' // 1f172-1f173 #321 - '17P' // 1f174 #457 - 'a12J' // 1f175-1f176 #321 - 'a17P' // 1f177-1f178 #457 - 'c12J' // 1f179-1f17c #321 - '17P' // 1f17d #457 - 'a74N' // 1f17e-1f17f #1937 - 'm12J' // 1f180-1f18d #321 - '74Y' // 1f18e #1948 - 'a12J' // 1f18f-1f190 #321 - '42H' // 1f191 #1099 - 'c51N' // 1f192-1f195 #1339 - '42H' // 1f196 #1099 - '42G' // 1f197 #1098 - 'b51N' // 1f198-1f19a #1339 - 'q12J' // 1f19b-1f1ac #321 - 'F' // 1f1ad #5 - '2cA' // 1f1ae-1f1e5 - 'y73J' // 1f1e6-1f1ff #1907 - '19F' // 1f200 #499 - 'a42H' // 1f201-1f202 #1099 - 'lA' // 1f203-1f20f - 'i19F' // 1f210-1f219 #499 - '42F' // 1f21a #1097 - 's19F' // 1f21b-1f22e #499 - '42G' // 1f22f #1098 - 'a19F' // 1f230-1f231 #499 - '42F' // 1f232 #1097 - '42G' // 1f233 #1098 - '42F' // 1f234 #1097 - 'b51M' // 1f235-1f237 #1338 - '74X' // 1f238 #1947 - 'a74L' // 1f239-1f23a #1935 - 'C' // 1f23b #2 - 'cA' // 1f23c-1f23f - 'hC' // 1f240-1f248 #2 - 'fA' // 1f249-1f24f - 'a51M' // 1f250-1f251 #1338 - '6qA' // 1f252-1f2ff - '5F' // 1f300 #135 - '4X' // 1f301 #127 - '2S' // 1f302 #70 - '4X' // 1f303 #127 - 'a5F' // 1f304-1f305 #135 - 'a4X' // 1f306-1f307 #127 - '73M' // 1f308 #1910 - '4X' // 1f309 #127 - 'b5F' // 1f30a-1f30c #135 - 'b5Z' // 1f30d-1f30f #155 - '51H' // 1f310 #1333 - 'c5F' // 1f311-1f314 #135 - '5Z' // 1f315 #155 - 'c5F' // 1f316-1f319 #135 - 'a27N' // 1f31a-1f31b #715 - '36U' // 1f31c #956 - 'b27N' // 1f31d-1f31f #715 - '5F' // 1f320 #135 - '75E' // 1f321 #1954 - 'aF' // 1f322-1f323 #5 - 'f5Z' // 1f324-1f32a #155 - '36U' // 1f32b #956 - '5Z' // 1f32c #155 - 'c15F' // 1f32d-1f330 #395 - 'd5F' // 1f331-1f335 #135 - '32A' // 1f336 #832 - 'e5F' // 1f337-1f33c #135 - '15F' // 1f33d #395 - '75R' // 1f33e #1967 - 'd5F' // 1f33f-1f343 #135 - '75T' // 1f344 #1969 - 'e15F' // 1f345-1f34a #395 - '75Q' // 1f34b #1966 - '1l15F' // 1f34c-1f372 #395 - '51V' // 1f373 #1347 - 'c15F' // 1f374-1f377 #395 - '32A' // 1f378 #832 - 'b15F' // 1f379-1f37b #395 - '51V' // 1f37c #1347 - '32A' // 1f37d #832 - 'a15F' // 1f37e-1f37f #395 - 'a5P' // 1f380-1f381 #145 - '75N' // 1f382 #1963 - '42L' // 1f383 #1103 - '42K' // 1f384 #1102 - '4S' // 1f385 #122 - 'b5P' // 1f386-1f388 #145 - '42L' // 1f389 #1103 - 'a5P' // 1f38a-1f38b #145 - '15E' // 1f38c #394 - 'd5P' // 1f38d-1f391 #145 - '2S' // 1f392 #70 - '51R' // 1f393 #1343 - 'aF' // 1f394-1f395 #5 - 'a6J' // 1f396-1f397 #165 - 'F' // 1f398 #5 - 'b6J' // 1f399-1f39b #165 - 'aF' // 1f39c-1f39d #5 - 'a6J' // 1f39e-1f39f #165 - 'b4X' // 1f3a0-1f3a2 #127 - '5P' // 1f3a3 #145 - '42K' // 1f3a4 #1102 - '5P' // 1f3a5 #145 - '3S' // 1f3a6 #96 - '6J' // 1f3a7 #165 - '42K' // 1f3a8 #1102 - '42I' // 1f3a9 #1100 - '4X' // 1f3aa #127 - '5P' // 1f3ab #145 - 'b6J' // 1f3ac-1f3ae #165 - 'e5P' // 1f3af-1f3b4 #145 - 'a3S' // 1f3b5-1f3b6 #96 - 'd5P' // 1f3b7-1f3bb #145 - '3S' // 1f3bc #96 - '42I' // 1f3bd #1100 - 'b5P' // 1f3be-1f3c0 #145 - '15E' // 1f3c1 #394 - '27O' // 1f3c2 #716 - '32B' // 1f3c3 #833 - '27O' // 1f3c4 #716 - '5P' // 1f3c5 #145 - '6J' // 1f3c6 #165 - '4S' // 1f3c7 #122 - 'a5P' // 1f3c8-1f3c9 #145 - 'b27O' // 1f3ca-1f3cc #716 - 'a9O' // 1f3cd-1f3ce #248 - 'd5P' // 1f3cf-1f3d3 #145 - '5Z' // 1f3d4 #155 - '9O' // 1f3d5 #248 - '5Z' // 1f3d6 #155 - 'd9O' // 1f3d7-1f3db #248 - 'b5Z' // 1f3dc-1f3de #155 - 'a9O' // 1f3df-1f3e0 #248 - 'e4X' // 1f3e1-1f3e6 #127 - '3S' // 1f3e7 #96 - 'b4X' // 1f3e8-1f3ea #127 - '42M' // 1f3eb #1104 - '4X' // 1f3ec #127 - '51T' // 1f3ed #1345 - '2S' // 1f3ee #70 - 'a4X' // 1f3ef-1f3f0 #127 - 'aF' // 1f3f1-1f3f2 #5 - '73O' // 1f3f3 #1912 - '15E' // 1f3f4 #394 - '5Z' // 1f3f5 #155 - 'F' // 1f3f6 #5 - '2E' // 1f3f7 #56 - 'a5P' // 1f3f8-1f3f9 #145 - '2S' // 1f3fa #70 - 'd75X' // 1f3fb-1f3ff #1973 - 'g5F' // 1f400-1f407 #135 - '5Z' // 1f408 #155 - 'k5F' // 1f409-1f414 #135 - '5Z' // 1f415 #155 - 'h5F' // 1f416-1f41e #135 - '5Z' // 1f41f #155 - 'e5F' // 1f420-1f425 #135 - '75U' // 1f426 #1970 - 'w5F' // 1f427-1f43e #135 - '5Z' // 1f43f #155 - '3O' // 1f440 #92 - '73U' // 1f441 #1918 - '17O' // 1f442 #456 - 'b3O' // 1f443-1f445 #92 - 'c17O' // 1f446-1f449 #456 - 'a3O' // 1f44a-1f44b #92 - 'b17O' // 1f44c-1f44e #456 - 'a3O' // 1f44f-1f450 #92 - 'a2S' // 1f451-1f452 #70 - '2E' // 1f453 #56 - 'n2S' // 1f454-1f462 #70 - 'b3O' // 1f463-1f465 #92 - 'a4S' // 1f466-1f467 #122 - 'a32B' // 1f468-1f469 #833 - '27O' // 1f46a #716 - 'm4S' // 1f46b-1f478 #122 - 'b3O' // 1f479-1f47b #92 - '4S' // 1f47c #122 - '17O' // 1f47d #456 - '42L' // 1f47e #1103 - 'a3O' // 1f47f-1f480 #92 - 'b4S' // 1f481-1f483 #122 - '2S' // 1f484 #70 - '3O' // 1f485 #92 - 'a4S' // 1f486-1f487 #122 - '4X' // 1f488 #127 - 'a2S' // 1f489-1f48a #70 - '51X' // 1f48b #1349 - '75H' // 1f48c #1957 - 'a2S' // 1f48d-1f48e #70 - '4S' // 1f48f #122 - '5F' // 1f490 #135 - '4S' // 1f491 #122 - '4X' // 1f492 #127 - 'e3O' // 1f493-1f498 #92 - 'c27L' // 1f499-1f49c #713 - 'a3O' // 1f49d-1f49e #92 - '27L' // 1f49f #713 - '3S' // 1f4a0 #96 - '2S' // 1f4a1 #70 - '3S' // 1f4a2 #96 - '2E' // 1f4a3 #56 - '3O' // 1f4a4 #92 - '51Z' // 1f4a5 #1351 - '3O' // 1f4a6 #92 - '5F' // 1f4a7 #135 - 'b3O' // 1f4a8-1f4aa #92 - '27N' // 1f4ab #715 - 'a3S' // 1f4ac-1f4ad #96 - '5F' // 1f4ae #135 - '3O' // 1f4af #92 - '2E' // 1f4b0 #56 - 'a3S' // 1f4b1-1f4b2 #96 - '2E' // 1f4b3 #56 - 'd2S' // 1f4b4-1f4b8 #70 - '4M' // 1f4b9 #116 - '4X' // 1f4ba #127 - '51R' // 1f4bb #1343 - '42J' // 1f4bc #1101 - 'a2S' // 1f4bd-1f4be #70 - '2E' // 1f4bf #56 - 'g2S' // 1f4c0-1f4c7 #70 - 'c2E' // 1f4c8-1f4cb #56 - 'm2S' // 1f4cc-1f4d9 #70 - '2E' // 1f4da #56 - '3S' // 1f4db #96 - 'b2S' // 1f4dc-1f4de #70 - '2E' // 1f4df #56 - 'a2S' // 1f4e0-1f4e1 #70 - '42I' // 1f4e2 #1100 - '2S' // 1f4e3 #70 - 'b2E' // 1f4e4-1f4e6 #56 - 'b2S' // 1f4e7-1f4e9 #70 - 'c2E' // 1f4ea-1f4ed #56 - 'c2S' // 1f4ee-1f4f1 #70 - 'd3S' // 1f4f2-1f4f6 #96 - '6J' // 1f4f7 #165 - '5P' // 1f4f8 #145 - 'b6J' // 1f4f9-1f4fb #165 - '5P' // 1f4fc #145 - '6J' // 1f4fd #165 - 'F' // 1f4fe #5 - '2S' // 1f4ff #70 - 'b3S' // 1f500-1f502 #96 - '4M' // 1f503 #116 - 'b3S' // 1f504-1f506 #96 - 'c4M' // 1f507-1f50a #116 - 'a2S' // 1f50b-1f50c #70 - '2E' // 1f50d #56 - 'c2S' // 1f50e-1f511 #70 - 'a2E' // 1f512-1f513 #56 - '2S' // 1f514 #70 - '3S' // 1f515 #96 - 'a2S' // 1f516-1f517 #70 - 'l3S' // 1f518-1f524 #96 - '75S' // 1f525 #1968 - '2S' // 1f526 #70 - '42J' // 1f527 #1101 - 'a2S' // 1f528-1f529 #70 - '15F' // 1f52a #395 - '5P' // 1f52b #145 - '42J' // 1f52c #1101 - 'a2S' // 1f52d-1f52e #70 - 'n3S' // 1f52f-1f53d #96 - 'gF' // 1f53e-1f545 #5 - 'b2M' // 1f546-1f548 #64 - '17N' // 1f549 #455 - '5Z' // 1f54a #155 - 'b4X' // 1f54b-1f54d #127 - '3S' // 1f54e #96 - '2M' // 1f54f #64 - 'w2E' // 1f550-1f567 #56 - 'fF' // 1f568-1f56e #5 - 'a2E' // 1f56f-1f570 #56 - 'aF' // 1f571-1f572 #5 - '36U' // 1f573 #956 - 'a27O' // 1f574-1f575 #716 - '2E' // 1f576 #56 - 'a5Z' // 1f577-1f578 #155 - '6J' // 1f579 #165 - '4S' // 1f57a #122 - 'kF' // 1f57b-1f586 #5 - '2E' // 1f587 #56 - 'aF' // 1f588-1f589 #5 - 'a2E' // 1f58a-1f58b #56 - 'a51Q' // 1f58c-1f58d #1342 - 'aF' // 1f58e-1f58f #5 - '17O' // 1f590 #456 - 'cF' // 1f591-1f594 #5 - 'a3O' // 1f595-1f596 #92 - 'lF' // 1f597-1f5a3 #5 - '27L' // 1f5a4 #713 - '2E' // 1f5a5 #56 - 'aF' // 1f5a6-1f5a7 #5 - '2E' // 1f5a8 #56 - 'gF' // 1f5a9-1f5b0 #5 - 'a2E' // 1f5b1-1f5b2 #56 - 'hF' // 1f5b3-1f5bb #5 - '6J' // 1f5bc #165 - 'dF' // 1f5bd-1f5c1 #5 - 'b2E' // 1f5c2-1f5c4 #56 - 'kF' // 1f5c5-1f5d0 #5 - 'b2E' // 1f5d1-1f5d3 #56 - 'gF' // 1f5d4-1f5db #5 - 'b2E' // 1f5dc-1f5de #56 - 'aF' // 1f5df-1f5e0 #5 - '2E' // 1f5e1 #56 - 'F' // 1f5e2 #5 - '17O' // 1f5e3 #456 - 'cF' // 1f5e4-1f5e7 #5 - '4M' // 1f5e8 #116 - 'eF' // 1f5e9-1f5ee #5 - '4M' // 1f5ef #116 - 'bF' // 1f5f0-1f5f2 #5 - '2E' // 1f5f3 #56 - 'eF' // 1f5f4-1f5f9 #5 - '9O' // 1f5fa #248 - 'd4X' // 1f5fb-1f5ff #127 - 'o3O' // 1f600-1f60f #92 - '52A' // 1f610 #1352 - '1v3O' // 1f611-1f641 #92 - '51Z' // 1f642 #1351 - 'a3O' // 1f643-1f644 #92 - 'b4S' // 1f645-1f647 #122 - 'b27N' // 1f648-1f64a #715 - '4S' // 1f64b #122 - '3O' // 1f64c #92 - 'a4S' // 1f64d-1f64e #122 - '3O' // 1f64f #92 - '1uF' // 1f650-1f67f #5 - '42M' // 1f680 #1104 - 'e4X' // 1f681-1f686 #127 - '9O' // 1f687 #248 - 'd4X' // 1f688-1f68c #127 - '9O' // 1f68d #248 - 'b4X' // 1f68e-1f690 #127 - '9O' // 1f691 #248 - '42M' // 1f692 #1104 - '4X' // 1f693 #127 - '9O' // 1f694 #248 - 'b4X' // 1f695-1f697 #127 - '9O' // 1f698 #248 - 'i4X' // 1f699-1f6a2 #127 - '4S' // 1f6a3 #122 - 'd4X' // 1f6a4-1f6a8 #127 - '15E' // 1f6a9 #394 - '2S' // 1f6aa #70 - '3S' // 1f6ab #96 - '2S' // 1f6ac #70 - '4M' // 1f6ad #116 - 'c3S' // 1f6ae-1f6b1 #96 - '9O' // 1f6b2 #248 - '3S' // 1f6b3 #96 - 'a4S' // 1f6b4-1f6b5 #122 - '32B' // 1f6b6 #833 - 'a3S' // 1f6b7-1f6b8 #96 - 'a4M' // 1f6b9-1f6ba #116 - '3S' // 1f6bb #96 - '4M' // 1f6bc #116 - '2S' // 1f6bd #70 - '3S' // 1f6be #96 - '2S' // 1f6bf #70 - '4S' // 1f6c0 #122 - '2S' // 1f6c1 #70 - 'c3S' // 1f6c2-1f6c5 #96 - 'dF' // 1f6c6-1f6ca #5 - '2E' // 1f6cb #56 - '4S' // 1f6cc #122 - 'b2E' // 1f6cd-1f6cf #56 - '3S' // 1f6d0 #96 - '51H' // 1f6d1 #1333 - '2S' // 1f6d2 #70 - 'aF' // 1f6d3-1f6d4 #5 - 'a9O' // 1f6d5-1f6d6 #248 - '4M' // 1f6d7 #116 - 'cA' // 1f6d8-1f6db - '3S' // 1f6dc #96 - 'b4X' // 1f6dd-1f6df #127 - 'a2E' // 1f6e0-1f6e1 #56 - 'c9O' // 1f6e2-1f6e5 #248 - 'bF' // 1f6e6-1f6e8 #5 - '9O' // 1f6e9 #248 - 'F' // 1f6ea #5 - 'a4X' // 1f6eb-1f6ec #127 - 'bA' // 1f6ed-1f6ef - '2E' // 1f6f0 #56 - 'aF' // 1f6f1-1f6f2 #5 - '9O' // 1f6f3 #248 - 'b4X' // 1f6f4-1f6f6 #127 - '6J' // 1f6f7 #165 - '9O' // 1f6f8 #248 - '6J' // 1f6f9 #165 - 'a9O' // 1f6fa-1f6fb #248 - '6J' // 1f6fc #165 - 'bA' // 1f6fd-1f6ff - '4k2M' // 1f700-1f773 #64 - 'bF' // 1f774-1f776 #5 - 'cA' // 1f777-1f77a - '3pF' // 1f77b-1f7d9 #5 - 'eA' // 1f7da-1f7df - 'h4M' // 1f7e0-1f7e8 #116 - '51I' // 1f7e9 #1334 - '4M' // 1f7ea #116 - '51I' // 1f7eb #1334 - 'cA' // 1f7ec-1f7ef - '3S' // 1f7f0 #96 - 'nA' // 1f7f1-1f7ff - 'kF' // 1f800-1f80b #5 - 'cA' // 1f80c-1f80f - '2cF' // 1f810-1f847 #5 - 'gA' // 1f848-1f84f - 'iF' // 1f850-1f859 #5 - 'eA' // 1f85a-1f85f - '1mF' // 1f860-1f887 #5 - 'gA' // 1f888-1f88f - '1cF' // 1f890-1f8ad #5 - 'aA' // 1f8ae-1f8af - 'aF' // 1f8b0-1f8b1 #5 - '2yA' // 1f8b2-1f8ff - 'kF' // 1f900-1f90b #5 - '3O' // 1f90c #92 - 'a27L' // 1f90d-1f90e #713 - 'm3O' // 1f90f-1f91c #92 - '51X' // 1f91d #1349 - 'g3O' // 1f91e-1f925 #92 - '4S' // 1f926 #122 - 'h3O' // 1f927-1f92f #92 - 'a4S' // 1f930-1f931 #122 - 'a3O' // 1f932-1f933 #92 - 'f4S' // 1f934-1f93a #122 - 'F' // 1f93b #5 - 'b4S' // 1f93c-1f93e #122 - '5P' // 1f93f #145 - '5F' // 1f940 #135 - '5P' // 1f941 #145 - 'b15F' // 1f942-1f944 #395 - '5P' // 1f945 #145 - 'F' // 1f946 #5 - 'h5P' // 1f947-1f94f #145 - '1e15F' // 1f950-1f96f #395 - 'f3O' // 1f970-1f976 #92 - '4S' // 1f977 #122 - 'b3O' // 1f978-1f97a #92 - 'd2S' // 1f97b-1f97f #70 - '1c5F' // 1f980-1f99d #135 - '51U' // 1f99e #1346 - '5F' // 1f99f #135 - '27N' // 1f9a0 #715 - 'h5F' // 1f9a1-1f9a9 #135 - '51U' // 1f9aa #1346 - 'c5F' // 1f9ab-1f9ae #135 - '75F' // 1f9af #1955 - 'c4S' // 1f9b0-1f9b3 #122 - 'c3O' // 1f9b4-1f9b7 #92 - 'a4S' // 1f9b8-1f9b9 #122 - '75D' // 1f9ba #1953 - '3O' // 1f9bb #92 - 'a75P' // 1f9bc-1f9bd #1965 - 'a3O' // 1f9be-1f9bf #92 - 'k15F' // 1f9c0-1f9cb #395 - 'a4S' // 1f9cc-1f9cd #122 - '32B' // 1f9ce #833 - '4S' // 1f9cf #122 - '3O' // 1f9d0 #92 - 'a32B' // 1f9d1-1f9d2 #833 - 'l4S' // 1f9d3-1f9df #122 - '3O' // 1f9e0 #92 - '27L' // 1f9e1 #713 - 'd2S' // 1f9e2-1f9e6 #70 - 'b5P' // 1f9e7-1f9e9 #145 - 'b2S' // 1f9ea-1f9ec #70 - '4X' // 1f9ed #127 - 'd2S' // 1f9ee-1f9f2 #70 - '75C' // 1f9f3 #1952 - '2S' // 1f9f4 #70 - 'a5P' // 1f9f5-1f9f6 #145 - 'h2S' // 1f9f7-1f9ff #70 - '3eF' // 1fa00-1fa53 #5 - 'kA' // 1fa54-1fa5f - 'mF' // 1fa60-1fa6d #5 - 'aA' // 1fa6e-1fa6f - '6J' // 1fa70 #165 - '51Q' // 1fa71 #1342 - 'b2E' // 1fa72-1fa74 #56 - 'b27L' // 1fa75-1fa77 #713 - '17O' // 1fa78 #456 - '75I' // 1fa79 #1958 - '2E' // 1fa7a #56 - '2S' // 1fa7b #70 - '4X' // 1fa7c #127 - 'bA' // 1fa7d-1fa7f - 'a6J' // 1fa80-1fa81 #165 - '27O' // 1fa82 #716 - 'b6J' // 1fa83-1fa85 #165 - '2E' // 1fa86 #56 - 'a5P' // 1fa87-1fa88 #145 - '24J' // 1fa89 #633 - 'dA' // 1fa8a-1fa8e - '24J' // 1fa8f #633 - '5Z' // 1fa90 #155 - 'b2E' // 1fa91-1fa93 #56 - 'a6J' // 1fa94-1fa95 #165 - '2E' // 1fa96 #56 - 'a6J' // 1fa97-1fa98 #165 - 'g2E' // 1fa99-1faa0 #56 - '6J' // 1faa1 #165 - 'e2E' // 1faa2-1faa7 #56 - '5Z' // 1faa8 #155 - '5P' // 1faa9 #145 - 'd2S' // 1faaa-1faae #70 - '3S' // 1faaf #96 - 'f5Z' // 1fab0-1fab6 #155 - 'f5F' // 1fab7-1fabd #135 - '24J' // 1fabe #633 - '5F' // 1fabf #135 - 'b17O' // 1fac0-1fac2 #456 - 'b4S' // 1fac3-1fac5 #122 - '24J' // 1fac6 #633 - 'fA' // 1fac7-1facd - 'a5F' // 1face-1facf #135 - 'f32A' // 1fad0-1fad6 #832 - 'd15F' // 1fad7-1fadb #395 - '24J' // 1fadc #633 - 'aA' // 1fadd-1fade - '24J' // 1fadf #633 - 'f3O' // 1fae0-1fae6 #92 - '5F' // 1fae7 #135 - '3O' // 1fae8 #92 - '24J' // 1fae9 #633 - 'eA' // 1faea-1faef - 'h3O' // 1faf0-1faf8 #92 - 'fA' // 1faf9-1faff - '5pF' // 1fb00-1fb92 #5 - 'A' // 1fb93 - '2bF' // 1fb94-1fbca #5 - '1jA' // 1fbcb-1fbef - 'iF' // 1fbf0-1fbf9 #5 - '39zA' // 1fbfa-2000a - 'C' // 2000b #2 - 'tA' // 2000c-20020 - '2A' // 20021 #52 - '1aA' // 20022-2003d - '2A' // 2003e #52 - 'fA' // 2003f-20045 - '2A' // 20046 #52 - 'fA' // 20047-2004d - '2A' // 2004e #52 - 'xA' // 2004f-20067 - '2A' // 20068 #52 - '1bA' // 20069-20085 - 'a2A' // 20086-20087 #52 - 'A' // 20088 - 'C' // 20089 #2 - '80P' // 2008a #2095 - 'hA' // 2008b-20093 - '2A' // 20094 #52 - 'lA' // 20095-200a1 - 'C' // 200a2 #2 - 'A' // 200a3 - 'C' // 200a4 #2 - 'jA' // 200a5-200af - 'C' // 200b0 #2 - 'xA' // 200b1-200c9 - 'c2A' // 200ca-200cd #52 - 'bA' // 200ce-200d0 - '2A' // 200d1 #52 - '1aA' // 200d2-200ed - '2A' // 200ee #52 - 'eA' // 200ef-200f4 - 'C' // 200f5 #2 - 'uA' // 200f6-2010b - '2A' // 2010c #52 - 'A' // 2010d - '2A' // 2010e #52 - 'hA' // 2010f-20117 - '2A' // 20118 #52 - '2jA' // 20119-20157 - 'C' // 20158 #2 - '2tA' // 20159-201a1 - 'C' // 201a2 #2 - 'A' // 201a3 - '2A' // 201a4 #52 - 'cA' // 201a5-201a8 - '2A' // 201a9 #52 - 'A' // 201aa - '2A' // 201ab #52 - 'tA' // 201ac-201c0 - '2A' // 201c1 #52 - 'qA' // 201c2-201d3 - '2A' // 201d4 #52 - '1bA' // 201d5-201f1 - '2A' // 201f2 #52 - 'pA' // 201f3-20203 - '2A' // 20204 #52 - 'fA' // 20205-2020b - '2A' // 2020c #52 - 'eA' // 2020d-20212 - 'C' // 20213 #2 - '2A' // 20214 #52 - '1iA' // 20215-20238 - '2A' // 20239 #52 - '1fA' // 2023a-2025a - '2A' // 2025b #52 - 'wA' // 2025c-20273 - 'a2A' // 20274-20275 #52 - '1hA' // 20276-20298 - '2A' // 20299 #52 - 'cA' // 2029a-2029d - '2A' // 2029e #52 - 'A' // 2029f - '2A' // 202a0 #52 - 'uA' // 202a1-202b6 - '2A' // 202b7 #52 - 'fA' // 202b8-202be - 'a2A' // 202bf-202c0 #52 - '1iA' // 202c1-202e4 - '2A' // 202e5 #52 - '1iA' // 202e6-20309 - '2A' // 2030a #52 - 'yA' // 2030b-20324 - '2A' // 20325 #52 - 'dA' // 20326-2032a - 'C' // 2032b #2 - 'tA' // 2032c-20340 - '2A' // 20341 #52 - 'bA' // 20342-20344 - 'b2A' // 20345-20347 #52 - '1nA' // 20348-20370 - 'C' // 20371 #2 - 'kA' // 20372-2037d - 'b2A' // 2037e-20380 #52 - 'C' // 20381 #2 - '1cA' // 20382-2039f - '2A' // 203a0 #52 - 'eA' // 203a1-203a6 - '2A' // 203a7 #52 - 'lA' // 203a8-203b4 - '2A' // 203b5 #52 - 'rA' // 203b6-203c8 - '2A' // 203c9 #52 - 'A' // 203ca - '2A' // 203cb #52 - '1nA' // 203cc-203f4 - '2A' // 203f5 #52 - 'bA' // 203f6-203f8 - 'C' // 203f9 #2 - 'aA' // 203fa-203fb - '2A' // 203fc #52 - 'uA' // 203fd-20412 - 'a2A' // 20413-20414 #52 - 'iA' // 20415-2041e - '2A' // 2041f #52 - '1oA' // 20420-20449 - 'C' // 2044a #2 - 'yA' // 2044b-20464 - '2A' // 20465 #52 - '1fA' // 20466-20486 - '80Q' // 20487 #2096 - 'eA' // 20488-2048d - 'O' // 2048e #14 - 'aA' // 2048f-20490 - 'aO' // 20491-20492 #14 - 'oA' // 20493-204a2 - 'O' // 204a3 #14 - '1xA' // 204a4-204d6 - 'O' // 204d7 #14 - '1iA' // 204d8-204fb - 'O' // 204fc #14 - 'A' // 204fd - 'O' // 204fe #14 - 'iA' // 204ff-20508 - 'C' // 20509 #2 - '1zA' // 2050a-2053e - 'C' // 2053f #2 - 'fA' // 20540-20546 - 'O' // 20547 #14 - '2qA' // 20548-2058d - 'O' // 2058e #14 - 'uA' // 2058f-205a4 - 'O' // 205a5 #14 - 'jA' // 205a6-205b0 - 'C' // 205b1 #2 - 'A' // 205b2 - 'O' // 205b3 #14 - 'nA' // 205b4-205c2 - 'O' // 205c3 #14 - 'eA' // 205c4-205c9 - 'O' // 205ca #14 - 'dA' // 205cb-205cf - 'O' // 205d0 #14 - 'cA' // 205d1-205d4 - 'O' // 205d5 #14 - 'C' // 205d6 #2 - 'gA' // 205d7-205de - 'aO' // 205df-205e0 #14 - 'iA' // 205e1-205ea - 'O' // 205eb #14 - '1jA' // 205ec-20610 - '42Z' // 20611 #1117 - 'bA' // 20612-20614 - 'O' // 20615 #14 - 'bA' // 20616-20618 - 'aO' // 20619-2061a #14 - 'lA' // 2061b-20627 - 'C' // 20628 #2 - 'fA' // 20629-2062f - 'O' // 20630 #14 - '1jA' // 20631-20655 - 'O' // 20656 #14 - '1dA' // 20657-20675 - 'O' // 20676 #14 - '4lA' // 20677-206eb - 'C' // 206ec #2 - '1fA' // 206ed-2070d - 'O' // 2070e #14 - '1gA' // 2070f-20730 - 'O' // 20731 #14 - '1bA' // 20732-2074e - 'C' // 2074f #2 - '1nA' // 20750-20778 - '27S' // 20779 #720 - '2yA' // 2077a-207c7 - 'C' // 207c8 #2 - '2iA' // 207c9-20806 - 'C' // 20807 #2 - '1iA' // 20808-2082b - 'O' // 2082c #14 - 'lA' // 2082d-20839 - 'C' // 2083a #2 - '2cA' // 2083b-20872 - 'O' // 20873 #14 - '2pA' // 20874-208b8 - 'C' // 208b9 #2 - 'zA' // 208ba-208d4 - 'O' // 208d5 #14 - '2cA' // 208d6-2090d - 'C' // 2090e #2 - 'fA' // 2090f-20915 - 'O' // 20916 #14 - 'kA' // 20917-20922 - 'O' // 20923 #14 - '1uA' // 20924-20953 - 'O' // 20954 #14 - '1iA' // 20955-20978 - 'O' // 20979 #14 - 'aA' // 2097a-2097b - 'C' // 2097c #2 - 'fA' // 2097d-20983 - 'C' // 20984 #2 - 'wA' // 20985-2099c - 'C' // 2099d #2 - '2tA' // 2099e-209e6 - 'O' // 209e7 #14 - '1nA' // 209e8-20a10 - 'O' // 20a11 #14 - '2iA' // 20a12-20a4f - 'O' // 20a50 #14 - 'rA' // 20a51-20a63 - 'C' // 20a64 #2 - 'iA' // 20a65-20a6e - 'O' // 20a6f #14 - 'yA' // 20a70-20a89 - 'O' // 20a8a #14 - '1nA' // 20a8b-20ab3 - 'O' // 20ab4 #14 - 'lA' // 20ab5-20ac1 - 'O' // 20ac2 #14 - 'iA' // 20ac3-20acc - 'O' // 20acd #14 - 'dA' // 20ace-20ad2 - 'C' // 20ad3 #2 - '2dA' // 20ad4-20b0c - 'O' // 20b0d #14 - 'nA' // 20b0e-20b1c - 'C' // 20b1d #2 - '4hA' // 20b1e-20b8e - 'O' // 20b8f #14 - 'nA' // 20b90-20b9e - '42Z' // 20b9f #1117 - 'gA' // 20ba0-20ba7 - 'aO' // 20ba8-20ba9 #14 - 'lA' // 20baa-20bb6 - 'C' // 20bb7 #2 - 'fA' // 20bb8-20bbe - 'O' // 20bbf #14 - 'eA' // 20bc0-20bc5 - 'O' // 20bc6 #14 - 'cA' // 20bc7-20bca - 'O' // 20bcb #14 - 'uA' // 20bcc-20be1 - 'O' // 20be2 #14 - 'gA' // 20be3-20bea - 'O' // 20beb #14 - 'nA' // 20bec-20bfa - 'O' // 20bfb #14 - 'bA' // 20bfc-20bfe - 'O' // 20bff #14 - 'jA' // 20c00-20c0a - 'O' // 20c0b #14 - 'A' // 20c0c - 'O' // 20c0d #14 - 'qA' // 20c0e-20c1f - 'O' // 20c20 #14 - 'rA' // 20c21-20c33 - 'O' // 20c34 #14 - 'dA' // 20c35-20c39 - 'aO' // 20c3a-20c3b #14 - 'dA' // 20c3c-20c40 - '27S' // 20c41 #720 - 'aO' // 20c42-20c43 #14 - 'nA' // 20c44-20c52 - 'O' // 20c53 #14 - 'pA' // 20c54-20c64 - 'O' // 20c65 #14 - 'pA' // 20c66-20c76 - 'O' // 20c77 #14 - '27S' // 20c78 #720 - 'bA' // 20c79-20c7b - 'O' // 20c7c #14 - 'oA' // 20c7d-20c8c - 'O' // 20c8d #14 - 'gA' // 20c8e-20c95 - 'O' // 20c96 #14 - 'dA' // 20c97-20c9b - 'O' // 20c9c #14 - 'wA' // 20c9d-20cb4 - 'O' // 20cb5 #14 - 'aA' // 20cb6-20cb7 - 'O' // 20cb8 #14 - 'uA' // 20cb9-20cce - 'O' // 20ccf #14 - 'bA' // 20cd0-20cd2 - 'cO' // 20cd3-20cd6 #14 - 'eA' // 20cd7-20cdc - 'O' // 20cdd #14 - 'nA' // 20cde-20cec - 'O' // 20ced #14 - 'pA' // 20cee-20cfe - 'O' // 20cff #14 - 'tA' // 20d00-20d14 - 'O' // 20d15 #14 - 'qA' // 20d16-20d27 - 'O' // 20d28 #14 - 'gA' // 20d29-20d30 - 'aO' // 20d31-20d32 #14 - 'qA' // 20d33-20d44 - 'C' // 20d45 #2 - 'cO' // 20d46-20d49 #14 - 'aA' // 20d4a-20d4b - 'bO' // 20d4c-20d4e #14 - 'hA' // 20d4f-20d57 - 'C' // 20d58 #2 - 'uA' // 20d59-20d6e - 'O' // 20d6f #14 - 'A' // 20d70 - '27S' // 20d71 #720 - 'aA' // 20d72-20d73 - 'O' // 20d74 #14 - 'fA' // 20d75-20d7b - 'O' // 20d7c #14 - 'A' // 20d7d - 'aO' // 20d7e-20d7f #14 - 'uA' // 20d80-20d95 - 'O' // 20d96 #14 - 'dA' // 20d97-20d9b - 'O' // 20d9c #14 - 'iA' // 20d9d-20da6 - 'O' // 20da7 #14 - 'iA' // 20da8-20db1 - 'O' // 20db2 #14 - 'tA' // 20db3-20dc7 - 'O' // 20dc8 #14 - 'wA' // 20dc9-20de0 - 'C' // 20de1 #2 - '1gA' // 20de2-20e03 - 'O' // 20e04 #14 - 'cA' // 20e05-20e08 - 'aO' // 20e09-20e0a #14 - 'aA' // 20e0b-20e0c - 'dO' // 20e0d-20e11 #14 - 'cA' // 20e12-20e15 - 'O' // 20e16 #14 - 'eA' // 20e17-20e1c - 'O' // 20e1d #14 - '1sA' // 20e1e-20e4b - 'O' // 20e4c #14 - 'vA' // 20e4d-20e63 - 'C' // 20e64 #2 - 'gA' // 20e65-20e6c - '42Z' // 20e6d #1117 - 'dA' // 20e6e-20e72 - 'O' // 20e73 #14 - 'A' // 20e74 - 'fO' // 20e75-20e7b #14 - 'oA' // 20e7c-20e8b - 'O' // 20e8c #14 - 'gA' // 20e8d-20e94 - 'C' // 20e95 #2 - 'O' // 20e96 #14 - 'A' // 20e97 - '27S' // 20e98 #720 - 'cA' // 20e99-20e9c - 'O' // 20e9d #14 - 'cA' // 20e9e-20ea1 - 'O' // 20ea2 #14 - 'fA' // 20ea3-20ea9 - 'bO' // 20eaa-20eac #14 - 'hA' // 20ead-20eb5 - 'O' // 20eb6 #14 - '1eA' // 20eb7-20ed6 - 'aO' // 20ed7-20ed8 #14 - 'cA' // 20ed9-20edc - 'O' // 20edd #14 - 'yA' // 20ede-20ef7 - 'O' // 20ef8 #14 - '27S' // 20ef9 #720 - 'aO' // 20efa-20efb #14 - '1fA' // 20efc-20f1c - 'O' // 20f1d #14 - 'gA' // 20f1e-20f25 - 'O' // 20f26 #14 - 'eA' // 20f27-20f2c - 'aO' // 20f2d-20f2e #14 - 'A' // 20f2f - 'aO' // 20f30-20f31 #14 - 'hA' // 20f32-20f3a - 'O' // 20f3b #14 - 'oA' // 20f3c-20f4b - 'O' // 20f4c #14 - 'qA' // 20f4d-20f5e - 'C' // 20f5f #2 - 'cA' // 20f60-20f63 - 'O' // 20f64 #14 - '1mA' // 20f65-20f8c - 'O' // 20f8d #14 - 'aA' // 20f8e-20f8f - 'O' // 20f90 #14 - '1aA' // 20f91-20fac - 'O' // 20fad #14 - 'eA' // 20fae-20fb3 - 'bO' // 20fb4-20fb6 #14 - 'dA' // 20fb7-20fbb - 'O' // 20fbc #14 - '1gA' // 20fbd-20fde - 'O' // 20fdf #14 - 'iA' // 20fe0-20fe9 - 'cO' // 20fea-20fed #14 - '1kA' // 20fee-21013 - 'O' // 21014 #14 - 'gA' // 21015-2101c - 'aO' // 2101d-2101e #14 - '1uA' // 2101f-2104e - 'O' // 2104f #14 - 'kA' // 21050-2105b - 'O' // 2105c #14 - 'qA' // 2105d-2106e - 'O' // 2106f #14 - 'dA' // 21070-21074 - 'O' // 21075 #14 - 'bS' // 21076-21078 #18 - 'aA' // 21079-2107a - '52T' // 2107b #1371 - 'kA' // 2107c-21087 - 'S' // 21088 #18 - 'lA' // 21089-21095 - 'S' // 21096 #18 - 'eA' // 21097-2109c - 'S' // 2109d #18 - 'uA' // 2109e-210b3 - 'S' // 210b4 #18 - 'iA' // 210b5-210be - 'aS' // 210bf-210c0 #18 - '52T' // 210c1 #1371 - 'dA' // 210c2-210c6 - 'bS' // 210c7-210c9 #18 - 'dA' // 210ca-210ce - 'S' // 210cf #18 - 'bA' // 210d0-210d2 - 'S' // 210d3 #18 - 'oA' // 210d4-210e3 - 'S' // 210e4 #18 - 'nA' // 210e5-210f3 - 'bS' // 210f4-210f6 #18 - '2cA' // 210f7-2112e - 'S' // 2112f #18 - 'jA' // 21130-2113a - 'S' // 2113b #18 - 'A' // 2113c - 'S' // 2113d #18 - 'fA' // 2113e-21144 - 'S' // 21145 #18 - 'aA' // 21146-21147 - 'S' // 21148 #18 - 'eA' // 21149-2114e - 'S' // 2114f #18 - '1uA' // 21150-2117f - 'S' // 21180 #18 - 'eA' // 21181-21186 - 'S' // 21187 #18 - '3bA' // 21188-211d8 - 'S' // 211d9 #18 - '1lA' // 211da-21200 - 'C' // 21201 #2 - '2eA' // 21202-2123b - 'S' // 2123c #18 - 'C' // 2123d #2 - 'pA' // 2123e-2124e - 'S' // 2124f #18 - 'dA' // 21250-21254 - 'C' // 21255 #2 - '1cA' // 21256-21273 - 'C' // 21274 #2 - 'eA' // 21275-2127a - 'C' // 2127b #2 - 'S' // 2127c #18 - '1pA' // 2127d-212a7 - 'aS' // 212a8-212a9 #18 - 'eA' // 212aa-212af - 'S' // 212b0 #18 - '1kA' // 212b1-212d6 - 'C' // 212d7 #2 - 'jA' // 212d8-212e2 - 'S' // 212e3 #18 - 'C' // 212e4 #2 - 'wA' // 212e5-212fc - 'C' // 212fd #2 - 'S' // 212fe #18 - 'bA' // 212ff-21301 - 'cS' // 21302-21305 #18 - 'tA' // 21306-2131a - 'C' // 2131b #2 - 'yA' // 2131c-21335 - '80O' // 21336 #2094 - 'bA' // 21337-21339 - 'S' // 2133a #18 - 'hA' // 2133b-21343 - 'C' // 21344 #2 - '1uA' // 21345-21374 - 'aS' // 21375-21376 #18 - 'vA' // 21377-2138d - 'S' // 2138e #18 - 'hA' // 2138f-21397 - 'S' // 21398 #18 - 'bA' // 21399-2139b - 'S' // 2139c #18 - '1lA' // 2139d-213c3 - 'C' // 213c4 #2 - 'aS' // 213c5-213c6 #18 - '1kA' // 213c7-213ec - 'S' // 213ed #18 - 'oA' // 213ee-213fd - 'S' // 213fe #18 - 'sA' // 213ff-21412 - 'S' // 21413 #18 - 'aA' // 21414-21415 - 'S' // 21416 #18 - 'lA' // 21417-21423 - 'S' // 21424 #18 - 'yA' // 21425-2143e - 'S' // 2143f #18 - 'qA' // 21440-21451 - 'S' // 21452 #18 - 'A' // 21453 - 'aS' // 21454-21455 #18 - 'vA' // 21456-2146c - 'aC' // 2146d-2146e #2 - 'zA' // 2146f-21489 - 'S' // 2148a #18 - 'kA' // 2148b-21496 - 'S' // 21497 #18 - '1cA' // 21498-214b5 - 'S' // 214b6 #18 - '1vA' // 214b7-214e7 - 'S' // 214e8 #18 - 'sA' // 214e9-214fc - 'S' // 214fd #18 - '4pA' // 214fe-21576 - 'S' // 21577 #18 - 'iA' // 21578-21581 - 'S' // 21582 #18 - 'rA' // 21583-21595 - 'S' // 21596 #18 - '2kA' // 21597-215d6 - 'C' // 215d7 #2 - '1wA' // 215d8-21609 - 'S' // 2160a #18 - 'gA' // 2160b-21612 - 'S' // 21613 #18 - 'dA' // 21614-21618 - 'S' // 21619 #18 - '1iA' // 2161a-2163d - 'S' // 2163e #18 - 'gA' // 2163f-21646 - 'C' // 21647 #2 - 'xA' // 21648-21660 - 'S' // 21661 #18 - '1uA' // 21662-21691 - 'S' // 21692 #18 - '1fA' // 21693-216b3 - 'C' // 216b4 #2 - 'bA' // 216b5-216b7 - 'S' // 216b8 #18 - 'A' // 216b9 - 'S' // 216ba #18 - 'dA' // 216bb-216bf - 'bS' // 216c0-216c2 #18 - 'oA' // 216c3-216d2 - 'S' // 216d3 #18 - 'A' // 216d4 - 'S' // 216d5 #18 - 'hA' // 216d6-216de - 'S' // 216df #18 - 'eA' // 216e0-216e5 - 'bS' // 216e6-216e8 #18 - 'pA' // 216e9-216f9 - 'bS' // 216fa-216fc #18 - 'A' // 216fd - 'S' // 216fe #18 - 'fA' // 216ff-21705 - 'C' // 21706 #2 - 'eA' // 21707-2170c - 'S' // 2170d #18 - 'aA' // 2170e-2170f - 'S' // 21710 #18 - 'tA' // 21711-21725 - 'S' // 21726 #18 - 'rA' // 21727-21739 - 'bS' // 2173a-2173c #18 - 'dA' // 2173d-21741 - 'C' // 21742 #2 - 'sA' // 21743-21756 - 'S' // 21757 #18 - 'sA' // 21758-2176b - 'eS' // 2176c-21771 #18 - 'A' // 21772 - 'aS' // 21773-21774 #18 - '2aA' // 21775-217aa - 'S' // 217ab #18 - 'cA' // 217ac-217af - 'eS' // 217b0-217b5 #18 - 'lA' // 217b6-217c2 - 'S' // 217c3 #18 - 'bA' // 217c4-217c6 - 'S' // 217c7 #18 - 'pA' // 217c8-217d8 - 'cS' // 217d9-217dc #18 - 'aA' // 217dd-217de - 'S' // 217df #18 - 'nA' // 217e0-217ee - 'S' // 217ef #18 - 'dA' // 217f0-217f4 - 'aS' // 217f5-217f6 #18 - 'A' // 217f7 - 'dS' // 217f8-217fc #18 - '1hA' // 217fd-2181f - 'S' // 21820 #18 - 'fA' // 21821-21827 - 'bS' // 21828-2182a #18 - 'aA' // 2182b-2182c - 'S' // 2182d #18 - 'jA' // 2182e-21838 - 'bS' // 21839-2183b #18 - 'cA' // 2183c-2183f - 'S' // 21840 #18 - 'cA' // 21841-21844 - 'S' // 21845 #18 - 'kA' // 21846-21851 - 'S' // 21852 #18 - 'jA' // 21853-2185d - 'S' // 2185e #18 - 'aA' // 2185f-21860 - 'cS' // 21861-21864 #18 - 'qA' // 21865-21876 - 'S' // 21877 #18 - 'bA' // 21878-2187a - 'S' // 2187b #18 - 'fA' // 2187c-21882 - 'bS' // 21883-21885 #18 - 'wA' // 21886-2189d - 'dS' // 2189e-218a2 #18 - 'yA' // 218a3-218bc - 'C' // 218bd #2 - 'aS' // 218be-218bf #18 - 'pA' // 218c0-218d0 - 'S' // 218d1 #18 - 'cA' // 218d2-218d5 - 'cS' // 218d6-218d9 #18 - '1eA' // 218da-218f9 - 'S' // 218fa #18 - 'gA' // 218fb-21902 - 'bS' // 21903-21905 #18 - 'iA' // 21906-2190f - 'bS' // 21910-21912 #18 - 'aA' // 21913-21914 - 'S' // 21915 #18 - 'eA' // 21916-2191b - 'S' // 2191c #18 - 'dA' // 2191d-21921 - 'S' // 21922 #18 - 'cA' // 21923-21926 - 'D' // 21927 #3 - 'rA' // 21928-2193a - 'D' // 2193b #3 - 'gA' // 2193c-21943 - 'D' // 21944 #3 - 'rA' // 21945-21957 - 'D' // 21958 #3 - 'pA' // 21959-21969 - 'D' // 2196a #3 - 'pA' // 2196b-2197b - 'D' // 2197c #3 - 'bA' // 2197d-2197f - 'D' // 21980 #3 - 'aA' // 21981-21982 - 'D' // 21983 #3 - 'cA' // 21984-21987 - 'D' // 21988 #3 - 'lA' // 21989-21995 - 'D' // 21996 #3 - '1qA' // 21997-219c2 - 'C' // 219c3 #2 - 'vA' // 219c4-219da - 'D' // 219db #3 - 'vA' // 219dc-219f2 - 'D' // 219f3 #3 - '1kA' // 219f4-21a19 - 'C' // 21a1a #2 - 'qA' // 21a1b-21a2c - 'D' // 21a2d #3 - 'eA' // 21a2e-21a33 - 'D' // 21a34 #3 - 'oA' // 21a35-21a44 - 'D' // 21a45 #3 - 'dA' // 21a46-21a4a - 'D' // 21a4b #3 - 'vA' // 21a4c-21a62 - 'D' // 21a63 #3 - '8oA' // 21a64-21b43 - 'D' // 21b44 #3 - '4sA' // 21b45-21bc0 - 'aD' // 21bc1-21bc2 #3 - '3xA' // 21bc3-21c29 - 'D' // 21c2a #3 - '1pA' // 21c2b-21c55 - 'C' // 21c56 #2 - 'xA' // 21c57-21c6f - 'D' // 21c70 #3 - '1vA' // 21c71-21ca1 - 'D' // 21ca2 #3 - 'aA' // 21ca3-21ca4 - 'D' // 21ca5 #3 - 'eA' // 21ca6-21cab - 'D' // 21cac #3 - '4wA' // 21cad-21d2c - 'C' // 21d2d #2 - 'vA' // 21d2e-21d44 - 'C' // 21d45 #2 - 'D' // 21d46 #3 - 'kA' // 21d47-21d52 - 'D' // 21d53 #3 - 'iA' // 21d54-21d5d - 'D' // 21d5e #3 - 'bA' // 21d5f-21d61 - 'C' // 21d62 #2 - 'tA' // 21d63-21d77 - 'C' // 21d78 #2 - 'vA' // 21d79-21d8f - 'D' // 21d90 #3 - 'A' // 21d91 - 'C' // 21d92 #2 - 'hA' // 21d93-21d9b - 'C' // 21d9c #2 - 'cA' // 21d9d-21da0 - 'C' // 21da1 #2 - 'sA' // 21da2-21db5 - 'D' // 21db6 #3 - 'C' // 21db7 #2 - 'aA' // 21db8-21db9 - 'D' // 21dba #3 - 'nA' // 21dbb-21dc9 - 'D' // 21dca #3 - 'eA' // 21dcb-21dd0 - 'D' // 21dd1 #3 - 'mA' // 21dd2-21ddf - 'C' // 21de0 #2 - 'iA' // 21de1-21dea - 'D' // 21deb #3 - 'lA' // 21dec-21df8 - 'D' // 21df9 #3 - '1gA' // 21dfa-21e1b - 'D' // 21e1c #3 - 'eA' // 21e1d-21e22 - 'D' // 21e23 #3 - 'nA' // 21e24-21e32 - 'aC' // 21e33-21e34 #2 - 'aA' // 21e35-21e36 - 'D' // 21e37 #3 - 'dA' // 21e38-21e3c - 'D' // 21e3d #3 - '2vA' // 21e3e-21e88 - 'D' // 21e89 #3 - 'yA' // 21e8a-21ea3 - 'D' // 21ea4 #3 - 'bA' // 21ea5-21ea7 - 'D' // 21ea8 #3 - '1dA' // 21ea9-21ec7 - 'D' // 21ec8 #3 - 'kA' // 21ec9-21ed4 - 'D' // 21ed5 #3 - '2dA' // 21ed6-21f0e - 'D' // 21f0f #3 - 'dA' // 21f10-21f14 - 'D' // 21f15 #3 - 'gA' // 21f16-21f1d - 'C' // 21f1e #2 - '2vA' // 21f1f-21f69 - 'D' // 21f6a #3 - 'jA' // 21f6b-21f75 - 'C' // 21f76 #2 - '1lA' // 21f77-21f9d - 'D' // 21f9e #3 - 'aA' // 21f9f-21fa0 - 'D' // 21fa1 #3 - '2qA' // 21fa2-21fe7 - 'D' // 21fe8 #3 - 'pA' // 21fe9-21ff9 - 'C' // 21ffa #2 - '2uA' // 21ffb-22044 - 'D' // 22045 #3 - 'bA' // 22046-22048 - 'D' // 22049 #3 - '1yA' // 2204a-2207d - 'D' // 2207e #3 - 'zA' // 2207f-22099 - 'D' // 2209a #3 - '1qA' // 2209b-220c6 - 'D' // 220c7 #3 - '1yA' // 220c8-220fb - 'D' // 220fc #3 - '1rA' // 220fd-22129 - 'D' // 2212a #3 - '1uA' // 2212b-2215a - 'D' // 2215b #3 - 'vA' // 2215c-22172 - 'D' // 22173 #3 - 'eA' // 22174-22179 - 'D' // 2217a #3 - 'C' // 2217b #2 - '1jA' // 2217c-221a0 - 'D' // 221a1 #3 - '1dA' // 221a2-221c0 - 'D' // 221c1 #3 - 'A' // 221c2 - 'D' // 221c3 #3 - '2oA' // 221c4-22207 - 'D' // 22208 #3 - 'nA' // 22209-22217 - 'C' // 22218 #2 - '3tA' // 22219-2227b - 'D' // 2227c #3 - '6dA' // 2227d-2231d - 'C' // 2231e #2 - 'aA' // 2231f-22320 - 'D' // 22321 #3 - 'bA' // 22322-22324 - 'D' // 22325 #3 - '5dA' // 22326-223ac - 'C' // 223ad #2 - 'nA' // 223ae-223bc - 'D' // 223bd #3 - 'qA' // 223be-223cf - 'D' // 223d0 #3 - 'eA' // 223d1-223d6 - 'D' // 223d7 #3 - '1gA' // 223d8-223f9 - 'D' // 223fa #3 - '4aA' // 223fb-22464 - 'D' // 22465 #3 - 'jA' // 22466-22470 - 'D' // 22471 #3 - 'xA' // 22472-2248a - 'D' // 2248b #3 - 'dA' // 2248c-22490 - 'D' // 22491 #3 - '1cA' // 22492-224af - 'D' // 224b0 #3 - 'jA' // 224b1-224bb - 'D' // 224bc #3 - 'cA' // 224bd-224c0 - 'D' // 224c1 #3 - 'fA' // 224c2-224c8 - 'D' // 224c9 #3 - 'aA' // 224ca-224cb - 'D' // 224cc #3 - '1eA' // 224cd-224ec - 'D' // 224ed #3 - '1jA' // 224ee-22512 - 'D' // 22513 #3 - 'fA' // 22514-2251a - 'D' // 2251b #3 - 'sA' // 2251c-2252f - 'D' // 22530 #3 - '1hA' // 22531-22553 - 'D' // 22554 #3 - '2cA' // 22555-2258c - 'D' // 2258d #3 - '1fA' // 2258e-225ae - 'D' // 225af #3 - 'mA' // 225b0-225bd - 'D' // 225be #3 - '2uA' // 225bf-22608 - 'C' // 22609 #2 - 'pA' // 2260a-2261a - 'aD' // 2261b-2261c #3 - 'mA' // 2261d-2262a - 'D' // 2262b #3 - '2gA' // 2262c-22667 - 'D' // 22668 #3 - 'pA' // 22669-22679 - 'D' // 2267a #3 - 'zA' // 2267b-22695 - 'D' // 22696 #3 - 'A' // 22697 - 'D' // 22698 #3 - '3kA' // 22699-226f2 - 'C' // 226f3 #2 - 'bD' // 226f4-226f6 #3 - 'zA' // 226f7-22711 - 'D' // 22712 #3 - 'A' // 22713 - 'D' // 22714 #3 - 'eA' // 22715-2271a - 'D' // 2271b #3 - 'bA' // 2271c-2271e - 'D' // 2271f #3 - 'iA' // 22720-22729 - 'D' // 2272a #3 - '2uA' // 2272b-22774 - 'D' // 22775 #3 - 'jA' // 22776-22780 - 'D' // 22781 #3 - 'sA' // 22782-22795 - 'D' // 22796 #3 - '1bA' // 22797-227b3 - 'aD' // 227b4-227b5 #3 - 'vA' // 227b6-227cc - 'D' // 227cd #3 - '1zA' // 227ce-22802 - 'D' // 22803 #3 - '3hA' // 22804-2285a - 'C' // 2285b #2 - 'bA' // 2285c-2285e - 'aD' // 2285f-22860 #3 - 'oA' // 22861-22870 - 'D' // 22871 #3 - '2dA' // 22872-228aa - 'C' // 228ab #2 - 'A' // 228ac - 'D' // 228ad #3 - 'rA' // 228ae-228c0 - 'D' // 228c1 #3 - '1zA' // 228c2-228f6 - 'D' // 228f7 #3 - '1sA' // 228f8-22925 - 'D' // 22926 #3 - 'qA' // 22927-22938 - 'D' // 22939 #3 - 'tA' // 2293a-2294e - 'D' // 2294f #3 - 'vA' // 22950-22966 - 'D' // 22967 #3 - 'bA' // 22968-2296a - 'D' // 2296b #3 - 'sA' // 2296c-2297f - 'D' // 22980 #3 - 'mA' // 22981-2298e - 'C' // 2298f #2 - 'bA' // 22990-22992 - 'D' // 22993 #3 - '8aA' // 22994-22a65 - 'D' // 22a66 #3 - '3bA' // 22a67-22ab7 - 'C' // 22ab8 #2 - 'uA' // 22ab9-22ace - 'D' // 22acf #3 - 'dA' // 22ad0-22ad4 - 'D' // 22ad5 #3 - 'oA' // 22ad6-22ae5 - 'D' // 22ae6 #3 - 'A' // 22ae7 - 'D' // 22ae8 #3 - '1jA' // 22ae9-22b0d - 'D' // 22b0e #3 - 'rA' // 22b0f-22b21 - 'D' // 22b22 #3 - '1aA' // 22b23-22b3e - 'D' // 22b3f #3 - 'bA' // 22b40-22b42 - 'D' // 22b43 #3 - 'aA' // 22b44-22b45 - 'C' // 22b46 #2 - 'gA' // 22b47-22b4e - 'aC' // 22b4f-22b50 #2 - 'xA' // 22b51-22b69 - 'D' // 22b6a #3 - '2fA' // 22b6b-22ba5 - 'C' // 22ba6 #2 - '1hA' // 22ba7-22bc9 - 'D' // 22bca #3 - 'bA' // 22bcb-22bcd - 'D' // 22bce #3 - '2yA' // 22bcf-22c1c - 'C' // 22c1d #2 - 'eA' // 22c1e-22c23 - 'C' // 22c24 #2 - 'A' // 22c25 - 'aD' // 22c26-22c27 #3 - 'oA' // 22c28-22c37 - 'D' // 22c38 #3 - 'rA' // 22c39-22c4b - 'D' // 22c4c #3 - 'cA' // 22c4d-22c50 - '80N' // 22c51 #2093 - 'bA' // 22c52-22c54 - 'D' // 22c55 #3 - 'kA' // 22c56-22c61 - 'D' // 22c62 #3 - '1jA' // 22c63-22c87 - 'D' // 22c88 #3 - 'qA' // 22c89-22c9a - 'D' // 22c9b #3 - 'dA' // 22c9c-22ca0 - 'D' // 22ca1 #3 - 'fA' // 22ca2-22ca8 - 'D' // 22ca9 #3 - 'gA' // 22caa-22cb1 - 'D' // 22cb2 #3 - 'cA' // 22cb3-22cb6 - 'D' // 22cb7 #3 - 'iA' // 22cb8-22cc1 - 'D' // 22cc2 #3 - 'bA' // 22cc3-22cc5 - 'D' // 22cc6 #3 - 'aA' // 22cc7-22cc8 - 'D' // 22cc9 #3 - '2hA' // 22cca-22d06 - 'aD' // 22d07-22d08 #3 - 'hA' // 22d09-22d11 - 'D' // 22d12 #3 - '1vA' // 22d13-22d43 - 'D' // 22d44 #3 - 'fA' // 22d45-22d4b - 'D' // 22d4c #3 - 'yA' // 22d4d-22d66 - 'D' // 22d67 #3 - '1jA' // 22d68-22d8c - 'D' // 22d8d #3 - 'fA' // 22d8e-22d94 - 'D' // 22d95 #3 - 'iA' // 22d96-22d9f - 'D' // 22da0 #3 - 'aA' // 22da1-22da2 - 'aD' // 22da3-22da4 #3 - 'qA' // 22da5-22db6 - 'D' // 22db7 #3 - '1nA' // 22db8-22de0 - 'C' // 22de1 #2 - 'kA' // 22de2-22ded - 'D' // 22dee #3 - '1cA' // 22def-22e0c - 'D' // 22e0d #3 - '1mA' // 22e0e-22e35 - 'D' // 22e36 #3 - 'jA' // 22e37-22e41 - '80M' // 22e42 #2092 - '1zA' // 22e43-22e77 - 'D' // 22e78 #3 - 'qA' // 22e79-22e8a - 'Q' // 22e8b #16 - '1lA' // 22e8c-22eb2 - 'Q' // 22eb3 #16 - '2fA' // 22eb4-22eee - 'Q' // 22eef #16 - '5aA' // 22ef0-22f73 - 'Q' // 22f74 #16 - '3hA' // 22f75-22fcb - 'Q' // 22fcc #16 - 'uA' // 22fcd-22fe2 - 'Q' // 22fe3 #16 - 'fA' // 22fe4-22fea - 'C' // 22feb #2 - '2rA' // 22fec-23032 - 'Q' // 23033 #16 - 'oA' // 23034-23043 - 'Q' // 23044 #16 - 'eA' // 23045-2304a - 'Q' // 2304b #16 - 'yA' // 2304c-23065 - 'Q' // 23066 #16 - 'uA' // 23067-2307c - 'aQ' // 2307d-2307e #16 - 'nA' // 2307f-2308d - 'Q' // 2308e #16 - '1mA' // 2308f-230b6 - 'Q' // 230b7 #16 - 'cA' // 230b8-230bb - 'Q' // 230bc #16 - '1bA' // 230bd-230d9 - 'Q' // 230da #16 - '1mA' // 230db-23102 - 'Q' // 23103 #16 - '2dA' // 23104-2313c - 'Q' // 2313d #16 - '2jA' // 2313e-2317c - 'Q' // 2317d #16 - 'cA' // 2317e-23181 - 'Q' // 23182 #16 - '1fA' // 23183-231a3 - 'aQ' // 231a4-231a5 #16 - 'lA' // 231a6-231b2 - 'Q' // 231b3 #16 - 'aA' // 231b4-231b5 - 'C' // 231b6 #2 - 'kA' // 231b7-231c2 - 'aC' // 231c3-231c4 #2 - 'bA' // 231c5-231c7 - 'aQ' // 231c8-231c9 #16 - '1eA' // 231ca-231e9 - 'Q' // 231ea #16 - 'iA' // 231eb-231f4 - 'C' // 231f5 #2 - 'A' // 231f6 - 'bQ' // 231f7-231f9 #16 - 'tA' // 231fa-2320e - 'Q' // 2320f #16 - 'tA' // 23210-23224 - 'Q' // 23225 #16 - 'hA' // 23226-2322e - 'Q' // 2322f #16 - 'A' // 23230 - 'cQ' // 23231-23234 #16 - '1fA' // 23235-23255 - 'Q' // 23256 #16 - 'fA' // 23257-2325d - 'Q' // 2325e #16 - 'bA' // 2325f-23261 - 'Q' // 23262 #16 - '1cA' // 23263-23280 - 'Q' // 23281 #16 - 'fA' // 23282-23288 - 'aQ' // 23289-2328a #16 - '1eA' // 2328b-232aa - 'bQ' // 232ab-232ad #16 - '1iA' // 232ae-232d1 - 'Q' // 232d2 #16 - 'lA' // 232d3-232df - 'aQ' // 232e0-232e1 #16 - '1cA' // 232e2-232ff - 'Q' // 23300 #16 - 'hA' // 23301-23309 - 'Q' // 2330a #16 - 'sA' // 2330b-2331e - 'Q' // 2331f #16 - '3cA' // 23320-23371 - 'C' // 23372 #2 - '2lA' // 23373-233b3 - '80L' // 233b4 #2091 - 'vA' // 233b5-233cb - '27R' // 233cc #719 - 'bA' // 233cd-233cf - 'C' // 233d0 #2 - 'A' // 233d1 - 'aC' // 233d2-233d3 #2 - 'A' // 233d4 - 'C' // 233d5 #2 - 'cA' // 233d6-233d9 - 'C' // 233da #2 - 'bA' // 233db-233dd - 'Q' // 233de #16 - 'C' // 233df #2 - 'cA' // 233e0-233e3 - 'C' // 233e4 #2 - 'A' // 233e5 - 'Q' // 233e6 #16 - 'lA' // 233e7-233f3 - 'aQ' // 233f4-233f5 #16 - 'bA' // 233f6-233f8 - 'aQ' // 233f9-233fa #16 - 'bA' // 233fb-233fd - '27R' // 233fe #719 - 'A' // 233ff - 'Q' // 23400 #16 - '2iA' // 23401-2343e - 'Q' // 2343f #16 - 'iA' // 23440-23449 - 'aC' // 2344a-2344b #2 - 'cA' // 2344c-2344f - 'Q' // 23450 #16 - 'C' // 23451 #2 - 'rA' // 23452-23464 - 'C' // 23465 #2 - 'hA' // 23466-2346e - 'Q' // 2346f #16 - 'aA' // 23470-23471 - 'Q' // 23472 #16 - '4hA' // 23473-234e3 - 'C' // 234e4 #2 - 'Q' // 234e5 #16 - '1xA' // 234e6-23518 - 'Q' // 23519 #16 - 'uA' // 2351a-2352f - 'Q' // 23530 #16 - '1eA' // 23531-23550 - 'Q' // 23551 #16 - 'gA' // 23552-23559 - '27R' // 2355a #719 - 'kA' // 2355b-23566 - 'Q' // 23567 #16 - '1qA' // 23568-23593 - 'C' // 23594 #2 - 'Q' // 23595 #16 - 'bA' // 23596-23598 - 'Q' // 23599 #16 - 'aA' // 2359a-2359b - 'Q' // 2359c #16 - '1cA' // 2359d-235ba - 'Q' // 235bb #16 - 'gA' // 235bc-235c3 - 'C' // 235c4 #2 - 'gA' // 235c5-235cc - 'bQ' // 235cd-235cf #16 - '1hA' // 235d0-235f2 - 'Q' // 235f3 #16 - 'kA' // 235f4-235ff - 'Q' // 23600 #16 - 'uA' // 23601-23616 - 'Q' // 23617 #16 - 'aA' // 23618-23619 - 'Q' // 2361a #16 - '1bA' // 2361b-23637 - 'bC' // 23638-2363a #2 - 'A' // 2363b - 'Q' // 2363c #16 - 'bA' // 2363d-2363f - 'Q' // 23640 #16 - 'eA' // 23641-23646 - 'C' // 23647 #2 - 'pA' // 23648-23658 - 'Q' // 23659 #16 - 'dA' // 2365a-2365e - 'Q' // 2365f #16 - 'vA' // 23660-23676 - 'Q' // 23677 #16 - 'uA' // 23678-2368d - 'Q' // 2368e #16 - 'nA' // 2368f-2369d - 'Q' // 2369e #16 - 'fA' // 2369f-236a5 - 'Q' // 236a6 #16 - 'eA' // 236a7-236ac - 'Q' // 236ad #16 - 'kA' // 236ae-236b9 - 'Q' // 236ba #16 - '1iA' // 236bb-236de - 'Q' // 236df #16 - 'mA' // 236e0-236ed - 'Q' // 236ee #16 - 'sA' // 236ef-23702 - 'Q' // 23703 #16 - 'gA' // 23704-2370b - 'C' // 2370c #2 - 'hA' // 2370d-23715 - 'Q' // 23716 #16 - 'dA' // 23717-2371b - 'C' // 2371c #2 - 'bA' // 2371d-2371f - 'Q' // 23720 #16 - 'kA' // 23721-2372c - 'Q' // 2372d #16 - 'A' // 2372e - 'Q' // 2372f #16 - 'nA' // 23730-2373e - '27R' // 2373f #719 - '1hA' // 23740-23762 - 'aC' // 23763-23764 #2 - 'A' // 23765 - 'Q' // 23766 #16 - 'yA' // 23767-23780 - 'Q' // 23781 #16 - '1eA' // 23782-237a1 - 'Q' // 237a2 #16 - 'xA' // 237a3-237bb - 'Q' // 237bc #16 - 'dA' // 237bd-237c1 - 'Q' // 237c2 #16 - 'qA' // 237c3-237d4 - 'bQ' // 237d5-237d7 #16 - 'nA' // 237d8-237e6 - 'C' // 237e7 #2 - 'hA' // 237e8-237f0 - 'C' // 237f1 #2 - 'lA' // 237f2-237fe - 'C' // 237ff #2 - '1iA' // 23800-23823 - 'C' // 23824 #2 - 'tA' // 23825-23839 - 'Q' // 2383a #16 - 'aA' // 2383b-2383c - 'C' // 2383d #2 - '14wA' // 2383e-239c1 - 'Q' // 239c2 #16 - '8dA' // 239c3-23a97 - 'C' // 23a98 #2 - 'mA' // 23a99-23aa6 - 'Q' // 23aa7 #16 - '1xA' // 23aa8-23ada - 'Q' // 23adb #16 - 'qA' // 23adc-23aed - 'Q' // 23aee #16 - 'jA' // 23aef-23af9 - 'Q' // 23afa #16 - '1dA' // 23afb-23b19 - 'Q' // 23b1a #16 - '2jA' // 23b1b-23b59 - 'Q' // 23b5a #16 - '10cA' // 23b5b-23c62 - 'Q' // 23c63 #16 - 'zA' // 23c64-23c7e - 'C' // 23c7f #2 - 'xA' // 23c80-23c98 - 'bQ' // 23c99-23c9b #16 - 'xA' // 23c9c-23cb4 - 'Q' // 23cb5 #16 - 'A' // 23cb6 - 'Q' // 23cb7 #16 - 'eA' // 23cb8-23cbd - 'C' // 23cbe #2 - 'gA' // 23cbf-23cc6 - 'bQ' // 23cc7-23cc9 #16 - '1wA' // 23cca-23cfb - 'aQ' // 23cfc-23cfd #16 - '27R' // 23cfe #719 - 'Q' // 23cff #16 - 'C' // 23d00 #2 - 'lA' // 23d01-23d0d - 'C' // 23d0e #2 - '1vA' // 23d0f-23d3f - '27R' // 23d40 #719 - 'yA' // 23d41-23d5a - 'Q' // 23d5b #16 - '1gA' // 23d5c-23d7d - 'Q' // 23d7e #16 - 'oA' // 23d7f-23d8e - 'Q' // 23d8f #16 - '1kA' // 23d90-23db5 - 'gQ' // 23db6-23dbd #16 - 'tA' // 23dbe-23dd2 - 'C' // 23dd3 #2 - 'nA' // 23dd4-23de2 - 'Q' // 23de3 #16 - 'sA' // 23de4-23df7 - 'Q' // 23df8 #16 - 'aC' // 23df9-23dfa #2 - 'jA' // 23dfb-23e05 - 'Q' // 23e06 #16 - 'iA' // 23e07-23e10 - 'Q' // 23e11 #16 - 'yA' // 23e12-23e2b - 'eQ' // 23e2c-23e31 #16 - 'fA' // 23e32-23e38 - 'Q' // 23e39 #16 - '2yA' // 23e3a-23e87 - 'cQ' // 23e88-23e8b #16 - '1rA' // 23e8c-23eb8 - 'Q' // 23eb9 #16 - 'dA' // 23eba-23ebe - 'Q' // 23ebf #16 - 'vA' // 23ec0-23ed6 - 'Q' // 23ed7 #16 - '1dA' // 23ed8-23ef6 - 'eQ' // 23ef7-23efc #16 - '2cA' // 23efd-23f34 - 'Q' // 23f35 #16 - 'jA' // 23f36-23f40 - 'Q' // 23f41 #16 - 'gA' // 23f42-23f49 - 'Q' // 23f4a #16 - 'uA' // 23f4b-23f60 - 'R' // 23f61 #17 - '1aA' // 23f62-23f7d - 'C' // 23f7e #2 - 'cR' // 23f7f-23f82 #17 - 'kA' // 23f83-23f8e - 'R' // 23f8f #17 - '1iA' // 23f90-23fb3 - 'R' // 23fb4 #17 - 'aA' // 23fb5-23fb6 - 'R' // 23fb7 #17 - 'gA' // 23fb8-23fbf - 'R' // 23fc0 #17 - 'cA' // 23fc1-23fc4 - 'R' // 23fc5 #17 - '1jA' // 23fc6-23fea - 'eR' // 23feb-23ff0 #17 - '1eA' // 23ff1-24010 - 'R' // 24011 #17 - '1lA' // 24012-24038 - 'dR' // 24039-2403d #17 - 'lA' // 2403e-2404a - 'C' // 2404b #2 - 'jA' // 2404c-24056 - 'R' // 24057 #17 - '1rA' // 24058-24084 - 'R' // 24085 #17 - 'dA' // 24086-2408a - 'bR' // 2408b-2408d #17 - 'bA' // 2408e-24090 - 'R' // 24091 #17 - 'cA' // 24092-24095 - 'C' // 24096 #2 - '1wA' // 24097-240c8 - 'R' // 240c9 #17 - 'vA' // 240ca-240e0 - 'R' // 240e1 #17 - 'iA' // 240e2-240eb - 'R' // 240ec #17 - 'uA' // 240ed-24102 - 'C' // 24103 #2 - 'R' // 24104 #17 - 'iA' // 24105-2410e - 'R' // 2410f #17 - 'hA' // 24110-24118 - 'R' // 24119 #17 - '1jA' // 2411a-2413e - 'aR' // 2413f-24140 #17 - 'bA' // 24141-24143 - 'R' // 24144 #17 - 'hA' // 24145-2414d - 'R' // 2414e #17 - 'eA' // 2414f-24154 - 'bR' // 24155-24157 #17 - 'cA' // 24158-2415b - 'R' // 2415c #17 - 'aA' // 2415d-2415e - 'R' // 2415f #17 - 'A' // 24160 - 'R' // 24161 #17 - 'tA' // 24162-24176 - 'R' // 24177 #17 - 'aA' // 24178-24179 - 'R' // 2417a #17 - '1mA' // 2417b-241a2 - 'bR' // 241a3-241a5 #17 - 'eA' // 241a6-241ab - 'R' // 241ac #17 - 'gA' // 241ad-241b4 - 'R' // 241b5 #17 - 'oA' // 241b6-241c5 - 'C' // 241c6 #2 - 'eA' // 241c7-241cc - 'R' // 241cd #17 - 'sA' // 241ce-241e1 - 'R' // 241e2 #17 - 'xA' // 241e3-241fb - 'R' // 241fc #17 - 'A' // 241fd - 'C' // 241fe #2 - '1aA' // 241ff-2421a - 'R' // 2421b #17 - '1tA' // 2421c-2424a - 'R' // 2424b #17 - 'iA' // 2424c-24255 - 'R' // 24256 #17 - 'aA' // 24257-24258 - 'R' // 24259 #17 - '1aA' // 2425a-24275 - 'bR' // 24276-24278 #17 - 'jA' // 24279-24283 - 'R' // 24284 #17 - 'mA' // 24285-24292 - 'R' // 24293 #17 - 'A' // 24294 - 'R' // 24295 #17 - 'nA' // 24296-242a4 - 'R' // 242a5 #17 - 'xA' // 242a6-242be - 'R' // 242bf #17 - 'A' // 242c0 - 'R' // 242c1 #17 - 'fA' // 242c2-242c8 - 'aR' // 242c9-242ca #17 - '1hA' // 242cb-242ed - '52S' // 242ee #1370 - 'jA' // 242ef-242f9 - 'R' // 242fa #17 - 'qA' // 242fb-2430c - 'R' // 2430d #17 - 'kA' // 2430e-24319 - 'R' // 2431a #17 - 'xA' // 2431b-24333 - 'R' // 24334 #17 - 'rA' // 24335-24347 - 'R' // 24348 #17 - 'xA' // 24349-24361 - 'cR' // 24362-24365 #17 - '1kA' // 24366-2438b - 'R' // 2438c #17 - 'hA' // 2438d-24395 - 'R' // 24396 #17 - 'dA' // 24397-2439b - 'R' // 2439c #17 - '1dA' // 2439d-243bb - 'C' // 243bc #2 - 'R' // 243bd #17 - 'bA' // 243be-243c0 - 'R' // 243c1 #17 - 'mA' // 243c2-243cf - 'C' // 243d0 #2 - 'wA' // 243d1-243e8 - 'aR' // 243e9-243ea #17 - 'fA' // 243eb-243f1 - 'R' // 243f2 #17 - 'dA' // 243f3-243f7 - 'R' // 243f8 #17 - 'jA' // 243f9-24403 - 'R' // 24404 #17 - '1uA' // 24405-24434 - 'aR' // 24435-24436 #17 - '1hA' // 24437-24459 - 'aR' // 2445a-2445b #17 - 'vA' // 2445c-24472 - 'R' // 24473 #17 - 'rA' // 24474-24486 - 'aR' // 24487-24488 #17 - '1uA' // 24489-244b8 - 'R' // 244b9 #17 - 'aA' // 244ba-244bb - 'R' // 244bc #17 - 'pA' // 244bd-244cd - 'R' // 244ce #17 - 'cA' // 244cf-244d2 - 'R' // 244d3 #17 - 'aA' // 244d4-244d5 - 'R' // 244d6 #17 - '1sA' // 244d7-24504 - 'R' // 24505 #17 - 'zA' // 24506-24520 - 'R' // 24521 #17 - '3gA' // 24522-24577 - 'R' // 24578 #17 - '2zA' // 24579-245c7 - 'R' // 245c8 #17 - '2zA' // 245c9-24617 - 'R' // 24618 #17 - 'oA' // 24619-24628 - 'C' // 24629 #2 - 'R' // 2462a #17 - '2eA' // 2462b-24664 - 'R' // 24665 #17 - 'mA' // 24666-24673 - 'R' // 24674 #17 - '1gA' // 24675-24696 - 'R' // 24697 #17 - 'lA' // 24698-246a4 - 'C' // 246a5 #2 - '1sA' // 246a6-246d3 - 'R' // 246d4 #17 - '1vA' // 246d5-24705 - 'R' // 24706 #17 - '1cA' // 24707-24724 - 'R' // 24725 #17 - 'hA' // 24726-2472e - 'R' // 2472f #17 - '3pA' // 24730-2478e - 'R' // 2478f #17 - '3aA' // 24790-247df - 'R' // 247e0 #17 - 'oA' // 247e1-247f0 - 'C' // 247f1 #2 - '1eA' // 247f2-24811 - 'R' // 24812 #17 - 'oA' // 24813-24822 - 'R' // 24823 #17 - '3oA' // 24824-24881 - 'R' // 24882 #17 - 'rA' // 24883-24895 - 'C' // 24896 #2 - '3cA' // 24897-248e8 - '52S' // 248e9 #1370 - 'eA' // 248ea-248ef - 'cR' // 248f0-248f3 #17 - 'fA' // 248f4-248fa - 'R' // 248fb #17 - 'bA' // 248fc-248fe - 'bR' // 248ff-24901 #17 - 'iA' // 24902-2490b - 'R' // 2490c #17 - 'hA' // 2490d-24915 - 'aR' // 24916-24917 #17 - 'A' // 24918 - 'R' // 24919 #17 - 'tA' // 2491a-2492e - 'R' // 2492f #17 - 'bA' // 24930-24932 - 'aR' // 24933-24934 #17 - 'hA' // 24935-2493d - 'eR' // 2493e-24943 #17 - '1cA' // 24944-24961 - 'aR' // 24962-24963 #17 - 'oA' // 24964-24973 - 'bR' // 24974-24976 #17 - 'cA' // 24977-2497a - 'R' // 2497b #17 - 'bA' // 2497c-2497e - 'R' // 2497f #17 - 'aA' // 24980-24981 - 'R' // 24982 #17 - 'dA' // 24983-24987 - 'gR' // 24988-2498f #17 - 'cA' // 24990-24993 - 'R' // 24994 #17 - 'nA' // 24995-249a3 - 'R' // 249a4 #17 - 'aA' // 249a5-249a6 - 'R' // 249a7 #17 - 'A' // 249a8 - 'R' // 249a9 #17 - 'A' // 249aa - 'bR' // 249ab-249ad #17 - 'hA' // 249ae-249b6 - 'cR' // 249b7-249ba #17 - 'P' // 249bb #15 - 'hA' // 249bc-249c4 - 'P' // 249c5 #15 - 'iA' // 249c6-249cf - 'P' // 249d0 #15 - 'hA' // 249d1-249d9 - 'P' // 249da #15 - 'bA' // 249db-249dd - 'aP' // 249de-249df #15 - 'bA' // 249e0-249e2 - 'P' // 249e3 #15 - 'A' // 249e4 - 'P' // 249e5 #15 - 'eA' // 249e6-249eb - 'aP' // 249ec-249ed #15 - 'gA' // 249ee-249f5 - 'cP' // 249f6-249f9 #15 - 'A' // 249fa - 'P' // 249fb #15 - 'qA' // 249fc-24a0d - 'P' // 24a0e #15 - 'bA' // 24a0f-24a11 - '37A' // 24a12 #962 - 'P' // 24a13 #15 - 'A' // 24a14 - 'P' // 24a15 #15 - 'jA' // 24a16-24a20 - 'iP' // 24a21-24a2a #15 - 'rA' // 24a2b-24a3d - 'P' // 24a3e #15 - 'bA' // 24a3f-24a41 - 'P' // 24a42 #15 - 'aA' // 24a43-24a44 - 'P' // 24a45 #15 - 'cA' // 24a46-24a49 - 'P' // 24a4a #15 - 'aA' // 24a4b-24a4c - 'C' // 24a4d #2 - 'cP' // 24a4e-24a51 #15 - 'jA' // 24a52-24a5c - 'P' // 24a5d #15 - 'fA' // 24a5e-24a64 - 'bP' // 24a65-24a67 #15 - 'hA' // 24a68-24a70 - 'P' // 24a71 #15 - 'dA' // 24a72-24a76 - 'cP' // 24a77-24a7a #15 - 'pA' // 24a7b-24a8b - 'P' // 24a8c #15 - 'eA' // 24a8d-24a92 - 'cP' // 24a93-24a96 #15 - 'lA' // 24a97-24aa3 - 'cP' // 24aa4-24aa7 #15 - 'hA' // 24aa8-24ab0 - 'bP' // 24ab1-24ab3 #15 - 'eA' // 24ab4-24ab9 - 'bP' // 24aba-24abc #15 - 'bA' // 24abd-24abf - 'P' // 24ac0 #15 - 'eA' // 24ac1-24ac6 - 'P' // 24ac7 #15 - 'aA' // 24ac8-24ac9 - 'P' // 24aca #15 - 'eA' // 24acb-24ad0 - 'P' // 24ad1 #15 - 'lA' // 24ad2-24ade - 'P' // 24adf #15 - 'aA' // 24ae0-24ae1 - 'P' // 24ae2 #15 - 'eA' // 24ae3-24ae8 - 'P' // 24ae9 #15 - '1jA' // 24aea-24b0e - 'P' // 24b0f #15 - '2qA' // 24b10-24b55 - 'C' // 24b56 #2 - 'vA' // 24b57-24b6d - 'P' // 24b6e #15 - 'C' // 24b6f #2 - '5bA' // 24b70-24bf4 - 'P' // 24bf5 #15 - 'rA' // 24bf6-24c08 - 'P' // 24c09 #15 - 'kA' // 24c0a-24c15 - 'C' // 24c16 #2 - '5dA' // 24c17-24c9d - 'aP' // 24c9e-24c9f #15 - '1nA' // 24ca0-24cc8 - 'P' // 24cc9 #15 - 'nA' // 24cca-24cd8 - 'P' // 24cd9 #15 - '1qA' // 24cda-24d05 - 'P' // 24d06 #15 - 'kA' // 24d07-24d12 - 'P' // 24d13 #15 - 'C' // 24d14 #2 - '6fA' // 24d15-24db7 - 'P' // 24db8 #15 - '1vA' // 24db9-24de9 - 'aP' // 24dea-24deb #15 - 'wA' // 24dec-24e03 - 'C' // 24e04 #2 - 'hA' // 24e05-24e0d - 'C' // 24e0e #2 - '1mA' // 24e0f-24e36 - 'C' // 24e37 #2 - 'bA' // 24e38-24e3a - 'P' // 24e3b #15 - 'sA' // 24e3c-24e4f - 'P' // 24e50 #15 - 'xA' // 24e51-24e69 - 'C' // 24e6a #2 - '1eA' // 24e6b-24e8a - 'C' // 24e8b #2 - 'xA' // 24e8c-24ea4 - 'P' // 24ea5 #15 - 'A' // 24ea6 - 'P' // 24ea7 #15 - '3wA' // 24ea8-24f0d - 'P' // 24f0e #15 - '2xA' // 24f0f-24f5b - 'P' // 24f5c #15 - '1jA' // 24f5d-24f81 - 'P' // 24f82 #15 - 'bA' // 24f83-24f85 - 'P' // 24f86 #15 - 'oA' // 24f87-24f96 - 'P' // 24f97 #15 - 'aA' // 24f98-24f99 - 'P' // 24f9a #15 - 'mA' // 24f9b-24fa8 - 'P' // 24fa9 #15 - 'mA' // 24faa-24fb7 - 'P' // 24fb8 #15 - 'hA' // 24fb9-24fc1 - 'P' // 24fc2 #15 - '1tA' // 24fc3-24ff1 - 'C' // 24ff2 #2 - '2dA' // 24ff3-2502b - 'P' // 2502c #15 - '1bA' // 2502d-25049 - 'C' // 2504a #2 - 'fA' // 2504b-25051 - 'P' // 25052 #15 - 'aA' // 25053-25054 - 'C' // 25055 #2 - '2rA' // 25056-2509c - 'P' // 2509d #15 - '5aA' // 2509e-25121 - 'C' // 25122 #2 - 'gA' // 25123-2512a - '37A' // 2512b #962 - '1aA' // 2512c-25147 - 'P' // 25148 #15 - '1yA' // 25149-2517c - 'aP' // 2517d-2517e #15 - '1oA' // 2517f-251a8 - 'C' // 251a9 #2 - '1hA' // 251aa-251cc - '52R' // 251cd #1369 - 'tA' // 251ce-251e2 - 'P' // 251e3 #15 - 'A' // 251e4 - 'C' // 251e5 #2 - 'aP' // 251e6-251e7 #15 - '2aA' // 251e8-2521d - 'C' // 2521e #2 - 'A' // 2521f - 'aP' // 25220-25221 #15 - '1oA' // 25222-2524b - 'C' // 2524c #2 - 'bA' // 2524d-2524f - 'P' // 25250 #15 - '2sA' // 25251-25298 - 'P' // 25299 #15 - '1rA' // 2529a-252c6 - 'P' // 252c7 #15 - 'oA' // 252c8-252d7 - 'P' // 252d8 #15 - '1zA' // 252d9-2530d - 'P' // 2530e #15 - 'aA' // 2530f-25310 - 'P' // 25311 #15 - 'A' // 25312 - 'P' // 25313 #15 - '9zA' // 25314-25418 - 'P' // 25419 #15 - 'jA' // 2541a-25424 - 'P' // 25425 #15 - 'gA' // 25426-2542d - 'C' // 2542e #2 - 'aP' // 2542f-25430 #15 - 'tA' // 25431-25445 - 'P' // 25446 #15 - '1jA' // 25447-2546b - 'P' // 2546c #15 - 'A' // 2546d - '37A' // 2546e #962 - '1dA' // 2546f-2548d - 'C' // 2548e #2 - 'jA' // 2548f-25499 - 'P' // 2549a #15 - '2iA' // 2549b-254d8 - 'C' // 254d9 #2 - '1yA' // 254da-2550d - 'C' // 2550e #2 - '1gA' // 2550f-25530 - 'P' // 25531 #15 - 'bA' // 25532-25534 - 'P' // 25535 #15 - 'hA' // 25536-2553e - 'P' // 2553f #15 - 'zA' // 25540-2555a - 'cP' // 2555b-2555e #15 - 'bA' // 2555f-25561 - 'P' // 25562 #15 - 'aA' // 25563-25564 - 'aP' // 25565-25566 #15 - 'yA' // 25567-25580 - 'P' // 25581 #15 - 'aA' // 25582-25583 - 'P' // 25584 #15 - 'iA' // 25585-2558e - 'P' // 2558f #15 - 'vA' // 25590-255a6 - 'C' // 255a7 #2 - 'pA' // 255a8-255b8 - 'P' // 255b9 #15 - 'zA' // 255ba-255d4 - 'P' // 255d5 #15 - 'dA' // 255d6-255da - 'P' // 255db #15 - 'cA' // 255dc-255df - 'P' // 255e0 #15 - '1iA' // 255e1-25604 - 'P' // 25605 #15 - '1tA' // 25606-25634 - 'P' // 25635 #15 - 'zA' // 25636-25650 - 'P' // 25651 #15 - '1rA' // 25652-2567e - 'C' // 2567f #2 - 'bA' // 25680-25682 - '37A' // 25683 #962 - 'pA' // 25684-25694 - 'P' // 25695 #15 - '2xA' // 25696-256e2 - 'P' // 256e3 #15 - 'qA' // 256e4-256f5 - 'P' // 256f6 #15 - 'nA' // 256f7-25705 - 'P' // 25706 #15 - 'uA' // 25707-2571c - 'P' // 2571d #15 - 'fA' // 2571e-25724 - 'P' // 25725 #15 - 'vA' // 25726-2573c - 'P' // 2573d #15 - '1xA' // 2573e-25770 - 'C' // 25771 #2 - 'P' // 25772 #15 - '2aA' // 25773-257a8 - 'C' // 257a9 #2 - 'iA' // 257aa-257b3 - 'C' // 257b4 #2 - 'qA' // 257b5-257c6 - 'P' // 257c7 #15 - 'vA' // 257c8-257de - 'bP' // 257df-257e1 #15 - '4lA' // 257e2-25856 - 'P' // 25857 #15 - 'dA' // 25858-2585c - 'P' // 2585d #15 - 'sA' // 2585e-25871 - 'P' // 25872 #15 - 'A' // 25873 - 'C' // 25874 #2 - '3dA' // 25875-258c7 - 'P' // 258c8 #15 - 'tA' // 258c9-258dd - 'P' // 258de #15 - 'aA' // 258df-258e0 - 'P' // 258e1 #15 - '1fA' // 258e2-25902 - 'P' // 25903 #15 - '2mA' // 25904-25945 - 'P' // 25946 #15 - 'nA' // 25947-25955 - 'P' // 25956 #15 - '3fA' // 25957-259ab - 'P' // 259ac #15 - 'vA' // 259ad-259c3 - 'C' // 259c4 #2 - 'fA' // 259c5-259cb - '52R' // 259cc #1369 - 'fA' // 259cd-259d3 - 'C' // 259d4 #2 - '4vA' // 259d5-25a53 - 'P' // 25a54 #15 - '2kA' // 25a55-25a94 - 'P' // 25a95 #15 - 'eA' // 25a96-25a9b - 'K' // 25a9c #10 - 'pA' // 25a9d-25aad - 'aK' // 25aae-25aaf #10 - '1lA' // 25ab0-25ad6 - 'C' // 25ad7 #2 - 'jA' // 25ad8-25ae2 - 'aC' // 25ae3-25ae4 #2 - 'cA' // 25ae5-25ae8 - 'K' // 25ae9 #10 - 'fA' // 25aea-25af0 - 'C' // 25af1 #2 - '4yA' // 25af2-25b73 - 'K' // 25b74 #10 - 'sA' // 25b75-25b88 - 'K' // 25b89 #10 - '1mA' // 25b8a-25bb1 - 'C' // 25bb2 #2 - 'aK' // 25bb3-25bb4 #10 - 'pA' // 25bb5-25bc5 - 'K' // 25bc6 #10 - '1bA' // 25bc7-25be3 - 'K' // 25be4 #10 - 'bA' // 25be5-25be7 - 'K' // 25be8 #10 - 'wA' // 25be9-25c00 - 'K' // 25c01 #10 - 'cA' // 25c02-25c05 - 'K' // 25c06 #10 - 'yA' // 25c07-25c20 - 'K' // 25c21 #10 - '1mA' // 25c22-25c49 - 'K' // 25c4a #10 - 'C' // 25c4b #2 - 'wA' // 25c4c-25c63 - 'C' // 25c64 #2 - 'K' // 25c65 #10 - '1pA' // 25c66-25c90 - 'K' // 25c91 #10 - 'qA' // 25c92-25ca3 - 'K' // 25ca4 #10 - 'zA' // 25ca5-25cbf - 'aK' // 25cc0-25cc1 #10 - '2gA' // 25cc2-25cfd - 'K' // 25cfe #10 - '1fA' // 25cff-25d1f - 'K' // 25d20 #10 - 'nA' // 25d21-25d2f - 'K' // 25d30 #10 - 'qA' // 25d31-25d42 - 'K' // 25d43 #10 - '3fA' // 25d44-25d98 - 'K' // 25d99 #10 - 'fA' // 25d9a-25da0 - 'C' // 25da1 #2 - 'vA' // 25da2-25db8 - 'K' // 25db9 #10 - '3eA' // 25dba-25e0d - 'K' // 25e0e #10 - '1dA' // 25e0f-25e2d - 'C' // 25e2e #2 - 'yA' // 25e2f-25e48 - 'K' // 25e49 #10 - 'kA' // 25e4a-25e55 - 'C' // 25e56 #2 - 'jA' // 25e57-25e61 - 'C' // 25e62 #2 - 'aA' // 25e63-25e64 - 'C' // 25e65 #2 - 'zA' // 25e66-25e80 - 'bK' // 25e81-25e83 #10 - '1gA' // 25e84-25ea5 - 'K' // 25ea6 #10 - 'tA' // 25ea7-25ebb - 'K' // 25ebc #10 - 'dA' // 25ebd-25ec1 - 'C' // 25ec2 #2 - 'sA' // 25ec3-25ed6 - 'K' // 25ed7 #10 - '80K' // 25ed8 #2090 - 'nA' // 25ed9-25ee7 - 'B' // 25ee8 #1 - '1vA' // 25ee9-25f19 - 'K' // 25f1a #10 - 'gA' // 25f1b-25f22 - 'B' // 25f23 #1 - '1lA' // 25f24-25f4a - 'K' // 25f4b #10 - 'oA' // 25f4c-25f5b - 'B' // 25f5c #1 - '4nA' // 25f5d-25fd3 - 'B' // 25fd4 #1 - 'jA' // 25fd5-25fdf - 'B' // 25fe0 #1 - 'aK' // 25fe1-25fe2 #10 - 'wA' // 25fe3-25ffa - 'B' // 25ffb #1 - 'oA' // 25ffc-2600b - 'B' // 2600c #1 - 'iA' // 2600d-26016 - 'B' // 26017 #1 - 'hA' // 26018-26020 - 'K' // 26021 #10 - 'fA' // 26022-26028 - 'K' // 26029 #10 - '1cA' // 2602a-26047 - 'K' // 26048 #10 - 'vA' // 26049-2605f - 'B' // 26060 #1 - 'bA' // 26061-26063 - 'K' // 26064 #10 - '1cA' // 26065-26082 - 'K' // 26083 #10 - 'rA' // 26084-26096 - 'K' // 26097 #10 - 'kA' // 26098-260a3 - 'aK' // 260a4-260a5 #10 - '2rA' // 260a6-260ec - 'B' // 260ed #1 - 'sA' // 260ee-26101 - 'K' // 26102 #10 - '1cA' // 26103-26120 - 'K' // 26121 #10 - '2bA' // 26122-26158 - 'cK' // 26159-2615c #10 - '3aA' // 2615d-261ac - 'aK' // 261ad-261ae #10 - 'bA' // 261af-261b1 - 'K' // 261b2 #10 - '1oA' // 261b3-261dc - 'K' // 261dd #10 - '2oA' // 261de-26221 - 'B' // 26222 #1 - '1zA' // 26223-26257 - 'K' // 26258 #10 - 'gA' // 26259-26260 - 'K' // 26261 #10 - 'gA' // 26262-26269 - '36Z' // 2626a #961 - 'K' // 2626b #10 - 'cA' // 2626c-2626f - 'B' // 26270 #1 - 'tA' // 26271-26285 - 'B' // 26286 #1 - '2tA' // 26287-262cf - 'K' // 262d0 #10 - '3uA' // 262d1-26334 - 'K' // 26335 #10 - 'tA' // 26336-2634a - 'K' // 2634b #10 - '36Z' // 2634c #961 - 'cA' // 2634d-26350 - 'K' // 26351 #10 - '4cA' // 26352-263bd - 'K' // 263be #10 - '2aA' // 263bf-263f4 - 'K' // 263f5 #10 - 'aA' // 263f6-263f7 - 'K' // 263f8 #10 - 'hA' // 263f9-26401 - '36Z' // 26402 #961 - 'lA' // 26403-2640f - 'bK' // 26410-26412 #10 - '2bA' // 26413-26449 - 'K' // 2644a #10 - '1cA' // 2644b-26468 - 'K' // 26469 #10 - 'yA' // 2646a-26483 - 'K' // 26484 #10 - 'bA' // 26485-26487 - 'aK' // 26488-26489 #10 - 'bA' // 2648a-2648c - 'K' // 2648d #10 - 'iA' // 2648e-26497 - 'K' // 26498 #10 - '4pA' // 26499-26511 - 'K' // 26512 #10 - '3pA' // 26513-26571 - 'K' // 26572 #10 - '1rA' // 26573-2659f - 'K' // 265a0 #10 - 'kA' // 265a1-265ac - 'K' // 265ad #10 - 'pA' // 265ae-265be - 'K' // 265bf #10 - '3cA' // 265c0-26611 - 'K' // 26612 #10 - 'rA' // 26613-26625 - 'K' // 26626 #10 - '3hA' // 26627-2667d - 'B' // 2667e #1 - '1uA' // 2667f-266ae - 'K' // 266af #10 - 'B' // 266b0 #1 - 'K' // 266b1 #10 - 'bA' // 266b2-266b4 - 'K' // 266b5 #10 - '1iA' // 266b6-266d9 - 'K' // 266da #10 - 'lA' // 266db-266e7 - 'K' // 266e8 #10 - 'rA' // 266e9-266fb - 'K' // 266fc #10 - 'xA' // 266fd-26715 - 'K' // 26716 #10 - 'eA' // 26717-2671c - 'B' // 2671d #1 - '1hA' // 2671e-26740 - 'K' // 26741 #10 - '3hA' // 26742-26798 - 'K' // 26799 #10 - 'xA' // 2679a-267b2 - 'aK' // 267b3-267b4 #10 - 'vA' // 267b5-267cb - '52Q' // 267cc #1368 - '2zA' // 267cd-2681b - 'K' // 2681c #10 - '1nA' // 2681d-26845 - 'K' // 26846 #10 - 'vA' // 26847-2685d - 'K' // 2685e #10 - 'nA' // 2685f-2686d - 'K' // 2686e #10 - 'xA' // 2686f-26887 - 'K' // 26888 #10 - 'A' // 26889 - 'K' // 2688a #10 - 'gA' // 2688b-26892 - 'K' // 26893 #10 - '1xA' // 26894-268c6 - 'K' // 268c7 #10 - 'tA' // 268c8-268dc - 'B' // 268dd #1 - 'kA' // 268de-268e9 - 'B' // 268ea #1 - '1hA' // 268eb-2690d - 'K' // 2690e #10 - 'aA' // 2690f-26910 - 'K' // 26911 #10 - 'sA' // 26912-26925 - 'K' // 26926 #10 - 'qA' // 26927-26938 - 'K' // 26939 #10 - 'vA' // 2693a-26950 - '36Z' // 26951 #961 - '1bA' // 26952-2696e - 'B' // 2696f #1 - '1nA' // 26970-26998 - 'B' // 26999 #1 - 'mA' // 2699a-269a7 - 'K' // 269a8 #10 - 'kA' // 269a9-269b4 - 'K' // 269b5 #10 - '1lA' // 269b6-269dc - 'B' // 269dd #1 - 'sA' // 269de-269f1 - '52Q' // 269f2 #1368 - 'fA' // 269f3-269f9 - 'K' // 269fa #10 - '1hA' // 269fb-26a1d - 'B' // 26a1e #1 - 'mA' // 26a1f-26a2c - 'aK' // 26a2d-26a2e #10 - 'dA' // 26a2f-26a33 - 'K' // 26a34 #10 - 'lA' // 26a35-26a41 - 'K' // 26a42 #10 - 'mA' // 26a43-26a50 - 'aK' // 26a51-26a52 #10 - 'dA' // 26a53-26a57 - 'B' // 26a58 #1 - '1xA' // 26a59-26a8b - 'B' // 26a8c #1 - '1oA' // 26a8d-26ab6 - 'B' // 26ab7 #1 - '2rA' // 26ab8-26afe - 'B' // 26aff #1 - 'dA' // 26b00-26b04 - 'K' // 26b05 #10 - 'cA' // 26b06-26b09 - 'K' // 26b0a #10 - 'gA' // 26b0b-26b12 - 'K' // 26b13 #10 - 'A' // 26b14 - 'K' // 26b15 #10 - 'lA' // 26b16-26b22 - 'K' // 26b23 #10 - 'cA' // 26b24-26b27 - 'K' // 26b28 #10 - '1lA' // 26b29-26b4f - 'cK' // 26b50-26b53 #10 - 'fA' // 26b54-26b5a - 'K' // 26b5b #10 - 'xA' // 26b5c-26b74 - 'K' // 26b75 #10 - 'kA' // 26b76-26b81 - 'K' // 26b82 #10 - 'rA' // 26b83-26b95 - 'aK' // 26b96-26b97 #10 - 'dA' // 26b98-26b9c - 'K' // 26b9d #10 - 'tA' // 26b9e-26bb2 - 'K' // 26bb3 #10 - 'kA' // 26bb4-26bbf - 'K' // 26bc0 #10 - '2aA' // 26bc1-26bf6 - 'K' // 26bf7 #10 - '1nA' // 26bf8-26c20 - 'K' // 26c21 #10 - 'fA' // 26c22-26c28 - 'B' // 26c29 #1 - 'uA' // 26c2a-26c3f - 'aK' // 26c40-26c41 #10 - 'cA' // 26c42-26c45 - 'K' // 26c46 #10 - '1qA' // 26c47-26c72 - 'B' // 26c73 #1 - 'iA' // 26c74-26c7d - 'dK' // 26c7e-26c82 #10 - 'zA' // 26c83-26c9d - 'B' // 26c9e #1 - 'dA' // 26c9f-26ca3 - 'K' // 26ca4 #10 - 'qA' // 26ca5-26cb6 - 'aK' // 26cb7-26cb8 #10 - 'cA' // 26cb9-26cbc - 'K' // 26cbd #10 - 'aA' // 26cbe-26cbf - 'K' // 26cc0 #10 - 'aA' // 26cc1-26cc2 - 'K' // 26cc3 #10 - 'lA' // 26cc4-26cd0 - 'K' // 26cd1 #10 - 'jA' // 26cd2-26cdc - 'B' // 26cdd #1 - '2oA' // 26cde-26d21 - 'hK' // 26d22-26d2a #10 - '1kA' // 26d2b-26d50 - 'K' // 26d51 #10 - '1gA' // 26d52-26d73 - 'K' // 26d74 #10 - '1pA' // 26d75-26d9f - 'gJ' // 26da0-26da7 #9 - 'eA' // 26da8-26dad - 'J' // 26dae #9 - '1rA' // 26daf-26ddb - 'J' // 26ddc #9 - 'lA' // 26ddd-26de9 - 'aJ' // 26dea-26deb #9 - 'cA' // 26dec-26def - 'J' // 26df0 #9 - 'nA' // 26df1-26dff - 'J' // 26e00 #9 - 'cA' // 26e01-26e04 - 'J' // 26e05 #9 - 'A' // 26e06 - 'J' // 26e07 #9 - 'iA' // 26e08-26e11 - 'J' // 26e12 #9 - '1rA' // 26e13-26e3f - 'B' // 26e40 #1 - 'A' // 26e41 - 'cJ' // 26e42-26e45 #9 - '1dA' // 26e46-26e64 - 'B' // 26e65 #1 - 'gA' // 26e66-26e6d - 'J' // 26e6e #9 - 'bA' // 26e6f-26e71 - 'J' // 26e72 #9 - 'cA' // 26e73-26e76 - 'J' // 26e77 #9 - 'kA' // 26e78-26e83 - 'J' // 26e84 #9 - 'bA' // 26e85-26e87 - 'J' // 26e88 #9 - 'aA' // 26e89-26e8a - 'J' // 26e8b #9 - 'lA' // 26e8c-26e98 - 'J' // 26e99 #9 - '2aA' // 26e9a-26ecf - 'gJ' // 26ed0-26ed7 #9 - '2yA' // 26ed8-26f25 - 'J' // 26f26 #9 - '2wA' // 26f27-26f72 - 'aJ' // 26f73-26f74 #9 - '1dA' // 26f75-26f93 - 'B' // 26f94 #1 - 'iA' // 26f95-26f9e - 'J' // 26f9f #9 - 'A' // 26fa0 - 'J' // 26fa1 #9 - '1aA' // 26fa2-26fbd - 'J' // 26fbe #9 - '1dA' // 26fbf-26fdd - 'aJ' // 26fde-26fdf #9 - 'uA' // 26fe0-26ff5 - 'bB' // 26ff6-26ff8 #1 - 'tA' // 26ff9-2700d - 'J' // 2700e #9 - '2gA' // 2700f-2704a - 'J' // 2704b #9 - 'eA' // 2704c-27051 - 'aJ' // 27052-27053 #9 - '1yA' // 27054-27087 - 'J' // 27088 #9 - '1iA' // 27089-270ac - 'bJ' // 270ad-270af #9 - '1bA' // 270b0-270cc - 'J' // 270cd #9 - 'cA' // 270ce-270d1 - 'J' // 270d2 #9 - '1bA' // 270d3-270ef - 'J' // 270f0 #9 - 'bA' // 270f1-270f3 - 'B' // 270f4 #1 - 'bA' // 270f5-270f7 - 'J' // 270f8 #9 - 'oA' // 270f9-27108 - 'J' // 27109 #9 - 'aA' // 2710a-2710b - 'J' // 2710c #9 - '36Y' // 2710d #960 - 'wA' // 2710e-27125 - 'aJ' // 27126-27127 #9 - 'pA' // 27128-27138 - 'B' // 27139 #1 - '1oA' // 2713a-27163 - 'aJ' // 27164-27165 #9 - 'nA' // 27166-27174 - 'J' // 27175 #9 - '3hA' // 27176-271cc - 'J' // 271cd #9 - '2xA' // 271ce-2721a - 'J' // 2721b #9 - '2vA' // 2721c-27266 - 'J' // 27267 #9 - 'wA' // 27268-2727f - 'J' // 27280 #9 - 'cA' // 27281-27284 - 'J' // 27285 #9 - 'dA' // 27286-2728a - 'J' // 2728b #9 - '1kA' // 2728c-272b1 - 'J' // 272b2 #9 - 'bA' // 272b3-272b5 - 'J' // 272b6 #9 - '1tA' // 272b7-272e5 - 'J' // 272e6 #9 - '4bA' // 272e7-27351 - 'J' // 27352 #9 - '2rA' // 27353-27399 - 'J' // 2739a #9 - '2jA' // 2739b-273d9 - 'aB' // 273da-273db #1 - '1gA' // 273dc-273fd - 'B' // 273fe #1 - 'J' // 273ff #9 - 'oA' // 27400-2740f - 'B' // 27410 #1 - 'pA' // 27411-27421 - 'J' // 27422 #9 - '1kA' // 27423-27448 - 'B' // 27449 #1 - 'eA' // 2744a-2744f - 'J' // 27450 #9 - '1xA' // 27451-27483 - 'J' // 27484 #9 - 'A' // 27485 - 'J' // 27486 #9 - '9bA' // 27487-27573 - 'J' // 27574 #9 - '1sA' // 27575-275a2 - 'J' // 275a3 #9 - '2gA' // 275a4-275df - 'J' // 275e0 #9 - 'bA' // 275e1-275e3 - 'J' // 275e4 #9 - 'wA' // 275e5-275fc - 'aJ' // 275fd-275fe #9 - 'gA' // 275ff-27606 - 'J' // 27607 #9 - 'cA' // 27608-2760b - 'J' // 2760c #9 - 'fA' // 2760d-27613 - 'aB' // 27614-27615 #1 - 'zA' // 27616-27630 - 'B' // 27631 #1 - 'J' // 27632 #9 - 'eA' // 27633-27638 - 'J' // 27639 #9 - 'zA' // 2763a-27654 - 'aJ' // 27655-27656 #9 - '80J' // 27657 #2089 - '1qA' // 27658-27683 - 'B' // 27684 #1 - 'mA' // 27685-27692 - 'B' // 27693 #1 - 'J' // 27694 #9 - '4pA' // 27695-2770d - 'B' // 2770e #1 - 'J' // 2770f #9 - 'rA' // 27710-27722 - 'B' // 27723 #1 - 'pA' // 27724-27734 - 'aJ' // 27735-27736 #9 - 'iA' // 27737-27740 - 'J' // 27741 #9 - 'oA' // 27742-27751 - 'B' // 27752 #1 - 'jA' // 27753-2775d - 'J' // 2775e #9 - '1jA' // 2775f-27783 - 'aJ' // 27784-27785 #9 - '2qA' // 27786-277cb - 'J' // 277cc #9 - '5hA' // 277cd-27857 - 'J' // 27858 #9 - 'vA' // 27859-2786f - 'J' // 27870 #9 - '1qA' // 27871-2789c - 'J' // 2789d #9 - 'sA' // 2789e-278b1 - '36Y' // 278b2 #960 - 'tA' // 278b3-278c7 - 'J' // 278c8 #9 - '3lA' // 278c9-27923 - 'J' // 27924 #9 - '2mA' // 27925-27966 - 'J' // 27967 #9 - 'qA' // 27968-27979 - 'J' // 2797a #9 - 'iA' // 2797b-27984 - 'B' // 27985 #1 - 'yA' // 27986-2799f - 'J' // 279a0 #9 - 'rA' // 279a1-279b3 - 'B' // 279b4 #1 - '1mA' // 279b5-279dc - 'J' // 279dd #9 - '1dA' // 279de-279fc - 'J' // 279fd #9 - 'kA' // 279fe-27a09 - 'J' // 27a0a #9 - 'bA' // 27a0b-27a0d - 'J' // 27a0e #9 - '1tA' // 27a0f-27a3d - 'J' // 27a3e #9 - 'sA' // 27a3f-27a52 - 'J' // 27a53 #9 - 'dA' // 27a54-27a58 - 'J' // 27a59 #9 - '1dA' // 27a5a-27a78 - 'J' // 27a79 #9 - 'iA' // 27a7a-27a83 - '36Y' // 27a84 #960 - '2cA' // 27a85-27abc - 'aJ' // 27abd-27abe #9 - '1zA' // 27abf-27af3 - 'J' // 27af4 #9 - 'pA' // 27af5-27b05 - 'J' // 27b06 #9 - 'cA' // 27b07-27b0a - 'J' // 27b0b #9 - 'kA' // 27b0c-27b17 - 'J' // 27b18 #9 - '1dA' // 27b19-27b37 - 'bJ' // 27b38-27b3a #9 - 'lA' // 27b3b-27b47 - 'J' // 27b48 #9 - '1aA' // 27b49-27b64 - 'J' // 27b65 #9 - '2xA' // 27b66-27bb2 - 'B' // 27bb3 #1 - 'iA' // 27bb4-27bbd - 'B' // 27bbe #1 - 'gA' // 27bbf-27bc6 - 'B' // 27bc7 #1 - '1lA' // 27bc8-27bee - 'J' // 27bef #9 - 'cA' // 27bf0-27bf3 - 'J' // 27bf4 #9 - '1bA' // 27bf5-27c11 - 'J' // 27c12 #9 - '1nA' // 27c13-27c3b - 'B' // 27c3c #1 - '1tA' // 27c3d-27c6b - 'J' // 27c6c #9 - '2oA' // 27c6d-27cb0 - 'J' // 27cb1 #9 - 'eA' // 27cb2-27cb7 - 'B' // 27cb8 #1 - 'kA' // 27cb9-27cc4 - 'J' // 27cc5 #9 - '3zA' // 27cc6-27d2e - 'J' // 27d2f #9 - '1hA' // 27d30-27d52 - 'aJ' // 27d53-27d54 #9 - 'pA' // 27d55-27d65 - 'J' // 27d66 #9 - 'kA' // 27d67-27d72 - '36Y' // 27d73 #960 - 'oA' // 27d74-27d83 - 'J' // 27d84 #9 - 'iA' // 27d85-27d8e - 'J' // 27d8f #9 - 'gA' // 27d90-27d97 - 'J' // 27d98 #9 - 'fA' // 27d99-27d9f - 'B' // 27da0 #1 - '1aA' // 27da1-27dbc - 'J' // 27dbd #9 - '1cA' // 27dbe-27ddb - 'J' // 27ddc #9 - '1xA' // 27ddd-27e0f - 'B' // 27e10 #1 - '2gA' // 27e11-27e4c - 'J' // 27e4d #9 - 'A' // 27e4e - 'J' // 27e4f #9 - '3pA' // 27e50-27eae - 'B' // 27eaf #1 - '4uA' // 27eb0-27f2d - 'J' // 27f2e #9 - '5eA' // 27f2f-27fb6 - 'B' // 27fb7 #1 - '2lA' // 27fb8-27ff8 - 'J' // 27ff9 #9 - 'gA' // 27ffa-28001 - 'J' // 28002 #9 - 'eA' // 28003-28008 - 'J' // 28009 #9 - 'sA' // 2800a-2801d - 'J' // 2801e #9 - 'cA' // 2801f-28022 - 'aJ' // 28023-28024 #9 - '1hA' // 28025-28047 - 'J' // 28048 #9 - '2eA' // 28049-28082 - 'J' // 28083 #9 - 'eA' // 28084-28089 - 'B' // 2808a #1 - 'dA' // 2808b-2808f - 'J' // 28090 #9 - '1oA' // 28091-280ba - 'B' // 280bb #1 - 'A' // 280bc - 'aJ' // 280bd-280be #9 - '1nA' // 280bf-280e7 - 'aJ' // 280e8-280e9 #9 - 'iA' // 280ea-280f3 - 'J' // 280f4 #9 - '2dA' // 280f5-2812d - 'J' // 2812e #9 - '1eA' // 2812f-2814e - 'J' // 2814f #9 - 'lA' // 28150-2815c - 'J' // 2815d #9 - 'pA' // 2815e-2816e - 'J' // 2816f #9 - 'xA' // 28170-28188 - 'N' // 28189 #13 - '1jA' // 2818a-281ae - 'N' // 281af #13 - 'kA' // 281b0-281bb - 'N' // 281bc #13 - '2uA' // 281bd-28206 - 'N' // 28207 #13 - 'oA' // 28208-28217 - 'N' // 28218 #13 - 'A' // 28219 - 'N' // 2821a #13 - '2fA' // 2821b-28255 - 'N' // 28256 #13 - '1eA' // 28257-28276 - 'B' // 28277 #1 - 'cA' // 28278-2827b - 'N' // 2827c #13 - 'dA' // 2827d-28281 - 'B' // 28282 #1 - 'wA' // 28283-2829a - 'N' // 2829b #13 - '1vA' // 2829c-282cc - 'N' // 282cd #13 - 'sA' // 282ce-282e1 - '80I' // 282e2 #2088 - 'oA' // 282e3-282f2 - 'B' // 282f3 #1 - 'qA' // 282f4-28305 - 'N' // 28306 #13 - 'pA' // 28307-28317 - 'N' // 28318 #13 - 'uA' // 28319-2832e - 'N' // 2832f #13 - 'iA' // 28330-28339 - 'N' // 2833a #13 - '1oA' // 2833b-28364 - 'N' // 28365 #13 - 'fA' // 28366-2836c - 'N' // 2836d #13 - 'nA' // 2836e-2837c - 'N' // 2837d #13 - 'kA' // 2837e-28389 - 'N' // 2838a #13 - '2mA' // 2838b-283cc - 'B' // 283cd #1 - '2iA' // 283ce-2840b - 'B' // 2840c #1 - 'dA' // 2840d-28411 - 'N' // 28412 #13 - '2mA' // 28413-28454 - 'B' // 28455 #1 - 'qA' // 28456-28467 - 'N' // 28468 #13 - 'bA' // 28469-2846b - 'N' // 2846c #13 - 'eA' // 2846d-28472 - 'N' // 28473 #13 - 'mA' // 28474-28481 - 'N' // 28482 #13 - '3jA' // 28483-284db - 'B' // 284dc #1 - '1iA' // 284dd-28500 - 'N' // 28501 #13 - '2eA' // 28502-2853b - 'aN' // 2853c-2853d #13 - '1rA' // 2853e-2856a - 'B' // 2856b #1 - 'N' // 2856c #13 - '3lA' // 2856d-285c7 - 'aB' // 285c8-285c9 #1 - '1cA' // 285ca-285e7 - 'N' // 285e8 #13 - 'jA' // 285e9-285f3 - 'N' // 285f4 #13 - 'jA' // 285f5-285ff - 'N' // 28600 #13 - 'iA' // 28601-2860a - 'N' // 2860b #13 - 'xA' // 2860c-28624 - 'N' // 28625 #13 - 'tA' // 28626-2863a - 'N' // 2863b #13 - '4eA' // 2863c-286a9 - 'aN' // 286aa-286ab #13 - 'eA' // 286ac-286b1 - 'N' // 286b2 #13 - 'hA' // 286b3-286bb - 'N' // 286bc #13 - 'yA' // 286bd-286d6 - 'B' // 286d7 #1 - 'N' // 286d8 #13 - 'lA' // 286d9-286e5 - 'N' // 286e6 #13 - 'rA' // 286e7-286f9 - 'B' // 286fa #1 - 'sA' // 286fb-2870e - 'N' // 2870f #13 - 'bA' // 28710-28712 - 'N' // 28713 #13 - '9eA' // 28714-28803 - 'N' // 28804 #13 - '1kA' // 28805-2882a - 'N' // 2882b #13 - '8pA' // 2882c-2890c - 'N' // 2890d #13 - '1jA' // 2890e-28932 - 'N' // 28933 #13 - 'qA' // 28934-28945 - 'B' // 28946 #1 - 'A' // 28947 - 'N' // 28948 #13 - '36X' // 28949 #959 - 'kA' // 2894a-28955 - 'N' // 28956 #13 - 'lA' // 28957-28963 - 'N' // 28964 #13 - 'bA' // 28965-28967 - 'N' // 28968 #13 - 'aA' // 28969-2896a - 'B' // 2896b #1 - 'aN' // 2896c-2896d #13 - 'oA' // 2896e-2897d - 'N' // 2897e #13 - 'gA' // 2897f-28986 - 'aB' // 28987-28988 #1 - 'N' // 28989 #13 - '1cA' // 2898a-289a7 - 'N' // 289a8 #13 - 'A' // 289a9 - 'aN' // 289aa-289ab #13 - 'kA' // 289ac-289b7 - 'N' // 289b8 #13 - 'A' // 289b9 - 'aB' // 289ba-289bb #1 - 'N' // 289bc #13 - 'bA' // 289bd-289bf - 'N' // 289c0 #13 - 'zA' // 289c1-289db - 'N' // 289dc #13 - 'A' // 289dd - 'N' // 289de #13 - 'aA' // 289df-289e0 - 'N' // 289e1 #13 - 'A' // 289e2 - 'aN' // 289e3-289e4 #13 - 'aA' // 289e5-289e6 - 'aN' // 289e7-289e8 #13 - 'oA' // 289e9-289f8 - 'cN' // 289f9-289fc #13 - 'qA' // 289fd-28a0e - 'N' // 28a0f #13 - 'eA' // 28a10-28a15 - 'N' // 28a16 #13 - 'fA' // 28a17-28a1d - 'B' // 28a1e #1 - 'eA' // 28a1f-28a24 - 'N' // 28a25 #13 - 'bA' // 28a26-28a28 - '36X' // 28a29 #959 - 'gA' // 28a2a-28a31 - 'N' // 28a32 #13 - 'bA' // 28a33-28a35 - 'N' // 28a36 #13 - 'kA' // 28a37-28a42 - 'B' // 28a43 #1 - 'gN' // 28a44-28a4b #13 - 'lA' // 28a4c-28a58 - 'aN' // 28a59-28a5a #13 - 'uA' // 28a5b-28a70 - 'B' // 28a71 #1 - 'nA' // 28a72-28a80 - 'bN' // 28a81-28a83 #13 - 'tA' // 28a84-28a98 - 'B' // 28a99 #1 - 'bN' // 28a9a-28a9c #13 - '1hA' // 28a9d-28abf - 'N' // 28ac0 #13 - 'dA' // 28ac1-28ac5 - 'N' // 28ac6 #13 - 'cA' // 28ac7-28aca - 'aN' // 28acb-28acc #13 - 'B' // 28acd #1 - 'N' // 28ace #13 - 'mA' // 28acf-28adc - 'B' // 28add #1 - 'eN' // 28ade-28ae3 #13 - 'B' // 28ae4 #1 - 'N' // 28ae5 #13 - 'cA' // 28ae6-28ae9 - 'N' // 28aea #13 - 'pA' // 28aeb-28afb - 'N' // 28afc #13 - 'nA' // 28afd-28b0b - 'N' // 28b0c #13 - 'eA' // 28b0d-28b12 - 'N' // 28b13 #13 - 'lA' // 28b14-28b20 - 'aN' // 28b21-28b22 #13 - 'gA' // 28b23-28b2a - 'bN' // 28b2b-28b2d #13 - 'A' // 28b2e - 'N' // 28b2f #13 - 'uA' // 28b30-28b45 - 'N' // 28b46 #13 - 'dA' // 28b47-28b4b - 'N' // 28b4c #13 - 'A' // 28b4d - 'N' // 28b4e #13 - 'A' // 28b4f - 'N' // 28b50 #13 - 'qA' // 28b51-28b62 - 'cN' // 28b63-28b66 #13 - 'dA' // 28b67-28b6b - 'N' // 28b6c #13 - '1gA' // 28b6d-28b8e - 'N' // 28b8f #13 - 'hA' // 28b90-28b98 - 'N' // 28b99 #13 - 'aA' // 28b9a-28b9b - 'aN' // 28b9c-28b9d #13 - 'zA' // 28b9e-28bb8 - 'N' // 28bb9 #13 - 'fA' // 28bba-28bc0 - 'B' // 28bc1 #1 - 'N' // 28bc2 #13 - 'aA' // 28bc3-28bc4 - 'N' // 28bc5 #13 - 'mA' // 28bc6-28bd3 - 'N' // 28bd4 #13 - 'aA' // 28bd5-28bd6 - 'N' // 28bd7 #13 - 'A' // 28bd8 - 'aN' // 28bd9-28bda #13 - 'kA' // 28bdb-28be6 - 'eN' // 28be7-28bec #13 - 'aA' // 28bed-28bee - 'B' // 28bef #1 - 'dA' // 28bf0-28bf4 - 'N' // 28bf5 #13 - 'hA' // 28bf6-28bfe - 'N' // 28bff #13 - 'bA' // 28c00-28c02 - 'N' // 28c03 #13 - 'dA' // 28c04-28c08 - 'N' // 28c09 #13 - 'qA' // 28c0a-28c1b - 'aN' // 28c1c-28c1d #13 - 'dA' // 28c1e-28c22 - 'N' // 28c23 #13 - 'aA' // 28c24-28c25 - 'N' // 28c26 #13 - 'cA' // 28c27-28c2a - 'N' // 28c2b #13 - 'cA' // 28c2c-28c2f - 'N' // 28c30 #13 - 'gA' // 28c31-28c38 - 'N' // 28c39 #13 - 'A' // 28c3a - 'N' // 28c3b #13 - '5kA' // 28c3c-28cc9 - 'N' // 28cca #13 - 'aA' // 28ccb-28ccc - 'N' // 28ccd #13 - 'cA' // 28cce-28cd1 - 'N' // 28cd2 #13 - 'iA' // 28cd3-28cdc - 'B' // 28cdd #1 - '1wA' // 28cde-28d0f - 'B' // 28d10 #1 - '1hA' // 28d11-28d33 - 'N' // 28d34 #13 - '2gA' // 28d35-28d70 - 'B' // 28d71 #1 - '1lA' // 28d72-28d98 - 'N' // 28d99 #13 - '1dA' // 28d9a-28db8 - 'N' // 28db9 #13 - '2lA' // 28dba-28dfa - 'B' // 28dfb #1 - 'rA' // 28dfc-28e0e - '36X' // 28e0f #959 - 'fA' // 28e10-28e16 - 'B' // 28e17 #1 - 'fA' // 28e18-28e1e - 'B' // 28e1f #1 - 'uA' // 28e20-28e35 - '36X' // 28e36 #959 - 'aA' // 28e37-28e38 - 'N' // 28e39 #13 - '1pA' // 28e3a-28e64 - 'aN' // 28e65-28e66 #13 - '1gA' // 28e67-28e88 - 'B' // 28e89 #1 - 'lA' // 28e8a-28e96 - 'N' // 28e97 #13 - 'sA' // 28e98-28eab - 'N' // 28eac #13 - 'dA' // 28ead-28eb1 - 'aH' // 28eb2-28eb3 #7 - '1jA' // 28eb4-28ed8 - 'H' // 28ed9 #7 - 'lA' // 28eda-28ee6 - 'H' // 28ee7 #7 - 'bA' // 28ee8-28eea - 'B' // 28eeb #1 - 'iA' // 28eec-28ef5 - 'B' // 28ef6 #1 - '2fA' // 28ef7-28f31 - 'B' // 28f32 #1 - '5oA' // 28f33-28fc4 - 'H' // 28fc5 #7 - '1wA' // 28fc6-28ff7 - 'B' // 28ff8 #1 - '4wA' // 28ff9-29078 - 'H' // 29079 #7 - 'mA' // 2907a-29087 - 'H' // 29088 #7 - 'aA' // 29089-2908a - 'H' // 2908b #7 - 'fA' // 2908c-29092 - 'H' // 29093 #7 - 'zA' // 29094-290ae - 'bH' // 290af-290b1 #7 - 'mA' // 290b2-290bf - 'H' // 290c0 #7 - '1hA' // 290c1-290e3 - 'aH' // 290e4-290e5 #7 - 'eA' // 290e6-290eb - 'aH' // 290ec-290ed #7 - '1dA' // 290ee-2910c - 'H' // 2910d #7 - 'aA' // 2910e-2910f - 'H' // 29110 #7 - '1pA' // 29111-2913b - 'H' // 2913c #7 - 'oA' // 2913d-2914c - 'H' // 2914d #7 - 'lA' // 2914e-2915a - 'H' // 2915b #7 - 'aA' // 2915c-2915d - 'H' // 2915e #7 - 'pA' // 2915f-2916f - 'H' // 29170 #7 - '1pA' // 29171-2919b - 'H' // 2919c #7 - 'jA' // 2919d-291a7 - 'H' // 291a8 #7 - '1qA' // 291a9-291d4 - 'H' // 291d5 #7 - 'tA' // 291d6-291ea - 'H' // 291eb #7 - '6wA' // 291ec-2929f - 'B' // 292a0 #1 - 'oA' // 292a1-292b0 - 'B' // 292b1 #1 - '13xA' // 292b2-2941c - 'H' // 2941d #7 - 'aA' // 2941e-2941f - 'H' // 29420 #7 - 'qA' // 29421-29432 - 'H' // 29433 #7 - 'jA' // 29434-2943e - 'H' // 2943f #7 - 'gA' // 29440-29447 - 'H' // 29448 #7 - '2rA' // 29449-2948f - 'B' // 29490 #1 - '2jA' // 29491-294cf - 'H' // 294d0 #7 - 'gA' // 294d1-294d8 - 'aH' // 294d9-294da #7 - 'iA' // 294db-294e4 - 'H' // 294e5 #7 - 'A' // 294e6 - 'H' // 294e7 #7 - '6yA' // 294e8-2959d - 'H' // 2959e #7 - 'pA' // 2959f-295af - 'H' // 295b0 #7 - 'fA' // 295b1-295b7 - 'H' // 295b8 #7 - 'uA' // 295b9-295ce - 'B' // 295cf #1 - 'fA' // 295d0-295d6 - 'H' // 295d7 #7 - 'pA' // 295d8-295e8 - 'H' // 295e9 #7 - 'iA' // 295ea-295f3 - 'H' // 295f4 #7 - '5gA' // 295f5-2967e - 'B' // 2967f #1 - '4gA' // 29680-296ef - 'B' // 296f0 #1 - '1mA' // 296f1-29718 - 'B' // 29719 #1 - 'eA' // 2971a-2971f - 'H' // 29720 #7 - 'pA' // 29721-29731 - 'H' // 29732 #7 - '1bA' // 29733-2974f - 'B' // 29750 #1 - '4zA' // 29751-297d3 - 'H' // 297d4 #7 - '2fA' // 297d5-2980f - '42Y' // 29810 #1116 - '2qA' // 29811-29856 - 'H' // 29857 #7 - '2wA' // 29858-298a3 - 'H' // 298a4 #7 - '1fA' // 298a5-298c5 - 'B' // 298c6 #1 - 'iA' // 298c7-298d0 - 'H' // 298d1 #7 - 'wA' // 298d2-298e9 - 'H' // 298ea #7 - 'eA' // 298eb-298f0 - 'H' // 298f1 #7 - 'gA' // 298f2-298f9 - 'H' // 298fa #7 - 'gA' // 298fb-29902 - 'H' // 29903 #7 - 'A' // 29904 - 'H' // 29905 #7 - '1nA' // 29906-2992e - 'H' // 2992f #7 - 'tA' // 29930-29944 - 'H' // 29945 #7 - 'A' // 29946 - 'bH' // 29947-29949 #7 - 'rA' // 2994a-2995c - 'H' // 2995d #7 - 'kA' // 2995e-29969 - 'H' // 2996a #7 - '1wA' // 2996b-2999c - 'H' // 2999d #7 - '1jA' // 2999e-299c2 - 'H' // 299c3 #7 - 'dA' // 299c4-299c8 - 'H' // 299c9 #7 - '3oA' // 299ca-29a27 - 'H' // 29a28 #7 - '1iA' // 29a29-29a4c - 'H' // 29a4d #7 - '1iA' // 29a4e-29a71 - 'B' // 29a72 #1 - '5oA' // 29a73-29b04 - 'H' // 29b05 #7 - 'gA' // 29b06-29b0d - 'H' // 29b0e #7 - '7oA' // 29b0f-29bd4 - 'H' // 29bd5 #7 - '5zA' // 29bd6-29c72 - 'H' // 29c73 #7 - '2dA' // 29c74-29cac - 'H' // 29cad #7 - '5mA' // 29cae-29d3d - 'H' // 29d3e #7 - 'kA' // 29d3f-29d4a - 'B' // 29d4b #1 - 'mA' // 29d4c-29d59 - '80H' // 29d5a #2087 - '1fA' // 29d5b-29d7b - 'H' // 29d7c #7 - 'zA' // 29d7d-29d97 - 'H' // 29d98 #7 - 'aA' // 29d99-29d9a - 'H' // 29d9b #7 - '2jA' // 29d9c-29dda - 'B' // 29ddb #1 - 'yA' // 29ddc-29df5 - 'H' // 29df6 #7 - 'nA' // 29df7-29e05 - 'H' // 29e06 #7 - 'mA' // 29e07-29e14 - 'B' // 29e15 #1 - 'vA' // 29e16-29e2c - 'H' // 29e2d #7 - 'nA' // 29e2e-29e3c - 'B' // 29e3d #1 - 'jA' // 29e3e-29e48 - 'B' // 29e49 #1 - '1cA' // 29e4a-29e67 - 'H' // 29e68 #7 - '1fA' // 29e69-29e89 - 'B' // 29e8a #1 - '1fA' // 29e8b-29eab - 'H' // 29eac #7 - 'bA' // 29ead-29eaf - 'H' // 29eb0 #7 - 'qA' // 29eb1-29ec2 - 'H' // 29ec3 #7 - 'B' // 29ec4 #1 - 'uA' // 29ec5-29eda - 'B' // 29edb #1 - 'lA' // 29edc-29ee8 - 'B' // 29ee9 #1 - 'mA' // 29eea-29ef7 - 'H' // 29ef8 #7 - '1oA' // 29ef9-29f22 - 'H' // 29f23 #7 - 'kA' // 29f24-29f2f - 'H' // 29f30 #7 - '5cA' // 29f31-29fb6 - 'H' // 29fb7 #7 - 'uA' // 29fb8-29fcd - 'B' // 29fce #1 - 'gA' // 29fcf-29fd6 - 'B' // 29fd7 #1 - 'eA' // 29fd8-29fdd - 'H' // 29fde #7 - '1zA' // 29fdf-2a013 - 'H' // 2a014 #7 - 'dA' // 2a015-2a019 - 'B' // 2a01a #1 - 'sA' // 2a01b-2a02e - 'B' // 2a02f #1 - '3cA' // 2a030-2a081 - 'B' // 2a082 #1 - 'cA' // 2a083-2a086 - 'H' // 2a087 #7 - '1vA' // 2a088-2a0b8 - 'H' // 2a0b9 #7 - '1lA' // 2a0ba-2a0e0 - 'H' // 2a0e1 #7 - 'jA' // 2a0e2-2a0ec - 'H' // 2a0ed #7 - 'dA' // 2a0ee-2a0f2 - 'H' // 2a0f3 #7 - 'cA' // 2a0f4-2a0f7 - 'H' // 2a0f8 #7 - 'B' // 2a0f9 #1 - 'cA' // 2a0fa-2a0fd - 'H' // 2a0fe #7 - 'gA' // 2a0ff-2a106 - 'H' // 2a107 #7 - 'zA' // 2a108-2a122 - 'H' // 2a123 #7 - 'nA' // 2a124-2a132 - 'aH' // 2a133-2a134 #7 - 'zA' // 2a135-2a14f - 'H' // 2a150 #7 - '2jA' // 2a151-2a18f - 'B' // 2a190 #1 - 'A' // 2a191 - 'aH' // 2a192-2a193 #7 - 'vA' // 2a194-2a1aa - 'H' // 2a1ab #7 - 'gA' // 2a1ac-2a1b3 - 'aH' // 2a1b4-2a1b5 #7 - '1nA' // 2a1b6-2a1de - 'H' // 2a1df #7 - 'tA' // 2a1e0-2a1f4 - 'H' // 2a1f5 #7 - '1oA' // 2a1f6-2a21f - 'H' // 2a220 #7 - 'qA' // 2a221-2a232 - 'H' // 2a233 #7 - '3pA' // 2a234-2a292 - 'H' // 2a293 #7 - 'jA' // 2a294-2a29e - 'H' // 2a29f #7 - 'qA' // 2a2a0-2a2b1 - '42Y' // 2a2b2 #1116 - 'A' // 2a2b3 - 'H' // 2a2b4 #7 - 'A' // 2a2b5 - 'H' // 2a2b6 #7 - 'bA' // 2a2b7-2a2b9 - 'H' // 2a2ba #7 - 'aA' // 2a2bb-2a2bc - 'H' // 2a2bd #7 - '1fA' // 2a2be-2a2de - 'H' // 2a2df #7 - '1dA' // 2a2e0-2a2fe - 'H' // 2a2ff #7 - '3bA' // 2a300-2a350 - 'H' // 2a351 #7 - '2eA' // 2a352-2a38b - 'B' // 2a38c #1 - '1aA' // 2a38d-2a3a8 - 'H' // 2a3a9 #7 - '2nA' // 2a3aa-2a3ec - 'H' // 2a3ed #7 - '2qA' // 2a3ee-2a433 - 'H' // 2a434 #7 - 'aA' // 2a435-2a436 - 'B' // 2a437 #1 - '1hA' // 2a438-2a45a - 'H' // 2a45b #7 - '13wA' // 2a45c-2a5c5 - 'H' // 2a5c6 #7 - 'cA' // 2a5c7-2a5ca - 'H' // 2a5cb #7 - '1jA' // 2a5cc-2a5f0 - 'B' // 2a5f1 #1 - 'nA' // 2a5f2-2a600 - 'H' // 2a601 #7 - 'B' // 2a602 #1 - 'vA' // 2a603-2a619 - 'B' // 2a61a #1 - 'vA' // 2a61b-2a631 - 'H' // 2a632 #7 - 'vA' // 2a633-2a649 - 'H' // 2a64a #7 - 'oA' // 2a64b-2a65a - 'H' // 2a65b #7 - '2xA' // 2a65c-2a6a8 - 'H' // 2a6a9 #7 - 'gA' // 2a6aa-2a6b1 - 'B' // 2a6b2 #1 - '31lA' // 2a6b3-2a9e5 - 'B' // 2a9e6 #1 - '40gA' // 2a9e7-2adfe - 'H' // 2adff #7 - '91gA' // 2ae00-2b745 - 'B' // 2b746 #1 - 'iA' // 2b747-2b750 - 'B' // 2b751 #1 - 'A' // 2b752 - 'B' // 2b753 #1 - 'eA' // 2b754-2b759 - 'B' // 2b75a #1 - 'A' // 2b75b - 'B' // 2b75c #1 - 'gA' // 2b75d-2b764 - 'B' // 2b765 #1 - 'oA' // 2b766-2b775 - 'aB' // 2b776-2b777 #1 - 'cA' // 2b778-2b77b - 'B' // 2b77c #1 - 'dA' // 2b77d-2b781 - 'B' // 2b782 #1 - 'eA' // 2b783-2b788 - 'B' // 2b789 #1 - 'A' // 2b78a - 'B' // 2b78b #1 - 'aA' // 2b78c-2b78d - 'B' // 2b78e #1 - 'dA' // 2b78f-2b793 - 'B' // 2b794 #1 - 'vA' // 2b795-2b7ab - 'B' // 2b7ac #1 - 'aA' // 2b7ad-2b7ae - 'B' // 2b7af #1 - 'lA' // 2b7b0-2b7bc - 'B' // 2b7bd #1 - 'jA' // 2b7be-2b7c8 - 'B' // 2b7c9 #1 - 'dA' // 2b7ca-2b7ce - 'B' // 2b7cf #1 - 'aA' // 2b7d0-2b7d1 - 'B' // 2b7d2 #1 - 'dA' // 2b7d3-2b7d7 - 'B' // 2b7d8 #1 - 'vA' // 2b7d9-2b7ef - 'B' // 2b7f0 #1 - '1aA' // 2b7f1-2b80c - 'B' // 2b80d #1 - 'hA' // 2b80e-2b816 - 'B' // 2b817 #1 - 'aA' // 2b818-2b819 - 'B' // 2b81a #1 - '287bA' // 2b81b-2d543 - 'B' // 2d544 #1 - '129xA' // 2d545-2e277 - 'B' // 2e278 #1 - '28wA' // 2e279-2e568 - 'B' // 2e569 #1 - '14sA' // 2e56a-2e6e9 - 'B' // 2e6ea #1 - '168hA' // 2e6eb-2f803 - 'B' // 2f804 #1 - 'iA' // 2f805-2f80e - 'B' // 2f80f #1 - 'dA' // 2f810-2f814 - 'B' // 2f815 #1 - 'aA' // 2f816-2f817 - 'B' // 2f818 #1 - 'A' // 2f819 - 'B' // 2f81a #1 - 'fA' // 2f81b-2f821 - 'B' // 2f822 #1 - 'aA' // 2f823-2f824 - 'H' // 2f825 #7 - 'aA' // 2f826-2f827 - 'B' // 2f828 #1 - 'bA' // 2f829-2f82b - 'B' // 2f82c #1 - 'eA' // 2f82d-2f832 - 'B' // 2f833 #1 - 'fA' // 2f834-2f83a - 'H' // 2f83b #7 - 'bA' // 2f83c-2f83e - 'B' // 2f83f #1 - 'H' // 2f840 #7 - 'dA' // 2f841-2f845 - 'B' // 2f846 #1 - 'jA' // 2f847-2f851 - 'B' // 2f852 #1 - 'nA' // 2f853-2f861 - 'B' // 2f862 #1 - 'iA' // 2f863-2f86c - 'B' // 2f86d #1 - 'dA' // 2f86e-2f872 - 'B' // 2f873 #1 - 'bA' // 2f874-2f876 - 'B' // 2f877 #1 - 'H' // 2f878 #7 - 'jA' // 2f879-2f883 - 'B' // 2f884 #1 - 'nA' // 2f885-2f893 - 'H' // 2f894 #7 - 'cA' // 2f895-2f898 - 'aB' // 2f899-2f89a #1 - 'jA' // 2f89b-2f8a5 - '42Y' // 2f8a6 #1116 - 'dA' // 2f8a7-2f8ab - 'B' // 2f8ac #1 - 'dA' // 2f8ad-2f8b1 - 'B' // 2f8b2 #1 - 'bA' // 2f8b3-2f8b5 - 'B' // 2f8b6 #1 - 'uA' // 2f8b7-2f8cc - 'H' // 2f8cd #7 - 'dA' // 2f8ce-2f8d2 - 'B' // 2f8d3 #1 - 'fA' // 2f8d4-2f8da - 'aB' // 2f8db-2f8dc #1 - 'cA' // 2f8dd-2f8e0 - 'B' // 2f8e1 #1 - 'bA' // 2f8e2-2f8e4 - 'B' // 2f8e5 #1 - 'cA' // 2f8e6-2f8e9 - 'B' // 2f8ea #1 - 'aA' // 2f8eb-2f8ec - 'B' // 2f8ed #1 - 'mA' // 2f8ee-2f8fb - 'B' // 2f8fc #1 - 'eA' // 2f8fd-2f902 - 'B' // 2f903 #1 - 'fA' // 2f904-2f90a - 'B' // 2f90b #1 - 'bA' // 2f90c-2f90e - 'B' // 2f90f #1 - 'iA' // 2f910-2f919 - 'B' // 2f91a #1 - 'dA' // 2f91b-2f91f - 'aB' // 2f920-2f921 #1 - '1hA' // 2f922-2f944 - 'B' // 2f945 #1 - 'A' // 2f946 - 'B' // 2f947 #1 - '1iA' // 2f948-2f96b - 'B' // 2f96c #1 - '1lA' // 2f96d-2f993 - 'H' // 2f994 #7 - 'B' // 2f995 #1 - '1aA' // 2f996-2f9b1 - 'H' // 2f9b2 #7 - 'hA' // 2f9b3-2f9bb - 'H' // 2f9bc #7 - 'rA' // 2f9bd-2f9cf - 'B' // 2f9d0 #1 - 'bA' // 2f9d1-2f9d3 - 'H' // 2f9d4 #7 - 'hA' // 2f9d5-2f9dd - 'aB' // 2f9de-2f9df #1 - 'sA' // 2f9e0-2f9f3 - 'B' // 2f9f4 #1 - '27789zA' // 2f9f5-e0061 - 'a15E' // e0062-e0063 #394 - 'A' // e0064 - '15E' // e0065 #394 - 'A' // e0066 - '15E' // e0067 #394 - 'cA' // e0068-e006b - '15E' // e006c #394 - 'A' // e006d - '15E' // e006e #394 - 'cA' // e006f-e0072 - 'a15E' // e0073-e0074 #394 - 'aA' // e0075-e0076 - '15E' // e0077 #394 - 'fA' // e0078-e007e - '15E' // e007f #394 - '7556wA' // e0080-10ffff + '1eA' // 0-1f + '75Z' // 20 #1975 + '76N' // 21 #1989 + '76P' // 22 #1991 + '73Z' // 23 #1923 + '76O' // 24 #1990 + '76Y' // 25 #2000 + '77B' // 26 #2003 + 'b24K' // 27-29 #634 + '51J' // 2a #1335 + '77E' // 2b #2006 + '24K' // 2c #634 + '77H' // 2d #2009 + 'a24K' // 2e-2f #634 + 'h51J' // 30-38 #1335 + '74A' // 39 #1924 + 'a24K' // 3a-3b #634 + '76V' // 3c #1997 + '76X' // 3d #1999 + '77D' // 3e #2005 + '77G' // 3f #2008 + '76Z' // 40 #2001 + 'c27P' // 41-44 #717 + '42N' // 45 #1105 + 'a27P' // 46-47 #717 + '76D' // 48 #1979 + '76F' // 49 #1981 + 'b27P' // 4a-4c #717 + '76E' // 4d #1980 + '76C' // 4e #1978 + '42N' // 4f #1105 + 'd27P' // 50-54 #717 + '42N' // 55 #1105 + 'd27P' // 56-5a #717 + 'b24K' // 5b-5d #634 + '76U' // 5e #1996 + '77A' // 5f #2002 + '76T' // 60 #1995 + 'y27P' // 61-7a #717 + '24K' // 7b #634 + '77F' // 7c #2007 + '24K' // 7d #634 + '77C' // 7e #2004 + 'F' // 7f #5 + '1eA' // 80-9f + '76A' // a0 #1976 + '79G' // a1 #2060 + '9A' // a2 #234 + '79J' // a3 #2063 + '48U' // a4 #1268 + '79M' // a5 #2066 + '62Y' // a6 #1636 + '76R' // a7 #1993 + '79H' // a8 #2061 + '74K' // a9 #1934 + '9A' // aa #234 + '42U' // ab #1112 + '121E' // ac #3150 + '247O' // ad #6436 + '51L' // ae #1337 + '79I' // af #2062 + '79N' // b0 #2067 + '121F' // b1 #3151 + '245W' // b2 #6392 + '245V' // b3 #6391 + '79L' // b4 #2065 + '62Y' // b5 #1636 + '9A' // b6 #234 + '79P' // b7 #2069 + '9A' // b8 #234 + '48U' // b9 #1268 + '9A' // ba #234 + '42U' // bb #1112 + 'b48U' // bc-be #1268 + '79S' // bf #2072 + '9A' // c0 #234 + '21S' // c1 #564 + '9A' // c2 #234 + 'b21S' // c3-c5 #564 + 'b9A' // c6-c8 #234 + '42T' // c9 #1111 + 'a9A' // ca-cb #234 + '21S' // cc #564 + '42V' // cd #1113 + 'b9A' // ce-d0 #234 + 'b21S' // d1-d3 #564 + '9A' // d4 #234 + '21S' // d5 #564 + '42V' // d6 #1113 + '76W' // d7 #1998 + '42S' // d8 #1110 + '9A' // d9 #234 + '21S' // da #564 + '9A' // db #234 + '42S' // dc #1110 + '21S' // dd #564 + '9A' // de #234 + '79F' // df #2059 + '52O' // e0 #1366 + '27Q' // e1 #718 + '52O' // e2 #1366 + 'b27Q' // e3-e5 #718 + '52N' // e6 #1365 + '79X' // e7 #2077 + '79U' // e8 #2074 + '79W' // e9 #2076 + '52P' // ea #1367 + '42T' // eb #1111 + 'a27Q' // ec-ed #718 + '52N' // ee #1365 + '42T' // ef #1111 + '9A' // f0 #234 + 'b27Q' // f1-f3 #718 + '79T' // f4 #2073 + '21S' // f5 #564 + '27Q' // f6 #718 + '76S' // f7 #1994 + '42S' // f8 #1110 + '79R' // f9 #2071 + '27Q' // fa #718 + '9A' // fb #234 + '52P' // fc #1367 + '42V' // fd #1113 + 'a9A' // fe-ff #234 + '32D' // 100 #835 + '36V' // 101 #957 + '78T' // 102 #2047 + '78X' // 103 #2051 + 'c8C' // 104-107 #210 + 'aE' // 108-109 #4 + 'e8C' // 10a-10f #210 + '78W' // 110 #2050 + '78V' // 111 #2049 + '32D' // 112 #835 + '36V' // 113 #957 + 'aE' // 114-115 #4 + 'c8C' // 116-119 #210 + '42R' // 11a #1109 + '32D' // 11b #835 + 'aE' // 11c-11d #4 + 'e8C' // 11e-123 #210 + 'aE' // 124-125 #4 + 'a8C' // 126-127 #210 + '49J' // 128 #1283 + '62X' // 129 #1635 + '78Z' // 12a #2053 + '36V' // 12b #957 + 'aE' // 12c-12d #4 + 'b8C' // 12e-130 #210 + '42W' // 131 #1114 + 'a80C' // 132-133 #2082 + 'aE' // 134-135 #4 + 'a8C' // 136-137 #210 + 'E' // 138 #4 + 'e8C' // 139-13e #210 + 'aE' // 13f-140 #4 + 'a8C' // 141-142 #210 + '42R' // 143 #1109 + '32D' // 144 #835 + 'a8C' // 145-146 #210 + '42R' // 147 #1109 + '78U' // 148 #2048 + 'E' // 149 #4 + 'a52L' // 14a-14b #1363 + '52K' // 14c #1362 + '79A' // 14d #2054 + '21F' // 14e #551 + '121C' // 14f #3148 + 'a8C' // 150-151 #210 + 'a9A' // 152-153 #234 + 'a8C' // 154-155 #210 + 'a52L' // 156-157 #1363 + 'c8C' // 158-15b #210 + 'aE' // 15c-15d #4 + 'c8C' // 15e-161 #210 + 'aE' // 162-163 #4 + 'a8C' // 164-165 #210 + 'aE' // 166-167 #4 + '247N' // 168 #6435 + '62X' // 169 #1635 + '32D' // 16a #835 + '36V' // 16b #957 + '79B' // 16c #2055 + '52K' // 16d #1362 + 'p8C' // 16e-17e #210 + 'rE' // 17f-191 #4 + '121B' // 192 #3147 + 'lE' // 193-19f #4 + '121A' // 1a0 #3146 + '245T' // 1a1 #6389 + 'lE' // 1a2-1ae #4 + '120Z' // 1af #3145 + '245U' // 1b0 #6390 + '1aE' // 1b1-1cc #4 + '80B' // 1cd #2081 + '80A' // 1ce #2080 + '21F' // 1cf #551 + '121D' // 1d0 #3149 + '21F' // 1d1 #551 + '35S' // 1d2 #928 + '21F' // 1d3 #551 + '35S' // 1d4 #928 + '21F' // 1d5 #551 + '49K' // 1d6 #1284 + '21F' // 1d7 #551 + '49K' // 1d8 #1284 + '21F' // 1d9 #551 + '35S' // 1da #928 + '21F' // 1db #551 + '35S' // 1dc #928 + 'zE' // 1dd-1f7 #4 + '49K' // 1f8 #1284 + '35S' // 1f9 #928 + '1cE' // 1fa-217 #4 + 'c8C' // 218-21b #210 + 'zE' // 21c-236 #4 + '8C' // 237 #210 + 'xE' // 238-250 #4 + '62W' // 251 #1634 + 'nE' // 252-260 #4 + '62W' // 261 #1634 + '3hE' // 262-2b8 #4 + '17K' // 2b9 #452 + 'E' // 2ba #4 + '120W' // 2bb #3142 + '261T' // 2bc #6805 + 'hE' // 2bd-2c5 #4 + '42W' // 2c6 #1114 + '79C' // 2c7 #2056 + 'E' // 2c8 #4 + '79D' // 2c9 #2057 + '124Y' // 2ca #3248 + '135O' // 2cb #3524 + 'E' // 2cc #4 + '262J' // 2cd #6821 + 'hE' // 2ce-2d6 #4 + '262W' // 2d7 #6834 + '41V' // 2d8 #1087 + '129Z' // 2d9 #3379 + '79Z' // 2da #2079 + '261J' // 2db #6795 + '42W' // 2dc #1114 + '79E' // 2dd #2058 + 'kE' // 2de-2e9 #4 + '41T' // 2ea #1085 + '120Y' // 2eb #3144 + 'sE' // 2ec-2ff #4 + '78R' // 300 #2045 + '78Q' // 301 #2044 + '77J' // 302 #2011 + '52C' // 303 #1354 + '78P' // 304 #2043 + '262A' // 305 #6812 + '41V' // 306 #1087 + '77I' // 307 #2010 + '52C' // 308 #1354 + '71L' // 309 #1857 + 'a41V' // 30a-30b #1087 + '120X' // 30c #3143 + 'E' // 30d #4 + '262E' // 30e #6816 + 'aE' // 30f-310 #4 + '17K' // 311 #452 + '77L' // 312 #2013 + '262Q' // 313 #6828 + 'kE' // 314-31f #4 + '50K' // 320 #1310 + 'aE' // 321-322 #4 + '80E' // 323 #2084 + '261Z' // 324 #6811 + '50K' // 325 #1310 + 'a77K' // 326-327 #2012 + '41V' // 328 #1087 + 'cE' // 329-32c #4 + 'a50K' // 32d-32e #1310 + 'E' // 32f #4 + '261Y' // 330 #6810 + '261V' // 331 #6807 + 'lE' // 332-33e #4 + '17K' // 33f #452 + 'nE' // 340-34e #4 + '261M' // 34f #6798 + 'gE' // 350-357 #4 + '262S' // 358 #6830 + 'E' // 359 #4 + '262R' // 35a #6829 + 'bE' // 35b-35d #4 + '261W' // 35e #6808 + 'aE' // 35f-360 #4 + '17K' // 361 #452 + 'qE' // 362-373 #4 + 'a17K' // 374-375 #452 + 'aE' // 376-377 #4 + 'aA' // 378-379 + 'eE' // 37a-37f #4 + 'cA' // 380-383 + 'fE' // 384-38a #4 + 'A' // 38b + 'E' // 38c #4 + 'A' // 38d + 'bE' // 38e-390 #4 + 'c50G' // 391-394 #1306 + '261I' // 395 #6794 + 'k50G' // 396-3a1 #1306 + 'A' // 3a2 + 'f50G' // 3a3-3a9 #1306 + 'fE' // 3aa-3b0 #4 + '261G' // 3b1 #6792 + 'd36N' // 3b2-3b6 #949 + '71G' // 3b7 #1852 + '36N' // 3b8 #949 + '50H' // 3b9 #1307 + '71G' // 3ba #1852 + '50H' // 3bb #1307 + '36N' // 3bc #949 + '50H' // 3bd #1307 + 'c36N' // 3be-3c1 #949 + '8B' // 3c2 #209 + 'f36N' // 3c3-3c9 #949 + 'fE' // 3ca-3d0 #4 + '8B' // 3d1 #209 + 'bE' // 3d2-3d4 #4 + 'a8B' // 3d5-3d6 #209 + 'bE' // 3d7-3d9 #4 + '41W' // 3da #1088 + 'E' // 3db #4 + '41W' // 3dc #1088 + 'E' // 3dd #4 + '41W' // 3de #1088 + 'E' // 3df #4 + '41W' // 3e0 #1088 + 'E' // 3e1 #4 + 'm50R' // 3e2-3ef #1317 + 'a8B' // 3f0-3f1 #209 + 'aE' // 3f2-3f3 #4 + 'a8B' // 3f4-3f5 #209 + 'jE' // 3f6-400 #4 + '49I' // 401 #1282 + 'mE' // 402-40f #4 + '2k49I' // 410-44f #1282 + 'E' // 450 #4 + '49I' // 451 #1282 + '1vE' // 452-482 #4 + '262F' // 483 #6817 + '41X' // 484 #1089 + 'aE' // 485-486 #4 + '41X' // 487 #1089 + '6kE' // 488-52f #4 + 'A' // 530 + '1k36P' // 531-556 #951 + 'aA' // 557-558 + '1u36P' // 559-588 #951 + '263E' // 589 #6842 + '36P' // 58a #951 + 'aA' // 58b-58c + 'b36P' // 58d-58f #951 + 'A' // 590 + '2b21R' // 591-5c7 #563 + 'gA' // 5c8-5cf + 'z21R' // 5d0-5ea #563 + 'cA' // 5eb-5ee + 'e21R' // 5ef-5f4 #563 + 'jA' // 5f5-5ff + 'd4W' // 600-604 #126 + '263A' // 605 #6838 + 'e4W' // 606-60b #126 + '50M' // 60c #1312 + 'm4W' // 60d-61a #126 + '50M' // 61b #1312 + 'b4W' // 61c-61e #126 + '262Y' // 61f #6836 + '4W' // 620 #126 + '50N' // 621 #1313 + 'd4W' // 622-626 #126 + '71M' // 627 #1858 + 'w4W' // 628-63f #126 + '262X' // 640 #6835 + 'i4W' // 641-64a #126 + 'j50N' // 64b-655 #1313 + 'i4W' // 656-65f #126 + 'i263B' // 660-669 #6839 + '50M' // 66a #1312 + 'a263D' // 66b-66c #6841 + '41Y' // 66d #1090 + 'a4W' // 66e-66f #126 + '50N' // 670 #1313 + '3t4W' // 671-6d3 #126 + '41Y' // 6d4 #1090 + 'z4W' // 6d5-6ef #126 + 'i71M' // 6f0-6f9 #1858 + 'e4W' // 6fa-6ff #126 + 'm51E' // 700-70d #1330 + 'A' // 70e + '2g51E' // 70f-74a #1330 + 'aA' // 74b-74c + 'b51E' // 74d-74f #1330 + '1u4W' // 750-77f #126 + '1w264Z' // 780-7b1 #6889 + 'mA' // 7b2-7bf + '2f72K' // 7c0-7fa #1882 + 'aA' // 7fb-7fc + 'b72K' // 7fd-7ff #1882 + '2kA' // 800-83f + '1a72G' // 840-85b #1878 + 'aA' // 85c-85d + '72G' // 85e #1878 + 'pA' // 85f-86f + '1d4W' // 870-88e #126 + 'A' // 88f + 'a4W' // 890-891 #126 + 'eA' // 892-897 + '3y4W' // 898-8ff #126 + '2h36O' // 900-93c #950 + '262D' // 93d #6815 + 'r36O' // 93e-950 #950 + 'a261R' // 951-952 #6803 + 'p36O' // 953-963 #950 + 'a261Q' // 964-965 #6802 + 'i262C' // 966-96f #6814 + 'o36O' // 970-97f #950 + 'c11N' // 980-983 #299 + 'A' // 984 + 'g11N' // 985-98c #299 + 'aA' // 98d-98e + 'a11N' // 98f-990 #299 + 'aA' // 991-992 + 'u11N' // 993-9a8 #299 + 'A' // 9a9 + 'f11N' // 9aa-9b0 #299 + 'A' // 9b1 + '11N' // 9b2 #299 + 'bA' // 9b3-9b5 + 'c11N' // 9b6-9b9 #299 + 'aA' // 9ba-9bb + 'h11N' // 9bc-9c4 #299 + 'aA' // 9c5-9c6 + 'a11N' // 9c7-9c8 #299 + 'aA' // 9c9-9ca + 'c11N' // 9cb-9ce #299 + 'gA' // 9cf-9d6 + '11N' // 9d7 #299 + 'cA' // 9d8-9db + 'a11N' // 9dc-9dd #299 + 'A' // 9de + 'd11N' // 9df-9e3 #299 + 'aA' // 9e4-9e5 + 'i263F' // 9e6-9ef #6843 + 'c11N' // 9f0-9f3 #299 + 'c263I' // 9f4-9f7 #6846 + 'f11N' // 9f8-9fe #299 + 'aA' // 9ff-a00 + 'b11P' // a01-a03 #301 + 'A' // a04 + 'e11P' // a05-a0a #301 + 'cA' // a0b-a0e + 'a11P' // a0f-a10 #301 + 'aA' // a11-a12 + 'u11P' // a13-a28 #301 + 'A' // a29 + 'f11P' // a2a-a30 #301 + 'A' // a31 + 'a11P' // a32-a33 #301 + 'A' // a34 + 'a11P' // a35-a36 #301 + 'A' // a37 + 'a11P' // a38-a39 #301 + 'aA' // a3a-a3b + '11P' // a3c #301 + 'A' // a3d + 'd11P' // a3e-a42 #301 + 'cA' // a43-a46 + 'a11P' // a47-a48 #301 + 'aA' // a49-a4a + 'b11P' // a4b-a4d #301 + 'bA' // a4e-a50 + '11P' // a51 #301 + 'fA' // a52-a58 + 'c11P' // a59-a5c #301 + 'A' // a5d + '11P' // a5e #301 + 'fA' // a5f-a65 + 'i264A' // a66-a6f #6864 + 'f11P' // a70-a76 #301 + 'iA' // a77-a80 + 'b13P' // a81-a83 #353 + 'A' // a84 + 'h13P' // a85-a8d #353 + 'A' // a8e + 'b13P' // a8f-a91 #353 + 'A' // a92 + 'u13P' // a93-aa8 #353 + 'A' // aa9 + 'f13P' // aaa-ab0 #353 + 'A' // ab1 + 'a13P' // ab2-ab3 #353 + 'A' // ab4 + 'd13P' // ab5-ab9 #353 + 'aA' // aba-abb + 'i13P' // abc-ac5 #353 + 'A' // ac6 + 'b13P' // ac7-ac9 #353 + 'A' // aca + 'b13P' // acb-acd #353 + 'aA' // ace-acf + '13P' // ad0 #353 + 'nA' // ad1-adf + 'c13P' // ae0-ae3 #353 + 'aA' // ae4-ae5 + 'i263Z' // ae6-aef #6863 + 'a13P' // af0-af1 #353 + 'fA' // af2-af8 + 'f13P' // af9-aff #353 + 'A' // b00 + 'b13Q' // b01-b03 #354 + 'A' // b04 + 'g13Q' // b05-b0c #354 + 'aA' // b0d-b0e + 'a13Q' // b0f-b10 #354 + 'aA' // b11-b12 + 'u13Q' // b13-b28 #354 + 'A' // b29 + 'f13Q' // b2a-b30 #354 + 'A' // b31 + 'a13Q' // b32-b33 #354 + 'A' // b34 + 'd13Q' // b35-b39 #354 + 'aA' // b3a-b3b + 'h13Q' // b3c-b44 #354 + 'aA' // b45-b46 + 'a13Q' // b47-b48 #354 + 'aA' // b49-b4a + 'b13Q' // b4b-b4d #354 + 'fA' // b4e-b54 + 'b13Q' // b55-b57 #354 + 'cA' // b58-b5b + 'a13Q' // b5c-b5d #354 + 'A' // b5e + 'd13Q' // b5f-b63 #354 + 'aA' // b64-b65 + 'q13Q' // b66-b77 #354 + 'iA' // b78-b81 + 'a10O' // b82-b83 #274 + 'A' // b84 + 'e10O' // b85-b8a #274 + 'bA' // b8b-b8d + 'b10O' // b8e-b90 #274 + 'A' // b91 + 'c10O' // b92-b95 #274 + 'bA' // b96-b98 + 'a10O' // b99-b9a #274 + 'A' // b9b + '10O' // b9c #274 + 'A' // b9d + 'a10O' // b9e-b9f #274 + 'bA' // ba0-ba2 + 'a10O' // ba3-ba4 #274 + 'bA' // ba5-ba7 + 'a10O' // ba8-ba9 #274 + '31U' // baa #826 + 'bA' // bab-bad + 'f10O' // bae-bb4 #274 + '31U' // bb5 #826 + 'c10O' // bb6-bb9 #274 + 'cA' // bba-bbd + 'd10O' // bbe-bc2 #274 + 'bA' // bc3-bc5 + 'b10O' // bc6-bc8 #274 + 'A' // bc9 + 'c10O' // bca-bcd #274 + 'aA' // bce-bcf + '10O' // bd0 #274 + 'eA' // bd1-bd6 + '10O' // bd7 #274 + 'mA' // bd8-be5 + 'l31U' // be6-bf2 #826 + 'g10O' // bf3-bfa #274 + 'dA' // bfb-bff + 'l15D' // c00-c0c #393 + 'A' // c0d + 'b15D' // c0e-c10 #393 + 'A' // c11 + 'v15D' // c12-c28 #393 + 'A' // c29 + 'o15D' // c2a-c39 #393 + 'aA' // c3a-c3b + 'h15D' // c3c-c44 #393 + 'A' // c45 + 'b15D' // c46-c48 #393 + 'A' // c49 + 'c15D' // c4a-c4d #393 + 'fA' // c4e-c54 + 'a15D' // c55-c56 #393 + 'A' // c57 + 'b15D' // c58-c5a #393 + 'aA' // c5b-c5c + '15D' // c5d #393 + 'aA' // c5e-c5f + 'c15D' // c60-c63 #393 + 'aA' // c64-c65 + 'i15D' // c66-c6f #393 + 'fA' // c70-c76 + 'h15D' // c77-c7f #393 + 'l15B' // c80-c8c #391 + 'A' // c8d + 'b15B' // c8e-c90 #391 + 'A' // c91 + 'v15B' // c92-ca8 #391 + 'A' // ca9 + 'i15B' // caa-cb3 #391 + 'A' // cb4 + 'd15B' // cb5-cb9 #391 + 'aA' // cba-cbb + 'h15B' // cbc-cc4 #391 + 'A' // cc5 + 'b15B' // cc6-cc8 #391 + 'A' // cc9 + 'c15B' // cca-ccd #391 + 'fA' // cce-cd4 + 'a15B' // cd5-cd6 #391 + 'eA' // cd7-cdc + 'a15B' // cdd-cde #391 + 'A' // cdf + 'c15B' // ce0-ce3 #391 + 'aA' // ce4-ce5 + 'i15B' // ce6-cef #391 + 'A' // cf0 + 'b15B' // cf1-cf3 #391 + 'kA' // cf4-cff + 'l27J' // d00-d0c #711 + 'A' // d0d + 'b27J' // d0e-d10 #711 + 'A' // d11 + '1x27J' // d12-d44 #711 + 'A' // d45 + 'b27J' // d46-d48 #711 + 'A' // d49 + 'e27J' // d4a-d4f #711 + 'cA' // d50-d53 + 'o27J' // d54-d63 #711 + 'aA' // d64-d65 + 'y27J' // d66-d7f #711 + 'A' // d80 + 'b15C' // d81-d83 #392 + 'A' // d84 + 'q15C' // d85-d96 #392 + 'bA' // d97-d99 + 'w15C' // d9a-db1 #392 + 'A' // db2 + 'h15C' // db3-dbb #392 + 'A' // dbc + '15C' // dbd #392 + 'aA' // dbe-dbf + 'f15C' // dc0-dc6 #392 + 'bA' // dc7-dc9 + '15C' // dca #392 + 'cA' // dcb-dce + 'e15C' // dcf-dd4 #392 + 'A' // dd5 + '15C' // dd6 #392 + 'A' // dd7 + 'g15C' // dd8-ddf #392 + 'eA' // de0-de5 + 'i15C' // de6-def #392 + 'aA' // df0-df1 + 'b15C' // df2-df4 #392 + 'kA' // df5-e00 + '2e73D' // e01-e3a #1901 + 'cA' // e3b-e3e + '1b73D' // e3f-e5b #1901 + '1jA' // e5c-e80 + 'a17M' // e81-e82 #454 + 'A' // e83 + '17M' // e84 #454 + 'A' // e85 + 'd17M' // e86-e8a #454 + 'A' // e8b + 'w17M' // e8c-ea3 #454 + 'A' // ea4 + '17M' // ea5 #454 + 'A' // ea6 + 'v17M' // ea7-ebd #454 + 'aA' // ebe-ebf + 'd17M' // ec0-ec4 #454 + 'A' // ec5 + '17M' // ec6 #454 + 'A' // ec7 + 'f17M' // ec8-ece #454 + 'A' // ecf + 'i17M' // ed0-ed9 #454 + 'aA' // eda-edb + 'c17M' // edc-edf #454 + '1eA' // ee0-eff + '2s31Y' // f00-f47 #830 + 'A' // f48 + '1i31Y' // f49-f6c #830 + 'cA' // f6d-f70 + '1l31Y' // f71-f97 #830 + 'A' // f98 + '1i31Y' // f99-fbc #830 + 'A' // fbd + 'n31Y' // fbe-fcc #830 + 'A' // fcd + 'l31Y' // fce-fda #830 + '1jA' // fdb-fff + '2k42D' // 1000-103f #1095 + 'i263N' // 1040-1049 #6851 + '3g42D' // 104a-109f #1095 + '1k19P' // 10a0-10c5 #509 + 'A' // 10c6 + '19P' // 10c7 #509 + 'dA' // 10c8-10cc + '19P' // 10cd #509 + 'aA' // 10ce-10cf + '1p19P' // 10d0-10fa #509 + '71K' // 10fb #1856 + 'c19P' // 10fc-10ff #509 + '9uA' // 1100-11ff + '2t3R' // 1200-1248 #95 + 'A' // 1249 + 'c3R' // 124a-124d #95 + 'aA' // 124e-124f + 'f3R' // 1250-1256 #95 + 'A' // 1257 + '3R' // 1258 #95 + 'A' // 1259 + 'c3R' // 125a-125d #95 + 'aA' // 125e-125f + '1n3R' // 1260-1288 #95 + 'A' // 1289 + 'c3R' // 128a-128d #95 + 'aA' // 128e-128f + '1f3R' // 1290-12b0 #95 + 'A' // 12b1 + 'c3R' // 12b2-12b5 #95 + 'aA' // 12b6-12b7 + 'f3R' // 12b8-12be #95 + 'A' // 12bf + '3R' // 12c0 #95 + 'A' // 12c1 + 'c3R' // 12c2-12c5 #95 + 'aA' // 12c6-12c7 + 'n3R' // 12c8-12d6 #95 + 'A' // 12d7 + '2d3R' // 12d8-1310 #95 + 'A' // 1311 + 'c3R' // 1312-1315 #95 + 'aA' // 1316-1317 + '2n3R' // 1318-135a #95 + 'aA' // 135b-135c + '1e3R' // 135d-137c #95 + 'bA' // 137d-137f + 'y3R' // 1380-1399 #95 + 'eA' // 139a-139f + '3g50Q' // 13a0-13f5 #1316 + 'aA' // 13f6-13f7 + 'e50Q' // 13f8-13fd #1316 + 'aA' // 13fe-13ff + '24o50P' // 1400-167f #1315 + '1b264I' // 1680-169c #6872 + 'bA' // 169d-169f + '3j264S' // 16a0-16f8 #6882 + 'fA' // 16f9-16ff + 'u72Y' // 1700-1715 #1896 + 'hA' // 1716-171e + '72Y' // 171f #1896 + 't264C' // 1720-1734 #6866 + 'a263L' // 1735-1736 #6849 + 'hA' // 1737-173f + 's263K' // 1740-1753 #6848 + 'kA' // 1754-175f + 'l51F' // 1760-176c #1331 + 'A' // 176d + 'b51F' // 176e-1770 #1331 + 'A' // 1771 + 'a51F' // 1772-1773 #1331 + 'kA' // 1774-177f + '3o42C' // 1780-17dd #1094 + 'aA' // 17de-17df + 'i42C' // 17e0-17e9 #1094 + 'eA' // 17ea-17ef + 'i42C' // 17f0-17f9 #1094 + 'eA' // 17fa-17ff + '31X' // 1800 #829 + 'b72J' // 1801-1803 #1881 + '31X' // 1804 #829 + '72J' // 1805 #1881 + 's31X' // 1806-1819 #829 + 'eA' // 181a-181f + '3j31X' // 1820-1878 #829 + 'fA' // 1879-187f + '1p31X' // 1880-18aa #829 + 'dA' // 18ab-18af + '2q50P' // 18b0-18f5 #1315 + 'iA' // 18f6-18ff + '1d36Q' // 1900-191e #952 + 'A' // 191f + 'k36Q' // 1920-192b #952 + 'cA' // 192c-192f + 'k36Q' // 1930-193b #952 + 'cA' // 193c-193f + '36Q' // 1940 #952 + 'bA' // 1941-1943 + 'k36Q' // 1944-194f #952 + '1c72Z' // 1950-196d #1897 + 'aA' // 196e-196f + 'd72Z' // 1970-1974 #1897 + 'jA' // 1975-197f + '1q42E' // 1980-19ab #1096 + 'cA' // 19ac-19af + 'y42E' // 19b0-19c9 #1096 + 'eA' // 19ca-19cf + 'j42E' // 19d0-19da #1096 + 'bA' // 19db-19dd + 'a42E' // 19de-19df #1096 + '1e42C' // 19e0-19ff #1094 + '1a71T' // 1a00-1a1b #1865 + 'aA' // 1a1c-1a1d + 'a71T' // 1a1e-1a1f #1865 + '2j36T' // 1a20-1a5e #955 + 'A' // 1a5f + '1b36T' // 1a60-1a7c #955 + 'aA' // 1a7d-1a7e + 'j36T' // 1a7f-1a89 #955 + 'eA' // 1a8a-1a8f + 'i36T' // 1a90-1a99 #955 + 'eA' // 1a9a-1a9f + 'm36T' // 1aa0-1aad #955 + 'aA' // 1aae-1aaf + 'pE' // 1ab0-1ac0 #4 + 'cA' // 1ac1-1ac4 + 'E' // 1ac5 #4 + 'A' // 1ac6 + 'gE' // 1ac7-1ace #4 + '1vA' // 1acf-1aff + '2w71O' // 1b00-1b4b #1860 + 'cA' // 1b4c-1b4f + '1r71O' // 1b50-1b7c #1860 + 'bA' // 1b7d-1b7f + '2k72X' // 1b80-1bbf #1895 + '1y71R' // 1bc0-1bf3 #1863 + 'gA' // 1bf4-1bfb + 'c71R' // 1bfc-1bff #1863 + '2c50V' // 1c00-1c37 #1321 + 'bA' // 1c38-1c3a + 'n50V' // 1c3b-1c49 #1321 + 'bA' // 1c4a-1c4c + 'b50V' // 1c4d-1c4f #1321 + '1u264J' // 1c50-1c7f #6873 + 'hE' // 1c80-1c88 #4 + 'fA' // 1c89-1c8f + '1p19P' // 1c90-1cba #509 + 'aA' // 1cbb-1cbc + 'b19P' // 1cbd-1cbf #509 + 'g72X' // 1cc0-1cc7 #1895 + 'gA' // 1cc8-1ccf + '71S' // 1cd0 #1864 + '17L' // 1cd1 #453 + '71S' // 1cd2 #1864 + '50S' // 1cd3 #1318 + '17L' // 1cd4 #453 + 'a27H' // 1cd5-1cd6 #709 + '42B' // 1cd7 #1093 + '27H' // 1cd8 #709 + '42B' // 1cd9 #1093 + '263T' // 1cda #6857 + '17L' // 1cdb #453 + 'a42B' // 1cdc-1cdd #1093 + 'a17L' // 1cde-1cdf #453 + '42B' // 1ce0 #1093 + '27H' // 1ce1 #709 + 'g17L' // 1ce2-1ce9 #453 + '27H' // 1cea #709 + 'a17L' // 1ceb-1cec #453 + '27H' // 1ced #709 + 'c17L' // 1cee-1cf1 #453 + '263G' // 1cf2 #6844 + '50S' // 1cf3 #1318 + '263P' // 1cf4 #6853 + '263H' // 1cf5 #6845 + '27H' // 1cf6 #709 + '11N' // 1cf7 #299 + 'a50S' // 1cf8-1cf9 #1318 + 'eA' // 1cfa-1cff + '7vE' // 1d00-1dcc #4 + '17K' // 1dcd #452 + '1qE' // 1dce-1df9 #4 + 'A' // 1dfa + '262N' // 1dfb #6825 + '2mE' // 1dfc-1e3d #4 + 'a21F' // 1e3e-1e3f #551 + '2kE' // 1e40-1e7f #4 + 'e8C' // 1e80-1e85 #210 + 'wE' // 1e86-1e9d #4 + '8C' // 1e9e #210 + 'E' // 1e9f #4 + '15Z' // 1ea0 #415 + '69E' // 1ea1 #1798 + '15Z' // 1ea2 #415 + '69E' // 1ea3 #1798 + 'e15Z' // 1ea4-1ea9 #415 + '11B' // 1eaa #287 + 'b15Z' // 1eab-1ead #415 + '120H' // 1eae #3127 + '15Z' // 1eaf #415 + '11B' // 1eb0 #287 + '15Z' // 1eb1 #415 + '11B' // 1eb2 #287 + '15Z' // 1eb3 #415 + '11B' // 1eb4 #287 + '15Z' // 1eb5 #415 + '11B' // 1eb6 #287 + '15Z' // 1eb7 #415 + '11B' // 1eb8 #287 + '15Z' // 1eb9 #415 + '11B' // 1eba #287 + '15Z' // 1ebb #415 + '11B' // 1ebc #287 + 'a15Z' // 1ebd-1ebe #415 + '69F' // 1ebf #1799 + 'c11A' // 1ec0-1ec3 #286 + '11B' // 1ec4 #287 + 'a11A' // 1ec5-1ec6 #286 + '69F' // 1ec7 #1799 + '11B' // 1ec8 #287 + '11A' // 1ec9 #286 + '35W' // 1eca #932 + 'b11A' // 1ecb-1ecd #286 + '11B' // 1ece #287 + 'b11A' // 1ecf-1ed1 #286 + '35W' // 1ed2 #932 + '11A' // 1ed3 #286 + '62U' // 1ed4 #1632 + '11A' // 1ed5 #286 + '11B' // 1ed6 #287 + 'f11A' // 1ed7-1edd #286 + '35W' // 1ede #932 + '11A' // 1edf #286 + '11B' // 1ee0 #287 + '11A' // 1ee1 #286 + '11B' // 1ee2 #287 + '11A' // 1ee3 #286 + '62U' // 1ee4 #1632 + 'b11A' // 1ee5-1ee7 #286 + '35W' // 1ee8 #932 + '11A' // 1ee9 #286 + '35W' // 1eea #932 + '11A' // 1eeb #286 + '11B' // 1eec #287 + '11A' // 1eed #286 + '11B' // 1eee #287 + 'b11A' // 1eef-1ef1 #286 + '78Y' // 1ef2 #2052 + '78S' // 1ef3 #2046 + 'b49J' // 1ef4-1ef6 #1283 + '62T' // 1ef7 #1631 + '49J' // 1ef8 #1283 + '62T' // 1ef9 #1631 + '1aE' // 1efa-1f15 #4 + 'aA' // 1f16-1f17 + 'eE' // 1f18-1f1d #4 + 'aA' // 1f1e-1f1f + '1kE' // 1f20-1f45 #4 + 'aA' // 1f46-1f47 + 'eE' // 1f48-1f4d #4 + 'aA' // 1f4e-1f4f + 'gE' // 1f50-1f57 #4 + 'A' // 1f58 + 'E' // 1f59 #4 + 'A' // 1f5a + 'E' // 1f5b #4 + 'A' // 1f5c + 'E' // 1f5d #4 + 'A' // 1f5e + '1dE' // 1f5f-1f7d #4 + 'aA' // 1f7e-1f7f + '1zE' // 1f80-1fb4 #4 + 'A' // 1fb5 + 'nE' // 1fb6-1fc4 #4 + 'A' // 1fc5 + 'mE' // 1fc6-1fd3 #4 + 'aA' // 1fd4-1fd5 + 'eE' // 1fd6-1fdb #4 + 'A' // 1fdc + 'rE' // 1fdd-1fef #4 + 'aA' // 1ff0-1ff1 + 'bE' // 1ff2-1ff4 #4 + 'A' // 1ff5 + 'hE' // 1ff6-1ffe #4 + 'A' // 1fff + 'aE' // 2000-2001 #4 + '120M' // 2002 #3132 + '120S' // 2003 #3138 + 'dE' // 2004-2008 #4 + '71J' // 2009 #1855 + 'E' // 200a #4 + '261K' // 200b #6796 + '80D' // 200c #2083 + '73K' // 200d #1908 + 'a261N' // 200e-200f #6799 + '120U' // 2010 #3140 + '120R' // 2011 #3137 + '120Q' // 2012 #3136 + 'a42U' // 2013-2014 #1112 + '120V' // 2015 #3141 + '120N' // 2016 #3133 + 'E' // 2017 #4 + 'a79Y' // 2018-2019 #2078 + '52M' // 201a #1364 + '71H' // 201b #1853 + 'a79O' // 201c-201d #2068 + '52M' // 201e #1364 + 'E' // 201f #4 + '120I' // 2020 #3128 + '62V' // 2021 #1633 + '77N' // 2022 #2015 + 'E' // 2023 #4 + '261O' // 2024 #6800 + '120T' // 2025 #3139 + '79Q' // 2026 #2070 + '216U' // 2027 #5636 + 'eE' // 2028-202d #4 + '71L' // 202e #1857 + '261L' // 202f #6797 + '62V' // 2030 #1633 + 'E' // 2031 #4 + '120L' // 2032 #3131 + '120J' // 2033 #3129 + '8B' // 2034 #209 + '120O' // 2035 #3134 + 'a8B' // 2036-2037 #209 + 'E' // 2038 #4 + 'a79V' // 2039-203a #2075 + '206L' // 203b #5367 + '74W' // 203c #1946 + 'dE' // 203d-2041 #4 + '120P' // 2042 #3135 + 'E' // 2043 #4 + '80F' // 2044 #2085 + 'aE' // 2045-2046 #4 + '41T' // 2047 #1085 + '260L' // 2048 #6771 + '74U' // 2049 #1944 + 'dE' // 204a-204e #4 + '71I' // 204f #1854 + 'E' // 2050 #4 + '41T' // 2051 #1085 + 'E' // 2052 #4 + '17K' // 2053 #452 + 'E' // 2054 #4 + '262T' // 2055 #6831 + '17K' // 2056 #452 + '8B' // 2057 #209 + 'a17K' // 2058-2059 #452 + '262P' // 205a #6827 + 'aE' // 205b-205c #4 + '262L' // 205d #6823 + '262O' // 205e #6826 + 'eE' // 205f-2064 #4 + 'A' // 2065 + 'kE' // 2066-2071 #4 + 'aA' // 2072-2073 + '120K' // 2074 #3130 + 'lE' // 2075-2081 #4 + 'b262V' // 2082-2084 #6833 + 'iE' // 2085-208e #4 + 'A' // 208f + 'lE' // 2090-209c #4 + 'bA' // 209d-209f + 'hE' // 20a0-20a8 #4 + '119B' // 20a9 #3095 + '262G' // 20aa #6818 + '119A' // 20ab #3094 + '79K' // 20ac #2064 + '262I' // 20ad #6820 + 'jE' // 20ae-20b8 #4 + '261S' // 20b9 #6804 + 'cE' // 20ba-20bd #4 + '71K' // 20be #1856 + 'aE' // 20bf-20c0 #4 + 'nA' // 20c1-20cf + 'jM' // 20d0-20da #12 + '264G' // 20db #6870 + 'M' // 20dc #12 + '119O' // 20dd #3108 + '251T' // 20de #6545 + 'a2M' // 20df-20e0 #64 + 'M' // 20e1 #12 + '52J' // 20e2 #1361 + '74J' // 20e3 #1933 + '2M' // 20e4 #64 + 'jM' // 20e5-20ef #12 + '262B' // 20f0 #6813 + 'nA' // 20f1-20ff + '41T' // 2100 #1085 + 'E' // 2101 #4 + '8B' // 2102 #209 + '154U' // 2103 #4024 + 'E' // 2104 #4 + '119N' // 2105 #3107 + 'bE' // 2106-2108 #4 + '40Y' // 2109 #1064 + '70T' // 210a #1839 + 'c8B' // 210b-210e #209 + '41R' // 210f #1083 + 'b8B' // 2110-2112 #209 + '119C' // 2113 #3096 + 'E' // 2114 #4 + '8B' // 2115 #209 + '118Z' // 2116 #3093 + 'aE' // 2117-2118 #4 + 'd8B' // 2119-211d #209 + 'bE' // 211e-2120 #4 + '40Y' // 2121 #1064 + '51L' // 2122 #1337 + 'E' // 2123 #4 + '8B' // 2124 #209 + 'E' // 2125 #4 + '40Y' // 2126 #1064 + '41R' // 2127 #1083 + '8B' // 2128 #209 + 'aE' // 2129-212a #4 + '40Y' // 212b #1064 + 'a8B' // 212c-212d #209 + '41R' // 212e #1083 + 'b8B' // 212f-2131 #209 + 'E' // 2132 #4 + 'a8B' // 2133-2134 #209 + '70T' // 2135 #1839 + 'b8B' // 2136-2138 #209 + '74Z' // 2139 #1949 + 'E' // 213a #4 + '41R' // 213b #1083 + 'd8B' // 213c-2140 #209 + 'cE' // 2141-2144 #4 + 'd8B' // 2145-2149 #209 + 'uE' // 214a-215f #4 + '261F' // 2160 #6791 + '261H' // 2161 #6793 + 'g71F' // 2162-2169 #1851 + 'e2M' // 216a-216f #64 + 'i71F' // 2170-2179 #1851 + 'h2M' // 217a-2182 #64 + '262U' // 2183 #6832 + 'E' // 2184 #4 + 'c2M' // 2185-2188 #64 + 'E' // 2189 #4 + 'a2M' // 218a-218b #64 + 'cA' // 218c-218f + '161Z' // 2190 #4211 + '155V' // 2191 #4051 + '206E' // 2192 #5360 + '155U' // 2193 #4050 + '73Y' // 2194 #1922 + '73X' // 2195 #1921 + 'a51P' // 2196-2197 #1341 + '74V' // 2198 #1945 + '51P' // 2199 #1341 + 'nM' // 219a-21a8 #12 + 'a75A' // 21a9-21aa #1950 + 'cM' // 21ab-21ae #12 + 'F' // 21af #5 + 'gM' // 21b0-21b7 #12 + 'a21N' // 21b8-21b9 #559 + 'iM' // 21ba-21c3 #12 + '8I' // 21c4 #216 + '12N' // 21c5 #325 + '21N' // 21c6 #559 + 'cM' // 21c7-21ca #12 + '21N' // 21cb #559 + '48T' // 21cc #1267 + 'bM' // 21cd-21cf #12 + '8I' // 21d0 #216 + 'M' // 21d1 #12 + '120G' // 21d2 #3126 + 'M' // 21d3 #12 + '119Q' // 21d4 #3110 + 'pM' // 21d5-21e5 #12 + 'c77Z' // 21e6-21e9 #2027 + 'fF' // 21ea-21f0 #5 + 'aM' // 21f1-21f2 #12 + 'F' // 21f3 #5 + 'M' // 21f4 #12 + '21N' // 21f5 #559 + 'iM' // 21f6-21ff #12 + '62S' // 2200 #1630 + 'M' // 2201 #12 + '8I' // 2202 #216 + '12N' // 2203 #325 + 'M' // 2204 #12 + '48T' // 2205 #1267 + '8I' // 2206 #216 + '119Z' // 2207 #3119 + '8I' // 2208 #216 + '12N' // 2209 #325 + '21N' // 220a #559 + '12N' // 220b #325 + 'bM' // 220c-220e #12 + '8I' // 220f #216 + 'M' // 2210 #12 + '8I' // 2211 #216 + '76Q' // 2212 #1992 + '12N' // 2213 #325 + 'M' // 2214 #12 + '119D' // 2215 #3097 + 'aM' // 2216-2217 #12 + '32C' // 2218 #834 + '76M' // 2219 #1988 + '119I' // 221a #3102 + 'aM' // 221b-221c #12 + '8I' // 221d #216 + '119X' // 221e #3117 + '120B' // 221f #3121 + '8I' // 2220 #216 + 'aM' // 2221-2222 #12 + '124N' // 2223 #3237 + 'M' // 2224 #12 + '8I' // 2225 #216 + '12N' // 2226 #325 + '119P' // 2227 #3109 + '119M' // 2228 #3106 + '119S' // 2229 #3112 + 'a8I' // 222a-222b #216 + '12N' // 222c #325 + '21N' // 222d #559 + '8I' // 222e #216 + 'dM' // 222f-2233 #12 + 'c8I' // 2234-2237 #216 + 'dM' // 2238-223c #12 + '8I' // 223d #216 + 'dM' // 223e-2242 #12 + '12N' // 2243 #325 + 'M' // 2244 #12 + '12N' // 2245 #325 + 'aM' // 2246-2247 #12 + '8I' // 2248 #216 + 'bM' // 2249-224b #12 + '251R' // 224c #6543 + 'rM' // 224d-225f #12 + '8I' // 2260 #216 + '62R' // 2261 #1629 + '12N' // 2262 #325 + 'M' // 2263 #12 + '119J' // 2264 #3103 + '119K' // 2265 #3104 + '130P' // 2266 #3395 + '136Q' // 2267 #3552 + 'aM' // 2268-2269 #12 + '120C' // 226a #3122 + '62S' // 226b #1630 + 'aM' // 226c-226d #12 + 'a8I' // 226e-226f #216 + 'aM' // 2270-2271 #12 + 'a12N' // 2272-2273 #325 + 'aM' // 2274-2275 #12 + 'a12N' // 2276-2277 #325 + 'iM' // 2278-2281 #12 + '62R' // 2282 #1629 + '8I' // 2283 #216 + 'c12N' // 2284-2287 #325 + 'aM' // 2288-2289 #12 + 'a12N' // 228a-228b #325 + 'hM' // 228c-2294 #12 + '8I' // 2295 #216 + '119L' // 2296 #3105 + '251P' // 2297 #6541 + '21N' // 2298 #559 + '76K' // 2299 #1986 + 'eM' // 229a-229f #12 + '21N' // 22a0 #559 + 'cM' // 22a1-22a4 #12 + '8I' // 22a5 #216 + 'xM' // 22a6-22be #12 + '48T' // 22bf #1267 + 'cM' // 22c0-22c3 #12 + 'b32C' // 22c4-22c6 #834 + 'rM' // 22c7-22d9 #12 + 'a12N' // 22da-22db #325 + 'qM' // 22dc-22ed #12 + '263X' // 22ee #6861 + '172M' // 22ef #4484 + 'oM' // 22f0-22ff #12 + 'd2M' // 2300-2304 #64 + '41S' // 2305 #1084 + '70S' // 2306 #1838 + '251S' // 2307 #6544 + 'c31W' // 2308-230b #828 + 'c2M' // 230c-230f #64 + 'M' // 2310 #12 + '2M' // 2311 #64 + '119V' // 2312 #3115 + 'b2M' // 2313-2315 #64 + 'F' // 2316 #5 + '2M' // 2317 #64 + '78A' // 2318 #2028 + 'M' // 2319 #12 + 'a2E' // 231a-231b #56 + 'c31W' // 231c-231f #828 + 'aM' // 2320-2321 #12 + 'a2M' // 2322-2323 #64 + 'cF' // 2324-2327 #5 + '2E' // 2328 #56 + 'a41S' // 2329-232a #1084 + 'F' // 232b #5 + 'i2M' // 232c-2335 #64 + '2pM' // 2336-237a #12 + 'F' // 237b #5 + '31W' // 237c #828 + 'bF' // 237d-237f #5 + 's2M' // 2380-2393 #64 + '52J' // 2394 #1361 + 'M' // 2395 #12 + 'd2M' // 2396-239a #64 + 'sM' // 239b-23ae #12 + '31W' // 23af #828 + 'a21N' // 23b0-23b1 #559 + 'dM' // 23b2-23b6 #12 + 'fA' // 23b7-23bd + 'n41S' // 23be-23cc #1084 + '2M' // 23cd #64 + '52I' // 23ce #1360 + '4M' // 23cf #116 + '31W' // 23d0 #828 + 'h2M' // 23d1-23d9 #64 + 'a41S' // 23da-23db #1084 + 'eM' // 23dc-23e1 #12 + 'f2M' // 23e2-23e8 #64 + 'a4M' // 23e9-23ea #116 + 'a3S' // 23eb-23ec #96 + 'b4M' // 23ed-23ef #116 + '2S' // 23f0 #70 + 'b2E' // 23f1-23f3 #56 + 'cF' // 23f4-23f7 #5 + 'b4M' // 23f8-23fa #116 + '1mF' // 23fb-2422 #5 + '52I' // 2423 #1360 + 'bF' // 2424-2426 #5 + 'xA' // 2427-243f + 'jF' // 2440-244a #5 + 'tA' // 244b-245f + '120F' // 2460 #3125 + '120E' // 2461 #3124 + '120D' // 2462 #3123 + '120A' // 2463 #3120 + '119Y' // 2464 #3118 + '119W' // 2465 #3116 + '119U' // 2466 #3114 + '119T' // 2467 #3113 + '119R' // 2468 #3111 + 'h119H' // 2469-2471 #3101 + 'a70S' // 2472-2473 #1838 + '119G' // 2474 #3100 + '119E' // 2475 #3098 + '119F' // 2476 #3099 + 'a118U' // 2477-2478 #3088 + 'h35R' // 2479-2481 #927 + 'e251O' // 2482-2487 #6540 + 'i118V' // 2488-2491 #3089 + 'i251Q' // 2492-249b #6542 + 'y36L' // 249c-24b5 #947 + 'a26W' // 24b6-24b7 #698 + '35R' // 24b8 #927 + 'h26W' // 24b9-24c1 #698 + '74T' // 24c2 #1943 + 'b26W' // 24c3-24c5 #698 + '36L' // 24c6 #947 + '35R' // 24c7 #927 + '26W' // 24c8 #698 + '35R' // 24c9 #927 + '26W' // 24ca #698 + '36L' // 24cb #947 + '26W' // 24cc #698 + '36L' // 24cd #947 + '26W' // 24ce #698 + '36L' // 24cf #947 + '35R' // 24d0 #927 + '21D' // 24d1 #549 + '118S' // 24d2 #3086 + '21D' // 24d3 #549 + '35Q' // 24d4 #926 + 'b21D' // 24d5-24d7 #549 + '118R' // 24d8 #3085 + 'c21D' // 24d9-24dc #549 + 'a35Q' // 24dd-24de #926 + '21D' // 24df #549 + '70R' // 24e0 #1837 + 'a21D' // 24e1-24e2 #549 + '35Q' // 24e3 #926 + 'a21D' // 24e4-24e5 #549 + '35Q' // 24e6 #926 + '21D' // 24e7 #549 + '35Q' // 24e8 #926 + '21D' // 24e9 #549 + '48S' // 24ea #1266 + 'i70R' // 24eb-24f4 #1837 + '48S' // 24f5 #1266 + 'h251N' // 24f6-24fe #6539 + '48S' // 24ff #1266 + '189E' // 2500 #4918 + '118Y' // 2501 #3092 + '196F' // 2502 #5101 + '10Z' // 2503 #285 + 'e62P' // 2504-2509 #1627 + '118T' // 250a #3087 + '62P' // 250b #1627 + 'a10Z' // 250c-250d #285 + '16D' // 250e #419 + 'b10Z' // 250f-2511 #285 + '16D' // 2512 #419 + '10Z' // 2513 #285 + '118X' // 2514 #3091 + '10Z' // 2515 #285 + '16D' // 2516 #419 + '118W' // 2517 #3090 + '10Z' // 2518 #285 + 'a16D' // 2519-251a #419 + '10Z' // 251b #285 + '62Q' // 251c #1628 + '10Z' // 251d #285 + 'a16D' // 251e-251f #419 + '10Z' // 2520 #285 + 'a16D' // 2521-2522 #419 + '62Q' // 2523 #1628 + '10Z' // 2524 #285 + 'a16D' // 2525-2526 #419 + '50D' // 2527 #1303 + '10Z' // 2528 #285 + 'a16D' // 2529-252a #419 + 'a10Z' // 252b-252c #285 + 'a16D' // 252d-252e #419 + '10Z' // 252f #285 + 'b16D' // 2530-2532 #419 + 'a10Z' // 2533-2534 #285 + '50D' // 2535 #1303 + '16D' // 2536 #419 + '10Z' // 2537 #285 + 'b50D' // 2538-253a #1303 + 'a10Z' // 253b-253c #285 + 'c16D' // 253d-2540 #419 + '48R' // 2541 #1265 + '50C' // 2542 #1302 + 'b48R' // 2543-2545 #1265 + 'b50C' // 2546-2548 #1302 + '251M' // 2549 #6538 + '50C' // 254a #1302 + '48R' // 254b #1265 + 'c36K' // 254c-254f #946 + 'a23X' // 2550-2551 #621 + 'a26V' // 2552-2553 #697 + '23X' // 2554 #621 + 'a26V' // 2555-2556 #697 + '23X' // 2557 #621 + 'a26V' // 2558-2559 #697 + 'a23X' // 255a-255b #621 + '26V' // 255c #697 + '23X' // 255d #621 + '26V' // 255e #697 + 'a23X' // 255f-2560 #621 + '26V' // 2561 #697 + 'a23X' // 2562-2563 #621 + '26V' // 2564 #697 + '23X' // 2565 #621 + 'a23W' // 2566-2567 #620 + '40X' // 2568 #1063 + 'a23W' // 2569-256a #620 + '40X' // 256b #1063 + 'd23W' // 256c-2570 #620 + '64X' // 2571 #1687 + '23W' // 2572 #620 + '135H' // 2573 #3517 + '40X' // 2574 #1063 + 'c36K' // 2575-2578 #946 + '23W' // 2579 #620 + 'e36K' // 257a-257f #946 + 'j23W' // 2580-258a #620 + '64X' // 258b #1687 + 'e23W' // 258c-2591 #620 + '118Q' // 2592 #3084 + 'b23W' // 2593-2595 #620 + '36K' // 2596 #946 + '40X' // 2597 #1063 + 'g36K' // 2598-259f #946 + '78E' // 25a0 #2032 + '77X' // 25a1 #2025 + '78N' // 25a2 #2041 + '77T' // 25a3 #2021 + '52G' // 25a4 #1358 + '52H' // 25a5 #1359 + 'b52G' // 25a6-25a8 #1358 + '42P' // 25a9 #1107 + 'a74G' // 25aa-25ab #1930 + 'bF' // 25ac-25ae #5 + '32C' // 25af #834 + 'F' // 25b0 #5 + '52H' // 25b1 #1359 + '78M' // 25b2 #2040 + '76L' // 25b3 #1987 + 'aF' // 25b4-25b5 #5 + '74I' // 25b6 #1932 + '76I' // 25b7 #1984 + 'cF' // 25b8-25bb #5 + '78K' // 25bc #2038 + '76J' // 25bd #1985 + 'aF' // 25be-25bf #5 + '74F' // 25c0 #1929 + '52B' // 25c1 #1353 + 'cF' // 25c2-25c5 #5 + '78H' // 25c6 #2035 + '77Y' // 25c7 #2026 + 'F' // 25c8 #5 + '42P' // 25c9 #1107 + '52B' // 25ca #1353 + '78F' // 25cb #2033 + '76G' // 25cc #1982 + 'F' // 25cd #5 + '78G' // 25ce #2034 + '78I' // 25cf #2036 + 'a42P' // 25d0-25d1 #1107 + 'a77V' // 25d2-25d3 #2023 + 'mF' // 25d4-25e1 #5 + 'a77U' // 25e2-25e3 #2022 + 'a52F' // 25e4-25e5 #1357 + '42O' // 25e6 #1106 + 'gF' // 25e7-25ee #5 + '77W' // 25ef #2024 + 'jF' // 25f0-25fa #5 + '74B' // 25fb #1925 + 'b4M' // 25fc-25fe #116 + 'F' // 25ff #5 + '75V' // 2600 #1971 + '51W' // 2601 #1348 + '75M' // 2602 #1962 + '51W' // 2603 #1348 + '5Z' // 2604 #155 + '78L' // 2605 #2039 + '78J' // 2606 #2037 + 'aF' // 2607-2608 #5 + '52F' // 2609 #1357 + 'c2M' // 260a-260d #64 + '75L' // 260e #1961 + '42O' // 260f #1106 + 'F' // 2610 #5 + '4M' // 2611 #116 + 'F' // 2612 #5 + '2M' // 2613 #64 + '5Z' // 2614 #155 + '32A' // 2615 #832 + 'a42Q' // 2616-2617 #1108 + '5Z' // 2618 #155 + 'bF' // 2619-261b #5 + '77Q' // 261c #2018 + '75Y' // 261d #1974 + '77R' // 261e #2019 + '42O' // 261f #1106 + '73N' // 2620 #1911 + 'F' // 2621 #5 + 'a4M' // 2622-2623 #116 + 'a2M' // 2624-2625 #64 + '17N' // 2626 #455 + 'b2M' // 2627-2629 #64 + '17N' // 262a #455 + '2M' // 262b #64 + '264B' // 262c #6865 + '2M' // 262d #64 + '17N' // 262e #455 + '74R' // 262f #1941 + 'gF' // 2630-2637 #5 + '75B' // 2638 #1951 + 'a52A' // 2639-263a #1352 + '2M' // 263b #64 + 'F' // 263c #5 + 'b2M' // 263d-263f #64 + '73S' // 2640 #1916 + '118L' // 2641 #3079 + '73R' // 2642 #1915 + 'd2M' // 2643-2647 #64 + 'k17N' // 2648-2653 #455 + 'jF' // 2654-265e #5 + '6J' // 265f #165 + '51K' // 2660 #1336 + '78D' // 2661 #2031 + '77S' // 2662 #2020 + '74D' // 2663 #1927 + '52E' // 2664 #1356 + '73V' // 2665 #1919 + '74E' // 2666 #1928 + '52E' // 2667 #1356 + '51K' // 2668 #1336 + '118N' // 2669 #3081 + '155N' // 266a #4043 + '118P' // 266b #3083 + '118O' // 266c #3082 + '118J' // 266d #3077 + '118K' // 266e #3078 + '118M' // 266f #3080 + 'a264Y' // 2670-2671 #6888 + 'h50B' // 2672-267a #1301 + '74S' // 267b #1942 + 'a50B' // 267c-267d #1301 + '17N' // 267e #455 + '4M' // 267f #116 + 'oF' // 2680-268f #5 + 'a2M' // 2690-2691 #64 + '31Z' // 2692 #831 + '27M' // 2693 #714 + '31Z' // 2694 #831 + '73T' // 2695 #1917 + '75G' // 2696 #1956 + '31Z' // 2697 #831 + '2M' // 2698 #64 + '31Z' // 2699 #831 + '2M' // 269a #64 + 'a17N' // 269b-269c #455 + '2M' // 269d #64 + 'aF' // 269e-269f #5 + '74H' // 26a0 #1931 + '5Z' // 26a1 #155 + 'd2M' // 26a2-26a6 #64 + '73L' // 26a7 #1909 + 'a2M' // 26a8-26a9 #64 + 'a4M' // 26aa-26ab #116 + 'F' // 26ac #5 + 'b2M' // 26ad-26af #64 + 'a31Z' // 26b0-26b1 #831 + 'j2M' // 26b2-26bc #64 + 'a75O' // 26bd-26be #1964 + 'dF' // 26bf-26c3 #5 + 'a5Z' // 26c4-26c5 #155 + 'aF' // 26c6-26c7 #5 + '5Z' // 26c8 #155 + 'dF' // 26c9-26cd #5 + '17N' // 26ce #455 + '2E' // 26cf #56 + 'F' // 26d0 #5 + '2E' // 26d1 #56 + 'F' // 26d2 #5 + '75J' // 26d3 #1959 + '4M' // 26d4 #116 + 'lF' // 26d5-26e1 #5 + 'f2M' // 26e2-26e8 #64 + 'a27M' // 26e9-26ea #714 + 'd2M' // 26eb-26ef #64 + '75W' // 26f0 #1972 + 'a27M' // 26f1-26f2 #714 + '51S' // 26f3 #1344 + 'a27M' // 26f4-26f5 #714 + '2M' // 26f6 #64 + '51Y' // 26f7 #1350 + '51S' // 26f8 #1344 + '51Y' // 26f9 #1350 + '27M' // 26fa #714 + 'a2M' // 26fb-26fc #64 + '27M' // 26fd #714 + 'a2M' // 26fe-26ff #64 + 'aF' // 2700-2701 #5 + '75K' // 2702 #1960 + 'aF' // 2703-2704 #5 + '3S' // 2705 #96 + 'aF' // 2706-2707 #5 + '51T' // 2708 #1345 + '2E' // 2709 #56 + '3O' // 270a #92 + 'b17O' // 270b-270d #456 + 'F' // 270e #5 + '2E' // 270f #56 + 'aF' // 2710-2711 #5 + '2E' // 2712 #56 + '78C' // 2713 #2030 + '4M' // 2714 #116 + 'F' // 2715 #5 + '4M' // 2716 #116 + 'bF' // 2717-2719 #5 + '52D' // 271a #1355 + 'aF' // 271b-271c #5 + '17N' // 271d #455 + 'b2M' // 271e-2720 #64 + '17N' // 2721 #455 + 'eF' // 2722-2727 #5 + '27N' // 2728 #715 + 'iF' // 2729-2732 #5 + 'a4M' // 2733-2734 #116 + 'gF' // 2735-273c #5 + '52D' // 273d #1355 + 'F' // 273e #5 + '78B' // 273f #2029 + '77P' // 2740 #2017 + 'bF' // 2741-2743 #5 + '5Z' // 2744 #155 + 'aF' // 2745-2746 #5 + '4M' // 2747 #116 + 'cF' // 2748-274b #5 + '3S' // 274c #96 + 'F' // 274d #5 + '3S' // 274e #96 + 'cF' // 274f-2752 #5 + '4M' // 2753 #116 + 'a3S' // 2754-2755 #96 + '77O' // 2756 #2016 + '4M' // 2757 #116 + 'jF' // 2758-2762 #5 + '17O' // 2763 #456 + '73Q' // 2764 #1914 + 'pF' // 2765-2775 #5 + 'h62O' // 2776-277e #1626 + '50A' // 277f #1300 + 'b118I' // 2780-2782 #3076 + 'f50B' // 2783-2789 #1301 + 'b62O' // 278a-278c #1626 + '50A' // 278d #1300 + '251I' // 278e #6534 + 'd50A' // 278f-2793 #1300 + 'F' // 2794 #5 + 'b3S' // 2795-2797 #96 + 'hF' // 2798-27a0 #5 + '73W' // 27a1 #1920 + 'mF' // 27a2-27af #5 + '3S' // 27b0 #96 + 'mF' // 27b1-27be #5 + '3S' // 27bf #96 + '2kM' // 27c0-27ff #12 + '9u76B' // 2800-28ff #1977 + '1fM' // 2900-2920 #12 + 'a31W' // 2921-2922 #828 + 'pM' // 2923-2933 #12 + 'a74Q' // 2934-2935 #1940 + '2vM' // 2936-2980 #12 + '32C' // 2981 #834 + '2hM' // 2982-29be #12 + '76H' // 29bf #1983 + '1pM' // 29c0-29ea #12 + '32C' // 29eb #834 + 'mM' // 29ec-29f9 #12 + 'a251L' // 29fa-29fb #6537 + '9yM' // 29fc-2aff #12 + 'dF' // 2b00-2b04 #5 + 'b74C' // 2b05-2b07 #1926 + 'eF' // 2b08-2b0d #5 + 'cM' // 2b0e-2b11 #12 + 'gF' // 2b12-2b19 #5 + '42Q' // 2b1a #1108 + '73P' // 2b1b #1913 + '4M' // 2b1c #116 + 'rF' // 2b1d-2b2f #5 + '1bM' // 2b30-2b4c #12 + 'bF' // 2b4d-2b4f #5 + '36U' // 2b50 #956 + 'cF' // 2b51-2b54 #5 + '4M' // 2b55 #116 + '1cF' // 2b56-2b73 #5 + 'aA' // 2b74-2b75 + '1dF' // 2b76-2b94 #5 + '42Q' // 2b95 #1108 + 'A' // 2b96 + '3xF' // 2b97-2bfd #5 + 'M' // 2bfe #12 + 'F' // 2bff #5 + '1t27I' // 2c00-2c2e #710 + 'A' // 2c2f + '1t27I' // 2c30-2c5e #710 + 'A' // 2c5f + '1eE' // 2c60-2c7f #4 + '4k50R' // 2c80-2cf3 #1317 + 'dA' // 2cf4-2cf8 + 'f50R' // 2cf9-2cff #1317 + '1k19P' // 2d00-2d25 #509 + 'A' // 2d26 + '19P' // 2d27 #509 + 'dA' // 2d28-2d2c + '19P' // 2d2d #509 + 'aA' // 2d2e-2d2f + '2c51G' // 2d30-2d67 #1332 + 'fA' // 2d68-2d6e + 'a51G' // 2d6f-2d70 #1332 + 'mA' // 2d71-2d7e + '51G' // 2d7f #1332 + 'v3R' // 2d80-2d96 #95 + 'hA' // 2d97-2d9f + 'f3R' // 2da0-2da6 #95 + 'A' // 2da7 + 'f3R' // 2da8-2dae #95 + 'A' // 2daf + 'f3R' // 2db0-2db6 #95 + 'A' // 2db7 + 'f3R' // 2db8-2dbe #95 + 'A' // 2dbf + 'f3R' // 2dc0-2dc6 #95 + 'A' // 2dc7 + 'f3R' // 2dc8-2dce #95 + 'A' // 2dcf + 'f3R' // 2dd0-2dd6 #95 + 'A' // 2dd7 + 'f3R' // 2dd8-2dde #95 + 'A' // 2ddf + '2bE' // 2de0-2e16 #4 + '17K' // 2e17 #452 + 'cE' // 2e18-2e1b #4 + 'a262M' // 2e1c-2e1d #6824 + 'iE' // 2e1e-2e27 #4 + 'a71H' // 2e28-2e29 #1853 + 'eE' // 2e2a-2e2f #4 + 'a261P' // 2e30-2e31 #6801 + 'E' // 2e32 #4 + 'a17K' // 2e33-2e34 #452 + 'dE' // 2e35-2e39 #4 + 'a251K' // 2e3a-2e3b #6536 + 'dE' // 2e3c-2e40 #4 + '71I' // 2e41 #1854 + '1aE' // 2e42-2e5d #4 + '1gA' // 2e5e-2e7f + 'y21M' // 2e80-2e99 #558 + 'A' // 2e9a + '1e21M' // 2e9b-2eba #558 + '251J' // 2ebb #6535 + '2c21M' // 2ebc-2ef3 #558 + 'kA' // 2ef4-2eff + '62N' // 2f00 #1625 + 'b21M' // 2f01-2f03 #558 + '41Q' // 2f04 #1082 + '21M' // 2f05 #558 + '41Q' // 2f06 #1082 + '21M' // 2f07 #558 + '62N' // 2f08 #1625 + '21M' // 2f09 #558 + 'a41Q' // 2f0a-2f0b #1082 + 'd21M' // 2f0c-2f10 #558 + '41Q' // 2f11 #1082 + '118H' // 2f12 #3075 + '21M' // 2f13 #558 + '6O' // 2f14 #170 + 'a4I' // 2f15-2f16 #112 + 'a6O' // 2f17-2f18 #170 + 'b4I' // 2f19-2f1b #112 + 'a6O' // 2f1c-2f1d #170 + '4I' // 2f1e #112 + 'a6O' // 2f1f-2f20 #170 + 'a4I' // 2f21-2f22 #112 + '6O' // 2f23 #170 + '48Q' // 2f24 #1264 + 'a6O' // 2f25-2f26 #170 + '4I' // 2f27 #112 + '6O' // 2f28 #170 + '118G' // 2f29 #3074 + '4I' // 2f2a #112 + '6O' // 2f2b #170 + '4I' // 2f2c #112 + '49Z' // 2f2d #1299 + '4I' // 2f2e #112 + '48Q' // 2f2f #1264 + '49Z' // 2f30 #1299 + 'a6O' // 2f31-2f32 #170 + 'd4I' // 2f33-2f37 #112 + '6O' // 2f38 #170 + 'b4I' // 2f39-2f3b #112 + '48Q' // 2f3c #1264 + 'a6O' // 2f3d-2f3e #170 + '62J' // 2f3f #1621 + '6O' // 2f40 #170 + '4I' // 2f41 #112 + '62J' // 2f42 #1621 + 'a6O' // 2f43-2f44 #170 + '62I' // 2f45 #1620 + 'f6O' // 2f46-2f4c #170 + 'a4I' // 2f4d-2f4e #112 + 'c6O' // 2f4f-2f52 #170 + '4I' // 2f53 #112 + 'd6O' // 2f54-2f58 #170 + '4I' // 2f59 #112 + 'h6O' // 2f5a-2f62 #170 + 'a62I' // 2f63-2f64 #1620 + 'a6O' // 2f65-2f66 #170 + 'a4I' // 2f67-2f68 #112 + 'g6O' // 2f69-2f70 #170 + '4I' // 2f71 #112 + 'a6O' // 2f72-2f73 #170 + '49Z' // 2f74 #1299 + 'a6O' // 2f75-2f76 #170 + '4I' // 2f77 #112 + '6O' // 2f78 #170 + '4I' // 2f79 #112 + 'b6O' // 2f7a-2f7c #170 + '14S' // 2f7d #382 + '4I' // 2f7e #112 + 'c14S' // 2f7f-2f82 #382 + '48N' // 2f83 #1261 + 'g14S' // 2f84-2f8b #382 + 'a4I' // 2f8c-2f8d #112 + '14S' // 2f8e #382 + '48N' // 2f8f #1261 + '14S' // 2f90 #382 + '4I' // 2f91 #112 + 'e14S' // 2f92-2f97 #382 + '4I' // 2f98 #112 + 'g14S' // 2f99-2fa0 #382 + '4I' // 2fa1 #112 + 'a14S' // 2fa2-2fa3 #382 + '4I' // 2fa4 #112 + 'd14S' // 2fa5-2fa9 #382 + 'a4I' // 2faa-2fab #112 + 'e14S' // 2fac-2fb1 #382 + '4I' // 2fb2 #112 + 'h14S' // 2fb3-2fbb #382 + '251G' // 2fbc #6532 + 'c4I' // 2fbd-2fc0 #112 + 'i14S' // 2fc1-2fca #382 + 'a4I' // 2fcb-2fcc #112 + 'g14S' // 2fcd-2fd4 #382 + '4I' // 2fd5 #112 + 'yA' // 2fd6-2fef + 'k4I' // 2ff0-2ffb #112 + 'cA' // 2ffc-2fff + '239K' // 3000 #6224 + '247L' // 3001 #6433 + '247M' // 3002 #6434 + '117T' // 3003 #3061 + '48O' // 3004 #1262 + '118C' // 3005 #3070 + '117U' // 3006 #3062 + '117Z' // 3007 #3067 + '182B' // 3008 #4733 + '182C' // 3009 #4734 + '233T' // 300a #6077 + '233U' // 300b #6078 + 'a245R' // 300c-300d #6387 + '214B' // 300e #5565 + '206I' // 300f #5364 + 'a239J' // 3010-3011 #6223 + '118A' // 3012 #3068 + '48N' // 3013 #1261 + 'a154W' // 3014-3015 #4026 + 'a117N' // 3016-3017 #3055 + 'a117P' // 3018-3019 #3057 + 'a251H' // 301a-301b #6533 + '126G' // 301c #3282 + '124U' // 301d #3244 + '129S' // 301e #3372 + '251W' // 301f #6548 + '117O' // 3020 #3056 + '62K' // 3021 #1622 + '48O' // 3022 #1262 + 'a62K' // 3023-3024 #1622 + '48O' // 3025 #1262 + 'a4I' // 3026-3027 #112 + 'g13J' // 3028-302f #347 + '74O' // 3030 #1938 + 'b13J' // 3031-3033 #347 + 'a251F' // 3034-3035 #6531 + '49X' // 3036 #1297 + 'e13J' // 3037-303c #347 + '74P' // 303d #1939 + 'a13J' // 303e-303f #347 + 'A' // 3040 + '35P' // 3041 #925 + '7C' // 3042 #184 + '35P' // 3043 #925 + '49E' // 3044 #1278 + '35P' // 3045 #925 + '17H' // 3046 #449 + '35P' // 3047 #925 + '21E' // 3048 #550 + '35P' // 3049 #925 + '7C' // 304a #184 + '14P' // 304b #379 + 'a17H' // 304c-304d #449 + '7B' // 304e #183 + '17H' // 304f #449 + '7B' // 3050 #183 + '143R' // 3051 #3735 + '118E' // 3052 #3072 + '7C' // 3053 #184 + '26X' // 3054 #699 + '17H' // 3055 #449 + '17G' // 3056 #448 + '14P' // 3057 #379 + '131I' // 3058 #3414 + '17H' // 3059 #449 + '7B' // 305a #183 + '26Y' // 305b #700 + '15Y' // 305c #414 + '26Y' // 305d #700 + '17G' // 305e #448 + '14P' // 305f #379 + '16A' // 3060 #416 + '7C' // 3061 #184 + '15Y' // 3062 #414 + '17H' // 3063 #449 + '16A' // 3064 #416 + '7B' // 3065 #183 + 'b17H' // 3066-3068 #449 + '26Y' // 3069 #700 + 'a14P' // 306a-306b #379 + '17G' // 306c #448 + '118F' // 306d #3073 + '68A' // 306e #1768 + '17H' // 306f #449 + 'c7B' // 3070-3073 #183 + '17G' // 3074 #448 + 'a7B' // 3075-3076 #183 + '17G' // 3077 #448 + 'a7B' // 3078-3079 #183 + '15Y' // 307a #414 + '7B' // 307b #183 + '15Y' // 307c #414 + '17G' // 307d #448 + '14P' // 307e #379 + '7C' // 307f #184 + '118D' // 3080 #3071 + '149Q' // 3081 #3890 + '7C' // 3082 #184 + 'a21E' // 3083-3084 #550 + '7B' // 3085 #183 + '26X' // 3086 #699 + '7B' // 3087 #183 + '16A' // 3088 #416 + '17H' // 3089 #449 + 'a14P' // 308a-308b #379 + '7C' // 308c #184 + '26X' // 308d #699 + '15Y' // 308e #414 + '21E' // 308f #550 + '62L' // 3090 #1623 + '15Y' // 3091 #414 + '7C' // 3092 #184 + '14P' // 3093 #379 + 'b13J' // 3094-3096 #347 + 'aA' // 3097-3098 + 'a48P' // 3099-309a #1263 + '117R' // 309b #3059 + '117X' // 309c #3065 + 'a48P' // 309d-309e #1263 + 'a13J' // 309f-30a0 #347 + '7B' // 30a1 #183 + '14P' // 30a2 #379 + '16A' // 30a3 #416 + '49E' // 30a4 #1278 + '15Y' // 30a5 #414 + '16A' // 30a6 #416 + '26Y' // 30a7 #700 + '7C' // 30a8 #184 + '7B' // 30a9 #183 + '16A' // 30aa #416 + '7C' // 30ab #184 + '26X' // 30ac #699 + '7C' // 30ad #184 + '7B' // 30ae #183 + '14P' // 30af #379 + '7C' // 30b0 #184 + 'a7B' // 30b1-30b2 #183 + '7C' // 30b3 #184 + '7B' // 30b4 #183 + '16A' // 30b5 #416 + '7B' // 30b6 #183 + '7C' // 30b7 #184 + '17H' // 30b8 #449 + '14P' // 30b9 #379 + '26Y' // 30ba #700 + '26X' // 30bb #699 + '17G' // 30bc #448 + '7B' // 30bd #183 + '17G' // 30be #448 + '17H' // 30bf #449 + '21E' // 30c0 #550 + '7C' // 30c1 #184 + '15Y' // 30c2 #414 + '14P' // 30c3 #379 + '26X' // 30c4 #699 + '15Y' // 30c5 #414 + '7C' // 30c6 #184 + '16A' // 30c7 #416 + '14P' // 30c8 #379 + '7C' // 30c9 #184 + 'a16A' // 30ca-30cb #416 + '17G' // 30cc #448 + 'a26X' // 30cd-30ce #699 + '26Y' // 30cf #700 + '16A' // 30d0 #416 + '21E' // 30d1 #550 + '7B' // 30d2 #183 + '21E' // 30d3 #550 + '7B' // 30d4 #183 + '7C' // 30d5 #184 + '21E' // 30d6 #550 + '7C' // 30d7 #184 + '17G' // 30d8 #448 + 'd7B' // 30d9-30dd #183 + '7C' // 30de #184 + '21E' // 30df #550 + 'a7C' // 30e0-30e1 #184 + '137L' // 30e2 #3573 + '16A' // 30e3 #416 + '7B' // 30e4 #183 + '16A' // 30e5 #416 + '7B' // 30e6 #183 + '21E' // 30e7 #550 + '17G' // 30e8 #448 + 'b14P' // 30e9-30eb #379 + 'a7C' // 30ec-30ed #184 + '117G' // 30ee #3048 + '26Y' // 30ef #700 + '15Y' // 30f0 #414 + '62L' // 30f1 #1623 + '15Y' // 30f2 #414 + '68A' // 30f3 #1768 + '15Y' // 30f4 #414 + '117Y' // 30f5 #3066 + '118B' // 30f6 #3069 + 'c13J' // 30f7-30fa #347 + '216T' // 30fb #5635 + '49E' // 30fc #1278 + 'a48P' // 30fd-30fe #1263 + '13J' // 30ff #347 + 'dA' // 3100-3104 + 'a31I' // 3105-3106 #814 + '63K' // 3107 #1648 + '31I' // 3108 #814 + '141M' // 3109 #3678 + 'a31I' // 310a-310b #814 + '63K' // 310c #1648 + 'e31I' // 310d-3112 #814 + 'a41E' // 3113-3114 #1070 + 'b31I' // 3115-3117 #814 + '41E' // 3118 #1070 + '31I' // 3119 #814 + 'l41E' // 311a-3126 #1070 + '245Z' // 3127 #6395 + '41E' // 3128 #1070 + '245Y' // 3129 #6394 + 'e13J' // 312a-312f #347 + 'A' // 3130 + '26U' // 3131 #696 + '49Y' // 3132 #1298 + '49X' // 3133 #1297 + '26U' // 3134 #696 + '49X' // 3135 #1297 + '13K' // 3136 #348 + '48L' // 3137 #1259 + '13K' // 3138 #348 + '26U' // 3139 #696 + 'f13K' // 313a-3140 #348 + 'a26U' // 3141-3142 #696 + 'a13K' // 3143-3144 #348 + '48L' // 3145 #1259 + '49Y' // 3146 #1298 + '62G' // 3147 #1618 + '26U' // 3148 #696 + '49Y' // 3149 #1298 + '70Q' // 314a #1836 + '117M' // 314b #3054 + '70Q' // 314c #1836 + '117J' // 314d #3051 + '117L' // 314e #3053 + '117I' // 314f #3050 + '13K' // 3150 #348 + '70P' // 3151 #1835 + '13K' // 3152 #348 + '117H' // 3153 #3049 + 'b13K' // 3154-3156 #348 + '70P' // 3157 #1835 + 'c13K' // 3158-315b #348 + '48L' // 315c #1259 + 'b13K' // 315d-315f #348 + '117K' // 3160 #3052 + '26U' // 3161 #696 + '13K' // 3162 #348 + '26U' // 3163 #696 + '261E' // 3164 #6790 + '1a13K' // 3165-3180 #348 + '117F' // 3181 #3047 + 'c13K' // 3182-3185 #348 + '251E' // 3186 #6530 + 'e13K' // 3187-318c #348 + '62G' // 318d #1618 + '13K' // 318e #348 + 'A' // 318f + 'a13J' // 3190-3191 #347 + '48M' // 3192 #1260 + '62H' // 3193 #1619 + 'a13J' // 3194-3195 #347 + 'b48M' // 3196-3198 #1260 + 'c13J' // 3199-319c #347 + '62H' // 319d #1619 + 'a48M' // 319e-319f #1260 + '1a13J' // 31a0-31bb #347 + 'cA' // 31bc-31bf + 'g13J' // 31c0-31c7 #347 + '1a12M' // 31c8-31e3 #324 + 'kA' // 31e4-31ef + 'o12M' // 31f0-31ff #324 + '1a17J' // 3200-321b #451 + '251C' // 321c #6528 + 'a12M' // 321d-321e #324 + 'A' // 321f + 'f62F' // 3220-3226 #1617 + 'b251D' // 3227-3229 #6529 + 'f12M' // 322a-3230 #324 + '117S' // 3231 #3060 + 'f12M' // 3232-3238 #324 + '17J' // 3239 #451 + 'v12M' // 323a-3250 #324 + 'i17J' // 3251-325a #451 + 'd12M' // 325b-325f #324 + 'g17J' // 3260-3267 #451 + '117D' // 3268 #3045 + 'r17J' // 3269-327b #451 + 'a12M' // 327c-327d #324 + 'a17J' // 327e-327f #451 + '12M' // 3280 #324 + '31H' // 3281 #813 + 'g12M' // 3282-3289 #324 + '17J' // 328a #451 + '117E' // 328b #3046 + 'd17J' // 328c-3290 #451 + 'a31H' // 3291-3292 #813 + '12M' // 3293 #324 + '17J' // 3294 #451 + 'a31H' // 3295-3296 #813 + '51O' // 3297 #1340 + '12M' // 3298 #324 + '51O' // 3299 #1340 + 'b12M' // 329a-329c #324 + '31H' // 329d #813 + '17J' // 329e #451 + '31H' // 329f #813 + 'b12M' // 32a0-32a2 #324 + '62F' // 32a3 #1617 + '31H' // 32a4 #813 + '17J' // 32a5 #451 + 'n12M' // 32a6-32b4 #324 + '1f27D' // 32b5-32d5 #705 + '35O' // 32d6 #924 + 'i27D' // 32d7-32e0 #705 + '35O' // 32e1 #924 + '1w27D' // 32e2-3313 #705 + '35O' // 3314 #924 + 'l27D' // 3315-3321 #705 + '35O' // 3322 #924 + 'h27D' // 3323-332b #705 + 'A' // 332c + '3c27D' // 332d-337e #705 + '35O' // 337f #924 + 'd70O' // 3380-3384 #1834 + 'b27D' // 3385-3387 #705 + 'c70O' // 3388-338b #1834 + 'a70N' // 338c-338d #1833 + 'a117C' // 338e-338f #3044 + 'd70N' // 3390-3394 #1833 + '251B' // 3395 #6527 + 'e21L' // 3396-339b #557 + '117Q' // 339c #3058 + '117W' // 339d #3064 + '116Z' // 339e #3041 + 'a21L' // 339f-33a0 #557 + '117V' // 33a1 #3063 + '1g21L' // 33a2-33c3 #557 + '116Y' // 33c4 #3040 + 'f21L' // 33c5-33cb #557 + 'bT' // 33cc-33ce #19 + 'a21L' // 33cf-33d0 #557 + 'a117B' // 33d1-33d2 #3043 + '21L' // 33d3 #557 + 'T' // 33d4 #19 + '70M' // 33d5 #1832 + '21L' // 33d6 #557 + 'T' // 33d7 #19 + '21L' // 33d8 #557 + 'aT' // 33d9-33da #19 + 'b21L' // 33db-33dd #557 + '1gT' // 33de-33ff #19 + 'aA' // 3400-3401 + 'T' // 3402 #19 + 'aA' // 3403-3404 + 'aT' // 3405-3406 #19 + '1eA' // 3407-3426 + 'T' // 3427 #19 + 'cA' // 3428-342b + 'T' // 342c #19 + 'A' // 342d + 'T' // 342e #19 + 'dA' // 342f-3433 + '7J' // 3434 #191 + '7A' // 3435 #182 + 'iA' // 3436-343f + '62M' // 3440 #1624 + 'fA' // 3441-3447 + 'a3G' // 3448-3449 #84 + '7A' // 344a #182 + 'A' // 344b + '7A' // 344c #182 + 'vA' // 344d-3463 + '7A' // 3464 #182 + 'bA' // 3465-3467 + 'T' // 3468 #19 + 'A' // 3469 + 'T' // 346a #19 + 'gA' // 346b-3472 + '7A' // 3473 #182 + 'eA' // 3474-3479 + '7A' // 347a #182 + 'aA' // 347b-347c + 'a7A' // 347d-347e #182 + 'hA' // 347f-3487 + 'T' // 3488 #19 + 'hA' // 3489-3491 + 'T' // 3492 #19 + '7A' // 3493 #182 + 'aA' // 3494-3495 + '7A' // 3496 #182 + 'mA' // 3497-34a4 + '7A' // 34a5 #182 + 'hA' // 34a6-34ae + '7A' // 34af #182 + 'dA' // 34b0-34b4 + 'T' // 34b5 #19 + 'eA' // 34b6-34bb + '62E' // 34bc #1616 + 'cA' // 34bd-34c0 + '62E' // 34c1 #1616 + 'dA' // 34c2-34c6 + 'T' // 34c7 #19 + '7A' // 34c8 #182 + 'qA' // 34c9-34da + 'T' // 34db #19 + '7J' // 34dc #191 + 'aA' // 34dd-34de + '7A' // 34df #182 + 'cA' // 34e0-34e3 + '7A' // 34e4 #182 + 'A' // 34e5 + '7A' // 34e6 #182 + 'fA' // 34e7-34ed + '7J' // 34ee #191 + 'kA' // 34ef-34fa + '7A' // 34fb #182 + 'iA' // 34fc-3505 + '7A' // 3506 #182 + 'wA' // 3507-351e + 'T' // 351f #19 + '1cA' // 3520-353d + '117A' // 353e #3042 + 'qA' // 353f-3550 + '62M' // 3551 #1624 + 'A' // 3552 + '7A' // 3553 #182 + 'dA' // 3554-3558 + '7A' // 3559 #182 + 'bA' // 355a-355c + '70M' // 355d #1832 + 'T' // 355e #19 + 'aA' // 355f-3560 + '7A' // 3561 #182 + 'A' // 3562 + 'T' // 3563 #19 + 'aA' // 3564-3565 + '7J' // 3566 #191 + 'eA' // 3567-356c + '7A' // 356d #182 + 'T' // 356e #19 + 'A' // 356f + '7A' // 3570 #182 + 'A' // 3571 + 'I' // 3572 #8 + 'aA' // 3573-3574 + '7J' // 3575 #191 + 'A' // 3576 + 'aI' // 3577-3578 #8 + 'jA' // 3579-3583 + 'I' // 3584 #8 + 'lA' // 3585-3591 + '7J' // 3592 #191 + 'cA' // 3593-3596 + 'aI' // 3597-3598 #8 + 'fA' // 3599-359f + '7J' // 35a0 #191 + '116X' // 35a1 #3039 + 'bA' // 35a2-35a4 + 'I' // 35a5 #8 + 'T' // 35a6 #19 + 'A' // 35a7 + 'T' // 35a8 #19 + 'cA' // 35a9-35ac + '62D' // 35ad #1615 + 'pA' // 35ae-35be + 'I' // 35bf #8 + 'A' // 35c0 + 'I' // 35c1 #8 + 'bA' // 35c2-35c4 + '26T' // 35c5 #695 + 'A' // 35c6 + 'I' // 35c7 #8 + 'aA' // 35c8-35c9 + 'I' // 35ca #8 + 'bA' // 35cb-35cd + '62D' // 35ce #1615 + 'bA' // 35cf-35d1 + 'I' // 35d2 #8 + 'bA' // 35d3-35d5 + 'I' // 35d6 #8 + 'bA' // 35d7-35d9 + 'T' // 35da #19 + 'I' // 35db #8 + 'A' // 35dc + 'I' // 35dd #8 + 'T' // 35de #19 + 'qA' // 35df-35f0 + 'bI' // 35f1-35f3 #8 + 'T' // 35f4 #19 + 'eA' // 35f5-35fa + 'I' // 35fb #8 + 'aA' // 35fc-35fd + 'I' // 35fe #8 + 'eA' // 35ff-3604 + 'T' // 3605 #19 + 'bA' // 3606-3608 + 'I' // 3609 #8 + 'iA' // 360a-3613 + 'T' // 3614 #19 + 'bA' // 3615-3617 + 'I' // 3618 #8 + 'A' // 3619 + 'I' // 361a #8 + 'gA' // 361b-3622 + 'I' // 3623 #8 + 'A' // 3624 + 'I' // 3625 #8 + 'fA' // 3626-362c + 'I' // 362d #8 + 'fA' // 362e-3634 + 'I' // 3635 #8 + 'bA' // 3636-3638 + 'I' // 3639 #8 + 'cA' // 363a-363d + 'I' // 363e #8 + 'gA' // 363f-3646 + 'bI' // 3647-3649 #8 + 'T' // 364a #19 + 'bA' // 364b-364d + 'I' // 364e #8 + 'oA' // 364f-365e + 'I' // 365f #8 + 'A' // 3660 + 'I' // 3661 #8 + 'wA' // 3662-3679 + 'I' // 367a #8 + 'eA' // 367b-3680 + 'I' // 3681 #8 + 'nA' // 3682-3690 + 'T' // 3691 #19 + 'cA' // 3692-3695 + 'T' // 3696 #19 + 'aA' // 3697-3698 + 'T' // 3699 #19 + 'I' // 369a #8 + 'fA' // 369b-36a1 + '7J' // 36a2 #191 + 'aA' // 36a3-36a4 + 'I' // 36a5 #8 + 'cA' // 36a6-36a9 + 'I' // 36aa #8 + '7J' // 36ab #191 + '35N' // 36ac #923 + 'bA' // 36ad-36af + 'aI' // 36b0-36b1 #8 + 'bA' // 36b2-36b4 + 'I' // 36b5 #8 + 'bA' // 36b6-36b8 + 'I' // 36b9 #8 + 'aA' // 36ba-36bb + 'I' // 36bc #8 + 'cA' // 36bd-36c0 + 'I' // 36c1 #8 + 'A' // 36c2 + 'bI' // 36c3-36c5 #8 + 'A' // 36c6 + 'aI' // 36c7-36c8 #8 + 'eA' // 36c9-36ce + 'T' // 36cf #19 + 'bA' // 36d0-36d2 + 'aI' // 36d3-36d4 #8 + 'A' // 36d5 + 'I' // 36d6 #8 + 'eA' // 36d7-36dc + 'I' // 36dd #8 + 'bA' // 36de-36e0 + 'aI' // 36e1-36e2 #8 + 'aA' // 36e3-36e4 + 'aI' // 36e5-36e6 #8 + 'mA' // 36e7-36f4 + 'I' // 36f5 #8 + 'jA' // 36f6-3700 + 'I' // 3701 #8 + 'A' // 3702 + 'I' // 3703 #8 + 'cA' // 3704-3707 + 'I' // 3708 #8 + 'A' // 3709 + 'I' // 370a #8 + 'aA' // 370b-370c + 'I' // 370d #8 + 'mA' // 370e-371b + 'I' // 371c #8 + 'dA' // 371d-3721 + 'aI' // 3722-3723 #8 + 'A' // 3724 + 'I' // 3725 #8 + 'eA' // 3726-372b + 'aI' // 372c-372d #8 + 'aA' // 372e-372f + 'I' // 3730 #8 + 'A' // 3731 + 'aI' // 3732-3733 #8 + 'eA' // 3734-3739 + '35N' // 373a #923 + 'dA' // 373b-373f + 'I' // 3740 #8 + 'aA' // 3741-3742 + 'I' // 3743 #8 + '1bA' // 3744-3760 + 'T' // 3761 #19 + '26T' // 3762 #695 + 'gA' // 3763-376a + 'aT' // 376b-376c #19 + 'aA' // 376d-376e + 'I' // 376f #8 + 'dA' // 3770-3774 + 'T' // 3775 #19 + 'vA' // 3776-378c + 'T' // 378d #19 + 'hA' // 378e-3796 + 'I' // 3797 #8 + 'gA' // 3798-379f + 'I' // 37a0 #8 + 'wA' // 37a1-37b8 + 'I' // 37b9 #8 + 'cA' // 37ba-37bd + 'I' // 37be #8 + 'aA' // 37bf-37c0 + 'T' // 37c1 #19 + 'sA' // 37c2-37d5 + 'I' // 37d6 #8 + 'jA' // 37d7-37e1 + 'T' // 37e2 #19 + 'dA' // 37e3-37e7 + 'T' // 37e8 #19 + 'hA' // 37e9-37f1 + 'I' // 37f2 #8 + 'A' // 37f3 + 'T' // 37f4 #19 + 'bA' // 37f5-37f7 + 'I' // 37f8 #8 + 'aA' // 37f9-37fa + 'I' // 37fb #8 + 'A' // 37fc + 'T' // 37fd #19 + 'aA' // 37fe-37ff + 'T' // 3800 #19 + 'mA' // 3801-380e + 'I' // 380f #8 + 'hA' // 3810-3818 + 'I' // 3819 #8 + 'eA' // 381a-381f + 'I' // 3820 #8 + 'kA' // 3821-382c + 'I' // 382d #8 + 'A' // 382e + 'T' // 382f #19 + 'eA' // 3830-3835 + '26T' // 3836 #695 + 'A' // 3837 + 'I' // 3838 #8 + 'fA' // 3839-383f + 'T' // 3840 #19 + 'zA' // 3841-385b + 'T' // 385c #19 + 'cA' // 385d-3860 + 'T' // 3861 #19 + 'A' // 3862 + '35N' // 3863 #923 + 'pA' // 3864-3874 + 'I' // 3875 #8 + '1oA' // 3876-389f + 'I' // 38a0 #8 + 'T' // 38a1 #19 + 'eA' // 38a2-38a7 + '7J' // 38a8 #191 + 'cA' // 38a9-38ac + 'T' // 38ad #19 + 'tA' // 38ae-38c2 + 'I' // 38c3 #8 + 'gA' // 38c4-38cb + 'I' // 38cc #8 + 'cA' // 38cd-38d0 + 'I' // 38d1 #8 + 'aA' // 38d2-38d3 + 'I' // 38d4 #8 + '1jA' // 38d5-38f9 + '26T' // 38fa #695 + 'lA' // 38fb-3907 + 'I' // 3908 #8 + 'jA' // 3909-3913 + 'I' // 3914 #8 + 'aA' // 3915-3916 + 'T' // 3917 #19 + 'aA' // 3918-3919 + 'T' // 391a #19 + 'kA' // 391b-3926 + 'I' // 3927 #8 + 'iA' // 3928-3931 + 'I' // 3932 #8 + 'kA' // 3933-393e + 'I' // 393f #8 + 'lA' // 3940-394c + 'I' // 394d #8 + 'tA' // 394e-3962 + 'I' // 3963 #8 + 'jA' // 3964-396e + 'T' // 396f #19 + 'gA' // 3970-3977 + 'I' // 3978 #8 + 'fA' // 3979-397f + 'I' // 3980 #8 + 'gA' // 3981-3988 + 'aI' // 3989-398a #8 + 'fA' // 398b-3991 + 'I' // 3992 #8 + 'eA' // 3993-3998 + 'I' // 3999 #8 + 'A' // 399a + 'I' // 399b #8 + 'dA' // 399c-39a0 + 'I' // 39a1 #8 + 'aA' // 39a2-39a3 + '26T' // 39a4 #695 + 'rA' // 39a5-39b7 + '62C' // 39b8 #1614 + '1hA' // 39b9-39db + 'I' // 39dc #8 + 'dA' // 39dd-39e1 + 'I' // 39e2 #8 + 'aA' // 39e3-39e4 + 'I' // 39e5 #8 + 'eA' // 39e6-39eb + 'I' // 39ec #8 + 'jA' // 39ed-39f7 + 'I' // 39f8 #8 + 'aA' // 39f9-39fa + 'I' // 39fb #8 + 'aA' // 39fc-39fd + 'I' // 39fe #8 + 'aA' // 39ff-3a00 + 'I' // 3a01 #8 + 'A' // 3a02 + 'I' // 3a03 #8 + 'aA' // 3a04-3a05 + 'I' // 3a06 #8 + 'oA' // 3a07-3a16 + '35N' // 3a17 #923 + 'I' // 3a18 #8 + 'oA' // 3a19-3a28 + 'aI' // 3a29-3a2a #8 + 'hA' // 3a2b-3a33 + 'I' // 3a34 #8 + 'uA' // 3a35-3a4a + 'I' // 3a4b #8 + 'eA' // 3a4c-3a51 + '35N' // 3a52 #923 + 'cA' // 3a53-3a56 + 'I' // 3a57 #8 + 'cA' // 3a58-3a5b + '26T' // 3a5c #695 + 'A' // 3a5d + 'I' // 3a5e #8 + 'fA' // 3a5f-3a65 + 'aI' // 3a66-3a67 #8 + 'eA' // 3a68-3a6d + 'T' // 3a6e #19 + 'cA' // 3a6f-3a72 + 'T' // 3a73 #19 + 'pA' // 3a74-3a84 + 'T' // 3a85 #19 + 'pA' // 3a86-3a96 + 'I' // 3a97 #8 + 'rA' // 3a98-3aaa + 'I' // 3aab #8 + 'pA' // 3aac-3abc + 'I' // 3abd #8 + 'eA' // 3abe-3ac3 + 'T' // 3ac4 #19 + 'eA' // 3ac5-3aca + 'T' // 3acb #19 + 'iA' // 3acc-3ad5 + 'aT' // 3ad6-3ad7 #19 + 'eA' // 3ad8-3add + 'I' // 3ade #8 + 'A' // 3adf + 'I' // 3ae0 #8 + 'hA' // 3ae1-3ae9 + 'T' // 3aea #19 + 'dA' // 3aeb-3aef + 'I' // 3af0 #8 + 'A' // 3af1 + 'I' // 3af2 #8 + 'T' // 3af3 #19 + 'A' // 3af4 + 'I' // 3af5 #8 + 'dA' // 3af6-3afa + 'I' // 3afb #8 + 'qA' // 3afc-3b0d + '26T' // 3b0e #695 + 'iA' // 3b0f-3b18 + 'I' // 3b19 #8 + 'T' // 3b1a #19 + 'A' // 3b1b + 'T' // 3b1c #19 + 'dA' // 3b1d-3b21 + '62C' // 3b22 #1614 + 'gA' // 3b23-3b2a + 'I' // 3b2b #8 + 'hA' // 3b2c-3b34 + 'T' // 3b35 #19 + 'bA' // 3b36-3b38 + 'I' // 3b39 #8 + 'gA' // 3b3a-3b41 + 'I' // 3b42 #8 + 'tA' // 3b43-3b57 + 'I' // 3b58 #8 + 'fA' // 3b59-3b5f + 'I' // 3b60 #8 + 'kA' // 3b61-3b6c + 'T' // 3b6d #19 + 'bA' // 3b6e-3b70 + 'aI' // 3b71-3b72 #8 + 'cA' // 3b73-3b76 + 'T' // 3b77 #19 + 'bA' // 3b78-3b7a + 'aI' // 3b7b-3b7c #8 + 'bA' // 3b7d-3b7f + 'I' // 3b80 #8 + 'eA' // 3b81-3b86 + 'aT' // 3b87-3b88 #19 + 'cA' // 3b89-3b8c + 'T' // 3b8d #19 + 'fA' // 3b8e-3b94 + 'aL' // 3b95-3b96 #11 + 'aA' // 3b97-3b98 + 'L' // 3b99 #11 + 'fA' // 3b9a-3ba0 + 'L' // 3ba1 #11 + 'aA' // 3ba2-3ba3 + 'T' // 3ba4 #19 + 'pA' // 3ba5-3bb5 + 'T' // 3bb6 #19 + 'dA' // 3bb7-3bbb + 'L' // 3bbc #11 + 'A' // 3bbd + 'L' // 3bbe #11 + 'bA' // 3bbf-3bc1 + 'L' // 3bc2 #11 + 'T' // 3bc3 #19 + 'L' // 3bc4 #11 + 'gA' // 3bc5-3bcc + 'T' // 3bcd #19 + 'hA' // 3bce-3bd6 + '40W' // 3bd7 #1062 + 'dA' // 3bd8-3bdc + 'L' // 3bdd #11 + 'mA' // 3bde-3beb + 'L' // 3bec #11 + 'bA' // 3bed-3bef + 'T' // 3bf0 #19 + 'A' // 3bf1 + 'L' // 3bf2 #11 + '23V' // 3bf3 #619 + 'L' // 3bf4 #11 + 'wA' // 3bf5-3c0c + 'L' // 3c0d #11 + 'A' // 3c0e + 'T' // 3c0f #19 + 'A' // 3c10 + 'L' // 3c11 #11 + 'bA' // 3c12-3c14 + 'L' // 3c15 #11 + 'aA' // 3c16-3c17 + 'L' // 3c18 #11 + 'lA' // 3c19-3c25 + 'T' // 3c26 #19 + '1rA' // 3c27-3c53 + 'L' // 3c54 #11 + '2aA' // 3c55-3c8a + 'L' // 3c8b #11 + '2bA' // 3c8c-3cc2 + 'T' // 3cc3 #19 + 'fA' // 3cc4-3cca + 'L' // 3ccb #11 + 'A' // 3ccc + 'L' // 3ccd #11 + 'bA' // 3cce-3cd0 + 'L' // 3cd1 #11 + 'T' // 3cd2 #19 + 'bA' // 3cd3-3cd5 + 'L' // 3cd6 #11 + 'dA' // 3cd7-3cdb + 'L' // 3cdc #11 + 'mA' // 3cdd-3cea + 'L' // 3ceb #11 + 'bA' // 3cec-3cee + 'L' // 3cef #11 + '1fA' // 3cf0-3d10 + 'T' // 3d11 #19 + 'aL' // 3d12-3d13 #11 + 'hA' // 3d14-3d1c + 'L' // 3d1d #11 + 'T' // 3d1e #19 + 'qA' // 3d1f-3d30 + 'T' // 3d31 #19 + 'L' // 3d32 #11 + 'gA' // 3d33-3d3a + 'L' // 3d3b #11 + 'iA' // 3d3c-3d45 + 'L' // 3d46 #11 + 'dA' // 3d47-3d4b + 'L' // 3d4c #11 + 'A' // 3d4d + '23V' // 3d4e #619 + 'aA' // 3d4f-3d50 + 'L' // 3d51 #11 + 'lA' // 3d52-3d5e + 'L' // 3d5f #11 + 'aA' // 3d60-3d61 + 'L' // 3d62 #11 + 'A' // 3d63 + 'T' // 3d64 #19 + 'cA' // 3d65-3d68 + 'aL' // 3d69-3d6a #11 + 'cA' // 3d6b-3d6e + 'L' // 3d6f #11 + 'dA' // 3d70-3d74 + 'L' // 3d75 #11 + 'fA' // 3d76-3d7c + 'L' // 3d7d #11 + 'fA' // 3d7e-3d84 + 'L' // 3d85 #11 + 'aA' // 3d86-3d87 + 'L' // 3d88 #11 + 'A' // 3d89 + 'L' // 3d8a #11 + 'cA' // 3d8b-3d8e + 'L' // 3d8f #11 + 'A' // 3d90 + 'L' // 3d91 #11 + 'gA' // 3d92-3d99 + 'T' // 3d9a #19 + 'iA' // 3d9b-3da4 + 'L' // 3da5 #11 + 'dA' // 3da6-3daa + '7J' // 3dab #191 + 'A' // 3dac + 'L' // 3dad #11 + 'eA' // 3dae-3db3 + 'L' // 3db4 #11 + 'iA' // 3db5-3dbe + 'L' // 3dbf #11 + 'T' // 3dc0 #19 + 'dA' // 3dc1-3dc5 + 'aL' // 3dc6-3dc7 #11 + 'A' // 3dc8 + 'L' // 3dc9 #11 + 'aA' // 3dca-3dcb + '23V' // 3dcc #619 + 'L' // 3dcd #11 + 'dA' // 3dce-3dd2 + 'L' // 3dd3 #11 + 'T' // 3dd4 #19 + 'eA' // 3dd5-3dda + '40W' // 3ddb #1062 + 'jA' // 3ddc-3de6 + '62A' // 3de7 #1612 + 'L' // 3de8 #11 + 'aA' // 3de9-3dea + '62A' // 3deb #1612 + 'fA' // 3dec-3df2 + 'aL' // 3df3-3df4 #11 + 'aA' // 3df5-3df6 + 'L' // 3df7 #11 + 'cA' // 3df8-3dfb + 'aL' // 3dfc-3dfd #11 + 'fA' // 3dfe-3e04 + 'T' // 3e05 #19 + 'L' // 3e06 #11 + 'rA' // 3e07-3e19 + '7J' // 3e1a #191 + '1iA' // 3e1b-3e3e + 'T' // 3e3f #19 + '23V' // 3e40 #619 + 'aA' // 3e41-3e42 + 'L' // 3e43 #11 + 'cA' // 3e44-3e47 + 'L' // 3e48 #11 + 'kA' // 3e49-3e54 + 'L' // 3e55 #11 + 'iA' // 3e56-3e5f + 'T' // 3e60 #19 + 'dA' // 3e61-3e65 + 'T' // 3e66 #19 + 'A' // 3e67 + 'T' // 3e68 #19 + 'jA' // 3e69-3e73 + '40W' // 3e74 #1062 + 'mA' // 3e75-3e82 + 'T' // 3e83 #19 + 'eA' // 3e84-3e89 + 'T' // 3e8a #19 + 'hA' // 3e8b-3e93 + 'T' // 3e94 #19 + 'rA' // 3e95-3ea7 + 'bL' // 3ea8-3eaa #11 + 'aA' // 3eab-3eac + 'L' // 3ead #11 + 'bA' // 3eae-3eb0 + 'L' // 3eb1 #11 + 'eA' // 3eb2-3eb7 + 'L' // 3eb8 #11 + 'eA' // 3eb9-3ebe + 'L' // 3ebf #11 + 'aA' // 3ec0-3ec1 + 'L' // 3ec2 #11 + 'cA' // 3ec3-3ec6 + 'L' // 3ec7 #11 + 'aA' // 3ec8-3ec9 + 'L' // 3eca #11 + 'A' // 3ecb + 'L' // 3ecc #11 + 'bA' // 3ecd-3ecf + 'aL' // 3ed0-3ed1 #11 + 'cA' // 3ed2-3ed5 + 'aL' // 3ed6-3ed7 #11 + 'aA' // 3ed8-3ed9 + '23V' // 3eda #619 + 'L' // 3edb #11 + 'aA' // 3edc-3edd + 'L' // 3ede #11 + 'aA' // 3edf-3ee0 + 'aL' // 3ee1-3ee2 #11 + 'cA' // 3ee3-3ee6 + 'L' // 3ee7 #11 + 'A' // 3ee8 + 'L' // 3ee9 #11 + 'A' // 3eea + 'aL' // 3eeb-3eec #11 + 'bA' // 3eed-3eef + 'L' // 3ef0 #11 + 'aA' // 3ef1-3ef2 + 'aL' // 3ef3-3ef4 #11 + 'dA' // 3ef5-3ef9 + 'L' // 3efa #11 + 'A' // 3efb + 'L' // 3efc #11 + 'aA' // 3efd-3efe + 'aL' // 3eff-3f00 #11 + 'bA' // 3f01-3f03 + 'L' // 3f04 #11 + 'A' // 3f05 + 'aL' // 3f06-3f07 #11 + 'eA' // 3f08-3f0d + '40W' // 3f0e #1062 + 'kA' // 3f0f-3f1a + '7J' // 3f1b #191 + '2bA' // 3f1c-3f52 + 'L' // 3f53 #11 + 'bA' // 3f54-3f56 + 'T' // 3f57 #19 + 'aL' // 3f58-3f59 #11 + 'hA' // 3f5a-3f62 + 'L' // 3f63 #11 + 'hA' // 3f64-3f6c + '7J' // 3f6d #191 + 'cA' // 3f6e-3f71 + 'T' // 3f72 #19 + 'aA' // 3f73-3f74 + 'T' // 3f75 #19 + 'A' // 3f76 + 'T' // 3f77 #19 + 'cA' // 3f78-3f7b + 'L' // 3f7c #11 + 'uA' // 3f7d-3f92 + 'L' // 3f93 #11 + 'yA' // 3f94-3fad + 'T' // 3fae #19 + 'aA' // 3faf-3fb0 + 'T' // 3fb1 #19 + 'mA' // 3fb2-3fbf + 'L' // 3fc0 #11 + 'fA' // 3fc1-3fc7 + 'L' // 3fc8 #11 + 'T' // 3fc9 #19 + 'lA' // 3fca-3fd6 + '23V' // 3fd7 #619 + 'cA' // 3fd8-3fdb + '23V' // 3fdc #619 + 'gA' // 3fdd-3fe4 + 'L' // 3fe5 #11 + 'fA' // 3fe6-3fec + 'L' // 3fed #11 + 'jA' // 3fee-3ff8 + 'aL' // 3ff9-3ffa #11 + 'hA' // 3ffb-4003 + 'L' // 4004 #11 + 'cA' // 4005-4008 + 'L' // 4009 #11 + 'rA' // 400a-401c + 'L' // 401d #11 + 'zA' // 401e-4038 + '23V' // 4039 #619 + 'jA' // 403a-4044 + 'L' // 4045 #11 + 'lA' // 4046-4052 + 'L' // 4053 #11 + 'bA' // 4054-4056 + 'L' // 4057 #11 + 'T' // 4058 #19 + 'hA' // 4059-4061 + 'L' // 4062 #11 + 'aA' // 4063-4064 + 'L' // 4065 #11 + 'cA' // 4066-4069 + 'L' // 406a #11 + 'cA' // 406b-406e + 'L' // 406f #11 + 'A' // 4070 + 'L' // 4071 #11 + '1fA' // 4072-4092 + '1B' // 4093 #27 + 'sA' // 4094-40a7 + 'L' // 40a8 #11 + 'jA' // 40a9-40b3 + 'L' // 40b4 #11 + 'eA' // 40b5-40ba + 'L' // 40bb #11 + 'bA' // 40bc-40be + 'L' // 40bf #11 + 'gA' // 40c0-40c7 + 'L' // 40c8 #11 + 'nA' // 40c9-40d7 + 'L' // 40d8 #11 + 'eA' // 40d9-40de + 'L' // 40df #11 + 'wA' // 40e0-40f7 + 'L' // 40f8 #11 + 'A' // 40f9 + 'L' // 40fa #11 + 'fA' // 40fb-4101 + '62B' // 4102 #1613 + '116W' // 4103 #3038 + 'L' // 4104 #11 + '1B' // 4105 #27 + 'bA' // 4106-4108 + 'L' // 4109 #11 + 'cA' // 410a-410d + 'L' // 410e #11 + '1gA' // 410f-4130 + 'aL' // 4131-4132 #11 + 'tA' // 4133-4147 + '1B' // 4148 #27 + 'eA' // 4149-414e + '1B' // 414f #27 + 'rA' // 4150-4162 + '1B' // 4163 #27 + 'bA' // 4164-4166 + 'L' // 4167 #11 + 'cA' // 4168-416b + 'L' // 416c #11 + 'A' // 416d + 'L' // 416e #11 + 'lA' // 416f-417b + 'L' // 417c #11 + 'aA' // 417d-417e + 'L' // 417f #11 + 'A' // 4180 + '62B' // 4181 #1613 + 'mA' // 4182-418f + 'L' // 4190 #11 + '1fA' // 4191-41b1 + 'L' // 41b2 #11 + 'A' // 41b3 + '1B' // 41b4 #27 + 'iA' // 41b5-41be + '1B' // 41bf #27 + 'cA' // 41c0-41c3 + 'L' // 41c4 #11 + 'dA' // 41c5-41c9 + 'L' // 41ca #11 + 'cA' // 41cb-41ce + 'L' // 41cf #11 + 'jA' // 41d0-41da + 'G' // 41db #6 + 'iA' // 41dc-41e5 + '1B' // 41e6 #27 + 'eA' // 41e7-41ec + 'G' // 41ed #6 + '1B' // 41ee #27 + 'G' // 41ef #6 + 'bA' // 41f0-41f2 + '1B' // 41f3 #27 + 'dA' // 41f4-41f8 + 'G' // 41f9 #6 + 'lA' // 41fa-4206 + '1B' // 4207 #27 + 'eA' // 4208-420d + '1B' // 420e #27 + 'aA' // 420f-4210 + 'G' // 4211 #6 + 'pA' // 4212-4222 + 'G' // 4223 #6 + '1aA' // 4224-423f + 'G' // 4240 #6 + '1dA' // 4241-425f + 'G' // 4260 #6 + 'bA' // 4261-4263 + '1B' // 4264 #27 + 'dA' // 4265-4269 + 'G' // 426a #6 + 'jA' // 426b-4275 + 'G' // 4276 #6 + 'bA' // 4277-4279 + 'G' // 427a #6 + 'pA' // 427b-428b + 'G' // 428c #6 + 'eA' // 428d-4292 + '1B' // 4293 #27 + 'G' // 4294 #6 + 'lA' // 4295-42a1 + 'G' // 42a2 #6 + 'qA' // 42a3-42b4 + 'G' // 42b5 #6 + 'bA' // 42b6-42b8 + 'G' // 42b9 #6 + 'aA' // 42ba-42bb + 'G' // 42bc #6 + 'hA' // 42bd-42c5 + '1B' // 42c6 #27 + 'nA' // 42c7-42d5 + '1B' // 42d6 #27 + 'eA' // 42d7-42dc + '1B' // 42dd #27 + 'uA' // 42de-42f3 + 'G' // 42f4 #6 + 'eA' // 42f5-42fa + 'aG' // 42fb-42fc #6 + 'dA' // 42fd-4301 + '1B' // 4302 #27 + 'fA' // 4303-4309 + 'G' // 430a #6 + '1eA' // 430b-432a + '19E' // 432b #498 + 'vA' // 432c-4342 + '1B' // 4343 #27 + '1oA' // 4344-436d + 'G' // 436e #6 + '1mA' // 436f-4396 + 'G' // 4397 #6 + 'aA' // 4398-4399 + 'G' // 439a #6 + '1dA' // 439b-43b9 + 'G' // 43ba #6 + 'eA' // 43bb-43c0 + 'G' // 43c1 #6 + 'vA' // 43c2-43d8 + 'G' // 43d9 #6 + 'dA' // 43da-43de + 'G' // 43df #6 + 'lA' // 43e0-43ec + 'G' // 43ed #6 + '1B' // 43ee #27 + 'A' // 43ef + '19E' // 43f0 #498 + 'A' // 43f1 + 'G' // 43f2 #6 + 'mA' // 43f3-4400 + 'aG' // 4401-4402 #6 + 'dA' // 4403-4407 + '1B' // 4408 #27 + 'bA' // 4409-440b + '1B' // 440c #27 + 'eA' // 440d-4412 + 'G' // 4413 #6 + 'bA' // 4414-4416 + '1B' // 4417 #27 + 'cA' // 4418-441b + '1B' // 441c #27 + 'dA' // 441d-4421 + '1B' // 4422 #27 + 'aA' // 4423-4424 + 'G' // 4425 #6 + 'fA' // 4426-442c + 'G' // 442d #6 + '1jA' // 442e-4452 + '1B' // 4453 #27 + 'fA' // 4454-445a + '1B' // 445b #27 + 'yA' // 445c-4475 + '1B' // 4476 #27 + 'bA' // 4477-4479 + '19E' // 447a #498 + 'sA' // 447b-448e + 'G' // 448f #6 + 'A' // 4490 + '19E' // 4491 #498 + 'bA' // 4492-4494 + '7J' // 4495 #191 + 'hA' // 4496-449e + 'aG' // 449f-44a0 #6 + 'A' // 44a1 + 'G' // 44a2 #6 + 'lA' // 44a3-44af + 'G' // 44b0 #6 + 'aA' // 44b1-44b2 + '1B' // 44b3 #27 + 'bA' // 44b4-44b6 + 'G' // 44b7 #6 + 'dA' // 44b8-44bc + 'G' // 44bd #6 + '1B' // 44be #27 + 'A' // 44bf + 'G' // 44c0 #6 + 'aA' // 44c1-44c2 + 'G' // 44c3 #6 + 'A' // 44c4 + 'G' // 44c5 #6 + 'gA' // 44c6-44cd + 'G' // 44ce #6 + 'dA' // 44cf-44d3 + '1B' // 44d4 #27 + 'gA' // 44d5-44dc + 'bG' // 44dd-44df #6 + 'A' // 44e0 + 'G' // 44e1 #6 + 'aA' // 44e2-44e3 + 'G' // 44e4 #6 + 'cA' // 44e5-44e8 + 'cG' // 44e9-44ec #6 + 'fA' // 44ed-44f3 + 'G' // 44f4 #6 + 'mA' // 44f5-4502 + 'aG' // 4503-4504 #6 + 'bA' // 4505-4507 + '1B' // 4508 #27 + 'G' // 4509 #6 + 'A' // 450a + 'G' // 450b #6 + 'A' // 450c + '1B' // 450d #27 + 'gA' // 450e-4515 + 'G' // 4516 #6 + 'cA' // 4517-451a + 'G' // 451b #6 + 'A' // 451c + 'G' // 451d #6 + 'fA' // 451e-4524 + '1B' // 4525 #27 + 'A' // 4526 + 'G' // 4527 #6 + 'eA' // 4528-452d + 'G' // 452e #6 + 'cA' // 452f-4532 + 'G' // 4533 #6 + 'aA' // 4534-4535 + 'G' // 4536 #6 + 'cA' // 4537-453a + 'G' // 453b #6 + 'A' // 453c + 'G' // 453d #6 + 'A' // 453e + 'G' // 453f #6 + 'bA' // 4540-4542 + '19E' // 4543 #498 + 'lA' // 4544-4550 + 'G' // 4551 #6 + '61Z' // 4552 #1611 + 'aA' // 4553-4554 + 'G' // 4555 #6 + 'aA' // 4556-4557 + 'G' // 4558 #6 + 'bA' // 4559-455b + 'G' // 455c #6 + 'cA' // 455d-4560 + 'aG' // 4561-4562 #6 + 'fA' // 4563-4569 + 'G' // 456a #6 + 'aA' // 456b-456c + 'G' // 456d #6 + 'hA' // 456e-4576 + 'aG' // 4577-4578 #6 + 'A' // 4579 + '1B' // 457a #27 + 'iA' // 457b-4584 + 'G' // 4585 #6 + 'vA' // 4586-459c + '1B' // 459d #27 + 'gA' // 459e-45a5 + 'G' // 45a6 #6 + 'kA' // 45a7-45b2 + 'G' // 45b3 #6 + 'cA' // 45b4-45b7 + '1B' // 45b8 #27 + 'dA' // 45b9-45bd + '1B' // 45be #27 + 'zA' // 45bf-45d9 + 'G' // 45da #6 + 'iA' // 45db-45e4 + '1B' // 45e5 #27 + 'bA' // 45e6-45e8 + 'G' // 45e9 #6 + '19E' // 45ea #498 + 'wA' // 45eb-4602 + 'G' // 4603 #6 + 'aA' // 4604-4605 + 'G' // 4606 #6 + 'gA' // 4607-460e + '19E' // 460f #498 + '1B' // 4610 #27 + 'cA' // 4611-4614 + 'G' // 4615 #6 + 'A' // 4616 + 'G' // 4617 #6 + '1nA' // 4618-4640 + '1B' // 4641 #27 + 'xA' // 4642-465a + 'G' // 465b #6 + 'hA' // 465c-4664 + '1B' // 4665 #27 + 'sA' // 4666-4679 + 'G' // 467a #6 + 'dA' // 467b-467f + 'G' // 4680 #6 + '1eA' // 4681-46a0 + '19E' // 46a1 #498 + 'kA' // 46a2-46ad + '19E' // 46ae #498 + '1B' // 46af #27 + 'jA' // 46b0-46ba + 'G' // 46bb #6 + 'rA' // 46bc-46ce + 'aG' // 46cf-46d0 #6 + '1iA' // 46d1-46f4 + 'G' // 46f5 #6 + 'A' // 46f6 + 'G' // 46f7 #6 + 'sA' // 46f8-470b + '1B' // 470c #27 + 'eA' // 470d-4712 + 'G' // 4713 #6 + 'cA' // 4714-4717 + 'G' // 4718 #6 + 'eA' // 4719-471e + '1B' // 471f #27 + 'bA' // 4720-4722 + '7J' // 4723 #191 + 'qA' // 4724-4735 + 'G' // 4736 #6 + 'lA' // 4737-4743 + 'G' // 4744 #6 + 'hA' // 4745-474d + 'aG' // 474e-474f #6 + 'sA' // 4750-4763 + '1B' // 4764 #27 + 'vA' // 4765-477b + 'G' // 477c #6 + 'zA' // 477d-4797 + 'G' // 4798 #6 + 'lA' // 4799-47a5 + 'G' // 47a6 #6 + '1sA' // 47a7-47d4 + 'G' // 47d5 #6 + 'oA' // 47d6-47e5 + '1B' // 47e6 #27 + 'eA' // 47e7-47ec + 'G' // 47ed #6 + 'eA' // 47ee-47f3 + 'G' // 47f4 #6 + 'gA' // 47f5-47fc + '1B' // 47fd #27 + 'aA' // 47fe-47ff + 'G' // 4800 #6 + 'iA' // 4801-480a + 'G' // 480b #6 + 'iA' // 480c-4815 + '1B' // 4816 #27 + 'fA' // 4817-481d + '1B' // 481e #27 + 'wA' // 481f-4836 + '61Z' // 4837 #1611 + 'kA' // 4838-4843 + '1B' // 4844 #27 + 'hA' // 4845-484d + '1B' // 484e #27 + 'mA' // 484f-485c + 'G' // 485d #6 + 'rA' // 485e-4870 + 'G' // 4871 #6 + '1nA' // 4872-489a + 'G' // 489b #6 + 'pA' // 489c-48ac + 'aG' // 48ad-48ae #6 + 'eA' // 48af-48b4 + '1B' // 48b5 #27 + 'yA' // 48b6-48cf + 'G' // 48d0 #6 + 'kA' // 48d1-48dc + 'G' // 48dd #6 + 'nA' // 48de-48ec + 'G' // 48ed #6 + 'dA' // 48ee-48f2 + 'G' // 48f3 #6 + 'eA' // 48f4-48f9 + '116V' // 48fa #3037 + 'jA' // 48fb-4905 + 'G' // 4906 #6 + 'iA' // 4907-4910 + 'G' // 4911 #6 + 'kA' // 4912-491d + 'G' // 491e #6 + 'eA' // 491f-4924 + 'G' // 4925 #6 + 'cA' // 4926-4929 + 'G' // 492a #6 + 'aA' // 492b-492c + 'G' // 492d #6 + 'A' // 492e + 'aG' // 492f-4930 #6 + 'cA' // 4931-4934 + 'G' // 4935 #6 + 'eA' // 4936-493b + 'G' // 493c #6 + 'A' // 493d + 'G' // 493e #6 + 'eA' // 493f-4944 + 'G' // 4945 #6 + 'jA' // 4946-4950 + 'G' // 4951 #6 + 'A' // 4952 + 'G' // 4953 #6 + 'pA' // 4954-4964 + 'G' // 4965 #6 + 'cA' // 4966-4969 + 'G' // 496a #6 + 'fA' // 496b-4971 + 'G' // 4972 #6 + 'uA' // 4973-4988 + 'G' // 4989 #6 + 'vA' // 498a-49a0 + 'G' // 49a1 #6 + 'dA' // 49a2-49a6 + 'G' // 49a7 #6 + 'gA' // 49a8-49af + '1B' // 49b0 #27 + '1sA' // 49b1-49de + 'G' // 49df #6 + 'dA' // 49e0-49e4 + 'G' // 49e5 #6 + 'A' // 49e6 + '19E' // 49e7 #498 + 'qA' // 49e8-49f9 + '1B' // 49fa #27 + 'hA' // 49fb-4a03 + '1B' // 4a04 #27 + 'iA' // 4a05-4a0e + 'G' // 4a0f #6 + 'lA' // 4a10-4a1c + 'G' // 4a1d #6 + 'eA' // 4a1e-4a23 + 'G' // 4a24 #6 + 'cA' // 4a25-4a28 + '1B' // 4a29 #27 + 'jA' // 4a2a-4a34 + 'G' // 4a35 #6 + '3qA' // 4a36-4a95 + 'G' // 4a96 #6 + 'lA' // 4a97-4aa3 + 'G' // 4aa4 #6 + 'nA' // 4aa5-4ab3 + 'G' // 4ab4 #6 + 'bA' // 4ab5-4ab7 + 'G' // 4ab8 #6 + 'bA' // 4ab9-4abb + '1B' // 4abc #27 + 'sA' // 4abd-4ad0 + 'G' // 4ad1 #6 + 'qA' // 4ad2-4ae3 + '1Y' // 4ae4 #50 + 'yA' // 4ae5-4afe + '1Y' // 4aff #50 + 'oA' // 4b00-4b0f + '1Y' // 4b10 #50 + 'gA' // 4b11-4b18 + '1Y' // 4b19 #50 + 'eA' // 4b1a-4b1f + '1Y' // 4b20 #50 + 'jA' // 4b21-4b2b + '1Y' // 4b2c #50 + 'iA' // 4b2d-4b36 + '1Y' // 4b37 #50 + '1B' // 4b38 #27 + 'aA' // 4b39-4b3a + '1B' // 4b3b #27 + '1xA' // 4b3c-4b6e + 'a1Y' // 4b6f-4b70 #50 + 'A' // 4b71 + '1Y' // 4b72 #50 + 'gA' // 4b73-4b7a + '1Y' // 4b7b #50 + 'aA' // 4b7c-4b7d + '40S' // 4b7e #1058 + 'nA' // 4b7f-4b8d + '1Y' // 4b8e #50 + 'A' // 4b8f + '1Y' // 4b90 #50 + 'aA' // 4b91-4b92 + '1Y' // 4b93 #50 + 'aA' // 4b94-4b95 + 'a1Y' // 4b96-4b97 #50 + 'dA' // 4b98-4b9c + '1Y' // 4b9d #50 + '1dA' // 4b9e-4bbc + 'a1Y' // 4bbd-4bbe #50 + 'A' // 4bbf + '1Y' // 4bc0 #50 + 'A' // 4bc1 + '1B' // 4bc2 #27 + 'fA' // 4bc3-4bc9 + '1B' // 4bca #27 + 'fA' // 4bcb-4bd1 + '1B' // 4bd2 #27 + 'tA' // 4bd3-4be7 + '1B' // 4be8 #27 + 'zA' // 4be9-4c03 + '1Y' // 4c04 #50 + 'aA' // 4c05-4c06 + '1Y' // 4c07 #50 + 'eA' // 4c08-4c0d + '1Y' // 4c0e #50 + 'gA' // 4c0f-4c16 + '1B' // 4c17 #27 + 'gA' // 4c18-4c1f + '1B' // 4c20 #27 + 'pA' // 4c21-4c31 + '1Y' // 4c32 #50 + 'dA' // 4c33-4c37 + '1B' // 4c38 #27 + 'aA' // 4c39-4c3a + '1Y' // 4c3b #50 + 'aA' // 4c3c-4c3d + '1Y' // 4c3e #50 + 'A' // 4c3f + '1Y' // 4c40 #50 + 'eA' // 4c41-4c46 + '1Y' // 4c47 #50 + 'nA' // 4c48-4c56 + '1Y' // 4c57 #50 + 'bA' // 4c58-4c5a + '1Y' // 4c5b #50 + 'pA' // 4c5c-4c6c + '1Y' // 4c6d #50 + 'hA' // 4c6e-4c76 + '1Y' // 4c77 #50 + 'bA' // 4c78-4c7a + '1Y' // 4c7b #50 + 'A' // 4c7c + '1Y' // 4c7d #50 + 'bA' // 4c7e-4c80 + '1Y' // 4c81 #50 + 'bA' // 4c82-4c84 + '1Y' // 4c85 #50 + '1bA' // 4c86-4ca2 + '7J' // 4ca3 #191 + '1Y' // 4ca4 #50 + 'hA' // 4ca5-4cad + '1Y' // 4cae #50 + 'A' // 4caf + '1Y' // 4cb0 #50 + 'eA' // 4cb1-4cb6 + '1Y' // 4cb7 #50 + 'kA' // 4cb8-4cc3 + '1B' // 4cc4 #27 + 'gA' // 4cc5-4ccc + '1Y' // 4ccd #50 + 'bA' // 4cce-4cd0 + '1B' // 4cd1 #27 + 'nA' // 4cd2-4ce0 + '40S' // 4ce1 #1058 + '1Y' // 4ce2 #50 + 'iA' // 4ce3-4cec + '1Y' // 4ced #50 + 'xA' // 4cee-4d06 + '40S' // 4d07 #1058 + 'A' // 4d08 + '48K' // 4d09 #1258 + 'eA' // 4d0a-4d0f + '1Y' // 4d10 #50 + '1hA' // 4d11-4d33 + '1Y' // 4d34 #50 + '2lA' // 4d35-4d75 + '1Y' // 4d76 #50 + '40S' // 4d77 #1058 + 'pA' // 4d78-4d88 + '1Y' // 4d89 #50 + 'fA' // 4d8a-4d90 + '1Y' // 4d91 #50 + 'iA' // 4d92-4d9b + '1Y' // 4d9c #50 + '1hA' // 4d9d-4dbf + '2kF' // 4dc0-4dff #5 + '247B' // 4e00 #6423 + '205Y' // 4e01 #5354 + '19J' // 4e02 #503 + '220E' // 4e03 #5724 + '14O' // 4e04 #378 + '19J' // 4e05 #503 + '7J' // 4e06 #191 + '131G' // 4e07 #3412 + '169H' // 4e08 #4401 + '243R' // 4e09 #6335 + '69L' // 4e0a #1805 + '69J' // 4e0b #1803 + '14O' // 4e0c #378 + '246N' // 4e0d #6409 + '162E' // 4e0e #4216 + '14O' // 4e0f #378 + '116J' // 4e10 #3025 + '141I' // 4e11 #3674 + '1B' // 4e12 #27 + '7K' // 4e13 #192 + '226O' // 4e14 #5890 + '40T' // 4e15 #1059 + '238A' // 4e16 #6188 + '19J' // 4e17 #503 + '161X' // 4e18 #4209 + '65L' // 4e19 #1701 + '35T' // 4e1a #929 + '2R' // 4e1b #69 + '64Z' // 4e1c #1689 + '2D' // 4e1d #55 + '147O' // 4e1e #3836 + '179T' // 4e1f #4673 + '7J' // 4e20 #191 + '116P' // 4e21 #3031 + '116S' // 4e22 #3034 + '1B' // 4e23 #27 + '141L' // 4e24 #3677 + '2C' // 4e25 #54 + '235L' // 4e26 #6121 + '2Y' // 4e27 #76 + '135G' // 4e28 #3516 + '19J' // 4e29 #503 + '166C' // 4e2a #4318 + '141K' // 4e2b #3676 + '61V' // 4e2c #1607 + '247G' // 4e2d #6428 + '1B' // 4e2e #27 + '61V' // 4e2f #1607 + '124M' // 4e30 #3236 + '14O' // 4e31 #378 + '195V' // 4e32 #5091 + 'A' // 4e33 + '2C' // 4e34 #54 + '19J' // 4e35 #503 + '116L' // 4e36 #3027 + '14O' // 4e37 #378 + '183Z' // 4e38 #4783 + '203G' // 4e39 #5284 + '7K' // 4e3a #192 + '243B' // 4e3b #6319 + '136V' // 4e3c #3557 + '61Y' // 4e3d #1610 + '3Q' // 4e3e #94 + '14O' // 4e3f #378 + 'a19J' // 4e40-4e41 #503 + '40T' // 4e42 #1059 + '183M' // 4e43 #4770 + '19J' // 4e44 #503 + '222Y' // 4e45 #5796 + '7J' // 4e46 #191 + '14O' // 4e47 #378 + '159D' // 4e48 #4137 + '61Y' // 4e49 #1610 + 'A' // 4e4a + '241F' // 4e4b #6271 + '3N' // 4e4c #91 + '135F' // 4e4d #3515 + '208H' // 4e4e #5415 + '174R' // 4e4f #4541 + '3Q' // 4e50 #94 + '1B' // 4e51 #27 + '116R' // 4e52 #3033 + '116Q' // 4e53 #3032 + '1R' // 4e54 #43 + '1B' // 4e55 #27 + '166A' // 4e56 #4316 + '259A' // 4e57 #6734 + '194G' // 4e58 #5050 + '176A' // 4e59 #4576 + 'a14O' // 4e5a-4e5b #378 + '147P' // 4e5c #3837 + '221T' // 4e5d #5765 + '125C' // 4e5e #3252 + '241B' // 4e5f #6267 + '2D' // 4e60 #55 + '3I' // 4e61 #86 + 'a1B' // 4e62-4e63 #27 + 'a7J' // 4e64-4e65 #191 + '1Z' // 4e66 #51 + '7J' // 4e67 #191 + '1B' // 4e68 #27 + '14O' // 4e69 #378 + '1Y' // 4e6a #50 + '50F' // 4e6b #1305 + 'A' // 4e6c + '261D' // 4e6d #6789 + 'aA' // 4e6e-4e6f + '1Z' // 4e70 #51 + '257I' // 4e71 #6690 + 'A' // 4e72 + '205Q' // 4e73 #5346 + 'a1B' // 4e74-4e75 #27 + 'a50F' // 4e76-4e77 #1305 + '40V' // 4e78 #1061 + '1B' // 4e79 #27 + 'cA' // 4e7a-4e7d + '215H' // 4e7e #5597 + '19J' // 4e7f #503 + '116M' // 4e80 #3028 + '40V' // 4e81 #1061 + '208F' // 4e82 #5413 + 'aA' // 4e83-4e84 + '14O' // 4e85 #378 + '242G' // 4e86 #6298 + '40V' // 4e87 #1061 + '206M' // 4e88 #5368 + '116N' // 4e89 #3029 + '19J' // 4e8a #503 + '245L' // 4e8b #6381 + '68T' // 4e8c #1787 + '14O' // 4e8d #378 + '200Z' // 4e8e #5225 + '2Y' // 4e8f #76 + '50F' // 4e90 #1305 + '160G' // 4e91 #4166 + '211M' // 4e92 #5498 + '40V' // 4e93 #1061 + '234W' // 4e94 #6106 + '191T' // 4e95 #4985 + '19J' // 4e96 #503 + '1B' // 4e97 #27 + '40T' // 4e98 #1059 + '14O' // 4e99 #378 + '169V' // 4e9a #4415 + '233D' // 4e9b #6061 + '256P' // 4e9c #6671 + '1B' // 4e9d #27 + '226D' // 4e9e #5879 + 'a14O' // 4e9f-4ea0 #378 + '198B' // 4ea1 #5149 + '40T' // 4ea2 #1059 + '61X' // 4ea3 #1609 + '237Y' // 4ea4 #6186 + '35M' // 4ea5 #922 + '214W' // 4ea6 #5586 + '7K' // 4ea7 #192 + '165Z' // 4ea8 #4315 + '3H' // 4ea9 #85 + 'A' // 4eaa + '240R' // 4eab #6257 + '216R' // 4eac #5633 + '168F' // 4ead #4373 + '218S' // 4eae #5686 + '1B' // 4eaf #27 + '16C' // 4eb0 #418 + '8A' // 4eb1 #208 + '2D' // 4eb2 #55 + '13F' // 4eb3 #343 + 'a8A' // 4eb4-4eb5 #208 + '35M' // 4eb6 #922 + '48K' // 4eb7 #1258 + '8A' // 4eb8 #208 + '13F' // 4eb9 #343 + '247E' // 4eba #6426 + 'a13F' // 4ebb-4ebc #343 + 'a8A' // 4ebd-4ebe #208 + '116U' // 4ebf #3036 + '226K' // 4ec0 #5886 + '209Z' // 4ec1 #5459 + 'a13F' // 4ec2-4ec3 #343 + '35M' // 4ec4 #922 + '2D' // 4ec5 #55 + '116K' // 4ec6 #3026 + '166B' // 4ec7 #4317 + '13F' // 4ec8 #343 + '40U' // 4ec9 #1060 + '239I' // 4eca #6222 + '238U' // 4ecb #6208 + '8A' // 4ecc #208 + '208G' // 4ecd #5414 + '130D' // 4ece #3383 + '255D' // 4ecf #6633 + '16C' // 4ed0 #418 + '2K' // 4ed1 #62 + '8A' // 4ed2 #208 + '3N' // 4ed3 #91 + '218D' // 4ed4 #5671 + '184I' // 4ed5 #4792 + '245A' // 4ed6 #6370 + '65L' // 4ed7 #1701 + '224E' // 4ed8 #5828 + '204E' // 4ed9 #5308 + 'a16C' // 4eda-4edb #418 + '48K' // 4edc #1258 + '35M' // 4edd #922 + '13F' // 4ede #343 + '35M' // 4edf #922 + '16C' // 4ee0 #418 + '13F' // 4ee1 #343 + '1B' // 4ee2 #27 + '243Z' // 4ee3 #6343 + '242Z' // 4ee4 #6317 + '69G' // 4ee5 #1800 + 'a8A' // 4ee6-4ee7 #208 + '13F' // 4ee8 #343 + '40U' // 4ee9 #1060 + '116T' // 4eea #3035 + '13F' // 4eeb #343 + '7K' // 4eec #192 + '1B' // 4eed #27 + '116O' // 4eee #3030 + '16C' // 4eef #418 + '181R' // 4ef0 #4723 + '61W' // 4ef1 #1608 + '198Y' // 4ef2 #5172 + '13F' // 4ef3 #343 + '40U' // 4ef4 #1060 + '13F' // 4ef5 #343 + '243X' // 4ef6 #6341 + '141J' // 4ef7 #3675 + '61X' // 4ef8 #1609 + 'a8A' // 4ef9-4efa #208 + '235W' // 4efb #6132 + '16C' // 4efc #418 + '233B' // 4efd #6059 + '1B' // 4efe #27 + '179U' // 4eff #4674 + '13F' // 4f00 #343 + '223P' // 4f01 #5813 + '61W' // 4f02 #1608 + '13F' // 4f03 #343 + '8A' // 4f04 #208 + '40U' // 4f05 #1060 + 'aA' // 4f06-4f07 + '13F' // 4f08 #343 + '61S' // 4f09 #1604 + '205L' // 4f0a #5341 + '61S' // 4f0b #1604 + '1B' // 4f0c #27 + '172K' // 4f0d #4482 + '116B' // 4f0e #3017 + '175L' // 4f0f #4561 + '153O' // 4f10 #3992 + '223K' // 4f11 #5808 + '16C' // 4f12 #418 + '8A' // 4f13 #208 + 'A' // 4f14 + '115V' // 4f15 #3011 + '1B' // 4f16 #27 + '115W' // 4f17 #3012 + '143W' // 4f18 #3740 + '172L' // 4f19 #4483 + '245S' // 4f1a #6388 + '8A' // 4f1b #208 + '1B' // 4f1c #27 + '116E' // 4f1d #3020 + '2R' // 4f1e #69 + '3Y' // 4f1f #102 + '3Q' // 4f20 #94 + '8A' // 4f21 #208 + '61U' // 4f22 #1606 + 'A' // 4f23 + '2C' // 4f24 #54 + '8A' // 4f25 #208 + '3I' // 4f26 #86 + '8A' // 4f27 #208 + '116G' // 4f28 #3022 + '61U' // 4f29 #1606 + '2L' // 4f2a #63 + '16C' // 4f2b #418 + '8A' // 4f2c #208 + '21C' // 4f2d #548 + '16C' // 4f2e #418 + '195L' // 4f2f #5081 + '194H' // 4f30 #5051 + '16C' // 4f31 #418 + '21C' // 4f32 #548 + '16C' // 4f33 #418 + '215F' // 4f34 #5595 + '1B' // 4f35 #27 + '141H' // 4f36 #3673 + '40Q' // 4f37 #1056 + '204U' // 4f38 #5324 + '40Q' // 4f39 #1056 + '167Y' // 4f3a #4366 + '40R' // 4f3b #1057 + '221U' // 4f3c #5766 + '159S' // 4f3d #4152 + '40Q' // 4f3e #1056 + 'A' // 4f3f + '16C' // 4f40 #418 + '21C' // 4f41 #548 + '40R' // 4f42 #1057 + '115U' // 4f43 #3010 + 'A' // 4f44 + '40N' // 4f45 #1053 + '233H' // 4f46 #6065 + '115Z' // 4f47 #3015 + '208E' // 4f48 #5412 + '40Q' // 4f49 #1056 + 'A' // 4f4a + '115T' // 4f4b #3009 + '40R' // 4f4c #1057 + '242Y' // 4f4d #6316 + '229N' // 4f4e #5967 + '231H' // 4f4f #6013 + '191S' // 4f50 #4984 + '167Q' // 4f51 #4358 + '40R' // 4f52 #1057 + '214E' // 4f53 #5568 + '187B' // 4f54 #4863 + '237X' // 4f55 #6185 + '1B' // 4f56 #27 + 'a9H' // 4f57-4f58 #241 + '176P' // 4f59 #4591 + '115M' // 4f5a #3002 + '200X' // 4f5b #5223 + '246S' // 4f5c #6414 + 'a9H' // 4f5d-4f5e #241 + '115Q' // 4f5f #3006 + '240M' // 4f60 #6252 + '21C' // 4f61 #548 + '169S' // 4f62 #4412 + '115R' // 4f63 #3007 + '9H' // 4f64 #241 + '8A' // 4f65 #208 + 'A' // 4f66 + '21C' // 4f67 #548 + 'A' // 4f68 + '186Y' // 4f69 #4860 + '9H' // 4f6a #241 + 'A' // 4f6b + '65W' // 4f6c #1712 + 'A' // 4f6d + '9H' // 4f6e #241 + '17F' // 4f6f #447 + '152L' // 4f70 #3963 + '3F' // 4f71 #83 + '21C' // 4f72 #548 + '227E' // 4f73 #5906 + '21C' // 4f74 #548 + '197G' // 4f75 #5128 + '17F' // 4f76 #447 + 'b9H' // 4f77-4f79 #241 + '17F' // 4f7a #447 + '9H' // 4f7b #241 + '124L' // 4f7c #3235 + '9H' // 4f7d #241 + '17F' // 4f7e #447 + '244Z' // 4f7f #6369 + '21C' // 4f80 #548 + '17F' // 4f81 #447 + '9H' // 4f82 #241 + '124K' // 4f83 #3234 + '17F' // 4f84 #447 + '3F' // 4f85 #83 + '240T' // 4f86 #6259 + 'A' // 4f87 + '135D' // 4f88 #3513 + '9H' // 4f89 #241 + '17F' // 4f8a #447 + '222X' // 4f8b #5795 + '3F' // 4f8c #83 + '148Q' // 4f8d #3864 + '49W' // 4f8e #1296 + '17F' // 4f8f #447 + '9H' // 4f90 #241 + '115Y' // 4f91 #3014 + '9H' // 4f92 #241 + '49W' // 4f93 #1296 + '9H' // 4f94 #241 + '8A' // 4f95 #208 + '17F' // 4f96 #447 + '9H' // 4f97 #241 + '17F' // 4f98 #447 + '3F' // 4f99 #83 + '21B' // 4f9a #547 + '237W' // 4f9b #6184 + 'A' // 4f9c + '234M' // 4f9d #6096 + '9H' // 4f9e #241 + '3F' // 4f9f #83 + '251A' // 4fa0 #6526 + '259Z' // 4fa1 #6759 + '23U' // 4fa2 #618 + '1R' // 4fa3 #43 + 'A' // 4fa4 + '8A' // 4fa5 #208 + '2R' // 4fa6 #69 + '3N' // 4fa7 #91 + '116H' // 4fa8 #3023 + 'a8A' // 4fa9-4faa #208 + '21B' // 4fab #547 + '8A' // 4fac #208 + '3F' // 4fad #83 + '173A' // 4fae #4498 + '165Y' // 4faf #4314 + '40N' // 4fb0 #1053 + 'A' // 4fb1 + '21B' // 4fb2 #547 + '21C' // 4fb3 #548 + '40N' // 4fb4 #1053 + '210D' // 4fb5 #5463 + '180C' // 4fb6 #4682 + '9H' // 4fb7 #241 + '8A' // 4fb8 #208 + '21B' // 4fb9 #547 + '23U' // 4fba #618 + 'a3F' // 4fbb-4fbc #83 + '115O' // 4fbd #3004 + '3F' // 4fbe #83 + '236Y' // 4fbf #6160 + 'a21B' // 4fc0-4fc1 #547 + '222W' // 4fc2 #5794 + '203F' // 4fc3 #5283 + '186Z' // 4fc4 #4861 + '9H' // 4fc5 #241 + '49W' // 4fc6 #1296 + '23U' // 4fc7 #618 + '40O' // 4fc8 #1054 + '70L' // 4fc9 #1831 + '189D' // 4fca #4917 + '21B' // 4fcb #547 + '40O' // 4fcc #1054 + '9H' // 4fcd #241 + '17F' // 4fce #447 + '65W' // 4fcf #1712 + '141F' // 4fd0 #3671 + '19D' // 4fd1 #497 + '3F' // 4fd2 #83 + '19D' // 4fd3 #497 + '115N' // 4fd4 #3003 + 'aA' // 4fd5-4fd6 + '197B' // 4fd7 #5123 + '115P' // 4fd8 #3005 + '23U' // 4fd9 #618 + '19D' // 4fda #497 + 'a15X' // 4fdb-4fdc #413 + '68V' // 4fdd #1789 + '131L' // 4fde #3417 + '19D' // 4fdf #497 + '179Q' // 4fe0 #4670 + '49G' // 4fe1 #1280 + '13I' // 4fe2 #346 + '252T' // 4fe3 #6571 + '15X' // 4fe4 #413 + '21B' // 4fe5 #547 + '13I' // 4fe6 #346 + 'A' // 4fe7 + '16H' // 4fe8 #423 + '2Y' // 4fe9 #76 + '3G' // 4fea #84 + '16H' // 4feb #423 + '61T' // 4fec #1605 + '16H' // 4fed #423 + '230K' // 4fee #5990 + '135C' // 4fef #3512 + '40O' // 4ff0 #1054 + '194F' // 4ff1 #5049 + '40O' // 4ff2 #1054 + '116C' // 4ff3 #3018 + '23U' // 4ff4 #618 + '116A' // 4ff5 #3016 + '15X' // 4ff6 #413 + 'A' // 4ff7 + '115X' // 4ff8 #3013 + '23U' // 4ff9 #618 + '116D' // 4ffa #3019 + 'A' // 4ffb + '3F' // 4ffc #83 + '21B' // 4ffd #547 + '147N' // 4ffe #3835 + '3F' // 4fff #83 + '21B' // 5000 #547 + '13I' // 5001 #346 + '70L' // 5002 #1831 + '40N' // 5003 #1053 + '3F' // 5004 #83 + '15X' // 5005 #413 + '65V' // 5006 #1711 + '3F' // 5007 #83 + '23U' // 5008 #618 + '191L' // 5009 #4977 + '3F' // 500a #83 + '246L' // 500b #6407 + '15X' // 500c #413 + '205V' // 500d #5351 + '3F' // 500e #83 + '15X' // 500f #413 + '3F' // 5010 #83 + '240H' // 5011 #6247 + '212G' // 5012 #5518 + 'a15X' // 5013-5014 #413 + '61T' // 5015 #1605 + '129R' // 5016 #3371 + '13I' // 5017 #346 + '135E' // 5018 #3514 + '219U' // 5019 #5714 + '135A' // 501a #3510 + '15X' // 501b #413 + '19D' // 501c #497 + '13I' // 501d #346 + '19D' // 501e #497 + '197V' // 501f #5143 + 'A' // 5020 + '147M' // 5021 #3834 + '15X' // 5022 #413 + '19D' // 5023 #497 + '258Q' // 5024 #6724 + '15X' // 5025 #413 + '135B' // 5026 #3511 + 'a19D' // 5027-5028 #497 + '141G' // 5029 #3672 + '141E' // 502a #3670 + '210N' // 502b #5473 + 'a19D' // 502c-502d #497 + '15X' // 502e #413 + 'A' // 502f + '15X' // 5030 #413 + '23U' // 5031 #618 + '3F' // 5032 #83 + '13I' // 5033 #346 + '116F' // 5034 #3021 + '3F' // 5035 #83 + '255A' // 5036 #6630 + 'a16H' // 5037-5038 #423 + '13I' // 5039 #346 + '2Y' // 503a #76 + '19D' // 503b #497 + '231T' // 503c #6025 + 'A' // 503d + '1R' // 503e #43 + 'A' // 503f + '115S' // 5040 #3008 + '40P' // 5041 #1055 + '13I' // 5042 #346 + '61R' // 5043 #1603 + 'A' // 5044 + 'a40P' // 5045-5046 #1055 + '226C' // 5047 #5878 + '61R' // 5048 #1603 + '196I' // 5049 #5104 + '40P' // 504a #1055 + '116I' // 504b #3024 + '40P' // 504c #1055 + '40M' // 504d #1052 + '12I' // 504e #320 + '202V' // 504f #5273 + '3F' // 5050 #83 + '12I' // 5051 #320 + '3F' // 5052 #83 + '12I' // 5053 #320 + 'A' // 5054 + '64W' // 5055 #1686 + '61Q' // 5056 #1602 + '13I' // 5057 #346 + '48F' // 5058 #1253 + '3F' // 5059 #83 + '233A' // 505a #6058 + '16H' // 505b #423 + '220U' // 505c #5740 + '19C' // 505d #496 + '40M' // 505e #1052 + '21A' // 505f #546 + '12I' // 5060 #320 + 'A' // 5061 + '13I' // 5062 #346 + '12I' // 5063 #320 + 'A' // 5064 + '236B' // 5065 #6137 + '40L' // 5066 #1051 + '13I' // 5067 #346 + 'aA' // 5068-5069 + '12I' // 506a #320 + 'A' // 506b + '61Q' // 506c #1602 + '3F' // 506d #83 + 'A' // 506e + '19C' // 506f #496 + '12I' // 5070 #320 + '13I' // 5071 #346 + '12I' // 5072 #320 + 'A' // 5073 + '199B' // 5074 #5175 + '175N' // 5075 #4563 + '202F' // 5076 #5257 + '200Y' // 5077 #5224 + '250Z' // 5078 #6525 + 'A' // 5079 + '19C' // 507a #496 + '16H' // 507b #423 + 'A' // 507c + '168O' // 507d #4382 + '16H' // 507e #423 + '2L' // 507f #63 + '48H' // 5080 #1255 + '40L' // 5081 #1051 + 'A' // 5082 + 'a3F' // 5083-5084 #83 + '179R' // 5085 #4671 + '3F' // 5086 #83 + 'A' // 5087 + '12I' // 5088 #320 + '16H' // 5089 #423 + '3F' // 508a #83 + 'a19C' // 508b-508c #496 + '142L' // 508d #3703 + '21A' // 508e #546 + '3F' // 508f #83 + '40L' // 5090 #1051 + '195I' // 5091 #5078 + '12I' // 5092 #320 + 'a3F' // 5093-5094 #83 + '12I' // 5095 #320 + '21A' // 5096 #546 + '16H' // 5097 #423 + '167S' // 5098 #4360 + '236X' // 5099 #6159 + 'b12I' // 509a-509c #320 + '40M' // 509d #1052 + '13I' // 509e #346 + 'b3F' // 509f-50a1 #83 + '179S' // 50a2 #4672 + '12I' // 50a3 #320 + 'A' // 50a4 + '16H' // 50a5 #423 + '115H' // 50a6 #2997 + '16H' // 50a7 #423 + '3Y' // 50a8 #102 + '16H' // 50a9 #423 + '3F' // 50aa #83 + 'A' // 50ab + '169L' // 50ac #4405 + '64W' // 50ad #1686 + 'A' // 50ae + '21A' // 50af #546 + '3F' // 50b0 #83 + '21A' // 50b1 #546 + '172J' // 50b2 #4481 + '240G' // 50b3 #6246 + '21A' // 50b4 #546 + '174G' // 50b5 #4530 + 'A' // 50b6 + '215O' // 50b7 #5604 + 'A' // 50b8 + '3F' // 50b9 #83 + '12I' // 50ba #320 + '187A' // 50bb #4862 + '48F' // 50bc #1253 + '3F' // 50bd #83 + '182N' // 50be #4745 + 'A' // 50bf + '3F' // 50c0 #83 + 'A' // 50c1 + '21A' // 50c2 #546 + '3F' // 50c3 #83 + '13I' // 50c4 #346 + '218J' // 50c5 #5677 + '19C' // 50c6 #496 + '12I' // 50c7 #320 + '19C' // 50c8 #496 + '114Y' // 50c9 #2988 + '48H' // 50ca #1255 + 'A' // 50cb + '3F' // 50cc #83 + '115F' // 50cd #2995 + '21A' // 50ce #546 + '238W' // 50cf #6210 + '40L' // 50d0 #1051 + '65V' // 50d1 #1711 + 'A' // 50d2 + 'a3F' // 50d3-50d4 #83 + '143M' // 50d5 #3730 + '48H' // 50d6 #1255 + 'A' // 50d7 + '3F' // 50d8 #83 + '21A' // 50d9 #546 + '125T' // 50da #3269 + 'A' // 50db + '3F' // 50dc #83 + '14N' // 50dd #377 + '250U' // 50de #6520 + '14N' // 50df #377 + 'A' // 50e0 + '14N' // 50e1 #377 + '2Q' // 50e2 #68 + '14N' // 50e3 #377 + '2Q' // 50e4 #68 + '40K' // 50e5 #1050 + '31G' // 50e6 #812 + '142M' // 50e7 #3704 + 'a14N' // 50e8-50e9 #377 + '19C' // 50ea #496 + 'A' // 50eb + '40M' // 50ec #1052 + '40K' // 50ed #1050 + '31G' // 50ee #812 + '14N' // 50ef #377 + '31G' // 50f0 #812 + '141C' // 50f1 #3668 + '2Q' // 50f2 #68 + '31G' // 50f3 #812 + '48G' // 50f4 #1254 + '141D' // 50f5 #3669 + '2Q' // 50f6 #68 + 'aA' // 50f7-50f8 + '232Y' // 50f9 #6056 + '250V' // 50fa #6521 + '40K' // 50fb #1050 + '48F' // 50fc #1253 + 'A' // 50fd + '31G' // 50fe #812 + '261C' // 50ff #6788 + '203B' // 5100 #5279 + '40K' // 5101 #1050 + '31G' // 5102 #812 + '2Q' // 5103 #68 + '203T' // 5104 #5297 + '19C' // 5105 #496 + '31F' // 5106 #811 + '10E' // 5107 #264 + '14N' // 5108 #377 + '31F' // 5109 #811 + '15A' // 510a #390 + '10E' // 510b #264 + '14N' // 510c #377 + '10E' // 510d #264 + '14N' // 510e #377 + '15A' // 510f #390 + '10E' // 5110 #264 + 'A' // 5111 + '159C' // 5112 #4136 + '48J' // 5113 #1257 + '14N' // 5114 #377 + '10E' // 5115 #264 + '2Q' // 5116 #68 + '10E' // 5117 #264 + '179O' // 5118 #4668 + '2Q' // 5119 #68 + '10E' // 511a #264 + '7R' // 511b #199 + '10E' // 511c #264 + '2Q' // 511d #68 + '7R' // 511e #199 + '176B' // 511f #4577 + '19C' // 5120 #496 + '31F' // 5121 #811 + 'A' // 5122 + '2Q' // 5123 #68 + 'A' // 5124 + '19C' // 5125 #496 + 'A' // 5126 + '2Q' // 5127 #68 + '7R' // 5128 #199 + 'A' // 5129 + '237M' // 512a #6174 + '48J' // 512b #1257 + 'a7R' // 512c-512d #199 + 'A' // 512e + '2Q' // 512f #68 + 'A' // 5130 + '10E' // 5131 #264 + '201S' // 5132 #5244 + '7R' // 5133 #199 + 'a10E' // 5134-5135 #264 + 'A' // 5136 + '134X' // 5137 #3507 + 'a10E' // 5138-5139 #264 + '61N' // 513a #1599 + '14N' // 513b #377 + '31F' // 513c #811 + 'aA' // 513d-513e + '134Z' // 513f #3509 + '31F' // 5140 #811 + '172I' // 5141 #4480 + '7R' // 5142 #199 + '238M' // 5143 #6200 + '198P' // 5144 #5163 + '221S' // 5145 #5764 + '174Q' // 5146 #4540 + '165X' // 5147 #4313 + '239C' // 5148 #6216 + '68T' // 5149 #1787 + '2Q' // 514a #68 + '226Y' // 514b #5900 + '172H' // 514c #4479 + '234N' // 514d #6097 + '253I' // 514e #6586 + '7R' // 514f #199 + '258T' // 5150 #6727 + '115J' // 5151 #2999 + '232Z' // 5152 #6057 + '2Q' // 5153 #68 + '186X' // 5154 #4859 + '10E' // 5155 #264 + '48J' // 5156 #1257 + '10E' // 5157 #264 + '7R' // 5158 #199 + 'A' // 5159 + '115D' // 515a #2993 + 'A' // 515b + '147W' // 515c #3844 + 'aA' // 515d-515e + '14N' // 515f #377 + '48G' // 5160 #1254 + 'A' // 5161 + '31F' // 5162 #811 + 'A' // 5163 + '2Q' // 5164 #68 + '246X' // 5165 #6419 + '2Q' // 5166 #68 + '240F' // 5167 #6245 + '41F' // 5168 #1071 + '232X' // 5169 #6055 + '61N' // 516a #1599 + '228O' // 516b #5942 + '69H' // 516c #1801 + '227S' // 516d #5920 + '114T' // 516e #2983 + 'A' // 516f + '2C' // 5170 #54 + '236Z' // 5171 #6161 + '15A' // 5172 #390 + '250Y' // 5173 #6524 + '114X' // 5174 #2987 + '205U' // 5175 #5350 + '240V' // 5176 #6261 + '236W' // 5177 #6158 + '229W' // 5178 #5976 + '114V' // 5179 #2985 + 'A' // 517a + '250X' // 517b #6523 + '197Q' // 517c #5138 + '3N' // 517d #91 + '2Q' // 517e #68 + '15A' // 517f #390 + '129Q' // 5180 #3370 + '15A' // 5181 #390 + '10E' // 5182 #264 + 'a7R' // 5183-5184 #199 + '260K' // 5185 #6770 + '115G' // 5186 #2996 + '169R' // 5187 #4411 + '115I' // 5188 #2998 + '114U' // 5189 #2984 + '233P' // 518a #6073 + '7R' // 518b #199 + '70K' // 518c #1830 + '242P' // 518d #6307 + '2Q' // 518e #68 + '10E' // 518f #264 + '2Q' // 5190 #68 + '10E' // 5191 #264 + '203O' // 5192 #5292 + '14N' // 5193 #377 + 'A' // 5194 + '114S' // 5195 #2982 + '10E' // 5196 #264 + '115A' // 5197 #2990 + '2Q' // 5198 #68 + '260A' // 5199 #6760 + '48I' // 519a #1256 + '2D' // 519b #55 + '115K' // 519c #3000 + '2Q' // 519d #68 + '115L' // 519e #3001 + 'A' // 519f + '202U' // 51a0 #5272 + '2Q' // 51a1 #68 + '35L' // 51a2 #921 + '7R' // 51a3 #199 + '147K' // 51a4 #3832 + '142A' // 51a5 #3692 + '7R' // 51a6 #199 + '48I' // 51a7 #1256 + '115B' // 51a8 #2991 + '7R' // 51a9 #199 + '129P' // 51aa #3369 + '35L' // 51ab #921 + '205P' // 51ac #5345 + '7R' // 51ad #199 + '48I' // 51ae #1256 + '2Y' // 51af #76 + '214V' // 51b0 #5585 + '35L' // 51b1 #921 + '114W' // 51b2 #2986 + '61O' // 51b3 #1600 + '115C' // 51b4 #2992 + '61O' // 51b5 #1600 + '114Z' // 51b6 #2989 + '221D' // 51b7 #5749 + '48G' // 51b8 #1254 + 'A' // 51b9 + '2Q' // 51ba #68 + '2L' // 51bb #63 + '35L' // 51bc #921 + '114R' // 51bd #2981 + '61P' // 51be #1601 + '7R' // 51bf #199 + '3Y' // 51c0 #102 + 'A' // 51c1 + '2Q' // 51c2 #68 + '35L' // 51c3 #921 + '115E' // 51c4 #2994 + '61P' // 51c5 #1601 + '195A' // 51c6 #5070 + '40J' // 51c7 #1049 + '141B' // 51c8 #3667 + '114A' // 51c9 #2964 + '10D' // 51ca #263 + '31E' // 51cb #810 + '188K' // 51cc #4898 + '196Y' // 51cd #5120 + '40J' // 51ce #1049 + '114E' // 51cf #2968 + '48E' // 51d0 #1252 + '114C' // 51d1 #2966 + 'a31D' // 51d2-51d3 #809 + '10D' // 51d4 #263 + '2Q' // 51d5 #68 + '7R' // 51d6 #199 + 'A' // 51d7 + '2Q' // 51d8 #68 + 'aA' // 51d9-51da + '114I' // 51db #2972 + '114G' // 51dc #2970 + '174P' // 51dd #4539 + '250T' // 51de #6519 + '15W' // 51df #412 + '147L' // 51e0 #3833 + '201Q' // 51e1 #5242 + '31D' // 51e2 #809 + 'A' // 51e3 + '114P' // 51e4 #2979 + '2Q' // 51e5 #68 + '258N' // 51e6 #6721 + '2Q' // 51e7 #68 + '15A' // 51e8 #390 + '7R' // 51e9 #199 + '253A' // 51ea #6578 + '15A' // 51eb #390 + '2Q' // 51ec #68 + '114D' // 51ed #2967 + '2Q' // 51ee #68 + '3N' // 51ef #91 + '153B' // 51f0 #3979 + '201I' // 51f1 #5234 + '2Q' // 51f2 #68 + '134Y' // 51f3 #3508 + '15W' // 51f4 #412 + '10D' // 51f5 #263 + '153U' // 51f6 #3998 + '2Q' // 51f7 #68 + '167B' // 51f8 #4343 + '153Y' // 51f9 #4002 + '41F' // 51fa #1071 + '3Q' // 51fb #94 + '40J' // 51fc #1049 + '175A' // 51fd #4550 + '31D' // 51fe #809 + '15A' // 51ff #390 + '203E' // 5200 #5282 + '114B' // 5201 #2965 + '10D' // 5202 #263 + '142W' // 5203 #3714 + '2Q' // 5204 #68 + '10D' // 5205 #263 + '69K' // 5206 #1804 + '223Y' // 5207 #5822 + '114H' // 5208 #2971 + 'A' // 5209 + '228A' // 520a #5928 + '10D' // 520b #263 + 'A' // 520c + '15A' // 520d #390 + '31E' // 520e #810 + 'aA' // 520f-5210 + '182M' // 5211 #4744 + '179P' // 5212 #4669 + '15W' // 5213 #412 + '2Q' // 5214 #68 + '7R' // 5215 #199 + '10D' // 5216 #263 + '234D' // 5217 #6087 + '250W' // 5218 #6522 + '2D' // 5219 #55 + '3I' // 521a #86 + '3Q' // 521b #94 + 'A' // 521c + '224C' // 521d #5826 + 'A' // 521e + '40F' // 521f #1045 + '114Q' // 5220 #2980 + 'A' // 5221 + '2Q' // 5222 #68 + 'A' // 5223 + '206B' // 5224 #5357 + '239B' // 5225 #6215 + 'a15W' // 5226-5227 #412 + '10D' // 5228 #263 + '245I' // 5229 #6378 + '208D' // 522a #5411 + '70K' // 522b #1830 + 'a15A' // 522c-522d #390 + '172G' // 522e #4478 + 'A' // 522f + '246H' // 5230 #6403 + '2Q' // 5231 #68 + '15W' // 5232 #412 + '10D' // 5233 #263 + '48E' // 5234 #1252 + '2Q' // 5235 #68 + '231E' // 5236 #6010 + '205B' // 5237 #5331 + '205O' // 5238 #5344 + '251V' // 5239 #6547 + '205D' // 523a #5333 + '215S' // 523b #5608 + '10D' // 523c #263 + '15A' // 523d #390 + 'A' // 523e + 'a15A' // 523f-5240 #390 + '114O' // 5241 #2978 + '1R' // 5242 #43 + '125H' // 5243 #3257 + '15W' // 5244 #412 + '7R' // 5245 #199 + 'A' // 5246 + '233X' // 5247 #6081 + 'A' // 5248 + '10D' // 5249 #263 + '162D' // 524a #4215 + '134V' // 524b #3505 + '31E' // 524c #810 + '245Q' // 524d #6386 + '143U' // 524e #3738 + '31D' // 524f #809 + '15A' // 5250 #390 + '3N' // 5251 #91 + '48E' // 5252 #1252 + 'A' // 5253 + '134W' // 5254 #3506 + '15W' // 5255 #412 + '153J' // 5256 #3987 + '15W' // 5257 #412 + '2Q' // 5258 #68 + '40F' // 5259 #1045 + '2Q' // 525a #68 + '219E' // 525b #5698 + '10D' // 525c #263 + '152K' // 525d #3962 + '10D' // 525e #263 + '7R' // 525f #199 + '31D' // 5260 #809 + '31E' // 5261 #810 + 'A' // 5262 + '256O' // 5263 #6670 + '257A' // 5264 #6682 + '254N' // 5265 #6617 + '2Q' // 5266 #68 + '2C' // 5267 #54 + '40F' // 5268 #1045 + '194E' // 5269 #5048 + '194L' // 526a #5055 + 'A' // 526b + '2Q' // 526c #68 + 'A' // 526d + '15W' // 526e #412 + '203W' // 526f #5300 + '253P' // 5270 #6593 + '2Q' // 5271 #68 + '184D' // 5272 #4787 + '10D' // 5273 #263 + '15W' // 5274 #412 + '234L' // 5275 #6095 + 'A' // 5276 + '10D' // 5277 #263 + '2Q' // 5278 #68 + '31D' // 5279 #809 + 'bA' // 527a-527c + '31E' // 527d #810 + 'A' // 527e + '31E' // 527f #810 + '2Q' // 5280 #68 + '40J' // 5281 #1049 + '10D' // 5282 #263 + '214U' // 5283 #5584 + '10D' // 5284 #263 + '7R' // 5285 #199 + 'A' // 5286 + '220G' // 5287 #5726 + '141A' // 5288 #3666 + '200W' // 5289 #5222 + '15W' // 528a #412 + '12T' // 528b #331 + '15W' // 528c #412 + '67B' // 528d #1743 + 'A' // 528e + '114N' // 528f #2977 + '40F' // 5290 #1045 + '67B' // 5291 #1743 + '250Q' // 5292 #6516 + '23T' // 5293 #617 + '40I' // 5294 #1048 + 'c3L' // 5295-5298 #89 + 'A' // 5299 + '113Y' // 529a #2962 + '68Y' // 529b #1792 + '3L' // 529c #89 + '3H' // 529d #85 + '1Z' // 529e #51 + '234A' // 529f #6084 + '41D' // 52a0 #1069 + '66M' // 52a1 #1728 + '12T' // 52a2 #331 + '161R' // 52a3 #4203 + '113W' // 52a4 #2960 + '3L' // 52a5 #89 + '23T' // 52a6 #617 + '9J' // 52a7 #243 + '66M' // 52a8 #1728 + '235E' // 52a9 #6114 + '197F' // 52aa #5127 + '159B' // 52ab #4135 + 'a23T' // 52ac-52ad #617 + 'A' // 52ae + '9J' // 52af #243 + '3L' // 52b0 #89 + '254T' // 52b1 #6623 + '1R' // 52b2 #43 + '3Y' // 52b3 #102 + '257E' // 52b4 #6686 + '23T' // 52b5 #617 + 'b3L' // 52b6-52b8 #89 + '114K' // 52b9 #2974 + '3L' // 52ba #89 + 'a23T' // 52bb-52bc #617 + '3L' // 52bd #89 + '31C' // 52be #808 + '2D' // 52bf #55 + '40I' // 52c0 #1048 + '194C' // 52c1 #5046 + 'A' // 52c2 + '161H' // 52c3 #4193 + '3L' // 52c4 #89 + '31C' // 52c5 #808 + '3L' // 52c6 #89 + '197L' // 52c7 #5133 + '3L' // 52c8 #89 + '155G' // 52c9 #4036 + '9J' // 52ca #243 + '2R' // 52cb #69 + '26S' // 52cc #694 + '31C' // 52cd #808 + 'A' // 52ce + '3L' // 52cf #89 + '23T' // 52d0 #617 + '40I' // 52d1 #1048 + '186V' // 52d2 #4857 + 'A' // 52d3 + '3L' // 52d4 #89 + '246Y' // 52d5 #6420 + '31C' // 52d6 #808 + '23T' // 52d7 #617 + '136W' // 52d8 #3558 + '242O' // 52d9 #6306 + 'A' // 52da + '31C' // 52db #808 + '3L' // 52dc #89 + '213J' // 52dd #5547 + '67O' // 52de #1756 + '199G' // 52df #5180 + '23T' // 52e0 #617 + '26S' // 52e1 #694 + '221X' // 52e2 #5769 + '31C' // 52e3 #808 + '191C' // 52e4 #4968 + '9J' // 52e5 #243 + '40I' // 52e6 #1048 + '256V' // 52e7 #6677 + 'b9J' // 52e8-52ea #243 + '61M' // 52eb #1598 + '9J' // 52ec #243 + 'b12T' // 52ed-52ef #331 + '15V' // 52f0 #411 + '40H' // 52f1 #1047 + '253F' // 52f2 #6583 + '66D' // 52f3 #1719 + '3L' // 52f4 #89 + '67O' // 52f5 #1756 + '9J' // 52f6 #243 + '15V' // 52f7 #411 + '165U' // 52f8 #4310 + '15V' // 52f9 #411 + '113X' // 52fa #2961 + '66D' // 52fb #1719 + '12T' // 52fc #331 + 'A' // 52fd + '180A' // 52fe #4680 + '209C' // 52ff #5436 + '250R' // 5300 #6517 + '40H' // 5301 #1047 + '254K' // 5302 #6614 + '3L' // 5303 #89 + '12T' // 5304 #331 + '234H' // 5305 #6091 + '129N' // 5306 #3367 + '3L' // 5307 #89 + '134U' // 5308 #3504 + 'A' // 5309 + '40H' // 530a #1047 + '15V' // 530b #411 + '3L' // 530c #89 + '40G' // 530d #1046 + 'A' // 530e + 'a40G' // 530f-5310 #1046 + '3L' // 5311 #89 + 'A' // 5312 + '3L' // 5313 #89 + 'A' // 5314 + '40G' // 5315 #1046 + '68X' // 5316 #1791 + '243H' // 5317 #6325 + '3L' // 5318 #89 + '172F' // 5319 #4477 + '15V' // 531a #411 + '3L' // 531b #89 + 'a15V' // 531c-531d #411 + 'a3L' // 531e-531f #89 + '161K' // 5320 #4196 + '124J' // 5321 #3233 + 'A' // 5322 + '140Z' // 5323 #3665 + '26S' // 5324 #694 + '3L' // 5325 #89 + '12T' // 5326 #331 + '26S' // 5327 #694 + 'a3L' // 5328-5329 #89 + '65K' // 532a #1700 + '3L' // 532b #89 + '26S' // 532c #694 + '40H' // 532d #1047 + '12T' // 532e #331 + '208C' // 532f #5410 + '9J' // 5330 #243 + '15V' // 5331 #411 + 'a26S' // 5332-5333 #694 + 'A' // 5334 + '3L' // 5335 #89 + 'aA' // 5336-5337 + '15V' // 5338 #411 + '161I' // 5339 #4194 + '259I' // 533a #6742 + '114J' // 533b #2973 + 'b15V' // 533c-533e #411 + '161O' // 533f #4200 + '240E' // 5340 #6244 + '235H' // 5341 #6117 + '26S' // 5342 #694 + '68L' // 5343 #1779 + '114L' // 5344 #2975 + '15V' // 5345 #411 + '3L' // 5346 #89 + '226J' // 5347 #5885 + '215N' // 5348 #5603 + '152I' // 5349 #3960 + '68L' // 534a #1779 + '9J' // 534b #243 + '15V' // 534c #411 + '114F' // 534d #2969 + '65M' // 534e #1702 + '1Z' // 534f #51 + '12T' // 5350 #331 + '154I' // 5351 #4012 + '126C' // 5352 #3278 + '190J' // 5353 #4949 + '216E' // 5354 #5620 + '3Q' // 5355 #94 + '2C' // 5356 #54 + '236F' // 5357 #6141 + '259U' // 5358 #6754 + '3L' // 5359 #89 + '220T' // 535a #5739 + '3L' // 535b #89 + '166D' // 535c #4319 + '114M' // 535d #2976 + '40G' // 535e #1046 + '61M' // 535f #1598 + '190V' // 5360 #4961 + '232W' // 5361 #6054 + '2L' // 5362 #63 + '15V' // 5363 #411 + '113Z' // 5364 #2963 + '3L' // 5365 #89 + '179M' // 5366 #4666 + '113G' // 5367 #2944 + '261B' // 5368 #6787 + '10C' // 5369 #262 + 'A' // 536a + '2D' // 536b #55 + '10C' // 536c #262 + '48B' // 536d #1249 + '10C' // 536e #262 + '113N' // 536f #2951 + '230F' // 5370 #5985 + '204T' // 5371 #5323 + '10C' // 5372 #262 + '234S' // 5373 #6102 + '113T' // 5374 #2957 + '155B' // 5375 #4031 + 'A' // 5376 + '194D' // 5377 #5047 + '175V' // 5378 #4571 + '10C' // 5379 #262 + '9J' // 537a #243 + '218A' // 537b #5668 + '31B' // 537c #807 + '61J' // 537d #1595 + '48B' // 537e #1249 + '65K' // 537f #1700 + 'aA' // 5380-5381 + '61K' // 5382 #1596 + '3L' // 5383 #89 + '136K' // 5384 #3546 + '3I' // 5385 #86 + '1Z' // 5386 #51 + 'a3L' // 5387-5388 #89 + '250S' // 5389 #6518 + 'A' // 538a + '2C' // 538b #54 + '2Y' // 538c #76 + '12T' // 538d #331 + '10C' // 538e #262 + 'A' // 538f + '12T' // 5390 #331 + 'A' // 5391 + '31B' // 5392 #807 + '61J' // 5393 #1595 + '10C' // 5394 #262 + '2R' // 5395 #69 + '10C' // 5396 #262 + 'A' // 5397 + '147I' // 5398 #3830 + '48C' // 5399 #1250 + '67T' // 539a #1761 + 'a12T' // 539b-539c #331 + '147J' // 539d #3831 + 'A' // 539e + '243M' // 539f #6330 + '61I' // 53a0 #1594 + '9J' // 53a1 #243 + '113V' // 53a2 #2959 + '12T' // 53a3 #331 + '10C' // 53a4 #262 + '61I' // 53a5 #1594 + '113B' // 53a6 #2939 + 'A' // 53a7 + '113Q' // 53a8 #2954 + '10C' // 53a9 #262 + 'a48B' // 53aa-53ab #1249 + 'A' // 53ac + '172E' // 53ad #4476 + '10C' // 53ae #262 + '3L' // 53af #89 + '10C' // 53b0 #262 + 'A' // 53b1 + '179N' // 53b2 #4667 + '257U' // 53b3 #6702 + '48C' // 53b4 #1250 + '9J' // 53b5 #243 + '10C' // 53b6 #262 + 'a9J' // 53b7-53b8 #243 + '31B' // 53b9 #807 + '3L' // 53ba #89 + '68Q' // 53bb #1784 + '12T' // 53bc #331 + '3L' // 53bd #89 + '12T' // 53be #331 + '3I' // 53bf #86 + '3L' // 53c0 #89 + '10C' // 53c1 #262 + '143Q' // 53c2 #3734 + '232V' // 53c3 #6053 + '9J' // 53c4 #243 + '113C' // 53c5 #2940 + 'a12T' // 53c6-53c7 #331 + '233Q' // 53c8 #6074 + '172Z' // 53c9 #4497 + '241I' // 53ca #6274 + '242F' // 53cb #6297 + '137B' // 53cc #3563 + '230R' // 53cd #5997 + '259M' // 53ce #6746 + '9J' // 53cf #243 + '48A' // 53d0 #1248 + '177D' // 53d1 #4605 + '10C' // 53d2 #262 + '9J' // 53d3 #243 + '173I' // 53d4 #4506 + '9J' // 53d5 #243 + '239G' // 53d6 #6220 + '238T' // 53d7 #6207 + '131N' // 53d8 #3419 + '113J' // 53d9 #2947 + '10C' // 53da #262 + '152H' // 53db #3959 + 'A' // 53dc + '3L' // 53dd #89 + '9J' // 53de #243 + '10C' // 53df #262 + '113F' // 53e0 #2943 + '113L' // 53e1 #2949 + '159A' // 53e2 #4134 + '238L' // 53e3 #6199 + '231I' // 53e4 #6014 + '203S' // 53e5 #5296 + '226A' // 53e6 #5876 + '36J' // 53e7 #945 + '23S' // 53e8 #616 + '113R' // 53e9 #2955 + '233G' // 53ea #6064 + '209L' // 53eb #5445 + '181V' // 53ec #4727 + '165T' // 53ed #4309 + '152J' // 53ee #3961 + '69G' // 53ef #1800 + '246K' // 53f0 #6406 + '113M' // 53f1 #2950 + '229B' // 53f2 #5955 + '212O' // 53f3 #5526 + '36J' // 53f4 #945 + '23S' // 53f5 #616 + '113S' // 53f6 #2956 + '155P' // 53f7 #4045 + '234K' // 53f8 #6094 + '2Y' // 53f9 #76 + '36J' // 53fa #945 + 'a31A' // 53fb-53fc #806 + '7Z' // 53fd #207 + '48D' // 53fe #1251 + 'a7Z' // 53ff-5400 #207 + '113D' // 5401 #2941 + '36J' // 5402 #945 + '225Z' // 5403 #5875 + '236O' // 5404 #6150 + '7Z' // 5405 #207 + 'a31A' // 5406-5407 #806 + '49H' // 5408 #1281 + '212V' // 5409 #5533 + '188O' // 540a #4902 + '186W' // 540b #4858 + '49G' // 540c #1280 + '68Z' // 540d #1793 + '201C' // 540e #5228 + '61H' // 540f #1593 + '189M' // 5410 #4926 + '231P' // 5411 #6021 + '23S' // 5412 #616 + '129O' // 5413 #3368 + '31A' // 5414 #806 + '2Y' // 5415 #76 + '31A' // 5416 #806 + '1Z' // 5417 #51 + 'a7Z' // 5418-5419 #207 + '23S' // 541a #616 + '198S' // 541b #5166 + 'A' // 541c + '129L' // 541d #3365 + '165W' // 541e #4312 + '148F' // 541f #3853 + '113K' // 5420 #2948 + '23S' // 5421 #616 + '7Z' // 5422 #207 + '48D' // 5423 #1251 + '48C' // 5424 #1250 + '31A' // 5425 #806 + '219S' // 5426 #5712 + '226B' // 5427 #5877 + '113E' // 5428 #2942 + '61H' // 5429 #1593 + '36J' // 542a #945 + '222V' // 542b #5793 + '113I' // 542c #2946 + 'a23S' // 542d-542e #616 + '61K' // 542f #1596 + 'A' // 5430 + '23S' // 5431 #616 + '31A' // 5432 #806 + '208B' // 5433 #5409 + '113H' // 5434 #2945 + '165V' // 5435 #4311 + '129M' // 5436 #3366 + '31B' // 5437 #807 + '219T' // 5438 #5713 + '198R' // 5439 #5165 + 'A' // 543a + '172X' // 543b #4495 + '147H' // 543c #3829 + '23S' // 543d #616 + '161M' // 543e #4198 + '113A' // 543f #2938 + '194A' // 5440 #5044 + '31B' // 5441 #807 + '176M' // 5442 #4588 + '64J' // 5443 #1673 + '4E' // 5444 #108 + '31B' // 5445 #807 + '173T' // 5446 #4517 + '26R' // 5447 #693 + '196C' // 5448 #5098 + '253O' // 5449 #6592 + '243W' // 544a #6340 + '17E' // 544b #446 + '15U' // 544c #410 + '26R' // 544d #693 + '147F' // 544e #3827 + '4E' // 544f #108 + '3G' // 5450 #84 + '253U' // 5451 #6598 + 'a7Z' // 5452-5453 #207 + '17E' // 5454 #446 + '250P' // 5455 #6515 + '7Z' // 5456 #207 + '3H' // 5457 #85 + '3Q' // 5458 #94 + '7Z' // 5459 #207 + 'A' // 545a + 'a7Z' // 545b-545c #207 + 'A' // 545d + '4E' // 545e #108 + '252U' // 545f #6572 + '61L' // 5460 #1597 + '7Z' // 5461 #207 + '68D' // 5462 #1771 + '17E' // 5463 #446 + '15U' // 5464 #410 + 'A' // 5465 + '134T' // 5466 #3503 + '4E' // 5467 #108 + '231D' // 5468 #6009 + '112Y' // 5469 #2936 + '113P' // 546a #2953 + 'b15U' // 546b-546d #410 + '31Q' // 546e #822 + '17E' // 546f #446 + '15U' // 5470 #410 + '30Z' // 5471 #805 + '17E' // 5472 #446 + '231O' // 5473 #6020 + '15U' // 5474 #410 + '165P' // 5475 #4305 + '15U' // 5476 #410 + '48W' // 5477 #1270 + '17E' // 5478 #446 + 'A' // 5479 + '7Z' // 547a #207 + '112Z' // 547b #2937 + '205N' // 547c #5343 + '229G' // 547d #5960 + '17E' // 547e #446 + '15U' // 547f #410 + '158W' // 5480 #4130 + '172C' // 5481 #4474 + '17E' // 5482 #446 + '31Q' // 5483 #822 + '15U' // 5484 #410 + '26R' // 5485 #693 + '30Z' // 5486 #805 + '61L' // 5487 #1597 + '26R' // 5488 #693 + '4E' // 5489 #108 + '31Q' // 548a #822 + '124H' // 548b #3231 + '243G' // 548c #6324 + '15U' // 548d #410 + '30Z' // 548e #805 + '61G' // 548f #1592 + '30Z' // 5490 #805 + '15U' // 5491 #410 + '152G' // 5492 #3958 + '48A' // 5493 #1248 + '17E' // 5494 #446 + '158Y' // 5495 #4132 + '207Z' // 5496 #5407 + '162O' // 5497 #4226 + '17E' // 5498 #446 + '7Z' // 5499 #207 + '113U' // 549a #2958 + '7Z' // 549b #207 + '26R' // 549c #693 + '7Z' // 549d #207 + '48A' // 549e #1248 + 'a4E' // 549f-54a0 #108 + 'a15U' // 54a1-54a2 #410 + '17E' // 54a3 #446 + '30Z' // 54a4 #805 + '113O' // 54a5 #2952 + '48W' // 54a6 #1270 + '64J' // 54a7 #1673 + '134R' // 54a8 #3501 + '165R' // 54a9 #4307 + '193Z' // 54aa #5043 + '30Z' // 54ab #805 + '179L' // 54ac #4665 + '15U' // 54ad #410 + '26R' // 54ae #693 + '61G' // 54af #1592 + 'A' // 54b0 + '140X' // 54b1 #3663 + '125V' // 54b2 #3271 + '141W' // 54b3 #3688 + '48D' // 54b4 #1251 + '7Z' // 54b5 #207 + 'A' // 54b6 + '26R' // 54b7 #693 + '140V' // 54b8 #3661 + '61E' // 54b9 #1590 + '4E' // 54ba #108 + '20Y' // 54bb #544 + '40E' // 54bc #1044 + '136D' // 54bd #3539 + 'a20Y' // 54be-54bf #544 + '167X' // 54c0 #4365 + '35V' // 54c1 #931 + '48W' // 54c2 #1270 + '31Q' // 54c3 #822 + '124C' // 54c4 #3226 + 'A' // 54c5 + '124F' // 54c6 #3229 + '165S' // 54c7 #4308 + '207W' // 54c8 #5404 + '136T' // 54c9 #3555 + '31Q' // 54ca #822 + '137P' // 54cb #3577 + '30Y' // 54cc #804 + '124I' // 54cd #3232 + '64A' // 54ce #1664 + 'a30Y' // 54cf-54d0 #804 + '2K' // 54d1 #62 + '2R' // 54d2 #69 + '7Z' // 54d3 #207 + '3H' // 54d4 #85 + '7Z' // 54d5 #207 + '30Y' // 54d6 #804 + '2W' // 54d7 #74 + '4E' // 54d8 #108 + '7Z' // 54d9 #207 + '30Y' // 54da #804 + 'A' // 54db + 'a7Z' // 54dc-54dd #207 + '30Y' // 54de #804 + '2K' // 54df #62 + '4E' // 54e0 #108 + '243V' // 54e1 #6339 + '20Y' // 54e2 #544 + '30X' // 54e3 #803 + '26Q' // 54e4 #692 + '214T' // 54e5 #5583 + '194B' // 54e6 #5045 + '30Y' // 54e7 #804 + '129K' // 54e8 #3364 + '172D' // 54e9 #4475 + '217Z' // 54ea #5667 + '26Q' // 54eb #692 + '4E' // 54ec #108 + '193Y' // 54ed #5042 + '63J' // 54ee #1647 + '61B' // 54ef #1587 + 'A' // 54f0 + '4E' // 54f1 #108 + '189U' // 54f2 #4934 + '20Y' // 54f3 #544 + '7Z' // 54f4 #207 + 'A' // 54f5 + '4E' // 54f6 #108 + 'A' // 54f7 + 'a7Z' // 54f8-54f9 #207 + '152P' // 54fa #3967 + 'A' // 54fb + '64A' // 54fc #1664 + '20Y' // 54fd #544 + '31Q' // 54fe #822 + '20Y' // 54ff #544 + '4E' // 5500 #108 + '20Y' // 5501 #544 + '26Q' // 5502 #692 + 'A' // 5503 + '112S' // 5504 #2930 + '20Y' // 5505 #544 + '112O' // 5506 #2926 + '174A' // 5507 #4524 + '4E' // 5508 #108 + '147G' // 5509 #3828 + '26Q' // 550a #692 + 'A' // 550b + '20Y' // 550c #544 + '61E' // 550d #1590 + '112M' // 550e #2924 + '17D' // 550f #445 + '189N' // 5510 #4927 + 'b20Z' // 5511-5513 #545 + '186U' // 5514 #4856 + '4E' // 5515 #108 + '31P' // 5516 #821 + '20Z' // 5517 #545 + '30X' // 5518 #803 + '10N' // 5519 #273 + '26Q' // 551a #692 + '10N' // 551b #273 + '261A' // 551c #6786 + '10N' // 551d #273 + '20Z' // 551e #545 + 'A' // 551f + '10N' // 5520 #273 + 'A' // 5521 + '10N' // 5522 #273 + '61F' // 5523 #1591 + '2L' // 5524 #63 + '30X' // 5525 #803 + '20Z' // 5526 #545 + '17D' // 5527 #445 + '30X' // 5528 #803 + 'A' // 5529 + '17D' // 552a #445 + '61D' // 552b #1589 + '20Z' // 552c #545 + '26Q' // 552d #692 + '225Y' // 552e #5874 + '203K' // 552f #5288 + '20Z' // 5530 #545 + '202S' // 5531 #5270 + 'a17D' // 5532-5533 #445 + '10N' // 5534 #273 + '47Y' // 5535 #1246 + '40E' // 5536 #1044 + '191V' // 5537 #4987 + '152F' // 5538 #3957 + '4E' // 5539 #108 + 'A' // 553a + 'a17D' // 553b-553c #445 + '31P' // 553d #821 + '112R' // 553e #2929 + '20Z' // 553f #545 + '31P' // 5540 #821 + '17D' // 5541 #445 + 'A' // 5542 + '63M' // 5543 #1650 + '47Y' // 5544 #1246 + '40E' // 5545 #1044 + '243K' // 5546 #6328 + '17D' // 5547 #445 + '10N' // 5548 #273 + '17D' // 5549 #445 + '208A' // 554a #5408 + '20Z' // 554b #545 + '4E' // 554c #108 + '17D' // 554d #445 + '26Q' // 554e #692 + '245E' // 554f #6374 + '17D' // 5550 #445 + '40E' // 5551 #1044 + 'A' // 5552 + '112V' // 5553 #2933 + 'A' // 5554 + '20Z' // 5555 #545 + '47Y' // 5556 #1246 + '17D' // 5557 #445 + '31P' // 5558 #821 + 'A' // 5559 + '4E' // 555a #108 + '31P' // 555b #821 + '8H' // 555c #215 + '61D' // 555d #1589 + '134P' // 555e #3499 + '224H' // 555f #5831 + '4E' // 5560 #108 + '207Y' // 5561 #5406 + '35K' // 5562 #920 + '112N' // 5563 #2925 + '158Z' // 5564 #4133 + '66P' // 5565 #1731 + '68D' // 5566 #1771 + 'a10N' // 5567-5568 #273 + '10Y' // 5569 #284 + '137S' // 556a #3580 + '10Y' // 556b #284 + 'd10N' // 556c-5570 #273 + '131K' // 5571 #3416 + '162N' // 5572 #4225 + '30X' // 5573 #803 + '10N' // 5574 #273 + 'b10Y' // 5575-5577 #284 + '3G' // 5578 #84 + '35K' // 5579 #920 + 'A' // 557a + '8H' // 557b #215 + '23R' // 557c #615 + '4E' // 557d #108 + '63Z' // 557e #1663 + '8H' // 557f #215 + '134Q' // 5580 #3500 + '8H' // 5581 #215 + '140Y' // 5582 #3664 + '8H' // 5583 #215 + '221C' // 5584 #5748 + '10N' // 5585 #273 + '23R' // 5586 #615 + '179K' // 5587 #4664 + '8H' // 5588 #215 + '167E' // 5589 #4346 + '186T' // 558a #4855 + '112T' // 558b #2931 + '10N' // 558c #273 + 'A' // 558d + '63Z' // 558e #1663 + '8H' // 558f #215 + '61F' // 5590 #1591 + '8H' // 5591 #215 + '4E' // 5592 #108 + '31P' // 5593 #821 + '207X' // 5594 #5405 + 'aA' // 5595-5596 + '4E' // 5597 #108 + '148P' // 5598 #3863 + '23R' // 5599 #615 + '175R' // 559a #4567 + 'A' // 559b + '234Q' // 559c #6100 + '208O' // 559d #5422 + '4E' // 559e #108 + '8H' // 559f #215 + 'A' // 55a0 + '35K' // 55a1 #920 + 'A' // 55a2 + '35J' // 55a3 #919 + '4E' // 55a4 #108 + 'a10Y' // 55a5-55a6 #284 + '130S' // 55a7 #3398 + '8H' // 55a8 #215 + '23R' // 55a9 #615 + '174F' // 55aa #4529 + '112W' // 55ab #2934 + '186S' // 55ac #4854 + '35J' // 55ad #919 + '240D' // 55ae #6243 + 'A' // 55af + '112U' // 55b0 #2932 + '10Y' // 55b1 #284 + '140W' // 55b2 #3662 + '10Y' // 55b3 #284 + '30X' // 55b4 #803 + '184J' // 55b5 #4793 + '259Y' // 55b6 #6758 + '3N' // 55b7 #91 + 'A' // 55b8 + '10Y' // 55b9 #284 + '143T' // 55ba #3737 + '65X' // 55bb #1713 + '10Y' // 55bc #284 + 'a10N' // 55bd-55be #273 + '4E' // 55bf #108 + 'A' // 55c0 + '61B' // 55c1 #1587 + 'A' // 55c2 + '4E' // 55c3 #108 + '8H' // 55c4 #215 + '112P' // 55c5 #2927 + '158X' // 55c6 #4131 + '23R' // 55c7 #615 + 'A' // 55c8 + '8H' // 55c9 #215 + 'A' // 55ca + '4E' // 55cb #108 + '8H' // 55cc #215 + '10Y' // 55cd #284 + '225X' // 55ce #5873 + '10N' // 55cf #273 + '10Y' // 55d0 #284 + '124E' // 55d1 #3228 + '8H' // 55d2 #215 + '124G' // 55d3 #3230 + '23R' // 55d4 #615 + 'a10Y' // 55d5-55d6 #284 + 'a8H' // 55d7-55d8 #215 + '10Y' // 55d9 #284 + '140U' // 55da #3660 + '8H' // 55db #215 + '142D' // 55dc #3695 + 'a8H' // 55dd-55de #215 + '23R' // 55df #615 + 'A' // 55e0 + '10Y' // 55e1 #284 + '35J' // 55e2 #919 + '112Q' // 55e3 #2928 + '23R' // 55e4 #615 + 'a10Y' // 55e5-55e6 #284 + 'A' // 55e7 + '65X' // 55e8 #1713 + '35J' // 55e9 #919 + '10Y' // 55ea #284 + '10N' // 55eb #273 + '8H' // 55ec #215 + '10N' // 55ed #273 + '8H' // 55ee #215 + '162P' // 55ef #4227 + '63M' // 55f0 #1650 + '8H' // 55f1 #215 + '10Y' // 55f2 #284 + '10N' // 55f3 #273 + 'A' // 55f4 + '47Z' // 55f5 #1247 + 'a61C' // 55f6-55f7 #1588 + '4E' // 55f8 #108 + '35J' // 55f9 #919 + '35K' // 55fa #920 + '112X' // 55fb #2935 + 'A' // 55fc + '63J' // 55fd #1647 + '112L' // 55fe #2923 + '4E' // 55ff #108 + 'a47Z' // 5600-5601 #1247 + '35K' // 5602 #920 + 'aA' // 5603-5604 + '165Q' // 5605 #4306 + '173P' // 5606 #4513 + '4E' // 5607 #108 + '61C' // 5608 #1588 + '218Q' // 5609 #5684 + '4E' // 560a #108 + 'A' // 560b + '47Z' // 560c #1247 + '124D' // 560d #3227 + '134S' // 560e #3502 + '112C' // 560f #2914 + '4E' // 5610 #108 + '112B' // 5611 #2913 + '112D' // 5612 #2915 + '35I' // 5613 #918 + '136J' // 5614 #3545 + '35I' // 5615 #918 + '13E' // 5616 #342 + '186R' // 5617 #4853 + '255L' // 5618 #6641 + '4B' // 5619 #105 + '12S' // 561a #330 + '179J' // 561b #4663 + '23Q' // 561c #614 + 'A' // 561d + '23Q' // 561e #614 + '137R' // 561f #3579 + '35H' // 5620 #917 + '112J' // 5621 #2921 + '149S' // 5622 #3892 + '23Q' // 5623 #614 + '12S' // 5624 #330 + '23Q' // 5625 #614 + 'A' // 5626 + '23Q' // 5627 #614 + '4B' // 5628 #105 + '125M' // 5629 #3262 + '35I' // 562a #918 + '12S' // 562b #330 + '13E' // 562c #342 + 'a23Q' // 562d-562e #614 + '64I' // 562f #1672 + '13E' // 5630 #342 + '250O' // 5631 #6514 + '147C' // 5632 #3824 + '4B' // 5633 #105 + '200U' // 5634 #5220 + '35H' // 5635 #917 + '61A' // 5636 #1586 + '23P' // 5637 #613 + 'a13E' // 5638-5639 #342 + '23Q' // 563a #614 + '158V' // 563b #4129 + '27C' // 563c #704 + '4B' // 563d #105 + 'A' // 563e + '147D' // 563f #3825 + '13E' // 5640 #342 + '49A' // 5641 #1274 + '112G' // 5642 #2918 + '35H' // 5643 #917 + '4B' // 5644 #105 + 'A' // 5645 + 'a4B' // 5646-5647 #105 + 'A' // 5648 + '13E' // 5649 #342 + 'A' // 564a + '4B' // 564b #105 + '112F' // 564c #2917 + 'c13E' // 564d-5650 #342 + 'A' // 5651 + '35G' // 5652 #916 + '158T' // 5653 #4127 + '13E' // 5654 #342 + '12S' // 5655 #330 + 'A' // 5656 + '162M' // 5657 #4224 + 'b23Q' // 5658-565a #614 + '254J' // 565b #6613 + '3W' // 565c #100 + '35I' // 565d #918 + '4B' // 565e #105 + 'A' // 565f + '124A' // 5660 #3224 + '23P' // 5661 #613 + '140T' // 5662 #3659 + '27C' // 5663 #704 + '13E' // 5664 #342 + '35I' // 5665 #918 + '13E' // 5666 #342 + 'A' // 5667 + '236V' // 5668 #6157 + '111Y' // 5669 #2910 + '147E' // 566a #3826 + '61A' // 566b #1586 + '134M' // 566c #3496 + '35H' // 566d #917 + 'A' // 566e + '13E' // 566f #342 + '12S' // 5670 #330 + '13E' // 5671 #342 + '35H' // 5672 #917 + '12S' // 5673 #330 + '195U' // 5674 #5090 + '27C' // 5675 #704 + '13E' // 5676 #342 + '12S' // 5677 #330 + '64V' // 5678 #1685 + '137O' // 5679 #3576 + '13E' // 567a #342 + 'a14M' // 567b-567c #376 + 'A' // 567d + '12S' // 567e #330 + 'A' // 567f + '17C' // 5680 #444 + 'bA' // 5681-5683 + 'a10B' // 5684-5685 #261 + '40C' // 5686 #1042 + '193X' // 5687 #5041 + '4B' // 5688 #105 + '35G' // 5689 #916 + 'a23P' // 568a-568b #613 + '10B' // 568c #261 + 'A' // 568d + '14M' // 568e #376 + '10B' // 568f #261 + '169Q' // 5690 #4410 + '12S' // 5691 #330 + 'a14M' // 5692-5693 #376 + '4B' // 5694 #105 + '49A' // 5695 #1274 + 'A' // 5696 + '14M' // 5697 #376 + '40D' // 5698 #1043 + '17C' // 5699 #444 + '4B' // 569a #105 + '12S' // 569b #330 + '14M' // 569c #376 + '4B' // 569d #105 + '10B' // 569e #261 + '49A' // 569f #1274 + '4B' // 56a0 #105 + '14M' // 56a1 #376 + '251Y' // 56a2 #6550 + '2K' // 56a3 #62 + '14M' // 56a4 #376 + '40C' // 56a5 #1042 + 'a10B' // 56a6-56a7 #261 + '129J' // 56a8 #3363 + '27C' // 56a9 #704 + 'A' // 56aa + '17C' // 56ab #444 + '40C' // 56ac #1042 + '10B' // 56ad #261 + '147B' // 56ae #3823 + '14M' // 56af #376 + 'A' // 56b0 + '111X' // 56b1 #2909 + '4B' // 56b2 #105 + '17C' // 56b3 #444 + '207V' // 56b4 #5403 + '40D' // 56b5 #1043 + '10B' // 56b6 #261 + '158U' // 56b7 #4128 + '12S' // 56b8 #330 + '35G' // 56b9 #916 + 'aA' // 56ba-56bb + '134L' // 56bc #3495 + 'A' // 56bd + '4B' // 56be #105 + '14M' // 56bf #376 + '17C' // 56c0 #444 + '112E' // 56c1 #2916 + '64V' // 56c2 #1685 + '10B' // 56c3 #261 + 'A' // 56c4 + '17C' // 56c5 #444 + 'aA' // 56c6-56c7 + '17C' // 56c8 #444 + '200V' // 56c9 #5221 + '172B' // 56ca #4473 + 'a17C' // 56cb-56cc #444 + '64I' // 56cd #1672 + 'b4B' // 56ce-56d0 #105 + '40C' // 56d1 #1042 + 'A' // 56d2 + '17C' // 56d3 #444 + '14M' // 56d4 #376 + 'A' // 56d5 + '14M' // 56d6 #376 + '10B' // 56d7 #261 + 'a27C' // 56d8-56d9 #704 + '148D' // 56da #3851 + '68Q' // 56db #1784 + '4B' // 56dc #105 + '10B' // 56dd #261 + '244J' // 56de #6353 + '10B' // 56df #261 + '234G' // 56e0 #6090 + '10B' // 56e1 #261 + '112K' // 56e2 #2922 + '258S' // 56e3 #6726 + 'a10B' // 56e4-56e5 #261 + '27C' // 56e6 #704 + '134N' // 56e7 #3497 + '27C' // 56e8 #704 + '12S' // 56e9 #330 + '14M' // 56ea #376 + '10B' // 56eb #261 + '12S' // 56ec #330 + '134O' // 56ed #3498 + '10B' // 56ee #261 + '14M' // 56ef #376 + '67T' // 56f0 #1761 + '10B' // 56f1 #261 + '258I' // 56f2 #6716 + '259G' // 56f3 #6740 + '2D' // 56f4 #55 + '12S' // 56f5 #330 + '4B' // 56f6 #105 + '10B' // 56f7 #261 + '12S' // 56f8 #330 + '111W' // 56f9 #2908 + '212U' // 56fa #5532 + '35G' // 56fb #916 + '7I' // 56fc #190 + '191U' // 56fd #4986 + '7K' // 56fe #192 + 'a20X' // 56ff-5700 #543 + 'a4B' // 5701-5702 #105 + 'a47X' // 5703-5704 #1245 + 'A' // 5705 + '3I' // 5706 #86 + '17C' // 5707 #444 + '214S' // 5708 #5582 + 'a20X' // 5709-570a #543 + '246E' // 570b #6400 + '20X' // 570c #543 + '217Y' // 570d #5666 + 'A' // 570e + '255Y' // 570f #6654 + 'A' // 5710 + '4B' // 5711 #105 + '236U' // 5712 #6156 + '207U' // 5713 #5402 + 'A' // 5714 + '23P' // 5715 #613 + '240C' // 5716 #6242 + 'A' // 5717 + '232U' // 5718 #6052 + '7I' // 5719 #190 + 'a4B' // 571a-571b #105 + '20X' // 571c #543 + '23P' // 571d #613 + '7I' // 571e #190 + '223V' // 571f #5819 + 'b4B' // 5720-5722 #105 + '112A' // 5723 #2912 + '4B' // 5724 #105 + '24A' // 5725 #624 + '4B' // 5726 #105 + '257T' // 5727 #6701 + '246M' // 5728 #6408 + 'a20X' // 5729-572a #543 + 'A' // 572b + '20X' // 572c #543 + '136U' // 572d #3556 + 'a20X' // 572e-572f #543 + '246U' // 5730 #6416 + 'A' // 5731 + '35G' // 5732 #916 + '165O' // 5733 #4304 + '20X' // 5734 #543 + '7I' // 5735 #190 + 'A' // 5736 + 'a4B' // 5737-5738 #105 + '7I' // 5739 #190 + '3Q' // 573a #94 + '47X' // 573b #1245 + 'A' // 573c + '23P' // 573d #613 + '67A' // 573e #1742 + '23P' // 573f #613 + '226G' // 5740 #5882 + '112I' // 5741 #2920 + '143N' // 5742 #3731 + '112H' // 5743 #2919 + 'A' // 5744 + '24A' // 5745 #624 + '23P' // 5746 #613 + '220L' // 5747 #5731 + 'A' // 5748 + '7I' // 5749 #190 + '203A' // 574a #5278 + '40D' // 574b #1043 + '20X' // 574c #543 + '47X' // 574d #1245 + '147A' // 574e #3822 + '111Z' // 574f #2911 + '208M' // 5750 #5420 + '179I' // 5751 #4662 + '17C' // 5752 #444 + 'A' // 5753 + '40D' // 5754 #1043 + 'aA' // 5755-5756 + '155S' // 5757 #4048 + 'A' // 5758 + '4B' // 5759 #105 + '3I' // 575a #86 + '111V' // 575b #2907 + '7I' // 575c #190 + 'a2K' // 575d-575e #62 + '111L' // 575f #2897 + '2Y' // 5760 #76 + '200T' // 5761 #5219 + '4B' // 5762 #105 + '7I' // 5763 #190 + '158S' // 5764 #4126 + '4B' // 5765 #105 + '187G' // 5766 #4868 + '47U' // 5767 #1242 + 'a15T' // 5768-5769 #409 + '181C' // 576a #4708 + '15T' // 576b #409 + 'A' // 576c + '15T' // 576d #409 + '250N' // 576e #6513 + '111K' // 576f #2896 + '30V' // 5770 #801 + '4B' // 5771 #105 + '47W' // 5772 #1244 + '15T' // 5773 #409 + '111M' // 5774 #2898 + '30V' // 5775 #801 + 'A' // 5776 + '15T' // 5777 #409 + 'A' // 5778 + '4B' // 5779 #105 + '47U' // 577a #1242 + '15T' // 577b #409 + '30V' // 577c #801 + '47W' // 577d #1244 + 'a47U' // 577e-577f #1242 + '47W' // 5780 #1244 + '4B' // 5781 #105 + '168N' // 5782 #4381 + '67A' // 5783 #1742 + '2W' // 5784 #74 + 'a7I' // 5785-5786 #190 + 'A' // 5787 + '30V' // 5788 #801 + '4B' // 5789 #105 + '23O' // 578a #612 + '237L' // 578b #6173 + '15T' // 578c #409 + '23O' // 578d #612 + 'a7I' // 578e-578f #190 + '23O' // 5790 #612 + 'A' // 5791 + '3X' // 5792 #101 + '30V' // 5793 #801 + '24A' // 5794 #624 + '15T' // 5795 #409 + 'A' // 5796 + '4B' // 5797 #105 + 'A' // 5798 + '24A' // 5799 #624 + '15T' // 579a #409 + '111T' // 579b #2905 + '47V' // 579c #1243 + 'a24A' // 579d-579e #624 + '15T' // 579f #409 + '30V' // 57a0 #801 + '15T' // 57a1 #409 + '130W' // 57a2 #3402 + '130Z' // 57a3 #3405 + '15T' // 57a4 #409 + 'A' // 57a5 + '7I' // 57a6 #190 + '47V' // 57a7 #1243 + 'a24A' // 57a8-57a9 #624 + '47V' // 57aa #1243 + '4C' // 57ab #106 + '24A' // 57ac #624 + '7I' // 57ad #190 + '124B' // 57ae #3225 + '7I' // 57af #190 + '24A' // 57b0 #624 + 'a7I' // 57b1-57b2 #190 + '35F' // 57b3 #915 + '40B' // 57b4 #1041 + '20W' // 57b5 #542 + '7I' // 57b6 #190 + 'A' // 57b7 + '10A' // 57b8 #260 + '20W' // 57b9 #542 + 'a30W' // 57ba-57bb #802 + 'A' // 57bc + '36I' // 57bd #944 + '40B' // 57be #1041 + '7I' // 57bf #190 + '2P' // 57c0 #67 + 'A' // 57c1 + '20W' // 57c2 #542 + '165M' // 57c3 #4302 + '40B' // 57c4 #1041 + '7I' // 57c5 #190 + '10A' // 57c6 #260 + 'a39Z' // 57c7-57c8 #1039 + 'aA' // 57c9-57ca + '182T' // 57cb #4751 + '10A' // 57cc #260 + '7I' // 57cd #190 + '230G' // 57ce #5986 + '10A' // 57cf #260 + 'aA' // 57d0-57d1 + '10A' // 57d2 #260 + '2P' // 57d3 #67 + '186Q' // 57d4 #4852 + '140R' // 57d5 #3657 + '36I' // 57d6 #944 + '134K' // 57d7 #3494 + 'c7I' // 57d8-57db #190 + 'b10A' // 57dc-57de #260 + '213R' // 57df #5555 + '123Y' // 57e0 #3222 + '10A' // 57e1 #260 + '20W' // 57e2 #542 + '2P' // 57e3 #67 + '123Z' // 57e4 #3223 + '20W' // 57e5 #542 + '35F' // 57e6 #915 + '10A' // 57e7 #260 + 'A' // 57e8 + '2P' // 57e9 #67 + 'A' // 57ea + '7I' // 57eb #190 + 'A' // 57ec + '10A' // 57ed #260 + '30W' // 57ee #802 + '40B' // 57ef #1041 + '30U' // 57f0 #800 + 'aA' // 57f1-57f2 + '30W' // 57f3 #802 + '39Z' // 57f4 #1039 + 'a10A' // 57f5-57f6 #260 + '204K' // 57f7 #5314 + '10A' // 57f8 #260 + '202B' // 57f9 #5253 + '237K' // 57fa #6172 + '26P' // 57fb #691 + '111S' // 57fc #2904 + '10A' // 57fd #260 + '35F' // 57fe #915 + '2P' // 57ff #67 + '111P' // 5800 #2901 + '20W' // 5801 #542 + '221R' // 5802 #5763 + '10A' // 5803 #260 + '26P' // 5804 #691 + '202A' // 5805 #5252 + '187F' // 5806 #4867 + '20W' // 5807 #542 + '36H' // 5808 #943 + '39Z' // 5809 #1039 + '30U' // 580a #800 + '10A' // 580b #260 + '36I' // 580c #944 + '10A' // 580d #260 + '20W' // 580e #542 + 'A' // 580f + '30W' // 5810 #802 + '7I' // 5811 #190 + '23O' // 5812 #612 + 'A' // 5813 + '20W' // 5814 #542 + '253Z' // 5815 #6603 + 'aA' // 5816-5817 + '23O' // 5818 #612 + '10A' // 5819 #260 + 'A' // 581a + '36I' // 581b #944 + '7I' // 581c #190 + '10A' // 581d #260 + '39Z' // 581e #1039 + '36I' // 581f #944 + '9Z' // 5820 #259 + '186P' // 5821 #4851 + '40A' // 5822 #1040 + '17B' // 5823 #443 + '160M' // 5824 #4172 + '10M' // 5825 #272 + '9Z' // 5826 #259 + '36H' // 5827 #943 + 'aA' // 5828-5829 + '175Q' // 582a #4566 + 'A' // 582b + '17B' // 582c #443 + '26P' // 582d #691 + 'A' // 582e + '134J' // 582f #3493 + '111J' // 5830 #2895 + '245D' // 5831 #6373 + '2P' // 5832 #67 + 'A' // 5833 + '245F' // 5834 #6375 + '65J' // 5835 #1699 + 'bA' // 5836-5838 + '2P' // 5839 #67 + '111N' // 583a #2899 + 'aA' // 583b-583c + '12L' // 583d #323 + 'A' // 583e + 'a9Z' // 583f-5840 #259 + '252Z' // 5841 #6577 + '10M' // 5842 #272 + 'A' // 5843 + '40A' // 5844 #1040 + 'a10M' // 5845-5846 #272 + '23O' // 5847 #612 + '17B' // 5848 #443 + '2P' // 5849 #67 + '208T' // 584a #5427 + '30U' // 584b #800 + '146Z' // 584c #3821 + '9Z' // 584d #259 + 'A' // 584e + '30U' // 584f #800 + '2P' // 5850 #67 + '200S' // 5851 #5218 + '9Z' // 5852 #259 + 'A' // 5853 + '202I' // 5854 #5260 + '2P' // 5855 #67 + 'A' // 5856 + '190U' // 5857 #4960 + '165L' // 5858 #4301 + '9Z' // 5859 #259 + '111R' // 585a #2903 + 'A' // 585b + '17B' // 585c #443 + '10M' // 585d #272 + '195H' // 585e #5077 + '35F' // 585f #915 + 'A' // 5860 + '36H' // 5861 #943 + '140O' // 5862 #3654 + 'A' // 5863 + '30U' // 5864 #800 + 'aA' // 5865-5866 + '2P' // 5867 #67 + '9Z' // 5868 #259 + '111Q' // 5869 #2902 + 'A' // 586a + '201F' // 586b #5231 + '17B' // 586c #443 + '9Z' // 586d #259 + 'A' // 586e + '10M' // 586f #272 + '12L' // 5870 #323 + '17B' // 5871 #443 + '9Z' // 5872 #259 + '40A' // 5873 #1040 + 'A' // 5874 + '187Y' // 5875 #4886 + 'aA' // 5876-5877 + '2P' // 5878 #67 + '39Y' // 5879 #1038 + 'aA' // 587a-587b + '30U' // 587c #800 + '17B' // 587d #443 + '111O' // 587e #2900 + '26P' // 587f #691 + 'a9Z' // 5880-5881 #259 + 'A' // 5882 + '229V' // 5883 #5975 + 'A' // 5884 + '65J' // 5885 #1699 + 'A' // 5886 + '26P' // 5887 #691 + '9Z' // 5888 #259 + '39Y' // 5889 #1038 + '67J' // 588a #1751 + 'a2P' // 588b-588c #67 + '12L' // 588d #323 + '30W' // 588e #802 + '2P' // 588f #67 + '26P' // 5890 #691 + '30W' // 5891 #802 + '17B' // 5892 #443 + '167R' // 5893 #4359 + '2P' // 5894 #67 + 'A' // 5895 + '9Z' // 5896 #259 + '258M' // 5897 #6720 + '9Z' // 5898 #259 + '111U' // 5899 #2906 + '40A' // 589a #1040 + 'A' // 589b + '166M' // 589c #4328 + '9Z' // 589d #259 + '225W' // 589e #5872 + '135L' // 589f #3521 + '12L' // 58a0 #323 + '9Z' // 58a1 #259 + '2P' // 58a2 #67 + '17B' // 58a3 #443 + 'aA' // 58a4-58a5 + '9Z' // 58a6 #259 + '23O' // 58a7 #612 + '196B' // 58a8 #5097 + '39Y' // 58a9 #1038 + '35F' // 58aa #915 + '12L' // 58ab #323 + '17B' // 58ac #443 + '10M' // 58ad #272 + '146Y' // 58ae #3820 + 'A' // 58af + '17B' // 58b0 #443 + '26P' // 58b1 #691 + '2P' // 58b2 #67 + '129H' // 58b3 #3361 + 'A' // 58b4 + 'a23O' // 58b5-58b6 #612 + 'A' // 58b7 + 'a2P' // 58b8-58b9 #67 + '36H' // 58ba #943 + '39Y' // 58bb #1038 + '9Z' // 58bc #259 + '10M' // 58bd #272 + '152E' // 58be #3956 + '10M' // 58bf #272 + 'A' // 58c0 + '198O' // 58c1 #5162 + '30T' // 58c2 #799 + '12L' // 58c3 #323 + '2P' // 58c4 #67 + '26O' // 58c5 #690 + '60Z' // 58c6 #1585 + '209J' // 58c7 #5443 + '12L' // 58c8 #323 + 'A' // 58c9 + '256Z' // 58ca #6681 + '23M' // 58cb #610 + '252K' // 58cc #6562 + '2P' // 58cd #67 + '26O' // 58ce #690 + 'A' // 58cf + '39X' // 58d0 #1037 + '26O' // 58d1 #690 + '12L' // 58d2 #323 + '217V' // 58d3 #5663 + '2P' // 58d4 #67 + '26O' // 58d5 #690 + 'a2P' // 58d6-58d7 #67 + '158Q' // 58d8 #4124 + '26O' // 58d9 #690 + '39X' // 58da #1037 + 'A' // 58db + '60V' // 58dc #1581 + '2P' // 58dd #67 + '67W' // 58de #1764 + '26O' // 58df #690 + '39X' // 58e0 #1037 + '2P' // 58e1 #67 + '165N' // 58e2 #4303 + 'A' // 58e3 + '140P' // 58e4 #3655 + '2P' // 58e5 #67 + 'bA' // 58e6-58e8 + '30T' // 58e9 #799 + 'A' // 58ea + '236D' // 58eb #6139 + '26O' // 58ec #690 + 'A' // 58ed + '254L' // 58ee #6615 + '179G' // 58ef #4660 + '143P' // 58f0 #3733 + '253S' // 58f1 #6596 + '111H' // 58f2 #2893 + '110X' // 58f3 #2883 + '2P' // 58f4 #67 + '10M' // 58f5 #272 + '2R' // 58f6 #69 + '12L' // 58f7 #323 + '10M' // 58f8 #272 + '186O' // 58f9 #4850 + '173G' // 58fa #4504 + '110U' // 58fb #2880 + '39X' // 58fc #1037 + '67N' // 58fd #1755 + 'aA' // 58fe-58ff + '10M' // 5900 #272 + 'A' // 5901 + '30T' // 5902 #799 + 'A' // 5903 + '63N' // 5904 #1651 + '60V' // 5905 #1581 + '30T' // 5906 #799 + '143X' // 5907 #3741 + '10M' // 5908 #272 + '260E' // 5909 #6764 + '30T' // 590a #799 + '12L' // 590b #323 + '30T' // 590c #799 + '140S' // 590d #3658 + '60Z' // 590e #1585 + '216K' // 590f #5626 + '12L' // 5910 #323 + '19B' // 5911 #495 + 'a2P' // 5912-5913 #67 + '39W' // 5914 #1036 + '176L' // 5915 #4587 + '69B' // 5916 #1795 + 'A' // 5917 + '2P' // 5918 #67 + '39W' // 5919 #1036 + '69H' // 591a #1801 + '12L' // 591b #323 + '223F' // 591c #5803 + '110V' // 591d #2881 + '10M' // 591e #272 + '110Y' // 591f #2884 + '224G' // 5920 #5830 + '2P' // 5921 #67 + '221Q' // 5922 #5762 + '12L' // 5923 #323 + '30S' // 5924 #798 + '67J' // 5925 #1751 + 'A' // 5926 + '247C' // 5927 #6424 + '12L' // 5928 #323 + '243S' // 5929 #6336 + '236C' // 592a #6138 + '216P' // 592b #5631 + '30S' // 592c #798 + '123X' // 592d #3221 + '205X' // 592e #5353 + '179H' // 592f #4661 + '2P' // 5930 #67 + '229F' // 5931 #5959 + '30S' // 5932 #798 + '2P' // 5933 #67 + '64Z' // 5934 #1689 + '2P' // 5935 #67 + '12L' // 5936 #323 + '152O' // 5937 #3966 + '110W' // 5938 #2882 + '250M' // 5939 #6512 + '1R' // 593a #43 + 'A' // 593b + '10M' // 593c #272 + '2P' // 593d #67 + '67W' // 593e #1764 + '2P' // 593f #67 + '111I' // 5940 #2894 + 'a10M' // 5941-5942 #272 + '2P' // 5943 #67 + '110Z' // 5944 #2885 + 'A' // 5945 + '2P' // 5946 #67 + '227M' // 5947 #5914 + '199F' // 5948 #5179 + '174Z' // 5949 #4549 + 'A' // 594a + '1R' // 594b #43 + 'a10M' // 594c-594d #272 + '110T' // 594e #2879 + '182X' // 594f #4755 + '39W' // 5950 #1036 + '183Q' // 5951 #4774 + '2P' // 5952 #67 + '30S' // 5953 #798 + '179Z' // 5954 #4679 + '158R' // 5955 #4125 + '2D' // 5956 #55 + '217W' // 5957 #5664 + '30S' // 5958 #798 + '2P' // 5959 #67 + '39W' // 595a #1036 + '2P' // 595b #67 + 'A' // 595c + 'b2P' // 595d-595f #67 + '129I' // 5960 #3362 + '30S' // 5961 #798 + '173H' // 5962 #4505 + '2P' // 5963 #67 + 'A' // 5964 + '126B' // 5965 #3277 + '10M' // 5966 #272 + '67N' // 5967 #1755 + '256K' // 5968 #6666 + '60X' // 5969 #1583 + '189T' // 596a #4933 + '36H' // 596b #943 + 'a110S' // 596c-596d #2878 + '182S' // 596e #4750 + '2P' // 596f #67 + 'aA' // 5970-5971 + '2P' // 5972 #67 + '244C' // 5973 #6346 + '169B' // 5974 #4395 + '60X' // 5975 #1583 + '207T' // 5976 #5401 + '9Y' // 5977 #258 + '140Q' // 5978 #3656 + '217X' // 5979 #5665 + 'A' // 597a + 'a60W' // 597b-597c #1582 + '243P' // 597d #6333 + '19B' // 597e #495 + 'A' // 597f + '19B' // 5980 #495 + '60W' // 5981 #1582 + '241A' // 5982 #6266 + '160P' // 5983 #4175 + '143E' // 5984 #3722 + 'A' // 5985 + '3Y' // 5986 #102 + 'a3I' // 5987-5988 #86 + '23M' // 5989 #610 + '111E' // 598a #2890 + 'a5S' // 598b-598c #148 + '158P' // 598d #4123 + '5S' // 598e #148 + '9Y' // 598f #258 + 'aA' // 5990-5991 + '129G' // 5992 #3360 + '129U' // 5993 #3374 + '23M' // 5994 #610 + '5S' // 5995 #148 + '175U' // 5996 #4570 + '110R' // 5997 #2877 + '9Y' // 5998 #258 + '198A' // 5999 #5148 + '60Y' // 599a #1584 + '5S' // 599b #148 + '7Y' // 599c #206 + '214R' // 599d #5581 + '169T' // 599e #4413 + '23N' // 599f #611 + 'a9Y' // 59a0-59a1 #258 + '19B' // 59a2 #495 + '8Y' // 59a3 #232 + '63Y' // 59a4 #1662 + '166Y' // 59a5 #4340 + '19B' // 59a6 #495 + '8Y' // 59a7 #232 + '160F' // 59a8 #4165 + '3W' // 59a9 #100 + 'a7Y' // 59aa-59ab #206 + '111C' // 59ac #2888 + '49V' // 59ad #1295 + '186N' // 59ae #4849 + '8Y' // 59af #232 + '39V' // 59b0 #1035 + '19B' // 59b1 #495 + '8Y' // 59b2 #232 + '193W' // 59b3 #5040 + 'A' // 59b4 + '7Y' // 59b5 #206 + '9Y' // 59b6 #258 + '39V' // 59b7 #1035 + '9Y' // 59b8 #258 + '215M' // 59b9 #5602 + '8Y' // 59ba #232 + '205I' // 59bb #5338 + '5S' // 59bc #148 + '7Y' // 59bd #206 + '26N' // 59be #689 + '23M' // 59bf #610 + '7Y' // 59c0 #206 + '8Y' // 59c1 #232 + 'A' // 59c2 + '26N' // 59c3 #689 + '47T' // 59c4 #1241 + 'A' // 59c5 + '186L' // 59c6 #4847 + '9Y' // 59c7 #258 + '8Y' // 59c8 #232 + '111G' // 59c9 #2892 + '186M' // 59ca #4848 + '231G' // 59cb #6012 + '7Y' // 59cc #206 + '8Y' // 59cd #232 + '19B' // 59ce #495 + '7Y' // 59cf #206 + '208J' // 59d0 #5417 + '180H' // 59d1 #4687 + '8Y' // 59d2 #232 + '201N' // 59d3 #5239 + '211I' // 59d4 #5494 + '7Y' // 59d5 #206 + '9Y' // 59d6 #258 + '3X' // 59d7 #101 + '9Y' // 59d8 #258 + '26N' // 59d9 #689 + '158O' // 59da #4122 + '7Y' // 59db #206 + '153I' // 59dc #3986 + '26N' // 59dd #689 + '8Y' // 59de #232 + '49V' // 59df #1295 + '9Y' // 59e0 #258 + 'A' // 59e1 + '7Y' // 59e2 #206 + 'b8Y' // 59e3-59e5 #232 + '161N' // 59e6 #4199 + '49V' // 59e7 #1295 + '152C' // 59e8 #3954 + '9Y' // 59e9 #258 + '111A' // 59ea #2886 + '111F' // 59eb #2891 + '165K' // 59ec #4300 + 'A' // 59ed + '26N' // 59ee #689 + '39V' // 59ef #1035 + '23M' // 59f0 #610 + '8Y' // 59f1 #232 + '23N' // 59f2 #611 + '9Y' // 59f3 #258 + '5S' // 59f4 #148 + '9Y' // 59f5 #258 + '23N' // 59f6 #611 + '8Y' // 59f7 #232 + '26N' // 59f8 #689 + '9Y' // 59f9 #258 + 'A' // 59fa + '172Q' // 59fb #4488 + '19B' // 59fc #495 + '9Y' // 59fd #258 + 'A' // 59fe + '191J' // 59ff #4975 + '23N' // 5a00 #611 + '214Z' // 5a01 #5589 + '23M' // 5a02 #610 + '66Z' // 5a03 #1741 + '250L' // 5a04 #6511 + '2K' // 5a05 #62 + '7Y' // 5a06 #206 + '4C' // 5a07 #106 + '7Y' // 5a08 #206 + '8Y' // 5a09 #232 + '7Y' // 5a0a #206 + '23M' // 5a0b #610 + '8Y' // 5a0c #232 + '47T' // 5a0d #1241 + '5S' // 5a0e #148 + 'aA' // 5a0f-5a10 + '26N' // 5a11 #689 + '47T' // 5a12 #1241 + '8Y' // 5a13 #232 + 'A' // 5a14 + '7Y' // 5a15 #206 + '9Y' // 5a16 #258 + '23N' // 5a17 #611 + '205T' // 5a18 #5349 + 'A' // 5a19 + '8Y' // 5a1a #232 + '225V' // 5a1b #5871 + '193V' // 5a1c #5039 + 'A' // 5a1d + '23N' // 5a1e #611 + '65I' // 5a1f #1698 + '111D' // 5a20 #2889 + '60Y' // 5a21 #1584 + '7Y' // 5a22 #206 + '8Y' // 5a23 #232 + '23N' // 5a24 #611 + '140N' // 5a25 #3653 + 'A' // 5a26 + '39V' // 5a27 #1035 + '5S' // 5a28 #148 + '111B' // 5a29 #2887 + '23N' // 5a2a #611 + '19B' // 5a2b #495 + '23M' // 5a2c #610 + '8Y' // 5a2d #232 + '9Y' // 5a2e #258 + '253E' // 5a2f #6582 + '5S' // 5a30 #148 + '1Z' // 5a31 #51 + '7Y' // 5a32 #206 + '19B' // 5a33 #495 + '2W' // 5a34 #74 + '5S' // 5a35 #148 + '158N' // 5a36 #4121 + 'A' // 5a37 + '26M' // 5a38 #688 + 'bA' // 5a39-5a3b + '110O' // 5a3c #2874 + '9G' // 5a3d #240 + 'a7Y' // 5a3e-5a3f #206 + '35E' // 5a40 #914 + '110I' // 5a41 #2868 + 'a26M' // 5a42-5a43 #688 + '35E' // 5a44 #914 + '110K' // 5a45 #2870 + '195D' // 5a46 #5073 + '35E' // 5a47 #914 + '110N' // 5a48 #2873 + '152B' // 5a49 #3953 + '26M' // 5a4a #688 + 'A' // 5a4b + '35E' // 5a4c #914 + '26M' // 5a4d #688 + 'A' // 5a4e + '7Y' // 5a4f #206 + '35E' // 5a50 #914 + '26M' // 5a51 #688 + 'A' // 5a52 + '26M' // 5a53 #688 + '9G' // 5a54 #240 + '63Y' // 5a55 #1662 + '26M' // 5a56 #688 + '30R' // 5a57 #797 + 'A' // 5a58 + '9G' // 5a59 #240 + '216J' // 5a5a #5625 + 'aA' // 5a5b-5a5c + '20V' // 5a5d #541 + '16Z' // 5a5e #441 + 'A' // 5a5f + '30R' // 5a60 #797 + '20V' // 5a61 #541 + '110H' // 5a62 #2867 + '35D' // 5a63 #913 + 'A' // 5a64 + '16Z' // 5a65 #441 + '212N' // 5a66 #5525 + '16Z' // 5a67 #441 + '9G' // 5a68 #240 + 'A' // 5a69 + '16Z' // 5a6a #441 + '9G' // 5a6b #240 + 'a16Z' // 5a6c-5a6d #441 + '9G' // 5a6e #240 + 'aA' // 5a6f-5a70 + '9G' // 5a71 #240 + 'a7Y' // 5a72-5a73 #206 + '3Y' // 5a74 #102 + 'a7Y' // 5a75-5a76 #206 + '172A' // 5a77 #4472 + 'A' // 5a78 + '110Q' // 5a79 #2876 + 'a16Z' // 5a7a-5a7b #441 + '30R' // 5a7c #797 + 'A' // 5a7d + '35D' // 5a7e #913 + '124S' // 5a7f #3242 + 'A' // 5a80 + 'b30R' // 5a81-5a83 #797 + '16Z' // 5a84 #441 + 'A' // 5a85 + '9G' // 5a86 #240 + 'A' // 5a87 + '9G' // 5a88 #240 + 'aA' // 5a89-5a8a + '5S' // 5a8b #148 + '30R' // 5a8c #797 + 'A' // 5a8d + '7Y' // 5a8e #206 + 'A' // 5a8f + '17A' // 5a90 #442 + '9G' // 5a91 #240 + '218P' // 5a92 #5683 + '16Z' // 5a93 #441 + 'aA' // 5a94-5a95 + '16Z' // 5a96 #441 + '30R' // 5a97 #797 + 'A' // 5a98 + '35D' // 5a99 #913 + '154G' // 5a9a #4010 + '155F' // 5a9b #4035 + '16Z' // 5a9c #441 + 'A' // 5a9d + '16Z' // 5a9e #441 + '17A' // 5a9f #442 + '4D' // 5aa0 #107 + '9G' // 5aa1 #240 + '5S' // 5aa2 #148 + '8T' // 5aa3 #227 + '60U' // 5aa4 #1580 + 'aA' // 5aa5-5aa6 + '17A' // 5aa7 #442 + 'aA' // 5aa8-5aa9 + '47S' // 5aaa #1240 + '20V' // 5aab #541 + '17A' // 5aac #442 + 'A' // 5aad + 'a9X' // 5aae-5aaf #257 + 'A' // 5ab0 + '4D' // 5ab1 #107 + '48Y' // 5ab2 #1272 + '152D' // 5ab3 #3955 + '9X' // 5ab4 #257 + '4D' // 5ab5 #107 + 'A' // 5ab6 + '8T' // 5ab7 #227 + '4D' // 5ab8 #107 + '8T' // 5ab9 #227 + '4D' // 5aba #107 + 'a17A' // 5abb-5abc #442 + '217T' // 5abd #5661 + 'a4D' // 5abe-5abf #107 + 'A' // 5ac0 + '183G' // 5ac1 #4764 + '65I' // 5ac2 #1698 + '9X' // 5ac3 #257 + '19A' // 5ac4 #494 + 'A' // 5ac5 + '4D' // 5ac6 #107 + '9X' // 5ac7 #257 + '4D' // 5ac8 #107 + '110P' // 5ac9 #2875 + '9X' // 5aca #257 + '4D' // 5acb #107 + '183S' // 5acc #4776 + '8T' // 5acd #227 + '9G' // 5ace #240 + '4D' // 5acf #107 + '27B' // 5ad0 #703 + '9X' // 5ad1 #257 + '8T' // 5ad2 #227 + '9G' // 5ad3 #240 + '8T' // 5ad4 #227 + '20V' // 5ad5 #541 + '110L' // 5ad6 #2871 + '17A' // 5ad7 #442 + 'a9X' // 5ad8-5ad9 #257 + '4D' // 5ada #107 + '20V' // 5adb #541 + '4D' // 5adc #107 + 'aA' // 5add-5ade + '8T' // 5adf #227 + '4D' // 5ae0 #107 + '19A' // 5ae1 #494 + '8T' // 5ae2 #227 + '110M' // 5ae3 #2872 + '47S' // 5ae4 #1240 + '17A' // 5ae5 #442 + '19A' // 5ae6 #494 + 'A' // 5ae7 + '9X' // 5ae8 #257 + '66Z' // 5ae9 #1741 + '4D' // 5aea #107 + '9X' // 5aeb #257 + 'a8T' // 5aec-5aed #227 + '17A' // 5aee #442 + 'A' // 5aef + '4D' // 5af0 #107 + '8T' // 5af1 #227 + '9X' // 5af2 #257 + '8T' // 5af3 #227 + 'A' // 5af4 + '4D' // 5af5 #107 + '5S' // 5af6 #148 + 'aA' // 5af7-5af8 + '8T' // 5af9 #227 + '4D' // 5afa #107 + '48Y' // 5afb #1272 + 'A' // 5afc + '27B' // 5afd #703 + '9G' // 5afe #240 + '20V' // 5aff #541 + '5S' // 5b00 #148 + '4D' // 5b01 #107 + 'bA' // 5b02-5b04 + '60U' // 5b05 #1580 + 'aA' // 5b06-5b07 + '4D' // 5b08 #107 + '137G' // 5b09 #3568 + 'A' // 5b0a + '19A' // 5b0b #494 + '172W' // 5b0c #4494 + '9G' // 5b0d #240 + 'bA' // 5b0e-5b10 + '9X' // 5b11 #257 + 'bA' // 5b12-5b14 + '9G' // 5b15 #240 + '19A' // 5b16 #494 + '4D' // 5b17 #107 + 'A' // 5b18 + '17A' // 5b19 #442 + 'A' // 5b1a + '4D' // 5b1b #107 + 'A' // 5b1c + '17A' // 5b1d #442 + 'A' // 5b1e + '9G' // 5b1f #240 + 'A' // 5b20 + '4D' // 5b21 #107 + '255K' // 5b22 #6640 + '20V' // 5b23 #541 + '149R' // 5b24 #3891 + '5S' // 5b25 #148 + 'A' // 5b26 + '8T' // 5b27 #227 + '9X' // 5b28 #257 + '8T' // 5b29 #227 + '19A' // 5b2a #494 + '9X' // 5b2b #257 + 'a4D' // 5b2c-5b2d #107 + '8T' // 5b2e #227 + 'A' // 5b2f + '200R' // 5b30 #5217 + 'A' // 5b31 + '4D' // 5b32 #107 + 'A' // 5b33 + '4D' // 5b34 #107 + 'A' // 5b35 + '27B' // 5b36 #703 + '8T' // 5b37 #227 + '4D' // 5b38 #107 + 'dA' // 5b39-5b3d + '4D' // 5b3e #107 + '9X' // 5b3f #257 + '19A' // 5b40 #494 + '35D' // 5b41 #913 + 'A' // 5b42 + '19A' // 5b43 #494 + '20V' // 5b44 #541 + '4D' // 5b45 #107 + '20V' // 5b46 #541 + 'bA' // 5b47-5b49 + '47S' // 5b4a #1240 + '4D' // 5b4b #107 + '17A' // 5b4c #442 + 'aA' // 5b4d-5b4e + '9G' // 5b4f #240 + '246V' // 5b50 #6417 + '19A' // 5b51 #494 + '27B' // 5b52 #703 + '9X' // 5b53 #257 + '194Z' // 5b54 #5069 + '201J' // 5b55 #5235 + '48Y' // 5b56 #1272 + '242B' // 5b57 #6293 + '230Q' // 5b58 #5996 + '3Y' // 5b59 #102 + '63I' // 5b5a #1646 + '4D' // 5b5b #107 + '110J' // 5b5c #2869 + '196H' // 5b5d #5103 + '5S' // 5b5e #148 + '171Z' // 5b5f #4471 + 'aA' // 5b60-5b61 + '9X' // 5b62 #257 + '222I' // 5b63 #5780 + '188T' // 5b64 #4907 + '4D' // 5b65 #107 + '169P' // 5b66 #4409 + '8T' // 5b67 #227 + '35D' // 5b68 #913 + '217S' // 5b69 #5660 + '8T' // 5b6a #227 + '196R' // 5b6b #5113 + 'a9X' // 5b6c-5b6d #257 + '4D' // 5b6e #107 + '5S' // 5b6f #148 + '19A' // 5b70 #494 + '60Q' // 5b71 #1576 + '110G' // 5b72 #2866 + '60R' // 5b73 #1577 + '35C' // 5b74 #912 + '109Q' // 5b75 #2850 + '47Q' // 5b76 #1238 + 'A' // 5b77 + '240N' // 5b78 #6253 + 'A' // 5b79 + '60Q' // 5b7a #1576 + '110C' // 5b7b #2862 + '109P' // 5b7c #2849 + '109T' // 5b7d #2853 + '5S' // 5b7e #148 + 'a60R' // 5b7f-5b80 #1577 + '109U' // 5b81 #2854 + '109V' // 5b82 #2855 + '217U' // 5b83 #5662 + '109S' // 5b84 #2852 + '213Q' // 5b85 #5554 + '5S' // 5b86 #148 + '205H' // 5b87 #5337 + '212T' // 5b88 #5531 + '244A' // 5b89 #6344 + '5S' // 5b8a #148 + '179F' // 5b8b #4659 + '242Q' // 5b8c #6308 + '27B' // 5b8d #703 + '5S' // 5b8e #148 + '196E' // 5b8f #5100 + '47Q' // 5b90 #1238 + '27B' // 5b91 #703 + '15S' // 5b92 #408 + '47R' // 5b93 #1239 + '27B' // 5b94 #703 + '109W' // 5b95 #2856 + '109R' // 5b96 #2851 + '67R' // 5b97 #1759 + '227V' // 5b98 #5923 + '169A' // 5b99 #4394 + '69C' // 5b9a #1796 + '154F' // 5b9b #4009 + '219D' // 5b9c #5697 + '169I' // 5b9d #4402 + '35T' // 5b9e #929 + '110B' // 5b9f #2861 + '3Y' // 5ba0 #102 + '2C' // 5ba1 #54 + '242N' // 5ba2 #6305 + '210M' // 5ba3 #5472 + '230Y' // 5ba4 #6004 + '134I' // 5ba5 #3492 + '47R' // 5ba6 #1239 + '15S' // 5ba7 #408 + '60S' // 5ba8 #1578 + '5S' // 5ba9 #148 + '110F' // 5baa #2865 + '3Y' // 5bab #102 + '47R' // 5bac #1239 + '60S' // 5bad #1578 + '213V' // 5bae #5559 + '5S' // 5baf #148 + '148I' // 5bb0 #3856 + 'a5S' // 5bb1-5bb2 #148 + '222S' // 5bb3 #5790 + '189S' // 5bb4 #4932 + '159V' // 5bb5 #4155 + '246Q' // 5bb6 #6412 + '47Q' // 5bb7 #1238 + '63I' // 5bb8 #1646 + '243J' // 5bb9 #6327 + '5S' // 5bba #148 + 'A' // 5bbb + '1S' // 5bbc #44 + 'a3Y' // 5bbd-5bbe #102 + '213G' // 5bbf #5544 + '30Q' // 5bc0 #796 + '23L' // 5bc1 #609 + '161J' // 5bc2 #4195 + '30Q' // 5bc3 #796 + '222U' // 5bc4 #5792 + '130J' // 5bc5 #3389 + '228N' // 5bc6 #5941 + '129F' // 5bc7 #3359 + 'A' // 5bc8 + '1S' // 5bc9 #44 + 'A' // 5bca + '8T' // 5bcb #227 + '230E' // 5bcc #5984 + 'b1S' // 5bcd-5bcf #44 + '30Q' // 5bd0 #796 + '60T' // 5bd1 #1579 + '198F' // 5bd2 #5153 + '171Y' // 5bd3 #4470 + '30Q' // 5bd4 #796 + '60T' // 5bd5 #1579 + '60P' // 5bd6 #1575 + '30Q' // 5bd7 #796 + '60P' // 5bd8 #1575 + '1S' // 5bd9 #44 + '250J' // 5bda #6509 + '109Y' // 5bdb #2858 + '8T' // 5bdc #227 + '258A' // 5bdd #6708 + '146X' // 5bde #3819 + '211V' // 5bdf #5507 + '1S' // 5be0 #44 + '146V' // 5be1 #3817 + '171X' // 5be2 #4469 + 'A' // 5be3 + 'a30Q' // 5be4-5be5 #796 + '240A' // 5be6 #6240 + '204D' // 5be7 #5307 + '152A' // 5be8 #3952 + '197U' // 5be9 #5142 + 'A' // 5bea + '225U' // 5beb #5870 + '200P' // 5bec #5215 + 'A' // 5bed + '160S' // 5bee #4178 + '47O' // 5bef #1236 + '63X' // 5bf0 #1661 + '1S' // 5bf1 #44 + '7X' // 5bf2 #205 + '8G' // 5bf3 #214 + '1S' // 5bf4 #44 + '217R' // 5bf5 #5659 + '225T' // 5bf6 #5869 + 'A' // 5bf7 + '67R' // 5bf8 #1759 + '7K' // 5bf9 #192 + '176T' // 5bfa #4595 + '3I' // 5bfb #86 + '3Q' // 5bfc #94 + '1S' // 5bfd #44 + '260D' // 5bfe #6763 + '109Z' // 5bff #2859 + 'A' // 5c00 + '210G' // 5c01 #5466 + '259R' // 5c02 #6751 + '8G' // 5c03 #214 + '211L' // 5c04 #5497 + '8G' // 5c05 #214 + '257F' // 5c06 #6687 + 'a239Z' // 5c07-5c08 #6239 + '47O' // 5c09 #1236 + '196V' // 5c0a #5117 + '233K' // 5c0b #6068 + '8G' // 5c0c #214 + '240B' // 5c0d #6241 + '234V' // 5c0e #6105 + '69J' // 5c0f #1803 + '12H' // 5c10 #319 + '238N' // 5c11 #6201 + 'a8G' // 5c12-5c13 #214 + '123W' // 5c14 #3220 + '12H' // 5c15 #319 + '194V' // 5c16 #5065 + '31O' // 5c17 #820 + '1R' // 5c18 #43 + '250I' // 5c19 #6508 + '233O' // 5c1a #6072 + '7X' // 5c1b #205 + '39U' // 5c1c #1034 + '1R' // 5c1d #43 + 'a8G' // 5c1e-5c1f #214 + '60M' // 5c20 #1572 + 'A' // 5c21 + '8G' // 5c22 #214 + '60M' // 5c23 #1572 + '200Q' // 5c24 #5216 + '12H' // 5c25 #319 + '1S' // 5c26 #44 + '2W' // 5c27 #74 + '47O' // 5c28 #1236 + '1S' // 5c29 #44 + '8G' // 5c2a #214 + '31O' // 5c2b #820 + '165J' // 5c2c #4299 + 'a1S' // 5c2d-5c2e #44 + '7X' // 5c2f #205 + '31O' // 5c30 #820 + '241G' // 5c31 #6272 + '1S' // 5c32 #44 + 'A' // 5c33 + '2Y' // 5c34 #76 + 'a1S' // 5c35-5c36 #44 + '155Q' // 5c37 #4046 + '109N' // 5c38 #2847 + '146W' // 5c39 #3818 + '208V' // 5c3a #5429 + '125X' // 5c3b #3273 + '218L' // 5c3c #5679 + '255R' // 5c3d #6647 + '212F' // 5c3e #5517 + '189L' // 5c3f #4925 + '222H' // 5c40 #5779 + '172R' // 5c41 #4489 + '2C' // 5c42 #54 + '7X' // 5c43 #205 + '12H' // 5c44 #319 + '229M' // 5c45 #5966 + '193U' // 5c46 #5038 + '12H' // 5c47 #319 + '168M' // 5c48 #4380 + '110D' // 5c49 #2863 + '110A' // 5c4a #2860 + '223X' // 5c4b #5821 + '149V' // 5c4c #3895 + '179D' // 5c4d #4657 + '151Y' // 5c4e #3950 + '207S' // 5c4f #5400 + '8G' // 5c50 #214 + '135R' // 5c51 #3527 + 'A' // 5c52 + '23L' // 5c53 #609 + 'A' // 5c54 + '235I' // 5c55 #6118 + 'aA' // 5c56-5c57 + '15S' // 5c58 #408 + '8G' // 5c59 #214 + '1S' // 5c5a #44 + '70J' // 5c5b #1829 + '63H' // 5c5c #1645 + '15S' // 5c5d #408 + '126E' // 5c5e #3280 + '1S' // 5c5f #44 + '146U' // 5c60 #3816 + '250K' // 5c61 #6510 + '48Z' // 5c62 #1273 + '8G' // 5c63 #214 + '219N' // 5c64 #5707 + '169K' // 5c65 #4404 + '7X' // 5c66 #205 + '1S' // 5c67 #44 + '23L' // 5c68 #609 + '1S' // 5c69 #44 + 'aA' // 5c6a-5c6b + '225S' // 5c6c #5868 + '23L' // 5c6d #609 + '8G' // 5c6e #214 + '179E' // 5c6f #4658 + '1S' // 5c70 #44 + '238Y' // 5c71 #6212 + 'aA' // 5c72-5c73 + '8G' // 5c74 #214 + 'a1S' // 5c75-5c76 #44 + 'A' // 5c77 + '39U' // 5c78 #1034 + '30P' // 5c79 #795 + 'b8G' // 5c7a-5c7c #214 + '31O' // 5c7d #820 + '12H' // 5c7e #319 + '3G' // 5c7f #84 + '7X' // 5c80 #205 + '2C' // 5c81 #54 + '3G' // 5c82 #84 + '39U' // 5c83 #1034 + '7X' // 5c84 #205 + '35C' // 5c85 #912 + '15S' // 5c86 #408 + '1S' // 5c87 #44 + '8G' // 5c88 #214 + 'A' // 5c89 + '23L' // 5c8a #609 + '12H' // 5c8b #319 + '8G' // 5c8c #214 + '12H' // 5c8d #319 + 'A' // 5c8e + '1S' // 5c8f #44 + '137F' // 5c90 #3567 + '48Z' // 5c91 #1273 + '23L' // 5c92 #609 + '15S' // 5c93 #408 + '8G' // 5c94 #214 + '12H' // 5c95 #319 + '7X' // 5c96 #205 + '1R' // 5c97 #43 + '7X' // 5c98 #205 + '12H' // 5c99 #319 + '110E' // 5c9a #2864 + '3I' // 5c9b #86 + '12H' // 5c9c #319 + '1S' // 5c9d #44 + '39U' // 5c9e #1034 + '1S' // 5c9f #44 + '23L' // 5ca0 #609 + '184G' // 5ca1 #4790 + 'a8G' // 5ca2-5ca3 #214 + 'A' // 5ca4 + '15S' // 5ca5 #408 + 'a23L' // 5ca6-5ca7 #609 + '1S' // 5ca8 #44 + '176U' // 5ca9 #4596 + '1S' // 5caa #44 + '30P' // 5cab #795 + '109X' // 5cac #2857 + '109O' // 5cad #2848 + 'bA' // 5cae-5cb0 + '129E' // 5cb1 #3358 + '1S' // 5cb2 #44 + '161G' // 5cb3 #4192 + '1S' // 5cb4 #44 + '30P' // 5cb5 #795 + '1S' // 5cb6 #44 + '30P' // 5cb7 #795 + '204C' // 5cb8 #5306 + 'A' // 5cb9 + '30P' // 5cba #795 + 'a1S' // 5cbb-5cbc #44 + '7X' // 5cbd #205 + '70J' // 5cbe #1829 + '7X' // 5cbf #205 + '71E' // 5cc0 #1850 + '12H' // 5cc1 #319 + '35C' // 5cc2 #912 + 'a7X' // 5cc3-5cc4 #205 + '1S' // 5cc5 #44 + 'A' // 5cc6 + '63X' // 5cc7 #1661 + '15S' // 5cc8 #408 + '1S' // 5cc9 #44 + '15S' // 5cca #408 + '8G' // 5ccb #214 + 'aA' // 5ccc-5ccd + '15S' // 5cce #408 + 'A' // 5ccf + '1S' // 5cd0 #44 + '35C' // 5cd1 #912 + '8G' // 5cd2 #214 + 'aA' // 5cd3-5cd4 + '35C' // 5cd5 #912 + '15S' // 5cd6 #408 + '1S' // 5cd7 #44 + '12H' // 5cd8 #319 + '30P' // 5cd9 #795 + '15S' // 5cda #408 + 'aA' // 5cdb-5cdc + '1S' // 5cdd #44 + 'A' // 5cde + '12H' // 5cdf #319 + '253C' // 5ce0 #6580 + '252W' // 5ce1 #6574 + 'A' // 5ce2 + 'a7X' // 5ce3-5ce4 #205 + '12H' // 5ce5 #319 + '31O' // 5ce6 #820 + 'A' // 5ce7 + '60N' // 5ce8 #1573 + 'a60O' // 5ce9-5cea #1574 + 'aA' // 5ceb-5cec + '60O' // 5ced #1574 + '47P' // 5cee #1237 + '135Z' // 5cef #3535 + '196M' // 5cf0 #5108 + '47P' // 5cf1 #1237 + '1S' // 5cf2 #44 + '7X' // 5cf3 #205 + '60N' // 5cf4 #1573 + '47P' // 5cf5 #1237 + '223U' // 5cf6 #5818 + 'A' // 5cf7 + '47N' // 5cf8 #1235 + 'A' // 5cf9 + '1S' // 5cfa #44 + '140K' // 5cfb #3650 + '30M' // 5cfc #792 + '165I' // 5cfd #4298 + 'A' // 5cfe + '15R' // 5cff #407 + '47N' // 5d00 #1235 + '140L' // 5d01 #3651 + 'b7X' // 5d02-5d04 #205 + 'A' // 5d05 + '20U' // 5d06 #540 + '173V' // 5d07 #4519 + '7X' // 5d08 #205 + 'aA' // 5d09-5d0a + '20U' // 5d0b #540 + '47N' // 5d0c #1235 + '109F' // 5d0d #2839 + '169M' // 5d0e #4406 + '7X' // 5d0f #205 + '63H' // 5d10 #1645 + '48Z' // 5d11 #1273 + '31O' // 5d12 #820 + '7X' // 5d13 #205 + '151Z' // 5d14 #3951 + '20U' // 5d15 #540 + '136A' // 5d16 #3536 + '158M' // 5d17 #4120 + '20U' // 5d18 #540 + '151X' // 5d19 #3949 + '20U' // 5d1a #540 + '140M' // 5d1b #3652 + 'A' // 5d1c + '20U' // 5d1d #540 + '7X' // 5d1e #205 + 'a20U' // 5d1f-5d20 #540 + '7X' // 5d21 #205 + '20U' // 5d22 #540 + '1S' // 5d23 #44 + '20U' // 5d24 #540 + '7X' // 5d25 #205 + '14L' // 5d26 #375 + '30N' // 5d27 #793 + '15R' // 5d28 #407 + '183B' // 5d29 #4759 + 'A' // 5d2a + '1S' // 5d2b #44 + '15R' // 5d2c #407 + '2W' // 5d2d #74 + '16Y' // 5d2e #440 + '35B' // 5d2f #911 + '5E' // 5d30 #134 + '31N' // 5d31 #819 + '5E' // 5d32 #134 + '16Y' // 5d33 #440 + '14L' // 5d34 #375 + '15R' // 5d35 #407 + 'bA' // 5d36-5d38 + '1S' // 5d39 #44 + 'aA' // 5d3a-5d3b + '5E' // 5d3c #134 + '14L' // 5d3d #375 + '35B' // 5d3e #911 + '30O' // 5d3f #794 + 'A' // 5d40 + '5E' // 5d41 #134 + 'a14L' // 5d42-5d43 #375 + '5E' // 5d44 #134 + 'A' // 5d45 + 'a14L' // 5d46-5d47 #375 + '109G' // 5d48 #2840 + '15R' // 5d49 #407 + '14L' // 5d4a #375 + '30N' // 5d4b #793 + '152T' // 5d4c #3971 + 'A' // 5d4d + '14L' // 5d4e #375 + 'A' // 5d4f + '155A' // 5d50 #4030 + '1S' // 5d51 #44 + '14L' // 5d52 #375 + '1S' // 5d53 #44 + 'A' // 5d54 + '31N' // 5d55 #819 + '35B' // 5d56 #911 + '16Y' // 5d57 #440 + '5E' // 5d58 #134 + '30O' // 5d59 #794 + '5E' // 5d5a #134 + '16Y' // 5d5b #440 + '31N' // 5d5c #819 + '5E' // 5d5d #134 + 'A' // 5d5e + 'c1S' // 5d5f-5d62 #44 + 'A' // 5d63 + '1S' // 5d64 #44 + '15R' // 5d65 #407 + 'aA' // 5d66-5d67 + '16Y' // 5d68 #440 + '30N' // 5d69 #793 + '1S' // 5d6a #44 + '16Y' // 5d6b #440 + '30N' // 5d6c #793 + '1S' // 5d6d #44 + 'A' // 5d6e + '30N' // 5d6f #793 + '109E' // 5d70 #2838 + 'aA' // 5d71-5d72 + '1S' // 5d73 #44 + '16Y' // 5d74 #440 + 'A' // 5d75 + '1S' // 5d76 #44 + 'A' // 5d77 + '30M' // 5d78 #792 + 'a1S' // 5d79-5d7a #44 + '30M' // 5d7b #792 + 'aA' // 5d7c-5d7d + '30O' // 5d7e #794 + '14L' // 5d7f #375 + 'A' // 5d80 + '30O' // 5d81 #794 + '14L' // 5d82 #375 + '31N' // 5d83 #819 + '134H' // 5d84 #3491 + 'a16Y' // 5d85-5d86 #440 + '30N' // 5d87 #793 + '14L' // 5d88 #375 + '5E' // 5d89 #134 + '1S' // 5d8a #44 + '109J' // 5d8b #2843 + '31N' // 5d8c #819 + 'A' // 5d8d + '30M' // 5d8e #792 + '5E' // 5d8f #134 + '1S' // 5d90 #44 + 'A' // 5d91 + '14L' // 5d92 #375 + '31N' // 5d93 #819 + '30O' // 5d94 #794 + '1S' // 5d95 #44 + 'A' // 5d96 + '30O' // 5d97 #794 + 'A' // 5d98 + '14L' // 5d99 #375 + 'A' // 5d9a + '1X' // 5d9b #49 + 'A' // 5d9c + '23I' // 5d9d #606 + 'A' // 5d9e + '1X' // 5d9f #49 + '47M' // 5da0 #1234 + '15R' // 5da1 #407 + '47M' // 5da2 #1234 + 'A' // 5da3 + '35A' // 5da4 #910 + 'aA' // 5da5-5da6 + '23K' // 5da7 #608 + 'A' // 5da8 + '15R' // 5da9 #407 + '109K' // 5daa #2844 + '35A' // 5dab #910 + '1X' // 5dac #49 + 'A' // 5dad + '23K' // 5dae #608 + 'A' // 5daf + '1X' // 5db0 #49 + 'A' // 5db1 + '20T' // 5db2 #539 + 'A' // 5db3 + '23K' // 5db4 #608 + 'A' // 5db5 + '35B' // 5db6 #911 + '20T' // 5db7 #539 + '47M' // 5db8 #1234 + '35A' // 5db9 #910 + '166Q' // 5dba #4332 + 'A' // 5dbb + '158K' // 5dbc #4118 + '23I' // 5dbd #606 + 'bA' // 5dbe-5dc0 + '30M' // 5dc1 #792 + '16Y' // 5dc2 #440 + '20T' // 5dc3 #539 + 'A' // 5dc4 + '2R' // 5dc5 #69 + '5E' // 5dc6 #134 + '36G' // 5dc7 #942 + 'A' // 5dc8 + '20T' // 5dc9 #539 + 'A' // 5dca + '23K' // 5dcb #608 + '36G' // 5dcc #942 + '108Z' // 5dcd #2833 + '1X' // 5dce #49 + 'A' // 5dcf + 'a1X' // 5dd0-5dd1 #49 + '23I' // 5dd2 #606 + '1X' // 5dd3 #49 + '137N' // 5dd4 #3575 + 'A' // 5dd5 + '23I' // 5dd6 #606 + '109B' // 5dd7 #2835 + '20T' // 5dd8 #539 + '1X' // 5dd9 #49 + 'A' // 5dda + '20T' // 5ddb #539 + '5E' // 5ddc #134 + '214D' // 5ddd #5567 + '212Z' // 5dde #5537 + 'A' // 5ddf + '23K' // 5de0 #608 + '182W' // 5de1 #4754 + '165G' // 5de2 #4296 + '254R' // 5de3 #6621 + '1X' // 5de4 #49 + '242M' // 5de5 #6304 + '221P' // 5de6 #5761 + '214Y' // 5de7 #5588 + '211U' // 5de8 #5506 + '250E' // 5de9 #6504 + 'A' // 5dea + '153M' // 5deb #3990 + 'A' // 5dec + '5E' // 5ded #134 + '229L' // 5dee #5965 + '5E' // 5def #134 + '15R' // 5df0 #407 + '234F' // 5df1 #6089 + '239Y' // 5df2 #6238 + '109H' // 5df3 #2841 + '226N' // 5df4 #5889 + '23K' // 5df5 #608 + '5E' // 5df6 #134 + '201E' // 5df7 #5230 + '1X' // 5df8 #49 + '23K' // 5df9 #608 + '5E' // 5dfa #134 + '257S' // 5dfb #6700 + '5E' // 5dfc #134 + '23I' // 5dfd #606 + '180Z' // 5dfe #4705 + '129D' // 5dff #3357 + '36G' // 5e00 #942 + '3I' // 5e01 #86 + '68X' // 5e02 #1791 + '229U' // 5e03 #5974 + '15R' // 5e04 #407 + '3N' // 5e05 #91 + '167P' // 5e06 #4357 + '36G' // 5e07 #942 + '1Z' // 5e08 #51 + '30M' // 5e09 #792 + '15R' // 5e0a #407 + '35A' // 5e0b #910 + '223L' // 5e0c #5809 + '1X' // 5e0d #49 + 'A' // 5e0e + '5E' // 5e0f #134 + '2C' // 5e10 #54 + '23I' // 5e11 #606 + '35A' // 5e12 #910 + '5E' // 5e13 #134 + '20T' // 5e14 #539 + '165H' // 5e15 #4297 + '188E' // 5e16 #4892 + 'A' // 5e17 + '109D' // 5e18 #2837 + '23I' // 5e19 #606 + '20T' // 5e1a #539 + '23I' // 5e1b #606 + '3W' // 5e1c #100 + '202Z' // 5e1d #5277 + 'A' // 5e1e + 'a23K' // 5e1f-5e20 #608 + '15R' // 5e21 #407 + '16Y' // 5e22 #440 + 'aA' // 5e23-5e24 + '193T' // 5e25 #5037 + '1Z' // 5e26 #51 + '3W' // 5e27 #100 + '20T' // 5e28 #539 + 'A' // 5e29 + '5E' // 5e2a #134 + '235R' // 5e2b #6127 + 'A' // 5e2c + '205G' // 5e2d #5336 + '158L' // 5e2e #4119 + '259F' // 5e2f #6739 + '258L' // 5e30 #6719 + '5E' // 5e31 #134 + '1X' // 5e32 #49 + '228G' // 5e33 #5934 + '16Y' // 5e34 #440 + '1X' // 5e35 #49 + '232T' // 5e36 #6051 + '109C' // 5e37 #2836 + '242X' // 5e38 #6315 + 'c5E' // 5e39-5e3c #134 + '197E' // 5e3d #5126 + '36G' // 5e3e #942 + '71E' // 5e3f #1850 + '47L' // 5e40 #1233 + 'A' // 5e41 + '109M' // 5e42 #2846 + '39T' // 5e43 #1033 + '47L' // 5e44 #1233 + '191I' // 5e45 #4974 + '5E' // 5e46 #134 + '70I' // 5e47 #1828 + '35B' // 5e48 #911 + '1X' // 5e49 #49 + 'A' // 5e4a + '1X' // 5e4b #49 + '125U' // 5e4c #3270 + 'A' // 5e4d + '1X' // 5e4e #49 + '5E' // 5e4f #134 + 'a1X' // 5e50-5e51 #49 + 'A' // 5e52 + '5E' // 5e53 #134 + '23J' // 5e54 #607 + '219R' // 5e55 #5711 + '1X' // 5e56 #49 + '23J' // 5e57 #607 + '39T' // 5e58 #1033 + '109L' // 5e59 #2845 + 'A' // 5e5a + '23J' // 5e5b #607 + '1X' // 5e5c #49 + 'A' // 5e5d + '23J' // 5e5e #607 + '47L' // 5e5f #1233 + 'A' // 5e60 + '109I' // 5e61 #2842 + '109A' // 5e62 #2834 + '208R' // 5e63 #5425 + '1X' // 5e64 #49 + 'bA' // 5e65-5e67 + '1X' // 5e68 #49 + 'A' // 5e69 + '23J' // 5e6a #607 + '225R' // 5e6b #5867 + '39T' // 5e6c #1033 + '1X' // 5e6d #49 + '39T' // 5e6e #1033 + 'A' // 5e6f + '1X' // 5e70 #49 + 'A' // 5e71 + '190P' // 5e72 #4955 + '244L' // 5e73 #6355 + '245M' // 5e74 #6382 + '23J' // 5e75 #607 + '146S' // 5e76 #3814 + '70I' // 5e77 #1828 + '216F' // 5e78 #5621 + '197Z' // 5e79 #5147 + '23J' // 5e7a #607 + '203J' // 5e7b #5287 + '211T' // 5e7c #5505 + '180X' // 5e7d #4703 + '226V' // 5e7e #5897 + '146T' // 5e7f #3815 + '23J' // 5e80 #607 + '256G' // 5e81 #6662 + 'A' // 5e82 + '108T' // 5e83 #2827 + '154R' // 5e84 #4021 + '5E' // 5e85 #134 + '108Y' // 5e86 #2832 + '140H' // 5e87 #3647 + '30L' // 5e88 #791 + 'A' // 5e89 + '211B' // 5e8a #5487 + '60I' // 5e8b #1568 + 'aA' // 5e8c-5e8d + '1X' // 5e8e #49 + '227A' // 5e8f #5902 + '3W' // 5e90 #100 + '5E' // 5e91 #134 + '108W' // 5e92 #2830 + '1Z' // 5e93 #51 + '3Q' // 5e94 #94 + '227X' // 5e95 #5925 + '60I' // 5e96 #1568 + '238S' // 5e97 #6206 + '14Z' // 5e98 #389 + '108I' // 5e99 #2816 + '65H' // 5e9a #1697 + '30K' // 5e9b #790 + '216H' // 5e9c #5623 + '14Z' // 5e9d #389 + '2R' // 5e9e #69 + '1R' // 5e9f #43 + '39S' // 5ea0 #1032 + '14Z' // 5ea1 #389 + '4P' // 5ea2 #119 + '14Z' // 5ea3 #389 + 'a4P' // 5ea4-5ea5 #119 + '41D' // 5ea6 #1069 + '230J' // 5ea7 #5989 + '27A' // 5ea8 #702 + 'A' // 5ea9 + '1X' // 5eaa #49 + '223J' // 5eab #5807 + '1X' // 5eac #49 + '221K' // 5ead #5756 + 'A' // 5eae + '14Z' // 5eaf #389 + 'A' // 5eb0 + '1X' // 5eb1 #49 + 'A' // 5eb2 + '4P' // 5eb3 #119 + '30L' // 5eb4 #791 + '108P' // 5eb5 #2823 + '108M' // 5eb6 #2820 + '235S' // 5eb7 #6128 + '65H' // 5eb8 #1697 + '4P' // 5eb9 #119 + 'bA' // 5eba-5ebc + '60G' // 5ebd #1566 + '39S' // 5ebe #1032 + '1X' // 5ebf #49 + 'A' // 5ec0 + '66L' // 5ec1 #1727 + '151W' // 5ec2 #3948 + '256J' // 5ec3 #6665 + '30L' // 5ec4 #791 + 'A' // 5ec5 + '4P' // 5ec6 #119 + 'A' // 5ec7 + '171W' // 5ec8 #4468 + '187Q' // 5ec9 #4878 + '173Z' // 5eca #4523 + 'a4P' // 5ecb-5ecc #119 + '30L' // 5ecd #791 + 'a1X' // 5ece-5ecf #49 + '108G' // 5ed0 #2814 + 'a4P' // 5ed1-5ed2 #119 + '129C' // 5ed3 #3356 + '4P' // 5ed4 #119 + '30J' // 5ed5 #789 + '165F' // 5ed6 #4295 + 'A' // 5ed7 + '30L' // 5ed8 #791 + '4P' // 5ed9 #119 + '67V' // 5eda #1763 + '39S' // 5edb #1032 + '1X' // 5edc #49 + '4P' // 5edd #119 + '1X' // 5ede #49 + '179C' // 5edf #4656 + '217Q' // 5ee0 #5658 + '30J' // 5ee1 #789 + '193S' // 5ee2 #5036 + '233F' // 5ee3 #6063 + 'A' // 5ee4 + '1X' // 5ee5 #49 + 'aA' // 5ee6-5ee7 + '4P' // 5ee8 #119 + '30J' // 5ee9 #789 + '14Z' // 5eea #389 + '1X' // 5eeb #49 + '39S' // 5eec #1032 + 'aA' // 5eed-5eee + '30K' // 5eef #790 + '27A' // 5ef0 #702 + '1X' // 5ef1 #49 + 'A' // 5ef2 + '67V' // 5ef3 #1763 + '4P' // 5ef4 #119 + 'A' // 5ef5 + '211C' // 5ef6 #5488 + '180G' // 5ef7 #4686 + '4P' // 5ef8 #119 + '60G' // 5ef9 #1566 + '235D' // 5efa #6113 + '108O' // 5efb #2822 + '4P' // 5efc #119 + '1X' // 5efd #49 + '4P' // 5efe #119 + '140I' // 5eff #3648 + '250H' // 5f00 #6507 + '108S' // 5f01 #2826 + '108J' // 5f02 #2817 + '250G' // 5f03 #6506 + '195P' // 5f04 #5085 + '30L' // 5f05 #791 + '1X' // 5f06 #49 + '4P' // 5f07 #119 + '108H' // 5f08 #2815 + '1X' // 5f09 #49 + '136Y' // 5f0a #3560 + 'c4P' // 5f0b-5f0e #119 + '69C' // 5f0f #1796 + '252S' // 5f10 #6570 + '70H' // 5f11 #1827 + '30K' // 5f12 #790 + '148V' // 5f13 #3869 + '108K' // 5f14 #2818 + '231R' // 5f15 #6023 + '1X' // 5f16 #49 + '151V' // 5f17 #3947 + '176D' // 5f18 #4579 + '1X' // 5f19 #49 + '14Z' // 5f1a #389 + '108F' // 5f1b #2813 + '1X' // 5f1c #49 + '30J' // 5f1d #789 + '1X' // 5f1e #49 + '204B' // 5f1f #5305 + '1Z' // 5f20 #51 + '1X' // 5f21 #49 + '4P' // 5f22 #119 + 'a1X' // 5f23-5f24 #49 + '108Q' // 5f25 #2824 + '154O' // 5f26 #4018 + '140F' // 5f27 #3645 + '4P' // 5f28 #119 + '34Z' // 5f29 #909 + 'A' // 5f2a + '1X' // 5f2b #49 + '27A' // 5f2c #702 + '4P' // 5f2d #119 + '1X' // 5f2e #49 + '250F' // 5f2f #6505 + '30J' // 5f30 #789 + '198N' // 5f31 #5161 + 'aA' // 5f32-5f33 + '1X' // 5f34 #49 + '236N' // 5f35 #6149 + '4P' // 5f36 #119 + '237Q' // 5f37 #6178 + '4P' // 5f38 #119 + '3I' // 5f39 #86 + '140G' // 5f3a #3646 + '60H' // 5f3b #1567 + '34Z' // 5f3c #909 + '27A' // 5f3d #702 + '257N' // 5f3e #6695 + '27A' // 5f3f #702 + '4P' // 5f40 #119 + '27A' // 5f41 #702 + 'a14Z' // 5f42-5f43 #389 + '1X' // 5f44 #49 + '4P' // 5f45 #119 + '30K' // 5f46 #790 + '27A' // 5f47 #702 + '207R' // 5f48 #5399 + '14Z' // 5f49 #389 + '34Z' // 5f4a #909 + 'A' // 5f4b + '165E' // 5f4c #4294 + '60H' // 5f4d #1567 + '171V' // 5f4e #4467 + 'A' // 5f4f + '4P' // 5f50 #119 + '30J' // 5f51 #789 + '2C' // 5f52 #54 + '260F' // 5f53 #6765 + '4P' // 5f54 #119 + '7K' // 5f55 #192 + 'a34Z' // 5f56-5f57 #909 + '4P' // 5f58 #119 + '173N' // 5f59 #4511 + 'A' // 5f5a + '70H' // 5f5b #1827 + 'a4P' // 5f5c-5f5d #119 + '30K' // 5f5e #790 + '14Z' // 5f5f #389 + '1X' // 5f60 #49 + '108N' // 5f61 #2821 + '231C' // 5f62 #6008 + '4P' // 5f63 #119 + '140J' // 5f64 #3649 + '66L' // 5f65 #1727 + '255S' // 5f66 #6648 + '34Z' // 5f67 #909 + '14Z' // 5f68 #389 + '227Z' // 5f69 #5927 + '129B' // 5f6a #3355 + '108R' // 5f6b #2825 + '146R' // 5f6c #3813 + '171S' // 5f6d #4464 + '14Z' // 5f6e #389 + '31M' // 5f6f #818 + '201Z' // 5f70 #5251 + '241Y' // 5f71 #6290 + 'b60E' // 5f72-5f74 #1564 + '31M' // 5f75 #818 + '30K' // 5f76 #790 + '152U' // 5f77 #3972 + '60E' // 5f78 #1564 + '176V' // 5f79 #4597 + '31M' // 5f7a #818 + '108X' // 5f7b #2831 + '184A' // 5f7c #4784 + '108E' // 5f7d #2812 + '31M' // 5f7e #818 + '151Q' // 5f7f #3942 + '226Z' // 5f80 #5901 + '181Q' // 5f81 #4722 + 'a30I' // 5f82-5f83 #788 + '255E' // 5f84 #6634 + '223O' // 5f85 #5812 + 'A' // 5f86 + '39R' // 5f87 #1031 + '232R' // 5f88 #6049 + '30I' // 5f89 #788 + '60J' // 5f8a #1569 + '220D' // 5f8b #5723 + '245B' // 5f8c #6371 + '31M' // 5f8d #818 + 'A' // 5f8e + '3C' // 5f8f #80 + '188S' // 5f90 #4906 + '186I' // 5f91 #4844 + '191N' // 5f92 #4979 + '257D' // 5f93 #6685 + 'A' // 5f94 + '14Z' // 5f95 #389 + '3C' // 5f96 #80 + '243O' // 5f97 #6332 + '60J' // 5f98 #1569 + '39R' // 5f99 #1031 + 'A' // 5f9a + '60L' // 5f9b #1571 + '30I' // 5f9c #788 + '3C' // 5f9d #80 + '233C' // 5f9e #6060 + 'A' // 5f9f + '39R' // 5fa0 #1031 + '183Y' // 5fa1 #4782 + '3C' // 5fa2 #80 + 'A' // 5fa3 + '60F' // 5fa4 #1565 + 'A' // 5fa5 + '108V' // 5fa6 #2829 + '30I' // 5fa7 #788 + '39R' // 5fa8 #1031 + '221W' // 5fa9 #5768 + '181B' // 5faa #4707 + '60F' // 5fab #1565 + 'a30I' // 5fac-5fad #788 + '227L' // 5fae #5913 + '31M' // 5faf #818 + '3C' // 5fb0 #80 + '108D' // 5fb1 #2811 + 'A' // 5fb2 + '257R' // 5fb3 #6699 + '257P' // 5fb4 #6697 + '214Q' // 5fb5 #5580 + 'A' // 5fb6 + '225Q' // 5fb7 #5866 + '3C' // 5fb8 #80 + '176I' // 5fb9 #4584 + '60D' // 5fba #1563 + 'A' // 5fbb + '30I' // 5fbc #788 + '151U' // 5fbd #3946 + 'cA' // 5fbe-5fc1 + '60D' // 5fc2 #1563 + '246O' // 5fc3 #6410 + '16X' // 5fc4 #439 + '238K' // 5fc5 #6198 + '3N' // 5fc6 #91 + 'a3C' // 5fc7-5fc8 #80 + '16X' // 5fc9 #439 + 'A' // 5fca + '17I' // 5fcb #450 + '180T' // 5fcc #4699 + '197K' // 5fcd #5132 + '6U' // 5fce #176 + '60K' // 5fcf #1570 + 'b16X' // 5fd0-5fd2 #439 + '17I' // 5fd3 #450 + '16X' // 5fd4 #439 + '60K' // 5fd5 #1570 + '108L' // 5fd6 #2819 + '221B' // 5fd7 #5747 + '221O' // 5fd8 #5760 + '203N' // 5fd9 #5291 + '6U' // 5fda #176 + '108U' // 5fdb #2828 + '260B' // 5fdc #6761 + 'a16X' // 5fdd-5fde #439 + '60L' // 5fdf #1571 + '196L' // 5fe0 #5107 + '16X' // 5fe1 #439 + '17I' // 5fe2 #450 + 'A' // 5fe3 + '16X' // 5fe4 #439 + '6U' // 5fe5 #176 + 'A' // 5fe6 + '3N' // 5fe7 #91 + 'a3C' // 5fe8-5fe9 #80 + '16X' // 5fea #439 + '241L' // 5feb #6277 + '3C' // 5fec #80 + 'a16X' // 5fed-5fee #439 + 'a3C' // 5fef-5ff0 #80 + '16X' // 5ff1 #439 + '17I' // 5ff2 #450 + '16X' // 5ff3 #439 + 'A' // 5ff4 + '223E' // 5ff5 #5802 + '17I' // 5ff6 #450 + 'A' // 5ff7 + '5J' // 5ff8 #139 + 'A' // 5ff9 + '18Z' // 5ffa #493 + '5J' // 5ffb #139 + '3C' // 5ffc #80 + '179A' // 5ffd #4654 + '6U' // 5ffe #176 + '16V' // 5fff #437 + '108C' // 6000 #2810 + '3Q' // 6001 #94 + 'd6U' // 6002-6006 #176 + '3C' // 6007 #80 + 'aA' // 6008-6009 + '5J' // 600a #139 + 'aA' // 600b-600c + '5J' // 600d #139 + '217P' // 600e #5657 + '16V' // 600f #437 + '18Z' // 6010 #493 + 'A' // 6011 + '190T' // 6012 #4959 + '3C' // 6013 #80 + '5J' // 6014 #139 + '207Q' // 6015 #5398 + '190R' // 6016 #4957 + '18Z' // 6017 #493 + '3C' // 6018 #80 + '5J' // 6019 #139 + '18Z' // 601a #493 + '5J' // 601b #139 + '107Q' // 601c #2798 + '231S' // 601d #6024 + '26L' // 601e #687 + '3C' // 601f #80 + '107S' // 6020 #2800 + '178Z' // 6021 #4653 + '18Z' // 6022 #493 + '108A' // 6023 #2808 + '3C' // 6024 #80 + '213P' // 6025 #5553 + '5J' // 6026 #139 + '244Q' // 6027 #6360 + '172P' // 6028 #4487 + '5J' // 6029 #139 + '210S' // 602a #5478 + '5J' // 602b #139 + '26L' // 602c #687 + '3C' // 602d #80 + '39Q' // 602e #1030 + '107P' // 602f #2797 + 'A' // 6030 + '5J' // 6031 #139 + 'A' // 6032 + '5J' // 6033 #139 + '26L' // 6034 #687 + '5J' // 6035 #139 + 'bA' // 6036-6038 + '39Q' // 6039 #1030 + '3C' // 603a #80 + '126K' // 603b #3286 + '2K' // 603c #62 + 'aA' // 603d-603e + '6U' // 603f #176 + '18Z' // 6040 #493 + 'b16V' // 6041-6043 #437 + 'A' // 6044 + '26L' // 6045 #687 + '179B' // 6046 #4655 + '18Z' // 6047 #493 + 'a3C' // 6048-6049 #80 + '18Z' // 604a #493 + '107Z' // 604b #2807 + '18Z' // 604c #493 + '123V' // 604d #3219 + 'A' // 604e + '6U' // 604f #176 + '211S' // 6050 #5504 + '3C' // 6051 #80 + '154P' // 6052 #4019 + '39Q' // 6053 #1030 + '17I' // 6054 #450 + '165D' // 6055 #4293 + 'a3C' // 6056-6057 #80 + '6U' // 6058 #176 + '16V' // 6059 #437 + '5J' // 605a #139 + '39Q' // 605b #1030 + 'A' // 605c + '16V' // 605d #437 + '6U' // 605e #176 + '3C' // 605f #80 + '17I' // 6060 #450 + '3C' // 6061 #80 + '171U' // 6062 #4466 + '16V' // 6063 #437 + '151T' // 6064 #3945 + '155D' // 6065 #4033 + 'A' // 6066 + '5J' // 6067 #139 + '173O' // 6068 #4512 + '209O' // 6069 #5448 + '16V' // 606a #437 + '5J' // 606b #139 + '134G' // 606c #3490 + '173Y' // 606d #4522 + '26L' // 606e #687 + '234J' // 606f #6093 + '158J' // 6070 #4117 + '3C' // 6071 #80 + '26L' // 6072 #687 + '2W' // 6073 #74 + 'A' // 6074 + '107Y' // 6075 #2806 + '3I' // 6076 #86 + '26K' // 6077 #686 + 'a6U' // 6078-6079 #176 + '3G' // 607a #84 + '6U' // 607b #176 + '2Y' // 607c #76 + '6U' // 607d #176 + '26K' // 607e #686 + '5J' // 607f #139 + '26L' // 6080 #687 + '18Z' // 6081 #493 + '3C' // 6082 #80 + '5J' // 6083 #139 + '186K' // 6084 #4846 + '186H' // 6085 #4843 + '5J' // 6086 #139 + '6U' // 6087 #176 + '3C' // 6088 #80 + '186J' // 6089 #4845 + '5J' // 608a #139 + '3C' // 608b #80 + '16V' // 608c #437 + '151S' // 608d #3944 + '5J' // 608e #139 + '6U' // 608f #176 + 'A' // 6090 + '3C' // 6091 #80 + '5J' // 6092 #139 + '17I' // 6093 #450 + '175Z' // 6094 #4575 + '5J' // 6095 #139 + '16V' // 6096 #437 + '5J' // 6097 #139 + '3C' // 6098 #80 + 'A' // 6099 + '151R' // 609a #3943 + '16V' // 609b #437 + '6U' // 609c #176 + '5J' // 609d #139 + '26K' // 609e #686 + '168R' // 609f #4385 + '189K' // 60a0 #4924 + 'A' // 60a1 + '5J' // 60a2 #139 + '189R' // 60a3 #4931 + '107L' // 60a4 #2793 + '3C' // 60a5 #80 + '107T' // 60a6 #2801 + '16V' // 60a7 #437 + '232S' // 60a8 #6050 + '257Z' // 60a9 #6707 + '258R' // 60aa #6725 + '6U' // 60ab #176 + '1R' // 60ac #43 + '6U' // 60ad #176 + 'A' // 60ae + '6U' // 60af #176 + '34Y' // 60b0 #908 + '16W' // 60b1 #438 + '190S' // 60b2 #4958 + 'a34Y' // 60b3-60b4 #908 + '16W' // 60b5 #438 + '167D' // 60b6 #4345 + '3C' // 60b7 #80 + '34Y' // 60b8 #908 + 'aA' // 60b9-60ba + '16W' // 60bb #438 + '130I' // 60bc #3388 + '34Y' // 60bd #908 + '16W' // 60be #438 + 'A' // 60bf + '47K' // 60c0 #1232 + '6U' // 60c1 #176 + '17I' // 60c2 #450 + '6U' // 60c3 #176 + '3C' // 60c4 #80 + '69D' // 60c5 #1797 + '16W' // 60c6 #438 + '34Y' // 60c7 #908 + 'a17I' // 60c8-60c9 #450 + '107O' // 60ca #2796 + '16W' // 60cb #438 + 'aA' // 60cc-60cd + '17I' // 60ce #450 + '3C' // 60cf #80 + 'A' // 60d0 + '190Z' // 60d1 #4965 + 'A' // 60d2 + 'a16W' // 60d3-60d4 #438 + '107N' // 60d5 #2795 + 'A' // 60d6 + '47K' // 60d7 #1232 + 'a16W' // 60d8-60d9 #438 + '107X' // 60da #2805 + '16W' // 60db #438 + '195O' // 60dc #5084 + '16W' // 60dd #438 + '26K' // 60de #686 + '158I' // 60df #4116 + '226I' // 60e0 #5884 + '207P' // 60e1 #5397 + '16W' // 60e2 #438 + '107U' // 60e3 #2802 + '6U' // 60e4 #176 + '3C' // 60e5 #80 + '47K' // 60e6 #1232 + '107R' // 60e7 #2799 + '107W' // 60e8 #2804 + '108B' // 60e9 #2809 + '6U' // 60ea #176 + '2W' // 60eb #74 + '6U' // 60ec #176 + '10L' // 60ed #271 + '250D' // 60ee #6503 + '1R' // 60ef #43 + '34X' // 60f0 #907 + '171R' // 60f1 #4463 + '30H' // 60f2 #787 + '242I' // 60f3 #6300 + '39P' // 60f4 #1029 + '3C' // 60f5 #80 + '34X' // 60f6 #907 + 'a3C' // 60f7-60f8 #80 + '188H' // 60f9 #4895 + '34X' // 60fa #907 + '107M' // 60fb #2794 + '3C' // 60fc #80 + '26K' // 60fd #686 + 'A' // 60fe + '60C' // 60ff #1562 + '39P' // 6100 #1029 + '152V' // 6101 #3973 + '3C' // 6102 #80 + '30H' // 6103 #787 + 'aA' // 6104-6105 + '34X' // 6106 #907 + '26K' // 6107 #686 + '171T' // 6108 #4465 + '174M' // 6109 #4536 + '30H' // 610a #787 + '60C' // 610b #1562 + '26K' // 610c #686 + 'a34X' // 610d-610e #907 + '68Y' // 610f #1792 + '30H' // 6110 #787 + '3C' // 6111 #80 + '39P' // 6112 #1029 + '30H' // 6113 #787 + '39P' // 6114 #1029 + '107V' // 6115 #2803 + '30H' // 6116 #787 + '3E' // 6117 #82 + 'A' // 6118 + '60B' // 6119 #1561 + '154L' // 611a #4015 + '243Y' // 611b #6342 + '123U' // 611c #3218 + 'A' // 611d + '3E' // 611e #82 + '68W' // 611f #1790 + '34W' // 6120 #906 + '3E' // 6121 #82 + '60B' // 6122 #1561 + '47J' // 6123 #1231 + '2Y' // 6124 #76 + 'A' // 6125 + '10L' // 6126 #271 + '146P' // 6127 #3811 + '34W' // 6128 #906 + '47J' // 6129 #1231 + '36F' // 612a #941 + 'a34W' // 612b-612c #906 + '10L' // 612d #271 + 'a47J' // 612e-612f #1231 + '60A' // 6130 #1560 + '3E' // 6131 #82 + 'aA' // 6132-6133 + '107K' // 6134 #2792 + '3E' // 6135 #82 + '34W' // 6136 #906 + '123T' // 6137 #3217 + 'A' // 6138 + '3E' // 6139 #82 + '36F' // 613a #941 + 'A' // 613b + '250B' // 613c #6501 + '34W' // 613d #906 + '60A' // 613e #1560 + '107J' // 613f #2791 + 'A' // 6140 + '3E' // 6141 #82 + '106Y' // 6142 #2780 + 'A' // 6143 + '47G' // 6144 #1228 + '3E' // 6145 #82 + '15P' // 6146 #405 + '47G' // 6147 #1228 + '188D' // 6148 #4891 + '18Y' // 6149 #492 + '47G' // 614a #1228 + '230P' // 614b #5995 + '153X' // 614c #4001 + '18Y' // 614d #492 + '175O' // 614e #4564 + 'A' // 614f + '59X' // 6150 #1557 + '10L' // 6151 #271 + '30G' // 6152 #786 + '14K' // 6153 #374 + 'A' // 6154 + '180B' // 6155 #4681 + 'aA' // 6156-6157 + '186G' // 6158 #4842 + '59Y' // 6159 #1558 + '15P' // 615a #405 + 'A' // 615b + '47I' // 615c #1230 + '14K' // 615d #374 + '18Y' // 615e #492 + '14K' // 615f #374 + '20S' // 6160 #538 + '10L' // 6161 #271 + '211A' // 6162 #5486 + '197Y' // 6163 #5146 + '14K' // 6164 #374 + '15P' // 6165 #405 + 'A' // 6166 + '214X' // 6167 #5587 + '141V' // 6168 #3687 + 'A' // 6169 + '30G' // 616a #786 + '14K' // 616b #374 + '15P' // 616c #405 + 'A' // 616d + '197T' // 616e #5141 + '18Y' // 616f #492 + '174Y' // 6170 #4548 + '15P' // 6171 #405 + '18Y' // 6172 #492 + 'b15P' // 6173-6175 #405 + '209Q' // 6176 #5450 + '14K' // 6177 #374 + '3E' // 6178 #82 + 'A' // 6179 + '30G' // 617a #786 + '3E' // 617b #82 + '18Y' // 617c #492 + '47F' // 617d #1227 + '171P' // 617e #4461 + '3E' // 617f #82 + '18Y' // 6180 #492 + '47F' // 6181 #1227 + '195Y' // 6182 #5094 + 'a3E' // 6183-6184 #82 + 'aA' // 6185-6186 + '15P' // 6187 #405 + 'aA' // 6188-6189 + '14K' // 618a #374 + '107A' // 618b #2782 + '30G' // 618c #786 + '15P' // 618d #405 + '107D' // 618e #2785 + 'A' // 618f + '159U' // 6190 #4154 + '187P' // 6191 #4877 + 'a18Y' // 6192-6193 #492 + '14K' // 6194 #374 + '59X' // 6195 #1557 + 'a3E' // 6196-6197 #82 + '47F' // 6198 #1227 + 'a14K' // 6199-619a #374 + '30G' // 619b #786 + '106Z' // 619c #2781 + '3E' // 619d #82 + 'A' // 619e + '15P' // 619f #405 + '3E' // 61a0 #82 + '47I' // 61a1 #1230 + 'A' // 61a2 + '10L' // 61a3 #271 + '159N' // 61a4 #4147 + '3E' // 61a5 #82 + 'A' // 61a6 + '107F' // 61a7 #2787 + '15P' // 61a8 #405 + '143C' // 61a9 #3720 + '18Y' // 61aa #492 + 'a14K' // 61ab-61ac #374 + '15P' // 61ad #405 + '59Y' // 61ae #1558 + '30G' // 61af #786 + 'aA' // 61b0-61b1 + '175K' // 61b2 #4560 + '10L' // 61b3 #271 + 'A' // 61b4 + '10L' // 61b5 #271 + '204M' // 61b6 #5316 + '47H' // 61b7 #1229 + '18Y' // 61b8 #492 + '20S' // 61b9 #538 + '14K' // 61ba #374 + '10L' // 61bb #271 + '3E' // 61bc #82 + 'A' // 61bd + '158G' // 61be #4114 + '47I' // 61bf #1230 + '20S' // 61c0 #538 + '3E' // 61c1 #82 + '207N' // 61c2 #5395 + '14K' // 61c3 #374 + '10L' // 61c4 #271 + 'A' // 61c5 + '15P' // 61c6 #405 + '136S' // 61c7 #3554 + '123S' // 61c8 #3216 + '240P' // 61c9 #6255 + 'a14K' // 61ca-61cb #374 + 'a15Q' // 61cc-61cd #406 + '3E' // 61ce #82 + '20S' // 61cf #538 + '107G' // 61d0 #2788 + '10L' // 61d1 #271 + '2L' // 61d2 #63 + '47H' // 61d3 #1229 + '10L' // 61d4 #271 + '3E' // 61d5 #82 + 'A' // 61d6 + '10L' // 61d7 #271 + 'aA' // 61d8-61d9 + '47H' // 61da #1229 + 'A' // 61db + 'a3E' // 61dc-61dd #82 + '15Q' // 61de #406 + '26J' // 61df #685 + '39O' // 61e0 #1028 + '36F' // 61e1 #941 + '20S' // 61e2 #538 + '15Q' // 61e3 #406 + 'A' // 61e4 + '3E' // 61e5 #82 + '34U' // 61e6 #904 + '3E' // 61e7 #82 + '15Q' // 61e8 #406 + '3E' // 61e9 #82 + 'aA' // 61ea-61eb + '3E' // 61ec #82 + '15Q' // 61ed #406 + '39O' // 61ee #1028 + '3E' // 61ef #82 + '10L' // 61f0 #271 + 'A' // 61f1 + '148H' // 61f2 #3855 + 'A' // 61f3 + '3E' // 61f4 #82 + '59Z' // 61f5 #1559 + '193N' // 61f6 #5031 + '214P' // 61f7 #5579 + '175T' // 61f8 #4569 + '34V' // 61f9 #905 + '34U' // 61fa #904 + 'A' // 61fb + '158F' // 61fc #4113 + 'a26J' // 61fd-61fe #685 + '128Z' // 61ff #3353 + '207M' // 6200 #5394 + '3E' // 6201 #82 + 'A' // 6202 + 'a3E' // 6203-6204 #82 + 'A' // 6205 + '10L' // 6206 #271 + '34U' // 6207 #904 + '146O' // 6208 #3810 + '26J' // 6209 #685 + '146N' // 620a #3809 + '10L' // 620b #271 + '147V' // 620c #3843 + 'a34U' // 620d-620e #904 + '1Z' // 620f #51 + '41D' // 6210 #1069 + '246I' // 6211 #6404 + '181G' // 6212 #4712 + '36F' // 6213 #941 + 'a26J' // 6214-6215 #685 + '240Q' // 6216 #6256 + '10L' // 6217 #271 + '1Z' // 6218 #51 + '39O' // 6219 #1028 + '153T' // 621a #3997 + '26J' // 621b #685 + 'b36F' // 621c-621e #941 + '107C' // 621f #2784 + '26J' // 6220 #685 + '34U' // 6221 #904 + 'a26J' // 6222-6223 #685 + 'A' // 6224 + '39O' // 6225 #1028 + '259E' // 6226 #6738 + '15Q' // 6227 #406 + 'A' // 6228 + '15Q' // 6229 #406 + '193O' // 622a #5032 + '15Q' // 622b #406 + '39N' // 622c #1027 + 'A' // 622d + '34T' // 622e #903 + '70X' // 622f #1843 + '225O' // 6230 #5864 + '249Z' // 6231 #6499 + '232P' // 6232 #6047 + '140E' // 6233 #3644 + '201M' // 6234 #5238 + 'A' // 6235 + '225P' // 6236 #5865 + '35T' // 6237 #929 + '70Y' // 6238 #1844 + '20S' // 6239 #538 + 'A' // 623a + '259Q' // 623b #6750 + 'A' // 623c + '16U' // 623d #436 + '34T' // 623e #903 + '229A' // 623f #5954 + '41D' // 6240 #1069 + '165A' // 6241 #4290 + '15Q' // 6242 #406 + '16U' // 6243 #436 + '3E' // 6244 #82 + 'A' // 6245 + '16U' // 6246 #436 + '175C' // 6247 #4552 + '34T' // 6248 #903 + '107E' // 6249 #2786 + 'A' // 624a + '35V' // 624b #931 + '16U' // 624c #436 + '233S' // 624d #6076 + '165C' // 624e #4292 + 'A' // 624f + '3E' // 6250 #82 + '107B' // 6251 #2783 + '146Q' // 6252 #3812 + '234P' // 6253 #6099 + '129A' // 6254 #3354 + '258H' // 6255 #6715 + '3E' // 6256 #82 + 'A' // 6257 + '193P' // 6258 #5033 + '39N' // 6259 #1027 + '15Q' // 625a #406 + '134F' // 625b #3489 + '3E' // 625c #82 + 'A' // 625d + '16U' // 625e #436 + 'A' // 625f + 'a16U' // 6260-6261 #436 + '34V' // 6262 #905 + '207O' // 6263 #5396 + '3E' // 6264 #82 + 'a39N' // 6265-6266 #1027 + '3I' // 6267 #86 + '20S' // 6268 #538 + '3N' // 6269 #91 + '11M' // 626a #298 + '2D' // 626b #55 + '3N' // 626c #91 + '171Q' // 626d #4462 + '187C' // 626e #4864 + '165B' // 626f #4291 + '107I' // 6270 #2790 + '107H' // 6271 #2789 + '34V' // 6272 #905 + '59Z' // 6273 #1559 + 'aA' // 6274-6275 + '173U' // 6276 #4518 + 'aA' // 6277-6278 + '209S' // 6279 #5452 + '16U' // 627a #436 + '15Q' // 627b #406 + '34T' // 627c #903 + '16U' // 627d #436 + '232Q' // 627e #6048 + '216G' // 627f #5622 + '235V' // 6280 #6131 + 'A' // 6281 + '20S' // 6282 #538 + '16U' // 6283 #436 + '152X' // 6284 #3975 + '15Q' // 6285 #406 + '39N' // 6286 #1027 + 'aA' // 6287-6288 + '34T' // 6289 #903 + '226X' // 628a #5899 + 'A' // 628b + '34V' // 628c #905 + '3E' // 628d #82 + '16U' // 628e #436 + '3E' // 628f #82 + '20S' // 6290 #538 + '168Z' // 6291 #4393 + '123R' // 6292 #3215 + '193R' // 6293 #5035 + '16U' // 6294 #436 + '231N' // 6295 #6019 + '151P' // 6296 #3941 + '210K' // 6297 #5470 + '220S' // 6298 #5738 + '3E' // 6299 #82 + '2Y' // 629a #76 + '250A' // 629b #6500 + '70Y' // 629c #1844 + '34V' // 629d #905 + '259D' // 629e #6737 + '11M' // 629f #298 + '2K' // 62a0 #62 + '11M' // 62a1 #298 + '3I' // 62a2 #86 + 'A' // 62a3 + '64L' // 62a4 #1675 + '7K' // 62a5 #192 + '39L' // 62a6 #1025 + 'A' // 62a7 + '34S' // 62a8 #902 + 'aA' // 62a9-62aa + '176C' // 62ab #4578 + '158H' // 62ac #4115 + 'aA' // 62ad-62ae + '47E' // 62af #1226 + 'A' // 62b0 + '211E' // 62b1 #5490 + 'A' // 62b2 + '39L' // 62b3 #1025 + 'A' // 62b4 + '202R' // 62b5 #5269 + '39L' // 62b6 #1025 + '3E' // 62b7 #82 + 'A' // 62b8 + '180Y' // 62b9 #4704 + '250C' // 62ba #6502 + '34S' // 62bb #902 + '162F' // 62bc #4217 + '209R' // 62bd #5451 + '39L' // 62be #1025 + '34S' // 62bf #902 + 'A' // 62c0 + '30F' // 62c1 #785 + '59W' // 62c2 #1556 + '30F' // 62c3 #785 + '34S' // 62c4 #902 + '106T' // 62c5 #2775 + '193Q' // 62c6 #5034 + '123Q' // 62c7 #3214 + '59W' // 62c8 #1556 + '226P' // 62c9 #5891 + '34S' // 62ca #902 + '177A' // 62cb #4602 + '167J' // 62cc #4351 + '233J' // 62cd #6067 + '140D' // 62ce #3643 + '59V' // 62cf #1555 + '135N' // 62d0 #3523 + '34R' // 62d1 #901 + '196Q' // 62d2 #5112 + '168I' // 62d3 #4376 + '186D' // 62d4 #4839 + '106K' // 62d5 #2766 + '67M' // 62d6 #1754 + '106N' // 62d7 #2769 + '168E' // 62d8 #4372 + '106M' // 62d9 #2768 + '164X' // 62da #4287 + '219H' // 62db #5701 + '67M' // 62dc #1754 + '70X' // 62dd #1843 + 'A' // 62de + '106W' // 62df #2778 + '256I' // 62e0 #6664 + '257O' // 62e1 #6696 + '3G' // 62e2 #84 + 'a11M' // 62e3-62e4 #298 + '106X' // 62e5 #2779 + '3H' // 62e6 #85 + '11M' // 62e7 #298 + '2L' // 62e8 #63 + '1Z' // 62e9 #51 + '4A' // 62ea #104 + '11M' // 62eb #298 + '210L' // 62ec #5471 + '142T' // 62ed #3711 + '59V' // 62ee #1555 + '146L' // 62ef #3807 + '11M' // 62f0 #298 + '134C' // 62f1 #3486 + '4A' // 62f2 #104 + '174V' // 62f3 #4545 + 'a34R' // 62f4-62f5 #901 + '106R' // 62f6 #2773 + '106P' // 62f7 #2771 + 'c47E' // 62f8-62fb #1226 + '200N' // 62fc #5213 + '106I' // 62fd #2764 + '167O' // 62fe #4356 + '217O' // 62ff #5656 + '47E' // 6300 #1226 + '238J' // 6301 #6197 + '106J' // 6302 #2765 + 'a4A' // 6303-6304 #104 + 'aA' // 6305-6306 + '238I' // 6307 #6196 + '34R' // 6308 #901 + '225N' // 6309 #5863 + 'a31L' // 630a-630b #817 + 'a34R' // 630c-630d #901 + '106U' // 630e #2776 + 'A' // 630f + '34R' // 6310 #901 + '211F' // 6311 #5491 + '11M' // 6312 #298 + '8X' // 6313 #231 + 'aA' // 6314-6315 + '178Y' // 6316 #4652 + '11M' // 6317 #298 + '59U' // 6318 #1554 + '256U' // 6319 #6676 + '3X' // 631a #101 + '31L' // 631b #817 + 'A' // 631c + 'a11M' // 631d-631e #298 + '70W' // 631f #1842 + '3W' // 6320 #100 + '2L' // 6321 #63 + '11M' // 6322 #298 + '3H' // 6323 #85 + '2Y' // 6324 #76 + '3N' // 6325 #91 + '11M' // 6326 #298 + '4A' // 6327 #104 + '149E' // 6328 #3878 + '31L' // 6329 #817 + '151O' // 632a #3940 + '153H' // 632b #3985 + 'A' // 632c + '18X' // 632d #491 + '26I' // 632e #684 + '191Q' // 632f #4982 + 'A' // 6330 + '47D' // 6331 #1225 + '8X' // 6332 #231 + 'A' // 6333 + '26H' // 6334 #683 + 'a8X' // 6335-6336 #231 + '26H' // 6337 #683 + '26I' // 6338 #684 + '8X' // 6339 #231 + '186E' // 633a #4840 + '39K' // 633b #1024 + '8X' // 633c #231 + '148C' // 633d #3850 + '39K' // 633e #1024 + '255X' // 633f #6653 + '26H' // 6340 #683 + '4A' // 6341 #104 + '106G' // 6342 #2762 + '8X' // 6343 #231 + '18X' // 6344 #491 + '106V' // 6345 #2777 + '106H' // 6346 #2763 + '11M' // 6347 #298 + 'A' // 6348 + '167C' // 6349 #4344 + '4A' // 634a #104 + '8X' // 634b #231 + '39K' // 634c #1024 + '128X' // 634d #3351 + '8X' // 634e #231 + '153A' // 634f #3978 + '178W' // 6350 #4650 + 'A' // 6351 + 'a4A' // 6352-6353 #104 + '31L' // 6354 #817 + '190I' // 6355 #4948 + 'A' // 6356 + '106Q' // 6357 #2772 + 'a4A' // 6358-6359 #104 + '26I' // 635a #684 + '4A' // 635b #104 + '70W' // 635c #1842 + '47D' // 635d #1225 + '2K' // 635e #62 + '3Y' // 635f #102 + 'A' // 6360 + '3H' // 6361 #85 + '1Z' // 6362 #51 + '3G' // 6363 #84 + '30F' // 6364 #785 + '18X' // 6365 #491 + '4A' // 6366 #104 + '166P' // 6367 #4331 + '176F' // 6368 #4581 + '8X' // 6369 #231 + 'A' // 636a + 'a18X' // 636b-636c #491 + '8X' // 636d #231 + '160O' // 636e #4174 + 'a26H' // 636f-6370 #683 + '63G' // 6371 #1644 + '193M' // 6372 #5030 + 'A' // 6373 + '4A' // 6374 #104 + '18X' // 6375 #491 + '8X' // 6376 #231 + '67U' // 6377 #1762 + '4A' // 6378 #104 + '30F' // 6379 #785 + '39K' // 637a #1024 + '106O' // 637b #2770 + '4A' // 637c #104 + '8X' // 637d #231 + 'A' // 637e + '106E' // 637f #2760 + '171O' // 6380 #4460 + '26I' // 6381 #684 + '63G' // 6382 #1644 + '205F' // 6383 #5335 + '18X' // 6384 #491 + 'aA' // 6385-6386 + '8X' // 6387 #231 + '211H' // 6388 #5493 + '67U' // 6389 #1762 + '8X' // 638a #231 + '47D' // 638b #1225 + '208Q' // 638c #5424 + '26H' // 638d #683 + '8X' // 638e #231 + '140C' // 638f #3642 + '106F' // 6390 #2761 + '11M' // 6391 #298 + '233R' // 6392 #6075 + '11M' // 6393 #298 + '8X' // 6394 #231 + '31L' // 6395 #817 + '106D' // 6396 #2759 + '26H' // 6397 #683 + '161T' // 6398 #4205 + '140B' // 6399 #3641 + '4A' // 639a #104 + '221J' // 639b #5755 + 'aA' // 639c-639d + '8X' // 639e #231 + '106L' // 639f #2767 + '134B' // 63a0 #3485 + '216N' // 63a1 #5629 + '214C' // 63a2 #5566 + 'a8X' // 63a3-63a4 #231 + '236A' // 63a5 #6136 + '4A' // 63a6 #104 + '219G' // 63a7 #5700 + '241K' // 63a8 #6276 + '158D' // 63a9 #4111 + '188R' // 63aa #4905 + '4A' // 63ab #104 + 'b8X' // 63ac-63ae #231 + '18X' // 63af #491 + '149T' // 63b0 #3893 + '26H' // 63b1 #683 + '259T' // 63b2 #6753 + '11M' // 63b3 #298 + '254D' // 63b4 #6607 + '31L' // 63b5 #817 + 'A' // 63b6 + '2W' // 63b7 #74 + '14Y' // 63b8 #388 + '39M' // 63b9 #1026 + '14Y' // 63ba #388 + '252R' // 63bb #6569 + '14Y' // 63bc #388 + '18X' // 63bd #491 + '18W' // 63be #490 + 'A' // 63bf + '140A' // 63c0 #3640 + '59U' // 63c1 #1554 + 'A' // 63c2 + '106S' // 63c3 #2774 + '47C' // 63c4 #1224 + '4A' // 63c5 #104 + '64H' // 63c6 #1671 + 'A' // 63c7 + '18W' // 63c8 #490 + '148W' // 63c9 #3870 + 'bA' // 63ca-63cc + '39M' // 63cd #1026 + '18W' // 63ce #490 + '205C' // 63cf #5332 + '242R' // 63d0 #6309 + '18W' // 63d1 #490 + '200O' // 63d2 #5214 + 'b4A' // 63d3-63d5 #104 + '47C' // 63d6 #1224 + 'bA' // 63d7-63d9 + '197J' // 63da #5131 + '230I' // 63db #5988 + '18X' // 63dc #491 + 'A' // 63dd + '39M' // 63de #1026 + 'A' // 63df + '18W' // 63e0 #490 + '67Q' // 63e1 #1758 + '30F' // 63e2 #785 + '18W' // 63e3 #490 + '26I' // 63e4 #684 + '4A' // 63e5 #104 + '26I' // 63e6 #684 + 'aA' // 63e7-63e8 + '18W' // 63e9 #490 + '164W' // 63ea #4286 + 'a4A' // 63eb-63ec #104 + '193L' // 63ed #5029 + '196X' // 63ee #5119 + 'A' // 63ef + '26I' // 63f0 #684 + 'A' // 63f1 + '18W' // 63f2 #490 + '18X' // 63f3 #491 + '213I' // 63f4 #5546 + '18W' // 63f5 #490 + '47C' // 63f6 #1224 + '249Y' // 63f7 #6498 + '18W' // 63f8 #490 + '134D' // 63f9 #3487 + '255J' // 63fa #6639 + '30F' // 63fb #785 + '39M' // 63fc #1026 + '2K' // 63fd #62 + '30E' // 63fe #784 + 'a14Y' // 63ff-6400 #388 + 'a3W' // 6401-6402 #100 + 'aA' // 6403-6404 + '3H' // 6405 #85 + '30D' // 6406 #783 + '26G' // 6407 #682 + 'A' // 6408 + 'a59S' // 6409-640a #1552 + 'a30E' // 640b-640c #784 + '205A' // 640d #5330 + 'A' // 640e + '146K' // 640f #3806 + '30D' // 6410 #783 + 'A' // 6411 + '4A' // 6412 #104 + '134E' // 6413 #3488 + '59R' // 6414 #1551 + '59T' // 6415 #1553 + '193J' // 6416 #5027 + '64H' // 6417 #1671 + '59S' // 6418 #1552 + 'aA' // 6419-641a + '30E' // 641b #784 + '240L' // 641c #6251 + 'A' // 641d + '207L' // 641e #5393 + '59T' // 641f #1553 + '30D' // 6420 #783 + '30E' // 6421 #784 + '59R' // 6422 #1551 + '30E' // 6423 #784 + '4A' // 6424 #104 + 'a30D' // 6425-6426 #783 + '30E' // 6427 #784 + '30D' // 6428 #783 + '4A' // 6429 #104 + '30D' // 642a #783 + '13D' // 642b #341 + '188N' // 642c #4901 + '219M' // 642d #5706 + 'A' // 642e + 'a20R' // 642f-6430 #537 + '14Y' // 6431 #388 + '10X' // 6432 #283 + 'A' // 6433 + '10W' // 6434 #282 + '49B' // 6435 #1275 + '207K' // 6436 #5392 + '10X' // 6437 #283 + '26G' // 6438 #682 + 'A' // 6439 + '106C' // 643a #2758 + '26G' // 643b #682 + 'A' // 643c + '10W' // 643d #282 + '106B' // 643e #2757 + '10W' // 643f #282 + 'a10X' // 6440-6441 #283 + '254X' // 6442 #6627 + '13D' // 6443 #341 + '2C' // 6444 #54 + '14Y' // 6445 #388 + '3N' // 6446 #91 + '1R' // 6447 #43 + '14Y' // 6448 #388 + 'A' // 6449 + '2R' // 644a #69 + '20R' // 644b #537 + 'aA' // 644c-644d + '20R' // 644e #537 + '4A' // 644f #104 + '10X' // 6450 #283 + 'a10W' // 6451-6452 #282 + '20R' // 6453 #537 + '164Y' // 6454 #4288 + 'bA' // 6455-6457 + '197D' // 6458 #5125 + '13D' // 6459 #341 + '105W' // 645a #2752 + 'a10W' // 645b-645c #282 + '70G' // 645d #1826 + '10X' // 645e #283 + '123O' // 645f #3212 + '105Y' // 6460 #2754 + '20R' // 6461 #537 + '14Y' // 6462 #388 + '4A' // 6463 #104 + 'A' // 6464 + '10X' // 6465 #283 + 'A' // 6466 + '128Y' // 6467 #3352 + '10X' // 6468 #283 + '219F' // 6469 #5699 + 'aA' // 646a-646b + '13D' // 646c #341 + '10W' // 646d #282 + '13D' // 646e #341 + '141O' // 646f #3680 + '10X' // 6470 #283 + '47B' // 6471 #1223 + '13D' // 6472 #341 + '10W' // 6473 #282 + '20R' // 6474 #537 + '13D' // 6475 #341 + '20R' // 6476 #537 + '10X' // 6477 #283 + '178V' // 6478 #4649 + '34Q' // 6479 #900 + '179W' // 647a #4676 + '10W' // 647b #282 + '47B' // 647c #1223 + '10W' // 647d #282 + 'cA' // 647e-6481 + '10X' // 6482 #283 + '257Y' // 6483 #6706 + '14Y' // 6484 #388 + '10W' // 6485 #282 + 'A' // 6486 + '49B' // 6487 #1275 + '151N' // 6488 #3939 + 'aA' // 6489-648a + '13D' // 648b #341 + '10X' // 648c #283 + '26G' // 648d #682 + 'A' // 648e + '4A' // 648f #104 + '178X' // 6490 #4651 + '105X' // 6491 #2753 + '173F' // 6492 #4503 + '34Q' // 6493 #900 + 'A' // 6494 + '158E' // 6495 #4112 + 'a10X' // 6496-6497 #283 + 'a10W' // 6498-6499 #282 + '34Q' // 649a #900 + '4A' // 649b #104 + 'A' // 649c + '10W' // 649d #282 + '193K' // 649e #5028 + '20R' // 649f #537 + '10X' // 64a0 #283 + '4A' // 64a1 #104 + '13D' // 64a2 #341 + '20R' // 64a3 #537 + '167N' // 64a4 #4355 + '187O' // 64a5 #4876 + '4A' // 64a6 #104 + 'A' // 64a7 + '4A' // 64a8 #104 + '146M' // 64a9 #3808 + 'A' // 64aa + '160L' // 64ab #4171 + '10W' // 64ac #282 + '218G' // 64ad #5674 + '126F' // 64ae #3281 + '26G' // 64af #682 + '172O' // 64b0 #4486 + '10X' // 64b1 #283 + '168D' // 64b2 #4371 + '10W' // 64b3 #282 + '10X' // 64b4 #283 + '14Y' // 64b5 #388 + '47B' // 64b6 #1223 + '14Y' // 64b7 #388 + '2R' // 64b8 #69 + '254G' // 64b9 #6610 + '14Y' // 64ba #388 + '34Q' // 64bb #900 + '164Z' // 64bc #4289 + '4A' // 64bd #104 + '10W' // 64be #282 + '49B' // 64bf #1275 + '10X' // 64c0 #283 + '208S' // 64c1 #5426 + '123P' // 64c2 #3213 + '13D' // 64c3 #341 + '34Q' // 64c4 #900 + '164V' // 64c5 #4285 + 'A' // 64c6 + '68I' // 64c7 #1776 + 'A' // 64c8 + '70G' // 64c9 #1826 + '68I' // 64ca #1776 + '186F' // 64cb #4841 + '4A' // 64cc #104 + '204S' // 64cd #5322 + '164U' // 64ce #4284 + 'A' // 64cf + '10W' // 64d0 #282 + '4A' // 64d1 #104 + '105V' // 64d2 #2751 + '26G' // 64d3 #682 + '214O' // 64d4 #5578 + '3K' // 64d5 #88 + 'A' // 64d6 + '39I' // 64d7 #1022 + '59P' // 64d8 #1549 + 'A' // 64d9 + '225L' // 64da #5861 + 'aA' // 64db-64dc + '26G' // 64dd #682 + '14Y' // 64de #388 + 'A' // 64df + '171N' // 64e0 #4459 + '59P' // 64e1 #1549 + '105Z' // 64e2 #2755 + '18V' // 64e3 #489 + '39I' // 64e4 #1022 + '105U' // 64e5 #2750 + '181J' // 64e6 #4715 + '59Q' // 64e7 #1550 + 'A' // 64e8 + '39I' // 64e9 #1022 + '46Z' // 64ea #1221 + 'A' // 64eb + '195F' // 64ec #5075 + '18V' // 64ed #489 + 'A' // 64ee + '18V' // 64ef #489 + '39I' // 64f0 #1022 + '128W' // 64f1 #3350 + '48X' // 64f2 #1271 + '13D' // 64f3 #341 + '193H' // 64f4 #5025 + 'a3K' // 64f5-64f6 #88 + '64G' // 64f7 #1670 + '39J' // 64f8 #1023 + 'A' // 64f9 + '67L' // 64fa #1753 + '18V' // 64fb #489 + '39J' // 64fc #1023 + '3K' // 64fd #88 + '67L' // 64fe #1753 + '23H' // 64ff #605 + '151L' // 6500 #3937 + '3K' // 6501 #88 + 'A' // 6502 + '8S' // 6503 #226 + '59Q' // 6504 #1550 + '3K' // 6505 #88 + '13D' // 6506 #341 + 'A' // 6507 + '3K' // 6508 #88 + '23H' // 6509 #605 + '46Z' // 650a #1221 + 'cA' // 650b-650e + '64G' // 650f #1670 + 'A' // 6510 + '13D' // 6511 #341 + '3W' // 6512 #100 + '3K' // 6513 #88 + '146J' // 6514 #3805 + 'A' // 6515 + '18V' // 6516 #489 + 'A' // 6517 + '47A' // 6518 #1222 + '18V' // 6519 #489 + 'A' // 651a + '18V' // 651b #489 + '193I' // 651c #5026 + '68C' // 651d #1770 + '123N' // 651e #3211 + '46Z' // 651f #1221 + 'a13D' // 6520-6521 #341 + '23H' // 6522 #605 + '106A' // 6523 #2756 + '178U' // 6524 #4648 + '39J' // 6525 #1023 + '18V' // 6526 #489 + 'aA' // 6527-6528 + '18V' // 6529 #489 + '151J' // 652a #3935 + '47A' // 652b #1222 + '65G' // 652c #1696 + 'A' // 652d + '23H' // 652e #605 + '231B' // 652f #6007 + '39J' // 6530 #1023 + '14R' // 6531 #381 + '23H' // 6532 #605 + 'A' // 6533 + 'a23H' // 6534-6535 #605 + '239X' // 6536 #6237 + 'a47A' // 6537-6538 #1222 + '235U' // 6539 #6130 + '14R' // 653a #381 + '220R' // 653b #5737 + '14R' // 653c #381 + '23H' // 653d #605 + '236T' // 653e #6155 + '234Z' // 653f #6109 + 'aA' // 6540-6541 + '8S' // 6542 #226 + '18V' // 6543 #489 + '3K' // 6544 #88 + '228Q' // 6545 #5944 + 'A' // 6546 + '3K' // 6547 #88 + '225M' // 6548 #5862 + '23H' // 6549 #605 + 'a8S' // 654a-654b #226 + '1R' // 654c #43 + '105E' // 654d #2734 + '249X' // 654e #6497 + '203I' // 654f #5286 + '3K' // 6550 #88 + '210Z' // 6551 #5485 + '3K' // 6552 #88 + '8S' // 6553 #226 + 'a59O' // 6554-6555 #1548 + '151K' // 6556 #3936 + '211K' // 6557 #5496 + '164S' // 6558 #4282 + '238D' // 6559 #6191 + '34P' // 655a #899 + '3W' // 655b #100 + 'A' // 655c + '59O' // 655d #1548 + '146H' // 655e #3803 + '39F' // 655f #1019 + '3K' // 6560 #88 + 'A' // 6561 + '201P' // 6562 #5241 + '212E' // 6563 #5516 + '105Q' // 6564 #2746 + '39H' // 6565 #1021 + '188J' // 6566 #4897 + '16T' // 6567 #435 + 'A' // 6568 + '8S' // 6569 #226 + 'A' // 656a + '6Z' // 656b #181 + '67Q' // 656c #1758 + '105M' // 656d #2742 + 'a8S' // 656e-656f #226 + '260I' // 6570 #6768 + '8S' // 6571 #226 + '66K' // 6572 #1726 + '23G' // 6573 #604 + '235Q' // 6574 #6126 + '191H' // 6575 #4973 + '23G' // 6576 #604 + '162A' // 6577 #4212 + '239W' // 6578 #6236 + '23G' // 6579 #604 + '6Z' // 657a #181 + '23G' // 657b #604 + '8S' // 657c #226 + '14R' // 657d #381 + '260Z' // 657e #6785 + 'aA' // 657f-6580 + '6Z' // 6581 #181 + '48X' // 6582 #1271 + '65G' // 6583 #1696 + '3K' // 6584 #88 + '59N' // 6585 #1547 + '23G' // 6586 #604 + '246W' // 6587 #6418 + '16T' // 6588 #435 + '105K' // 6589 #2740 + '3K' // 658a #88 + '105R' // 658b #2747 + '146I' // 658c #3804 + 'A' // 658d + '255I' // 658e #6638 + '8S' // 658f #226 + '142R' // 6590 #3709 + '180R' // 6591 #4697 + '14R' // 6592 #381 + '8S' // 6593 #226 + 'A' // 6594 + '6Z' // 6595 #181 + '8S' // 6596 #226 + '189J' // 6597 #4923 + '3K' // 6598 #88 + '244Y' // 6599 #6368 + 'A' // 659a + '20Q' // 659b #536 + '182A' // 659c #4732 + '6Z' // 659d #181 + 'A' // 659e + '20Q' // 659f #536 + '6Z' // 65a0 #181 + '20Q' // 65a1 #536 + 'A' // 65a2 + '14R' // 65a3 #381 + '178T' // 65a4 #4647 + '66K' // 65a5 #1726 + '3K' // 65a6 #88 + '135K' // 65a7 #3520 + 'A' // 65a8 + '2R' // 65a9 #69 + 'A' // 65aa + '20Q' // 65ab #536 + '161F' // 65ac #4191 + '259N' // 65ad #6747 + '3K' // 65ae #88 + '226H' // 65af #5883 + '247D' // 65b0 #6425 + 'A' // 65b1 + '6Z' // 65b2 #181 + '16T' // 65b3 #435 + '3K' // 65b4 #88 + '16T' // 65b5 #435 + '8S' // 65b6 #226 + '68C' // 65b7 #1770 + '3K' // 65b8 #88 + '49H' // 65b9 #1281 + '8S' // 65ba #226 + '23G' // 65bb #604 + '246C' // 65bc #6398 + '216O' // 65bd #5630 + 'a6Z' // 65be-65bf #181 + '8S' // 65c0 #226 + '200M' // 65c1 #5212 + 'b6Z' // 65c2-65c4 #181 + '236M' // 65c5 #6148 + '6Z' // 65c6 #181 + '8S' // 65c7 #226 + '14R' // 65c8 #381 + '3K' // 65c9 #88 + 'A' // 65ca + '195T' // 65cb #5089 + '20Q' // 65cc #536 + 'A' // 65cd + '6Z' // 65ce #181 + '222O' // 65cf #5786 + '14R' // 65d0 #381 + '34P' // 65d1 #899 + '20Q' // 65d2 #536 + '8S' // 65d3 #226 + '39F' // 65d4 #1019 + 'A' // 65d5 + '6Z' // 65d6 #181 + '201Y' // 65d7 #5250 + 'a3K' // 65d8-65d9 #88 + 'A' // 65da + '6Z' // 65db #181 + 'A' // 65dc + '8S' // 65dd #226 + 'A' // 65de + '3K' // 65df #88 + '151M' // 65e0 #3938 + '6Z' // 65e1 #181 + '198H' // 65e2 #5155 + '20Q' // 65e3 #536 + 'A' // 65e4 + '247J' // 65e5 #6431 + '183F' // 65e6 #4763 + '257B' // 65e7 #6683 + '168U' // 65e8 #4388 + '230X' // 65e9 #6003 + 'aA' // 65ea-65eb + '155E' // 65ec #4034 + '168L' // 65ed #4379 + 'a23G' // 65ee-65ef #604 + '6Z' // 65f0 #181 + '123M' // 65f1 #3210 + '16T' // 65f2 #435 + '23G' // 65f3 #604 + '20Q' // 65f4 #536 + '14R' // 65f5 #381 + '7K' // 65f6 #192 + '3W' // 65f7 #100 + '8S' // 65f8 #226 + '3K' // 65f9 #88 + '194U' // 65fa #5064 + '48X' // 65fb #1271 + '20Q' // 65fc #536 + '105N' // 65fd #2743 + '14R' // 65fe #381 + '59N' // 65ff #1547 + '6Z' // 6600 #181 + 'A' // 6601 + '159J' // 6602 #4143 + '6Z' // 6603 #181 + '16T' // 6604 #435 + '39H' // 6605 #1021 + '167I' // 6606 #4350 + '183L' // 6607 #4769 + '3K' // 6608 #88 + '39G' // 6609 #1020 + '123L' // 660a #3209 + '8S' // 660b #226 + '189I' // 660c #4922 + '16T' // 660d #435 + '244N' // 660e #6357 + '173E' // 660f #4502 + '105L' // 6610 #2741 + '39G' // 6611 #1020 + '3K' // 6612 #88 + '233Z' // 6613 #6083 + '169G' // 6614 #4400 + '128V' // 6615 #3349 + '3K' // 6616 #88 + 'A' // 6617 + '34P' // 6618 #899 + 'a8S' // 6619-661a #226 + 'A' // 661b + 'a6Z' // 661c-661d #181 + '39G' // 661e #1020 + '241Q' // 661f #6282 + '206H' // 6620 #5363 + '6Z' // 6621 #181 + '16T' // 6622 #435 + '39F' // 6623 #1019 + '6Z' // 6624 #181 + '68M' // 6625 #1780 + '6Z' // 6626 #181 + '148U' // 6627 #3868 + '198X' // 6628 #5171 + '3K' // 6629 #88 + '14R' // 662a #381 + '6Z' // 662b #181 + '14R' // 662c #381 + '242W' // 662d #6314 + '14R' // 662e #381 + '246G' // 662f #6402 + '39G' // 6630 #1020 + '64U' // 6631 #1684 + 'A' // 6632 + '26F' // 6633 #681 + '15O' // 6634 #404 + '105H' // 6635 #2737 + '15O' // 6636 #404 + '3K' // 6637 #88 + 'A' // 6638 + '16T' // 6639 #435 + '15O' // 663a #404 + '249W' // 663b #6496 + '257H' // 663c #6689 + '10K' // 663d #270 + '1Z' // 663e #51 + 'a3K' // 663f-6640 #88 + '15O' // 6641 #404 + '246Z' // 6642 #6421 + '154E' // 6643 #4008 + '15O' // 6644 #404 + '26F' // 6645 #681 + '3K' // 6646 #88 + 'A' // 6647 + '39F' // 6648 #1019 + '171M' // 6649 #4458 + '16T' // 664a #435 + '105J' // 664b #2739 + '26F' // 664c #681 + '34P' // 664d #899 + '3K' // 664e #88 + '64U' // 664f #1684 + 'A' // 6650 + '3K' // 6651 #88 + '160V' // 6652 #4181 + '105S' // 6653 #2748 + '10K' // 6654 #270 + '2L' // 6655 #63 + '2K' // 6656 #62 + '105G' // 6657 #2736 + '3K' // 6658 #88 + '15O' // 6659 #404 + '217N' // 665a #5655 + '15O' // 665b #404 + '3K' // 665c #88 + 'a15O' // 665d-665e #404 + '134A' // 665f #3484 + '105F' // 6660 #2735 + 'b26F' // 6661-6663 #681 + 'a15O' // 6664-6665 #404 + '105I' // 6666 #2738 + '15O' // 6667 #404 + '193G' // 6668 #5024 + '255F' // 6669 #6635 + '3K' // 666a #88 + '105D' // 666b #2733 + '26F' // 666c #681 + '3K' // 666d #88 + '222N' // 666e #5785 + '228Z' // 666f #5953 + '164T' // 6670 #4283 + '10K' // 6671 #270 + '39H' // 6672 #1021 + '15O' // 6673 #404 + '183T' // 6674 #4777 + '3K' // 6675 #88 + '202Y' // 6676 #5276 + 'a15O' // 6677-6678 #404 + '26F' // 6679 #681 + '227H' // 667a #5909 + '16T' // 667b #435 + '26F' // 667c #681 + '39H' // 667d #1021 + '105C' // 667e #2732 + '3U' // 667f #98 + '49U' // 6680 #1294 + '253N' // 6681 #6591 + '3I' // 6682 #86 + '3U' // 6683 #98 + '26E' // 6684 #680 + '105O' // 6685 #2744 + 'A' // 6686 + '131A' // 6687 #3406 + '164Q' // 6688 #4280 + '141X' // 6689 #3689 + 'A' // 668a + 'b46Y' // 668b-668d #1220 + '26E' // 668e #680 + 'A' // 668f + '26E' // 6690 #680 + '175S' // 6691 #4568 + '46Y' // 6692 #1220 + 'aA' // 6693-6694 + '10K' // 6695 #270 + '210F' // 6696 #5465 + '205E' // 6697 #5334 + '26E' // 6698 #680 + '3U' // 6699 #98 + '105B' // 669a #2731 + 'a3U' // 669b-669c #98 + '26E' // 669d #680 + 'A' // 669e + '46Y' // 669f #1220 + '26E' // 66a0 #680 + 'A' // 66a1 + '67I' // 66a2 #1750 + '10K' // 66a3 #270 + '59L' // 66a4 #1545 + 'A' // 66a5 + '70V' // 66a6 #1841 + '3W' // 66a7 #100 + '177B' // 66a8 #4603 + 'A' // 66a9 + '105P' // 66aa #2745 + '201L' // 66ab #5237 + 'A' // 66ac + '59L' // 66ad #1545 + '137J' // 66ae #3571 + 'aA' // 66af-66b0 + '66Y' // 66b1 #1740 + '26E' // 66b2 #680 + '105A' // 66b3 #2730 + '210Y' // 66b4 #5484 + '59M' // 66b5 #1546 + '34P' // 66b6 #899 + 'A' // 66b7 + '59M' // 66b8 #1546 + '46X' // 66b9 #1219 + '105T' // 66ba #2749 + '46X' // 66bb #1219 + '3U' // 66bc #98 + 'A' // 66bd + '46X' // 66be #1219 + '26C' // 66bf #678 + '3U' // 66c0 #98 + '49U' // 66c1 #1294 + 'a3U' // 66c2-66c3 #98 + '104N' // 66c4 #2717 + 'A' // 66c5 + '200K' // 66c6 #5210 + '104V' // 66c7 #2725 + '7P' // 66c8 #197 + '67I' // 66c9 #1750 + 'aA' // 66ca-66cb + '7P' // 66cc #197 + '39E' // 66cd #1018 + '26C' // 66ce #678 + '9W' // 66cf #256 + 'cA' // 66d0-66d3 + '3U' // 66d4 #98 + '10K' // 66d5 #270 + '135X' // 66d6 #3533 + '10K' // 66d7 #270 + '23F' // 66d8 #603 + '104T' // 66d9 #2723 + 'a7P' // 66da-66db #197 + '143O' // 66dc #3732 + '200L' // 66dd #5211 + '10K' // 66de #270 + '49U' // 66df #1294 + '133Y' // 66e0 #3482 + 'a10K' // 66e1-66e2 #270 + 'bA' // 66e3-66e5 + '128T' // 66e6 #3347 + '39E' // 66e7 #1018 + 'a7P' // 66e8-66e9 #197 + '10K' // 66ea #270 + '3U' // 66eb #98 + '66Y' // 66ec #1740 + 'A' // 66ed + '3U' // 66ee #98 + 'A' // 66ef + '130H' // 66f0 #3387 + '23F' // 66f1 #603 + '212S' // 66f2 #5530 + '147Y' // 66f3 #3846 + '244I' // 66f4 #6352 + '3U' // 66f5 #98 + 'A' // 66f6 + '18U' // 66f7 #488 + '239D' // 66f8 #6217 + '160E' // 66f9 #4164 + '249T' // 66fa #6493 + '3U' // 66fb #98 + '193F' // 66fc #5023 + '70V' // 66fd #1841 + '218B' // 66fe #5669 + '206C' // 66ff #5358 + '41F' // 6700 #1071 + '3U' // 6701 #98 + '46W' // 6702 #1218 + '246B' // 6703 #6397 + 'A' // 6704 + '7P' // 6705 #197 + 'A' // 6706 + '3U' // 6707 #98 + '247I' // 6708 #6430 + '69I' // 6709 #1802 + '23F' // 670a #603 + '226R' // 670b #5893 + '26C' // 670c #678 + '241X' // 670d #6289 + '26C' // 670e #678 + 'a7P' // 670f-6710 #197 + 'A' // 6711 + '3U' // 6712 #98 + '7P' // 6713 #197 + '59I' // 6714 #1542 + '18U' // 6715 #488 + '26C' // 6716 #678 + '197S' // 6717 #5140 + '34O' // 6718 #898 + '7P' // 6719 #197 + 'A' // 671a + '231M' // 671b #6018 + '3U' // 671c #98 + '206G' // 671d #5362 + '104L' // 671e #2715 + '243U' // 671f #6338 + '9W' // 6720 #256 + 'A' // 6721 + '7P' // 6722 #197 + '23F' // 6723 #603 + 'A' // 6724 + '7P' // 6725 #197 + '18U' // 6726 #488 + '104R' // 6727 #2721 + '224A' // 6728 #5824 + '10K' // 6729 #270 + '237J' // 672a #6171 + '206A' // 672b #5356 + '247F' // 672c #6427 + '162B' // 672d #4213 + '18U' // 672e #488 + '3Q' // 672f #94 + 'A' // 6730 + '188P' // 6731 #4903 + '10K' // 6732 #270 + '7P' // 6733 #197 + '148L' // 6734 #3859 + '186B' // 6735 #4837 + '18U' // 6736 #488 + '3U' // 6737 #98 + '9W' // 6738 #256 + '23F' // 6739 #603 + '167M' // 673a #4354 + '10K' // 673b #270 + 'A' // 673c + '125B' // 673d #3251 + '3U' // 673e #98 + '7P' // 673f #197 + '3I' // 6740 #86 + '3U' // 6741 #98 + '2C' // 6742 #54 + '249V' // 6743 #6495 + '46W' // 6744 #1218 + '9W' // 6745 #256 + '133Z' // 6746 #3483 + '9W' // 6747 #256 + '7P' // 6748 #197 + '176H' // 6749 #4583 + 'A' // 674a + '34O' // 674b #898 + 'a7P' // 674c-674d #197 + '218N' // 674e #5681 + '168H' // 674f #4375 + '230W' // 6750 #6002 + '213U' // 6751 #5558 + 'A' // 6752 + '18U' // 6753 #488 + '3U' // 6754 #98 + '7P' // 6755 #197 + '135Y' // 6756 #3534 + 'aA' // 6757-6758 + '9W' // 6759 #256 + 'aA' // 675a-675b + '188C' // 675c #4890 + '9W' // 675d #256 + '59I' // 675e #1542 + '210X' // 675f #5483 + '104P' // 6760 #2719 + '162I' // 6761 #4220 + '7P' // 6762 #197 + 'a3U' // 6763-6764 #98 + '260C' // 6765 #6762 + '3U' // 6766 #98 + '23F' // 6767 #603 + '3I' // 6768 #86 + '10K' // 6769 #270 + '7P' // 676a #197 + '46W' // 676b #1218 + '7P' // 676c #197 + '147U' // 676d #3842 + '7P' // 676e #197 + '210J' // 676f #5469 + '164R' // 6770 #4281 + '244O' // 6771 #6358 + '7P' // 6772 #197 + '18U' // 6773 #488 + '9W' // 6774 #256 + '104S' // 6775 #2722 + '9W' // 6776 #256 + '18U' // 6777 #488 + 'aA' // 6778-6779 + '23F' // 677a #603 + '18U' // 677b #488 + '7P' // 677c #197 + '10K' // 677d #270 + '206D' // 677e #5359 + '230D' // 677f #5983 + '3U' // 6780 #98 + '128U' // 6781 #3348 + '23F' // 6782 #603 + '10K' // 6783 #270 + '123K' // 6784 #3208 + '3U' // 6785 #98 + '34O' // 6786 #898 + '18U' // 6787 #488 + 'A' // 6788 + '123J' // 6789 #3207 + '10K' // 678a #270 + '26D' // 678b #679 + '15N' // 678c #403 + '7H' // 678d #189 + '9W' // 678e #256 + '26D' // 678f #679 + '211D' // 6790 #5489 + '9W' // 6791 #256 + '15N' // 6792 #403 + '26D' // 6793 #679 + 'A' // 6794 + '168T' // 6795 #4387 + '23Z' // 6796 #623 + '234X' // 6797 #6107 + '15N' // 6798 #403 + '9W' // 6799 #256 + '155M' // 679a #4042 + '3U' // 679b #98 + '243D' // 679c #6321 + '182L' // 679d #4743 + '7H' // 679e #189 + '59K' // 679f #1544 + '104W' // 67a0 #2726 + '23Z' // 67a1 #623 + '252B' // 67a2 #6553 + '3H' // 67a3 #85 + '26C' // 67a4 #678 + '7H' // 67a5 #189 + '3U' // 67a6 #98 + 'a7H' // 67a7-67a8 #189 + '23Z' // 67a9 #623 + '3N' // 67aa #91 + '2R' // 67ab #69 + '59K' // 67ac #1544 + '7H' // 67ad #189 + '34O' // 67ae #898 + '142O' // 67af #3706 + '26D' // 67b0 #679 + '15N' // 67b1 #403 + '9W' // 67b2 #256 + '26D' // 67b3 #679 + '9W' // 67b4 #256 + '15N' // 67b5 #403 + '218W' // 67b6 #5690 + '26D' // 67b7 #679 + '104M' // 67b8 #2716 + '15N' // 67b9 #403 + '34O' // 67ba #898 + '15N' // 67bb #403 + '23Z' // 67bc #623 + '3U' // 67bd #98 + '249S' // 67be #6492 + '39E' // 67bf #1018 + 'a15N' // 67c0-67c1 #403 + '9W' // 67c2 #256 + '15N' // 67c3 #403 + '149K' // 67c4 #3884 + 'a15N' // 67c5-67c6 #403 + '7H' // 67c7 #189 + 'a15N' // 67c8-67c9 #403 + '104U' // 67ca #2724 + 'bA' // 67cb-67cd + '9W' // 67ce #256 + '196P' // 67cf #5111 + '202J' // 67d0 #5261 + '136L' // 67d1 #3547 + '26D' // 67d2 #679 + '204Z' // 67d3 #5329 + '198E' // 67d4 #5152 + 'A' // 67d5 + '39E' // 67d6 #1018 + '104O' // 67d7 #2718 + 'a15N' // 67d8-67d9 #403 + '148O' // 67da #3862 + '9W' // 67db #256 + '104Q' // 67dc #2720 + '59H' // 67dd #1541 + '30C' // 67de #782 + '46V' // 67df #1217 + '2K' // 67e0 #62 + '3U' // 67e1 #98 + '30C' // 67e2 #782 + '7H' // 67e3 #189 + '30C' // 67e4 #782 + '239L' // 67e5 #6225 + 'a23Z' // 67e6-67e7 #623 + '7H' // 67e8 #189 + '59H' // 67e9 #1541 + '104Z' // 67ea #2729 + 'A' // 67eb + '139Z' // 67ec #3639 + 'A' // 67ed + '3U' // 67ee #98 + '186A' // 67ef #4836 + '59G' // 67f0 #1540 + '175J' // 67f1 #4559 + '59J' // 67f2 #1543 + '175Y' // 67f3 #4574 + '175I' // 67f4 #4558 + '129Y' // 67f5 #3378 + '104K' // 67f6 #2714 + '30C' // 67f7 #782 + '46V' // 67f8 #1217 + '26C' // 67f9 #678 + '30C' // 67fa #782 + '258D' // 67fb #6711 + '3U' // 67fc #98 + '7H' // 67fd #189 + '59G' // 67fe #1540 + '125K' // 67ff #3260 + '46V' // 6800 #1217 + '30C' // 6801 #782 + '59J' // 6802 #1543 + '104X' // 6803 #2727 + '104Y' // 6804 #2728 + '249U' // 6805 #6494 + '7H' // 6806 #189 + '3Q' // 6807 #94 + '3H' // 6808 #85 + 'a7H' // 6809-680a #189 + '2Y' // 680b #76 + '7H' // 680c #189 + '34L' // 680d #895 + '7H' // 680e #189 + '2C' // 680f #54 + '59C' // 6810 #1536 + '3I' // 6811 #86 + '104G' // 6812 #2710 + '125J' // 6813 #3259 + '59D' // 6814 #1537 + 'A' // 6815 + '104C' // 6816 #2706 + '196U' // 6817 #5116 + '59D' // 6818 #1537 + '3U' // 6819 #98 + 'A' // 681a + '34L' // 681b #895 + '26B' // 681c #677 + '23E' // 681d #602 + '104A' // 681e #2704 + '23Z' // 681f #623 + '26B' // 6820 #677 + '223I' // 6821 #5806 + '39D' // 6822 #1017 + 'A' // 6823 + '7H' // 6824 #189 + '59F' // 6825 #1539 + '7H' // 6826 #189 + '23Z' // 6827 #623 + 'a23E' // 6828-6829 #602 + '69D' // 682a #1797 + '23E' // 682b #602 + 'a23Z' // 682c-682d #623 + '26B' // 682e #677 + '103Y' // 682f #2702 + '3U' // 6830 #98 + 'c23E' // 6831-6834 #602 + '7H' // 6835 #189 + '34L' // 6836 #895 + '137T' // 6837 #3581 + '209F' // 6838 #5439 + '222T' // 6839 #5791 + '26B' // 683a #677 + '23E' // 683b #602 + '68V' // 683c #1789 + '161A' // 683d #4186 + '23E' // 683e #602 + '3U' // 683f #98 + '39D' // 6840 #1017 + '104E' // 6841 #2708 + '174E' // 6842 #4528 + '219K' // 6843 #5704 + 'a23E' // 6844-6845 #602 + '186C' // 6846 #4838 + '34L' // 6847 #895 + '231A' // 6848 #6006 + '23E' // 6849 #602 + '59C' // 684a #1536 + 'A' // 684b + '207J' // 684c #5391 + '3U' // 684d #98 + '39D' // 684e #1017 + 'A' // 684f + '154Y' // 6850 #4028 + '174U' // 6851 #4544 + '3U' // 6852 #98 + '39D' // 6853 #1017 + '123I' // 6854 #3206 + '9V' // 6855 #255 + '59F' // 6856 #1539 + 'b2U' // 6857-6859 #72 + 'A' // 685a + '2U' // 685b #72 + '257V' // 685c #6703 + '9V' // 685d #255 + 'A' // 685e + '36E' // 685f #940 + 'b7H' // 6860-6862 #189 + '249R' // 6863 #6491 + '7H' // 6864 #189 + '104J' // 6865 #2713 + '7H' // 6866 #189 + '36E' // 6867 #940 + '7H' // 6868 #189 + '3X' // 6869 #101 + 'A' // 686a + '9V' // 686b #255 + 'A' // 686c + '104H' // 686d #2711 + '2U' // 686e #72 + '9V' // 686f #255 + '2U' // 6870 #72 + '14J' // 6871 #373 + '9V' // 6872 #255 + 'A' // 6873 + '9V' // 6874 #255 + '14J' // 6875 #373 + '187L' // 6876 #4873 + '9V' // 6877 #255 + 'A' // 6878 + '14J' // 6879 #373 + '2U' // 687a #72 + 'a14J' // 687b-687c #373 + 'A' // 687d + '14J' // 687e #373 + '171L' // 687f #4457 + '26B' // 6880 #677 + '187V' // 6881 #4883 + '14J' // 6882 #373 + '9V' // 6883 #255 + '30A' // 6884 #780 + '198Q' // 6885 #5164 + '9V' // 6886 #255 + 'A' // 6887 + '30A' // 6888 #780 + 'cA' // 6889-688c + 'a2U' // 688d-688e #72 + '46S' // 688f #1214 + '2U' // 6890 #72 + 'a26B' // 6891-6892 #677 + '152Z' // 6893 #3977 + '103T' // 6894 #2697 + 'A' // 6895 + '14J' // 6896 #373 + '153F' // 6897 #3983 + '14J' // 6898 #373 + 'a2U' // 6899-689a #72 + '9V' // 689b #255 + '14J' // 689c #373 + '240Z' // 689d #6265 + 'A' // 689e + '46S' // 689f #1214 + '9V' // 68a0 #255 + '249Q' // 68a1 #6490 + '46S' // 68a2 #1214 + '9V' // 68a3 #255 + 'A' // 68a4 + '2U' // 68a5 #72 + '103X' // 68a6 #2701 + '128S' // 68a7 #3346 + '183U' // 68a8 #4778 + '14J' // 68a9 #373 + 'a2U' // 68aa-68ab #72 + 'A' // 68ac + '158B' // 68ad #4109 + '2U' // 68ae #72 + '66X' // 68af #1739 + '190H' // 68b0 #4947 + '104F' // 68b1 #2709 + '14J' // 68b2 #373 + '65U' // 68b3 #1710 + '14J' // 68b4 #373 + '158C' // 68b5 #4110 + '104B' // 68b6 #2705 + 'aA' // 68b7-68b8 + '103V' // 68b9 #2699 + '36E' // 68ba #940 + '2U' // 68bb #72 + '36E' // 68bc #940 + '59E' // 68bd #1538 + 'a7H' // 68be-68bf #189 + '2C' // 68c0 #54 + '59E' // 68c1 #1538 + '7H' // 68c2 #189 + '9V' // 68c3 #255 + '202P' // 68c4 #5267 + '103Q' // 68c5 #2694 + '9V' // 68c6 #255 + 'A' // 68c7 + '9V' // 68c8 #255 + '66X' // 68c9 #1739 + '9V' // 68ca #255 + '167L' // 68cb #4353 + '36E' // 68cc #940 + '146G' // 68cd #3802 + 'A' // 68ce + '2U' // 68cf #72 + 'a9V' // 68d0-68d1 #255 + '210C' // 68d2 #5462 + '26A' // 68d3 #676 + '2U' // 68d4 #72 + '65U' // 68d5 #1710 + '14J' // 68d6 #373 + '64F' // 68d7 #1669 + '123H' // 68d8 #3205 + '2U' // 68d9 #72 + '161W' // 68da #4208 + 'A' // 68db + '2U' // 68dc #72 + '19I' // 68dd #502 + 'A' // 68de + '174T' // 68df #4543 + '133S' // 68e0 #3476 + '26A' // 68e1 #676 + 'A' // 68e2 + '26A' // 68e3 #676 + '19I' // 68e4 #502 + '2U' // 68e5 #72 + '46T' // 68e6 #1215 + '158A' // 68e7 #4108 + '46R' // 68e8 #1213 + '46T' // 68e9 #1215 + 'b26A' // 68ea-68ec #676 + '19I' // 68ed #502 + '216M' // 68ee #5628 + 'a26A' // 68ef-68f0 #676 + '103W' // 68f1 #2700 + '148N' // 68f2 #3861 + '26B' // 68f3 #677 + '13O' // 68f4 #352 + '133X' // 68f5 #3481 + 'a26A' // 68f6-68f7 #676 + 'A' // 68f8 + '46R' // 68f9 #1213 + '124R' // 68fa #3241 + 'b26A' // 68fb-68fd #676 + 'A' // 68fe + '13O' // 68ff #352 + '46R' // 6900 #1213 + '103U' // 6901 #2698 + '46T' // 6902 #1215 + '30A' // 6903 #780 + '30B' // 6904 #781 + '189H' // 6905 #4921 + 'a16S' // 6906-6907 #434 + '19I' // 6908 #502 + '30B' // 6909 #781 + '2U' // 690a #72 + '16S' // 690b #434 + '2U' // 690c #72 + '204Y' // 690d #5328 + '149B' // 690e #3875 + '30B' // 690f #781 + '16S' // 6910 #434 + '2U' // 6911 #72 + '166J' // 6912 #4325 + '2U' // 6913 #72 + 'aA' // 6914-6915 + '2U' // 6916 #72 + '30B' // 6917 #781 + '34L' // 6918 #895 + '30A' // 6919 #780 + 'a16S' // 691a-691b #434 + '260H' // 691c #6767 + 'aA' // 691d-691e + 'a13O' // 691f-6920 #352 + 'b2U' // 6921-6923 #72 + '13O' // 6924 #352 + '16S' // 6925 #434 + '2U' // 6926 #72 + '260Y' // 6927 #6784 + '2U' // 6928 #72 + 'A' // 6929 + '16S' // 692a #434 + 'A' // 692b + '46U' // 692c #1216 + '13O' // 692d #352 + 'aA' // 692e-692f + '151I' // 6930 #3934 + '2U' // 6931 #72 + '46U' // 6932 #1216 + '2U' // 6933 #72 + '16S' // 6934 #434 + '2U' // 6935 #72 + '30A' // 6936 #780 + 'A' // 6937 + '2U' // 6938 #72 + '16S' // 6939 #434 + 'A' // 693a + '2U' // 693b #72 + '34N' // 693c #897 + '34M' // 693d #896 + '34N' // 693e #897 + '104D' // 693f #2707 + '34N' // 6940 #897 + '13O' // 6941 #352 + '16S' // 6942 #434 + '104I' // 6943 #2712 + '13O' // 6944 #352 + '19I' // 6945 #502 + '30A' // 6946 #780 + 'aA' // 6947-6948 + '30B' // 6949 #781 + '201B' // 694a #5227 + '13O' // 694b #352 + 'aA' // 694c-694d + '2U' // 694e #72 + 'bA' // 694f-6951 + '34N' // 6952 #897 + '166V' // 6953 #4337 + '34M' // 6954 #896 + '103Z' // 6955 #2703 + '46U' // 6956 #1216 + '34M' // 6957 #896 + 'A' // 6958 + '34M' // 6959 #896 + '201U' // 695a #5246 + '16S' // 695b #434 + '30B' // 695c #781 + '16S' // 695d #434 + '103R' // 695e #2695 + '34N' // 695f #897 + '153N' // 6960 #3991 + '103S' // 6961 #2696 + 'a34M' // 6962-6963 #896 + '8F' // 6964 #213 + '29Z' // 6965 #779 + '8F' // 6966 #213 + '25Z' // 6967 #675 + '20O' // 6968 #534 + 'a8F' // 6969-696a #213 + '20O' // 696b #534 + '8F' // 696c #213 + '244X' // 696d #6367 + 'a20O' // 696e-696f #534 + '29Z' // 6970 #779 + '19I' // 6971 #502 + '39C' // 6972 #1016 + 'a8F' // 6973-6974 #213 + '229K' // 6975 #5964 + '25Z' // 6976 #675 + '133T' // 6977 #3477 + 'a20O' // 6978-6979 #534 + '29Z' // 697a #779 + '2U' // 697b #72 + '253B' // 697c #6579 + '71A' // 697d #1846 + '2U' // 697e #72 + '19I' // 697f #502 + '8F' // 6980 #213 + '2U' // 6981 #72 + '213S' // 6982 #5556 + 'A' // 6983 + '3X' // 6984 #101 + '20P' // 6985 #535 + '103D' // 6986 #2681 + 'b13O' // 6987-6989 #352 + '103H' // 698a #2685 + 'aA' // 698b-698c + '8F' // 698d #213 + '103J' // 698e #2687 + 'A' // 698f + '25Z' // 6990 #675 + '29Z' // 6991 #779 + '2U' // 6992 #72 + 'A' // 6993 + '8F' // 6994 #213 + '64F' // 6995 #1669 + '8F' // 6996 #213 + '20P' // 6997 #535 + '8F' // 6998 #213 + '20P' // 6999 #535 + '25Z' // 699a #675 + '103G' // 699b #2684 + '207I' // 699c #5390 + 'A' // 699d + '25Z' // 699e #675 + '12G' // 699f #318 + 'a2U' // 69a0-69a1 #72 + '12G' // 69a2 #318 + 'a20P' // 69a3-69a4 #535 + '103C' // 69a5 #2680 + '8F' // 69a6 #213 + '20O' // 69a7 #534 + '133W' // 69a8 #3480 + 'aA' // 69a9-69aa + '8F' // 69ab #213 + 'A' // 69ac + '8F' // 69ad #213 + '208I' // 69ae #5416 + '29Z' // 69af #779 + '2U' // 69b0 #72 + '8F' // 69b1 #213 + '39C' // 69b2 #1016 + '25Z' // 69b3 #675 + '133U' // 69b4 #3478 + 'A' // 69b5 + '25Z' // 69b6 #675 + '8F' // 69b7 #213 + '2U' // 69b8 #72 + 'A' // 69b9 + '2U' // 69ba #72 + '20O' // 69bb #534 + '8F' // 69bc #213 + 'A' // 69bd + '2U' // 69be #72 + '29Z' // 69bf #779 + '39C' // 69c0 #1016 + '20O' // 69c1 #534 + 'A' // 69c2 + '20O' // 69c3 #534 + '20P' // 69c4 #535 + '19I' // 69c5 #502 + 'A' // 69c6 + '19I' // 69c7 #502 + '2U' // 69c8 #72 + 'A' // 69c9 + '8F' // 69ca #213 + '213O' // 69cb #5552 + '103F' // 69cc #2683 + '194Y' // 69cd #5068 + '8F' // 69ce #213 + '2U' // 69cf #72 + '20O' // 69d0 #534 + '8F' // 69d1 #213 + 'A' // 69d2 + '133V' // 69d3 #3479 + '20P' // 69d4 #535 + '12G' // 69d5 #318 + '39C' // 69d6 #1016 + '19I' // 69d7 #502 + '71A' // 69d8 #1846 + '8F' // 69d9 #213 + '13O' // 69da #352 + '3X' // 69db #101 + 'A' // 69dc + 'a1Q' // 69dd-69de #42 + '3G' // 69df #84 + '13O' // 69e0 #352 + '12G' // 69e1 #318 + 'a1Q' // 69e2-69e3 #42 + '20P' // 69e4 #535 + '1Q' // 69e5 #42 + '13O' // 69e6 #352 + '1Q' // 69e7 #42 + '103A' // 69e8 #2678 + '29Y' // 69e9 #778 + '249P' // 69ea #6489 + '1Q' // 69eb #42 + '13O' // 69ec #352 + '59A' // 69ed #1534 + '29Y' // 69ee #778 + '1Q' // 69ef #42 + 'A' // 69f0 + 'b59A' // 69f1-69f3 #1534 + '29Y' // 69f4 #778 + '1Q' // 69f5 #42 + '29Y' // 69f6 #778 + 'A' // 69f7 + '20P' // 69f8 #535 + '23D' // 69f9 #601 + '20P' // 69fa #535 + '103L' // 69fb #2689 + '13O' // 69fc #352 + '174L' // 69fd #4535 + '12K' // 69fe #322 + '58Z' // 69ff #1533 + '46O' // 6a00 #1210 + '123G' // 6a01 #3204 + '240K' // 6a02 #6250 + '23D' // 6a03 #601 + 'A' // 6a04 + '29Y' // 6a05 #778 + '11L' // 6a06 #297 + 'aA' // 6a07-6a08 + '11L' // 6a09 #297 + '102Z' // 6a0a #2677 + '103K' // 6a0b #2688 + '23D' // 6a0c #601 + 'aA' // 6a0d-6a0e + '1Q' // 6a0f #42 + 'A' // 6a10 + '65T' // 6a11 #1709 + '1Q' // 6a12 #42 + '225J' // 6a13 #5859 + 'a1Q' // 6a14-6a15 #42 + 'A' // 6a16 + '58Z' // 6a17 #1533 + '103P' // 6a18 #2693 + '235C' // 6a19 #6112 + '23D' // 6a1a #601 + '46O' // 6a1b #1210 + '12G' // 6a1c #318 + '1Q' // 6a1d #42 + '123F' // 6a1e #3203 + '128Q' // 6a1f #3344 + '1Q' // 6a20 #42 + '228Y' // 6a21 #5952 + '1Q' // 6a22 #42 + '232O' // 6a23 #6046 + '1Q' // 6a24 #42 + 'bA' // 6a25-6a27 + '46O' // 6a28 #1210 + '103M' // 6a29 #2690 + '258O' // 6a2a #6722 + '103I' // 6a2b #2686 + 'a12G' // 6a2c-6a2d #318 + '1Q' // 6a2e #42 + '11L' // 6a2f #297 + '12K' // 6a30 #322 + '4C' // 6a31 #106 + '29Y' // 6a32 #778 + '23D' // 6a33 #601 + '1Q' // 6a34 #42 + '34K' // 6a35 #894 + 'a1Q' // 6a36-6a37 #42 + '65T' // 6a38 #1709 + '211Z' // 6a39 #5511 + '152R' // 6a3a #3969 + '16R' // 6a3b #433 + '11L' // 6a3c #297 + '136N' // 6a3d #3549 + 'a16R' // 6a3e-6a3f #433 + '46P' // 6a40 #1211 + 'aA' // 6a41-6a42 + '12G' // 6a43 #318 + '139Y' // 6a44 #3638 + '23D' // 6a45 #601 + '12K' // 6a46 #322 + '16R' // 6a47 #433 + '34K' // 6a48 #894 + '1Q' // 6a49 #42 + '12K' // 6a4a #322 + '213F' // 6a4b #5543 + '12G' // 6a4c #318 + 'A' // 6a4d + '12K' // 6a4e #322 + 'A' // 6a4f + '16R' // 6a50 #433 + '1Q' // 6a51 #42 + '34K' // 6a52 #894 + '103N' // 6a53 #2691 + '1Q' // 6a54 #42 + 'a25Y' // 6a55-6a56 #674 + '46Q' // 6a57 #1212 + '174D' // 6a58 #4527 + '159M' // 6a59 #4146 + '46Q' // 6a5a #1212 + '16R' // 6a5b #433 + 'aA' // 6a5c-6a5d + '46P' // 6a5e #1211 + '244H' // 6a5f #6351 + 'A' // 6a60 + '157Z' // 6a61 #4107 + '16R' // 6a62 #433 + '12G' // 6a63 #318 + '1Q' // 6a64 #42 + '103O' // 6a65 #2692 + '16R' // 6a66 #433 + '12K' // 6a67 #322 + 'aA' // 6a68-6a69 + '25Y' // 6a6a #674 + '193D' // 6a6b #5021 + 'dA' // 6a6c-6a70 + '103B' // 6a71 #2679 + 'a1Q' // 6a72-6a73 #42 + '12G' // 6a74 #318 + 'bA' // 6a75-6a77 + '1Q' // 6a78 #42 + '11L' // 6a79 #297 + '23D' // 6a7a #601 + 'A' // 6a7b + '11L' // 6a7c #297 + 'A' // 6a7d + 'a16R' // 6a7e-6a7f #433 + '103E' // 6a80 #2682 + '25Y' // 6a81 #674 + '12G' // 6a82 #318 + '1Q' // 6a83 #42 + '34K' // 6a84 #894 + 'A' // 6a85 + '12K' // 6a86 #322 + '25Y' // 6a87 #674 + 'A' // 6a88 + '46N' // 6a89 #1209 + '12G' // 6a8a #318 + '1Q' // 6a8b #42 + 'A' // 6a8c + '46N' // 6a8d #1209 + '59B' // 6a8e #1535 + '12G' // 6a8f #318 + 'a16R' // 6a90-6a91 #433 + '46Q' // 6a92 #1212 + 'A' // 6a93 + '217M' // 6a94 #5654 + 'aA' // 6a95-6a96 + '34K' // 6a97 #894 + 'A' // 6a98 + '12G' // 6a99 #318 + 'A' // 6a9a + '1Q' // 6a9b #42 + '59B' // 6a9c #1535 + '23D' // 6a9d #601 + '16R' // 6a9e #433 + '25Y' // 6a9f #674 + '16R' // 6aa0 #433 + '25Y' // 6aa1 #674 + '225K' // 6aa2 #5860 + '46N' // 6aa3 #1209 + '46P' // 6aa4 #1211 + '25Y' // 6aa5 #674 + 'A' // 6aa6 + '12G' // 6aa7 #318 + '29X' // 6aa8 #777 + '11L' // 6aa9 #297 + '1Q' // 6aaa #42 + '8E' // 6aab #212 + '164P' // 6aac #4279 + 'A' // 6aad + '8E' // 6aae #212 + '66J' // 6aaf #1725 + '12K' // 6ab0 #322 + '46L' // 6ab1 #1207 + '58Y' // 6ab2 #1532 + '146E' // 6ab3 #3800 + '1Q' // 6ab4 #42 + '39A' // 6ab5 #1014 + 'A' // 6ab6 + '11L' // 6ab7 #297 + '66J' // 6ab8 #1725 + 'A' // 6ab9 + '39A' // 6aba #1014 + '133R' // 6abb #3475 + 'A' // 6abc + '12K' // 6abd #322 + '14H' // 6abe #371 + '12K' // 6abf #322 + 'A' // 6ac0 + '1Q' // 6ac1 #42 + '58X' // 6ac2 #1531 + '200J' // 6ac3 #5209 + '11L' // 6ac4 #297 + '29X' // 6ac5 #777 + '8E' // 6ac6 #212 + 'A' // 6ac7 + '8E' // 6ac8 #212 + '14H' // 6ac9 #371 + '29W' // 6aca #776 + 'A' // 6acb + '8E' // 6acc #212 + 'A' // 6acd + '11L' // 6ace #297 + 'A' // 6acf + 'a1Q' // 6ad0-6ad1 #42 + '11L' // 6ad2 #297 + '58X' // 6ad3 #1531 + '46L' // 6ad4 #1207 + 'a1Q' // 6ad5-6ad6 #42 + 'A' // 6ad7 + '58Y' // 6ad8 #1532 + '11L' // 6ad9 #297 + 'a46M' // 6ada-6adb #1208 + '12K' // 6adc #322 + 'a14H' // 6add-6ade #371 + '8E' // 6adf #212 + '11L' // 6ae0 #297 + 'A' // 6ae1 + '1Q' // 6ae2 #42 + 'A' // 6ae3 + '12K' // 6ae4 #322 + '162L' // 6ae5 #4223 + 'A' // 6ae6 + 'a8E' // 6ae7-6ae8 #212 + 'A' // 6ae9 + '14H' // 6aea #371 + '29X' // 6aeb #777 + '14H' // 6aec #371 + 'bA' // 6aed-6aef + '1Q' // 6af0 #42 + '14H' // 6af1 #371 + '1Q' // 6af2 #42 + '14H' // 6af3 #371 + 'aA' // 6af4-6af5 + '102U' // 6af6 #2672 + 'A' // 6af7 + '14H' // 6af8 #371 + 'A' // 6af9 + '14H' // 6afa #371 + '195S' // 6afb #5088 + '14H' // 6afc #371 + '1Q' // 6afd #42 + 'cA' // 6afe-6b01 + 'a1Q' // 6b02-6b03 #42 + '204A' // 6b04 #5304 + '102O' // 6b05 #2666 + 'a1Q' // 6b06-6b07 #42 + 'A' // 6b08 + '14H' // 6b09 #371 + '239V' // 6b0a #6235 + '1Q' // 6b0b #42 + '260X' // 6b0c #6783 + 'a11L' // 6b0d-6b0e #297 + 'b8E' // 6b0f-6b11 #212 + '46M' // 6b12 #1208 + '39A' // 6b13 #1014 + 'aA' // 6b14-6b15 + '65F' // 6b16 #1695 + '8E' // 6b17 #212 + 'A' // 6b18 + '11L' // 6b19 #297 + 'A' // 6b1a + '1Q' // 6b1b #42 + 'A' // 6b1c + 'a14H' // 6b1d-6b1e #371 + '1Q' // 6b1f #42 + '169D' // 6b20 #4397 + '68U' // 6b21 #1788 + '3Q' // 6b22 #94 + '200I' // 6b23 #5208 + '12K' // 6b24 #322 + '39A' // 6b25 #1014 + 'A' // 6b26 + '256B' // 6b27 #6657 + '1Q' // 6b28 #42 + 'aA' // 6b29-6b2a + '1Q' // 6b2b #42 + '8E' // 6b2c #212 + 'aA' // 6b2d-6b2e + '1Q' // 6b2f #42 + 'A' // 6b30 + '29X' // 6b31 #777 + '199E' // 6b32 #5178 + 'aA' // 6b33-6b34 + 'a14H' // 6b35-6b36 #371 + '8E' // 6b37 #212 + '146F' // 6b38 #3801 + '8E' // 6b39 #212 + '182K' // 6b3a #4742 + '8E' // 6b3b #212 + 'A' // 6b3c + '65F' // 6b3d #1695 + '240X' // 6b3e #6263 + '1Q' // 6b3f #42 + 'bA' // 6b40-6b42 + '8E' // 6b43 #212 + 'aA' // 6b44-6b45 + '46M' // 6b46 #1208 + '157Y' // 6b47 #4106 + '29X' // 6b48 #777 + '193E' // 6b49 #5022 + '1Q' // 6b4a #42 + 'A' // 6b4b + '216L' // 6b4c #5627 + '1Q' // 6b4d #42 + '128P' // 6b4e #3343 + 'A' // 6b4f + '217K' // 6b50 #5652 + 'A' // 6b51 + '46L' // 6b52 #1207 + '256T' // 6b53 #6675 + '8E' // 6b54 #212 + '29X' // 6b55 #777 + '1Q' // 6b56 #42 + '29W' // 6b57 #776 + '12K' // 6b58 #322 + '8E' // 6b59 #212 + 'A' // 6b5a + '8E' // 6b5b #212 + 'A' // 6b5c + '1Q' // 6b5d #42 + 'A' // 6b5e + '58W' // 6b5f #1530 + '8E' // 6b60 #212 + '232N' // 6b61 #6045 + '223N' // 6b62 #5811 + '41F' // 6b63 #1071 + '240U' // 6b64 #6260 + '225I' // 6b65 #5858 + '212R' // 6b66 #5529 + '151H' // 6b67 #3933 + 'A' // 6b68 + '259C' // 6b69 #6736 + '154B' // 6b6a #4005 + 'a1Q' // 6b6b-6b6c #42 + '11L' // 6b6d #297 + '1Q' // 6b6e #42 + '102S' // 6b6f #2670 + '12K' // 6b70 #322 + 'A' // 6b71 + '217L' // 6b72 #5653 + '258Z' // 6b73 #6733 + '102T' // 6b74 #2671 + '1Q' // 6b75 #42 + 'A' // 6b76 + '225H' // 6b77 #5857 + '200H' // 6b78 #5207 + '128R' // 6b79 #3345 + '8E' // 6b7a #212 + '68E' // 6b7b #1772 + '3W' // 6b7c #100 + 'a1Q' // 6b7d-6b7e #42 + '58W' // 6b7f #1530 + 'b8E' // 6b80-6b82 #212 + 'a102N' // 6b83-6b84 #2665 + '21K' // 6b85 #556 + '102P' // 6b86 #2667 + '2W' // 6b87 #74 + 'A' // 6b88 + '46J' // 6b89 #1205 + '203H' // 6b8a #5285 + '259H' // 6b8b #6741 + 'A' // 6b8c + '23C' // 6b8d #600 + 'bA' // 6b8e-6b90 + '102W' // 6b91 #2674 + 'a8R' // 6b92-6b93 #225 + 'A' // 6b94 + '21K' // 6b95 #556 + '167H' // 6b96 #4349 + '21K' // 6b97 #556 + '185V' // 6b98 #4831 + 'A' // 6b99 + '8R' // 6b9a #225 + '23C' // 6b9b #600 + 'aA' // 6b9c-6b9d + '46J' // 6b9e #1205 + 'a5L' // 6b9f-6ba0 #141 + '8R' // 6ba1 #225 + '23C' // 6ba2 #600 + '21K' // 6ba3 #556 + '23C' // 6ba4 #600 + 'bA' // 6ba5-6ba7 + '5L' // 6ba8 #141 + '21K' // 6ba9 #556 + '23C' // 6baa #600 + '102M' // 6bab #2664 + '5L' // 6bac #141 + '23C' // 6bad #600 + '102I' // 6bae #2660 + '63F' // 6baf #1643 + '21K' // 6bb0 #556 + '5L' // 6bb1 #141 + '46J' // 6bb2 #1205 + '23C' // 6bb3 #600 + '254A' // 6bb4 #6604 + '230C' // 6bb5 #5982 + 'A' // 6bb6 + '133Q' // 6bb7 #3474 + 'a5L' // 6bb8-6bb9 #141 + '215Q' // 6bba #5606 + '253R' // 6bbb #6595 + '193B' // 6bbc #5019 + '23C' // 6bbd #600 + '5L' // 6bbe #141 + '168Y' // 6bbf #4392 + '185Y' // 6bc0 #4834 + '102V' // 6bc1 #2673 + '8R' // 6bc2 #225 + 'a5L' // 6bc3-6bc4 #141 + '166K' // 6bc5 #4326 + '146C' // 6bc6 #3798 + 'b5L' // 6bc7-6bc9 #141 + '8R' // 6bca #225 + '128N' // 6bcb #3341 + '9U' // 6bcc #254 + '221Y' // 6bcd #5770 + '259B' // 6bce #6735 + '232M' // 6bcf #6044 + '14I' // 6bd0 #372 + '8R' // 6bd1 #225 + '209Y' // 6bd2 #5458 + '133P' // 6bd3 #3473 + '236E' // 6bd4 #6140 + '3Y' // 6bd5 #102 + 'b46I' // 6bd6-6bd8 #1204 + '3W' // 6bd9 #100 + '21K' // 6bda #556 + '221I' // 6bdb #5754 + '29W' // 6bdc #776 + 'A' // 6bdd + '39B' // 6bde #1015 + '5L' // 6bdf #141 + 'A' // 6be0 + '9U' // 6be1 #254 + 'A' // 6be2 + '5L' // 6be3 #141 + 'aA' // 6be4-6be5 + '21K' // 6be6 #556 + '5L' // 6be7 #141 + 'aA' // 6be8-6be9 + '29W' // 6bea #776 + '185X' // 6beb #4833 + '46I' // 6bec #1204 + 'A' // 6bed + '5L' // 6bee #141 + '159E' // 6bef #4138 + 'A' // 6bf0 + '21K' // 6bf1 #556 + 'A' // 6bf2 + '9U' // 6bf3 #254 + 'a8R' // 6bf4-6bf5 #225 + 'A' // 6bf6 + '5L' // 6bf7 #141 + 'A' // 6bf8 + '9U' // 6bf9 #254 + '29W' // 6bfa #776 + 'aA' // 6bfb-6bfc + '14I' // 6bfd #372 + 'A' // 6bfe + '46K' // 6bff #1206 + '39B' // 6c00 #1015 + 'A' // 6c01 + '46K' // 6c02 #1206 + 'A' // 6c03 + '5L' // 6c04 #141 + '9U' // 6c05 #254 + '14I' // 6c06 #372 + '8R' // 6c07 #225 + '63F' // 6c08 #1643 + 'a5L' // 6c09-6c0a #141 + 'A' // 6c0b + '39B' // 6c0c #1015 + '9U' // 6c0d #254 + '5L' // 6c0e #141 + '191P' // 6c0f #4981 + '9U' // 6c10 #254 + '235F' // 6c11 #6115 + '5L' // 6c12 #141 + '102H' // 6c13 #2659 + '139X' // 6c14 #3637 + '8R' // 6c15 #225 + '14I' // 6c16 #372 + '260J' // 6c17 #6769 + '14I' // 6c18 #372 + '9U' // 6c19 #254 + '14I' // 6c1a #372 + '185Z' // 6c1b #4835 + '29W' // 6c1c #776 + 'aA' // 6c1d-6c1e + '102K' // 6c1f #2662 + 'A' // 6c20 + '14I' // 6c21 #372 + '3G' // 6c22 #84 + '240J' // 6c23 #6249 + '9U' // 6c24 #254 + '8R' // 6c25 #225 + '9U' // 6c26 #254 + '171K' // 6c27 #4456 + '102L' // 6c28 #2663 + '8R' // 6c29 #225 + '14I' // 6c2a #372 + '126H' // 6c2b #3283 + '9U' // 6c2c #254 + '8R' // 6c2d #225 + '102J' // 6c2e #2661 + '102X' // 6c2f #2675 + 'b14I' // 6c30-6c32 #372 + '9U' // 6c33 #254 + '244P' // 6c34 #6359 + 'a9U' // 6c35-6c36 #254 + '102R' // 6c37 #2669 + '220K' // 6c38 #5730 + '14I' // 6c39 #372 + '9U' // 6c3a #254 + '5L' // 6c3b #141 + '8R' // 6c3c #225 + '14I' // 6c3d #372 + '46I' // 6c3e #1204 + '9U' // 6c3f #254 + '139W' // 6c40 #3636 + '190C' // 6c41 #4942 + '236L' // 6c42 #6147 + '39B' // 6c43 #1015 + 'aA' // 6c44-6c45 + '14I' // 6c46 #372 + '2C' // 6c47 #54 + 'A' // 6c48 + '102Y' // 6c49 #2676 + 'a9U' // 6c4a-6c4b #254 + '14I' // 6c4c #372 + '46K' // 6c4d #1206 + '102Q' // 6c4e #2668 + '9U' // 6c4f #254 + '159Z' // 6c50 #4159 + 'A' // 6c51 + '5L' // 6c52 #141 + 'A' // 6c53 + '18T' // 6c54 #487 + '58R' // 6c55 #1525 + '8R' // 6c56 #225 + '182R' // 6c57 #4749 + '29V' // 6c58 #775 + '151G' // 6c59 #3932 + '102D' // 6c5a #2655 + 'a18T' // 6c5b-6c5c #487 + '128O' // 6c5d #3342 + '22Z' // 6c5e #597 + '216B' // 6c5f #5617 + '205M' // 6c60 #5342 + '184K' // 6c61 #4794 + '5L' // 6c62 #141 + '8R' // 6c63 #225 + '3N' // 6c64 #91 + 'a29V' // 6c65-6c66 #775 + '18T' // 6c67 #487 + '22Z' // 6c68 #597 + '46H' // 6c69 #1203 + '164N' // 6c6a #4277 + '18T' // 6c6b #487 + 'A' // 6c6c + '22Z' // 6c6d #597 + '29V' // 6c6e #775 + '18T' // 6c6f #487 + '142Q' // 6c70 #3708 + '29V' // 6c71 #775 + '101Z' // 6c72 #2651 + 'a18T' // 6c73-6c74 #487 + '29V' // 6c75 #775 + '139V' // 6c76 #3635 + 'A' // 6c77 + 'a18T' // 6c78-6c79 #487 + '223T' // 6c7a #5817 + '5L' // 6c7b #141 + '8R' // 6c7c #225 + '218E' // 6c7d #5672 + '101V' // 6c7e #2647 + '34J' // 6c7f #893 + 'A' // 6c80 + '123E' // 6c81 #3202 + '58R' // 6c82 #1525 + '151F' // 6c83 #3931 + '18T' // 6c84 #487 + 'b22Z' // 6c85-6c87 #597 + '175H' // 6c88 #4557 + '193C' // 6c89 #5020 + 'A' // 6c8a + '8R' // 6c8b #225 + '22Z' // 6c8c #597 + '46G' // 6c8d #1202 + 'A' // 6c8e + '46H' // 6c8f #1203 + '178S' // 6c90 #4646 + '8R' // 6c91 #225 + '239U' // 6c92 #6234 + 'a22Z' // 6c93-6c94 #597 + '249N' // 6c95 #6487 + '199D' // 6c96 #5177 + '5L' // 6c97 #141 + '18T' // 6c98 #487 + '219J' // 6c99 #5703 + '22Z' // 6c9a #597 + '157X' // 6c9b #4105 + '5L' // 6c9c #141 + '46H' // 6c9d #1203 + 'A' // 6c9e + '58T' // 6c9f #1527 + 'A' // 6ca0 + '254U' // 6ca1 #6624 + '102F' // 6ca2 #2657 + 'a8R' // 6ca3-6ca4 #225 + '3G' // 6ca5 #84 + '2K' // 6ca6 #62 + '3H' // 6ca7 #85 + 'a8R' // 6ca8-6ca9 #225 + '58T' // 6caa #1527 + '146D' // 6cab #3799 + 'a18T' // 6cac-6cad #487 + '22Z' // 6cae #597 + '29V' // 6caf #775 + '46G' // 6cb0 #1202 + 'a23A' // 6cb1-6cb2 #598 + '67X' // 6cb3 #1765 + '23A' // 6cb4 #598 + '5D' // 6cb5 #133 + 'A' // 6cb6 + '5D' // 6cb7 #133 + '143D' // 6cb8 #3721 + '219Z' // 6cb9 #5719 + '23A' // 6cba #598 + '244G' // 6cbb #6350 + '102C' // 6cbc #2654 + '123D' // 6cbd #3201 + '164M' // 6cbe #4276 + '183I' // 6cbf #4766 + 'A' // 6cc0 + '229T' // 6cc1 #5973 + '58Q' // 6cc2 #1524 + '58V' // 6cc3 #1529 + '135Q' // 6cc4 #3526 + 'a23A' // 6cc5-6cc6 #598 + '58V' // 6cc7 #1529 + 'A' // 6cc8 + '198Z' // 6cc9 #5173 + '176R' // 6cca #4593 + '34J' // 6ccb #893 + '153S' // 6ccc #3996 + '5L' // 6ccd #141 + '34J' // 6cce #893 + '5L' // 6ccf #141 + 'b23A' // 6cd0-6cd2 #598 + '133O' // 6cd3 #3472 + '23A' // 6cd4 #598 + '69A' // 6cd5 #1794 + '23A' // 6cd6 #598 + '58Q' // 6cd7 #1524 + 'A' // 6cd8 + 'a23A' // 6cd9-6cda #598 + '171J' // 6cdb #4455 + '46G' // 6cdc #1202 + '25W' // 6cdd #672 + '23B' // 6cde #599 + '58U' // 6cdf #1528 + '6Y' // 6ce0 #180 + '209X' // 6ce1 #5457 + '68E' // 6ce2 #1772 + '143G' // 6ce3 #3724 + '5D' // 6ce4 #133 + '189C' // 6ce5 #4916 + '5D' // 6ce6 #133 + '25W' // 6ce7 #672 + '231Q' // 6ce8 #6022 + '6Y' // 6ce9 #180 + '101X' // 6cea #2649 + '22Y' // 6ceb #596 + '6Y' // 6cec #180 + '49T' // 6ced #1293 + 'a22Y' // 6cee-6cef #596 + '218T' // 6cf0 #5687 + '6Y' // 6cf1 #180 + '49T' // 6cf2 #1293 + '189G' // 6cf3 #4920 + '5L' // 6cf4 #141 + '64C' // 6cf5 #1666 + 'a5D' // 6cf6-6cf7 #133 + '3W' // 6cf8 #100 + 'A' // 6cf9 + '5D' // 6cfa #133 + '249O' // 6cfb #6488 + '2R' // 6cfc #69 + '3N' // 6cfd #91 + '5D' // 6cfe #133 + '34J' // 6cff #893 + '25W' // 6d00 #672 + '101Y' // 6d01 #2650 + '25X' // 6d02 #673 + '5D' // 6d03 #133 + '6Y' // 6d04 #180 + '58U' // 6d05 #1528 + '25X' // 6d06 #673 + '6Y' // 6d07 #180 + '5D' // 6d08 #133 + '25X' // 6d09 #673 + '6Y' // 6d0a #180 + '216I' // 6d0b #5624 + '22Y' // 6d0c #596 + 'A' // 6d0d + 'a6Y' // 6d0e-6d0f #180 + '23B' // 6d10 #599 + '22Y' // 6d11 #596 + '102B' // 6d12 #2653 + '49T' // 6d13 #1293 + '5D' // 6d14 #133 + 'A' // 6d15 + '5D' // 6d16 #133 + '221N' // 6d17 #5759 + '23B' // 6d18 #599 + '22Y' // 6d19 #596 + '6Y' // 6d1a #180 + '194O' // 6d1b #5058 + '5D' // 6d1c #133 + 'A' // 6d1d + '195X' // 6d1e #5093 + '6Y' // 6d1f #180 + 'aA' // 6d20-6d21 + '23B' // 6d22 #599 + '5D' // 6d23 #133 + '25W' // 6d24 #672 + '191G' // 6d25 #4972 + '58S' // 6d26 #1526 + '22Y' // 6d27 #596 + '6Y' // 6d28 #180 + '172V' // 6d29 #4493 + '187W' // 6d2a #4884 + '6Y' // 6d2b #180 + 'A' // 6d2c + '25X' // 6d2d #673 + 'a6Y' // 6d2e-6d2f #180 + '23B' // 6d30 #599 + '101W' // 6d31 #2648 + '218U' // 6d32 #5688 + '6Y' // 6d33 #180 + '25W' // 6d34 #672 + 'a22Y' // 6d35-6d36 #596 + '25X' // 6d37 #673 + 'a22Y' // 6d38-6d39 #596 + '23B' // 6d3a #599 + '68U' // 6d3b #1788 + '6Y' // 6d3c #180 + '185W' // 6d3d #4832 + '67X' // 6d3e #1765 + '6Y' // 6d3f #180 + 'A' // 6d40 + '237I' // 6d41 #6170 + 'a5D' // 6d42-6d43 #133 + '255W' // 6d44 #6652 + '256Q' // 6d45 #6672 + '2Y' // 6d46 #76 + '3X' // 6d47 #101 + 'a5D' // 6d48-6d49 #133 + '3W' // 6d4a #100 + '2D' // 6d4b #55 + 'A' // 6d4c + '5D' // 6d4d #133 + '102G' // 6d4e #2658 + '2C' // 6d4f #54 + '5D' // 6d50 #133 + '3H' // 6d51 #85 + '5D' // 6d52 #133 + '1R' // 6d53 #43 + '5D' // 6d54 #133 + 'A' // 6d55 + '5D' // 6d56 #133 + 'a6Y' // 6d57-6d58 #180 + '164O' // 6d59 #4278 + '58P' // 6d5a #1523 + '58S' // 6d5b #1526 + '102E' // 6d5c #2656 + 'A' // 6d5d + '6Y' // 6d5e #180 + '25W' // 6d5f #672 + 'a6Y' // 6d60-6d61 #180 + '25X' // 6d62 #673 + '102A' // 6d63 #2652 + 'a6Y' // 6d64-6d65 #180 + '176N' // 6d66 #4589 + '6Y' // 6d67 #180 + 'A' // 6d68 + '182F' // 6d69 #4737 + '215A' // 6d6a #5590 + 'A' // 6d6b + '58P' // 6d6c #1523 + '23B' // 6d6d #599 + '190Y' // 6d6e #4964 + '6Y' // 6d6f #180 + '25W' // 6d70 #672 + '34J' // 6d71 #893 + '25X' // 6d72 #673 + 'A' // 6d73 + '205K' // 6d74 #5340 + '23B' // 6d75 #599 + 'A' // 6d76 + '238X' // 6d77 #6211 + '168A' // 6d78 #4368 + '101M' // 6d79 #2638 + 'A' // 6d7a + '5D' // 6d7b #133 + '20N' // 6d7c #533 + '5D' // 6d7d #133 + 'A' // 6d7e + '101R' // 6d7f #2643 + '7D' // 6d80 #185 + '46E' // 6d81 #1200 + '101L' // 6d82 #2637 + 'aA' // 6d83-6d84 + '101F' // 6d85 #2631 + 'A' // 6d86 + '34H' // 6d87 #891 + '235Z' // 6d88 #6135 + '200F' // 6d89 #5205 + '70F' // 6d8a #1825 + 'A' // 6d8b + '146B' // 6d8c #3797 + '70D' // 6d8d #1823 + '34H' // 6d8e #891 + '46F' // 6d8f #1201 + '5D' // 6d90 #133 + '34H' // 6d91 #891 + '20N' // 6d92 #533 + '34H' // 6d93 #891 + '20N' // 6d94 #533 + '34H' // 6d95 #891 + '101I' // 6d96 #2634 + 'a20N' // 6d97-6d98 #533 + '256F' // 6d99 #6661 + '101S' // 6d9a #2644 + '252C' // 6d9b #6554 + '70F' // 6d9c #1825 + 'c5D' // 6d9d-6da0 #133 + '3X' // 6da1 #101 + 'a5D' // 6da2-6da3 #133 + '101U' // 6da4 #2646 + '25V' // 6da5 #671 + '3Y' // 6da6 #102 + '5D' // 6da7 #133 + '1R' // 6da8 #43 + '3H' // 6da9 #85 + 'b20N' // 6daa-6dac #533 + 'A' // 6dad + '133N' // 6dae #3471 + '174K' // 6daf #4534 + 'A' // 6db0 + '25V' // 6db1 #671 + '67S' // 6db2 #1760 + '101T' // 6db3 #2645 + '20N' // 6db4 #533 + '178R' // 6db5 #4645 + '5D' // 6db6 #133 + '34I' // 6db7 #892 + '20N' // 6db8 #533 + '46E' // 6db9 #1200 + 'aA' // 6dba-6dbb + '203P' // 6dbc #5293 + '7D' // 6dbd #185 + '46F' // 6dbe #1201 + '20N' // 6dbf #533 + '101P' // 6dc0 #2641 + 'A' // 6dc1 + '20N' // 6dc2 #533 + '260W' // 6dc3 #6782 + '101G' // 6dc4 #2632 + '58M' // 6dc5 #1520 + '123A' // 6dc6 #3198 + '164L' // 6dc7 #4275 + '34I' // 6dc8 #892 + '58O' // 6dc9 #1522 + '34I' // 6dca #892 + '179Y' // 6dcb #4678 + '58N' // 6dcc #1521 + '46F' // 6dcd #1201 + '7D' // 6dce #185 + '58M' // 6dcf #1520 + '34I' // 6dd0 #892 + '180F' // 6dd1 #4685 + '58N' // 6dd2 #1521 + '58O' // 6dd3 #1522 + 'A' // 6dd4 + '34I' // 6dd5 #892 + '5I' // 6dd6 #138 + 'A' // 6dd7 + '185U' // 6dd8 #4830 + '10V' // 6dd9 #281 + '66W' // 6dda #1738 + '5I' // 6ddb #138 + '18S' // 6ddc #486 + '5I' // 6ddd #138 + '10V' // 6dde #281 + '18R' // 6ddf #485 + '5I' // 6de0 #138 + '202O' // 6de1 #5266 + '18R' // 6de2 #485 + '14G' // 6de3 #370 + '5I' // 6de4 #138 + '18R' // 6de5 #485 + '5I' // 6de6 #138 + 'A' // 6de7 + '207H' // 6de8 #5389 + '5I' // 6de9 #138 + '145Z' // 6dea #3795 + '168K' // 6deb #4378 + '5I' // 6dec #138 + 'A' // 6ded + '128M' // 6dee #3340 + 'a5I' // 6def-6df0 #138 + '229J' // 6df1 #5963 + '18R' // 6df2 #485 + '142S' // 6df3 #3710 + '18R' // 6df4 #485 + '153R' // 6df5 #3995 + '5I' // 6df6 #138 + '67S' // 6df7 #1760 + '249L' // 6df8 #6485 + '123B' // 6df9 #3199 + '66W' // 6dfa #1738 + '197X' // 6dfb #5145 + '5I' // 6dfc #138 + '14G' // 6dfd #370 + '25V' // 6dfe #671 + 'A' // 6dff + '18R' // 6e00 #485 + 'A' // 6e01 + '14G' // 6e02 #370 + '18S' // 6e03 #486 + '101H' // 6e04 #2633 + '235G' // 6e05 #6116 + 'A' // 6e06 + '252G' // 6e07 #6558 + '70Z' // 6e08 #1845 + '255H' // 6e09 #6637 + '101J' // 6e0a #2635 + '256Y' // 6e0b #6680 + '11K' // 6e0c #296 + '2W' // 6e0d #74 + '11K' // 6e0e #296 + '25V' // 6e0f #671 + '3N' // 6e10 #91 + '11K' // 6e11 #296 + 'A' // 6e12 + '253J' // 6e13 #6587 + '2Y' // 6e14 #76 + '101O' // 6e15 #2640 + '11K' // 6e16 #296 + '249M' // 6e17 #6486 + '25V' // 6e18 #671 + '10V' // 6e19 #281 + '101N' // 6e1a #2639 + '215V' // 6e1b #5611 + 'A' // 6e1c + '101K' // 6e1d #2636 + '36D' // 6e1e #939 + '10V' // 6e1f #281 + '139S' // 6e20 #3632 + '183V' // 6e21 #4779 + '5I' // 6e22 #138 + '157W' // 6e23 #4104 + '101E' // 6e24 #2630 + '10V' // 6e25 #281 + '135W' // 6e26 #3532 + '5I' // 6e27 #138 + 'A' // 6e28 + '137K' // 6e29 #3572 + '25V' // 6e2a #671 + '10V' // 6e2b #281 + '220J' // 6e2c #5729 + '101D' // 6e2d #2629 + '18R' // 6e2e #485 + '234O' // 6e2f #6098 + 'a14G' // 6e30-6e31 #370 + '146A' // 6e32 #3796 + 'A' // 6e33 + '157V' // 6e34 #4103 + 'A' // 6e35 + '10V' // 6e36 #281 + '11K' // 6e37 #296 + '200G' // 6e38 #5206 + '5I' // 6e39 #138 + '10V' // 6e3a #281 + '7D' // 6e3b #185 + '10V' // 6e3c #281 + '101Q' // 6e3d #2642 + '147X' // 6e3e #3845 + '11K' // 6e3f #296 + 'a18S' // 6e40-6e41 #486 + '7D' // 6e42 #185 + '139R' // 6e43 #3631 + '10V' // 6e44 #281 + '5I' // 6e45 #138 + 'A' // 6e46 + '18S' // 6e47 #486 + '7D' // 6e48 #185 + '5I' // 6e49 #138 + '160K' // 6e4a #4170 + '5I' // 6e4b #138 + '7D' // 6e4c #185 + '10V' // 6e4d #281 + '5I' // 6e4e #138 + '18R' // 6e4f #485 + '25V' // 6e50 #671 + '5I' // 6e51 #138 + '7D' // 6e52 #185 + 'a5I' // 6e53-6e54 #138 + '11K' // 6e55 #296 + '215D' // 6e56 #5593 + '46E' // 6e57 #1200 + '148X' // 6e58 #3871 + '14G' // 6e59 #370 + 'A' // 6e5a + '133M' // 6e5b #3470 + '10V' // 6e5c #281 + '36D' // 6e5d #939 + 'a10V' // 6e5e-6e5f #281 + '14G' // 6e60 #370 + '18S' // 6e61 #486 + '36D' // 6e62 #939 + '5I' // 6e63 #138 + '14G' // 6e64 #370 + 'a18S' // 6e65-6e66 #486 + '160X' // 6e67 #4183 + '7D' // 6e68 #185 + '14G' // 6e69 #370 + '11K' // 6e6a #296 + '10V' // 6e6b #281 + 'A' // 6e6c + '11K' // 6e6d #296 + '10V' // 6e6e #281 + '204X' // 6e6f #5327 + '11K' // 6e70 #296 + '14G' // 6e71 #370 + 'a10V' // 6e72-6e73 #281 + '18S' // 6e74 #486 + 'A' // 6e75 + '5I' // 6e76 #138 + '11K' // 6e77 #296 + '18S' // 6e78 #486 + 'A' // 6e79 + '260V' // 6e7a #6781 + '7D' // 6e7b #185 + '18S' // 6e7c #486 + '7D' // 6e7d #185 + '256R' // 6e7e #6673 + '255Q' // 6e7f #6646 + '70Z' // 6e80 #1845 + '11K' // 6e81 #296 + '36D' // 6e82 #939 + '2R' // 6e83 #69 + 'A' // 6e84 + '3G' // 6e85 #84 + '18S' // 6e86 #486 + '11K' // 6e87 #296 + '14G' // 6e88 #370 + '5I' // 6e89 #138 + 'A' // 6e8a + '14G' // 6e8b #370 + '7D' // 6e8c #185 + '5I' // 6e8d #138 + '14G' // 6e8e #370 + '5I' // 6e8f #138 + '228F' // 6e90 #5933 + 'aA' // 6e91-6e92 + '18R' // 6e93 #485 + 'aA' // 6e94-6e95 + '230A' // 6e96 #5980 + 'A' // 6e97 + '5I' // 6e98 #138 + '18R' // 6e99 #485 + '14G' // 6e9a #370 + 'A' // 6e9b + '167Z' // 6e9c #4367 + '188Q' // 6e9d #4904 + '11K' // 6e9e #296 + '29U' // 6e9f #774 + '7D' // 6ea0 #185 + '38Z' // 6ea1 #1013 + '161Q' // 6ea2 #4202 + 'A' // 6ea3 + '46D' // 6ea4 #1199 + '29U' // 6ea5 #774 + '38Z' // 6ea6 #1013 + '20M' // 6ea7 #532 + 'aA' // 6ea8-6ea9 + '185T' // 6eaa #4829 + '225G' // 6eab #5856 + 'A' // 6eac + '7D' // 6ead #185 + '58L' // 6eae #1519 + '133L' // 6eaf #3469 + 'A' // 6eb0 + '29U' // 6eb1 #774 + '20M' // 6eb2 #532 + '7D' // 6eb3 #185 + '20M' // 6eb4 #532 + '46D' // 6eb5 #1199 + '161S' // 6eb6 #4204 + '20M' // 6eb7 #532 + '29T' // 6eb8 #773 + 'A' // 6eb9 + '136I' // 6eba #3544 + '100R' // 6ebb #2617 + '139U' // 6ebc #3634 + '20M' // 6ebd #532 + 'A' // 6ebe + 'a36D' // 6ebf-6ec0 #939 + '20M' // 6ec1 #532 + '29U' // 6ec2 #774 + '20M' // 6ec3 #532 + '63W' // 6ec4 #1660 + '190M' // 6ec5 #4952 + '11K' // 6ec6 #296 + '100S' // 6ec7 #2618 + '20M' // 6ec8 #532 + '29U' // 6ec9 #774 + '7D' // 6eca #185 + '190X' // 6ecb #4963 + '63W' // 6ecc #1660 + '58L' // 6ecd #1519 + '100P' // 6ece #2615 + '20M' // 6ecf #532 + '38Z' // 6ed0 #1013 + '209W' // 6ed1 #5456 + 'A' // 6ed2 + 'a29U' // 6ed3-6ed4 #774 + '100T' // 6ed5 #2619 + '46D' // 6ed6 #1199 + '11K' // 6ed7 #296 + '38Z' // 6ed8 #1013 + '123C' // 6ed9 #3200 + '100U' // 6eda #2620 + '18P' // 6edb #483 + 'A' // 6edc + '100Y' // 6edd #2624 + '255P' // 6ede #6645 + 'a11J' // 6edf-6ee0 #295 + '2D' // 6ee1 #55 + '14F' // 6ee2 #369 + 'A' // 6ee3 + '2L' // 6ee4 #63 + '2K' // 6ee5 #62 + '70E' // 6ee6 #1824 + 'A' // 6ee7 + '101C' // 6ee8 #2628 + '101B' // 6ee9 #2627 + '11J' // 6eea #295 + '18Q' // 6eeb #484 + '139T' // 6eec #3633 + '7D' // 6eed #185 + '18P' // 6eee #483 + '145Y' // 6eef #3794 + 'aA' // 6ef0-6ef1 + '147S' // 6ef2 #3840 + '11J' // 6ef3 #295 + '188G' // 6ef4 #4894 + 'aA' // 6ef5-6ef6 + '151D' // 6ef7 #3929 + '46B' // 6ef8 #1197 + '18P' // 6ef9 #483 + '14F' // 6efa #369 + '18Q' // 6efb #484 + 'A' // 6efc + '7D' // 6efd #185 + '67H' // 6efe #1749 + '232L' // 6eff #6043 + '9T' // 6f00 #253 + '174X' // 6f01 #4547 + '202H' // 6f02 #5259 + '11J' // 6f03 #295 + '58J' // 6f04 #1517 + 'A' // 6f05 + '173S' // 6f06 #4516 + 'A' // 6f07 + 'a18P' // 6f08-6f09 #483 + '18Q' // 6f0a #484 + '29T' // 6f0b #773 + '58J' // 6f0c #1517 + '18Q' // 6f0d #484 + '9T' // 6f0e #253 + '189Y' // 6f0f #4938 + '7D' // 6f10 #185 + '70D' // 6f11 #1823 + '9T' // 6f12 #253 + '58K' // 6f13 #1518 + '230B' // 6f14 #5981 + '46B' // 6f15 #1197 + '100Q' // 6f16 #2616 + '29T' // 6f17 #773 + '7D' // 6f18 #185 + '14F' // 6f19 #369 + '18Q' // 6f1a #484 + '7D' // 6f1b #185 + 'cA' // 6f1c-6f1f + '160D' // 6f20 #4163 + 'A' // 6f21 + '210E' // 6f22 #5464 + '100V' // 6f23 #2621 + '46C' // 6f24 #1198 + '18P' // 6f25 #483 + '18Q' // 6f26 #484 + '9T' // 6f27 #253 + '11J' // 6f28 #295 + 'a18P' // 6f29-6f2a #483 + '228E' // 6f2b #5932 + '143B' // 6f2c #3719 + '18P' // 6f2d #483 + '9T' // 6f2e #253 + '18P' // 6f2f #483 + '18Q' // 6f30 #484 + '100N' // 6f31 #2613 + '185S' // 6f32 #4828 + '58K' // 6f33 #1518 + '9T' // 6f34 #253 + '18Q' // 6f35 #484 + '18P' // 6f36 #483 + '11J' // 6f37 #295 + '67H' // 6f38 #1749 + 'A' // 6f39 + '9T' // 6f3a #253 + 'a18Q' // 6f3b-6f3c #484 + '29T' // 6f3d #773 + '151E' // 6f3e #3930 + '66I' // 6f3f #1724 + '9T' // 6f40 #253 + '100O' // 6f41 #2614 + 'A' // 6f42 + 'a14F' // 6f43-6f44 #369 + '70E' // 6f45 #1824 + '11J' // 6f46 #295 + '3H' // 6f47 #85 + '11J' // 6f48 #295 + 'aA' // 6f49-6f4a + '11J' // 6f4b #295 + 'A' // 6f4c + '2K' // 6f4d #62 + '14F' // 6f4e #369 + '18P' // 6f4f #483 + 'A' // 6f50 + '66I' // 6f51 #1724 + '7D' // 6f52 #185 + '18Q' // 6f53 #484 + '209I' // 6f54 #5442 + '11J' // 6f55 #295 + '46C' // 6f56 #1198 + '46B' // 6f57 #1197 + '171G' // 6f58 #4452 + '41P' // 6f59 #1081 + '38Y' // 6f5a #1012 + '192Z' // 6f5b #5017 + '100Z' // 6f5c #2625 + '5K' // 6f5d #140 + '38Y' // 6f5e #1012 + '101A' // 6f5f #2626 + '25U' // 6f60 #670 + '15M' // 6f61 #402 + '171E' // 6f62 #4450 + '9T' // 6f63 #253 + '196O' // 6f64 #5110 + '11J' // 6f65 #295 + '15M' // 6f66 #402 + '14F' // 6f67 #369 + '5K' // 6f68 #140 + 'b14F' // 6f69-6f6b #369 + '15M' // 6f6c #402 + '171F' // 6f6d #4451 + '215G' // 6f6e #5596 + '15M' // 6f6f #402 + '167W' // 6f70 #4364 + '11J' // 6f71 #295 + '14F' // 6f72 #369 + '9T' // 6f73 #253 + '15M' // 6f74 #402 + '11J' // 6f75 #295 + '14F' // 6f76 #369 + '9T' // 6f77 #253 + '15M' // 6f78 #402 + '46C' // 6f79 #1198 + '38Y' // 6f7a #1012 + '14F' // 6f7b #369 + 'b38Y' // 6f7c-6f7e #1012 + '9T' // 6f7f #253 + '151C' // 6f80 #3928 + '100X' // 6f81 #2623 + '15M' // 6f82 #402 + '41P' // 6f83 #1081 + '167G' // 6f84 #4348 + 'A' // 6f85 + '63E' // 6f86 #1642 + '25U' // 6f87 #670 + '128L' // 6f88 #3339 + '14F' // 6f89 #369 + '29T' // 6f8a #773 + 'a15M' // 6f8b-6f8c #402 + '18O' // 6f8d #482 + '192Y' // 6f8e #5016 + 'A' // 6f8f + '18O' // 6f90 #482 + '5K' // 6f91 #140 + '15M' // 6f92 #402 + '5K' // 6f93 #140 + '18O' // 6f94 #482 + '9T' // 6f95 #253 + '25U' // 6f96 #670 + '18O' // 6f97 #482 + '41P' // 6f98 #1081 + 'A' // 6f99 + '5K' // 6f9a #140 + '11J' // 6f9b #295 + '2K' // 6f9c #62 + '58I' // 6f9d #1516 + 'A' // 6f9e + 'a25U' // 6f9f-6fa0 #670 + '171H' // 6fa1 #4453 + '9T' // 6fa2 #253 + '18O' // 6fa3 #482 + '197P' // 6fa4 #5137 + '15M' // 6fa5 #402 + '25U' // 6fa6 #670 + '18O' // 6fa7 #482 + '25U' // 6fa8 #670 + 'A' // 6fa9 + '100W' // 6faa #2622 + '9T' // 6fab #253 + 'aA' // 6fac-6fad + '100M' // 6fae #2612 + '18O' // 6faf #482 + '5K' // 6fb0 #140 + '64T' // 6fb1 #1683 + 'A' // 6fb2 + '217J' // 6fb3 #5651 + '14F' // 6fb4 #369 + '58I' // 6fb5 #1516 + '15M' // 6fb6 #402 + '5K' // 6fb7 #140 + 'A' // 6fb8 + '18O' // 6fb9 #482 + '9T' // 6fba #253 + '29T' // 6fbb #773 + '25U' // 6fbc #670 + 'A' // 6fbd + '100L' // 6fbe #2611 + 'A' // 6fbf + '213E' // 6fc0 #5542 + '125G' // 6fc1 #3256 + '18O' // 6fc2 #482 + '204R' // 6fc3 #5321 + '11J' // 6fc4 #295 + '41P' // 6fc5 #1081 + 'c15M' // 6fc6-6fc9 #402 + '18O' // 6fca #482 + '14F' // 6fcb #369 + 'aA' // 6fcc-6fcd + '9T' // 6fce #253 + 'aA' // 6fcf-6fd0 + 'a11J' // 6fd1-6fd2 #295 + '100F' // 6fd3 #2605 + '46A' // 6fd4 #1196 + '200E' // 6fd5 #5204 + 'aA' // 6fd6-6fd7 + '46A' // 6fd8 #1196 + '13C' // 6fd9 #340 + '99P' // 6fda #2589 + '63E' // 6fdb #1642 + 'aA' // 6fdc-6fdd + '99Q' // 6fde #2590 + '207G' // 6fdf #5388 + '58G' // 6fe0 #1514 + '99Z' // 6fe1 #2599 + '100K' // 6fe2 #2610 + 'A' // 6fe3 + '145W' // 6fe4 #3792 + 'bA' // 6fe5-6fe7 + '46A' // 6fe8 #1196 + '58G' // 6fe9 #1514 + 'A' // 6fea + '157T' // 6feb #4101 + '58F' // 6fec #1513 + 'A' // 6fed + '45Z' // 6fee #1195 + '131C' // 6fef #3408 + '45Z' // 6ff0 #1195 + '174C' // 6ff1 #4526 + 'A' // 6ff2 + '5K' // 6ff3 #140 + 'A' // 6ff4 + '5K' // 6ff5 #140 + '14D' // 6ff6 #367 + 'A' // 6ff7 + '13C' // 6ff8 #340 + '5K' // 6ff9 #140 + '45Z' // 6ffa #1195 + 'A' // 6ffb + '14D' // 6ffc #367 + '5K' // 6ffd #140 + '171D' // 6ffe #4449 + '9S' // 6fff #252 + '14D' // 7000 #367 + '58H' // 7001 #1515 + 'A' // 7002 + '9S' // 7003 #252 + 'A' // 7004 + 'a58F' // 7005-7006 #1513 + '14D' // 7007 #367 + 'A' // 7008 + '64T' // 7009 #1683 + '5K' // 700a #140 + '8D' // 700b #211 + 'A' // 700c + '7O' // 700d #196 + 'A' // 700e + '225F' // 700f #5855 + 'A' // 7010 + '139Q' // 7011 #3630 + 'bA' // 7012-7014 + '8D' // 7015 #211 + 'A' // 7016 + '5K' // 7017 #140 + '8D' // 7018 #211 + 'A' // 7019 + '139P' // 701a #3629 + '8D' // 701b #211 + '50E' // 701c #1304 + '63V' // 701d #1659 + '8D' // 701e #211 + '63D' // 701f #1641 + '14D' // 7020 #367 + '9S' // 7021 #252 + 'A' // 7022 + '8D' // 7023 #211 + 'aA' // 7024-7025 + '7O' // 7026 #196 + '99W' // 7027 #2596 + '133J' // 7028 #3467 + 'bA' // 7029-702b + '100B' // 702c #2601 + 'A' // 702d + '6T' // 702e #175 + '8D' // 702f #211 + '7O' // 7030 #196 + '9S' // 7031 #252 + '14D' // 7032 #367 + 'A' // 7033 + '14D' // 7034 #367 + '14E' // 7035 #368 + 'A' // 7036 + '8D' // 7037 #211 + '9S' // 7038 #252 + 'a7O' // 7039-703a #196 + '9S' // 703b #252 + '7O' // 703c #196 + '6T' // 703d #175 + '63V' // 703e #1659 + 'A' // 703f + '9S' // 7040 #252 + 'A' // 7041 + '9S' // 7042 #252 + '14D' // 7043 #367 + '7O' // 7044 #196 + 'A' // 7045 + '9S' // 7046 #252 + 'a5K' // 7047-7048 #140 + '7O' // 7049 #196 + '36C' // 704a #938 + '7O' // 704b #196 + '164K' // 704c #4274 + '13C' // 704d #340 + '5K' // 704e #140 + '6T' // 704f #175 + '100C' // 7050 #2602 + '164H' // 7051 #4271 + '9S' // 7052 #252 + 'A' // 7053 + 'a14D' // 7054-7055 #367 + 'aA' // 7056-7057 + '173J' // 7058 #4507 + 'A' // 7059 + '6T' // 705a #175 + 'A' // 705b + '14E' // 705c #368 + '8D' // 705d #211 + '7O' // 705e #196 + 'a9S' // 705f-7060 #252 + '14E' // 7061 #368 + 'A' // 7062 + '240S' // 7063 #6258 + '7O' // 7064 #196 + '14D' // 7065 #367 + '14E' // 7066 #368 + 'a9S' // 7067-7068 #252 + '14D' // 7069 #367 + 'A' // 706a + '68M' // 706b #1780 + '7O' // 706c #196 + '1R' // 706d #43 + '45Y' // 706e #1194 + '100A' // 706f #2600 + '196A' // 7070 #5096 + 'bA' // 7071-7073 + '9S' // 7074 #252 + '99U' // 7075 #2594 + '122Z' // 7076 #3197 + '13C' // 7077 #340 + '99X' // 7078 #2597 + '13C' // 7079 #340 + '9S' // 707a #252 + 'A' // 707b + '129X' // 707c #3377 + '191F' // 707d #4971 + '99T' // 707e #2593 + '100I' // 707f #2608 + '6T' // 7080 #175 + '7O' // 7081 #196 + 'bA' // 7082-7084 + '8D' // 7085 #211 + '7O' // 7086 #196 + 'aA' // 7087-7088 + '99Y' // 7089 #2598 + '149A' // 708a #3874 + '13C' // 708b #340 + 'aA' // 708c-708d + '190O' // 708e #4954 + '100E' // 708f #2604 + 'A' // 7090 + '14E' // 7091 #368 + '188F' // 7092 #4893 + 'A' // 7093 + 'a7O' // 7094-7095 #196 + '99S' // 7096 #2592 + '5K' // 7097 #140 + '8D' // 7098 #211 + '130C' // 7099 #3382 + '50E' // 709a #1304 + '5K' // 709b #140 + 'a6T' // 709c-709d #175 + 'A' // 709e + '7O' // 709f #196 + '13C' // 70a0 #340 + '100D' // 70a1 #2603 + 'A' // 70a2 + '13C' // 70a3 #340 + '8D' // 70a4 #211 + 'b13C' // 70a5-70a7 #340 + 'A' // 70a8 + '14E' // 70a9 #368 + '6T' // 70aa #175 + '164J' // 70ab #4273 + '99N' // 70ac #2587 + '168V' // 70ad #4389 + '171I' // 70ae #4454 + '8D' // 70af #211 + 'a7O' // 70b0-70b1 #196 + '6T' // 70b2 #175 + '133K' // 70b3 #3468 + '7O' // 70b4 #196 + '14E' // 70b5 #368 + '6T' // 70b6 #175 + '8D' // 70b7 #211 + '194R' // 70b8 #5061 + '247H' // 70b9 #6429 + '246J' // 70ba #6405 + '7O' // 70bb #196 + '100J' // 70bc #2609 + '100G' // 70bd #2606 + '9S' // 70be #252 + 'A' // 70bf + '14E' // 70c0 #368 + '3W' // 70c1 #100 + '1R' // 70c2 #43 + '6T' // 70c3 #175 + '13C' // 70c4 #340 + 'aA' // 70c5-70c6 + '6T' // 70c7 #175 + '202N' // 70c8 #5265 + 'A' // 70c9 + '99R' // 70ca #2591 + '8D' // 70cb #211 + '13C' // 70cc #340 + 'A' // 70cd + '6T' // 70ce #175 + '195K' // 70cf #5080 + '13C' // 70d0 #340 + '5K' // 70d1 #140 + '9S' // 70d2 #252 + '5K' // 70d3 #140 + '7O' // 70d4 #196 + '14D' // 70d5 #367 + '45Y' // 70d6 #1194 + 'A' // 70d7 + '178Q' // 70d8 #4644 + '8D' // 70d9 #211 + '14E' // 70da #368 + '3X' // 70db #101 + '7O' // 70dc #196 + '8D' // 70dd #211 + 'A' // 70de + '99O' // 70df #2588 + '6T' // 70e0 #175 + 'bA' // 70e1-70e3 + '193A' // 70e4 #5018 + 'A' // 70e5 + '1R' // 70e6 #43 + '3Y' // 70e7 #102 + '2W' // 70e8 #74 + '6T' // 70e9 #175 + 'A' // 70ea + '2R' // 70eb #69 + '36C' // 70ec #938 + '7K' // 70ed #192 + 'A' // 70ee + '100H' // 70ef #2607 + 'A' // 70f0 + '58H' // 70f1 #1515 + 'aA' // 70f2-70f3 + '9S' // 70f4 #252 + '13C' // 70f5 #340 + 'A' // 70f6 + '14E' // 70f7 #368 + 'A' // 70f8 + '166N' // 70f9 #4329 + '7O' // 70fa #196 + 'aA' // 70fb-70fc + '8D' // 70fd #211 + '13C' // 70fe #340 + '14E' // 70ff #368 + 'bA' // 7100-7102 + '5K' // 7103 #140 + '8D' // 7104 #211 + '45Y' // 7105 #1194 + '14D' // 7106 #367 + '5K' // 7107 #140 + '36C' // 7108 #938 + '99V' // 7109 #2595 + '64C' // 710a #1666 + '5K' // 710b #140 + '8D' // 710c #211 + 'aA' // 710d-710e + '5K' // 710f #140 + '14E' // 7110 #368 + 'aA' // 7111-7112 + '14E' // 7113 #368 + '36C' // 7114 #938 + '2K' // 7115 #62 + '6T' // 7116 #175 + '143S' // 7117 #3736 + '6T' // 7118 #175 + '164I' // 7119 #4272 + '136C' // 711a #3538 + 'A' // 711b + '22X' // 711c #595 + '6N' // 711d #169 + '34G' // 711e #890 + 'A' // 711f + '22X' // 7120 #595 + '245P' // 7121 #6385 + '12F' // 7122 #317 + 'bA' // 7123-7125 + '202X' // 7126 #5275 + 'aA' // 7127-7128 + '6N' // 7129 #169 + 'A' // 712a + '58D' // 712b #1511 + '6N' // 712c #169 + '5K' // 712d #140 + 'a22X' // 712e-712f #595 + '145X' // 7130 #3793 + '22X' // 7131 #595 + 'A' // 7132 + '6N' // 7133 #169 + '12F' // 7134 #317 + '6N' // 7135 #169 + '237H' // 7136 #6169 + 'A' // 7137 + '5K' // 7138 #140 + 'aA' // 7139-713a + '6N' // 713b #169 + '258K' // 713c #6718 + 'A' // 713d + '6N' // 713e #169 + 'A' // 713f + '6N' // 7140 #169 + '5K' // 7141 #140 + '6T' // 7142 #175 + '12F' // 7143 #317 + '6T' // 7144 #175 + 'a22X' // 7145-7146 #595 + '34G' // 7147 #890 + 'A' // 7148 + '157S' // 7149 #4100 + '34G' // 714a #890 + '22X' // 714b #595 + '159R' // 714c #4151 + 'A' // 714d + '166O' // 714e #4330 + '6N' // 714f #169 + '34G' // 7150 #890 + '38X' // 7151 #1011 + '22X' // 7152 #595 + '38X' // 7153 #1011 + 'A' // 7154 + '36C' // 7155 #938 + '34G' // 7156 #890 + '58D' // 7157 #1511 + 'A' // 7158 + '197I' // 7159 #5130 + '22X' // 715a #595 + 'A' // 715b + '63D' // 715c #1641 + 'A' // 715d + '157U' // 715e #4102 + 'A' // 715f + '38X' // 7160 #1011 + '6T' // 7161 #175 + '38X' // 7162 #1011 + 'A' // 7163 + '151B' // 7164 #3927 + '145V' // 7165 #3791 + '58B' // 7166 #1509 + '234U' // 7167 #6104 + '38W' // 7168 #1010 + '194T' // 7169 #5063 + 'A' // 716a + '6N' // 716b #169 + '58C' // 716c #1510 + '6T' // 716d #175 + '189F' // 716e #4919 + 'aA' // 716f-7170 + '12F' // 7171 #317 + '149U' // 7172 #3894 + '29S' // 7173 #772 + '58E' // 7174 #1512 + '6N' // 7175 #169 + '58E' // 7176 #1512 + '6N' // 7177 #169 + '29S' // 7178 #772 + '2Z' // 7179 #77 + '29S' // 717a #772 + '12F' // 717b #317 + '6N' // 717c #169 + '99M' // 717d #2586 + '6N' // 717e #169 + '6T' // 717f #175 + '38W' // 7180 #1010 + '12F' // 7181 #317 + 'aA' // 7182-7183 + '139O' // 7184 #3628 + '16Q' // 7185 #432 + '6T' // 7186 #175 + 'a38W' // 7187-7188 #1010 + '50E' // 7189 #1304 + '205J' // 718a #5339 + 'A' // 718b + '34E' // 718c #888 + 'A' // 718d + '6N' // 718e #169 + '99J' // 718f #2583 + '12F' // 7190 #317 + '6N' // 7191 #169 + '58B' // 7192 #1509 + 'A' // 7193 + '122X' // 7194 #3195 + '2Z' // 7195 #77 + '16Q' // 7196 #432 + '12F' // 7197 #317 + '29S' // 7198 #772 + '157R' // 7199 #4099 + 'a16Q' // 719a-719b #432 + '29S' // 719c #772 + 'aA' // 719d-719e + '210W' // 719f #5482 + '38W' // 71a0 #1010 + 'A' // 71a1 + '99I' // 71a2 #2582 + '6N' // 71a3 #169 + '29S' // 71a4 #772 + '6T' // 71a5 #175 + 'aA' // 71a6-71a7 + '7N' // 71a8 #195 + 'bA' // 71a9-71ab + '157Q' // 71ac #4098 + '6N' // 71ad #169 + '2Z' // 71ae #77 + '7N' // 71af #195 + '2Z' // 71b0 #77 + '241O' // 71b1 #6280 + 'a7N' // 71b2-71b3 #195 + '45X' // 71b4 #1193 + '18N' // 71b5 #481 + '11I' // 71b6 #294 + 'a18N' // 71b7-71b8 #481 + 'a18M' // 71b9-71ba #480 + 'a11I' // 71bb-71bc #294 + 'A' // 71bd + '18M' // 71be #480 + 'a2Z' // 71bf-71c0 #77 + '18M' // 71c1 #480 + '11I' // 71c2 #294 + '183A' // 71c3 #4758 + '7N' // 71c4 #195 + 'bA' // 71c5-71c7 + '208L' // 71c8 #5419 + '65E' // 71c9 #1694 + '18N' // 71ca #481 + '7N' // 71cb #195 + '2Z' // 71cc #77 + 'A' // 71cd + '18M' // 71ce #480 + '18N' // 71cf #481 + '18M' // 71d0 #480 + '6N' // 71d1 #169 + '67Z' // 71d2 #1767 + '2Z' // 71d3 #77 + '18M' // 71d4 #480 + '180J' // 71d5 #4689 + 'a2Z' // 71d6-71d7 #77 + '12F' // 71d8 #317 + '164G' // 71d9 #4270 + '7N' // 71da #195 + 'A' // 71db + '7N' // 71dc #195 + '6N' // 71dd #169 + 'A' // 71de + '232K' // 71df #6042 + '7N' // 71e0 #195 + '12F' // 71e1 #317 + 'bA' // 71e2-71e4 + '168Q' // 71e5 #4384 + '164F' // 71e6 #4269 + '18M' // 71e7 #480 + '11I' // 71e8 #294 + 'A' // 71e9 + '11I' // 71ea #294 + '6N' // 71eb #169 + '16Q' // 71ec #432 + '65E' // 71ed #1694 + '18M' // 71ee #480 + 'dA' // 71ef-71f3 + '7N' // 71f4 #195 + '16Q' // 71f5 #432 + '18N' // 71f6 #481 + 'A' // 71f7 + '2Z' // 71f8 #77 + '7N' // 71f9 #195 + 'A' // 71fa + '124Q' // 71fb #3240 + '18M' // 71fc #480 + 'A' // 71fd + '58C' // 71fe #1510 + 'a18M' // 71ff-7200 #480 + '12F' // 7201 #317 + 'A' // 7202 + '12F' // 7203 #317 + 'aA' // 7204-7205 + '220Q' // 7206 #5736 + '7N' // 7207 #195 + '2Z' // 7208 #77 + '16Q' // 7209 #432 + 'aA' // 720a-720b + '18N' // 720c #481 + '122Y' // 720d #3196 + 'a6N' // 720e-720f #169 + '192W' // 7210 #5014 + 'aA' // 7211-7212 + '16Q' // 7213 #432 + '12F' // 7214 #317 + '16Q' // 7215 #432 + '45X' // 7216 #1193 + '16Q' // 7217 #432 + 'aA' // 7218-7219 + '7N' // 721a #195 + '194W' // 721b #5066 + '11I' // 721c #294 + '7N' // 721d #195 + '11I' // 721e #294 + '2Z' // 721f #77 + 'aA' // 7220-7221 + '12F' // 7222 #317 + '18N' // 7223 #481 + '34E' // 7224 #888 + '6N' // 7225 #169 + 'aA' // 7226-7227 + '7N' // 7228 #195 + 'A' // 7229 + '154N' // 722a #4017 + '7N' // 722b #195 + '180Q' // 722c #4696 + '67Z' // 722d #1767 + '45X' // 722e #1193 + '2Z' // 722f #77 + '34F' // 7230 #889 + '3Q' // 7231 #94 + '249J' // 7232 #6483 + 'A' // 7233 + '2Z' // 7234 #77 + '173C' // 7235 #4500 + '212A' // 7236 #5512 + '1R' // 7237 #43 + '192X' // 7238 #5015 + '145U' // 7239 #3790 + '188B' // 723a #4889 + '34F' // 723b #889 + '21J' // 723c #555 + '202M' // 723d #5264 + '217H' // 723e #5649 + '7N' // 723f #195 + '34F' // 7240 #889 + 'a7N' // 7241-7242 #195 + '2Z' // 7243 #77 + 'A' // 7244 + '2Z' // 7245 #77 + '200D' // 7246 #5203 + '241H' // 7247 #6273 + '242E' // 7248 #6296 + 'aA' // 7249-724a + '7N' // 724b #195 + '226F' // 724c #5881 + '11I' // 724d #294 + '21J' // 724e #555 + '2Z' // 724f #77 + '34E' // 7250 #888 + 'A' // 7251 + '34F' // 7252 #889 + '16Q' // 7253 #432 + 'A' // 7254 + '34E' // 7255 #888 + '7N' // 7256 #195 + '34E' // 7257 #888 + '34F' // 7258 #889 + '208U' // 7259 #5428 + '21J' // 725a #555 + '219Q' // 725b #5710 + '99K' // 725c #2584 + '99L' // 725d #2585 + '2Z' // 725e #77 + '130G' // 725f #3386 + '151A' // 7260 #3926 + '148M' // 7261 #3860 + '159G' // 7262 #4140 + '16Q' // 7263 #432 + 'a11I' // 7264-7265 #294 + '18N' // 7266 #481 + '175X' // 7267 #4573 + '2Z' // 7268 #77 + '35V' // 7269 #931 + '18N' // 726a #481 + '2Z' // 726b #77 + '11I' // 726c #294 + 'A' // 726d + 'a7N' // 726e-726f #195 + '12F' // 7270 #317 + '21J' // 7271 #555 + '153L' // 7272 #3989 + '18N' // 7273 #481 + '7N' // 7274 #195 + '4C' // 7275 #106 + 'A' // 7276 + '58A' // 7277 #1508 + '21J' // 7278 #555 + '35V' // 7279 #931 + '3X' // 727a #101 + '57Z' // 727b #1507 + '2Z' // 727c #77 + '180L' // 727d #4691 + '57Z' // 727e #1507 + '58A' // 727f #1508 + '145T' // 7280 #3789 + '45V' // 7281 #1191 + '10U' // 7282 #280 + 'A' // 7283 + '10U' // 7284 #280 + 'aA' // 7285-7286 + '10U' // 7287 #280 + 'A' // 7288 + '2Z' // 7289 #77 + '11I' // 728a #294 + 'aA' // 728b-728c + '10U' // 728d #280 + '2Z' // 728e #77 + '99H' // 728f #2581 + 'aA' // 7290-7291 + '10U' // 7292 #280 + '2Z' // 7293 #77 + '38U' // 7294 #1008 + 'A' // 7295 + '25T' // 7296 #669 + 'A' // 7297 + '20L' // 7298 #531 + 'aA' // 7299-729a + '10U' // 729b #280 + 'bA' // 729c-729e + '34D' // 729f #887 + '253D' // 72a0 #6581 + '20L' // 72a1 #531 + '57X' // 72a2 #1505 + 'cA' // 72a3-72a6 + '145S' // 72a7 #3788 + '2Z' // 72a8 #77 + 'bA' // 72a9-72ab + '183P' // 72ac #4773 + 'a10U' // 72ad-72ae #280 + '204W' // 72af #5326 + '10U' // 72b0 #280 + '21J' // 72b1 #555 + '10U' // 72b2 #280 + '11I' // 72b3 #294 + '10U' // 72b4 #280 + '34D' // 72b5 #887 + '259V' // 72b6 #6755 + 'a11I' // 72b7-72b8 #294 + '249K' // 72b9 #6484 + 'b11I' // 72ba-72bc #294 + '34D' // 72bd #887 + '21J' // 72be #555 + 'A' // 72bf + '225E' // 72c0 #5854 + '10U' // 72c1 #280 + '215E' // 72c2 #5594 + '10U' // 72c3 #280 + '164E' // 72c4 #4268 + '34D' // 72c5 #887 + '10U' // 72c6 #280 + '2Z' // 72c7 #77 + '11I' // 72c8 #294 + '2Z' // 72c9 #77 + 'aA' // 72ca-72cb + '10U' // 72cc #280 + '34D' // 72cd #887 + '45V' // 72ce #1191 + 'A' // 72cf + '166T' // 72d0 #4335 + 'A' // 72d1 + '10U' // 72d2 #280 + 'A' // 72d3 + '20L' // 72d4 #531 + '2Z' // 72d5 #77 + '21J' // 72d6 #555 + '208N' // 72d7 #5421 + '2Z' // 72d8 #77 + '131B' // 72d9 #3407 + 'A' // 72da + '252J' // 72db #6561 + 'A' // 72dc + 'a11I' // 72dd-72de #294 + '10U' // 72df #280 + '66H' // 72e0 #1723 + '45V' // 72e1 #1191 + '98Z' // 72e2 #2573 + 'aA' // 72e3-72e4 + '21J' // 72e5 #555 + '7G' // 72e6 #188 + 'A' // 72e7 + '14C' // 72e8 #366 + '130X' // 72e9 #3403 + 'aA' // 72ea-72eb + '258U' // 72ec #6728 + '255Z' // 72ed #6655 + '2L' // 72ee #63 + 'a7G' // 72ef-72f0 #188 + '2L' // 72f1 #63 + '7G' // 72f2 #188 + 'a12E' // 72f3-72f4 #316 + 'A' // 72f5 + '7G' // 72f6 #188 + '12E' // 72f7 #316 + '148B' // 72f8 #3849 + '139N' // 72f9 #3627 + 'a12E' // 72fa-72fb #316 + '181F' // 72fc #4711 + '20K' // 72fd #530 + '2Z' // 72fe #77 + 'A' // 72ff + 'a14C' // 7300-7301 #366 + '57Y' // 7302 #1506 + '7G' // 7303 #188 + '12E' // 7304 #316 + '2Z' // 7305 #77 + 'A' // 7306 + '12E' // 7307 #316 + 'aA' // 7308-7309 + '20K' // 730a #530 + '12E' // 730b #316 + '7G' // 730c #188 + '2Z' // 730d #77 + '1R' // 730e #43 + 'A' // 730f + '38U' // 7310 #1008 + 'A' // 7311 + '2Z' // 7312 #77 + '12E' // 7313 #316 + 'a7G' // 7314-7315 #188 + '20K' // 7316 #530 + '12E' // 7317 #316 + '2Z' // 7318 #77 + '25T' // 7319 #669 + 'A' // 731a + '189B' // 731b #4915 + '178P' // 731c #4643 + '128K' // 731d #3338 + '12E' // 731e #316 + '252Q' // 731f #6568 + 'a7G' // 7320-7321 #188 + '12E' // 7322 #316 + 'A' // 7323 + '2Z' // 7324 #77 + '130O' // 7325 #3394 + 'A' // 7326 + '36B' // 7327 #937 + '25T' // 7328 #669 + '20K' // 7329 #530 + '99B' // 732a #2575 + '99D' // 732b #2577 + '12E' // 732c #316 + '14C' // 732d #366 + '99C' // 732e #2576 + '2Z' // 732f #77 + '14C' // 7330 #366 + '12E' // 7331 #316 + '2Z' // 7332 #77 + '36B' // 7333 #937 + '66H' // 7334 #1723 + '36B' // 7335 #937 + '178O' // 7336 #4642 + '20K' // 7337 #530 + '38U' // 7338 #1008 + '57Y' // 7339 #1506 + 'a25T' // 733a-733b #669 + '20L' // 733c #531 + '36B' // 733d #937 + '20K' // 733e #530 + '136R' // 733f #3553 + '14C' // 7340 #366 + '20L' // 7341 #531 + '7G' // 7342 #188 + '25T' // 7343 #669 + '182V' // 7344 #4753 + '187U' // 7345 #4882 + 'aA' // 7346-7347 + '38U' // 7348 #1008 + 'A' // 7349 + '7G' // 734a #188 + 'A' // 734b + '20L' // 734c #531 + '12E' // 734d #316 + '217I' // 734e #5650 + '12E' // 734f #316 + '20K' // 7350 #530 + 'A' // 7351 + '20K' // 7352 #530 + 'aA' // 7353-7354 + '7G' // 7355 #188 + '2Z' // 7356 #77 + '20K' // 7357 #530 + '2Z' // 7358 #77 + '14C' // 7359 #366 + '20L' // 735a #531 + 'aA' // 735b-735c + 'a2Z' // 735d-735e #77 + '36B' // 735f #937 + '12E' // 7360 #316 + '20L' // 7361 #531 + '14C' // 7362 #366 + '255O' // 7363 #6644 + 'A' // 7364 + '14C' // 7365 #366 + 'a2Z' // 7366-7367 #77 + '225D' // 7368 #5853 + '25T' // 7369 #669 + '57X' // 736a #1505 + '25T' // 736b #669 + '20J' // 736c #529 + '7G' // 736d #188 + '34C' // 736e #886 + '20J' // 736f #529 + '25S' // 7370 #668 + '98W' // 7371 #2570 + '227R' // 7372 #5919 + '20L' // 7373 #531 + '14C' // 7374 #366 + '66V' // 7375 #1737 + '7G' // 7376 #188 + '34C' // 7377 #886 + '66V' // 7378 #1737 + '9I' // 7379 #242 + '25S' // 737a #668 + '192U' // 737b #5012 + '34C' // 737c #886 + '7G' // 737d #188 + '14C' // 737e #366 + 'A' // 737f + '34C' // 7380 #886 + '9I' // 7381 #242 + '7G' // 7382 #188 + '70C' // 7383 #1822 + '182Z' // 7384 #4757 + '34C' // 7385 #886 + '25S' // 7386 #668 + '222G' // 7387 #5778 + '7G' // 7388 #188 + '213N' // 7389 #5551 + '20J' // 738a #529 + '235J' // 738b #6119 + '99F' // 738c #2579 + '7G' // 738d #188 + '25S' // 738e #668 + '14C' // 738f #366 + '70C' // 7390 #1822 + '7G' // 7391 #188 + '14C' // 7392 #366 + '20J' // 7393 #529 + '98X' // 7394 #2571 + '20J' // 7395 #529 + '122W' // 7396 #3194 + 'a25S' // 7397-7398 #668 + 'a7G' // 7399-739a #188 + '1R' // 739b #43 + '45U' // 739c #1190 + 'A' // 739d + '45U' // 739e #1190 + '128H' // 739f #3335 + '20J' // 73a0 #529 + '14C' // 73a1 #366 + '20J' // 73a2 #529 + '7G' // 73a3 #188 + 'A' // 73a4 + 'a20J' // 73a5-73a6 #529 + '99E' // 73a7 #2578 + '20J' // 73a8 #529 + '233M' // 73a9 #6070 + '45U' // 73aa #1190 + '66T' // 73ab #1735 + 'A' // 73ac + '25S' // 73ad #668 + '3X' // 73ae #101 + '1Z' // 73af #51 + '7K' // 73b0 #192 + '7G' // 73b1 #188 + '189A' // 73b2 #4914 + '25S' // 73b3 #668 + '45W' // 73b4 #1192 + '38V' // 73b5 #1009 + '99G' // 73b6 #2580 + '57W' // 73b7 #1504 + '45W' // 73b8 #1192 + '57V' // 73b9 #1503 + '98Y' // 73ba #2572 + '192V' // 73bb #5013 + '38V' // 73bc #1009 + '9I' // 73bd #242 + 'A' // 73be + '38V' // 73bf #1009 + '135M' // 73c0 #3522 + 'A' // 73c1 + '99A' // 73c2 #2574 + 'A' // 73c3 + '45W' // 73c4 #1192 + 'a57W' // 73c5-73c6 #1504 + '7G' // 73c7 #188 + '148R' // 73c8 #3865 + '57V' // 73c9 #1503 + '171B' // 73ca #4447 + '38V' // 73cb #1009 + '22W' // 73cc #594 + '203R' // 73cd #5295 + '45T' // 73ce #1189 + '22W' // 73cf #594 + '38T' // 73d0 #1007 + '3G' // 73d1 #84 + '45T' // 73d2 #1189 + '9I' // 73d3 #242 + 'A' // 73d4 + '12B' // 73d5 #313 + '22W' // 73d6 #594 + '12D' // 73d7 #315 + 'A' // 73d8 + '22W' // 73d9 #594 + 'A' // 73da + 'a38T' // 73db-73dc #1007 + 'a22W' // 73dd-73de #594 + 'A' // 73df + '209K' // 73e0 #5444 + '25R' // 73e1 #667 + '12B' // 73e2 #313 + '22W' // 73e3 #594 + '98J' // 73e4 #2557 + 'a22W' // 73e5-73e6 #594 + '45T' // 73e7 #1189 + '38T' // 73e8 #1007 + 'a22W' // 73e9-73ea #594 + '12D' // 73eb #315 + 'A' // 73ec + '218I' // 73ed #5676 + '133I' // 73ee #3466 + '38T' // 73ef #1007 + '9N' // 73f0 #247 + '9I' // 73f1 #242 + '9N' // 73f2 #247 + '12B' // 73f3 #313 + '31K' // 73f4 #816 + '7M' // 73f5 #194 + '12D' // 73f6 #315 + '12C' // 73f7 #314 + '9I' // 73f8 #242 + '12C' // 73f9 #314 + '7M' // 73fa #194 + '25R' // 73fb #667 + '18L' // 73fc #479 + '12C' // 73fd #314 + '244F' // 73fe #6349 + '31K' // 73ff #816 + '7M' // 7400 #194 + '12C' // 7401 #314 + '57T' // 7402 #1501 + '235B' // 7403 #6111 + '7M' // 7404 #194 + '98M' // 7405 #2560 + '35U' // 7406 #930 + '12C' // 7407 #314 + '18L' // 7408 #479 + '160J' // 7409 #4169 + '7M' // 740a #194 + 'b18L' // 740b-740d #479 + 'a9N' // 740e-740f #247 + '3X' // 7410 #101 + '25R' // 7411 #667 + '12B' // 7412 #313 + '70B' // 7413 #1821 + 'a12B' // 7414-7415 #313 + '12D' // 7416 #315 + '12B' // 7417 #313 + '9N' // 7418 #247 + '12B' // 7419 #313 + '7M' // 741a #194 + '12C' // 741b #314 + '57T' // 741c #1501 + '12D' // 741d #315 + '18L' // 741e #479 + '12B' // 741f #313 + '98U' // 7420 #2568 + '98N' // 7421 #2561 + '98P' // 7422 #2563 + '12D' // 7423 #315 + '7M' // 7424 #194 + '57R' // 7425 #1499 + '139M' // 7426 #3626 + 'A' // 7427 + '12C' // 7428 #314 + '7M' // 7429 #194 + '171C' // 742a #4448 + '70B' // 742b #1821 + '12C' // 742c #314 + '7M' // 742d #194 + 'b12C' // 742e-7430 #314 + '29R' // 7431 #771 + '98Q' // 7432 #2564 + '178N' // 7433 #4641 + '181U' // 7434 #4726 + 'a57R' // 7435-7436 #1499 + '12B' // 7437 #313 + '57S' // 7438 #1500 + '7M' // 7439 #194 + '12C' // 743a #314 + '9N' // 743b #247 + '98V' // 743c #2569 + 'A' // 743d + '9N' // 743e #247 + 'b12C' // 743f-7441 #314 + '12D' // 7442 #315 + '98K' // 7443 #2558 + '133G' // 7444 #3464 + '18L' // 7445 #479 + '7M' // 7446 #194 + '25R' // 7447 #667 + '18L' // 7448 #479 + '12D' // 7449 #315 + '18L' // 744a #479 + '164C' // 744b #4266 + '12B' // 744c #313 + '9I' // 744d #242 + 'bA' // 744e-7450 + '29R' // 7451 #771 + '7M' // 7452 #194 + '25R' // 7453 #667 + '12D' // 7454 #315 + '164D' // 7455 #4267 + '12B' // 7456 #313 + '12C' // 7457 #314 + 'A' // 7458 + '98L' // 7459 #2559 + '133H' // 745a #3465 + '125D' // 745b #3253 + '178M' // 745c #4640 + '7M' // 745d #194 + '209D' // 745e #5437 + '157P' // 745f #4097 + '98R' // 7460 #2565 + '12D' // 7461 #315 + '12C' // 7462 #314 + '128I' // 7463 #3336 + '145R' // 7464 #3787 + '57S' // 7465 #1500 + '9I' // 7466 #242 + '7M' // 7467 #194 + '12C' // 7468 #314 + '157O' // 7469 #4096 + '192T' // 746a #5011 + '25R' // 746b #667 + '12D' // 746c #315 + '7M' // 746d #194 + '29R' // 746e #771 + '122V' // 746f #3193 + '66T' // 7470 #1735 + '7M' // 7471 #194 + '29R' // 7472 #771 + '7M' // 7473 #194 + '18L' // 7474 #479 + '12D' // 7475 #315 + '57Q' // 7476 #1498 + '9N' // 7477 #247 + 'A' // 7478 + '12D' // 7479 #315 + '18L' // 747a #479 + 'A' // 747b + 'a12D' // 747c-747d #315 + '12C' // 747e #314 + '12D' // 747f #315 + '128J' // 7480 #3337 + '7M' // 7481 #194 + '98T' // 7482 #2567 + '195E' // 7483 #5074 + '9N' // 7484 #247 + '29R' // 7485 #771 + '7M' // 7486 #194 + '133F' // 7487 #3463 + '7M' // 7488 #194 + '57P' // 7489 #1497 + '18L' // 748a #479 + '122U' // 748b #3192 + 'a12B' // 748c-748d #313 + '9N' // 748e #247 + '31K' // 748f #816 + '57Q' // 7490 #1498 + '9I' // 7491 #242 + '29R' // 7492 #771 + '9N' // 7493 #247 + 'bA' // 7494-7496 + '9I' // 7497 #242 + '57P' // 7498 #1497 + '25R' // 7499 #667 + '7M' // 749a #194 + '12B' // 749b #313 + '34A' // 749c #884 + '57U' // 749d #1502 + 'a34A' // 749e-749f #884 + '34B' // 74a0 #885 + '45S' // 74a1 #1188 + '9I' // 74a2 #242 + '34A' // 74a3 #884 + '12B' // 74a4 #313 + '45S' // 74a5 #1188 + '34B' // 74a6 #885 + '98S' // 74a7 #2566 + '128G' // 74a8 #3334 + '34B' // 74a9 #885 + '34A' // 74aa #884 + '98O' // 74ab #2562 + 'aA' // 74ac-74ad + '31K' // 74ae #816 + '9I' // 74af #242 + '229S' // 74b0 #5972 + '34B' // 74b1 #885 + '34A' // 74b2 #884 + 'A' // 74b3 + '12B' // 74b4 #313 + '45S' // 74b5 #1188 + '9N' // 74b6 #247 + 'A' // 74b7 + '57U' // 74b8 #1502 + '98I' // 74b9 #2556 + '34B' // 74ba #885 + '9I' // 74bb #242 + 'A' // 74bc + '64S' // 74bd #1682 + 'A' // 74be + '29Q' // 74bf #770 + 'A' // 74c0 + '9N' // 74c1 #247 + 'A' // 74c2 + '9N' // 74c3 #247 + 'A' // 74c4 + '98H' // 74c5 #2555 + '98E' // 74c6 #2552 + 'A' // 74c7 + '22U' // 74c8 #592 + '9I' // 74c9 #242 + '150Y' // 74ca #3924 + 'A' // 74cb + '22U' // 74cc #592 + 'aA' // 74cd-74ce + '64S' // 74cf #1682 + '33Y' // 74d0 #882 + 'a9N' // 74d1-74d2 #247 + '33Y' // 74d3 #882 + '29Q' // 74d4 #770 + '9N' // 74d5 #247 + '45Q' // 74d6 #1186 + 'A' // 74d7 + '29Q' // 74d8 #770 + '9N' // 74d9 #247 + '29Q' // 74da #770 + '31K' // 74db #816 + '194S' // 74dc #5062 + 'A' // 74dd + 'a22U' // 74de-74df #592 + '29Q' // 74e0 #770 + 'A' // 74e1 + '97T' // 74e2 #2541 + '150Z' // 74e3 #3925 + '22U' // 74e4 #592 + '9N' // 74e5 #247 + '188A' // 74e6 #4888 + '57O' // 74e7 #1496 + 'a22U' // 74e8-74e9 #592 + 'a9I' // 74ea-74eb #242 + 'aA' // 74ec-74ed + '29Q' // 74ee #770 + '31K' // 74ef #816 + 'b33Y' // 74f0-74f2 #882 + 'A' // 74f3 + '22U' // 74f4 #592 + 'A' // 74f5 + '196J' // 74f6 #5105 + '164B' // 74f7 #4265 + '33Y' // 74f8 #882 + 'A' // 74f9 + '9I' // 74fa #242 + '45Q' // 74fb #1186 + '9I' // 74fc #242 + 'aA' // 74fd-74fe + '22U' // 74ff #592 + '22V' // 7500 #593 + '249I' // 7501 #6482 + 'A' // 7502 + '22U' // 7503 #592 + '157N' // 7504 #4095 + '57O' // 7505 #1496 + '9I' // 7506 #242 + '22V' // 7507 #593 + '9N' // 7508 #247 + 'bA' // 7509-750b + '45Q' // 750c #1186 + '97S' // 750d #2540 + '33Y' // 750e #882 + '13N' // 750f #351 + 'A' // 7510 + '9F' // 7511 #239 + '3P' // 7512 #93 + '5A' // 7513 #130 + 'A' // 7514 + '9F' // 7515 #239 + '25Q' // 7516 #666 + '5A' // 7517 #130 + '198M' // 7518 #5160 + '33Z' // 7519 #883 + '208P' // 751a #5423 + '260U' // 751b #6780 + '214N' // 751c #5577 + 'A' // 751d + '25Q' // 751e #666 + '69L' // 751f #1805 + '3P' // 7520 #93 + '5A' // 7521 #130 + '239T' // 7522 #6233 + '259S' // 7523 #6752 + '11C' // 7524 #288 + '97U' // 7525 #2542 + '128D' // 7526 #3331 + '11C' // 7527 #288 + '69K' // 7528 #1804 + '164A' // 7529 #4264 + '5A' // 752a #130 + '139K' // 752b #3624 + '9F' // 752c #239 + '33Z' // 752d #883 + '22V' // 752e #593 + '5A' // 752f #130 + '216S' // 7530 #5634 + '236S' // 7531 #6154 + '211W' // 7532 #5508 + '213Y' // 7533 #5562 + '33Z' // 7534 #883 + '49C' // 7535 #1276 + '11C' // 7536 #288 + '237P' // 7537 #6177 + '150U' // 7538 #3920 + '3P' // 7539 #93 + '162H' // 753a #4219 + '149P' // 753b #3889 + 'a3P' // 753c-753d #93 + '5A' // 753e #130 + '3P' // 753f #93 + '133D' // 7540 #3461 + 'A' // 7541 + '22V' // 7542 #593 + '3P' // 7543 #93 + '11C' // 7544 #288 + '3Y' // 7545 #102 + '45P' // 7546 #1185 + '9F' // 7547 #239 + '5A' // 7548 #130 + '11C' // 7549 #288 + 'a5A' // 754a-754b #130 + '237G' // 754c #6168 + '97R' // 754d #2539 + '5A' // 754e #130 + '65D' // 754f #1693 + '11C' // 7550 #288 + '98D' // 7551 #2551 + '11C' // 7552 #288 + '98F' // 7553 #2553 + '141S' // 7554 #3684 + '57M' // 7555 #1494 + '13N' // 7556 #351 + '11C' // 7557 #288 + 'A' // 7558 + '234E' // 7559 #6088 + '5A' // 755a #130 + '9F' // 755b #239 + '148Z' // 755c #3873 + '9F' // 755d #239 + '11C' // 755e #288 + '3P' // 755f #93 + '97W' // 7560 #2544 + '3P' // 7561 #93 + '207F' // 7562 #5387 + '22V' // 7563 #593 + '5A' // 7564 #130 + '220P' // 7565 #5735 + '9F' // 7566 #239 + '5A' // 7567 #130 + '13N' // 7568 #351 + '3P' // 7569 #93 + '199I' // 756a #5182 + '225C' // 756b #5852 + '5A' // 756c #130 + '25Q' // 756d #666 + '22V' // 756e #593 + '9F' // 756f #239 + '223D' // 7570 #5801 + '11C' // 7571 #288 + '5A' // 7572 #130 + '255B' // 7573 #6631 + '249H' // 7574 #6481 + '249G' // 7575 #6480 + '239S' // 7576 #6232 + '25Q' // 7577 #666 + '97P' // 7578 #2537 + '5A' // 7579 #130 + '9F' // 757a #239 + 'a11C' // 757b-757c #288 + '3P' // 757d #93 + '5A' // 757e #130 + '98C' // 757f #2550 + 'A' // 7580 + 'a11C' // 7581-7582 #288 + 'a33Z' // 7583-7584 #883 + '3P' // 7585 #93 + '150W' // 7586 #3922 + '9F' // 7587 #239 + 'A' // 7588 + '11C' // 7589 #288 + '178L' // 758a #4639 + '9F' // 758b #239 + '5A' // 758c #130 + '33Z' // 758d #883 + '98B' // 758e #2549 + '171A' // 758f #4446 + '5A' // 7590 #130 + '211Y' // 7591 #5510 + '5A' // 7592 #130 + '3P' // 7593 #93 + '5A' // 7594 #130 + '25Q' // 7595 #666 + '13N' // 7596 #351 + '2C' // 7597 #54 + 'A' // 7598 + 'a5A' // 7599-759a #130 + 'A' // 759b + '3P' // 759c #93 + '9F' // 759d #239 + '57M' // 759e #1494 + 'a13N' // 759f-75a0 #351 + '2W' // 75a1 #74 + '25Q' // 75a2 #666 + '5A' // 75a3 #130 + '133E' // 75a4 #3462 + '9F' // 75a5 #239 + 'A' // 75a6 + '22V' // 75a7 #593 + '13N' // 75a8 #351 + 'A' // 75a9 + '22V' // 75aa #593 + '166U' // 75ab #4336 + 'a13N' // 75ac-75ad #351 + '3X' // 75ae #101 + '3N' // 75af #91 + '5A' // 75b0 #130 + '9F' // 75b1 #239 + '169C' // 75b2 #4396 + '9F' // 75b3 #239 + '5A' // 75b4 #130 + '150T' // 75b5 #3919 + 'A' // 75b6 + '3P' // 75b7 #93 + '9F' // 75b8 #239 + '142C' // 75b9 #3694 + '3P' // 75ba #93 + 'A' // 75bb + '172T' // 75bc #4491 + '9F' // 75bd #239 + '188X' // 75be #4911 + '3P' // 75bf #93 + '25Q' // 75c0 #666 + '11C' // 75c1 #288 + '9F' // 75c2 #239 + 'a5A' // 75c3-75c4 #130 + '212Y' // 75c5 #5536 + '3P' // 75c6 #93 + '198U' // 75c7 #5168 + '98G' // 75c8 #2554 + '13N' // 75c9 #351 + '5A' // 75ca #130 + 'A' // 75cb + '5A' // 75cc #130 + '33X' // 75cd #881 + 'a3P' // 75ce-75cf #93 + 'aA' // 75d0-75d1 + '97V' // 75d2 #2543 + '3P' // 75d3 #93 + '33X' // 75d4 #881 + '180P' // 75d5 #4695 + '13N' // 75d6 #351 + '3P' // 75d7 #93 + '65D' // 75d8 #1693 + '98A' // 75d9 #2548 + 'A' // 75da + '211X' // 75db #5509 + '45P' // 75dc #1185 + '3P' // 75dd #93 + '185R' // 75de #4827 + '57N' // 75df #1495 + '128F' // 75e0 #3333 + '3P' // 75e1 #93 + '97X' // 75e2 #2545 + 'a20I' // 75e3-75e4 #528 + 'A' // 75e5 + '45R' // 75e6 #1187 + '20I' // 75e7 #528 + '13N' // 75e8 #351 + '254Q' // 75e9 #6620 + '13N' // 75ea #351 + '3G' // 75eb #84 + '3P' // 75ec #93 + 'A' // 75ed + 'a3P' // 75ee-75ef #93 + '97O' // 75f0 #2536 + '20I' // 75f1 #528 + '33X' // 75f2 #881 + '20I' // 75f3 #528 + '161V' // 75f4 #4207 + 'aA' // 75f5-75f6 + '45R' // 75f7 #1187 + 'A' // 75f8 + '20I' // 75f9 #528 + '97Y' // 75fa #2546 + 'A' // 75fb + '33X' // 75fc #881 + 'A' // 75fd + 'a20I' // 75fe-75ff #528 + '33X' // 7600 #881 + '20I' // 7601 #528 + '57N' // 7602 #1495 + '11C' // 7603 #288 + '3P' // 7604 #93 + 'a13N' // 7605-7606 #351 + '45P' // 7607 #1185 + 'b20I' // 7608-760a #528 + '200C' // 760b #5202 + '20I' // 760c #528 + '97Z' // 760d #2547 + '13N' // 760e #351 + '3P' // 760f #93 + '45R' // 7610 #1187 + '13N' // 7611 #351 + '11C' // 7612 #288 + '128E' // 7613 #3332 + '6I' // 7614 #164 + '97Q' // 7615 #2538 + '6X' // 7616 #179 + '6I' // 7617 #164 + '41O' // 7618 #1080 + '25O' // 7619 #664 + '38S' // 761a #1006 + 'b6X' // 761b-761d #179 + '29O' // 761e #768 + 'a25O' // 761f-7620 #664 + '64R' // 7621 #1681 + '25O' // 7622 #664 + '29O' // 7623 #768 + '150V' // 7624 #3921 + '6X' // 7625 #179 + '200B' // 7626 #5201 + '6X' // 7627 #179 + '41O' // 7628 #1080 + '6X' // 7629 #179 + '6I' // 762a #164 + '3X' // 762b #101 + '33V' // 762c #879 + '3P' // 762d #93 + 'a6I' // 762e-762f #164 + '6X' // 7630 #179 + 'A' // 7631 + 'c6X' // 7632-7635 #179 + 'aA' // 7636-7637 + '6X' // 7638 #179 + '41O' // 7639 #1080 + '6X' // 763a #179 + '45O' // 763b #1184 + '6X' // 763c #179 + '6I' // 763d #164 + '2R' // 763e #69 + '6I' // 763f #164 + '6X' // 7640 #179 + '3P' // 7641 #93 + '216A' // 7642 #5616 + '6X' // 7643 #179 + 'a3P' // 7644-7645 #93 + '29O' // 7646 #768 + '122T' // 7647 #3191 + '6X' // 7648 #179 + '29O' // 7649 #768 + 'a3P' // 764a-764b #93 + '187T' // 764c #4881 + '25P' // 764d #665 + '25O' // 764e #664 + '33V' // 764f #879 + 'A' // 7650 + '33V' // 7651 #879 + '183E' // 7652 #4762 + 'A' // 7653 + '25P' // 7654 #665 + '3P' // 7655 #93 + '130V' // 7656 #3401 + 'A' // 7657 + '6X' // 7658 #179 + '3P' // 7659 #93 + '6I' // 765a #164 + 'A' // 765b + '6X' // 765c #179 + 'A' // 765d + '6I' // 765e #164 + '6X' // 765f #179 + 'A' // 7660 + '150S' // 7661 #3918 + '150X' // 7662 #3923 + '6I' // 7663 #164 + '45O' // 7664 #1184 + '6X' // 7665 #179 + '38S' // 7666 #1006 + '29O' // 7667 #768 + '3P' // 7668 #93 + '25O' // 7669 #664 + '3P' // 766a #93 + '3X' // 766b #101 + '25O' // 766c #664 + '29O' // 766d #768 + '163Z' // 766e #4263 + '6X' // 766f #179 + '45O' // 7670 #1184 + '139L' // 7671 #3625 + '64R' // 7672 #1681 + '97M' // 7673 #2534 + '97G' // 7674 #2528 + '6I' // 7675 #164 + '6X' // 7676 #179 + 'A' // 7677 + '25O' // 7678 #664 + '25P' // 7679 #665 + '97L' // 767a #2533 + '35U' // 767b #930 + '246A' // 767c #6396 + '238B' // 767d #6189 + '234R' // 767e #6101 + '25P' // 767f #665 + '3P' // 7680 #93 + '6X' // 7681 #179 + '157M' // 7682 #4094 + '41O' // 7683 #1080 + '69I' // 7684 #1802 + '3P' // 7685 #93 + '213D' // 7686 #5541 + '202W' // 7687 #5274 + '6X' // 7688 #179 + 'A' // 7689 + '25P' // 768a #665 + '6X' // 768b #179 + '21I' // 768c #554 + '3J' // 768d #87 + '45N' // 768e #1183 + 'A' // 768f + '45N' // 7690 #1183 + 'a6I' // 7691-7692 #164 + '139H' // 7693 #3621 + 'A' // 7694 + '14B' // 7695 #365 + '97F' // 7696 #2527 + 'A' // 7697 + '6I' // 7698 #164 + '122S' // 7699 #3190 + 'a14B' // 769a-769b #365 + '38R' // 769c #1005 + 'a14B' // 769d-769e #365 + 'a21I' // 769f-76a0 #554 + '45M' // 76a1 #1182 + '21I' // 76a2 #554 + '3J' // 76a3 #87 + '14B' // 76a4 #365 + '57J' // 76a5 #1491 + 'a21I' // 76a6-76a7 #554 + '3J' // 76a8 #87 + 'A' // 76a9 + '38R' // 76aa #1005 + 'a6I' // 76ab-76ac #164 + '3J' // 76ad #87 + '227Q' // 76ae #5918 + '21I' // 76af #554 + '14B' // 76b0 #365 + '3H' // 76b1 #85 + '6I' // 76b2 #164 + 'A' // 76b3 + '14B' // 76b4 #365 + '6I' // 76b5 #164 + '3J' // 76b6 #87 + 'a38R' // 76b7-76b8 #1005 + '3J' // 76b9 #87 + '157K' // 76ba #4092 + '6I' // 76bb #164 + 'A' // 76bc + '3J' // 76bd #87 + 'A' // 76be + '125S' // 76bf #3268 + 'A' // 76c0 + '3J' // 76c1 #87 + '45N' // 76c2 #1183 + '163X' // 76c3 #4261 + 'A' // 76c4 + '14B' // 76c5 #365 + '174W' // 76c6 #4546 + 'A' // 76c7 + '178J' // 76c8 #4637 + '14B' // 76c9 #365 + '219L' // 76ca #5705 + '3J' // 76cb #87 + 'a14B' // 76cc-76cd #365 + '122Q' // 76ce #3188 + '2W' // 76cf #74 + '4C' // 76d0 #106 + '2C' // 76d1 #54 + '207E' // 76d2 #5386 + 'A' // 76d3 + '63U' // 76d4 #1658 + 'A' // 76d5 + '97E' // 76d6 #2526 + '256L' // 76d7 #6667 + '2D' // 76d8 #55 + '45M' // 76d9 #1182 + 'A' // 76da + '212M' // 76db #5524 + '185Q' // 76dc #4826 + '6I' // 76dd #164 + '122P' // 76de #3187 + '210B' // 76df #5461 + '3J' // 76e0 #87 + '214M' // 76e1 #5576 + '6I' // 76e2 #164 + '211Q' // 76e3 #5502 + '220I' // 76e4 #5728 + 'a14B' // 76e5-76e6 #365 + '178I' // 76e7 #4636 + '21I' // 76e8 #554 + '25P' // 76e9 #665 + '145O' // 76ea #3784 + '3J' // 76eb #87 + '38R' // 76ec #1005 + 'A' // 76ed + '245G' // 76ee #6376 + '143V' // 76ef #3739 + '3J' // 76f0 #87 + '14B' // 76f1 #365 + '159T' // 76f2 #4153 + 'A' // 76f3 + '237N' // 76f4 #6175 + 'A' // 76f5 + '3J' // 76f6 #87 + '38S' // 76f7 #1006 + '243N' // 76f8 #6331 + '14B' // 76f9 #365 + '25P' // 76fa #665 + '14B' // 76fb #365 + '163Y' // 76fc #4262 + '6I' // 76fd #164 + '167A' // 76fe #4342 + '6I' // 76ff #164 + '21I' // 7700 #554 + '211R' // 7701 #5503 + 'A' // 7702 + '6I' // 7703 #164 + '97C' // 7704 #2524 + '38S' // 7705 #1006 + '3J' // 7706 #87 + '57K' // 7707 #1492 + '97B' // 7708 #2523 + '180W' // 7709 #4702 + '38Q' // 770a #1004 + '241D' // 770b #6269 + '97K' // 770c #2532 + '6I' // 770d #164 + '57J' // 770e #1491 + '97N' // 770f #2535 + 'aA' // 7710-7711 + '21I' // 7712 #554 + 'A' // 7713 + '21I' // 7714 #554 + '38Q' // 7715 #1004 + '6I' // 7716 #164 + '3J' // 7717 #87 + 'A' // 7718 + 'b57K' // 7719-771b #1492 + '3J' // 771c #87 + '29P' // 771d #769 + '97J' // 771e #2531 + '244S' // 771f #6362 + '183J' // 7720 #4767 + '6I' // 7721 #164 + '9R' // 7722 #251 + 'A' // 7723 + '45M' // 7724 #1182 + '38Q' // 7725 #1004 + '9R' // 7726 #251 + 'A' // 7727 + '9R' // 7728 #251 + '135V' // 7729 #3531 + 'A' // 772a + '57L' // 772b #1493 + '8Q' // 772c #224 + '9R' // 772d #251 + '23Y' // 772e #622 + '9R' // 772f #251 + '8Q' // 7730 #224 + 'A' // 7731 + '8Q' // 7732 #224 + '33W' // 7733 #880 + 'b9R' // 7734-7736 #251 + '139E' // 7737 #3618 + '97D' // 7738 #2525 + '23Y' // 7739 #622 + '130U' // 773a #3400 + '29P' // 773b #769 + '227U' // 773c #5922 + '9R' // 773d #251 + '217G' // 773e #5648 + '8Q' // 773f #224 + '176Z' // 7740 #4601 + '3G' // 7741 #84 + '3J' // 7742 #87 + '57L' // 7743 #1493 + '29P' // 7744 #769 + '3J' // 7745 #87 + '9R' // 7746 #251 + '178K' // 7747 #4638 + 'aA' // 7748-7749 + '3J' // 774a #87 + 'A' // 774b + '33W' // 774c #880 + '249E' // 774d #6478 + 'a9R' // 774e-774f #251 + '3X' // 7750 #101 + '8Q' // 7751 #224 + '9R' // 7752 #251 + 'aA' // 7753-7754 + '29P' // 7755 #769 + 'a3J' // 7756-7757 #87 + '57I' // 7758 #1490 + '33W' // 7759 #880 + '9R' // 775a #251 + '192S' // 775b #5010 + '63T' // 775c #1657 + '8Q' // 775d #224 + '63T' // 775e #1657 + 'a38Q' // 775f-7760 #1004 + '209N' // 7761 #5447 + '9R' // 7762 #251 + '183H' // 7763 #4765 + '3J' // 7764 #87 + '9R' // 7765 #251 + '97I' // 7766 #2530 + '3J' // 7767 #87 + '97H' // 7768 #2529 + '33W' // 7769 #880 + '9R' // 776a #251 + '145M' // 776b #3782 + '9R' // 776c #251 + '33W' // 776d #880 + '29P' // 776e #769 + 'A' // 776f + '3J' // 7770 #87 + '8Q' // 7771 #224 + '57I' // 7772 #1490 + 'a3J' // 7773-7774 #87 + 'aA' // 7775-7776 + '33V' // 7777 #879 + '29P' // 7778 #769 + '65C' // 7779 #1692 + '9R' // 777a #251 + '33V' // 777b #879 + '23Y' // 777c #622 + '9R' // 777d #251 + '22S' // 777e #590 + '145N' // 777f #3783 + '6W' // 7780 #178 + 'bA' // 7781-7783 + '145P' // 7784 #3785 + '12A' // 7785 #312 + 'A' // 7786 + '12A' // 7787 #312 + 'A' // 7788 + '29N' // 7789 #767 + 'A' // 778a + '22S' // 778b #590 + 'a6W' // 778c-778d #178 + '145Q' // 778e #3786 + 'a8Q' // 778f-7790 #224 + '57H' // 7791 #1489 + '3G' // 7792 #84 + '12A' // 7793 #312 + 'b3J' // 7794-7796 #87 + 'A' // 7797 + '38O' // 7798 #1002 + 'A' // 7799 + '3J' // 779a #87 + 'A' // 779b + '29N' // 779c #767 + 'A' // 779d + '133C' // 779e #3460 + 'a6W' // 779f-77a0 #178 + 'A' // 77a1 + '6W' // 77a2 #178 + 'A' // 77a3 + '3J' // 77a4 #87 + '22S' // 77a5 #590 + 'A' // 77a6 + '139J' // 77a7 #3623 + 'A' // 77a8 + '249F' // 77a9 #6479 + '122R' // 77aa #3189 + 'A' // 77ab + '183K' // 77ac #4768 + '180E' // 77ad #4684 + '3J' // 77ae #87 + '57F' // 77af #1487 + '57D' // 77b0 #1485 + '29M' // 77b1 #766 + 'A' // 77b2 + '142V' // 77b3 #3713 + '12A' // 77b4 #312 + '23Y' // 77b5 #622 + 'a6W' // 77b6-77b7 #178 + 'A' // 77b8 + '29M' // 77b9 #766 + 'A' // 77ba + '139F' // 77bb #3619 + '96X' // 77bc #2519 + 'a6W' // 77bd-77be #178 + '22S' // 77bf #590 + '8Q' // 77c0 #224 + 'A' // 77c1 + '8Q' // 77c2 #224 + '45K' // 77c3 #1180 + 'A' // 77c4 + '12A' // 77c5 #312 + 'A' // 77c6 + '6W' // 77c7 #178 + 'A' // 77c8 + '3J' // 77c9 #87 + 'A' // 77ca + '38O' // 77cb #1002 + '29N' // 77cc #767 + '6W' // 77cd #178 + 'bA' // 77ce-77d0 + 'a3J' // 77d1-77d2 #87 + '29N' // 77d3 #767 + 'A' // 77d4 + '3J' // 77d5 #87 + '8Q' // 77d6 #224 + '22S' // 77d7 #590 + 'A' // 77d8 + '23Y' // 77d9 #622 + '139I' // 77da #3622 + '148E' // 77db #3852 + '96V' // 77dc #2517 + '38P' // 77dd #1003 + '6W' // 77de #178 + 'a3J' // 77df-77e0 #87 + 'A' // 77e1 + '143I' // 77e2 #3726 + '57D' // 77e3 #1485 + '3J' // 77e4 #87 + '35U' // 77e5 #930 + '45K' // 77e6 #1180 + '6W' // 77e7 #178 + 'A' // 77e8 + '139G' // 77e9 #3620 + '23Y' // 77ea #622 + '3X' // 77eb #101 + '6W' // 77ec #178 + '222F' // 77ed #5777 + '65C' // 77ee #1692 + '143A' // 77ef #3718 + '29M' // 77f0 #766 + '3J' // 77f1 #87 + '29N' // 77f2 #767 + '223Q' // 77f3 #5814 + '45K' // 77f4 #1180 + 'A' // 77f5 + '2K' // 77f6 #62 + 'A' // 77f7 + '6W' // 77f8 #178 + 'A' // 77f9 + '29N' // 77fa #767 + '6W' // 77fb #178 + '29M' // 77fc #766 + '162K' // 77fd #4222 + '12A' // 77fe #312 + '1R' // 77ff #43 + '8Q' // 7800 #224 + '3Q' // 7801 #94 + '183D' // 7802 #4761 + '12A' // 7803 #312 + 'A' // 7804 + '29M' // 7805 #766 + '6W' // 7806 #178 + 'A' // 7807 + '38O' // 7808 #1002 + '6W' // 7809 #178 + 'aA' // 780a-780b + '63U' // 780c #1658 + '157L' // 780d #4093 + '3J' // 780e #87 + '8Q' // 780f #224 + '12A' // 7810 #312 + '6W' // 7811 #178 + '22S' // 7812 #590 + 'A' // 7813 + '222Z' // 7814 #5797 + '253T' // 7815 #6597 + '2L' // 7816 #63 + '8Q' // 7817 #224 + '38P' // 7818 #1003 + '3J' // 7819 #87 + 'a8Q' // 781a-781b #224 + '12A' // 781c #312 + '6W' // 781d #178 + '38P' // 781e #1003 + '12A' // 781f #312 + '29M' // 7820 #766 + 'b6W' // 7821-7823 #178 + 'A' // 7824 + '57H' // 7825 #1489 + 'a22S' // 7826-7827 #590 + 'A' // 7828 + '12A' // 7829 #312 + 'A' // 782a + '8Q' // 782b #224 + '22S' // 782c #590 + 'a6W' // 782d-782e #178 + '12A' // 782f #312 + '6W' // 7830 #178 + 'A' // 7831 + '161D' // 7832 #4189 + '12A' // 7833 #312 + '221H' // 7834 #5753 + '6W' // 7835 #178 + 'A' // 7836 + '6W' // 7837 #178 + '155R' // 7838 #4047 + '12A' // 7839 #312 + '23Y' // 783a #622 + '8Q' // 783b #224 + '12A' // 783c #312 + '38O' // 783d #1002 + '8Q' // 783e #224 + '3J' // 783f #87 + '3I' // 7840 #86 + '8Q' // 7841 #224 + '97A' // 7842 #2522 + '57G' // 7843 #1488 + '57F' // 7844 #1487 + '96W' // 7845 #2518 + 'A' // 7846 + '57G' // 7847 #1488 + '23Y' // 7848 #622 + '8Q' // 7849 #224 + '25N' // 784a #663 + '38P' // 784b #1003 + '57E' // 784c #1486 + '25N' // 784d #663 + '57E' // 784e #1486 + '249D' // 784f #6477 + '22T' // 7850 #591 + '57C' // 7851 #1484 + '15L' // 7852 #401 + '22T' // 7853 #591 + '45L' // 7854 #1181 + '4C' // 7855 #106 + 'a6H' // 7856-7857 #163 + 'A' // 7858 + 'a6H' // 7859-785a #163 + 'A' // 785b + '22R' // 785c #589 + '96Y' // 785d #2520 + '3T' // 785e #97 + 'A' // 785f + 'a3T' // 7860-7861 #97 + 'A' // 7862 + '3T' // 7863 #97 + '22R' // 7864 #589 + '6H' // 7865 #163 + '25N' // 7866 #663 + 'A' // 7867 + '22R' // 7868 #589 + '6H' // 7869 #163 + '15L' // 786a #401 + '124X' // 786b #3247 + '209V' // 786c #5455 + '6H' // 786d #163 + '150R' // 786e #3917 + '33U' // 786f #878 + 'aA' // 7870-7871 + '3T' // 7872 #97 + 'A' // 7873 + '3T' // 7874 #97 + 'A' // 7875 + 'a6H' // 7876-7877 #163 + 'aA' // 7878-7879 + '33T' // 787a #877 + 'A' // 787b + '33U' // 787c #878 + 'A' // 787d + '16B' // 787e #417 + '22T' // 787f #591 + 'A' // 7880 + '125L' // 7881 #3261 + 'cA' // 7882-7885 + '22R' // 7886 #589 + '33U' // 7887 #878 + '45L' // 7888 #1181 + '22T' // 7889 #591 + '3T' // 788a #97 + 'A' // 788b + '150Q' // 788c #3916 + '96Q' // 788d #2512 + '185N' // 788e #4823 + '22R' // 788f #589 + 'A' // 7890 + '166I' // 7891 #4324 + 'A' // 7892 + '15L' // 7893 #401 + '16B' // 7894 #417 + '22R' // 7895 #589 + '25N' // 7896 #663 + '187K' // 7897 #4872 + '15L' // 7898 #401 + '6H' // 7899 #163 + '15L' // 789a #401 + 'a6H' // 789b-789c #163 + '16B' // 789d #417 + '15L' // 789e #401 + '185O' // 789f #4824 + 'A' // 78a0 + '15L' // 78a1 #401 + 'A' // 78a2 + '33U' // 78a3 #878 + '3T' // 78a4 #97 + '22T' // 78a5 #591 + 'A' // 78a6 + '180V' // 78a7 #4701 + '3T' // 78a8 #97 + '178G' // 78a9 #4634 + '22R' // 78aa #589 + 'A' // 78ab + '3T' // 78ac #97 + '15L' // 78ad #401 + 'A' // 78ae + '33T' // 78af #877 + '185P' // 78b0 #4825 + '96U' // 78b1 #2516 + '15L' // 78b2 #401 + '178H' // 78b3 #4635 + '22T' // 78b4 #591 + '3T' // 78b5 #97 + '22T' // 78b6 #591 + 'A' // 78b7 + '25N' // 78b8 #663 + '45L' // 78b9 #1181 + '231L' // 78ba #6017 + '249B' // 78bb #6475 + '225B' // 78bc #5851 + '3T' // 78bd #97 + '96T' // 78be #2515 + '16B' // 78bf #417 + 'A' // 78c0 + '181P' // 78c1 #4721 + 'A' // 78c2 + '6H' // 78c3 #163 + 'A' // 78c4 + '157J' // 78c5 #4091 + '3T' // 78c6 #97 + '33T' // 78c7 #877 + '22R' // 78c8 #589 + '15L' // 78c9 #401 + '122N' // 78ca #3185 + '33U' // 78cb #878 + '3T' // 78cc #97 + 'A' // 78cd + '96R' // 78ce #2513 + 'A' // 78cf + '96Z' // 78d0 #2521 + '15L' // 78d1 #401 + '57C' // 78d2 #1484 + '33T' // 78d3 #877 + '15L' // 78d4 #401 + '96S' // 78d5 #2514 + '3T' // 78d6 #97 + 'a25N' // 78d7-78d8 #663 + '6H' // 78d9 #163 + '66S' // 78da #1734 + '3T' // 78db #97 + '6H' // 78dc #163 + 'A' // 78dd + '22T' // 78de #591 + 'a3T' // 78df-78e0 #97 + '128B' // 78e1 #3329 + 'A' // 78e2 + '25N' // 78e3 #663 + '33T' // 78e4 #877 + '6H' // 78e5 #163 + '16B' // 78e6 #417 + '18K' // 78e7 #478 + '197O' // 78e8 #5136 + 'A' // 78e9 + '5R' // 78ea #147 + 'A' // 78eb + '29L' // 78ec #765 + 'A' // 78ed + '20H' // 78ee #527 + '160Z' // 78ef #4185 + '20H' // 78f0 #527 + '57B' // 78f1 #1483 + '5R' // 78f2 #147 + '18K' // 78f3 #478 + '5R' // 78f4 #147 + '96P' // 78f5 #2511 + '3T' // 78f6 #97 + '128C' // 78f7 #3330 + 'A' // 78f8 + '3T' // 78f9 #97 + '5R' // 78fa #147 + '29L' // 78fb #765 + 'A' // 78fc + '18K' // 78fd #478 + '5R' // 78fe #147 + '18K' // 78ff #478 + '3T' // 7900 #97 + '157G' // 7901 #4088 + '6H' // 7902 #163 + 'A' // 7903 + '33S' // 7904 #876 + '25M' // 7905 #662 + '18K' // 7906 #478 + '16B' // 7907 #417 + 'A' // 7908 + '6H' // 7909 #163 + 'A' // 790a + '6H' // 790b #163 + '5R' // 790c #147 + 'A' // 790d + '190G' // 790e #4946 + 'A' // 790f + '5R' // 7910 #147 + 'a18K' // 7911-7912 #478 + '6H' // 7913 #163 + 'aA' // 7914-7915 + '260S' // 7916 #6778 + 'aA' // 7917-7918 + '192R' // 7919 #5009 + 'a16B' // 791a-791b #417 + '18K' // 791c #478 + 'A' // 791d + '5R' // 791e #147 + '16B' // 791f #417 + '3T' // 7920 #97 + '25M' // 7921 #662 + 'aA' // 7922-7923 + '6H' // 7924 #163 + '3T' // 7925 #97 + '66S' // 7926 #1734 + 'b3T' // 7927-7929 #97 + 'a29L' // 792a-792b #765 + '96H' // 792c #2503 + '3T' // 792d #97 + '18K' // 792e #478 + 'A' // 792f + '3T' // 7930 #97 + '18K' // 7931 #478 + 'a20H' // 7932-7933 #527 + '5R' // 7934 #147 + '3T' // 7935 #97 + '57B' // 7936 #1483 + 'A' // 7937 + '33S' // 7938 #876 + '6H' // 7939 #163 + '239A' // 793a #6214 + '5R' // 793b #147 + '125Z' // 793c #3275 + '5R' // 793d #147 + '49H' // 793e #1281 + '18K' // 793f #478 + '141Q' // 7940 #3682 + '96F' // 7941 #2501 + '5R' // 7942 #147 + '6H' // 7943 #163 + '3T' // 7944 #97 + 'a5R' // 7945-7946 #147 + '96K' // 7947 #2506 + '168C' // 7948 #4370 + '96L' // 7949 #2507 + '16B' // 794a #417 + '3T' // 794b #97 + '25M' // 794c #662 + 'A' // 794d + '6H' // 794e #163 + '16B' // 794f #417 + '154X' // 7950 #4027 + '16B' // 7951 #417 + 'A' // 7952 + '96J' // 7953 #2505 + '5R' // 7954 #147 + '170Z' // 7955 #4445 + '196T' // 7956 #5115 + '29L' // 7957 #765 + '5R' // 7958 #147 + '25M' // 7959 #662 + '29L' // 795a #765 + '96G' // 795b #2502 + '29L' // 795c #765 + '199C' // 795d #5176 + '238O' // 795e #6202 + '5R' // 795f #147 + '128A' // 7960 #3328 + '33S' // 7961 #876 + '5R' // 7962 #147 + 'A' // 7963 + '25M' // 7964 #662 + '189P' // 7965 #4929 + '6H' // 7966 #163 + '5R' // 7967 #147 + '220C' // 7968 #5722 + '5R' // 7969 #147 + 'A' // 796a + '5R' // 796b #147 + 'A' // 796c + '183W' // 796d #4780 + 'A' // 796e + '6H' // 796f #163 + 'A' // 7970 + '20H' // 7971 #527 + '5R' // 7972 #147 + '33S' // 7973 #876 + '6H' // 7974 #163 + 'aA' // 7975-7976 + '249C' // 7977 #6476 + '2Y' // 7978 #76 + '5R' // 7979 #147 + '122L' // 797a #3183 + '16B' // 797b #417 + '5R' // 797c #147 + 'A' // 797d + '5R' // 797e #147 + '139B' // 797f #3615 + '5R' // 7980 #147 + '213H' // 7981 #5545 + '25M' // 7982 #662 + '20H' // 7983 #527 + '252H' // 7984 #6559 + '253G' // 7985 #6584 + 'a25M' // 7986-7987 #662 + '33S' // 7988 #876 + '6H' // 7989 #163 + '96I' // 798a #2504 + '9E' // 798b #238 + '31J' // 798c #815 + '178F' // 798d #4633 + '133A' // 798e #3458 + '230Z' // 798f #6005 + 'A' // 7990 + '56Y' // 7991 #1480 + '9M' // 7992 #246 + '25K' // 7993 #660 + 'a9E' // 7994-7995 #238 + '25K' // 7996 #660 + '9M' // 7997 #246 + '9E' // 7998 #238 + '20H' // 7999 #527 + '25L' // 799a #661 + '9E' // 799b #238 + '31J' // 799c #815 + '29K' // 799d #764 + 'A' // 799e + '25L' // 799f #661 + '33R' // 79a0 #875 + '25K' // 79a1 #660 + '33R' // 79a2 #875 + '9M' // 79a3 #246 + '25L' // 79a4 #661 + '57A' // 79a5 #1482 + '65S' // 79a6 #1708 + '139C' // 79a7 #3616 + 'a9E' // 79a8-79a9 #238 + '65S' // 79aa #1708 + '31J' // 79ab #815 + '9M' // 79ac #246 + 'A' // 79ad + '225A' // 79ae #5850 + '31J' // 79af #815 + '9E' // 79b0 #238 + '139A' // 79b1 #3614 + 'A' // 79b2 + '45J' // 79b3 #1179 + '31J' // 79b4 #815 + '9M' // 79b5 #246 + 'aA' // 79b6-79b7 + '9E' // 79b8 #238 + '122M' // 79b9 #3184 + '9E' // 79ba #238 + '122O' // 79bb #3186 + 'A' // 79bc + '139D' // 79bd #3617 + '157H' // 79be #4089 + '124P' // 79bf #3239 + '215I' // 79c0 #5598 + '237F' // 79c1 #6167 + '31J' // 79c2 #815 + '2W' // 79c3 #74 + '29K' // 79c4 #764 + 'A' // 79c5 + '25L' // 79c6 #661 + '3T' // 79c7 #97 + '9E' // 79c8 #238 + '157I' // 79c9 #4090 + '96E' // 79ca #2500 + '213L' // 79cb #5549 + '29K' // 79cc #764 + '133B' // 79cd #3459 + 'A' // 79ce + '9E' // 79cf #238 + '20H' // 79d0 #527 + '235X' // 79d1 #6133 + '210V' // 79d2 #5481 + 'A' // 79d3 + '29K' // 79d4 #764 + '45J' // 79d5 #1179 + '9E' // 79d6 #238 + 'A' // 79d7 + '221A' // 79d8 #5746 + 'A' // 79d9 + '3T' // 79da #97 + 'A' // 79db + '33R' // 79dc #875 + 'a9E' // 79dd-79de #238 + '200A' // 79df #5200 + 'a3T' // 79e0-79e1 #97 + '29K' // 79e2 #764 + '9E' // 79e3 #238 + '148Y' // 79e4 #3872 + '3T' // 79e5 #97 + '160B' // 79e6 #4161 + '45J' // 79e7 #1179 + '9M' // 79e8 #246 + '136H' // 79e9 #3543 + '25K' // 79ea #660 + '9E' // 79eb #238 + '25K' // 79ec #660 + '9E' // 79ed #238 + 'A' // 79ee + '2D' // 79ef #55 + '257J' // 79f0 #6691 + '29K' // 79f1 #764 + 'aA' // 79f2-79f3 + '25L' // 79f4 #661 + 'A' // 79f5 + '33R' // 79f6 #875 + '25L' // 79f7 #661 + '9E' // 79f8 #238 + 'A' // 79f9 + '9M' // 79fa #246 + '213M' // 79fb #5550 + '3T' // 79fc #97 + '2Y' // 79fd #76 + '9M' // 79fe #246 + 'A' // 79ff + '174J' // 7a00 #4533 + 'A' // 7a01 + 'a9E' // 7a02-7a03 #238 + 'A' // 7a04 + '199Z' // 7a05 #5199 + '20H' // 7a06 #527 + '3T' // 7a07 #97 + '56Y' // 7a08 #1480 + '3T' // 7a09 #97 + '9E' // 7a0a #238 + '241V' // 7a0b #6287 + '25K' // 7a0c #660 + '192Q' // 7a0d #5008 + '96O' // 7a0e #2510 + 'A' // 7a0f + '33R' // 7a10 #875 + '25K' // 7a11 #660 + 'aA' // 7a12-7a13 + '56Z' // 7a14 #1481 + '3T' // 7a15 #97 + 'A' // 7a16 + '38N' // 7a17 #1001 + '56X' // 7a18 #1479 + '38N' // 7a19 #1001 + '161Y' // 7a1a #4210 + '2O' // 7a1b #66 + '56Z' // 7a1c #1481 + 'A' // 7a1d + '56W' // 7a1e #1478 + '38N' // 7a1f #1001 + '127X' // 7a20 #3325 + '2O' // 7a21 #66 + 'A' // 7a22 + '9M' // 7a23 #246 + 'A' // 7a24 + '9M' // 7a25 #246 + '25L' // 7a26 #661 + '2O' // 7a27 #66 + 'aA' // 7a28-7a29 + '20H' // 7a2a #527 + '2O' // 7a2b #66 + '57A' // 7a2c #1482 + '56X' // 7a2d #1479 + '237V' // 7a2e #6183 + '2O' // 7a2f #66 + '19H' // 7a30 #501 + '232J' // 7a31 #6041 + '96N' // 7a32 #2509 + '3I' // 7a33 #86 + 'a2O' // 7a34-7a35 #66 + '260T' // 7a36 #6779 + '38N' // 7a37 #1001 + '2O' // 7a38 #66 + '56W' // 7a39 #1478 + '56V' // 7a3a #1477 + '170Y' // 7a3b #4444 + '96M' // 7a3c #2508 + '136G' // 7a3d #3542 + '56V' // 7a3e #1477 + '206K' // 7a3f #5366 + '153Q' // 7a40 #3994 + 'A' // 7a41 + '255N' // 7a42 #6643 + '18H' // 7a43 #475 + '2O' // 7a44 #66 + '18J' // 7a45 #477 + '145L' // 7a46 #3781 + '19H' // 7a47 #501 + '2O' // 7a48 #66 + '29J' // 7a49 #763 + 'aA' // 7a4a-7a4b + '132Y' // 7a4c #3456 + '228X' // 7a4d #5951 + '66U' // 7a4e #1736 + '253Y' // 7a4f #6602 + '2O' // 7a50 #66 + '9M' // 7a51 #246 + 'bA' // 7a52-7a54 + '19H' // 7a55 #501 + '18J' // 7a56 #477 + '145J' // 7a57 #3779 + 'A' // 7a58 + '2O' // 7a59 #66 + '38M' // 7a5a #1000 + '9M' // 7a5b #246 + '18J' // 7a5c #477 + '19H' // 7a5d #501 + '9M' // 7a5e #246 + '2O' // 7a5f #66 + '18J' // 7a60 #477 + '56T' // 7a61 #1475 + '150P' // 7a62 #3915 + '19H' // 7a63 #501 + 'A' // 7a64 + '18H' // 7a65 #475 + '9M' // 7a66 #246 + '2O' // 7a67 #66 + '38L' // 7a68 #999 + '199Y' // 7a69 #5198 + '2O' // 7a6a #66 + '142N' // 7a6b #3705 + 'A' // 7a6c + '18J' // 7a6d #477 + '38M' // 7a6e #1000 + 'A' // 7a6f + '29J' // 7a70 #763 + '38M' // 7a71 #1000 + '45G' // 7a72 #1176 + 'A' // 7a73 + '169F' // 7a74 #4399 + '2O' // 7a75 #66 + '222M' // 7a76 #5784 + '4C' // 7a77 #106 + '11Z' // 7a78 #311 + '95P' // 7a79 #2485 + '237Z' // 7a7a #6187 + 'aA' // 7a7b-7a7c + '95M' // 7a7d #2482 + '2O' // 7a7e #66 + '218F' // 7a7f #5673 + '11Z' // 7a80 #311 + '213C' // 7a81 #5540 + '2O' // 7a82 #66 + '95V' // 7a83 #2491 + '157F' // 7a84 #4087 + 'a11Z' // 7a85-7a86 #311 + '9M' // 7a87 #246 + '29J' // 7a88 #763 + 'A' // 7a89 + '19H' // 7a8a #501 + '2O' // 7a8b #66 + 'A' // 7a8c + '2K' // 7a8d #62 + 'aA' // 7a8e-7a8f + '11Z' // 7a90 #311 + '95R' // 7a91 #2487 + '125A' // 7a92 #3250 + '96D' // 7a93 #2499 + '11Z' // 7a94 #311 + '29J' // 7a95 #763 + '11Z' // 7a96 #311 + '207D' // 7a97 #5385 + '29J' // 7a98 #763 + 'aA' // 7a99-7a9a + 'a9M' // 7a9b-7a9c #246 + '2L' // 7a9d #63 + '19H' // 7a9e #501 + '136F' // 7a9f #3541 + '11Z' // 7aa0 #311 + '9M' // 7aa1 #246 + 'A' // 7aa2 + '11Z' // 7aa3 #311 + 'A' // 7aa4 + '3H' // 7aa5 #85 + '3G' // 7aa6 #84 + 'A' // 7aa7 + '38L' // 7aa8 #999 + '66U' // 7aa9 #1736 + '95Z' // 7aaa #2495 + 'A' // 7aab + '11Z' // 7aac #311 + '9M' // 7aad #246 + '180O' // 7aae #4694 + '130L' // 7aaf #3391 + '11Z' // 7ab0 #311 + 'aA' // 7ab1-7ab2 + '11Z' // 7ab3 #311 + 'A' // 7ab4 + '2O' // 7ab5 #66 + '18J' // 7ab6 #477 + 'A' // 7ab7 + '38L' // 7ab8 #999 + '2O' // 7ab9 #66 + '145I' // 7aba #3778 + '18J' // 7abb #477 + '18H' // 7abc #475 + '19H' // 7abd #501 + 'a11Z' // 7abe-7abf #311 + 'aA' // 7ac0-7ac1 + '38M' // 7ac2 #1000 + '18H' // 7ac3 #475 + '135J' // 7ac4 #3519 + '138Z' // 7ac5 #3613 + '2O' // 7ac6 #66 + '122K' // 7ac7 #3182 + '11Z' // 7ac8 #311 + '18J' // 7ac9 #477 + '157E' // 7aca #4086 + '68W' // 7acb #1790 + 'b2O' // 7acc-7ace #66 + '18H' // 7acf #475 + 'A' // 7ad0 + '11Z' // 7ad1 #311 + '19H' // 7ad2 #501 + '18H' // 7ad3 #475 + 'A' // 7ad4 + '2O' // 7ad5 #66 + '3H' // 7ad6 #85 + '71D' // 7ad7 #1849 + 'A' // 7ad8 + '240W' // 7ad9 #6262 + '11Z' // 7ada #311 + '95Q' // 7adb #2486 + '96C' // 7adc #2498 + '29J' // 7add #763 + '3I' // 7ade #86 + '214L' // 7adf #5575 + '234C' // 7ae0 #6086 + '19H' // 7ae1 #501 + '18H' // 7ae2 #475 + '124W' // 7ae3 #3246 + '38L' // 7ae4 #999 + '227W' // 7ae5 #5924 + '56U' // 7ae6 #1476 + '18H' // 7ae7 #475 + '2O' // 7ae8 #66 + '18J' // 7ae9 #477 + '56T' // 7aea #1475 + '18J' // 7aeb #477 + '2O' // 7aec #66 + '145K' // 7aed #3780 + 'A' // 7aee + '212Q' // 7aef #5528 + 'a2O' // 7af0-7af1 #66 + 'aA' // 7af2-7af3 + '14Q' // 7af4 #380 + '7W' // 7af5 #204 + '204V' // 7af6 #5325 + '7W' // 7af7 #204 + '2O' // 7af8 #66 + '215K' // 7af9 #5600 + '95O' // 7afa #2484 + '56U' // 7afb #1476 + '45G' // 7afc #1176 + 'a5H' // 7afd-7afe #137 + '135U' // 7aff #3530 + '7W' // 7b00 #204 + '14A' // 7b01 #364 + '14Q' // 7b02 #380 + '7W' // 7b03 #204 + '5H' // 7b04 #137 + '14A' // 7b05 #364 + '5H' // 7b06 #137 + '14Q' // 7b07 #380 + '127Y' // 7b08 #3326 + '14A' // 7b09 #364 + '5H' // 7b0a #137 + '95S' // 7b0b #2488 + '38K' // 7b0c #998 + 'A' // 7b0d + '14A' // 7b0e #364 + '18I' // 7b0f #476 + '45I' // 7b10 #1178 + '223C' // 7b11 #5800 + '2O' // 7b12 #66 + '7W' // 7b13 #204 + '95T' // 7b14 #2489 + 'a7W' // 7b15-7b16 #204 + 'A' // 7b17 + '5H' // 7b18 #137 + '122J' // 7b19 #3181 + '45I' // 7b1a #1178 + '142J' // 7b1b #3701 + 'aA' // 7b1c-7b1d + '18I' // 7b1e #476 + '5H' // 7b1f #137 + '125Q' // 7b20 #3266 + 'A' // 7b21 + '14A' // 7b22 #364 + '5H' // 7b23 #137 + '14A' // 7b24 #364 + '5H' // 7b25 #137 + '209A' // 7b26 #5434 + '18H' // 7b27 #475 + '163W' // 7b28 #4260 + 'b5H' // 7b29-7b2b #137 + '243A' // 7b2c #6318 + '18I' // 7b2d #476 + '5H' // 7b2e #137 + '2O' // 7b2f #66 + '14Q' // 7b30 #380 + '5H' // 7b31 #137 + '14A' // 7b32 #364 + 'b5H' // 7b33-7b35 #137 + '14Q' // 7b36 #380 + '7W' // 7b37 #204 + '14A' // 7b38 #364 + '96A' // 7b39 #2496 + '7W' // 7b3a #204 + '5H' // 7b3b #137 + '2R' // 7b3c #69 + '2O' // 7b3d #66 + '7W' // 7b3e #204 + '14Q' // 7b3f #380 + '2O' // 7b40 #66 + '14Q' // 7b41 #380 + '38K' // 7b42 #998 + '45G' // 7b43 #1176 + '7W' // 7b44 #204 + '5H' // 7b45 #137 + '221G' // 7b46 #5752 + '5H' // 7b47 #137 + '95X' // 7b48 #2493 + '242V' // 7b49 #6313 + '14A' // 7b4a #364 + '176Q' // 7b4b #4592 + '18I' // 7b4c #476 + '145H' // 7b4d #3777 + '5H' // 7b4e #137 + 'a18I' // 7b4f-7b50 #476 + '125P' // 7b51 #3265 + '182E' // 7b52 #4736 + '2O' // 7b53 #66 + '222E' // 7b54 #5776 + '18H' // 7b55 #475 + '68J' // 7b56 #1777 + 'A' // 7b57 + '14A' // 7b58 #364 + 'A' // 7b59 + '7W' // 7b5a #204 + '2Y' // 7b5b #76 + '7W' // 7b5c #204 + '248Z' // 7b5d #6473 + 'aA' // 7b5e-7b5f + '18I' // 7b60 #476 + 'A' // 7b61 + '45I' // 7b62 #1178 + 'A' // 7b63 + '2O' // 7b64 #66 + '45H' // 7b65 #1177 + '5H' // 7b66 #137 + '95U' // 7b67 #2490 + 'A' // 7b68 + '5H' // 7b69 #137 + '2O' // 7b6a #66 + 'A' // 7b6b + '95N' // 7b6c #2483 + '5H' // 7b6d #137 + '18I' // 7b6e #476 + '5H' // 7b6f #137 + '2O' // 7b70 #66 + '127Z' // 7b71 #3327 + 'b5H' // 7b72-7b74 #137 + '18I' // 7b75 #476 + '14A' // 7b76 #364 + '132Z' // 7b77 #3457 + 'A' // 7b78 + '249A' // 7b79 #6474 + '2O' // 7b7a #66 + '38K' // 7b7b #998 + '7W' // 7b7c #204 + '260R' // 7b7d #6777 + '1Z' // 7b7e #51 + '14Q' // 7b7f #380 + '3Q' // 7b80 #94 + 'A' // 7b81 + '14A' // 7b82 #364 + 'A' // 7b83 + '45H' // 7b84 #1177 + '14A' // 7b85 #364 + '2O' // 7b86 #66 + '96B' // 7b87 #2497 + 'A' // 7b88 + '2O' // 7b89 #66 + 'A' // 7b8a + '95W' // 7b8b #2492 + '7W' // 7b8c #204 + 'a5H' // 7b8d-7b8e #137 + '18I' // 7b8f #476 + 'b5H' // 7b90-7b92 #137 + '7W' // 7b93 #204 + '124V' // 7b94 #3245 + '95Y' // 7b95 #2494 + '5H' // 7b96 #137 + '68J' // 7b97 #1777 + '5H' // 7b98 #137 + '14Q' // 7b99 #380 + '248Y' // 7b9a #6472 + '14Q' // 7b9b #380 + '5H' // 7b9c #137 + '18I' // 7b9d #476 + 'a14Q' // 7b9e-7b9f #380 + '45H' // 7ba0 #1177 + '237E' // 7ba1 #6166 + '38K' // 7ba2 #998 + '56R' // 7ba3 #1473 + '7W' // 7ba4 #204 + '2O' // 7ba5 #66 + 'c7W' // 7ba6-7ba9 #204 + '252F' // 7baa #6557 + '7W' // 7bab #204 + '95D' // 7bac #2473 + '170X' // 7bad #4443 + '56R' // 7bae #1473 + 'a14Q' // 7baf-7bb0 #380 + '228D' // 7bb1 #5931 + '56P' // 7bb2 #1471 + 'A' // 7bb3 + '45D' // 7bb4 #1173 + 'a2O' // 7bb5-7bb6 #66 + '7W' // 7bb7 #204 + '95H' // 7bb8 #2477 + '95K' // 7bb9 #2480 + '2O' // 7bba #66 + '14Q' // 7bbb #380 + 'a2O' // 7bbc-7bbd #66 + 'aA' // 7bbe-7bbf + '235O' // 7bc0 #6124 + '45D' // 7bc1 #1173 + '2O' // 7bc2 #66 + '7W' // 7bc3 #204 + '219P' // 7bc4 #5709 + '56P' // 7bc5 #1471 + '45D' // 7bc6 #1173 + '226U' // 7bc7 #5896 + '70A' // 7bc8 #1820 + '198W' // 7bc9 #5170 + '70A' // 7bca #1820 + 'a95C' // 7bcb-7bcc #2472 + 'aA' // 7bcd-7bce + '33P' // 7bcf #873 + '29I' // 7bd0 #762 + '7W' // 7bd1 #204 + '71D' // 7bd2 #1849 + '5Y' // 7bd3 #154 + '13Z' // 7bd4 #363 + 'A' // 7bd5 + 'a3B' // 7bd6-7bd7 #79 + 'A' // 7bd8 + 'a13Z' // 7bd9-7bda #363 + '33P' // 7bdb #873 + 'A' // 7bdc + '13Z' // 7bdd #363 + 'aA' // 7bde-7bdf + '95I' // 7be0 #2478 + '45E' // 7be1 #1174 + 'aA' // 7be2-7be3 + '130N' // 7be4 #3393 + 'a13Z' // 7be5-7be6 #363 + 'A' // 7be7 + '3B' // 7be8 #79 + '66N' // 7be9 #1729 + '13Z' // 7bea #363 + 'A' // 7beb + '29I' // 7bec #762 + '251X' // 7bed #6549 + '1R' // 7bee #43 + '5Y' // 7bef #154 + '3B' // 7bf0 #79 + '95L' // 7bf1 #2481 + 'a33P' // 7bf2-7bf3 #873 + '10F' // 7bf4 #265 + '3B' // 7bf5 #79 + '10F' // 7bf6 #265 + '150O' // 7bf7 #3914 + 'a33P' // 7bf8-7bf9 #873 + '18G' // 7bfa #474 + 'A' // 7bfb + '13Z' // 7bfc #363 + '56S' // 7bfd #1474 + '13Z' // 7bfe #363 + '56S' // 7bff #1474 + '33P' // 7c00 #873 + '13Z' // 7c01 #363 + '3B' // 7c02 #79 + '13Z' // 7c03 #363 + '3B' // 7c04 #79 + 'A' // 7c05 + '3B' // 7c06 #79 + '56N' // 7c07 #1469 + '5Y' // 7c08 #154 + '3B' // 7c09 #79 + '45E' // 7c0a #1174 + 'b13Z' // 7c0b-7c0d #363 + '3B' // 7c0e #79 + '13Z' // 7c0f #363 + 'A' // 7c10 + '13Z' // 7c11 #363 + '95A' // 7c12 #2470 + 'a3B' // 7c13-7c14 #79 + '45E' // 7c15 #1174 + '5Y' // 7c16 #154 + '3B' // 7c17 #79 + 'A' // 7c18 + '10F' // 7c19 #265 + 'A' // 7c1a + '18G' // 7c1b #474 + 'aA' // 7c1c-7c1d + '56N' // 7c1e #1469 + 'a13Z' // 7c1f-7c20 #363 + '236J' // 7c21 #6145 + 'A' // 7c22 + '5G' // 7c23 #136 + '5Y' // 7c24 #154 + '13B' // 7c25 #339 + '5G' // 7c26 #136 + '132W' // 7c27 #3454 + '10F' // 7c28 #265 + '5Y' // 7c29 #154 + 'a38I' // 7c2a-7c2b #996 + '10F' // 7c2c #265 + 'a5Y' // 7c2d-7c2e #154 + '10F' // 7c2f #265 + '5Y' // 7c30 #154 + '10F' // 7c31 #265 + '5Y' // 7c32 #154 + '10F' // 7c33 #265 + '3B' // 7c34 #79 + '38J' // 7c35 #997 + '3B' // 7c36 #79 + 'b5G' // 7c37-7c39 #136 + '10F' // 7c3a #265 + '5Y' // 7c3b #154 + 'A' // 7c3c + '199X' // 7c3d #5197 + '150M' // 7c3e #3912 + '201X' // 7c3f #5249 + '5G' // 7c40 #136 + '5Y' // 7c41 #154 + '18G' // 7c42 #474 + '185M' // 7c43 #4822 + '29I' // 7c44 #762 + 'a3B' // 7c45-7c46 #79 + '5Y' // 7c47 #154 + '33Q' // 7c48 #874 + '45F' // 7c49 #1175 + '3B' // 7c4a #79 + 'A' // 7c4b + '66N' // 7c4c #1729 + '205S' // 7c4d #5348 + 'A' // 7c4e + '3B' // 7c4f #79 + '5G' // 7c50 #136 + '18G' // 7c51 #474 + '3B' // 7c52 #79 + 'a5G' // 7c53-7c54 #136 + '3B' // 7c55 #79 + 'a13B' // 7c56-7c57 #339 + '3B' // 7c58 #79 + '5G' // 7c59 #136 + 'b13B' // 7c5a-7c5c #339 + '18G' // 7c5d #474 + '3B' // 7c5e #79 + '5G' // 7c5f #136 + '167V' // 7c60 #4363 + '3B' // 7c61 #79 + 'A' // 7c62 + '5G' // 7c63 #136 + '207C' // 7c64 #5384 + '5G' // 7c65 #136 + 'A' // 7c66 + '3B' // 7c67 #79 + 'A' // 7c68 + '13B' // 7c69 #339 + 'aA' // 7c6a-7c6b + '38I' // 7c6c #996 + '13B' // 7c6d #339 + '5G' // 7c6e #136 + '3B' // 7c6f #79 + '18G' // 7c70 #474 + 'A' // 7c71 + '163V' // 7c72 #4259 + '229I' // 7c73 #5962 + '33Q' // 7c74 #874 + '13B' // 7c75 #339 + 'bA' // 7c76-7c78 + '5G' // 7c79 #136 + '5Y' // 7c7a #154 + '145G' // 7c7b #3776 + '5G' // 7c7c #136 + '145F' // 7c7d #3775 + '13B' // 7c7e #339 + 'aA' // 7c7f-7c80 + 'a10F' // 7c81-7c82 #265 + '94X' // 7c83 #2467 + '33Q' // 7c84 #874 + '5Y' // 7c85 #154 + '18G' // 7c86 #474 + '3B' // 7c87 #79 + '5Y' // 7c88 #154 + '227K' // 7c89 #5912 + '5Y' // 7c8a #154 + '254P' // 7c8b #6619 + '5Y' // 7c8c #154 + '5G' // 7c8d #136 + '38J' // 7c8e #997 + 'a10F' // 7c8f-7c90 #265 + '33Q' // 7c91 #874 + '181Z' // 7c92 #4731 + '5Y' // 7c93 #154 + '5G' // 7c94 #136 + '95E' // 7c95 #2474 + '5Y' // 7c96 #154 + '195N' // 7c97 #5083 + '154Q' // 7c98 #4020 + '5Y' // 7c99 #154 + 'A' // 7c9a + '252I' // 7c9b #6560 + '38J' // 7c9c #997 + '5Y' // 7c9d #154 + '10F' // 7c9e #265 + '95G' // 7c9f #2476 + 'a10F' // 7ca0-7ca1 #265 + '5G' // 7ca2 #136 + 'A' // 7ca3 + '248X' // 7ca4 #6471 + '150N' // 7ca5 #3913 + '5G' // 7ca6 #136 + '136Z' // 7ca7 #3561 + '5G' // 7ca8 #136 + '5Y' // 7ca9 #154 + '3G' // 7caa #84 + '3B' // 7cab #79 + '38J' // 7cac #997 + '3B' // 7cad #79 + '94Y' // 7cae #2468 + '5Y' // 7caf #154 + '10F' // 7cb0 #265 + 'b38I' // 7cb1-7cb3 #996 + 'A' // 7cb4 + '162J' // 7cb5 #4221 + 'a10F' // 7cb6-7cb7 #265 + '29I' // 7cb8 #762 + '66C' // 7cb9 #1718 + '5G' // 7cba #136 + '10F' // 7cbb #265 + '5G' // 7cbc #136 + '122H' // 7cbd #3179 + '241N' // 7cbe #6279 + '5G' // 7cbf #136 + '10F' // 7cc0 #265 + '5Y' // 7cc1 #154 + '56O' // 7cc2 #1470 + '29I' // 7cc3 #762 + '3B' // 7cc4 #79 + '5G' // 7cc5 #136 + 'A' // 7cc6 + '56O' // 7cc7 #1470 + 'a5G' // 7cc8-7cc9 #136 + '163U' // 7cca #4258 + 'A' // 7ccb + '33Q' // 7ccc #874 + '5G' // 7ccd #136 + '13B' // 7cce #339 + '3B' // 7ccf #79 + 'aA' // 7cd0-7cd1 + '3B' // 7cd2 #79 + '18G' // 7cd3 #474 + '3B' // 7cd4 #79 + '192P' // 7cd5 #5007 + '203D' // 7cd6 #5281 + '5G' // 7cd7 #136 + '3B' // 7cd8 #79 + '132X' // 7cd9 #3455 + '18G' // 7cda #474 + 'A' // 7cdb + '5G' // 7cdc #136 + '13B' // 7cdd #339 + '125O' // 7cde #3264 + '66C' // 7cdf #1718 + '38I' // 7ce0 #996 + 'A' // 7ce1 + '13B' // 7ce2 #339 + 'A' // 7ce3 + 'a5Y' // 7ce4-7ce5 #154 + '18G' // 7ce6 #474 + '159Q' // 7ce7 #4150 + '56Q' // 7ce8 #1472 + '3B' // 7ce9 #79 + '45F' // 7cea #1175 + '3B' // 7ceb #79 + '64B' // 7cec #1665 + '45F' // 7ced #1175 + 'A' // 7cee + '122I' // 7cef #3180 + '64B' // 7cf0 #1665 + 'A' // 7cf1 + '13B' // 7cf2 #339 + '29I' // 7cf3 #762 + '13B' // 7cf4 #339 + '95B' // 7cf5 #2471 + '33O' // 7cf6 #872 + '11H' // 7cf7 #293 + '95J' // 7cf8 #2479 + '33O' // 7cf9 #872 + '3B' // 7cfa #79 + '236K' // 7cfb #6146 + '56Q' // 7cfc #1472 + 'A' // 7cfd + '170W' // 7cfe #4442 + 'A' // 7cff + '227Y' // 7d00 #5926 + 'A' // 7d01 + '94Z' // 7d02 #2469 + '3B' // 7d03 #79 + '239F' // 7d04 #6219 + '233W' // 7d05 #6080 + 'b56M' // 7d06-7d08 #1468 + '33O' // 7d09 #872 + '56M' // 7d0a #1468 + '202E' // 7d0b #5256 + 'A' // 7d0c + '213K' // 7d0d #5548 + 'A' // 7d0e + '13B' // 7d0f #339 + '195R' // 7d10 #5087 + 'a33O' // 7d11-7d12 #872 + '145E' // 7d13 #3774 + '212D' // 7d14 #5515 + '13B' // 7d15 #339 + '3B' // 7d16 #79 + '189O' // 7d17 #4928 + '95F' // 7d18 #2475 + '222D' // 7d19 #5775 + '235P' // 7d1a #6125 + '196D' // 7d1b #5099 + '33O' // 7d1c #872 + 'a33M' // 7d1d-7d1e #870 + '11H' // 7d1f #293 + '223S' // 7d20 #5816 + '153E' // 7d21 #3982 + '224F' // 7d22 #5829 + '3B' // 7d23 #79 + '11H' // 7d24 #293 + '33N' // 7d25 #871 + '36A' // 7d26 #936 + '2C' // 7d27 #54 + '11H' // 7d28 #293 + '33N' // 7d29 #871 + '36A' // 7d2a #936 + '203V' // 7d2b #5299 + '94P' // 7d2c #2459 + '36A' // 7d2d #936 + '150L' // 7d2e #3911 + '208Z' // 7d2f #5433 + '68N' // 7d30 #1781 + '33M' // 7d31 #870 + '29G' // 7d32 #760 + '148T' // 7d33 #3867 + 'A' // 7d34 + '56K' // 7d35 #1466 + '11H' // 7d36 #293 + 'A' // 7d37 + '33N' // 7d38 #871 + '68N' // 7d39 #1781 + '94S' // 7d3a #2462 + 'A' // 7d3b + '29G' // 7d3c #760 + '3B' // 7d3d #79 + 'a29G' // 7d3e-7d3f #760 + '33M' // 7d40 #870 + '29G' // 7d41 #760 + '223R' // 7d42 #5815 + '56K' // 7d43 #1466 + '237D' // 7d44 #6165 + '94O' // 7d45 #2458 + '125N' // 7d46 #3263 + 'a3B' // 7d47-7d48 #79 + 'aA' // 7d49-7d4a + '36A' // 7d4b #936 + '259P' // 7d4c #6749 + '94N' // 7d4d #2457 + '29G' // 7d4e #760 + '33M' // 7d4f #870 + '243T' // 7d50 #6337 + '36A' // 7d51 #936 + 'A' // 7d52 + '29G' // 7d53 #760 + '33N' // 7d54 #871 + '217F' // 7d55 #5647 + '33M' // 7d56 #870 + '4H' // 7d57 #111 + '11H' // 7d58 #293 + '4H' // 7d59 #111 + '38H' // 7d5a #995 + 'a29E' // 7d5b-7d5c #758 + '29F' // 7d5d #759 + '131D' // 7d5e #3409 + '45C' // 7d5f #1172 + 'A' // 7d60 + '241U' // 7d61 #6286 + '130M' // 7d62 #3392 + '29E' // 7d63 #758 + 'A' // 7d64 + '4H' // 7d65 #111 + '68P' // 7d66 #1783 + '29F' // 7d67 #759 + '179X' // 7d68 #4677 + 'A' // 7d69 + '56J' // 7d6a #1465 + 'a11H' // 7d6b-7d6c #293 + '45C' // 7d6d #1172 + '66B' // 7d6e #1717 + '11H' // 7d6f #293 + '29F' // 7d70 #759 + '68P' // 7d71 #1783 + '224Z' // 7d72 #5849 + '56J' // 7d73 #1465 + 'A' // 7d74 + '258G' // 7d75 #6714 + '258W' // 7d76 #6730 + '11H' // 7d77 #293 + '4H' // 7d78 #111 + '94R' // 7d79 #2461 + '29E' // 7d7a #758 + '29F' // 7d7b #759 + 'A' // 7d7c + '29F' // 7d7d #759 + '11H' // 7d7e #293 + '69Z' // 7d7f #1819 + '45C' // 7d80 #1172 + '178E' // 7d81 #4632 + '4H' // 7d82 #111 + '29F' // 7d83 #759 + '33N' // 7d84 #871 + '49S' // 7d85 #1292 + '29E' // 7d86 #758 + '11H' // 7d87 #293 + 'a29E' // 7d88-7d89 #758 + '11H' // 7d8a #293 + '29E' // 7d8b #758 + '9D' // 7d8c #237 + '18F' // 7d8d #473 + '94V' // 7d8e #2465 + '29D' // 7d8f #757 + 'A' // 7d90 + '18F' // 7d91 #473 + 'A' // 7d92 + '239R' // 7d93 #6231 + '11H' // 7d94 #293 + '22Q' // 7d95 #588 + '18F' // 7d96 #473 + '38H' // 7d97 #995 + '11H' // 7d98 #293 + '257M' // 7d99 #6694 + '259X' // 7d9a #6757 + '4H' // 7d9b #111 + '67Y' // 7d9c #1766 + 'a18F' // 7d9d-7d9e #473 + '49S' // 7d9f #1292 + '67Y' // 7da0 #1766 + 'A' // 7da1 + '29D' // 7da2 #757 + '9D' // 7da3 #237 + '29B' // 7da4 #755 + 'A' // 7da5 + '18F' // 7da6 #473 + '4H' // 7da7 #111 + '29B' // 7da8 #755 + 'A' // 7da9 + '18F' // 7daa #473 + '138Y' // 7dab #3612 + '29D' // 7dac #757 + '227G' // 7dad #5908 + 'b18F' // 7dae-7db0 #473 + '160W' // 7db1 #4182 + '246F' // 7db2 #6401 + '9D' // 7db3 #237 + '142U' // 7db4 #3712 + '29D' // 7db5 #757 + '49S' // 7db6 #1292 + '18F' // 7db7 #473 + '138X' // 7db8 #3611 + '9D' // 7db9 #237 + '155H' // 7dba #4037 + '148A' // 7dbb #3848 + '11H' // 7dbc #293 + '29D' // 7dbd #757 + '130Y' // 7dbe #3404 + '175M' // 7dbf #4562 + '4H' // 7dc0 #111 + '94W' // 7dc1 #2466 + 'a4H' // 7dc2-7dc3 #111 + '18F' // 7dc4 #473 + 'a9D' // 7dc5-7dc6 #237 + '29D' // 7dc7 #757 + '11H' // 7dc8 #293 + 'A' // 7dc9 + '210U' // 7dca #5480 + '141Z' // 7dcb #3691 + 'a18F' // 7dcc-7dcd #473 + '9D' // 7dce #237 + '94U' // 7dcf #2464 + '38H' // 7dd0 #995 + '256X' // 7dd1 #6679 + '191R' // 7dd2 #4983 + '29B' // 7dd3 #755 + '56L' // 7dd4 #1467 + '4H' // 7dd5 #111 + '248W' // 7dd6 #6470 + '13Y' // 7dd7 #362 + '29C' // 7dd8 #756 + '9D' // 7dd9 #237 + '242L' // 7dda #6303 + '29H' // 7ddb #761 + '13Y' // 7ddc #362 + '64Q' // 7ddd #1680 + '122G' // 7dde #3178 + '5C' // 7ddf #132 + '143L' // 7de0 #3729 + '33L' // 7de1 #869 + '19G' // 7de2 #500 + '199W' // 7de3 #5196 + 'a45B' // 7de4-7de5 #1171 + '13Y' // 7de6 #362 + 'A' // 7de7 + '237C' // 7de8 #6164 + '196N' // 7de9 #5109 + 'a19G' // 7dea-7deb #500 + '65R' // 7dec #1707 + '19G' // 7ded #500 + 'A' // 7dee + '160R' // 7def #4177 + '22Q' // 7df0 #588 + 'a13Y' // 7df1-7df2 #362 + '29H' // 7df3 #761 + '212C' // 7df4 #5514 + '45B' // 7df5 #1171 + '9D' // 7df6 #237 + '5C' // 7df7 #132 + 'A' // 7df8 + '145D' // 7df9 #3773 + '4H' // 7dfa #111 + '194N' // 7dfb #5057 + '56L' // 7dfc #1467 + '29B' // 7dfd #755 + '29H' // 7dfe #761 + '5C' // 7dff #132 + '19G' // 7e00 #500 + '256N' // 7e01 #6669 + '5C' // 7e02 #132 + 'A' // 7e03 + '257X' // 7e04 #6705 + '4H' // 7e05 #111 + 'A' // 7e06 + '29B' // 7e07 #755 + '13Y' // 7e08 #362 + 'a29C' // 7e09-7e0a #756 + '13Y' // 7e0b #362 + 'cA' // 7e0c-7e0f + 'a9D' // 7e10-7e11 #237 + '19G' // 7e12 #500 + '22Q' // 7e13 #588 + 'A' // 7e14 + '33L' // 7e15 #869 + 'A' // 7e16 + '4H' // 7e17 #111 + 'bA' // 7e18-7e1a + '130T' // 7e1b #3399 + '4H' // 7e1c #111 + '33L' // 7e1d #869 + '94Q' // 7e1e #2460 + '33L' // 7e1f #869 + '13Y' // 7e20 #362 + '69Z' // 7e21 #1819 + '13Y' // 7e22 #362 + '214K' // 7e23 #5574 + 'A' // 7e24 + '22Q' // 7e25 #588 + '255V' // 7e26 #6651 + '9D' // 7e27 #237 + '4H' // 7e28 #111 + '29H' // 7e29 #761 + 'A' // 7e2a + '175B' // 7e2b #4551 + '4H' // 7e2c #111 + '9D' // 7e2d #237 + '204H' // 7e2e #5311 + '29C' // 7e2f #756 + '22Q' // 7e30 #588 + '170V' // 7e31 #4441 + 'a9D' // 7e32-7e33 #237 + '22Q' // 7e34 #588 + 'a9D' // 7e35-7e36 #237 + '29C' // 7e37 #756 + 'A' // 7e38 + '13Y' // 7e39 #362 + '19G' // 7e3a #500 + '13Y' // 7e3b #362 + '5C' // 7e3c #132 + '232I' // 7e3d #6040 + '198K' // 7e3e #5158 + '4H' // 7e3f #111 + '5C' // 7e40 #132 + '215W' // 7e41 #5612 + 'A' // 7e42 + '132V' // 7e43 #3453 + '13Y' // 7e44 #362 + '9D' // 7e45 #237 + '145C' // 7e46 #3772 + '29C' // 7e47 #756 + '9D' // 7e48 #237 + 'A' // 7e49 + '254W' // 7e4a #6626 + '256E' // 7e4b #6660 + 'A' // 7e4c + '254C' // 7e4d #6606 + '19G' // 7e4e #500 + 'A' // 7e4f + '9D' // 7e50 #237 + '29H' // 7e51 #761 + '33L' // 7e52 #869 + 'A' // 7e53 + '211P' // 7e54 #5501 + '142B' // 7e55 #3693 + '13Y' // 7e56 #362 + 'A' // 7e57 + 'b13Y' // 7e58-7e5a #362 + '29H' // 7e5b #761 + 'A' // 7e5c + '19G' // 7e5d #500 + '66R' // 7e5e #1733 + '4H' // 7e5f #111 + 'A' // 7e60 + '157C' // 7e61 #4084 + '9D' // 7e62 #237 + 'aA' // 7e63-7e64 + '38H' // 7e65 #995 + '19G' // 7e66 #500 + '45B' // 7e67 #1171 + '22Q' // 7e68 #588 + '66R' // 7e69 #1733 + '192O' // 7e6a #5006 + '207A' // 7e6b #5382 + '29B' // 7e6c #755 + '29C' // 7e6d #756 + 'a9D' // 7e6e-7e6f #237 + '94T' // 7e70 #2463 + 'aA' // 7e71-7e72 + '178D' // 7e73 #4631 + 'A' // 7e74 + '4H' // 7e75 #111 + '22Q' // 7e76 #588 + '5C' // 7e77 #132 + '33K' // 7e78 #868 + '65R' // 7e79 #1707 + 'A' // 7e7a + '29A' // 7e7b #754 + '217E' // 7e7c #5646 + '157D' // 7e7d #4085 + '33K' // 7e7e #868 + '56H' // 7e7f #1463 + 'A' // 7e80 + '29A' // 7e81 #754 + '38G' // 7e82 #994 + '4H' // 7e83 #111 + 'aA' // 7e84-7e85 + 'b33K' // 7e86-7e88 #868 + '4H' // 7e89 #111 + '33K' // 7e8a #868 + 'A' // 7e8b + '224Y' // 7e8c #5848 + '29A' // 7e8d #754 + '33K' // 7e8e #868 + '159Y' // 7e8f #4158 + 'a4H' // 7e90-7e91 #111 + '29A' // 7e92 #754 + '38G' // 7e93 #994 + '29A' // 7e94 #754 + '4H' // 7e95 #111 + '185K' // 7e96 #4820 + 'A' // 7e97 + '38G' // 7e98 #994 + '94M' // 7e99 #2456 + '29A' // 7e9a #754 + '38G' // 7e9b #994 + '64Q' // 7e9c #1680 + '4H' // 7e9d #111 + '19G' // 7e9e #500 + '94E' // 7e9f #2448 + '3Y' // 7ea0 #102 + '5C' // 7ea1 #132 + '1Z' // 7ea2 #51 + '5C' // 7ea3 #132 + '94K' // 7ea4 #2454 + '5C' // 7ea5 #132 + '1Z' // 7ea6 #51 + '3Q' // 7ea7 #94 + 'a5C' // 7ea8-7ea9 #132 + '2D' // 7eaa #55 + '5C' // 7eab #132 + '94H' // 7eac #2451 + 'a5C' // 7ead-7eae #132 + '2C' // 7eaf #54 + '5C' // 7eb0 #132 + '1R' // 7eb1 #43 + '2R' // 7eb2 #69 + '2C' // 7eb3 #54 + 'A' // 7eb4 + '4C' // 7eb5 #106 + '2W' // 7eb6 #74 + '1R' // 7eb7 #43 + '2C' // 7eb8 #54 + '3Y' // 7eb9 #102 + '94J' // 7eba #2453 + '5C' // 7ebb #132 + 'A' // 7ebc + '4C' // 7ebd #106 + '5C' // 7ebe #132 + '7K' // 7ebf #192 + 'b5C' // 7ec0-7ec2 #132 + '3I' // 7ec3 #86 + '1Z' // 7ec4 #51 + '3X' // 7ec5 #101 + '1Z' // 7ec6 #51 + '94L' // 7ec7 #2455 + '2D' // 7ec8 #55 + '5C' // 7ec9 #132 + '2W' // 7eca #74 + 'a5C' // 7ecb-7ecc #132 + '1Z' // 7ecd #51 + '2Y' // 7ece #76 + '35T' // 7ecf #929 + '5C' // 7ed0 #132 + '1R' // 7ed1 #43 + '2Y' // 7ed2 #76 + '3Q' // 7ed3 #94 + '5C' // 7ed4 #132 + '1R' // 7ed5 #43 + 'A' // 7ed6 + '5C' // 7ed7 #132 + '3Y' // 7ed8 #102 + '3Q' // 7ed9 #94 + '3G' // 7eda #84 + '5C' // 7edb #132 + '1Z' // 7edc #51 + '2D' // 7edd #55 + '3G' // 7ede #84 + '155T' // 7edf #4049 + 'b5C' // 7ee0-7ee2 #132 + '2Y' // 7ee3 #76 + 'A' // 7ee4 + 'a5C' // 7ee5-7ee6 #132 + '2C' // 7ee7 #54 + '5C' // 7ee8 #132 + '3N' // 7ee9 #91 + '2L' // 7eea #63 + '5C' // 7eeb #132 + 'A' // 7eec + '2D' // 7eed #55 + '3X' // 7eee #101 + '2K' // 7eef #62 + 'b5X' // 7ef0-7ef2 #153 + '2Y' // 7ef3 #76 + '1Z' // 7ef4 #51 + '2L' // 7ef5 #63 + '5X' // 7ef6 #153 + '2W' // 7ef7 #74 + '2K' // 7ef8 #62 + 'A' // 7ef9 + 'a5X' // 7efa-7efb #153 + '2D' // 7efc #55 + '2K' // 7efd #62 + '5X' // 7efe #153 + '2C' // 7eff #54 + '2K' // 7f00 #62 + 'c5X' // 7f01-7f04 #153 + '2R' // 7f05 #69 + '94I' // 7f06 #2452 + '2W' // 7f07 #74 + '5X' // 7f08 #153 + '2W' // 7f09 #74 + 'h5X' // 7f0a-7f12 #153 + '3N' // 7f13 #91 + '3X' // 7f14 #101 + '2W' // 7f15 #74 + '3Q' // 7f16 #94 + '5X' // 7f17 #153 + '3Y' // 7f18 #102 + '5X' // 7f19 #153 + '3X' // 7f1a #101 + 'a5X' // 7f1b-7f1c #153 + '2L' // 7f1d #63 + 'A' // 7f1e + '5X' // 7f1f #153 + '2R' // 7f20 #69 + 'b5X' // 7f21-7f23 #153 + '3G' // 7f24 #84 + 'c5X' // 7f25-7f28 #153 + '3Y' // 7f29 #102 + 'i5X' // 7f2a-7f33 #153 + '2R' // 7f34 #69 + '5X' // 7f35 #153 + '94G' // 7f36 #2450 + '44Z' // 7f37 #1169 + '66B' // 7f38 #1717 + 'A' // 7f39 + '207B' // 7f3a #5383 + 'a4H' // 7f3b-7f3c #111 + '16P' // 7f3d #431 + 'a4H' // 7f3e-7f3f #111 + 'a93Z' // 7f40-7f41 #2443 + '5X' // 7f42 #153 + '44Y' // 7f43 #1168 + 'a16P' // 7f44-7f45 #431 + 'A' // 7f46 + '44Y' // 7f47 #1168 + 'c45A' // 7f48-7f4b #1170 + '56I' // 7f4c #1464 + '16P' // 7f4d #431 + '44Y' // 7f4e #1168 + '4H' // 7f4f #111 + '178C' // 7f50 #4630 + '187Z' // 7f51 #4887 + '16P' // 7f52 #431 + '56H' // 7f53 #1463 + '56I' // 7f54 #1464 + '163T' // 7f55 #4257 + 'A' // 7f56 + '2C' // 7f57 #54 + '16P' // 7f58 #431 + '5X' // 7f59 #153 + '4C' // 7f5a #106 + 'a4H' // 7f5b-7f5c #111 + '16P' // 7f5d #431 + 'A' // 7f5e + '16P' // 7f5f #431 + '94F' // 7f60 #2449 + '16P' // 7f61 #431 + '2R' // 7f62 #69 + '16P' // 7f63 #431 + '4H' // 7f64 #111 + '16P' // 7f65 #431 + 'a4H' // 7f66-7f67 #111 + '16P' // 7f68 #431 + '185L' // 7f69 #4821 + '197R' // 7f6a #5139 + '94C' // 7f6b #2446 + 'A' // 7f6c + '4H' // 7f6d #111 + '230V' // 7f6e #6001 + 'A' // 7f6f + '181N' // 7f70 #4719 + '94D' // 7f71 #2447 + '188Z' // 7f72 #4913 + 'a5X' // 7f73-7f74 #153 + '180N' // 7f75 #4693 + 'A' // 7f76 + '66A' // 7f77 #1716 + '28Z' // 7f78 #753 + '150K' // 7f79 #3910 + 'bA' // 7f7a-7f7c + 'a25J' // 7f7d-7f7e #659 + 'a1V' // 7f7f-7f80 #47 + '2W' // 7f81 #74 + '1V' // 7f82 #47 + '28Z' // 7f83 #753 + 'A' // 7f84 + '219C' // 7f85 #5696 + '25J' // 7f86 #659 + '28Z' // 7f87 #753 + '122F' // 7f88 #3177 + '5X' // 7f89 #153 + '196S' // 7f8a #5114 + '25J' // 7f8b #659 + '44X' // 7f8c #1167 + '28Z' // 7f8d #753 + '244R' // 7f8e #6361 + '94A' // 7f8f #2444 + '41N' // 7f90 #1079 + '25J' // 7f91 #659 + 'A' // 7f92 + '45A' // 7f93 #1170 + '44X' // 7f94 #1167 + '44Z' // 7f95 #1169 + '41N' // 7f96 #1079 + '28Z' // 7f97 #753 + 'a5X' // 7f98-7f99 #153 + '44X' // 7f9a #1167 + '5X' // 7f9b #153 + '1V' // 7f9c #47 + '25J' // 7f9d #659 + '173M' // 7f9e #4510 + '5X' // 7f9f #153 + 'A' // 7fa0 + '94B' // 7fa1 #2445 + '28Z' // 7fa2 #753 + '25J' // 7fa3 #659 + '229E' // 7fa4 #5958 + '45A' // 7fa5 #1170 + '1V' // 7fa6 #47 + '44Z' // 7fa7 #1169 + '160Q' // 7fa8 #4176 + '228W' // 7fa9 #5950 + '1V' // 7faa #47 + 'A' // 7fab + '5X' // 7fac #153 + 'b25J' // 7fad-7faf #659 + 'a93X' // 7fb0-7fb1 #2441 + '93J' // 7fb2 #2427 + 'A' // 7fb3 + '56F' // 7fb4 #1461 + 'A' // 7fb5 + '56F' // 7fb6 #1461 + 'A' // 7fb7 + '25H' // 7fb8 #657 + '127W' // 7fb9 #3324 + 'aA' // 7fba-7fbb + '11Y' // 7fbc #310 + '198T' // 7fbd #5167 + 'A' // 7fbe + 'a11Y' // 7fbf-7fc0 #310 + '172U' // 7fc1 #4492 + 'A' // 7fc2 + '11Y' // 7fc3 #310 + 'A' // 7fc4 + '157A' // 7fc5 #4082 + '1V' // 7fc6 #47 + 'A' // 7fc7 + '1V' // 7fc8 #47 + 'A' // 7fc9 + '127V' // 7fca #3323 + '25I' // 7fcb #658 + '93U' // 7fcc #2438 + 'A' // 7fcd + '25H' // 7fce #657 + '13X' // 7fcf #361 + 'A' // 7fd0 + '25I' // 7fd1 #658 + '229R' // 7fd2 #5971 + 'A' // 7fd3 + '190B' // 7fd4 #4941 + '25H' // 7fd5 #657 + 'aA' // 7fd6-7fd7 + '2R' // 7fd8 #69 + 'a5X' // 7fd9-7fda #153 + '11Y' // 7fdb #310 + 'A' // 7fdc + '33J' // 7fdd #867 + '25I' // 7fde #658 + '25H' // 7fdf #657 + '187X' // 7fe0 #4885 + '156Z' // 7fe1 #4081 + 'A' // 7fe2 + '41N' // 7fe3 #1079 + 'A' // 7fe4 + 'a11Y' // 7fe5-7fe6 #310 + '93W' // 7fe7 #2440 + '1V' // 7fe8 #47 + '25H' // 7fe9 #657 + 'A' // 7fea + '25H' // 7feb #657 + '11Y' // 7fec #310 + 'A' // 7fed + '11Y' // 7fee #310 + '41N' // 7fef #1079 + '163S' // 7ff0 #4256 + '137M' // 7ff1 #3574 + '13X' // 7ff2 #361 + '11Y' // 7ff3 #310 + '5X' // 7ff4 #153 + 'cA' // 7ff5-7ff8 + '66A' // 7ff9 #1716 + '11Y' // 7ffa #310 + '218Z' // 7ffb #5693 + '175P' // 7ffc #4565 + '13X' // 7ffd #361 + '11Y' // 7ffe #310 + '1V' // 7fff #47 + '194K' // 8000 #5054 + '234T' // 8001 #6103 + '13X' // 8002 #361 + '231F' // 8003 #6011 + '11Y' // 8004 #310 + '35V' // 8005 #931 + '25H' // 8006 #657 + '1V' // 8007 #47 + '22P' // 8008 #587 + '71C' // 8009 #1848 + '1V' // 800a #47 + '11Y' // 800b #310 + '232H' // 800c #6039 + '157B' // 800d #4083 + '11Y' // 800e #310 + '1V' // 800f #47 + '204I' // 8010 #5312 + 'a11Y' // 8011-8012 #310 + '1V' // 8013 #47 + '11Y' // 8014 #310 + '160I' // 8015 #4168 + '4Z' // 8016 #129 + '188I' // 8017 #4896 + '93I' // 8018 #2426 + '4Z' // 8019 #129 + 'aA' // 801a-801b + '4Z' // 801c #129 + '22P' // 801d #587 + '26Z' // 801e #701 + '1V' // 801f #47 + '22P' // 8020 #587 + '1V' // 8021 #47 + 'aA' // 8022-8023 + '13X' // 8024 #361 + '33J' // 8025 #867 + '4Z' // 8026 #129 + '6G' // 8027 #162 + '4Z' // 8028 #129 + 'a6G' // 8029-802a #162 + 'A' // 802b + '13X' // 802c #361 + '71C' // 802d #1848 + '22P' // 802e #587 + '33J' // 802f #867 + '13X' // 8030 #361 + '28Y' // 8031 #752 + 'A' // 8032 + '210R' // 8033 #5477 + '26Z' // 8034 #701 + '4Z' // 8035 #129 + '195G' // 8036 #5076 + '4Z' // 8037 #129 + '2W' // 8038 #74 + '13X' // 8039 #361 + '1V' // 803a #47 + '93L' // 803b #2429 + '22P' // 803c #587 + '127U' // 803d #3322 + '1V' // 803e #47 + '64P' // 803f #1679 + '1V' // 8040 #47 + 'A' // 8041 + '3W' // 8042 #100 + '18E' // 8043 #472 + '1V' // 8044 #47 + 'A' // 8045 + '64P' // 8046 #1679 + 'bA' // 8047-8049 + '206Y' // 804a #5380 + '6G' // 804b #162 + '2D' // 804c #55 + '6G' // 804d #162 + 'cA' // 804e-8051 + '4Z' // 8052 #129 + 'A' // 8053 + '49C' // 8054 #1276 + 'A' // 8055 + '215J' // 8056 #5599 + 'A' // 8057 + '178B' // 8058 #4629 + '6G' // 8059 #162 + '206Z' // 805a #5381 + '33J' // 805b #867 + 'aA' // 805c-805d + '236I' // 805e #6144 + 'a1V' // 805f-8060 #47 + '93R' // 8061 #2435 + '22P' // 8062 #587 + '33J' // 8063 #867 + '1V' // 8064 #47 + 'A' // 8065 + '22P' // 8066 #587 + 'A' // 8067 + '26Z' // 8068 #701 + '6G' // 8069 #162 + '2L' // 806a #63 + 'aA' // 806b-806c + '1V' // 806d #47 + '6G' // 806e #162 + '239Q' // 806f #6230 + '178A' // 8070 #4628 + '4Z' // 8071 #129 + '232G' // 8072 #6038 + '18E' // 8073 #472 + '257Q' // 8074 #6698 + '13X' // 8075 #361 + '4Z' // 8076 #129 + '223B' // 8077 #5799 + '6G' // 8078 #162 + '13X' // 8079 #361 + 'A' // 807a + '1V' // 807b #47 + '6G' // 807c #162 + '224X' // 807d #5847 + 'a18E' // 807e-807f #472 + '93K' // 8080 #2428 + '1V' // 8081 #47 + '6G' // 8082 #162 + '4C' // 8083 #106 + '18E' // 8084 #472 + '145B' // 8085 #3771 + '150I' // 8086 #3908 + '138V' // 8087 #3609 + '26Z' // 8088 #701 + '222B' // 8089 #5773 + 'A' // 808a + '132T' // 808b #3451 + '204Q' // 808c #5320 + 'A' // 808d + '1V' // 808e #47 + '20G' // 808f #526 + 'bA' // 8090-8092 + '4Z' // 8093 #129 + 'A' // 8094 + '25I' // 8095 #658 + '173R' // 8096 #4515 + 'A' // 8097 + '93O' // 8098 #2432 + '1V' // 8099 #47 + '192N' // 809a #5005 + '136M' // 809b #3548 + '4Z' // 809c #129 + '175G' // 809d #4556 + '1V' // 809e #47 + '20G' // 809f #526 + '4C' // 80a0 #106 + '227D' // 80a1 #5905 + '161P' // 80a2 #4201 + 'A' // 80a3 + '248V' // 80a4 #6469 + '202G' // 80a5 #5258 + '1V' // 80a6 #47 + '13X' // 80a7 #361 + 'A' // 80a8 + '190L' // 80a9 #4951 + '161C' // 80aa #4188 + '4Z' // 80ab #129 + '1V' // 80ac #47 + '4Z' // 80ad #129 + '20G' // 80ae #526 + '201H' // 80af #5233 + 'A' // 80b0 + '18E' // 80b1 #472 + '230O' // 80b2 #5994 + 'A' // 80b3 + '93Q' // 80b4 #2434 + '25I' // 80b5 #658 + '20G' // 80b6 #526 + '28Y' // 80b7 #752 + '4Z' // 80b8 #129 + '1V' // 80b9 #47 + '160A' // 80ba #4160 + 'A' // 80bb + 'a20G' // 80bc-80bd #526 + '2R' // 80be #69 + '2L' // 80bf #63 + 'a2R' // 80c0-80c1 #69 + '20G' // 80c2 #526 + '182G' // 80c3 #4738 + '18E' // 80c4 #472 + '1V' // 80c5 #47 + '93S' // 80c6 #2436 + '25I' // 80c7 #658 + '1V' // 80c8 #47 + 'A' // 80c9 + '26Z' // 80ca #701 + 'A' // 80cb + '220Z' // 80cc #5745 + '4Z' // 80cd #129 + '194X' // 80ce #5067 + '13X' // 80cf #361 + 'A' // 80d0 + '6G' // 80d1 #162 + '1V' // 80d2 #47 + 'A' // 80d3 + '4Z' // 80d4 #129 + '1V' // 80d5 #47 + '185J' // 80d6 #4819 + '4Z' // 80d7 #129 + '1V' // 80d8 #47 + '4Z' // 80d9 #129 + '122E' // 80da #3176 + '18E' // 80db #472 + '93Y' // 80dc #2442 + '4Z' // 80dd #129 + '174S' // 80de #4542 + 'A' // 80df + '4Z' // 80e0 #129 + '195J' // 80e1 #5079 + 'A' // 80e2 + '25I' // 80e3 #658 + 'a18E' // 80e4-80e5 #472 + '1V' // 80e6 #47 + 'a6G' // 80e7-80e8 #162 + '28Y' // 80e9 #752 + 'a6G' // 80ea-80eb #162 + '20G' // 80ec #526 + '4Z' // 80ed #129 + '1V' // 80ee #47 + 'a4Z' // 80ef-80f0 #129 + '18E' // 80f1 #472 + '1V' // 80f2 #47 + '4Z' // 80f3 #129 + '93P' // 80f4 #2433 + '1V' // 80f5 #47 + '93N' // 80f6 #2431 + '1V' // 80f7 #47 + '198D' // 80f8 #5151 + '1V' // 80f9 #47 + '132U' // 80fa #3452 + '1V' // 80fb #47 + '4Z' // 80fc #129 + '244W' // 80fd #6366 + '13X' // 80fe #361 + 'aA' // 80ff-8100 + '4Z' // 8101 #129 + '190F' // 8102 #4945 + '22P' // 8103 #587 + '6G' // 8104 #162 + '181M' // 8105 #4718 + '180S' // 8106 #4698 + '93T' // 8107 #2437 + '175F' // 8108 #4555 + '93M' // 8109 #2430 + '152Y' // 810a #3976 + '1V' // 810b #47 + '28Y' // 810c #752 + '26Z' // 810d #701 + '28Y' // 810e #752 + '4C' // 810f #106 + '2W' // 8110 #74 + '2D' // 8111 #55 + '20G' // 8112 #526 + '6G' // 8113 #162 + '28Y' // 8114 #752 + '20G' // 8115 #526 + '138W' // 8116 #3610 + '4Z' // 8117 #129 + '18E' // 8118 #472 + '6G' // 8119 #162 + '93V' // 811a #2439 + '56D' // 811b #1459 + '26Z' // 811c #701 + '6G' // 811d #162 + '56E' // 811e #1460 + '6G' // 811f #162 + '1V' // 8120 #47 + 'a56G' // 8121-8122 #1462 + '56D' // 8123 #1459 + '56E' // 8124 #1460 + '56G' // 8125 #1462 + '6G' // 8126 #162 + '38E' // 8127 #992 + '6G' // 8128 #162 + '56A' // 8129 #1456 + '44W' // 812a #1166 + '199V' // 812b #5195 + '38E' // 812c #992 + 'a6G' // 812d-812e #162 + '56A' // 812f #1456 + '38E' // 8130 #992 + '93F' // 8131 #2423 + '28X' // 8132 #751 + '256H' // 8133 #6663 + '28X' // 8134 #751 + '1V' // 8135 #47 + 'A' // 8136 + '28X' // 8137 #751 + '3I' // 8138 #86 + '150H' // 8139 #3907 + '56B' // 813a #1457 + 'A' // 813b + '1V' // 813c #47 + '56B' // 813d #1457 + '150J' // 813e #3909 + 'a6G' // 813f-8140 #162 + '1V' // 8141 #47 + '44W' // 8142 #1166 + 'A' // 8143 + '25G' // 8144 #656 + '1V' // 8145 #47 + '38E' // 8146 #992 + '1V' // 8147 #47 + '28X' // 8148 #751 + 'A' // 8149 + '92X' // 814a #2415 + '124O' // 814b #3238 + '92W' // 814c #2414 + '28X' // 814d #751 + '159X' // 814e #4157 + 'A' // 814f + '190A' // 8150 #4940 + '92T' // 8151 #2411 + '2J' // 8152 #61 + '55Z' // 8153 #1455 + '166S' // 8154 #4334 + '176S' // 8155 #4594 + '25G' // 8156 #656 + '2J' // 8157 #61 + '6G' // 8158 #162 + '44W' // 8159 #1166 + '28X' // 815a #751 + 'bA' // 815b-815d + '6G' // 815e #162 + '2J' // 815f #61 + '55Z' // 8160 #1455 + '2J' // 8161 #61 + 'bA' // 8162-8164 + '144Z' // 8165 #3769 + '224V' // 8166 #5845 + '4O' // 8167 #118 + '21H' // 8168 #553 + '4O' // 8169 #118 + 'A' // 816a + '174O' // 816b #4538 + '38D' // 816c #991 + '4O' // 816d #118 + '127T' // 816e #3321 + '11X' // 816f #309 + '197N' // 8170 #5135 + '16O' // 8171 #430 + 'A' // 8172 + '214J' // 8173 #5573 + '4O' // 8174 #118 + 'aA' // 8175-8176 + '2J' // 8177 #61 + '189W' // 8178 #4936 + '183O' // 8179 #4772 + '153W' // 817a #4000 + '2Y' // 817b #76 + '28W' // 817c #750 + '38F' // 817d #993 + '2D' // 817e #55 + '201D' // 817f #5229 + '156X' // 8180 #4079 + '2J' // 8181 #61 + '4O' // 8182 #118 + '2J' // 8183 #61 + '20F' // 8184 #525 + 'a2J' // 8185-8186 #61 + 'A' // 8187 + '16O' // 8188 #430 + 'A' // 8189 + '55Y' // 818a #1454 + '2J' // 818b #61 + 'aA' // 818c-818d + '2J' // 818e #61 + '180K' // 818f #4690 + '2J' // 8190 #61 + '16G' // 8191 #422 + 'A' // 8192 + '20F' // 8193 #525 + 'A' // 8194 + '11X' // 8195 #309 + '2J' // 8196 #61 + 'A' // 8197 + '4O' // 8198 #118 + '16G' // 8199 #422 + '202L' // 819a #5263 + '4O' // 819b #118 + '195Q' // 819c #5086 + '161B' // 819d #4187 + '11X' // 819e #309 + 'A' // 819f + '206W' // 81a0 #5378 + 'A' // 81a1 + '2J' // 81a2 #61 + '93C' // 81a3 #2420 + '2J' // 81a4 #61 + '38F' // 81a5 #993 + '28W' // 81a6 #750 + '25G' // 81a7 #656 + '142Z' // 81a8 #3717 + '177Z' // 81a9 #4627 + '38D' // 81aa #991 + '25G' // 81ab #656 + 'aA' // 81ac-81ad + '2J' // 81ae #61 + 'A' // 81af + '11X' // 81b0 #309 + 'A' // 81b1 + '2J' // 81b2 #61 + '154A' // 81b3 #4004 + '2J' // 81b4 #61 + '33I' // 81b5 #866 + '28W' // 81b6 #750 + 'A' // 81b7 + '2J' // 81b8 #61 + 'A' // 81b9 + '16O' // 81ba #430 + '4O' // 81bb #118 + 'A' // 81bc + '185I' // 81bd #4818 + '33I' // 81be #866 + '92Z' // 81bf #2417 + '156Y' // 81c0 #4080 + '92V' // 81c1 #2413 + '163Q' // 81c2 #4254 + '4O' // 81c3 #118 + 'A' // 81c4 + '2J' // 81c5 #61 + '16O' // 81c6 #430 + 'A' // 81c7 + '20F' // 81c8 #525 + '217D' // 81c9 #5645 + '4O' // 81ca #118 + '2J' // 81cb #61 + '28W' // 81cc #750 + '122B' // 81cd #3173 + '2J' // 81ce #61 + '11X' // 81cf #309 + 'A' // 81d0 + '4O' // 81d1 #118 + '25G' // 81d2 #656 + '254Z' // 81d3 #6629 + '16G' // 81d4 #422 + 'a2J' // 81d5-81d6 #61 + '11X' // 81d7 #309 + '66G' // 81d8 #1722 + 'a11X' // 81d9-81da #309 + '21H' // 81db #553 + '16G' // 81dc #422 + '11X' // 81dd #309 + '4O' // 81de #118 + '170T' // 81df #4439 + 'a11X' // 81e0-81e1 #309 + '25G' // 81e2 #656 + '167U' // 81e3 #4362 + '20F' // 81e4 #525 + '66G' // 81e5 #1722 + 'A' // 81e6 + '16O' // 81e7 #430 + '210I' // 81e8 #5468 + '16G' // 81e9 #422 + '68Z' // 81ea #1793 + '21H' // 81eb #553 + '4O' // 81ec #118 + '182Y' // 81ed #4756 + '16G' // 81ee #422 + '4O' // 81ef #118 + 'b2J' // 81f0-81f2 #61 + '233V' // 81f3 #6079 + '222C' // 81f4 #5774 + '21H' // 81f5 #553 + '20F' // 81f6 #525 + 'A' // 81f7 + '21H' // 81f8 #553 + '2J' // 81f9 #61 + '68B' // 81fa #1769 + '144Y' // 81fb #3768 + '56C' // 81fc #1458 + '2J' // 81fd #61 + '16O' // 81fe #430 + '2J' // 81ff #61 + 'b4O' // 8200-8202 #118 + '2J' // 8203 #61 + '4O' // 8204 #118 + '55Y' // 8205 #1454 + '2R' // 8206 #69 + '240I' // 8207 #6248 + '229D' // 8208 #5957 + '224W' // 8209 #5846 + '68B' // 820a #1769 + '11X' // 820b #309 + '181T' // 820c #4725 + '177Y' // 820d #4626 + '256D' // 820e #6659 + '21H' // 820f #553 + '93E' // 8210 #2422 + 'A' // 8211 + '206X' // 8212 #5379 + '2J' // 8213 #61 + '138U' // 8214 #3608 + '25G' // 8215 #656 + '192M' // 8216 #5004 + '258Y' // 8217 #6732 + '93A' // 8218 #2418 + '2J' // 8219 #61 + '20F' // 821a #525 + '16O' // 821b #430 + '138T' // 821c #3607 + '11X' // 821d #309 + '212L' // 821e #5523 + '154D' // 821f #4007 + 'A' // 8220 + '16O' // 8221 #430 + '4O' // 8222 #118 + 'a16G' // 8223-8224 #422 + 'A' // 8225 + '38F' // 8226 #993 + '16G' // 8227 #422 + '4O' // 8228 #118 + '11X' // 8229 #309 + '215P' // 822a #5605 + '16O' // 822b #430 + '222R' // 822c #5789 + '38F' // 822d #993 + '2J' // 822e #61 + '28W' // 822f #750 + '1R' // 8230 #43 + '3X' // 8231 #101 + 'b4O' // 8232-8234 #118 + '122C' // 8235 #3174 + '93B' // 8236 #2419 + '16O' // 8237 #430 + '4O' // 8238 #118 + '198J' // 8239 #5157 + '4O' // 823a #118 + '16G' // 823b #422 + '2J' // 823c #61 + 'A' // 823d + '28W' // 823e #750 + 'A' // 823f + '33I' // 8240 #866 + 'aA' // 8241-8242 + '2J' // 8243 #61 + '4O' // 8244 #118 + '33I' // 8245 #866 + '2J' // 8246 #61 + '153G' // 8247 #3984 + 'A' // 8248 + '4O' // 8249 #118 + 'A' // 824a + '4O' // 824b #118 + 'aA' // 824c-824d + '11X' // 824e #309 + '4O' // 824f #118 + 'A' // 8250 + '2J' // 8251 #61 + 'aA' // 8252-8253 + '38D' // 8254 #991 + 'A' // 8255 + '2J' // 8256 #61 + '11X' // 8257 #309 + '122D' // 8258 #3175 + '65Q' // 8259 #1706 + '4O' // 825a #118 + 'A' // 825b + 'a2J' // 825c-825d #61 + 'A' // 825e + '4O' // 825f #118 + '2J' // 8260 #61 + 'A' // 8261 + '20F' // 8262 #525 + '2J' // 8263 #61 + '33I' // 8264 #866 + '38D' // 8265 #991 + '196W' // 8266 #5118 + '2J' // 8267 #61 + '4O' // 8268 #118 + 'A' // 8269 + '2J' // 826a #61 + '11X' // 826b #309 + 'A' // 826c + '2J' // 826d #61 + '16O' // 826e #430 + '223Z' // 826f #5823 + '2R' // 8270 #69 + '65Q' // 8271 #1706 + '243F' // 8272 #6323 + '3N' // 8273 #91 + '2J' // 8274 #61 + 'A' // 8275 + '93D' // 8276 #2421 + '170U' // 8277 #4440 + '56C' // 8278 #1458 + '4O' // 8279 #118 + '64L' // 827a #1675 + '20F' // 827b #525 + 'A' // 827c + '25F' // 827d #655 + '192L' // 827e #5003 + '25F' // 827f #655 + 'a2J' // 8280-8281 #61 + '3Q' // 8282 #94 + 'a25F' // 8283-8284 #655 + 'aA' // 8285-8286 + '20F' // 8287 #525 + '16G' // 8288 #422 + '21H' // 8289 #553 + '25F' // 828a #655 + '160U' // 828b #4180 + 'A' // 828c + 'a92S' // 828d-828e #2410 + 'a93G' // 828f-8290 #2424 + '25F' // 8291 #655 + '163R' // 8292 #4255 + 'a25F' // 8293-8294 #655 + 'A' // 8295 + '21H' // 8296 #553 + '16G' // 8297 #422 + '25F' // 8298 #655 + '163P' // 8299 #4253 + '92U' // 829a #2412 + '92Y' // 829b #2416 + '2K' // 829c #62 + '189X' // 829d #4937 + '93H' // 829e #2425 + '92H' // 829f #2399 + 'a28U' // 82a0-82a1 #748 + '28V' // 82a2 #749 + 'a28U' // 82a3-82a4 #748 + '129W' // 82a5 #3376 + '92O' // 82a6 #2406 + 'a28U' // 82a7-82a8 #748 + '55U' // 82a9 #1450 + 'a28U' // 82aa-82ab #748 + '185H' // 82ac #4817 + '159L' // 82ad #4145 + '55U' // 82ae #1450 + '160N' // 82af #4173 + '28U' // 82b0 #748 + '68S' // 82b1 #1786 + '21H' // 82b2 #553 + '196G' // 82b3 #5102 + '28U' // 82b4 #748 + '16G' // 82b5 #422 + '92Q' // 82b6 #2408 + '127S' // 82b7 #3320 + '149N' // 82b8 #3887 + '125F' // 82b9 #3255 + '92J' // 82ba #2401 + '92G' // 82bb #2398 + '28S' // 82bc #746 + '175E' // 82bd #4554 + '28T' // 82be #747 + '92E' // 82bf #2396 + 'b5B' // 82c0-82c2 #131 + 'A' // 82c3 + '18D' // 82c4 #471 + 'a248U' // 82c5-82c6 #6468 + 'a5B' // 82c7-82c8 #131 + 'A' // 82c9 + '33H' // 82ca #865 + 'a5B' // 82cb-82cc #131 + '2L' // 82cd #63 + '5B' // 82ce #131 + '55X' // 82cf #1453 + '28T' // 82d0 #747 + '181E' // 82d1 #4710 + '28S' // 82d2 #746 + '145A' // 82d3 #3770 + '142G' // 82d4 #3698 + '28S' // 82d5 #746 + '18D' // 82d6 #471 + '202D' // 82d7 #5255 + '33H' // 82d8 #865 + '28T' // 82d9 #747 + '2J' // 82da #61 + '92K' // 82db #2402 + '28T' // 82dc #747 + '5B' // 82dd #131 + '28S' // 82de #746 + '92F' // 82df #2397 + '28T' // 82e0 #747 + '28S' // 82e1 #746 + '92I' // 82e2 #2400 + 'a28T' // 82e3-82e4 #747 + '229H' // 82e5 #5961 + '212H' // 82e6 #5519 + '28S' // 82e7 #746 + '3A' // 82e8 #78 + '5B' // 82e9 #131 + '8W' // 82ea #230 + '92L' // 82eb #2403 + 'A' // 82ec + '41M' // 82ed #1078 + '33H' // 82ee #865 + '92B' // 82ef #2393 + '28V' // 82f0 #749 + '235K' // 82f1 #6120 + 'A' // 82f2 + 'a8W' // 82f3-82f4 #230 + 'A' // 82f5 + '22O' // 82f6 #586 + '8W' // 82f7 #230 + '33H' // 82f8 #865 + '92C' // 82f9 #2394 + '92M' // 82fa #2404 + '8W' // 82fb #230 + '18D' // 82fc #471 + '91Z' // 82fd #2391 + '18C' // 82fe #470 + '18D' // 82ff #471 + '8W' // 8300 #230 + '18C' // 8301 #470 + '168G' // 8302 #4374 + '185F' // 8303 #4815 + '166F' // 8304 #4321 + '142P' // 8305 #3707 + 'b8W' // 8306-8308 #230 + '136O' // 8309 #3550 + '3A' // 830a #78 + '22O' // 830b #586 + '8W' // 830c #230 + '18D' // 830d #471 + '253Q' // 830e #6594 + '5B' // 830f #131 + 'A' // 8310 + '5B' // 8311 #131 + 'A' // 8312 + 'b5B' // 8313-8315 #131 + '22O' // 8316 #586 + '127Q' // 8317 #3318 + '8W' // 8318 #230 + 'A' // 8319 + '18D' // 831a #471 + '8W' // 831b #230 + '142H' // 831c #3699 + '8W' // 831d #230 + '22O' // 831e #586 + '3A' // 831f #78 + 'A' // 8320 + 'b3A' // 8321-8323 #78 + '5B' // 8324 #131 + 'aA' // 8325-8326 + '18D' // 8327 #471 + '137E' // 8328 #3566 + 'A' // 8329 + '18D' // 832a #471 + '132R' // 832b #3449 + 'a8W' // 832c-832d #230 + '3A' // 832e #78 + '18C' // 832f #470 + '3A' // 8330 #78 + '18C' // 8331 #470 + '163N' // 8332 #4251 + '8W' // 8333 #230 + '18C' // 8334 #470 + '138Q' // 8335 #3604 + '212X' // 8336 #5535 + '22O' // 8337 #586 + '130K' // 8338 #3390 + '141Y' // 8339 #3690 + '8W' // 833a #230 + '18D' // 833b #471 + '8W' // 833c #230 + '44T' // 833d #1163 + 'A' // 833e + '28V' // 833f #749 + '18C' // 8340 #470 + 'A' // 8341 + '22O' // 8342 #586 + '144X' // 8343 #3767 + 'a8W' // 8344-8345 #230 + '248R' // 8346 #6465 + '18C' // 8347 #470 + '5B' // 8348 #131 + '215X' // 8349 #5613 + '127P' // 834a #3317 + '28V' // 834b #749 + '18D' // 834c #471 + 'a3A' // 834d-834e #78 + '18C' // 834f #470 + '138S' // 8350 #3606 + '18C' // 8351 #470 + '176J' // 8352 #4585 + '3A' // 8353 #78 + '138R' // 8354 #3605 + '3A' // 8355 #78 + '8W' // 8356 #230 + '22O' // 8357 #586 + '254V' // 8358 #6625 + '5B' // 8359 #131 + '41M' // 835a #1078 + 'a5B' // 835b-835c #131 + 'A' // 835d + '5B' // 835e #131 + '3X' // 835f #101 + '5B' // 8360 #131 + '4C' // 8361 #106 + '44T' // 8362 #1163 + '92D' // 8363 #2395 + 'a5B' // 8364-8365 #131 + '33H' // 8366 #865 + '2K' // 8367 #62 + 'b5B' // 8368-836a #131 + '3W' // 836b #100 + 'b5B' // 836c-836e #131 + '55X' // 836f #1453 + '3A' // 8370 #78 + 'aA' // 8371-8372 + '18C' // 8373 #470 + 'A' // 8374 + '22O' // 8375 #586 + 'A' // 8376 + '199A' // 8377 #5174 + '8W' // 8378 #230 + 'A' // 8379 + '28V' // 837a #749 + '92N' // 837b #2405 + 'a8W' // 837c-837d #230 + '28V' // 837e #749 + '8W' // 837f #230 + '41M' // 8380 #1078 + 'A' // 8381 + '41M' // 8382 #1078 + '44V' // 8383 #1165 + '3A' // 8384 #78 + 'a92A' // 8385-8386 #2392 + '3A' // 8387 #78 + '5B' // 8388 #131 + '202Q' // 8389 #5268 + '199T' // 838a #5193 + '5B' // 838b #131 + 'A' // 838c + '3A' // 838d #78 + '177W' // 838e #4624 + 'aA' // 838f-8390 + '55T' // 8391 #1449 + '25E' // 8392 #654 + '170S' // 8393 #4438 + '25E' // 8394 #654 + '44U' // 8395 #1164 + '132Q' // 8396 #3448 + 'A' // 8397 + '38C' // 8398 #990 + '25E' // 8399 #654 + '3A' // 839a #78 + 'a25E' // 839b-839c #654 + '3A' // 839d #78 + '144W' // 839e #3766 + '3A' // 839f #78 + '25E' // 83a0 #654 + 'A' // 83a1 + '38C' // 83a2 #990 + '5B' // 83a3 #131 + '44V' // 83a4 #1165 + 'A' // 83a5 + '3A' // 83a6 #78 + '44U' // 83a7 #1164 + '25E' // 83a8 #654 + 'a38C' // 83a9-83aa #990 + '192K' // 83ab #5002 + '44T' // 83ac #1163 + '3A' // 83ad #78 + '5B' // 83ae #131 + 'a55W' // 83af-83b0 #1452 + '248T' // 83b1 #6467 + '1R' // 83b2 #43 + 'a5B' // 83b3-83b4 #131 + '3A' // 83b5 #78 + '5B' // 83b6 #131 + '1Z' // 83b7 #51 + '5B' // 83b8 #131 + '92R' // 83b9 #2409 + '5B' // 83ba #131 + 'A' // 83bb + '5B' // 83bc #131 + '38C' // 83bd #990 + '44U' // 83be #1164 + 'a25E' // 83bf-83c0 #654 + '163M' // 83c1 #4250 + '55W' // 83c2 #1452 + 'A' // 83c3 + '44V' // 83c4 #1165 + '92P' // 83c5 #2407 + 'A' // 83c6 + '163O' // 83c7 #4252 + '55V' // 83c8 #1451 + '91Y' // 83c9 #2390 + '168J' // 83ca #4377 + '55V' // 83cb #1451 + '182J' // 83cc #4741 + '55T' // 83cd #1449 + '18B' // 83ce #469 + '91S' // 83cf #2384 + '3A' // 83d0 #78 + '3Z' // 83d1 #103 + 'A' // 83d2 + '131F' // 83d3 #3411 + '3Z' // 83d4 #103 + '25D' // 83d5 #653 + '16N' // 83d6 #429 + 'A' // 83d7 + '3Z' // 83d8 #103 + 'aA' // 83d9-83da + '11G' // 83db #292 + '216D' // 83dc #5619 + '3Z' // 83dd #103 + 'A' // 83de + '3Z' // 83df #103 + '132S' // 83e0 #3450 + '3Z' // 83e1 #103 + '15K' // 83e2 #400 + 'aA' // 83e3-83e4 + '3Z' // 83e5 #103 + 'aA' // 83e6-83e7 + '3A' // 83e8 #78 + '147R' // 83e9 #3839 + '3Z' // 83ea #103 + '16N' // 83eb #429 + 'A' // 83ec + '10T' // 83ed #279 + 'A' // 83ee + '234Y' // 83ef #6108 + '16N' // 83f0 #429 + '161U' // 83f1 #4206 + '199U' // 83f2 #5194 + '15K' // 83f3 #400 + '16N' // 83f4 #429 + 'A' // 83f5 + 'a3A' // 83f6-83f7 #78 + '156W' // 83f8 #4078 + '16N' // 83f9 #429 + 'A' // 83fa + '3Z' // 83fb #103 + '18B' // 83fc #469 + '16N' // 83fd #429 + '15K' // 83fe #400 + '11G' // 83ff #292 + 'A' // 8400 + '63S' // 8401 #1656 + 'A' // 8402 + '156V' // 8403 #4077 + '170R' // 8404 #4437 + '10T' // 8405 #279 + '3Z' // 8406 #103 + '18B' // 8407 #469 + 'aA' // 8408-8409 + '199S' // 840a #5192 + '3Z' // 840b #103 + '189Q' // 840c #4930 + '144V' // 840d #3765 + '135T' // 840e #3529 + '3Z' // 840f #103 + 'A' // 8410 + '3Z' // 8411 #103 + 'A' // 8412 + '18B' // 8413 #469 + '10T' // 8414 #279 + '3A' // 8415 #78 + '10T' // 8416 #279 + '3A' // 8417 #78 + '25D' // 8418 #653 + '3A' // 8419 #78 + 'A' // 841a + '15K' // 841b #400 + '25D' // 841c #653 + '4C' // 841d #106 + 'aA' // 841e-841f + '3Z' // 8420 #103 + '15K' // 8421 #400 + '49R' // 8422 #1291 + 'a25D' // 8423-8424 #653 + '1Z' // 8425 #51 + '25D' // 8426 #653 + '2R' // 8427 #69 + '1R' // 8428 #43 + '91W' // 8429 #2388 + '3A' // 842a #78 + '15K' // 842b #400 + '233E' // 842c #6062 + 'a15K' // 842d-842e #400 + '3A' // 842f #78 + 'A' // 8430 + '150F' // 8431 #3905 + 'a15K' // 8432-8433 #400 + 'A' // 8434 + '18B' // 8435 #469 + 'A' // 8436 + '15K' // 8437 #400 + '16N' // 8438 #429 + '3Z' // 8439 #103 + 'A' // 843a + '25D' // 843b #653 + '3Z' // 843c #103 + '230N' // 843d #5993 + '15K' // 843e #400 + '11G' // 843f #292 + 'dA' // 8440-8444 + '18B' // 8445 #469 + 'a3Z' // 8446-8447 #103 + '28R' // 8448 #745 + '213X' // 8449 #5561 + '28R' // 844a #745 + 'aA' // 844b-844c + '3A' // 844d #78 + '3Z' // 844e #103 + '3A' // 844f #78 + 'A' // 8450 + 'a3Z' // 8451-8452 #103 + '10T' // 8453 #279 + 'A' // 8454 + '10T' // 8455 #279 + '3Z' // 8456 #103 + '235A' // 8457 #6110 + '28R' // 8458 #745 + 'a3Z' // 8459-845a #103 + '168S' // 845b #4386 + '3Z' // 845c #103 + 'aA' // 845d-845e + '18B' // 845f #469 + '3A' // 8460 #78 + '177X' // 8461 #4625 + '3Z' // 8462 #103 + '201G' // 8463 #5232 + '28R' // 8464 #745 + '3A' // 8465 #78 + '16N' // 8466 #429 + '18B' // 8467 #469 + 'A' // 8468 + '127R' // 8469 #3319 + '3A' // 846a #78 + '122A' // 846b #3172 + '154V' // 846c #4025 + '3Z' // 846d #103 + '3A' // 846e #78 + '16N' // 846f #429 + '3Z' // 8470 #103 + '91T' // 8471 #2385 + '10T' // 8472 #279 + '3Z' // 8473 #103 + '18B' // 8474 #469 + '161E' // 8475 #4190 + 'b3Z' // 8476-8478 #103 + '3A' // 8479 #78 + '16N' // 847a #429 + 'A' // 847b + '3A' // 847c #78 + '49R' // 847d #1291 + 'A' // 847e + 'a10T' // 847f-8480 #279 + '3A' // 8481 #78 + '185G' // 8482 #4816 + 'A' // 8483 + '3Z' // 8484 #103 + '49R' // 8485 #1291 + 'A' // 8486 + '11G' // 8487 #292 + '10T' // 8488 #279 + '11G' // 8489 #292 + 'A' // 848a + '248S' // 848b #6466 + '11G' // 848c #292 + '15K' // 848d #400 + '25D' // 848e #653 + 'A' // 848f + '163L' // 8490 #4249 + 'A' // 8491 + '28R' // 8492 #745 + '3Z' // 8493 #103 + '91U' // 8494 #2386 + '3A' // 8495 #78 + '10T' // 8496 #279 + '3Z' // 8497 #103 + 'A' // 8498 + '187J' // 8499 #4871 + 'A' // 849a + '11G' // 849b #292 + '150G' // 849c #3906 + '15K' // 849d #400 + '63S' // 849e #1656 + '3Z' // 849f #103 + 'A' // 84a0 + '16N' // 84a1 #429 + 'A' // 84a2 + '10T' // 84a3 #279 + 'A' // 84a4 + '11G' // 84a5 #292 + '3A' // 84a6 #78 + 'A' // 84a7 + '3Z' // 84a8 #103 + 'a3A' // 84a9-84aa #78 + 'aA' // 84ab-84ac + '28R' // 84ad #745 + 'A' // 84ae + '3Z' // 84af #103 + 'A' // 84b0 + '18B' // 84b1 #469 + '154K' // 84b2 #4014 + 'A' // 84b3 + '44R' // 84b4 #1161 + 'bA' // 84b5-84b7 + '181Y' // 84b8 #4730 + 'a44R' // 84b9-84ba #1161 + '55R' // 84bb #1447 + '161L' // 84bc #4197 + 'a44R' // 84bd-84be #1161 + '55R' // 84bf #1447 + '38B' // 84c0 #989 + '63C' // 84c1 #1640 + '28Q' // 84c2 #744 + 'A' // 84c3 + '154T' // 84c4 #4023 + '11G' // 84c5 #292 + '28Q' // 84c6 #744 + '15J' // 84c7 #399 + '2H' // 84c8 #59 + '156U' // 84c9 #4076 + '15J' // 84ca #399 + '208X' // 84cb #5431 + '2H' // 84cc #59 + '28Q' // 84cd #744 + 'a25C' // 84ce-84cf #652 + '15J' // 84d0 #399 + '28Q' // 84d1 #744 + '44S' // 84d2 #1162 + '63C' // 84d3 #1640 + 'aA' // 84d4-84d5 + '15J' // 84d6 #399 + 'aA' // 84d7-84d8 + '2H' // 84d9 #59 + '91R' // 84da #2383 + 'A' // 84db + '2H' // 84dc #59 + '2C' // 84dd #54 + '10T' // 84de #279 + 'a11G' // 84df-84e0 #292 + '10T' // 84e1 #279 + '55S' // 84e2 #1448 + '11G' // 84e3 #292 + '10T' // 84e4 #279 + '91X' // 84e5 #2389 + '11G' // 84e6 #292 + '15J' // 84e7 #399 + '44S' // 84e8 #1162 + 'A' // 84e9 + '25C' // 84ea #652 + 'A' // 84eb + '156T' // 84ec #4075 + 'A' // 84ed + '209M' // 84ee #5446 + 'a25C' // 84ef-84f0 #652 + 'a2H' // 84f1-84f2 #59 + '55S' // 84f3 #1448 + '38B' // 84f4 #989 + 'A' // 84f5 + '11G' // 84f6 #292 + '25C' // 84f7 #652 + '10T' // 84f8 #279 + 'A' // 84f9 + '15J' // 84fa #399 + '2H' // 84fb #59 + '28Q' // 84fc #744 + '25C' // 84fd #652 + 'A' // 84fe + 'a15J' // 84ff-8500 #399 + 'A' // 8501 + '2H' // 8502 #59 + '44Q' // 8503 #1160 + 'a10T' // 8504-8505 #279 + '25C' // 8506 #652 + '2H' // 8507 #59 + 'cA' // 8508-850b + '15J' // 850c #399 + 'A' // 850d + '2H' // 850e #59 + 'A' // 850f + '44Q' // 8510 #1160 + '91V' // 8511 #2387 + 'A' // 8512 + '152N' // 8513 #3965 + '150E' // 8514 #3904 + '15J' // 8515 #399 + 'A' // 8516 + '28Q' // 8517 #744 + '121Y' // 8518 #3170 + 'A' // 8519 + '144U' // 851a #3764 + 'a2H' // 851b-851c #59 + '44S' // 851d #1162 + '38B' // 851e #989 + '15J' // 851f #399 + 'A' // 8520 + '199Q' // 8521 #5190 + '2H' // 8522 #59 + '156S' // 8523 #4074 + '25C' // 8524 #652 + '170Q' // 8525 #4436 + '141R' // 8526 #3683 + '2H' // 8527 #59 + 'A' // 8528 + '11G' // 8529 #292 + '2H' // 852a #59 + '15J' // 852b #399 + '177U' // 852c #4622 + '135S' // 852d #3528 + 'A' // 852e + '38B' // 852f #989 + 'aA' // 8530-8531 + '248Q' // 8532 #6464 + '44Q' // 8533 #1160 + '15J' // 8534 #399 + '257W' // 8535 #6704 + '2H' // 8536 #59 + '2W' // 8537 #74 + '55Q' // 8538 #1446 + 'a11G' // 8539-853a #292 + '137Q' // 853b #3578 + '11G' // 853c #292 + '147T' // 853d #3841 + '6M' // 853e #168 + '248P' // 853f #6463 + '2H' // 8540 #59 + '55P' // 8541 #1445 + '28P' // 8542 #743 + '144T' // 8543 #3763 + 'A' // 8544 + '55Q' // 8545 #1446 + '2H' // 8546 #59 + 'A' // 8547 + '6B' // 8548 #157 + '152S' // 8549 #3970 + '127O' // 854a #3316 + '6M' // 854b #168 + '16M' // 854c #428 + '28P' // 854d #743 + '91M' // 854e #2378 + 'b2H' // 854f-8551 #59 + '6M' // 8552 #168 + '55O' // 8553 #1444 + 'A' // 8554 + '6M' // 8555 #168 + 'a6B' // 8556-8557 #157 + '6M' // 8558 #168 + '127N' // 8559 #3315 + '6M' // 855a #168 + 'A' // 855b + 'a2H' // 855c-855d #59 + '6B' // 855e #157 + '28L' // 855f #739 + '2H' // 8560 #59 + '6B' // 8561 #157 + '6M' // 8562 #168 + '55P' // 8563 #1445 + '6B' // 8564 #157 + '91P' // 8565 #2381 + 'aA' // 8566-8567 + '91J' // 8568 #2375 + '156R' // 8569 #4073 + '55O' // 856a #1444 + '6M' // 856b #168 + '28P' // 856c #743 + '170P' // 856d #4435 + 'A' // 856e + '6B' // 856f #157 + '91O' // 8570 #2380 + 'A' // 8571 + '16F' // 8572 #421 + '16M' // 8573 #428 + '2K' // 8574 #62 + 'A' // 8575 + '16F' // 8576 #421 + '6M' // 8577 #168 + '28P' // 8578 #743 + 'b6B' // 8579-857b #157 + 'A' // 857c + '2H' // 857d #59 + '177V' // 857e #4623 + '2H' // 857f #59 + '6B' // 8580 #157 + '6M' // 8581 #168 + 'aA' // 8582-8583 + '204P' // 8584 #5319 + 'a6B' // 8585-8586 #157 + '173Q' // 8587 #4514 + '132P' // 8588 #3447 + '2H' // 8589 #59 + '6B' // 858a #157 + '2H' // 858b #59 + '6M' // 858c #168 + 'aA' // 858d-858e + '20E' // 858f #524 + '6M' // 8590 #168 + '156P' // 8591 #4071 + 'A' // 8592 + '28L' // 8593 #739 + '91L' // 8594 #2377 + 'aA' // 8595-8596 + 'a6M' // 8597-8598 #168 + '91H' // 8599 #2373 + 'A' // 859a + '144S' // 859b #3762 + '6B' // 859c #157 + '2H' // 859d #59 + 'A' // 859e + '6M' // 859f #168 + '2H' // 85a0 #59 + '28P' // 85a1 #743 + '6B' // 85a2 #157 + 'A' // 85a3 + '6B' // 85a4 #157 + '2H' // 85a5 #59 + '233N' // 85a6 #6071 + '2H' // 85a7 #59 + '20E' // 85a8 #524 + '187S' // 85a9 #4880 + '187N' // 85aa #4875 + '253M' // 85ab #6590 + '258F' // 85ac #6713 + '2H' // 85ad #59 + '35Z' // 85ae #935 + '163K' // 85af #4248 + '156Q' // 85b0 #4072 + 'aA' // 85b1-85b2 + '28P' // 85b3 #743 + '6M' // 85b4 #168 + 'A' // 85b5 + '2H' // 85b6 #59 + '6B' // 85b7 #157 + '35Z' // 85b8 #935 + '6B' // 85b9 #157 + '33G' // 85ba #864 + 'A' // 85bb + '2H' // 85bc #59 + '6M' // 85bd #168 + '6B' // 85be #157 + '2H' // 85bf #59 + 'A' // 85c0 + '20E' // 85c1 #524 + '6M' // 85c2 #168 + 'cA' // 85c3-85c6 + '35Z' // 85c7 #935 + 'A' // 85c8 + '192J' // 85c9 #5001 + '2H' // 85ca #59 + '6M' // 85cb #168 + 'A' // 85cc + '218O' // 85cd #5682 + '33G' // 85ce #864 + '224U' // 85cf #5844 + '6B' // 85d0 #157 + 'aA' // 85d1-85d2 + '16F' // 85d3 #421 + 'A' // 85d4 + '20E' // 85d5 #524 + '16M' // 85d6 #428 + 'A' // 85d7 + 'b2H' // 85d8-85da #59 + 'A' // 85db + '20E' // 85dc #524 + '226M' // 85dd #5888 + 'A' // 85de + '35Z' // 85df #935 + '6B' // 85e0 #157 + '2H' // 85e1 #59 + 'aA' // 85e2-85e3 + '184C' // 85e4 #4786 + '206V' // 85e5 #5377 + '6B' // 85e6 #157 + 'A' // 85e7 + '6B' // 85e8 #157 + '135P' // 85e9 #3525 + '91I' // 85ea #2374 + 'aA' // 85eb-85ec + '6M' // 85ed #168 + '16M' // 85ee #428 + 'cA' // 85ef-85f2 + '2H' // 85f3 #59 + '6B' // 85f4 #157 + 'A' // 85f5 + '6M' // 85f6 #168 + '33G' // 85f7 #864 + 'A' // 85f8 + '6B' // 85f9 #157 + '33G' // 85fa #864 + '148G' // 85fb #3854 + '28L' // 85fc #739 + 'A' // 85fd + '35Z' // 85fe #935 + '20E' // 85ff #524 + '2H' // 8600 #59 + 'A' // 8601 + '20E' // 8602 #524 + 'A' // 8603 + '6M' // 8604 #168 + '6B' // 8605 #157 + '170O' // 8606 #4434 + '208W' // 8607 #5430 + 'aA' // 8608-8609 + '138P' // 860a #3603 + '199R' // 860b #5191 + 'A' // 860c + '28L' // 860d #739 + '2H' // 860e #59 + '16M' // 860f #428 + '6M' // 8610 #168 + '121Z' // 8611 #3171 + '2H' // 8612 #59 + '28L' // 8613 #739 + '16M' // 8614 #428 + 'A' // 8615 + '20E' // 8616 #524 + '33G' // 8617 #864 + '6B' // 8618 #157 + '2H' // 8619 #59 + '20E' // 861a #524 + '2H' // 861b #59 + 'aA' // 861c-861d + '6M' // 861e #168 + 'aA' // 861f-8620 + 'a28N' // 8621-8622 #741 + 'A' // 8623 + '2H' // 8624 #59 + 'aA' // 8625-8626 + '28M' // 8627 #740 + '16M' // 8628 #428 + '28M' // 8629 #740 + '28O' // 862a #742 + 'aA' // 862b-862c + '226W' // 862d #5898 + 'A' // 862e + '28L' // 862f #739 + '2H' // 8630 #59 + 'bA' // 8631-8633 + 'a28O' // 8634-8635 #742 + '28N' // 8636 #741 + 'A' // 8637 + '28M' // 8638 #740 + '2H' // 8639 #59 + '28N' // 863a #741 + 'A' // 863b + '28M' // 863c #740 + '2H' // 863d #59 + 'A' // 863e + '177T' // 863f #4621 + '28N' // 8640 #741 + '2H' // 8641 #59 + '28N' // 8642 #741 + 'aA' // 8643-8644 + '16M' // 8645 #428 + '28N' // 8646 #741 + 'dA' // 8647-864b + '28O' // 864c #742 + '28M' // 864d #740 + '202K' // 864e #5262 + '16F' // 864f #421 + '167T' // 8650 #4361 + '3Y' // 8651 #102 + 'a28M' // 8652-8653 #740 + '91G' // 8654 #2372 + '232F' // 8655 #6037 + 'b1O' // 8656-8658 #40 + '33F' // 8659 #863 + '254H' // 865a #6611 + '192I' // 865b #5000 + '91K' // 865c #2376 + '1O' // 865d #40 + '127K' // 865e #3312 + '239P' // 865f #6229 + 'a1O' // 8660-8661 #40 + '20D' // 8662 #523 + 'a1O' // 8663-8664 #40 + 'aA' // 8665-8666 + '163J' // 8667 #4247 + 'A' // 8668 + '1O' // 8669 #40 + 'A' // 866a + '91N' // 866b #2379 + '20D' // 866c #523 + 'A' // 866d + '16F' // 866e #421 + '33F' // 866f #863 + '28O' // 8670 #742 + '20D' // 8671 #523 + '16M' // 8672 #428 + '28O' // 8673 #742 + 'A' // 8674 + '41L' // 8675 #1077 + '1O' // 8676 #40 + '33F' // 8677 #863 + 'A' // 8678 + '181L' // 8679 #4717 + 'a20D' // 867a-867b #523 + '16F' // 867c #421 + '248O' // 867d #6462 + '91Q' // 867e #2382 + '16F' // 867f #421 + '3H' // 8680 #85 + 'a2R' // 8681-8682 #69 + 'cA' // 8683-8686 + 'b1O' // 8687-8689 #40 + '172Y' // 868a #4496 + '20D' // 868b #523 + '91F' // 868c #2371 + '20D' // 868d #523 + 'bA' // 868e-8690 + '1O' // 8691 #40 + '16M' // 8692 #428 + '44P' // 8693 #1159 + '28O' // 8694 #742 + '55N' // 8695 #1443 + '33F' // 8696 #863 + 'A' // 8697 + '1O' // 8698 #40 + 'A' // 8699 + '33F' // 869a #863 + 'A' // 869b + 'a20D' // 869c-869d #523 + 'aA' // 869e-869f + '16M' // 86a0 #428 + '20D' // 86a1 #523 + 'A' // 86a2 + 'a44P' // 86a3-86a4 #1159 + 'A' // 86a5 + '1O' // 86a6 #40 + 'a20D' // 86a7-86a8 #523 + '44P' // 86a9 #1159 + '55N' // 86aa #1443 + '1O' // 86ab #40 + '16F' // 86ac #421 + '91E' // 86ad #2370 + 'A' // 86ae + 'b18A' // 86af-86b1 #468 + '44M' // 86b2 #1156 + '22N' // 86b3 #585 + '18A' // 86b4 #468 + '132O' // 86b5 #3446 + '18A' // 86b6 #468 + 'a1O' // 86b7-86b8 #40 + '22N' // 86b9 #585 + '55M' // 86ba #1442 + 'cA' // 86bb-86be + '1O' // 86bf #40 + '18A' // 86c0 #468 + '22N' // 86c1 #585 + '38A' // 86c2 #988 + '1O' // 86c3 #40 + '18A' // 86c4 #468 + '1O' // 86c5 #40 + '18A' // 86c6 #468 + '181D' // 86c7 #4709 + 'A' // 86c8 + '18A' // 86c9 #468 + '16F' // 86ca #421 + '217C' // 86cb #5644 + '38A' // 86cc #988 + '253X' // 86cd #6601 + '41L' // 86ce #1077 + '16F' // 86cf #421 + '55M' // 86d0 #1442 + '41L' // 86d1 #1077 + '1O' // 86d2 #40 + '38A' // 86d3 #988 + '44O' // 86d4 #1158 + '1O' // 86d5 #40 + 'A' // 86d6 + '1O' // 86d7 #40 + '16F' // 86d8 #421 + '159F' // 86d9 #4139 + '1O' // 86da #40 + '141N' // 86db #3679 + '1O' // 86dc #40 + 'A' // 86dd + '18A' // 86de #468 + '44O' // 86df #1158 + '1O' // 86e0 #40 + 'aA' // 86e1-86e2 + '1O' // 86e3 #40 + '127J' // 86e4 #3311 + '1O' // 86e5 #40 + '41L' // 86e6 #1077 + '1O' // 86e7 #40 + 'A' // 86e8 + '18A' // 86e9 #468 + 'aA' // 86ea-86eb + '1O' // 86ec #40 + '44O' // 86ed #1158 + '252L' // 86ee #6563 + '18A' // 86ef #468 + 'c16F' // 86f0-86f3 #421 + '24G' // 86f4 #630 + 'bA' // 86f5-86f7 + 'a8V' // 86f8-86f9 #229 + '22N' // 86fa #585 + '8V' // 86fb #229 + 'a1O' // 86fc-86fd #40 + '28K' // 86fe #738 + 'A' // 86ff + '127L' // 8700 #3313 + 'A' // 8701 + '180U' // 8702 #4700 + '28K' // 8703 #738 + 'a1O' // 8704-8705 #40 + 'a8V' // 8706-8707 #229 + '28K' // 8708 #738 + 'a8V' // 8709-870a #229 + '1O' // 870b #40 + 'A' // 870c + '8V' // 870d #229 + '22N' // 870e #585 + 'a1O' // 870f-8710 #40 + 'a8V' // 8711-8712 #229 + '64O' // 8713 #1678 + '1O' // 8714 #40 + '91D' // 8715 #2369 + 'A' // 8716 + '3G' // 8717 #84 + '135I' // 8718 #3518 + '22N' // 8719 #585 + '28K' // 871a #738 + 'A' // 871b + '201W' // 871c #5248 + 'A' // 871d + '8V' // 871e #229 + '1O' // 871f #40 + 'A' // 8720 + '91C' // 8721 #2368 + 'a8V' // 8722-8723 #229 + 'A' // 8724 + '8V' // 8725 #229 + 'aA' // 8726-8727 + '22N' // 8728 #585 + '8V' // 8729 #229 + 'cA' // 872a-872d + '8V' // 872e #229 + '1O' // 872f #40 + 'A' // 8730 + '8V' // 8731 #229 + '1O' // 8732 #40 + 'A' // 8733 + '8V' // 8734 #229 + 'aA' // 8735-8736 + '8V' // 8737 #229 + 'A' // 8738 + '1O' // 8739 #40 + '8V' // 873a #229 + '64O' // 873b #1678 + 'a1O' // 873c-873d #40 + 'b8V' // 873e-8740 #229 + 'A' // 8741 + '55L' // 8742 #1441 + '1O' // 8743 #40 + 'A' // 8744 + '1O' // 8745 #40 + 'A' // 8746 + 'a24G' // 8747-8748 #630 + '248N' // 8749 #6461 + 'A' // 874a + '1O' // 874b #40 + '91B' // 874c #2367 + '1O' // 874d #40 + '91A' // 874e #2366 + '38A' // 874f #988 + 'A' // 8750 + '1O' // 8751 #40 + 'A' // 8752 + '8V' // 8753 #229 + 'A' // 8754 + '150D' // 8755 #3903 + 'A' // 8756 + '28K' // 8757 #738 + '8V' // 8758 #229 + '63R' // 8759 #1655 + 'bA' // 875a-875c + '8V' // 875d #229 + 'A' // 875e + '28K' // 875f #738 + '63R' // 8760 #1655 + '22N' // 8761 #585 + '55L' // 8762 #1441 + 'b9C' // 8763-8765 #236 + '67G' // 8766 #1748 + 'A' // 8767 + '33D' // 8768 #861 + 'A' // 8769 + '22M' // 876a #584 + 'A' // 876b + 'a16L' // 876c-876d #427 + '9C' // 876e #236 + '22M' // 876f #584 + '33E' // 8770 #862 + '25B' // 8771 #651 + '49Q' // 8772 #1290 + 'A' // 8773 + '156O' // 8774 #4070 + '24G' // 8775 #630 + '174N' // 8776 #4537 + '16L' // 8777 #427 + '63Q' // 8778 #1654 + 'A' // 8779 + '16L' // 877a #427 + '9C' // 877b #236 + '49Q' // 877c #1290 + '33E' // 877d #862 + '24G' // 877e #630 + '1O' // 877f #40 + 'A' // 8780 + '16L' // 8781 #427 + '63Q' // 8782 #1654 + '121X' // 8783 #3169 + '22M' // 8784 #584 + '9C' // 8785 #236 + '25B' // 8786 #651 + '1O' // 8787 #40 + '9C' // 8788 #236 + '1O' // 8789 #40 + 'A' // 878a + '9C' // 878b #236 + '25B' // 878c #651 + '203Z' // 878d #5303 + '1O' // 878e #40 + 'A' // 878f + '1O' // 8790 #40 + 'aA' // 8791-8792 + '9C' // 8793 #236 + 'A' // 8794 + '1O' // 8795 #40 + 'A' // 8796 + '9C' // 8797 #236 + '22M' // 8798 #584 + '1O' // 8799 #40 + '24G' // 879a #630 + 'bA' // 879b-879d + '127M' // 879e #3314 + '33D' // 879f #861 + '49Q' // 87a0 #1290 + 'A' // 87a1 + '67G' // 87a2 #1748 + '9C' // 87a3 #236 + 'A' // 87a4 + '44M' // 87a5 #1156 + 'A' // 87a6 + '1O' // 87a7 #40 + '24G' // 87a8 #630 + '44M' // 87a9 #1156 + 'A' // 87aa + 'b9C' // 87ab-87ad #236 + '1O' // 87ae #40 + '9C' // 87af #236 + 'A' // 87b0 + '25B' // 87b1 #651 + 'A' // 87b2 + '33D' // 87b3 #861 + 'A' // 87b4 + '9C' // 87b5 #236 + 'bA' // 87b6-87b8 + '16L' // 87b9 #427 + '172S' // 87ba #4490 + '22M' // 87bb #584 + 'A' // 87bc + '9C' // 87bd #236 + 'a22M' // 87be-87bf #584 + '9C' // 87c0 #236 + '25B' // 87c1 #651 + 'aA' // 87c2-87c3 + '33D' // 87c4 #861 + '16L' // 87c5 #427 + '9C' // 87c6 #236 + '1O' // 87c7 #40 + '22M' // 87c8 #584 + '1O' // 87c9 #40 + 'a9C' // 87ca-87cb #236 + '16L' // 87cc #427 + 'A' // 87cd + '22M' // 87ce #584 + 'A' // 87cf + '1O' // 87d0 #40 + '131J' // 87d1 #3415 + '9C' // 87d2 #236 + 'aA' // 87d3-87d4 + '1O' // 87d5 #40 + '25B' // 87d6 #651 + 'aA' // 87d7-87d8 + '1O' // 87d9 #40 + '25B' // 87da #651 + '33E' // 87db #862 + '9C' // 87dc #236 + 'A' // 87dd + '24G' // 87de #630 + '1O' // 87df #40 + '33D' // 87e0 #861 + '16L' // 87e1 #427 + '1L' // 87e2 #37 + '33C' // 87e3 #860 + '1L' // 87e4 #37 + '25A' // 87e5 #650 + '1L' // 87e6 #37 + '16L' // 87e7 #427 + 'aA' // 87e8-87e9 + '25A' // 87ea #650 + '33C' // 87eb #860 + '132N' // 87ec #3445 + '1L' // 87ed #37 + '33E' // 87ee #862 + '90Z' // 87ef #2365 + 'A' // 87f0 + '1L' // 87f1 #37 + '194J' // 87f2 #5053 + '25A' // 87f3 #650 + '16L' // 87f4 #427 + '44N' // 87f5 #1157 + 'a33C' // 87f6-87f7 #860 + '1L' // 87f8 #37 + '173L' // 87f9 #4509 + '1L' // 87fa #37 + '144Q' // 87fb #3760 + 'A' // 87fc + '24G' // 87fd #630 + '55K' // 87fe #1440 + '1L' // 87ff #37 + 'A' // 8800 + '1L' // 8801 #37 + '33E' // 8802 #862 + '25A' // 8803 #650 + '16L' // 8804 #427 + '55K' // 8805 #1440 + '33C' // 8806 #860 + '1L' // 8807 #37 + 'A' // 8808 + '1L' // 8809 #37 + 'a25A' // 880a-880b #650 + 'A' // 880c + '127I' // 880d #3310 + '1L' // 880e #37 + '44N' // 880f #1157 + 'a33C' // 8810-8811 #860 + '1L' // 8812 #37 + '25A' // 8813 #650 + '121W' // 8814 #3168 + 'a25A' // 8815-8816 #650 + 'A' // 8817 + '44N' // 8818 #1157 + '69Y' // 8819 #1818 + '1L' // 881a #37 + '10S' // 881b #278 + '1L' // 881c #37 + 'A' // 881d + '1L' // 881e #37 + '156N' // 881f #4069 + 'A' // 8820 + '10S' // 8821 #278 + '138O' // 8822 #3602 + '90O' // 8823 #2354 + 'bA' // 8824-8826 + '24Z' // 8827 #649 + '1L' // 8828 #37 + 'cA' // 8829-882c + '28I' // 882d #736 + '1L' // 882e #37 + 'A' // 882f + '1L' // 8830 #37 + '28J' // 8831 #737 + '10S' // 8832 #278 + 'aA' // 8833-8834 + '10S' // 8835 #278 + '127H' // 8836 #3309 + 'aA' // 8837-8838 + '10S' // 8839 #278 + '1L' // 883a #37 + '185D' // 883b #4813 + '10S' // 883c #278 + 'bA' // 883d-883f + '215R' // 8840 #5607 + '1L' // 8841 #37 + '28I' // 8842 #736 + '1L' // 8843 #37 + '10S' // 8844 #278 + '90K' // 8845 #2350 + '90V' // 8846 #2361 + 'A' // 8847 + 'a1L' // 8848-8849 #37 + '10S' // 884a #278 + '1L' // 884b #37 + '247A' // 884c #6422 + '65Z' // 884d #1715 + '10S' // 884e #278 + '22L' // 884f #583 + 'A' // 8850 + '1L' // 8851 #37 + '28J' // 8852 #737 + '236H' // 8853 #6143 + '2R' // 8854 #69 + '10S' // 8855 #278 + '24Z' // 8856 #649 + '228U' // 8857 #5948 + '1L' // 8858 #37 + '28J' // 8859 #737 + '24Z' // 885a #649 + '215T' // 885b #5609 + '1L' // 885c #37 + '204J' // 885d #5313 + '10S' // 885e #278 + '1L' // 885f #37 + '28I' // 8860 #736 + '187E' // 8861 #4866 + '28J' // 8862 #737 + '228V' // 8863 #5949 + '10S' // 8864 #278 + '90Y' // 8865 #2364 + 'aA' // 8866-8867 + '244V' // 8868 #6365 + '10S' // 8869 #278 + '90X' // 886a #2363 + '170M' // 886b #4432 + '2L' // 886c #63 + '55J' // 886d #1439 + '10S' // 886e #278 + '69Y' // 886f #1818 + '173X' // 8870 #4521 + '24Z' // 8871 #649 + '28J' // 8872 #737 + 'aA' // 8873-8874 + '24Z' // 8875 #649 + 'A' // 8876 + '150A' // 8877 #3900 + 'A' // 8878 + '10S' // 8879 #278 + 'A' // 887a + '1L' // 887b #37 + 'A' // 887c + '10S' // 887d #278 + '28J' // 887e #737 + '90N' // 887f #2353 + '1L' // 8880 #37 + '150B' // 8881 #3901 + '44L' // 8882 #1155 + 'A' // 8883 + '55I' // 8884 #1438 + 'a10J' // 8885-8886 #269 + '22L' // 8887 #583 + '44L' // 8888 #1155 + 'aA' // 8889-888a + '213B' // 888b #5539 + 'A' // 888c + '144R' // 888d #3761 + 'A' // 888e + '90W' // 888f #2362 + '55J' // 8890 #1439 + 'A' // 8891 + '44L' // 8892 #1155 + '55I' // 8893 #1438 + 'aA' // 8894-8895 + '182P' // 8896 #4747 + '55H' // 8897 #1437 + '24Z' // 8898 #649 + '1L' // 8899 #37 + '24Z' // 889a #649 + '24Y' // 889b #648 + '90L' // 889c #2351 + '22L' // 889d #583 + '55H' // 889e #1437 + '1L' // 889f #37 + '28I' // 88a0 #736 + 'A' // 88a1 + '24Y' // 88a2 #648 + 'A' // 88a3 + '24Y' // 88a4 #648 + 'A' // 88a5 + '10J' // 88a6 #269 + 'A' // 88a7 + '24Y' // 88a8 #648 + 'A' // 88a9 + '24Y' // 88aa #648 + '233Y' // 88ab #6082 + 'A' // 88ac + '3Y' // 88ad #102 + '24Y' // 88ae #648 + 'A' // 88af + '1L' // 88b0 #37 + '24Y' // 88b1 #648 + 'aA' // 88b2-88b3 + '90P' // 88b4 #2355 + '28I' // 88b5 #736 + 'A' // 88b6 + '6L' // 88b7 #167 + '17Z' // 88b8 #467 + 'A' // 88b9 + '1L' // 88ba #37 + 'A' // 88bb + '6L' // 88bc #167 + 'a20C' // 88bd-88be #522 + '28I' // 88bf #736 + '6L' // 88c0 #167 + '190E' // 88c1 #4944 + '181X' // 88c2 #4729 + 'a1L' // 88c3-88c4 #37 + '149O' // 88c5 #3888 + '41K' // 88c6 #1076 + '17Y' // 88c7 #466 + '10J' // 88c8 #269 + '17Y' // 88c9 #466 + 'b20C' // 88ca-88cc #522 + '1L' // 88cd #37 + '6L' // 88ce #167 + '191M' // 88cf #4978 + 'A' // 88d0 + '41K' // 88d1 #1076 + '6L' // 88d2 #167 + '41K' // 88d3 #1076 + '65Z' // 88d4 #1715 + '176K' // 88d5 #4586 + '17Z' // 88d6 #467 + 'A' // 88d7 + '6L' // 88d8 #167 + '185E' // 88d9 #4814 + 'A' // 88da + '6L' // 88db #167 + '221V' // 88dc #5767 + '232E' // 88dd #6036 + '1L' // 88de #37 + '24X' // 88df #647 + '1L' // 88e0 #37 + '224T' // 88e1 #5843 + 'a10J' // 88e2-88e3 #269 + '3N' // 88e4 #91 + '10J' // 88e5 #269 + '22L' // 88e6 #583 + '1L' // 88e7 #37 + '24X' // 88e8 #647 + 'bA' // 88e9-88eb + '17Y' // 88ec #466 + 'aA' // 88ed-88ee + '20C' // 88ef #522 + 'a6L' // 88f0-88f1 #167 + '1L' // 88f2 #37 + '90M' // 88f3 #2352 + '24X' // 88f4 #647 + '55G' // 88f5 #1436 + 'A' // 88f6 + '1L' // 88f7 #37 + '183C' // 88f8 #4760 + '150C' // 88f9 #3902 + 'aA' // 88fa-88fb + '6L' // 88fc #167 + '236R' // 88fd #6153 + '90R' // 88fe #2357 + '22L' // 88ff #583 + '17Y' // 8900 #466 + '1L' // 8901 #37 + '6L' // 8902 #167 + 'A' // 8903 + '1L' // 8904 #37 + 'A' // 8905 + '6L' // 8906 #167 + '216C' // 8907 #5618 + 'A' // 8908 + '10J' // 8909 #269 + '6L' // 890a #167 + '17Y' // 890b #466 + '6L' // 890c #167 + 'b1L' // 890d-890f #37 + '136B' // 8910 #3537 + 'A' // 8911 + '90T' // 8912 #2359 + '24X' // 8913 #647 + '17Y' // 8914 #466 + '6L' // 8915 #167 + '1L' // 8916 #37 + 'A' // 8917 + 'a24X' // 8918-8919 #647 + '6L' // 891a #167 + '10J' // 891b #269 + 'b1L' // 891c-891e #37 + '17Z' // 891f #467 + '1L' // 8920 #37 + '17Y' // 8921 #466 + 'A' // 8922 + '17Z' // 8923 #467 + '22L' // 8924 #583 + '24X' // 8925 #647 + 'b1L' // 8926-8928 #37 + 'A' // 8929 + '90J' // 892a #2349 + '6L' // 892b #167 + 'A' // 892c + '17Z' // 892d #467 + 'aA' // 892e-892f + '6L' // 8930 #167 + '1L' // 8931 #37 + '199P' // 8932 #5189 + '17Z' // 8933 #467 + '10J' // 8934 #269 + '20C' // 8935 #522 + '24X' // 8936 #647 + '1L' // 8937 #37 + '55G' // 8938 #1436 + 'a1L' // 8939-893a #37 + '132M' // 893b #3444 + 'A' // 893c + '17Y' // 893d #466 + '1L' // 893e #37 + 'A' // 893f + '1L' // 8940 #37 + '90H' // 8941 #2347 + 'a20C' // 8942-8943 #522 + '127G' // 8944 #3308 + '1L' // 8945 #37 + '20C' // 8946 #522 + '17Z' // 8947 #467 + 'A' // 8948 + '20C' // 8949 #522 + 'aA' // 894a-894b + '6L' // 894c #167 + '20C' // 894d #522 + 'A' // 894e + '1L' // 894f #37 + 'aA' // 8950-8951 + '1L' // 8952 #37 + 'A' // 8953 + '22L' // 8954 #583 + '10J' // 8955 #269 + '6L' // 8956 #167 + '20C' // 8957 #522 + 'A' // 8958 + '17Y' // 8959 #466 + 'a1L' // 895a-895b #37 + '6L' // 895c #167 + 'A' // 895d + '6L' // 895e #167 + '90Q' // 895f #2356 + '6L' // 8960 #167 + 'b1L' // 8961-8963 #37 + '90I' // 8964 #2348 + '22L' // 8965 #583 + '6L' // 8966 #167 + 'bA' // 8967-8969 + '170L' // 896a #4431 + '1L' // 896b #37 + '17Y' // 896c #466 + 'a1L' // 896d-896e #37 + '170N' // 896f #4433 + '41K' // 8970 #1076 + '17Z' // 8971 #467 + '197A' // 8972 #5122 + '1T' // 8973 #45 + '33B' // 8974 #859 + '1T' // 8975 #45 + 'A' // 8976 + '55F' // 8977 #1435 + 'aA' // 8978-8979 + '1T' // 897a #45 + '37Z' // 897b #987 + 'a1T' // 897c-897d #45 + '37Z' // 897e #987 + '68S' // 897f #1786 + '37Z' // 8980 #987 + '244U' // 8981 #6364 + '17Z' // 8982 #467 + '90G' // 8983 #2346 + 'A' // 8984 + '10J' // 8985 #269 + '218R' // 8986 #5685 + '90U' // 8987 #2360 + '37Z' // 8988 #987 + '55F' // 8989 #1435 + '33B' // 898a #859 + '245H' // 898b #6377 + '10J' // 898c #269 + '1T' // 898d #45 + 'A' // 898e + '238Z' // 898f #6213 + '1T' // 8990 #45 + '17Z' // 8991 #467 + 'A' // 8992 + '138M' // 8993 #3600 + 'a33B' // 8994-8995 #859 + '241T' // 8996 #6285 + '90S' // 8997 #2358 + '33B' // 8998 #859 + 'A' // 8999 + '258E' // 899a #6712 + '1T' // 899b #45 + '33B' // 899c #859 + 'aA' // 899d-899e + 'a1T' // 899f-89a0 #45 + '37Y' // 89a1 #986 + 'aA' // 89a2-89a3 + '90F' // 89a4 #2345 + 'a20B' // 89a5-89a6 #521 + '90B' // 89a7 #2341 + 'A' // 89a8 + '37Y' // 89a9 #986 + '235N' // 89aa #6123 + 'A' // 89ab + '20B' // 89ac #521 + 'aA' // 89ad-89ae + '20B' // 89af #521 + '1T' // 89b0 #45 + 'A' // 89b1 + '37Y' // 89b2 #986 + '259L' // 89b3 #6745 + 'b1T' // 89b4-89b6 #45 + '17X' // 89b7 #465 + 'aA' // 89b8-89b9 + '224S' // 89ba #5842 + 'A' // 89bb + '28H' // 89bc #735 + '232C' // 89bd #6034 + 'A' // 89be + '20B' // 89bf #521 + '232B' // 89c0 #6033 + '132L' // 89c1 #3443 + '1Z' // 89c2 #51 + 'A' // 89c3 + '3Q' // 89c4 #94 + '3G' // 89c5 #84 + '49C' // 89c6 #1276 + '10J' // 89c7 #269 + '1Z' // 89c8 #51 + '2D' // 89c9 #55 + 'b10J' // 89ca-89cc #269 + 'A' // 89cd + 'c10J' // 89ce-89d1 #269 + '228T' // 89d2 #5947 + 'A' // 89d3 + 'a20B' // 89d4-89d5 #521 + '49P' // 89d6 #1289 + 'a1T' // 89d7-89d8 #45 + 'A' // 89d9 + '17X' // 89da #465 + 'A' // 89db + '17X' // 89dc #465 + '20B' // 89dd #521 + '10J' // 89de #269 + 'cA' // 89df-89e2 + '238H' // 89e3 #6195 + 'A' // 89e4 + '17X' // 89e5 #465 + '90A' // 89e6 #2340 + '17X' // 89e7 #465 + 'A' // 89e8 + '1T' // 89e9 #45 + 'A' // 89ea + '49P' // 89eb #1289 + 'A' // 89ec + '20B' // 89ed #521 + 'A' // 89ee + '10J' // 89ef #269 + 'A' // 89f0 + '17X' // 89f1 #465 + 'A' // 89f2 + '17X' // 89f3 #465 + '37Y' // 89f4 #986 + 'A' // 89f5 + '20B' // 89f6 #521 + 'A' // 89f7 + '199O' // 89f8 #5188 + '1T' // 89f9 #45 + 'bA' // 89fa-89fc + '1T' // 89fd #45 + 'A' // 89fe + '17X' // 89ff #465 + '243L' // 8a00 #6329 + '49P' // 8a01 #1289 + '233L' // 8a02 #6069 + '89U' // 8a03 #2334 + 'a1T' // 8a04-8a05 #45 + 'A' // 8a06 + '17X' // 8a07 #465 + '242U' // 8a08 #6312 + '10J' // 8a09 #269 + '240O' // 8a0a #6254 + 'A' // 8a0b + '89Q' // 8a0c #2330 + 'A' // 8a0d + '220B' // 8a0e #5721 + '17X' // 8a0f #465 + 'b20B' // 8a10-8a12 #521 + '209P' // 8a13 #5449 + '1T' // 8a14 #45 + '121V' // 8a15 #3167 + '89P' // 8a16 #2329 + '197M' // 8a17 #5134 + '245J' // 8a18 #6379 + 'A' // 8a19 + '27G' // 8a1a #708 + '8U' // 8a1b #228 + '33A' // 8a1c #858 + '41B' // 8a1d #1067 + '1T' // 8a1e #45 + '142F' // 8a1f #3697 + 'a1T' // 8a20-8a21 #45 + '10R' // 8a22 #277 + '66O' // 8a23 #1730 + '1T' // 8a24 #45 + '8U' // 8a25 #228 + '1T' // 8a26 #45 + '24W' // 8a27 #646 + 'A' // 8a28 + '33A' // 8a29 #858 + '221F' // 8a2a #5751 + '10R' // 8a2b #277 + '1T' // 8a2c #45 + '244E' // 8a2d #6348 + 'A' // 8a2e + '1T' // 8a2f #45 + 'A' // 8a30 + '68K' // 8a31 #1778 + 'A' // 8a32 + '258C' // 8a33 #6710 + '215C' // 8a34 #5592 + '1T' // 8a35 #45 + '8U' // 8a36 #228 + '41J' // 8a37 #1075 + '90E' // 8a38 #2344 + 'A' // 8a39 + '67C' // 8a3a #1744 + '232D' // 8a3b #6035 + '155O' // 8a3c #4044 + '4G' // 8a3d #110 + '10R' // 8a3e #277 + 'A' // 8a3f + '41J' // 8a40 #1075 + '10R' // 8a41 #277 + 'A' // 8a42 + '1T' // 8a43 #45 + 'A' // 8a44 + '4G' // 8a45 #110 + '10R' // 8a46 #277 + '1T' // 8a47 #45 + '10R' // 8a48 #277 + '28H' // 8a49 #735 + 'bA' // 8a4a-8a4c + '1T' // 8a4d #45 + '4G' // 8a4e #110 + 'A' // 8a4f + '196Z' // 8a50 #5121 + '4G' // 8a51 #110 + '10R' // 8a52 #277 + '1T' // 8a53 #45 + '8U' // 8a54 #228 + '68R' // 8a55 #1785 + 'a4G' // 8a56-8a57 #110 + '10R' // 8a58 #277 + 'aA' // 8a59-8a5a + '8U' // 8a5b #228 + '1T' // 8a5c #45 + '41J' // 8a5d #1075 + '210H' // 8a5e #5467 + '27G' // 8a5f #708 + '159P' // 8a60 #4149 + '10R' // 8a61 #277 + '224R' // 8a62 #5841 + '89T' // 8a63 #2333 + 'A' // 8a64 + '1T' // 8a65 #45 + '230M' // 8a66 #5992 + '4G' // 8a67 #110 + 'A' // 8a68 + '202T' // 8a69 #5271 + '27G' // 8a6a #708 + '89X' // 8a6b #2337 + '4G' // 8a6c #110 + '41B' // 8a6d #1067 + '152W' // 8a6e #3974 + 'A' // 8a6f + '89Z' // 8a70 #2339 + '244K' // 8a71 #6354 + '227T' // 8a72 #5921 + '223W' // 8a73 #5820 + '24W' // 8a74 #646 + '17W' // 8a75 #464 + '4G' // 8a76 #110 + '1T' // 8a77 #45 + 'A' // 8a78 + '156M' // 8a79 #4068 + '10R' // 8a7a #277 + 'a4G' // 8a7b-8a7c #110 + 'A' // 8a7d + '28H' // 8a7e #735 + 'a1T' // 8a7f-8a80 #45 + 'A' // 8a81 + '4G' // 8a82 #110 + '1T' // 8a83 #45 + '4G' // 8a84 #110 + '8U' // 8a85 #228 + '4G' // 8a86 #110 + '175W' // 8a87 #4572 + 'A' // 8a88 + '254E' // 8a89 #6608 + '27G' // 8a8a #708 + '1T' // 8a8b #45 + '222L' // 8a8c #5783 + '238G' // 8a8d #6194 + 'A' // 8a8e + '4G' // 8a8f #110 + 'b10R' // 8a90-8a92 #277 + '153V' // 8a93 #3999 + '24W' // 8a94 #646 + '198V' // 8a95 #5169 + 'a1T' // 8a96-8a97 #45 + '67C' // 8a98 #1744 + '1T' // 8a99 #45 + '4G' // 8a9a #110 + 'A' // 8a9b + '33A' // 8a9c #858 + 'A' // 8a9d + '238P' // 8a9e #6203 + '1T' // 8a9f #45 + '210A' // 8aa0 #5460 + '8U' // 8aa1 #228 + 'A' // 8aa2 + '8U' // 8aa3 #228 + '210T' // 8aa4 #5479 + '8U' // 8aa5 #228 + '63B' // 8aa6 #1639 + '4G' // 8aa7 #110 + '8U' // 8aa8 #228 + '89R' // 8aa9 #2331 + '239O' // 8aaa #6228 + 'A' // 8aab + '131H' // 8aac #3413 + '259W' // 8aad #6756 + '41J' // 8aae #1075 + '28H' // 8aaf #735 + '213A' // 8ab0 #5538 + 'A' // 8ab1 + '212B' // 8ab2 #5513 + '1T' // 8ab3 #45 + '33A' // 8ab4 #858 + 'A' // 8ab5 + '4G' // 8ab6 #110 + '1T' // 8ab7 #45 + '27G' // 8ab8 #708 + '142K' // 8ab9 #3702 + 'A' // 8aba + '1T' // 8abb #45 + '163I' // 8abc #4246 + 'A' // 8abd + '8U' // 8abe #228 + '231K' // 8abf #6016 + 'aA' // 8ac0-8ac1 + '17W' // 8ac2 #464 + '1T' // 8ac3 #45 + '17W' // 8ac4 #464 + 'A' // 8ac5 + '4G' // 8ac6 #110 + '213W' // 8ac7 #5560 + '1T' // 8ac8 #45 + '4G' // 8ac9 #110 + '1T' // 8aca #45 + '241M' // 8acb #6278 + '4G' // 8acc #110 + '17W' // 8acd #464 + 'A' // 8ace + '89V' // 8acf #2335 + '1T' // 8ad0 #45 + '4G' // 8ad1 #110 + '172N' // 8ad2 #4485 + 'b1T' // 8ad3-8ad5 #45 + '234I' // 8ad6 #6092 + '138N' // 8ad7 #3601 + 'aA' // 8ad8-8ad9 + '28H' // 8ada #735 + '8U' // 8adb #228 + '144O' // 8adc #3758 + 'a4G' // 8add-8ade #110 + '10R' // 8adf #277 + '4G' // 8ae0 #110 + '8U' // 8ae1 #228 + '4G' // 8ae2 #110 + 'A' // 8ae3 + '4G' // 8ae4 #110 + 'A' // 8ae5 + '89Y' // 8ae6 #2338 + '41B' // 8ae7 #1067 + '27G' // 8ae8 #708 + 'A' // 8ae9 + '90C' // 8aea #2342 + '8U' // 8aeb #228 + '1T' // 8aec #45 + '89W' // 8aed #2336 + '185C' // 8aee #4812 + 'A' // 8aef + '1T' // 8af0 #45 + '8U' // 8af1 #228 + '24W' // 8af2 #646 + 'a10R' // 8af3-8af4 #277 + '4G' // 8af5 #110 + '17W' // 8af6 #464 + '41B' // 8af7 #1067 + '182Q' // 8af8 #4748 + '33A' // 8af9 #858 + '8U' // 8afa #228 + 'A' // 8afb + '4G' // 8afc #110 + 'A' // 8afd + '203U' // 8afe #5298 + '1T' // 8aff #45 + '66O' // 8b00 #1730 + '8U' // 8b01 #228 + '192H' // 8b02 #4999 + 'A' // 8b03 + '17W' // 8b04 #464 + '4G' // 8b05 #110 + '1T' // 8b06 #45 + '10R' // 8b07 #277 + 'aA' // 8b08-8b09 + '144P' // 8b0a #3759 + '4G' // 8b0b #110 + '10R' // 8b0c #277 + '4G' // 8b0d #110 + '168X' // 8b0e #4391 + '4G' // 8b0f #110 + '8U' // 8b10 #228 + '1T' // 8b11 #45 + 'A' // 8b12 + '24W' // 8b13 #646 + '17W' // 8b14 #464 + 'A' // 8b15 + '17W' // 8b16 #464 + '153P' // 8b17 #3993 + 'A' // 8b18 + '160H' // 8b19 #4167 + '17W' // 8b1a #464 + '221M' // 8b1b #5758 + '4G' // 8b1c #110 + '228C' // 8b1d #5930 + '1T' // 8b1e #45 + '28H' // 8b1f #735 + '170K' // 8b20 #4430 + '253L' // 8b21 #6589 + '24W' // 8b22 #646 + 'bA' // 8b23-8b25 + '10R' // 8b26 #277 + 'A' // 8b27 + '8U' // 8b28 #228 + 'aA' // 8b29-8b2a + '17W' // 8b2b #464 + '63B' // 8b2c #1639 + '55D' // 8b2d #1433 + '24W' // 8b2e #646 + 'A' // 8b2f + '1K' // 8b30 #36 + 'aA' // 8b31-8b32 + '89S' // 8b33 #2332 + 'bA' // 8b34-8b36 + '1K' // 8b37 #36 + 'A' // 8b38 + '166L' // 8b39 #4327 + 'aA' // 8b3a-8b3b + '1K' // 8b3c #36 + 'A' // 8b3d + '132K' // 8b3e #3442 + '90D' // 8b3f #2343 + 'A' // 8b40 + '89M' // 8b41 #2326 + '1K' // 8b42 #36 + '55D' // 8b43 #1433 + '1K' // 8b44 #36 + '248M' // 8b45 #6460 + '55E' // 8b46 #1434 + 'A' // 8b47 + '1K' // 8b48 #36 + '232A' // 8b49 #6032 + 'aA' // 8b4a-8b4b + '89O' // 8b4c #2328 + '55E' // 8b4d #1434 + '89N' // 8b4e #2327 + '54Z' // 8b4f #1429 + 'A' // 8b50 + 'a1K' // 8b51-8b52 #36 + 'a20A' // 8b53-8b54 #520 + 'A' // 8b55 + '20A' // 8b56 #520 + '27G' // 8b57 #708 + '68K' // 8b58 #1778 + '20A' // 8b59 #520 + '153D' // 8b5a #3981 + '1K' // 8b5b #36 + '202C' // 8b5c #5254 + 'A' // 8b5d + '89D' // 8b5e #2317 + '20A' // 8b5f #520 + '89K' // 8b60 #2324 + 'A' // 8b61 + '54Y' // 8b62 #1428 + '1K' // 8b63 #36 + 'aA' // 8b64-8b65 + '211G' // 8b66 #5492 + 'aA' // 8b67-8b68 + '28G' // 8b69 #734 + '89L' // 8b6a #2325 + '20A' // 8b6b #520 + '54Z' // 8b6c #1429 + '20A' // 8b6d #520 + 'A' // 8b6e + '192F' // 8b6f #4997 + '228S' // 8b70 #5946 + '1K' // 8b71 #36 + '255U' // 8b72 #6650 + 'A' // 8b73 + '127E' // 8b74 #3306 + 'A' // 8b75 + '1K' // 8b76 #36 + '68R' // 8b77 #1785 + 'a1K' // 8b78-8b79 #36 + 'aA' // 8b7a-8b7b + '1K' // 8b7c #36 + '177S' // 8b7d #4620 + '20A' // 8b7e #520 + '1K' // 8b7f #36 + '224Q' // 8b80 #5840 + '28G' // 8b81 #734 + 'A' // 8b82 + '89F' // 8b83 #2319 + 'a1K' // 8b84-8b85 #36 + 'cA' // 8b86-8b89 + '68O' // 8b8a #1782 + '1K' // 8b8b #36 + '55B' // 8b8c #1431 + '1K' // 8b8d #36 + '20A' // 8b8e #520 + '28G' // 8b8f #734 + '89E' // 8b90 #2318 + 'A' // 8b91 + '55A' // 8b92 #1430 + '68O' // 8b93 #1782 + '1K' // 8b94 #36 + '20A' // 8b95 #520 + '55A' // 8b96 #1430 + 'aA' // 8b97-8b98 + '55B' // 8b99 #1431 + '217A' // 8b9a #5642 + '54Y' // 8b9b #1428 + '11W' // 8b9c #308 + '1K' // 8b9d #36 + 'a11W' // 8b9e-8b9f #308 + '89C' // 8ba0 #2316 + '3Q' // 8ba1 #94 + '2D' // 8ba2 #55 + '3D' // 8ba3 #81 + '3Q' // 8ba4 #94 + 'b3D' // 8ba5-8ba7 #81 + '3I' // 8ba8 #86 + '1Z' // 8ba9 #51 + 'b3D' // 8baa-8bac #81 + '3I' // 8bad #86 + 'b3Q' // 8bae-8bb0 #94 + 'A' // 8bb1 + '2C' // 8bb2 #54 + '2W' // 8bb3 #74 + 'a3D' // 8bb4-8bb5 #81 + '3X' // 8bb6 #101 + '3D' // 8bb7 #81 + '1Z' // 8bb8 #51 + '3D' // 8bb9 #81 + '7K' // 8bba #192 + 'A' // 8bbb + '3X' // 8bbc #101 + '2K' // 8bbd #62 + '65M' // 8bbe #1702 + '2D' // 8bbf #55 + '2R' // 8bc0 #69 + '3Q' // 8bc1 #94 + 'a3D' // 8bc2-8bc3 #81 + '3Q' // 8bc4 #94 + '3D' // 8bc5 #81 + '1Z' // 8bc6 #51 + 'A' // 8bc7 + '4C' // 8bc8 #106 + '2D' // 8bc9 #55 + '1R' // 8bca #43 + 'a3D' // 8bcb-8bcc #81 + '2D' // 8bcd #55 + 'b3D' // 8bce-8bd0 #81 + '3N' // 8bd1 #91 + 'b3D' // 8bd2-8bd4 #81 + '1Z' // 8bd5 #51 + '3D' // 8bd6 #81 + '3Y' // 8bd7 #102 + 'a3D' // 8bd8-8bd9 #81 + '2C' // 8bda #54 + '3G' // 8bdb #84 + '3D' // 8bdc #81 + '7K' // 8bdd #192 + '1R' // 8bde #43 + '3D' // 8bdf #81 + 'a2K' // 8be0-8be1 #62 + '63N' // 8be2 #1651 + 'a3D' // 8be3-8be4 #81 + '1Z' // 8be5 #51 + '2D' // 8be6 #55 + 'b3D' // 8be7-8be9 #81 + 'A' // 8bea + 'a3D' // 8beb-8bec #81 + '1Z' // 8bed #51 + '3D' // 8bee #81 + '3I' // 8bef #86 + '3D' // 8bf0 #81 + '3N' // 8bf1 #91 + 'a3D' // 8bf2-8bf3 #81 + '7K' // 8bf4 #192 + '3G' // 8bf5 #84 + '3D' // 8bf6 #81 + '7K' // 8bf7 #192 + '4C' // 8bf8 #106 + '3D' // 8bf9 #81 + '3Y' // 8bfa #102 + '3Q' // 8bfb #94 + 'a3D' // 8bfc-8bfd #81 + '3I' // 8bfe #86 + 'a3D' // 8bff-8c00 #81 + '2C' // 8c01 #54 + '3D' // 8c02 #81 + '1Z' // 8c03 #51 + '3D' // 8c04 #81 + '3H' // 8c05 #85 + 'a3D' // 8c06-8c07 #81 + '2C' // 8c08 #54 + 'A' // 8c09 + '2R' // 8c0a #69 + '4C' // 8c0b #106 + '3D' // 8c0c #81 + '3H' // 8c0d #85 + '2K' // 8c0e #62 + '3D' // 8c0f #81 + '2L' // 8c10 #63 + 'a3D' // 8c11-8c12 #81 + '1R' // 8c13 #43 + 'g3D' // 8c14-8c1b #81 + '2Y' // 8c1c #76 + 'd3D' // 8c1d-8c21 #81 + '2D' // 8c22 #55 + '2L' // 8c23 #63 + 'a3D' // 8c24-8c25 #81 + '3H' // 8c26 #85 + '3D' // 8c27 #81 + '4C' // 8c28 #106 + '2R' // 8c29 #69 + 'b3D' // 8c2a-8c2c #81 + '2R' // 8c2d #69 + 'b3D' // 8c2e-8c30 #81 + '3N' // 8c31 #91 + 'd3D' // 8c32-8c36 #81 + '206F' // 8c37 #5361 + '1K' // 8c38 #36 + '11W' // 8c39 #308 + '1K' // 8c3a #36 + 'aA' // 8c3b-8c3c + 'a1K' // 8c3d-8c3e #36 + '44K' // 8c3f #1154 + 'A' // 8c40 + '132J' // 8c41 #3441 + 'bA' // 8c42-8c44 + '1K' // 8c45 #36 + '204O' // 8c46 #5318 + '11V' // 8c47 #307 + '132I' // 8c48 #3440 + '11V' // 8c49 #307 + '89J' // 8c4a #2323 + '11V' // 8c4b #307 + '44K' // 8c4c #1154 + 'A' // 8c4d + '127F' // 8c4e #3307 + '11V' // 8c4f #307 + '217B' // 8c50 #5643 + '11W' // 8c51 #308 + 'A' // 8c52 + '1K' // 8c53 #36 + '163G' // 8c54 #4244 + '44K' // 8c55 #1154 + '3D' // 8c56 #81 + 'b1K' // 8c57-8c59 #36 + '149D' // 8c5a #3877 + '1K' // 8c5b #36 + 'A' // 8c5c + '1K' // 8c5d #36 + 'bA' // 8c5e-8c60 + '223A' // 8c61 #5798 + '11V' // 8c62 #307 + 'a1K' // 8c63-8c64 #36 + 'A' // 8c65 + '1K' // 8c66 #36 + 'A' // 8c67 + '11V' // 8c68 #307 + '1K' // 8c69 #36 + '210Q' // 8c6a #5476 + '163F' // 8c6b #4243 + '192G' // 8c6c #4998 + '1K' // 8c6d #36 + 'dA' // 8c6e-8c72 + '11V' // 8c73 #307 + 'A' // 8c74 + 'a1K' // 8c75-8c76 #36 + 'A' // 8c77 + '11V' // 8c78 #307 + '159O' // 8c79 #4148 + '44J' // 8c7a #1153 + '11W' // 8c7b #308 + '1K' // 8c7c #36 + 'A' // 8c7d + '1K' // 8c7e #36 + 'bA' // 8c7f-8c81 + '44J' // 8c82 #1153 + 'aA' // 8c83-8c84 + '11V' // 8c85 #307 + 'a1K' // 8c86-8c87 #36 + 'A' // 8c88 + '11V' // 8c89 #307 + '44J' // 8c8a #1153 + '1K' // 8c8b #36 + '188L' // 8c8c #4899 + '11V' // 8c8d #307 + '248L' // 8c8e #6459 + 'A' // 8c8f + '11V' // 8c90 #307 + 'A' // 8c91 + '1K' // 8c92 #36 + '206U' // 8c93 #5376 + '11V' // 8c94 #307 + 'bA' // 8c95-8c97 + '11V' // 8c98 #307 + '11W' // 8c99 #308 + 'A' // 8c9a + '28G' // 8c9b #734 + '1K' // 8c9c #36 + '209B' // 8c9d #5435 + '168B' // 8c9e #4369 + '28G' // 8c9f #734 + '222A' // 8ca0 #5772 + '228H' // 8ca1 #5935 + '182H' // 8ca2 #4739 + '55C' // 8ca3 #1432 + '11W' // 8ca4 #308 + 'aA' // 8ca5-8ca6 + '168W' // 8ca7 #4390 + '229C' // 8ca8 #5956 + '199H' // 8ca9 #5181 + '166G' // 8caa #4322 + '168P' // 8cab #4383 + '229Q' // 8cac #5970 + '11W' // 8cad #308 + '1K' // 8cae #36 + '89I' // 8caf #2322 + '89H' // 8cb0 #2321 + 'A' // 8cb1 + '11W' // 8cb2 #308 + '63P' // 8cb3 #1653 + '215Z' // 8cb4 #5615 + 'A' // 8cb5 + '63P' // 8cb6 #1653 + '237U' // 8cb7 #6182 + '176O' // 8cb8 #4590 + '11V' // 8cb9 #307 + '11W' // 8cba #308 + '241R' // 8cbb #6283 + '227P' // 8cbc #5917 + '44I' // 8cbd #1152 + 'A' // 8cbe + '201O' // 8cbf #5240 + '183X' // 8cc0 #4781 + 'a44I' // 8cc1-8cc2 #1152 + '149G' // 8cc3 #3880 + '44I' // 8cc4 #1152 + '11W' // 8cc5 #308 + '1K' // 8cc6 #36 + '241Z' // 8cc7 #6291 + '156K' // 8cc8 #4066 + '1K' // 8cc9 #36 + '174I' // 8cca #4532 + '1K' // 8ccb #36 + 'A' // 8ccc + '89B' // 8ccd #2315 + '1K' // 8cce #36 + '11W' // 8ccf #308 + 'A' // 8cd0 + '89G' // 8cd1 #2320 + '11W' // 8cd2 #308 + '201A' // 8cd3 #5226 + '55C' // 8cd4 #1432 + '11W' // 8cd5 #308 + '28G' // 8cd6 #734 + 'aA' // 8cd7-8cd8 + '11W' // 8cd9 #308 + '89A' // 8cda #2314 + '88W' // 8cdb #2310 + '153C' // 8cdc #3980 + '1K' // 8cdd #36 + '221L' // 8cde #5757 + 'A' // 8cdf + '174B' // 8ce0 #4525 + '13A' // 8ce1 #338 + '189V' // 8ce2 #4935 + '231Z' // 8ce3 #6031 + '144N' // 8ce4 #3757 + 'A' // 8ce5 + '163E' // 8ce6 #4242 + '4R' // 8ce7 #121 + '16K' // 8ce8 #426 + '24U' // 8ce9 #644 + '238F' // 8cea #6193 + '19Z' // 8ceb #519 + '163H' // 8cec #4245 + '166R' // 8ced #4333 + 'A' // 8cee + '1K' // 8cef #36 + '13A' // 8cf0 #338 + '1K' // 8cf1 #36 + '32Z' // 8cf2 #857 + 'A' // 8cf3 + '67K' // 8cf4 #1752 + '1K' // 8cf5 #36 + 'A' // 8cf6 + '32Z' // 8cf7 #857 + '16K' // 8cf8 #426 + 'A' // 8cf9 + '67F' // 8cfa #1747 + '88Q' // 8cfb #2304 + '237B' // 8cfc #6163 + '216Z' // 8cfd #5641 + '13A' // 8cfe #338 + '1K' // 8cff #36 + 'A' // 8d00 + '1K' // 8d01 #36 + 'A' // 8d02 + '32Z' // 8d03 #857 + '28F' // 8d04 #733 + '125R' // 8d05 #3267 + 'A' // 8d06 + '28F' // 8d07 #733 + '203Y' // 8d08 #5302 + '1K' // 8d09 #36 + '67K' // 8d0a #1752 + '13A' // 8d0b #338 + '24U' // 8d0c #644 + '28F' // 8d0d #733 + '1K' // 8d0e #36 + '185B' // 8d0f #4811 + '13A' // 8d10 #338 + '24U' // 8d11 #644 + '13A' // 8d12 #338 + '28F' // 8d13 #733 + '16K' // 8d14 #426 + 'A' // 8d15 + '127D' // 8d16 #3305 + '13A' // 8d17 #338 + '24U' // 8d18 #644 + 'aA' // 8d19-8d1a + '16K' // 8d1b #426 + '13A' // 8d1c #338 + '88S' // 8d1d #2306 + '3H' // 8d1e #85 + '2C' // 8d1f #54 + '4R' // 8d20 #121 + '1R' // 8d21 #43 + '2D' // 8d22 #55 + '1Z' // 8d23 #51 + '1R' // 8d24 #43 + '3Y' // 8d25 #102 + 'a2D' // 8d26-8d27 #55 + '3Q' // 8d28 #94 + '2K' // 8d29 #62 + '2Y' // 8d2a #76 + '4C' // 8d2b #106 + '3W' // 8d2c #100 + '1Z' // 8d2d #51 + '2W' // 8d2e #74 + '4C' // 8d2f #106 + '4R' // 8d30 #121 + '3H' // 8d31 #85 + 'a4R' // 8d32-8d33 #121 + '1Z' // 8d34 #51 + '2C' // 8d35 #54 + '4R' // 8d36 #121 + '4C' // 8d37 #106 + '3Y' // 8d38 #102 + '3Q' // 8d39 #94 + '4C' // 8d3a #106 + '4R' // 8d3b #121 + '2Y' // 8d3c #76 + '4R' // 8d3d #121 + '2L' // 8d3e #63 + '3G' // 8d3f #84 + '4R' // 8d40 #121 + '2R' // 8d41 #69 + 'a4R' // 8d42-8d43 #121 + '7K' // 8d44 #192 + 'a4R' // 8d45-8d46 #121 + 'A' // 8d47 + 'b4R' // 8d48-8d4a #121 + 'a2L' // 8d4b-8d4c #63 + '4R' // 8d4d #121 + '3W' // 8d4e #100 + '3I' // 8d4f #86 + '2K' // 8d50 #62 + '4R' // 8d51 #121 + 'A' // 8d52 + '4R' // 8d53 #121 + '4C' // 8d54 #106 + '4R' // 8d55 #121 + '1R' // 8d56 #43 + 'A' // 8d57 + '2W' // 8d58 #74 + '4R' // 8d59 #121 + '3N' // 8d5a #91 + '2C' // 8d5b #54 + 'a4R' // 8d5c-8d5d #121 + '2C' // 8d5e #54 + '4R' // 8d5f #121 + '1R' // 8d60 #43 + '4R' // 8d61 #121 + '3I' // 8d62 #86 + '2K' // 8d63 #62 + '184B' // 8d64 #4785 + '1K' // 8d65 #36 + '88U' // 8d66 #2308 + '16K' // 8d67 #426 + '19Z' // 8d68 #519 + '13A' // 8d69 #338 + '4R' // 8d6a #121 + '170I' // 8d6b #4428 + '13A' // 8d6c #338 + '16K' // 8d6d #426 + '13A' // 8d6e #338 + 'A' // 8d6f + '228M' // 8d70 #5940 + '248K' // 8d71 #6458 + 'A' // 8d72 + '28F' // 8d73 #733 + '180M' // 8d74 #4692 + '3N' // 8d75 #91 + '88R' // 8d76 #2305 + '241S' // 8d77 #6284 + 'aA' // 8d78-8d79 + '24U' // 8d7a #644 + '19Z' // 8d7b #519 + 'A' // 8d7c + '19Z' // 8d7d #519 + 'A' // 8d7e + '1K' // 8d7f #36 + 'A' // 8d80 + '170J' // 8d81 #4429 + '32Z' // 8d82 #857 + 'A' // 8d83 + '16K' // 8d84 #426 + '242A' // 8d85 #6292 + 'aA' // 8d86-8d87 + '1K' // 8d88 #36 + 'A' // 8d89 + '223H' // 8d8a #5805 + '1R' // 8d8b #43 + 'A' // 8d8c + '1K' // 8d8d #36 + 'aA' // 8d8e-8d8f + 'a16K' // 8d90-8d91 #426 + 'aA' // 8d92-8d93 + '24V' // 8d94 #645 + '67F' // 8d95 #1747 + '19Z' // 8d96 #519 + 'aA' // 8d97-8d98 + '177R' // 8d99 #4619 + 'aA' // 8d9a-8d9b + '19Z' // 8d9c #519 + 'A' // 8d9d + '1K' // 8d9e #36 + '156L' // 8d9f #4067 + '1K' // 8da0 #36 + 'aA' // 8da1-8da2 + '228L' // 8da3 #5939 + 'aA' // 8da4-8da5 + '32Z' // 8da6 #857 + 'A' // 8da7 + '185A' // 8da8 #4810 + '24U' // 8da9 #644 + 'A' // 8daa + '13A' // 8dab #338 + '1K' // 8dac #36 + 'aA' // 8dad-8dae + '16K' // 8daf #426 + 'A' // 8db0 + '4R' // 8db1 #121 + '13A' // 8db2 #338 + '230U' // 8db3 #6000 + '66P' // 8db4 #1731 + '16K' // 8db5 #426 + 'A' // 8db6 + '13A' // 8db7 #338 + '4R' // 8db8 #121 + '1K' // 8db9 #36 + '28F' // 8dba #733 + '1K' // 8dbb #36 + '16K' // 8dbc #426 + 'A' // 8dbd + '121U' // 8dbe #3166 + '24V' // 8dbf #645 + '44F' // 8dc0 #1149 + 'A' // 8dc1 + '11U' // 8dc2 #306 + '88Y' // 8dc3 #2312 + '4R' // 8dc4 #121 + '1N' // 8dc5 #39 + '28E' // 8dc6 #732 + 'a1N' // 8dc7-8dc8 #39 + 'A' // 8dc9 + '1N' // 8dca #39 + '28E' // 8dcb #732 + '184Y' // 8dcc #4808 + 'A' // 8dcd + '11U' // 8dce #306 + '28E' // 8dcf #732 + '19Z' // 8dd0 #519 + '214I' // 8dd1 #5572 + 'aA' // 8dd2-8dd3 + '44F' // 8dd4 #1149 + '1N' // 8dd5 #39 + 'a11U' // 8dd6-8dd7 #306 + 'A' // 8dd8 + '1N' // 8dd9 #39 + '11U' // 8dda #306 + '28E' // 8ddb #732 + 'A' // 8ddc + '204L' // 8ddd #5315 + '4R' // 8dde #121 + '224P' // 8ddf #5839 + 'A' // 8de0 + '198L' // 8de1 #5159 + 'A' // 8de2 + '28E' // 8de3 #732 + '11U' // 8de4 #306 + '1N' // 8de5 #39 + 'A' // 8de6 + '1N' // 8de7 #39 + '208K' // 8de8 #5418 + '24V' // 8de9 #645 + '149Z' // 8dea #3899 + 'a11U' // 8deb-8dec #306 + 'aA' // 8ded-8dee + '241W' // 8def #6288 + '69X' // 8df0 #1817 + '11U' // 8df1 #306 + '1N' // 8df2 #39 + '218M' // 8df3 #5680 + '1N' // 8df4 #39 + '255C' // 8df5 #6632 + 'c4R' // 8df6-8df9 #121 + '24V' // 8dfa #645 + '4R' // 8dfb #121 + 'a11U' // 8dfc-8dfd #306 + 'A' // 8dfe + '1N' // 8dff #39 + 'A' // 8e00 + '44F' // 8e01 #1149 + 'aA' // 8e02-8e03 + '1N' // 8e04 #39 + '11U' // 8e05 #306 + '1N' // 8e06 #39 + '4R' // 8e07 #121 + '1N' // 8e08 #39 + '11U' // 8e09 #306 + '88V' // 8e0a #2309 + '1N' // 8e0b #39 + '69X' // 8e0c #1817 + 'A' // 8e0d + '24V' // 8e0e #645 + '190D' // 8e0f #4943 + '156I' // 8e10 #4064 + '1N' // 8e11 #39 + 'aA' // 8e12-8e13 + '11U' // 8e14 #306 + 'A' // 8e15 + '44H' // 8e16 #1151 + 'A' // 8e17 + '19Z' // 8e18 #519 + 'cA' // 8e19-8e1c + '11U' // 8e1d #306 + '28E' // 8e1e #732 + '11U' // 8e1f #306 + 'a44H' // 8e20-8e21 #1151 + '184Z' // 8e22 #4809 + '11U' // 8e23 #306 + 'aA' // 8e24-8e25 + '11U' // 8e26 #306 + '44H' // 8e27 #1151 + '19Z' // 8e28 #519 + '177C' // 8e29 #4604 + '88T' // 8e2a #2307 + '24V' // 8e2b #645 + '4R' // 8e2c #121 + 'a24V' // 8e2d-8e2e #645 + '4R' // 8e2f #121 + '54W' // 8e30 #1426 + '37X' // 8e31 #985 + 'A' // 8e32 + '1N' // 8e33 #39 + '121T' // 8e34 #3165 + '54W' // 8e35 #1426 + 'b1N' // 8e36-8e38 #39 + '37X' // 8e39 #985 + '88X' // 8e3a #2311 + 'A' // 8e3b + '88Z' // 8e3c #2313 + '37X' // 8e3d #985 + 'aA' // 8e3e-8e3f + '37X' // 8e40 #985 + '54X' // 8e41 #1427 + '44G' // 8e42 #1150 + 'A' // 8e43 + '127C' // 8e44 #3304 + 'A' // 8e45 + '24U' // 8e46 #644 + '44G' // 8e47 #1150 + '156J' // 8e48 #4065 + 'a44G' // 8e49-8e4a #1150 + '54X' // 8e4b #1427 + 'b88P' // 8e4c-8e4e #2303 + '22K' // 8e4f #582 + '13W' // 8e50 #360 + 'a8P' // 8e51-8e52 #223 + '37W' // 8e53 #984 + 'a5Q' // 8e54-8e55 #146 + 'bA' // 8e56-8e58 + '12Z' // 8e59 #337 + '37W' // 8e5a #984 + '5Q' // 8e5b #146 + 'b1N' // 8e5c-8e5e #39 + '177Q' // 8e5f #4618 + '12Z' // 8e60 #337 + '1N' // 8e61 #39 + 'a5Q' // 8e62-8e63 #146 + '216Y' // 8e64 #5640 + 'A' // 8e65 + '126I' // 8e66 #3284 + '19Y' // 8e67 #518 + '32X' // 8e68 #855 + '13W' // 8e69 #360 + 'aA' // 8e6a-8e6b + '13W' // 8e6c #360 + '88B' // 8e6d #2289 + 'A' // 8e6e + '5Q' // 8e6f #146 + '49O' // 8e70 #1288 + '22K' // 8e71 #582 + '138L' // 8e72 #3599 + 'A' // 8e73 + '88F' // 8e74 #2293 + '22K' // 8e75 #582 + '12Z' // 8e76 #337 + '22K' // 8e77 #582 + 'A' // 8e78 + '1N' // 8e79 #39 + 'b13W' // 8e7a-8e7c #360 + 'A' // 8e7d + '32X' // 8e7e #855 + '8P' // 8e7f #223 + '32X' // 8e80 #855 + '144M' // 8e81 #3756 + '5Q' // 8e82 #146 + '1N' // 8e83 #39 + 'a13W' // 8e84-8e85 #360 + 'A' // 8e86 + '88C' // 8e87 #2290 + 'A' // 8e88 + '13W' // 8e89 #360 + '88D' // 8e8a #2291 + '13W' // 8e8b #360 + 'A' // 8e8c + '183R' // 8e8d #4775 + 'A' // 8e8e + '8P' // 8e8f #223 + '13W' // 8e90 #360 + 'b5Q' // 8e91-8e93 #146 + '13W' // 8e94 #360 + '5Q' // 8e95 #146 + 'aA' // 8e96-8e97 + '1N' // 8e98 #39 + '49O' // 8e99 #1288 + '5Q' // 8e9a #146 + '1N' // 8e9b #39 + '8P' // 8e9c #223 + '5Q' // 8e9d #146 + '13W' // 8e9e #360 + 'aA' // 8e9f-8ea0 + '5Q' // 8ea1 #146 + '1N' // 8ea2 #39 + '19Y' // 8ea3 #518 + 'A' // 8ea4 + 'a19Y' // 8ea5-8ea6 #518 + '22K' // 8ea7 #582 + 'A' // 8ea8 + '1N' // 8ea9 #39 + '12Z' // 8eaa #337 + '242T' // 8eab #6311 + '12Z' // 8eac #337 + '5Q' // 8ead #146 + '1N' // 8eae #39 + '248J' // 8eaf #6457 + '13W' // 8eb0 #360 + '1N' // 8eb1 #39 + '169U' // 8eb2 #4414 + '1N' // 8eb3 #39 + 'A' // 8eb4 + '1N' // 8eb5 #39 + '88A' // 8eb6 #2288 + 'aA' // 8eb7-8eb8 + '19Y' // 8eb9 #518 + '163D' // 8eba #4241 + '1N' // 8ebb #39 + '19Y' // 8ebc #518 + 'A' // 8ebd + '1N' // 8ebe #39 + 'A' // 8ebf + '121S' // 8ec0 #3164 + '1N' // 8ec1 #39 + 'A' // 8ec2 + '22K' // 8ec3 #582 + 'a1N' // 8ec4-8ec5 #39 + '49O' // 8ec6 #1288 + 'a1N' // 8ec7-8ec8 #39 + 'A' // 8ec9 + '243Q' // 8eca #6334 + '12Z' // 8ecb #337 + '181I' // 8ecc #4714 + '220A' // 8ecd #5720 + '88K' // 8ece #2298 + '5Q' // 8ecf #146 + 'A' // 8ed0 + '5Q' // 8ed1 #146 + '182O' // 8ed2 #4746 + 'A' // 8ed3 + '5Q' // 8ed4 #146 + 'aA' // 8ed5-8ed6 + '19Y' // 8ed7 #518 + 'aA' // 8ed8-8ed9 + '37W' // 8eda #984 + '5Q' // 8edb #146 + '1N' // 8edc #39 + 'aA' // 8edd-8ede + '218Y' // 8edf #5692 + 'aA' // 8ee0-8ee1 + '88I' // 8ee2 #2296 + '1N' // 8ee3 #39 + '32X' // 8ee4 #855 + 'bA' // 8ee5-8ee7 + '5Q' // 8ee8 #146 + 'aA' // 8ee9-8eea + '12Z' // 8eeb #337 + 'A' // 8eec + '22K' // 8eed #582 + '1N' // 8eee #39 + 'A' // 8eef + 'a1N' // 8ef0-8ef1 #39 + '19Y' // 8ef2 #518 + 'cA' // 8ef3-8ef6 + '1N' // 8ef7 #39 + '175D' // 8ef8 #4553 + 'a5Q' // 8ef9-8efa #146 + '12Z' // 8efb #337 + '13W' // 8efc #360 + '259K' // 8efd #6744 + '12Z' // 8efe #337 + 'A' // 8eff + '1N' // 8f00 #39 + 'A' // 8f01 + '1N' // 8f02 #39 + '228R' // 8f03 #5945 + 'A' // 8f04 + '32Y' // 8f05 #856 + 'A' // 8f06 + 'a5Q' // 8f07-8f08 #146 + '238R' // 8f09 #6205 + '5Q' // 8f0a #146 + '37W' // 8f0b #984 + '251Z' // 8f0c #6551 + 'aA' // 8f0d-8f0e + 'a1N' // 8f0f-8f10 #39 + 'A' // 8f11 + 'a12Z' // 8f12-8f13 #337 + '195Z' // 8f14 #5095 + '68H' // 8f15 #1775 + '1N' // 8f16 #39 + '5Q' // 8f17 #146 + '1N' // 8f18 #39 + '5Q' // 8f19 #146 + '19Y' // 8f1a #518 + '170H' // 8f1b #4427 + '32Y' // 8f1c #856 + '190N' // 8f1d #4953 + '32Y' // 8f1e #856 + '12Z' // 8f1f #337 + 'a1N' // 8f20-8f21 #39 + 'A' // 8f22 + '1N' // 8f23 #39 + 'A' // 8f24 + '5Q' // 8f25 #146 + '32Y' // 8f26 #856 + '69W' // 8f27 #1816 + '1N' // 8f28 #39 + '190W' // 8f29 #4962 + '212K' // 8f2a #5522 + 'a1N' // 8f2b-8f2c #39 + '5Q' // 8f2d #146 + '1N' // 8f2e #39 + '68H' // 8f2f #1775 + '19Y' // 8f30 #518 + 'aA' // 8f31-8f32 + '32Y' // 8f33 #856 + 'a1N' // 8f34-8f35 #39 + '13W' // 8f36 #360 + '1N' // 8f37 #39 + '219Y' // 8f38 #5718 + '69W' // 8f39 #1816 + '1N' // 8f3a #39 + '127B' // 8f3b #3303 + '32X' // 8f3c #855 + 'A' // 8f3d + '12Z' // 8f3e #337 + '129V' // 8f3f #3375 + 'b5Q' // 8f40-8f42 #146 + '1N' // 8f43 #39 + '147Z' // 8f44 #3847 + '12Z' // 8f45 #337 + 'a5Q' // 8f46-8f47 #146 + 'A' // 8f48 + '239N' // 8f49 #6227 + '22K' // 8f4a #582 + 'A' // 8f4b + '1N' // 8f4c #39 + '12Z' // 8f4d #337 + '138K' // 8f4e #3598 + '1N' // 8f4f #39 + 'A' // 8f50 + '1N' // 8f51 #39 + 'a2G' // 8f52-8f53 #58 + 'a44E' // 8f54-8f55 #1148 + 'A' // 8f56 + 'a2G' // 8f57-8f58 #58 + 'bA' // 8f59-8f5b + '37V' // 8f5c #983 + '54U' // 8f5d #1424 + '2G' // 8f5e #58 + '173B' // 8f5f #4499 + 'A' // 8f60 + '44E' // 8f61 #1148 + '54U' // 8f62 #1424 + '2G' // 8f63 #58 + '44E' // 8f64 #1148 + '2G' // 8f65 #58 + '144L' // 8f66 #3755 + '88L' // 8f67 #2299 + '1R' // 8f68 #43 + '2L' // 8f69 #63 + 'A' // 8f6a + '8P' // 8f6b #223 + '3Q' // 8f6c #94 + '8P' // 8f6d #223 + '88M' // 8f6e #2300 + '2D' // 8f6f #55 + '2Y' // 8f70 #76 + 'b8P' // 8f71-8f73 #223 + '2L' // 8f74 #63 + 'a8P' // 8f75-8f76 #223 + 'A' // 8f77 + 'b8P' // 8f78-8f7a #223 + '2D' // 8f7b #55 + '8P' // 8f7c #223 + '3Q' // 8f7d #94 + '8P' // 8f7e #223 + '2K' // 8f7f #62 + 'A' // 8f80 + 'a8P' // 8f81-8f82 #223 + '2D' // 8f83 #55 + '8P' // 8f84 #223 + '3N' // 8f85 #91 + '4C' // 8f86 #106 + '8P' // 8f87 #223 + '4C' // 8f88 #106 + '1R' // 8f89 #43 + 'a8P' // 8f8a-8f8b #223 + 'A' // 8f8c + 'b8P' // 8f8d-8f8f #223 + '3H' // 8f90 #85 + '2D' // 8f91 #55 + 'A' // 8f92 + '88N' // 8f93 #2301 + 'a8P' // 8f94-8f95 #223 + '3H' // 8f96 #85 + 'c8P' // 8f97-8f9a #223 + '197W' // 8f9b #5144 + '138I' // 8f9c #3596 + '2G' // 8f9d #58 + '256W' // 8f9e #6678 + '87X' // 8f9f #2285 + '37V' // 8fa0 #983 + 'a2G' // 8fa1-8fa2 #58 + '194I' // 8fa3 #5052 + '2G' // 8fa4 #58 + '37V' // 8fa5 #983 + '224O' // 8fa6 #5838 + '87Z' // 8fa7 #2287 + '170G' // 8fa8 #4426 + '2Y' // 8fa9 #76 + 'A' // 8faa + '2W' // 8fab #74 + 'A' // 8fac + '184V' // 8fad #4805 + '54V' // 8fae #1425 + '156H' // 8faf #4063 + '166X' // 8fb0 #4339 + '181O' // 8fb1 #4720 + '210O' // 8fb2 #5474 + '88O' // 8fb3 #2302 + '2G' // 8fb4 #58 + 'a54V' // 8fb5-8fb6 #1425 + '87W' // 8fb7 #2284 + '37V' // 8fb8 #983 + '131M' // 8fb9 #3418 + '88H' // 8fba #2295 + '88G' // 8fbb #2294 + '88J' // 8fbc #2297 + '1R' // 8fbd #43 + '132H' // 8fbe #3439 + '88E' // 8fbf #2292 + '2G' // 8fc0 #58 + '87Y' // 8fc1 #2286 + '87V' // 8fc2 #2283 + 'A' // 8fc3 + '130Q' // 8fc4 #3396 + '181K' // 8fc5 #4716 + '22J' // 8fc6 #581 + '7K' // 8fc7 #192 + '248I' // 8fc8 #6456 + 'A' // 8fc9 + '28C' // 8fca #730 + '69V' // 8fcb #1815 + '32W' // 8fcc #854 + '22I' // 8fcd #580 + '228K' // 8fce #5938 + '44B' // 8fcf #1145 + '138J' // 8fd0 #3597 + '239E' // 8fd1 #6218 + '22J' // 8fd2 #581 + '22I' // 8fd3 #580 + '216Q' // 8fd4 #5632 + '22I' // 8fd5 #580 + 'A' // 8fd6 + '8P' // 8fd7 #223 + 'a7K' // 8fd8-8fd9 #192 + '87N' // 8fda #2275 + '7K' // 8fdb #192 + '2D' // 8fdc #55 + '2C' // 8fdd #54 + '1Z' // 8fde #51 + '2L' // 8fdf #63 + '2G' // 8fe0 #58 + 'A' // 8fe1 + 'c22I' // 8fe2-8fe5 #580 + '141P' // 8fe6 #3681 + 'A' // 8fe7 + '22I' // 8fe8 #580 + '69V' // 8fe9 #1815 + '199N' // 8fea #5187 + '198G' // 8feb #5154 + 'A' // 8fec + '54S' // 8fed #1422 + '22I' // 8fee #580 + '2G' // 8fef #58 + '215B' // 8ff0 #5591 + '2G' // 8ff1 #58 + '260Q' // 8ff2 #6776 + '8P' // 8ff3 #223 + '192D' // 8ff4 #4995 + '22J' // 8ff5 #581 + '2G' // 8ff6 #58 + '220O' // 8ff7 #5734 + '22I' // 8ff8 #580 + '126Z' // 8ff9 #3301 + '22I' // 8ffa #580 + '22J' // 8ffb #581 + '32W' // 8ffc #854 + '231J' // 8ffd #6015 + '22J' // 8ffe #581 + 'A' // 8fff + '220Y' // 9000 #5744 + '238Q' // 9001 #6204 + '87L' // 9002 #2273 + '191B' // 9003 #4967 + '4Y' // 9004 #128 + '63A' // 9005 #1638 + '191E' // 9006 #4970 + 'A' // 9007 + '87M' // 9008 #2274 + '3Q' // 9009 #94 + '2Y' // 900a #76 + '12Y' // 900b #336 + '2G' // 900c #58 + '144K' // 900d #3754 + '2G' // 900e #58 + '227O' // 900f #5916 + '194Q' // 9010 #5060 + '12Y' // 9011 #336 + '87U' // 9012 #2282 + '2G' // 9013 #58 + '205W' // 9014 #5352 + '64E' // 9015 #1668 + '4Y' // 9016 #128 + '159K' // 9017 #4144 + '2G' // 9018 #58 + '240Y' // 9019 #6264 + '35U' // 901a #930 + '192E' // 901b #4996 + 'A' // 901c + '166W' // 901d #4338 + '12Y' // 901e #336 + '235Y' // 901f #6134 + '235M' // 9020 #6122 + '12Y' // 9021 #336 + '181H' // 9022 #4713 + '244T' // 9023 #6363 + '32W' // 9024 #854 + 'A' // 9025 + '14X' // 9026 #387 + 'c2G' // 9027-902a #58 + 'A' // 902b + '2G' // 902c #58 + '4Y' // 902d #128 + '154S' // 902e #4022 + '4Y' // 902f #128 + 'A' // 9030 + '230T' // 9031 #5999 + '242D' // 9032 #6295 + '28C' // 9033 #730 + '22J' // 9034 #581 + '12Y' // 9035 #336 + '4Y' // 9036 #128 + '28C' // 9037 #730 + '182D' // 9038 #4735 + '69U' // 9039 #1814 + '14X' // 903a #387 + '2R' // 903b #69 + '184W' // 903c #4806 + 'A' // 903d + '177P' // 903e #4617 + '2G' // 903f #58 + 'A' // 9040 + '12Y' // 9041 #336 + '143F' // 9042 #3723 + '2G' // 9043 #58 + '4Y' // 9044 #128 + '257L' // 9045 #6693 + '87S' // 9046 #2280 + '219B' // 9047 #5695 + 'A' // 9048 + '2G' // 9049 #58 + '241P' // 904a #6281 + '243I' // 904b #6326 + '28C' // 904c #730 + '187M' // 904d #4874 + '242K' // 904e #6302 + '4Y' // 904f #128 + 'a12Y' // 9050-9051 #336 + '4Y' // 9052 #128 + '244B' // 9053 #6345 + '237S' // 9054 #6180 + '206J' // 9055 #5365 + '28C' // 9056 #730 + '3I' // 9057 #86 + '4Y' // 9058 #128 + '187D' // 9059 #4865 + 'A' // 905a + '4Y' // 905b #128 + '49D' // 905c #1277 + '12Y' // 905d #336 + '67E' // 905e #1746 + 'A' // 905f + '222K' // 9060 #5782 + '87P' // 9061 #2277 + '4Y' // 9062 #128 + '149I' // 9063 #3882 + '44B' // 9064 #1145 + '254S' // 9065 #6622 + 'a2G' // 9066-9067 #58 + '4Y' // 9068 #128 + '230L' // 9069 #5991 + 'aA' // 906a-906b + '28C' // 906c #730 + '209E' // 906d #5438 + '173W' // 906e #4520 + '12Y' // 906f #336 + '2G' // 9070 #58 + 'A' // 9071 + '49D' // 9072 #1277 + 'A' // 9073 + '4Y' // 9074 #128 + '187H' // 9075 #4869 + '127A' // 9076 #3302 + '174H' // 9077 #4531 + '244D' // 9078 #6347 + '4Y' // 9079 #128 + '203X' // 907a #5301 + 'A' // 907b + '141U' // 907c #3686 + '87Q' // 907d #2278 + 'A' // 907e + '210P' // 907f #5475 + '192C' // 9080 #4994 + '49D' // 9081 #1277 + '63A' // 9082 #1638 + '12Y' // 9083 #336 + '241C' // 9084 #6268 + '4Y' // 9085 #128 + 'A' // 9086 + 'a12Y' // 9087-9088 #336 + '252N' // 9089 #6565 + '226L' // 908a #5887 + '4Y' // 908b #128 + '2G' // 908c #58 + 'A' // 908d + '2G' // 908e #58 + '66F' // 908f #1721 + '22J' // 9090 #581 + '124T' // 9091 #3243 + 'A' // 9092 + '2L' // 9093 #63 + 'A' // 9094 + '12Y' // 9095 #336 + 'A' // 9096 + '4Y' // 9097 #128 + '2G' // 9098 #58 + '12Y' // 9099 #336 + '14X' // 909a #387 + '4Y' // 909b #128 + 'A' // 909c + '14X' // 909d #387 + '32W' // 909e #854 + 'A' // 909f + 'a4Y' // 90a0-90a1 #128 + '87J' // 90a2 #2271 + '234B' // 90a3 #6085 + 'A' // 90a4 + '2G' // 90a5 #58 + '209U' // 90a6 #5454 + 'A' // 90a7 + '64E' // 90a8 #1668 + '14X' // 90a9 #387 + '176E' // 90aa #4580 + 'A' // 90ab + '14X' // 90ac #387 + 'A' // 90ad + '131O' // 90ae #3420 + '54S' // 90af #1422 + '12Y' // 90b0 #336 + '170F' // 90b1 #4425 + 'b4Y' // 90b2-90b4 #128 + '132G' // 90b5 #3438 + '4Y' // 90b6 #128 + 'A' // 90b7 + '130R' // 90b8 #3397 + '3X' // 90b9 #101 + '14X' // 90ba #387 + '87T' // 90bb #2281 + 'A' // 90bc + 'a4Y' // 90bd-90be #128 + 'aA' // 90bf-90c0 + '173K' // 90c1 #4508 + 'A' // 90c2 + 'b4Y' // 90c3-90c5 #128 + 'A' // 90c6 + '4Y' // 90c7 #128 + '69U' // 90c8 #1814 + '2G' // 90c9 #58 + '154C' // 90ca #4006 + 'A' // 90cb + '2G' // 90cc #58 + 'A' // 90cd + '191O' // 90ce #4980 + 'a14X' // 90cf-90d0 #387 + '3Y' // 90d1 #102 + '2G' // 90d2 #58 + '14X' // 90d3 #387 + '32W' // 90d4 #854 + '4Y' // 90d5 #128 + 'A' // 90d6 + '4Y' // 90d7 #128 + 'a2G' // 90d8-90d9 #58 + '14X' // 90da #387 + 'a4Y' // 90db-90dc #128 + '87O' // 90dd #2276 + '69T' // 90de #1813 + '22J' // 90df #581 + 'A' // 90e0 + '137A' // 90e1 #3562 + '10Q' // 90e2 #276 + '44D' // 90e3 #1147 + '10Q' // 90e4 #276 + '2G' // 90e5 #58 + 'a14X' // 90e6-90e7 #387 + '35U' // 90e8 #930 + 'A' // 90e9 + '54T' // 90ea #1423 + '10Q' // 90eb #276 + 'A' // 90ec + '187R' // 90ed #4879 + 'A' // 90ee + '10Q' // 90ef #276 + '2G' // 90f0 #58 + 'A' // 90f1 + '2G' // 90f2 #58 + 'A' // 90f3 + '10Q' // 90f4 #276 + '219X' // 90f5 #5717 + '2G' // 90f6 #58 + '256M' // 90f7 #6668 + '3G' // 90f8 #84 + 'bA' // 90f9-90fb + '44D' // 90fc #1147 + '243E' // 90fd #6322 + 'b10Q' // 90fe-9100 #276 + 'A' // 9101 + '87K' // 9102 #2272 + 'A' // 9103 + '10Q' // 9104 #276 + '2G' // 9105 #58 + '28D' // 9106 #731 + 'A' // 9107 + '2G' // 9108 #58 + '214G' // 9109 #5570 + 'aA' // 910a-910b + '14X' // 910c #387 + '2G' // 910d #58 + 'aA' // 910e-910f + '2G' // 9110 #58 + 'A' // 9111 + '54R' // 9112 #1421 + 'A' // 9113 + '10Q' // 9114 #276 + '69T' // 9115 #1813 + 'a28D' // 9116-9117 #731 + '10Q' // 9118 #276 + '87I' // 9119 #2270 + '28D' // 911a #731 + 'A' // 911b + '10Q' // 911c #276 + 'A' // 911d + '10Q' // 911e #276 + 'A' // 911f + '10Q' // 9120 #276 + 'A' // 9121 + 'a10Q' // 9122-9123 #276 + 'A' // 9124 + '2G' // 9125 #58 + 'A' // 9126 + '66F' // 9127 #1721 + 'A' // 9128 + '28D' // 9129 #731 + 'A' // 912a + '44D' // 912b #1147 + 'A' // 912c + '67E' // 912d #1746 + '2G' // 912e #58 + '10Q' // 912f #276 + '184X' // 9130 #4807 + '10Q' // 9131 #276 + '54R' // 9132 #1421 + 'A' // 9133 + '28D' // 9134 #731 + 'A' // 9135 + '28D' // 9136 #731 + '2G' // 9137 #58 + 'A' // 9138 + 'a10Q' // 9139-913a #276 + 'A' // 913b + 'a2G' // 913c-913d #58 + 'dA' // 913e-9142 + '32V' // 9143 #853 + 'aA' // 9144-9145 + '32V' // 9146 #853 + '2T' // 9147 #71 + '44C' // 9148 #1146 + 'a87H' // 9149-914a #2269 + '121R' // 914b #3163 + '144J' // 914c #3753 + '237T' // 914d #6181 + '87R' // 914e #2279 + '32V' // 914f #853 + '54T' // 9150 #1423 + '44B' // 9151 #1145 + '222Q' // 9152 #5788 + '2T' // 9153 #71 + '255G' // 9154 #6636 + 'A' // 9155 + '44C' // 9156 #1146 + '32V' // 9157 #853 + '2T' // 9158 #71 + '44C' // 9159 #1146 + '32V' // 915a #853 + '2T' // 915b #71 + '24T' // 915c #643 + '14X' // 915d #387 + '87F' // 915e #2267 + 'aA' // 915f-9160 + '86X' // 9161 #2259 + '87C' // 9162 #2264 + 'a9Q' // 9163-9164 #250 + '170E' // 9165 #4424 + 'A' // 9166 + '13V' // 9167 #359 + 'A' // 9168 + '64D' // 9169 #1667 + '152Q' // 916a #3968 + 'A' // 916b + '154M' // 916c #4016 + '2T' // 916d #71 + '54Q' // 916e #1420 + '126J' // 916f #3285 + '32U' // 9170 #852 + '2L' // 9171 #63 + '9Q' // 9172 #250 + '2T' // 9173 #71 + '9Q' // 9174 #250 + '160T' // 9175 #4179 + '87G' // 9176 #2268 + '197C' // 9177 #5124 + '203Q' // 9178 #5294 + 'a9Q' // 9179-917a #250 + '2T' // 917b #71 + '24T' // 917c #643 + 'a50J' // 917d-917e #1309 + '3H' // 917f #85 + 'A' // 9180 + '7L' // 9181 #193 + '9Q' // 9182 #250 + '132F' // 9183 #3437 + '22H' // 9184 #579 + '9Q' // 9185 #250 + '7L' // 9186 #193 + '65Y' // 9187 #1714 + 'A' // 9188 + '177O' // 9189 #4616 + '2T' // 918a #71 + '65Y' // 918b #1714 + '32U' // 918c #852 + '86Y' // 918d #2260 + '7L' // 918e #193 + 'A' // 918f + '86Z' // 9190 #2261 + '9Q' // 9191 #250 + '208Y' // 9192 #5432 + 'b2T' // 9193-9195 #71 + '22H' // 9196 #579 + 'a2T' // 9197-9198 #71 + 'A' // 9199 + '32U' // 919a #852 + '54Q' // 919b #1420 + '173D' // 919c #4501 + 'A' // 919d + '7L' // 919e #193 + 'aA' // 919f-91a0 + 'a9Q' // 91a1-91a2 #250 + '32U' // 91a3 #852 + '254B' // 91a4 #6605 + 'A' // 91a5 + '2T' // 91a6 #71 + '22H' // 91a7 #579 + '35Y' // 91a8 #934 + '24T' // 91a9 #643 + '9Q' // 91aa #250 + '224N' // 91ab #5837 + '184U' // 91ac #4804 + '35Y' // 91ad #934 + 'a37U' // 91ae-91af #982 + '7L' // 91b0 #193 + '86V' // 91b1 #2257 + '7L' // 91b2 #193 + '2T' // 91b3 #71 + 'a37U' // 91b4-91b5 #982 + '13V' // 91b6 #359 + 'A' // 91b7 + '253K' // 91b8 #6588 + 'A' // 91b9 + '138H' // 91ba #3595 + '13V' // 91bb #359 + '2T' // 91bc #71 + '7L' // 91bd #193 + '32U' // 91be #852 + '2T' // 91bf #71 + '163B' // 91c0 #4239 + '9Q' // 91c1 #250 + '2T' // 91c2 #71 + '7L' // 91c3 #193 + '13V' // 91c4 #359 + '7L' // 91c5 #193 + '9Q' // 91c6 #250 + '194P' // 91c7 #5059 + '254M' // 91c8 #6616 + '86U' // 91c9 #2256 + '3Y' // 91ca #102 + '206T' // 91cb #5375 + '228P' // 91cc #5943 + '243C' // 91cd #6320 + '224B' // 91ce #5825 + '236G' // 91cf #6142 + '64D' // 91d0 #1667 + '69B' // 91d1 #1795 + '50J' // 91d2 #1309 + 'a7L' // 91d3-91d4 #193 + '22H' // 91d5 #579 + '13V' // 91d6 #359 + '37U' // 91d7 #982 + '160C' // 91d8 #4162 + '7L' // 91d9 #193 + 'a2T' // 91da-91db #71 + '136P' // 91dc #3551 + '212P' // 91dd #5527 + '2T' // 91de #71 + '13V' // 91df #359 + 'A' // 91e0 + '2T' // 91e1 #71 + 'A' // 91e2 + '190Q' // 91e3 #4956 + '7L' // 91e4 #193 + '13V' // 91e5 #359 + '9Q' // 91e6 #250 + '87A' // 91e7 #2262 + '22H' // 91e8 #579 + '7L' // 91e9 #193 + '248H' // 91ea #6455 + 'A' // 91eb + '7L' // 91ec #193 + '9Q' // 91ed #250 + 'a2T' // 91ee-91ef #71 + '35Y' // 91f0 #934 + '7L' // 91f1 #193 + 'bA' // 91f2-91f4 + '37U' // 91f5 #982 + 'a7L' // 91f6-91f7 #193 + '22H' // 91f8 #579 + '9Q' // 91f9 #250 + '22H' // 91fa #579 + 'a2T' // 91fb-91fc #71 + '7L' // 91fd #193 + '24T' // 91fe #643 + '7L' // 91ff #193 + '9Q' // 9200 #250 + '7L' // 9201 #193 + 'A' // 9202 + '22H' // 9203 #579 + '7L' // 9204 #193 + '9Q' // 9205 #250 + '7L' // 9206 #193 + '9Q' // 9207 #250 + '22F' // 9208 #577 + 'a12X' // 9209-920a #335 + 'A' // 920b + '2T' // 920c #71 + '87B' // 920d #2263 + '12X' // 920e #335 + 'A' // 920f + '32T' // 9210 #851 + '54P' // 9211 #1419 + '248G' // 9212 #6454 + '13V' // 9213 #359 + '156G' // 9214 #4062 + '163C' // 9215 #4240 + '2T' // 9216 #71 + '54P' // 9217 #1419 + '13V' // 9218 #359 + '22G' // 9219 #578 + 'aA' // 921a-921b + '12X' // 921c #335 + '2T' // 921d #71 + '65P' // 921e #1705 + 'aA' // 921f-9220 + '22F' // 9221 #577 + 'A' // 9222 + '65B' // 9223 #1691 + 'a12X' // 9224-9225 #335 + '65B' // 9226 #1691 + '22F' // 9227 #577 + '13V' // 9228 #359 + '35Y' // 9229 #934 + '22F' // 922a #577 + '24T' // 922b #643 + '2T' // 922c #71 + '22F' // 922d #577 + '11T' // 922e #305 + '2T' // 922f #71 + '11T' // 9230 #305 + '22G' // 9231 #578 + 'A' // 9232 + '11T' // 9233 #305 + '183N' // 9234 #4771 + '12X' // 9235 #335 + '11T' // 9236 #305 + '12X' // 9237 #335 + '11T' // 9238 #305 + '12X' // 9239 #335 + '41A' // 923a #1066 + 'A' // 923b + '86W' // 923c #2258 + '22F' // 923d #577 + '12X' // 923e #335 + 'a32T' // 923f-9240 #851 + '22G' // 9241 #578 + 'a2T' // 9242-9243 #71 + '87E' // 9244 #2266 + '65P' // 9245 #1705 + '12X' // 9246 #335 + '2T' // 9247 #71 + '11T' // 9248 #305 + '32T' // 9249 #851 + '2T' // 924a #71 + '11T' // 924b #305 + '22G' // 924c #578 + '11T' // 924d #305 + 'a12X' // 924e-924f #335 + '11T' // 9250 #305 + '12X' // 9251 #335 + 'A' // 9252 + '22F' // 9253 #577 + 'A' // 9254 + '24T' // 9255 #643 + '2T' // 9256 #71 + '126Y' // 9257 #3300 + '13V' // 9258 #359 + '2T' // 9259 #71 + '11T' // 925a #305 + '148S' // 925b #3866 + '2T' // 925c #71 + '11T' // 925d #305 + '32T' // 925e #851 + '24T' // 925f #643 + 'a2T' // 9260-9261 #71 + '87D' // 9262 #2265 + 'A' // 9263 + '41A' // 9264 #1066 + 'a32T' // 9265-9266 #851 + '11T' // 9267 #305 + '35Y' // 9268 #934 + '2T' // 9269 #71 + 'A' // 926a + '22G' // 926b #578 + '22F' // 926c #577 + '22G' // 926d #578 + '13V' // 926e #359 + '12X' // 926f #335 + '2T' // 9270 #71 + '253W' // 9271 #6600 + '22G' // 9272 #578 + 'A' // 9273 + '3N' // 9274 #91 + '2T' // 9275 #71 + '11T' // 9276 #305 + '13V' // 9277 #359 + '11T' // 9278 #305 + '2T' // 9279 #71 + '22G' // 927a #578 + '12X' // 927b #335 + '11T' // 927c #305 + '2T' // 927d #71 + '12X' // 927e #335 + '17V' // 927f #463 + '222P' // 9280 #5787 + '9P' // 9281 #249 + '15I' // 9282 #398 + '86T' // 9283 #2255 + '9P' // 9284 #249 + '188M' // 9285 #4900 + '37T' // 9286 #981 + '50J' // 9287 #1309 + '32S' // 9288 #850 + '86L' // 9289 #2247 + '32S' // 928a #850 + 'aA' // 928b-928c + '17V' // 928d #463 + '32S' // 928e #850 + '9P' // 928f #249 + 'A' // 9290 + '44A' // 9291 #1144 + '2T' // 9292 #71 + '132E' // 9293 #3436 + 'A' // 9294 + '17V' // 9295 #463 + '44A' // 9296 #1144 + '2T' // 9297 #71 + '188V' // 9298 #4909 + '17V' // 9299 #463 + '86M' // 929a #2248 + '17V' // 929b #463 + '41A' // 929c #1066 + '37T' // 929d #981 + 'A' // 929e + '2T' // 929f #71 + '17V' // 92a0 #463 + 'b15I' // 92a1-92a3 #398 + 'a17V' // 92a4-92a5 #463 + '15I' // 92a6 #398 + '17V' // 92a7 #463 + '32S' // 92a8 #850 + 'a15I' // 92a9-92aa #398 + '32S' // 92ab #850 + '37T' // 92ac #981 + '256A' // 92ad #6656 + '37T' // 92ae #981 + '2T' // 92af #71 + 'A' // 92b0 + '9P' // 92b1 #249 + '17V' // 92b2 #463 + '163A' // 92b3 #4238 + 'A' // 92b4 + '15I' // 92b5 #398 + '44A' // 92b6 #1144 + '224M' // 92b7 #5836 + '2T' // 92b8 #71 + '41A' // 92b9 #1066 + '86K' // 92ba #2246 + '17V' // 92bb #463 + '9B' // 92bc #235 + '2X' // 92bd #75 + '9P' // 92be #249 + '28A' // 92bf #728 + '2X' // 92c0 #75 + '170D' // 92c1 #4423 + 'a9B' // 92c2-92c3 #235 + 'A' // 92c4 + '121Q' // 92c5 #3162 + '43Z' // 92c6 #1143 + '9B' // 92c7 #235 + '43Z' // 92c8 #1143 + 'a15I' // 92c9-92ca #398 + '9B' // 92cb #235 + '54M' // 92cc #1416 + '9B' // 92cd #235 + '2X' // 92ce #75 + '86J' // 92cf #2245 + '43Z' // 92d0 #1143 + '15I' // 92d1 #398 + '177N' // 92d2 #4615 + '2X' // 92d3 #75 + '9P' // 92d4 #249 + '9B' // 92d5 #235 + 'A' // 92d6 + '9B' // 92d7 #235 + '2X' // 92d8 #75 + '9B' // 92d9 #235 + 'A' // 92da + '9P' // 92db #249 + '2X' // 92dc #75 + '9B' // 92dd #235 + 'A' // 92de + '9B' // 92df #235 + 'a2X' // 92e0-92e1 #75 + 'A' // 92e2 + '28A' // 92e3 #728 + '54M' // 92e4 #1416 + '9B' // 92e5 #235 + '15I' // 92e6 #398 + '2X' // 92e7 #75 + 'a9B' // 92e8-92e9 #235 + '184S' // 92ea #4802 + '9P' // 92eb #249 + '54N' // 92ec #1417 + '86S' // 92ed #2254 + '9B' // 92ee #235 + '15I' // 92ef #398 + '40Z' // 92f0 #1065 + '15I' // 92f1 #398 + '9B' // 92f2 #235 + '86O' // 92f3 #2250 + '9P' // 92f4 #249 + 'A' // 92f5 + '15I' // 92f6 #398 + '2X' // 92f7 #75 + '132D' // 92f8 #3435 + '9B' // 92f9 #235 + '2X' // 92fa #75 + '9B' // 92fb #235 + '67P' // 92fc #1757 + '9P' // 92fd #249 + 'A' // 92fe + '2X' // 92ff #75 + '9B' // 9300 #235 + '15I' // 9301 #398 + '9B' // 9302 #235 + '9P' // 9303 #249 + '231Y' // 9304 #6030 + 'A' // 9305 + '86P' // 9306 #2251 + '9P' // 9307 #249 + '2X' // 9308 #75 + 'aA' // 9309-930a + '19X' // 930b #517 + 'A' // 930c + '2X' // 930d #75 + 'A' // 930e + '11S' // 930f #304 + '126W' // 9310 #3298 + '2X' // 9311 #75 + '22E' // 9312 #576 + 'A' // 9313 + '2X' // 9314 #75 + '13U' // 9315 #358 + 'aA' // 9316-9317 + '121P' // 9318 #3161 + '11S' // 9319 #304 + '28B' // 931a #729 + '19X' // 931b #517 + '2X' // 931c #75 + '11S' // 931d #304 + '28B' // 931e #729 + '43Y' // 931f #1142 + '148K' // 9320 #3858 + '28B' // 9321 #729 + '224L' // 9322 #5835 + '11S' // 9323 #304 + '28B' // 9324 #729 + '11S' // 9325 #304 + '188Y' // 9326 #4912 + '13U' // 9327 #358 + '28B' // 9328 #729 + '13U' // 9329 #358 + '11S' // 932a #304 + '162Y' // 932b #4236 + '86R' // 932c #2253 + '19X' // 932d #517 + '43Y' // 932e #1142 + '226S' // 932f #5894 + '9P' // 9330 #249 + '22E' // 9331 #576 + '260G' // 9332 #6766 + '11S' // 9333 #304 + '2X' // 9334 #75 + '13U' // 9335 #358 + '192B' // 9336 #4993 + '2X' // 9337 #75 + '19X' // 9338 #517 + 'A' // 9339 + 'a2X' // 933a-933b #75 + '19X' // 933c #517 + 'A' // 933d + '19O' // 933e #508 + 'A' // 933f + 'a22E' // 9340-9341 #576 + 'a9P' // 9342-9343 #249 + '28A' // 9344 #728 + 'a19X' // 9345-9346 #517 + '11S' // 9347 #304 + '43Y' // 9348 #1142 + '11S' // 9349 #304 + '177M' // 934a #4614 + '203M' // 934b #5290 + '19O' // 934c #508 + '144I' // 934d #3752 + '19O' // 934e #508 + '22E' // 934f #576 + 'b13U' // 9350-9352 #358 + '19O' // 9353 #508 + '28B' // 9354 #729 + '21G' // 9355 #552 + 'b13U' // 9356-9358 #358 + '22E' // 9359 #576 + '13U' // 935a #358 + '149C' // 935b #3876 + '13U' // 935c #358 + '19O' // 935d #508 + '13U' // 935e #358 + '22E' // 935f #576 + '13U' // 9360 #358 + 'A' // 9361 + '54O' // 9362 #1418 + '22E' // 9363 #576 + '11S' // 9364 #304 + '13U' // 9365 #358 + '22E' // 9366 #576 + '13U' // 9367 #358 + '54O' // 9368 #1418 + 'a13U' // 9369-936a #358 + '54N' // 936b #1417 + '11S' // 936c #304 + '2X' // 936d #75 + '86N' // 936e #2249 + '2X' // 936f #75 + 'a11S' // 9370-9371 #304 + 'A' // 9372 + '11S' // 9373 #304 + '28A' // 9374 #728 + '227B' // 9375 #5903 + '11S' // 9376 #304 + 'A' // 9377 + '9P' // 9378 #249 + '19X' // 9379 #517 + '11S' // 937a #304 + 'A' // 937b + '86Q' // 937c #2252 + '28A' // 937d #728 + '184R' // 937e #4801 + 'a2X' // 937f-9380 #75 + '28A' // 9381 #728 + '40Z' // 9382 #1065 + 'A' // 9383 + '9P' // 9384 #249 + '19X' // 9385 #517 + '9P' // 9386 #249 + '19X' // 9387 #517 + '2X' // 9388 #75 + 'A' // 9389 + '40Z' // 938a #1065 + '2X' // 938b #75 + '86E' // 938c #2240 + '2X' // 938d #75 + 'A' // 938e + '11R' // 938f #303 + '16J' // 9390 #425 + 'A' // 9391 + '2X' // 9392 #75 + '19O' // 9393 #508 + '22D' // 9394 #575 + '2X' // 9395 #75 + '67P' // 9396 #1757 + '11R' // 9397 #303 + '13T' // 9398 #357 + 'A' // 9399 + '22D' // 939a #575 + '13T' // 939b #357 + '16J' // 939c #425 + '32R' // 939d #849 + '13T' // 939e #357 + 'A' // 939f + '16J' // 93a0 #425 + '13T' // 93a1 #357 + '24S' // 93a2 #642 + '86B' // 93a3 #2237 + '2X' // 93a4 #75 + 'A' // 93a5 + '13T' // 93a6 #357 + '86D' // 93a7 #2239 + '2X' // 93a8 #75 + '13T' // 93a9 #357 + '32R' // 93aa #849 + '2X' // 93ab #75 + 'a22D' // 93ac-93ad #575 + '201T' // 93ae #5245 + '24S' // 93af #642 + '22D' // 93b0 #575 + 'a19O' // 93b1-93b2 #508 + '24S' // 93b3 #642 + 'a11R' // 93b4-93b5 #303 + '21G' // 93b6 #552 + 'a24S' // 93b7-93b8 #642 + 'a21G' // 93b9-93ba #552 + '11R' // 93bb #303 + '19O' // 93bc #508 + '24S' // 93bd #642 + '19O' // 93be #508 + '86G' // 93bf #2242 + '24S' // 93c0 #642 + '21G' // 93c1 #552 + '24S' // 93c2 #642 + '22D' // 93c3 #575 + '11R' // 93c4 #303 + '21G' // 93c5 #552 + '86C' // 93c6 #2238 + '11R' // 93c7 #303 + '184T' // 93c8 #4803 + '21G' // 93c9 #552 + 'c11R' // 93ca-93cd #303 + 'aA' // 93ce-93cf + '11R' // 93d0 #303 + '22D' // 93d1 #575 + 'A' // 93d2 + '27Y' // 93d3 #726 + 'aA' // 93d4-93d5 + 'b11R' // 93d6-93d8 #303 + '2X' // 93d9 #75 + 'A' // 93da + '16J' // 93db #425 + 'a13T' // 93dc-93dd #357 + '22D' // 93de #575 + '126X' // 93df #3299 + '32R' // 93e0 #849 + '215L' // 93e1 #5601 + '40Z' // 93e2 #1065 + 'A' // 93e3 + '22D' // 93e4 #575 + 'b2X' // 93e5-93e7 #75 + '13T' // 93e8 #357 + 'dA' // 93e9-93ed + '32R' // 93ee #849 + 'A' // 93ef + '32R' // 93f0 #849 + '27Y' // 93f1 #726 + 'A' // 93f2 + 'a16J' // 93f3-93f4 #425 + '13T' // 93f5 #357 + '260P' // 93f6 #6775 + '13T' // 93f7 #357 + '11R' // 93f8 #303 + '13T' // 93f9 #357 + '2X' // 93fa #75 + '11R' // 93fb #303 + 'A' // 93fc + '162Z' // 93fd #4237 + 'bA' // 93fe-9400 + '27Y' // 9401 #726 + '2X' // 9402 #75 + '13T' // 9403 #357 + '86A' // 9404 #2236 + 'aA' // 9405-9406 + '13T' // 9407 #357 + '27Y' // 9408 #726 + '2X' // 9409 #75 + 'bA' // 940a-940c + '2X' // 940d #75 + '21G' // 940e #552 + 'a11R' // 940f-9410 #303 + 'a19O' // 9411-9412 #508 + 'a11R' // 9413-9414 #303 + 'a21G' // 9415-9416 #552 + '11R' // 9417 #303 + '218H' // 9418 #5675 + '11R' // 9419 #303 + '21G' // 941a #552 + '16J' // 941b #425 + '5W' // 941c #152 + '27Z' // 941d #727 + '5W' // 941e #152 + '69S' // 941f #1812 + '24R' // 9420 #641 + '69S' // 9421 #1812 + 'a5W' // 9422-9423 #152 + '24R' // 9424 #641 + '86F' // 9425 #2241 + '27Z' // 9426 #727 + 'c24R' // 9427-942a #641 + '54K' // 942b #1414 + 'A' // 942c + '27Z' // 942d #727 + '43X' // 942e #1141 + '27Y' // 942f #726 + 'A' // 9430 + '2X' // 9431 #75 + 'a43X' // 9432-9433 #1141 + '2X' // 9434 #75 + '218C' // 9435 #5670 + '37S' // 9436 #980 + 'A' // 9437 + '54K' // 9438 #1414 + 'A' // 9439 + '43X' // 943a #1141 + '2X' // 943b #75 + 'A' // 943c + '37S' // 943d #980 + '27Z' // 943e #727 + '37S' // 943f #980 + '54L' // 9440 #1415 + '2X' // 9441 #75 + '16J' // 9442 #425 + '27Y' // 9443 #726 + '144H' // 9444 #3751 + '37S' // 9445 #980 + 'aA' // 9446-9447 + '2X' // 9448 #75 + 'A' // 9449 + '24Q' // 944a #640 + 'A' // 944b + '22C' // 944c #574 + '16J' // 944d #425 + 'bA' // 944e-9450 + '198C' // 9451 #5150 + '138D' // 9452 #3591 + '2N' // 9453 #65 + '54L' // 9454 #1415 + '22C' // 9455 #574 + 'aA' // 9456-9457 + '16J' // 9458 #425 + '2N' // 9459 #65 + '69R' // 945a #1811 + '85Y' // 945b #2234 + '2N' // 945c #65 + 'A' // 945d + '22C' // 945e #574 + '2N' // 945f #65 + '22C' // 9460 #574 + '2N' // 9461 #65 + 'a24Q' // 9462-9463 #640 + 'A' // 9464 + '27Z' // 9465 #727 + 'A' // 9466 + '16J' // 9467 #425 + '22C' // 9468 #574 + 'A' // 9469 + '22C' // 946a #574 + '156F' // 946b #4061 + '16J' // 946c #425 + '22C' // 946d #574 + '2N' // 946e #65 + '22C' // 946f #574 + '156E' // 9470 #4060 + '24Q' // 9471 #640 + '132C' // 9472 #3434 + '5W' // 9473 #152 + '24R' // 9474 #641 + '24Q' // 9475 #640 + '24R' // 9476 #641 + '24Q' // 9477 #640 + '5W' // 9478 #152 + '24R' // 9479 #641 + '5W' // 947a #152 + '27Z' // 947b #727 + '149Y' // 947c #3898 + '191Z' // 947d #4991 + '24Q' // 947e #640 + '85X' // 947f #2233 + '24R' // 9480 #641 + '24Q' // 9481 #640 + '5W' // 9482 #152 + 'a69R' // 9483-9484 #1811 + '85Z' // 9485 #2235 + 'a5W' // 9486-9487 #152 + '3I' // 9488 #86 + '2L' // 9489 #63 + 'h5W' // 948a-9492 #152 + '2R' // 9493 #69 + 'd5W' // 9494-9498 #152 + '3H' // 9499 #85 + '5W' // 949a #152 + '3W' // 949b #100 + 'a5W' // 949c-949d #152 + '3G' // 949e #84 + '86I' // 949f #2244 + '3W' // 94a0 #100 + '5W' // 94a1 #152 + '86H' // 94a2 #2243 + 'a5W' // 94a3-94a4 #152 + '2R' // 94a5 #69 + '2K' // 94a6 #62 + '3G' // 94a7 #84 + '5W' // 94a8 #152 + '3H' // 94a9 #85 + 'c5W' // 94aa-94ad #152 + '2L' // 94ae #63 + '5W' // 94af #152 + '3W' // 94b0 #100 + '2D' // 94b1 #55 + '5W' // 94b2 #152 + '2W' // 94b3 #74 + 'f5W' // 94b4-94ba #152 + '1R' // 94bb #43 + 'd5W' // 94bc-94c0 #152 + '54J' // 94c1 #1413 + '3G' // 94c2 #84 + '85T' // 94c3 #2229 + '5W' // 94c4 #152 + '2K' // 94c5 #62 + 'u5W' // 94c6-94db #152 + '85U' // 94dc #2230 + '2Y' // 94dd #76 + 'n5W' // 94de-94ec #152 + '2Y' // 94ed #76 + 'c5W' // 94ee-94f1 #152 + '3X' // 94f2 #101 + '5W' // 94f3 #152 + 'A' // 94f4 + '5V' // 94f5 #151 + '54J' // 94f6 #1413 + '5V' // 94f7 #151 + '2R' // 94f8 #69 + '5V' // 94f9 #151 + '3Y' // 94fa #102 + 'b5V' // 94fb-94fd #151 + '1Z' // 94fe #51 + '5V' // 94ff #151 + '1Z' // 9500 #51 + '3Y' // 9501 #102 + '2W' // 9502 #74 + 'a5V' // 9503-9504 #151 + '1R' // 9505 #43 + 'a5V' // 9506-9507 #151 + '2L' // 9508 #63 + 'a5V' // 9509-950a #151 + '3N' // 950b #91 + '2K' // 950c #62 + 'b5V' // 950d-950f #151 + '2L' // 9510 #63 + 'g5V' // 9511-9518 #151 + '2D' // 9519 #55 + 'f5V' // 951a-9520 #151 + '4C' // 9521 #106 + '5V' // 9522 #151 + '2W' // 9523 #74 + '3H' // 9524 #85 + '3G' // 9525 #84 + '3N' // 9526 #91 + 'A' // 9527 + 'd5V' // 9528-952c #151 + '85S' // 952d #2228 + '1Z' // 952e #51 + '3X' // 952f #101 + 'j5V' // 9530-953a #151 + '2Y' // 953b #76 + 'c5V' // 953c-953f #151 + '3H' // 9540 #85 + 'a3W' // 9541-9542 #100 + 'c5V' // 9543-9546 #151 + '85V' // 9547 #2231 + 'h5V' // 9548-9550 #151 + '2W' // 9551 #74 + 'c5V' // 9552-9555 #151 + '3W' // 9556 #100 + 'd5V' // 9557-955b #151 + '3I' // 955c #86 + 'k5V' // 955d-9568 #151 + 'A' // 9569 + 'c5V' // 956a-956d #151 + 'A' // 956e + '2W' // 956f #74 + 'd5V' // 9570-9574 #151 + 'A' // 9575 + '3X' // 9576 #101 + '244M' // 9577 #6356 + '13S' // 9578 #356 + '2N' // 9579 #65 + 'cA' // 957a-957d + '2N' // 957e #65 + '144G' // 957f #3750 + '242H' // 9580 #6299 + 'A' // 9581 + '13S' // 9582 #356 + '201K' // 9583 #5236 + '2N' // 9584 #65 + '54I' // 9585 #1412 + '19W' // 9586 #516 + '2N' // 9587 #65 + '13S' // 9588 #356 + '223M' // 9589 #5810 + '2N' // 958a #65 + '246R' // 958b #6413 + 'a2N' // 958c-958d #65 + '19W' // 958e #516 + '37R' // 958f #979 + 'A' // 9590 + '142I' // 9591 #3700 + '216X' // 9592 #5639 + '245K' // 9593 #6380 + '65O' // 9594 #1704 + 'A' // 9595 + '85M' // 9596 #2222 + '54I' // 9597 #1412 + '132B' // 9598 #3433 + '19W' // 9599 #516 + 'aA' // 959a-959b + '85W' // 959c #2232 + '2N' // 959d #65 + 'a19W' // 959e-959f #516 + '85N' // 95a0 #2223 + '13S' // 95a1 #356 + '85R' // 95a2 #2227 + '182I' // 95a3 #4740 + '37R' // 95a4 #979 + '129T' // 95a5 #3373 + '19W' // 95a6 #516 + '13S' // 95a7 #356 + '65O' // 95a8 #1704 + '126U' // 95a9 #3296 + '43W' // 95aa #1140 + 'a19W' // 95ab-95ac #516 + '37R' // 95ad #979 + '43W' // 95ae #1140 + '5V' // 95af #151 + '43W' // 95b0 #1140 + '231X' // 95b1 #6029 + '85Q' // 95b2 #2226 + 'A' // 95b3 + '248E' // 95b4 #6452 + 'A' // 95b5 + '13S' // 95b6 #356 + 'A' // 95b7 + '5V' // 95b8 #151 + 'a19W' // 95b9-95ba #516 + 'a37R' // 95bb-95bc #979 + 'b19W' // 95bd-95bf #516 + 'b5V' // 95c0-95c2 #151 + '19W' // 95c3 #516 + 'a8O' // 95c4-95c5 #222 + '184Q' // 95c6 #4800 + '85O' // 95c7 #2224 + 'a43V' // 95c8-95c9 #1139 + '162V' // 95ca #4233 + '13S' // 95cb #356 + 'a43V' // 95cc-95cd #1139 + 'aA' // 95ce-95cf + '13S' // 95d0 #356 + 'a2N' // 95d1-95d2 #65 + '13S' // 95d3 #356 + 'a43U' // 95d4-95d5 #1138 + '170B' // 95d6 #4421 + 'A' // 95d7 + '257C' // 95d8 #6684 + '2N' // 95d9 #65 + '13S' // 95da #356 + 'A' // 95db + '245X' // 95dc #6393 + '2N' // 95dd #65 + '13S' // 95de #356 + '2N' // 95df #65 + '13S' // 95e0 #356 + '43U' // 95e1 #1138 + '138C' // 95e2 #3590 + 'A' // 95e3 + 'a13S' // 95e4-95e5 #356 + '2N' // 95e6 #65 + 'A' // 95e7 + '170C' // 95e8 #4422 + '8O' // 95e9 #222 + '3N' // 95ea #91 + '3W' // 95eb #100 + 'A' // 95ec + '2D' // 95ed #55 + '7K' // 95ee #192 + '2Y' // 95ef #76 + 'a8O' // 95f0-95f1 #222 + '3I' // 95f2 #86 + '8O' // 95f3 #222 + '35T' // 95f4 #929 + '3W' // 95f5 #100 + '8O' // 95f6 #222 + '2R' // 95f7 #69 + '3X' // 95f8 #101 + '1R' // 95f9 #43 + '2Y' // 95fa #76 + '3Q' // 95fb #94 + '8O' // 95fc #222 + '2Y' // 95fd #76 + 'a8O' // 95fe-95ff #222 + '3H' // 9600 #85 + '2L' // 9601 #63 + 'b8O' // 9602-9604 #222 + '1Z' // 9605 #51 + 'g8O' // 9606-960d #222 + '2W' // 960e #74 + '8O' // 960f #222 + '2K' // 9610 #62 + 'b8O' // 9611-9613 #222 + '2L' // 9614 #63 + 'b8O' // 9615-9617 #222 + 'A' // 9618 + 'b8O' // 9619-961b #222 + '85P' // 961c #2225 + '43V' // 961d #1139 + '2N' // 961e #65 + '2D' // 961f #55 + 'A' // 9620 + '43U' // 9621 #1138 + '2N' // 9622 #65 + 'A' // 9623 + '43S' // 9624 #1136 + 'a2N' // 9625-9626 #65 + 'A' // 9627 + '24O' // 9628 #638 + 'A' // 9629 + '184F' // 962a #4789 + 'A' // 962b + '2N' // 962c #65 + 'A' // 962d + '126T' // 962e #3295 + '24O' // 962f #638 + 'A' // 9630 + '138F' // 9631 #3593 + '229Z' // 9632 #5979 + '85A' // 9633 #2210 + '248F' // 9634 #6453 + '3Y' // 9635 #102 + '3N' // 9636 #91 + '2N' // 9637 #65 + '22B' // 9638 #573 + 'a2N' // 9639-963a #65 + '195C' // 963b #5072 + '24O' // 963c #638 + '69Q' // 963d #1810 + '8O' // 963e #222 + '227F' // 963f #5907 + '159I' // 9640 #4142 + '24O' // 9641 #638 + '54G' // 9642 #1410 + '37Q' // 9643 #978 + '226T' // 9644 #5895 + '85L' // 9645 #2221 + '2C' // 9646 #54 + '2W' // 9647 #74 + '2C' // 9648 #54 + '8O' // 9649 #222 + 'A' // 964a + '121O' // 964b #3160 + '162W' // 964c #4234 + '212W' // 964d #5534 + '37Q' // 964e #978 + '43S' // 964f #1136 + '238E' // 9650 #6192 + '37Q' // 9651 #978 + '2N' // 9652 #65 + '37Q' // 9653 #978 + '24O' // 9654 #638 + '1R' // 9655 #43 + '22B' // 9656 #573 + '2N' // 9657 #65 + '43S' // 9658 #1136 + 'aA' // 9659-965a + '85D' // 965b #2213 + 'c54G' // 965c-965f #1410 + 'A' // 9660 + '24O' // 9661 #638 + '229P' // 9662 #5969 + '203C' // 9663 #5280 + '237A' // 9664 #6162 + '254I' // 9665 #6612 + '2N' // 9666 #65 + 'a8O' // 9667-9668 #222 + '85K' // 9669 #2220 + '192A' // 966a #4992 + 'A' // 966b + '24O' // 966c #638 + 'A' // 966d + '2N' // 966e #65 + 'A' // 966f + '197H' // 9670 #5129 + 'A' // 9671 + '12W' // 9672 #334 + '226Q' // 9673 #5892 + '12W' // 9674 #334 + '160Y' // 9675 #4184 + '181W' // 9676 #4728 + '184P' // 9677 #4799 + '220X' // 9678 #5743 + 'A' // 9679 + '258J' // 967a #6717 + '22B' // 967b #573 + '2N' // 967c #65 + '221E' // 967d #5750 + 'a2N' // 967e-967f #65 + 'A' // 9680 + '22B' // 9681 #573 + '69Q' // 9682 #1810 + 'a24P' // 9683-9684 #639 + '85H' // 9685 #2217 + '209T' // 9686 #5453 + 'A' // 9687 + '85G' // 9688 #2216 + '24P' // 9689 #639 + '219W' // 968a #5716 + '84Y' // 968b #2208 + 'A' // 968c + '43R' // 968d #1135 + '212J' // 968e #5521 + '131E' // 968f #3410 + '2D' // 9690 #55 + '2N' // 9691 #65 + 'aA' // 9692-9693 + '209H' // 9694 #5441 + '43R' // 9695 #1135 + '22B' // 9696 #573 + '12W' // 9697 #334 + '43R' // 9698 #1135 + '142X' // 9699 #3715 + '2N' // 969a #65 + '237R' // 969b #6179 + '211O' // 969c #5500 + '2N' // 969d #65 + '54H' // 969e #1411 + '2N' // 969f #65 + '257K' // 96a0 #6692 + 'a17U' // 96a1-96a2 #462 + '85J' // 96a3 #2219 + '12W' // 96a4 #334 + '22B' // 96a5 #573 + '2N' // 96a6 #65 + '144F' // 96a7 #3749 + '68G' // 96a8 #1774 + '24P' // 96a9 #639 + '49F' // 96aa #1279 + 'A' // 96ab + '17U' // 96ac #462 + 'A' // 96ad + '24P' // 96ae #639 + '2N' // 96af #65 + '12W' // 96b0 #334 + '231W' // 96b1 #6028 + '2N' // 96b2 #65 + 'a12W' // 96b3-96b4 #334 + 'A' // 96b5 + '84Z' // 96b6 #2209 + '254F' // 96b7 #6609 + '138E' // 96b8 #3592 + '12W' // 96b9 #334 + '2N' // 96ba #65 + '199M' // 96bb #5186 + '85E' // 96bc #2214 + '12W' // 96bd #334 + '2D' // 96be #55 + 'A' // 96bf + '167K' // 96c0 #4352 + '121N' // 96c1 #3159 + 'A' // 96c2 + '17U' // 96c3 #462 + '227N' // 96c4 #5915 + '219I' // 96c5 #5702 + '239H' // 96c6 #6221 + '136X' // 96c7 #3559 + 'A' // 96c8 + '24N' // 96c9 #637 + '24P' // 96ca #639 + '24N' // 96cb #637 + '125E' // 96cc #3254 + '121M' // 96cd #3158 + '24N' // 96ce #637 + '3W' // 96cf #100 + 'A' // 96d0 + '259J' // 96d1 #6743 + '12W' // 96d2 #334 + 'aA' // 96d3-96d4 + '177L' // 96d5 #4613 + '49F' // 96d6 #1279 + 'A' // 96d7 + '24P' // 96d8 #639 + '68G' // 96d9 #1774 + '2N' // 96da #65 + '125I' // 96db #3258 + '49F' // 96dc #1279 + '24P' // 96dd #639 + '206S' // 96de #5374 + '2N' // 96df #65 + '8O' // 96e0 #222 + 'A' // 96e1 + '229Y' // 96e2 #5978 + '229O' // 96e3 #5968 + 'cA' // 96e4-96e7 + '211N' // 96e8 #5499 + '24N' // 96e9 #637 + '211J' // 96ea #5495 + '252P' // 96eb #6567 + 'bA' // 96ec-96ee + '156D' // 96ef #4059 + '85I' // 96f0 #2218 + '12W' // 96f1 #334 + '227J' // 96f2 #5911 + '2W' // 96f3 #74 + '17U' // 96f4 #462 + 'A' // 96f5 + '218K' // 96f6 #5678 + '209G' // 96f7 #5440 + 'A' // 96f8 + '24N' // 96f9 #637 + '2N' // 96fa #65 + '246P' // 96fb #6411 + 'aA' // 96fc-96fd + '1R' // 96fe #43 + '54H' // 96ff #1411 + '233I' // 9700 #6066 + '8O' // 9701 #222 + '12W' // 9702 #334 + '22B' // 9703 #573 + '126V' // 9704 #3297 + '12W' // 9705 #334 + '149X' // 9706 #3897 + '204G' // 9707 #5310 + '12W' // 9708 #334 + '138G' // 9709 #3594 + '255M' // 970a #6642 + 'aA' // 970b-970c + '162X' // 970d #4235 + 'a12W' // 970e-970f #334 + '17U' // 9710 #462 + '24N' // 9711 #637 + 'A' // 9712 + '126S' // 9713 #3294 + '2N' // 9714 #65 + 'A' // 9715 + '156C' // 9716 #4058 + 'aA' // 9717-9718 + '24N' // 9719 #637 + '2N' // 971a #65 + '22B' // 971b #573 + '180I' // 971c #4688 + '22A' // 971d #572 + '153Z' // 971e #4003 + 'a17U' // 971f-9720 #462 + '17T' // 9721 #461 + 'b1U' // 9722-9724 #46 + 'aA' // 9725-9726 + '188W' // 9727 #4910 + '22A' // 9728 #572 + 'A' // 9729 + '54F' // 972a #1409 + 'aA' // 972b-972c + '8O' // 972d #222 + 'aA' // 972e-972f + '43Q' // 9730 #1134 + '17T' // 9731 #461 + '220N' // 9732 #5733 + '1U' // 9733 #46 + '32Q' // 9734 #848 + 'A' // 9735 + '17T' // 9736 #461 + 'A' // 9737 + '199L' // 9738 #5185 + '131Z' // 9739 #3431 + '17U' // 973a #462 + '1U' // 973b #46 + 'A' // 973c + '43Q' // 973d #1134 + '121L' // 973e #3157 + 'A' // 973f + '32Q' // 9740 #848 + '17T' // 9741 #461 + '131Y' // 9742 #3430 + '1U' // 9743 #46 + '43Q' // 9744 #1134 + 'A' // 9745 + 'a22A' // 9746-9747 #572 + '216W' // 9748 #5638 + '54F' // 9749 #1409 + '17T' // 974a #461 + 'aA' // 974b-974c + 'b1U' // 974d-974f #46 + '8O' // 9750 #222 + '84X' // 9751 #2207 + '230S' // 9752 #5998 + '2Y' // 9753 #76 + 'A' // 9754 + '17T' // 9755 #461 + '153K' // 9756 #3988 + '17T' // 9757 #461 + '22A' // 9758 #572 + '258P' // 9759 #6723 + '170A' // 975a #4420 + '16I' // 975b #424 + '206P' // 975c #5371 + '17U' // 975d #462 + '236P' // 975e #6151 + '32Q' // 975f #848 + '206R' // 9760 #5373 + '132A' // 9761 #3432 + '49G' // 9762 #1280 + '41I' // 9763 #1074 + '1U' // 9764 #46 + '8N' // 9765 #221 + '16I' // 9766 #424 + '1U' // 9767 #46 + '22A' // 9768 #572 + '198I' // 9769 #5156 + 'a1U' // 976a-976b #46 + '43T' // 976c #1137 + '85C' // 976d #2212 + '1U' // 976e #46 + 'aA' // 976f-9770 + '22A' // 9771 #572 + 'A' // 9772 + '16I' // 9773 #424 + '169E' // 9774 #4398 + 'A' // 9775 + '16I' // 9776 #424 + '248C' // 9777 #6450 + 'a1U' // 9778-9779 #46 + '37P' // 977a #977 + '1U' // 977b #46 + '16I' // 977c #424 + '1U' // 977d #46 + 'A' // 977e + '1U' // 977f #46 + '22A' // 9780 #572 + '1U' // 9781 #46 + 'aA' // 9782-9783 + '85F' // 9784 #2215 + '16I' // 9785 #424 + '1U' // 9786 #46 + '32Q' // 9787 #848 + '17U' // 9788 #462 + '17T' // 9789 #461 + 'A' // 978a + '206Q' // 978b #5372 + 'A' // 978c + '141T' // 978d #3685 + '43T' // 978e #1137 + '37P' // 978f #977 + '1U' // 9790 #46 + 'a8N' // 9791-9792 #221 + 'A' // 9793 + '8N' // 9794 #221 + '41I' // 9795 #1074 + 'a1U' // 9796-9797 #46 + '85B' // 9798 #2211 + '1U' // 9799 #46 + '41I' // 979a #1074 + '32Q' // 979b #848 + '1U' // 979c #46 + '17U' // 979d #462 + '16I' // 979e #424 + '17T' // 979f #461 + '121K' // 97a0 #3156 + 'A' // 97a1 + '1U' // 97a2 #46 + '16I' // 97a3 #424 + 'A' // 97a4 + '43T' // 97a5 #1137 + '16I' // 97a6 #424 + 'A' // 97a7 + '37P' // 97a8 #977 + 'aA' // 97a9-97aa + '37P' // 97ab #977 + '16I' // 97ac #424 + '147Q' // 97ad #3838 + '16I' // 97ae #424 + '8N' // 97af #221 + 'A' // 97b0 + '17T' // 97b1 #461 + '54E' // 97b2 #1408 + '1U' // 97b3 #46 + '54E' // 97b4 #1408 + '1U' // 97b5 #46 + '22A' // 97b6 #572 + '43P' // 97b7 #1133 + '12V' // 97b8 #333 + '19V' // 97b9 #515 + '12V' // 97ba #333 + 'A' // 97bb + '1U' // 97bc #46 + '17S' // 97bd #460 + '12V' // 97be #333 + '19V' // 97bf #515 + '17S' // 97c0 #460 + '19V' // 97c1 #515 + '17S' // 97c2 #460 + '19V' // 97c3 #515 + 'a1U' // 97c4-97c5 #46 + '54B' // 97c6 #1405 + '1U' // 97c7 #46 + '12V' // 97c8 #333 + '19V' // 97c9 #515 + '1U' // 97ca #46 + '156A' // 97cb #4056 + '144E' // 97cc #3748 + '19V' // 97cd #515 + '1U' // 97ce #46 + 'A' // 97cf + 'a1U' // 97d0-97d1 #46 + '17S' // 97d2 #460 + '228I' // 97d3 #5936 + '1U' // 97d4 #46 + 'A' // 97d5 + '43P' // 97d6 #1133 + '1U' // 97d7 #46 + '43N' // 97d8 #1131 + '19V' // 97d9 #515 + 'A' // 97da + '1U' // 97db #46 + '54B' // 97dc #1405 + 'a19V' // 97dd-97de #515 + 'A' // 97df + '12V' // 97e0 #333 + '43N' // 97e1 #1131 + 'aA' // 97e2-97e3 + '1U' // 97e4 #46 + 'A' // 97e5 + '84P' // 97e6 #2199 + '3X' // 97e7 #101 + 'A' // 97e8 + '2C' // 97e9 #54 + 'a8N' // 97ea-97eb #221 + '2K' // 97ec #62 + '84O' // 97ed #2198 + '43N' // 97ee #1131 + '1U' // 97ef #46 + '43P' // 97f0 #1133 + '19V' // 97f1 #515 + '12V' // 97f2 #333 + '238C' // 97f3 #6190 + '1U' // 97f4 #46 + '84Q' // 97f5 #2200 + '126Q' // 97f6 #3292 + 'a1U' // 97f7-97f8 #46 + '27X' // 97f9 #725 + '6V' // 97fa #177 + '166H' // 97fb #4323 + 'aA' // 97fc-97fd + '43O' // 97fe #1132 + '228J' // 97ff #5937 + '260O' // 9800 #6774 + '246D' // 9801 #6399 + '68F' // 9802 #1773 + '126D' // 9803 #3279 + '6V' // 9804 #177 + '229X' // 9805 #5977 + '68F' // 9806 #1773 + '6V' // 9807 #177 + '220W' // 9808 #5742 + 'A' // 9809 + '54C' // 980a #1406 + 'A' // 980b + '65N' // 980c #1703 + '1U' // 980d #46 + 'a6V' // 980e-980f #177 + '227C' // 9810 #5904 + '143K' // 9811 #3728 + '166Z' // 9812 #4341 + '195B' // 9813 #5071 + '15H' // 9814 #397 + '17S' // 9815 #460 + '6V' // 9816 #177 + '162U' // 9817 #4232 + '219O' // 9818 #5708 + 'a1U' // 9819-981a #46 + 'A' // 981b + '6V' // 981c #177 + 'A' // 981d + '15H' // 981e #397 + '17S' // 981f #460 + 'a15H' // 9820-9821 #397 + 'A' // 9822 + '6V' // 9823 #177 + '126R' // 9824 #3293 + '1U' // 9825 #46 + '6V' // 9826 #177 + 'A' // 9827 + '43O' // 9828 #1132 + 'aA' // 9829-982a + '15H' // 982b #397 + '253H' // 982c #6585 + '242J' // 982d #6301 + '12V' // 982e #333 + '1U' // 982f #46 + '138A' // 9830 #3588 + 'A' // 9831 + '6V' // 9832 #177 + '12V' // 9833 #333 + '15H' // 9834 #397 + '6V' // 9835 #177 + 'A' // 9836 + '6V' // 9837 #177 + '180D' // 9838 #4683 + '43M' // 9839 #1130 + 'A' // 983a + '218V' // 983b #5689 + '258B' // 983c #6709 + 'a1U' // 983d-983e #46 + 'dA' // 983f-9843 + '1U' // 9844 #46 + '27X' // 9845 #725 + '199K' // 9846 #5184 + '12V' // 9847 #333 + 'a27X' // 9848-9849 #725 + '1U' // 984a #46 + '12V' // 984b #333 + '242S' // 984c #6310 + '222J' // 984d #5781 + '84U' // 984e #2204 + '214H' // 984f #5571 + 'A' // 9850 + '41I' // 9851 #1074 + '15H' // 9852 #397 + '6V' // 9853 #177 + '258V' // 9854 #6729 + '253V' // 9855 #6599 + '6V' // 9856 #177 + '15H' // 9857 #397 + '213Z' // 9858 #5563 + '6V' // 9859 #177 + '248D' // 985a #6451 + '156B' // 985b #4057 + 'aA' // 985c-985d + '242C' // 985e #6294 + 'bA' // 985f-9861 + 'a15H' // 9862-9863 #397 + 'A' // 9864 + '43M' // 9865 #1130 + '12V' // 9866 #333 + '218X' // 9867 #5691 + '17S' // 9868 #460 + 'A' // 9869 + '1U' // 986a #46 + '126P' // 986b #3291 + '12V' // 986c #333 + 'aA' // 986d-986e + '224K' // 986f #5834 + 'a15H' // 9870-9871 #397 + 'A' // 9872 + 'a6V' // 9873-9874 #177 + '177K' // 9875 #4612 + '2D' // 9876 #55 + 'a8N' // 9877-9878 #221 + '1Z' // 9879 #51 + '3I' // 987a #86 + '2C' // 987b #54 + '8N' // 987c #221 + '2K' // 987d #62 + '2C' // 987e #54 + '3N' // 987f #91 + '8N' // 9880 #221 + '2Y' // 9881 #76 + '2K' // 9882 #62 + '8N' // 9883 #221 + '2D' // 9884 #55 + '8N' // 9885 #221 + '1Z' // 9886 #51 + '2Y' // 9887 #76 + '2L' // 9888 #63 + '8N' // 9889 #221 + '2W' // 988a #74 + 'd8N' // 988b-988f #221 + '3W' // 9890 #100 + '3Q' // 9891 #94 + 'A' // 9892 + 'b8N' // 9893-9895 #221 + '1R' // 9896 #43 + '4C' // 9897 #106 + '7K' // 9898 #192 + 'b8N' // 9899-989b #221 + '3I' // 989c #86 + '2C' // 989d #54 + 'a8N' // 989e-989f #221 + '2R' // 98a0 #69 + 'a8N' // 98a1-98a2 #221 + 'A' // 98a3 + '3X' // 98a4 #101 + 'b8N' // 98a5-98a7 #221 + '237O' // 98a8 #6176 + '43O' // 98a9 #1132 + 'a1U' // 98aa-98ab #46 + 'A' // 98ac + '1U' // 98ad #46 + '6V' // 98ae #177 + '84S' // 98af #2202 + '1U' // 98b0 #46 + '65N' // 98b1 #1703 + 'a27X' // 98b2-98b3 #725 + '12V' // 98b4 #333 + 'A' // 98b5 + '15H' // 98b6 #397 + 'a6V' // 98b7-98b8 #177 + '17S' // 98b9 #460 + '15H' // 98ba #397 + 'a6V' // 98bb-98bc #177 + '27X' // 98bd #725 + '8N' // 98be #221 + '6V' // 98bf #177 + 'aA' // 98c0-98c1 + '1U' // 98c2 #46 + '84N' // 98c3 #2197 + '177J' // 98c4 #4611 + '1U' // 98c5 #46 + '169Z' // 98c6 #4419 + '43M' // 98c7 #1130 + '15H' // 98c8 #397 + 'A' // 98c9 + '27X' // 98ca #725 + 'a1U' // 98cb-98cc #46 + 'A' // 98cd + '138B' // 98ce #3589 + 'g8N' // 98cf-98d6 #221 + 'A' // 98d7 + '2L' // 98d8 #63 + '3H' // 98d9 #85 + '8N' // 98da #221 + '221Z' // 98db #5771 + '54C' // 98dc #1406 + '7V' // 98dd #203 + '84R' // 98de #2201 + '238V' // 98df #6209 + '6V' // 98e0 #177 + '84M' // 98e1 #2196 + '130A' // 98e2 #3380 + '6V' // 98e3 #177 + 'A' // 98e4 + '6V' // 98e5 #177 + '12V' // 98e6 #333 + '54D' // 98e7 #1407 + '7V' // 98e8 #203 + '54D' // 98e9 #1407 + '144D' // 98ea #3747 + '37O' // 98eb #976 + '84W' // 98ec #2206 + '53Z' // 98ed #1403 + '248B' // 98ee #6449 + '215U' // 98ef #5610 + '1M' // 98f0 #38 + '37O' // 98f1 #976 + '223G' // 98f2 #5804 + '1M' // 98f3 #38 + '84T' // 98f4 #2203 + '17S' // 98f5 #460 + '1M' // 98f6 #38 + 'dA' // 98f7-98fb + '154Z' // 98fc #4029 + '196K' // 98fd #5106 + '219V' // 98fe #5715 + 'bA' // 98ff-9901 + '1M' // 9902 #38 + '154H' // 9903 #4011 + 'A' // 9904 + '201V' // 9905 #5247 + 'A' // 9906 + '1M' // 9907 #38 + '54A' // 9908 #1404 + '53Z' // 9909 #1403 + '228B' // 990a #5929 + 'A' // 990b + '84V' // 990c #2205 + '7V' // 990d #203 + '17S' // 990e #460 + 'A' // 990f + '226E' // 9910 #5880 + 'a37O' // 9911-9912 #976 + '166E' // 9913 #4320 + '54A' // 9914 #1404 + '1M' // 9915 #38 + 'a37O' // 9916-9917 #976 + '206O' // 9918 #5370 + '12U' // 9919 #332 + '126O' // 991a #3290 + '43L' // 991b #1129 + '32P' // 991c #847 + '1M' // 991d #38 + '84G' // 991e #2190 + '1M' // 991f #38 + '69O' // 9920 #1808 + '144C' // 9921 #3746 + '1M' // 9922 #38 + 'A' // 9923 + '1M' // 9924 #38 + 'A' // 9925 + '1M' // 9926 #38 + '32P' // 9927 #847 + '230H' // 9928 #5987 + 'aA' // 9929-992a + '32P' // 992b #847 + '43L' // 992c #1129 + 'A' // 992d + '43L' // 992e #1129 + 'aA' // 992f-9930 + 'b32P' // 9931-9933 #847 + '1M' // 9934 #38 + '162T' // 9935 #4231 + 'A' // 9936 + '12U' // 9937 #332 + '84L' // 9938 #2195 + '43K' // 9939 #1128 + '32P' // 993a #847 + '27W' // 993b #724 + '4K' // 993c #114 + 'a17R' // 993d-993e #459 + '13R' // 993f #355 + '4K' // 9940 #114 + '1M' // 9941 #38 + '27W' // 9942 #724 + '13R' // 9943 #355 + 'A' // 9944 + '137Z' // 9945 #3587 + 'a1M' // 9946-9947 #38 + '4K' // 9948 #114 + '32O' // 9949 #846 + '13R' // 994a #355 + '184N' // 994b #4797 + '37N' // 994c #975 + '32O' // 994d #846 + '4K' // 994e #114 + 'A' // 994f + '1M' // 9950 #38 + '37N' // 9951 #975 + '144B' // 9952 #3745 + 'A' // 9953 + '37N' // 9954 #975 + '121I' // 9955 #3154 + 'A' // 9956 + '152M' // 9957 #3964 + 'a1M' // 9958-9959 #38 + 'A' // 995a + '1M' // 995b #38 + '4K' // 995c #114 + '12U' // 995d #332 + '17R' // 995e #459 + '4K' // 995f #114 + '1M' // 9960 #38 + 'a13R' // 9961-9962 #355 + '43K' // 9963 #1128 + 'A' // 9964 + '2K' // 9965 #62 + 'c7V' // 9966-9969 #203 + '2K' // 996a #62 + 'a7V' // 996b-996c #203 + 'a3I' // 996d-996e #86 + '7V' // 996f #203 + '2C' // 9970 #54 + '2L' // 9971 #63 + '2K' // 9972 #62 + 'A' // 9973 + 'a7V' // 9974-9975 #203 + '3H' // 9976 #85 + 'b7V' // 9977-9979 #203 + '3X' // 997a #101 + 'A' // 997b + '4C' // 997c #106 + 'a7V' // 997d-997e #203 + '3H' // 997f #85 + 'a7V' // 9980-9981 #203 + 'A' // 9982 + 'a7V' // 9983-9984 #203 + '3W' // 9985 #100 + '2C' // 9986 #54 + '7V' // 9987 #203 + '2D' // 9988 #55 + 'A' // 9989 + 'a7V' // 998a-998b #203 + 'A' // 998c + 'd7V' // 998d-9991 #203 + '3W' // 9992 #100 + 'b7V' // 9993-9995 #203 + '241J' // 9996 #6275 + 'a17R' // 9997-9998 #459 + '236Q' // 9999 #6152 + 'A' // 999a + '27W' // 999b #724 + '13R' // 999c #355 + '69O' // 999d #1808 + '4K' // 999e #114 + '1M' // 999f #38 + 'A' // 99a0 + '13R' // 99a1 #355 + 'A' // 99a2 + '1M' // 99a3 #38 + '12U' // 99a4 #332 + '63O' // 99a5 #1652 + '1M' // 99a6 #38 + 'A' // 99a7 + '184O' // 99a8 #4798 + 'A' // 99a9 + '12U' // 99aa #332 + '53Y' // 99ab #1402 + '235T' // 99ac #6129 + '131X' // 99ad #3429 + '149W' // 99ae #3896 + '13R' // 99af #355 + '1M' // 99b0 #38 + '37N' // 99b1 #975 + '1M' // 99b2 #38 + '159W' // 99b3 #4156 + '84I' // 99b4 #2192 + '1M' // 99b5 #38 + 'aA' // 99b6-99b7 + '12U' // 99b8 #332 + '32O' // 99b9 #846 + '1M' // 99ba #38 + 'A' // 99bb + '43K' // 99bc #1128 + '1M' // 99bd #38 + 'A' // 99be + '69P' // 99bf #1809 + 'A' // 99c0 + '162S' // 99c1 #4230 + 'A' // 99c2 + '69P' // 99c3 #1809 + '84J' // 99c4 #2193 + '84K' // 99c5 #2194 + '256S' // 99c6 #6674 + 'A' // 99c7 + 'a1M' // 99c8-99c9 #38 + 'aA' // 99ca-99cb + '7V' // 99cc #203 + 'aA' // 99cd-99ce + '13R' // 99cf #355 + '191A' // 99d0 #4966 + '32O' // 99d1 #846 + '84H' // 99d2 #2191 + '1M' // 99d3 #38 + '4K' // 99d4 #114 + '191Y' // 99d5 #4990 + '13R' // 99d6 #355 + 'A' // 99d7 + '4K' // 99d8 #114 + '32O' // 99d9 #846 + '27W' // 99da #724 + '177H' // 99db #4609 + '1M' // 99dc #38 + '63O' // 99dd #1652 + '1M' // 99de #38 + '37M' // 99df #974 + '12U' // 99e0 #332 + '17R' // 99e1 #459 + '4K' // 99e2 #114 + 'bA' // 99e3-99e5 + '12U' // 99e6 #332 + '1M' // 99e7 #38 + 'A' // 99e8 + '13R' // 99e9 #355 + 'b1M' // 99ea-99ec #38 + '137X' // 99ed #3585 + '4K' // 99ee #114 + 'A' // 99ef + '4K' // 99f0 #114 + '62Z' // 99f1 #1637 + '1M' // 99f2 #38 + 'A' // 99f3 + '1M' // 99f4 #38 + '27W' // 99f5 #724 + 'aA' // 99f6-99f7 + '4K' // 99f8 #114 + '1M' // 99f9 #38 + 'A' // 99fa + '4K' // 99fb #114 + 'b1M' // 99fc-99fe #38 + '154J' // 99ff #4013 + 'A' // 9a00 + '21Z' // 9a01 #571 + '4K' // 9a02 #114 + 'a17R' // 9a03-9a04 #459 + '4K' // 9a05 #114 + 'aA' // 9a06-9a07 + '248A' // 9a08 #6448 + 'A' // 9a09 + 'a1M' // 9a0a-9a0b #38 + '4K' // 9a0c #114 + 'A' // 9a0d + '203L' // 9a0e #5289 + '21Z' // 9a0f #571 + '4K' // 9a10 #114 + '17R' // 9a11 #459 + '255T' // 9a12 #6649 + '259O' // 9a13 #6748 + 'aA' // 9a14-9a15 + '4K' // 9a16 #114 + 'aA' // 9a17-9a18 + '195W' // 9a19 #5092 + '1M' // 9a1a #38 + '53Y' // 9a1b #1402 + '13R' // 9a1c #355 + 'A' // 9a1d + '1M' // 9a1e #38 + '12U' // 9a1f #332 + '4K' // 9a20 #114 + '12U' // 9a21 #332 + 'a1M' // 9a22-9a23 #38 + '4K' // 9a24 #114 + 'A' // 9a25 + '12U' // 9a26 #332 + '1M' // 9a27 #38 + '252E' // 9a28 #6556 + 'aA' // 9a29-9a2a + '21Z' // 9a2b #571 + 'A' // 9a2c + 'a4K' // 9a2d-9a2e #114 + '12U' // 9a2f #332 + '188U' // 9a30 #4908 + '1M' // 9a31 #38 + '7V' // 9a32 #203 + '1M' // 9a33 #38 + '13R' // 9a34 #355 + '4K' // 9a35 #114 + '37M' // 9a36 #974 + '184M' // 9a37 #4796 + '4K' // 9a38 #114 + 'aA' // 9a39-9a3a + 'a12U' // 9a3b-9a3c #332 + 'A' // 9a3d + '4K' // 9a3e #114 + 'A' // 9a3f + '21Z' // 9a40 #571 + 'a4K' // 9a41-9a42 #114 + '37M' // 9a43 #974 + '4K' // 9a44 #114 + '66Q' // 9a45 #1732 + 'A' // 9a46 + '1M' // 9a47 #38 + 'aA' // 9a48-9a49 + '17R' // 9a4a #459 + '1M' // 9a4b #38 + '4K' // 9a4c #114 + '21Z' // 9a4d #571 + '17R' // 9a4e #459 + 'aA' // 9a4f-9a50 + '1M' // 9a51 #38 + '17R' // 9a52 #459 + 'A' // 9a53 + '1M' // 9a54 #38 + '144A' // 9a55 #3744 + '4K' // 9a56 #114 + '231V' // 9a57 #6027 + '27W' // 9a58 #724 + 'A' // 9a59 + '220V' // 9a5a #5741 + '62Z' // 9a5b #1637 + '12U' // 9a5c #332 + '1M' // 9a5d #38 + 'A' // 9a5e + '66Q' // 9a5f #1732 + 'aA' // 9a60-9a61 + '21Z' // 9a62 #571 + '13R' // 9a63 #355 + '4K' // 9a64 #114 + '21Z' // 9a65 #571 + 'bA' // 9a66-9a68 + '21Z' // 9a69 #571 + '37M' // 9a6a #974 + '17R' // 9a6b #459 + '177I' // 9a6c #4610 + '3W' // 9a6d #100 + '7V' // 9a6e #203 + '2W' // 9a6f #74 + '4C' // 9a70 #106 + '1R' // 9a71 #43 + 'A' // 9a72 + '3X' // 9a73 #101 + '3H' // 9a74 #85 + '7V' // 9a75 #203 + '4C' // 9a76 #106 + 'c7V' // 9a77-9a7a #203 + '3Y' // 9a7b #102 + '3G' // 9a7c #84 + '7V' // 9a7d #203 + '3N' // 9a7e #91 + '3G' // 9a7f #84 + '8M' // 9a80 #220 + '3W' // 9a81 #100 + '1R' // 9a82 #43 + '8M' // 9a83 #220 + '3H' // 9a84 #85 + '8M' // 9a85 #220 + '3W' // 9a86 #100 + '2W' // 9a87 #74 + 'b8M' // 9a88-9a8a #220 + '2W' // 9a8b #74 + '1Z' // 9a8c #51 + 'a8M' // 9a8d-9a8e #220 + '84F' // 9a8f #2189 + '8M' // 9a90 #220 + '1R' // 9a91 #43 + 'a8M' // 9a92-9a93 #220 + 'A' // 9a94 + 'a8M' // 9a95-9a96 #220 + '3Y' // 9a97 #102 + 'a8M' // 9a98-9a99 #220 + '4C' // 9a9a #106 + 'g8M' // 9a9b-9aa2 #220 + 'A' // 9aa3 + '2L' // 9aa4 #63 + '8M' // 9aa5 #220 + 'A' // 9aa6 + '8M' // 9aa7 #220 + '204N' // 9aa8 #5317 + 'A' // 9aa9 + '1M' // 9aaa #38 + 'A' // 9aab + 'b1M' // 9aac-9aae #38 + 'a21X' // 9aaf-9ab0 #569 + '21Y' // 9ab1 #570 + '37L' // 9ab2 #973 + 'A' // 9ab3 + '1M' // 9ab4 #38 + '41H' // 9ab5 #1073 + '21X' // 9ab6 #569 + '83X' // 9ab7 #2181 + '130E' // 9ab8 #3384 + '21X' // 9ab9 #569 + '21Y' // 9aba #570 + '1M' // 9abb #38 + '121J' // 9abc #3155 + '37L' // 9abd #973 + '53X' // 9abe #1401 + '1M' // 9abf #38 + 'a21X' // 9ac0-9ac1 #569 + '21Y' // 9ac2 #570 + '41H' // 9ac3 #1073 + '252V' // 9ac4 #6573 + '2W' // 9ac5 #74 + '1M' // 9ac6 #38 + 'A' // 9ac7 + '41H' // 9ac8 #1073 + 'aA' // 9ac9-9aca + 'a8M' // 9acb-9acc #220 + 'A' // 9acd + '41H' // 9ace #1073 + '21X' // 9acf #569 + '1M' // 9ad0 #38 + '21X' // 9ad1 #569 + '177G' // 9ad2 #4608 + '137Y' // 9ad3 #3586 + '241E' // 9ad4 #6270 + 'b53X' // 9ad5-9ad7 #1401 + '69A' // 9ad8 #1794 + '252M' // 9ad9 #6564 + 'A' // 9ada + 'a1M' // 9adb-9adc #38 + 'A' // 9add + '1M' // 9ade #38 + '21X' // 9adf #569 + '37L' // 9ae0 #973 + '21Y' // 9ae1 #570 + '37L' // 9ae2 #973 + '21X' // 9ae3 #569 + '1M' // 9ae4 #38 + '247Z' // 9ae5 #6447 + '131W' // 9ae6 #3428 + '1H' // 9ae7 #33 + 'A' // 9ae8 + '1H' // 9ae9 #33 + '257G' // 9aea #6688 + '6K' // 9aeb #166 + '1H' // 9aec #33 + '84B' // 9aed #2185 + '206N' // 9aee #5369 + '6K' // 9aef #166 + 'A' // 9af0 + '1H' // 9af1 #33 + '6A' // 9af2 #156 + '1H' // 9af3 #33 + '6K' // 9af4 #166 + '1H' // 9af5 #33 + 'A' // 9af6 + '1H' // 9af7 #33 + 'A' // 9af8 + '6K' // 9af9 #166 + '1H' // 9afa #33 + '6K' // 9afb #166 + 'A' // 9afc + '6A' // 9afd #156 + 'A' // 9afe + '19U' // 9aff #514 + 'a1H' // 9b00-9b01 #33 + '19U' // 9b02 #514 + 'a6K' // 9b03-9b04 #166 + '1H' // 9b05 #33 + '216V' // 9b06 #5637 + 'A' // 9b07 + '6K' // 9b08 #166 + '19U' // 9b09 #514 + 'A' // 9b0a + 'a1H' // 9b0b-9b0c #33 + '65A' // 9b0d #1690 + '1H' // 9b0e #33 + '84E' // 9b0f #2188 + '41G' // 9b10 #1072 + 'A' // 9b11 + '1H' // 9b12 #33 + '8M' // 9b13 #220 + '32M' // 9b14 #844 + 'A' // 9b15 + '1H' // 9b16 #33 + 'A' // 9b17 + '6K' // 9b18 #166 + '1H' // 9b19 #33 + '131U' // 9b1a #3426 + 'b1H' // 9b1b-9b1d #33 + 'A' // 9b1e + '6K' // 9b1f #166 + '1H' // 9b20 #33 + 'A' // 9b21 + 'a6K' // 9b22-9b23 #166 + 'A' // 9b24 + '199J' // 9b25 #5183 + '1H' // 9b26 #33 + '67D' // 9b27 #1745 + '6K' // 9b28 #166 + '6A' // 9b29 #156 + '43J' // 9b2a #1127 + 'a1H' // 9b2b-9b2c #33 + 'a6A' // 9b2d-9b2e #156 + '6K' // 9b2f #166 + 'A' // 9b30 + '167F' // 9b31 #4347 + '6K' // 9b32 #166 + '1H' // 9b33 #33 + '19U' // 9b34 #514 + '1H' // 9b35 #33 + 'A' // 9b36 + '1H' // 9b37 #33 + 'A' // 9b38 + '19U' // 9b39 #514 + '1H' // 9b3a #33 + '6K' // 9b3b #166 + '204F' // 9b3c #5309 + '1H' // 9b3d #33 + 'aA' // 9b3e-9b3f + '32M' // 9b40 #844 + '130B' // 9b41 #3381 + '189Z' // 9b42 #4939 + '43J' // 9b43 #1127 + '131V' // 9b44 #3427 + '191D' // 9b45 #4969 + '21Y' // 9b46 #570 + '8M' // 9b47 #220 + '6K' // 9b48 #166 + '8M' // 9b49 #220 + 'A' // 9b4a + 'a6A' // 9b4b-9b4c #156 + 'a6K' // 9b4d-9b4e #166 + '155Z' // 9b4f #4055 + '32M' // 9b50 #844 + '6K' // 9b51 #166 + 'aA' // 9b52-9b53 + '215Y' // 9b54 #5614 + '6A' // 9b55 #156 + '41G' // 9b56 #1072 + '1H' // 9b57 #33 + '6K' // 9b58 #166 + 'A' // 9b59 + '220H' // 9b5a #5727 + '6A' // 9b5b #156 + '8M' // 9b5c #220 + 'A' // 9b5d + '1H' // 9b5e #33 + '21Y' // 9b5f #570 + '17Q' // 9b60 #458 + '41G' // 9b61 #1072 + '8M' // 9b62 #220 + '1H' // 9b63 #33 + 'A' // 9b64 + 'a1H' // 9b65-9b66 #33 + 'A' // 9b67 + '6A' // 9b68 #156 + '17Q' // 9b69 #458 + 'd1H' // 9b6a-9b6e #33 + '67D' // 9b6f #1745 + 'aA' // 9b70-9b71 + 'a1H' // 9b72-9b73 #33 + '6A' // 9b74 #156 + 'a1H' // 9b75-9b76 #33 + '126N' // 9b77 #3289 + 'a1H' // 9b78-9b79 #33 + 'bA' // 9b7a-9b7c + '17Q' // 9b7d #458 + 'A' // 9b7e + '19U' // 9b7f #514 + '6K' // 9b80 #166 + '17Q' // 9b81 #458 + 'A' // 9b82 + '6A' // 9b83 #156 + 'b1H' // 9b84-9b86 #33 + '6A' // 9b87 #156 + '21Y' // 9b88 #570 + 'a1H' // 9b89-9b8a #33 + '6K' // 9b8b #166 + 'A' // 9b8c + '6A' // 9b8d #156 + '84A' // 9b8e #2184 + '19U' // 9b8f #514 + '6A' // 9b90 #156 + '143Z' // 9b91 #3743 + 'a6A' // 9b92-9b93 #156 + '1H' // 9b94 #33 + '17Q' // 9b95 #458 + '1H' // 9b96 #33 + '6A' // 9b97 #156 + 'aA' // 9b98-9b99 + '1H' // 9b9a #33 + 'aA' // 9b9b-9b9c + '19U' // 9b9d #514 + '1H' // 9b9e #33 + '43J' // 9b9f #1127 + '6K' // 9ba0 #166 + 'A' // 9ba1 + '17Q' // 9ba2 #458 + 'bA' // 9ba3-9ba5 + 'a1H' // 9ba6-9ba7 #33 + '83Z' // 9ba8 #2183 + '1H' // 9ba9 #33 + '137W' // 9baa #3584 + '83Y' // 9bab #2182 + '1H' // 9bac #33 + '130F' // 9bad #3385 + '220F' // 9bae #5725 + 'A' // 9baf + '6K' // 9bb0 #166 + '41G' // 9bb1 #1072 + '1H' // 9bb2 #33 + 'A' // 9bb3 + '1H' // 9bb4 #33 + 'aA' // 9bb5-9bb6 + '1H' // 9bb7 #33 + '6K' // 9bb8 #166 + '1H' // 9bb9 #33 + 'A' // 9bba + 'a1H' // 9bbb-9bbc #33 + 'A' // 9bbd + 'a1H' // 9bbe-9bbf #33 + 'a6A' // 9bc0-9bc1 #156 + 'A' // 9bc2 + '17Q' // 9bc3 #458 + 'aA' // 9bc4-9bc5 + 'b6A' // 9bc6-9bc8 #156 + '136E' // 9bc9 #3540 + '65A' // 9bca #1690 + 'bA' // 9bcb-9bcd + '1H' // 9bce #33 + '19U' // 9bcf #514 + 'b1H' // 9bd0-9bd2 #33 + '21Y' // 9bd3 #570 + '6A' // 9bd4 #156 + '17Q' // 9bd5 #458 + '84D' // 9bd6 #2187 + '6A' // 9bd7 #156 + '1H' // 9bd8 #33 + '17Q' // 9bd9 #458 + 'A' // 9bda + '84C' // 9bdb #2186 + 'A' // 9bdc + '6A' // 9bdd #156 + 'A' // 9bde + '1H' // 9bdf #33 + 'A' // 9be0 + 'a6A' // 9be1-9be2 #156 + '1H' // 9be3 #33 + '83W' // 9be4 #2180 + '6A' // 9be5 #156 + 'A' // 9be6 + '6A' // 9be7 #156 + '142E' // 9be8 #3696 + '32M' // 9be9 #844 + '6A' // 9bea #156 + '1H' // 9beb #33 + 'A' // 9bec + '17Q' // 9bed #458 + 'a1H' // 9bee-9bef #33 + 'a53W' // 9bf0-9bf1 #1400 + 'a1H' // 9bf2-9bf3 #33 + '32M' // 9bf4 #844 + '1H' // 9bf5 #33 + 'A' // 9bf6 + '32N' // 9bf7 #845 + 'b1H' // 9bf8-9bfa #33 + 'aA' // 9bfb-9bfc + '32N' // 9bfd #845 + 'A' // 9bfe + '32N' // 9bff #845 + '1H' // 9c00 #33 + 'A' // 9c01 + '53W' // 9c02 #1400 + 'A' // 9c03 + '1H' // 9c04 #33 + 'A' // 9c05 + '32N' // 9c06 #845 + 'A' // 9c07 + 'b32N' // 9c08-9c0a #845 + '1H' // 9c0b #33 + '37J' // 9c0c #971 + '43H' // 9c0d #1125 + 'A' // 9c0e + '1H' // 9c0f #33 + '43G' // 9c10 #1124 + '1H' // 9c11 #33 + '43H' // 9c12 #1125 + '19T' // 9c13 #513 + '1H' // 9c14 #33 + '43G' // 9c15 #1124 + '1H' // 9c16 #33 + 'A' // 9c17 + 'b1H' // 9c18-9c1a #33 + '37J' // 9c1b #971 + '19T' // 9c1c #513 + 'a1H' // 9c1d-9c1e #33 + '37K' // 9c1f #972 + '21W' // 9c20 #568 + '19T' // 9c21 #513 + '1H' // 9c22 #33 + '19T' // 9c23 #513 + '43I' // 9c24 #1126 + '43H' // 9c25 #1125 + '37J' // 9c26 #971 + '1H' // 9c27 #33 + 'a19T' // 9c28-9c29 #513 + '1H' // 9c2a #33 + 'aA' // 9c2b-9c2c + '43I' // 9c2d #1126 + '1H' // 9c2e #33 + '37J' // 9c2f #971 + '1H' // 9c30 #33 + '19T' // 9c31 #513 + '43G' // 9c32 #1124 + '37K' // 9c33 #972 + 'A' // 9c34 + 'b19T' // 9c35-9c37 #513 + 'A' // 9c38 + '43I' // 9c39 #1126 + '19T' // 9c3a #513 + '83S' // 9c3b #2176 + 'A' // 9c3c + '19T' // 9c3d #513 + '4N' // 9c3e #117 + 'A' // 9c3f + '83V' // 9c40 #2179 + '1I' // 9c41 #34 + 'A' // 9c42 + 'a1I' // 9c43-9c44 #34 + 'a4N' // 9c45-9c46 #117 + '53T' // 9c47 #1397 + '37I' // 9c48 #970 + '53T' // 9c49 #1397 + '1I' // 9c4a #34 + 'bA' // 9c4b-9c4d + '1I' // 9c4e #34 + '32L' // 9c4f #843 + '1I' // 9c50 #34 + 'A' // 9c51 + '4N' // 9c52 #117 + '83Q' // 9c53 #2174 + '4N' // 9c54 #117 + 'A' // 9c55 + '4N' // 9c56 #117 + '124Z' // 9c57 #3249 + '4N' // 9c58 #117 + '37K' // 9c59 #972 + 'b1I' // 9c5a-9c5c #34 + '4N' // 9c5d #117 + '1I' // 9c5e #34 + 'a4N' // 9c5f-9c60 #117 + '1I' // 9c61 #34 + 'A' // 9c62 + '4N' // 9c63 #117 + '8M' // 9c64 #220 + '1I' // 9c65 #34 + 'A' // 9c66 + 'a4N' // 9c67-9c68 #117 + 'b1I' // 9c69-9c6b #34 + 'A' // 9c6c + 'a1I' // 9c6d-9c6e #34 + 'A' // 9c6f + '1I' // 9c70 #34 + 'A' // 9c71 + '37I' // 9c72 #970 + 'aA' // 9c73-9c74 + '4N' // 9c75 #117 + '1I' // 9c76 #34 + '137U' // 9c77 #3582 + '37I' // 9c78 #970 + 'A' // 9c79 + '4N' // 9c7a #117 + '37I' // 9c7b #970 + '83R' // 9c7c #2175 + 'aA' // 9c7d-9c7e + 'a8M' // 9c7f-9c80 #220 + '3Y' // 9c81 #102 + 'a6F' // 9c82-9c83 #161 + 'A' // 9c84 + 'g6F' // 9c85-9c8c #161 + '3H' // 9c8d #85 + 'd6F' // 9c8e-9c92 #161 + 'A' // 9c93 + 'g6F' // 9c94-9c9b #161 + '2C' // 9c9c #54 + 'A' // 9c9d + 'e6F' // 9c9e-9ca3 #161 + '3W' // 9ca4 #100 + 'b6F' // 9ca5-9ca7 #161 + '3G' // 9ca8 #84 + '6F' // 9ca9 #161 + 'A' // 9caa + '6F' // 9cab #161 + 'A' // 9cac + 'a6F' // 9cad-9cae #161 + 'aA' // 9caf-9cb0 + 'f6F' // 9cb1-9cb7 #161 + '2K' // 9cb8 #62 + 'd6F' // 9cb9-9cbd #161 + 'A' // 9cbe + 'a6F' // 9cbf-9cc0 #161 + 'aA' // 9cc1-9cc2 + '6F' // 9cc3 #161 + '3G' // 9cc4 #84 + 'b6F' // 9cc5-9cc7 #161 + 'A' // 9cc8 + 'h6F' // 9cc9-9cd1 #161 + 'A' // 9cd2 + 'g6F' // 9cd3-9cda #161 + 'A' // 9cdb + 'a6F' // 9cdc-9cdd #161 + '3G' // 9cde #84 + '6F' // 9cdf #161 + 'A' // 9ce0 + 'b6F' // 9ce1-9ce3 #161 + 'A' // 9ce4 + '205Z' // 9ce5 #5355 + '1I' // 9ce6 #34 + '83O' // 9ce7 #2172 + 'A' // 9ce8 + '83U' // 9ce9 #2178 + 'A' // 9cea + 'a1I' // 9ceb-9cec #34 + 'A' // 9ced + 'a6F' // 9cee-9cef #161 + '1I' // 9cf0 #34 + 'A' // 9cf1 + '4N' // 9cf2 #117 + '201R' // 9cf3 #5243 + '182U' // 9cf4 #4752 + 'A' // 9cf5 + '64N' // 9cf6 #1677 + '1I' // 9cf7 #34 + 'A' // 9cf8 + '1I' // 9cf9 #34 + 'aA' // 9cfa-9cfb + 'a53U' // 9cfc-9cfd #1398 + 'cA' // 9cfe-9d01 + '83P' // 9d02 #2173 + '4N' // 9d03 #117 + 'aA' // 9d04-9d05 + 'a4N' // 9d06-9d07 #117 + '27V' // 9d08 #723 + '155Y' // 9d09 #4054 + 'A' // 9d0a + '1I' // 9d0b #34 + '21W' // 9d0c #568 + 'A' // 9d0d + '1I' // 9d0e #34 + 'A' // 9d0f + '37K' // 9d10 #972 + '1I' // 9d11 #34 + '27V' // 9d12 #723 + 'aA' // 9d13-9d14 + '4N' // 9d15 #117 + '21W' // 9d16 #568 + '4N' // 9d17 #117 + '1I' // 9d18 #34 + 'aA' // 9d19-9d1a + '48V' // 9d1b #1269 + '1I' // 9d1c #34 + '4N' // 9d1d #117 + '27V' // 9d1e #723 + '4N' // 9d1f #117 + 'A' // 9d20 + '21W' // 9d21 #568 + 'A' // 9d22 + '4N' // 9d23 #117 + 'aA' // 9d24-9d25 + '48V' // 9d26 #1269 + 'A' // 9d27 + '181A' // 9d28 #4706 + 'A' // 9d29 + 'b1I' // 9d2a-9d2c #34 + 'aA' // 9d2d-9d2e + 'a4N' // 9d2f-9d30 #117 + 'A' // 9d31 + 'a1I' // 9d32-9d33 #34 + '4N' // 9d34 #117 + 'aA' // 9d35-9d36 + '53U' // 9d37 #1398 + 'A' // 9d38 + '21W' // 9d39 #568 + '1I' // 9d3a #34 + '187I' // 9d3b #4870 + '1I' // 9d3c #34 + '4N' // 9d3d #117 + '1I' // 9d3e #34 + '131T' // 9d3f #3425 + 'A' // 9d40 + '1I' // 9d41 #34 + '4N' // 9d42 #117 + '1I' // 9d43 #34 + '32L' // 9d44 #843 + 'c1I' // 9d45-9d48 #34 + '21W' // 9d49 #568 + '1I' // 9d4a #34 + 'bA' // 9d4b-9d4d + '21W' // 9d4e #568 + 'A' // 9d4f + '4N' // 9d50 #117 + '48V' // 9d51 #1269 + 'a4N' // 9d52-9d53 #117 + '1I' // 9d54 #34 + 'cA' // 9d55-9d58 + '27V' // 9d59 #723 + 'aA' // 9d5a-9d5b + '83T' // 9d5c #2177 + '66E' // 9d5d #1720 + '4N' // 9d5e #117 + '27V' // 9d5f #723 + 'a83M' // 9d60-9d61 #2170 + 'c1I' // 9d62-9d65 #34 + 'bA' // 9d66-9d68 + '1I' // 9d69 #34 + '4N' // 9d6a #117 + '1I' // 9d6b #34 + '66E' // 9d6c #1720 + 'a21W' // 9d6d-9d6e #568 + '4N' // 9d6f #117 + '27V' // 9d70 #723 + 'A' // 9d71 + '64N' // 9d72 #1677 + '1I' // 9d73 #34 + 'aA' // 9d74-9d75 + '1I' // 9d76 #34 + '15G' // 9d77 #396 + 'aA' // 9d78-9d79 + '49N' // 9d7a #1287 + '1I' // 9d7b #34 + '32L' // 9d7c #843 + 'A' // 9d7d + '37H' // 9d7e #969 + 'cA' // 9d7f-9d82 + '32L' // 9d83 #843 + '49N' // 9d84 #1287 + 'A' // 9d85 + '1I' // 9d86 #34 + '15G' // 9d87 #396 + 'A' // 9d88 + '37H' // 9d89 #969 + '1I' // 9d8a #34 + 'aA' // 9d8b-9d8c + 'a1I' // 9d8d-9d8e #34 + '254Y' // 9d8f #6628 + 'aA' // 9d90-9d91 + '37H' // 9d92 #969 + '15G' // 9d93 #396 + 'A' // 9d94 + '1I' // 9d95 #34 + '15G' // 9d96 #396 + '1I' // 9d97 #34 + '15G' // 9d98 #396 + '1I' // 9d99 #34 + '15G' // 9d9a #396 + 'eA' // 9d9b-9da0 + '15G' // 9da1 #396 + '53V' // 9da2 #1399 + 'A' // 9da3 + '1I' // 9da4 #34 + '53V' // 9da5 #1399 + 'bA' // 9da6-9da8 + '83N' // 9da9 #2171 + '1I' // 9daa #34 + 'a15G' // 9dab-9dac #396 + 'A' // 9dad + '1I' // 9dae #34 + '179V' // 9daf #4675 + 'A' // 9db0 + 'a15G' // 9db1-9db2 #396 + 'A' // 9db3 + '155C' // 9db4 #4032 + '15G' // 9db5 #396 + 'aA' // 9db6-9db7 + '49N' // 9db8 #1287 + 'b15G' // 9db9-9dbb #396 + '37H' // 9dbc #969 + '32L' // 9dbd #843 + 'A' // 9dbe + '15G' // 9dbf #396 + 'b10P' // 9dc0-9dc2 #275 + '1I' // 9dc3 #34 + '43F' // 9dc4 #1123 + 'A' // 9dc5 + '1I' // 9dc6 #34 + '32K' // 9dc7 #842 + '19S' // 9dc8 #512 + '32K' // 9dc9 #842 + '1I' // 9dca #34 + 'cA' // 9dcb-9dce + '1I' // 9dcf #34 + 'bA' // 9dd0-9dd2 + '10P' // 9dd3 #275 + '32J' // 9dd4 #841 + '1I' // 9dd5 #34 + '10P' // 9dd6 #275 + '131R' // 9dd7 #3423 + 'A' // 9dd8 + 'a10P' // 9dd9-9dda #275 + 'bA' // 9ddb-9ddd + '10P' // 9dde #275 + '32K' // 9ddf #842 + '1I' // 9de0 #34 + 'A' // 9de1 + '19S' // 9de2 #512 + '1I' // 9de3 #34 + 'A' // 9de4 + 'a10P' // 9de5-9de6 #275 + '1I' // 9de7 #34 + '19S' // 9de8 #512 + '1I' // 9de9 #34 + 'A' // 9dea + '1I' // 9deb #34 + 'A' // 9dec + 'a1I' // 9ded-9dee #34 + 'a10P' // 9def-9df0 #275 + 'A' // 9df1 + '83J' // 9df2 #2167 + 'a10P' // 9df3-9df4 #275 + 'bA' // 9df5-9df7 + '83G' // 9df8 #2164 + '181S' // 9df9 #4724 + '83I' // 9dfa #2166 + 'A' // 9dfb + '24L' // 9dfc #635 + '10P' // 9dfd #275 + '1I' // 9dfe #34 + '19S' // 9dff #512 + 'aA' // 9e00-9e01 + '10P' // 9e02 #275 + 'cA' // 9e03-9e06 + '10P' // 9e07 #275 + 'aA' // 9e08-9e09 + '32K' // 9e0a #842 + 'A' // 9e0b + '24L' // 9e0c #635 + '1I' // 9e0d #34 + '32J' // 9e0e #841 + 'A' // 9e0f + '1I' // 9e10 #34 + '32K' // 9e11 #842 + '1I' // 9e12 #34 + 'aA' // 9e13-9e14 + '10P' // 9e15 #275 + '1I' // 9e16 #34 + 'A' // 9e17 + '19S' // 9e18 #512 + '1I' // 9e19 #34 + '43F' // 9e1a #1123 + '10P' // 9e1b #275 + '32J' // 9e1c #841 + '10P' // 9e1d #275 + '43F' // 9e1e #1123 + '137V' // 9e1f #3583 + '6F' // 9e20 #161 + '3I' // 9e21 #86 + '6F' // 9e22 #161 + '4C' // 9e23 #106 + 'A' // 9e24 + '2W' // 9e25 #74 + '3H' // 9e26 #85 + 'A' // 9e27 + 'd6F' // 9e28-9e2c #161 + '2Y' // 9e2d #76 + 'a6F' // 9e2e-9e2f #161 + 'c6S' // 9e30-9e33 #174 + 'A' // 9e34 + 'f6S' // 9e35-9e3b #174 + 'A' // 9e3c + '3X' // 9e3d #101 + '6S' // 9e3e #174 + '2L' // 9e3f #63 + 'd6S' // 9e40-9e44 #174 + '2L' // 9e45 #63 + 'h6S' // 9e46-9e4e #174 + '2L' // 9e4f #63 + 'A' // 9e50 + '6S' // 9e51 #174 + 'A' // 9e52 + '6S' // 9e53 #174 + 'A' // 9e54 + 'c6S' // 9e55-9e58 #174 + 'A' // 9e59 + 'b6S' // 9e5a-9e5c #174 + 'A' // 9e5d + 'e6S' // 9e5e-9e63 #174 + '2R' // 9e64 #69 + 'A' // 9e65 + 'h6S' // 9e66-9e6e #174 + 'A' // 9e6f + '2L' // 9e70 #63 + '6S' // 9e71 #174 + 'A' // 9e72 + '6S' // 9e73 #174 + 'A' // 9e74 + '83F' // 9e75 #2163 + 'aA' // 9e76-9e77 + '252Y' // 9e78 #6576 + '162R' // 9e79 #4229 + '1I' // 9e7a #34 + '32J' // 9e7b #841 + '131S' // 9e7c #3424 + '177F' // 9e7d #4607 + '6S' // 9e7e #174 + '191K' // 9e7f #4976 + 'a10P' // 9e80-9e81 #275 + '53Q' // 9e82 #1394 + '10P' // 9e83 #275 + 'a32J' // 9e84-9e85 #841 + '6S' // 9e86 #174 + 'a53Q' // 9e87-9e88 #1394 + 'aA' // 9e89-9e8a + 'a21V' // 9e8b-9e8c #567 + 'A' // 9e8d + 'a2F' // 9e8e-9e8f #57 + '53S' // 9e90 #1396 + '21V' // 9e91 #567 + '131Q' // 9e92 #3422 + '83H' // 9e93 #2165 + 'A' // 9e94 + '37F' // 9e95 #967 + '37G' // 9e96 #968 + '220M' // 9e97 #5732 + '19R' // 9e98 #511 + 'aA' // 9e99-9e9a + '2F' // 9e9b #57 + 'A' // 9e9c + '32I' // 9e9d #840 + '19R' // 9e9e #511 + '159H' // 9e9f #4141 + 'A' // 9ea0 + '19S' // 9ea1 #512 + '24L' // 9ea2 #635 + 'A' // 9ea3 + '21V' // 9ea4 #567 + '41C' // 9ea5 #1068 + '83K' // 9ea6 #2168 + 'A' // 9ea7 + '19R' // 9ea8 #511 + 'a21V' // 9ea9-9eaa #567 + '19S' // 9eab #512 + '19R' // 9eac #511 + '37G' // 9ead #968 + '2F' // 9eae #57 + '37G' // 9eaf #968 + '2F' // 9eb0 #57 + '24L' // 9eb1 #635 + 'A' // 9eb2 + '2F' // 9eb3 #57 + '32I' // 9eb4 #840 + '41C' // 9eb5 #1068 + 'A' // 9eb6 + '19S' // 9eb7 #512 + '49M' // 9eb8 #1286 + '252D' // 9eb9 #6555 + '256C' // 9eba #6658 + '212I' // 9ebb #5520 + '231U' // 9ebc #6026 + '143Y' // 9ebd #3742 + '32I' // 9ebe #840 + '21V' // 9ebf #567 + 'A' // 9ec0 + '24L' // 9ec1 #635 + 'A' // 9ec2 + '224I' // 9ec3 #5832 + '143H' // 9ec4 #3725 + 'A' // 9ec5 + '19R' // 9ec6 #511 + '24L' // 9ec7 #635 + '2F' // 9ec8 #57 + '6S' // 9ec9 #174 + 'A' // 9eca + '2F' // 9ecb #57 + '37G' // 9ecc #968 + '32I' // 9ecd #840 + '194M' // 9ece #5056 + '169Y' // 9ecf #4418 + '21V' // 9ed0 #567 + '224J' // 9ed1 #5833 + '258X' // 9ed2 #6731 + '6S' // 9ed3 #174 + '83E' // 9ed4 #2162 + '49M' // 9ed5 #1286 + '6S' // 9ed6 #174 + 'A' // 9ed7 + '191X' // 9ed8 #4989 + '254O' // 9ed9 #6618 + '19S' // 9eda #512 + '155X' // 9edb #4053 + '32I' // 9edc #840 + '21V' // 9edd #567 + '239M' // 9ede #6226 + 'a21V' // 9edf-9ee0 #567 + 'A' // 9ee1 + '53S' // 9ee2 #1396 + 'A' // 9ee3 + '2F' // 9ee4 #57 + '11Q' // 9ee5 #302 + 'A' // 9ee6 + '11Q' // 9ee7 #302 + '184L' // 9ee8 #4795 + 'a6S' // 9ee9-9eea #174 + 'A' // 9eeb + 'a2F' // 9eec-9eed #57 + '24M' // 9eee #636 + '121G' // 9eef #3152 + '2F' // 9ef0 #57 + '37F' // 9ef1 #967 + '2F' // 9ef2 #57 + '53R' // 9ef3 #1395 + '83D' // 9ef4 #2161 + '2F' // 9ef5 #57 + '11Q' // 9ef6 #302 + '24M' // 9ef7 #636 + '19R' // 9ef8 #511 + '11Q' // 9ef9 #302 + 'A' // 9efa + 'a11Q' // 9efb-9efc #302 + '24M' // 9efd #636 + '37F' // 9efe #967 + '24M' // 9eff #636 + 'aA' // 9f00-9f01 + '19R' // 9f02 #511 + '2F' // 9f03 #57 + 'bA' // 9f04-9f06 + 'a53P' // 9f07-9f08 #1393 + '24M' // 9f09 #636 + 'A' // 9f0a + '6S' // 9f0b #174 + 'A' // 9f0c + '6S' // 9f0d #174 + '169X' // 9f0e #4417 + '2F' // 9f0f #57 + '11Q' // 9f10 #302 + 'a2F' // 9f11-9f12 #57 + '195M' // 9f13 #5082 + '49M' // 9f14 #1286 + '24M' // 9f15 #636 + '2F' // 9f16 #57 + '37F' // 9f17 #967 + 'A' // 9f18 + '11Q' // 9f19 #302 + 'a2F' // 9f1a-9f1b #57 + 'bA' // 9f1c-9f1e + '2F' // 9f1f #57 + '191W' // 9f20 #4988 + '2F' // 9f21 #57 + '11Q' // 9f22 #302 + 'bA' // 9f23-9f25 + '19R' // 9f26 #511 + '24L' // 9f27 #635 + 'A' // 9f28 + '53R' // 9f29 #1395 + 'a2F' // 9f2a-9f2b #57 + '11Q' // 9f2c #302 + 'aA' // 9f2d-9f2e + '11Q' // 9f2f #302 + 'A' // 9f30 + '11Q' // 9f31 #302 + '2F' // 9f32 #57 + 'A' // 9f33 + '24M' // 9f34 #636 + 'aA' // 9f35-9f36 + '11Q' // 9f37 #302 + 'A' // 9f38 + '11Q' // 9f39 #302 + '2F' // 9f3a #57 + '190K' // 9f3b #4950 + '2F' // 9f3c #57 + 'a11Q' // 9f3d-9f3e #302 + '2F' // 9f3f #57 + 'A' // 9f40 + '11Q' // 9f41 #302 + 'A' // 9f42 + '2F' // 9f43 #57 + 'a19R' // 9f44-9f45 #511 + 'a2F' // 9f46-9f47 #57 + 'aA' // 9f48-9f49 + '41C' // 9f4a #1068 + '148J' // 9f4b #3857 + 'a83L' // 9f4c-9f4d #2169 + '53P' // 9f4e #1393 + '27U' // 9f4f #722 + '83A' // 9f50 #2158 + '6S' // 9f51 #174 + '169W' // 9f52 #4416 + '32G' // 9f53 #838 + '27U' // 9f54 #722 + 'a2F' // 9f55-9f56 #57 + '27U' // 9f57 #722 + '2F' // 9f58 #57 + '37E' // 9f59 #966 + '32G' // 9f5a #838 + 'A' // 9f5b + '37E' // 9f5c #966 + 'a2F' // 9f5d-9f5e #57 + '37D' // 9f5f #965 + '27U' // 9f60 #722 + '41C' // 9f61 #1068 + '83B' // 9f62 #2159 + '53O' // 9f63 #1392 + 'aA' // 9f64-9f65 + '53O' // 9f66 #1392 + '82X' // 9f67 #2155 + '2F' // 9f68 #57 + '32G' // 9f69 #838 + '37D' // 9f6a #965 + 'A' // 9f6b + '37D' // 9f6c #965 + 'd2F' // 9f6d-9f71 #57 + '27U' // 9f72 #722 + '2F' // 9f73 #57 + 'A' // 9f74 + '2F' // 9f75 #57 + '27U' // 9f76 #722 + '37D' // 9f77 #965 + 'aA' // 9f78-9f79 + '2F' // 9f7a #57 + 'aA' // 9f7b-9f7c + '2F' // 9f7d #57 + 'A' // 9f7e + '82Z' // 9f7f #2157 + 'a6S' // 9f80-9f81 #174 + 'A' // 9f82 + '50I' // 9f83 #1308 + '3N' // 9f84 #91 + 'g50I' // 9f85-9f8c #1308 + '227I' // 9f8d #5910 + '37E' // 9f8e #966 + '2F' // 9f8f #57 + '155W' // 9f90 #4052 + '32H' // 9f91 #839 + '2F' // 9f92 #57 + 'A' // 9f93 + '32H' // 9f94 #839 + '82W' // 9f95 #2154 + '32H' // 9f96 #839 + '32G' // 9f97 #838 + '83C' // 9f98 #2160 + '121H' // 9f99 #3153 + '3G' // 9f9a #84 + '50I' // 9f9b #1308 + '177E' // 9f9c #4606 + '2F' // 9f9d #57 + '247Y' // 9f9e #6446 + '82Y' // 9f9f #2156 + '32H' // 9fa0 #839 + '2F' // 9fa1 #57 + '32H' // 9fa2 #839 + '2F' // 9fa3 #57 + '37E' // 9fa4 #966 + '32G' // 9fa5 #838 + 'm43E' // 9fa6-9fb3 #1122 + '2F' // 9fb4 #57 + 'fA' // 9fb5-9fbb + 'f2F' // 9fbc-9fc2 #57 + 'A' // 9fc3 + '2F' // 9fc4 #57 + 'A' // 9fc5 + '2F' // 9fc6 #57 + 'd43E' // 9fc7-9fcb #1122 + '2F' // 9fcc #57 + 'bA' // 9fcd-9fcf + '43E' // 9fd0 #1122 + '1tA' // 9fd1-9fff + '44t73I' // a000-a48c #1906 + 'bA' // a48d-a48f + '2b73I' // a490-a4c6 #1906 + 'hA' // a4c7-a4cf + '1u72E' // a4d0-a4ff #1876 + '11m265A' // a500-a62b #6890 + 'sA' // a62c-a63f + '1tE' // a640-a66e #4 + '41X' // a66f #1089 + '1uE' // a670-a69f #4 + '3i71P' // a6a0-a6f7 #1861 + 'gA' // a6f8-a6ff + '7tE' // a700-a7ca #4 + 'dA' // a7cb-a7cf + 'aE' // a7d0-a7d1 #4 + 'A' // a7d2 + 'E' // a7d3 #4 + 'A' // a7d4 + 'dE' // a7d5-a7d9 #4 + 'wA' // a7da-a7f1 + 'mE' // a7f2-a7ff #4 + '1r264X' // a800-a82c #6887 + 'bA' // a82d-a82f + 'b263Q' // a830-a832 #6854 + 'b263R' // a833-a835 #6855 + 'c263S' // a836-a839 #6856 + 'eA' // a83a-a83f + '2c264R' // a840-a877 #6881 + 'gA' // a878-a87f + '2q72U' // a880-a8c5 #1892 + 'gA' // a8c6-a8cd + 'k72U' // a8ce-a8d9 #1892 + 'eA' // a8da-a8df + 'p17L' // a8e0-a8f0 #453 + '27H' // a8f1 #709 + '17L' // a8f2 #453 + '263U' // a8f3 #6858 + 'j17L' // a8f4-a8fe #453 + '36O' // a8ff #950 + '1s72B' // a900-a92d #1873 + '262H' // a92e #6819 + '72B' // a92f #1873 + '1i72T' // a930-a953 #1891 + 'jA' // a954-a95e + '72T' // a95f #1891 + '1eA' // a960-a97f + '2y50U' // a980-a9cd #1320 + 'A' // a9ce + '263J' // a9cf #6847 + 'i50U' // a9d0-a9d9 #1320 + 'cA' // a9da-a9dd + 'a50U' // a9de-a9df #1320 + '1d42D' // a9e0-a9fe #1095 + 'A' // a9ff + '2b42A' // aa00-aa36 #1092 + 'hA' // aa37-aa3f + 'm42A' // aa40-aa4d #1092 + 'aA' // aa4e-aa4f + 'i42A' // aa50-aa59 #1092 + 'aA' // aa5a-aa5b + 'c42A' // aa5c-aa5f #1092 + '1e42D' // aa60-aa7f #1095 + '2n73A' // aa80-aac2 #1898 + 'wA' // aac3-aada + 'd73A' // aadb-aadf #1898 + 'v50Y' // aae0-aaf6 #1324 + 'iA' // aaf7-ab00 + 'e3R' // ab01-ab06 #95 + 'aA' // ab07-ab08 + 'e3R' // ab09-ab0e #95 + 'aA' // ab0f-ab10 + 'e3R' // ab11-ab16 #95 + 'hA' // ab17-ab1f + 'f3R' // ab20-ab26 #95 + 'A' // ab27 + 'f3R' // ab28-ab2e #95 + 'A' // ab2f + '2gE' // ab30-ab6b #4 + 'cA' // ab6c-ab6f + '3a50Q' // ab70-abbf #1316 + '1s50Y' // abc0-abed #1324 + 'aA' // abee-abef + 'i50Y' // abf0-abf9 #1324 + 'eA' // abfa-abff + '14W' // ac00 #386 + '1A' // ac01 #26 + 'a27F' // ac02-ac03 #707 + 'X' // ac04 #23 + 'a27F' // ac05-ac06 #707 + '1W' // ac07 #48 + 'W' // ac08 #22 + 'f27F' // ac09-ac0f #707 + 'Z' // ac10 #25 + 'V' // ac11 #21 + 'U' // ac12 #20 + '1C' // ac13 #28 + 'U' // ac14 #20 + 'Z' // ac15 #25 + 'U' // ac16 #20 + 'a27F' // ac17-ac18 #707 + '1A' // ac19 #26 + '1W' // ac1a #48 + '27F' // ac1b #707 + 'X' // ac1c #23 + '1A' // ac1d #26 + 'a27F' // ac1e-ac1f #707 + '2B' // ac20 #53 + 'b27F' // ac21-ac23 #707 + 'Y' // ac24 #24 + 'g5U' // ac25-ac2c #150 + '3M' // ac2d #90 + '5U' // ac2e #150 + '1P' // ac2f #41 + '5U' // ac30 #150 + '1G' // ac31 #32 + 'e5U' // ac32-ac37 #150 + '2I' // ac38 #60 + 'f5U' // ac39-ac3f #150 + '1W' // ac40 #48 + 'k5U' // ac41-ac4c #150 + '1P' // ac4d #41 + '1g5U' // ac4e-ac6f #150 + 'X' // ac70 #23 + '1C' // ac71 #28 + 'a5U' // ac72-ac73 #150 + 'Z' // ac74 #25 + 'a5U' // ac75-ac76 #150 + '1G' // ac77 #32 + 'W' // ac78 #22 + 'f5U' // ac79-ac7f #150 + 'Z' // ac80 #25 + 'U' // ac81 #20 + '5U' // ac82 #150 + 'Z' // ac83 #25 + 'd5U' // ac84-ac88 #150 + '1E' // ac89 #30 + 'a5U' // ac8a-ac8b #150 + 'X' // ac8c #23 + 'b5U' // ac8d-ac8f #150 + '1C' // ac90 #28 + 'b5U' // ac91-ac93 #150 + '1J' // ac94 #35 + 'f5U' // ac95-ac9b #150 + '1E' // ac9c #30 + 'a5U' // ac9d-ac9e #150 + '1G' // ac9f #32 + '1A' // aca0 #26 + 'f5U' // aca1-aca7 #150 + 'W' // aca8 #22 + '1A' // aca9 #26 + '1D' // acaa #29 + '5U' // acab #150 + 'W' // acac #22 + 'b5U' // acad-acaf #150 + '1A' // acb0 #26 + 'f5U' // acb1-acb7 #150 + '1C' // acb8 #28 + '1G' // acb9 #32 + 'a5U' // acba-acbb #150 + '1D' // acbc #29 + 'X' // acbd #23 + 'b5U' // acbe-acc0 #150 + '1E' // acc1 #30 + 'a5U' // acc2-acc3 #150 + 'Z' // acc4 #25 + 'z5U' // acc5-acdf #150 + '14W' // ace0 #386 + 'Y' // ace1 #24 + 'a6E' // ace2-ace3 #160 + 'Y' // ace4 #24 + 'a6E' // ace5-ace6 #160 + '1C' // ace7 #28 + 'W' // ace8 #22 + 'f6E' // ace9-acef #160 + 'a1G' // acf0-acf1 #32 + '6E' // acf2 #160 + 'V' // acf3 #21 + '6E' // acf4 #160 + 'X' // acf5 #23 + '2B' // acf6 #53 + 'd6E' // acf7-acfb #160 + 'X' // acfc #23 + '1G' // acfd #32 + 'a6E' // acfe-acff #160 + 'X' // ad00 #23 + 'b6E' // ad01-ad03 #160 + '1E' // ad04 #30 + 'f6E' // ad05-ad0b #160 + '1W' // ad0c #48 + 'c6E' // ad0d-ad10 #160 + 'Z' // ad11 #25 + 'i6E' // ad12-ad1b #160 + '1D' // ad1c #29 + 'v6E' // ad1d-ad33 #160 + 'U' // ad34 #20 + 's6E' // ad35-ad48 #160 + '1F' // ad49 #31 + 'e6E' // ad4a-ad4f #160 + 'Z' // ad50 #25 + 'z6E' // ad51-ad6b #160 + 'X' // ad6c #23 + 'Z' // ad6d #25 + 'a6E' // ad6e-ad6f #160 + 'W' // ad70 #22 + 'a6E' // ad71-ad72 #160 + '1G' // ad73 #32 + 'V' // ad74 #21 + '1E' // ad75 #30 + '2I' // ad76 #60 + 'e6E' // ad77-ad7c #160 + '1E' // ad7d #30 + '6E' // ad7e #160 + '1C' // ad7f #28 + '6E' // ad80 #160 + 'V' // ad81 #21 + 'e6E' // ad82-ad87 #160 + '1W' // ad88 #48 + 'b6E' // ad89-ad8b #160 + '1A' // ad8c #26 + 'b6E' // ad8d-ad8f #160 + '2B' // ad90 #53 + 'j6E' // ad91-ad9b #160 + 'g5O' // ad9c-ada3 #144 + '1W' // ada4 #48 + 'z5O' // ada5-adbf #144 + 'W' // adc0 #22 + 'b5O' // adc1-adc3 #144 + '1P' // adc4 #41 + 'b5O' // adc5-adc7 #144 + '3M' // adc8 #90 + 'i5O' // adc9-add2 #144 + '3M' // add3 #90 + 'g5O' // add4-addb #144 + 'W' // addc #22 + 'b5O' // addd-addf #144 + 'U' // ade0 #20 + 'b5O' // ade1-ade3 #144 + '1P' // ade4 #41 + 'r5O' // ade5-adf7 #144 + 'X' // adf8 #23 + 'V' // adf9 #21 + 'a5O' // adfa-adfb #144 + 'Z' // adfc #25 + 'b5O' // adfd-adff #144 + 'Z' // ae00 #25 + '1W' // ae01 #48 + 'e5O' // ae02-ae07 #144 + 'Z' // ae08 #25 + '1A' // ae09 #26 + '5O' // ae0a #144 + '1F' // ae0b #31 + '5O' // ae0c #144 + '1F' // ae0d #31 + 'e5O' // ae0e-ae13 #144 + '3V' // ae14 #99 + 'z5O' // ae15-ae2f #144 + '14W' // ae30 #386 + 'b5O' // ae31-ae33 #144 + 'V' // ae34 #21 + 'b5O' // ae35-ae37 #144 + '1A' // ae38 #26 + 'f5O' // ae39-ae3f #144 + '1A' // ae40 #26 + '1P' // ae41 #41 + '5O' // ae42 #144 + '1F' // ae43 #31 + '5O' // ae44 #144 + '1E' // ae45 #30 + 'c5O' // ae46-ae49 #144 + '1D' // ae4a #29 + '5O' // ae4b #144 + 'Z' // ae4c #25 + 'a1P' // ae4d-ae4e #41 + '5O' // ae4f #144 + '1C' // ae50 #28 + 'b5O' // ae51-ae53 #144 + 'U' // ae54 #20 + '5O' // ae55 #144 + 'e12R' // ae56-ae5b #329 + '1D' // ae5c #29 + '1F' // ae5d #31 + 'b12R' // ae5e-ae60 #329 + '1E' // ae61 #30 + 'b12R' // ae62-ae64 #329 + '1W' // ae65 #48 + 'a12R' // ae66-ae67 #329 + 'Y' // ae68 #24 + 'b12R' // ae69-ae6b #329 + '3V' // ae6c #99 + 'v12R' // ae6d-ae83 #329 + '2I' // ae84 #60 + '2b12R' // ae85-aebb #329 + 'U' // aebc #20 + '1W' // aebd #48 + '1J' // aebe #35 + '12R' // aebf #329 + '3V' // aec0 #99 + 'b12R' // aec1-aec3 #329 + '1W' // aec4 #48 + 'f12R' // aec5-aecb #329 + '2I' // aecc #60 + '1J' // aecd #35 + '12R' // aece #329 + '1F' // aecf #31 + '12R' // aed0 #329 + '1P' // aed1 #41 + 'e12R' // aed2-aed7 #329 + '1A' // aed8 #26 + 'z12R' // aed9-aef3 #329 + '1C' // aef4 #28 + 'm12R' // aef5-af02 #329 + 'd19N' // af03-af07 #507 + '1J' // af08 #35 + '1h19N' // af09-af2b #507 + 'U' // af2c #20 + 'Y' // af2d #24 + 'e19N' // af2e-af33 #507 + '1C' // af34 #28 + 'f19N' // af35-af3b #507 + '1F' // af3c #31 + '1G' // af3d #32 + 'b19N' // af3e-af40 #507 + '1J' // af41 #35 + '1E' // af42 #30 + 'V' // af43 #21 + 'c19N' // af44-af47 #507 + '3M' // af48 #90 + '1J' // af49 #35 + 'r19N' // af4a-af5c #507 + '2B' // af5d #53 + 'e19N' // af5e-af63 #507 + '1F' // af64 #31 + 'z19N' // af65-af7f #507 + '2B' // af80 #53 + '1p19N' // af81-afab #507 + 'k7U' // afac-afb7 #202 + 'V' // afb8 #21 + '1P' // afb9 #41 + 'a7U' // afba-afbb #202 + '1C' // afbc #28 + 'b7U' // afbd-afbf #202 + 'U' // afc0 #20 + 'e7U' // afc1-afc6 #202 + '3V' // afc7 #99 + 'U' // afc8 #20 + '3V' // afc9 #99 + 'b7U' // afca-afcc #202 + '2B' // afcd #53 + 'e7U' // afce-afd3 #202 + '1F' // afd4 #31 + 'r7U' // afd5-afe7 #202 + '1W' // afe8 #48 + 'f7U' // afe9-afef #202 + '2B' // aff0 #53 + 'z7U' // aff1-b00b #202 + '1G' // b00c #32 + 'b7U' // b00d-b00f #202 + '1J' // b010 #35 + 'b7U' // b011-b013 #202 + '3M' // b014 #90 + 'f7U' // b015-b01b #202 + '3V' // b01c #99 + 'j7U' // b01d-b027 #202 + '3V' // b028 #99 + 'z7U' // b029-b043 #202 + '1D' // b044 #29 + 'b7U' // b045-b047 #202 + '1C' // b048 #28 + '7U' // b049 #202 + '1G' // b04a #32 + '7U' // b04b #202 + 'U' // b04c #20 + 'e7U' // b04d-b052 #202 + '1J' // b053 #35 + '1D' // b054 #29 + 'a7U' // b055-b056 #202 + '1G' // b057 #32 + 'd7U' // b058-b05c #202 + 'V' // b05d #21 + '7U' // b05e #202 + '1b5T' // b05f-b07b #149 + 'V' // b07c #21 + '2I' // b07d #60 + 'a5T' // b07e-b07f #149 + '1F' // b080 #31 + 'b5T' // b081-b083 #149 + '1F' // b084 #31 + 'f5T' // b085-b08b #149 + 'U' // b08c #20 + 'j5T' // b08d-b097 #149 + 'X' // b098 #23 + 'U' // b099 #20 + '1C' // b09a #28 + '5T' // b09b #149 + '1A' // b09c #26 + 'b5T' // b09d-b09f #149 + '1A' // b0a0 #26 + '2B' // b0a1 #53 + 'e5T' // b0a2-b0a7 #149 + 'Z' // b0a8 #25 + 'U' // b0a9 #20 + '5T' // b0aa #149 + '1E' // b0ab #30 + 'U' // b0ac #20 + '1C' // b0ad #28 + 'U' // b0ae #20 + '1E' // b0af #30 + '5T' // b0b0 #149 + '2I' // b0b1 #60 + '5T' // b0b2 #149 + '1E' // b0b3 #30 + 'X' // b0b4 #23 + '2B' // b0b5 #53 + 'a5T' // b0b6-b0b7 #149 + 'U' // b0b8 #20 + 'b5T' // b0b9-b0bb #149 + '1C' // b0bc #28 + 'f5T' // b0bd-b0c3 #149 + '1G' // b0c4 #32 + '1F' // b0c5 #31 + 'a5T' // b0c6-b0c7 #149 + 'a1D' // b0c8-b0c9 #29 + 'e5T' // b0ca-b0cf #149 + 'Y' // b0d0 #24 + 'n5T' // b0d1-b0df #149 + '1P' // b0e0 #41 + 'c5T' // b0e1-b0e4 #149 + 'Y' // b0e5 #24 + '1g5T' // b0e6-b107 #149 + '1A' // b108 #26 + '1J' // b109 #35 + 'a5T' // b10a-b10b #149 + '1F' // b10c #31 + 'b5T' // b10d-b10f #149 + 'V' // b110 #21 + 'a5T' // b111-b112 #149 + '1C' // b113 #28 + 'c5T' // b114-b117 #149 + 'V' // b118 #21 + 'a5T' // b119-b11a #149 + '1J' // b11b #35 + 'c5T' // b11c-b11f #149 + 'b8L' // b120-b122 #219 + 'U' // b123 #20 + 'Z' // b124 #25 + '1C' // b125 #28 + 'a8L' // b126-b127 #219 + '1J' // b128 #35 + 'b8L' // b129-b12b #219 + '1E' // b12c #30 + 'f8L' // b12d-b133 #219 + 'a3M' // b134-b135 #90 + '8L' // b136 #219 + 'W' // b137 #22 + '8L' // b138 #219 + '1W' // b139 #48 + 'e8L' // b13a-b13f #219 + 'W' // b140 #22 + '1C' // b141 #28 + 'a8L' // b142-b143 #219 + 'Z' // b144 #25 + 'j8L' // b145-b14f #219 + 'Y' // b150 #24 + 'b8L' // b151-b153 #219 + '1J' // b154 #35 + 'Y' // b155 #24 + 'a8L' // b156-b157 #219 + '3M' // b158 #90 + '1d8L' // b159-b177 #219 + 'Z' // b178 #25 + 'U' // b179 #20 + 'a8L' // b17a-b17b #219 + 'V' // b17c #21 + 'b8L' // b17d-b17f #219 + 'V' // b180 #21 + 'f8L' // b181-b187 #219 + '1G' // b188 #32 + 'c8L' // b189-b18c #219 + 'V' // b18d #21 + 'c8L' // b18e-b191 #219 + 'V' // b192 #21 + 'Y' // b193 #24 + '1E' // b194 #30 + 'r8L' // b195-b1a7 #219 + '1E' // b1a8 #30 + '1h8L' // b1a9-b1cb #219 + '1G' // b1cc #32 + 'h8L' // b1cd-b1d5 #219 + 'q19M' // b1d6-b1e7 #506 + '1F' // b1e8 #31 + 's19M' // b1e9-b1fc #506 + '2B' // b1fd #53 + 'e19M' // b1fe-b203 #506 + '1A' // b204 #26 + '1W' // b205 #48 + 'a19M' // b206-b207 #506 + 'W' // b208 #22 + 'b19M' // b209-b20b #506 + '1C' // b20c #28 + 'f19M' // b20d-b213 #506 + '1G' // b214 #32 + '2I' // b215 #60 + 'i19M' // b216-b21f #506 + '1E' // b220 #30 + '2b19M' // b221-b257 #506 + '1E' // b258 #30 + 'z19M' // b259-b273 #506 + 'Z' // b274 #25 + 'f19M' // b275-b27b #506 + 'g4V' // b27c-b283 #125 + '1E' // b284 #30 + 'j4V' // b285-b28f #125 + 'W' // b290 #22 + '1J' // b291 #35 + 'a4V' // b292-b293 #125 + 'X' // b294 #23 + 'b4V' // b295-b297 #125 + '1A' // b298 #26 + '1P' // b299 #41 + 'e4V' // b29a-b29f #125 + '2I' // b2a0 #60 + 'c4V' // b2a1-b2a4 #125 + 'Z' // b2a5 #25 + '1D' // b2a6 #29 + 'b4V' // b2a7-b2a9 #125 + '3M' // b2aa #90 + '4V' // b2ab #125 + '1F' // b2ac #31 + 'z4V' // b2ad-b2c7 #125 + 'X' // b2c8 #23 + 'Y' // b2c9 #24 + 'a4V' // b2ca-b2cb #125 + 'V' // b2cc #21 + 'b4V' // b2cd-b2cf #125 + '1D' // b2d0 #29 + 'f4V' // b2d1-b2d7 #125 + '1A' // b2d8 #26 + '1C' // b2d9 #28 + '4V' // b2da #125 + '1G' // b2db #32 + '4V' // b2dc #125 + 'Y' // b2dd #24 + 'e4V' // b2de-b2e3 #125 + '14W' // b2e4 #386 + 'U' // b2e5 #20 + '1J' // b2e6 #35 + '4V' // b2e7 #125 + 'Z' // b2e8 #25 + 'a4V' // b2e9-b2ea #125 + 'a1A' // b2eb-b2ec #26 + '1C' // b2ed #28 + '1G' // b2ee #32 + 'd4V' // b2ef-b2f3 #125 + '1A' // b2f4 #26 + 'W' // b2f5 #22 + '4V' // b2f6 #125 + 'U' // b2f7 #20 + '4V' // b2f8 #125 + 'Z' // b2f9 #25 + 'a4V' // b2fa-b2fb #125 + '2B' // b2fc #53 + 'a4V' // b2fd-b2fe #125 + '1E' // b2ff #30 + 'X' // b300 #23 + '1J' // b301 #35 + 'a4V' // b302-b303 #125 + '1G' // b304 #32 + 'b4V' // b305-b307 #125 + '3V' // b308 #99 + 'f4V' // b309-b30f #125 + '2I' // b310 #60 + '3V' // b311 #99 + '4V' // b312 #125 + '1A' // b313 #26 + '3V' // b314 #99 + '1E' // b315 #30 + 'e4V' // b316-b31b #125 + '3V' // b31c #99 + '1j4V' // b31d-b341 #125 + 'q7F' // b342-b353 #187 + 'Z' // b354 #25 + 'V' // b355 #21 + 'a7F' // b356-b357 #187 + '1A' // b358 #26 + 'b7F' // b359-b35b #187 + '1D' // b35c #29 + 'a7F' // b35d-b35e #187 + '3M' // b35f #90 + 'c7F' // b360-b363 #187 + '1D' // b364 #29 + '1P' // b365 #41 + '7F' // b366 #187 + '1C' // b367 #28 + '7F' // b368 #187 + '1C' // b369 #28 + 'c7F' // b36a-b36d #187 + '1F' // b36e #31 + '7F' // b36f #187 + 'Z' // b370 #25 + '1E' // b371 #30 + 'a7F' // b372-b373 #187 + '1C' // b374 #28 + 'b7F' // b375-b377 #187 + 'V' // b378 #21 + 'f7F' // b379-b37f #187 + '2B' // b380 #53 + 'a7F' // b381-b382 #187 + '1W' // b383 #48 + '7F' // b384 #187 + '1J' // b385 #35 + 'e7F' // b386-b38b #187 + '1W' // b38c #48 + '2b7F' // b38d-b3c3 #187 + 'X' // b3c4 #23 + 'W' // b3c5 #22 + 'a7F' // b3c6-b3c7 #187 + 'Y' // b3c8 #24 + 'a7F' // b3c9-b3ca #187 + '1C' // b3cb #28 + 'W' // b3cc #22 + 'f7F' // b3cd-b3d3 #187 + 'a1E' // b3d4-b3d5 #30 + '7F' // b3d6 #187 + '2I' // b3d7 #60 + '7F' // b3d8 #187 + 'X' // b3d9 #23 + '1c7F' // b3da-b3f7 #187 + 'c12Q' // b3f8-b3fb #328 + 'Y' // b3fc #24 + 'r12Q' // b3fd-b40f #328 + 'Y' // b410 #24 + 'f12Q' // b411-b417 #328 + 'Z' // b418 #25 + 'b12Q' // b419-b41b #328 + 'Z' // b41c #25 + 'b12Q' // b41d-b41f #328 + 'W' // b420 #22 + 'f12Q' // b421-b427 #328 + '1D' // b428 #29 + 'W' // b429 #22 + 'a12Q' // b42a-b42b #328 + '1W' // b42c #48 + '1h12Q' // b42d-b44f #328 + '1A' // b450 #26 + '1F' // b451 #31 + 'a12Q' // b452-b453 #328 + '1D' // b454 #29 + 'b12Q' // b455-b457 #328 + 'Y' // b458 #24 + 'f12Q' // b459-b45f #328 + '1E' // b460 #30 + '1W' // b461 #48 + 'b12Q' // b462-b464 #328 + '1D' // b465 #29 + 'e12Q' // b466-b46b #328 + '1P' // b46c #41 + 'r12Q' // b46d-b47f #328 + '1P' // b480 #41 + '1h12Q' // b481-b4a3 #328 + 'V' // b4a4 #21 + 'q6D' // b4a5-b4b6 #159 + '1D' // b4b7 #29 + 'g6D' // b4b8-b4bf #159 + 'U' // b4c0 #20 + 'f6D' // b4c1-b4c7 #159 + '1P' // b4c8 #41 + 'r6D' // b4c9-b4db #159 + 'X' // b4dc #23 + 'Y' // b4dd #24 + 'a6D' // b4de-b4df #159 + '1A' // b4e0 #26 + 'a6D' // b4e1-b4e2 #159 + '1D' // b4e3 #29 + 'X' // b4e4 #23 + 'f6D' // b4e5-b4eb #159 + '1C' // b4ec #28 + '1G' // b4ed #32 + '6D' // b4ee #159 + 'V' // b4ef #21 + '6D' // b4f0 #159 + 'Z' // b4f1 #25 + '1g6D' // b4f2-b513 #159 + 'Z' // b514 #25 + '1F' // b515 #31 + 'a6D' // b516-b517 #159 + '1G' // b518 #32 + 'a6D' // b519-b51a #159 + '2B' // b51b #53 + '1D' // b51c #29 + 'f6D' // b51d-b523 #159 + '3V' // b524 #99 + '1J' // b525 #35 + '6D' // b526 #159 + '1E' // b527 #30 + '6D' // b528 #159 + 'W' // b529 #22 + '2I' // b52a #60 + 'd6D' // b52b-b52f #159 + 'W' // b530 #22 + 'U' // b531 #20 + 'a6D' // b532-b533 #159 + '1E' // b534 #30 + 'b6D' // b535-b537 #159 + 'U' // b538 #20 + 'f6D' // b539-b53f #159 + '1F' // b540 #31 + 'c6D' // b541-b544 #159 + 'U' // b545 #20 + 'e6D' // b546-b54b #159 + '1A' // b54c #26 + 'b6D' // b54d-b54f #159 + 'U' // b550 #20 + 'j6D' // b551-b55b #159 + '1W' // b55c #48 + 'a6D' // b55d-b55e #159 + '24F' // b55f #629 + '3V' // b560 #99 + '1F' // b561 #31 + '1g24F' // b562-b583 #629 + '2B' // b584 #53 + 'z24F' // b585-b59f #629 + 'Y' // b5a0 #24 + '1D' // b5a1 #29 + 'a24F' // b5a2-b5a3 #629 + 'Y' // b5a4 #24 + 'b24F' // b5a5-b5a7 #629 + 'U' // b5a8 #20 + 'i24F' // b5a9-b5b2 #629 + '2B' // b5b3 #53 + '1J' // b5b4 #35 + 'e24F' // b5b5-b5ba #629 + 'Y' // b5bb #24 + '1G' // b5bc #32 + '2s24F' // b5bd-b604 #629 + 'j31S' // b605-b60f #824 + '1A' // b610 #26 + '1C' // b611 #28 + 'e31S' // b612-b617 #824 + '2I' // b618 #60 + 'k31S' // b619-b624 #824 + '1G' // b625 #32 + '4m31S' // b626-b69b #824 + 'a1G' // b69c-b69d #32 + 'e31S' // b69e-b6a3 #824 + '3M' // b6a4 #90 + 'a31S' // b6a5-b6a6 #824 + 'c19L' // b6a7-b6aa #505 + '1F' // b6ab #31 + 'd19L' // b6ac-b6b0 #505 + '1F' // b6b1 #31 + '2i19L' // b6b2-b6ef #505 + 'U' // b6f0 #20 + '2b19L' // b6f1-b727 #505 + 'U' // b728 #20 + '1J' // b729 #35 + 'a19L' // b72a-b72b #505 + '1F' // b72c #31 + 'a19L' // b72d-b72e #505 + '1J' // b72f #35 + '1F' // b730 #31 + 'f19L' // b731-b737 #505 + '1J' // b738 #35 + 'a19L' // b739-b73a #505 + 'U' // b73b #20 + 'g19L' // b73c-b743 #505 + '1F' // b744 #31 + 'g19L' // b745-b74c #505 + 'r7T' // b74d-b75f #201 + '1C' // b760 #28 + 'b7T' // b761-b763 #201 + '3V' // b764 #99 + 'o7T' // b765-b774 #201 + '2I' // b775 #60 + 'e7T' // b776-b77b #201 + 'X' // b77c #23 + 'W' // b77d #22 + 'a7T' // b77e-b77f #201 + '1A' // b780 #26 + 'b7T' // b781-b783 #201 + '1D' // b784 #29 + 'f7T' // b785-b78b #201 + '1A' // b78c #26 + 'V' // b78d #21 + '7T' // b78e #201 + '1W' // b78f #48 + '1D' // b790 #29 + '1A' // b791 #26 + 'd7T' // b792-b796 #201 + '2I' // b797 #60 + 'Z' // b798 #25 + 'V' // b799 #21 + 'a7T' // b79a-b79b #201 + 'W' // b79c #22 + 'j7T' // b79d-b7a7 #201 + 'W' // b7a8 #22 + 'V' // b7a9 #21 + '7T' // b7aa #201 + '1D' // b7ab #29 + '1G' // b7ac #32 + 'U' // b7ad #20 + 'e7T' // b7ae-b7b3 #201 + '1P' // b7b4 #41 + 'Y' // b7b5 #24 + 'r7T' // b7b6-b7c8 #201 + 'W' // b7c9 #22 + '1g7T' // b7ca-b7eb #201 + 'X' // b7ec #23 + 'U' // b7ed #20 + 'a7T' // b7ee-b7ef #201 + 'W' // b7f0 #22 + 'b7T' // b7f1-b7f3 #201 + '1D' // b7f4 #29 + 'f7T' // b7f5-b7fb #201 + 'W' // b7fc #22 + 'V' // b7fd #21 + '7T' // b7fe #201 + '1J' // b7ff #35 + '1F' // b800 #31 + '1G' // b801 #32 + 'd7T' // b802-b806 #201 + 'V' // b807 #21 + 'Z' // b808 #25 + 'U' // b809 #20 + 'a7T' // b80a-b80b #201 + 'V' // b80c #21 + 'b10I' // b80d-b80f #268 + '1E' // b810 #30 + 'f10I' // b811-b817 #268 + '1E' // b818 #30 + '1P' // b819 #41 + '10I' // b81a #268 + '1C' // b81b #28 + 'g10I' // b81c-b823 #268 + 'aZ' // b824-b825 #25 + 'a10I' // b826-b827 #268 + 'W' // b828 #22 + 'b10I' // b829-b82b #268 + '1D' // b82c #29 + 'f10I' // b82d-b833 #268 + '1C' // b834 #28 + '1D' // b835 #29 + '10I' // b836 #268 + '1J' // b837 #35 + 'Y' // b838 #24 + 'V' // b839 #21 + 'e10I' // b83a-b83f #268 + 'Y' // b840 #24 + 'z10I' // b841-b85b #268 + '14W' // b85c #386 + 'X' // b85d #23 + 'a10I' // b85e-b85f #268 + 'W' // b860 #22 + 'b10I' // b861-b863 #268 + 'U' // b864 #20 + 'f10I' // b865-b86b #268 + '1G' // b86c #32 + 'U' // b86d #20 + '10I' // b86e #268 + 'Y' // b86f #24 + '10I' // b870 #268 + 'U' // b871 #20 + '2i10I' // b872-b8af #268 + '1C' // b8b0 #28 + 'm10I' // b8b1-b8be #268 + 'l10H' // b8bf-b8cb #267 + 'Z' // b8cc #25 + 's10H' // b8cd-b8e0 #267 + '1C' // b8e1 #28 + 'e10H' // b8e2-b8e7 #267 + '1A' // b8e8 #26 + '1D' // b8e9 #29 + 'a10H' // b8ea-b8eb #267 + '1F' // b8ec #31 + 'b10H' // b8ed-b8ef #267 + '1F' // b8f0 #31 + 'f10H' // b8f1-b8f7 #267 + '1D' // b8f8 #29 + 'Y' // b8f9 #24 + '10H' // b8fa #267 + '1W' // b8fb #48 + '10H' // b8fc #267 + '2B' // b8fd #53 + 'e10H' // b8fe-b903 #267 + '1F' // b904 #31 + 'r10H' // b905-b917 #267 + '1P' // b918 #41 + 'f10H' // b919-b91f #267 + '3V' // b920 #99 + 'z10H' // b921-b93b #267 + '2B' // b93c #53 + 'z10H' // b93d-b957 #267 + '1A' // b958 #26 + '1G' // b959 #32 + 'a10H' // b95a-b95b #267 + '1G' // b95c #32 + 'b10H' // b95d-b95f #267 + 'Y' // b960 #24 + 'f10H' // b961-b967 #267 + '1F' // b968 #31 + 'c10H' // b969-b96c #267 + '1F' // b96d #31 + 'e5N' // b96e-b973 #143 + '1A' // b974 #26 + '1J' // b975 #35 + 'a5N' // b976-b977 #143 + 'Z' // b978 #25 + 'b5N' // b979-b97b #143 + 'X' // b97c #23 + 'f5N' // b97d-b983 #143 + '1A' // b984 #26 + '1G' // b985 #32 + '5N' // b986 #143 + '1F' // b987 #31 + '5N' // b988 #143 + '1C' // b989 #28 + 'c5N' // b98a-b98d #143 + '1E' // b98e #30 + '1b5N' // b98f-b9ab #143 + '14W' // b9ac #386 + 'W' // b9ad #22 + 'a5N' // b9ae-b9af #143 + '1A' // b9b0 #26 + 'b5N' // b9b1-b9b3 #143 + 'V' // b9b4 #21 + 'f5N' // b9b5-b9bb #143 + 'a1A' // b9bc-b9bd #26 + '5N' // b9be #143 + 'U' // b9bf #20 + '5N' // b9c0 #143 + 'W' // b9c1 #22 + 'e5N' // b9c2-b9c7 #143 + 'X' // b9c8 #23 + 'W' // b9c9 #22 + 'a5N' // b9ca-b9cb #143 + 'X' // b9cc #23 + '5N' // b9cd #143 + '1A' // b9ce #26 + '3M' // b9cf #90 + 'Z' // b9d0 #25 + '1C' // b9d1 #28 + 'e5N' // b9d2-b9d7 #143 + '1D' // b9d8 #29 + '1F' // b9d9 #31 + '5N' // b9da #143 + 'V' // b9db #21 + '5N' // b9dc #143 + 'aW' // b9dd-b9de #22 + 'a5N' // b9df-b9e0 #143 + '1C' // b9e1 #28 + '5N' // b9e2 #143 + '3V' // b9e3 #99 + 'Z' // b9e4 #25 + 'Y' // b9e5 #24 + 'a5N' // b9e6-b9e7 #143 + 'W' // b9e8 #22 + 'j5N' // b9e9-b9f3 #143 + '2B' // b9f4 #53 + 'U' // b9f5 #20 + '5N' // b9f6 #143 + '1W' // b9f7 #48 + '5N' // b9f8 #143 + '1D' // b9f9 #29 + '1C' // b9fa #28 + '1z5N' // b9fb-ba2f #143 + 'g6R' // ba30-ba37 #173 + 'aW' // ba38-ba39 #22 + 'a6R' // ba3a-ba3b #173 + 'V' // ba3c #21 + 'b6R' // ba3d-ba3f #173 + 'U' // ba40 #20 + 'f6R' // ba41-ba47 #173 + '1G' // ba48 #32 + 'a6R' // ba49-ba4a #173 + 'U' // ba4b #20 + '6R' // ba4c #173 + '1C' // ba4d #28 + 'e6R' // ba4e-ba53 #173 + 'X' // ba54 #23 + '1E' // ba55 #30 + 'a6R' // ba56-ba57 #173 + 'U' // ba58 #20 + 'b6R' // ba59-ba5b #173 + '1D' // ba5c #29 + 'f6R' // ba5d-ba63 #173 + '1D' // ba64 #29 + 'a6R' // ba65-ba66 #173 + '1J' // ba67 #35 + 'g6R' // ba68-ba6f #173 + '1A' // ba70 #26 + 'b6R' // ba71-ba73 #173 + 'X' // ba74 #23 + 'b6R' // ba75-ba77 #173 + '1C' // ba78 #28 + 'k6R' // ba79-ba84 #173 + 'X' // ba85 #23 + '6R' // ba86 #173 + 'U' // ba87 #20 + '1e6R' // ba88-baa7 #173 + 'X' // baa8 #23 + 'Z' // baa9 #25 + '6R' // baaa #173 + '1P' // baab #41 + 'U' // baac #20 + 'b6R' // baad-baaf #173 + 'V' // bab0 #21 + 'f6R' // bab1-bab7 #173 + 'Y' // bab8 #24 + '1P' // bab9 #41 + '6R' // baba #173 + 'W' // babb #22 + '6R' // babc #173 + '1D' // babd #29 + '1m6R' // babe-bae5 #173 + 'u14V' // bae6-bafb #385 + '1W' // bafc #48 + 'z14V' // bafd-bb17 #385 + '1D' // bb18 #29 + 'z14V' // bb19-bb33 #385 + 'X' // bb34 #23 + '1C' // bb35 #28 + '1F' // bb36 #31 + '14V' // bb37 #385 + 'X' // bb38 #23 + 'a14V' // bb39-bb3a #385 + '1D' // bb3b #29 + 'Z' // bb3c #25 + 'f14V' // bb3d-bb43 #385 + '2B' // bb44 #53 + 'a14V' // bb45-bb46 #385 + '1J' // bb47 #35 + '14V' // bb48 #385 + '1F' // bb49 #31 + 'e14V' // bb4a-bb4f #385 + 'Y' // bb50 #24 + 'b14V' // bb51-bb53 #385 + '1C' // bb54 #28 + 'b14V' // bb55-bb57 #385 + '1G' // bb58 #32 + 'i14V' // bb59-bb62 #385 + '3M' // bb63 #90 + '1q14V' // bb64-bb8f #385 + 's5M' // bb90-bba3 #142 + 'V' // bba4 #21 + 'f5M' // bba5-bbab #142 + '1E' // bbac #30 + 'r5M' // bbad-bbbf #142 + 'U' // bbc0 #20 + '2b5M' // bbc1-bbf7 #142 + 'X' // bbf8 #23 + '1C' // bbf9 #28 + 'a5M' // bbfa-bbfb #142 + '1A' // bbfc #26 + 'a5M' // bbfd-bbfe #142 + '1D' // bbff #29 + 'W' // bc00 #22 + 'f5M' // bc01-bc07 #142 + '2B' // bc08 #53 + 'a5M' // bc09-bc0a #142 + '1E' // bc0b #30 + '1G' // bc0c #32 + '1D' // bc0d #29 + '5M' // bc0e #142 + '1A' // bc0f #26 + '5M' // bc10 #142 + '1C' // bc11 #28 + 'a5M' // bc12-bc13 #142 + 'X' // bc14 #23 + '1A' // bc15 #26 + 'U' // bc16 #20 + '5M' // bc17 #142 + 'Z' // bc18 #25 + 'a5M' // bc19-bc1a #142 + '1A' // bc1b #26 + 'Z' // bc1c #25 + 'Y' // bc1d #24 + '5M' // bc1e #142 + '1J' // bc1f #35 + 'c5M' // bc20-bc23 #142 + 'aY' // bc24-bc25 #24 + '5M' // bc26 #142 + '2B' // bc27 #53 + '5M' // bc28 #142 + 'X' // bc29 #23 + 'b5M' // bc2a-bc2c #142 + '1F' // bc2d #31 + 'a5M' // bc2e-bc2f #142 + 'Z' // bc30 #25 + '1A' // bc31 #26 + 'a5M' // bc32-bc33 #142 + 'U' // bc34 #20 + 'b5M' // bc35-bc37 #142 + '1G' // bc38 #32 + 'f5M' // bc39-bc3f #142 + '1E' // bc40 #30 + 'a5M' // bc41-bc42 #142 + '1F' // bc43 #31 + '5M' // bc44 #142 + '1D' // bc45 #29 + 'b5M' // bc46-bc48 #142 + '2B' // bc49 #53 + 'c5M' // bc4a-bc4d #142 + '2a8K' // bc4e-bc83 #218 + 'Z' // bc84 #25 + '1G' // bc85 #32 + 'a8K' // bc86-bc87 #218 + 'X' // bc88 #23 + 'b8K' // bc89-bc8b #218 + 'W' // bc8c #22 + 'f8K' // bc8d-bc93 #218 + 'V' // bc94 #21 + '1A' // bc95 #26 + '8K' // bc96 #218 + '1D' // bc97 #29 + '8K' // bc98 #218 + '1W' // bc99 #48 + '1G' // bc9a #32 + 'd8K' // bc9b-bc9f #218 + 'W' // bca0 #22 + '1E' // bca1 #30 + 'a8K' // bca2-bca3 #218 + 'W' // bca4 #22 + 'b8K' // bca5-bca7 #218 + 'Y' // bca8 #24 + 'i8K' // bca9-bcb2 #218 + '1C' // bcb3 #28 + 'g8K' // bcb4-bcbb #218 + '1D' // bcbc #29 + 'Y' // bcbd #24 + 'a8K' // bcbe-bcbf #218 + '1A' // bcc0 #26 + 'b8K' // bcc1-bcc3 #218 + '1A' // bcc4 #26 + 'g8K' // bcc5-bccc #218 + '1E' // bccd #30 + 'b8K' // bcce-bcd0 #218 + 'W' // bcd1 #22 + 'b8K' // bcd2-bcd4 #218 + '2I' // bcd5 #60 + '1c8K' // bcd6-bcf3 #218 + 'X' // bcf4 #23 + 'Z' // bcf5 #25 + '1F' // bcf6 #31 + '8K' // bcf7 #218 + 'Z' // bcf8 #25 + 'b8K' // bcf9-bcfb #218 + 'W' // bcfc #22 + 'e8K' // bcfd-bd02 #218 + '12P' // bd03 #327 + 'Y' // bd04 #24 + '1C' // bd05 #28 + '12P' // bd06 #327 + 'U' // bd07 #20 + '12P' // bd08 #327 + 'W' // bd09 #22 + 'e12P' // bd0a-bd0f #327 + 'Y' // bd10 #24 + 'q12P' // bd11-bd22 #327 + '3M' // bd23 #90 + 'Y' // bd24 #24 + '1h12P' // bd25-bd47 #327 + '3V' // bd48 #99 + 'o12P' // bd49-bd58 #327 + '2I' // bd59 #60 + '1k12P' // bd5a-bd7f #327 + 'X' // bd80 #23 + '1A' // bd81 #26 + 'a12P' // bd82-bd83 #327 + 'Z' // bd84 #25 + 'b12P' // bd85-bd87 #327 + '1A' // bd88 #26 + '1F' // bd89 #31 + 'e12P' // bd8a-bd8f #327 + '1J' // bd90 #35 + 'a12P' // bd91-bd92 #327 + '1E' // bd93 #30 + '12P' // bd94 #327 + '1C' // bd95 #28 + 'b12P' // bd96-bd98 #327 + 'U' // bd99 #20 + 'u12P' // bd9a-bdaf #327 + '1i21Q' // bdb0-bdd3 #562 + '1C' // bdd4 #28 + 'z21Q' // bdd5-bdef #562 + 'W' // bdf0 #22 + 'z21Q' // bdf1-be0b #562 + 'Z' // be0c #25 + 'b21Q' // be0d-be0f #562 + '1C' // be10 #28 + 'b21Q' // be11-be13 #562 + 'Z' // be14 #25 + '1t21Q' // be15-be43 #562 + 'X' // be44 #23 + '1D' // be45 #29 + 'a21Q' // be46-be47 #562 + 'Y' // be48 #24 + 'b21Q' // be49-be4b #562 + 'V' // be4c #21 + 'f21Q' // be4d-be53 #562 + '1E' // be54 #30 + '2B' // be55 #53 + '11F' // be56 #291 + '1F' // be57 #31 + '11F' // be58 #291 + 'U' // be59 #20 + '1E' // be5a #30 + 'Y' // be5b #24 + 'c11F' // be5c-be5f #291 + 'W' // be60 #22 + '1F' // be61 #31 + 'a11F' // be62-be63 #291 + '2B' // be64 #53 + 'b11F' // be65-be67 #291 + 'Y' // be68 #24 + 'k11F' // be69-be74 #291 + '1D' // be75 #29 + 'e11F' // be76-be7b #291 + '1D' // be7c #29 + '3V' // be7d #99 + 'a11F' // be7e-be7f #291 + '2I' // be80 #60 + 'm11F' // be81-be8e #291 + '1P' // be8f #41 + '11F' // be90 #291 + '3M' // be91 #90 + 'u11F' // be92-bea7 #291 + '2I' // bea8 #60 + '1l11F' // bea9-becf #291 + '1C' // bed0 #28 + '2I' // bed1 #60 + 'a11F' // bed2-bed3 #291 + '1F' // bed4 #31 + 'a11F' // bed5-bed6 #291 + '1P' // bed7 #41 + '1W' // bed8 #48 + 'j11F' // bed9-bee3 #291 + 'a2I' // bee4-bee5 #60 + '1f11F' // bee6-bf06 #291 + '36M' // bf07 #948 + '1E' // bf08 #30 + '2b36M' // bf09-bf3f #948 + '1G' // bf40 #32 + 'n36M' // bf41-bf4f #948 + '1E' // bf50 #30 + '1C' // bf51 #28 + 'b36M' // bf52-bf54 #948 + '1E' // bf55 #30 + '3b36M' // bf56-bfa6 #948 + 'h27E' // bfa7-bfaf #706 + '2B' // bfb0 #53 + 's27E' // bfb1-bfc4 #706 + '2B' // bfc5 #53 + 'e27E' // bfc6-bfcb #706 + '1D' // bfcc #29 + '1W' // bfcd #48 + 'a27E' // bfce-bfcf #706 + 'U' // bfd0 #20 + 'b27E' // bfd1-bfd3 #706 + '1J' // bfd4 #35 + 'f27E' // bfd5-bfdb #706 + '1E' // bfdc #30 + '4c27E' // bfdd-c048 #706 + 'n6Q' // c049-c057 #172 + 'U' // c058 #20 + 'b6Q' // c059-c05b #172 + 'Y' // c05c #24 + 'b6Q' // c05d-c05f #172 + '2I' // c060 #60 + 'f6Q' // c061-c067 #172 + '1G' // c068 #32 + '1l6Q' // c069-c08f #172 + '1F' // c090 #31 + 'z6Q' // c091-c0ab #172 + '14W' // c0ac #386 + 'W' // c0ad #22 + 'a6Q' // c0ae-c0af #172 + 'Z' // c0b0 #25 + 'b6Q' // c0b1-c0b3 #172 + 'W' // c0b4 #22 + '6Q' // c0b5 #172 + '1D' // c0b6 #29 + 'd6Q' // c0b7-c0bb #172 + 'W' // c0bc #22 + '1G' // c0bd #32 + '6Q' // c0be #172 + '1W' // c0bf #48 + '1E' // c0c0 #30 + 'X' // c0c1 #23 + 'e6Q' // c0c2-c0c7 #172 + '1A' // c0c8 #26 + 'Z' // c0c9 #25 + 'a6Q' // c0ca-c0cb #172 + '1G' // c0cc #32 + 'b6Q' // c0cd-c0cf #172 + '1E' // c0d0 #30 + 'f6Q' // c0d1-c0d7 #172 + '1C' // c0d8 #28 + 'a6Q' // c0d9-c0da #172 + '3V' // c0db #99 + '6Q' // c0dc #172 + 'Z' // c0dd #25 + 'e6Q' // c0de-c0e3 #172 + 'U' // c0e4 #20 + 'f6Q' // c0e5-c0eb #172 + '1P' // c0ec #41 + 'f6Q' // c0ed-c0f3 #172 + '1E' // c0f4 #30 + 'U' // c0f5 #20 + '6Q' // c0f6 #172 + '1D' // c0f7 #29 + '6Q' // c0f8 #172 + '1W' // c0f9 #48 + 'e6Q' // c0fa-c0ff #172 + '1E' // c100 #30 + 'z6C' // c101-c11b #158 + 'X' // c11c #23 + '1A' // c11d #26 + '1E' // c11e #30 + '6C' // c11f #158 + 'X' // c120 #23 + 'b6C' // c121-c123 #158 + 'Z' // c124 #25 + 'f6C' // c125-c12b #158 + 'Y' // c12c #24 + 'U' // c12d #20 + '6C' // c12e #158 + '1G' // c12f #32 + '1F' // c130 #31 + 'X' // c131 #23 + 'e6C' // c132-c137 #158 + 'X' // c138 #23 + 'U' // c139 #20 + 'a6C' // c13a-c13b #158 + '1A' // c13c #26 + 'b6C' // c13d-c13f #158 + 'Y' // c140 #24 + 'f6C' // c141-c147 #158 + '1F' // c148 #31 + '1G' // c149 #32 + '6C' // c14a #158 + '1D' // c14b #29 + 'g6C' // c14c-c153 #158 + 'V' // c154 #21 + 'b6C' // c155-c157 #158 + '1A' // c158 #26 + 'b6C' // c159-c15b #158 + 'Y' // c15c #24 + 'i6C' // c15d-c166 #158 + '1W' // c167 #48 + 'Y' // c168 #24 + 'f6C' // c169-c16f #158 + '1E' // c170 #30 + 'f6C' // c171-c177 #158 + '2B' // c178 #53 + 'r6C' // c179-c18b #158 + 'X' // c18c #23 + '1A' // c18d #26 + 'a6C' // c18e-c18f #158 + 'W' // c190 #22 + 'b6C' // c191-c193 #158 + 'Y' // c194 #24 + 'f6C' // c195-c19b #158 + '1F' // c19c #31 + 'a6C' // c19d-c19e #158 + '1P' // c19f #41 + '6C' // c1a0 #158 + '1A' // c1a1 #26 + 'b6C' // c1a2-c1a4 #158 + '1P' // c1a5 #41 + 'u6C' // c1a6-c1bb #158 + 'g10G' // c1bc-c1c3 #266 + 'V' // c1c4 #21 + 'z10G' // c1c5-c1df #266 + '1G' // c1e0 #32 + 'z10G' // c1e1-c1fb #266 + 'W' // c1fc #22 + 'f10G' // c1fd-c203 #266 + '1W' // c204 #48 + 'g10G' // c205-c20c #266 + '1E' // c20d #30 + '10G' // c20e #266 + '1W' // c20f #48 + 'g10G' // c210-c217 #266 + 'X' // c218 #23 + 'Y' // c219 #24 + 'a10G' // c21a-c21b #266 + '1A' // c21c #26 + 'a10G' // c21d-c21e #266 + '2B' // c21f #53 + 'W' // c220 #22 + 'f10G' // c221-c227 #266 + 'U' // c228 #20 + 'a10G' // c229-c22a #266 + '1G' // c22b #32 + '10G' // c22c #266 + '1G' // c22d #32 + '10G' // c22e #266 + '1W' // c22f #48 + '10G' // c230 #266 + '3V' // c231 #99 + '1C' // c232 #28 + '1b10G' // c233-c24f #266 + '1G' // c250 #32 + 'f10G' // c251-c257 #266 + '1W' // c258 #48 + 'p10G' // c259-c269 #266 + 'a4J' // c26a-c26b #113 + 'V' // c26c #21 + 'b4J' // c26d-c26f #113 + '3V' // c270 #99 + 'b4J' // c271-c273 #113 + '1E' // c274 #30 + 'f4J' // c275-c27b #113 + '1J' // c27c #35 + 'Y' // c27d #24 + 'i4J' // c27e-c287 #113 + 'W' // c288 #22 + 'f4J' // c289-c28f #113 + '1E' // c290 #30 + 'f4J' // c291-c297 #113 + '1W' // c298 #48 + 'a4J' // c299-c29a #113 + '3M' // c29b #90 + 'g4J' // c29c-c2a3 #113 + '14W' // c2a4 #386 + 'b4J' // c2a5-c2a7 #113 + 'Y' // c2a8 #24 + 'b4J' // c2a9-c2ab #113 + 'V' // c2ac #21 + 'f4J' // c2ad-c2b3 #113 + '1D' // c2b4 #29 + 'X' // c2b5 #23 + '4J' // c2b6 #113 + '1C' // c2b7 #28 + '4J' // c2b8 #113 + 'W' // c2b9 #22 + 'a4J' // c2ba-c2bb #113 + '1W' // c2bc #48 + '1d4J' // c2bd-c2db #113 + '14W' // c2dc #386 + 'Z' // c2dd #25 + 'a4J' // c2de-c2df #113 + 'X' // c2e0 #23 + 'a4J' // c2e1-c2e2 #113 + '3V' // c2e3 #99 + 'Z' // c2e4 #25 + 'e4J' // c2e5-c2ea #113 + '1C' // c2eb #28 + 'Z' // c2ec #25 + 'V' // c2ed #21 + '4J' // c2ee #113 + '2I' // c2ef #60 + '4J' // c2f0 #113 + 'Y' // c2f1 #24 + 'c4J' // c2f2-c2f5 #113 + 'W' // c2f6 #22 + '4J' // c2f7 #113 + 'Y' // c2f8 #24 + '1F' // c2f9 #31 + 'a4J' // c2fa-c2fb #113 + '1G' // c2fc #32 + 'b4J' // c2fd-c2ff #113 + '1G' // c300 #32 + 'f4J' // c301-c307 #113 + '1E' // c308 #30 + 'c4J' // c309-c30c #113 + '1D' // c30d #29 + 'd4J' // c30e-c312 #113 + '1G' // c313 #32 + '2B' // c314 #53 + 'n4J' // c315-c323 #113 + '1J' // c324 #35 + 'c4J' // c325-c328 #113 + '2I' // c329 #60 + '4J' // c32a #113 + '2h24E' // c32b-c367 #628 + 'Y' // c368 #24 + '1E' // c369 #30 + 'a24E' // c36a-c36b #628 + '1F' // c36c #31 + 'b24E' // c36d-c36f #628 + '1G' // c370 #32 + 'f24E' // c371-c377 #628 + '1C' // c378 #28 + '1J' // c379 #35 + 'a24E' // c37a-c37b #628 + '1F' // c37c #31 + '3V' // c37d #99 + 'e24E' // c37e-c383 #628 + '1F' // c384 #31 + 'b24E' // c385-c387 #628 + '1W' // c388 #48 + '2r24E' // c389-c3cf #628 + 'g24D' // c3d0-c3d7 #627 + '1G' // c3d8 #32 + '1P' // c3d9 #41 + 'a24D' // c3da-c3db #627 + '2I' // c3dc #60 + 'a24D' // c3dd-c3de #627 + '1F' // c3df #31 + '1J' // c3e0 #35 + 'k24D' // c3e1-c3ec #627 + '3V' // c3ed #99 + 'e24D' // c3ee-c3f3 #627 + '3M' // c3f4 #90 + '2b24D' // c3f5-c42b #627 + '3M' // c42c #90 + '2b24D' // c42d-c463 #627 + '1P' // c464 #41 + '1J' // c465 #35 + 'n24D' // c466-c474 #627 + '4r31R' // c475-c4ef #823 + '1A' // c4f0 #26 + '2B' // c4f1 #53 + 'a31R' // c4f2-c4f3 #823 + 'V' // c4f4 #21 + 'b31R' // c4f5-c4f7 #823 + '1D' // c4f8 #29 + 'f31R' // c4f9-c4ff #823 + '1C' // c500 #28 + '1W' // c501 #48 + 'i31R' // c502-c50b #823 + '1W' // c50c #48 + 'i31R' // c50d-c516 #823 + 'p2V' // c517-c527 #73 + 'W' // c528 #22 + 'U' // c529 #20 + 'a2V' // c52a-c52b #73 + '1C' // c52c #28 + 'b2V' // c52d-c52f #73 + '3V' // c530 #99 + 'g2V' // c531-c538 #73 + '1E' // c539 #30 + '2V' // c53a #73 + '1J' // c53b #35 + '2V' // c53c #73 + '1P' // c53d #41 + 'e2V' // c53e-c543 #73 + 'X' // c544 #23 + 'W' // c545 #22 + 'a2V' // c546-c547 #73 + 'X' // c548 #23 + '1C' // c549 #28 + '1A' // c54a #26 + '2V' // c54b #73 + 'Z' // c54c #25 + 'e2V' // c54d-c552 #73 + '1P' // c553 #41 + 'aY' // c554-c555 #24 + '2V' // c556 #73 + '1G' // c557 #32 + 'W' // c558 #22 + 'Y' // c559 #24 + 'a2V' // c55a-c55b #73 + '3M' // c55c #90 + '2V' // c55d #73 + 'V' // c55e #21 + '2V' // c55f #73 + '1A' // c560 #26 + 'V' // c561 #21 + 'a2V' // c562-c563 #73 + 'Y' // c564 #24 + 'b2V' // c565-c567 #73 + '1C' // c568 #28 + 'f2V' // c569-c56f #73 + '1P' // c570 #41 + 'V' // c571 #21 + '2V' // c572 #73 + '3M' // c573 #90 + '2V' // c574 #73 + '1F' // c575 #31 + 'e2V' // c576-c57b #73 + 'aZ' // c57c-c57d #25 + 'a2V' // c57e-c57f #73 + '1G' // c580 #32 + 'b2V' // c581-c583 #73 + '1F' // c584 #31 + 'a2V' // c585-c586 #73 + '1E' // c587 #30 + 'c2V' // c588-c58b #73 + '1J' // c58c #35 + '2B' // c58d #53 + '2V' // c58e #73 + '3M' // c58f #90 + '2V' // c590 #73 + 'Z' // c591 #25 + 'd2V' // c592-c596 #73 + '2I' // c597 #60 + '1C' // c598 #28 + 'z2V' // c599-c5b3 #73 + 'X' // c5b4 #23 + 'V' // c5b5 #21 + 'a2V' // c5b6-c5b7 #73 + '1A' // c5b8 #26 + '1W' // c5b9 #48 + '2V' // c5ba #73 + '1D' // c5bb #29 + 'W' // c5bc #22 + '2I' // c5bd #60 + 'e2V' // c5be-c5c3 #73 + 'V' // c5c4 #21 + 'aZ' // c5c5-c5c6 #25 + 'U' // c5c7 #20 + '1A' // c5c8 #26 + '1C' // c5c9 #28 + 'a2V' // c5ca-c5cb #73 + '1P' // c5cc #41 + '2V' // c5cd #73 + '1W' // c5ce #48 + '2V' // c5cf #73 + 'X' // c5d0 #23 + '1D' // c5d1 #29 + 'a2V' // c5d2-c5d3 #73 + 'W' // c5d4 #22 + 'b2V' // c5d5-c5d7 #73 + 'Y' // c5d8 #24 + 'f2V' // c5d9-c5df #73 + '1D' // c5e0 #29 + 'a2V' // c5e1-c5e2 #73 + '1G' // c5e3 #32 + '2V' // c5e4 #73 + '2I' // c5e5 #60 + 'e2V' // c5e6-c5eb #73 + 'X' // c5ec #23 + 'Z' // c5ed #25 + '1D' // c5ee #29 + '2V' // c5ef #73 + 'X' // c5f0 #23 + 'b2V' // c5f1-c5f3 #73 + '1A' // c5f4 #26 + 'f4F' // c5f5-c5fb #109 + 'Y' // c5fc #24 + '1D' // c5fd #29 + '4F' // c5fe #109 + '1E' // c5ff #30 + 'W' // c600 #22 + 'X' // c601 #23 + 'c4F' // c602-c605 #109 + '1D' // c606 #29 + '4F' // c607 #109 + 'Z' // c608 #25 + 'f4F' // c609-c60f #109 + '1J' // c610 #35 + 'i4F' // c611-c61a #109 + '1C' // c61b #28 + 'g4F' // c61c-c623 #109 + 'X' // c624 #23 + 'Y' // c625 #24 + 'a4F' // c626-c627 #109 + '1A' // c628 #26 + 'b4F' // c629-c62b #109 + '1A' // c62c #26 + '4F' // c62d #109 + '1G' // c62e #32 + 'c4F' // c62f-c632 #109 + '1P' // c633 #41 + '1G' // c634 #32 + 'U' // c635 #20 + '4F' // c636 #109 + 'U' // c637 #20 + '4F' // c638 #109 + '1C' // c639 #28 + 'e4F' // c63a-c63f #109 + 'Z' // c640 #25 + '1W' // c641 #48 + 'a4F' // c642-c643 #109 + 'W' // c644 #22 + 'b4F' // c645-c647 #109 + '1W' // c648 #48 + 'i4F' // c649-c652 #109 + '1J' // c653 #35 + 'aV' // c654-c655 #21 + 'e4F' // c656-c65b #109 + 'Y' // c65c #24 + 'b4F' // c65d-c65f #109 + '1F' // c660 #31 + 'b4F' // c661-c663 #109 + '2B' // c664 #53 + 'r4F' // c665-c677 #109 + '1A' // c678 #26 + 'b4F' // c679-c67b #109 + '1C' // c67c #28 + 'v4F' // c67d-c693 #109 + 'X' // c694 #23 + 'V' // c695 #21 + 'a4F' // c696-c697 #109 + '2B' // c698 #53 + 'b4F' // c699-c69b #109 + '2I' // c69c #60 + 'f4F' // c69d-c6a3 #109 + '1P' // c6a4 #41 + '3V' // c6a5 #99 + '4F' // c6a6 #109 + '2B' // c6a7 #53 + '4F' // c6a8 #109 + 'X' // c6a9 #23 + 'e4F' // c6aa-c6af #109 + 'X' // c6b0 #23 + 'Y' // c6b1 #24 + 'a4F' // c6b2-c6b3 #109 + 'X' // c6b4 #23 + 'b4F' // c6b5-c6b7 #109 + 'Z' // c6b8 #25 + 'a4F' // c6b9-c6ba #109 + 'd4Q' // c6bb-c6bf #120 + 'W' // c6c0 #22 + '2B' // c6c1 #53 + '4Q' // c6c2 #120 + 'W' // c6c3 #22 + '4Q' // c6c4 #120 + '1D' // c6c5 #29 + 'e4Q' // c6c6-c6cb #120 + '1A' // c6cc #26 + '1J' // c6cd #35 + 'a4Q' // c6ce-c6cf #120 + 'X' // c6d0 #23 + 'b4Q' // c6d1-c6d3 #120 + 'Z' // c6d4 #25 + 'f4Q' // c6d5-c6db #120 + '3V' // c6dc #99 + 'b4Q' // c6dd-c6df #120 + '1D' // c6e0 #29 + 'f4Q' // c6e1-c6e7 #120 + 'V' // c6e8 #21 + 'b4Q' // c6e9-c6eb #120 + '1E' // c6ec #30 + 'b4Q' // c6ed-c6ef #120 + '1G' // c6f0 #32 + 'g4Q' // c6f1-c6f8 #120 + 'V' // c6f9 #21 + 'i4Q' // c6fa-c703 #120 + 'X' // c704 #23 + 'b4Q' // c705-c707 #120 + '1D' // c708 #29 + 'b4Q' // c709-c70b #120 + '1E' // c70c #30 + 'i4Q' // c70d-c716 #120 + '1C' // c717 #28 + '4Q' // c718 #120 + '1F' // c719 #31 + 'e4Q' // c71a-c71f #120 + 'X' // c720 #23 + 'W' // c721 #22 + 'a4Q' // c722-c723 #120 + 'V' // c724 #21 + 'b4Q' // c725-c727 #120 + 'Y' // c728 #24 + 'k4Q' // c729-c734 #120 + 'U' // c735 #20 + 'e4Q' // c736-c73b #120 + 'X' // c73c #23 + '1W' // c73d #48 + 'a4Q' // c73e-c73f #120 + 'X' // c740 #23 + 'b4Q' // c741-c743 #120 + 'X' // c744 #23 + 'f4Q' // c745-c74b #120 + 'X' // c74c #23 + '1G' // c74d #32 + 'b4Q' // c74e-c750 #120 + 'V' // c751 #21 + 'e4Q' // c752-c757 #120 + 'X' // c758 #23 + 'z4Q' // c759-c773 #120 + '14W' // c774 #386 + 'V' // c775 #21 + 'a4Q' // c776-c777 #120 + '14W' // c778 #386 + 'b4L' // c779-c77b #115 + 'X' // c77c #23 + 'U' // c77d #20 + 'd4L' // c77e-c782 #115 + '1G' // c783 #32 + 'Z' // c784 #25 + 'X' // c785 #23 + '4L' // c786 #115 + 'U' // c787 #20 + 'X' // c788 #23 + 'U' // c789 #20 + '1C' // c78a #28 + '4L' // c78b #115 + '3M' // c78c #90 + '4L' // c78d #115 + '1G' // c78e #32 + '4L' // c78f #115 + 'aX' // c790-c791 #23 + 'a4L' // c792-c793 #115 + 'Y' // c794 #24 + '4L' // c795 #115 + '1G' // c796 #32 + '4L' // c797 #115 + 'W' // c798 #22 + 'f4L' // c799-c79f #115 + 'V' // c7a0 #21 + 'W' // c7a1 #22 + '4L' // c7a2 #115 + '1P' // c7a3 #41 + '3V' // c7a4 #99 + 'X' // c7a5 #23 + '1P' // c7a6 #41 + 'd4L' // c7a7-c7ab #115 + 'Z' // c7ac #25 + '1E' // c7ad #30 + 'm4L' // c7ae-c7bb #115 + '1G' // c7bc #32 + 'c4L' // c7bd-c7c0 #115 + 'V' // c7c1 #21 + 'e4L' // c7c2-c7c7 #115 + '1E' // c7c8 #30 + 's4L' // c7c9-c7dc #115 + '3M' // c7dd #90 + '1g4L' // c7de-c7ff #115 + 'aZ' // c800-c801 #25 + 'a4L' // c802-c803 #115 + 'X' // c804 #23 + 'b4L' // c805-c807 #115 + '1A' // c808 #26 + '4L' // c809 #115 + '1C' // c80a #28 + 'd4L' // c80b-c80f #115 + '1A' // c810 #26 + 'W' // c811 #22 + '4L' // c812 #115 + '1J' // c813 #35 + '4L' // c814 #115 + 'X' // c815 #23 + '1F' // c816 #31 + 'd4L' // c817-c81b #115 + 'X' // c81c #23 + '1D' // c81d #29 + 'a4L' // c81e-c81f #115 + '1D' // c820 #29 + 'b4L' // c821-c823 #115 + '1D' // c824 #29 + 'i4L' // c825-c82e #115 + '1J' // c82f #35 + 'g4L' // c830-c837 #115 + 'W' // c838 #22 + 'b4L' // c839-c83b #115 + '1E' // c83c #30 + 'c4L' // c83d-c840 #115 + 'j19K' // c841-c84b #504 + 'Y' // c84c #24 + '1h19K' // c84d-c86f #504 + 'X' // c870 #23 + 'W' // c871 #22 + 'a19K' // c872-c873 #504 + 'V' // c874 #21 + 'b19K' // c875-c877 #504 + '1D' // c878 #29 + 'f19K' // c879-c87f #504 + 'V' // c880 #21 + '1E' // c881 #30 + 'b19K' // c882-c884 #504 + '1A' // c885 #26 + '1P' // c886 #41 + 'c19K' // c887-c88a #504 + '1A' // c88b #26 + 'Y' // c88c #24 + '2b19K' // c88d-c8c3 #504 + 'U' // c8c4 #20 + 'z19K' // c8c5-c8df #504 + 'Y' // c8e0 #24 + 'g19K' // c8e1-c8e8 #504 + 'k11E' // c8e9-c8f4 #290 + '3M' // c8f5 #90 + 'e11E' // c8f6-c8fb #290 + 'X' // c8fc #23 + 'Y' // c8fd #24 + 'a11E' // c8fe-c8ff #290 + '1A' // c900 #26 + 'b11E' // c901-c903 #290 + 'W' // c904 #22 + 'f11E' // c905-c90b #290 + 'a1C' // c90c-c90d #28 + 'b11E' // c90e-c910 #290 + 'X' // c911 #23 + 'e11E' // c912-c917 #290 + '1D' // c918 #29 + 'r11E' // c919-c92b #290 + '1G' // c92c #32 + '1h11E' // c92d-c94f #290 + '1C' // c950 #28 + 'b11E' // c951-c953 #290 + '3V' // c954 #99 + 'v11E' // c955-c96b #290 + '1D' // c96c #29 + 'f11E' // c96d-c973 #290 + '3V' // c974 #99 + 'r11E' // c975-c987 #290 + '1A' // c988 #26 + 'U' // c989 #20 + 'a11E' // c98a-c98b #290 + 'U' // c98c #20 + 'b11E' // c98d-c98f #290 + 'W' // c990 #22 + 'd11E' // c991-c995 #290 + 'a8Z' // c996-c997 #233 + 'Y' // c998 #24 + '1J' // c999 #35 + 'b8Z' // c99a-c99c #233 + '1A' // c99d #26 + '1g8Z' // c99e-c9bf #233 + '14W' // c9c0 #386 + 'Z' // c9c1 #25 + 'a8Z' // c9c2-c9c3 #233 + 'X' // c9c4 #23 + 'b8Z' // c9c5-c9c7 #233 + '1A' // c9c8 #26 + 'f8Z' // c9c9-c9cf #233 + 'U' // c9d0 #20 + 'Z' // c9d1 #25 + '8Z' // c9d2 #233 + 'U' // c9d3 #20 + '8Z' // c9d4 #233 + 'V' // c9d5 #21 + '2I' // c9d6 #60 + 'a8Z' // c9d7-c9d8 #233 + 'a1P' // c9d9-c9da #41 + '8Z' // c9db #233 + 'W' // c9dc #22 + 'Y' // c9dd #24 + 'a8Z' // c9de-c9df #233 + '1J' // c9e0 #35 + 'b8Z' // c9e1-c9e3 #233 + '1D' // c9e4 #29 + 'a8Z' // c9e5-c9e6 #233 + '1C' // c9e7 #28 + 'c8Z' // c9e8-c9eb #233 + '1P' // c9ec #41 + '2B' // c9ed #53 + 'b8Z' // c9ee-c9f0 #233 + '1D' // c9f1 #29 + 'e8Z' // c9f2-c9f7 #233 + 'V' // c9f8 #21 + 'r8Z' // c9f9-ca0b #233 + '1P' // ca0c #41 + '1a8Z' // ca0d-ca28 #233 + '2I' // ca29 #60 + '1e8Z' // ca2a-ca49 #233 + 'a13M' // ca4a-ca4b #350 + '1G' // ca4c #32 + '1F' // ca4d #31 + 'a13M' // ca4e-ca4f #350 + '1W' // ca50 #48 + 'b13M' // ca51-ca53 #350 + '1F' // ca54 #31 + 'f13M' // ca55-ca5b #350 + '2I' // ca5c #60 + 'c13M' // ca5d-ca60 #350 + '1W' // ca61 #48 + '1g13M' // ca62-ca83 #350 + '2I' // ca84 #60 + '2b13M' // ca85-cabb #350 + '1F' // cabc #31 + 'V' // cabd #21 + 'a13M' // cabe-cabf #350 + '2B' // cac0 #53 + 'b13M' // cac1-cac3 #350 + '1J' // cac4 #35 + 'f13M' // cac5-cacb #350 + '2B' // cacc #53 + 'c13M' // cacd-cad0 #350 + '3M' // cad1 #90 + '13M' // cad2 #350 + '1J' // cad3 #35 + 'd13M' // cad4-cad8 #350 + '2B' // cad9 #53 + 'y13M' // cada-caf3 #350 + '3e71B' // caf4-cb47 #1847 + '1E' // cb48 #30 + '1F' // cb49 #31 + '2r71B' // cb4a-cb90 #1847 + '2n13L' // cb91-cbd3 #349 + '1J' // cbd4 #35 + 'n13L' // cbd5-cbe3 #349 + '1C' // cbe4 #28 + '1l13L' // cbe5-cc0b #349 + 'U' // cc0c #20 + 'Y' // cc0d #24 + 'a13L' // cc0e-cc0f #349 + '1J' // cc10 #35 + 'b13L' // cc11-cc13 #349 + '1F' // cc14 #31 + 'f13L' // cc15-cc1b #349 + '1G' // cc1c #32 + 'c13L' // cc1d-cc20 #349 + '1P' // cc21 #41 + '1J' // cc22 #35 + 'd13L' // cc23-cc27 #349 + 'Z' // cc28 #25 + 'V' // cc29 #21 + 'a13L' // cc2a-cc2b #349 + 'V' // cc2c #21 + '13L' // cc2d #349 + '1D' // cc2e #29 + '13L' // cc2f #349 + 'Y' // cc30 #24 + 'f13L' // cc31-cc37 #349 + 'W' // cc38 #22 + '2I' // cc39 #60 + '13L' // cc3a #349 + '2I' // cc3b #60 + '13L' // cc3c #349 + 'a1A' // cc3d-cc3e #26 + 'd9L' // cc3f-cc43 #245 + 'W' // cc44 #22 + '1A' // cc45 #26 + 'a9L' // cc46-cc47 #245 + '3M' // cc48 #90 + 'b9L' // cc49-cc4b #245 + '1W' // cc4c #48 + 'f9L' // cc4d-cc53 #245 + '1G' // cc54 #32 + 'c9L' // cc55-cc58 #245 + '1G' // cc59 #32 + 'e9L' // cc5a-cc5f #245 + '1E' // cc60 #30 + '2b9L' // cc61-cc97 #245 + 'Z' // cc98 #25 + 'U' // cc99 #20 + 'a9L' // cc9a-cc9b #245 + 'Z' // cc9c #25 + 'b9L' // cc9d-cc9f #245 + 'W' // cca0 #22 + 'f9L' // cca1-cca7 #245 + 'V' // cca8 #21 + '1C' // cca9 #28 + '9L' // ccaa #245 + 'V' // ccab #21 + '9L' // ccac #245 + 'Z' // ccad #25 + 'e9L' // ccae-ccb3 #245 + 'X' // ccb4 #23 + '2B' // ccb5 #53 + 'a9L' // ccb6-ccb7 #245 + '1J' // ccb8 #35 + 'b9L' // ccb9-ccbb #245 + '1J' // ccbc #35 + 'r9L' // ccbd-cccf #245 + 'V' // ccd0 #21 + 'r9L' // ccd1-cce3 #245 + '1C' // cce4 #28 + 'i9L' // cce5-ccee #245 + 'x24C' // ccef-cd07 #626 + '1A' // cd08 #26 + '1D' // cd09 #29 + 'a24C' // cd0a-cd0b #626 + 'U' // cd0c #20 + 'm24C' // cd0d-cd1a #626 + '1P' // cd1b #41 + '24C' // cd1c #626 + 'W' // cd1d #22 + 'm24C' // cd1e-cd2b #626 + 'U' // cd2c #20 + '1t24C' // cd2d-cd5b #626 + 'Z' // cd5c #25 + 'z24C' // cd5d-cd77 #626 + '1P' // cd78 #41 + 'x24C' // cd79-cd91 #626 + 'a14U' // cd92-cd93 #384 + 'Z' // cd94 #25 + 'W' // cd95 #22 + 'a14U' // cd96-cd97 #384 + 'U' // cd98 #20 + 'b14U' // cd99-cd9b #384 + 'Z' // cd9c #25 + 'f14U' // cd9d-cda3 #384 + 'U' // cda4 #20 + '1J' // cda5 #35 + 'b14U' // cda6-cda8 #384 + 'W' // cda9 #22 + 'e14U' // cdaa-cdaf #384 + '1C' // cdb0 #28 + 'r14U' // cdb1-cdc3 #384 + '1P' // cdc4 #41 + 'f14U' // cdc5-cdcb #384 + '2B' // cdcc #53 + 'z14U' // cdcd-cde7 #384 + 'Z' // cde8 #25 + 'z14U' // cde9-ce03 #384 + '1G' // ce04 #32 + 'z14U' // ce05-ce1f #384 + '1A' // ce20 #26 + 'V' // ce21 #21 + 'r14U' // ce22-ce34 #384 + 'V' // ce35 #21 + 'e14U' // ce36-ce3b #384 + '1a7S' // ce3c-ce57 #200 + 'X' // ce58 #23 + 'U' // ce59 #20 + 'a7S' // ce5a-ce5b #200 + 'W' // ce5c #22 + 'b7S' // ce5d-ce5f #200 + 'U' // ce60 #20 + 'f7S' // ce61-ce67 #200 + 'Z' // ce68 #25 + '1E' // ce69 #30 + '7S' // ce6a #200 + '1E' // ce6b #30 + '7S' // ce6c #200 + 'U' // ce6d #20 + 'e7S' // ce6e-ce73 #200 + 'X' // ce74 #23 + '1E' // ce75 #30 + 'a7S' // ce76-ce77 #200 + '1C' // ce78 #28 + 'b7S' // ce79-ce7b #200 + 'V' // ce7c #21 + 'f7S' // ce7d-ce83 #200 + '1J' // ce84 #35 + 'c7S' // ce85-ce88 #200 + '2I' // ce89 #60 + 'e7S' // ce8a-ce8f #200 + 'V' // ce90 #21 + 'b7S' // ce91-ce93 #200 + '1C' // ce94 #28 + 'b7S' // ce95-ce97 #200 + '1C' // ce98 #28 + 'f7S' // ce99-ce9f #200 + 'U' // cea0 #20 + '1D' // cea1 #29 + '7S' // cea2 #200 + '1J' // cea3 #35 + 'g7S' // cea4-ceab #200 + '1W' // ceac #48 + '2b7S' // cead-cee3 #200 + '1A' // cee4 #26 + '2I' // cee5 #60 + 'a7S' // cee6-cee7 #200 + 'V' // cee8 #21 + 'b7S' // cee9-ceeb #200 + 'Y' // ceec #24 + 'b7S' // ceed-ceef #200 + 'c8J' // cef0-cef3 #217 + 'V' // cef4 #21 + '1D' // cef5 #29 + '8J' // cef6 #217 + '1D' // cef7 #29 + '1P' // cef8 #41 + 'f8J' // cef9-ceff #217 + 'W' // cf00 #22 + 'b8J' // cf01-cf03 #217 + '1E' // cf04 #30 + 'b8J' // cf05-cf07 #217 + '1E' // cf08 #30 + 'f8J' // cf09-cf0f #217 + '3M' // cf10 #90 + 'a8J' // cf11-cf12 #217 + 'Y' // cf13 #24 + 'g8J' // cf14-cf1b #217 + 'Y' // cf1c #24 + 'b8J' // cf1d-cf1f #217 + '3M' // cf20 #90 + 'n8J' // cf21-cf2f #217 + '1G' // cf30 #32 + '1h8J' // cf31-cf53 #217 + 'Z' // cf54 #25 + '1E' // cf55 #30 + 'a8J' // cf56-cf57 #217 + 'W' // cf58 #22 + 'b8J' // cf59-cf5b #217 + 'Y' // cf5c #24 + 'f8J' // cf5d-cf63 #217 + '1D' // cf64 #29 + '2I' // cf65 #60 + '8J' // cf66 #217 + '1J' // cf67 #35 + '8J' // cf68 #217 + '1D' // cf69 #29 + 'e8J' // cf6a-cf6f #217 + '1E' // cf70 #30 + 's8J' // cf71-cf84 #217 + '2I' // cf85 #60 + 'e8J' // cf86-cf8b #217 + '1C' // cf8c #28 + 't8J' // cf8d-cfa1 #217 + '1g14T' // cfa2-cfc3 #383 + '1G' // cfc4 #32 + 'z14T' // cfc5-cfdf #383 + 'V' // cfe0 #21 + '1J' // cfe1 #35 + 'a14T' // cfe2-cfe3 #383 + '1P' // cfe4 #41 + 'b14T' // cfe5-cfe7 #383 + '1D' // cfe8 #29 + 'k14T' // cfe9-cff4 #383 + '1F' // cff5 #31 + 'e14T' // cff6-cffb #383 + '1E' // cffc #30 + 'b14T' // cffd-cfff #383 + '3V' // d000 #99 + 'b14T' // d001-d003 #383 + '1F' // d004 #31 + 'r14T' // d005-d017 #383 + '1C' // d018 #28 + 'z14T' // d019-d033 #383 + '1C' // d034 #28 + '1D' // d035 #29 + 'a14T' // d036-d037 #383 + '1F' // d038 #31 + 'b14T' // d039-d03b #383 + '3M' // d03c #90 + 'm14T' // d03d-d04a #383 + 'd6P' // d04b-d04f #171 + 'Y' // d050 #24 + 'f6P' // d051-d057 #171 + '2I' // d058 #60 + 'r6P' // d059-d06b #171 + 'X' // d06c #23 + 'b6P' // d06d-d06f #171 + 'V' // d070 #21 + 'b6P' // d071-d073 #171 + '1A' // d074 #26 + 'f6P' // d075-d07b #171 + 'Y' // d07c #24 + '3M' // d07d #90 + '1k6P' // d07e-d0a3 #171 + '1A' // d0a4 #26 + '1P' // d0a5 #41 + 'a6P' // d0a6-d0a7 #171 + 'Y' // d0a8 #24 + 'b6P' // d0a9-d0ab #171 + '1D' // d0ac #29 + 'f6P' // d0ad-d0b3 #171 + '1F' // d0b4 #31 + '1P' // d0b5 #41 + '6P' // d0b6 #171 + '1E' // d0b7 #30 + '6P' // d0b8 #171 + 'V' // d0b9 #21 + 'e6P' // d0ba-d0bf #171 + 'X' // d0c0 #23 + 'V' // d0c1 #21 + 'a6P' // d0c2-d0c3 #171 + 'V' // d0c4 #21 + 'b6P' // d0c5-d0c7 #171 + 'V' // d0c8 #21 + 'f6P' // d0c9-d0cf #171 + 'aU' // d0d0-d0d1 #20 + '6P' // d0d2 #171 + '1F' // d0d3 #31 + '1W' // d0d4 #48 + 'U' // d0d5 #20 + 'e6P' // d0d6-d0db #171 + 'Z' // d0dc #25 + '1A' // d0dd #26 + 'a6P' // d0de-d0df #171 + '1F' // d0e0 #31 + 'b6P' // d0e1-d0e3 #171 + '2B' // d0e4 #53 + 'f6P' // d0e5-d0eb #171 + '1P' // d0ec #41 + '1F' // d0ed #31 + 'b6P' // d0ee-d0f0 #171 + '1C' // d0f1 #28 + 'r6P' // d0f2-d104 #171 + '1p11D' // d105-d12f #289 + 'X' // d130 #23 + '1F' // d131 #31 + 'a11D' // d132-d133 #289 + 'Y' // d134 #24 + 'b11D' // d135-d137 #289 + 'Y' // d138 #24 + 'f11D' // d139-d13f #289 + '1G' // d140 #32 + 'a11D' // d141-d142 #289 + '2B' // d143 #53 + '11D' // d144 #289 + '2I' // d145 #60 + 'e11D' // d146-d14b #289 + 'Z' // d14c #25 + 'U' // d14d #20 + 'a11D' // d14e-d14f #289 + 'W' // d150 #22 + 'b11D' // d151-d153 #289 + 'Y' // d154 #24 + 'f11D' // d155-d15b #289 + 'V' // d15c #21 + '1P' // d15d #41 + '1o11D' // d15e-d187 #289 + '1P' // d188 #41 + 'v11D' // d189-d19f #289 + 'Z' // d1a0 #25 + 'V' // d1a1 #21 + 'a11D' // d1a2-d1a3 #289 + 'U' // d1a4 #20 + 'b11D' // d1a5-d1a7 #289 + '1E' // d1a8 #30 + 'f11D' // d1a9-d1af #289 + '1F' // d1b0 #31 + '1C' // d1b1 #28 + 'a11D' // d1b2-d1b3 #289 + '24B' // d1b4 #625 + 'Z' // d1b5 #25 + '2i24B' // d1b6-d1f3 #625 + 'Y' // d1f4 #24 + '2b24B' // d1f5-d22b #625 + 'W' // d22c #22 + '2I' // d22d #60 + 'a24B' // d22e-d22f #625 + '1D' // d230 #29 + 'b24B' // d231-d233 #625 + '1D' // d234 #29 + 'f24B' // d235-d23b #625 + '1P' // d23c #41 + 'c24B' // d23d-d240 #625 + '1J' // d241 #35 + 't24B' // d242-d256 #625 + '1n16E' // d257-d27f #420 + '1C' // d280 #28 + 'z16E' // d281-d29b #420 + 'U' // d29c #20 + 'b16E' // d29d-d29f #420 + '3M' // d2a0 #90 + 'j16E' // d2a1-d2ab #420 + '1P' // d2ac #41 + 'j16E' // d2ad-d2b7 #420 + 'X' // d2b8 #23 + '1A' // d2b9 #26 + 'a16E' // d2ba-d2bb #420 + 'Y' // d2bc #24 + 'b16E' // d2bd-d2bf #420 + 'V' // d2c0 #21 + 'f16E' // d2c1-d2c7 #420 + '1E' // d2c8 #30 + '1l16E' // d2c9-d2ef #420 + 'Z' // d2f0 #25 + 'U' // d2f1 #20 + 'a16E' // d2f2-d2f3 #420 + '1D' // d2f4 #29 + 'b16E' // d2f5-d2f7 #420 + '1C' // d2f8 #28 + 'e16E' // d2f9-d2fe #420 + '4U' // d2ff #124 + 'V' // d300 #21 + 'U' // d301 #20 + 'b4U' // d302-d304 #124 + 'W' // d305 #22 + 'e4U' // d306-d30b #124 + 'Z' // d30c #25 + '1J' // d30d #35 + '3M' // d30e #90 + '4U' // d30f #124 + 'Z' // d310 #25 + 'b4U' // d311-d313 #124 + 'V' // d314 #21 + 'f4U' // d315-d31b #124 + '1J' // d31c #35 + '1D' // d31d #29 + '4U' // d31e #124 + '1F' // d31f #31 + '2B' // d320 #53 + '1G' // d321 #32 + 'b4U' // d322-d324 #124 + '2I' // d325 #60 + 'a4U' // d326-d327 #124 + 'W' // d328 #22 + 'Y' // d329 #24 + 'a4U' // d32a-d32b #124 + 'Y' // d32c #24 + 'b4U' // d32d-d32f #124 + '2B' // d330 #53 + 'f4U' // d331-d337 #124 + '1C' // d338 #28 + 'c4U' // d339-d33c #124 + '1F' // d33d #31 + '2i4U' // d33e-d37b #124 + 'W' // d37c #22 + '2I' // d37d #60 + 'a4U' // d37e-d37f #124 + '1C' // d380 #28 + 'b4U' // d381-d383 #124 + '1E' // d384 #30 + 'f4U' // d385-d38b #124 + '1G' // d38c #32 + '3M' // d38d #90 + 'b4U' // d38e-d390 #124 + '1J' // d391 #35 + 'e4U' // d392-d397 #124 + 'Z' // d398 #25 + '1G' // d399 #32 + 'a4U' // d39a-d39b #124 + 'U' // d39c #20 + 'b4U' // d39d-d39f #124 + '1F' // d3a0 #31 + 'f4U' // d3a1-d3a7 #124 + '3M' // d3a8 #90 + 'a4U' // d3a9-d3aa #124 + '1F' // d3ab #31 + '4U' // d3ac #124 + '2I' // d3ad #60 + 'e4U' // d3ae-d3b3 #124 + '1C' // d3b4 #28 + 'b4U' // d3b5-d3b7 #124 + '1A' // d3b8 #26 + 'b4U' // d3b9-d3bb #124 + 'U' // d3bc #20 + 'a4U' // d3bd-d3be #124 + 'h21P' // d3bf-d3c7 #561 + '3M' // d3c8 #90 + '1A' // d3c9 #26 + 'e21P' // d3ca-d3cf #561 + 'Y' // d3d0 #24 + 'z21P' // d3d1-d3eb #561 + 'X' // d3ec #23 + 'V' // d3ed #21 + 'a21P' // d3ee-d3ef #561 + 'W' // d3f0 #22 + 'b21P' // d3f1-d3f3 #561 + 'Y' // d3f4 #24 + 'f21P' // d3f5-d3fb #561 + 'Y' // d3fc #24 + 'c21P' // d3fd-d400 #561 + '1J' // d401 #35 + '3k21P' // d402-d45b #561 + 'Z' // d45c #25 + 'f21P' // d45d-d463 #561 + 's21O' // d464-d477 #560 + 'V' // d478 #21 + '1J' // d479 #35 + 'a21O' // d47a-d47b #560 + '1E' // d47c #30 + 'b21O' // d47d-d47f #560 + 'V' // d480 #21 + 'f21O' // d481-d487 #560 + '1A' // d488 #26 + 'a21O' // d489-d48a #560 + '1E' // d48b #30 + '21O' // d48c #560 + 'V' // d48d #21 + '3k21O' // d48e-d4e7 #560 + 'U' // d4e8 #20 + 'z21O' // d4e9-d503 #560 + 'X' // d504 #23 + 'a21O' // d505-d506 #560 + '7E' // d507 #186 + 'V' // d508 #21 + 'b7E' // d509-d50b #186 + 'Z' // d50c #25 + 'f7E' // d50d-d513 #186 + '1F' // d514 #31 + '1l7E' // d515-d53b #186 + 'Z' // d53c #25 + 'V' // d53d #21 + 'a7E' // d53e-d53f #186 + 'U' // d540 #20 + 'b7E' // d541-d543 #186 + '1A' // d544 #26 + 'g7E' // d545-d54c #186 + '2B' // d54d #53 + '7E' // d54e #186 + '1G' // d54f #32 + '7E' // d550 #186 + 'W' // d551 #22 + 'e7E' // d552-d557 #186 + '14W' // d558 #386 + '1A' // d559 #26 + 'a7E' // d55a-d55b #186 + 'X' // d55c #23 + 'b7E' // d55d-d55f #186 + 'Z' // d560 #25 + 'c7E' // d561-d564 #186 + '3V' // d565 #99 + 'a7E' // d566-d567 #186 + 'Z' // d568 #25 + 'X' // d569 #23 + '7E' // d56a #186 + 'Y' // d56b #24 + '7E' // d56c #186 + '1A' // d56d #26 + 'e7E' // d56e-d573 #186 + 'X' // d574 #23 + '1D' // d575 #29 + 'a7E' // d576-d577 #186 + '1D' // d578 #29 + 'j7E' // d579-d583 #186 + '1F' // d584 #31 + 'a7E' // d585-d586 #186 + '1G' // d587 #32 + '1A' // d588 #26 + 'Z' // d589 #25 + 'z7E' // d58a-d5a4 #186 + 'W' // d5a5 #22 + 'u7E' // d5a6-d5bb #186 + 'k4T' // d5bc-d5c7 #123 + 'W' // d5c8 #22 + '1J' // d5c9 #35 + 'a4T' // d5ca-d5cb #123 + 'Y' // d5cc #24 + 'b4T' // d5cd-d5cf #123 + '1G' // d5d0 #32 + 'f4T' // d5d1-d5d7 #123 + 'W' // d5d8 #22 + 'a4T' // d5d9-d5da #123 + '1J' // d5db #35 + '4T' // d5dc #123 + '1E' // d5dd #30 + 'e4T' // d5de-d5e3 #123 + 'V' // d5e4 #21 + '3M' // d5e5 #90 + 'a4T' // d5e6-d5e7 #123 + '1E' // d5e8 #30 + 'b4T' // d5e9-d5eb #123 + 'U' // d5ec #20 + 'i4T' // d5ed-d5f6 #123 + '1W' // d5f7 #48 + '4T' // d5f8 #123 + '3M' // d5f9 #90 + 'e4T' // d5fa-d5ff #123 + 'aY' // d600-d601 #24 + 'a4T' // d602-d603 #123 + 'Z' // d604 #25 + 'b4T' // d605-d607 #123 + '1D' // d608 #29 + 'f4T' // d609-d60f #123 + '1C' // d610 #28 + 'V' // d611 #21 + 'a4T' // d612-d613 #123 + '1D' // d614 #29 + '1A' // d615 #26 + 'e4T' // d616-d61b #123 + 'V' // d61c #21 + 'z4T' // d61d-d637 #123 + 'X' // d638 #23 + 'Y' // d639 #24 + 'a4T' // d63a-d63b #123 + 'W' // d63c #22 + 'b4T' // d63d-d63f #123 + 'U' // d640 #20 + 'f4T' // d641-d647 #123 + '1A' // d648 #26 + '1J' // d649 #35 + 'b4T' // d64a-d64c #123 + 'W' // d64d #22 + 'e4T' // d64e-d653 #123 + 'X' // d654 #23 + '1A' // d655 #26 + 'a4T' // d656-d657 #123 + '1A' // d658 #26 + 'b4T' // d659-d65b #123 + '1A' // d65c #26 + 'i4T' // d65d-d666 #123 + '3V' // d667 #99 + '4T' // d668 #123 + 'W' // d669 #22 + 'n4T' // d66a-d678 #123 + 'r12O' // d679-d68b #326 + 'X' // d68c #23 + 'V' // d68d #21 + 'p12O' // d68e-d69e #326 + '1C' // d69f #28 + '12O' // d6a0 #326 + '1F' // d6a1 #31 + 'e12O' // d6a2-d6a7 #326 + 'V' // d6a8 #21 + 'z12O' // d6a9-d6c3 #326 + 'Z' // d6c4 #25 + '3V' // d6c5 #99 + 'a12O' // d6c6-d6c7 #326 + 'Y' // d6c8 #24 + 'b12O' // d6c9-d6cb #326 + '1F' // d6cc #31 + 'f12O' // d6cd-d6d3 #326 + '1J' // d6d4 #35 + 'a12O' // d6d5-d6d6 #326 + '1W' // d6d7 #48 + 'k12O' // d6d8-d6e3 #326 + '3M' // d6e4 #90 + 'b12O' // d6e5-d6e7 #326 + '1G' // d6e8 #32 + 'r12O' // d6e9-d6fb #326 + '1C' // d6fc #28 + 'z12O' // d6fd-d717 #326 + '1D' // d718 #29 + 'f12O' // d719-d71f #326 + '1E' // d720 #30 + 'a12O' // d721-d722 #326 + 'e9K' // d723-d728 #244 + '1P' // d729 #41 + 'i9K' // d72a-d733 #244 + 'W' // d734 #22 + 's9K' // d735-d748 #244 + '1F' // d749 #31 + 'e9K' // d74a-d74f #244 + 'U' // d750 #20 + '1D' // d751 #29 + 'a9K' // d752-d753 #244 + 'Y' // d754 #24 + 'b9K' // d755-d757 #244 + '1C' // d758 #28 + '1E' // d759 #30 + 'e9K' // d75a-d75f #244 + '1F' // d760 #31 + '1D' // d761 #29 + 'b9K' // d762-d764 #244 + 'Y' // d765 #24 + 'b9K' // d766-d768 #244 + '2I' // d769 #60 + 'a9K' // d76a-d76b #244 + 'W' // d76c #22 + 'b9K' // d76d-d76f #244 + '1G' // d770 #32 + 'v9K' // d771-d787 #244 + 'Z' // d788 #25 + 'b9K' // d789-d78b #244 + '1D' // d78c #29 + 'b9K' // d78d-d78f #244 + '1D' // d790 #29 + 'f9K' // d791-d797 #244 + 'V' // d798 #21 + '1F' // d799 #31 + '9K' // d79a #244 + '1P' // d79b #41 + '9K' // d79c #244 + '3V' // d79d #99 + 'e9K' // d79e-d7a3 #244 + '328kA' // d7a4-f8ff + '35X' // f900 #933 + '82V' // f901 #2153 + '53N' // f902 #1391 + '35X' // f903 #933 + '53M' // f904 #1390 + '35X' // f905 #933 + '53N' // f906 #1391 + '53M' // f907 #1390 + 'a35X' // f908-f909 #933 + '247X' // f90a #6445 + '1f35X' // f90b-f92b #933 + '247W' // f92c #6444 + '82U' // f92d #2152 + '247U' // f92e #6442 + 'd7Q' // f92f-f933 #198 + '82S' // f934 #2150 + 'a7Q' // f935-f936 #198 + '82T' // f937 #2151 + '1g7Q' // f938-f959 #198 + '260N' // f95a #6773 + 'b7Q' // f95b-f95d #198 + '21U' // f95e #566 + '49L' // f95f #1285 + 'a7Q' // f960-f961 #198 + '21U' // f962 #566 + 'a7Q' // f963-f964 #198 + '27T' // f965 #721 + '7Q' // f966 #198 + '27T' // f967 #721 + 'd7Q' // f968-f96c #198 + '82L' // f96d #2143 + 'e7Q' // f96e-f973 #198 + '247T' // f974 #6441 + '7Q' // f975 #198 + '21U' // f976 #566 + '7Q' // f977 #198 + '21U' // f978 #566 + '27T' // f979 #721 + 'c7Q' // f97a-f97d #198 + '27T' // f97e #721 + 'a7Q' // f97f-f980 #198 + '247V' // f981 #6443 + 'g7Q' // f982-f989 #198 + '27T' // f98a #721 + 'b7Q' // f98b-f98d #198 + '27T' // f98e #721 + 'l7Q' // f98f-f99b #198 + '21U' // f99c #566 + 'a7Q' // f99d-f99e #198 + '21U' // f99f #566 + 'i7Q' // f9a0-f9a9 #198 + '49L' // f9aa #1285 + 'i7Q' // f9ab-f9b4 #198 + '21U' // f9b5 #566 + 'f7Q' // f9b6-f9bc #198 + '21U' // f9bd #566 + '82M' // f9be #2144 + 'j7Q' // f9bf-f9c9 #198 + 'e13H' // f9ca-f9cf #345 + '82J' // f9d0 #2141 + 'f13H' // f9d1-f9d7 #345 + '21T' // f9d8 #565 + 'b13H' // f9d9-f9db #345 + '21T' // f9dc #565 + '37C' // f9dd #964 + 'a13H' // f9de-f9df #345 + '37C' // f9e0 #964 + 'b13H' // f9e1-f9e3 #345 + '37C' // f9e4 #964 + 'c13H' // f9e5-f9e8 #345 + '21T' // f9e9 #565 + 'h13H' // f9ea-f9f2 #345 + '260M' // f9f3 #6772 + '21T' // f9f4 #565 + 'd13H' // f9f5-f9f9 #345 + '21T' // f9fa #565 + 'a13H' // f9fb-f9fc #345 + '21T' // f9fd #565 + '13H' // f9fe #345 + '21T' // f9ff #565 + 'e13H' // fa00-fa05 #345 + '21T' // fa06 #565 + '13H' // fa07 #345 + '37C' // fa08 #964 + '13H' // fa09 #345 + '82K' // fa0a #2142 + '13H' // fa0b #345 + '82R' // fa0c #2149 + 'A' // fa0d + 'a49L' // fa0e-fa0f #1285 + '13G' // fa10 #344 + '252O' // fa11 #6566 + '3m13G' // fa12-fa6d #344 + '5oA' // fa6e-faff + '69N' // fb00 #1807 + '82I' // fb01 #2140 + '247S' // fb02 #6440 + 'a69N' // fb03-fb04 #1807 + 'aE' // fb05-fb06 #4 + 'kA' // fb07-fb12 + 'd36P' // fb13-fb17 #951 + 'dA' // fb18-fb1c + 'y21R' // fb1d-fb36 #563 + 'A' // fb37 + 'd21R' // fb38-fb3c #563 + 'A' // fb3d + '21R' // fb3e #563 + 'A' // fb3f + 'a21R' // fb40-fb41 #563 + 'A' // fb42 + 'a21R' // fb43-fb44 #563 + 'A' // fb45 + 'i21R' // fb46-fb4f #563 + '4j4W' // fb50-fbc2 #126 + 'oA' // fbc3-fbd2 + '13x4W' // fbd3-fd3d #126 + 'a263C' // fd3e-fd3f #6840 + '3a4W' // fd40-fd8f #126 + 'aA' // fd90-fd91 + '2a4W' // fd92-fdc7 #126 + 'fA' // fdc8-fdce + '4W' // fdcf #126 + '1eA' // fdd0-fdef + 'a4W' // fdf0-fdf1 #126 + '41Y' // fdf2 #1090 + 'i4W' // fdf3-fdfc #126 + '41Y' // fdfd #1090 + 'a4W' // fdfe-fdff #126 + '262K' // fe00 #6822 + 'nA' // fe01-fe0f + 'i13G' // fe10-fe19 #344 + 'eA' // fe1a-fe1f + 'cE' // fe20-fe23 #4 + 'b261X' // fe24-fe26 #6809 + 'cE' // fe27-fe2a #4 + 'b261U' // fe2b-fe2d #6806 + 'a41X' // fe2e-fe2f #1089 + '162Q' // fe30 #4228 + '64M' // fe31 #1676 + '13G' // fe32 #344 + 'c19Q' // fe33-fe36 #510 + '69M' // fe37 #1806 + 'j19Q' // fe38-fe42 #510 + '82N' // fe43 #2145 + '19Q' // fe44 #510 + 'c13G' // fe45-fe48 #344 + 'h19Q' // fe49-fe51 #510 + '126M' // fe52 #3288 + 'A' // fe53 + '19Q' // fe54 #510 + '64M' // fe55 #1676 + 'a19Q' // fe56-fe57 #510 + '13G' // fe58 #344 + 'c19Q' // fe59-fe5c #510 + 'a131P' // fe5d-fe5e #3421 + 'g19Q' // fe5f-fe66 #510 + 'A' // fe67 + 'b19Q' // fe68-fe6a #510 + '69M' // fe6b #1806 + 'cA' // fe6c-fe6f + 'd4W' // fe70-fe74 #126 + 'A' // fe75 + '5d4W' // fe76-fefc #126 + 'aA' // fefd-fefe + '71J' // feff #1855 + 'A' // ff00 + '245O' // ff01 #6384 + '126L' // ff02 #3287 + '142Y' // ff03 #3716 + '53J' // ff04 #1387 + '162C' // ff05 #4214 + '176X' // ff06 #4599 + '53J' // ff07 #1387 + 'a245N' // ff08-ff09 #6383 + '176G' // ff0a #4582 + '169J' // ff0b #4403 + '247K' // ff0c #6432 + '205R' // ff0d #5347 + '214F' // ff0e #5569 + '213T' // ff0f #5557 + '169O' // ff10 #4408 + '184H' // ff11 #4791 + '176Y' // ff12 #4600 + '176W' // ff13 #4598 + '162G' // ff14 #4218 + '169N' // ff15 #4407 + '155L' // ff16 #4041 + '155J' // ff17 #4039 + '155K' // ff18 #4040 + '155I' // ff19 #4038 + '246T' // ff1a #6415 + '219A' // ff1b #5694 + '149L' // ff1c #3885 + '149H' // ff1d #3881 + '184E' // ff1e #4788 + '245C' // ff1f #6372 + '143J' // ff20 #3727 + '149M' // ff21 #3886 + '137C' // ff22 #3564 + '64K' // ff23 #1674 + '137D' // ff24 #3565 + '63L' // ff25 #1649 + '53K' // ff26 #1388 + '43D' // ff27 #1121 + '53K' // ff28 #1388 + '53L' // ff29 #1389 + '43D' // ff2a #1121 + 'a53L' // ff2b-ff2c #1389 + '63L' // ff2d #1649 + '125Y' // ff2e #3274 + '137H' // ff2f #3569 + '64K' // ff30 #1674 + '82O' // ff31 #2146 + '82Q' // ff32 #2148 + '137I' // ff33 #3570 + '126A' // ff34 #3276 + '82P' // ff35 #2147 + '43D' // ff36 #1121 + '53I' // ff37 #1386 + '149F' // ff38 #3879 + '81Y' // ff39 #2130 + '81L' // ff3a #2117 + '64Y' // ff3b #1688 + '81X' // ff3c #2129 + '64Y' // ff3d #1688 + '82B' // ff3e #2133 + '52Y' // ff3f #1376 + '53I' // ff40 #1386 + '32F' // ff41 #837 + '81F' // ff42 #2111 + '32F' // ff43 #837 + '52Z' // ff44 #1377 + '32F' // ff45 #837 + '80V' // ff46 #2101 + '53E' // ff47 #1382 + '53C' // ff48 #1380 + '32F' // ff49 #837 + '52V' // ff4a #1373 + '53E' // ff4b #1382 + '53C' // ff4c #1380 + '82D' // ff4d #2135 + '81R' // ff4e #2123 + '32F' // ff4f #837 + '53A' // ff50 #1378 + '80S' // ff51 #2098 + 'b81T' // ff52-ff54 #2125 + '52Z' // ff55 #1377 + '81A' // ff56 #2106 + '82G' // ff57 #2138 + '52Y' // ff58 #1376 + '53A' // ff59 #1378 + '52V' // ff5a #1373 + '52U' // ff5b #1372 + '214A' // ff5c #5564 + '52U' // ff5d #1372 + '224D' // ff5e #5827 + 'a13G' // ff5f-ff60 #344 + '81W' // ff61 #2128 + 'a82A' // ff62-ff63 #2132 + '81O' // ff64 #2120 + '82H' // ff65 #2139 + '13G' // ff66 #344 + '80X' // ff67 #2103 + '81C' // ff68 #2108 + '247R' // ff69 #6439 + '80Z' // ff6a #2105 + '252X' // ff6b #6575 + '43C' // ff6c #1120 + '81E' // ff6d #2110 + '81B' // ff6e #2107 + '53H' // ff6f #1385 + '82E' // ff70 #2136 + '81S' // ff71 #2124 + '53H' // ff72 #1385 + '43C' // ff73 #1120 + '81G' // ff74 #2112 + '43C' // ff75 #1120 + '81Q' // ff76 #2122 + '81V' // ff77 #2127 + '37B' // ff78 #963 + '52W' // ff79 #1374 + '53D' // ff7a #1381 + '53B' // ff7b #1379 + '53G' // ff7c #1384 + '37B' // ff7d #963 + '52X' // ff7e #1375 + '70U' // ff7f #1840 + '81Z' // ff80 #2131 + '81N' // ff81 #2119 + '80W' // ff82 #2102 + '53D' // ff83 #1381 + '37B' // ff84 #963 + '52X' // ff85 #1375 + '43B' // ff86 #1119 + '13G' // ff87 #344 + '70U' // ff88 #1840 + '53F' // ff89 #1383 + '53G' // ff8a #1384 + '81J' // ff8b #2115 + '37B' // ff8c #963 + '43B' // ff8d #1119 + '53B' // ff8e #1379 + '81K' // ff8f #2116 + '52W' // ff90 #1374 + '81H' // ff91 #2113 + '43B' // ff92 #1119 + '80Y' // ff93 #2104 + '80U' // ff94 #2100 + '251U' // ff95 #6546 + '252A' // ff96 #6552 + '53F' // ff97 #1383 + 'a81U' // ff98-ff99 #2126 + '81M' // ff9a #2118 + '81P' // ff9b #2121 + '81D' // ff9c #2109 + '82C' // ff9d #2134 + '82F' // ff9e #2137 + '149J' // ff9f #3883 + 'A' // ffa0 + 'w13G' // ffa1-ffb8 #344 + '80T' // ffb9 #2099 + 'd13G' // ffba-ffbe #344 + 'bA' // ffbf-ffc1 + 'e13G' // ffc2-ffc7 #344 + 'aA' // ffc8-ffc9 + 'e13G' // ffca-ffcf #344 + 'aA' // ffd0-ffd1 + 'd13G' // ffd2-ffd6 #344 + '19F' // ffd7 #499 + 'aA' // ffd8-ffd9 + 'b19F' // ffda-ffdc #499 + 'bA' // ffdd-ffdf + 'b80R' // ffe0-ffe2 #2097 + '81I' // ffe3 #2114 + '247Q' // ffe4 #6438 + '125W' // ffe5 #3272 + '247P' // ffe6 #6437 + 'A' // ffe7 + '19F' // ffe8 #499 + '43A' // ffe9 #1118 + '19F' // ffea #499 + '43A' // ffeb #1118 + '19F' // ffec #499 + '43A' // ffed #1118 + '19F' // ffee #499 + 'iA' // ffef-fff8 + 'bF' // fff9-fffb #5 + 'aE' // fffc-fffd #4 + 'aA' // fffe-ffff + 'k24I' // 10000-1000b #632 + 'A' // 1000c + 'y24I' // 1000d-10026 #632 + 'A' // 10027 + 'r24I' // 10028-1003a #632 + 'A' // 1003b + 'a24I' // 1003c-1003d #632 + 'A' // 1003e + 'n24I' // 1003f-1004d #632 + 'aA' // 1004e-1004f + 'm24I' // 10050-1005d #632 + '1gA' // 1005e-1007f + '4r24I' // 10080-100fa #632 + 'dA' // 100fb-100ff + 'b71W' // 10100-10102 #1868 + 'cA' // 10103-10106 + '1r71W' // 10107-10133 #1868 + 'bA' // 10134-10136 + 'h24I' // 10137-1013f #632 + '2zF' // 10140-1018e #5 + 'A' // 1018f + 'lF' // 10190-1019c #5 + 'bA' // 1019d-1019f + 'F' // 101a0 #5 + '1tA' // 101a1-101cf + '1sF' // 101d0-101fd #5 + '4yA' // 101fe-1027f + '1b264E' // 10280-1029c #6868 + 'bA' // 1029d-1029f + '1v263M' // 102a0-102d0 #6850 + 'nA' // 102d1-102df + '1a78O' // 102e0-102fb #2042 + 'cA' // 102fc-102ff + '1i72O' // 10300-10323 #1886 + 'hA' // 10324-1032c + 'b72O' // 1032d-1032f #1886 + 'z263Y' // 10330-1034a #6862 + 'dA' // 1034b-1034f + '1p264L' // 10350-1037a #6875 + 'dA' // 1037b-1037f + '1c73F' // 10380-1039d #1903 + 'A' // 1039e + '73F' // 1039f #1903 + '1i72P' // 103a0-103c3 #1887 + 'cA' // 103c4-103c7 + 'm72P' // 103c8-103d5 #1887 + '1oA' // 103d6-103ff + '3a263O' // 10400-1044f #6852 + '1u264U' // 10450-1047f #6884 + '1c72R' // 10480-1049d #1889 + 'aA' // 1049e-1049f + 'i72R' // 104a0-104a9 #1889 + 'eA' // 104aa-104af + '1i72Q' // 104b0-104d3 #1888 + 'cA' // 104d4-104d7 + '1i72Q' // 104d8-104fb #1888 + 'cA' // 104fc-104ff + '1m263V' // 10500-10527 #6859 + 'gA' // 10528-1052f + '1y71U' // 10530-10563 #1866 + 'jA' // 10564-1056e + '71U' // 1056f #1866 + '5mA' // 10570-105ff + '11x50W' // 10600-10736 #1322 + 'hA' // 10737-1073f + 'u50W' // 10740-10755 #1322 + 'iA' // 10756-1075f + 'g50W' // 10760-10767 #1322 + 'wA' // 10768-1077f + 'eE' // 10780-10785 #4 + 'A' // 10786 + '1oE' // 10787-107b0 #4 + 'A' // 107b1 + 'hE' // 107b2-107ba #4 + '2pA' // 107bb-107ff + 'e31T' // 10800-10805 #825 + 'aA' // 10806-10807 + '31T' // 10808 #825 + 'A' // 10809 + '1q31T' // 1080a-10835 #825 + 'A' // 10836 + 'a31T' // 10837-10838 #825 + 'bA' // 10839-1083b + '31T' // 1083c #825 + 'aA' // 1083d-1083e + '31T' // 1083f #825 + 'u71X' // 10840-10855 #1869 + 'A' // 10856 + 'h71X' // 10857-1085f #1869 + '1e264P' // 10860-1087f #6879 + '1d72L' // 10880-1089e #1883 + 'gA' // 1089f-108a6 + 'h72L' // 108a7-108af #1883 + '1uA' // 108b0-108df + 'r50T' // 108e0-108f2 #1319 + 'A' // 108f3 + 'a50T' // 108f4-108f5 #1319 + 'dA' // 108f6-108fa + 'd50T' // 108fb-108ff #1319 + '1a72S' // 10900-1091b #1890 + 'bA' // 1091c-1091e + '72S' // 1091f #1890 + 'y72F' // 10920-10939 #1877 + 'dA' // 1093a-1093e + '72F' // 1093f #1877 + '2kA' // 10940-1097f + '2c50Z' // 10980-109b7 #1325 + 'cA' // 109b8-109bb + 's50Z' // 109bc-109cf #1325 + 'aA' // 109d0-109d1 + '1s50Z' // 109d2-109ff #1325 + 'c24H' // 10a00-10a03 #631 + 'A' // 10a04 + 'a24H' // 10a05-10a06 #631 + 'dA' // 10a07-10a0b + 'g24H' // 10a0c-10a13 #631 + 'A' // 10a14 + 'b24H' // 10a15-10a17 #631 + 'A' // 10a18 + '1b24H' // 10a19-10a35 #631 + 'aA' // 10a36-10a37 + 'b24H' // 10a38-10a3a #631 + 'cA' // 10a3b-10a3e + 'i24H' // 10a3f-10a48 #631 + 'fA' // 10a49-10a4f + 'h24H' // 10a50-10a58 #631 + 'fA' // 10a59-10a5f + '1e264N' // 10a60-10a7f #6877 + '1e264K' // 10a80-10a9f #6874 + '1eA' // 10aa0-10abf + '1l72H' // 10ac0-10ae6 #1879 + 'cA' // 10ae7-10aea + 'k72H' // 10aeb-10af6 #1879 + 'hA' // 10af7-10aff + '2a71N' // 10b00-10b35 #1859 + 'bA' // 10b36-10b38 + 'f71N' // 10b39-10b3f #1859 + 'u71Z' // 10b40-10b55 #1871 + 'aA' // 10b56-10b57 + 'g71Z' // 10b58-10b5f #1871 + 'r71Y' // 10b60-10b72 #1870 + 'dA' // 10b73-10b77 + 'g71Y' // 10b78-10b7f #1870 + 'q51D' // 10b80-10b91 #1329 + 'fA' // 10b92-10b98 + 'c51D' // 10b99-10b9c #1329 + 'kA' // 10b9d-10ba8 + 'f51D' // 10ba9-10baf #1329 + '3aA' // 10bb0-10bff + '2t264O' // 10c00-10c48 #6878 + '2bA' // 10c49-10c7f + '1x51C' // 10c80-10cb2 #1328 + 'lA' // 10cb3-10cbf + '1x51C' // 10cc0-10cf2 #1328 + 'fA' // 10cf3-10cf9 + 'e51C' // 10cfa-10cff #1328 + '13mA' // 10d00-10e5f + '1dF' // 10e60-10e7e #5 + '4uA' // 10e7f-10efc + 'b4W' // 10efd-10eff #126 + '1m264M' // 10f00-10f27 #6876 + 'gA' // 10f28-10f2f + '1o264V' // 10f30-10f59 #6885 + '5cA' // 10f5a-10fdf + 'v263W' // 10fe0-10ff6 #6860 + 'hA' // 10ff7-10fff + '2y50O' // 11000-1104d #1314 + 'cA' // 1104e-11051 + '1i50O' // 11052-11075 #1314 + 'hA' // 11076-1107e + '50O' // 1107f #1314 + '2n72A' // 11080-110c2 #1872 + 'iA' // 110c3-110cc + '72A' // 110cd #1872 + 'aA' // 110ce-110cf + 'x72W' // 110d0-110e8 #1894 + 'fA' // 110e9-110ef + 'i72W' // 110f0-110f9 #1894 + 'eA' // 110fa-110ff + '1z71V' // 11100-11134 #1867 + 'A' // 11135 + 'q71V' // 11136-11147 #1867 + 'gA' // 11148-1114f + '1l264F' // 11150-11176 #6869 + 'hA' // 11177-1117f + '3q264T' // 11180-111df #6883 + 'A' // 111e0 + 's15C' // 111e1-111f4 #392 + 'jA' // 111f5-111ff + 'q72C' // 11200-11211 #1874 + 'A' // 11212 + '1s72C' // 11213-11240 #1874 + '2jA' // 11241-1127f + 'f36R' // 11280-11286 #953 + 'A' // 11287 + '36R' // 11288 #953 + 'A' // 11289 + 'c36R' // 1128a-1128d #953 + 'A' // 1128e + 'n36R' // 1128f-1129d #953 + 'A' // 1129e + 'j36R' // 1129f-112a9 #953 + 'eA' // 112aa-112af + '2f72D' // 112b0-112ea #1875 + 'dA' // 112eb-112ef + 'i72D' // 112f0-112f9 #1875 + 'eA' // 112fa-112ff + '11O' // 11300 #300 + '31U' // 11301 #826 + '11O' // 11302 #300 + '31U' // 11303 #826 + 'A' // 11304 + 'g11O' // 11305-1130c #300 + 'aA' // 1130d-1130e + 'a11O' // 1130f-11310 #300 + 'aA' // 11311-11312 + 'u11O' // 11313-11328 #300 + 'A' // 11329 + 'f11O' // 1132a-11330 #300 + 'A' // 11331 + 'a11O' // 11332-11333 #300 + 'A' // 11334 + 'd11O' // 11335-11339 #300 + 'A' // 1133a + 'a31U' // 1133b-1133c #826 + 'g11O' // 1133d-11344 #300 + 'aA' // 11345-11346 + 'a11O' // 11347-11348 #300 + 'aA' // 11349-1134a + 'b11O' // 1134b-1134d #300 + 'aA' // 1134e-1134f + '11O' // 11350 #300 + 'eA' // 11351-11356 + '11O' // 11357 #300 + 'dA' // 11358-1135c + 'f11O' // 1135d-11363 #300 + 'aA' // 11364-11365 + 'f11O' // 11366-1136c #300 + 'bA' // 1136d-1136f + 'd11O' // 11370-11374 #300 + '5hA' // 11375-113ff + '3m72M' // 11400-1145b #1884 + 'A' // 1145c + 'd72M' // 1145d-11461 #1884 + '1cA' // 11462-1147f + '2s73E' // 11480-114c7 #1902 + 'gA' // 114c8-114cf + 'i73E' // 114d0-114d9 #1902 + '6iA' // 114da-1157f + '2a72V' // 11580-115b5 #1893 + 'aA' // 115b6-115b7 + '1k72V' // 115b8-115dd #1893 + '1gA' // 115de-115ff + '2p72I' // 11600-11644 #1880 + 'jA' // 11645-1164f + 'i72I' // 11650-11659 #1880 + 'eA' // 1165a-1165f + 'l31X' // 11660-1166c #829 + 'rA' // 1166d-1167f + '2e73B' // 11680-116b9 #1899 + 'eA' // 116ba-116bf + 'i73B' // 116c0-116c9 #1899 + '18aA' // 116ca-1189f + '3d73H' // 118a0-118f2 #1905 + 'kA' // 118f3-118fe + '73H' // 118ff #1905 + '9uA' // 11900-119ff + '2s265B' // 11a00-11a47 #6891 + 'gA' // 11a48-11a4f + '3d264W' // 11a50-11aa2 #6886 + 'lA' // 11aa3-11aaf + 'o50P' // 11ab0-11abf #1315 + '2d264Q' // 11ac0-11af8 #6880 + 'fA' // 11af9-11aff + 'i17L' // 11b00-11b09 #453 + '9kA' // 11b0a-11bff + 'h41Z' // 11c00-11c08 #1091 + 'A' // 11c09 + '1r41Z' // 11c0a-11c36 #1091 + 'A' // 11c37 + 'm41Z' // 11c38-11c45 #1091 + 'iA' // 11c46-11c4f + '1b41Z' // 11c50-11c6c #1091 + 'bA' // 11c6d-11c6f + '1e50X' // 11c70-11c8f #1323 + 'aA' // 11c90-11c91 + 'u50X' // 11c92-11ca7 #1323 + 'A' // 11ca8 + 'm50X' // 11ca9-11cb6 #1323 + '2tA' // 11cb7-11cff + 'f27K' // 11d00-11d06 #712 + 'A' // 11d07 + 'a27K' // 11d08-11d09 #712 + 'A' // 11d0a + '1q27K' // 11d0b-11d36 #712 + 'bA' // 11d37-11d39 + '27K' // 11d3a #712 + 'A' // 11d3b + 'a27K' // 11d3c-11d3d #712 + 'A' // 11d3e + 'h27K' // 11d3f-11d47 #712 + 'gA' // 11d48-11d4f + 'i27K' // 11d50-11d59 #712 + 'eA' // 11d5a-11d5f + 'e31V' // 11d60-11d65 #827 + 'A' // 11d66 + 'a31V' // 11d67-11d68 #827 + 'A' // 11d69 + '1j31V' // 11d6a-11d8e #827 + 'A' // 11d8f + 'a31V' // 11d90-11d91 #827 + 'A' // 11d92 + 'e31V' // 11d93-11d98 #827 + 'fA' // 11d99-11d9f + 'i31V' // 11da0-11da9 #827 + '19wA' // 11daa-11faf + '72E' // 11fb0 #1876 + 'nA' // 11fb1-11fbf + '1w73C' // 11fc0-11ff1 #1900 + 'lA' // 11ff2-11ffe + '73C' // 11fff #1900 + '35k36W' // 12000-12399 #958 + '3wA' // 1239a-123ff + '4f36W' // 12400-1246e #958 + 'A' // 1246f + 'd36W' // 12470-12474 #958 + 'jA' // 12475-1247f + '7m36W' // 12480-12543 #958 + '105qA' // 12544-12fff + '33s42X' // 13000-1336d #1115 + '80G' // 1336e #2086 + '7j42X' // 1336f-1342f #1115 + 'pA' // 13430-13440 + 'e42X' // 13441-13446 #1115 + '154tA' // 13447-143ff + '22j262Z' // 14400-14646 #6837 + '331zA' // 14647-167ff + '21v71P' // 16800-16a38 #1861 + 'fA' // 16a39-16a3f + '1d51B' // 16a40-16a5e #1327 + 'A' // 16a5f + 'i51B' // 16a60-16a69 #1327 + 'cA' // 16a6a-16a6d + 'a51B' // 16a6e-16a6f #1327 + '3qA' // 16a70-16acf + '1c71Q' // 16ad0-16aed #1862 + 'aA' // 16aee-16aef + 'e71Q' // 16af0-16af5 #1862 + 'iA' // 16af6-16aff + '2q36S' // 16b00-16b45 #954 + 'iA' // 16b46-16b4f + 'i36S' // 16b50-16b59 #954 + 'A' // 16b5a + 'f36S' // 16b5b-16b61 #954 + 'A' // 16b62 + 't36S' // 16b63-16b77 #954 + 'dA' // 16b78-16b7c + 'r36S' // 16b7d-16b8f #954 + '26kA' // 16b90-16e3f + '3l264H' // 16e40-16e9a #6871 + '3vA' // 16e9b-16eff + '2v51A' // 16f00-16f4a #1326 + 'cA' // 16f4b-16f4e + '2d51A' // 16f4f-16f87 #1326 + 'fA' // 16f88-16f8e + 'p51A' // 16f8f-16f9f #1326 + '2lA' // 16fa0-16fe0 + '72N' // 16fe1 #1885 + '645kA' // 16fe2-1b16f + '15e72N' // 1b170-1b2fb #1885 + '88sA' // 1b2fc-1bbff + '4b32E' // 1bc00-1bc6a #836 + 'dA' // 1bc6b-1bc6f + 'l32E' // 1bc70-1bc7c #836 + 'bA' // 1bc7d-1bc7f + 'h32E' // 1bc80-1bc88 #836 + 'fA' // 1bc89-1bc8f + 'i32E' // 1bc90-1bc99 #836 + 'aA' // 1bc9a-1bc9b + 'g32E' // 1bc9c-1bca3 #836 + '190oA' // 1bca4-1cfff + '9k41U' // 1d000-1d0f5 #1086 + 'iA' // 1d0f6-1d0ff + '1l41U' // 1d100-1d126 #1086 + 'aA' // 1d127-1d128 + '7k41U' // 1d129-1d1ea #1086 + 'tA' // 1d1eb-1d1ff + '2q41U' // 1d200-1d245 #1086 + '4qA' // 1d246-1d2bf + 'sF' // 1d2c0-1d2d3 #5 + 'kA' // 1d2d4-1d2df + 's77M' // 1d2e0-1d2f3 #2014 + 'kA' // 1d2f4-1d2ff + '3hF' // 1d300-1d356 #5 + 'hA' // 1d357-1d35f + 'xF' // 1d360-1d378 #5 + '5dA' // 1d379-1d3ff + '3fM' // 1d400-1d454 #12 + 'A' // 1d455 + '2rM' // 1d456-1d49c #12 + 'A' // 1d49d + 'aM' // 1d49e-1d49f #12 + 'aA' // 1d4a0-1d4a1 + 'M' // 1d4a2 #12 + 'aA' // 1d4a3-1d4a4 + 'aM' // 1d4a5-1d4a6 #12 + 'aA' // 1d4a7-1d4a8 + 'cM' // 1d4a9-1d4ac #12 + 'A' // 1d4ad + 'kM' // 1d4ae-1d4b9 #12 + 'A' // 1d4ba + 'M' // 1d4bb #12 + 'A' // 1d4bc + 'fM' // 1d4bd-1d4c3 #12 + 'A' // 1d4c4 + '2lM' // 1d4c5-1d505 #12 + 'A' // 1d506 + 'cM' // 1d507-1d50a #12 + 'aA' // 1d50b-1d50c + 'gM' // 1d50d-1d514 #12 + 'A' // 1d515 + 'fM' // 1d516-1d51c #12 + 'A' // 1d51d + '1aM' // 1d51e-1d539 #12 + 'A' // 1d53a + 'cM' // 1d53b-1d53e #12 + 'A' // 1d53f + 'dM' // 1d540-1d544 #12 + 'A' // 1d545 + 'M' // 1d546 #12 + 'bA' // 1d547-1d549 + 'fM' // 1d54a-1d550 #12 + 'A' // 1d551 + '13aM' // 1d552-1d6a5 #12 + 'aA' // 1d6a6-1d6a7 + '11eM' // 1d6a8-1d7cb #12 + 'aA' // 1d7cc-1d7cd + '1wM' // 1d7ce-1d7ff #12 + '68wA' // 1d800-1deff + '1dE' // 1df00-1df1e #4 + '8pA' // 1df1f-1dfff + 'f27I' // 1e000-1e006 #710 + 'A' // 1e007 + 'p27I' // 1e008-1e018 #710 + 'aA' // 1e019-1e01a + 'f27I' // 1e01b-1e021 #710 + 'A' // 1e022 + 'a27I' // 1e023-1e024 #710 + 'A' // 1e025 + 'd27I' // 1e026-1e02a #710 + '25jA' // 1e02b-1e2bf + '2e73G' // 1e2c0-1e2f9 #1904 + 'dA' // 1e2fa-1e2fe + '73G' // 1e2ff #1904 + '47yA' // 1e300-1e7df + 'f3R' // 1e7e0-1e7e6 #95 + 'A' // 1e7e7 + 'c3R' // 1e7e8-1e7eb #95 + 'A' // 1e7ec + 'a3R' // 1e7ed-1e7ee #95 + 'A' // 1e7ef + 'n3R' // 1e7f0-1e7fe #95 + '9vA' // 1e7ff-1e8ff + '2w50L' // 1e900-1e94b #1311 + 'cA' // 1e94c-1e94f + 'i50L' // 1e950-1e959 #1311 + 'cA' // 1e95a-1e95d + 'a50L' // 1e95e-1e95f #1311 + '30dA' // 1e960-1ec70 + '2o264D' // 1ec71-1ecb4 #6867 + '12rA' // 1ecb5-1edff + 'cM' // 1ee00-1ee03 #12 + 'A' // 1ee04 + 'zM' // 1ee05-1ee1f #12 + 'A' // 1ee20 + 'aM' // 1ee21-1ee22 #12 + 'A' // 1ee23 + 'M' // 1ee24 #12 + 'aA' // 1ee25-1ee26 + 'M' // 1ee27 #12 + 'A' // 1ee28 + 'iM' // 1ee29-1ee32 #12 + 'A' // 1ee33 + 'cM' // 1ee34-1ee37 #12 + 'A' // 1ee38 + 'M' // 1ee39 #12 + 'A' // 1ee3a + 'M' // 1ee3b #12 + 'eA' // 1ee3c-1ee41 + 'M' // 1ee42 #12 + 'cA' // 1ee43-1ee46 + 'M' // 1ee47 #12 + 'A' // 1ee48 + 'M' // 1ee49 #12 + 'A' // 1ee4a + 'M' // 1ee4b #12 + 'A' // 1ee4c + 'bM' // 1ee4d-1ee4f #12 + 'A' // 1ee50 + 'aM' // 1ee51-1ee52 #12 + 'A' // 1ee53 + 'M' // 1ee54 #12 + 'aA' // 1ee55-1ee56 + 'M' // 1ee57 #12 + 'A' // 1ee58 + 'M' // 1ee59 #12 + 'A' // 1ee5a + 'M' // 1ee5b #12 + 'A' // 1ee5c + 'M' // 1ee5d #12 + 'A' // 1ee5e + 'M' // 1ee5f #12 + 'A' // 1ee60 + 'aM' // 1ee61-1ee62 #12 + 'A' // 1ee63 + 'M' // 1ee64 #12 + 'aA' // 1ee65-1ee66 + 'cM' // 1ee67-1ee6a #12 + 'A' // 1ee6b + 'fM' // 1ee6c-1ee72 #12 + 'A' // 1ee73 + 'cM' // 1ee74-1ee77 #12 + 'A' // 1ee78 + 'cM' // 1ee79-1ee7c #12 + 'A' // 1ee7d + 'M' // 1ee7e #12 + 'A' // 1ee7f + 'iM' // 1ee80-1ee89 #12 + 'A' // 1ee8a + 'pM' // 1ee8b-1ee9b #12 + 'dA' // 1ee9c-1eea0 + 'bM' // 1eea1-1eea3 #12 + 'A' // 1eea4 + 'dM' // 1eea5-1eea9 #12 + 'A' // 1eeaa + 'pM' // 1eeab-1eebb #12 + '1yA' // 1eebc-1eeef + 'aM' // 1eef0-1eef1 #12 + '10iA' // 1eef2-1efff + 'cF' // 1f000-1f003 #5 + '6J' // 1f004 #165 + '1lF' // 1f005-1f02b #5 + 'cA' // 1f02c-1f02f + '3uF' // 1f030-1f093 #5 + 'kA' // 1f094-1f09f + 'nF' // 1f0a0-1f0ae #5 + 'aA' // 1f0af-1f0b0 + 'nF' // 1f0b1-1f0bf #5 + 'A' // 1f0c0 + 'mF' // 1f0c1-1f0ce #5 + '6J' // 1f0cf #165 + 'A' // 1f0d0 + '1jF' // 1f0d1-1f0f5 #5 + 'iA' // 1f0f6-1f0ff + 'l12J' // 1f100-1f10c #321 + 'bF' // 1f10d-1f10f #5 + '1v12J' // 1f110-1f140 #321 + 'a17P' // 1f141-1f142 #457 + 'l12J' // 1f143-1f14f #321 + '17P' // 1f150 #457 + 'b12J' // 1f151-1f153 #321 + '17P' // 1f154 #457 + 'b12J' // 1f155-1f157 #321 + '17P' // 1f158 #457 + 'a12J' // 1f159-1f15a #321 + '17P' // 1f15b #457 + '12J' // 1f15c #321 + 'a17P' // 1f15d-1f15e #457 + 'b12J' // 1f15f-1f161 #321 + 'a17P' // 1f162-1f163 #457 + 'h12J' // 1f164-1f16c #321 + 'bF' // 1f16d-1f16f #5 + 'a74M' // 1f170-1f171 #1936 + 'a12J' // 1f172-1f173 #321 + '17P' // 1f174 #457 + 'a12J' // 1f175-1f176 #321 + 'a17P' // 1f177-1f178 #457 + 'c12J' // 1f179-1f17c #321 + '17P' // 1f17d #457 + 'a74N' // 1f17e-1f17f #1937 + 'm12J' // 1f180-1f18d #321 + '74Y' // 1f18e #1948 + 'a12J' // 1f18f-1f190 #321 + '42H' // 1f191 #1099 + 'c51N' // 1f192-1f195 #1339 + '42H' // 1f196 #1099 + '42G' // 1f197 #1098 + 'b51N' // 1f198-1f19a #1339 + 'q12J' // 1f19b-1f1ac #321 + 'F' // 1f1ad #5 + '2cA' // 1f1ae-1f1e5 + 'y73J' // 1f1e6-1f1ff #1907 + '19F' // 1f200 #499 + 'a42H' // 1f201-1f202 #1099 + 'lA' // 1f203-1f20f + 'i19F' // 1f210-1f219 #499 + '42F' // 1f21a #1097 + 's19F' // 1f21b-1f22e #499 + '42G' // 1f22f #1098 + 'a19F' // 1f230-1f231 #499 + '42F' // 1f232 #1097 + '42G' // 1f233 #1098 + '42F' // 1f234 #1097 + 'b51M' // 1f235-1f237 #1338 + '74X' // 1f238 #1947 + 'a74L' // 1f239-1f23a #1935 + 'C' // 1f23b #2 + 'cA' // 1f23c-1f23f + 'hC' // 1f240-1f248 #2 + 'fA' // 1f249-1f24f + 'a51M' // 1f250-1f251 #1338 + '6qA' // 1f252-1f2ff + '5F' // 1f300 #135 + '4X' // 1f301 #127 + '2S' // 1f302 #70 + '4X' // 1f303 #127 + 'a5F' // 1f304-1f305 #135 + 'a4X' // 1f306-1f307 #127 + '73M' // 1f308 #1910 + '4X' // 1f309 #127 + 'b5F' // 1f30a-1f30c #135 + 'b5Z' // 1f30d-1f30f #155 + '51H' // 1f310 #1333 + 'c5F' // 1f311-1f314 #135 + '5Z' // 1f315 #155 + 'c5F' // 1f316-1f319 #135 + 'a27N' // 1f31a-1f31b #715 + '36U' // 1f31c #956 + 'b27N' // 1f31d-1f31f #715 + '5F' // 1f320 #135 + '75E' // 1f321 #1954 + 'aF' // 1f322-1f323 #5 + 'f5Z' // 1f324-1f32a #155 + '36U' // 1f32b #956 + '5Z' // 1f32c #155 + 'c15F' // 1f32d-1f330 #395 + 'd5F' // 1f331-1f335 #135 + '32A' // 1f336 #832 + 'e5F' // 1f337-1f33c #135 + '15F' // 1f33d #395 + '75R' // 1f33e #1967 + 'd5F' // 1f33f-1f343 #135 + '75T' // 1f344 #1969 + 'e15F' // 1f345-1f34a #395 + '75Q' // 1f34b #1966 + '1l15F' // 1f34c-1f372 #395 + '51V' // 1f373 #1347 + 'c15F' // 1f374-1f377 #395 + '32A' // 1f378 #832 + 'b15F' // 1f379-1f37b #395 + '51V' // 1f37c #1347 + '32A' // 1f37d #832 + 'a15F' // 1f37e-1f37f #395 + 'a5P' // 1f380-1f381 #145 + '75N' // 1f382 #1963 + '42L' // 1f383 #1103 + '42K' // 1f384 #1102 + '4S' // 1f385 #122 + 'b5P' // 1f386-1f388 #145 + '42L' // 1f389 #1103 + 'a5P' // 1f38a-1f38b #145 + '15E' // 1f38c #394 + 'd5P' // 1f38d-1f391 #145 + '2S' // 1f392 #70 + '51R' // 1f393 #1343 + 'aF' // 1f394-1f395 #5 + 'a6J' // 1f396-1f397 #165 + 'F' // 1f398 #5 + 'b6J' // 1f399-1f39b #165 + 'aF' // 1f39c-1f39d #5 + 'a6J' // 1f39e-1f39f #165 + 'b4X' // 1f3a0-1f3a2 #127 + '5P' // 1f3a3 #145 + '42K' // 1f3a4 #1102 + '5P' // 1f3a5 #145 + '3S' // 1f3a6 #96 + '6J' // 1f3a7 #165 + '42K' // 1f3a8 #1102 + '42I' // 1f3a9 #1100 + '4X' // 1f3aa #127 + '5P' // 1f3ab #145 + 'b6J' // 1f3ac-1f3ae #165 + 'e5P' // 1f3af-1f3b4 #145 + 'a3S' // 1f3b5-1f3b6 #96 + 'd5P' // 1f3b7-1f3bb #145 + '3S' // 1f3bc #96 + '42I' // 1f3bd #1100 + 'b5P' // 1f3be-1f3c0 #145 + '15E' // 1f3c1 #394 + '27O' // 1f3c2 #716 + '32B' // 1f3c3 #833 + '27O' // 1f3c4 #716 + '5P' // 1f3c5 #145 + '6J' // 1f3c6 #165 + '4S' // 1f3c7 #122 + 'a5P' // 1f3c8-1f3c9 #145 + 'b27O' // 1f3ca-1f3cc #716 + 'a9O' // 1f3cd-1f3ce #248 + 'd5P' // 1f3cf-1f3d3 #145 + '5Z' // 1f3d4 #155 + '9O' // 1f3d5 #248 + '5Z' // 1f3d6 #155 + 'd9O' // 1f3d7-1f3db #248 + 'b5Z' // 1f3dc-1f3de #155 + 'a9O' // 1f3df-1f3e0 #248 + 'e4X' // 1f3e1-1f3e6 #127 + '3S' // 1f3e7 #96 + 'b4X' // 1f3e8-1f3ea #127 + '42M' // 1f3eb #1104 + '4X' // 1f3ec #127 + '51T' // 1f3ed #1345 + '2S' // 1f3ee #70 + 'a4X' // 1f3ef-1f3f0 #127 + 'aF' // 1f3f1-1f3f2 #5 + '73O' // 1f3f3 #1912 + '15E' // 1f3f4 #394 + '5Z' // 1f3f5 #155 + 'F' // 1f3f6 #5 + '2E' // 1f3f7 #56 + 'a5P' // 1f3f8-1f3f9 #145 + '2S' // 1f3fa #70 + 'd75X' // 1f3fb-1f3ff #1973 + 'g5F' // 1f400-1f407 #135 + '5Z' // 1f408 #155 + 'k5F' // 1f409-1f414 #135 + '5Z' // 1f415 #155 + 'h5F' // 1f416-1f41e #135 + '5Z' // 1f41f #155 + 'e5F' // 1f420-1f425 #135 + '75U' // 1f426 #1970 + 'w5F' // 1f427-1f43e #135 + '5Z' // 1f43f #155 + '3O' // 1f440 #92 + '73U' // 1f441 #1918 + '17O' // 1f442 #456 + 'b3O' // 1f443-1f445 #92 + 'c17O' // 1f446-1f449 #456 + 'a3O' // 1f44a-1f44b #92 + 'b17O' // 1f44c-1f44e #456 + 'a3O' // 1f44f-1f450 #92 + 'a2S' // 1f451-1f452 #70 + '2E' // 1f453 #56 + 'n2S' // 1f454-1f462 #70 + 'b3O' // 1f463-1f465 #92 + 'a4S' // 1f466-1f467 #122 + 'a32B' // 1f468-1f469 #833 + '27O' // 1f46a #716 + 'm4S' // 1f46b-1f478 #122 + 'b3O' // 1f479-1f47b #92 + '4S' // 1f47c #122 + '17O' // 1f47d #456 + '42L' // 1f47e #1103 + 'a3O' // 1f47f-1f480 #92 + 'b4S' // 1f481-1f483 #122 + '2S' // 1f484 #70 + '3O' // 1f485 #92 + 'a4S' // 1f486-1f487 #122 + '4X' // 1f488 #127 + 'a2S' // 1f489-1f48a #70 + '51X' // 1f48b #1349 + '75H' // 1f48c #1957 + 'a2S' // 1f48d-1f48e #70 + '4S' // 1f48f #122 + '5F' // 1f490 #135 + '4S' // 1f491 #122 + '4X' // 1f492 #127 + 'e3O' // 1f493-1f498 #92 + 'c27L' // 1f499-1f49c #713 + 'a3O' // 1f49d-1f49e #92 + '27L' // 1f49f #713 + '3S' // 1f4a0 #96 + '2S' // 1f4a1 #70 + '3S' // 1f4a2 #96 + '2E' // 1f4a3 #56 + '3O' // 1f4a4 #92 + '51Z' // 1f4a5 #1351 + '3O' // 1f4a6 #92 + '5F' // 1f4a7 #135 + 'b3O' // 1f4a8-1f4aa #92 + '27N' // 1f4ab #715 + 'a3S' // 1f4ac-1f4ad #96 + '5F' // 1f4ae #135 + '3O' // 1f4af #92 + '2E' // 1f4b0 #56 + 'a3S' // 1f4b1-1f4b2 #96 + '2E' // 1f4b3 #56 + 'd2S' // 1f4b4-1f4b8 #70 + '4M' // 1f4b9 #116 + '4X' // 1f4ba #127 + '51R' // 1f4bb #1343 + '42J' // 1f4bc #1101 + 'a2S' // 1f4bd-1f4be #70 + '2E' // 1f4bf #56 + 'g2S' // 1f4c0-1f4c7 #70 + 'c2E' // 1f4c8-1f4cb #56 + 'm2S' // 1f4cc-1f4d9 #70 + '2E' // 1f4da #56 + '3S' // 1f4db #96 + 'b2S' // 1f4dc-1f4de #70 + '2E' // 1f4df #56 + 'a2S' // 1f4e0-1f4e1 #70 + '42I' // 1f4e2 #1100 + '2S' // 1f4e3 #70 + 'b2E' // 1f4e4-1f4e6 #56 + 'b2S' // 1f4e7-1f4e9 #70 + 'c2E' // 1f4ea-1f4ed #56 + 'c2S' // 1f4ee-1f4f1 #70 + 'd3S' // 1f4f2-1f4f6 #96 + '6J' // 1f4f7 #165 + '5P' // 1f4f8 #145 + 'b6J' // 1f4f9-1f4fb #165 + '5P' // 1f4fc #145 + '6J' // 1f4fd #165 + 'F' // 1f4fe #5 + '2S' // 1f4ff #70 + 'b3S' // 1f500-1f502 #96 + '4M' // 1f503 #116 + 'b3S' // 1f504-1f506 #96 + 'c4M' // 1f507-1f50a #116 + 'a2S' // 1f50b-1f50c #70 + '2E' // 1f50d #56 + 'c2S' // 1f50e-1f511 #70 + 'a2E' // 1f512-1f513 #56 + '2S' // 1f514 #70 + '3S' // 1f515 #96 + 'a2S' // 1f516-1f517 #70 + 'l3S' // 1f518-1f524 #96 + '75S' // 1f525 #1968 + '2S' // 1f526 #70 + '42J' // 1f527 #1101 + 'a2S' // 1f528-1f529 #70 + '15F' // 1f52a #395 + '5P' // 1f52b #145 + '42J' // 1f52c #1101 + 'a2S' // 1f52d-1f52e #70 + 'n3S' // 1f52f-1f53d #96 + 'gF' // 1f53e-1f545 #5 + 'b2M' // 1f546-1f548 #64 + '17N' // 1f549 #455 + '5Z' // 1f54a #155 + 'b4X' // 1f54b-1f54d #127 + '3S' // 1f54e #96 + '2M' // 1f54f #64 + 'w2E' // 1f550-1f567 #56 + 'fF' // 1f568-1f56e #5 + 'a2E' // 1f56f-1f570 #56 + 'aF' // 1f571-1f572 #5 + '36U' // 1f573 #956 + 'a27O' // 1f574-1f575 #716 + '2E' // 1f576 #56 + 'a5Z' // 1f577-1f578 #155 + '6J' // 1f579 #165 + '4S' // 1f57a #122 + 'kF' // 1f57b-1f586 #5 + '2E' // 1f587 #56 + 'aF' // 1f588-1f589 #5 + 'a2E' // 1f58a-1f58b #56 + 'a51Q' // 1f58c-1f58d #1342 + 'aF' // 1f58e-1f58f #5 + '17O' // 1f590 #456 + 'cF' // 1f591-1f594 #5 + 'a3O' // 1f595-1f596 #92 + 'lF' // 1f597-1f5a3 #5 + '27L' // 1f5a4 #713 + '2E' // 1f5a5 #56 + 'aF' // 1f5a6-1f5a7 #5 + '2E' // 1f5a8 #56 + 'gF' // 1f5a9-1f5b0 #5 + 'a2E' // 1f5b1-1f5b2 #56 + 'hF' // 1f5b3-1f5bb #5 + '6J' // 1f5bc #165 + 'dF' // 1f5bd-1f5c1 #5 + 'b2E' // 1f5c2-1f5c4 #56 + 'kF' // 1f5c5-1f5d0 #5 + 'b2E' // 1f5d1-1f5d3 #56 + 'gF' // 1f5d4-1f5db #5 + 'b2E' // 1f5dc-1f5de #56 + 'aF' // 1f5df-1f5e0 #5 + '2E' // 1f5e1 #56 + 'F' // 1f5e2 #5 + '17O' // 1f5e3 #456 + 'cF' // 1f5e4-1f5e7 #5 + '4M' // 1f5e8 #116 + 'eF' // 1f5e9-1f5ee #5 + '4M' // 1f5ef #116 + 'bF' // 1f5f0-1f5f2 #5 + '2E' // 1f5f3 #56 + 'eF' // 1f5f4-1f5f9 #5 + '9O' // 1f5fa #248 + 'd4X' // 1f5fb-1f5ff #127 + 'o3O' // 1f600-1f60f #92 + '52A' // 1f610 #1352 + '1v3O' // 1f611-1f641 #92 + '51Z' // 1f642 #1351 + 'a3O' // 1f643-1f644 #92 + 'b4S' // 1f645-1f647 #122 + 'b27N' // 1f648-1f64a #715 + '4S' // 1f64b #122 + '3O' // 1f64c #92 + 'a4S' // 1f64d-1f64e #122 + '3O' // 1f64f #92 + '1uF' // 1f650-1f67f #5 + '42M' // 1f680 #1104 + 'e4X' // 1f681-1f686 #127 + '9O' // 1f687 #248 + 'd4X' // 1f688-1f68c #127 + '9O' // 1f68d #248 + 'b4X' // 1f68e-1f690 #127 + '9O' // 1f691 #248 + '42M' // 1f692 #1104 + '4X' // 1f693 #127 + '9O' // 1f694 #248 + 'b4X' // 1f695-1f697 #127 + '9O' // 1f698 #248 + 'i4X' // 1f699-1f6a2 #127 + '4S' // 1f6a3 #122 + 'd4X' // 1f6a4-1f6a8 #127 + '15E' // 1f6a9 #394 + '2S' // 1f6aa #70 + '3S' // 1f6ab #96 + '2S' // 1f6ac #70 + '4M' // 1f6ad #116 + 'c3S' // 1f6ae-1f6b1 #96 + '9O' // 1f6b2 #248 + '3S' // 1f6b3 #96 + 'a4S' // 1f6b4-1f6b5 #122 + '32B' // 1f6b6 #833 + 'a3S' // 1f6b7-1f6b8 #96 + 'a4M' // 1f6b9-1f6ba #116 + '3S' // 1f6bb #96 + '4M' // 1f6bc #116 + '2S' // 1f6bd #70 + '3S' // 1f6be #96 + '2S' // 1f6bf #70 + '4S' // 1f6c0 #122 + '2S' // 1f6c1 #70 + 'c3S' // 1f6c2-1f6c5 #96 + 'dF' // 1f6c6-1f6ca #5 + '2E' // 1f6cb #56 + '4S' // 1f6cc #122 + 'b2E' // 1f6cd-1f6cf #56 + '3S' // 1f6d0 #96 + '51H' // 1f6d1 #1333 + '2S' // 1f6d2 #70 + 'aF' // 1f6d3-1f6d4 #5 + 'a9O' // 1f6d5-1f6d6 #248 + '4M' // 1f6d7 #116 + 'cA' // 1f6d8-1f6db + '3S' // 1f6dc #96 + 'b4X' // 1f6dd-1f6df #127 + 'a2E' // 1f6e0-1f6e1 #56 + 'c9O' // 1f6e2-1f6e5 #248 + 'bF' // 1f6e6-1f6e8 #5 + '9O' // 1f6e9 #248 + 'F' // 1f6ea #5 + 'a4X' // 1f6eb-1f6ec #127 + 'bA' // 1f6ed-1f6ef + '2E' // 1f6f0 #56 + 'aF' // 1f6f1-1f6f2 #5 + '9O' // 1f6f3 #248 + 'b4X' // 1f6f4-1f6f6 #127 + '6J' // 1f6f7 #165 + '9O' // 1f6f8 #248 + '6J' // 1f6f9 #165 + 'a9O' // 1f6fa-1f6fb #248 + '6J' // 1f6fc #165 + 'bA' // 1f6fd-1f6ff + '4k2M' // 1f700-1f773 #64 + 'bF' // 1f774-1f776 #5 + 'cA' // 1f777-1f77a + '3pF' // 1f77b-1f7d9 #5 + 'eA' // 1f7da-1f7df + 'h4M' // 1f7e0-1f7e8 #116 + '51I' // 1f7e9 #1334 + '4M' // 1f7ea #116 + '51I' // 1f7eb #1334 + 'cA' // 1f7ec-1f7ef + '3S' // 1f7f0 #96 + 'nA' // 1f7f1-1f7ff + 'kF' // 1f800-1f80b #5 + 'cA' // 1f80c-1f80f + '2cF' // 1f810-1f847 #5 + 'gA' // 1f848-1f84f + 'iF' // 1f850-1f859 #5 + 'eA' // 1f85a-1f85f + '1mF' // 1f860-1f887 #5 + 'gA' // 1f888-1f88f + '1cF' // 1f890-1f8ad #5 + 'aA' // 1f8ae-1f8af + 'aF' // 1f8b0-1f8b1 #5 + '2yA' // 1f8b2-1f8ff + 'kF' // 1f900-1f90b #5 + '3O' // 1f90c #92 + 'a27L' // 1f90d-1f90e #713 + 'm3O' // 1f90f-1f91c #92 + '51X' // 1f91d #1349 + 'g3O' // 1f91e-1f925 #92 + '4S' // 1f926 #122 + 'h3O' // 1f927-1f92f #92 + 'a4S' // 1f930-1f931 #122 + 'a3O' // 1f932-1f933 #92 + 'f4S' // 1f934-1f93a #122 + 'F' // 1f93b #5 + 'b4S' // 1f93c-1f93e #122 + '5P' // 1f93f #145 + '5F' // 1f940 #135 + '5P' // 1f941 #145 + 'b15F' // 1f942-1f944 #395 + '5P' // 1f945 #145 + 'F' // 1f946 #5 + 'h5P' // 1f947-1f94f #145 + '1e15F' // 1f950-1f96f #395 + 'f3O' // 1f970-1f976 #92 + '4S' // 1f977 #122 + 'b3O' // 1f978-1f97a #92 + 'd2S' // 1f97b-1f97f #70 + '1c5F' // 1f980-1f99d #135 + '51U' // 1f99e #1346 + '5F' // 1f99f #135 + '27N' // 1f9a0 #715 + 'h5F' // 1f9a1-1f9a9 #135 + '51U' // 1f9aa #1346 + 'c5F' // 1f9ab-1f9ae #135 + '75F' // 1f9af #1955 + 'c4S' // 1f9b0-1f9b3 #122 + 'c3O' // 1f9b4-1f9b7 #92 + 'a4S' // 1f9b8-1f9b9 #122 + '75D' // 1f9ba #1953 + '3O' // 1f9bb #92 + 'a75P' // 1f9bc-1f9bd #1965 + 'a3O' // 1f9be-1f9bf #92 + 'k15F' // 1f9c0-1f9cb #395 + 'a4S' // 1f9cc-1f9cd #122 + '32B' // 1f9ce #833 + '4S' // 1f9cf #122 + '3O' // 1f9d0 #92 + 'a32B' // 1f9d1-1f9d2 #833 + 'l4S' // 1f9d3-1f9df #122 + '3O' // 1f9e0 #92 + '27L' // 1f9e1 #713 + 'd2S' // 1f9e2-1f9e6 #70 + 'b5P' // 1f9e7-1f9e9 #145 + 'b2S' // 1f9ea-1f9ec #70 + '4X' // 1f9ed #127 + 'd2S' // 1f9ee-1f9f2 #70 + '75C' // 1f9f3 #1952 + '2S' // 1f9f4 #70 + 'a5P' // 1f9f5-1f9f6 #145 + 'h2S' // 1f9f7-1f9ff #70 + '3eF' // 1fa00-1fa53 #5 + 'kA' // 1fa54-1fa5f + 'mF' // 1fa60-1fa6d #5 + 'aA' // 1fa6e-1fa6f + '6J' // 1fa70 #165 + '51Q' // 1fa71 #1342 + 'b2E' // 1fa72-1fa74 #56 + 'b27L' // 1fa75-1fa77 #713 + '17O' // 1fa78 #456 + '75I' // 1fa79 #1958 + '2E' // 1fa7a #56 + '2S' // 1fa7b #70 + '4X' // 1fa7c #127 + 'bA' // 1fa7d-1fa7f + 'a6J' // 1fa80-1fa81 #165 + '27O' // 1fa82 #716 + 'b6J' // 1fa83-1fa85 #165 + '2E' // 1fa86 #56 + 'a5P' // 1fa87-1fa88 #145 + '24J' // 1fa89 #633 + 'dA' // 1fa8a-1fa8e + '24J' // 1fa8f #633 + '5Z' // 1fa90 #155 + 'b2E' // 1fa91-1fa93 #56 + 'a6J' // 1fa94-1fa95 #165 + '2E' // 1fa96 #56 + 'a6J' // 1fa97-1fa98 #165 + 'g2E' // 1fa99-1faa0 #56 + '6J' // 1faa1 #165 + 'e2E' // 1faa2-1faa7 #56 + '5Z' // 1faa8 #155 + '5P' // 1faa9 #145 + 'd2S' // 1faaa-1faae #70 + '3S' // 1faaf #96 + 'f5Z' // 1fab0-1fab6 #155 + 'f5F' // 1fab7-1fabd #135 + '24J' // 1fabe #633 + '5F' // 1fabf #135 + 'b17O' // 1fac0-1fac2 #456 + 'b4S' // 1fac3-1fac5 #122 + '24J' // 1fac6 #633 + 'fA' // 1fac7-1facd + 'a5F' // 1face-1facf #135 + 'f32A' // 1fad0-1fad6 #832 + 'd15F' // 1fad7-1fadb #395 + '24J' // 1fadc #633 + 'aA' // 1fadd-1fade + '24J' // 1fadf #633 + 'f3O' // 1fae0-1fae6 #92 + '5F' // 1fae7 #135 + '3O' // 1fae8 #92 + '24J' // 1fae9 #633 + 'eA' // 1faea-1faef + 'h3O' // 1faf0-1faf8 #92 + 'fA' // 1faf9-1faff + '5pF' // 1fb00-1fb92 #5 + 'A' // 1fb93 + '2bF' // 1fb94-1fbca #5 + '1jA' // 1fbcb-1fbef + 'iF' // 1fbf0-1fbf9 #5 + '39zA' // 1fbfa-2000a + 'C' // 2000b #2 + 'tA' // 2000c-20020 + '2A' // 20021 #52 + '1aA' // 20022-2003d + '2A' // 2003e #52 + 'fA' // 2003f-20045 + '2A' // 20046 #52 + 'fA' // 20047-2004d + '2A' // 2004e #52 + 'xA' // 2004f-20067 + '2A' // 20068 #52 + '1bA' // 20069-20085 + 'a2A' // 20086-20087 #52 + 'A' // 20088 + 'C' // 20089 #2 + '80P' // 2008a #2095 + 'hA' // 2008b-20093 + '2A' // 20094 #52 + 'lA' // 20095-200a1 + 'C' // 200a2 #2 + 'A' // 200a3 + 'C' // 200a4 #2 + 'jA' // 200a5-200af + 'C' // 200b0 #2 + 'xA' // 200b1-200c9 + 'c2A' // 200ca-200cd #52 + 'bA' // 200ce-200d0 + '2A' // 200d1 #52 + '1aA' // 200d2-200ed + '2A' // 200ee #52 + 'eA' // 200ef-200f4 + 'C' // 200f5 #2 + 'uA' // 200f6-2010b + '2A' // 2010c #52 + 'A' // 2010d + '2A' // 2010e #52 + 'hA' // 2010f-20117 + '2A' // 20118 #52 + '2jA' // 20119-20157 + 'C' // 20158 #2 + '2tA' // 20159-201a1 + 'C' // 201a2 #2 + 'A' // 201a3 + '2A' // 201a4 #52 + 'cA' // 201a5-201a8 + '2A' // 201a9 #52 + 'A' // 201aa + '2A' // 201ab #52 + 'tA' // 201ac-201c0 + '2A' // 201c1 #52 + 'qA' // 201c2-201d3 + '2A' // 201d4 #52 + '1bA' // 201d5-201f1 + '2A' // 201f2 #52 + 'pA' // 201f3-20203 + '2A' // 20204 #52 + 'fA' // 20205-2020b + '2A' // 2020c #52 + 'eA' // 2020d-20212 + 'C' // 20213 #2 + '2A' // 20214 #52 + '1iA' // 20215-20238 + '2A' // 20239 #52 + '1fA' // 2023a-2025a + '2A' // 2025b #52 + 'wA' // 2025c-20273 + 'a2A' // 20274-20275 #52 + '1hA' // 20276-20298 + '2A' // 20299 #52 + 'cA' // 2029a-2029d + '2A' // 2029e #52 + 'A' // 2029f + '2A' // 202a0 #52 + 'uA' // 202a1-202b6 + '2A' // 202b7 #52 + 'fA' // 202b8-202be + 'a2A' // 202bf-202c0 #52 + '1iA' // 202c1-202e4 + '2A' // 202e5 #52 + '1iA' // 202e6-20309 + '2A' // 2030a #52 + 'yA' // 2030b-20324 + '2A' // 20325 #52 + 'dA' // 20326-2032a + 'C' // 2032b #2 + 'tA' // 2032c-20340 + '2A' // 20341 #52 + 'bA' // 20342-20344 + 'b2A' // 20345-20347 #52 + '1nA' // 20348-20370 + 'C' // 20371 #2 + 'kA' // 20372-2037d + 'b2A' // 2037e-20380 #52 + 'C' // 20381 #2 + '1cA' // 20382-2039f + '2A' // 203a0 #52 + 'eA' // 203a1-203a6 + '2A' // 203a7 #52 + 'lA' // 203a8-203b4 + '2A' // 203b5 #52 + 'rA' // 203b6-203c8 + '2A' // 203c9 #52 + 'A' // 203ca + '2A' // 203cb #52 + '1nA' // 203cc-203f4 + '2A' // 203f5 #52 + 'bA' // 203f6-203f8 + 'C' // 203f9 #2 + 'aA' // 203fa-203fb + '2A' // 203fc #52 + 'uA' // 203fd-20412 + 'a2A' // 20413-20414 #52 + 'iA' // 20415-2041e + '2A' // 2041f #52 + '1oA' // 20420-20449 + 'C' // 2044a #2 + 'yA' // 2044b-20464 + '2A' // 20465 #52 + '1fA' // 20466-20486 + '80Q' // 20487 #2096 + 'eA' // 20488-2048d + 'O' // 2048e #14 + 'aA' // 2048f-20490 + 'aO' // 20491-20492 #14 + 'oA' // 20493-204a2 + 'O' // 204a3 #14 + '1xA' // 204a4-204d6 + 'O' // 204d7 #14 + '1iA' // 204d8-204fb + 'O' // 204fc #14 + 'A' // 204fd + 'O' // 204fe #14 + 'iA' // 204ff-20508 + 'C' // 20509 #2 + '1zA' // 2050a-2053e + 'C' // 2053f #2 + 'fA' // 20540-20546 + 'O' // 20547 #14 + '2qA' // 20548-2058d + 'O' // 2058e #14 + 'uA' // 2058f-205a4 + 'O' // 205a5 #14 + 'jA' // 205a6-205b0 + 'C' // 205b1 #2 + 'A' // 205b2 + 'O' // 205b3 #14 + 'nA' // 205b4-205c2 + 'O' // 205c3 #14 + 'eA' // 205c4-205c9 + 'O' // 205ca #14 + 'dA' // 205cb-205cf + 'O' // 205d0 #14 + 'cA' // 205d1-205d4 + 'O' // 205d5 #14 + 'C' // 205d6 #2 + 'gA' // 205d7-205de + 'aO' // 205df-205e0 #14 + 'iA' // 205e1-205ea + 'O' // 205eb #14 + '1jA' // 205ec-20610 + '42Z' // 20611 #1117 + 'bA' // 20612-20614 + 'O' // 20615 #14 + 'bA' // 20616-20618 + 'aO' // 20619-2061a #14 + 'lA' // 2061b-20627 + 'C' // 20628 #2 + 'fA' // 20629-2062f + 'O' // 20630 #14 + '1jA' // 20631-20655 + 'O' // 20656 #14 + '1dA' // 20657-20675 + 'O' // 20676 #14 + '4lA' // 20677-206eb + 'C' // 206ec #2 + '1fA' // 206ed-2070d + 'O' // 2070e #14 + '1gA' // 2070f-20730 + 'O' // 20731 #14 + '1bA' // 20732-2074e + 'C' // 2074f #2 + '1nA' // 20750-20778 + '27S' // 20779 #720 + '2yA' // 2077a-207c7 + 'C' // 207c8 #2 + '2iA' // 207c9-20806 + 'C' // 20807 #2 + '1iA' // 20808-2082b + 'O' // 2082c #14 + 'lA' // 2082d-20839 + 'C' // 2083a #2 + '2cA' // 2083b-20872 + 'O' // 20873 #14 + '2pA' // 20874-208b8 + 'C' // 208b9 #2 + 'zA' // 208ba-208d4 + 'O' // 208d5 #14 + '2cA' // 208d6-2090d + 'C' // 2090e #2 + 'fA' // 2090f-20915 + 'O' // 20916 #14 + 'kA' // 20917-20922 + 'O' // 20923 #14 + '1uA' // 20924-20953 + 'O' // 20954 #14 + '1iA' // 20955-20978 + 'O' // 20979 #14 + 'aA' // 2097a-2097b + 'C' // 2097c #2 + 'fA' // 2097d-20983 + 'C' // 20984 #2 + 'wA' // 20985-2099c + 'C' // 2099d #2 + '2tA' // 2099e-209e6 + 'O' // 209e7 #14 + '1nA' // 209e8-20a10 + 'O' // 20a11 #14 + '2iA' // 20a12-20a4f + 'O' // 20a50 #14 + 'rA' // 20a51-20a63 + 'C' // 20a64 #2 + 'iA' // 20a65-20a6e + 'O' // 20a6f #14 + 'yA' // 20a70-20a89 + 'O' // 20a8a #14 + '1nA' // 20a8b-20ab3 + 'O' // 20ab4 #14 + 'lA' // 20ab5-20ac1 + 'O' // 20ac2 #14 + 'iA' // 20ac3-20acc + 'O' // 20acd #14 + 'dA' // 20ace-20ad2 + 'C' // 20ad3 #2 + '2dA' // 20ad4-20b0c + 'O' // 20b0d #14 + 'nA' // 20b0e-20b1c + 'C' // 20b1d #2 + '4hA' // 20b1e-20b8e + 'O' // 20b8f #14 + 'nA' // 20b90-20b9e + '42Z' // 20b9f #1117 + 'gA' // 20ba0-20ba7 + 'aO' // 20ba8-20ba9 #14 + 'lA' // 20baa-20bb6 + 'C' // 20bb7 #2 + 'fA' // 20bb8-20bbe + 'O' // 20bbf #14 + 'eA' // 20bc0-20bc5 + 'O' // 20bc6 #14 + 'cA' // 20bc7-20bca + 'O' // 20bcb #14 + 'uA' // 20bcc-20be1 + 'O' // 20be2 #14 + 'gA' // 20be3-20bea + 'O' // 20beb #14 + 'nA' // 20bec-20bfa + 'O' // 20bfb #14 + 'bA' // 20bfc-20bfe + 'O' // 20bff #14 + 'jA' // 20c00-20c0a + 'O' // 20c0b #14 + 'A' // 20c0c + 'O' // 20c0d #14 + 'qA' // 20c0e-20c1f + 'O' // 20c20 #14 + 'rA' // 20c21-20c33 + 'O' // 20c34 #14 + 'dA' // 20c35-20c39 + 'aO' // 20c3a-20c3b #14 + 'dA' // 20c3c-20c40 + '27S' // 20c41 #720 + 'aO' // 20c42-20c43 #14 + 'nA' // 20c44-20c52 + 'O' // 20c53 #14 + 'pA' // 20c54-20c64 + 'O' // 20c65 #14 + 'pA' // 20c66-20c76 + 'O' // 20c77 #14 + '27S' // 20c78 #720 + 'bA' // 20c79-20c7b + 'O' // 20c7c #14 + 'oA' // 20c7d-20c8c + 'O' // 20c8d #14 + 'gA' // 20c8e-20c95 + 'O' // 20c96 #14 + 'dA' // 20c97-20c9b + 'O' // 20c9c #14 + 'wA' // 20c9d-20cb4 + 'O' // 20cb5 #14 + 'aA' // 20cb6-20cb7 + 'O' // 20cb8 #14 + 'uA' // 20cb9-20cce + 'O' // 20ccf #14 + 'bA' // 20cd0-20cd2 + 'cO' // 20cd3-20cd6 #14 + 'eA' // 20cd7-20cdc + 'O' // 20cdd #14 + 'nA' // 20cde-20cec + 'O' // 20ced #14 + 'pA' // 20cee-20cfe + 'O' // 20cff #14 + 'tA' // 20d00-20d14 + 'O' // 20d15 #14 + 'qA' // 20d16-20d27 + 'O' // 20d28 #14 + 'gA' // 20d29-20d30 + 'aO' // 20d31-20d32 #14 + 'qA' // 20d33-20d44 + 'C' // 20d45 #2 + 'cO' // 20d46-20d49 #14 + 'aA' // 20d4a-20d4b + 'bO' // 20d4c-20d4e #14 + 'hA' // 20d4f-20d57 + 'C' // 20d58 #2 + 'uA' // 20d59-20d6e + 'O' // 20d6f #14 + 'A' // 20d70 + '27S' // 20d71 #720 + 'aA' // 20d72-20d73 + 'O' // 20d74 #14 + 'fA' // 20d75-20d7b + 'O' // 20d7c #14 + 'A' // 20d7d + 'aO' // 20d7e-20d7f #14 + 'uA' // 20d80-20d95 + 'O' // 20d96 #14 + 'dA' // 20d97-20d9b + 'O' // 20d9c #14 + 'iA' // 20d9d-20da6 + 'O' // 20da7 #14 + 'iA' // 20da8-20db1 + 'O' // 20db2 #14 + 'tA' // 20db3-20dc7 + 'O' // 20dc8 #14 + 'wA' // 20dc9-20de0 + 'C' // 20de1 #2 + '1gA' // 20de2-20e03 + 'O' // 20e04 #14 + 'cA' // 20e05-20e08 + 'aO' // 20e09-20e0a #14 + 'aA' // 20e0b-20e0c + 'dO' // 20e0d-20e11 #14 + 'cA' // 20e12-20e15 + 'O' // 20e16 #14 + 'eA' // 20e17-20e1c + 'O' // 20e1d #14 + '1sA' // 20e1e-20e4b + 'O' // 20e4c #14 + 'vA' // 20e4d-20e63 + 'C' // 20e64 #2 + 'gA' // 20e65-20e6c + '42Z' // 20e6d #1117 + 'dA' // 20e6e-20e72 + 'O' // 20e73 #14 + 'A' // 20e74 + 'fO' // 20e75-20e7b #14 + 'oA' // 20e7c-20e8b + 'O' // 20e8c #14 + 'gA' // 20e8d-20e94 + 'C' // 20e95 #2 + 'O' // 20e96 #14 + 'A' // 20e97 + '27S' // 20e98 #720 + 'cA' // 20e99-20e9c + 'O' // 20e9d #14 + 'cA' // 20e9e-20ea1 + 'O' // 20ea2 #14 + 'fA' // 20ea3-20ea9 + 'bO' // 20eaa-20eac #14 + 'hA' // 20ead-20eb5 + 'O' // 20eb6 #14 + '1eA' // 20eb7-20ed6 + 'aO' // 20ed7-20ed8 #14 + 'cA' // 20ed9-20edc + 'O' // 20edd #14 + 'yA' // 20ede-20ef7 + 'O' // 20ef8 #14 + '27S' // 20ef9 #720 + 'aO' // 20efa-20efb #14 + '1fA' // 20efc-20f1c + 'O' // 20f1d #14 + 'gA' // 20f1e-20f25 + 'O' // 20f26 #14 + 'eA' // 20f27-20f2c + 'aO' // 20f2d-20f2e #14 + 'A' // 20f2f + 'aO' // 20f30-20f31 #14 + 'hA' // 20f32-20f3a + 'O' // 20f3b #14 + 'oA' // 20f3c-20f4b + 'O' // 20f4c #14 + 'qA' // 20f4d-20f5e + 'C' // 20f5f #2 + 'cA' // 20f60-20f63 + 'O' // 20f64 #14 + '1mA' // 20f65-20f8c + 'O' // 20f8d #14 + 'aA' // 20f8e-20f8f + 'O' // 20f90 #14 + '1aA' // 20f91-20fac + 'O' // 20fad #14 + 'eA' // 20fae-20fb3 + 'bO' // 20fb4-20fb6 #14 + 'dA' // 20fb7-20fbb + 'O' // 20fbc #14 + '1gA' // 20fbd-20fde + 'O' // 20fdf #14 + 'iA' // 20fe0-20fe9 + 'cO' // 20fea-20fed #14 + '1kA' // 20fee-21013 + 'O' // 21014 #14 + 'gA' // 21015-2101c + 'aO' // 2101d-2101e #14 + '1uA' // 2101f-2104e + 'O' // 2104f #14 + 'kA' // 21050-2105b + 'O' // 2105c #14 + 'qA' // 2105d-2106e + 'O' // 2106f #14 + 'dA' // 21070-21074 + 'O' // 21075 #14 + 'bS' // 21076-21078 #18 + 'aA' // 21079-2107a + '52T' // 2107b #1371 + 'kA' // 2107c-21087 + 'S' // 21088 #18 + 'lA' // 21089-21095 + 'S' // 21096 #18 + 'eA' // 21097-2109c + 'S' // 2109d #18 + 'uA' // 2109e-210b3 + 'S' // 210b4 #18 + 'iA' // 210b5-210be + 'aS' // 210bf-210c0 #18 + '52T' // 210c1 #1371 + 'dA' // 210c2-210c6 + 'bS' // 210c7-210c9 #18 + 'dA' // 210ca-210ce + 'S' // 210cf #18 + 'bA' // 210d0-210d2 + 'S' // 210d3 #18 + 'oA' // 210d4-210e3 + 'S' // 210e4 #18 + 'nA' // 210e5-210f3 + 'bS' // 210f4-210f6 #18 + '2cA' // 210f7-2112e + 'S' // 2112f #18 + 'jA' // 21130-2113a + 'S' // 2113b #18 + 'A' // 2113c + 'S' // 2113d #18 + 'fA' // 2113e-21144 + 'S' // 21145 #18 + 'aA' // 21146-21147 + 'S' // 21148 #18 + 'eA' // 21149-2114e + 'S' // 2114f #18 + '1uA' // 21150-2117f + 'S' // 21180 #18 + 'eA' // 21181-21186 + 'S' // 21187 #18 + '3bA' // 21188-211d8 + 'S' // 211d9 #18 + '1lA' // 211da-21200 + 'C' // 21201 #2 + '2eA' // 21202-2123b + 'S' // 2123c #18 + 'C' // 2123d #2 + 'pA' // 2123e-2124e + 'S' // 2124f #18 + 'dA' // 21250-21254 + 'C' // 21255 #2 + '1cA' // 21256-21273 + 'C' // 21274 #2 + 'eA' // 21275-2127a + 'C' // 2127b #2 + 'S' // 2127c #18 + '1pA' // 2127d-212a7 + 'aS' // 212a8-212a9 #18 + 'eA' // 212aa-212af + 'S' // 212b0 #18 + '1kA' // 212b1-212d6 + 'C' // 212d7 #2 + 'jA' // 212d8-212e2 + 'S' // 212e3 #18 + 'C' // 212e4 #2 + 'wA' // 212e5-212fc + 'C' // 212fd #2 + 'S' // 212fe #18 + 'bA' // 212ff-21301 + 'cS' // 21302-21305 #18 + 'tA' // 21306-2131a + 'C' // 2131b #2 + 'yA' // 2131c-21335 + '80O' // 21336 #2094 + 'bA' // 21337-21339 + 'S' // 2133a #18 + 'hA' // 2133b-21343 + 'C' // 21344 #2 + '1uA' // 21345-21374 + 'aS' // 21375-21376 #18 + 'vA' // 21377-2138d + 'S' // 2138e #18 + 'hA' // 2138f-21397 + 'S' // 21398 #18 + 'bA' // 21399-2139b + 'S' // 2139c #18 + '1lA' // 2139d-213c3 + 'C' // 213c4 #2 + 'aS' // 213c5-213c6 #18 + '1kA' // 213c7-213ec + 'S' // 213ed #18 + 'oA' // 213ee-213fd + 'S' // 213fe #18 + 'sA' // 213ff-21412 + 'S' // 21413 #18 + 'aA' // 21414-21415 + 'S' // 21416 #18 + 'lA' // 21417-21423 + 'S' // 21424 #18 + 'yA' // 21425-2143e + 'S' // 2143f #18 + 'qA' // 21440-21451 + 'S' // 21452 #18 + 'A' // 21453 + 'aS' // 21454-21455 #18 + 'vA' // 21456-2146c + 'aC' // 2146d-2146e #2 + 'zA' // 2146f-21489 + 'S' // 2148a #18 + 'kA' // 2148b-21496 + 'S' // 21497 #18 + '1cA' // 21498-214b5 + 'S' // 214b6 #18 + '1vA' // 214b7-214e7 + 'S' // 214e8 #18 + 'sA' // 214e9-214fc + 'S' // 214fd #18 + '4pA' // 214fe-21576 + 'S' // 21577 #18 + 'iA' // 21578-21581 + 'S' // 21582 #18 + 'rA' // 21583-21595 + 'S' // 21596 #18 + '2kA' // 21597-215d6 + 'C' // 215d7 #2 + '1wA' // 215d8-21609 + 'S' // 2160a #18 + 'gA' // 2160b-21612 + 'S' // 21613 #18 + 'dA' // 21614-21618 + 'S' // 21619 #18 + '1iA' // 2161a-2163d + 'S' // 2163e #18 + 'gA' // 2163f-21646 + 'C' // 21647 #2 + 'xA' // 21648-21660 + 'S' // 21661 #18 + '1uA' // 21662-21691 + 'S' // 21692 #18 + '1fA' // 21693-216b3 + 'C' // 216b4 #2 + 'bA' // 216b5-216b7 + 'S' // 216b8 #18 + 'A' // 216b9 + 'S' // 216ba #18 + 'dA' // 216bb-216bf + 'bS' // 216c0-216c2 #18 + 'oA' // 216c3-216d2 + 'S' // 216d3 #18 + 'A' // 216d4 + 'S' // 216d5 #18 + 'hA' // 216d6-216de + 'S' // 216df #18 + 'eA' // 216e0-216e5 + 'bS' // 216e6-216e8 #18 + 'pA' // 216e9-216f9 + 'bS' // 216fa-216fc #18 + 'A' // 216fd + 'S' // 216fe #18 + 'fA' // 216ff-21705 + 'C' // 21706 #2 + 'eA' // 21707-2170c + 'S' // 2170d #18 + 'aA' // 2170e-2170f + 'S' // 21710 #18 + 'tA' // 21711-21725 + 'S' // 21726 #18 + 'rA' // 21727-21739 + 'bS' // 2173a-2173c #18 + 'dA' // 2173d-21741 + 'C' // 21742 #2 + 'sA' // 21743-21756 + 'S' // 21757 #18 + 'sA' // 21758-2176b + 'eS' // 2176c-21771 #18 + 'A' // 21772 + 'aS' // 21773-21774 #18 + '2aA' // 21775-217aa + 'S' // 217ab #18 + 'cA' // 217ac-217af + 'eS' // 217b0-217b5 #18 + 'lA' // 217b6-217c2 + 'S' // 217c3 #18 + 'bA' // 217c4-217c6 + 'S' // 217c7 #18 + 'pA' // 217c8-217d8 + 'cS' // 217d9-217dc #18 + 'aA' // 217dd-217de + 'S' // 217df #18 + 'nA' // 217e0-217ee + 'S' // 217ef #18 + 'dA' // 217f0-217f4 + 'aS' // 217f5-217f6 #18 + 'A' // 217f7 + 'dS' // 217f8-217fc #18 + '1hA' // 217fd-2181f + 'S' // 21820 #18 + 'fA' // 21821-21827 + 'bS' // 21828-2182a #18 + 'aA' // 2182b-2182c + 'S' // 2182d #18 + 'jA' // 2182e-21838 + 'bS' // 21839-2183b #18 + 'cA' // 2183c-2183f + 'S' // 21840 #18 + 'cA' // 21841-21844 + 'S' // 21845 #18 + 'kA' // 21846-21851 + 'S' // 21852 #18 + 'jA' // 21853-2185d + 'S' // 2185e #18 + 'aA' // 2185f-21860 + 'cS' // 21861-21864 #18 + 'qA' // 21865-21876 + 'S' // 21877 #18 + 'bA' // 21878-2187a + 'S' // 2187b #18 + 'fA' // 2187c-21882 + 'bS' // 21883-21885 #18 + 'wA' // 21886-2189d + 'dS' // 2189e-218a2 #18 + 'yA' // 218a3-218bc + 'C' // 218bd #2 + 'aS' // 218be-218bf #18 + 'pA' // 218c0-218d0 + 'S' // 218d1 #18 + 'cA' // 218d2-218d5 + 'cS' // 218d6-218d9 #18 + '1eA' // 218da-218f9 + 'S' // 218fa #18 + 'gA' // 218fb-21902 + 'bS' // 21903-21905 #18 + 'iA' // 21906-2190f + 'bS' // 21910-21912 #18 + 'aA' // 21913-21914 + 'S' // 21915 #18 + 'eA' // 21916-2191b + 'S' // 2191c #18 + 'dA' // 2191d-21921 + 'S' // 21922 #18 + 'cA' // 21923-21926 + 'D' // 21927 #3 + 'rA' // 21928-2193a + 'D' // 2193b #3 + 'gA' // 2193c-21943 + 'D' // 21944 #3 + 'rA' // 21945-21957 + 'D' // 21958 #3 + 'pA' // 21959-21969 + 'D' // 2196a #3 + 'pA' // 2196b-2197b + 'D' // 2197c #3 + 'bA' // 2197d-2197f + 'D' // 21980 #3 + 'aA' // 21981-21982 + 'D' // 21983 #3 + 'cA' // 21984-21987 + 'D' // 21988 #3 + 'lA' // 21989-21995 + 'D' // 21996 #3 + '1qA' // 21997-219c2 + 'C' // 219c3 #2 + 'vA' // 219c4-219da + 'D' // 219db #3 + 'vA' // 219dc-219f2 + 'D' // 219f3 #3 + '1kA' // 219f4-21a19 + 'C' // 21a1a #2 + 'qA' // 21a1b-21a2c + 'D' // 21a2d #3 + 'eA' // 21a2e-21a33 + 'D' // 21a34 #3 + 'oA' // 21a35-21a44 + 'D' // 21a45 #3 + 'dA' // 21a46-21a4a + 'D' // 21a4b #3 + 'vA' // 21a4c-21a62 + 'D' // 21a63 #3 + '8oA' // 21a64-21b43 + 'D' // 21b44 #3 + '4sA' // 21b45-21bc0 + 'aD' // 21bc1-21bc2 #3 + '3xA' // 21bc3-21c29 + 'D' // 21c2a #3 + '1pA' // 21c2b-21c55 + 'C' // 21c56 #2 + 'xA' // 21c57-21c6f + 'D' // 21c70 #3 + '1vA' // 21c71-21ca1 + 'D' // 21ca2 #3 + 'aA' // 21ca3-21ca4 + 'D' // 21ca5 #3 + 'eA' // 21ca6-21cab + 'D' // 21cac #3 + '4wA' // 21cad-21d2c + 'C' // 21d2d #2 + 'vA' // 21d2e-21d44 + 'C' // 21d45 #2 + 'D' // 21d46 #3 + 'kA' // 21d47-21d52 + 'D' // 21d53 #3 + 'iA' // 21d54-21d5d + 'D' // 21d5e #3 + 'bA' // 21d5f-21d61 + 'C' // 21d62 #2 + 'tA' // 21d63-21d77 + 'C' // 21d78 #2 + 'vA' // 21d79-21d8f + 'D' // 21d90 #3 + 'A' // 21d91 + 'C' // 21d92 #2 + 'hA' // 21d93-21d9b + 'C' // 21d9c #2 + 'cA' // 21d9d-21da0 + 'C' // 21da1 #2 + 'sA' // 21da2-21db5 + 'D' // 21db6 #3 + 'C' // 21db7 #2 + 'aA' // 21db8-21db9 + 'D' // 21dba #3 + 'nA' // 21dbb-21dc9 + 'D' // 21dca #3 + 'eA' // 21dcb-21dd0 + 'D' // 21dd1 #3 + 'mA' // 21dd2-21ddf + 'C' // 21de0 #2 + 'iA' // 21de1-21dea + 'D' // 21deb #3 + 'lA' // 21dec-21df8 + 'D' // 21df9 #3 + '1gA' // 21dfa-21e1b + 'D' // 21e1c #3 + 'eA' // 21e1d-21e22 + 'D' // 21e23 #3 + 'nA' // 21e24-21e32 + 'aC' // 21e33-21e34 #2 + 'aA' // 21e35-21e36 + 'D' // 21e37 #3 + 'dA' // 21e38-21e3c + 'D' // 21e3d #3 + '2vA' // 21e3e-21e88 + 'D' // 21e89 #3 + 'yA' // 21e8a-21ea3 + 'D' // 21ea4 #3 + 'bA' // 21ea5-21ea7 + 'D' // 21ea8 #3 + '1dA' // 21ea9-21ec7 + 'D' // 21ec8 #3 + 'kA' // 21ec9-21ed4 + 'D' // 21ed5 #3 + '2dA' // 21ed6-21f0e + 'D' // 21f0f #3 + 'dA' // 21f10-21f14 + 'D' // 21f15 #3 + 'gA' // 21f16-21f1d + 'C' // 21f1e #2 + '2vA' // 21f1f-21f69 + 'D' // 21f6a #3 + 'jA' // 21f6b-21f75 + 'C' // 21f76 #2 + '1lA' // 21f77-21f9d + 'D' // 21f9e #3 + 'aA' // 21f9f-21fa0 + 'D' // 21fa1 #3 + '2qA' // 21fa2-21fe7 + 'D' // 21fe8 #3 + 'pA' // 21fe9-21ff9 + 'C' // 21ffa #2 + '2uA' // 21ffb-22044 + 'D' // 22045 #3 + 'bA' // 22046-22048 + 'D' // 22049 #3 + '1yA' // 2204a-2207d + 'D' // 2207e #3 + 'zA' // 2207f-22099 + 'D' // 2209a #3 + '1qA' // 2209b-220c6 + 'D' // 220c7 #3 + '1yA' // 220c8-220fb + 'D' // 220fc #3 + '1rA' // 220fd-22129 + 'D' // 2212a #3 + '1uA' // 2212b-2215a + 'D' // 2215b #3 + 'vA' // 2215c-22172 + 'D' // 22173 #3 + 'eA' // 22174-22179 + 'D' // 2217a #3 + 'C' // 2217b #2 + '1jA' // 2217c-221a0 + 'D' // 221a1 #3 + '1dA' // 221a2-221c0 + 'D' // 221c1 #3 + 'A' // 221c2 + 'D' // 221c3 #3 + '2oA' // 221c4-22207 + 'D' // 22208 #3 + 'nA' // 22209-22217 + 'C' // 22218 #2 + '3tA' // 22219-2227b + 'D' // 2227c #3 + '6dA' // 2227d-2231d + 'C' // 2231e #2 + 'aA' // 2231f-22320 + 'D' // 22321 #3 + 'bA' // 22322-22324 + 'D' // 22325 #3 + '5dA' // 22326-223ac + 'C' // 223ad #2 + 'nA' // 223ae-223bc + 'D' // 223bd #3 + 'qA' // 223be-223cf + 'D' // 223d0 #3 + 'eA' // 223d1-223d6 + 'D' // 223d7 #3 + '1gA' // 223d8-223f9 + 'D' // 223fa #3 + '4aA' // 223fb-22464 + 'D' // 22465 #3 + 'jA' // 22466-22470 + 'D' // 22471 #3 + 'xA' // 22472-2248a + 'D' // 2248b #3 + 'dA' // 2248c-22490 + 'D' // 22491 #3 + '1cA' // 22492-224af + 'D' // 224b0 #3 + 'jA' // 224b1-224bb + 'D' // 224bc #3 + 'cA' // 224bd-224c0 + 'D' // 224c1 #3 + 'fA' // 224c2-224c8 + 'D' // 224c9 #3 + 'aA' // 224ca-224cb + 'D' // 224cc #3 + '1eA' // 224cd-224ec + 'D' // 224ed #3 + '1jA' // 224ee-22512 + 'D' // 22513 #3 + 'fA' // 22514-2251a + 'D' // 2251b #3 + 'sA' // 2251c-2252f + 'D' // 22530 #3 + '1hA' // 22531-22553 + 'D' // 22554 #3 + '2cA' // 22555-2258c + 'D' // 2258d #3 + '1fA' // 2258e-225ae + 'D' // 225af #3 + 'mA' // 225b0-225bd + 'D' // 225be #3 + '2uA' // 225bf-22608 + 'C' // 22609 #2 + 'pA' // 2260a-2261a + 'aD' // 2261b-2261c #3 + 'mA' // 2261d-2262a + 'D' // 2262b #3 + '2gA' // 2262c-22667 + 'D' // 22668 #3 + 'pA' // 22669-22679 + 'D' // 2267a #3 + 'zA' // 2267b-22695 + 'D' // 22696 #3 + 'A' // 22697 + 'D' // 22698 #3 + '3kA' // 22699-226f2 + 'C' // 226f3 #2 + 'bD' // 226f4-226f6 #3 + 'zA' // 226f7-22711 + 'D' // 22712 #3 + 'A' // 22713 + 'D' // 22714 #3 + 'eA' // 22715-2271a + 'D' // 2271b #3 + 'bA' // 2271c-2271e + 'D' // 2271f #3 + 'iA' // 22720-22729 + 'D' // 2272a #3 + '2uA' // 2272b-22774 + 'D' // 22775 #3 + 'jA' // 22776-22780 + 'D' // 22781 #3 + 'sA' // 22782-22795 + 'D' // 22796 #3 + '1bA' // 22797-227b3 + 'aD' // 227b4-227b5 #3 + 'vA' // 227b6-227cc + 'D' // 227cd #3 + '1zA' // 227ce-22802 + 'D' // 22803 #3 + '3hA' // 22804-2285a + 'C' // 2285b #2 + 'bA' // 2285c-2285e + 'aD' // 2285f-22860 #3 + 'oA' // 22861-22870 + 'D' // 22871 #3 + '2dA' // 22872-228aa + 'C' // 228ab #2 + 'A' // 228ac + 'D' // 228ad #3 + 'rA' // 228ae-228c0 + 'D' // 228c1 #3 + '1zA' // 228c2-228f6 + 'D' // 228f7 #3 + '1sA' // 228f8-22925 + 'D' // 22926 #3 + 'qA' // 22927-22938 + 'D' // 22939 #3 + 'tA' // 2293a-2294e + 'D' // 2294f #3 + 'vA' // 22950-22966 + 'D' // 22967 #3 + 'bA' // 22968-2296a + 'D' // 2296b #3 + 'sA' // 2296c-2297f + 'D' // 22980 #3 + 'mA' // 22981-2298e + 'C' // 2298f #2 + 'bA' // 22990-22992 + 'D' // 22993 #3 + '8aA' // 22994-22a65 + 'D' // 22a66 #3 + '3bA' // 22a67-22ab7 + 'C' // 22ab8 #2 + 'uA' // 22ab9-22ace + 'D' // 22acf #3 + 'dA' // 22ad0-22ad4 + 'D' // 22ad5 #3 + 'oA' // 22ad6-22ae5 + 'D' // 22ae6 #3 + 'A' // 22ae7 + 'D' // 22ae8 #3 + '1jA' // 22ae9-22b0d + 'D' // 22b0e #3 + 'rA' // 22b0f-22b21 + 'D' // 22b22 #3 + '1aA' // 22b23-22b3e + 'D' // 22b3f #3 + 'bA' // 22b40-22b42 + 'D' // 22b43 #3 + 'aA' // 22b44-22b45 + 'C' // 22b46 #2 + 'gA' // 22b47-22b4e + 'aC' // 22b4f-22b50 #2 + 'xA' // 22b51-22b69 + 'D' // 22b6a #3 + '2fA' // 22b6b-22ba5 + 'C' // 22ba6 #2 + '1hA' // 22ba7-22bc9 + 'D' // 22bca #3 + 'bA' // 22bcb-22bcd + 'D' // 22bce #3 + '2yA' // 22bcf-22c1c + 'C' // 22c1d #2 + 'eA' // 22c1e-22c23 + 'C' // 22c24 #2 + 'A' // 22c25 + 'aD' // 22c26-22c27 #3 + 'oA' // 22c28-22c37 + 'D' // 22c38 #3 + 'rA' // 22c39-22c4b + 'D' // 22c4c #3 + 'cA' // 22c4d-22c50 + '80N' // 22c51 #2093 + 'bA' // 22c52-22c54 + 'D' // 22c55 #3 + 'kA' // 22c56-22c61 + 'D' // 22c62 #3 + '1jA' // 22c63-22c87 + 'D' // 22c88 #3 + 'qA' // 22c89-22c9a + 'D' // 22c9b #3 + 'dA' // 22c9c-22ca0 + 'D' // 22ca1 #3 + 'fA' // 22ca2-22ca8 + 'D' // 22ca9 #3 + 'gA' // 22caa-22cb1 + 'D' // 22cb2 #3 + 'cA' // 22cb3-22cb6 + 'D' // 22cb7 #3 + 'iA' // 22cb8-22cc1 + 'D' // 22cc2 #3 + 'bA' // 22cc3-22cc5 + 'D' // 22cc6 #3 + 'aA' // 22cc7-22cc8 + 'D' // 22cc9 #3 + '2hA' // 22cca-22d06 + 'aD' // 22d07-22d08 #3 + 'hA' // 22d09-22d11 + 'D' // 22d12 #3 + '1vA' // 22d13-22d43 + 'D' // 22d44 #3 + 'fA' // 22d45-22d4b + 'D' // 22d4c #3 + 'yA' // 22d4d-22d66 + 'D' // 22d67 #3 + '1jA' // 22d68-22d8c + 'D' // 22d8d #3 + 'fA' // 22d8e-22d94 + 'D' // 22d95 #3 + 'iA' // 22d96-22d9f + 'D' // 22da0 #3 + 'aA' // 22da1-22da2 + 'aD' // 22da3-22da4 #3 + 'qA' // 22da5-22db6 + 'D' // 22db7 #3 + '1nA' // 22db8-22de0 + 'C' // 22de1 #2 + 'kA' // 22de2-22ded + 'D' // 22dee #3 + '1cA' // 22def-22e0c + 'D' // 22e0d #3 + '1mA' // 22e0e-22e35 + 'D' // 22e36 #3 + 'jA' // 22e37-22e41 + '80M' // 22e42 #2092 + '1zA' // 22e43-22e77 + 'D' // 22e78 #3 + 'qA' // 22e79-22e8a + 'Q' // 22e8b #16 + '1lA' // 22e8c-22eb2 + 'Q' // 22eb3 #16 + '2fA' // 22eb4-22eee + 'Q' // 22eef #16 + '5aA' // 22ef0-22f73 + 'Q' // 22f74 #16 + '3hA' // 22f75-22fcb + 'Q' // 22fcc #16 + 'uA' // 22fcd-22fe2 + 'Q' // 22fe3 #16 + 'fA' // 22fe4-22fea + 'C' // 22feb #2 + '2rA' // 22fec-23032 + 'Q' // 23033 #16 + 'oA' // 23034-23043 + 'Q' // 23044 #16 + 'eA' // 23045-2304a + 'Q' // 2304b #16 + 'yA' // 2304c-23065 + 'Q' // 23066 #16 + 'uA' // 23067-2307c + 'aQ' // 2307d-2307e #16 + 'nA' // 2307f-2308d + 'Q' // 2308e #16 + '1mA' // 2308f-230b6 + 'Q' // 230b7 #16 + 'cA' // 230b8-230bb + 'Q' // 230bc #16 + '1bA' // 230bd-230d9 + 'Q' // 230da #16 + '1mA' // 230db-23102 + 'Q' // 23103 #16 + '2dA' // 23104-2313c + 'Q' // 2313d #16 + '2jA' // 2313e-2317c + 'Q' // 2317d #16 + 'cA' // 2317e-23181 + 'Q' // 23182 #16 + '1fA' // 23183-231a3 + 'aQ' // 231a4-231a5 #16 + 'lA' // 231a6-231b2 + 'Q' // 231b3 #16 + 'aA' // 231b4-231b5 + 'C' // 231b6 #2 + 'kA' // 231b7-231c2 + 'aC' // 231c3-231c4 #2 + 'bA' // 231c5-231c7 + 'aQ' // 231c8-231c9 #16 + '1eA' // 231ca-231e9 + 'Q' // 231ea #16 + 'iA' // 231eb-231f4 + 'C' // 231f5 #2 + 'A' // 231f6 + 'bQ' // 231f7-231f9 #16 + 'tA' // 231fa-2320e + 'Q' // 2320f #16 + 'tA' // 23210-23224 + 'Q' // 23225 #16 + 'hA' // 23226-2322e + 'Q' // 2322f #16 + 'A' // 23230 + 'cQ' // 23231-23234 #16 + '1fA' // 23235-23255 + 'Q' // 23256 #16 + 'fA' // 23257-2325d + 'Q' // 2325e #16 + 'bA' // 2325f-23261 + 'Q' // 23262 #16 + '1cA' // 23263-23280 + 'Q' // 23281 #16 + 'fA' // 23282-23288 + 'aQ' // 23289-2328a #16 + '1eA' // 2328b-232aa + 'bQ' // 232ab-232ad #16 + '1iA' // 232ae-232d1 + 'Q' // 232d2 #16 + 'lA' // 232d3-232df + 'aQ' // 232e0-232e1 #16 + '1cA' // 232e2-232ff + 'Q' // 23300 #16 + 'hA' // 23301-23309 + 'Q' // 2330a #16 + 'sA' // 2330b-2331e + 'Q' // 2331f #16 + '3cA' // 23320-23371 + 'C' // 23372 #2 + '2lA' // 23373-233b3 + '80L' // 233b4 #2091 + 'vA' // 233b5-233cb + '27R' // 233cc #719 + 'bA' // 233cd-233cf + 'C' // 233d0 #2 + 'A' // 233d1 + 'aC' // 233d2-233d3 #2 + 'A' // 233d4 + 'C' // 233d5 #2 + 'cA' // 233d6-233d9 + 'C' // 233da #2 + 'bA' // 233db-233dd + 'Q' // 233de #16 + 'C' // 233df #2 + 'cA' // 233e0-233e3 + 'C' // 233e4 #2 + 'A' // 233e5 + 'Q' // 233e6 #16 + 'lA' // 233e7-233f3 + 'aQ' // 233f4-233f5 #16 + 'bA' // 233f6-233f8 + 'aQ' // 233f9-233fa #16 + 'bA' // 233fb-233fd + '27R' // 233fe #719 + 'A' // 233ff + 'Q' // 23400 #16 + '2iA' // 23401-2343e + 'Q' // 2343f #16 + 'iA' // 23440-23449 + 'aC' // 2344a-2344b #2 + 'cA' // 2344c-2344f + 'Q' // 23450 #16 + 'C' // 23451 #2 + 'rA' // 23452-23464 + 'C' // 23465 #2 + 'hA' // 23466-2346e + 'Q' // 2346f #16 + 'aA' // 23470-23471 + 'Q' // 23472 #16 + '4hA' // 23473-234e3 + 'C' // 234e4 #2 + 'Q' // 234e5 #16 + '1xA' // 234e6-23518 + 'Q' // 23519 #16 + 'uA' // 2351a-2352f + 'Q' // 23530 #16 + '1eA' // 23531-23550 + 'Q' // 23551 #16 + 'gA' // 23552-23559 + '27R' // 2355a #719 + 'kA' // 2355b-23566 + 'Q' // 23567 #16 + '1qA' // 23568-23593 + 'C' // 23594 #2 + 'Q' // 23595 #16 + 'bA' // 23596-23598 + 'Q' // 23599 #16 + 'aA' // 2359a-2359b + 'Q' // 2359c #16 + '1cA' // 2359d-235ba + 'Q' // 235bb #16 + 'gA' // 235bc-235c3 + 'C' // 235c4 #2 + 'gA' // 235c5-235cc + 'bQ' // 235cd-235cf #16 + '1hA' // 235d0-235f2 + 'Q' // 235f3 #16 + 'kA' // 235f4-235ff + 'Q' // 23600 #16 + 'uA' // 23601-23616 + 'Q' // 23617 #16 + 'aA' // 23618-23619 + 'Q' // 2361a #16 + '1bA' // 2361b-23637 + 'bC' // 23638-2363a #2 + 'A' // 2363b + 'Q' // 2363c #16 + 'bA' // 2363d-2363f + 'Q' // 23640 #16 + 'eA' // 23641-23646 + 'C' // 23647 #2 + 'pA' // 23648-23658 + 'Q' // 23659 #16 + 'dA' // 2365a-2365e + 'Q' // 2365f #16 + 'vA' // 23660-23676 + 'Q' // 23677 #16 + 'uA' // 23678-2368d + 'Q' // 2368e #16 + 'nA' // 2368f-2369d + 'Q' // 2369e #16 + 'fA' // 2369f-236a5 + 'Q' // 236a6 #16 + 'eA' // 236a7-236ac + 'Q' // 236ad #16 + 'kA' // 236ae-236b9 + 'Q' // 236ba #16 + '1iA' // 236bb-236de + 'Q' // 236df #16 + 'mA' // 236e0-236ed + 'Q' // 236ee #16 + 'sA' // 236ef-23702 + 'Q' // 23703 #16 + 'gA' // 23704-2370b + 'C' // 2370c #2 + 'hA' // 2370d-23715 + 'Q' // 23716 #16 + 'dA' // 23717-2371b + 'C' // 2371c #2 + 'bA' // 2371d-2371f + 'Q' // 23720 #16 + 'kA' // 23721-2372c + 'Q' // 2372d #16 + 'A' // 2372e + 'Q' // 2372f #16 + 'nA' // 23730-2373e + '27R' // 2373f #719 + '1hA' // 23740-23762 + 'aC' // 23763-23764 #2 + 'A' // 23765 + 'Q' // 23766 #16 + 'yA' // 23767-23780 + 'Q' // 23781 #16 + '1eA' // 23782-237a1 + 'Q' // 237a2 #16 + 'xA' // 237a3-237bb + 'Q' // 237bc #16 + 'dA' // 237bd-237c1 + 'Q' // 237c2 #16 + 'qA' // 237c3-237d4 + 'bQ' // 237d5-237d7 #16 + 'nA' // 237d8-237e6 + 'C' // 237e7 #2 + 'hA' // 237e8-237f0 + 'C' // 237f1 #2 + 'lA' // 237f2-237fe + 'C' // 237ff #2 + '1iA' // 23800-23823 + 'C' // 23824 #2 + 'tA' // 23825-23839 + 'Q' // 2383a #16 + 'aA' // 2383b-2383c + 'C' // 2383d #2 + '14wA' // 2383e-239c1 + 'Q' // 239c2 #16 + '8dA' // 239c3-23a97 + 'C' // 23a98 #2 + 'mA' // 23a99-23aa6 + 'Q' // 23aa7 #16 + '1xA' // 23aa8-23ada + 'Q' // 23adb #16 + 'qA' // 23adc-23aed + 'Q' // 23aee #16 + 'jA' // 23aef-23af9 + 'Q' // 23afa #16 + '1dA' // 23afb-23b19 + 'Q' // 23b1a #16 + '2jA' // 23b1b-23b59 + 'Q' // 23b5a #16 + '10cA' // 23b5b-23c62 + 'Q' // 23c63 #16 + 'zA' // 23c64-23c7e + 'C' // 23c7f #2 + 'xA' // 23c80-23c98 + 'bQ' // 23c99-23c9b #16 + 'xA' // 23c9c-23cb4 + 'Q' // 23cb5 #16 + 'A' // 23cb6 + 'Q' // 23cb7 #16 + 'eA' // 23cb8-23cbd + 'C' // 23cbe #2 + 'gA' // 23cbf-23cc6 + 'bQ' // 23cc7-23cc9 #16 + '1wA' // 23cca-23cfb + 'aQ' // 23cfc-23cfd #16 + '27R' // 23cfe #719 + 'Q' // 23cff #16 + 'C' // 23d00 #2 + 'lA' // 23d01-23d0d + 'C' // 23d0e #2 + '1vA' // 23d0f-23d3f + '27R' // 23d40 #719 + 'yA' // 23d41-23d5a + 'Q' // 23d5b #16 + '1gA' // 23d5c-23d7d + 'Q' // 23d7e #16 + 'oA' // 23d7f-23d8e + 'Q' // 23d8f #16 + '1kA' // 23d90-23db5 + 'gQ' // 23db6-23dbd #16 + 'tA' // 23dbe-23dd2 + 'C' // 23dd3 #2 + 'nA' // 23dd4-23de2 + 'Q' // 23de3 #16 + 'sA' // 23de4-23df7 + 'Q' // 23df8 #16 + 'aC' // 23df9-23dfa #2 + 'jA' // 23dfb-23e05 + 'Q' // 23e06 #16 + 'iA' // 23e07-23e10 + 'Q' // 23e11 #16 + 'yA' // 23e12-23e2b + 'eQ' // 23e2c-23e31 #16 + 'fA' // 23e32-23e38 + 'Q' // 23e39 #16 + '2yA' // 23e3a-23e87 + 'cQ' // 23e88-23e8b #16 + '1rA' // 23e8c-23eb8 + 'Q' // 23eb9 #16 + 'dA' // 23eba-23ebe + 'Q' // 23ebf #16 + 'vA' // 23ec0-23ed6 + 'Q' // 23ed7 #16 + '1dA' // 23ed8-23ef6 + 'eQ' // 23ef7-23efc #16 + '2cA' // 23efd-23f34 + 'Q' // 23f35 #16 + 'jA' // 23f36-23f40 + 'Q' // 23f41 #16 + 'gA' // 23f42-23f49 + 'Q' // 23f4a #16 + 'uA' // 23f4b-23f60 + 'R' // 23f61 #17 + '1aA' // 23f62-23f7d + 'C' // 23f7e #2 + 'cR' // 23f7f-23f82 #17 + 'kA' // 23f83-23f8e + 'R' // 23f8f #17 + '1iA' // 23f90-23fb3 + 'R' // 23fb4 #17 + 'aA' // 23fb5-23fb6 + 'R' // 23fb7 #17 + 'gA' // 23fb8-23fbf + 'R' // 23fc0 #17 + 'cA' // 23fc1-23fc4 + 'R' // 23fc5 #17 + '1jA' // 23fc6-23fea + 'eR' // 23feb-23ff0 #17 + '1eA' // 23ff1-24010 + 'R' // 24011 #17 + '1lA' // 24012-24038 + 'dR' // 24039-2403d #17 + 'lA' // 2403e-2404a + 'C' // 2404b #2 + 'jA' // 2404c-24056 + 'R' // 24057 #17 + '1rA' // 24058-24084 + 'R' // 24085 #17 + 'dA' // 24086-2408a + 'bR' // 2408b-2408d #17 + 'bA' // 2408e-24090 + 'R' // 24091 #17 + 'cA' // 24092-24095 + 'C' // 24096 #2 + '1wA' // 24097-240c8 + 'R' // 240c9 #17 + 'vA' // 240ca-240e0 + 'R' // 240e1 #17 + 'iA' // 240e2-240eb + 'R' // 240ec #17 + 'uA' // 240ed-24102 + 'C' // 24103 #2 + 'R' // 24104 #17 + 'iA' // 24105-2410e + 'R' // 2410f #17 + 'hA' // 24110-24118 + 'R' // 24119 #17 + '1jA' // 2411a-2413e + 'aR' // 2413f-24140 #17 + 'bA' // 24141-24143 + 'R' // 24144 #17 + 'hA' // 24145-2414d + 'R' // 2414e #17 + 'eA' // 2414f-24154 + 'bR' // 24155-24157 #17 + 'cA' // 24158-2415b + 'R' // 2415c #17 + 'aA' // 2415d-2415e + 'R' // 2415f #17 + 'A' // 24160 + 'R' // 24161 #17 + 'tA' // 24162-24176 + 'R' // 24177 #17 + 'aA' // 24178-24179 + 'R' // 2417a #17 + '1mA' // 2417b-241a2 + 'bR' // 241a3-241a5 #17 + 'eA' // 241a6-241ab + 'R' // 241ac #17 + 'gA' // 241ad-241b4 + 'R' // 241b5 #17 + 'oA' // 241b6-241c5 + 'C' // 241c6 #2 + 'eA' // 241c7-241cc + 'R' // 241cd #17 + 'sA' // 241ce-241e1 + 'R' // 241e2 #17 + 'xA' // 241e3-241fb + 'R' // 241fc #17 + 'A' // 241fd + 'C' // 241fe #2 + '1aA' // 241ff-2421a + 'R' // 2421b #17 + '1tA' // 2421c-2424a + 'R' // 2424b #17 + 'iA' // 2424c-24255 + 'R' // 24256 #17 + 'aA' // 24257-24258 + 'R' // 24259 #17 + '1aA' // 2425a-24275 + 'bR' // 24276-24278 #17 + 'jA' // 24279-24283 + 'R' // 24284 #17 + 'mA' // 24285-24292 + 'R' // 24293 #17 + 'A' // 24294 + 'R' // 24295 #17 + 'nA' // 24296-242a4 + 'R' // 242a5 #17 + 'xA' // 242a6-242be + 'R' // 242bf #17 + 'A' // 242c0 + 'R' // 242c1 #17 + 'fA' // 242c2-242c8 + 'aR' // 242c9-242ca #17 + '1hA' // 242cb-242ed + '52S' // 242ee #1370 + 'jA' // 242ef-242f9 + 'R' // 242fa #17 + 'qA' // 242fb-2430c + 'R' // 2430d #17 + 'kA' // 2430e-24319 + 'R' // 2431a #17 + 'xA' // 2431b-24333 + 'R' // 24334 #17 + 'rA' // 24335-24347 + 'R' // 24348 #17 + 'xA' // 24349-24361 + 'cR' // 24362-24365 #17 + '1kA' // 24366-2438b + 'R' // 2438c #17 + 'hA' // 2438d-24395 + 'R' // 24396 #17 + 'dA' // 24397-2439b + 'R' // 2439c #17 + '1dA' // 2439d-243bb + 'C' // 243bc #2 + 'R' // 243bd #17 + 'bA' // 243be-243c0 + 'R' // 243c1 #17 + 'mA' // 243c2-243cf + 'C' // 243d0 #2 + 'wA' // 243d1-243e8 + 'aR' // 243e9-243ea #17 + 'fA' // 243eb-243f1 + 'R' // 243f2 #17 + 'dA' // 243f3-243f7 + 'R' // 243f8 #17 + 'jA' // 243f9-24403 + 'R' // 24404 #17 + '1uA' // 24405-24434 + 'aR' // 24435-24436 #17 + '1hA' // 24437-24459 + 'aR' // 2445a-2445b #17 + 'vA' // 2445c-24472 + 'R' // 24473 #17 + 'rA' // 24474-24486 + 'aR' // 24487-24488 #17 + '1uA' // 24489-244b8 + 'R' // 244b9 #17 + 'aA' // 244ba-244bb + 'R' // 244bc #17 + 'pA' // 244bd-244cd + 'R' // 244ce #17 + 'cA' // 244cf-244d2 + 'R' // 244d3 #17 + 'aA' // 244d4-244d5 + 'R' // 244d6 #17 + '1sA' // 244d7-24504 + 'R' // 24505 #17 + 'zA' // 24506-24520 + 'R' // 24521 #17 + '3gA' // 24522-24577 + 'R' // 24578 #17 + '2zA' // 24579-245c7 + 'R' // 245c8 #17 + '2zA' // 245c9-24617 + 'R' // 24618 #17 + 'oA' // 24619-24628 + 'C' // 24629 #2 + 'R' // 2462a #17 + '2eA' // 2462b-24664 + 'R' // 24665 #17 + 'mA' // 24666-24673 + 'R' // 24674 #17 + '1gA' // 24675-24696 + 'R' // 24697 #17 + 'lA' // 24698-246a4 + 'C' // 246a5 #2 + '1sA' // 246a6-246d3 + 'R' // 246d4 #17 + '1vA' // 246d5-24705 + 'R' // 24706 #17 + '1cA' // 24707-24724 + 'R' // 24725 #17 + 'hA' // 24726-2472e + 'R' // 2472f #17 + '3pA' // 24730-2478e + 'R' // 2478f #17 + '3aA' // 24790-247df + 'R' // 247e0 #17 + 'oA' // 247e1-247f0 + 'C' // 247f1 #2 + '1eA' // 247f2-24811 + 'R' // 24812 #17 + 'oA' // 24813-24822 + 'R' // 24823 #17 + '3oA' // 24824-24881 + 'R' // 24882 #17 + 'rA' // 24883-24895 + 'C' // 24896 #2 + '3cA' // 24897-248e8 + '52S' // 248e9 #1370 + 'eA' // 248ea-248ef + 'cR' // 248f0-248f3 #17 + 'fA' // 248f4-248fa + 'R' // 248fb #17 + 'bA' // 248fc-248fe + 'bR' // 248ff-24901 #17 + 'iA' // 24902-2490b + 'R' // 2490c #17 + 'hA' // 2490d-24915 + 'aR' // 24916-24917 #17 + 'A' // 24918 + 'R' // 24919 #17 + 'tA' // 2491a-2492e + 'R' // 2492f #17 + 'bA' // 24930-24932 + 'aR' // 24933-24934 #17 + 'hA' // 24935-2493d + 'eR' // 2493e-24943 #17 + '1cA' // 24944-24961 + 'aR' // 24962-24963 #17 + 'oA' // 24964-24973 + 'bR' // 24974-24976 #17 + 'cA' // 24977-2497a + 'R' // 2497b #17 + 'bA' // 2497c-2497e + 'R' // 2497f #17 + 'aA' // 24980-24981 + 'R' // 24982 #17 + 'dA' // 24983-24987 + 'gR' // 24988-2498f #17 + 'cA' // 24990-24993 + 'R' // 24994 #17 + 'nA' // 24995-249a3 + 'R' // 249a4 #17 + 'aA' // 249a5-249a6 + 'R' // 249a7 #17 + 'A' // 249a8 + 'R' // 249a9 #17 + 'A' // 249aa + 'bR' // 249ab-249ad #17 + 'hA' // 249ae-249b6 + 'cR' // 249b7-249ba #17 + 'P' // 249bb #15 + 'hA' // 249bc-249c4 + 'P' // 249c5 #15 + 'iA' // 249c6-249cf + 'P' // 249d0 #15 + 'hA' // 249d1-249d9 + 'P' // 249da #15 + 'bA' // 249db-249dd + 'aP' // 249de-249df #15 + 'bA' // 249e0-249e2 + 'P' // 249e3 #15 + 'A' // 249e4 + 'P' // 249e5 #15 + 'eA' // 249e6-249eb + 'aP' // 249ec-249ed #15 + 'gA' // 249ee-249f5 + 'cP' // 249f6-249f9 #15 + 'A' // 249fa + 'P' // 249fb #15 + 'qA' // 249fc-24a0d + 'P' // 24a0e #15 + 'bA' // 24a0f-24a11 + '37A' // 24a12 #962 + 'P' // 24a13 #15 + 'A' // 24a14 + 'P' // 24a15 #15 + 'jA' // 24a16-24a20 + 'iP' // 24a21-24a2a #15 + 'rA' // 24a2b-24a3d + 'P' // 24a3e #15 + 'bA' // 24a3f-24a41 + 'P' // 24a42 #15 + 'aA' // 24a43-24a44 + 'P' // 24a45 #15 + 'cA' // 24a46-24a49 + 'P' // 24a4a #15 + 'aA' // 24a4b-24a4c + 'C' // 24a4d #2 + 'cP' // 24a4e-24a51 #15 + 'jA' // 24a52-24a5c + 'P' // 24a5d #15 + 'fA' // 24a5e-24a64 + 'bP' // 24a65-24a67 #15 + 'hA' // 24a68-24a70 + 'P' // 24a71 #15 + 'dA' // 24a72-24a76 + 'cP' // 24a77-24a7a #15 + 'pA' // 24a7b-24a8b + 'P' // 24a8c #15 + 'eA' // 24a8d-24a92 + 'cP' // 24a93-24a96 #15 + 'lA' // 24a97-24aa3 + 'cP' // 24aa4-24aa7 #15 + 'hA' // 24aa8-24ab0 + 'bP' // 24ab1-24ab3 #15 + 'eA' // 24ab4-24ab9 + 'bP' // 24aba-24abc #15 + 'bA' // 24abd-24abf + 'P' // 24ac0 #15 + 'eA' // 24ac1-24ac6 + 'P' // 24ac7 #15 + 'aA' // 24ac8-24ac9 + 'P' // 24aca #15 + 'eA' // 24acb-24ad0 + 'P' // 24ad1 #15 + 'lA' // 24ad2-24ade + 'P' // 24adf #15 + 'aA' // 24ae0-24ae1 + 'P' // 24ae2 #15 + 'eA' // 24ae3-24ae8 + 'P' // 24ae9 #15 + '1jA' // 24aea-24b0e + 'P' // 24b0f #15 + '2qA' // 24b10-24b55 + 'C' // 24b56 #2 + 'vA' // 24b57-24b6d + 'P' // 24b6e #15 + 'C' // 24b6f #2 + '5bA' // 24b70-24bf4 + 'P' // 24bf5 #15 + 'rA' // 24bf6-24c08 + 'P' // 24c09 #15 + 'kA' // 24c0a-24c15 + 'C' // 24c16 #2 + '5dA' // 24c17-24c9d + 'aP' // 24c9e-24c9f #15 + '1nA' // 24ca0-24cc8 + 'P' // 24cc9 #15 + 'nA' // 24cca-24cd8 + 'P' // 24cd9 #15 + '1qA' // 24cda-24d05 + 'P' // 24d06 #15 + 'kA' // 24d07-24d12 + 'P' // 24d13 #15 + 'C' // 24d14 #2 + '6fA' // 24d15-24db7 + 'P' // 24db8 #15 + '1vA' // 24db9-24de9 + 'aP' // 24dea-24deb #15 + 'wA' // 24dec-24e03 + 'C' // 24e04 #2 + 'hA' // 24e05-24e0d + 'C' // 24e0e #2 + '1mA' // 24e0f-24e36 + 'C' // 24e37 #2 + 'bA' // 24e38-24e3a + 'P' // 24e3b #15 + 'sA' // 24e3c-24e4f + 'P' // 24e50 #15 + 'xA' // 24e51-24e69 + 'C' // 24e6a #2 + '1eA' // 24e6b-24e8a + 'C' // 24e8b #2 + 'xA' // 24e8c-24ea4 + 'P' // 24ea5 #15 + 'A' // 24ea6 + 'P' // 24ea7 #15 + '3wA' // 24ea8-24f0d + 'P' // 24f0e #15 + '2xA' // 24f0f-24f5b + 'P' // 24f5c #15 + '1jA' // 24f5d-24f81 + 'P' // 24f82 #15 + 'bA' // 24f83-24f85 + 'P' // 24f86 #15 + 'oA' // 24f87-24f96 + 'P' // 24f97 #15 + 'aA' // 24f98-24f99 + 'P' // 24f9a #15 + 'mA' // 24f9b-24fa8 + 'P' // 24fa9 #15 + 'mA' // 24faa-24fb7 + 'P' // 24fb8 #15 + 'hA' // 24fb9-24fc1 + 'P' // 24fc2 #15 + '1tA' // 24fc3-24ff1 + 'C' // 24ff2 #2 + '2dA' // 24ff3-2502b + 'P' // 2502c #15 + '1bA' // 2502d-25049 + 'C' // 2504a #2 + 'fA' // 2504b-25051 + 'P' // 25052 #15 + 'aA' // 25053-25054 + 'C' // 25055 #2 + '2rA' // 25056-2509c + 'P' // 2509d #15 + '5aA' // 2509e-25121 + 'C' // 25122 #2 + 'gA' // 25123-2512a + '37A' // 2512b #962 + '1aA' // 2512c-25147 + 'P' // 25148 #15 + '1yA' // 25149-2517c + 'aP' // 2517d-2517e #15 + '1oA' // 2517f-251a8 + 'C' // 251a9 #2 + '1hA' // 251aa-251cc + '52R' // 251cd #1369 + 'tA' // 251ce-251e2 + 'P' // 251e3 #15 + 'A' // 251e4 + 'C' // 251e5 #2 + 'aP' // 251e6-251e7 #15 + '2aA' // 251e8-2521d + 'C' // 2521e #2 + 'A' // 2521f + 'aP' // 25220-25221 #15 + '1oA' // 25222-2524b + 'C' // 2524c #2 + 'bA' // 2524d-2524f + 'P' // 25250 #15 + '2sA' // 25251-25298 + 'P' // 25299 #15 + '1rA' // 2529a-252c6 + 'P' // 252c7 #15 + 'oA' // 252c8-252d7 + 'P' // 252d8 #15 + '1zA' // 252d9-2530d + 'P' // 2530e #15 + 'aA' // 2530f-25310 + 'P' // 25311 #15 + 'A' // 25312 + 'P' // 25313 #15 + '9zA' // 25314-25418 + 'P' // 25419 #15 + 'jA' // 2541a-25424 + 'P' // 25425 #15 + 'gA' // 25426-2542d + 'C' // 2542e #2 + 'aP' // 2542f-25430 #15 + 'tA' // 25431-25445 + 'P' // 25446 #15 + '1jA' // 25447-2546b + 'P' // 2546c #15 + 'A' // 2546d + '37A' // 2546e #962 + '1dA' // 2546f-2548d + 'C' // 2548e #2 + 'jA' // 2548f-25499 + 'P' // 2549a #15 + '2iA' // 2549b-254d8 + 'C' // 254d9 #2 + '1yA' // 254da-2550d + 'C' // 2550e #2 + '1gA' // 2550f-25530 + 'P' // 25531 #15 + 'bA' // 25532-25534 + 'P' // 25535 #15 + 'hA' // 25536-2553e + 'P' // 2553f #15 + 'zA' // 25540-2555a + 'cP' // 2555b-2555e #15 + 'bA' // 2555f-25561 + 'P' // 25562 #15 + 'aA' // 25563-25564 + 'aP' // 25565-25566 #15 + 'yA' // 25567-25580 + 'P' // 25581 #15 + 'aA' // 25582-25583 + 'P' // 25584 #15 + 'iA' // 25585-2558e + 'P' // 2558f #15 + 'vA' // 25590-255a6 + 'C' // 255a7 #2 + 'pA' // 255a8-255b8 + 'P' // 255b9 #15 + 'zA' // 255ba-255d4 + 'P' // 255d5 #15 + 'dA' // 255d6-255da + 'P' // 255db #15 + 'cA' // 255dc-255df + 'P' // 255e0 #15 + '1iA' // 255e1-25604 + 'P' // 25605 #15 + '1tA' // 25606-25634 + 'P' // 25635 #15 + 'zA' // 25636-25650 + 'P' // 25651 #15 + '1rA' // 25652-2567e + 'C' // 2567f #2 + 'bA' // 25680-25682 + '37A' // 25683 #962 + 'pA' // 25684-25694 + 'P' // 25695 #15 + '2xA' // 25696-256e2 + 'P' // 256e3 #15 + 'qA' // 256e4-256f5 + 'P' // 256f6 #15 + 'nA' // 256f7-25705 + 'P' // 25706 #15 + 'uA' // 25707-2571c + 'P' // 2571d #15 + 'fA' // 2571e-25724 + 'P' // 25725 #15 + 'vA' // 25726-2573c + 'P' // 2573d #15 + '1xA' // 2573e-25770 + 'C' // 25771 #2 + 'P' // 25772 #15 + '2aA' // 25773-257a8 + 'C' // 257a9 #2 + 'iA' // 257aa-257b3 + 'C' // 257b4 #2 + 'qA' // 257b5-257c6 + 'P' // 257c7 #15 + 'vA' // 257c8-257de + 'bP' // 257df-257e1 #15 + '4lA' // 257e2-25856 + 'P' // 25857 #15 + 'dA' // 25858-2585c + 'P' // 2585d #15 + 'sA' // 2585e-25871 + 'P' // 25872 #15 + 'A' // 25873 + 'C' // 25874 #2 + '3dA' // 25875-258c7 + 'P' // 258c8 #15 + 'tA' // 258c9-258dd + 'P' // 258de #15 + 'aA' // 258df-258e0 + 'P' // 258e1 #15 + '1fA' // 258e2-25902 + 'P' // 25903 #15 + '2mA' // 25904-25945 + 'P' // 25946 #15 + 'nA' // 25947-25955 + 'P' // 25956 #15 + '3fA' // 25957-259ab + 'P' // 259ac #15 + 'vA' // 259ad-259c3 + 'C' // 259c4 #2 + 'fA' // 259c5-259cb + '52R' // 259cc #1369 + 'fA' // 259cd-259d3 + 'C' // 259d4 #2 + '4vA' // 259d5-25a53 + 'P' // 25a54 #15 + '2kA' // 25a55-25a94 + 'P' // 25a95 #15 + 'eA' // 25a96-25a9b + 'K' // 25a9c #10 + 'pA' // 25a9d-25aad + 'aK' // 25aae-25aaf #10 + '1lA' // 25ab0-25ad6 + 'C' // 25ad7 #2 + 'jA' // 25ad8-25ae2 + 'aC' // 25ae3-25ae4 #2 + 'cA' // 25ae5-25ae8 + 'K' // 25ae9 #10 + 'fA' // 25aea-25af0 + 'C' // 25af1 #2 + '4yA' // 25af2-25b73 + 'K' // 25b74 #10 + 'sA' // 25b75-25b88 + 'K' // 25b89 #10 + '1mA' // 25b8a-25bb1 + 'C' // 25bb2 #2 + 'aK' // 25bb3-25bb4 #10 + 'pA' // 25bb5-25bc5 + 'K' // 25bc6 #10 + '1bA' // 25bc7-25be3 + 'K' // 25be4 #10 + 'bA' // 25be5-25be7 + 'K' // 25be8 #10 + 'wA' // 25be9-25c00 + 'K' // 25c01 #10 + 'cA' // 25c02-25c05 + 'K' // 25c06 #10 + 'yA' // 25c07-25c20 + 'K' // 25c21 #10 + '1mA' // 25c22-25c49 + 'K' // 25c4a #10 + 'C' // 25c4b #2 + 'wA' // 25c4c-25c63 + 'C' // 25c64 #2 + 'K' // 25c65 #10 + '1pA' // 25c66-25c90 + 'K' // 25c91 #10 + 'qA' // 25c92-25ca3 + 'K' // 25ca4 #10 + 'zA' // 25ca5-25cbf + 'aK' // 25cc0-25cc1 #10 + '2gA' // 25cc2-25cfd + 'K' // 25cfe #10 + '1fA' // 25cff-25d1f + 'K' // 25d20 #10 + 'nA' // 25d21-25d2f + 'K' // 25d30 #10 + 'qA' // 25d31-25d42 + 'K' // 25d43 #10 + '3fA' // 25d44-25d98 + 'K' // 25d99 #10 + 'fA' // 25d9a-25da0 + 'C' // 25da1 #2 + 'vA' // 25da2-25db8 + 'K' // 25db9 #10 + '3eA' // 25dba-25e0d + 'K' // 25e0e #10 + '1dA' // 25e0f-25e2d + 'C' // 25e2e #2 + 'yA' // 25e2f-25e48 + 'K' // 25e49 #10 + 'kA' // 25e4a-25e55 + 'C' // 25e56 #2 + 'jA' // 25e57-25e61 + 'C' // 25e62 #2 + 'aA' // 25e63-25e64 + 'C' // 25e65 #2 + 'zA' // 25e66-25e80 + 'bK' // 25e81-25e83 #10 + '1gA' // 25e84-25ea5 + 'K' // 25ea6 #10 + 'tA' // 25ea7-25ebb + 'K' // 25ebc #10 + 'dA' // 25ebd-25ec1 + 'C' // 25ec2 #2 + 'sA' // 25ec3-25ed6 + 'K' // 25ed7 #10 + '80K' // 25ed8 #2090 + 'nA' // 25ed9-25ee7 + 'B' // 25ee8 #1 + '1vA' // 25ee9-25f19 + 'K' // 25f1a #10 + 'gA' // 25f1b-25f22 + 'B' // 25f23 #1 + '1lA' // 25f24-25f4a + 'K' // 25f4b #10 + 'oA' // 25f4c-25f5b + 'B' // 25f5c #1 + '4nA' // 25f5d-25fd3 + 'B' // 25fd4 #1 + 'jA' // 25fd5-25fdf + 'B' // 25fe0 #1 + 'aK' // 25fe1-25fe2 #10 + 'wA' // 25fe3-25ffa + 'B' // 25ffb #1 + 'oA' // 25ffc-2600b + 'B' // 2600c #1 + 'iA' // 2600d-26016 + 'B' // 26017 #1 + 'hA' // 26018-26020 + 'K' // 26021 #10 + 'fA' // 26022-26028 + 'K' // 26029 #10 + '1cA' // 2602a-26047 + 'K' // 26048 #10 + 'vA' // 26049-2605f + 'B' // 26060 #1 + 'bA' // 26061-26063 + 'K' // 26064 #10 + '1cA' // 26065-26082 + 'K' // 26083 #10 + 'rA' // 26084-26096 + 'K' // 26097 #10 + 'kA' // 26098-260a3 + 'aK' // 260a4-260a5 #10 + '2rA' // 260a6-260ec + 'B' // 260ed #1 + 'sA' // 260ee-26101 + 'K' // 26102 #10 + '1cA' // 26103-26120 + 'K' // 26121 #10 + '2bA' // 26122-26158 + 'cK' // 26159-2615c #10 + '3aA' // 2615d-261ac + 'aK' // 261ad-261ae #10 + 'bA' // 261af-261b1 + 'K' // 261b2 #10 + '1oA' // 261b3-261dc + 'K' // 261dd #10 + '2oA' // 261de-26221 + 'B' // 26222 #1 + '1zA' // 26223-26257 + 'K' // 26258 #10 + 'gA' // 26259-26260 + 'K' // 26261 #10 + 'gA' // 26262-26269 + '36Z' // 2626a #961 + 'K' // 2626b #10 + 'cA' // 2626c-2626f + 'B' // 26270 #1 + 'tA' // 26271-26285 + 'B' // 26286 #1 + '2tA' // 26287-262cf + 'K' // 262d0 #10 + '3uA' // 262d1-26334 + 'K' // 26335 #10 + 'tA' // 26336-2634a + 'K' // 2634b #10 + '36Z' // 2634c #961 + 'cA' // 2634d-26350 + 'K' // 26351 #10 + '4cA' // 26352-263bd + 'K' // 263be #10 + '2aA' // 263bf-263f4 + 'K' // 263f5 #10 + 'aA' // 263f6-263f7 + 'K' // 263f8 #10 + 'hA' // 263f9-26401 + '36Z' // 26402 #961 + 'lA' // 26403-2640f + 'bK' // 26410-26412 #10 + '2bA' // 26413-26449 + 'K' // 2644a #10 + '1cA' // 2644b-26468 + 'K' // 26469 #10 + 'yA' // 2646a-26483 + 'K' // 26484 #10 + 'bA' // 26485-26487 + 'aK' // 26488-26489 #10 + 'bA' // 2648a-2648c + 'K' // 2648d #10 + 'iA' // 2648e-26497 + 'K' // 26498 #10 + '4pA' // 26499-26511 + 'K' // 26512 #10 + '3pA' // 26513-26571 + 'K' // 26572 #10 + '1rA' // 26573-2659f + 'K' // 265a0 #10 + 'kA' // 265a1-265ac + 'K' // 265ad #10 + 'pA' // 265ae-265be + 'K' // 265bf #10 + '3cA' // 265c0-26611 + 'K' // 26612 #10 + 'rA' // 26613-26625 + 'K' // 26626 #10 + '3hA' // 26627-2667d + 'B' // 2667e #1 + '1uA' // 2667f-266ae + 'K' // 266af #10 + 'B' // 266b0 #1 + 'K' // 266b1 #10 + 'bA' // 266b2-266b4 + 'K' // 266b5 #10 + '1iA' // 266b6-266d9 + 'K' // 266da #10 + 'lA' // 266db-266e7 + 'K' // 266e8 #10 + 'rA' // 266e9-266fb + 'K' // 266fc #10 + 'xA' // 266fd-26715 + 'K' // 26716 #10 + 'eA' // 26717-2671c + 'B' // 2671d #1 + '1hA' // 2671e-26740 + 'K' // 26741 #10 + '3hA' // 26742-26798 + 'K' // 26799 #10 + 'xA' // 2679a-267b2 + 'aK' // 267b3-267b4 #10 + 'vA' // 267b5-267cb + '52Q' // 267cc #1368 + '2zA' // 267cd-2681b + 'K' // 2681c #10 + '1nA' // 2681d-26845 + 'K' // 26846 #10 + 'vA' // 26847-2685d + 'K' // 2685e #10 + 'nA' // 2685f-2686d + 'K' // 2686e #10 + 'xA' // 2686f-26887 + 'K' // 26888 #10 + 'A' // 26889 + 'K' // 2688a #10 + 'gA' // 2688b-26892 + 'K' // 26893 #10 + '1xA' // 26894-268c6 + 'K' // 268c7 #10 + 'tA' // 268c8-268dc + 'B' // 268dd #1 + 'kA' // 268de-268e9 + 'B' // 268ea #1 + '1hA' // 268eb-2690d + 'K' // 2690e #10 + 'aA' // 2690f-26910 + 'K' // 26911 #10 + 'sA' // 26912-26925 + 'K' // 26926 #10 + 'qA' // 26927-26938 + 'K' // 26939 #10 + 'vA' // 2693a-26950 + '36Z' // 26951 #961 + '1bA' // 26952-2696e + 'B' // 2696f #1 + '1nA' // 26970-26998 + 'B' // 26999 #1 + 'mA' // 2699a-269a7 + 'K' // 269a8 #10 + 'kA' // 269a9-269b4 + 'K' // 269b5 #10 + '1lA' // 269b6-269dc + 'B' // 269dd #1 + 'sA' // 269de-269f1 + '52Q' // 269f2 #1368 + 'fA' // 269f3-269f9 + 'K' // 269fa #10 + '1hA' // 269fb-26a1d + 'B' // 26a1e #1 + 'mA' // 26a1f-26a2c + 'aK' // 26a2d-26a2e #10 + 'dA' // 26a2f-26a33 + 'K' // 26a34 #10 + 'lA' // 26a35-26a41 + 'K' // 26a42 #10 + 'mA' // 26a43-26a50 + 'aK' // 26a51-26a52 #10 + 'dA' // 26a53-26a57 + 'B' // 26a58 #1 + '1xA' // 26a59-26a8b + 'B' // 26a8c #1 + '1oA' // 26a8d-26ab6 + 'B' // 26ab7 #1 + '2rA' // 26ab8-26afe + 'B' // 26aff #1 + 'dA' // 26b00-26b04 + 'K' // 26b05 #10 + 'cA' // 26b06-26b09 + 'K' // 26b0a #10 + 'gA' // 26b0b-26b12 + 'K' // 26b13 #10 + 'A' // 26b14 + 'K' // 26b15 #10 + 'lA' // 26b16-26b22 + 'K' // 26b23 #10 + 'cA' // 26b24-26b27 + 'K' // 26b28 #10 + '1lA' // 26b29-26b4f + 'cK' // 26b50-26b53 #10 + 'fA' // 26b54-26b5a + 'K' // 26b5b #10 + 'xA' // 26b5c-26b74 + 'K' // 26b75 #10 + 'kA' // 26b76-26b81 + 'K' // 26b82 #10 + 'rA' // 26b83-26b95 + 'aK' // 26b96-26b97 #10 + 'dA' // 26b98-26b9c + 'K' // 26b9d #10 + 'tA' // 26b9e-26bb2 + 'K' // 26bb3 #10 + 'kA' // 26bb4-26bbf + 'K' // 26bc0 #10 + '2aA' // 26bc1-26bf6 + 'K' // 26bf7 #10 + '1nA' // 26bf8-26c20 + 'K' // 26c21 #10 + 'fA' // 26c22-26c28 + 'B' // 26c29 #1 + 'uA' // 26c2a-26c3f + 'aK' // 26c40-26c41 #10 + 'cA' // 26c42-26c45 + 'K' // 26c46 #10 + '1qA' // 26c47-26c72 + 'B' // 26c73 #1 + 'iA' // 26c74-26c7d + 'dK' // 26c7e-26c82 #10 + 'zA' // 26c83-26c9d + 'B' // 26c9e #1 + 'dA' // 26c9f-26ca3 + 'K' // 26ca4 #10 + 'qA' // 26ca5-26cb6 + 'aK' // 26cb7-26cb8 #10 + 'cA' // 26cb9-26cbc + 'K' // 26cbd #10 + 'aA' // 26cbe-26cbf + 'K' // 26cc0 #10 + 'aA' // 26cc1-26cc2 + 'K' // 26cc3 #10 + 'lA' // 26cc4-26cd0 + 'K' // 26cd1 #10 + 'jA' // 26cd2-26cdc + 'B' // 26cdd #1 + '2oA' // 26cde-26d21 + 'hK' // 26d22-26d2a #10 + '1kA' // 26d2b-26d50 + 'K' // 26d51 #10 + '1gA' // 26d52-26d73 + 'K' // 26d74 #10 + '1pA' // 26d75-26d9f + 'gJ' // 26da0-26da7 #9 + 'eA' // 26da8-26dad + 'J' // 26dae #9 + '1rA' // 26daf-26ddb + 'J' // 26ddc #9 + 'lA' // 26ddd-26de9 + 'aJ' // 26dea-26deb #9 + 'cA' // 26dec-26def + 'J' // 26df0 #9 + 'nA' // 26df1-26dff + 'J' // 26e00 #9 + 'cA' // 26e01-26e04 + 'J' // 26e05 #9 + 'A' // 26e06 + 'J' // 26e07 #9 + 'iA' // 26e08-26e11 + 'J' // 26e12 #9 + '1rA' // 26e13-26e3f + 'B' // 26e40 #1 + 'A' // 26e41 + 'cJ' // 26e42-26e45 #9 + '1dA' // 26e46-26e64 + 'B' // 26e65 #1 + 'gA' // 26e66-26e6d + 'J' // 26e6e #9 + 'bA' // 26e6f-26e71 + 'J' // 26e72 #9 + 'cA' // 26e73-26e76 + 'J' // 26e77 #9 + 'kA' // 26e78-26e83 + 'J' // 26e84 #9 + 'bA' // 26e85-26e87 + 'J' // 26e88 #9 + 'aA' // 26e89-26e8a + 'J' // 26e8b #9 + 'lA' // 26e8c-26e98 + 'J' // 26e99 #9 + '2aA' // 26e9a-26ecf + 'gJ' // 26ed0-26ed7 #9 + '2yA' // 26ed8-26f25 + 'J' // 26f26 #9 + '2wA' // 26f27-26f72 + 'aJ' // 26f73-26f74 #9 + '1dA' // 26f75-26f93 + 'B' // 26f94 #1 + 'iA' // 26f95-26f9e + 'J' // 26f9f #9 + 'A' // 26fa0 + 'J' // 26fa1 #9 + '1aA' // 26fa2-26fbd + 'J' // 26fbe #9 + '1dA' // 26fbf-26fdd + 'aJ' // 26fde-26fdf #9 + 'uA' // 26fe0-26ff5 + 'bB' // 26ff6-26ff8 #1 + 'tA' // 26ff9-2700d + 'J' // 2700e #9 + '2gA' // 2700f-2704a + 'J' // 2704b #9 + 'eA' // 2704c-27051 + 'aJ' // 27052-27053 #9 + '1yA' // 27054-27087 + 'J' // 27088 #9 + '1iA' // 27089-270ac + 'bJ' // 270ad-270af #9 + '1bA' // 270b0-270cc + 'J' // 270cd #9 + 'cA' // 270ce-270d1 + 'J' // 270d2 #9 + '1bA' // 270d3-270ef + 'J' // 270f0 #9 + 'bA' // 270f1-270f3 + 'B' // 270f4 #1 + 'bA' // 270f5-270f7 + 'J' // 270f8 #9 + 'oA' // 270f9-27108 + 'J' // 27109 #9 + 'aA' // 2710a-2710b + 'J' // 2710c #9 + '36Y' // 2710d #960 + 'wA' // 2710e-27125 + 'aJ' // 27126-27127 #9 + 'pA' // 27128-27138 + 'B' // 27139 #1 + '1oA' // 2713a-27163 + 'aJ' // 27164-27165 #9 + 'nA' // 27166-27174 + 'J' // 27175 #9 + '3hA' // 27176-271cc + 'J' // 271cd #9 + '2xA' // 271ce-2721a + 'J' // 2721b #9 + '2vA' // 2721c-27266 + 'J' // 27267 #9 + 'wA' // 27268-2727f + 'J' // 27280 #9 + 'cA' // 27281-27284 + 'J' // 27285 #9 + 'dA' // 27286-2728a + 'J' // 2728b #9 + '1kA' // 2728c-272b1 + 'J' // 272b2 #9 + 'bA' // 272b3-272b5 + 'J' // 272b6 #9 + '1tA' // 272b7-272e5 + 'J' // 272e6 #9 + '4bA' // 272e7-27351 + 'J' // 27352 #9 + '2rA' // 27353-27399 + 'J' // 2739a #9 + '2jA' // 2739b-273d9 + 'aB' // 273da-273db #1 + '1gA' // 273dc-273fd + 'B' // 273fe #1 + 'J' // 273ff #9 + 'oA' // 27400-2740f + 'B' // 27410 #1 + 'pA' // 27411-27421 + 'J' // 27422 #9 + '1kA' // 27423-27448 + 'B' // 27449 #1 + 'eA' // 2744a-2744f + 'J' // 27450 #9 + '1xA' // 27451-27483 + 'J' // 27484 #9 + 'A' // 27485 + 'J' // 27486 #9 + '9bA' // 27487-27573 + 'J' // 27574 #9 + '1sA' // 27575-275a2 + 'J' // 275a3 #9 + '2gA' // 275a4-275df + 'J' // 275e0 #9 + 'bA' // 275e1-275e3 + 'J' // 275e4 #9 + 'wA' // 275e5-275fc + 'aJ' // 275fd-275fe #9 + 'gA' // 275ff-27606 + 'J' // 27607 #9 + 'cA' // 27608-2760b + 'J' // 2760c #9 + 'fA' // 2760d-27613 + 'aB' // 27614-27615 #1 + 'zA' // 27616-27630 + 'B' // 27631 #1 + 'J' // 27632 #9 + 'eA' // 27633-27638 + 'J' // 27639 #9 + 'zA' // 2763a-27654 + 'aJ' // 27655-27656 #9 + '80J' // 27657 #2089 + '1qA' // 27658-27683 + 'B' // 27684 #1 + 'mA' // 27685-27692 + 'B' // 27693 #1 + 'J' // 27694 #9 + '4pA' // 27695-2770d + 'B' // 2770e #1 + 'J' // 2770f #9 + 'rA' // 27710-27722 + 'B' // 27723 #1 + 'pA' // 27724-27734 + 'aJ' // 27735-27736 #9 + 'iA' // 27737-27740 + 'J' // 27741 #9 + 'oA' // 27742-27751 + 'B' // 27752 #1 + 'jA' // 27753-2775d + 'J' // 2775e #9 + '1jA' // 2775f-27783 + 'aJ' // 27784-27785 #9 + '2qA' // 27786-277cb + 'J' // 277cc #9 + '5hA' // 277cd-27857 + 'J' // 27858 #9 + 'vA' // 27859-2786f + 'J' // 27870 #9 + '1qA' // 27871-2789c + 'J' // 2789d #9 + 'sA' // 2789e-278b1 + '36Y' // 278b2 #960 + 'tA' // 278b3-278c7 + 'J' // 278c8 #9 + '3lA' // 278c9-27923 + 'J' // 27924 #9 + '2mA' // 27925-27966 + 'J' // 27967 #9 + 'qA' // 27968-27979 + 'J' // 2797a #9 + 'iA' // 2797b-27984 + 'B' // 27985 #1 + 'yA' // 27986-2799f + 'J' // 279a0 #9 + 'rA' // 279a1-279b3 + 'B' // 279b4 #1 + '1mA' // 279b5-279dc + 'J' // 279dd #9 + '1dA' // 279de-279fc + 'J' // 279fd #9 + 'kA' // 279fe-27a09 + 'J' // 27a0a #9 + 'bA' // 27a0b-27a0d + 'J' // 27a0e #9 + '1tA' // 27a0f-27a3d + 'J' // 27a3e #9 + 'sA' // 27a3f-27a52 + 'J' // 27a53 #9 + 'dA' // 27a54-27a58 + 'J' // 27a59 #9 + '1dA' // 27a5a-27a78 + 'J' // 27a79 #9 + 'iA' // 27a7a-27a83 + '36Y' // 27a84 #960 + '2cA' // 27a85-27abc + 'aJ' // 27abd-27abe #9 + '1zA' // 27abf-27af3 + 'J' // 27af4 #9 + 'pA' // 27af5-27b05 + 'J' // 27b06 #9 + 'cA' // 27b07-27b0a + 'J' // 27b0b #9 + 'kA' // 27b0c-27b17 + 'J' // 27b18 #9 + '1dA' // 27b19-27b37 + 'bJ' // 27b38-27b3a #9 + 'lA' // 27b3b-27b47 + 'J' // 27b48 #9 + '1aA' // 27b49-27b64 + 'J' // 27b65 #9 + '2xA' // 27b66-27bb2 + 'B' // 27bb3 #1 + 'iA' // 27bb4-27bbd + 'B' // 27bbe #1 + 'gA' // 27bbf-27bc6 + 'B' // 27bc7 #1 + '1lA' // 27bc8-27bee + 'J' // 27bef #9 + 'cA' // 27bf0-27bf3 + 'J' // 27bf4 #9 + '1bA' // 27bf5-27c11 + 'J' // 27c12 #9 + '1nA' // 27c13-27c3b + 'B' // 27c3c #1 + '1tA' // 27c3d-27c6b + 'J' // 27c6c #9 + '2oA' // 27c6d-27cb0 + 'J' // 27cb1 #9 + 'eA' // 27cb2-27cb7 + 'B' // 27cb8 #1 + 'kA' // 27cb9-27cc4 + 'J' // 27cc5 #9 + '3zA' // 27cc6-27d2e + 'J' // 27d2f #9 + '1hA' // 27d30-27d52 + 'aJ' // 27d53-27d54 #9 + 'pA' // 27d55-27d65 + 'J' // 27d66 #9 + 'kA' // 27d67-27d72 + '36Y' // 27d73 #960 + 'oA' // 27d74-27d83 + 'J' // 27d84 #9 + 'iA' // 27d85-27d8e + 'J' // 27d8f #9 + 'gA' // 27d90-27d97 + 'J' // 27d98 #9 + 'fA' // 27d99-27d9f + 'B' // 27da0 #1 + '1aA' // 27da1-27dbc + 'J' // 27dbd #9 + '1cA' // 27dbe-27ddb + 'J' // 27ddc #9 + '1xA' // 27ddd-27e0f + 'B' // 27e10 #1 + '2gA' // 27e11-27e4c + 'J' // 27e4d #9 + 'A' // 27e4e + 'J' // 27e4f #9 + '3pA' // 27e50-27eae + 'B' // 27eaf #1 + '4uA' // 27eb0-27f2d + 'J' // 27f2e #9 + '5eA' // 27f2f-27fb6 + 'B' // 27fb7 #1 + '2lA' // 27fb8-27ff8 + 'J' // 27ff9 #9 + 'gA' // 27ffa-28001 + 'J' // 28002 #9 + 'eA' // 28003-28008 + 'J' // 28009 #9 + 'sA' // 2800a-2801d + 'J' // 2801e #9 + 'cA' // 2801f-28022 + 'aJ' // 28023-28024 #9 + '1hA' // 28025-28047 + 'J' // 28048 #9 + '2eA' // 28049-28082 + 'J' // 28083 #9 + 'eA' // 28084-28089 + 'B' // 2808a #1 + 'dA' // 2808b-2808f + 'J' // 28090 #9 + '1oA' // 28091-280ba + 'B' // 280bb #1 + 'A' // 280bc + 'aJ' // 280bd-280be #9 + '1nA' // 280bf-280e7 + 'aJ' // 280e8-280e9 #9 + 'iA' // 280ea-280f3 + 'J' // 280f4 #9 + '2dA' // 280f5-2812d + 'J' // 2812e #9 + '1eA' // 2812f-2814e + 'J' // 2814f #9 + 'lA' // 28150-2815c + 'J' // 2815d #9 + 'pA' // 2815e-2816e + 'J' // 2816f #9 + 'xA' // 28170-28188 + 'N' // 28189 #13 + '1jA' // 2818a-281ae + 'N' // 281af #13 + 'kA' // 281b0-281bb + 'N' // 281bc #13 + '2uA' // 281bd-28206 + 'N' // 28207 #13 + 'oA' // 28208-28217 + 'N' // 28218 #13 + 'A' // 28219 + 'N' // 2821a #13 + '2fA' // 2821b-28255 + 'N' // 28256 #13 + '1eA' // 28257-28276 + 'B' // 28277 #1 + 'cA' // 28278-2827b + 'N' // 2827c #13 + 'dA' // 2827d-28281 + 'B' // 28282 #1 + 'wA' // 28283-2829a + 'N' // 2829b #13 + '1vA' // 2829c-282cc + 'N' // 282cd #13 + 'sA' // 282ce-282e1 + '80I' // 282e2 #2088 + 'oA' // 282e3-282f2 + 'B' // 282f3 #1 + 'qA' // 282f4-28305 + 'N' // 28306 #13 + 'pA' // 28307-28317 + 'N' // 28318 #13 + 'uA' // 28319-2832e + 'N' // 2832f #13 + 'iA' // 28330-28339 + 'N' // 2833a #13 + '1oA' // 2833b-28364 + 'N' // 28365 #13 + 'fA' // 28366-2836c + 'N' // 2836d #13 + 'nA' // 2836e-2837c + 'N' // 2837d #13 + 'kA' // 2837e-28389 + 'N' // 2838a #13 + '2mA' // 2838b-283cc + 'B' // 283cd #1 + '2iA' // 283ce-2840b + 'B' // 2840c #1 + 'dA' // 2840d-28411 + 'N' // 28412 #13 + '2mA' // 28413-28454 + 'B' // 28455 #1 + 'qA' // 28456-28467 + 'N' // 28468 #13 + 'bA' // 28469-2846b + 'N' // 2846c #13 + 'eA' // 2846d-28472 + 'N' // 28473 #13 + 'mA' // 28474-28481 + 'N' // 28482 #13 + '3jA' // 28483-284db + 'B' // 284dc #1 + '1iA' // 284dd-28500 + 'N' // 28501 #13 + '2eA' // 28502-2853b + 'aN' // 2853c-2853d #13 + '1rA' // 2853e-2856a + 'B' // 2856b #1 + 'N' // 2856c #13 + '3lA' // 2856d-285c7 + 'aB' // 285c8-285c9 #1 + '1cA' // 285ca-285e7 + 'N' // 285e8 #13 + 'jA' // 285e9-285f3 + 'N' // 285f4 #13 + 'jA' // 285f5-285ff + 'N' // 28600 #13 + 'iA' // 28601-2860a + 'N' // 2860b #13 + 'xA' // 2860c-28624 + 'N' // 28625 #13 + 'tA' // 28626-2863a + 'N' // 2863b #13 + '4eA' // 2863c-286a9 + 'aN' // 286aa-286ab #13 + 'eA' // 286ac-286b1 + 'N' // 286b2 #13 + 'hA' // 286b3-286bb + 'N' // 286bc #13 + 'yA' // 286bd-286d6 + 'B' // 286d7 #1 + 'N' // 286d8 #13 + 'lA' // 286d9-286e5 + 'N' // 286e6 #13 + 'rA' // 286e7-286f9 + 'B' // 286fa #1 + 'sA' // 286fb-2870e + 'N' // 2870f #13 + 'bA' // 28710-28712 + 'N' // 28713 #13 + '9eA' // 28714-28803 + 'N' // 28804 #13 + '1kA' // 28805-2882a + 'N' // 2882b #13 + '8pA' // 2882c-2890c + 'N' // 2890d #13 + '1jA' // 2890e-28932 + 'N' // 28933 #13 + 'qA' // 28934-28945 + 'B' // 28946 #1 + 'A' // 28947 + 'N' // 28948 #13 + '36X' // 28949 #959 + 'kA' // 2894a-28955 + 'N' // 28956 #13 + 'lA' // 28957-28963 + 'N' // 28964 #13 + 'bA' // 28965-28967 + 'N' // 28968 #13 + 'aA' // 28969-2896a + 'B' // 2896b #1 + 'aN' // 2896c-2896d #13 + 'oA' // 2896e-2897d + 'N' // 2897e #13 + 'gA' // 2897f-28986 + 'aB' // 28987-28988 #1 + 'N' // 28989 #13 + '1cA' // 2898a-289a7 + 'N' // 289a8 #13 + 'A' // 289a9 + 'aN' // 289aa-289ab #13 + 'kA' // 289ac-289b7 + 'N' // 289b8 #13 + 'A' // 289b9 + 'aB' // 289ba-289bb #1 + 'N' // 289bc #13 + 'bA' // 289bd-289bf + 'N' // 289c0 #13 + 'zA' // 289c1-289db + 'N' // 289dc #13 + 'A' // 289dd + 'N' // 289de #13 + 'aA' // 289df-289e0 + 'N' // 289e1 #13 + 'A' // 289e2 + 'aN' // 289e3-289e4 #13 + 'aA' // 289e5-289e6 + 'aN' // 289e7-289e8 #13 + 'oA' // 289e9-289f8 + 'cN' // 289f9-289fc #13 + 'qA' // 289fd-28a0e + 'N' // 28a0f #13 + 'eA' // 28a10-28a15 + 'N' // 28a16 #13 + 'fA' // 28a17-28a1d + 'B' // 28a1e #1 + 'eA' // 28a1f-28a24 + 'N' // 28a25 #13 + 'bA' // 28a26-28a28 + '36X' // 28a29 #959 + 'gA' // 28a2a-28a31 + 'N' // 28a32 #13 + 'bA' // 28a33-28a35 + 'N' // 28a36 #13 + 'kA' // 28a37-28a42 + 'B' // 28a43 #1 + 'gN' // 28a44-28a4b #13 + 'lA' // 28a4c-28a58 + 'aN' // 28a59-28a5a #13 + 'uA' // 28a5b-28a70 + 'B' // 28a71 #1 + 'nA' // 28a72-28a80 + 'bN' // 28a81-28a83 #13 + 'tA' // 28a84-28a98 + 'B' // 28a99 #1 + 'bN' // 28a9a-28a9c #13 + '1hA' // 28a9d-28abf + 'N' // 28ac0 #13 + 'dA' // 28ac1-28ac5 + 'N' // 28ac6 #13 + 'cA' // 28ac7-28aca + 'aN' // 28acb-28acc #13 + 'B' // 28acd #1 + 'N' // 28ace #13 + 'mA' // 28acf-28adc + 'B' // 28add #1 + 'eN' // 28ade-28ae3 #13 + 'B' // 28ae4 #1 + 'N' // 28ae5 #13 + 'cA' // 28ae6-28ae9 + 'N' // 28aea #13 + 'pA' // 28aeb-28afb + 'N' // 28afc #13 + 'nA' // 28afd-28b0b + 'N' // 28b0c #13 + 'eA' // 28b0d-28b12 + 'N' // 28b13 #13 + 'lA' // 28b14-28b20 + 'aN' // 28b21-28b22 #13 + 'gA' // 28b23-28b2a + 'bN' // 28b2b-28b2d #13 + 'A' // 28b2e + 'N' // 28b2f #13 + 'uA' // 28b30-28b45 + 'N' // 28b46 #13 + 'dA' // 28b47-28b4b + 'N' // 28b4c #13 + 'A' // 28b4d + 'N' // 28b4e #13 + 'A' // 28b4f + 'N' // 28b50 #13 + 'qA' // 28b51-28b62 + 'cN' // 28b63-28b66 #13 + 'dA' // 28b67-28b6b + 'N' // 28b6c #13 + '1gA' // 28b6d-28b8e + 'N' // 28b8f #13 + 'hA' // 28b90-28b98 + 'N' // 28b99 #13 + 'aA' // 28b9a-28b9b + 'aN' // 28b9c-28b9d #13 + 'zA' // 28b9e-28bb8 + 'N' // 28bb9 #13 + 'fA' // 28bba-28bc0 + 'B' // 28bc1 #1 + 'N' // 28bc2 #13 + 'aA' // 28bc3-28bc4 + 'N' // 28bc5 #13 + 'mA' // 28bc6-28bd3 + 'N' // 28bd4 #13 + 'aA' // 28bd5-28bd6 + 'N' // 28bd7 #13 + 'A' // 28bd8 + 'aN' // 28bd9-28bda #13 + 'kA' // 28bdb-28be6 + 'eN' // 28be7-28bec #13 + 'aA' // 28bed-28bee + 'B' // 28bef #1 + 'dA' // 28bf0-28bf4 + 'N' // 28bf5 #13 + 'hA' // 28bf6-28bfe + 'N' // 28bff #13 + 'bA' // 28c00-28c02 + 'N' // 28c03 #13 + 'dA' // 28c04-28c08 + 'N' // 28c09 #13 + 'qA' // 28c0a-28c1b + 'aN' // 28c1c-28c1d #13 + 'dA' // 28c1e-28c22 + 'N' // 28c23 #13 + 'aA' // 28c24-28c25 + 'N' // 28c26 #13 + 'cA' // 28c27-28c2a + 'N' // 28c2b #13 + 'cA' // 28c2c-28c2f + 'N' // 28c30 #13 + 'gA' // 28c31-28c38 + 'N' // 28c39 #13 + 'A' // 28c3a + 'N' // 28c3b #13 + '5kA' // 28c3c-28cc9 + 'N' // 28cca #13 + 'aA' // 28ccb-28ccc + 'N' // 28ccd #13 + 'cA' // 28cce-28cd1 + 'N' // 28cd2 #13 + 'iA' // 28cd3-28cdc + 'B' // 28cdd #1 + '1wA' // 28cde-28d0f + 'B' // 28d10 #1 + '1hA' // 28d11-28d33 + 'N' // 28d34 #13 + '2gA' // 28d35-28d70 + 'B' // 28d71 #1 + '1lA' // 28d72-28d98 + 'N' // 28d99 #13 + '1dA' // 28d9a-28db8 + 'N' // 28db9 #13 + '2lA' // 28dba-28dfa + 'B' // 28dfb #1 + 'rA' // 28dfc-28e0e + '36X' // 28e0f #959 + 'fA' // 28e10-28e16 + 'B' // 28e17 #1 + 'fA' // 28e18-28e1e + 'B' // 28e1f #1 + 'uA' // 28e20-28e35 + '36X' // 28e36 #959 + 'aA' // 28e37-28e38 + 'N' // 28e39 #13 + '1pA' // 28e3a-28e64 + 'aN' // 28e65-28e66 #13 + '1gA' // 28e67-28e88 + 'B' // 28e89 #1 + 'lA' // 28e8a-28e96 + 'N' // 28e97 #13 + 'sA' // 28e98-28eab + 'N' // 28eac #13 + 'dA' // 28ead-28eb1 + 'aH' // 28eb2-28eb3 #7 + '1jA' // 28eb4-28ed8 + 'H' // 28ed9 #7 + 'lA' // 28eda-28ee6 + 'H' // 28ee7 #7 + 'bA' // 28ee8-28eea + 'B' // 28eeb #1 + 'iA' // 28eec-28ef5 + 'B' // 28ef6 #1 + '2fA' // 28ef7-28f31 + 'B' // 28f32 #1 + '5oA' // 28f33-28fc4 + 'H' // 28fc5 #7 + '1wA' // 28fc6-28ff7 + 'B' // 28ff8 #1 + '4wA' // 28ff9-29078 + 'H' // 29079 #7 + 'mA' // 2907a-29087 + 'H' // 29088 #7 + 'aA' // 29089-2908a + 'H' // 2908b #7 + 'fA' // 2908c-29092 + 'H' // 29093 #7 + 'zA' // 29094-290ae + 'bH' // 290af-290b1 #7 + 'mA' // 290b2-290bf + 'H' // 290c0 #7 + '1hA' // 290c1-290e3 + 'aH' // 290e4-290e5 #7 + 'eA' // 290e6-290eb + 'aH' // 290ec-290ed #7 + '1dA' // 290ee-2910c + 'H' // 2910d #7 + 'aA' // 2910e-2910f + 'H' // 29110 #7 + '1pA' // 29111-2913b + 'H' // 2913c #7 + 'oA' // 2913d-2914c + 'H' // 2914d #7 + 'lA' // 2914e-2915a + 'H' // 2915b #7 + 'aA' // 2915c-2915d + 'H' // 2915e #7 + 'pA' // 2915f-2916f + 'H' // 29170 #7 + '1pA' // 29171-2919b + 'H' // 2919c #7 + 'jA' // 2919d-291a7 + 'H' // 291a8 #7 + '1qA' // 291a9-291d4 + 'H' // 291d5 #7 + 'tA' // 291d6-291ea + 'H' // 291eb #7 + '6wA' // 291ec-2929f + 'B' // 292a0 #1 + 'oA' // 292a1-292b0 + 'B' // 292b1 #1 + '13xA' // 292b2-2941c + 'H' // 2941d #7 + 'aA' // 2941e-2941f + 'H' // 29420 #7 + 'qA' // 29421-29432 + 'H' // 29433 #7 + 'jA' // 29434-2943e + 'H' // 2943f #7 + 'gA' // 29440-29447 + 'H' // 29448 #7 + '2rA' // 29449-2948f + 'B' // 29490 #1 + '2jA' // 29491-294cf + 'H' // 294d0 #7 + 'gA' // 294d1-294d8 + 'aH' // 294d9-294da #7 + 'iA' // 294db-294e4 + 'H' // 294e5 #7 + 'A' // 294e6 + 'H' // 294e7 #7 + '6yA' // 294e8-2959d + 'H' // 2959e #7 + 'pA' // 2959f-295af + 'H' // 295b0 #7 + 'fA' // 295b1-295b7 + 'H' // 295b8 #7 + 'uA' // 295b9-295ce + 'B' // 295cf #1 + 'fA' // 295d0-295d6 + 'H' // 295d7 #7 + 'pA' // 295d8-295e8 + 'H' // 295e9 #7 + 'iA' // 295ea-295f3 + 'H' // 295f4 #7 + '5gA' // 295f5-2967e + 'B' // 2967f #1 + '4gA' // 29680-296ef + 'B' // 296f0 #1 + '1mA' // 296f1-29718 + 'B' // 29719 #1 + 'eA' // 2971a-2971f + 'H' // 29720 #7 + 'pA' // 29721-29731 + 'H' // 29732 #7 + '1bA' // 29733-2974f + 'B' // 29750 #1 + '4zA' // 29751-297d3 + 'H' // 297d4 #7 + '2fA' // 297d5-2980f + '42Y' // 29810 #1116 + '2qA' // 29811-29856 + 'H' // 29857 #7 + '2wA' // 29858-298a3 + 'H' // 298a4 #7 + '1fA' // 298a5-298c5 + 'B' // 298c6 #1 + 'iA' // 298c7-298d0 + 'H' // 298d1 #7 + 'wA' // 298d2-298e9 + 'H' // 298ea #7 + 'eA' // 298eb-298f0 + 'H' // 298f1 #7 + 'gA' // 298f2-298f9 + 'H' // 298fa #7 + 'gA' // 298fb-29902 + 'H' // 29903 #7 + 'A' // 29904 + 'H' // 29905 #7 + '1nA' // 29906-2992e + 'H' // 2992f #7 + 'tA' // 29930-29944 + 'H' // 29945 #7 + 'A' // 29946 + 'bH' // 29947-29949 #7 + 'rA' // 2994a-2995c + 'H' // 2995d #7 + 'kA' // 2995e-29969 + 'H' // 2996a #7 + '1wA' // 2996b-2999c + 'H' // 2999d #7 + '1jA' // 2999e-299c2 + 'H' // 299c3 #7 + 'dA' // 299c4-299c8 + 'H' // 299c9 #7 + '3oA' // 299ca-29a27 + 'H' // 29a28 #7 + '1iA' // 29a29-29a4c + 'H' // 29a4d #7 + '1iA' // 29a4e-29a71 + 'B' // 29a72 #1 + '5oA' // 29a73-29b04 + 'H' // 29b05 #7 + 'gA' // 29b06-29b0d + 'H' // 29b0e #7 + '7oA' // 29b0f-29bd4 + 'H' // 29bd5 #7 + '5zA' // 29bd6-29c72 + 'H' // 29c73 #7 + '2dA' // 29c74-29cac + 'H' // 29cad #7 + '5mA' // 29cae-29d3d + 'H' // 29d3e #7 + 'kA' // 29d3f-29d4a + 'B' // 29d4b #1 + 'mA' // 29d4c-29d59 + '80H' // 29d5a #2087 + '1fA' // 29d5b-29d7b + 'H' // 29d7c #7 + 'zA' // 29d7d-29d97 + 'H' // 29d98 #7 + 'aA' // 29d99-29d9a + 'H' // 29d9b #7 + '2jA' // 29d9c-29dda + 'B' // 29ddb #1 + 'yA' // 29ddc-29df5 + 'H' // 29df6 #7 + 'nA' // 29df7-29e05 + 'H' // 29e06 #7 + 'mA' // 29e07-29e14 + 'B' // 29e15 #1 + 'vA' // 29e16-29e2c + 'H' // 29e2d #7 + 'nA' // 29e2e-29e3c + 'B' // 29e3d #1 + 'jA' // 29e3e-29e48 + 'B' // 29e49 #1 + '1cA' // 29e4a-29e67 + 'H' // 29e68 #7 + '1fA' // 29e69-29e89 + 'B' // 29e8a #1 + '1fA' // 29e8b-29eab + 'H' // 29eac #7 + 'bA' // 29ead-29eaf + 'H' // 29eb0 #7 + 'qA' // 29eb1-29ec2 + 'H' // 29ec3 #7 + 'B' // 29ec4 #1 + 'uA' // 29ec5-29eda + 'B' // 29edb #1 + 'lA' // 29edc-29ee8 + 'B' // 29ee9 #1 + 'mA' // 29eea-29ef7 + 'H' // 29ef8 #7 + '1oA' // 29ef9-29f22 + 'H' // 29f23 #7 + 'kA' // 29f24-29f2f + 'H' // 29f30 #7 + '5cA' // 29f31-29fb6 + 'H' // 29fb7 #7 + 'uA' // 29fb8-29fcd + 'B' // 29fce #1 + 'gA' // 29fcf-29fd6 + 'B' // 29fd7 #1 + 'eA' // 29fd8-29fdd + 'H' // 29fde #7 + '1zA' // 29fdf-2a013 + 'H' // 2a014 #7 + 'dA' // 2a015-2a019 + 'B' // 2a01a #1 + 'sA' // 2a01b-2a02e + 'B' // 2a02f #1 + '3cA' // 2a030-2a081 + 'B' // 2a082 #1 + 'cA' // 2a083-2a086 + 'H' // 2a087 #7 + '1vA' // 2a088-2a0b8 + 'H' // 2a0b9 #7 + '1lA' // 2a0ba-2a0e0 + 'H' // 2a0e1 #7 + 'jA' // 2a0e2-2a0ec + 'H' // 2a0ed #7 + 'dA' // 2a0ee-2a0f2 + 'H' // 2a0f3 #7 + 'cA' // 2a0f4-2a0f7 + 'H' // 2a0f8 #7 + 'B' // 2a0f9 #1 + 'cA' // 2a0fa-2a0fd + 'H' // 2a0fe #7 + 'gA' // 2a0ff-2a106 + 'H' // 2a107 #7 + 'zA' // 2a108-2a122 + 'H' // 2a123 #7 + 'nA' // 2a124-2a132 + 'aH' // 2a133-2a134 #7 + 'zA' // 2a135-2a14f + 'H' // 2a150 #7 + '2jA' // 2a151-2a18f + 'B' // 2a190 #1 + 'A' // 2a191 + 'aH' // 2a192-2a193 #7 + 'vA' // 2a194-2a1aa + 'H' // 2a1ab #7 + 'gA' // 2a1ac-2a1b3 + 'aH' // 2a1b4-2a1b5 #7 + '1nA' // 2a1b6-2a1de + 'H' // 2a1df #7 + 'tA' // 2a1e0-2a1f4 + 'H' // 2a1f5 #7 + '1oA' // 2a1f6-2a21f + 'H' // 2a220 #7 + 'qA' // 2a221-2a232 + 'H' // 2a233 #7 + '3pA' // 2a234-2a292 + 'H' // 2a293 #7 + 'jA' // 2a294-2a29e + 'H' // 2a29f #7 + 'qA' // 2a2a0-2a2b1 + '42Y' // 2a2b2 #1116 + 'A' // 2a2b3 + 'H' // 2a2b4 #7 + 'A' // 2a2b5 + 'H' // 2a2b6 #7 + 'bA' // 2a2b7-2a2b9 + 'H' // 2a2ba #7 + 'aA' // 2a2bb-2a2bc + 'H' // 2a2bd #7 + '1fA' // 2a2be-2a2de + 'H' // 2a2df #7 + '1dA' // 2a2e0-2a2fe + 'H' // 2a2ff #7 + '3bA' // 2a300-2a350 + 'H' // 2a351 #7 + '2eA' // 2a352-2a38b + 'B' // 2a38c #1 + '1aA' // 2a38d-2a3a8 + 'H' // 2a3a9 #7 + '2nA' // 2a3aa-2a3ec + 'H' // 2a3ed #7 + '2qA' // 2a3ee-2a433 + 'H' // 2a434 #7 + 'aA' // 2a435-2a436 + 'B' // 2a437 #1 + '1hA' // 2a438-2a45a + 'H' // 2a45b #7 + '13wA' // 2a45c-2a5c5 + 'H' // 2a5c6 #7 + 'cA' // 2a5c7-2a5ca + 'H' // 2a5cb #7 + '1jA' // 2a5cc-2a5f0 + 'B' // 2a5f1 #1 + 'nA' // 2a5f2-2a600 + 'H' // 2a601 #7 + 'B' // 2a602 #1 + 'vA' // 2a603-2a619 + 'B' // 2a61a #1 + 'vA' // 2a61b-2a631 + 'H' // 2a632 #7 + 'vA' // 2a633-2a649 + 'H' // 2a64a #7 + 'oA' // 2a64b-2a65a + 'H' // 2a65b #7 + '2xA' // 2a65c-2a6a8 + 'H' // 2a6a9 #7 + 'gA' // 2a6aa-2a6b1 + 'B' // 2a6b2 #1 + '31lA' // 2a6b3-2a9e5 + 'B' // 2a9e6 #1 + '40gA' // 2a9e7-2adfe + 'H' // 2adff #7 + '91gA' // 2ae00-2b745 + 'B' // 2b746 #1 + 'iA' // 2b747-2b750 + 'B' // 2b751 #1 + 'A' // 2b752 + 'B' // 2b753 #1 + 'eA' // 2b754-2b759 + 'B' // 2b75a #1 + 'A' // 2b75b + 'B' // 2b75c #1 + 'gA' // 2b75d-2b764 + 'B' // 2b765 #1 + 'oA' // 2b766-2b775 + 'aB' // 2b776-2b777 #1 + 'cA' // 2b778-2b77b + 'B' // 2b77c #1 + 'dA' // 2b77d-2b781 + 'B' // 2b782 #1 + 'eA' // 2b783-2b788 + 'B' // 2b789 #1 + 'A' // 2b78a + 'B' // 2b78b #1 + 'aA' // 2b78c-2b78d + 'B' // 2b78e #1 + 'dA' // 2b78f-2b793 + 'B' // 2b794 #1 + 'vA' // 2b795-2b7ab + 'B' // 2b7ac #1 + 'aA' // 2b7ad-2b7ae + 'B' // 2b7af #1 + 'lA' // 2b7b0-2b7bc + 'B' // 2b7bd #1 + 'jA' // 2b7be-2b7c8 + 'B' // 2b7c9 #1 + 'dA' // 2b7ca-2b7ce + 'B' // 2b7cf #1 + 'aA' // 2b7d0-2b7d1 + 'B' // 2b7d2 #1 + 'dA' // 2b7d3-2b7d7 + 'B' // 2b7d8 #1 + 'vA' // 2b7d9-2b7ef + 'B' // 2b7f0 #1 + '1aA' // 2b7f1-2b80c + 'B' // 2b80d #1 + 'hA' // 2b80e-2b816 + 'B' // 2b817 #1 + 'aA' // 2b818-2b819 + 'B' // 2b81a #1 + '287bA' // 2b81b-2d543 + 'B' // 2d544 #1 + '129xA' // 2d545-2e277 + 'B' // 2e278 #1 + '28wA' // 2e279-2e568 + 'B' // 2e569 #1 + '14sA' // 2e56a-2e6e9 + 'B' // 2e6ea #1 + '168hA' // 2e6eb-2f803 + 'B' // 2f804 #1 + 'iA' // 2f805-2f80e + 'B' // 2f80f #1 + 'dA' // 2f810-2f814 + 'B' // 2f815 #1 + 'aA' // 2f816-2f817 + 'B' // 2f818 #1 + 'A' // 2f819 + 'B' // 2f81a #1 + 'fA' // 2f81b-2f821 + 'B' // 2f822 #1 + 'aA' // 2f823-2f824 + 'H' // 2f825 #7 + 'aA' // 2f826-2f827 + 'B' // 2f828 #1 + 'bA' // 2f829-2f82b + 'B' // 2f82c #1 + 'eA' // 2f82d-2f832 + 'B' // 2f833 #1 + 'fA' // 2f834-2f83a + 'H' // 2f83b #7 + 'bA' // 2f83c-2f83e + 'B' // 2f83f #1 + 'H' // 2f840 #7 + 'dA' // 2f841-2f845 + 'B' // 2f846 #1 + 'jA' // 2f847-2f851 + 'B' // 2f852 #1 + 'nA' // 2f853-2f861 + 'B' // 2f862 #1 + 'iA' // 2f863-2f86c + 'B' // 2f86d #1 + 'dA' // 2f86e-2f872 + 'B' // 2f873 #1 + 'bA' // 2f874-2f876 + 'B' // 2f877 #1 + 'H' // 2f878 #7 + 'jA' // 2f879-2f883 + 'B' // 2f884 #1 + 'nA' // 2f885-2f893 + 'H' // 2f894 #7 + 'cA' // 2f895-2f898 + 'aB' // 2f899-2f89a #1 + 'jA' // 2f89b-2f8a5 + '42Y' // 2f8a6 #1116 + 'dA' // 2f8a7-2f8ab + 'B' // 2f8ac #1 + 'dA' // 2f8ad-2f8b1 + 'B' // 2f8b2 #1 + 'bA' // 2f8b3-2f8b5 + 'B' // 2f8b6 #1 + 'uA' // 2f8b7-2f8cc + 'H' // 2f8cd #7 + 'dA' // 2f8ce-2f8d2 + 'B' // 2f8d3 #1 + 'fA' // 2f8d4-2f8da + 'aB' // 2f8db-2f8dc #1 + 'cA' // 2f8dd-2f8e0 + 'B' // 2f8e1 #1 + 'bA' // 2f8e2-2f8e4 + 'B' // 2f8e5 #1 + 'cA' // 2f8e6-2f8e9 + 'B' // 2f8ea #1 + 'aA' // 2f8eb-2f8ec + 'B' // 2f8ed #1 + 'mA' // 2f8ee-2f8fb + 'B' // 2f8fc #1 + 'eA' // 2f8fd-2f902 + 'B' // 2f903 #1 + 'fA' // 2f904-2f90a + 'B' // 2f90b #1 + 'bA' // 2f90c-2f90e + 'B' // 2f90f #1 + 'iA' // 2f910-2f919 + 'B' // 2f91a #1 + 'dA' // 2f91b-2f91f + 'aB' // 2f920-2f921 #1 + '1hA' // 2f922-2f944 + 'B' // 2f945 #1 + 'A' // 2f946 + 'B' // 2f947 #1 + '1iA' // 2f948-2f96b + 'B' // 2f96c #1 + '1lA' // 2f96d-2f993 + 'H' // 2f994 #7 + 'B' // 2f995 #1 + '1aA' // 2f996-2f9b1 + 'H' // 2f9b2 #7 + 'hA' // 2f9b3-2f9bb + 'H' // 2f9bc #7 + 'rA' // 2f9bd-2f9cf + 'B' // 2f9d0 #1 + 'bA' // 2f9d1-2f9d3 + 'H' // 2f9d4 #7 + 'hA' // 2f9d5-2f9dd + 'aB' // 2f9de-2f9df #1 + 'sA' // 2f9e0-2f9f3 + 'B' // 2f9f4 #1 + '27789zA' // 2f9f5-e0061 + 'a15E' // e0062-e0063 #394 + 'A' // e0064 + '15E' // e0065 #394 + 'A' // e0066 + '15E' // e0067 #394 + 'cA' // e0068-e006b + '15E' // e006c #394 + 'A' // e006d + '15E' // e006e #394 + 'cA' // e006f-e0072 + 'a15E' // e0073-e0074 #394 + 'aA' // e0075-e0076 + '15E' // e0077 #394 + 'fA' // e0078-e007e + '15E' // e007f #394 + '7556wA' // e0080-10ffff ; diff --git a/lib/web_ui/lib/src/engine/font_fallbacks.dart b/lib/web_ui/lib/src/engine/font_fallbacks.dart index bbd1da6196d72..6d11f081fa51b 100644 --- a/lib/web_ui/lib/src/engine/font_fallbacks.dart +++ b/lib/web_ui/lib/src/engine/font_fallbacks.dart @@ -24,9 +24,11 @@ class FontFallbackManager { factory FontFallbackManager(FallbackFontRegistry registry) => FontFallbackManager._(registry, getFallbackFontList()); - FontFallbackManager._(this.registry, this.fallbackFonts) : - _notoSymbols = fallbackFonts.singleWhere((NotoFont font) => font.name == 'Noto Sans Symbols') { - downloadQueue = FallbackFontDownloadQueue(this); + FontFallbackManager._(this.registry, this.fallbackFonts) + : _notoSymbols = fallbackFonts.singleWhere( + (NotoFont font) => font.name == 'Noto Sans Symbols', + ) { + downloadQueue = FallbackFontDownloadQueue(this); } final FallbackFontRegistry registry; @@ -110,8 +112,7 @@ class FontFallbackManager { } final List codePoints = runesToCheck.toList(); - final List missingCodePoints = - registry.getMissingCodePoints(codePoints, fontFamilies); + final List missingCodePoints = registry.getMissingCodePoints(codePoints, fontFamilies); if (missingCodePoints.isNotEmpty) { addMissingCodePoints(codePoints); @@ -172,14 +173,12 @@ class FontFallbackManager { void findFontsForMissingCodePoints(List codePoints) { final List missingCodePoints = []; - final List requiredComponents = - []; + final List requiredComponents = []; final List candidateFonts = []; // Collect the components that cover the code points. for (final int codePoint in codePoints) { - final FallbackFontComponent component = - codePointToComponents.lookup(codePoint); + final FallbackFontComponent component = codePointToComponents.lookup(codePoint); if (component.fonts.isEmpty) { missingCodePoints.add(codePoint); } else { @@ -213,7 +212,7 @@ class FontFallbackManager { // component that is used by the font and adjust the counts of other fonts // that use the same components. for (final FallbackFontComponent component in [ - ...selectedFont.coverComponents + ...selectedFont.coverComponents, ]) { for (final NotoFont font in component.fonts) { font.coverCount -= component.coverCount; @@ -235,9 +234,10 @@ class FontFallbackManager { if (missingCodePoints.isNotEmpty) { if (!downloadQueue.isPending) { printWarning( - 'Could not find a set of Noto fonts to display all missing ' - 'characters. Please add a font asset for the missing characters.' - ' See: https://flutter.dev/docs/cookbook/design/fonts'); + 'Could not find a set of Noto fonts to display all missing ' + 'characters. Please add a font asset for the missing characters.' + ' See: https://flutter.dev/docs/cookbook/design/fonts', + ); codePointsWithNoKnownFont.addAll(missingCodePoints); } } @@ -269,20 +269,20 @@ class FontFallbackManager { if (bestFonts.length > 1) { // If the list of best fonts are all CJK fonts, choose the best one based // on user preferred language. Otherwise just choose the first font. - if (bestFonts.every((NotoFont font) => - _isNotoSansSC(font) || - _isNotoSansTC(font) || - _isNotoSansHK(font) || - _isNotoSansJP(font) || - _isNotoSansKR(font))) { + if (bestFonts.every( + (NotoFont font) => + _isNotoSansSC(font) || + _isNotoSansTC(font) || + _isNotoSansHK(font) || + _isNotoSansJP(font) || + _isNotoSansKR(font), + )) { if (_language == 'zh-Hans' || _language == 'zh-CN' || _language == 'zh-SG' || _language == 'zh-MY') { bestFontForLanguage = bestFonts.firstWhereOrNull(_isNotoSansSC); - } else if (_language == 'zh-Hant' || - _language == 'zh-TW' || - _language == 'zh-MO') { + } else if (_language == 'zh-Hant' || _language == 'zh-TW' || _language == 'zh-MO') { bestFontForLanguage = bestFonts.firstWhereOrNull(_isNotoSansTC); } else if (_language == 'zh-HK') { bestFontForLanguage = bestFonts.firstWhereOrNull(_isNotoSansHK); @@ -310,17 +310,18 @@ class FontFallbackManager { return bestFontForLanguage ?? bestFont!; } - late final List fontComponents = - _decodeFontComponents(encodedFontSets); + late final List fontComponents = _decodeFontComponents(encodedFontSets); late final _UnicodePropertyLookup codePointToComponents = _UnicodePropertyLookup.fromPackedData( - encodedFontSetRanges, fontComponents); + encodedFontSetRanges, + fontComponents, + ); List _decodeFontComponents(String data) { return [ for (final String componentData in data.split(',')) - FallbackFontComponent(_decodeFontSet(componentData)) + FallbackFontComponent(_decodeFontSet(componentData)), ]; } @@ -331,8 +332,7 @@ class FontFallbackManager { for (int i = 0; i < data.length; i++) { final int code = data.codeUnitAt(i); - if (kFontIndexDigit0 <= code && - code < kFontIndexDigit0 + kFontIndexRadix) { + if (kFontIndexDigit0 <= code && code < kFontIndexDigit0 + kFontIndexRadix) { final int delta = prefix * kFontIndexRadix + (code - kFontIndexDigit0); final int index = previousIndex + delta + 1; result.add(fallbackFonts[index]); @@ -352,10 +352,7 @@ class FontFallbackManager { class _UnicodePropertyLookup

{ _UnicodePropertyLookup._(this._boundaries, this._values); - factory _UnicodePropertyLookup.fromPackedData( - String packedData, - List

propertyEnumValues, - ) { + factory _UnicodePropertyLookup.fromPackedData(String packedData, List

propertyEnumValues) { final List boundaries = []; final List

values =

[]; @@ -365,18 +362,15 @@ class _UnicodePropertyLookup

{ for (int i = 0; i < packedData.length; i++) { final int code = packedData.codeUnitAt(i); - if (kRangeValueDigit0 <= code && - code < kRangeValueDigit0 + kRangeValueRadix) { - final int index = - prefix * kRangeValueRadix + (code - kRangeValueDigit0); + if (kRangeValueDigit0 <= code && code < kRangeValueDigit0 + kRangeValueRadix) { + final int index = prefix * kRangeValueRadix + (code - kRangeValueDigit0); final P value = propertyEnumValues[index]; start += size; boundaries.add(start); values.add(value); prefix = 0; size = 1; - } else if (kRangeSizeDigit0 <= code && - code < kRangeSizeDigit0 + kRangeSizeRadix) { + } else if (kRangeSizeDigit0 <= code && code < kRangeSizeDigit0 + kRangeSizeRadix) { size = prefix * kRangeSizeRadix + (code - kRangeSizeDigit0) + 2; prefix = 0; } else if (kPrefixDigit0 <= code && code < kPrefixDigit0 + kPrefixRadix) { @@ -491,8 +485,10 @@ class FallbackFontDownloadQueue { downloadedFontFamilies.add(font.url); } catch (e) { pendingFonts.remove(font.url); - printWarning('Failed to load font ${font.name} at ' - '$fallbackFontUrlPrefix${font.url}'); + printWarning( + 'Failed to load font ${font.name} at ' + '$fallbackFontUrlPrefix${font.url}', + ); printWarning(e.toString()); return; } @@ -512,8 +508,7 @@ class FallbackFontDownloadQueue { } if (pendingFonts.isEmpty) { - fallbackManager.registry - .updateFallbackFontFamilies(fallbackManager.globalFontFallbacks); + fallbackManager.registry.updateFallbackFontFamilies(fallbackManager.globalFontFallbacks); sendFontChangeMessage(); final Completer idleCompleter = _idleCompleter!; _idleCompleter = null; diff --git a/lib/web_ui/lib/src/engine/fonts.dart b/lib/web_ui/lib/src/engine/fonts.dart index 3d51de4fb33a6..a5df8b3bac7d8 100644 --- a/lib/web_ui/lib/src/engine/fonts.dart +++ b/lib/web_ui/lib/src/engine/fonts.dart @@ -31,7 +31,8 @@ class FontManifest { } Future fetchFontManifest(ui_web.AssetManager assetManager) async { - final HttpFetchResponse response = await assetManager.loadAsset('FontManifest.json') as HttpFetchResponse; + final HttpFetchResponse response = + await assetManager.loadAsset('FontManifest.json') as HttpFetchResponse; if (!response.hasPayload) { printWarning('Font manifest does not exist at `${response.url}` - ignoring.'); return FontManifest([]); @@ -40,41 +41,44 @@ Future fetchFontManifest(ui_web.AssetManager assetManager) async { final Converter, Object?> decoder = const Utf8Decoder().fuse(const JsonDecoder()); Object? fontManifestJson; final Sink> inputSink = decoder.startChunkedConversion( - ChunkedConversionSink.withCallback( - (List accumulated) { - if (accumulated.length != 1) { - throw AssertionError('There was a problem trying to load FontManifest.json'); - } - fontManifestJson = accumulated.first; + ChunkedConversionSink.withCallback((List accumulated) { + if (accumulated.length != 1) { + throw AssertionError('There was a problem trying to load FontManifest.json'); } - )); + fontManifestJson = accumulated.first; + }), + ); await response.read((JSUint8Array chunk) => inputSink.add(chunk.toDart)); inputSink.close(); if (fontManifestJson == null) { throw AssertionError('There was a problem trying to load FontManifest.json'); } - final List families = (fontManifestJson! as List).map( - (dynamic fontFamilyJson) { - final Map fontFamily = fontFamilyJson as Map; - final String familyName = fontFamily.readString('family'); - final List fontAssets = fontFamily.readList('fonts'); - return FontFamily(familyName, fontAssets.map((dynamic fontAssetJson) { - String? asset; - final Map descriptors = {}; - for (final MapEntry descriptor in (fontAssetJson as Map).entries) { - if (descriptor.key == 'asset') { - asset = descriptor.value as String; - } else { - // Sometimes these descriptors are strings, and sometimes numbers, so we stringify them here. - descriptors[descriptor.key] = '${descriptor.value}'; - } - } - if (asset == null) { - throw AssertionError("Invalid Font manifest, missing 'asset' key on font."); - } - return FontAsset(asset, descriptors); - }).toList()); - }).toList(); + final List families = + (fontManifestJson! as List).map((dynamic fontFamilyJson) { + final Map fontFamily = fontFamilyJson as Map; + final String familyName = fontFamily.readString('family'); + final List fontAssets = fontFamily.readList('fonts'); + return FontFamily( + familyName, + fontAssets.map((dynamic fontAssetJson) { + String? asset; + final Map descriptors = {}; + for (final MapEntry descriptor + in (fontAssetJson as Map).entries) { + if (descriptor.key == 'asset') { + asset = descriptor.value as String; + } else { + // Sometimes these descriptors are strings, and sometimes numbers, so we stringify them here. + descriptors[descriptor.key] = '${descriptor.value}'; + } + } + if (asset == null) { + throw AssertionError("Invalid Font manifest, missing 'asset' key on font."); + } + return FontAsset(asset, descriptors); + }).toList(), + ); + }).toList(); return FontManifest(families); } diff --git a/lib/web_ui/lib/src/engine/frame_timing_recorder.dart b/lib/web_ui/lib/src/engine/frame_timing_recorder.dart index ec2944bb41327..0f329ab21158d 100644 --- a/lib/web_ui/lib/src/engine/frame_timing_recorder.dart +++ b/lib/web_ui/lib/src/engine/frame_timing_recorder.dart @@ -38,6 +38,7 @@ class FrameTimingRecorder { /// The last time (in microseconds) we submitted frame timings. static int _frameTimingsLastSubmitTime = _nowMicros(); + /// The amount of time in microseconds we wait between submitting /// frame timings. static const int _kFrameTimingsSubmitInterval = 100000; // 100 milliseconds @@ -76,10 +77,8 @@ class FrameTimingRecorder { void submitTimings() { assert( - _buildFinishMicros != null && - _rasterStartMicros != null && - _rasterFinishMicros != null, - 'Attempted to submit an incomplete timings.' + _buildFinishMicros != null && _rasterStartMicros != null && _rasterFinishMicros != null, + 'Attempted to submit an incomplete timings.', ); final ui.FrameTiming timing = ui.FrameTiming( vsyncStart: _vsyncStartMicros, diff --git a/lib/web_ui/lib/src/engine/html/backdrop_filter.dart b/lib/web_ui/lib/src/engine/html/backdrop_filter.dart index 1ab5427e907df..d0b883f428fa7 100644 --- a/lib/web_ui/lib/src/engine/html/backdrop_filter.dart +++ b/lib/web_ui/lib/src/engine/html/backdrop_filter.dart @@ -56,7 +56,9 @@ class PersistedBackdropFilter extends PersistedContainerSurface } _filterElement = defaultCreateElement('flt-backdrop-filter'); _filterElement!.style.transformOrigin = '0 0 0'; - element..append(_filterElement!)..append(_childContainer!); + element + ..append(_filterElement!) + ..append(_childContainer!); return element; } @@ -95,9 +97,14 @@ class PersistedBackdropFilter extends PersistedContainerSurface // Therefore we need to use parent clip element bounds for // backdrop boundary. final double dpr = ui.window.devicePixelRatio; - final ui.Rect rect = _invertedTransform.transformRect(ui.Rect.fromLTRB(0, 0, + final ui.Rect rect = _invertedTransform.transformRect( + ui.Rect.fromLTRB( + 0, + 0, ui.window.physicalSize.width * dpr, - ui.window.physicalSize.height * dpr)); + ui.window.physicalSize.height * dpr, + ), + ); double left = rect.left; double top = rect.top; double width = rect.width; @@ -131,9 +138,10 @@ class PersistedBackdropFilter extends PersistedContainerSurface } else { if (backendFilter is ModeHtmlColorFilter) { _svgFilter = backendFilter.makeSvgFilter(_filterElement); + /// Some blendModes do not make an svgFilter. See [EngineHtmlColorFilter.makeSvgFilter()] if (_svgFilter == null) { - return; + return; } } else if (backendFilter is MatrixHtmlColorFilter) { _svgFilter = backendFilter.makeSvgFilter(_filterElement); @@ -143,8 +151,7 @@ class PersistedBackdropFilter extends PersistedContainerSurface // Gaussian blur with standard deviation (normal distribution), // the blur will fall within 2 * sigma pixels. if (ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit) { - setElementStyle(_filterElement!, '-webkit-backdrop-filter', - backendFilter.filterAttribute); + setElementStyle(_filterElement!, '-webkit-backdrop-filter', backendFilter.filterAttribute); } setElementStyle(_filterElement!, 'backdrop-filter', backendFilter.filterAttribute); } diff --git a/lib/web_ui/lib/src/engine/html/bitmap_canvas.dart b/lib/web_ui/lib/src/engine/html/bitmap_canvas.dart index 5d21eed1cf5dd..edbf7a5105d64 100644 --- a/lib/web_ui/lib/src/engine/html/bitmap_canvas.dart +++ b/lib/web_ui/lib/src/engine/html/bitmap_canvas.dart @@ -35,14 +35,16 @@ class BitmapCanvas extends EngineCanvas { /// This canvas can be reused by pictures with different paint bounds as long /// as the [Rect.size] of the bounds fully fit within the size used to /// initialize this canvas. - BitmapCanvas(this._bounds, RenderStrategy renderStrategy, - {double density = 1.0}) - : _density = density, - _renderStrategy = renderStrategy, - widthInBitmapPixels = widthToPhysical(_bounds.width), - heightInBitmapPixels = heightToPhysical(_bounds.height), - _canvasPool = CanvasPool(widthToPhysical(_bounds.width), - heightToPhysical(_bounds.height), density) { + BitmapCanvas(this._bounds, RenderStrategy renderStrategy, {double density = 1.0}) + : _density = density, + _renderStrategy = renderStrategy, + widthInBitmapPixels = widthToPhysical(_bounds.width), + heightInBitmapPixels = heightToPhysical(_bounds.height), + _canvasPool = CanvasPool( + widthToPhysical(_bounds.width), + heightToPhysical(_bounds.height), + density, + ) { rootElement.style.position = 'absolute'; // Adds one extra pixel to the requested size. This is to compensate for // _initializeViewport() snapping canvas position to 1 pixel, causing @@ -70,8 +72,7 @@ class BitmapCanvas extends EngineCanvas { _bounds = newValue; final int newCanvasPositionX = _bounds.left.floor() - kPaddingPixels; final int newCanvasPositionY = _bounds.top.floor() - kPaddingPixels; - if (_canvasPositionX != newCanvasPositionX || - _canvasPositionY != newCanvasPositionY) { + if (_canvasPositionX != newCanvasPositionX || _canvasPositionY != newCanvasPositionY) { _canvasPositionX = newCanvasPositionX; _canvasPositionY = newCanvasPositionY; _updateRootElementTransform(); @@ -124,8 +125,7 @@ class BitmapCanvas extends EngineCanvas { /// Keeps track of what device pixel ratio was used when this [BitmapCanvas] /// was created. - final double _devicePixelRatio = - EngineFlutterDisplay.instance.browserDevicePixelRatio; + final double _devicePixelRatio = EngineFlutterDisplay.instance.browserDevicePixelRatio; // Compensation for [_initializeViewport] snapping canvas position to 1 pixel. int? _canvasPositionX, _canvasPositionY; @@ -180,17 +180,14 @@ class BitmapCanvas extends EngineCanvas { // lands on the physical pixel. // TODO(yjbanov): !This is not accurate if there are // transforms higher up in the stack. - rootElement.style.transform = - 'translate(${_canvasPositionX}px, ${_canvasPositionY}px)'; + rootElement.style.transform = 'translate(${_canvasPositionX}px, ${_canvasPositionY}px)'; } void _setupInitialTransform() { - final double canvasPositionCorrectionX = _bounds.left - - BitmapCanvas.kPaddingPixels - - _canvasPositionX!.toDouble(); - final double canvasPositionCorrectionY = _bounds.top - - BitmapCanvas.kPaddingPixels - - _canvasPositionY!.toDouble(); + final double canvasPositionCorrectionX = + _bounds.left - BitmapCanvas.kPaddingPixels - _canvasPositionX!.toDouble(); + final double canvasPositionCorrectionY = + _bounds.top - BitmapCanvas.kPaddingPixels - _canvasPositionY!.toDouble(); // This compensates for the translate on the `rootElement`. _canvasPool.initialTransform = ui.Offset( -_bounds.left + canvasPositionCorrectionX + BitmapCanvas.kPaddingPixels, @@ -200,16 +197,13 @@ class BitmapCanvas extends EngineCanvas { static int widthToPhysical(double width) { final double boundsWidth = width + 1; - return (boundsWidth * EngineFlutterDisplay.instance.browserDevicePixelRatio) - .ceil() + + return (boundsWidth * EngineFlutterDisplay.instance.browserDevicePixelRatio).ceil() + 2 * kPaddingPixels; } static int heightToPhysical(double height) { final double boundsHeight = height + 1; - return (boundsHeight * - EngineFlutterDisplay.instance.browserDevicePixelRatio) - .ceil() + + return (boundsHeight * EngineFlutterDisplay.instance.browserDevicePixelRatio).ceil() + 2 * kPaddingPixels; } @@ -253,8 +247,7 @@ class BitmapCanvas extends EngineCanvas { /// * [PersistedPicture._recycleCanvas] which also uses this method /// for the same reason. bool isReusable() { - return _devicePixelRatio == - EngineFlutterDisplay.instance.browserDevicePixelRatio; + return _devicePixelRatio == EngineFlutterDisplay.instance.browserDevicePixelRatio; } /// Returns a "data://" URI containing a representation of the image in this @@ -384,9 +377,7 @@ class BitmapCanvas extends EngineCanvas { } return _renderStrategy.isInsideSvgFilterTree || _contains3dTransform || - ((_childOverdraw || - _renderStrategy.hasImageElements || - _renderStrategy.hasParagraphs) && + ((_childOverdraw || _renderStrategy.hasImageElements || _renderStrategy.hasParagraphs) && !_canvasPool.hasCanvas && paint.maskFilter == null && paint.shader == null); @@ -394,9 +385,10 @@ class BitmapCanvas extends EngineCanvas { @override void drawColor(ui.Color color, ui.BlendMode blendMode) { - final SurfacePaintData paintData = SurfacePaintData() - ..color = color.value - ..blendMode = blendMode; + final SurfacePaintData paintData = + SurfacePaintData() + ..color = color.value + ..blendMode = blendMode; if (_useDomForRenderingFill(paintData)) { drawRect(_computeScreenBounds(_canvasPool.currentTransform), paintData); } else { @@ -407,13 +399,13 @@ class BitmapCanvas extends EngineCanvas { @override void drawLine(ui.Offset p1, ui.Offset p2, SurfacePaintData paint) { if (_useDomForRenderingFill(paint)) { - final SurfacePath path = SurfacePath() - ..moveTo(p1.dx, p1.dy) - ..lineTo(p2.dx, p2.dy); + final SurfacePath path = + SurfacePath() + ..moveTo(p1.dx, p1.dy) + ..lineTo(p2.dx, p2.dy); drawPath(path, paint); } else { - final ui.Rect? shaderBounds = - (paint.shader != null) ? ui.Rect.fromPoints(p1, p2) : null; + final ui.Rect? shaderBounds = (paint.shader != null) ? ui.Rect.fromPoints(p1, p2) : null; setUpPaint(paint, shaderBounds); _canvasPool.strokeLine(p1, p2); tearDownPaint(); @@ -425,8 +417,7 @@ class BitmapCanvas extends EngineCanvas { if (_useDomForRenderingFill(paint)) { drawRect(_computeScreenBounds(_canvasPool.currentTransform), paint); } else { - final ui.Rect? shaderBounds = - (paint.shader != null) ? _computePictureBounds() : null; + final ui.Rect? shaderBounds = (paint.shader != null) ? _computePictureBounds() : null; setUpPaint(paint, shaderBounds); _canvasPool.fill(); tearDownPaint(); @@ -438,7 +429,11 @@ class BitmapCanvas extends EngineCanvas { if (_useDomForRenderingFillAndStroke(paint)) { rect = adjustRectForDom(rect, paint); final DomHTMLElement element = buildDrawRectElement( - rect, paint, 'draw-rect', _canvasPool.currentTransform); + rect, + paint, + 'draw-rect', + _canvasPool.currentTransform, + ); _drawElement(element, rect.topLeft, paint); } else { setUpPaint(paint, rect); @@ -449,14 +444,14 @@ class BitmapCanvas extends EngineCanvas { /// Inserts a dom element at [offset] creating stack of divs for clipping /// if required. - void _drawElement( - DomElement element, ui.Offset offset, SurfacePaintData paint) { + void _drawElement(DomElement element, ui.Offset offset, SurfacePaintData paint) { if (_canvasPool.isClipped) { final List clipElements = _clipContent( - _canvasPool.clipStack!, - element, - ui.Offset.zero, - transformWithOffset(_canvasPool.currentTransform, offset)); + _canvasPool.clipStack!, + element, + ui.Offset.zero, + transformWithOffset(_canvasPool.currentTransform, offset), + ); for (final DomElement clipElement in clipElements) { rootElement.append(clipElement); _children.add(clipElement); @@ -478,7 +473,11 @@ class BitmapCanvas extends EngineCanvas { if (_useDomForRenderingFillAndStroke(paint)) { final ui.Rect rect = adjustRectForDom(rrect.outerRect, paint); final DomHTMLElement element = buildDrawRectElement( - rect, paint, 'draw-rrect', _canvasPool.currentTransform); + rect, + paint, + 'draw-rrect', + _canvasPool.currentTransform, + ); applyRRectBorderRadius(element.style, rrect); _drawElement(element, rect.topLeft, paint); } else { @@ -500,10 +499,13 @@ class BitmapCanvas extends EngineCanvas { if (_useDomForRenderingFill(paint)) { rect = adjustRectForDom(rect, paint); final DomHTMLElement element = buildDrawRectElement( - rect, paint, 'draw-oval', _canvasPool.currentTransform); + rect, + paint, + 'draw-oval', + _canvasPool.currentTransform, + ); _drawElement(element, rect.topLeft, paint); - element.style.borderRadius = - '${rect.width / 2.0}px / ${rect.height / 2.0}px'; + element.style.borderRadius = '${rect.width / 2.0}px / ${rect.height / 2.0}px'; } else { setUpPaint(paint, rect); _canvasPool.drawOval(rect, paint.style); @@ -514,18 +516,20 @@ class BitmapCanvas extends EngineCanvas { @override void drawCircle(ui.Offset c, double radius, SurfacePaintData paint) { if (_useDomForRenderingFillAndStroke(paint)) { - final ui.Rect rect = adjustRectForDom( - ui.Rect.fromCircle(center: c, radius: radius), paint); + final ui.Rect rect = adjustRectForDom(ui.Rect.fromCircle(center: c, radius: radius), paint); final DomHTMLElement element = buildDrawRectElement( - rect, paint, 'draw-circle', _canvasPool.currentTransform); + rect, + paint, + 'draw-circle', + _canvasPool.currentTransform, + ); _drawElement(element, rect.topLeft, paint); element.style.borderRadius = '50%'; } else { setUpPaint( - paint, - paint.shader != null - ? ui.Rect.fromCircle(center: c, radius: radius) - : null); + paint, + paint.shader != null ? ui.Rect.fromCircle(center: c, radius: radius) : null, + ); _canvasPool.drawCircle(c, radius, paint.style); tearDownPaint(); } @@ -575,8 +579,7 @@ class BitmapCanvas extends EngineCanvas { final bool isStroke = paint.style == ui.PaintingStyle.stroke; final String cssColor = colorValueToCssString(paint.color); final double sigma = paint.maskFilter!.webOnlySigma; - if (ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit && - !isStroke) { + if (ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit && !isStroke) { // A bug in webkit leaves artifacts when this element is animated // with filter: blur, we use boxShadow instead. element.style.boxShadow = '0px 0px ${sigma * 2.0}px $cssColor'; @@ -587,8 +590,7 @@ class BitmapCanvas extends EngineCanvas { } @override - void drawShadow(ui.Path path, ui.Color color, double elevation, - bool transparentOccluder) { + void drawShadow(ui.Path path, ui.Color color, double elevation, bool transparentOccluder) { _canvasPool.drawShadow(path, color, elevation, transparentOccluder); } @@ -596,8 +598,7 @@ class BitmapCanvas extends EngineCanvas { void drawImage(ui.Image image, ui.Offset p, SurfacePaintData paint) { final DomHTMLElement imageElement = _drawImage(image, p, paint); if (paint.colorFilter != null) { - _applyTargetSize( - imageElement, image.width.toDouble(), image.height.toDouble()); + _applyTargetSize(imageElement, image.width.toDouble(), image.height.toDouble()); } if (!_preserveImageData) { _closeCanvas(); @@ -625,19 +626,20 @@ class BitmapCanvas extends EngineCanvas { element.remove(); } - DomHTMLElement _drawImage( - ui.Image image, ui.Offset p, SurfacePaintData paint) { + DomHTMLElement _drawImage(ui.Image image, ui.Offset p, SurfacePaintData paint) { final HtmlImage htmlImage = image as HtmlImage; final ui.BlendMode? blendMode = paint.blendMode; - final EngineHtmlColorFilter? colorFilter = - createHtmlColorFilter(paint.colorFilter); + final EngineHtmlColorFilter? colorFilter = createHtmlColorFilter(paint.colorFilter); DomHTMLElement imgElement; if (colorFilter is ModeHtmlColorFilter) { imgElement = _createImageElementWithBlend( - image, colorFilter.color, colorFilter.blendMode, paint); + image, + colorFilter.color, + colorFilter.blendMode, + paint, + ); } else if (colorFilter is MatrixHtmlColorFilter) { - imgElement = _createImageElementWithSvgColorMatrixFilter( - image, colorFilter.matrix, paint); + imgElement = _createImageElementWithSvgColorMatrixFilter(image, colorFilter.matrix, paint); } else { // No Blending, create an image by cloning original loaded image. imgElement = _reuseOrCreateImage(htmlImage); @@ -657,17 +659,19 @@ class BitmapCanvas extends EngineCanvas { ..removeProperty('width') ..removeProperty('height'); final List clipElements = _clipContent( - _canvasPool.clipStack!, - imgElement, - p, - _canvasPool.currentTransform); + _canvasPool.clipStack!, + imgElement, + p, + _canvasPool.currentTransform, + ); for (final DomElement clipElement in clipElements) { rootElement.append(clipElement); _children.add(clipElement); } } else { final String cssTransform = float64ListToCssTransform( - transformWithOffset(_canvasPool.currentTransform, p).storage); + transformWithOffset(_canvasPool.currentTransform, p).storage, + ); imgElement.style ..transformOrigin = '0 0 0' ..transform = cssTransform @@ -681,8 +685,12 @@ class BitmapCanvas extends EngineCanvas { return imgElement; } - DomHTMLElement _createImageElementWithBlend(HtmlImage image, ui.Color color, - ui.BlendMode blendMode, SurfacePaintData paint) { + DomHTMLElement _createImageElementWithBlend( + HtmlImage image, + ui.Color color, + ui.BlendMode blendMode, + SurfacePaintData paint, + ) { switch (blendMode) { case ui.BlendMode.colorBurn: case ui.BlendMode.colorDodge: @@ -698,20 +706,16 @@ class BitmapCanvas extends EngineCanvas { case ui.BlendMode.luminosity: case ui.BlendMode.xor: case ui.BlendMode.dstATop: - return _createImageElementWithSvgBlendFilter( - image, color, blendMode, paint); + return _createImageElementWithSvgBlendFilter(image, color, blendMode, paint); default: return _createBackgroundImageWithBlend(image, color, blendMode, paint); } } @override - void drawImageRect( - ui.Image image, ui.Rect src, ui.Rect dst, SurfacePaintData paint) { - final bool requiresClipping = src.left != 0 || - src.top != 0 || - src.width != image.width || - src.height != image.height; + void drawImageRect(ui.Image image, ui.Rect src, ui.Rect dst, SurfacePaintData paint) { + final bool requiresClipping = + src.left != 0 || src.top != 0 || src.width != image.width || src.height != image.height; // If source and destination sizes are identical, we can skip the longer // code path that sets the size of the element and clips. // @@ -741,8 +745,7 @@ class BitmapCanvas extends EngineCanvas { } } - final DomElement imgElement = - _drawImage(image, ui.Offset(targetLeft, targetTop), paint); + final DomElement imgElement = _drawImage(image, ui.Offset(targetLeft, targetTop), paint); // To scale set width / height on destination image. // For clipping we need to scale according to // clipped-width/full image width and shift it according to left/top of @@ -761,8 +764,7 @@ class BitmapCanvas extends EngineCanvas { _closeCanvas(); } - void _applyTargetSize( - DomHTMLElement imageElement, double targetWidth, double targetHeight) { + void _applyTargetSize(DomHTMLElement imageElement, double targetWidth, double targetHeight) { final DomCSSStyleDeclaration imageStyle = imageElement.style; final String widthPx = '${targetWidth.toStringAsFixed(2)}px'; final String heightPx = '${targetHeight.toStringAsFixed(2)}px'; @@ -793,10 +795,11 @@ class BitmapCanvas extends EngineCanvas { // For src,srcOver it only sets background-color attribute. // For dst,dstIn , it only sets source not background color. DomHTMLElement _createBackgroundImageWithBlend( - HtmlImage image, - ui.Color? filterColor, - ui.BlendMode colorFilterBlendMode, - SurfacePaintData paint) { + HtmlImage image, + ui.Color? filterColor, + ui.BlendMode colorFilterBlendMode, + SurfacePaintData paint, + ) { // When blending with color we can't use an image element. // Instead use a div element with background image, color and // background blend mode. @@ -820,8 +823,7 @@ class BitmapCanvas extends EngineCanvas { style ..position = 'absolute' ..backgroundImage = "url('${image.imgElement.src}')" - ..backgroundBlendMode = - blendModeToCssMixBlendMode(colorFilterBlendMode) ?? '' + ..backgroundBlendMode = blendModeToCssMixBlendMode(colorFilterBlendMode) ?? '' ..backgroundColor = filterColor!.toCssString(); } return imgElement; @@ -829,13 +831,13 @@ class BitmapCanvas extends EngineCanvas { // Creates an image element and an svg filter to apply on the element. DomHTMLElement _createImageElementWithSvgBlendFilter( - HtmlImage image, - ui.Color? filterColor, - ui.BlendMode colorFilterBlendMode, - SurfacePaintData paint) { + HtmlImage image, + ui.Color? filterColor, + ui.BlendMode colorFilterBlendMode, + SurfacePaintData paint, + ) { // For srcIn blendMode, we use an svg filter to apply to image element. - final SvgFilter svgFilter = - svgFilterFromBlendMode(filterColor, colorFilterBlendMode); + final SvgFilter svgFilter = svgFilterFromBlendMode(filterColor, colorFilterBlendMode); rootElement.append(svgFilter.element); _children.add(svgFilter.element); final DomHTMLElement imgElement = _reuseOrCreateImage(image); @@ -848,7 +850,10 @@ class BitmapCanvas extends EngineCanvas { // Creates an image element and an svg color matrix filter to apply on the element. DomHTMLElement _createImageElementWithSvgColorMatrixFilter( - HtmlImage image, List matrix, SurfacePaintData paint) { + HtmlImage image, + List matrix, + SurfacePaintData paint, + ) { // For srcIn blendMode, we use an svg filter to apply to image element. final SvgFilter svgFilter = svgFilterFromColorMatrix(matrix); rootElement.append(svgFilter.element); @@ -897,8 +902,13 @@ class BitmapCanvas extends EngineCanvas { /// /// The text is drawn starting at coordinates ([x], [y]). It uses the current /// font set by the most recent call to [setCssFont]. - void drawText(String text, double x, double y, - {ui.PaintingStyle? style, List? shadows}) { + void drawText( + String text, + double x, + double y, { + ui.PaintingStyle? style, + List? shadows, + }) { final DomCanvasRenderingContext2D ctx = _canvasPool.context; if (shadows != null) { ctx.save(); @@ -937,13 +947,14 @@ class BitmapCanvas extends EngineCanvas { // Cannot composite if the paragraph cannot be drawn into bitmap canvas // in the first place. paragraph.canDrawOnCanvas && - // Cannot composite if there's no bitmap canvas to composite into. + // Cannot composite if there's no bitmap canvas to composite into. // Creating a new bitmap canvas just to draw text doesn't make sense. - _canvasPool.hasCanvas && - !_childOverdraw && - // Bitmap canvas introduces correctness issues in the presence of SVG - // filters, so prefer plain HTML in this case. - !_renderStrategy.isInsideSvgFilterTree; + _canvasPool + .hasCanvas && + !_childOverdraw && + // Bitmap canvas introduces correctness issues in the presence of SVG + // filters, so prefer plain HTML in this case. + !_renderStrategy.isInsideSvgFilterTree; if (canCompositeIntoBitmapCanvas) { paragraph.paint(this, offset); @@ -952,8 +963,12 @@ class BitmapCanvas extends EngineCanvas { final DomElement paragraphElement = drawParagraphElement(paragraph, offset); if (_canvasPool.isClipped) { - final List clipElements = _clipContent(_canvasPool.clipStack!, - paragraphElement, offset, _canvasPool.currentTransform); + final List clipElements = _clipContent( + _canvasPool.clipStack!, + paragraphElement, + offset, + _canvasPool.currentTransform, + ); for (final DomElement clipElement in clipElements) { rootElement.append(clipElement); _children.add(clipElement); @@ -986,23 +1001,23 @@ class BitmapCanvas extends EngineCanvas { /// If colors is specified, convert colors to premultiplied (alpha) colors /// and use a SkTriColorShader to render. @override - void drawVertices(SurfaceVertices vertices, ui.BlendMode blendMode, - SurfacePaintData paint) { + void drawVertices(SurfaceVertices vertices, ui.BlendMode blendMode, SurfacePaintData paint) { // TODO(ferhat): Implement shaders for [Paint.shader] and // blendMode. https://github.com/flutter/flutter/issues/40096 // Move rendering to OffscreenCanvas so that transform is preserved // as well. - assert(paint.shader == null || paint.shader is EngineImageShader, - 'Linear/Radial/SweepGradient not supported yet'); + assert( + paint.shader == null || paint.shader is EngineImageShader, + 'Linear/Radial/SweepGradient not supported yet', + ); final Int32List? colors = vertices.colors; final ui.VertexMode mode = vertices.mode; final DomCanvasRenderingContext2D ctx = _canvasPool.context; - if (colors == null && - paint.style != ui.PaintingStyle.fill && - paint.shader == null) { - final Float32List positions = mode == ui.VertexMode.triangles - ? vertices.positions - : convertVertexPositions(mode, vertices.positions); + if (colors == null && paint.style != ui.PaintingStyle.fill && paint.shader == null) { + final Float32List positions = + mode == ui.VertexMode.triangles + ? vertices.positions + : convertVertexPositions(mode, vertices.positions); // Draw hairline for vertices if no vertex colors are specified. save(); final ui.Color color = ui.Color(paint.color); @@ -1013,21 +1028,28 @@ class BitmapCanvas extends EngineCanvas { restore(); return; } - glRenderer!.drawVertices(ctx, widthInBitmapPixels, heightInBitmapPixels, - _canvasPool.currentTransform, vertices, blendMode, paint); + glRenderer!.drawVertices( + ctx, + widthInBitmapPixels, + heightInBitmapPixels, + _canvasPool.currentTransform, + vertices, + blendMode, + paint, + ); } /// Stores paint data used by [drawPoints]. We cannot use the original paint /// data object because painting style is determined by [ui.PointMode] and /// not by [SurfacePointData.style]. - static final SurfacePaintData _drawPointsPaint = SurfacePaintData() - ..strokeCap = ui.StrokeCap.round - ..strokeJoin = ui.StrokeJoin.round - ..blendMode = ui.BlendMode.srcOver; + static final SurfacePaintData _drawPointsPaint = + SurfacePaintData() + ..strokeCap = ui.StrokeCap.round + ..strokeJoin = ui.StrokeJoin.round + ..blendMode = ui.BlendMode.srcOver; @override - void drawPoints( - ui.PointMode pointMode, Float32List points, SurfacePaintData paint) { + void drawPoints(ui.PointMode pointMode, Float32List points, SurfacePaintData paint) { if (pointMode == ui.PointMode.points) { _drawPointsPaint.style = ui.PaintingStyle.stroke; } else { @@ -1038,8 +1060,7 @@ class BitmapCanvas extends EngineCanvas { final double dpr = EngineFlutterDisplay.instance.devicePixelRatio; // Use hairline (device pixel when strokeWidth is not specified). - final double strokeWidth = - paint.strokeWidth == null ? 1.0 / dpr : paint.strokeWidth!; + final double strokeWidth = paint.strokeWidth == null ? 1.0 / dpr : paint.strokeWidth!; _drawPointsPaint.strokeWidth = strokeWidth; setUpPaint(_drawPointsPaint, null); // Draw point using circle with half radius. @@ -1051,13 +1072,12 @@ class BitmapCanvas extends EngineCanvas { void endOfPaint() { _canvasPool.endOfPaint(); _elementCache?.commitFrame(); - if (_contains3dTransform && - ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit) { + if (_contains3dTransform && ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit) { // Copy the children list to avoid concurrent modification. final List children = rootElement.children.toList(); for (final DomElement element in children) { - final DomHTMLDivElement paintOrderElement = createDomHTMLDivElement() - ..style.transform = 'translate3d(0,0,0)'; + final DomHTMLDivElement paintOrderElement = + createDomHTMLDivElement()..style.transform = 'translate3d(0,0,0)'; paintOrderElement.append(element); rootElement.append(paintOrderElement); _children.add(paintOrderElement); @@ -1082,21 +1102,14 @@ class BitmapCanvas extends EngineCanvas { final double width = ui.window.physicalSize.width * dpr; final double height = ui.window.physicalSize.height * dpr; final Vector3 topLeft = inverted.perspectiveTransform(x: 0, y: 0, z: 0); - final Vector3 topRight = - inverted.perspectiveTransform(x: width, y: 0, z: 0); - final Vector3 bottomRight = - inverted.perspectiveTransform(x: width, y: height, z: 0); - final Vector3 bottomLeft = - inverted.perspectiveTransform(x: 0, y: height, z: 0); + final Vector3 topRight = inverted.perspectiveTransform(x: width, y: 0, z: 0); + final Vector3 bottomRight = inverted.perspectiveTransform(x: width, y: height, z: 0); + final Vector3 bottomLeft = inverted.perspectiveTransform(x: 0, y: height, z: 0); return ui.Rect.fromLTRB( - math.min(topLeft.x, - math.min(topRight.x, math.min(bottomRight.x, bottomLeft.x))), - math.min(topLeft.y, - math.min(topRight.y, math.min(bottomRight.y, bottomLeft.y))), - math.max(topLeft.x, - math.max(topRight.x, math.max(bottomRight.x, bottomLeft.x))), - math.max(topLeft.y, - math.max(topRight.y, math.max(bottomRight.y, bottomLeft.y))), + math.min(topLeft.x, math.min(topRight.x, math.min(bottomRight.x, bottomLeft.x))), + math.min(topLeft.y, math.min(topRight.y, math.min(bottomRight.y, bottomLeft.y))), + math.max(topLeft.x, math.max(topRight.x, math.max(bottomRight.x, bottomLeft.x))), + math.max(topLeft.y, math.max(topRight.y, math.max(bottomRight.y, bottomLeft.y))), ); } @@ -1175,8 +1188,7 @@ String? blendModeToCssMixBlendMode(ui.BlendMode? blendMode) { case ui.BlendMode.luminosity: return 'luminosity'; default: - throw UnimplementedError( - 'Flutter Web does not support the blend mode: $blendMode'); + throw UnimplementedError('Flutter Web does not support the blend mode: $blendMode'); } } @@ -1258,17 +1270,13 @@ SvgBlendMode? blendModeToSvgEnum(ui.BlendMode? blendMode) { case ui.BlendMode.srcATop: return const SvgBlendMode(kCompositeSourceAtop, SVG_FEBLEND_MODE_UNKNOWN); case ui.BlendMode.dstOver: - return const SvgBlendMode( - kCompositeDestinationOver, SVG_FEBLEND_MODE_UNKNOWN); + return const SvgBlendMode(kCompositeDestinationOver, SVG_FEBLEND_MODE_UNKNOWN); case ui.BlendMode.dstIn: - return const SvgBlendMode( - kCompositeDestinationIn, SVG_FEBLEND_MODE_UNKNOWN); + return const SvgBlendMode(kCompositeDestinationIn, SVG_FEBLEND_MODE_UNKNOWN); case ui.BlendMode.dstOut: - return const SvgBlendMode( - kCompositeDestinationOut, SVG_FEBLEND_MODE_UNKNOWN); + return const SvgBlendMode(kCompositeDestinationOut, SVG_FEBLEND_MODE_UNKNOWN); case ui.BlendMode.dstATop: - return const SvgBlendMode( - kCompositeDestinationAtop, SVG_FEBLEND_MODE_UNKNOWN); + return const SvgBlendMode(kCompositeDestinationAtop, SVG_FEBLEND_MODE_UNKNOWN); case ui.BlendMode.plus: return const SvgBlendMode(kCompositeLighter, SVG_FEBLEND_MODE_UNKNOWN); case ui.BlendMode.src: @@ -1279,8 +1287,7 @@ SvgBlendMode? blendModeToSvgEnum(ui.BlendMode? blendMode) { // Falling back to multiply, ignoring alpha channel. // TODO(ferhat): only used for debug, find better fallback for web. case ui.BlendMode.modulate: - return const SvgBlendMode( - kCompositeSourceOver, SVG_FEBLEND_MODE_MULTIPLY); + return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_MULTIPLY); case ui.BlendMode.screen: return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_SCREEN); case ui.BlendMode.overlay: @@ -1290,38 +1297,27 @@ SvgBlendMode? blendModeToSvgEnum(ui.BlendMode? blendMode) { case ui.BlendMode.lighten: return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_LIGHTEN); case ui.BlendMode.colorDodge: - return const SvgBlendMode( - kCompositeSourceOver, SVG_FEBLEND_MODE_COLOR_DODGE); + return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_COLOR_DODGE); case ui.BlendMode.colorBurn: - return const SvgBlendMode( - kCompositeSourceOver, SVG_FEBLEND_MODE_COLOR_BURN); + return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_COLOR_BURN); case ui.BlendMode.hardLight: - return const SvgBlendMode( - kCompositeSourceOver, SVG_FEBLEND_MODE_HARD_LIGHT); + return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_HARD_LIGHT); case ui.BlendMode.softLight: - return const SvgBlendMode( - kCompositeSourceOver, SVG_FEBLEND_MODE_SOFT_LIGHT); + return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_SOFT_LIGHT); case ui.BlendMode.difference: - return const SvgBlendMode( - kCompositeSourceOver, SVG_FEBLEND_MODE_DIFFERENCE); + return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_DIFFERENCE); case ui.BlendMode.exclusion: - return const SvgBlendMode( - kCompositeSourceOver, SVG_FEBLEND_MODE_EXCLUSION); + return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_EXCLUSION); case ui.BlendMode.hue: return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_HUE); case ui.BlendMode.saturation: - return const SvgBlendMode( - kCompositeSourceOver, SVG_FEBLEND_MODE_SATURATION); + return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_SATURATION); case ui.BlendMode.color: return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_COLOR); case ui.BlendMode.luminosity: - return const SvgBlendMode( - kCompositeSourceOver, SVG_FEBLEND_MODE_LUMINOSITY); + return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_LUMINOSITY); default: - assert( - false, - 'Flutter Web does not support the blend mode: $blendMode', - ); + assert(false, 'Flutter Web does not support the blend mode: $blendMode'); return const SvgBlendMode(kCompositeSourceOver, SVG_FEBLEND_MODE_NORMAL); } @@ -1359,8 +1355,12 @@ String stringForStrokeJoin(ui.StrokeJoin strokeJoin) { /// overflow:hidden with bounds to clip child or sets a clip-path to clip /// it's contents. The clipping rectangles are nested and returned together /// with a list of svg elements that provide clip-paths. -List _clipContent(List clipStack, DomElement content, - ui.Offset offset, Matrix4 currentTransform) { +List _clipContent( + List clipStack, + DomElement content, + ui.Offset offset, + Matrix4 currentTransform, +) { DomElement? root, curElement; final List clipDefs = []; final int len = clipStack.length; @@ -1377,14 +1377,12 @@ List _clipContent(List clipStack, DomElement content, curElement = newElement; final ui.Rect? rect = entry.rect; Matrix4 newClipTransform = entry.currentTransform; - final TransformKind transformKind = - transformKindOf(newClipTransform.storage); + final TransformKind transformKind = transformKindOf(newClipTransform.storage); final bool requiresTransformStyle = transformKind == TransformKind.complex; if (rect != null) { final double clipOffsetX = rect.left; final double clipOffsetY = rect.top; - newClipTransform = newClipTransform.clone() - ..translate(clipOffsetX, clipOffsetY); + newClipTransform = newClipTransform.clone()..translate(clipOffsetX, clipOffsetY); curElement.style ..overflow = 'hidden' ..width = '${rect.right - clipOffsetX}px' @@ -1397,8 +1395,7 @@ List _clipContent(List clipStack, DomElement content, '${roundRect.brRadiusX}px ${roundRect.blRadiusX}px'; final double clipOffsetX = roundRect.left; final double clipOffsetY = roundRect.top; - newClipTransform = newClipTransform.clone() - ..translate(clipOffsetX, clipOffsetY); + newClipTransform = newClipTransform.clone()..translate(clipOffsetX, clipOffsetY); curElement.style ..borderRadius = borderRadius ..overflow = 'hidden' @@ -1414,8 +1411,7 @@ List _clipContent(List clipStack, DomElement content, final ui.Rect ovalBounds = surfacePath.getBounds(); final double clipOffsetX = ovalBounds.left; final double clipOffsetY = ovalBounds.top; - newClipTransform = newClipTransform.clone() - ..translate(clipOffsetX, clipOffsetY); + newClipTransform = newClipTransform.clone()..translate(clipOffsetX, clipOffsetY); curElement.style ..overflow = 'hidden' ..width = '${ovalBounds.width}px' @@ -1427,8 +1423,7 @@ List _clipContent(List clipStack, DomElement content, curElement.style ..transform = matrix4ToCssTransform(newClipTransform) ..transformOrigin = '0 0 0'; - final DomElement clipElement = - createSvgClipDef(curElement, entry.path!); + final DomElement clipElement = createSvgClipDef(curElement, entry.path!); clipDefs.add(clipElement); } } @@ -1438,10 +1433,7 @@ List _clipContent(List clipStack, DomElement content, // reduce number of div nodes by merging (multiplying transforms). final DomElement reverseTransformDiv = createDomHTMLDivElement(); reverseTransformDiv.style.position = 'absolute'; - setElementTransform( - reverseTransformDiv, - (newClipTransform.clone()..invert()).storage, - ); + setElementTransform(reverseTransformDiv, (newClipTransform.clone()..invert()).storage); if (requiresTransformStyle) { // Instead of flattening matrix3d, preserve so it can be reversed. curElement.style.transformStyle = 'preserve-3d'; @@ -1453,10 +1445,7 @@ List _clipContent(List clipStack, DomElement content, root!.style.position = 'absolute'; curElement!.append(content); - setElementTransform( - content, - transformWithOffset(currentTransform, offset).storage, - ); + setElementTransform(content, transformWithOffset(currentTransform, offset).storage); return [root, ...clipDefs]; } diff --git a/lib/web_ui/lib/src/engine/html/canvas.dart b/lib/web_ui/lib/src/engine/html/canvas.dart index 220756f482bdf..4925d41e8daae 100644 --- a/lib/web_ui/lib/src/engine/html/canvas.dart +++ b/lib/web_ui/lib/src/engine/html/canvas.dart @@ -18,8 +18,7 @@ import 'render_vertices.dart'; class SurfaceCanvas implements ui.Canvas { factory SurfaceCanvas(EnginePictureRecorder recorder, [ui.Rect? cullRect]) { if (recorder.isRecording) { - throw ArgumentError( - '"recorder" must not already be associated with another Canvas.'); + throw ArgumentError('"recorder" must not already be associated with another Canvas.'); } cullRect ??= ui.Rect.largest; return SurfaceCanvas._(recorder.beginRecording(cullRect)); @@ -105,8 +104,7 @@ class SurfaceCanvas implements ui.Canvas { } @override - void clipRect(ui.Rect rect, - {ui.ClipOp clipOp = ui.ClipOp.intersect, bool doAntiAlias = true}) { + void clipRect(ui.Rect rect, {ui.ClipOp clipOp = ui.ClipOp.intersect, bool doAntiAlias = true}) { assert(rectIsValid(rect)); _clipRect(rect, clipOp, doAntiAlias); } @@ -243,16 +241,14 @@ class SurfaceCanvas implements ui.Canvas { } @override - void drawArc(ui.Rect rect, double startAngle, double sweepAngle, - bool useCenter, ui.Paint paint) { + void drawArc(ui.Rect rect, double startAngle, double sweepAngle, bool useCenter, ui.Paint paint) { assert(rectIsValid(rect)); const double pi = math.pi; const double pi2 = 2.0 * pi; final ui.Path path = ui.Path(); if (useCenter) { - path.moveTo( - (rect.left + rect.right) / 2.0, (rect.top + rect.bottom) / 2.0); + path.moveTo((rect.left + rect.right) / 2.0, (rect.top + rect.bottom) / 2.0); } bool forceMoveTo = !useCenter; if (sweepAngle <= -pi2) { @@ -304,8 +300,7 @@ class SurfaceCanvas implements ui.Canvas { _drawImageRect(image, src, dst, paint); } - void _drawImageRect( - ui.Image image, ui.Rect src, ui.Rect dst, ui.Paint paint) { + void _drawImageRect(ui.Image image, ui.Rect src, ui.Rect dst, ui.Paint paint) { _canvas.drawImageRect(image, src, dst, paint as SurfacePaint); } @@ -314,14 +309,21 @@ class SurfaceCanvas implements ui.Canvas { // source (image) and dest (screen) in the order (src0, dst0, src1, dst1). // The area from src0 => src1 of the image is painted on the screen from dst0 => dst1 // The slices for each dimension are generated independently. - List _initSlices(double img0, double imgC0, double imgC1, double img1, double dst0, double dst1) { + List _initSlices( + double img0, + double imgC0, + double imgC1, + double img1, + double dst0, + double dst1, + ) { final double imageDim = img1 - img0; final double destDim = dst1 - dst0; if (imageDim == destDim) { // If the src and dest are the same size then we do not need scaling // We return 4 values for a single slice - return [ img0, dst0, img1, dst1 ]; + return [img0, dst0, img1, dst1]; } final double edge0Dim = imgC0 - img0; @@ -333,26 +335,18 @@ class SurfaceCanvas implements ui.Canvas { // center position in the destination // this produces only 2 slices which is 8 values final double dstC = dst0 + destDim * edge0Dim / edgesDim; - return [ - img0, dst0, imgC0, dstC, - imgC1, dstC, img1, dst1, - ]; + return [img0, dst0, imgC0, dstC, imgC1, dstC, img1, dst1]; } // center portion is nonEmpty and only that part is scaled // we need 3 slices which is 12 values final double dstC0 = dst0 + edge0Dim; final double dstC1 = dst1 - edge1Dim; - return [ - img0, dst0, imgC0, dstC0, - imgC0, dstC0, imgC1, dstC1, - imgC1, dstC1, img1, dst1 - ]; + return [img0, dst0, imgC0, dstC0, imgC0, dstC0, imgC1, dstC1, imgC1, dstC1, img1, dst1]; } @override - void drawImageNine( - ui.Image image, ui.Rect center, ui.Rect dst, ui.Paint paint) { + void drawImageNine(ui.Image image, ui.Rect center, ui.Rect dst, ui.Paint paint) { assert(rectIsValid(center)); assert(rectIsValid(dst)); @@ -413,15 +407,13 @@ class SurfaceCanvas implements ui.Canvas { } @override - void drawPoints( - ui.PointMode pointMode, List points, ui.Paint paint) { + void drawPoints(ui.PointMode pointMode, List points, ui.Paint paint) { final Float32List pointList = offsetListToFloat32List(points); drawRawPoints(pointMode, pointList, paint); } @override - void drawRawPoints( - ui.PointMode pointMode, Float32List points, ui.Paint paint) { + void drawRawPoints(ui.PointMode pointMode, Float32List points, ui.Paint paint) { if (points.length % 2 != 0) { throw ArgumentError('"points" must have an even number of values.'); } @@ -429,10 +421,8 @@ class SurfaceCanvas implements ui.Canvas { } @override - void drawVertices( - ui.Vertices vertices, ui.BlendMode blendMode, ui.Paint paint) { - _canvas.drawVertices( - vertices as SurfaceVertices, blendMode, paint as SurfacePaint); + void drawVertices(ui.Vertices vertices, ui.BlendMode blendMode, ui.Paint paint) { + _canvas.drawVertices(vertices as SurfaceVertices, blendMode, paint as SurfacePaint); } @override @@ -453,7 +443,8 @@ class SurfaceCanvas implements ui.Canvas { } if (colors != null && colors.isNotEmpty && colors.length != rectCount) { throw ArgumentError( - 'If non-null, "colors" length must match that of "transforms" and "rects".'); + 'If non-null, "colors" length must match that of "transforms" and "rects".', + ); } // TODO(het): Do we need to support this? @@ -477,12 +468,12 @@ class SurfaceCanvas implements ui.Canvas { throw ArgumentError('"rstTransforms" and "rects" lengths must match.'); } if (rectCount % 4 != 0) { - throw ArgumentError( - '"rstTransforms" and "rects" lengths must be a multiple of four.'); + throw ArgumentError('"rstTransforms" and "rects" lengths must be a multiple of four.'); } if (colors != null && colors.length * 4 != rectCount) { throw ArgumentError( - 'If non-null, "colors" length must be one fourth the length of "rstTransforms" and "rects".'); + 'If non-null, "colors" length must be one fourth the length of "rstTransforms" and "rects".', + ); } // TODO(het): Do we need to support this? @@ -490,12 +481,7 @@ class SurfaceCanvas implements ui.Canvas { } @override - void drawShadow( - ui.Path path, - ui.Color color, - double elevation, - bool transparentOccluder, - ) { + void drawShadow(ui.Path path, ui.Color color, double elevation, bool transparentOccluder) { _canvas.drawShadow(path, color, elevation, transparentOccluder); } } diff --git a/lib/web_ui/lib/src/engine/html/clip.dart b/lib/web_ui/lib/src/engine/html/clip.dart index d713a48f86d89..b75461003c37b 100644 --- a/lib/web_ui/lib/src/engine/html/clip.dart +++ b/lib/web_ui/lib/src/engine/html/clip.dart @@ -129,7 +129,7 @@ class PersistedClipRRect extends PersistedContainerSurface with _DomClip implements ui.ClipRRectEngineLayer { PersistedClipRRect(ui.EngineLayer? oldLayer, this.rrect, this.clipBehavior) - : super(oldLayer as PersistedSurface?); + : super(oldLayer as PersistedSurface?); final ui.RRect rrect; // TODO(yjbanov): can this be controlled in the browser? @@ -187,10 +187,8 @@ class PersistedClipRRect extends PersistedContainerSurface } /// A surface that clips it's children. -class PersistedClipPath extends PersistedContainerSurface - implements ui.ClipPathEngineLayer { - PersistedClipPath( - PersistedClipPath? super.oldLayer, this.clipPath, this.clipBehavior); +class PersistedClipPath extends PersistedContainerSurface implements ui.ClipPathEngineLayer { + PersistedClipPath(PersistedClipPath? super.oldLayer, this.clipPath, this.clipBehavior); final ui.Path clipPath; final ui.Clip clipBehavior; @@ -245,8 +243,11 @@ class PersistedClipPath extends PersistedContainerSurface /// Creates an svg clipPath and applies it to [element]. SVGSVGElement createSvgClipDef(DomElement element, ui.Path clipPath) { final ui.Rect pathBounds = clipPath.getBounds(); - final SVGSVGElement svgClipPath = pathToSvgClipPath(clipPath, - scaleX: 1.0 / pathBounds.right, scaleY: 1.0 / pathBounds.bottom); + final SVGSVGElement svgClipPath = pathToSvgClipPath( + clipPath, + scaleX: 1.0 / pathBounds.right, + scaleY: 1.0 / pathBounds.bottom, + ); setClipPath(element, createSvgClipUrl()); // We need to set width and height for the clipElement to cover the // bounds of the path since browsers such as Safari and Edge diff --git a/lib/web_ui/lib/src/engine/html/color_filter.dart b/lib/web_ui/lib/src/engine/html/color_filter.dart index 819e488040246..eb04b40bc13c5 100644 --- a/lib/web_ui/lib/src/engine/html/color_filter.dart +++ b/lib/web_ui/lib/src/engine/html/color_filter.dart @@ -16,8 +16,7 @@ import 'shaders/shader.dart'; import 'surface.dart'; /// A surface that applies an [ColorFilter] to its children. -class PersistedColorFilter extends PersistedContainerSurface - implements ui.ColorFilterEngineLayer { +class PersistedColorFilter extends PersistedContainerSurface implements ui.ColorFilterEngineLayer { PersistedColorFilter(PersistedColorFilter? super.oldLayer, this.filter); @override @@ -114,8 +113,7 @@ class PersistedColorFilter extends PersistedContainerSurface } } -SvgFilter svgFilterFromBlendMode( - ui.Color? filterColor, ui.BlendMode colorFilterBlendMode) { +SvgFilter svgFilterFromBlendMode(ui.Color? filterColor, ui.BlendMode colorFilterBlendMode) { final SvgFilter svgFilter; switch (colorFilterBlendMode) { case ui.BlendMode.srcIn: @@ -166,8 +164,7 @@ SvgFilter svgFilterFromBlendMode( case ui.BlendMode.softLight: case ui.BlendMode.difference: case ui.BlendMode.exclusion: - svgFilter = _blendColorFilterToSvg( - filterColor, blendModeToSvgEnum(colorFilterBlendMode)!); + svgFilter = _blendColorFilterToSvg(filterColor, blendModeToSvgEnum(colorFilterBlendMode)!); case ui.BlendMode.src: case ui.BlendMode.dst: case ui.BlendMode.dstIn: @@ -175,9 +172,7 @@ SvgFilter svgFilterFromBlendMode( case ui.BlendMode.dstOver: case ui.BlendMode.clear: case ui.BlendMode.srcOver: - throw UnimplementedError( - 'Blend mode not supported in HTML renderer: $colorFilterBlendMode', - ); + throw UnimplementedError('Blend mode not supported in HTML renderer: $colorFilterBlendMode'); } return svgFilter; } @@ -213,15 +208,14 @@ class SvgFilterBuilder { static int _filterIdCounter = 0; final String id; - final SVGSVGElement root = kSvgResourceHeader.cloneNode(false) as - SVGSVGElement; + final SVGSVGElement root = kSvgResourceHeader.cloneNode(false) as SVGSVGElement; final SVGFilterElement filter = createSVGFilterElement(); set colorInterpolationFilters(String filters) { filter.setAttribute('color-interpolation-filters', filters); } - void setFeColorMatrix(List matrix, { required String result }) { + void setFeColorMatrix(List matrix, {required String result}) { final SVGFEColorMatrixElement element = createSVGFEColorMatrixElement(); element.type!.baseVal = kMatrixType; element.result!.baseVal = result; @@ -244,11 +238,7 @@ class SvgFilterBuilder { filter.append(element); } - void setFeBlend({ - required String in1, - required String in2, - required int mode, - }) { + void setFeBlend({required String in1, required String in2, required int mode}) { final SVGFEBlendElement element = createSVGFEBlendElement(); element.in1!.baseVal = in1; element.in2!.baseVal = in2; @@ -342,20 +332,29 @@ SvgFilter svgFilterFromColorMatrix(List matrix) { SvgFilter _srcInColorFilterToSvg(ui.Color? color) { final SvgFilterBuilder builder = SvgFilterBuilder(); builder.colorInterpolationFilters = 'sRGB'; - builder.setFeColorMatrix( - const [ - 0, 0, 0, 0, 1, - 0, 0, 0, 0, 1, - 0, 0, 0, 0, 1, - 0, 0, 0, 1, 0, - ], - result: 'destalpha', - ); - builder.setFeFlood( - floodColor: color?.toCssString() ?? '', - floodOpacity: '1', - result: 'flood', - ); + builder.setFeColorMatrix(const [ + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + ], result: 'destalpha'); + builder.setFeFlood(floodColor: color?.toCssString() ?? '', floodOpacity: '1', result: 'flood'); builder.setFeComposite( in1: 'flood', in2: 'destalpha', @@ -373,11 +372,7 @@ SvgFilter _srcInColorFilterToSvg(ui.Color? color) { /// replaces the destination. dst-atop CR = CB*αB*αA+CA*αA*(1-αB) αR=αA SvgFilter _dstATopColorFilterToSvg(ui.Color? color) { final SvgFilterBuilder builder = SvgFilterBuilder(); - builder.setFeFlood( - floodColor: color?.toCssString() ?? '', - floodOpacity: '1', - result: 'flood', - ); + builder.setFeFlood(floodColor: color?.toCssString() ?? '', floodOpacity: '1', result: 'flood'); builder.setFeComposite( in1: 'SourceGraphic', in2: 'flood', @@ -389,11 +384,7 @@ SvgFilter _dstATopColorFilterToSvg(ui.Color? color) { SvgFilter _srcOutColorFilterToSvg(ui.Color? color) { final SvgFilterBuilder builder = SvgFilterBuilder(); - builder.setFeFlood( - floodColor: color?.toCssString() ?? '', - floodOpacity: '1', - result: 'flood', - ); + builder.setFeFlood(floodColor: color?.toCssString() ?? '', floodOpacity: '1', result: 'flood'); builder.setFeComposite( in1: 'flood', in2: 'SourceGraphic', @@ -405,11 +396,7 @@ SvgFilter _srcOutColorFilterToSvg(ui.Color? color) { SvgFilter _xorColorFilterToSvg(ui.Color? color) { final SvgFilterBuilder builder = SvgFilterBuilder(); - builder.setFeFlood( - floodColor: color?.toCssString() ?? '', - floodOpacity: '1', - result: 'flood', - ); + builder.setFeFlood(floodColor: color?.toCssString() ?? '', floodOpacity: '1', result: 'flood'); builder.setFeComposite( in1: 'flood', in2: 'SourceGraphic', @@ -421,14 +408,9 @@ SvgFilter _xorColorFilterToSvg(ui.Color? color) { // The source image and color are composited using : // result = k1 *in*in2 + k2*in + k3*in2 + k4. -SvgFilter _compositeColorFilterToSvg( - ui.Color? color, double k1, double k2, double k3, double k4) { +SvgFilter _compositeColorFilterToSvg(ui.Color? color, double k1, double k2, double k3, double k4) { final SvgFilterBuilder builder = SvgFilterBuilder(); - builder.setFeFlood( - floodColor: color?.toCssString() ?? '', - floodOpacity: '1', - result: 'flood', - ); + builder.setFeFlood(floodColor: color?.toCssString() ?? '', floodOpacity: '1', result: 'flood'); builder.setFeComposite( in1: 'flood', in2: 'SourceGraphic', @@ -451,15 +433,28 @@ SvgFilter _modulateColorFilterToSvg(ui.Color color) { final double g = color.green / 255.0; final SvgFilterBuilder builder = SvgFilterBuilder(); - builder.setFeColorMatrix( - [ - 0, 0, 0, 0, r, - 0, 0, 0, 0, g, - 0, 0, 0, 0, b, - 0, 0, 0, 1, 0, - ], - result: 'recolor', - ); + builder.setFeColorMatrix([ + 0, + 0, + 0, + 0, + r, + 0, + 0, + 0, + 0, + g, + 0, + 0, + 0, + 0, + b, + 0, + 0, + 0, + 1, + 0, + ], result: 'recolor'); builder.setFeComposite( in1: 'recolor', in2: 'SourceGraphic', @@ -474,26 +469,17 @@ SvgFilter _modulateColorFilterToSvg(ui.Color color) { } // Uses feBlend element to blend source image with a color. -SvgFilter _blendColorFilterToSvg(ui.Color? color, SvgBlendMode svgBlendMode, - {bool swapLayers = false}) { +SvgFilter _blendColorFilterToSvg( + ui.Color? color, + SvgBlendMode svgBlendMode, { + bool swapLayers = false, +}) { final SvgFilterBuilder builder = SvgFilterBuilder(); - builder.setFeFlood( - floodColor: color?.toCssString() ?? '', - floodOpacity: '1', - result: 'flood', - ); + builder.setFeFlood(floodColor: color?.toCssString() ?? '', floodOpacity: '1', result: 'flood'); if (swapLayers) { - builder.setFeBlend( - in1: 'SourceGraphic', - in2: 'flood', - mode: svgBlendMode.blendMode, - ); + builder.setFeBlend(in1: 'SourceGraphic', in2: 'flood', mode: svgBlendMode.blendMode); } else { - builder.setFeBlend( - in1: 'flood', - in2: 'SourceGraphic', - mode: svgBlendMode.blendMode, - ); + builder.setFeBlend(in1: 'flood', in2: 'SourceGraphic', mode: svgBlendMode.blendMode); } return builder.build(); } diff --git a/lib/web_ui/lib/src/engine/html/debug_canvas_reuse_overlay.dart b/lib/web_ui/lib/src/engine/html/debug_canvas_reuse_overlay.dart index 87f2b8b798e72..fbe08011c79c7 100644 --- a/lib/web_ui/lib/src/engine/html/debug_canvas_reuse_overlay.dart +++ b/lib/web_ui/lib/src/engine/html/debug_canvas_reuse_overlay.dart @@ -60,13 +60,11 @@ class DebugCanvasReuseOverlay { ..append(_inRecycle), ) ..append( - createDomHTMLDivElement() - ..append( - createDomHTMLButtonElement() - ..text = 'Reset' - ..addEventListener('click', - createDomEventListener((_) => _reset())), - ), + createDomHTMLDivElement()..append( + createDomHTMLButtonElement() + ..text = 'Reset' + ..addEventListener('click', createDomEventListener((_) => _reset())), + ), ), ); } @@ -134,8 +132,7 @@ class DebugCanvasReuseOverlay { } void _reset() { - _createdCount = - _keptCount = _reusedCount = _disposedCount = _inRecycleCount = 0; + _createdCount = _keptCount = _reusedCount = _disposedCount = _inRecycleCount = 0; _update(); } } diff --git a/lib/web_ui/lib/src/engine/html/dom_canvas.dart b/lib/web_ui/lib/src/engine/html/dom_canvas.dart index c1d0e091233c8..b03830652491c 100644 --- a/lib/web_ui/lib/src/engine/html/dom_canvas.dart +++ b/lib/web_ui/lib/src/engine/html/dom_canvas.dart @@ -77,15 +77,18 @@ class DomCanvas extends EngineCanvas with SaveElementStackTracking { @override void drawRect(ui.Rect rect, SurfacePaintData paint) { rect = adjustRectForDom(rect, paint); - currentElement.append( - buildDrawRectElement(rect, paint, 'draw-rect', currentTransform)); + currentElement.append(buildDrawRectElement(rect, paint, 'draw-rect', currentTransform)); } @override void drawRRect(ui.RRect rrect, SurfacePaintData paint) { final ui.Rect outerRect = adjustRectForDom(rrect.outerRect, paint); final DomElement element = buildDrawRectElement( - outerRect, paint, 'draw-rrect', currentTransform); + outerRect, + paint, + 'draw-rrect', + currentTransform, + ); applyRRectBorderRadius(element.style, rrect); currentElement.append(element); } @@ -111,8 +114,7 @@ class DomCanvas extends EngineCanvas with SaveElementStackTracking { } @override - void drawShadow(ui.Path path, ui.Color color, double elevation, - bool transparentOccluder) { + void drawShadow(ui.Path path, ui.Color color, double elevation, bool transparentOccluder) { throw UnimplementedError(); } @@ -122,28 +124,27 @@ class DomCanvas extends EngineCanvas with SaveElementStackTracking { } @override - void drawImageRect( - ui.Image image, ui.Rect src, ui.Rect dst, SurfacePaintData paint) { + void drawImageRect(ui.Image image, ui.Rect src, ui.Rect dst, SurfacePaintData paint) { throw UnimplementedError(); } @override void drawParagraph(ui.Paragraph paragraph, ui.Offset offset) { final DomElement paragraphElement = drawParagraphElement( - paragraph as CanvasParagraph, offset, - transform: currentTransform); + paragraph as CanvasParagraph, + offset, + transform: currentTransform, + ); currentElement.append(paragraphElement); } @override - void drawVertices( - ui.Vertices vertices, ui.BlendMode blendMode, SurfacePaintData paint) { + void drawVertices(ui.Vertices vertices, ui.BlendMode blendMode, SurfacePaintData paint) { throw UnimplementedError(); } @override - void drawPoints( - ui.PointMode pointMode, Float32List points, SurfacePaintData paint) { + void drawPoints(ui.PointMode pointMode, Float32List points, SurfacePaintData paint) { throw UnimplementedError(); } @@ -221,21 +222,21 @@ ui.Rect adjustRectForDom(ui.Rect rect, SurfacePaintData paint) { height = math.max(0, height - strokeWidth); } - if (left != rect.left || - top != rect.top || - width != rect.width || - height != rect.height) { + if (left != rect.left || top != rect.top || width != rect.width || height != rect.height) { return ui.Rect.fromLTWH(left, top, width, height); } return rect; } DomHTMLElement buildDrawRectElement( - ui.Rect rect, SurfacePaintData paint, String tagName, Matrix4 transform) { + ui.Rect rect, + SurfacePaintData paint, + String tagName, + Matrix4 transform, +) { assert(rect.left <= rect.right); assert(rect.top <= rect.bottom); - final DomHTMLElement rectangle = domDocument.createElement(tagName) as - DomHTMLElement; + final DomHTMLElement rectangle = domDocument.createElement(tagName) as DomHTMLElement; assert(() { rectangle.setAttribute('flt-rect', '$rect'); rectangle.setAttribute('flt-paint', '$paint'); @@ -287,16 +288,16 @@ DomHTMLElement buildDrawRectElement( String _getBackgroundImageCssValue(ui.Shader? shader, ui.Rect bounds) { final String url = _getBackgroundImageUrl(shader, bounds); - return (url != '') ? "url('$url'": ''; + return (url != '') ? "url('$url'" : ''; } String _getBackgroundImageUrl(ui.Shader? shader, ui.Rect bounds) { - if(shader != null) { - if(shader is EngineImageShader) { + if (shader != null) { + if (shader is EngineImageShader) { return shader.image.imgElement.src ?? ''; } - if(shader is EngineGradient) { + if (shader is EngineGradient) { return shader.createImageBitmap(bounds, 1, true) as String; } } @@ -315,13 +316,17 @@ void applyRRectBorderRadius(DomCSSStyleDeclaration style, ui.RRect rrect) { return; } // Non-uniform. Apply each corner radius. - style.borderTopLeftRadius = '${_borderStrokeToCssUnit(rrect.tlRadiusX)} ' + style.borderTopLeftRadius = + '${_borderStrokeToCssUnit(rrect.tlRadiusX)} ' '${_borderStrokeToCssUnit(rrect.tlRadiusY)}'; - style.borderTopRightRadius = '${_borderStrokeToCssUnit(rrect.trRadiusX)} ' + style.borderTopRightRadius = + '${_borderStrokeToCssUnit(rrect.trRadiusX)} ' '${_borderStrokeToCssUnit(rrect.trRadiusY)}'; - style.borderBottomLeftRadius = '${_borderStrokeToCssUnit(rrect.blRadiusX)} ' + style.borderBottomLeftRadius = + '${_borderStrokeToCssUnit(rrect.blRadiusX)} ' '${_borderStrokeToCssUnit(rrect.blRadiusY)}'; - style.borderBottomRightRadius = '${_borderStrokeToCssUnit(rrect.brRadiusX)} ' + style.borderBottomRightRadius = + '${_borderStrokeToCssUnit(rrect.brRadiusX)} ' '${_borderStrokeToCssUnit(rrect.brRadiusY)}'; } @@ -336,8 +341,7 @@ String _borderStrokeToCssUnit(double value) { SVGSVGElement pathToSvgElement(SurfacePath path, SurfacePaintData paint) { // In Firefox some SVG typed attributes are returned as null without a // setter. So we use strings here. - final SVGSVGElement root = createSVGSVGElement() - ..setAttribute('overflow', 'visible'); + final SVGSVGElement root = createSVGSVGElement()..setAttribute('overflow', 'visible'); final SVGPathElement svgPath = createSVGPathElement(); root.append(svgPath); diff --git a/lib/web_ui/lib/src/engine/html/image.dart b/lib/web_ui/lib/src/engine/html/image.dart index b1a5b6d3cfb65..9a1bca2a52f86 100644 --- a/lib/web_ui/lib/src/engine/html/image.dart +++ b/lib/web_ui/lib/src/engine/html/image.dart @@ -65,8 +65,7 @@ class HtmlImage implements ui.Image { return result!; } - throw StateError( - 'Image.debugDisposed is only available when asserts are enabled.'); + throw StateError('Image.debugDisposed is only available when asserts are enabled.'); } @override @@ -85,16 +84,16 @@ class HtmlImage implements ui.Image { final int height; @override - Future toByteData( - {ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba}) { + Future toByteData({ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba}) { switch (format) { // TODO(ColdPaleLight): https://github.com/flutter/flutter/issues/89128 // The format rawRgba always returns straight rather than premul currently. case ui.ImageByteFormat.rawRgba: case ui.ImageByteFormat.rawStraightRgba: - final DomCanvasElement canvas = createDomCanvasElement() - ..width = width.toDouble() - ..height = height.toDouble(); + final DomCanvasElement canvas = + createDomCanvasElement() + ..width = width.toDouble() + ..height = height.toDouble(); final DomCanvasRenderingContext2D ctx = canvas.context2D; ctx.drawImage(imgElement, 0, 0); final DomImageData imageData = ctx.getImageData(0, 0, width, height); @@ -106,8 +105,7 @@ class HtmlImage implements ui.Image { default: if (imgElement.src?.startsWith('data:') ?? false) { final UriData data = UriData.fromUri(Uri.parse(imgElement.src!)); - return Future.value( - data.contentAsBytes().buffer.asByteData()); + return Future.value(data.contentAsBytes().buffer.asByteData()); } else { return Future.value(); } diff --git a/lib/web_ui/lib/src/engine/html/image_filter.dart b/lib/web_ui/lib/src/engine/html/image_filter.dart index c6d70bab15991..77b66ee20880d 100644 --- a/lib/web_ui/lib/src/engine/html/image_filter.dart +++ b/lib/web_ui/lib/src/engine/html/image_filter.dart @@ -14,8 +14,7 @@ import 'surface.dart'; import 'surface_stats.dart'; /// A surface that applies an [imageFilter] to its children. -class PersistedImageFilter extends PersistedContainerSurface - implements ui.ImageFilterEngineLayer { +class PersistedImageFilter extends PersistedContainerSurface implements ui.ImageFilterEngineLayer { PersistedImageFilter(PersistedImageFilter? super.oldLayer, this.filter, this.offset); final ui.ImageFilter filter; @@ -40,8 +39,8 @@ class PersistedImageFilter extends PersistedContainerSurface Matrix4? _localTransformInverse; @override - Matrix4 get localTransformInverse => _localTransformInverse ??= - Matrix4.translationValues(-offset.dx, -offset.dy, 0); + Matrix4 get localTransformInverse => + _localTransformInverse ??= Matrix4.translationValues(-offset.dx, -offset.dy, 0); DomElement? _svgFilter; @override @@ -96,9 +95,10 @@ class PersistedImageFilter extends PersistedContainerSurface _svgFilter = null; if (backendFilter is ModeHtmlColorFilter) { _svgFilter = backendFilter.makeSvgFilter(rootElement); + /// Some blendModes do not make an svgFilter. See [EngineHtmlColorFilter.makeSvgFilter()] if (_svgFilter == null) { - return; + return; } } else if (backendFilter is MatrixHtmlColorFilter) { _svgFilter = backendFilter.makeSvgFilter(rootElement); diff --git a/lib/web_ui/lib/src/engine/html/offset.dart b/lib/web_ui/lib/src/engine/html/offset.dart index d206328c4f178..c1484d8cccd1f 100644 --- a/lib/web_ui/lib/src/engine/html/offset.dart +++ b/lib/web_ui/lib/src/engine/html/offset.dart @@ -10,8 +10,7 @@ import '../vector_math.dart'; import 'surface.dart'; /// A surface that translates its children using CSS transform and translate. -class PersistedOffset extends PersistedContainerSurface - implements ui.OffsetEngineLayer { +class PersistedOffset extends PersistedContainerSurface implements ui.OffsetEngineLayer { PersistedOffset(PersistedOffset? super.oldLayer, this.dx, this.dy); /// Horizontal displacement. diff --git a/lib/web_ui/lib/src/engine/html/opacity.dart b/lib/web_ui/lib/src/engine/html/opacity.dart index b3cd19cbdc455..ada0643fc4bd8 100644 --- a/lib/web_ui/lib/src/engine/html/opacity.dart +++ b/lib/web_ui/lib/src/engine/html/opacity.dart @@ -10,8 +10,7 @@ import '../vector_math.dart'; import 'surface.dart'; /// A surface that makes its children transparent. -class PersistedOpacity extends PersistedContainerSurface - implements ui.OpacityEngineLayer { +class PersistedOpacity extends PersistedContainerSurface implements ui.OpacityEngineLayer { PersistedOpacity(PersistedOpacity? super.oldLayer, this.alpha, this.offset); final int alpha; @@ -36,8 +35,8 @@ class PersistedOpacity extends PersistedContainerSurface Matrix4? _localTransformInverse; @override - Matrix4 get localTransformInverse => _localTransformInverse ??= - Matrix4.translationValues(-offset.dx, -offset.dy, 0); + Matrix4 get localTransformInverse => + _localTransformInverse ??= Matrix4.translationValues(-offset.dx, -offset.dy, 0); @override DomElement createElement() { diff --git a/lib/web_ui/lib/src/engine/html/path/conic.dart b/lib/web_ui/lib/src/engine/html/path/conic.dart index fa8a338dcabfb..3fb62a5cbd41b 100644 --- a/lib/web_ui/lib/src/engine/html/path/conic.dart +++ b/lib/web_ui/lib/src/engine/html/path/conic.dart @@ -106,8 +106,7 @@ class Conic { if (!SPath.between(startY, midY, endY)) { // The computed midpoint is outside end points, move it to // closer one. - final double closerY = - (midY - startY).abs() < (midY - endY).abs() ? startY : endY; + final double closerY = (midY - startY).abs() < (midY - endY).abs() ? startY : endY; conic0.p2y = conic1.p0y = closerY; } if (!SPath.between(startY, conic0.p1y, conic0.p2y)) { @@ -139,18 +138,17 @@ class Conic { final double scale = 1.0 / (1.0 + fW); final double newW = _subdivideWeightValue(fW); final ui.Offset wp1 = ui.Offset(fW * p1x, fW * p1y); - ui.Offset m = ui.Offset((p0x + (2 * wp1.dx) + p2x) * scale * 0.5, - (p0y + 2 * wp1.dy + p2y) * scale * 0.5); + ui.Offset m = ui.Offset( + (p0x + (2 * wp1.dx) + p2x) * scale * 0.5, + (p0y + 2 * wp1.dy + p2y) * scale * 0.5, + ); if (m.dx.isNaN || m.dy.isNaN) { final double w2 = fW * 2; final double scaleHalf = 1.0 / (1 + fW) * 0.5; - m = ui.Offset((p0x + (w2 * p1x) + p2x) * scaleHalf, - (p0y + (w2 * p1y) + p2y) * scaleHalf); + m = ui.Offset((p0x + (w2 * p1x) + p2x) * scaleHalf, (p0y + (w2 * p1y) + p2y) * scaleHalf); } - pair.first = Conic(p0x, p0y, (p0x + wp1.dx) * scale, (p0y + wp1.dy) * scale, - m.dx, m.dy, newW); - pair.second = Conic(m.dx, m.dy, (p2x + wp1.dx) * scale, - (p2y + wp1.dy) * scale, p2x, p2y, newW); + pair.first = Conic(p0x, p0y, (p0x + wp1.dx) * scale, (p0y + wp1.dy) * scale, m.dx, m.dy, newW); + pair.second = Conic(m.dx, m.dy, (p2x + wp1.dx) * scale, (p2y + wp1.dy) * scale, p2x, p2y, newW); } void chopAtYExtrema(List dst) { @@ -256,10 +254,8 @@ class Conic { cp1y = chopPointY; } - final Conic conic0 = - Conic(p0x, p0y, dx0 / dz0, cp0y, chopPointX, chopPointY, w0); - final Conic conic1 = - Conic(chopPointX, chopPointY, dx2 / dz2, cp1y, p2x, p2y, w2); + final Conic conic0 = Conic(p0x, p0y, dx0 / dz0, cp0y, chopPointX, chopPointY, w0); + final Conic conic1 = Conic(chopPointX, chopPointY, dx2 / dz2, cp1y, p2x, p2y, w2); dst.add(conic0); dst.add(conic1); return true; @@ -272,12 +268,9 @@ class Conic { int _computeSubdivisionCount(double tolerance) { assert(tolerance.isFinite); // Expecting finite coordinates. - assert(p0x.isFinite && - p1x.isFinite && - p2x.isFinite && - p0y.isFinite && - p1y.isFinite && - p2y.isFinite); + assert( + p0x.isFinite && p1x.isFinite && p2x.isFinite && p0y.isFinite && p1y.isFinite && p2y.isFinite, + ); if (tolerance < 0) { return 0; } @@ -304,8 +297,7 @@ class Conic { // The derivative equation returns a zero tangent vector when t is 0 or 1, // and the control point is equal to the end point. // In this case, use the conic endpoints to compute the tangent. - if ((t == 0 && p0x == p1x && p0y == p1y) || - (t == 1 && p1x == p2x && p1y == p2y)) { + if ((t == 0 && p0x == p1x && p0y == p1y) || (t == 1 && p1x == p2x && p1y == p2y)) { return ui.Offset(p2x - p0x, p2y - p0y); } final double p20x = p2x - p0x; @@ -323,8 +315,7 @@ class Conic { return ui.Offset(quadC.evalX(t), quadC.evalY(t)); } - static double evalNumerator( - double p0, double p1, double p2, double w, double t) { + static double evalNumerator(double p0, double p1, double p2, double w, double t) { assert(t >= 0 && t <= 1); final double src2w = p1 * w; final double C = p0; @@ -371,10 +362,8 @@ class QuadBounds { if ((t1 >= 0) && (t1 <= 1.0)) { // Solve (x,y) for curve at t = tx to find extrema final double tprime = 1.0 - t1; - final double extremaX = - (tprime * tprime * x1) + (2 * t1 * tprime * cpX) + (t1 * t1 * x2); - final double extremaY = - (tprime * tprime * y1) + (2 * t1 * tprime * cpY) + (t1 * t1 * y2); + final double extremaX = (tprime * tprime * x1) + (2 * t1 * tprime * cpX) + (t1 * t1 * x2); + final double extremaY = (tprime * tprime * y1) + (2 * t1 * tprime * cpY) + (t1 * t1 * y2); // Expand bounds. minX = math.min(minX, extremaX); maxX = math.max(maxX, extremaX); @@ -388,12 +377,10 @@ class QuadBounds { final double t2 = (y1 - cpY) / denom; if ((t2 >= 0) && (t2 <= 1.0)) { final double tprime2 = 1.0 - t2; - final double extrema2X = (tprime2 * tprime2 * x1) + - (2 * t2 * tprime2 * cpX) + - (t2 * t2 * x2); - final double extrema2Y = (tprime2 * tprime2 * y1) + - (2 * t2 * tprime2 * cpY) + - (t2 * t2 * y2); + final double extrema2X = + (tprime2 * tprime2 * x1) + (2 * t2 * tprime2 * cpX) + (t2 * t2 * x2); + final double extrema2Y = + (tprime2 * tprime2 * y1) + (2 * t2 * tprime2 * cpY) + (t2 * t2 * y2); // Expand bounds. minX = math.min(minX, extrema2X); maxX = math.max(maxX, extrema2X); diff --git a/lib/web_ui/lib/src/engine/html/path/cubic.dart b/lib/web_ui/lib/src/engine/html/path/cubic.dart index 81b5dda6fe0ee..58090496588ff 100644 --- a/lib/web_ui/lib/src/engine/html/path/cubic.dart +++ b/lib/web_ui/lib/src/engine/html/path/cubic.dart @@ -42,19 +42,16 @@ QuadRoots _findCubicExtrema(double a, double b, double c, double d) { } /// Subdivides cubic curve for a list of t values. -void _chopCubicAt( - List tValues, Float32List points, Float32List outPts) { +void _chopCubicAt(List tValues, Float32List points, Float32List outPts) { assert(() { for (int i = 0; i < tValues.length - 1; i++) { final double tValue = tValues[i]; - assert(tValue > 0 && tValue < 1, - 'Not expecting to chop curve at start, end points'); + assert(tValue > 0 && tValue < 1, 'Not expecting to chop curve at start, end points'); } for (int i = 0; i < tValues.length - 1; i++) { final double tValue = tValues[i]; final double nextTValue = tValues[i + 1]; - assert( - nextTValue > tValue, 'Expecting t value to monotonically increase'); + assert(nextTValue > tValue, 'Expecting t value to monotonically increase'); } return true; }()); @@ -76,12 +73,10 @@ void _chopCubicAt( bufferPos += 6; // watch out in case the renormalized t isn't in range - if ((t = validUnitDivide( - tValues[i + 1] - tValues[i], 1.0 - tValues[i])) == - null) { + if ((t = validUnitDivide(tValues[i + 1] - tValues[i], 1.0 - tValues[i])) == null) { // Can't renormalize last point, just create a degenerate cubic. - outPts[bufferPos + 4] = outPts[bufferPos + 5] = - outPts[bufferPos + 6] = points[bufferPos + 3]; + outPts[bufferPos + 4] = + outPts[bufferPos + 5] = outPts[bufferPos + 6] = points[bufferPos + 3]; break; } } @@ -91,8 +86,7 @@ void _chopCubicAt( /// Subdivides cubic curve at [t] and writes to [outPts] at position [outIndex]. /// /// The cubic points are read from [points] at [bufferPos] offset. -void _chopCubicAtT(Float32List points, int bufferPos, Float32List outPts, - int outIndex, double t) { +void _chopCubicAtT(Float32List points, int bufferPos, Float32List outPts, int outIndex, double t) { assert(t > 0 && t < 1); final double p3y = points[bufferPos + 7]; final double p0x = points[bufferPos + 0]; @@ -245,7 +239,8 @@ class CubicBounds { final double t = -b / (2 * a); final double tprime = 1.0 - t; if ((t >= 0.0) && (t <= 1.0)) { - extremaX = ((tprime * tprime * tprime) * startX) + + extremaX = + ((tprime * tprime * tprime) * startX) + ((3 * tprime * tprime * t) * cpX1) + ((3 * tprime * t * t) * cpX2) + (t * t * t * endX); @@ -258,7 +253,8 @@ class CubicBounds { double t = (-b - s) / (2 * a); double tprime = 1.0 - t; if ((t >= 0.0) && (t <= 1.0)) { - extremaX = ((tprime * tprime * tprime) * startX) + + extremaX = + ((tprime * tprime * tprime) * startX) + ((3 * tprime * tprime * t) * cpX1) + ((3 * tprime * t * t) * cpX2) + (t * t * t * endX); @@ -269,7 +265,8 @@ class CubicBounds { t = (-b + s) / (2 * a); tprime = 1.0 - t; if ((t >= 0.0) && (t <= 1.0)) { - extremaX = ((tprime * tprime * tprime) * startX) + + extremaX = + ((tprime * tprime * tprime) * startX) + ((3 * tprime * tprime * t) * cpX1) + ((3 * tprime * t * t) * cpX2) + (t * t * t * endX); @@ -303,7 +300,8 @@ class CubicBounds { final double t = -b / (2 * a); final double tprime = 1.0 - t; if ((t >= 0.0) && (t <= 1.0)) { - extremaY = ((tprime * tprime * tprime) * startY) + + extremaY = + ((tprime * tprime * tprime) * startY) + ((3 * tprime * tprime * t) * cpY1) + ((3 * tprime * t * t) * cpY2) + (t * t * t * endY); @@ -316,7 +314,8 @@ class CubicBounds { final double t = (-b - s) / (2 * a); final double tprime = 1.0 - t; if ((t >= 0.0) && (t <= 1.0)) { - extremaY = ((tprime * tprime * tprime) * startY) + + extremaY = + ((tprime * tprime * tprime) * startY) + ((3 * tprime * tprime * t) * cpY1) + ((3 * tprime * t * t) * cpY2) + (t * t * t * endY); @@ -327,7 +326,8 @@ class CubicBounds { final double t2 = (-b + s) / (2 * a); final double tprime2 = 1.0 - t2; if ((t2 >= 0.0) && (t2 <= 1.0)) { - extremaY = ((tprime2 * tprime2 * tprime2) * startY) + + extremaY = + ((tprime2 * tprime2 * tprime2) * startY) + ((3 * tprime2 * tprime2 * t2) * cpY1) + ((3 * tprime2 * t2 * t2) * cpY2) + (t2 * t2 * t2 * endY); @@ -341,8 +341,7 @@ class CubicBounds { } /// Chops cubic spline at startT and stopT, writes result to buffer. -void chopCubicBetweenT( - List points, double startT, double stopT, Float32List buffer) { +void chopCubicBetweenT(List points, double startT, double stopT, Float32List buffer) { assert(startT != 0 || stopT != 0); final double p3y = points[7]; final double p0x = points[0]; diff --git a/lib/web_ui/lib/src/engine/html/path/path.dart b/lib/web_ui/lib/src/engine/html/path/path.dart index 3d2d926fb91ac..a93945aa81309 100644 --- a/lib/web_ui/lib/src/engine/html/path/path.dart +++ b/lib/web_ui/lib/src/engine/html/path/path.dart @@ -39,19 +39,17 @@ class SurfacePath implements ui.Path { } /// Creates a copy of another [Path]. - SurfacePath.from(SurfacePath source) - : pathRef = PathRef()..copy(source.pathRef, 0, 0) { + SurfacePath.from(SurfacePath source) : pathRef = PathRef()..copy(source.pathRef, 0, 0) { _copyFields(source); } /// Creates a shifted copy of another [Path]. SurfacePath.shiftedFrom(SurfacePath source, double offsetX, double offsetY) - : pathRef = PathRef.shiftedFrom(source.pathRef, offsetX, offsetY) { + : pathRef = PathRef.shiftedFrom(source.pathRef, offsetX, offsetY) { _copyFields(source); } - SurfacePath.shallowCopy(SurfacePath source) - : pathRef = PathRef.shallowCopy(source.pathRef) { + SurfacePath.shallowCopy(SurfacePath source) : pathRef = PathRef.shallowCopy(source.pathRef) { _copyFields(source); } @@ -264,8 +262,7 @@ class SurfacePath implements ui.Path { int pointIndex = (pointCount - 1) * 2; final double lastPointX = pathRef.points[pointIndex++]; final double lastPointY = pathRef.points[pointIndex]; - quadraticBezierTo( - x1 + lastPointX, y1 + lastPointY, x2 + lastPointX, y2 + lastPointY); + quadraticBezierTo(x1 + lastPointX, y1 + lastPointY, x2 + lastPointX, y2 + lastPointY); } } @@ -305,8 +302,7 @@ class SurfacePath implements ui.Path { int pointIndex = (pointCount - 1) * 2; final double lastPointX = pathRef.points[pointIndex++]; final double lastPointY = pathRef.points[pointIndex]; - conicTo(lastPointX + x1, lastPointY + y1, lastPointX + x2, - lastPointY + y2, w); + conicTo(lastPointX + x1, lastPointY + y1, lastPointX + x2, lastPointY + y2, w); } } @@ -314,8 +310,7 @@ class SurfacePath implements ui.Path { /// to the given point (x3,y3), using the control points (x1,y1) and /// (x2,y2). @override - void cubicTo( - double x1, double y1, double x2, double y2, double x3, double y3) { + void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3) { _injectMoveToIfNeeded(); final int pointIndex = pathRef.growForVerb(SPathVerb.kCubic, 0); pathRef.setPoint(pointIndex, x1, y1); @@ -329,8 +324,7 @@ class SurfacePath implements ui.Path { /// the control points at the offsets (x1,y1) and (x2,y2) from the /// current point. @override - void relativeCubicTo( - double x1, double y1, double x2, double y2, double x3, double y3) { + void relativeCubicTo(double x1, double y1, double x2, double y2, double x3, double y3) { final int pointCount = pathRef.countPoints(); if (pointCount == 0) { cubicTo(x1, y1, x2, y2, x3, y3); @@ -338,8 +332,14 @@ class SurfacePath implements ui.Path { int pointIndex = (pointCount - 1) * 2; final double lastPointX = pathRef.points[pointIndex++]; final double lastPointY = pathRef.points[pointIndex]; - cubicTo(x1 + lastPointX, y1 + lastPointY, x2 + lastPointX, - y2 + lastPointY, x3 + lastPointX, y3 + lastPointY); + cubicTo( + x1 + lastPointX, + y1 + lastPointY, + x2 + lastPointX, + y2 + lastPointY, + x3 + lastPointX, + y3 + lastPointY, + ); } } @@ -386,8 +386,7 @@ class SurfacePath implements ui.Path { assert(direction != SPathDirection.kUnknown); final bool isRect = _hasOnlyMoveTos(); // SkAutoDisableDirectionCheck. - final int finalDirection = - _hasOnlyMoveTos() ? direction : SPathDirection.kUnknown; + final int finalDirection = _hasOnlyMoveTos() ? direction : SPathDirection.kUnknown; final int pointIndex0 = pathRef.growForVerb(SPathVerb.kMove, 0); fLastMoveToIndex = pointIndex0 + 1; final int pointIndex1 = pathRef.growForVerb(SPathVerb.kLine, 0); @@ -429,8 +428,7 @@ class SurfacePath implements ui.Path { /// The line segment added if `forceMoveTo` is false starts at the /// current point and ends at the start of the arc. @override - void arcTo( - ui.Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) { + void arcTo(ui.Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) { assert(rectIsValid(rect)); // If width or height is 0, we still stroke a line, only abort if both // are empty. @@ -509,8 +507,7 @@ class SurfacePath implements ui.Path { // dot product sign to distinguish between the two. if (absY <= SPath.scalarNearlyZero && x > 0 && - ((y >= 0 && dir == SPathDirection.kCW) || - (y <= 0 && dir == SPathDirection.kCCW))) { + ((y >= 0 && dir == SPathDirection.kCW) || (y <= 0 && dir == SPathDirection.kCCW))) { // No conics, just use single line to connect point. if (forceMoveTo) { moveTo(px, py); @@ -569,8 +566,7 @@ class SurfacePath implements ui.Path { final ui.Offset p0 = quadPoints[quadPointIndex]; final ui.Offset p1 = quadPoints[quadPointIndex + 1]; final ui.Offset p2 = quadPoints[quadPointIndex + 2]; - conics - .add(Conic(p0.dx, p0.dy, p1.dx, p1.dy, p2.dx, p2.dy, quadrantWeight)); + conics.add(Conic(p0.dx, p0.dy, p1.dx, p1.dy, p2.dx, p2.dy, quadrantWeight)); } // Now compute any remaining ( < 90degree ) arc for last conic. @@ -587,15 +583,23 @@ class SurfacePath implements ui.Path { double offCurveX = lastQuadrantPoint.dx + x; double offCurveY = lastQuadrantPoint.dy + y; final double cosThetaOver2 = math.sqrt((1.0 + dot) / 2.0); - final double unscaledLength = - math.sqrt((offCurveX * offCurveX) + (offCurveY * offCurveY)); + final double unscaledLength = math.sqrt((offCurveX * offCurveX) + (offCurveY * offCurveY)); assert(unscaledLength > SPath.scalarNearlyZero); offCurveX /= cosThetaOver2 * unscaledLength; offCurveY /= cosThetaOver2 * unscaledLength; if (!SPath.nearlyEqual(offCurveX, lastQuadrantPoint.dx) || !SPath.nearlyEqual(offCurveY, lastQuadrantPoint.dy)) { - conics.add(Conic(lastQuadrantPoint.dx, lastQuadrantPoint.dy, offCurveX, - offCurveY, finalPx, finalPy, cosThetaOver2)); + conics.add( + Conic( + lastQuadrantPoint.dx, + lastQuadrantPoint.dy, + offCurveX, + offCurveY, + finalPx, + finalPy, + cosThetaOver2, + ), + ); ++conicCount; } } @@ -723,10 +727,8 @@ class SurfacePath implements ui.Path { final double sinXAxisRotation = math.sin(xAxisRotation); // Calculate rotated midpoint. - final double xPrime = - (cosXAxisRotation * midPointX) + (sinXAxisRotation * midPointY); - final double yPrime = - (-sinXAxisRotation * midPointX) + (cosXAxisRotation * midPointY); + final double xPrime = (cosXAxisRotation * midPointX) + (sinXAxisRotation * midPointY); + final double yPrime = (-sinXAxisRotation * midPointX) + (cosXAxisRotation * midPointY); // Check if the radii are big enough to draw the arc, scale radii if not. // http://www.w3.org/TR/SVG/implnote.html#ArcCorrectionOutOfRangeRadii @@ -745,10 +747,8 @@ class SurfacePath implements ui.Path { } // Switch to unit vectors - double unitPts0x = - (lastPointX * cosXAxisRotation + lastPointY * sinXAxisRotation) / rx; - double unitPts0y = - (lastPointY * cosXAxisRotation - lastPointX * sinXAxisRotation) / ry; + double unitPts0x = (lastPointX * cosXAxisRotation + lastPointY * sinXAxisRotation) / rx; + double unitPts0y = (lastPointY * cosXAxisRotation - lastPointX * sinXAxisRotation) / ry; double unitPts1x = (x * cosXAxisRotation + y * sinXAxisRotation) / rx; double unitPts1y = (y * cosXAxisRotation - x * sinXAxisRotation) / ry; double deltaX = unitPts1x - unitPts0x; @@ -790,8 +790,7 @@ class SurfacePath implements ui.Path { } // The arc may be slightly bigger than 1/4 circle, so allow up to 1/3rd. - final int segments = - (thetaArc / (2.0 * math.pi / 3.0)).abs().ceil(); + final int segments = (thetaArc / (2.0 * math.pi / 3.0)).abs().ceil(); final double thetaWidth = thetaArc / segments; final double t = math.tan(thetaWidth / 2.0); if (!t.isFinite) { @@ -805,7 +804,8 @@ class SurfacePath implements ui.Path { // to start outside their marks. A round rect may lose convexity as a // result. If the input values are on integers, place the conic on // integers as well. - final bool expectIntegers = SPath.nearlyEqual(math.pi / 2 - thetaWidth.abs(), 0) && + final bool expectIntegers = + SPath.nearlyEqual(math.pi / 2 - thetaWidth.abs(), 0) && SPath.isInteger(rx) && SPath.isInteger(ry) && SPath.isInteger(x) && @@ -823,10 +823,8 @@ class SurfacePath implements ui.Path { unitPts0y = unitPts0y * ry; unitPts1x = unitPts1x * rx; unitPts1y = unitPts1y * ry; - double xStart = - unitPts0x * cosXAxisRotation - unitPts0y * sinXAxisRotation; - double yStart = - unitPts0y * cosXAxisRotation + unitPts0x * sinXAxisRotation; + double xStart = unitPts0x * cosXAxisRotation - unitPts0y * sinXAxisRotation; + double yStart = unitPts0y * cosXAxisRotation + unitPts0x * sinXAxisRotation; double xEnd = unitPts1x * cosXAxisRotation - unitPts1y * sinXAxisRotation; double yEnd = unitPts1y * cosXAxisRotation + unitPts1x * sinXAxisRotation; if (expectIntegers) { @@ -875,11 +873,12 @@ class SurfacePath implements ui.Path { lastPointY = pathRef.points[pointIndex]; } arcToPoint( - ui.Offset(lastPointX + arcEndDelta.dx, lastPointY + arcEndDelta.dy), - radius: radius, - rotation: rotation, - largeArc: largeArc, - clockwise: clockwise); + ui.Offset(lastPointX + arcEndDelta.dx, lastPointY + arcEndDelta.dy), + radius: radius, + rotation: rotation, + largeArc: largeArc, + clockwise: clockwise, + ); } /// Adds a new subpath that consists of a curve that forms the @@ -957,9 +956,10 @@ class SurfacePath implements ui.Path { double startIndex = startOver90I + 1.0 % 4.0; startIndex = startIndex < 0 ? startIndex + 4.0 : startIndex; _addOval( - oval, - sweepAngle > 0 ? SPathDirection.kCW : SPathDirection.kCCW, - startIndex.toInt()); + oval, + sweepAngle > 0 ? SPathDirection.kCW : SPathDirection.kCCW, + startIndex.toInt(), + ); return; } } @@ -1051,8 +1051,7 @@ class SurfacePath implements ui.Path { close(); // SkAutoDisableDirectionCheck. _firstDirection = isRRect ? direction : SPathDirection.kUnknown; - pathRef.setIsRRect( - isRRect, direction == SPathDirection.kCCW, startIndex % 8, rrect); + pathRef.setIsRRect(isRRect, direction == SPathDirection.kCCW, startIndex % 8, rrect); } _debugValidate(); } @@ -1065,8 +1064,13 @@ class SurfacePath implements ui.Path { /// matrix stored in column major order. @override void addPath(ui.Path path, ui.Offset offset, {Float64List? matrix4}) { - addPathWithMode(path, offset.dx, offset.dy, - matrix4 == null ? null : toMatrix32(matrix4), SPathAddPathMode.kAppend); + addPathWithMode( + path, + offset.dx, + offset.dy, + matrix4 == null ? null : toMatrix32(matrix4), + SPathAddPathMode.kAppend, + ); } /// Adds a new subpath that consists of the given `path` offset by the given @@ -1074,8 +1078,13 @@ class SurfacePath implements ui.Path { /// /// If `matrix4` is not null, the path will be transformed by this matrix /// after the matrix is translated by the given offset. - void addPathWithMode(ui.Path path, double offsetX, double offsetY, - Float32List? matrix4, int mode) { + void addPathWithMode( + ui.Path path, + double offsetX, + double offsetY, + Float32List? matrix4, + int mode, + ) { SurfacePath source = path as SurfacePath; if (source.pathRef.isEmpty) { return; @@ -1089,8 +1098,7 @@ class SurfacePath implements ui.Path { // Fast path add points,verbs if matrix doesn't have perspective and // we are not extending. - if (mode == SPathAddPathMode.kAppend && - (matrix4 == null || _isSimple2dTransform(matrix4))) { + if (mode == SPathAddPathMode.kAppend && (matrix4 == null || _isSimple2dTransform(matrix4))) { pathRef.append(source.pathRef); } else { bool firstVerb = true; @@ -1100,17 +1108,19 @@ class SurfacePath implements ui.Path { while ((verb = iter.next(outPts)) != SPath.kDoneVerb) { switch (verb) { case SPath.kMoveVerb: - final double point0X = matrix4 == null - ? outPts[0] + offsetX - : (matrix4[0] * (outPts[0] + offsetX)) + - (matrix4[4] * (outPts[1] + offsetY)) + - matrix4[12]; - final double point0Y = matrix4 == null - ? outPts[1] + offsetY - : (matrix4[1] * (outPts[0] + offsetX)) + - (matrix4[5] * (outPts[1] + offsetY)) + - matrix4[13] + - offsetY; + final double point0X = + matrix4 == null + ? outPts[0] + offsetX + : (matrix4[0] * (outPts[0] + offsetX)) + + (matrix4[4] * (outPts[1] + offsetY)) + + matrix4[12]; + final double point0Y = + matrix4 == null + ? outPts[1] + offsetY + : (matrix4[1] * (outPts[0] + offsetX)) + + (matrix4[5] * (outPts[1] + offsetY)) + + matrix4[13] + + offsetY; if (firstVerb && !pathRef.isEmpty) { assert(mode == SPathAddPathMode.kExtend); // In case last contour is closed inject move to. @@ -1139,11 +1149,9 @@ class SurfacePath implements ui.Path { case SPath.kQuadVerb: _quadTo(outPts[2], outPts[3], outPts[4], outPts[5]); case SPath.kConicVerb: - conicTo( - outPts[2], outPts[3], outPts[4], outPts[5], iter.conicWeight); + conicTo(outPts[2], outPts[3], outPts[4], outPts[5], iter.conicWeight); case SPath.kCubicVerb: - cubicTo(outPts[2], outPts[3], outPts[4], outPts[5], outPts[6], - outPts[7]); + cubicTo(outPts[2], outPts[3], outPts[4], outPts[5], outPts[6], outPts[7]); case SPath.kCloseVerb: close(); } @@ -1181,8 +1189,13 @@ class SurfacePath implements ui.Path { @override void extendWithPath(ui.Path path, ui.Offset offset, {Float64List? matrix4}) { assert(offsetIsValid(offset)); - addPathWithMode(path, offset.dx, offset.dy, - matrix4 == null ? null : toMatrix32(matrix4), SPathAddPathMode.kExtend); + addPathWithMode( + path, + offset.dx, + offset.dy, + matrix4 == null ? null : toMatrix32(matrix4), + SPathAddPathMode.kExtend, + ); } /// Tests to see if the given point is within the path. (That is, whether the @@ -1206,8 +1219,7 @@ class SurfacePath implements ui.Path { final ui.Rect bounds = getBounds(); final double x = point.dx; final double y = point.dy; - if (x < bounds.left || y < bounds.top || x > bounds.right || - y > bounds.bottom) { + if (x < bounds.left || y < bounds.top || x > bounds.right || y > bounds.bottom) { return false; } final PathWinding windings = PathWinding(pathRef, point.dx, point.dy); @@ -1278,8 +1290,7 @@ class SurfacePath implements ui.Path { /// Returns a copy of the path with all the segments of every /// subpath translated by the given offset. @override - SurfacePath shift(ui.Offset offset) => - SurfacePath.shiftedFrom(this, offset.dx, offset.dy); + SurfacePath shift(ui.Offset offset) => SurfacePath.shiftedFrom(this, offset.dx, offset.dy); /// Returns a copy of the path with all the segments of every /// sub path transformed by the given matrix. @@ -1354,8 +1365,7 @@ class SurfacePath implements ui.Path { pointIndex++; } --pointIndex; - final int convexity = - Convexicator.bySign(pathRef, pointIndex, pointCount - pointIndex); + final int convexity = Convexicator.bySign(pathRef, pointIndex, pointCount - pointIndex); if (SPathConvexityType.kConcave == convexity) { setConvexityType(SPathConvexityType.kConcave); return SPathConvexityType.kConcave; @@ -1411,11 +1421,10 @@ class SurfacePath implements ui.Path { } if (_firstDirection == SPathDirection.kUnknown) { - if (state.firstDirection == SPathDirection.kUnknown && - !pathRef.getBounds().isEmpty) { - return _setComputedConvexity(state.reversals < 3 - ? SPathConvexityType.kConvex - : SPathConvexityType.kConcave); + if (state.firstDirection == SPathDirection.kUnknown && !pathRef.getBounds().isEmpty) { + return _setComputedConvexity( + state.reversals < 3 ? SPathConvexityType.kConvex : SPathConvexityType.kConcave, + ); } _firstDirection = state.firstDirection; } @@ -1499,9 +1508,8 @@ class SurfacePath implements ui.Path { bottom = math.max(bottom, maxY); } } - final ui.Rect newBounds = ltrbInitialized - ? ui.Rect.fromLTRB(left, top, right, bottom) - : ui.Rect.zero; + final ui.Rect newBounds = + ltrbInitialized ? ui.Rect.fromLTRB(left, top, right, bottom) : ui.Rect.zero; pathRef.getBounds(); pathRef.cachedBounds = newBounds; return newBounds; @@ -1547,8 +1555,7 @@ class SurfacePath implements ui.Path { /// /// Used for web optimization of physical shape represented as /// a persistent div. - ui.Rect? toCircle() => - pathRef.isOval == -1 ? null : pathRef.getBounds(); + ui.Rect? toCircle() => pathRef.isOval == -1 ? null : pathRef.getBounds(); /// Returns if Path is empty. /// Empty Path may have FillType but has no points, verbs or weights. @@ -1572,15 +1579,21 @@ class SurfacePath implements ui.Path { case SPath.kLineVerb: sb.write('LineTo(${points[pIndex + 2]}, ${points[pIndex + 3]})'); case SPath.kQuadVerb: - sb.write('Quad(${points[pIndex + 2]}, ${points[pIndex + 3]},' - ' ${points[pIndex + 3]}, ${points[pIndex + 4]})'); + sb.write( + 'Quad(${points[pIndex + 2]}, ${points[pIndex + 3]},' + ' ${points[pIndex + 3]}, ${points[pIndex + 4]})', + ); case SPath.kConicVerb: - sb.write('Conic(${points[pIndex + 2]}, ${points[pIndex + 3]},' - ' ${points[pIndex + 3]}, ${points[pIndex + 4]}, w = ${iter.conicWeight})'); + sb.write( + 'Conic(${points[pIndex + 2]}, ${points[pIndex + 3]},' + ' ${points[pIndex + 3]}, ${points[pIndex + 4]}, w = ${iter.conicWeight})', + ); case SPath.kCubicVerb: - sb.write('Cubic(${points[pIndex + 2]}, ${points[pIndex + 3]},' - ' ${points[pIndex + 3]}, ${points[pIndex + 4]}, ' - ' ${points[pIndex + 5]}, ${points[pIndex + 6]})'); + sb.write( + 'Cubic(${points[pIndex + 2]}, ${points[pIndex + 3]},' + ' ${points[pIndex + 3]}, ${points[pIndex + 4]}, ' + ' ${points[pIndex + 5]}, ${points[pIndex + 6]})', + ); case SPath.kCloseVerb: sb.write('Close()'); } @@ -1610,8 +1623,7 @@ ui.Offset? _arcIsLonePoint(ui.Rect oval, double startAngle, double sweepAngle) { // Computed scaling factor for opposing sides with corner radius given // a [limit] max width or height. -double _computeMinScale( - double radius1, double radius2, double limit, double scale) { +double _computeMinScale(double radius1, double radius2, double limit, double scale) { final double totalRadius = radius1 + radius2; if (totalRadius <= limit) { // Radii fit within the limit so return existing scale factor. diff --git a/lib/web_ui/lib/src/engine/html/path/path_iterator.dart b/lib/web_ui/lib/src/engine/html/path/path_iterator.dart index 5f09e134ffba2..c2437af3bed3e 100644 --- a/lib/web_ui/lib/src/engine/html/path/path_iterator.dart +++ b/lib/web_ui/lib/src/engine/html/path/path_iterator.dart @@ -12,8 +12,8 @@ import 'path_utils.dart'; // Iterates through path including generating closing segments. class PathIterator { PathIterator(this.pathRef, bool forceClose) - : _forceClose = forceClose, - _verbCount = pathRef.countVerbs() { + : _forceClose = forceClose, + _verbCount = pathRef.countVerbs() { _pointIndex = 0; if (!pathRef.isFinite) { // Don't allow iteration through non-finite points, prepare to return @@ -71,10 +71,7 @@ class PathIterator { if (_lastPointX != _moveToX || _lastPointY != _moveToY) { // Handle special case where comparison above will return true for // NaN != NaN although it should be false. - if (_lastPointX.isNaN || - _lastPointY.isNaN || - _moveToX.isNaN || - _moveToY.isNaN) { + if (_lastPointX.isNaN || _lastPointY.isNaN || _moveToX.isNaN || _moveToY.isNaN) { return SPath.kCloseVerb; } outPts[0] = _lastPointX; @@ -99,8 +96,7 @@ class PathIterator { _segmentState = SPathSegmentState.kAfterPrimitive; return ui.Offset(_moveToX, _moveToY); } - return ui.Offset( - pathRef.points[_pointIndex - 2], pathRef.points[_pointIndex - 1]); + return ui.Offset(pathRef.points[_pointIndex - 2], pathRef.points[_pointIndex - 1]); } int peek() { diff --git a/lib/web_ui/lib/src/engine/html/path/path_metrics.dart b/lib/web_ui/lib/src/engine/html/path/path_metrics.dart index 5170b20416263..f9746dc7dc917 100644 --- a/lib/web_ui/lib/src/engine/html/path/path_metrics.dart +++ b/lib/web_ui/lib/src/engine/html/path/path_metrics.dart @@ -30,11 +30,11 @@ const double kEpsilon = 0.000000001; /// /// When iterating across a [PathMetrics]' contours, the [PathMetric] objects /// are only valid until the next one is obtained. -class SurfacePathMetrics extends IterableBase - implements ui.PathMetrics { +class SurfacePathMetrics extends IterableBase implements ui.PathMetrics { SurfacePathMetrics(PathRef path, bool forceClosed) - : _iterator = - SurfacePathMetricIterator._(_SurfacePathMeasure(PathRef.shallowCopy(path), forceClosed)); + : _iterator = SurfacePathMetricIterator._( + _SurfacePathMeasure(PathRef.shallowCopy(path), forceClosed), + ); final SurfacePathMetricIterator _iterator; @@ -46,10 +46,9 @@ class SurfacePathMetrics extends IterableBase /// objects exposed through iterator. class _SurfacePathMeasure { _SurfacePathMeasure(this._path, this.forceClosed) - : - // nextContour will increment this to the zero based index. - _currentContourIndex = -1, - _pathIterator = PathIterator(_path, forceClosed); + : // nextContour will increment this to the zero based index. + _currentContourIndex = -1, + _pathIterator = PathIterator(_path, forceClosed); final PathRef _path; final PathIterator _pathIterator; @@ -63,8 +62,10 @@ class _SurfacePathMeasure { int get currentContourIndex => _currentContourIndex; double length(int contourIndex) { - assert(contourIndex <= currentContourIndex, - 'Iterator must be advanced before index $contourIndex can be used.'); + assert( + contourIndex <= currentContourIndex, + 'Iterator must be advanced before index $contourIndex can be used.', + ); return _contours[contourIndex].length; } @@ -113,15 +114,13 @@ class _SurfacePathMeasure { if (_verbIterIndex == _path.countVerbs()) { return false; } - final _PathContourMeasure measure = - _PathContourMeasure(_path, _pathIterator, forceClosed); + final _PathContourMeasure measure = _PathContourMeasure(_path, _pathIterator, forceClosed); _verbIterIndex = measure.verbEndIndex; _contours.add(measure); return true; } - ui.Path extractPath(int contourIndex, double start, double end, - {bool startWithMoveTo = true}) { + ui.Path extractPath(int contourIndex, double start, double end, {bool startWithMoveTo = true}) { return _contours[contourIndex].extractPath(start, end, startWithMoveTo); } } @@ -193,17 +192,13 @@ class _PathContourMeasure { // Compute distance to segment. Since distance is cumulative to find // t = 0..1 on the segment, we need to calculate start distance using prior // segment. - final double startDistance = - segmentIndex == 0 ? 0 : _segments[segmentIndex - 1].distance; + final double startDistance = segmentIndex == 0 ? 0 : _segments[segmentIndex - 1].distance; final double totalDistance = segment.distance - startDistance; - final double t = totalDistance < kEpsilon - ? 0 - : (distance - startDistance) / totalDistance; + final double t = totalDistance < kEpsilon ? 0 : (distance - startDistance) / totalDistance; return segment.computeTangent(t); } - ui.Path extractPath( - double startDistance, double stopDistance, bool startWithMoveTo) { + ui.Path extractPath(double startDistance, double stopDistance, bool startWithMoveTo) { if (startDistance < 0) { startDistance = 0; } @@ -221,14 +216,12 @@ class _PathContourMeasure { } int currentSegmentIndex = startSegmentIndex; _PathSegment seg = _segments[currentSegmentIndex]; - final _SurfaceTangent startTangent = - _getPosTan(startSegmentIndex, startDistance); + final _SurfaceTangent startTangent = _getPosTan(startSegmentIndex, startDistance); if (startWithMoveTo) { final ui.Offset startPosition = startTangent.position; path.moveTo(startPosition.dx, startPosition.dy); } - final _SurfaceTangent stopTangent = - _getPosTan(stopSegmentIndex, stopDistance); + final _SurfaceTangent stopTangent = _getPosTan(stopSegmentIndex, stopDistance); double startT = startTangent.t; final double stopT = stopTangent.t; if (startSegmentIndex == stopSegmentIndex) { @@ -250,8 +243,7 @@ class _PathContourMeasure { } // Chops the segment at startT and endT and writes it to output [path]. - void _outputSegmentTo( - _PathSegment segment, double startT, double stopT, ui.Path path) { + void _outputSegmentTo(_PathSegment segment, double startT, double stopT, ui.Path path) { final List points = segment.points; switch (segment.segmentType) { case SPath.kLineVerb: @@ -260,8 +252,7 @@ class _PathContourMeasure { path.lineTo(toX, toY); case SPath.kCubicVerb: chopCubicBetweenT(points, startT, stopT, _buffer); - path.cubicTo(_buffer[2], _buffer[3], _buffer[4], _buffer[5], _buffer[6], - _buffer[7]); + path.cubicTo(_buffer[2], _buffer[3], _buffer[4], _buffer[5], _buffer[6], _buffer[7]); case SPath.kQuadVerb: _chopQuadBetweenT(points, startT, stopT, _buffer); path.quadraticBezierTo(_buffer[2], _buffer[3], _buffer[4], _buffer[5]); @@ -290,9 +281,7 @@ class _PathContourMeasure { // actually made it larger, since a very small delta might be > 0, but // still have no effect on distance (if distance >>> delta). if (distance > prevDistance) { - _segments.add( - _PathSegment(SPath.kLineVerb, distance, [fromX, fromY, x, y]), - ); + _segments.add(_PathSegment(SPath.kLineVerb, distance, [fromX, fromY, x, y])); } } @@ -313,23 +302,31 @@ class _PathContourMeasure { assert(haveSeenMoveTo); // Compute cubic curve distance. distance = _computeCubicSegments( - points[0], - points[1], - points[2], - points[3], - points[4], - points[5], - points[6], - points[7], - distance, - 0, - _kMaxTValue, - _segments); + points[0], + points[1], + points[2], + points[3], + points[4], + points[5], + points[6], + points[7], + distance, + 0, + _kMaxTValue, + _segments, + ); case SPath.kConicVerb: assert(haveSeenMoveTo); final double w = iter.conicWeight; - final Conic conic = Conic(points[0], points[1], points[2], points[3], - points[4], points[5], w); + final Conic conic = Conic( + points[0], + points[1], + points[2], + points[3], + points[4], + points[5], + w, + ); final List conicPoints = conic.toQuads(); final int len = conicPoints.length; double startX = conicPoints[0].dx; @@ -340,15 +337,33 @@ class _PathContourMeasure { final double p2x = conicPoints[i + 1].dx; final double p2y = conicPoints[i + 1].dy; distance = _computeQuadSegments( - startX, startY, p1x, p1y, p2x, p2y, distance, 0, _kMaxTValue); + startX, + startY, + p1x, + p1y, + p2x, + p2y, + distance, + 0, + _kMaxTValue, + ); startX = p2x; startY = p2y; } case SPath.kQuadVerb: assert(haveSeenMoveTo); // Compute quad curve distance. - distance = _computeQuadSegments(points[0], points[1], points[2], - points[3], points[4], points[5], distance, 0, _kMaxTValue); + distance = _computeQuadSegments( + points[0], + points[1], + points[2], + points[3], + points[4], + points[5], + distance, + 0, + _kMaxTValue, + ); case SPath.kCloseVerb: _contourLength = distance; return iter.pathVerbIndex; @@ -362,8 +377,16 @@ class _PathContourMeasure { static bool _tspanBigEnough(int tSpan) => (tSpan >> 10) != 0; - static bool _cubicTooCurvy(double x0, double y0, double x1, double y1, - double x2, double y2, double x3, double y3) { + static bool _cubicTooCurvy( + double x0, + double y0, + double x1, + double y1, + double x2, + double y2, + double x3, + double y3, + ) { // Measure distance from start-end line at 1/3 and 2/3rds to control // points. If distance is less than _fTolerance we should continue // subdividing curve. Uses approx distance for speed. @@ -391,20 +414,20 @@ class _PathContourMeasure { // Recursively subdivides cubic and adds segments. static double _computeCubicSegments( - double x0, - double y0, - double x1, - double y1, - double x2, - double y2, - double x3, - double y3, - double distance, - int tMin, - int tMax, - List<_PathSegment> segments) { - if (_tspanBigEnough(tMax - tMin) && - _cubicTooCurvy(x0, y0, x1, y1, x2, y2, x3, y3)) { + double x0, + double y0, + double x1, + double y1, + double x2, + double y2, + double x3, + double y3, + double distance, + int tMin, + int tMax, + List<_PathSegment> segments, + ) { + if (_tspanBigEnough(tMax - tMin) && _cubicTooCurvy(x0, y0, x1, y1, x2, y2, x3, y3)) { // Chop cubic into two halves (De Cateljau's algorithm) // See https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm final double abX = (x0 + x1) / 2; @@ -420,10 +443,34 @@ class _PathContourMeasure { final double abcdX = (abcX + bcdX) / 2; final double abcdY = (abcY + bcdY) / 2; final int tHalf = (tMin + tMax) >> 1; - distance = _computeCubicSegments(x0, y0, abX, abY, abcX, abcY, abcdX, - abcdY, distance, tMin, tHalf, segments); - distance = _computeCubicSegments(abcdX, abcdY, bcdX, bcdY, cdX, cdY, x3, - y3, distance, tHalf, tMax, segments); + distance = _computeCubicSegments( + x0, + y0, + abX, + abY, + abcX, + abcY, + abcdX, + abcdY, + distance, + tMin, + tHalf, + segments, + ); + distance = _computeCubicSegments( + abcdX, + abcdY, + bcdX, + bcdY, + cdX, + cdY, + x3, + y3, + distance, + tHalf, + tMax, + segments, + ); } else { final double dx = x0 - x3; final double dy = y0 - y3; @@ -431,15 +478,15 @@ class _PathContourMeasure { final double prevDistance = distance; distance += startToEndDistance; if (distance > prevDistance) { - segments.add(_PathSegment(SPath.kCubicVerb, distance, - [x0, y0, x1, y1, x2, y2, x3, y3])); + segments.add( + _PathSegment(SPath.kCubicVerb, distance, [x0, y0, x1, y1, x2, y2, x3, y3]), + ); } } return distance; } - static bool _quadTooCurvy( - double x0, double y0, double x1, double y1, double x2, double y2) { + static bool _quadTooCurvy(double x0, double y0, double x1, double y1, double x2, double y2) { // (a/4 + b/2 + c/4) - (a/2 + c/2) = -a/4 + b/2 - c/4 final double dx = (x1 / 2) - (x0 + x2) / 4; if (dx.abs() > _fTolerance) { @@ -452,8 +499,17 @@ class _PathContourMeasure { return false; } - double _computeQuadSegments(double x0, double y0, double x1, double y1, - double x2, double y2, double distance, int tMin, int tMax) { + double _computeQuadSegments( + double x0, + double y0, + double x1, + double y1, + double x2, + double y2, + double distance, + int tMin, + int tMax, + ) { if (_tspanBigEnough(tMax - tMin) && _quadTooCurvy(x0, y0, x1, y1, x2, y2)) { final double p01x = (x0 + x1) / 2; final double p01y = (y0 + y1) / 2; @@ -462,10 +518,8 @@ class _PathContourMeasure { final double p012x = (p01x + p12x) / 2; final double p012y = (p01y + p12y) / 2; final int tHalf = (tMin + tMax) >> 1; - distance = _computeQuadSegments( - x0, y0, p01x, p01y, p012x, p012y, distance, tMin, tHalf); - distance = _computeQuadSegments( - p012x, p012y, p12x, p12y, x2, y2, distance, tMin, tHalf); + distance = _computeQuadSegments(x0, y0, p01x, p01y, p012x, p012y, distance, tMin, tHalf); + distance = _computeQuadSegments(p012x, p012y, p12x, p12y, x2, y2, distance, tMin, tHalf); } else { final double dx = x0 - x2; final double dy = y0 - y2; @@ -473,8 +527,7 @@ class _PathContourMeasure { final double prevDistance = distance; distance += startToEndDistance; if (distance > prevDistance) { - _segments.add(_PathSegment( - SPath.kQuadVerb, distance, [x0, y0, x1, y1, x2, y2])); + _segments.add(_PathSegment(SPath.kQuadVerb, distance, [x0, y0, x1, y1, x2, y2])); } } return distance; @@ -492,9 +545,10 @@ class SurfacePathMetricIterator implements Iterator { SurfacePathMetric get current { if (_pathMetric == null) { throw RangeError( - 'PathMetricIterator is not pointing to a PathMetric. This can happen in two situations:\n' - '- The iteration has not started yet. If so, call "moveNext" to start iteration.\n' - '- The iterator ran out of elements. If so, check that "moveNext" returns true prior to calling "current".'); + 'PathMetricIterator is not pointing to a PathMetric. This can happen in two situations:\n' + '- The iteration has not started yet. If so, call "moveNext" to start iteration.\n' + '- The iterator ran out of elements. If so, check that "moveNext" returns true prior to calling "current".', + ); } return _pathMetric!; } @@ -534,9 +588,9 @@ const double _fTolerance = 0.5; /// to maintain consistency with native platforms. class SurfacePathMetric implements ui.PathMetric { SurfacePathMetric._(this._measure) - : length = _measure.length(_measure.currentContourIndex), - isClosed = _measure.isClosed(_measure.currentContourIndex), - contourIndex = _measure.currentContourIndex; + : length = _measure.length(_measure.currentContourIndex), + isClosed = _measure.isClosed(_measure.currentContourIndex), + contourIndex = _measure.currentContourIndex; /// Return the total length of the current contour. @override @@ -589,8 +643,7 @@ class SurfacePathMetric implements ui.PathMetric { /// Begin the segment with a moveTo if `startWithMoveTo` is true. @override ui.Path extractPath(double start, double end, {bool startWithMoveTo = true}) { - return _measure.extractPath(contourIndex, start, end, - startWithMoveTo: startWithMoveTo); + return _measure.extractPath(contourIndex, start, end, startWithMoveTo: startWithMoveTo); } @override @@ -600,9 +653,7 @@ class SurfacePathMetric implements ui.PathMetric { // Given a vector dx, dy representing slope, normalize and return as [ui.Offset]. ui.Offset _normalizeSlope(double dx, double dy) { final double length = math.sqrt(dx * dx + dy * dy); - return length < kEpsilon - ? ui.Offset.zero - : ui.Offset(dx / length, dy / length); + return length < kEpsilon ? ui.Offset.zero : ui.Offset(dx / length, dy / length); } class _SurfaceTangent extends ui.Tangent { @@ -625,42 +676,74 @@ class _PathSegment { // Simple line. Position is simple interpolation from start to end point. final double xAtDistance = (points[2] * t) + (points[0] * (1.0 - t)); final double yAtDistance = (points[3] * t) + (points[1] * (1.0 - t)); - return _SurfaceTangent(ui.Offset(xAtDistance, yAtDistance), - _normalizeSlope(points[2] - points[0], points[3] - points[1]), t); + return _SurfaceTangent( + ui.Offset(xAtDistance, yAtDistance), + _normalizeSlope(points[2] - points[0], points[3] - points[1]), + t, + ); case SPath.kCubicVerb: - return tangentForCubicAt(t, points[0], points[1], points[2], points[3], - points[4], points[5], points[6], points[7]); + return tangentForCubicAt( + t, + points[0], + points[1], + points[2], + points[3], + points[4], + points[5], + points[6], + points[7], + ); case SPath.kQuadVerb: - return tangentForQuadAt(t, points[0], points[1], points[2], points[3], - points[4], points[5]); + return tangentForQuadAt( + t, + points[0], + points[1], + points[2], + points[3], + points[4], + points[5], + ); default: throw UnsupportedError('Invalid segment type'); } } - _SurfaceTangent tangentForQuadAt(double t, double x0, double y0, double x1, - double y1, double x2, double y2) { + _SurfaceTangent tangentForQuadAt( + double t, + double x0, + double y0, + double x1, + double y1, + double x2, + double y2, + ) { assert(t >= 0 && t <= 1); - final SkQuadCoefficients quadEval = - SkQuadCoefficients(x0, y0, x1, y1, x2, y2); + final SkQuadCoefficients quadEval = SkQuadCoefficients(x0, y0, x1, y1, x2, y2); final ui.Offset pos = ui.Offset(quadEval.evalX(t), quadEval.evalY(t)); // Derivative of quad curve is 2(b - a + (a - 2b + c)t). // If control point is at start or end point, this yields 0 for t = 0 and // t = 1. In that case use the quad end points to compute tangent instead // of derivative. - final ui.Offset tangentVector = ((t == 0 && x0 == x1 && y0 == y1) || - (t == 1 && x1 == x2 && y1 == y2)) - ? _normalizeSlope(x2 - x0, y2 - y0) - : _normalizeSlope( - 2 * ((x2 - x0) * t + (x1 - x0)), 2 * ((y2 - y0) * t + (y1 - y0))); + final ui.Offset tangentVector = + ((t == 0 && x0 == x1 && y0 == y1) || (t == 1 && x1 == x2 && y1 == y2)) + ? _normalizeSlope(x2 - x0, y2 - y0) + : _normalizeSlope(2 * ((x2 - x0) * t + (x1 - x0)), 2 * ((y2 - y0) * t + (y1 - y0))); return _SurfaceTangent(pos, tangentVector, t); } - _SurfaceTangent tangentForCubicAt(double t, double x0, double y0, double x1, - double y1, double x2, double y2, double x3, double y3) { + _SurfaceTangent tangentForCubicAt( + double t, + double x0, + double y0, + double x1, + double y1, + double x2, + double y2, + double x3, + double y3, + ) { assert(t >= 0 && t <= 1); - final _SkCubicCoefficients cubicEval = - _SkCubicCoefficients(x0, y0, x1, y1, x2, y2, x3, y3); + final _SkCubicCoefficients cubicEval = _SkCubicCoefficients(x0, y0, x1, y1, x2, y2, x3, y3); final ui.Offset pos = ui.Offset(cubicEval.evalX(t), cubicEval.evalY(t)); // Derivative of cubic is zero when t = 0 or 1 and adjacent control point // is on the start or end point of curve. Use the other control point @@ -693,16 +776,23 @@ class _PathSegment { // Evaluates A * t^3 + B * t^2 + Ct + D = 0 for cubic curve. class _SkCubicCoefficients { - _SkCubicCoefficients(double x0, double y0, double x1, double y1, double x2, - double y2, double x3, double y3) - : ax = x3 + (3 * (x1 - x2)) - x0, - ay = y3 + (3 * (y1 - y2)) - y0, - bx = 3 * (x2 - (2 * x1) + x0), - by = 3 * (y2 - (2 * y1) + y0), - cx = 3 * (x1 - x0), - cy = 3 * (y1 - y0), - dx = x0, - dy = y0; + _SkCubicCoefficients( + double x0, + double y0, + double x1, + double y1, + double x2, + double y2, + double x3, + double y3, + ) : ax = x3 + (3 * (x1 - x2)) - x0, + ay = y3 + (3 * (y1 - y2)) - y0, + bx = 3 * (x2 - (2 * x1) + x0), + by = 3 * (y2 - (2 * y1) + y0), + cx = 3 * (x1 - x0), + cy = 3 * (y1 - y0), + dx = x0, + dy = y0; final double ax, ay, bx, by, cx, cy, dx, dy; @@ -712,8 +802,7 @@ class _SkCubicCoefficients { } /// Chops quadratic curve at startT and stopT and writes result to buffer. -void _chopQuadBetweenT( - List points, double startT, double stopT, Float32List buffer) { +void _chopQuadBetweenT(List points, double startT, double stopT, Float32List buffer) { assert(startT != 0 || stopT != 0); final double p2y = points[5]; final double p0x = points[0]; diff --git a/lib/web_ui/lib/src/engine/html/path/path_ref.dart b/lib/web_ui/lib/src/engine/html/path/path_ref.dart index c1dde261551e2..cc69e08417b2f 100644 --- a/lib/web_ui/lib/src/engine/html/path/path_ref.dart +++ b/lib/web_ui/lib/src/engine/html/path/path_ref.dart @@ -19,8 +19,8 @@ import 'path_utils.dart'; /// to update caches due to content changes. class PathRef { PathRef() - : fPoints = Float32List(kInitialPointsCapacity * 2), - _fVerbs = Uint8List(kInitialVerbsCapacity) { + : fPoints = Float32List(kInitialPointsCapacity * 2), + _fVerbs = Uint8List(kInitialVerbsCapacity) { _fPointsCapacity = kInitialPointsCapacity; _fVerbsCapacity = kInitialVerbsCapacity; _resetFields(); @@ -30,9 +30,7 @@ class PathRef { /// points,verbs and weights arrays. If original path is mutated by adding /// more verbs, this copy only returns path at the time of copy and shares /// typed arrays of original path. - PathRef.shallowCopy(PathRef ref) - : fPoints = ref.fPoints, - _fVerbs = ref._fVerbs { + PathRef.shallowCopy(PathRef ref) : fPoints = ref.fPoints, _fVerbs = ref._fVerbs { _fVerbsCapacity = ref._fVerbsCapacity; _fVerbsLength = ref._fVerbsLength; @@ -59,8 +57,8 @@ class PathRef { /// Returns a new path by translating [source] by [offsetX], [offsetY]. PathRef.shiftedFrom(PathRef source, double offsetX, double offsetY) - : fPoints = _fPointsFromSource(source, offsetX, offsetY), - _fVerbs = _fVerbsFromSource(source) { + : fPoints = _fPointsFromSource(source, offsetX, offsetY), + _fVerbs = _fVerbsFromSource(source) { _conicWeightsCapacity = source._conicWeightsCapacity; _conicWeightsLength = source._conicWeightsLength; if (source._conicWeights != null) { @@ -92,23 +90,19 @@ class PathRef { static const int kInitialLastMoveToIndex = -1; // SerializationOffsets - static const int kLegacyRRectOrOvalStartIdx_SerializationShift = - 28; // requires 3 bits, ignored. - static const int kLegacyRRectOrOvalIsCCW_SerializationShift = - 27; // requires 1 bit, ignored. - static const int kLegacyIsRRect_SerializationShift = - 26; // requires 1 bit, ignored. + static const int kLegacyRRectOrOvalStartIdx_SerializationShift = 28; // requires 3 bits, ignored. + static const int kLegacyRRectOrOvalIsCCW_SerializationShift = 27; // requires 1 bit, ignored. + static const int kLegacyIsRRect_SerializationShift = 26; // requires 1 bit, ignored. static const int kIsFinite_SerializationShift = 25; // requires 1 bit - static const int kLegacyIsOval_SerializationShift = - 24; // requires 1 bit, ignored. - static const int kSegmentMask_SerializationShift = - 0; // requires 4 bits (deprecated) + static const int kLegacyIsOval_SerializationShift = 24; // requires 1 bit, ignored. + static const int kSegmentMask_SerializationShift = 0; // requires 4 bits (deprecated) static const int kInitialPointsCapacity = 8; static const int kInitialVerbsCapacity = 8; /// Bounds of points that define path. ui.Rect? fBounds; + /// Computed tight bounds of path (may exclude curve control points). ui.Rect? cachedBounds; int _fPointsCapacity = 0; @@ -198,12 +192,12 @@ class PathRef { /// Use _detectRect() for detection if explicitly addRect was used (fIsRect) or /// it is a potential due to moveTo + 3 lineTo verbs. if (fIsRect) { - return ui.Rect.fromLTRB( - atPoint(0).dx, atPoint(0).dy, atPoint(1).dx, atPoint(2).dy); + return ui.Rect.fromLTRB(atPoint(0).dx, atPoint(0).dy, atPoint(1).dx, atPoint(2).dy); } else { return _fVerbsLength == 4 ? _detectRect() : null; } } + bool get isRectCCW => fRRectOrOvalIsCCW; bool get hasComputedBounds => !fBoundsIsDirty; @@ -253,8 +247,7 @@ class PathRef { /// Returns horizontal/vertical line bounds or null if not a line. ui.Rect? getStraightLine() { - if (_fVerbsLength != 2 || _fVerbs[0] != SPath.kMoveVerb || - _fVerbs[1] != SPath.kLineVerb) { + if (_fVerbsLength != 2 || _fVerbs[0] != SPath.kMoveVerb || _fVerbs[1] != SPath.kLineVerb) { return null; } final double x0 = fPoints[0]; @@ -304,13 +297,14 @@ class PathRef { dy = vector1_0y.abs(); } assert(() { - final int checkCornerIndex = SPath.nearlyEqual(controlPx, bounds.left) - ? (SPath.nearlyEqual(controlPy, bounds.top) - ? _Corner.kUpperLeft - : _Corner.kLowerLeft) - : (SPath.nearlyEqual(controlPy, bounds.top) - ? _Corner.kUpperRight - : _Corner.kLowerRight); + final int checkCornerIndex = + SPath.nearlyEqual(controlPx, bounds.left) + ? (SPath.nearlyEqual(controlPy, bounds.top) + ? _Corner.kUpperLeft + : _Corner.kLowerLeft) + : (SPath.nearlyEqual(controlPy, bounds.top) + ? _Corner.kUpperRight + : _Corner.kLowerRight); return checkCornerIndex == cornerIndex; }()); radii.add(ui.Radius.elliptical(dx, dy)); @@ -319,11 +313,10 @@ class PathRef { assert(() { if (verb == SPath.kLineVerb) { final bool isVerticalOrHorizontal = - SPath.nearlyEqual(pts[2], pts[0]) || - SPath.nearlyEqual(pts[3], pts[1]); + SPath.nearlyEqual(pts[2], pts[0]) || SPath.nearlyEqual(pts[3], pts[1]); assert( isVerticalOrHorizontal, - 'An RRect path must only contain vertical and horizontal lines.' + 'An RRect path must only contain vertical and horizontal lines.', ); } else { assert(verb == SPath.kCloseVerb); @@ -332,11 +325,13 @@ class PathRef { }()); } } - return ui.RRect.fromRectAndCorners(bounds, - topLeft: radii[_Corner.kUpperLeft], - topRight: radii[_Corner.kUpperRight], - bottomRight: radii[_Corner.kLowerRight], - bottomLeft: radii[_Corner.kLowerLeft]); + return ui.RRect.fromRectAndCorners( + bounds, + topLeft: radii[_Corner.kUpperLeft], + topRight: radii[_Corner.kUpperRight], + bottomRight: radii[_Corner.kLowerRight], + bottomLeft: radii[_Corner.kLowerLeft], + ); } @override @@ -351,8 +346,7 @@ class PathRef { } @override - int get hashCode => Object.hash(fSegmentMask, - fPoints, _conicWeights, _fVerbs); + int get hashCode => Object.hash(fSegmentMask, fPoints, _conicWeights, _fVerbs); bool equals(PathRef ref) { // We explicitly check fSegmentMask as a quick-reject. We could skip it, @@ -407,8 +401,7 @@ class PathRef { return true; } - static Float32List _fPointsFromSource( - PathRef source, double offsetX, double offsetY) { + static Float32List _fPointsFromSource(PathRef source, double offsetX, double offsetY) { final int sourceLength = source._fPointsLength; final int sourceCapacity = source._fPointsCapacity; final Float32List dest = Float32List(sourceCapacity * 2); @@ -428,14 +421,18 @@ class PathRef { } /// Copies contents from a source path [ref]. - void copy( - PathRef ref, int additionalReserveVerbs, int additionalReservePoints) { + void copy(PathRef ref, int additionalReserveVerbs, int additionalReservePoints) { ref.debugValidate(); final int verbCount = ref.countVerbs(); final int pointCount = ref.countPoints(); final int weightCount = ref.countWeights(); - resetToSize(verbCount, pointCount, weightCount, additionalReserveVerbs, - additionalReservePoints); + resetToSize( + verbCount, + pointCount, + weightCount, + additionalReserveVerbs, + additionalReservePoints, + ); _fVerbs.setAll(0, ref._fVerbs); fPoints.setAll(0, ref.fPoints); @@ -499,9 +496,11 @@ class PathRef { startEdit(); _resizePoints(newPointCount); final Float32List sourcePoints = source.points; - for (int source = pointCount * 2 - 1, dst = newPointCount * 2 - 1; - source >= 0; - source--, dst--) { + for ( + int source = pointCount * 2 - 1, dst = newPointCount * 2 - 1; + source >= 0; + source--, dst-- + ) { fPoints[dst] = sourcePoints[source]; } final int verbCount = countVerbs(); @@ -554,8 +553,7 @@ class PathRef { final Float32List outValues = out.points; final Float32List inValues = ending.points; for (int index = 0; index < count; ++index) { - outValues[index] = - outValues[index] * weight + inValues[index] * (1.0 - weight); + outValues[index] = outValues[index] * weight + inValues[index] * (1.0 - weight); } out.fBoundsIsDirty = true; out.startEdit(); @@ -613,8 +611,13 @@ class PathRef { /// Resets the path ref with verbCount verbs and pointCount points, all /// uninitialized. Also allocates space for reserveVerb additional verbs /// and reservePoints additional points. - void resetToSize(int verbCount, int pointCount, int conicCount, - [int reserveVerbs = 0, int reservePoints = 0]) { + void resetToSize( + int verbCount, + int pointCount, + int conicCount, [ + int reserveVerbs = 0, + int reservePoints = 0, + ]) { debugValidate(); fBoundsIsDirty = true; // this also invalidates fIsFinite @@ -933,8 +936,7 @@ class PathRef { // we wrapped around, so abort break; } - if (fPoints[index * 2] != fPoints[i * 2] || - fPoints[index * 2 + 1] != fPoints[i * 2 + 1]) { + if (fPoints[index * 2] != fPoints[i * 2] || fPoints[index * 2 + 1] != fPoints[i * 2 + 1]) { // found a different point, success! break; } @@ -978,8 +980,7 @@ class PathRefIterator { do { curPointIndex = _pointIndex; verb = nextIndex(); - } while ( - verb != SPath.kDoneVerb && (iterIndex == 0 || verb != SPath.kMoveVerb)); + } while (verb != SPath.kDoneVerb && (iterIndex == 0 || verb != SPath.kMoveVerb)); return (verb == SPath.kDoneVerb ? _pointIndex : curPointIndex) ~/ 2; } @@ -1070,9 +1071,7 @@ class PathRefIterator { double get conicWeight => pathRef._conicWeights![_conicWeightIndex]; - int peek() => _verbIndex < pathRef.countVerbs() - ? pathRef._fVerbs[_verbIndex] - : SPath.kDoneVerb; + int peek() => _verbIndex < pathRef.countVerbs() ? pathRef._fVerbs[_verbIndex] : SPath.kDoneVerb; } class _Corner { diff --git a/lib/web_ui/lib/src/engine/html/path/path_to_svg.dart b/lib/web_ui/lib/src/engine/html/path/path_to_svg.dart index ae08d0d53d2c6..43b2602c19644 100644 --- a/lib/web_ui/lib/src/engine/html/path/path_to_svg.dart +++ b/lib/web_ui/lib/src/engine/html/path/path_to_svg.dart @@ -24,15 +24,26 @@ String pathToSvg(PathRef pathRef, {double offsetX = 0, double offsetY = 0}) { case SPath.kLineVerb: buffer.write('L ${outPts[2] + offsetX} ${outPts[3] + offsetY}'); case SPath.kCubicVerb: - buffer.write('C ${outPts[2] + offsetX} ${outPts[3] + offsetY} ' - '${outPts[4] + offsetX} ${outPts[5] + offsetY} ${outPts[6] + offsetX} ${outPts[7] + offsetY}'); + buffer.write( + 'C ${outPts[2] + offsetX} ${outPts[3] + offsetY} ' + '${outPts[4] + offsetX} ${outPts[5] + offsetY} ${outPts[6] + offsetX} ${outPts[7] + offsetY}', + ); case SPath.kQuadVerb: - buffer.write('Q ${outPts[2] + offsetX} ${outPts[3] + offsetY} ' - '${outPts[4] + offsetX} ${outPts[5] + offsetY}'); + buffer.write( + 'Q ${outPts[2] + offsetX} ${outPts[3] + offsetY} ' + '${outPts[4] + offsetX} ${outPts[5] + offsetY}', + ); case SPath.kConicVerb: final double w = iter.conicWeight; - final Conic conic = Conic(outPts[0], outPts[1], outPts[2], outPts[3], - outPts[4], outPts[5], w); + final Conic conic = Conic( + outPts[0], + outPts[1], + outPts[2], + outPts[3], + outPts[4], + outPts[5], + w, + ); final List points = conic.toQuads(); final int len = points.length; for (int i = 1; i < len; i += 2) { @@ -40,8 +51,10 @@ String pathToSvg(PathRef pathRef, {double offsetX = 0, double offsetY = 0}) { final double p1y = points[i].dy; final double p2x = points[i + 1].dx; final double p2y = points[i + 1].dy; - buffer.write('Q ${p1x + offsetX} ${p1y + offsetY} ' - '${p2x + offsetX} ${p2y + offsetY}'); + buffer.write( + 'Q ${p1x + offsetX} ${p1y + offsetY} ' + '${p2x + offsetX} ${p2y + offsetY}', + ); } case SPath.kCloseVerb: buffer.write('Z'); diff --git a/lib/web_ui/lib/src/engine/html/path/path_utils.dart b/lib/web_ui/lib/src/engine/html/path/path_utils.dart index 8c77c3e4ef1f0..16e6e8110def9 100644 --- a/lib/web_ui/lib/src/engine/html/path/path_utils.dart +++ b/lib/web_ui/lib/src/engine/html/path/path_utils.dart @@ -37,10 +37,8 @@ abstract final class SPath { static const int kLineSegmentMask = SPathSegmentMask.kLine_SkPathSegmentMask; static const int kQuadSegmentMask = SPathSegmentMask.kQuad_SkPathSegmentMask; - static const int kConicSegmentMask = - SPathSegmentMask.kConic_SkPathSegmentMask; - static const int kCubicSegmentMask = - SPathSegmentMask.kCubic_SkPathSegmentMask; + static const int kConicSegmentMask = SPathSegmentMask.kConic_SkPathSegmentMask; + static const int kCubicSegmentMask = SPathSegmentMask.kCubic_SkPathSegmentMask; static const double scalarNearlyZero = 1.0 / (1 << 12); @@ -117,9 +115,8 @@ class QuadRoots { double? root1; /// Returns roots as list. - List get roots => (root0 == null) - ? [] - : (root1 == null ? [root0!] : [root0!, root1!]); + List get roots => + (root0 == null) ? [] : (root1 == null ? [root0!] : [root0!, root1!]); int findRoots(double a, double b, double c) { int rootCount = 0; @@ -205,8 +202,7 @@ bool isRRectOval(ui.RRect rrect) { double polyEval(double A, double B, double C, double t) => (A * t + B) * t + C; /// Evaluates degree 3 polynomial (cubic). -double polyEval4(double A, double B, double C, double D, double t) => - ((A * t + B) * t + C) * t + D; +double polyEval4(double A, double B, double C, double D, double t) => ((A * t + B) * t + C) * t + D; // Interpolate between two doubles (Not using lerpDouble here since it null // checks and treats values as 0). @@ -304,14 +300,13 @@ class Convexicator { // Detect straight and backwards direction change. // Instead of comparing absolute crossproduct size, compare // largest component double+crossproduct. - final double smallest = - math.min(curVecX, math.min(curVecY, math.min(lastX, lastY))); + final double smallest = math.min(curVecX, math.min(curVecY, math.min(lastX, lastY))); final double largest = math.max( - math.max(curVecX, math.max(curVecY, math.max(lastX, lastY))), - -smallest); + math.max(curVecX, math.max(curVecY, math.max(lastX, lastY))), + -smallest, + ); if (SPath.nearlyEqual(largest, largest + cross)) { - const double nearlyZeroSquared = - SPath.scalarNearlyZero * SPath.scalarNearlyZero; + const double nearlyZeroSquared = SPath.scalarNearlyZero * SPath.scalarNearlyZero; if (SPath.nearlyEqual(lengthSquared(lastX, lastY), nearlyZeroSquared) || SPath.nearlyEqual(lengthSquared(curVecX, curVecY), nearlyZeroSquared)) { // Length of either vector is smaller than tolerance to be able @@ -334,8 +329,7 @@ class Convexicator { if (_expectedDirection == DirChange.kInvalid) { // First valid direction. From this point on expect always left. _expectedDirection = dir; - _firstDirection = - isDirectionRight ? SPathDirection.kCW : SPathDirection.kCCW; + _firstDirection = isDirectionRight ? SPathDirection.kCW : SPathDirection.kCCW; } else if (dir != _expectedDirection) { _firstDirection = SPathDirection.kUnknown; return false; @@ -375,10 +369,8 @@ class Convexicator { int lastSy = kValueNeverReturnedBySign; for (int outerLoop = 0; outerLoop < 2; ++outerLoop) { while (pointIndex != lastPointIndex) { - final double vecX = pathRef.pointXAt(pointIndex) - - pathRef.pointXAt(currentPoint); - final double vecY = pathRef.pointYAt(pointIndex) - - pathRef.pointYAt(currentPoint); + final double vecX = pathRef.pointXAt(pointIndex) - pathRef.pointXAt(currentPoint); + final double vecY = pathRef.pointYAt(pointIndex) - pathRef.pointYAt(currentPoint); if (!(vecX == 0 && vecY == 0)) { // Give up if vector construction failed. // give up if vector construction failed @@ -412,7 +404,7 @@ enum DirChange { kRight, kStraight, kBackwards, // if double back, allow simple lines to be convex - kInvalid + kInvalid, } double lengthSquaredOffset(ui.Offset offset) { @@ -425,14 +417,13 @@ double lengthSquared(double dx, double dy) => dx * dx + dy * dy; /// Evaluates A * t^2 + B * t + C = 0 for quadratic curve. class SkQuadCoefficients { - SkQuadCoefficients( - double x0, double y0, double x1, double y1, double x2, double y2) - : cx = x0, - cy = y0, - bx = 2 * (x1 - x0), - by = 2 * (y1 - y0), - ax = x2 - (2 * x1) + x0, - ay = y2 - (2 * y1) + y0; + SkQuadCoefficients(double x0, double y0, double x1, double y1, double x2, double y2) + : cx = x0, + cy = y0, + bx = 2 * (x1 - x0), + by = 2 * (y1 - y0), + ax = x2 - (2 * x1) + x0, + ay = y2 - (2 * y1) + y0; final double ax, ay, bx, by, cx, cy; double evalX(double t) => (ax * t + bx) * t + cx; diff --git a/lib/web_ui/lib/src/engine/html/path/path_windings.dart b/lib/web_ui/lib/src/engine/html/path/path_windings.dart index 24779051d8732..4e1617f4c4572 100644 --- a/lib/web_ui/lib/src/engine/html/path/path_windings.dart +++ b/lib/web_ui/lib/src/engine/html/path/path_windings.dart @@ -98,8 +98,14 @@ class PathWinding { // Check if point starts the line, handle special case for horizontal lines // where and point except the end point is considered on curve. - static bool _checkOnCurve(double x, double y, double startX, double startY, - double endX, double endY) { + static bool _checkOnCurve( + double x, + double y, + double startX, + double startY, + double endX, + double endY, + ) { if (startY == endY) { // Horizontal line. return SPath.between(startX, x, endX) && x != endX; @@ -116,16 +122,27 @@ class PathWinding { n = _chopQuadAtExtrema(_buffer); } int winding = _computeMonoQuadWinding( - _buffer[0], _buffer[1], _buffer[2], _buffer[3], _buffer[4], _buffer[5]); + _buffer[0], + _buffer[1], + _buffer[2], + _buffer[3], + _buffer[4], + _buffer[5], + ); if (n > 0) { - winding += _computeMonoQuadWinding(_buffer[4], _buffer[5], _buffer[6], - _buffer[7], _buffer[8], _buffer[9]); + winding += _computeMonoQuadWinding( + _buffer[4], + _buffer[5], + _buffer[6], + _buffer[7], + _buffer[8], + _buffer[9], + ); } _w += winding; } - int _computeMonoQuadWinding( - double x0, double y0, double x1, double y1, double x2, double y2) { + int _computeMonoQuadWinding(double x0, double y0, double x1, double y1, double x2, double y2) { int dir = 1; final double startY = y0; final double endY = y2; @@ -147,8 +164,7 @@ class PathWinding { } final QuadRoots quadRoots = QuadRoots(); - final int n = quadRoots.findRoots( - startY - 2 * y1 + endY, 2 * (y1 - startY), startY - y); + final int n = quadRoots.findRoots(startY - 2 * y1 + endY, 2 * (y1 - startY), startY - y); assert(n <= 1); double xt; if (0 == n) { @@ -220,8 +236,15 @@ class PathWinding { } void _computeConicWinding(double weight) { - final Conic conic = Conic(_buffer[0], _buffer[1], _buffer[2], _buffer[3], - _buffer[4], _buffer[5], weight); + final Conic conic = Conic( + _buffer[0], + _buffer[1], + _buffer[2], + _buffer[3], + _buffer[4], + _buffer[5], + weight, + ); // If the data points are very large, the conic may not be monotonic but may also // fail to chop. Then, the chopper does not split the original conic in two. final bool isMono = _isQuadMonotonic(_buffer); @@ -275,7 +298,7 @@ class PathWinding { final double root = quadRoots.root0!; xt = Conic.evalNumerator(conic.p0x, conic.p1x, conic.p2x, conic.fW, root) / - Conic.evalDenominator(conic.fW, root); + Conic.evalDenominator(conic.fW, root); } if (SPath.nearlyEqual(xt, x)) { if (x != conic.p2x || y != conic.p2y) { diff --git a/lib/web_ui/lib/src/engine/html/path/tangent.dart b/lib/web_ui/lib/src/engine/html/path/tangent.dart index d65365b034596..2f09450834af6 100644 --- a/lib/web_ui/lib/src/engine/html/path/tangent.dart +++ b/lib/web_ui/lib/src/engine/html/path/tangent.dart @@ -11,8 +11,7 @@ import 'cubic.dart'; import 'path_utils.dart'; /// Computes tangent at point x,y on a line. -void tangentLine( - Float32List pts, double x, double y, List tangents) { +void tangentLine(Float32List pts, double x, double y, List tangents) { final double y0 = pts[1]; final double y1 = pts[3]; if (!SPath.between(y0, y, y1)) { @@ -32,8 +31,7 @@ void tangentLine( } /// Computes tangent at point x,y on a quadratic curve. -void tangentQuad( - Float32List pts, double x, double y, List tangents) { +void tangentQuad(Float32List pts, double x, double y, List tangents) { final double y0 = pts[1]; final double y1 = pts[3]; final double y2 = pts[5]; @@ -61,8 +59,15 @@ void tangentQuad( } } -ui.Offset _evalQuadTangentAt(double x0, double y0, double x1, double y1, - double x2, double y2, double t) { +ui.Offset _evalQuadTangentAt( + double x0, + double y0, + double x1, + double y1, + double x2, + double y2, + double t, +) { // The derivative of a quad equation is 2(b - a +(a - 2b +c)t). // This returns a zero tangent vector when t is 0 or 1, and the control // point is equal to the end point. In this case, use the quad end points to @@ -83,8 +88,7 @@ ui.Offset _evalQuadTangentAt(double x0, double y0, double x1, double y1, } /// Computes tangent at point x,y on a conic curve. -void tangentConic(Float32List pts, double x, double y, double weight, - List tangents) { +void tangentConic(Float32List pts, double x, double y, double weight, List tangents) { final double y0 = pts[1]; final double y1 = pts[3]; final double y2 = pts[5]; @@ -110,8 +114,7 @@ void tangentConic(Float32List pts, double x, double y, double weight, final int n = quadRoots.findRoots(A, 2 * B, C); for (int index = 0; index < n; ++index) { final double t = index == 0 ? quadRoots.root0! : quadRoots.root1!; - final double xt = Conic.evalNumerator(x0, x1, x2, weight, t) / - Conic.evalDenominator(weight, t); + final double xt = Conic.evalNumerator(x0, x1, x2, weight, t) / Conic.evalDenominator(weight, t); if (!SPath.nearlyEqual(x, xt)) { continue; } @@ -121,24 +124,19 @@ void tangentConic(Float32List pts, double x, double y, double weight, } /// Computes tangent at point x,y on a cubic curve. -void tangentCubic( - Float32List pts, double x, double y, List tangents) { +void tangentCubic(Float32List pts, double x, double y, List tangents) { final double y3 = pts[7]; final double y0 = pts[1]; final double y1 = pts[3]; final double y2 = pts[5]; - if (!SPath.between(y0, y, y1) && - !SPath.between(y1, y, y2) && - !SPath.between(y2, y, y3)) { + if (!SPath.between(y0, y, y1) && !SPath.between(y1, y, y2) && !SPath.between(y2, y, y3)) { return; } final double x0 = pts[0]; final double x1 = pts[2]; final double x2 = pts[4]; final double x3 = pts[6]; - if (!SPath.between(x0, x, x1) && - !SPath.between(x1, x, x2) && - !SPath.between(x2, x, x3)) { + if (!SPath.between(x0, x, x1) && !SPath.between(x1, x, x2) && !SPath.between(x2, x, x3)) { return; } final Float32List dst = Float32List(20); @@ -149,8 +147,13 @@ void tangentCubic( if (t == null) { continue; } - final double xt = evalCubicPts(dst[bufferPos], dst[bufferPos + 2], - dst[bufferPos + 4], dst[bufferPos + 6], t); + final double xt = evalCubicPts( + dst[bufferPos], + dst[bufferPos + 2], + dst[bufferPos + 4], + dst[bufferPos + 6], + t, + ); if (!SPath.nearlyEqual(x, xt)) { continue; } @@ -190,8 +193,17 @@ ui.Offset _evalCubicTangentAt(Float32List points, int bufferPos, double t) { } } -ui.Offset _evalCubicDerivative(double x0, double y0, double x1, double y1, - double x2, double y2, double x3, double y3, double t) { +ui.Offset _evalCubicDerivative( + double x0, + double y0, + double x1, + double y1, + double x2, + double y2, + double x3, + double y3, + double t, +) { final SkQuadCoefficients coeff = SkQuadCoefficients( x3 + 3 * (x1 - x2) - x0, y3 + 3 * (y1 - y2) - y0, diff --git a/lib/web_ui/lib/src/engine/html/path_to_svg_clip.dart b/lib/web_ui/lib/src/engine/html/path_to_svg_clip.dart index a8522a459b83d..f77c808a684dd 100644 --- a/lib/web_ui/lib/src/engine/html/path_to_svg_clip.dart +++ b/lib/web_ui/lib/src/engine/html/path_to_svg_clip.dart @@ -17,20 +17,23 @@ int _clipIdCounter = 0; /// /// Position needs to be absolute since these svgs are sandwiched between /// canvas elements and can cause layout shifts otherwise. -final SVGSVGElement kSvgResourceHeader = createSVGSVGElement() - ..setAttribute('width', 0) - ..setAttribute('height', 0) - ..style.position = 'absolute'; +final SVGSVGElement kSvgResourceHeader = + createSVGSVGElement() + ..setAttribute('width', 0) + ..setAttribute('height', 0) + ..style.position = 'absolute'; /// Converts Path to svg element that contains a clip-path definition. /// /// Calling this method updates [_clipIdCounter]. The HTML id of the generated /// clip is set to "svgClip${_clipIdCounter}", e.g. "svgClip123". -SVGSVGElement pathToSvgClipPath(ui.Path path, - {double offsetX = 0, - double offsetY = 0, - double scaleX = 1.0, - double scaleY = 1.0}) { +SVGSVGElement pathToSvgClipPath( + ui.Path path, { + double offsetX = 0, + double offsetY = 0, + double scaleX = 1.0, + double scaleY = 1.0, +}) { _clipIdCounter += 1; final SVGSVGElement root = kSvgResourceHeader.cloneNode(false) as SVGSVGElement; final SVGDefsElement defs = createSVGDefsElement(); @@ -56,7 +59,10 @@ SVGSVGElement pathToSvgClipPath(ui.Path path, } else { svgPath.setAttribute('clip-rule', 'nonzero'); } - svgPath.setAttribute('d', pathToSvg((path as SurfacePath).pathRef, offsetX: offsetX, offsetY: offsetY)); + svgPath.setAttribute( + 'd', + pathToSvg((path as SurfacePath).pathRef, offsetX: offsetX, offsetY: offsetY), + ); return root; } diff --git a/lib/web_ui/lib/src/engine/html/picture.dart b/lib/web_ui/lib/src/engine/html/picture.dart index 312b2642b9716..a694cc9d1693b 100644 --- a/lib/web_ui/lib/src/engine/html/picture.dart +++ b/lib/web_ui/lib/src/engine/html/picture.dart @@ -66,15 +66,15 @@ class EnginePicture implements ui.Picture { @override Future toImage(int width, int height) async { - final ui.Rect imageRect = - ui.Rect.fromLTRB(0, 0, width.toDouble(), height.toDouble()); + final ui.Rect imageRect = ui.Rect.fromLTRB(0, 0, width.toDouble(), height.toDouble()); final BitmapCanvas canvas = BitmapCanvas.imageData(imageRect); recordingCanvas!.apply(canvas, imageRect); final String imageDataUrl = canvas.toDataUrl(); - final DomHTMLImageElement imageElement = createDomHTMLImageElement() - ..src = imageDataUrl - ..width = width.toDouble() - ..height = height.toDouble(); + final DomHTMLImageElement imageElement = + createDomHTMLImageElement() + ..src = imageDataUrl + ..width = width.toDouble() + ..height = height.toDouble(); // The image loads asynchronously. We need to wait before returning, // otherwise the returned HtmlImage will be temporarily unusable. @@ -90,11 +90,7 @@ class EnginePicture implements ui.Picture { imageElement.addEventListener('error', errorListener); late final DomEventListener loadListener; loadListener = createDomEventListener((DomEvent event) { - onImageLoaded.complete(HtmlImage( - imageElement, - width, - height, - )); + onImageLoaded.complete(HtmlImage(imageElement, width, height)); imageElement.removeEventListener('load', loadListener); }); imageElement.addEventListener('load', loadListener); @@ -104,7 +100,8 @@ class EnginePicture implements ui.Picture { @override ui.Image toImageSync(int width, int height) { throw UnsupportedError( - 'toImageSync is not supported on the HTML backend. Use drawPicture instead, or toImage.'); + 'toImageSync is not supported on the HTML backend. Use drawPicture instead, or toImage.', + ); } bool _disposed = false; @@ -127,8 +124,7 @@ class EnginePicture implements ui.Picture { return result!; } - throw StateError( - 'Picture.debugDisposed is only available when asserts are enabled.'); + throw StateError('Picture.debugDisposed is only available when asserts are enabled.'); } @override @@ -169,10 +165,7 @@ void reduceCanvasMemoryUsage() { /// larger recycled canvases. Otherwise, small pictures would claim the large /// canvases forcing us to allocate new large canvases. class PaintRequest { - PaintRequest({ - required this.canvasSize, - required this.paintCallback, - }); + PaintRequest({required this.canvasSize, required this.paintCallback}); final ui.Size canvasSize; final ui.VoidCallback paintCallback; @@ -201,8 +194,7 @@ void _recycleCanvas(EngineCanvas? canvas) { } } if (debugShowCanvasReuseStats) { - DebugCanvasReuseOverlay.instance.inRecycleCount = - _recycledCanvases.length; + DebugCanvasReuseOverlay.instance.inRecycleCount = _recycledCanvases.length; } } else { canvas.dispose(); @@ -214,7 +206,7 @@ void _recycleCanvas(EngineCanvas? canvas) { /// to draw shapes and text. class PersistedPicture extends PersistedLeafSurface { PersistedPicture(this.dx, this.dy, this.picture, this.hints) - : localPaintBounds = picture.recordingCanvas!.pictureBounds; + : localPaintBounds = picture.recordingCanvas!.pictureBounds; EngineCanvas? _canvas; @@ -234,8 +226,7 @@ class PersistedPicture extends PersistedLeafSurface { bool _requiresRepaint = false; /// Cache for reusing elements such as images across picture updates. - CrossFrameCache? _elementCache = - CrossFrameCache(); + CrossFrameCache? _elementCache = CrossFrameCache(); @override DomElement createElement() { @@ -257,8 +248,7 @@ class PersistedPicture extends PersistedLeafSurface { @override void preroll(PrerollSurfaceContext prerollContext) { - if (prerollContext.activeShaderMaskCount != 0 || - prerollContext.activeColorFilterCount != 0) { + if (prerollContext.activeShaderMaskCount != 0 || prerollContext.activeColorFilterCount != 0) { picture.recordingCanvas?.renderStrategy.isInsideSvgFilterTree = true; } super.preroll(prerollContext); @@ -325,8 +315,7 @@ class PersistedPicture extends PersistedLeafSurface { if (bounds == null) { bounds = clipTransform.transformRect(localClipBounds); } else { - bounds = - bounds.intersect(clipTransform.transformRect(localClipBounds)); + bounds = bounds.intersect(clipTransform.transformRect(localClipBounds)); } } final Matrix4? localInverse = parentSurface.localTransformInverse; @@ -372,9 +361,8 @@ class PersistedPicture extends PersistedLeafSurface { assert(oldSurface._optimalLocalCullRect != null); final bool surfaceBeingRetained = identical(oldSurface, this); - final ui.Rect? oldOptimalLocalCullRect = surfaceBeingRetained - ? _optimalLocalCullRect - : oldSurface._optimalLocalCullRect; + final ui.Rect? oldOptimalLocalCullRect = + surfaceBeingRetained ? _optimalLocalCullRect : oldSurface._optimalLocalCullRect; if (_exactLocalCullRect == ui.Rect.zero) { // The clip collapsed into a zero-sized rectangle. If it was already zero, @@ -408,25 +396,17 @@ class PersistedPicture extends PersistedLeafSurface { // Compute the delta, by which each of the side of the clip rect has "moved" // since the last time we updated the cull rect. - final double leftwardDelta = - oldOptimalLocalCullRect.left - _exactLocalCullRect!.left; - final double upwardDelta = - oldOptimalLocalCullRect.top - _exactLocalCullRect!.top; - final double rightwardDelta = - _exactLocalCullRect!.right - oldOptimalLocalCullRect.right; - final double bottomwardDelta = - _exactLocalCullRect!.bottom - oldOptimalLocalCullRect.bottom; + final double leftwardDelta = oldOptimalLocalCullRect.left - _exactLocalCullRect!.left; + final double upwardDelta = oldOptimalLocalCullRect.top - _exactLocalCullRect!.top; + final double rightwardDelta = _exactLocalCullRect!.right - oldOptimalLocalCullRect.right; + final double bottomwardDelta = _exactLocalCullRect!.bottom - oldOptimalLocalCullRect.bottom; // Compute the new optimal rect to paint into. final ui.Rect newLocalCullRect = ui.Rect.fromLTRB( - _exactLocalCullRect!.left - - _predictTrend(leftwardDelta, _exactLocalCullRect!.width), - _exactLocalCullRect!.top - - _predictTrend(upwardDelta, _exactLocalCullRect!.height), - _exactLocalCullRect!.right + - _predictTrend(rightwardDelta, _exactLocalCullRect!.width), - _exactLocalCullRect!.bottom + - _predictTrend(bottomwardDelta, _exactLocalCullRect!.height), + _exactLocalCullRect!.left - _predictTrend(leftwardDelta, _exactLocalCullRect!.width), + _exactLocalCullRect!.top - _predictTrend(upwardDelta, _exactLocalCullRect!.height), + _exactLocalCullRect!.right + _predictTrend(rightwardDelta, _exactLocalCullRect!.width), + _exactLocalCullRect!.bottom + _predictTrend(bottomwardDelta, _exactLocalCullRect!.height), ).intersect(localPaintBounds!); _requiresRepaint = _optimalLocalCullRect != newLocalCullRect; @@ -445,10 +425,7 @@ class PersistedPicture extends PersistedLeafSurface { // 50% of the extent (protect from extremely slow growth trend such as // slow scrolling). Give no more than the full extent (protects from // fast scrolling that could lead to overallocation). - return math.min( - math.max(extent * 0.5, delta * 10.0), - extent, - ); + return math.min(math.max(extent * 0.5, delta * 10.0), extent); } } @@ -508,10 +485,9 @@ class PersistedPicture extends PersistedLeafSurface { return 1.0; } - final bool didRequireBitmap = existingSurface - .picture.recordingCanvas!.renderStrategy.hasArbitraryPaint; - final bool requiresBitmap = - picture.recordingCanvas!.renderStrategy.hasArbitraryPaint; + final bool didRequireBitmap = + existingSurface.picture.recordingCanvas!.renderStrategy.hasArbitraryPaint; + final bool requiresBitmap = picture.recordingCanvas!.renderStrategy.hasArbitraryPaint; if (didRequireBitmap != requiresBitmap) { // Switching canvas types is always expensive. return 1.0; @@ -532,17 +508,18 @@ class PersistedPicture extends PersistedLeafSurface { } else { final int newPixelCount = BitmapCanvas.widthToPhysical(_exactLocalCullRect!.width) * - BitmapCanvas.heightToPhysical(_exactLocalCullRect!.height); - final int oldPixelCount = - oldCanvas.widthInBitmapPixels * oldCanvas.heightInBitmapPixels; + BitmapCanvas.heightToPhysical(_exactLocalCullRect!.height); + final int oldPixelCount = oldCanvas.widthInBitmapPixels * oldCanvas.heightInBitmapPixels; if (oldPixelCount == 0) { return 1.0; } final double pixelCountRatio = newPixelCount / oldPixelCount; - assert(0 <= pixelCountRatio && pixelCountRatio <= 1.0, - 'Invalid pixel count ratio $pixelCountRatio'); + assert( + 0 <= pixelCountRatio && pixelCountRatio <= 1.0, + 'Invalid pixel count ratio $pixelCountRatio', + ); return 1.0 - pixelCountRatio; } } @@ -590,23 +567,23 @@ class PersistedPicture extends PersistedLeafSurface { // able to reuse have been released yet. So instead we enqueue this // picture to be painted after the update cycle is done syncing the layer // tree then reuse canvases that were freed up. - paintQueue.add(PaintRequest( - canvasSize: _optimalLocalCullRect!.size, - paintCallback: () { - final BitmapCanvas bitmapCanvas = - _findOrCreateCanvas(_optimalLocalCullRect!); - _canvas = bitmapCanvas; - bitmapCanvas.setElementCache(_elementCache); - if (debugExplainSurfaceStats) { - surfaceStatsFor(this).paintPixelCount += - bitmapCanvas.bitmapPixelCount; - } - removeAllChildren(rootElement!); - rootElement!.append(bitmapCanvas.rootElement); - bitmapCanvas.clear(); - picture.recordingCanvas!.apply(bitmapCanvas, _optimalLocalCullRect!); - }, - )); + paintQueue.add( + PaintRequest( + canvasSize: _optimalLocalCullRect!.size, + paintCallback: () { + final BitmapCanvas bitmapCanvas = _findOrCreateCanvas(_optimalLocalCullRect!); + _canvas = bitmapCanvas; + bitmapCanvas.setElementCache(_elementCache); + if (debugExplainSurfaceStats) { + surfaceStatsFor(this).paintPixelCount += bitmapCanvas.bitmapPixelCount; + } + removeAllChildren(rootElement!); + rootElement!.append(bitmapCanvas.rootElement); + bitmapCanvas.clear(); + picture.recordingCanvas!.apply(bitmapCanvas, _optimalLocalCullRect!); + }, + ), + ); } } @@ -632,8 +609,7 @@ class PersistedPicture extends PersistedLeafSurface { } final ui.Size candidateSize = candidate.size; - final double candidatePixelCount = - candidateSize.width * candidateSize.height; + final double candidatePixelCount = candidateSize.width * candidateSize.height; final bool fits = candidate.doesFitBounds(bounds, _density); final bool isSmaller = candidatePixelCount < lastPixelCount; @@ -641,14 +617,13 @@ class PersistedPicture extends PersistedLeafSurface { // [isTooSmall] is used to make sure that a small picture doesn't // reuse and hold onto memory of a large canvas. final double requestedPixelCount = bounds.width * bounds.height; - final bool isTooSmall = isSmaller && - requestedPixelCount > 1 && - (candidatePixelCount / requestedPixelCount) > 4; + final bool isTooSmall = + isSmaller && requestedPixelCount > 1 && (candidatePixelCount / requestedPixelCount) > 4; if (!isTooSmall) { bestRecycledCanvas = candidate; lastPixelCount = candidatePixelCount; - final bool fitsExactly = candidateSize.width == canvasSize.width && - candidateSize.height == canvasSize.height; + final bool fitsExactly = + candidateSize.width == canvasSize.width && candidateSize.height == canvasSize.height; if (fitsExactly) { // No need to keep looking any more. break; @@ -663,8 +638,7 @@ class PersistedPicture extends PersistedLeafSurface { } _recycledCanvases.remove(bestRecycledCanvas); if (debugShowCanvasReuseStats) { - DebugCanvasReuseOverlay.instance.inRecycleCount = - _recycledCanvases.length; + DebugCanvasReuseOverlay.instance.inRecycleCount = _recycledCanvases.length; } if (debugShowCanvasReuseStats) { DebugCanvasReuseOverlay.instance.reusedCount++; @@ -678,14 +652,15 @@ class PersistedPicture extends PersistedLeafSurface { DebugCanvasReuseOverlay.instance.createdCount++; } final BitmapCanvas canvas = BitmapCanvas( - bounds, picture.recordingCanvas!.renderStrategy, - density: _density); + bounds, + picture.recordingCanvas!.renderStrategy, + density: _density, + ); canvas.setElementCache(_elementCache); if (debugExplainSurfaceStats) { surfaceStatsFor(this) ..allocateBitmapCanvasCount += 1 - ..allocatedBitmapSizeInPixels = - canvas.widthInBitmapPixels * canvas.heightInBitmapPixels; + ..allocatedBitmapSizeInPixels = canvas.widthInBitmapPixels * canvas.heightInBitmapPixels; } return canvas; } @@ -722,8 +697,8 @@ class PersistedPicture extends PersistedLeafSurface { _computeOptimalCullRect(oldSurface); if (identical(picture, oldSurface.picture)) { - final bool densityChanged = _canvas is BitmapCanvas && - _density != (_canvas! as BitmapCanvas).density; + final bool densityChanged = + _canvas is BitmapCanvas && _density != (_canvas! as BitmapCanvas).density; // The picture is the same. Attempt to avoid repaint. if (_requiresRepaint || densityChanged) { @@ -765,8 +740,7 @@ class PersistedPicture extends PersistedLeafSurface { final int canvasHash = firstChild.hashCode; buffer.writeln('${' ' * (indent + 1)}<$canvasTag @$canvasHash />'); } else if (rootElement != null) { - buffer.writeln( - '${' ' * (indent + 1)}<${rootElement!.tagName.toLowerCase()} @$hashCode />'); + buffer.writeln('${' ' * (indent + 1)}<${rootElement!.tagName.toLowerCase()} @$hashCode />'); } else { buffer.writeln('${' ' * (indent + 1)}'); } @@ -778,8 +752,7 @@ class PersistedPicture extends PersistedLeafSurface { if (picture.recordingCanvas!.didDraw) { if (!_optimalLocalCullRect!.isEmpty && canvas == null) { - validationErrors - .add('$runtimeType has non-trivial picture but it has null canvas'); + validationErrors.add('$runtimeType has non-trivial picture but it has null canvas'); } if (_optimalLocalCullRect == null) { validationErrors.add('$runtimeType has null _optimalLocalCullRect'); diff --git a/lib/web_ui/lib/src/engine/html/recording_canvas.dart b/lib/web_ui/lib/src/engine/html/recording_canvas.dart index 1f353fae20802..578efba9b7261 100644 --- a/lib/web_ui/lib/src/engine/html/recording_canvas.dart +++ b/lib/web_ui/lib/src/engine/html/recording_canvas.dart @@ -30,8 +30,7 @@ double _measureBorderRadius(double x, double y) => x * x + y * y; /// /// See [Canvas] for docs for these methods. class RecordingCanvas { - RecordingCanvas(ui.Rect? bounds) - : _paintBounds = _PaintBounds(bounds ?? ui.Rect.largest); + RecordingCanvas(ui.Rect? bounds) : _paintBounds = _PaintBounds(bounds ?? ui.Rect.largest); /// Computes [_pictureBounds]. final _PaintBounds _paintBounds; @@ -134,9 +133,10 @@ class RecordingCanvas { final StringBuffer debugBuf = StringBuffer(); int skips = 0; debugBuf.writeln( - '--- Applying RecordingCanvas to ${engineCanvas.runtimeType} ' - 'with bounds $_paintBounds and clip $clipRect (w = ${clipRect.width},' - ' h = ${clipRect.height})'); + '--- Applying RecordingCanvas to ${engineCanvas.runtimeType} ' + 'with bounds $_paintBounds and clip $clipRect (w = ${clipRect.width},' + ' h = ${clipRect.height})', + ); for (int i = 0; i < _commands.length; i++) { final PaintCommand command = _commands[i]; if (command is DrawCommand) { @@ -314,8 +314,7 @@ class RecordingCanvas { _commands.add(command); } - ui.Rect? getDestinationClipBounds() => - _paintBounds.getDestinationClipBounds(); + ui.Rect? getDestinationClipBounds() => _paintBounds.getDestinationClipBounds(); void drawColor(ui.Color color, ui.BlendMode blendMode) { assert(!_recordingEnded); @@ -326,8 +325,10 @@ class RecordingCanvas { void drawLine(ui.Offset p1, ui.Offset p2, SurfacePaint paint) { assert(!_recordingEnded); - assert(paint.shader == null || paint.shader is! EngineImageShader, - 'ImageShader not supported yet'); + assert( + paint.shader == null || paint.shader is! EngineImageShader, + 'ImageShader not supported yet', + ); final double paintSpread = math.max(_getPaintSpread(paint), 1.0); final PaintDrawLine command = PaintDrawLine(p1, p2, paint.paintData); // TODO(yjbanov): This can be optimized. Currently we create a box around @@ -351,8 +352,10 @@ class RecordingCanvas { void drawPaint(SurfacePaint paint) { assert(!_recordingEnded); - assert(paint.shader == null || paint.shader is! EngineImageShader, - 'ImageShader not supported yet'); + assert( + paint.shader == null || paint.shader is! EngineImageShader, + 'ImageShader not supported yet', + ); renderStrategy.hasArbitraryPaint = true; _didDraw = true; final PaintDrawPaint command = PaintDrawPaint(paint.paintData); @@ -406,36 +409,24 @@ class RecordingCanvas { final ui.RRect scaledOuter = outer.scaleRadii(); final ui.RRect scaledInner = inner.scaleRadii(); - final double outerTl = - _measureBorderRadius(scaledOuter.tlRadiusX, scaledOuter.tlRadiusY); - final double outerTr = - _measureBorderRadius(scaledOuter.trRadiusX, scaledOuter.trRadiusY); - final double outerBl = - _measureBorderRadius(scaledOuter.blRadiusX, scaledOuter.blRadiusY); - final double outerBr = - _measureBorderRadius(scaledOuter.brRadiusX, scaledOuter.brRadiusY); - - final double innerTl = - _measureBorderRadius(scaledInner.tlRadiusX, scaledInner.tlRadiusY); - final double innerTr = - _measureBorderRadius(scaledInner.trRadiusX, scaledInner.trRadiusY); - final double innerBl = - _measureBorderRadius(scaledInner.blRadiusX, scaledInner.blRadiusY); - final double innerBr = - _measureBorderRadius(scaledInner.brRadiusX, scaledInner.brRadiusY); - - if (innerTl > outerTl || - innerTr > outerTr || - innerBl > outerBl || - innerBr > outerBr) { + final double outerTl = _measureBorderRadius(scaledOuter.tlRadiusX, scaledOuter.tlRadiusY); + final double outerTr = _measureBorderRadius(scaledOuter.trRadiusX, scaledOuter.trRadiusY); + final double outerBl = _measureBorderRadius(scaledOuter.blRadiusX, scaledOuter.blRadiusY); + final double outerBr = _measureBorderRadius(scaledOuter.brRadiusX, scaledOuter.brRadiusY); + + final double innerTl = _measureBorderRadius(scaledInner.tlRadiusX, scaledInner.tlRadiusY); + final double innerTr = _measureBorderRadius(scaledInner.trRadiusX, scaledInner.trRadiusY); + final double innerBl = _measureBorderRadius(scaledInner.blRadiusX, scaledInner.blRadiusY); + final double innerBr = _measureBorderRadius(scaledInner.brRadiusX, scaledInner.brRadiusY); + + if (innerTl > outerTl || innerTr > outerTr || innerBl > outerBl || innerBr > outerBr) { return; // Some inner radius is overlapping some outer radius } renderStrategy.hasArbitraryPaint = true; _didDraw = true; final double paintSpread = _getPaintSpread(paint); - final PaintDrawDRRect command = - PaintDrawDRRect(outer, inner, paint.paintData); + final PaintDrawDRRect command = PaintDrawDRRect(outer, inner, paint.paintData); final double left = math.min(outer.left, outer.right); final double right = math.max(outer.left, outer.right); final double top = math.min(outer.top, outer.bottom); @@ -523,8 +514,7 @@ class RecordingCanvas { } // Clone path so it can be reused for subsequent draw calls. final ui.Path clone = SurfacePath.shallowCopy(path); - final PaintDrawPath command = - PaintDrawPath(clone as SurfacePath, paint.paintData); + final PaintDrawPath command = PaintDrawPath(clone as SurfacePath, paint.paintData); _paintBounds.grow(pathBounds, command); clone.fillType = sPath.fillType; _commands.add(command); @@ -533,17 +523,17 @@ class RecordingCanvas { void drawImage(ui.Image image, ui.Offset offset, SurfacePaint paint) { assert(!_recordingEnded); - assert(paint.shader == null || paint.shader is! EngineImageShader, - 'ImageShader not supported yet'); + assert( + paint.shader == null || paint.shader is! EngineImageShader, + 'ImageShader not supported yet', + ); renderStrategy.hasArbitraryPaint = true; renderStrategy.hasImageElements = true; _didDraw = true; final double left = offset.dx; final double top = offset.dy; - final PaintDrawImage command = - PaintDrawImage(image, offset, paint.paintData); - _paintBounds.growLTRB( - left, top, left + image.width, top + image.height, command); + final PaintDrawImage command = PaintDrawImage(image, offset, paint.paintData); + _paintBounds.growLTRB(left, top, left + image.width, top + image.height, command); _commands.add(command); } @@ -569,16 +559,16 @@ class RecordingCanvas { } } - void drawImageRect( - ui.Image image, ui.Rect src, ui.Rect dst, SurfacePaint paint) { + void drawImageRect(ui.Image image, ui.Rect src, ui.Rect dst, SurfacePaint paint) { assert(!_recordingEnded); - assert(paint.shader == null || paint.shader is! EngineImageShader, - 'ImageShader not supported yet'); + assert( + paint.shader == null || paint.shader is! EngineImageShader, + 'ImageShader not supported yet', + ); renderStrategy.hasArbitraryPaint = true; renderStrategy.hasImageElements = true; _didDraw = true; - final PaintDrawImageRect command = - PaintDrawImageRect(image, src, dst, paint.paintData); + final PaintDrawImageRect command = PaintDrawImageRect(image, src, dst, paint.paintData); _paintBounds.grow(dst, command); _commands.add(command); } @@ -596,8 +586,7 @@ class RecordingCanvas { renderStrategy.hasArbitraryPaint = true; } renderStrategy.hasParagraphs = true; - final PaintDrawParagraph command = - PaintDrawParagraph(engineParagraph, offset); + final PaintDrawParagraph command = PaintDrawParagraph(engineParagraph, offset); final ui.Rect paragraphBounds = engineParagraph.paintBounds; _paintBounds.growLTRB( @@ -611,43 +600,45 @@ class RecordingCanvas { _commands.add(command); } - void drawShadow(ui.Path path, ui.Color color, double elevation, - bool transparentOccluder) { + void drawShadow(ui.Path path, ui.Color color, double elevation, bool transparentOccluder) { assert(!_recordingEnded); renderStrategy.hasArbitraryPaint = true; _didDraw = true; - final ui.Rect shadowRect = - computePenumbraBounds(path.getBounds(), elevation); + final ui.Rect shadowRect = computePenumbraBounds(path.getBounds(), elevation); final PaintDrawShadow command = PaintDrawShadow( - path as SurfacePath, color, elevation, transparentOccluder); + path as SurfacePath, + color, + elevation, + transparentOccluder, + ); _paintBounds.grow(shadowRect, command); _commands.add(command); } - void drawVertices( - SurfaceVertices vertices, ui.BlendMode blendMode, SurfacePaint paint) { + void drawVertices(SurfaceVertices vertices, ui.BlendMode blendMode, SurfacePaint paint) { assert(!_recordingEnded); renderStrategy.hasArbitraryPaint = true; _didDraw = true; - final PaintDrawVertices command = - PaintDrawVertices(vertices, blendMode, paint.paintData); + final PaintDrawVertices command = PaintDrawVertices(vertices, blendMode, paint.paintData); _growPaintBoundsByPoints(vertices.positions, 0, paint, command); _commands.add(command); } - void drawRawPoints( - ui.PointMode pointMode, Float32List points, SurfacePaint paint) { + void drawRawPoints(ui.PointMode pointMode, Float32List points, SurfacePaint paint) { assert(!_recordingEnded); renderStrategy.hasArbitraryPaint = true; _didDraw = true; - final PaintDrawPoints command = - PaintDrawPoints(pointMode, points, paint.paintData); + final PaintDrawPoints command = PaintDrawPoints(pointMode, points, paint.paintData); _growPaintBoundsByPoints(points, paint.strokeWidth, paint, command); _commands.add(command); } - void _growPaintBoundsByPoints(Float32List points, double thickness, - SurfacePaint paint, DrawCommand command) { + void _growPaintBoundsByPoints( + Float32List points, + double thickness, + SurfacePaint paint, + DrawCommand command, + ) { double minValueX, maxValueX, minValueY, maxValueY; minValueX = maxValueX = points[0]; minValueY = maxValueY = points[1]; @@ -1099,11 +1090,12 @@ class PaintDrawRRect extends DrawCommand { class PaintDrawDRRect extends DrawCommand { PaintDrawDRRect(this.outer, this.inner, this.paint) { - path = ui.Path() - ..fillType = ui.PathFillType.evenOdd - ..addRRect(outer) - ..addRRect(inner) - ..close(); + path = + ui.Path() + ..fillType = ui.PathFillType.evenOdd + ..addRRect(outer) + ..addRRect(inner) + ..close(); } final ui.RRect outer; @@ -1196,8 +1188,7 @@ class PaintDrawPath extends DrawCommand { } class PaintDrawShadow extends DrawCommand { - PaintDrawShadow( - this.path, this.color, this.elevation, this.transparentOccluder); + PaintDrawShadow(this.path, this.color, this.elevation, this.transparentOccluder); final SurfacePath path; final ui.Color color; @@ -1300,9 +1291,10 @@ class Subpath { final List commands; Subpath shift(ui.Offset offset) { - final Subpath result = Subpath(startX + offset.dx, startY + offset.dy) - ..currentX = currentX + offset.dx - ..currentY = currentY + offset.dy; + final Subpath result = + Subpath(startX + offset.dx, startY + offset.dy) + ..currentX = currentX + offset.dx + ..currentY = currentY + offset.dy; for (final PathCommand command in commands) { result.commands.add(command.shifted(offset)); @@ -1331,9 +1323,10 @@ abstract class PathCommand { void transform(Float32List matrix4, SurfacePath targetPath); /// Helper method for implementing transforms. - static ui.Offset _transformOffset(double x, double y, Float32List matrix4) => - ui.Offset((matrix4[0] * x) + (matrix4[4] * y) + matrix4[12], - (matrix4[1] * x) + (matrix4[5] * y) + matrix4[13]); + static ui.Offset _transformOffset(double x, double y, Float32List matrix4) => ui.Offset( + (matrix4[0] * x) + (matrix4[4] * y) + matrix4[12], + (matrix4[1] * x) + (matrix4[5] * y) + matrix4[13], + ); } class MoveTo extends PathCommand { @@ -1393,8 +1386,16 @@ class LineTo extends PathCommand { } class Ellipse extends PathCommand { - const Ellipse(this.x, this.y, this.radiusX, this.radiusY, this.rotation, - this.startAngle, this.endAngle, this.anticlockwise); + const Ellipse( + this.x, + this.y, + this.radiusX, + this.radiusY, + this.rotation, + this.startAngle, + this.endAngle, + this.anticlockwise, + ); final double x; final double y; @@ -1407,37 +1408,46 @@ class Ellipse extends PathCommand { @override Ellipse shifted(ui.Offset offset) { - return Ellipse(x + offset.dx, y + offset.dy, radiusX, radiusY, rotation, - startAngle, endAngle, anticlockwise); + return Ellipse( + x + offset.dx, + y + offset.dy, + radiusX, + radiusY, + rotation, + startAngle, + endAngle, + anticlockwise, + ); } @override void transform(Float32List matrix4, SurfacePath targetPath) { final ui.Path bezierPath = ui.Path(); _drawArcWithBezier( - x, - y, - radiusX, - radiusY, - rotation, - startAngle, - anticlockwise ? startAngle - endAngle : endAngle - startAngle, - matrix4, - bezierPath); - targetPath.addPathWithMode( - bezierPath, 0, 0, matrix4, SPathAddPathMode.kAppend); + x, + y, + radiusX, + radiusY, + rotation, + startAngle, + anticlockwise ? startAngle - endAngle : endAngle - startAngle, + matrix4, + bezierPath, + ); + targetPath.addPathWithMode(bezierPath, 0, 0, matrix4, SPathAddPathMode.kAppend); } void _drawArcWithBezier( - double centerX, - double centerY, - double radiusX, - double radiusY, - double rotation, - double startAngle, - double sweep, - Float32List matrix4, - ui.Path targetPath) { + double centerX, + double centerY, + double radiusX, + double radiusY, + double rotation, + double startAngle, + double sweep, + Float32List matrix4, + ui.Path targetPath, + ) { double ratio = sweep.abs() / (math.pi / 2.0); if ((1.0 - ratio).abs() < 0.0000001) { ratio = 1.0; @@ -1446,23 +1456,34 @@ class Ellipse extends PathCommand { final double anglePerSegment = sweep / segments; double angle = startAngle; for (int segment = 0; segment < segments; segment++) { - _drawArcSegment(targetPath, centerX, centerY, radiusX, radiusY, rotation, - angle, anglePerSegment, segment == 0, matrix4); + _drawArcSegment( + targetPath, + centerX, + centerY, + radiusX, + radiusY, + rotation, + angle, + anglePerSegment, + segment == 0, + matrix4, + ); angle += anglePerSegment; } } void _drawArcSegment( - ui.Path path, - double centerX, - double centerY, - double radiusX, - double radiusY, - double rotation, - double startAngle, - double sweep, - bool startPath, - Float32List matrix4) { + ui.Path path, + double centerX, + double centerY, + double radiusX, + double radiusY, + double rotation, + double startAngle, + double sweep, + bool startPath, + Float32List matrix4, + ) { final double s = 4 / 3 * math.tan(sweep / 4); // Rotate unit vector to startAngle and endAngle to use for computing start @@ -1497,21 +1518,24 @@ class Ellipse extends PathCommand { } } if (rotation == 0.0) { - path.cubicTo(centerX + cpx1, centerY + cpy1, centerX + cpx2, - centerY + cpy2, endPointX, endPointY); + path.cubicTo( + centerX + cpx1, + centerY + cpy1, + centerX + cpx2, + centerY + cpy2, + endPointX, + endPointY, + ); } else { final double rotatedCpx1 = centerX + (cpx1 * cosR) + (cpy1 * sinR); final double rotatedCpy1 = centerY + (cpy1 * cosR) - (cpx1 * sinR); final double rotatedCpx2 = centerX + (cpx2 * cosR) + (cpy2 * sinR); final double rotatedCpy2 = centerY + (cpy2 * cosR) - (cpx2 * sinR); - final double rotatedEndX = centerX + - ((endPointX - centerX) * cosR) + - ((endPointY - centerY) * sinR); - final double rotatedEndY = centerY + - ((endPointY - centerY) * cosR) - - ((endPointX - centerX) * sinR); - path.cubicTo(rotatedCpx1, rotatedCpy1, rotatedCpx2, rotatedCpy2, - rotatedEndX, rotatedEndY); + final double rotatedEndX = + centerX + ((endPointX - centerX) * cosR) + ((endPointY - centerY) * sinR); + final double rotatedEndY = + centerY + ((endPointY - centerY) * cosR) - ((endPointX - centerX) * sinR); + path.cubicTo(rotatedCpx1, rotatedCpy1, rotatedCpx2, rotatedCpy2, rotatedEndX, rotatedEndY); } } @@ -1536,8 +1560,7 @@ class QuadraticCurveTo extends PathCommand { @override QuadraticCurveTo shifted(ui.Offset offset) { - return QuadraticCurveTo( - x1 + offset.dx, y1 + offset.dy, x2 + offset.dx, y2 + offset.dy); + return QuadraticCurveTo(x1 + offset.dx, y1 + offset.dy, x2 + offset.dx, y2 + offset.dy); } @override @@ -1552,8 +1575,7 @@ class QuadraticCurveTo extends PathCommand { final double transformedY1 = (m1 * x1) + (m5 * y1) + m13; final double transformedX2 = (m0 * x2) + (m4 * y2) + m12; final double transformedY2 = (m1 * x2) + (m5 * y2) + m13; - targetPath.quadraticBezierTo( - transformedX1, transformedY1, transformedX2, transformedY2); + targetPath.quadraticBezierTo(transformedX1, transformedY1, transformedX2, transformedY2); } @override @@ -1579,8 +1601,14 @@ class BezierCurveTo extends PathCommand { @override BezierCurveTo shifted(ui.Offset offset) { - return BezierCurveTo(x1 + offset.dx, y1 + offset.dy, x2 + offset.dx, - y2 + offset.dy, x3 + offset.dx, y3 + offset.dy); + return BezierCurveTo( + x1 + offset.dx, + y1 + offset.dy, + x2 + offset.dx, + y2 + offset.dy, + x3 + offset.dx, + y3 + offset.dy, + ); } @override @@ -1597,8 +1625,14 @@ class BezierCurveTo extends PathCommand { final double transformedY2 = (s1 * x2) + (s5 * y2) + s13; final double transformedX3 = (s0 * x3) + (s4 * y3) + s12; final double transformedY3 = (s1 * x3) + (s5 * y3) + s13; - targetPath.cubicTo(transformedX1, transformedY1, transformedX2, - transformedY2, transformedX3, transformedY3); + targetPath.cubicTo( + transformedX1, + transformedY1, + transformedX2, + transformedY2, + transformedX3, + transformedY3, + ); } @override @@ -1648,8 +1682,9 @@ class RectCommand extends PathCommand { transformedX1 == transformedX4 && transformedX2 == transformedX3) { // It is still a rectangle. - targetPath.addRect(ui.Rect.fromLTRB( - transformedX1, transformedY1, transformedX3, transformedY3)); + targetPath.addRect( + ui.Rect.fromLTRB(transformedX1, transformedY1, transformedX3, transformedY3), + ); } else { targetPath.moveTo(transformedX1, transformedY1); targetPath.lineTo(transformedX2, transformedY2); @@ -1684,8 +1719,7 @@ class RRectCommand extends PathCommand { void transform(Float32List matrix4, SurfacePath targetPath) { final ui.Path roundRectPath = ui.Path(); RRectToPathRenderer(roundRectPath).render(rrect); - targetPath.addPathWithMode( - roundRectPath, 0, 0, matrix4, SPathAddPathMode.kAppend); + targetPath.addPathWithMode(roundRectPath, 0, 0, matrix4, SPathAddPathMode.kAppend); } @override @@ -1830,8 +1864,7 @@ class _PaintBounds { _currentClipBottom = bottom; } } - if (_currentClipLeft >= _currentClipRight || - _currentClipTop >= _currentClipBottom) { + if (_currentClipLeft >= _currentClipRight || _currentClipTop >= _currentClipBottom) { command.isClippedOut = true; } else { command.leftBound = _currentClipLeft; @@ -1861,8 +1894,7 @@ class _PaintBounds { /// Grow painted area to include given rectangle and precompute /// clipped out state for command. - void growLTRB(double left, double top, double right, double bottom, - DrawCommand command) { + void growLTRB(double left, double top, double right, double bottom, DrawCommand command) { if (left == right || top == bottom) { command.isClippedOut = true; return; @@ -1923,14 +1955,10 @@ class _PaintBounds { command.bottomBound = transformedPointBottom; if (_didPaintInsideClipArea) { - _left = math.min( - math.min(_left, transformedPointLeft), transformedPointRight); - _right = math.max( - math.max(_right, transformedPointLeft), transformedPointRight); - _top = - math.min(math.min(_top, transformedPointTop), transformedPointBottom); - _bottom = math.max( - math.max(_bottom, transformedPointTop), transformedPointBottom); + _left = math.min(math.min(_left, transformedPointLeft), transformedPointRight); + _right = math.max(math.max(_right, transformedPointLeft), transformedPointRight); + _top = math.min(math.min(_top, transformedPointTop), transformedPointBottom); + _bottom = math.max(math.max(_bottom, transformedPointTop), transformedPointBottom); } else { _left = math.min(transformedPointLeft, transformedPointRight); _right = math.max(transformedPointLeft, transformedPointRight); @@ -1969,14 +1997,10 @@ class _PaintBounds { } if (_didPaintInsideClipArea) { - _left = math.min( - math.min(_left, transformedPointLeft), transformedPointRight); - _right = math.max( - math.max(_right, transformedPointLeft), transformedPointRight); - _top = - math.min(math.min(_top, transformedPointTop), transformedPointBottom); - _bottom = math.max( - math.max(_bottom, transformedPointTop), transformedPointBottom); + _left = math.min(math.min(_left, transformedPointLeft), transformedPointRight); + _right = math.max(math.max(_right, transformedPointLeft), transformedPointRight); + _top = math.min(math.min(_top, transformedPointTop), transformedPointBottom); + _bottom = math.max(math.max(_bottom, transformedPointTop), transformedPointBottom); } else { _left = math.min(transformedPointLeft, transformedPointRight); _right = math.max(transformedPointLeft, transformedPointRight); @@ -1988,10 +2012,16 @@ class _PaintBounds { void saveTransformsAndClip() { _transforms.add(_currentMatrix.clone()); - _clipStack.add(_clipRectInitialized - ? ui.Rect.fromLTRB(_currentClipLeft, _currentClipTop, _currentClipRight, - _currentClipBottom) - : null); + _clipStack.add( + _clipRectInitialized + ? ui.Rect.fromLTRB( + _currentClipLeft, + _currentClipTop, + _currentClipRight, + _currentClipBottom, + ) + : null, + ); } void restoreTransformsAndClip() { @@ -2015,15 +2045,11 @@ class _PaintBounds { // The framework may send us NaNs in the case when it attempts to invert an // infinitely size rect. - final double maxLeft = maxPaintBounds.left.isNaN - ? double.negativeInfinity - : maxPaintBounds.left; - final double maxRight = - maxPaintBounds.right.isNaN ? double.infinity : maxPaintBounds.right; - final double maxTop = - maxPaintBounds.top.isNaN ? double.negativeInfinity : maxPaintBounds.top; - final double maxBottom = - maxPaintBounds.bottom.isNaN ? double.infinity : maxPaintBounds.bottom; + final double maxLeft = + maxPaintBounds.left.isNaN ? double.negativeInfinity : maxPaintBounds.left; + final double maxRight = maxPaintBounds.right.isNaN ? double.infinity : maxPaintBounds.right; + final double maxTop = maxPaintBounds.top.isNaN ? double.negativeInfinity : maxPaintBounds.top; + final double maxBottom = maxPaintBounds.bottom.isNaN ? double.infinity : maxPaintBounds.bottom; final double left = math.min(_left, _right); final double right = math.max(_left, _right); diff --git a/lib/web_ui/lib/src/engine/html/render_vertices.dart b/lib/web_ui/lib/src/engine/html/render_vertices.dart index ca5d2acacf450..c417f4682e19e 100644 --- a/lib/web_ui/lib/src/engine/html/render_vertices.dart +++ b/lib/web_ui/lib/src/engine/html/render_vertices.dart @@ -26,18 +26,13 @@ class SurfaceVertices implements ui.Vertices { List positions, { List? colors, List? indices, - }) : colors = colors != null ? _int32ListFromColors(colors) : null, - indices = indices != null ? Uint16List.fromList(indices) : null, - positions = offsetListToFloat32List(positions) { + }) : colors = colors != null ? _int32ListFromColors(colors) : null, + indices = indices != null ? Uint16List.fromList(indices) : null, + positions = offsetListToFloat32List(positions) { initWebGl(); } - SurfaceVertices.raw( - this.mode, - this.positions, { - this.colors, - this.indices, - }) { + SurfaceVertices.raw(this.mode, this.positions, {this.colors, this.indices}) { initWebGl(); } @@ -88,24 +83,32 @@ void initWebGl() { abstract class GlRenderer { void drawVertices( - DomCanvasRenderingContext2D? context, - int canvasWidthInPixels, - int canvasHeightInPixels, - Matrix4 transform, - SurfaceVertices vertices, - ui.BlendMode blendMode, - SurfacePaintData paint); - - Object? drawRect(ui.Rect targetRect, GlContext gl, GlProgram glProgram, - NormalizedGradient gradient, int widthInPixels, int heightInPixels); + DomCanvasRenderingContext2D? context, + int canvasWidthInPixels, + int canvasHeightInPixels, + Matrix4 transform, + SurfaceVertices vertices, + ui.BlendMode blendMode, + SurfacePaintData paint, + ); + + Object? drawRect( + ui.Rect targetRect, + GlContext gl, + GlProgram glProgram, + NormalizedGradient gradient, + int widthInPixels, + int heightInPixels, + ); String drawRectToImageUrl( - ui.Rect targetRect, - GlContext gl, - GlProgram glProgram, - NormalizedGradient gradient, - int widthInPixels, - int heightInPixels); + ui.Rect targetRect, + GlContext gl, + GlProgram glProgram, + NormalizedGradient gradient, + int widthInPixels, + int heightInPixels, + ); void drawHairline(DomCanvasRenderingContext2D? ctx, Float32List positions); } @@ -117,13 +120,14 @@ abstract class GlRenderer { class _WebGlRenderer implements GlRenderer { @override void drawVertices( - DomCanvasRenderingContext2D? context, - int canvasWidthInPixels, - int canvasHeightInPixels, - Matrix4 transform, - SurfaceVertices vertices, - ui.BlendMode blendMode, - SurfacePaintData paint) { + DomCanvasRenderingContext2D? context, + int canvasWidthInPixels, + int canvasHeightInPixels, + Matrix4 transform, + SurfaceVertices vertices, + ui.BlendMode blendMode, + SurfacePaintData paint, + ) { // Compute bounds of vertices. final Float32List positions = vertices.positions; final ui.Rect bounds = _computeVerticesBounds(positions, transform); @@ -144,8 +148,7 @@ class _WebGlRenderer implements GlRenderer { } // If Vertices are is smaller than hosting canvas, allocate minimal // offscreen canvas to reduce readPixels data size. - if ((maxValueX - minValueX) < widthInPixels && - (maxValueY - minValueY) < heightInPixels) { + if ((maxValueX - minValueX) < widthInPixels && (maxValueY - minValueY) < heightInPixels) { widthInPixels = maxValueX.ceil() - minValueX.floor(); heightInPixels = maxValueY.ceil() - minValueY.floor(); offsetX = minValueX.floor().toDouble(); @@ -160,36 +163,47 @@ class _WebGlRenderer implements GlRenderer { final EngineImageShader? imageShader = paint.shader == null ? null : paint.shader! as EngineImageShader; - final String vertexShader = imageShader == null - ? VertexShaders.writeBaseVertexShader() - : VertexShaders.writeTextureVertexShader(); - final String fragmentShader = imageShader == null - ? _writeVerticesFragmentShader() - : FragmentShaders.writeTextureFragmentShader( - isWebGl2, imageShader.tileModeX, imageShader.tileModeY); - - final GlContext gl = - GlContextCache.createGlContext(widthInPixels, heightInPixels)!; + final String vertexShader = + imageShader == null + ? VertexShaders.writeBaseVertexShader() + : VertexShaders.writeTextureVertexShader(); + final String fragmentShader = + imageShader == null + ? _writeVerticesFragmentShader() + : FragmentShaders.writeTextureFragmentShader( + isWebGl2, + imageShader.tileModeX, + imageShader.tileModeY, + ); + + final GlContext gl = GlContextCache.createGlContext(widthInPixels, heightInPixels)!; final GlProgram glProgram = gl.cacheProgram(vertexShader, fragmentShader); gl.useProgram(glProgram); - final Object positionAttributeLocation = - gl.getAttributeLocation(glProgram.program, 'position'); + final Object positionAttributeLocation = gl.getAttributeLocation(glProgram.program, 'position'); - setupVertexTransforms(gl, glProgram, offsetX, offsetY, - widthInPixels.toDouble(), heightInPixels.toDouble(), transform); + setupVertexTransforms( + gl, + glProgram, + offsetX, + offsetY, + widthInPixels.toDouble(), + heightInPixels.toDouble(), + transform, + ); if (imageShader != null) { /// To map from vertex position to texture coordinate in 0..1 range, /// we setup scalar to be used in vertex shader. setupTextureTransform( - gl, - glProgram, - 0.0, - 0.0, - 1.0 / imageShader.image.width.toDouble(), - 1.0 / imageShader.image.height.toDouble()); + gl, + glProgram, + 0.0, + 0.0, + 1.0 / imageShader.image.width.toDouble(), + 1.0 / imageShader.image.height.toDouble(), + ); } // Setup geometry. @@ -242,15 +256,7 @@ class _WebGlRenderer implements GlRenderer { gl.bufferData(vertices.colors, gl.kStaticDraw); } final Object colorLoc = gl.getAttributeLocation(glProgram.program, 'color'); - vertexAttribPointerGlContext( - gl.glContext, - colorLoc, - 4, - gl.kUnsignedByte, - true, - 0, - 0, - ); + vertexAttribPointerGlContext(gl.glContext, colorLoc, 4, gl.kUnsignedByte, true, 0, 0); gl.enableVertexAttribArray(colorLoc); } else { // Copy image it to the texture. @@ -264,17 +270,29 @@ class _WebGlRenderer implements GlRenderer { gl.activeTexture(gl.kTexture0); gl.bindTexture(gl.kTexture2D, texture); - gl.texImage2D(gl.kTexture2D, 0, gl.kRGBA, gl.kRGBA, gl.kUnsignedByte, - imageShader.image.imgElement); + gl.texImage2D( + gl.kTexture2D, + 0, + gl.kRGBA, + gl.kRGBA, + gl.kUnsignedByte, + imageShader.image.imgElement, + ); if (isWebGl2) { // Texture REPEAT and MIRROR is only supported in WebGL 2, for // WebGL 1.0 we let shader compute correct uv coordinates. - gl.texParameteri(gl.kTexture2D, gl.kTextureWrapS, - tileModeToGlWrapping(gl, imageShader.tileModeX)); - - gl.texParameteri(gl.kTexture2D, gl.kTextureWrapT, - tileModeToGlWrapping(gl, imageShader.tileModeY)); + gl.texParameteri( + gl.kTexture2D, + gl.kTextureWrapS, + tileModeToGlWrapping(gl, imageShader.tileModeX), + ); + + gl.texParameteri( + gl.kTexture2D, + gl.kTextureWrapT, + tileModeToGlWrapping(gl, imageShader.tileModeY), + ); // Mipmapping saves your texture in different resolutions // so the graphics card can choose which resolution is optimal @@ -322,10 +340,15 @@ class _WebGlRenderer implements GlRenderer { /// Browsers that support OffscreenCanvas and the transferToImageBitmap api /// will return ImageBitmap, otherwise will return CanvasElement. @override - Object? drawRect(ui.Rect targetRect, GlContext gl, GlProgram glProgram, - NormalizedGradient gradient, int widthInPixels, int heightInPixels) { - drawRectToGl( - targetRect, gl, glProgram, gradient, widthInPixels, heightInPixels); + Object? drawRect( + ui.Rect targetRect, + GlContext gl, + GlProgram glProgram, + NormalizedGradient gradient, + int widthInPixels, + int heightInPixels, + ) { + drawRectToGl(targetRect, gl, glProgram, gradient, widthInPixels, heightInPixels); final Object? image = gl.readPatternData(gradient.isOpaque); gl.bindArrayBuffer(null); gl.bindElementArrayBuffer(null); @@ -336,14 +359,14 @@ class _WebGlRenderer implements GlRenderer { /// url. @override String drawRectToImageUrl( - ui.Rect targetRect, - GlContext gl, - GlProgram glProgram, - NormalizedGradient gradient, - int widthInPixels, - int heightInPixels) { - drawRectToGl( - targetRect, gl, glProgram, gradient, widthInPixels, heightInPixels); + ui.Rect targetRect, + GlContext gl, + GlProgram glProgram, + NormalizedGradient gradient, + int widthInPixels, + int heightInPixels, + ) { + drawRectToGl(targetRect, gl, glProgram, gradient, widthInPixels, heightInPixels); final String imageUrl = gl.toImageUrl(); // Cleanup buffers. gl.bindArrayBuffer(null); @@ -354,8 +377,14 @@ class _WebGlRenderer implements GlRenderer { /// Renders a rectangle using given program into [GlContext]. /// /// Caller has to cleanup gl array and element array buffers. - void drawRectToGl(ui.Rect targetRect, GlContext gl, GlProgram glProgram, - NormalizedGradient gradient, int widthInPixels, int heightInPixels) { + void drawRectToGl( + ui.Rect targetRect, + GlContext gl, + GlProgram glProgram, + NormalizedGradient gradient, + int widthInPixels, + int heightInPixels, + ) { // Setup rectangle coordinates. final double left = targetRect.left; final double top = targetRect.top; @@ -372,15 +401,19 @@ class _WebGlRenderer implements GlRenderer { vertices[6] = left; vertices[7] = bottom; - final Object transformUniform = - gl.getUniformLocation(glProgram.program, 'u_ctransform'); + final Object transformUniform = gl.getUniformLocation(glProgram.program, 'u_ctransform'); gl.setUniformMatrix4fv(transformUniform, false, Matrix4.identity().storage); // Set uniform to scale 0..width/height pixels coordinates to -1..1 // clipspace range and flip the Y axis. final Object resolution = gl.getUniformLocation(glProgram.program, 'u_scale'); - gl.setUniform4f(resolution, 2.0 / widthInPixels.toDouble(), - -2.0 / heightInPixels.toDouble(), 1, 1); + gl.setUniform4f( + resolution, + 2.0 / widthInPixels.toDouble(), + -2.0 / heightInPixels.toDouble(), + 1, + 1, + ); final Object shift = gl.getUniformLocation(glProgram.program, 'u_shift'); gl.setUniform4f(shift, -1, 1, 0, 0); @@ -389,15 +422,7 @@ class _WebGlRenderer implements GlRenderer { gl.bindArrayBuffer(positionsBuffer); gl.bufferData(vertices, gl.kStaticDraw); // Point an attribute to the currently bound vertex buffer object. - vertexAttribPointerGlContext( - gl.glContext, - 0, - 2, - gl.kFloat, - false, - 0, - 0, - ); + vertexAttribPointerGlContext(gl.glContext, 0, 2, gl.kFloat, false, 0, 0); gl.enableVertexAttribArray(0); // Setup color buffer. @@ -411,15 +436,7 @@ class _WebGlRenderer implements GlRenderer { 0xFF00FFFF, ]); gl.bufferData(colors, gl.kStaticDraw); - vertexAttribPointerGlContext( - gl.glContext, - 1, - 4, - gl.kUnsignedByte, - true, - 0, - 0, - ); + vertexAttribPointerGlContext(gl.glContext, 1, 4, gl.kUnsignedByte, true, 0, 0); gl.enableVertexAttribArray(1); final Object? indexBuffer = gl.createBuffer(); @@ -428,15 +445,13 @@ class _WebGlRenderer implements GlRenderer { if (gl.containsUniform(glProgram.program, 'u_resolution')) { final Object uRes = gl.getUniformLocation(glProgram.program, 'u_resolution'); - gl.setUniform2f( - uRes, widthInPixels.toDouble(), heightInPixels.toDouble()); + gl.setUniform2f(uRes, widthInPixels.toDouble(), heightInPixels.toDouble()); } gl.clear(); gl.viewport(0, 0, widthInPixels.toDouble(), heightInPixels.toDouble()); - gl.drawElements( - gl.kTriangles, VertexShaders.vertexIndicesForRect.length, gl.kUnsignedShort); + gl.drawElements(gl.kTriangles, VertexShaders.vertexIndicesForRect.length, gl.kUnsignedShort); } /// This fragment shader enables Int32List of colors to be passed directly @@ -458,16 +473,13 @@ class _WebGlRenderer implements GlRenderer { } @override - void drawHairline( - DomCanvasRenderingContext2D? ctx, Float32List positions) { + void drawHairline(DomCanvasRenderingContext2D? ctx, Float32List positions) { final int pointCount = positions.length ~/ 2; ctx!.lineWidth = 1.0; ctx.beginPath(); final int len = pointCount * 2; for (int i = 0; i < len;) { - for (int triangleVertexIndex = 0; - triangleVertexIndex < 3; - triangleVertexIndex++, i += 2) { + for (int triangleVertexIndex = 0; triangleVertexIndex < 3; triangleVertexIndex++, i += 2) { final double dx = positions[i]; final double dy = positions[i + 1]; switch (triangleVertexIndex) { @@ -503,12 +515,10 @@ ui.Rect _computeVerticesBounds(Float32List positions, Matrix4 transform) { minValueY = math.min(minValueY, y); maxValueY = math.max(maxValueY, y); } - return _transformBounds( - transform, minValueX, minValueY, maxValueX, maxValueY); + return _transformBounds(transform, minValueX, minValueY, maxValueX, maxValueY); } -ui.Rect _transformBounds( - Matrix4 transform, double left, double top, double right, double bottom) { +ui.Rect _transformBounds(Matrix4 transform, double left, double top, double right, double bottom) { final Float32List storage = transform.storage; final double m0 = storage[0]; final double m1 = storage[1]; @@ -525,10 +535,11 @@ ui.Rect _transformBounds( final double x3 = (m0 * left) + (m4 * bottom) + m12; final double y3 = (m1 * left) + (m5 * bottom) + m13; return ui.Rect.fromLTRB( - math.min(x0, math.min(x1, math.min(x2, x3))), - math.min(y0, math.min(y1, math.min(y2, y3))), - math.max(x0, math.max(x1, math.max(x2, x3))), - math.max(y0, math.max(y1, math.max(y2, y3)))); + math.min(x0, math.min(x1, math.min(x2, x3))), + math.min(y0, math.min(y1, math.min(y2, y3))), + math.max(x0, math.max(x1, math.max(x2, x3))), + math.max(y0, math.max(y1, math.max(y2, y3))), + ); } /// Converts from [VertexMode] triangleFan and triangleStrip to triangles. @@ -542,9 +553,11 @@ Float32List convertVertexPositions(ui.VertexMode mode, Float32List positions) { final double centerY = positions[1]; int destIndex = 0; int positionIndex = 2; - for (int triangleIndex = 0; - triangleIndex < triangleCount; - triangleIndex++, positionIndex += 2) { + for ( + int triangleIndex = 0; + triangleIndex < triangleCount; + triangleIndex++, positionIndex += 2 + ) { triangleList[destIndex++] = centerX; triangleList[destIndex++] = centerY; triangleList[destIndex++] = positions[positionIndex]; diff --git a/lib/web_ui/lib/src/engine/html/renderer.dart b/lib/web_ui/lib/src/engine/html/renderer.dart index fa9aee9f78a8b..8a77dee38f725 100644 --- a/lib/web_ui/lib/src/engine/html/renderer.dart +++ b/lib/web_ui/lib/src/engine/html/renderer.dart @@ -45,8 +45,7 @@ class HtmlRenderer implements Renderer { List? textureCoordinates, List? colors, List? indices, - }) => - SurfaceVertices(mode, positions, colors: colors, indices: indices); + }) => SurfaceVertices(mode, positions, colors: colors, indices: indices); @override ui.Vertices createVerticesRaw( @@ -55,8 +54,7 @@ class HtmlRenderer implements Renderer { Float32List? textureCoordinates, Int32List? colors, Uint16List? indices, - }) => - SurfaceVertices.raw(mode, positions, colors: colors, indices: indices); + }) => SurfaceVertices.raw(mode, positions, colors: colors, indices: indices); @override ui.Canvas createCanvas(ui.PictureRecorder recorder, [ui.Rect? cullRect]) => @@ -64,11 +62,13 @@ class HtmlRenderer implements Renderer { @override ui.Gradient createLinearGradient( - ui.Offset from, ui.Offset to, List colors, - [List? colorStops, - ui.TileMode tileMode = ui.TileMode.clamp, - Float32List? matrix4]) => - GradientLinear(from, to, colors, colorStops, tileMode, matrix4); + ui.Offset from, + ui.Offset to, + List colors, [ + List? colorStops, + ui.TileMode tileMode = ui.TileMode.clamp, + Float32List? matrix4, + ]) => GradientLinear(from, to, colors, colorStops, tileMode, matrix4); @override ui.Gradient createRadialGradient( @@ -78,27 +78,30 @@ class HtmlRenderer implements Renderer { List? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, Float32List? matrix4, - ]) => - GradientRadial(center, radius, colors, colorStops, tileMode, matrix4); + ]) => GradientRadial(center, radius, colors, colorStops, tileMode, matrix4); @override - ui.Gradient createConicalGradient(ui.Offset focal, double focalRadius, - ui.Offset center, double radius, List colors, - [List? colorStops, - ui.TileMode tileMode = ui.TileMode.clamp, - Float32List? matrix]) => - GradientConical(focal, focalRadius, center, radius, colors, colorStops, - tileMode, matrix); + ui.Gradient createConicalGradient( + ui.Offset focal, + double focalRadius, + ui.Offset center, + double radius, + List colors, [ + List? colorStops, + ui.TileMode tileMode = ui.TileMode.clamp, + Float32List? matrix, + ]) => GradientConical(focal, focalRadius, center, radius, colors, colorStops, tileMode, matrix); @override - ui.Gradient createSweepGradient(ui.Offset center, List colors, - [List? colorStops, - ui.TileMode tileMode = ui.TileMode.clamp, - double startAngle = 0.0, - double endAngle = math.pi * 2, - Float32List? matrix4]) => - GradientSweep( - center, colors, colorStops, tileMode, startAngle, endAngle, matrix4); + ui.Gradient createSweepGradient( + ui.Offset center, + List colors, [ + List? colorStops, + ui.TileMode tileMode = ui.TileMode.clamp, + double startAngle = 0.0, + double endAngle = math.pi * 2, + Float32List? matrix4, + ]) => GradientSweep(center, colors, colorStops, tileMode, startAngle, endAngle, matrix4); @override ui.PictureRecorder createPictureRecorder() => EnginePictureRecorder(); @@ -108,46 +111,47 @@ class HtmlRenderer implements Renderer { // TODO(ferhat): implement TileMode. @override - ui.ImageFilter createBlurImageFilter( - {double sigmaX = 0.0, - double sigmaY = 0.0, - ui.TileMode? tileMode}) => - EngineImageFilter.blur( - sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode); + ui.ImageFilter createBlurImageFilter({ + double sigmaX = 0.0, + double sigmaY = 0.0, + ui.TileMode? tileMode, + }) => EngineImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode); @override - ui.ImageFilter createDilateImageFilter( - {double radiusX = 0.0, double radiusY = 0.0}) { + ui.ImageFilter createDilateImageFilter({double radiusX = 0.0, double radiusY = 0.0}) { // TODO(fzyzcjy): implement dilate. https://github.com/flutter/flutter/issues/101085 - throw UnimplementedError( - 'ImageFilter.dilate not implemented for HTML renderer.'); + throw UnimplementedError('ImageFilter.dilate not implemented for HTML renderer.'); } @override - ui.ImageFilter createErodeImageFilter( - {double radiusX = 0.0, double radiusY = 0.0}) { + ui.ImageFilter createErodeImageFilter({double radiusX = 0.0, double radiusY = 0.0}) { // TODO(fzyzcjy): implement erode. https://github.com/flutter/flutter/issues/101085 - throw UnimplementedError( - 'ImageFilter.erode not implemented for HTML renderer.'); + throw UnimplementedError('ImageFilter.erode not implemented for HTML renderer.'); } @override - ui.ImageFilter createMatrixImageFilter(Float64List matrix4, - {ui.FilterQuality filterQuality = ui.FilterQuality.low}) => - EngineImageFilter.matrix(matrix: matrix4, filterQuality: filterQuality); + ui.ImageFilter createMatrixImageFilter( + Float64List matrix4, { + ui.FilterQuality filterQuality = ui.FilterQuality.low, + }) => EngineImageFilter.matrix(matrix: matrix4, filterQuality: filterQuality); @override - ui.ImageFilter composeImageFilters( - {required ui.ImageFilter outer, required ui.ImageFilter inner}) { + ui.ImageFilter composeImageFilters({ + required ui.ImageFilter outer, + required ui.ImageFilter inner, + }) { // TODO(ferhat): add implementation and remove the "ignore". // ignore: avoid_unused_constructor_parameters - throw UnimplementedError( - 'ImageFilter.erode not implemented for HTML renderer.'); + throw UnimplementedError('ImageFilter.erode not implemented for HTML renderer.'); } @override - Future instantiateImageCodec(Uint8List list, - {int? targetWidth, int? targetHeight, bool allowUpscaling = true}) async { + Future instantiateImageCodec( + Uint8List list, { + int? targetWidth, + int? targetHeight, + bool allowUpscaling = true, + }) async { final DomBlob blob = createDomBlob([list.buffer]); return HtmlRendererBlobCodec(blob); } @@ -161,31 +165,34 @@ class HtmlRenderer implements Renderer { } @override - void decodeImageFromPixels(Uint8List pixels, int width, int height, - ui.PixelFormat format, ui.ImageDecoderCallback callback, - {int? rowBytes, - int? targetWidth, - int? targetHeight, - bool allowUpscaling = true}) { + void decodeImageFromPixels( + Uint8List pixels, + int width, + int height, + ui.PixelFormat format, + ui.ImageDecoderCallback callback, { + int? rowBytes, + int? targetWidth, + int? targetHeight, + bool allowUpscaling = true, + }) { void executeCallback(ui.Codec codec) { codec.getNextFrame().then((ui.FrameInfo frameInfo) { callback(frameInfo.image); }); } - ui - .createBmp(pixels, width, height, rowBytes ?? width, format) - .then(executeCallback); + ui.createBmp(pixels, width, height, rowBytes ?? width, format).then(executeCallback); } @override ui.ImageShader createImageShader( - ui.Image image, - ui.TileMode tmx, - ui.TileMode tmy, - Float64List matrix4, - ui.FilterQuality? filterQuality) => - EngineImageShader(image, tmx, tmy, matrix4, filterQuality); + ui.Image image, + ui.TileMode tmx, + ui.TileMode tmy, + Float64List matrix4, + ui.FilterQuality? filterQuality, + ) => EngineImageShader(image, tmx, tmy, matrix4, filterQuality); @override ui.Path createPath() => SurfacePath(); @@ -199,103 +206,103 @@ class HtmlRenderer implements Renderer { } @override - ui.TextStyle createTextStyle( - {ui.Color? color, - ui.TextDecoration? decoration, - ui.Color? decorationColor, - ui.TextDecorationStyle? decorationStyle, - double? decorationThickness, - ui.FontWeight? fontWeight, - ui.FontStyle? fontStyle, - ui.TextBaseline? textBaseline, - String? fontFamily, - List? fontFamilyFallback, - double? fontSize, - double? letterSpacing, - double? wordSpacing, - double? height, - ui.TextLeadingDistribution? leadingDistribution, - ui.Locale? locale, - ui.Paint? background, - ui.Paint? foreground, - List? shadows, - List? fontFeatures, - List? fontVariations}) => - EngineTextStyle( - color: color, - decoration: decoration, - decorationColor: decorationColor, - decorationStyle: decorationStyle, - decorationThickness: decorationThickness, - fontWeight: fontWeight, - fontStyle: fontStyle, - textBaseline: textBaseline, - fontFamily: fontFamily, - fontFamilyFallback: fontFamilyFallback, - fontSize: fontSize, - letterSpacing: letterSpacing, - wordSpacing: wordSpacing, - height: height, - leadingDistribution: leadingDistribution, - locale: locale, - background: background, - foreground: foreground, - shadows: shadows, - fontFeatures: fontFeatures, - fontVariations: fontVariations, - ); - - @override - ui.ParagraphStyle createParagraphStyle( - {ui.TextAlign? textAlign, - ui.TextDirection? textDirection, - int? maxLines, - String? fontFamily, - double? fontSize, - double? height, - ui.TextHeightBehavior? textHeightBehavior, - ui.FontWeight? fontWeight, - ui.FontStyle? fontStyle, - ui.StrutStyle? strutStyle, - String? ellipsis, - ui.Locale? locale}) => - EngineParagraphStyle( - textAlign: textAlign, - textDirection: textDirection, - maxLines: maxLines, - fontFamily: fontFamily, - fontSize: fontSize, - height: height, - textHeightBehavior: textHeightBehavior, - fontWeight: fontWeight, - fontStyle: fontStyle, - strutStyle: strutStyle, - ellipsis: ellipsis, - locale: locale, - ); - - @override - ui.StrutStyle createStrutStyle( - {String? fontFamily, - List? fontFamilyFallback, - double? fontSize, - double? height, - ui.TextLeadingDistribution? leadingDistribution, - double? leading, - ui.FontWeight? fontWeight, - ui.FontStyle? fontStyle, - bool? forceStrutHeight}) => - EngineStrutStyle( - fontFamily: fontFamily, - fontFamilyFallback: fontFamilyFallback, - fontSize: fontSize, - height: height, - leadingDistribution: leadingDistribution, - leading: leading, - fontWeight: fontWeight, - fontStyle: fontStyle, - forceStrutHeight: forceStrutHeight, - ); + ui.TextStyle createTextStyle({ + ui.Color? color, + ui.TextDecoration? decoration, + ui.Color? decorationColor, + ui.TextDecorationStyle? decorationStyle, + double? decorationThickness, + ui.FontWeight? fontWeight, + ui.FontStyle? fontStyle, + ui.TextBaseline? textBaseline, + String? fontFamily, + List? fontFamilyFallback, + double? fontSize, + double? letterSpacing, + double? wordSpacing, + double? height, + ui.TextLeadingDistribution? leadingDistribution, + ui.Locale? locale, + ui.Paint? background, + ui.Paint? foreground, + List? shadows, + List? fontFeatures, + List? fontVariations, + }) => EngineTextStyle( + color: color, + decoration: decoration, + decorationColor: decorationColor, + decorationStyle: decorationStyle, + decorationThickness: decorationThickness, + fontWeight: fontWeight, + fontStyle: fontStyle, + textBaseline: textBaseline, + fontFamily: fontFamily, + fontFamilyFallback: fontFamilyFallback, + fontSize: fontSize, + letterSpacing: letterSpacing, + wordSpacing: wordSpacing, + height: height, + leadingDistribution: leadingDistribution, + locale: locale, + background: background, + foreground: foreground, + shadows: shadows, + fontFeatures: fontFeatures, + fontVariations: fontVariations, + ); + + @override + ui.ParagraphStyle createParagraphStyle({ + ui.TextAlign? textAlign, + ui.TextDirection? textDirection, + int? maxLines, + String? fontFamily, + double? fontSize, + double? height, + ui.TextHeightBehavior? textHeightBehavior, + ui.FontWeight? fontWeight, + ui.FontStyle? fontStyle, + ui.StrutStyle? strutStyle, + String? ellipsis, + ui.Locale? locale, + }) => EngineParagraphStyle( + textAlign: textAlign, + textDirection: textDirection, + maxLines: maxLines, + fontFamily: fontFamily, + fontSize: fontSize, + height: height, + textHeightBehavior: textHeightBehavior, + fontWeight: fontWeight, + fontStyle: fontStyle, + strutStyle: strutStyle, + ellipsis: ellipsis, + locale: locale, + ); + + @override + ui.StrutStyle createStrutStyle({ + String? fontFamily, + List? fontFamilyFallback, + double? fontSize, + double? height, + ui.TextLeadingDistribution? leadingDistribution, + double? leading, + ui.FontWeight? fontWeight, + ui.FontStyle? fontStyle, + bool? forceStrutHeight, + }) => EngineStrutStyle( + fontFamily: fontFamily, + fontFamilyFallback: fontFamilyFallback, + fontSize: fontSize, + height: height, + leadingDistribution: leadingDistribution, + leading: leading, + fontWeight: fontWeight, + fontStyle: fontStyle, + forceStrutHeight: forceStrutHeight, + ); @override ui.ParagraphBuilder createParagraphBuilder(ui.ParagraphStyle style) => @@ -303,8 +310,7 @@ class HtmlRenderer implements Renderer { @override Future renderScene(ui.Scene scene, EngineFlutterView view) async { - final EngineFlutterView implicitView = - EnginePlatformDispatcher.instance.implicitView!; + final EngineFlutterView implicitView = EnginePlatformDispatcher.instance.implicitView!; scene as SurfaceScene; implicitView.dom.setScene(scene.webOnlyRootElement!); final FrameTimingRecorder? recorder = scene.timingRecorder; @@ -321,35 +327,34 @@ class HtmlRenderer implements Renderer { } @override - ui.LineMetrics createLineMetrics( - {required bool hardBreak, - required double ascent, - required double descent, - required double unscaledAscent, - required double height, - required double width, - required double left, - required double baseline, - required int lineNumber}) => - EngineLineMetrics( - hardBreak: hardBreak, - ascent: ascent, - descent: descent, - unscaledAscent: unscaledAscent, - height: height, - width: width, - left: left, - baseline: baseline, - lineNumber: lineNumber); - - @override - Future createImageFromImageBitmap( - DomImageBitmap imageSource) async { + ui.LineMetrics createLineMetrics({ + required bool hardBreak, + required double ascent, + required double descent, + required double unscaledAscent, + required double height, + required double width, + required double left, + required double baseline, + required int lineNumber, + }) => EngineLineMetrics( + hardBreak: hardBreak, + ascent: ascent, + descent: descent, + unscaledAscent: unscaledAscent, + height: height, + width: width, + left: left, + baseline: baseline, + lineNumber: lineNumber, + ); + + @override + Future createImageFromImageBitmap(DomImageBitmap imageSource) async { final int width = imageSource.width.toDartInt; final int height = imageSource.height.toDartInt; final OffScreenCanvas canvas = OffScreenCanvas(width, height); - final DomCanvasRenderingContextBitmapRenderer context = - canvas.getBitmapRendererContext()!; + final DomCanvasRenderingContextBitmapRenderer context = canvas.getBitmapRendererContext()!; context.transferFromImageBitmap(imageSource); final DomHTMLImageElement imageElement = createDomHTMLImageElement(); late final DomEventListener loadListener; @@ -361,8 +366,7 @@ class HtmlRenderer implements Renderer { imageElement.removeEventListener('error', errorListener); }); errorListener = createDomEventListener((DomEvent event) { - completer.completeError( - Exception('Failed to create image from image bitmap.')); + completer.completeError(Exception('Failed to create image from image bitmap.')); imageElement.removeEventListener('load', loadListener); imageElement.removeEventListener('error', errorListener); }); @@ -373,7 +377,12 @@ class HtmlRenderer implements Renderer { } @override - FutureOr createImageFromTextureSource(JSAny object, { required int width, required int height, required bool transferOwnership }) { + FutureOr createImageFromTextureSource( + JSAny object, { + required int width, + required int height, + required bool transferOwnership, + }) { throw Exception('Not implemented for HTML renderer'); } } diff --git a/lib/web_ui/lib/src/engine/html/scene.dart b/lib/web_ui/lib/src/engine/html/scene.dart index b4deb9a6dac69..2cec91a158d4e 100644 --- a/lib/web_ui/lib/src/engine/html/scene.dart +++ b/lib/web_ui/lib/src/engine/html/scene.dart @@ -10,9 +10,7 @@ class SurfaceScene implements ui.Scene { /// or extended directly. /// /// To create a Scene object, use a [SceneBuilder]. - SurfaceScene(this.webOnlyRootElement, { - required this.timingRecorder, - }); + SurfaceScene(this.webOnlyRootElement, {required this.timingRecorder}); final DomElement? webOnlyRootElement; final FrameTimingRecorder? timingRecorder; @@ -64,8 +62,7 @@ class PersistedScene extends PersistedContainerSurface { Matrix4? _localTransformInverse; @override - Matrix4? get localTransformInverse => - _localTransformInverse ??= Matrix4.identity(); + Matrix4? get localTransformInverse => _localTransformInverse ??= Matrix4.identity(); @override DomElement createElement() { diff --git a/lib/web_ui/lib/src/engine/html/scene_builder.dart b/lib/web_ui/lib/src/engine/html/scene_builder.dart index 88644adc0529a..b2c24c8293c49 100644 --- a/lib/web_ui/lib/src/engine/html/scene_builder.dart +++ b/lib/web_ui/lib/src/engine/html/scene_builder.dart @@ -32,8 +32,7 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { _surfaceStack.add(PersistedScene(_lastFrameScene)); } - final List _surfaceStack = - []; + final List _surfaceStack = []; /// The scene built by this scene builder. /// @@ -50,8 +49,7 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { // the live tree. if (surface.oldLayer != null) { assert(surface.oldLayer!.runtimeType == surface.runtimeType); - assert(debugAssertSurfaceState( - surface.oldLayer!, PersistedSurfaceState.active)); + assert(debugAssertSurfaceState(surface.oldLayer!, PersistedSurfaceState.active)); surface.oldLayer!.state = PersistedSurfaceState.pendingUpdate; } _adoptSurface(surface); @@ -83,13 +81,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { /// /// See [pop] for details about the operation stack. @override - ui.OffsetEngineLayer pushOffset( - double dx, - double dy, { - ui.OffsetEngineLayer? oldLayer, - }) { - return _pushSurface( - PersistedOffset(oldLayer as PersistedOffset?, dx, dy)); + ui.OffsetEngineLayer pushOffset(double dx, double dy, {ui.OffsetEngineLayer? oldLayer}) { + return _pushSurface(PersistedOffset(oldLayer as PersistedOffset?, dx, dy)); } /// Pushes a transform operation onto the operation stack. @@ -98,10 +91,7 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { /// /// See [pop] for details about the operation stack. @override - ui.TransformEngineLayer pushTransform( - Float64List matrix4, { - ui.TransformEngineLayer? oldLayer, - }) { + ui.TransformEngineLayer pushTransform(Float64List matrix4, {ui.TransformEngineLayer? oldLayer}) { if (matrix4.length != 16) { throw ArgumentError('"matrix4" must have 16 entries.'); } @@ -112,15 +102,18 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { // scene to devicepixelratio. Use identity instead since CSS uses // logical device pixels. if (!ui_web.debugEmulateFlutterTesterEnvironment) { - assert(matrix4[0] == EngineFlutterDisplay.instance.devicePixelRatio && - matrix4[5] == EngineFlutterDisplay.instance.devicePixelRatio); + assert( + matrix4[0] == EngineFlutterDisplay.instance.devicePixelRatio && + matrix4[5] == EngineFlutterDisplay.instance.devicePixelRatio, + ); } matrix = Matrix4.identity().storage; } else { matrix = toMatrix32(matrix4); } return _pushSurface( - PersistedTransform(oldLayer as PersistedTransform?, matrix)); + PersistedTransform(oldLayer as PersistedTransform?, matrix), + ); } /// Pushes a rectangular clip operation onto the operation stack. @@ -136,7 +129,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { ui.ClipRectEngineLayer? oldLayer, }) { return _pushSurface( - PersistedClipRect(oldLayer as PersistedClipRect?, rect, clipBehavior)); + PersistedClipRect(oldLayer as PersistedClipRect?, rect, clipBehavior), + ); } /// Pushes a rounded-rectangular clip operation onto the operation stack. @@ -150,8 +144,7 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { ui.Clip? clipBehavior, ui.ClipRRectEngineLayer? oldLayer, }) { - return _pushSurface( - PersistedClipRRect(oldLayer, rrect, clipBehavior)); + return _pushSurface(PersistedClipRRect(oldLayer, rrect, clipBehavior)); } /// Pushes a path clip operation onto the operation stack. @@ -166,7 +159,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { ui.ClipPathEngineLayer? oldLayer, }) { return _pushSurface( - PersistedClipPath(oldLayer as PersistedClipPath?, path, clipBehavior)); + PersistedClipPath(oldLayer as PersistedClipPath?, path, clipBehavior), + ); } /// Pushes an opacity operation onto the operation stack. @@ -184,7 +178,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { ui.OpacityEngineLayer? oldLayer, }) { return _pushSurface( - PersistedOpacity(oldLayer as PersistedOpacity?, alpha, offset)); + PersistedOpacity(oldLayer as PersistedOpacity?, alpha, offset), + ); } /// Pushes a color filter operation onto the operation stack. @@ -203,7 +198,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { ui.ColorFilterEngineLayer? oldLayer, }) { return _pushSurface( - PersistedColorFilter(oldLayer as PersistedColorFilter?, filter)); + PersistedColorFilter(oldLayer as PersistedColorFilter?, filter), + ); } /// Pushes an image filter operation onto the operation stack. @@ -223,7 +219,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { ui.ImageFilterEngineLayer? oldLayer, }) { return _pushSurface( - PersistedImageFilter(oldLayer as PersistedImageFilter?, filter, offset)); + PersistedImageFilter(oldLayer as PersistedImageFilter?, filter, offset), + ); } /// Pushes a backdrop filter operation onto the operation stack. @@ -242,8 +239,9 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { ui.BackdropFilterEngineLayer? oldLayer, int? backdropId, }) { - return _pushSurface(PersistedBackdropFilter( - oldLayer as PersistedBackdropFilter?, filter)); + return _pushSurface( + PersistedBackdropFilter(oldLayer as PersistedBackdropFilter?, filter), + ); } /// Pushes a shader mask operation onto the operation stack. @@ -260,9 +258,15 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { ui.ShaderMaskEngineLayer? oldLayer, ui.FilterQuality filterQuality = ui.FilterQuality.low, }) { - return _pushSurface(PersistedShaderMask( + return _pushSurface( + PersistedShaderMask( oldLayer as PersistedShaderMask?, - shader, maskRect, blendMode, filterQuality)); + shader, + maskRect, + blendMode, + filterQuality, + ), + ); } /// Add a retained engine layer subtree from previous frames. @@ -275,12 +279,14 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { /// no need to call [addToScene] for its children layers. @override void addRetained(ui.EngineLayer retainedLayer) { - final PersistedContainerSurface retainedSurface = - retainedLayer as PersistedContainerSurface; - assert(debugAssertSurfaceState(retainedSurface, - PersistedSurfaceState.active, - PersistedSurfaceState.released, - )); + final PersistedContainerSurface retainedSurface = retainedLayer as PersistedContainerSurface; + assert( + debugAssertSurfaceState( + retainedSurface, + PersistedSurfaceState.active, + PersistedSurfaceState.released, + ), + ); retainedSurface.tryRetain(); _adoptSurface(retainedSurface); } @@ -321,8 +327,7 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { /// for more details. @override void addPerformanceOverlay(int enabledOptions, ui.Rect bounds) { - _addPerformanceOverlay( - enabledOptions, bounds.left, bounds.right, bounds.top, bounds.bottom); + _addPerformanceOverlay(enabledOptions, bounds.left, bounds.right, bounds.top, bounds.bottom); } /// Whether we've already warned the user about the lack of the performance @@ -361,8 +366,7 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { if (willChangeHint) { hints |= 2; } - _addSurface(PersistedPicture( - offset.dx, offset.dy, picture as EnginePicture, hints)); + _addSurface(PersistedPicture(offset.dx, offset.dy, picture as EnginePicture, hints)); } /// Adds a backend texture to the scene. @@ -381,8 +385,14 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { _addTexture(offset.dx, offset.dy, width, height, textureId, filterQuality); } - void _addTexture(double dx, double dy, double width, double height, - int textureId, ui.FilterQuality filterQuality) { + void _addTexture( + double dx, + double dy, + double width, + double height, + int textureId, + ui.FilterQuality filterQuality, + ) { // In test mode, allow this to be a no-op. if (!ui_web.debugEmulateFlutterTesterEnvironment) { throw UnimplementedError('Textures are not supported in Flutter Web'); @@ -415,13 +425,7 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { _addPlatformView(offset.dx, offset.dy, width, height, viewId); } - void _addPlatformView( - double dx, - double dy, - double width, - double height, - int viewId, - ) { + void _addPlatformView(double dx, double dy, double width, double height, int viewId) { _addSurface(PersistedPlatformView(viewId, dx, dy, width, height)); } @@ -475,7 +479,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder { // In the HTML renderer we time the beginning of the rasterization phase // (counter-intuitively) in SceneBuilder.build because DOM updates happen // here. This is different from CanvasKit. - final FrameTimingRecorder? recorder = FrameTimingRecorder.frameTimingsEnabled ? FrameTimingRecorder() : null; + final FrameTimingRecorder? recorder = + FrameTimingRecorder.frameTimingsEnabled ? FrameTimingRecorder() : null; recorder?.recordBuildFinish(); recorder?.recordRasterStart(); timeAction(kProfilePrerollFrame, () { diff --git a/lib/web_ui/lib/src/engine/html/shader_mask.dart b/lib/web_ui/lib/src/engine/html/shader_mask.dart index f1fe76fa7afaa..9d383b119bcf6 100644 --- a/lib/web_ui/lib/src/engine/html/shader_mask.dart +++ b/lib/web_ui/lib/src/engine/html/shader_mask.dart @@ -23,8 +23,7 @@ import 'surface.dart'; /// into an image and referencing the image in an svg filter to apply /// to DOM tree. /// -class PersistedShaderMask extends PersistedContainerSurface - implements ui.ShaderMaskEngineLayer { +class PersistedShaderMask extends PersistedContainerSurface implements ui.ShaderMaskEngineLayer { PersistedShaderMask( PersistedShaderMask? super.oldLayer, this.shader, @@ -111,10 +110,8 @@ class PersistedShaderMask extends PersistedContainerSurface // The gradient shader's bounds are in the context of the element itself, // rather than the global position, so translate it back to the origin. - final ui.Rect translatedRect = - maskRect.translate(-maskRect.left, -maskRect.top); - final String imageUrl = - gradientShader.createImageBitmap(translatedRect, 1, true) as String; + final ui.Rect translatedRect = maskRect.translate(-maskRect.left, -maskRect.top); + final String imageUrl = gradientShader.createImageBitmap(translatedRect, 1, true) as String; ui.BlendMode blendModeTemp = blendMode; switch (blendModeTemp) { case ui.BlendMode.clear: @@ -159,7 +156,11 @@ class PersistedShaderMask extends PersistedContainerSurface } final SvgFilter svgFilter = svgMaskFilterFromImageAndBlendMode( - imageUrl, blendModeTemp, maskRect.width, maskRect.height); + imageUrl, + blendModeTemp, + maskRect.width, + maskRect.height, + ); _shaderElement = svgFilter.element; if (isWebKit) { _childContainer!.style.filter = 'url(#${svgFilter.id})'; @@ -182,7 +183,11 @@ class PersistedShaderMask extends PersistedContainerSurface } SvgFilter svgMaskFilterFromImageAndBlendMode( - String imageUrl, ui.BlendMode blendMode, double width, double height) { + String imageUrl, + ui.BlendMode blendMode, + double width, + double height, +) { final SvgFilter svgFilter; switch (blendMode) { case ui.BlendMode.src: @@ -235,8 +240,7 @@ SvgFilter svgMaskFilterFromImageAndBlendMode( case ui.BlendMode.softLight: case ui.BlendMode.difference: case ui.BlendMode.exclusion: - svgFilter = _blendImageToSvg( - imageUrl, blendModeToSvgEnum(blendMode)!, width, height); + svgFilter = _blendImageToSvg(imageUrl, blendModeToSvgEnum(blendMode)!, width, height); case ui.BlendMode.dst: case ui.BlendMode.dstATop: case ui.BlendMode.dstIn: @@ -244,8 +248,7 @@ SvgFilter svgMaskFilterFromImageAndBlendMode( case ui.BlendMode.dstOver: case ui.BlendMode.clear: case ui.BlendMode.srcOver: - throw UnsupportedError( - 'Invalid svg filter request for blend-mode $blendMode'); + throw UnsupportedError('Invalid svg filter request for blend-mode $blendMode'); } return svgFilter; } @@ -265,21 +268,29 @@ SvgFilter svgMaskFilterFromImageAndBlendMode( // A' = a1*R + a2*G + a3*B + a4*A + a5 SvgFilter _srcInImageToSvg(String imageUrl, double width, double height) { final SvgFilterBuilder builder = SvgFilterBuilder(); - builder.setFeColorMatrix( - const [ - 0, 0, 0, 0, 1, - 0, 0, 0, 0, 1, - 0, 0, 0, 0, 1, - 0, 0, 0, 1, 0, - ], - result: 'destalpha', - ); - builder.setFeImage( - href: imageUrl, - result: 'image', - width: width, - height: height, - ); + builder.setFeColorMatrix(const [ + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + ], result: 'destalpha'); + builder.setFeImage(href: imageUrl, result: 'image', width: width, height: height); builder.setFeComposite( in1: 'image', in2: 'destalpha', @@ -295,23 +306,13 @@ SvgFilter _srcInImageToSvg(String imageUrl, double width, double height) { SvgFilter _srcImageToSvg(String imageUrl, double width, double height) { final SvgFilterBuilder builder = SvgFilterBuilder(); - builder.setFeImage( - href: imageUrl, - result: 'comp', - width: width, - height: height, - ); + builder.setFeImage(href: imageUrl, result: 'comp', width: width, height: height); return builder.build(); } SvgFilter _srcOutImageToSvg(String imageUrl, double width, double height) { final SvgFilterBuilder builder = SvgFilterBuilder(); - builder.setFeImage( - href: imageUrl, - result: 'image', - width: width, - height: height, - ); + builder.setFeImage(href: imageUrl, result: 'image', width: width, height: height); builder.setFeComposite( in1: 'image', in2: 'SourceGraphic', @@ -323,12 +324,7 @@ SvgFilter _srcOutImageToSvg(String imageUrl, double width, double height) { SvgFilter _xorImageToSvg(String imageUrl, double width, double height) { final SvgFilterBuilder builder = SvgFilterBuilder(); - builder.setFeImage( - href: imageUrl, - result: 'image', - width: width, - height: height, - ); + builder.setFeImage(href: imageUrl, result: 'image', width: width, height: height); builder.setFeComposite( in1: 'image', in2: 'SourceGraphic', @@ -340,15 +336,17 @@ SvgFilter _xorImageToSvg(String imageUrl, double width, double height) { // The source image and color are composited using : // result = k1 *in*in2 + k2*in + k3*in2 + k4. -SvgFilter _compositeImageToSvg(String imageUrl, double k1, double k2, double k3, - double k4, double width, double height) { +SvgFilter _compositeImageToSvg( + String imageUrl, + double k1, + double k2, + double k3, + double k4, + double width, + double height, +) { final SvgFilterBuilder builder = SvgFilterBuilder(); - builder.setFeImage( - href: imageUrl, - result: 'image', - width: width, - height: height, - ); + builder.setFeImage(href: imageUrl, result: 'image', width: width, height: height); builder.setFeComposite( in1: 'image', in2: 'SourceGraphic', @@ -367,12 +365,7 @@ SvgFilter _compositeImageToSvg(String imageUrl, double k1, double k2, double k3, // composite using multiplication. SvgFilter _modulateImageToSvg(String imageUrl, double width, double height) { final SvgFilterBuilder builder = SvgFilterBuilder(); - builder.setFeImage( - href: imageUrl, - result: 'image', - width: width, - height: height, - ); + builder.setFeImage(href: imageUrl, result: 'image', width: width, height: height); builder.setFeComposite( in1: 'image', in2: 'SourceGraphic', @@ -388,27 +381,18 @@ SvgFilter _modulateImageToSvg(String imageUrl, double width, double height) { // Uses feBlend element to blend source image with a color. SvgFilter _blendImageToSvg( - String imageUrl, SvgBlendMode svgBlendMode, double width, double height, - {bool swapLayers = false}) { + String imageUrl, + SvgBlendMode svgBlendMode, + double width, + double height, { + bool swapLayers = false, +}) { final SvgFilterBuilder builder = SvgFilterBuilder(); - builder.setFeImage( - href: imageUrl, - result: 'image', - width: width, - height: height, - ); + builder.setFeImage(href: imageUrl, result: 'image', width: width, height: height); if (swapLayers) { - builder.setFeBlend( - in1: 'SourceGraphic', - in2: 'image', - mode: svgBlendMode.blendMode, - ); + builder.setFeBlend(in1: 'SourceGraphic', in2: 'image', mode: svgBlendMode.blendMode); } else { - builder.setFeBlend( - in1: 'image', - in2: 'SourceGraphic', - mode: svgBlendMode.blendMode, - ); + builder.setFeBlend(in1: 'image', in2: 'SourceGraphic', mode: svgBlendMode.blendMode); } return builder.build(); } diff --git a/lib/web_ui/lib/src/engine/html/shaders/image_shader.dart b/lib/web_ui/lib/src/engine/html/shaders/image_shader.dart index 2b6be0d435976..63b8a1da5a005 100644 --- a/lib/web_ui/lib/src/engine/html/shaders/image_shader.dart +++ b/lib/web_ui/lib/src/engine/html/shaders/image_shader.dart @@ -15,10 +15,14 @@ import '../render_vertices.dart'; import 'vertex_shaders.dart'; class EngineImageShader implements ui.ImageShader { - EngineImageShader(ui.Image image, this.tileModeX, this.tileModeY, - Float64List matrix4, this.filterQuality) - : image = image as HtmlImage, - matrix4 = Float32List.fromList(matrix4); + EngineImageShader( + ui.Image image, + this.tileModeX, + this.tileModeY, + Float64List matrix4, + this.filterQuality, + ) : image = image as HtmlImage, + matrix4 = Float32List.fromList(matrix4); final ui.TileMode tileModeX; final ui.TileMode tileModeY; @@ -29,15 +33,19 @@ class EngineImageShader implements ui.ImageShader { /// Whether fill pattern requires transform to shift tiling offset. bool requiresTileOffset = false; - Object createPaintStyle(DomCanvasRenderingContext2D context, - ui.Rect? shaderBounds, double density) { + Object createPaintStyle( + DomCanvasRenderingContext2D context, + ui.Rect? shaderBounds, + double density, + ) { /// Creates a canvas rendering context pattern based on image and tile modes. final ui.TileMode tileX = tileModeX; final ui.TileMode tileY = tileModeY; if (tileX != ui.TileMode.clamp && tileY != ui.TileMode.clamp) { return context.createPattern( - _resolveTiledImageSource(image, tileX, tileY)!, - _tileModeToHtmlRepeatAttribute(tileX, tileY))!; + _resolveTiledImageSource(image, tileX, tileY)!, + _tileModeToHtmlRepeatAttribute(tileX, tileY), + )!; } else { initWebGl(); return _createGlShader(context, shaderBounds!, density); @@ -50,15 +58,10 @@ class EngineImageShader implements ui.ImageShader { /// For mirroring we create a new image with mirror builtin so both /// repeated and mirrored modes can be supported when applied to /// html element background-image or used by createPattern api. - String _tileModeToHtmlRepeatAttribute( - ui.TileMode tileModeX, ui.TileMode tileModeY) { - final bool repeatX = - tileModeX == ui.TileMode.repeated || tileModeX == ui.TileMode.mirror; - final bool repeatY = - tileModeY == ui.TileMode.repeated || tileModeY == ui.TileMode.mirror; - return repeatX - ? (repeatY ? 'repeat' : 'repeat-x') - : (repeatY ? 'repeat-y' : 'no-repeat'); + String _tileModeToHtmlRepeatAttribute(ui.TileMode tileModeX, ui.TileMode tileModeY) { + final bool repeatX = tileModeX == ui.TileMode.repeated || tileModeX == ui.TileMode.mirror; + final bool repeatY = tileModeY == ui.TileMode.repeated || tileModeY == ui.TileMode.mirror; + return repeatX ? (repeatY ? 'repeat' : 'repeat-x') : (repeatY ? 'repeat-y' : 'no-repeat'); } /// Tiles the image and returns an image or canvas element to be used as @@ -68,8 +71,7 @@ class EngineImageShader implements ui.ImageShader { /// tile in the shader, but that will generate a much larger image footprint /// when the pattern is small. So we opt here for mirroring by /// redrawing the image 2 or 4 times into a new bitmap. - Object? _resolveTiledImageSource( - HtmlImage image, ui.TileMode tileX, ui.TileMode tileY) { + Object? _resolveTiledImageSource(HtmlImage image, ui.TileMode tileX, ui.TileMode tileY) { final int mirrorX = tileX == ui.TileMode.mirror ? 2 : 1; final int mirrorY = tileY == ui.TileMode.mirror ? 2 : 1; @@ -83,8 +85,7 @@ class EngineImageShader implements ui.ImageShader { final int imageHeight = image.height; final int newWidth = imageWidth * mirrorX; final int newHeight = imageHeight * mirrorY; - final OffScreenCanvas offscreenCanvas = - OffScreenCanvas(newWidth, newHeight); + final OffScreenCanvas offscreenCanvas = OffScreenCanvas(newWidth, newHeight); final Object renderContext = offscreenCanvas.getContext2d()!; for (int y = 0; y < mirrorY; y++) { for (int x = 0; x < mirrorX; x++) { @@ -111,12 +112,10 @@ class EngineImageShader implements ui.ImageShader { // When using OffscreenCanvas and transferToImageBitmap is supported by // browser create ImageBitmap otherwise use more expensive canvas // allocation. - if (OffScreenCanvas.supported && - offscreenCanvas.transferToImageBitmapSupported) { + if (OffScreenCanvas.supported && offscreenCanvas.transferToImageBitmapSupported) { return offscreenCanvas.transferToImageBitmap(); } else { - final DomCanvasElement canvas = - createDomCanvasElement(width: newWidth, height: newHeight); + final DomCanvasElement canvas = createDomCanvasElement(width: newWidth, height: newHeight); final DomCanvasRenderingContext2D ctx = canvas.context2D; offscreenCanvas.transferImage(ctx); return canvas; @@ -124,8 +123,11 @@ class EngineImageShader implements ui.ImageShader { } /// Creates an image with tiled/transformed images. - DomCanvasPattern _createGlShader(DomCanvasRenderingContext2D? context, - ui.Rect shaderBounds, double density) { + DomCanvasPattern _createGlShader( + DomCanvasRenderingContext2D? context, + ui.Rect shaderBounds, + double density, + ) { final Matrix4 transform = Matrix4.fromFloat32List(matrix4); final double dpr = ui.window.devicePixelRatio; @@ -139,11 +141,13 @@ class EngineImageShader implements ui.ImageShader { final String vertexShader = VertexShaders.writeTextureVertexShader(); final String fragmentShader = FragmentShaders.writeTextureFragmentShader( - isWebGl2, tileModeX, tileModeY); + isWebGl2, + tileModeX, + tileModeY, + ); /// Render gradient into a bitmap and create a canvas pattern. - final OffScreenCanvas offScreenCanvas = - OffScreenCanvas(widthInPixels, heightInPixels); + final OffScreenCanvas offScreenCanvas = OffScreenCanvas(widthInPixels, heightInPixels); final GlContext gl = GlContext(offScreenCanvas); gl.setViewportSize(widthInPixels, heightInPixels); @@ -152,8 +156,7 @@ class EngineImageShader implements ui.ImageShader { const int vertexCount = 6; final Float32List vertices = Float32List(vertexCount * 2); - final ui.Rect vRect = - shaderBounds.translate(-shaderBounds.left, -shaderBounds.top); + final ui.Rect vRect = shaderBounds.translate(-shaderBounds.left, -shaderBounds.top); vertices[0] = vRect.left; vertices[1] = vRect.top; vertices[2] = vRect.right; @@ -167,18 +170,30 @@ class EngineImageShader implements ui.ImageShader { vertices[10] = vRect.left; vertices[11] = vRect.top; - final Object positionAttributeLocation = - gl.getAttributeLocation(glProgram.program, 'position'); + final Object positionAttributeLocation = gl.getAttributeLocation(glProgram.program, 'position'); - setupVertexTransforms(gl, glProgram, 0, 0, widthInPixels.toDouble(), - heightInPixels.toDouble(), transform); + setupVertexTransforms( + gl, + glProgram, + 0, + 0, + widthInPixels.toDouble(), + heightInPixels.toDouble(), + transform, + ); requiresTileOffset = shaderBounds.left != 0 || shaderBounds.top != 0; /// To map from vertex position to texture coordinate in 0..1 range, /// we setup scalar to be used in vertex shader. - setupTextureTransform(gl, glProgram, shaderBounds.left, shaderBounds.top, - 1.0 / image.width.toDouble(), 1.0 / image.height.toDouble()); + setupTextureTransform( + gl, + glProgram, + shaderBounds.left, + shaderBounds.top, + 1.0 / image.width.toDouble(), + 1.0 / image.height.toDouble(), + ); /// Setup geometry. /// @@ -224,17 +239,14 @@ class EngineImageShader implements ui.ImageShader { gl.activeTexture(gl.kTexture0); gl.bindTexture(gl.kTexture2D, texture); - gl.texImage2D(gl.kTexture2D, 0, gl.kRGBA, gl.kRGBA, gl.kUnsignedByte, - image.imgElement); + gl.texImage2D(gl.kTexture2D, 0, gl.kRGBA, gl.kRGBA, gl.kUnsignedByte, image.imgElement); if (isWebGl2) { /// Texture REPEAT and MIRROR is only supported in WebGL 2, for /// WebGL 1.0 we let shader compute correct uv coordinates. - gl.texParameteri( - gl.kTexture2D, gl.kTextureWrapS, tileModeToGlWrapping(gl, tileModeX)); + gl.texParameteri(gl.kTexture2D, gl.kTextureWrapS, tileModeToGlWrapping(gl, tileModeX)); - gl.texParameteri( - gl.kTexture2D, gl.kTextureWrapT, tileModeToGlWrapping(gl, tileModeY)); + gl.texParameteri(gl.kTexture2D, gl.kTextureWrapT, tileModeToGlWrapping(gl, tileModeY)); /// Mipmapping saves your texture in different resolutions /// so the graphics card can choose which resolution is optimal diff --git a/lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart b/lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart index e3c9261fa21dc..14631b4df31df 100644 --- a/lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart +++ b/lib/web_ui/lib/src/engine/html/shaders/normalized_gradient.dart @@ -40,8 +40,7 @@ class NormalizedGradient { } final Float32List bias = Float32List(normalizedCount * 4); final Float32List scale = Float32List(normalizedCount * 4); - final Float32List thresholds = - Float32List(4 * ((normalizedCount - 1) ~/ 4 + 1)); + final Float32List thresholds = Float32List(4 * ((normalizedCount - 1) ~/ 4 + 1)); int targetIndex = 0; int thresholdIndex = 0; if (addFirst) { @@ -74,8 +73,8 @@ class NormalizedGradient { final int lastColorIndex = 4 * (normalizedCount - 1); for (int i = 0; i < lastColorIndex; i++) { final int thresholdIndex = i >> 2; - scale[i] = (bias[i + 4] - bias[i]) / - (thresholds[thresholdIndex + 1] - thresholds[thresholdIndex]); + scale[i] = + (bias[i + 4] - bias[i]) / (thresholds[thresholdIndex + 1] - thresholds[thresholdIndex]); } scale[lastColorIndex] = 0.0; scale[lastColorIndex + 1] = 0.0; @@ -94,7 +93,12 @@ class NormalizedGradient { } NormalizedGradient._( - this.thresholdCount, this._thresholds, this._scale, this._bias, this.isOpaque); + this.thresholdCount, + this._thresholds, + this._scale, + this._bias, + this.isOpaque, + ); final Float32List _thresholds; final Float32List _bias; @@ -106,17 +110,25 @@ class NormalizedGradient { void setupUniforms(GlContext gl, GlProgram glProgram) { for (int i = 0; i < thresholdCount; i++) { final Object biasId = gl.getUniformLocation(glProgram.program, 'bias_$i'); - gl.setUniform4f(biasId, _bias[i * 4], _bias[i * 4 + 1], _bias[i * 4 + 2], - _bias[i * 4 + 3]); + gl.setUniform4f(biasId, _bias[i * 4], _bias[i * 4 + 1], _bias[i * 4 + 2], _bias[i * 4 + 3]); final Object scaleId = gl.getUniformLocation(glProgram.program, 'scale_$i'); - gl.setUniform4f(scaleId, _scale[i * 4], _scale[i * 4 + 1], - _scale[i * 4 + 2], _scale[i * 4 + 3]); + gl.setUniform4f( + scaleId, + _scale[i * 4], + _scale[i * 4 + 1], + _scale[i * 4 + 2], + _scale[i * 4 + 3], + ); } for (int i = 0; i < _thresholds.length; i += 4) { - final Object thresId = - gl.getUniformLocation(glProgram.program, 'threshold_${i ~/ 4}'); - gl.setUniform4f(thresId, _thresholds[i], _thresholds[i + 1], - _thresholds[i + 2], _thresholds[i + 3]); + final Object thresId = gl.getUniformLocation(glProgram.program, 'threshold_${i ~/ 4}'); + gl.setUniform4f( + thresId, + _thresholds[i], + _thresholds[i + 1], + _thresholds[i + 2], + _thresholds[i + 3], + ); } } @@ -137,11 +149,15 @@ class NormalizedGradient { /// uniforms. /// /// Bias and scale data are vec4 uniforms that hold color data. -void writeUnrolledBinarySearch(ShaderMethod method, int start, int end, - {required String probe, - required String sourcePrefix, - required String biasName, - required String scaleName}) { +void writeUnrolledBinarySearch( + ShaderMethod method, + int start, + int end, { + required String probe, + required String sourcePrefix, + required String biasName, + required String scaleName, +}) { if (start == end) { final String biasSource = '${biasName}_$start'; method.addStatement('$biasName = $biasSource;'); @@ -154,19 +170,27 @@ void writeUnrolledBinarySearch(ShaderMethod method, int start, int end, thresholdAtMid += '.${vectorComponentIndexToName((mid + 1) % 4)}'; method.addStatement('if ($probe < $thresholdAtMid) {'); method.indent(); - writeUnrolledBinarySearch(method, start, mid, - probe: probe, - sourcePrefix: sourcePrefix, - biasName: biasName, - scaleName: scaleName); + writeUnrolledBinarySearch( + method, + start, + mid, + probe: probe, + sourcePrefix: sourcePrefix, + biasName: biasName, + scaleName: scaleName, + ); method.unindent(); method.addStatement('} else {'); method.indent(); - writeUnrolledBinarySearch(method, mid + 1, end, - probe: probe, - sourcePrefix: sourcePrefix, - biasName: biasName, - scaleName: scaleName); + writeUnrolledBinarySearch( + method, + mid + 1, + end, + probe: probe, + sourcePrefix: sourcePrefix, + biasName: biasName, + scaleName: scaleName, + ); method.unindent(); method.addStatement('}'); } diff --git a/lib/web_ui/lib/src/engine/html/shaders/shader.dart b/lib/web_ui/lib/src/engine/html/shaders/shader.dart index 62f722eee772b..c2f4db6c1ba9f 100644 --- a/lib/web_ui/lib/src/engine/html/shaders/shader.dart +++ b/lib/web_ui/lib/src/engine/html/shaders/shader.dart @@ -31,7 +31,7 @@ class SharedCanvas { GlContext checkOutContext(int width, int height) { assert(!_checkedOut); _checkedOut = true; - if(_canvas == null) { + if (_canvas == null) { _canvas = OffScreenCanvas(width, height); } else { _canvas!.resize(width, height); @@ -44,6 +44,7 @@ class SharedCanvas { _checkedOut = false; } } + SharedCanvas _sharedCanvas = SharedCanvas(); abstract class EngineGradient implements ui.Gradient { @@ -51,12 +52,10 @@ abstract class EngineGradient implements ui.Gradient { EngineGradient._(); /// Creates a fill style to be used in painting. - Object createPaintStyle(DomCanvasRenderingContext2D? ctx, - ui.Rect? shaderBounds, double density); + Object createPaintStyle(DomCanvasRenderingContext2D? ctx, ui.Rect? shaderBounds, double density); /// Creates a CanvasImageSource to paint gradient. - Object createImageBitmap( - ui.Rect? shaderBounds, double density, bool createDataUrl); + Object createImageBitmap(ui.Rect? shaderBounds, double density, bool createDataUrl); @override bool debugDisposed = false; @@ -69,16 +68,21 @@ abstract class EngineGradient implements ui.Gradient { } class GradientSweep extends EngineGradient { - GradientSweep(this.center, this.colors, this.colorStops, this.tileMode, - this.startAngle, this.endAngle, this.matrix4) - : assert(offsetIsValid(center)), - super._() { + GradientSweep( + this.center, + this.colors, + this.colorStops, + this.tileMode, + this.startAngle, + this.endAngle, + this.matrix4, + ) : assert(offsetIsValid(center)), + super._() { validateColorStops(colors, colorStops); } @override - Object createImageBitmap( - ui.Rect? shaderBounds, double density, bool createDataUrl) { + Object createImageBitmap(ui.Rect? shaderBounds, double density, bool createDataUrl) { assert(shaderBounds != null); final int widthInPixels = shaderBounds!.width.ceil(); final int heightInPixels = shaderBounds.height.ceil(); @@ -89,29 +93,30 @@ class GradientSweep extends EngineGradient { final GlContext gl = _sharedCanvas.checkOutContext(widthInPixels, heightInPixels); gl.setViewportSize(widthInPixels, heightInPixels); - final NormalizedGradient normalizedGradient = - NormalizedGradient(colors, stops: colorStops); + final NormalizedGradient normalizedGradient = NormalizedGradient(colors, stops: colorStops); - final GlProgram glProgram = gl.cacheProgram(VertexShaders.writeBaseVertexShader(), - _createSweepFragmentShader(normalizedGradient, tileMode)); + final GlProgram glProgram = gl.cacheProgram( + VertexShaders.writeBaseVertexShader(), + _createSweepFragmentShader(normalizedGradient, tileMode), + ); gl.useProgram(glProgram); - final Object tileOffset = - gl.getUniformLocation(glProgram.program, 'u_tile_offset'); + final Object tileOffset = gl.getUniformLocation(glProgram.program, 'u_tile_offset'); final double centerX = (center.dx - shaderBounds.left) / (shaderBounds.width); final double centerY = (center.dy - shaderBounds.top) / (shaderBounds.height); - gl.setUniform2f(tileOffset, 2 * (shaderBounds.width * (centerX - 0.5)), - 2 * (shaderBounds.height * (0.5 - centerY))); + gl.setUniform2f( + tileOffset, + 2 * (shaderBounds.width * (centerX - 0.5)), + 2 * (shaderBounds.height * (0.5 - centerY)), + ); final Object angleRange = gl.getUniformLocation(glProgram.program, 'angle_range'); gl.setUniform2f(angleRange, startAngle, endAngle); normalizedGradient.setupUniforms(gl, glProgram); - final Object gradientMatrix = - gl.getUniformLocation(glProgram.program, 'm_gradient'); + final Object gradientMatrix = gl.getUniformLocation(glProgram.program, 'm_gradient'); final Matrix4 gradientTransform = Matrix4.identity(); if (matrix4 != null) { - final Matrix4 m4 = Matrix4.zero() - ..copyInverse(Matrix4.fromFloat32List(matrix4!)); + final Matrix4 m4 = Matrix4.zero()..copyInverse(Matrix4.fromFloat32List(matrix4!)); gradientTransform.translate(-center.dx, -center.dy); gradientTransform.multiply(m4); gradientTransform.translate(center.dx, center.dy); @@ -121,20 +126,22 @@ class GradientSweep extends EngineGradient { final Object result = () { if (createDataUrl) { return glRenderer!.drawRectToImageUrl( - ui.Rect.fromLTWH(0, 0, shaderBounds.width, shaderBounds.height), - gl, - glProgram, - normalizedGradient, - widthInPixels, - heightInPixels); + ui.Rect.fromLTWH(0, 0, shaderBounds.width, shaderBounds.height), + gl, + glProgram, + normalizedGradient, + widthInPixels, + heightInPixels, + ); } else { return glRenderer!.drawRect( - ui.Rect.fromLTWH(0, 0, shaderBounds.width, shaderBounds.height), - gl, - glProgram, - normalizedGradient, - widthInPixels, - heightInPixels)!; + ui.Rect.fromLTWH(0, 0, shaderBounds.width, shaderBounds.height), + gl, + glProgram, + normalizedGradient, + widthInPixels, + heightInPixels, + )!; } }(); _sharedCanvas.checkInContext(); @@ -142,14 +149,12 @@ class GradientSweep extends EngineGradient { } @override - Object createPaintStyle(DomCanvasRenderingContext2D? ctx, - ui.Rect? shaderBounds, double density) { + Object createPaintStyle(DomCanvasRenderingContext2D? ctx, ui.Rect? shaderBounds, double density) { final Object imageBitmap = createImageBitmap(shaderBounds, density, false); return ctx!.createPattern(imageBitmap, 'no-repeat')!; } - String _createSweepFragmentShader( - NormalizedGradient gradient, ui.TileMode tileMode) { + String _createSweepFragmentShader(NormalizedGradient gradient, ui.TileMode tileMode) { final ShaderBuilder builder = ShaderBuilder.fragment(webGLVersion); builder.floatPrecision = ShaderPrecision.kMedium; builder.addIn(ShaderType.kVec4, name: 'v_color'); @@ -162,16 +167,14 @@ class GradientSweep extends EngineGradient { // Sweep gradient method.addStatement('vec2 center = 0.5 * (u_resolution + u_tile_offset);'); method.addStatement( - 'vec4 localCoord = m_gradient * vec4(gl_FragCoord.x - center.x, center.y - gl_FragCoord.y, 0, 1);'); - method.addStatement( - 'float angle = atan(-localCoord.y, -localCoord.x) + ${math.pi};'); + 'vec4 localCoord = m_gradient * vec4(gl_FragCoord.x - center.x, center.y - gl_FragCoord.y, 0, 1);', + ); + method.addStatement('float angle = atan(-localCoord.y, -localCoord.x) + ${math.pi};'); method.addStatement('float sweep = angle_range.y - angle_range.x;'); method.addStatement('angle = (angle - angle_range.x) / sweep;'); - method.addStatement( - 'float st = angle;'); + method.addStatement('float st = angle;'); - final String probeName = - _writeSharedGradientShader(builder, method, gradient, tileMode); + final String probeName = _writeSharedGradientShader(builder, method, gradient, tileMode); method.addStatement('${fragColor.name} = $probeName * scale + bias;'); final String shader = builder.build(); @@ -195,10 +198,10 @@ class GradientLinear extends EngineGradient { this.colorStops, this.tileMode, Float32List? matrix, - ) : assert(offsetIsValid(from)), - assert(offsetIsValid(to)), - matrix4 = matrix == null ? null : FastMatrix32(matrix), - super._() { + ) : assert(offsetIsValid(from)), + assert(offsetIsValid(to)), + matrix4 = matrix == null ? null : FastMatrix32(matrix), + super._() { // ignore: prefer_asserts_in_initializer_lists assert(() { validateColorStops(colors, colorStops); @@ -214,8 +217,7 @@ class GradientLinear extends EngineGradient { final FastMatrix32? matrix4; @override - Object createPaintStyle(DomCanvasRenderingContext2D? ctx, - ui.Rect? shaderBounds, double density) { + Object createPaintStyle(DomCanvasRenderingContext2D? ctx, ui.Rect? shaderBounds, double density) { if (tileMode == ui.TileMode.clamp || tileMode == ui.TileMode.decal) { return _createCanvasGradient(ctx, shaderBounds, density); } else { @@ -223,8 +225,11 @@ class GradientLinear extends EngineGradient { } } - DomCanvasGradient _createCanvasGradient(DomCanvasRenderingContext2D? ctx, - ui.Rect? shaderBounds, double density) { + DomCanvasGradient _createCanvasGradient( + DomCanvasRenderingContext2D? ctx, + ui.Rect? shaderBounds, + double density, + ) { final FastMatrix32? matrix4 = this.matrix4; DomCanvasGradient gradient; final double offsetX = shaderBounds!.left; @@ -240,22 +245,25 @@ class GradientLinear extends EngineGradient { final double fromY = matrix4.transformedY + centerY; matrix4.transform(to.dx - centerX, to.dy - centerY); gradient = ctx!.createLinearGradient( - fromX - offsetX, - fromY - offsetY, - matrix4.transformedX + centerX - offsetX, - matrix4.transformedY + centerY - offsetY); + fromX - offsetX, + fromY - offsetY, + matrix4.transformedX + centerX - offsetX, + matrix4.transformedY + centerY - offsetY, + ); } else { - gradient = ctx!.createLinearGradient(from.dx - offsetX, from.dy - offsetY, - to.dx - offsetX, to.dy - offsetY); + gradient = ctx!.createLinearGradient( + from.dx - offsetX, + from.dy - offsetY, + to.dx - offsetX, + to.dy - offsetY, + ); } - _addColorStopsToCanvasGradient( - gradient, colors, colorStops, tileMode == ui.TileMode.decal); + _addColorStopsToCanvasGradient(gradient, colors, colorStops, tileMode == ui.TileMode.decal); return gradient; } @override - Object createImageBitmap( - ui.Rect? shaderBounds, double density, bool createDataUrl) { + Object createImageBitmap(ui.Rect? shaderBounds, double density, bool createDataUrl) { assert(shaderBounds != null); final int widthInPixels = shaderBounds!.width.ceil(); final int heightInPixels = shaderBounds.height.ceil(); @@ -265,11 +273,12 @@ class GradientLinear extends EngineGradient { final GlContext gl = _sharedCanvas.checkOutContext(widthInPixels, heightInPixels); gl.setViewportSize(widthInPixels, heightInPixels); - final NormalizedGradient normalizedGradient = - NormalizedGradient(colors, stops: colorStops); + final NormalizedGradient normalizedGradient = NormalizedGradient(colors, stops: colorStops); - final GlProgram glProgram = gl.cacheProgram(VertexShaders.writeBaseVertexShader(), - _createLinearFragmentShader(normalizedGradient, tileMode)); + final GlProgram glProgram = gl.cacheProgram( + VertexShaders.writeBaseVertexShader(), + _createLinearFragmentShader(normalizedGradient, tileMode), + ); gl.useProgram(glProgram); // Setup from/to uniforms. @@ -300,15 +309,12 @@ class GradientLinear extends EngineGradient { // If tile mode is repeated we need to shift the center of from->to // vector to the center of shader bounds. final bool isRepeated = tileMode != ui.TileMode.clamp; - final double originX = isRepeated - ? (shaderBounds.width / 2) - : (fromX + toX) / 2.0 - shaderBounds.left; - final double originY = isRepeated - ? (shaderBounds.height / 2) - : (fromY + toY) / 2.0 - shaderBounds.top; - - final Matrix4 originTranslation = - Matrix4.translationValues(-originX, -originY, 0); + final double originX = + isRepeated ? (shaderBounds.width / 2) : (fromX + toX) / 2.0 - shaderBounds.left; + final double originY = + isRepeated ? (shaderBounds.height / 2) : (fromY + toY) / 2.0 - shaderBounds.top; + + final Matrix4 originTranslation = Matrix4.translationValues(-originX, -originY, 0); // Rotate around Z axis. final Matrix4 rotationZ = Matrix4.identity(); final Float32List storage = rotationZ.storage; @@ -330,8 +336,7 @@ class GradientLinear extends EngineGradient { // with flipped y axis. // We flip y axis, translate to center, multiply matrix and translate // and flip back so it is applied correctly. - final Matrix4 m4 = Matrix4.zero() - ..copyInverse(Matrix4.fromFloat32List(matrix4!.matrix)); + final Matrix4 m4 = Matrix4.zero()..copyInverse(Matrix4.fromFloat32List(matrix4!.matrix)); final ui.Offset center = shaderBounds.center; gradientTransform.translate(-center.dx, -center.dy); gradientTransform.multiply(m4); @@ -343,8 +348,7 @@ class GradientLinear extends EngineGradient { // Setup gradient uniforms for t search. normalizedGradient.setupUniforms(gl, glProgram); // Setup matrix transform uniform. - final Object gradientMatrix = - gl.getUniformLocation(glProgram.program, 'm_gradient'); + final Object gradientMatrix = gl.getUniformLocation(glProgram.program, 'm_gradient'); gl.setUniformMatrix4fv(gradientMatrix, false, gradientTransform.storage); final Object uRes = gl.getUniformLocation(glProgram.program, 'u_resolution'); @@ -353,8 +357,7 @@ class GradientLinear extends EngineGradient { final Object result = () { if (createDataUrl) { return glRenderer!.drawRectToImageUrl( - ui.Rect.fromLTWH(0, 0, shaderBounds.width, - shaderBounds.height) /* !! shaderBounds */, + ui.Rect.fromLTWH(0, 0, shaderBounds.width, shaderBounds.height) /* !! shaderBounds */, gl, glProgram, normalizedGradient, @@ -363,8 +366,7 @@ class GradientLinear extends EngineGradient { ); } else { return glRenderer!.drawRect( - ui.Rect.fromLTWH(0, 0, shaderBounds.width, - shaderBounds.height) /* !! shaderBounds */, + ui.Rect.fromLTWH(0, 0, shaderBounds.width, shaderBounds.height) /* !! shaderBounds */, gl, glProgram, normalizedGradient, @@ -378,14 +380,16 @@ class GradientLinear extends EngineGradient { } /// Creates a linear gradient with tiling repeat or mirror. - DomCanvasPattern _createGlGradient(DomCanvasRenderingContext2D? ctx, - ui.Rect? shaderBounds, double density) { + DomCanvasPattern _createGlGradient( + DomCanvasRenderingContext2D? ctx, + ui.Rect? shaderBounds, + double density, + ) { final Object imageBitmap = createImageBitmap(shaderBounds, density, false); return ctx!.createPattern(imageBitmap, 'no-repeat')!; } - String _createLinearFragmentShader( - NormalizedGradient gradient, ui.TileMode tileMode) { + String _createLinearFragmentShader(NormalizedGradient gradient, ui.TileMode tileMode) { final ShaderBuilder builder = ShaderBuilder.fragment(webGLVersion); builder.floatPrecision = ShaderPrecision.kMedium; builder.addIn(ShaderType.kVec4, name: 'v_color'); @@ -396,19 +400,24 @@ class GradientLinear extends EngineGradient { // Linear gradient. // Multiply with m_gradient transform to convert from fragment coordinate to // distance on the from-to line. - method.addStatement('vec4 localCoord = m_gradient * vec4(gl_FragCoord.x, ' - 'u_resolution.y - gl_FragCoord.y, 0, 1);'); + method.addStatement( + 'vec4 localCoord = m_gradient * vec4(gl_FragCoord.x, ' + 'u_resolution.y - gl_FragCoord.y, 0, 1);', + ); method.addStatement('float st = localCoord.x;'); - final String probeName = - _writeSharedGradientShader(builder, method, gradient, tileMode); + final String probeName = _writeSharedGradientShader(builder, method, gradient, tileMode); method.addStatement('${fragColor.name} = $probeName * scale + bias;'); final String shader = builder.build(); return shader; } } -void _addColorStopsToCanvasGradient(DomCanvasGradient gradient, - List colors, List? colorStops, bool isDecal) { +void _addColorStopsToCanvasGradient( + DomCanvasGradient gradient, + List colors, + List? colorStops, + bool isDecal, +) { double scale, offset; if (isDecal) { scale = 0.999; @@ -425,8 +434,7 @@ void _addColorStopsToCanvasGradient(DomCanvasGradient gradient, } else { for (int i = 0; i < colors.length; i++) { final double colorStop = colorStops[i].clamp(0.0, 1.0); - gradient.addColorStop( - colorStop * scale + offset, colors[i].toCssString()); + gradient.addColorStop(colorStop * scale + offset, colors[i].toCssString()); } } if (isDecal) { @@ -437,8 +445,12 @@ void _addColorStopsToCanvasGradient(DomCanvasGradient gradient, /// Writes shader code to map fragment value to gradient color. /// /// Returns name of gradient treshold variable to use to compute color. -String _writeSharedGradientShader(ShaderBuilder builder, ShaderMethod method, - NormalizedGradient gradient, ui.TileMode tileMode) { +String _writeSharedGradientShader( + ShaderBuilder builder, + ShaderMethod method, + NormalizedGradient gradient, + ui.TileMode tileMode, +) { method.addStatement('vec4 bias;'); method.addStatement('vec4 scale;'); // Write uniforms for each threshold, bias and scale. @@ -467,15 +479,18 @@ String _writeSharedGradientShader(ShaderBuilder builder, ShaderMethod method, probeName = 'tiled_st'; case ui.TileMode.mirror: method.addStatement('float t_1 = (st - 1.0);'); - method.addStatement( - 'float tiled_st = abs((t_1 - 2.0 * floor(t_1 * 0.5)) - 1.0);'); + method.addStatement('float tiled_st = abs((t_1 - 2.0 * floor(t_1 * 0.5)) - 1.0);'); probeName = 'tiled_st'; } - writeUnrolledBinarySearch(method, 0, gradient.thresholdCount - 1, - probe: probeName, - sourcePrefix: 'threshold', - biasName: 'bias', - scaleName: 'scale'); + writeUnrolledBinarySearch( + method, + 0, + gradient.thresholdCount - 1, + probe: probeName, + sourcePrefix: 'threshold', + biasName: 'bias', + scaleName: 'scale', + ); if (tileMode == ui.TileMode.decal) { method.addStatement('if (st < 0.0 || st > 1.0) {'); method.addStatement(' ${builder.fragmentColor.name} = vec4(0, 0, 0, 0);'); @@ -486,9 +501,14 @@ String _writeSharedGradientShader(ShaderBuilder builder, ShaderMethod method, } class GradientRadial extends EngineGradient { - GradientRadial(this.center, this.radius, this.colors, this.colorStops, - this.tileMode, this.matrix4) - : super._(); + GradientRadial( + this.center, + this.radius, + this.colors, + this.colorStops, + this.tileMode, + this.matrix4, + ) : super._(); final ui.Offset center; final double radius; @@ -498,8 +518,7 @@ class GradientRadial extends EngineGradient { final Float32List? matrix4; @override - Object createPaintStyle(DomCanvasRenderingContext2D? ctx, - ui.Rect? shaderBounds, double density) { + Object createPaintStyle(DomCanvasRenderingContext2D? ctx, ui.Rect? shaderBounds, double density) { if (matrix4 == null && (tileMode == ui.TileMode.clamp || tileMode == ui.TileMode.decal)) { return _createCanvasGradient(ctx, shaderBounds, density); } else { @@ -507,25 +526,27 @@ class GradientRadial extends EngineGradient { } } - Object _createCanvasGradient(DomCanvasRenderingContext2D? ctx, - ui.Rect? shaderBounds, double density) { + Object _createCanvasGradient( + DomCanvasRenderingContext2D? ctx, + ui.Rect? shaderBounds, + double density, + ) { final double offsetX = shaderBounds!.left; final double offsetY = shaderBounds.top; final DomCanvasGradient gradient = ctx!.createRadialGradient( - center.dx - offsetX, - center.dy - offsetY, - 0, - center.dx - offsetX, - center.dy - offsetY, - radius); - _addColorStopsToCanvasGradient( - gradient, colors, colorStops, tileMode == ui.TileMode.decal); + center.dx - offsetX, + center.dy - offsetY, + 0, + center.dx - offsetX, + center.dy - offsetY, + radius, + ); + _addColorStopsToCanvasGradient(gradient, colors, colorStops, tileMode == ui.TileMode.decal); return gradient; } @override - Object createImageBitmap( - ui.Rect? shaderBounds, double density, bool createDataUrl) { + Object createImageBitmap(ui.Rect? shaderBounds, double density, bool createDataUrl) { assert(shaderBounds != null); final int widthInPixels = shaderBounds!.width.ceil(); final int heightInPixels = shaderBounds.height.ceil(); @@ -536,33 +557,32 @@ class GradientRadial extends EngineGradient { final GlContext gl = _sharedCanvas.checkOutContext(widthInPixels, heightInPixels); gl.setViewportSize(widthInPixels, heightInPixels); - final NormalizedGradient normalizedGradient = - NormalizedGradient(colors, stops: colorStops); + final NormalizedGradient normalizedGradient = NormalizedGradient(colors, stops: colorStops); final GlProgram glProgram = gl.cacheProgram( - VertexShaders.writeBaseVertexShader(), - _createRadialFragmentShader( - normalizedGradient, shaderBounds, tileMode)); + VertexShaders.writeBaseVertexShader(), + _createRadialFragmentShader(normalizedGradient, shaderBounds, tileMode), + ); gl.useProgram(glProgram); - final Object tileOffset = - gl.getUniformLocation(glProgram.program, 'u_tile_offset'); + final Object tileOffset = gl.getUniformLocation(glProgram.program, 'u_tile_offset'); final double centerX = (center.dx - shaderBounds.left) / (shaderBounds.width); final double centerY = (center.dy - shaderBounds.top) / (shaderBounds.height); - gl.setUniform2f(tileOffset, 2 * (shaderBounds.width * (centerX - 0.5)), - 2 * (shaderBounds.height * (0.5 - centerY))); + gl.setUniform2f( + tileOffset, + 2 * (shaderBounds.width * (centerX - 0.5)), + 2 * (shaderBounds.height * (0.5 - centerY)), + ); final Object radiusUniform = gl.getUniformLocation(glProgram.program, 'u_radius'); gl.setUniform1f(radiusUniform, radius); normalizedGradient.setupUniforms(gl, glProgram); - final Object gradientMatrix = - gl.getUniformLocation(glProgram.program, 'm_gradient'); + final Object gradientMatrix = gl.getUniformLocation(glProgram.program, 'm_gradient'); final Matrix4 gradientTransform = Matrix4.identity(); if (matrix4 != null) { - final Matrix4 m4 = Matrix4.zero() - ..copyInverse(Matrix4.fromFloat32List(matrix4!)); + final Matrix4 m4 = Matrix4.zero()..copyInverse(Matrix4.fromFloat32List(matrix4!)); gradientTransform.translate(-center.dx, -center.dy); gradientTransform.multiply(m4); gradientTransform.translate(center.dx, center.dy); @@ -572,20 +592,22 @@ class GradientRadial extends EngineGradient { final Object result = () { if (createDataUrl) { return glRenderer!.drawRectToImageUrl( - ui.Rect.fromLTWH(0, 0, shaderBounds.width, shaderBounds.height), - gl, - glProgram, - normalizedGradient, - widthInPixels, - heightInPixels); + ui.Rect.fromLTWH(0, 0, shaderBounds.width, shaderBounds.height), + gl, + glProgram, + normalizedGradient, + widthInPixels, + heightInPixels, + ); } else { return glRenderer!.drawRect( - ui.Rect.fromLTWH(0, 0, shaderBounds.width, shaderBounds.height), - gl, - glProgram, - normalizedGradient, - widthInPixels, - heightInPixels)!; + ui.Rect.fromLTWH(0, 0, shaderBounds.width, shaderBounds.height), + gl, + glProgram, + normalizedGradient, + widthInPixels, + heightInPixels, + )!; } }(); _sharedCanvas.checkInContext(); @@ -593,14 +615,20 @@ class GradientRadial extends EngineGradient { } /// Creates a radial gradient with tiling repeat or mirror. - DomCanvasPattern _createGlGradient(DomCanvasRenderingContext2D? ctx, - ui.Rect? shaderBounds, double density) { + DomCanvasPattern _createGlGradient( + DomCanvasRenderingContext2D? ctx, + ui.Rect? shaderBounds, + double density, + ) { final Object imageBitmap = createImageBitmap(shaderBounds, density, false); return ctx!.createPattern(imageBitmap, 'no-repeat')!; } String _createRadialFragmentShader( - NormalizedGradient gradient, ui.Rect shaderBounds, ui.TileMode tileMode) { + NormalizedGradient gradient, + ui.Rect shaderBounds, + ui.TileMode tileMode, + ) { final ShaderBuilder builder = ShaderBuilder.fragment(webGLVersion); builder.floatPrecision = ShaderPrecision.kMedium; builder.addIn(ShaderType.kVec4, name: 'v_color'); @@ -613,12 +641,11 @@ class GradientRadial extends EngineGradient { // Sweep gradient method.addStatement('vec2 center = 0.5 * (u_resolution + u_tile_offset);'); method.addStatement( - 'vec4 localCoord = m_gradient * vec4(gl_FragCoord.x - center.x, center.y - gl_FragCoord.y, 0, 1);'); + 'vec4 localCoord = m_gradient * vec4(gl_FragCoord.x - center.x, center.y - gl_FragCoord.y, 0, 1);', + ); method.addStatement('float dist = length(localCoord);'); - method.addStatement( - 'float st = abs(dist / u_radius);'); - final String probeName = - _writeSharedGradientShader(builder, method, gradient, tileMode); + method.addStatement('float st = abs(dist / u_radius);'); + final String probeName = _writeSharedGradientShader(builder, method, gradient, tileMode); method.addStatement('${fragColor.name} = $probeName * scale + bias;'); final String shader = builder.build(); return shader; @@ -628,22 +655,21 @@ class GradientRadial extends EngineGradient { // TODO(ferhat): Implement focal https://github.com/flutter/flutter/issues/76643. class GradientConical extends GradientRadial { GradientConical( - this.focal, - this.focalRadius, - ui.Offset center, - double radius, - List colors, - List? colorStops, - ui.TileMode tileMode, - Float32List? matrix4) - : super(center, radius, colors, colorStops, tileMode, matrix4); + this.focal, + this.focalRadius, + ui.Offset center, + double radius, + List colors, + List? colorStops, + ui.TileMode tileMode, + Float32List? matrix4, + ) : super(center, radius, colors, colorStops, tileMode, matrix4); final ui.Offset focal; final double focalRadius; @override - Object createPaintStyle(DomCanvasRenderingContext2D? ctx, - ui.Rect? shaderBounds, double density) { + Object createPaintStyle(DomCanvasRenderingContext2D? ctx, ui.Rect? shaderBounds, double density) { if ((tileMode == ui.TileMode.clamp || tileMode == ui.TileMode.decal) && focalRadius == 0.0 && focal == ui.Offset.zero) { @@ -656,7 +682,10 @@ class GradientConical extends GradientRadial { @override String _createRadialFragmentShader( - NormalizedGradient gradient, ui.Rect shaderBounds, ui.TileMode tileMode) { + NormalizedGradient gradient, + ui.Rect shaderBounds, + ui.TileMode tileMode, + ) { /// If distance between centers is nearly zero we can pretend we're radial /// to prevent divide by zero in computing gradient. final double centerDistanceX = center.dx - focal.dx; @@ -664,8 +693,7 @@ class GradientConical extends GradientRadial { final double centerDistanceSq = centerDistanceX * centerDistanceX + centerDistanceY * centerDistanceY; if (centerDistanceSq < kFltEpsilonSquared) { - return super - ._createRadialFragmentShader(gradient, shaderBounds, tileMode); + return super._createRadialFragmentShader(gradient, shaderBounds, tileMode); } final double centerDistance = math.sqrt(centerDistanceSq); double r0 = focalRadius / centerDistance; @@ -692,19 +720,20 @@ class GradientConical extends GradientRadial { // Sweep gradient method.addStatement('vec2 center = 0.5 * (u_resolution + u_tile_offset);'); method.addStatement( - 'vec4 localCoord = m_gradient * vec4(gl_FragCoord.x - center.x, center.y - gl_FragCoord.y, 0, 1);'); + 'vec4 localCoord = m_gradient * vec4(gl_FragCoord.x - center.x, center.y - gl_FragCoord.y, 0, 1);', + ); method.addStatement('float dist = length(localCoord);'); - final String f = (focalRadius / - (math.min(shaderBounds.width, shaderBounds.height) / 2.0)) + final String f = (focalRadius / (math.min(shaderBounds.width, shaderBounds.height) / 2.0)) .toStringAsPrecision(8); - method.addStatement(focalRadius == 0.0 - ? 'float st = dist / u_radius;' - : 'float st = ((dist / u_radius) - $f) / (1.0 - $f);'); + method.addStatement( + focalRadius == 0.0 + ? 'float st = dist / u_radius;' + : 'float st = ((dist / u_radius) - $f) / (1.0 - $f);', + ); if (tileMode == ui.TileMode.clamp) { method.addStatement('if (st < 0.0) { st = -1.0; }'); } - final String probeName = - _writeSharedGradientShader(builder, method, gradient, tileMode); + final String probeName = _writeSharedGradientShader(builder, method, gradient, tileMode); method.addStatement('${fragColor.name} = $probeName * scale + bias;'); return builder.build(); } @@ -732,7 +761,7 @@ abstract class EngineImageFilter implements ui.ImageFilter { } class _BlurEngineImageFilter extends EngineImageFilter { - _BlurEngineImageFilter({ this.sigmaX = 0.0, this.sigmaY = 0.0, this.tileMode }) : super._(); + _BlurEngineImageFilter({this.sigmaX = 0.0, this.sigmaY = 0.0, this.tileMode}) : super._(); final double sigmaX; final double sigmaY; @@ -763,9 +792,9 @@ class _BlurEngineImageFilter extends EngineImageFilter { } class _MatrixEngineImageFilter extends EngineImageFilter { - _MatrixEngineImageFilter({ required Float64List matrix, required this.filterQuality }) - : webMatrix = Float64List.fromList(matrix), - super._(); + _MatrixEngineImageFilter({required Float64List matrix, required this.filterQuality}) + : webMatrix = Float64List.fromList(matrix), + super._(); final Float64List webMatrix; final ui.FilterQuality filterQuality; @@ -779,9 +808,9 @@ class _MatrixEngineImageFilter extends EngineImageFilter { if (other.runtimeType != runtimeType) { return false; } - return other is _MatrixEngineImageFilter - && other.filterQuality == filterQuality - && listEquals(other.webMatrix, webMatrix); + return other is _MatrixEngineImageFilter && + other.filterQuality == filterQuality && + listEquals(other.webMatrix, webMatrix); } @override @@ -869,7 +898,7 @@ class ModeHtmlColorFilter extends EngineHtmlColorFilter { if (blendMode == ui.BlendMode.saturation || blendMode == ui.BlendMode.multiply || blendMode == ui.BlendMode.modulate) { - filterElement!.style.backgroundColor = color.toCssString(); + filterElement!.style.backgroundColor = color.toCssString(); } return svgFilter.element; } @@ -899,20 +928,20 @@ EngineHtmlColorFilter? createHtmlColorFilter(EngineColorFilter? colorFilter) { return null; } switch (colorFilter.type) { - case ColorFilterType.mode: - if (colorFilter.color == null || colorFilter.blendMode == null) { - return null; - } - return ModeHtmlColorFilter(colorFilter.color!, colorFilter.blendMode!); - case ColorFilterType.matrix: - if (colorFilter.matrix == null) { - return null; - } - assert(colorFilter.matrix!.length == 20, 'Color Matrix must have 20 entries.'); - return MatrixHtmlColorFilter(colorFilter.matrix!); - case ColorFilterType.linearToSrgbGamma: - throw UnimplementedError('ColorFilter.linearToSrgbGamma not implemented for HTML renderer'); - case ColorFilterType.srgbToLinearGamma: - throw UnimplementedError('ColorFilter.srgbToLinearGamma not implemented for HTML renderer.'); - } + case ColorFilterType.mode: + if (colorFilter.color == null || colorFilter.blendMode == null) { + return null; + } + return ModeHtmlColorFilter(colorFilter.color!, colorFilter.blendMode!); + case ColorFilterType.matrix: + if (colorFilter.matrix == null) { + return null; + } + assert(colorFilter.matrix!.length == 20, 'Color Matrix must have 20 entries.'); + return MatrixHtmlColorFilter(colorFilter.matrix!); + case ColorFilterType.linearToSrgbGamma: + throw UnimplementedError('ColorFilter.linearToSrgbGamma not implemented for HTML renderer'); + case ColorFilterType.srgbToLinearGamma: + throw UnimplementedError('ColorFilter.srgbToLinearGamma not implemented for HTML renderer.'); + } } diff --git a/lib/web_ui/lib/src/engine/html/shaders/shader_builder.dart b/lib/web_ui/lib/src/engine/html/shaders/shader_builder.dart index 6c971c0832449..c66e1f911c045 100644 --- a/lib/web_ui/lib/src/engine/html/shaders/shader_builder.dart +++ b/lib/web_ui/lib/src/engine/html/shaders/shader_builder.dart @@ -27,12 +27,13 @@ import '../../browser_detection.dart'; /// method.addStatement('${u1.name} = vec4(1.0, 1.0, 1.0, 0.0);'); /// source = builder.build(); class ShaderBuilder { - ShaderBuilder(this.version) : isWebGl2 = version == WebGLVersion.webgl2, - _isFragmentShader = false; + ShaderBuilder(this.version) + : isWebGl2 = version == WebGLVersion.webgl2, + _isFragmentShader = false; - ShaderBuilder.fragment(this.version) : - isWebGl2 = version == WebGLVersion.webgl2, - _isFragmentShader = true; + ShaderBuilder.fragment(this.version) + : isWebGl2 = version == WebGLVersion.webgl2, + _isFragmentShader = true; /// WebGL version. final int version; @@ -70,9 +71,10 @@ class ShaderBuilder { /// This is hard coded for webgl1 as gl_FragColor. ShaderDeclaration get fragmentColor { _fragmentColorDeclaration ??= ShaderDeclaration( - isWebGl2 ? 'gFragColor' : 'gl_FragColor', - ShaderType.kVec4, - ShaderStorageQualifier.kVarying); + isWebGl2 ? 'gFragColor' : 'gl_FragColor', + ShaderType.kVec4, + ShaderStorageQualifier.kVarying, + ); return _fragmentColorDeclaration!; } @@ -83,9 +85,10 @@ class ShaderBuilder { /// in the vertex shader. ShaderDeclaration addIn(int dataType, {String? name}) { final ShaderDeclaration attrib = ShaderDeclaration( - name ?? 'attr_${_attribCounter++}', - dataType, - ShaderStorageQualifier.kAttribute); + name ?? 'attr_${_attribCounter++}', + dataType, + ShaderStorageQualifier.kAttribute, + ); declarations.add(attrib); return attrib; } @@ -93,7 +96,10 @@ class ShaderBuilder { /// Adds a constant. ShaderDeclaration addConst(int dataType, String value, {String? name}) { final ShaderDeclaration declaration = ShaderDeclaration.constant( - name ?? 'c_${_constCounter++}', dataType, value); + name ?? 'c_${_constCounter++}', + dataType, + value, + ); declarations.add(declaration); return declaration; } @@ -105,9 +111,10 @@ class ShaderBuilder { /// ShaderDeclaration addUniform(int dataType, {String? name}) { final ShaderDeclaration uniform = ShaderDeclaration( - name ?? 'uni_${_uniformCounter++}', - dataType, - ShaderStorageQualifier.kUniform); + name ?? 'uni_${_uniformCounter++}', + dataType, + ShaderStorageQualifier.kUniform, + ); declarations.add(uniform); return uniform; } @@ -120,9 +127,10 @@ class ShaderBuilder { /// It can be used in a fragment shader, but not changed. ShaderDeclaration addOut(int dataType, {String? name}) { final ShaderDeclaration varying = ShaderDeclaration( - name ?? 'output_${_varyingCounter++}', - dataType, - ShaderStorageQualifier.kVarying); + name ?? 'output_${_varyingCounter++}', + dataType, + ShaderStorageQualifier.kVarying, + ); declarations.add(varying); return varying; } @@ -132,8 +140,13 @@ class ShaderBuilder { case ShaderStorageQualifier.kConst: _buffer.write('const '); case ShaderStorageQualifier.kAttribute: - _buffer.write(isWebGl2 ? 'in ' - : _isFragmentShader ? 'varying ' : 'attribute '); + _buffer.write( + isWebGl2 + ? 'in ' + : _isFragmentShader + ? 'varying ' + : 'attribute ', + ); case ShaderStorageQualifier.kUniform: _buffer.write('uniform '); case ShaderStorageQualifier.kVarying: @@ -205,12 +218,10 @@ class ShaderBuilder { } // Write optional precision. if (integerPrecision != null) { - _buffer - .writeln('precision ${_precisionToString(integerPrecision!)} int;'); + _buffer.writeln('precision ${_precisionToString(integerPrecision!)} int;'); } if (floatPrecision != null) { - _buffer - .writeln('precision ${_precisionToString(floatPrecision!)} float;'); + _buffer.writeln('precision ${_precisionToString(floatPrecision!)} float;'); } if (isWebGl2 && _fragmentColorDeclaration != null) { _writeVariableDeclaration(_buffer, _fragmentColorDeclaration!); @@ -224,9 +235,12 @@ class ShaderBuilder { return _buffer.toString(); } - String _precisionToString(int precision) => precision == ShaderPrecision.kLow - ? 'lowp' - : precision == ShaderPrecision.kMedium ? 'mediump' : 'highp'; + String _precisionToString(int precision) => + precision == ShaderPrecision.kLow + ? 'lowp' + : precision == ShaderPrecision.kMedium + ? 'mediump' + : 'highp'; String get texture2DFunction => isWebGl2 ? 'texture' : 'texture2D'; } @@ -268,16 +282,16 @@ class ShaderMethod { /// from floor. /// float destination = 1.0 - source; /// destination = abs((destination - 2.0 * floor(destination * 0.5)) - 1.0); - void addTileStatements(String source, String destination, - ui.TileMode tileMode) { - switch(tileMode) { + void addTileStatements(String source, String destination, ui.TileMode tileMode) { + switch (tileMode) { case ui.TileMode.repeated: addStatement('float $destination = fract($source);'); case ui.TileMode.mirror: addStatement('float $destination = ($source - 1.0);'); addStatement( - '$destination = ' - 'abs(($destination - 2.0 * floor($destination * 0.5)) - 1.0);'); + '$destination = ' + 'abs(($destination - 2.0 * floor($destination * 0.5)) - 1.0);', + ); case ui.TileMode.clamp: case ui.TileMode.decal: addStatement('float $destination = $source;'); @@ -339,12 +353,12 @@ abstract class ShaderStorageQualifier { /// Shader variable and constant declaration. class ShaderDeclaration { ShaderDeclaration(this.name, this.dataType, this.storage) - : assert(!_isGLSLReservedWord(name)), - constValue = ''; + : assert(!_isGLSLReservedWord(name)), + constValue = ''; /// Constructs a constant. ShaderDeclaration.constant(this.name, this.dataType, this.constValue) - : storage = ShaderStorageQualifier.kConst; + : storage = ShaderStorageQualifier.kConst; final String name; final int dataType; diff --git a/lib/web_ui/lib/src/engine/html/shaders/vertex_shaders.dart b/lib/web_ui/lib/src/engine/html/shaders/vertex_shaders.dart index bd9a5d78b6a8a..4c705a95d46f3 100644 --- a/lib/web_ui/lib/src/engine/html/shaders/vertex_shaders.dart +++ b/lib/web_ui/lib/src/engine/html/shaders/vertex_shaders.dart @@ -11,8 +11,7 @@ import 'shader_builder.dart'; /// Provides common shaders used for gradients and drawVertices APIs. abstract final class VertexShaders { - static final Uint16List vertexIndicesForRect = - Uint16List.fromList([0, 1, 2, 2, 3, 0]); + static final Uint16List vertexIndicesForRect = Uint16List.fromList([0, 1, 2, 2, 3, 0]); /// Cached vertex shaders. static String? _baseVertexShader; @@ -41,8 +40,7 @@ abstract final class VertexShaders { builder.addUniform(ShaderType.kVec4, name: 'u_shift'); builder.addOut(ShaderType.kVec4, name: 'v_color'); final ShaderMethod method = builder.addMethod('main'); - method.addStatement( - 'gl_Position = ((u_ctransform * position) * u_scale) + u_shift;'); + method.addStatement('gl_Position = ((u_ctransform * position) * u_scale) + u_shift;'); method.addStatement('v_color = color.zyxw;'); _baseVertexShader = builder.build(); } @@ -59,10 +57,11 @@ abstract final class VertexShaders { builder.addUniform(ShaderType.kVec4, name: 'u_shift'); builder.addOut(ShaderType.kVec2, name: 'v_texcoord'); final ShaderMethod method = builder.addMethod('main'); + method.addStatement('gl_Position = ((u_ctransform * position) * u_scale) + u_shift;'); method.addStatement( - 'gl_Position = ((u_ctransform * position) * u_scale) + u_shift;'); - method.addStatement('v_texcoord = vec2((u_textransform.z + position.x) * u_textransform.x, ' - '((u_textransform.w + position.y) * u_textransform.y));'); + 'v_texcoord = vec2((u_textransform.z + position.x) * u_textransform.x, ' + '((u_textransform.w + position.y) * u_textransform.y));', + ); _textureVertexShader = builder.build(); } return _textureVertexShader!; @@ -71,7 +70,10 @@ abstract final class VertexShaders { abstract final class FragmentShaders { static String writeTextureFragmentShader( - bool isWebGl2, ui.TileMode? tileModeX, ui.TileMode? tileModeY) { + bool isWebGl2, + ui.TileMode? tileModeX, + ui.TileMode? tileModeY, + ) { final ShaderBuilder builder = ShaderBuilder.fragment(webGLVersion); builder.floatPrecision = ShaderPrecision.kMedium; builder.addIn(ShaderType.kVec2, name: 'v_texcoord'); @@ -81,8 +83,10 @@ abstract final class FragmentShaders { tileModeX == null || tileModeY == null || (tileModeX == ui.TileMode.clamp && tileModeY == ui.TileMode.clamp)) { - method.addStatement('${builder.fragmentColor.name} = ' - '${builder.texture2DFunction}(u_texture, v_texcoord);'); + method.addStatement( + '${builder.fragmentColor.name} = ' + '${builder.texture2DFunction}(u_texture, v_texcoord);', + ); } else { // Repeat and mirror are not supported for webgl1. Write code to // adjust texture coordinate. @@ -92,8 +96,10 @@ abstract final class FragmentShaders { method.addTileStatements('v_texcoord.x', 'u', tileModeX); method.addTileStatements('v_texcoord.y', 'v', tileModeY); method.addStatement('vec2 uv = vec2(u, v);'); - method.addStatement('${builder.fragmentColor.name} = ' - '${builder.texture2DFunction}(u_texture, uv);'); + method.addStatement( + '${builder.fragmentColor.name} = ' + '${builder.texture2DFunction}(u_texture, uv);', + ); } return builder.build(); } diff --git a/lib/web_ui/lib/src/engine/html/surface.dart b/lib/web_ui/lib/src/engine/html/surface.dart index cd76aff0c7d30..60e8002f8597d 100644 --- a/lib/web_ui/lib/src/engine/html/surface.dart +++ b/lib/web_ui/lib/src/engine/html/surface.dart @@ -67,8 +67,7 @@ void commitScene(PersistedScene scene) { if (retainedSurfaces.isNotEmpty) { for (int i = 0; i < retainedSurfaces.length; i++) { final PersistedSurface retainedSurface = retainedSurfaces[i]; - assert(debugAssertSurfaceState( - retainedSurface, PersistedSurfaceState.pendingRetention)); + assert(debugAssertSurfaceState(retainedSurface, PersistedSurfaceState.pendingRetention)); retainedSurface.state = PersistedSurfaceState.active; } retainedSurfaces = []; @@ -82,8 +81,10 @@ void commitScene(PersistedScene scene) { final List validationErrors = []; scene.debugValidate(validationErrors); if (validationErrors.isNotEmpty) { - print('ENGINE LAYER TREE INCONSISTENT:\n' - '${validationErrors.map((String e) => ' - $e\n').join()}'); + print( + 'ENGINE LAYER TREE INCONSISTENT:\n' + '${validationErrors.map((String e) => ' - $e\n').join()}', + ); } return true; }()); @@ -179,8 +180,11 @@ class PersistedSurfaceException implements Exception { /// /// This function should be used inside an assertion expression. bool debugAssertSurfaceState( - PersistedSurface surface, PersistedSurfaceState state1, - [PersistedSurfaceState? state2, PersistedSurfaceState? state3]) { + PersistedSurface surface, + PersistedSurfaceState state1, [ + PersistedSurfaceState? state2, + PersistedSurfaceState? state3, +]) { final List validStates = [state1, state2, state3]; if (validStates.contains(surface.state)) { @@ -200,8 +204,9 @@ bool debugAssertSurfaceState( abstract class PersistedSurface implements ui.EngineLayer { /// Creates a persisted surface. PersistedSurface(PersistedSurface? oldLayer) - : _oldLayer = FrameReference( - oldLayer != null && oldLayer.isActive ? oldLayer : null); + : _oldLayer = FrameReference( + oldLayer != null && oldLayer.isActive ? oldLayer : null, + ); /// The surface that is being updated using this surface. /// @@ -225,8 +230,10 @@ abstract class PersistedSurface implements ui.EngineLayer { /// surface. PersistedSurfaceState get state => _state; set state(PersistedSurfaceState newState) { - assert(newState != _state, - 'Attempted to set state that the surface is already in. This likely indicates a bug in the compositor.'); + assert( + newState != _state, + 'Attempted to set state that the surface is already in. This likely indicates a bug in the compositor.', + ); assert(_debugValidateStateTransition(newState)); _state = newState; } @@ -237,8 +244,10 @@ abstract class PersistedSurface implements ui.EngineLayer { if (newState == PersistedSurfaceState.created) { assert(isReleased, 'Only released surfaces may be revived.'); } else { - assert(!isReleased, - 'Released surfaces may only be revived, but caught attempt to set $newState.'); + assert( + !isReleased, + 'Released surfaces may only be revived, but caught attempt to set $newState.', + ); } if (newState == PersistedSurfaceState.active) { assert( @@ -247,16 +256,16 @@ abstract class PersistedSurface implements ui.EngineLayer { ); } if (newState == PersistedSurfaceState.pendingRetention) { - assert(isActive, - 'Surface is not active. Only active surfaces may be retained.'); + assert(isActive, 'Surface is not active. Only active surfaces may be retained.'); } if (newState == PersistedSurfaceState.pendingUpdate) { - assert(isActive, - 'Surface is not active. Only active surfaces may be updated.'); + assert(isActive, 'Surface is not active. Only active surfaces may be updated.'); } if (newState == PersistedSurfaceState.released) { - assert(isActive || isPendingUpdate, - 'A surface may only be released if it is currently active or pending update, but it is in $state.'); + assert( + isActive || isPendingUpdate, + 'A surface may only be released if it is currently active or pending update, but it is in $state.', + ); } return true; } @@ -268,8 +277,9 @@ abstract class PersistedSurface implements ui.EngineLayer { /// reused before the request to retain it came in. In this case, the surface /// is [revive]d and rebuilt from scratch. void tryRetain() { - assert(debugAssertSurfaceState( - this, PersistedSurfaceState.active, PersistedSurfaceState.released)); + assert( + debugAssertSurfaceState(this, PersistedSurfaceState.active, PersistedSurfaceState.released), + ); // Request that the layer is retained, but only if it's still active. It // could have been released. if (isActive) { @@ -301,8 +311,7 @@ abstract class PersistedSurface implements ui.EngineLayer { bool get isActive => _state == PersistedSurfaceState.active; /// The surface is in the [PersistedSurfaceState.pendingRetention] state; - bool get isPendingRetention => - _state == PersistedSurfaceState.pendingRetention; + bool get isPendingRetention => _state == PersistedSurfaceState.pendingRetention; /// The surface is in the [PersistedSurfaceState.pendingUpdate] state; bool get isPendingUpdate => _state == PersistedSurfaceState.pendingUpdate; @@ -389,12 +398,16 @@ abstract class PersistedSurface implements ui.EngineLayer { @mustCallSuper void adoptElements(covariant PersistedSurface oldSurface) { assert(oldSurface.rootElement != null); - assert(debugAssertSurfaceState(oldSurface, PersistedSurfaceState.active, - PersistedSurfaceState.pendingUpdate)); + assert( + debugAssertSurfaceState( + oldSurface, + PersistedSurfaceState.active, + PersistedSurfaceState.pendingUpdate, + ), + ); assert(() { if (oldSurface.isPendingUpdate) { - final PersistedContainerSurface self = - this as PersistedContainerSurface; + final PersistedContainerSurface self = this as PersistedContainerSurface; assert(identical(self.oldLayer, oldSurface)); } return true; @@ -418,8 +431,13 @@ abstract class PersistedSurface implements ui.EngineLayer { void update(covariant PersistedSurface oldSurface) { assert(!identical(oldSurface, this)); assert(debugAssertSurfaceState(this, PersistedSurfaceState.created)); - assert(debugAssertSurfaceState(oldSurface, PersistedSurfaceState.active, - PersistedSurfaceState.pendingUpdate)); + assert( + debugAssertSurfaceState( + oldSurface, + PersistedSurfaceState.active, + PersistedSurfaceState.pendingUpdate, + ), + ); adoptElements(oldSurface); @@ -494,9 +512,11 @@ abstract class PersistedSurface implements ui.EngineLayer { validationErrors.add('${debugIdentify(this)} has null rootElement.'); } if (!isActive) { - validationErrors.add('${debugIdentify(this)} is in the wrong state.\n' - 'It is in the live DOM tree expectec to be in ${PersistedSurfaceState.active}.\n' - 'However, it is currently in $state.'); + validationErrors.add( + '${debugIdentify(this)} is in the wrong state.\n' + 'It is in the live DOM tree expectec to be in ${PersistedSurfaceState.active}.\n' + 'However, it is currently in $state.', + ); } } @@ -582,8 +602,7 @@ abstract class PersistedSurface implements ui.EngineLayer { buffer.writeln('>'); debugPrintChildren(buffer, indent); if (rootElement != null) { - buffer - .writeln('${' ' * indent}'); + buffer.writeln('${' ' * indent}'); } else { buffer.writeln('${' ' * indent}'); } @@ -641,11 +660,14 @@ abstract class PersistedContainerSurface extends PersistedSurface { /// Adds a child to this container. void appendChild(PersistedSurface child) { - assert(debugAssertSurfaceState( + assert( + debugAssertSurfaceState( child, PersistedSurfaceState.created, PersistedSurfaceState.pendingRetention, - PersistedSurfaceState.pendingUpdate)); + PersistedSurfaceState.pendingUpdate, + ), + ); _children.add(child); child.parent = this; } @@ -681,8 +703,7 @@ abstract class PersistedContainerSurface extends PersistedSurface { } else if (child is PersistedContainerSurface && child.oldLayer != null) { final PersistedSurface oldLayer = child.oldLayer!; assert(oldLayer.rootElement != null); - assert(debugAssertSurfaceState( - oldLayer, PersistedSurfaceState.pendingUpdate)); + assert(debugAssertSurfaceState(oldLayer, PersistedSurfaceState.pendingUpdate)); child.update(oldLayer as PersistedContainerSurface); } else { assert(debugAssertSurfaceState(child, PersistedSurfaceState.created)); @@ -711,8 +732,13 @@ abstract class PersistedContainerSurface extends PersistedSurface { @override void update(PersistedContainerSurface oldSurface) { - assert(debugAssertSurfaceState(oldSurface, PersistedSurfaceState.active, - PersistedSurfaceState.pendingUpdate)); + assert( + debugAssertSurfaceState( + oldSurface, + PersistedSurfaceState.active, + PersistedSurfaceState.pendingUpdate, + ), + ); assert(runtimeType == oldSurface.runtimeType); super.update(oldSurface); assert(debugAssertSurfaceState(oldSurface, PersistedSurfaceState.released)); @@ -744,8 +770,10 @@ abstract class PersistedContainerSurface extends PersistedSurface { assert(() { for (int i = 0; i < oldSurface._children.length; i++) { final PersistedSurface oldChild = oldSurface._children[i]; - assert(!oldChild.isActive && !oldChild.isCreated, - 'Old child is in incorrect state ${oldChild.state}'); + assert( + !oldChild.isActive && !oldChild.isCreated, + 'Old child is in incorrect state ${oldChild.state}', + ); if (oldChild.isReleased) { assert(oldChild.rootElement == null); assert(oldChild.childContainer == null); @@ -761,8 +789,13 @@ abstract class PersistedContainerSurface extends PersistedSurface { for (int i = 0; i < _children.length; i++) { final PersistedSurface newChild = _children[i]; assert(newChild._index == i); - assert(debugAssertSurfaceState(newChild, PersistedSurfaceState.active, - PersistedSurfaceState.pendingRetention)); + assert( + debugAssertSurfaceState( + newChild, + PersistedSurfaceState.active, + PersistedSurfaceState.pendingRetention, + ), + ); assert(newChild.rootElement != null); assert(newChild.rootElement!.parent == childContainer); } @@ -785,17 +818,12 @@ abstract class PersistedContainerSurface extends PersistedSurface { final PersistedSurface newChild = _children[i]; if (newChild.isPendingRetention) { newChild.retain(); - assert(debugAssertSurfaceState( - newChild, PersistedSurfaceState.pendingRetention)); - } else if (newChild is PersistedContainerSurface && - newChild.oldLayer != null) { - final PersistedContainerSurface oldLayer = - newChild.oldLayer! as PersistedContainerSurface; - assert(debugAssertSurfaceState( - oldLayer, PersistedSurfaceState.pendingUpdate)); + assert(debugAssertSurfaceState(newChild, PersistedSurfaceState.pendingRetention)); + } else if (newChild is PersistedContainerSurface && newChild.oldLayer != null) { + final PersistedContainerSurface oldLayer = newChild.oldLayer! as PersistedContainerSurface; + assert(debugAssertSurfaceState(oldLayer, PersistedSurfaceState.pendingUpdate)); newChild.update(oldLayer); - assert( - debugAssertSurfaceState(oldLayer, PersistedSurfaceState.released)); + assert(debugAssertSurfaceState(oldLayer, PersistedSurfaceState.released)); assert(debugAssertSurfaceState(newChild, PersistedSurfaceState.active)); } else { newChild.build(); @@ -841,21 +869,18 @@ abstract class PersistedContainerSurface extends PersistedSurface { newChild.retain(); _discardActiveChildren(oldSurface); - assert(debugAssertSurfaceState( - newChild, PersistedSurfaceState.pendingRetention)); + assert(debugAssertSurfaceState(newChild, PersistedSurfaceState.pendingRetention)); return; } // Updated child is moved to the correct location in the tree; all others // are released. if (newChild is PersistedContainerSurface && newChild.oldLayer != null) { - assert(debugAssertSurfaceState( - newChild.oldLayer!, PersistedSurfaceState.pendingUpdate)); + assert(debugAssertSurfaceState(newChild.oldLayer!, PersistedSurfaceState.pendingUpdate)); assert(newChild.rootElement == null); assert(newChild.oldLayer!.rootElement != null); - final PersistedContainerSurface oldLayer = - newChild.oldLayer! as PersistedContainerSurface; + final PersistedContainerSurface oldLayer = newChild.oldLayer! as PersistedContainerSurface; // Move the HTML node if necessary. if (oldLayer.rootElement!.parent != childContainer) { @@ -894,8 +919,7 @@ abstract class PersistedContainerSurface extends PersistedSurface { childContainer!.append(newChild.rootElement!); } - assert( - debugAssertSurfaceState(bestMatch, PersistedSurfaceState.released)); + assert(debugAssertSurfaceState(bestMatch, PersistedSurfaceState.released)); } else { newChild.build(); childContainer!.append(newChild.rootElement!); @@ -920,8 +944,7 @@ abstract class PersistedContainerSurface extends PersistedSurface { // Memoize container element for efficiency. [childContainer] is polymorphic final DomElement? containerElement = childContainer; - final Map matches = - _matchChildren(oldSurface); + final Map matches = _matchChildren(oldSurface); // This pair of lists maps from _children indices to oldSurface._children indices. // These lists are initialized lazily, only when we discover that we will need to @@ -950,36 +973,26 @@ abstract class PersistedContainerSurface extends PersistedSurface { isReparenting = newChild.rootElement!.parent != containerElement; newChild.retain(); matchedOldChild = newChild; - assert(debugAssertSurfaceState( - newChild, PersistedSurfaceState.pendingRetention)); - } else if (newChild is PersistedContainerSurface && - newChild.oldLayer != null) { - final PersistedContainerSurface oldLayer = - newChild.oldLayer! as PersistedContainerSurface; + assert(debugAssertSurfaceState(newChild, PersistedSurfaceState.pendingRetention)); + } else if (newChild is PersistedContainerSurface && newChild.oldLayer != null) { + final PersistedContainerSurface oldLayer = newChild.oldLayer! as PersistedContainerSurface; isReparenting = oldLayer.rootElement!.parent != containerElement; matchedOldChild = oldLayer; - assert(debugAssertSurfaceState( - oldLayer, PersistedSurfaceState.pendingUpdate)); + assert(debugAssertSurfaceState(oldLayer, PersistedSurfaceState.pendingUpdate)); newChild.update(oldLayer); - assert( - debugAssertSurfaceState(oldLayer, PersistedSurfaceState.released)); + assert(debugAssertSurfaceState(oldLayer, PersistedSurfaceState.released)); assert(debugAssertSurfaceState(newChild, PersistedSurfaceState.active)); } else { matchedOldChild = matches[newChild]; if (matchedOldChild != null) { - assert(debugAssertSurfaceState( - matchedOldChild, PersistedSurfaceState.active)); - isReparenting = - matchedOldChild.rootElement!.parent != containerElement; + assert(debugAssertSurfaceState(matchedOldChild, PersistedSurfaceState.active)); + isReparenting = matchedOldChild.rootElement!.parent != containerElement; newChild.update(matchedOldChild); - assert(debugAssertSurfaceState( - matchedOldChild, PersistedSurfaceState.released)); - assert( - debugAssertSurfaceState(newChild, PersistedSurfaceState.active)); + assert(debugAssertSurfaceState(matchedOldChild, PersistedSurfaceState.released)); + assert(debugAssertSurfaceState(newChild, PersistedSurfaceState.active)); } else { newChild.build(); - assert( - debugAssertSurfaceState(newChild, PersistedSurfaceState.active)); + assert(debugAssertSurfaceState(newChild, PersistedSurfaceState.active)); } } @@ -1015,8 +1028,13 @@ abstract class PersistedContainerSurface extends PersistedSurface { newChild._index = topInNew; assert(newChild.rootElement != null); - assert(debugAssertSurfaceState(newChild, PersistedSurfaceState.active, - PersistedSurfaceState.pendingRetention)); + assert( + debugAssertSurfaceState( + newChild, + PersistedSurfaceState.active, + PersistedSurfaceState.pendingRetention, + ), + ); } // Avoid calling `_insertChildDomNodes` unnecessarily. Only call it if we @@ -1038,8 +1056,7 @@ abstract class PersistedContainerSurface extends PersistedSurface { /// Performs the minimum number of DOM moves necessary to put all children in /// the right place in the DOM. void _insertChildDomNodes(List? indexMapNew, List indexMapOld) { - final List stationaryIndices = - longestIncreasingSubsequence(indexMapOld); + final List stationaryIndices = longestIncreasingSubsequence(indexMapOld); // Convert to stationary new indices for (int i = 0; i < stationaryIndices.length; i++) { @@ -1050,11 +1067,9 @@ abstract class PersistedContainerSurface extends PersistedSurface { final DomElement? containerElement = childContainer; for (int i = _children.length - 1; i >= 0; i -= 1) { final int indexInNew = indexMapNew!.indexOf(i); - final bool isStationary = - indexInNew != -1 && stationaryIndices.contains(i); + final bool isStationary = indexInNew != -1 && stationaryIndices.contains(i); final PersistedSurface child = _children[i]; - final DomHTMLElement childElement = - child.rootElement! as DomHTMLElement; + final DomHTMLElement childElement = child.rootElement! as DomHTMLElement; if (!isStationary) { if (refNode == null) { containerElement!.append(childElement); @@ -1067,8 +1082,7 @@ abstract class PersistedContainerSurface extends PersistedSurface { } } - Map _matchChildren( - PersistedContainerSurface oldSurface) { + Map _matchChildren(PersistedContainerSurface oldSurface) { final int newUnfilteredChildCount = _children.length; final int oldUnfilteredChildCount = oldSurface._children.length; @@ -1116,11 +1130,13 @@ abstract class PersistedContainerSurface extends PersistedSurface { if (childAlreadyClaimed || !newChild.canUpdateAsMatch(oldChild)) { continue; } - allMatches.add(_PersistedSurfaceMatch( - matchQuality: newChild.matchForUpdate(oldChild), - newChild: newChild, - oldChildIndex: indexInOld, - )); + allMatches.add( + _PersistedSurfaceMatch( + matchQuality: newChild.matchForUpdate(oldChild), + newChild: newChild, + oldChildIndex: indexInOld, + ), + ); } } @@ -1128,8 +1144,7 @@ abstract class PersistedContainerSurface extends PersistedSurface { return m1.matchQuality!.compareTo(m2.matchQuality!); }); - final Map result = - {}; + final Map result = {}; for (int i = 0; i < allMatches.length; i += 1) { final _PersistedSurfaceMatch match = allMatches[i]; // This may be null if it has been claimed. @@ -1178,7 +1193,8 @@ abstract class PersistedContainerSurface extends PersistedSurface { final PersistedSurface child = _children[i]; if (child.parent != this) { validationErrors.add( - '${debugIdentify(child)} parent is ${debugIdentify(child.parent)} but expected the parent to be ${debugIdentify(this)}'); + '${debugIdentify(child)} parent is ${debugIdentify(child.parent)} but expected the parent to be ${debugIdentify(this)}', + ); } } for (int i = 0; i < _children.length; i++) { @@ -1197,11 +1213,7 @@ abstract class PersistedContainerSurface extends PersistedSurface { /// A custom class used by [PersistedContainerSurface._matchChildren]. class _PersistedSurfaceMatch { - _PersistedSurfaceMatch({ - this.newChild, - this.oldChildIndex, - this.matchQuality, - }); + _PersistedSurfaceMatch({this.newChild, this.oldChildIndex, this.matchQuality}); /// The child in the new scene who we are trying to match to an existing /// child. @@ -1222,7 +1234,8 @@ class _PersistedSurfaceMatch { String toString() { String result = super.toString(); assert(() { - result = '_PersistedSurfaceMatch(${newChild!.runtimeType}#${newChild!.hashCode}: $oldChildIndex, quality: $matchQuality)'; + result = + '_PersistedSurfaceMatch(${newChild!.runtimeType}#${newChild!.hashCode}: $oldChildIndex, quality: $matchQuality)'; return true; }()); return result; @@ -1235,6 +1248,7 @@ class _PersistedSurfaceMatch { class PrerollSurfaceContext { /// Number of active color filters in parent surfaces. int activeColorFilterCount = 0; + /// Number of active shader masks in parent surfaces. int activeShaderMaskCount = 0; } diff --git a/lib/web_ui/lib/src/engine/html/surface_stats.dart b/lib/web_ui/lib/src/engine/html/surface_stats.dart index 62c284f67c4cf..c0da27a7eb2f7 100644 --- a/lib/web_ui/lib/src/engine/html/surface_stats.dart +++ b/lib/web_ui/lib/src/engine/html/surface_stats.dart @@ -19,8 +19,7 @@ import 'surface.dart'; List retainedSurfaces = []; /// Maps every surface currently active on the screen to debug statistics. -Map surfaceStats = - {}; +Map surfaceStats = {}; List> _surfaceStatsTimeline = >[]; @@ -29,7 +28,8 @@ List> _surfaceStatsTimeline = DebugSurfaceStats surfaceStatsFor(PersistedSurface surface) { if (!debugExplainSurfaceStats) { throw Exception( - 'surfaceStatsFor is only available when debugExplainSurfaceStats is set to true.'); + 'surfaceStatsFor is only available when debugExplainSurfaceStats is set to true.', + ); } return surfaceStats.putIfAbsent(surface, () => DebugSurfaceStats(surface)); } @@ -134,14 +134,12 @@ void debugRepaintSurfaceStatsOverlay(PersistedScene scene) { domWindow.innerWidth! * EngineFlutterDisplay.instance.browserDevicePixelRatio; final double physicalScreenHeight = domWindow.innerHeight! * EngineFlutterDisplay.instance.browserDevicePixelRatio; - final double physicsScreenPixelCount = - physicalScreenWidth * physicalScreenHeight; + final double physicsScreenPixelCount = physicalScreenWidth * physicalScreenHeight; final int totalDomNodeCount = scene.rootElement!.querySelectorAll('*').length; for (int i = 0; i < _surfaceStatsTimeline.length; i++) { - final Map statsMap = - _surfaceStatsTimeline[i]; + final Map statsMap = _surfaceStatsTimeline[i]; final DebugSurfaceStats totals = DebugSurfaceStats(null); int pixelCount = 0; for (final DebugSurfaceStats oneSurfaceStats in statsMap.values) { @@ -153,12 +151,10 @@ void debugRepaintSurfaceStatsOverlay(PersistedScene scene) { } final double repaintRate = totals.paintPixelCount / pixelCount; - final double domAllocationRate = - totals.allocatedDomNodeCount / totalDomNodeCount; + final double domAllocationRate = totals.allocatedDomNodeCount / totalDomNodeCount; final double bitmapAllocationRate = totals.allocatedBitmapSizeInPixels / physicsScreenPixelCount; - final double surfaceRetainRate = - totals.retainSurfaceCount / _surfaceStatsTimeline[i].length; + final double surfaceRetainRate = totals.retainSurfaceCount / _surfaceStatsTimeline[i].length; // Repaints _debugSurfaceStatsOverlayCtx! @@ -268,8 +264,7 @@ void debugPrintSurfaceStats(PersistedScene scene, int frameNumber) { final StringBuffer buf = StringBuffer(); buf - ..writeln( - '---------------------- FRAME #$frameNumber -------------------------') + ..writeln('---------------------- FRAME #$frameNumber -------------------------') ..writeln('Surfaces retained: $surfaceRetainCount') ..writeln('Elements reused: $elementReuseCount') ..writeln('Elements allocated: $totalAllocatedDomNodeCount') @@ -295,16 +290,16 @@ void debugPrintSurfaceStats(PersistedScene scene, int frameNumber) { final int pixelCount = canvasElements .cast() .map((DomCanvasElement e) { - final int pixels = (e.width! * e.height!).toInt(); - canvasInfo.writeln(' - ${e.width!} x ${e.height!} = $pixels pixels'); - return pixels; - }).fold(0, (int total, int pixels) => total + pixels); + final int pixels = (e.width! * e.height!).toInt(); + canvasInfo.writeln(' - ${e.width!} x ${e.height!} = $pixels pixels'); + return pixels; + }) + .fold(0, (int total, int pixels) => total + pixels); final double physicalScreenWidth = domWindow.innerWidth! * EngineFlutterDisplay.instance.browserDevicePixelRatio; final double physicalScreenHeight = domWindow.innerHeight! * EngineFlutterDisplay.instance.browserDevicePixelRatio; - final double physicsScreenPixelCount = - physicalScreenWidth * physicalScreenHeight; + final double physicsScreenPixelCount = physicalScreenWidth * physicalScreenHeight; final double screenPixelRatio = pixelCount / physicsScreenPixelCount; final String screenDescription = '1 screen is $physicalScreenWidth x $physicalScreenHeight = $physicsScreenPixelCount pixels'; @@ -315,11 +310,9 @@ void debugPrintSurfaceStats(PersistedScene scene, int frameNumber) { ..writeln(canvasInfo) ..writeln(' Pixels: $canvasPixelDescription; $screenDescription)') ..writeln('-----------------------------------------------------------'); - final bool screenPixelRatioTooHigh = - screenPixelRatio > kScreenPixelRatioWarningThreshold; + final bool screenPixelRatioTooHigh = screenPixelRatio > kScreenPixelRatioWarningThreshold; if (screenPixelRatioTooHigh) { - print( - 'WARNING: pixel/screen ratio too high (${screenPixelRatio.toStringAsFixed(2)}x)'); + print('WARNING: pixel/screen ratio too high (${screenPixelRatio.toStringAsFixed(2)}x)'); } print(buf); }); diff --git a/lib/web_ui/lib/src/engine/html/transform.dart b/lib/web_ui/lib/src/engine/html/transform.dart index ba8759d46fec0..706a83f720710 100644 --- a/lib/web_ui/lib/src/engine/html/transform.dart +++ b/lib/web_ui/lib/src/engine/html/transform.dart @@ -12,8 +12,7 @@ import '../vector_math.dart'; import 'surface.dart'; /// A surface that transforms its children using CSS transform. -class PersistedTransform extends PersistedContainerSurface - implements ui.TransformEngineLayer { +class PersistedTransform extends PersistedContainerSurface implements ui.TransformEngineLayer { PersistedTransform(PersistedTransform? super.oldLayer, this._matrixStorage); /// The storage representing the transform of this surface. diff --git a/lib/web_ui/lib/src/engine/html_image_element_codec.dart b/lib/web_ui/lib/src/engine/html_image_element_codec.dart index 9784345e999f3..2be5e5fa8b978 100644 --- a/lib/web_ui/lib/src/engine/html_image_element_codec.dart +++ b/lib/web_ui/lib/src/engine/html_image_element_codec.dart @@ -50,16 +50,18 @@ abstract class HtmlImageElementCodec implements ui.Codec { ..decoding = 'async' ..src = src; - // Ignoring the returned future on purpose because we're communicating // through the `completer`. // ignore: unawaited_futures - imgElement!.decode().then((dynamic _) { - chunkCallback?.call(100, 100); - completer.complete(); - }).catchError((dynamic e) { - completer.completeError(e.toString()); - }); + imgElement! + .decode() + .then((dynamic _) { + chunkCallback?.call(100, 100); + completer.complete(); + }) + .catchError((dynamic e) { + completer.completeError(e.toString()); + }); return completer.future; } @@ -97,10 +99,7 @@ abstract class HtmlImageElementCodec implements ui.Codec { abstract class HtmlBlobCodec extends HtmlImageElementCodec { HtmlBlobCodec(this.blob, {super.chunkCallback}) - : super( - domWindow.URL.createObjectURL(blob), - debugSource: 'encoded image bytes', - ); + : super(domWindow.URL.createObjectURL(blob), debugSource: 'encoded image bytes'); final DomBlob blob; diff --git a/lib/web_ui/lib/src/engine/image_decoder.dart b/lib/web_ui/lib/src/engine/image_decoder.dart index b8f2963ddac91..40e38cdf68b07 100644 --- a/lib/web_ui/lib/src/engine/image_decoder.dart +++ b/lib/web_ui/lib/src/engine/image_decoder.dart @@ -59,8 +59,7 @@ abstract class BrowserImageDecoder implements ui.Codec { } void _debugCheckNotDisposed() { - assert(!_isDisposed, - 'Cannot use this image decoder. It has been disposed of.'); + assert(!_isDisposed, 'Cannot use this image decoder. It has been disposed of.'); } /// The index of the frame that will be decoded on the next call of [getNextFrame]; @@ -92,8 +91,7 @@ abstract class BrowserImageDecoder implements ui.Codec { if (_cachedWebDecoder != null) { // Give the cached value some time for reuse, e.g. if the image is // currently animating. - _cacheExpirationClock.datetime = - DateTime.now().add(_kWebDecoderExpireDuration); + _cacheExpirationClock.datetime = DateTime.now().add(_kWebDecoderExpireDuration); return _cachedWebDecoder!; } @@ -102,20 +100,22 @@ abstract class BrowserImageDecoder implements ui.Codec { // initialization will take place. We just let it proceed at its own pace. _cacheExpirationClock.callback = null; try { - final ImageDecoder webDecoder = ImageDecoder(ImageDecoderOptions( - type: contentType.toJS, - data: dataSource, - - // Flutter always uses premultiplied alpha when decoding. - premultiplyAlpha: 'premultiply'.toJS, - // "default" gives the browser the liberty to convert to display-appropriate - // color space, typically SRGB, which is what we want. - colorSpaceConversion: 'default'.toJS, - - // Flutter doesn't give the developer a way to customize this, so if this - // is an animated image we should prefer the animated track. - preferAnimation: true.toJS, - )); + final ImageDecoder webDecoder = ImageDecoder( + ImageDecoderOptions( + type: contentType.toJS, + data: dataSource, + + // Flutter always uses premultiplied alpha when decoding. + premultiplyAlpha: 'premultiply'.toJS, + // "default" gives the browser the liberty to convert to display-appropriate + // color space, typically SRGB, which is what we want. + colorSpaceConversion: 'default'.toJS, + + // Flutter doesn't give the developer a way to customize this, so if this + // is an animated image we should prefer the animated track. + preferAnimation: true.toJS, + ), + ); await promiseToFuture(webDecoder.tracks.ready); @@ -129,11 +129,8 @@ abstract class BrowserImageDecoder implements ui.Codec { // We coerce the DOM's `repetitionCount` into an int by explicitly // handling `infinity`. Note: This will still throw if the DOM returns a // `NaN`. - final double rawRepetitionCount = - webDecoder.tracks.selectedTrack!.repetitionCount; - repetitionCount = rawRepetitionCount == double.infinity - ? -1 - : rawRepetitionCount.toInt(); + final double rawRepetitionCount = webDecoder.tracks.selectedTrack!.repetitionCount; + repetitionCount = rawRepetitionCount == double.infinity ? -1 : rawRepetitionCount.toInt(); _cachedWebDecoder = webDecoder; // Expire the decoder if it's not used for several seconds. If the image is @@ -150,8 +147,7 @@ abstract class BrowserImageDecoder implements ui.Codec { _cachedWebDecoder = null; _cacheExpirationClock.callback = null; }; - _cacheExpirationClock.datetime = - DateTime.now().add(_kWebDecoderExpireDuration); + _cacheExpirationClock.datetime = DateTime.now().add(_kWebDecoderExpireDuration); return webDecoder; } catch (error) { @@ -164,9 +160,10 @@ abstract class BrowserImageDecoder implements ui.Codec { } } throw ImageCodecException( - "Failed to decode image using the browser's ImageDecoder API.\n" - 'Image source: $debugSource\n' - 'Original browser error: $error'); + "Failed to decode image using the browser's ImageDecoder API.\n" + 'Image source: $debugSource\n' + 'Original browser error: $error', + ); } } @@ -183,8 +180,7 @@ abstract class BrowserImageDecoder implements ui.Codec { // Duration can be null if the image is not animated. However, Flutter // requires a non-null value. 0 indicates that the frame is meant to be // displayed indefinitely, which is fine for a static image. - final Duration duration = - Duration(microseconds: frame.duration?.toInt() ?? 0); + final Duration duration = Duration(microseconds: frame.duration?.toInt() ?? 0); final ui.Image image = generateImageFromVideoFrame(frame); return AnimatedImageFrameInfo(duration, image); } @@ -208,12 +204,7 @@ class AnimatedImageFrameInfo implements ui.FrameInfo { // Wraps another codec and resizes each output image. class ResizingCodec implements ui.Codec { - ResizingCodec( - this.delegate, { - this.targetWidth, - this.targetHeight, - this.allowUpscaling = true, - }); + ResizingCodec(this.delegate, {this.targetWidth, this.targetHeight, this.allowUpscaling = true}); final ui.Codec delegate; final int? targetWidth; @@ -245,24 +236,18 @@ class ResizingCodec implements ui.Codec { int? targetWidth, int? targetHeight, bool allowUpscaling = true, - }) => - scaleImageIfNeeded( - image, - targetWidth: targetWidth, - targetHeight: targetHeight, - allowUpscaling: allowUpscaling, - ); + }) => scaleImageIfNeeded( + image, + targetWidth: targetWidth, + targetHeight: targetHeight, + allowUpscaling: allowUpscaling, + ); @override int get repetitionCount => delegate.frameCount; } -BitmapSize? scaledImageSize( - int width, - int height, - int? targetWidth, - int? targetHeight, -) { +BitmapSize? scaledImageSize(int width, int height, int? targetWidth, int? targetHeight) { if (targetWidth == width && targetHeight == height) { // Not scaled return null; @@ -291,18 +276,20 @@ ui.Image scaleImageIfNeeded( }) { final int width = image.width; final int height = image.height; - final BitmapSize? scaledSize = - scaledImageSize(width, height, targetWidth, targetHeight); + final BitmapSize? scaledSize = scaledImageSize(width, height, targetWidth, targetHeight); if (scaledSize == null) { return image; } - if (!allowUpscaling && - (scaledSize.width > width || scaledSize.height > height)) { + if (!allowUpscaling && (scaledSize.width > width || scaledSize.height > height)) { return image; } final ui.Rect outputRect = ui.Rect.fromLTWH( - 0, 0, scaledSize.width.toDouble(), scaledSize.height.toDouble()); + 0, + 0, + scaledSize.width.toDouble(), + scaledSize.height.toDouble(), + ); final ui.PictureRecorder recorder = ui.PictureRecorder(); final ui.Canvas canvas = ui.Canvas(recorder, outputRect); @@ -313,8 +300,7 @@ ui.Image scaleImageIfNeeded( ui.Paint(), ); final ui.Picture picture = recorder.endRecording(); - final ui.Image finalImage = - picture.toImageSync(scaledSize.width, scaledSize.height); + final ui.Image finalImage = picture.toImageSync(scaledSize.width, scaledSize.height); picture.dispose(); image.dispose(); return finalImage; diff --git a/lib/web_ui/lib/src/engine/image_format_detector.dart b/lib/web_ui/lib/src/engine/image_format_detector.dart index 335aabf0ebd6b..514a2649ae54a 100644 --- a/lib/web_ui/lib/src/engine/image_format_detector.dart +++ b/lib/web_ui/lib/src/engine/image_format_detector.dart @@ -61,14 +61,7 @@ ImageType? detectImageType(Uint8List data) { } /// The supported image file formats in Flutter Web. -enum ImageFileType { - png, - gif, - jpeg, - webp, - bmp, - avif, -} +enum ImageFileType { png, gif, jpeg, webp, bmp, avif } /// The file format of the image, and whether or not it is animated. enum ImageType { @@ -96,43 +89,25 @@ enum ImageType { /// The signature bytes in an image file that identify the format. enum ImageFileSignature { - png( - [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A], - ImageType.png, - ), - gif87a( - [0x47, 0x49, 0x46, 0x38, 0x37, 0x61], - ImageType.animatedGif, - ), - gif89a( - [0x47, 0x49, 0x46, 0x38, 0x39, 0x61], - ImageType.animatedGif, - ), - jpeg( - [0xFF, 0xD8, 0xFF], - ImageType.jpeg, - ), - webp( - [ - 0x52, - 0x49, - 0x46, - 0x46, - null, - null, - null, - null, - 0x57, - 0x45, - 0x42, - 0x50 - ], - ImageType.webp, - ), - bmp( - [0x42, 0x4D], - ImageType.bmp, - ); + png([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A], ImageType.png), + gif87a([0x47, 0x49, 0x46, 0x38, 0x37, 0x61], ImageType.animatedGif), + gif89a([0x47, 0x49, 0x46, 0x38, 0x39, 0x61], ImageType.animatedGif), + jpeg([0xFF, 0xD8, 0xFF], ImageType.jpeg), + webp([ + 0x52, + 0x49, + 0x46, + 0x46, + null, + null, + null, + null, + 0x57, + 0x45, + 0x42, + 0x50, + ], ImageType.webp), + bmp([0x42, 0x4D], ImageType.bmp); const ImageFileSignature(this.header, this.imageType); @@ -290,8 +265,7 @@ class _GifHeaderReader { final int logicalScreenDescriptorFields = _readUint8(); const int globalColorTableFlagMask = 1 << 7; - final bool hasGlobalColorTable = - logicalScreenDescriptorFields & globalColorTableFlagMask != 0; + final bool hasGlobalColorTable = logicalScreenDescriptorFields & globalColorTableFlagMask != 0; // Skip over the background color index and pixel aspect ratio. _position += 2; @@ -299,11 +273,9 @@ class _GifHeaderReader { if (hasGlobalColorTable) { // Skip past the global color table. const int globalColorTableSizeMask = 1 << 2 | 1 << 1 | 1; - final int globalColorTableSize = - logicalScreenDescriptorFields & globalColorTableSizeMask; + final int globalColorTableSize = logicalScreenDescriptorFields & globalColorTableSizeMask; // This is 3 * 2^(Global Color Table Size + 1). - final int globalColorTableSizeInBytes = - 3 * (1 << (globalColorTableSize + 1)); + final int globalColorTableSizeInBytes = 3 * (1 << (globalColorTableSize + 1)); _position += globalColorTableSizeInBytes; } @@ -411,16 +383,13 @@ class _GifHeaderReader { final int packedImageDescriptorFields = _readUint8(); const int localColorTableFlagMask = 1 << 7; - final bool hasLocalColorTable = - packedImageDescriptorFields & localColorTableFlagMask != 0; + final bool hasLocalColorTable = packedImageDescriptorFields & localColorTableFlagMask != 0; if (hasLocalColorTable) { // Skip past the local color table. const int localColorTableSizeMask = 1 << 2 | 1 << 1 | 1; - final int localColorTableSize = - packedImageDescriptorFields & localColorTableSizeMask; + final int localColorTableSize = packedImageDescriptorFields & localColorTableSizeMask; // This is 3 * 2^(Local Color Table Size + 1). - final int localColorTableSizeInBytes = - 3 * (1 << (localColorTableSize + 1)); + final int localColorTableSizeInBytes = 3 * (1 << (localColorTableSize + 1)); _position += localColorTableSizeInBytes; } // Skip LZW minimum code size byte. diff --git a/lib/web_ui/lib/src/engine/initialization.dart b/lib/web_ui/lib/src/engine/initialization.dart index 9fddd5544bf6d..1bb537c56ea70 100644 --- a/lib/web_ui/lib/src/engine/initialization.dart +++ b/lib/web_ui/lib/src/engine/initialization.dart @@ -13,17 +13,19 @@ import 'package:web_test_fonts/web_test_fonts.dart'; /// The mode the app is running in. /// Keep these in sync with the same constants on the framework-side under foundation/constants.dart. -const bool kReleaseMode = - bool.fromEnvironment('dart.vm.product'); +const bool kReleaseMode = bool.fromEnvironment('dart.vm.product'); + /// A constant that is true if the application was compiled in profile mode. -const bool kProfileMode = - bool.fromEnvironment('dart.vm.profile'); +const bool kProfileMode = bool.fromEnvironment('dart.vm.profile'); + /// A constant that is true if the application was compiled in debug mode. const bool kDebugMode = !kReleaseMode && !kProfileMode; + /// Returns mode of the app is running in as a string. -String get buildMode => kReleaseMode - ? 'release' - : kProfileMode +String get buildMode => + kReleaseMode + ? 'release' + : kProfileMode ? 'profile' : 'debug'; @@ -115,14 +117,14 @@ void debugResetEngineInitializationState() { /// puts UI elements on the page. Future initializeEngineServices({ ui_web.AssetManager? assetManager, - JsFlutterConfiguration? jsConfiguration + JsFlutterConfiguration? jsConfiguration, }) async { if (_initializationState != DebugEngineInitializationState.uninitialized) { assert(() { throw StateError( 'Invalid engine initialization state. `initializeEngineServices` was ' 'called, but the engine has already started initialization and is ' - 'currently in state "$_initializationState".' + 'currently in state "$_initializationState".', ); }()); return; @@ -144,7 +146,8 @@ Future initializeEngineServices({ listener(); } return Future.value( - developer.ServiceExtensionResponse.result('OK')); + developer.ServiceExtensionResponse.result('OK'), + ); }); if (Profiler.isBenchmarkMode) { @@ -176,12 +179,12 @@ Future initializeEngineServices({ // milliseconds as a double value, with sub-millisecond information // hidden in the fraction. So we first multiply it by 1000 to uncover // microsecond precision, and only then convert to `int`. - final int highResTimeMicroseconds = - (1000 * highResTime.toDartDouble).toInt(); + final int highResTimeMicroseconds = (1000 * highResTime.toDartDouble).toInt(); if (EnginePlatformDispatcher.instance.onBeginFrame != null) { EnginePlatformDispatcher.instance.invokeOnBeginFrame( - Duration(microseconds: highResTimeMicroseconds)); + Duration(microseconds: highResTimeMicroseconds), + ); } if (EnginePlatformDispatcher.instance.onDrawFrame != null) { @@ -199,7 +202,7 @@ Future initializeEngineServices({ assetManager ??= ui_web.AssetManager(assetBase: configuration.assetBase); _setAssetManager(assetManager); - Future initializeRendererCallback () async => renderer.initialize(); + Future initializeRendererCallback() async => renderer.initialize(); await Future.wait(>[initializeRendererCallback(), _downloadAssetFonts()]); _initializationState = DebugEngineInitializationState.initializedServices; } @@ -219,7 +222,7 @@ Future initializeEngineUi() async { 'called while the engine initialization state was ' '"$_initializationState". `initializeEngineUi` can only be called ' 'when the engine is in state ' - '"${DebugEngineInitializationState.initializedServices}".' + '"${DebugEngineInitializationState.initializedServices}".', ); }()); return; @@ -233,8 +236,9 @@ Future initializeEngineUi() async { ensureMetaTag('generator', 'Flutter'); if (!configuration.multiViewEnabled) { - final EngineFlutterWindow implicitView = - ensureImplicitViewInitialized(hostElement: configuration.hostElement); + final EngineFlutterWindow implicitView = ensureImplicitViewInitialized( + hostElement: configuration.hostElement, + ); if (renderer is HtmlRenderer) { ensureResourceManagerInitialized(implicitView); } @@ -264,7 +268,7 @@ Future _downloadAssetFonts() async { // the embedded test font is the default (first) font. await renderer.fontCollection.loadFontFromList( EmbeddedTestFont.flutterTest.data, - fontFamily: EmbeddedTestFont.flutterTest.fontFamily + fontFamily: EmbeddedTestFont.flutterTest.fontFamily, ); } @@ -284,4 +288,5 @@ bool get debugDisableFontFallbacks => _debugDisableFontFallbacks; set debugDisableFontFallbacks(bool value) { _debugDisableFontFallbacks = value; } + bool _debugDisableFontFallbacks = false; diff --git a/lib/web_ui/lib/src/engine/js_interop/js_app.dart b/lib/web_ui/lib/src/engine/js_interop/js_app.dart index 959abb059624d..42be1959028ab 100644 --- a/lib/web_ui/lib/src/engine/js_interop/js_app.dart +++ b/lib/web_ui/lib/src/engine/js_interop/js_app.dart @@ -21,7 +21,7 @@ extension JsFlutterViewOptionsExtension on JsFlutterViewOptions { @JS('hostElement') external DomElement? get _hostElement; DomElement get hostElement { - assert (_hostElement != null, '`hostElement` passed to addView cannot be null.'); + assert(_hostElement != null, '`hostElement` passed to addView cannot be null.'); return _hostElement!; } @@ -65,15 +65,11 @@ abstract class FlutterApp { factory FlutterApp({ required AddFlutterViewFn addView, required RemoveFlutterViewFn removeView, - }) => - FlutterApp._( - addView: addView.toJS, - removeView: ((JSNumber id) => removeView(id.toDartInt)).toJS, - ); - external factory FlutterApp._({ - required JSFunction addView, - required JSFunction removeView, - }); + }) => FlutterApp._( + addView: addView.toJS, + removeView: ((JSNumber id) => removeView(id.toDartInt)).toJS, + ); + external factory FlutterApp._({required JSFunction addView, required JSFunction removeView}); } /// Typedef for the function that adds a new view to the app. diff --git a/lib/web_ui/lib/src/engine/js_interop/js_loader.dart b/lib/web_ui/lib/src/engine/js_interop/js_loader.dart index ab625fbc9b419..a0755a8e73894 100644 --- a/lib/web_ui/lib/src/engine/js_interop/js_loader.dart +++ b/lib/web_ui/lib/src/engine/js_interop/js_loader.dart @@ -52,14 +52,17 @@ typedef ImmediateRunAppFn = Future Function(); @JS() @anonymous @staticInterop -abstract class FlutterEngineInitializer{ +abstract class FlutterEngineInitializer { factory FlutterEngineInitializer({ required InitializeEngineFn initializeEngine, required ImmediateRunAppFn autoStart, }) => FlutterEngineInitializer._( - initializeEngine: (([JsFlutterConfiguration? config]) => (initializeEngine(config) as Future).toPromise).toJS, - autoStart: (() => (autoStart() as Future).toPromise).toJS, - ); + initializeEngine: + (([JsFlutterConfiguration? config]) => + (initializeEngine(config) as Future).toPromise) + .toJS, + autoStart: (() => (autoStart() as Future).toPromise).toJS, + ); external factory FlutterEngineInitializer._({ required JSFunction initializeEngine, required JSFunction autoStart, @@ -74,8 +77,8 @@ abstract class FlutterEngineInitializer{ @anonymous @staticInterop abstract class FlutterAppRunner { - factory FlutterAppRunner({required RunAppFn runApp,}) => FlutterAppRunner._( - runApp: (([RunAppFnParameters? args]) => (runApp(args) as Future).toPromise).toJS + factory FlutterAppRunner({required RunAppFn runApp}) => FlutterAppRunner._( + runApp: (([RunAppFnParameters? args]) => (runApp(args) as Future).toPromise).toJS, ); /// Runs a flutter app @@ -89,8 +92,7 @@ abstract class FlutterAppRunner { @JS() @anonymous @staticInterop -abstract class RunAppFnParameters { -} +abstract class RunAppFnParameters {} /// Typedef for the function that runs the flutter app main entrypoint. typedef RunAppFn = Future Function([RunAppFnParameters?]); diff --git a/lib/web_ui/lib/src/engine/js_interop/js_promise.dart b/lib/web_ui/lib/src/engine/js_interop/js_promise.dart index 598b2f41e7e7d..c84a0d05eb887 100644 --- a/lib/web_ui/lib/src/engine/js_interop/js_promise.dart +++ b/lib/web_ui/lib/src/engine/js_interop/js_promise.dart @@ -15,21 +15,25 @@ extension CustomFutureOfJSAnyToJSPromise on Future { /// [Future] or rejects with an object that contains its error. JSPromise get toPromise { // TODO(ditman): Move to js_interop's .toJS, https://github.com/dart-lang/sdk/issues/56898 - return JSPromise((JSFunction resolve, JSFunction reject) { - then((JSAny? value) { - resolve.callAsFunction(resolve, value); - }, onError: (Object error, StackTrace stackTrace) { - final errorConstructor = globalContext['Error']! as JSFunction; - var userError = '$error\n'; - // Only append the stack trace string if it looks like a DDC one... - final stackTraceString = stackTrace.toString(); - if (!stackTraceString.startsWith('\n')) { - userError += '\nDart stack trace:\n$stackTraceString'; - } - final wrapper = - errorConstructor.callAsConstructor(userError.toJS); - reject.callAsFunction(reject, wrapper); - }); - }.toJS); + return JSPromise( + (JSFunction resolve, JSFunction reject) { + then( + (JSAny? value) { + resolve.callAsFunction(resolve, value); + }, + onError: (Object error, StackTrace stackTrace) { + final errorConstructor = globalContext['Error']! as JSFunction; + var userError = '$error\n'; + // Only append the stack trace string if it looks like a DDC one... + final stackTraceString = stackTrace.toString(); + if (!stackTraceString.startsWith('\n')) { + userError += '\nDart stack trace:\n$stackTraceString'; + } + final wrapper = errorConstructor.callAsConstructor(userError.toJS); + reject.callAsFunction(reject, wrapper); + }, + ); + }.toJS, + ); } } diff --git a/lib/web_ui/lib/src/engine/js_interop/js_typed_data.dart b/lib/web_ui/lib/src/engine/js_interop/js_typed_data.dart index f82ce73af0998..f1150dfb38218 100644 --- a/lib/web_ui/lib/src/engine/js_interop/js_typed_data.dart +++ b/lib/web_ui/lib/src/engine/js_interop/js_typed_data.dart @@ -17,21 +17,14 @@ extension TypedArrayExtension on JSTypedArray { @staticInterop class JSUint8Array1 { external factory JSUint8Array1._create1(JSAny bufferOrLength); - external factory JSUint8Array1._create3( - JSArrayBuffer buffer, - JSNumber start, - JSNumber length, - ); + external factory JSUint8Array1._create3(JSArrayBuffer buffer, JSNumber start, JSNumber length); } -JSUint8Array createUint8ArrayFromBuffer(JSArrayBuffer buffer) - => JSUint8Array1._create1(buffer) as JSUint8Array; +JSUint8Array createUint8ArrayFromBuffer(JSArrayBuffer buffer) => + JSUint8Array1._create1(buffer) as JSUint8Array; -JSUint8Array createUint8ArrayFromSubBuffer( - JSArrayBuffer buffer, - int start, - int length, -) => JSUint8Array1._create3(buffer, start.toJS, length.toJS) as JSUint8Array; +JSUint8Array createUint8ArrayFromSubBuffer(JSArrayBuffer buffer, int start, int length) => + JSUint8Array1._create3(buffer, start.toJS, length.toJS) as JSUint8Array; -JSUint8Array createUint8ArrayFromLength(int length) - => JSUint8Array1._create1(length.toJS) as JSUint8Array; +JSUint8Array createUint8ArrayFromLength(int length) => + JSUint8Array1._create1(length.toJS) as JSUint8Array; diff --git a/lib/web_ui/lib/src/engine/key_map.g.dart b/lib/web_ui/lib/src/engine/key_map.g.dart index d4cecae4e6bc0..6383650911dc2 100644 --- a/lib/web_ui/lib/src/engine/key_map.g.dart +++ b/lib/web_ui/lib/src/engine/key_map.g.dart @@ -573,21 +573,41 @@ const Map> kWebLogicalLocationMap = >{ '7': [0x00000000037, null, null, 0x00200000237], // digit7, null, null, numpad7 '8': [0x00000000038, null, null, 0x00200000238], // digit8, null, null, numpad8 '9': [0x00000000039, null, null, 0x00200000239], // digit9, null, null, numpad9 - 'Alt': [0x00200000104, 0x00200000104, 0x00200000105, null], // altLeft, altLeft, altRight, null + 'Alt': [ + 0x00200000104, + 0x00200000104, + 0x00200000105, + null, + ], // altLeft, altLeft, altRight, null 'AltGraph': [0x00100000103, null, 0x00100000103, null], // altGraph, null, altGraph, null 'ArrowDown': [0x00100000301, null, null, 0x00200000232], // arrowDown, null, null, numpad2 'ArrowLeft': [0x00100000302, null, null, 0x00200000234], // arrowLeft, null, null, numpad4 'ArrowRight': [0x00100000303, null, null, 0x00200000236], // arrowRight, null, null, numpad6 'ArrowUp': [0x00100000304, null, null, 0x00200000238], // arrowUp, null, null, numpad8 'Clear': [0x00100000401, null, null, 0x00200000235], // clear, null, null, numpad5 - 'Control': [0x00200000100, 0x00200000100, 0x00200000101, null], // controlLeft, controlLeft, controlRight, null + 'Control': [ + 0x00200000100, + 0x00200000100, + 0x00200000101, + null, + ], // controlLeft, controlLeft, controlRight, null 'Delete': [0x0010000007f, null, null, 0x0020000022e], // delete, null, null, numpadDecimal 'End': [0x00100000305, null, null, 0x00200000231], // end, null, null, numpad1 'Enter': [0x0010000000d, null, null, 0x0020000020d], // enter, null, null, numpadEnter 'Home': [0x00100000306, null, null, 0x00200000237], // home, null, null, numpad7 'Insert': [0x00100000407, null, null, 0x00200000230], // insert, null, null, numpad0 - 'Meta': [0x00200000106, 0x00200000106, 0x00200000107, null], // metaLeft, metaLeft, metaRight, null + 'Meta': [ + 0x00200000106, + 0x00200000106, + 0x00200000107, + null, + ], // metaLeft, metaLeft, metaRight, null 'PageDown': [0x00100000307, null, null, 0x00200000233], // pageDown, null, null, numpad3 'PageUp': [0x00100000308, null, null, 0x00200000239], // pageUp, null, null, numpad9 - 'Shift': [0x00200000102, 0x00200000102, 0x00200000103, null], // shiftLeft, shiftLeft, shiftRight, null + 'Shift': [ + 0x00200000102, + 0x00200000102, + 0x00200000103, + null, + ], // shiftLeft, shiftLeft, shiftRight, null }; diff --git a/lib/web_ui/lib/src/engine/keyboard_binding.dart b/lib/web_ui/lib/src/engine/keyboard_binding.dart index f70456e42239c..627918cd5d58d 100644 --- a/lib/web_ui/lib/src/engine/keyboard_binding.dart +++ b/lib/web_ui/lib/src/engine/keyboard_binding.dart @@ -9,7 +9,7 @@ import 'package:ui/ui.dart' as ui; import 'package:ui/ui_web/src/ui_web.dart' as ui_web; import 'package:web_locale_keymap/web_locale_keymap.dart' as locale_keymap; -import '../engine.dart' show registerHotRestartListener; +import '../engine.dart' show registerHotRestartListener; import 'dom.dart'; import 'key_map.g.dart'; import 'platform_dispatcher.dart'; @@ -137,10 +137,7 @@ class KeyboardBinding { } KeyboardConverter get converter => _converter; - late final KeyboardConverter _converter = KeyboardConverter( - _onKeyData, - localPlatform, - ); + late final KeyboardConverter _converter = KeyboardConverter(_onKeyData, localPlatform); final Map _listeners = {}; void _addEventListener(String eventName, DartDomEventListener handler) { @@ -166,12 +163,14 @@ class KeyboardBinding { }); _listeners.clear(); } + bool _onKeyData(ui.KeyData data) { bool? result; // This callback is designed to be invoked synchronously. This is enforced // by `result`, which starts null and is asserted non-null when returned. - EnginePlatformDispatcher.instance.invokeOnKeyData(data, - (bool handled) { result = handled; }); + EnginePlatformDispatcher.instance.invokeOnKeyData(data, (bool handled) { + result = handled; + }); return result!; } @@ -182,10 +181,7 @@ class KeyboardBinding { } class AsyncKeyboardDispatching { - AsyncKeyboardDispatching({ - required this.keyData, - this.callback, - }); + AsyncKeyboardDispatching({required this.keyData, this.callback}); final ui.KeyData keyData; final _VoidCallback? callback; @@ -228,9 +224,11 @@ class KeyboardConverter { _mapping = _mappingFromPlatform(platform); final DispatchKeyData performDispatchKeyData; + /// Whether the current platform is macOS or iOS, which affects how certain key /// events are comprehended, including CapsLock and key guarding. final bool onDarwin; + /// Maps logical keys from key event properties. final locale_keymap.LocaleKeymap _mapping; @@ -304,9 +302,9 @@ class KeyboardConverter { final bool shiftDown = event.shiftKey; final bool metaDown = event.metaKey; return (altDown ? _kDeadKeyAlt : 0) + - (ctrlDown ? _kDeadKeyCtrl : 0) + - (shiftDown ? _kDeadKeyShift : 0) + - (metaDown ? _kDeadKeyMeta : 0); + (ctrlDown ? _kDeadKeyCtrl : 0) + + (shiftDown ? _kDeadKeyShift : 0) + + (metaDown ? _kDeadKeyMeta : 0); } // Whether `event.key` is a key name, such as "Shift", or otherwise a @@ -339,7 +337,11 @@ class KeyboardConverter { // // Returns a callback that cancels the schedule. Disposal of // `KeyBoardConverter` also cancels the shedule automatically. - _VoidCallback _scheduleAsyncEvent(Duration duration, ValueGetter getData, _VoidCallback callback) { + _VoidCallback _scheduleAsyncEvent( + Duration duration, + ValueGetter getData, + _VoidCallback callback, + ) { bool canceled = false; Future.delayed(duration).then((_) { if (!canceled && !_disposed) { @@ -349,7 +351,9 @@ class KeyboardConverter { performDispatchKeyData(getData()); } }); - return () { canceled = true; }; + return () { + canceled = true; + }; } final Map _keyGuards = {}; @@ -370,11 +374,12 @@ class KeyboardConverter { ), () { _pressingRecords.remove(physicalKey); - } + }, ); _keyGuards.remove(physicalKey)?.call(); _keyGuards[physicalKey] = cancelingCallback; } + // Call this method on an up event of a non-modifier key. void _stopGuardingKey(int physicalKey) { _keyGuards.remove(physicalKey)?.call(); @@ -418,10 +423,11 @@ class KeyboardConverter { }); assert(event.type == 'keydown' || event.type == 'keyup'); - final bool isPhysicalDown = event.type == 'keydown' || - // On macOS, both keydown and keyup events of CapsLock should be considered keydown, - // followed by an immediate cancel event. - (_shouldSynthesizeCapsLockUp() && event.code! == _kPhysicalCapsLock); + final bool isPhysicalDown = + event.type == 'keydown' || + // On macOS, both keydown and keyup events of CapsLock should be considered keydown, + // followed by an immediate cancel event. + (_shouldSynthesizeCapsLockUp() && event.code! == _kPhysicalCapsLock); final ui.KeyEventType type; @@ -443,10 +449,9 @@ class KeyboardConverter { ), () { _pressingRecords.remove(physicalKey); - } + }, ); type = ui.KeyEventType.down; - } else if (isPhysicalDown) { // Case 2: Handle key down of normal keys if (_pressingRecords[physicalKey] != null) { @@ -468,14 +473,16 @@ class KeyboardConverter { // latter event must be dispatched as down events for the framework to // correctly recognize and choose to not to handle. Therefore, an up // event is synthesized before it. - _dispatchKeyData!(ui.KeyData( - timeStamp: timeStamp, - type: ui.KeyEventType.up, - physical: physicalKey, - logical: _pressingRecords[physicalKey]!, - character: null, - synthesized: true, - )); + _dispatchKeyData!( + ui.KeyData( + timeStamp: timeStamp, + type: ui.KeyEventType.up, + physical: physicalKey, + logical: _pressingRecords[physicalKey]!, + character: null, + synthesized: true, + ), + ); _pressingRecords.remove(physicalKey); type = ui.KeyEventType.down; } @@ -484,8 +491,8 @@ class KeyboardConverter { // normal down event, whether the system event is a repeat or not. type = ui.KeyEventType.down; } - - } else { // isPhysicalDown is false and not CapsLock + } else { + // isPhysicalDown is false and not CapsLock // Case 2: Handle key up of normal keys if (_pressingRecords[physicalKey] == null) { // The physical key has been released before. It indicates multiple @@ -536,14 +543,16 @@ class KeyboardConverter { return false; } - _dispatchKeyData!(ui.KeyData( - timeStamp: timeStamp, - type: ui.KeyEventType.up, - physical: physicalKey, - logical: testeeLogicalKey, - character: null, - synthesized: true, - )); + _dispatchKeyData!( + ui.KeyData( + timeStamp: timeStamp, + type: ui.KeyEventType.up, + physical: physicalKey, + logical: testeeLogicalKey, + character: null, + synthesized: true, + ), + ); return true; }); @@ -677,27 +686,31 @@ class KeyboardConverter { } void _synthesizeKeyDownEvent(num domTimestamp, int physical, int logical) { - performDispatchKeyData(ui.KeyData( - timeStamp: _eventTimeStampToDuration(domTimestamp), - type: ui.KeyEventType.down, - physical: physical, - logical: logical, - character: null, - synthesized: true, - )); + performDispatchKeyData( + ui.KeyData( + timeStamp: _eventTimeStampToDuration(domTimestamp), + type: ui.KeyEventType.down, + physical: physical, + logical: logical, + character: null, + synthesized: true, + ), + ); // Update pressing state _pressingRecords[physical] = logical; } void _synthesizeKeyUpEvent(num domTimestamp, int physical, int logical) { - performDispatchKeyData(ui.KeyData( - timeStamp: _eventTimeStampToDuration(domTimestamp), - type: ui.KeyEventType.up, - physical: physical, - logical: logical, - character: null, - synthesized: true, - )); + performDispatchKeyData( + ui.KeyData( + timeStamp: _eventTimeStampToDuration(domTimestamp), + type: ui.KeyEventType.up, + physical: physical, + logical: logical, + character: null, + synthesized: true, + ), + ); // Update pressing states _pressingRecords.remove(physical); } diff --git a/lib/web_ui/lib/src/engine/layers.dart b/lib/web_ui/lib/src/engine/layers.dart index 0949a02f376a2..ae86ab7e3d56c 100644 --- a/lib/web_ui/lib/src/engine/layers.dart +++ b/lib/web_ui/lib/src/engine/layers.dart @@ -43,9 +43,7 @@ class NoopOperation implements LayerOperation { String toString() => 'NoopOperation()'; } -class BackdropFilterLayer - with PictureEngineLayer - implements ui.BackdropFilterEngineLayer { +class BackdropFilterLayer with PictureEngineLayer implements ui.BackdropFilterEngineLayer { BackdropFilterLayer(this.operation); @override @@ -54,6 +52,7 @@ class BackdropFilterLayer @override BackdropFilterLayer emptyClone() => BackdropFilterLayer(operation); } + class BackdropFilterOperation implements LayerOperation { BackdropFilterOperation(this.filter, this.mode); @@ -83,9 +82,7 @@ class BackdropFilterOperation implements LayerOperation { String toString() => 'BackdropFilterOperation(filter: $filter, mode: $mode)'; } -class ClipPathLayer - with PictureEngineLayer - implements ui.ClipPathEngineLayer { +class ClipPathLayer with PictureEngineLayer implements ui.ClipPathEngineLayer { ClipPathLayer(this.operation); @override @@ -94,6 +91,7 @@ class ClipPathLayer @override ClipPathLayer emptyClone() => ClipPathLayer(operation); } + class ClipPathOperation implements LayerOperation { ClipPathOperation(this.path, this.clip); @@ -132,9 +130,7 @@ class ClipPathOperation implements LayerOperation { String toString() => 'ClipPathOperation(path: $path, clip: $clip)'; } -class ClipRectLayer - with PictureEngineLayer - implements ui.ClipRectEngineLayer { +class ClipRectLayer with PictureEngineLayer implements ui.ClipRectEngineLayer { ClipRectLayer(this.operation); @override @@ -143,6 +139,7 @@ class ClipRectLayer @override ClipRectLayer emptyClone() => ClipRectLayer(operation); } + class ClipRectOperation implements LayerOperation { const ClipRectOperation(this.rect, this.clip); @@ -181,9 +178,7 @@ class ClipRectOperation implements LayerOperation { String toString() => 'ClipRectOperation(rect: $rect, clip: $clip)'; } -class ClipRRectLayer - with PictureEngineLayer - implements ui.ClipRRectEngineLayer { +class ClipRRectLayer with PictureEngineLayer implements ui.ClipRRectEngineLayer { ClipRRectLayer(this.operation); @override @@ -192,6 +187,7 @@ class ClipRRectLayer @override ClipRRectLayer emptyClone() => ClipRRectLayer(operation); } + class ClipRRectOperation implements LayerOperation { const ClipRRectOperation(this.rrect, this.clip); @@ -230,9 +226,7 @@ class ClipRRectOperation implements LayerOperation { String toString() => 'ClipRRectOperation(rrect: $rrect, clip: $clip)'; } -class ColorFilterLayer - with PictureEngineLayer - implements ui.ColorFilterEngineLayer { +class ColorFilterLayer with PictureEngineLayer implements ui.ColorFilterEngineLayer { ColorFilterLayer(this.operation); @override @@ -241,6 +235,7 @@ class ColorFilterLayer @override ColorFilterLayer emptyClone() => ColorFilterLayer(operation); } + class ColorFilterOperation implements LayerOperation { ColorFilterOperation(this.filter); @@ -269,9 +264,7 @@ class ColorFilterOperation implements LayerOperation { String toString() => 'ColorFilterOperation(filter: $filter)'; } -class ImageFilterLayer - with PictureEngineLayer - implements ui.ImageFilterEngineLayer { +class ImageFilterLayer with PictureEngineLayer implements ui.ImageFilterEngineLayer { ImageFilterLayer(this.operation); @override @@ -280,6 +273,7 @@ class ImageFilterLayer @override ImageFilterLayer emptyClone() => ImageFilterLayer(operation); } + class ImageFilterOperation implements LayerOperation { ImageFilterOperation(this.filter, this.offset); @@ -310,17 +304,13 @@ class ImageFilterOperation implements LayerOperation { PlatformViewStyling createPlatformViewStyling() { PlatformViewStyling styling = const PlatformViewStyling(); if (offset != ui.Offset.zero) { - styling = PlatformViewStyling( - position: PlatformViewPosition.offset(offset) - ); + styling = PlatformViewStyling(position: PlatformViewPosition.offset(offset)); } final Matrix4? transform = filter.transform; if (transform != null) { styling = PlatformViewStyling.combine( styling, - PlatformViewStyling( - position: PlatformViewPosition.transform(transform), - ), + PlatformViewStyling(position: PlatformViewPosition.transform(transform)), ); } return const PlatformViewStyling(); @@ -333,9 +323,7 @@ class ImageFilterOperation implements LayerOperation { String toString() => 'ImageFilterOperation(filter: $filter)'; } -class OffsetLayer - with PictureEngineLayer - implements ui.OffsetEngineLayer { +class OffsetLayer with PictureEngineLayer implements ui.OffsetEngineLayer { OffsetLayer(this.operation); @override @@ -344,6 +332,7 @@ class OffsetLayer @override OffsetLayer emptyClone() => OffsetLayer(operation); } + class OffsetOperation implements LayerOperation { OffsetOperation(this.dx, this.dy); @@ -365,9 +354,8 @@ class OffsetOperation implements LayerOperation { } @override - PlatformViewStyling createPlatformViewStyling() => PlatformViewStyling( - position: PlatformViewPosition.offset(ui.Offset(dx, dy)) - ); + PlatformViewStyling createPlatformViewStyling() => + PlatformViewStyling(position: PlatformViewPosition.offset(ui.Offset(dx, dy))); @override bool get affectsBackdrop => false; @@ -376,9 +364,7 @@ class OffsetOperation implements LayerOperation { String toString() => 'OffsetOperation(dx: $dx, dy: $dy)'; } -class OpacityLayer - with PictureEngineLayer - implements ui.OpacityEngineLayer { +class OpacityLayer with PictureEngineLayer implements ui.OpacityEngineLayer { OpacityLayer(this.operation); @override @@ -387,6 +373,7 @@ class OpacityLayer @override OpacityLayer emptyClone() => OpacityLayer(operation); } + class OpacityOperation implements LayerOperation { OpacityOperation(this.alpha, this.offset); @@ -402,10 +389,7 @@ class OpacityOperation implements LayerOperation { canvas.save(); canvas.translate(offset.dx, offset.dy); } - canvas.saveLayer( - ui.Rect.largest, - ui.Paint()..color = ui.Color.fromARGB(alpha, 0, 0, 0) - ); + canvas.saveLayer(ui.Rect.largest, ui.Paint()..color = ui.Color.fromARGB(alpha, 0, 0, 0)); } @override @@ -418,7 +402,10 @@ class OpacityOperation implements LayerOperation { @override PlatformViewStyling createPlatformViewStyling() => PlatformViewStyling( - position: offset != ui.Offset.zero ? PlatformViewPosition.offset(offset) : const PlatformViewPosition.zero(), + position: + offset != ui.Offset.zero + ? PlatformViewPosition.offset(offset) + : const PlatformViewPosition.zero(), opacity: alpha.toDouble() / 255.0, ); @@ -429,9 +416,7 @@ class OpacityOperation implements LayerOperation { String toString() => 'OpacityOperation(offset: $offset, alpha: $alpha)'; } -class TransformLayer - with PictureEngineLayer - implements ui.TransformEngineLayer { +class TransformLayer with PictureEngineLayer implements ui.TransformEngineLayer { TransformLayer(this.operation); @override @@ -440,13 +425,15 @@ class TransformLayer @override TransformLayer emptyClone() => TransformLayer(operation); } + class TransformOperation implements LayerOperation { TransformOperation(this.transform); final Float64List transform; Matrix4? _memoizedMatrix; - Matrix4 get matrix => _memoizedMatrix ?? (_memoizedMatrix = Matrix4.fromFloat32List(toMatrix32(transform))); + Matrix4 get matrix => + _memoizedMatrix ?? (_memoizedMatrix = Matrix4.fromFloat32List(toMatrix32(transform))); @override ui.Rect mapRect(ui.Rect contentRect) => matrix.transformRect(contentRect); @@ -463,9 +450,8 @@ class TransformOperation implements LayerOperation { } @override - PlatformViewStyling createPlatformViewStyling() => PlatformViewStyling( - position: PlatformViewPosition.transform(matrix), - ); + PlatformViewStyling createPlatformViewStyling() => + PlatformViewStyling(position: PlatformViewPosition.transform(matrix)); @override bool get affectsBackdrop => false; @@ -474,9 +460,7 @@ class TransformOperation implements LayerOperation { String toString() => 'TransformOperation(matrix: $matrix)'; } -class ShaderMaskLayer - with PictureEngineLayer - implements ui.ShaderMaskEngineLayer { +class ShaderMaskLayer with PictureEngineLayer implements ui.ShaderMaskEngineLayer { ShaderMaskLayer(this.operation); @override @@ -485,6 +469,7 @@ class ShaderMaskLayer @override ShaderMaskLayer emptyClone() => ShaderMaskLayer(operation); } + class ShaderMaskOperation implements LayerOperation { ShaderMaskOperation(this.shader, this.maskRect, this.blendMode); @@ -497,10 +482,7 @@ class ShaderMaskOperation implements LayerOperation { @override void pre(SceneCanvas canvas) { - canvas.saveLayer( - ui.Rect.largest, - ui.Paint(), - ); + canvas.saveLayer(ui.Rect.largest, ui.Paint()); } @override @@ -511,7 +493,7 @@ class ShaderMaskOperation implements LayerOperation { ui.Rect.fromLTWH(0, 0, maskRect.width, maskRect.height), ui.Paint() ..blendMode = blendMode - ..shader = shader + ..shader = shader, ); canvas.restore(); canvas.restore(); @@ -524,7 +506,8 @@ class ShaderMaskOperation implements LayerOperation { bool get affectsBackdrop => false; @override - String toString() => 'ShaderMaskOperation(shader: $shader, maskRect: $maskRect, blendMode: $blendMode)'; + String toString() => + 'ShaderMaskOperation(shader: $shader, maskRect: $maskRect, blendMode: $blendMode)'; } class PlatformView { @@ -612,8 +595,7 @@ abstract class LayerOperation { bool get affectsBackdrop; } -sealed class LayerDrawCommand { -} +sealed class LayerDrawCommand {} class PictureDrawCommand extends LayerDrawCommand { PictureDrawCommand(this.offset, this.picture, this.sliceIndex); @@ -735,7 +717,7 @@ class PlatformViewStyling { const PlatformViewStyling({ this.position = const PlatformViewPosition.zero(), this.clip = const PlatformViewNoClip(), - this.opacity = 1.0 + this.opacity = 1.0, }); bool get isDefault => position.isZero && (opacity == 1.0) && clip is PlatformViewNoClip; @@ -800,9 +782,9 @@ sealed class PlatformViewClip { static bool rectCovers(ui.Rect covering, ui.Rect covered) { return covering.left <= covered.left && - covering.right >= covered.right && - covering.top <= covered.top && - covering.bottom >= covered.bottom; + covering.right >= covered.right && + covering.top <= covered.top && + covering.bottom >= covered.bottom; } static PlatformViewClip combine(PlatformViewClip outer, PlatformViewClip inner) { @@ -822,7 +804,7 @@ sealed class PlatformViewClip { } return PlatformViewPathClip( - ui.Path.combine(ui.PathOperation.intersect, outer.toPath, inner.toPath) as ScenePath + ui.Path.combine(ui.PathOperation.intersect, outer.toPath, inner.toPath) as ScenePath, ); } } @@ -988,7 +970,7 @@ class LayerSliceBuilder { } (ui.PictureRecorder, SceneCanvas) createRecorder(ui.Rect rect) => - debugRecorderFactory != null ? debugRecorderFactory!(rect) : defaultRecorderFactory(rect); + debugRecorderFactory != null ? debugRecorderFactory!(rect) : defaultRecorderFactory(rect); LayerSlice buildWithOperation(LayerOperation operation, ui.Rect? backdropRect) { final ui.Rect effectiveRect; @@ -1031,9 +1013,7 @@ class LayerBuilder { return LayerBuilder._(parent, layer); } - LayerBuilder._( - this.parent, - this.layer); + LayerBuilder._(this.parent, this.layer); final LayerBuilder? parent; final PictureEngineLayer layer; @@ -1074,7 +1054,10 @@ class LayerBuilder { return _memoizedGlobalPlatformViewStyling!; } if (parent != null) { - return _memoizedGlobalPlatformViewStyling ??= PlatformViewStyling.combine(parent!.globalPlatformViewStyling, platformViewStyling); + return _memoizedGlobalPlatformViewStyling ??= PlatformViewStyling.combine( + parent!.globalPlatformViewStyling, + platformViewStyling, + ); } return _memoizedGlobalPlatformViewStyling ??= platformViewStyling; } @@ -1092,21 +1075,13 @@ class LayerBuilder { return newSliceBuilder; } - void addPicture( - ui.Offset offset, - ui.Picture picture, { - required int sliceIndex, - }) { + void addPicture(ui.Offset offset, ui.Picture picture, {required int sliceIndex}) { final LayerSliceBuilder sliceBuilder = getOrCreateSliceBuilderAtIndex(sliceIndex); sliceBuilder.addPicture(offset, picture as ScenePicture); drawCommands.add(PictureDrawCommand(offset, picture, sliceIndex)); } - void addPlatformView( - int viewId, { - required ui.Rect bounds, - required int sliceIndex, - }) { + void addPlatformView(int viewId, {required ui.Rect bounds, required int sliceIndex}) { final LayerSliceBuilder sliceBuilder = getOrCreateSliceBuilderAtIndex(sliceIndex); sliceBuilder.platformViews.add(PlatformView(viewId, bounds, platformViewStyling)); drawCommands.add(PlatformViewDrawCommand(viewId, bounds, sliceIndex)); @@ -1118,16 +1093,23 @@ class LayerBuilder { if (slice != null) { final LayerSliceBuilder sliceBuilder = getOrCreateSliceBuilderAtIndex(i); sliceBuilder.addPicture(ui.Offset.zero, slice.picture); - sliceBuilder.platformViews.addAll(slice.platformViews.map((PlatformView view) { - return PlatformView(view.viewId, view.bounds, PlatformViewStyling.combine(platformViewStyling, view.styling)); - })); + sliceBuilder.platformViews.addAll( + slice.platformViews.map((PlatformView view) { + return PlatformView( + view.viewId, + view.bounds, + PlatformViewStyling.combine(platformViewStyling, view.styling), + ); + }), + ); } } drawCommands.add(RetainedLayerDrawCommand(layer)); } PictureEngineLayer sliceUp() { - final int sliceCount = layer.operation.affectsBackdrop ? getCurrentSliceCount() : sliceBuilders.length; + final int sliceCount = + layer.operation.affectsBackdrop ? getCurrentSliceCount() : sliceBuilders.length; final slices = []; for (int i = 0; i < sliceCount; i++) { final ui.Rect? backdropRect; diff --git a/lib/web_ui/lib/src/engine/navigation/history.dart b/lib/web_ui/lib/src/engine/navigation/history.dart index ef90ad9c9f282..99d44c8a2fb42 100644 --- a/lib/web_ui/lib/src/engine/navigation/history.dart +++ b/lib/web_ui/lib/src/engine/navigation/history.dart @@ -18,7 +18,8 @@ import '../services/message_codecs.dart'; BrowserHistory createHistoryForExistingState(ui_web.UrlStrategy? urlStrategy) { if (urlStrategy != null) { final Object? state = urlStrategy.getState(); - if (SingleEntryBrowserHistory._isOriginEntry(state) || SingleEntryBrowserHistory._isFlutterEntry(state)) { + if (SingleEntryBrowserHistory._isOriginEntry(state) || + SingleEntryBrowserHistory._isFlutterEntry(state)) { return SingleEntryBrowserHistory(urlStrategy: urlStrategy); } } @@ -130,8 +131,7 @@ class MultiEntriesBrowserHistory extends BrowserHistory { _setupStrategy(strategy); if (!_hasSerialCount(currentState)) { - strategy.replaceState( - _tagWithSerialCount(currentState, 0), 'flutter', currentPath); + strategy.replaceState(_tagWithSerialCount(currentState, 0), 'flutter', currentPath); } // If we restore from a page refresh, the _currentSerialCount may not be 0. _lastSeenSerialCount = _currentSerialCount; @@ -143,18 +143,14 @@ class MultiEntriesBrowserHistory extends BrowserHistory { late int _lastSeenSerialCount; int get _currentSerialCount { if (_hasSerialCount(currentState)) { - final Map stateMap = - currentState! as Map; + final Map stateMap = currentState! as Map; return (stateMap['serialCount'] as double).toInt(); } return 0; } Object _tagWithSerialCount(Object? originialState, int count) { - return { - 'serialCount': count.toDouble(), - 'state': originialState, - }; + return {'serialCount': count.toDouble(), 'state': originialState}; } bool _hasSerialCount(Object? state) { @@ -191,18 +187,20 @@ class MultiEntriesBrowserHistory extends BrowserHistory { // In this case we assume this will be the next history entry from the // last seen entry. urlStrategy!.replaceState( - _tagWithSerialCount(state, _lastSeenSerialCount + 1), - 'flutter', - currentPath); + _tagWithSerialCount(state, _lastSeenSerialCount + 1), + 'flutter', + currentPath, + ); } _lastSeenSerialCount = _currentSerialCount; EnginePlatformDispatcher.instance.invokeOnPlatformMessage( 'flutter/navigation', const JSONMethodCodec().encodeMethodCall( - MethodCall('pushRouteInformation', { - 'location': currentPath, - 'state': (state as Map?)?['state'], - })), + MethodCall('pushRouteInformation', { + 'location': currentPath, + 'state': (state as Map?)?['state'], + }), + ), (_) {}, ); } @@ -229,13 +227,8 @@ class MultiEntriesBrowserHistory extends BrowserHistory { } // Unwrap state. assert(_hasSerialCount(currentState) && _currentSerialCount == 0); - final Map stateMap = - currentState! as Map; - urlStrategy!.replaceState( - stateMap['state'], - 'flutter', - currentPath, - ); + final Map stateMap = currentState! as Map; + urlStrategy!.replaceState(stateMap['state'], 'flutter', currentPath); } } @@ -340,9 +333,7 @@ class SingleEntryBrowserHistory extends BrowserHistory { // Send a 'pushRoute' platform message so the app handles it accordingly. EnginePlatformDispatcher.instance.invokeOnPlatformMessage( 'flutter/navigation', - const JSONMethodCodec().encodeMethodCall( - MethodCall('pushRoute', newRouteName), - ), + const JSONMethodCodec().encodeMethodCall(MethodCall('pushRoute', newRouteName)), (_) {}, ); } else { @@ -369,11 +360,7 @@ class SingleEntryBrowserHistory extends BrowserHistory { /// This method is used manipulate the Flutter Entry which is always the /// active entry while the Flutter app is running. - void _setupFlutterEntry( - ui_web.UrlStrategy strategy, { - bool replace = false, - String? path, - }) { + void _setupFlutterEntry(ui_web.UrlStrategy strategy, {bool replace = false, String? path}) { path ??= currentPath; if (replace) { strategy.replaceState(_flutterState, 'flutter', path); @@ -394,7 +381,6 @@ class SingleEntryBrowserHistory extends BrowserHistory { // We need to remove the flutter entry that we pushed in setup. await urlStrategy!.go(-1); // Restores original state. - urlStrategy! - .replaceState(_unwrapOriginState(currentState), 'flutter', currentPath); + urlStrategy!.replaceState(_unwrapOriginState(currentState), 'flutter', currentPath); } } diff --git a/lib/web_ui/lib/src/engine/onscreen_logging.dart b/lib/web_ui/lib/src/engine/onscreen_logging.dart index 219ae36af5ad1..b26af3a41bae0 100644 --- a/lib/web_ui/lib/src/engine/onscreen_logging.dart +++ b/lib/web_ui/lib/src/engine/onscreen_logging.dart @@ -89,8 +89,7 @@ void debugPrintStack({String? label, int? maxFrames}) { if (label != null) { print(label); } - Iterable lines = - StackTrace.current.toString().trimRight().split('\n'); + Iterable lines = StackTrace.current.toString().trimRight().split('\n'); if (maxFrames != null) { lines = lines.take(maxFrames); } @@ -105,13 +104,10 @@ void debugPrintStack({String? label, int? maxFrames}) { /// format but the frame numbers will not be consecutive (frames are elided) /// and the final line may be prose rather than a stack frame. Iterable defaultStackFilter(Iterable frames) { - const List filteredPackages = [ - 'dart:async-patch', - 'dart:async', - 'dart:_runtime', - ]; - final RegExp stackParser = - RegExp(r'^#[0-9]+ +([^.]+).* \(([^/\\]*)[/\\].+:[0-9]+(?::[0-9]+)?\)$'); + const List filteredPackages = ['dart:async-patch', 'dart:async', 'dart:_runtime']; + final RegExp stackParser = RegExp( + r'^#[0-9]+ +([^.]+).* \(([^/\\]*)[/\\].+:[0-9]+(?::[0-9]+)?\)$', + ); final RegExp packageParser = RegExp(r'^([^:]+):(.+)$'); final List result = []; final List skipped = []; @@ -122,8 +118,7 @@ Iterable defaultStackFilter(Iterable frames) { if (filteredPackages.contains(match.group(2))) { final Match? packageMatch = packageParser.firstMatch(match.group(2)!); if (packageMatch != null && packageMatch.group(1) == 'package') { - skipped.add( - 'package ${packageMatch.group(2)}'); // avoid "package package:foo" + skipped.add('package ${packageMatch.group(2)}'); // avoid "package package:foo" } else { skipped.add('package ${match.group(2)}'); } diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index 8070fd1e71d42..adc5da66b219e 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -33,10 +33,12 @@ class HighContrastSupport { final List _listeners = []; /// Reference to css media query that indicates whether high contrast is on. - final DomMediaQueryList _highContrastMediaQuery = - domWindow.matchMedia(_highContrastMediaQueryString); - late final DomEventListener _onHighContrastChangeListener = - createDomEventListener(_onHighContrastChange); + final DomMediaQueryList _highContrastMediaQuery = domWindow.matchMedia( + _highContrastMediaQueryString, + ); + late final DomEventListener _onHighContrastChangeListener = createDomEventListener( + _onHighContrastChange, + ); bool get isHighContrastEnabled => _highContrastMediaQuery.matches; @@ -96,10 +98,8 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { static final EnginePlatformDispatcher _instance = EnginePlatformDispatcher(); @visibleForTesting - final DomElement accessibilityPlaceholder = EngineSemantics - .instance - .semanticsHelper - .prepareAccessibilityPlaceholder(); + final DomElement accessibilityPlaceholder = + EngineSemantics.instance.semanticsHelper.prepareAccessibilityPlaceholder(); PlatformConfiguration configuration = PlatformConfiguration( locales: parseBrowserLanguages(), @@ -109,8 +109,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// Compute accessibility features based on the current value of high contrast flag static EngineAccessibilityFeatures computeAccessibilityFeatures() { - final EngineAccessibilityFeaturesBuilder builder = - EngineAccessibilityFeaturesBuilder(0); + final EngineAccessibilityFeaturesBuilder builder = EngineAccessibilityFeaturesBuilder(0); if (HighContrastSupport.instance.isHighContrastEnabled) { builder.highContrast = true; } @@ -131,8 +130,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// Receives all events related to platform configuration changes. @override - ui.VoidCallback? get onPlatformConfigurationChanged => - _onPlatformConfigurationChanged; + ui.VoidCallback? get onPlatformConfigurationChanged => _onPlatformConfigurationChanged; ui.VoidCallback? _onPlatformConfigurationChanged; Zone? _onPlatformConfigurationChangedZone; @override @@ -144,19 +142,15 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// Engine code should use this method instead of the callback directly. /// Otherwise zones won't work properly. void invokeOnPlatformConfigurationChanged() { - invoke( - _onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone); + invoke(_onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone); } @override - Iterable displays = [ - EngineFlutterDisplay.instance, - ]; + Iterable displays = [EngineFlutterDisplay.instance]; late final FlutterViewManager viewManager = FlutterViewManager(this); - late final AppLifecycleState _appLifecycleState = - AppLifecycleState.create(viewManager); + late final AppLifecycleState _appLifecycleState = AppLifecycleState.create(viewManager); /// The current list of windows. @override @@ -193,8 +187,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// * [PlatformDisptacher.views] for a list of all [FlutterView]s provided /// by the platform. @override - EngineFlutterWindow? get implicitView => - viewManager[kImplicitViewId] as EngineFlutterWindow?; + EngineFlutterWindow? get implicitView => viewManager[kImplicitViewId] as EngineFlutterWindow?; /// A callback that is invoked whenever the platform's [devicePixelRatio], /// [physicalSize], [padding], [viewInsets], or [systemGestureInsets] @@ -231,8 +224,10 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { } } - late final ViewFocusBinding _viewFocusBinding = - ViewFocusBinding(viewManager, invokeOnViewFocusChange); + late final ViewFocusBinding _viewFocusBinding = ViewFocusBinding( + viewManager, + invokeOnViewFocusChange, + ); @override ui.ViewFocusChangeCallback? get onViewFocusChange => _onViewFocusChange; @@ -247,11 +242,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { // Engine code should use this method instead of the callback directly. // Otherwise zones won't work properly. void invokeOnViewFocusChange(ui.ViewFocusEvent viewFocusEvent) { - invoke1( - _onViewFocusChange, - _onViewFocusChangeZone, - viewFocusEvent, - ); + invoke1(_onViewFocusChange, _onViewFocusChangeZone, viewFocusEvent); } @override @@ -339,8 +330,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// Engine code should use this method instead of the callback directly. /// Otherwise zones won't work properly. void invokeOnPointerDataPacket(ui.PointerDataPacket dataPacket) { - invoke1( - _onPointerDataPacket, _onPointerDataPacketZone, dataPacket); + invoke1(_onPointerDataPacket, _onPointerDataPacketZone, dataPacket); } /// A callback that is invoked when key data is available. @@ -367,10 +357,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { void invokeOnKeyData(ui.KeyData data, _KeyDataResponseCallback callback) { final ui.KeyDataCallback? onKeyData = _onKeyData; if (onKeyData != null) { - invoke( - () => callback(onKeyData(data)), - _onKeyDataZone, - ); + invoke(() => callback(onKeyData(data)), _onKeyDataZone); } else { callback(false); } @@ -410,8 +397,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// Engine code should use this method instead of the callback directly. /// Otherwise zones won't work properly. void invokeOnReportTimings(List timings) { - invoke1>( - _onReportTimings, _onReportTimingsZone, timings); + invoke1>(_onReportTimings, _onReportTimingsZone, timings); } @override @@ -420,17 +406,11 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { ByteData? data, ui.PlatformMessageResponseCallback? callback, ) { - _sendPlatformMessage( - name, data, _zonedPlatformMessageResponseCallback(callback)); + _sendPlatformMessage(name, data, _zonedPlatformMessageResponseCallback(callback)); } @override - void sendPortPlatformMessage( - String name, - ByteData? data, - int identifier, - Object port, - ) { + void sendPortPlatformMessage(String name, ByteData? data, int identifier, Object port) { throw Exception("Isolates aren't supported in web."); } @@ -480,9 +460,9 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// Wraps the given [callback] in another callback that ensures that the /// original callback is called in the zone it was registered in. - static ui.PlatformMessageResponseCallback? - _zonedPlatformMessageResponseCallback( - ui.PlatformMessageResponseCallback? callback) { + static ui.PlatformMessageResponseCallback? _zonedPlatformMessageResponseCallback( + ui.PlatformMessageResponseCallback? callback, + ) { if (callback == null) { return null; } @@ -541,14 +521,12 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { 'Argument to Skia.setResourceCacheMaxBytes must be an int, but was ${(decoded.arguments as Object?).runtimeType}', ); final int cacheSizeInBytes = decoded.arguments as int; - CanvasKitRenderer.instance.resourceCacheMaxBytes = - cacheSizeInBytes; + CanvasKitRenderer.instance.resourceCacheMaxBytes = cacheSizeInBytes; } // Also respond in HTML mode. Otherwise, apps would have to detect // CanvasKit vs HTML before invoking this method. - replyToPlatformMessage( - callback, jsonCodec.encodeSuccessEnvelope([true])); + replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope([true])); } return; @@ -569,58 +547,41 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { // https://github.com/flutter/flutter/issues/139174 if (implicitView != null) { implicitView!.browserHistory.exit().then((_) { - replyToPlatformMessage( - callback, - jsonCodec.encodeSuccessEnvelope(true), - ); + replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true)); }); } else { - replyToPlatformMessage( - callback, - jsonCodec.encodeSuccessEnvelope(true), - ); + replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true)); } return; case 'HapticFeedback.vibrate': final String? type = decoded.arguments as String?; vibrate(_getHapticFeedbackDuration(type)); - replyToPlatformMessage( - callback, jsonCodec.encodeSuccessEnvelope(true)); + replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true)); return; case 'SystemChrome.setApplicationSwitcherDescription': - final Map arguments = - decoded.arguments as Map; + final Map arguments = decoded.arguments as Map; final String label = arguments['label'] as String? ?? ''; // TODO(web): Stop setting the color from here, https://github.com/flutter/flutter/issues/123365 - final int primaryColor = - arguments['primaryColor'] as int? ?? 0xFF000000; + final int primaryColor = arguments['primaryColor'] as int? ?? 0xFF000000; domDocument.title = label; setThemeColor(ui.Color(primaryColor)); - replyToPlatformMessage( - callback, jsonCodec.encodeSuccessEnvelope(true)); + replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true)); return; case 'SystemChrome.setSystemUIOverlayStyle': - final Map arguments = - decoded.arguments as Map; + final Map arguments = decoded.arguments as Map; final int? statusBarColor = arguments['statusBarColor'] as int?; - setThemeColor( - statusBarColor == null ? null : ui.Color(statusBarColor)); - replyToPlatformMessage( - callback, jsonCodec.encodeSuccessEnvelope(true)); + setThemeColor(statusBarColor == null ? null : ui.Color(statusBarColor)); + replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true)); return; case 'SystemChrome.setPreferredOrientations': final List arguments = decoded.arguments as List; - ScreenOrientation.instance - .setPreferredOrientation(arguments) - .then((bool success) { - replyToPlatformMessage( - callback, jsonCodec.encodeSuccessEnvelope(success)); + ScreenOrientation.instance.setPreferredOrientation(arguments).then((bool success) { + replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(success)); }); return; case 'SystemSound.play': // There are no default system sounds on web. - replyToPlatformMessage( - callback, jsonCodec.encodeSuccessEnvelope(true)); + replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true)); return; case 'Clipboard.setData': ClipboardMessageHandler().setDataMethodCall(decoded, callback); @@ -647,45 +608,39 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { switch (decoded.method) { case 'enableContextMenu': implicitView!.contextMenu.enable(); - replyToPlatformMessage( - callback, jsonCodec.encodeSuccessEnvelope(true)); + replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true)); return; case 'disableContextMenu': implicitView!.contextMenu.disable(); - replyToPlatformMessage( - callback, jsonCodec.encodeSuccessEnvelope(true)); + replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true)); return; } return; case 'flutter/mousecursor': final MethodCall decoded = standardCodec.decodeMethodCall(data); - final Map arguments = - decoded.arguments as Map; + final Map arguments = decoded.arguments as Map; switch (decoded.method) { case 'activateSystemCursor': // TODO(mdebbar): Once the framework starts sending us a viewId, we // should use it to grab the correct view. // https://github.com/flutter/flutter/issues/140226 - views.firstOrNull?.mouseCursor - .activateSystemCursor(arguments.tryString('kind')); + views.firstOrNull?.mouseCursor.activateSystemCursor(arguments.tryString('kind')); } return; case 'flutter/web_test_e2e': replyToPlatformMessage( - callback, - jsonCodec.encodeSuccessEnvelope( - _handleWebTestEnd2EndMessage(jsonCodec, data))); + callback, + jsonCodec.encodeSuccessEnvelope(_handleWebTestEnd2EndMessage(jsonCodec, data)), + ); return; case PlatformViewMessageHandler.channelName: // `arguments` can be a Map for `create`, // but an `int` for `dispose`, hence why `dynamic` everywhere. - final MethodCall(:String method, :dynamic arguments) = - standardCodec.decodeMethodCall(data); - PlatformViewMessageHandler.instance - .handlePlatformViewCall(method, arguments, callback!); + final MethodCall(:String method, :dynamic arguments) = standardCodec.decodeMethodCall(data); + PlatformViewMessageHandler.instance.handlePlatformViewCall(method, arguments, callback!); return; case 'flutter/accessibility': @@ -708,10 +663,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { if (implicitView != null) { implicitView!.handleNavigationMessage(data).then((bool handled) { if (handled) { - replyToPlatformMessage( - callback, - jsonCodec.encodeSuccessEnvelope(true), - ); + replyToPlatformMessage(callback, jsonCodec.encodeSuccessEnvelope(true)); } else { callback?.call(null); } @@ -739,7 +691,9 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { } Future _handleFlutterAssetsMessage( - String url, ui.PlatformMessageResponseCallback? callback) async { + String url, + ui.PlatformMessageResponseCallback? callback, + ) async { try { final HttpFetchResponse response = await ui_web.assetManager.loadAsset(url) as HttpFetchResponse; @@ -788,7 +742,10 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { } @override - void scheduleWarmUpFrame({required ui.VoidCallback beginFrame, required ui.VoidCallback drawFrame}) { + void scheduleWarmUpFrame({ + required ui.VoidCallback beginFrame, + required ui.VoidCallback drawFrame, + }) { Timer.run(beginFrame); // We use timers here to ensure that microtasks flush in between. // @@ -835,8 +792,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { // Only render in an `onDrawFrame` or `onBeginFrame` scope. This is checked // by checking if the `_viewsRenderedInCurrentFrame` is non-null and this // view hasn't been rendered already in this scope. - final bool shouldRender = - _viewsRenderedInCurrentFrame?.add(target) ?? false; + final bool shouldRender = _viewsRenderedInCurrentFrame?.add(target) ?? false; // TODO(harryterkelsen): HTML renderer needs to violate the render rule in // order to perform golden tests in Flutter framework because on the HTML // renderer, golden tests render to DOM and then take a browser screenshot, @@ -848,16 +804,14 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// Additional accessibility features that may be enabled by the platform. @override - ui.AccessibilityFeatures get accessibilityFeatures => - configuration.accessibilityFeatures; + ui.AccessibilityFeatures get accessibilityFeatures => configuration.accessibilityFeatures; /// A callback that is invoked when the value of [accessibilityFeatures] changes. /// /// The framework invokes this callback in the same zone in which the /// callback was set. @override - ui.VoidCallback? get onAccessibilityFeaturesChanged => - _onAccessibilityFeaturesChanged; + ui.VoidCallback? get onAccessibilityFeaturesChanged => _onAccessibilityFeaturesChanged; ui.VoidCallback? _onAccessibilityFeaturesChanged; Zone? _onAccessibilityFeaturesChangedZone; @override @@ -869,8 +823,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// Engine code should use this method instead of the callback directly. /// Otherwise zones won't work properly. void invokeOnAccessibilityFeaturesChanged() { - invoke( - _onAccessibilityFeaturesChanged, _onAccessibilityFeaturesChangedZone); + invoke(_onAccessibilityFeaturesChanged, _onAccessibilityFeaturesChangedZone); } /// Change the retained semantics data about this window. @@ -906,8 +859,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// * https://developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/languages, /// which explains browser quirks in the implementation notes. @override - ui.Locale get locale => - locales.isEmpty ? const ui.Locale.fromSubtags() : locales.first; + ui.Locale get locale => locales.isEmpty ? const ui.Locale.fromSubtags() : locales.first; /// The full system-reported supported locales of the device. /// @@ -935,8 +887,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { return; } updateLocales(); // First time, for good measure. - _onLocaleChangedSubscription = - DomSubscription(domWindow, 'languagechange', (DomEvent _) { + _onLocaleChangedSubscription = DomSubscription(domWindow, 'languagechange', (DomEvent _) { // Update internal config, then propagate the changes. updateLocales(); invokeOnLocaleChanged(); @@ -1070,12 +1021,13 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { void _addFontSizeObserver() { const String styleAttribute = 'style'; - _fontSizeObserver = createDomMutationObserver( - (JSArray mutations, DomMutationObserver _) { + _fontSizeObserver = createDomMutationObserver(( + JSArray mutations, + DomMutationObserver _, + ) { for (final JSAny? mutation in mutations.toDart) { final DomMutationRecord record = mutation! as DomMutationRecord; - if (record.type == 'attributes' && - record.attributeName == styleAttribute) { + if (record.type == 'attributes' && record.attributeName == styleAttribute) { final double newTextScaleFactor = findBrowserTextScaleFactor(); _updateTextScaleFactor(newTextScaleFactor); } @@ -1129,8 +1081,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { void updateSemanticsEnabled(bool semanticsEnabled) { if (semanticsEnabled != this.semanticsEnabled) { - configuration = - configuration.copyWith(semanticsEnabled: semanticsEnabled); + configuration = configuration.copyWith(semanticsEnabled: semanticsEnabled); if (_onSemanticsEnabledChanged != null) { invokeOnSemanticsEnabledChanged(); } @@ -1163,14 +1114,16 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { final EngineAccessibilityFeatures original = configuration.accessibilityFeatures as EngineAccessibilityFeatures; configuration = configuration.copyWith( - accessibilityFeatures: original.copyWith(highContrast: value)); + accessibilityFeatures: original.copyWith(highContrast: value), + ); invokeOnPlatformConfigurationChanged(); } } /// Reference to css media query that indicates the user theme preference on the web. - final DomMediaQueryList _brightnessMediaQuery = - domWindow.matchMedia('(prefers-color-scheme: dark)'); + final DomMediaQueryList _brightnessMediaQuery = domWindow.matchMedia( + '(prefers-color-scheme: dark)', + ); /// A callback that is invoked whenever [_brightnessMediaQuery] changes value. /// @@ -1179,14 +1132,13 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// Set the callback function for listening changes in [_brightnessMediaQuery] value. void _addBrightnessMediaQueryListener() { - _updatePlatformBrightness(_brightnessMediaQuery.matches - ? ui.Brightness.dark - : ui.Brightness.light); + _updatePlatformBrightness( + _brightnessMediaQuery.matches ? ui.Brightness.dark : ui.Brightness.light, + ); _brightnessMediaQueryListener = createDomEventListener((DomEvent event) { final DomMediaQueryListEvent mqEvent = event as DomMediaQueryListEvent; - _updatePlatformBrightness( - mqEvent.matches! ? ui.Brightness.dark : ui.Brightness.light); + _updatePlatformBrightness(mqEvent.matches! ? ui.Brightness.dark : ui.Brightness.light); }); _brightnessMediaQuery.addListener(_brightnessMediaQueryListener); } @@ -1207,8 +1159,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. @override - ui.VoidCallback? get onPlatformBrightnessChanged => - _onPlatformBrightnessChanged; + ui.VoidCallback? get onPlatformBrightnessChanged => _onPlatformBrightnessChanged; ui.VoidCallback? _onPlatformBrightnessChanged; Zone? _onPlatformBrightnessChangedZone; @override @@ -1285,8 +1236,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// The framework invokes this callback in the same zone in which the /// callback was set. @override - ui.SemanticsActionEventCallback? get onSemanticsActionEvent => - _onSemanticsActionEvent; + ui.SemanticsActionEventCallback? get onSemanticsActionEvent => _onSemanticsActionEvent; ui.SemanticsActionEventCallback? _onSemanticsActionEvent; Zone _onSemanticsActionEventZone = Zone.root; @override @@ -1297,21 +1247,11 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// Engine code should use this method instead of the callback directly. /// Otherwise zones won't work properly. - void invokeOnSemanticsAction( - int viewId, - int nodeId, - ui.SemanticsAction action, - ByteData? args, - ) { + void invokeOnSemanticsAction(int viewId, int nodeId, ui.SemanticsAction action, ByteData? args) { invoke1( _onSemanticsActionEvent, _onSemanticsActionEventZone, - ui.SemanticsActionEvent( - type: action, - nodeId: nodeId, - viewId: viewId, - arguments: args, - ), + ui.SemanticsActionEvent(type: action, nodeId: nodeId, viewId: viewId, arguments: args), ); } @@ -1362,8 +1302,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { String get defaultRouteName { // TODO(mdebbar): What should we do in multi-view mode? // https://github.com/flutter/flutter/issues/139174 - return _defaultRouteName ??= - implicitView?.browserHistory.currentPath ?? '/'; + return _defaultRouteName ??= implicitView?.browserHistory.currentPath ?? '/'; } /// Lazily initialized when the `defaultRouteName` getter is invoked. @@ -1375,10 +1314,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { /// In Flutter, platform messages are exchanged between threads so the /// messages and responses have to be exchanged asynchronously. We simulate /// that by adding a zero-length delay to the reply. - void replyToPlatformMessage( - ui.PlatformMessageResponseCallback? callback, - ByteData? data, - ) { + void replyToPlatformMessage(ui.PlatformMessageResponseCallback? callback, ByteData? data) { Future.delayed(Duration.zero).then((_) { if (callback != null) { callback(data); @@ -1390,8 +1326,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { ui.FrameData get frameData => const ui.FrameData.webOnly(); @override - double scaleFontSize(double unscaledFontSize) => - unscaledFontSize * textScaleFactor; + double scaleFontSize(double unscaledFontSize) => unscaledFontSize * textScaleFactor; } bool _handleWebTestEnd2EndMessage(MethodCodec codec, ByteData? data) { @@ -1437,8 +1372,7 @@ void invoke1(void Function(A a)? callback, Zone? zone, A arg) { } /// Invokes [callback] inside the given [zone] passing it [arg1] and [arg2]. -void invoke2( - void Function(A1 a1, A2 a2)? callback, Zone? zone, A1 arg1, A2 arg2) { +void invoke2(void Function(A1 a1, A2 a2)? callback, Zone? zone, A1 arg1, A2 arg2) { if (callback == null) { return; } @@ -1455,8 +1389,13 @@ void invoke2( } /// Invokes [callback] inside the given [zone] passing it [arg1], [arg2], and [arg3]. -void invoke3(void Function(A1 a1, A2 a2, A3 a3)? callback, - Zone? zone, A1 arg1, A2 arg2, A3 arg3) { +void invoke3( + void Function(A1 a1, A2 a2, A3 a3)? callback, + Zone? zone, + A1 arg1, + A2 arg2, + A3 arg3, +) { if (callback == null) { return; } @@ -1477,8 +1416,7 @@ const double _defaultRootFontSize = 16.0; /// Finds the text scale factor of the browser by looking at the computed style /// of the browser's element. double findBrowserTextScaleFactor() { - final num fontSize = - parseFontSize(domDocument.documentElement!) ?? _defaultRootFontSize; + final num fontSize = parseFontSize(domDocument.documentElement!) ?? _defaultRootFontSize; return fontSize / _defaultRootFontSize; } @@ -1558,10 +1496,8 @@ class PlatformConfiguration { String? systemFontFamily, }) { return PlatformConfiguration( - accessibilityFeatures: - accessibilityFeatures ?? this.accessibilityFeatures, - alwaysUse24HourFormat: - alwaysUse24HourFormat ?? this.alwaysUse24HourFormat, + accessibilityFeatures: accessibilityFeatures ?? this.accessibilityFeatures, + alwaysUse24HourFormat: alwaysUse24HourFormat ?? this.alwaysUse24HourFormat, semanticsEnabled: semanticsEnabled ?? this.semanticsEnabled, platformBrightness: platformBrightness ?? this.platformBrightness, textScaleFactor: textScaleFactor ?? this.textScaleFactor, diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher/app_lifecycle_state.dart b/lib/web_ui/lib/src/engine/platform_dispatcher/app_lifecycle_state.dart index 27db846163035..f78e14f04696a 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher/app_lifecycle_state.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher/app_lifecycle_state.dart @@ -21,8 +21,7 @@ abstract class AppLifecycleState { ui.AppLifecycleState get appLifecycleState => _appLifecycleState; ui.AppLifecycleState _appLifecycleState = ui.AppLifecycleState.resumed; - final List _listeners = - []; + final List _listeners = []; void addListener(AppLifecycleStateListener listener) { if (_listeners.isEmpty) { @@ -82,28 +81,22 @@ class _BrowserAppLifecycleState extends AppLifecycleState { void deactivate() { domWindow.removeEventListener('focus', _focusListener); domWindow.removeEventListener('blur', _blurListener); - domDocument.removeEventListener( - 'visibilitychange', - _visibilityChangeListener, - ); + domDocument.removeEventListener('visibilitychange', _visibilityChangeListener); for (final StreamSubscription subscription in _subscriptions) { subscription.cancel(); } _subscriptions.clear(); } - late final DomEventListener _focusListener = - createDomEventListener((DomEvent event) { + late final DomEventListener _focusListener = createDomEventListener((DomEvent event) { onAppLifecycleStateChange(ui.AppLifecycleState.resumed); }); - late final DomEventListener _blurListener = - createDomEventListener((DomEvent event) { + late final DomEventListener _blurListener = createDomEventListener((DomEvent event) { onAppLifecycleStateChange(ui.AppLifecycleState.inactive); }); - late final DomEventListener _visibilityChangeListener = - createDomEventListener((DomEvent event) { + late final DomEventListener _visibilityChangeListener = createDomEventListener((DomEvent event) { if (domDocument.visibilityState == 'visible') { onAppLifecycleStateChange(ui.AppLifecycleState.resumed); } else if (domDocument.visibilityState == 'hidden') { diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher/view_focus_binding.dart b/lib/web_ui/lib/src/engine/platform_dispatcher/view_focus_binding.dart index f3aa40ac161ed..64a1ca6adee86 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher/view_focus_binding.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher/view_focus_binding.dart @@ -11,7 +11,6 @@ import 'package:ui/ui.dart' as ui; final class ViewFocusBinding { ViewFocusBinding(this._viewManager, this._onViewFocusChange); - /// Whether [FlutterView] focus changes will be reported and performed. /// /// DO NOT rely on this bit as it will go away soon. You're warned :)! @@ -72,7 +71,8 @@ final class ViewFocusBinding { // We leverage this behavior to ignore focusout events where the document has focus but activeElement is not . // // Refer to https://github.com/flutter/engine/pull/54965 for more info. - final bool wasFocusInvoked = domDocument.hasFocus() && domDocument.activeElement != domDocument.body; + final bool wasFocusInvoked = + domDocument.hasFocus() && domDocument.activeElement != domDocument.body; if (wasFocusInvoked) { return; } @@ -140,10 +140,7 @@ final class ViewFocusBinding { // Controls whether the Flutter view identified by [viewId] is reachable by // keyboard. - void _updateViewKeyboardReachability( - int? viewId, { - required bool reachable, - }) { + void _updateViewKeyboardReachability(int? viewId, {required bool reachable}) { if (viewId == null) { return; } diff --git a/lib/web_ui/lib/src/engine/platform_views/content_manager.dart b/lib/web_ui/lib/src/engine/platform_views/content_manager.dart index c09eefcc18972..f21ff6ce97db6 100644 --- a/lib/web_ui/lib/src/engine/platform_views/content_manager.dart +++ b/lib/web_ui/lib/src/engine/platform_views/content_manager.dart @@ -25,10 +25,7 @@ import 'slots.dart'; class PlatformViewManager { PlatformViewManager() { // Register some default factories. - registerFactory( - ui_web.PlatformViewRegistry.defaultVisibleViewType, - _defaultFactory, - ); + registerFactory(ui_web.PlatformViewRegistry.defaultVisibleViewType, _defaultFactory); registerFactory( ui_web.PlatformViewRegistry.defaultInvisibleViewType, _defaultFactory, @@ -95,8 +92,7 @@ class PlatformViewManager { /// it's been set. /// /// `factoryFunction` needs to be a [PlatformViewFactory]. - bool registerFactory(String viewType, Function factoryFunction, - {bool isVisible = true}) { + bool registerFactory(String viewType, Function factoryFunction, {bool isVisible = true}) { assert( factoryFunction is ui_web.PlatformViewFactory || factoryFunction is ui_web.ParameterizedPlatformViewFactory, @@ -135,20 +131,18 @@ class PlatformViewManager { /// a place where to attach the `slot` property, that will tell the browser /// what `slot` tag will reveal this `contents`, **without modifying the returned /// html from the `factory` function**. - DomElement renderContent( - String viewType, - int viewId, - Object? params, - ) { - assert(knowsViewType(viewType), - 'Attempted to render contents of unregistered viewType: $viewType'); + DomElement renderContent(String viewType, int viewId, Object? params) { + assert( + knowsViewType(viewType), + 'Attempted to render contents of unregistered viewType: $viewType', + ); final String slotName = getPlatformViewSlotName(viewId); _viewIdToType[viewId] = viewType; return _contents.putIfAbsent(viewId, () { - final DomElement wrapper = domDocument - .createElement('flt-platform-view') + final DomElement wrapper = + domDocument.createElement('flt-platform-view') ..id = getPlatformViewDomId(viewId) ..setAttribute('slot', slotName); @@ -186,17 +180,21 @@ class PlatformViewManager { // Note there's also no getContent(viewId) function anymore, to prevent // from later modifications too. if (content.style.height.isEmpty) { - printWarning('Height of Platform View type: [$viewType] may not be set.' - ' Defaulting to `height: 100%`.\n' - 'Set `style.height` to any appropriate value to stop this message.'); + printWarning( + 'Height of Platform View type: [$viewType] may not be set.' + ' Defaulting to `height: 100%`.\n' + 'Set `style.height` to any appropriate value to stop this message.', + ); content.style.height = '100%'; } if (content.style.width.isEmpty) { - printWarning('Width of Platform View type: [$viewType] may not be set.' - ' Defaulting to `width: 100%`.\n' - 'Set `style.width` to any appropriate value to stop this message.'); + printWarning( + 'Width of Platform View type: [$viewType] may not be set.' + ' Defaulting to `width: 100%`.\n' + 'Set `style.width` to any appropriate value to stop this message.', + ); content.style.width = '100%'; } @@ -222,10 +220,7 @@ class PlatformViewManager { } } -DomElement _defaultFactory( - int viewId, { - Object? params, -}) { +DomElement _defaultFactory(int viewId, {Object? params}) { params!; params as Map; return domDocument.createElement(params.readString('tagName')) diff --git a/lib/web_ui/lib/src/engine/platform_views/message_handler.dart b/lib/web_ui/lib/src/engine/platform_views/message_handler.dart index 86cd7a7e96991..feb83203254f4 100644 --- a/lib/web_ui/lib/src/engine/platform_views/message_handler.dart +++ b/lib/web_ui/lib/src/engine/platform_views/message_handler.dart @@ -40,9 +40,8 @@ typedef PlatformViewContentHandler = void Function(DomElement); /// some extra cleanup of its internal state, but it can do it automatically. See /// [HtmlViewEmbedder.disposeViews]. class PlatformViewMessageHandler { - PlatformViewMessageHandler({ - required PlatformViewManager contentManager, - }) : _contentManager = contentManager; + PlatformViewMessageHandler({required PlatformViewManager contentManager}) + : _contentManager = contentManager; static const String channelName = 'flutter/platform_views'; @@ -77,30 +76,32 @@ class PlatformViewMessageHandler { required Object? params, }) { if (!_contentManager.knowsViewType(platformViewType)) { - callback(_codec.encodeErrorEnvelope( - code: 'unregistered_view_type', - message: 'A HtmlElementView widget is trying to create a platform view ' - 'with an unregistered type: <$platformViewType>.', - details: 'If you are the author of the PlatformView, make sure ' - '`registerViewFactory` is invoked.', - )); + callback( + _codec.encodeErrorEnvelope( + code: 'unregistered_view_type', + message: + 'A HtmlElementView widget is trying to create a platform view ' + 'with an unregistered type: <$platformViewType>.', + details: + 'If you are the author of the PlatformView, make sure ' + '`registerViewFactory` is invoked.', + ), + ); return; } if (_contentManager.knowsViewId(platformViewId)) { - callback(_codec.encodeErrorEnvelope( - code: 'recreating_view', - message: 'trying to create an already created view', - details: 'view id: $platformViewId', - )); + callback( + _codec.encodeErrorEnvelope( + code: 'recreating_view', + message: 'trying to create an already created view', + details: 'view id: $platformViewId', + ), + ); return; } - _contentManager.renderContent( - platformViewType, - platformViewId, - params, - ); + _contentManager.renderContent(platformViewType, platformViewId, params); callback(_codec.encodeSuccessEnvelope(null)); } diff --git a/lib/web_ui/lib/src/engine/platform_views/slots.dart b/lib/web_ui/lib/src/engine/platform_views/slots.dart index b0305834cf9d3..5b6aa64739495 100644 --- a/lib/web_ui/lib/src/engine/platform_views/slots.dart +++ b/lib/web_ui/lib/src/engine/platform_views/slots.dart @@ -41,12 +41,10 @@ String getPlatformViewDomId(int viewId) { DomElement createPlatformViewSlot(int viewId) { final String slotName = getPlatformViewSlotName(viewId); - final DomElement wrapper = domDocument - .createElement('flt-platform-view-slot') - ..style.pointerEvents = 'auto'; + final DomElement wrapper = domDocument.createElement('flt-platform-view-slot') + ..style.pointerEvents = 'auto'; - final DomElement slot = domDocument.createElement('slot') - ..setAttribute('name', slotName); + final DomElement slot = domDocument.createElement('slot')..setAttribute('name', slotName); return wrapper..append(slot); } diff --git a/lib/web_ui/lib/src/engine/pointer_binding.dart b/lib/web_ui/lib/src/engine/pointer_binding.dart index 091212d5407dd..4c8a9b176aab9 100644 --- a/lib/web_ui/lib/src/engine/pointer_binding.dart +++ b/lib/web_ui/lib/src/engine/pointer_binding.dart @@ -69,7 +69,7 @@ int _nthButton(int n) => 0x1 << n; @visibleForTesting int convertButtonToButtons(int button) { assert(button >= 0, 'Unexpected negative button $button.'); - switch(button) { + switch (button) { case 0: return _kPrimaryMouseButton; case 1: @@ -109,8 +109,8 @@ class PointerBinding { this.view, { PointerSupportDetector detector = const PointerSupportDetector(), SafariPointerEventWorkaround? safariWorkaround, - }) : _pointerDataConverter = PointerDataConverter(), - _detector = detector { + }) : _pointerDataConverter = PointerDataConverter(), + _detector = detector { if (isIosSafari) { _safariWorkaround = safariWorkaround ?? _defaultSafariWorkaround; _safariWorkaround!.workAroundMissingPointerEvents(); @@ -122,7 +122,8 @@ class PointerBinding { }()); } - static final SafariPointerEventWorkaround _defaultSafariWorkaround = SafariPointerEventWorkaround._(); + static final SafariPointerEventWorkaround _defaultSafariWorkaround = + SafariPointerEventWorkaround._(); static final ClickDebouncer clickDebouncer = ClickDebouncer(); /// Resets global pointer state that's not tied to any single [PointerBinding] @@ -167,14 +168,10 @@ class PointerBinding { } @visibleForTesting -typedef QueuedEvent = ({ DomEvent event, Duration timeStamp, List data }); +typedef QueuedEvent = ({DomEvent event, Duration timeStamp, List data}); @visibleForTesting -typedef DebounceState = ({ - DomElement target, - Timer timer, - List queue, -}); +typedef DebounceState = ({DomElement target, Timer timer, List queue}); /// Disambiguates taps and clicks that are produced both by the framework from /// `pointerdown`/`pointerup` events and those detected as DOM "click" events by @@ -301,11 +298,7 @@ class ClickDebouncer { } } - void _sendSemanticsTapToFramework( - DomEvent click, - int viewId, - int semanticsNodeId, - ) { + void _sendSemanticsTapToFramework(DomEvent click, int viewId, int semanticsNodeId) { // Tappable nodes can be nested inside other tappable nodes. If a click // lands on an inner element and is allowed to propagate, it will also // land on the ancestor tappable, leading to both the descendant and the @@ -325,14 +318,8 @@ class ClickDebouncer { } void _startDebouncing(DomEvent event, List data) { - assert( - _state == null, - 'Cannot start debouncing. Already debouncing.' - ); - assert( - event.type == 'pointerdown', - 'Click debouncing must begin with a pointerdown' - ); + assert(_state == null, 'Cannot start debouncing. Already debouncing.'); + assert(event.type == 'pointerdown', 'Click debouncing must begin with a pointerdown'); final DomEventTarget? target = event.target; if (target is DomElement && target.hasAttribute('flt-tappable')) { @@ -349,11 +336,13 @@ class ClickDebouncer { // * It was successful at detecting taps generated by all tested // screen readers. timer: Timer(const Duration(milliseconds: 200), _onTimerExpired), - queue: [( - event: event, - timeStamp: _BaseAdapter._eventTimeStampToDuration(event.timeStamp!), - data: data, - )], + queue: [ + ( + event: event, + timeStamp: _BaseAdapter._eventTimeStampToDuration(event.timeStamp!), + data: data, + ), + ], ); } else { // The event landed on an non-tappable target. Assume this won't lead to @@ -365,7 +354,7 @@ class ClickDebouncer { void _debounce(DomEvent event, List data) { assert( _state != null, - 'Cannot debounce event. Debouncing state not established by _startDebouncing.' + 'Cannot debounce event. Debouncing state not established by _startDebouncing.', ); final DebounceState state = _state!; @@ -429,7 +418,7 @@ class ClickDebouncer { void _sendToFramework(DomEvent? event, List data) { final ui.PointerDataPacket packet = ui.PointerDataPacket(data: data.toList()); if (_debugLogFlutterEvents) { - for(final ui.PointerData datum in data) { + for (final ui.PointerData datum in data) { print('fw:${datum.change} ${datum.physicalX},${datum.physicalY}'); } } @@ -459,11 +448,7 @@ class PointerSupportDetector { /// Encapsulates a DomEvent registration so it can be easily unregistered later. @visibleForTesting class Listener { - Listener._({ - required this.event, - required this.target, - required this.handler, - }); + Listener._({required this.event, required this.target, required this.handler}); /// Registers a listener for the given `event` on a `target`. /// @@ -485,17 +470,11 @@ class Listener { if (passive == null) { target.addEventListener(event, jsHandler); } else { - final Map eventOptions = { - 'passive': passive, - }; + final Map eventOptions = {'passive': passive}; target.addEventListenerWithOptions(event, jsHandler, eventOptions); } - final Listener listener = Listener._( - event: event, - target: target, - handler: jsHandler, - ); + final Listener listener = Listener._(event: event, target: target, handler: jsHandler); return listener; } @@ -548,19 +527,17 @@ abstract class _BaseAdapter { /// as the [target], while move and up events should use [domWindow] /// instead, because the browser doesn't fire the latter two for DOM elements /// when the pointer is outside the window. - void addEventListener( - DomEventTarget target, - String eventName, - DartDomEventListener handler, - ) { + void addEventListener(DomEventTarget target, String eventName, DartDomEventListener handler) { JSVoid loggedHandler(DomEvent event) { if (_debugLogPointerEvents) { if (domInstanceOfString(event, 'PointerEvent')) { final DomPointerEvent pointerEvent = event as DomPointerEvent; final ui.Offset offset = computeEventOffsetToTarget(event, _view); - print('${pointerEvent.type} ' - '${offset.dx.toStringAsFixed(1)},' - '${offset.dy.toStringAsFixed(1)}'); + print( + '${pointerEvent.type} ' + '${offset.dx.toStringAsFixed(1)},' + '${offset.dy.toStringAsFixed(1)}', + ); } else { print(event.type); } @@ -572,19 +549,15 @@ abstract class _BaseAdapter { handler(event); } } - _listeners.add(Listener.register( - event: eventName, - target: target, - handler: loggedHandler, - )); + + _listeners.add(Listener.register(event: eventName, target: target, handler: loggedHandler)); } /// Converts a floating number timestamp (in milliseconds) to a [Duration] by /// splitting it into two integer components: milliseconds + microseconds. static Duration _eventTimeStampToDuration(num milliseconds) { final int ms = milliseconds.toInt(); - final int micro = - ((milliseconds - ms) * Duration.microsecondsPerMillisecond).toInt(); + final int micro = ((milliseconds - ms) * Duration.microsecondsPerMillisecond).toInt(); return Duration(milliseconds: ms, microseconds: micro); } } @@ -657,9 +630,7 @@ mixin _WheelEventListenerMixin on _BaseAdapter { return true; } - List _convertWheelEventToPointerData( - DomWheelEvent event - ) { + List _convertWheelEventToPointerData(DomWheelEvent event) { const int domDeltaPixel = 0x00; const int domDeltaLine = 0x01; const int domDeltaPage = 0x02; @@ -698,8 +669,9 @@ mixin _WheelEventListenerMixin on _BaseAdapter { final ui.Offset offset = computeEventOffsetToTarget(event, _view); bool ignoreCtrlKey = false; if (ui_web.browser.operatingSystem == ui_web.OperatingSystem.macOs) { - ignoreCtrlKey = (_keyboardConverter?.keyIsPressed(kPhysicalControlLeft) ?? false) || - (_keyboardConverter?.keyIsPressed(kPhysicalControlRight) ?? false); + ignoreCtrlKey = + (_keyboardConverter?.keyIsPressed(kPhysicalControlLeft) ?? false) || + (_keyboardConverter?.keyIsPressed(kPhysicalControlRight) ?? false); } if (event.ctrlKey && !ignoreCtrlKey) { _pointerDataConverter.convert( @@ -745,12 +717,9 @@ mixin _WheelEventListenerMixin on _BaseAdapter { } void _addWheelEventListener(DartDomEventListener handler) { - _listeners.add(Listener.register( - event: 'wheel', - target: _viewTarget, - handler: handler, - passive: false, - )); + _listeners.add( + Listener.register(event: 'wheel', target: _viewTarget, handler: handler, passive: false), + ); } void _handleWheelEvent(DomEvent event) { @@ -778,14 +747,14 @@ mixin _WheelEventListenerMixin on _BaseAdapter { const double kFallbackFontHeight = 16.0; final DomHTMLDivElement probe = createDomHTMLDivElement(); probe.style - ..fontSize = 'initial' - ..display = 'none'; + ..fontSize = 'initial' + ..display = 'none'; domDocument.body!.append(probe); String fontSize = domWindow.getComputedStyle(probe).fontSize; double? res; if (fontSize.contains('px')) { - fontSize = fontSize.replaceAll('px', ''); - res = double.tryParse(fontSize); + fontSize = fontSize.replaceAll('px', ''); + res = double.tryParse(fontSize); } probe.remove(); return res == null ? kFallbackFontHeight : res / 4.0; @@ -794,10 +763,7 @@ mixin _WheelEventListenerMixin on _BaseAdapter { @immutable class _SanitizedDetails { - const _SanitizedDetails({ - required this.buttons, - required this.change, - }); + const _SanitizedDetails({required this.buttons, required this.change}); final ui.PointerChange change; final int buttons; @@ -828,10 +794,7 @@ class _ButtonSanitizer { return _htmlButtonsToFlutterButtons(buttons); } - _SanitizedDetails sanitizeDownEvent({ - required int button, - required int buttons, - }) { + _SanitizedDetails sanitizeDownEvent({required int button, required int buttons}) { // If the pointer is already down, we just send a move event with the new // `buttons` value. if (_pressedButtons != 0) { @@ -840,10 +803,7 @@ class _ButtonSanitizer { _pressedButtons = _inferDownFlutterButtons(button, buttons); - return _SanitizedDetails( - change: ui.PointerChange.down, - buttons: _pressedButtons, - ); + return _SanitizedDetails(change: ui.PointerChange.down, buttons: _pressedButtons); } _SanitizedDetails sanitizeMoveEvent({required int buttons}) { @@ -852,18 +812,13 @@ class _ButtonSanitizer { // The brower sends a move event with `buttons:2` even though there's no // buttons down yet. if (_pressedButtons == 0 && newPressedButtons != 0) { - return _SanitizedDetails( - change: ui.PointerChange.hover, - buttons: _pressedButtons, - ); + return _SanitizedDetails(change: ui.PointerChange.hover, buttons: _pressedButtons); } _pressedButtons = newPressedButtons; return _SanitizedDetails( - change: _pressedButtons == 0 - ? ui.PointerChange.hover - : ui.PointerChange.move, + change: _pressedButtons == 0 ? ui.PointerChange.hover : ui.PointerChange.move, buttons: _pressedButtons, ); } @@ -874,10 +829,7 @@ class _ButtonSanitizer { // event was received because context menu was shown. if (_pressedButtons != 0 && newPressedButtons == 0) { _pressedButtons = 0; - return _SanitizedDetails( - change: ui.PointerChange.up, - buttons: _pressedButtons, - ); + return _SanitizedDetails(change: ui.PointerChange.up, buttons: _pressedButtons); } return null; } @@ -890,10 +842,7 @@ class _ButtonSanitizer { if (newPressedButtons == 0) { _pressedButtons = 0; - return _SanitizedDetails( - change: ui.PointerChange.hover, - buttons: _pressedButtons, - ); + return _SanitizedDetails(change: ui.PointerChange.hover, buttons: _pressedButtons); } return null; @@ -910,26 +859,17 @@ class _ButtonSanitizer { if (_pressedButtons == 0) { // All buttons have been released. - return _SanitizedDetails( - change: ui.PointerChange.up, - buttons: _pressedButtons, - ); + return _SanitizedDetails(change: ui.PointerChange.up, buttons: _pressedButtons); } else { // There are still some unreleased buttons, we shouldn't send an up event // yet. Instead we send a move event to update the position of the pointer. - return _SanitizedDetails( - change: ui.PointerChange.move, - buttons: _pressedButtons, - ); + return _SanitizedDetails(change: ui.PointerChange.move, buttons: _pressedButtons); } } _SanitizedDetails sanitizeCancelEvent() { _pressedButtons = 0; - return _SanitizedDetails( - change: ui.PointerChange.cancel, - buttons: _pressedButtons, - ); + return _SanitizedDetails(change: ui.PointerChange.cancel, buttons: _pressedButtons); } } @@ -996,16 +936,16 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { final int device = _getPointerId(event); final List pointerData = []; final _ButtonSanitizer sanitizer = _ensureSanitizer(device); - final _SanitizedDetails? up = - sanitizer.sanitizeMissingRightClickUp(buttons: event.buttons!.toInt()); + final _SanitizedDetails? up = sanitizer.sanitizeMissingRightClickUp( + buttons: event.buttons!.toInt(), + ); if (up != null) { _convertEventsToPointerData(data: pointerData, event: event, details: up); } - final _SanitizedDetails down = - sanitizer.sanitizeDownEvent( - button: event.button.toInt(), - buttons: event.buttons!.toInt(), - ); + final _SanitizedDetails down = sanitizer.sanitizeDownEvent( + button: event.button.toInt(), + buttons: event.buttons!.toInt(), + ); _convertEventsToPointerData(data: pointerData, event: event, details: down); _callback(event, pointerData); @@ -1045,7 +985,9 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { final List pointerData = []; final List expandedEvents = _expandEvents(moveEvent); for (final DomPointerEvent event in expandedEvents) { - final _SanitizedDetails? up = sanitizer.sanitizeMissingRightClickUp(buttons: event.buttons!.toInt()); + final _SanitizedDetails? up = sanitizer.sanitizeMissingRightClickUp( + buttons: event.buttons!.toInt(), + ); if (up != null) { _convertEventsToPointerData( data: pointerData, @@ -1071,7 +1013,9 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { final int device = _getPointerId(event); final _ButtonSanitizer sanitizer = _ensureSanitizer(device); final List pointerData = []; - final _SanitizedDetails? details = sanitizer.sanitizeLeaveEvent(buttons: event.buttons!.toInt()); + final _SanitizedDetails? details = sanitizer.sanitizeLeaveEvent( + buttons: event.buttons!.toInt(), + ); if (details != null) { _convertEventsToPointerData(data: pointerData, event: event, details: details); _callback(event, pointerData); @@ -1083,7 +1027,9 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { final int device = _getPointerId(event); if (_hasSanitizer(device)) { final List pointerData = []; - final _SanitizedDetails? details = _getSanitizer(device).sanitizeUpEvent(buttons: event.buttons?.toInt()); + final _SanitizedDetails? details = _getSanitizer( + device, + ).sanitizeUpEvent(buttons: event.buttons?.toInt()); _removePointerIfUnhoverable(event); if (details != null) { _convertEventsToPointerData(data: pointerData, event: event, details: details); @@ -1140,7 +1086,7 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { physicalX: offset.dx * _view.devicePixelRatio, physicalY: offset.dy * _view.devicePixelRatio, buttons: details.buttons, - pressure: pressure == null ? 0.0 : pressure.toDouble(), + pressure: pressure == null ? 0.0 : pressure.toDouble(), pressureMax: 1.0, tilt: tilt, ); @@ -1182,23 +1128,19 @@ class _PointerAdapter extends _BaseAdapter with _WheelEventListenerMixin { // All mouse pointer events are given `_mouseDeviceId`, including wheel // events, because wheel events might come before any other PointerEvents, // and wheel PointerEvents don't contain pointerIds. - return switch(_pointerTypeToDeviceKind(event.pointerType!)) { + return switch (_pointerTypeToDeviceKind(event.pointerType!)) { ui.PointerDeviceKind.mouse => _mouseDeviceId, - ui.PointerDeviceKind.stylus || - ui.PointerDeviceKind.invertedStylus => _stylusDeviceId, + ui.PointerDeviceKind.stylus || ui.PointerDeviceKind.invertedStylus => _stylusDeviceId, // Trackpad processing doesn't call this function. ui.PointerDeviceKind.trackpad => throw Exception('Unreachable'), - ui.PointerDeviceKind.touch || - ui.PointerDeviceKind.unknown => event.pointerId!.toInt(), + ui.PointerDeviceKind.touch || ui.PointerDeviceKind.unknown => event.pointerId!.toInt(), }; } /// Tilt angle is -90 to + 90. Take maximum deflection and convert to radians. double _computeHighestTilt(DomPointerEvent e) => - (e.tiltX!.abs() > e.tiltY!.abs() ? e.tiltX : e.tiltY)! / - 180.0 * - math.pi; + (e.tiltX!.abs() > e.tiltY!.abs() ? e.tiltX : e.tiltY)! / 180.0 * math.pi; } diff --git a/lib/web_ui/lib/src/engine/pointer_binding/event_position_helper.dart b/lib/web_ui/lib/src/engine/pointer_binding/event_position_helper.dart index 82954325ab85e..6a210c9a0114c 100644 --- a/lib/web_ui/lib/src/engine/pointer_binding/event_position_helper.dart +++ b/lib/web_ui/lib/src/engine/pointer_binding/event_position_helper.dart @@ -82,11 +82,18 @@ ui.Offset _computeOffsetForInputs( ) { final DomElement targetElement = eventTarget as DomElement; final DomHTMLElement domElement = textEditing.strategy.activeDomElement; - assert(targetElement == domElement, 'The targeted input element must be the active input element'); + assert( + targetElement == domElement, + 'The targeted input element must be the active input element', + ); final Float32List transformValues = inputGeometry.globalTransform; assert(transformValues.length == 16); final Matrix4 transform = Matrix4.fromFloat32List(transformValues); - final Vector3 transformedPoint = transform.perspectiveTransform(x: event.offsetX, y: event.offsetY, z: 0); + final Vector3 transformedPoint = transform.perspectiveTransform( + x: event.offsetX, + y: event.offsetY, + z: 0, + ); return ui.Offset(transformedPoint.x, transformedPoint.y); } @@ -129,7 +136,7 @@ ui.Offset _computeOffsetForTalkbackEvent(DomMouseEvent event, DomElement actualT double offsetY = event.clientY; // Compute the scroll offset of actualTarget DomHTMLElement parent = actualTarget as DomHTMLElement; - while(parent.offsetParent != null){ + while (parent.offsetParent != null) { offsetX -= parent.offsetLeft - parent.scrollLeft; offsetY -= parent.offsetTop - parent.scrollTop; parent = parent.offsetParent!; diff --git a/lib/web_ui/lib/src/engine/pointer_converter.dart b/lib/web_ui/lib/src/engine/pointer_converter.dart index e52b50232f820..e496e2232aefb 100644 --- a/lib/web_ui/lib/src/engine/pointer_converter.dart +++ b/lib/web_ui/lib/src/engine/pointer_converter.dart @@ -45,10 +45,7 @@ class _GlobalPointerState { int activeButtons = 0; _PointerDeviceState ensurePointerDeviceState(int device, double x, double y) { - return pointers.putIfAbsent( - device, - () => _PointerDeviceState(x, y), - ); + return pointers.putIfAbsent(device, () => _PointerDeviceState(x, y)); } /// Resets all pointer states. @@ -271,8 +268,7 @@ class PointerDataConverter { print('>> view=$viewId device=$device change=$change buttons=$buttons'); } final bool isDown = buttons != 0; - if (signalKind == null || - signalKind == ui.PointerSignalKind.none) { + if (signalKind == null || signalKind == ui.PointerSignalKind.none) { switch (change) { case ui.PointerChange.add: assert(!globalPointerState.pointers.containsKey(device)); @@ -306,7 +302,7 @@ class PointerDataConverter { scrollDeltaX: scrollDeltaX, scrollDeltaY: scrollDeltaY, scale: scale, - ) + ), ); case ui.PointerChange.hover: final bool alreadyAdded = globalPointerState.pointers.containsKey(device); @@ -341,7 +337,7 @@ class PointerDataConverter { scrollDeltaX: scrollDeltaX, scrollDeltaY: scrollDeltaY, scale: scale, - ) + ), ); } result.add( @@ -372,13 +368,16 @@ class PointerDataConverter { scrollDeltaX: scrollDeltaX, scrollDeltaY: scrollDeltaY, scale: scale, - ) + ), ); globalPointerState.activeButtons = buttons; case ui.PointerChange.down: final bool alreadyAdded = globalPointerState.pointers.containsKey(device); final _PointerDeviceState state = globalPointerState.ensurePointerDeviceState( - device, physicalX, physicalY); + device, + physicalX, + physicalY, + ); assert(isDown); state.startNewPointer(); if (!alreadyAdded) { @@ -410,7 +409,7 @@ class PointerDataConverter { scrollDeltaX: scrollDeltaX, scrollDeltaY: scrollDeltaY, scale: scale, - ) + ), ); } if (_locationHasChanged(device, physicalX, physicalY)) { @@ -444,7 +443,7 @@ class PointerDataConverter { scrollDeltaX: scrollDeltaX, scrollDeltaY: scrollDeltaY, scale: scale, - ) + ), ); } result.add( @@ -475,7 +474,7 @@ class PointerDataConverter { scrollDeltaX: scrollDeltaX, scrollDeltaY: scrollDeltaY, scale: scale, - ) + ), ); globalPointerState.activeButtons = buttons; case ui.PointerChange.move: @@ -509,7 +508,7 @@ class PointerDataConverter { scrollDeltaX: scrollDeltaX, scrollDeltaY: scrollDeltaY, scale: scale, - ) + ), ); globalPointerState.activeButtons = buttons; case ui.PointerChange.up: @@ -556,7 +555,7 @@ class PointerDataConverter { scrollDeltaX: scrollDeltaX, scrollDeltaY: scrollDeltaY, scale: scale, - ) + ), ); } result.add( @@ -587,7 +586,7 @@ class PointerDataConverter { scrollDeltaX: scrollDeltaX, scrollDeltaY: scrollDeltaY, scale: scale, - ) + ), ); if (kind == ui.PointerDeviceKind.touch) { // The browser sends a new device ID for each touch gesture. To @@ -620,7 +619,7 @@ class PointerDataConverter { scrollDeltaX: scrollDeltaX, scrollDeltaY: scrollDeltaY, scale: scale, - ) + ), ); globalPointerState.pointers.remove(device); } @@ -656,7 +655,7 @@ class PointerDataConverter { scrollDeltaX: scrollDeltaX, scrollDeltaY: scrollDeltaY, scale: scale, - ) + ), ); globalPointerState.pointers.remove(device); case ui.PointerChange.panZoomStart: @@ -701,7 +700,7 @@ class PointerDataConverter { scrollDeltaX: scrollDeltaX, scrollDeltaY: scrollDeltaY, scale: scale, - ) + ), ); } if (_locationHasChanged(device, physicalX, physicalY)) { @@ -737,7 +736,7 @@ class PointerDataConverter { scrollDeltaX: scrollDeltaX, scrollDeltaY: scrollDeltaY, scale: scale, - ) + ), ); } else { result.add( @@ -767,7 +766,7 @@ class PointerDataConverter { scrollDeltaX: scrollDeltaX, scrollDeltaY: scrollDeltaY, scale: scale, - ) + ), ); } } @@ -800,12 +799,12 @@ class PointerDataConverter { scrollDeltaY: scrollDeltaY, scale: scale, onRespond: onRespond, - ) + ), ); case ui.PointerSignalKind.none: assert(false); // This branch should already have 'none' filtered out. case ui.PointerSignalKind.unknown: - // Ignore unknown signals. + // Ignore unknown signals. break; } } diff --git a/lib/web_ui/lib/src/engine/profiler.dart b/lib/web_ui/lib/src/engine/profiler.dart index 5a5c137671821..dcca0710374ff 100644 --- a/lib/web_ui/lib/src/engine/profiler.dart +++ b/lib/web_ui/lib/src/engine/profiler.dart @@ -69,9 +69,7 @@ class Profiler { _checkBenchmarkMode(); } - static bool isBenchmarkMode = const bool.fromEnvironment( - 'FLUTTER_WEB_ENABLE_PROFILING', - ); + static bool isBenchmarkMode = const bool.fromEnvironment('FLUTTER_WEB_ENABLE_PROFILING'); static Profiler ensureInitialized() { _checkBenchmarkMode(); @@ -149,9 +147,8 @@ class Instrumentation { _enabled = value; } - static bool _enabled = const bool.fromEnvironment( - 'FLUTTER_WEB_ENABLE_INSTRUMENTATION', - ); + + static bool _enabled = const bool.fromEnvironment('FLUTTER_WEB_ENABLE_INSTRUMENTATION'); /// Returns the singleton that provides instrumentation API. static Instrumentation get instance { @@ -184,24 +181,21 @@ class Instrumentation { _checkInstrumentationEnabled(); final int currentCount = _counters[event] ?? 0; _counters[event] = currentCount + 1; - _printTimer ??= Timer( - const Duration(seconds: 2), - () { - if (_printTimer == null || !_enabled) { - return; - } - final StringBuffer message = StringBuffer('Engine counters:\n'); - // Entries are sorted for readability and testability. - final List> entries = _counters.entries.toList() - ..sort((MapEntry a, MapEntry b) { + _printTimer ??= Timer(const Duration(seconds: 2), () { + if (_printTimer == null || !_enabled) { + return; + } + final StringBuffer message = StringBuffer('Engine counters:\n'); + // Entries are sorted for readability and testability. + final List> entries = + _counters.entries.toList()..sort((MapEntry a, MapEntry b) { return a.key.compareTo(b.key); }); - for (final MapEntry entry in entries) { - message.writeln(' ${entry.key}: ${entry.value}'); - } - print(message); - _printTimer = null; - }, - ); + for (final MapEntry entry in entries) { + message.writeln(' ${entry.key}: ${entry.value}'); + } + print(message); + _printTimer = null; + }); } } diff --git a/lib/web_ui/lib/src/engine/raw_keyboard.dart b/lib/web_ui/lib/src/engine/raw_keyboard.dart index 2f09387c7cbe7..6c47f42b772c8 100644 --- a/lib/web_ui/lib/src/engine/raw_keyboard.dart +++ b/lib/web_ui/lib/src/engine/raw_keyboard.dart @@ -6,7 +6,7 @@ import 'dart:async'; import 'dart:typed_data'; import 'package:ui/ui_web/src/ui_web.dart' as ui_web; -import '../engine.dart' show registerHotRestartListener; +import '../engine.dart' show registerHotRestartListener; import 'dom.dart'; import 'keyboard_binding.dart'; import 'platform_dispatcher.dart'; @@ -120,7 +120,8 @@ class RawKeyboard { _lastMetaState |= modifierNumLock; } else if (event.key == 'ScrollLock') { _lastMetaState |= modifierScrollLock; - } else if (event.key == 'Meta' && ui_web.browser.operatingSystem == ui_web.OperatingSystem.linux) { + } else if (event.key == 'Meta' && + ui_web.browser.operatingSystem == ui_web.OperatingSystem.linux) { // On Chrome Linux, metaState can be wrong when a Meta key is pressed. _lastMetaState |= _modifierMeta; } else if (event.code == 'MetaLeft' && event.key == 'Process') { @@ -139,12 +140,15 @@ class RawKeyboard { 'keyCode': event.keyCode, }; - EnginePlatformDispatcher.instance.invokeOnPlatformMessage('flutter/keyevent', - _messageCodec.encodeMessage(eventData), (ByteData? data) { + EnginePlatformDispatcher.instance.invokeOnPlatformMessage( + 'flutter/keyevent', + _messageCodec.encodeMessage(eventData), + (ByteData? data) { if (data == null) { return; } - final Map jsonResponse = _messageCodec.decodeMessage(data) as Map; + final Map jsonResponse = + _messageCodec.decodeMessage(data) as Map; if (jsonResponse['handled'] as bool) { // If the framework handled it, then don't propagate it any further. event.preventDefault(); @@ -165,8 +169,11 @@ class RawKeyboard { 'keyCode': event.keyCode, }; - EnginePlatformDispatcher.instance.invokeOnPlatformMessage('flutter/keyevent', - _messageCodec.encodeMessage(eventData), _noopCallback); + EnginePlatformDispatcher.instance.invokeOnPlatformMessage( + 'flutter/keyevent', + _messageCodec.encodeMessage(eventData), + _noopCallback, + ); } /// After a keydown is received, this is the duration we wait for a repeat event diff --git a/lib/web_ui/lib/src/engine/rrect_renderer.dart b/lib/web_ui/lib/src/engine/rrect_renderer.dart index 91ead1328414e..ab84af3c9314f 100644 --- a/lib/web_ui/lib/src/engine/rrect_renderer.dart +++ b/lib/web_ui/lib/src/engine/rrect_renderer.dart @@ -28,8 +28,7 @@ abstract class RRectRenderer { // rounded rectangle (after the corner). // TODO(het): Confirm that this is the end point in Flutter for RRect - void render(ui.RRect inputRRect, - {bool startNewPath = true, bool reverse = false}) { + void render(ui.RRect inputRRect, {bool startNewPath = true, bool reverse = false}) { // Ensure border radius curves never overlap final ui.RRect rrect = inputRRect.scaleRadii(); @@ -177,8 +176,16 @@ abstract class RRectRenderer { void beginPath(); void moveTo(double x, double y); void lineTo(double x, double y); - void ellipse(double centerX, double centerY, double radiusX, double radiusY, - double rotation, double startAngle, double endAngle, bool antiClockwise); + void ellipse( + double centerX, + double centerY, + double radiusX, + double radiusY, + double rotation, + double startAngle, + double endAngle, + bool antiClockwise, + ); } /// Renders RRect to a 2d canvas. @@ -201,10 +208,27 @@ class RRectToCanvasRenderer extends RRectRenderer { } @override - void ellipse(double centerX, double centerY, double radiusX, double radiusY, - double rotation, double startAngle, double endAngle, bool antiClockwise) { - drawEllipse(context, centerX, centerY, radiusX, radiusY, rotation, startAngle, - endAngle, antiClockwise); + void ellipse( + double centerX, + double centerY, + double radiusX, + double radiusY, + double rotation, + double startAngle, + double endAngle, + bool antiClockwise, + ) { + drawEllipse( + context, + centerX, + centerY, + radiusX, + radiusY, + rotation, + startAngle, + endAngle, + antiClockwise, + ); } } @@ -226,17 +250,35 @@ class RRectToPathRenderer extends RRectRenderer { } @override - void ellipse(double centerX, double centerY, double radiusX, double radiusY, - double rotation, double startAngle, double endAngle, bool antiClockwise) { + void ellipse( + double centerX, + double centerY, + double radiusX, + double radiusY, + double rotation, + double startAngle, + double endAngle, + bool antiClockwise, + ) { path.addArc( - ui.Rect.fromLTRB(centerX - radiusX, centerY - radiusY, - centerX + radiusX, centerY + radiusY), - startAngle, - antiClockwise ? startAngle - endAngle : endAngle - startAngle); + ui.Rect.fromLTRB(centerX - radiusX, centerY - radiusY, centerX + radiusX, centerY + radiusY), + startAngle, + antiClockwise ? startAngle - endAngle : endAngle - startAngle, + ); } } -typedef RRectRendererEllipseCallback = void Function(double centerX, double centerY, double radiusX, double radiusY, double rotation, double startAngle, double endAngle, bool antiClockwise); +typedef RRectRendererEllipseCallback = + void Function( + double centerX, + double centerY, + double radiusX, + double radiusY, + double rotation, + double startAngle, + double endAngle, + bool antiClockwise, + ); typedef RRectRendererCallback = void Function(double x, double y); /// Converts RRect to path primitives with callbacks. @@ -250,8 +292,25 @@ class RRectMetricsRenderer extends RRectRenderer { void beginPath() {} @override - void ellipse(double centerX, double centerY, double radiusX, double radiusY, double rotation, double startAngle, double endAngle, bool antiClockwise) => ellipseCallback!( - centerX, centerY, radiusX, radiusY, rotation, startAngle, endAngle, antiClockwise); + void ellipse( + double centerX, + double centerY, + double radiusX, + double radiusY, + double rotation, + double startAngle, + double endAngle, + bool antiClockwise, + ) => ellipseCallback!( + centerX, + centerY, + radiusX, + radiusY, + rotation, + startAngle, + endAngle, + antiClockwise, + ); @override void lineTo(double x, double y) => lineToCallback!(x, y); diff --git a/lib/web_ui/lib/src/engine/safe_browser_api.dart b/lib/web_ui/lib/src/engine/safe_browser_api.dart index 59d9f7a07e174..c2b586f3e784b 100644 --- a/lib/web_ui/lib/src/engine/safe_browser_api.dart +++ b/lib/web_ui/lib/src/engine/safe_browser_api.dart @@ -39,10 +39,7 @@ T getJsProperty(Object object, String name) { return js_util.getProperty(object, name); } -const Set _safeJsProperties = { - 'decoding', - '__flutter_state', -}; +const Set _safeJsProperties = {'decoding', '__flutter_state'}; /// Sets the value of property [name] on a JavaScript [object]. /// @@ -89,8 +86,7 @@ num? parseFloat(String source) { /// This getter calls the `hasFocus` method of the `Document` interface. /// See for more details: /// https://developer.mozilla.org/en-US/docs/Web/API/Document/hasFocus -bool get windowHasFocus => - js_util.callMethod(domDocument, 'hasFocus', []); +bool get windowHasFocus => js_util.callMethod(domDocument, 'hasFocus', []); /// Parses the font size of [element] and returns the value without a unit. num? parseFontSize(DomElement element) { @@ -98,11 +94,15 @@ num? parseFontSize(DomElement element) { if (hasJsProperty(element, 'computedStyleMap')) { // Use the newer `computedStyleMap` API available on some browsers. - final Object? computedStyleMap = - js_util.callMethod(element, 'computedStyleMap', const []); + final Object? computedStyleMap = js_util.callMethod( + element, + 'computedStyleMap', + const [], + ); if (computedStyleMap is Object) { - final Object? fontSizeObject = - js_util.callMethod(computedStyleMap, 'get', ['font-size']); + final Object? fontSizeObject = js_util.callMethod(computedStyleMap, 'get', [ + 'font-size', + ]); if (fontSizeObject is Object) { fontSize = js_util.getProperty(fontSizeObject, 'value'); } @@ -111,8 +111,7 @@ num? parseFontSize(DomElement element) { if (fontSize == null) { // Fallback to `getComputedStyle`. - final String fontSizeString = - domWindow.getComputedStyle(element).getPropertyValue('font-size'); + final String fontSizeString = domWindow.getComputedStyle(element).getPropertyValue('font-size'); fontSize = parseFloat(fontSizeString); } @@ -156,8 +155,7 @@ DomCanvasElement? tryCreateCanvasElement(int width, int height) { @JS('window.ImageDecoder') external JSAny? get __imageDecoderConstructor; -Object? get _imageDecoderConstructor => - __imageDecoderConstructor?.toObjectShallow; +Object? get _imageDecoderConstructor => __imageDecoderConstructor?.toObjectShallow; /// Environment variable that allows the developer to opt out of using browser's /// `ImageDecoder` API, and use the WASM codecs bundled with CanvasKit. @@ -263,9 +261,7 @@ extension DecodeResultExtension on DecodeResult { @anonymous @staticInterop class DecodeOptions { - external factory DecodeOptions({ - required JSNumber frameIndex, - }); + external factory DecodeOptions({required JSNumber frameIndex}); } /// The only frame in a static image, or one of the frames in an animated one. @@ -287,8 +283,7 @@ extension VideoFrameExtension on VideoFrame { @JS('copyTo') external JSPromise _copyTo(JSAny destination); - JSPromise copyTo(Object destination) => - _copyTo(destination.toJSAnyShallow); + JSPromise copyTo(Object destination) => _copyTo(destination.toJSAnyShallow); @JS('format') external JSString? get _format; @@ -358,11 +353,7 @@ void scaleCanvas2D(Object context2d, num x, num y) { } void drawImageCanvas2D(Object context2d, Object imageSource, num width, num height) { - js_util.callMethod(context2d, 'drawImage', [ - imageSource, - width, - height, - ]); + js_util.callMethod(context2d, 'drawImage', [imageSource, width, height]); } void vertexAttribPointerGlContext( @@ -396,20 +387,24 @@ class GlContext { return OffScreenCanvas.supported ? GlContext._fromOffscreenCanvas(offScreenCanvas.offScreenCanvas!) : GlContext._fromCanvasElement( - offScreenCanvas.canvasElement!, webGLVersion == WebGLVersion.webgl1); + offScreenCanvas.canvasElement!, + webGLVersion == WebGLVersion.webgl1, + ); } GlContext._fromOffscreenCanvas(DomOffscreenCanvas canvas) - : glContext = canvas.getContext('webgl2', {'premultipliedAlpha': false})!, - isOffscreen = true { + : glContext = canvas.getContext('webgl2', {'premultipliedAlpha': false})!, + isOffscreen = true { _programCache = {}; _canvas = canvas; } GlContext._fromCanvasElement(DomCanvasElement canvas, bool useWebGl1) - : glContext = canvas.getContext(useWebGl1 ? 'webgl' : 'webgl2', - {'premultipliedAlpha': false})!, - isOffscreen = false { + : glContext = + canvas.getContext(useWebGl1 ? 'webgl' : 'webgl2', { + 'premultipliedAlpha': false, + })!, + isOffscreen = false { _programCache = {}; _canvas = canvas; } @@ -448,24 +443,29 @@ class GlContext { } /// Draws Gl context contents to canvas context. - void drawImage(DomCanvasRenderingContext2D context, - double left, double top) { + void drawImage(DomCanvasRenderingContext2D context, double left, double top) { // Actual size of canvas may be larger than viewport size. Use // source/destination to draw part of the image data. - js_util.callMethod(context, 'drawImage', - [_canvas, 0, 0, _widthInPixels, _heightInPixels, - left, top, _widthInPixels, _heightInPixels]); + js_util.callMethod(context, 'drawImage', [ + _canvas, + 0, + 0, + _widthInPixels, + _heightInPixels, + left, + top, + _widthInPixels, + _heightInPixels, + ]); } - GlProgram cacheProgram( - String vertexShaderSource, String fragmentShaderSource) { + GlProgram cacheProgram(String vertexShaderSource, String fragmentShaderSource) { final String cacheKey = '$vertexShaderSource||$fragmentShaderSource'; GlProgram? cachedProgram = _programCache[cacheKey]; if (cachedProgram == null) { // Create and compile shaders. final Object vertexShader = compileShader('VERTEX_SHADER', vertexShaderSource); - final Object fragmentShader = - compileShader('FRAGMENT_SHADER', fragmentShaderSource); + final Object fragmentShader = compileShader('FRAGMENT_SHADER', fragmentShaderSource); // Create a gl program and link shaders. final Object program = createProgram(); attachShader(program, vertexShader); @@ -484,16 +484,16 @@ class GlContext { } js_util.callMethod(glContext, 'shaderSource', [shader, source]); js_util.callMethod(glContext, 'compileShader', [shader]); - final bool shaderStatus = js_util.callMethod( - glContext, - 'getShaderParameter', - [shader, compileStatus], - ); + final bool shaderStatus = js_util.callMethod(glContext, 'getShaderParameter', [ + shader, + compileStatus, + ]); if (!shaderStatus) { throw Exception('Shader compilation failed: ${getShaderInfoLog(shader)}'); } return shader; } + Object createProgram() => js_util.callMethod(glContext, 'createProgram', const []); @@ -503,11 +503,10 @@ class GlContext { void linkProgram(Object program) { js_util.callMethod(glContext, 'linkProgram', [program]); - final bool programStatus = js_util.callMethod( - glContext, - 'getProgramParameter', - [program, kLinkStatus], - ); + final bool programStatus = js_util.callMethod(glContext, 'getProgramParameter', [ + program, + kLinkStatus, + ]); if (!programStatus) { throw Exception(getProgramInfoLog(program)); } @@ -517,8 +516,7 @@ class GlContext { js_util.callMethod(glContext, 'useProgram', [program.program]); } - Object? createBuffer() => - js_util.callMethod(glContext, 'createBuffer', const []); + Object? createBuffer() => js_util.callMethod(glContext, 'createBuffer', const []); void bindArrayBuffer(Object? buffer) { js_util.callMethod(glContext, 'bindBuffer', [kArrayBuffer, buffer]); @@ -528,21 +526,18 @@ class GlContext { js_util.callMethod(glContext, 'createVertexArray', const []); void bindVertexArray(Object vertexObjectArray) { - js_util.callMethod(glContext, 'bindVertexArray', - [vertexObjectArray]); + js_util.callMethod(glContext, 'bindVertexArray', [vertexObjectArray]); } void unbindVertexArray() { - js_util.callMethod(glContext, 'bindVertexArray', - [null]); + js_util.callMethod(glContext, 'bindVertexArray', [null]); } void bindElementArrayBuffer(Object? buffer) { js_util.callMethod(glContext, 'bindBuffer', [kElementArrayBuffer, buffer]); } - Object? createTexture() => - js_util.callMethod(glContext, 'createTexture', const []); + Object? createTexture() => js_util.callMethod(glContext, 'createTexture', const []); void generateMipmap(dynamic target) => js_util.callMethod(glContext, 'generateMipmap', [target]); @@ -555,22 +550,43 @@ class GlContext { js_util.callMethod(glContext, 'activeTexture', [textureUnit]); } - void texImage2D(dynamic target, int level, dynamic internalFormat, - dynamic format, dynamic dataType, - dynamic pixels, {int? width, int? height, int border = 0}) { + void texImage2D( + dynamic target, + int level, + dynamic internalFormat, + dynamic format, + dynamic dataType, + dynamic pixels, { + int? width, + int? height, + int border = 0, + }) { if (width == null) { js_util.callMethod(glContext, 'texImage2D', [ - target, level, internalFormat, format, dataType, pixels]); + target, + level, + internalFormat, + format, + dataType, + pixels, + ]); } else { js_util.callMethod(glContext, 'texImage2D', [ - target, level, internalFormat, width, height, border, format, dataType, - pixels]); + target, + level, + internalFormat, + width, + height, + border, + format, + dataType, + pixels, + ]); } } void texParameteri(dynamic target, dynamic parameterName, dynamic value) { - js_util.callMethod(glContext, 'texParameteri', [ - target, parameterName, value]); + js_util.callMethod(glContext, 'texParameteri', [target, parameterName, value]); } void deleteBuffer(Object buffer) { @@ -598,11 +614,7 @@ class GlContext { void dispose() { final Object? loseContextExtension = _getExtension('WEBGL_lose_context'); if (loseContextExtension != null) { - js_util.callMethod( - loseContextExtension, - 'loseContext', - const [], - ); + js_util.callMethod(loseContextExtension, 'loseContext', const []); } } @@ -644,25 +656,24 @@ class GlContext { } Object? _createShader(String shaderType) => js_util.callMethod( - glContext, 'createShader', [js_util.getProperty(glContext, shaderType)]); + glContext, + 'createShader', + [js_util.getProperty(glContext, shaderType)], + ); /// Error state of gl context. Object? get error => js_util.callMethod(glContext, 'getError', const []); /// Shader compiler error, if this returns [kFalse], to get details use /// [getShaderInfoLog]. - Object? get compileStatus => - _kCompileStatus ??= js_util.getProperty(glContext, 'COMPILE_STATUS'); + Object? get compileStatus => _kCompileStatus ??= js_util.getProperty(glContext, 'COMPILE_STATUS'); - Object? get kArrayBuffer => - _kArrayBuffer ??= js_util.getProperty(glContext, 'ARRAY_BUFFER'); + Object? get kArrayBuffer => _kArrayBuffer ??= js_util.getProperty(glContext, 'ARRAY_BUFFER'); Object? get kElementArrayBuffer => - _kElementArrayBuffer ??= js_util.getProperty(glContext, - 'ELEMENT_ARRAY_BUFFER'); + _kElementArrayBuffer ??= js_util.getProperty(glContext, 'ELEMENT_ARRAY_BUFFER'); - Object get kLinkStatus => - _kLinkStatus ??= js_util.getProperty(glContext, 'LINK_STATUS'); + Object get kLinkStatus => _kLinkStatus ??= js_util.getProperty(glContext, 'LINK_STATUS'); Object get kFloat => _kFloat ??= js_util.getProperty(glContext, 'FLOAT'); @@ -674,14 +685,11 @@ class GlContext { Object? get kUnsignedShort => _kUnsignedShort ??= js_util.getProperty(glContext, 'UNSIGNED_SHORT'); - Object? get kStaticDraw => - _kStaticDraw ??= js_util.getProperty(glContext, 'STATIC_DRAW'); + Object? get kStaticDraw => _kStaticDraw ??= js_util.getProperty(glContext, 'STATIC_DRAW'); - Object get kTriangles => - _kTriangles ??= js_util.getProperty(glContext, 'TRIANGLES'); + Object get kTriangles => _kTriangles ??= js_util.getProperty(glContext, 'TRIANGLES'); - Object get kTriangleFan => - _kTriangles ??= js_util.getProperty(glContext, 'TRIANGLE_FAN'); + Object get kTriangleFan => _kTriangles ??= js_util.getProperty(glContext, 'TRIANGLE_FAN'); Object get kTriangleStrip => _kTriangles ??= js_util.getProperty(glContext, 'TRIANGLE_STRIP'); @@ -689,38 +697,32 @@ class GlContext { Object? get kColorBufferBit => _kColorBufferBit ??= js_util.getProperty(glContext, 'COLOR_BUFFER_BIT'); - Object? get kTexture2D => - _kTexture2D ??= js_util.getProperty(glContext, 'TEXTURE_2D'); + Object? get kTexture2D => _kTexture2D ??= js_util.getProperty(glContext, 'TEXTURE_2D'); - double get kTexture0 => - _kTexture0 ??= js_util.getProperty(glContext, 'TEXTURE0'); + double get kTexture0 => _kTexture0 ??= js_util.getProperty(glContext, 'TEXTURE0'); - Object? get kTextureWrapS => - _kTextureWrapS ??= js_util.getProperty(glContext, 'TEXTURE_WRAP_S'); + Object? get kTextureWrapS => _kTextureWrapS ??= js_util.getProperty(glContext, 'TEXTURE_WRAP_S'); - Object? get kTextureWrapT => - _kTextureWrapT ??= js_util.getProperty(glContext, 'TEXTURE_WRAP_T'); + Object? get kTextureWrapT => _kTextureWrapT ??= js_util.getProperty(glContext, 'TEXTURE_WRAP_T'); - Object? get kRepeat => - _kRepeat ??= js_util.getProperty(glContext, 'REPEAT'); + Object? get kRepeat => _kRepeat ??= js_util.getProperty(glContext, 'REPEAT'); - Object? get kClampToEdge => - _kClampToEdge ??= js_util.getProperty(glContext, 'CLAMP_TO_EDGE'); + Object? get kClampToEdge => _kClampToEdge ??= js_util.getProperty(glContext, 'CLAMP_TO_EDGE'); Object? get kMirroredRepeat => _kMirroredRepeat ??= js_util.getProperty(glContext, 'MIRRORED_REPEAT'); - Object? get kLinear => - _kLinear ??= js_util.getProperty(glContext, 'LINEAR'); + Object? get kLinear => _kLinear ??= js_util.getProperty(glContext, 'LINEAR'); Object? get kTextureMinFilter => - _kTextureMinFilter ??= js_util.getProperty(glContext, - 'TEXTURE_MIN_FILTER'); + _kTextureMinFilter ??= js_util.getProperty(glContext, 'TEXTURE_MIN_FILTER'); /// Returns reference to uniform in program. Object getUniformLocation(Object program, String uniformName) { - final Object? res = js_util - .callMethod(glContext, 'getUniformLocation', [program, uniformName]); + final Object? res = js_util.callMethod(glContext, 'getUniformLocation', [ + program, + uniformName, + ]); if (res == null) { throw Exception('$uniformName not found'); } else { @@ -730,15 +732,19 @@ class GlContext { /// Returns true if uniform exists. bool containsUniform(Object program, String uniformName) { - final Object? res = js_util - .callMethod(glContext, 'getUniformLocation', [program, uniformName]); + final Object? res = js_util.callMethod(glContext, 'getUniformLocation', [ + program, + uniformName, + ]); return res != null; } /// Returns reference to uniform in program. Object getAttributeLocation(Object program, String attribName) { - final Object? res = js_util - .callMethod(glContext, 'getAttribLocation', [program, attribName]); + final Object? res = js_util.callMethod(glContext, 'getAttribLocation', [ + program, + attribName, + ]); if (res == null) { throw Exception('$attribName not found'); } else { @@ -757,16 +763,19 @@ class GlContext { } /// Sets vec4 uniform values. - void setUniform4f(Object uniform, double value1, double value2, double value3, - double value4) { - js_util.callMethod( - glContext, 'uniform4f', [uniform, value1, value2, value3, value4]); + void setUniform4f(Object uniform, double value1, double value2, double value3, double value4) { + js_util.callMethod(glContext, 'uniform4f', [ + uniform, + value1, + value2, + value3, + value4, + ]); } /// Sets mat4 uniform values. void setUniformMatrix4fv(Object uniform, bool transpose, Float32List value) { - js_util.callMethod( - glContext, 'uniformMatrix4fv', [uniform, transpose, value]); + js_util.callMethod(glContext, 'uniformMatrix4fv', [uniform, transpose, value]); } /// Shader compile error log. @@ -780,10 +789,8 @@ class GlContext { return js_util.callMethod(glContext, 'getProgramInfoLog', [glProgram]); } - int? get drawingBufferWidth => - js_util.getProperty(glContext, 'drawingBufferWidth'); - int? get drawingBufferHeight => - js_util.getProperty(glContext, 'drawingBufferWidth'); + int? get drawingBufferWidth => js_util.getProperty(glContext, 'drawingBufferWidth'); + int? get drawingBufferHeight => js_util.getProperty(glContext, 'drawingBufferWidth'); /// Reads gl contents as image data. /// @@ -794,17 +801,28 @@ class GlContext { final int bufferHeight = _heightInPixels!; if (ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit || ui_web.browser.browserEngine == ui_web.BrowserEngine.firefox) { - final Uint8List pixels = - Uint8List(bufferWidth * bufferHeight * kBytesPerPixel); - js_util.callMethod(glContext, 'readPixels', - [0, 0, bufferWidth, bufferHeight, kRGBA, kUnsignedByte, pixels]); - return createDomImageData( - Uint8ClampedList.fromList(pixels), bufferWidth, bufferHeight); + final Uint8List pixels = Uint8List(bufferWidth * bufferHeight * kBytesPerPixel); + js_util.callMethod(glContext, 'readPixels', [ + 0, + 0, + bufferWidth, + bufferHeight, + kRGBA, + kUnsignedByte, + pixels, + ]); + return createDomImageData(Uint8ClampedList.fromList(pixels), bufferWidth, bufferHeight); } else { - final Uint8ClampedList pixels = - Uint8ClampedList(bufferWidth * bufferHeight * kBytesPerPixel); - js_util.callMethod(glContext, 'readPixels', - [0, 0, bufferWidth, bufferHeight, kRGBA, kUnsignedByte, pixels]); + final Uint8ClampedList pixels = Uint8ClampedList(bufferWidth * bufferHeight * kBytesPerPixel); + js_util.callMethod(glContext, 'readPixels', [ + 0, + 0, + bufferWidth, + bufferHeight, + kRGBA, + kUnsignedByte, + pixels, + ]); return createDomImageData(pixels, bufferWidth, bufferHeight); } } @@ -816,16 +834,20 @@ class GlContext { // browser create ImageBitmap otherwise use more expensive canvas // allocation. However, transferToImageBitmap does not properly preserve // the alpha channel, so only use it if the pattern is opaque. - if (_canvas != null && - js_util.hasProperty(_canvas!, 'transferToImageBitmap') && - isOpaque) { + if (_canvas != null && js_util.hasProperty(_canvas!, 'transferToImageBitmap') && isOpaque) { // TODO(yjbanov): find out why we need to call getContext and ignore the return value. js_util.callMethod(_canvas!, 'getContext', ['webgl2']); - final Object? imageBitmap = js_util.callMethod(_canvas!, 'transferToImageBitmap', - []); + final Object? imageBitmap = js_util.callMethod( + _canvas!, + 'transferToImageBitmap', + [], + ); return imageBitmap; } else { - final DomCanvasElement canvas = createDomCanvasElement(width: _widthInPixels, height: _heightInPixels); + final DomCanvasElement canvas = createDomCanvasElement( + width: _widthInPixels, + height: _heightInPixels, + ); final DomCanvasRenderingContext2D ctx = canvas.context2D; drawImage(ctx, 0, 0); return canvas; @@ -834,7 +856,10 @@ class GlContext { /// Returns image data in data url format. String toImageUrl() { - final DomCanvasElement canvas = createDomCanvasElement(width: _widthInPixels, height: _heightInPixels); + final DomCanvasElement canvas = createDomCanvasElement( + width: _widthInPixels, + height: _heightInPixels, + ); final DomCanvasRenderingContext2D ctx = canvas.context2D; drawImage(ctx, 0, 0); final String dataUrl = canvas.toDataURL(); @@ -875,36 +900,39 @@ class GlContextCache { } void setupVertexTransforms( - GlContext gl, - GlProgram glProgram, - double offsetX, - double offsetY, - double widthInPixels, - double heightInPixels, - Matrix4 transform) { - final Object transformUniform = - gl.getUniformLocation(glProgram.program, 'u_ctransform'); - final Matrix4 transformAtOffset = transform.clone() - ..translate(-offsetX, -offsetY); + GlContext gl, + GlProgram glProgram, + double offsetX, + double offsetY, + double widthInPixels, + double heightInPixels, + Matrix4 transform, +) { + final Object transformUniform = gl.getUniformLocation(glProgram.program, 'u_ctransform'); + final Matrix4 transformAtOffset = transform.clone()..translate(-offsetX, -offsetY); gl.setUniformMatrix4fv(transformUniform, false, transformAtOffset.storage); // Set uniform to scale 0..width/height pixels coordinates to -1..1 // clipspace range and flip the Y axis. final Object resolution = gl.getUniformLocation(glProgram.program, 'u_scale'); - gl.setUniform4f(resolution, 2.0 / widthInPixels, - -2.0 / heightInPixels, 1, 1); + gl.setUniform4f(resolution, 2.0 / widthInPixels, -2.0 / heightInPixels, 1, 1); final Object shift = gl.getUniformLocation(glProgram.program, 'u_shift'); gl.setUniform4f(shift, -1, 1, 0, 0); } void setupTextureTransform( - GlContext gl, GlProgram glProgram, double offsetx, double offsety, double sx, double sy) { + GlContext gl, + GlProgram glProgram, + double offsetx, + double offsety, + double sx, + double sy, +) { final Object scalar = gl.getUniformLocation(glProgram.program, 'u_textransform'); gl.setUniform4f(scalar, sx, sy, offsetx, offsety); } -void bufferVertexData(GlContext gl, Float32List positions, - double devicePixelRatio) { +void bufferVertexData(GlContext gl, Float32List positions, double devicePixelRatio) { if (devicePixelRatio == 1.0) { gl.bufferData(positions, gl.kStaticDraw); } else { @@ -920,13 +948,13 @@ void bufferVertexData(GlContext gl, Float32List positions, dynamic tileModeToGlWrapping(GlContext gl, ui.TileMode tileMode) { switch (tileMode) { case ui.TileMode.clamp: - return gl.kClampToEdge; + return gl.kClampToEdge; case ui.TileMode.decal: - return gl.kClampToEdge; + return gl.kClampToEdge; case ui.TileMode.mirror: - return gl.kMirroredRepeat; + return gl.kMirroredRepeat; case ui.TileMode.repeated: - return gl.kRepeat; + return gl.kRepeat; } } @@ -936,10 +964,7 @@ class OffScreenCanvas { if (OffScreenCanvas.supported) { offScreenCanvas = createDomOffscreenCanvas(width, height); } else { - canvasElement = createDomCanvasElement( - width: width, - height: height, - ); + canvasElement = createDomCanvasElement(width: width, height: height); canvasElement!.className = 'gl-canvas'; _updateCanvasCssSize(canvasElement!); } @@ -961,10 +986,10 @@ class OffScreenCanvas { } void resize(int requestedWidth, int requestedHeight) { - if(requestedWidth != width || requestedHeight != height) { + if (requestedWidth != width || requestedHeight != height) { width = requestedWidth; height = requestedHeight; - if(offScreenCanvas != null) { + if (offScreenCanvas != null) { offScreenCanvas!.width = requestedWidth.toDouble(); offScreenCanvas!.height = requestedHeight.toDouble(); } else if (canvasElement != null) { @@ -990,8 +1015,9 @@ class OffScreenCanvas { DomCanvasRenderingContextBitmapRenderer? getBitmapRendererContext() { return (offScreenCanvas != null - ? offScreenCanvas!.getContext('bitmaprenderer') - : canvasElement!.getContext('bitmaprenderer')) as DomCanvasRenderingContextBitmapRenderer?; + ? offScreenCanvas!.getContext('bitmaprenderer') + : canvasElement!.getContext('bitmaprenderer')) + as DomCanvasRenderingContextBitmapRenderer?; } /// Feature detection for transferToImageBitmap on OffscreenCanvas. @@ -1003,17 +1029,24 @@ class OffScreenCanvas { /// /// !Warning API still in experimental status, feature detect before using. Object? transferToImageBitmap() { - return js_util.callMethod(offScreenCanvas!, 'transferToImageBitmap', - []); + return js_util.callMethod(offScreenCanvas!, 'transferToImageBitmap', []); } /// Draws canvas contents to a rendering context. void transferImage(Object targetContext) { // Actual size of canvas may be larger than viewport size. Use // source/destination to draw part of the image data. - js_util.callMethod(targetContext, 'drawImage', - [offScreenCanvas ?? canvasElement!, 0, 0, width, height, - 0, 0, width, height]); + js_util.callMethod(targetContext, 'drawImage', [ + offScreenCanvas ?? canvasElement!, + 0, + 0, + width, + height, + 0, + 0, + width, + height, + ]); } /// Converts canvas contents to an image and returns as data URL. @@ -1022,11 +1055,14 @@ class OffScreenCanvas { if (offScreenCanvas != null) { offScreenCanvas!.convertToBlob().then((DomBlob value) { final DomFileReader fileReader = createDomFileReader(); - fileReader.addEventListener('load', createDomEventListener((DomEvent event) { - completer.complete( - js_util.getProperty(js_util.getProperty(event, 'target'), 'result'), - ); - })); + fileReader.addEventListener( + 'load', + createDomEventListener((DomEvent event) { + completer.complete( + js_util.getProperty(js_util.getProperty(event, 'target'), 'result'), + ); + }), + ); fileReader.readAsDataURL(value); }); return completer.future; @@ -1037,14 +1073,13 @@ class OffScreenCanvas { /// Draws an image to canvas for both offscreen canvas context2d. void drawImage(Object image, int x, int y, int width, int height) { - js_util.callMethod( - getContext2d()!, 'drawImage', [image, x, y, width, height]); + js_util.callMethod(getContext2d()!, 'drawImage', [image, x, y, width, height]); } /// Feature detects OffscreenCanvas. - static bool get supported => _supported ??= - // Safari 16.4 implements OffscreenCanvas, but without WebGL support. So - // it's not really supported in a way that is useful to us. - !ui_web.browser.isSafari - && js_util.hasProperty(domWindow, 'OffscreenCanvas'); + static bool get supported => + _supported ??= + // Safari 16.4 implements OffscreenCanvas, but without WebGL support. So + // it's not really supported in a way that is useful to us. + !ui_web.browser.isSafari && js_util.hasProperty(domWindow, 'OffscreenCanvas'); } diff --git a/lib/web_ui/lib/src/engine/scene_builder.dart b/lib/web_ui/lib/src/engine/scene_builder.dart index 9fe4c17c57711..61ab18b73f8ca 100644 --- a/lib/web_ui/lib/src/engine/scene_builder.dart +++ b/lib/web_ui/lib/src/engine/scene_builder.dart @@ -18,7 +18,6 @@ import 'package:ui/ui.dart' as ui; // additional sizing information that the scene builder uses to determine how // these object might occlude one another. - class EngineScene implements ui.Scene { EngineScene(this.rootLayer); @@ -88,7 +87,6 @@ class OcclusionMapEmpty implements OcclusionMapNode { @override bool overlaps(ui.Rect rect) => false; - } class OcclusionMapLeaf implements OcclusionMapNode { @@ -117,8 +115,8 @@ class OcclusionMapBranch implements OcclusionMapNode { final ui.Rect boundingBox; double _areaOfUnion(ui.Rect first, ui.Rect second) { - return (math.max(first.right, second.right) - math.min(first.left, second.left)) - * (math.max(first.bottom, second.bottom) - math.max(first.top, second.top)); + return (math.max(first.right, second.right) - math.min(first.left, second.left)) * + (math.max(first.bottom, second.bottom) - math.max(first.top, second.top)); } @override @@ -129,17 +127,11 @@ class OcclusionMapBranch implements OcclusionMapNode { final double leftRightArea = boundingBox.width * boundingBox.height; if (leftOtherArea < rightOtherArea) { if (leftOtherArea < leftRightArea) { - return OcclusionMapBranch( - left.insert(other), - right, - ); + return OcclusionMapBranch(left.insert(other), right); } } else { if (rightOtherArea < leftRightArea) { - return OcclusionMapBranch( - left, - right.insert(other), - ); + return OcclusionMapBranch(left, right.insert(other)); } } return OcclusionMapBranch(this, OcclusionMapLeaf(other)); @@ -187,14 +179,14 @@ class EngineSceneBuilder implements ui.SceneBuilder { ui.Offset offset, ui.Picture picture, { bool isComplexHint = false, - bool willChangeHint = false + bool willChangeHint = false, }) { - final int sliceIndex = _placePicture(offset, picture as ScenePicture, currentBuilder.globalPlatformViewStyling); - currentBuilder.addPicture( + final int sliceIndex = _placePicture( offset, - picture, - sliceIndex: sliceIndex, + picture as ScenePicture, + currentBuilder.globalPlatformViewStyling, ); + currentBuilder.addPicture(offset, picture, sliceIndex: sliceIndex); } // This function determines the lowest scene slice that this picture can be placed @@ -242,15 +234,15 @@ class EngineSceneBuilder implements ui.SceneBuilder { int viewId, { ui.Offset offset = ui.Offset.zero, double width = 0.0, - double height = 0.0 + double height = 0.0, }) { final ui.Rect platformViewRect = ui.Rect.fromLTWH(offset.dx, offset.dy, width, height); - final int sliceIndex = _placePlatformView(viewId, platformViewRect, currentBuilder.globalPlatformViewStyling); - currentBuilder.addPlatformView( + final int sliceIndex = _placePlatformView( viewId, - bounds: platformViewRect, - sliceIndex: sliceIndex, + platformViewRect, + currentBuilder.globalPlatformViewStyling, ); + currentBuilder.addPlatformView(viewId, bounds: platformViewRect, sliceIndex: sliceIndex); } // This function determines the lowest scene slice this platform view can be placed @@ -258,11 +250,7 @@ class EngineSceneBuilder implements ui.SceneBuilder { // // The platform view is placed into the last slice where it intersects with a picture // or a platform view. - int _placePlatformView( - int viewId, - ui.Rect rect, - PlatformViewStyling styling, - ) { + int _placePlatformView(int viewId, ui.Rect rect, PlatformViewStyling styling) { // Once we add a platform view, we actually have to do proper occlusion tracking. _isSimple = false; @@ -284,11 +272,17 @@ class EngineSceneBuilder implements ui.SceneBuilder { @override void addRetained(ui.EngineLayer retainedLayer) { - final PictureEngineLayer placedEngineLayer = _placeRetainedLayer(retainedLayer as PictureEngineLayer, currentBuilder.globalPlatformViewStyling); + final PictureEngineLayer placedEngineLayer = _placeRetainedLayer( + retainedLayer as PictureEngineLayer, + currentBuilder.globalPlatformViewStyling, + ); currentBuilder.mergeLayer(placedEngineLayer); } - PictureEngineLayer _placeRetainedLayer(PictureEngineLayer retainedLayer, PlatformViewStyling styling) { + PictureEngineLayer _placeRetainedLayer( + PictureEngineLayer retainedLayer, + PlatformViewStyling styling, + ) { if (_isSimple && retainedLayer.isSimple) { // There are no platform views, so we don't need to do any occlusion tracking // and can simply merge the layer. @@ -296,10 +290,16 @@ class EngineSceneBuilder implements ui.SceneBuilder { } bool needsRebuild = false; final List revisedDrawCommands = []; - final PlatformViewStyling combinedStyling = PlatformViewStyling.combine(styling, retainedLayer.platformViewStyling); + final PlatformViewStyling combinedStyling = PlatformViewStyling.combine( + styling, + retainedLayer.platformViewStyling, + ); for (final LayerDrawCommand command in retainedLayer.drawCommands) { switch (command) { - case PictureDrawCommand(offset: final ui.Offset offset, picture: final ScenePicture picture): + case PictureDrawCommand( + offset: final ui.Offset offset, + picture: final ScenePicture picture, + ): final int sliceIndex = _placePicture(offset, picture, combinedStyling); if (command.sliceIndex != sliceIndex) { needsRebuild = true; @@ -327,10 +327,16 @@ class EngineSceneBuilder implements ui.SceneBuilder { } // Otherwise, we replace the commands of the layer to create a new one. - currentBuilder = LayerBuilder.childLayer(parent: currentBuilder, layer: retainedLayer.emptyClone()); + currentBuilder = LayerBuilder.childLayer( + parent: currentBuilder, + layer: retainedLayer.emptyClone(), + ); for (final LayerDrawCommand command in revisedDrawCommands) { switch (command) { - case PictureDrawCommand(offset: final ui.Offset offset, picture: final ScenePicture picture): + case PictureDrawCommand( + offset: final ui.Offset offset, + picture: final ScenePicture picture, + ): currentBuilder.addPicture(offset, picture, sliceIndex: command.sliceIndex); case PlatformViewDrawCommand(viewId: final int viewId, bounds: final ui.Rect bounds): currentBuilder.addPlatformView(viewId, bounds: bounds, sliceIndex: command.sliceIndex); @@ -350,7 +356,7 @@ class EngineSceneBuilder implements ui.SceneBuilder { double width = 0.0, double height = 0.0, bool freeze = false, - ui.FilterQuality filterQuality = ui.FilterQuality.low + ui.FilterQuality filterQuality = ui.FilterQuality.low, }) { // addTexture is not implemented on web. } @@ -361,27 +367,29 @@ class EngineSceneBuilder implements ui.SceneBuilder { ui.BlendMode blendMode = ui.BlendMode.srcOver, ui.BackdropFilterEngineLayer? oldLayer, int? backdropId, - }) => pushLayer(BackdropFilterLayer(BackdropFilterOperation(filter, blendMode))); + }) => pushLayer( + BackdropFilterLayer(BackdropFilterOperation(filter, blendMode)), + ); @override ui.ClipPathEngineLayer pushClipPath( ui.Path path, { ui.Clip clipBehavior = ui.Clip.antiAlias, - ui.ClipPathEngineLayer? oldLayer + ui.ClipPathEngineLayer? oldLayer, }) => pushLayer(ClipPathLayer(ClipPathOperation(path as ScenePath, clipBehavior))); @override ui.ClipRRectEngineLayer pushClipRRect( ui.RRect rrect, { required ui.Clip clipBehavior, - ui.ClipRRectEngineLayer? oldLayer + ui.ClipRRectEngineLayer? oldLayer, }) => pushLayer(ClipRRectLayer(ClipRRectOperation(rrect, clipBehavior))); @override ui.ClipRectEngineLayer pushClipRect( ui.Rect rect, { ui.Clip clipBehavior = ui.Clip.antiAlias, - ui.ClipRectEngineLayer? oldLayer + ui.ClipRectEngineLayer? oldLayer, }) { return pushLayer(ClipRectLayer(ClipRectOperation(rect, clipBehavior))); } @@ -389,29 +397,27 @@ class EngineSceneBuilder implements ui.SceneBuilder { @override ui.ColorFilterEngineLayer pushColorFilter( ui.ColorFilter filter, { - ui.ColorFilterEngineLayer? oldLayer + ui.ColorFilterEngineLayer? oldLayer, }) => pushLayer(ColorFilterLayer(ColorFilterOperation(filter))); @override ui.ImageFilterEngineLayer pushImageFilter( ui.ImageFilter filter, { ui.Offset offset = ui.Offset.zero, - ui.ImageFilterEngineLayer? oldLayer + ui.ImageFilterEngineLayer? oldLayer, }) => pushLayer( - ImageFilterLayer(ImageFilterOperation(filter as SceneImageFilter, offset)), - ); + ImageFilterLayer(ImageFilterOperation(filter as SceneImageFilter, offset)), + ); @override - ui.OffsetEngineLayer pushOffset( - double dx, - double dy, { - ui.OffsetEngineLayer? oldLayer - }) => pushLayer(OffsetLayer(OffsetOperation(dx, dy))); + ui.OffsetEngineLayer pushOffset(double dx, double dy, {ui.OffsetEngineLayer? oldLayer}) => + pushLayer(OffsetLayer(OffsetOperation(dx, dy))); @override - ui.OpacityEngineLayer pushOpacity(int alpha, { + ui.OpacityEngineLayer pushOpacity( + int alpha, { ui.Offset offset = ui.Offset.zero, - ui.OpacityEngineLayer? oldLayer + ui.OpacityEngineLayer? oldLayer, }) => pushLayer(OpacityLayer(OpacityOperation(alpha, offset))); @override @@ -420,16 +426,13 @@ class EngineSceneBuilder implements ui.SceneBuilder { ui.Rect maskRect, ui.BlendMode blendMode, { ui.ShaderMaskEngineLayer? oldLayer, - ui.FilterQuality filterQuality = ui.FilterQuality.low - }) => pushLayer( - ShaderMaskLayer(ShaderMaskOperation(shader, maskRect, blendMode)), - ); + ui.FilterQuality filterQuality = ui.FilterQuality.low, + }) => + pushLayer(ShaderMaskLayer(ShaderMaskOperation(shader, maskRect, blendMode))); @override - ui.TransformEngineLayer pushTransform( - Float64List matrix4, { - ui.TransformEngineLayer? oldLayer - }) => pushLayer(TransformLayer(TransformOperation(matrix4))); + ui.TransformEngineLayer pushTransform(Float64List matrix4, {ui.TransformEngineLayer? oldLayer}) => + pushLayer(TransformLayer(TransformOperation(matrix4))); @override void setProperties( @@ -439,7 +442,7 @@ class EngineSceneBuilder implements ui.SceneBuilder { double insetRight, double insetBottom, double insetLeft, - bool focusable + bool focusable, ) { // Not implemented on web } @@ -465,10 +468,7 @@ class EngineSceneBuilder implements ui.SceneBuilder { } T pushLayer(T layer) { - currentBuilder = LayerBuilder.childLayer( - parent: currentBuilder, - layer: layer, - ); + currentBuilder = LayerBuilder.childLayer(parent: currentBuilder, layer: layer); return layer; } } diff --git a/lib/web_ui/lib/src/engine/scene_view.dart b/lib/web_ui/lib/src/engine/scene_view.dart index bedffaef2d7f8..a4d474728fa19 100644 --- a/lib/web_ui/lib/src/engine/scene_view.dart +++ b/lib/web_ui/lib/src/engine/scene_view.dart @@ -9,11 +9,8 @@ import 'package:ui/ui.dart' as ui; const String kCanvasContainerTag = 'flt-canvas-container'; -typedef RenderResult = ({ - List imageBitmaps, - int rasterStartMicros, - int rasterEndMicros, -}); +typedef RenderResult = + ({List imageBitmaps, int rasterStartMicros, int rasterEndMicros}); // This is an interface that renders a `ScenePicture` as a `DomImageBitmap`. // It is optionally asynchronous. It is required for the `EngineSceneView` to @@ -24,11 +21,7 @@ abstract class PictureRenderer { } class _SceneRender { - _SceneRender( - this.scene, - this._completer, { - this.recorder, - }) { + _SceneRender(this.scene, this._completer, {this.recorder}) { scene.beginRender(); } @@ -214,7 +207,7 @@ final class PictureSliceContainer extends SliceContainer { final DomElement container = domDocument.createElement(kCanvasContainerTag); final DomCanvasElement canvas = createDomCanvasElement( width: bounds.width.toInt(), - height: bounds.height.toInt() + height: bounds.height.toInt(), ); container.appendChild(canvas); return PictureSliceContainer._(bounds, container, canvas); @@ -242,7 +235,7 @@ final class PictureSliceContainer extends SliceContainer { bounds.left.floorToDouble(), bounds.top.floorToDouble(), bounds.right.ceilToDouble(), - bounds.bottom.ceilToDouble() + bounds.bottom.ceilToDouble(), ); final DomCSSStyleDeclaration style = canvas.style; final double devicePixelRatio = EngineFlutterDisplay.instance.devicePixelRatio; @@ -271,10 +264,10 @@ final class PictureSliceContainer extends SliceContainer { } final class PlatformViewContainer extends SliceContainer { - PlatformViewContainer(this.viewId) : - container = createDomElement('flt-clip'), - slot = createPlatformViewSlot(viewId) { - container.appendChild(slot); + PlatformViewContainer(this.viewId) + : container = createDomElement('flt-clip'), + slot = createPlatformViewSlot(viewId) { + container.appendChild(slot); } final int viewId; @@ -364,11 +357,14 @@ final class PlatformViewContainer extends SliceContainer { transform = position.transform!.clone()..translate(_bounds!.left, _bounds!.top); } else { final ui.Offset offset = position.offset ?? ui.Offset.zero; - transform = Matrix4.translationValues(_bounds!.left + offset.dx, _bounds!.top + offset.dy, 0); + transform = Matrix4.translationValues( + _bounds!.left + offset.dx, + _bounds!.top + offset.dy, + 0, + ); } final double inverseScale = 1.0 / devicePixelRatio; - final Matrix4 scaleMatrix = - Matrix4.diagonal3Values(inverseScale, inverseScale, 1); + final Matrix4 scaleMatrix = Matrix4.diagonal3Values(inverseScale, inverseScale, 1); scaleMatrix.multiply(transform); style.transform = float64ListToCssTransform(scaleMatrix.storage); style.transformOrigin = '0 0 0'; diff --git a/lib/web_ui/lib/src/engine/semantics/accessibility.dart b/lib/web_ui/lib/src/engine/semantics/accessibility.dart index f0ba1d480a4be..207acaa97e895 100644 --- a/lib/web_ui/lib/src/engine/semantics/accessibility.dart +++ b/lib/web_ui/lib/src/engine/semantics/accessibility.dart @@ -14,10 +14,7 @@ import '../util.dart'; /// It is used to set the priority with which assistive technology should treat announcements. /// /// The order of this enum must match the order of the values in semantics_event.dart in framework. -enum Assertiveness { - polite, - assertive, -} +enum Assertiveness { polite, assertive } /// Duration for which a live message will be present in the DOM for the screen /// reader to announce it. @@ -63,8 +60,10 @@ class AccessibilityAnnouncements { /// Looks up the element used to announce messages of the given [assertiveness]. DomHTMLElement ariaLiveElementFor(Assertiveness assertiveness) { switch (assertiveness) { - case Assertiveness.polite: return _politeElement; - case Assertiveness.assertive: return _assertiveElement; + case Assertiveness.polite: + return _politeElement; + case Assertiveness.assertive: + return _assertiveElement; } } @@ -101,8 +100,10 @@ class AccessibilityAnnouncements { } static DomHTMLElement _createElement(Assertiveness assertiveness) { - final String ariaLiveValue = (assertiveness == Assertiveness.assertive) ? 'assertive' : 'polite'; - final DomHTMLElement liveRegion = createDomElement('flt-announcement-$ariaLiveValue') as DomHTMLElement; + final String ariaLiveValue = + (assertiveness == Assertiveness.assertive) ? 'assertive' : 'polite'; + final DomHTMLElement liveRegion = + createDomElement('flt-announcement-$ariaLiveValue') as DomHTMLElement; liveRegion.style ..position = 'fixed' ..overflow = 'hidden' diff --git a/lib/web_ui/lib/src/engine/semantics/checkable.dart b/lib/web_ui/lib/src/engine/semantics/checkable.dart index af136fb7f794b..b8724ecd4c9ef 100644 --- a/lib/web_ui/lib/src/engine/semantics/checkable.dart +++ b/lib/web_ui/lib/src/engine/semantics/checkable.dart @@ -30,8 +30,7 @@ enum _CheckableKind { toggle, } -_CheckableKind _checkableKindFromSemanticsFlag( - SemanticsObject semanticsObject) { +_CheckableKind _checkableKindFromSemanticsFlag(SemanticsObject semanticsObject) { if (semanticsObject.hasFlag(ui.SemanticsFlag.isInMutuallyExclusiveGroup)) { return _CheckableKind.radio; } else if (semanticsObject.hasFlag(ui.SemanticsFlag.hasToggledState)) { @@ -54,12 +53,12 @@ _CheckableKind _checkableKindFromSemanticsFlag( /// boolean state of being "selected". class SemanticCheckable extends SemanticRole { SemanticCheckable(SemanticsObject semanticsObject) - : _kind = _checkableKindFromSemanticsFlag(semanticsObject), - super.withBasics( - SemanticRoleKind.checkable, - semanticsObject, - preferredLabelRepresentation: LabelRepresentation.ariaLabel, - ) { + : _kind = _checkableKindFromSemanticsFlag(semanticsObject), + super.withBasics( + SemanticRoleKind.checkable, + semanticsObject, + preferredLabelRepresentation: LabelRepresentation.ariaLabel, + ) { addTappable(); } diff --git a/lib/web_ui/lib/src/engine/semantics/focusable.dart b/lib/web_ui/lib/src/engine/semantics/focusable.dart index 745b646d134c0..79b7925bb8fd5 100644 --- a/lib/web_ui/lib/src/engine/semantics/focusable.dart +++ b/lib/web_ui/lib/src/engine/semantics/focusable.dart @@ -29,7 +29,7 @@ import 'semantics.dart'; /// * https://developer.mozilla.org/en-US/docs/Web/Accessibility/Keyboard-navigable_JavaScript_widgets class Focusable extends SemanticBehavior { Focusable(super.semanticsObject, super.owner) - : _focusManager = AccessibilityFocusManager(semanticsObject.owner); + : _focusManager = AccessibilityFocusManager(semanticsObject.owner); final AccessibilityFocusManager _focusManager; @@ -58,7 +58,9 @@ class Focusable extends SemanticBehavior { if (!_focusManager.isManaging) { _focusManager.manage(semanticsObject.id, owner.element); } - _focusManager.changeFocus(semanticsObject.hasFocus && (!semanticsObject.hasEnabledState || semanticsObject.isEnabled)); + _focusManager.changeFocus( + semanticsObject.hasFocus && (!semanticsObject.hasEnabledState || semanticsObject.isEnabled), + ); } else { _focusManager.stopManaging(); } @@ -72,19 +74,20 @@ class Focusable extends SemanticBehavior { } /// Objects associated with the element whose focus is being managed. -typedef _FocusTarget = ({ - /// [SemanticsObject.id] of the semantics node being managed. - int semanticsNodeId, +typedef _FocusTarget = + ({ + /// [SemanticsObject.id] of the semantics node being managed. + int semanticsNodeId, - /// The element whose focus is being managed. - DomElement element, + /// The element whose focus is being managed. + DomElement element, - /// The listener for the "focus" DOM event. - DomEventListener domFocusListener, + /// The listener for the "focus" DOM event. + DomEventListener domFocusListener, - /// The listener for the "blur" DOM event. - DomEventListener domBlurListener, -}); + /// The listener for the "blur" DOM event. + DomEventListener domBlurListener, + }); enum AccessibilityFocusManagerEvent { /// No event has happend for the target element. @@ -233,7 +236,7 @@ class AccessibilityFocusManager { assert(() { printWarning( 'Cannot change focus to $value. No element is being managed by this ' - 'AccessibilityFocusManager.' + 'AccessibilityFocusManager.', ); return true; }()); diff --git a/lib/web_ui/lib/src/engine/semantics/header.dart b/lib/web_ui/lib/src/engine/semantics/header.dart index 9de9e3d4d271f..b1acd2525fab3 100644 --- a/lib/web_ui/lib/src/engine/semantics/header.dart +++ b/lib/web_ui/lib/src/engine/semantics/header.dart @@ -17,24 +17,25 @@ import 'semantics.dart'; /// * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/header /// * https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/banner_role class SemanticHeader extends SemanticRole { - SemanticHeader(SemanticsObject semanticsObject) : super.withBasics( - SemanticRoleKind.header, - semanticsObject, + SemanticHeader(SemanticsObject semanticsObject) + : super.withBasics( + SemanticRoleKind.header, + semanticsObject, - // Why use sizedSpan? - // - // On an empty
aria-label alone will read the label but also add - // "empty banner". Additionally, if the label contains information that's - // meant to be crawlable, it will be lost by moving into aria-label, because - // most crawlers ignore ARIA labels. - // - // Using DOM text, such as
DOM text
causes the browser to - // generate two a11y nodes, one for the
element, and one for the - // "DOM text" text node. The text node is sized according to the text size, - // and does not match the size of the
element, which is the same - // issue as https://github.com/flutter/flutter/issues/146774. - preferredLabelRepresentation: LabelRepresentation.sizedSpan, - ); + // Why use sizedSpan? + // + // On an empty
aria-label alone will read the label but also add + // "empty banner". Additionally, if the label contains information that's + // meant to be crawlable, it will be lost by moving into aria-label, because + // most crawlers ignore ARIA labels. + // + // Using DOM text, such as
DOM text
causes the browser to + // generate two a11y nodes, one for the
element, and one for the + // "DOM text" text node. The text node is sized according to the text size, + // and does not match the size of the
element, which is the same + // issue as https://github.com/flutter/flutter/issues/146774. + preferredLabelRepresentation: LabelRepresentation.sizedSpan, + ); @override DomElement createElement() => createDomElement('header'); diff --git a/lib/web_ui/lib/src/engine/semantics/heading.dart b/lib/web_ui/lib/src/engine/semantics/heading.dart index 051b6829c2508..2ab09e24149be 100644 --- a/lib/web_ui/lib/src/engine/semantics/heading.dart +++ b/lib/web_ui/lib/src/engine/semantics/heading.dart @@ -10,7 +10,7 @@ import 'semantics.dart'; /// level (h1 ... h6). class SemanticHeading extends SemanticRole { SemanticHeading(SemanticsObject semanticsObject) - : super.blank(SemanticRoleKind.heading, semanticsObject) { + : super.blank(SemanticRoleKind.heading, semanticsObject) { addFocusManagement(); addLiveRegion(); addRouteName(); @@ -27,7 +27,6 @@ class SemanticHeading extends SemanticRole { // by semanticsObject.rect, the extra margins/paddings must be zeroed out. ..margin = '0' ..padding = '0' - // The 10px size was picked empirically. By default the browser will scale // the font size based on the heading level. Font size should not be // important in semantics since rendering is done via the render tree. diff --git a/lib/web_ui/lib/src/engine/semantics/image.dart b/lib/web_ui/lib/src/engine/semantics/image.dart index b058934343ae1..8af65e1337907 100644 --- a/lib/web_ui/lib/src/engine/semantics/image.dart +++ b/lib/web_ui/lib/src/engine/semantics/image.dart @@ -12,7 +12,7 @@ import 'semantics.dart'; /// Screen-readers takes advantage of "aria-label" to describe the visual. class SemanticImage extends SemanticRole { SemanticImage(SemanticsObject semanticsObject) - : super.blank(SemanticRoleKind.image, semanticsObject) { + : super.blank(SemanticRoleKind.image, semanticsObject) { // The following behaviors can coexist with images. `LabelAndValue` is // not used because this behavior uses special auxiliary elements to // supply ARIA labels. diff --git a/lib/web_ui/lib/src/engine/semantics/incrementable.dart b/lib/web_ui/lib/src/engine/semantics/incrementable.dart index ef351e660cb2b..4742a5121e6ea 100644 --- a/lib/web_ui/lib/src/engine/semantics/incrementable.dart +++ b/lib/web_ui/lib/src/engine/semantics/incrementable.dart @@ -21,8 +21,8 @@ import 'semantics.dart'; /// gestures must be interpreted by the Flutter framework. class SemanticIncrementable extends SemanticRole { SemanticIncrementable(SemanticsObject semanticsObject) - : _focusManager = AccessibilityFocusManager(semanticsObject.owner), - super.blank(SemanticRoleKind.incrementable, semanticsObject) { + : _focusManager = AccessibilityFocusManager(semanticsObject.owner), + super.blank(SemanticRoleKind.incrementable, semanticsObject) { // The following generic roles can coexist with incrementables. Generic focus // management is not used by this role because the root DOM element is not // the one being focused on, but the internal `` element. @@ -34,22 +34,33 @@ class SemanticIncrementable extends SemanticRole { _element.type = 'range'; _element.setAttribute('role', 'slider'); - _element.addEventListener('change', createDomEventListener((_) { - if (_element.disabled!) { - return; - } - _pendingResync = true; - final int newInputValue = int.parse(_element.value!); - if (newInputValue > _currentSurrogateValue) { - _currentSurrogateValue += 1; - EnginePlatformDispatcher.instance.invokeOnSemanticsAction( - viewId, semanticsObject.id, ui.SemanticsAction.increase, null); - } else if (newInputValue < _currentSurrogateValue) { - _currentSurrogateValue -= 1; - EnginePlatformDispatcher.instance.invokeOnSemanticsAction( - viewId, semanticsObject.id, ui.SemanticsAction.decrease, null); - } - })); + _element.addEventListener( + 'change', + createDomEventListener((_) { + if (_element.disabled!) { + return; + } + _pendingResync = true; + final int newInputValue = int.parse(_element.value!); + if (newInputValue > _currentSurrogateValue) { + _currentSurrogateValue += 1; + EnginePlatformDispatcher.instance.invokeOnSemanticsAction( + viewId, + semanticsObject.id, + ui.SemanticsAction.increase, + null, + ); + } else if (newInputValue < _currentSurrogateValue) { + _currentSurrogateValue -= 1; + EnginePlatformDispatcher.instance.invokeOnSemanticsAction( + viewId, + semanticsObject.id, + ui.SemanticsAction.decrease, + null, + ); + } + }), + ); // Store the callback as a closure because Dart does not guarantee that // tear-offs produce the same function object. @@ -117,7 +128,8 @@ class SemanticIncrementable extends SemanticRole { void _updateInputValues() { assert(EngineSemantics.instance.gestureMode == GestureMode.browserGestures); - final bool updateNeeded = _pendingResync || + final bool updateNeeded = + _pendingResync || semanticsObject.isValueDirty || semanticsObject.isIncreasedValueDirty || semanticsObject.isDecreasedValueDirty; diff --git a/lib/web_ui/lib/src/engine/semantics/label_and_value.dart b/lib/web_ui/lib/src/engine/semantics/label_and_value.dart index 006be31641f1a..f1ef2a666c5f2 100644 --- a/lib/web_ui/lib/src/engine/semantics/label_and_value.dart +++ b/lib/web_ui/lib/src/engine/semantics/label_and_value.dart @@ -172,26 +172,28 @@ final class DomTextRepresentation extends LabelRepresentationBehavior { } /// A span queue for a size update. -typedef _QueuedSizeUpdate = ({ - // The span to be sized. - SizedSpanRepresentation representation, +typedef _QueuedSizeUpdate = + ({ + // The span to be sized. + SizedSpanRepresentation representation, - // The desired size. - ui.Size targetSize, -}); + // The desired size. + ui.Size targetSize, + }); /// The size of a span as measured in the DOM. -typedef _Measurement = ({ - // The span that was measured. - SizedSpanRepresentation representation, +typedef _Measurement = + ({ + // The span that was measured. + SizedSpanRepresentation representation, - // The measured size of the DOM element before the size adjustment. - ui.Size domSize, + // The measured size of the DOM element before the size adjustment. + ui.Size domSize, - // The size of the element that the screen reader should observe after the - // size adjustment. - ui.Size targetSize, -}); + // The size of the element that the screen reader should observe after the + // size adjustment. + ui.Size targetSize, + }); /// Sets the label as the text of a `` child element. /// @@ -241,15 +243,12 @@ final class SizedSpanRepresentation extends LabelRepresentationBehavior { // - It supports the `transform` and `transform-origin` properties. Pure // `inline` does not support them. ..display = 'inline-block' - // Do not wrap text based on parent constraints. Instead, to fit in the // parent's box the text will be scaled. ..whiteSpace = 'nowrap' - // The origin of the coordinate system is the top-left corner of the // parent element. ..transformOrigin = '0 0 0' - // The node may be tappable without having a more concrete role set on it, // such as "button". It will just have a tap handler. This could lead to // sized span to be chosen as the label representation strategy. However, @@ -320,10 +319,7 @@ final class SizedSpanRepresentation extends LabelRepresentationBehavior { // corresponding to the semantic node may still be detached. semanticsObject.owner.addOneTimePostUpdateCallback(_updateSizes); } - _resizeQueue!.add(( - representation: this, - targetSize: size, - )); + _resizeQueue!.add((representation: this, targetSize: size)); } @override @@ -439,7 +435,7 @@ final class SizedSpanRepresentation extends LabelRepresentationBehavior { /// See [computeDomSemanticsLabel] for the exact logic that constructs the label /// of a semantic node. class LabelAndValue extends SemanticBehavior { - LabelAndValue(super.semanticsObject, super.owner, { required this.preferredRepresentation }); + LabelAndValue(super.semanticsObject, super.owner, {required this.preferredRepresentation}); /// The preferred representation of the label in the DOM. /// @@ -472,9 +468,8 @@ class LabelAndValue extends SemanticBehavior { /// screen reader. If the are no children, use the representation preferred /// by the role. LabelRepresentationBehavior _getEffectiveRepresentation() { - final LabelRepresentation effectiveRepresentation = semanticsObject.hasChildren - ? LabelRepresentation.ariaLabel - : preferredRepresentation; + final LabelRepresentation effectiveRepresentation = + semanticsObject.hasChildren ? LabelRepresentation.ariaLabel : preferredRepresentation; LabelRepresentationBehavior? representation = _representation; if (representation == null || representation.kind != effectiveRepresentation) { @@ -524,12 +519,7 @@ class LabelAndValue extends SemanticBehavior { } } -String? computeDomSemanticsLabel({ - String? tooltip, - String? label, - String? hint, - String? value, -}) { +String? computeDomSemanticsLabel({String? tooltip, String? label, String? hint, String? value}) { final String? labelHintValue = _computeLabelHintValue(label: label, hint: hint, value: value); if (tooltip == null && labelHintValue == null) { @@ -553,14 +543,10 @@ String? computeDomSemanticsLabel({ return combinedValue.isNotEmpty ? combinedValue.toString() : null; } -String? _computeLabelHintValue({ - String? label, - String? hint, - String? value, -}) { +String? _computeLabelHintValue({String? label, String? hint, String? value}) { final String combinedValue = [label, hint, value] - .whereType() // poor man's null filter - .where((String element) => element.trim().isNotEmpty) - .join(' '); + .whereType() // poor man's null filter + .where((String element) => element.trim().isNotEmpty) + .join(' '); return combinedValue.isNotEmpty ? combinedValue : null; } diff --git a/lib/web_ui/lib/src/engine/semantics/link.dart b/lib/web_ui/lib/src/engine/semantics/link.dart index d13f69fac09d5..4bea1f36349f7 100644 --- a/lib/web_ui/lib/src/engine/semantics/link.dart +++ b/lib/web_ui/lib/src/engine/semantics/link.dart @@ -7,11 +7,12 @@ import '../semantics.dart'; /// Provides accessibility for links. class SemanticLink extends SemanticRole { - SemanticLink(SemanticsObject semanticsObject) : super.withBasics( - SemanticRoleKind.link, - semanticsObject, - preferredLabelRepresentation: LabelRepresentation.domText, - ) { + SemanticLink(SemanticsObject semanticsObject) + : super.withBasics( + SemanticRoleKind.link, + semanticsObject, + preferredLabelRepresentation: LabelRepresentation.domText, + ) { addTappable(); } diff --git a/lib/web_ui/lib/src/engine/semantics/live_region.dart b/lib/web_ui/lib/src/engine/semantics/live_region.dart index 25d0291db90c0..30d1419027a7c 100644 --- a/lib/web_ui/lib/src/engine/semantics/live_region.dart +++ b/lib/web_ui/lib/src/engine/semantics/live_region.dart @@ -36,8 +36,7 @@ class LiveRegion extends SemanticBehavior { } AccessibilityAnnouncements get _accessibilityAnnouncements => - _accessibilityAnnouncementsOverride ?? - EngineSemantics.instance.accessibilityAnnouncements; + _accessibilityAnnouncementsOverride ?? EngineSemantics.instance.accessibilityAnnouncements; @override void update() { @@ -49,10 +48,7 @@ class LiveRegion extends SemanticBehavior { if (_lastAnnouncement != semanticsObject.label) { _lastAnnouncement = semanticsObject.label; if (semanticsObject.hasLabel) { - _accessibilityAnnouncements.announce( - _lastAnnouncement!, - Assertiveness.polite, - ); + _accessibilityAnnouncements.announce(_lastAnnouncement!, Assertiveness.polite); } } } diff --git a/lib/web_ui/lib/src/engine/semantics/platform_view.dart b/lib/web_ui/lib/src/engine/semantics/platform_view.dart index a14fa791a8636..f0e311afceb22 100644 --- a/lib/web_ui/lib/src/engine/semantics/platform_view.dart +++ b/lib/web_ui/lib/src/engine/semantics/platform_view.dart @@ -22,11 +22,11 @@ import 'semantics.dart'; /// * https://bugs.webkit.org/show_bug.cgi?id=223798 class SemanticPlatformView extends SemanticRole { SemanticPlatformView(SemanticsObject semanticsObject) - : super.withBasics( - SemanticRoleKind.platformView, - semanticsObject, - preferredLabelRepresentation: LabelRepresentation.ariaLabel, - ); + : super.withBasics( + SemanticRoleKind.platformView, + semanticsObject, + preferredLabelRepresentation: LabelRepresentation.ariaLabel, + ); /// Ignores pointer events on all platform view nodes. /// @@ -41,10 +41,7 @@ class SemanticPlatformView extends SemanticRole { if (semanticsObject.isPlatformView) { if (semanticsObject.isPlatformViewIdDirty) { - setAttribute( - 'aria-owns', - getPlatformViewDomId(semanticsObject.platformViewId), - ); + setAttribute('aria-owns', getPlatformViewDomId(semanticsObject.platformViewId)); } } else { removeAttribute('aria-owns'); diff --git a/lib/web_ui/lib/src/engine/semantics/route.dart b/lib/web_ui/lib/src/engine/semantics/route.dart index a7294d02f2595..a1eb630c00d2e 100644 --- a/lib/web_ui/lib/src/engine/semantics/route.dart +++ b/lib/web_ui/lib/src/engine/semantics/route.dart @@ -15,7 +15,8 @@ import '../util.dart'; /// * [RouteName], which provides a description for this route in the absense /// of an explicit route label set on the route itself. class SemanticRoute extends SemanticRole { - SemanticRoute(SemanticsObject semanticsObject) : super.blank(SemanticRoleKind.route, semanticsObject) { + SemanticRoute(SemanticsObject semanticsObject) + : super.blank(SemanticRoleKind.route, semanticsObject) { // The following behaviors can coexist with the route. Generic `RouteName` // and `LabelAndValue` are not used by this role because when the route // names its own route an `aria-label` is used instead of @@ -79,7 +80,7 @@ class SemanticRoute extends SemanticRole { 'namesRoute set, indicating a self-labelled route, but it is ' 'missing the label. A route should be labelled either by setting ' 'namesRoute on itself and providing a label, or by containing a ' - 'child node with namesRoute that can describe it with its content.' + 'child node with namesRoute that can describe it with its content.', ); } return true; @@ -97,10 +98,7 @@ class SemanticRoute extends SemanticRole { return; } - setAttribute( - 'aria-describedby', - routeName.semanticsObject.element.id, - ); + setAttribute('aria-describedby', routeName.semanticsObject.element.id); } @override diff --git a/lib/web_ui/lib/src/engine/semantics/scrollable.dart b/lib/web_ui/lib/src/engine/semantics/scrollable.dart index 508e5e6bd954f..38a5702e97a3f 100644 --- a/lib/web_ui/lib/src/engine/semantics/scrollable.dart +++ b/lib/web_ui/lib/src/engine/semantics/scrollable.dart @@ -24,11 +24,11 @@ import 'package:ui/ui.dart' as ui; /// viewport "scrollTop" may take positive values. class SemanticScrollable extends SemanticRole { SemanticScrollable(SemanticsObject semanticsObject) - : super.withBasics( - SemanticRoleKind.scrollable, - semanticsObject, - preferredLabelRepresentation: LabelRepresentation.ariaLabel, - ) { + : super.withBasics( + SemanticRoleKind.scrollable, + semanticsObject, + preferredLabelRepresentation: LabelRepresentation.ariaLabel, + ) { // Mark as group to prevent the browser from merging this element along with // all the children into one giant node. This is what happened with the // repro provided in https://github.com/flutter/flutter/issues/130950. @@ -67,8 +67,7 @@ class SemanticScrollable extends SemanticRole { if (!EngineSemantics.instance.shouldAcceptBrowserGesture('scroll')) { return; } - final bool doScrollForward = - _domScrollPosition > _effectiveNeutralScrollPosition; + final bool doScrollForward = _domScrollPosition > _effectiveNeutralScrollPosition; _neutralizeDomScrollPosition(); semanticsObject.recomputePositionAndSize(); @@ -76,20 +75,36 @@ class SemanticScrollable extends SemanticRole { if (doScrollForward) { if (semanticsObject.isVerticalScrollContainer) { EnginePlatformDispatcher.instance.invokeOnSemanticsAction( - viewId, semanticsId, ui.SemanticsAction.scrollUp, null); + viewId, + semanticsId, + ui.SemanticsAction.scrollUp, + null, + ); } else { assert(semanticsObject.isHorizontalScrollContainer); EnginePlatformDispatcher.instance.invokeOnSemanticsAction( - viewId, semanticsId, ui.SemanticsAction.scrollLeft, null); + viewId, + semanticsId, + ui.SemanticsAction.scrollLeft, + null, + ); } } else { if (semanticsObject.isVerticalScrollContainer) { EnginePlatformDispatcher.instance.invokeOnSemanticsAction( - viewId, semanticsId, ui.SemanticsAction.scrollDown, null); + viewId, + semanticsId, + ui.SemanticsAction.scrollDown, + null, + ); } else { assert(semanticsObject.isHorizontalScrollContainer); EnginePlatformDispatcher.instance.invokeOnSemanticsAction( - viewId, semanticsId, ui.SemanticsAction.scrollRight, null); + viewId, + semanticsId, + ui.SemanticsAction.scrollRight, + null, + ); } } } @@ -188,8 +203,7 @@ class SemanticScrollable extends SemanticRole { // Read back because the effective value depends on the amount of content. _effectiveNeutralScrollPosition = element.scrollTop.toInt(); semanticsObject - ..verticalContainerAdjustment = - _effectiveNeutralScrollPosition.toDouble() + ..verticalContainerAdjustment = _effectiveNeutralScrollPosition.toDouble() ..horizontalContainerAdjustment = 0.0; } else { // Place the _scrollOverflowElement at the end of the content and @@ -206,8 +220,7 @@ class SemanticScrollable extends SemanticRole { _effectiveNeutralScrollPosition = element.scrollLeft.toInt(); semanticsObject ..verticalContainerAdjustment = 0.0 - ..horizontalContainerAdjustment = - _effectiveNeutralScrollPosition.toDouble(); + ..horizontalContainerAdjustment = _effectiveNeutralScrollPosition.toDouble(); } } diff --git a/lib/web_ui/lib/src/engine/semantics/semantics.dart b/lib/web_ui/lib/src/engine/semantics/semantics.dart index d35c9c1e69f36..fb2a0fc034609 100644 --- a/lib/web_ui/lib/src/engine/semantics/semantics.dart +++ b/lib/web_ui/lib/src/engine/semantics/semantics.dart @@ -9,7 +9,7 @@ import 'package:meta/meta.dart'; import 'package:ui/ui.dart' as ui; import 'package:ui/ui_web/src/ui_web.dart' as ui_web; -import '../../engine.dart' show registerHotRestartListener; +import '../../engine.dart' show registerHotRestartListener; import '../alarm_clock.dart'; import '../browser_detection.dart'; import '../configuration.dart'; @@ -103,14 +103,14 @@ class EngineAccessibilityFeatures implements ui.AccessibilityFeatures { int get hashCode => _index.hashCode; EngineAccessibilityFeatures copyWith({ - bool? accessibleNavigation, - bool? invertColors, - bool? disableAnimations, - bool? boldText, - bool? reduceMotion, - bool? highContrast, - bool? onOffSwitchLabels}) - { + bool? accessibleNavigation, + bool? invertColors, + bool? disableAnimations, + bool? boldText, + bool? reduceMotion, + bool? highContrast, + bool? onOffSwitchLabels, + }) { final EngineAccessibilityFeaturesBuilder builder = EngineAccessibilityFeaturesBuilder(0); builder.accessibleNavigation = accessibleNavigation ?? this.accessibleNavigation; @@ -140,37 +140,37 @@ class EngineAccessibilityFeaturesBuilder { set accessibleNavigation(bool value) { const int accessibleNavigation = EngineAccessibilityFeatures._kAccessibleNavigation; - _index = value? _index | accessibleNavigation : _index & ~accessibleNavigation; + _index = value ? _index | accessibleNavigation : _index & ~accessibleNavigation; } set invertColors(bool value) { const int invertColors = EngineAccessibilityFeatures._kInvertColorsIndex; - _index = value? _index | invertColors : _index & ~invertColors; + _index = value ? _index | invertColors : _index & ~invertColors; } set disableAnimations(bool value) { const int disableAnimations = EngineAccessibilityFeatures._kDisableAnimationsIndex; - _index = value? _index | disableAnimations : _index & ~disableAnimations; + _index = value ? _index | disableAnimations : _index & ~disableAnimations; } set boldText(bool value) { const int boldText = EngineAccessibilityFeatures._kBoldTextIndex; - _index = value? _index | boldText : _index & ~boldText; + _index = value ? _index | boldText : _index & ~boldText; } set reduceMotion(bool value) { const int reduceMotion = EngineAccessibilityFeatures._kReduceMotionIndex; - _index = value? _index | reduceMotion : _index & ~reduceMotion; + _index = value ? _index | reduceMotion : _index & ~reduceMotion; } set highContrast(bool value) { const int highContrast = EngineAccessibilityFeatures._kHighContrastIndex; - _index = value? _index | highContrast : _index & ~highContrast; + _index = value ? _index | highContrast : _index & ~highContrast; } set onOffSwitchLabels(bool value) { const int onOffSwitchLabels = EngineAccessibilityFeatures._kOnOffSwitchLabelsIndex; - _index = value? _index | onOffSwitchLabels : _index & ~onOffSwitchLabels; + _index = value ? _index | onOffSwitchLabels : _index & ~onOffSwitchLabels; } /// Creates and returns an instance of EngineAccessibilityFeatures based on the value of _index @@ -184,8 +184,7 @@ class EngineAccessibilityFeaturesBuilder { /// This class provides private engine-side API that's not available in the /// `dart:ui` [ui.SemanticsUpdate]. class SemanticsUpdate implements ui.SemanticsUpdate { - SemanticsUpdate({List? nodeUpdates}) - : _nodeUpdates = nodeUpdates; + SemanticsUpdate({List? nodeUpdates}) : _nodeUpdates = nodeUpdates; /// Updates for individual nodes. final List? _nodeUpdates; @@ -418,7 +417,11 @@ abstract class SemanticRole { /// /// If `labelRepresentation` is true, configures the [LabelAndValue] role with /// [LabelAndValue.labelRepresentation] set to true. - SemanticRole.withBasics(this.kind, this.semanticsObject, { required LabelRepresentation preferredLabelRepresentation }) { + SemanticRole.withBasics( + this.kind, + this.semanticsObject, { + required LabelRepresentation preferredLabelRepresentation, + }) { element = _initElement(createElement(), semanticsObject); addFocusManagement(); addLiveRegion(); @@ -536,9 +539,11 @@ abstract class SemanticRole { void removeAttribute(String name) => element.removeAttribute(name); - void addEventListener(String type, DomEventListener? listener, [bool? useCapture]) => element.addEventListener(type, listener, useCapture); + void addEventListener(String type, DomEventListener? listener, [bool? useCapture]) => + element.addEventListener(type, listener, useCapture); - void removeEventListener(String type, DomEventListener? listener, [bool? useCapture]) => element.removeEventListener(type, listener, useCapture); + void removeEventListener(String type, DomEventListener? listener, [bool? useCapture]) => + element.removeEventListener(type, listener, useCapture); /// Convenience getter for the [Focusable] behavior, if any. Focusable? get focusable => _focusable; @@ -564,8 +569,14 @@ abstract class SemanticRole { LabelAndValue? _labelAndValue; /// Adds generic label features. - void addLabelAndValue({ required LabelRepresentation preferredRepresentation }) { - addSemanticBehavior(_labelAndValue = LabelAndValue(semanticsObject, this, preferredRepresentation: preferredRepresentation)); + void addLabelAndValue({required LabelRepresentation preferredRepresentation}) { + addSemanticBehavior( + _labelAndValue = LabelAndValue( + semanticsObject, + this, + preferredRepresentation: preferredRepresentation, + ), + ); } /// Adds generic functionality for handling taps and clicks. @@ -665,14 +676,15 @@ abstract class SemanticRole { /// A role used when a more specific role couldn't be assigned to the node. final class GenericRole extends SemanticRole { - GenericRole(SemanticsObject semanticsObject) : super.withBasics( - SemanticRoleKind.generic, - semanticsObject, - // Prefer sized span because if this is a leaf it is frequently a Text widget. - // But if it turns out to be a container, then LabelAndValue will automatically - // switch to `aria-label`. - preferredLabelRepresentation: LabelRepresentation.sizedSpan, - ) { + GenericRole(SemanticsObject semanticsObject) + : super.withBasics( + SemanticRoleKind.generic, + semanticsObject, + // Prefer sized span because if this is a leaf it is frequently a Text widget. + // But if it turns out to be a container, then LabelAndValue will automatically + // switch to `aria-label`. + preferredLabelRepresentation: LabelRepresentation.sizedSpan, + ) { // Typically a tappable widget would have a more specific role, such as // "link", "button", "checkbox", etc. However, there are situations when a // tappable is not a leaf node, but contains other nodes, which can also be @@ -1063,8 +1075,7 @@ class SemanticsObject { /// Whether the [childrenInTraversalOrder] field has been updated but has not /// been applied to the DOM yet. - bool get isChildrenInTraversalOrderDirty => - _isDirty(_childrenInTraversalOrderIndex); + bool get isChildrenInTraversalOrderDirty => _isDirty(_childrenInTraversalOrderIndex); void _markChildrenInTraversalOrderDirty() { _dirtyFields |= _childrenInTraversalOrderIndex; } @@ -1077,8 +1088,7 @@ class SemanticsObject { /// Whether the [childrenInHitTestOrder] field has been updated but has not /// been applied to the DOM yet. - bool get isChildrenInHitTestOrderDirty => - _isDirty(_childrenInHitTestOrderIndex); + bool get isChildrenInHitTestOrderDirty => _isDirty(_childrenInHitTestOrderIndex); void _markChildrenInHitTestOrderDirty() { _dirtyFields |= _childrenInHitTestOrderIndex; } @@ -1274,6 +1284,7 @@ class SemanticsObject { assert(owner.phase == SemanticsUpdatePhase.postUpdate); return _parent; } + SemanticsObject? _parent; /// Whether this node currently has a given [SemanticsFlag]. @@ -1302,13 +1313,11 @@ class SemanticsObject { /// Whether this object represents a vertically scrollable area. bool get isVerticalScrollContainer => - hasAction(ui.SemanticsAction.scrollDown) || - hasAction(ui.SemanticsAction.scrollUp); + hasAction(ui.SemanticsAction.scrollDown) || hasAction(ui.SemanticsAction.scrollUp); /// Whether this object represents a horizontally scrollable area. bool get isHorizontalScrollContainer => - hasAction(ui.SemanticsAction.scrollLeft) || - hasAction(ui.SemanticsAction.scrollRight); + hasAction(ui.SemanticsAction.scrollLeft) || hasAction(ui.SemanticsAction.scrollRight); /// Whether this object represents a scrollable area in any direction. bool get isScrollContainer => isVerticalScrollContainer || isHorizontalScrollContainer; @@ -1325,14 +1334,10 @@ class SemanticsObject { /// Whether this object needs screen readers attention right away. bool get isLiveRegion => - hasFlag(ui.SemanticsFlag.isLiveRegion) && - !hasFlag(ui.SemanticsFlag.isHidden); + hasFlag(ui.SemanticsFlag.isLiveRegion) && !hasFlag(ui.SemanticsFlag.isHidden); /// Whether this object represents an image with no tappable functionality. - bool get isVisualOnly => - hasFlag(ui.SemanticsFlag.isImage) && - !isTappable && - !isButton; + bool get isVisualOnly => hasFlag(ui.SemanticsFlag.isImage) && !isTappable && !isButton; /// Whether this node defines a scope for a route. bool get scopesRoute => hasFlag(ui.SemanticsFlag.scopesRoute); @@ -1547,10 +1552,8 @@ class SemanticsObject { /// z-index CSS style attribute. void updateChildren() { // Trivial case: remove all children. - if (_childrenInHitTestOrder == null || - _childrenInHitTestOrder!.isEmpty) { - if (_currentChildrenInRenderOrder == null || - _currentChildrenInRenderOrder!.isEmpty) { + if (_childrenInHitTestOrder == null || _childrenInHitTestOrder!.isEmpty) { + if (_currentChildrenInRenderOrder == null || _currentChildrenInRenderOrder!.isEmpty) { // A container element must not have been created when child list is empty. assert(_childContainerElement == null); _currentChildrenInRenderOrder = null; @@ -1605,8 +1608,7 @@ class SemanticsObject { } // Trivial case: previous list was empty => just populate the container. - if (_currentChildrenInRenderOrder == null || - _currentChildrenInRenderOrder!.isEmpty) { + if (_currentChildrenInRenderOrder == null || _currentChildrenInRenderOrder!.isEmpty) { for (final SemanticsObject child in childrenInRenderOrder) { containerElement!.append(child.element); owner._attachObject(parent: this, child: child); @@ -1646,8 +1648,7 @@ class SemanticsObject { // Scan forward until first discrepancy. while (newIndex < minLength && - previousChildrenInRenderOrder[newIndex] == - childrenInRenderOrder[newIndex]) { + previousChildrenInRenderOrder[newIndex] == childrenInRenderOrder[newIndex]) { intersectionIndicesOld.add(newIndex); newIndex += 1; } @@ -1661,8 +1662,7 @@ class SemanticsObject { // between the two lists. while (newIndex < childCount) { for (int oldIndex = 0; oldIndex < previousCount; oldIndex += 1) { - if (previousChildrenInRenderOrder[oldIndex] == - childrenInRenderOrder[newIndex]) { + if (previousChildrenInRenderOrder[oldIndex] == childrenInRenderOrder[newIndex]) { intersectionIndicesOld.add(oldIndex); break; } @@ -1676,7 +1676,7 @@ class SemanticsObject { final List stationaryIds = []; for (int i = 0; i < longestSequence.length; i += 1) { stationaryIds.add( - previousChildrenInRenderOrder[intersectionIndicesOld[longestSequence[i]!]].id + previousChildrenInRenderOrder[intersectionIndicesOld[longestSequence[i]!]].id, ); } @@ -1815,8 +1815,7 @@ class SemanticsObject { /// /// Such objects are expressed in HTML using ``. bool get isIncrementable => - hasAction(ui.SemanticsAction.increase) || - hasAction(ui.SemanticsAction.decrease); + hasAction(ui.SemanticsAction.increase) || hasAction(ui.SemanticsAction.decrease); /// Whether the object represents a button. bool get isButton => hasFlag(ui.SemanticsFlag.isButton); @@ -1832,8 +1831,7 @@ class SemanticsObject { /// elements, they are managed by the [SemanticCheckable] role, and they do /// not use the [Selectable] behavior. bool get isCheckable => - hasFlag(ui.SemanticsFlag.hasCheckedState) || - hasFlag(ui.SemanticsFlag.hasToggledState); + hasFlag(ui.SemanticsFlag.hasCheckedState) || hasFlag(ui.SemanticsFlag.hasToggledState); /// If true, this node represents something that can be annotated as /// "selected", such as a tab, or an item in a list. @@ -1878,8 +1876,7 @@ class SemanticsObject { ..width = '${_rect!.width}px' ..height = '${_rect!.height}px'; - final DomElement? containerElement = - hasChildren ? getOrCreateChildContainer() : null; + final DomElement? containerElement = hasChildren ? getOrCreateChildContainer() : null; final bool hasZeroRectOffset = _rect!.top == 0.0 && _rect!.left == 0.0; final Float32List? transform = _transform; @@ -1907,8 +1904,8 @@ class SemanticsObject { effectiveTransformIsIdentity = left == 0.0 && top == 0.0; } else { // Clone to avoid mutating _transform. - effectiveTransform = Matrix4.fromFloat32List(transform).clone() - ..translate(_rect!.left, _rect!.top); + effectiveTransform = + Matrix4.fromFloat32List(transform).clone()..translate(_rect!.left, _rect!.top); effectiveTransformIsIdentity = effectiveTransform.isIdentity(); } } else if (!hasIdentityTransform) { @@ -2023,10 +2020,10 @@ class SemanticsObject { String toString() { String result = super.toString(); assert(() { - final String children = _childrenInTraversalOrder != null && - _childrenInTraversalOrder!.isNotEmpty - ? '[${_childrenInTraversalOrder!.join(', ')}]' - : ''; + final String children = + _childrenInTraversalOrder != null && _childrenInTraversalOrder!.isNotEmpty + ? '[${_childrenInTraversalOrder!.join(', ')}]' + : ''; result = '$runtimeType(#$id, children: $children)'; return true; }()); @@ -2118,8 +2115,9 @@ class EngineSemantics { static const String announcementsHostTagName = 'flt-announcement-host'; /// Implements verbal accessibility announcements. - final AccessibilityAnnouncements accessibilityAnnouncements = - AccessibilityAnnouncements(hostElement: _initializeAccessibilityAnnouncementHost()); + final AccessibilityAnnouncements accessibilityAnnouncements = AccessibilityAnnouncements( + hostElement: _initializeAccessibilityAnnouncementHost(), + ); static DomElement _initializeAccessibilityAnnouncementHost() { final DomElement host = createDomElement(announcementsHostTagName); @@ -2156,11 +2154,9 @@ class EngineSemantics { } final EngineAccessibilityFeatures original = EnginePlatformDispatcher.instance.configuration.accessibilityFeatures - as EngineAccessibilityFeatures; - final PlatformConfiguration newConfiguration = - EnginePlatformDispatcher.instance.configuration.copyWith( - accessibilityFeatures: - original.copyWith(accessibleNavigation: value)); + as EngineAccessibilityFeatures; + final PlatformConfiguration newConfiguration = EnginePlatformDispatcher.instance.configuration + .copyWith(accessibilityFeatures: original.copyWith(accessibleNavigation: value)); EnginePlatformDispatcher.instance.configuration = newConfiguration; _semanticsEnabled = value; @@ -2372,10 +2368,7 @@ class EngineSemantics { return semanticsEnabled; } - const List pointerDebouncedGestures = [ - 'click', - 'scroll', - ]; + const List pointerDebouncedGestures = ['click', 'scroll']; if (pointerDebouncedGestures.contains(eventType)) { return _gestureMode == GestureMode.browserGestures; @@ -2548,7 +2541,8 @@ class EngineSemanticsOwner { } final bool isConsistent = _semanticsTree.keys.every(liveIds.keys.contains); - final String heading = 'The semantics node map is ${isConsistent ? 'consistent' : 'inconsistent'}'; + final String heading = + 'The semantics node map is ${isConsistent ? 'consistent' : 'inconsistent'}'; final StringBuffer message = StringBuffer('$heading:\n'); message.writeln(' Nodes in tree:'); for (final MapEntry> entry in liveIds.entries) { @@ -2639,13 +2633,15 @@ AFTER: $description } if (child._parent == null) { throw AssertionError( - 'Child #$childId of parent #${object.id} has null parent ' - 'reference.'); + 'Child #$childId of parent #${object.id} has null parent ' + 'reference.', + ); } if (!identical(child._parent, object)) { throw AssertionError( - 'Parent #${object.id} has child #$childId. However, the ' - 'child is attached to #${child._parent!.id}.'); + 'Parent #${object.id} has child #$childId. However, the ' + 'child is attached to #${child._parent!.id}.', + ); } } } diff --git a/lib/web_ui/lib/src/engine/semantics/semantics_helper.dart b/lib/web_ui/lib/src/engine/semantics/semantics_helper.dart index f0a54e9c1c338..ccf8c41151e3d 100644 --- a/lib/web_ui/lib/src/engine/semantics/semantics_helper.dart +++ b/lib/web_ui/lib/src/engine/semantics/semantics_helper.dart @@ -179,9 +179,13 @@ class DesktopSemanticsEnabler extends SemanticsEnabler { // Only listen to "click" because other kinds of events are reported via // PointerBinding. - placeholder.addEventListener('click', createDomEventListener((DomEvent event) { - tryEnableSemantics(event); - }), true); + placeholder.addEventListener( + 'click', + createDomEventListener((DomEvent event) { + tryEnableSemantics(event); + }), + true, + ); // Adding roles to semantics placeholder. 'aria-live' will make sure that // the content is announced to the assistive technology user as soon as the @@ -257,7 +261,8 @@ class MobileSemanticsEnabler extends SemanticsEnabler { if (_schedulePlaceholderRemoval) { // The event type can also be click for VoiceOver. - final bool removeNow = ui_web.browser.browserEngine != ui_web.BrowserEngine.webkit || + final bool removeNow = + ui_web.browser.browserEngine != ui_web.BrowserEngine.webkit || event.type == 'touchend' || event.type == 'pointerup' || event.type == 'click'; @@ -339,12 +344,11 @@ class MobileSemanticsEnabler extends SemanticsEnabler { return true; } - final DomRect activatingElementRect = - _semanticsPlaceholder!.getBoundingClientRect(); - final double midX = activatingElementRect.left + - (activatingElementRect.right - activatingElementRect.left) / 2; - final double midY = activatingElementRect.top + - (activatingElementRect.bottom - activatingElementRect.top) / 2; + final DomRect activatingElementRect = _semanticsPlaceholder!.getBoundingClientRect(); + final double midX = + activatingElementRect.left + (activatingElementRect.right - activatingElementRect.left) / 2; + final double midY = + activatingElementRect.top + (activatingElementRect.bottom - activatingElementRect.top) / 2; final double deltaX = activationPoint.x.toDouble() - midX; final double deltaY = activationPoint.y.toDouble() - midY; final double deltaSquared = deltaX * deltaX + deltaY * deltaY; @@ -373,9 +377,13 @@ class MobileSemanticsEnabler extends SemanticsEnabler { // Only listen to "click" because other kinds of events are reported via // PointerBinding. - placeholder.addEventListener('click', createDomEventListener((DomEvent event) { - tryEnableSemantics(event); - }), true); + placeholder.addEventListener( + 'click', + createDomEventListener((DomEvent event) { + tryEnableSemantics(event); + }), + true, + ); placeholder ..setAttribute('role', 'button') diff --git a/lib/web_ui/lib/src/engine/semantics/tappable.dart b/lib/web_ui/lib/src/engine/semantics/tappable.dart index aea41d9e9ddad..f8b4c1f90c1d3 100644 --- a/lib/web_ui/lib/src/engine/semantics/tappable.dart +++ b/lib/web_ui/lib/src/engine/semantics/tappable.dart @@ -7,11 +7,12 @@ import 'package:ui/ui.dart' as ui; /// Sets the "button" ARIA role. class SemanticButton extends SemanticRole { - SemanticButton(SemanticsObject semanticsObject) : super.withBasics( - SemanticRoleKind.button, - semanticsObject, - preferredLabelRepresentation: LabelRepresentation.domText, - ) { + SemanticButton(SemanticsObject semanticsObject) + : super.withBasics( + SemanticRoleKind.button, + semanticsObject, + preferredLabelRepresentation: LabelRepresentation.domText, + ) { addTappable(); setAriaRole('button'); } @@ -44,12 +45,7 @@ class SemanticButton extends SemanticRole { class Tappable extends SemanticBehavior { Tappable(super.semanticsObject, super.owner) { _clickListener = createDomEventListener((DomEvent click) { - PointerBinding.clickDebouncer.onClick( - click, - viewId, - semanticsObject.id, - _isListening, - ); + PointerBinding.clickDebouncer.onClick(click, viewId, semanticsObject.id, _isListening); }); owner.element.addEventListener('click', _clickListener); } @@ -63,7 +59,8 @@ class Tappable extends SemanticBehavior { @override void update() { final bool wasListening = _isListening; - _isListening = semanticsObject.enabledState() != EnabledState.disabled && semanticsObject.isTappable; + _isListening = + semanticsObject.enabledState() != EnabledState.disabled && semanticsObject.isTappable; if (wasListening != _isListening) { _updateAttribute(); } diff --git a/lib/web_ui/lib/src/engine/semantics/text_field.dart b/lib/web_ui/lib/src/engine/semantics/text_field.dart index 6c58d3b102f27..0afa27a412efb 100644 --- a/lib/web_ui/lib/src/engine/semantics/text_field.dart +++ b/lib/web_ui/lib/src/engine/semantics/text_field.dart @@ -27,8 +27,7 @@ class SemanticsTextEditingStrategy extends DefaultTextEditingStrategy { /// Initializes the [SemanticsTextEditingStrategy] singleton. /// /// This method must be called prior to accessing [instance]. - static SemanticsTextEditingStrategy ensureInitialized( - HybridTextEditing owner) { + static SemanticsTextEditingStrategy ensureInitialized(HybridTextEditing owner) { if (_instance != null && _instance?.owner == owner) { return _instance!; } @@ -123,25 +122,22 @@ class SemanticsTextEditingStrategy extends DefaultTextEditingStrategy { @override void addEventHandlers() { if (inputConfiguration.autofillGroup != null) { - subscriptions - .addAll(inputConfiguration.autofillGroup!.addInputEventListeners()); + subscriptions.addAll(inputConfiguration.autofillGroup!.addInputEventListeners()); } // Subscribe to text and selection changes. - subscriptions.add( - DomSubscription(activeDomElement, 'input', handleChange)); - subscriptions.add( - DomSubscription(activeDomElement, 'keydown', - maybeSendAction)); - subscriptions.add( - DomSubscription(domDocument, 'selectionchange', - handleChange)); + subscriptions.add(DomSubscription(activeDomElement, 'input', handleChange)); + subscriptions.add(DomSubscription(activeDomElement, 'keydown', maybeSendAction)); + subscriptions.add(DomSubscription(domDocument, 'selectionchange', handleChange)); preventDefaultForMouseEvents(); } @override - void initializeTextEditing(InputConfiguration inputConfig, - {OnChangeCallback? onChange, OnActionCallback? onAction}) { + void initializeTextEditing( + InputConfiguration inputConfig, { + OnChangeCallback? onChange, + OnActionCallback? onAction, + }) { isEnabled = true; inputConfiguration = inputConfig; applyConfiguration(inputConfig); @@ -162,8 +158,7 @@ class SemanticsTextEditingStrategy extends DefaultTextEditingStrategy { } @override - void placeForm() { - } + void placeForm() {} @override void updateElementPlacement(EditableTextGeometry textGeometry) { @@ -200,7 +195,8 @@ class SemanticsTextEditingStrategy extends DefaultTextEditingStrategy { /// used to detect text box invocation. This is because Safari issues touch /// events even when VoiceOver is enabled. class SemanticTextField extends SemanticRole { - SemanticTextField(SemanticsObject semanticsObject) : super.blank(SemanticRoleKind.textField, semanticsObject) { + SemanticTextField(SemanticsObject semanticsObject) + : super.blank(SemanticRoleKind.textField, semanticsObject) { _initializeEditableElement(); } @@ -219,9 +215,7 @@ class SemanticTextField extends SemanticRole { DomHTMLInputElement _createSingleLineField() { return createDomHTMLInputElement() - ..type = semanticsObject.hasFlag(ui.SemanticsFlag.isObscured) - ? 'password' - : 'text'; + ..type = semanticsObject.hasFlag(ui.SemanticsFlag.isObscured) ? 'password' : 'text'; } DomHTMLTextAreaElement _createMultiLineField() { @@ -241,9 +235,10 @@ class SemanticTextField extends SemanticRole { } void _initializeEditableElement() { - editableElement = semanticsObject.hasFlag(ui.SemanticsFlag.isMultiline) - ? _createMultiLineField() - : _createSingleLineField(); + editableElement = + semanticsObject.hasFlag(ui.SemanticsFlag.isMultiline) + ? _createMultiLineField() + : _createSingleLineField(); _updateEnabledState(); // On iOS, even though the semantic text field is transparent, the cursor @@ -274,18 +269,31 @@ class SemanticTextField extends SemanticRole { ..height = '${semanticsObject.rect!.height}px'; append(editableElement); - editableElement.addEventListener('focus', createDomEventListener((DomEvent event) { - // IMPORTANT: because this event listener can be triggered by either or - // both a "focus" and a "click" DOM events, this code must be idempotent. - EnginePlatformDispatcher.instance.invokeOnSemanticsAction( - viewId, semanticsObject.id, ui.SemanticsAction.focus, null); - })); - editableElement.addEventListener('click', createDomEventListener((DomEvent event) { - editableElement.focusWithoutScroll(); - })); - editableElement.addEventListener('blur', createDomEventListener((DomEvent event) { - SemanticsTextEditingStrategy._instance?.deactivate(this); - })); + editableElement.addEventListener( + 'focus', + createDomEventListener((DomEvent event) { + // IMPORTANT: because this event listener can be triggered by either or + // both a "focus" and a "click" DOM events, this code must be idempotent. + EnginePlatformDispatcher.instance.invokeOnSemanticsAction( + viewId, + semanticsObject.id, + ui.SemanticsAction.focus, + null, + ); + }), + ); + editableElement.addEventListener( + 'click', + createDomEventListener((DomEvent event) { + editableElement.focusWithoutScroll(); + }), + ); + editableElement.addEventListener( + 'blur', + createDomEventListener((DomEvent event) { + SemanticsTextEditingStrategy._instance?.deactivate(this); + }), + ); } @override diff --git a/lib/web_ui/lib/src/engine/services/buffers.dart b/lib/web_ui/lib/src/engine/services/buffers.dart index fba3823a24744..d8c08ade57fa0 100644 --- a/lib/web_ui/lib/src/engine/services/buffers.dart +++ b/lib/web_ui/lib/src/engine/services/buffers.dart @@ -6,9 +6,7 @@ import 'dart:collection'; import 'dart:typed_data'; abstract class _TypedDataBuffer extends ListBase { - _TypedDataBuffer(List buffer) - : _buffer = buffer, - _length = buffer.length; + _TypedDataBuffer(List buffer) : _buffer = buffer, _length = buffer.length; static const int _initialLength = 8; @@ -220,8 +218,7 @@ abstract class _TypedDataBuffer extends ListBase { final int newLength = _length + valuesLength; _ensureCapacity(newLength); - _buffer.setRange( - index + valuesLength, _length + valuesLength, _buffer, index); + _buffer.setRange(index + valuesLength, _length + valuesLength, _buffer, index); _buffer.setRange(index, index + valuesLength, values, start); _length = newLength; } diff --git a/lib/web_ui/lib/src/engine/services/message_codec.dart b/lib/web_ui/lib/src/engine/services/message_codec.dart index c8f9e7d8da801..13ec8f260e272 100644 --- a/lib/web_ui/lib/src/engine/services/message_codec.dart +++ b/lib/web_ui/lib/src/engine/services/message_codec.dart @@ -76,8 +76,7 @@ abstract class MethodCodec { /// /// The specified error [code], human-readable error [message], and error /// [details] correspond to the fields of [PlatformException]. - ByteData? encodeErrorEnvelope( - {required String code, String? message, dynamic details}); + ByteData? encodeErrorEnvelope({required String code, String? message, dynamic details}); } /// Thrown to indicate that a platform interaction failed in the platform @@ -97,11 +96,7 @@ class PlatformException implements Exception { /// Creates a [PlatformException] with the specified error [code] and optional /// [message], and with the optional error [details] which must be a valid /// value for the [MethodCodec] involved in the interaction. - PlatformException({ - required this.code, - this.message, - this.details, - }); + PlatformException({required this.code, this.message, this.details}); /// An error code. final String code; diff --git a/lib/web_ui/lib/src/engine/services/message_codecs.dart b/lib/web_ui/lib/src/engine/services/message_codecs.dart index 70a70c39243da..2dbe201be8f62 100644 --- a/lib/web_ui/lib/src/engine/services/message_codecs.dart +++ b/lib/web_ui/lib/src/engine/services/message_codecs.dart @@ -158,10 +158,8 @@ class JSONMethodCodec implements MethodCodec { } @override - ByteData? encodeErrorEnvelope( - {required String code, String? message, dynamic details}) { - return const JSONMessageCodec() - .encodeMessage([code, message, details]); + ByteData? encodeErrorEnvelope({required String code, String? message, dynamic details}) { + return const JSONMessageCodec().encodeMessage([code, message, details]); } } @@ -522,8 +520,7 @@ class StandardMethodCodec implements MethodCodec { } @override - ByteData encodeErrorEnvelope( - {required String code, String? message, dynamic details}) { + ByteData encodeErrorEnvelope({required String code, String? message, dynamic details}) { final WriteBuffer buffer = WriteBuffer(); buffer.putUint8(1); messageCodec.writeValue(buffer, code); diff --git a/lib/web_ui/lib/src/engine/services/serialization.dart b/lib/web_ui/lib/src/engine/services/serialization.dart index e428e6a3f06c7..aebf3554acee8 100644 --- a/lib/web_ui/lib/src/engine/services/serialization.dart +++ b/lib/web_ui/lib/src/engine/services/serialization.dart @@ -80,24 +80,21 @@ class WriteBuffer { void putInt32List(Int32List list) { assert(!_debugFinalized); _alignTo(4); - _buffer - .addAll(list.buffer.asUint8List(list.offsetInBytes, 4 * list.length)); + _buffer.addAll(list.buffer.asUint8List(list.offsetInBytes, 4 * list.length)); } /// Write all the values from an [Int64List] into the buffer. void putInt64List(Int64List list) { assert(!_debugFinalized); _alignTo(8); - _buffer - .addAll(list.buffer.asUint8List(list.offsetInBytes, 8 * list.length)); + _buffer.addAll(list.buffer.asUint8List(list.offsetInBytes, 8 * list.length)); } /// Write all the values from a [Float64List] into the buffer. void putFloat64List(Float64List list) { assert(!_debugFinalized); _alignTo(8); - _buffer - .addAll(list.buffer.asUint8List(list.offsetInBytes, 8 * list.length)); + _buffer.addAll(list.buffer.asUint8List(list.offsetInBytes, 8 * list.length)); } void _alignTo(int alignment) { @@ -176,8 +173,7 @@ class ReadBuffer { /// Reads the given number of Uint8s from the buffer. Uint8List getUint8List(int length) { - final Uint8List list = - data.buffer.asUint8List(data.offsetInBytes + _position, length); + final Uint8List list = data.buffer.asUint8List(data.offsetInBytes + _position, length); _position += length; return list; } @@ -185,8 +181,7 @@ class ReadBuffer { /// Reads the given number of Int32s from the buffer. Int32List getInt32List(int length) { _alignTo(4); - final Int32List list = - data.buffer.asInt32List(data.offsetInBytes + _position, length); + final Int32List list = data.buffer.asInt32List(data.offsetInBytes + _position, length); _position += 4 * length; return list; } @@ -194,8 +189,7 @@ class ReadBuffer { /// Reads the given number of Int64s from the buffer. Int64List getInt64List(int length) { _alignTo(8); - final Int64List list = - data.buffer.asInt64List(data.offsetInBytes + _position, length); + final Int64List list = data.buffer.asInt64List(data.offsetInBytes + _position, length); _position += 8 * length; return list; } @@ -203,8 +197,7 @@ class ReadBuffer { /// Reads the given number of Float64s from the buffer. Float64List getFloat64List(int length) { _alignTo(8); - final Float64List list = - data.buffer.asFloat64List(data.offsetInBytes + _position, length); + final Float64List list = data.buffer.asFloat64List(data.offsetInBytes + _position, length); _position += 8 * length; return list; } diff --git a/lib/web_ui/lib/src/engine/shader_data.dart b/lib/web_ui/lib/src/engine/shader_data.dart index 306472ec3515d..afd2f20658dd9 100644 --- a/lib/web_ui/lib/src/engine/shader_data.dart +++ b/lib/web_ui/lib/src/engine/shader_data.dart @@ -30,7 +30,10 @@ class ShaderData { throw const FormatException('Invalid Shader Data'); } - final List uniforms = List.filled(rawUniforms.length, UniformData.empty); + final List uniforms = List.filled( + rawUniforms.length, + UniformData.empty, + ); int textureCount = 0; int floatCount = 0; @@ -58,10 +61,7 @@ class ShaderData { final Object? rows = rawUniformData['rows']; final Object? columns = rawUniformData['columns']; - if (bitWidth is! int || - rows is! int || - arrayElements is! int || - columns is! int) { + if (bitWidth is! int || rows is! int || arrayElements is! int || columns is! int) { throw const FormatException('Invalid Shader Data'); } @@ -75,11 +75,7 @@ class ShaderData { floatCount += value; } - uniforms[i] = UniformData( - name: name, - location: location, - type: type, - ); + uniforms[i] = UniformData(name: name, location: location, type: type); } return ShaderData( source: source, @@ -96,18 +92,13 @@ class ShaderData { } class UniformData { - const UniformData({ - required this.name, - required this.location, - required this.type, - }); + const UniformData({required this.name, required this.location, required this.type}); final String name; final UniformType type; final int location; - static const UniformData empty = - UniformData(name: '', location: -1, type: UniformType.Float); + static const UniformData empty = UniformData(name: '', location: -1, type: UniformType.Float); } enum UniformType { diff --git a/lib/web_ui/lib/src/engine/shadow.dart b/lib/web_ui/lib/src/engine/shadow.dart index f083d0e962dbd..ddf0d22a2470b 100644 --- a/lib/web_ui/lib/src/engine/shadow.dart +++ b/lib/web_ui/lib/src/engine/shadow.dart @@ -91,10 +91,7 @@ ui.Rect computePenumbraBounds(ui.Rect shape, double elevation) { /// Information needed to render a shadow using CSS or canvas. @immutable class SurfaceShadowData { - const SurfaceShadowData({ - required this.blurWidth, - required this.offset, - }); + const SurfaceShadowData({required this.blurWidth, required this.offset}); /// The length in pixels of the shadow. /// @@ -122,10 +119,8 @@ SurfaceShadowData? computeShadow(ui.Rect shape, double elevation) { return null; } - final double penumbraTangentX = - (kLightRadius + shape.width * 0.5) / kLightHeight; - final double penumbraTangentY = - (kLightRadius + shape.height * 0.5) / kLightHeight; + final double penumbraTangentX = (kLightRadius + shape.width * 0.5) / kLightHeight; + final double penumbraTangentY = (kLightRadius + shape.height * 0.5) / kLightHeight; final double penumbraWidth = elevation * penumbraTangentX; final double penumbraHeight = elevation * penumbraTangentY; return SurfaceShadowData( @@ -138,14 +133,14 @@ SurfaceShadowData? computeShadow(ui.Rect shape, double elevation) { } /// Applies a CSS shadow to the [shape]. -void applyCssShadow( - DomElement? element, ui.Rect shape, double elevation, ui.Color color) { +void applyCssShadow(DomElement? element, ui.Rect shape, double elevation, ui.Color color) { final SurfaceShadowData? shadow = computeShadow(shape, elevation); if (shadow == null) { element!.style.boxShadow = 'none'; } else { color = toShadowColor(color); - element!.style.boxShadow = '${shadow.offset.dx}px ${shadow.offset.dy}px ' + element!.style.boxShadow = + '${shadow.offset.dx}px ${shadow.offset.dy}px ' '${shadow.blurWidth}px 0px rgba(${color.red}, ${color.green}, ${color.blue}, ${color.alpha / 255})'; } } diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/canvas.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/canvas.dart index 2c918a11890dc..d643821c576c3 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/canvas.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/canvas.dart @@ -10,10 +10,12 @@ import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; import 'package:ui/ui.dart' as ui; class SkwasmCanvas implements SceneCanvas { - factory SkwasmCanvas(SkwasmPictureRecorder recorder, ui.Rect cullRect) => - SkwasmCanvas.fromHandle(withStackScope((StackScope s) => - pictureRecorderBeginRecording( - recorder.handle, s.convertRectToNative(cullRect)))); + factory SkwasmCanvas(SkwasmPictureRecorder recorder, ui.Rect cullRect) => SkwasmCanvas.fromHandle( + withStackScope( + (StackScope s) => + pictureRecorderBeginRecording(recorder.handle, s.convertRectToNative(cullRect)), + ), + ); SkwasmCanvas.fromHandle(this._handle); CanvasHandle _handle; @@ -32,8 +34,13 @@ class SkwasmCanvas implements SceneCanvas { final paintHandle = (paint as SkwasmPaint).toRawPaint(); if (bounds != null) { withStackScope((StackScope s) { - canvasSaveLayer(_handle, s.convertRectToNative(bounds), paintHandle, nullptr, - ui.TileMode.clamp.index); + canvasSaveLayer( + _handle, + s.convertRectToNative(bounds), + paintHandle, + nullptr, + ui.TileMode.clamp.index, + ); }); } else { canvasSaveLayer(_handle, nullptr, paintHandle, nullptr, ui.TileMode.clamp.index); @@ -58,14 +65,24 @@ class SkwasmCanvas implements SceneCanvas { if (bounds != null) { withStackScope((StackScope s) { nativeFilter.withRawImageFilter((nativeFilterHandle) { - canvasSaveLayer(_handle, s.convertRectToNative(bounds), paintHandle, nativeFilterHandle, - (backdropTileMode ?? ui.TileMode.mirror).index); + canvasSaveLayer( + _handle, + s.convertRectToNative(bounds), + paintHandle, + nativeFilterHandle, + (backdropTileMode ?? ui.TileMode.mirror).index, + ); }, defaultBlurTileMode: ui.TileMode.mirror); }); } else { nativeFilter.withRawImageFilter((nativeFilterHandle) { - canvasSaveLayer(_handle, nullptr, paintHandle, nativeFilterHandle, - (backdropTileMode ?? ui.TileMode.mirror).index); + canvasSaveLayer( + _handle, + nullptr, + paintHandle, + nativeFilterHandle, + (backdropTileMode ?? ui.TileMode.mirror).index, + ); }, defaultBlurTileMode: ui.TileMode.mirror); } paintDispose(paintHandle); @@ -104,8 +121,7 @@ class SkwasmCanvas implements SceneCanvas { } @override - void clipRect(ui.Rect rect, - {ui.ClipOp clipOp = ui.ClipOp.intersect, bool doAntiAlias = true}) { + void clipRect(ui.Rect rect, {ui.ClipOp clipOp = ui.ClipOp.intersect, bool doAntiAlias = true}) { withStackScope((StackScope s) { canvasClipRect(_handle, s.convertRectToNative(rect), clipOp.index, doAntiAlias); }); @@ -146,11 +162,7 @@ class SkwasmCanvas implements SceneCanvas { void drawRect(ui.Rect rect, ui.Paint paint) { final paintHandle = (paint as SkwasmPaint).toRawPaint(); withStackScope((StackScope s) { - canvasDrawRect( - _handle, - s.convertRectToNative(rect), - paintHandle - ); + canvasDrawRect(_handle, s.convertRectToNative(rect), paintHandle); }); paintDispose(paintHandle); } @@ -159,11 +171,7 @@ class SkwasmCanvas implements SceneCanvas { void drawRRect(ui.RRect rrect, ui.Paint paint) { final paintHandle = (paint as SkwasmPaint).toRawPaint(); withStackScope((StackScope s) { - canvasDrawRRect( - _handle, - s.convertRRectToNative(rrect), - paintHandle - ); + canvasDrawRRect(_handle, s.convertRRectToNative(rrect), paintHandle); }); paintDispose(paintHandle); } @@ -176,7 +184,7 @@ class SkwasmCanvas implements SceneCanvas { _handle, s.convertRRectToNative(outer), s.convertRRectToNative(inner), - paintHandle + paintHandle, ); }); paintDispose(paintHandle); @@ -199,8 +207,7 @@ class SkwasmCanvas implements SceneCanvas { } @override - void drawArc(ui.Rect rect, double startAngle, double sweepAngle, - bool useCenter, ui.Paint paint) { + void drawArc(ui.Rect rect, double startAngle, double sweepAngle, bool useCenter, ui.Paint paint) { final paintHandle = (paint as SkwasmPaint).toRawPaint(); withStackScope((StackScope s) { canvasDrawArc( @@ -225,9 +232,7 @@ class SkwasmCanvas implements SceneCanvas { @override void drawImage(ui.Image image, ui.Offset offset, ui.Paint paint) { - final paintHandle = (paint as SkwasmPaint).toRawPaint( - defaultBlurTileMode: ui.TileMode.clamp, - ); + final paintHandle = (paint as SkwasmPaint).toRawPaint(defaultBlurTileMode: ui.TileMode.clamp); canvasDrawImage( _handle, (image as SkwasmImage).handle, @@ -240,18 +245,11 @@ class SkwasmCanvas implements SceneCanvas { } @override - void drawImageRect( - ui.Image image, - ui.Rect src, - ui.Rect dst, - ui.Paint paint, - ) { + void drawImageRect(ui.Image image, ui.Rect src, ui.Rect dst, ui.Paint paint) { withStackScope((StackScope scope) { final Pointer sourceRect = scope.convertRectToNative(src); final Pointer destRect = scope.convertRectToNative(dst); - final paintHandle = (paint as SkwasmPaint).toRawPaint( - defaultBlurTileMode: ui.TileMode.clamp, - ); + final paintHandle = (paint as SkwasmPaint).toRawPaint(defaultBlurTileMode: ui.TileMode.clamp); canvasDrawImageRect( _handle, (image as SkwasmImage).handle, @@ -265,18 +263,11 @@ class SkwasmCanvas implements SceneCanvas { } @override - void drawImageNine( - ui.Image image, - ui.Rect center, - ui.Rect dst, - ui.Paint paint, - ) { + void drawImageNine(ui.Image image, ui.Rect center, ui.Rect dst, ui.Paint paint) { withStackScope((StackScope scope) { final Pointer centerRect = scope.convertIRectToNative(center); final Pointer destRect = scope.convertRectToNative(dst); - final paintHandle = (paint as SkwasmPaint).toRawPaint( - defaultBlurTileMode: ui.TileMode.clamp, - ); + final paintHandle = (paint as SkwasmPaint).toRawPaint(defaultBlurTileMode: ui.TileMode.clamp); canvasDrawImageNine( _handle, (image as SkwasmImage).handle, @@ -296,63 +287,31 @@ class SkwasmCanvas implements SceneCanvas { @override void drawParagraph(ui.Paragraph paragraph, ui.Offset offset) { - canvasDrawParagraph( - _handle, - (paragraph as SkwasmParagraph).handle, - offset.dx, - offset.dy, - ); + canvasDrawParagraph(_handle, (paragraph as SkwasmParagraph).handle, offset.dx, offset.dy); } @override - void drawPoints( - ui.PointMode pointMode, - List points, - ui.Paint paint - ) => withStackScope((StackScope scope) { - final RawPointArray rawPoints = scope.convertPointArrayToNative(points); - final paintHandle = (paint as SkwasmPaint).toRawPaint(); - canvasDrawPoints( - _handle, - pointMode.index, - rawPoints, - points.length, - paintHandle, - ); - paintDispose(paintHandle); - }); + void drawPoints(ui.PointMode pointMode, List points, ui.Paint paint) => + withStackScope((StackScope scope) { + final RawPointArray rawPoints = scope.convertPointArrayToNative(points); + final paintHandle = (paint as SkwasmPaint).toRawPaint(); + canvasDrawPoints(_handle, pointMode.index, rawPoints, points.length, paintHandle); + paintDispose(paintHandle); + }); @override - void drawRawPoints( - ui.PointMode pointMode, - Float32List points, - ui.Paint paint - ) => withStackScope((StackScope scope) { - final RawPointArray rawPoints = scope.convertDoublesToNative(points); - final paintHandle = (paint as SkwasmPaint).toRawPaint(); - canvasDrawPoints( - _handle, - pointMode.index, - rawPoints, - points.length ~/ 2, - paintHandle, - ); - paintDispose(paintHandle); - }); + void drawRawPoints(ui.PointMode pointMode, Float32List points, ui.Paint paint) => + withStackScope((StackScope scope) { + final RawPointArray rawPoints = scope.convertDoublesToNative(points); + final paintHandle = (paint as SkwasmPaint).toRawPaint(); + canvasDrawPoints(_handle, pointMode.index, rawPoints, points.length ~/ 2, paintHandle); + paintDispose(paintHandle); + }); @override - void drawVertices( - ui.Vertices vertices, - ui.BlendMode blendMode, - ui.Paint paint, - ) { + void drawVertices(ui.Vertices vertices, ui.BlendMode blendMode, ui.Paint paint) { final paintHandle = (paint as SkwasmPaint).toRawPaint(); - canvasDrawVertices( - _handle, - (vertices as SkwasmVertices).handle, - blendMode.index, - paintHandle, - ); + canvasDrawVertices(_handle, (vertices as SkwasmVertices).handle, blendMode.index, paintHandle); paintDispose(paintHandle); } @@ -368,15 +327,10 @@ class SkwasmCanvas implements SceneCanvas { ) => withStackScope((StackScope scope) { final RawRSTransformArray rawTransforms = scope.convertRSTransformsToNative(transforms); final RawRect rawRects = scope.convertRectsToNative(rects); - final RawColorArray rawColors = colors != null - ? scope.convertColorArrayToNative(colors) - : nullptr; - final RawRect rawCullRect = cullRect != null - ? scope.convertRectToNative(cullRect) - : nullptr; - final paintHandle = (paint as SkwasmPaint).toRawPaint( - defaultBlurTileMode: ui.TileMode.clamp, - ); + final RawColorArray rawColors = + colors != null ? scope.convertColorArrayToNative(colors) : nullptr; + final RawRect rawCullRect = cullRect != null ? scope.convertRectToNative(cullRect) : nullptr; + final paintHandle = (paint as SkwasmPaint).toRawPaint(defaultBlurTileMode: ui.TileMode.clamp); canvasDrawAtlas( _handle, (atlas as SkwasmImage).handle, @@ -403,15 +357,10 @@ class SkwasmCanvas implements SceneCanvas { ) => withStackScope((StackScope scope) { final RawRSTransformArray rawTransforms = scope.convertDoublesToNative(rstTransforms); final RawRect rawRects = scope.convertDoublesToNative(rects); - final RawColorArray rawColors = colors != null - ? scope.convertIntsToUint32Native(colors) - : nullptr; - final RawRect rawCullRect = cullRect != null - ? scope.convertRectToNative(cullRect) - : nullptr; - final paintHandle = (paint as SkwasmPaint).toRawPaint( - defaultBlurTileMode: ui.TileMode.clamp, - ); + final RawColorArray rawColors = + colors != null ? scope.convertIntsToUint32Native(colors) : nullptr; + final RawRect rawCullRect = cullRect != null ? scope.convertRectToNative(cullRect) : nullptr; + final paintHandle = (paint as SkwasmPaint).toRawPaint(defaultBlurTileMode: ui.TileMode.clamp); canvasDrawAtlas( _handle, (atlas as SkwasmImage).handle, @@ -427,12 +376,7 @@ class SkwasmCanvas implements SceneCanvas { }); @override - void drawShadow( - ui.Path path, - ui.Color color, - double elevation, - bool transparentOccluder, - ) { + void drawShadow(ui.Path path, ui.Color color, double elevation, bool transparentOccluder) { path as SkwasmPath; canvasDrawShadow( _handle, @@ -440,7 +384,8 @@ class SkwasmCanvas implements SceneCanvas { elevation, EngineFlutterDisplay.instance.devicePixelRatio, color.value, - transparentOccluder); + transparentOccluder, + ); } @override diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/codecs.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/codecs.dart index ed317f45223cd..3cf1eeb83c7d2 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/codecs.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/codecs.dart @@ -20,11 +20,8 @@ class SkwasmImageDecoder extends BrowserImageDecoder { final int width = frame.displayWidth.toInt(); final int height = frame.displayHeight.toInt(); final SkwasmSurface surface = (renderer as SkwasmRenderer).surface; - return SkwasmImage(imageCreateFromTextureSource( - frame as JSObject, - width, - height, - surface.handle, - )); + return SkwasmImage( + imageCreateFromTextureSource(frame as JSObject, width, height, surface.handle), + ); } } diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/filters.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/filters.dart index 615a0285cdffd..a49aef28a5251 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/filters.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/filters.dart @@ -19,23 +19,19 @@ abstract class SkwasmImageFilter implements SceneImageFilter { ui.TileMode? tileMode, }) => SkwasmBlurFilter(sigmaX, sigmaY, tileMode); - factory SkwasmImageFilter.dilate({ - double radiusX = 0.0, - double radiusY = 0.0, - }) => SkwasmDilateFilter(radiusX, radiusY); + factory SkwasmImageFilter.dilate({double radiusX = 0.0, double radiusY = 0.0}) => + SkwasmDilateFilter(radiusX, radiusY); - factory SkwasmImageFilter.erode({ - double radiusX = 0.0, - double radiusY = 0.0, - }) => SkwasmErodeFilter(radiusX, radiusY); + factory SkwasmImageFilter.erode({double radiusX = 0.0, double radiusY = 0.0}) => + SkwasmErodeFilter(radiusX, radiusY); factory SkwasmImageFilter.matrix( Float64List matrix4, { - ui.FilterQuality filterQuality = ui.FilterQuality.low + ui.FilterQuality filterQuality = ui.FilterQuality.low, }) => SkwasmMatrixFilter(matrix4, filterQuality); factory SkwasmImageFilter.fromColorFilter(SkwasmColorFilter filter) => - SkwasmColorImageFilter(filter); + SkwasmColorImageFilter(filter); factory SkwasmImageFilter.fromUiFilter(ui.ImageFilter filter) { if (filter is ui.ColorFilter) { @@ -47,13 +43,11 @@ abstract class SkwasmImageFilter implements SceneImageFilter { } } - factory SkwasmImageFilter.compose( - ui.ImageFilter outer, - ui.ImageFilter inner, - ) => SkwasmComposedImageFilter( - SkwasmImageFilter.fromUiFilter(outer), - SkwasmImageFilter.fromUiFilter(inner), - ); + factory SkwasmImageFilter.compose(ui.ImageFilter outer, ui.ImageFilter inner) => + SkwasmComposedImageFilter( + SkwasmImageFilter.fromUiFilter(outer), + SkwasmImageFilter.fromUiFilter(inner), + ); /// Creates a temporary [ImageFilterHandle] and passes it to the [borrow] /// function. @@ -63,7 +57,8 @@ abstract class SkwasmImageFilter implements SceneImageFilter { /// /// The handle is deleted immediately after [borrow] returns. The [borrow] /// function must not store the handle to avoid dangling pointer bugs. - void withRawImageFilter(ImageFilterHandleBorrow borrow, { + void withRawImageFilter( + ImageFilterHandleBorrow borrow, { ui.TileMode defaultBlurTileMode = ui.TileMode.clamp, }); @@ -90,10 +85,15 @@ class SkwasmBlurFilter extends SkwasmImageFilter { ui.TileMode? get backdropTileMode => tileMode; @override - void withRawImageFilter(ImageFilterHandleBorrow borrow, { + void withRawImageFilter( + ImageFilterHandleBorrow borrow, { ui.TileMode defaultBlurTileMode = ui.TileMode.clamp, }) { - final rawImageFilter = imageFilterCreateBlur(sigmaX, sigmaY, (tileMode ?? defaultBlurTileMode).index); + final rawImageFilter = imageFilterCreateBlur( + sigmaX, + sigmaY, + (tileMode ?? defaultBlurTileMode).index, + ); borrow(rawImageFilter); imageFilterDispose(rawImageFilter); } @@ -112,7 +112,8 @@ class SkwasmDilateFilter extends SkwasmImageFilter { final double radiusY; @override - void withRawImageFilter(ImageFilterHandleBorrow borrow, { + void withRawImageFilter( + ImageFilterHandleBorrow borrow, { ui.TileMode defaultBlurTileMode = ui.TileMode.clamp, }) { final rawImageFilter = imageFilterCreateDilate(radiusX, radiusY); @@ -134,7 +135,8 @@ class SkwasmErodeFilter extends SkwasmImageFilter { final double radiusY; @override - void withRawImageFilter(ImageFilterHandleBorrow borrow, { + void withRawImageFilter( + ImageFilterHandleBorrow borrow, { ui.TileMode defaultBlurTileMode = ui.TileMode.clamp, }) { final rawImageFilter = imageFilterCreateErode(radiusX, radiusY); @@ -156,7 +158,8 @@ class SkwasmMatrixFilter extends SkwasmImageFilter { final ui.FilterQuality filterQuality; @override - void withRawImageFilter(ImageFilterHandleBorrow borrow, { + void withRawImageFilter( + ImageFilterHandleBorrow borrow, { ui.TileMode defaultBlurTileMode = ui.TileMode.clamp, }) { withStackScope((scope) { @@ -182,7 +185,8 @@ class SkwasmColorImageFilter extends SkwasmImageFilter { final SkwasmColorFilter filter; @override - void withRawImageFilter(ImageFilterHandleBorrow borrow, { + void withRawImageFilter( + ImageFilterHandleBorrow borrow, { ui.TileMode defaultBlurTileMode = ui.TileMode.clamp, }) { filter.withRawColorFilter((colroFilterHandle) { @@ -206,7 +210,8 @@ class SkwasmComposedImageFilter extends SkwasmImageFilter { final SkwasmImageFilter inner; @override - void withRawImageFilter(ImageFilterHandleBorrow borrow, { + void withRawImageFilter( + ImageFilterHandleBorrow borrow, { ui.TileMode defaultBlurTileMode = ui.TileMode.clamp, }) { outer.withRawImageFilter((outerHandle) { @@ -238,17 +243,15 @@ abstract class SkwasmColorFilter { const SkwasmColorFilter(); factory SkwasmColorFilter.fromEngineColorFilter(EngineColorFilter colorFilter) => - switch (colorFilter.type) { - ColorFilterType.mode => SkwasmModeColorFilter(colorFilter.color!, colorFilter.blendMode!), - ColorFilterType.linearToSrgbGamma => const SkwasmLinearToSrgbGammaColorFilter(), - ColorFilterType.srgbToLinearGamma => const SkwasmSrgbToLinearGammaColorFilter(), - ColorFilterType.matrix => SkwasmMatrixColorFilter(colorFilter.matrix!), - }; - - factory SkwasmColorFilter.composed( - SkwasmColorFilter outer, - SkwasmColorFilter inner, - ) => SkwasmComposedColorFilter(outer, inner); + switch (colorFilter.type) { + ColorFilterType.mode => SkwasmModeColorFilter(colorFilter.color!, colorFilter.blendMode!), + ColorFilterType.linearToSrgbGamma => const SkwasmLinearToSrgbGammaColorFilter(), + ColorFilterType.srgbToLinearGamma => const SkwasmSrgbToLinearGammaColorFilter(), + ColorFilterType.matrix => SkwasmMatrixColorFilter(colorFilter.matrix!), + }; + + factory SkwasmColorFilter.composed(SkwasmColorFilter outer, SkwasmColorFilter inner) => + SkwasmComposedColorFilter(outer, inner); /// Creates a temporary [ColorFilterHandle] and passes it to the [borrow] /// function. @@ -259,20 +262,14 @@ abstract class SkwasmColorFilter { } class SkwasmModeColorFilter extends SkwasmColorFilter { - const SkwasmModeColorFilter( - this.color, - this.blendMode, - ); + const SkwasmModeColorFilter(this.color, this.blendMode); final ui.Color color; final ui.BlendMode blendMode; @override void withRawColorFilter(ColorFilterHandleBorrow borrow) { - final rawColorFilter = colorFilterCreateMode( - color.value, - blendMode.index, - ); + final rawColorFilter = colorFilterCreateMode(color.value, blendMode.index); borrow(rawColorFilter); colorFilterDispose(rawColorFilter); } @@ -321,9 +318,7 @@ class SkwasmMatrixColorFilter extends SkwasmColorFilter { @override void withRawColorFilter(ColorFilterHandleBorrow borrow) { withStackScope((scope) { - final rawColorFilter = colorFilterCreateMatrix( - scope.convertDoublesToNative(matrix), - ); + final rawColorFilter = colorFilterCreateMatrix(scope.convertDoublesToNative(matrix)); borrow(rawColorFilter); colorFilterDispose(rawColorFilter); }); @@ -357,12 +352,10 @@ class SkwasmComposedColorFilter extends SkwasmColorFilter { class SkwasmMaskFilter extends SkwasmObjectWrapper { SkwasmMaskFilter._(MaskFilterHandle handle) : super(handle, _registry); - factory SkwasmMaskFilter.fromUiMaskFilter(ui.MaskFilter maskFilter) => - SkwasmMaskFilter._(maskFilterCreateBlur( - maskFilter.webOnlyBlurStyle.index, - maskFilter.webOnlySigma - )); + factory SkwasmMaskFilter.fromUiMaskFilter(ui.MaskFilter maskFilter) => SkwasmMaskFilter._( + maskFilterCreateBlur(maskFilter.webOnlyBlurStyle.index, maskFilter.webOnlySigma), + ); static final SkwasmFinalizationRegistry _registry = - SkwasmFinalizationRegistry(maskFilterDispose); + SkwasmFinalizationRegistry(maskFilterDispose); } diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/font_collection.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/font_collection.dart index cd1d862532968..24af20555ee03 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/font_collection.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/font_collection.dart @@ -24,7 +24,7 @@ class SkwasmTypeface extends SkwasmObjectWrapper { SkwasmTypeface(SkDataHandle data) : super(typefaceCreate(data), _registry); static final SkwasmFinalizationRegistry _registry = - SkwasmFinalizationRegistry(typefaceDispose); + SkwasmFinalizationRegistry(typefaceDispose); } class SkwasmFontCollection implements FlutterFontCollection { @@ -43,7 +43,7 @@ class SkwasmFontCollection implements FlutterFontCollection { void setDefaultFontFamilies(List families) => withStackScope((StackScope scope) { final Pointer familyPointers = - scope.allocPointerArray(families.length).cast(); + scope.allocPointerArray(families.length).cast(); for (int i = 0; i < families.length; i++) { familyPointers[i] = skStringFromDartString(families[i]); } @@ -55,8 +55,7 @@ class SkwasmFontCollection implements FlutterFontCollection { }); @override - late FontFallbackManager fontFallbackManager = - FontFallbackManager(SkwasmFallbackRegistry(this)); + late FontFallbackManager fontFallbackManager = FontFallbackManager(SkwasmFallbackRegistry(this)); @override void clear() { @@ -74,7 +73,7 @@ class SkwasmFontCollection implements FlutterFontCollection { /// match Android. if (!manifest.families.any((FontFamily family) => family.name == 'Roboto')) { manifest.families.add( - FontFamily('Roboto', [FontAsset(_robotoUrl, {})]) + FontFamily('Roboto', [FontAsset(_robotoUrl, {})]), ); } @@ -198,35 +197,40 @@ class SkwasmFallbackRegistry implements FallbackFontRegistry { final SkwasmFontCollection fontCollection; @override - List getMissingCodePoints(List codePoints, List fontFamilies) - => withStackScope((StackScope scope) { - final List typefaces = fontFamilies - .map((String family) => fontCollection.registeredTypefaces[family]) - .fold(const Iterable.empty(), - (Iterable accumulated, List? typefaces) => - typefaces == null ? accumulated : accumulated.followedBy(typefaces)).toList(); - final Pointer typefaceBuffer = scope.allocPointerArray(typefaces.length).cast(); - for (int i = 0; i < typefaces.length; i++) { - typefaceBuffer[i] = typefaces[i].handle; - } - final Pointer codePointBuffer = scope.allocInt32Array(codePoints.length); - for (int i = 0; i < codePoints.length; i++) { - codePointBuffer[i] = codePoints[i]; - } - final int missingCodePointCount = typefacesFilterCoveredCodePoints( - typefaceBuffer, - typefaces.length, - codePointBuffer, - codePoints.length - ); - return List.generate(missingCodePointCount, (int index) => codePointBuffer[index]); - }); + List getMissingCodePoints(List codePoints, List fontFamilies) => + withStackScope((StackScope scope) { + final List typefaces = + fontFamilies + .map((String family) => fontCollection.registeredTypefaces[family]) + .fold( + const Iterable.empty(), + (Iterable accumulated, List? typefaces) => + typefaces == null ? accumulated : accumulated.followedBy(typefaces), + ) + .toList(); + final Pointer typefaceBuffer = + scope.allocPointerArray(typefaces.length).cast(); + for (int i = 0; i < typefaces.length; i++) { + typefaceBuffer[i] = typefaces[i].handle; + } + final Pointer codePointBuffer = scope.allocInt32Array(codePoints.length); + for (int i = 0; i < codePoints.length; i++) { + codePointBuffer[i] = codePoints[i]; + } + final int missingCodePointCount = typefacesFilterCoveredCodePoints( + typefaceBuffer, + typefaces.length, + codePointBuffer, + codePoints.length, + ); + return List.generate(missingCodePointCount, (int index) => codePointBuffer[index]); + }); @override Future loadFallbackFont(String familyName, String url) => - fontCollection.loadFontFromUrl(familyName, url); + fontCollection.loadFontFromUrl(familyName, url); @override void updateFallbackFontFamilies(List families) => - fontCollection.setDefaultFontFamilies(families); + fontCollection.setDefaultFontFamilies(families); } diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/image.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/image.dart index 2b800ba276964..66938ce0876e8 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/image.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/image.dart @@ -11,8 +11,7 @@ import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; import 'package:ui/ui.dart' as ui; class SkwasmImage extends SkwasmObjectWrapper implements ui.Image { - SkwasmImage(ImageHandle handle) : super(handle, _registry) - { + SkwasmImage(ImageHandle handle) : super(handle, _registry) { ui.Image.onCreate?.call(this); } @@ -40,7 +39,7 @@ class SkwasmImage extends SkwasmObjectWrapper implements ui.Image { } static final SkwasmFinalizationRegistry _registry = - SkwasmFinalizationRegistry(imageDispose); + SkwasmFinalizationRegistry(imageDispose); @override void dispose() { @@ -55,20 +54,21 @@ class SkwasmImage extends SkwasmObjectWrapper implements ui.Image { int get height => imageGetHeight(handle); @override - Future toByteData( - {ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba}) async { + Future toByteData({ui.ImageByteFormat format = ui.ImageByteFormat.rawRgba}) async { if (format == ui.ImageByteFormat.png) { final ui.PictureRecorder recorder = ui.PictureRecorder(); final ui.Canvas canvas = ui.Canvas(recorder); canvas.drawImage(this, ui.Offset.zero, ui.Paint()); final DomImageBitmap bitmap = - (await (renderer as SkwasmRenderer).surface.renderPictures( - [recorder.endRecording() as SkwasmPicture], - )).imageBitmaps.single; - final DomOffscreenCanvas offscreenCanvas = - createDomOffscreenCanvas(bitmap.width.toDartInt, bitmap.height.toDartInt); + (await (renderer as SkwasmRenderer).surface.renderPictures([ + recorder.endRecording() as SkwasmPicture, + ])).imageBitmaps.single; + final DomOffscreenCanvas offscreenCanvas = createDomOffscreenCanvas( + bitmap.width.toDartInt, + bitmap.height.toDartInt, + ); final DomCanvasRenderingContextBitmapRenderer context = - offscreenCanvas.getContext('bitmaprenderer')! as DomCanvasRenderingContextBitmapRenderer; + offscreenCanvas.getContext('bitmaprenderer')! as DomCanvasRenderingContextBitmapRenderer; context.transferFromImageBitmap(bitmap); final DomBlob blob = await offscreenCanvas.convertToBlob(); final JSArrayBuffer arrayBuffer = (await blob.arrayBuffer().toDart)! as JSArrayBuffer; diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/memory.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/memory.dart index b6ca67f5f7198..d6fd60d0cbbc3 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/memory.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/memory.dart @@ -28,17 +28,18 @@ typedef DisposeFunction = void Function(Pointer); class SkwasmFinalizationRegistry { SkwasmFinalizationRegistry(this.dispose) - : registry = DomFinalizationRegistry(((ExternalDartReference address) => - dispose(Pointer.fromAddress(address.toDartObject)) - ).toJS); + : registry = DomFinalizationRegistry( + ((ExternalDartReference address) => + dispose(Pointer.fromAddress(address.toDartObject))) + .toJS, + ); final DomFinalizationRegistry registry; final DisposeFunction dispose; void register(SkwasmObjectWrapper wrapper) { final ExternalDartReference jsWrapper = wrapper.toExternalReference; - registry.registerWithToken( - jsWrapper, wrapper.handle.address.toExternalReference, jsWrapper); + registry.registerWithToken(jsWrapper, wrapper.handle.address.toExternalReference, jsWrapper); } void evict(SkwasmObjectWrapper wrapper) { diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paint.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paint.dart index 1d8ab97c44de8..40a25bf891ce7 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paint.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paint.dart @@ -56,15 +56,11 @@ class SkwasmPaint implements ui.Paint { /// If `invertColors` is true or `colorFilter` is not null, sets the /// appropriate Skia color filter. Otherwise, does nothing. void _maybeSetEffectiveColorFilter(Pointer handle) { - final nativeFilter = _colorFilter != null - ? SkwasmColorFilter.fromEngineColorFilter(_colorFilter!) - : null; + final nativeFilter = + _colorFilter != null ? SkwasmColorFilter.fromEngineColorFilter(_colorFilter!) : null; if (invertColors) { if (nativeFilter != null) { - final composedFilter = SkwasmColorFilter.composed( - _invertColorFilter, - nativeFilter, - ); + final composedFilter = SkwasmColorFilter.composed(_invertColorFilter, nativeFilter); composedFilter.withRawColorFilter((composedFilterHandle) { paintSetColorFilter(handle, composedFilterHandle); }); @@ -85,8 +81,8 @@ class SkwasmPaint implements ui.Paint { -1.0, 0, 0, 1.0, 0, // row 0, -1.0, 0, 1.0, 0, // row 0, 0, -1.0, 1.0, 0, // row - 1.0, 1.0, 1.0, 1.0, 0 - ]) + 1.0, 1.0, 1.0, 1.0, 0, + ]), ); @override @@ -132,6 +128,7 @@ class SkwasmPaint implements ui.Paint { uiShader as SkwasmShader?; _shader = uiShader; } + SkwasmShader? _shader; @override diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart index 88cb9fdc54096..ef62bba7f0fe7 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart @@ -34,22 +34,24 @@ class SkwasmLineMetrics extends SkwasmObjectWrapper implements u required double left, required double baseline, required int lineNumber, - }) => SkwasmLineMetrics._(lineMetricsCreate( - hardBreak, - ascent, - descent, - unscaledAscent, - height, - width, - left, - baseline, - lineNumber, - )); + }) => SkwasmLineMetrics._( + lineMetricsCreate( + hardBreak, + ascent, + descent, + unscaledAscent, + height, + width, + left, + baseline, + lineNumber, + ), + ); SkwasmLineMetrics._(LineMetricsHandle handle) : super(handle, _registry); static final SkwasmFinalizationRegistry _registry = - SkwasmFinalizationRegistry(lineMetricsDispose); + SkwasmFinalizationRegistry(lineMetricsDispose); @override bool get hardBreak => lineMetricsGetHardBreak(handle); @@ -86,7 +88,7 @@ class SkwasmParagraph extends SkwasmObjectWrapper implements ui.Pa SkwasmParagraph(ParagraphHandle handle) : super(handle, _registry); static final SkwasmFinalizationRegistry _registry = - SkwasmFinalizationRegistry(paragraphDispose); + SkwasmFinalizationRegistry(paragraphDispose); bool _hasCheckedForMissingCodePoints = false; @@ -135,14 +137,11 @@ class SkwasmParagraph extends SkwasmObjectWrapper implements ui.Pa final int returnedCodePointCount = paragraphGetUnresolvedCodePoints( handle, codePointBuffer, - missingCodePointCount + missingCodePointCount, ); assert(missingCodePointCount == returnedCodePointCount); renderer.fontCollection.fontFallbackManager!.addMissingCodePoints( - List.generate( - missingCodePointCount, - (int index) => codePointBuffer[index] - ) + List.generate(missingCodePointCount, (int index) => codePointBuffer[index]), ); }); } @@ -154,8 +153,7 @@ class SkwasmParagraph extends SkwasmObjectWrapper implements ui.Pa return withStackScope((StackScope scope) { final RawRect tempRect = scope.allocFloatArray(4); return List.generate(length, (int index) { - final int textDirectionIndex = - textBoxListGetBoxAtIndex(listHandle, index, tempRect); + final int textDirectionIndex = textBoxListGetBoxAtIndex(listHandle, index, tempRect); return ui.TextBox.fromLTRBD( tempRect[0], tempRect[1], @@ -172,14 +170,14 @@ class SkwasmParagraph extends SkwasmObjectWrapper implements ui.Pa int start, int end, { ui.BoxHeightStyle boxHeightStyle = ui.BoxHeightStyle.tight, - ui.BoxWidthStyle boxWidthStyle = ui.BoxWidthStyle.tight + ui.BoxWidthStyle boxWidthStyle = ui.BoxWidthStyle.tight, }) { final TextBoxListHandle listHandle = paragraphGetBoxesForRange( handle, start, end, boxHeightStyle.index, - boxWidthStyle.index + boxWidthStyle.index, ); final List boxes = _convertTextBoxList(listHandle); textBoxListDispose(listHandle); @@ -189,16 +187,8 @@ class SkwasmParagraph extends SkwasmObjectWrapper implements ui.Pa @override ui.TextPosition getPositionForOffset(ui.Offset offset) => withStackScope((StackScope scope) { final Pointer outAffinity = scope.allocInt32Array(1); - final int position = paragraphGetPositionForOffset( - handle, - offset.dx, - offset.dy, - outAffinity - ); - return ui.TextPosition( - offset: position, - affinity: ui.TextAffinity.values[outAffinity[0]], - ); + final int position = paragraphGetPositionForOffset(handle, offset.dx, offset.dy, outAffinity); + return ui.TextPosition(offset: position, affinity: ui.TextAffinity.values[outAffinity[0]]); }); @override @@ -208,12 +198,12 @@ class SkwasmParagraph extends SkwasmObjectWrapper implements ui.Pa final Pointer outRange = scope.allocUint32Array(2); final Pointer outBooleanFlags = scope.allocBoolArray(1); return paragraphGetGlyphInfoAt(handle, codeUnitOffset, outRect, outRange, outBooleanFlags) - ? ui.GlyphInfo( - scope.convertRectFromNative(outRect), - ui.TextRange(start: outRange[0], end: outRange[1]), - outBooleanFlags[0] ? ui.TextDirection.ltr : ui.TextDirection.rtl, - ) - : null; + ? ui.GlyphInfo( + scope.convertRectFromNative(outRect), + ui.TextRange(start: outRange[0], end: outRange[1]), + outBooleanFlags[0] ? ui.TextDirection.ltr : ui.TextDirection.rtl, + ) + : null; }); } @@ -223,13 +213,20 @@ class SkwasmParagraph extends SkwasmObjectWrapper implements ui.Pa final Pointer outRect = scope.allocFloatArray(4); final Pointer outRange = scope.allocUint32Array(2); final Pointer outBooleanFlags = scope.allocBoolArray(1); - return paragraphGetClosestGlyphInfoAtCoordinate(handle, offset.dx, offset.dy, outRect, outRange, outBooleanFlags) - ? ui.GlyphInfo( - scope.convertRectFromNative(outRect), - ui.TextRange(start: outRange[0], end: outRange[1]), - outBooleanFlags[0] ? ui.TextDirection.ltr : ui.TextDirection.rtl, - ) - : null; + return paragraphGetClosestGlyphInfoAtCoordinate( + handle, + offset.dx, + offset.dy, + outRect, + outRange, + outBooleanFlags, + ) + ? ui.GlyphInfo( + scope.convertRectFromNative(outRect), + ui.TextRange(start: outRange[0], end: outRange[1]), + outBooleanFlags[0] ? ui.TextDirection.ltr : ui.TextDirection.rtl, + ) + : null; }); } @@ -266,8 +263,9 @@ class SkwasmParagraph extends SkwasmObjectWrapper implements ui.Pa @override List computeLineMetrics() { final int lineCount = paragraphGetLineCount(handle); - return List.generate(lineCount, - (int index) => SkwasmLineMetrics._(paragraphGetLineMetricsAtIndex(handle, index)) + return List.generate( + lineCount, + (int index) => SkwasmLineMetrics._(paragraphGetLineMetricsAtIndex(handle, index)), ); } @@ -279,11 +277,12 @@ class SkwasmParagraph extends SkwasmObjectWrapper implements ui.Pa } void withScopedFontList( - List fontFamilies, - void Function(Pointer, int) callback) { + List fontFamilies, + void Function(Pointer, int) callback, +) { withStackScope((StackScope scope) { final Pointer familiesPtr = - scope.allocPointerArray(fontFamilies.length).cast(); + scope.allocPointerArray(fontFamilies.length).cast(); int nativeIndex = 0; for (int i = 0; i < fontFamilies.length; i++) { familiesPtr[nativeIndex] = skStringFromDartString(fontFamilies[i]); @@ -302,7 +301,7 @@ class SkwasmNativeTextStyle extends SkwasmObjectWrapper { factory SkwasmNativeTextStyle.defaultTextStyle() => SkwasmNativeTextStyle(textStyleCreate()); static final SkwasmFinalizationRegistry _registry = - SkwasmFinalizationRegistry(textStyleDispose); + SkwasmFinalizationRegistry(textStyleDispose); SkwasmNativeTextStyle copy() { return SkwasmNativeTextStyle(textStyleCopy(handle)); @@ -333,9 +332,9 @@ class SkwasmTextStyle implements ui.TextStyle { this.fontFeatures, this.fontVariations, }) : assert( - color == null || foreground == null, - 'Cannot provide both a color and a foreground\n' - 'The color argument is just a shorthand for "foreground: Paint()..color = color".', + color == null || foreground == null, + 'Cannot provide both a color and a foreground\n' + 'The color argument is just a shorthand for "foreground: Paint()..color = color".', ); void applyToNative(SkwasmNativeTextStyle style) { @@ -359,7 +358,7 @@ class SkwasmTextStyle implements ui.TextStyle { textStyleSetFontStyle( handle, (fontWeight ?? ui.FontWeight.normal).value, - (fontStyle ?? ui.FontStyle.normal).index + (fontStyle ?? ui.FontStyle.normal).index, ); } if (textBaseline != null) { @@ -368,9 +367,11 @@ class SkwasmTextStyle implements ui.TextStyle { final List effectiveFontFamilies = _computeEffectiveFontFamilies(fontFamilies); if (effectiveFontFamilies.isNotEmpty) { - withScopedFontList(effectiveFontFamilies, + withScopedFontList( + effectiveFontFamilies, (Pointer families, int count) => - textStyleAddFontFamilies(handle, families, count)); + textStyleAddFontFamilies(handle, families, count), + ); } if (fontSize != null) { @@ -386,14 +387,10 @@ class SkwasmTextStyle implements ui.TextStyle { textStyleSetHeight(handle, height!); } if (leadingDistribution != null) { - textStyleSetHalfLeading( - handle, - leadingDistribution == ui.TextLeadingDistribution.even - ); + textStyleSetHalfLeading(handle, leadingDistribution == ui.TextLeadingDistribution.even); } if (locale != null) { - final SkStringHandle localeHandle = - skStringFromDartString(locale!.toLanguageTag()); + final SkStringHandle localeHandle = skStringFromDartString(locale!.toLanguageTag()); textStyleSetLocale(handle, localeHandle); skStringFree(localeHandle); } @@ -436,10 +433,10 @@ class SkwasmTextStyle implements ui.TextStyle { final String axis = variation.axis; assert(axis.length == 4); // 4 byte code final int axisNumber = - axis.codeUnitAt(0) << 24 | - axis.codeUnitAt(1) << 16 | - axis.codeUnitAt(2) << 8 | - axis.codeUnitAt(3); + axis.codeUnitAt(0) << 24 | + axis.codeUnitAt(1) << 16 | + axis.codeUnitAt(2) << 8 | + axis.codeUnitAt(3); axisBuffer[i] = axisNumber; valueBuffer[i] = variation.value; } @@ -480,28 +477,28 @@ class SkwasmTextStyle implements ui.TextStyle { if (identical(this, other)) { return true; } - return other is SkwasmTextStyle - && other.color == color - && other.decoration == decoration - && other.decorationColor == decorationColor - && other.decorationStyle == decorationStyle - && other.fontWeight == fontWeight - && other.fontStyle == fontStyle - && other.textBaseline == textBaseline - && other.leadingDistribution == leadingDistribution - && other.fontFamily == fontFamily - && other.fontSize == fontSize - && other.letterSpacing == letterSpacing - && other.wordSpacing == wordSpacing - && other.height == height - && other.decorationThickness == decorationThickness - && other.locale == locale - && other.background == background - && other.foreground == foreground - && listEquals(other.shadows, shadows) - && listEquals(other.fontFamilyFallback, fontFamilyFallback) - && listEquals(other.fontFeatures, fontFeatures) - && listEquals(other.fontVariations, fontVariations); + return other is SkwasmTextStyle && + other.color == color && + other.decoration == decoration && + other.decorationColor == decorationColor && + other.decorationStyle == decorationStyle && + other.fontWeight == fontWeight && + other.fontStyle == fontStyle && + other.textBaseline == textBaseline && + other.leadingDistribution == leadingDistribution && + other.fontFamily == fontFamily && + other.fontSize == fontSize && + other.letterSpacing == letterSpacing && + other.wordSpacing == wordSpacing && + other.height == height && + other.decorationThickness == decorationThickness && + other.locale == locale && + other.background == background && + other.foreground == foreground && + listEquals(other.shadows, shadows) && + listEquals(other.fontFamilyFallback, fontFamilyFallback) && + listEquals(other.fontFeatures, fontFeatures) && + listEquals(other.fontVariations, fontVariations); } @override @@ -534,7 +531,7 @@ class SkwasmTextStyle implements ui.TextStyle { Object.hash( fontFeatures == null ? null : Object.hashAll(fontFeatures), fontVariations == null ? null : Object.hashAll(fontVariations), - ) + ), ); } @@ -545,7 +542,8 @@ class SkwasmTextStyle implements ui.TextStyle { final List? fontFamilyFallback = this.fontFamilyFallback; final double? fontSize = this.fontSize; final double? height = this.height; - result = 'TextStyle(' + result = + 'TextStyle(' 'color: ${color ?? "unspecified"}, ' 'decoration: ${decoration ?? "unspecified"}, ' 'decorationColor: ${decorationColor ?? "unspecified"}, ' @@ -592,8 +590,11 @@ final class SkwasmStrutStyle extends SkwasmObjectWrapper implemen if (fontFamilyFallback != null) ...fontFamilyFallback, ]); if (effectiveFontFamilies.isNotEmpty) { - withScopedFontList(effectiveFontFamilies, (Pointer families, int count) => - strutStyleSetFontFamilies(handle, families, count)); + withScopedFontList( + effectiveFontFamilies, + (Pointer families, int count) => + strutStyleSetFontFamilies(handle, families, count), + ); } if (fontSize != null) { strutStyleSetFontSize(handle, fontSize); @@ -602,9 +603,7 @@ final class SkwasmStrutStyle extends SkwasmObjectWrapper implemen strutStyleSetHeight(handle, height); } if (leadingDistribution != null) { - strutStyleSetHalfLeading( - handle, - leadingDistribution == ui.TextLeadingDistribution.even); + strutStyleSetHalfLeading(handle, leadingDistribution == ui.TextLeadingDistribution.even); } if (leading != null) { strutStyleSetLeading(handle, leading); @@ -645,7 +644,7 @@ final class SkwasmStrutStyle extends SkwasmObjectWrapper implemen ) : super(handle, _registry); static final SkwasmFinalizationRegistry _registry = - SkwasmFinalizationRegistry(strutStyleDispose); + SkwasmFinalizationRegistry(strutStyleDispose); final String? _fontFamily; final List? _fontFamilyFallback; @@ -695,7 +694,8 @@ final class SkwasmStrutStyle extends SkwasmObjectWrapper implemen final double? fontSize = _fontSize; final double? height = _height; final double? leading = _leading; - result = 'StrutStyle(' + result = + 'StrutStyle(' 'fontFamily: ${_fontFamily ?? "unspecified"}, ' 'fontFamilyFallback: ${_fontFamilyFallback ?? "unspecified"}, ' 'fontFamilyFallback: ${fontFamilyFallback != null && fontFamilyFallback.isNotEmpty ? fontFamilyFallback : "unspecified"}, ' @@ -713,7 +713,8 @@ final class SkwasmStrutStyle extends SkwasmObjectWrapper implemen } } -class SkwasmParagraphStyle extends SkwasmObjectWrapper implements ui.ParagraphStyle { +class SkwasmParagraphStyle extends SkwasmObjectWrapper + implements ui.ParagraphStyle { factory SkwasmParagraphStyle({ ui.TextAlign? textAlign, ui.TextDirection? textDirection, @@ -758,15 +759,18 @@ class SkwasmParagraphStyle extends SkwasmObjectWrapper implem paragraphStyleSetStrutStyle(handle, strutStyle.handle); } final SkwasmNativeTextStyle textStyle = - (renderer.fontCollection as SkwasmFontCollection).defaultTextStyle.copy(); + (renderer.fontCollection as SkwasmFontCollection).defaultTextStyle.copy(); final TextStyleHandle textStyleHandle = textStyle.handle; final List effectiveFontFamilies = _computeEffectiveFontFamilies([ - if (fontFamily != null) fontFamily + if (fontFamily != null) fontFamily, ]); if (effectiveFontFamilies.isNotEmpty) { - withScopedFontList(effectiveFontFamilies, (Pointer families, int count) => - textStyleAddFontFamilies(textStyleHandle, families, count)); + withScopedFontList( + effectiveFontFamilies, + (Pointer families, int count) => + textStyleAddFontFamilies(textStyleHandle, families, count), + ); } if (fontSize != null) { textStyleSetFontSize(textStyleHandle, fontSize); @@ -786,8 +790,7 @@ class SkwasmParagraphStyle extends SkwasmObjectWrapper implem ); } if (locale != null) { - final SkStringHandle localeHandle = - skStringFromDartString(locale.toLanguageTag()); + final SkStringHandle localeHandle = skStringFromDartString(locale.toLanguageTag()); textStyleSetLocale(textStyleHandle, localeHandle); skStringFree(localeHandle); } @@ -832,7 +835,7 @@ class SkwasmParagraphStyle extends SkwasmObjectWrapper implem ) : super(handle, _registry); static final SkwasmFinalizationRegistry _registry = - SkwasmFinalizationRegistry(paragraphStyleDispose); + SkwasmFinalizationRegistry(paragraphStyleDispose); final SkwasmNativeTextStyle textStyle; final String? defaultFontFamily; @@ -897,7 +900,8 @@ class SkwasmParagraphStyle extends SkwasmObjectWrapper implem assert(() { final double? fontSize = _fontSize; final double? height = _height; - result = 'ParagraphStyle(' + result = + 'ParagraphStyle(' 'textAlign: ${_textAlign ?? "unspecified"}, ' 'textDirection: ${_textDirection ?? "unspecified"}, ' 'fontWeight: ${_fontWeight ?? "unspecified"}, ' @@ -917,19 +921,15 @@ class SkwasmParagraphStyle extends SkwasmObjectWrapper implem } } -class SkwasmParagraphBuilder extends SkwasmObjectWrapper implements ui.ParagraphBuilder { - factory SkwasmParagraphBuilder( - SkwasmParagraphStyle style, - SkwasmFontCollection collection, - ) => SkwasmParagraphBuilder._(paragraphBuilderCreate( - style.handle, - collection.handle, - ), style); +class SkwasmParagraphBuilder extends SkwasmObjectWrapper + implements ui.ParagraphBuilder { + factory SkwasmParagraphBuilder(SkwasmParagraphStyle style, SkwasmFontCollection collection) => + SkwasmParagraphBuilder._(paragraphBuilderCreate(style.handle, collection.handle), style); SkwasmParagraphBuilder._(ParagraphBuilderHandle handle, this.style) : super(handle, _registry); static final SkwasmFinalizationRegistry _registry = - SkwasmFinalizationRegistry(paragraphBuilderDispose); + SkwasmFinalizationRegistry(paragraphBuilderDispose); final SkwasmParagraphStyle style; final List textStyleStack = []; @@ -944,7 +944,7 @@ class SkwasmParagraphBuilder extends SkwasmObjectWrapper im ui.PlaceholderAlignment alignment, { double scale = 1.0, double? baselineOffset, - ui.TextBaseline? baseline + ui.TextBaseline? baseline, }) { paragraphBuilderAddPlaceholder( handle, @@ -986,7 +986,7 @@ class SkwasmParagraphBuilder extends SkwasmObjectWrapper im } else { final List codeUnitList = List.generate( outSize.value, - (int index) => utf8Data[index] + (int index) => utf8Data[index], ); text = utf8.decode(codeUnitList); jsText = _utf8Decoder.decode( @@ -994,10 +994,10 @@ class SkwasmParagraphBuilder extends SkwasmObjectWrapper im // than a slice, but the TextDecoder API doesn't work on shared buffer // sources yet. // See https://bugs.chromium.org/p/chromium/issues/detail?id=1012656 - createUint8ArrayFromBuffer(skwasmInstance.wasmMemory.buffer).slice( - utf8Data.address.toJS, - (utf8Data.address + outSize.value).toJS - )); + createUint8ArrayFromBuffer( + skwasmInstance.wasmMemory.buffer, + ).slice(utf8Data.address.toJS, (utf8Data.address + outSize.value).toJS), + ); } _addGraphemeBreakData(text, jsText); @@ -1005,7 +1005,11 @@ class SkwasmParagraphBuilder extends SkwasmObjectWrapper im _addLineBreakData(text, jsText); }); - UnicodePositionBufferHandle _createBreakPositionBuffer(String text, JSString jsText, DomSegmenter segmenter) { + UnicodePositionBufferHandle _createBreakPositionBuffer( + String text, + JSString jsText, + DomSegmenter segmenter, + ) { final DomIteratorWrapper iterator = segmenter.segmentRaw(jsText).iterator(); final List breaks = []; while (iterator.moveNext()) { @@ -1022,21 +1026,31 @@ class SkwasmParagraphBuilder extends SkwasmObjectWrapper im } void _addGraphemeBreakData(String text, JSString jsText) { - final UnicodePositionBufferHandle positionBuffer = - _createBreakPositionBuffer(text, jsText, _graphemeSegmenter); + final UnicodePositionBufferHandle positionBuffer = _createBreakPositionBuffer( + text, + jsText, + _graphemeSegmenter, + ); paragraphBuilderSetGraphemeBreaksUtf16(handle, positionBuffer); unicodePositionBufferFree(positionBuffer); } void _addWordBreakData(String text, JSString jsText) { - final UnicodePositionBufferHandle positionBuffer = - _createBreakPositionBuffer(text, jsText, _wordSegmenter); + final UnicodePositionBufferHandle positionBuffer = _createBreakPositionBuffer( + text, + jsText, + _wordSegmenter, + ); paragraphBuilderSetWordBreaksUtf16(handle, positionBuffer); unicodePositionBufferFree(positionBuffer); } void _addLineBreakData(String text, JSString jsText) { - final List lineBreaks = breakLinesUsingV8BreakIterator(text, jsText, _v8BreakIterator); + final List lineBreaks = breakLinesUsingV8BreakIterator( + text, + jsText, + _v8BreakIterator, + ); final LineBreakBufferHandle lineBreakBuffer = lineBreakBufferCreate(lineBreaks.length + 1); final Pointer lineBreakPointer = lineBreakBufferGetDataPointer(lineBreakBuffer); @@ -1045,9 +1059,8 @@ class SkwasmParagraphBuilder extends SkwasmObjectWrapper im for (int i = 0; i < lineBreaks.length; i++) { final LineBreakFragment fragment = lineBreaks[i]; lineBreakPointer[i + 1].position = fragment.end; - lineBreakPointer[i + 1].lineBreakType = fragment.type == LineBreakType.mandatory - ? _kHardLineBreak - : _kSoftLineBreak; + lineBreakPointer[i + 1].lineBreakType = + fragment.type == LineBreakType.mandatory ? _kHardLineBreak : _kSoftLineBreak; } paragraphBuilderSetLineBreaksUtf16(handle, lineBreakBuffer); lineBreakBufferFree(lineBreakBuffer); @@ -1072,9 +1085,8 @@ class SkwasmParagraphBuilder extends SkwasmObjectWrapper im @override void pushStyle(ui.TextStyle textStyle) { textStyle as SkwasmTextStyle; - final SkwasmNativeTextStyle baseStyle = textStyleStack.isNotEmpty - ? textStyleStack.last - : style.textStyle; + final SkwasmNativeTextStyle baseStyle = + textStyleStack.isNotEmpty ? textStyleStack.last : style.textStyle; final SkwasmNativeTextStyle nativeStyle = baseStyle.copy(); textStyle.applyToNative(nativeStyle); textStyleStack.add(nativeStyle); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart index 7e08092ae14ed..3710a9b2ee608 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart @@ -10,15 +10,9 @@ import 'package:ui/src/engine.dart'; import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; import 'package:ui/ui.dart' as ui; -enum PathDirection { - clockwise, - counterClockwise, -} +enum PathDirection { clockwise, counterClockwise } -enum PathArcSize { - small, - large, -} +enum PathArcSize { small, large } class SkwasmPath extends SkwasmObjectWrapper implements ScenePath { factory SkwasmPath() { @@ -31,8 +25,9 @@ class SkwasmPath extends SkwasmObjectWrapper implements ScenePath { SkwasmPath.fromHandle(PathHandle handle) : super(handle, _registry); - static final SkwasmFinalizationRegistry _registry = - SkwasmFinalizationRegistry(pathDispose); + static final SkwasmFinalizationRegistry _registry = SkwasmFinalizationRegistry( + pathDispose, + ); @override ui.PathFillType get fillType => ui.PathFillType.values[pathGetFillType(handle)]; @@ -54,50 +49,37 @@ class SkwasmPath extends SkwasmObjectWrapper implements ScenePath { @override void quadraticBezierTo(double x1, double y1, double x2, double y2) => - pathQuadraticBezierTo(handle, x1, y1, x2, y2); + pathQuadraticBezierTo(handle, x1, y1, x2, y2); @override void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2) => - pathRelativeQuadraticBezierTo(handle, x1, y1, x2, y2); + pathRelativeQuadraticBezierTo(handle, x1, y1, x2, y2); @override - void cubicTo( - double x1, - double y1, - double x2, - double y2, - double x3, - double y3) => - pathCubicTo(handle, x1, y1, x2, y2, x3, y3); + void cubicTo(double x1, double y1, double x2, double y2, double x3, double y3) => + pathCubicTo(handle, x1, y1, x2, y2, x3, y3); @override - void relativeCubicTo( - double x1, - double y1, - double x2, - double y2, - double x3, - double y3) => - pathRelativeCubicTo(handle, x1, y1, x2, y2, x3, y3); + void relativeCubicTo(double x1, double y1, double x2, double y2, double x3, double y3) => + pathRelativeCubicTo(handle, x1, y1, x2, y2, x3, y3); @override void conicTo(double x1, double y1, double x2, double y2, double w) => - pathConicTo(handle, x1, y1, x2, y2, w); + pathConicTo(handle, x1, y1, x2, y2, w); @override void relativeConicTo(double x1, double y1, double x2, double y2, double w) => - pathRelativeConicTo(handle, x1, y1, x2, y2, w); + pathRelativeConicTo(handle, x1, y1, x2, y2, w); @override - void arcTo( - ui.Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) { + void arcTo(ui.Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) { withStackScope((StackScope s) { pathArcToOval( - handle, - s.convertRectToNative(rect), - ui.toDegrees(startAngle), - ui.toDegrees(sweepAngle), - forceMoveTo + handle, + s.convertRectToNative(rect), + ui.toDegrees(startAngle), + ui.toDegrees(sweepAngle), + forceMoveTo, ); }); } @@ -110,19 +92,18 @@ class SkwasmPath extends SkwasmObjectWrapper implements ScenePath { bool largeArc = false, bool clockwise = true, }) { - final PathArcSize arcSize = - largeArc ? PathArcSize.large : PathArcSize.small; + final PathArcSize arcSize = largeArc ? PathArcSize.large : PathArcSize.small; final PathDirection pathDirection = clockwise ? PathDirection.clockwise : PathDirection.counterClockwise; pathArcToRotated( - handle, - radius.x, - radius.y, - ui.toDegrees(rotation), - arcSize.index, - pathDirection.index, - arcEnd.dx, - arcEnd.dy + handle, + radius.x, + radius.y, + ui.toDegrees(rotation), + arcSize.index, + pathDirection.index, + arcEnd.dx, + arcEnd.dy, ); } @@ -134,19 +115,18 @@ class SkwasmPath extends SkwasmObjectWrapper implements ScenePath { bool largeArc = false, bool clockwise = true, }) { - final PathArcSize arcSize = - largeArc ? PathArcSize.large : PathArcSize.small; + final PathArcSize arcSize = largeArc ? PathArcSize.large : PathArcSize.small; final PathDirection pathDirection = clockwise ? PathDirection.clockwise : PathDirection.counterClockwise; pathRelativeArcToRotated( - handle, - radius.x, - radius.y, - ui.toDegrees(rotation), - arcSize.index, - pathDirection.index, - arcEndDelta.dx, - arcEndDelta.dy + handle, + radius.x, + radius.y, + ui.toDegrees(rotation), + arcSize.index, + pathDirection.index, + arcEndDelta.dx, + arcEndDelta.dy, ); } @@ -171,7 +151,7 @@ class SkwasmPath extends SkwasmObjectWrapper implements ScenePath { handle, s.convertRectToNative(rect), ui.toDegrees(startAngle), - ui.toDegrees(sweepAngle) + ui.toDegrees(sweepAngle), ); }); } @@ -203,8 +183,9 @@ class SkwasmPath extends SkwasmObjectWrapper implements ScenePath { void _addPath(ui.Path path, ui.Offset offset, bool extend, {Float64List? matrix4}) { assert(path is SkwasmPath); withStackScope((StackScope s) { - final Pointer convertedMatrix = - s.convertMatrix4toSkMatrix(matrix4 ?? Matrix4.identity().toFloat64()); + final Pointer convertedMatrix = s.convertMatrix4toSkMatrix( + matrix4 ?? Matrix4.identity().toFloat64(), + ); convertedMatrix[2] += offset.dx; convertedMatrix[5] += offset.dy; pathAddPath(handle, (path as SkwasmPath).handle, convertedMatrix, extend); @@ -222,7 +203,7 @@ class SkwasmPath extends SkwasmObjectWrapper implements ScenePath { @override ui.Path shift(ui.Offset offset) => - transform(Matrix4.translationValues(offset.dx, offset.dy, 0.0).toFloat64()); + transform(Matrix4.translationValues(offset.dx, offset.dy, 0.0).toFloat64()); @override ui.Path transform(Float64List matrix4) { @@ -242,12 +223,8 @@ class SkwasmPath extends SkwasmObjectWrapper implements ScenePath { }); } - static SkwasmPath combine( - ui.PathOperation operation, - SkwasmPath path1, - SkwasmPath path2) => - SkwasmPath.fromHandle(pathCombine( - operation.index, path1.handle, path2.handle)); + static SkwasmPath combine(ui.PathOperation operation, SkwasmPath path1, SkwasmPath path2) => + SkwasmPath.fromHandle(pathCombine(operation.index, path1.handle, path2.handle)); @override ui.PathMetrics computeMetrics({bool forceClosed = false}) { diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path_metrics.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path_metrics.dart index d6b558b356191..490ca84fe0aba 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path_metrics.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path_metrics.dart @@ -8,8 +8,7 @@ import 'dart:ffi'; import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; import 'package:ui/ui.dart' as ui; -class SkwasmPathMetrics extends IterableBase - implements ui.PathMetrics { +class SkwasmPathMetrics extends IterableBase implements ui.PathMetrics { SkwasmPathMetrics({required this.path, required this.forceClosed}); SkwasmPath path; @@ -19,12 +18,13 @@ class SkwasmPathMetrics extends IterableBase late Iterator iterator = SkwasmPathMetricIterator(path, forceClosed); } -class SkwasmPathMetricIterator extends SkwasmObjectWrapper implements Iterator { +class SkwasmPathMetricIterator extends SkwasmObjectWrapper + implements Iterator { SkwasmPathMetricIterator(SkwasmPath path, bool forceClosed) - : super(contourMeasureIterCreate(path.handle, forceClosed, 1.0), _registry); + : super(contourMeasureIterCreate(path.handle, forceClosed, 1.0), _registry); static final SkwasmFinalizationRegistry _registry = - SkwasmFinalizationRegistry(contourMeasureIterDispose); + SkwasmFinalizationRegistry(contourMeasureIterDispose); SkwasmPathMetric? _current; int _nextIndex = 0; @@ -35,7 +35,8 @@ class SkwasmPathMetricIterator extends SkwasmObjectWrapper implements SkwasmPathMetric(ContourMeasureHandle handle, this.contourIndex) : super(handle, _registry); static final SkwasmFinalizationRegistry _registry = - SkwasmFinalizationRegistry(contourMeasureDispose); + SkwasmFinalizationRegistry(contourMeasureDispose); @override final int contourIndex; @override ui.Path extractPath(double start, double end, {bool startWithMoveTo = true}) { - return SkwasmPath.fromHandle( - contourMeasureGetSegment(handle, start, end, startWithMoveTo)); + return SkwasmPath.fromHandle(contourMeasureGetSegment(handle, start, end, startWithMoveTo)); } @override ui.Tangent? getTangentForOffset(double distance) { return withStackScope((StackScope scope) { final Pointer outPosition = scope.allocFloatArray(4); - final Pointer outTangent = - Pointer.fromAddress(outPosition.address + sizeOf() * 2); - final bool result = - contourMeasureGetPosTan(handle, distance, outPosition, outTangent); + final Pointer outTangent = Pointer.fromAddress( + outPosition.address + sizeOf() * 2, + ); + final bool result = contourMeasureGetPosTan(handle, distance, outPosition, outTangent); assert(result); return ui.Tangent( ui.Offset(outPosition[0], outPosition[1]), - ui.Offset(outTangent[0], outTangent[1]) + ui.Offset(outTangent[0], outTangent[1]), ); }); } diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/picture.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/picture.dart index 887cbffc53f5c..ec90e61bf839f 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/picture.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/picture.dart @@ -10,7 +10,7 @@ class SkwasmPicture extends SkwasmObjectWrapper implements ScenePict SkwasmPicture.fromHandle(PictureHandle handle) : super(handle, _registry); static final SkwasmFinalizationRegistry _registry = - SkwasmFinalizationRegistry(pictureDispose); + SkwasmFinalizationRegistry(pictureDispose); @override Future toImage(int width, int height) async => toImageSync(width, height); @@ -26,7 +26,7 @@ class SkwasmPicture extends SkwasmObjectWrapper implements ScenePict @override ui.Image toImageSync(int width, int height) => - SkwasmImage(imageCreateFromPicture(handle, width, height)); + SkwasmImage(imageCreateFromPicture(handle, width, height)); @override ui.Rect get cullRect { @@ -38,19 +38,18 @@ class SkwasmPicture extends SkwasmObjectWrapper implements ScenePict } } -class SkwasmPictureRecorder extends SkwasmObjectWrapper implements ui.PictureRecorder { +class SkwasmPictureRecorder extends SkwasmObjectWrapper + implements ui.PictureRecorder { SkwasmPictureRecorder() : super(pictureRecorderCreate(), _registry); static final SkwasmFinalizationRegistry _registry = - SkwasmFinalizationRegistry(pictureRecorderDispose); + SkwasmFinalizationRegistry(pictureRecorderDispose); @override SkwasmPicture endRecording() { isRecording = false; - final SkwasmPicture picture = SkwasmPicture.fromHandle( - pictureRecorderEndRecording(handle) - ); + final SkwasmPicture picture = SkwasmPicture.fromHandle(pictureRecorderEndRecording(handle)); ui.Picture.onCreate?.call(picture); return picture; } diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_canvas.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_canvas.dart index 391e18a82dcd1..b1e74c6f901a0 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_canvas.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_canvas.dart @@ -16,13 +16,10 @@ typedef CanvasHandle = Pointer; @Native(symbol: 'canvas_save', isLeaf: true) external void canvasSave(CanvasHandle canvas); -@Native(symbol: 'canvas_saveLayer', isLeaf: true) +@Native( + symbol: 'canvas_saveLayer', + isLeaf: true, +) external void canvasSaveLayer( CanvasHandle canvas, RawRect rect, @@ -34,113 +31,116 @@ external void canvasSaveLayer( @Native(symbol: 'canvas_restore', isLeaf: true) external void canvasRestore(CanvasHandle canvas); -@Native( - symbol: 'canvas_restoreToCount', isLeaf: true) +@Native(symbol: 'canvas_restoreToCount', isLeaf: true) external void canvasRestoreToCount(CanvasHandle canvas, int count); @Native(symbol: 'canvas_getSaveCount', isLeaf: true) external int canvasGetSaveCount(CanvasHandle canvas); -@Native( - symbol: 'canvas_translate', isLeaf: true) +@Native(symbol: 'canvas_translate', isLeaf: true) external void canvasTranslate(CanvasHandle canvas, double dx, double dy); -@Native( - symbol: 'canvas_scale', isLeaf: true) +@Native(symbol: 'canvas_scale', isLeaf: true) external void canvasScale(CanvasHandle canvas, double sx, double sy); -@Native( - symbol: 'canvas_rotate', isLeaf: true) +@Native(symbol: 'canvas_rotate', isLeaf: true) external void canvasRotate(CanvasHandle canvas, double degrees); -@Native( - symbol: 'canvas_skew', isLeaf: true) +@Native(symbol: 'canvas_skew', isLeaf: true) external void canvasSkew(CanvasHandle canvas, double sx, double sy); -@Native( - symbol: 'canvas_transform', isLeaf: true) +@Native(symbol: 'canvas_transform', isLeaf: true) external void canvasTransform(CanvasHandle canvas, RawMatrix44 matrix); -@Native( - symbol: 'canvas_clipRect', isLeaf: true) -external void canvasClipRect( - CanvasHandle canvas, RawRect rect, int op, bool antialias); +@Native(symbol: 'canvas_clipRect', isLeaf: true) +external void canvasClipRect(CanvasHandle canvas, RawRect rect, int op, bool antialias); -@Native( - symbol: 'canvas_clipRRect', isLeaf: true) -external void canvasClipRRect( - CanvasHandle canvas, RawRRect rrect, bool antialias); +@Native(symbol: 'canvas_clipRRect', isLeaf: true) +external void canvasClipRRect(CanvasHandle canvas, RawRRect rrect, bool antialias); -@Native( - symbol: 'canvas_clipPath', isLeaf: true) -external void canvasClipPath( - CanvasHandle canvas, PathHandle path, bool antialias); +@Native(symbol: 'canvas_clipPath', isLeaf: true) +external void canvasClipPath(CanvasHandle canvas, PathHandle path, bool antialias); -@Native( - symbol: 'canvas_drawColor', isLeaf: true) +@Native(symbol: 'canvas_drawColor', isLeaf: true) external void canvasDrawColor(CanvasHandle canvas, int color, int blendMode); @Native( - symbol: 'canvas_drawLine', isLeaf: true) -external void canvasDrawLine(CanvasHandle canvas, double x1, double y1, - double x2, double y2, PaintHandle paint); + symbol: 'canvas_drawLine', + isLeaf: true, +) +external void canvasDrawLine( + CanvasHandle canvas, + double x1, + double y1, + double x2, + double y2, + PaintHandle paint, +); -@Native( - symbol: 'canvas_drawPaint', isLeaf: true) +@Native(symbol: 'canvas_drawPaint', isLeaf: true) external void canvasDrawPaint(CanvasHandle canvas, PaintHandle paint); -@Native( - symbol: 'canvas_drawRect', isLeaf: true) -external void canvasDrawRect( - CanvasHandle canvas, RawRect rect, PaintHandle paint); +@Native(symbol: 'canvas_drawRect', isLeaf: true) +external void canvasDrawRect(CanvasHandle canvas, RawRect rect, PaintHandle paint); @Native( - symbol: 'canvas_drawRRect', isLeaf: true) -external void canvasDrawRRect( - CanvasHandle canvas, RawRRect rrect, PaintHandle paint); + symbol: 'canvas_drawRRect', + isLeaf: true, +) +external void canvasDrawRRect(CanvasHandle canvas, RawRRect rrect, PaintHandle paint); @Native( - symbol: 'canvas_drawDRRect', isLeaf: true) + symbol: 'canvas_drawDRRect', + isLeaf: true, +) external void canvasDrawDRRect( - CanvasHandle canvas, RawRRect outer, RawRRect inner, PaintHandle paint); + CanvasHandle canvas, + RawRRect outer, + RawRRect inner, + PaintHandle paint, +); -@Native( - symbol: 'canvas_drawOval', isLeaf: true) -external void canvasDrawOval( - CanvasHandle canvas, RawRect oval, PaintHandle paint); +@Native(symbol: 'canvas_drawOval', isLeaf: true) +external void canvasDrawOval(CanvasHandle canvas, RawRect oval, PaintHandle paint); @Native( - symbol: 'canvas_drawCircle', isLeaf: true) + symbol: 'canvas_drawCircle', + isLeaf: true, +) external void canvasDrawCircle( - CanvasHandle canvas, double x, double y, double radius, PaintHandle paint); + CanvasHandle canvas, + double x, + double y, + double radius, + PaintHandle paint, +); @Native( - symbol: 'canvas_drawArc', isLeaf: true) + symbol: 'canvas_drawArc', + isLeaf: true, +) external void canvasDrawArc( - CanvasHandle canvas, - RawRect rect, - double startAngleDegrees, - double sweepAngleDegrees, - bool useCenter, - PaintHandle paint); + CanvasHandle canvas, + RawRect rect, + double startAngleDegrees, + double sweepAngleDegrees, + bool useCenter, + PaintHandle paint, +); @Native( - symbol: 'canvas_drawPath', isLeaf: true) -external void canvasDrawPath( - CanvasHandle canvas, PathHandle path, PaintHandle paint); + symbol: 'canvas_drawPath', + isLeaf: true, +) +external void canvasDrawPath(CanvasHandle canvas, PathHandle path, PaintHandle paint); -@Native( - symbol: 'canvas_drawPicture', isLeaf: true) +@Native(symbol: 'canvas_drawPicture', isLeaf: true) external void canvasDrawPicture(CanvasHandle canvas, PictureHandle picture); -@Native(symbol: 'canvas_drawImage', isLeaf: true) +@Native( + symbol: 'canvas_drawImage', + isLeaf: true, +) external void canvasDrawImage( CanvasHandle handle, ImageHandle image, @@ -150,14 +150,10 @@ external void canvasDrawImage( int filterQuality, ); -@Native, - Pointer, - PaintHandle, - Int, -)>(symbol: 'canvas_drawImageRect', isLeaf: true) +@Native, Pointer, PaintHandle, Int)>( + symbol: 'canvas_drawImageRect', + isLeaf: true, +) external void canvasDrawImageRect( CanvasHandle handle, ImageHandle image, @@ -167,14 +163,10 @@ external void canvasDrawImageRect( int filterQuality, ); -@Native, - Pointer, - PaintHandle, - Int, -)>(symbol: 'canvas_drawImageNine', isLeaf: true) +@Native, Pointer, PaintHandle, Int)>( + symbol: 'canvas_drawImageNine', + isLeaf: true, +) external void canvasDrawImageNine( CanvasHandle handle, ImageHandle image, @@ -185,7 +177,9 @@ external void canvasDrawImageNine( ); @Native( - symbol: 'canvas_drawShadow', isLeaf: true) + symbol: 'canvas_drawShadow', + isLeaf: true, +) external void canvasDrawShadow( CanvasHandle canvas, PathHandle path, @@ -195,12 +189,10 @@ external void canvasDrawShadow( bool transparentOccluder, ); -@Native(symbol: 'canvas_drawParagraph', isLeaf: true) +@Native( + symbol: 'canvas_drawParagraph', + isLeaf: true, +) external void canvasDrawParagraph( CanvasHandle handle, ParagraphHandle paragraphHandle, @@ -208,12 +200,10 @@ external void canvasDrawParagraph( double y, ); -@Native(symbol: 'canvas_drawVertices', isLeaf: true) +@Native( + symbol: 'canvas_drawVertices', + isLeaf: true, +) external void canvasDrawVertices( CanvasHandle handle, VerticesHandle vertices, @@ -221,13 +211,10 @@ external void canvasDrawVertices( PaintHandle paint, ); -@Native(symbol: 'canvas_drawPoints', isLeaf: true) +@Native( + symbol: 'canvas_drawPoints', + isLeaf: true, +) external void canvasDrawPoints( CanvasHandle handle, int pointMode, @@ -236,17 +223,19 @@ external void canvasDrawPoints( PaintHandle paint, ); -@Native(symbol: 'canvas_drawAtlas', isLeaf: true) +@Native< + Void Function( + CanvasHandle, + ImageHandle, + RawRSTransformArray, + RawRect, + RawColorArray, + Int, + Int, + RawRect, + PaintHandle, + ) +>(symbol: 'canvas_drawAtlas', isLeaf: true) external void canvasDrawAtlas( CanvasHandle handle, ImageHandle atlas, @@ -259,14 +248,11 @@ external void canvasDrawAtlas( PaintHandle paint, ); -@Native( - symbol: 'canvas_getTransform', isLeaf: true) +@Native(symbol: 'canvas_getTransform', isLeaf: true) external void canvasGetTransform(CanvasHandle canvas, RawMatrix44 outMatrix); -@Native( - symbol: 'canvas_getLocalClipBounds', isLeaf: true) +@Native(symbol: 'canvas_getLocalClipBounds', isLeaf: true) external void canvasGetLocalClipBounds(CanvasHandle canvas, RawRect outRect); -@Native( - symbol: 'canvas_getDeviceClipBounds', isLeaf: true) +@Native(symbol: 'canvas_getDeviceClipBounds', isLeaf: true) external void canvasGetDeviceClipBounds(CanvasHandle canvas, RawIRect outRect); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_filters.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_filters.dart index 7bf759842145b..26a107897e9d4 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_filters.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_filters.dart @@ -10,83 +10,63 @@ import 'dart:ffi'; import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; final class RawImageFilter extends Opaque {} + typedef ImageFilterHandle = Pointer; final class RawColorFilter extends Opaque {} + typedef ColorFilterHandle = Pointer; final class RawMaskFilter extends Opaque {} + typedef MaskFilterHandle = Pointer; -@Native(symbol: 'imageFilter_createBlur', isLeaf: true) -external ImageFilterHandle imageFilterCreateBlur( - double sigmaX, - double sigmaY, - int tileMode, -); - -@Native(symbol: 'imageFilter_createDilate', isLeaf: true) -external ImageFilterHandle imageFilterCreateDilate( - double radiusX, - double radiusY, -); - -@Native(symbol: 'imageFilter_createErode', isLeaf: true) -external ImageFilterHandle imageFilterCreateErode( - double radiusX, - double radiusY, -); - -@Native, - Int, -)>(symbol: 'imageFilter_createMatrix', isLeaf: true) -external ImageFilterHandle imageFilterCreateMatrix( - Pointer matrix33, - int quality, -); +@Native( + symbol: 'imageFilter_createBlur', + isLeaf: true, +) +external ImageFilterHandle imageFilterCreateBlur(double sigmaX, double sigmaY, int tileMode); + +@Native(symbol: 'imageFilter_createDilate', isLeaf: true) +external ImageFilterHandle imageFilterCreateDilate(double radiusX, double radiusY); + +@Native(symbol: 'imageFilter_createErode', isLeaf: true) +external ImageFilterHandle imageFilterCreateErode(double radiusX, double radiusY); + +@Native, Int)>( + symbol: 'imageFilter_createMatrix', + isLeaf: true, +) +external ImageFilterHandle imageFilterCreateMatrix(Pointer matrix33, int quality); @Native( - symbol: 'imageFilter_createFromColorFilter', isLeaf: true) -external ImageFilterHandle imageFilterCreateFromColorFilter( - ColorFilterHandle colorFilte -); - -@Native(symbol: 'imageFilter_compose', isLeaf: true) -external ImageFilterHandle imageFilterCompose( - ImageFilterHandle outer, - ImageFilterHandle inner, -); + symbol: 'imageFilter_createFromColorFilter', + isLeaf: true, +) +external ImageFilterHandle imageFilterCreateFromColorFilter(ColorFilterHandle colorFilte); + +@Native( + symbol: 'imageFilter_compose', + isLeaf: true, +) +external ImageFilterHandle imageFilterCompose(ImageFilterHandle outer, ImageFilterHandle inner); @Native(symbol: 'imageFilter_dispose', isLeaf: true) external void imageFilterDispose(ImageFilterHandle handle); @Native( - symbol: 'imageFilter_getFilterBounds', isLeaf: true) + symbol: 'imageFilter_getFilterBounds', + isLeaf: true, +) external void imageFilterGetFilterBounds(ImageFilterHandle handle, RawIRect inOutRect); -@Native(symbol: 'colorFilter_createMode', isLeaf: true) +@Native(symbol: 'colorFilter_createMode', isLeaf: true) external ColorFilterHandle colorFilterCreateMode(int color, int mode); -@Native -)>(symbol: 'colorFilter_createMatrix', isLeaf: true) +@Native)>( + symbol: 'colorFilter_createMatrix', + isLeaf: true, +) external ColorFilterHandle colorFilterCreateMatrix(Pointer matrix); @Native(symbol: 'colorFilter_createSRGBToLinearGamma', isLeaf: true) @@ -95,26 +75,17 @@ external ColorFilterHandle colorFilterCreateSRGBToLinearGamma(); @Native(symbol: 'colorFilter_createLinearToSRGBGamma', isLeaf: true) external ColorFilterHandle colorFilterCreateLinearToSRGBGamma(); -@Native(symbol: 'colorFilter_compose', isLeaf: true) -external ColorFilterHandle colorFilterCompose( - ColorFilterHandle outer, - ColorFilterHandle inner, -); +@Native( + symbol: 'colorFilter_compose', + isLeaf: true, +) +external ColorFilterHandle colorFilterCompose(ColorFilterHandle outer, ColorFilterHandle inner); @Native(symbol: 'colorFilter_dispose', isLeaf: true) external void colorFilterDispose(ColorFilterHandle handle); -@Native(symbol: 'maskFilter_createBlur', isLeaf: true) -external MaskFilterHandle maskFilterCreateBlur( - int blurStyle, - double sigma, -); +@Native(symbol: 'maskFilter_createBlur', isLeaf: true) +external MaskFilterHandle maskFilterCreateBlur(int blurStyle, double sigma); @Native(symbol: 'maskFilter_dispose', isLeaf: true) external void maskFilterDispose(MaskFilterHandle handle); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_fonts.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_fonts.dart index 855fdeb399a35..a02503e111fcd 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_fonts.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_fonts.dart @@ -10,9 +10,11 @@ import 'dart:ffi'; import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; final class RawFontCollection extends Opaque {} + typedef FontCollectionHandle = Pointer; final class RawTypeface extends Opaque {} + typedef TypefaceHandle = Pointer; @Native(symbol: 'fontCollection_create', isLeaf: true) @@ -27,12 +29,10 @@ external TypefaceHandle typefaceCreate(SkDataHandle fontData); @Native(symbol: 'typeface_dispose', isLeaf: true) external void typefaceDispose(TypefaceHandle handle); -@Native, - Int, - Pointer, - Int, -)>(symbol: 'typefaces_filterCoveredCodePoints', isLeaf: true) +@Native, Int, Pointer, Int)>( + symbol: 'typefaces_filterCoveredCodePoints', + isLeaf: true, +) external int typefacesFilterCoveredCodePoints( Pointer typefaces, int typefaceCount, @@ -40,18 +40,15 @@ external int typefacesFilterCoveredCodePoints( int codePointCount, ); -@Native(symbol: 'fontCollection_registerTypeface', isLeaf: true) +@Native( + symbol: 'fontCollection_registerTypeface', + isLeaf: true, +) external void fontCollectionRegisterTypeface( FontCollectionHandle handle, TypefaceHandle typeface, SkStringHandle fontName, ); -@Native(symbol: 'fontCollection_clearCaches', isLeaf: true) +@Native(symbol: 'fontCollection_clearCaches', isLeaf: true) external void fontCollectionClearCaches(FontCollectionHandle handle); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_image.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_image.dart index c0cd2e25db51c..f1acb7b009cd9 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_image.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_image.dart @@ -12,26 +12,19 @@ import 'dart:js_interop'; import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; final class RawImage extends Opaque {} + typedef ImageHandle = Pointer; -@Native(symbol: 'image_createFromPicture', isLeaf: true) -external ImageHandle imageCreateFromPicture( - PictureHandle handle, - int width, - int height, -); +@Native( + symbol: 'image_createFromPicture', + isLeaf: true, +) +external ImageHandle imageCreateFromPicture(PictureHandle handle, int width, int height); -@Native(symbol: 'image_createFromPixels', isLeaf: true) +@Native( + symbol: 'image_createFromPixels', + isLeaf: true, +) external ImageHandle imageCreateFromPixels( SkDataHandle pixelData, int width, @@ -46,14 +39,14 @@ ImageHandle imageCreateFromTextureSource( JSAny frame, int width, int height, - SurfaceHandle handle + SurfaceHandle handle, ) => ImageHandle.fromAddress( imageCreateFromTextureSourceImpl( externRefForJSAny(frame), width.toWasmI32(), height.toWasmI32(), handle.address.toWasmI32(), - ).toIntUnsigned() + ).toIntUnsigned(), ); @pragma('wasm:import', 'skwasm.image_createFromTextureSource') external WasmI32 imageCreateFromTextureSourceImpl( @@ -63,7 +56,7 @@ external WasmI32 imageCreateFromTextureSourceImpl( WasmI32 surfaceHandle, ); -@Native(symbol:'image_ref', isLeaf: true) +@Native(symbol: 'image_ref', isLeaf: true) external void imageRef(ImageHandle handle); @Native(symbol: 'image_dispose', isLeaf: true) diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_memory.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_memory.dart index 4cb95aa99489a..e1bb4c9730c32 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_memory.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_memory.dart @@ -12,6 +12,7 @@ import 'dart:typed_data'; import 'package:ui/ui.dart' as ui; final class Stack extends Opaque {} + typedef StackPointer = Pointer; /// Generic linear memory allocation @@ -97,12 +98,7 @@ class StackScope { } ui.Rect convertRectFromNative(Pointer buffer) { - return ui.Rect.fromLTRB( - buffer[0], - buffer[1], - buffer[2], - buffer[3], - ); + return ui.Rect.fromLTRB(buffer[0], buffer[1], buffer[2], buffer[3]); } Pointer convertIRectToNative(ui.Rect rect) { diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_paint.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_paint.dart index b74f14aff13ac..2e78ff03e970e 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_paint.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_paint.dart @@ -13,16 +13,8 @@ final class RawPaint extends Opaque {} typedef PaintHandle = Pointer; -typedef _PaintCreateInitSignature = PaintHandle Function( - Bool, - Int, - Int, - Int, - Float, - Int, - Int, - Float, -); +typedef _PaintCreateInitSignature = + PaintHandle Function(Bool, Int, Int, Int, Float, Int, Int, Float); @Native<_PaintCreateInitSignature>(symbol: 'paint_create', isLeaf: true) external PaintHandle paintCreate( diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path.dart index afa739b963af5..aad301fbf6a51 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path.dart @@ -37,26 +37,31 @@ external void pathRelativeMoveTo(PathHandle path, double x, double y); @Native(symbol: 'path_lineTo', isLeaf: true) external void pathLineTo(PathHandle path, double x, double y); -@Native( - symbol: 'path_relativeLineTo', - isLeaf: true) +@Native(symbol: 'path_relativeLineTo', isLeaf: true) external void pathRelativeLineTo(PathHandle path, double x, double y); @Native( symbol: 'path_quadraticBezierTo', - isLeaf: true) -external void pathQuadraticBezierTo( - PathHandle path, double x1, double y1, double x2, double y2); + isLeaf: true, +) +external void pathQuadraticBezierTo(PathHandle path, double x1, double y1, double x2, double y2); @Native( symbol: 'path_relativeQuadraticBezierTo', - isLeaf: true) + isLeaf: true, +) external void pathRelativeQuadraticBezierTo( - PathHandle path, double x1, double y1, double x2, double y2); + PathHandle path, + double x1, + double y1, + double x2, + double y2, +); @Native( symbol: 'path_cubicTo', - isLeaf: true) + isLeaf: true, +) external void pathCubicTo( PathHandle path, double x1, @@ -64,12 +69,13 @@ external void pathCubicTo( double x2, double y2, double x3, - double y3 + double y3, ); @Native( symbol: 'path_relativeCubicTo', - isLeaf: true) + isLeaf: true, +) external void pathRelativeCubicTo( PathHandle path, double x1, @@ -77,70 +83,68 @@ external void pathRelativeCubicTo( double x2, double y2, double x3, - double y3 + double y3, ); @Native( symbol: 'path_conicTo', - isLeaf: true) -external void pathConicTo( - PathHandle path, - double x1, - double y1, - double x2, - double y2, - double w -); + isLeaf: true, +) +external void pathConicTo(PathHandle path, double x1, double y1, double x2, double y2, double w); @Native( symbol: 'path_relativeConicTo', - isLeaf: true) + isLeaf: true, +) external void pathRelativeConicTo( PathHandle path, double x1, double y1, double x2, double y2, - double w + double w, ); @Native( symbol: 'path_arcToOval', - isLeaf: true) + isLeaf: true, +) external void pathArcToOval( PathHandle path, RawRect rect, double startAngle, double sweepAngle, - bool forceMoveto + bool forceMoveto, ); @Native( symbol: 'path_arcToRotated', - isLeaf: true) + isLeaf: true, +) external void pathArcToRotated( - PathHandle path, - double rx, - double ry, - double xAxisRotate, - int arcSize, - int pathDirection, - double x, - double y + PathHandle path, + double rx, + double ry, + double xAxisRotate, + int arcSize, + int pathDirection, + double x, + double y, ); @Native( symbol: 'path_relativeArcToRotated', - isLeaf: true) + isLeaf: true, +) external void pathRelativeArcToRotated( - PathHandle path, - double rx, - double ry, - double xAxisRotate, - int arcSize, - int pathDirection, - double x, - double y + PathHandle path, + double rx, + double ry, + double xAxisRotate, + int arcSize, + int pathDirection, + double x, + double y, ); @Native(symbol: 'path_addRect', isLeaf: true) @@ -149,38 +153,28 @@ external void pathAddRect(PathHandle path, RawRect oval); @Native(symbol: 'path_addOval', isLeaf: true) external void pathAddOval(PathHandle path, RawRect oval); -@Native( - symbol: 'path_addArc', - isLeaf: true) +@Native(symbol: 'path_addArc', isLeaf: true) external void pathAddArc( PathHandle path, RawRect ovalRect, double startAngleDegrees, - double sweepAngleDegrees + double sweepAngleDegrees, ); @Native( symbol: 'path_addPolygon', - isLeaf: true) -external void pathAddPolygon( - PathHandle path, - RawPointArray points, - int pointCount, - bool close -); + isLeaf: true, +) +external void pathAddPolygon(PathHandle path, RawPointArray points, int pointCount, bool close); @Native(symbol: 'path_addRRect', isLeaf: true) external void pathAddRRect(PathHandle path, RawRRect rrectValues); @Native( symbol: 'path_addPath', - isLeaf: true) -external void pathAddPath( - PathHandle path, - PathHandle other, - RawMatrix33 matrix33, - bool extendPath -); + isLeaf: true, +) +external void pathAddPath(PathHandle path, PathHandle other, RawMatrix33 matrix33, bool extendPath); @Native(symbol: 'path_close', isLeaf: true) external void pathClose(PathHandle path); @@ -197,9 +191,7 @@ external void pathTransform(PathHandle path, RawMatrix33 matrix33); @Native(symbol: 'path_getBounds', isLeaf: true) external void pathGetBounds(PathHandle path, RawRect outRect); -@Native( - symbol: 'path_combine', - isLeaf: true) +@Native(symbol: 'path_combine', isLeaf: true) external PathHandle pathCombine(int operation, PathHandle path1, PathHandle path2); @Native(symbol: 'path_getSvgString', isLeaf: true) diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path_metrics.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path_metrics.dart index 01705325ce947..c3cb666a0ba98 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path_metrics.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_path_metrics.dart @@ -17,17 +17,18 @@ typedef ContourMeasureHandle = Pointer; typedef ContourMeasureIterHandle = Pointer; @Native( - symbol: 'contourMeasureIter_create') + symbol: 'contourMeasureIter_create', +) external ContourMeasureIterHandle contourMeasureIterCreate( - PathHandle path, bool forceClosed, double resScale); + PathHandle path, + bool forceClosed, + double resScale, +); -@Native( - symbol: 'contourMeasureIter_next') -external ContourMeasureHandle contourMeasureIterNext( - ContourMeasureIterHandle handle); +@Native(symbol: 'contourMeasureIter_next') +external ContourMeasureHandle contourMeasureIterNext(ContourMeasureIterHandle handle); -@Native( - symbol: 'contourMeasureIter_dispose') +@Native(symbol: 'contourMeasureIter_dispose') external void contourMeasureIterDispose(ContourMeasureIterHandle handle); @Native(symbol: 'contourMeasure_dispose') @@ -39,13 +40,22 @@ external double contourMeasureLength(ContourMeasureHandle handle); @Native(symbol: 'contourMeasure_isClosed') external bool contourMeasureIsClosed(ContourMeasureHandle handle); -@Native< - Bool Function(ContourMeasureHandle, Float, RawPointArray, - RawPointArray)>(symbol: 'contourMeasure_getPosTan') -external bool contourMeasureGetPosTan(ContourMeasureHandle handle, - double distance, RawPointArray outPosition, RawPointArray outTangent); +@Native( + symbol: 'contourMeasure_getPosTan', +) +external bool contourMeasureGetPosTan( + ContourMeasureHandle handle, + double distance, + RawPointArray outPosition, + RawPointArray outTangent, +); @Native( - symbol: 'contourMeasure_getSegment') -external PathHandle contourMeasureGetSegment(ContourMeasureHandle handle, - double start, double stop, bool startWithMoveTo); + symbol: 'contourMeasure_getSegment', +) +external PathHandle contourMeasureGetSegment( + ContourMeasureHandle handle, + double start, + double stop, + bool startWithMoveTo, +); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_picture.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_picture.dart index a1499068df49b..2027e886c3125 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_picture.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_picture.dart @@ -10,43 +10,39 @@ import 'dart:ffi'; import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; final class RawPictureRecorder extends Opaque {} + typedef PictureRecorderHandle = Pointer; final class RawPicture extends Opaque {} + typedef PictureHandle = Pointer; -@Native( - symbol: 'pictureRecorder_create', - isLeaf: true) +@Native(symbol: 'pictureRecorder_create', isLeaf: true) external PictureRecorderHandle pictureRecorderCreate(); -@Native( - symbol: 'pictureRecorder_dispose', - isLeaf: true) +@Native(symbol: 'pictureRecorder_dispose', isLeaf: true) external void pictureRecorderDispose(PictureRecorderHandle picture); @Native( symbol: 'pictureRecorder_beginRecording', - isLeaf: true) + isLeaf: true, +) external CanvasHandle pictureRecorderBeginRecording( - PictureRecorderHandle picture, RawRect cullRect); + PictureRecorderHandle picture, + RawRect cullRect, +); @Native( symbol: 'pictureRecorder_endRecording', - isLeaf: true) + isLeaf: true, +) external PictureHandle pictureRecorderEndRecording(PictureRecorderHandle picture); -@Native( - symbol: 'picture_dispose', - isLeaf: true) +@Native(symbol: 'picture_dispose', isLeaf: true) external void pictureDispose(PictureHandle handle); -@Native( - symbol: 'picture_approximateBytesUsed', - isLeaf: true) +@Native(symbol: 'picture_approximateBytesUsed', isLeaf: true) external int pictureApproximateBytesUsed(PictureHandle handle); -@Native( - symbol: 'picture_getCullRect', - isLeaf: true) +@Native(symbol: 'picture_getCullRect', isLeaf: true) external void pictureGetCullRect(PictureHandle handle, RawRect outRect); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_shaders.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_shaders.dart index 9bda0b4e08e33..a4dfd920249c4 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_shaders.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_shaders.dart @@ -10,19 +10,17 @@ import 'dart:ffi'; import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; final class RawShader extends Opaque {} + typedef ShaderHandle = Pointer; final class RawRuntimeEffect extends Opaque {} + typedef RuntimeEffectHandle = Pointer; -@Native, - Int, - Int, - RawMatrix33, -)>(symbol: 'shader_createLinearGradient', isLeaf: true) +@Native, Int, Int, RawMatrix33)>( + symbol: 'shader_createLinearGradient', + isLeaf: true, +) external ShaderHandle shaderCreateLinearGradient( RawPointArray endPoints, // two points RawColorArray colors, @@ -32,16 +30,9 @@ external ShaderHandle shaderCreateLinearGradient( RawMatrix33 matrix, // Can be nullptr ); -@Native, - Int, - Int, - RawMatrix33, -)>(symbol: 'shader_createRadialGradient', isLeaf: true) +@Native< + ShaderHandle Function(Float, Float, Float, RawColorArray, Pointer, Int, Int, RawMatrix33) +>(symbol: 'shader_createRadialGradient', isLeaf: true) external ShaderHandle shaderCreateRadialGradient( double centerX, double centerY, @@ -53,16 +44,18 @@ external ShaderHandle shaderCreateRadialGradient( RawMatrix33 localMatrix, ); -@Native, - Int, - Int, - RawMatrix33, -)>(symbol: 'shader_createConicalGradient', isLeaf: true) +@Native< + ShaderHandle Function( + RawPointArray, + Float, + Float, + RawColorArray, + Pointer, + Int, + Int, + RawMatrix33, + ) +>(symbol: 'shader_createConicalGradient', isLeaf: true) external ShaderHandle shaderCreateConicalGradient( RawPointArray endPoints, // Two points, double startRadius, @@ -74,17 +67,19 @@ external ShaderHandle shaderCreateConicalGradient( RawMatrix33 localMatrix, ); -@Native, - Int, - Int, - Float, - Float, - RawMatrix33, -)>(symbol: 'shader_createSweepGradient', isLeaf: true) +@Native< + ShaderHandle Function( + Float, + Float, + RawColorArray, + Pointer, + Int, + Int, + Float, + Float, + RawMatrix33, + ) +>(symbol: 'shader_createSweepGradient', isLeaf: true) external ShaderHandle shaderCreateSweepGradient( double centerX, double centerY, @@ -94,7 +89,7 @@ external ShaderHandle shaderCreateSweepGradient( int tileMode, double startAngle, double endAngle, - RawMatrix33 localMatrix + RawMatrix33 localMatrix, ); @Native(symbol: 'shader_dispose', isLeaf: true) @@ -109,26 +104,21 @@ external void runtimeEffectDispose(RuntimeEffectHandle handle); @Native(symbol: 'runtimeEffect_getUniformSize', isLeaf: true) external int runtimeEffectGetUniformSize(RuntimeEffectHandle handle); -@Native, - Size -)>(symbol: 'shader_createRuntimeEffectShader', isLeaf: true) +@Native, Size)>( + symbol: 'shader_createRuntimeEffectShader', + isLeaf: true, +) external ShaderHandle shaderCreateRuntimeEffectShader( RuntimeEffectHandle runtimeEffect, SkDataHandle uniforms, Pointer childShaders, - int childCount + int childCount, ); -@Native(symbol: 'shader_createFromImage', isLeaf: true) +@Native( + symbol: 'shader_createFromImage', + isLeaf: true, +) external ShaderHandle shaderCreateFromImage( ImageHandle handle, int tileModeX, diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_skdata.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_skdata.dart index 8d25154f870df..ecfb023821c30 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_skdata.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_skdata.dart @@ -8,6 +8,7 @@ library skwasm_impl; import 'dart:ffi'; final class RawSkData extends Opaque {} + typedef SkDataHandle = Pointer; @Native(symbol: 'skData_create', isLeaf: true) diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_skstring.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_skstring.dart index 7b1508e77b8f6..ecf9067c30a22 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_skstring.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_skstring.dart @@ -9,9 +9,11 @@ import 'dart:convert'; import 'dart:ffi'; final class RawSkString extends Opaque {} + typedef SkStringHandle = Pointer; final class RawSkString16 extends Opaque {} + typedef SkString16Handle = Pointer; @Native(symbol: 'skString_allocate', isLeaf: true) diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_surface.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_surface.dart index 22b7462eec9bc..c08199572fdc5 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_surface.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_surface.dart @@ -9,9 +9,11 @@ import 'dart:ffi'; import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; final class RawSurface extends Opaque {} + typedef SurfaceHandle = Pointer; final class RawRenderCallback extends Opaque {} + typedef OnRenderCallbackHandle = Pointer; typedef CallbackId = int; @@ -24,29 +26,25 @@ external int surfaceGetThreadId(SurfaceHandle handle); @Native( symbol: 'surface_setCallbackHandler', - isLeaf: true) -external void surfaceSetCallbackHandler( - SurfaceHandle surface, - OnRenderCallbackHandle callback, -); + isLeaf: true, +) +external void surfaceSetCallbackHandler(SurfaceHandle surface, OnRenderCallbackHandle callback); -@Native( - symbol: 'surface_destroy', - isLeaf: true) +@Native(symbol: 'surface_destroy', isLeaf: true) external void surfaceDestroy(SurfaceHandle surface); @Native, Int)>( symbol: 'surface_renderPictures', - isLeaf: true) -external CallbackId surfaceRenderPictures(SurfaceHandle surface, Pointer picture, int count); - -@Native(symbol: 'surface_rasterizeImage', isLeaf: true) -external CallbackId surfaceRasterizeImage( - SurfaceHandle handle, - ImageHandle image, - int format, + isLeaf: true, +) +external CallbackId surfaceRenderPictures( + SurfaceHandle surface, + Pointer picture, + int count, ); + +@Native( + symbol: 'surface_rasterizeImage', + isLeaf: true, +) +external CallbackId surfaceRasterizeImage(SurfaceHandle handle, ImageHandle image, int format); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_vertices.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_vertices.dart index dd9f61b914195..d7d81a7c1098a 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_vertices.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_vertices.dart @@ -10,17 +10,20 @@ import 'dart:ffi'; import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; final class RawVertices extends Opaque {} + typedef VerticesHandle = Pointer; -@Native indices, -)>(symbol: 'vertices_create', isLeaf: true) +@Native< + VerticesHandle Function( + Int vertexMode, + Int vertexCount, + RawPointArray positions, + RawPointArray textureCoordinates, + RawColorArray colors, + Int indexCount, + Pointer indices, + ) +>(symbol: 'vertices_create', isLeaf: true) external VerticesHandle verticesCreate( int vertexMode, int vertexCount, diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_line_metrics.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_line_metrics.dart index c00d4acd1641e..409bd5bfb3897 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_line_metrics.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_line_metrics.dart @@ -8,19 +8,12 @@ library skwasm_impl; import 'dart:ffi'; final class RawLineMetrics extends Opaque {} + typedef LineMetricsHandle = Pointer; -@Native(symbol: 'lineMetrics_create', isLeaf: true) +@Native< + LineMetricsHandle Function(Bool, Double, Double, Double, Double, Double, Double, Double, Size) +>(symbol: 'lineMetrics_create', isLeaf: true) external LineMetricsHandle lineMetricsCreate( bool hardBreak, double ascent, @@ -30,7 +23,7 @@ external LineMetricsHandle lineMetricsCreate( double width, double left, double baseline, - int lineNumber + int lineNumber, ); @Native(symbol: 'lineMetrics_dispose', isLeaf: true) diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph.dart index f8a3e811719a7..8f541a678859a 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph.dart @@ -10,15 +10,19 @@ import 'dart:ffi'; import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; final class RawParagraph extends Opaque {} + typedef ParagraphHandle = Pointer; final class RawTextBoxList extends Opaque {} + typedef TextBoxListHandle = Pointer; final class RawUnicodePositionBuffer extends Opaque {} + typedef UnicodePositionBufferHandle = Pointer; final class RawLineBreakBuffer extends Opaque {} + typedef LineBreakBufferHandle = Pointer; final class LineBreak extends Struct { @@ -59,12 +63,10 @@ external bool paragraphGetDidExceedMaxLines(ParagraphHandle handle); @Native(symbol: 'paragraph_layout', isLeaf: true) external void paragraphLayout(ParagraphHandle handle, double width); -@Native -)>(symbol: 'paragraph_getPositionForOffset', isLeaf: true) +@Native)>( + symbol: 'paragraph_getPositionForOffset', + isLeaf: true, +) external int paragraphGetPositionForOffset( ParagraphHandle handle, double offsetX, @@ -72,29 +74,33 @@ external int paragraphGetPositionForOffset( Pointer outAffinity, ); -@Native, Pointer)>(symbol: 'paragraph_getClosestGlyphInfoAtCoordinate') +@Native, Pointer)>( + symbol: 'paragraph_getClosestGlyphInfoAtCoordinate', +) external bool paragraphGetClosestGlyphInfoAtCoordinate( ParagraphHandle handle, - double offsetX, double offsetY, - RawRect graphemeLayoutBounds, // 4 floats, [LTRB] + double offsetX, + double offsetY, + RawRect graphemeLayoutBounds, // 4 floats, [LTRB] Pointer graphemeCodeUnitRange, // 2 `size_t`s, start and end. - Pointer booleanFlags, // 1 boolean, isLTR. + Pointer booleanFlags, // 1 boolean, isLTR. ); -@Native, Pointer)>(symbol: 'paragraph_getGlyphInfoAt') +@Native, Pointer)>( + symbol: 'paragraph_getGlyphInfoAt', +) external bool paragraphGetGlyphInfoAt( ParagraphHandle handle, int codeUnitOffset, - RawRect graphemeLayoutBounds, // 4 floats, [LTRB] + RawRect graphemeLayoutBounds, // 4 floats, [LTRB] Pointer graphemeCodeUnitRange, // 2 `size_t`s, start and end. - Pointer booleanFlags, // 1 boolean, isLTR. + Pointer booleanFlags, // 1 boolean, isLTR. ); -@Native, -)>(symbol: 'paragraph_getWordBoundary', isLeaf: true) +@Native)>( + symbol: 'paragraph_getWordBoundary', + isLeaf: true, +) external void paragraphGetWordBoundary( ParagraphHandle handle, int position, @@ -107,14 +113,11 @@ external int paragraphGetLineCount(ParagraphHandle handle); @Native(symbol: 'paragraph_getLineNumberAt', isLeaf: true) external int paragraphGetLineNumberAt(ParagraphHandle handle, int characterIndex); -@Native(symbol: 'paragraph_getLineMetricsAtIndex', isLeaf: true) -external LineMetricsHandle paragraphGetLineMetricsAtIndex( - ParagraphHandle handle, - int index, -); +@Native( + symbol: 'paragraph_getLineMetricsAtIndex', + isLeaf: true, +) +external LineMetricsHandle paragraphGetLineMetricsAtIndex(ParagraphHandle handle, int index); @Native(symbol: 'textBoxList_dispose', isLeaf: true) external void textBoxListDispose(TextBoxListHandle handle); @@ -122,24 +125,16 @@ external void textBoxListDispose(TextBoxListHandle handle); @Native(symbol: 'textBoxList_getLength', isLeaf: true) external int textBoxListGetLength(TextBoxListHandle handle); -@Native(symbol: 'textBoxList_getBoxAtIndex', isLeaf: true) -external int textBoxListGetBoxAtIndex( - TextBoxListHandle handle, - int index, - RawRect outRect, -); +@Native( + symbol: 'textBoxList_getBoxAtIndex', + isLeaf: true, +) +external int textBoxListGetBoxAtIndex(TextBoxListHandle handle, int index, RawRect outRect); -@Native(symbol: 'paragraph_getBoxesForRange', isLeaf: true) +@Native( + symbol: 'paragraph_getBoxesForRange', + isLeaf: true, +) external TextBoxListHandle paragraphGetBoxesForRange( ParagraphHandle handle, int start, @@ -149,7 +144,9 @@ external TextBoxListHandle paragraphGetBoxesForRange( ); @Native( - symbol: 'paragraph_getBoxesForPlaceholders', isLeaf: true) + symbol: 'paragraph_getBoxesForPlaceholders', + isLeaf: true, +) external TextBoxListHandle paragraphGetBoxesForPlaceholders(ParagraphHandle handle); // Returns a list of the code points that were unable to be rendered with the @@ -159,11 +156,10 @@ external TextBoxListHandle paragraphGetBoxesForPlaceholders(ParagraphHandle hand // of the code points. // Note: This must be called after the paragraph has been laid out at least // once in order to get valid data. -@Native, - Int, -)>(symbol: 'paragraph_getUnresolvedCodePoints', isLeaf: true) +@Native, Int)>( + symbol: 'paragraph_getUnresolvedCodePoints', + isLeaf: true, +) external int paragraphGetUnresolvedCodePoints( ParagraphHandle handle, Pointer outCodePoints, diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph_builder.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph_builder.dart index 451f66126fcdd..c00ee0d7a9106 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph_builder.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph_builder.dart @@ -10,12 +10,13 @@ import 'dart:ffi'; import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; final class RawParagraphBuilder extends Opaque {} + typedef ParagraphBuilderHandle = Pointer; -@Native(symbol: 'paragraphBuilder_create', isLeaf: true) +@Native( + symbol: 'paragraphBuilder_create', + isLeaf: true, +) external ParagraphBuilderHandle paragraphBuilderCreate( ParagraphStyleHandle styleHandle, FontCollectionHandle fontCollectionHandle, @@ -24,14 +25,10 @@ external ParagraphBuilderHandle paragraphBuilderCreate( @Native(symbol: 'paragraphBuilder_dispose', isLeaf: true) external void paragraphBuilderDispose(ParagraphBuilderHandle handle); -@Native(symbol: 'paragraphBuilder_addPlaceholder', isLeaf: true) +@Native( + symbol: 'paragraphBuilder_addPlaceholder', + isLeaf: true, +) external void paragraphBuilderAddPlaceholder( ParagraphBuilderHandle handle, double width, @@ -41,79 +38,88 @@ external void paragraphBuilderAddPlaceholder( int baseline, ); -@Native(symbol: 'paragraphBuilder_addText', isLeaf: true) -external void paragraphBuilderAddText( - ParagraphBuilderHandle handle, - SkString16Handle text, -); +@Native( + symbol: 'paragraphBuilder_addText', + isLeaf: true, +) +external void paragraphBuilderAddText(ParagraphBuilderHandle handle, SkString16Handle text); -@Native Function( - ParagraphBuilderHandle, - Pointer -)>(symbol: 'paragraphBuilder_getUtf8Text', isLeaf: true) +@Native Function(ParagraphBuilderHandle, Pointer)>( + symbol: 'paragraphBuilder_getUtf8Text', + isLeaf: true, +) external Pointer paragraphBuilderGetUtf8Text( ParagraphBuilderHandle handle, - Pointer outSize + Pointer outSize, ); -@Native(symbol: 'paragraphBuilder_pushStyle', isLeaf: true) -external void paragraphBuilderPushStyle( - ParagraphBuilderHandle handle, - TextStyleHandle styleHandle, -); +@Native( + symbol: 'paragraphBuilder_pushStyle', + isLeaf: true, +) +external void paragraphBuilderPushStyle(ParagraphBuilderHandle handle, TextStyleHandle styleHandle); @Native(symbol: 'paragraphBuilder_pop', isLeaf: true) external void paragraphBuilderPop(ParagraphBuilderHandle handle); -@Native(symbol: 'paragraphBuilder_build', isLeaf: true) +@Native( + symbol: 'paragraphBuilder_build', + isLeaf: true, +) external ParagraphHandle paragraphBuilderBuild(ParagraphBuilderHandle handle); @Native( - symbol: 'unicodePositionBuffer_create', isLeaf: true) + symbol: 'unicodePositionBuffer_create', + isLeaf: true, +) external UnicodePositionBufferHandle unicodePositionBufferCreate(int size); @Native Function(UnicodePositionBufferHandle)>( - symbol: 'unicodePositionBuffer_getDataPointer', isLeaf: true) + symbol: 'unicodePositionBuffer_getDataPointer', + isLeaf: true, +) external Pointer unicodePositionBufferGetDataPointer(UnicodePositionBufferHandle handle); @Native( - symbol: 'unicodePositionBuffer_free', isLeaf: true) + symbol: 'unicodePositionBuffer_free', + isLeaf: true, +) external void unicodePositionBufferFree(UnicodePositionBufferHandle handle); -@Native( - symbol: 'lineBreakBuffer_create', isLeaf: true) +@Native(symbol: 'lineBreakBuffer_create', isLeaf: true) external LineBreakBufferHandle lineBreakBufferCreate(int size); @Native Function(LineBreakBufferHandle)>( - symbol: 'lineBreakBuffer_getDataPointer', isLeaf: true) + symbol: 'lineBreakBuffer_getDataPointer', + isLeaf: true, +) external Pointer lineBreakBufferGetDataPointer(LineBreakBufferHandle handle); -@Native( - symbol: 'lineBreakBuffer_free', isLeaf: true) +@Native(symbol: 'lineBreakBuffer_free', isLeaf: true) external void lineBreakBufferFree(LineBreakBufferHandle handle); @Native( - symbol: 'paragraphBuilder_setGraphemeBreaksUtf16', isLeaf: true) + symbol: 'paragraphBuilder_setGraphemeBreaksUtf16', + isLeaf: true, +) external void paragraphBuilderSetGraphemeBreaksUtf16( ParagraphBuilderHandle handle, UnicodePositionBufferHandle positionBuffer, ); @Native( - symbol: 'paragraphBuilder_setWordBreaksUtf16', isLeaf: true) + symbol: 'paragraphBuilder_setWordBreaksUtf16', + isLeaf: true, +) external void paragraphBuilderSetWordBreaksUtf16( ParagraphBuilderHandle handle, UnicodePositionBufferHandle positionBuffer, ); @Native( - symbol: 'paragraphBuilder_setLineBreaksUtf16', isLeaf: true) + symbol: 'paragraphBuilder_setLineBreaksUtf16', + isLeaf: true, +) external void paragraphBuilderSetLineBreaksUtf16( ParagraphBuilderHandle handle, LineBreakBufferHandle positionBuffer, diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph_style.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph_style.dart index 77a7d64328638..695c6f4569ced 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph_style.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph_style.dart @@ -10,6 +10,7 @@ import 'dart:ffi'; import 'package:ui/src/engine/skwasm/skwasm_impl.dart'; final class RawParagraphStyle extends Opaque {} + typedef ParagraphStyleHandle = Pointer; @Native(symbol: 'paragraphStyle_create', isLeaf: true) @@ -18,37 +19,63 @@ external ParagraphStyleHandle paragraphStyleCreate(); @Native(symbol: 'paragraphStyle_dispose', isLeaf: true) external void paragraphStyleDispose(ParagraphStyleHandle handle); -@Native(symbol: 'paragraphStyle_setTextAlign', isLeaf: true) +@Native( + symbol: 'paragraphStyle_setTextAlign', + isLeaf: true, +) external void paragraphStyleSetTextAlign(ParagraphStyleHandle handle, int textAlign); -@Native(symbol: 'paragraphStyle_setTextDirection', isLeaf: true) +@Native( + symbol: 'paragraphStyle_setTextDirection', + isLeaf: true, +) external void paragraphStyleSetTextDirection(ParagraphStyleHandle handle, int textDirection); -@Native(symbol: 'paragraphStyle_setMaxLines', isLeaf: true) +@Native( + symbol: 'paragraphStyle_setMaxLines', + isLeaf: true, +) external void paragraphStyleSetMaxLines(ParagraphStyleHandle handle, int maxLines); -@Native(symbol: 'paragraphStyle_setHeight', isLeaf: true) +@Native( + symbol: 'paragraphStyle_setHeight', + isLeaf: true, +) external void paragraphStyleSetHeight(ParagraphStyleHandle handle, double height); -@Native(symbol: 'paragraphStyle_setTextHeightBehavior', isLeaf: true) +@Native( + symbol: 'paragraphStyle_setTextHeightBehavior', + isLeaf: true, +) external void paragraphStyleSetTextHeightBehavior( ParagraphStyleHandle handle, bool applyHeightToFirstAscent, bool applyHeightToLastDescent, ); -@Native(symbol: 'paragraphStyle_setEllipsis', isLeaf: true) +@Native( + symbol: 'paragraphStyle_setEllipsis', + isLeaf: true, +) external void paragraphStyleSetEllipsis(ParagraphStyleHandle handle, SkStringHandle ellipsis); -@Native(symbol: 'paragraphStyle_setStrutStyle', isLeaf: true) +@Native( + symbol: 'paragraphStyle_setStrutStyle', + isLeaf: true, +) external void paragraphStyleSetStrutStyle(ParagraphStyleHandle handle, StrutStyleHandle strutStyle); -@Native(symbol: 'paragraphStyle_setTextStyle', isLeaf: true) +@Native( + symbol: 'paragraphStyle_setTextStyle', + isLeaf: true, +) external void paragraphStyleSetTextStyle(ParagraphStyleHandle handle, TextStyleHandle textStyle); -@Native(symbol: 'paragraphStyle_setApplyRoundingHack', isLeaf: true) -external void paragraphStyleSetApplyRoundingHack(ParagraphStyleHandle handle, bool applyRoundingHack); +@Native( + symbol: 'paragraphStyle_setApplyRoundingHack', + isLeaf: true, +) +external void paragraphStyleSetApplyRoundingHack( + ParagraphStyleHandle handle, + bool applyRoundingHack, +); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_strut_style.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_strut_style.dart index d9f925b125444..b10816591039d 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_strut_style.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_strut_style.dart @@ -19,15 +19,14 @@ external StrutStyleHandle strutStyleCreate(); @Native(symbol: 'strutStyle_dispose', isLeaf: true) external void strutStyleDispose(StrutStyleHandle handle); -@Native families, - Int count -)>(symbol: 'strutStyle_setFontFamilies', isLeaf: true) +@Native families, Int count)>( + symbol: 'strutStyle_setFontFamilies', + isLeaf: true, +) external void strutStyleSetFontFamilies( StrutStyleHandle handle, Pointer families, - int count + int count, ); @Native(symbol: 'strutStyle_setFontSize', isLeaf: true) @@ -42,16 +41,11 @@ external void strutStyleSetHalfLeading(StrutStyleHandle handle, bool height); @Native(symbol: 'strutStyle_setLeading', isLeaf: true) external void strutStyleSetLeading(StrutStyleHandle handle, double leading); -@Native(symbol: 'strutStyle_setFontStyle', isLeaf: true) -external void strutStyleSetFontStyle( - StrutStyleHandle handle, - int weight, - int slant, -); +@Native(symbol: 'strutStyle_setFontStyle', isLeaf: true) +external void strutStyleSetFontStyle(StrutStyleHandle handle, int weight, int slant); -@Native(symbol: 'strutStyle_setForceStrutHeight', isLeaf: true) +@Native( + symbol: 'strutStyle_setForceStrutHeight', + isLeaf: true, +) external void strutStyleSetForceStrutHeight(StrutStyleHandle handle, bool forceStrutHeight); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_text_style.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_text_style.dart index 3a8aca91674ae..7474d6049a478 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_text_style.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_text_style.dart @@ -34,19 +34,14 @@ external void textStyleSetDecorationColor(TextStyleHandle handle, int decoration @Native(symbol: 'textStyle_setDecorationStyle', isLeaf: true) external void textStyleSetDecorationStyle(TextStyleHandle handle, int decorationStyle); -@Native(symbol: 'textStyle_setDecorationThickness', isLeaf: true) +@Native( + symbol: 'textStyle_setDecorationThickness', + isLeaf: true, +) external void textStyleSetDecorationThickness(TextStyleHandle handle, double decorationThickness); -@Native(symbol: 'textStyle_setFontStyle', isLeaf: true) -external void textStyleSetFontStyle( - TextStyleHandle handle, - int weight, - int slant -); +@Native(symbol: 'textStyle_setFontStyle', isLeaf: true) +external void textStyleSetFontStyle(TextStyleHandle handle, int weight, int slant); @Native(symbol: 'textStyle_setTextBaseline', isLeaf: true) external void textStyleSetTextBaseline(TextStyleHandle handle, int baseline); @@ -54,15 +49,14 @@ external void textStyleSetTextBaseline(TextStyleHandle handle, int baseline); @Native(symbol: 'textStyle_clearFontFamilies', isLeaf: true) external void textStyleClearFontFamilies(TextStyleHandle handle); -@Native, - Int count -)>(symbol: 'textStyle_addFontFamilies', isLeaf: true) +@Native, Int count)>( + symbol: 'textStyle_addFontFamilies', + isLeaf: true, +) external void textStyleAddFontFamilies( TextStyleHandle handle, Pointer families, - int count + int count, ); @Native(symbol: 'textStyle_setFontSize', isLeaf: true) @@ -83,19 +77,22 @@ external void textStyleSetHalfLeading(TextStyleHandle handle, bool halfLeading); @Native(symbol: 'textStyle_setLocale', isLeaf: true) external void textStyleSetLocale(TextStyleHandle handle, SkStringHandle locale); -@Native(symbol: 'textStyle_setBackground', isLeaf: true) +@Native( + symbol: 'textStyle_setBackground', + isLeaf: true, +) external void textStyleSetBackground(TextStyleHandle handle, PaintHandle paint); -@Native(symbol: 'textStyle_setForeground', isLeaf: true) +@Native( + symbol: 'textStyle_setForeground', + isLeaf: true, +) external void textStyleSetForeground(TextStyleHandle handle, PaintHandle paint); -@Native(symbol: 'textStyle_addShadow', isLeaf: true) +@Native( + symbol: 'textStyle_addShadow', + isLeaf: true, +) external void textStyleAddShadow( TextStyleHandle handle, int color, @@ -104,23 +101,20 @@ external void textStyleAddShadow( double blurSigma, ); -@Native(symbol: 'textStyle_addFontFeature', isLeaf: true) +@Native( + symbol: 'textStyle_addFontFeature', + isLeaf: true, +) external void textStyleAddFontFeature( TextStyleHandle handle, SkStringHandle featureName, int value, ); -@Native, - Pointer, - Int -)>(symbol: 'textStyle_setFontVariations', isLeaf: true) +@Native, Pointer, Int)>( + symbol: 'textStyle_setFontVariations', + isLeaf: true, +) external void textStyleSetFontVariations( TextStyleHandle handle, Pointer axes, diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart index 27d29b1418210..e60e9f7238580 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/renderer.dart @@ -14,7 +14,8 @@ import 'package:ui/ui_web/src/ui_web.dart' as ui_web; class SkwasmRenderer implements Renderer { late SkwasmSurface surface; - final Map _sceneViews = {}; + final Map _sceneViews = + {}; bool get isMultiThreaded => skwasmIsMultiThreaded(); @@ -45,50 +46,37 @@ class SkwasmRenderer implements Renderer { List colors, [ List? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, - Float32List? matrix]) => SkwasmGradient.conical( - focal: focal, - focalRadius: focalRadius, - center: center, - centerRadius: radius, - colors: colors, - colorStops: colorStops, - tileMode: tileMode, - matrix4: matrix, - ); + Float32List? matrix, + ]) => SkwasmGradient.conical( + focal: focal, + focalRadius: focalRadius, + center: center, + centerRadius: radius, + colors: colors, + colorStops: colorStops, + tileMode: tileMode, + matrix4: matrix, + ); @override ui.ImageFilter createBlurImageFilter({ double sigmaX = 0.0, double sigmaY = 0.0, ui.TileMode? tileMode, - }) => SkwasmImageFilter.blur( - sigmaX: sigmaX, - sigmaY: sigmaY, - tileMode: tileMode - ); + }) => SkwasmImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode); @override - ui.ImageFilter createDilateImageFilter({ - double radiusX = 0.0, - double radiusY = 0.0 - }) => SkwasmImageFilter.dilate( - radiusX: radiusX, - radiusY: radiusY, - ); + ui.ImageFilter createDilateImageFilter({double radiusX = 0.0, double radiusY = 0.0}) => + SkwasmImageFilter.dilate(radiusX: radiusX, radiusY: radiusY); @override - ui.ImageFilter createErodeImageFilter({ - double radiusX = 0.0, - double radiusY = 0.0 - }) => SkwasmImageFilter.erode( - radiusX: radiusX, - radiusY: radiusY, - ); + ui.ImageFilter createErodeImageFilter({double radiusX = 0.0, double radiusY = 0.0}) => + SkwasmImageFilter.erode(radiusX: radiusX, radiusY: radiusY); @override ui.ImageFilter composeImageFilters({ required ui.ImageFilter outer, - required ui.ImageFilter inner + required ui.ImageFilter inner, }) => SkwasmImageFilter.compose( SkwasmImageFilter.fromUiFilter(outer), SkwasmImageFilter.fromUiFilter(inner), @@ -97,11 +85,8 @@ class SkwasmRenderer implements Renderer { @override ui.ImageFilter createMatrixImageFilter( Float64List matrix4, { - ui.FilterQuality filterQuality = ui.FilterQuality.low - }) => SkwasmImageFilter.matrix( - matrix4, - filterQuality: filterQuality - ); + ui.FilterQuality filterQuality = ui.FilterQuality.low, + }) => SkwasmImageFilter.matrix(matrix4, filterQuality: filterQuality); @override ui.ImageShader createImageShader( @@ -109,14 +94,8 @@ class SkwasmRenderer implements Renderer { ui.TileMode tmx, ui.TileMode tmy, Float64List matrix4, - ui.FilterQuality? filterQuality - ) => SkwasmImageShader.imageShader( - image as SkwasmImage, - tmx, - tmy, - matrix4, - filterQuality - ); + ui.FilterQuality? filterQuality, + ) => SkwasmImageShader.imageShader(image as SkwasmImage, tmx, tmy, matrix4, filterQuality); @override ui.Gradient createLinearGradient( @@ -125,7 +104,7 @@ class SkwasmRenderer implements Renderer { List colors, [ List? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, - Float32List? matrix4 + Float32List? matrix4, ]) => SkwasmGradient.linear( from: from, to: to, @@ -135,19 +114,19 @@ class SkwasmRenderer implements Renderer { matrix4: matrix4, ); - @override ui.Paint createPaint() => SkwasmPaint(); @override ui.ParagraphBuilder createParagraphBuilder(ui.ParagraphStyle style) => - SkwasmParagraphBuilder(style as SkwasmParagraphStyle, fontCollection); + SkwasmParagraphBuilder(style as SkwasmParagraphStyle, fontCollection); @override ui.ParagraphStyle createParagraphStyle({ ui.TextAlign? textAlign, ui.TextDirection? textDirection, - int? maxLines, String? fontFamily, + int? maxLines, + String? fontFamily, double? fontSize, double? height, ui.TextHeightBehavior? textHeightBehavior, @@ -155,7 +134,7 @@ class SkwasmRenderer implements Renderer { ui.FontStyle? fontStyle, ui.StrutStyle? strutStyle, String? ellipsis, - ui.Locale? locale + ui.Locale? locale, }) => SkwasmParagraphStyle( textAlign: textAlign, textDirection: textDirection, @@ -184,14 +163,14 @@ class SkwasmRenderer implements Renderer { List colors, [ List? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, - Float32List? matrix4 + Float32List? matrix4, ]) => SkwasmGradient.radial( center: center, radius: radius, colors: colors, colorStops: colorStops, tileMode: tileMode, - matrix4: matrix4 + matrix4: matrix4, ); @override @@ -207,7 +186,7 @@ class SkwasmRenderer implements Renderer { double? leading, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, - bool? forceStrutHeight + bool? forceStrutHeight, }) => SkwasmStrutStyle( fontFamily: fontFamily, fontFamilyFallback: fontFamilyFallback, @@ -228,7 +207,7 @@ class SkwasmRenderer implements Renderer { ui.TileMode tileMode = ui.TileMode.clamp, double startAngle = 0.0, double endAngle = math.pi * 2, - Float32List? matrix4 + Float32List? matrix4, ]) => SkwasmGradient.sweep( center: center, colors: colors, @@ -236,7 +215,7 @@ class SkwasmRenderer implements Renderer { tileMode: tileMode, startAngle: startAngle, endAngle: endAngle, - matrix4: matrix4 + matrix4: matrix4, ); @override @@ -261,7 +240,7 @@ class SkwasmRenderer implements Renderer { ui.Paint? foreground, List? shadows, List? fontFeatures, - List? fontVariations + List? fontVariations, }) => SkwasmTextStyle( color: color, decoration: decoration, @@ -289,36 +268,32 @@ class SkwasmRenderer implements Renderer { @override ui.Vertices createVertices( ui.VertexMode mode, - List positions, - { - List? textureCoordinates, - List? colors, - List? indices - }) => - SkwasmVertices( - mode, - positions, - textureCoordinates: textureCoordinates, - colors: colors, - indices: indices - ); + List positions, { + List? textureCoordinates, + List? colors, + List? indices, + }) => SkwasmVertices( + mode, + positions, + textureCoordinates: textureCoordinates, + colors: colors, + indices: indices, + ); @override ui.Vertices createVerticesRaw( ui.VertexMode mode, - Float32List positions, - { - Float32List? textureCoordinates, - Int32List? colors, - Uint16List? indices - }) => - SkwasmVertices.raw( - mode, - positions, - textureCoordinates: textureCoordinates, - colors: colors, - indices: indices - ); + Float32List positions, { + Float32List? textureCoordinates, + Int32List? colors, + Uint16List? indices, + }) => SkwasmVertices.raw( + mode, + positions, + textureCoordinates: textureCoordinates, + colors: colors, + indices: indices, + ); @override void decodeImageFromPixels( @@ -330,14 +305,9 @@ class SkwasmRenderer implements Renderer { int? rowBytes, int? targetWidth, int? targetHeight, - bool allowUpscaling = true + bool allowUpscaling = true, }) { - final SkwasmImage pixelImage = SkwasmImage.fromPixels( - pixels, - width, - height, - format - ); + final SkwasmImage pixelImage = SkwasmImage.fromPixels(pixels, width, height, format); final ui.Image scaledImage = scaleImageIfNeeded( pixelImage, targetWidth: targetWidth, @@ -357,7 +327,7 @@ class SkwasmRenderer implements Renderer { Uint8List list, { int? targetWidth, int? targetHeight, - bool allowUpscaling = true + bool allowUpscaling = true, }) async { final ImageType? contentType = detectImageType(list); if (contentType == null) { @@ -376,14 +346,14 @@ class SkwasmRenderer implements Renderer { baseDecoder, targetWidth: targetWidth, targetHeight: targetHeight, - allowUpscaling: allowUpscaling + allowUpscaling: allowUpscaling, ); } @override Future instantiateImageCodecFromUrl( Uri uri, { - ui_web.ImageCodecChunkCallback? chunkCallback + ui_web.ImageCodecChunkCallback? chunkCallback, }) async { final DomResponse response = await rawHttpGet(uri.toString()); final String? contentType = response.headers.get('Content-Type'); @@ -401,7 +371,8 @@ class SkwasmRenderer implements Renderer { @override Future renderScene(ui.Scene scene, EngineFlutterView view) { - final FrameTimingRecorder? recorder = FrameTimingRecorder.frameTimingsEnabled ? FrameTimingRecorder() : null; + final FrameTimingRecorder? recorder = + FrameTimingRecorder.frameTimingsEnabled ? FrameTimingRecorder() : null; recorder?.recordBuildFinish(); final EngineSceneView sceneView = _getSceneViewForView(view); @@ -419,7 +390,8 @@ class SkwasmRenderer implements Renderer { @override String get rendererTag => 'skwasm'; - static final Map> _programs = >{}; + static final Map> _programs = + >{}; @override void clearFragmentProgramCache() { @@ -446,7 +418,7 @@ class SkwasmRenderer implements Renderer { required double width, required double left, required double baseline, - required int lineNumber + required int lineNumber, }) => SkwasmLineMetrics( hardBreak: hardBreak, ascent: ascent, @@ -456,30 +428,40 @@ class SkwasmRenderer implements Renderer { width: width, left: left, baseline: baseline, - lineNumber: lineNumber + lineNumber: lineNumber, ); @override ui.Image createImageFromImageBitmap(DomImageBitmap imageSource) { - return SkwasmImage(imageCreateFromTextureSource( - imageSource as JSObject, - imageSource.width.toDartInt, - imageSource.height.toDartInt, - surface.handle, - )); + return SkwasmImage( + imageCreateFromTextureSource( + imageSource as JSObject, + imageSource.width.toDartInt, + imageSource.height.toDartInt, + surface.handle, + ), + ); } @override - FutureOr createImageFromTextureSource(JSAny textureSource, { required int width, required int height, required bool transferOwnership }) async { + FutureOr createImageFromTextureSource( + JSAny textureSource, { + required int width, + required int height, + required bool transferOwnership, + }) async { if (!transferOwnership) { - textureSource = (await createImageBitmap(textureSource, (x: 0, y: 0, width: width, height: height))).toJSAnyShallow; + textureSource = + (await createImageBitmap(textureSource, ( + x: 0, + y: 0, + width: width, + height: height, + ))).toJSAnyShallow; } - return SkwasmImage(imageCreateFromTextureSource( - textureSource as JSObject, - width, - height, - surface.handle, - )); + return SkwasmImage( + imageCreateFromTextureSource(textureSource as JSObject, width, height, surface.handle), + ); } } @@ -490,7 +472,7 @@ class SkwasmPictureRenderer implements PictureRenderer { @override FutureOr renderPictures(List pictures) => - surface.renderPictures(pictures.cast()); + surface.renderPictures(pictures.cast()); @override ScenePicture clipPicture(ScenePicture picture, ui.Rect clip) { diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.dart index 378bc72be1043..4561c26cd0c85 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/shaders.dart @@ -21,7 +21,7 @@ class SkwasmNativeShader extends SkwasmObjectWrapper implements Skwas SkwasmNativeShader(ShaderHandle handle) : super(handle, _registry); static final SkwasmFinalizationRegistry _registry = - SkwasmFinalizationRegistry(shaderDispose); + SkwasmFinalizationRegistry(shaderDispose); } class SkwasmGradient extends SkwasmNativeShader implements ui.Gradient { @@ -38,22 +38,19 @@ class SkwasmGradient extends SkwasmNativeShader implements ui.Gradient { return true; }()); - final RawPointArray endPoints = - scope.convertPointArrayToNative([from, to]); + final RawPointArray endPoints = scope.convertPointArrayToNative([from, to]); final RawColorArray nativeColors = scope.convertColorArrayToNative(colors); - final Pointer stops = colorStops != null - ? scope.convertDoublesToNative(colorStops) - : nullptr; - final Pointer matrix = matrix4 != null - ? scope.convertMatrix4toSkMatrix(matrix4) - : nullptr; + final Pointer stops = + colorStops != null ? scope.convertDoublesToNative(colorStops) : nullptr; + final Pointer matrix = + matrix4 != null ? scope.convertMatrix4toSkMatrix(matrix4) : nullptr; final ShaderHandle handle = shaderCreateLinearGradient( endPoints, nativeColors, stops, colors.length, tileMode.index, - matrix + matrix, ); return SkwasmGradient._(handle); }); @@ -72,12 +69,10 @@ class SkwasmGradient extends SkwasmNativeShader implements ui.Gradient { }()); final RawColorArray rawColors = scope.convertColorArrayToNative(colors); - final Pointer rawStops = colorStops != null - ? scope.convertDoublesToNative(colorStops) - : nullptr; - final Pointer matrix = matrix4 != null - ? scope.convertMatrix4toSkMatrix(matrix4) - : nullptr; + final Pointer rawStops = + colorStops != null ? scope.convertDoublesToNative(colorStops) : nullptr; + final Pointer matrix = + matrix4 != null ? scope.convertMatrix4toSkMatrix(matrix4) : nullptr; final ShaderHandle handle = shaderCreateRadialGradient( center.dx, center.dy, @@ -106,15 +101,12 @@ class SkwasmGradient extends SkwasmNativeShader implements ui.Gradient { return true; }()); - final RawPointArray endPoints = - scope.convertPointArrayToNative([focal, center]); + final RawPointArray endPoints = scope.convertPointArrayToNative([focal, center]); final RawColorArray rawColors = scope.convertColorArrayToNative(colors); - final Pointer rawStops = colorStops != null - ? scope.convertDoublesToNative(colorStops) - : nullptr; - final Pointer matrix = matrix4 != null - ? scope.convertMatrix4toSkMatrix(matrix4) - : nullptr; + final Pointer rawStops = + colorStops != null ? scope.convertDoublesToNative(colorStops) : nullptr; + final Pointer matrix = + matrix4 != null ? scope.convertMatrix4toSkMatrix(matrix4) : nullptr; final ShaderHandle handle = shaderCreateConicalGradient( endPoints, focalRadius, @@ -123,7 +115,7 @@ class SkwasmGradient extends SkwasmNativeShader implements ui.Gradient { rawStops, colors.length, tileMode.index, - matrix + matrix, ); return SkwasmGradient._(handle); }); @@ -143,12 +135,10 @@ class SkwasmGradient extends SkwasmNativeShader implements ui.Gradient { }()); final RawColorArray rawColors = scope.convertColorArrayToNative(colors); - final Pointer rawStops = colorStops != null - ? scope.convertDoublesToNative(colorStops) - : nullptr; - final Pointer matrix = matrix4 != null - ? scope.convertMatrix4toSkMatrix(matrix4) - : nullptr; + final Pointer rawStops = + colorStops != null ? scope.convertDoublesToNative(colorStops) : nullptr; + final Pointer matrix = + matrix4 != null ? scope.convertMatrix4toSkMatrix(matrix4) : nullptr; final ShaderHandle handle = shaderCreateSweepGradient( center.dx, center.dy, @@ -158,7 +148,7 @@ class SkwasmGradient extends SkwasmNativeShader implements ui.Gradient { tileMode.index, ui.toDegrees(startAngle), ui.toDegrees(endAngle), - matrix + matrix, ); return SkwasmGradient._(handle); }); @@ -182,27 +172,32 @@ class SkwasmImageShader extends SkwasmNativeShader implements ui.ImageShader { if (matrix4 != null) { return withStackScope((StackScope scope) { final RawMatrix33 localMatrix = scope.convertMatrix4toSkMatrix(matrix4); - return SkwasmImageShader._(shaderCreateFromImage( + return SkwasmImageShader._( + shaderCreateFromImage( + image.handle, + tmx.index, + tmy.index, + (filterQuality ?? ui.FilterQuality.none).index, + localMatrix, + ), + ); + }); + } else { + return SkwasmImageShader._( + shaderCreateFromImage( image.handle, tmx.index, tmy.index, (filterQuality ?? ui.FilterQuality.none).index, - localMatrix, - )); - }); - } else { - return SkwasmImageShader._(shaderCreateFromImage( - image.handle, - tmx.index, - tmy.index, - (filterQuality ?? ui.FilterQuality.none).index, - nullptr, - )); + nullptr, + ), + ); } } } -class SkwasmFragmentProgram extends SkwasmObjectWrapper implements ui.FragmentProgram { +class SkwasmFragmentProgram extends SkwasmObjectWrapper + implements ui.FragmentProgram { SkwasmFragmentProgram._( this.name, RuntimeEffectHandle handle, @@ -224,16 +219,11 @@ class SkwasmFragmentProgram extends SkwasmObjectWrapper implem } final RuntimeEffectHandle handle = runtimeEffectCreate(sourceString); skStringFree(sourceString); - return SkwasmFragmentProgram._( - name, - handle, - shaderData.floatCount, - shaderData.textureCount - ); + return SkwasmFragmentProgram._(name, handle, shaderData.floatCount, shaderData.textureCount); } static final SkwasmFinalizationRegistry _registry = - SkwasmFinalizationRegistry(runtimeEffectDispose); + SkwasmFinalizationRegistry(runtimeEffectDispose); final String name; final int floatUniformCount; @@ -249,7 +239,7 @@ class SkwasmShaderData extends SkwasmObjectWrapper { SkwasmShaderData(int size) : super(skDataCreate(size), _registry); static final SkwasmFinalizationRegistry _registry = - SkwasmFinalizationRegistry(skDataDispose); + SkwasmFinalizationRegistry(skDataDispose); } // This class does not inherit from SkwasmNativeShader, as its handle might @@ -258,11 +248,11 @@ class SkwasmShaderData extends SkwasmObjectWrapper { // implement SkwasmShader though, in order to provide the handle for the // underlying shader object. class SkwasmFragmentShader implements SkwasmShader, ui.FragmentShader { - SkwasmFragmentShader(SkwasmFragmentProgram program) : - _program = program, - _uniformData = SkwasmShaderData(program.uniformSize), - _floatUniformCount = program.floatUniformCount, - _childShaders = List.filled(program.childShaderCount, null); + SkwasmFragmentShader(SkwasmFragmentProgram program) + : _program = program, + _uniformData = SkwasmShaderData(program.uniformSize), + _floatUniformCount = program.floatUniformCount, + _childShaders = List.filled(program.childShaderCount, null); @override ShaderHandle get handle { @@ -270,8 +260,7 @@ class SkwasmFragmentShader implements SkwasmShader, ui.FragmentShader { final ShaderHandle newHandle = withStackScope((StackScope s) { Pointer childShaders = nullptr; if (_childShaders.isNotEmpty) { - childShaders = s.allocPointerArray(_childShaders.length) - .cast(); + childShaders = s.allocPointerArray(_childShaders.length).cast(); for (int i = 0; i < _childShaders.length; i++) { final SkwasmShader? child = _childShaders[i]; childShaders[i] = child != null ? child.handle : nullptr; diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/surface.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/surface.dart index eddcc19ff402b..0d873097b878f 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/surface.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/surface.dart @@ -38,10 +38,13 @@ class SkwasmCallbackHandler { SkwasmCallbackHandler._withCallbackPointer(this.callbackPointer); factory SkwasmCallbackHandler._() { - final WasmFuncRef wasmFunction = WasmFunction.fromFunction(callbackHandler); + final WasmFuncRef wasmFunction = + WasmFunction.fromFunction( + callbackHandler, + ); final int functionIndex = addFunction(wasmFunction).toIntUnsigned(); return SkwasmCallbackHandler._withCallbackPointer( - OnRenderCallbackHandle.fromAddress(functionIndex) + OnRenderCallbackHandle.fromAddress(functionIndex), ); } static SkwasmCallbackHandler instance = SkwasmCallbackHandler._(); @@ -90,29 +93,27 @@ class SkwasmSurface { } Future renderPictures(List pictures) => - withStackScope((StackScope scope) async { - final Pointer pictureHandles = - scope.allocPointerArray(pictures.length).cast(); - for (int i = 0; i < pictures.length; i++) { - pictureHandles[i] = pictures[i].handle; - } - final int callbackId = surfaceRenderPictures(handle, pictureHandles, pictures.length); - final RasterResult rasterResult = (await SkwasmCallbackHandler.instance.registerCallback(callbackId)) as RasterResult; - final RenderResult result = ( - imageBitmaps: rasterResult.imageBitmaps.toDart.cast(), - rasterStartMicros: (rasterResult.rasterStartMilliseconds.toDartDouble * 1000).toInt(), - rasterEndMicros: (rasterResult.rasterEndMilliseconds.toDartDouble * 1000).toInt(), - ); - return result; - }); + withStackScope((StackScope scope) async { + final Pointer pictureHandles = + scope.allocPointerArray(pictures.length).cast(); + for (int i = 0; i < pictures.length; i++) { + pictureHandles[i] = pictures[i].handle; + } + final int callbackId = surfaceRenderPictures(handle, pictureHandles, pictures.length); + final RasterResult rasterResult = + (await SkwasmCallbackHandler.instance.registerCallback(callbackId)) as RasterResult; + final RenderResult result = ( + imageBitmaps: rasterResult.imageBitmaps.toDart.cast(), + rasterStartMicros: (rasterResult.rasterStartMilliseconds.toDartDouble * 1000).toInt(), + rasterEndMicros: (rasterResult.rasterEndMilliseconds.toDartDouble * 1000).toInt(), + ); + return result; + }); Future rasterizeImage(SkwasmImage image, ui.ImageByteFormat format) async { - final int callbackId = surfaceRasterizeImage( - handle, - image.handle, - format.index, - ); - final int context = (await SkwasmCallbackHandler.instance.registerCallback(callbackId) as JSNumber).toDartInt; + final int callbackId = surfaceRasterizeImage(handle, image.handle, format.index); + final int context = + (await SkwasmCallbackHandler.instance.registerCallback(callbackId) as JSNumber).toDartInt; final SkDataHandle dataHandle = SkDataHandle.fromAddress(context); final int byteCount = skDataGetSize(dataHandle); final Pointer dataPointer = skDataGetConstPointer(dataHandle).cast(); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/vertices.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/vertices.dart index e4d5142c0e767..80bf64eea1d30 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/vertices.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/vertices.dart @@ -19,25 +19,24 @@ class SkwasmVertices extends SkwasmObjectWrapper implements ui.Vert List? indices, }) => withStackScope((StackScope scope) { final RawPointArray rawPositions = scope.convertPointArrayToNative(positions); - final RawPointArray rawTextureCoordinates = textureCoordinates != null - ? scope.convertPointArrayToNative(textureCoordinates) - : nullptr; - final RawColorArray rawColors = colors != null - ? scope.convertColorArrayToNative(colors) - : nullptr; - final Pointer rawIndices = indices != null - ? scope.convertIntsToUint16Native(indices) - : nullptr; + final RawPointArray rawTextureCoordinates = + textureCoordinates != null ? scope.convertPointArrayToNative(textureCoordinates) : nullptr; + final RawColorArray rawColors = + colors != null ? scope.convertColorArrayToNative(colors) : nullptr; + final Pointer rawIndices = + indices != null ? scope.convertIntsToUint16Native(indices) : nullptr; final int indexCount = indices != null ? indices.length : 0; - return SkwasmVertices._(verticesCreate( - mode.index, - positions.length, - rawPositions, - rawTextureCoordinates, - rawColors, - indexCount, - rawIndices, - )); + return SkwasmVertices._( + verticesCreate( + mode.index, + positions.length, + rawPositions, + rawTextureCoordinates, + rawColors, + indexCount, + rawIndices, + ), + ); }); factory SkwasmVertices.raw( @@ -48,29 +47,28 @@ class SkwasmVertices extends SkwasmObjectWrapper implements ui.Vert Uint16List? indices, }) => withStackScope((StackScope scope) { final RawPointArray rawPositions = scope.convertDoublesToNative(positions); - final RawPointArray rawTextureCoordinates = textureCoordinates != null - ? scope.convertDoublesToNative(textureCoordinates) - : nullptr; - final RawColorArray rawColors = colors != null - ? scope.convertIntsToUint32Native(colors) - : nullptr; - final Pointer rawIndices = indices != null - ? scope.convertIntsToUint16Native(indices) - : nullptr; + final RawPointArray rawTextureCoordinates = + textureCoordinates != null ? scope.convertDoublesToNative(textureCoordinates) : nullptr; + final RawColorArray rawColors = + colors != null ? scope.convertIntsToUint32Native(colors) : nullptr; + final Pointer rawIndices = + indices != null ? scope.convertIntsToUint16Native(indices) : nullptr; final int indexCount = indices != null ? indices.length : 0; - return SkwasmVertices._(verticesCreate( - mode.index, - positions.length ~/ 2, - rawPositions, - rawTextureCoordinates, - rawColors, - indexCount, - rawIndices, - )); + return SkwasmVertices._( + verticesCreate( + mode.index, + positions.length ~/ 2, + rawPositions, + rawTextureCoordinates, + rawColors, + indexCount, + rawIndices, + ), + ); }); SkwasmVertices._(VerticesHandle handle) : super(handle, _registry); static final SkwasmFinalizationRegistry _registry = - SkwasmFinalizationRegistry(verticesDispose); + SkwasmFinalizationRegistry(verticesDispose); } diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart index 5e48b42f5f325..14e3c2743c139 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_stub/renderer.dart @@ -20,7 +20,10 @@ class SkwasmRenderer implements Renderer { } @override - ui.ImageFilter composeImageFilters({required ui.ImageFilter outer, required ui.ImageFilter inner}) { + ui.ImageFilter composeImageFilters({ + required ui.ImageFilter outer, + required ui.ImageFilter inner, + }) { throw UnimplementedError('Skwasm not implemented on this platform.'); } @@ -30,7 +33,11 @@ class SkwasmRenderer implements Renderer { } @override - ui.ImageFilter createBlurImageFilter({double sigmaX = 0.0, double sigmaY = 0.0, ui.TileMode? tileMode}) { + ui.ImageFilter createBlurImageFilter({ + double sigmaX = 0.0, + double sigmaY = 0.0, + ui.TileMode? tileMode, + }) { throw UnimplementedError('Skwasm not implemented on this platform.'); } @@ -40,7 +47,16 @@ class SkwasmRenderer implements Renderer { } @override - ui.Gradient createConicalGradient(ui.Offset focal, double focalRadius, ui.Offset center, double radius, List colors, [List? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, Float32List? matrix]) { + ui.Gradient createConicalGradient( + ui.Offset focal, + double focalRadius, + ui.Offset center, + double radius, + List colors, [ + List? colorStops, + ui.TileMode tileMode = ui.TileMode.clamp, + Float32List? matrix, + ]) { throw UnimplementedError('Skwasm not implemented on this platform.'); } @@ -55,17 +71,33 @@ class SkwasmRenderer implements Renderer { } @override - ui.ImageShader createImageShader(ui.Image image, ui.TileMode tmx, ui.TileMode tmy, Float64List matrix4, ui.FilterQuality? filterQuality) { + ui.ImageShader createImageShader( + ui.Image image, + ui.TileMode tmx, + ui.TileMode tmy, + Float64List matrix4, + ui.FilterQuality? filterQuality, + ) { throw UnimplementedError('Skwasm not implemented on this platform.'); } @override - ui.Gradient createLinearGradient(ui.Offset from, ui.Offset to, List colors, [List? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, Float32List? matrix4]) { + ui.Gradient createLinearGradient( + ui.Offset from, + ui.Offset to, + List colors, [ + List? colorStops, + ui.TileMode tileMode = ui.TileMode.clamp, + Float32List? matrix4, + ]) { throw UnimplementedError('Skwasm not implemented on this platform.'); } @override - ui.ImageFilter createMatrixImageFilter(Float64List matrix4, {ui.FilterQuality filterQuality = ui.FilterQuality.low}) { + ui.ImageFilter createMatrixImageFilter( + Float64List matrix4, { + ui.FilterQuality filterQuality = ui.FilterQuality.low, + }) { throw UnimplementedError('Skwasm not implemented on this platform.'); } @@ -80,7 +112,20 @@ class SkwasmRenderer implements Renderer { } @override - ui.ParagraphStyle createParagraphStyle({ui.TextAlign? textAlign, ui.TextDirection? textDirection, int? maxLines, String? fontFamily, double? fontSize, double? height, ui.TextHeightBehavior? textHeightBehavior, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, ui.StrutStyle? strutStyle, String? ellipsis, ui.Locale? locale}) { + ui.ParagraphStyle createParagraphStyle({ + ui.TextAlign? textAlign, + ui.TextDirection? textDirection, + int? maxLines, + String? fontFamily, + double? fontSize, + double? height, + ui.TextHeightBehavior? textHeightBehavior, + ui.FontWeight? fontWeight, + ui.FontStyle? fontStyle, + ui.StrutStyle? strutStyle, + String? ellipsis, + ui.Locale? locale, + }) { throw UnimplementedError('Skwasm not implemented on this platform.'); } @@ -95,7 +140,14 @@ class SkwasmRenderer implements Renderer { } @override - ui.Gradient createRadialGradient(ui.Offset center, double radius, List colors, [List? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, Float32List? matrix4]) { + ui.Gradient createRadialGradient( + ui.Offset center, + double radius, + List colors, [ + List? colorStops, + ui.TileMode tileMode = ui.TileMode.clamp, + Float32List? matrix4, + ]) { throw UnimplementedError('Skwasm not implemented on this platform.'); } @@ -105,37 +157,100 @@ class SkwasmRenderer implements Renderer { } @override - ui.StrutStyle createStrutStyle({String? fontFamily, List? fontFamilyFallback, double? fontSize, double? height, ui.TextLeadingDistribution? leadingDistribution, double? leading, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, bool? forceStrutHeight}) { + ui.StrutStyle createStrutStyle({ + String? fontFamily, + List? fontFamilyFallback, + double? fontSize, + double? height, + ui.TextLeadingDistribution? leadingDistribution, + double? leading, + ui.FontWeight? fontWeight, + ui.FontStyle? fontStyle, + bool? forceStrutHeight, + }) { throw UnimplementedError('Skwasm not implemented on this platform.'); } @override - ui.Gradient createSweepGradient(ui.Offset center, List colors, [List? colorStops, ui.TileMode tileMode = ui.TileMode.clamp, double startAngle = 0.0, double endAngle = math.pi * 2, Float32List? matrix4]) { + ui.Gradient createSweepGradient( + ui.Offset center, + List colors, [ + List? colorStops, + ui.TileMode tileMode = ui.TileMode.clamp, + double startAngle = 0.0, + double endAngle = math.pi * 2, + Float32List? matrix4, + ]) { throw UnimplementedError('Skwasm not implemented on this platform.'); } @override - ui.TextStyle createTextStyle({ui.Color? color, ui.TextDecoration? decoration, ui.Color? decorationColor, ui.TextDecorationStyle? decorationStyle, double? decorationThickness, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, ui.TextBaseline? textBaseline, String? fontFamily, List? fontFamilyFallback, double? fontSize, double? letterSpacing, double? wordSpacing, double? height, ui.TextLeadingDistribution? leadingDistribution, ui.Locale? locale, ui.Paint? background, ui.Paint? foreground, List? shadows, List? fontFeatures, List? fontVariations}) { + ui.TextStyle createTextStyle({ + ui.Color? color, + ui.TextDecoration? decoration, + ui.Color? decorationColor, + ui.TextDecorationStyle? decorationStyle, + double? decorationThickness, + ui.FontWeight? fontWeight, + ui.FontStyle? fontStyle, + ui.TextBaseline? textBaseline, + String? fontFamily, + List? fontFamilyFallback, + double? fontSize, + double? letterSpacing, + double? wordSpacing, + double? height, + ui.TextLeadingDistribution? leadingDistribution, + ui.Locale? locale, + ui.Paint? background, + ui.Paint? foreground, + List? shadows, + List? fontFeatures, + List? fontVariations, + }) { throw UnimplementedError('Skwasm not implemented on this platform.'); } @override - ui.Vertices createVertices(ui.VertexMode mode, List positions, {List? textureCoordinates, List? colors, List? indices}) { + ui.Vertices createVertices( + ui.VertexMode mode, + List positions, { + List? textureCoordinates, + List? colors, + List? indices, + }) { throw UnimplementedError('Skwasm not implemented on this platform.'); } @override - ui.Vertices createVerticesRaw(ui.VertexMode mode, Float32List positions, {Float32List? textureCoordinates, Int32List? colors, Uint16List? indices}) { + ui.Vertices createVerticesRaw( + ui.VertexMode mode, + Float32List positions, { + Float32List? textureCoordinates, + Int32List? colors, + Uint16List? indices, + }) { throw UnimplementedError('Skwasm not implemented on this platform.'); } @override - void decodeImageFromPixels(Uint8List pixels, int width, int height, ui.PixelFormat format, ui.ImageDecoderCallback callback, {int? rowBytes, int? targetWidth, int? targetHeight, bool allowUpscaling = true}) { + void decodeImageFromPixels( + Uint8List pixels, + int width, + int height, + ui.PixelFormat format, + ui.ImageDecoderCallback callback, { + int? rowBytes, + int? targetWidth, + int? targetHeight, + bool allowUpscaling = true, + }) { throw UnimplementedError('Skwasm not implemented on this platform.'); } @override - FlutterFontCollection get fontCollection => throw UnimplementedError('Skwasm not implemented on this platform.'); + FlutterFontCollection get fontCollection => + throw UnimplementedError('Skwasm not implemented on this platform.'); @override FutureOr initialize() { @@ -143,12 +258,20 @@ class SkwasmRenderer implements Renderer { } @override - Future instantiateImageCodec(Uint8List list, {int? targetWidth, int? targetHeight, bool allowUpscaling = true}) { + Future instantiateImageCodec( + Uint8List list, { + int? targetWidth, + int? targetHeight, + bool allowUpscaling = true, + }) { throw UnimplementedError('Skwasm not implemented on this platform.'); } @override - Future instantiateImageCodecFromUrl(Uri uri, {ui_web.ImageCodecChunkCallback? chunkCallback}) { + Future instantiateImageCodecFromUrl( + Uri uri, { + ui_web.ImageCodecChunkCallback? chunkCallback, + }) { throw UnimplementedError('Skwasm not implemented on this platform.'); } @@ -163,7 +286,8 @@ class SkwasmRenderer implements Renderer { @override void clearFragmentProgramCache() => _programs.clear(); - static final Map> _programs = >{}; + static final Map> _programs = + >{}; @override Future createFragmentProgram(String assetKey) { @@ -185,7 +309,7 @@ class SkwasmRenderer implements Renderer { required double width, required double left, required double baseline, - required int lineNumber + required int lineNumber, }) => throw UnimplementedError('Skwasm not implemented on this platform.'); @override @@ -194,7 +318,12 @@ class SkwasmRenderer implements Renderer { } @override - ui.Image createImageFromTextureSource(JSAny object, { required int width, required int height, required bool transferOwnership }) { + ui.Image createImageFromTextureSource( + JSAny object, { + required int width, + required int height, + required bool transferOwnership, + }) { throw Exception('Skwasm not implemented on this platform.'); } } diff --git a/lib/web_ui/lib/src/engine/svg.dart b/lib/web_ui/lib/src/engine/svg.dart index db6784a14c9a8..718a5c454be65 100644 --- a/lib/web_ui/lib/src/engine/svg.dart +++ b/lib/web_ui/lib/src/engine/svg.dart @@ -11,8 +11,7 @@ import 'dom.dart'; class SVGElement extends DomElement {} SVGElement createSVGElement(String tag) => - domDocument.createElementNS('http://www.w3.org/2000/svg', tag) - as SVGElement; + domDocument.createElementNS('http://www.w3.org/2000/svg', tag) as SVGElement; @JS() @staticInterop @@ -39,16 +38,14 @@ extension SVGSVGElementExtension on SVGSVGElement { class SVGClipPathElement extends SVGGraphicsElement {} SVGClipPathElement createSVGClipPathElement() => - domDocument.createElementNS('http://www.w3.org/2000/svg', 'clipPath') - as SVGClipPathElement; + domDocument.createElementNS('http://www.w3.org/2000/svg', 'clipPath') as SVGClipPathElement; @JS() @staticInterop class SVGDefsElement extends SVGGraphicsElement {} SVGDefsElement createSVGDefsElement() => - domDocument.createElementNS('http://www.w3.org/2000/svg', 'defs') - as SVGDefsElement; + domDocument.createElementNS('http://www.w3.org/2000/svg', 'defs') as SVGDefsElement; @JS() @staticInterop @@ -59,8 +56,7 @@ class SVGGeometryElement extends SVGGraphicsElement {} class SVGPathElement extends SVGGeometryElement {} SVGPathElement createSVGPathElement() => - domDocument.createElementNS('http://www.w3.org/2000/svg', 'path') - as SVGPathElement; + domDocument.createElementNS('http://www.w3.org/2000/svg', 'path') as SVGPathElement; @JS() @staticInterop @@ -75,8 +71,7 @@ extension SVGFilterElementExtension on SVGFilterElement { } SVGFilterElement createSVGFilterElement() => - domDocument.createElementNS('http://www.w3.org/2000/svg', 'filter') - as SVGFilterElement; + domDocument.createElementNS('http://www.w3.org/2000/svg', 'filter') as SVGFilterElement; @JS() @staticInterop @@ -136,8 +131,7 @@ extension SVGFEFloodElementExtension on SVGFEFloodElement { } SVGFEFloodElement createSVGFEFloodElement() => - domDocument.createElementNS('http://www.w3.org/2000/svg', 'feFlood') - as SVGFEFloodElement; + domDocument.createElementNS('http://www.w3.org/2000/svg', 'feFlood') as SVGFEFloodElement; @JS() @staticInterop @@ -150,8 +144,7 @@ extension SVGFEBlendElementExtension on SVGFEBlendElement { } SVGFEBlendElement createSVGFEBlendElement() => - domDocument.createElementNS('http://www.w3.org/2000/svg', 'feBlend') - as SVGFEBlendElement; + domDocument.createElementNS('http://www.w3.org/2000/svg', 'feBlend') as SVGFEBlendElement; @JS() @staticInterop @@ -167,8 +160,7 @@ extension SVGFEImageElementExtension on SVGFEImageElement { } SVGFEImageElement createSVGFEImageElement() => - domDocument.createElementNS('http://www.w3.org/2000/svg', 'feImage') - as SVGFEImageElement; + domDocument.createElementNS('http://www.w3.org/2000/svg', 'feImage') as SVGFEImageElement; @JS() @staticInterop diff --git a/lib/web_ui/lib/src/engine/test_embedding.dart b/lib/web_ui/lib/src/engine/test_embedding.dart index ba5647c8df475..b33cc96cac3bd 100644 --- a/lib/web_ui/lib/src/engine/test_embedding.dart +++ b/lib/web_ui/lib/src/engine/test_embedding.dart @@ -38,8 +38,8 @@ class TestUrlStrategy implements ui_web.UrlStrategy { /// Creates an instance of [TestUrlStrategy] and populates it with a list /// that has [initialEntry] as the only item. TestUrlStrategy.fromEntry(TestHistoryEntry initialEntry) - : _currentEntryIndex = 0, - history = [initialEntry]; + : _currentEntryIndex = 0, + history = [initialEntry]; @override String getPath() => currentEntry.url; diff --git a/lib/web_ui/lib/src/engine/text/canvas_paragraph.dart b/lib/web_ui/lib/src/engine/text/canvas_paragraph.dart index 2d437364baeb5..04aa59b20407f 100644 --- a/lib/web_ui/lib/src/engine/text/canvas_paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/canvas_paragraph.dart @@ -98,8 +98,7 @@ class CanvasParagraph implements ui.Paragraph { _layoutService.performLayout(constraints); if (Profiler.isBenchmarkMode) { stopwatch.stop(); - Profiler.instance - .benchmark('text_layout', stopwatch.elapsedMicroseconds.toDouble()); + Profiler.instance.benchmark('text_layout', stopwatch.elapsedMicroseconds.toDouble()); } isLaidOut = true; @@ -195,7 +194,8 @@ class CanvasParagraph implements ui.Paragraph { } @override - ui.GlyphInfo? getClosestGlyphInfoForOffset(ui.Offset offset) => _layoutService.getClosestGlyphInfo(offset); + ui.GlyphInfo? getClosestGlyphInfoForOffset(ui.Offset offset) => + _layoutService.getClosestGlyphInfo(offset); @override ui.GlyphInfo? getGlyphInfoAt(int codeUnitOffset) { @@ -255,9 +255,7 @@ class CanvasParagraph implements ui.Paragraph { @override EngineLineMetrics? getLineMetricsAt(int lineNumber) { - return 0 <= lineNumber && lineNumber < lines.length - ? lines[lineNumber].lineMetrics - : null; + return 0 <= lineNumber && lineNumber < lines.length ? lines[lineNumber].lineMetrics : null; } @override @@ -268,9 +266,10 @@ class CanvasParagraph implements ui.Paragraph { int? _findLine(int codeUnitOffset, int startLine, int endLine) { assert(endLine <= lines.length); - final bool isOutOfBounds = endLine <= startLine - || codeUnitOffset < lines[startLine].startIndex - || (endLine < numberOfLines && lines[endLine].startIndex <= codeUnitOffset); + final bool isOutOfBounds = + endLine <= startLine || + codeUnitOffset < lines[startLine].startIndex || + (endLine < numberOfLines && lines[endLine].startIndex <= codeUnitOffset); if (isOutOfBounds) { return null; } @@ -283,7 +282,8 @@ class CanvasParagraph implements ui.Paragraph { // endLine >= startLine + 2 thus we have // startLine + 1 <= midIndex <= endLine - 1 final int midIndex = (startLine + endLine) ~/ 2; - return _findLine(codeUnitOffset, midIndex, endLine) ?? _findLine(codeUnitOffset, startLine, midIndex); + return _findLine(codeUnitOffset, midIndex, endLine) ?? + _findLine(codeUnitOffset, startLine, midIndex); } bool _disposed = false; @@ -334,11 +334,7 @@ void _positionSpanElement(DomElement element, ParagraphLine line, LayoutFragment class ParagraphSpan { /// Creates a [ParagraphSpan] with the given [style], representing the span of /// text in the range between [start] and [end]. - ParagraphSpan({ - required this.style, - required this.start, - required this.end, - }); + ParagraphSpan({required this.style, required this.start, required this.end}); /// The resolved style of the span. final EngineTextStyle style; @@ -360,13 +356,7 @@ class PlaceholderSpan extends ParagraphPlaceholder implements ParagraphSpan { ui.PlaceholderAlignment alignment, { required double baselineOffset, required ui.TextBaseline baseline, - }) : super( - width, - height, - alignment, - baselineOffset: baselineOffset, - baseline: baseline, - ); + }) : super(width, height, alignment, baselineOffset: baselineOffset, baseline: baseline); @override final EngineTextStyle style; @@ -511,7 +501,8 @@ class ChildStyleNode extends StyleNode { } @override - ui.TextLeadingDistribution? get _leadingDistribution => style.leadingDistribution ?? parent._leadingDistribution; + ui.TextLeadingDistribution? get _leadingDistribution => + style.leadingDistribution ?? parent._leadingDistribution; @override ui.Locale? get _locale => style.locale ?? parent._locale; @@ -612,8 +603,8 @@ class CanvasParagraphBuilder implements ui.ParagraphBuilder { /// Creates a [CanvasParagraphBuilder] object, which is used to create a /// [CanvasParagraph]. CanvasParagraphBuilder(EngineParagraphStyle style) - : _paragraphStyle = style, - _rootStyleNode = RootStyleNode(style); + : _paragraphStyle = style, + _rootStyleNode = RootStyleNode(style); final StringBuffer _plainTextBuffer = StringBuffer(); final EngineParagraphStyle _paragraphStyle; @@ -622,9 +613,8 @@ class CanvasParagraphBuilder implements ui.ParagraphBuilder { final List _styleStack = []; final RootStyleNode _rootStyleNode; - StyleNode get _currentStyleNode => _styleStack.isEmpty - ? _rootStyleNode - : _styleStack[_styleStack.length - 1]; + StyleNode get _currentStyleNode => + _styleStack.isEmpty ? _rootStyleNode : _styleStack[_styleStack.length - 1]; @override int get placeholderCount => _placeholderCount; @@ -644,9 +634,12 @@ class CanvasParagraphBuilder implements ui.ParagraphBuilder { ui.TextBaseline? baseline, }) { // Require a baseline to be specified if using a baseline-based alignment. - assert(!(alignment == ui.PlaceholderAlignment.aboveBaseline || - alignment == ui.PlaceholderAlignment.belowBaseline || - alignment == ui.PlaceholderAlignment.baseline) || baseline != null); + assert( + !(alignment == ui.PlaceholderAlignment.aboveBaseline || + alignment == ui.PlaceholderAlignment.belowBaseline || + alignment == ui.PlaceholderAlignment.baseline) || + baseline != null, + ); final int start = _plainTextBuffer.length; _plainTextBuffer.write(placeholderChar); @@ -657,16 +650,18 @@ class CanvasParagraphBuilder implements ui.ParagraphBuilder { _placeholderCount++; _placeholderScales.add(scale); - _spans.add(PlaceholderSpan( - style, - start, - end, - width * scale, - height * scale, - alignment, - baselineOffset: (baselineOffset ?? height) * scale, - baseline: baseline ?? ui.TextBaseline.alphabetic, - )); + _spans.add( + PlaceholderSpan( + style, + start, + end, + width * scale, + height * scale, + alignment, + baselineOffset: (baselineOffset ?? height) * scale, + baseline: baseline ?? ui.TextBaseline.alphabetic, + ), + ); } @override @@ -732,9 +727,7 @@ class CanvasParagraphBuilder implements ui.ParagraphBuilder { // // We want the paragraph to always have a non-empty list of spans to match // the expectations of the [LayoutFragmenter]. - _spans.add( - ParagraphSpan(style: _rootStyleNode.resolveStyle(), start: 0, end: 0), - ); + _spans.add(ParagraphSpan(style: _rootStyleNode.resolveStyle(), start: 0, end: 0)); } return CanvasParagraph( diff --git a/lib/web_ui/lib/src/engine/text/font_collection.dart b/lib/web_ui/lib/src/engine/text/font_collection.dart index fca0270597468..b258ba4ddd4ce 100644 --- a/lib/web_ui/lib/src/engine/text/font_collection.dart +++ b/lib/web_ui/lib/src/engine/text/font_collection.dart @@ -19,13 +19,14 @@ class HtmlFontCollection implements FlutterFontCollection { /// fonts declared within. @override Future loadAssetFonts(FontManifest manifest) async { - final List> pendingFonts = >[]; + final List> pendingFonts = + >[]; for (final FontFamily family in manifest.families) { for (final FontAsset fontAsset in family.fontAssets) { pendingFonts.add(() async { return ( fontAsset.asset, - await _loadFontAsset(family.name, fontAsset.asset, fontAsset.descriptors) + await _loadFontAsset(family.name, fontAsset.asset, fontAsset.descriptors), ); }()); } @@ -64,8 +65,7 @@ class HtmlFontCollection implements FlutterFontCollection { // Regular expression to detect a string with no punctuations. // For example font family 'Ahem!' does not fall into this category // so the family name will be wrapped in quotes. - static final RegExp notPunctuation = - RegExp(r'[a-z0-9\s]+', caseSensitive: false); + static final RegExp notPunctuation = RegExp(r'[a-z0-9\s]+', caseSensitive: false); // Regular expression to detect tokens starting with a digit. // For example font family 'Goudy Bookletter 1911' falls into this // category. @@ -110,8 +110,7 @@ class HtmlFontCollection implements FlutterFontCollection { final List fontFaces = []; final List errors = []; try { - if (startWithDigit.hasMatch(family) || - notPunctuation.stringMatch(family) != family) { + if (startWithDigit.hasMatch(family) || notPunctuation.stringMatch(family) != family) { // Load a font family name with special characters once here wrapped in // quotes. fontFaces.add(await _loadFontFace("'$family'", asset, descriptors)); @@ -120,7 +119,7 @@ class HtmlFontCollection implements FlutterFontCollection { errors.add(error); } try { - // Load all fonts, without quoted family names. + // Load all fonts, without quoted family names. fontFaces.add(await _loadFontFace(family, asset, descriptors)); } on FontLoadError catch (error) { errors.add(error); @@ -150,7 +149,11 @@ class HtmlFontCollection implements FlutterFontCollection { ) async { // try/catch because `new FontFace` can crash with an improper font family. try { - final DomFontFace fontFace = createDomFontFace(family, 'url(${ui_web.assetManager.getAssetUrl(asset)})', descriptors); + final DomFontFace fontFace = createDomFontFace( + family, + 'url(${ui_web.assetManager.getAssetUrl(asset)})', + descriptors, + ); return await fontFace.load(); } catch (e) { printWarning('Error while loading font family "$family":\n$e'); @@ -182,6 +185,5 @@ class HtmlFontCollection implements FlutterFontCollection { } @override - void debugResetFallbackFonts() { - } + void debugResetFallbackFonts() {} } diff --git a/lib/web_ui/lib/src/engine/text/layout_fragmenter.dart b/lib/web_ui/lib/src/engine/text/layout_fragmenter.dart index cf1eb647c2103..c892f95d75e0e 100644 --- a/lib/web_ui/lib/src/engine/text/layout_fragmenter.dart +++ b/lib/web_ui/lib/src/engine/text/layout_fragmenter.dart @@ -29,8 +29,10 @@ class LayoutFragmenter extends TextFragmenter { int fragmentStart = 0; - final Iterator lineBreakFragments = LineBreakFragmenter(text).fragment().iterator..moveNext(); - final Iterator bidiFragments = BidiFragmenter(text).fragment().iterator..moveNext(); + final Iterator lineBreakFragments = + LineBreakFragmenter(text).fragment().iterator..moveNext(); + final Iterator bidiFragments = + BidiFragmenter(text).fragment().iterator..moveNext(); final Iterator spans = paragraphSpans.iterator..moveNext(); LineBreakFragment currentLineBreakFragment = lineBreakFragments.current; @@ -40,32 +42,31 @@ class LayoutFragmenter extends TextFragmenter { while (true) { final int fragmentEnd = math.min( currentLineBreakFragment.end, - math.min( - currentBidiFragment.end, - currentSpan.end, - ), + math.min(currentBidiFragment.end, currentSpan.end), ); final int distanceFromLineBreak = currentLineBreakFragment.end - fragmentEnd; - final LineBreakType lineBreakType = distanceFromLineBreak == 0 - ? currentLineBreakFragment.type - : LineBreakType.prohibited; + final LineBreakType lineBreakType = + distanceFromLineBreak == 0 ? currentLineBreakFragment.type : LineBreakType.prohibited; - final int trailingNewlines = currentLineBreakFragment.trailingNewlines - distanceFromLineBreak; + final int trailingNewlines = + currentLineBreakFragment.trailingNewlines - distanceFromLineBreak; final int trailingSpaces = currentLineBreakFragment.trailingSpaces - distanceFromLineBreak; final int fragmentLength = fragmentEnd - fragmentStart; - fragments.add(LayoutFragment( - fragmentStart, - fragmentEnd, - lineBreakType, - currentBidiFragment.textDirection, - currentBidiFragment.fragmentFlow, - currentSpan, - trailingNewlines: clampInt(trailingNewlines, 0, fragmentLength), - trailingSpaces: clampInt(trailingSpaces, 0, fragmentLength), - )); + fragments.add( + LayoutFragment( + fragmentStart, + fragmentEnd, + lineBreakType, + currentBidiFragment.textDirection, + currentBidiFragment.fragmentFlow, + currentSpan, + trailingNewlines: clampInt(trailingNewlines, 0, fragmentLength), + trailingSpaces: clampInt(trailingSpaces, 0, fragmentLength), + ), + ); fragmentStart = fragmentEnd; @@ -109,8 +110,8 @@ abstract class _CombinedFragment extends TextFragment { this.span, { required this.trailingNewlines, required this.trailingSpaces, - }) : assert(trailingNewlines >= 0), - assert(trailingSpaces >= trailingNewlines); + }) : assert(trailingNewlines >= 0), + assert(trailingSpaces >= trailingNewlines); final LineBreakType type; @@ -151,7 +152,8 @@ abstract class _CombinedFragment extends TextFragment { } } -class LayoutFragment extends _CombinedFragment with _FragmentMetrics, _FragmentPosition, _FragmentBox { +class LayoutFragment extends _CombinedFragment + with _FragmentMetrics, _FragmentPosition, _FragmentBox { LayoutFragment( super.start, super.end, @@ -246,7 +248,8 @@ mixin _FragmentMetrics on _CombinedFragment { late double _widthExcludingTrailingSpaces; /// The width of the measured text, including any trailing spaces. - double get widthIncludingTrailingSpaces => _widthIncludingTrailingSpaces + _extraWidthForJustification; + double get widthIncludingTrailingSpaces => + _widthIncludingTrailingSpaces + _extraWidthForJustification; late double _widthIncludingTrailingSpaces; double _extraWidthForJustification = 0.0; @@ -257,7 +260,8 @@ mixin _FragmentMetrics on _CombinedFragment { double get widthOfTrailingSpaces => widthIncludingTrailingSpaces - widthExcludingTrailingSpaces; /// Set measurement values for the fragment. - void setMetrics(Spanometer spanometer, { + void setMetrics( + Spanometer spanometer, { required double ascent, required double descent, required double widthExcludingTrailingSpaces, @@ -316,21 +320,16 @@ mixin _FragmentPosition on _CombinedFragment, _FragmentMetrics { double get endOffset => startOffset + widthIncludingTrailingSpaces; /// The distance from the left edge of the line to the left edge of the fragment. - double get left => line.textDirection == ui.TextDirection.ltr - ? startOffset - : line.width - endOffset; + double get left => + line.textDirection == ui.TextDirection.ltr ? startOffset : line.width - endOffset; /// The distance from the left edge of the line to the right edge of the fragment. - double get right => line.textDirection == ui.TextDirection.ltr - ? endOffset - : line.width - startOffset; + double get right => + line.textDirection == ui.TextDirection.ltr ? endOffset : line.width - startOffset; /// Set the horizontal position of this fragment relative to the [line] that /// contains it. - void setPosition({ - required double startOffset, - required ui.TextDirection textDirection, - }) { + void setPosition({required double startOffset, required ui.TextDirection textDirection}) { _startOffset = startOffset; _textDirection ??= textDirection; } @@ -384,19 +383,19 @@ mixin _FragmentBox on _CombinedFragment, _FragmentMetrics, _FragmentPosition { // For painting, we exclude the width of trailing spaces from the box. return textDirection! == ui.TextDirection.ltr ? ui.TextBox.fromLTRBD( - line.left + left, - top, - line.left + right - widthOfTrailingSpaces, - bottom, - textDirection!, - ) + line.left + left, + top, + line.left + right - widthOfTrailingSpaces, + bottom, + textDirection!, + ) : ui.TextBox.fromLTRBD( - line.left + left + widthOfTrailingSpaces, - top, - line.left + right, - bottom, - textDirection!, - ); + line.left + left + widthOfTrailingSpaces, + top, + line.left + right, + bottom, + textDirection!, + ); } return _textBoxIncludingTrailingSpaces; } @@ -408,10 +407,7 @@ mixin _FragmentBox on _CombinedFragment, _FragmentMetrics, _FragmentPosition { /// /// As opposed to [toPaintingTextBox], the resulting text box from this method /// includes trailing spaces of the fragment. - ui.TextBox toTextBox({ - int? start, - int? end, - }) { + ui.TextBox toTextBox({int? start, int? end}) { start ??= this.start; end ??= this.end; @@ -483,13 +479,7 @@ mixin _FragmentBox on _CombinedFragment, _FragmentMetrics, _FragmentPosition { // The fragment's left and right edges are relative to the line. In order // to make them relative to the paragraph, we need to add the left edge of // the line. - return ui.TextBox.fromLTRBD( - line.left + left, - top, - line.left + right, - bottom, - textDirection!, - ); + return ui.TextBox.fromLTRBD(line.left + left, top, line.left + right, bottom, textDirection!); } /// Returns the text position within this fragment's range that's closest to @@ -514,7 +504,7 @@ mixin _FragmentBox on _CombinedFragment, _FragmentMetrics, _FragmentPosition { final double distanceFromEnd = widthIncludingTrailingSpaces - x; return distanceFromStart < distanceFromEnd ? ui.TextPosition(offset: startIndex) - : ui.TextPosition(offset: endIndex, affinity: ui.TextAffinity.upstream,); + : ui.TextPosition(offset: endIndex, affinity: ui.TextAffinity.upstream); } _spanometer.currentSpan = span; @@ -535,10 +525,7 @@ mixin _FragmentBox on _CombinedFragment, _FragmentMetrics, _FragmentPosition { ); if (cutoff == endIndex) { - return ui.TextPosition( - offset: cutoff, - affinity: ui.TextAffinity.upstream, - ); + return ui.TextPosition(offset: cutoff, affinity: ui.TextAffinity.upstream); } final double lowWidth = _spanometer.measureRange(startIndex, cutoff); @@ -550,10 +537,7 @@ mixin _FragmentBox on _CombinedFragment, _FragmentMetrics, _FragmentPosition { return ui.TextPosition(offset: cutoff); } else { // The offset is closer to cutoff + 1. - return ui.TextPosition( - offset: cutoff + 1, - affinity: ui.TextAffinity.upstream, - ); + return ui.TextPosition(offset: cutoff + 1, affinity: ui.TextAffinity.upstream); } } @@ -605,13 +589,14 @@ mixin _FragmentBox on _CombinedFragment, _FragmentMetrics, _FragmentPosition { assert(end > start); assert(line.graphemeStarts.isNotEmpty); final int startIndex = line.graphemeStartIndexBefore(start, 0, lineGraphemeBreaks.length); - final int endIndex = end == start + 1 - ? startIndex + 1 - : line.graphemeStartIndexBefore(end - 1, startIndex, lineGraphemeBreaks.length) + 1; + final int endIndex = + end == start + 1 + ? startIndex + 1 + : line.graphemeStartIndexBefore(end - 1, startIndex, lineGraphemeBreaks.length) + 1; final int firstGraphemeStart = lineGraphemeBreaks[startIndex]; return firstGraphemeStart > start - ? (endIndex == startIndex + 1 ? null : (startIndex + 1, endIndex)) - : (startIndex, endIndex); + ? (endIndex == startIndex + 1 ? null : (startIndex + 1, endIndex)) + : (startIndex, endIndex); } /// Whether the first codepoints of this fragment is not a valid grapheme start, @@ -627,14 +612,18 @@ mixin _FragmentBox on _CombinedFragment, _FragmentMetrics, _FragmentPosition { // U+25CC or U+00A0 for showing nonspacing marks in isolation. bool get hasLeadingBrokenGrapheme { final int? graphemeStartIndexRangeStart = graphemeStartIndexRange?.$1; - return graphemeStartIndexRangeStart == null || line.graphemeStarts[graphemeStartIndexRangeStart] != start; + return graphemeStartIndexRangeStart == null || + line.graphemeStarts[graphemeStartIndexRangeStart] != start; } /// Returns the GlyphInfo within the range [line.graphemeStarts[startIndex], line.graphemeStarts[endIndex]), /// that's visually closeset to the given horizontal offset `x` (in the paragraph's coordinates). ui.GlyphInfo _getClosestCharacterInRange(double x, int startIndex, int endIndex) { final List graphemeStartIndices = line.graphemeStarts; - final ui.TextRange fullRange = ui.TextRange(start: graphemeStartIndices[startIndex], end: graphemeStartIndices[endIndex]); + final ui.TextRange fullRange = ui.TextRange( + start: graphemeStartIndices[startIndex], + end: graphemeStartIndices[endIndex], + ); final ui.TextBox fullBox = toTextBox(start: fullRange.start, end: fullRange.end); if (startIndex + 1 == endIndex) { return ui.GlyphInfo(fullBox.toRect(), fullRange, fullBox.direction); @@ -650,17 +639,31 @@ mixin _FragmentBox on _CombinedFragment, _FragmentMetrics, _FragmentPosition { final int midIndex = (startIndex + endIndex) ~/ 2; // endIndex >= startIndex + 2, so midIndex >= start + 1 final ui.GlyphInfo firstHalf = _getClosestCharacterInRange(x, startIndex, midIndex); - if (firstHalf.graphemeClusterLayoutBounds.left < x && x < firstHalf.graphemeClusterLayoutBounds.right) { + if (firstHalf.graphemeClusterLayoutBounds.left < x && + x < firstHalf.graphemeClusterLayoutBounds.right) { return firstHalf; } // startIndex <= endIndex - 2, so midIndex <= endIndex - 1 final ui.GlyphInfo secondHalf = _getClosestCharacterInRange(x, midIndex, endIndex); - if (secondHalf.graphemeClusterLayoutBounds.left < x && x < secondHalf.graphemeClusterLayoutBounds.right) { + if (secondHalf.graphemeClusterLayoutBounds.left < x && + x < secondHalf.graphemeClusterLayoutBounds.right) { return secondHalf; } // Neither box clips the given x. This is supposed to be rare. - final double distanceToFirst = (x - x.clamp(firstHalf.graphemeClusterLayoutBounds.left, firstHalf.graphemeClusterLayoutBounds.right)).abs(); - final double distanceToSecond = (x - x.clamp(secondHalf.graphemeClusterLayoutBounds.left, secondHalf.graphemeClusterLayoutBounds.right)).abs(); + final double distanceToFirst = + (x - + x.clamp( + firstHalf.graphemeClusterLayoutBounds.left, + firstHalf.graphemeClusterLayoutBounds.right, + )) + .abs(); + final double distanceToSecond = + (x - + x.clamp( + secondHalf.graphemeClusterLayoutBounds.left, + secondHalf.graphemeClusterLayoutBounds.right, + )) + .abs(); return distanceToFirst > distanceToSecond ? firstHalf : secondHalf; } @@ -695,21 +698,19 @@ mixin _FragmentBox on _CombinedFragment, _FragmentMetrics, _FragmentPosition { } class EllipsisFragment extends LayoutFragment { - EllipsisFragment( - int index, - ParagraphSpan span, - ) : super( - index, - index, - LineBreakType.endOfText, - null, - // The ellipsis is always at the end of the line, so it can't be - // sandwiched. This means it'll always follow the paragraph direction. - FragmentFlow.sandwich, - span, - trailingNewlines: 0, - trailingSpaces: 0, - ); + EllipsisFragment(int index, ParagraphSpan span) + : super( + index, + index, + LineBreakType.endOfText, + null, + // The ellipsis is always at the end of the line, so it can't be + // sandwiched. This means it'll always follow the paragraph direction. + FragmentFlow.sandwich, + span, + trailingNewlines: 0, + trailingSpaces: 0, + ); @override bool get isSpaceOnly => false; diff --git a/lib/web_ui/lib/src/engine/text/layout_service.dart b/lib/web_ui/lib/src/engine/text/layout_service.dart index 3273c85f47556..539cf6cacca0e 100644 --- a/lib/web_ui/lib/src/engine/text/layout_service.dart +++ b/lib/web_ui/lib/src/engine/text/layout_service.dart @@ -62,8 +62,10 @@ class TextLayoutService { late final Spanometer spanometer = Spanometer(paragraph); - late final LayoutFragmenter layoutFragmenter = - LayoutFragmenter(paragraph.plainText, paragraph.spans); + late final LayoutFragmenter layoutFragmenter = LayoutFragmenter( + paragraph.plainText, + paragraph.spans, + ); /// Performs the layout on a paragraph given the [constraints]. /// @@ -89,8 +91,7 @@ class TextLayoutService { didExceedMaxLines = false; lines.clear(); - LineBuilder currentLine = - LineBuilder.first(paragraph, spanometer, maxWidth: constraints.width); + LineBuilder currentLine = LineBuilder.first(paragraph, spanometer, maxWidth: constraints.width); final List fragments = layoutFragmenter.fragment()..forEach(spanometer.measureFragment); @@ -160,12 +161,7 @@ class TextLayoutService { boundsRight = right; } } - _paintBounds = ui.Rect.fromLTRB( - boundsLeft, - 0, - boundsRight, - height, - ); + _paintBounds = ui.Rect.fromLTRB(boundsLeft, 0, boundsRight, height); // **************************** // // *** FRAGMENT POSITIONING *** // @@ -174,8 +170,8 @@ class TextLayoutService { // We have to perform justification alignment first so that we can position // fragments correctly later. if (lines.isNotEmpty) { - final bool shouldJustifyParagraph = width.isFinite && - paragraph.paragraphStyle.textAlign == ui.TextAlign.justify; + final bool shouldJustifyParagraph = + width.isFinite && paragraph.paragraphStyle.textAlign == ui.TextAlign.justify; if (shouldJustifyParagraph) { // Don't apply justification to the last line. @@ -221,8 +217,7 @@ class TextLayoutService { } } - ui.TextDirection get _paragraphDirection => - paragraph.paragraphStyle.effectiveTextDirection; + ui.TextDirection get _paragraphDirection => paragraph.paragraphStyle.effectiveTextDirection; /// Positions the fragments taking into account their directions and the /// paragraph's direction. @@ -246,14 +241,12 @@ class TextLayoutService { continue; } - - assert(fragment.fragmentFlow == FragmentFlow.ltr || - fragment.fragmentFlow == FragmentFlow.rtl); + assert( + fragment.fragmentFlow == FragmentFlow.ltr || fragment.fragmentFlow == FragmentFlow.rtl, + ); final ui.TextDirection currentDirection = - fragment.fragmentFlow == FragmentFlow.ltr - ? ui.TextDirection.ltr - : ui.TextDirection.rtl; + fragment.fragmentFlow == FragmentFlow.ltr ? ui.TextDirection.ltr : ui.TextDirection.rtl; if (currentDirection == previousDirection) { sandwichStart = null; @@ -295,7 +288,7 @@ class TextLayoutService { sequenceStart = i; sandwichStart = null; - if (i < line.fragments.length){ + if (i < line.fragments.length) { previousDirection = line.fragments[i].textDirection!; } } @@ -318,13 +311,11 @@ class TextLayoutService { if (direction == _paragraphDirection) { for (int i = start; i < end; i++) { - cumulativeWidth += - _positionOneFragment(line, i, startOffset + cumulativeWidth, direction); + cumulativeWidth += _positionOneFragment(line, i, startOffset + cumulativeWidth, direction); } } else { for (int i = end - 1; i >= start; i--) { - cumulativeWidth += - _positionOneFragment(line, i, startOffset + cumulativeWidth, direction); + cumulativeWidth += _positionOneFragment(line, i, startOffset + cumulativeWidth, direction); } } @@ -396,9 +387,7 @@ class TextLayoutService { } // [offset] is to the left of the line. if (offset.dx <= line.left) { - return ui.TextPosition( - offset: line.startIndex, - ); + return ui.TextPosition(offset: line.startIndex); } // [offset] is to the right of the line. @@ -429,14 +418,15 @@ class TextLayoutService { return null; } final double dx = offset.dx; - final bool closestGraphemeStartInFragment = !fragment.hasLeadingBrokenGrapheme - || dx <= fragment.line.left - || fragment.line.left + fragment.line.width <= dx - || switch (fragment.textDirection!) { - // If dx is closer to the trailing edge, no need to check other fragments. - ui.TextDirection.ltr => dx >= line.left + (fragment.left + fragment.right) / 2, - ui.TextDirection.rtl => dx <= line.left + (fragment.left + fragment.right) / 2, - }; + final bool closestGraphemeStartInFragment = + !fragment.hasLeadingBrokenGrapheme || + dx <= fragment.line.left || + fragment.line.left + fragment.line.width <= dx || + switch (fragment.textDirection!) { + // If dx is closer to the trailing edge, no need to check other fragments. + ui.TextDirection.ltr => dx >= line.left + (fragment.left + fragment.right) / 2, + ui.TextDirection.rtl => dx <= line.left + (fragment.left + fragment.right) / 2, + }; final ui.GlyphInfo candidate1 = fragment.getClosestCharacterBox(dx); if (closestGraphemeStartInFragment) { return candidate1; @@ -445,7 +435,9 @@ class TextLayoutService { ui.TextDirection.ltr => true, ui.TextDirection.rtl => false, }; - final ui.GlyphInfo? candidate2 = fragment.line.closestFragmentTo(fragment, searchLeft)?.getClosestCharacterBox(dx); + final ui.GlyphInfo? candidate2 = fragment.line + .closestFragmentTo(fragment, searchLeft) + ?.getClosestCharacterBox(dx); if (candidate2 == null) { return candidate1; } @@ -527,17 +519,13 @@ class LineBuilder { int get startIndex { assert(_fragments.isNotEmpty || _fragmentsForNextLine!.isNotEmpty); - return isNotEmpty - ? _fragments.first.start - : _fragmentsForNextLine!.first.start; + return isNotEmpty ? _fragments.first.start : _fragmentsForNextLine!.first.start; } int get endIndex { assert(_fragments.isNotEmpty || _fragmentsForNextLine!.isNotEmpty); - return isNotEmpty - ? _fragments.last.end - : _fragmentsForNextLine!.first.start; + return isNotEmpty ? _fragments.last.end : _fragmentsForNextLine!.first.start; } final double maxWidth; @@ -554,9 +542,10 @@ class LineBuilder { /// The width of the line so far, including trailing white space. double widthIncludingSpace = 0.0; - double get _widthExcludingLastFragment => _fragments.length > 1 - ? widthIncludingSpace - _fragments.last.widthIncludingTrailingSpaces - : 0; + double get _widthExcludingLastFragment => + _fragments.length > 1 + ? widthIncludingSpace - _fragments.last.widthIncludingTrailingSpaces + : 0; /// The distance from the top of the line to the alphabetic baseline. double ascent = 0.0; @@ -638,8 +627,7 @@ class LineBuilder { return true; } - ui.TextDirection get _paragraphDirection => - paragraph.paragraphStyle.effectiveTextDirection; + ui.TextDirection get _paragraphDirection => paragraph.paragraphStyle.effectiveTextDirection; void addFragment(LayoutFragment fragment) { _updateMetrics(fragment); @@ -715,7 +703,8 @@ class LineBuilder { // Update the metrics of the fragment to reflect the calculated ascent and // descent. - fragment.setMetrics(spanometer, + fragment.setMetrics( + spanometer, ascent: ascent, descent: descent, widthExcludingTrailingSpaces: fragment.widthExcludingTrailingSpaces, @@ -741,7 +730,7 @@ class LineBuilder { } } - void forceBreakLastFragment({ double? availableWidth, bool allowEmptyLine = false }) { + void forceBreakLastFragment({double? availableWidth, bool allowEmptyLine = false}) { assert(isNotEmpty); availableWidth ??= maxWidth; @@ -767,7 +756,8 @@ class LineBuilder { } spanometer.currentSpan = lastFragment.span; - final double lineWidthWithoutLastFragment = widthIncludingSpace - lastFragment.widthIncludingTrailingSpaces; + final double lineWidthWithoutLastFragment = + widthIncludingSpace - lastFragment.widthIncludingTrailingSpaces; final double availableWidthForFragment = availableWidth - lineWidthWithoutLastFragment; final int forceBreakEnd = lastFragment.end - lastFragment.trailingNewlines; @@ -825,11 +815,9 @@ class LineBuilder { final LayoutFragment lastFragment = _fragments.last; forceBreakLastFragment(availableWidth: availableWidth, allowEmptyLine: true); - final EllipsisFragment ellipsisFragment = EllipsisFragment( - endIndex, - lastFragment.span, - ); - ellipsisFragment.setMetrics(spanometer, + final EllipsisFragment ellipsisFragment = EllipsisFragment(endIndex, lastFragment.span); + ellipsisFragment.setMetrics( + spanometer, ascent: lastFragment.ascent, descent: lastFragment.descent, widthExcludingTrailingSpaces: ellipsisWidth, @@ -876,7 +864,8 @@ class LineBuilder { /// Builds the [ParagraphLine] instance that represents this line. ParagraphLine build() { if (_fragmentsForNextLine == null) { - _fragmentsForNextLine = _fragments.getRange(_lastBreakableFragment + 1, _fragments.length).toList(); + _fragmentsForNextLine = + _fragments.getRange(_lastBreakableFragment + 1, _fragments.length).toList(); _fragments.removeRange(_lastBreakableFragment + 1, _fragments.length); } @@ -936,8 +925,7 @@ class Spanometer { static final RulerHost _rulerHost = RulerHost(); - static final Map _rulers = - {}; + static final Map _rulers = {}; @visibleForTesting static Map get rulers => _rulers; @@ -1023,7 +1011,8 @@ class Spanometer { final PlaceholderSpan placeholder = fragment.span as PlaceholderSpan; // The ascent/descent values of the placeholder fragment will be finalized // later when the line is built. - fragment.setMetrics(this, + fragment.setMetrics( + this, ascent: placeholder.height, descent: 0, widthExcludingTrailingSpaces: placeholder.width, @@ -1031,9 +1020,16 @@ class Spanometer { ); } else { currentSpan = fragment.span; - final double widthExcludingTrailingSpaces = _measure(fragment.start, fragment.end - fragment.trailingSpaces); - final double widthIncludingTrailingSpaces = _measure(fragment.start, fragment.end - fragment.trailingNewlines); - fragment.setMetrics(this, + final double widthExcludingTrailingSpaces = _measure( + fragment.start, + fragment.end - fragment.trailingSpaces, + ); + final double widthIncludingTrailingSpaces = _measure( + fragment.start, + fragment.end - fragment.trailingNewlines, + ); + fragment.setMetrics( + this, ascent: ascent, descent: descent, widthExcludingTrailingSpaces: widthExcludingTrailingSpaces, @@ -1054,12 +1050,7 @@ class Spanometer { /// /// See also: /// - [LineBuilder.forceBreak]. - int forceBreak( - int start, - int end, { - required double availableWidth, - required bool allowEmpty, - }) { + int forceBreak(int start, int end, {required double availableWidth, required bool allowEmpty}) { assert(_currentSpan != null); // Make sure the range is within the current span. diff --git a/lib/web_ui/lib/src/engine/text/line_break_properties.dart b/lib/web_ui/lib/src/engine/text/line_break_properties.dart index f3d89b8a016be..9f4bff02142cd 100644 --- a/lib/web_ui/lib/src/engine/text/line_break_properties.dart +++ b/lib/web_ui/lib/src/engine/text/line_break_properties.dart @@ -17,13 +17,12 @@ UnicodePropertyLookup get lineLookup => ensureLineLookupInitia /// frame is rendered will reduce jank by moving the initialization out of /// the frame. UnicodePropertyLookup ensureLineLookupInitialized() { - return _lineLookup ??= - UnicodePropertyLookup.fromPackedData( - packedLineBreakProperties, - singleLineBreakRangesCount, - LineCharProperty.values, - defaultLineCharProperty, - ); + return _lineLookup ??= UnicodePropertyLookup.fromPackedData( + packedLineBreakProperties, + singleLineBreakRangesCount, + LineCharProperty.values, + defaultLineCharProperty, + ); } UnicodePropertyLookup? _lineLookup; diff --git a/lib/web_ui/lib/src/engine/text/line_breaker.dart b/lib/web_ui/lib/src/engine/text/line_breaker.dart index 5e24a28b0f215..839e147873c8e 100644 --- a/lib/web_ui/lib/src/engine/text/line_breaker.dart +++ b/lib/web_ui/lib/src/engine/text/line_breaker.dart @@ -77,7 +77,11 @@ class V8LineBreakFragmenter extends TextFragmenter implements LineBreakFragmente } } -List breakLinesUsingV8BreakIterator(String text, JSString jsText, DomV8BreakIterator iterator) { +List breakLinesUsingV8BreakIterator( + String text, + JSString jsText, + DomV8BreakIterator iterator, +) { final List breaks = []; int fragmentStart = 0; @@ -99,13 +103,15 @@ List breakLinesUsingV8BreakIterator(String text, JSString jsT } else { // Always break after a sequence of spaces. if (trailingSpaces > 0) { - breaks.add(LineBreakFragment( - fragmentStart, - i, - LineBreakType.opportunity, - trailingNewlines: trailingNewlines, - trailingSpaces: trailingSpaces, - )); + breaks.add( + LineBreakFragment( + fragmentStart, + i, + LineBreakType.opportunity, + trailingNewlines: trailingNewlines, + trailingSpaces: trailingSpaces, + ), + ); fragmentStart = i; trailingNewlines = 0; trailingSpaces = 0; @@ -122,25 +128,38 @@ List breakLinesUsingV8BreakIterator(String text, JSString jsT type = LineBreakType.opportunity; } - breaks.add(LineBreakFragment( - fragmentStart, - fragmentEnd, - type, - trailingNewlines: trailingNewlines, - trailingSpaces: trailingSpaces, - )); + breaks.add( + LineBreakFragment( + fragmentStart, + fragmentEnd, + type, + trailingNewlines: trailingNewlines, + trailingSpaces: trailingSpaces, + ), + ); fragmentStart = fragmentEnd; } if (breaks.isEmpty || breaks.last.type == LineBreakType.mandatory) { - breaks.add(LineBreakFragment(text.length, text.length, LineBreakType.endOfText, trailingNewlines: 0, trailingSpaces: 0)); + breaks.add( + LineBreakFragment( + text.length, + text.length, + LineBreakType.endOfText, + trailingNewlines: 0, + trailingSpaces: 0, + ), + ); } return breaks; } class LineBreakFragment extends TextFragment { - const LineBreakFragment(super.start, super.end, this.type, { + const LineBreakFragment( + super.start, + super.end, + this.type, { required this.trailingNewlines, required this.trailingSpaces, }); @@ -243,8 +262,7 @@ List _computeLineBreakFragments(String text) { int fragmentStart = 0; void setBreak(LineBreakType type, int debugRuleNumber) { - final int fragmentEnd = - type == LineBreakType.endOfText ? text.length : index; + final int fragmentEnd = type == LineBreakType.endOfText ? text.length : index; assert(fragmentEnd >= fragmentStart); // Uncomment the following line to help debug line breaking. @@ -262,13 +280,15 @@ List _computeLineBreakFragments(String text) { return; } - fragments.add(LineBreakFragment( - fragmentStart, - fragmentEnd, - type, - trailingNewlines: trailingNewlines, - trailingSpaces: trailingSpaces, - )); + fragments.add( + LineBreakFragment( + fragmentStart, + fragmentEnd, + type, + trailingNewlines: trailingNewlines, + trailingSpaces: trailingSpaces, + ), + ); fragmentStart = index; @@ -594,14 +614,12 @@ List _computeLineBreakFragments(String text) { // Do not break between numeric prefix/postfix and letters, or between // letters and prefix/postfix. // LB24: (PR | PO) × (AL | HL) - if ((prev1 == LineCharProperty.PR || prev1 == LineCharProperty.PO) && - _isALorHL(curr)) { + if ((prev1 == LineCharProperty.PR || prev1 == LineCharProperty.PO) && _isALorHL(curr)) { setBreak(LineBreakType.prohibited, 24); continue; } // LB24: (AL | HL) × (PR | PO) - if (_isALorHL(prev1) && - (curr == LineCharProperty.PR || curr == LineCharProperty.PO)) { + if (_isALorHL(prev1) && (curr == LineCharProperty.PR || curr == LineCharProperty.PO)) { setBreak(LineBreakType.prohibited, 24); continue; } diff --git a/lib/web_ui/lib/src/engine/text/measurement.dart b/lib/web_ui/lib/src/engine/text/measurement.dart index b5ca5dff66b3a..99cb3cbfaaadd 100644 --- a/lib/web_ui/lib/src/engine/text/measurement.dart +++ b/lib/web_ui/lib/src/engine/text/measurement.dart @@ -89,17 +89,13 @@ double measureSubstring( double width; // TODO(mdebbar): Explore caching all widths in a map, not only the last one. - if (start == _lastStart && - end == _lastEnd && - text == _lastText && - cssFont == _lastCssFont) { + if (start == _lastStart && end == _lastEnd && text == _lastText && cssFont == _lastCssFont) { // Reuse the previously calculated width if all factors that affect width // are unchanged. The only exception is letter-spacing. We always add // letter-spacing to the width later below. width = _lastWidth; } else { - final String sub = - start == 0 && end == text.length ? text : text.substring(start, end); + final String sub = start == 0 && end == text.length ? text : text.substring(start, end); width = canvasContext.measureText(sub).width!; } diff --git a/lib/web_ui/lib/src/engine/text/paint_service.dart b/lib/web_ui/lib/src/engine/text/paint_service.dart index 7c1de48489c73..4aa3a8943bb2d 100644 --- a/lib/web_ui/lib/src/engine/text/paint_service.dart +++ b/lib/web_ui/lib/src/engine/text/paint_service.dart @@ -30,11 +30,7 @@ class TextPaintService { } } - void _paintBackground( - BitmapCanvas canvas, - ui.Offset offset, - LayoutFragment fragment, - ) { + void _paintBackground(BitmapCanvas canvas, ui.Offset offset, LayoutFragment fragment) { if (fragment.isPlaceholder) { return; } @@ -67,9 +63,8 @@ class TextPaintService { } _prepareCanvasForFragment(canvas, fragment); - final double fragmentX = fragment.textDirection! == ui.TextDirection.ltr - ? fragment.left - : fragment.right; + final double fragmentX = + fragment.textDirection! == ui.TextDirection.ltr ? fragment.left : fragment.right; final double x = offset.dx + line.left + fragmentX; final double y = offset.dy + line.baseline; diff --git a/lib/web_ui/lib/src/engine/text/paragraph.dart b/lib/web_ui/lib/src/engine/text/paragraph.dart index dbb88918ddfb7..fb266863dbdff 100644 --- a/lib/web_ui/lib/src/engine/text/paragraph.dart +++ b/lib/web_ui/lib/src/engine/text/paragraph.dart @@ -56,16 +56,16 @@ class EngineLineMetrics implements ui.LineMetrics { @override int get hashCode => Object.hash( - hardBreak, - ascent, - descent, - unscaledAscent, - height, - width, - left, - baseline, - lineNumber, - ); + hardBreak, + ascent, + descent, + unscaledAscent, + height, + width, + left, + baseline, + lineNumber, + ); @override bool operator ==(Object other) { @@ -91,7 +91,8 @@ class EngineLineMetrics implements ui.LineMetrics { String toString() { String result = super.toString(); assert(() { - result = 'LineMetrics(hardBreak: $hardBreak, ' + result = + 'LineMetrics(hardBreak: $hardBreak, ' 'ascent: $ascent, ' 'descent: $descent, ' 'unscaledAscent: $unscaledAscent, ' @@ -128,16 +129,16 @@ class ParagraphLine { this.displayText, }) : assert(trailingNewlines <= endIndex - startIndex), lineMetrics = EngineLineMetrics( - hardBreak: hardBreak, - ascent: ascent, - descent: descent, - unscaledAscent: ascent, - height: height, - width: width, - left: left, - baseline: baseline, - lineNumber: lineNumber, - ); + hardBreak: hardBreak, + ascent: ascent, + descent: descent, + unscaledAscent: ascent, + height: height, + width: width, + left: left, + baseline: baseline, + lineNumber: lineNumber, + ); /// Metrics for this line of the paragraph. final EngineLineMetrics lineMetrics; @@ -158,8 +159,8 @@ class ParagraphLine { /// ellipsized, this returns [startIndex]; late final int visibleEndIndex = switch (fragments) { [] => startIndex, - [...final List rest, EllipsisFragment()] - || final List rest => rest.last.end, + [...final List rest, EllipsisFragment()] || final List rest => + rest.last.end, }; /// The number of new line characters at the end of the line. @@ -256,7 +257,8 @@ class ParagraphLine { } List _breakTextIntoGraphemes(String text) { - final List graphemeStarts = domIntl.Segmenter == null ? _fallbackGraphemeStartIterable(text) : _fromDomSegmenter(text); + final List graphemeStarts = + domIntl.Segmenter == null ? _fallbackGraphemeStartIterable(text) : _fromDomSegmenter(text); // Add the end index of the fragment to the list if the text is not empty. if (graphemeStarts.isNotEmpty) { graphemeStarts.add(visibleEndIndex); @@ -270,9 +272,10 @@ class ParagraphLine { /// /// For example, `graphemeStarts[n]` gives the UTF16 offset of the `n`-th /// grapheme in the line. - late final List graphemeStarts = visibleEndIndex == startIndex - ? const [] - : _breakTextIntoGraphemes(paragraph.plainText.substring(startIndex, visibleEndIndex)); + late final List graphemeStarts = + visibleEndIndex == startIndex + ? const [] + : _breakTextIntoGraphemes(paragraph.plainText.substring(startIndex, visibleEndIndex)); /// Translate a UTF16 code unit in the paragaph (`offset`), to a grapheme /// offset with in the current line. @@ -294,9 +297,12 @@ class ParagraphLine { // high >= low + 2, so low + 1 <= mid <= high - 1 final int mid = (low + high) ~/ 2; switch (lineGraphemeBreaks[mid] - offset) { - case > 0: high = mid; - case < 0: low = mid; - case == 0: return mid; + case > 0: + high = mid; + case < 0: + low = mid; + case == 0: + return mid; } } @@ -328,15 +334,16 @@ class ParagraphLine { if (fragment.graphemeStartIndexRange == null) { continue; } - final double distance = searchLeft - ? targetFragment.left - fragment.right - : fragment.left - targetFragment.right; + final double distance = + searchLeft ? targetFragment.left - fragment.right : fragment.left - targetFragment.right; final double? minDistance = closestFragment?.distance; switch (distance) { case > 0.0 when minDistance == null || minDistance > distance: closestFragment = (fragment: fragment, distance: distance); - case == 0.0: return fragment; - case _: continue; + case == 0.0: + return fragment; + case _: + continue; } } return closestFragment?.fragment; @@ -387,17 +394,17 @@ class ParagraphLine { @override int get hashCode => Object.hash( - lineMetrics, - startIndex, - endIndex, - trailingNewlines, - trailingSpaces, - spaceCount, - widthWithTrailingSpaces, - fragments, - textDirection, - displayText, - ); + lineMetrics, + startIndex, + endIndex, + trailingNewlines, + trailingSpaces, + spaceCount, + widthWithTrailingSpaces, + fragments, + textDirection, + displayText, + ); @override bool operator ==(Object other) { @@ -442,10 +449,10 @@ class EngineParagraphStyle implements ui.ParagraphStyle { ui.StrutStyle? strutStyle, this.ellipsis, this.locale, - }) : _textHeightBehavior = textHeightBehavior, - // TODO(mdebbar): add support for strut style., b/128317744 - _strutStyle = strutStyle as EngineStrutStyle?, - height = height == ui.kTextHeightNone ? null : height; + }) : _textHeightBehavior = textHeightBehavior, + // TODO(mdebbar): add support for strut style., b/128317744 + _strutStyle = strutStyle as EngineStrutStyle?, + height = height == ui.kTextHeightNone ? null : height; final ui.TextAlign? textAlign; final ui.TextDirection? textDirection; @@ -462,8 +469,7 @@ class EngineParagraphStyle implements ui.ParagraphStyle { // The effective style attributes should be consistent with paragraph_style.h. ui.TextAlign get effectiveTextAlign => textAlign ?? ui.TextAlign.start; - ui.TextDirection get effectiveTextDirection => - textDirection ?? ui.TextDirection.ltr; + ui.TextDirection get effectiveTextDirection => textDirection ?? ui.TextDirection.ltr; double? get lineHeight { // TODO(mdebbar): Implement proper support for strut styles. @@ -530,7 +536,8 @@ class EngineParagraphStyle implements ui.ParagraphStyle { assert(() { final double? fontSize = this.fontSize; final double? height = this.height; - result = 'ParagraphStyle(' + result = + 'ParagraphStyle(' 'textAlign: ${textAlign ?? "unspecified"}, ' 'textDirection: ${textDirection ?? "unspecified"}, ' 'fontWeight: ${fontWeight ?? "unspecified"}, ' @@ -607,18 +614,17 @@ class EngineTextStyle implements ui.TextStyle { this.shadows, this.fontFeatures, this.fontVariations, - }) : assert( - color == null || foreground == null, - 'Cannot provide both a color and a foreground\n' - 'The color argument is just a shorthand for "foreground: Paint()..color = color".'), - isFontFamilyProvided = fontFamily != null, - fontFamily = fontFamily ?? ''; + }) : assert( + color == null || foreground == null, + 'Cannot provide both a color and a foreground\n' + 'The color argument is just a shorthand for "foreground: Paint()..color = color".', + ), + isFontFamilyProvided = fontFamily != null, + fontFamily = fontFamily ?? ''; /// Constructs an [EngineTextStyle] by reading properties from an /// [EngineParagraphStyle]. - factory EngineTextStyle.fromParagraphStyle( - EngineParagraphStyle paragraphStyle, - ) { + factory EngineTextStyle.fromParagraphStyle(EngineParagraphStyle paragraphStyle) { return EngineTextStyle.only( fontWeight: paragraphStyle.fontWeight, fontStyle: paragraphStyle.fontStyle, @@ -654,7 +660,8 @@ class EngineTextStyle implements ui.TextStyle { static final List _testFonts = ['FlutterTest', 'Ahem']; String get effectiveFontFamily { - final String fontFamily = this.fontFamily.isEmpty ? StyleManager.defaultFontFamily : this.fontFamily; + final String fontFamily = + this.fontFamily.isEmpty ? StyleManager.defaultFontFamily : this.fontFamily; // In the flutter tester environment, we use predictable-size test fonts. // This makes widget tests predictable and less flaky. String result = fontFamily; @@ -700,28 +707,28 @@ class EngineTextStyle implements ui.TextStyle { if (identical(this, other)) { return true; } - return other is EngineTextStyle - && other.color == color - && other.decoration == decoration - && other.decorationColor == decorationColor - && other.decorationStyle == decorationStyle - && other.fontWeight == fontWeight - && other.fontStyle == fontStyle - && other.textBaseline == textBaseline - && other.leadingDistribution == leadingDistribution - && other.fontFamily == fontFamily - && other.fontSize == fontSize - && other.letterSpacing == letterSpacing - && other.wordSpacing == wordSpacing - && other.height == height - && other.decorationThickness == decorationThickness - && other.locale == locale - && other.background == background - && other.foreground == foreground - && listEquals(other.shadows, shadows) - && listEquals(other.fontFamilyFallback, fontFamilyFallback) - && listEquals(other.fontFeatures, fontFeatures) - && listEquals(other.fontVariations, fontVariations); + return other is EngineTextStyle && + other.color == color && + other.decoration == decoration && + other.decorationColor == decorationColor && + other.decorationStyle == decorationStyle && + other.fontWeight == fontWeight && + other.fontStyle == fontStyle && + other.textBaseline == textBaseline && + other.leadingDistribution == leadingDistribution && + other.fontFamily == fontFamily && + other.fontSize == fontSize && + other.letterSpacing == letterSpacing && + other.wordSpacing == wordSpacing && + other.height == height && + other.decorationThickness == decorationThickness && + other.locale == locale && + other.background == background && + other.foreground == foreground && + listEquals(other.shadows, shadows) && + listEquals(other.fontFamilyFallback, fontFamilyFallback) && + listEquals(other.fontFeatures, fontFeatures) && + listEquals(other.fontVariations, fontVariations); } @override @@ -754,7 +761,7 @@ class EngineTextStyle implements ui.TextStyle { Object.hash( fontFeatures == null ? null : Object.hashAll(fontFeatures), fontVariations == null ? null : Object.hashAll(fontVariations), - ) + ), ); } @@ -765,7 +772,8 @@ class EngineTextStyle implements ui.TextStyle { final List? fontFamilyFallback = this.fontFamilyFallback; final double? fontSize = this.fontSize; final double? height = this.height; - result = 'TextStyle(' + result = + 'TextStyle(' 'color: ${color ?? "unspecified"}, ' 'decoration: ${decoration ?? "unspecified"}, ' 'decorationColor: ${decorationColor ?? "unspecified"}, ' @@ -806,15 +814,15 @@ class EngineStrutStyle implements ui.StrutStyle { ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, bool? forceStrutHeight, - }) : _fontFamily = fontFamily, - _fontFamilyFallback = fontFamilyFallback, - _fontSize = fontSize, - _height = height == ui.kTextHeightNone ? null : height, - _leadingDistribution = leadingDistribution, - _leading = leading, - _fontWeight = fontWeight, - _fontStyle = fontStyle, - _forceStrutHeight = forceStrutHeight; + }) : _fontFamily = fontFamily, + _fontFamilyFallback = fontFamilyFallback, + _fontSize = fontSize, + _height = height == ui.kTextHeightNone ? null : height, + _leadingDistribution = leadingDistribution, + _leading = leading, + _fontWeight = fontWeight, + _fontStyle = fontStyle, + _forceStrutHeight = forceStrutHeight; final String? _fontFamily; final List? _fontFamilyFallback; @@ -850,16 +858,16 @@ class EngineStrutStyle implements ui.StrutStyle { int get hashCode { final List? fontFamilyFallback = _fontFamilyFallback; return Object.hash( - _fontFamily, - fontFamilyFallback != null ? Object.hashAll(fontFamilyFallback) : null, - _fontSize, - _height, - _leading, - _leadingDistribution, - _fontWeight, - _fontStyle, - _forceStrutHeight, - ); + _fontFamily, + fontFamilyFallback != null ? Object.hashAll(fontFamilyFallback) : null, + _fontSize, + _height, + _leading, + _leadingDistribution, + _fontWeight, + _fontStyle, + _forceStrutHeight, + ); } } @@ -931,9 +939,7 @@ String fontWeightIndexToCss({int fontWeightIndex = 3}) { } assert(() { - throw AssertionError( - 'Failed to convert font weight $fontWeightIndex to CSS.', - ); + throw AssertionError('Failed to convert font weight $fontWeightIndex to CSS.'); }()); return ''; @@ -941,10 +947,7 @@ String fontWeightIndexToCss({int fontWeightIndex = 3}) { /// Applies a text [style] to an [element], translating the properties to their /// corresponding CSS equivalents. -void applyTextStyleToElement({ - required DomElement element, - required EngineTextStyle style, -}) { +void applyTextStyleToElement({required DomElement element, required EngineTextStyle style}) { bool updateDecoration = false; final DomCSSStyleDeclaration cssStyle = element.style; @@ -959,9 +962,8 @@ void applyTextStyleToElement({ cssStyle.color = 'transparent'; // Use hairline (device pixel when strokeWidth is not specified). final double? strokeWidth = style.foreground?.strokeWidth; - final double adaptedWidth = strokeWidth != null && strokeWidth > 0 - ? strokeWidth - : 1.0 / ui.window.devicePixelRatio; + final double adaptedWidth = + strokeWidth != null && strokeWidth > 0 ? strokeWidth : 1.0 / ui.window.devicePixelRatio; cssStyle.textStroke = '${adaptedWidth}px ${color?.toCssString()}'; } else if (color != null) { cssStyle.color = color.toCssString(); @@ -1003,8 +1005,10 @@ void applyTextStyleToElement({ if (updateDecoration) { if (style.decoration != null) { - final String? textDecoration = - _textDecorationToCssString(style.decoration, style.decorationStyle); + final String? textDecoration = _textDecorationToCssString( + style.decoration, + style.decorationStyle, + ); if (textDecoration != null) { if (ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit) { setElementStyle(element, '-webkit-text-decoration', textDecoration); @@ -1026,8 +1030,7 @@ void applyTextStyleToElement({ final List? fontVariations = style.fontVariations; if (fontVariations != null && fontVariations.isNotEmpty) { - cssStyle.setProperty( - 'font-variation-settings', _fontVariationListToCss(fontVariations)); + cssStyle.setProperty('font-variation-settings', _fontVariationListToCss(fontVariations)); } } @@ -1047,8 +1050,10 @@ String _shadowListToCss(List shadows) { sb.write(','); } final ui.Shadow shadow = shadows[i]; - sb.write('${shadow.offset.dx}px ${shadow.offset.dy}px ' - '${shadow.blurRadius}px ${shadow.color.toCssString()}'); + sb.write( + '${shadow.offset.dx}px ${shadow.offset.dy}px ' + '${shadow.blurRadius}px ${shadow.color.toCssString()}', + ); } return sb.toString(); } @@ -1087,7 +1092,9 @@ String _fontVariationListToCss(List fontVariations) { /// Converts text decoration style to CSS text-decoration-style value. String? _textDecorationToCssString( - ui.TextDecoration? decoration, ui.TextDecorationStyle? decorationStyle) { + ui.TextDecoration? decoration, + ui.TextDecorationStyle? decorationStyle, +) { final StringBuffer decorations = StringBuffer(); if (decoration != null) { if (decoration.contains(ui.TextDecoration.underline)) { @@ -1128,8 +1135,7 @@ String? _decorationStyleToCssString(ui.TextDecorationStyle decorationStyle) { /// ```css /// text-align: right; /// ``` -String textAlignToCssValue( - ui.TextAlign? align, ui.TextDirection textDirection) { +String textAlignToCssValue(ui.TextAlign? align, ui.TextDirection textDirection) { switch (align) { case ui.TextAlign.left: return 'left'; diff --git a/lib/web_ui/lib/src/engine/text/ruler.dart b/lib/web_ui/lib/src/engine/text/ruler.dart index 9cdd6e7c4ea0e..17f6a2b9d088a 100644 --- a/lib/web_ui/lib/src/engine/text/ruler.dart +++ b/lib/web_ui/lib/src/engine/text/ruler.dart @@ -114,16 +114,15 @@ class TextDimensions { _invalidateBoundsCache(); } - DomRect _readAndCacheMetrics() => - _cachedBoundingClientRect ??= _element.getBoundingClientRect(); + DomRect _readAndCacheMetrics() => _cachedBoundingClientRect ??= _element.getBoundingClientRect(); /// The height of the paragraph being measured. double get height { double cachedHeight = _readAndCacheMetrics().height; if (ui_web.browser.browserEngine == ui_web.BrowserEngine.firefox && - // In the flutter tester environment, we use a predictable-size for font - // measurement tests. - !ui_web.debugEmulateFlutterTesterEnvironment) { + // In the flutter tester environment, we use a predictable-size for font + // measurement tests. + !ui_web.debugEmulateFlutterTesterEnvironment) { // See subpixel rounding bug : // https://bugzilla.mozilla.org/show_bug.cgi?id=442139 // This causes bottom of letters such as 'y' to be cutoff and diff --git a/lib/web_ui/lib/src/engine/text/text_direction.dart b/lib/web_ui/lib/src/engine/text/text_direction.dart index 5fe0f3358825c..dcfeea3c026af 100644 --- a/lib/web_ui/lib/src/engine/text/text_direction.dart +++ b/lib/web_ui/lib/src/engine/text/text_direction.dart @@ -10,8 +10,10 @@ import 'unicode_range.dart'; enum FragmentFlow { /// The fragment flows from left to right regardless of its surroundings. ltr, + /// The fragment flows from right to left regardless of its surroundings. rtl, + /// The fragment flows the same as the previous fragment. /// /// If it's the first fragment in a line, then it flows the same as the @@ -19,6 +21,7 @@ enum FragmentFlow { /// /// E.g. digits. previous, + /// If the previous and next fragments flow in the same direction, then this /// fragment flows in that same direction. Otherwise, it flows the same as the /// paragraph direction. @@ -64,45 +67,43 @@ class BidiFragment extends TextFragment { // This data was taken from the source code of the Closure library: // // - https://github.com/google/closure-library/blob/9d24a6c1809a671c2e54c328897ebeae15a6d172/closure/goog/i18n/bidi.js#L203-L234 -final UnicodePropertyLookup _textDirectionLookup = UnicodePropertyLookup( - >[ - // LTR - const UnicodeRange(kChar_A, kChar_Z, ui.TextDirection.ltr), - const UnicodeRange(kChar_a, kChar_z, ui.TextDirection.ltr), - const UnicodeRange(0x00C0, 0x00D6, ui.TextDirection.ltr), - const UnicodeRange(0x00D8, 0x00F6, ui.TextDirection.ltr), - const UnicodeRange(0x00F8, 0x02B8, ui.TextDirection.ltr), - const UnicodeRange(0x0300, 0x0590, ui.TextDirection.ltr), - // RTL - const UnicodeRange(0x0591, 0x06EF, ui.TextDirection.rtl), - const UnicodeRange(0x06FA, 0x08FF, ui.TextDirection.rtl), - // LTR - const UnicodeRange(0x0900, 0x1FFF, ui.TextDirection.ltr), - const UnicodeRange(0x200E, 0x200E, ui.TextDirection.ltr), - // RTL - const UnicodeRange(0x200F, 0x200F, ui.TextDirection.rtl), - // LTR - const UnicodeRange(0x2C00, 0xD801, ui.TextDirection.ltr), - // RTL - const UnicodeRange(0xD802, 0xD803, ui.TextDirection.rtl), - // LTR - const UnicodeRange(0xD804, 0xD839, ui.TextDirection.ltr), - // RTL - const UnicodeRange(0xD83A, 0xD83B, ui.TextDirection.rtl), - // LTR - const UnicodeRange(0xD83C, 0xDBFF, ui.TextDirection.ltr), - const UnicodeRange(0xF900, 0xFB1C, ui.TextDirection.ltr), - // RTL - const UnicodeRange(0xFB1D, 0xFDFF, ui.TextDirection.rtl), - // LTR - const UnicodeRange(0xFE00, 0xFE6F, ui.TextDirection.ltr), - // RTL - const UnicodeRange(0xFE70, 0xFEFC, ui.TextDirection.rtl), - // LTR - const UnicodeRange(0xFEFD, 0xFFFF, ui.TextDirection.ltr), - ], - null, -); +final UnicodePropertyLookup _textDirectionLookup = + UnicodePropertyLookup(>[ + // LTR + const UnicodeRange(kChar_A, kChar_Z, ui.TextDirection.ltr), + const UnicodeRange(kChar_a, kChar_z, ui.TextDirection.ltr), + const UnicodeRange(0x00C0, 0x00D6, ui.TextDirection.ltr), + const UnicodeRange(0x00D8, 0x00F6, ui.TextDirection.ltr), + const UnicodeRange(0x00F8, 0x02B8, ui.TextDirection.ltr), + const UnicodeRange(0x0300, 0x0590, ui.TextDirection.ltr), + // RTL + const UnicodeRange(0x0591, 0x06EF, ui.TextDirection.rtl), + const UnicodeRange(0x06FA, 0x08FF, ui.TextDirection.rtl), + // LTR + const UnicodeRange(0x0900, 0x1FFF, ui.TextDirection.ltr), + const UnicodeRange(0x200E, 0x200E, ui.TextDirection.ltr), + // RTL + const UnicodeRange(0x200F, 0x200F, ui.TextDirection.rtl), + // LTR + const UnicodeRange(0x2C00, 0xD801, ui.TextDirection.ltr), + // RTL + const UnicodeRange(0xD802, 0xD803, ui.TextDirection.rtl), + // LTR + const UnicodeRange(0xD804, 0xD839, ui.TextDirection.ltr), + // RTL + const UnicodeRange(0xD83A, 0xD83B, ui.TextDirection.rtl), + // LTR + const UnicodeRange(0xD83C, 0xDBFF, ui.TextDirection.ltr), + const UnicodeRange(0xF900, 0xFB1C, ui.TextDirection.ltr), + // RTL + const UnicodeRange(0xFB1D, 0xFDFF, ui.TextDirection.rtl), + // LTR + const UnicodeRange(0xFE00, 0xFE6F, ui.TextDirection.ltr), + // RTL + const UnicodeRange(0xFE70, 0xFEFC, ui.TextDirection.rtl), + // LTR + const UnicodeRange(0xFEFD, 0xFFFF, ui.TextDirection.ltr), + ], null); List _computeBidiFragments(String text) { final List fragments = []; diff --git a/lib/web_ui/lib/src/engine/text/unicode_range.dart b/lib/web_ui/lib/src/engine/text/unicode_range.dart index d612efb80a322..93e7e40403801 100644 --- a/lib/web_ui/lib/src/engine/text/unicode_range.dart +++ b/lib/web_ui/lib/src/engine/text/unicode_range.dart @@ -12,11 +12,7 @@ const int kCharBang = 33; const int kMashriqi_0 = 0x660; const int kMashriqi_9 = kMashriqi_0 + 9; -enum _ComparisonResult { - inside, - higher, - lower, -} +enum _ComparisonResult { inside, higher, lower } /// Each instance of [UnicodeRange] represents a range of unicode characters /// that are assigned a [CharProperty]. For example, the following snippet: @@ -218,8 +214,7 @@ List> _unpackProperties

( i += 4; } final int charCode = packedData.codeUnitAt(i); - final P property = - propertyEnumValues[_getEnumIndexFromPackedValue(charCode)]; + final P property = propertyEnumValues[_getEnumIndexFromPackedValue(charCode)]; i++; ranges.add(UnicodeRange

(rangeStart, rangeEnd, property)); @@ -231,8 +226,9 @@ int _getEnumIndexFromPackedValue(int charCode) { // This has to stay in sync with [EnumValue.serialized] in // `tool/unicode_sync_script.dart`. - assert((charCode >= kChar_A && charCode <= kChar_Z) || - (charCode >= kChar_a && charCode <= kChar_z)); + assert( + (charCode >= kChar_A && charCode <= kChar_Z) || (charCode >= kChar_a && charCode <= kChar_z), + ); // Uppercase letters were assigned to the first 26 enum values. if (charCode <= kChar_Z) { @@ -261,8 +257,9 @@ int _consumeInt(String packedData, int index) { /// Does the same thing as [int.parse(str, 36)] but takes only a single /// character as a [charCode] integer. int getIntFromCharCode(int charCode) { - assert((charCode >= kChar_0 && charCode <= kChar_9) || - (charCode >= kChar_a && charCode <= kChar_z)); + assert( + (charCode >= kChar_0 && charCode <= kChar_9) || (charCode >= kChar_a && charCode <= kChar_z), + ); if (charCode <= kChar_9) { return charCode - kChar_0; diff --git a/lib/web_ui/lib/src/engine/text/word_break_properties.dart b/lib/web_ui/lib/src/engine/text/word_break_properties.dart index 4b1ed77b137e5..915210d7d5d46 100644 --- a/lib/web_ui/lib/src/engine/text/word_break_properties.dart +++ b/lib/web_ui/lib/src/engine/text/word_break_properties.dart @@ -8,11 +8,10 @@ import 'unicode_range.dart'; export 'package:web_unicode/web_unicode.dart' show WordCharProperty; - UnicodePropertyLookup wordLookup = UnicodePropertyLookup.fromPackedData( - packedWordBreakProperties, - singleWordBreakRangesCount, - WordCharProperty.values, - defaultWordCharProperty, -); + packedWordBreakProperties, + singleWordBreakRangesCount, + WordCharProperty.values, + defaultWordCharProperty, + ); diff --git a/lib/web_ui/lib/src/engine/text/word_breaker.dart b/lib/web_ui/lib/src/engine/text/word_breaker.dart index 55a395cbad545..d1dafbe37ae2f 100644 --- a/lib/web_ui/lib/src/engine/text/word_breaker.dart +++ b/lib/web_ui/lib/src/engine/text/word_breaker.dart @@ -25,11 +25,7 @@ abstract final class WordBreaker { static int prevBreakIndex(String text, int index) => _findBreakIndex(_FindBreakDirection.backward, text, index); - static int _findBreakIndex( - _FindBreakDirection direction, - String text, - int index, - ) { + static int _findBreakIndex(_FindBreakDirection direction, String text, int index) { int i = index; while (i >= 0 && i <= text.length) { i += direction.step; @@ -66,12 +62,7 @@ abstract final class WordBreaker { // Otherwise break before and after Newlines (including CR and LF) // WB3a: (Newline | CR | LF) ÷ - if (_oneOf( - immediateLeft, - WordCharProperty.Newline, - WordCharProperty.CR, - WordCharProperty.LF, - )) { + if (_oneOf(immediateLeft, WordCharProperty.Newline, WordCharProperty.CR, WordCharProperty.LF)) { return true; } @@ -207,8 +198,7 @@ abstract final class WordBreaker { // Do not break within sequences of digits, or digits adjacent to letters // (“3a”, or “A3”). // WB8: Numeric × Numeric - if (immediateLeft == WordCharProperty.Numeric && - immediateRight == WordCharProperty.Numeric) { + if (immediateLeft == WordCharProperty.Numeric && immediateRight == WordCharProperty.Numeric) { return false; } @@ -249,8 +239,7 @@ abstract final class WordBreaker { // Do not break between Katakana. // WB13: Katakana × Katakana - if (immediateLeft == WordCharProperty.Katakana && - immediateRight == WordCharProperty.Katakana) { + if (immediateLeft == WordCharProperty.Katakana && immediateRight == WordCharProperty.Katakana) { return false; } diff --git a/lib/web_ui/lib/src/engine/text_editing/autofill_hint.dart b/lib/web_ui/lib/src/engine/text_editing/autofill_hint.dart index 392998111c048..2581ec2a0340c 100644 --- a/lib/web_ui/lib/src/engine/text_editing/autofill_hint.dart +++ b/lib/web_ui/lib/src/engine/text_editing/autofill_hint.dart @@ -8,65 +8,64 @@ /// See: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete class BrowserAutofillHints { BrowserAutofillHints._() - : _flutterToEngineMap = { - 'birthday': 'bday', - 'birthdayDay': 'bday-day', - 'birthdayMonth': 'bday-month', - 'birthdayYear': 'bday-year', - 'countryCode': 'country', - 'countryName': 'country-name', - 'creditCardExpirationDate': 'cc-exp', - 'creditCardExpirationMonth': 'cc-exp-month', - 'creditCardExpirationYear': 'cc-exp-year', - 'creditCardFamilyName': 'cc-family-name', - 'creditCardGivenName': 'cc-given-name', - 'creditCardMiddleName': 'cc-additional-name', - 'creditCardName': 'cc-name', - 'creditCardNumber': 'cc-number', - 'creditCardSecurityCode': 'cc-csc', - 'creditCardType': 'cc-type', - 'email': 'email', - 'familyName': 'family-name', - 'fullStreetAddress': 'street-address', - 'gender': 'sex', - 'givenName': 'given-name', - 'impp': 'impp', - 'jobTitle': 'organization-title', - 'language': 'language', - 'middleName': 'additional-name', - 'name': 'name', - 'namePrefix': 'honorific-prefix', - 'nameSuffix': 'honorific-suffix', - 'newPassword': 'new-password', - 'nickname': 'nickname', - 'oneTimeCode': 'one-time-code', - 'organizationName': 'organization', - 'password': 'current-password', - 'photo': 'photo', - 'postalCode': 'postal-code', - 'streetAddressLevel1': 'address-level1', - 'streetAddressLevel2': 'address-level2', - 'streetAddressLevel3': 'address-level3', - 'streetAddressLevel4': 'address-level4', - 'streetAddressLine1': 'address-line1', - 'streetAddressLine2': 'address-line2', - 'streetAddressLine3': 'address-line3', - 'telephoneNumber': 'tel', - 'telephoneNumberAreaCode': 'tel-area-code', - 'telephoneNumberCountryCode': 'tel-country-code', - 'telephoneNumberExtension': 'tel-extension', - 'telephoneNumberLocal': 'tel-local', - 'telephoneNumberLocalPrefix': 'tel-local-prefix', - 'telephoneNumberLocalSuffix': 'tel-local-suffix', - 'telephoneNumberNational': 'tel-national', - 'transactionAmount': 'transaction-amount', - 'transactionCurrency': 'transaction-currency', - 'url': 'url', - 'username': 'username', - }; + : _flutterToEngineMap = { + 'birthday': 'bday', + 'birthdayDay': 'bday-day', + 'birthdayMonth': 'bday-month', + 'birthdayYear': 'bday-year', + 'countryCode': 'country', + 'countryName': 'country-name', + 'creditCardExpirationDate': 'cc-exp', + 'creditCardExpirationMonth': 'cc-exp-month', + 'creditCardExpirationYear': 'cc-exp-year', + 'creditCardFamilyName': 'cc-family-name', + 'creditCardGivenName': 'cc-given-name', + 'creditCardMiddleName': 'cc-additional-name', + 'creditCardName': 'cc-name', + 'creditCardNumber': 'cc-number', + 'creditCardSecurityCode': 'cc-csc', + 'creditCardType': 'cc-type', + 'email': 'email', + 'familyName': 'family-name', + 'fullStreetAddress': 'street-address', + 'gender': 'sex', + 'givenName': 'given-name', + 'impp': 'impp', + 'jobTitle': 'organization-title', + 'language': 'language', + 'middleName': 'additional-name', + 'name': 'name', + 'namePrefix': 'honorific-prefix', + 'nameSuffix': 'honorific-suffix', + 'newPassword': 'new-password', + 'nickname': 'nickname', + 'oneTimeCode': 'one-time-code', + 'organizationName': 'organization', + 'password': 'current-password', + 'photo': 'photo', + 'postalCode': 'postal-code', + 'streetAddressLevel1': 'address-level1', + 'streetAddressLevel2': 'address-level2', + 'streetAddressLevel3': 'address-level3', + 'streetAddressLevel4': 'address-level4', + 'streetAddressLine1': 'address-line1', + 'streetAddressLine2': 'address-line2', + 'streetAddressLine3': 'address-line3', + 'telephoneNumber': 'tel', + 'telephoneNumberAreaCode': 'tel-area-code', + 'telephoneNumberCountryCode': 'tel-country-code', + 'telephoneNumberExtension': 'tel-extension', + 'telephoneNumberLocal': 'tel-local', + 'telephoneNumberLocalPrefix': 'tel-local-prefix', + 'telephoneNumberLocalSuffix': 'tel-local-suffix', + 'telephoneNumberNational': 'tel-national', + 'transactionAmount': 'transaction-amount', + 'transactionCurrency': 'transaction-currency', + 'url': 'url', + 'username': 'username', + }; - static final BrowserAutofillHints _singletonInstance = - BrowserAutofillHints._(); + static final BrowserAutofillHints _singletonInstance = BrowserAutofillHints._(); /// The [BrowserAutofillHints] singleton. static BrowserAutofillHints get instance => _singletonInstance; diff --git a/lib/web_ui/lib/src/engine/text_editing/composition_aware_mixin.dart b/lib/web_ui/lib/src/engine/text_editing/composition_aware_mixin.dart index 71294e25d7930..d471fa763be3c 100644 --- a/lib/web_ui/lib/src/engine/text_editing/composition_aware_mixin.dart +++ b/lib/web_ui/lib/src/engine/text_editing/composition_aware_mixin.dart @@ -30,12 +30,15 @@ mixin CompositionAwareMixin { /// The name of the browser composition event type that triggers on ending a composition. static const String _kCompositionEnd = 'compositionend'; - late final DomEventListener _compositionStartListener = - createDomEventListener(_handleCompositionStart); - late final DomEventListener _compositionUpdateListener = - createDomEventListener(_handleCompositionUpdate); - late final DomEventListener _compositionEndListener = - createDomEventListener(_handleCompositionEnd); + late final DomEventListener _compositionStartListener = createDomEventListener( + _handleCompositionStart, + ); + late final DomEventListener _compositionUpdateListener = createDomEventListener( + _handleCompositionUpdate, + ); + late final DomEventListener _compositionEndListener = createDomEventListener( + _handleCompositionEnd, + ); /// The currently composing text in the `domElement`. /// diff --git a/lib/web_ui/lib/src/engine/text_editing/input_action.dart b/lib/web_ui/lib/src/engine/text_editing/input_action.dart index bc96927f0ecb8..59542cdcc72dd 100644 --- a/lib/web_ui/lib/src/engine/text_editing/input_action.dart +++ b/lib/web_ui/lib/src/engine/text_editing/input_action.dart @@ -65,7 +65,6 @@ abstract class EngineInputAction { /// Action to send static const SendInputAction send = SendInputAction(); - /// The HTML `enterkeyhint` attribute to be set on the DOM element. /// /// This HTML attribute helps the browser decide what kind of keyboard action diff --git a/lib/web_ui/lib/src/engine/text_editing/text_capitalization.dart b/lib/web_ui/lib/src/engine/text_editing/text_capitalization.dart index b230fcfaa4973..1de39a3f02599 100644 --- a/lib/web_ui/lib/src/engine/text_editing/text_capitalization.dart +++ b/lib/web_ui/lib/src/engine/text_editing/text_capitalization.dart @@ -34,17 +34,17 @@ enum TextCapitalization { /// https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autocapitalize class TextCapitalizationConfig { const TextCapitalizationConfig.defaultCapitalization() - : textCapitalization = TextCapitalization.none; + : textCapitalization = TextCapitalization.none; const TextCapitalizationConfig.fromInputConfiguration(String inputConfiguration) - : textCapitalization = - inputConfiguration == 'TextCapitalization.words' - ? TextCapitalization.words - : inputConfiguration == 'TextCapitalization.characters' - ? TextCapitalization.characters - : inputConfiguration == 'TextCapitalization.sentences' - ? TextCapitalization.sentences - : TextCapitalization.none; + : textCapitalization = + inputConfiguration == 'TextCapitalization.words' + ? TextCapitalization.words + : inputConfiguration == 'TextCapitalization.characters' + ? TextCapitalization.characters + : inputConfiguration == 'TextCapitalization.sentences' + ? TextCapitalization.sentences + : TextCapitalization.none; final TextCapitalization textCapitalization; @@ -81,8 +81,7 @@ class TextCapitalizationConfig { final DomHTMLInputElement element = domElement as DomHTMLInputElement; element.setAttribute('autocapitalize', autocapitalize); } else if (domInstanceOfString(domElement, 'HTMLTextAreaElement')) { - final DomHTMLTextAreaElement element = domElement as - DomHTMLTextAreaElement; + final DomHTMLTextAreaElement element = domElement as DomHTMLTextAreaElement; element.setAttribute('autocapitalize', autocapitalize); } } diff --git a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart index edba5f73afe7c..eb0cd4f66c7fa 100644 --- a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart +++ b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart @@ -196,6 +196,7 @@ class EngineAutofillForm { final Map? items; final DomHTMLElement? insertionReferenceNode; + /// Identifier for the form. /// /// It is constructed by concatenating unique ids of input elements on the @@ -255,13 +256,11 @@ class EngineAutofillForm { final List ids = List.empty(growable: true); // The focused text editing element will not be created here. - final AutofillInfo focusedElement = - AutofillInfo.fromFrameworkMessage(focusedElementAutofill); + final AutofillInfo focusedElement = AutofillInfo.fromFrameworkMessage(focusedElementAutofill); if (fields != null) { bool fieldIsFocusedElement = false; - for (final Map field in - fields.cast>()) { + for (final Map field in fields.cast>()) { final Map autofillInfo = field.readJson('autofill'); final AutofillInfo autofill = AutofillInfo.fromFrameworkMessage( autofillInfo, @@ -287,9 +286,11 @@ class EngineAutofillForm { // Thus, we have to make sure that the elements remain invisible to users, // but not to Safari for autofill to work. Since these elements are // sized and placed on the DOM, we also have to disable pointer events. - _styleAutofillElements(htmlElement, - shouldHideElement: !isSafariDesktopStrategy, - shouldDisablePointerEvents: isSafariDesktopStrategy); + _styleAutofillElements( + htmlElement, + shouldHideElement: !isSafariDesktopStrategy, + shouldDisablePointerEvents: isSafariDesktopStrategy, + ); items[autofill.uniqueIdentifier] = autofill; elements[autofill.uniqueIdentifier] = htmlElement; @@ -298,7 +299,7 @@ class EngineAutofillForm { // We want to track the node in the position directly after our focused // element, so we can later insert that element in the correct position // right before this node. - if(fieldIsFocusedElement){ + if (fieldIsFocusedElement) { insertionReferenceNode = htmlElement; fieldIsFocusedElement = false; } @@ -350,7 +351,7 @@ class EngineAutofillForm { elements: elements, items: items, formIdentifier: formIdentifier, - insertionReferenceNode: insertionReferenceNode + insertionReferenceNode: insertionReferenceNode, ); } @@ -359,7 +360,7 @@ class EngineAutofillForm { // we need to explicitly set pointer events on the active input element in // order to calculate the correct pointer event offsets. // See: https://github.com/flutter/flutter/issues/136006 - if(textEditing.strategy is SafariDesktopTextEditingStrategy) { + if (textEditing.strategy is SafariDesktopTextEditingStrategy) { mainTextEditingElement.style.pointerEvents = 'all'; } @@ -387,18 +388,17 @@ class EngineAutofillForm { final List subscriptions = []; void addSubscriptionForKey(String key) { - final DomElement element = elements![key]!; - subscriptions.add( - DomSubscription(element, 'input', - (DomEvent e) { - if (items![key] == null) { - throw StateError( - 'AutofillInfo must have a valid uniqueIdentifier.'); - } else { - final AutofillInfo autofillInfo = items![key]!; - handleChange(element, autofillInfo); - } - })); + final DomElement element = elements![key]!; + subscriptions.add( + DomSubscription(element, 'input', (DomEvent e) { + if (items![key] == null) { + throw StateError('AutofillInfo must have a valid uniqueIdentifier.'); + } else { + final AutofillInfo autofillInfo = items![key]!; + handleChange(element, autofillInfo); + } + }), + ); } keys.forEach(addSubscriptionForKey); @@ -406,8 +406,7 @@ class EngineAutofillForm { } void handleChange(DomElement domElement, AutofillInfo autofillInfo) { - final EditingState newEditingState = EditingState.fromDomElement( - domElement as DomHTMLElement); + final EditingState newEditingState = EditingState.fromDomElement(domElement as DomHTMLElement); _sendAutofillEditingState(autofillInfo.uniqueIdentifier, newEditingState); } @@ -417,13 +416,10 @@ class EngineAutofillForm { EnginePlatformDispatcher.instance.invokeOnPlatformMessage( 'flutter/textinput', const JSONMethodCodec().encodeMethodCall( - MethodCall( - 'TextInputClient.updateEditingStateWithTag', - [ - 0, - {tag: editingState.toFlutter()} - ], - ), + MethodCall('TextInputClient.updateEditingStateWithTag', [ + 0, + {tag: editingState.toFlutter()}, + ]), ), _emptyCallback, ); @@ -443,17 +439,22 @@ class AutofillInfo { this.placeholder, }); - factory AutofillInfo.fromFrameworkMessage(Map autofill, - {TextCapitalizationConfig textCapitalization = - const TextCapitalizationConfig.defaultCapitalization()}) { + factory AutofillInfo.fromFrameworkMessage( + Map autofill, { + TextCapitalizationConfig textCapitalization = + const TextCapitalizationConfig.defaultCapitalization(), + }) { final String uniqueIdentifier = autofill.readString('uniqueIdentifier'); final List? hintsList = autofill.tryList('hints'); - final String? firstHint = (hintsList == null || hintsList.isEmpty) ? null : hintsList.first as String; - final EditingState editingState = - EditingState.fromFrameworkMessage(autofill.readJson('editingValue')); + final String? firstHint = + (hintsList == null || hintsList.isEmpty) ? null : hintsList.first as String; + final EditingState editingState = EditingState.fromFrameworkMessage( + autofill.readJson('editingValue'), + ); return AutofillInfo( uniqueIdentifier: uniqueIdentifier, - autofillHint: (firstHint != null) ? BrowserAutofillHints.instance.flutterToEngine(firstHint) : null, + autofillHint: + (firstHint != null) ? BrowserAutofillHints.instance.flutterToEngine(firstHint) : null, editingState: editingState, placeholder: autofill.tryString('hintText'), textCapitalization: textCapitalization, @@ -498,8 +499,7 @@ class AutofillInfo { /// information is expected in this field. final String? placeholder; - void applyToDomElement(DomHTMLElement domElement, - {bool focusedElement = false}) { + void applyToDomElement(DomHTMLElement domElement, {bool focusedElement = false}) { final String? autofillHint = this.autofillHint; final String? placeholder = this.placeholder; if (domInstanceOfString(domElement, 'HTMLInputElement')) { @@ -537,9 +537,14 @@ String _replace(String originalText, String replacementText, ui.TextRange replac assert(replacedRange.isValid); assert(replacedRange.start <= originalText.length && replacedRange.end <= originalText.length); - final ui.TextRange normalizedRange = ui.TextRange(start: math.min(replacedRange.start, replacedRange.end), end: math.max(replacedRange.start, replacedRange.end)); + final ui.TextRange normalizedRange = ui.TextRange( + start: math.min(replacedRange.start, replacedRange.end), + end: math.max(replacedRange.start, replacedRange.end), + ); - return normalizedRange.textBefore(originalText) + replacementText + normalizedRange.textAfter(originalText); + return normalizedRange.textBefore(originalText) + + replacementText + + normalizedRange.textAfter(originalText); } /// The change between the last editing state and the current editing state @@ -579,11 +584,18 @@ class TextEditingDeltaState { /// We then verify that the delta we collected results in the text contained within the new editing state /// when applied to the last editing state. If it is not then we use our new editing state as the source of truth, /// and use regex to find the correct [deltaStart] and [deltaEnd]. - static TextEditingDeltaState inferDeltaState(EditingState newEditingState, EditingState? lastEditingState, TextEditingDeltaState lastTextEditingDeltaState) { + static TextEditingDeltaState inferDeltaState( + EditingState newEditingState, + EditingState? lastEditingState, + TextEditingDeltaState lastTextEditingDeltaState, + ) { final TextEditingDeltaState newTextEditingDeltaState = lastTextEditingDeltaState.copyWith(); - final bool previousSelectionWasCollapsed = lastEditingState?.baseOffset == lastEditingState?.extentOffset; - final bool isTextBeingRemoved = newTextEditingDeltaState.deltaText.isEmpty && newTextEditingDeltaState.deltaEnd != -1; - final bool isTextBeingChangedAtActiveSelection = newTextEditingDeltaState.deltaText.isNotEmpty && !previousSelectionWasCollapsed; + final bool previousSelectionWasCollapsed = + lastEditingState?.baseOffset == lastEditingState?.extentOffset; + final bool isTextBeingRemoved = + newTextEditingDeltaState.deltaText.isEmpty && newTextEditingDeltaState.deltaEnd != -1; + final bool isTextBeingChangedAtActiveSelection = + newTextEditingDeltaState.deltaText.isNotEmpty && !previousSelectionWasCollapsed; if (isTextBeingRemoved) { // When text is deleted outside of the composing region or is cut using the native toolbar, @@ -592,7 +604,8 @@ class TextEditingDeltaState { // that we set when beforeinput was fired to determine the [deltaStart]. // If the deletion is forward, [deltaStart] is set to the new editing state baseOffset // and [deltaEnd] is set to [deltaStart] incremented by the length of the deletion. - final int deletedLength = newTextEditingDeltaState.oldText.length - newEditingState.text!.length; + final int deletedLength = + newTextEditingDeltaState.oldText.length - newEditingState.text!.length; final bool backwardDeletion = newEditingState.baseOffset != lastEditingState?.baseOffset; if (backwardDeletion) { newTextEditingDeltaState.deltaStart = newTextEditingDeltaState.deltaEnd - deletedLength; @@ -602,20 +615,30 @@ class TextEditingDeltaState { newTextEditingDeltaState.deltaEnd = newTextEditingDeltaState.deltaStart + deletedLength; } } else if (isTextBeingChangedAtActiveSelection) { - final bool isPreviousSelectionInverted = lastEditingState!.baseOffset! > lastEditingState.extentOffset!; + final bool isPreviousSelectionInverted = + lastEditingState!.baseOffset! > lastEditingState.extentOffset!; // When a selection of text is replaced by a copy/paste operation we set the starting range // of the delta to be the beginning of the selection of the previous editing state. - newTextEditingDeltaState.deltaStart = isPreviousSelectionInverted ? lastEditingState.extentOffset! : lastEditingState.baseOffset!; + newTextEditingDeltaState.deltaStart = + isPreviousSelectionInverted + ? lastEditingState.extentOffset! + : lastEditingState.baseOffset!; } // If we are composing then set the delta range to the composing region we // captured in compositionupdate. - final bool isCurrentlyComposing = newTextEditingDeltaState.composingOffset != null && newTextEditingDeltaState.composingOffset != newTextEditingDeltaState.composingExtent; - if (newTextEditingDeltaState.deltaText.isNotEmpty && previousSelectionWasCollapsed && isCurrentlyComposing) { + final bool isCurrentlyComposing = + newTextEditingDeltaState.composingOffset != null && + newTextEditingDeltaState.composingOffset != newTextEditingDeltaState.composingExtent; + if (newTextEditingDeltaState.deltaText.isNotEmpty && + previousSelectionWasCollapsed && + isCurrentlyComposing) { newTextEditingDeltaState.deltaStart = newTextEditingDeltaState.composingOffset!; } - final bool isDeltaRangeEmpty = newTextEditingDeltaState.deltaStart == -1 && newTextEditingDeltaState.deltaStart == newTextEditingDeltaState.deltaEnd; + final bool isDeltaRangeEmpty = + newTextEditingDeltaState.deltaStart == -1 && + newTextEditingDeltaState.deltaStart == newTextEditingDeltaState.deltaEnd; if (!isDeltaRangeEmpty) { // To verify the range of our delta we should compare the newEditingState's // text with the delta applied to the oldText. If they differ then capture @@ -629,10 +652,15 @@ class TextEditingDeltaState { // This verification is needed for cases such as the insertion of a period // after a double space, and the insertion of an accented character through // a native composing menu. - final ui.TextRange replacementRange = ui.TextRange(start: newTextEditingDeltaState.deltaStart, end: newTextEditingDeltaState.deltaEnd); + final ui.TextRange replacementRange = ui.TextRange( + start: newTextEditingDeltaState.deltaStart, + end: newTextEditingDeltaState.deltaEnd, + ); final String textAfterDelta = _replace( - newTextEditingDeltaState.oldText, newTextEditingDeltaState.deltaText, - replacementRange); + newTextEditingDeltaState.oldText, + newTextEditingDeltaState.deltaText, + replacementRange, + ); final bool isDeltaVerified = textAfterDelta == newEditingState.text!; if (!isDeltaVerified) { @@ -644,26 +672,21 @@ class TextEditingDeltaState { for (final Match match in deltaTextPattern.allMatches(newEditingState.text!)) { String textAfterMatch; int actualEnd; - final bool isMatchWithinOldTextBounds = match.start >= 0 && match.end <= newTextEditingDeltaState.oldText.length; + final bool isMatchWithinOldTextBounds = + match.start >= 0 && match.end <= newTextEditingDeltaState.oldText.length; if (!isMatchWithinOldTextBounds) { actualEnd = match.start + newTextEditingDeltaState.deltaText.length - 1; textAfterMatch = _replace( newTextEditingDeltaState.oldText, newTextEditingDeltaState.deltaText, - ui.TextRange( - start: match.start, - end: actualEnd, - ), + ui.TextRange(start: match.start, end: actualEnd), ); } else { - actualEnd = actualEnd = isPeriodInsertion? match.end - 1 : match.end; + actualEnd = actualEnd = isPeriodInsertion ? match.end - 1 : match.end; textAfterMatch = _replace( newTextEditingDeltaState.oldText, newTextEditingDeltaState.deltaText, - ui.TextRange( - start: match.start, - end: actualEnd, - ), + ui.TextRange(start: match.start, end: actualEnd), ); } @@ -723,7 +746,7 @@ class TextEditingDeltaState { 'selectionBase': baseOffset, 'selectionExtent': extentOffset, 'composingBase': composingOffset, - 'composingExtent': composingExtent + 'composingExtent': composingExtent, }, ], }; @@ -754,16 +777,15 @@ class TextEditingDeltaState { /// The current text and selection state of a text field. class EditingState { EditingState({ - this.text, - int? baseOffset, - int? extentOffset, - this.composingBaseOffset = -1, - this.composingExtentOffset = -1 - }) : - // Don't allow negative numbers. - baseOffset = math.max(0, baseOffset ?? 0), - // Don't allow negative numbers. - extentOffset = math.max(0, extentOffset ?? 0); + this.text, + int? baseOffset, + int? extentOffset, + this.composingBaseOffset = -1, + this.composingExtentOffset = -1, + }) : // Don't allow negative numbers. + baseOffset = math.max(0, baseOffset ?? 0), + // Don't allow negative numbers. + extentOffset = math.max(0, extentOffset ?? 0); /// Creates an [EditingState] instance using values from an editing state Map /// coming from Flutter. @@ -784,8 +806,7 @@ class EditingState { /// Flutter Framework can send the [selectionBase] and [selectionExtent] as /// -1, if so 0 assigned to the [baseOffset] and [extentOffset]. -1 is not a /// valid selection range for input DOM elements. - factory EditingState.fromFrameworkMessage( - Map flutterEditingState) { + factory EditingState.fromFrameworkMessage(Map flutterEditingState) { final String? text = flutterEditingState.tryString('text'); final int selectionBase = flutterEditingState.readInt('selectionBase'); @@ -799,7 +820,7 @@ class EditingState { baseOffset: selectionBase, extentOffset: selectionExtent, composingBaseOffset: composingBase ?? -1, - composingExtentOffset: composingExtent ?? -1 + composingExtentOffset: composingExtent ?? -1, ); } @@ -813,28 +834,31 @@ class EditingState { final DomHTMLInputElement element = domElement! as DomHTMLInputElement; if (element.selectionDirection == 'backward') { return EditingState( - text: element.value, - baseOffset: element.selectionEnd?.toInt(), - extentOffset: element.selectionStart?.toInt()); + text: element.value, + baseOffset: element.selectionEnd?.toInt(), + extentOffset: element.selectionStart?.toInt(), + ); } else { return EditingState( - text: element.value, - baseOffset: element.selectionStart?.toInt(), - extentOffset: element.selectionEnd?.toInt()); - } + text: element.value, + baseOffset: element.selectionStart?.toInt(), + extentOffset: element.selectionEnd?.toInt(), + ); + } } else if (domInstanceOfString(domElement, 'HTMLTextAreaElement')) { - final DomHTMLTextAreaElement element = domElement! as - DomHTMLTextAreaElement; + final DomHTMLTextAreaElement element = domElement! as DomHTMLTextAreaElement; if (element.selectionDirection == 'backward') { return EditingState( - text: element.value, - baseOffset: element.selectionEnd?.toInt(), - extentOffset: element.selectionStart?.toInt()); + text: element.value, + baseOffset: element.selectionEnd?.toInt(), + extentOffset: element.selectionStart?.toInt(), + ); } else { return EditingState( - text: element.value, - baseOffset: element.selectionStart?.toInt(), - extentOffset: element.selectionEnd?.toInt()); + text: element.value, + baseOffset: element.selectionStart?.toInt(), + extentOffset: element.selectionEnd?.toInt(), + ); } } else { throw UnsupportedError('Initialized with unsupported input type'); @@ -846,32 +870,32 @@ class EditingState { // Pick the greatest selection index for extent. int get maxOffset => math.max(baseOffset ?? 0, extentOffset ?? 0); - EditingState copyWith({ - String? text, - int? baseOffset, - int? extentOffset, - int? composingBaseOffset, - int? composingExtentOffset, - }) { - return EditingState( - text: text ?? this.text, - baseOffset: baseOffset ?? this.baseOffset, - extentOffset: extentOffset ?? this.extentOffset, - composingBaseOffset: composingBaseOffset ?? this.composingBaseOffset, - composingExtentOffset: composingExtentOffset ?? this.composingExtentOffset, - ); - } + EditingState copyWith({ + String? text, + int? baseOffset, + int? extentOffset, + int? composingBaseOffset, + int? composingExtentOffset, + }) { + return EditingState( + text: text ?? this.text, + baseOffset: baseOffset ?? this.baseOffset, + extentOffset: extentOffset ?? this.extentOffset, + composingBaseOffset: composingBaseOffset ?? this.composingBaseOffset, + composingExtentOffset: composingExtentOffset ?? this.composingExtentOffset, + ); + } /// The counterpart of [EditingState.fromFrameworkMessage]. It generates a Map that /// can be sent to Flutter. // TODO(mdebbar): Should we get `selectionAffinity` and other properties from flutter's editing state? Map toFlutter() => { - 'text': text, - 'selectionBase': baseOffset, - 'selectionExtent': extentOffset, - 'composingBase': composingBaseOffset, - 'composingExtent': composingExtentOffset, - }; + 'text': text, + 'selectionBase': baseOffset, + 'selectionExtent': extentOffset, + 'composingBase': composingBaseOffset, + 'composingExtent': composingExtentOffset, + }; /// The current text being edited. final String? text; @@ -892,9 +916,8 @@ class EditingState { bool get isValid => baseOffset! >= 0 && extentOffset! >= 0; @override - int get hashCode => Object.hash( - text, baseOffset, extentOffset, composingBaseOffset, composingExtentOffset - ); + int get hashCode => + Object.hash(text, baseOffset, extentOffset, composingBaseOffset, composingExtentOffset); @override bool operator ==(Object other) { @@ -916,7 +939,8 @@ class EditingState { String toString() { String result = super.toString(); assert(() { - result = 'EditingState("$text", base:$baseOffset, extent:$extentOffset, composingBase:$composingBaseOffset, composingExtent:$composingExtentOffset)'; + result = + 'EditingState("$text", base:$baseOffset, extent:$extentOffset, composingBase:$composingBaseOffset, composingExtent:$composingExtentOffset)'; return true; }()); return result; @@ -940,12 +964,13 @@ class EditingState { element.value = text; element.setSelectionRange(minOffset, maxOffset); } else if (domInstanceOfString(domElement, 'HTMLTextAreaElement')) { - final DomHTMLTextAreaElement element = domElement! as - DomHTMLTextAreaElement; + final DomHTMLTextAreaElement element = domElement! as DomHTMLTextAreaElement; element.value = text; element.setSelectionRange(minOffset, maxOffset); } else { - throw UnsupportedError('Unsupported DOM element type: <${domElement?.tagName}> (${domElement.runtimeType})'); + throw UnsupportedError( + 'Unsupported DOM element type: <${domElement?.tagName}> (${domElement.runtimeType})', + ); } } @@ -961,8 +986,7 @@ class EditingState { final DomHTMLInputElement element = domElement! as DomHTMLInputElement; element.value = text; } else if (domInstanceOfString(domElement, 'HTMLTextAreaElement')) { - final DomHTMLTextAreaElement element = domElement! as - DomHTMLTextAreaElement; + final DomHTMLTextAreaElement element = domElement! as DomHTMLTextAreaElement; element.value = text; } else { throw UnsupportedError('Unsupported DOM element type'); @@ -984,39 +1008,37 @@ class InputConfiguration { this.obscureText = false, this.readOnly = false, this.autocorrect = true, - this.textCapitalization = - const TextCapitalizationConfig.defaultCapitalization(), + this.textCapitalization = const TextCapitalizationConfig.defaultCapitalization(), this.autofill, this.autofillGroup, this.enableDeltaModel = false, }); - InputConfiguration.fromFrameworkMessage( - Map flutterInputConfiguration) - : viewId = flutterInputConfiguration.tryInt('viewId') ?? kImplicitViewId, - inputType = EngineInputType.fromName( - flutterInputConfiguration.readJson('inputType').readString('name'), - isDecimal: flutterInputConfiguration.readJson('inputType').tryBool('decimal') ?? false, - isMultiline: flutterInputConfiguration.readJson('inputType').tryBool('isMultiline') ?? false, - ), - inputAction = - flutterInputConfiguration.tryString('inputAction') ?? 'TextInputAction.done', - obscureText = flutterInputConfiguration.tryBool('obscureText') ?? false, - readOnly = flutterInputConfiguration.tryBool('readOnly') ?? false, - autocorrect = flutterInputConfiguration.tryBool('autocorrect') ?? true, - textCapitalization = TextCapitalizationConfig.fromInputConfiguration( - flutterInputConfiguration.readString('textCapitalization'), - ), - autofill = flutterInputConfiguration.containsKey('autofill') - ? AutofillInfo.fromFrameworkMessage( - flutterInputConfiguration.readJson('autofill')) - : null, - autofillGroup = EngineAutofillForm.fromFrameworkMessage( - flutterInputConfiguration.tryInt('viewId') ?? kImplicitViewId, - flutterInputConfiguration.tryJson('autofill'), - flutterInputConfiguration.tryList('fields'), - ), - enableDeltaModel = flutterInputConfiguration.tryBool('enableDeltaModel') ?? false; + InputConfiguration.fromFrameworkMessage(Map flutterInputConfiguration) + : viewId = flutterInputConfiguration.tryInt('viewId') ?? kImplicitViewId, + inputType = EngineInputType.fromName( + flutterInputConfiguration.readJson('inputType').readString('name'), + isDecimal: flutterInputConfiguration.readJson('inputType').tryBool('decimal') ?? false, + isMultiline: + flutterInputConfiguration.readJson('inputType').tryBool('isMultiline') ?? false, + ), + inputAction = flutterInputConfiguration.tryString('inputAction') ?? 'TextInputAction.done', + obscureText = flutterInputConfiguration.tryBool('obscureText') ?? false, + readOnly = flutterInputConfiguration.tryBool('readOnly') ?? false, + autocorrect = flutterInputConfiguration.tryBool('autocorrect') ?? true, + textCapitalization = TextCapitalizationConfig.fromInputConfiguration( + flutterInputConfiguration.readString('textCapitalization'), + ), + autofill = + flutterInputConfiguration.containsKey('autofill') + ? AutofillInfo.fromFrameworkMessage(flutterInputConfiguration.readJson('autofill')) + : null, + autofillGroup = EngineAutofillForm.fromFrameworkMessage( + flutterInputConfiguration.tryInt('viewId') ?? kImplicitViewId, + flutterInputConfiguration.tryJson('autofill'), + flutterInputConfiguration.tryList('fields'), + ), + enableDeltaModel = flutterInputConfiguration.tryBool('enableDeltaModel') ?? false; /// The ID of the view that contains the text field. final int viewId; @@ -1053,7 +1075,8 @@ class InputConfiguration { final TextCapitalizationConfig textCapitalization; } -typedef OnChangeCallback = void Function(EditingState? editingState, TextEditingDeltaState? editingDeltaState); +typedef OnChangeCallback = + void Function(EditingState? editingState, TextEditingDeltaState? editingDeltaState); typedef OnActionCallback = void Function(String? inputAction); /// Provides HTML DOM functionality for editable text. @@ -1205,7 +1228,9 @@ class SafariDesktopTextEditingStrategy extends DefaultTextEditingStrategy { /// /// Unless a formfactor/browser requires specific implementation for a specific /// strategy the methods in this class should be used. -abstract class DefaultTextEditingStrategy with CompositionAwareMixin implements TextEditingStrategy { +abstract class DefaultTextEditingStrategy + with CompositionAwareMixin + implements TextEditingStrategy { DefaultTextEditingStrategy(this.owner); final HybridTextEditing owner; @@ -1231,7 +1256,7 @@ abstract class DefaultTextEditingStrategy with CompositionAwareMixin implements EngineFlutterView? get activeDomElementView => _viewForElement(activeDomElement); EngineFlutterView? _viewForElement(DomElement element) => - EnginePlatformDispatcher.instance.viewManager.findViewForElement(element); + EnginePlatformDispatcher.instance.viewManager.findViewForElement(element); late InputConfiguration inputConfiguration; EditingState? lastEditingState; @@ -1259,8 +1284,7 @@ abstract class DefaultTextEditingStrategy with CompositionAwareMixin implements bool get appendedToForm => _appendedToForm; bool _appendedToForm = false; - DomHTMLFormElement? get focusedFormElement => - inputConfiguration.autofillGroup?.formElement; + DomHTMLFormElement? get focusedFormElement => inputConfiguration.autofillGroup?.formElement; @override void initializeTextEditing( @@ -1335,22 +1359,17 @@ abstract class DefaultTextEditingStrategy with CompositionAwareMixin implements @override void addEventHandlers() { if (inputConfiguration.autofillGroup != null) { - subscriptions - .addAll(inputConfiguration.autofillGroup!.addInputEventListeners()); + subscriptions.addAll(inputConfiguration.autofillGroup!.addInputEventListeners()); } // Subscribe to text and selection changes. - subscriptions.add(DomSubscription(activeDomElement, 'input', - handleChange)); + subscriptions.add(DomSubscription(activeDomElement, 'input', handleChange)); - subscriptions.add(DomSubscription(activeDomElement, 'keydown', - maybeSendAction)); + subscriptions.add(DomSubscription(activeDomElement, 'keydown', maybeSendAction)); - subscriptions.add(DomSubscription(domDocument, 'selectionchange', - handleChange)); + subscriptions.add(DomSubscription(domDocument, 'selectionchange', handleChange)); - subscriptions.add(DomSubscription(activeDomElement, 'beforeinput', - handleBeforeInput)); + subscriptions.add(DomSubscription(activeDomElement, 'beforeinput', handleBeforeInput)); if (this is! SafariDesktopTextEditingStrategy) { // handleBlur causes Safari to reopen autofill dialogs after autofill, @@ -1407,14 +1426,13 @@ abstract class DefaultTextEditingStrategy with CompositionAwareMixin implements // If focused element is a part of a form, it needs to stay on the DOM // until the autofill context of the form is finalized. // More details on `TextInput.finishAutofillContext` call. - if (_appendedToForm && - inputConfiguration.autofillGroup?.formElement != null) { + if (_appendedToForm && inputConfiguration.autofillGroup?.formElement != null) { _styleAutofillElements(activeDomElement, isOffScreen: true); inputConfiguration.autofillGroup?.storeForm(); EnginePlatformDispatcher.instance.viewManager.safeBlur(activeDomElement); } else { EnginePlatformDispatcher.instance.viewManager.safeRemove(activeDomElement); - } + } domElement = null; } @@ -1446,7 +1464,11 @@ abstract class DefaultTextEditingStrategy with CompositionAwareMixin implements if (inputConfiguration.enableDeltaModel) { editingDeltaState.composingOffset = newEditingState.composingBaseOffset; editingDeltaState.composingExtent = newEditingState.composingExtentOffset; - newTextEditingDeltaState = TextEditingDeltaState.inferDeltaState(newEditingState, lastEditingState, editingDeltaState); + newTextEditingDeltaState = TextEditingDeltaState.inferDeltaState( + newEditingState, + lastEditingState, + editingDeltaState, + ); } if (newEditingState != lastEditingState) { @@ -1471,14 +1493,16 @@ abstract class DefaultTextEditingStrategy with CompositionAwareMixin implements final String? inputType = getJsProperty(event, 'inputType') as String?; if (inputType != null) { - final bool isSelectionInverted = lastEditingState!.baseOffset! > lastEditingState!.extentOffset!; - final int deltaOffset = isSelectionInverted ? lastEditingState!.baseOffset! : lastEditingState!.extentOffset!; + final bool isSelectionInverted = + lastEditingState!.baseOffset! > lastEditingState!.extentOffset!; + final int deltaOffset = + isSelectionInverted ? lastEditingState!.baseOffset! : lastEditingState!.extentOffset!; if (inputType.contains('delete')) { // The deltaStart is set in handleChange because there is where we get access // to the new selection baseOffset which is our new deltaStart. editingDeltaState.deltaText = ''; editingDeltaState.deltaEnd = deltaOffset; - } else if (inputType == 'insertLineBreak'){ + } else if (inputType == 'insertLineBreak') { // event.data is null on a line break, so we manually set deltaText as a line break by setting it to '\n'. editingDeltaState.deltaText = '\n'; editingDeltaState.deltaStart = deltaOffset; @@ -1498,7 +1522,8 @@ abstract class DefaultTextEditingStrategy with CompositionAwareMixin implements event as DomFocusEvent; final DomElement? willGainFocusElement = event.relatedTarget as DomElement?; - if (willGainFocusElement == null || _viewForElement(willGainFocusElement) == activeDomElementView) { + if (willGainFocusElement == null || + _viewForElement(willGainFocusElement) == activeDomElementView) { moveFocusToActiveDomElement(); } } @@ -1508,8 +1533,9 @@ abstract class DefaultTextEditingStrategy with CompositionAwareMixin implements final DomKeyboardEvent event = e as DomKeyboardEvent; if (event.keyCode == _kReturnKeyCode) { onAction!(inputConfiguration.inputAction); - if (inputConfiguration.inputType is MultilineInputType && inputConfiguration.inputAction == 'TextInputAction.newline' ) { - return; + if (inputConfiguration.inputType is MultilineInputType && + inputConfiguration.inputAction == 'TextInputAction.newline') { + return; } // Prevent the browser from inserting a new line. event.preventDefault(); @@ -1554,19 +1580,22 @@ abstract class DefaultTextEditingStrategy with CompositionAwareMixin implements /// see: https://bugs.chromium.org/p/chromium/issues/detail?id=119216#c11. void preventDefaultForMouseEvents() { subscriptions.add( - DomSubscription(activeDomElement, 'mousedown', (DomEvent event) { - event.preventDefault(); - })); + DomSubscription(activeDomElement, 'mousedown', (DomEvent event) { + event.preventDefault(); + }), + ); subscriptions.add( - DomSubscription(activeDomElement, 'mouseup', (DomEvent event) { - event.preventDefault(); - })); + DomSubscription(activeDomElement, 'mouseup', (DomEvent event) { + event.preventDefault(); + }), + ); subscriptions.add( - DomSubscription(activeDomElement, 'mousemove', (DomEvent event) { - event.preventDefault(); - })); + DomSubscription(activeDomElement, 'mousemove', (DomEvent event) { + event.preventDefault(); + }), + ); } /// Moves the focus to the [activeDomElement]. @@ -1625,8 +1654,7 @@ class IOSTextEditingStrategy extends GloballyPositionedTextEditingStrategy { required OnChangeCallback onChange, required OnActionCallback onAction, }) { - super.initializeTextEditing(inputConfig, - onChange: onChange, onAction: onAction); + super.initializeTextEditing(inputConfig, onChange: onChange, onAction: onAction); inputConfig.inputType.configureInputMode(activeDomElement); if (hasAutofillGroup) { placeForm(); @@ -1647,34 +1675,29 @@ class IOSTextEditingStrategy extends GloballyPositionedTextEditingStrategy { @override void addEventHandlers() { if (inputConfiguration.autofillGroup != null) { - subscriptions - .addAll(inputConfiguration.autofillGroup!.addInputEventListeners()); + subscriptions.addAll(inputConfiguration.autofillGroup!.addInputEventListeners()); } // Subscribe to text and selection changes. - subscriptions.add(DomSubscription(activeDomElement, 'input', - handleChange)); + subscriptions.add(DomSubscription(activeDomElement, 'input', handleChange)); - subscriptions.add(DomSubscription(activeDomElement, 'keydown', - maybeSendAction)); + subscriptions.add(DomSubscription(activeDomElement, 'keydown', maybeSendAction)); - subscriptions.add(DomSubscription(domDocument, 'selectionchange', - handleChange)); + subscriptions.add(DomSubscription(domDocument, 'selectionchange', handleChange)); - subscriptions.add(DomSubscription(activeDomElement, 'beforeinput', - handleBeforeInput)); + subscriptions.add(DomSubscription(activeDomElement, 'beforeinput', handleBeforeInput)); - subscriptions.add(DomSubscription(activeDomElement, 'blur', - handleBlur)); + subscriptions.add(DomSubscription(activeDomElement, 'blur', handleBlur)); addCompositionEventHandlers(activeDomElement); // Position the DOM element after it is focused. - subscriptions.add(DomSubscription(activeDomElement, 'focus', - (_) { - // Cancel previous timer if exists. - _schedulePlacement(); - })); + subscriptions.add( + DomSubscription(activeDomElement, 'focus', (_) { + // Cancel previous timer if exists. + _schedulePlacement(); + }), + ); _addTapListener(); } @@ -1712,18 +1735,20 @@ class IOSTextEditingStrategy extends GloballyPositionedTextEditingStrategy { /// [_positionInputElementTimer] timer is restarted. The element will be /// placed to its correct position after [_delayBeforePlacement]. void _addTapListener() { - subscriptions.add(DomSubscription(activeDomElement, 'click', (_) { - // Check if the element is already positioned. If not this does not fall - // under `The user was using the long press, now they want to enter text - // via keyboard` journey. - if (_canPosition) { - // Re-place the element somewhere outside of the screen. - initializeElementPlacement(); - - // Re-configure the timer to place the element. - _schedulePlacement(); - } - })); + subscriptions.add( + DomSubscription(activeDomElement, 'click', (_) { + // Check if the element is already positioned. If not this does not fall + // under `The user was using the long press, now they want to enter text + // via keyboard` journey. + if (_canPosition) { + // Re-place the element somewhere outside of the screen. + initializeElementPlacement(); + + // Re-configure the timer to place the element. + _schedulePlacement(); + } + }), + ); } void _schedulePlacement() { @@ -1757,42 +1782,32 @@ class AndroidTextEditingStrategy extends GloballyPositionedTextEditingStrategy { required OnChangeCallback onChange, required OnActionCallback onAction, }) { - super.initializeTextEditing(inputConfig, - onChange: onChange, onAction: onAction); + super.initializeTextEditing(inputConfig, onChange: onChange, onAction: onAction); inputConfig.inputType.configureInputMode(activeDomElement); if (hasAutofillGroup) { placeForm(); } else { _insertEditingElementInView(activeDomElement, inputConfig.viewId); } - inputConfig.textCapitalization.setAutocapitalizeAttribute( - activeDomElement); + inputConfig.textCapitalization.setAutocapitalizeAttribute(activeDomElement); } @override void addEventHandlers() { if (inputConfiguration.autofillGroup != null) { - subscriptions - .addAll(inputConfiguration.autofillGroup!.addInputEventListeners()); + subscriptions.addAll(inputConfiguration.autofillGroup!.addInputEventListeners()); } // Subscribe to text and selection changes. - subscriptions.add( - DomSubscription(activeDomElement, 'input', handleChange)); + subscriptions.add(DomSubscription(activeDomElement, 'input', handleChange)); - subscriptions.add( - DomSubscription(activeDomElement, 'keydown', - maybeSendAction)); + subscriptions.add(DomSubscription(activeDomElement, 'keydown', maybeSendAction)); - subscriptions.add( - DomSubscription(domDocument, 'selectionchange', - handleChange)); + subscriptions.add(DomSubscription(domDocument, 'selectionchange', handleChange)); - subscriptions.add(DomSubscription(activeDomElement, 'beforeinput', - handleBeforeInput)); + subscriptions.add(DomSubscription(activeDomElement, 'beforeinput', handleBeforeInput)); - subscriptions.add(DomSubscription(activeDomElement, 'blur', - handleBlur)); + subscriptions.add(DomSubscription(activeDomElement, 'blur', handleBlur)); addCompositionEventHandlers(activeDomElement); @@ -1819,8 +1834,7 @@ class FirefoxTextEditingStrategy extends GloballyPositionedTextEditingStrategy { required OnChangeCallback onChange, required OnActionCallback onAction, }) { - super.initializeTextEditing(inputConfig, - onChange: onChange, onAction: onAction); + super.initializeTextEditing(inputConfig, onChange: onChange, onAction: onAction); if (hasAutofillGroup) { placeForm(); } @@ -1829,21 +1843,15 @@ class FirefoxTextEditingStrategy extends GloballyPositionedTextEditingStrategy { @override void addEventHandlers() { if (inputConfiguration.autofillGroup != null) { - subscriptions - .addAll(inputConfiguration.autofillGroup!.addInputEventListeners()); + subscriptions.addAll(inputConfiguration.autofillGroup!.addInputEventListeners()); } // Subscribe to text and selection changes. - subscriptions.add( - DomSubscription(activeDomElement, 'input', handleChange)); + subscriptions.add(DomSubscription(activeDomElement, 'input', handleChange)); - subscriptions.add( - DomSubscription( - activeDomElement, 'keydown', maybeSendAction)); + subscriptions.add(DomSubscription(activeDomElement, 'keydown', maybeSendAction)); - subscriptions.add( - DomSubscription( - activeDomElement, 'beforeinput', handleBeforeInput)); + subscriptions.add(DomSubscription(activeDomElement, 'beforeinput', handleBeforeInput)); addCompositionEventHandlers(activeDomElement); @@ -1861,22 +1869,17 @@ class FirefoxTextEditingStrategy extends GloballyPositionedTextEditingStrategy { // After each keyup, the start/end values of the selection is compared to // the previously saved editing state. subscriptions.add( - DomSubscription( - activeDomElement, - 'keyup', - (DomEvent event) { - handleChange(event); - })); + DomSubscription(activeDomElement, 'keyup', (DomEvent event) { + handleChange(event); + }), + ); // In Firefox the context menu item "Select All" does not work without // listening to onSelect. On the other browsers onSelectionChange is // enough for covering "Select All" functionality. - subscriptions.add( - DomSubscription( - activeDomElement, 'select', handleChange)); + subscriptions.add(DomSubscription(activeDomElement, 'select', handleChange)); - subscriptions.add(DomSubscription(activeDomElement, 'blur', - handleBlur)); + subscriptions.add(DomSubscription(activeDomElement, 'blur', handleBlur)); preventDefaultForMouseEvents(); } @@ -1904,10 +1907,7 @@ abstract class TextInputCommand { /// Responds to the 'TextInput.setClient' message. class TextInputSetClient extends TextInputCommand { - const TextInputSetClient({ - required this.clientId, - required this.configuration, - }); + const TextInputSetClient({required this.clientId, required this.configuration}); final int clientId; final InputConfiguration configuration; @@ -1929,13 +1929,13 @@ class TextInputSetClient extends TextInputCommand { DefaultTextEditingStrategy createDefaultTextEditingStrategy(HybridTextEditing textEditing) { DefaultTextEditingStrategy strategy; - if(ui_web.browser.operatingSystem == ui_web.OperatingSystem.iOs) { + if (ui_web.browser.operatingSystem == ui_web.OperatingSystem.iOs) { strategy = IOSTextEditingStrategy(textEditing); - } else if(ui_web.browser.operatingSystem == ui_web.OperatingSystem.android) { + } else if (ui_web.browser.operatingSystem == ui_web.OperatingSystem.android) { strategy = AndroidTextEditingStrategy(textEditing); - } else if(ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit) { + } else if (ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit) { strategy = SafariDesktopTextEditingStrategy(textEditing); - } else if(ui_web.browser.browserEngine == ui_web.BrowserEngine.firefox) { + } else if (ui_web.browser.browserEngine == ui_web.BrowserEngine.firefox) { strategy = FirefoxTextEditingStrategy(textEditing); } else { strategy = GloballyPositionedTextEditingStrategy(textEditing); @@ -1956,9 +1956,7 @@ class TextInputUpdateConfig extends TextInputCommand { /// Responds to the 'TextInput.setEditingState' message. class TextInputSetEditingState extends TextInputCommand { - const TextInputSetEditingState({ - required this.state, - }); + const TextInputSetEditingState({required this.state}); final EditingState state; @@ -1982,9 +1980,7 @@ class TextInputShow extends TextInputCommand { /// Responds to the 'TextInput.setEditableSizeAndTransform' message. class TextInputSetEditableSizeAndTransform extends TextInputCommand { - const TextInputSetEditableSizeAndTransform({ - required this.geometry, - }); + const TextInputSetEditableSizeAndTransform({required this.geometry}); final EditableTextGeometry geometry; @@ -1996,9 +1992,7 @@ class TextInputSetEditableSizeAndTransform extends TextInputCommand { /// Responds to the 'TextInput.setStyle' message. class TextInputSetStyle extends TextInputCommand { - const TextInputSetStyle({ - required this.style, - }); + const TextInputSetStyle({required this.style}); final EditableTextStyle style; @@ -2061,9 +2055,7 @@ class TextInputRequestAutofill extends TextInputCommand { } class TextInputFinishAutofillContext extends TextInputCommand { - const TextInputFinishAutofillContext({ - required this.saveForm, - }); + const TextInputFinishAutofillContext({required this.saveForm}); final bool saveForm; @@ -2114,8 +2106,7 @@ class TextEditingChannel { final HybridTextEditing implementation; /// Handles "flutter/textinput" platform messages received from the framework. - void handleTextInput( - ByteData? data, ui.PlatformMessageResponseCallback? callback) { + void handleTextInput(ByteData? data, ui.PlatformMessageResponseCallback? callback) { const JSONMethodCodec codec = JSONMethodCodec(); final MethodCall call = codec.decodeMethodCall(data); final TextInputCommand command; @@ -2132,15 +2123,13 @@ class TextEditingChannel { // field used to flush the command queue. However, delaye applying the // configuration because the strategy may not be available yet. implementation.configuration = InputConfiguration.fromFrameworkMessage( - call.arguments as Map + call.arguments as Map, ); command = const TextInputUpdateConfig(); case 'TextInput.setEditingState': command = TextInputSetEditingState( - state: EditingState.fromFrameworkMessage( - call.arguments as Map - ), + state: EditingState.fromFrameworkMessage(call.arguments as Map), ); case 'TextInput.show': @@ -2149,15 +2138,13 @@ class TextEditingChannel { case 'TextInput.setEditableSizeAndTransform': command = TextInputSetEditableSizeAndTransform( geometry: EditableTextGeometry.fromFrameworkMessage( - call.arguments as Map + call.arguments as Map, ), ); case 'TextInput.setStyle': command = TextInputSetStyle( - style: EditableTextStyle.fromFrameworkMessage( - call.arguments as Map, - ), + style: EditableTextStyle.fromFrameworkMessage(call.arguments as Map), ); case 'TextInput.clearClient': @@ -2173,9 +2160,7 @@ class TextEditingChannel { command = const TextInputRequestAutofill(); case 'TextInput.finishAutofillContext': - command = TextInputFinishAutofillContext( - saveForm: call.arguments as bool, - ); + command = TextInputFinishAutofillContext(saveForm: call.arguments as bool); case 'TextInput.setMarkedTextRect': command = const TextInputSetMarkedTextRect(); @@ -2192,8 +2177,10 @@ class TextEditingChannel { } implementation.acceptCommand(command, () { - EnginePlatformDispatcher.instance - .replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true)); + EnginePlatformDispatcher.instance.replyToPlatformMessage( + callback, + codec.encodeSuccessEnvelope(true), + ); }); } @@ -2230,10 +2217,7 @@ class TextEditingChannel { EnginePlatformDispatcher.instance.invokeOnPlatformMessage( 'flutter/textinput', const JSONMethodCodec().encodeMethodCall( - MethodCall( - 'TextInputClient.performAction', - [clientId, inputAction], - ), + MethodCall('TextInputClient.performAction', [clientId, inputAction]), ), _emptyCallback, ); @@ -2244,10 +2228,7 @@ class TextEditingChannel { EnginePlatformDispatcher.instance.invokeOnPlatformMessage( 'flutter/textinput', const JSONMethodCodec().encodeMethodCall( - MethodCall( - 'TextInputClient.onConnectionClosed', - [clientId], - ), + MethodCall('TextInputClient.onConnectionClosed', [clientId]), ), _emptyCallback, ); @@ -2263,8 +2244,7 @@ final HybridTextEditing textEditing = HybridTextEditing(); /// save or cancel them. /// /// See: https://github.com/flutter/flutter/blob/bf9f3a3dcfea3022f9cf2dfc3ab10b120b48b19d/packages/flutter/lib/src/services/text_input.dart#L1277 -final Map formsOnTheDom = - {}; +final Map formsOnTheDom = {}; /// Should be used as a singleton to provide support for text editing in /// Flutter Web. @@ -2300,10 +2280,10 @@ class HybridTextEditing { /// Supplies the DOM element used for editing. late final DefaultTextEditingStrategy strategy = - debugTextEditingStrategyOverride ?? - (EngineSemantics.instance.semanticsEnabled - ? SemanticsTextEditingStrategy.ensureInitialized(this) - : createDefaultTextEditingStrategy(this)); + debugTextEditingStrategyOverride ?? + (EngineSemantics.instance.semanticsEnabled + ? SemanticsTextEditingStrategy.ensureInitialized(this) + : createDefaultTextEditingStrategy(this)); void acceptCommand(TextInputCommand command, ui.VoidCallback callback) { if (_debugPrintTextInputCommands) { @@ -2357,8 +2337,7 @@ class EditableTextStyle { required this.fontWeight, }); - factory EditableTextStyle.fromFrameworkMessage( - Map flutterStyle) { + factory EditableTextStyle.fromFrameworkMessage(Map flutterStyle) { assert(flutterStyle.containsKey('fontSize')); assert(flutterStyle.containsKey('fontFamily')); assert(flutterStyle.containsKey('textAlignIndex')); @@ -2369,9 +2348,8 @@ class EditableTextStyle { final int? fontWeightIndex = flutterStyle['fontWeightIndex'] as int?; // Convert [fontWeightIndex] to its CSS equivalent value. - final String fontWeight = fontWeightIndex != null - ? fontWeightIndexToCss(fontWeightIndex: fontWeightIndex) - : 'normal'; + final String fontWeight = + fontWeightIndex != null ? fontWeightIndexToCss(fontWeightIndex: fontWeightIndex) : 'normal'; // Also convert [textAlignIndex] and [textDirectionIndex] to their // corresponding enum values in [ui.TextAlign] and [ui.TextDirection] @@ -2395,8 +2373,7 @@ class EditableTextStyle { String? get align => textAlignToCssValue(textAlign, textDirection); - String get cssFont => - '$fontWeight ${fontSize}px ${canonicalizeFontFamily(fontFamily)}'; + String get cssFont => '$fontWeight ${fontSize}px ${canonicalizeFontFamily(fontFamily)}'; void applyToDomElement(DomHTMLElement domElement) { domElement.style @@ -2418,17 +2395,14 @@ class EditableTextGeometry { }); /// Parses the geometry from a message sent by the framework. - factory EditableTextGeometry.fromFrameworkMessage( - Map encodedGeometry, - ) { + factory EditableTextGeometry.fromFrameworkMessage(Map encodedGeometry) { assert(encodedGeometry.containsKey('width')); assert(encodedGeometry.containsKey('height')); assert(encodedGeometry.containsKey('transform')); - final List transformList = - List.from(encodedGeometry.readList('transform').map( - (final dynamic e) => (e as num).toDouble() - )); + final List transformList = List.from( + encodedGeometry.readList('transform').map((final dynamic e) => (e as num).toDouble()), + ); return EditableTextGeometry( width: encodedGeometry.readDouble('width'), height: encodedGeometry.readDouble('height'), diff --git a/lib/web_ui/lib/src/engine/util.dart b/lib/web_ui/lib/src/engine/util.dart index 3734ec541fd3f..d711ee1d6e476 100644 --- a/lib/web_ui/lib/src/engine/util.dart +++ b/lib/web_ui/lib/src/engine/util.dart @@ -133,7 +133,8 @@ TransformKind transformKindOf(List matrix) { // If matrix contains scaling, rotation, z translation or // perspective transform, it is not considered simple. - final bool isSimple2dTransform = m[15] == + final bool isSimple2dTransform = + m[15] == 1.0 && // start reading from the last element to eliminate range checks in subsequent reads. m[14] == 0.0 && // z translation is NOT simple // m[13] - y translation is simple @@ -158,12 +159,8 @@ TransformKind transformKindOf(List matrix) { // From this point on we're sure the transform is 2D, but we don't know if // it's identity or not. To check, we need to look at the remaining elements // that were not checked above. - final bool isIdentityTransform = m[0] == 1.0 && - m[1] == 0.0 && - m[4] == 0.0 && - m[5] == 1.0 && - m[12] == 0.0 && - m[13] == 0.0; + final bool isIdentityTransform = + m[0] == 1.0 && m[1] == 0.0 && m[4] == 0.0 && m[5] == 1.0 && m[12] == 0.0 && m[13] == 0.0; if (isIdentityTransform) { return TransformKind.identity; @@ -231,12 +228,7 @@ ui.Rect transformRectWithMatrix(Matrix4 transform, ui.Rect rect) { _tempRectData[2] = rect.right; _tempRectData[3] = rect.bottom; transformLTRB(transform, _tempRectData); - return ui.Rect.fromLTRB( - _tempRectData[0], - _tempRectData[1], - _tempRectData[2], - _tempRectData[3], - ); + return ui.Rect.fromLTRB(_tempRectData[0], _tempRectData[1], _tempRectData[2], _tempRectData[3]); } /// Temporary storage for intermediate data used by [transformLTRB]. @@ -300,25 +292,29 @@ void transformLTRB(Matrix4 transform, Float32List ltrb) { w = 1.0; } - ltrb[0] = math.min( - math.min(math.min(_tempPointData[0], _tempPointData[1]), - _tempPointData[2]), - _tempPointData[3]) / + ltrb[0] = + math.min( + math.min(math.min(_tempPointData[0], _tempPointData[1]), _tempPointData[2]), + _tempPointData[3], + ) / w; - ltrb[1] = math.min( - math.min(math.min(_tempPointData[4], _tempPointData[5]), - _tempPointData[6]), - _tempPointData[7]) / + ltrb[1] = + math.min( + math.min(math.min(_tempPointData[4], _tempPointData[5]), _tempPointData[6]), + _tempPointData[7], + ) / w; - ltrb[2] = math.max( - math.max(math.max(_tempPointData[0], _tempPointData[1]), - _tempPointData[2]), - _tempPointData[3]) / + ltrb[2] = + math.max( + math.max(math.max(_tempPointData[0], _tempPointData[1]), _tempPointData[2]), + _tempPointData[3], + ) / w; - ltrb[3] = math.max( - math.max(math.max(_tempPointData[4], _tempPointData[5]), - _tempPointData[6]), - _tempPointData[7]) / + ltrb[3] = + math.max( + math.max(math.max(_tempPointData[4], _tempPointData[5]), _tempPointData[6]), + _tempPointData[7], + ) / w; } @@ -673,9 +669,7 @@ int? tryViewId(Object? arguments) { /// Input: [0, 1, 2, 3] /// Output: 0x00 0x01 0x02 0x03 String bytesToHexString(List data) { - return data - .map((int byte) => '0x${byte.toRadixString(16).padLeft(2, '0')}') - .join(' '); + return data.map((int byte) => '0x${byte.toRadixString(16).padLeft(2, '0')}').join(' '); } /// Sets a style property on [element]. @@ -706,14 +700,14 @@ void setClipPath(DomElement element, String? value) { } void setThemeColor(ui.Color? color) { - DomHTMLMetaElement? theme = - domDocument.querySelector('#flutterweb-theme') as DomHTMLMetaElement?; + DomHTMLMetaElement? theme = domDocument.querySelector('#flutterweb-theme') as DomHTMLMetaElement?; if (color != null) { if (theme == null) { - theme = createDomHTMLMetaElement() - ..id = 'flutterweb-theme' - ..name = 'theme-color'; + theme = + createDomHTMLMetaElement() + ..id = 'flutterweb-theme' + ..name = 'theme-color'; domDocument.head!.append(theme); } theme.content = color.toCssString(); @@ -724,13 +718,13 @@ void setThemeColor(ui.Color? color) { /// Ensure a "meta" tag with [name] and [content] is set on the page. void ensureMetaTag(String name, String content) { - final DomElement? existingTag = - domDocument.querySelector('meta[name=$name][content=$content]'); + final DomElement? existingTag = domDocument.querySelector('meta[name=$name][content=$content]'); if (existingTag == null) { - final DomHTMLMetaElement meta = createDomHTMLMetaElement() - ..name = name - ..content = content; + final DomHTMLMetaElement meta = + createDomHTMLMetaElement() + ..name = name + ..content = content; domDocument.head!.append(meta); } } @@ -739,20 +733,28 @@ bool? _ellipseFeatureDetected; /// Draws CanvasElement ellipse with fallback. void drawEllipse( - DomCanvasRenderingContext2D context, - double centerX, - double centerY, - double radiusX, - double radiusY, - double rotation, - double startAngle, - double endAngle, - bool antiClockwise) { - _ellipseFeatureDetected ??= - getJsProperty(context, 'ellipse') != null; + DomCanvasRenderingContext2D context, + double centerX, + double centerY, + double radiusX, + double radiusY, + double rotation, + double startAngle, + double endAngle, + bool antiClockwise, +) { + _ellipseFeatureDetected ??= getJsProperty(context, 'ellipse') != null; if (_ellipseFeatureDetected!) { - context.ellipse(centerX, centerY, radiusX, radiusY, rotation, startAngle, - endAngle, antiClockwise); + context.ellipse( + centerX, + centerY, + radiusX, + radiusY, + rotation, + startAngle, + endAngle, + antiClockwise, + ); } else { context.save(); context.translate(centerX, centerY); @@ -908,18 +910,14 @@ class BitmapSize { /// Returns a [BitmapSize] by rounding the width and height of a [ui.Size] to /// the nearest integer. - BitmapSize.fromSize(ui.Size size) - : width = size.width.round(), - height = size.height.round(); + BitmapSize.fromSize(ui.Size size) : width = size.width.round(), height = size.height.round(); final int width; final int height; @override bool operator ==(Object other) { - return other is BitmapSize && - other.width == width && - other.height == height; + return other is BitmapSize && other.width == width && other.height == height; } @override diff --git a/lib/web_ui/lib/src/engine/validators.dart b/lib/web_ui/lib/src/engine/validators.dart index 5db10f9dc8289..d6ee7df6501e0 100644 --- a/lib/web_ui/lib/src/engine/validators.dart +++ b/lib/web_ui/lib/src/engine/validators.dart @@ -8,27 +8,22 @@ import 'package:ui/ui.dart' as ui; bool rectIsValid(ui.Rect rect) { assert( - !(rect.left.isNaN || - rect.right.isNaN || - rect.top.isNaN || - rect.bottom.isNaN), - 'Rect argument contained a NaN value.'); + !(rect.left.isNaN || rect.right.isNaN || rect.top.isNaN || rect.bottom.isNaN), + 'Rect argument contained a NaN value.', + ); return true; } bool rrectIsValid(ui.RRect rrect) { assert( - !(rrect.left.isNaN || - rrect.right.isNaN || - rrect.top.isNaN || - rrect.bottom.isNaN), - 'RRect argument contained a NaN value.'); + !(rrect.left.isNaN || rrect.right.isNaN || rrect.top.isNaN || rrect.bottom.isNaN), + 'RRect argument contained a NaN value.', + ); return true; } bool offsetIsValid(ui.Offset offset) { - assert(!offset.dx.isNaN && !offset.dy.isNaN, - 'Offset argument contained a NaN value.'); + assert(!offset.dx.isNaN && !offset.dy.isNaN, 'Offset argument contained a NaN value.'); return true; } @@ -38,8 +33,7 @@ bool matrix4IsValid(Float32List matrix4) { } bool radiusIsValid(ui.Radius radius) { - assert(!radius.x.isNaN && !radius.y.isNaN, - 'Radius argument contained a NaN value.'); + assert(!radius.x.isNaN && !radius.y.isNaN, 'Radius argument contained a NaN value.'); return true; } @@ -47,13 +41,11 @@ bool radiusIsValid(ui.Radius radius) { void validateColorStops(List colors, List? colorStops) { if (colorStops == null) { if (colors.length != 2) { - throw ArgumentError( - '"colors" must have length 2 if "colorStops" is omitted.'); + throw ArgumentError('"colors" must have length 2 if "colorStops" is omitted.'); } } else { if (colors.length != colorStops.length) { - throw ArgumentError( - '"colors" and "colorStops" arguments must have equal length.'); + throw ArgumentError('"colors" and "colorStops" arguments must have equal length.'); } } } diff --git a/lib/web_ui/lib/src/engine/vector_math.dart b/lib/web_ui/lib/src/engine/vector_math.dart index a6edaa5a61438..aa7769f8e1928 100644 --- a/lib/web_ui/lib/src/engine/vector_math.dart +++ b/lib/web_ui/lib/src/engine/vector_math.dart @@ -12,25 +12,41 @@ import 'util.dart'; class Matrix4 { /// Constructs a new mat4. factory Matrix4( - double arg0, - double arg1, - double arg2, - double arg3, - double arg4, - double arg5, - double arg6, - double arg7, - double arg8, - double arg9, - double arg10, - double arg11, - double arg12, - double arg13, - double arg14, - double arg15) => - Matrix4.zero() - ..setValues(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, - arg10, arg11, arg12, arg13, arg14, arg15); + double arg0, + double arg1, + double arg2, + double arg3, + double arg4, + double arg5, + double arg6, + double arg7, + double arg8, + double arg9, + double arg10, + double arg11, + double arg12, + double arg13, + double arg14, + double arg15, + ) => + Matrix4.zero()..setValues( + arg0, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + arg8, + arg9, + arg10, + arg11, + arg12, + arg13, + arg14, + arg15, + ); /// Zero matrix. Matrix4.zero() : _m4storage = Float32List(16); @@ -57,28 +73,30 @@ class Matrix4 { } /// Rotation of [radians_] around X. - factory Matrix4.rotationX(double radians) => Matrix4.zero() - .._m4storage[15] = 1.0 - ..setRotationX(radians); + factory Matrix4.rotationX(double radians) => + Matrix4.zero() + .._m4storage[15] = 1.0 + ..setRotationX(radians); /// Rotation of [radians_] around Y. - factory Matrix4.rotationY(double radians) => Matrix4.zero() - .._m4storage[15] = 1.0 - ..setRotationY(radians); + factory Matrix4.rotationY(double radians) => + Matrix4.zero() + .._m4storage[15] = 1.0 + ..setRotationY(radians); /// Rotation of [radians_] around Z. - factory Matrix4.rotationZ(double radians) => Matrix4.zero() - .._m4storage[15] = 1.0 - ..setRotationZ(radians); + factory Matrix4.rotationZ(double radians) => + Matrix4.zero() + .._m4storage[15] = 1.0 + ..setRotationZ(radians); /// Translation matrix. - factory Matrix4.translation(Vector3 translation) => Matrix4.identity() - ..setTranslation(translation); + factory Matrix4.translation(Vector3 translation) => + Matrix4.identity()..setTranslation(translation); /// Translation matrix. factory Matrix4.translationValues(double x, double y, double z) => - Matrix4.identity() - ..setTranslationRaw(x, y, z); + Matrix4.identity()..setTranslationRaw(x, y, z); /// Scale matrix. factory Matrix4.diagonal3Values(double x, double y, double z) => @@ -94,7 +112,7 @@ class Matrix4 { /// Constructs Matrix4 with a [storage] that views given [buffer] starting at /// [offset]. [offset] has to be multiple of [Float32List.bytesPerElement]. Matrix4.fromBuffer(ByteBuffer buffer, int offset) - : _m4storage = Float32List.view(buffer, offset, 16); + : _m4storage = Float32List.view(buffer, offset, 16); final Float32List _m4storage; @@ -133,22 +151,23 @@ class Matrix4 { /// Sets the matrix with specified values. void setValues( - double arg0, - double arg1, - double arg2, - double arg3, - double arg4, - double arg5, - double arg6, - double arg7, - double arg8, - double arg9, - double arg10, - double arg11, - double arg12, - double arg13, - double arg14, - double arg15) { + double arg0, + double arg1, + double arg2, + double arg3, + double arg4, + double arg5, + double arg6, + double arg7, + double arg8, + double arg9, + double arg10, + double arg11, + double arg12, + double arg13, + double arg14, + double arg15, + ) { _m4storage[15] = arg15; _m4storage[14] = arg14; _m4storage[13] = arg13; @@ -229,22 +248,14 @@ class Matrix4 { /// Translate this matrix by x, y, and z. void translate(double x, [double y = 0.0, double z = 0.0]) { const double tw = 1.0; - final double t1 = _m4storage[0] * x + - _m4storage[4] * y + - _m4storage[8] * z + - _m4storage[12] * tw; - final double t2 = _m4storage[1] * x + - _m4storage[5] * y + - _m4storage[9] * z + - _m4storage[13] * tw; - final double t3 = _m4storage[2] * x + - _m4storage[6] * y + - _m4storage[10] * z + - _m4storage[14] * tw; - final double t4 = _m4storage[3] * x + - _m4storage[7] * y + - _m4storage[11] * z + - _m4storage[15] * tw; + final double t1 = + _m4storage[0] * x + _m4storage[4] * y + _m4storage[8] * z + _m4storage[12] * tw; + final double t2 = + _m4storage[1] * x + _m4storage[5] * y + _m4storage[9] * z + _m4storage[13] * tw; + final double t3 = + _m4storage[2] * x + _m4storage[6] * y + _m4storage[10] * z + _m4storage[14] * tw; + final double t4 = + _m4storage[3] * x + _m4storage[7] * y + _m4storage[11] * z + _m4storage[15] * tw; _m4storage[12] = t1; _m4storage[13] = t2; _m4storage[14] = t3; @@ -347,30 +358,16 @@ class Matrix4 { /// Returns the determinant of this matrix. double determinant() { final Float32List m = _m4storage; - final double det2_01_01 = - m[0] * m[5] - m[1] * m[4]; - final double det2_01_02 = - m[0] * m[6] - m[2] * m[4]; - final double det2_01_03 = - m[0] * m[7] - m[3] * m[4]; - final double det2_01_12 = - m[1] * m[6] - m[2] * m[5]; - final double det2_01_13 = - m[1] * m[7] - m[3] * m[5]; - final double det2_01_23 = - m[2] * m[7] - m[3] * m[6]; - final double det3_201_012 = m[8] * det2_01_12 - - m[9] * det2_01_02 + - m[10] * det2_01_01; - final double det3_201_013 = m[8] * det2_01_13 - - m[9] * det2_01_03 + - m[11] * det2_01_01; - final double det3_201_023 = m[8] * det2_01_23 - - m[10] * det2_01_03 + - m[11] * det2_01_02; - final double det3_201_123 = m[9] * det2_01_23 - - m[10] * det2_01_13 + - m[11] * det2_01_12; + final double det2_01_01 = m[0] * m[5] - m[1] * m[4]; + final double det2_01_02 = m[0] * m[6] - m[2] * m[4]; + final double det2_01_03 = m[0] * m[7] - m[3] * m[4]; + final double det2_01_12 = m[1] * m[6] - m[2] * m[5]; + final double det2_01_13 = m[1] * m[7] - m[3] * m[5]; + final double det2_01_23 = m[2] * m[7] - m[3] * m[6]; + final double det3_201_012 = m[8] * det2_01_12 - m[9] * det2_01_02 + m[10] * det2_01_01; + final double det3_201_013 = m[8] * det2_01_13 - m[9] * det2_01_03 + m[11] * det2_01_01; + final double det3_201_023 = m[8] * det2_01_23 - m[10] * det2_01_03 + m[11] * det2_01_02; + final double det3_201_123 = m[9] * det2_01_23 - m[10] * det2_01_13 + m[11] * det2_01_12; return -det3_201_123 * m[12] + det3_201_023 * m[13] - det3_201_013 * m[14] + @@ -379,34 +376,17 @@ class Matrix4 { /// Transform [arg] of type [Vector3] using the perspective transformation /// defined by [this]. - Vector3 perspectiveTransform({ - required double x, - required double y, - required double z, - }) { - final double transformedX = (_m4storage[0] * x) + - (_m4storage[4] * y) + - (_m4storage[8] * z) + - _m4storage[12]; - final double transformedY = (_m4storage[1] * x) + - (_m4storage[5] * y) + - (_m4storage[9] * z) + - _m4storage[13]; - final double transformedZ = (_m4storage[2] * x) + - (_m4storage[6] * y) + - (_m4storage[10] * z) + - _m4storage[14]; - final double w = 1.0 / - ((_m4storage[3] * x) + - (_m4storage[7] * y) + - (_m4storage[11] * z) + - _m4storage[15]); - - return ( - x: transformedX * w, - y: transformedY * w, - z: transformedZ * w, - ); + Vector3 perspectiveTransform({required double x, required double y, required double z}) { + final double transformedX = + (_m4storage[0] * x) + (_m4storage[4] * y) + (_m4storage[8] * z) + _m4storage[12]; + final double transformedY = + (_m4storage[1] * x) + (_m4storage[5] * y) + (_m4storage[9] * z) + _m4storage[13]; + final double transformedZ = + (_m4storage[2] * x) + (_m4storage[6] * y) + (_m4storage[10] * z) + _m4storage[14]; + final double w = + 1.0 / ((_m4storage[3] * x) + (_m4storage[7] * y) + (_m4storage[11] * z) + _m4storage[15]); + + return (x: transformedX * w, y: transformedY * w, z: transformedZ * w); } bool isIdentity() => @@ -433,25 +413,21 @@ class Matrix4 { bool isIdentityOrTranslation() => _m4storage[15] == 1.0 && _m4storage[0] == 1.0 && // col 1 - _m4storage[1] == 0.0 && - _m4storage[2] == 0.0 && - _m4storage[3] == 0.0 && - _m4storage[4] == 0.0 && // col 2 - _m4storage[5] == 1.0 && - _m4storage[6] == 0.0 && - _m4storage[7] == 0.0 && - _m4storage[8] == 0.0 && // col 3 - _m4storage[9] == 0.0 && - _m4storage[10] == 1.0 && - _m4storage[11] == 0.0; + _m4storage[1] == 0.0 && + _m4storage[2] == 0.0 && + _m4storage[3] == 0.0 && + _m4storage[4] == 0.0 && // col 2 + _m4storage[5] == 1.0 && + _m4storage[6] == 0.0 && + _m4storage[7] == 0.0 && + _m4storage[8] == 0.0 && // col 3 + _m4storage[9] == 0.0 && + _m4storage[10] == 1.0 && + _m4storage[11] == 0.0; /// Returns the translation vector from this homogeneous transformation matrix. Vector3 getTranslation() { - return ( - x: _m4storage[12], - y: _m4storage[13], - z: _m4storage[14], - ); + return (x: _m4storage[12], y: _m4storage[13], z: _m4storage[14]); } void rotate(Vector3 axis, double angle) { @@ -471,30 +447,18 @@ class Matrix4 { final double m31 = z * x * C - y * s; final double m32 = z * y * C + x * s; final double m33 = z * z * C + c; - final double t1 = - _m4storage[0] * m11 + _m4storage[4] * m21 + _m4storage[8] * m31; - final double t2 = - _m4storage[1] * m11 + _m4storage[5] * m21 + _m4storage[9] * m31; - final double t3 = - _m4storage[2] * m11 + _m4storage[6] * m21 + _m4storage[10] * m31; - final double t4 = - _m4storage[3] * m11 + _m4storage[7] * m21 + _m4storage[11] * m31; - final double t5 = - _m4storage[0] * m12 + _m4storage[4] * m22 + _m4storage[8] * m32; - final double t6 = - _m4storage[1] * m12 + _m4storage[5] * m22 + _m4storage[9] * m32; - final double t7 = - _m4storage[2] * m12 + _m4storage[6] * m22 + _m4storage[10] * m32; - final double t8 = - _m4storage[3] * m12 + _m4storage[7] * m22 + _m4storage[11] * m32; - final double t9 = - _m4storage[0] * m13 + _m4storage[4] * m23 + _m4storage[8] * m33; - final double t10 = - _m4storage[1] * m13 + _m4storage[5] * m23 + _m4storage[9] * m33; - final double t11 = - _m4storage[2] * m13 + _m4storage[6] * m23 + _m4storage[10] * m33; - final double t12 = - _m4storage[3] * m13 + _m4storage[7] * m23 + _m4storage[11] * m33; + final double t1 = _m4storage[0] * m11 + _m4storage[4] * m21 + _m4storage[8] * m31; + final double t2 = _m4storage[1] * m11 + _m4storage[5] * m21 + _m4storage[9] * m31; + final double t3 = _m4storage[2] * m11 + _m4storage[6] * m21 + _m4storage[10] * m31; + final double t4 = _m4storage[3] * m11 + _m4storage[7] * m21 + _m4storage[11] * m31; + final double t5 = _m4storage[0] * m12 + _m4storage[4] * m22 + _m4storage[8] * m32; + final double t6 = _m4storage[1] * m12 + _m4storage[5] * m22 + _m4storage[9] * m32; + final double t7 = _m4storage[2] * m12 + _m4storage[6] * m22 + _m4storage[10] * m32; + final double t8 = _m4storage[3] * m12 + _m4storage[7] * m22 + _m4storage[11] * m32; + final double t9 = _m4storage[0] * m13 + _m4storage[4] * m23 + _m4storage[8] * m33; + final double t10 = _m4storage[1] * m13 + _m4storage[5] * m23 + _m4storage[9] * m33; + final double t11 = _m4storage[2] * m13 + _m4storage[6] * m23 + _m4storage[10] * m33; + final double t12 = _m4storage[3] * m13 + _m4storage[7] * m23 + _m4storage[11] * m33; _m4storage[0] = t1; _m4storage[1] = t2; _m4storage[2] = t3; @@ -601,8 +565,7 @@ class Matrix4 { final double b09 = a21 * a32 - a22 * a31; final double b10 = a21 * a33 - a23 * a31; final double b11 = a22 * a33 - a23 * a32; - final double det = - b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; + final double det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; if (det == 0.0) { setFrom(arg); return 0.0; @@ -642,24 +605,15 @@ class Matrix4 { double kx; double ky; double kz; - ix = invDet * - (_m4storage[5] * _m4storage[10] - _m4storage[6] * _m4storage[9]); - iy = invDet * - (_m4storage[2] * _m4storage[9] - _m4storage[1] * _m4storage[10]); - iz = invDet * - (_m4storage[1] * _m4storage[6] - _m4storage[2] * _m4storage[5]); - jx = invDet * - (_m4storage[6] * _m4storage[8] - _m4storage[4] * _m4storage[10]); - jy = invDet * - (_m4storage[0] * _m4storage[10] - _m4storage[2] * _m4storage[8]); - jz = invDet * - (_m4storage[2] * _m4storage[4] - _m4storage[0] * _m4storage[6]); - kx = invDet * - (_m4storage[4] * _m4storage[9] - _m4storage[5] * _m4storage[8]); - ky = invDet * - (_m4storage[1] * _m4storage[8] - _m4storage[0] * _m4storage[9]); - kz = invDet * - (_m4storage[0] * _m4storage[5] - _m4storage[1] * _m4storage[4]); + ix = invDet * (_m4storage[5] * _m4storage[10] - _m4storage[6] * _m4storage[9]); + iy = invDet * (_m4storage[2] * _m4storage[9] - _m4storage[1] * _m4storage[10]); + iz = invDet * (_m4storage[1] * _m4storage[6] - _m4storage[2] * _m4storage[5]); + jx = invDet * (_m4storage[6] * _m4storage[8] - _m4storage[4] * _m4storage[10]); + jy = invDet * (_m4storage[0] * _m4storage[10] - _m4storage[2] * _m4storage[8]); + jz = invDet * (_m4storage[2] * _m4storage[4] - _m4storage[0] * _m4storage[6]); + kx = invDet * (_m4storage[4] * _m4storage[9] - _m4storage[5] * _m4storage[8]); + ky = invDet * (_m4storage[1] * _m4storage[8] - _m4storage[0] * _m4storage[9]); + kz = invDet * (_m4storage[0] * _m4storage[5] - _m4storage[1] * _m4storage[4]); _m4storage[0] = ix; _m4storage[1] = iy; _m4storage[2] = iz; @@ -802,67 +756,83 @@ class Matrix4 { final double m32 = _m4storage[14]; final Float32List argStorage = arg._m4storage; - _m4storage[0] = (m00 * argStorage[0]) + + _m4storage[0] = + (m00 * argStorage[0]) + (m01 * argStorage[1]) + (m02 * argStorage[2]) + (m03 * argStorage[3]); - _m4storage[4] = (m00 * argStorage[4]) + + _m4storage[4] = + (m00 * argStorage[4]) + (m01 * argStorage[5]) + (m02 * argStorage[6]) + (m03 * argStorage[7]); - _m4storage[8] = (m00 * argStorage[8]) + + _m4storage[8] = + (m00 * argStorage[8]) + (m01 * argStorage[9]) + (m02 * argStorage[10]) + (m03 * argStorage[11]); - _m4storage[12] = (m00 * argStorage[12]) + + _m4storage[12] = + (m00 * argStorage[12]) + (m01 * argStorage[13]) + (m02 * argStorage[14]) + (m03 * argStorage[15]); - _m4storage[1] = (m10 * argStorage[0]) + + _m4storage[1] = + (m10 * argStorage[0]) + (m11 * argStorage[1]) + (m12 * argStorage[2]) + (m13 * argStorage[3]); - _m4storage[5] = (m10 * argStorage[4]) + + _m4storage[5] = + (m10 * argStorage[4]) + (m11 * argStorage[5]) + (m12 * argStorage[6]) + (m13 * argStorage[7]); - _m4storage[9] = (m10 * argStorage[8]) + + _m4storage[9] = + (m10 * argStorage[8]) + (m11 * argStorage[9]) + (m12 * argStorage[10]) + (m13 * argStorage[11]); - _m4storage[13] = (m10 * argStorage[12]) + + _m4storage[13] = + (m10 * argStorage[12]) + (m11 * argStorage[13]) + (m12 * argStorage[14]) + (m13 * argStorage[15]); - _m4storage[2] = (m20 * argStorage[0]) + + _m4storage[2] = + (m20 * argStorage[0]) + (m21 * argStorage[1]) + (m22 * argStorage[2]) + (m23 * argStorage[3]); - _m4storage[6] = (m20 * argStorage[4]) + + _m4storage[6] = + (m20 * argStorage[4]) + (m21 * argStorage[5]) + (m22 * argStorage[6]) + (m23 * argStorage[7]); - _m4storage[10] = (m20 * argStorage[8]) + + _m4storage[10] = + (m20 * argStorage[8]) + (m21 * argStorage[9]) + (m22 * argStorage[10]) + (m23 * argStorage[11]); - _m4storage[14] = (m20 * argStorage[12]) + + _m4storage[14] = + (m20 * argStorage[12]) + (m21 * argStorage[13]) + (m22 * argStorage[14]) + (m23 * argStorage[15]); - _m4storage[3] = (m30 * argStorage[0]) + + _m4storage[3] = + (m30 * argStorage[0]) + (m31 * argStorage[1]) + (m32 * argStorage[2]) + (m33 * argStorage[3]); - _m4storage[7] = (m30 * argStorage[4]) + + _m4storage[7] = + (m30 * argStorage[4]) + (m31 * argStorage[5]) + (m32 * argStorage[6]) + (m33 * argStorage[7]); - _m4storage[11] = (m30 * argStorage[8]) + + _m4storage[11] = + (m30 * argStorage[8]) + (m31 * argStorage[9]) + (m32 * argStorage[10]) + (m33 * argStorage[11]); - _m4storage[15] = (m30 * argStorage[12]) + + _m4storage[15] = + (m30 * argStorage[12]) + (m31 * argStorage[13]) + (m32 * argStorage[14]) + (m33 * argStorage[15]); @@ -887,67 +857,83 @@ class Matrix4 { final double m32 = _m4storage[11]; final double m33 = _m4storage[15]; final Float32List argStorage = arg._m4storage; - _m4storage[0] = (m00 * argStorage[0]) + + _m4storage[0] = + (m00 * argStorage[0]) + (m01 * argStorage[4]) + (m02 * argStorage[8]) + (m03 * argStorage[12]); - _m4storage[4] = (m00 * argStorage[1]) + + _m4storage[4] = + (m00 * argStorage[1]) + (m01 * argStorage[5]) + (m02 * argStorage[9]) + (m03 * argStorage[13]); - _m4storage[8] = (m00 * argStorage[2]) + + _m4storage[8] = + (m00 * argStorage[2]) + (m01 * argStorage[6]) + (m02 * argStorage[10]) + (m03 * argStorage[14]); - _m4storage[12] = (m00 * argStorage[3]) + + _m4storage[12] = + (m00 * argStorage[3]) + (m01 * argStorage[7]) + (m02 * argStorage[11]) + (m03 * argStorage[15]); - _m4storage[1] = (m10 * argStorage[0]) + + _m4storage[1] = + (m10 * argStorage[0]) + (m11 * argStorage[4]) + (m12 * argStorage[8]) + (m13 * argStorage[12]); - _m4storage[5] = (m10 * argStorage[1]) + + _m4storage[5] = + (m10 * argStorage[1]) + (m11 * argStorage[5]) + (m12 * argStorage[9]) + (m13 * argStorage[13]); - _m4storage[9] = (m10 * argStorage[2]) + + _m4storage[9] = + (m10 * argStorage[2]) + (m11 * argStorage[6]) + (m12 * argStorage[10]) + (m13 * argStorage[14]); - _m4storage[13] = (m10 * argStorage[3]) + + _m4storage[13] = + (m10 * argStorage[3]) + (m11 * argStorage[7]) + (m12 * argStorage[11]) + (m13 * argStorage[15]); - _m4storage[2] = (m20 * argStorage[0]) + + _m4storage[2] = + (m20 * argStorage[0]) + (m21 * argStorage[4]) + (m22 * argStorage[8]) + (m23 * argStorage[12]); - _m4storage[6] = (m20 * argStorage[1]) + + _m4storage[6] = + (m20 * argStorage[1]) + (m21 * argStorage[5]) + (m22 * argStorage[9]) + (m23 * argStorage[13]); - _m4storage[10] = (m20 * argStorage[2]) + + _m4storage[10] = + (m20 * argStorage[2]) + (m21 * argStorage[6]) + (m22 * argStorage[10]) + (m23 * argStorage[14]); - _m4storage[14] = (m20 * argStorage[3]) + + _m4storage[14] = + (m20 * argStorage[3]) + (m21 * argStorage[7]) + (m22 * argStorage[11]) + (m23 * argStorage[15]); - _m4storage[3] = (m30 * argStorage[0]) + + _m4storage[3] = + (m30 * argStorage[0]) + (m31 * argStorage[4]) + (m32 * argStorage[8]) + (m33 * argStorage[12]); - _m4storage[7] = (m30 * argStorage[1]) + + _m4storage[7] = + (m30 * argStorage[1]) + (m31 * argStorage[5]) + (m32 * argStorage[9]) + (m33 * argStorage[13]); - _m4storage[11] = (m30 * argStorage[2]) + + _m4storage[11] = + (m30 * argStorage[2]) + (m31 * argStorage[6]) + (m32 * argStorage[10]) + (m33 * argStorage[14]); - _m4storage[15] = (m30 * argStorage[3]) + + _m4storage[15] = + (m30 * argStorage[3]) + (m31 * argStorage[7]) + (m32 * argStorage[11]) + (m33 * argStorage[15]); @@ -955,15 +941,18 @@ class Matrix4 { /// Transforms a 3-component vector in-place. void transform3(Float32List vector) { - final double x = (_m4storage[0] * vector[0]) + + final double x = + (_m4storage[0] * vector[0]) + (_m4storage[4] * vector[1]) + (_m4storage[8] * vector[2]) + _m4storage[12]; - final double y = (_m4storage[1] * vector[0]) + + final double y = + (_m4storage[1] * vector[0]) + (_m4storage[5] * vector[1]) + (_m4storage[9] * vector[2]) + _m4storage[13]; - final double z = (_m4storage[2] * vector[0]) + + final double z = + (_m4storage[2] * vector[0]) + (_m4storage[6] * vector[1]) + (_m4storage[10] * vector[2]) + _m4storage[14]; @@ -979,12 +968,8 @@ class Matrix4 { void transform2(Float32List vector) { final double x = vector[0]; final double y = vector[1]; - vector[0] = (_m4storage[0] * x) + - (_m4storage[4] * y) + - _m4storage[12]; - vector[1] = (_m4storage[1] * x) + - (_m4storage[5] * y) + - _m4storage[13]; + vector[0] = (_m4storage[0] * x) + (_m4storage[4] * y) + _m4storage[12]; + vector[1] = (_m4storage[1] * x) + (_m4storage[5] * y) + _m4storage[13]; } /// Transforms the input rect and calculates the bounding box of the rect @@ -1053,10 +1038,11 @@ class Matrix4 { return storage[index].toStringAsFixed(2); } - result = '[${fmt(0)}, ${fmt(4)}, ${fmt(8)}, ${fmt(12)}]\n' - '[${fmt(1)}, ${fmt(5)}, ${fmt(9)}, ${fmt(13)}]\n' - '[${fmt(2)}, ${fmt(6)}, ${fmt(10)}, ${fmt(14)}]\n' - '[${fmt(3)}, ${fmt(7)}, ${fmt(11)}, ${fmt(15)}]'; + result = + '[${fmt(0)}, ${fmt(4)}, ${fmt(8)}, ${fmt(12)}]\n' + '[${fmt(1)}, ${fmt(5)}, ${fmt(9)}, ${fmt(13)}]\n' + '[${fmt(2)}, ${fmt(6)}, ${fmt(10)}, ${fmt(14)}]\n' + '[${fmt(3)}, ${fmt(7)}, ${fmt(11)}, ${fmt(15)}]'; return true; }()); return result; diff --git a/lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/custom_element_dimensions_provider.dart b/lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/custom_element_dimensions_provider.dart index 25f77afdd5038..580418cc75b26 100644 --- a/lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/custom_element_dimensions_provider.dart +++ b/lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/custom_element_dimensions_provider.dart @@ -27,9 +27,7 @@ import 'dimensions_provider.dart'; /// to be effective. class CustomElementDimensionsProvider extends DimensionsProvider { /// Creates a [CustomElementDimensionsProvider] from a [_hostElement]. - CustomElementDimensionsProvider(this._hostElement, { - Stream? onDprChange, - }) { + CustomElementDimensionsProvider(this._hostElement, {Stream? onDprChange}) { // Send a resize event when the page DPR changes. _dprChangeStreamSubscription = onDprChange?.listen((_) { _broadcastSize(null); @@ -47,8 +45,10 @@ class CustomElementDimensionsProvider extends DimensionsProvider { assert(() { if (_hostElementResizeObserver == null) { - domWindow.console.warn('ResizeObserver API not supported. ' - 'Flutter will not resize with its hostElement.'); + domWindow.console.warn( + 'ResizeObserver API not supported. ' + 'Flutter will not resize with its hostElement.', + ); } return true; }()); @@ -93,15 +93,7 @@ class CustomElementDimensionsProvider extends DimensionsProvider { } @override - ViewPadding computeKeyboardInsets( - double physicalHeight, - bool isEditingOnMobile, - ) { - return const ViewPadding( - top: 0, - right: 0, - bottom: 0, - left: 0, - ); + ViewPadding computeKeyboardInsets(double physicalHeight, bool isEditingOnMobile) { + return const ViewPadding(top: 0, right: 0, bottom: 0, left: 0); } } diff --git a/lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/dimensions_provider.dart b/lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/dimensions_provider.dart index 469f3a197d25b..f882c50a5b4be 100644 --- a/lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/dimensions_provider.dart +++ b/lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/dimensions_provider.dart @@ -48,10 +48,7 @@ abstract class DimensionsProvider { ui.Size computePhysicalSize(); /// Returns the [ViewPadding] of the keyboard insets (if present). - ViewPadding computeKeyboardInsets( - double physicalHeight, - bool isEditingOnMobile, - ); + ViewPadding computeKeyboardInsets(double physicalHeight, bool isEditingOnMobile); /// Returns a Stream with the changes to [ui.Size] (when cheap to get). /// diff --git a/lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/full_page_dimensions_provider.dart b/lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/full_page_dimensions_provider.dart index 4781394e39733..2c65550c6952c 100644 --- a/lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/full_page_dimensions_provider.dart +++ b/lib/web_ui/lib/src/engine/view_embedder/dimensions_provider/full_page_dimensions_provider.dart @@ -26,15 +26,10 @@ class FullPageDimensionsProvider extends DimensionsProvider { // Determine what 'resize' event we'll be listening to. // This is needed for older browsers (Firefox < 91, Safari < 13) // TODO(dit): Clean this up, https://github.com/flutter/flutter/issues/117105 - final DomEventTarget resizeEventTarget = - domWindow.visualViewport ?? domWindow; + final DomEventTarget resizeEventTarget = domWindow.visualViewport ?? domWindow; // Subscribe to the 'resize' event, and convert it to a ui.Size stream. - _domResizeSubscription = DomSubscription( - resizeEventTarget, - 'resize', - _onVisualViewportResize, - ); + _domResizeSubscription = DomSubscription(resizeEventTarget, 'resize', _onVisualViewportResize); } late DomSubscription _domResizeSubscription; @@ -92,25 +87,18 @@ class FullPageDimensionsProvider extends DimensionsProvider { windowInnerWidth = domWindow.innerWidth! * devicePixelRatio; windowInnerHeight = domWindow.innerHeight! * devicePixelRatio; } - return ui.Size( - windowInnerWidth, - windowInnerHeight, - ); + return ui.Size(windowInnerWidth, windowInnerHeight); } @override - ViewPadding computeKeyboardInsets( - double physicalHeight, - bool isEditingOnMobile, - ) { + ViewPadding computeKeyboardInsets(double physicalHeight, bool isEditingOnMobile) { final double devicePixelRatio = EngineFlutterDisplay.instance.devicePixelRatio; final DomVisualViewport? viewport = domWindow.visualViewport; late double windowInnerHeight; if (viewport != null) { if (ui_web.browser.operatingSystem == ui_web.OperatingSystem.iOs && !isEditingOnMobile) { - windowInnerHeight = - domDocument.documentElement!.clientHeight * devicePixelRatio; + windowInnerHeight = domDocument.documentElement!.clientHeight * devicePixelRatio; } else { windowInnerHeight = viewport.height! * devicePixelRatio; } diff --git a/lib/web_ui/lib/src/engine/view_embedder/display_dpr_stream.dart b/lib/web_ui/lib/src/engine/view_embedder/display_dpr_stream.dart index d73eeb5aec244..4bb8d8fd26fa5 100644 --- a/lib/web_ui/lib/src/engine/view_embedder/display_dpr_stream.dart +++ b/lib/web_ui/lib/src/engine/view_embedder/display_dpr_stream.dart @@ -18,18 +18,15 @@ import 'package:ui/ui.dart' as ui show Display; /// /// See: https://developer.mozilla.org/en-US/docs/Web/API/Window_Management_API class DisplayDprStream { - DisplayDprStream( - this._display, { - @visibleForTesting DebugDisplayDprStreamOverrides? overrides, - }) : _currentDpr = _display.devicePixelRatio, - _debugOverrides = overrides { + DisplayDprStream(this._display, {@visibleForTesting DebugDisplayDprStreamOverrides? overrides}) + : _currentDpr = _display.devicePixelRatio, + _debugOverrides = overrides { // Start listening to DPR changes. _subscribeToMediaQuery(); } /// A singleton instance of DisplayDprStream. - static DisplayDprStream instance = - DisplayDprStream(EngineFlutterDisplay.instance); + static DisplayDprStream instance = DisplayDprStream(EngineFlutterDisplay.instance); // The display object that will provide the DPR information. final ui.Display _display; @@ -38,8 +35,7 @@ class DisplayDprStream { double _currentDpr; // Controls the [dprChanged] broadcast Stream. - final StreamController _dprStreamController = - StreamController.broadcast(); + final StreamController _dprStreamController = StreamController.broadcast(); // Object that fires a `change` event for the `_currentDpr`. late DomEventTarget _dprMediaQuery; @@ -86,8 +82,6 @@ class DisplayDprStream { @visibleForTesting class DebugDisplayDprStreamOverrides { - DebugDisplayDprStreamOverrides({ - this.getMediaQuery, - }); + DebugDisplayDprStreamOverrides({this.getMediaQuery}); final DomEventTarget Function(double currentValue)? getMediaQuery; } diff --git a/lib/web_ui/lib/src/engine/view_embedder/dom_manager.dart b/lib/web_ui/lib/src/engine/view_embedder/dom_manager.dart index 52de171182e71..1bccfff86a4d2 100644 --- a/lib/web_ui/lib/src/engine/view_embedder/dom_manager.dart +++ b/lib/web_ui/lib/src/engine/view_embedder/dom_manager.dart @@ -90,10 +90,7 @@ class DomManager { debugShowSemanticsNodes: configuration.debugShowSemanticsNodes, ); - StyleManager.styleSemanticsHost( - semanticsHost, - devicePixelRatio, - ); + StyleManager.styleSemanticsHost(semanticsHost, devicePixelRatio); return DomManager._( rootElement: rootElement, @@ -194,8 +191,10 @@ class DomManager { // to a PlatformViewStrategy class for each web-renderer backend? final DomElement? pv = PlatformViewManager.instance.getSlottedContent(platformViewId); if (pv == null) { - domWindow.console.debug('Failed to inject Platform View Id: $platformViewId. ' - 'Render seems to be happening before a `flutter/platform_views:create` platform message!'); + domWindow.console.debug( + 'Failed to inject Platform View Id: $platformViewId. ' + 'Render seems to be happening before a `flutter/platform_views:create` platform message!', + ); return; } // If pv is already a descendant of platformViewsHost -> noop diff --git a/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/full_page_embedding_strategy.dart b/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/full_page_embedding_strategy.dart index 90de0467d295d..82e14741c7d40 100644 --- a/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/full_page_embedding_strategy.dart +++ b/lib/web_ui/lib/src/engine/view_embedder/embedding_strategy/full_page_embedding_strategy.dart @@ -62,8 +62,9 @@ class FullPageEmbeddingStrategy implements EmbeddingStrategy { // Sets a meta viewport tag appropriate for Flutter Web in full screen. void _applyViewportMeta() { - for (final DomElement viewportMeta - in domDocument.head!.querySelectorAll('meta[name="viewport"]')) { + for (final DomElement viewportMeta in domDocument.head!.querySelectorAll( + 'meta[name="viewport"]', + )) { assert(() { // Filter out the meta tag that the engine placed on the page. This is // to avoid UI flicker during hot restart. Hot restart will clean up the @@ -82,11 +83,13 @@ class FullPageEmbeddingStrategy implements EmbeddingStrategy { // The meta viewport is always removed by the for method above, so we don't // need to do anything else here, other than create it again. - final DomHTMLMetaElement viewportMeta = createDomHTMLMetaElement() - ..setAttribute('flt-viewport', '') - ..name = 'viewport' - ..content = 'width=device-width, initial-scale=1.0, ' - 'maximum-scale=1.0, user-scalable=no'; + final DomHTMLMetaElement viewportMeta = + createDomHTMLMetaElement() + ..setAttribute('flt-viewport', '') + ..name = 'viewport' + ..content = + 'width=device-width, initial-scale=1.0, ' + 'maximum-scale=1.0, user-scalable=no'; domDocument.head!.append(viewportMeta); diff --git a/lib/web_ui/lib/src/engine/view_embedder/flutter_view_manager.dart b/lib/web_ui/lib/src/engine/view_embedder/flutter_view_manager.dart index 16a16d5192781..081710ed5c235 100644 --- a/lib/web_ui/lib/src/engine/view_embedder/flutter_view_manager.dart +++ b/lib/web_ui/lib/src/engine/view_embedder/flutter_view_manager.dart @@ -14,16 +14,17 @@ class FlutterViewManager { final Map _viewData = {}; // A map of (optional) JsFlutterViewOptions, indexed by their viewId. - final Map _jsViewOptions = - {}; + final Map _jsViewOptions = {}; // The controller of the [onViewCreated] stream. - final StreamController _onViewCreatedController = - StreamController.broadcast(sync: true); + final StreamController _onViewCreatedController = StreamController.broadcast( + sync: true, + ); // The controller of the [onViewDisposed] stream. - final StreamController _onViewDisposedController = - StreamController.broadcast(sync: true); + final StreamController _onViewDisposedController = StreamController.broadcast( + sync: true, + ); /// A stream of viewIds that will fire when a view is created. Stream get onViewCreated => _onViewCreatedController.stream; @@ -39,9 +40,7 @@ class FlutterViewManager { return _viewData[viewId]; } - EngineFlutterView createAndRegisterView( - JsFlutterViewOptions jsViewOptions, - ) { + EngineFlutterView createAndRegisterView(JsFlutterViewOptions jsViewOptions) { final EngineFlutterView view = EngineFlutterView( _dispatcher, jsViewOptions.hostElement, @@ -54,10 +53,7 @@ class FlutterViewManager { /// Stores a [view] and its (optional) [jsViewOptions], indexed by `viewId`. /// /// Returns the registered [view]. - EngineFlutterView registerView( - EngineFlutterView view, { - JsFlutterViewOptions? jsViewOptions, - }) { + EngineFlutterView registerView(EngineFlutterView view, {JsFlutterViewOptions? jsViewOptions}) { final int viewId = view.viewId; assert(!_viewData.containsKey(viewId)); // Adding the same view twice? @@ -113,7 +109,9 @@ class FlutterViewManager { return null; } - final String? viewIdAttribute = viewRoot.getAttribute(GlobalHtmlAttributes.flutterViewIdAttributeName); + final String? viewIdAttribute = viewRoot.getAttribute( + GlobalHtmlAttributes.flutterViewIdAttributeName, + ); assert(viewIdAttribute != null, 'Located Flutter view is missing its id attribute.'); final int? viewId = int.tryParse(viewIdAttribute!); @@ -171,10 +169,7 @@ class FlutterViewManager { /// the view. /// /// See: https://jsfiddle.net/ditman/1e2swpno for a JS focus transfer demo. - void _transferFocusToViewRoot( - DomElement element, { - bool removeElement = false, - }) { + void _transferFocusToViewRoot(DomElement element, {bool removeElement = false}) { final DomElement? activeElement = domDocument.activeElement; // If the element we're operating on is not active anymore (it can happen // when this method is called asynchronously), OR the element that we want diff --git a/lib/web_ui/lib/src/engine/view_embedder/global_html_attributes.dart b/lib/web_ui/lib/src/engine/view_embedder/global_html_attributes.dart index 1af573c93d207..e37d9816614b0 100644 --- a/lib/web_ui/lib/src/engine/view_embedder/global_html_attributes.dart +++ b/lib/web_ui/lib/src/engine/view_embedder/global_html_attributes.dart @@ -40,9 +40,7 @@ class GlobalHtmlAttributes { rootElement.setAttribute(flutterViewIdAttributeName, viewId); // How was the current renderer selected? - final String rendererSelection = autoDetectRenderer - ? 'auto-selected' - : 'requested explicitly'; + final String rendererSelection = autoDetectRenderer ? 'auto-selected' : 'requested explicitly'; hostElement.setAttribute('flt-renderer', '$rendererTag ($rendererSelection)'); hostElement.setAttribute('flt-build-mode', buildMode); diff --git a/lib/web_ui/lib/src/engine/view_embedder/style_manager.dart b/lib/web_ui/lib/src/engine/view_embedder/style_manager.dart index 5080b852c1d4a..ba1f0382fcb65 100644 --- a/lib/web_ui/lib/src/engine/view_embedder/style_manager.dart +++ b/lib/web_ui/lib/src/engine/view_embedder/style_manager.dart @@ -15,7 +15,8 @@ class StyleManager { static const String defaultFontWeight = 'normal'; static const double defaultFontSize = 14.0; static const String defaultFontFamily = 'sans-serif'; - static const String defaultCssFont = '$defaultFontStyle $defaultFontWeight ${defaultFontSize}px $defaultFontFamily'; + static const String defaultCssFont = + '$defaultFontStyle $defaultFontWeight ${defaultFontSize}px $defaultFontFamily'; static void attachGlobalStyles({ required DomNode node, @@ -34,10 +35,7 @@ class StyleManager { ); } - static void styleSceneHost( - DomElement sceneHost, { - bool debugShowSemanticsNodes = false, - }) { + static void styleSceneHost(DomElement sceneHost, {bool debugShowSemanticsNodes = false}) { assert(sceneHost.tagName.toLowerCase() == DomManager.sceneHostTagName.toLowerCase()); // Don't allow the scene to receive pointer events. sceneHost.style.pointerEvents = 'none'; @@ -48,10 +46,7 @@ class StyleManager { } } - static void styleSemanticsHost( - DomElement semanticsHost, - double devicePixelRatio, - ) { + static void styleSemanticsHost(DomElement semanticsHost, double devicePixelRatio) { assert(semanticsHost.tagName.toLowerCase() == DomManager.semanticsHostTagName.toLowerCase()); semanticsHost.style ..position = 'absolute' @@ -62,10 +57,7 @@ class StyleManager { /// The framework specifies semantics in physical pixels, but CSS uses /// logical pixels. To compensate, an inverse scale is injected at the root /// level. - static void scaleSemanticsHost( - DomElement semanticsHost, - double devicePixelRatio, - ) { + static void scaleSemanticsHost(DomElement semanticsHost, double devicePixelRatio) { assert(semanticsHost.tagName.toLowerCase() == DomManager.semanticsHostTagName.toLowerCase()); semanticsHost.style.transform = 'scale(${1 / devicePixelRatio})'; } @@ -83,7 +75,6 @@ void applyGlobalCssRulesToSheet( '$cssSelectorPrefix ${DomManager.sceneHostTagName} {' ' font: $defaultCssFont;' '}' - // This undoes browser's default painting and layout attributes of range // input, which is used in semantics. '$cssSelectorPrefix flt-semantics input[type=range] {' @@ -97,7 +88,6 @@ void applyGlobalCssRulesToSheet( ' bottom: 0;' ' left: 0;' '}' - // The invisible semantic text field may have a visible cursor and selection // highlight. The following 2 CSS rules force everything to be transparent. '$cssSelectorPrefix input::selection {' @@ -106,18 +96,15 @@ void applyGlobalCssRulesToSheet( '$cssSelectorPrefix textarea::selection {' ' background-color: transparent;' '}' - '$cssSelectorPrefix flt-semantics input,' '$cssSelectorPrefix flt-semantics textarea,' '$cssSelectorPrefix flt-semantics [contentEditable="true"] {' ' caret-color: transparent;' '}' - // Hide placeholder text '$cssSelectorPrefix .flt-text-editing::placeholder {' ' opacity: 0;' '}' - // Hide outline when the flutter-view root element is focused. '$cssSelectorPrefix:focus {' ' outline: none;' @@ -131,10 +118,9 @@ void applyGlobalCssRulesToSheet( '$cssSelectorPrefix * {' ' -webkit-tap-highlight-color: transparent;' '}' - '$cssSelectorPrefix flt-semantics input[type=range]::-webkit-slider-thumb {' ' -webkit-appearance: none;' - '}' + '}', ); } @@ -147,7 +133,7 @@ void applyGlobalCssRulesToSheet( '$cssSelectorPrefix flt-paragraph,' '$cssSelectorPrefix flt-span {' ' line-height: 100%;' - '}' + '}', ); } @@ -161,7 +147,7 @@ void applyGlobalCssRulesToSheet( '$cssSelectorPrefix .transparentTextEditing:-webkit-autofill:focus,' '$cssSelectorPrefix .transparentTextEditing:-webkit-autofill:active {' ' opacity: 0 !important;' - '}' + '}', ); } @@ -177,7 +163,7 @@ void applyGlobalCssRulesToSheet( styleElement.appendText( '$cssSelectorPrefix input::-ms-reveal {' ' display: none;' - '}' + '}', ); } on DomException catch (e) { // Browsers that don't understand ::-ms-reveal throw a DOMException @@ -188,7 +174,7 @@ void applyGlobalCssRulesToSheet( styleElement.appendText( '$cssSelectorPrefix input.fallback-for-fakey-browser-in-ci {' ' display: none;' - '}' + '}', ); return true; }()); diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index 0281aa7b7c287..f4cce74800c79 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -52,9 +52,8 @@ class EngineFlutterView implements ui.FlutterView { factory EngineFlutterView( EnginePlatformDispatcher platformDispatcher, DomElement hostElement, { - JsViewConstraints? viewConstraints, - } - ) = _EngineFlutterViewImpl; + JsViewConstraints? viewConstraints, + }) = _EngineFlutterViewImpl; EngineFlutterView._( this.viewId, @@ -63,11 +62,10 @@ class EngineFlutterView implements ui.FlutterView { // multi-view mode, the host element is required for each view (as reflected // by the public `EngineFlutterView` constructor). DomElement? hostElement, { - JsViewConstraints? viewConstraints, - } - ) : _jsViewConstraints = viewConstraints, - embeddingStrategy = EmbeddingStrategy.create(hostElement: hostElement), - dimensionsProvider = DimensionsProvider.create(hostElement: hostElement) { + JsViewConstraints? viewConstraints, + }) : _jsViewConstraints = viewConstraints, + embeddingStrategy = EmbeddingStrategy.create(hostElement: hostElement), + dimensionsProvider = DimensionsProvider.create(hostElement: hostElement) { // The embeddingStrategy will take care of cleaning up the rootElement on // hot restart. embeddingStrategy.attachViewRoot(dom.rootElement); @@ -310,11 +308,14 @@ class EngineFlutterView implements ui.FlutterView { // Return false if the previous dimensions are not set. if (_physicalSize != null) { // First confirm both height and width are effected. - if (_physicalSize!.height != newPhysicalSize.height && _physicalSize!.width != newPhysicalSize.width) { + if (_physicalSize!.height != newPhysicalSize.height && + _physicalSize!.width != newPhysicalSize.width) { // If prior to rotation height is bigger than width it should be the // opposite after the rotation and vice versa. - if ((_physicalSize!.height > _physicalSize!.width && newPhysicalSize.height < newPhysicalSize.width) || - (_physicalSize!.width > _physicalSize!.height && newPhysicalSize.width < newPhysicalSize.height)) { + if ((_physicalSize!.height > _physicalSize!.width && + newPhysicalSize.height < newPhysicalSize.width) || + (_physicalSize!.width > _physicalSize!.height && + newPhysicalSize.width < newPhysicalSize.height)) { // Rotation detected return true; } @@ -335,17 +336,14 @@ final class _EngineFlutterViewImpl extends EngineFlutterView { _EngineFlutterViewImpl( EnginePlatformDispatcher platformDispatcher, DomElement hostElement, { - JsViewConstraints? viewConstraints, - } - ) : super._(_nextViewId++, platformDispatcher, hostElement, viewConstraints: viewConstraints); + JsViewConstraints? viewConstraints, + }) : super._(_nextViewId++, platformDispatcher, hostElement, viewConstraints: viewConstraints); } /// The Web implementation of [ui.SingletonFlutterWindow]. final class EngineFlutterWindow extends EngineFlutterView implements ui.SingletonFlutterWindow { - EngineFlutterWindow._( - EnginePlatformDispatcher platformDispatcher, - DomElement? hostElement, - ) : super._(kImplicitViewId, platformDispatcher, hostElement) { + EngineFlutterWindow._(EnginePlatformDispatcher platformDispatcher, DomElement? hostElement) + : super._(kImplicitViewId, platformDispatcher, hostElement) { if (ui_web.isCustomUrlStrategySet) { _browserHistory = createHistoryForExistingState(ui_web.urlStrategy); } @@ -410,7 +408,8 @@ final class EngineFlutterWindow extends EngineFlutterView implements ui.Singleto ui.Brightness get platformBrightness => platformDispatcher.platformBrightness; @override - ui.VoidCallback? get onPlatformBrightnessChanged => platformDispatcher.onPlatformBrightnessChanged; + ui.VoidCallback? get onPlatformBrightnessChanged => + platformDispatcher.onPlatformBrightnessChanged; @override set onPlatformBrightnessChanged(ui.VoidCallback? callback) { platformDispatcher.onPlatformBrightnessChanged = callback; @@ -518,8 +517,7 @@ final class EngineFlutterWindow extends EngineFlutterView implements ui.Singleto /// Handles the browser history integration to allow users to use the back /// button, etc. BrowserHistory get browserHistory { - return _browserHistory ??= - createHistoryForExistingState(_urlStrategyForInitialization); + return _browserHistory ??= createHistoryForExistingState(_urlStrategyForInitialization); } ui_web.UrlStrategy? get _urlStrategyForInitialization { @@ -529,7 +527,7 @@ final class EngineFlutterWindow extends EngineFlutterView implements ui.Singleto } BrowserHistory? - _browserHistory; // Must be either SingleEntryBrowserHistory or MultiEntriesBrowserHistory. + _browserHistory; // Must be either SingleEntryBrowserHistory or MultiEntriesBrowserHistory. Future _useSingleEntryBrowserHistory() async { // Recreate the browser history mode that's appropriate for the existing @@ -541,8 +539,7 @@ final class EngineFlutterWindow extends EngineFlutterView implements ui.Singleto // with a single-entry history. // // See: https://github.com/flutter/flutter/issues/79241 - _browserHistory ??= - createHistoryForExistingState(_urlStrategyForInitialization); + _browserHistory ??= createHistoryForExistingState(_urlStrategyForInitialization); if (_browserHistory is SingleEntryBrowserHistory) { return; @@ -565,8 +562,7 @@ final class EngineFlutterWindow extends EngineFlutterView implements ui.Singleto // with a multi-entry history. // // See: https://github.com/flutter/flutter/issues/79241 - _browserHistory ??= - createHistoryForExistingState(_urlStrategyForInitialization); + _browserHistory ??= createHistoryForExistingState(_urlStrategyForInitialization); if (_browserHistory is MultiEntriesBrowserHistory) { return; @@ -719,18 +715,14 @@ EngineFlutterWindow get window { ); return _window!; } + EngineFlutterWindow? _window; /// Initializes the [window] (aka the implicit view), if it's not already /// initialized. -EngineFlutterWindow ensureImplicitViewInitialized({ - DomElement? hostElement, -}) { +EngineFlutterWindow ensureImplicitViewInitialized({DomElement? hostElement}) { if (_window == null) { - _window = EngineFlutterView.implicit( - EnginePlatformDispatcher.instance, - hostElement, - ); + _window = EngineFlutterView.implicit(EnginePlatformDispatcher.instance, hostElement); EnginePlatformDispatcher.instance.viewManager.registerView(_window!); } return _window!; @@ -777,8 +769,7 @@ class ViewConstraints implements ui.ViewConstraints { /// The resulting ViewConstraints object will be multiplied by devicePixelRatio /// later to compute the physicalViewConstraints, which is what the framework /// uses. - factory ViewConstraints.fromJs( - JsViewConstraints? constraints, ui.Size currentLogicalSize) { + factory ViewConstraints.fromJs(JsViewConstraints? constraints, ui.Size currentLogicalSize) { if (constraints == null) { return ViewConstraints.tight(currentLogicalSize); } @@ -801,14 +792,16 @@ class ViewConstraints implements ui.ViewConstraints { @override bool isSatisfiedBy(ui.Size size) { - return (minWidth <= size.width) && (size.width <= maxWidth) && - (minHeight <= size.height) && (size.height <= maxHeight); + return (minWidth <= size.width) && + (size.width <= maxWidth) && + (minHeight <= size.height) && + (size.height <= maxHeight); } @override bool get isTight => minWidth >= maxWidth && minHeight >= maxHeight; - ViewConstraints operator*(double factor) { + ViewConstraints operator *(double factor) { return ViewConstraints( minWidth: minWidth * factor, maxWidth: maxWidth * factor, @@ -818,7 +811,7 @@ class ViewConstraints implements ui.ViewConstraints { } @override - ViewConstraints operator/(double factor) { + ViewConstraints operator /(double factor) { return ViewConstraints( minWidth: minWidth / factor, maxWidth: maxWidth / factor, @@ -835,11 +828,11 @@ class ViewConstraints implements ui.ViewConstraints { if (other.runtimeType != runtimeType) { return false; } - return other is ViewConstraints - && other.minWidth == minWidth - && other.maxWidth == maxWidth - && other.minHeight == minHeight - && other.maxHeight == maxHeight; + return other is ViewConstraints && + other.minWidth == minWidth && + other.maxWidth == maxWidth && + other.minHeight == minHeight && + other.maxHeight == maxHeight; } @override @@ -850,8 +843,10 @@ class ViewConstraints implements ui.ViewConstraints { if (minWidth == double.infinity && minHeight == double.infinity) { return 'ViewConstraints(biggest)'; } - if (minWidth == 0 && maxWidth == double.infinity && - minHeight == 0 && maxHeight == double.infinity) { + if (minWidth == 0 && + maxWidth == double.infinity && + minHeight == 0 && + maxHeight == double.infinity) { return 'ViewConstraints(unconstrained)'; } String describe(double min, double max, String dim) { @@ -860,6 +855,7 @@ class ViewConstraints implements ui.ViewConstraints { } return '${min.toStringAsFixed(1)}<=$dim<=${max.toStringAsFixed(1)}'; } + final String width = describe(minWidth, maxWidth, 'w'); final String height = describe(minHeight, maxHeight, 'h'); return 'ViewConstraints($width, $height)'; diff --git a/lib/web_ui/lib/text.dart b/lib/web_ui/lib/text.dart index 42391c6bee380..240883e653913 100644 --- a/lib/web_ui/lib/text.dart +++ b/lib/web_ui/lib/text.dart @@ -9,19 +9,9 @@ part of ui; // To change the sentinel value, search for "kTextHeightNone" in the source code. const double kTextHeightNone = 0.0; -enum FontStyle { - normal, - italic, -} +enum FontStyle { normal, italic } -enum PlaceholderAlignment { - baseline, - aboveBaseline, - belowBaseline, - top, - bottom, - middle, -} +enum PlaceholderAlignment { baseline, aboveBaseline, belowBaseline, top, bottom, middle } class FontWeight { const FontWeight._(this.index, this.value); @@ -47,15 +37,14 @@ class FontWeight { w600, w700, w800, - w900 + w900, ]; static FontWeight? lerp(FontWeight? a, FontWeight? b, double t) { if (a == null && b == null) { return null; } return values[engine.clampInt( - lerpDouble(a?.index ?? normal.index, b?.index ?? normal.index, t)! - .round(), + lerpDouble(a?.index ?? normal.index, b?.index ?? normal.index, t)!.round(), 0, 8, )]; @@ -79,88 +68,43 @@ class FontWeight { class FontFeature { const FontFeature(this.feature, [this.value = 1]) - : assert(feature.length == 4, - 'Feature tag must be exactly four characters long.'), - assert(value >= 0, 'Feature value must be zero or a positive integer.'); + : assert(feature.length == 4, 'Feature tag must be exactly four characters long.'), + assert(value >= 0, 'Feature value must be zero or a positive integer.'); const FontFeature.enable(String feature) : this(feature, 1); const FontFeature.disable(String feature) : this(feature, 0); const FontFeature.alternative(this.value) : feature = 'aalt'; - const FontFeature.alternativeFractions() - : feature = 'afrc', - value = 1; - const FontFeature.contextualAlternates() - : feature = 'calt', - value = 1; - const FontFeature.caseSensitiveForms() - : feature = 'case', - value = 1; + const FontFeature.alternativeFractions() : feature = 'afrc', value = 1; + const FontFeature.contextualAlternates() : feature = 'calt', value = 1; + const FontFeature.caseSensitiveForms() : feature = 'case', value = 1; factory FontFeature.characterVariant(int value) { assert(value >= 1); assert(value <= 20); return FontFeature('cv${value.toString().padLeft(2, "0")}'); } - const FontFeature.denominator() - : feature = 'dnom', - value = 1; - const FontFeature.fractions() - : feature = 'frac', - value = 1; - const FontFeature.historicalForms() - : feature = 'hist', - value = 1; - const FontFeature.historicalLigatures() - : feature = 'hlig', - value = 1; - const FontFeature.liningFigures() - : feature = 'lnum', - value = 1; - const FontFeature.localeAware({bool enable = true}) - : feature = 'locl', - value = enable ? 1 : 0; - const FontFeature.notationalForms([this.value = 1]) - : feature = 'nalt', - assert(value >= 0); - const FontFeature.numerators() - : feature = 'numr', - value = 1; - const FontFeature.oldstyleFigures() - : feature = 'onum', - value = 1; - const FontFeature.ordinalForms() - : feature = 'ordn', - value = 1; - const FontFeature.proportionalFigures() - : feature = 'pnum', - value = 1; - const FontFeature.randomize() - : feature = 'rand', - value = 1; - const FontFeature.stylisticAlternates() - : feature = 'salt', - value = 1; - const FontFeature.scientificInferiors() - : feature = 'sinf', - value = 1; + const FontFeature.denominator() : feature = 'dnom', value = 1; + const FontFeature.fractions() : feature = 'frac', value = 1; + const FontFeature.historicalForms() : feature = 'hist', value = 1; + const FontFeature.historicalLigatures() : feature = 'hlig', value = 1; + const FontFeature.liningFigures() : feature = 'lnum', value = 1; + const FontFeature.localeAware({bool enable = true}) : feature = 'locl', value = enable ? 1 : 0; + const FontFeature.notationalForms([this.value = 1]) : feature = 'nalt', assert(value >= 0); + const FontFeature.numerators() : feature = 'numr', value = 1; + const FontFeature.oldstyleFigures() : feature = 'onum', value = 1; + const FontFeature.ordinalForms() : feature = 'ordn', value = 1; + const FontFeature.proportionalFigures() : feature = 'pnum', value = 1; + const FontFeature.randomize() : feature = 'rand', value = 1; + const FontFeature.stylisticAlternates() : feature = 'salt', value = 1; + const FontFeature.scientificInferiors() : feature = 'sinf', value = 1; factory FontFeature.stylisticSet(int value) { assert(value >= 1); assert(value <= 20); return FontFeature('ss${value.toString().padLeft(2, "0")}'); } - const FontFeature.subscripts() - : feature = 'subs', - value = 1; - const FontFeature.superscripts() - : feature = 'sups', - value = 1; - const FontFeature.swash([this.value = 1]) - : feature = 'swsh', - assert(value >= 0); - const FontFeature.tabularFigures() - : feature = 'tnum', - value = 1; - const FontFeature.slashedZero() - : feature = 'zero', - value = 1; + const FontFeature.subscripts() : feature = 'subs', value = 1; + const FontFeature.superscripts() : feature = 'sups', value = 1; + const FontFeature.swash([this.value = 1]) : feature = 'swsh', assert(value >= 0); + const FontFeature.tabularFigures() : feature = 'tnum', value = 1; + const FontFeature.slashedZero() : feature = 'zero', value = 1; final String feature; final int value; @@ -170,9 +114,7 @@ class FontFeature { if (other.runtimeType != runtimeType) { return false; } - return other is FontFeature && - other.feature == feature && - other.value == value; + return other is FontFeature && other.feature == feature && other.value == value; } @override @@ -183,15 +125,22 @@ class FontFeature { } class FontVariation { - const FontVariation( - this.axis, - this.value, - ) : assert(axis.length == 4, 'Axis tag must be exactly four characters long.'), - assert(value >= -32768.0 && value < 32768.0, 'Value must be representable as a signed 16.16 fixed-point number, i.e. it must be in this range: -32768.0 ≤ value < 32768.0'); + const FontVariation(this.axis, this.value) + : assert(axis.length == 4, 'Axis tag must be exactly four characters long.'), + assert( + value >= -32768.0 && value < 32768.0, + 'Value must be representable as a signed 16.16 fixed-point number, i.e. it must be in this range: -32768.0 ≤ value < 32768.0', + ); - const FontVariation.italic(this.value) : assert(value >= 0.0), assert(value <= 1.0), axis = 'ital'; + const FontVariation.italic(this.value) + : assert(value >= 0.0), + assert(value <= 1.0), + axis = 'ital'; const FontVariation.opticalSize(this.value) : assert(value > 0.0), axis = 'opsz'; - const FontVariation.slant(this.value) : assert(value > -90.0), assert(value < 90.0), axis = 'slnt'; + const FontVariation.slant(this.value) + : assert(value > -90.0), + assert(value < 90.0), + axis = 'slnt'; const FontVariation.width(this.value) : assert(value >= 0.0), axis = 'wdth'; const FontVariation.weight(this.value) : assert(value >= 1), assert(value <= 1000), axis = 'wght'; @@ -203,9 +152,7 @@ class FontVariation { if (other.runtimeType != runtimeType) { return false; } - return other is FontVariation - && other.axis == axis - && other.value == value; + return other is FontVariation && other.axis == axis && other.value == value; } @override @@ -217,7 +164,7 @@ class FontVariation { } return FontVariation( a!.axis, - clampDouble(lerpDouble(a.value, b!.value, t)!, -32768.0, 32768.0 - 1.0/65536.0), + clampDouble(lerpDouble(a.value, b!.value, t)!, -32768.0, 32768.0 - 1.0 / 65536.0), ); } @@ -226,7 +173,11 @@ class FontVariation { } final class GlyphInfo { - GlyphInfo(this.graphemeClusterLayoutBounds, this.graphemeClusterCodeUnitRange, this.writingDirection); + GlyphInfo( + this.graphemeClusterLayoutBounds, + this.graphemeClusterCodeUnitRange, + this.writingDirection, + ); final Rect graphemeClusterLayoutBounds; final TextRange graphemeClusterCodeUnitRange; @@ -237,33 +188,25 @@ final class GlyphInfo { if (identical(this, other)) { return true; } - return other is GlyphInfo - && graphemeClusterLayoutBounds == other.graphemeClusterLayoutBounds - && graphemeClusterCodeUnitRange == other.graphemeClusterCodeUnitRange - && writingDirection == other.writingDirection; + return other is GlyphInfo && + graphemeClusterLayoutBounds == other.graphemeClusterLayoutBounds && + graphemeClusterCodeUnitRange == other.graphemeClusterCodeUnitRange && + writingDirection == other.writingDirection; } @override - int get hashCode => Object.hash(graphemeClusterLayoutBounds, graphemeClusterCodeUnitRange, writingDirection); + int get hashCode => + Object.hash(graphemeClusterLayoutBounds, graphemeClusterCodeUnitRange, writingDirection); @override - String toString() => 'Glyph($graphemeClusterLayoutBounds, textRange: $graphemeClusterCodeUnitRange, direction: $writingDirection)'; + String toString() => + 'Glyph($graphemeClusterLayoutBounds, textRange: $graphemeClusterCodeUnitRange, direction: $writingDirection)'; } // The order of this enum must match the order of the values in RenderStyleConstants.h's ETextAlign. -enum TextAlign { - left, - right, - center, - justify, - start, - end, -} +enum TextAlign { left, right, center, justify, start, end } -enum TextBaseline { - alphabetic, - ideographic, -} +enum TextBaseline { alphabetic, ideographic } class TextDecoration { const TextDecoration._(this._mask); @@ -320,10 +263,7 @@ class TextDecoration { enum TextDecorationStyle { solid, double, dotted, dashed, wavy } -enum TextLeadingDistribution { - proportional, - even, -} +enum TextLeadingDistribution { proportional, even } class TextHeightBehavior { const TextHeightBehavior({ @@ -348,10 +288,7 @@ class TextHeightBehavior { @override int get hashCode { - return Object.hash( - applyHeightToFirstAscent, - applyHeightToLastDescent, - ); + return Object.hash(applyHeightToFirstAscent, applyHeightToLastDescent); } @override @@ -503,19 +440,10 @@ abstract class StrutStyle { } // The order of this enum must match the order of the values in TextDirection.h's TextDirection. -enum TextDirection { - rtl, - ltr, -} +enum TextDirection { rtl, ltr } class TextBox { - const TextBox.fromLTRBD( - this.left, - this.top, - this.right, - this.bottom, - this.direction, - ); + const TextBox.fromLTRBD(this.left, this.top, this.right, this.bottom, this.direction); final double left; final double top; final double right; @@ -555,16 +483,10 @@ class TextBox { } } -enum TextAffinity { - upstream, - downstream, -} +enum TextAffinity { upstream, downstream } class TextPosition { - const TextPosition({ - required this.offset, - this.affinity = TextAffinity.downstream, - }); + const TextPosition({required this.offset, this.affinity = TextAffinity.downstream}); final int offset; final TextAffinity affinity; @@ -573,9 +495,7 @@ class TextPosition { if (other.runtimeType != runtimeType) { return false; } - return other is TextPosition && - other.offset == offset && - other.affinity == affinity; + return other is TextPosition && other.offset == offset && other.affinity == affinity; } @override @@ -588,15 +508,10 @@ class TextPosition { } class TextRange { - const TextRange({ - required this.start, - required this.end, - }) : assert(start >= -1), - assert(end >= -1); - const TextRange.collapsed(int offset) - : assert(offset >= -1), - start = offset, - end = offset; + const TextRange({required this.start, required this.end}) + : assert(start >= -1), + assert(end >= -1); + const TextRange.collapsed(int offset) : assert(offset >= -1), start = offset, end = offset; static const TextRange empty = TextRange(start: -1, end: -1); final int start; final int end; @@ -627,19 +542,14 @@ class TextRange { } @override - int get hashCode => Object.hash( - start.hashCode, - end.hashCode, - ); + int get hashCode => Object.hash(start.hashCode, end.hashCode); @override String toString() => 'TextRange(start: $start, end: $end)'; } class ParagraphConstraints { - const ParagraphConstraints({ - required this.width, - }); + const ParagraphConstraints({required this.width}); final double width; @override @@ -717,9 +627,12 @@ abstract class Paragraph { double get ideographicBaseline; bool get didExceedMaxLines; void layout(ParagraphConstraints constraints); - List getBoxesForRange(int start, int end, - {BoxHeightStyle boxHeightStyle = BoxHeightStyle.tight, - BoxWidthStyle boxWidthStyle = BoxWidthStyle.tight}); + List getBoxesForRange( + int start, + int end, { + BoxHeightStyle boxHeightStyle = BoxHeightStyle.tight, + BoxWidthStyle boxWidthStyle = BoxWidthStyle.tight, + }); TextPosition getPositionForOffset(Offset offset); GlyphInfo? getGlyphInfoAt(int codeUnitOffset); GlyphInfo? getClosestGlyphInfoForOffset(Offset offset); @@ -735,8 +648,7 @@ abstract class Paragraph { } abstract class ParagraphBuilder { - factory ParagraphBuilder(ParagraphStyle style) => - engine.renderer.createParagraphBuilder(style); + factory ParagraphBuilder(ParagraphStyle style) => engine.renderer.createParagraphBuilder(style); void pushStyle(TextStyle style); void pop(); diff --git a/lib/web_ui/lib/tile_mode.dart b/lib/web_ui/lib/tile_mode.dart index 2f98e128e5c78..d7a0308bcd76f 100644 --- a/lib/web_ui/lib/tile_mode.dart +++ b/lib/web_ui/lib/tile_mode.dart @@ -5,9 +5,4 @@ part of ui; // These enum values must be kept in sync with SkShader::TileMode. -enum TileMode { - clamp, - repeated, - mirror, - decal, -} +enum TileMode { clamp, repeated, mirror, decal } diff --git a/lib/web_ui/lib/ui_web/src/ui_web/asset_manager.dart b/lib/web_ui/lib/ui_web/src/ui_web/asset_manager.dart index 53788bdd368be..666b279a9b400 100644 --- a/lib/web_ui/lib/ui_web/src/ui_web/asset_manager.dart +++ b/lib/web_ui/lib/ui_web/src/ui_web/asset_manager.dart @@ -18,14 +18,12 @@ AssetManager get assetManager => engineAssetManager; /// By default, URLs are relative to the `` of the current website. class AssetManager { /// Initializes [AssetManager] with paths. - AssetManager({ - this.assetsDir = _defaultAssetsDir, - String? assetBase, - }) : assert( - assetBase == null || assetBase.endsWith('/'), - '`assetBase` must end with a `/` character.', - ), - _assetBase = assetBase; + AssetManager({this.assetsDir = _defaultAssetsDir, String? assetBase}) + : assert( + assetBase == null || assetBase.endsWith('/'), + '`assetBase` must end with a `/` character.', + ), + _assetBase = assetBase; static const String _defaultAssetsDir = 'assets'; @@ -42,16 +40,18 @@ class AssetManager { // // This warns the user and points them to the new initializeEngine style. String? get _deprecatedAssetBase { - final DomHTMLMetaElement? meta = domWindow.document - .querySelector('meta[name=assetBase]') as DomHTMLMetaElement?; + final DomHTMLMetaElement? meta = + domWindow.document.querySelector('meta[name=assetBase]') as DomHTMLMetaElement?; final String? fallbackBaseUrl = meta?.content; if (fallbackBaseUrl != null) { // Warn users that they're using a deprecated configuration style... - domWindow.console.warn('The `assetBase` meta tag is now deprecated.\n' - 'Use engineInitializer.initializeEngine(config) instead.\n' - 'See: https://docs.flutter.dev/development/platform-integration/web/initialization'); + domWindow.console.warn( + 'The `assetBase` meta tag is now deprecated.\n' + 'Use engineInitializer.initializeEngine(config) instead.\n' + 'See: https://docs.flutter.dev/development/platform-integration/web/initialization', + ); } return fallbackBaseUrl; } diff --git a/lib/web_ui/lib/ui_web/src/ui_web/browser_detection.dart b/lib/web_ui/lib/ui_web/src/ui_web/browser_detection.dart index 0f8bc7efc7b6f..a3d816e45530e 100644 --- a/lib/web_ui/lib/ui_web/src/ui_web/browser_detection.dart +++ b/lib/web_ui/lib/ui_web/src/ui_web/browser_detection.dart @@ -14,8 +14,10 @@ enum BrowserEngine { /// Blink is assumed in case when a more precise browser engine wasn't /// detected. blink, + /// The engine that powers Safari. webkit, + /// The engine that powers Firefox. firefox, } @@ -27,14 +29,19 @@ enum BrowserEngine { enum OperatingSystem { /// iOS: iOs, + /// Android: android, + /// Linux: linux, + /// Windows: windows, + /// MacOs: macOs, + /// We were unable to detect the current operating system. unknown, } @@ -114,7 +121,8 @@ class BrowserDetection { // Assume Blink otherwise, but issue a warning. print( - 'WARNING: failed to detect current browser engine. Assuming this is a Chromium-compatible browser.'); + 'WARNING: failed to detect current browser engine. Assuming this is a Chromium-compatible browser.', + ); return BrowserEngine.blink; } @@ -139,18 +147,14 @@ class BrowserDetection { /// Detects operating system using platform and UA used for unit testing. @visibleForTesting - OperatingSystem detectOperatingSystem({ - String? overridePlatform, - int? overrideMaxTouchPoints, - }) { + OperatingSystem detectOperatingSystem({String? overridePlatform, int? overrideMaxTouchPoints}) { final String platform = overridePlatform ?? domWindow.navigator.platform!; if (platform.startsWith('Mac')) { // iDevices requesting a "desktop site" spoof their UA so it looks like a Mac. // This checks if we're in a touch device, or on a real mac. - final int maxTouchPoints = overrideMaxTouchPoints ?? - domWindow.navigator.maxTouchPoints?.toInt() ?? - 0; + final int maxTouchPoints = + overrideMaxTouchPoints ?? domWindow.navigator.maxTouchPoints?.toInt() ?? 0; if (maxTouchPoints > 2) { return OperatingSystem.iOs; } diff --git a/lib/web_ui/lib/ui_web/src/ui_web/flutter_views_proxy.dart b/lib/web_ui/lib/ui_web/src/ui_web/flutter_views_proxy.dart index f19ee0344f54c..ba851dbd0e607 100644 --- a/lib/web_ui/lib/ui_web/src/ui_web/flutter_views_proxy.dart +++ b/lib/web_ui/lib/ui_web/src/ui_web/flutter_views_proxy.dart @@ -11,9 +11,7 @@ final FlutterViewManagerProxy views = FlutterViewManagerProxy( /// This class exposes web-only attributes for the registered views in a multi-view app. class FlutterViewManagerProxy { - FlutterViewManagerProxy({ - required FlutterViewManager viewManager, - }) : _viewManager = viewManager; + FlutterViewManagerProxy({required FlutterViewManager viewManager}) : _viewManager = viewManager; // The proxied viewManager instance. final FlutterViewManager _viewManager; diff --git a/lib/web_ui/lib/ui_web/src/ui_web/images.dart b/lib/web_ui/lib/ui_web/src/ui_web/images.dart index a21900200a637..eb53016ba93fb 100644 --- a/lib/web_ui/lib/ui_web/src/ui_web/images.dart +++ b/lib/web_ui/lib/ui_web/src/ui_web/images.dart @@ -10,23 +10,14 @@ import 'package:ui/ui.dart' as ui; /// Signature of the callback that receives progress updates as image chunks are /// loaded. -typedef ImageCodecChunkCallback = void Function( - int cumulativeBytesLoaded, - int expectedTotalBytes, -); +typedef ImageCodecChunkCallback = void Function(int cumulativeBytesLoaded, int expectedTotalBytes); /// Creates a [ui.Codec] for the image located at [uri]. /// /// The [chunkCallback] is called with progress updates as image chunks are /// loaded. -Future createImageCodecFromUrl( - Uri uri, { - ImageCodecChunkCallback? chunkCallback, -}) { - return renderer.instantiateImageCodecFromUrl( - uri, - chunkCallback: chunkCallback, - ); +Future createImageCodecFromUrl(Uri uri, {ImageCodecChunkCallback? chunkCallback}) { + return renderer.instantiateImageCodecFromUrl(uri, chunkCallback: chunkCallback); } /// Creates a [ui.Image] from an ImageBitmap object. @@ -40,9 +31,7 @@ FutureOr createImageFromImageBitmap(JSAny imageSource) { if (!domInstanceOfString(imageSource, 'ImageBitmap')) { throw ArgumentError('Image source $imageSource is not an ImageBitmap.', 'imageSource'); } - return renderer.createImageFromImageBitmap( - imageSource as DomImageBitmap, - ); + return renderer.createImageFromImageBitmap(imageSource as DomImageBitmap); } /// Creates a [ui.Image] from a valid texture source (for example @@ -53,8 +42,16 @@ FutureOr createImageFromImageBitmap(JSAny imageSource) { /// will be made. If this is not desired, the ownership of the object can be /// transferred to the renderer and the engine will take ownership of the /// texture source and consume its contents. -FutureOr createImageFromTextureSource(JSAny object, { required int width, required int height, bool transferOwnership = false }) { +FutureOr createImageFromTextureSource( + JSAny object, { + required int width, + required int height, + bool transferOwnership = false, +}) { return renderer.createImageFromTextureSource( - object, width: width, height: height, transferOwnership: transferOwnership + object, + width: width, + height: height, + transferOwnership: transferOwnership, ); } diff --git a/lib/web_ui/lib/ui_web/src/ui_web/initialization.dart b/lib/web_ui/lib/ui_web/src/ui_web/initialization.dart index ad42a26dae677..4e7aa1b2b02af 100644 --- a/lib/web_ui/lib/ui_web/src/ui_web/initialization.dart +++ b/lib/web_ui/lib/ui_web/src/ui_web/initialization.dart @@ -25,15 +25,13 @@ import 'package:ui/ui.dart' as ui; /// `flutter.js` does it: /// /// * https://github.com/flutter/flutter/blob/95be76ab7e3dca2def54454313e97f94f4ac4582/packages/flutter_tools/lib/src/web/file_generators/js/flutter.js -Future bootstrapEngine({ - ui.VoidCallback? registerPlugins, - ui.VoidCallback? runApp, -}) async { +Future bootstrapEngine({ui.VoidCallback? registerPlugins, ui.VoidCallback? runApp}) async { // Create the object that knows how to bootstrap an app from JS and Dart. final AppBootstrap bootstrap = AppBootstrap( initializeEngine: ([JsFlutterConfiguration? configuration]) async { await initializeEngineServices(jsConfiguration: configuration); - }, runApp: () async { + }, + runApp: () async { if (registerPlugins != null) { registerPlugins(); } diff --git a/lib/web_ui/lib/ui_web/src/ui_web/navigation/url_strategy.dart b/lib/web_ui/lib/ui_web/src/ui_web/navigation/url_strategy.dart index 9760f005bf92f..a9b2af0fa59fe 100644 --- a/lib/web_ui/lib/ui_web/src/ui_web/navigation/url_strategy.dart +++ b/lib/web_ui/lib/ui_web/src/ui_web/navigation/url_strategy.dart @@ -11,9 +11,10 @@ import 'package:ui/ui.dart' as ui; import '../testing.dart'; import 'platform_location.dart'; -UrlStrategy _realDefaultUrlStrategy = debugEmulateFlutterTesterEnvironment - ? TestUrlStrategy.fromEntry(const TestHistoryEntry('default', null, '/')) - : const HashUrlStrategy(); +UrlStrategy _realDefaultUrlStrategy = + debugEmulateFlutterTesterEnvironment + ? TestUrlStrategy.fromEntry(const TestHistoryEntry('default', null, '/')) + : const HashUrlStrategy(); UrlStrategy get _defaultUrlStrategy => debugDefaultUrlStrategyOverride ?? _realDefaultUrlStrategy; @@ -43,8 +44,7 @@ UrlStrategy? _customUrlStrategy; /// Returns the present [UrlStrategy] for handling the browser URL. /// /// Returns null when the browser integration has been manually disabled. -UrlStrategy? get urlStrategy => - isCustomUrlStrategySet ? _customUrlStrategy : _defaultUrlStrategy; +UrlStrategy? get urlStrategy => isCustomUrlStrategySet ? _customUrlStrategy : _defaultUrlStrategy; /// Sets a custom URL strategy instead of the default one. /// @@ -141,8 +141,7 @@ class HashUrlStrategy implements UrlStrategy { /// /// The [PlatformLocation] parameter is useful for testing to mock out browser /// interactions. - const HashUrlStrategy( - [this._platformLocation = const BrowserPlatformLocation()]); + const HashUrlStrategy([this._platformLocation = const BrowserPlatformLocation()]); final PlatformLocation _platformLocation; @@ -152,6 +151,7 @@ class HashUrlStrategy implements UrlStrategy { // `fn` expects `event.state`, not a `DomEvent`. fn((event as DomPopStateEvent).state); } + _platformLocation.addPopStateListener(wrappedFn); return () => _platformLocation.removePopStateListener(wrappedFn); } diff --git a/lib/web_ui/lib/ui_web/src/ui_web/platform_view_registry.dart b/lib/web_ui/lib/ui_web/src/ui_web/platform_view_registry.dart index fc17cfb1ee3d3..eb4e9abcaafa6 100644 --- a/lib/web_ui/lib/ui_web/src/ui_web/platform_view_registry.dart +++ b/lib/web_ui/lib/ui_web/src/ui_web/platform_view_registry.dart @@ -6,10 +6,7 @@ import 'package:ui/src/engine.dart'; /// A function which takes a unique `id` and some `params` and creates an HTML /// element. -typedef ParameterizedPlatformViewFactory = Object Function( - int viewId, { - Object? params, -}); +typedef ParameterizedPlatformViewFactory = Object Function(int viewId, {Object? params}); /// A function which takes a unique `id` and creates an HTML element. typedef PlatformViewFactory = Object Function(int viewId); @@ -37,11 +34,7 @@ class PlatformViewRegistry { /// /// [viewFactory] can be any function that takes an integer and optional /// `params` and returns an `HTMLElement` DOM object. - bool registerViewFactory( - String viewType, - Function viewFactory, { - bool isVisible = true, - }) { + bool registerViewFactory(String viewType, Function viewFactory, {bool isVisible = true}) { return PlatformViewManager.instance.registerFactory( viewType, viewFactory, diff --git a/lib/web_ui/lib/ui_web/src/ui_web/testing.dart b/lib/web_ui/lib/ui_web/src/ui_web/testing.dart index ccf19efdfe3d0..f4ce510eb0f7b 100644 --- a/lib/web_ui/lib/ui_web/src/ui_web/testing.dart +++ b/lib/web_ui/lib/ui_web/src/ui_web/testing.dart @@ -24,8 +24,7 @@ void debugOverrideDevicePixelRatio(double? value) { /// When true, the engine will emulate a specific screen size, and always /// use the "Ahem" font to reduce test flakiness and dependence on the test /// environment. -bool get debugEmulateFlutterTesterEnvironment => - _debugEmulateFlutterTesterEnvironment; +bool get debugEmulateFlutterTesterEnvironment => _debugEmulateFlutterTesterEnvironment; /// Sets whether the Flutter engine is running in `flutter test` emulation mode. set debugEmulateFlutterTesterEnvironment(bool value) { diff --git a/lib/web_ui/lib/window.dart b/lib/web_ui/lib/window.dart index d42bbf7854395..6abd944be64ff 100644 --- a/lib/web_ui/lib/window.dart +++ b/lib/web_ui/lib/window.dart @@ -99,11 +99,7 @@ abstract class SingletonFlutterWindow extends FlutterView { VoidCallback? get onAccessibilityFeaturesChanged; set onAccessibilityFeaturesChanged(VoidCallback? callback); - void sendPlatformMessage( - String name, - ByteData? data, - PlatformMessageResponseCallback? callback, - ); + void sendPlatformMessage(String name, ByteData? data, PlatformMessageResponseCallback? callback); PlatformMessageCallback? get onPlatformMessage; set onPlatformMessage(PlatformMessageCallback? callback); @@ -121,10 +117,7 @@ abstract class AccessibilityFeatures { bool get onOffSwitchLabels; } -enum Brightness { - dark, - light, -} +enum Brightness { dark, light } // Unimplemented classes. // TODO(dit): see https://github.com/flutter/flutter/issues/33614. @@ -177,19 +170,13 @@ class FrameData { } class GestureSettings { - const GestureSettings({ - this.physicalTouchSlop, - this.physicalDoubleTapSlop, - }); + const GestureSettings({this.physicalTouchSlop, this.physicalDoubleTapSlop}); final double? physicalTouchSlop; final double? physicalDoubleTapSlop; - GestureSettings copyWith({ - double? physicalTouchSlop, - double? physicalDoubleTapSlop, - }) { + GestureSettings copyWith({double? physicalTouchSlop, double? physicalDoubleTapSlop}) { return GestureSettings( physicalTouchSlop: physicalTouchSlop ?? this.physicalTouchSlop, physicalDoubleTapSlop: physicalDoubleTapSlop ?? this.physicalDoubleTapSlop, @@ -202,13 +189,14 @@ class GestureSettings { return false; } return other is GestureSettings && - other.physicalTouchSlop == physicalTouchSlop && - other.physicalDoubleTapSlop == physicalDoubleTapSlop; + other.physicalTouchSlop == physicalTouchSlop && + other.physicalDoubleTapSlop == physicalDoubleTapSlop; } @override int get hashCode => Object.hash(physicalTouchSlop, physicalDoubleTapSlop); @override - String toString() => 'GestureSettings(physicalTouchSlop: $physicalTouchSlop, physicalDoubleTapSlop: $physicalDoubleTapSlop)'; + String toString() => + 'GestureSettings(physicalTouchSlop: $physicalTouchSlop, physicalDoubleTapSlop: $physicalDoubleTapSlop)'; } diff --git a/lib/web_ui/test/canvaskit/backdrop_filter_golden_test.dart b/lib/web_ui/test/canvaskit/backdrop_filter_golden_test.dart index d297e7b6ec7f0..d8c4d5a256ff7 100644 --- a/lib/web_ui/test/canvaskit/backdrop_filter_golden_test.dart +++ b/lib/web_ui/test/canvaskit/backdrop_filter_golden_test.dart @@ -40,9 +40,7 @@ void testMain() { for (int row = 0; row < rows; row++) { for (int column = 0; column < 10; column++) { final ui.Rect rect = ui.Rect.fromLTWH( - row.isEven - ? (column * 2) * sideLength - : (column * 2 + 1) * sideLength, + row.isEven ? (column * 2) * sideLength : (column * 2 + 1) * sideLength, row * sideLength, sideLength, sideLength, @@ -57,8 +55,10 @@ void testMain() { builder.addPicture(ui.Offset.zero, checkerboard); builder.pushBackdropFilter(ui.ImageFilter.blur(sigmaX: 10, sigmaY: 10)); await matchSceneGolden( - 'canvaskit_backdropfilter_blur_edges.png', builder.build(), - region: region); + 'canvaskit_backdropfilter_blur_edges.png', + builder.build(), + region: region, + ); }); test('ImageFilter with ColorFilter as child', () async { final LayerSceneBuilder builder = LayerSceneBuilder(); @@ -69,7 +69,9 @@ void testMain() { final CkPictureRecorder recorder = CkPictureRecorder(); final CkCanvas canvas = recorder.beginRecording(region); final ui.ColorFilter colorFilter = ui.ColorFilter.mode( - const ui.Color(0XFF00FF00).withOpacity(0.55), ui.BlendMode.darken); + const ui.Color(0XFF00FF00).withOpacity(0.55), + ui.BlendMode.darken, + ); // using a colorFilter as an imageFilter for backDrop filter builder.pushBackdropFilter(colorFilter); @@ -81,9 +83,10 @@ void testMain() { final CkPicture redCircle1 = recorder.endRecording(); builder.addPicture(ui.Offset.zero, redCircle1); await matchSceneGolden( - 'canvaskit_red_circle_green_backdrop_colorFilter.png', - builder.build(), - region: region); + 'canvaskit_red_circle_green_backdrop_colorFilter.png', + builder.build(), + region: region, + ); }); test('works with an invisible platform view inside', () async { @@ -104,9 +107,7 @@ void testMain() { for (int row = 0; row < rows; row++) { for (int column = 0; column < 10; column++) { final ui.Rect rect = ui.Rect.fromLTWH( - row.isEven - ? (column * 2) * sideLength - : (column * 2 + 1) * sideLength, + row.isEven ? (column * 2) * sideLength : (column * 2 + 1) * sideLength, row * sideLength, sideLength, sideLength, @@ -127,22 +128,26 @@ void testMain() { final CkCanvas greenRectCanvas = greenRectRecorder.beginRecording(region); final CkPaint greenPaint = CkPaint()..color = const ui.Color(0xff00ff00); greenRectCanvas.drawRect( - ui.Rect.fromCenter( - center: ui.Offset(region.width / 3, region.height / 2), - width: region.width / 6, - height: region.height / 6), - greenPaint); + ui.Rect.fromCenter( + center: ui.Offset(region.width / 3, region.height / 2), + width: region.width / 6, + height: region.height / 6, + ), + greenPaint, + ); final CkPicture greenRectPicture = greenRectRecorder.endRecording(); final CkPictureRecorder blueRectRecorder = CkPictureRecorder(); final CkCanvas blueRectCanvas = blueRectRecorder.beginRecording(region); final CkPaint bluePaint = CkPaint()..color = const ui.Color(0xff0000ff); blueRectCanvas.drawRect( - ui.Rect.fromCenter( - center: ui.Offset(2 * region.width / 3, region.height / 2), - width: region.width / 6, - height: region.height / 6), - bluePaint); + ui.Rect.fromCenter( + center: ui.Offset(2 * region.width / 3, region.height / 2), + width: region.width / 6, + height: region.height / 6, + ), + bluePaint, + ); final CkPicture blueRectPicture = blueRectRecorder.endRecording(); builder.addPicture(ui.Offset.zero, greenRectPicture); @@ -153,8 +158,10 @@ void testMain() { builder.pop(); await matchSceneGolden( - 'canvaskit_backdropfilter_with_platformview.png', builder.build(), - region: region); + 'canvaskit_backdropfilter_with_platformview.png', + builder.build(), + region: region, + ); }); // TODO(hterkelsen): https://github.com/flutter/flutter/issues/71520 }, skip: isSafari || isFirefox); diff --git a/lib/web_ui/test/canvaskit/canvas_golden_test.dart b/lib/web_ui/test/canvaskit/canvas_golden_test.dart index 66f12821ab367..fba554c7e8a16 100644 --- a/lib/web_ui/test/canvaskit/canvas_golden_test.dart +++ b/lib/web_ui/test/canvaskit/canvas_golden_test.dart @@ -27,8 +27,7 @@ void testMain() { renderer.fontCollection.debugResetFallbackFonts(); }); - test('renders using non-recording canvas if weak refs are supported', - () async { + test('renders using non-recording canvas if weak refs are supported', () async { final CkPictureRecorder recorder = CkPictureRecorder(); final CkCanvas canvas = recorder.beginRecording(kDefaultRegion); expect(canvas.runtimeType, CkCanvas); @@ -40,21 +39,14 @@ void testMain() { ); }); - test( - 'text style - foreground/background/color do not leak across paragraphs', - () async { + test('text style - foreground/background/color do not leak across paragraphs', () async { const double testWidth = 440; const double middle = testWidth / 2; - CkParagraph createTestParagraph( - {ui.Color? color, CkPaint? foreground, CkPaint? background}) { - final CkParagraphBuilder builder = - CkParagraphBuilder(CkParagraphStyle()); - builder.pushStyle(CkTextStyle( - fontSize: 16, - color: color, - foreground: foreground, - background: background, - )); + CkParagraph createTestParagraph({ui.Color? color, CkPaint? foreground, CkPaint? background}) { + final CkParagraphBuilder builder = CkParagraphBuilder(CkParagraphStyle()); + builder.pushStyle( + CkTextStyle(fontSize: 16, color: color, foreground: foreground, background: background), + ); final StringBuffer text = StringBuffer(); if (color == null && foreground == null && background == null) { text.write('Default'); @@ -84,18 +76,16 @@ void testMain() { final List variations = [ () => createTestParagraph(), () => createTestParagraph(color: const ui.Color(0xFF009900)), + () => createTestParagraph(foreground: CkPaint()..color = const ui.Color(0xFF990000)), + () => createTestParagraph(background: CkPaint()..color = const ui.Color(0xFF7777FF)), () => createTestParagraph( - foreground: CkPaint()..color = const ui.Color(0xFF990000)), - () => createTestParagraph( - background: CkPaint()..color = const ui.Color(0xFF7777FF)), - () => createTestParagraph( - color: const ui.Color(0xFFFF00FF), - background: CkPaint()..color = const ui.Color(0xFF0000FF), - ), + color: const ui.Color(0xFFFF00FF), + background: CkPaint()..color = const ui.Color(0xFF0000FF), + ), () => createTestParagraph( - foreground: CkPaint()..color = const ui.Color(0xFF00FFFF), - background: CkPaint()..color = const ui.Color(0xFF0000FF), - ), + foreground: CkPaint()..color = const ui.Color(0xFF00FFFF), + background: CkPaint()..color = const ui.Color(0xFF0000FF), + ), ]; final CkPictureRecorder recorder = CkPictureRecorder(); @@ -109,13 +99,14 @@ void testMain() { canvas.drawParagraph(fromParagraph, ui.Offset.zero); final ui.Offset leftEnd = ui.Offset( - fromParagraph.maxIntrinsicWidth + 10, fromParagraph.height / 2); + fromParagraph.maxIntrinsicWidth + 10, + fromParagraph.height / 2, + ); final ui.Offset rightEnd = ui.Offset(middle - 10, leftEnd.dy); const ui.Offset tipOffset = ui.Offset(-5, -5); canvas.drawLine(leftEnd, rightEnd, CkPaint()); canvas.drawLine(rightEnd, rightEnd + tipOffset, CkPaint()); - canvas.drawLine( - rightEnd, rightEnd + tipOffset.scale(1, -1), CkPaint()); + canvas.drawLine(rightEnd, rightEnd + tipOffset.scale(1, -1), CkPaint()); canvas.translate(middle, 0); canvas.drawParagraph(to(), ui.Offset.zero); @@ -137,8 +128,10 @@ void testMain() { // First draw a frame with a red rectangle final CkPictureRecorder recorder = CkPictureRecorder(); final CkCanvas canvas = recorder.beginRecording(ui.Rect.largest); - canvas.drawRect(const ui.Rect.fromLTRB(20, 20, 100, 100), - CkPaint()..color = const ui.Color(0xffff0000)); + canvas.drawRect( + const ui.Rect.fromLTRB(20, 20, 100, 100), + CkPaint()..color = const ui.Color(0xffff0000), + ); final CkPicture picture = recorder.endRecording(); final LayerSceneBuilder builder = LayerSceneBuilder(); builder.pushOffset(0, 0); @@ -151,64 +144,71 @@ void testMain() { final LayerSceneBuilder emptySceneBuilder = LayerSceneBuilder(); emptySceneBuilder.pushOffset(0, 0); final LayerScene emptyScene = emptySceneBuilder.build(); - await matchSceneGolden('canvaskit_empty_scene.png', emptyScene, - region: const ui.Rect.fromLTRB(0, 0, 100, 100)); + await matchSceneGolden( + 'canvaskit_empty_scene.png', + emptyScene, + region: const ui.Rect.fromLTRB(0, 0, 100, 100), + ); }); // Regression test for https://github.com/flutter/flutter/issues/121758 test( - 'resources used in temporary surfaces for Image.toByteData can cross to rendering overlays', - () async { - ui_web.platformViewRegistry.registerViewFactory( - 'test-platform-view', - (int viewId) => createDomHTMLDivElement()..id = 'view-0', - ); - await createPlatformView(0, 'test-platform-view'); - - CkPicture makeTextPicture(String text, ui.Offset offset) { - final CkPictureRecorder recorder = CkPictureRecorder(); - final CkCanvas canvas = recorder.beginRecording(ui.Rect.largest); - final CkParagraphBuilder builder = CkParagraphBuilder(CkParagraphStyle()); - builder.addText(text); - final CkParagraph paragraph = builder.build(); - paragraph.layout(const ui.ParagraphConstraints(width: 100)); - canvas.drawRect( - ui.Rect.fromLTWH(offset.dx, offset.dy, paragraph.width, paragraph.height).inflate(10), - CkPaint()..color = const ui.Color(0xFF00FF00) + 'resources used in temporary surfaces for Image.toByteData can cross to rendering overlays', + () async { + ui_web.platformViewRegistry.registerViewFactory( + 'test-platform-view', + (int viewId) => createDomHTMLDivElement()..id = 'view-0', ); - canvas.drawParagraph(paragraph, offset); - return recorder.endRecording(); - } + await createPlatformView(0, 'test-platform-view'); + + CkPicture makeTextPicture(String text, ui.Offset offset) { + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(ui.Rect.largest); + final CkParagraphBuilder builder = CkParagraphBuilder(CkParagraphStyle()); + builder.addText(text); + final CkParagraph paragraph = builder.build(); + paragraph.layout(const ui.ParagraphConstraints(width: 100)); + canvas.drawRect( + ui.Rect.fromLTWH(offset.dx, offset.dy, paragraph.width, paragraph.height).inflate(10), + CkPaint()..color = const ui.Color(0xFF00FF00), + ); + canvas.drawParagraph(paragraph, offset); + return recorder.endRecording(); + } - CkPicture imageToPicture(CkImage image, ui.Offset offset) { - final CkPictureRecorder recorder = CkPictureRecorder(); - final CkCanvas canvas = recorder.beginRecording(ui.Rect.largest); - canvas.drawImage(image, offset, CkPaint()); - return recorder.endRecording(); - } + CkPicture imageToPicture(CkImage image, ui.Offset offset) { + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(ui.Rect.largest); + canvas.drawImage(image, offset, CkPaint()); + return recorder.endRecording(); + } - final CkPicture helloPicture = makeTextPicture('Hello', ui.Offset.zero); + final CkPicture helloPicture = makeTextPicture('Hello', ui.Offset.zero); - final CkImage helloImage = helloPicture.toImageSync(100, 100); + final CkImage helloImage = helloPicture.toImageSync(100, 100); - // Calling toByteData is essential to hit the bug. - await helloImage.toByteData(format: ui.ImageByteFormat.png); + // Calling toByteData is essential to hit the bug. + await helloImage.toByteData(format: ui.ImageByteFormat.png); - final LayerSceneBuilder sb = LayerSceneBuilder(); - sb.pushOffset(0, 0); - sb.addPicture(ui.Offset.zero, helloPicture); - sb.addPlatformView(0, width: 10, height: 10); + final LayerSceneBuilder sb = LayerSceneBuilder(); + sb.pushOffset(0, 0); + sb.addPicture(ui.Offset.zero, helloPicture); + sb.addPlatformView(0, width: 10, height: 10); - // The image is rendered after the platform view so that it's rendered into - // a separate surface, which is what triggers the bug. If the bug is present - // the image will not appear on the UI. - sb.addPicture(const ui.Offset(0, 50), imageToPicture(helloImage, ui.Offset.zero)); - sb.pop(); + // The image is rendered after the platform view so that it's rendered into + // a separate surface, which is what triggers the bug. If the bug is present + // the image will not appear on the UI. + sb.addPicture(const ui.Offset(0, 50), imageToPicture(helloImage, ui.Offset.zero)); + sb.pop(); - // The below line should not throw an error. - await matchSceneGolden('cross_overlay_resources.png', sb.build(), - region: const ui.Rect.fromLTRB(0, 0, 100, 100)); - }); + // The below line should not throw an error. + await matchSceneGolden( + 'cross_overlay_resources.png', + sb.build(), + region: const ui.Rect.fromLTRB(0, 0, 100, 100), + ); + }, + ); }); } @@ -223,15 +223,8 @@ void drawTestPicture(CkCanvas canvas) { canvas.save(); canvas.save(); - canvas.clipRect( - const ui.Rect.fromLTRB(0, 0, 45, 45), - ui.ClipOp.intersect, - true, - ); - canvas.clipRRect( - ui.RRect.fromLTRBR(5, 5, 50, 50, const ui.Radius.circular(8)), - true, - ); + canvas.clipRect(const ui.Rect.fromLTRB(0, 0, 45, 45), ui.ClipOp.intersect, true); + canvas.clipRRect(ui.RRect.fromLTRBR(5, 5, 50, 50, const ui.Radius.circular(8)), true); canvas.clipPath( CkPath() ..moveTo(5, 5) @@ -245,11 +238,7 @@ void drawTestPicture(CkCanvas canvas) { canvas.restore(); // remove clips canvas.translate(60, 0); - canvas.drawCircle( - const ui.Offset(30, 25), - 15, - CkPaint()..color = const ui.Color(0xFF0000AA), - ); + canvas.drawCircle(const ui.Offset(30, 25), 15, CkPaint()..color = const ui.Color(0xFF0000AA)); canvas.translate(60, 0); canvas.drawArc( @@ -261,11 +250,7 @@ void drawTestPicture(CkCanvas canvas) { ); canvas.translate(60, 0); - canvas.drawImage( - generateTestImage(), - const ui.Offset(20, 20), - CkPaint(), - ); + canvas.drawImage(generateTestImage(), const ui.Offset(20, 20), CkPaint()); canvas.translate(60, 0); final ui.RSTransform transform = ui.RSTransform.fromComponents( @@ -325,10 +310,7 @@ void drawTestPicture(CkCanvas canvas) { canvas.drawLine(ui.Offset.zero, const ui.Offset(40, 30), CkPaint()); canvas.translate(60, 0); - canvas.drawOval( - const ui.Rect.fromLTRB(0, 0, 40, 30), - CkPaint(), - ); + canvas.drawOval(const ui.Rect.fromLTRB(0, 0, 40, 30), CkPaint()); canvas.translate(60, 0); canvas.save(); @@ -339,8 +321,7 @@ void drawTestPicture(CkCanvas canvas) { canvas.translate(60, 0); { final CkPictureRecorder otherRecorder = CkPictureRecorder(); - final CkCanvas otherCanvas = - otherRecorder.beginRecording(const ui.Rect.fromLTRB(0, 0, 40, 20)); + final CkCanvas otherCanvas = otherRecorder.beginRecording(const ui.Rect.fromLTRB(0, 0, 40, 20)); otherCanvas.drawCircle( const ui.Offset(30, 15), 10, @@ -364,21 +345,15 @@ void drawTestPicture(CkCanvas canvas) { ui.Offset(10, 10), ui.Offset(20, 10), ui.Offset(30, 20), - ui.Offset(40, 20) + ui.Offset(40, 20), ]), ); canvas.translate(60, 0); - canvas.drawRRect( - ui.RRect.fromLTRBR(0, 0, 40, 30, const ui.Radius.circular(10)), - CkPaint(), - ); + canvas.drawRRect(ui.RRect.fromLTRBR(0, 0, 40, 30, const ui.Radius.circular(10)), CkPaint()); canvas.translate(60, 0); - canvas.drawRect( - const ui.Rect.fromLTRB(0, 0, 40, 30), - CkPaint(), - ); + canvas.drawRect(const ui.Rect.fromLTRB(0, 0, 40, 30), CkPaint()); canvas.translate(60, 0); canvas.drawShadow( @@ -395,14 +370,11 @@ void drawTestPicture(CkCanvas canvas) { canvas.save(); canvas.drawVertices( - CkVertices( - ui.VertexMode.triangleFan, - const [ - ui.Offset(10, 30), - ui.Offset(30, 50), - ui.Offset(10, 60), - ], - ), + CkVertices(ui.VertexMode.triangleFan, const [ + ui.Offset(10, 30), + ui.Offset(30, 50), + ui.Offset(10, 60), + ]), ui.BlendMode.srcOver, CkPaint(), ); @@ -422,14 +394,14 @@ void drawTestPicture(CkCanvas canvas) { canvas.save(); canvas.rotate(-math.pi / 8); canvas.drawLine(ui.Offset.zero, const ui.Offset(30, 30), CkPaint()); - canvas.drawCircle( - const ui.Offset(30, 30), 7, CkPaint()..color = const ui.Color(0xFF00AA00)); + canvas.drawCircle(const ui.Offset(30, 30), 7, CkPaint()..color = const ui.Color(0xFF00AA00)); canvas.restore(); canvas.translate(60, 0); - final CkPaint thickStroke = CkPaint() - ..style = ui.PaintingStyle.stroke - ..strokeWidth = 20; + final CkPaint thickStroke = + CkPaint() + ..style = ui.PaintingStyle.stroke + ..strokeWidth = 20; final CkPaint semitransparent = CkPaint()..color = const ui.Color(0x66000000); canvas.saveLayer(kDefaultRegion, semitransparent); @@ -450,7 +422,9 @@ void drawTestPicture(CkCanvas canvas) { canvas.drawCircle(const ui.Offset(30, 30), 10, CkPaint()); { canvas.saveLayerWithFilter( - kDefaultRegion, ui.ImageFilter.blur(sigmaX: 5, sigmaY: 10, tileMode: ui.TileMode.clamp)); + kDefaultRegion, + ui.ImageFilter.blur(sigmaX: 5, sigmaY: 10, tileMode: ui.TileMode.clamp), + ); canvas.drawCircle(const ui.Offset(10, 10), 10, CkPaint()); canvas.drawCircle(const ui.Offset(50, 50), 10, CkPaint()); canvas.restore(); @@ -487,10 +461,7 @@ void drawTestPicture(CkCanvas canvas) { canvas.translate(60, 0); final CkParagraph p = makeSimpleText('Hello', fontSize: 18, color: const ui.Color(0xFF0000AA)); - canvas.drawParagraph( - p, - const ui.Offset(10, 20), - ); + canvas.drawParagraph(p, const ui.Offset(10, 20)); canvas.translate(60, 0); canvas.drawPath( @@ -516,17 +487,18 @@ CkImage generateTestImage() { ctx.fillRect(10, 0, 10, 10); ctx.fillStyle = '#FF00FF'; ctx.fillRect(10, 10, 10, 10); - final Uint8List imageData = - ctx.getImageData(0, 0, 20, 20).data.buffer.asUint8List(); - final SkImage skImage = canvasKit.MakeImage( - SkImageInfo( - width: 20, - height: 20, - alphaType: canvasKit.AlphaType.Premul, - colorType: canvasKit.ColorType.RGBA_8888, - colorSpace: SkColorSpaceSRGB, - ), - imageData, - 4 * 20)!; + final Uint8List imageData = ctx.getImageData(0, 0, 20, 20).data.buffer.asUint8List(); + final SkImage skImage = + canvasKit.MakeImage( + SkImageInfo( + width: 20, + height: 20, + alphaType: canvasKit.AlphaType.Premul, + colorType: canvasKit.ColorType.RGBA_8888, + colorSpace: SkColorSpaceSRGB, + ), + imageData, + 4 * 20, + )!; return CkImage(skImage); } diff --git a/lib/web_ui/test/canvaskit/canvaskit_api_test.dart b/lib/web_ui/test/canvaskit/canvaskit_api_test.dart index b70007019b21b..3bd46f4051cc0 100644 --- a/lib/web_ui/test/canvaskit/canvaskit_api_test.dart +++ b/lib/web_ui/test/canvaskit/canvaskit_api_test.dart @@ -192,13 +192,11 @@ void _fillTypeTests() { void _pathOpTests() { test('path op mapping is correct', () { - expect( - canvasKit.PathOp.Difference.value, ui.PathOperation.difference.index); + expect(canvasKit.PathOp.Difference.value, ui.PathOperation.difference.index); expect(canvasKit.PathOp.Intersect.value, ui.PathOperation.intersect.index); expect(canvasKit.PathOp.Union.value, ui.PathOperation.union.index); expect(canvasKit.PathOp.XOR.value, ui.PathOperation.xor.index); - expect(canvasKit.PathOp.ReverseDifference.value, - ui.PathOperation.reverseDifference.index); + expect(canvasKit.PathOp.ReverseDifference.value, ui.PathOperation.reverseDifference.index); }); test('ui.PathOperation converts to SkPathOp', () { @@ -260,10 +258,8 @@ void _pointModeTests() { void _vertexModeTests() { test('vertex mode mapping is correct', () { expect(canvasKit.VertexMode.Triangles.value, ui.VertexMode.triangles.index); - expect(canvasKit.VertexMode.TrianglesStrip.value, - ui.VertexMode.triangleStrip.index); - expect(canvasKit.VertexMode.TriangleFan.value, - ui.VertexMode.triangleFan.index); + expect(canvasKit.VertexMode.TrianglesStrip.value, ui.VertexMode.triangleStrip.index); + expect(canvasKit.VertexMode.TriangleFan.value, ui.VertexMode.triangleFan.index); }); test('ui.VertexMode converts to SkVertexMode', () { @@ -275,8 +271,7 @@ void _vertexModeTests() { void _imageTests() { test('MakeAnimatedImageFromEncoded makes a non-animated image', () { - final SkAnimatedImage nonAnimated = - canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!; + final SkAnimatedImage nonAnimated = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!; expect(nonAnimated.getFrameCount(), 1); expect(nonAnimated.getRepetitionCount(), 0); expect(nonAnimated.width(), 1); @@ -300,8 +295,7 @@ void _imageTests() { }); test('MakeAnimatedImageFromEncoded makes an animated image', () { - final SkAnimatedImage animated = - canvasKit.MakeAnimatedImageFromEncoded(kAnimatedGif)!; + final SkAnimatedImage animated = canvasKit.MakeAnimatedImageFromEncoded(kAnimatedGif)!; expect(animated.getFrameCount(), 3); expect(animated.getRepetitionCount(), -1); // animates forever expect(animated.width(), 1); @@ -322,32 +316,34 @@ void _shaderTests() { test('MakeRadialGradient', () { expect( - canvasKit.Shader.MakeRadialGradient( - Float32List.fromList([1, 1]), - 10.0, - Uint32List.fromList([0xff000000, 0xffffffff]), - Float32List.fromList([0, 1]), - canvasKit.TileMode.Repeat, - toSkMatrixFromFloat32(Matrix4.identity().storage), - 0, - ), - isNotNull); + canvasKit.Shader.MakeRadialGradient( + Float32List.fromList([1, 1]), + 10.0, + Uint32List.fromList([0xff000000, 0xffffffff]), + Float32List.fromList([0, 1]), + canvasKit.TileMode.Repeat, + toSkMatrixFromFloat32(Matrix4.identity().storage), + 0, + ), + isNotNull, + ); }); test('MakeTwoPointConicalGradient', () { expect( - canvasKit.Shader.MakeTwoPointConicalGradient( - Float32List.fromList([1, 1]), - 10.0, - Float32List.fromList([1, 1]), - 10.0, - Uint32List.fromList([0xff000000, 0xffffffff]), - Float32List.fromList([0, 1]), - canvasKit.TileMode.Repeat, - toSkMatrixFromFloat32(Matrix4.identity().storage), - 0, - ), - isNotNull); + canvasKit.Shader.MakeTwoPointConicalGradient( + Float32List.fromList([1, 1]), + 10.0, + Float32List.fromList([1, 1]), + 10.0, + Uint32List.fromList([0xff000000, 0xffffffff]), + Float32List.fromList([0, 1]), + canvasKit.TileMode.Repeat, + toSkMatrixFromFloat32(Matrix4.identity().storage), + 0, + ), + isNotNull, + ); }); test('RuntimeEffect', () { @@ -392,8 +388,9 @@ return u_color; uniformData[2] = 0.0; uniformData[3] = 1.0; - final SkShader? shaderWithUniform = MakeRuntimeEffect(kSkSlProgramWithUniforms) - !.makeShader(uniforms); + final SkShader? shaderWithUniform = MakeRuntimeEffect( + kSkSlProgramWithUniforms, + )!.makeShader(uniforms); expect(shaderWithUniform, isNotNull); }); @@ -421,42 +418,22 @@ void _paintTests() { paint.setAntiAlias(true); paint.setColorInt(0x00FFCCAA); paint.setShader(_makeTestShader()); - paint.setMaskFilter(canvasKit.MaskFilter.MakeBlur( - canvasKit.BlurStyle.Outer, - 2.0, - true, - )); + paint.setMaskFilter(canvasKit.MaskFilter.MakeBlur(canvasKit.BlurStyle.Outer, 2.0, true)); paint.setColorFilter(canvasKit.ColorFilter.MakeLinearToSRGBGamma()); paint.setStrokeMiter(1.4); - paint.setImageFilter(canvasKit.ImageFilter.MakeBlur( - 1, - 2, - canvasKit.TileMode.Repeat, - null, - )); + paint.setImageFilter(canvasKit.ImageFilter.MakeBlur(1, 2, canvasKit.TileMode.Repeat, null)); }); } void _maskFilterTests() { test('MaskFilter.MakeBlur', () { - expect( - canvasKit.MaskFilter.MakeBlur( - canvasKit.BlurStyle.Outer, - 5.0, - false, - ), - isNotNull); + expect(canvasKit.MaskFilter.MakeBlur(canvasKit.BlurStyle.Outer, 5.0, false), isNotNull); }); test('MaskFilter.MakeBlur with 0 sigma returns null', () { - expect( - canvasKit.MaskFilter.MakeBlur(canvasKit.BlurStyle.Normal, 0.0, false), - isNull); + expect(canvasKit.MaskFilter.MakeBlur(canvasKit.BlurStyle.Normal, 0.0, false), isNull); }); test('MaskFilter.MakeBlur with NaN sigma returns null', () { - expect( - canvasKit.MaskFilter.MakeBlur( - canvasKit.BlurStyle.Normal, double.nan, false), - isNull); + expect(canvasKit.MaskFilter.MakeBlur(canvasKit.BlurStyle.Normal, double.nan, false), isNull); }); } @@ -472,35 +449,21 @@ void _colorFilterTests() { }); test('MakeMatrix', () { - expect( - canvasKit.ColorFilter.MakeMatrix( - Float32List(20), - ), - isNotNull, - ); + expect(canvasKit.ColorFilter.MakeMatrix(Float32List(20)), isNotNull); }); test('MakeSRGBToLinearGamma', () { - expect( - canvasKit.ColorFilter.MakeSRGBToLinearGamma(), - isNotNull, - ); + expect(canvasKit.ColorFilter.MakeSRGBToLinearGamma(), isNotNull); }); test('MakeLinearToSRGBGamma', () { - expect( - canvasKit.ColorFilter.MakeLinearToSRGBGamma(), - isNotNull, - ); + expect(canvasKit.ColorFilter.MakeLinearToSRGBGamma(), isNotNull); }); } void _imageFilterTests() { test('MakeBlur', () { - expect( - canvasKit.ImageFilter.MakeBlur(1, 2, canvasKit.TileMode.Repeat, null), - isNotNull, - ); + expect(canvasKit.ImageFilter.MakeBlur(1, 2, canvasKit.TileMode.Repeat, null), isNotNull); }); test('toSkFilterOptions', () { @@ -522,10 +485,7 @@ void _imageFilterTests() { test('MakeColorFilter', () { expect( - canvasKit.ImageFilter.MakeColorFilter( - canvasKit.ColorFilter.MakeLinearToSRGBGamma(), - null, - ), + canvasKit.ImageFilter.MakeColorFilter(canvasKit.ColorFilter.MakeLinearToSRGBGamma(), null), isNotNull, ); }); @@ -541,17 +501,11 @@ void _imageFilterTests() { }); test('MakeDilate', () { - expect( - canvasKit.ImageFilter.MakeDilate(1, 2, null), - isNotNull, - ); + expect(canvasKit.ImageFilter.MakeDilate(1, 2, null), isNotNull); }); test('MakeErode', () { - expect( - canvasKit.ImageFilter.MakeErode(1, 2, null), - isNotNull, - ); + expect(canvasKit.ImageFilter.MakeErode(1, 2, null), isNotNull); }); } @@ -662,50 +616,54 @@ void _toSkColorStopsTests() { void _toSkMatrixFromFloat32Tests() { test('toSkMatrixFromFloat32', () { - final Matrix4 matrix = Matrix4.identity() - ..translate(1, 2, 3) - ..rotateZ(4); + final Matrix4 matrix = + Matrix4.identity() + ..translate(1, 2, 3) + ..rotateZ(4); expect( - toSkMatrixFromFloat32(matrix.storage), - Float32List.fromList([ - -0.6536436080932617, - 0.756802499294281, - 1, - -0.756802499294281, - -0.6536436080932617, - 2, - -0.0, - 0, - 1, - ])); + toSkMatrixFromFloat32(matrix.storage), + Float32List.fromList([ + -0.6536436080932617, + 0.756802499294281, + 1, + -0.756802499294281, + -0.6536436080932617, + 2, + -0.0, + 0, + 1, + ]), + ); }); } void _toSkM44FromFloat32Tests() { test('toSkM44FromFloat32', () { - final Matrix4 matrix = Matrix4.identity() - ..translate(1, 2, 3) - ..rotateZ(4); + final Matrix4 matrix = + Matrix4.identity() + ..translate(1, 2, 3) + ..rotateZ(4); expect( - toSkM44FromFloat32(matrix.storage), - Float32List.fromList([ - -0.6536436080932617, - 0.756802499294281, - 0, - 1, - -0.756802499294281, - -0.6536436080932617, - 0, - 2, - 0, - 0, - 1, - 3, - 0, - 0, - 0, - 1, - ])); + toSkM44FromFloat32(matrix.storage), + Float32List.fromList([ + -0.6536436080932617, + 0.756802499294281, + 0, + 1, + -0.756802499294281, + -0.6536436080932617, + 0, + 2, + 0, + 0, + 1, + 3, + 0, + 0, + 0, + 1, + ]), + ); }); } @@ -714,7 +672,9 @@ typedef CanvasCallback = void Function(ui.Canvas canvas); Future toImage(CanvasCallback callback, int width, int height) { final ui.PictureRecorder recorder = ui.PictureRecorder(); final ui.Canvas canvas = ui.Canvas( - recorder, ui.Rect.fromLTRB(0, 0, width.toDouble(), height.toDouble())); + recorder, + ui.Rect.fromLTRB(0, 0, width.toDouble(), height.toDouble()), + ); callback(canvas); final ui.Picture picture = recorder.endRecording(); return picture.toImage(width, height); @@ -726,8 +686,7 @@ Future fuzzyCompareImages(ui.Image golden, ui.Image img) async { if (golden.width != img.width || golden.height != img.height) { return false; } - int getPixel(ByteData data, int x, int y) => - data.getUint32((x + y * golden.width) * 4); + int getPixel(ByteData data, int x, int y) => data.getUint32((x + y * golden.width) * 4); final ByteData goldenData = (await golden.toByteData())!; final ByteData imgData = (await img.toByteData())!; for (int y = 0; y < golden.height; y++) { @@ -754,39 +713,50 @@ void _matrix4x4CompositionTests() { const double width3 = width / 3.0; const double width5 = width / 5.0; const double width10 = width / 10.0; - canvas.drawRect(const ui.Rect.fromLTRB(-width3, -width3, width3, width3), - ui.Paint()..color = green); canvas.drawRect( - const ui.Rect.fromLTRB(-width5, -width5, -width10, width5), - ui.Paint()..color = black); + const ui.Rect.fromLTRB(-width3, -width3, width3, width3), + ui.Paint()..color = green, + ); + canvas.drawRect( + const ui.Rect.fromLTRB(-width5, -width5, -width10, width5), + ui.Paint()..color = black, + ); canvas.drawRect( - const ui.Rect.fromLTRB(-width5, -width5, width5, -width10), - ui.Paint()..color = black); + const ui.Rect.fromLTRB(-width5, -width5, width5, -width10), + ui.Paint()..color = black, + ); } - final ui.Image incrementalMatrixImage = await toImage((ui.Canvas canvas) { - paint(canvas, (ui.Canvas canvas) { - final Matrix4 matrix = Matrix4.identity(); - matrix.setEntry(3, 2, 0.001); - canvas.transform(matrix.toFloat64()); - matrix.setRotationX(rotateAroundX); - canvas.transform(matrix.toFloat64()); - matrix.setRotationY(rotateAroundY); - canvas.transform(matrix.toFloat64()); - }); - }, width, height); - final ui.Image combinedMatrixImage = await toImage((ui.Canvas canvas) { - paint(canvas, (ui.Canvas canvas) { - final Matrix4 matrix = Matrix4.identity(); - matrix.setEntry(3, 2, 0.001); - matrix.rotate(kUnitX, rotateAroundX); - matrix.rotate(kUnitY, rotateAroundY); - canvas.transform(matrix.toFloat64()); - }); - }, width, height); - - final bool areEqual = - await fuzzyCompareImages(incrementalMatrixImage, combinedMatrixImage); + final ui.Image incrementalMatrixImage = await toImage( + (ui.Canvas canvas) { + paint(canvas, (ui.Canvas canvas) { + final Matrix4 matrix = Matrix4.identity(); + matrix.setEntry(3, 2, 0.001); + canvas.transform(matrix.toFloat64()); + matrix.setRotationX(rotateAroundX); + canvas.transform(matrix.toFloat64()); + matrix.setRotationY(rotateAroundY); + canvas.transform(matrix.toFloat64()); + }); + }, + width, + height, + ); + final ui.Image combinedMatrixImage = await toImage( + (ui.Canvas canvas) { + paint(canvas, (ui.Canvas canvas) { + final Matrix4 matrix = Matrix4.identity(); + matrix.setEntry(3, 2, 0.001); + matrix.rotate(kUnitX, rotateAroundX); + matrix.rotate(kUnitY, rotateAroundY); + canvas.transform(matrix.toFloat64()); + }); + }, + width, + height, + ); + + final bool areEqual = await fuzzyCompareImages(incrementalMatrixImage, combinedMatrixImage); expect(areEqual, true); }); } @@ -797,22 +767,26 @@ void _toSkRectTests() { }); test('fromSkRect', () { - expect(fromSkRect(Float32List.fromList([1, 2, 3, 4])), - const ui.Rect.fromLTRB(1, 2, 3, 4)); + expect( + fromSkRect(Float32List.fromList([1, 2, 3, 4])), + const ui.Rect.fromLTRB(1, 2, 3, 4), + ); }); test('toSkRRect', () { expect( - toSkRRect(ui.RRect.fromLTRBAndCorners( - 1, - 2, - 3, - 4, - topLeft: const ui.Radius.elliptical(5, 6), - topRight: const ui.Radius.elliptical(7, 8), - bottomRight: const ui.Radius.elliptical(9, 10), - bottomLeft: const ui.Radius.elliptical(11, 12), - )), + toSkRRect( + ui.RRect.fromLTRBAndCorners( + 1, + 2, + 3, + 4, + topLeft: const ui.Radius.elliptical(5, 6), + topRight: const ui.Radius.elliptical(7, 8), + bottomRight: const ui.Radius.elliptical(9, 10), + bottomLeft: const ui.Radius.elliptical(11, 12), + ), + ), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], ); }); @@ -839,19 +813,11 @@ void _pathTests() { }); test('addArc', () { - path.addArc( - toSkRect(const ui.Rect.fromLTRB(10, 20, 30, 40)), - 1, - 5, - ); + path.addArc(toSkRect(const ui.Rect.fromLTRB(10, 20, 30, 40)), 1, 5); }); test('addOval', () { - path.addOval( - toSkRect(const ui.Rect.fromLTRB(10, 20, 30, 40)), - false, - 1, - ); + path.addOval(toSkRect(const ui.Rect.fromLTRB(10, 20, 30, 40)), false, 1); }); test('addPath', () { @@ -872,10 +838,7 @@ void _pathTests() { const ui.Rect.fromLTRB(10, 10, 20, 20), const ui.Radius.circular(3), ); - path.addRRect( - toSkRRect(rrect), - false, - ); + path.addRRect(toSkRRect(rrect), false); }); test('addRect', () { @@ -883,24 +846,11 @@ void _pathTests() { }); test('arcTo', () { - path.arcToOval( - toSkRect(const ui.Rect.fromLTRB(1, 2, 3, 4)), - 5, - 40, - false, - ); + path.arcToOval(toSkRect(const ui.Rect.fromLTRB(1, 2, 3, 4)), 5, 40, false); }); test('overloaded arcTo (used for arcToPoint)', () { - path.arcToRotated( - 1, - 2, - 3, - false, - true, - 4, - 5, - ); + path.arcToRotated(1, 2, 3, false, true, 4, 5); }); test('close', () { @@ -940,15 +890,7 @@ void _pathTests() { }); test('rArcTo', () { - path.rArcTo( - 10, - 20, - 30, - false, - true, - 40, - 50, - ); + path.rArcTo(10, 20, 30, false, true, 40, 50); }); test('rConicTo', () { @@ -973,15 +915,13 @@ void _pathTests() { test('reset', () { final SkPath testPath = _testClosedSkPath(); - expect(fromSkRect(testPath.getBounds()), - const ui.Rect.fromLTRB(10, 10, 20, 20)); + expect(fromSkRect(testPath.getBounds()), const ui.Rect.fromLTRB(10, 10, 20, 20)); testPath.reset(); expect(fromSkRect(testPath.getBounds()), ui.Rect.zero); }); test('toSVGString', () { - expect( - _testClosedSkPath().toSVGString(), 'M10 10L20 10L20 20L10 20L10 10Z'); + expect(_testClosedSkPath().toSVGString(), 'M10 10L20 10L20 20L10 20L10 10Z'); }); test('isEmpty', () { @@ -1003,13 +943,11 @@ void _pathTests() { }); test('SkContourMeasureIter/SkContourMeasure', () { - final SkContourMeasureIter iter = - SkContourMeasureIter(_testClosedSkPath(), false, 1.0); + final SkContourMeasureIter iter = SkContourMeasureIter(_testClosedSkPath(), false, 1.0); final SkContourMeasure measure1 = iter.next()!; expect(measure1.length(), 40); expect(measure1.getPosTan(5), Float32List.fromList([15, 10, 1, 0])); - expect( - measure1.getPosTan(15), Float32List.fromList([20, 15, 0, 1])); + expect(measure1.getPosTan(15), Float32List.fromList([20, 15, 0, 1])); expect(measure1.isClosed(), isTrue); // Starting with a box path: @@ -1034,8 +972,7 @@ void _pathTests() { // | | // 20 +-----------+ final SkPath segment = measure1.getSegment(5, 15, true); - expect(fromSkRect(segment.getBounds()), - const ui.Rect.fromLTRB(15, 10, 20, 15)); + expect(fromSkRect(segment.getBounds()), const ui.Rect.fromLTRB(15, 10, 20, 15)); final SkContourMeasure? measure2 = iter.next(); expect(measure2, isNull); @@ -1080,13 +1017,14 @@ void _pictureTests() { setUp(() { final SkPictureRecorder recorder = SkPictureRecorder(); final SkCanvas canvas = recorder.beginRecording(toSkRect(ui.Rect.largest)); - canvas.drawRect(toSkRect(const ui.Rect.fromLTRB(20, 30, 40, 50)), - SkPaint()..setColorInt(0xffff00ff)); + canvas.drawRect( + toSkRect(const ui.Rect.fromLTRB(20, 30, 40, 50)), + SkPaint()..setColorInt(0xffff00ff), + ); picture = recorder.finishRecordingAsPicture(); }); test('cullRect', () { - expect( - fromSkRect(picture.cullRect()), const ui.Rect.fromLTRB(20, 30, 40, 50)); + expect(fromSkRect(picture.cullRect()), const ui.Rect.fromLTRB(20, 30, 40, 50)); }); test('approximateBytesUsed', () { @@ -1100,8 +1038,7 @@ void _canvasTests() { setUp(() { recorder = SkPictureRecorder(); - canvas = recorder - .beginRecording(toSkRect(const ui.Rect.fromLTRB(0, 0, 100, 100))); + canvas = recorder.beginRecording(toSkRect(const ui.Rect.fromLTRB(0, 0, 100, 100))); }); tearDown(() { @@ -1180,18 +1117,11 @@ void _canvasTests() { }); test('drawArc', () { - canvas.drawArc( - Float32List.fromList([0, 0, 100, 50]), - 0, - 100, - true, - SkPaint(), - ); + canvas.drawArc(Float32List.fromList([0, 0, 100, 50]), 0, 100, true, SkPaint()); }); test('drawAtlas', () { - final SkAnimatedImage image = - canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!; + final SkAnimatedImage image = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!; canvas.drawAtlas( image.makeImageAtCurrentFrame(), Float32List.fromList([0, 0, 1, 1]), @@ -1219,8 +1149,7 @@ void _canvasTests() { }); test('drawImageOptions', () { - final SkAnimatedImage image = - canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!; + final SkAnimatedImage image = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!; canvas.drawImageOptions( image.makeImageAtCurrentFrame(), 10, @@ -1232,21 +1161,12 @@ void _canvasTests() { }); test('drawImageCubic', () { - final SkAnimatedImage image = - canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!; - canvas.drawImageCubic( - image.makeImageAtCurrentFrame(), - 10, - 20, - 0.3, - 0.3, - SkPaint(), - ); + final SkAnimatedImage image = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!; + canvas.drawImageCubic(image.makeImageAtCurrentFrame(), 10, 20, 0.3, 0.3, SkPaint()); }); test('drawImageRectOptions', () { - final SkAnimatedImage image = - canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!; + final SkAnimatedImage image = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!; canvas.drawImageRectOptions( image.makeImageAtCurrentFrame(), Float32List.fromList([0, 0, 1, 1]), @@ -1258,8 +1178,7 @@ void _canvasTests() { }); test('drawImageRectCubic', () { - final SkAnimatedImage image = - canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!; + final SkAnimatedImage image = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!; canvas.drawImageRectCubic( image.makeImageAtCurrentFrame(), Float32List.fromList([0, 0, 1, 1]), @@ -1271,8 +1190,7 @@ void _canvasTests() { }); test('drawImageNine', () { - final SkAnimatedImage image = - canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!; + final SkAnimatedImage image = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!; canvas.drawImageNine( image.makeImageAtCurrentFrame(), Float32List.fromList([0, 0, 1, 1]), @@ -1295,10 +1213,7 @@ void _canvasTests() { }); test('drawPath', () { - canvas.drawPath( - _testClosedSkPath(), - SkPaint(), - ); + canvas.drawPath(_testClosedSkPath(), SkPaint()); }); test('drawPoints', () { @@ -1317,10 +1232,7 @@ void _canvasTests() { }); test('drawRect', () { - canvas.drawRect( - Float32List.fromList([0, 0, 100, 100]), - SkPaint(), - ); + canvas.drawRect(Float32List.fromList([0, 0, 100, 100]), SkPaint()); }); test('drawShadow', () { @@ -1336,18 +1248,15 @@ void _canvasTests() { final double shadowY = bounds.top - 600.0; const ui.Color color = ui.Color(0xAABBCCDD); - final ui.Color inAmbient = - color.withAlpha((color.alpha * ambientAlpha).round()); - final ui.Color inSpot = - color.withAlpha((color.alpha * spotAlpha).round()); + final ui.Color inAmbient = color.withAlpha((color.alpha * ambientAlpha).round()); + final ui.Color inSpot = color.withAlpha((color.alpha * spotAlpha).round()); final SkTonalColors inTonalColors = SkTonalColors( ambient: makeFreshSkColor(inAmbient), spot: makeFreshSkColor(inSpot), ); - final SkTonalColors tonalColors = - canvasKit.computeTonalColors(inTonalColors); + final SkTonalColors tonalColors = canvasKit.computeTonalColors(inTonalColors); canvas.drawShadow( path, @@ -1365,18 +1274,14 @@ void _canvasTests() { }); test('drawVertices', () { - canvas.drawVertices( - _testVertices(), - canvasKit.BlendMode.SrcOver, - SkPaint(), - ); + canvas.drawVertices(_testVertices(), canvasKit.BlendMode.SrcOver, SkPaint()); }); test('rotate', () { canvas.rotate(90, 10, 20); expect(canvas.getLocalToDevice(), [ 0, -1, 0, 30, // tx = 10 - (-20) == 30 - 1, 0, 0, 10, // ty = 20 - 10 == 10 + 1, 0, 0, 10, // ty = 20 - 10 == 10 0, 0, 1, 0, 0, 0, 0, 1, ]); @@ -1384,54 +1289,60 @@ void _canvasTests() { test('scale', () { canvas.scale(2, 3); - expect(canvas.getLocalToDevice(), [ - 2, 0, 0, 0, - 0, 3, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1, - ]); + expect(canvas.getLocalToDevice(), [2, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); }); test('skew', () { canvas.skew(4, 5); - expect(canvas.getLocalToDevice(), [ - 1, 4, 0, 0, - 5, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1, - ]); + expect(canvas.getLocalToDevice(), [1, 4, 0, 0, 5, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); }); test('concat', () { canvas.concat(toSkM44FromFloat32(Matrix4.identity().storage)); + expect(canvas.getLocalToDevice(), [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]); + canvas.concat( + Float32List.fromList([ + 11, + 12, + 13, + 14, + 21, + 22, + 23, + 24, + 31, + 32, + 33, + 34, + 41, + 42, + 43, + 44, + ]), + ); expect(canvas.getLocalToDevice(), [ - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1, - ]); - canvas.concat(Float32List.fromList([ - 11, 12, 13, 14, - 21, 22, 23, 24, - 31, 32, 33, 34, - 41, 42, 43, 44, - ])); - expect(canvas.getLocalToDevice(), [ - 11, 12, 13, 14, - 21, 22, 23, 24, - 31, 32, 33, 34, - 41, 42, 43, 44, + 11, + 12, + 13, + 14, + 21, + 22, + 23, + 24, + 31, + 32, + 33, + 34, + 41, + 42, + 43, + 44, ]); }); test('translate', () { canvas.translate(4, 5); - expect(canvas.getLocalToDevice(), [ - 1, 0, 0, 4, - 0, 1, 0, 5, - 0, 0, 1, 0, - 0, 0, 0, 1, - ]); + expect(canvas.getLocalToDevice(), [1, 0, 0, 4, 0, 1, 0, 5, 0, 0, 1, 0, 0, 0, 0, 1]); }); test('quickReject', () { @@ -1442,47 +1353,36 @@ void _canvasTests() { canvasKit.ClipOp.Intersect, false, ); - expect( - canvas.quickReject(toSkRect(const ui.Rect.fromLTRB(5, 5, 15, 15))), - isFalse, - ); - expect( - canvas.quickReject(toSkRect(const ui.Rect.fromLTRB(25, 25, 35, 35))), - isTrue, - ); + expect(canvas.quickReject(toSkRect(const ui.Rect.fromLTRB(5, 5, 15, 15))), isFalse); + expect(canvas.quickReject(toSkRect(const ui.Rect.fromLTRB(25, 25, 35, 35))), isTrue); canvas.restore(); }); test('drawPicture', () { final SkPictureRecorder otherRecorder = SkPictureRecorder(); - final SkCanvas otherCanvas = otherRecorder - .beginRecording(Float32List.fromList([0, 0, 100, 100])); + final SkCanvas otherCanvas = otherRecorder.beginRecording( + Float32List.fromList([0, 0, 100, 100]), + ); otherCanvas.drawLine(0, 0, 10, 10, SkPaint()); canvas.drawPicture(otherRecorder.finishRecordingAsPicture()); }); test('drawParagraph', () { - final CkParagraphBuilder builder = CkParagraphBuilder( - CkParagraphStyle(), - ); + final CkParagraphBuilder builder = CkParagraphBuilder(CkParagraphStyle()); builder.addText('Hello'); final CkParagraph paragraph = builder.build(); paragraph.layout(const ui.ParagraphConstraints(width: 100)); - canvas.drawParagraph( - paragraph.skiaObject, - 10, - 20, - ); + canvas.drawParagraph(paragraph.skiaObject, 10, 20); }); test('Paragraph converts caret position to charactor position', () { - final CkParagraphBuilder builder = CkParagraphBuilder( - CkParagraphStyle(), - ); + final CkParagraphBuilder builder = CkParagraphBuilder(CkParagraphStyle()); builder.addText('Hello there'); final CkParagraph paragraph = builder.build(); paragraph.layout(const ui.ParagraphConstraints(width: 100)); - ui.TextRange range = paragraph.getWordBoundary(const ui.TextPosition(offset: 5, affinity: ui.TextAffinity.upstream)); + ui.TextRange range = paragraph.getWordBoundary( + const ui.TextPosition(offset: 5, affinity: ui.TextAffinity.upstream), + ); expect(range.start, 0); expect(range.end, 5); @@ -1492,9 +1392,7 @@ void _canvasTests() { }); test('Paragraph dispose', () { - final CkParagraphBuilder builder = CkParagraphBuilder( - CkParagraphStyle(), - ); + final CkParagraphBuilder builder = CkParagraphBuilder(CkParagraphStyle()); builder.addText('Hello'); final CkParagraph paragraph = builder.build(); @@ -1504,62 +1402,46 @@ void _canvasTests() { test('toImage.toByteData', () async { final SkPictureRecorder otherRecorder = SkPictureRecorder(); - final SkCanvas otherCanvas = otherRecorder - .beginRecording(Float32List.fromList([0, 0, 1, 1])); + final SkCanvas otherCanvas = otherRecorder.beginRecording( + Float32List.fromList([0, 0, 1, 1]), + ); otherCanvas.drawRect( Float32List.fromList([0, 0, 1, 1]), SkPaint()..setColorInt(0xAAFFFFFF), ); - final CkPicture picture = - CkPicture(otherRecorder.finishRecordingAsPicture()); + final CkPicture picture = CkPicture(otherRecorder.finishRecordingAsPicture()); final CkImage image = await picture.toImage(1, 1) as CkImage; - final ByteData rawData = - await image.toByteData(); + final ByteData rawData = await image.toByteData(); expect(rawData.lengthInBytes, greaterThan(0)); - expect( - rawData.buffer.asUint32List(), - [0xAAAAAAAA], + expect(rawData.buffer.asUint32List(), [0xAAAAAAAA]); + final ByteData rawStraightData = await image.toByteData( + format: ui.ImageByteFormat.rawStraightRgba, ); - final ByteData rawStraightData = - await image.toByteData(format: ui.ImageByteFormat.rawStraightRgba); expect(rawStraightData.lengthInBytes, greaterThan(0)); - expect( - rawStraightData.buffer.asUint32List(), - [0xAAFFFFFF], - ); - final ByteData pngData = - await image.toByteData(format: ui.ImageByteFormat.png); + expect(rawStraightData.buffer.asUint32List(), [0xAAFFFFFF]); + final ByteData pngData = await image.toByteData(format: ui.ImageByteFormat.png); expect(pngData.lengthInBytes, greaterThan(0)); }); } void _textStyleTests() { test('SkTextDecorationStyle mapping is correct', () { - expect(canvasKit.DecorationStyle.Solid.value, - ui.TextDecorationStyle.solid.index); - expect(canvasKit.DecorationStyle.Double.value, - ui.TextDecorationStyle.double.index); - expect(canvasKit.DecorationStyle.Dotted.value, - ui.TextDecorationStyle.dotted.index); - expect(canvasKit.DecorationStyle.Dashed.value, - ui.TextDecorationStyle.dashed.index); - expect(canvasKit.DecorationStyle.Wavy.value, - ui.TextDecorationStyle.wavy.index); + expect(canvasKit.DecorationStyle.Solid.value, ui.TextDecorationStyle.solid.index); + expect(canvasKit.DecorationStyle.Double.value, ui.TextDecorationStyle.double.index); + expect(canvasKit.DecorationStyle.Dotted.value, ui.TextDecorationStyle.dotted.index); + expect(canvasKit.DecorationStyle.Dashed.value, ui.TextDecorationStyle.dashed.index); + expect(canvasKit.DecorationStyle.Wavy.value, ui.TextDecorationStyle.wavy.index); }); test('ui.TextDecorationStyle converts to SkTextDecorationStyle', () { - for (final ui.TextDecorationStyle decorationStyle - in ui.TextDecorationStyle.values) { - expect(toSkTextDecorationStyle(decorationStyle).value, - decorationStyle.index); + for (final ui.TextDecorationStyle decorationStyle in ui.TextDecorationStyle.values) { + expect(toSkTextDecorationStyle(decorationStyle).value, decorationStyle.index); } }); test('SkTextBaseline mapping is correct', () { - expect(canvasKit.TextBaseline.Alphabetic.value, - ui.TextBaseline.alphabetic.index); - expect(canvasKit.TextBaseline.Ideographic.value, - ui.TextBaseline.ideographic.index); + expect(canvasKit.TextBaseline.Alphabetic.value, ui.TextBaseline.alphabetic.index); + expect(canvasKit.TextBaseline.Ideographic.value, ui.TextBaseline.ideographic.index); }); test('ui.TextBaseline converts to SkTextBaseline', () { @@ -1569,25 +1451,23 @@ void _textStyleTests() { }); test('SkPlaceholderAlignment mapping is correct', () { - expect(canvasKit.PlaceholderAlignment.Baseline.value, - ui.PlaceholderAlignment.baseline.index); - expect(canvasKit.PlaceholderAlignment.AboveBaseline.value, - ui.PlaceholderAlignment.aboveBaseline.index); - expect(canvasKit.PlaceholderAlignment.BelowBaseline.value, - ui.PlaceholderAlignment.belowBaseline.index); - expect(canvasKit.PlaceholderAlignment.Top.value, - ui.PlaceholderAlignment.top.index); - expect(canvasKit.PlaceholderAlignment.Bottom.value, - ui.PlaceholderAlignment.bottom.index); - expect(canvasKit.PlaceholderAlignment.Middle.value, - ui.PlaceholderAlignment.middle.index); + expect(canvasKit.PlaceholderAlignment.Baseline.value, ui.PlaceholderAlignment.baseline.index); + expect( + canvasKit.PlaceholderAlignment.AboveBaseline.value, + ui.PlaceholderAlignment.aboveBaseline.index, + ); + expect( + canvasKit.PlaceholderAlignment.BelowBaseline.value, + ui.PlaceholderAlignment.belowBaseline.index, + ); + expect(canvasKit.PlaceholderAlignment.Top.value, ui.PlaceholderAlignment.top.index); + expect(canvasKit.PlaceholderAlignment.Bottom.value, ui.PlaceholderAlignment.bottom.index); + expect(canvasKit.PlaceholderAlignment.Middle.value, ui.PlaceholderAlignment.middle.index); }); test('ui.PlaceholderAlignment converts to SkPlaceholderAlignment', () { - for (final ui.PlaceholderAlignment placeholderAlignment - in ui.PlaceholderAlignment.values) { - expect(toSkPlaceholderAlignment(placeholderAlignment).value, - placeholderAlignment.index); + for (final ui.PlaceholderAlignment placeholderAlignment in ui.PlaceholderAlignment.values) { + expect(toSkPlaceholderAlignment(placeholderAlignment).value, placeholderAlignment.index); } }); } @@ -1605,46 +1485,49 @@ void _paragraphTests() { props.textHeightBehavior = canvasKit.TextHeightBehavior.All; props.maxLines = 4; props.ellipsis = '___'; - props.textStyle = SkTextStyleProperties() - ..backgroundColor = Float32List.fromList([0.2, 0, 0, 0.5]) - ..color = Float32List.fromList([0, 1, 0, 1]) - ..foregroundColor = Float32List.fromList([1, 0, 1, 1]) - ..decoration = 0x2 - ..decorationThickness = 2.0 - ..decorationColor = Float32List.fromList([13, 14, 15, 16]) - ..decorationStyle = canvasKit.DecorationStyle.Dotted - ..textBaseline = canvasKit.TextBaseline.Ideographic - ..fontSize = 48 - ..letterSpacing = 5 - ..wordSpacing = 10 - ..heightMultiplier = 1.3 - ..halfLeading = true - ..locale = 'en_CA' - ..fontFamilies = ['Roboto', 'serif'] - ..fontStyle = (SkFontStyle() - ..slant = canvasKit.FontSlant.Upright - ..weight = canvasKit.FontWeight.Normal) - ..shadows = [] - ..fontFeatures = [ - SkFontFeature() - ..name = 'pnum' - ..value = 1, - SkFontFeature() - ..name = 'tnum' - ..value = 1, - ] - ; - props.strutStyle = SkStrutStyleProperties() - ..fontFamilies = ['Roboto', 'Noto'] - ..fontStyle = (SkFontStyle() - ..slant = canvasKit.FontSlant.Italic - ..weight = canvasKit.FontWeight.Bold) - ..fontSize = 72 - ..heightMultiplier = 1.5 - ..halfLeading = true - ..leading = 0 - ..strutEnabled = true - ..forceStrutHeight = false; + props.textStyle = + SkTextStyleProperties() + ..backgroundColor = Float32List.fromList([0.2, 0, 0, 0.5]) + ..color = Float32List.fromList([0, 1, 0, 1]) + ..foregroundColor = Float32List.fromList([1, 0, 1, 1]) + ..decoration = 0x2 + ..decorationThickness = 2.0 + ..decorationColor = Float32List.fromList([13, 14, 15, 16]) + ..decorationStyle = canvasKit.DecorationStyle.Dotted + ..textBaseline = canvasKit.TextBaseline.Ideographic + ..fontSize = 48 + ..letterSpacing = 5 + ..wordSpacing = 10 + ..heightMultiplier = 1.3 + ..halfLeading = true + ..locale = 'en_CA' + ..fontFamilies = ['Roboto', 'serif'] + ..fontStyle = + (SkFontStyle() + ..slant = canvasKit.FontSlant.Upright + ..weight = canvasKit.FontWeight.Normal) + ..shadows = [] + ..fontFeatures = [ + SkFontFeature() + ..name = 'pnum' + ..value = 1, + SkFontFeature() + ..name = 'tnum' + ..value = 1, + ]; + props.strutStyle = + SkStrutStyleProperties() + ..fontFamilies = ['Roboto', 'Noto'] + ..fontStyle = + (SkFontStyle() + ..slant = canvasKit.FontSlant.Italic + ..weight = canvasKit.FontWeight.Bold) + ..fontSize = 72 + ..heightMultiplier = 1.5 + ..halfLeading = true + ..leading = 0 + ..strutEnabled = true + ..forceStrutHeight = false; final SkParagraphStyle paragraphStyle = canvasKit.ParagraphStyle(props); final SkParagraphBuilder builder = canvasKit.ParagraphBuilder.MakeFromFontCollection( @@ -1660,26 +1543,29 @@ void _paragraphTests() { canvasKit.TextBaseline.Ideographic, 4.0, ); - builder.pushStyle(canvasKit.TextStyle(SkTextStyleProperties() - ..color = Float32List.fromList([1, 0, 0, 1]) - ..fontSize = 24 - ..fontFamilies = ['Roboto', 'serif'] - )); + builder.pushStyle( + canvasKit.TextStyle( + SkTextStyleProperties() + ..color = Float32List.fromList([1, 0, 0, 1]) + ..fontSize = 24 + ..fontFamilies = ['Roboto', 'serif'], + ), + ); builder.addText('World'); builder.pop(); builder.pushPaintStyle( - canvasKit.TextStyle(SkTextStyleProperties() - ..color = Float32List.fromList([1, 0, 0, 1]) - ..fontSize = 60 - ..fontFamilies = ['Roboto', 'serif'] + canvasKit.TextStyle( + SkTextStyleProperties() + ..color = Float32List.fromList([1, 0, 0, 1]) + ..fontSize = 60 + ..fontFamilies = ['Roboto', 'serif'], ), SkPaint()..setColorInt(0xFF0000FF), SkPaint()..setColorInt(0xFFFF0000), ); builder.addText('!'); builder.pop(); - builder.pushStyle( - canvasKit.TextStyle(SkTextStyleProperties()..halfLeading = true)); + builder.pushStyle(canvasKit.TextStyle(SkTextStyleProperties()..halfLeading = true)); builder.pop(); if (canvasKit.ParagraphBuilder.RequiresClientICU()) { injectClientICU(builder); @@ -1687,10 +1573,7 @@ void _paragraphTests() { final SkParagraph paragraph = builder.build(); paragraph.layout(500); - final DomCanvasElement canvas = createDomCanvasElement( - width: 400, - height: 160, - ); + final DomCanvasElement canvas = createDomCanvasElement(width: 400, height: 160); domDocument.body!.append(canvas); // TODO(yjbanov): WebGL screenshot tests do not work on Firefox - https://github.com/flutter/flutter/issues/109265 @@ -1700,7 +1583,12 @@ void _paragraphTests() { skCanvas.drawColorInt(0xFFCCCCCC, toSkBlendMode(ui.BlendMode.srcOver)); skCanvas.drawParagraph(paragraph, 20, 20); skCanvas.drawRect( - Float32List.fromList([20, 20, 20 + paragraph.getMaxIntrinsicWidth(), 20 + paragraph.getHeight()]), + Float32List.fromList([ + 20, + 20, + 20 + paragraph.getMaxIntrinsicWidth(), + 20 + paragraph.getHeight(), + ]), SkPaint() ..setStyle(toSkPaintStyle(ui.PaintingStyle.stroke)) ..setStrokeWidth(1) @@ -1727,20 +1615,14 @@ void _paragraphTests() { expectAlmost(paragraph.getMinIntrinsicWidth(), 135); expectAlmost(paragraph.getMaxWidth(), 500); final SkRectWithDirection rectWithDirection = - paragraph.getRectsForRange( - 1, - 3, - canvasKit.RectHeightStyle.Tight, - canvasKit.RectWidthStyle.Max).single; - expect( - rectWithDirection.rect, - hasLength(4), - ); + paragraph + .getRectsForRange(1, 3, canvasKit.RectHeightStyle.Tight, canvasKit.RectWidthStyle.Max) + .single; + expect(rectWithDirection.rect, hasLength(4)); expect(paragraph.getRectsForPlaceholders(), hasLength(1)); expect(paragraph.getLineMetrics(), hasLength(1)); - final SkLineMetrics lineMetrics = - paragraph.getLineMetrics().single; + final SkLineMetrics lineMetrics = paragraph.getLineMetrics().single; expectAlmost(lineMetrics.ascent, 55.6); expectAlmost(lineMetrics.descent, 14.8); expect(lineMetrics.isHardBreak, isTrue); @@ -1750,10 +1632,7 @@ void _paragraphTests() { expectAlmost(lineMetrics.width, 263); expect(lineMetrics.lineNumber, 0); - expect( - paragraph.getGlyphPositionAtCoordinate(5, 5).affinity, - canvasKit.Affinity.Upstream, - ); + expect(paragraph.getGlyphPositionAtCoordinate(5, 5).affinity, canvasKit.Affinity.Upstream); // "Hello" for (int i = 0; i < 5; i++) { @@ -1780,20 +1659,21 @@ void _paragraphTests() { props.heightMultiplier = 3; props.textAlign = canvasKit.TextAlign.Start; props.textDirection = canvasKit.TextDirection.LTR; - props.textStyle = SkTextStyleProperties() - ..fontSize = 25 - ..fontFamilies = ['Roboto'] - ..fontStyle = (SkFontStyle()..weight = canvasKit.FontWeight.Normal); - props.strutStyle = SkStrutStyleProperties() - ..strutEnabled = true - ..forceStrutHeight = true - ..fontSize = 25 - ..fontFamilies = ['Roboto'] - ..heightMultiplier = 3 - ..fontStyle = (SkFontStyle()..weight = canvasKit.FontWeight.Normal); + props.textStyle = + SkTextStyleProperties() + ..fontSize = 25 + ..fontFamilies = ['Roboto'] + ..fontStyle = (SkFontStyle()..weight = canvasKit.FontWeight.Normal); + props.strutStyle = + SkStrutStyleProperties() + ..strutEnabled = true + ..forceStrutHeight = true + ..fontSize = 25 + ..fontFamilies = ['Roboto'] + ..heightMultiplier = 3 + ..fontStyle = (SkFontStyle()..weight = canvasKit.FontWeight.Normal); final SkParagraphStyle paragraphStyle = canvasKit.ParagraphStyle(props); - final SkParagraphBuilder builder = - canvasKit.ParagraphBuilder.MakeFromFontCollection( + final SkParagraphBuilder builder = canvasKit.ParagraphBuilder.MakeFromFontCollection( paragraphStyle, CanvasKitRenderer.instance.fontCollection.skFontCollection, ); @@ -1818,78 +1698,70 @@ void _paragraphTests() { }); test('TextHeightBehavior', () { + expect(toSkTextHeightBehavior(const ui.TextHeightBehavior()), canvasKit.TextHeightBehavior.All); expect( - toSkTextHeightBehavior(const ui.TextHeightBehavior()), - canvasKit.TextHeightBehavior.All, - ); - expect( - toSkTextHeightBehavior(const ui.TextHeightBehavior( - applyHeightToFirstAscent: false, - )), + toSkTextHeightBehavior(const ui.TextHeightBehavior(applyHeightToFirstAscent: false)), canvasKit.TextHeightBehavior.DisableFirstAscent, ); expect( - toSkTextHeightBehavior(const ui.TextHeightBehavior( - applyHeightToLastDescent: false, - )), + toSkTextHeightBehavior(const ui.TextHeightBehavior(applyHeightToLastDescent: false)), canvasKit.TextHeightBehavior.DisableLastDescent, ); expect( - toSkTextHeightBehavior(const ui.TextHeightBehavior( - applyHeightToFirstAscent: false, - applyHeightToLastDescent: false, - )), + toSkTextHeightBehavior( + const ui.TextHeightBehavior( + applyHeightToFirstAscent: false, + applyHeightToLastDescent: false, + ), + ), canvasKit.TextHeightBehavior.DisableAll, ); }); - test('MakeOnScreenGLSurface test', () { - final DomCanvasElement canvas = createDomCanvasElement( - width: 100, - height: 100, - ); - final WebGLContext gl = canvas.getGlContext(webGLVersion); - final int sampleCount = gl.getParameter(gl.samples); - final int stencilBits = gl.getParameter(gl.stencilBits); - - final double glContext = canvasKit.GetWebGLContext( - canvas, - SkWebGLContextOptions( - antialias: 0, - majorVersion: webGLVersion.toDouble(), - ), - ); - final SkGrContext grContext = canvasKit.MakeGrContext(glContext); - final SkSurface? skSurface = canvasKit.MakeOnScreenGLSurface( - grContext, - 100, - 100, - SkColorSpaceSRGB, - sampleCount, - stencilBits - ); - - expect(skSurface, isNotNull); - }, skip: isFirefox); // Intended: Headless firefox has no webgl support https://github.com/flutter/flutter/issues/109265 - - test('MakeRenderTarget test', () { - final DomCanvasElement canvas = createDomCanvasElement( - width: 100, - height: 100, - ); + test( + 'MakeOnScreenGLSurface test', + () { + final DomCanvasElement canvas = createDomCanvasElement(width: 100, height: 100); + final WebGLContext gl = canvas.getGlContext(webGLVersion); + final int sampleCount = gl.getParameter(gl.samples); + final int stencilBits = gl.getParameter(gl.stencilBits); - final int glContext = canvasKit.GetWebGLContext( - canvas, - SkWebGLContextOptions( - antialias: 0, - majorVersion: webGLVersion.toDouble(), - ), - ).toInt(); - final SkGrContext grContext = canvasKit.MakeGrContext(glContext.toDouble()); - final SkSurface? surface = canvasKit.MakeRenderTarget(grContext, 1, 1); + final double glContext = canvasKit.GetWebGLContext( + canvas, + SkWebGLContextOptions(antialias: 0, majorVersion: webGLVersion.toDouble()), + ); + final SkGrContext grContext = canvasKit.MakeGrContext(glContext); + final SkSurface? skSurface = canvasKit.MakeOnScreenGLSurface( + grContext, + 100, + 100, + SkColorSpaceSRGB, + sampleCount, + stencilBits, + ); - expect(surface, isNotNull); - }, skip: isFirefox); // Intended: Headless firefox has no webgl support https://github.com/flutter/flutter/issues/109265 + expect(skSurface, isNotNull); + }, + skip: isFirefox, + ); // Intended: Headless firefox has no webgl support https://github.com/flutter/flutter/issues/109265 + + test( + 'MakeRenderTarget test', + () { + final DomCanvasElement canvas = createDomCanvasElement(width: 100, height: 100); + + final int glContext = + canvasKit.GetWebGLContext( + canvas, + SkWebGLContextOptions(antialias: 0, majorVersion: webGLVersion.toDouble()), + ).toInt(); + final SkGrContext grContext = canvasKit.MakeGrContext(glContext.toDouble()); + final SkSurface? surface = canvasKit.MakeRenderTarget(grContext, 1, 1); + + expect(surface, isNotNull); + }, + skip: isFirefox, + ); // Intended: Headless firefox has no webgl support https://github.com/flutter/flutter/issues/109265 group('getCanvasKitJsFileNames', () { JSAny? oldV8BreakIterator = v8BreakIterator; @@ -1943,7 +1815,10 @@ void _paragraphTests() { // https://github.com/flutter/flutter/issues/122331 expect(getCanvasKitJsFileNames(CanvasKitVariant.full), ['canvaskit.js']); expect(getCanvasKitJsFileNames(CanvasKitVariant.chromium), ['chromium/canvaskit.js']); - expect(getCanvasKitJsFileNames(CanvasKitVariant.auto), ['chromium/canvaskit.js', 'canvaskit.js']); + expect(getCanvasKitJsFileNames(CanvasKitVariant.auto), [ + 'chromium/canvaskit.js', + 'canvaskit.js', + ]); v8BreakIterator = null; browserSupportsImageDecoder = false; @@ -1978,7 +1853,6 @@ void _paragraphTests() { }); } - @JS('window.Intl.v8BreakIterator') external JSAny? get v8BreakIterator; diff --git a/lib/web_ui/test/canvaskit/canvaskit_api_tt_on_test.dart b/lib/web_ui/test/canvaskit/canvaskit_api_tt_on_test.dart index 1827f23ef6bd8..0819442e8a8f4 100644 --- a/lib/web_ui/test/canvaskit/canvaskit_api_tt_on_test.dart +++ b/lib/web_ui/test/canvaskit/canvaskit_api_tt_on_test.dart @@ -55,8 +55,9 @@ void testMainWithTTOn() { /// void enableTrustedTypes() { print('Enabling TrustedTypes in browser window...'); - final DomHTMLMetaElement enableTTMeta = createDomHTMLMetaElement() - ..setAttribute('http-equiv', 'Content-Security-Policy') - ..content = "require-trusted-types-for 'script'"; + final DomHTMLMetaElement enableTTMeta = + createDomHTMLMetaElement() + ..setAttribute('http-equiv', 'Content-Security-Policy') + ..content = "require-trusted-types-for 'script'"; domDocument.head!.append(enableTTMeta); } diff --git a/lib/web_ui/test/canvaskit/color_filter_golden_test.dart b/lib/web_ui/test/canvaskit/color_filter_golden_test.dart index 49b8c895f1693..fa5ec73d4b6b2 100644 --- a/lib/web_ui/test/canvaskit/color_filter_golden_test.dart +++ b/lib/web_ui/test/canvaskit/color_filter_golden_test.dart @@ -38,12 +38,14 @@ void testMain() { builder.addPicture(ui.Offset.zero, redCircle); // Apply a "greyscale" color filter. - builder.pushColorFilter(const ui.ColorFilter.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, // - ])); + builder.pushColorFilter( + const ui.ColorFilter.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, // + ]), + ); // Draw another red circle and apply it to the scene. // This one should be grey since we have the color filter. @@ -104,22 +106,30 @@ void testMain() { builder.pushOffset(0, 0); // Draw a red, green, and blue square with the inverted color matrix. - builder.pushColorFilter(const ui.ColorFilter.matrix([ - -1, 0, 0, 0, 255, // - 0, -1, 0, 0, 255, // - 0, 0, -1, 0, 255, // - 0, 0, 0, 1, 0, // - ])); + builder.pushColorFilter( + const ui.ColorFilter.matrix([ + -1, 0, 0, 0, 255, // + 0, -1, 0, 0, 255, // + 0, 0, -1, 0, 255, // + 0, 0, 0, 1, 0, // + ]), + ); final CkPictureRecorder recorder = CkPictureRecorder(); final CkCanvas canvas = recorder.beginRecording(region); - canvas.drawRect(const ui.Rect.fromLTWH(50, 50, 100, 100), - CkPaint()..color = const ui.Color.fromARGB(255, 255, 0, 0)); - canvas.drawRect(const ui.Rect.fromLTWH(200, 50, 100, 100), - CkPaint()..color = const ui.Color.fromARGB(255, 0, 255, 0)); - canvas.drawRect(const ui.Rect.fromLTWH(350, 50, 100, 100), - CkPaint()..color = const ui.Color.fromARGB(255, 0, 0, 255)); + canvas.drawRect( + const ui.Rect.fromLTWH(50, 50, 100, 100), + CkPaint()..color = const ui.Color.fromARGB(255, 255, 0, 0), + ); + canvas.drawRect( + const ui.Rect.fromLTWH(200, 50, 100, 100), + CkPaint()..color = const ui.Color.fromARGB(255, 0, 255, 0), + ); + canvas.drawRect( + const ui.Rect.fromLTWH(350, 50, 100, 100), + CkPaint()..color = const ui.Color.fromARGB(255, 0, 0, 255), + ); final CkPicture invertedSquares = recorder.endRecording(); builder.addPicture(ui.Offset.zero, invertedSquares); @@ -129,7 +139,7 @@ void testMain() { test('ColorFilter color with 0 opacity', () async { final LayerSceneBuilder builder = LayerSceneBuilder(); - builder.pushOffset(0,0); + builder.pushOffset(0, 0); final CkPictureRecorder recorder = CkPictureRecorder(); final CkCanvas canvas = recorder.beginRecording(region); @@ -141,7 +151,9 @@ void testMain() { final CkPicture redCircle1 = recorder.endRecording(); builder.addPicture(ui.Offset.zero, redCircle1); - builder.pushColorFilter(ui.ColorFilter.mode(const ui.Color(0x00000000).withOpacity(0), ui.BlendMode.srcOver)); + builder.pushColorFilter( + ui.ColorFilter.mode(const ui.Color(0x00000000).withOpacity(0), ui.BlendMode.srcOver), + ); // Draw another red circle and apply it to the scene. // This one should also be red with the color filter doing nothing @@ -157,8 +169,10 @@ void testMain() { builder.addPicture(ui.Offset.zero, redCircle2); await matchSceneGolden( - 'canvaskit_transparent_colorfilter.png', builder.build(), - region: region); + 'canvaskit_transparent_colorfilter.png', + builder.build(), + region: region, + ); }); test('ColorFilter with dst blend mode', () async { @@ -176,8 +190,7 @@ void testMain() { builder.addPicture(ui.Offset.zero, redCircle1); // Push dst color filter - builder.pushColorFilter( - const ui.ColorFilter.mode(ui.Color(0xffff0000), ui.BlendMode.dst)); + builder.pushColorFilter(const ui.ColorFilter.mode(ui.Color(0xffff0000), ui.BlendMode.dst)); // Draw another red circle and apply it to the scene. // This one should also be red with the color filter doing nothing @@ -192,8 +205,7 @@ void testMain() { builder.addPicture(ui.Offset.zero, redCircle2); - await matchSceneGolden('canvaskit_dst_colorfilter.png', builder.build(), - region: region); + await matchSceneGolden('canvaskit_dst_colorfilter.png', builder.build(), region: region); }); test('ColorFilter only applies to child bounds', () async { @@ -215,8 +227,7 @@ void testMain() { builder.addPicture(ui.Offset.zero, redCircle); // Apply a green color filter. - builder.pushColorFilter( - const ui.ColorFilter.mode(ui.Color(0xff00ff00), ui.BlendMode.color)); + builder.pushColorFilter(const ui.ColorFilter.mode(ui.Color(0xff00ff00), ui.BlendMode.color)); // Draw another red circle and apply it to the scene. // This one should be green since we have the color filter. final CkPictureRecorder recorder2 = CkPictureRecorder(); @@ -231,11 +242,7 @@ void testMain() { builder.addPicture(ui.Offset.zero, greenCircle); - await matchSceneGolden( - 'canvaskit_colorfilter_bounds.png', - builder.build(), - region: region, - ); + await matchSceneGolden('canvaskit_colorfilter_bounds.png', builder.build(), region: region); }); test('ColorFilter works as an ImageFilter', () async { @@ -257,8 +264,7 @@ void testMain() { builder.addPicture(ui.Offset.zero, redCircle); // Apply a green color filter as an ImageFilter. - builder.pushImageFilter( - const ui.ColorFilter.mode(ui.Color(0xff00ff00), ui.BlendMode.color)); + builder.pushImageFilter(const ui.ColorFilter.mode(ui.Color(0xff00ff00), ui.BlendMode.color)); // Draw another red circle and apply it to the scene. // This one should be green since we have the color filter. final CkPictureRecorder recorder2 = CkPictureRecorder(); diff --git a/lib/web_ui/test/canvaskit/common.dart b/lib/web_ui/test/canvaskit/common.dart index b3624123686f9..8e0a9633dfaa4 100644 --- a/lib/web_ui/test/canvaskit/common.dart +++ b/lib/web_ui/test/canvaskit/common.dart @@ -27,30 +27,27 @@ void setUpCanvasKitTest({bool withImplicitView = false}) { setUpTestViewDimensions: false, ); - setUp(() => debugOverrideJsConfiguration({ - 'fontFallbackBaseUrl': 'assets/fallback_fonts/', - }.jsify() as JsFlutterConfiguration?)); + setUp( + () => debugOverrideJsConfiguration( + {'fontFallbackBaseUrl': 'assets/fallback_fonts/'}.jsify() + as JsFlutterConfiguration?, + ), + ); } /// Convenience getter for the implicit view. -EngineFlutterWindow get implicitView => - EnginePlatformDispatcher.instance.implicitView!; +EngineFlutterWindow get implicitView => EnginePlatformDispatcher.instance.implicitView!; /// Utility function for CanvasKit tests to draw pictures without /// the [CkPictureRecorder] boilerplate. -CkPicture paintPicture( - ui.Rect cullRect, void Function(CkCanvas canvas) painter) { +CkPicture paintPicture(ui.Rect cullRect, void Function(CkCanvas canvas) painter) { final CkPictureRecorder recorder = CkPictureRecorder(); final CkCanvas canvas = recorder.beginRecording(cullRect); painter(canvas); return recorder.endRecording(); } -Future matchSceneGolden( - String goldenFile, - ui.Scene scene, { - required ui.Rect region, -}) async { +Future matchSceneGolden(String goldenFile, ui.Scene scene, {required ui.Rect region}) async { await renderScene(scene); await matchGoldenFile(goldenFile, region: region); } @@ -59,8 +56,11 @@ Future matchSceneGolden( /// /// The picture is drawn onto the UI at [ui.Offset.zero] with no additional /// layers. -Future matchPictureGolden(String goldenFile, CkPicture picture, - {required ui.Rect region}) async { +Future matchPictureGolden( + String goldenFile, + CkPicture picture, { + required ui.Rect region, +}) async { final LayerSceneBuilder sb = LayerSceneBuilder(); sb.pushOffset(0, 0); sb.addPicture(ui.Offset.zero, picture); @@ -72,8 +72,7 @@ Future matchImage(ui.Image left, ui.Image right) async { if (left.width != right.width || left.height != right.height) { return false; } - int getPixel(ByteData data, int x, int y) => - data.getUint32((x + y * left.width) * 4); + int getPixel(ByteData data, int x, int y) => data.getUint32((x + y * left.width) * 4); final ByteData leftData = (await left.toByteData())!; final ByteData rightData = (await right.toByteData())!; for (int y = 0; y < left.height; y++) { @@ -91,13 +90,7 @@ 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, - }, - )), + codec.encodeMethodCall(MethodCall('create', {'id': id, 'viewType': viewType})), (dynamic _) => completer.complete(), ); return completer.future; @@ -125,15 +118,15 @@ CkParagraph makeSimpleText( ui.FontWeight? fontWeight, ui.Color? color, }) { - final CkParagraphBuilder builder = CkParagraphBuilder(CkParagraphStyle( - fontFamily: fontFamily ?? 'Roboto', - fontSize: fontSize ?? 14, - fontStyle: fontStyle ?? ui.FontStyle.normal, - fontWeight: fontWeight ?? ui.FontWeight.normal, - )); - builder.pushStyle(CkTextStyle( - color: color ?? const ui.Color(0xFF000000), - )); + final CkParagraphBuilder builder = CkParagraphBuilder( + CkParagraphStyle( + fontFamily: fontFamily ?? 'Roboto', + fontSize: fontSize ?? 14, + fontStyle: fontStyle ?? ui.FontStyle.normal, + fontWeight: fontWeight ?? ui.FontWeight.normal, + ), + ); + builder.pushStyle(CkTextStyle(color: color ?? const ui.Color(0xFF000000))); builder.addText(text); builder.pop(); final CkParagraph paragraph = builder.build(); diff --git a/lib/web_ui/test/canvaskit/configuration_canvaskit_variant_test.dart b/lib/web_ui/test/canvaskit/configuration_canvaskit_variant_test.dart index 6166cd3b86347..cd66d1442dfbd 100644 --- a/lib/web_ui/test/canvaskit/configuration_canvaskit_variant_test.dart +++ b/lib/web_ui/test/canvaskit/configuration_canvaskit_variant_test.dart @@ -33,9 +33,7 @@ void testMain() { expect(configuration.canvasKitVariant, isNot(CanvasKitVariant.auto)); debugOverrideJsConfiguration( - { - 'canvasKitVariant': 'auto', - }.jsify() as JsFlutterConfiguration? + {'canvasKitVariant': 'auto'}.jsify() as JsFlutterConfiguration?, ); expect(configuration.canvasKitVariant, CanvasKitVariant.auto); diff --git a/lib/web_ui/test/canvaskit/display_canvas_factory_test.dart b/lib/web_ui/test/canvaskit/display_canvas_factory_test.dart index 228ce121a41df..832c874d87ee0 100644 --- a/lib/web_ui/test/canvaskit/display_canvas_factory_test.dart +++ b/lib/web_ui/test/canvaskit/display_canvas_factory_test.dart @@ -33,9 +33,9 @@ void testMain() { setUpCanvasKitTest(withImplicitView: true); test('getCanvas', () { - final DisplayCanvasFactory factory = - DisplayCanvasFactory( - createCanvas: () => DummyDisplayCanvas()); + final DisplayCanvasFactory factory = DisplayCanvasFactory( + createCanvas: () => DummyDisplayCanvas(), + ); expect(factory.baseCanvas, isNotNull); expect(factory.debugSurfaceCount, equals(1)); @@ -54,9 +54,9 @@ void testMain() { }); test('releaseCanvas', () { - final DisplayCanvasFactory factory = - DisplayCanvasFactory( - createCanvas: () => DummyDisplayCanvas()); + final DisplayCanvasFactory factory = DisplayCanvasFactory( + createCanvas: () => DummyDisplayCanvas(), + ); // Create a new canvas and immediately release it. final DisplayCanvas canvas = factory.getCanvas(); @@ -69,9 +69,9 @@ void testMain() { }); test('isLive', () { - final DisplayCanvasFactory factory = - DisplayCanvasFactory( - createCanvas: () => DummyDisplayCanvas()); + final DisplayCanvasFactory factory = DisplayCanvasFactory( + createCanvas: () => DummyDisplayCanvas(), + ); expect(factory.isLive(factory.baseCanvas), isTrue); @@ -87,17 +87,13 @@ void testMain() { expect(canvas.isConnected, isFalse); } - final EngineFlutterView implicitView = - EnginePlatformDispatcher.instance.implicitView!; + final EngineFlutterView implicitView = EnginePlatformDispatcher.instance.implicitView!; final DisplayCanvasFactory originalFactory = - CanvasKitRenderer.instance - .debugGetRasterizerForView(implicitView)! - .displayFactory; + CanvasKitRenderer.instance.debugGetRasterizerForView(implicitView)!.displayFactory; // Cause the surface and its canvas to be attached to the page - implicitView.dom.sceneHost - .prepend(originalFactory.baseCanvas.hostElement); + implicitView.dom.sceneHost.prepend(originalFactory.baseCanvas.hostElement); expect(originalFactory.baseCanvas.isConnected, isTrue); // Create a few overlay canvases diff --git a/lib/web_ui/test/canvaskit/embedded_views_test.dart b/lib/web_ui/test/canvaskit/embedded_views_test.dart index 231aa8ea2ff35..1b0cf7c5d54ec 100644 --- a/lib/web_ui/test/canvaskit/embedded_views_test.dart +++ b/lib/web_ui/test/canvaskit/embedded_views_test.dart @@ -14,8 +14,7 @@ import 'package:ui/ui_web/src/ui_web.dart' as ui_web; import 'common.dart'; import 'test_data.dart'; -EngineFlutterWindow get implicitView => - EnginePlatformDispatcher.instance.implicitView!; +EngineFlutterWindow get implicitView => EnginePlatformDispatcher.instance.implicitView!; DomElement get platformViewsHost => implicitView.dom.platformViewsHost; DomElement get sceneHost => implicitView.dom.sceneHost; @@ -58,16 +57,17 @@ void testMain() { final DomElement contentsHost = contents.parent!; final DomElement slotHost = slot.parent!; - expect(contents, isNotNull, - reason: 'The view from the factory is injected in the DOM.'); + expect(contents, isNotNull, reason: 'The view from the factory is injected in the DOM.'); expect(contentsHost.tagName, equalsIgnoringCase('flt-platform-view')); expect(slotHost.tagName, equalsIgnoringCase('flt-platform-view-slot')); - expect(slotHost.style.pointerEvents, 'auto', - reason: 'The slot reenables pointer events.'); - expect(contentsHost.getAttribute('slot'), slot.getAttribute('name'), - reason: 'The contents and slot are correctly related.'); + expect(slotHost.style.pointerEvents, 'auto', reason: 'The slot reenables pointer events.'); + expect( + contentsHost.getAttribute('slot'), + slot.getAttribute('name'), + reason: 'The contents and slot are correctly related.', + ); }); test('clips platform views with RRects', () async { @@ -79,35 +79,18 @@ void testMain() { final LayerSceneBuilder sb = LayerSceneBuilder(); sb.pushOffset(0, 0); - sb.pushClipRRect( - ui.RRect.fromLTRBR(0, 0, 10, 10, const ui.Radius.circular(3))); + sb.pushClipRRect(ui.RRect.fromLTRBR(0, 0, 10, 10, const ui.Radius.circular(3))); sb.addPlatformView(0, width: 10, height: 10); await renderScene(sb.build()); + expect(sceneHost.querySelectorAll('#sk_path_defs').single, isNotNull); expect( - sceneHost.querySelectorAll('#sk_path_defs').single, + sceneHost.querySelectorAll('#sk_path_defs').single.querySelectorAll('clipPath').single, isNotNull, ); - expect( - sceneHost - .querySelectorAll('#sk_path_defs') - .single - .querySelectorAll('clipPath') - .single, - isNotNull, - ); - expect( - sceneHost.querySelectorAll('flt-clip').single.style.clipPath, - 'url("#svgClip1")', - ); - expect( - sceneHost.querySelectorAll('flt-clip').single.style.width, - '100%', - ); - expect( - sceneHost.querySelectorAll('flt-clip').single.style.height, - '100%', - ); + expect(sceneHost.querySelectorAll('flt-clip').single.style.clipPath, 'url("#svgClip1")'); + expect(sceneHost.querySelectorAll('flt-clip').single.style.width, '100%'); + expect(sceneHost.querySelectorAll('flt-clip').single.style.height, '100%'); }); test('correctly transforms platform views', () async { @@ -119,17 +102,17 @@ void testMain() { final LayerSceneBuilder sb = LayerSceneBuilder(); sb.pushOffset(0, 0); - final Matrix4 scaleMatrix = Matrix4.identity() - ..scale(5, 5) - ..translate(100, 100); + final Matrix4 scaleMatrix = + Matrix4.identity() + ..scale(5, 5) + ..translate(100, 100); sb.pushTransform(scaleMatrix.toFloat64()); sb.pushOffset(3, 3); sb.addPlatformView(0, width: 10, height: 10); await renderScene(sb.build()); // Transformations happen on the slot element. - final DomElement slotHost = - sceneHost.querySelector('flt-platform-view-slot')!; + final DomElement slotHost = sceneHost.querySelector('flt-platform-view-slot')!; expect( slotHost.style.transform, @@ -151,8 +134,7 @@ void testMain() { sb.addPlatformView(0, offset: const ui.Offset(3, 4), width: 5, height: 6); await renderScene(sb.build()); - final DomElement slotHost = - sceneHost.querySelector('flt-platform-view-slot')!; + final DomElement slotHost = sceneHost.querySelector('flt-platform-view-slot')!; final DomCSSStyleDeclaration style = slotHost.style; expect(style.transform, 'matrix(1, 0, 0, 1, 3, 4)'); @@ -172,8 +154,7 @@ void testMain() { List getTransformChain(DomElement viewHost) { final List chain = []; DomElement? element = viewHost; - while (element != null && - element.tagName.toLowerCase() != 'flt-scene-host') { + while (element != null && element.tagName.toLowerCase() != 'flt-scene-host') { chain.add(element.style.transform); element = element.parent; } @@ -198,13 +179,10 @@ void testMain() { // Transformations happen on the slot element. DomElement slotHost = sceneHost.querySelector('flt-platform-view-slot')!; - expect( - getTransformChain(slotHost), - [ - 'matrix(1, 0, 0, 1, 6, 6)', - 'matrix(1, 0, 0, 1, 3, 3)', - ], - ); + expect(getTransformChain(slotHost), [ + 'matrix(1, 0, 0, 1, 6, 6)', + 'matrix(1, 0, 0, 1, 3, 3)', + ]); sb = LayerSceneBuilder(); sb.pushOffset(3, 3); @@ -218,14 +196,11 @@ void testMain() { // Transformations happen on the slot element. slotHost = sceneHost.querySelector('flt-platform-view-slot')!; - expect( - getTransformChain(slotHost), - [ - 'matrix(1, 0, 0, 1, 9, 9)', - 'matrix(1, 0, 0, 1, 6, 6)', - 'matrix(1, 0, 0, 1, 3, 3)', - ], - ); + expect(getTransformChain(slotHost), [ + 'matrix(1, 0, 0, 1, 9, 9)', + 'matrix(1, 0, 0, 1, 6, 6)', + 'matrix(1, 0, 0, 1, 3, 3)', + ]); }); test('converts device pixels to logical pixels (no clips)', () async { @@ -244,13 +219,9 @@ void testMain() { await renderScene(sb.build()); // Transformations happen on the slot element. - final DomElement slotHost = - sceneHost.querySelector('flt-platform-view-slot')!; + final DomElement slotHost = sceneHost.querySelector('flt-platform-view-slot')!; - expect( - getTransformChain(slotHost), - ['matrix(0.25, 0, 0, 0.25, 1.5, 1.5)'], - ); + expect(getTransformChain(slotHost), ['matrix(0.25, 0, 0, 0.25, 1.5, 1.5)']); }); test('converts device pixels to logical pixels (with clips)', () async { @@ -271,22 +242,19 @@ void testMain() { await renderScene(sb.build()); // Transformations happen on the slot element. - final DomElement slotHost = - sceneHost.querySelector('flt-platform-view-slot')!; + final DomElement slotHost = sceneHost.querySelector('flt-platform-view-slot')!; - expect( - getTransformChain(slotHost), - [ - 'matrix(1, 0, 0, 1, 9, 9)', - 'matrix(1, 0, 0, 1, 6, 6)', - 'matrix(0.25, 0, 0, 0.25, 0.75, 0.75)', - ], - ); + expect(getTransformChain(slotHost), [ + 'matrix(1, 0, 0, 1, 9, 9)', + 'matrix(1, 0, 0, 1, 6, 6)', + 'matrix(0.25, 0, 0, 0.25, 0.75, 0.75)', + ]); }); test('renders overlays on top of platform views', () async { - final CkPicture testPicture = - paintPicture(const ui.Rect.fromLTRB(0, 0, 10, 10), (CkCanvas canvas) { + final CkPicture testPicture = paintPicture(const ui.Rect.fromLTRB(0, 0, 10, 10), ( + CkCanvas canvas, + ) { canvas.drawCircle(const ui.Offset(5, 5), 5, CkPaint()); }); @@ -404,10 +372,7 @@ void testMain() { final Completer completer = Completer(); ui.PlatformDispatcher.instance.sendPlatformMessage( 'flutter/platform_views', - codec.encodeMethodCall(MethodCall( - 'dispose', - id, - )), + codec.encodeMethodCall(MethodCall('dispose', id)), completer.complete, ); await completer.future; @@ -420,7 +385,8 @@ void testMain() { expect( error.toString(), contains( - 'Cannot render platform views: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15. These views have not been created, or they have been deleted.'), + 'Cannot render platform views: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15. These views have not been created, or they have been deleted.', + ), ); } @@ -433,8 +399,9 @@ void testMain() { }); test('correctly reuses overlays', () async { - final CkPicture testPicture = - paintPicture(const ui.Rect.fromLTRB(0, 0, 10, 10), (CkCanvas canvas) { + final CkPicture testPicture = paintPicture(const ui.Rect.fromLTRB(0, 0, 10, 10), ( + CkCanvas canvas, + ) { canvas.drawCircle(const ui.Offset(5, 5), 5, CkPaint()); }); @@ -571,9 +538,7 @@ void testMain() { sb.pushOffset(0, 0); sb.addPlatformView(0, width: 10, height: 10); await renderScene(sb.build()); - _expectSceneMatches(<_EmbeddedViewMarker>[ - _platformView, - ]); + _expectSceneMatches(<_EmbeddedViewMarker>[_platformView]); expect(platformViewsHost.querySelector('flt-platform-view'), isNotNull); @@ -589,52 +554,48 @@ void testMain() { }); test( - 'does not crash when resizing the window after textures have been registered', - () async { - ui_web.platformViewRegistry.registerViewFactory( - 'test-platform-view', - (int viewId) => createDomHTMLDivElement()..id = 'view-0', - ); - await createPlatformView(0, 'test-platform-view'); + 'does not crash when resizing the window after textures have been registered', + () async { + ui_web.platformViewRegistry.registerViewFactory( + 'test-platform-view', + (int viewId) => createDomHTMLDivElement()..id = 'view-0', + ); + await createPlatformView(0, 'test-platform-view'); - final CkBrowserImageDecoder image = await CkBrowserImageDecoder.create( - contentType: 'image/gif', - data: kAnimatedGif, - debugSource: 'test', - ); - final ui.FrameInfo frame = await image.getNextFrame(); - final CkImage ckImage = frame.image as CkImage; + final CkBrowserImageDecoder image = await CkBrowserImageDecoder.create( + contentType: 'image/gif', + data: kAnimatedGif, + debugSource: 'test', + ); + final ui.FrameInfo frame = await image.getNextFrame(); + final CkImage ckImage = frame.image as CkImage; - final LayerSceneBuilder sb = LayerSceneBuilder(); - sb.pushOffset(0, 0); - final CkPictureRecorder recorder = CkPictureRecorder(); - final CkCanvas canvas = recorder.beginRecording(ui.Rect.largest); - canvas.drawImage(ckImage, ui.Offset.zero, CkPaint()); - final CkPicture picture = recorder.endRecording(); - sb.addPicture(ui.Offset.zero, picture); - sb.addPlatformView(0, width: 10, height: 10); + final LayerSceneBuilder sb = LayerSceneBuilder(); + sb.pushOffset(0, 0); + final CkPictureRecorder recorder = CkPictureRecorder(); + final CkCanvas canvas = recorder.beginRecording(ui.Rect.largest); + canvas.drawImage(ckImage, ui.Offset.zero, CkPaint()); + final CkPicture picture = recorder.endRecording(); + sb.addPicture(ui.Offset.zero, picture); + sb.addPlatformView(0, width: 10, height: 10); - implicitView.debugPhysicalSizeOverride = const ui.Size(100, 100); - implicitView.debugForceResize(); - await renderScene(sb.build()); - _expectSceneMatches(<_EmbeddedViewMarker>[ - _overlay, - _platformView, - ]); + implicitView.debugPhysicalSizeOverride = const ui.Size(100, 100); + implicitView.debugForceResize(); + await renderScene(sb.build()); + _expectSceneMatches(<_EmbeddedViewMarker>[_overlay, _platformView]); - implicitView.debugPhysicalSizeOverride = const ui.Size(200, 200); - implicitView.debugForceResize(); - await renderScene(sb.build()); - _expectSceneMatches(<_EmbeddedViewMarker>[ - _overlay, - _platformView, - ]); + implicitView.debugPhysicalSizeOverride = const ui.Size(200, 200); + implicitView.debugForceResize(); + await renderScene(sb.build()); + _expectSceneMatches(<_EmbeddedViewMarker>[_overlay, _platformView]); - implicitView.debugPhysicalSizeOverride = null; - implicitView.debugForceResize(); + implicitView.debugPhysicalSizeOverride = null; + implicitView.debugForceResize(); - // ImageDecoder is not supported in Safari or Firefox. - }, skip: isSafari || isFirefox); + // ImageDecoder is not supported in Safari or Firefox. + }, + skip: isSafari || isFirefox, + ); test('removes the DOM node of an unrendered platform view', () async { ui_web.platformViewRegistry.registerViewFactory( @@ -647,9 +608,7 @@ void testMain() { sb.pushOffset(0, 0); sb.addPlatformView(0, width: 10, height: 10); await renderScene(sb.build()); - _expectSceneMatches(<_EmbeddedViewMarker>[ - _platformView, - ]); + _expectSceneMatches(<_EmbeddedViewMarker>[_platformView]); expect(platformViewsHost.querySelector('flt-platform-view'), isNotNull); @@ -659,14 +618,9 @@ void testMain() { sb.pushOffset(0, 0); sb.addPlatformView(1, width: 10, height: 10); await renderScene(sb.build()); - _expectSceneMatches(<_EmbeddedViewMarker>[ - _platformView, - ]); + _expectSceneMatches(<_EmbeddedViewMarker>[_platformView]); - expect( - platformViewsHost.querySelectorAll('flt-platform-view'), - hasLength(2), - ); + expect(platformViewsHost.querySelectorAll('flt-platform-view'), hasLength(2)); // Render a frame without a platform view, but also without disposing of // the platform view. @@ -677,15 +631,10 @@ void testMain() { // The actual contents of the platform view are kept in the dom, until // it's actually disposed of! - expect( - platformViewsHost.querySelectorAll('flt-platform-view'), - hasLength(2), - ); + expect(platformViewsHost.querySelectorAll('flt-platform-view'), hasLength(2)); }); - test( - 'removes old SVG clip definitions from the DOM when the view is recomposited', - () async { + test('removes old SVG clip definitions from the DOM when the view is recomposited', () async { ui_web.platformViewRegistry.registerViewFactory( 'test-platform-view', (int viewId) => createDomHTMLDivElement()..id = 'test-view', @@ -695,8 +644,7 @@ void testMain() { Future renderTestScene() async { final LayerSceneBuilder sb = LayerSceneBuilder(); sb.pushOffset(0, 0); - sb.pushClipRRect( - ui.RRect.fromLTRBR(0, 0, 10, 10, const ui.Radius.circular(3))); + sb.pushClipRRect(ui.RRect.fromLTRBR(0, 0, 10, 10, const ui.Radius.circular(3))); sb.addPlatformView(0, width: 10, height: 10); await renderScene(sb.build()); } @@ -715,8 +663,7 @@ void testMain() { expect(skPathDefs.childNodes, hasLength(1)); }); - test('does not crash when a prerolled platform view is not composited', - () async { + test('does not crash when a prerolled platform view is not composited', () async { ui_web.platformViewRegistry.registerViewFactory( 'test-platform-view', (int viewId) => createDomHTMLDivElement()..id = 'view-0', @@ -734,18 +681,18 @@ void testMain() { }); test('does not create overlays for invisible platform views', () async { - final CkPicture testPicture = - paintPicture(const ui.Rect.fromLTRB(0, 0, 10, 10), (CkCanvas canvas) { + final CkPicture testPicture = paintPicture(const ui.Rect.fromLTRB(0, 0, 10, 10), ( + CkCanvas canvas, + ) { canvas.drawCircle(const ui.Offset(5, 5), 5, CkPaint()); }); ui_web.platformViewRegistry.registerViewFactory( - 'test-visible-view', - (int viewId) => - createDomHTMLDivElement()..className = 'visible-platform-view'); + 'test-visible-view', + (int viewId) => createDomHTMLDivElement()..className = 'visible-platform-view', + ); ui_web.platformViewRegistry.registerViewFactory( 'test-invisible-view', - (int viewId) => - createDomHTMLDivElement()..className = 'invisible-platform-view', + (int viewId) => createDomHTMLDivElement()..className = 'invisible-platform-view', isVisible: false, ); await createPlatformView(0, 'test-visible-view'); @@ -801,9 +748,7 @@ void testMain() { _platformView, _overlay, _platformView, - ], - reason: - 'Overlays created after each group containing a visible view.'); + ], reason: 'Overlays created after each group containing a visible view.'); sb = LayerSceneBuilder(); sb.pushOffset(0, 0); @@ -932,9 +877,7 @@ void testMain() { _platformView, _platformView, _overlay, - ], - reason: - 'Many invisible views can be rendered on top of the base overlay.'); + ], reason: 'Many invisible views can be rendered on top of the base overlay.'); sb = LayerSceneBuilder(); sb.pushOffset(0, 0); @@ -981,41 +924,36 @@ void testMain() { test('can dispose without crashing', () async { ui_web.platformViewRegistry.registerViewFactory( - 'test-view', - (int viewId) => - createDomHTMLDivElement()..className = 'platform-view', - isVisible: false); + 'test-view', + (int viewId) => createDomHTMLDivElement()..className = 'platform-view', + isVisible: false, + ); await createPlatformView(0, 'test-view'); await createPlatformView(1, 'test-view'); await createPlatformView(2, 'test-view'); - final LayerSceneBuilder sb = LayerSceneBuilder() - ..pushOffset(0, 0) - ..addPlatformView(0, width: 10, height: 10) - ..addPlatformView(1, width: 10, height: 10) - ..addPlatformView(2, width: 10, height: 10) - ..pop(); + final LayerSceneBuilder sb = + LayerSceneBuilder() + ..pushOffset(0, 0) + ..addPlatformView(0, width: 10, height: 10) + ..addPlatformView(1, width: 10, height: 10) + ..addPlatformView(2, width: 10, height: 10) + ..pop(); await renderScene(sb.build()); - _expectSceneMatches(<_EmbeddedViewMarker>[ - _platformView, - _platformView, - _platformView, - ]); + _expectSceneMatches(<_EmbeddedViewMarker>[_platformView, _platformView, _platformView]); expect(() { - final HtmlViewEmbedder embedder = (renderer as CanvasKitRenderer) - .debugGetRasterizerForView(implicitView)! - .viewEmbedder; + final HtmlViewEmbedder embedder = + (renderer as CanvasKitRenderer).debugGetRasterizerForView(implicitView)!.viewEmbedder; // The following line used to cause a "Concurrent modification during iteration" embedder.dispose(); }, returnsNormally); }); - test('optimizes overlays when pictures and platform views do not overlap', - () async { + test('optimizes overlays when pictures and platform views do not overlap', () async { ui_web.platformViewRegistry.registerViewFactory( 'test-view', (int viewId) => createDomHTMLDivElement()..className = 'platform-view', @@ -1023,8 +961,7 @@ void testMain() { CkPicture rectPicture(ui.Rect rect) { return paintPicture(rect, (CkCanvas canvas) { - canvas.drawRect( - rect, CkPaint()..color = const ui.Color.fromARGB(255, 255, 0, 0)); + canvas.drawRect(rect, CkPaint()..color = const ui.Color.fromARGB(255, 255, 0, 0)); }); } @@ -1041,18 +978,12 @@ void testMain() { // only need one overlay at the end of the scene. final LayerSceneBuilder sb1 = LayerSceneBuilder(); sb1.pushOffset(0, 0); - sb1.addPlatformView(0, - offset: const ui.Offset(10, 10), width: 50, height: 50); - sb1.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(12, 12, 10, 10))); - sb1.addPlatformView(1, - offset: const ui.Offset(70, 10), width: 50, height: 50); - sb1.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(72, 12, 10, 10))); - sb1.addPlatformView(2, - offset: const ui.Offset(130, 10), width: 50, height: 50); - sb1.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(132, 12, 10, 10))); + sb1.addPlatformView(0, offset: const ui.Offset(10, 10), width: 50, height: 50); + sb1.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(12, 12, 10, 10))); + sb1.addPlatformView(1, offset: const ui.Offset(70, 10), width: 50, height: 50); + sb1.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(72, 12, 10, 10))); + sb1.addPlatformView(2, offset: const ui.Offset(130, 10), width: 50, height: 50); + sb1.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(132, 12, 10, 10))); final LayerScene scene1 = sb1.build(); await renderScene(scene1); _expectSceneMatches(<_EmbeddedViewMarker>[ @@ -1067,20 +998,13 @@ void testMain() { // pictures. final LayerSceneBuilder sb2 = LayerSceneBuilder(); sb2.pushOffset(0, 0); - sb2.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(0, 0, 300, 300))); - sb2.addPlatformView(0, - offset: const ui.Offset(10, 10), width: 50, height: 50); - sb2.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(12, 12, 10, 10))); - sb2.addPlatformView(1, - offset: const ui.Offset(70, 10), width: 50, height: 50); - sb2.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(72, 12, 10, 10))); - sb2.addPlatformView(2, - offset: const ui.Offset(130, 10), width: 50, height: 50); - sb2.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(132, 12, 10, 10))); + sb2.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(0, 0, 300, 300))); + sb2.addPlatformView(0, offset: const ui.Offset(10, 10), width: 50, height: 50); + sb2.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(12, 12, 10, 10))); + sb2.addPlatformView(1, offset: const ui.Offset(70, 10), width: 50, height: 50); + sb2.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(72, 12, 10, 10))); + sb2.addPlatformView(2, offset: const ui.Offset(130, 10), width: 50, height: 50); + sb2.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(132, 12, 10, 10))); final LayerScene scene2 = sb2.build(); await renderScene(scene2); _expectSceneMatches(<_EmbeddedViewMarker>[ @@ -1096,20 +1020,13 @@ void testMain() { // platform view. final LayerSceneBuilder sb3 = LayerSceneBuilder(); sb3.pushOffset(0, 0); - sb3.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(0, 0, 300, 300))); - sb3.addPlatformView(0, - offset: const ui.Offset(10, 10), width: 50, height: 50); - sb3.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(0, 0, 300, 300))); - sb3.addPlatformView(1, - offset: const ui.Offset(70, 10), width: 50, height: 50); - sb3.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(0, 0, 300, 300))); - sb3.addPlatformView(2, - offset: const ui.Offset(130, 10), width: 50, height: 50); - sb3.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(0, 0, 300, 300))); + sb3.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(0, 0, 300, 300))); + sb3.addPlatformView(0, offset: const ui.Offset(10, 10), width: 50, height: 50); + sb3.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(0, 0, 300, 300))); + sb3.addPlatformView(1, offset: const ui.Offset(70, 10), width: 50, height: 50); + sb3.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(0, 0, 300, 300))); + sb3.addPlatformView(2, offset: const ui.Offset(130, 10), width: 50, height: 50); + sb3.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(0, 0, 300, 300))); final LayerScene scene3 = sb3.build(); await renderScene(scene3); _expectSceneMatches(<_EmbeddedViewMarker>[ @@ -1128,20 +1045,13 @@ void testMain() { // case every drawing can go in a base canvas. final LayerSceneBuilder sb4 = LayerSceneBuilder(); sb4.pushOffset(0, 0); - sb4.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(0, 0, 300, 300))); - sb4.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(10, 10, 50, 50))); - sb4.addPlatformView(0, - offset: const ui.Offset(10, 10), width: 50, height: 50); - sb4.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(70, 10, 50, 50))); - sb4.addPlatformView(1, - offset: const ui.Offset(70, 10), width: 50, height: 50); - sb4.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(130, 10, 50, 50))); - sb4.addPlatformView(2, - offset: const ui.Offset(130, 10), width: 50, height: 50); + sb4.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(0, 0, 300, 300))); + sb4.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(10, 10, 50, 50))); + sb4.addPlatformView(0, offset: const ui.Offset(10, 10), width: 50, height: 50); + sb4.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(70, 10, 50, 50))); + sb4.addPlatformView(1, offset: const ui.Offset(70, 10), width: 50, height: 50); + sb4.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(130, 10, 50, 50))); + sb4.addPlatformView(2, offset: const ui.Offset(130, 10), width: 50, height: 50); final LayerScene scene4 = sb4.build(); await renderScene(scene4); _expectSceneMatches(<_EmbeddedViewMarker>[ @@ -1156,26 +1066,16 @@ void testMain() { // one. final LayerSceneBuilder sb5 = LayerSceneBuilder(); sb5.pushOffset(0, 0); - sb5.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(0, 0, 300, 300))); - sb5.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(10, 10, 50, 50))); - sb5.addPlatformView(0, - offset: const ui.Offset(10, 10), width: 50, height: 50); - sb5.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(12, 12, 10, 10))); - sb5.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(70, 10, 50, 50))); - sb5.addPlatformView(1, - offset: const ui.Offset(70, 10), width: 50, height: 50); - sb5.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(72, 12, 10, 10))); - sb5.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(130, 10, 50, 50))); - sb5.addPlatformView(2, - offset: const ui.Offset(130, 10), width: 50, height: 50); - sb5.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(132, 12, 10, 10))); + sb5.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(0, 0, 300, 300))); + sb5.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(10, 10, 50, 50))); + sb5.addPlatformView(0, offset: const ui.Offset(10, 10), width: 50, height: 50); + sb5.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(12, 12, 10, 10))); + sb5.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(70, 10, 50, 50))); + sb5.addPlatformView(1, offset: const ui.Offset(70, 10), width: 50, height: 50); + sb5.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(72, 12, 10, 10))); + sb5.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(130, 10, 50, 50))); + sb5.addPlatformView(2, offset: const ui.Offset(130, 10), width: 50, height: 50); + sb5.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(132, 12, 10, 10))); final LayerScene scene5 = sb5.build(); await renderScene(scene5); _expectSceneMatches(<_EmbeddedViewMarker>[ @@ -1187,8 +1087,7 @@ void testMain() { ]); }); - test( - 'correctly places pictures in case where next ' + test('correctly places pictures in case where next ' 'picture intersects multiple elements', () async { ui_web.platformViewRegistry.registerViewFactory( 'test-view', @@ -1196,15 +1095,13 @@ void testMain() { ); ui_web.platformViewRegistry.registerViewFactory( 'invisible-view', - (int viewId) => - createDomHTMLDivElement()..className = 'invisible-platform-view', + (int viewId) => createDomHTMLDivElement()..className = 'invisible-platform-view', isVisible: false, ); CkPicture rectPicture(ui.Rect rect) { return paintPicture(rect, (CkCanvas canvas) { - canvas.drawRect( - rect, CkPaint()..color = const ui.Color.fromARGB(255, 255, 0, 0)); + canvas.drawRect(rect, CkPaint()..color = const ui.Color.fromARGB(255, 255, 0, 0)); }); } @@ -1216,38 +1113,26 @@ void testMain() { final LayerSceneBuilder sb = LayerSceneBuilder(); sb.pushOffset(0, 0); - sb.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(0, 0, 100, 100))); - sb.addPlatformView(0, - offset: const ui.Offset(10, 10), width: 50, height: 50); - sb.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(0, 0, 100, 100))); - sb.addPlatformView(1, - offset: const ui.Offset(10, 10), width: 50, height: 50); - sb.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(0, 0, 5, 5))); + sb.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(0, 0, 100, 100))); + sb.addPlatformView(0, offset: const ui.Offset(10, 10), width: 50, height: 50); + sb.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(0, 0, 100, 100))); + sb.addPlatformView(1, offset: const ui.Offset(10, 10), width: 50, height: 50); + sb.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(0, 0, 5, 5))); final LayerScene scene = sb.build(); await renderScene(scene); - _expectSceneMatches(<_EmbeddedViewMarker>[ - _overlay, - _platformView, - _platformView, - _overlay, - ]); - - final Rendering rendering = CanvasKitRenderer.instance - .debugGetRasterizerForView(implicitView)! - .viewEmbedder - .debugActiveRendering; - final List picturesPerCanvas = rendering.canvases - .map((RenderingRenderCanvas canvas) => canvas.pictures.length) - .toList(); + _expectSceneMatches(<_EmbeddedViewMarker>[_overlay, _platformView, _platformView, _overlay]); + + final Rendering rendering = + CanvasKitRenderer.instance + .debugGetRasterizerForView(implicitView)! + .viewEmbedder + .debugActiveRendering; + final List picturesPerCanvas = + rendering.canvases.map((RenderingRenderCanvas canvas) => canvas.pictures.length).toList(); expect(picturesPerCanvas, [1, 2]); }); - test( - 'sinks platform view under the canvas if it does not overlap with the picture', - () async { + test('sinks platform view under the canvas if it does not overlap with the picture', () async { ui_web.platformViewRegistry.registerViewFactory( 'test-view', (int viewId) => createDomHTMLDivElement()..className = 'platform-view', @@ -1256,8 +1141,7 @@ void testMain() { CkPicture rectPicture(double l, double t, double w, double h) { final ui.Rect rect = ui.Rect.fromLTWH(l, t, w, h); return paintPicture(rect, (CkCanvas canvas) { - canvas.drawRect( - rect, CkPaint()..color = const ui.Color.fromARGB(255, 255, 0, 0)); + canvas.drawRect(rect, CkPaint()..color = const ui.Color.fromARGB(255, 255, 0, 0)); }); } @@ -1272,49 +1156,24 @@ void testMain() { // First picture-view-picture stack. { sb.pushOffset(0, 0); - sb.addPicture( - ui.Offset.zero, - rectPicture(0, 0, 10, 10), - ); - sb.addPlatformView( - 0, - width: 10, - height: 10, - ); - sb.addPicture( - ui.Offset.zero, - rectPicture(2, 2, 5, 5), - ); + sb.addPicture(ui.Offset.zero, rectPicture(0, 0, 10, 10)); + sb.addPlatformView(0, width: 10, height: 10); + sb.addPicture(ui.Offset.zero, rectPicture(2, 2, 5, 5)); sb.pop(); } // Second picture-view-picture stack that does not overlap with the first one. { sb.pushOffset(20, 0); - sb.addPicture( - ui.Offset.zero, - rectPicture(0, 0, 10, 10), - ); - sb.addPlatformView( - 1, - width: 10, - height: 10, - ); - sb.addPicture( - ui.Offset.zero, - rectPicture(2, 2, 5, 5), - ); + sb.addPicture(ui.Offset.zero, rectPicture(0, 0, 10, 10)); + sb.addPlatformView(1, width: 10, height: 10); + sb.addPicture(ui.Offset.zero, rectPicture(2, 2, 5, 5)); sb.pop(); } final LayerScene scene1 = sb.build(); await renderScene(scene1); - _expectSceneMatches(<_EmbeddedViewMarker>[ - _overlay, - _platformView, - _platformView, - _overlay, - ]); + _expectSceneMatches(<_EmbeddedViewMarker>[_overlay, _platformView, _platformView, _overlay]); }); test('optimizes overlays correctly with transforms and clips', () async { @@ -1325,8 +1184,7 @@ void testMain() { CkPicture rectPicture(ui.Rect rect) { return paintPicture(rect, (CkCanvas canvas) { - canvas.drawRect( - rect, CkPaint()..color = const ui.Color.fromARGB(255, 255, 0, 0)); + canvas.drawRect(rect, CkPaint()..color = const ui.Color.fromARGB(255, 255, 0, 0)); }); } @@ -1340,20 +1198,17 @@ void testMain() { final Matrix4 scaleMatrix = Matrix4.identity()..scale(3, 3, 1); sb.pushTransform(scaleMatrix.toFloat64()); sb.pushClipRect(const ui.Rect.fromLTWH(10, 10, 10, 10)); - sb.addPicture( - ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(0, 0, 20, 20))); + sb.addPicture(ui.Offset.zero, rectPicture(const ui.Rect.fromLTWH(0, 0, 20, 20))); sb.addPlatformView(0, width: 20, height: 20); final LayerScene scene = sb.build(); await renderScene(scene); - _expectSceneMatches(<_EmbeddedViewMarker>[ - _overlay, - _platformView, - ]); + _expectSceneMatches(<_EmbeddedViewMarker>[_overlay, _platformView]); }); test('can customize amount of overlays', () async { - final CkPicture testPicture = - paintPicture(const ui.Rect.fromLTRB(0, 0, 10, 10), (CkCanvas canvas) { + final CkPicture testPicture = paintPicture(const ui.Rect.fromLTRB(0, 0, 10, 10), ( + CkCanvas canvas, + ) { canvas.drawCircle(const ui.Offset(5, 5), 5, CkPaint()); }); @@ -1379,9 +1234,9 @@ void testMain() { } // Set maximum overlays to 4. - debugOverrideJsConfiguration({ - 'canvasKitMaximumSurfaces': 4, - }.jsify() as JsFlutterConfiguration?); + debugOverrideJsConfiguration( + {'canvasKitMaximumSurfaces': 4}.jsify() as JsFlutterConfiguration?, + ); await renderTestScene(viewCount: 8); _expectSceneMatches(<_EmbeddedViewMarker>[ @@ -1400,9 +1255,9 @@ void testMain() { ]); // Set maximum overlays to -1. Should default to 1. - debugOverrideJsConfiguration({ - 'canvasKitMaximumSurfaces': -1, - }.jsify() as JsFlutterConfiguration?); + debugOverrideJsConfiguration( + {'canvasKitMaximumSurfaces': -1}.jsify() as JsFlutterConfiguration?, + ); await renderTestScene(viewCount: 8); _expectSceneMatches(<_EmbeddedViewMarker>[ @@ -1420,11 +1275,11 @@ void testMain() { debugOverrideJsConfiguration(null); }); - test( - 'correctly rearranges pictures to second-to-last canvas ' + test('correctly rearranges pictures to second-to-last canvas ' 'when hitting canvas limit', () async { - final CkPicture testPicture = - paintPicture(const ui.Rect.fromLTRB(0, 0, 10, 10), (CkCanvas canvas) { + final CkPicture testPicture = paintPicture(const ui.Rect.fromLTRB(0, 0, 10, 10), ( + CkCanvas canvas, + ) { canvas.drawCircle(const ui.Offset(5, 5), 5, CkPaint()); }); @@ -1503,19 +1358,19 @@ void testMain() { ]); // The second-to-last canvas should have all the extra pictures. - final Rendering rendering = CanvasKitRenderer.instance - .debugGetRasterizerForView(implicitView)! - .viewEmbedder - .debugActiveRendering; - final List numPicturesPerCanvas = rendering.canvases - .map((RenderingRenderCanvas canvas) => canvas.pictures.length) - .toList(); + final Rendering rendering = + CanvasKitRenderer.instance + .debugGetRasterizerForView(implicitView)! + .viewEmbedder + .debugActiveRendering; + final List numPicturesPerCanvas = + rendering.canvases.map((RenderingRenderCanvas canvas) => canvas.pictures.length).toList(); expect(numPicturesPerCanvas, [1, 1, 1, 1, 1, 1, 12, 1]); // It should also work when the maximum canvases is just one. - debugOverrideJsConfiguration({ - 'canvasKitMaximumSurfaces': 1, - }.jsify() as JsFlutterConfiguration?); + debugOverrideJsConfiguration( + {'canvasKitMaximumSurfaces': 1}.jsify() as JsFlutterConfiguration?, + ); // Render scene with 20 pictures. Check that the last canvas contains the // pictures from the canvases that were deleted. @@ -1564,14 +1419,15 @@ void testMain() { ]); // The last canvas should have all the pictures. - final Rendering secondRendering = CanvasKitRenderer.instance - .debugGetRasterizerForView(implicitView)! - .viewEmbedder - .debugActiveRendering; - final List picturesPerCanvasInSecondRendering = secondRendering - .canvases - .map((RenderingRenderCanvas canvas) => canvas.pictures.length) - .toList(); + final Rendering secondRendering = + CanvasKitRenderer.instance + .debugGetRasterizerForView(implicitView)! + .viewEmbedder + .debugActiveRendering; + final List picturesPerCanvasInSecondRendering = + secondRendering.canvases + .map((RenderingRenderCanvas canvas) => canvas.pictures.length) + .toList(); expect(picturesPerCanvasInSecondRendering, [19]); debugOverrideJsConfiguration(null); }); @@ -1587,10 +1443,10 @@ void testMain() { CkPicture rectPicture(ui.Rect rect) { return paintPicture(rect, (CkCanvas canvas) { - canvas.drawRect( - rect, CkPaint()..color = const ui.Color.fromARGB(255, 255, 0, 0)); + canvas.drawRect(rect, CkPaint()..color = const ui.Color.fromARGB(255, 255, 0, 0)); }); } + final CkPicture picture = rectPicture(const ui.Rect.fromLTWH(0, 0, 20, 20)); await createPlatformView(0, 'test-view'); @@ -1610,31 +1466,24 @@ void testMain() { // Used to test that the platform views and overlays are in the correct order in // the scene. -enum _EmbeddedViewMarker { - overlay, - platformView, -} +enum _EmbeddedViewMarker { overlay, platformView } _EmbeddedViewMarker get _overlay => _EmbeddedViewMarker.overlay; _EmbeddedViewMarker get _platformView => _EmbeddedViewMarker.platformView; -const Map _tagToViewMarker = - { +const Map _tagToViewMarker = { 'flt-canvas-container': _EmbeddedViewMarker.overlay, 'flt-platform-view-slot': _EmbeddedViewMarker.platformView, 'flt-clip': _EmbeddedViewMarker.platformView, }; -void _expectSceneMatches( - List<_EmbeddedViewMarker> expectedMarkers, { - String? reason, -}) { +void _expectSceneMatches(List<_EmbeddedViewMarker> expectedMarkers, {String? reason}) { // Convert the scene elements to its corresponding array of _EmbeddedViewMarker - final List<_EmbeddedViewMarker> sceneElements = sceneHost.children - .where((DomElement element) => element.tagName != 'svg') - .map((DomElement element) => - _tagToViewMarker[element.tagName.toLowerCase()]!) - .toList(); + final List<_EmbeddedViewMarker> sceneElements = + sceneHost.children + .where((DomElement element) => element.tagName != 'svg') + .map((DomElement element) => _tagToViewMarker[element.tagName.toLowerCase()]!) + .toList(); expect(sceneElements, expectedMarkers, reason: reason); } diff --git a/lib/web_ui/test/canvaskit/filter_test.dart b/lib/web_ui/test/canvaskit/filter_test.dart index f02230fbd7f65..b9f797e0716f8 100644 --- a/lib/web_ui/test/canvaskit/filter_test.dart +++ b/lib/web_ui/test/canvaskit/filter_test.dart @@ -16,24 +16,68 @@ void main() { void testMain() { List createColorFilters() { - return [ - createCkColorFilter(const EngineColorFilter.mode(ui.Color(0x12345678), ui.BlendMode.srcOver))!, - createCkColorFilter(const EngineColorFilter.mode(ui.Color(0x12345678), ui.BlendMode.dstOver))!, - createCkColorFilter(const EngineColorFilter.mode(ui.Color(0x87654321), ui.BlendMode.dstOver))!, - createCkColorFilter(const EngineColorFilter.matrix([ - 1, 0, 0, 0, 0, - 0, 1, 0, 0, 0, - 0, 0, 1, 0, 0, - 0, 0, 0, 1, 0, - ]))!, - createCkColorFilter(EngineColorFilter.matrix(Float32List.fromList([ - 2, 0, 0, 0, 0, - 0, 2, 0, 0, 0, - 0, 0, 2, 0, 0, - 0, 0, 0, 2, 0, - ])))!, - createCkColorFilter(const EngineColorFilter.linearToSrgbGamma())!, - createCkColorFilter(const EngineColorFilter.srgbToLinearGamma())!, + return [ + createCkColorFilter( + const EngineColorFilter.mode(ui.Color(0x12345678), ui.BlendMode.srcOver), + )!, + createCkColorFilter( + const EngineColorFilter.mode(ui.Color(0x12345678), ui.BlendMode.dstOver), + )!, + createCkColorFilter( + const EngineColorFilter.mode(ui.Color(0x87654321), ui.BlendMode.dstOver), + )!, + createCkColorFilter( + const EngineColorFilter.matrix([ + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + ]), + )!, + createCkColorFilter( + EngineColorFilter.matrix( + Float32List.fromList([ + 2, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + ]), + ), + )!, + createCkColorFilter(const EngineColorFilter.linearToSrgbGamma())!, + createCkColorFilter(const EngineColorFilter.srgbToLinearGamma())!, ]; } @@ -44,7 +88,8 @@ void testMain() { CkImageFilter.blur(sigmaX: 6, sigmaY: 5, tileMode: ui.TileMode.decal), CkImageFilter.dilate(radiusX: 5, radiusY: 6), CkImageFilter.erode(radiusX: 7, radiusY: 8), - for (final CkColorFilter colorFilter in createColorFilters()) CkImageFilter.color(colorFilter: colorFilter), + for (final CkColorFilter colorFilter in createColorFilters()) + CkImageFilter.color(colorFilter: colorFilter), ]; filters.add(CkImageFilter.compose(outer: filters[0], inner: filters[1])); filters.add(CkImageFilter.compose(outer: filters[1], inner: filters[3])); @@ -99,22 +144,28 @@ void testMain() { final CkPaint paint = CkPaint(); paint.imageFilter = CkImageFilter.blur(sigmaX: 5, sigmaY: 10, tileMode: ui.TileMode.clamp); - final CkManagedSkImageFilterConvertible managedFilter1 = paint.imageFilter! as CkManagedSkImageFilterConvertible; + final CkManagedSkImageFilterConvertible managedFilter1 = + paint.imageFilter! as CkManagedSkImageFilterConvertible; paint.imageFilter = CkImageFilter.blur(sigmaX: 5, sigmaY: 10, tileMode: ui.TileMode.clamp); - final CkManagedSkImageFilterConvertible managedFilter2 = paint.imageFilter! as CkManagedSkImageFilterConvertible; + final CkManagedSkImageFilterConvertible managedFilter2 = + paint.imageFilter! as CkManagedSkImageFilterConvertible; expect(managedFilter1, same(managedFilter2)); }); test('does not throw for both sigmaX and sigmaY set to 0', () async { - final CkImageFilter imageFilter = CkImageFilter.blur(sigmaX: 0, sigmaY: 0, tileMode: ui.TileMode.clamp); + final CkImageFilter imageFilter = CkImageFilter.blur( + sigmaX: 0, + sigmaY: 0, + tileMode: ui.TileMode.clamp, + ); expect(imageFilter, isNotNull); const ui.Rect region = ui.Rect.fromLTRB(0, 0, 500, 250); final LayerSceneBuilder builder = LayerSceneBuilder(); - builder.pushOffset(0,0); + builder.pushOffset(0, 0); final CkPictureRecorder recorder = CkPictureRecorder(); final CkCanvas canvas = recorder.beginRecording(region); @@ -145,16 +196,15 @@ void testMain() { }); test('using a colorFilter', () async { - final CkColorFilter colorFilter = createCkColorFilter( - const EngineColorFilter.mode( - ui.Color.fromARGB(255, 0, 255, 0), - ui.BlendMode.srcIn - ))!; + final CkColorFilter colorFilter = + createCkColorFilter( + const EngineColorFilter.mode(ui.Color.fromARGB(255, 0, 255, 0), ui.BlendMode.srcIn), + )!; const ui.Rect region = ui.Rect.fromLTRB(0, 0, 500, 250); final LayerSceneBuilder builder = LayerSceneBuilder(); - builder.pushOffset(0,0); + builder.pushOffset(0, 0); builder.pushImageFilter(colorFilter); @@ -171,8 +221,10 @@ void testMain() { // The drawn red circle should actually be green with the colorFilter. await matchSceneGolden( - 'canvaskit_imageFilter_using_colorFilter.png', builder.build(), - region: region); + 'canvaskit_imageFilter_using_colorFilter.png', + builder.build(), + region: region, + ); }); test('using a compose filter', () async { @@ -181,13 +233,15 @@ void testMain() { sigmaY: 5, tileMode: ui.TileMode.clamp, ); - final CkColorFilter colorFilter = createCkColorFilter( - const EngineColorFilter.mode( - ui.Color.fromARGB(255, 0, 255, 0), ui.BlendMode.srcIn))!; - final CkImageFilter colorImageFilter = - CkImageFilter.color(colorFilter: colorFilter); - final CkImageFilter composeFilter = - CkImageFilter.compose(outer: blurFilter, inner: colorImageFilter); + final CkColorFilter colorFilter = + createCkColorFilter( + const EngineColorFilter.mode(ui.Color.fromARGB(255, 0, 255, 0), ui.BlendMode.srcIn), + )!; + final CkImageFilter colorImageFilter = CkImageFilter.color(colorFilter: colorFilter); + final CkImageFilter composeFilter = CkImageFilter.compose( + outer: blurFilter, + inner: colorImageFilter, + ); const ui.Rect region = ui.Rect.fromLTRB(0, 0, 500, 250); @@ -208,9 +262,7 @@ void testMain() { builder.addPicture(ui.Offset.zero, redCircle1); // The drawn red circle should actually be green and blurred. - await matchSceneGolden( - 'canvaskit_composeImageFilter.png', builder.build(), - region: region); + await matchSceneGolden('canvaskit_composeImageFilter.png', builder.build(), region: region); }); }); @@ -221,6 +273,5 @@ void testMain() { expect(() => paint.maskFilter = filter, isNot(throwsException)); }); - }); } diff --git a/lib/web_ui/test/canvaskit/flutter_tester_emulation_golden_test.dart b/lib/web_ui/test/canvaskit/flutter_tester_emulation_golden_test.dart index 3bc6a5cd3893e..b1fcb33f13cad 100644 --- a/lib/web_ui/test/canvaskit/flutter_tester_emulation_golden_test.dart +++ b/lib/web_ui/test/canvaskit/flutter_tester_emulation_golden_test.dart @@ -22,13 +22,13 @@ void testMain() { group('flutter_tester emulation', () { setUpCanvasKitTest(withImplicitView: true); - test('defaults to FlutterTest font family', - () async { + test('defaults to FlutterTest font family', () async { final CkPictureRecorder recorder = CkPictureRecorder(); final CkCanvas canvas = recorder.beginRecording(kDefaultRegion); canvas.translate(10, 10); - void drawTextWithOutline(String text, { + void drawTextWithOutline( + String text, { String? paragraphFontFamily, String? textFontFamily, List? textFontFallbacks, @@ -38,7 +38,9 @@ void testMain() { }) { final CkStrutStyle? strutStyle; - if (strutStyleFontFamily != null || strutStyleFontFallbacks != null || strutStyleFontSize != null) { + if (strutStyleFontFamily != null || + strutStyleFontFallbacks != null || + strutStyleFontSize != null) { strutStyle = CkStrutStyle( fontFamily: strutStyleFontFamily, fontFamilyFallback: strutStyleFontFallbacks, @@ -48,18 +50,16 @@ void testMain() { strutStyle = null; } - final CkParagraphBuilder builder = CkParagraphBuilder(CkParagraphStyle( - fontFamily: paragraphFontFamily, - strutStyle: strutStyle, - )); + final CkParagraphBuilder builder = CkParagraphBuilder( + CkParagraphStyle(fontFamily: paragraphFontFamily, strutStyle: strutStyle), + ); final bool needsTextStyle = textFontFamily != null || textFontFallbacks != null; if (needsTextStyle) { - builder.pushStyle(CkTextStyle( - fontFamily: textFontFamily, - fontFamilyFallback: textFontFallbacks, - )); + builder.pushStyle( + CkTextStyle(fontFamily: textFontFamily, fontFamilyFallback: textFontFallbacks), + ); } builder.addText(text); @@ -77,25 +77,13 @@ void testMain() { ..style = ui.PaintingStyle.stroke ..strokeWidth = 1, ); - canvas.translate( - 0, - paragraph.height + 16, - ); + canvas.translate(0, paragraph.height + 16); } drawTextWithOutline('default'); - drawTextWithOutline( - 'roboto paragraph', - paragraphFontFamily: 'Roboto', - ); - drawTextWithOutline( - 'roboto text', - textFontFamily: 'Roboto', - ); - drawTextWithOutline( - 'roboto text fallback', - textFontFallbacks: ['Roboto'], - ); + drawTextWithOutline('roboto paragraph', paragraphFontFamily: 'Roboto'); + drawTextWithOutline('roboto text', textFontFamily: 'Roboto'); + drawTextWithOutline('roboto text fallback', textFontFallbacks: ['Roboto']); drawTextWithOutline( 'roboto strut style', strutStyleFontFamily: 'Roboto', diff --git a/lib/web_ui/test/canvaskit/fragment_program_test.dart b/lib/web_ui/test/canvaskit/fragment_program_test.dart index b0899e1efda7b..184719722a170 100644 --- a/lib/web_ui/test/canvaskit/fragment_program_test.dart +++ b/lib/web_ui/test/canvaskit/fragment_program_test.dart @@ -287,15 +287,10 @@ void testMain() { expect(program.name, 'test'); { - final CkFragmentShader shader = - program.fragmentShader() as CkFragmentShader; + final CkFragmentShader shader = program.fragmentShader() as CkFragmentShader; shader.setFloat(0, 4); - expect( - reason: 'SkShaders are created lazily', - shader.ref, - isNull, - ); + expect(reason: 'SkShaders are created lazily', shader.ref, isNull); final SkShader skShader = shader.getSkShader(ui.FilterQuality.none); final UniqueRef ref = shader.ref!; @@ -309,8 +304,7 @@ void testMain() { } { - final CkFragmentShader shader = - program.fragmentShader() as CkFragmentShader; + final CkFragmentShader shader = program.fragmentShader() as CkFragmentShader; shader.setFloat(0, 5); final SkShader skShader1 = shader.getSkShader(ui.FilterQuality.none); @@ -345,9 +339,7 @@ void testMain() { } }); - test( - 'FragmentProgram can be created from JSON IPLR bundle with arrays and matrices', - () { + test('FragmentProgram can be created from JSON IPLR bundle with arrays and matrices', () { final Uint8List data = utf8.encode(kJsonArrayIPLR); final CkFragmentProgram program = CkFragmentProgram.fromBytes('test', data); @@ -355,7 +347,7 @@ void testMain() { expect( program.floatCount, 70, - reason: 'Columns, rows and array elements should be accounted for' + reason: 'Columns, rows and array elements should be accounted for', ); expect(program.textureCount, 0); expect(program.uniforms, hasLength(7)); diff --git a/lib/web_ui/test/canvaskit/image_golden_test.dart b/lib/web_ui/test/canvaskit/image_golden_test.dart index e0681ce6e1f02..659c84e6a14ce 100644 --- a/lib/web_ui/test/canvaskit/image_golden_test.dart +++ b/lib/web_ui/test/canvaskit/image_golden_test.dart @@ -46,7 +46,7 @@ abstract class TestFileCodec extends TestCodec { class UrlTestCodec extends TestFileCodec { UrlTestCodec(super.testFile, this.codecFactory, String function) - : super.fromTestFile(description: 'created with $function("$testFile")'); + : super.fromTestFile(description: 'created with $function("$testFile")'); final Future Function(String) codecFactory; @@ -57,13 +57,12 @@ class UrlTestCodec extends TestFileCodec { } class FetchTestCodec extends TestFileCodec { - FetchTestCodec( - super.testFile, - this.codecFactory, - String function, - ) : super.fromTestFile( - description: 'created with $function from bytes ' - 'fetch()\'ed from "$testFile"'); + FetchTestCodec(super.testFile, this.codecFactory, String function) + : super.fromTestFile( + description: + 'created with $function from bytes ' + 'fetch()\'ed from "$testFile"', + ); final Future Function(Uint8List) codecFactory; @@ -81,13 +80,12 @@ class FetchTestCodec extends TestFileCodec { } class BitmapTestCodec extends TestFileCodec { - BitmapTestCodec( - super.testFile, - this.codecFactory, - String function, - ) : super.fromTestFile( - description: 'created with $function from ImageBitmap' - ' created from "$testFile"'); + BitmapTestCodec(super.testFile, this.codecFactory, String function) + : super.fromTestFile( + description: + 'created with $function from ImageBitmap' + ' created from "$testFile"', + ); final Future Function(DomImageBitmap) codecFactory; @@ -99,8 +97,7 @@ class BitmapTestCodec extends TestFileCodec { await imageElement.decode(); - final DomImageBitmap bitmap = - await createImageBitmap(imageElement as JSObject, ( + final DomImageBitmap bitmap = await createImageBitmap(imageElement as JSObject, ( x: 0, y: 0, width: imageElement.naturalWidth.toInt(), @@ -137,11 +134,12 @@ class BitmapSingleFrameCodec implements ui.Codec { } Future testMain() async { - Future> createTestCodecs( - {int testTargetWidth = 300, int testTargetHeight = 300}) async { + Future> createTestCodecs({ + int testTargetWidth = 300, + int testTargetHeight = 300, + }) async { final HttpFetchResponse listingResponse = await httpFetch('/test_images/'); - final List testFiles = - (await listingResponse.json() as List).cast(); + final List testFiles = (await listingResponse.json() as List).cast(); // Sanity-check the test file list. If suddenly test files are moved or // deleted, and the test server returns an empty list, or is missing some @@ -155,13 +153,14 @@ Future testMain() async { final List testCodecs = []; for (final String testFile in testFiles) { - testCodecs.add(UrlTestCodec( - testFile, - (String file) => renderer.instantiateImageCodecFromUrl( - Uri.tryParse('/test_images/$file')!, + testCodecs.add( + UrlTestCodec( + testFile, + (String file) => + renderer.instantiateImageCodecFromUrl(Uri.tryParse('/test_images/$file')!), + 'renderer.instantiateImageFromUrl', ), - 'renderer.instantiateImageFromUrl', - )); + ); testCodecs.add( FetchTestCodec( '/test_images/$testFile', @@ -184,8 +183,7 @@ Future testMain() async { testCodecs.add( BitmapTestCodec( 'test_images/$testFile', - (DomImageBitmap bitmap) async => - renderer.createImageFromImageBitmap(bitmap), + (DomImageBitmap bitmap) async => renderer.createImageFromImageBitmap(bitmap), 'renderer.createImageFromImageBitmap', ), ); @@ -215,21 +213,18 @@ Future testMain() async { expect(image.height, isNonZero); expect(image.colorSpace, isNotNull); } catch (e) { - throw TestFailure( - 'Failed to get image for ${testCodec.description}: $e'); + throw TestFailure('Failed to get image for ${testCodec.description}: $e'); } }); - test('${testCodec.description} can be decoded with toByteData', - () async { + test('${testCodec.description} can be decoded with toByteData', () async { ui.Image image; try { final ui.Codec codec = await testCodec.getCodec(); final ui.FrameInfo frameInfo = await codec.getNextFrame(); image = frameInfo.image; } catch (e) { - throw TestFailure( - 'Failed to get image for ${testCodec.description}: $e'); + throw TestFailure('Failed to get image for ${testCodec.description}: $e'); } final ByteData? byteData = await image.toByteData(); @@ -246,7 +241,8 @@ Future testMain() async { expect( byteData.buffer.asUint8List().any((int byte) => byte > 0), isTrue, - reason: '${testCodec.description} toByteData() should ' + reason: + '${testCodec.description} toByteData() should ' 'contain nonzero value', ); }); @@ -254,12 +250,12 @@ Future testMain() async { }); test('crossOrigin requests cause an error', () async { - final String otherOrigin = - domWindow.location.origin.replaceAll('localhost', '127.0.0.1'); + final String otherOrigin = domWindow.location.origin.replaceAll('localhost', '127.0.0.1'); bool gotError = false; try { final ui.Codec _ = await renderer.instantiateImageCodecFromUrl( - Uri.parse('$otherOrigin/test_images/1x1.png')); + Uri.parse('$otherOrigin/test_images/1x1.png'), + ); } catch (e) { gotError = true; } @@ -272,45 +268,49 @@ Future testMain() async { expect(isAvif(Uint8List.fromList([])), isFalse); expect(isAvif(Uint8List.fromList([1, 2, 3])), isFalse); expect( - isAvif(Uint8List.fromList([ - 0x00, - 0x00, - 0x00, - 0x1c, - 0x66, - 0x74, - 0x79, - 0x70, - 0x61, - 0x76, - 0x69, - 0x66, - 0x00, - 0x00, - 0x00, - 0x00, - ])), + isAvif( + Uint8List.fromList([ + 0x00, + 0x00, + 0x00, + 0x1c, + 0x66, + 0x74, + 0x79, + 0x70, + 0x61, + 0x76, + 0x69, + 0x66, + 0x00, + 0x00, + 0x00, + 0x00, + ]), + ), isTrue, ); expect( - isAvif(Uint8List.fromList([ - 0x00, - 0x00, - 0x00, - 0x20, - 0x66, - 0x74, - 0x79, - 0x70, - 0x61, - 0x76, - 0x69, - 0x66, - 0x00, - 0x00, - 0x00, - 0x00, - ])), + isAvif( + Uint8List.fromList([ + 0x00, + 0x00, + 0x00, + 0x20, + 0x66, + 0x74, + 0x79, + 0x70, + 0x61, + 0x76, + 0x69, + 0x66, + 0x00, + 0x00, + 0x00, + 0x00, + ]), + ), isTrue, ); }); @@ -320,11 +320,9 @@ Future testMain() async { /// Tests specific to WASM codecs bundled with CanvasKit. void _testCkAnimatedImage() { test('ImageDecoder toByteData(PNG)', () async { - final CkAnimatedImage image = - CkAnimatedImage.decodeFromBytes(kAnimatedGif, 'test'); + final CkAnimatedImage image = CkAnimatedImage.decodeFromBytes(kAnimatedGif, 'test'); final ui.FrameInfo frame = await image.getNextFrame(); - final ByteData? png = - await frame.image.toByteData(format: ui.ImageByteFormat.png); + final ByteData? png = await frame.image.toByteData(format: ui.ImageByteFormat.png); expect(png, isNotNull); // The precise PNG encoding is browser-specific, but we can check the file @@ -333,8 +331,7 @@ void _testCkAnimatedImage() { }); test('CkAnimatedImage toByteData(RGBA)', () async { - final CkAnimatedImage image = - CkAnimatedImage.decodeFromBytes(kAnimatedGif, 'test'); + final CkAnimatedImage image = CkAnimatedImage.decodeFromBytes(kAnimatedGif, 'test'); const List> expectedColors = >[ [255, 0, 0, 255], [0, 255, 0, 255], diff --git a/lib/web_ui/test/canvaskit/image_test.dart b/lib/web_ui/test/canvaskit/image_test.dart index 9a30849365011..b6c898f5e822e 100644 --- a/lib/web_ui/test/canvaskit/image_test.dart +++ b/lib/web_ui/test/canvaskit/image_test.dart @@ -39,7 +39,7 @@ void testMain() { createdImage = image; }; - final ui.Image image1 = await _createImage(); + final ui.Image image1 = await _createImage(); expect(onCreateInvokedCount, 1); expect(createdImage, image1); @@ -58,12 +58,16 @@ void testMain() { disposedImage = image; }; - final ui.Image image1 = await _createImage()..dispose(); + final ui.Image image1 = + await _createImage() + ..dispose(); expect(onDisposeInvokedCount, 1); expect(disposedImage, image1); - final ui.Image image2 = await _createImage()..dispose(); + final ui.Image image2 = + await _createImage() + ..dispose(); expect(onDisposeInvokedCount, 2); expect(disposedImage, image2); @@ -72,7 +76,10 @@ void testMain() { test('fetchImage fetches image in chunks', () async { final List cumulativeBytesLoadedInvocations = []; final List expectedTotalBytesInvocations = []; - final Uint8List result = await fetchImage('/long_test_payload?length=100000&chunk=1000', (int cumulativeBytesLoaded, int expectedTotalBytes) { + final Uint8List result = await fetchImage('/long_test_payload?length=100000&chunk=1000', ( + int cumulativeBytesLoaded, + int expectedTotalBytes, + ) { cumulativeBytesLoadedInvocations.add(cumulativeBytesLoaded); expectedTotalBytesInvocations.add(expectedTotalBytes); }); @@ -95,10 +102,7 @@ void testMain() { expect(cumulativeBytesLoadedInvocations.last, 100000); // Check the contents of the returned data. - expect( - result, - List.generate(100000, (int i) => i & 0xFF), - ); + expect(result, List.generate(100000, (int i) => i & 0xFF)); }); test('scaledImageSize scales to a target width with no target height', () { @@ -114,12 +118,14 @@ void testMain() { final ui.Image image = await _createImage(); final ByteData? imageData = await image.toByteData(format: ui.ImageByteFormat.png); - final ui.ImmutableBuffer imageBuffer = await ui.ImmutableBuffer.fromUint8List(imageData!.buffer.asUint8List()); + final ui.ImmutableBuffer imageBuffer = await ui.ImmutableBuffer.fromUint8List( + imageData!.buffer.asUint8List(), + ); image.dispose(); final ui.Codec codec = await ui.instantiateImageCodecWithSize( imageBuffer, - getTargetSize: (w, h) => ui.TargetImageSize(width: w ~/ 2, height: h ~/ 2) + getTargetSize: (w, h) => ui.TargetImageSize(width: w ~/ 2, height: h ~/ 2), ); final ui.FrameInfo frameInfo = await codec.getNextFrame(); @@ -136,10 +142,12 @@ void testMain() { await domWindow.createImageBitmap(createBlankDomImageData(4, 4)), ); - final SkImage skImage1 = canvasKit.MakeAnimatedImageFromEncoded(k4x4PngImage)!.makeImageAtCurrentFrame(); + final SkImage skImage1 = + canvasKit.MakeAnimatedImageFromEncoded(k4x4PngImage)!.makeImageAtCurrentFrame(); final CkImage image1 = CkImage(skImage1, imageSource: imageSource); - final SkImage skImage2 = canvasKit.MakeAnimatedImageFromEncoded(k4x4PngImage)!.makeImageAtCurrentFrame(); + final SkImage skImage2 = + canvasKit.MakeAnimatedImageFromEncoded(k4x4PngImage)!.makeImageAtCurrentFrame(); final CkImage image2 = CkImage(skImage2, imageSource: imageSource); final CkImage image3 = image1.clone(); diff --git a/lib/web_ui/test/canvaskit/initialization/services_vs_ui_test.dart b/lib/web_ui/test/canvaskit/initialization/services_vs_ui_test.dart index bb91d9afe2bc6..75754916aad1a 100644 --- a/lib/web_ui/test/canvaskit/initialization/services_vs_ui_test.dart +++ b/lib/web_ui/test/canvaskit/initialization/services_vs_ui_test.dart @@ -54,7 +54,7 @@ Future bootstrapAndExtractConfig() { final Completer configCompleter = Completer(); final AppBootstrap bootstrap = AppBootstrap( initializeEngine: ([JsFlutterConfiguration? config]) async => configCompleter.complete(config), - runApp: () async {} + runApp: () async {}, ); flutter!.loader!.didCreateEngineInitializer(bootstrap.prepareEngineInitializer()); diff --git a/lib/web_ui/test/canvaskit/layer_test.dart b/lib/web_ui/test/canvaskit/layer_test.dart index 6b8ecdbe99372..18faaefc91d02 100644 --- a/lib/web_ui/test/canvaskit/layer_test.dart +++ b/lib/web_ui/test/canvaskit/layer_test.dart @@ -21,10 +21,13 @@ void testMain() { // Regression test for https://github.com/flutter/flutter/issues/63715 test('TransformLayer prerolls correctly', () async { - final CkPicture picture = - paintPicture(const ui.Rect.fromLTRB(0, 0, 60, 60), (CkCanvas canvas) { - canvas.drawRect(const ui.Rect.fromLTRB(0, 0, 60, 60), - CkPaint()..style = ui.PaintingStyle.fill); + final CkPicture picture = paintPicture(const ui.Rect.fromLTRB(0, 0, 60, 60), ( + CkCanvas canvas, + ) { + canvas.drawRect( + const ui.Rect.fromLTRB(0, 0, 60, 60), + CkPaint()..style = ui.PaintingStyle.fill, + ); }); final LayerSceneBuilder sb = LayerSceneBuilder(); @@ -32,9 +35,7 @@ void testMain() { // Intentionally use a perspective transform, which triggered the // https://github.com/flutter/flutter/issues/63715 bug. - sb.pushTransform(Float64List.fromList( - Matrix4.identity().storage..[15] = 2, - )); + sb.pushTransform(Float64List.fromList(Matrix4.identity().storage..[15] = 2)); sb.addPicture(ui.Offset.zero, picture); final LayerScene scene = sb.build(); @@ -44,8 +45,7 @@ void testMain() { layerTree.rootLayer.debugLayers.single as ClipRectEngineLayer; expect(clipRect.paintBounds, const ui.Rect.fromLTRB(15, 15, 30, 30)); - final TransformEngineLayer transform = - clipRect.debugLayers.single as TransformEngineLayer; + final TransformEngineLayer transform = clipRect.debugLayers.single as TransformEngineLayer; expect(transform.paintBounds, const ui.Rect.fromLTRB(0, 0, 30, 30)); }); @@ -76,10 +76,13 @@ void testMain() { }); test('ImageFilter layer applies matrix in preroll', () async { - final CkPicture picture = paintPicture( - const ui.Rect.fromLTRB(0, 0, 100, 100), (CkCanvas canvas) { - canvas.drawRect(const ui.Rect.fromLTRB(0, 0, 100, 100), - CkPaint()..style = ui.PaintingStyle.fill); + final CkPicture picture = paintPicture(const ui.Rect.fromLTRB(0, 0, 100, 100), ( + CkCanvas canvas, + ) { + canvas.drawRect( + const ui.Rect.fromLTRB(0, 0, 100, 100), + CkPaint()..style = ui.PaintingStyle.fill, + ); }); final LayerSceneBuilder sb = LayerSceneBuilder(); @@ -99,16 +102,18 @@ void testMain() { final ImageFilterEngineLayer imageFilterLayer = layerTree.rootLayer.debugLayers.single as ImageFilterEngineLayer; - expect( - imageFilterLayer.paintBounds, const ui.Rect.fromLTRB(10, 0, 60, 50)); + expect(imageFilterLayer.paintBounds, const ui.Rect.fromLTRB(10, 0, 60, 50)); }); test('Opacity layer works correctly with Scene.toImage', () async { // This is a regression test for https://github.com/flutter/flutter/issues/138009 - final CkPicture picture = paintPicture( - const ui.Rect.fromLTRB(0, 0, 100, 100), (CkCanvas canvas) { - canvas.drawRect(const ui.Rect.fromLTRB(0, 0, 100, 100), - CkPaint()..style = ui.PaintingStyle.fill); + final CkPicture picture = paintPicture(const ui.Rect.fromLTRB(0, 0, 100, 100), ( + CkCanvas canvas, + ) { + canvas.drawRect( + const ui.Rect.fromLTRB(0, 0, 100, 100), + CkPaint()..style = ui.PaintingStyle.fill, + ); }); final LayerSceneBuilder sb = LayerSceneBuilder(); @@ -120,8 +125,7 @@ void testMain() { final ui.Image testImage = await scene.toImage(200, 200); final CkPictureRecorder recorder = CkPictureRecorder(); - final CkCanvas canvas = - recorder.beginRecording(const ui.Rect.fromLTRB(0, 0, 200, 200)); + final CkCanvas canvas = recorder.beginRecording(const ui.Rect.fromLTRB(0, 0, 200, 200)); canvas.drawImage(testImage as CkImage, ui.Offset.zero, CkPaint()); await matchPictureGolden( 'canvaskit_scene_toimage_opacity_layer.png', diff --git a/lib/web_ui/test/canvaskit/linear_gradient_golden_test.dart b/lib/web_ui/test/canvaskit/linear_gradient_golden_test.dart index af4888b457453..627fbf2cbfcbf 100644 --- a/lib/web_ui/test/canvaskit/linear_gradient_golden_test.dart +++ b/lib/web_ui/test/canvaskit/linear_gradient_golden_test.dart @@ -26,24 +26,19 @@ void testMain() { final CkCanvas canvas = recorder.beginRecording(region); final CkGradientLinear gradient = CkGradientLinear( - ui.Offset(region.left + region.width / 4, region.height / 2), - ui.Offset(region.right - region.width / 8, region.height / 2), - const [ - ui.Color(0xFF4285F4), - ui.Color(0xFF34A853), - ui.Color(0xFFFBBC05), - ui.Color(0xFFEA4335), - ui.Color(0xFF4285F4), - ], - const [ - 0.0, - 0.25, - 0.5, - 0.75, - 1.0, - ], - ui.TileMode.clamp, - null); + ui.Offset(region.left + region.width / 4, region.height / 2), + ui.Offset(region.right - region.width / 8, region.height / 2), + const [ + ui.Color(0xFF4285F4), + ui.Color(0xFF34A853), + ui.Color(0xFFFBBC05), + ui.Color(0xFFEA4335), + ui.Color(0xFF4285F4), + ], + const [0.0, 0.25, 0.5, 0.75, 1.0], + ui.TileMode.clamp, + null, + ); final CkPaint paint = CkPaint()..shader = gradient; @@ -61,24 +56,19 @@ void testMain() { final CkCanvas canvas = recorder.beginRecording(region); final CkGradientLinear gradient = CkGradientLinear( - ui.Offset(region.left + region.width / 4, region.height / 2), - ui.Offset(region.right - region.width / 8, region.height / 2), - const [ - ui.Color(0xFF4285F4), - ui.Color(0xFF34A853), - ui.Color(0xFFFBBC05), - ui.Color(0xFFEA4335), - ui.Color(0xFF4285F4), - ], - const [ - 0.0, - 0.25, - 0.5, - 0.75, - 1.0, - ], - ui.TileMode.clamp, - Matrix4.rotationZ(math.pi / 6.0).storage); + ui.Offset(region.left + region.width / 4, region.height / 2), + ui.Offset(region.right - region.width / 8, region.height / 2), + const [ + ui.Color(0xFF4285F4), + ui.Color(0xFF34A853), + ui.Color(0xFFFBBC05), + ui.Color(0xFFEA4335), + ui.Color(0xFF4285F4), + ], + const [0.0, 0.25, 0.5, 0.75, 1.0], + ui.TileMode.clamp, + Matrix4.rotationZ(math.pi / 6.0).storage, + ); final CkPaint paint = CkPaint()..shader = gradient; diff --git a/lib/web_ui/test/canvaskit/multi_view_test.dart b/lib/web_ui/test/canvaskit/multi_view_test.dart index 84dca650681e9..88f98b7ae5c77 100644 --- a/lib/web_ui/test/canvaskit/multi_view_test.dart +++ b/lib/web_ui/test/canvaskit/multi_view_test.dart @@ -23,10 +23,13 @@ void testMain() { setUp(() { // Create a scene to use in tests. - final CkPicture picture = - paintPicture(const ui.Rect.fromLTRB(0, 0, 60, 60), (CkCanvas canvas) { - canvas.drawRect(const ui.Rect.fromLTRB(0, 0, 60, 60), - CkPaint()..style = ui.PaintingStyle.fill); + final CkPicture picture = paintPicture(const ui.Rect.fromLTRB(0, 0, 60, 60), ( + CkCanvas canvas, + ) { + canvas.drawRect( + const ui.Rect.fromLTRB(0, 0, 60, 60), + CkPaint()..style = ui.PaintingStyle.fill, + ); }); final LayerSceneBuilder sb = LayerSceneBuilder(); sb.addPicture(ui.Offset.zero, picture); @@ -37,7 +40,9 @@ void testMain() { await CanvasKitRenderer.instance.renderScene(scene, implicitView); final EngineFlutterView anotherView = EngineFlutterView( - EnginePlatformDispatcher.instance, createDomElement('another-view')); + EnginePlatformDispatcher.instance, + createDomElement('another-view'), + ); EnginePlatformDispatcher.instance.viewManager.registerView(anotherView); await CanvasKitRenderer.instance.renderScene(scene, anotherView); @@ -45,8 +50,9 @@ void testMain() { test('will error if trying to render into an unregistered view', () async { final EngineFlutterView unregisteredView = EngineFlutterView( - EnginePlatformDispatcher.instance, - createDomElement('unregistered-view')); + EnginePlatformDispatcher.instance, + createDomElement('unregistered-view'), + ); expect( () => CanvasKitRenderer.instance.renderScene(scene, unregisteredView), throwsAssertionError, @@ -55,49 +61,42 @@ void testMain() { test('will dispose the Rasterizer for a disposed view', () async { final EngineFlutterView view = EngineFlutterView( - EnginePlatformDispatcher.instance, createDomElement('multi-view')); - EnginePlatformDispatcher.instance.viewManager.registerView(view); - expect( - CanvasKitRenderer.instance.debugGetRasterizerForView(view), - isNotNull, + EnginePlatformDispatcher.instance, + createDomElement('multi-view'), ); + EnginePlatformDispatcher.instance.viewManager.registerView(view); + expect(CanvasKitRenderer.instance.debugGetRasterizerForView(view), isNotNull); - EnginePlatformDispatcher.instance.viewManager - .disposeAndUnregisterView(view.viewId); - expect( - CanvasKitRenderer.instance.debugGetRasterizerForView(view), - isNull, - ); + EnginePlatformDispatcher.instance.viewManager.disposeAndUnregisterView(view.viewId); + expect(CanvasKitRenderer.instance.debugGetRasterizerForView(view), isNull); }); // Issue https://github.com/flutter/flutter/issues/142094 - test('does not reset platform view factories when disposing a view', - () async { + test('does not reset platform view factories when disposing a view', () async { expect(PlatformViewManager.instance.knowsViewType('self-test'), isFalse); final EngineFlutterView view = EngineFlutterView( - EnginePlatformDispatcher.instance, createDomElement('multi-view')); - EnginePlatformDispatcher.instance.viewManager.registerView(view); - expect( - CanvasKitRenderer.instance.debugGetRasterizerForView(view), - isNotNull, + EnginePlatformDispatcher.instance, + createDomElement('multi-view'), ); + EnginePlatformDispatcher.instance.viewManager.registerView(view); + expect(CanvasKitRenderer.instance.debugGetRasterizerForView(view), isNotNull); - EnginePlatformDispatcher.instance.viewManager - .disposeAndUnregisterView(view.viewId); - expect( - CanvasKitRenderer.instance.debugGetRasterizerForView(view), - isNull, - ); + EnginePlatformDispatcher.instance.viewManager.disposeAndUnregisterView(view.viewId); + expect(CanvasKitRenderer.instance.debugGetRasterizerForView(view), isNull); expect( - PlatformViewManager.instance.knowsViewType( - ui_web.PlatformViewRegistry.defaultVisibleViewType), - isTrue); + PlatformViewManager.instance.knowsViewType( + ui_web.PlatformViewRegistry.defaultVisibleViewType, + ), + isTrue, + ); expect( - PlatformViewManager.instance.knowsViewType( - ui_web.PlatformViewRegistry.defaultInvisibleViewType), - isTrue); + PlatformViewManager.instance.knowsViewType( + ui_web.PlatformViewRegistry.defaultInvisibleViewType, + ), + isTrue, + ); }); }); } diff --git a/lib/web_ui/test/canvaskit/native_memory_test.dart b/lib/web_ui/test/canvaskit/native_memory_test.dart index beecd63db179b..3cec2cd3806ee 100644 --- a/lib/web_ui/test/canvaskit/native_memory_test.dart +++ b/lib/web_ui/test/canvaskit/native_memory_test.dart @@ -22,7 +22,8 @@ void testMain() { setUp(() { TestSkDeletableMock.deleteCount = 0; - nativeMemoryFinalizationRegistry = mockFinalizationRegistry = _MockNativeMemoryFinalizationRegistry(); + nativeMemoryFinalizationRegistry = + mockFinalizationRegistry = _MockNativeMemoryFinalizationRegistry(); }); tearDown(() { @@ -34,7 +35,11 @@ void testMain() { expect(mockFinalizationRegistry.registeredPairs, hasLength(0)); final Object owner = Object(); final TestSkDeletable nativeObject = TestSkDeletable(); - final UniqueRef ref = UniqueRef(owner, nativeObject, 'TestSkDeletable'); + final UniqueRef ref = UniqueRef( + owner, + nativeObject, + 'TestSkDeletable', + ); expect(ref.isDisposed, isFalse); expect(ref.nativeObject, same(nativeObject)); expect(TestSkDeletableMock.deleteCount, 0); @@ -47,11 +52,13 @@ void testMain() { expect(ref.isDisposed, isTrue); expect( reason: 'Cannot access object that was disposed', - () => ref.nativeObject, throwsA(isA()), + () => ref.nativeObject, + throwsA(isA()), ); expect( reason: 'Cannot dispose object more than once', - () => ref.dispose(), throwsA(isA()), + () => ref.dispose(), + throwsA(isA()), ); expect(TestSkDeletableMock.deleteCount, 1); @@ -59,7 +66,8 @@ void testMain() { mockFinalizationRegistry.registeredPairs.single.ref.collect(); expect( reason: 'Manually disposed object should not be deleted again by GC.', - TestSkDeletableMock.deleteCount, 1, + TestSkDeletableMock.deleteCount, + 1, ); }); @@ -67,7 +75,11 @@ void testMain() { expect(mockFinalizationRegistry.registeredPairs, hasLength(0)); final Object owner = Object(); final TestSkDeletable nativeObject = TestSkDeletable(); - final UniqueRef ref = UniqueRef(owner, nativeObject, 'TestSkDeletable'); + final UniqueRef ref = UniqueRef( + owner, + nativeObject, + 'TestSkDeletable', + ); expect(ref.isDisposed, isFalse); expect(ref.nativeObject, same(nativeObject)); expect(TestSkDeletableMock.deleteCount, 0); @@ -89,10 +101,12 @@ void testMain() { final TestSkDeletable nativeObject = TestSkDeletable(); expect(Instrumentation.instance.debugCounters, {}); - final UniqueRef ref = UniqueRef(owner, nativeObject, 'TestSkDeletable'); - expect(Instrumentation.instance.debugCounters, { - 'TestSkDeletable Created': 1, - }); + final UniqueRef ref = UniqueRef( + owner, + nativeObject, + 'TestSkDeletable', + ); + expect(Instrumentation.instance.debugCounters, {'TestSkDeletable Created': 1}); ref.dispose(); expect(Instrumentation.instance.debugCounters, { 'TestSkDeletable Created': 1, @@ -108,10 +122,12 @@ void testMain() { final TestSkDeletable nativeObject = TestSkDeletable(); expect(Instrumentation.instance.debugCounters, {}); - final UniqueRef ref = UniqueRef(owner, nativeObject, 'TestSkDeletable'); - expect(Instrumentation.instance.debugCounters, { - 'TestSkDeletable Created': 1, - }); + final UniqueRef ref = UniqueRef( + owner, + nativeObject, + 'TestSkDeletable', + ); + expect(Instrumentation.instance.debugCounters, {'TestSkDeletable Created': 1}); ref.collect(); expect(Instrumentation.instance.debugCounters, { 'TestSkDeletable Created': 1, @@ -138,13 +154,15 @@ void testMain() { expect(owner.ref.refCount, 0); expect( reason: 'Cannot access object that was disposed', - () => owner.ref.nativeObject, throwsA(isA()), + () => owner.ref.nativeObject, + throwsA(isA()), ); expect(TestSkDeletableMock.deleteCount, 1); expect( reason: 'Cannot dispose object more than once', - () => owner.dispose(), throwsA(isA()), + () => owner.dispose(), + throwsA(isA()), ); }); @@ -168,9 +186,11 @@ void testMain() { expect(owner2.ref.nativeObject, nativeObject); expect(TestSkDeletableMock.deleteCount, 0); expect( - reason: 'Second owner does not add more native object owners. ' - 'The underlying shared UniqueRef is the only one.', - mockFinalizationRegistry.registeredPairs, hasLength(1), + reason: + 'Second owner does not add more native object owners. ' + 'The underlying shared UniqueRef is the only one.', + mockFinalizationRegistry.registeredPairs, + hasLength(1), ); owner1.dispose(); @@ -180,8 +200,10 @@ void testMain() { expect(owner2.ref.nativeObject, nativeObject); expect(TestSkDeletableMock.deleteCount, 0); expect( - reason: 'The same owner cannot dispose its CountedRef more than once, even when CountedRef is still alive.', - () => owner1.dispose(), throwsA(isA()), + reason: + 'The same owner cannot dispose its CountedRef more than once, even when CountedRef is still alive.', + () => owner1.dispose(), + throwsA(isA()), ); owner2.dispose(); @@ -189,20 +211,23 @@ void testMain() { expect(owner2.ref.refCount, 0); expect( reason: 'Cannot access object that was disposed', - () => owner2.ref.nativeObject, throwsA(isA()), + () => owner2.ref.nativeObject, + throwsA(isA()), ); expect(TestSkDeletableMock.deleteCount, 1); expect( reason: 'The same owner cannot dispose its CountedRef more than once.', - () => owner2.dispose(), throwsA(isA()), + () => owner2.dispose(), + throwsA(isA()), ); // Simulate a GC mockFinalizationRegistry.registeredPairs.single.ref.collect(); expect( reason: 'Manually disposed object should not be deleted again by GC.', - TestSkDeletableMock.deleteCount, 1, + TestSkDeletableMock.deleteCount, + 1, ); }); }); @@ -215,9 +240,11 @@ class TestSkDeletableMock { bool _isDeleted = false; void delete() { - expect(_isDeleted, isFalse, - reason: - 'CanvasKit does not allow deleting the same object more than once.'); + expect( + _isDeleted, + isFalse, + reason: 'CanvasKit does not allow deleting the same object more than once.', + ); _isDeleted = true; deleteCount++; } @@ -232,15 +259,23 @@ class TestSkDeletable implements SkDeletable { factory TestSkDeletable() { final TestSkDeletableMock mock = TestSkDeletableMock(); return TestSkDeletable._( - isDeleted: () { return mock.isDeleted(); }.toJS, - delete: () { return mock.delete(); }.toJS, - constructor: mock.constructor); + isDeleted: + () { + return mock.isDeleted(); + }.toJS, + delete: + () { + return mock.delete(); + }.toJS, + constructor: mock.constructor, + ); } external factory TestSkDeletable._({ JSFunction isDeleted, JSFunction delete, - JsConstructor constructor}); + JsConstructor constructor, + }); } @JS() @@ -257,7 +292,10 @@ class TestCountedRefOwner implements StackTraceDebugger { return true; }()); ref = CountedRef( - nativeObject, this, 'TestCountedRefOwner'); + nativeObject, + this, + 'TestCountedRefOwner', + ); } TestCountedRefOwner.cloneOf(this.ref) { diff --git a/lib/web_ui/test/canvaskit/no_create_image_bitmap_test.dart b/lib/web_ui/test/canvaskit/no_create_image_bitmap_test.dart index a3855e4b0030f..e9a8a4484515f 100644 --- a/lib/web_ui/test/canvaskit/no_create_image_bitmap_test.dart +++ b/lib/web_ui/test/canvaskit/no_create_image_bitmap_test.dart @@ -36,24 +36,19 @@ void testMain() { final CkCanvas canvas = recorder.beginRecording(region); final CkGradientLinear gradient = CkGradientLinear( - ui.Offset(region.left + region.width / 4, region.height / 2), - ui.Offset(region.right - region.width / 8, region.height / 2), - const [ - ui.Color(0xFF4285F4), - ui.Color(0xFF34A853), - ui.Color(0xFFFBBC05), - ui.Color(0xFFEA4335), - ui.Color(0xFF4285F4), - ], - const [ - 0.0, - 0.25, - 0.5, - 0.75, - 1.0, - ], - ui.TileMode.clamp, - null); + ui.Offset(region.left + region.width / 4, region.height / 2), + ui.Offset(region.right - region.width / 8, region.height / 2), + const [ + ui.Color(0xFF4285F4), + ui.Color(0xFF34A853), + ui.Color(0xFFFBBC05), + ui.Color(0xFFEA4335), + ui.Color(0xFF4285F4), + ], + const [0.0, 0.25, 0.5, 0.75, 1.0], + ui.TileMode.clamp, + null, + ); final CkPaint paint = CkPaint()..shader = gradient; @@ -66,8 +61,7 @@ void testMain() { ); }); - test( - 'createImageBitmap support is disabled on ' + test('createImageBitmap support is disabled on ' 'Windows on Chrome version 110 or older', () async { debugIsChrome110OrOlder = true; debugDisableCreateImageBitmapSupport = false; diff --git a/lib/web_ui/test/canvaskit/picture_test.dart b/lib/web_ui/test/canvaskit/picture_test.dart index 035ea3ba4c197..9fef48c10662a 100644 --- a/lib/web_ui/test/canvaskit/picture_test.dart +++ b/lib/web_ui/test/canvaskit/picture_test.dart @@ -45,10 +45,13 @@ void testMain() { // TODO(yjbanov): cannot test precise message due to https://github.com/flutter/flutter/issues/96298 expect( - '$actualError', - startsWith('Bad state: Test.\n' - 'The picture has been disposed. ' - 'When the picture was disposed the stack trace was:\n')); + '$actualError', + startsWith( + 'Bad state: Test.\n' + 'The picture has been disposed. ' + 'When the picture was disposed the stack trace was:\n', + ), + ); }); }); @@ -80,15 +83,8 @@ void testMain() { ui.RRect.fromRectXY(const ui.Rect.fromLTRB(20, 20, 150, 300), 15, 15), ui.Paint()..color = red, ); - canvas.drawCircle( - const ui.Offset(200, 200), - 100, - ui.Paint()..color = green, - ); - canvas.drawOval( - const ui.Rect.fromLTRB(210, 40, 268, 199), - ui.Paint()..color = blue, - ); + canvas.drawCircle(const ui.Offset(200, 200), 100, ui.Paint()..color = green); + canvas.drawOval(const ui.Rect.fromLTRB(210, 40, 268, 199), ui.Paint()..color = blue); final CkPicture picture = recorder.endRecording() as CkPicture; final ui.Rect bounds = picture.cullRect; @@ -123,15 +119,8 @@ void testMain() { ui.RRect.fromRectXY(const ui.Rect.fromLTRB(20, 20, 150, 300), 15, 15), ui.Paint()..color = red, ); - canvas.drawCircle( - const ui.Offset(200, 200), - 100, - ui.Paint()..color = green, - ); - canvas.drawOval( - const ui.Rect.fromLTRB(210, 40, 268, 199), - ui.Paint()..color = blue, - ); + canvas.drawCircle(const ui.Offset(200, 200), 100, ui.Paint()..color = green); + canvas.drawOval(const ui.Rect.fromLTRB(210, 40, 268, 199), ui.Paint()..color = blue); final CkPicture picture = recorder.endRecording() as CkPicture; final int bytesUsed = picture.approximateBytesUsed; diff --git a/lib/web_ui/test/canvaskit/platform_dispatcher_test.dart b/lib/web_ui/test/canvaskit/platform_dispatcher_test.dart index 85421d24f70ab..1310479956521 100644 --- a/lib/web_ui/test/canvaskit/platform_dispatcher_test.dart +++ b/lib/web_ui/test/canvaskit/platform_dispatcher_test.dart @@ -26,19 +26,15 @@ void testMain() { final Completer completer = Completer(); ui.PlatformDispatcher.instance.sendPlatformMessage( 'flutter/skia', - codec.encodeMethodCall(const MethodCall( - 'Skia.setResourceCacheMaxBytes', - 512 * 1000 * 1000, - )), + codec.encodeMethodCall( + const MethodCall('Skia.setResourceCacheMaxBytes', 512 * 1000 * 1000), + ), completer.complete, ); final ByteData? response = await completer.future; expect(response, isNotNull); - expect( - codec.decodeEnvelope(response!), - [true], - ); + expect(codec.decodeEnvelope(response!), [true]); }); }); } diff --git a/lib/web_ui/test/canvaskit/render_canvas_test.dart b/lib/web_ui/test/canvaskit/render_canvas_test.dart index d85d45bd14ea4..b728858f37f9a 100644 --- a/lib/web_ui/test/canvaskit/render_canvas_test.dart +++ b/lib/web_ui/test/canvaskit/render_canvas_test.dart @@ -22,8 +22,7 @@ void testMain() { }); Future newBitmap(int width, int height) async { - return createImageBitmap( - createBlankDomImageData(width, height) as JSAny, ( + return createImageBitmap(createBlankDomImageData(width, height) as JSAny, ( x: 0, y: 0, width: width, @@ -32,8 +31,7 @@ void testMain() { } // Regression test for https://github.com/flutter/flutter/issues/75286 - test('updates canvas logical size when device-pixel ratio changes', - () async { + test('updates canvas logical size when device-pixel ratio changes', () async { final RenderCanvas canvas = RenderCanvas(); canvas.render(await newBitmap(10, 16)); @@ -62,10 +60,8 @@ void testMain() { }); test('rounds physical size to nearest integer size', () async { - final EngineFlutterWindow implicitView = - EnginePlatformDispatcher.instance.implicitView!; - implicitView.debugPhysicalSizeOverride = - const ui.Size(199.999999, 200.000001); + final EngineFlutterWindow implicitView = EnginePlatformDispatcher.instance.implicitView!; + implicitView.debugPhysicalSizeOverride = const ui.Size(199.999999, 200.000001); final ui.SceneBuilder sceneBuilder = LayerSceneBuilder(); final CkPictureRecorder recorder = CkPictureRecorder(); @@ -78,9 +74,7 @@ void testMain() { await renderScene(scene); expect( - CanvasKitRenderer.instance - .debugGetRasterizerForView(implicitView)! - .currentFrameSize, + CanvasKitRenderer.instance.debugGetRasterizerForView(implicitView)!.currentFrameSize, const BitmapSize(200, 200), ); diff --git a/lib/web_ui/test/canvaskit/renderer_test.dart b/lib/web_ui/test/canvaskit/renderer_test.dart index 6dc626462461c..1dbb0763d7964 100644 --- a/lib/web_ui/test/canvaskit/renderer_test.dart +++ b/lib/web_ui/test/canvaskit/renderer_test.dart @@ -44,8 +44,7 @@ class TestViewRasterizer extends ViewRasterizer { List treesRendered = []; @override - DisplayCanvasFactory get displayFactory => - throw UnimplementedError(); + DisplayCanvasFactory get displayFactory => throw UnimplementedError(); @override void prepareToDraw() { @@ -59,8 +58,7 @@ class TestViewRasterizer extends ViewRasterizer { } @override - Future rasterizeToCanvas( - DisplayCanvas canvas, List pictures) { + Future rasterizeToCanvas(DisplayCanvas canvas, List pictures) { // No-op return Future.value(); } @@ -70,15 +68,16 @@ void testMain() { group('Renderer', () { setUpCanvasKitTest(); - test('always renders most recent picture and skips intermediate pictures', - () async { + test('always renders most recent picture and skips intermediate pictures', () async { final TestRasterizer testRasterizer = TestRasterizer(); CanvasKitRenderer.instance.debugOverrideRasterizer(testRasterizer); // Create another view to render into to force the renderer to make // a [ViewRasterizer] for it. final EngineFlutterView testView = EngineFlutterView( - EnginePlatformDispatcher.instance, createDomElement('test-view')); + EnginePlatformDispatcher.instance, + createDomElement('test-view'), + ); EnginePlatformDispatcher.instance.viewManager.registerView(testView); final List treesToRender = []; @@ -86,21 +85,21 @@ void testMain() { for (int i = 1; i < 20; i++) { final ui.PictureRecorder recorder = ui.PictureRecorder(); final ui.Canvas canvas = ui.Canvas(recorder); - canvas.drawRect(const ui.Rect.fromLTWH(0, 0, 50, 50), - ui.Paint()..color = const ui.Color(0xff00ff00)); + canvas.drawRect( + const ui.Rect.fromLTWH(0, 0, 50, 50), + ui.Paint()..color = const ui.Color(0xff00ff00), + ); final ui.Picture picture = recorder.endRecording(); final ui.SceneBuilder builder = ui.SceneBuilder(); builder.addPicture(ui.Offset.zero, picture); final ui.Scene scene = builder.build(); treesToRender.add((scene as LayerScene).layerTree); - renderFutures - .add(CanvasKitRenderer.instance.renderScene(scene, testView)); + renderFutures.add(CanvasKitRenderer.instance.renderScene(scene, testView)); } await Future.wait(renderFutures); // Should just render the first and last pictures and skip the one inbetween. - final List treesRendered = - testRasterizer.treesRenderedInView(testView); + final List treesRendered = testRasterizer.treesRenderedInView(testView); expect(treesRendered.length, 2); expect(treesRendered.first, treesToRender.first); expect(treesRendered.last, treesToRender.last); @@ -113,13 +112,19 @@ void testMain() { // Create another view to render into to force the renderer to make // a [ViewRasterizer] for it. final EngineFlutterView testView1 = EngineFlutterView( - EnginePlatformDispatcher.instance, createDomElement('test-view')); + EnginePlatformDispatcher.instance, + createDomElement('test-view'), + ); EnginePlatformDispatcher.instance.viewManager.registerView(testView1); final EngineFlutterView testView2 = EngineFlutterView( - EnginePlatformDispatcher.instance, createDomElement('test-view')); + EnginePlatformDispatcher.instance, + createDomElement('test-view'), + ); EnginePlatformDispatcher.instance.viewManager.registerView(testView2); final EngineFlutterView testView3 = EngineFlutterView( - EnginePlatformDispatcher.instance, createDomElement('test-view')); + EnginePlatformDispatcher.instance, + createDomElement('test-view'), + ); EnginePlatformDispatcher.instance.viewManager.registerView(testView3); final Map> treesToRender = @@ -137,36 +142,34 @@ void testMain() { ]) { final ui.PictureRecorder recorder = ui.PictureRecorder(); final ui.Canvas canvas = ui.Canvas(recorder); - canvas.drawRect(const ui.Rect.fromLTWH(0, 0, 50, 50), - ui.Paint()..color = const ui.Color(0xff00ff00)); + canvas.drawRect( + const ui.Rect.fromLTWH(0, 0, 50, 50), + ui.Paint()..color = const ui.Color(0xff00ff00), + ); final ui.Picture picture = recorder.endRecording(); final ui.SceneBuilder builder = ui.SceneBuilder(); builder.addPicture(ui.Offset.zero, picture); final ui.Scene scene = builder.build(); treesToRender[testView]!.add((scene as LayerScene).layerTree); - renderFutures - .add(CanvasKitRenderer.instance.renderScene(scene, testView)); + renderFutures.add(CanvasKitRenderer.instance.renderScene(scene, testView)); } } await Future.wait(renderFutures); // Should just render the first and last pictures and skip the one inbetween. - final List treesRenderedInView1 = - testRasterizer.treesRenderedInView(testView1); + final List treesRenderedInView1 = testRasterizer.treesRenderedInView(testView1); final List treesToRenderInView1 = treesToRender[testView1]!; expect(treesRenderedInView1.length, 2); expect(treesRenderedInView1.first, treesToRenderInView1.first); expect(treesRenderedInView1.last, treesToRenderInView1.last); - final List treesRenderedInView2 = - testRasterizer.treesRenderedInView(testView2); + final List treesRenderedInView2 = testRasterizer.treesRenderedInView(testView2); final List treesToRenderInView2 = treesToRender[testView2]!; expect(treesRenderedInView2.length, 2); expect(treesRenderedInView2.first, treesToRenderInView2.first); expect(treesRenderedInView2.last, treesToRenderInView2.last); - final List treesRenderedInView3 = - testRasterizer.treesRenderedInView(testView3); + final List treesRenderedInView3 = testRasterizer.treesRenderedInView(testView3); final List treesToRenderInView3 = treesToRender[testView3]!; expect(treesRenderedInView3.length, 2); expect(treesRenderedInView3.first, treesToRenderInView3.first); diff --git a/lib/web_ui/test/canvaskit/shader_mask_golden_test.dart b/lib/web_ui/test/canvaskit/shader_mask_golden_test.dart index f1af16b2ec797..af1ec63ca52af 100644 --- a/lib/web_ui/test/canvaskit/shader_mask_golden_test.dart +++ b/lib/web_ui/test/canvaskit/shader_mask_golden_test.dart @@ -39,40 +39,34 @@ void testMain() { builder.addPicture(ui.Offset.zero, redCircle); final CkGradientSweep shader = CkGradientSweep( - const ui.Offset(250, 125), - const [ - ui.Color(0xFF4285F4), - ui.Color(0xFF34A853), - ui.Color(0xFFFBBC05), - ui.Color(0xFFEA4335), - ui.Color(0xFF4285F4), - ], - const [ - 0.0, - 0.25, - 0.5, - 0.75, - 1.0, - ], - ui.TileMode.clamp, - -(math.pi / 2), - math.pi * 2 - (math.pi / 2), - null); - - final ui.Path clipPath = ui.Path() - ..addOval(const ui.Rect.fromLTWH(25, 75, 100, 100)); + const ui.Offset(250, 125), + const [ + ui.Color(0xFF4285F4), + ui.Color(0xFF34A853), + ui.Color(0xFFFBBC05), + ui.Color(0xFFEA4335), + ui.Color(0xFF4285F4), + ], + const [0.0, 0.25, 0.5, 0.75, 1.0], + ui.TileMode.clamp, + -(math.pi / 2), + math.pi * 2 - (math.pi / 2), + null, + ); + + final ui.Path clipPath = ui.Path()..addOval(const ui.Rect.fromLTWH(25, 75, 100, 100)); builder.pushClipPath(clipPath); // Apply a shader mask. - builder.pushShaderMask(shader, const ui.Rect.fromLTRB(0, 0, 200, 250), - ui.BlendMode.color); + builder.pushShaderMask(shader, const ui.Rect.fromLTRB(0, 0, 200, 250), ui.BlendMode.color); // Draw another red circle and apply it to the scene. // This one should be grey since we have the color filter. final CkPictureRecorder recorder2 = CkPictureRecorder(); final CkCanvas canvas2 = recorder2.beginRecording(region); - canvas2.drawRect(const ui.Rect.fromLTWH(25, 75, 100, 100), + canvas2.drawRect( + const ui.Rect.fromLTWH(25, 75, 100, 100), CkPaint()..color = const ui.Color.fromARGB(255, 0, 255, 0), ); @@ -80,11 +74,7 @@ void testMain() { builder.addPicture(ui.Offset.zero, sweepCircle); - await matchSceneGolden( - 'canvaskit_shadermask_linear.png', - builder.build(), - region: region, - ); + await matchSceneGolden('canvaskit_shadermask_linear.png', builder.build(), region: region); }); /// Regression test for https://github.com/flutter/flutter/issues/78959 @@ -107,40 +97,34 @@ void testMain() { builder.addPicture(ui.Offset.zero, redCircle); final CkGradientSweep shader = CkGradientSweep( - const ui.Offset(250, 125), - const [ - ui.Color(0xFF4285F4), - ui.Color(0xFF34A853), - ui.Color(0xFFFBBC05), - ui.Color(0xFFEA4335), - ui.Color(0xFF4285F4), - ], - const [ - 0.0, - 0.25, - 0.5, - 0.75, - 1.0, - ], - ui.TileMode.clamp, - -(math.pi / 2), - math.pi * 2 - (math.pi / 2), - null); - - final ui.Path clipPath = ui.Path() - ..addOval(const ui.Rect.fromLTWH(25, 75, 100, 100)); + const ui.Offset(250, 125), + const [ + ui.Color(0xFF4285F4), + ui.Color(0xFF34A853), + ui.Color(0xFFFBBC05), + ui.Color(0xFFEA4335), + ui.Color(0xFF4285F4), + ], + const [0.0, 0.25, 0.5, 0.75, 1.0], + ui.TileMode.clamp, + -(math.pi / 2), + math.pi * 2 - (math.pi / 2), + null, + ); + + final ui.Path clipPath = ui.Path()..addOval(const ui.Rect.fromLTWH(25, 75, 100, 100)); builder.pushClipPath(clipPath); // Apply a shader mask. - builder.pushShaderMask(shader, const ui.Rect.fromLTRB(50, 50, 200, 250), - ui.BlendMode.color); + builder.pushShaderMask(shader, const ui.Rect.fromLTRB(50, 50, 200, 250), ui.BlendMode.color); // Draw another red circle and apply it to the scene. // This one should be grey since we have the color filter. final CkPictureRecorder recorder2 = CkPictureRecorder(); final CkCanvas canvas2 = recorder2.beginRecording(region); - canvas2.drawRect(const ui.Rect.fromLTWH(25, 75, 100, 100), + canvas2.drawRect( + const ui.Rect.fromLTWH(25, 75, 100, 100), CkPaint()..color = const ui.Color.fromARGB(255, 0, 255, 0), ); diff --git a/lib/web_ui/test/canvaskit/shader_test.dart b/lib/web_ui/test/canvaskit/shader_test.dart index 6be40459c7295..fb06b1a3fefdb 100644 --- a/lib/web_ui/test/canvaskit/shader_test.dart +++ b/lib/web_ui/test/canvaskit/shader_test.dart @@ -21,54 +21,51 @@ void testMain() { setUpCanvasKitTest(); test('Sweep gradient', () { - final CkGradientSweep gradient = ui.Gradient.sweep( - ui.Offset.zero, - testColors, - ) as CkGradientSweep; + final CkGradientSweep gradient = + ui.Gradient.sweep(ui.Offset.zero, testColors) as CkGradientSweep; expect(gradient.getSkShader(ui.FilterQuality.none), isNotNull); }); test('Linear gradient', () { - final CkGradientLinear gradient = ui.Gradient.linear( - ui.Offset.zero, - const ui.Offset(0, 1), - testColors, - ) as CkGradientLinear; + final CkGradientLinear gradient = + ui.Gradient.linear(ui.Offset.zero, const ui.Offset(0, 1), testColors) as CkGradientLinear; expect(gradient.getSkShader(ui.FilterQuality.none), isNotNull); }); test('Radial gradient', () { - final CkGradientRadial gradient = ui.Gradient.radial( - ui.Offset.zero, - 10, - testColors, - ) as CkGradientRadial; + final CkGradientRadial gradient = + ui.Gradient.radial(ui.Offset.zero, 10, testColors) as CkGradientRadial; expect(gradient.getSkShader(ui.FilterQuality.none), isNotNull); }); test('Conical gradient', () { - final CkGradientConical gradient = ui.Gradient.radial( - ui.Offset.zero, - 10, - testColors, - null, - ui.TileMode.clamp, - null, - const ui.Offset(10, 10), - 40, - ) as CkGradientConical; + final CkGradientConical gradient = + ui.Gradient.radial( + ui.Offset.zero, + 10, + testColors, + null, + ui.TileMode.clamp, + null, + const ui.Offset(10, 10), + 40, + ) + as CkGradientConical; expect(gradient.getSkShader(ui.FilterQuality.none), isNotNull); }); test('Image shader initialize/dispose cycle', () { - final SkImage skImage = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!.makeImageAtCurrentFrame(); + final SkImage skImage = + canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!.makeImageAtCurrentFrame(); final CkImage image = CkImage(skImage); - final CkImageShader imageShader = ui.ImageShader( - image, - ui.TileMode.clamp, - ui.TileMode.repeated, - Float64List.fromList(Matrix4.diagonal3Values(1, 2, 3).storage), - ) as CkImageShader; + final CkImageShader imageShader = + ui.ImageShader( + image, + ui.TileMode.clamp, + ui.TileMode.repeated, + Float64List.fromList(Matrix4.diagonal3Values(1, 2, 3).storage), + ) + as CkImageShader; expect(imageShader, isA()); final UniqueRef ref = imageShader.ref!; @@ -84,14 +81,17 @@ void testMain() { }); test('Image shader withQuality', () { - final SkImage skImage = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!.makeImageAtCurrentFrame(); + final SkImage skImage = + canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage)!.makeImageAtCurrentFrame(); final CkImage image = CkImage(skImage); - final CkImageShader imageShader = ui.ImageShader( - image, - ui.TileMode.clamp, - ui.TileMode.repeated, - Float64List.fromList(Matrix4.diagonal3Values(1, 2, 3).storage), - ) as CkImageShader; + final CkImageShader imageShader = + ui.ImageShader( + image, + ui.TileMode.clamp, + ui.TileMode.repeated, + Float64List.fromList(Matrix4.diagonal3Values(1, 2, 3).storage), + ) + as CkImageShader; expect(imageShader, isA()); final UniqueRef ref1 = imageShader.ref!; @@ -108,7 +108,11 @@ void testMain() { expect(imageShader.getSkShader(ui.FilterQuality.medium), isNotNull); final UniqueRef ref3 = imageShader.ref!; expect(ref1, isNot(same(ref3))); - expect(ref1.isDisposed, true, reason: 'The previous reference must be released to avoid a memory leak'); + expect( + ref1.isDisposed, + true, + reason: 'The previous reference must be released to avoid a memory leak', + ); expect(image.debugDisposed, false); expect(imageShader.ref!.nativeObject, same(ref3.nativeObject)); diff --git a/lib/web_ui/test/canvaskit/skia_font_collection_test.dart b/lib/web_ui/test/canvaskit/skia_font_collection_test.dart index 6b364009e70b7..c1e1929cc40f7 100644 --- a/lib/web_ui/test/canvaskit/skia_font_collection_test.dart +++ b/lib/web_ui/test/canvaskit/skia_font_collection_test.dart @@ -55,7 +55,8 @@ void testMain() { test('logs a warning if one of the registered fonts is invalid', () async { mockHttpFetchResponseFactory = (String url) async { - final ByteBuffer bogusData = Uint8List.fromList('this is not valid font data'.codeUnits).buffer; + final ByteBuffer bogusData = + Uint8List.fromList('this is not valid font data'.codeUnits).buffer; return MockHttpFetchResponse( status: 200, url: url, @@ -64,7 +65,9 @@ void testMain() { ); }; final SkiaFontCollection fontCollection = SkiaFontCollection(); - testAssetScope.setAsset('FontManifest.json', stringAsUtf8Data(''' + testAssetScope.setAsset( + 'FontManifest.json', + stringAsUtf8Data(''' [ { "family":"Roboto", @@ -75,23 +78,26 @@ void testMain() { "fonts":[{"asset":"packages/bogus/BrokenFont.ttf"}] } ] - ''')); + '''), + ); // It should complete without error, but emit a warning about BrokenFont. await fontCollection.loadAssetFonts(await fetchFontManifest(fakeAssetManager)); expect( warnings, - containsAllInOrder( - [ - 'Failed to load font BrokenFont at packages/bogus/BrokenFont.ttf', - 'Verify that packages/bogus/BrokenFont.ttf contains a valid font.', - ], - ), + containsAllInOrder([ + 'Failed to load font BrokenFont at packages/bogus/BrokenFont.ttf', + 'Verify that packages/bogus/BrokenFont.ttf contains a valid font.', + ]), ); }); - test('logs an HTTP warning if one of the registered fonts is missing (404 file not found)', () async { - final SkiaFontCollection fontCollection = SkiaFontCollection(); - testAssetScope.setAsset('FontManifest.json', stringAsUtf8Data(''' + test( + 'logs an HTTP warning if one of the registered fonts is missing (404 file not found)', + () async { + final SkiaFontCollection fontCollection = SkiaFontCollection(); + testAssetScope.setAsset( + 'FontManifest.json', + stringAsUtf8Data(''' [ { "family":"Roboto", @@ -102,21 +108,26 @@ void testMain() { "fonts":[{"asset":"packages/bogus/ThisFontDoesNotExist.ttf"}] } ] - ''')); + '''), + ); - // It should complete without error, but emit a warning about ThisFontDoesNotExist. - await fontCollection.loadAssetFonts(await fetchFontManifest(fakeAssetManager)); - expect( - warnings, - containsAllInOrder([ - 'Font family ThisFontDoesNotExist not found (404) at packages/bogus/ThisFontDoesNotExist.ttf' - ]), - ); - }); + // It should complete without error, but emit a warning about ThisFontDoesNotExist. + await fontCollection.loadAssetFonts(await fetchFontManifest(fakeAssetManager)); + expect( + warnings, + containsAllInOrder([ + 'Font family ThisFontDoesNotExist not found (404) at packages/bogus/ThisFontDoesNotExist.ttf', + ]), + ); + }, + ); test('prioritizes Ahem loaded via FontManifest.json', () async { final SkiaFontCollection fontCollection = SkiaFontCollection(); - testAssetScope.setAsset('FontManifest.json', stringAsUtf8Data(''' + testAssetScope.setAsset( + 'FontManifest.json', + stringAsUtf8Data( + ''' [ { "family":"Roboto", @@ -127,7 +138,9 @@ void testMain() { "fonts":[{"asset":"/assets/fonts/Roboto-Regular.ttf"}] } ] - '''.trim())); + '''.trim(), + ), + ); final ByteBuffer robotoData = await httpFetchByteBuffer('/assets/fonts/Roboto-Regular.ttf'); @@ -135,8 +148,9 @@ void testMain() { expect(warnings, isEmpty); // Use `singleWhere` to make sure only one version of 'Ahem' is loaded. - final RegisteredFont ahem = fontCollection.debugRegisteredFonts! - .singleWhere((RegisteredFont font) => font.family == 'Ahem'); + final RegisteredFont ahem = fontCollection.debugRegisteredFonts!.singleWhere( + (RegisteredFont font) => font.family == 'Ahem', + ); // Check that the contents of 'Ahem' is actually Roboto, because that's // what's specified in the manifest, and the manifest takes precedence. @@ -149,8 +163,9 @@ void testMain() { final ByteBuffer ahemData = await httpFetchByteBuffer('/assets/fonts/ahem.ttf'); // Use `singleWhere` to make sure only one version of 'Ahem' is loaded. - final RegisteredFont ahem = fontCollection.debugRegisteredFonts! - .singleWhere((RegisteredFont font) => font.family == 'Ahem'); + final RegisteredFont ahem = fontCollection.debugRegisteredFonts!.singleWhere( + (RegisteredFont font) => font.family == 'Ahem', + ); // Check that the contents of 'Ahem' is actually Roboto, because that's // what's specified in the manifest, and the manifest takes precedence. diff --git a/lib/web_ui/test/canvaskit/surface_test.dart b/lib/web_ui/test/canvaskit/surface_test.dart index a1242c7a4e6d3..8861d8a1fe214 100644 --- a/lib/web_ui/test/canvaskit/surface_test.dart +++ b/lib/web_ui/test/canvaskit/surface_test.dart @@ -25,8 +25,7 @@ void testMain() { test('Surface allocates canvases efficiently', () { final Surface surface = Surface(); - final CkSurface originalSurface = - surface.acquireFrame(const ui.Size(9, 19)).skiaSurface; + final CkSurface originalSurface = surface.acquireFrame(const ui.Size(9, 19)).skiaSurface; final DomOffscreenCanvas original = surface.debugOffscreenCanvas!; // Expect exact requested dimensions. @@ -37,8 +36,7 @@ void testMain() { // Shrinking reuses the existing canvas but translates it so // Skia renders into the visible area. - final CkSurface shrunkSurface = - surface.acquireFrame(const ui.Size(5, 15)).skiaSurface; + final CkSurface shrunkSurface = surface.acquireFrame(const ui.Size(5, 15)).skiaSurface; final DomOffscreenCanvas shrunk = surface.debugOffscreenCanvas!; expect(shrunk, same(original)); expect(shrunkSurface, isNot(same(originalSurface))); @@ -69,8 +67,7 @@ void testMain() { expect(secondIncreaseSurface.height(), 22); // Increases beyond the 40% limit will cause a new allocation. - final CkSurface hugeSurface = - surface.acquireFrame(const ui.Size(20, 40)).skiaSurface; + final CkSurface hugeSurface = surface.acquireFrame(const ui.Size(20, 40)).skiaSurface; final DomOffscreenCanvas huge = surface.debugOffscreenCanvas!; expect(huge, same(secondIncrease)); expect(hugeSurface, isNot(same(secondIncreaseSurface))); @@ -82,8 +79,7 @@ void testMain() { expect(hugeSurface.height(), 40); // Shrink again. Reuse the last allocated surface. - final CkSurface shrunkSurface2 = - surface.acquireFrame(const ui.Size(5, 15)).skiaSurface; + final CkSurface shrunkSurface2 = surface.acquireFrame(const ui.Size(5, 15)).skiaSurface; final DomOffscreenCanvas shrunk2 = surface.debugOffscreenCanvas!; expect(shrunk2, same(huge)); expect(shrunkSurface2, isNot(same(hugeSurface))); @@ -93,8 +89,7 @@ void testMain() { // Doubling the DPR should halve the CSS width, height, and translation of the canvas. // This tests https://github.com/flutter/flutter/issues/77084 EngineFlutterDisplay.instance.debugOverrideDevicePixelRatio(2.0); - final CkSurface dpr2Surface2 = - surface.acquireFrame(const ui.Size(5, 15)).skiaSurface; + final CkSurface dpr2Surface2 = surface.acquireFrame(const ui.Size(5, 15)).skiaSurface; final DomOffscreenCanvas dpr2Canvas = surface.debugOffscreenCanvas!; expect(dpr2Canvas, same(huge)); expect(dpr2Surface2, isNot(same(hugeSurface))); @@ -202,46 +197,38 @@ void testMain() { () async { final Surface surface = Surface(); expect(surface.debugForceNewContext, isTrue); - final CkSurface before = - surface.acquireFrame(const ui.Size(9, 19)).skiaSurface; + final CkSurface before = surface.acquireFrame(const ui.Size(9, 19)).skiaSurface; expect(surface.debugForceNewContext, isFalse); // Pump a timer to flush any microtasks. await Future.delayed(Duration.zero); - final CkSurface afterAcquireFrame = - surface.acquireFrame(const ui.Size(9, 19)).skiaSurface; + final CkSurface afterAcquireFrame = surface.acquireFrame(const ui.Size(9, 19)).skiaSurface; // Existing context is reused. expect(afterAcquireFrame, same(before)); // Emulate WebGL context loss. final DomOffscreenCanvas canvas = surface.debugOffscreenCanvas!; final Object ctx = canvas.getContext('webgl2')!; - final Object loseContextExtension = js_util.callMethod( - ctx, - 'getExtension', - ['WEBGL_lose_context'], - ); - js_util.callMethod( - loseContextExtension, 'loseContext', const []); + final Object loseContextExtension = js_util.callMethod(ctx, 'getExtension', [ + 'WEBGL_lose_context', + ]); + js_util.callMethod(loseContextExtension, 'loseContext', const []); // Pump a timer to allow the "lose context" event to propagate. await Future.delayed(Duration.zero); // We don't create a new GL context until the context is restored. expect(surface.debugContextLost, isTrue); - final bool isContextLost = - js_util.callMethod(ctx, 'isContextLost', const []); + final bool isContextLost = js_util.callMethod(ctx, 'isContextLost', const []); expect(isContextLost, isTrue); // Emulate WebGL context restoration. - js_util.callMethod( - loseContextExtension, 'restoreContext', const []); + js_util.callMethod(loseContextExtension, 'restoreContext', const []); // Pump a timer to allow the "restore context" event to propagate. await Future.delayed(Duration.zero); expect(surface.debugForceNewContext, isTrue); - final CkSurface afterContextLost = - surface.acquireFrame(const ui.Size(9, 19)).skiaSurface; + final CkSurface afterContextLost = surface.acquireFrame(const ui.Size(9, 19)).skiaSurface; // A new context is created. expect(afterContextLost, isNot(same(before))); }, @@ -254,8 +241,7 @@ void testMain() { 'updates canvas logical size when device-pixel ratio changes', () { final Surface surface = Surface(); - final CkSurface original = - surface.acquireFrame(const ui.Size(10, 16)).skiaSurface; + final CkSurface original = surface.acquireFrame(const ui.Size(10, 16)).skiaSurface; expect(original.width(), 10); expect(original.height(), 16); @@ -265,8 +251,7 @@ void testMain() { // Increase device-pixel ratio: this makes CSS pixels bigger, so we need // fewer of them to cover the browser window. EngineFlutterDisplay.instance.debugOverrideDevicePixelRatio(2.0); - final CkSurface highDpr = - surface.acquireFrame(const ui.Size(10, 16)).skiaSurface; + final CkSurface highDpr = surface.acquireFrame(const ui.Size(10, 16)).skiaSurface; expect(highDpr.width(), 10); expect(highDpr.height(), 16); expect(surface.debugOffscreenCanvas!.width, 10); @@ -275,8 +260,7 @@ void testMain() { // Decrease device-pixel ratio: this makes CSS pixels smaller, so we need // more of them to cover the browser window. EngineFlutterDisplay.instance.debugOverrideDevicePixelRatio(0.5); - final CkSurface lowDpr = - surface.acquireFrame(const ui.Size(10, 16)).skiaSurface; + final CkSurface lowDpr = surface.acquireFrame(const ui.Size(10, 16)).skiaSurface; expect(lowDpr.width(), 10); expect(lowDpr.height(), 16); expect(surface.debugOffscreenCanvas!.width, 10); @@ -295,9 +279,9 @@ void testMain() { ); test('can recover from MakeSWCanvasSurface failure', () async { - debugOverrideJsConfiguration({ - 'canvasKitForceCpuOnly': true, - }.jsify() as JsFlutterConfiguration?); + debugOverrideJsConfiguration( + {'canvasKitForceCpuOnly': true}.jsify() as JsFlutterConfiguration?, + ); addTearDown(() => debugOverrideJsConfiguration(null)); final Surface surface = Surface(); @@ -332,9 +316,7 @@ ui.Size getCssSize(Surface surface) { final String cssHeight = canvas.style.height; // CSS width and height should be in the form 'NNNpx'. So cut off the 'px' and // convert to a number. - final double width = - double.parse(cssWidth.substring(0, cssWidth.length - 2).trim()); - final double height = - double.parse(cssHeight.substring(0, cssHeight.length - 2).trim()); + final double width = double.parse(cssWidth.substring(0, cssWidth.length - 2).trim()); + final double height = double.parse(cssHeight.substring(0, cssHeight.length - 2).trim()); return ui.Size(width, height); } diff --git a/lib/web_ui/test/canvaskit/sweep_gradient_golden_test.dart b/lib/web_ui/test/canvaskit/sweep_gradient_golden_test.dart index c00eceb0a93ce..489c4b62fe3fc 100644 --- a/lib/web_ui/test/canvaskit/sweep_gradient_golden_test.dart +++ b/lib/web_ui/test/canvaskit/sweep_gradient_golden_test.dart @@ -26,25 +26,20 @@ void testMain() { final CkCanvas canvas = recorder.beginRecording(region); final CkGradientSweep gradient = CkGradientSweep( - const ui.Offset(250, 125), - const [ - ui.Color(0xFF4285F4), - ui.Color(0xFF34A853), - ui.Color(0xFFFBBC05), - ui.Color(0xFFEA4335), - ui.Color(0xFF4285F4), - ], - const [ - 0.0, - 0.25, - 0.5, - 0.75, - 1.0, - ], - ui.TileMode.clamp, - -(math.pi / 2), - math.pi * 2 - (math.pi / 2), - null); + const ui.Offset(250, 125), + const [ + ui.Color(0xFF4285F4), + ui.Color(0xFF34A853), + ui.Color(0xFFFBBC05), + ui.Color(0xFFEA4335), + ui.Color(0xFF4285F4), + ], + const [0.0, 0.25, 0.5, 0.75, 1.0], + ui.TileMode.clamp, + -(math.pi / 2), + math.pi * 2 - (math.pi / 2), + null, + ); final CkPaint paint = CkPaint()..shader = gradient; diff --git a/lib/web_ui/test/canvaskit/test_data.dart b/lib/web_ui/test/canvaskit/test_data.dart index 8a131cde1dd51..a44f0968338c6 100644 --- a/lib/web_ui/test/canvaskit/test_data.dart +++ b/lib/web_ui/test/canvaskit/test_data.dart @@ -6,50 +6,451 @@ import 'dart:typed_data'; /// A 1x1 fully transparent PNG image. final Uint8List kTransparentImage = Uint8List.fromList([ - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x04, 0x00, 0x00, 0x00, 0xb5, 0x1c, 0x0c, - 0x02, 0x00, 0x00, 0x00, 0x0b, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0x64, 0x60, 0x00, 0x00, - 0x00, 0x06, 0x00, 0x02, 0x30, 0x81, 0xd0, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, - 0xae, 0x42, 0x60, 0x82, + 0x89, + 0x50, + 0x4e, + 0x47, + 0x0d, + 0x0a, + 0x1a, + 0x0a, + 0x00, + 0x00, + 0x00, + 0x0d, + 0x49, + 0x48, + 0x44, + 0x52, + 0x00, + 0x00, + 0x00, + 0x01, + 0x00, + 0x00, + 0x00, + 0x01, + 0x08, + 0x04, + 0x00, + 0x00, + 0x00, + 0xb5, + 0x1c, + 0x0c, + 0x02, + 0x00, + 0x00, + 0x00, + 0x0b, + 0x49, + 0x44, + 0x41, + 0x54, + 0x78, + 0xda, + 0x63, + 0x64, + 0x60, + 0x00, + 0x00, + 0x00, + 0x06, + 0x00, + 0x02, + 0x30, + 0x81, + 0xd0, + 0x2f, + 0x00, + 0x00, + 0x00, + 0x00, + 0x49, + 0x45, + 0x4e, + 0x44, + 0xae, + 0x42, + 0x60, + 0x82, ]); /// A 4x4 PNG image sample. final Uint8List k4x4PngImage = Uint8List.fromList([ - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, - 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x08, 0x06, 0x00, 0x00, 0x00, 0xa9, 0xf1, 0x9e, - 0x7e, 0x00, 0x00, 0x00, 0x13, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x63, 0xfc, 0xcf, 0xc0, 0x50, - 0xcf, 0x80, 0x04, 0x18, 0x49, 0x17, 0x00, 0x00, 0xf2, 0xae, 0x05, 0xfd, 0x52, 0x01, 0xc2, 0xde, - 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, + 0x89, + 0x50, + 0x4e, + 0x47, + 0x0d, + 0x0a, + 0x1a, + 0x0a, + 0x00, + 0x00, + 0x00, + 0x0d, + 0x49, + 0x48, + 0x44, + 0x52, + 0x00, + 0x00, + 0x00, + 0x04, + 0x00, + 0x00, + 0x00, + 0x04, + 0x08, + 0x06, + 0x00, + 0x00, + 0x00, + 0xa9, + 0xf1, + 0x9e, + 0x7e, + 0x00, + 0x00, + 0x00, + 0x13, + 0x49, + 0x44, + 0x41, + 0x54, + 0x78, + 0xda, + 0x63, + 0xfc, + 0xcf, + 0xc0, + 0x50, + 0xcf, + 0x80, + 0x04, + 0x18, + 0x49, + 0x17, + 0x00, + 0x00, + 0xf2, + 0xae, + 0x05, + 0xfd, + 0x52, + 0x01, + 0xc2, + 0xde, + 0x00, + 0x00, + 0x00, + 0x00, + 0x49, + 0x45, + 0x4e, + 0x44, + 0xae, + 0x42, + 0x60, + 0x82, ]); /// An animated GIF image with 3 1x1 pixel frames (a red, green, and blue /// frames). The GIF animates forever, and each frame has a 100ms delay. -final Uint8List kAnimatedGif = Uint8List.fromList( [ - 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0xa1, 0x03, 0x00, - 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x21, - 0xff, 0x0b, 0x4e, 0x45, 0x54, 0x53, 0x43, 0x41, 0x50, 0x45, 0x32, 0x2e, 0x30, - 0x03, 0x01, 0x00, 0x00, 0x00, 0x21, 0xf9, 0x04, 0x00, 0x0a, 0x00, 0xff, 0x00, - 0x2c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x4c, - 0x01, 0x00, 0x21, 0xf9, 0x04, 0x00, 0x0a, 0x00, 0xff, 0x00, 0x2c, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x54, 0x01, 0x00, 0x21, - 0xf9, 0x04, 0x00, 0x0a, 0x00, 0xff, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3b, +final Uint8List kAnimatedGif = Uint8List.fromList([ + 0x47, + 0x49, + 0x46, + 0x38, + 0x39, + 0x61, + 0x01, + 0x00, + 0x01, + 0x00, + 0xa1, + 0x03, + 0x00, + 0x00, + 0x00, + 0xff, + 0xff, + 0x00, + 0x00, + 0x00, + 0xff, + 0x00, + 0xff, + 0xff, + 0xff, + 0x21, + 0xff, + 0x0b, + 0x4e, + 0x45, + 0x54, + 0x53, + 0x43, + 0x41, + 0x50, + 0x45, + 0x32, + 0x2e, + 0x30, + 0x03, + 0x01, + 0x00, + 0x00, + 0x00, + 0x21, + 0xf9, + 0x04, + 0x00, + 0x0a, + 0x00, + 0xff, + 0x00, + 0x2c, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, + 0x00, + 0x01, + 0x00, + 0x00, + 0x02, + 0x02, + 0x4c, + 0x01, + 0x00, + 0x21, + 0xf9, + 0x04, + 0x00, + 0x0a, + 0x00, + 0xff, + 0x00, + 0x2c, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, + 0x00, + 0x01, + 0x00, + 0x00, + 0x02, + 0x02, + 0x54, + 0x01, + 0x00, + 0x21, + 0xf9, + 0x04, + 0x00, + 0x0a, + 0x00, + 0xff, + 0x00, + 0x2c, + 0x00, + 0x00, + 0x00, + 0x00, + 0x01, + 0x00, + 0x01, + 0x00, + 0x00, + 0x02, + 0x02, + 0x44, + 0x01, + 0x00, + 0x3b, ]); /// A 2x2 translucent PNG. -final Uint8List kTranslucentPng = Uint8List.fromList( [ - 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, - 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x01, 0x03, - 0x00, 0x00, 0x00, 0x48, 0x78, 0x9f, 0x67, 0x00, 0x00, 0x00, 0x20, 0x63, 0x48, - 0x52, 0x4d, 0x00, 0x00, 0x7a, 0x26, 0x00, 0x00, 0x80, 0x84, 0x00, 0x00, 0xfa, - 0x00, 0x00, 0x00, 0x80, 0xe8, 0x00, 0x00, 0x75, 0x30, 0x00, 0x00, 0xea, 0x60, - 0x00, 0x00, 0x3a, 0x98, 0x00, 0x00, 0x17, 0x70, 0x9c, 0xba, 0x51, 0x3c, 0x00, - 0x00, 0x00, 0x06, 0x50, 0x4c, 0x54, 0x45, 0x22, 0x44, 0x66, 0xff, 0xff, 0xff, - 0x5c, 0x83, 0x6d, 0xb6, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4e, 0x53, 0x80, - 0xad, 0x5e, 0x5b, 0x46, 0x00, 0x00, 0x00, 0x01, 0x62, 0x4b, 0x47, 0x44, 0x01, - 0xff, 0x02, 0x2d, 0xde, 0x00, 0x00, 0x00, 0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, - 0xe8, 0x04, 0x0c, 0x15, 0x16, 0x21, 0xc3, 0x89, 0xee, 0x25, 0x00, 0x00, 0x00, - 0x0c, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x63, 0x60, 0x60, 0x60, 0x00, 0x00, - 0x00, 0x04, 0x00, 0x01, 0x27, 0x34, 0x27, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x49, - 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +final Uint8List kTranslucentPng = Uint8List.fromList([ + 0x89, + 0x50, + 0x4e, + 0x47, + 0x0d, + 0x0a, + 0x1a, + 0x0a, + 0x00, + 0x00, + 0x00, + 0x0d, + 0x49, + 0x48, + 0x44, + 0x52, + 0x00, + 0x00, + 0x00, + 0x02, + 0x00, + 0x00, + 0x00, + 0x02, + 0x01, + 0x03, + 0x00, + 0x00, + 0x00, + 0x48, + 0x78, + 0x9f, + 0x67, + 0x00, + 0x00, + 0x00, + 0x20, + 0x63, + 0x48, + 0x52, + 0x4d, + 0x00, + 0x00, + 0x7a, + 0x26, + 0x00, + 0x00, + 0x80, + 0x84, + 0x00, + 0x00, + 0xfa, + 0x00, + 0x00, + 0x00, + 0x80, + 0xe8, + 0x00, + 0x00, + 0x75, + 0x30, + 0x00, + 0x00, + 0xea, + 0x60, + 0x00, + 0x00, + 0x3a, + 0x98, + 0x00, + 0x00, + 0x17, + 0x70, + 0x9c, + 0xba, + 0x51, + 0x3c, + 0x00, + 0x00, + 0x00, + 0x06, + 0x50, + 0x4c, + 0x54, + 0x45, + 0x22, + 0x44, + 0x66, + 0xff, + 0xff, + 0xff, + 0x5c, + 0x83, + 0x6d, + 0xb6, + 0x00, + 0x00, + 0x00, + 0x01, + 0x74, + 0x52, + 0x4e, + 0x53, + 0x80, + 0xad, + 0x5e, + 0x5b, + 0x46, + 0x00, + 0x00, + 0x00, + 0x01, + 0x62, + 0x4b, + 0x47, + 0x44, + 0x01, + 0xff, + 0x02, + 0x2d, + 0xde, + 0x00, + 0x00, + 0x00, + 0x07, + 0x74, + 0x49, + 0x4d, + 0x45, + 0x07, + 0xe8, + 0x04, + 0x0c, + 0x15, + 0x16, + 0x21, + 0xc3, + 0x89, + 0xee, + 0x25, + 0x00, + 0x00, + 0x00, + 0x0c, + 0x49, + 0x44, + 0x41, + 0x54, + 0x08, + 0xd7, + 0x63, + 0x60, + 0x60, + 0x60, + 0x00, + 0x00, + 0x00, + 0x04, + 0x00, + 0x01, + 0x27, + 0x34, + 0x27, + 0x0a, + 0x00, + 0x00, + 0x00, + 0x00, + 0x49, + 0x45, + 0x4e, + 0x44, + 0xae, + 0x42, + 0x60, + 0x82, ]); diff --git a/lib/web_ui/test/canvaskit/text_fragmenter_test.dart b/lib/web_ui/test/canvaskit/text_fragmenter_test.dart index 365de0c8677a2..2451f0b381aa0 100644 --- a/lib/web_ui/test/canvaskit/text_fragmenter_test.dart +++ b/lib/web_ui/test/canvaskit/text_fragmenter_test.dart @@ -24,10 +24,7 @@ void testMain() { 'Hello world 你好世界', IntlSegmenterGranularity.word, ); - expect( - breaks, - orderedEquals([0, 5, 6, 11, 12, 14, 16]), - ); + expect(breaks, orderedEquals([0, 5, 6, 11, 12, 14, 16])); }); test('fragments multi-line text into words', () { @@ -84,9 +81,7 @@ void testMain() { const int kHard = 1; test('fragments text into soft and hard line breaks', () { - final Uint32List breaks = fragmentUsingV8LineBreaker( - 'Lorem-ipsum 你好🙂\nDolor sit', - ); + final Uint32List breaks = fragmentUsingV8LineBreaker('Lorem-ipsum 你好🙂\nDolor sit'); expect( breaks, orderedEquals([ @@ -115,18 +110,12 @@ void testMain() { test('segments correctly', () { const String text = 'Lorem-ipsum 你好🙂\nDolor sit'; final SegmentationResult segmentation = segmentText(text); - expect( - segmentation.words, - fragmentUsingIntlSegmenter(text, IntlSegmenterGranularity.word), - ); + expect(segmentation.words, fragmentUsingIntlSegmenter(text, IntlSegmenterGranularity.word)); expect( segmentation.graphemes, fragmentUsingIntlSegmenter(text, IntlSegmenterGranularity.grapheme), ); - expect( - segmentation.breaks, - fragmentUsingV8LineBreaker(text), - ); + expect(segmentation.breaks, fragmentUsingV8LineBreaker(text)); }); test('caches segmentation results in LRU fashion', () { @@ -187,10 +176,7 @@ void testMain() { }, skip: !browserSupportsCanvaskitChromium); } -void testCacheCapacity( - LruCache cache, - SegmentationCacheSpec spec, -) { +void testCacheCapacity(LruCache cache, SegmentationCacheSpec spec) { // 1. Fill the cache. for (int i = 0; i < spec.cacheSize; i++) { final String text = _randomString(spec.maxTextLength); @@ -218,7 +204,8 @@ void testCacheCapacity( int _seed = 0; String _randomString(int length) { - const String allChars = ' 1234567890' + const String allChars = + ' 1234567890' 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; diff --git a/lib/web_ui/test/canvaskit/text_test.dart b/lib/web_ui/test/canvaskit/text_test.dart index 52532fc4ee4b5..77280d803cf31 100644 --- a/lib/web_ui/test/canvaskit/text_test.dart +++ b/lib/web_ui/test/canvaskit/text_test.dart @@ -22,29 +22,16 @@ void testMain() { final ui.TextStyle textStyleWithShadows = ui.TextStyle( fontSize: 16, shadows: [ - const ui.Shadow( - blurRadius: 3.0, - offset: ui.Offset(3.0, 3.0), - ), - const ui.Shadow( - blurRadius: 3.0, - offset: ui.Offset(-3.0, 3.0), - ), - const ui.Shadow( - blurRadius: 3.0, - offset: ui.Offset(3.0, -3.0), - ), - const ui.Shadow( - blurRadius: 3.0, - offset: ui.Offset(-3.0, -3.0), - ), + const ui.Shadow(blurRadius: 3.0, offset: ui.Offset(3.0, 3.0)), + const ui.Shadow(blurRadius: 3.0, offset: ui.Offset(-3.0, 3.0)), + const ui.Shadow(blurRadius: 3.0, offset: ui.Offset(3.0, -3.0)), + const ui.Shadow(blurRadius: 3.0, offset: ui.Offset(-3.0, -3.0)), ], fontFamily: 'Roboto', ); for (int i = 0; i < 10; i++) { - final ui.ParagraphBuilder builder = - ui.ParagraphBuilder(ui.ParagraphStyle(fontSize: 16)); + final ui.ParagraphBuilder builder = ui.ParagraphBuilder(ui.ParagraphStyle(fontSize: 16)); builder.pushStyle(textStyleWithShadows); builder.addText('test'); final ui.Paragraph paragraph = builder.build(); @@ -56,7 +43,8 @@ void testMain() { test('getBoxesForRange works for LTR text in an RTL paragraph', () { // Create builder for an RTL paragraph. final ui.ParagraphBuilder builder = ui.ParagraphBuilder( - ui.ParagraphStyle(fontSize: 16, textDirection: ui.TextDirection.rtl)); + ui.ParagraphStyle(fontSize: 16, textDirection: ui.TextDirection.rtl), + ); builder.addText('hello'); final ui.Paragraph paragraph = builder.build(); paragraph.layout(const ui.ParagraphConstraints(width: 100)); @@ -105,9 +93,18 @@ void testMain() { test('The default test font is used when a non-test fontFamily is specified', () { final String defaultTestFontFamily = testFonts.first; - expect(CkTextStyle(fontFamily: 'BogusFontFamily').effectiveFontFamily, defaultTestFontFamily); - expect(CkParagraphStyle(fontFamily: 'BogusFontFamily').getTextStyle().effectiveFontFamily, defaultTestFontFamily); - expect(CkStrutStyle(fontFamily: 'BogusFontFamily'), CkStrutStyle(fontFamily: defaultTestFontFamily)); + expect( + CkTextStyle(fontFamily: 'BogusFontFamily').effectiveFontFamily, + defaultTestFontFamily, + ); + expect( + CkParagraphStyle(fontFamily: 'BogusFontFamily').getTextStyle().effectiveFontFamily, + defaultTestFontFamily, + ); + expect( + CkStrutStyle(fontFamily: 'BogusFontFamily'), + CkStrutStyle(fontFamily: defaultTestFontFamily), + ); }); test('The default test font is used when fontFamily is unspecified', () { @@ -121,7 +118,10 @@ void testMain() { test('Can specify test fontFamily to use', () { for (final String testFont in testFonts) { expect(CkTextStyle(fontFamily: testFont).effectiveFontFamily, testFont); - expect(CkParagraphStyle(fontFamily: testFont).getTextStyle().effectiveFontFamily, testFont); + expect( + CkParagraphStyle(fontFamily: testFont).getTextStyle().effectiveFontFamily, + testFont, + ); } }); }); diff --git a/lib/web_ui/test/common/fake_asset_manager.dart b/lib/web_ui/test/common/fake_asset_manager.dart index b6f8c176e5c8a..ad24a4827327d 100644 --- a/lib/web_ui/test/common/fake_asset_manager.dart +++ b/lib/web_ui/test/common/fake_asset_manager.dart @@ -33,15 +33,10 @@ class FakeAssetManager implements ui_web.AssetManager { return MockHttpFetchResponse( url: asset, status: 200, - payload: MockHttpFetchPayload( - byteBuffer: assetData.buffer, - ), + payload: MockHttpFetchPayload(byteBuffer: assetData.buffer), ); } else { - return MockHttpFetchResponse( - url: asset, - status: 404, - ); + return MockHttpFetchResponse(url: asset, status: 404); } } @@ -63,7 +58,8 @@ class FakeAssetScope { FakeAssetScope._(this._parent); final FakeAssetScope? _parent; - final Map Function()> _assetFetcherMap = Function()>{}; + final Map Function()> _assetFetcherMap = + Function()>{}; void setAsset(String assetKey, ByteData assetData) { _assetFetcherMap[assetKey] = () async => assetData; @@ -112,7 +108,9 @@ const Map testFontUrls = { FakeAssetScope configureDebugFontsAssetScope(FakeAssetManager manager) { final FakeAssetScope scope = manager.pushAssetScope(); scope.setAsset('AssetManifest.json', stringAsUtf8Data('{}')); - scope.setAsset('FontManifest.json', stringAsUtf8Data(''' + scope.setAsset( + 'FontManifest.json', + stringAsUtf8Data(''' [ { "family":"$robotoFontFamily", @@ -126,7 +124,8 @@ FakeAssetScope configureDebugFontsAssetScope(FakeAssetManager manager) { "family":"$robotoVariableFontFamily", "fonts":[{"asset":"$robotoVariableFontUrl"}] } - ]''')); + ]'''), + ); scope.setAssetPassthrough(robotoTestFontUrl); scope.setAssetPassthrough(ahemFontUrl); scope.setAssetPassthrough(robotoVariableFontUrl); diff --git a/lib/web_ui/test/common/keyboard_test_common.dart b/lib/web_ui/test/common/keyboard_test_common.dart index 0ec1e5d75acb9..9cc5f43292d62 100644 --- a/lib/web_ui/test/common/keyboard_test_common.dart +++ b/lib/web_ui/test/common/keyboard_test_common.dart @@ -23,14 +23,13 @@ class MockKeyboardEvent implements FlutterHtmlKeyboardEvent { this.location = 0, this.onPreventDefault, this.onStopPropagation, - }) : modifierState = - { - if (altKey) 'Alt', - if (ctrlKey) 'Control', - if (shiftKey) 'Shift', - if (metaKey) 'Meta', - if (altGrKey) 'AltGraph', - } { + }) : modifierState = { + if (altKey) 'Alt', + if (ctrlKey) 'Control', + if (shiftKey) 'Shift', + if (metaKey) 'Meta', + if (altGrKey) 'AltGraph', + } { _lastEvent = this; } @@ -79,6 +78,7 @@ class MockKeyboardEvent implements FlutterHtmlKeyboardEvent { onPreventDefault?.call(); _defaultPrevented = true; } + VoidCallback? onPreventDefault; @override @@ -89,6 +89,7 @@ class MockKeyboardEvent implements FlutterHtmlKeyboardEvent { void stopPropagation() { onStopPropagation?.call(); } + VoidCallback? onStopPropagation; static bool get lastDefaultPrevented => _lastEvent?.defaultPrevented ?? false; diff --git a/lib/web_ui/test/common/matchers.dart b/lib/web_ui/test/common/matchers.dart index 95d4c8ba2348d..1f8d540825f81 100644 --- a/lib/web_ui/test/common/matchers.dart +++ b/lib/web_ui/test/common/matchers.dart @@ -82,8 +82,7 @@ typedef DistanceFunction = double Function(T a, T b); /// first casting it to a [DistanceFunction] for some concrete T. typedef AnyDistanceFunction = double Function(Never a, Never b); -const Map _kStandardDistanceFunctions = - { +const Map _kStandardDistanceFunctions = { Color: _maxComponentColorDistance, Offset: _offsetDistance, int: _intDistance, @@ -104,8 +103,7 @@ double _maxComponentColorDistance(Color a, Color b) { } double _rectDistance(Rect a, Rect b) { - double delta = - math.max((a.left - b.left).abs(), (a.top - b.top).abs()); + double delta = math.max((a.left - b.left).abs(), (a.top - b.top).abs()); delta = math.max(delta, (a.right - b.right).abs()); delta = math.max(delta, (a.bottom - b.bottom).abs()); return delta; @@ -146,9 +144,10 @@ Matcher within({ if (distanceFunction == null) { throw ArgumentError( - 'The specified distanceFunction was null, and a standard distance ' - 'function was not found for type $T of the provided ' - '`from` argument.'); + 'The specified distanceFunction was null, and a standard distance ' + 'function was not found for type $T of the provided ' + '`from` argument.', + ); } return _IsWithinDistance(distanceFunction, from, distance); @@ -173,17 +172,17 @@ class _IsWithinDistance extends Matcher { final double distance = distanceFunction(test, value); if (distance < 0) { throw ArgumentError( - 'Invalid distance function was used to compare a ${value.runtimeType} ' - 'to a ${object.runtimeType}. The function must return a non-negative ' - 'double value, but it returned $distance.'); + 'Invalid distance function was used to compare a ${value.runtimeType} ' + 'to a ${object.runtimeType}. The function must return a non-negative ' + 'double value, but it returned $distance.', + ); } matchState['distance'] = distance; return distance <= epsilon; } @override - Description describe(Description description) => - description.add('$value (±$epsilon)'); + Description describe(Description description) => description.add('$value (±$epsilon)'); @override Description describeMismatch( @@ -192,8 +191,7 @@ class _IsWithinDistance extends Matcher { Map matchState, bool verbose, ) { - mismatchDescription - .add('was ${matchState['distance']} away from the desired value.'); + mismatchDescription.add('was ${matchState['distance']} away from the desired value.'); return mismatchDescription; } } @@ -240,12 +238,14 @@ Matcher hasHtml(String htmlPattern) { if (originalDom.children.isEmpty) { fail( 'Test HTML pattern is empty.\n' - 'The pattern must contain exacly one top-level element, but was: $htmlPattern'); + 'The pattern must contain exacly one top-level element, but was: $htmlPattern', + ); } if (originalDom.children.length > 1) { fail( 'Test HTML pattern has more than one top-level element.\n' - 'The pattern must contain exacly one top-level element, but was: $htmlPattern'); + 'The pattern must contain exacly one top-level element, but was: $htmlPattern', + ); } return HtmlPatternMatcher(originalDom.children.single); } @@ -325,12 +325,17 @@ class HtmlPatternMatcher extends Matcher { return aName == bName; } - void matchElements(_Breadcrumbs parent, List mismatches, html.Element element, html.Element pattern) { + void matchElements( + _Breadcrumbs parent, + List mismatches, + html.Element element, + html.Element pattern, + ) { final _Breadcrumbs breadcrumb = parent.element(pattern.localName!); if (!_areTagsEqual(element, pattern)) { mismatches.add( - '$breadcrumb: unexpected tag name <${element.localName}> (expected <${pattern.localName}>).' + '$breadcrumb: unexpected tag name <${element.localName}> (expected <${pattern.localName}>).', ); // Don't bother matching anything else. If tags are different, it's likely // we're comparing apples to oranges at this point. @@ -341,7 +346,12 @@ class HtmlPatternMatcher extends Matcher { matchChildren(breadcrumb, mismatches, element, pattern); } - void matchAttributes(_Breadcrumbs parent, List mismatches, html.Element element, html.Element pattern) { + void matchAttributes( + _Breadcrumbs parent, + List mismatches, + html.Element element, + html.Element pattern, + ) { for (final MapEntry attribute in pattern.attributes.entries) { final String expectedName = attribute.key as String; final String expectedValue = attribute.value; @@ -358,7 +368,7 @@ class HtmlPatternMatcher extends Matcher { if (actualValue != expectedValue) { mismatches.add( '$breadcrumb: expected attribute value $expectedName="$expectedValue", ' - 'but found $expectedName="$actualValue".' + 'but found $expectedName="$actualValue".', ); } } @@ -380,19 +390,22 @@ class HtmlPatternMatcher extends Matcher { return result; } - void matchStyle(_Breadcrumbs parent, List mismatches, html.Element element, html.Element pattern) { + void matchStyle( + _Breadcrumbs parent, + List mismatches, + html.Element element, + html.Element pattern, + ) { final Map expected = parseStyle(pattern); final Map actual = parseStyle(element); for (final MapEntry entry in expected.entries) { final _Breadcrumbs breadcrumb = parent.styleProperty(entry.key); if (!actual.containsKey(entry.key)) { - mismatches.add( - '$breadcrumb: style property ${entry.key}="${entry.value}" missing.' - ); + mismatches.add('$breadcrumb: style property ${entry.key}="${entry.value}" missing.'); } else if (actual[entry.key] != entry.value) { mismatches.add( '$breadcrumb: expected style property ${entry.key}="${entry.value}", ' - 'but found ${entry.key}="${actual[entry.key]}".' + 'but found ${entry.key}="${actual[entry.key]}".', ); } } @@ -432,13 +445,18 @@ class HtmlPatternMatcher extends Matcher { return cleanNodes; } - void matchChildren(_Breadcrumbs parent, List mismatches, html.Element element, html.Element pattern) { + void matchChildren( + _Breadcrumbs parent, + List mismatches, + html.Element element, + html.Element pattern, + ) { final List actualChildNodes = _cleanUpNodeList(element.nodes); final List expectedChildNodes = _cleanUpNodeList(pattern.nodes); if (actualChildNodes.length != expectedChildNodes.length) { mismatches.add( - '$parent: expected ${expectedChildNodes.length} child nodes, but found ${actualChildNodes.length}.' + '$parent: expected ${expectedChildNodes.length} child nodes, but found ${actualChildNodes.length}.', ); return; } @@ -452,12 +470,12 @@ class HtmlPatternMatcher extends Matcher { } else if (expectedChild is html.Text && actualChild is html.Text) { if (expectedChild.data != actualChild.data) { mismatches.add( - '$parent: expected text content "${expectedChild.data}", but found "${actualChild.data}".' + '$parent: expected text content "${expectedChild.data}", but found "${actualChild.data}".', ); } } else { mismatches.add( - '$parent: expected child type ${expectedChild.runtimeType}, but found ${actualChild.runtimeType}.' + '$parent: expected child type ${expectedChild.runtimeType}, but found ${actualChild.runtimeType}.', ); } } diff --git a/lib/web_ui/test/common/mock_engine_canvas.dart b/lib/web_ui/test/common/mock_engine_canvas.dart index 59c4c81680372..434d3e7c008f4 100644 --- a/lib/web_ui/test/common/mock_engine_canvas.dart +++ b/lib/web_ui/test/common/mock_engine_canvas.dart @@ -10,10 +10,7 @@ import 'package:ui/ui.dart'; /// Contains method name that was called on [MockEngineCanvas] and arguments /// that were passed. class MockCanvasCall { - MockCanvasCall._({ - required this.methodName, - this.arguments, - }); + MockCanvasCall._({required this.methodName, this.arguments}); final String methodName; final dynamic arguments; @@ -37,10 +34,7 @@ class MockEngineCanvas implements EngineCanvas { final DomElement _rootElement = createDomHTMLDivElement(); void _called(String methodName, {dynamic arguments}) { - methodCallLog.add(MockCanvasCall._( - methodName: methodName, - arguments: arguments, - )); + methodCallLog.add(MockCanvasCall._(methodName: methodName, arguments: arguments)); } @override @@ -65,18 +59,12 @@ class MockEngineCanvas implements EngineCanvas { @override void translate(double dx, double dy) { - _called('translate', arguments: { - 'dx': dx, - 'dy': dy, - }); + _called('translate', arguments: {'dx': dx, 'dy': dy}); } @override void scale(double sx, double sy) { - _called('scale', arguments: { - 'sx': sx, - 'sy': sy, - }); + _called('scale', arguments: {'sx': sx, 'sy': sy}); } @override @@ -86,10 +74,7 @@ class MockEngineCanvas implements EngineCanvas { @override void skew(double sx, double sy) { - _called('skew', arguments: { - 'sx': sx, - 'sy': sy, - }); + _called('skew', arguments: {'sx': sx, 'sy': sy}); } @override @@ -114,19 +99,12 @@ class MockEngineCanvas implements EngineCanvas { @override void drawColor(Color color, BlendMode blendMode) { - _called('drawColor', arguments: { - 'color': color, - 'blendMode': blendMode, - }); + _called('drawColor', arguments: {'color': color, 'blendMode': blendMode}); } @override void drawLine(Offset p1, Offset p2, SurfacePaintData paint) { - _called('drawLine', arguments: { - 'p1': p1, - 'p2': p2, - 'paint': paint, - }); + _called('drawLine', arguments: {'p1': p1, 'p2': p2, 'paint': paint}); } @override @@ -136,109 +114,85 @@ class MockEngineCanvas implements EngineCanvas { @override void drawRect(Rect rect, SurfacePaintData paint) { - _called('drawRect', arguments: { - 'rect': rect, - 'paint': paint, - }); + _called('drawRect', arguments: {'rect': rect, 'paint': paint}); } @override void drawRRect(RRect rrect, SurfacePaintData paint) { - _called('drawRRect', arguments: { - 'rrect': rrect, - 'paint': paint, - }); + _called('drawRRect', arguments: {'rrect': rrect, 'paint': paint}); } @override void drawDRRect(RRect outer, RRect inner, SurfacePaintData paint) { - _called('drawDRRect', arguments: { - 'outer': outer, - 'inner': inner, - 'paint': paint, - }); + _called( + 'drawDRRect', + arguments: {'outer': outer, 'inner': inner, 'paint': paint}, + ); } @override void drawOval(Rect rect, SurfacePaintData paint) { - _called('drawOval', arguments: { - 'rect': rect, - 'paint': paint, - }); + _called('drawOval', arguments: {'rect': rect, 'paint': paint}); } @override void drawCircle(Offset c, double radius, SurfacePaintData paint) { - _called('drawCircle', arguments: { - 'c': c, - 'radius': radius, - 'paint': paint, - }); + _called('drawCircle', arguments: {'c': c, 'radius': radius, 'paint': paint}); } @override void drawPath(Path path, SurfacePaintData paint) { - _called('drawPath', arguments: { - 'path': path, - 'paint': paint, - }); + _called('drawPath', arguments: {'path': path, 'paint': paint}); } @override - void drawShadow( - Path path, Color color, double elevation, bool transparentOccluder) { - _called('drawShadow', arguments: { - 'path': path, - 'color': color, - 'elevation': elevation, - 'transparentOccluder': transparentOccluder, - }); + void drawShadow(Path path, Color color, double elevation, bool transparentOccluder) { + _called( + 'drawShadow', + arguments: { + 'path': path, + 'color': color, + 'elevation': elevation, + 'transparentOccluder': transparentOccluder, + }, + ); } @override void drawImage(Image image, Offset p, SurfacePaintData paint) { - _called('drawImage', arguments: { - 'image': image, - 'p': p, - 'paint': paint, - }); + _called('drawImage', arguments: {'image': image, 'p': p, 'paint': paint}); } @override void drawImageRect(Image image, Rect src, Rect dst, SurfacePaintData paint) { - _called('drawImageRect', arguments: { - 'image': image, - 'src': src, - 'dst': dst, - 'paint': paint, - }); + _called( + 'drawImageRect', + arguments: {'image': image, 'src': src, 'dst': dst, 'paint': paint}, + ); } @override void drawParagraph(Paragraph paragraph, Offset offset) { - _called('drawParagraph', arguments: { - 'paragraph': paragraph, - 'offset': offset, - }); + _called( + 'drawParagraph', + arguments: {'paragraph': paragraph, 'offset': offset}, + ); } @override - void drawVertices( - Vertices vertices, BlendMode blendMode, SurfacePaintData paint) { - _called('drawVertices', arguments: { - 'vertices': vertices, - 'blendMode': blendMode, - 'paint': paint, - }); + void drawVertices(Vertices vertices, BlendMode blendMode, SurfacePaintData paint) { + _called( + 'drawVertices', + arguments: {'vertices': vertices, 'blendMode': blendMode, 'paint': paint}, + ); } @override void drawPoints(PointMode pointMode, Float32List points, SurfacePaintData paint) { - _called('drawPoints', arguments: { - 'pointMode': pointMode, - 'points': points, - 'paint': paint, - }); + _called( + 'drawPoints', + arguments: {'pointMode': pointMode, 'points': points, 'paint': paint}, + ); } @override diff --git a/lib/web_ui/test/common/rendering.dart b/lib/web_ui/test/common/rendering.dart index 89ecc319b19bb..9e7e2fae71faf 100644 --- a/lib/web_ui/test/common/rendering.dart +++ b/lib/web_ui/test/common/rendering.dart @@ -19,11 +19,14 @@ void setUpRenderingForTests() { // Set `onDrawFrame` to call `renderer.renderScene`. EnginePlatformDispatcher.instance.onDrawFrame = () { if (_sceneToRender != null) { - EnginePlatformDispatcher.instance.render(_sceneToRender!).then((_) { - _sceneCompleter?.complete(); - }).catchError((Object error) { - _sceneCompleter?.completeError(error); - }); + EnginePlatformDispatcher.instance + .render(_sceneToRender!) + .then((_) { + _sceneCompleter?.complete(); + }) + .catchError((Object error) { + _sceneCompleter?.completeError(error); + }); _sceneToRender = null; } }; diff --git a/lib/web_ui/test/common/spy.dart b/lib/web_ui/test/common/spy.dart index f339151197caa..819c406e2097d 100644 --- a/lib/web_ui/test/common/spy.dart +++ b/lib/web_ui/test/common/spy.dart @@ -45,12 +45,8 @@ class PlatformMessagesSpy { /// This is typically called inside a test's `setUp` callback. void setUp() { assert(!_isActive); - _callback = (String channel, ByteData? data, - PlatformMessageResponseCallback? callback) { - messages.add(PlatformMessage( - channel, - const JSONMethodCodec().decodeMethodCall(data), - )); + _callback = (String channel, ByteData? data, PlatformMessageResponseCallback? callback) { + messages.add(PlatformMessage(channel, const JSONMethodCodec().decodeMethodCall(data))); }; _backup = PlatformDispatcher.instance.onPlatformMessage; diff --git a/lib/web_ui/test/common/test_initialization.dart b/lib/web_ui/test/common/test_initialization.dart index a766291c0263d..d47a2d2439a50 100644 --- a/lib/web_ui/test/common/test_initialization.dart +++ b/lib/web_ui/test/common/test_initialization.dart @@ -28,9 +28,10 @@ void setUpUnitTests({ debugFontsScope = configureDebugFontsAssetScope(fakeAssetManager); debugOnlyAssetManager = fakeAssetManager; await bootstrapAndRunApp(withImplicitView: withImplicitView); - engine.debugOverrideJsConfiguration({ - 'fontFallbackBaseUrl': 'assets/fallback_fonts/', - }.jsify() as engine.JsFlutterConfiguration?); + engine.debugOverrideJsConfiguration( + {'fontFallbackBaseUrl': 'assets/fallback_fonts/'}.jsify() + as engine.JsFlutterConfiguration?, + ); if (setUpTestViewDimensions) { // The following parameters are hard-coded in Flutter's test embedder. Since @@ -81,8 +82,11 @@ void _disableImplicitView() { // TODO(mdebbar): Instead of disabling the implicit view, we should be able to // initialize tests without an implicit view to begin with. // https://github.com/flutter/flutter/issues/138906 - final engine.EngineFlutterWindow? implicitView = engine.EnginePlatformDispatcher.instance.implicitView; + final engine.EngineFlutterWindow? implicitView = + engine.EnginePlatformDispatcher.instance.implicitView; if (implicitView != null) { - engine.EnginePlatformDispatcher.instance.viewManager.disposeAndUnregisterView(implicitView.viewId); + engine.EnginePlatformDispatcher.instance.viewManager.disposeAndUnregisterView( + implicitView.viewId, + ); } } diff --git a/lib/web_ui/test/engine/alarm_clock_test.dart b/lib/web_ui/test/engine/alarm_clock_test.dart index 4a1c744f68ddf..75eeed9dffdaa 100644 --- a/lib/web_ui/test/engine/alarm_clock_test.dart +++ b/lib/web_ui/test/engine/alarm_clock_test.dart @@ -30,8 +30,7 @@ void _alarmClockTests() { callCount = 0; }); - testAsync('AlarmClock calls the callback in the future', - (FakeAsync fakeAsync) { + testAsync('AlarmClock calls the callback in the future', (FakeAsync fakeAsync) { final Clock clock = fakeAsync.getClock(DateTime(2019, 1, 24)); final AlarmClock alarm = AlarmClock(clock.now); alarm.callback = testCallback; @@ -66,8 +65,7 @@ void _alarmClockTests() { expect(callCount, 2); }); - testAsync('AlarmClock does nothing when new datetime is the same', - (FakeAsync fakeAsync) { + testAsync('AlarmClock does nothing when new datetime is the same', (FakeAsync fakeAsync) { final Clock clock = fakeAsync.getClock(DateTime(2019, 1, 24)); final AlarmClock alarm = AlarmClock(clock.now); alarm.callback = testCallback; @@ -91,8 +89,7 @@ void _alarmClockTests() { expect(callCount, 1); }); - testAsync('AlarmClock does not call the callback in the past', - (FakeAsync fakeAsync) { + testAsync('AlarmClock does not call the callback in the past', (FakeAsync fakeAsync) { final Clock clock = fakeAsync.getClock(DateTime(2019, 1, 24)); final AlarmClock alarm = AlarmClock(clock.now); alarm.callback = testCallback; @@ -148,8 +145,7 @@ void _alarmClockTests() { expect(fakeAsync.nonPeriodicTimerCount, 0); }); - testAsync('AlarmClock cancels the timer when datetime is null', - (FakeAsync fakeAsync) { + testAsync('AlarmClock cancels the timer when datetime is null', (FakeAsync fakeAsync) { final Clock clock = fakeAsync.getClock(DateTime(2019, 1, 24)); final AlarmClock alarm = AlarmClock(clock.now); alarm.callback = testCallback; @@ -168,8 +164,7 @@ void _alarmClockTests() { expect(fakeAsync.nonPeriodicTimerCount, 0); }); - testAsync('AlarmClock cancels the timer when datetime is in the past', - (FakeAsync fakeAsync) { + testAsync('AlarmClock cancels the timer when datetime is in the past', (FakeAsync fakeAsync) { final Clock clock = fakeAsync.getClock(DateTime(2019, 1, 24)); final AlarmClock alarm = AlarmClock(clock.now); alarm.callback = testCallback; diff --git a/lib/web_ui/test/engine/app_bootstrap_test.dart b/lib/web_ui/test/engine/app_bootstrap_test.dart index d9f55f6f6029b..f6a7aa326f870 100644 --- a/lib/web_ui/test/engine/app_bootstrap_test.dart +++ b/lib/web_ui/test/engine/app_bootstrap_test.dart @@ -16,7 +16,6 @@ void main() { } void testMain() { - int callOrder = 1; int initCalled = 0; int runCalled = 0; @@ -27,22 +26,19 @@ void testMain() { runCalled = 0; }); - Future mockInit ([JsFlutterConfiguration? configuration]) async { + Future mockInit([JsFlutterConfiguration? configuration]) async { debugOverrideJsConfiguration(configuration); addTearDown(() => debugOverrideJsConfiguration(null)); initCalled = callOrder++; await Future.delayed(const Duration(milliseconds: 1)); } - Future mockRunApp () async { + Future mockRunApp() async { runCalled = callOrder++; } test('autoStart() immediately calls init and run', () async { - final AppBootstrap bootstrap = AppBootstrap( - initializeEngine: mockInit, - runApp: mockRunApp, - ); + final AppBootstrap bootstrap = AppBootstrap(initializeEngine: mockInit, runApp: mockRunApp); await bootstrap.autoStart(); @@ -51,16 +47,15 @@ void testMain() { }); test('engineInitializer autoStart() does the same as Dart autoStart()', () async { - final AppBootstrap bootstrap = AppBootstrap( - initializeEngine: mockInit, - runApp: mockRunApp, - ); + final AppBootstrap bootstrap = AppBootstrap(initializeEngine: mockInit, runApp: mockRunApp); final FlutterEngineInitializer engineInitializer = bootstrap.prepareEngineInitializer(); expect(engineInitializer, isNotNull); - final Object maybeApp = await promiseToFuture(callMethod(engineInitializer, 'autoStart', [])); + final Object maybeApp = await promiseToFuture( + callMethod(engineInitializer, 'autoStart', []), + ); expect(maybeApp, isA()); expect(initCalled, 1, reason: 'initEngine should be called first.'); @@ -68,14 +63,13 @@ void testMain() { }); test('engineInitializer initEngine() calls init and returns an appRunner', () async { - final AppBootstrap bootstrap = AppBootstrap( - initializeEngine: mockInit, - runApp: mockRunApp, - ); + final AppBootstrap bootstrap = AppBootstrap(initializeEngine: mockInit, runApp: mockRunApp); final FlutterEngineInitializer engineInitializer = bootstrap.prepareEngineInitializer(); - final Object maybeAppInitializer = await promiseToFuture(callMethod(engineInitializer, 'initializeEngine', [])); + final Object maybeAppInitializer = await promiseToFuture( + callMethod(engineInitializer, 'initializeEngine', []), + ); expect(maybeAppInitializer, isA()); expect(initCalled, 1, reason: 'initEngine should have been called.'); @@ -83,24 +77,17 @@ void testMain() { }); test('appRunner runApp() calls run and returns a FlutterApp', () async { - final AppBootstrap bootstrap = AppBootstrap( - initializeEngine: mockInit, - runApp: mockRunApp, - ); + final AppBootstrap bootstrap = AppBootstrap(initializeEngine: mockInit, runApp: mockRunApp); final FlutterEngineInitializer engineInitializer = bootstrap.prepareEngineInitializer(); - final Object appInitializer = await promiseToFuture(callMethod( - engineInitializer, - 'initializeEngine', - [] - )); + final Object appInitializer = await promiseToFuture( + callMethod(engineInitializer, 'initializeEngine', []), + ); expect(appInitializer, isA()); - final Object maybeApp = await promiseToFuture(callMethod( - appInitializer, - 'runApp', - [] - )); + final Object maybeApp = await promiseToFuture( + callMethod(appInitializer, 'runApp', []), + ); expect(maybeApp, isA()); expect(initCalled, 1, reason: 'initEngine should have been called.'); expect(runCalled, 2, reason: 'runApp should have been called.'); @@ -108,62 +95,41 @@ void testMain() { group('FlutterApp', () { test('has addView/removeView methods', () async { - final AppBootstrap bootstrap = AppBootstrap( - initializeEngine: mockInit, - runApp: mockRunApp, - ); + final AppBootstrap bootstrap = AppBootstrap(initializeEngine: mockInit, runApp: mockRunApp); final FlutterEngineInitializer engineInitializer = bootstrap.prepareEngineInitializer(); - final Object appInitializer = await promiseToFuture(callMethod( - engineInitializer, - 'initializeEngine', - [] - )); - final Object maybeApp = await promiseToFuture(callMethod( - appInitializer, - 'runApp', - [] - )); + final Object appInitializer = await promiseToFuture( + callMethod(engineInitializer, 'initializeEngine', []), + ); + final Object maybeApp = await promiseToFuture( + callMethod(appInitializer, 'runApp', []), + ); expect(maybeApp, isA()); expect(getJsProperty(maybeApp, 'addView'), isA()); expect(getJsProperty(maybeApp, 'removeView'), isA()); }); test('addView/removeView respectively adds/removes view', () async { - final AppBootstrap bootstrap = AppBootstrap( - initializeEngine: mockInit, - runApp: mockRunApp, - ); + final AppBootstrap bootstrap = AppBootstrap(initializeEngine: mockInit, runApp: mockRunApp); final FlutterEngineInitializer engineInitializer = bootstrap.prepareEngineInitializer(); - final Object appInitializer = await promiseToFuture(callMethod( - engineInitializer, - 'initializeEngine', - [jsify({ - 'multiViewEnabled': true, - })] - )); - final Object maybeApp = await promiseToFuture(callMethod( - appInitializer, - 'runApp', - [] - )); - final int viewId = callMethod( - maybeApp, - 'addView', - [jsify({ - 'hostElement': createDomElement('div'), - })] - ).toInt(); + final Object appInitializer = await promiseToFuture( + callMethod(engineInitializer, 'initializeEngine', [ + jsify({'multiViewEnabled': true}), + ]), + ); + final Object maybeApp = await promiseToFuture( + callMethod(appInitializer, 'runApp', []), + ); + final int viewId = + callMethod(maybeApp, 'addView', [ + jsify({'hostElement': createDomElement('div')}), + ]).toInt(); expect(bootstrap.viewManager[viewId], isNotNull); - callMethod( - maybeApp, - 'removeView', - [viewId] - ); + callMethod(maybeApp, 'removeView', [viewId]); expect(bootstrap.viewManager[viewId], isNull); }); }); diff --git a/lib/web_ui/test/engine/assets_test.dart b/lib/web_ui/test/engine/assets_test.dart index bb1b0080b4aec..4cc0da24fabf0 100644 --- a/lib/web_ui/test/engine/assets_test.dart +++ b/lib/web_ui/test/engine/assets_test.dart @@ -56,10 +56,7 @@ void testMain() { assetBase: 'https://www.gstatic.com/my-app/', ); - expect( - assets.getAssetUrl('asset.txt'), - 'https://www.gstatic.com/my-app/assets/asset.txt', - ); + expect(assets.getAssetUrl('asset.txt'), 'https://www.gstatic.com/my-app/assets/asset.txt'); }); test('assetBase in conjunction with assetsDir, fully custom paths', () { @@ -106,10 +103,7 @@ void testMain() { test('reads value from DOM (only once!)', () { final ui_web.AssetManager firstManager = ui_web.AssetManager(); - expect( - firstManager.getAssetUrl('asset.txt'), - '/dom/base/assets/asset.txt', - ); + expect(firstManager.getAssetUrl('asset.txt'), '/dom/base/assets/asset.txt'); removeAssetBaseMeta(); final ui_web.AssetManager anotherManager = ui_web.AssetManager(); @@ -126,18 +120,17 @@ void testMain() { /// Removes all meta-tags with name=assetBase. void removeAssetBaseMeta() { - domWindow.document - .querySelectorAll('meta[name=assetBase]') - .forEach((DomElement element) { + domWindow.document.querySelectorAll('meta[name=assetBase]').forEach((DomElement element) { element.remove(); }); } /// Adds a meta-tag with name=assetBase and the passed-in [value]. void addAssetBaseMeta(String value) { - final DomHTMLMetaElement meta = createDomHTMLMetaElement() - ..name = 'assetBase' - ..content = value; + final DomHTMLMetaElement meta = + createDomHTMLMetaElement() + ..name = 'assetBase' + ..content = value; domDocument.head!.append(meta); } diff --git a/lib/web_ui/test/engine/browser_detect_test.dart b/lib/web_ui/test/engine/browser_detect_test.dart index ea4e9dcb463d1..eab24decd758d 100644 --- a/lib/web_ui/test/engine/browser_detect_test.dart +++ b/lib/web_ui/test/engine/browser_detect_test.dart @@ -15,26 +15,29 @@ void testMain() { test('Should detect Blink', () { // Chrome Version 89.0.4389.90 (Official Build) (x86_64) / MacOS final ui_web.BrowserEngine browserEngine = ui_web.browser.detectBrowserEngineByVendorAgent( - 'Google Inc.', - 'mozilla/5.0 (macintosh; intel mac os x 11_2_3) applewebkit/537.36 ' - '(khtml, like gecko) chrome/89.0.4389.90 safari/537.36'); + 'Google Inc.', + 'mozilla/5.0 (macintosh; intel mac os x 11_2_3) applewebkit/537.36 ' + '(khtml, like gecko) chrome/89.0.4389.90 safari/537.36', + ); expect(browserEngine, ui_web.BrowserEngine.blink); }); test('Should detect Firefox', () { // 85.0.2 (64-bit) / MacOS final ui_web.BrowserEngine browserEngine = ui_web.browser.detectBrowserEngineByVendorAgent( - '', - 'mozilla/5.0 (macintosh; intel mac os x 10.16; rv:85.0) ' - 'gecko/20100101 firefox/85.0'); + '', + 'mozilla/5.0 (macintosh; intel mac os x 10.16; rv:85.0) ' + 'gecko/20100101 firefox/85.0', + ); expect(browserEngine, ui_web.BrowserEngine.firefox); }); test('Should detect Safari', () { final ui_web.BrowserEngine browserEngine = ui_web.browser.detectBrowserEngineByVendorAgent( - 'Apple Computer, Inc.', - 'mozilla/5.0 (macintosh; intel mac os x 10_15_6) applewebkit/605.1.15 ' - '(khtml, like gecko) version/14.0.3 safari/605.1.15'); + 'Apple Computer, Inc.', + 'mozilla/5.0 (macintosh; intel mac os x 10_15_6) applewebkit/605.1.15 ' + '(khtml, like gecko) version/14.0.3 safari/605.1.15', + ); expect(browserEngine, ui_web.BrowserEngine.webkit); }); }); @@ -65,96 +68,45 @@ void testMain() { }); test('Determine MacOS if platform starts by Mac', () { - expectOs( - ui_web.OperatingSystem.macOs, - platform: 'MacIntel', - ); - expectOs( - ui_web.OperatingSystem.macOs, - platform: 'MacAnythingElse', - ); + expectOs(ui_web.OperatingSystem.macOs, platform: 'MacIntel'); + expectOs(ui_web.OperatingSystem.macOs, platform: 'MacAnythingElse'); }); test('Determine iOS if platform contains iPhone/iPad/iPod', () { - expectOs( - ui_web.OperatingSystem.iOs, - platform: 'iPhone', - ); - expectOs( - ui_web.OperatingSystem.iOs, - platform: 'iPhone Simulator', - ); - expectOs( - ui_web.OperatingSystem.iOs, - platform: 'iPad', - ); - expectOs( - ui_web.OperatingSystem.iOs, - platform: 'iPad Simulator', - ); - expectOs( - ui_web.OperatingSystem.iOs, - platform: 'iPod', - ); - expectOs( - ui_web.OperatingSystem.iOs, - platform: 'iPod Simulator', - ); + expectOs(ui_web.OperatingSystem.iOs, platform: 'iPhone'); + expectOs(ui_web.OperatingSystem.iOs, platform: 'iPhone Simulator'); + expectOs(ui_web.OperatingSystem.iOs, platform: 'iPad'); + expectOs(ui_web.OperatingSystem.iOs, platform: 'iPad Simulator'); + expectOs(ui_web.OperatingSystem.iOs, platform: 'iPod'); + expectOs(ui_web.OperatingSystem.iOs, platform: 'iPod Simulator'); }); // See https://github.com/flutter/flutter/issues/81918 test('Tell apart MacOS from iOS requesting a desktop site.', () { - expectOs( - ui_web.OperatingSystem.macOs, - platform: 'MacARM', - ); + expectOs(ui_web.OperatingSystem.macOs, platform: 'MacARM'); - expectOs( - ui_web.OperatingSystem.iOs, - platform: 'MacARM', - touchPoints: 5, - ); + expectOs(ui_web.OperatingSystem.iOs, platform: 'MacARM', touchPoints: 5); }); test('Determine Android if user agent contains Android', () { expectOs( ui_web.OperatingSystem.android, - ua: 'Mozilla/5.0 (Linux; U; Android 2.2) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1', + ua: + 'Mozilla/5.0 (Linux; U; Android 2.2) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1', ); }); test('Determine Linux if the platform begins with Linux', () { - expectOs( - ui_web.OperatingSystem.linux, - platform: 'Linux', - ); - expectOs( - ui_web.OperatingSystem.linux, - platform: 'Linux armv8l', - ); - expectOs( - ui_web.OperatingSystem.linux, - platform: 'Linux x86_64', - ); + expectOs(ui_web.OperatingSystem.linux, platform: 'Linux'); + expectOs(ui_web.OperatingSystem.linux, platform: 'Linux armv8l'); + expectOs(ui_web.OperatingSystem.linux, platform: 'Linux x86_64'); }); test('Determine Windows if the platform begins with Win', () { - expectOs( - ui_web.OperatingSystem.windows, - platform: 'Windows', - ); - expectOs( - ui_web.OperatingSystem.windows, - platform: 'Win32', - ); - expectOs( - ui_web.OperatingSystem.windows, - platform: 'Win16', - ); - expectOs( - ui_web.OperatingSystem.windows, - platform: 'WinCE', - ); + expectOs(ui_web.OperatingSystem.windows, platform: 'Windows'); + expectOs(ui_web.OperatingSystem.windows, platform: 'Win32'); + expectOs(ui_web.OperatingSystem.windows, platform: 'Win16'); + expectOs(ui_web.OperatingSystem.windows, platform: 'WinCE'); }); }); } diff --git a/lib/web_ui/test/engine/channel_buffers_test.dart b/lib/web_ui/test/engine/channel_buffers_test.dart index 7bc79d3397957..2ce189229878b 100644 --- a/lib/web_ui/test/engine/channel_buffers_test.dart +++ b/lib/web_ui/test/engine/channel_buffers_test.dart @@ -41,8 +41,12 @@ void testMain() { void callback(ByteData? responseData) { called = true; } + buffers.push(channel, data, callback); - await buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) { + await buffers.drain(channel, ( + ByteData? drainedData, + ui.PlatformMessageResponseCallback drainedCallback, + ) { expect(drainedData, equals(data)); assert(!called); drainedCallback(drainedData); @@ -59,13 +63,18 @@ void testMain() { buffers.push(channel, data, callback); final List log = []; final Completer completer = Completer(); - scheduleMicrotask(() { log.add('before drain, microtask'); }); + scheduleMicrotask(() { + log.add('before drain, microtask'); + }); log.add('before drain'); // Ignoring the returned future because the completion of the drain is // communicated using the `completer`. // ignore: unawaited_futures - buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) async { + buffers.drain(channel, ( + ByteData? drainedData, + ui.PlatformMessageResponseCallback drainedCallback, + ) async { log.add('callback'); completer.complete(); }); @@ -77,20 +86,22 @@ void testMain() { 'callback', 'after drain, before await', 'before drain, microtask', - 'after await' + 'after await', ]); }); test('push drain zero', () async { const String channel = 'foo'; final ByteData data = _makeByteData('bar'); - final - ui.ChannelBuffers buffers = ui.ChannelBuffers(); + final ui.ChannelBuffers buffers = ui.ChannelBuffers(); void callback(ByteData? responseData) {} _resize(buffers, channel, 0); buffers.push(channel, data, callback); bool didCall = false; - await buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) { + await buffers.drain(channel, ( + ByteData? drainedData, + ui.PlatformMessageResponseCallback drainedCallback, + ) { didCall = true; return Future.value(); }); @@ -101,7 +112,10 @@ void testMain() { const String channel = 'foo'; final ui.ChannelBuffers buffers = ui.ChannelBuffers(); bool didCall = false; - await buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) { + await buffers.drain(channel, ( + ByteData? drainedData, + ui.PlatformMessageResponseCallback drainedCallback, + ) { didCall = true; return Future.value(); }); @@ -122,7 +136,10 @@ void testMain() { buffers.push(channel, three, callback); buffers.push(channel, four, callback); int counter = 0; - await buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) { + await buffers.drain(channel, ( + ByteData? drainedData, + ui.PlatformMessageResponseCallback drainedCallback, + ) { switch (counter) { case 0: expect(drainedData, equals(two)); @@ -148,7 +165,10 @@ void testMain() { buffers.push(channel, two, callback); _resize(buffers, channel, 1); int counter = 0; - await buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) { + await buffers.drain(channel, ( + ByteData? drainedData, + ui.PlatformMessageResponseCallback drainedCallback, + ) { switch (counter) { case 0: expect(drainedData, equals(two)); @@ -169,9 +189,11 @@ void testMain() { expect(responseData, isNull); didCallCallback = true; } + void twoCallback(ByteData? responseData) { throw TestFailure('wrong callback called'); // ignore: only_throw_errors } + _resize(buffers, channel, 100); buffers.push(channel, one, oneCallback); buffers.push(channel, two, twoCallback); @@ -190,9 +212,11 @@ void testMain() { expect(responseData, isNull); didCallCallback = true; } + void twoCallback(ByteData? responseData) { throw TestFailure('wrong callback called'); // ignore: only_throw_errors } + _resize(buffers, channel, 1); buffers.push(channel, one, oneCallback); buffers.push(channel, two, twoCallback); @@ -201,14 +225,12 @@ void testMain() { test('handle garbage', () async { final ui.ChannelBuffers buffers = ui.ChannelBuffers(); - expect(() => buffers.handleMessage(_makeByteData('asdfasdf')), - throwsException); + expect(() => buffers.handleMessage(_makeByteData('asdfasdf')), throwsException); }); test('handle resize garbage', () async { final ui.ChannelBuffers buffers = ui.ChannelBuffers(); - expect(() => buffers.handleMessage(_makeByteData('resize\rfoo\rbar')), - throwsException); + expect(() => buffers.handleMessage(_makeByteData('resize\rfoo\rbar')), throwsException); }); test('ChannelBuffers.setListener', () async { @@ -221,9 +243,9 @@ void testMain() { final ByteData five = _makeByteData('five'); final ByteData six = _makeByteData('six'); final ByteData seven = _makeByteData('seven'); - buffers.push('a', one, (ByteData? data) { }); - buffers.push('b', two, (ByteData? data) { }); - buffers.push('a', three, (ByteData? data) { }); + buffers.push('a', one, (ByteData? data) {}); + buffers.push('b', two, (ByteData? data) {}); + buffers.push('a', three, (ByteData? data) {}); log.add('top'); buffers.setListener('a', (ByteData? data, ui.PlatformMessageResponseCallback callback) { log.add('a1: ${utf8.decode(data!.buffer.asUint8List())}'); @@ -244,14 +266,14 @@ void testMain() { await null; // first microtask after setting listener drains the first message await null; // second microtask ends the draining. log.add('-6'); - buffers.push('b', four, (ByteData? data) { }); - buffers.push('a', five, (ByteData? data) { }); + buffers.push('b', four, (ByteData? data) {}); + buffers.push('a', five, (ByteData? data) {}); log.add('-7'); await null; log.add('-8'); buffers.clearListener('a'); - buffers.push('a', six, (ByteData? data) { }); - buffers.push('b', seven, (ByteData? data) { }); + buffers.push('a', six, (ByteData? data) {}); + buffers.push('b', seven, (ByteData? data) {}); await null; log.add('-9'); expect(log, [ @@ -281,9 +303,9 @@ void testMain() { final ByteData three = _makeByteData('three'); final ByteData four = _makeByteData('four'); buffers.handleMessage(_makeByteData('resize\ra\r10')); - buffers.push('a', one, (ByteData? data) { }); - buffers.push('a', two, (ByteData? data) { }); - buffers.push('a', three, (ByteData? data) { }); + buffers.push('a', one, (ByteData? data) {}); + buffers.push('a', two, (ByteData? data) {}); + buffers.push('a', three, (ByteData? data) {}); log.add('-1'); buffers.setListener('a', (ByteData? data, ui.PlatformMessageResponseCallback callback) { log.add('a1: ${utf8.decode(data!.buffer.asUint8List())}'); @@ -298,7 +320,7 @@ void testMain() { }); log.add('-4'); await null; - buffers.push('a', four, (ByteData? data) { }); + buffers.push('a', four, (ByteData? data) {}); log.add('-5'); await null; log.add('-6'); @@ -326,7 +348,52 @@ void testMain() { // Created as follows: // print(StandardMethodCodec().encodeMethodCall(MethodCall('resize', ['abcdef', 12345])).buffer.asUint8List()); // ...with three 0xFF bytes on either side to ensure the method works with an offset on the underlying buffer. - buffers.handleMessage(ByteData.sublistView(Uint8List.fromList([255, 255, 255, 7, 6, 114, 101, 115, 105, 122, 101, 12, 2, 7, 6, 97, 98, 99, 100, 101, 102, 3, 57, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255]), 3, 27)); + buffers.handleMessage( + ByteData.sublistView( + Uint8List.fromList([ + 255, + 255, + 255, + 7, + 6, + 114, + 101, + 115, + 105, + 122, + 101, + 12, + 2, + 7, + 6, + 97, + 98, + 99, + 100, + 101, + 102, + 3, + 57, + 48, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 255, + 255, + 255, + ]), + 3, + 27, + ), + ); expect(log, const ['resize abcdef 12345']); }); @@ -336,7 +403,52 @@ void testMain() { // Created as follows: // print(StandardMethodCodec().encodeMethodCall(MethodCall('overflow', ['abcdef', false])).buffer.asUint8List()); // ...with three 0xFF bytes on either side to ensure the method works with an offset on the underlying buffer. - buffers.handleMessage(ByteData.sublistView(Uint8List.fromList([255, 255, 255, 7, 8, 111, 118, 101, 114, 102, 108, 111, 119, 12, 2, 7, 6, 97, 98, 99, 100, 101, 102, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255]), 3, 24)); + buffers.handleMessage( + ByteData.sublistView( + Uint8List.fromList([ + 255, + 255, + 255, + 7, + 8, + 111, + 118, + 101, + 114, + 102, + 108, + 111, + 119, + 12, + 2, + 7, + 6, + 97, + 98, + 99, + 100, + 101, + 102, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 255, + 255, + 255, + ]), + 3, + 24, + ), + ); expect(log, const ['allowOverflow abcdef false']); }); diff --git a/lib/web_ui/test/engine/clipboard_test.dart b/lib/web_ui/test/engine/clipboard_test.dart index 61357850cc635..89d078513ce0b 100644 --- a/lib/web_ui/test/engine/clipboard_test.dart +++ b/lib/web_ui/test/engine/clipboard_test.dart @@ -21,19 +21,15 @@ Future testMain() async { const String testText = 'test text'; late ClipboardMessageHandler clipboardMessageHandler; - MockClipboardAPICopyStrategy clipboardAPICopyStrategy = - MockClipboardAPICopyStrategy(); - MockClipboardAPIPasteStrategy clipboardAPIPasteStrategy = - MockClipboardAPIPasteStrategy(); + MockClipboardAPICopyStrategy clipboardAPICopyStrategy = MockClipboardAPICopyStrategy(); + MockClipboardAPIPasteStrategy clipboardAPIPasteStrategy = MockClipboardAPIPasteStrategy(); setUp(() { clipboardMessageHandler = ClipboardMessageHandler(); clipboardAPICopyStrategy = MockClipboardAPICopyStrategy(); clipboardAPIPasteStrategy = MockClipboardAPIPasteStrategy(); - clipboardMessageHandler.copyToClipboardStrategy = - clipboardAPICopyStrategy; - clipboardMessageHandler.pasteFromClipboardStrategy = - clipboardAPIPasteStrategy; + clipboardMessageHandler.copyToClipboardStrategy = clipboardAPICopyStrategy; + clipboardMessageHandler.pasteFromClipboardStrategy = clipboardAPIPasteStrategy; }); test('set data successful', () async { @@ -45,10 +41,9 @@ Future testMain() async { } clipboardMessageHandler.setDataMethodCall( - const MethodCall('Clipboard.setData', { - 'text': testText, - }), - callback); + const MethodCall('Clipboard.setData', {'text': testText}), + callback, + ); expect(await completer.future, isTrue); }); @@ -62,16 +57,21 @@ Future testMain() async { } clipboardMessageHandler.setDataMethodCall( - const MethodCall('Clipboard.setData', { - 'text': testText, - }), - callback); + const MethodCall('Clipboard.setData', {'text': testText}), + callback, + ); final ByteData result = await completer.future; expect( - () =>codec.decodeEnvelope(result), - throwsA(const TypeMatcher() - .having((PlatformException e) => e.code, 'code', equals('copy_fail')))); + () => codec.decodeEnvelope(result), + throwsA( + const TypeMatcher().having( + (PlatformException e) => e.code, + 'code', + equals('copy_fail'), + ), + ), + ); }); test('get data successful', () async { diff --git a/lib/web_ui/test/engine/composition_test.dart b/lib/web_ui/test/engine/composition_test.dart index 57451e0751613..2c30a44ad46ef 100644 --- a/lib/web_ui/test/engine/composition_test.dart +++ b/lib/web_ui/test/engine/composition_test.dart @@ -31,15 +31,12 @@ DomHTMLInputElement get _inputElement { } GloballyPositionedTextEditingStrategy _enableEditingStrategy({ - required bool deltaModel, - void Function(EditingState?, TextEditingDeltaState?)? onChange, - }) { + required bool deltaModel, + void Function(EditingState?, TextEditingDeltaState?)? onChange, +}) { final HybridTextEditing owner = HybridTextEditing(); - owner.configuration = InputConfiguration( - viewId: kImplicitViewId, - enableDeltaModel: deltaModel, - ); + owner.configuration = InputConfiguration(viewId: kImplicitViewId, enableDeltaModel: deltaModel); final GloballyPositionedTextEditingStrategy editingStrategy = GloballyPositionedTextEditingStrategy(owner); @@ -73,7 +70,9 @@ Future testMain() async { mockWithCompositionAwareMixin.composingText = fakeComposingText; mockWithCompositionAwareMixin.addCompositionEventHandlers(_inputElement); - _inputElement.dispatchEvent(createDomEvent('Event', _MockWithCompositionAwareMixin._kCompositionEnd)); + _inputElement.dispatchEvent( + createDomEvent('Event', _MockWithCompositionAwareMixin._kCompositionEnd), + ); expect(mockWithCompositionAwareMixin.composingText, null); }); @@ -86,7 +85,9 @@ Future testMain() async { mockWithCompositionAwareMixin.composingText = fakeComposingText; mockWithCompositionAwareMixin.addCompositionEventHandlers(_inputElement); - _inputElement.dispatchEvent(createDomEvent('Event', _MockWithCompositionAwareMixin._kCompositionStart)); + _inputElement.dispatchEvent( + createDomEvent('Event', _MockWithCompositionAwareMixin._kCompositionStart), + ); expect(mockWithCompositionAwareMixin.composingText, null); }); @@ -100,10 +101,12 @@ Future testMain() async { mockWithCompositionAwareMixin.composingText = fakeComposingText; mockWithCompositionAwareMixin.addCompositionEventHandlers(_inputElement); - _inputElement.dispatchEvent(createDomCompositionEvent( + _inputElement.dispatchEvent( + createDomCompositionEvent( _MockWithCompositionAwareMixin._kCompositionUpdate, - { 'data': fakeEventText } - )); + {'data': fakeEventText}, + ), + ); expect(mockWithCompositionAwareMixin.composingText, fakeEventText); }); @@ -117,10 +120,7 @@ Future testMain() async { _MockWithCompositionAwareMixin(); mockWithCompositionAwareMixin.composingText = 'Test'; - expect( - mockWithCompositionAwareMixin.determineCompositionState(editingState), - editingState, - ); + expect(mockWithCompositionAwareMixin.determineCompositionState(editingState), editingState); }); test('should return editing state if composingText is null', () { @@ -133,31 +133,20 @@ Future testMain() async { final _MockWithCompositionAwareMixin mockWithCompositionAwareMixin = _MockWithCompositionAwareMixin(); - expect( - mockWithCompositionAwareMixin.determineCompositionState(editingState), - editingState, - ); + expect(mockWithCompositionAwareMixin.determineCompositionState(editingState), editingState); }); test('should return editing state if text is null', () { - final EditingState editingState = EditingState( - baseOffset: 0, - extentOffset: 0, - ); + final EditingState editingState = EditingState(baseOffset: 0, extentOffset: 0); final _MockWithCompositionAwareMixin mockWithCompositionAwareMixin = _MockWithCompositionAwareMixin(); mockWithCompositionAwareMixin.composingText = 'Test'; - expect( - mockWithCompositionAwareMixin.determineCompositionState(editingState), - editingState, - ); + expect(mockWithCompositionAwareMixin.determineCompositionState(editingState), editingState); }); - test( - 'should return editing state if extentOffset is smaller than composingText length', - () { + test('should return editing state if extentOffset is smaller than composingText length', () { const String composingText = 'composeMe'; final EditingState editingState = EditingState( @@ -170,14 +159,10 @@ Future testMain() async { _MockWithCompositionAwareMixin(); mockWithCompositionAwareMixin.composingText = composingText; - expect( - mockWithCompositionAwareMixin.determineCompositionState(editingState), - editingState, - ); + expect(mockWithCompositionAwareMixin.determineCompositionState(editingState), editingState); }); - test('should return new composition state - compositing middle of text', - () { + test('should return new composition state - compositing middle of text', () { const int baseOffset = 7; const String composingText = 'Test'; @@ -202,16 +187,10 @@ Future testMain() async { ); }); - test( - 'should return new composition state - compositing from beginning of text', - () { + test('should return new composition state - compositing from beginning of text', () { const String composingText = '今日は'; - final EditingState editingState = EditingState( - text: '今日は', - baseOffset: 0, - extentOffset: 3, - ); + final EditingState editingState = EditingState(text: '今日は', baseOffset: 0, extentOffset: 3); final _MockWithCompositionAwareMixin mockWithCompositionAwareMixin = _MockWithCompositionAwareMixin(); @@ -220,12 +199,12 @@ Future testMain() async { const int expectedComposingBase = 0; expect( - mockWithCompositionAwareMixin - .determineCompositionState(editingState), - editingState.copyWith( - composingBaseOffset: expectedComposingBase, - composingExtentOffset: - expectedComposingBase + composingText.length)); + mockWithCompositionAwareMixin.determineCompositionState(editingState), + editingState.copyWith( + composingBaseOffset: expectedComposingBase, + composingExtentOffset: expectedComposingBase + composingText.length, + ), + ); }); }); }); @@ -244,50 +223,70 @@ Future testMain() async { test('should be [0, compostionStrLength] on new composition', () { const String composingText = 'hi'; - _inputElement.dispatchEvent(createDomCompositionEvent( - _MockWithCompositionAwareMixin._kCompositionUpdate, - {'data': composingText})); + _inputElement.dispatchEvent( + createDomCompositionEvent( + _MockWithCompositionAwareMixin._kCompositionUpdate, + {'data': composingText}, + ), + ); // Set the selection text. _inputElement.value = composingText; _inputElement.dispatchEvent(createDomEvent('Event', 'input')); expect( - editingStrategy.lastEditingState, - isA() - .having((EditingState editingState) => editingState.composingBaseOffset, - 'composingBaseOffset', 0) - .having((EditingState editingState) => editingState.composingExtentOffset, - 'composingExtentOffset', composingText.length)); + editingStrategy.lastEditingState, + isA() + .having( + (EditingState editingState) => editingState.composingBaseOffset, + 'composingBaseOffset', + 0, + ) + .having( + (EditingState editingState) => editingState.composingExtentOffset, + 'composingExtentOffset', + composingText.length, + ), + ); }); test( - 'should be [beforeComposingText - composingText, compostionStrLength] on composition in the middle of text', - () { - const String composingText = 'hi'; - const String beforeComposingText = 'beforeComposingText'; - const String afterComposingText = 'afterComposingText'; - - // Type in the text box, then move cursor to the middle. - _inputElement.value = '$beforeComposingText$afterComposingText'; - _inputElement.setSelectionRange(beforeComposingText.length, beforeComposingText.length); - - _inputElement.dispatchEvent(createDomCompositionEvent( - _MockWithCompositionAwareMixin._kCompositionUpdate, - { 'data': composingText } - )); + 'should be [beforeComposingText - composingText, compostionStrLength] on composition in the middle of text', + () { + const String composingText = 'hi'; + const String beforeComposingText = 'beforeComposingText'; + const String afterComposingText = 'afterComposingText'; + + // Type in the text box, then move cursor to the middle. + _inputElement.value = '$beforeComposingText$afterComposingText'; + _inputElement.setSelectionRange(beforeComposingText.length, beforeComposingText.length); + + _inputElement.dispatchEvent( + createDomCompositionEvent( + _MockWithCompositionAwareMixin._kCompositionUpdate, + {'data': composingText}, + ), + ); - // Flush editing state (since we did not compositionend). - _inputElement.dispatchEvent(createDomEvent('Event', 'input')); + // Flush editing state (since we did not compositionend). + _inputElement.dispatchEvent(createDomEvent('Event', 'input')); - expect( + expect( editingStrategy.lastEditingState, isA() - .having((EditingState editingState) => editingState.composingBaseOffset, - 'composingBaseOffset', beforeComposingText.length - composingText.length) - .having((EditingState editingState) => editingState.composingExtentOffset, - 'composingExtentOffset', beforeComposingText.length)); - }); + .having( + (EditingState editingState) => editingState.composingBaseOffset, + 'composingBaseOffset', + beforeComposingText.length - composingText.length, + ) + .having( + (EditingState editingState) => editingState.composingExtentOffset, + 'composingExtentOffset', + beforeComposingText.length, + ), + ); + }, + ); }); group('Text Editing Delta Model', () { @@ -297,10 +296,10 @@ Future testMain() async { StreamController.broadcast(); setUp(() { - editingStrategy = _enableEditingStrategy( - deltaModel: true, - onChange: (_, TextEditingDeltaState? deltaState) => deltaStream.add(deltaState) - ); + editingStrategy = _enableEditingStrategy( + deltaModel: true, + onChange: (_, TextEditingDeltaState? deltaState) => deltaStream.add(deltaState), + ); }); tearDown(() { @@ -310,19 +309,33 @@ Future testMain() async { test('should have newly entered composing characters', () async { const String newComposingText = 'n'; - editingStrategy.setEditingState(EditingState(text: newComposingText, baseOffset: 1, extentOffset: 1)); + editingStrategy.setEditingState( + EditingState(text: newComposingText, baseOffset: 1, extentOffset: 1), + ); final Future containExpect = expectLater( - deltaStream.stream.first, - completion(isA() - .having((TextEditingDeltaState deltaState) => deltaState.composingOffset, 'composingOffset', 0) - .having((TextEditingDeltaState deltaState) => deltaState.composingExtent, 'composingExtent', newComposingText.length) - )); - - - _inputElement.dispatchEvent(createDomCompositionEvent( + deltaStream.stream.first, + completion( + isA() + .having( + (TextEditingDeltaState deltaState) => deltaState.composingOffset, + 'composingOffset', + 0, + ) + .having( + (TextEditingDeltaState deltaState) => deltaState.composingExtent, + 'composingExtent', + newComposingText.length, + ), + ), + ); + + _inputElement.dispatchEvent( + createDomCompositionEvent( _MockWithCompositionAwareMixin._kCompositionUpdate, - { 'data': newComposingText })); + {'data': newComposingText}, + ), + ); // On Chrome and Safari, a `compositionupdate` event automatically // triggers a `selectionchange` event, which leads to triggering // `DefaultTextEditingStrategy.handleChange`. @@ -335,34 +348,61 @@ Future testMain() async { await containExpect; }); - test('should emit changed composition', () async { - const String newComposingCharsInOrder = 'hiCompose'; - - for (int currCharIndex = 0; currCharIndex < newComposingCharsInOrder.length; currCharIndex++) { - final String currComposingSubstr = newComposingCharsInOrder.substring(0, currCharIndex + 1); - - editingStrategy.setEditingState( - EditingState(text: currComposingSubstr, baseOffset: currCharIndex + 1, extentOffset: currCharIndex + 1) - ); - - final Future containExpect = expectLater( - deltaStream.stream.first, - completion(isA() - .having((TextEditingDeltaState deltaState) => deltaState.composingOffset, 'composingOffset', 0) - .having((TextEditingDeltaState deltaState) => deltaState.composingExtent, 'composingExtent', currCharIndex + 1) - )); - - _inputElement.dispatchEvent(createDomCompositionEvent( - _MockWithCompositionAwareMixin._kCompositionUpdate, - { 'data': currComposingSubstr })); - - await containExpect; - } - }, - // TODO(antholeole): This test fails on Firefox because of how it orders events; - // it's likely that this will be fixed by https://github.com/flutter/flutter/issues/105243. - // Until the refactor gets merged, this test should run on all other browsers to prevent - // regressions in the meantime. - skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.firefox); + test( + 'should emit changed composition', + () async { + const String newComposingCharsInOrder = 'hiCompose'; + + for ( + int currCharIndex = 0; + currCharIndex < newComposingCharsInOrder.length; + currCharIndex++ + ) { + final String currComposingSubstr = newComposingCharsInOrder.substring( + 0, + currCharIndex + 1, + ); + + editingStrategy.setEditingState( + EditingState( + text: currComposingSubstr, + baseOffset: currCharIndex + 1, + extentOffset: currCharIndex + 1, + ), + ); + + final Future containExpect = expectLater( + deltaStream.stream.first, + completion( + isA() + .having( + (TextEditingDeltaState deltaState) => deltaState.composingOffset, + 'composingOffset', + 0, + ) + .having( + (TextEditingDeltaState deltaState) => deltaState.composingExtent, + 'composingExtent', + currCharIndex + 1, + ), + ), + ); + + _inputElement.dispatchEvent( + createDomCompositionEvent( + _MockWithCompositionAwareMixin._kCompositionUpdate, + {'data': currComposingSubstr}, + ), + ); + + await containExpect; + } + }, + // TODO(antholeole): This test fails on Firefox because of how it orders events; + // it's likely that this will be fixed by https://github.com/flutter/flutter/issues/105243. + // Until the refactor gets merged, this test should run on all other browsers to prevent + // regressions in the meantime. + skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.firefox, + ); }); } diff --git a/lib/web_ui/test/engine/configuration_test.dart b/lib/web_ui/test/engine/configuration_test.dart index 07910d693a781..ebfc250917f22 100644 --- a/lib/web_ui/test/engine/configuration_test.dart +++ b/lib/web_ui/test/engine/configuration_test.dart @@ -27,9 +27,9 @@ void testMain() { test('legacy constructor initializes with a Js Object', () async { final FlutterConfiguration config = FlutterConfiguration.legacy( - js_util.jsify({ - 'canvasKitBaseUrl': '/some_other_url/', - }) as JsFlutterConfiguration); + js_util.jsify({'canvasKitBaseUrl': '/some_other_url/'}) + as JsFlutterConfiguration, + ); expect(config.canvasKitBaseUrl, '/some_other_url/'); }); @@ -38,15 +38,15 @@ void testMain() { group('setUserConfiguration', () { test('throws assertion error if already initialized from JS', () async { final FlutterConfiguration config = FlutterConfiguration.legacy( - js_util.jsify({ - 'canvasKitBaseUrl': '/some_other_url/', - }) as JsFlutterConfiguration); + js_util.jsify({'canvasKitBaseUrl': '/some_other_url/'}) + as JsFlutterConfiguration, + ); expect(() { config.setUserConfiguration( - js_util.jsify({ - 'canvasKitBaseUrl': '/yet_another_url/', - }) as JsFlutterConfiguration); + js_util.jsify({'canvasKitBaseUrl': '/yet_another_url/'}) + as JsFlutterConfiguration, + ); }, throwsAssertionError); }); @@ -54,9 +54,9 @@ void testMain() { final FlutterConfiguration config = FlutterConfiguration.legacy(null); config.setUserConfiguration( - js_util.jsify({ - 'canvasKitBaseUrl': '/one_more_url/', - }) as JsFlutterConfiguration); + js_util.jsify({'canvasKitBaseUrl': '/one_more_url/'}) + as JsFlutterConfiguration, + ); expect(config.canvasKitBaseUrl, '/one_more_url/'); }); @@ -66,9 +66,8 @@ void testMain() { expect(() { config.setUserConfiguration( - js_util.jsify({ - 'nonexistentProperty': 32.0, - }) as JsFlutterConfiguration); + js_util.jsify({'nonexistentProperty': 32.0}) as JsFlutterConfiguration, + ); }, returnsNormally); }); }); @@ -89,7 +88,6 @@ void testMain() { test('multiViewEnabled', () { expect(defaultConfig.multiViewEnabled, isFalse); }); - }); group('setUserConfiguration (values)', () { @@ -123,7 +121,8 @@ void testMain() { expect(config.canvasKitVariant, CanvasKitVariant.full); config.setUserConfiguration( - js_util.jsify({'canvasKitVariant': 'chromium'}) as JsFlutterConfiguration, + js_util.jsify({'canvasKitVariant': 'chromium'}) + as JsFlutterConfiguration, ); expect(config.canvasKitVariant, CanvasKitVariant.chromium); }); diff --git a/lib/web_ui/test/engine/dom_http_fetch_test.dart b/lib/web_ui/test/engine/dom_http_fetch_test.dart index ac2bf7ac33dda..41d6cb4db1a3d 100644 --- a/lib/web_ui/test/engine/dom_http_fetch_test.dart +++ b/lib/web_ui/test/engine/dom_http_fetch_test.dart @@ -27,10 +27,7 @@ Future testMain() async { await _testNetworkErrors(); test('window.fetch is banned', () async { - expect( - () => domWindow.fetch('/'), - throwsA(isA()), - ); + expect(() => domWindow.fetch('/'), throwsA(isA())); }); } @@ -58,10 +55,16 @@ Future _testSuccessfulPayloads() async { expect(response.hasPayload, isTrue); expect(response.payload, isNotNull); expect(response.url, '/test_images/1x1.png'); - expect( - (await response.asByteBuffer()).asUint8List().sublist(0, 8), - [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A], - ); + expect((await response.asByteBuffer()).asUint8List().sublist(0, 8), [ + 0x89, + 0x50, + 0x4E, + 0x47, + 0x0D, + 0x0A, + 0x1A, + 0x0A, + ]); }); test('httpFetch fetches a binary file as Uint8List', () async { @@ -71,10 +74,16 @@ Future _testSuccessfulPayloads() async { expect(response.hasPayload, isTrue); expect(response.payload, isNotNull); expect(response.url, '/test_images/1x1.png'); - expect( - (await response.asUint8List()).sublist(0, 8), - [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A], - ); + expect((await response.asUint8List()).sublist(0, 8), [ + 0x89, + 0x50, + 0x4E, + 0x47, + 0x0D, + 0x0A, + 0x1A, + 0x0A, + ]); }); test('httpFetch fetches json', () async { @@ -84,10 +93,7 @@ Future _testSuccessfulPayloads() async { expect(response.hasPayload, isTrue); expect(response.payload, isNotNull); expect(response.url, '/test_images/'); - expect( - await response.json(), - isA>(), - ); + expect(await response.json(), isA>()); }); test('httpFetch reads data in chunks', () async { @@ -113,12 +119,11 @@ Future _testSuccessfulPayloads() async { expect(response.url, url); final List result = []; - await response.payload.read((JSUint8Array chunk) => result.addAll(chunk.toDart)); - expect(result, hasLength(length)); - expect( - result, - List.generate(length, (int i) => i & 0xFF), + await response.payload.read( + (JSUint8Array chunk) => result.addAll(chunk.toDart), ); + expect(result, hasLength(length)); + expect(result, List.generate(length, (int i) => i & 0xFF)); } }); @@ -135,10 +140,16 @@ Future _testSuccessfulPayloads() async { test('httpFetchByteBuffer fetches a binary file as ByteBuffer', () async { final ByteBuffer response = await httpFetchByteBuffer('/test_images/1x1.png'); - expect( - response.asUint8List().sublist(0, 8), - [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A], - ); + expect(response.asUint8List().sublist(0, 8), [ + 0x89, + 0x50, + 0x4E, + 0x47, + 0x0D, + 0x0A, + 0x1A, + 0x0A, + ]); }); test('httpFetchJson fetches json', () async { @@ -159,7 +170,7 @@ Future _testHttpErrorCodes() async { // HttpFetchNoPayloadError thrown. response.payload; fail('Expected HttpFetchNoPayloadError'); - } on HttpFetchNoPayloadError catch(error) { + } on HttpFetchNoPayloadError catch (error) { expect(error.status, 404); expect(error.url, '/file_not_found'); expect( @@ -181,7 +192,7 @@ Future _testHttpErrorCodes() async { try { await testFunction(); fail('Expected HttpFetchNoPayloadError'); - } on HttpFetchNoPayloadError catch(error) { + } on HttpFetchNoPayloadError catch (error) { expect(error.status, 404); expect(error.url, '/file_not_found'); expect( @@ -210,7 +221,7 @@ Future _testNetworkErrors() async { try { await testFunction(); fail('Expected HttpFetchError'); - } on HttpFetchError catch(error) { + } on HttpFetchError catch (error) { expect(error.url, badUrl); expect( error.toString(), @@ -219,7 +230,7 @@ Future _testNetworkErrors() async { // not the entire error message. startsWith( 'Flutter Web engine failed to complete HTTP request to fetch ' - '"https://user:password@example.com/": TypeError: ' + '"https://user:password@example.com/": TypeError: ', ), ); } diff --git a/lib/web_ui/test/engine/frame_reference_test.dart b/lib/web_ui/test/engine/frame_reference_test.dart index 5e3c37ed1bc17..8901aafc734c4 100644 --- a/lib/web_ui/test/engine/frame_reference_test.dart +++ b/lib/web_ui/test/engine/frame_reference_test.dart @@ -62,8 +62,12 @@ void testMain() { final CrossFrameCache cache = CrossFrameCache(); final TestItem testItem1 = TestItem('item1'); final TestItem testItem2 = TestItem('item2'); - cache.cache(testItem1.label, testItem1, (TestItem item) {evictedItems.add(item);}); - cache.cache(testItem2.label, testItem2, (TestItem item) {evictedItems.add(item);}); + cache.cache(testItem1.label, testItem1, (TestItem item) { + evictedItems.add(item); + }); + cache.cache(testItem2.label, testItem2, (TestItem item) { + evictedItems.add(item); + }); cache.commitFrame(); expect(evictedItems.length, 0); cache.reuse('item2'); diff --git a/lib/web_ui/test/engine/geometry_test.dart b/lib/web_ui/test/engine/geometry_test.dart index d1a9d89580d76..14d4e3b6aa141 100644 --- a/lib/web_ui/test/engine/geometry_test.dart +++ b/lib/web_ui/test/engine/geometry_test.dart @@ -33,7 +33,7 @@ void testMain() { }); test('Offset.fromDirection', () { expect(Offset.fromDirection(0.0, 0.0), const Offset(0.0, 0.0)); - // aah, floating point math. i love you so. + // aah, floating point math. i love you so. expect(Offset.fromDirection(pi / 2.0), within(from: const Offset(0.0, 1.0))); expect(Offset.fromDirection(-pi / 2.0), within(from: const Offset(0.0, -1.0))); expect(Offset.fromDirection(0.0), const Offset(1.0, 0.0)); @@ -55,10 +55,7 @@ void testMain() { within(from: Offset(-1.0 / math.sqrt(2.0), -1.0 / math.sqrt(2.0))), ); expect(Offset.fromDirection(0.0, 2.0), const Offset(2.0, 0.0)); - expect( - Offset.fromDirection(pi / 6, 2.0), - within(from: Offset(math.sqrt(3.0), 1.0)), - ); + expect(Offset.fromDirection(pi / 6, 2.0), within(from: Offset(math.sqrt(3.0), 1.0))); }); test('Size.aspectRatio', () { expect(const Size(0.0, 0.0).aspectRatio, 0.0); @@ -76,8 +73,13 @@ void testMain() { expect(const Size(3.0, 4.0).aspectRatio, 3.0 / 4.0); }); test('Radius.clamp() operates as expected', () { - final RRect rrectMin = RRect.fromLTRBR(1, 3, 5, 7, - const Radius.circular(-100).clamp(minimum: Radius.zero)); + final RRect rrectMin = RRect.fromLTRBR( + 1, + 3, + 5, + 7, + const Radius.circular(-100).clamp(minimum: Radius.zero), + ); expect(rrectMin.left, 1); expect(rrectMin.top, 3); @@ -86,8 +88,13 @@ void testMain() { expect(rrectMin.trRadius, equals(const Radius.circular(0))); expect(rrectMin.blRadius, equals(const Radius.circular(0))); - final RRect rrectMax = RRect.fromLTRBR(1, 3, 5, 7, - const Radius.circular(100).clamp(maximum: const Radius.circular(10))); + final RRect rrectMax = RRect.fromLTRBR( + 1, + 3, + 5, + 7, + const Radius.circular(100).clamp(maximum: const Radius.circular(10)), + ); expect(rrectMax.left, 1); expect(rrectMax.top, 3); @@ -96,8 +103,16 @@ void testMain() { expect(rrectMax.trRadius, equals(const Radius.circular(10))); expect(rrectMax.blRadius, equals(const Radius.circular(10))); - final RRect rrectMix = RRect.fromLTRBR(1, 3, 5, 7, - const Radius.elliptical(-100, 100).clamp(minimum: Radius.zero, maximum: const Radius.circular(10))); + final RRect rrectMix = RRect.fromLTRBR( + 1, + 3, + 5, + 7, + const Radius.elliptical( + -100, + 100, + ).clamp(minimum: Radius.zero, maximum: const Radius.circular(10)), + ); expect(rrectMix.left, 1); expect(rrectMix.top, 3); @@ -106,8 +121,16 @@ void testMain() { expect(rrectMix.trRadius, equals(const Radius.elliptical(0, 10))); expect(rrectMix.blRadius, equals(const Radius.elliptical(0, 10))); - final RRect rrectMix1 = RRect.fromLTRBR(1, 3, 5, 7, - const Radius.elliptical(100, -100).clamp(minimum: Radius.zero, maximum: const Radius.circular(10))); + final RRect rrectMix1 = RRect.fromLTRBR( + 1, + 3, + 5, + 7, + const Radius.elliptical( + 100, + -100, + ).clamp(minimum: Radius.zero, maximum: const Radius.circular(10)), + ); expect(rrectMix1.left, 1); expect(rrectMix1.top, 3); @@ -117,8 +140,13 @@ void testMain() { expect(rrectMix1.blRadius, equals(const Radius.elliptical(10, 0))); }); test('Radius.clampValues() operates as expected', () { - final RRect rrectMin = RRect.fromLTRBR(1, 3, 5, 7, - const Radius.circular(-100).clampValues(minimumX: 0, minimumY: 0)); + final RRect rrectMin = RRect.fromLTRBR( + 1, + 3, + 5, + 7, + const Radius.circular(-100).clampValues(minimumX: 0, minimumY: 0), + ); expect(rrectMin.left, 1); expect(rrectMin.top, 3); @@ -127,8 +155,13 @@ void testMain() { expect(rrectMin.trRadius, equals(const Radius.circular(0))); expect(rrectMin.blRadius, equals(const Radius.circular(0))); - final RRect rrectMax = RRect.fromLTRBR(1, 3, 5, 7, - const Radius.circular(100).clampValues(maximumX: 10, maximumY: 20)); + final RRect rrectMax = RRect.fromLTRBR( + 1, + 3, + 5, + 7, + const Radius.circular(100).clampValues(maximumX: 10, maximumY: 20), + ); expect(rrectMax.left, 1); expect(rrectMax.top, 3); @@ -137,8 +170,16 @@ void testMain() { expect(rrectMax.trRadius, equals(const Radius.elliptical(10, 20))); expect(rrectMax.blRadius, equals(const Radius.elliptical(10, 20))); - final RRect rrectMix = RRect.fromLTRBR(1, 3, 5, 7, - const Radius.elliptical(-100, 100).clampValues(minimumX: 5, minimumY: 6, maximumX: 10, maximumY: 20)); + final RRect rrectMix = RRect.fromLTRBR( + 1, + 3, + 5, + 7, + const Radius.elliptical( + -100, + 100, + ).clampValues(minimumX: 5, minimumY: 6, maximumX: 10, maximumY: 20), + ); expect(rrectMix.left, 1); expect(rrectMix.top, 3); @@ -147,8 +188,16 @@ void testMain() { expect(rrectMix.trRadius, equals(const Radius.elliptical(5, 20))); expect(rrectMix.blRadius, equals(const Radius.elliptical(5, 20))); - final RRect rrectMix2 = RRect.fromLTRBR(1, 3, 5, 7, - const Radius.elliptical(100, -100).clampValues(minimumX: 5, minimumY: 6, maximumX: 10, maximumY: 20)); + final RRect rrectMix2 = RRect.fromLTRBR( + 1, + 3, + 5, + 7, + const Radius.elliptical( + 100, + -100, + ).clampValues(minimumX: 5, minimumY: 6, maximumX: 10, maximumY: 20), + ); expect(rrectMix2.left, 1); expect(rrectMix2.top, 3); diff --git a/lib/web_ui/test/engine/gesture_settings_test.dart b/lib/web_ui/test/engine/gesture_settings_test.dart index c86414e13c066..882722521ddef 100644 --- a/lib/web_ui/test/engine/gesture_settings_test.dart +++ b/lib/web_ui/test/engine/gesture_settings_test.dart @@ -12,22 +12,32 @@ void main() { void testMain() { test('GestureSettings has a reasonable toString', () { - const GestureSettings gestureSettings = - GestureSettings(physicalDoubleTapSlop: 2.5, physicalTouchSlop: 1.5); - - expect(gestureSettings.toString(), - 'GestureSettings(physicalTouchSlop: 1.5, physicalDoubleTapSlop: 2.5)'); + const GestureSettings gestureSettings = GestureSettings( + physicalDoubleTapSlop: 2.5, + physicalTouchSlop: 1.5, + ); + + expect( + gestureSettings.toString(), + 'GestureSettings(physicalTouchSlop: 1.5, physicalDoubleTapSlop: 2.5)', + ); }); test('GestureSettings has a correct equality', () { // don't refactor these to be const, that defeats the point! final double value = nonconst(2.0); - final GestureSettings settingsA = - GestureSettings(physicalDoubleTapSlop: value, physicalTouchSlop: 1.0); - final GestureSettings settingsB = - GestureSettings(physicalDoubleTapSlop: value, physicalTouchSlop: 3.0); - final GestureSettings settingsC = - GestureSettings(physicalDoubleTapSlop: value, physicalTouchSlop: 1.0); + final GestureSettings settingsA = GestureSettings( + physicalDoubleTapSlop: value, + physicalTouchSlop: 1.0, + ); + final GestureSettings settingsB = GestureSettings( + physicalDoubleTapSlop: value, + physicalTouchSlop: 3.0, + ); + final GestureSettings settingsC = GestureSettings( + physicalDoubleTapSlop: value, + physicalTouchSlop: 1.0, + ); expect(settingsA, equals(settingsC)); expect(settingsC, equals(settingsA)); @@ -40,16 +50,20 @@ void testMain() { }); test('GestureSettings copyWith preserves already set values', () { - const GestureSettings initial = - GestureSettings(physicalDoubleTapSlop: 1.0, physicalTouchSlop: 1.0); + const GestureSettings initial = GestureSettings( + physicalDoubleTapSlop: 1.0, + physicalTouchSlop: 1.0, + ); final GestureSettings copyA = initial.copyWith(); expect(copyA.physicalDoubleTapSlop, 1.0); expect(copyA.physicalTouchSlop, 1.0); - final GestureSettings copyB = - copyA.copyWith(physicalDoubleTapSlop: 2.0, physicalTouchSlop: 2.0); + final GestureSettings copyB = copyA.copyWith( + physicalDoubleTapSlop: 2.0, + physicalTouchSlop: 2.0, + ); expect(copyB.physicalDoubleTapSlop, 2.0); expect(copyB.physicalTouchSlop, 2.0); diff --git a/lib/web_ui/test/engine/global_styles_test.dart b/lib/web_ui/test/engine/global_styles_test.dart index f87dd1633dbc3..2caa46e6eaee4 100644 --- a/lib/web_ui/test/engine/global_styles_test.dart +++ b/lib/web_ui/test/engine/global_styles_test.dart @@ -17,10 +17,7 @@ void testMain() { setUp(() { styleElement = createDomHTMLStyleElement(null); - applyGlobalCssRulesToSheet( - styleElement, - defaultCssFont: _kDefaultCssFont, - ); + applyGlobalCssRulesToSheet(styleElement, defaultCssFont: _kDefaultCssFont); }); tearDown(() { @@ -35,11 +32,17 @@ void testMain() { }); test('(Self-test) hasCssRule can extract rules', () { - final bool hasRule = hasCssRule(styleElement, - selector: '.flt-text-editing::placeholder', declaration: 'opacity: 0'); + final bool hasRule = hasCssRule( + styleElement, + selector: '.flt-text-editing::placeholder', + declaration: 'opacity: 0', + ); - final bool hasFakeRule = hasCssRule(styleElement, - selector: 'input::selection', declaration: 'color: #fabada;'); + final bool hasFakeRule = hasCssRule( + styleElement, + selector: 'input::selection', + declaration: 'color: #fabada;', + ); expect(hasRule, isTrue); expect(hasFakeRule, isFalse); @@ -47,51 +50,72 @@ void testMain() { test('Attaches styling to remove password reveal icons on Edge', () { // Check that style.sheet! contains input::-ms-reveal rule - final bool hidesRevealIcons = hasCssRule(styleElement, - selector: 'input::-ms-reveal', declaration: 'display: none'); + final bool hidesRevealIcons = hasCssRule( + styleElement, + selector: 'input::-ms-reveal', + declaration: 'display: none', + ); - final bool codeRanInFakeyBrowser = hasCssRule(styleElement, - selector: 'input.fallback-for-fakey-browser-in-ci', - declaration: 'display: none'); + final bool codeRanInFakeyBrowser = hasCssRule( + styleElement, + selector: 'input.fallback-for-fakey-browser-in-ci', + declaration: 'display: none', + ); if (codeRanInFakeyBrowser) { print('Please, fix https://github.com/flutter/flutter/issues/116302'); } - expect(hidesRevealIcons || codeRanInFakeyBrowser, isTrue, - reason: 'In Edge, stylesheet must contain "input::-ms-reveal" rule.'); + expect( + hidesRevealIcons || codeRanInFakeyBrowser, + isTrue, + reason: 'In Edge, stylesheet must contain "input::-ms-reveal" rule.', + ); }, skip: !isEdge); test('Does not attach the Edge-specific style tag on non-Edge browsers', () { // Check that style.sheet! contains input::-ms-reveal rule - final bool hidesRevealIcons = hasCssRule(styleElement, - selector: 'input::-ms-reveal', declaration: 'display: none'); + final bool hidesRevealIcons = hasCssRule( + styleElement, + selector: 'input::-ms-reveal', + declaration: 'display: none', + ); expect(hidesRevealIcons, isFalse); }, skip: isEdge); test( - 'Attaches styles to hide the autofill overlay for browsers that support it', - () { - final String vendorPrefix = (isSafari || isFirefox) ? '' : '-webkit-'; - final bool autofillOverlay = hasCssRule(styleElement, + 'Attaches styles to hide the autofill overlay for browsers that support it', + () { + final String vendorPrefix = (isSafari || isFirefox) ? '' : '-webkit-'; + final bool autofillOverlay = hasCssRule( + styleElement, selector: '.transparentTextEditing:${vendorPrefix}autofill', - declaration: 'opacity: 0 !important'); - final bool autofillOverlayHovered = hasCssRule(styleElement, + declaration: 'opacity: 0 !important', + ); + final bool autofillOverlayHovered = hasCssRule( + styleElement, selector: '.transparentTextEditing:${vendorPrefix}autofill:hover', - declaration: 'opacity: 0 !important'); - final bool autofillOverlayFocused = hasCssRule(styleElement, + declaration: 'opacity: 0 !important', + ); + final bool autofillOverlayFocused = hasCssRule( + styleElement, selector: '.transparentTextEditing:${vendorPrefix}autofill:focus', - declaration: 'opacity: 0 !important'); - final bool autofillOverlayActive = hasCssRule(styleElement, + declaration: 'opacity: 0 !important', + ); + final bool autofillOverlayActive = hasCssRule( + styleElement, selector: '.transparentTextEditing:${vendorPrefix}autofill:active', - declaration: 'opacity: 0 !important'); - - expect(autofillOverlay, isTrue); - expect(autofillOverlayHovered, isTrue); - expect(autofillOverlayFocused, isTrue); - expect(autofillOverlayActive, isTrue); - }, skip: !browserHasAutofillOverlay()); + declaration: 'opacity: 0 !important', + ); + + expect(autofillOverlay, isTrue); + expect(autofillOverlayHovered, isTrue); + expect(autofillOverlayFocused, isTrue); + expect(autofillOverlayActive, isTrue); + }, + skip: !browserHasAutofillOverlay(), + ); } /// Finds out whether a given CSS Rule ([selector] { [declaration]; }) exists in a [styleElement]. @@ -104,8 +128,7 @@ bool hasCssRule( assert(styleElement.sheet != null); // regexr.com/740ff - final RegExp ruleLike = - RegExp('[^{]*(?:$selector)[^{]*{[^}]*(?:$declaration)[^}]*}'); + final RegExp ruleLike = RegExp('[^{]*(?:$selector)[^{]*{[^}]*(?:$declaration)[^}]*}'); final DomCSSStyleSheet sheet = styleElement.sheet! as DomCSSStyleSheet; diff --git a/lib/web_ui/test/engine/history_test.dart b/lib/web_ui/test/engine/history_test.dart index 4d3e80fa9955d..231725fbe76b5 100644 --- a/lib/web_ui/test/engine/history_test.dart +++ b/lib/web_ui/test/engine/history_test.dart @@ -14,18 +14,14 @@ import '../common/matchers.dart'; import '../common/spy.dart'; import '../common/test_initialization.dart'; -EngineFlutterWindow get implicitView => - EnginePlatformDispatcher.instance.implicitView!; +EngineFlutterWindow get implicitView => EnginePlatformDispatcher.instance.implicitView!; Map _wrapOriginState(dynamic state) { return {'origin': true, 'state': state}; } Map _tagStateWithSerialCount(dynamic state, int serialCount) { - return { - 'serialCount': serialCount, - 'state': state, - }; + return {'serialCount': serialCount, 'state': state}; } const Map flutterState = {'flutter': true}; @@ -130,7 +126,6 @@ void testMain() { await implicitView.debugInitializeHistory(strategy, useSingle: true); - // There should be one `popstate` listener and two history entries. expect(strategy.listeners, hasLength(1)); expect(strategy.history, hasLength(2)); @@ -386,7 +381,6 @@ void testMain() { await implicitView.debugInitializeHistory(strategy, useSingle: false); - // There should be one `popstate` listener and one history entry. expect(strategy.listeners, hasLength(1)); expect(strategy.history, hasLength(1)); @@ -658,10 +652,7 @@ void testMain() { expect(strategy.prepareExternalUrl(internalUrl), '/main#/menu?foo=bar'); location.search = '?foo=bar'; - expect( - strategy.prepareExternalUrl(internalUrl), - '/main?foo=bar#/menu?foo=bar', - ); + expect(strategy.prepareExternalUrl(internalUrl), '/main?foo=bar#/menu?foo=bar'); }); test('removes /#/ from the home page', () { @@ -675,10 +666,7 @@ void testMain() { expect(strategy.prepareExternalUrl(internalUrl), '/main'); location.search = '?foo=bar'; - expect( - strategy.prepareExternalUrl(internalUrl), - '/main?foo=bar', - ); + expect(strategy.prepareExternalUrl(internalUrl), '/main?foo=bar'); }); test('addPopStateListener fn unwraps DomPopStateEvent state', () { @@ -746,10 +734,7 @@ Future routeUpdated(String routeName) { final Completer completer = Completer(); EnginePlatformDispatcher.instance.sendPlatformMessage( 'flutter/navigation', - codec.encodeMethodCall(MethodCall( - 'routeUpdated', - {'routeName': routeName}, - )), + codec.encodeMethodCall(MethodCall('routeUpdated', {'routeName': routeName})), (_) => completer.complete(), ); return completer.future; @@ -759,10 +744,12 @@ Future routeInformationUpdated(String location, dynamic state) { final Completer completer = Completer(); EnginePlatformDispatcher.instance.sendPlatformMessage( 'flutter/navigation', - codec.encodeMethodCall(MethodCall( - 'routeInformationUpdated', - {'location': location, 'state': state}, - )), + codec.encodeMethodCall( + MethodCall('routeInformationUpdated', { + 'location': location, + 'state': state, + }), + ), (_) => completer.complete(), ); return completer.future; @@ -797,12 +784,9 @@ class TestPlatformLocation implements PlatformLocation { /// Calls all the registered `popStateListeners` with a 'popstate' /// event with value `state` void debugTriggerPopState(Object? state) { - final DomEvent event = createDomPopStateEvent( - 'popstate', - { - if (state != null) 'state': state, - }, - ); + final DomEvent event = createDomPopStateEvent('popstate', { + if (state != null) 'state': state, + }); for (final EventListener listener in popStateListeners) { listener(event); } diff --git a/lib/web_ui/test/engine/image/image_test.dart b/lib/web_ui/test/engine/image/image_test.dart index c0b6235368b3f..87aa3285a1eb1 100644 --- a/lib/web_ui/test/engine/image/image_test.dart +++ b/lib/web_ui/test/engine/image/image_test.dart @@ -11,13 +11,12 @@ void main() { } Future testMain() async { - test('toImage succeeds', () async { final ui.Image image = await _createImage(); expect(image.runtimeType.toString(), equals('HtmlImage')); image.dispose(); - // TODO(polina-c): unskip the test when bug is fixed: - // https://github.com/flutter/flutter/issues/110599 + // TODO(polina-c): unskip the test when bug is fixed: + // https://github.com/flutter/flutter/issues/110599 }, skip: true); test('Image constructor invokes onCreate once', () async { @@ -39,8 +38,8 @@ Future testMain() async { expect(createdImage, image2); ui.Image.onCreate = null; - // TODO(polina-c): unskip the test when bug is fixed: - // https://github.com/flutter/flutter/issues/110599 + // TODO(polina-c): unskip the test when bug is fixed: + // https://github.com/flutter/flutter/issues/110599 }, skip: true); test('dispose() invokes onDispose once', () async { @@ -51,19 +50,23 @@ Future testMain() async { disposedImage = image; }; - final ui.Image image1 = await _createImage()..dispose(); + final ui.Image image1 = + await _createImage() + ..dispose(); expect(onDisposeInvokedCount, 1); expect(disposedImage, image1); - final ui.Image image2 = await _createImage()..dispose(); + final ui.Image image2 = + await _createImage() + ..dispose(); expect(onDisposeInvokedCount, 2); expect(disposedImage, image2); ui.Image.onDispose = null; - // TODO(polina-c): unskip the test when bug is fixed: - // https://github.com/flutter/flutter/issues/110599 + // TODO(polina-c): unskip the test when bug is fixed: + // https://github.com/flutter/flutter/issues/110599 }, skip: true); } diff --git a/lib/web_ui/test/engine/image_format_detector_test.dart b/lib/web_ui/test/engine/image_format_detector_test.dart index f2aac31955147..1279f548a615c 100644 --- a/lib/web_ui/test/engine/image_format_detector_test.dart +++ b/lib/web_ui/test/engine/image_format_detector_test.dart @@ -21,8 +21,7 @@ Future testMain() async { Future> createTestFiles() async { final HttpFetchResponse listingResponse = await httpFetch('/test_images/'); - final List testFiles = - (await listingResponse.json() as List).cast(); + final List testFiles = (await listingResponse.json() as List).cast(); // Sanity-check the test file list. If suddenly test files are moved or // deleted, and the test server returns an empty list, or is missing some @@ -41,8 +40,7 @@ Future testMain() async { for (final String testFile in testFiles!) { test('can detect image type of $testFile', () async { - final HttpFetchResponse response = - await httpFetch('/test_images/$testFile'); + final HttpFetchResponse response = await httpFetch('/test_images/$testFile'); if (!response.hasPayload) { throw Exception('Unable to fetch() image test file "$testFile"'); @@ -71,17 +69,12 @@ Future testMain() async { 'xOffsetTooBig.gif', ]; - final String testFileExtension = - testFile.substring(testFile.lastIndexOf('.') + 1); + final String testFileExtension = testFile.substring(testFile.lastIndexOf('.') + 1); final ImageType? expectedImageType = switch (testFileExtension) { 'jpg' => ImageType.jpeg, 'jpeg' => ImageType.jpeg, - 'gif' => animatedGifFiles.contains(testFile) - ? ImageType.animatedGif - : ImageType.gif, - 'webp' => animatedWebpFiles.contains(testFile) - ? ImageType.animatedWebp - : ImageType.webp, + 'gif' => animatedGifFiles.contains(testFile) ? ImageType.animatedGif : ImageType.gif, + 'webp' => animatedWebpFiles.contains(testFile) ? ImageType.animatedWebp : ImageType.webp, 'avif' => ImageType.avif, 'bmp' => ImageType.bmp, 'png' => ImageType.png, diff --git a/lib/web_ui/test/engine/image_to_byte_data_test.dart b/lib/web_ui/test/engine/image_to_byte_data_test.dart index 13c61ccddf9c8..f5aba71f96d8a 100644 --- a/lib/web_ui/test/engine/image_to_byte_data_test.dart +++ b/lib/web_ui/test/engine/image_to_byte_data_test.dart @@ -20,8 +20,7 @@ Future testMain() async { Future createTestImageByColor(Color color) async { final EnginePictureRecorder recorder = EnginePictureRecorder(); - final RecordingCanvas canvas = - recorder.beginRecording(const Rect.fromLTRB(0, 0, 2, 2)); + final RecordingCanvas canvas = recorder.beginRecording(const Rect.fromLTRB(0, 0, 2, 2)); canvas.drawColor(color, BlendMode.srcOver); final Picture testPicture = recorder.endRecording(); final Image testImage = await testPicture.toImage(2, 2); @@ -31,33 +30,21 @@ Future testMain() async { test('Picture.toImage().toByteData()', () async { final Image testImage = await createTestImageByColor(const Color(0xFFCCDD00)); - final ByteData bytes = - (await testImage.toByteData())!; - expect( - bytes.buffer.asUint32List(), - [0xFF00DDCC, 0xFF00DDCC, 0xFF00DDCC, 0xFF00DDCC], - ); + final ByteData bytes = (await testImage.toByteData())!; + expect(bytes.buffer.asUint32List(), [0xFF00DDCC, 0xFF00DDCC, 0xFF00DDCC, 0xFF00DDCC]); - final ByteData pngBytes = - (await testImage.toByteData(format: ImageByteFormat.png))!; + final ByteData pngBytes = (await testImage.toByteData(format: ImageByteFormat.png))!; // PNG-encoding is browser-specific, but the header is standard. We only // test the header. final List pngHeader = [137, 80, 78, 71, 13, 10, 26, 10]; - expect( - pngBytes.buffer.asUint8List().sublist(0, pngHeader.length), - pngHeader, - ); + expect(pngBytes.buffer.asUint8List().sublist(0, pngHeader.length), pngHeader); }); test('Image.toByteData(format: ImageByteFormat.rawStraightRgba)', () async { final Image testImage = await createTestImageByColor(const Color(0xAAFFFF00)); - final ByteData bytes = - (await testImage.toByteData(format: ImageByteFormat.rawStraightRgba))!; - expect( - bytes.buffer.asUint32List(), - [0xAA00FFFF, 0xAA00FFFF, 0xAA00FFFF, 0xAA00FFFF], - ); + final ByteData bytes = (await testImage.toByteData(format: ImageByteFormat.rawStraightRgba))!; + expect(bytes.buffer.asUint32List(), [0xAA00FFFF, 0xAA00FFFF, 0xAA00FFFF, 0xAA00FFFF]); }); } diff --git a/lib/web_ui/test/engine/initialization_test.dart b/lib/web_ui/test/engine/initialization_test.dart index 2fbb2f76e15f6..a2e4bc43e12d0 100644 --- a/lib/web_ui/test/engine/initialization_test.dart +++ b/lib/web_ui/test/engine/initialization_test.dart @@ -22,68 +22,90 @@ void main() { // Prepare _flutter.loader.didCreateEngineInitializer, so it's ready in the page ASAP. loader = js_util.jsify({ 'loader': { - 'didCreateEngineInitializer': () { print('not mocked'); }.toJS, + 'didCreateEngineInitializer': + () { + print('not mocked'); + }.toJS, }, }); internalBootstrapBrowserTest(() => testMain); } void testMain() { - test('bootstrapEngine calls _flutter.loader.didCreateEngineInitializer callback', () async { - JSAny? engineInitializer; - - void didCreateEngineInitializerMock(JSAny? obj) { - print('obj: $obj'); - engineInitializer = obj; - } - - // Prepare the DOM for: _flutter.loader.didCreateEngineInitializer - didCreateEngineInitializer = didCreateEngineInitializerMock.toJS; - - // Reset the engine - engine.debugResetEngineInitializationState(); - - await ui_web.bootstrapEngine( - registerPlugins: () {}, - runApp: () {}, - ); - - // Check that the object we captured is actually a loader - expect(engineInitializer, isNotNull); - expect(js_util.hasProperty(engineInitializer!, 'initializeEngine'), isTrue, reason: 'Missing FlutterEngineInitializer method: initializeEngine.'); - expect(js_util.hasProperty(engineInitializer!, 'autoStart'), isTrue, reason: 'Missing FlutterEngineInitializer method: autoStart.'); - // https://github.com/flutter/flutter/issues/160096 - }, skip: ui_web.browser.isFirefox); - - test('bootstrapEngine does auto-start when _flutter.loader.didCreateEngineInitializer does not exist', () async { - loader = null; - - bool pluginsRegistered = false; - bool appRan = false; - void registerPluginsMock() { - pluginsRegistered = true; - } - void runAppMock() { - appRan = true; - } - - // Reset the engine - engine.debugResetEngineInitializationState(); - - await ui_web.bootstrapEngine( - registerPlugins: registerPluginsMock, - runApp: runAppMock, - ); - - // Check that the object we captured is actually a loader - expect(pluginsRegistered, isTrue, reason: 'Plugins should be immediately registered in autoStart mode.'); - expect(appRan, isTrue, reason: 'App should run immediately in autoStart mode'); - - // After starting the engine, the meta-generator tag should be on the page - final DomElement? meta = domDocument.querySelector('meta[name=generator][content=Flutter]'); - expect(meta, isNotNull, reason: 'The generator meta-tag should be added when Flutter initializes its UI.'); - // https://github.com/flutter/flutter/issues/160096 - }, skip: ui_web.browser.isFirefox); + test( + 'bootstrapEngine calls _flutter.loader.didCreateEngineInitializer callback', + () async { + JSAny? engineInitializer; + + void didCreateEngineInitializerMock(JSAny? obj) { + print('obj: $obj'); + engineInitializer = obj; + } + + // Prepare the DOM for: _flutter.loader.didCreateEngineInitializer + didCreateEngineInitializer = didCreateEngineInitializerMock.toJS; + + // Reset the engine + engine.debugResetEngineInitializationState(); + + await ui_web.bootstrapEngine(registerPlugins: () {}, runApp: () {}); + + // Check that the object we captured is actually a loader + expect(engineInitializer, isNotNull); + expect( + js_util.hasProperty(engineInitializer!, 'initializeEngine'), + isTrue, + reason: 'Missing FlutterEngineInitializer method: initializeEngine.', + ); + expect( + js_util.hasProperty(engineInitializer!, 'autoStart'), + isTrue, + reason: 'Missing FlutterEngineInitializer method: autoStart.', + ); + // https://github.com/flutter/flutter/issues/160096 + }, + skip: ui_web.browser.isFirefox, + ); + + test( + 'bootstrapEngine does auto-start when _flutter.loader.didCreateEngineInitializer does not exist', + () async { + loader = null; + + bool pluginsRegistered = false; + bool appRan = false; + void registerPluginsMock() { + pluginsRegistered = true; + } + + void runAppMock() { + appRan = true; + } + + // Reset the engine + engine.debugResetEngineInitializationState(); + + await ui_web.bootstrapEngine(registerPlugins: registerPluginsMock, runApp: runAppMock); + + // Check that the object we captured is actually a loader + expect( + pluginsRegistered, + isTrue, + reason: 'Plugins should be immediately registered in autoStart mode.', + ); + expect(appRan, isTrue, reason: 'App should run immediately in autoStart mode'); + + // After starting the engine, the meta-generator tag should be on the page + final DomElement? meta = domDocument.querySelector('meta[name=generator][content=Flutter]'); + expect( + meta, + isNotNull, + reason: 'The generator meta-tag should be added when Flutter initializes its UI.', + ); + // https://github.com/flutter/flutter/issues/160096 + }, + skip: ui_web.browser.isFirefox, + ); // We cannot test anymore, because by now the engine has registered some stuff that can't be rewound back. // Like the `ext.flutter.disassemble` developer extension. diff --git a/lib/web_ui/test/engine/keyboard_converter_test.dart b/lib/web_ui/test/engine/keyboard_converter_test.dart index a3b4c18f711b7..b9e8a8aff6791 100644 --- a/lib/web_ui/test/engine/keyboard_converter_test.dart +++ b/lib/web_ui/test/engine/keyboard_converter_test.dart @@ -53,32 +53,41 @@ void main() { void testMain() { test('KeyData.toString', () { - expect(const ui.KeyData( - type: ui.KeyEventType.down, - physical: 0x700e5, - logical: 0x61, - character: 'A', - timeStamp: Duration.zero, - synthesized: false, - ).toString(), 'KeyData(Key Down, physical: 0x700e5, logical: 0x61 (Unicode), character: "A" (0x41))'); + expect( + const ui.KeyData( + type: ui.KeyEventType.down, + physical: 0x700e5, + logical: 0x61, + character: 'A', + timeStamp: Duration.zero, + synthesized: false, + ).toString(), + 'KeyData(Key Down, physical: 0x700e5, logical: 0x61 (Unicode), character: "A" (0x41))', + ); - expect(const ui.KeyData( - type: ui.KeyEventType.up, - physical: 0x700e6, - logical: 0x100000061, - character: '\n', - timeStamp: Duration.zero, - synthesized: true, - ).toString(), r'KeyData(Key Up, physical: 0x700e6, logical: 0x100000061 (Unprintable), character: "\n" (0x0a), synthesized)'); + expect( + const ui.KeyData( + type: ui.KeyEventType.up, + physical: 0x700e6, + logical: 0x100000061, + character: '\n', + timeStamp: Duration.zero, + synthesized: true, + ).toString(), + r'KeyData(Key Up, physical: 0x700e6, logical: 0x100000061 (Unprintable), character: "\n" (0x0a), synthesized)', + ); - expect(const ui.KeyData( - type: ui.KeyEventType.repeat, - physical: 0x700e7, - logical: 0x9900000071, - character: null, - timeStamp: Duration.zero, - synthesized: false, - ).toString(), 'KeyData(Key Repeat, physical: 0x700e7, logical: 0x9900000071, character: )'); + expect( + const ui.KeyData( + type: ui.KeyEventType.repeat, + physical: 0x700e7, + logical: 0x9900000071, + character: null, + timeStamp: Duration.zero, + synthesized: false, + ).toString(), + 'KeyData(Key Repeat, physical: 0x700e7, logical: 0x9900000071, character: )', + ); }); test('Single key press, repeat, and release', () { @@ -90,7 +99,8 @@ void testMain() { }, ui_web.OperatingSystem.linux); converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 1); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, timeStamp: const Duration(milliseconds: 1), type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, @@ -101,7 +111,8 @@ void testMain() { expect(MockKeyboardEvent.lastDefaultPrevented, isTrue); converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a')..timeStamp = 1.5); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, timeStamp: const Duration(milliseconds: 1, microseconds: 500), type: ui.KeyEventType.repeat, deviceType: ui.KeyEventDeviceType.keyboard, @@ -112,7 +123,8 @@ void testMain() { expect(MockKeyboardEvent.lastDefaultPrevented, isFalse); converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a')..timeStamp = 1500); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, timeStamp: const Duration(seconds: 1, milliseconds: 500), type: ui.KeyEventType.repeat, deviceType: ui.KeyEventDeviceType.keyboard, @@ -123,7 +135,8 @@ void testMain() { expect(MockKeyboardEvent.lastDefaultPrevented, isFalse); converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 2000.5); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, timeStamp: const Duration(seconds: 2, microseconds: 500), type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, @@ -144,7 +157,8 @@ void testMain() { // en-in.win, with AltGr converter.handleEvent(keyDownEvent('KeyL', 'l̥', kCtrl | kAlt)..timeStamp = 1); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, timeStamp: const Duration(milliseconds: 1), type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, @@ -163,7 +177,8 @@ void testMain() { }, ui_web.OperatingSystem.linux); converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalShiftLeft, @@ -173,7 +188,8 @@ void testMain() { expect(MockKeyboardEvent.lastDefaultPrevented, isTrue); converter.handleEvent(keyDownEvent('KeyA', 'A', kShift)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalKeyA, @@ -183,7 +199,8 @@ void testMain() { expect(MockKeyboardEvent.lastDefaultPrevented, isTrue); converter.handleEvent(keyRepeatedDownEvent('KeyA', 'A', kShift)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.repeat, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalKeyA, @@ -193,7 +210,8 @@ void testMain() { expect(MockKeyboardEvent.lastDefaultPrevented, isFalse); converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalShiftLeft, @@ -203,7 +221,8 @@ void testMain() { expect(MockKeyboardEvent.lastDefaultPrevented, isFalse); converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a')); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.repeat, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalKeyA, @@ -213,7 +232,8 @@ void testMain() { expect(MockKeyboardEvent.lastDefaultPrevented, isFalse); converter.handleEvent(keyRepeatedDownEvent('KeyA', 'a')); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.repeat, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalKeyA, @@ -223,7 +243,8 @@ void testMain() { expect(MockKeyboardEvent.lastDefaultPrevented, isFalse); converter.handleEvent(keyUpEvent('KeyA', 'a')); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalKeyA, @@ -241,7 +262,8 @@ void testMain() { }, ui_web.OperatingSystem.linux); converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalShiftLeft, @@ -250,7 +272,8 @@ void testMain() { ); converter.handleEvent(keyDownEvent('ShiftRight', 'Shift', kShift, kLocationRight)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalShiftRight, @@ -259,7 +282,8 @@ void testMain() { ); converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalShiftLeft, @@ -268,7 +292,8 @@ void testMain() { ); converter.handleEvent(keyUpEvent('ShiftRight', 'Shift', 0, kLocationRight)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalShiftRight, @@ -285,7 +310,8 @@ void testMain() { }, ui_web.OperatingSystem.linux); converter.handleEvent(keyDownEvent('', 'Shift', kShift)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalEmptyCode, @@ -294,7 +320,8 @@ void testMain() { ); converter.handleEvent(keyUpEvent('', 'Shift', kShift)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalEmptyCode, @@ -303,7 +330,8 @@ void testMain() { ); converter.handleEvent(keyDownEvent('', 'Control', kCtrl)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalEmptyCode, @@ -312,7 +340,8 @@ void testMain() { ); converter.handleEvent(keyUpEvent('', 'Control', kCtrl)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalEmptyCode, @@ -321,7 +350,8 @@ void testMain() { ); converter.handleEvent(keyDownEvent('', 'Alt', kAlt)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalEmptyCode, @@ -330,7 +360,8 @@ void testMain() { ); converter.handleEvent(keyUpEvent('', 'Alt', kAlt)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalEmptyCode, @@ -339,7 +370,8 @@ void testMain() { ); converter.handleEvent(keyDownEvent('', 'Meta', kMeta)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalEmptyCode, @@ -348,7 +380,8 @@ void testMain() { ); converter.handleEvent(keyUpEvent('', 'Meta', kMeta)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalEmptyCode, @@ -365,7 +398,8 @@ void testMain() { }, ui_web.OperatingSystem.linux); converter.handleEvent(keyDownEvent('Digit1', '1')); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalDigit1, @@ -374,7 +408,8 @@ void testMain() { ); converter.handleEvent(keyDownEvent('Numpad1', '1', 0, kLocationNumpad)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalNumpad1, @@ -383,7 +418,8 @@ void testMain() { ); converter.handleEvent(keyUpEvent('Digit1', '1')); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalDigit1, @@ -392,7 +428,8 @@ void testMain() { ); converter.handleEvent(keyUpEvent('Numpad1', '1', 0, kLocationNumpad)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalNumpad1, @@ -419,7 +456,8 @@ void testMain() { converter.handleEvent(keyDownEvent('AltLeft', 'Alt', kAlt, kLocationLeft)); converter.handleEvent(keyDownEvent('KeyE', 'Dead', kAlt)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalKeyE, @@ -428,7 +466,8 @@ void testMain() { ); converter.handleEvent(keyUpEvent('KeyE', 'Dead', kAlt)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalKeyE, @@ -437,7 +476,8 @@ void testMain() { ); converter.handleEvent(keyDownEvent('KeyU', 'Dead', kAlt)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalKeyU, @@ -446,7 +486,8 @@ void testMain() { ); converter.handleEvent(keyUpEvent('KeyU', 'Dead', kAlt)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalKeyU, @@ -459,7 +500,8 @@ void testMain() { // This does not actually produce a Dead key on macOS (US layout); just for // testing. converter.handleEvent(keyDownEvent('KeyE', 'Dead', kAlt | kShift)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalKeyE, @@ -470,7 +512,8 @@ void testMain() { converter.handleEvent(keyUpEvent('AltLeft', 'Alt', kShift, kLocationLeft)); converter.handleEvent(keyUpEvent('KeyE', 'e', kShift)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalKeyE, @@ -495,7 +538,8 @@ void testMain() { keyDataList.clear(); converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); expect(keyDataList, hasLength(2)); - expectKeyData(keyDataList.first, + expectKeyData( + keyDataList.first, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalShiftLeft, @@ -503,7 +547,8 @@ void testMain() { character: null, synthesized: true, ); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalShiftLeft, @@ -515,7 +560,8 @@ void testMain() { keyDataList.clear(); converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft)); expect(keyDataList, hasLength(1)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalShiftLeft, @@ -537,7 +583,8 @@ void testMain() { // https://github.com/flutter/flutter/issues/126247#issuecomment-1856112566. converter.handleEvent(keyDownEvent('BracketLeft', 'Dead')); expect(MockKeyboardEvent.lastDefaultPrevented, isTrue); - expectKeyData(keyDataList.first, + expectKeyData( + keyDataList.first, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalBracketLeft, @@ -550,7 +597,8 @@ void testMain() { converter.handleEvent(keyDownEvent('BracketLeft', 'Process')); expect(keyDataList, hasLength(2)); - expectKeyData(keyDataList.first, + expectKeyData( + keyDataList.first, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalBracketLeft, @@ -558,7 +606,8 @@ void testMain() { character: null, synthesized: true, ); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalBracketLeft, @@ -605,7 +654,8 @@ void testMain() { // Passes if there's no crash, and states are reset after everything is released. keyDataList.clear(); converter.handleEvent(keyDownEvent('KeyA', 'a')); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalKeyA, @@ -614,7 +664,8 @@ void testMain() { ); converter.handleEvent(keyDownEvent('KeyU', 'u')); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalKeyU, @@ -623,7 +674,10 @@ void testMain() { ); }); - for (final ui_web.OperatingSystem system in [ui_web.OperatingSystem.macOs, ui_web.OperatingSystem.iOs]) { + for (final ui_web.OperatingSystem system in [ + ui_web.OperatingSystem.macOs, + ui_web.OperatingSystem.iOs, + ]) { testFakeAsync('CapsLock down synthesizes an immediate cancel on $system', (FakeAsync async) { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { @@ -634,7 +688,8 @@ void testMain() { // A KeyDown of ShiftRight is missed due to loss of focus. converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalCapsLock, @@ -646,7 +701,8 @@ void testMain() { async.elapse(const Duration(microseconds: 1)); expect(keyDataList, hasLength(1)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalCapsLock, @@ -659,7 +715,8 @@ void testMain() { converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalCapsLock, @@ -671,7 +728,8 @@ void testMain() { async.elapse(const Duration(microseconds: 1)); expect(keyDataList, hasLength(1)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalCapsLock, @@ -685,7 +743,8 @@ void testMain() { // Another key down works converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalCapsLock, @@ -694,7 +753,6 @@ void testMain() { ); keyDataList.clear(); - // Schedules are canceled after disposal converter.dispose(); async.elapse(const Duration(seconds: 10)); @@ -711,7 +769,8 @@ void testMain() { converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalCapsLock, @@ -725,7 +784,8 @@ void testMain() { converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); expect(keyDataList, hasLength(1)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalCapsLock, @@ -738,7 +798,8 @@ void testMain() { expect(keyDataList, isEmpty); converter.handleEvent(keyDownEvent('CapsLock', 'CapsLock')); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalCapsLock, @@ -747,7 +808,8 @@ void testMain() { ); converter.handleEvent(keyUpEvent('CapsLock', 'CapsLock')); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalCapsLock, @@ -756,7 +818,10 @@ void testMain() { ); }); - for (final ui_web.OperatingSystem system in [ui_web.OperatingSystem.macOs, ui_web.OperatingSystem.iOs]) { + for (final ui_web.OperatingSystem system in [ + ui_web.OperatingSystem.macOs, + ui_web.OperatingSystem.iOs, + ]) { testFakeAsync('Key guards: key down events are guarded on $system', (FakeAsync async) { final List keyDataList = []; final KeyboardConverter converter = KeyboardConverter((ui.KeyData key) { @@ -764,11 +829,14 @@ void testMain() { return true; }, system); - converter.handleEvent(keyDownEvent('MetaLeft', 'Meta', kMeta, kLocationLeft)..timeStamp = 100); + converter.handleEvent( + keyDownEvent('MetaLeft', 'Meta', kMeta, kLocationLeft)..timeStamp = 100, + ); async.elapse(const Duration(milliseconds: 100)); converter.handleEvent(keyDownEvent('KeyA', 'a', kMeta)..timeStamp = 200); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, timeStamp: const Duration(milliseconds: 200), type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, @@ -781,7 +849,8 @@ void testMain() { // Key Up of KeyA is omitted due to being a shortcut. async.elapse(const Duration(milliseconds: 2500)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, timeStamp: const Duration(milliseconds: 2200), type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, @@ -793,7 +862,8 @@ void testMain() { keyDataList.clear(); converter.handleEvent(keyUpEvent('MetaLeft', 'Meta', 0, kLocationLeft)..timeStamp = 2700); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, timeStamp: const Duration(milliseconds: 2700), type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, @@ -805,7 +875,8 @@ void testMain() { // Key A states are cleared converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 2800); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, timeStamp: const Duration(milliseconds: 2800), type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, @@ -816,7 +887,8 @@ void testMain() { async.elapse(const Duration(milliseconds: 100)); converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 2900); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, timeStamp: const Duration(milliseconds: 2900), type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, @@ -849,7 +921,8 @@ void testMain() { // Key Up of KeyA is omitted due to being a shortcut. async.elapse(const Duration(milliseconds: 2000)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, timeStamp: const Duration(milliseconds: 2700), type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, @@ -861,7 +934,8 @@ void testMain() { keyDataList.clear(); converter.handleEvent(keyUpEvent('MetaLeft', 'Meta', 0, kLocationLeft)..timeStamp = 3200); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, timeStamp: const Duration(milliseconds: 3200), type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, @@ -873,7 +947,8 @@ void testMain() { // Key A states are cleared converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 3300); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, timeStamp: const Duration(milliseconds: 3300), type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, @@ -884,7 +959,8 @@ void testMain() { async.elapse(const Duration(milliseconds: 100)); converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 3400); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, timeStamp: const Duration(milliseconds: 3400), type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, @@ -905,7 +981,8 @@ void testMain() { async.elapse(const Duration(milliseconds: 100)); converter.handleEvent(keyDownEvent('KeyA', 'a', kCtrl)..timeStamp = 200); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, timeStamp: const Duration(milliseconds: 200), type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, @@ -920,7 +997,8 @@ void testMain() { async.elapse(const Duration(milliseconds: 100)); converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 800); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, timeStamp: const Duration(milliseconds: 800), type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, @@ -934,7 +1012,8 @@ void testMain() { // Key A states are cleared converter.handleEvent(keyDownEvent('KeyA', 'a')..timeStamp = 2800); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, timeStamp: const Duration(milliseconds: 2800), type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, @@ -945,7 +1024,8 @@ void testMain() { async.elapse(const Duration(milliseconds: 100)); converter.handleEvent(keyUpEvent('KeyA', 'a')..timeStamp = 2900); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, timeStamp: const Duration(milliseconds: 2900), type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, @@ -966,7 +1046,8 @@ void testMain() { async.elapse(const Duration(milliseconds: 100)); converter.handleEvent(keyDownEvent('KeyA', 'a', kMeta)..timeStamp = 200); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, timeStamp: const Duration(milliseconds: 200), type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, @@ -989,7 +1070,8 @@ void testMain() { converter.handleEvent(keyDownEvent('ScrollLock', 'ScrollLock')); expect(keyDataList, hasLength(1)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalScrollLock, @@ -1003,7 +1085,8 @@ void testMain() { converter.handleEvent(keyUpEvent('ScrollLock', 'ScrollLock')); expect(keyDataList, hasLength(1)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalScrollLock, @@ -1013,7 +1096,8 @@ void testMain() { keyDataList.clear(); converter.handleEvent(keyDownEvent('ScrollLock', 'ScrollLock')); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalScrollLock, @@ -1022,7 +1106,8 @@ void testMain() { ); converter.handleEvent(keyUpEvent('ScrollLock', 'ScrollLock')); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalScrollLock, @@ -1039,7 +1124,8 @@ void testMain() { }, ui_web.OperatingSystem.linux); converter.handleEvent(keyDownEvent('ShiftRight', 'Shift', kShift, kLocationRight)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalShiftRight, @@ -1048,7 +1134,8 @@ void testMain() { ); converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalShiftLeft, @@ -1061,7 +1148,8 @@ void testMain() { converter.handleEvent(keyDownEvent('KeyA', 'a')); expect(keyDataList, hasLength(3)); - expectKeyData(keyDataList[0], + expectKeyData( + keyDataList[0], type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalShiftLeft, @@ -1069,7 +1157,8 @@ void testMain() { character: null, synthesized: true, ); - expectKeyData(keyDataList[1], + expectKeyData( + keyDataList[1], type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalShiftRight, @@ -1077,7 +1166,8 @@ void testMain() { character: null, synthesized: true, ); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalKeyA, @@ -1100,7 +1190,8 @@ void testMain() { }, ui_web.OperatingSystem.linux); converter.handleEvent(keyDownEvent('ShiftLeft', 'Shift', kShift, kLocationLeft)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalShiftLeft, @@ -1109,10 +1200,13 @@ void testMain() { ); keyDataList.clear(); - converter.handleEvent(keyDownEvent('MetaLeft', 'Meta', kShift /* No kMeta here! */, kLocationLeft)); + converter.handleEvent( + keyDownEvent('MetaLeft', 'Meta', kShift /* No kMeta here! */, kLocationLeft), + ); // Only a MetaLeft down event, no synthesized MetaLeft up events. expect(keyDataList, hasLength(1)); - expectKeyData(keyDataList.first, + expectKeyData( + keyDataList.first, type: ui.KeyEventType.down, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalMetaLeft, @@ -1121,10 +1215,13 @@ void testMain() { ); keyDataList.clear(); - converter.handleEvent(keyUpEvent('MetaLeft', 'Meta', kShift | kMeta /* Yes, kMeta here! */, kLocationLeft)); + converter.handleEvent( + keyUpEvent('MetaLeft', 'Meta', kShift | kMeta /* Yes, kMeta here! */, kLocationLeft), + ); // Only a MetaLeft down event, no synthesized MetaLeft up events. expect(keyDataList, hasLength(1)); - expectKeyData(keyDataList.first, + expectKeyData( + keyDataList.first, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalMetaLeft, @@ -1134,7 +1231,8 @@ void testMain() { keyDataList.clear(); converter.handleEvent(keyUpEvent('ShiftLeft', 'Shift', 0, kLocationLeft)); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, physical: kPhysicalShiftLeft, @@ -1195,7 +1293,12 @@ MockKeyboardEvent keyUpEvent(String? code, String? key, [int modifiers = 0, int ); } -MockKeyboardEvent keyRepeatedDownEvent(String code, String key, [int modifiers = 0, int location = 0]) { +MockKeyboardEvent keyRepeatedDownEvent( + String code, + String key, [ + int modifiers = 0, + int location = 0, +]) { return MockKeyboardEvent( type: 'keydown', code: code, diff --git a/lib/web_ui/test/engine/lerp_test.dart b/lib/web_ui/test/engine/lerp_test.dart index d69d5c1ffc2ea..e1920545d0e19 100644 --- a/lib/web_ui/test/engine/lerp_test.dart +++ b/lib/web_ui/test/engine/lerp_test.dart @@ -113,10 +113,13 @@ void testMain() { expectAssertion(() => lerpDouble(0.0, 10.0, double.nan)); }); - test('lerpDouble should throw AssertionError if interpolation value is +/- infinity and a != b', () { - expectAssertion(() => lerpDouble(0.0, 10.0, double.infinity)); - expectAssertion(() => lerpDouble(0.0, 10.0, -double.infinity)); - }); + test( + 'lerpDouble should throw AssertionError if interpolation value is +/- infinity and a != b', + () { + expectAssertion(() => lerpDouble(0.0, 10.0, double.infinity)); + expectAssertion(() => lerpDouble(0.0, 10.0, -double.infinity)); + }, + ); test('lerpDouble should throw AssertionError if either start or end are NaN', () { expectAssertion(() => lerpDouble(double.nan, 10.0, 5.0)); diff --git a/lib/web_ui/test/engine/locale_test.dart b/lib/web_ui/test/engine/locale_test.dart index 8a80c7ce355ed..f0a2fceb30694 100644 --- a/lib/web_ui/test/engine/locale_test.dart +++ b/lib/web_ui/test/engine/locale_test.dart @@ -36,34 +36,28 @@ void testMain() { expect(const Locale.fromSubtags(countryCode: 'US').toString(), 'und_US'); expect(const Locale.fromSubtags(countryCode: 'US').countryCode, 'US'); - expect( - const Locale.fromSubtags(languageCode: 'es', countryCode: '419') - .toString(), - 'es_419'); - expect( - const Locale.fromSubtags(languageCode: 'es', countryCode: '419') - .languageCode, - 'es'); - expect( - const Locale.fromSubtags(languageCode: 'es', countryCode: '419') - .countryCode, - '419'); + expect(const Locale.fromSubtags(languageCode: 'es', countryCode: '419').toString(), 'es_419'); + expect(const Locale.fromSubtags(languageCode: 'es', countryCode: '419').languageCode, 'es'); + expect(const Locale.fromSubtags(languageCode: 'es', countryCode: '419').countryCode, '419'); expect( - const Locale.fromSubtags( - languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN') - .toString(), - 'zh_Hans_CN'); + const Locale.fromSubtags( + languageCode: 'zh', + scriptCode: 'Hans', + countryCode: 'CN', + ).toString(), + 'zh_Hans_CN', + ); }); test('Locale equality', () { expect( - const Locale.fromSubtags(languageCode: 'en'), - isNot( - const Locale.fromSubtags(languageCode: 'en', scriptCode: 'Latn'))); + const Locale.fromSubtags(languageCode: 'en'), + isNot(const Locale.fromSubtags(languageCode: 'en', scriptCode: 'Latn')), + ); expect( - const Locale.fromSubtags(languageCode: 'en').hashCode, - isNot(const Locale.fromSubtags(languageCode: 'en', scriptCode: 'Latn') - .hashCode)); + const Locale.fromSubtags(languageCode: 'en').hashCode, + isNot(const Locale.fromSubtags(languageCode: 'en', scriptCode: 'Latn').hashCode), + ); }); } diff --git a/lib/web_ui/test/engine/lru_cache_test.dart b/lib/web_ui/test/engine/lru_cache_test.dart index ba1dfcaea0fd6..0027efb3c709a 100644 --- a/lib/web_ui/test/engine/lru_cache_test.dart +++ b/lib/web_ui/test/engine/lru_cache_test.dart @@ -21,9 +21,7 @@ void testMain() { test('$LruCache adds up to a maximum number of items in most recently used first order', () { final LruCache cache = LruCache(3); cache.cache('a', 1); - expect(cache.debugItemQueue.toList(), [ - (key: 'a', value: 1), - ]); + expect(cache.debugItemQueue.toList(), [(key: 'a', value: 1)]); expect(cache['a'], 1); expect(cache['b'], isNull); diff --git a/lib/web_ui/test/engine/matchers_test.dart b/lib/web_ui/test/engine/matchers_test.dart index b305d92553c79..96c91112f6df4 100644 --- a/lib/web_ui/test/engine/matchers_test.dart +++ b/lib/web_ui/test/engine/matchers_test.dart @@ -23,79 +23,55 @@ void testMain() { void _expectDomTests() { test('trivial equal elements', () { - expectDom( - '
', - hasHtml('
'), - ); + expectDom('
', hasHtml('
')); }); test('trivial unequal elements', () { expectDom( '
', - expectMismatch( - hasHtml(''), - ''' + expectMismatch(hasHtml(''), ''' The following DOM structure did not match the expected pattern:
Specifically: - - @span: unexpected tag name
(expected ).''', - ), + - @span: unexpected tag name
(expected ).'''), ); }); test('trivial equal text content', () { - expectDom( - '
hello
', - hasHtml('
hello
'), - ); + expectDom('
hello
', hasHtml('
hello
')); }); test('trivial unequal text content', () { expectDom( '
hello
', - expectMismatch( - hasHtml('
world
'), - ''' + expectMismatch(hasHtml('
world
'), ''' The following DOM structure did not match the expected pattern:
hello
Specifically: - - @div: expected text content "world", but found "hello".''', - ), + - @div: expected text content "world", but found "hello".'''), ); }); test('white space between elements', () { - expectDom( - ' ', - hasHtml(' '), - ); + expectDom(' ', hasHtml(' ')); - expectDom( - ' ', - hasHtml(' '), - ); + expectDom(' ', hasHtml(' ')); expectDom( ' ', - expectMismatch( - hasHtml(' '), - ''' + expectMismatch(hasHtml(' '), ''' The following DOM structure did not match the expected pattern: Specifically: - - @a > b: expected text content " ", but found " ".''', - ), + - @a > b: expected text content " ", but found " ".'''), ); }); test('trivial equal attributes', () { - expectDom( - '
', - hasHtml('
'), - ); + expectDom('
', hasHtml('
')); }); test('trivial out-of-order equal attributes', () { @@ -108,43 +84,31 @@ Specifically: test('trivial unequal attributes', () { expectDom( '
', - expectMismatch( - hasHtml('
'), - ''' + expectMismatch(hasHtml('
'), ''' The following DOM structure did not match the expected pattern:
Specifically: - - @div#id: expected attribute value id="world", but found id="hello".''', - ), + - @div#id: expected attribute value id="world", but found id="hello".'''), ); }); test('trivial missing attributes', () { expectDom( '
', - expectMismatch( - hasHtml('
'), - ''' + expectMismatch(hasHtml('
'), ''' The following DOM structure did not match the expected pattern:
Specifically: - - @div#id: attribute id="hello" missing.''', - ), + - @div#id: attribute id="hello" missing.'''), ); }); test('trivial additional attributes', () { - expectDom( - '
', - hasHtml('
'), - ); + expectDom('
', hasHtml('
')); - expectDom( - '
', - hasHtml('
'), - ); + expectDom('
', hasHtml('
')); }); test('trivial equal style', () { @@ -171,30 +135,24 @@ Specifically: test('trivial unequal style attributes', () { expectDom( '
', - expectMismatch( - hasHtml('
'), - ''' + expectMismatch(hasHtml('
'), ''' The following DOM structure did not match the expected pattern:
Specifically: - - @div#style(width): expected style property width="12px", but found width="10px".''', - ), + - @div#style(width): expected style property width="12px", but found width="10px".'''), ); }); test('trivial missing style attribute', () { expectDom( '
', - expectMismatch( - hasHtml('
'), - ''' + expectMismatch(hasHtml('
'), ''' The following DOM structure did not match the expected pattern:
Specifically: - - @div#style(height): style property height="20px" missing.''', - ), + - @div#style(height): style property height="20px" missing.'''), ); }); @@ -202,7 +160,9 @@ Specifically: expectDom( '
', expectMismatch( - hasHtml('
'), + hasHtml( + '
', + ), ''' The following DOM structure did not match the expected pattern:
@@ -216,61 +176,46 @@ Specifically: }); test('trivial child elements', () { - expectDom( - '

', - hasHtml('

'), - ); + expectDom('

', hasHtml('

')); }); test('trivial nested child elements', () { - expectDom( - '

', - hasHtml('

'), - ); + expectDom('

', hasHtml('

')); }); test('missing child elements', () { expectDom( '

', - expectMismatch( - hasHtml('

'), - ''' + expectMismatch(hasHtml('

'), ''' The following DOM structure did not match the expected pattern:

Specifically: - - @div: expected 3 child nodes, but found 2.''', - ), + - @div: expected 3 child nodes, but found 2.'''), ); }); test('additional child elements', () { expectDom( '

', - expectMismatch( - hasHtml('

'), - ''' + expectMismatch(hasHtml('

'), ''' The following DOM structure did not match the expected pattern:

Specifically: - - @div: expected 2 child nodes, but found 3.''', - ), + - @div: expected 2 child nodes, but found 3.'''), ); }); test('deep breadcrumbs', () { expectDom( '', - expectMismatch( - hasHtml(''), - ''' + expectMismatch(hasHtml(''), ''' The following DOM structure did not match the expected pattern: Specifically: - - @a > b > c > d#style(width): expected style property width="2px", but found width="1px".''', - ), + - @a > b > c > d#style(width): expected style property width="2px", but found width="1px".'''), ); }); } @@ -299,12 +244,7 @@ class _ExpectMismatch extends Matcher { } final _TestDescription description = _TestDescription(); - _matcher.describeMismatch( - item, - description, - matchState, - false, - ); + _matcher.describeMismatch(item, description, matchState, false); final String mismatchDescription = description.items.join(); if (mismatchDescription.trim() != expectedMismatchDescription.trim()) { diff --git a/lib/web_ui/test/engine/navigation_test.dart b/lib/web_ui/test/engine/navigation_test.dart index d1d93fc7dc14d..1b602da8f3501 100644 --- a/lib/web_ui/test/engine/navigation_test.dart +++ b/lib/web_ui/test/engine/navigation_test.dart @@ -14,8 +14,7 @@ import '../common/test_initialization.dart'; const MethodCodec codec = JSONMethodCodec(); -EngineFlutterWindow get implicitView => - EnginePlatformDispatcher.instance.implicitView!; +EngineFlutterWindow get implicitView => EnginePlatformDispatcher.instance.implicitView!; void main() { internalBootstrapBrowserTest(() => testMain); @@ -29,10 +28,9 @@ void testMain() { final Completer completer = Completer(); ui.PlatformDispatcher.instance.sendPlatformMessage( 'flutter/navigation', - codec.encodeMethodCall(const MethodCall( - 'routeUpdated', - {'routeName': '/foo'}, - )), + codec.encodeMethodCall( + const MethodCall('routeUpdated', {'routeName': '/foo'}), + ), (ByteData? response) => completer.complete(response), ); final ByteData? response = await completer.future; @@ -54,10 +52,9 @@ void testMain() { final Completer completer = Completer(); ui.PlatformDispatcher.instance.sendPlatformMessage( 'flutter/navigation', - codec.encodeMethodCall(const MethodCall( - 'routeUpdated', - {'routeName': '/foo'}, - )), + codec.encodeMethodCall( + const MethodCall('routeUpdated', {'routeName': '/foo'}), + ), (_) => completer.complete(), ); await completer.future; diff --git a/lib/web_ui/test/engine/platform_dispatcher/application_switcher_description_test.dart b/lib/web_ui/test/engine/platform_dispatcher/application_switcher_description_test.dart index 9b23085505dbc..7d405de4fbf4e 100644 --- a/lib/web_ui/test/engine/platform_dispatcher/application_switcher_description_test.dart +++ b/lib/web_ui/test/engine/platform_dispatcher/application_switcher_description_test.dart @@ -31,13 +31,12 @@ Future testMain() async { ui.PlatformDispatcher.instance.sendPlatformMessage( 'flutter/platform', - codec.encodeMethodCall(const MethodCall( - 'SystemChrome.setApplicationSwitcherDescription', - { + codec.encodeMethodCall( + const MethodCall('SystemChrome.setApplicationSwitcherDescription', { 'label': 'Title Test', 'primaryColor': 0xFF00FF00, - }, - )), + }), + ), null, ); @@ -48,13 +47,12 @@ Future testMain() async { ui.PlatformDispatcher.instance.sendPlatformMessage( 'flutter/platform', - codec.encodeMethodCall(const MethodCall( - 'SystemChrome.setApplicationSwitcherDescription', - { + codec.encodeMethodCall( + const MethodCall('SystemChrome.setApplicationSwitcherDescription', { 'label': 'Different title', 'primaryColor': 0xFFFABADA, - }, - )), + }), + ), null, ); @@ -75,13 +73,12 @@ Future testMain() async { ui.PlatformDispatcher.instance.sendPlatformMessage( 'flutter/platform', - codec.encodeMethodCall(const MethodCall( - 'SystemChrome.setApplicationSwitcherDescription', - { + codec.encodeMethodCall( + const MethodCall('SystemChrome.setApplicationSwitcherDescription', { 'label': null, 'primaryColor': null, - }, - )), + }), + ), null, ); @@ -93,10 +90,9 @@ Future testMain() async { ui.PlatformDispatcher.instance.sendPlatformMessage( 'flutter/platform', - codec.encodeMethodCall(const MethodCall( - 'SystemChrome.setApplicationSwitcherDescription', - {}, - )), + codec.encodeMethodCall( + const MethodCall('SystemChrome.setApplicationSwitcherDescription', {}), + ), null, ); diff --git a/lib/web_ui/test/engine/platform_dispatcher/platform_dispatcher_test.dart b/lib/web_ui/test/engine/platform_dispatcher/platform_dispatcher_test.dart index ea2e9b7037c0d..b07b5f48e2917 100644 --- a/lib/web_ui/test/engine/platform_dispatcher/platform_dispatcher_test.dart +++ b/lib/web_ui/test/engine/platform_dispatcher/platform_dispatcher_test.dart @@ -38,12 +38,10 @@ void testMain() { }); test('high contrast in accessibilityFeatures has the correct value', () { - final MockHighContrastSupport mockHighContrast = - MockHighContrastSupport(); + final MockHighContrastSupport mockHighContrast = MockHighContrastSupport(); HighContrastSupport.instance = mockHighContrast; - final EnginePlatformDispatcher dispatcher = - EnginePlatformDispatcher(); + final EnginePlatformDispatcher dispatcher = EnginePlatformDispatcher(); expect(dispatcher.accessibilityFeatures.highContrast, isTrue); mockHighContrast.isEnabled = false; @@ -59,72 +57,64 @@ void testMain() { states.add(state); } - final MockAppLifecycleState mockAppLifecycleState = - MockAppLifecycleState(); + final MockAppLifecycleState mockAppLifecycleState = MockAppLifecycleState(); - expect(mockAppLifecycleState.appLifecycleState, - ui.AppLifecycleState.resumed); + expect(mockAppLifecycleState.appLifecycleState, ui.AppLifecycleState.resumed); mockAppLifecycleState.addListener(listener); expect(mockAppLifecycleState.activeCallCount, 1); - expect( - states, equals([ui.AppLifecycleState.resumed])); + expect(states, equals([ui.AppLifecycleState.resumed])); mockAppLifecycleState.inactive(); - expect(mockAppLifecycleState.appLifecycleState, - ui.AppLifecycleState.inactive); + expect(mockAppLifecycleState.appLifecycleState, ui.AppLifecycleState.inactive); expect( - states, - equals([ - ui.AppLifecycleState.resumed, - ui.AppLifecycleState.inactive - ])); + states, + equals([ui.AppLifecycleState.resumed, ui.AppLifecycleState.inactive]), + ); // consecutive same states are skipped mockAppLifecycleState.inactive(); expect( - states, - equals([ - ui.AppLifecycleState.resumed, - ui.AppLifecycleState.inactive - ])); + states, + equals([ui.AppLifecycleState.resumed, ui.AppLifecycleState.inactive]), + ); mockAppLifecycleState.hidden(); + expect(mockAppLifecycleState.appLifecycleState, ui.AppLifecycleState.hidden); expect( - mockAppLifecycleState.appLifecycleState, ui.AppLifecycleState.hidden); - expect( - states, - equals([ - ui.AppLifecycleState.resumed, - ui.AppLifecycleState.inactive, - ui.AppLifecycleState.hidden - ])); + states, + equals([ + ui.AppLifecycleState.resumed, + ui.AppLifecycleState.inactive, + ui.AppLifecycleState.hidden, + ]), + ); mockAppLifecycleState.resume(); - expect(mockAppLifecycleState.appLifecycleState, - ui.AppLifecycleState.resumed); + expect(mockAppLifecycleState.appLifecycleState, ui.AppLifecycleState.resumed); expect( - states, - equals([ - ui.AppLifecycleState.resumed, - ui.AppLifecycleState.inactive, - ui.AppLifecycleState.hidden, - ui.AppLifecycleState.resumed - ])); + states, + equals([ + ui.AppLifecycleState.resumed, + ui.AppLifecycleState.inactive, + ui.AppLifecycleState.hidden, + ui.AppLifecycleState.resumed, + ]), + ); mockAppLifecycleState.detach(); - expect(mockAppLifecycleState.appLifecycleState, - ui.AppLifecycleState.detached); + expect(mockAppLifecycleState.appLifecycleState, ui.AppLifecycleState.detached); expect( - states, - equals([ - ui.AppLifecycleState.resumed, - ui.AppLifecycleState.inactive, - ui.AppLifecycleState.hidden, - ui.AppLifecycleState.resumed, - ui.AppLifecycleState.detached - ])); + states, + equals([ + ui.AppLifecycleState.resumed, + ui.AppLifecycleState.inactive, + ui.AppLifecycleState.hidden, + ui.AppLifecycleState.resumed, + ui.AppLifecycleState.detached, + ]), + ); mockAppLifecycleState.removeListener(listener); expect(mockAppLifecycleState.deactivateCallCount, 1); @@ -132,14 +122,15 @@ void testMain() { // No more states should be recorded after the listener is removed. mockAppLifecycleState.resume(); expect( - states, - equals([ - ui.AppLifecycleState.resumed, - ui.AppLifecycleState.inactive, - ui.AppLifecycleState.hidden, - ui.AppLifecycleState.resumed, - ui.AppLifecycleState.detached - ])); + states, + equals([ + ui.AppLifecycleState.resumed, + ui.AppLifecycleState.inactive, + ui.AppLifecycleState.hidden, + ui.AppLifecycleState.resumed, + ui.AppLifecycleState.detached, + ]), + ); }); test('responds to flutter/skia Skia.setResourceCacheMaxBytes', () async { @@ -147,19 +138,15 @@ void testMain() { final Completer completer = Completer(); ui.PlatformDispatcher.instance.sendPlatformMessage( 'flutter/skia', - codec.encodeMethodCall(const MethodCall( - 'Skia.setResourceCacheMaxBytes', - 512 * 1000 * 1000, - )), + codec.encodeMethodCall( + const MethodCall('Skia.setResourceCacheMaxBytes', 512 * 1000 * 1000), + ), completer.complete, ); final ByteData? response = await completer.future; expect(response, isNotNull); - expect( - codec.decodeEnvelope(response!), - [true], - ); + expect(codec.decodeEnvelope(response!), [true]); }); test('responds to flutter/platform HapticFeedback.vibrate', () async { @@ -167,39 +154,29 @@ void testMain() { final Completer completer = Completer(); ui.PlatformDispatcher.instance.sendPlatformMessage( 'flutter/platform', - codec.encodeMethodCall(const MethodCall( - 'HapticFeedback.vibrate', - )), + codec.encodeMethodCall(const MethodCall('HapticFeedback.vibrate')), completer.complete, ); final ByteData? response = await completer.future; expect(response, isNotNull); - expect( - codec.decodeEnvelope(response!), - true, - ); + expect(codec.decodeEnvelope(response!), true); }); - test('responds to flutter/platform SystemChrome.setSystemUIOverlayStyle', - () async { + test('responds to flutter/platform SystemChrome.setSystemUIOverlayStyle', () async { const MethodCodec codec = JSONMethodCodec(); final Completer completer = Completer(); ui.PlatformDispatcher.instance.sendPlatformMessage( 'flutter/platform', - codec.encodeMethodCall(const MethodCall( - 'SystemChrome.setSystemUIOverlayStyle', - {}, - )), + codec.encodeMethodCall( + const MethodCall('SystemChrome.setSystemUIOverlayStyle', {}), + ), completer.complete, ); final ByteData? response = await completer.future; expect(response, isNotNull); - expect( - codec.decodeEnvelope(response!), - true, - ); + expect(codec.decodeEnvelope(response!), true); }); test('responds to flutter/contextmenu enable', () async { @@ -207,18 +184,13 @@ void testMain() { final Completer completer = Completer(); ui.PlatformDispatcher.instance.sendPlatformMessage( 'flutter/contextmenu', - codec.encodeMethodCall(const MethodCall( - 'enableContextMenu', - )), + codec.encodeMethodCall(const MethodCall('enableContextMenu')), completer.complete, ); final ByteData? response = await completer.future; expect(response, isNotNull); - expect( - codec.decodeEnvelope(response!), - true, - ); + expect(codec.decodeEnvelope(response!), true); }); test('responds to flutter/contextmenu disable', () async { @@ -226,18 +198,13 @@ void testMain() { final Completer completer = Completer(); ui.PlatformDispatcher.instance.sendPlatformMessage( 'flutter/contextmenu', - codec.encodeMethodCall(const MethodCall( - 'disableContextMenu', - )), + codec.encodeMethodCall(const MethodCall('disableContextMenu')), completer.complete, ); final ByteData? response = await completer.future; expect(response, isNotNull); - expect( - codec.decodeEnvelope(response!), - true, - ); + expect(codec.decodeEnvelope(response!), true); }); test('can find text scale factor', () async { @@ -269,13 +236,10 @@ void testMain() { expect(findBrowserTextScaleFactor(), 1.0); }); - test( - "calls onTextScaleFactorChanged when the element's font-size changes", - () async { + test("calls onTextScaleFactorChanged when the element's font-size changes", () async { final DomElement root = domDocument.documentElement!; final String oldFontSize = root.style.fontSize; - final ui.VoidCallback? oldCallback = - ui.PlatformDispatcher.instance.onTextScaleFactorChanged; + final ui.VoidCallback? oldCallback = ui.PlatformDispatcher.instance.onTextScaleFactorChanged; addTearDown(() { root.style.fontSize = oldFontSize; @@ -293,8 +257,7 @@ void testMain() { await Future.delayed(Duration.zero); expect(root.style.fontSize, '20px'); expect(isCalled, isTrue); - expect(ui.PlatformDispatcher.instance.textScaleFactor, - findBrowserTextScaleFactor()); + expect(ui.PlatformDispatcher.instance.textScaleFactor, findBrowserTextScaleFactor()); isCalled = false; @@ -302,17 +265,13 @@ void testMain() { await Future.delayed(Duration.zero); expect(root.style.fontSize, '16px'); expect(isCalled, isTrue); - expect(ui.PlatformDispatcher.instance.textScaleFactor, - findBrowserTextScaleFactor()); + expect(ui.PlatformDispatcher.instance.textScaleFactor, findBrowserTextScaleFactor()); }); test('disposes all its views', () { - final EngineFlutterView view1 = - EngineFlutterView(dispatcher, createDomHTMLDivElement()); - final EngineFlutterView view2 = - EngineFlutterView(dispatcher, createDomHTMLDivElement()); - final EngineFlutterView view3 = - EngineFlutterView(dispatcher, createDomHTMLDivElement()); + final EngineFlutterView view1 = EngineFlutterView(dispatcher, createDomHTMLDivElement()); + final EngineFlutterView view2 = EngineFlutterView(dispatcher, createDomHTMLDivElement()); + final EngineFlutterView view3 = EngineFlutterView(dispatcher, createDomHTMLDivElement()); dispatcher.viewManager ..registerView(view1) @@ -330,10 +289,8 @@ void testMain() { }); test('connects view disposal to metrics changed event', () { - final EngineFlutterView view1 = - EngineFlutterView(dispatcher, createDomHTMLDivElement()); - final EngineFlutterView view2 = - EngineFlutterView(dispatcher, createDomHTMLDivElement()); + final EngineFlutterView view1 = EngineFlutterView(dispatcher, createDomHTMLDivElement()); + final EngineFlutterView view2 = EngineFlutterView(dispatcher, createDomHTMLDivElement()); dispatcher.viewManager ..registerView(view1) @@ -357,8 +314,7 @@ void testMain() { }); test('disconnects view disposal event on dispose', () { - final EngineFlutterView view1 = - EngineFlutterView(dispatcher, createDomHTMLDivElement()); + final EngineFlutterView view1 = EngineFlutterView(dispatcher, createDomHTMLDivElement()); dispatcher.viewManager.registerView(view1); @@ -423,15 +379,18 @@ void testMain() { test('scheduleWarmupFrame should call both callbacks', () async { bool beginFrameCalled = false; final Completer drawFrameCalled = Completer(); - dispatcher.scheduleWarmUpFrame(beginFrame: () { - expect(drawFrameCalled.isCompleted, false); - expect(beginFrameCalled, false); - beginFrameCalled = true; - }, drawFrame: () { - expect(beginFrameCalled, true); - expect(drawFrameCalled.isCompleted, false); - drawFrameCalled.complete(); - }); + dispatcher.scheduleWarmUpFrame( + beginFrame: () { + expect(drawFrameCalled.isCompleted, false); + expect(beginFrameCalled, false); + beginFrameCalled = true; + }, + drawFrame: () { + expect(beginFrameCalled, true); + expect(drawFrameCalled.isCompleted, false); + drawFrameCalled.complete(); + }, + ); await drawFrameCalled.future; expect(beginFrameCalled, true); expect(drawFrameCalled.isCompleted, true); diff --git a/lib/web_ui/test/engine/platform_dispatcher/system_ui_overlay_style_test.dart b/lib/web_ui/test/engine/platform_dispatcher/system_ui_overlay_style_test.dart index c0d6feea3f6f7..d47c1bd2eead3 100644 --- a/lib/web_ui/test/engine/platform_dispatcher/system_ui_overlay_style_test.dart +++ b/lib/web_ui/test/engine/platform_dispatcher/system_ui_overlay_style_test.dart @@ -18,12 +18,11 @@ void testMain() { void sendSetSystemUIOverlayStyle({ui.Color? statusBarColor}) { ui.PlatformDispatcher.instance.sendPlatformMessage( 'flutter/platform', - codec.encodeMethodCall(MethodCall( - 'SystemChrome.setSystemUIOverlayStyle', - { + codec.encodeMethodCall( + MethodCall('SystemChrome.setSystemUIOverlayStyle', { 'statusBarColor': statusBarColor?.value, - }, - )), + }), + ), null, ); } diff --git a/lib/web_ui/test/engine/platform_dispatcher/view_focus_binding_test.dart b/lib/web_ui/test/engine/platform_dispatcher/view_focus_binding_test.dart index 7e7754836c1e8..5284ad7386fb2 100644 --- a/lib/web_ui/test/engine/platform_dispatcher/view_focus_binding_test.dart +++ b/lib/web_ui/test/engine/platform_dispatcher/view_focus_binding_test.dart @@ -32,7 +32,6 @@ void testMain() { test('The view is focusable and reachable by keyboard when registered', () async { final EngineFlutterView view = createAndRegisterView(dispatcher); - // The root element should have a tabindex="0" to make the flutter view // focusable and reachable by the keyboard. expect(view.dom.rootElement.getAttribute('tabindex'), '0'); @@ -292,13 +291,7 @@ extension on DomElement { dispatchKeyboardEvent(type: 'keyup', key: 'Tab', shiftKey: shift); } - void dispatchKeyboardEvent({ - required String type, - required String key, - bool shiftKey = false, - }) { - dispatchEvent( - createDomKeyboardEvent(type, {'key': key, 'shiftKey': shiftKey}), - ); + void dispatchKeyboardEvent({required String type, required String key, bool shiftKey = false}) { + dispatchEvent(createDomKeyboardEvent(type, {'key': key, 'shiftKey': shiftKey})); } } diff --git a/lib/web_ui/test/engine/platform_views/content_manager_test.dart b/lib/web_ui/test/engine/platform_views/content_manager_test.dart index 544ab026fda20..b48b7997520a1 100644 --- a/lib/web_ui/test/engine/platform_views/content_manager_test.dart +++ b/lib/web_ui/test/engine/platform_views/content_manager_test.dart @@ -65,17 +65,23 @@ void testMain() { group('registerFactory', () { test('does NOT re-register factories', () async { contentManager.registerFactory( - viewType, (int id) => createDomHTMLDivElement()..id = 'pass'); + viewType, + (int id) => createDomHTMLDivElement()..id = 'pass', + ); // this should be rejected contentManager.registerFactory( - viewType, (int id) => createDomHTMLSpanElement()..id = 'fail'); + viewType, + (int id) => createDomHTMLSpanElement()..id = 'fail', + ); - final DomElement contents = - contentManager.renderContent(viewType, viewId, null); + final DomElement contents = contentManager.renderContent(viewType, viewId, null); expect(contents.querySelector('#pass'), isNotNull); - expect(contents.querySelector('#fail'), isNull, - reason: 'Factories cannot be overridden once registered'); + expect( + contents.querySelector('#fail'), + isNull, + reason: 'Factories cannot be overridden once registered', + ); }); }); @@ -99,17 +105,18 @@ void testMain() { test('refuse to render views for unregistered factories', () async { expect( () => contentManager.renderContent(unregisteredViewType, viewId, null), - throwsA(const TypeMatcher().having( - (AssertionError error) => error.message, - 'assertion message', - contains(unregisteredViewType), - )), + throwsA( + const TypeMatcher().having( + (AssertionError error) => error.message, + 'assertion message', + contains(unregisteredViewType), + ), + ), ); }); test('rendered markup contains required attributes', () async { - final DomElement content = - contentManager.renderContent(viewType, viewId, null); + final DomElement content = contentManager.renderContent(viewType, viewId, null); expect(content.getAttribute('slot'), getPlatformViewSlotName(viewId)); expect(content.getAttribute('id'), getPlatformViewDomId(viewId)); @@ -119,30 +126,31 @@ void testMain() { }); test('slot property has the same value as createPlatformViewSlot', () async { - final DomElement content = - contentManager.renderContent(viewType, viewId, null); + final DomElement content = contentManager.renderContent(viewType, viewId, null); final DomElement slot = createPlatformViewSlot(viewId); final DomElement innerSlot = slot.querySelector('slot')!; - expect(content.getAttribute('slot'), innerSlot.getAttribute('name'), - reason: - 'The slot attribute of the rendered content must match the name attribute of the SLOT of a given viewId'); + expect( + content.getAttribute('slot'), + innerSlot.getAttribute('name'), + reason: + 'The slot attribute of the rendered content must match the name attribute of the SLOT of a given viewId', + ); }); - test('do not modify style.height / style.width if passed by the user (anotherViewType)', - () async { - final DomElement content = - contentManager.renderContent(anotherViewType, viewId, null); - final DomElement userContent = content.querySelector('div')!; - expect(userContent.style.height, 'auto'); - expect(userContent.style.width, '55%'); - }); + test( + 'do not modify style.height / style.width if passed by the user (anotherViewType)', + () async { + final DomElement content = contentManager.renderContent(anotherViewType, viewId, null); + final DomElement userContent = content.querySelector('div')!; + expect(userContent.style.height, 'auto'); + expect(userContent.style.width, '55%'); + }, + ); test('returns cached instances of already-rendered content', () async { - final DomElement firstRender = - contentManager.renderContent(viewType, viewId, null); - final DomElement anotherRender = - contentManager.renderContent(viewType, viewId, null); + final DomElement firstRender = contentManager.renderContent(viewType, viewId, null); + final DomElement anotherRender = contentManager.renderContent(viewType, viewId, null); expect(firstRender, same(anotherRender)); }); @@ -212,10 +220,7 @@ void testMain() { viewId, {'tagName': 'table'}, ); - expect( - contentManager.getViewById(viewId), - content0.querySelector('table'), - ); + expect(contentManager.getViewById(viewId), content0.querySelector('table')); expect(contentManager.isVisible(viewId), isTrue); expect(contentManager.isInvisible(viewId), isFalse); @@ -224,10 +229,7 @@ void testMain() { viewId + 1, {'tagName': 'script'}, ); - expect( - contentManager.getViewById(viewId + 1), - content1.querySelector('script'), - ); + expect(contentManager.getViewById(viewId + 1), content1.querySelector('script')); expect(contentManager.isVisible(viewId + 1), isFalse); expect(contentManager.isInvisible(viewId + 1), isTrue); @@ -236,10 +238,7 @@ void testMain() { viewId + 2, {'tagName': 'p'}, ); - expect( - contentManager.getViewById(viewId + 2), - content2.querySelector('p'), - ); + expect(contentManager.getViewById(viewId + 2), content2.querySelector('p')); expect(contentManager.isVisible(viewId + 2), isTrue); expect(contentManager.isInvisible(viewId + 2), isFalse); }); diff --git a/lib/web_ui/test/engine/platform_views/message_handler_test.dart b/lib/web_ui/test/engine/platform_views/message_handler_test.dart index 42b7059e8299f..8ec8b25e5b6ef 100644 --- a/lib/web_ui/test/engine/platform_views/message_handler_test.dart +++ b/lib/web_ui/test/engine/platform_views/message_handler_test.dart @@ -31,12 +31,14 @@ void testMain() { }); group('"create" message', () { - test('unregistered viewType, fails with descriptive exception', - () async { + test('unregistered viewType, fails with descriptive exception', () async { final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( contentManager: contentManager, ); - final Map arguments = _getCreateArguments(platformViewType, platformViewId); + final Map arguments = _getCreateArguments( + platformViewType, + platformViewId, + ); messageHandler.handlePlatformViewCall('create', arguments, completer.complete); @@ -51,13 +53,15 @@ void testMain() { }); test('duplicate viewId, fails with descriptive exception', () async { - contentManager.registerFactory( - platformViewType, (int id) => createDomHTMLDivElement()); + contentManager.registerFactory(platformViewType, (int id) => createDomHTMLDivElement()); contentManager.renderContent(platformViewType, platformViewId, null); final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( contentManager: contentManager, ); - final Map arguments = _getCreateArguments(platformViewType, platformViewId); + final Map arguments = _getCreateArguments( + platformViewType, + platformViewId, + ); messageHandler.handlePlatformViewCall('create', arguments, completer.complete); @@ -70,32 +74,42 @@ void testMain() { } }); - test('returns a successEnvelope when the view is created normally', - () async { + test('returns a successEnvelope when the view is created normally', () async { contentManager.registerFactory( - platformViewType, (int id) => createDomHTMLDivElement()..id = 'success'); + platformViewType, + (int id) => createDomHTMLDivElement()..id = 'success', + ); final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( contentManager: contentManager, ); - final Map arguments = _getCreateArguments(platformViewType, platformViewId); + final Map arguments = _getCreateArguments( + platformViewType, + platformViewId, + ); messageHandler.handlePlatformViewCall('create', arguments, completer.complete); final ByteData? response = await completer.future; - expect(codec.decodeEnvelope(response!), isNull, - reason: - 'The response should be a success envelope, with null in it.'); + expect( + codec.decodeEnvelope(response!), + isNull, + reason: 'The response should be a success envelope, with null in it.', + ); }); - test('caches the created view so it can be retrieved (not on the DOM)', - () async { + test('caches the created view so it can be retrieved (not on the DOM)', () async { final DomElement platformViewsContainer = createDomElement('pv-container'); contentManager.registerFactory( - platformViewType, (int id) => createDomHTMLDivElement()..id = 'success'); + platformViewType, + (int id) => createDomHTMLDivElement()..id = 'success', + ); final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( contentManager: contentManager, ); - final Map arguments = _getCreateArguments(platformViewType, platformViewId); + final Map arguments = _getCreateArguments( + platformViewType, + platformViewId, + ); messageHandler.handlePlatformViewCall('create', arguments, completer.complete); @@ -109,7 +123,7 @@ void testMain() { expect( contentManager.knowsViewId(platformViewId), isTrue, - reason: 'The contentManager should have pre-rendered the platformViewId.' + reason: 'The contentManager should have pre-rendered the platformViewId.', ); expect( contentManager.getViewById(platformViewId).matches('div#success'), @@ -195,7 +209,10 @@ void testMain() { final PlatformViewMessageHandler messageHandler = PlatformViewMessageHandler( contentManager: contentManager, ); - final Map arguments = _getCreateArguments(platformViewType, platformViewId); + final Map arguments = _getCreateArguments( + platformViewType, + platformViewId, + ); expect(() { messageHandler.handlePlatformViewCall('create', arguments, (_) {}); @@ -218,9 +235,11 @@ void testMain() { messageHandler.handlePlatformViewCall('dispose', platformViewId, completer.complete); final ByteData? response = await completer.future; - expect(codec.decodeEnvelope(response!), isNull, - reason: - 'The response should be a success envelope, with null in it.'); + expect( + codec.decodeEnvelope(response!), + isNull, + reason: 'The response should be a success envelope, with null in it.', + ); }); test('never fails, even for unknown viewIds', () async { @@ -231,9 +250,11 @@ void testMain() { messageHandler.handlePlatformViewCall('dispose', platformViewId, completer.complete); final int disposedViewId = await viewIdCompleter.future; - expect(disposedViewId, platformViewId, - reason: - 'The viewId to dispose should be passed to the contentManager'); + expect( + disposedViewId, + platformViewId, + reason: 'The viewId to dispose should be passed to the contentManager', + ); }); }); }); @@ -241,8 +262,7 @@ void testMain() { } class _FakePlatformViewManager extends PlatformViewManager { - _FakePlatformViewManager(void Function(int) clearFunction) - : _clearPlatformView = clearFunction; + _FakePlatformViewManager(void Function(int) clearFunction) : _clearPlatformView = clearFunction; final void Function(int) _clearPlatformView; diff --git a/lib/web_ui/test/engine/platform_views/slots_test.dart b/lib/web_ui/test/engine/platform_views/slots_test.dart index bc6d175209c8d..a1ffa36cdefd6 100644 --- a/lib/web_ui/test/engine/platform_views/slots_test.dart +++ b/lib/web_ui/test/engine/platform_views/slots_test.dart @@ -15,9 +15,7 @@ void testMain() { const int viewId = 6; group('createPlatformViewSlot', () { - test( - 'can render slot, even for views that might have never been rendered before', - () async { + test('can render slot, even for views that might have never been rendered before', () async { final DomElement slot = createPlatformViewSlot(viewId); expect(slot, isNotNull); expect(slot.querySelector('slot'), isNotNull); @@ -25,13 +23,17 @@ void testMain() { test('rendered markup contains required attributes', () async { final DomElement slot = createPlatformViewSlot(viewId); - expect(slot.style.pointerEvents, 'auto', - reason: - 'Should re-enable pointer events for the contents of the view.'); + expect( + slot.style.pointerEvents, + 'auto', + reason: 'Should re-enable pointer events for the contents of the view.', + ); final DomElement innerSlot = slot.querySelector('slot')!; - expect(innerSlot.getAttribute('name'), contains('$viewId'), - reason: - 'The name attribute of the inner SLOT tag must refer to the viewId.'); + expect( + innerSlot.getAttribute('name'), + contains('$viewId'), + reason: 'The name attribute of the inner SLOT tag must refer to the viewId.', + ); }); }); }); diff --git a/lib/web_ui/test/engine/pointer_binding/event_position_helper_test.dart b/lib/web_ui/test/engine/pointer_binding/event_position_helper_test.dart index 14f41dccbbf9a..35ea7b8df4d84 100644 --- a/lib/web_ui/test/engine/pointer_binding/event_position_helper_test.dart +++ b/lib/web_ui/test/engine/pointer_binding/event_position_helper_test.dart @@ -53,9 +53,12 @@ void doTests() { ..top = '100px' ..left = '120px'; - rootElement.addEventListener('click', createDomEventListener((DomEvent e) { - events.add(e); - })); + rootElement.addEventListener( + 'click', + createDomEventListener((DomEvent e) { + events.add(e); + }), + ); }); tearDown(() { @@ -65,14 +68,14 @@ void doTests() { test('Event dispatched by target returns offsetX, offsetY', () async { // Fire an event contained within target... - final DomMouseEvent event = await dispatchAndCatch(rootElement, createDomPointerEvent( - 'click', - { + final DomMouseEvent event = await dispatchAndCatch( + rootElement, + createDomPointerEvent('click', { 'bubbles': true, 'clientX': 10, 'clientY': 20, - } - )); + }), + ); expect(event.offsetX, 10); expect(event.offsetY, 20); @@ -85,14 +88,14 @@ void doTests() { test('Event dispatched on child re-computes offset (offsetX/Y invalid)', () async { // Fire an event contained within target... - final DomMouseEvent event = await dispatchAndCatch(eventSource, createDomPointerEvent( - 'click', - { + final DomMouseEvent event = await dispatchAndCatch( + eventSource, + createDomPointerEvent('click', { 'bubbles': true, // So it can be caught in `target` 'clientX': 140, // x = 20px into `eventSource`. 'clientY': 110, // y = 10px into `eventSource`. - } - )); + }), + ); expect(event.offsetX, 20); expect(event.offsetY, 10); @@ -122,10 +125,7 @@ void doTests() { 'clientY': 20, }); - expect( - () => computeEventOffsetToTarget(moveEvent, view), - throwsA(anything), - ); + expect(() => computeEventOffsetToTarget(moveEvent, view), throwsA(anything)); expect( () => computeEventOffsetToTarget(moveEvent, view, eventTarget: input), @@ -137,8 +137,12 @@ void doTests() { // Fill this in to test _computeOffsetForTalkbackEvent }, skip: 'To be implemented!'); - test('Event dispatched on text editing node computes offset with framework geometry', () async { - // Fill this in to test _computeOffsetForInputs - }, skip: 'To be implemented!'); + test( + 'Event dispatched on text editing node computes offset with framework geometry', + () async { + // Fill this in to test _computeOffsetForInputs + }, + skip: 'To be implemented!', + ); }); } diff --git a/lib/web_ui/test/engine/pointer_binding_test.dart b/lib/web_ui/test/engine/pointer_binding_test.dart index 9757758ffcb8f..55c78fd5913cd 100644 --- a/lib/web_ui/test/engine/pointer_binding_test.dart +++ b/lib/web_ui/test/engine/pointer_binding_test.dart @@ -43,7 +43,6 @@ void testMain() { late PointerBinding instance; late double dpi; - KeyboardConverter createKeyboardConverter(List keyDataList) { return KeyboardConverter((ui.KeyData key) { keyDataList.add(key); @@ -77,10 +76,7 @@ void testMain() { final MockSafariPointerEventWorkaround mockSafariWorkaround = MockSafariPointerEventWorkaround(); - final PointerBinding instance = PointerBinding( - view, - safariWorkaround: mockSafariWorkaround, - ); + final PointerBinding instance = PointerBinding(view, safariWorkaround: mockSafariWorkaround); expect(mockSafariWorkaround.workAroundInvoked, isIosSafari); instance.dispose(); }, skip: !isSafari); @@ -107,8 +103,7 @@ void testMain() { expect(event.client.x, equals(100)); expect(event.client.y, equals(101)); - event = expectCorrectType( - context.mouseDown(clientX: 110, clientY: 111, button: 2, buttons: 2)); + event = expectCorrectType(context.mouseDown(clientX: 110, clientY: 111, button: 2, buttons: 2)); expect(event.type, equals('pointerdown')); expect(event.pointerId, equals(1)); expect(event.button, equals(2)); @@ -116,10 +111,12 @@ void testMain() { expect(event.client.x, equals(110)); expect(event.client.y, equals(111)); - events = expectCorrectTypes(context.multiTouchDown(const <_TouchDetails>[ - _TouchDetails(pointer: 100, clientX: 120, clientY: 121), - _TouchDetails(pointer: 101, clientX: 122, clientY: 123), - ])); + events = expectCorrectTypes( + context.multiTouchDown(const <_TouchDetails>[ + _TouchDetails(pointer: 100, clientX: 120, clientY: 121), + _TouchDetails(pointer: 101, clientX: 122, clientY: 123), + ]), + ); expect(events.length, equals(2)); expect(events[0].type, equals('pointerdown')); expect(events[0].pointerId, equals(100)); @@ -142,8 +139,9 @@ void testMain() { expect(event.client.x, equals(200)); expect(event.client.y, equals(201)); - event = expectCorrectType(context.mouseMove( - clientX: 210, clientY: 211, button: _kNoButtonChange, buttons: 6)); + event = expectCorrectType( + context.mouseMove(clientX: 210, clientY: 211, button: _kNoButtonChange, buttons: 6), + ); expect(event.type, equals('pointermove')); expect(event.pointerId, equals(1)); expect(event.button, equals(-1)); @@ -151,8 +149,7 @@ void testMain() { expect(event.client.x, equals(210)); expect(event.client.y, equals(211)); - event = expectCorrectType( - context.mouseMove(clientX: 212, clientY: 213, button: 2, buttons: 6)); + event = expectCorrectType(context.mouseMove(clientX: 212, clientY: 213, button: 2, buttons: 6)); expect(event.type, equals('pointermove')); expect(event.pointerId, equals(1)); expect(event.button, equals(2)); @@ -160,8 +157,7 @@ void testMain() { expect(event.client.x, equals(212)); expect(event.client.y, equals(213)); - event = expectCorrectType( - context.mouseMove(clientX: 214, clientY: 215, button: 2, buttons: 1)); + event = expectCorrectType(context.mouseMove(clientX: 214, clientY: 215, button: 2, buttons: 1)); expect(event.type, equals('pointermove')); expect(event.pointerId, equals(1)); expect(event.button, equals(2)); @@ -169,10 +165,12 @@ void testMain() { expect(event.client.x, equals(214)); expect(event.client.y, equals(215)); - events = expectCorrectTypes(context.multiTouchMove(const <_TouchDetails>[ - _TouchDetails(pointer: 102, clientX: 220, clientY: 221), - _TouchDetails(pointer: 103, clientX: 222, clientY: 223), - ])); + events = expectCorrectTypes( + context.multiTouchMove(const <_TouchDetails>[ + _TouchDetails(pointer: 102, clientX: 220, clientY: 221), + _TouchDetails(pointer: 103, clientX: 222, clientY: 223), + ]), + ); expect(events.length, equals(2)); expect(events[0].type, equals('pointermove')); expect(events[0].pointerId, equals(102)); @@ -203,8 +201,7 @@ void testMain() { expect(event.client.x, equals(300)); expect(event.client.y, equals(301)); - event = expectCorrectType( - context.mouseUp(clientX: 310, clientY: 311, button: 2)); + event = expectCorrectType(context.mouseUp(clientX: 310, clientY: 311, button: 2)); expect(event.type, equals('pointerup')); expect(event.pointerId, equals(1)); expect(event.button, equals(2)); @@ -212,10 +209,12 @@ void testMain() { expect(event.client.x, equals(310)); expect(event.client.y, equals(311)); - events = expectCorrectTypes(context.multiTouchUp(const <_TouchDetails>[ - _TouchDetails(pointer: 104, clientX: 320, clientY: 321), - _TouchDetails(pointer: 105, clientX: 322, clientY: 323), - ])); + events = expectCorrectTypes( + context.multiTouchUp(const <_TouchDetails>[ + _TouchDetails(pointer: 104, clientX: 320, clientY: 321), + _TouchDetails(pointer: 105, clientX: 322, clientY: 323), + ]), + ); expect(events.length, equals(2)); expect(events[0].type, equals('pointerup')); expect(events[0].pointerId, equals(104)); @@ -238,10 +237,12 @@ void testMain() { expect(event.client.x, equals(400)); expect(event.client.y, equals(401)); - events = expectCorrectTypes(context.multiTouchCancel(const <_TouchDetails>[ - _TouchDetails(pointer: 106, clientX: 500, clientY: 501), - _TouchDetails(pointer: 107, clientX: 502, clientY: 503), - ])); + events = expectCorrectTypes( + context.multiTouchCancel(const <_TouchDetails>[ + _TouchDetails(pointer: 106, clientX: 500, clientY: 501), + _TouchDetails(pointer: 107, clientX: 502, clientY: 503), + ]), + ); expect(events.length, equals(2)); expect(events[0].type, equals('pointercancel')); expect(events[0].pointerId, equals(106)); @@ -274,37 +275,34 @@ void testMain() { // capture phase is to allow platform views and native text fields to receive // the event first. This way, they can potentially handle the event and stop // its propagation to prevent Flutter from receiving and handling it. - test( - 'event listeners are attached to the bubble phase', - () { - final _BasicEventContext context = _PointerEventContext(); - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + test('event listeners are attached to the bubble phase', () { + final _BasicEventContext context = _PointerEventContext(); + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - final DomElement child = createDomHTMLDivElement(); - rootElement.append(child); + final DomElement child = createDomHTMLDivElement(); + rootElement.append(child); - final DomEventListener stopPropagationListener = createDomEventListener((DomEvent event) { - event.stopPropagation(); - }); + final DomEventListener stopPropagationListener = createDomEventListener((DomEvent event) { + event.stopPropagation(); + }); - // The event reaches `PointerBinding` as expected. - child.dispatchEvent(context.primaryDown()); - expect(packets, isNotEmpty); - packets.clear(); + // The event reaches `PointerBinding` as expected. + child.dispatchEvent(context.primaryDown()); + expect(packets, isNotEmpty); + packets.clear(); - // The child stops propagation so the event doesn't reach `PointerBinding`. - final DomEvent event = context.primaryDown(); - child.addEventListener(event.type, stopPropagationListener); - child.dispatchEvent(event); - expect(packets, isEmpty); - packets.clear(); + // The child stops propagation so the event doesn't reach `PointerBinding`. + final DomEvent event = context.primaryDown(); + child.addEventListener(event.type, stopPropagationListener); + child.dispatchEvent(event); + expect(packets, isEmpty); + packets.clear(); - child.remove(); - }, - ); + child.remove(); + }); test('allows default on touchstart events', () async { final event = createDomEvent('Event', 'touchstart'); @@ -318,276 +316,259 @@ void testMain() { ); }); - test( - 'can receive pointer events on the app root', - () { - final _BasicEventContext context = _PointerEventContext(); - ui.PointerDataPacket? receivedPacket; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - receivedPacket = packet; - }; + test('can receive pointer events on the app root', () { + final _BasicEventContext context = _PointerEventContext(); + ui.PointerDataPacket? receivedPacket; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + receivedPacket = packet; + }; - rootElement.dispatchEvent(context.primaryDown()); + rootElement.dispatchEvent(context.primaryDown()); - expect(receivedPacket, isNotNull); - expect(receivedPacket!.data[0].buttons, equals(1)); - }, - ); + expect(receivedPacket, isNotNull); + expect(receivedPacket!.data[0].buttons, equals(1)); + }); - test( - 'does create an add event if got a pointerdown', - () { - final _BasicEventContext context = _PointerEventContext(); - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + test('does create an add event if got a pointerdown', () { + final _BasicEventContext context = _PointerEventContext(); + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; + + rootElement.dispatchEvent(context.primaryDown()); + + expect(packets, hasLength(1)); + expect(packets.single.data, hasLength(2)); + + expect(packets.single.data[0].change, equals(ui.PointerChange.add)); + expect(packets.single.data[1].change, equals(ui.PointerChange.down)); + }); + test('synthesize modifier keys left down event if left or right are not pressed', () { + final _BasicEventContext context = _PointerEventContext(); + + // Should synthesize a modifier left key down event when DOM event indicates + // that the modifier key is pressed and known pressing state doesn't contain + // the modifier left key nor the modifier right key. + void shouldSynthesizeLeftDownIfNotPressed(String key) { + final int physicalLeft = kWebToPhysicalKey['${key}Left']!; + final int physicalRight = kWebToPhysicalKey['${key}Right']!; + final int logicalLeft = kWebLogicalLocationMap[key]![kLocationLeft]!; + + expect(keyboardConverter.keyIsPressed(physicalLeft), false); + expect(keyboardConverter.keyIsPressed(physicalRight), false); rootElement.dispatchEvent(context.primaryDown()); + expect(keyDataList.length, 1); + expectKeyData( + keyDataList.last, + type: ui.KeyEventType.down, + deviceType: ui.KeyEventDeviceType.keyboard, + physical: physicalLeft, + logical: logicalLeft, + character: null, + synthesized: true, + ); + keyDataList.clear(); + keyboardConverter.clearPressedKeys(); + } - expect(packets, hasLength(1)); - expect(packets.single.data, hasLength(2)); + context.altPressed = true; + shouldSynthesizeLeftDownIfNotPressed('Alt'); + context.unpressAllModifiers(); + context.ctrlPressed = true; + shouldSynthesizeLeftDownIfNotPressed('Control'); + context.unpressAllModifiers(); + context.metaPressed = true; + shouldSynthesizeLeftDownIfNotPressed('Meta'); + context.unpressAllModifiers(); + context.shiftPressed = true; + shouldSynthesizeLeftDownIfNotPressed('Shift'); + context.unpressAllModifiers(); + }); - expect(packets.single.data[0].change, equals(ui.PointerChange.add)); - expect(packets.single.data[1].change, equals(ui.PointerChange.down)); - }, - ); + test('should not synthesize modifier keys down event if left or right are pressed', () { + final _BasicEventContext context = _PointerEventContext(); - test( - 'synthesize modifier keys left down event if left or right are not pressed', - () { - final _BasicEventContext context = _PointerEventContext(); - - // Should synthesize a modifier left key down event when DOM event indicates - // that the modifier key is pressed and known pressing state doesn't contain - // the modifier left key nor the modifier right key. - void shouldSynthesizeLeftDownIfNotPressed(String key) { - final int physicalLeft = kWebToPhysicalKey['${key}Left']!; - final int physicalRight = kWebToPhysicalKey['${key}Right']!; - final int logicalLeft = kWebLogicalLocationMap[key]![kLocationLeft]!; - - expect(keyboardConverter.keyIsPressed(physicalLeft), false); - expect(keyboardConverter.keyIsPressed(physicalRight), false); - rootElement.dispatchEvent(context.primaryDown()); - expect(keyDataList.length, 1); - expectKeyData(keyDataList.last, - type: ui.KeyEventType.down, - deviceType: ui.KeyEventDeviceType.keyboard, - physical: physicalLeft, - logical: logicalLeft, - character: null, - synthesized: true, - ); - keyDataList.clear(); - keyboardConverter.clearPressedKeys(); - } + // Should not synthesize a modifier down event when DOM event indicates + // that the modifier key is pressed and known pressing state contains + // the modifier left key. + void shouldNotSynthesizeDownIfLeftPressed(String key, int modifiers) { + final int physicalLeft = kWebToPhysicalKey['${key}Left']!; + final int physicalRight = kWebToPhysicalKey['${key}Right']!; - context.altPressed = true; - shouldSynthesizeLeftDownIfNotPressed('Alt'); - context.unpressAllModifiers(); - context.ctrlPressed = true; - shouldSynthesizeLeftDownIfNotPressed('Control'); - context.unpressAllModifiers(); - context.metaPressed = true; - shouldSynthesizeLeftDownIfNotPressed('Meta'); - context.unpressAllModifiers(); - context.shiftPressed = true; - shouldSynthesizeLeftDownIfNotPressed('Shift'); - context.unpressAllModifiers(); - }, - ); + keyboardConverter.handleEvent(keyDownEvent('${key}Left', key, modifiers, kLocationLeft)); + expect(keyboardConverter.keyIsPressed(physicalLeft), true); + expect(keyboardConverter.keyIsPressed(physicalRight), false); + keyDataList.clear(); // Remove key data generated by handleEvent - test( - 'should not synthesize modifier keys down event if left or right are pressed', - () { - final _BasicEventContext context = _PointerEventContext(); - - // Should not synthesize a modifier down event when DOM event indicates - // that the modifier key is pressed and known pressing state contains - // the modifier left key. - void shouldNotSynthesizeDownIfLeftPressed(String key, int modifiers) { - final int physicalLeft = kWebToPhysicalKey['${key}Left']!; - final int physicalRight = kWebToPhysicalKey['${key}Right']!; - - keyboardConverter.handleEvent(keyDownEvent('${key}Left', key, modifiers, kLocationLeft)); - expect(keyboardConverter.keyIsPressed(physicalLeft), true); - expect(keyboardConverter.keyIsPressed(physicalRight), false); - keyDataList.clear(); // Remove key data generated by handleEvent - - rootElement.dispatchEvent(context.primaryDown()); - expect(keyDataList.length, 0); - keyboardConverter.clearPressedKeys(); - } + rootElement.dispatchEvent(context.primaryDown()); + expect(keyDataList.length, 0); + keyboardConverter.clearPressedKeys(); + } - // Should not synthesize a modifier down event when DOM event indicates - // that the modifier key is pressed and known pressing state contains - // the modifier right key. - void shouldNotSynthesizeDownIfRightPressed(String key, int modifiers) { - final int physicalLeft = kWebToPhysicalKey['${key}Left']!; - final int physicalRight = kWebToPhysicalKey['${key}Right']!; - - keyboardConverter.handleEvent(keyDownEvent('${key}Right', key, modifiers, kLocationRight)); - expect(keyboardConverter.keyIsPressed(physicalLeft), false); - expect(keyboardConverter.keyIsPressed(physicalRight), true); - keyDataList.clear(); // Remove key data generated by handleEvent - - rootElement.dispatchEvent(context.primaryDown()); - expect(keyDataList.length, 0); - keyboardConverter.clearPressedKeys(); - } + // Should not synthesize a modifier down event when DOM event indicates + // that the modifier key is pressed and known pressing state contains + // the modifier right key. + void shouldNotSynthesizeDownIfRightPressed(String key, int modifiers) { + final int physicalLeft = kWebToPhysicalKey['${key}Left']!; + final int physicalRight = kWebToPhysicalKey['${key}Right']!; - context.altPressed = true; - shouldNotSynthesizeDownIfLeftPressed('Alt', kAlt); - shouldNotSynthesizeDownIfRightPressed('Alt', kAlt); - context.unpressAllModifiers(); - context.ctrlPressed = true; - shouldNotSynthesizeDownIfLeftPressed('Control', kCtrl); - shouldNotSynthesizeDownIfRightPressed('Control', kCtrl); - context.unpressAllModifiers(); - context.metaPressed = true; - shouldNotSynthesizeDownIfLeftPressed('Meta', kMeta); - shouldNotSynthesizeDownIfRightPressed('Meta', kMeta); - context.unpressAllModifiers(); - context.shiftPressed = true; - shouldNotSynthesizeDownIfLeftPressed('Shift', kShift); - shouldNotSynthesizeDownIfRightPressed('Shift', kShift); - context.unpressAllModifiers(); - }, - ); + keyboardConverter.handleEvent(keyDownEvent('${key}Right', key, modifiers, kLocationRight)); + expect(keyboardConverter.keyIsPressed(physicalLeft), false); + expect(keyboardConverter.keyIsPressed(physicalRight), true); + keyDataList.clear(); // Remove key data generated by handleEvent - test( - 'synthesize modifier keys up event if left or right are pressed', - () { - final _BasicEventContext context = _PointerEventContext(); - - // Should synthesize a modifier left key up event when DOM event indicates - // that the modifier key is not pressed and known pressing state contains - // the modifier left key. - void shouldSynthesizeLeftUpIfLeftPressed(String key, int modifiers) { - final int physicalLeft = kWebToPhysicalKey['${key}Left']!; - final int physicalRight = kWebToPhysicalKey['${key}Right']!; - final int logicalLeft = kWebLogicalLocationMap[key]![kLocationLeft]!; - - keyboardConverter.handleEvent(keyDownEvent('${key}Left', key, modifiers, kLocationLeft)); - expect(keyboardConverter.keyIsPressed(physicalLeft), true); - expect(keyboardConverter.keyIsPressed(physicalRight), false); - keyDataList.clear(); // Remove key data generated by handleEvent - - rootElement.dispatchEvent(context.primaryDown()); - expect(keyDataList.length, 1); - expectKeyData(keyDataList.last, - type: ui.KeyEventType.up, - deviceType: ui.KeyEventDeviceType.keyboard, - physical: physicalLeft, - logical: logicalLeft, - character: null, - synthesized: true, - ); - expect(keyboardConverter.keyIsPressed(physicalLeft), false); - keyboardConverter.clearPressedKeys(); - } + rootElement.dispatchEvent(context.primaryDown()); + expect(keyDataList.length, 0); + keyboardConverter.clearPressedKeys(); + } - // Should synthesize a modifier right key up event when DOM event indicates - // that the modifier key is not pressed and known pressing state contains - // the modifier right key. - void shouldSynthesizeRightUpIfRightPressed(String key, int modifiers) { - final int physicalLeft = kWebToPhysicalKey['${key}Left']!; - final int physicalRight = kWebToPhysicalKey['${key}Right']!; - final int logicalRight = kWebLogicalLocationMap[key]![kLocationRight]!; - - keyboardConverter.handleEvent(keyDownEvent('${key}Right', key, modifiers, kLocationRight)); - expect(keyboardConverter.keyIsPressed(physicalLeft), false); - expect(keyboardConverter.keyIsPressed(physicalRight), true); - keyDataList.clear(); // Remove key data generated by handleEvent - - rootElement.dispatchEvent(context.primaryDown()); - expect(keyDataList.length, 1); - expectKeyData(keyDataList.last, - type: ui.KeyEventType.up, - deviceType: ui.KeyEventDeviceType.keyboard, - physical: physicalRight, - logical: logicalRight, - character: null, - synthesized: true, - ); - expect(keyboardConverter.keyIsPressed(physicalRight), false); - keyboardConverter.clearPressedKeys(); - } + context.altPressed = true; + shouldNotSynthesizeDownIfLeftPressed('Alt', kAlt); + shouldNotSynthesizeDownIfRightPressed('Alt', kAlt); + context.unpressAllModifiers(); + context.ctrlPressed = true; + shouldNotSynthesizeDownIfLeftPressed('Control', kCtrl); + shouldNotSynthesizeDownIfRightPressed('Control', kCtrl); + context.unpressAllModifiers(); + context.metaPressed = true; + shouldNotSynthesizeDownIfLeftPressed('Meta', kMeta); + shouldNotSynthesizeDownIfRightPressed('Meta', kMeta); + context.unpressAllModifiers(); + context.shiftPressed = true; + shouldNotSynthesizeDownIfLeftPressed('Shift', kShift); + shouldNotSynthesizeDownIfRightPressed('Shift', kShift); + context.unpressAllModifiers(); + }); - context.altPressed = false; - shouldSynthesizeLeftUpIfLeftPressed('Alt', kAlt); - shouldSynthesizeRightUpIfRightPressed('Alt', kAlt); - context.ctrlPressed = false; - shouldSynthesizeLeftUpIfLeftPressed('Control', kCtrl); - shouldSynthesizeRightUpIfRightPressed('Control', kCtrl); - context.metaPressed = false; - shouldSynthesizeLeftUpIfLeftPressed('Meta', kMeta); - shouldSynthesizeRightUpIfRightPressed('Meta', kMeta); - context.shiftPressed = false; - shouldSynthesizeLeftUpIfLeftPressed('Shift', kShift); - shouldSynthesizeRightUpIfRightPressed('Shift', kShift); - }, - ); + test('synthesize modifier keys up event if left or right are pressed', () { + final _BasicEventContext context = _PointerEventContext(); - test( - 'should not synthesize modifier keys up event if left or right are not pressed', - () { - final _BasicEventContext context = _PointerEventContext(); - - // Should not synthesize a modifier up event when DOM event indicates - // that the modifier key is not pressed and known pressing state does - // not contain the modifier left key nor the modifier right key. - void shouldNotSynthesizeUpIfNotPressed(String key) { - final int physicalLeft = kWebToPhysicalKey['${key}Left']!; - final int physicalRight = kWebToPhysicalKey['${key}Right']!; - - expect(keyboardConverter.keyIsPressed(physicalLeft), false); - expect(keyboardConverter.keyIsPressed(physicalRight), false); - keyDataList.clear(); // Remove key data generated by handleEvent - - rootElement.dispatchEvent(context.primaryDown()); - expect(keyDataList.length, 0); - keyboardConverter.clearPressedKeys(); - } + // Should synthesize a modifier left key up event when DOM event indicates + // that the modifier key is not pressed and known pressing state contains + // the modifier left key. + void shouldSynthesizeLeftUpIfLeftPressed(String key, int modifiers) { + final int physicalLeft = kWebToPhysicalKey['${key}Left']!; + final int physicalRight = kWebToPhysicalKey['${key}Right']!; + final int logicalLeft = kWebLogicalLocationMap[key]![kLocationLeft]!; - context.altPressed = false; - shouldNotSynthesizeUpIfNotPressed('Alt'); - context.ctrlPressed = false; - shouldNotSynthesizeUpIfNotPressed('Control'); - context.metaPressed = false; - shouldNotSynthesizeUpIfNotPressed('Meta'); - context.shiftPressed = false; - shouldNotSynthesizeUpIfNotPressed('Shift'); - }, - ); + keyboardConverter.handleEvent(keyDownEvent('${key}Left', key, modifiers, kLocationLeft)); + expect(keyboardConverter.keyIsPressed(physicalLeft), true); + expect(keyboardConverter.keyIsPressed(physicalRight), false); + keyDataList.clear(); // Remove key data generated by handleEvent - test( - 'should synthesize modifier keys up event for AltGraph', - () { - final _BasicEventContext context = _PointerEventContext(); + rootElement.dispatchEvent(context.primaryDown()); + expect(keyDataList.length, 1); + expectKeyData( + keyDataList.last, + type: ui.KeyEventType.up, + deviceType: ui.KeyEventDeviceType.keyboard, + physical: physicalLeft, + logical: logicalLeft, + character: null, + synthesized: true, + ); + expect(keyboardConverter.keyIsPressed(physicalLeft), false); + keyboardConverter.clearPressedKeys(); + } - final int physicalAltRight = kWebToPhysicalKey['AltRight']!; - final int logicalAltGraph = kWebLogicalLocationMap['AltGraph']![0]!; + // Should synthesize a modifier right key up event when DOM event indicates + // that the modifier key is not pressed and known pressing state contains + // the modifier right key. + void shouldSynthesizeRightUpIfRightPressed(String key, int modifiers) { + final int physicalLeft = kWebToPhysicalKey['${key}Left']!; + final int physicalRight = kWebToPhysicalKey['${key}Right']!; + final int logicalRight = kWebLogicalLocationMap[key]![kLocationRight]!; - // Simulate pressing `AltGr` key. - keyboardConverter.handleEvent(keyDownEvent('AltRight', 'AltGraph')); - expect(keyboardConverter.keyIsPressed(physicalAltRight), true); - keyDataList.clear(); // Remove key data generated by handleEvent. + keyboardConverter.handleEvent(keyDownEvent('${key}Right', key, modifiers, kLocationRight)); + expect(keyboardConverter.keyIsPressed(physicalLeft), false); + expect(keyboardConverter.keyIsPressed(physicalRight), true); + keyDataList.clear(); // Remove key data generated by handleEvent rootElement.dispatchEvent(context.primaryDown()); expect(keyDataList.length, 1); - expectKeyData(keyDataList.last, + expectKeyData( + keyDataList.last, type: ui.KeyEventType.up, deviceType: ui.KeyEventDeviceType.keyboard, - physical: physicalAltRight, - logical: logicalAltGraph, + physical: physicalRight, + logical: logicalRight, character: null, synthesized: true, ); - expect(keyboardConverter.keyIsPressed(physicalAltRight), false); - keyDataList.clear(); - }, - ); + expect(keyboardConverter.keyIsPressed(physicalRight), false); + keyboardConverter.clearPressedKeys(); + } + + context.altPressed = false; + shouldSynthesizeLeftUpIfLeftPressed('Alt', kAlt); + shouldSynthesizeRightUpIfRightPressed('Alt', kAlt); + context.ctrlPressed = false; + shouldSynthesizeLeftUpIfLeftPressed('Control', kCtrl); + shouldSynthesizeRightUpIfRightPressed('Control', kCtrl); + context.metaPressed = false; + shouldSynthesizeLeftUpIfLeftPressed('Meta', kMeta); + shouldSynthesizeRightUpIfRightPressed('Meta', kMeta); + context.shiftPressed = false; + shouldSynthesizeLeftUpIfLeftPressed('Shift', kShift); + shouldSynthesizeRightUpIfRightPressed('Shift', kShift); + }); + + test('should not synthesize modifier keys up event if left or right are not pressed', () { + final _BasicEventContext context = _PointerEventContext(); + + // Should not synthesize a modifier up event when DOM event indicates + // that the modifier key is not pressed and known pressing state does + // not contain the modifier left key nor the modifier right key. + void shouldNotSynthesizeUpIfNotPressed(String key) { + final int physicalLeft = kWebToPhysicalKey['${key}Left']!; + final int physicalRight = kWebToPhysicalKey['${key}Right']!; + + expect(keyboardConverter.keyIsPressed(physicalLeft), false); + expect(keyboardConverter.keyIsPressed(physicalRight), false); + keyDataList.clear(); // Remove key data generated by handleEvent + + rootElement.dispatchEvent(context.primaryDown()); + expect(keyDataList.length, 0); + keyboardConverter.clearPressedKeys(); + } + + context.altPressed = false; + shouldNotSynthesizeUpIfNotPressed('Alt'); + context.ctrlPressed = false; + shouldNotSynthesizeUpIfNotPressed('Control'); + context.metaPressed = false; + shouldNotSynthesizeUpIfNotPressed('Meta'); + context.shiftPressed = false; + shouldNotSynthesizeUpIfNotPressed('Shift'); + }); + + test('should synthesize modifier keys up event for AltGraph', () { + final _BasicEventContext context = _PointerEventContext(); + + final int physicalAltRight = kWebToPhysicalKey['AltRight']!; + final int logicalAltGraph = kWebLogicalLocationMap['AltGraph']![0]!; + + // Simulate pressing `AltGr` key. + keyboardConverter.handleEvent(keyDownEvent('AltRight', 'AltGraph')); + expect(keyboardConverter.keyIsPressed(physicalAltRight), true); + keyDataList.clear(); // Remove key data generated by handleEvent. + + rootElement.dispatchEvent(context.primaryDown()); + expect(keyDataList.length, 1); + expectKeyData( + keyDataList.last, + type: ui.KeyEventType.up, + deviceType: ui.KeyEventDeviceType.keyboard, + physical: physicalAltRight, + logical: logicalAltGraph, + character: null, + synthesized: true, + ); + expect(keyboardConverter.keyIsPressed(physicalAltRight), false); + keyDataList.clear(); + }); test( 'correctly detects events on the semantics placeholder', @@ -598,15 +579,11 @@ void testMain() { packets.add(packet); }; - final DomElement semanticsPlaceholder = - createDomElement('flt-semantics-placeholder'); + final DomElement semanticsPlaceholder = createDomElement('flt-semantics-placeholder'); rootElement.append(semanticsPlaceholder); // Press on the semantics placeholder. - semanticsPlaceholder.dispatchEvent(context.primaryDown( - clientX: 10.0, - clientY: 10.0, - )); + semanticsPlaceholder.dispatchEvent(context.primaryDown(clientX: 10.0, clientY: 10.0)); expect(packets, hasLength(1)); expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.add)); @@ -616,10 +593,7 @@ void testMain() { packets.clear(); // Drag on the semantics placeholder. - semanticsPlaceholder.dispatchEvent(context.primaryMove( - clientX: 12.0, - clientY: 10.0, - )); + semanticsPlaceholder.dispatchEvent(context.primaryMove(clientX: 12.0, clientY: 10.0)); expect(packets, hasLength(1)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.move)); @@ -628,10 +602,7 @@ void testMain() { packets.clear(); // Keep dragging. - semanticsPlaceholder.dispatchEvent(context.primaryMove( - clientX: 15.0, - clientY: 10.0, - )); + semanticsPlaceholder.dispatchEvent(context.primaryMove(clientX: 15.0, clientY: 10.0)); expect(packets[0].data, hasLength(1)); expect(packets[0].data[0].change, equals(ui.PointerChange.move)); expect(packets[0].data[0].physicalX, equals(15.0 * dpi)); @@ -639,10 +610,7 @@ void testMain() { packets.clear(); // Release the pointer on the semantics placeholder. - rootElement.dispatchEvent(context.primaryUp( - clientX: 100.0, - clientY: 200.0, - )); + rootElement.dispatchEvent(context.primaryUp(clientX: 100.0, clientY: 200.0)); expect(packets, hasLength(1)); expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.move)); @@ -660,58 +628,46 @@ void testMain() { // BUTTONED ADAPTERS - test( - 'creates an add event if the first pointer activity is a hover', - () { - final _ButtonedEventMixin context = _PointerEventContext(); - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + test('creates an add event if the first pointer activity is a hover', () { + final _ButtonedEventMixin context = _PointerEventContext(); + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - rootElement.dispatchEvent(context.hover()); + rootElement.dispatchEvent(context.hover()); - expect(packets, hasLength(1)); - expect(packets.single.data, hasLength(2)); + expect(packets, hasLength(1)); + expect(packets.single.data, hasLength(2)); - expect(packets.single.data[0].change, equals(ui.PointerChange.add)); - expect(packets.single.data[0].synthesized, isTrue); - expect(packets.single.data[1].change, equals(ui.PointerChange.hover)); - }, - ); + expect(packets.single.data[0].change, equals(ui.PointerChange.add)); + expect(packets.single.data[0].synthesized, isTrue); + expect(packets.single.data[1].change, equals(ui.PointerChange.hover)); + }); - test( - 'sends a pointermove event instead of the second pointerdown in a row', - () { - final _ButtonedEventMixin context = _PointerEventContext(); - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + test('sends a pointermove event instead of the second pointerdown in a row', () { + final _ButtonedEventMixin context = _PointerEventContext(); + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - rootElement.dispatchEvent(context.primaryDown( - clientX: 10.0, - clientY: 10.0, - )); - expect(packets, hasLength(1)); - // An add will be synthesized. - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, isTrue); - expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - packets.clear(); + rootElement.dispatchEvent(context.primaryDown(clientX: 10.0, clientY: 10.0)); + expect(packets, hasLength(1)); + // An add will be synthesized. + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[1].change, equals(ui.PointerChange.down)); + packets.clear(); - rootElement.dispatchEvent(context.primaryDown( - clientX: 20.0, - clientY: 20.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].buttons, equals(1)); - packets.clear(); - }, - ); + rootElement.dispatchEvent(context.primaryDown(clientX: 20.0, clientY: 20.0)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].buttons, equals(1)); + packets.clear(); + }); test('wheel event - preventDefault called', () { // Synthesize a 'wheel' event. @@ -730,13 +686,11 @@ void testMain() { test('wheel event - framework can stop preventDefault (allowPlatformDefault)', () { // The framework calls `data.respond(allowPlatformDefault: true)` ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packet.data.where( - (ui.PointerData datum) => datum.signalKind == ui.PointerSignalKind.scroll - ).forEach( - (ui.PointerData datum) { - datum.respond(allowPlatformDefault: true); - } - ); + packet.data + .where((ui.PointerData datum) => datum.signalKind == ui.PointerSignalKind.scroll) + .forEach((ui.PointerData datum) { + datum.respond(allowPlatformDefault: true); + }); }; // Synthesize a 'wheel' event. @@ -756,15 +710,13 @@ void testMain() { test('wheel event - once allowPlatformDefault is set to true, it cannot be rolled back', () { // The framework calls `data.respond(allowPlatformDefault: true)` ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packet.data.where( - (ui.PointerData datum) => datum.signalKind == ui.PointerSignalKind.scroll - ).forEach( - (ui.PointerData datum) { - datum.respond(allowPlatformDefault: false); - datum.respond(allowPlatformDefault: true); - datum.respond(allowPlatformDefault: false); - } - ); + packet.data + .where((ui.PointerData datum) => datum.signalKind == ui.PointerSignalKind.scroll) + .forEach((ui.PointerData datum) { + datum.respond(allowPlatformDefault: false); + datum.respond(allowPlatformDefault: true); + datum.respond(allowPlatformDefault: false); + }); }; // Synthesize a 'wheel' event. @@ -781,179 +733,149 @@ void testMain() { expect(event.defaultPrevented, isFalse); }); - test( - 'does synthesize add or hover or move for scroll', - () { - final _ButtonedEventMixin context = _PointerEventContext(); - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + test('does synthesize add or hover or move for scroll', () { + final _ButtonedEventMixin context = _PointerEventContext(); + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - rootElement.dispatchEvent(context.wheel( - buttons: 0, - clientX: 10, - clientY: 10, - deltaX: 10, - deltaY: 10, - )); + rootElement.dispatchEvent( + context.wheel(buttons: 0, clientX: 10, clientY: 10, deltaX: 10, deltaY: 10), + ); - rootElement.dispatchEvent(context.wheel( - buttons: 0, - clientX: 20, - clientY: 50, - deltaX: 10, - deltaY: 10, - )); - - rootElement.dispatchEvent(context.mouseDown( - button: 0, - buttons: 1, - clientX: 20.0, - clientY: 50.0, - )); - - rootElement.dispatchEvent(context.wheel( - buttons: 1, - clientX: 30, - clientY: 60, - deltaX: 10, - deltaY: 10, - )); - - expect(packets, hasLength(4)); - - // An add will be synthesized. - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].pointerIdentifier, equals(0)); - expect(packets[0].data[0].synthesized, isTrue); - expect(packets[0].data[0].physicalX, equals(10.0 * dpi)); - expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); - expect(packets[0].data[0].physicalDeltaX, equals(0.0)); - expect(packets[0].data[0].physicalDeltaY, equals(0.0)); + rootElement.dispatchEvent( + context.wheel(buttons: 0, clientX: 20, clientY: 50, deltaX: 10, deltaY: 10), + ); - expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); - expect( - packets[0].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); - expect(packets[0].data[1].pointerIdentifier, equals(0)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].physicalX, equals(10.0 * dpi)); - expect(packets[0].data[1].physicalY, equals(10.0 * dpi)); - expect(packets[0].data[1].physicalDeltaX, equals(0.0)); - expect(packets[0].data[1].physicalDeltaY, equals(0.0)); - - // A hover will be synthesized. - expect(packets[1].data, hasLength(2)); - expect(packets[1].data[0].change, equals(ui.PointerChange.hover)); - expect(packets[1].data[0].pointerIdentifier, equals(0)); - expect(packets[1].data[0].synthesized, isTrue); - expect(packets[1].data[0].physicalX, equals(20.0 * dpi)); - expect(packets[1].data[0].physicalY, equals(50.0 * dpi)); - expect(packets[1].data[0].physicalDeltaX, equals(10.0 * dpi)); - expect(packets[1].data[0].physicalDeltaY, equals(40.0 * dpi)); - - expect(packets[1].data[1].change, equals(ui.PointerChange.hover)); - expect( - packets[1].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); - expect(packets[1].data[1].pointerIdentifier, equals(0)); - expect(packets[1].data[1].synthesized, isFalse); - expect(packets[1].data[1].physicalX, equals(20.0 * dpi)); - expect(packets[1].data[1].physicalY, equals(50.0 * dpi)); - expect(packets[1].data[1].physicalDeltaX, equals(0.0)); - expect(packets[1].data[1].physicalDeltaY, equals(0.0)); - - // No synthetic pointer data for down event. - expect(packets[2].data, hasLength(1)); - expect(packets[2].data[0].change, equals(ui.PointerChange.down)); - expect(packets[2].data[0].signalKind, equals(ui.PointerSignalKind.none)); - expect(packets[2].data[0].pointerIdentifier, equals(1)); - expect(packets[2].data[0].synthesized, isFalse); - expect(packets[2].data[0].physicalX, equals(20.0 * dpi)); - expect(packets[2].data[0].physicalY, equals(50.0 * dpi)); - expect(packets[2].data[0].physicalDeltaX, equals(0.0)); - expect(packets[2].data[0].physicalDeltaY, equals(0.0)); - - // A move will be synthesized instead of hover because the button is currently down. - expect(packets[3].data, hasLength(2)); - expect(packets[3].data[0].change, equals(ui.PointerChange.move)); - expect(packets[3].data[0].pointerIdentifier, equals(1)); - expect(packets[3].data[0].synthesized, isTrue); - expect(packets[3].data[0].physicalX, equals(30.0 * dpi)); - expect(packets[3].data[0].physicalY, equals(60.0 * dpi)); - expect(packets[3].data[0].physicalDeltaX, equals(10.0 * dpi)); - expect(packets[3].data[0].physicalDeltaY, equals(10.0 * dpi)); - - expect(packets[3].data[1].change, equals(ui.PointerChange.hover)); - expect( - packets[3].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); - expect(packets[3].data[1].pointerIdentifier, equals(1)); - expect(packets[3].data[1].synthesized, isFalse); - expect(packets[3].data[1].physicalX, equals(30.0 * dpi)); - expect(packets[3].data[1].physicalY, equals(60.0 * dpi)); - expect(packets[3].data[1].physicalDeltaX, equals(0.0)); - expect(packets[3].data[1].physicalDeltaY, equals(0.0)); - }, - ); + rootElement.dispatchEvent( + context.mouseDown(button: 0, buttons: 1, clientX: 20.0, clientY: 50.0), + ); - test( - 'converts scroll delta to physical pixels (macOs)', - () { - final _ButtonedEventMixin context = _PointerEventContext(); + rootElement.dispatchEvent( + context.wheel(buttons: 1, clientX: 30, clientY: 60, deltaX: 10, deltaY: 10), + ); - const double dpi = 2.5; - ui_web.browser.debugOperatingSystemOverride = ui_web.OperatingSystem.macOs; - EngineFlutterDisplay.instance.debugOverrideDevicePixelRatio(dpi); + expect(packets, hasLength(4)); - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + // An add will be synthesized. + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].pointerIdentifier, equals(0)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[0].physicalX, equals(10.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); + expect(packets[0].data[0].physicalDeltaX, equals(0.0)); + expect(packets[0].data[0].physicalDeltaY, equals(0.0)); + + expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); + expect(packets[0].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); + expect(packets[0].data[1].pointerIdentifier, equals(0)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].physicalX, equals(10.0 * dpi)); + expect(packets[0].data[1].physicalY, equals(10.0 * dpi)); + expect(packets[0].data[1].physicalDeltaX, equals(0.0)); + expect(packets[0].data[1].physicalDeltaY, equals(0.0)); + + // A hover will be synthesized. + expect(packets[1].data, hasLength(2)); + expect(packets[1].data[0].change, equals(ui.PointerChange.hover)); + expect(packets[1].data[0].pointerIdentifier, equals(0)); + expect(packets[1].data[0].synthesized, isTrue); + expect(packets[1].data[0].physicalX, equals(20.0 * dpi)); + expect(packets[1].data[0].physicalY, equals(50.0 * dpi)); + expect(packets[1].data[0].physicalDeltaX, equals(10.0 * dpi)); + expect(packets[1].data[0].physicalDeltaY, equals(40.0 * dpi)); + + expect(packets[1].data[1].change, equals(ui.PointerChange.hover)); + expect(packets[1].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); + expect(packets[1].data[1].pointerIdentifier, equals(0)); + expect(packets[1].data[1].synthesized, isFalse); + expect(packets[1].data[1].physicalX, equals(20.0 * dpi)); + expect(packets[1].data[1].physicalY, equals(50.0 * dpi)); + expect(packets[1].data[1].physicalDeltaX, equals(0.0)); + expect(packets[1].data[1].physicalDeltaY, equals(0.0)); + + // No synthetic pointer data for down event. + expect(packets[2].data, hasLength(1)); + expect(packets[2].data[0].change, equals(ui.PointerChange.down)); + expect(packets[2].data[0].signalKind, equals(ui.PointerSignalKind.none)); + expect(packets[2].data[0].pointerIdentifier, equals(1)); + expect(packets[2].data[0].synthesized, isFalse); + expect(packets[2].data[0].physicalX, equals(20.0 * dpi)); + expect(packets[2].data[0].physicalY, equals(50.0 * dpi)); + expect(packets[2].data[0].physicalDeltaX, equals(0.0)); + expect(packets[2].data[0].physicalDeltaY, equals(0.0)); + + // A move will be synthesized instead of hover because the button is currently down. + expect(packets[3].data, hasLength(2)); + expect(packets[3].data[0].change, equals(ui.PointerChange.move)); + expect(packets[3].data[0].pointerIdentifier, equals(1)); + expect(packets[3].data[0].synthesized, isTrue); + expect(packets[3].data[0].physicalX, equals(30.0 * dpi)); + expect(packets[3].data[0].physicalY, equals(60.0 * dpi)); + expect(packets[3].data[0].physicalDeltaX, equals(10.0 * dpi)); + expect(packets[3].data[0].physicalDeltaY, equals(10.0 * dpi)); + + expect(packets[3].data[1].change, equals(ui.PointerChange.hover)); + expect(packets[3].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); + expect(packets[3].data[1].pointerIdentifier, equals(1)); + expect(packets[3].data[1].synthesized, isFalse); + expect(packets[3].data[1].physicalX, equals(30.0 * dpi)); + expect(packets[3].data[1].physicalY, equals(60.0 * dpi)); + expect(packets[3].data[1].physicalDeltaX, equals(0.0)); + expect(packets[3].data[1].physicalDeltaY, equals(0.0)); + }); - rootElement.dispatchEvent(context.wheel( - buttons: 0, - clientX: 10, - clientY: 10, - deltaX: 10, - deltaY: 10, - )); + test('converts scroll delta to physical pixels (macOs)', () { + final _ButtonedEventMixin context = _PointerEventContext(); - expect(packets, hasLength(1)); + const double dpi = 2.5; + ui_web.browser.debugOperatingSystemOverride = ui_web.OperatingSystem.macOs; + EngineFlutterDisplay.instance.debugOverrideDevicePixelRatio(dpi); + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - // An add will be synthesized. - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - // Scroll deltas should be multiplied by `dpi`. - expect(packets[0].data[0].scrollDeltaX, equals(10.0 * dpi)); - expect(packets[0].data[0].scrollDeltaY, equals(10.0 * dpi)); - - expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); - expect(packets[0].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); - // Scroll deltas should be multiplied by `dpi`. - expect(packets[0].data[0].scrollDeltaX, equals(10.0 * dpi)); - expect(packets[0].data[0].scrollDeltaY, equals(10.0 * dpi)); - - EngineFlutterDisplay.instance.debugOverrideDevicePixelRatio(1.0); - ui_web.browser.debugBrowserEngineOverride = null; - }, - ); + rootElement.dispatchEvent( + context.wheel(buttons: 0, clientX: 10, clientY: 10, deltaX: 10, deltaY: 10), + ); - test( - 'does set pointer device kind based on delta precision and wheelDelta', - () { - if (isFirefox) { - // Firefox does not support trackpad events, as they cannot be - // disambiguated from smoothed mouse wheel events. - return; - } - final _ButtonedEventMixin context = _PointerEventContext(); - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + expect(packets, hasLength(1)); + + // An add will be synthesized. + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + // Scroll deltas should be multiplied by `dpi`. + expect(packets[0].data[0].scrollDeltaX, equals(10.0 * dpi)); + expect(packets[0].data[0].scrollDeltaY, equals(10.0 * dpi)); + + expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); + expect(packets[0].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); + // Scroll deltas should be multiplied by `dpi`. + expect(packets[0].data[0].scrollDeltaX, equals(10.0 * dpi)); + expect(packets[0].data[0].scrollDeltaY, equals(10.0 * dpi)); + + EngineFlutterDisplay.instance.debugOverrideDevicePixelRatio(1.0); + ui_web.browser.debugBrowserEngineOverride = null; + }); - rootElement.dispatchEvent(context.wheel( + test('does set pointer device kind based on delta precision and wheelDelta', () { + if (isFirefox) { + // Firefox does not support trackpad events, as they cannot be + // disambiguated from smoothed mouse wheel events. + return; + } + final _ButtonedEventMixin context = _PointerEventContext(); + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; + + rootElement.dispatchEvent( + context.wheel( buttons: 0, clientX: 10, clientY: 10, @@ -962,9 +884,11 @@ void testMain() { wheelDeltaX: -357, wheelDeltaY: -357, timeStamp: 0, - )); + ), + ); - rootElement.dispatchEvent(context.wheel( + rootElement.dispatchEvent( + context.wheel( buttons: 0, clientX: 10, clientY: 10, @@ -973,9 +897,11 @@ void testMain() { wheelDeltaX: -360, wheelDeltaY: -360, timeStamp: 10, - )); + ), + ); - rootElement.dispatchEvent(context.wheel( + rootElement.dispatchEvent( + context.wheel( buttons: 0, clientX: 10, clientY: 10, @@ -984,9 +910,11 @@ void testMain() { wheelDeltaX: -360, wheelDeltaY: -360, timeStamp: 20, - )); + ), + ); - rootElement.dispatchEvent(context.wheel( + rootElement.dispatchEvent( + context.wheel( buttons: 0, clientX: 10, clientY: 10, @@ -995,9 +923,11 @@ void testMain() { wheelDeltaX: -357, wheelDeltaY: -357, timeStamp: 1000, - )); + ), + ); - rootElement.dispatchEvent(context.wheel( + rootElement.dispatchEvent( + context.wheel( buttons: 0, clientX: 10, clientY: 10, @@ -1006,9 +936,11 @@ void testMain() { wheelDeltaX: 360, wheelDeltaY: 360, timeStamp: 1010, - )); + ), + ); - rootElement.dispatchEvent(context.wheel( + rootElement.dispatchEvent( + context.wheel( buttons: 0, clientX: 10, clientY: 10, @@ -1017,9 +949,11 @@ void testMain() { wheelDeltaX: 0, wheelDeltaY: 360, timeStamp: 2000, - )); + ), + ); - rootElement.dispatchEvent(context.wheel( + rootElement.dispatchEvent( + context.wheel( buttons: 0, clientX: 10, clientY: 10, @@ -1028,1586 +962,1306 @@ void testMain() { wheelDeltaX: 0, wheelDeltaY: -360, timeStamp: 3000, - )); + ), + ); - expect(packets, hasLength(7)); + expect(packets, hasLength(7)); - // An add will be synthesized. - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].pointerIdentifier, equals(0)); - expect(packets[0].data[0].synthesized, isTrue); - expect(packets[0].data[0].physicalX, equals(10.0 * dpi)); - expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); - expect(packets[0].data[0].physicalDeltaX, equals(0.0)); - expect(packets[0].data[0].physicalDeltaY, equals(0.0)); - // Because the delta is not in increments of 120 and has matching wheelDelta, - // it will be a trackpad event. - expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); - expect( - packets[0].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); - expect( - packets[0].data[1].kind, equals(ui.PointerDeviceKind.trackpad)); - expect(packets[0].data[1].device, equals(-2)); - expect(packets[0].data[1].pointerIdentifier, equals(0)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].physicalX, equals(10.0 * dpi)); - expect(packets[0].data[1].physicalY, equals(10.0 * dpi)); - expect(packets[0].data[1].physicalDeltaX, equals(0.0)); - expect(packets[0].data[1].physicalDeltaY, equals(0.0)); - expect(packets[0].data[1].scrollDeltaX, equals(119.0)); - expect(packets[0].data[1].scrollDeltaY, equals(119.0)); - - // Because the delta is in increments of 120, but is similar to the - // previous event, it will be a trackpad event. - expect(packets[1].data[0].change, equals(ui.PointerChange.hover)); - expect( - packets[1].data[0].signalKind, equals(ui.PointerSignalKind.scroll)); - expect( - packets[1].data[0].kind, equals(ui.PointerDeviceKind.trackpad)); - expect(packets[1].data[0].device, equals(-2)); - expect(packets[1].data[0].pointerIdentifier, equals(0)); - expect(packets[1].data[0].synthesized, isFalse); - expect(packets[1].data[0].physicalX, equals(10.0 * dpi)); - expect(packets[1].data[0].physicalY, equals(10.0 * dpi)); - expect(packets[1].data[0].physicalDeltaX, equals(0.0)); - expect(packets[1].data[0].physicalDeltaY, equals(0.0)); - expect(packets[1].data[0].scrollDeltaX, equals(120.0)); - expect(packets[1].data[0].scrollDeltaY, equals(120.0)); - - // Because the delta is in increments of 120, but is again similar to the - // previous event, it will be a trackpad event. - expect(packets[2].data[0].change, equals(ui.PointerChange.hover)); - expect( - packets[2].data[0].signalKind, equals(ui.PointerSignalKind.scroll)); - expect( - packets[2].data[0].kind, equals(ui.PointerDeviceKind.trackpad)); - expect(packets[2].data[0].device, equals(-2)); - expect(packets[2].data[0].pointerIdentifier, equals(0)); - expect(packets[2].data[0].synthesized, isFalse); - expect(packets[2].data[0].physicalX, equals(10.0 * dpi)); - expect(packets[2].data[0].physicalY, equals(10.0 * dpi)); - expect(packets[2].data[0].physicalDeltaX, equals(0.0)); - expect(packets[2].data[0].physicalDeltaY, equals(0.0)); - expect(packets[2].data[0].scrollDeltaX, equals(120.0)); - expect(packets[2].data[0].scrollDeltaY, equals(120.0)); - - // Because the delta is not in increments of 120 and has matching wheelDelta, - // it will be a trackpad event. - expect(packets[3].data[0].change, equals(ui.PointerChange.hover)); - expect( - packets[3].data[0].signalKind, equals(ui.PointerSignalKind.scroll)); - expect( - packets[3].data[0].kind, equals(ui.PointerDeviceKind.trackpad)); - expect(packets[3].data[0].device, equals(-2)); - expect(packets[3].data[0].pointerIdentifier, equals(0)); - expect(packets[3].data[0].synthesized, isFalse); - expect(packets[3].data[0].physicalX, equals(10.0 * dpi)); - expect(packets[3].data[0].physicalY, equals(10.0 * dpi)); - expect(packets[3].data[0].physicalDeltaX, equals(0.0)); - expect(packets[3].data[0].physicalDeltaY, equals(0.0)); - expect(packets[3].data[0].scrollDeltaX, equals(119.0)); - expect(packets[3].data[0].scrollDeltaY, equals(119.0)); - - // Because the delta is in increments of 120, and is not similar to the - // previous event, but occurred soon after the previous event, it will be - // a trackpad event. - expect(packets[4].data[0].change, equals(ui.PointerChange.hover)); - expect( - packets[4].data[0].signalKind, equals(ui.PointerSignalKind.scroll)); - expect( - packets[4].data[0].kind, equals(ui.PointerDeviceKind.trackpad)); - expect(packets[4].data[0].device, equals(-2)); - expect(packets[4].data[0].pointerIdentifier, equals(0)); - expect(packets[4].data[0].synthesized, isFalse); - expect(packets[4].data[0].physicalX, equals(10.0 * dpi)); - expect(packets[4].data[0].physicalY, equals(10.0 * dpi)); - expect(packets[4].data[0].physicalDeltaX, equals(0.0)); - expect(packets[4].data[0].physicalDeltaY, equals(0.0)); - expect(packets[4].data[0].scrollDeltaX, equals(-120.0)); - expect(packets[4].data[0].scrollDeltaY, equals(-120.0)); - - // An add will be synthesized. - expect(packets[5].data, hasLength(2)); - expect(packets[5].data[0].change, equals(ui.PointerChange.add)); - expect( - packets[5].data[0].signalKind, equals(ui.PointerSignalKind.none)); - expect( - packets[5].data[0].kind, equals(ui.PointerDeviceKind.mouse)); - expect(packets[5].data[0].device, equals(-1)); - expect(packets[5].data[0].pointerIdentifier, equals(0)); - expect(packets[5].data[0].synthesized, isTrue); - expect(packets[5].data[0].physicalX, equals(10.0 * dpi)); - expect(packets[5].data[0].physicalY, equals(10.0 * dpi)); - expect(packets[5].data[0].physicalDeltaX, equals(0.0)); - expect(packets[5].data[0].physicalDeltaY, equals(0.0)); - expect(packets[5].data[0].scrollDeltaX, equals(0.0)); - expect(packets[5].data[0].scrollDeltaY, equals(-120.0)); - // Because the delta is in increments of 120, and is not similar to - // the previous event, and occurred long after the previous event, it will - // be a mouse event. - expect(packets[5].data[1].change, equals(ui.PointerChange.hover)); - expect( - packets[5].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); - expect( - packets[5].data[1].kind, equals(ui.PointerDeviceKind.mouse)); - expect(packets[5].data[1].device, equals(-1)); - expect(packets[5].data[1].pointerIdentifier, equals(0)); - expect(packets[5].data[1].synthesized, isFalse); - expect(packets[5].data[1].physicalX, equals(10.0 * dpi)); - expect(packets[5].data[1].physicalY, equals(10.0 * dpi)); - expect(packets[5].data[1].physicalDeltaX, equals(0.0)); - expect(packets[5].data[1].physicalDeltaY, equals(0.0)); - expect(packets[5].data[1].scrollDeltaX, equals(0.0)); - expect(packets[5].data[1].scrollDeltaY, equals(-120.0)); - - // Because the delta is not in increments of 120 and has non-matching - // wheelDelta, it will be a mouse event. - expect(packets[6].data, hasLength(1)); - expect(packets[6].data[0].change, equals(ui.PointerChange.hover)); - expect( - packets[6].data[0].signalKind, equals(ui.PointerSignalKind.scroll)); - expect( - packets[6].data[0].kind, equals(ui.PointerDeviceKind.mouse)); - expect(packets[6].data[0].device, equals(-1)); - expect(packets[6].data[0].pointerIdentifier, equals(0)); - expect(packets[6].data[0].synthesized, isFalse); - expect(packets[6].data[0].physicalX, equals(10.0 * dpi)); - expect(packets[6].data[0].physicalY, equals(10.0 * dpi)); - expect(packets[6].data[0].physicalDeltaX, equals(0.0)); - expect(packets[6].data[0].physicalDeltaY, equals(0.0)); - expect(packets[6].data[0].scrollDeltaX, equals(0.0)); - expect(packets[6].data[0].scrollDeltaY, equals(40.0)); - }, - ); + // An add will be synthesized. + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].pointerIdentifier, equals(0)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[0].physicalX, equals(10.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); + expect(packets[0].data[0].physicalDeltaX, equals(0.0)); + expect(packets[0].data[0].physicalDeltaY, equals(0.0)); + // Because the delta is not in increments of 120 and has matching wheelDelta, + // it will be a trackpad event. + expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); + expect(packets[0].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); + expect(packets[0].data[1].kind, equals(ui.PointerDeviceKind.trackpad)); + expect(packets[0].data[1].device, equals(-2)); + expect(packets[0].data[1].pointerIdentifier, equals(0)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].physicalX, equals(10.0 * dpi)); + expect(packets[0].data[1].physicalY, equals(10.0 * dpi)); + expect(packets[0].data[1].physicalDeltaX, equals(0.0)); + expect(packets[0].data[1].physicalDeltaY, equals(0.0)); + expect(packets[0].data[1].scrollDeltaX, equals(119.0)); + expect(packets[0].data[1].scrollDeltaY, equals(119.0)); + + // Because the delta is in increments of 120, but is similar to the + // previous event, it will be a trackpad event. + expect(packets[1].data[0].change, equals(ui.PointerChange.hover)); + expect(packets[1].data[0].signalKind, equals(ui.PointerSignalKind.scroll)); + expect(packets[1].data[0].kind, equals(ui.PointerDeviceKind.trackpad)); + expect(packets[1].data[0].device, equals(-2)); + expect(packets[1].data[0].pointerIdentifier, equals(0)); + expect(packets[1].data[0].synthesized, isFalse); + expect(packets[1].data[0].physicalX, equals(10.0 * dpi)); + expect(packets[1].data[0].physicalY, equals(10.0 * dpi)); + expect(packets[1].data[0].physicalDeltaX, equals(0.0)); + expect(packets[1].data[0].physicalDeltaY, equals(0.0)); + expect(packets[1].data[0].scrollDeltaX, equals(120.0)); + expect(packets[1].data[0].scrollDeltaY, equals(120.0)); + + // Because the delta is in increments of 120, but is again similar to the + // previous event, it will be a trackpad event. + expect(packets[2].data[0].change, equals(ui.PointerChange.hover)); + expect(packets[2].data[0].signalKind, equals(ui.PointerSignalKind.scroll)); + expect(packets[2].data[0].kind, equals(ui.PointerDeviceKind.trackpad)); + expect(packets[2].data[0].device, equals(-2)); + expect(packets[2].data[0].pointerIdentifier, equals(0)); + expect(packets[2].data[0].synthesized, isFalse); + expect(packets[2].data[0].physicalX, equals(10.0 * dpi)); + expect(packets[2].data[0].physicalY, equals(10.0 * dpi)); + expect(packets[2].data[0].physicalDeltaX, equals(0.0)); + expect(packets[2].data[0].physicalDeltaY, equals(0.0)); + expect(packets[2].data[0].scrollDeltaX, equals(120.0)); + expect(packets[2].data[0].scrollDeltaY, equals(120.0)); + + // Because the delta is not in increments of 120 and has matching wheelDelta, + // it will be a trackpad event. + expect(packets[3].data[0].change, equals(ui.PointerChange.hover)); + expect(packets[3].data[0].signalKind, equals(ui.PointerSignalKind.scroll)); + expect(packets[3].data[0].kind, equals(ui.PointerDeviceKind.trackpad)); + expect(packets[3].data[0].device, equals(-2)); + expect(packets[3].data[0].pointerIdentifier, equals(0)); + expect(packets[3].data[0].synthesized, isFalse); + expect(packets[3].data[0].physicalX, equals(10.0 * dpi)); + expect(packets[3].data[0].physicalY, equals(10.0 * dpi)); + expect(packets[3].data[0].physicalDeltaX, equals(0.0)); + expect(packets[3].data[0].physicalDeltaY, equals(0.0)); + expect(packets[3].data[0].scrollDeltaX, equals(119.0)); + expect(packets[3].data[0].scrollDeltaY, equals(119.0)); + + // Because the delta is in increments of 120, and is not similar to the + // previous event, but occurred soon after the previous event, it will be + // a trackpad event. + expect(packets[4].data[0].change, equals(ui.PointerChange.hover)); + expect(packets[4].data[0].signalKind, equals(ui.PointerSignalKind.scroll)); + expect(packets[4].data[0].kind, equals(ui.PointerDeviceKind.trackpad)); + expect(packets[4].data[0].device, equals(-2)); + expect(packets[4].data[0].pointerIdentifier, equals(0)); + expect(packets[4].data[0].synthesized, isFalse); + expect(packets[4].data[0].physicalX, equals(10.0 * dpi)); + expect(packets[4].data[0].physicalY, equals(10.0 * dpi)); + expect(packets[4].data[0].physicalDeltaX, equals(0.0)); + expect(packets[4].data[0].physicalDeltaY, equals(0.0)); + expect(packets[4].data[0].scrollDeltaX, equals(-120.0)); + expect(packets[4].data[0].scrollDeltaY, equals(-120.0)); + + // An add will be synthesized. + expect(packets[5].data, hasLength(2)); + expect(packets[5].data[0].change, equals(ui.PointerChange.add)); + expect(packets[5].data[0].signalKind, equals(ui.PointerSignalKind.none)); + expect(packets[5].data[0].kind, equals(ui.PointerDeviceKind.mouse)); + expect(packets[5].data[0].device, equals(-1)); + expect(packets[5].data[0].pointerIdentifier, equals(0)); + expect(packets[5].data[0].synthesized, isTrue); + expect(packets[5].data[0].physicalX, equals(10.0 * dpi)); + expect(packets[5].data[0].physicalY, equals(10.0 * dpi)); + expect(packets[5].data[0].physicalDeltaX, equals(0.0)); + expect(packets[5].data[0].physicalDeltaY, equals(0.0)); + expect(packets[5].data[0].scrollDeltaX, equals(0.0)); + expect(packets[5].data[0].scrollDeltaY, equals(-120.0)); + // Because the delta is in increments of 120, and is not similar to + // the previous event, and occurred long after the previous event, it will + // be a mouse event. + expect(packets[5].data[1].change, equals(ui.PointerChange.hover)); + expect(packets[5].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); + expect(packets[5].data[1].kind, equals(ui.PointerDeviceKind.mouse)); + expect(packets[5].data[1].device, equals(-1)); + expect(packets[5].data[1].pointerIdentifier, equals(0)); + expect(packets[5].data[1].synthesized, isFalse); + expect(packets[5].data[1].physicalX, equals(10.0 * dpi)); + expect(packets[5].data[1].physicalY, equals(10.0 * dpi)); + expect(packets[5].data[1].physicalDeltaX, equals(0.0)); + expect(packets[5].data[1].physicalDeltaY, equals(0.0)); + expect(packets[5].data[1].scrollDeltaX, equals(0.0)); + expect(packets[5].data[1].scrollDeltaY, equals(-120.0)); + + // Because the delta is not in increments of 120 and has non-matching + // wheelDelta, it will be a mouse event. + expect(packets[6].data, hasLength(1)); + expect(packets[6].data[0].change, equals(ui.PointerChange.hover)); + expect(packets[6].data[0].signalKind, equals(ui.PointerSignalKind.scroll)); + expect(packets[6].data[0].kind, equals(ui.PointerDeviceKind.mouse)); + expect(packets[6].data[0].device, equals(-1)); + expect(packets[6].data[0].pointerIdentifier, equals(0)); + expect(packets[6].data[0].synthesized, isFalse); + expect(packets[6].data[0].physicalX, equals(10.0 * dpi)); + expect(packets[6].data[0].physicalY, equals(10.0 * dpi)); + expect(packets[6].data[0].physicalDeltaX, equals(0.0)); + expect(packets[6].data[0].physicalDeltaY, equals(0.0)); + expect(packets[6].data[0].scrollDeltaX, equals(0.0)); + expect(packets[6].data[0].scrollDeltaY, equals(40.0)); + }); - test( - 'does choose scroll vs scale based on ctrlKey', - () { - final _ButtonedEventMixin context = _PointerEventContext(); - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + test('does choose scroll vs scale based on ctrlKey', () { + final _ButtonedEventMixin context = _PointerEventContext(); + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - ui_web.browser.debugOperatingSystemOverride = ui_web.OperatingSystem.macOs; + ui_web.browser.debugOperatingSystemOverride = ui_web.OperatingSystem.macOs; - rootElement.dispatchEvent(context.wheel( - buttons: 0, - clientX: 10, - clientY: 10, - deltaX: 0, - deltaY: 120, - )); + rootElement.dispatchEvent( + context.wheel(buttons: 0, clientX: 10, clientY: 10, deltaX: 0, deltaY: 120), + ); - rootElement.dispatchEvent(context.wheel( - buttons: 0, - clientX: 10, - clientY: 10, - deltaX: 0, - deltaY: 100, - ctrlKey: true, - )); + rootElement.dispatchEvent( + context.wheel(buttons: 0, clientX: 10, clientY: 10, deltaX: 0, deltaY: 100, ctrlKey: true), + ); - keyboardConverter.handleEvent(keyDownEvent('ControlLeft', 'Control', kCtrl)); + keyboardConverter.handleEvent(keyDownEvent('ControlLeft', 'Control', kCtrl)); - rootElement.dispatchEvent(context.wheel( - buttons: 0, - clientX: 10, - clientY: 10, - deltaX: 0, - deltaY: 240, - ctrlKey: true, - )); + rootElement.dispatchEvent( + context.wheel(buttons: 0, clientX: 10, clientY: 10, deltaX: 0, deltaY: 240, ctrlKey: true), + ); - keyboardConverter.handleEvent(keyUpEvent('ControlLeft', 'Control', kCtrl)); + keyboardConverter.handleEvent(keyUpEvent('ControlLeft', 'Control', kCtrl)); - expect(packets, hasLength(3)); + expect(packets, hasLength(3)); - // An add will be synthesized. - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].pointerIdentifier, equals(0)); - expect(packets[0].data[0].synthesized, isTrue); - expect(packets[0].data[0].physicalX, equals(10.0 * dpi)); - expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); - expect(packets[0].data[0].physicalDeltaX, equals(0.0)); - expect(packets[0].data[0].physicalDeltaY, equals(0.0)); - // Because ctrlKey is not pressed, it will be a scroll. - expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); - expect( - packets[0].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); - expect( - packets[0].data[1].kind, equals(ui.PointerDeviceKind.mouse)); - expect(packets[0].data[1].pointerIdentifier, equals(0)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].physicalX, equals(10.0 * dpi)); - expect(packets[0].data[1].physicalY, equals(10.0 * dpi)); - expect(packets[0].data[1].physicalDeltaX, equals(0.0)); - expect(packets[0].data[1].physicalDeltaY, equals(0.0)); - expect(packets[0].data[1].scrollDeltaX, equals(0.0)); - expect(packets[0].data[1].scrollDeltaY, equals(120.0)); - - // Because ctrlKey is pressed, it will be a scale. - expect(packets[1].data, hasLength(1)); - expect(packets[1].data[0].change, equals(ui.PointerChange.hover)); - expect( - packets[1].data[0].signalKind, equals(ui.PointerSignalKind.scale)); - expect( - packets[1].data[0].kind, equals(ui.PointerDeviceKind.mouse)); - expect(packets[1].data[0].pointerIdentifier, equals(0)); - expect(packets[1].data[0].synthesized, isFalse); - expect(packets[1].data[0].physicalX, equals(10.0 * dpi)); - expect(packets[1].data[0].physicalY, equals(10.0 * dpi)); - expect(packets[1].data[0].physicalDeltaX, equals(0.0)); - expect(packets[1].data[0].physicalDeltaY, equals(0.0)); - expect(packets[1].data[0].scale, closeTo(0.60653065971, 1e-10)); // math.exp(-100/200) - - // [macOS only]: Because ctrlKey is true, but the key is pressed physically, it will be a scroll. - expect(packets[2].data, hasLength(1)); - expect(packets[2].data[0].change, equals(ui.PointerChange.hover)); - expect( - packets[2].data[0].signalKind, equals(ui.PointerSignalKind.scroll)); - expect( - packets[2].data[0].kind, equals(ui.PointerDeviceKind.mouse)); - expect(packets[2].data[0].pointerIdentifier, equals(0)); - expect(packets[2].data[0].synthesized, isFalse); - expect(packets[2].data[0].physicalX, equals(10.0 * dpi)); - expect(packets[2].data[0].physicalY, equals(10.0 * dpi)); - expect(packets[2].data[0].physicalDeltaX, equals(0.0)); - expect(packets[2].data[0].physicalDeltaY, equals(0.0)); - expect(packets[2].data[0].scrollDeltaX, equals(0.0)); - expect(packets[2].data[0].scrollDeltaY, equals(240.0)); - - ui_web.browser.debugOperatingSystemOverride = null; - }, - ); + // An add will be synthesized. + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].pointerIdentifier, equals(0)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[0].physicalX, equals(10.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); + expect(packets[0].data[0].physicalDeltaX, equals(0.0)); + expect(packets[0].data[0].physicalDeltaY, equals(0.0)); + // Because ctrlKey is not pressed, it will be a scroll. + expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); + expect(packets[0].data[1].signalKind, equals(ui.PointerSignalKind.scroll)); + expect(packets[0].data[1].kind, equals(ui.PointerDeviceKind.mouse)); + expect(packets[0].data[1].pointerIdentifier, equals(0)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].physicalX, equals(10.0 * dpi)); + expect(packets[0].data[1].physicalY, equals(10.0 * dpi)); + expect(packets[0].data[1].physicalDeltaX, equals(0.0)); + expect(packets[0].data[1].physicalDeltaY, equals(0.0)); + expect(packets[0].data[1].scrollDeltaX, equals(0.0)); + expect(packets[0].data[1].scrollDeltaY, equals(120.0)); + + // Because ctrlKey is pressed, it will be a scale. + expect(packets[1].data, hasLength(1)); + expect(packets[1].data[0].change, equals(ui.PointerChange.hover)); + expect(packets[1].data[0].signalKind, equals(ui.PointerSignalKind.scale)); + expect(packets[1].data[0].kind, equals(ui.PointerDeviceKind.mouse)); + expect(packets[1].data[0].pointerIdentifier, equals(0)); + expect(packets[1].data[0].synthesized, isFalse); + expect(packets[1].data[0].physicalX, equals(10.0 * dpi)); + expect(packets[1].data[0].physicalY, equals(10.0 * dpi)); + expect(packets[1].data[0].physicalDeltaX, equals(0.0)); + expect(packets[1].data[0].physicalDeltaY, equals(0.0)); + expect(packets[1].data[0].scale, closeTo(0.60653065971, 1e-10)); // math.exp(-100/200) + + // [macOS only]: Because ctrlKey is true, but the key is pressed physically, it will be a scroll. + expect(packets[2].data, hasLength(1)); + expect(packets[2].data[0].change, equals(ui.PointerChange.hover)); + expect(packets[2].data[0].signalKind, equals(ui.PointerSignalKind.scroll)); + expect(packets[2].data[0].kind, equals(ui.PointerDeviceKind.mouse)); + expect(packets[2].data[0].pointerIdentifier, equals(0)); + expect(packets[2].data[0].synthesized, isFalse); + expect(packets[2].data[0].physicalX, equals(10.0 * dpi)); + expect(packets[2].data[0].physicalY, equals(10.0 * dpi)); + expect(packets[2].data[0].physicalDeltaX, equals(0.0)); + expect(packets[2].data[0].physicalDeltaY, equals(0.0)); + expect(packets[2].data[0].scrollDeltaX, equals(0.0)); + expect(packets[2].data[0].scrollDeltaY, equals(240.0)); + + ui_web.browser.debugOperatingSystemOverride = null; + }); - test( - 'does calculate delta and pointer identifier correctly', - () { - final _ButtonedEventMixin context = _PointerEventContext(); - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + test('does calculate delta and pointer identifier correctly', () { + final _ButtonedEventMixin context = _PointerEventContext(); + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - rootElement.dispatchEvent(context.hover( - clientX: 10.0, - clientY: 10.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].pointerIdentifier, equals(0)); - expect(packets[0].data[0].synthesized, isTrue); - expect(packets[0].data[0].physicalX, equals(10.0 * dpi)); - expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); - expect(packets[0].data[0].physicalDeltaX, equals(0.0)); - expect(packets[0].data[0].physicalDeltaY, equals(0.0)); + rootElement.dispatchEvent(context.hover(clientX: 10.0, clientY: 10.0)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].pointerIdentifier, equals(0)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[0].physicalX, equals(10.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); + expect(packets[0].data[0].physicalDeltaX, equals(0.0)); + expect(packets[0].data[0].physicalDeltaY, equals(0.0)); + + expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); + expect(packets[0].data[1].pointerIdentifier, equals(0)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].physicalX, equals(10.0 * dpi)); + expect(packets[0].data[1].physicalY, equals(10.0 * dpi)); + expect(packets[0].data[1].physicalDeltaX, equals(0.0)); + expect(packets[0].data[1].physicalDeltaY, equals(0.0)); + packets.clear(); - expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); - expect(packets[0].data[1].pointerIdentifier, equals(0)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].physicalX, equals(10.0 * dpi)); - expect(packets[0].data[1].physicalY, equals(10.0 * dpi)); - expect(packets[0].data[1].physicalDeltaX, equals(0.0)); - expect(packets[0].data[1].physicalDeltaY, equals(0.0)); - packets.clear(); + rootElement.dispatchEvent(context.hover(clientX: 20.0, clientY: 20.0)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.hover)); + expect(packets[0].data[0].pointerIdentifier, equals(0)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(20.0 * dpi)); + expect(packets[0].data[0].physicalDeltaX, equals(10.0 * dpi)); + expect(packets[0].data[0].physicalDeltaY, equals(10.0 * dpi)); + packets.clear(); - rootElement.dispatchEvent(context.hover( - clientX: 20.0, - clientY: 20.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.hover)); - expect(packets[0].data[0].pointerIdentifier, equals(0)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); - expect(packets[0].data[0].physicalY, equals(20.0 * dpi)); - expect(packets[0].data[0].physicalDeltaX, equals(10.0 * dpi)); - expect(packets[0].data[0].physicalDeltaY, equals(10.0 * dpi)); - packets.clear(); + rootElement.dispatchEvent(context.primaryDown(clientX: 20.0, clientY: 20.0)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.down)); + expect(packets[0].data[0].pointerIdentifier, equals(1)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(20.0 * dpi)); + expect(packets[0].data[0].physicalDeltaX, equals(0.0)); + expect(packets[0].data[0].physicalDeltaY, equals(0.0)); + packets.clear(); - rootElement.dispatchEvent(context.primaryDown( - clientX: 20.0, - clientY: 20.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.down)); - expect(packets[0].data[0].pointerIdentifier, equals(1)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); - expect(packets[0].data[0].physicalY, equals(20.0 * dpi)); - expect(packets[0].data[0].physicalDeltaX, equals(0.0)); - expect(packets[0].data[0].physicalDeltaY, equals(0.0)); - packets.clear(); + rootElement.dispatchEvent(context.primaryMove(clientX: 40.0, clientY: 30.0)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].pointerIdentifier, equals(1)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(40.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(30.0 * dpi)); + expect(packets[0].data[0].physicalDeltaX, equals(20.0 * dpi)); + expect(packets[0].data[0].physicalDeltaY, equals(10.0 * dpi)); + packets.clear(); - rootElement.dispatchEvent(context.primaryMove( - clientX: 40.0, - clientY: 30.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].pointerIdentifier, equals(1)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].physicalX, equals(40.0 * dpi)); - expect(packets[0].data[0].physicalY, equals(30.0 * dpi)); - expect(packets[0].data[0].physicalDeltaX, equals(20.0 * dpi)); - expect(packets[0].data[0].physicalDeltaY, equals(10.0 * dpi)); - packets.clear(); + rootElement.dispatchEvent(context.primaryUp(clientX: 40.0, clientY: 30.0)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.up)); + expect(packets[0].data[0].pointerIdentifier, equals(1)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(40.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(30.0 * dpi)); + expect(packets[0].data[0].physicalDeltaX, equals(0.0)); + expect(packets[0].data[0].physicalDeltaY, equals(0.0)); + packets.clear(); - rootElement.dispatchEvent(context.primaryUp( - clientX: 40.0, - clientY: 30.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].pointerIdentifier, equals(1)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].physicalX, equals(40.0 * dpi)); - expect(packets[0].data[0].physicalY, equals(30.0 * dpi)); - expect(packets[0].data[0].physicalDeltaX, equals(0.0)); - expect(packets[0].data[0].physicalDeltaY, equals(0.0)); - packets.clear(); + rootElement.dispatchEvent(context.hover(clientX: 20.0, clientY: 10.0)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.hover)); + expect(packets[0].data[0].pointerIdentifier, equals(1)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); + expect(packets[0].data[0].physicalDeltaX, equals(-20.0 * dpi)); + expect(packets[0].data[0].physicalDeltaY, equals(-20.0 * dpi)); + packets.clear(); - rootElement.dispatchEvent(context.hover( - clientX: 20.0, - clientY: 10.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.hover)); - expect(packets[0].data[0].pointerIdentifier, equals(1)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); - expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); - expect(packets[0].data[0].physicalDeltaX, equals(-20.0 * dpi)); - expect(packets[0].data[0].physicalDeltaY, equals(-20.0 * dpi)); - packets.clear(); + rootElement.dispatchEvent(context.primaryDown(clientX: 20.0, clientY: 10.0)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.down)); + expect(packets[0].data[0].pointerIdentifier, equals(2)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); + expect(packets[0].data[0].physicalDeltaX, equals(0.0)); + expect(packets[0].data[0].physicalDeltaY, equals(0.0)); + packets.clear(); + }); - rootElement.dispatchEvent(context.primaryDown( - clientX: 20.0, - clientY: 10.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.down)); - expect(packets[0].data[0].pointerIdentifier, equals(2)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); - expect(packets[0].data[0].physicalY, equals(10.0 * dpi)); - expect(packets[0].data[0].physicalDeltaX, equals(0.0)); - expect(packets[0].data[0].physicalDeltaY, equals(0.0)); - packets.clear(); - }, - ); + test('correctly converts buttons of down, move, leave, and up events', () { + final _ButtonedEventMixin context = _PointerEventContext(); + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - test( - 'correctly converts buttons of down, move, leave, and up events', - () { - final _ButtonedEventMixin context = _PointerEventContext(); - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + // Add and hover - // Add and hover + rootElement.dispatchEvent(context.hover(clientX: 10, clientY: 11)); - rootElement.dispatchEvent(context.hover( - clientX: 10, - clientY: 11, - )); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[0].physicalX, equals(10 * dpi)); + expect(packets[0].data[0].physicalY, equals(11 * dpi)); + + expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].physicalX, equals(10 * dpi)); + expect(packets[0].data[1].physicalY, equals(11 * dpi)); + expect(packets[0].data[1].buttons, equals(0)); + packets.clear(); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, isTrue); - expect(packets[0].data[0].physicalX, equals(10 * dpi)); - expect(packets[0].data[0].physicalY, equals(11 * dpi)); - - expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].physicalX, equals(10 * dpi)); - expect(packets[0].data[1].physicalY, equals(11 * dpi)); - expect(packets[0].data[1].buttons, equals(0)); - packets.clear(); + rootElement.dispatchEvent( + context.mouseDown(button: 0, buttons: 1, clientX: 10.0, clientY: 11.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.down)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(10 * dpi)); + expect(packets[0].data[0].physicalY, equals(11 * dpi)); + expect(packets[0].data[0].buttons, equals(1)); + packets.clear(); - rootElement.dispatchEvent(context.mouseDown( - button: 0, - buttons: 1, - clientX: 10.0, - clientY: 11.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.down)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].physicalX, equals(10 * dpi)); - expect(packets[0].data[0].physicalY, equals(11 * dpi)); - expect(packets[0].data[0].buttons, equals(1)); - packets.clear(); + rootElement.dispatchEvent( + context.mouseMove(button: _kNoButtonChange, buttons: 1, clientX: 20.0, clientY: 21.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(20 * dpi)); + expect(packets[0].data[0].physicalY, equals(21 * dpi)); + expect(packets[0].data[0].buttons, equals(1)); + packets.clear(); - rootElement.dispatchEvent(context.mouseMove( - button: _kNoButtonChange, - buttons: 1, - clientX: 20.0, - clientY: 21.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].physicalX, equals(20 * dpi)); - expect(packets[0].data[0].physicalY, equals(21 * dpi)); - expect(packets[0].data[0].buttons, equals(1)); - packets.clear(); + rootElement.dispatchEvent(context.mouseUp(button: 0, clientX: 20.0, clientY: 21.0)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.up)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(20 * dpi)); + expect(packets[0].data[0].physicalY, equals(21 * dpi)); + expect(packets[0].data[0].buttons, equals(0)); + packets.clear(); - rootElement.dispatchEvent(context.mouseUp( - button: 0, - clientX: 20.0, - clientY: 21.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].physicalX, equals(20 * dpi)); - expect(packets[0].data[0].physicalY, equals(21 * dpi)); - expect(packets[0].data[0].buttons, equals(0)); - packets.clear(); + // Drag with secondary button + rootElement.dispatchEvent( + context.mouseDown(button: 2, buttons: 2, clientX: 20.0, clientY: 21.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.down)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(20 * dpi)); + expect(packets[0].data[0].physicalY, equals(21 * dpi)); + expect(packets[0].data[0].buttons, equals(2)); + packets.clear(); - // Drag with secondary button - rootElement.dispatchEvent(context.mouseDown( - button: 2, - buttons: 2, - clientX: 20.0, - clientY: 21.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.down)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].physicalX, equals(20 * dpi)); - expect(packets[0].data[0].physicalY, equals(21 * dpi)); - expect(packets[0].data[0].buttons, equals(2)); - packets.clear(); + rootElement.dispatchEvent( + context.mouseMove(button: _kNoButtonChange, buttons: 2, clientX: 30.0, clientY: 31.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(30 * dpi)); + expect(packets[0].data[0].physicalY, equals(31 * dpi)); + expect(packets[0].data[0].buttons, equals(2)); + packets.clear(); - rootElement.dispatchEvent(context.mouseMove( - button: _kNoButtonChange, - buttons: 2, - clientX: 30.0, - clientY: 31.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].physicalX, equals(30 * dpi)); - expect(packets[0].data[0].physicalY, equals(31 * dpi)); - expect(packets[0].data[0].buttons, equals(2)); - packets.clear(); + rootElement.dispatchEvent(context.mouseUp(button: 2, clientX: 30.0, clientY: 31.0)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.up)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(30 * dpi)); + expect(packets[0].data[0].physicalY, equals(31 * dpi)); + expect(packets[0].data[0].buttons, equals(0)); + packets.clear(); - rootElement.dispatchEvent(context.mouseUp( - button: 2, - clientX: 30.0, - clientY: 31.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].physicalX, equals(30 * dpi)); - expect(packets[0].data[0].physicalY, equals(31 * dpi)); - expect(packets[0].data[0].buttons, equals(0)); - packets.clear(); + // Drag with middle button + rootElement.dispatchEvent( + context.mouseDown(button: 1, buttons: 4, clientX: 30.0, clientY: 31.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.down)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(30 * dpi)); + expect(packets[0].data[0].physicalY, equals(31 * dpi)); + expect(packets[0].data[0].buttons, equals(4)); + packets.clear(); - // Drag with middle button - rootElement.dispatchEvent(context.mouseDown( - button: 1, - buttons: 4, - clientX: 30.0, - clientY: 31.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.down)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].physicalX, equals(30 * dpi)); - expect(packets[0].data[0].physicalY, equals(31 * dpi)); - expect(packets[0].data[0].buttons, equals(4)); - packets.clear(); + rootElement.dispatchEvent( + context.mouseMove(button: _kNoButtonChange, buttons: 4, clientX: 40.0, clientY: 41.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(40 * dpi)); + expect(packets[0].data[0].physicalY, equals(41 * dpi)); + expect(packets[0].data[0].buttons, equals(4)); + packets.clear(); - rootElement.dispatchEvent(context.mouseMove( - button: _kNoButtonChange, - buttons: 4, - clientX: 40.0, - clientY: 41.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].physicalX, equals(40 * dpi)); - expect(packets[0].data[0].physicalY, equals(41 * dpi)); - expect(packets[0].data[0].buttons, equals(4)); - packets.clear(); + rootElement.dispatchEvent(context.mouseUp(button: 1, clientX: 40.0, clientY: 41.0)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.up)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(40 * dpi)); + expect(packets[0].data[0].physicalY, equals(41 * dpi)); + expect(packets[0].data[0].buttons, equals(0)); + packets.clear(); - rootElement.dispatchEvent(context.mouseUp( - button: 1, - clientX: 40.0, - clientY: 41.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].physicalX, equals(40 * dpi)); - expect(packets[0].data[0].physicalY, equals(41 * dpi)); - expect(packets[0].data[0].buttons, equals(0)); - packets.clear(); + // Leave - // Leave + rootElement.dispatchEvent(context.mouseLeave(buttons: 1, clientX: 1000.0, clientY: 2000.0)); + expect(packets, isEmpty); + packets.clear(); - rootElement.dispatchEvent(context.mouseLeave( - buttons: 1, - clientX: 1000.0, - clientY: 2000.0, - )); - expect(packets, isEmpty); - packets.clear(); + rootElement.dispatchEvent(context.mouseLeave(buttons: 0, clientX: 1000.0, clientY: 2000.0)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.hover)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(1000 * dpi)); + expect(packets[0].data[0].physicalY, equals(2000 * dpi)); + expect(packets[0].data[0].buttons, equals(0)); + packets.clear(); + }); - rootElement.dispatchEvent(context.mouseLeave( - buttons: 0, - clientX: 1000.0, - clientY: 2000.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.hover)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].physicalX, equals(1000 * dpi)); - expect(packets[0].data[0].physicalY, equals(2000 * dpi)); - expect(packets[0].data[0].buttons, equals(0)); - packets.clear(); - }, - ); + test('correctly handles button changes during a down sequence', () { + final _ButtonedEventMixin context = _PointerEventContext(); + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - test( - 'correctly handles button changes during a down sequence', - () { - final _ButtonedEventMixin context = _PointerEventContext(); - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + // Press LMB. + rootElement.dispatchEvent(context.mouseDown(button: 0, buttons: 1)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].synthesized, isTrue); - // Press LMB. - rootElement.dispatchEvent(context.mouseDown( - button: 0, - buttons: 1, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[1].change, equals(ui.PointerChange.down)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].buttons, equals(1)); + packets.clear(); - expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].buttons, equals(1)); - packets.clear(); + // Press MMB. + rootElement.dispatchEvent(context.mouseMove(button: 1, buttons: 5)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].buttons, equals(5)); + packets.clear(); - // Press MMB. - rootElement.dispatchEvent(context.mouseMove( - button: 1, - buttons: 5, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].buttons, equals(5)); - packets.clear(); + // Release LMB. + rootElement.dispatchEvent(context.mouseMove(button: 0, buttons: 4)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].buttons, equals(4)); + packets.clear(); - // Release LMB. - rootElement.dispatchEvent(context.mouseMove( - button: 0, - buttons: 4, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].buttons, equals(4)); - packets.clear(); + // Release MMB. + rootElement.dispatchEvent(context.mouseUp(button: 1)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.up)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].buttons, equals(0)); + packets.clear(); + }); - // Release MMB. - rootElement.dispatchEvent(context.mouseUp( - button: 1, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].buttons, equals(0)); - packets.clear(); - }, - ); + test('synthesizes a pointerup event when pointermove comes before the up', () { + final _ButtonedEventMixin context = _PointerEventContext(); + // This can happen when the user pops up the context menu by right + // clicking, then dismisses it with a left click. - test( - 'synthesizes a pointerup event when pointermove comes before the up', - () { - final _ButtonedEventMixin context = _PointerEventContext(); - // This can happen when the user pops up the context menu by right - // clicking, then dismisses it with a left click. + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + rootElement.dispatchEvent(context.mouseDown(button: 2, buttons: 2, clientX: 10, clientY: 11)); - rootElement.dispatchEvent(context.mouseDown( - button: 2, - buttons: 2, - clientX: 10, - clientY: 11, - )); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[0].physicalX, equals(10 * dpi)); + expect(packets[0].data[0].physicalY, equals(11 * dpi)); + + expect(packets[0].data[1].change, equals(ui.PointerChange.down)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].physicalX, equals(10 * dpi)); + expect(packets[0].data[1].physicalY, equals(11 * dpi)); + expect(packets[0].data[1].buttons, equals(2)); + packets.clear(); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, isTrue); - expect(packets[0].data[0].physicalX, equals(10 * dpi)); - expect(packets[0].data[0].physicalY, equals(11 * dpi)); + rootElement.dispatchEvent( + context.mouseMove(button: _kNoButtonChange, buttons: 2, clientX: 20.0, clientY: 21.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(20 * dpi)); + expect(packets[0].data[0].physicalY, equals(21 * dpi)); + expect(packets[0].data[0].buttons, equals(2)); + packets.clear(); - expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].physicalX, equals(10 * dpi)); - expect(packets[0].data[1].physicalY, equals(11 * dpi)); - expect(packets[0].data[1].buttons, equals(2)); - packets.clear(); + rootElement.dispatchEvent( + context.mouseMove(button: _kNoButtonChange, buttons: 2, clientX: 20.0, clientY: 21.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(20 * dpi)); + expect(packets[0].data[0].physicalY, equals(21 * dpi)); + expect(packets[0].data[0].buttons, equals(2)); + packets.clear(); - rootElement.dispatchEvent(context.mouseMove( - button: _kNoButtonChange, - buttons: 2, - clientX: 20.0, - clientY: 21.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].physicalX, equals(20 * dpi)); - expect(packets[0].data[0].physicalY, equals(21 * dpi)); - expect(packets[0].data[0].buttons, equals(2)); - packets.clear(); + rootElement.dispatchEvent(context.mouseUp(button: 2, clientX: 20.0, clientY: 21.0)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.up)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].physicalX, equals(20 * dpi)); + expect(packets[0].data[0].physicalY, equals(21 * dpi)); + expect(packets[0].data[0].buttons, equals(0)); + packets.clear(); + }); - rootElement.dispatchEvent(context.mouseMove( - button: _kNoButtonChange, - buttons: 2, - clientX: 20.0, - clientY: 21.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].physicalX, equals(20 * dpi)); - expect(packets[0].data[0].physicalY, equals(21 * dpi)); - expect(packets[0].data[0].buttons, equals(2)); - packets.clear(); + test('correctly handles uncontinuous button changes during a down sequence', () { + final _ButtonedEventMixin context = _PointerEventContext(); + // This can happen with the following gesture sequence: + // + // - Pops up the context menu by right clicking, but holds RMB; + // - Clicks LMB; + // - Releases RMB. - rootElement.dispatchEvent(context.mouseUp( - button: 2, - clientX: 20.0, - clientY: 21.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].physicalX, equals(20 * dpi)); - expect(packets[0].data[0].physicalY, equals(21 * dpi)); - expect(packets[0].data[0].buttons, equals(0)); - packets.clear(); - }, - ); + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - test( - 'correctly handles uncontinuous button changes during a down sequence', - () { - final _ButtonedEventMixin context = _PointerEventContext(); - // This can happen with the following gesture sequence: - // - // - Pops up the context menu by right clicking, but holds RMB; - // - Clicks LMB; - // - Releases RMB. + // Press RMB and hold, popping up the context menu. + rootElement.dispatchEvent(context.mouseDown(button: 2, buttons: 2)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].synthesized, isTrue); - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + expect(packets[0].data[1].change, equals(ui.PointerChange.down)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].buttons, equals(2)); + packets.clear(); - // Press RMB and hold, popping up the context menu. - rootElement.dispatchEvent(context.mouseDown( - button: 2, - buttons: 2, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, isTrue); + // Press LMB. The event will have "button: -1" here, despite the change + // in "buttons", probably because the "press" gesture was absorbed by + // dismissing the context menu. + rootElement.dispatchEvent(context.mouseMove(button: _kNoButtonChange, buttons: 3)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].buttons, equals(3)); + packets.clear(); - expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].buttons, equals(2)); - packets.clear(); + // Release LMB. + rootElement.dispatchEvent(context.mouseMove(button: 0, buttons: 2)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].buttons, equals(2)); + packets.clear(); - // Press LMB. The event will have "button: -1" here, despite the change - // in "buttons", probably because the "press" gesture was absorbed by - // dismissing the context menu. - rootElement.dispatchEvent(context.mouseMove( - button: _kNoButtonChange, - buttons: 3, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].buttons, equals(3)); - packets.clear(); + // Release RMB. + rootElement.dispatchEvent(context.mouseUp(button: 2)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.up)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].buttons, equals(0)); + packets.clear(); + }); - // Release LMB. - rootElement.dispatchEvent(context.mouseMove( - button: 0, - buttons: 2, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].buttons, equals(2)); - packets.clear(); + test('correctly handles missing right mouse button up when followed by move', () { + final _ButtonedEventMixin context = _PointerEventContext(); + // This can happen with the following gesture sequence: + // + // - Pops up the context menu by right clicking; + // - Clicks LMB to close context menu. + // - Moves mouse. - // Release RMB. - rootElement.dispatchEvent(context.mouseUp( - button: 2, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].buttons, equals(0)); - packets.clear(); - }, - ); + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - test( - 'correctly handles missing right mouse button up when followed by move', - () { - final _ButtonedEventMixin context = _PointerEventContext(); - // This can happen with the following gesture sequence: - // - // - Pops up the context menu by right clicking; - // - Clicks LMB to close context menu. - // - Moves mouse. + // Press RMB popping up the context menu, then release by LMB down and up. + // Browser won't send up event in that case. + rootElement.dispatchEvent(context.mouseDown(button: 2, buttons: 2)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].synthesized, isTrue); - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + expect(packets[0].data[1].change, equals(ui.PointerChange.down)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].buttons, equals(2)); + packets.clear(); - // Press RMB popping up the context menu, then release by LMB down and up. - // Browser won't send up event in that case. - rootElement.dispatchEvent(context.mouseDown( - button: 2, - buttons: 2, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, isTrue); + // User now hovers. + rootElement.dispatchEvent(context.mouseMove(button: _kNoButtonChange, buttons: 0)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.up)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].buttons, equals(0)); + expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].buttons, equals(0)); + packets.clear(); + }); - expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].buttons, equals(2)); - packets.clear(); + test('handles RMB click when the browser sends it as a move', () { + final _ButtonedEventMixin context = _PointerEventContext(); + // When the user clicks the RMB and moves the mouse quickly (before the + // context menu shows up), the browser sends a move event before down. + // The move event will have "button:-1, buttons:2". - // User now hovers. - rootElement.dispatchEvent(context.mouseMove( - button: _kNoButtonChange, - buttons: 0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].buttons, equals(0)); - expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].buttons, equals(0)); - packets.clear(); - }, - ); + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - test( - 'handles RMB click when the browser sends it as a move', - () { - final _ButtonedEventMixin context = _PointerEventContext(); - // When the user clicks the RMB and moves the mouse quickly (before the - // context menu shows up), the browser sends a move event before down. - // The move event will have "button:-1, buttons:2". + // Press RMB and hold, popping up the context menu. + rootElement.dispatchEvent( + context.mouseMove(button: -1, buttons: 2, clientX: 10.0, clientY: 10.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].synthesized, isTrue); - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].buttons, equals(0)); + packets.clear(); + }); - // Press RMB and hold, popping up the context menu. - rootElement.dispatchEvent(context.mouseMove( - button: -1, - buttons: 2, - clientX: 10.0, - clientY: 10.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, isTrue); + test('correctly handles hover after RMB click', () { + final _ButtonedEventMixin context = _PointerEventContext(); + // This can happen with the following gesture sequence: + // + // - Pops up the context menu by right clicking, but holds RMB; + // - Move the pointer to hover. - expect(packets[0].data[1].change, equals(ui.PointerChange.hover)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].buttons, equals(0)); - packets.clear(); - }, - ); + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - test( - 'correctly handles hover after RMB click', - () { - final _ButtonedEventMixin context = _PointerEventContext(); - // This can happen with the following gesture sequence: - // - // - Pops up the context menu by right clicking, but holds RMB; - // - Move the pointer to hover. + // Press RMB and hold, popping up the context menu. + rootElement.dispatchEvent( + context.mouseDown(button: 2, buttons: 2, clientX: 10.0, clientY: 10.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].synthesized, isTrue); - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + expect(packets[0].data[1].change, equals(ui.PointerChange.down)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].buttons, equals(2)); + packets.clear(); - // Press RMB and hold, popping up the context menu. - rootElement.dispatchEvent(context.mouseDown( - button: 2, - buttons: 2, - clientX: 10.0, - clientY: 10.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, isTrue); + // Move the mouse. The event will have "buttons: 0" because RMB was + // released but the browser didn't send a pointerup/mouseup event. + // The hover is also triggered at a different position. + rootElement.dispatchEvent(context.hover(clientX: 20.0, clientY: 20.0)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(3)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[0].buttons, equals(2)); + expect(packets[0].data[1].change, equals(ui.PointerChange.up)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].buttons, equals(0)); + expect(packets[0].data[2].change, equals(ui.PointerChange.hover)); + expect(packets[0].data[2].synthesized, isFalse); + expect(packets[0].data[2].buttons, equals(0)); + packets.clear(); + }); - expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].buttons, equals(2)); - packets.clear(); + test('correctly handles LMB click after RMB click', () { + final _ButtonedEventMixin context = _PointerEventContext(); + // This can happen with the following gesture sequence: + // + // - Pops up the context menu by right clicking, but holds RMB; + // - Clicks LMB in a different location; + // - Release LMB. + // + // The LMB click occurs in a different location because when RMB is + // clicked, and the contextmenu is shown, the browser stops sending + // `pointermove`/`mousemove` events. Then when the LMB click comes in, it + // could be in a different location without any `*move` events in between. - // Move the mouse. The event will have "buttons: 0" because RMB was - // released but the browser didn't send a pointerup/mouseup event. - // The hover is also triggered at a different position. - rootElement.dispatchEvent(context.hover( - clientX: 20.0, - clientY: 20.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(3)); - expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, isTrue); - expect(packets[0].data[0].buttons, equals(2)); - expect(packets[0].data[1].change, equals(ui.PointerChange.up)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].buttons, equals(0)); - expect(packets[0].data[2].change, equals(ui.PointerChange.hover)); - expect(packets[0].data[2].synthesized, isFalse); - expect(packets[0].data[2].buttons, equals(0)); - packets.clear(); - }, - ); + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - test( - 'correctly handles LMB click after RMB click', - () { - final _ButtonedEventMixin context = _PointerEventContext(); - // This can happen with the following gesture sequence: - // - // - Pops up the context menu by right clicking, but holds RMB; - // - Clicks LMB in a different location; - // - Release LMB. - // - // The LMB click occurs in a different location because when RMB is - // clicked, and the contextmenu is shown, the browser stops sending - // `pointermove`/`mousemove` events. Then when the LMB click comes in, it - // could be in a different location without any `*move` events in between. + // Press RMB and hold, popping up the context menu. + rootElement.dispatchEvent( + context.mouseDown(button: 2, buttons: 2, clientX: 10.0, clientY: 10.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].synthesized, isTrue); - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + expect(packets[0].data[1].change, equals(ui.PointerChange.down)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].buttons, equals(2)); + packets.clear(); - // Press RMB and hold, popping up the context menu. - rootElement.dispatchEvent(context.mouseDown( - button: 2, - buttons: 2, - clientX: 10.0, - clientY: 10.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, isTrue); + // Press LMB. + rootElement.dispatchEvent( + context.mouseDown(button: 0, buttons: 3, clientX: 20.0, clientY: 20.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].buttons, equals(3)); + packets.clear(); - expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].buttons, equals(2)); - packets.clear(); + // Release LMB. + rootElement.dispatchEvent(context.primaryUp(clientX: 20.0, clientY: 20.0)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.up)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].buttons, equals(0)); + packets.clear(); + }); - // Press LMB. - rootElement.dispatchEvent(context.mouseDown( - button: 0, - buttons: 3, - clientX: 20.0, - clientY: 20.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].buttons, equals(3)); - packets.clear(); + test('correctly handles two consecutive RMB clicks with no up in between', () { + final _ButtonedEventMixin context = _PointerEventContext(); + // This can happen with the following gesture sequence: + // + // - Pops up the context menu by right clicking, but holds RMB; + // - Clicks RMB again in a different location; - // Release LMB. - rootElement.dispatchEvent(context.primaryUp( - clientX: 20.0, - clientY: 20.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].buttons, equals(0)); - packets.clear(); - }, - ); + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - test( - 'correctly handles two consecutive RMB clicks with no up in between', - () { - final _ButtonedEventMixin context = _PointerEventContext(); - // This can happen with the following gesture sequence: - // - // - Pops up the context menu by right clicking, but holds RMB; - // - Clicks RMB again in a different location; + // Press RMB and hold, popping up the context menu. + rootElement.dispatchEvent( + context.mouseDown(button: 2, buttons: 2, clientX: 10.0, clientY: 10.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[1].change, equals(ui.PointerChange.down)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].buttons, equals(2)); + packets.clear(); - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + // Press RMB again. In Chrome, when RMB is clicked again while the + // context menu is still active, it sends a pointerdown/mousedown event + // with "buttons:0". We convert this to pointer up, pointer down. + rootElement.dispatchEvent( + context.mouseDown(button: 2, buttons: 0, clientX: 20.0, clientY: 20.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(3)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[0].buttons, equals(2)); + expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(20.0 * dpi)); + expect(packets[0].data[1].change, equals(ui.PointerChange.up)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].buttons, equals(0)); + expect(packets[0].data[2].change, equals(ui.PointerChange.down)); + expect(packets[0].data[2].synthesized, isFalse); + expect(packets[0].data[2].buttons, equals(2)); + packets.clear(); - // Press RMB and hold, popping up the context menu. - rootElement.dispatchEvent(context.mouseDown( - button: 2, - buttons: 2, - clientX: 10.0, - clientY: 10.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, isTrue); - expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].buttons, equals(2)); - packets.clear(); + // Release RMB. + rootElement.dispatchEvent(context.mouseUp(button: 2, clientX: 20.0, clientY: 20.0)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.up)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].buttons, equals(0)); + packets.clear(); + }); - // Press RMB again. In Chrome, when RMB is clicked again while the - // context menu is still active, it sends a pointerdown/mousedown event - // with "buttons:0". We convert this to pointer up, pointer down. - rootElement.dispatchEvent(context.mouseDown( - button: 2, - buttons: 0, - clientX: 20.0, - clientY: 20.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(3)); - expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].synthesized, isTrue); - expect(packets[0].data[0].buttons, equals(2)); - expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); - expect(packets[0].data[0].physicalY, equals(20.0 * dpi)); - expect(packets[0].data[1].change, equals(ui.PointerChange.up)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].buttons, equals(0)); - expect(packets[0].data[2].change, equals(ui.PointerChange.down)); - expect(packets[0].data[2].synthesized, isFalse); - expect(packets[0].data[2].buttons, equals(2)); - packets.clear(); + test('correctly handles two consecutive RMB clicks with up in between', () { + final _ButtonedEventMixin context = _PointerEventContext(); + // This can happen with the following gesture sequence: + // + // - Pops up the context menu by right clicking, but doesn't hold RMB; + // - Clicks RMB again in a different location; + // + // This seems to be happening sometimes when using RMB on the Mac trackpad. - // Release RMB. - rootElement.dispatchEvent(context.mouseUp( - button: 2, - clientX: 20.0, - clientY: 20.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].buttons, equals(0)); - packets.clear(); - }, - ); - - test( - 'correctly handles two consecutive RMB clicks with up in between', - () { - final _ButtonedEventMixin context = _PointerEventContext(); - // This can happen with the following gesture sequence: - // - // - Pops up the context menu by right clicking, but doesn't hold RMB; - // - Clicks RMB again in a different location; - // - // This seems to be happening sometimes when using RMB on the Mac trackpad. - - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - // Press RMB, popping up the context menu. - rootElement.dispatchEvent(context.mouseDown( - button: 2, - buttons: 2, - clientX: 10.0, - clientY: 10.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, isTrue); - expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].buttons, equals(2)); - packets.clear(); + // Press RMB, popping up the context menu. + rootElement.dispatchEvent( + context.mouseDown(button: 2, buttons: 2, clientX: 10.0, clientY: 10.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[1].change, equals(ui.PointerChange.down)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].buttons, equals(2)); + packets.clear(); - // RMB up. - rootElement.dispatchEvent(context.mouseUp( - button: 2, - clientX: 10.0, - clientY: 10.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].buttons, equals(0)); - packets.clear(); + // RMB up. + rootElement.dispatchEvent(context.mouseUp(button: 2, clientX: 10.0, clientY: 10.0)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.up)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].buttons, equals(0)); + packets.clear(); - // Press RMB again. In Chrome, when RMB is clicked again while the - // context menu is still active, it sends a pointerdown/mousedown event - // with "buttons:0". - rootElement.dispatchEvent(context.mouseDown( - button: 2, - buttons: 0, - clientX: 20.0, - clientY: 20.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.hover)); - expect(packets[0].data[0].synthesized, isTrue); - expect(packets[0].data[0].buttons, equals(0)); - expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].buttons, equals(2)); - packets.clear(); + // Press RMB again. In Chrome, when RMB is clicked again while the + // context menu is still active, it sends a pointerdown/mousedown event + // with "buttons:0". + rootElement.dispatchEvent( + context.mouseDown(button: 2, buttons: 0, clientX: 20.0, clientY: 20.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.hover)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[0].buttons, equals(0)); + expect(packets[0].data[1].change, equals(ui.PointerChange.down)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].buttons, equals(2)); + packets.clear(); - // Release RMB. - rootElement.dispatchEvent(context.mouseUp( - button: 2, - clientX: 20.0, - clientY: 20.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].buttons, equals(0)); - packets.clear(); - }, - ); + // Release RMB. + rootElement.dispatchEvent(context.mouseUp(button: 2, clientX: 20.0, clientY: 20.0)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.up)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].buttons, equals(0)); + packets.clear(); + }); - test( - 'correctly handles two consecutive RMB clicks in two different locations', - () { - final _ButtonedEventMixin context = _PointerEventContext(); - // This can happen with the following gesture sequence: - // - // - Pops up the context menu by right clicking; - // - The browser sends RMB up event; - // - Click RMB again in a different location; - // - // This scenario happens occasionally. I'm still not sure why, but in some - // cases, the browser actually sends an `up` event for the RMB click even - // when the context menu is shown. + test('correctly handles two consecutive RMB clicks in two different locations', () { + final _ButtonedEventMixin context = _PointerEventContext(); + // This can happen with the following gesture sequence: + // + // - Pops up the context menu by right clicking; + // - The browser sends RMB up event; + // - Click RMB again in a different location; + // + // This scenario happens occasionally. I'm still not sure why, but in some + // cases, the browser actually sends an `up` event for the RMB click even + // when the context menu is shown. - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - // Press RMB and hold, popping up the context menu. - rootElement.dispatchEvent(context.mouseDown( - button: 2, - buttons: 2, - clientX: 10.0, - clientY: 10.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, isTrue); - expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].buttons, equals(2)); - packets.clear(); + // Press RMB and hold, popping up the context menu. + rootElement.dispatchEvent( + context.mouseDown(button: 2, buttons: 2, clientX: 10.0, clientY: 10.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[1].change, equals(ui.PointerChange.down)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].buttons, equals(2)); + packets.clear(); - // Release RMB. - rootElement.dispatchEvent(context.mouseUp( - button: 2, - clientX: 10.0, - clientY: 10.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].buttons, equals(0)); - packets.clear(); + // Release RMB. + rootElement.dispatchEvent(context.mouseUp(button: 2, clientX: 10.0, clientY: 10.0)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.up)); + expect(packets[0].data[0].buttons, equals(0)); + packets.clear(); - // Press RMB again, in a different location. - rootElement.dispatchEvent(context.mouseDown( - button: 2, - buttons: 2, - clientX: 20.0, - clientY: 20.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.hover)); - expect(packets[0].data[0].synthesized, isTrue); - expect(packets[0].data[0].buttons, equals(0)); - expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].buttons, equals(2)); - packets.clear(); - }, - ); + // Press RMB again, in a different location. + rootElement.dispatchEvent( + context.mouseDown(button: 2, buttons: 2, clientX: 20.0, clientY: 20.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.hover)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[0].buttons, equals(0)); + expect(packets[0].data[1].change, equals(ui.PointerChange.down)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].buttons, equals(2)); + packets.clear(); + }); - test( - 'handles overlapping left/right down and up events', - () { - final _ButtonedEventMixin context = _PointerEventContext(); - // This can happen with the following gesture sequence: - // - // LMB: down-------------------up - // RMB: down------------------up - // Flutter: down-------move-------move-------up + test('handles overlapping left/right down and up events', () { + final _ButtonedEventMixin context = _PointerEventContext(); + // This can happen with the following gesture sequence: + // + // LMB: down-------------------up + // RMB: down------------------up + // Flutter: down-------move-------move-------up - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - // Press and hold LMB. - rootElement.dispatchEvent(context.mouseDown( - button: 0, - buttons: 1, - clientX: 5.0, - clientY: 100.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, isTrue); - expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].buttons, equals(1)); - expect(packets[0].data[1].physicalX, equals(5.0 * dpi)); - expect(packets[0].data[1].physicalY, equals(100.0 * dpi)); - packets.clear(); + // Press and hold LMB. + rootElement.dispatchEvent( + context.mouseDown(button: 0, buttons: 1, clientX: 5.0, clientY: 100.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[1].change, equals(ui.PointerChange.down)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].buttons, equals(1)); + expect(packets[0].data[1].physicalX, equals(5.0 * dpi)); + expect(packets[0].data[1].physicalY, equals(100.0 * dpi)); + packets.clear(); - // Press and hold RMB. The pointer is already down, so we only send a move - // to update the position of the pointer. - rootElement.dispatchEvent(context.mouseDown( - button: 2, - buttons: 3, - clientX: 20.0, - clientY: 100.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].buttons, equals(3)); - expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); - expect(packets[0].data[0].physicalY, equals(100.0 * dpi)); - packets.clear(); + // Press and hold RMB. The pointer is already down, so we only send a move + // to update the position of the pointer. + rootElement.dispatchEvent( + context.mouseDown(button: 2, buttons: 3, clientX: 20.0, clientY: 100.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].buttons, equals(3)); + expect(packets[0].data[0].physicalX, equals(20.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(100.0 * dpi)); + packets.clear(); - // Release LMB. The pointer is still down (RMB), so we only send a move to - // update the position of the pointer. - rootElement.dispatchEvent(context.mouseUp( - button: 0, - buttons: 2, - clientX: 30.0, - clientY: 100.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].buttons, equals(2)); - expect(packets[0].data[0].physicalX, equals(30.0 * dpi)); - expect(packets[0].data[0].physicalY, equals(100.0 * dpi)); - packets.clear(); + // Release LMB. The pointer is still down (RMB), so we only send a move to + // update the position of the pointer. + rootElement.dispatchEvent( + context.mouseUp(button: 0, buttons: 2, clientX: 30.0, clientY: 100.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].buttons, equals(2)); + expect(packets[0].data[0].physicalX, equals(30.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(100.0 * dpi)); + packets.clear(); - // Release RMB. There's no more buttons down, so we send an up event. - rootElement.dispatchEvent(context.mouseUp( - button: 2, - buttons: 0, - clientX: 30.0, - clientY: 100.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].buttons, equals(0)); - packets.clear(); - }, - ); + // Release RMB. There's no more buttons down, so we send an up event. + rootElement.dispatchEvent( + context.mouseUp(button: 2, buttons: 0, clientX: 30.0, clientY: 100.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.up)); + expect(packets[0].data[0].buttons, equals(0)); + packets.clear(); + }); - test( - 'correctly detects up event outside of flutterViewElement', - () { - final _ButtonedEventMixin context = _PointerEventContext(); - // This can happen when the up event occurs while the mouse is outside the - // browser window. + test('correctly detects up event outside of flutterViewElement', () { + final _ButtonedEventMixin context = _PointerEventContext(); + // This can happen when the up event occurs while the mouse is outside the + // browser window. - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - // Press and drag around. - rootElement.dispatchEvent(context.primaryDown( - clientX: 10.0, - clientY: 10.0, - )); - rootElement.dispatchEvent(context.primaryMove( - clientX: 12.0, - clientY: 10.0, - )); - rootElement.dispatchEvent(context.primaryMove( - clientX: 15.0, - clientY: 10.0, - )); - rootElement.dispatchEvent(context.primaryMove( - clientX: 20.0, - clientY: 10.0, - )); - packets.clear(); + // Press and drag around. + rootElement.dispatchEvent(context.primaryDown(clientX: 10.0, clientY: 10.0)); + rootElement.dispatchEvent(context.primaryMove(clientX: 12.0, clientY: 10.0)); + rootElement.dispatchEvent(context.primaryMove(clientX: 15.0, clientY: 10.0)); + rootElement.dispatchEvent(context.primaryMove(clientX: 20.0, clientY: 10.0)); + packets.clear(); - // Move outside the flutterViewElement. - rootElement.dispatchEvent(context.primaryMove( - clientX: 900.0, - clientY: 1900.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].physicalX, equals(900.0 * dpi)); - expect(packets[0].data[0].physicalY, equals(1900.0 * dpi)); - packets.clear(); + // Move outside the flutterViewElement. + rootElement.dispatchEvent(context.primaryMove(clientX: 900.0, clientY: 1900.0)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].physicalX, equals(900.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(1900.0 * dpi)); + packets.clear(); - // Release outside the flutterViewElement. - rootElement.dispatchEvent(context.primaryUp( - clientX: 1000.0, - clientY: 2000.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.move)); - expect(packets[0].data[0].physicalX, equals(1000.0 * dpi)); - expect(packets[0].data[0].physicalY, equals(2000.0 * dpi)); - expect(packets[0].data[1].change, equals(ui.PointerChange.up)); - expect(packets[0].data[1].physicalX, equals(1000.0 * dpi)); - expect(packets[0].data[1].physicalY, equals(2000.0 * dpi)); - packets.clear(); - }, - ); + // Release outside the flutterViewElement. + rootElement.dispatchEvent(context.primaryUp(clientX: 1000.0, clientY: 2000.0)); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.move)); + expect(packets[0].data[0].physicalX, equals(1000.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(2000.0 * dpi)); + expect(packets[0].data[1].change, equals(ui.PointerChange.up)); + expect(packets[0].data[1].physicalX, equals(1000.0 * dpi)); + expect(packets[0].data[1].physicalY, equals(2000.0 * dpi)); + packets.clear(); + }); // STYLUS - test( - 'handles stylus touches', - () { - // Repeated stylus touches use different pointerIds. + test('handles stylus touches', () { + // Repeated stylus touches use different pointerIds. - final _PointerEventContext context = _PointerEventContext(); + final _PointerEventContext context = _PointerEventContext(); - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - rootElement.dispatchEvent(context.stylusTouchDown( - pointerId: 100, - buttons: 1, - clientX: 5.0, - clientY: 100.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, isTrue); - expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].buttons, equals(1)); - expect(packets[0].data[1].physicalX, equals(5.0 * dpi)); - expect(packets[0].data[1].physicalY, equals(100.0 * dpi)); - packets.clear(); + rootElement.dispatchEvent( + context.stylusTouchDown(pointerId: 100, buttons: 1, clientX: 5.0, clientY: 100.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[1].change, equals(ui.PointerChange.down)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].buttons, equals(1)); + expect(packets[0].data[1].physicalX, equals(5.0 * dpi)); + expect(packets[0].data[1].physicalY, equals(100.0 * dpi)); + packets.clear(); - rootElement.dispatchEvent(context.stylusTouchUp( - pointerId: 100, - buttons: 0, - clientX: 5.0, - clientY: 100.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].buttons, equals(0)); - expect(packets[0].data[0].physicalX, equals(5.0 * dpi)); - expect(packets[0].data[0].physicalY, equals(100.0 * dpi)); - packets.clear(); + rootElement.dispatchEvent( + context.stylusTouchUp(pointerId: 100, buttons: 0, clientX: 5.0, clientY: 100.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.up)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].buttons, equals(0)); + expect(packets[0].data[0].physicalX, equals(5.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(100.0 * dpi)); + packets.clear(); - rootElement.dispatchEvent(context.stylusTouchDown( - pointerId: 101, - buttons: 1, - clientX: 5.0, - clientY: 150.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.hover)); - expect(packets[0].data[0].synthesized, isTrue); - expect(packets[0].data[0].buttons, equals(0)); - expect(packets[0].data[0].physicalX, equals(5.0 * dpi)); - expect(packets[0].data[0].physicalY, equals(150.0 * dpi)); - expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].synthesized, isFalse); - expect(packets[0].data[1].buttons, equals(1)); - expect(packets[0].data[1].physicalX, equals(5.0 * dpi)); - expect(packets[0].data[1].physicalY, equals(150.0 * dpi)); - packets.clear(); + rootElement.dispatchEvent( + context.stylusTouchDown(pointerId: 101, buttons: 1, clientX: 5.0, clientY: 150.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.hover)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[0].buttons, equals(0)); + expect(packets[0].data[0].physicalX, equals(5.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(150.0 * dpi)); + expect(packets[0].data[1].change, equals(ui.PointerChange.down)); + expect(packets[0].data[1].synthesized, isFalse); + expect(packets[0].data[1].buttons, equals(1)); + expect(packets[0].data[1].physicalX, equals(5.0 * dpi)); + expect(packets[0].data[1].physicalY, equals(150.0 * dpi)); + packets.clear(); - rootElement.dispatchEvent(context.stylusTouchUp( - pointerId: 101, - buttons: 0, - clientX: 5.0, - clientY: 150.0, - )); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(1)); - expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].synthesized, isFalse); - expect(packets[0].data[0].buttons, equals(0)); - expect(packets[0].data[0].physicalX, equals(5.0 * dpi)); - expect(packets[0].data[0].physicalY, equals(150.0 * dpi)); - packets.clear(); - }, - ); + rootElement.dispatchEvent( + context.stylusTouchUp(pointerId: 101, buttons: 0, clientX: 5.0, clientY: 150.0), + ); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(1)); + expect(packets[0].data[0].change, equals(ui.PointerChange.up)); + expect(packets[0].data[0].synthesized, isFalse); + expect(packets[0].data[0].buttons, equals(0)); + expect(packets[0].data[0].physicalX, equals(5.0 * dpi)); + expect(packets[0].data[0].physicalY, equals(150.0 * dpi)); + packets.clear(); + }); // MULTIPOINTER ADAPTERS - test( - 'treats each pointer separately', - () { - final _MultiPointerEventMixin context = _PointerEventContext(); - final List packets = []; - List data; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + test('treats each pointer separately', () { + final _MultiPointerEventMixin context = _PointerEventContext(); + final List packets = []; + List data; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - // Two pointers down - context.multiTouchDown(const <_TouchDetails>[ - _TouchDetails(pointer: 2, clientX: 100, clientY: 101), - _TouchDetails(pointer: 3, clientX: 200, clientY: 201), - ]).forEach(rootElement.dispatchEvent); - expect(packets.length, 2); - expect(packets[0].data.length, 2); - expect(packets[1].data.length, 2); - - data = _allPointerData(packets); - expect(data, hasLength(4)); - expect(data[0].change, equals(ui.PointerChange.add)); - expect(data[0].synthesized, isTrue); - expect(data[0].device, equals(2)); - expect(data[0].physicalX, equals(100 * dpi)); - expect(data[0].physicalY, equals(101 * dpi)); - - expect(data[1].change, equals(ui.PointerChange.down)); - expect(data[1].device, equals(2)); - expect(data[1].buttons, equals(1)); - expect(data[1].physicalX, equals(100 * dpi)); - expect(data[1].physicalY, equals(101 * dpi)); - expect(data[1].physicalDeltaX, equals(0)); - expect(data[1].physicalDeltaY, equals(0)); - - expect(data[2].change, equals(ui.PointerChange.add)); - expect(data[2].synthesized, isTrue); - expect(data[2].device, equals(3)); - expect(data[2].physicalX, equals(200 * dpi)); - expect(data[2].physicalY, equals(201 * dpi)); - - expect(data[3].change, equals(ui.PointerChange.down)); - expect(data[3].device, equals(3)); - expect(data[3].buttons, equals(1)); - expect(data[3].physicalX, equals(200 * dpi)); - expect(data[3].physicalY, equals(201 * dpi)); - expect(data[3].physicalDeltaX, equals(0)); - expect(data[3].physicalDeltaY, equals(0)); - packets.clear(); + // Two pointers down + context + .multiTouchDown(const <_TouchDetails>[ + _TouchDetails(pointer: 2, clientX: 100, clientY: 101), + _TouchDetails(pointer: 3, clientX: 200, clientY: 201), + ]) + .forEach(rootElement.dispatchEvent); + expect(packets.length, 2); + expect(packets[0].data.length, 2); + expect(packets[1].data.length, 2); + + data = _allPointerData(packets); + expect(data, hasLength(4)); + expect(data[0].change, equals(ui.PointerChange.add)); + expect(data[0].synthesized, isTrue); + expect(data[0].device, equals(2)); + expect(data[0].physicalX, equals(100 * dpi)); + expect(data[0].physicalY, equals(101 * dpi)); - // Two pointers move - context.multiTouchMove(const <_TouchDetails>[ - _TouchDetails(pointer: 3, clientX: 300, clientY: 302), - _TouchDetails(pointer: 2, clientX: 400, clientY: 402), - ]).forEach(rootElement.dispatchEvent); - expect(packets.length, 2); - expect(packets[0].data.length, 1); - expect(packets[1].data.length, 1); - - data = _allPointerData(packets); - expect(data, hasLength(2)); - expect(data[0].change, equals(ui.PointerChange.move)); - expect(data[0].device, equals(3)); - expect(data[0].buttons, equals(1)); - expect(data[0].physicalX, equals(300 * dpi)); - expect(data[0].physicalY, equals(302 * dpi)); - expect(data[0].physicalDeltaX, equals(100 * dpi)); - expect(data[0].physicalDeltaY, equals(101 * dpi)); - - expect(data[1].change, equals(ui.PointerChange.move)); - expect(data[1].device, equals(2)); - expect(data[1].buttons, equals(1)); - expect(data[1].physicalX, equals(400 * dpi)); - expect(data[1].physicalY, equals(402 * dpi)); - expect(data[1].physicalDeltaX, equals(300 * dpi)); - expect(data[1].physicalDeltaY, equals(301 * dpi)); - packets.clear(); + expect(data[1].change, equals(ui.PointerChange.down)); + expect(data[1].device, equals(2)); + expect(data[1].buttons, equals(1)); + expect(data[1].physicalX, equals(100 * dpi)); + expect(data[1].physicalY, equals(101 * dpi)); + expect(data[1].physicalDeltaX, equals(0)); + expect(data[1].physicalDeltaY, equals(0)); - // One pointer up - context.multiTouchUp(const <_TouchDetails>[ - _TouchDetails(pointer: 3, clientX: 300, clientY: 302), - ]).forEach(rootElement.dispatchEvent); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].device, equals(3)); - expect(packets[0].data[0].buttons, equals(0)); - expect(packets[0].data[0].physicalX, equals(300 * dpi)); - expect(packets[0].data[0].physicalY, equals(302 * dpi)); - expect(packets[0].data[0].physicalDeltaX, equals(0)); - expect(packets[0].data[0].physicalDeltaY, equals(0)); - - expect(packets[0].data[1].change, equals(ui.PointerChange.remove)); - expect(packets[0].data[1].device, equals(3)); - expect(packets[0].data[1].buttons, equals(0)); - expect(packets[0].data[1].physicalX, equals(300 * dpi)); - expect(packets[0].data[1].physicalY, equals(302 * dpi)); - expect(packets[0].data[1].physicalDeltaX, equals(0)); - expect(packets[0].data[1].physicalDeltaY, equals(0)); - packets.clear(); + expect(data[2].change, equals(ui.PointerChange.add)); + expect(data[2].synthesized, isTrue); + expect(data[2].device, equals(3)); + expect(data[2].physicalX, equals(200 * dpi)); + expect(data[2].physicalY, equals(201 * dpi)); + + expect(data[3].change, equals(ui.PointerChange.down)); + expect(data[3].device, equals(3)); + expect(data[3].buttons, equals(1)); + expect(data[3].physicalX, equals(200 * dpi)); + expect(data[3].physicalY, equals(201 * dpi)); + expect(data[3].physicalDeltaX, equals(0)); + expect(data[3].physicalDeltaY, equals(0)); + packets.clear(); + + // Two pointers move + context + .multiTouchMove(const <_TouchDetails>[ + _TouchDetails(pointer: 3, clientX: 300, clientY: 302), + _TouchDetails(pointer: 2, clientX: 400, clientY: 402), + ]) + .forEach(rootElement.dispatchEvent); + expect(packets.length, 2); + expect(packets[0].data.length, 1); + expect(packets[1].data.length, 1); + + data = _allPointerData(packets); + expect(data, hasLength(2)); + expect(data[0].change, equals(ui.PointerChange.move)); + expect(data[0].device, equals(3)); + expect(data[0].buttons, equals(1)); + expect(data[0].physicalX, equals(300 * dpi)); + expect(data[0].physicalY, equals(302 * dpi)); + expect(data[0].physicalDeltaX, equals(100 * dpi)); + expect(data[0].physicalDeltaY, equals(101 * dpi)); + + expect(data[1].change, equals(ui.PointerChange.move)); + expect(data[1].device, equals(2)); + expect(data[1].buttons, equals(1)); + expect(data[1].physicalX, equals(400 * dpi)); + expect(data[1].physicalY, equals(402 * dpi)); + expect(data[1].physicalDeltaX, equals(300 * dpi)); + expect(data[1].physicalDeltaY, equals(301 * dpi)); + packets.clear(); + + // One pointer up + context + .multiTouchUp(const <_TouchDetails>[_TouchDetails(pointer: 3, clientX: 300, clientY: 302)]) + .forEach(rootElement.dispatchEvent); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.up)); + expect(packets[0].data[0].device, equals(3)); + expect(packets[0].data[0].buttons, equals(0)); + expect(packets[0].data[0].physicalX, equals(300 * dpi)); + expect(packets[0].data[0].physicalY, equals(302 * dpi)); + expect(packets[0].data[0].physicalDeltaX, equals(0)); + expect(packets[0].data[0].physicalDeltaY, equals(0)); + + expect(packets[0].data[1].change, equals(ui.PointerChange.remove)); + expect(packets[0].data[1].device, equals(3)); + expect(packets[0].data[1].buttons, equals(0)); + expect(packets[0].data[1].physicalX, equals(300 * dpi)); + expect(packets[0].data[1].physicalY, equals(302 * dpi)); + expect(packets[0].data[1].physicalDeltaX, equals(0)); + expect(packets[0].data[1].physicalDeltaY, equals(0)); + packets.clear(); + + // Another pointer up + context + .multiTouchUp(const <_TouchDetails>[_TouchDetails(pointer: 2, clientX: 400, clientY: 402)]) + .forEach(rootElement.dispatchEvent); + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.up)); + expect(packets[0].data[0].device, equals(2)); + expect(packets[0].data[0].buttons, equals(0)); + expect(packets[0].data[0].physicalX, equals(400 * dpi)); + expect(packets[0].data[0].physicalY, equals(402 * dpi)); + expect(packets[0].data[0].physicalDeltaX, equals(0)); + expect(packets[0].data[0].physicalDeltaY, equals(0)); + + expect(packets[0].data[1].change, equals(ui.PointerChange.remove)); + expect(packets[0].data[1].device, equals(2)); + expect(packets[0].data[1].buttons, equals(0)); + expect(packets[0].data[1].physicalX, equals(400 * dpi)); + expect(packets[0].data[1].physicalY, equals(402 * dpi)); + expect(packets[0].data[1].physicalDeltaX, equals(0)); + expect(packets[0].data[1].physicalDeltaY, equals(0)); + packets.clear(); - // Another pointer up - context.multiTouchUp(const <_TouchDetails>[ - _TouchDetails(pointer: 2, clientX: 400, clientY: 402), - ]).forEach(rootElement.dispatchEvent); - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.up)); - expect(packets[0].data[0].device, equals(2)); - expect(packets[0].data[0].buttons, equals(0)); - expect(packets[0].data[0].physicalX, equals(400 * dpi)); - expect(packets[0].data[0].physicalY, equals(402 * dpi)); - expect(packets[0].data[0].physicalDeltaX, equals(0)); - expect(packets[0].data[0].physicalDeltaY, equals(0)); - - expect(packets[0].data[1].change, equals(ui.PointerChange.remove)); - expect(packets[0].data[1].device, equals(2)); - expect(packets[0].data[1].buttons, equals(0)); - expect(packets[0].data[1].physicalX, equals(400 * dpi)); - expect(packets[0].data[1].physicalY, equals(402 * dpi)); - expect(packets[0].data[1].physicalDeltaX, equals(0)); - expect(packets[0].data[1].physicalDeltaY, equals(0)); - packets.clear(); + // Again two pointers down (reuse pointer ID) + context + .multiTouchDown(const <_TouchDetails>[ + _TouchDetails(pointer: 3, clientX: 500, clientY: 501), + _TouchDetails(pointer: 2, clientX: 600, clientY: 601), + ]) + .forEach(rootElement.dispatchEvent); + expect(packets.length, 2); + expect(packets[0].data.length, 2); + expect(packets[1].data.length, 2); + + data = _allPointerData(packets); + expect(data, hasLength(4)); + expect(data[0].change, equals(ui.PointerChange.add)); + expect(data[0].synthesized, isTrue); + expect(data[0].device, equals(3)); + expect(data[0].physicalX, equals(500 * dpi)); + expect(data[0].physicalY, equals(501 * dpi)); - // Again two pointers down (reuse pointer ID) - context.multiTouchDown(const <_TouchDetails>[ - _TouchDetails(pointer: 3, clientX: 500, clientY: 501), - _TouchDetails(pointer: 2, clientX: 600, clientY: 601), - ]).forEach(rootElement.dispatchEvent); - expect(packets.length, 2); - expect(packets[0].data.length, 2); - expect(packets[1].data.length, 2); - - data = _allPointerData(packets); - expect(data, hasLength(4)); - expect(data[0].change, equals(ui.PointerChange.add)); - expect(data[0].synthesized, isTrue); - expect(data[0].device, equals(3)); - expect(data[0].physicalX, equals(500 * dpi)); - expect(data[0].physicalY, equals(501 * dpi)); - - expect(data[1].change, equals(ui.PointerChange.down)); - expect(data[1].device, equals(3)); - expect(data[1].buttons, equals(1)); - expect(data[1].physicalX, equals(500 * dpi)); - expect(data[1].physicalY, equals(501 * dpi)); - expect(data[1].physicalDeltaX, equals(0)); - expect(data[1].physicalDeltaY, equals(0)); - - expect(data[2].change, equals(ui.PointerChange.add)); - expect(data[2].synthesized, isTrue); - expect(data[2].device, equals(2)); - expect(data[2].physicalX, equals(600 * dpi)); - expect(data[2].physicalY, equals(601 * dpi)); - - expect(data[3].change, equals(ui.PointerChange.down)); - expect(data[3].device, equals(2)); - expect(data[3].buttons, equals(1)); - expect(data[3].physicalX, equals(600 * dpi)); - expect(data[3].physicalY, equals(601 * dpi)); - expect(data[3].physicalDeltaX, equals(0)); - expect(data[3].physicalDeltaY, equals(0)); - packets.clear(); - }, - ); + expect(data[1].change, equals(ui.PointerChange.down)); + expect(data[1].device, equals(3)); + expect(data[1].buttons, equals(1)); + expect(data[1].physicalX, equals(500 * dpi)); + expect(data[1].physicalY, equals(501 * dpi)); + expect(data[1].physicalDeltaX, equals(0)); + expect(data[1].physicalDeltaY, equals(0)); + + expect(data[2].change, equals(ui.PointerChange.add)); + expect(data[2].synthesized, isTrue); + expect(data[2].device, equals(2)); + expect(data[2].physicalX, equals(600 * dpi)); + expect(data[2].physicalY, equals(601 * dpi)); + + expect(data[3].change, equals(ui.PointerChange.down)); + expect(data[3].device, equals(2)); + expect(data[3].buttons, equals(1)); + expect(data[3].physicalX, equals(600 * dpi)); + expect(data[3].physicalY, equals(601 * dpi)); + expect(data[3].physicalDeltaX, equals(0)); + expect(data[3].physicalDeltaY, equals(0)); + packets.clear(); + }); test('ignores pointerId on coalesced events', () { final _MultiPointerEventMixin context = _PointerEventContext(); @@ -2617,9 +2271,11 @@ void testMain() { packets.add(packet); }; - context.multiTouchDown(const <_TouchDetails>[ - _TouchDetails(pointer: 52, clientX: 100, clientY: 101), - ]).forEach(rootElement.dispatchEvent); + context + .multiTouchDown(const <_TouchDetails>[ + _TouchDetails(pointer: 52, clientX: 100, clientY: 101), + ]) + .forEach(rootElement.dispatchEvent); expect(packets.length, 1); data = packets.single.data; @@ -2640,12 +2296,17 @@ void testMain() { packets.clear(); // Pointer move with coaleasced events - context.multiTouchMove(const <_TouchDetails>[ - _TouchDetails(pointer: 52, coalescedEvents: <_CoalescedTouchDetails>[ - _CoalescedTouchDetails(pointer: 0, clientX: 301, clientY: 302), - _CoalescedTouchDetails(pointer: 0, clientX: 401, clientY: 402), - ]), - ]).forEach(rootElement.dispatchEvent); + context + .multiTouchMove(const <_TouchDetails>[ + _TouchDetails( + pointer: 52, + coalescedEvents: <_CoalescedTouchDetails>[ + _CoalescedTouchDetails(pointer: 0, clientX: 301, clientY: 302), + _CoalescedTouchDetails(pointer: 0, clientX: 401, clientY: 402), + ], + ), + ]) + .forEach(rootElement.dispatchEvent); expect(packets.length, 1); data = packets.single.data; @@ -2668,9 +2329,9 @@ void testMain() { packets.clear(); // Pointer up - context.multiTouchUp(const <_TouchDetails>[ - _TouchDetails(pointer: 52, clientX: 401, clientY: 402), - ]).forEach(rootElement.dispatchEvent); + context + .multiTouchUp(const <_TouchDetails>[_TouchDetails(pointer: 52, clientX: 401, clientY: 402)]) + .forEach(rootElement.dispatchEvent); expect(packets, hasLength(1)); expect(packets[0].data, hasLength(2)); expect(packets[0].data[0].change, equals(ui.PointerChange.up)); @@ -2691,155 +2352,142 @@ void testMain() { packets.clear(); }); - test( - 'correctly parses cancel event', - () { - final _MultiPointerEventMixin context = _PointerEventContext(); - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; - - // Two pointers down - context.multiTouchDown(const <_TouchDetails>[ - _TouchDetails(pointer: 2, clientX: 100, clientY: 101), - _TouchDetails(pointer: 3, clientX: 200, clientY: 201), - ]).forEach(rootElement.dispatchEvent); - packets.clear(); // Down event is tested in other tests. + test('correctly parses cancel event', () { + final _MultiPointerEventMixin context = _PointerEventContext(); + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - // One pointer cancel - context.multiTouchCancel(const <_TouchDetails>[ - _TouchDetails(pointer: 3, clientX: 300, clientY: 302), - ]).forEach(rootElement.dispatchEvent); - expect(packets.length, 1); - expect(packets[0].data.length, 2); - expect(packets[0].data[0].change, equals(ui.PointerChange.cancel)); - expect(packets[0].data[0].device, equals(3)); - expect(packets[0].data[0].buttons, equals(0)); - expect(packets[0].data[0].physicalX, equals(200 * dpi)); - expect(packets[0].data[0].physicalY, equals(201 * dpi)); - expect(packets[0].data[0].physicalDeltaX, equals(0)); - expect(packets[0].data[0].physicalDeltaY, equals(0)); - - expect(packets[0].data[1].change, equals(ui.PointerChange.remove)); - expect(packets[0].data[1].device, equals(3)); - expect(packets[0].data[1].buttons, equals(0)); - expect(packets[0].data[1].physicalX, equals(200 * dpi)); - expect(packets[0].data[1].physicalY, equals(201 * dpi)); - expect(packets[0].data[1].physicalDeltaX, equals(0)); - expect(packets[0].data[1].physicalDeltaY, equals(0)); - packets.clear(); - }, - ); + // Two pointers down + context + .multiTouchDown(const <_TouchDetails>[ + _TouchDetails(pointer: 2, clientX: 100, clientY: 101), + _TouchDetails(pointer: 3, clientX: 200, clientY: 201), + ]) + .forEach(rootElement.dispatchEvent); + packets.clear(); // Down event is tested in other tests. + + // One pointer cancel + context + .multiTouchCancel(const <_TouchDetails>[ + _TouchDetails(pointer: 3, clientX: 300, clientY: 302), + ]) + .forEach(rootElement.dispatchEvent); + expect(packets.length, 1); + expect(packets[0].data.length, 2); + expect(packets[0].data[0].change, equals(ui.PointerChange.cancel)); + expect(packets[0].data[0].device, equals(3)); + expect(packets[0].data[0].buttons, equals(0)); + expect(packets[0].data[0].physicalX, equals(200 * dpi)); + expect(packets[0].data[0].physicalY, equals(201 * dpi)); + expect(packets[0].data[0].physicalDeltaX, equals(0)); + expect(packets[0].data[0].physicalDeltaY, equals(0)); - test( - 'does not synthesize pointer up if from different device', - () { - final _PointerEventContext context = _PointerEventContext(); - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + expect(packets[0].data[1].change, equals(ui.PointerChange.remove)); + expect(packets[0].data[1].device, equals(3)); + expect(packets[0].data[1].buttons, equals(0)); + expect(packets[0].data[1].physicalX, equals(200 * dpi)); + expect(packets[0].data[1].physicalY, equals(201 * dpi)); + expect(packets[0].data[1].physicalDeltaX, equals(0)); + expect(packets[0].data[1].physicalDeltaY, equals(0)); + packets.clear(); + }); - context.multiTouchDown(const <_TouchDetails>[ - _TouchDetails(pointer: 1, clientX: 100, clientY: 101), - ]).forEach(rootElement.dispatchEvent); - expect(packets, hasLength(1)); - // An add will be synthesized. - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, isTrue); - expect(packets[0].data[0].device, equals(1)); - expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].device, equals(1)); - packets.clear(); + test('does not synthesize pointer up if from different device', () { + final _PointerEventContext context = _PointerEventContext(); + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - context.multiTouchDown(const <_TouchDetails>[ - _TouchDetails(pointer: 2, clientX: 200, clientY: 202), - ]).forEach(rootElement.dispatchEvent); - // An add will be synthesized. - expect(packets, hasLength(1)); - expect(packets[0].data, hasLength(2)); - expect(packets[0].data[0].change, equals(ui.PointerChange.add)); - expect(packets[0].data[0].synthesized, isTrue); - expect(packets[0].data[0].device, equals(2)); - expect(packets[0].data[1].change, equals(ui.PointerChange.down)); - expect(packets[0].data[1].device, equals(2)); - packets.clear(); - }, - ); + context + .multiTouchDown(const <_TouchDetails>[ + _TouchDetails(pointer: 1, clientX: 100, clientY: 101), + ]) + .forEach(rootElement.dispatchEvent); + expect(packets, hasLength(1)); + // An add will be synthesized. + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[0].device, equals(1)); + expect(packets[0].data[1].change, equals(ui.PointerChange.down)); + expect(packets[0].data[1].device, equals(1)); + packets.clear(); - test( - 'ignores pointer up or pointer cancel events for unknown device', - () { - final _PointerEventContext context = _PointerEventContext(); - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + context + .multiTouchDown(const <_TouchDetails>[ + _TouchDetails(pointer: 2, clientX: 200, clientY: 202), + ]) + .forEach(rootElement.dispatchEvent); + // An add will be synthesized. + expect(packets, hasLength(1)); + expect(packets[0].data, hasLength(2)); + expect(packets[0].data[0].change, equals(ui.PointerChange.add)); + expect(packets[0].data[0].synthesized, isTrue); + expect(packets[0].data[0].device, equals(2)); + expect(packets[0].data[1].change, equals(ui.PointerChange.down)); + expect(packets[0].data[1].device, equals(2)); + packets.clear(); + }); - context.multiTouchUp(const <_TouchDetails>[ - _TouchDetails(pointer: 23, clientX: 200, clientY: 202), - ]).forEach(rootElement.dispatchEvent); - expect(packets, hasLength(0)); + test('ignores pointer up or pointer cancel events for unknown device', () { + final _PointerEventContext context = _PointerEventContext(); + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - context.multiTouchCancel(const <_TouchDetails>[ - _TouchDetails(pointer: 24, clientX: 200, clientY: 202), - ]).forEach(rootElement.dispatchEvent); - expect(packets, hasLength(0)); - }, - ); + context + .multiTouchUp(const <_TouchDetails>[_TouchDetails(pointer: 23, clientX: 200, clientY: 202)]) + .forEach(rootElement.dispatchEvent); + expect(packets, hasLength(0)); + + context + .multiTouchCancel(const <_TouchDetails>[ + _TouchDetails(pointer: 24, clientX: 200, clientY: 202), + ]) + .forEach(rootElement.dispatchEvent); + expect(packets, hasLength(0)); + }); - test( - 'handles random pointer id on up events', - () { - final _PointerEventContext context = _PointerEventContext(); - // This happens with pens that are simulated with mouse events - // (e.g. Wacom). It sends events with the pointer type "mouse", and - // assigns a random pointer ID to each event. - // - // For more info, see: https://github.com/flutter/flutter/issues/75559 + test('handles random pointer id on up events', () { + final _PointerEventContext context = _PointerEventContext(); + // This happens with pens that are simulated with mouse events + // (e.g. Wacom). It sends events with the pointer type "mouse", and + // assigns a random pointer ID to each event. + // + // For more info, see: https://github.com/flutter/flutter/issues/75559 - final List packets = []; - ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { - packets.add(packet); - }; + final List packets = []; + ui.PlatformDispatcher.instance.onPointerDataPacket = (ui.PointerDataPacket packet) { + packets.add(packet); + }; - rootElement.dispatchEvent(context.mouseDown( - pointerId: 12, - button: 0, - buttons: 1, - clientX: 10.0, - clientY: 10.0, - )); + rootElement.dispatchEvent( + context.mouseDown(pointerId: 12, button: 0, buttons: 1, clientX: 10.0, clientY: 10.0), + ); - expect(packets, hasLength(1)); - expect(packets.single.data, hasLength(2)); + expect(packets, hasLength(1)); + expect(packets.single.data, hasLength(2)); - expect(packets.single.data[0].change, equals(ui.PointerChange.add)); - expect(packets.single.data[0].synthesized, isTrue); - expect(packets.single.data[1].change, equals(ui.PointerChange.down)); - packets.clear(); + expect(packets.single.data[0].change, equals(ui.PointerChange.add)); + expect(packets.single.data[0].synthesized, isTrue); + expect(packets.single.data[1].change, equals(ui.PointerChange.down)); + packets.clear(); - expect( - () { - rootElement.dispatchEvent(context.mouseUp( - pointerId: 41, - button: 0, - buttons: 0, - clientX: 10.0, - clientY: 10.0, - )); - }, - returnsNormally, + expect(() { + rootElement.dispatchEvent( + context.mouseUp(pointerId: 41, button: 0, buttons: 0, clientX: 10.0, clientY: 10.0), ); + }, returnsNormally); - expect(packets, hasLength(1)); - expect(packets.single.data, hasLength(1)); + expect(packets, hasLength(1)); + expect(packets.single.data, hasLength(1)); - expect(packets.single.data[0].change, equals(ui.PointerChange.up)); - }, - ); + expect(packets.single.data[0].change, equals(ui.PointerChange.up)); + }); test('throws if browser does not support pointer events', () { expect( @@ -2907,10 +2555,7 @@ void testMain() { }); } -typedef CapturedSemanticsEvent = ({ - ui.SemanticsAction type, - int nodeId, -}); +typedef CapturedSemanticsEvent = ({ui.SemanticsAction type, int nodeId}); void _testClickDebouncer({required PointerBinding Function() getBinding}) { final DateTime testTime = DateTime(2018, 12, 17); @@ -2958,10 +2603,7 @@ void _testClickDebouncer({required PointerBinding Function() getBinding}) { expect(EnginePlatformDispatcher.instance.semanticsEnabled, false); expect(PointerBinding.clickDebouncer.isDebouncing, false); binding.rootElement.dispatchEvent(context.primaryDown()); - expect(pointerPackets, [ - ui.PointerChange.add, - ui.PointerChange.down, - ]); + expect(pointerPackets, [ui.PointerChange.add, ui.PointerChange.down]); expect(PointerBinding.clickDebouncer.isDebouncing, false); expect(semanticsActions, isEmpty); }); @@ -3015,14 +2657,8 @@ void _testClickDebouncer({required PointerBinding Function() getBinding}) { pointerPackets, [], ); - expect( - PointerBinding.clickDebouncer.debugState!.target, - testElement, - ); - expect( - PointerBinding.clickDebouncer.debugState!.timer.isActive, - isTrue, - ); + expect(PointerBinding.clickDebouncer.debugState!.target, testElement); + expect(PointerBinding.clickDebouncer.debugState!.timer.isActive, isTrue); expect( PointerBinding.clickDebouncer.debugState!.queue.map((QueuedEvent e) => e.event.type), ['pointerdown', 'pointerup'], @@ -3037,11 +2673,7 @@ void _testClickDebouncer({required PointerBinding Function() getBinding}) { expect( reason: 'Queued up events should be flushed to the framework.', pointerPackets, - [ - ui.PointerChange.add, - ui.PointerChange.down, - ui.PointerChange.up, - ], + [ui.PointerChange.add, ui.PointerChange.down, ui.PointerChange.up], ); expect(semanticsActions, isEmpty); }); @@ -3079,11 +2711,7 @@ void _testClickDebouncer({required PointerBinding Function() getBinding}) { expect( reason: 'Queued up events should be flushed to the framework.', pointerPackets, - [ - ui.PointerChange.add, - ui.PointerChange.down, - ui.PointerChange.up, - ], + [ui.PointerChange.add, ui.PointerChange.down, ui.PointerChange.up], ); expect(semanticsActions, isEmpty); }); @@ -3095,20 +2723,15 @@ void _testClickDebouncer({required PointerBinding Function() getBinding}) { testElement.setAttribute('flt-tappable', ''); view.dom.semanticsHost.appendChild(testElement); - final DomEvent click = createDomMouseEvent( - 'click', - { - 'clientX': testElement.getBoundingClientRect().x, - 'clientY': testElement.getBoundingClientRect().y, - } - ); + final DomEvent click = createDomMouseEvent('click', { + 'clientX': testElement.getBoundingClientRect().x, + 'clientY': testElement.getBoundingClientRect().y, + }); PointerBinding.clickDebouncer.onClick(click, view.viewId, 42, true); expect(PointerBinding.clickDebouncer.isDebouncing, false); expect(pointerPackets, isEmpty); - expect(semanticsActions, [ - (type: ui.SemanticsAction.tap, nodeId: 42) - ]); + expect(semanticsActions, [(type: ui.SemanticsAction.tap, nodeId: 42)]); }); testWithSemantics('Forwards click to framework when debouncing and listening', () async { @@ -3120,19 +2743,14 @@ void _testClickDebouncer({required PointerBinding Function() getBinding}) { testElement.dispatchEvent(context.primaryDown()); expect(PointerBinding.clickDebouncer.isDebouncing, true); - final DomEvent click = createDomMouseEvent( - 'click', - { - 'clientX': testElement.getBoundingClientRect().x, - 'clientY': testElement.getBoundingClientRect().y, - } - ); + final DomEvent click = createDomMouseEvent('click', { + 'clientX': testElement.getBoundingClientRect().x, + 'clientY': testElement.getBoundingClientRect().y, + }); PointerBinding.clickDebouncer.onClick(click, view.viewId, 42, true); expect(pointerPackets, isEmpty); - expect(semanticsActions, [ - (type: ui.SemanticsAction.tap, nodeId: 42) - ]); + expect(semanticsActions, [(type: ui.SemanticsAction.tap, nodeId: 42)]); }); testWithSemantics('Dedupes click if debouncing but not listening', () async { @@ -3144,82 +2762,68 @@ void _testClickDebouncer({required PointerBinding Function() getBinding}) { testElement.dispatchEvent(context.primaryDown()); expect(PointerBinding.clickDebouncer.isDebouncing, true); - final DomEvent click = createDomMouseEvent( - 'click', - { - 'clientX': testElement.getBoundingClientRect().x, - 'clientY': testElement.getBoundingClientRect().y, - } - ); + final DomEvent click = createDomMouseEvent('click', { + 'clientX': testElement.getBoundingClientRect().x, + 'clientY': testElement.getBoundingClientRect().y, + }); PointerBinding.clickDebouncer.onClick(click, view.viewId, 42, false); expect( - reason: 'When tappable declares that it is not listening to click events ' - 'the debouncer flushes the pointer events to the framework and ' - 'lets it sort it out.', + reason: + 'When tappable declares that it is not listening to click events ' + 'the debouncer flushes the pointer events to the framework and ' + 'lets it sort it out.', pointerPackets, - [ - ui.PointerChange.add, - ui.PointerChange.down, - ], + [ui.PointerChange.add, ui.PointerChange.down], ); expect(semanticsActions, isEmpty); }); - testWithSemantics('Dedupes click if pointer down/up flushed recently', () async { - expect(EnginePlatformDispatcher.instance.semanticsEnabled, true); - expect(PointerBinding.clickDebouncer.isDebouncing, false); + testWithSemantics( + 'Dedupes click if pointer down/up flushed recently', + () async { + expect(EnginePlatformDispatcher.instance.semanticsEnabled, true); + expect(PointerBinding.clickDebouncer.isDebouncing, false); - final DomElement testElement = createDomElement('flt-semantics'); - testElement.setAttribute('flt-tappable', ''); - view.dom.semanticsHost.appendChild(testElement); + final DomElement testElement = createDomElement('flt-semantics'); + testElement.setAttribute('flt-tappable', ''); + view.dom.semanticsHost.appendChild(testElement); - testElement.dispatchEvent(context.primaryDown()); + testElement.dispatchEvent(context.primaryDown()); - // Simulate the user holding the pointer down for some time before releasing, - // such that the pointerup event happens close to timer expiration. This - // will create the situation that the click event arrives just after the - // pointerup is flushed. Forwarding the click to the framework would look - // like a double-click, so the click event is deduped. - await Future.delayed(const Duration(milliseconds: 190)); + // Simulate the user holding the pointer down for some time before releasing, + // such that the pointerup event happens close to timer expiration. This + // will create the situation that the click event arrives just after the + // pointerup is flushed. Forwarding the click to the framework would look + // like a double-click, so the click event is deduped. + await Future.delayed(const Duration(milliseconds: 190)); - testElement.dispatchEvent(context.primaryUp()); - expect(PointerBinding.clickDebouncer.isDebouncing, true); - expect( - reason: 'Timer has not expired yet', - pointerPackets, isEmpty, - ); + testElement.dispatchEvent(context.primaryUp()); + expect(PointerBinding.clickDebouncer.isDebouncing, true); + expect(reason: 'Timer has not expired yet', pointerPackets, isEmpty); - // Wait for the timer to expire to make sure pointer events are flushed. - await Future.delayed(const Duration(milliseconds: 20)); + // Wait for the timer to expire to make sure pointer events are flushed. + await Future.delayed(const Duration(milliseconds: 20)); - expect( - reason: 'Queued up events should be flushed to the framework because the ' - 'time expired before the click event arrived.', - pointerPackets, - [ - ui.PointerChange.add, - ui.PointerChange.down, - ui.PointerChange.up, - ], - ); + expect( + reason: + 'Queued up events should be flushed to the framework because the ' + 'time expired before the click event arrived.', + pointerPackets, + [ui.PointerChange.add, ui.PointerChange.down, ui.PointerChange.up], + ); - final DomEvent click = createDomMouseEvent( - 'click', - { + final DomEvent click = createDomMouseEvent('click', { 'clientX': testElement.getBoundingClientRect().x, 'clientY': testElement.getBoundingClientRect().y, - } - ); - PointerBinding.clickDebouncer.onClick(click, view.viewId, 42, true); + }); + PointerBinding.clickDebouncer.onClick(click, view.viewId, 42, true); - expect( - reason: 'Because the DOM click event was deduped.', - semanticsActions, - isEmpty, - ); - // TODO(yjbanov): https://github.com/flutter/flutter/issues/142991. - }, skip: ui_web.browser.operatingSystem == ui_web.OperatingSystem.windows); + expect(reason: 'Because the DOM click event was deduped.', semanticsActions, isEmpty); + // TODO(yjbanov): https://github.com/flutter/flutter/issues/142991. + }, + skip: ui_web.browser.operatingSystem == ui_web.OperatingSystem.windows, + ); // Regression test for https://github.com/flutter/flutter/issues/147050 // @@ -3231,114 +2835,104 @@ void _testClickDebouncer({required PointerBinding Function() getBinding}) { // followed by a "click". Since we sent the "pointerdown" and "pointerup" to // the framework already, the framework registered a tap. Forwarding the // "click" would lead to a double-tap. This was the bug. - testWithSemantics('Dedupes click if pointer up happened recently without debouncing', () async { - expect(EnginePlatformDispatcher.instance.semanticsEnabled, true); - expect(PointerBinding.clickDebouncer.isDebouncing, false); + testWithSemantics( + 'Dedupes click if pointer up happened recently without debouncing', + () async { + expect(EnginePlatformDispatcher.instance.semanticsEnabled, true); + expect(PointerBinding.clickDebouncer.isDebouncing, false); - final DomElement testElement = createDomElement('flt-semantics'); - testElement.setAttribute('flt-tappable', ''); - view.dom.semanticsHost.appendChild(testElement); + final DomElement testElement = createDomElement('flt-semantics'); + testElement.setAttribute('flt-tappable', ''); + view.dom.semanticsHost.appendChild(testElement); - // Begin a long-press with a "pointerdown". - testElement.dispatchEvent(context.primaryDown()); + // Begin a long-press with a "pointerdown". + testElement.dispatchEvent(context.primaryDown()); - // Expire the timer causing the debouncer to reset itself. - await Future.delayed(const Duration(milliseconds: 250)); - expect( - reason: '"pointerdown" should be flushed when the timer expires.', - pointerPackets, - [ui.PointerChange.add, ui.PointerChange.down], - ); - pointerPackets.clear(); + // Expire the timer causing the debouncer to reset itself. + await Future.delayed(const Duration(milliseconds: 250)); + expect( + reason: '"pointerdown" should be flushed when the timer expires.', + pointerPackets, + [ui.PointerChange.add, ui.PointerChange.down], + ); + pointerPackets.clear(); - // Send a "pointerup" while the debouncer is not debouncing anything. - testElement.dispatchEvent(context.primaryUp()); + // Send a "pointerup" while the debouncer is not debouncing anything. + testElement.dispatchEvent(context.primaryUp()); - // A standalone "pointerup" should not start debouncing anything. - expect(PointerBinding.clickDebouncer.isDebouncing, isFalse); - expect( - reason: 'The "pointerup" should be forwarded to the framework immediately', - pointerPackets, - [ui.PointerChange.up], - ); + // A standalone "pointerup" should not start debouncing anything. + expect(PointerBinding.clickDebouncer.isDebouncing, isFalse); + expect( + reason: 'The "pointerup" should be forwarded to the framework immediately', + pointerPackets, + [ui.PointerChange.up], + ); - // Use a delay that's short enough for the click to be deduped. - await Future.delayed(const Duration(milliseconds: 10)); + // Use a delay that's short enough for the click to be deduped. + await Future.delayed(const Duration(milliseconds: 10)); - final DomEvent click = createDomMouseEvent( - 'click', - { + final DomEvent click = createDomMouseEvent('click', { 'clientX': testElement.getBoundingClientRect().x, 'clientY': testElement.getBoundingClientRect().y, - } - ); - PointerBinding.clickDebouncer.onClick(click, view.viewId, 42, true); + }); + PointerBinding.clickDebouncer.onClick(click, view.viewId, 42, true); - expect( - reason: 'Because the DOM click event was deduped.', - semanticsActions, - isEmpty, - ); - // TODO(yjbanov): https://github.com/flutter/flutter/issues/142991. - }, skip: ui_web.browser.operatingSystem == ui_web.OperatingSystem.windows); + expect(reason: 'Because the DOM click event was deduped.', semanticsActions, isEmpty); + // TODO(yjbanov): https://github.com/flutter/flutter/issues/142991. + }, + skip: ui_web.browser.operatingSystem == ui_web.OperatingSystem.windows, + ); - testWithSemantics('Forwards click if enough time passed after the last flushed pointerup', () async { - expect(EnginePlatformDispatcher.instance.semanticsEnabled, true); - expect(PointerBinding.clickDebouncer.isDebouncing, false); + testWithSemantics( + 'Forwards click if enough time passed after the last flushed pointerup', + () async { + expect(EnginePlatformDispatcher.instance.semanticsEnabled, true); + expect(PointerBinding.clickDebouncer.isDebouncing, false); - final DomElement testElement = createDomElement('flt-semantics'); - testElement.setAttribute('flt-tappable', ''); - view.dom.semanticsHost.appendChild(testElement); + final DomElement testElement = createDomElement('flt-semantics'); + testElement.setAttribute('flt-tappable', ''); + view.dom.semanticsHost.appendChild(testElement); - testElement.dispatchEvent(context.primaryDown()); + testElement.dispatchEvent(context.primaryDown()); - // Simulate the user holding the pointer down for some time before releasing, - // such that the pointerup event happens close to timer expiration. This - // makes it possible for the click to arrive early. However, this test in - // particular will delay the click to check that the delay is checked - // correctly. The inverse situation was already tested in the previous test. - await Future.delayed(const Duration(milliseconds: 190)); + // Simulate the user holding the pointer down for some time before releasing, + // such that the pointerup event happens close to timer expiration. This + // makes it possible for the click to arrive early. However, this test in + // particular will delay the click to check that the delay is checked + // correctly. The inverse situation was already tested in the previous test. + await Future.delayed(const Duration(milliseconds: 190)); - testElement.dispatchEvent(context.primaryUp()); - expect(PointerBinding.clickDebouncer.isDebouncing, true); - expect( - reason: 'Timer has not expired yet', - pointerPackets, isEmpty, - ); + testElement.dispatchEvent(context.primaryUp()); + expect(PointerBinding.clickDebouncer.isDebouncing, true); + expect(reason: 'Timer has not expired yet', pointerPackets, isEmpty); - // Wait for the timer to expire to make sure pointer events are flushed. - await Future.delayed(const Duration(milliseconds: 100)); + // Wait for the timer to expire to make sure pointer events are flushed. + await Future.delayed(const Duration(milliseconds: 100)); - expect( - reason: 'Queued up events should be flushed to the framework because the ' - 'time expired before the click event arrived.', - pointerPackets, - [ - ui.PointerChange.add, - ui.PointerChange.down, - ui.PointerChange.up, - ], - ); + expect( + reason: + 'Queued up events should be flushed to the framework because the ' + 'time expired before the click event arrived.', + pointerPackets, + [ui.PointerChange.add, ui.PointerChange.down, ui.PointerChange.up], + ); - final DomEvent click = createDomMouseEvent( - 'click', - { + final DomEvent click = createDomMouseEvent('click', { 'clientX': testElement.getBoundingClientRect().x, 'clientY': testElement.getBoundingClientRect().y, - } - ); - PointerBinding.clickDebouncer.onClick(click, view.viewId, 42, true); + }); + PointerBinding.clickDebouncer.onClick(click, view.viewId, 42, true); - expect( - reason: 'The DOM click should still be sent to the framework because it ' - 'happened far enough from the last pointerup that it is unlikely ' - 'to be a duplicate.', - semanticsActions, - [ - (type: ui.SemanticsAction.tap, nodeId: 42) - ], - ); - }); + expect( + reason: + 'The DOM click should still be sent to the framework because it ' + 'happened far enough from the last pointerup that it is unlikely ' + 'to be a duplicate.', + semanticsActions, + [(type: ui.SemanticsAction.tap, nodeId: 42)], + ); + }, + ); } class MockSafariPointerEventWorkaround implements SafariPointerEventWorkaround { @@ -3400,18 +2994,13 @@ abstract class _BasicEventContext { mixin _ButtonedEventMixin on _BasicEventContext { // Generate an event that is a mouse down with the specific buttons. - DomEvent mouseDown( - {double? clientX, double? clientY, int? button, int? buttons}); + DomEvent mouseDown({double? clientX, double? clientY, int? button, int? buttons}); // Generate an event that is a mouse drag with the specific buttons, or button // changes during the drag. // // If there is no button change, assign `button` with _kNoButtonChange. - DomEvent mouseMove( - {double? clientX, - double? clientY, - required int button, - required int buttons}); + DomEvent mouseMove({double? clientX, double? clientY, required int button, required int buttons}); // Generate an event that moves the mouse outside of the tracked area. DomEvent mouseLeave({double? clientX, double? clientY, required int buttons}); @@ -3420,41 +3009,22 @@ mixin _ButtonedEventMixin on _BasicEventContext { DomEvent mouseUp({double? clientX, double? clientY, int? button, int? buttons}); DomEvent hover({double? clientX, double? clientY}) { - return mouseMove( - buttons: 0, - button: _kNoButtonChange, - clientX: clientX, - clientY: clientY, - ); + return mouseMove(buttons: 0, button: _kNoButtonChange, clientX: clientX, clientY: clientY); } @override DomEvent primaryDown({double? clientX, double? clientY}) { - return mouseDown( - buttons: 1, - button: 0, - clientX: clientX, - clientY: clientY, - ); + return mouseDown(buttons: 1, button: 0, clientX: clientX, clientY: clientY); } @override DomEvent primaryMove({double? clientX, double? clientY}) { - return mouseMove( - buttons: 1, - button: _kNoButtonChange, - clientX: clientX, - clientY: clientY, - ); + return mouseMove(buttons: 1, button: _kNoButtonChange, clientX: clientX, clientY: clientY); } @override DomEvent primaryUp({double? clientX, double? clientY}) { - return mouseUp( - button: 0, - clientX: clientX, - clientY: clientY, - ); + return mouseUp(button: 0, clientX: clientX, clientY: clientY); } DomEvent wheel({ @@ -3469,44 +3039,32 @@ mixin _ButtonedEventMixin on _BasicEventContext { bool ctrlKey = false, }) { final DomEvent event = createDomWheelEvent('wheel', { - if (buttons != null) 'buttons': buttons, - if (clientX != null) 'clientX': clientX, - if (clientY != null) 'clientY': clientY, - if (deltaX != null) 'deltaX': deltaX, - if (deltaY != null) 'deltaY': deltaY, - if (wheelDeltaX != null) 'wheelDeltaX': wheelDeltaX, - if (wheelDeltaY != null) 'wheelDeltaY': wheelDeltaY, - 'ctrlKey': ctrlKey, - 'cancelable': true, - 'bubbles': true, - 'composed': true, + if (buttons != null) 'buttons': buttons, + if (clientX != null) 'clientX': clientX, + if (clientY != null) 'clientY': clientY, + if (deltaX != null) 'deltaX': deltaX, + if (deltaY != null) 'deltaY': deltaY, + if (wheelDeltaX != null) 'wheelDeltaX': wheelDeltaX, + if (wheelDeltaY != null) 'wheelDeltaY': wheelDeltaY, + 'ctrlKey': ctrlKey, + 'cancelable': true, + 'bubbles': true, + 'composed': true, }); // timeStamp can't be set in the constructor, need to override the getter. if (timeStamp != null) { - js_util.callMethod( - objectConstructor, - 'defineProperty', - [ - event, - 'timeStamp', - js_util.jsify({ - 'value': timeStamp, - 'configurable': true - }) - ] - ); + js_util.callMethod(objectConstructor, 'defineProperty', [ + event, + 'timeStamp', + js_util.jsify({'value': timeStamp, 'configurable': true}), + ]); } return event; } } class _TouchDetails { - const _TouchDetails({ - this.pointer, - this.clientX, - this.clientY, - this.coalescedEvents, - }); + const _TouchDetails({this.pointer, this.clientX, this.clientY, this.coalescedEvents}); final int? pointer; final double? clientX; @@ -3516,11 +3074,7 @@ class _TouchDetails { } class _CoalescedTouchDetails { - const _CoalescedTouchDetails({ - this.pointer, - this.clientX, - this.clientY, - }); + const _CoalescedTouchDetails({this.pointer, this.clientX, this.clientY}); final int? pointer; final double? clientX; @@ -3536,33 +3090,21 @@ mixin _MultiPointerEventMixin on _BasicEventContext { @override DomEvent primaryDown({double? clientX, double? clientY}) { return multiTouchDown(<_TouchDetails>[ - _TouchDetails( - pointer: 1, - clientX: clientX, - clientY: clientY, - ), + _TouchDetails(pointer: 1, clientX: clientX, clientY: clientY), ])[0]; } @override DomEvent primaryMove({double? clientX, double? clientY}) { return multiTouchMove(<_TouchDetails>[ - _TouchDetails( - pointer: 1, - clientX: clientX, - clientY: clientY, - ), + _TouchDetails(pointer: 1, clientX: clientX, clientY: clientY), ])[0]; } @override DomEvent primaryUp({double? clientX, double? clientY}) { return multiTouchUp(<_TouchDetails>[ - _TouchDetails( - pointer: 1, - clientX: clientX, - clientY: clientY, - ), + _TouchDetails(pointer: 1, clientX: clientX, clientY: clientY), ])[0]; } } @@ -3584,14 +3126,16 @@ class _PointerEventContext extends _BasicEventContext 'Coalesced events are not allowed for pointerdown events.', ); return touches - .map((_TouchDetails details) => _downWithFullDetails( - pointer: details.pointer, - buttons: 1, - button: 0, - clientX: details.clientX, - clientY: details.clientY, - pointerType: 'touch', - )) + .map( + (_TouchDetails details) => _downWithFullDetails( + pointer: details.pointer, + buttons: 1, + button: 0, + clientX: details.clientX, + clientY: details.clientY, + pointerType: 'touch', + ), + ) .toList(); } @@ -3639,15 +3183,17 @@ class _PointerEventContext extends _BasicEventContext @override List multiTouchMove(List<_TouchDetails> touches) { return touches - .map((_TouchDetails details) => _moveWithFullDetails( - pointer: details.pointer, - buttons: 1, - button: _kNoButtonChange, - clientX: details.clientX, - clientY: details.clientY, - pointerType: 'touch', - coalescedEvents: details.coalescedEvents, - )) + .map( + (_TouchDetails details) => _moveWithFullDetails( + pointer: details.pointer, + buttons: 1, + button: _kNoButtonChange, + clientX: details.clientX, + clientY: details.clientY, + pointerType: 'touch', + coalescedEvents: details.coalescedEvents, + ), + ) .toList(); } @@ -3691,31 +3237,34 @@ class _PointerEventContext extends _BasicEventContext if (coalescedEvents != null) { // There's no JS API for setting coalesced events, so we need to // monkey-patch the `getCoalescedEvents` method to return what we want. - final coalescedEventJs = coalescedEvents - .map((_CoalescedTouchDetails details) => _moveWithFullDetails( - pointer: details.pointer, - button: button, - buttons: buttons, - clientX: details.clientX, - clientY: details.clientY, - pointerType: 'touch', - )).toJSAnyDeep; - - js_util.setProperty(event, 'getCoalescedEvents', js_util.allowInterop(() { - return coalescedEventJs; - })); + final coalescedEventJs = + coalescedEvents + .map( + (_CoalescedTouchDetails details) => _moveWithFullDetails( + pointer: details.pointer, + button: button, + buttons: buttons, + clientX: details.clientX, + clientY: details.clientY, + pointerType: 'touch', + ), + ) + .toJSAnyDeep; + + js_util.setProperty( + event, + 'getCoalescedEvents', + js_util.allowInterop(() { + return coalescedEventJs; + }), + ); } return event; } @override - DomEvent mouseLeave({ - double? clientX, - double? clientY, - required int buttons, - int pointerId = 1, - }) { + DomEvent mouseLeave({double? clientX, double? clientY, required int buttons, int pointerId = 1}) { return _leaveWithFullDetails( pointer: pointerId, buttons: buttons, @@ -3752,13 +3301,15 @@ class _PointerEventContext extends _BasicEventContext 'Coalesced events are not allowed for pointerup events.', ); return touches - .map((_TouchDetails details) => _upWithFullDetails( - pointer: details.pointer, - button: 0, - clientX: details.clientX, - clientY: details.clientY, - pointerType: 'touch', - )) + .map( + (_TouchDetails details) => _upWithFullDetails( + pointer: details.pointer, + button: 0, + clientX: details.clientX, + clientY: details.clientY, + pointerType: 'touch', + ), + ) .toList(); } @@ -3806,16 +3357,17 @@ class _PointerEventContext extends _BasicEventContext 'Coalesced events are not allowed for pointercancel events.', ); return touches - .map((_TouchDetails details) => - createDomPointerEvent('pointercancel', { - 'bubbles': true, - 'pointerId': details.pointer, - 'button': 0, - 'buttons': 0, - 'clientX': 0, - 'clientY': 0, - 'pointerType': 'touch', - })) + .map( + (_TouchDetails details) => createDomPointerEvent('pointercancel', { + 'bubbles': true, + 'pointerId': details.pointer, + 'button': 0, + 'buttons': 0, + 'clientX': 0, + 'clientY': 0, + 'pointerType': 'touch', + }), + ) .toList(); } @@ -3837,12 +3389,7 @@ class _PointerEventContext extends _BasicEventContext ); } - DomEvent stylusTouchUp({ - double? clientX, - double? clientY, - int? buttons, - int? pointerId = 1000, - }) { + DomEvent stylusTouchUp({double? clientX, double? clientY, int? buttons, int? pointerId = 1000}) { return _upWithFullDetails( pointer: pointerId, buttons: buttons, diff --git a/lib/web_ui/test/engine/profiler_test.dart b/lib/web_ui/test/engine/profiler_test.dart index 5f113891eaa9f..ff49e31dc1fe7 100644 --- a/lib/web_ui/test/engine/profiler_test.dart +++ b/lib/web_ui/test/engine/profiler_test.dart @@ -85,9 +85,10 @@ void _profilerTests() { group('[JS API]', () { test('can listen to benchmarks', () { final List data = []; - jsBenchmarkValueCallback = (String name, double value) { - data.add((name, value)); - }.toJS; + jsBenchmarkValueCallback = + (String name, double value) { + data.add((name, value)); + }.toJS; Profiler.instance.benchmark('foo', 123); expect(warnings, hasLength(1)); @@ -114,9 +115,10 @@ void _profilerTests() { final List data = []; // Wrong callback signature. - jsBenchmarkValueCallback = (double value) { - data.add(('bad', value)); - }.toJS; + jsBenchmarkValueCallback = + (double value) { + data.add(('bad', value)); + }.toJS; expect( () => Profiler.instance.benchmark('foo', 123), @@ -143,9 +145,10 @@ void _profilerTests() { ui_web.benchmarkValueCallback = (String name, double value) { uiWebData.add((name, value)); }; - jsBenchmarkValueCallback = (String name, double value) { - jsData.add((name, value)); - }.toJS; + jsBenchmarkValueCallback = + (String name, double value) { + jsData.add((name, value)); + }.toJS; Profiler.instance.benchmark('foo', 123); expect(warnings, hasLength(1)); diff --git a/lib/web_ui/test/engine/raw_keyboard_test.dart b/lib/web_ui/test/engine/raw_keyboard_test.dart index 916735a2a38a3..42b1c4833143e 100644 --- a/lib/web_ui/test/engine/raw_keyboard_test.dart +++ b/lib/web_ui/test/engine/raw_keyboard_test.dart @@ -41,8 +41,11 @@ void testMain() { String? channelReceived; Map? dataReceived; - ui.PlatformDispatcher.instance.onPlatformMessage = (String channel, ByteData? data, - ui.PlatformMessageResponseCallback? callback) { + ui.PlatformDispatcher.instance.onPlatformMessage = ( + String channel, + ByteData? data, + ui.PlatformMessageResponseCallback? callback, + ) { channelReceived = channel; dataReceived = const JSONMessageCodec().decodeMessage(data) as Map?; }; @@ -75,16 +78,18 @@ void testMain() { String? channelReceived; Map? dataReceived; - ui.PlatformDispatcher.instance.onPlatformMessage = (String channel, ByteData? data, - ui.PlatformMessageResponseCallback? callback) { + ui.PlatformDispatcher.instance.onPlatformMessage = ( + String channel, + ByteData? data, + ui.PlatformMessageResponseCallback? callback, + ) { channelReceived = channel; dataReceived = const JSONMessageCodec().decodeMessage(data) as Map?; }; DomKeyboardEvent event; - event = - dispatchKeyboardEvent('keydown', key: 'SomeKey', code: 'SomeCode', keyCode: 1); + event = dispatchKeyboardEvent('keydown', key: 'SomeKey', code: 'SomeCode', keyCode: 1); expect(channelReceived, 'flutter/keyevent'); expect(dataReceived, { @@ -105,8 +110,11 @@ void testMain() { RawKeyboard.initialize(); Map? dataReceived; - ui.PlatformDispatcher.instance.onPlatformMessage = (String channel, ByteData? data, - ui.PlatformMessageResponseCallback? callback) { + ui.PlatformDispatcher.instance.onPlatformMessage = ( + String channel, + ByteData? data, + ui.PlatformMessageResponseCallback? callback, + ) { dataReceived = const JSONMessageCodec().decodeMessage(data) as Map?; }; @@ -154,41 +162,51 @@ void testMain() { }); // Regression test for https://github.com/flutter/flutter/issues/125672. - test('updates meta state for Meta key and wrong DOM event metaKey value (Linux)', () { - RawKeyboard.initialize(); + test( + 'updates meta state for Meta key and wrong DOM event metaKey value (Linux)', + () { + RawKeyboard.initialize(); - Map? dataReceived; - ui.PlatformDispatcher.instance.onPlatformMessage = (String channel, ByteData? data, - ui.PlatformMessageResponseCallback? callback) { - dataReceived = const JSONMessageCodec().decodeMessage(data) as Map?; - }; + Map? dataReceived; + ui.PlatformDispatcher.instance.onPlatformMessage = ( + String channel, + ByteData? data, + ui.PlatformMessageResponseCallback? callback, + ) { + dataReceived = const JSONMessageCodec().decodeMessage(data) as Map?; + }; - // Purposely send an incoherent DOM event where Meta key is pressed but event.metaKey is not set to true. - final DomKeyboardEvent event = dispatchKeyboardEvent( - 'keydown', - key: 'Meta', - code: 'MetaLeft', - ); - expect(event.defaultPrevented, isFalse); - expect(dataReceived, { - 'type': 'keydown', - 'keymap': 'web', - 'code': 'MetaLeft', - 'key': 'Meta', - 'location': 0, - 'metaState': 0x8, - 'keyCode': 0, - }); - RawKeyboard.instance!.dispose(); - }, skip: ui_web.browser.operatingSystem != ui_web.OperatingSystem.linux); + // Purposely send an incoherent DOM event where Meta key is pressed but event.metaKey is not set to true. + final DomKeyboardEvent event = dispatchKeyboardEvent( + 'keydown', + key: 'Meta', + code: 'MetaLeft', + ); + expect(event.defaultPrevented, isFalse); + expect(dataReceived, { + 'type': 'keydown', + 'keymap': 'web', + 'code': 'MetaLeft', + 'key': 'Meta', + 'location': 0, + 'metaState': 0x8, + 'keyCode': 0, + }); + RawKeyboard.instance!.dispose(); + }, + skip: ui_web.browser.operatingSystem != ui_web.OperatingSystem.linux, + ); // Regression test for https://github.com/flutter/flutter/issues/141186. test('updates meta state for Meta key seen as "Process" key', () { RawKeyboard.initialize(); Map? dataReceived; - ui.PlatformDispatcher.instance.onPlatformMessage = (String channel, ByteData? data, - ui.PlatformMessageResponseCallback? callback) { + ui.PlatformDispatcher.instance.onPlatformMessage = ( + String channel, + ByteData? data, + ui.PlatformMessageResponseCallback? callback, + ) { dataReceived = const JSONMessageCodec().decodeMessage(data) as Map?; }; @@ -218,35 +236,23 @@ void testMain() { RawKeyboard.initialize(); final List> messages = >[]; - ui.PlatformDispatcher.instance.onPlatformMessage = (String channel, ByteData? data, - ui.PlatformMessageResponseCallback? callback) { + ui.PlatformDispatcher.instance.onPlatformMessage = ( + String channel, + ByteData? data, + ui.PlatformMessageResponseCallback? callback, + ) { messages.add(const JSONMessageCodec().decodeMessage(data) as Map); }; DomKeyboardEvent event; - event = dispatchKeyboardEvent( - 'keydown', - key: 'SomeKey', - code: 'SomeCode', - repeat: true, - ); + event = dispatchKeyboardEvent('keydown', key: 'SomeKey', code: 'SomeCode', repeat: true); expect(event.defaultPrevented, isFalse); - event = dispatchKeyboardEvent( - 'keydown', - key: 'SomeKey', - code: 'SomeCode', - repeat: true, - ); + event = dispatchKeyboardEvent('keydown', key: 'SomeKey', code: 'SomeCode', repeat: true); expect(event.defaultPrevented, isFalse); - event = dispatchKeyboardEvent( - 'keydown', - key: 'SomeKey', - code: 'SomeCode', - repeat: true, - ); + event = dispatchKeyboardEvent('keydown', key: 'SomeKey', code: 'SomeCode', repeat: true); expect(event.defaultPrevented, isFalse); final Map expectedMessage = { @@ -258,11 +264,7 @@ void testMain() { 'metaState': 0, 'keyCode': 0, }; - expect(messages, >[ - expectedMessage, - expectedMessage, - expectedMessage, - ]); + expect(messages, >[expectedMessage, expectedMessage, expectedMessage]); RawKeyboard.instance!.dispose(); }); @@ -271,8 +273,11 @@ void testMain() { RawKeyboard.initialize(); int count = 0; - ui.PlatformDispatcher.instance.onPlatformMessage = (String channel, ByteData? data, - ui.PlatformMessageResponseCallback? callback) { + ui.PlatformDispatcher.instance.onPlatformMessage = ( + String channel, + ByteData? data, + ui.PlatformMessageResponseCallback? callback, + ) { count += 1; }; @@ -295,18 +300,18 @@ void testMain() { RawKeyboard.initialize(); int count = 0; - ui.PlatformDispatcher.instance.onPlatformMessage = (String channel, ByteData? data, - ui.PlatformMessageResponseCallback? callback) { + ui.PlatformDispatcher.instance.onPlatformMessage = ( + String channel, + ByteData? data, + ui.PlatformMessageResponseCallback? callback, + ) { count += 1; - final ByteData response = const JSONMessageCodec().encodeMessage({'handled': true})!; + final ByteData response = + const JSONMessageCodec().encodeMessage({'handled': true})!; callback!(response); }; - final DomKeyboardEvent event = dispatchKeyboardEvent( - 'keydown', - key: 'Tab', - code: 'Tab', - ); + final DomKeyboardEvent event = dispatchKeyboardEvent('keydown', key: 'Tab', code: 'Tab'); expect(event.defaultPrevented, isTrue); expect(count, 1); @@ -318,18 +323,18 @@ void testMain() { RawKeyboard.initialize(); int count = 0; - ui.PlatformDispatcher.instance.onPlatformMessage = (String channel, ByteData? data, - ui.PlatformMessageResponseCallback? callback) { + ui.PlatformDispatcher.instance.onPlatformMessage = ( + String channel, + ByteData? data, + ui.PlatformMessageResponseCallback? callback, + ) { count += 1; - final ByteData response = const JSONMessageCodec().encodeMessage({'handled': false})!; + final ByteData response = + const JSONMessageCodec().encodeMessage({'handled': false})!; callback!(response); }; - final DomKeyboardEvent event = dispatchKeyboardEvent( - 'keydown', - key: 'Tab', - code: 'Tab', - ); + final DomKeyboardEvent event = dispatchKeyboardEvent('keydown', key: 'Tab', code: 'Tab'); expect(event.defaultPrevented, isFalse); expect(count, 1); @@ -341,8 +346,11 @@ void testMain() { RawKeyboard.initialize(); int count = 0; - ui.PlatformDispatcher.instance.onPlatformMessage = (String channel, ByteData? data, - ui.PlatformMessageResponseCallback? callback) { + ui.PlatformDispatcher.instance.onPlatformMessage = ( + String channel, + ByteData? data, + ui.PlatformMessageResponseCallback? callback, + ) { count += 1; }; @@ -361,16 +369,18 @@ void testMain() { RawKeyboard.instance!.dispose(); }); - test( - 'the "Tab" key should never be ignored when it is not a part of IME composition', - () { + test('the "Tab" key should never be ignored when it is not a part of IME composition', () { RawKeyboard.initialize(); int count = 0; - ui.PlatformDispatcher.instance.onPlatformMessage = (String channel, ByteData? data, - ui.PlatformMessageResponseCallback? callback) { + ui.PlatformDispatcher.instance.onPlatformMessage = ( + String channel, + ByteData? data, + ui.PlatformMessageResponseCallback? callback, + ) { count += 1; - final ByteData response = const JSONMessageCodec().encodeMessage({'handled': true})!; + final ByteData response = + const JSONMessageCodec().encodeMessage({'handled': true})!; callback!(response); }; @@ -393,17 +403,25 @@ void testMain() { RawKeyboard.initialize(); int count = 0; - ui.PlatformDispatcher.instance.onPlatformMessage = (String channel, ByteData? data, - ui.PlatformMessageResponseCallback? callback) { + ui.PlatformDispatcher.instance.onPlatformMessage = ( + String channel, + ByteData? data, + ui.PlatformMessageResponseCallback? callback, + ) { count += 1; - final ByteData response = const JSONMessageCodec() - .encodeMessage({'handled': true})!; + final ByteData response = + const JSONMessageCodec().encodeMessage({'handled': true})!; callback!(response); }; useTextEditingElement((DomElement element) { - dispatchKeyboardEvent('keydown', - key: 'Tab', code: 'Tab', target: element, isComposing: true); + dispatchKeyboardEvent( + 'keydown', + key: 'Tab', + code: 'Tab', + target: element, + isComposing: true, + ); expect(count, 0); // no message sent to framework }); @@ -411,217 +429,206 @@ void testMain() { RawKeyboard.instance!.dispose(); }); - testFakeAsync( - 'On macOS, synthesize keyup when shortcut is handled by the system', - (FakeAsync async) { - // This can happen when the user clicks `cmd+alt+i` to open devtools. Here - // is the sequence we receive from the browser in such case: - // - // keydown(cmd) -> keydown(alt) -> keydown(i) -> keyup(alt) -> keyup(cmd) - // - // There's no `keyup(i)`. The web engine is expected to synthesize a - // `keyup(i)` event. - RawKeyboard.initialize(onMacOs: true); + testFakeAsync('On macOS, synthesize keyup when shortcut is handled by the system', ( + FakeAsync async, + ) { + // This can happen when the user clicks `cmd+alt+i` to open devtools. Here + // is the sequence we receive from the browser in such case: + // + // keydown(cmd) -> keydown(alt) -> keydown(i) -> keyup(alt) -> keyup(cmd) + // + // There's no `keyup(i)`. The web engine is expected to synthesize a + // `keyup(i)` event. + RawKeyboard.initialize(onMacOs: true); - final List> messages = >[]; - ui.PlatformDispatcher.instance.onPlatformMessage = (String channel, ByteData? data, - ui.PlatformMessageResponseCallback? callback) { - messages.add(const JSONMessageCodec().decodeMessage(data) as Map); - }; + final List> messages = >[]; + ui.PlatformDispatcher.instance.onPlatformMessage = ( + String channel, + ByteData? data, + ui.PlatformMessageResponseCallback? callback, + ) { + messages.add(const JSONMessageCodec().decodeMessage(data) as Map); + }; - dispatchKeyboardEvent( - 'keydown', - key: 'Meta', - code: 'MetaLeft', - location: 1, - isMetaPressed: true, - ); - dispatchKeyboardEvent( - 'keydown', - key: 'Alt', - code: 'AltLeft', - location: 1, - isMetaPressed: true, - isAltPressed: true, - ); - dispatchKeyboardEvent( - 'keydown', - key: 'i', - code: 'KeyI', - isMetaPressed: true, - isAltPressed: true, - ); - async.elapse(const Duration(milliseconds: 10)); - dispatchKeyboardEvent( - 'keyup', - key: 'Meta', - code: 'MetaLeft', - location: 1, - isAltPressed: true, - ); - dispatchKeyboardEvent( - 'keyup', - key: 'Alt', - code: 'AltLeft', - location: 1, - ); - // Notice no `keyup` for "i". - - expect(messages, >[ - { - 'type': 'keydown', - 'keymap': 'web', - 'key': 'Meta', - 'code': 'MetaLeft', - 'location': 1, - // meta - 'metaState': 0x8, - 'keyCode': 0, - }, - { - 'type': 'keydown', - 'keymap': 'web', - 'key': 'Alt', - 'code': 'AltLeft', - 'location': 1, - // alt meta - 'metaState': 0x2 | 0x8, - 'keyCode': 0, - }, - { - 'type': 'keydown', - 'keymap': 'web', - 'key': 'i', - 'code': 'KeyI', - 'location': 0, - // alt meta - 'metaState': 0x2 | 0x8, - 'keyCode': 0, - }, - { - 'type': 'keyup', - 'keymap': 'web', - 'key': 'Meta', - 'code': 'MetaLeft', - 'location': 1, - // alt - 'metaState': 0x2, - 'keyCode': 0, - }, - { - 'type': 'keyup', - 'keymap': 'web', - 'key': 'Alt', - 'code': 'AltLeft', - 'location': 1, - 'metaState': 0x0, - 'keyCode': 0, - }, - ]); - messages.clear(); + dispatchKeyboardEvent( + 'keydown', + key: 'Meta', + code: 'MetaLeft', + location: 1, + isMetaPressed: true, + ); + dispatchKeyboardEvent( + 'keydown', + key: 'Alt', + code: 'AltLeft', + location: 1, + isMetaPressed: true, + isAltPressed: true, + ); + dispatchKeyboardEvent( + 'keydown', + key: 'i', + code: 'KeyI', + isMetaPressed: true, + isAltPressed: true, + ); + async.elapse(const Duration(milliseconds: 10)); + dispatchKeyboardEvent( + 'keyup', + key: 'Meta', + code: 'MetaLeft', + location: 1, + isAltPressed: true, + ); + dispatchKeyboardEvent('keyup', key: 'Alt', code: 'AltLeft', location: 1); + // Notice no `keyup` for "i". - // Still too eary to synthesize a keyup event. - async.elapse(const Duration(milliseconds: 50)); - expect(messages, isEmpty); + expect(messages, >[ + { + 'type': 'keydown', + 'keymap': 'web', + 'key': 'Meta', + 'code': 'MetaLeft', + 'location': 1, + // meta + 'metaState': 0x8, + 'keyCode': 0, + }, + { + 'type': 'keydown', + 'keymap': 'web', + 'key': 'Alt', + 'code': 'AltLeft', + 'location': 1, + // alt meta + 'metaState': 0x2 | 0x8, + 'keyCode': 0, + }, + { + 'type': 'keydown', + 'keymap': 'web', + 'key': 'i', + 'code': 'KeyI', + 'location': 0, + // alt meta + 'metaState': 0x2 | 0x8, + 'keyCode': 0, + }, + { + 'type': 'keyup', + 'keymap': 'web', + 'key': 'Meta', + 'code': 'MetaLeft', + 'location': 1, + // alt + 'metaState': 0x2, + 'keyCode': 0, + }, + { + 'type': 'keyup', + 'keymap': 'web', + 'key': 'Alt', + 'code': 'AltLeft', + 'location': 1, + 'metaState': 0x0, + 'keyCode': 0, + }, + ]); + messages.clear(); - async.elapse(const Duration(seconds: 3)); - expect(messages, >[ - { - 'type': 'keyup', - 'keymap': 'web', - 'key': 'i', - 'code': 'KeyI', - 'location': 0, - 'metaState': 0x0, - 'keyCode': 0, - } - ]); + // Still too eary to synthesize a keyup event. + async.elapse(const Duration(milliseconds: 50)); + expect(messages, isEmpty); - RawKeyboard.instance!.dispose(); - }, - ); + async.elapse(const Duration(seconds: 3)); + expect(messages, >[ + { + 'type': 'keyup', + 'keymap': 'web', + 'key': 'i', + 'code': 'KeyI', + 'location': 0, + 'metaState': 0x0, + 'keyCode': 0, + }, + ]); - testFakeAsync( - 'On macOS, do not synthesize keyup when we receive repeat events', - (FakeAsync async) { - RawKeyboard.initialize(onMacOs: true); + RawKeyboard.instance!.dispose(); + }); - final List> messages = >[]; - ui.PlatformDispatcher.instance.onPlatformMessage = (String channel, ByteData? data, - ui.PlatformMessageResponseCallback? callback) { - messages.add(const JSONMessageCodec().decodeMessage(data) as Map); - }; + testFakeAsync('On macOS, do not synthesize keyup when we receive repeat events', ( + FakeAsync async, + ) { + RawKeyboard.initialize(onMacOs: true); - dispatchKeyboardEvent( - 'keydown', - key: 'Meta', - code: 'MetaLeft', - location: 1, - isMetaPressed: true, - ); - dispatchKeyboardEvent( - 'keydown', - key: 'Alt', - code: 'AltLeft', - location: 1, - isMetaPressed: true, - isAltPressed: true, - ); - dispatchKeyboardEvent( - 'keydown', - key: 'i', - code: 'KeyI', - isMetaPressed: true, - isAltPressed: true, - ); - async.elapse(const Duration(milliseconds: 10)); - dispatchKeyboardEvent( - 'keyup', - key: 'Meta', - code: 'MetaLeft', - location: 1, - isAltPressed: true, - ); - dispatchKeyboardEvent( - 'keyup', - key: 'Alt', - code: 'AltLeft', - location: 1, - ); - // Notice no `keyup` for "i". + final List> messages = >[]; + ui.PlatformDispatcher.instance.onPlatformMessage = ( + String channel, + ByteData? data, + ui.PlatformMessageResponseCallback? callback, + ) { + messages.add(const JSONMessageCodec().decodeMessage(data) as Map); + }; - messages.clear(); + dispatchKeyboardEvent( + 'keydown', + key: 'Meta', + code: 'MetaLeft', + location: 1, + isMetaPressed: true, + ); + dispatchKeyboardEvent( + 'keydown', + key: 'Alt', + code: 'AltLeft', + location: 1, + isMetaPressed: true, + isAltPressed: true, + ); + dispatchKeyboardEvent( + 'keydown', + key: 'i', + code: 'KeyI', + isMetaPressed: true, + isAltPressed: true, + ); + async.elapse(const Duration(milliseconds: 10)); + dispatchKeyboardEvent( + 'keyup', + key: 'Meta', + code: 'MetaLeft', + location: 1, + isAltPressed: true, + ); + dispatchKeyboardEvent('keyup', key: 'Alt', code: 'AltLeft', location: 1); + // Notice no `keyup` for "i". - // Spend more than 2 seconds sending repeat events and make sure no - // keyup was synthesized. - for (int i = 0; i < 20; i++) { - async.elapse(const Duration(milliseconds: 100)); - dispatchKeyboardEvent( - 'keydown', - key: 'i', - code: 'KeyI', - repeat: true, - ); - } - - // There should be no synthesized keyup. - expect(messages, hasLength(20)); - for (int i = 0; i < 20; i++) { - expect(messages[i], { - 'type': 'keydown', - 'keymap': 'web', - 'key': 'i', - 'code': 'KeyI', - 'location': 0, - 'metaState': 0x0, - 'keyCode': 0, - }); - } - messages.clear(); + messages.clear(); - RawKeyboard.instance!.dispose(); - }, - ); + // Spend more than 2 seconds sending repeat events and make sure no + // keyup was synthesized. + for (int i = 0; i < 20; i++) { + async.elapse(const Duration(milliseconds: 100)); + dispatchKeyboardEvent('keydown', key: 'i', code: 'KeyI', repeat: true); + } + + // There should be no synthesized keyup. + expect(messages, hasLength(20)); + for (int i = 0; i < 20; i++) { + expect(messages[i], { + 'type': 'keydown', + 'keymap': 'web', + 'key': 'i', + 'code': 'KeyI', + 'location': 0, + 'metaState': 0x0, + 'keyCode': 0, + }); + } + messages.clear(); + + RawKeyboard.instance!.dispose(); + }); testFakeAsync( 'On macOS, do not synthesize keyup when keys are not affected by meta modifiers', @@ -629,21 +636,16 @@ void testMain() { RawKeyboard.initialize(); final List> messages = >[]; - ui.PlatformDispatcher.instance.onPlatformMessage = (String channel, ByteData? data, - ui.PlatformMessageResponseCallback? callback) { + ui.PlatformDispatcher.instance.onPlatformMessage = ( + String channel, + ByteData? data, + ui.PlatformMessageResponseCallback? callback, + ) { messages.add(const JSONMessageCodec().decodeMessage(data) as Map); }; - dispatchKeyboardEvent( - 'keydown', - key: 'i', - code: 'KeyI', - ); - dispatchKeyboardEvent( - 'keydown', - key: 'o', - code: 'KeyO', - ); + dispatchKeyboardEvent('keydown', key: 'i', code: 'KeyI'); + dispatchKeyboardEvent('keydown', key: 'o', code: 'KeyO'); messages.clear(); // Wait for a long-enough period of time and no events @@ -659,8 +661,11 @@ void testMain() { RawKeyboard.initialize(onMacOs: true); final List> messages = >[]; - ui.PlatformDispatcher.instance.onPlatformMessage = (String channel, ByteData? data, - ui.PlatformMessageResponseCallback? callback) { + ui.PlatformDispatcher.instance.onPlatformMessage = ( + String channel, + ByteData? data, + ui.PlatformMessageResponseCallback? callback, + ) { messages.add(const JSONMessageCodec().decodeMessage(data) as Map); }; @@ -711,72 +716,66 @@ void testMain() { // alt 'metaState': 0x2, 'keyCode': 0, - } + }, ]); RawKeyboard.instance!.dispose(); }); - testFakeAsync( - 'On non-macOS, do not synthesize keyup for shortcuts', - (FakeAsync async) { - RawKeyboard.initialize(); // onMacOs: false + testFakeAsync('On non-macOS, do not synthesize keyup for shortcuts', (FakeAsync async) { + RawKeyboard.initialize(); // onMacOs: false - final List> messages = >[]; - ui.PlatformDispatcher.instance.onPlatformMessage = (String channel, ByteData? data, - ui.PlatformMessageResponseCallback? callback) { - messages.add(const JSONMessageCodec().decodeMessage(data) as Map); - }; - - dispatchKeyboardEvent( - 'keydown', - key: 'Meta', - code: 'MetaLeft', - location: 1, - isMetaPressed: true, - ); - dispatchKeyboardEvent( - 'keydown', - key: 'Alt', - code: 'AltLeft', - location: 1, - isMetaPressed: true, - isAltPressed: true, - ); - dispatchKeyboardEvent( - 'keydown', - key: 'i', - code: 'KeyI', - isMetaPressed: true, - isAltPressed: true, - ); - async.elapse(const Duration(milliseconds: 10)); - dispatchKeyboardEvent( - 'keyup', - key: 'Meta', - code: 'MetaLeft', - location: 1, - isAltPressed: true, - ); - dispatchKeyboardEvent( - 'keyup', - key: 'Alt', - code: 'AltLeft', - location: 1, - ); - // Notice no `keyup` for "i". + final List> messages = >[]; + ui.PlatformDispatcher.instance.onPlatformMessage = ( + String channel, + ByteData? data, + ui.PlatformMessageResponseCallback? callback, + ) { + messages.add(const JSONMessageCodec().decodeMessage(data) as Map); + }; - expect(messages, hasLength(5)); - messages.clear(); + dispatchKeyboardEvent( + 'keydown', + key: 'Meta', + code: 'MetaLeft', + location: 1, + isMetaPressed: true, + ); + dispatchKeyboardEvent( + 'keydown', + key: 'Alt', + code: 'AltLeft', + location: 1, + isMetaPressed: true, + isAltPressed: true, + ); + dispatchKeyboardEvent( + 'keydown', + key: 'i', + code: 'KeyI', + isMetaPressed: true, + isAltPressed: true, + ); + async.elapse(const Duration(milliseconds: 10)); + dispatchKeyboardEvent( + 'keyup', + key: 'Meta', + code: 'MetaLeft', + location: 1, + isAltPressed: true, + ); + dispatchKeyboardEvent('keyup', key: 'Alt', code: 'AltLeft', location: 1); + // Notice no `keyup` for "i". - // Never synthesize keyup events. - async.elapse(const Duration(seconds: 3)); - expect(messages, isEmpty); + expect(messages, hasLength(5)); + messages.clear(); - RawKeyboard.instance!.dispose(); - }, - ); + // Never synthesize keyup events. + async.elapse(const Duration(seconds: 3)); + expect(messages, isEmpty); + RawKeyboard.instance!.dispose(); + }); }); } @@ -810,7 +809,7 @@ DomKeyboardEvent dispatchKeyboardEvent( }) { target ??= domWindow; - final DomKeyboardEvent event = createDomKeyboardEvent(type, { + final DomKeyboardEvent event = createDomKeyboardEvent(type, { if (key != null) 'key': key, if (code != null) 'code': code, 'location': location, diff --git a/lib/web_ui/test/engine/routing_test.dart b/lib/web_ui/test/engine/routing_test.dart index 641d922e0fc8b..04e82893ae99e 100644 --- a/lib/web_ui/test/engine/routing_test.dart +++ b/lib/web_ui/test/engine/routing_test.dart @@ -17,10 +17,7 @@ import 'history_test.dart'; EngineFlutterWindow get myWindow => EnginePlatformDispatcher.instance.implicitView!; Map _tagStateWithSerialCount(dynamic state, int serialCount) { - return { - 'serialCount': serialCount, - 'state': state, - }; + return {'serialCount': serialCount, 'state': state}; } void main() { @@ -63,13 +60,15 @@ void testMain() { // window.defaultRouteName is now permanently decoupled from the history, // even in subsequent tests, because the PlatformDispatcher caches it. - test('window.defaultRouteName should reset after navigation platform message', - () async { - await myWindow.debugInitializeHistory(TestUrlStrategy.fromEntry( - // The URL here does not set the PlatformDispatcher's defaultRouteName, - // since it got cached as soon as we read it above. - const TestHistoryEntry('initial state', null, '/not-really-inital/THIS_IS_IGNORED'), - ), useSingle: true); + test('window.defaultRouteName should reset after navigation platform message', () async { + await myWindow.debugInitializeHistory( + TestUrlStrategy.fromEntry( + // The URL here does not set the PlatformDispatcher's defaultRouteName, + // since it got cached as soon as we read it above. + const TestHistoryEntry('initial state', null, '/not-really-inital/THIS_IS_IGNORED'), + ), + useSingle: true, + ); // Reading it multiple times should return the same value. expect(myWindow.defaultRouteName, '/initial'); expect(myWindow.defaultRouteName, '/initial'); @@ -77,11 +76,12 @@ void testMain() { final Completer callback = Completer(); myWindow.sendPlatformMessage( 'flutter/navigation', - const JSONMethodCodec().encodeMethodCall(const MethodCall( - 'routeUpdated', - {'routeName': '/bar'}, - )), - (_) { callback.complete(); }, + const JSONMethodCodec().encodeMethodCall( + const MethodCall('routeUpdated', {'routeName': '/bar'}), + ), + (_) { + callback.complete(); + }, ); await callback.future; // After a navigation platform message, the PlatformDispatcher's @@ -93,9 +93,10 @@ void testMain() { test('can switch history mode', () async { Completer callback; - await myWindow.debugInitializeHistory(TestUrlStrategy.fromEntry( - const TestHistoryEntry('initial state', null, '/initial'), - ), useSingle: false); + await myWindow.debugInitializeHistory( + TestUrlStrategy.fromEntry(const TestHistoryEntry('initial state', null, '/initial')), + useSingle: false, + ); expect(myWindow.browserHistory, isA()); Future check(String method, Object? arguments) async { @@ -103,7 +104,9 @@ void testMain() { myWindow.sendPlatformMessage( 'flutter/navigation', const JSONMethodCodec().encodeMethodCall(MethodCall(method, arguments)), - (_) { callback.complete(); }, + (_) { + callback.complete(); + }, ); await callback.future; expect(myWindow.browserHistory, isA()); @@ -113,72 +116,87 @@ void testMain() { // See https://github.com/flutter/flutter/issues/83158#issuecomment-847483010 await check('selectSingleEntryHistory', null); // -> single await check('selectMultiEntryHistory', null); // -> multi - await check('selectSingleEntryHistory', {}); // -> single - await check('selectMultiEntryHistory', {}); // -> multi - await check('routeUpdated', {'routeName': '/bar'}); // -> single - await check('routeInformationUpdated', {'location': '/bar'}); // does not change mode - await check('selectMultiEntryHistory', {}); // -> multi - await check('routeInformationUpdated', {'location': '/bar'}); // does not change mode + await check( + 'selectSingleEntryHistory', + {}, + ); // -> single + await check( + 'selectMultiEntryHistory', + {}, + ); // -> multi + await check('routeUpdated', { + 'routeName': '/bar', + }); // -> single + await check('routeInformationUpdated', { + 'location': '/bar', + }); // does not change mode + await check( + 'selectMultiEntryHistory', + {}, + ); // -> multi + await check('routeInformationUpdated', { + 'location': '/bar', + }); // does not change mode }); - test('handleNavigationMessage throws for route update methods called with null arguments', - () async { - expect(() async { - await myWindow.handleNavigationMessage( - const JSONMethodCodec().encodeMethodCall(const MethodCall( - 'routeUpdated', - )) - ); - }, throwsAssertionError); - - expect(() async { - await myWindow.handleNavigationMessage( - const JSONMethodCodec().encodeMethodCall(const MethodCall( - 'routeInformationUpdated', - )) - ); - }, throwsAssertionError); - }); + test( + 'handleNavigationMessage throws for route update methods called with null arguments', + () async { + expect(() async { + await myWindow.handleNavigationMessage( + const JSONMethodCodec().encodeMethodCall(const MethodCall('routeUpdated')), + ); + }, throwsAssertionError); + + expect(() async { + await myWindow.handleNavigationMessage( + const JSONMethodCodec().encodeMethodCall(const MethodCall('routeInformationUpdated')), + ); + }, throwsAssertionError); + }, + ); test('handleNavigationMessage execute request in order.', () async { // Start with multi entries. - await myWindow.debugInitializeHistory(TestUrlStrategy.fromEntry( - const TestHistoryEntry('initial state', null, '/initial'), - ), useSingle: false); + await myWindow.debugInitializeHistory( + TestUrlStrategy.fromEntry(const TestHistoryEntry('initial state', null, '/initial')), + useSingle: false, + ); expect(myWindow.browserHistory, isA()); final List executionOrder = []; - await myWindow.handleNavigationMessage( - const JSONMethodCodec().encodeMethodCall(const MethodCall( - 'selectSingleEntryHistory', - )) - ).then((bool data) { - executionOrder.add('1'); - }); - await myWindow.handleNavigationMessage( - const JSONMethodCodec().encodeMethodCall(const MethodCall( - 'selectMultiEntryHistory', - )) - ).then((bool data) { - executionOrder.add('2'); - }); - await myWindow.handleNavigationMessage( - const JSONMethodCodec().encodeMethodCall(const MethodCall( - 'selectSingleEntryHistory', - )) - ).then((bool data) { - executionOrder.add('3'); - }); - await myWindow.handleNavigationMessage( - const JSONMethodCodec().encodeMethodCall(const MethodCall( - 'routeInformationUpdated', - { - 'location': '/baz', - 'state': null, - }, // boom - )) - ).then((bool data) { - executionOrder.add('4'); - }); + await myWindow + .handleNavigationMessage( + const JSONMethodCodec().encodeMethodCall(const MethodCall('selectSingleEntryHistory')), + ) + .then((bool data) { + executionOrder.add('1'); + }); + await myWindow + .handleNavigationMessage( + const JSONMethodCodec().encodeMethodCall(const MethodCall('selectMultiEntryHistory')), + ) + .then((bool data) { + executionOrder.add('2'); + }); + await myWindow + .handleNavigationMessage( + const JSONMethodCodec().encodeMethodCall(const MethodCall('selectSingleEntryHistory')), + ) + .then((bool data) { + executionOrder.add('3'); + }); + await myWindow + .handleNavigationMessage( + const JSONMethodCodec().encodeMethodCall( + const MethodCall( + 'routeInformationUpdated', + {'location': '/baz', 'state': null}, // boom + ), + ), + ) + .then((bool data) { + executionOrder.add('4'); + }); // The routeInformationUpdated should finish after the browser history // has been set to single entry. expect(executionOrder.length, 4); @@ -188,22 +206,23 @@ void testMain() { expect(executionOrder[3], '4'); }); - test('should not throw when using nav1 and nav2 together', - () async { - await myWindow.debugInitializeHistory(TestUrlStrategy.fromEntry( - const TestHistoryEntry('initial state', null, '/initial'), - ), useSingle: false); + test('should not throw when using nav1 and nav2 together', () async { + await myWindow.debugInitializeHistory( + TestUrlStrategy.fromEntry(const TestHistoryEntry('initial state', null, '/initial')), + useSingle: false, + ); expect(myWindow.browserHistory, isA()); // routeUpdated resets the history type Completer callback = Completer(); myWindow.sendPlatformMessage( 'flutter/navigation', - const JSONMethodCodec().encodeMethodCall(const MethodCall( - 'routeUpdated', - {'routeName': '/bar'}, - )), - (_) { callback.complete(); }, + const JSONMethodCodec().encodeMethodCall( + const MethodCall('routeUpdated', {'routeName': '/bar'}), + ), + (_) { + callback.complete(); + }, ); await callback.future; expect(myWindow.browserHistory, isA()); @@ -213,14 +232,15 @@ void testMain() { callback = Completer(); myWindow.sendPlatformMessage( 'flutter/navigation', - const JSONMethodCodec().encodeMethodCall(const MethodCall( - 'routeInformationUpdated', - { + const JSONMethodCodec().encodeMethodCall( + const MethodCall('routeInformationUpdated', { 'location': '/baz', 'state': null, - }, - )), - (_) { callback.complete(); }, + }), + ), + (_) { + callback.complete(); + }, ); await callback.future; expect(myWindow.browserHistory, isA()); @@ -228,43 +248,40 @@ void testMain() { // they can be interleaved safely await myWindow.handleNavigationMessage( - const JSONMethodCodec().encodeMethodCall(const MethodCall( - 'routeUpdated', - {'routeName': '/foo'}, - )) + const JSONMethodCodec().encodeMethodCall( + const MethodCall('routeUpdated', {'routeName': '/foo'}), + ), ); expect(myWindow.browserHistory, isA()); expect(myWindow.browserHistory.urlStrategy!.getPath(), '/foo'); }); - test('should not throw when state is complex json object', - () async { + test('should not throw when state is complex json object', () async { // Regression test https://github.com/flutter/flutter/issues/87823. - await myWindow.debugInitializeHistory(TestUrlStrategy.fromEntry( - const TestHistoryEntry('initial state', null, '/initial'), - ), useSingle: false); + await myWindow.debugInitializeHistory( + TestUrlStrategy.fromEntry(const TestHistoryEntry('initial state', null, '/initial')), + useSingle: false, + ); expect(myWindow.browserHistory, isA()); // routeInformationUpdated does not final Completer callback = Completer(); myWindow.sendPlatformMessage( 'flutter/navigation', - const JSONMethodCodec().encodeMethodCall(const MethodCall( - 'routeInformationUpdated', - { + const JSONMethodCodec().encodeMethodCall( + const MethodCall('routeInformationUpdated', { 'location': '/baz', 'state': { 'state1': true, 'state2': 1, 'state3': 'string', - 'state4': { - 'substate1': 1.0, - 'substate2': 'string2', - } + 'state4': {'substate1': 1.0, 'substate2': 'string2'}, }, - }, - )), - (_) { callback.complete(); }, + }), + ), + (_) { + callback.complete(); + }, ); await callback.future; expect(myWindow.browserHistory, isA()); @@ -280,48 +297,50 @@ void testMain() { expect(state4['substate2'], 'string2'); }); - test('routeInformationUpdated can handle uri', - () async { - await myWindow.debugInitializeHistory(TestUrlStrategy.fromEntry( - const TestHistoryEntry('initial state', null, '/initial'), - ), useSingle: false); + test('routeInformationUpdated can handle uri', () async { + await myWindow.debugInitializeHistory( + TestUrlStrategy.fromEntry(const TestHistoryEntry('initial state', null, '/initial')), + useSingle: false, + ); expect(myWindow.browserHistory, isA()); // routeInformationUpdated does not final Completer callback = Completer(); myWindow.sendPlatformMessage( 'flutter/navigation', - const JSONMethodCodec().encodeMethodCall(const MethodCall( - 'routeInformationUpdated', - { + const JSONMethodCodec().encodeMethodCall( + const MethodCall('routeInformationUpdated', { 'uri': 'http://myhostname.com/baz?abc=def#fragment', - }, - )), - (_) { callback.complete(); }, + }), + ), + (_) { + callback.complete(); + }, ); await callback.future; expect(myWindow.browserHistory, isA()); expect(myWindow.browserHistory.urlStrategy!.getPath(), '/baz?abc=def#fragment'); }); - test('can replace in MultiEntriesBrowserHistory', - () async { - await myWindow.debugInitializeHistory(TestUrlStrategy.fromEntry( - const TestHistoryEntry('initial state', null, '/initial'), - ), useSingle: false); + test('can replace in MultiEntriesBrowserHistory', () async { + await myWindow.debugInitializeHistory( + TestUrlStrategy.fromEntry(const TestHistoryEntry('initial state', null, '/initial')), + useSingle: false, + ); expect(myWindow.browserHistory, isA()); Completer callback = Completer(); myWindow.sendPlatformMessage( 'flutter/navigation', - const JSONMethodCodec().encodeMethodCall(const MethodCall( - 'routeInformationUpdated', - { + const JSONMethodCodec().encodeMethodCall( + const MethodCall('routeInformationUpdated', { 'location': '/baz', 'state': '/state', - }, - )), - (_) { callback.complete(); }, + }), + ), + (_) { + callback.complete(); + }, ); await callback.future; expect(myWindow.browserHistory.urlStrategy!.getPath(), '/baz'); @@ -330,15 +349,16 @@ void testMain() { callback = Completer(); myWindow.sendPlatformMessage( 'flutter/navigation', - const JSONMethodCodec().encodeMethodCall(const MethodCall( - 'routeInformationUpdated', - { + const JSONMethodCodec().encodeMethodCall( + const MethodCall('routeInformationUpdated', { 'location': '/baz', 'state': '/state1', - 'replace': true - }, - )), - (_) { callback.complete(); }, + 'replace': true, + }), + ), + (_) { + callback.complete(); + }, ); await callback.future; expect(myWindow.browserHistory.urlStrategy!.getPath(), '/baz'); @@ -347,18 +367,22 @@ void testMain() { callback = Completer(); myWindow.sendPlatformMessage( 'flutter/navigation', - const JSONMethodCodec().encodeMethodCall(const MethodCall( - 'routeInformationUpdated', - { + const JSONMethodCodec().encodeMethodCall( + const MethodCall('routeInformationUpdated', { 'location': '/foo', 'state': '/foostate1', - }, - )), - (_) { callback.complete(); }, + }), + ), + (_) { + callback.complete(); + }, ); await callback.future; expect(myWindow.browserHistory.urlStrategy!.getPath(), '/foo'); - expect(myWindow.browserHistory.urlStrategy!.getState(), _tagStateWithSerialCount('/foostate1', 2)); + expect( + myWindow.browserHistory.urlStrategy!.getState(), + _tagStateWithSerialCount('/foostate1', 2), + ); await myWindow.browserHistory.back(); expect(myWindow.browserHistory.urlStrategy!.getPath(), '/baz'); @@ -376,11 +400,12 @@ void testMain() { final Completer callback = Completer(); myWindow.sendPlatformMessage( 'flutter/navigation', - const JSONMethodCodec().encodeMethodCall(const MethodCall( - 'routeUpdated', - {'routeName': '/bar'}, - )), - (_) { callback.complete(); }, + const JSONMethodCodec().encodeMethodCall( + const MethodCall('routeUpdated', {'routeName': '/bar'}), + ), + (_) { + callback.complete(); + }, ); await callback.future; expect(myWindow.browserHistory, isA()); @@ -401,14 +426,15 @@ void testMain() { final Completer callback = Completer(); myWindow.sendPlatformMessage( 'flutter/navigation', - const JSONMethodCodec().encodeMethodCall(const MethodCall( - 'routeInformationUpdated', - { + const JSONMethodCodec().encodeMethodCall( + const MethodCall('routeInformationUpdated', { 'location': '/baz', 'state': null, - }, - )), - (_) { callback.complete(); }, + }), + ), + (_) { + callback.complete(); + }, ); await callback.future; expect(myWindow.browserHistory, isA()); @@ -420,12 +446,9 @@ void testMain() { test('can disable location strategy', () async { // Disable URL strategy. - expect( - () { - ui_web.urlStrategy = null; - }, - returnsNormally, - ); + expect(() { + ui_web.urlStrategy = null; + }, returnsNormally); // History should be initialized. expect(myWindow.browserHistory, isNotNull); // But without a URL strategy. @@ -445,29 +468,20 @@ void testMain() { ); await myWindow.debugInitializeHistory(testStrategy, useSingle: true); - expect( - () { - ui_web.urlStrategy = null; - }, - throwsA(isAssertionError), - ); + expect(() { + ui_web.urlStrategy = null; + }, throwsA(isAssertionError)); }); test('cannot set url strategy more than once', () async { // First time is okay. - expect( - () { - ui_web.urlStrategy = null; - }, - returnsNormally, - ); + expect(() { + ui_web.urlStrategy = null; + }, returnsNormally); // Second time is not allowed. - expect( - () { - ui_web.urlStrategy = null; - }, - throwsA(isAssertionError), - ); + expect(() { + ui_web.urlStrategy = null; + }, throwsA(isAssertionError)); }); // Regression test for https://github.com/flutter/flutter/issues/77817 diff --git a/lib/web_ui/test/engine/scene_builder_test.dart b/lib/web_ui/test/engine/scene_builder_test.dart index 3d39a22f71342..0053ccc239ba0 100644 --- a/lib/web_ui/test/engine/scene_builder_test.dart +++ b/lib/web_ui/test/engine/scene_builder_test.dart @@ -55,7 +55,7 @@ void testMain() { }); test('picture + platform view (overlapping)', () { - final EngineSceneBuilder sceneBuilder = EngineSceneBuilder(); + final EngineSceneBuilder sceneBuilder = EngineSceneBuilder(); const ui.Rect pictureRect = ui.Rect.fromLTRB(100, 100, 200, 200); const ui.Rect platformViewRect = ui.Rect.fromLTRB(150, 150, 250, 250); @@ -64,17 +64,21 @@ void testMain() { 1, offset: platformViewRect.topLeft, width: platformViewRect.width, - height: platformViewRect.height + height: platformViewRect.height, ); final EngineScene scene = sceneBuilder.build() as EngineScene; final List slices = scene.rootLayer.slices; expect(slices.length, 1); - expect(slices[0], layerSlice( - withPictureRect: pictureRect, - withPlatformViews: [ - PlatformView(1, platformViewRect, const PlatformViewStyling()) - ])); + expect( + slices[0], + layerSlice( + withPictureRect: pictureRect, + withPlatformViews: [ + PlatformView(1, platformViewRect, const PlatformViewStyling()), + ], + ), + ); }); test('platform view + picture (overlapping)', () { @@ -86,16 +90,21 @@ void testMain() { 1, offset: platformViewRect.topLeft, width: platformViewRect.width, - height: platformViewRect.height + height: platformViewRect.height, ); sceneBuilder.addPicture(ui.Offset.zero, StubPicture(pictureRect)); final EngineScene scene = sceneBuilder.build() as EngineScene; final List slices = scene.rootLayer.slices; expect(slices.length, 2); - expect(slices[0], layerSlice(withPlatformViews: [ - PlatformView(1, platformViewRect, const PlatformViewStyling()) - ])); + expect( + slices[0], + layerSlice( + withPlatformViews: [ + PlatformView(1, platformViewRect, const PlatformViewStyling()), + ], + ), + ); expect(slices[1], layerSlice(withPictureRect: pictureRect)); }); @@ -110,18 +119,22 @@ void testMain() { 1, offset: platformViewRect.topLeft, width: platformViewRect.width, - height: platformViewRect.height + height: platformViewRect.height, ); sceneBuilder.addPicture(ui.Offset.zero, StubPicture(pictureRect2)); final EngineScene scene = sceneBuilder.build() as EngineScene; final List slices = scene.rootLayer.slices; expect(slices.length, 2); - expect(slices[0], layerSlice( - withPictureRect: pictureRect1, - withPlatformViews: [ - PlatformView(1, platformViewRect, const PlatformViewStyling()) - ])); + expect( + slices[0], + layerSlice( + withPictureRect: pictureRect1, + withPlatformViews: [ + PlatformView(1, platformViewRect, const PlatformViewStyling()), + ], + ), + ); expect(slices[1], layerSlice(withPictureRect: pictureRect2)); }); @@ -136,7 +149,7 @@ void testMain() { 1, offset: platformViewRect.topLeft, width: platformViewRect.width, - height: platformViewRect.height + height: platformViewRect.height, ); sceneBuilder.addPicture(ui.Offset.zero, StubPicture(pictureRect2)); @@ -147,11 +160,15 @@ void testMain() { // be grouped into the slice below it to reduce the number of canvases we // need. expect(slices.length, 1); - expect(slices[0], layerSlice( - withPictureRect: const ui.Rect.fromLTRB(50, 50, 200, 200), - withPlatformViews: [ - PlatformView(1, platformViewRect, const PlatformViewStyling()) - ])); + expect( + slices[0], + layerSlice( + withPictureRect: const ui.Rect.fromLTRB(50, 50, 200, 200), + withPlatformViews: [ + PlatformView(1, platformViewRect, const PlatformViewStyling()), + ], + ), + ); }); test('platform view sandwich (overlapping) with offset layers', () { @@ -166,7 +183,7 @@ void testMain() { 1, offset: platformViewRect.topLeft, width: platformViewRect.width, - height: platformViewRect.height + height: platformViewRect.height, ); sceneBuilder.pushOffset(50, 50); sceneBuilder.addPicture(ui.Offset.zero, StubPicture(const ui.Rect.fromLTRB(0, 0, 100, 100))); @@ -174,11 +191,19 @@ void testMain() { final EngineScene scene = sceneBuilder.build() as EngineScene; final List slices = scene.rootLayer.slices; expect(slices.length, 2); - expect(slices[0], layerSlice( - withPictureRect: pictureRect1, - withPlatformViews: [ - PlatformView(1, platformViewRect, const PlatformViewStyling(position: PlatformViewPosition.offset(ui.Offset(150, 150)))) - ])); + expect( + slices[0], + layerSlice( + withPictureRect: pictureRect1, + withPlatformViews: [ + PlatformView( + 1, + platformViewRect, + const PlatformViewStyling(position: PlatformViewPosition.offset(ui.Offset(150, 150))), + ), + ], + ), + ); expect(slices[1], layerSlice(withPictureRect: const ui.Rect.fromLTRB(200, 200, 300, 300))); }); @@ -201,7 +226,7 @@ void testMain() { sceneBuilder.pushOffset(offset.dx, offset.dy); sceneBuilder.addPicture( ui.Offset.zero, - StubPicture(const ui.Rect.fromLTWH(0, 0, tileSize, tileSize)) + StubPicture(const ui.Rect.fromLTWH(0, 0, tileSize, tileSize)), ); sceneBuilder.addPlatformView( 1, @@ -214,11 +239,13 @@ void testMain() { StubPicture(const ui.Rect.fromLTWH(0, 0, tileSize - 20, tileSize - 20)), ); sceneBuilder.pop(); - expectedPlatformViews.add(PlatformView( - 1, - const ui.Rect.fromLTRB(5.0, 5.0, tileSize - 5.0, tileSize - 5.0), - PlatformViewStyling(position: PlatformViewPosition.offset(offset)) - )); + expectedPlatformViews.add( + PlatformView( + 1, + const ui.Rect.fromLTRB(5.0, 5.0, tileSize - 5.0, tileSize - 5.0), + PlatformViewStyling(position: PlatformViewPosition.offset(offset)), + ), + ); } } @@ -228,21 +255,29 @@ void testMain() { // It is important that the optimizations of the scene builder result in // there only being two scene slices. expect(slices.length, 2); - expect(slices[0], layerSlice( - withPictureRect: const ui.Rect.fromLTRB( - padding, - padding, - 10 * (padding + tileSize), - 10 * (padding + tileSize) + expect( + slices[0], + layerSlice( + withPictureRect: const ui.Rect.fromLTRB( + padding, + padding, + 10 * (padding + tileSize), + 10 * (padding + tileSize), + ), + withPlatformViews: expectedPlatformViews, ), - withPlatformViews: expectedPlatformViews, - )); - expect(slices[1], layerSlice(withPictureRect: const ui.Rect.fromLTRB( - padding + 10, - padding + 10, - 10 * (padding + tileSize) - 10, - 10 * (padding + tileSize) - 10, - ))); + ); + expect( + slices[1], + layerSlice( + withPictureRect: const ui.Rect.fromLTRB( + padding + 10, + padding + 10, + 10 * (padding + tileSize) - 10, + 10 * (padding + tileSize) - 10, + ), + ), + ); }); }); } @@ -251,6 +286,7 @@ LayerSliceMatcher layerSlice({ ui.Rect withPictureRect = ui.Rect.zero, List withPlatformViews = const [], }) => LayerSliceMatcher(withPictureRect, withPlatformViews); + class LayerSliceMatcher extends Matcher { LayerSliceMatcher(this.expectedPictureRect, this.expectedPlatformViews); @@ -259,7 +295,9 @@ class LayerSliceMatcher extends Matcher { @override Description describe(Description description) { - return description.add(''); + return description.add( + '', + ); } @override diff --git a/lib/web_ui/test/engine/scene_builder_utils.dart b/lib/web_ui/test/engine/scene_builder_utils.dart index ec014e70548ff..bb145d7cae1df 100644 --- a/lib/web_ui/test/engine/scene_builder_utils.dart +++ b/lib/web_ui/test/engine/scene_builder_utils.dart @@ -34,15 +34,17 @@ class StubPicture implements ScenePicture { } class StubCompositePicture extends StubPicture { - StubCompositePicture(this.children) : super( - children.fold(null, (ui.Rect? previousValue, StubPicture child) { - final ui.Rect childRect = child.cullRect; - if (childRect.isEmpty) { - return previousValue; - } - return previousValue?.expandToInclude(child.cullRect) ?? child.cullRect; - }) ?? ui.Rect.zero - ); + StubCompositePicture(this.children) + : super( + children.fold(null, (ui.Rect? previousValue, StubPicture child) { + final ui.Rect childRect = child.cullRect; + if (childRect.isEmpty) { + return previousValue; + } + return previousValue?.expandToInclude(child.cullRect) ?? child.cullRect; + }) ?? + ui.Rect.zero, + ); final List children; } @@ -91,10 +93,24 @@ class StubSceneCanvas implements SceneCanvas { void clipRect(ui.Rect rect, {ui.ClipOp clipOp = ui.ClipOp.intersect, bool doAntiAlias = true}) {} @override - void drawArc(ui.Rect rect, double startAngle, double sweepAngle, bool useCenter, ui.Paint paint) {} + void drawArc( + ui.Rect rect, + double startAngle, + double sweepAngle, + bool useCenter, + ui.Paint paint, + ) {} @override - void drawAtlas(ui.Image atlas, List transforms, List rects, List? colors, ui.BlendMode? blendMode, ui.Rect? cullRect, ui.Paint paint) {} + void drawAtlas( + ui.Image atlas, + List transforms, + List rects, + List? colors, + ui.BlendMode? blendMode, + ui.Rect? cullRect, + ui.Paint paint, + ) {} @override void drawCircle(ui.Offset c, double radius, ui.Paint paint) {} @@ -136,7 +152,15 @@ class StubSceneCanvas implements SceneCanvas { void drawRRect(ui.RRect rrect, ui.Paint paint) {} @override - void drawRawAtlas(ui.Image atlas, Float32List rstTransforms, Float32List rects, Int32List? colors, ui.BlendMode? blendMode, ui.Rect? cullRect, ui.Paint paint) {} + void drawRawAtlas( + ui.Image atlas, + Float32List rstTransforms, + Float32List rects, + Int32List? colors, + ui.BlendMode? blendMode, + ui.Rect? cullRect, + ui.Paint paint, + ) {} @override void drawRawPoints(ui.PointMode pointMode, Float32List points, ui.Paint paint) {} diff --git a/lib/web_ui/test/engine/scene_view_test.dart b/lib/web_ui/test/engine/scene_view_test.dart index 1b253478ed348..fc9d43ad8e0ce 100644 --- a/lib/web_ui/test/engine/scene_view_test.dart +++ b/lib/web_ui/test/engine/scene_view_test.dart @@ -20,27 +20,24 @@ void main() { } class StubPictureRenderer implements PictureRenderer { - final DomCanvasElement scratchCanvasElement = - createDomCanvasElement(width: 500, height: 500); + final DomCanvasElement scratchCanvasElement = createDomCanvasElement(width: 500, height: 500); @override Future renderPictures(List pictures) async { renderedPictures.addAll(pictures); - final List bitmaps = await Future.wait(pictures.map((ScenePicture picture) { - final ui.Rect cullRect = picture.cullRect; - final Future bitmap = createImageBitmap(scratchCanvasElement as JSObject, ( - x: 0, - y: 0, - width: cullRect.width.toInt(), - height: cullRect.height.toInt(), - )); - return bitmap; - })); - return ( - imageBitmaps: bitmaps, - rasterStartMicros: 0, - rasterEndMicros: 0, + final List bitmaps = await Future.wait( + pictures.map((ScenePicture picture) { + final ui.Rect cullRect = picture.cullRect; + final Future bitmap = createImageBitmap(scratchCanvasElement as JSObject, ( + x: 0, + y: 0, + width: cullRect.width.toInt(), + height: cullRect.height.toInt(), + )); + return bitmap; + }), ); + return (imageBitmaps: bitmaps, rasterStartMicros: 0, rasterEndMicros: 0); } @override @@ -83,15 +80,13 @@ class StubFlutterView implements EngineFlutterView { EnginePlatformDispatcher get platformDispatcher => throw UnimplementedError(); @override - void render(ui.Scene scene, {ui.Size? size}) { - } + void render(ui.Scene scene, {ui.Size? size}) {} @override ViewPadding get systemGestureInsets => throw UnimplementedError(); @override - void updateSemantics(ui.SemanticsUpdate update) { - } + void updateSemantics(ui.SemanticsUpdate update) {} @override int get viewId => throw UnimplementedError(); @@ -164,12 +159,7 @@ void testMain() { test('SceneView places canvas according to device-pixel ratio', () async { debugOverrideDevicePixelRatio(2.0); - final StubPicture picture = StubPicture(const ui.Rect.fromLTWH( - 50, - 80, - 100, - 120, - )); + final StubPicture picture = StubPicture(const ui.Rect.fromLTWH(50, 80, 100, 120)); final EngineRootLayer rootLayer = EngineRootLayer(); rootLayer.slices.add(LayerSlice(picture, [])); final EngineScene scene = EngineScene(rootLayer); @@ -182,8 +172,7 @@ void testMain() { final DomElement containerElement = children.first; expect(containerElement.tagName, equalsIgnoringCase('flt-canvas-container')); - final List containerChildren = - containerElement.children.toList(); + final List containerChildren = containerElement.children.toList(); expect(containerChildren.length, 1); final DomElement canvasElement = containerChildren.first; final DomCSSStyleDeclaration style = canvasElement.style; @@ -195,14 +184,14 @@ void testMain() { debugOverrideDevicePixelRatio(null); }); - test('SceneView places platform view according to device-pixel ratio', - () async { + test('SceneView places platform view according to device-pixel ratio', () async { debugOverrideDevicePixelRatio(2.0); final PlatformView platformView = PlatformView( - 1, - const ui.Rect.fromLTWH(50, 80, 100, 120), - const PlatformViewStyling()); + 1, + const ui.Rect.fromLTWH(50, 80, 100, 120), + const PlatformViewStyling(), + ); final EngineRootLayer rootLayer = EngineRootLayer(); rootLayer.slices.add(LayerSlice(StubPicture(ui.Rect.zero), [platformView])); final EngineScene scene = EngineScene(rootLayer); @@ -231,18 +220,11 @@ void testMain() { debugOverrideDevicePixelRatio(null); }); - test( - 'SceneView always renders most recent picture and skips intermediate pictures', - () async { + test('SceneView always renders most recent picture and skips intermediate pictures', () async { final List pictures = []; final List> renderFutures = >[]; for (int i = 1; i < 20; i++) { - final StubPicture picture = StubPicture(const ui.Rect.fromLTWH( - 50, - 80, - 100, - 120, - )); + final StubPicture picture = StubPicture(const ui.Rect.fromLTWH(50, 80, 100, 120)); pictures.add(picture); final EngineRootLayer rootLayer = EngineRootLayer(); rootLayer.slices.add(LayerSlice(picture, [])); @@ -258,41 +240,36 @@ void testMain() { }); test('SceneView clips pictures that are outside the window screen', () async { - final StubPicture picture = StubPicture(const ui.Rect.fromLTWH( - -50, - -50, - 100, - 120, - )); + final StubPicture picture = StubPicture(const ui.Rect.fromLTWH(-50, -50, 100, 120)); - final EngineRootLayer rootLayer = EngineRootLayer(); - rootLayer.slices.add(LayerSlice(picture, [])); - final EngineScene scene = EngineScene(rootLayer); - await sceneView.renderScene(scene, null); + final EngineRootLayer rootLayer = EngineRootLayer(); + rootLayer.slices.add(LayerSlice(picture, [])); + final EngineScene scene = EngineScene(rootLayer); + await sceneView.renderScene(scene, null); - expect(stubPictureRenderer.renderedPictures.length, 1); - expect(stubPictureRenderer.clipRequests.containsKey(picture), true); + expect(stubPictureRenderer.renderedPictures.length, 1); + expect(stubPictureRenderer.clipRequests.containsKey(picture), true); }); test('SceneView places platform view contents in the DOM', () async { const int expectedPlatformViewId = 1234; int? injectedViewId; - final DomManager stubDomManager = StubDomManager() - ..injectPlatformViewOverride = (int viewId) { - injectedViewId = viewId; - }; - sceneView = EngineSceneView( - stubPictureRenderer, - StubFlutterView()..dom = stubDomManager, - ); + final DomManager stubDomManager = + StubDomManager() + ..injectPlatformViewOverride = (int viewId) { + injectedViewId = viewId; + }; + sceneView = EngineSceneView(stubPictureRenderer, StubFlutterView()..dom = stubDomManager); - final PlatformView platformView = PlatformView(expectedPlatformViewId, - const ui.Rect.fromLTWH(50, 80, 100, 120), const PlatformViewStyling()); + final PlatformView platformView = PlatformView( + expectedPlatformViewId, + const ui.Rect.fromLTWH(50, 80, 100, 120), + const PlatformViewStyling(), + ); final EngineRootLayer rootLayer = EngineRootLayer(); - rootLayer.slices.add( - LayerSlice(StubPicture(ui.Rect.zero), [platformView])); + rootLayer.slices.add(LayerSlice(StubPicture(ui.Rect.zero), [platformView])); final EngineScene scene = EngineScene(rootLayer); await sceneView.renderScene(scene, null); diff --git a/lib/web_ui/test/engine/semantics/semantics_announcement_test.dart b/lib/web_ui/test/engine/semantics/semantics_announcement_test.dart index a9c4e9fa9453b..a91a2b79a6f53 100644 --- a/lib/web_ui/test/engine/semantics/semantics_announcement_test.dart +++ b/lib/web_ui/test/engine/semantics/semantics_announcement_test.dart @@ -44,17 +44,20 @@ void testMain() { } void sendAnnouncementMessage({required String message, int? assertiveness}) { - accessibilityAnnouncements.handleMessage(codec, codec.encodeMessage({ - 'data': { - 'message': message, - 'assertiveness': assertiveness, - }, - })); + accessibilityAnnouncements.handleMessage( + codec, + codec.encodeMessage({ + 'data': {'message': message, 'assertiveness': assertiveness}, + }), + ); } void expectMessages({String polite = '', String assertive = ''}) { expect(accessibilityAnnouncements.ariaLiveElementFor(Assertiveness.polite).text, polite); - expect(accessibilityAnnouncements.ariaLiveElementFor(Assertiveness.assertive).text, assertive); + expect( + accessibilityAnnouncements.ariaLiveElementFor(Assertiveness.assertive).text, + assertive, + ); } void expectNoMessages() => expectMessages(); diff --git a/lib/web_ui/test/engine/semantics/semantics_api_test.dart b/lib/web_ui/test/engine/semantics/semantics_api_test.dart index e56242639b44c..8576246b9c6a2 100644 --- a/lib/web_ui/test/engine/semantics/semantics_api_test.dart +++ b/lib/web_ui/test/engine/semantics/semantics_api_test.dart @@ -40,10 +40,19 @@ void testMain() { }); test('SpellOutStringAttribute.toString', () async { - expect(SpellOutStringAttribute(range: const TextRange(start: 2, end: 5)).toString(), 'SpellOutStringAttribute(TextRange(start: 2, end: 5))'); + expect( + SpellOutStringAttribute(range: const TextRange(start: 2, end: 5)).toString(), + 'SpellOutStringAttribute(TextRange(start: 2, end: 5))', + ); }); test('LocaleStringAttribute.toString', () async { - expect(LocaleStringAttribute(range: const TextRange(start: 2, end: 5), locale: const Locale('test')).toString(), 'LocaleStringAttribute(TextRange(start: 2, end: 5), test)'); + expect( + LocaleStringAttribute( + range: const TextRange(start: 2, end: 5), + locale: const Locale('test'), + ).toString(), + 'LocaleStringAttribute(TextRange(start: 2, end: 5), test)', + ); }); } diff --git a/lib/web_ui/test/engine/semantics/semantics_auto_enable_test.dart b/lib/web_ui/test/engine/semantics/semantics_auto_enable_test.dart index 00c4708027a57..5b60107bdc924 100644 --- a/lib/web_ui/test/engine/semantics/semantics_auto_enable_test.dart +++ b/lib/web_ui/test/engine/semantics/semantics_auto_enable_test.dart @@ -26,10 +26,7 @@ Future testMain() async { test('EngineSemanticsOwner auto-enables semantics on update', () async { expect(semantics().semanticsEnabled, isFalse); - expect( - EnginePlatformDispatcher - .instance.accessibilityFeatures.accessibleNavigation, - isFalse); + expect(EnginePlatformDispatcher.instance.accessibilityFeatures.accessibleNavigation, isFalse); final DomElement placeholder = domDocument.querySelector('flt-semantics-placeholder')!; @@ -41,10 +38,7 @@ Future testMain() async { tester.apply(); expect(semantics().semanticsEnabled, isTrue); - expect( - EnginePlatformDispatcher.instance.accessibilityFeatures.accessibleNavigation, - isTrue, - ); + expect(EnginePlatformDispatcher.instance.accessibilityFeatures.accessibleNavigation, isTrue); // The placeholder should be removed expect(placeholder.isConnected, isFalse); diff --git a/lib/web_ui/test/engine/semantics/semantics_helper_test.dart b/lib/web_ui/test/engine/semantics/semantics_helper_test.dart index 4e78eaea44b6d..00db89c0ecd34 100644 --- a/lib/web_ui/test/engine/semantics/semantics_helper_test.dart +++ b/lib/web_ui/test/engine/semantics/semantics_helper_test.dart @@ -26,8 +26,7 @@ void testMain() { }); tearDown(() { - expect(placeholder, isNotNull, - reason: 'Expected the test to create a placeholder'); + expect(placeholder, isNotNull, reason: 'Expected the test to create a placeholder'); placeholder!.remove(); EngineSemantics.instance.semanticsEnabled = false; }); @@ -39,8 +38,7 @@ void testMain() { domDocument.body!.append(placeholder!); - expect(domDocument.getElementsByTagName('flt-semantics-placeholder'), - isNotEmpty); + expect(domDocument.getElementsByTagName('flt-semantics-placeholder'), isNotEmpty); expect(placeholder!.getBoundingClientRect().height, 1); expect(placeholder!.getBoundingClientRect().width, 1); @@ -53,32 +51,30 @@ void testMain() { domDocument.body!.append(placeholder!); DomEvent event = createDomEvent('Event', 'mousemove'); - bool shouldForwardToFramework = - desktopSemanticsEnabler.tryEnableSemantics(event); + bool shouldForwardToFramework = desktopSemanticsEnabler.tryEnableSemantics(event); expect(shouldForwardToFramework, isTrue); // Pointer events are not defined in webkit. if (ui_web.browser.browserEngine != ui_web.BrowserEngine.webkit) { event = createDomEvent('Event', 'pointermove'); - shouldForwardToFramework = - desktopSemanticsEnabler.tryEnableSemantics(event); + shouldForwardToFramework = desktopSemanticsEnabler.tryEnableSemantics(event); expect(shouldForwardToFramework, isTrue); } }); test( - 'Relevant events targeting placeholder should not be forwarded to the framework', - () async { - final DomEvent event = createDomEvent('Event', 'mousedown'); - placeholder!.dispatchEvent(event); + 'Relevant events targeting placeholder should not be forwarded to the framework', + () async { + final DomEvent event = createDomEvent('Event', 'mousedown'); + placeholder!.dispatchEvent(event); - final bool shouldForwardToFramework = - desktopSemanticsEnabler.tryEnableSemantics(event); + final bool shouldForwardToFramework = desktopSemanticsEnabler.tryEnableSemantics(event); - expect(shouldForwardToFramework, isFalse); - }); + expect(shouldForwardToFramework, isFalse); + }, + ); test('disposes of the placeholder', () { domDocument.body!.append(placeholder!); @@ -118,12 +114,10 @@ void testMain() { expect(placeholder!.getBoundingClientRect().width, bodyWidth); }); - test('Non-relevant events should be forwarded to the framework', - () async { + test('Non-relevant events should be forwarded to the framework', () async { final DomEvent event = createDomPointerEvent('pointermove'); - final bool shouldForwardToFramework = - mobileSemanticsEnabler.tryEnableSemantics(event); + final bool shouldForwardToFramework = mobileSemanticsEnabler.tryEnableSemantics(event); expect(shouldForwardToFramework, isTrue); }); @@ -132,31 +126,24 @@ void testMain() { expect(mobileSemanticsEnabler.semanticsActivationTimer, isNull); // Send a click off center - placeholder!.dispatchEvent(createDomMouseEvent( - 'click', - { - 'clientX': 0, - 'clientY': 0, - } - )); + placeholder!.dispatchEvent( + createDomMouseEvent('click', {'clientX': 0, 'clientY': 0}), + ); expect(mobileSemanticsEnabler.semanticsActivationTimer, isNull); // Send a click at center - final DomRect activatingElementRect = - placeholder!.getBoundingClientRect(); - final int midX = (activatingElementRect.left + - (activatingElementRect.right - activatingElementRect.left) / 2) - .toInt(); - final int midY = (activatingElementRect.top + - (activatingElementRect.bottom - activatingElementRect.top) / 2) - .toInt(); - placeholder!.dispatchEvent(createDomMouseEvent( - 'click', - { - 'clientX': midX, - 'clientY': midY, - } - )); + final DomRect activatingElementRect = placeholder!.getBoundingClientRect(); + final int midX = + (activatingElementRect.left + + (activatingElementRect.right - activatingElementRect.left) / 2) + .toInt(); + final int midY = + (activatingElementRect.top + + (activatingElementRect.bottom - activatingElementRect.top) / 2) + .toInt(); + placeholder!.dispatchEvent( + createDomMouseEvent('click', {'clientX': midX, 'clientY': midY}), + ); expect(mobileSemanticsEnabler.semanticsActivationTimer, isNotNull); }); }, diff --git a/lib/web_ui/test/engine/semantics/semantics_multi_view_test.dart b/lib/web_ui/test/engine/semantics/semantics_multi_view_test.dart index fc38923e6e8fc..676c41d8b2110 100644 --- a/lib/web_ui/test/engine/semantics/semantics_multi_view_test.dart +++ b/lib/web_ui/test/engine/semantics/semantics_multi_view_test.dart @@ -42,8 +42,14 @@ Future testMain() async { // Check that we have both root nodes in the DOM (root nodes have id == 0) expect(domDocument.querySelectorAll('flutter-view'), hasLength(2)); - expect(domDocument.querySelectorAll('flutter-view[flt-view-id="${view1.viewId}"]'), hasLength(1)); - expect(domDocument.querySelectorAll('flutter-view[flt-view-id="${view2.viewId}"]'), hasLength(1)); + expect( + domDocument.querySelectorAll('flutter-view[flt-view-id="${view1.viewId}"]'), + hasLength(1), + ); + expect( + domDocument.querySelectorAll('flutter-view[flt-view-id="${view2.viewId}"]'), + hasLength(1), + ); expect(domDocument.querySelectorAll('flt-semantics[id=flt-semantic-node-0]'), hasLength(2)); // Check that each is attached to its own view @@ -51,8 +57,14 @@ Future testMain() async { expect(view2.semantics.semanticsHost, view2.dom.semanticsHost); // Check semantics - expectSemanticsTree(view1.semantics, ''); - expectSemanticsTree(view2.semantics, ''); + expectSemanticsTree( + view1.semantics, + '', + ); + expectSemanticsTree( + view2.semantics, + '', + ); // Add some children tester1.updateNode( @@ -66,7 +78,7 @@ Future testMain() async { isEnabled: true, isButton: true, rect: const ui.Rect.fromLTRB(0, 0, 100, 50), - ) + ), ], ); tester1.apply(); @@ -85,18 +97,13 @@ Future testMain() async { tester2.apply(); // Test that each view renders its own semantics tree. - expectSemanticsTree( - view1.semantics, - ''' + expectSemanticsTree(view1.semantics, ''' -''', - ); - expectSemanticsTree( - view2.semantics, - ''' +'''); + expectSemanticsTree(view2.semantics, ''' diff --git a/lib/web_ui/test/engine/semantics/semantics_placeholder_enable_test.dart b/lib/web_ui/test/engine/semantics/semantics_placeholder_enable_test.dart index b72726ed5736d..fe0eb7eca81d6 100644 --- a/lib/web_ui/test/engine/semantics/semantics_placeholder_enable_test.dart +++ b/lib/web_ui/test/engine/semantics/semantics_placeholder_enable_test.dart @@ -31,10 +31,12 @@ Future testMain() async { expect(placeholder.isConnected, isTrue); final DomRect rect = placeholder.getBoundingClientRect(); - placeholder.dispatchEvent(createDomMouseEvent('click', { - 'clientX': (rect.left + (rect.right - rect.left) / 2).floor(), - 'clientY': (rect.top + (rect.bottom - rect.top) / 2).floor(), - })); + placeholder.dispatchEvent( + createDomMouseEvent('click', { + 'clientX': (rect.left + (rect.right - rect.left) / 2).floor(), + 'clientY': (rect.top + (rect.bottom - rect.top) / 2).floor(), + }), + ); // On mobile semantics is enabled asynchronously. if (isMobile) { diff --git a/lib/web_ui/test/engine/semantics/semantics_test.dart b/lib/web_ui/test/engine/semantics/semantics_test.dart index 104fd56505685..c7e94c3fbfee7 100644 --- a/lib/web_ui/test/engine/semantics/semantics_test.dart +++ b/lib/web_ui/test/engine/semantics/semantics_test.dart @@ -25,8 +25,7 @@ EngineSemanticsOwner owner() => EnginePlatformDispatcher.instance.implicitView!. DomElement get platformViewsHost => EnginePlatformDispatcher.instance.implicitView!.dom.platformViewsHost; -DomElement get flutterViewRoot => - EnginePlatformDispatcher.instance.implicitView!.dom.rootElement; +DomElement get flutterViewRoot => EnginePlatformDispatcher.instance.implicitView!.dom.rootElement; void main() { internalBootstrapBrowserTest(() { @@ -134,10 +133,7 @@ void _testSemanticRole() { final SemanticsTester tester = SemanticsTester(owner()); tester.updateNode( id: 0, - children: [ - tester.updateNode(id: 372), - tester.updateNode(id: 599), - ], + children: [tester.updateNode(id: 372), tester.updateNode(id: 599)], ); tester.apply(); @@ -196,11 +192,7 @@ void _testRoleLifecycle() { // Check that roles are initialized immediately { final SemanticsTester tester = SemanticsTester(owner()); - tester.updateNode( - id: 0, - isButton: true, - rect: const ui.Rect.fromLTRB(0, 0, 100, 50), - ); + tester.updateNode(id: 0, isButton: true, rect: const ui.Rect.fromLTRB(0, 0, 100, 50)); tester.apply(); tester.expectSemantics(''); @@ -242,8 +234,7 @@ void _testRoleLifecycle() { } void _testEngineAccessibilityBuilder() { - final EngineAccessibilityFeaturesBuilder builder = - EngineAccessibilityFeaturesBuilder(0); + final EngineAccessibilityFeaturesBuilder builder = EngineAccessibilityFeaturesBuilder(0); EngineAccessibilityFeatures features = builder.build(); test('accessible navigation', () { @@ -318,16 +309,18 @@ void _testEngineSemanticsOwner() { // // test('places accessibility announcements in the tag', () { - final AccessibilityAnnouncements accessibilityAnnouncements = semantics().accessibilityAnnouncements; - final DomElement politeElement = accessibilityAnnouncements.ariaLiveElementFor(Assertiveness.polite); - final DomElement assertiveElement = accessibilityAnnouncements.ariaLiveElementFor(Assertiveness.assertive); + final AccessibilityAnnouncements accessibilityAnnouncements = + semantics().accessibilityAnnouncements; + final DomElement politeElement = accessibilityAnnouncements.ariaLiveElementFor( + Assertiveness.polite, + ); + final DomElement assertiveElement = accessibilityAnnouncements.ariaLiveElementFor( + Assertiveness.assertive, + ); final DomElement announcementHost = politeElement.parent!; // Polite and assertive elements share the same host. - expect( - assertiveElement.parent, - announcementHost, - ); + expect(assertiveElement.parent, announcementHost); // The host is a direct child of expect(announcementHost.parent, domDocument.body); @@ -335,8 +328,7 @@ void _testEngineSemanticsOwner() { test('accessibilityFeatures copyWith function works', () { const EngineAccessibilityFeatures original = EngineAccessibilityFeatures(0); - EngineAccessibilityFeatures copy = - original.copyWith(accessibleNavigation: true); + EngineAccessibilityFeatures copy = original.copyWith(accessibleNavigation: true); expect(copy.accessibleNavigation, true); expect(copy.boldText, false); expect(copy.disableAnimations, false); @@ -406,25 +398,22 @@ void _testEngineSemanticsOwner() { ..semanticsEnabled = true; final SemanticsTester tester = SemanticsTester(owner()); - tester.updateNode( - id: 0, - label: 'I am root', - rect: const ui.Rect.fromLTRB(0, 0, 100, 50), - ); + tester.updateNode(id: 0, label: 'I am root', rect: const ui.Rect.fromLTRB(0, 0, 100, 50)); tester.apply(); - expectSemanticsTree( - owner(), - ''' + expectSemanticsTree(owner(), ''' I am root -''', - ); +'''); semantics().semanticsEnabled = false; }); - void renderSemantics({String? label, String? tooltip, Set flags = const {}}) { + void renderSemantics({ + String? label, + String? tooltip, + Set flags = const {}, + }) { int flagValues = 0; for (final ui.SemanticsFlag flag in flags) { flagValues = flagValues | flag.index; @@ -517,7 +506,7 @@ void _testEngineSemanticsOwner() { '''); // Update - renderSemantics(label: 'Hello', flags: { ui.SemanticsFlag.isLink }); + renderSemantics(label: 'Hello', flags: {ui.SemanticsFlag.isLink}); tree = owner().debugSemanticsTree!; expect(tree.length, 2); @@ -616,14 +605,10 @@ void _testEngineSemanticsOwner() { semantics().receiveGlobalEvent(pointerEvent); // Verify the interactions. - expect( - mockSemanticsEnabler.shouldEnableSemanticsEvents, - [pointerEvent], - ); + expect(mockSemanticsEnabler.shouldEnableSemanticsEvents, [pointerEvent]); }); - test('forwards events to framework if shouldEnableSemantics returns true', - () { + test('forwards events to framework if shouldEnableSemantics returns true', () { final MockSemanticsEnabler mockSemanticsEnabler = MockSemanticsEnabler(); semantics().semanticsHelper.semanticsEnabler = mockSemanticsEnabler; final DomEvent pointerEvent = createDomEvent('Event', 'pointermove'); @@ -636,19 +621,13 @@ void _testEngineSemanticsOwner() { ..debugOverrideTimestampFunction(() => _testTime) ..semanticsEnabled = true; - expect( - reason: 'Should start in idle phase', - owner().phase, - SemanticsUpdatePhase.idle, - ); + expect(reason: 'Should start in idle phase', owner().phase, SemanticsUpdatePhase.idle); - void pumpSemantics({ required String label }) { + void pumpSemantics({required String label}) { final SemanticsTester tester = SemanticsTester(owner()); tester.updateNode( id: 0, - children: [ - tester.updateNode(id: 1, label: label), - ], + children: [tester.updateNode(id: 1, label: label)], ); tester.apply(); } @@ -682,19 +661,14 @@ void _testEngineSemanticsOwner() { expect( reason: 'While updating must be in SemanticsUpdatePhase.updating phase', mockRole.log, - [ - (method: 'update', phase: SemanticsUpdatePhase.updating), - ], + [(method: 'update', phase: SemanticsUpdatePhase.updating)], ); semantics().semanticsEnabled = false; }); } -typedef MockRoleLogEntry = ({ - String method, - SemanticsUpdatePhase phase, -}); +typedef MockRoleLogEntry = ({String method, SemanticsUpdatePhase phase}); class MockRole extends SemanticRole { MockRole(super.role, super.semanticsObject) : super.blank(); @@ -702,10 +676,7 @@ class MockRole extends SemanticRole { final List log = []; void _log(String method) { - log.add(( - method: method, - phase: semanticsObject.owner.phase, - )); + log.add((method: method, phase: semanticsObject.owner.phase)); } @override @@ -748,25 +719,28 @@ class MockSemanticsEnabler implements SemanticsEnabler { } void _testHeader() { - test('renders an empty labeled header as a heading with a label and uses a sized span for label', () { - semantics() - ..debugOverrideTimestampFunction(() => _testTime) - ..semanticsEnabled = true; + test( + 'renders an empty labeled header as a heading with a label and uses a sized span for label', + () { + semantics() + ..debugOverrideTimestampFunction(() => _testTime) + ..semanticsEnabled = true; - final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder(); - updateNode( - builder, - flags: 0 | ui.SemanticsFlag.isHeader.index, - label: 'Header of the page', - transform: Matrix4.identity().toFloat64(), - rect: const ui.Rect.fromLTRB(0, 0, 100, 50), - ); + final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder(); + updateNode( + builder, + flags: 0 | ui.SemanticsFlag.isHeader.index, + label: 'Header of the page', + transform: Matrix4.identity().toFloat64(), + rect: const ui.Rect.fromLTRB(0, 0, 100, 50), + ); - owner().updateSemantics(builder.build()); - expectSemanticsTree(owner(), '

Header of the page

'); + owner().updateSemantics(builder.build()); + expectSemanticsTree(owner(), '

Header of the page

'); - semantics().semanticsEnabled = false; - }); + semantics().semanticsEnabled = false; + }, + ); // This is a useless case, but we should at least not crash if it happens. test('renders an empty unlabeled header', () { @@ -863,8 +837,7 @@ void _testLongestIncreasingSubsequence() { }); test('longest in a jagged pattern', () { - expectLis( - [0, 1, -1, 2, -2, 3, -3, 4, -4, 5, -5], [0, 1, 3, 5, 7, 9]); + expectLis([0, 1, -1, 2, -2, 3, -3, 4, -4, 5, -5], [0, 1, 3, 5, 7, 9]); }); test('fully sorted up', () { @@ -878,10 +851,7 @@ void _testLongestIncreasingSubsequence() { test('fully sorted down', () { for (int count = 1; count < 100; count += 1) { - expectLis( - List.generate(count, (int i) => 10 * (count - i)), - [count - 1], - ); + expectLis(List.generate(count, (int i) => 10 * (count - i)), [count - 1]); } }); } @@ -893,29 +863,19 @@ void _testText() { ..semanticsEnabled = true; final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder(); - updateNode( - builder, - label: 'plain text', - rect: const ui.Rect.fromLTRB(0, 0, 100, 50), - ); + updateNode(builder, label: 'plain text', rect: const ui.Rect.fromLTRB(0, 0, 100, 50)); owner().updateSemantics(builder.build()); - expectSemanticsTree( - owner(), - '''plain text''', - ); + expectSemanticsTree(owner(), '''plain text'''); final SemanticsObject node = owner().debugSemanticsTree![0]!; expect(node.semanticRole?.kind, SemanticRoleKind.generic); - expect( - node.semanticRole!.behaviors!.map((m) => m.runtimeType).toList(), - [ - Focusable, - LiveRegion, - RouteName, - LabelAndValue, - ], - ); + expect(node.semanticRole!.behaviors!.map((m) => m.runtimeType).toList(), [ + Focusable, + LiveRegion, + RouteName, + LabelAndValue, + ]); semantics().semanticsEnabled = false; }); @@ -933,114 +893,59 @@ void _testText() { ); tester.apply(); - expectSemanticsTree( - owner(), - '''tappable text''', - ); + expectSemanticsTree(owner(), '''tappable text'''); final SemanticsObject node = owner().debugSemanticsTree![0]!; expect(node.semanticRole?.kind, SemanticRoleKind.generic); - expect( - node.semanticRole!.behaviors!.map((m) => m.runtimeType).toList(), - [ - Focusable, - LiveRegion, - RouteName, - LabelAndValue, - Tappable, - ], - ); + expect(node.semanticRole!.behaviors!.map((m) => m.runtimeType).toList(), [ + Focusable, + LiveRegion, + RouteName, + LabelAndValue, + Tappable, + ]); semantics().semanticsEnabled = false; }); } void _testLabels() { test('computeDomSemanticsLabel combines tooltip, label, value, and hint', () { - expect( - computeDomSemanticsLabel(tooltip: 'tooltip'), - 'tooltip', - ); - expect( - computeDomSemanticsLabel(label: 'label'), - 'label', - ); - expect( - computeDomSemanticsLabel(value: 'value'), - 'value', - ); - expect( - computeDomSemanticsLabel(hint: 'hint'), - 'hint', - ); + expect(computeDomSemanticsLabel(tooltip: 'tooltip'), 'tooltip'); + expect(computeDomSemanticsLabel(label: 'label'), 'label'); + expect(computeDomSemanticsLabel(value: 'value'), 'value'); + expect(computeDomSemanticsLabel(hint: 'hint'), 'hint'); expect( computeDomSemanticsLabel(tooltip: 'tooltip', label: 'label', hint: 'hint', value: 'value'), ''' tooltip -label hint value''' +label hint value''', ); - expect( - computeDomSemanticsLabel(tooltip: 'tooltip', hint: 'hint', value: 'value'), - ''' + expect(computeDomSemanticsLabel(tooltip: 'tooltip', hint: 'hint', value: 'value'), ''' tooltip -hint value''' - ); - expect( - computeDomSemanticsLabel(tooltip: 'tooltip', label: 'label', value: 'value'), - ''' +hint value'''); + expect(computeDomSemanticsLabel(tooltip: 'tooltip', label: 'label', value: 'value'), ''' tooltip -label value''' - ); - expect( - computeDomSemanticsLabel(tooltip: 'tooltip', label: 'label', hint: 'hint'), - ''' +label value'''); + expect(computeDomSemanticsLabel(tooltip: 'tooltip', label: 'label', hint: 'hint'), ''' tooltip -label hint''' - ); +label hint'''); }); test('computeDomSemanticsLabel collapses empty labels to null', () { - expect( - computeDomSemanticsLabel(), - isNull, - ); - expect( - computeDomSemanticsLabel(tooltip: ''), - isNull, - ); - expect( - computeDomSemanticsLabel(label: ''), - isNull, - ); - expect( - computeDomSemanticsLabel(value: ''), - isNull, - ); - expect( - computeDomSemanticsLabel(hint: ''), - isNull, - ); - expect( - computeDomSemanticsLabel(tooltip: '', label: '', hint: '', value: ''), - isNull, - ); - expect( - computeDomSemanticsLabel(tooltip: '', hint: '', value: ''), - isNull, - ); - expect( - computeDomSemanticsLabel(tooltip: '', label: '', value: ''), - isNull, - ); - expect( - computeDomSemanticsLabel(tooltip: '', label: '', hint: ''), - isNull, - ); + expect(computeDomSemanticsLabel(), isNull); + expect(computeDomSemanticsLabel(tooltip: ''), isNull); + expect(computeDomSemanticsLabel(label: ''), isNull); + expect(computeDomSemanticsLabel(value: ''), isNull); + expect(computeDomSemanticsLabel(hint: ''), isNull); + expect(computeDomSemanticsLabel(tooltip: '', label: '', hint: '', value: ''), isNull); + expect(computeDomSemanticsLabel(tooltip: '', hint: '', value: ''), isNull); + expect(computeDomSemanticsLabel(tooltip: '', label: '', value: ''), isNull); + expect(computeDomSemanticsLabel(tooltip: '', label: '', hint: ''), isNull); }); } void _testContainer() { - test('container node has no transform when there is no rect offset', - () async { + test('container node has no transform when there is no rect offset', () async { semantics() ..debugOverrideTimestampFunction(() => _testTime) ..semanticsEnabled = true; @@ -1054,12 +959,7 @@ void _testContainer() { childrenInHitTestOrder: Int32List.fromList([1]), childrenInTraversalOrder: Int32List.fromList([1]), ); - updateNode( - builder, - id: 1, - transform: Matrix4.identity().toFloat64(), - rect: zeroOffsetRect, - ); + updateNode(builder, id: 1, transform: Matrix4.identity().toFloat64(), rect: zeroOffsetRect); owner().updateSemantics(builder.build()); expectSemanticsTree(owner(), ''' @@ -1069,10 +969,8 @@ void _testContainer() { '''); - final DomElement parentElement = - owner().semanticsHost.querySelector('flt-semantics')!; - final DomElement container = - owner().semanticsHost.querySelector('flt-semantics-container')!; + final DomElement parentElement = owner().semanticsHost.querySelector('flt-semantics')!; + final DomElement container = owner().semanticsHost.querySelector('flt-semantics-container')!; if (isMacOrIOS) { expect(parentElement.style.top, '0px'); @@ -1120,15 +1018,16 @@ void _testContainer() { '''); - final DomElement parentElement = - owner().semanticsHost.querySelector('flt-semantics')!; - final DomElement container = - owner().semanticsHost.querySelector('flt-semantics-container')!; + final DomElement parentElement = owner().semanticsHost.querySelector('flt-semantics')!; + final DomElement container = owner().semanticsHost.querySelector('flt-semantics-container')!; expect(parentElement.style.transform, 'matrix(1, 0, 0, 1, 10, 10)'); if (isSafari) { // macOS 13 returns different values than macOS 12. - expect(parentElement.style.transformOrigin, anyOf(contains('0px 0px 0px'), contains('0px 0px'))); + expect( + parentElement.style.transformOrigin, + anyOf(contains('0px 0px 0px'), contains('0px 0px')), + ); } else { expect(parentElement.style.transformOrigin, '0px 0px 0px'); } @@ -1165,10 +1064,8 @@ void _testContainer() { '''); - final DomElement parentElement = - owner().semanticsHost.querySelector('flt-semantics')!; - final DomElement container = - owner().semanticsHost.querySelector('flt-semantics-container')!; + final DomElement parentElement = owner().semanticsHost.querySelector('flt-semantics')!; + final DomElement container = owner().semanticsHost.querySelector('flt-semantics-container')!; if (isMacOrIOS) { expect(parentElement.style.top, '0px'); @@ -1189,8 +1086,7 @@ void _testContainer() { semantics().semanticsEnabled = false; }); - test('renders in traversal order, hit-tests in reverse z-index order', - () async { + test('renders in traversal order, hit-tests in reverse z-index order', () async { semantics() ..debugOverrideTimestampFunction(() => _testTime) ..semanticsEnabled = true; @@ -1283,9 +1179,7 @@ void _testContainer() { semantics().semanticsEnabled = false; }); - test( - 'container nodes are transparent and leaf children are opaque hit-test wise', - () async { + test('container nodes are transparent and leaf children are opaque hit-test wise', () async { semantics() ..debugOverrideTimestampFunction(() => _testTime) ..semanticsEnabled = true; @@ -1311,12 +1205,10 @@ void _testContainer() { final DomElement root = owner().semanticsHost.querySelector('#flt-semantic-node-0')!; expect(root.style.pointerEvents, 'none'); - final DomElement child1 = - owner().semanticsHost.querySelector('#flt-semantic-node-1')!; + final DomElement child1 = owner().semanticsHost.querySelector('#flt-semantic-node-1')!; expect(child1.style.pointerEvents, 'all'); - final DomElement child2 = - owner().semanticsHost.querySelector('#flt-semantic-node-2')!; + final DomElement child2 = owner().semanticsHost.querySelector('#flt-semantic-node-2')!; expect(child2.style.pointerEvents, 'all'); semantics().semanticsEnabled = false; @@ -1431,7 +1323,10 @@ void _testContainer() { '''); - expect(owner().debugSemanticsTree!.keys.toList(), unorderedEquals([0, 1, 2, 3, 4, 5, 6])); + expect( + owner().debugSemanticsTree!.keys.toList(), + unorderedEquals([0, 1, 2, 3, 4, 5, 6]), + ); } // Remove node #2 => expect nodes #2 and #5 to be removed and #6 reparented. @@ -1555,9 +1450,7 @@ void _testVerticalScrolling() { final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder(); updateNode( builder, - actions: 0 | - ui.SemanticsAction.scrollUp.index | - ui.SemanticsAction.scrollDown.index, + actions: 0 | ui.SemanticsAction.scrollUp.index | ui.SemanticsAction.scrollDown.index, transform: Matrix4.identity().toFloat64(), rect: const ui.Rect.fromLTRB(0, 0, 50, 100), childrenInHitTestOrder: Int32List.fromList([1, 2, 3]), @@ -1706,9 +1599,7 @@ void _testHorizontalScrolling() { final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder(); updateNode( builder, - actions: 0 | - ui.SemanticsAction.scrollLeft.index | - ui.SemanticsAction.scrollRight.index, + actions: 0 | ui.SemanticsAction.scrollLeft.index | ui.SemanticsAction.scrollRight.index, transform: Matrix4.identity().toFloat64(), rect: const ui.Rect.fromLTRB(0, 0, 100, 50), childrenInHitTestOrder: Int32List.fromList([1, 2, 3]), @@ -1879,9 +1770,7 @@ void _testIncrementables() { final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder(); updateNode( builder, - actions: 0 | - ui.SemanticsAction.decrease.index | - ui.SemanticsAction.increase.index, + actions: 0 | ui.SemanticsAction.decrease.index | ui.SemanticsAction.increase.index, value: 'd', increasedValue: 'e', decreasedValue: 'c', @@ -1903,7 +1792,7 @@ void _testIncrementables() { ..debugOverrideTimestampFunction(() => _testTime) ..semanticsEnabled = true; - void pumpSemantics({ required bool isFocused }) { + void pumpSemantics({required bool isFocused}) { final SemanticsTester tester = SemanticsTester(owner()); tester.updateNode( id: 0, @@ -1941,18 +1830,12 @@ void _testIncrementables() { expect( reason: 'Browser-initiated focus even should be communicated to the framework.', capturedActions, - [ - (0, ui.SemanticsAction.focus, null), - ], + [(0, ui.SemanticsAction.focus, null)], ); capturedActions.clear(); pumpSemantics(isFocused: false); - expect( - reason: 'The engine never calls blur() explicitly.', - capturedActions, - isEmpty, - ); + expect(reason: 'The engine never calls blur() explicitly.', capturedActions, isEmpty); // The web doesn't send didLoseAccessibilityFocus as on the web, // accessibility focus is not observable, only input focus is. As of this @@ -1988,7 +1871,6 @@ void _testTextField() { '''); - final SemanticsObject node = owner().debugSemanticsTree![0]!; final SemanticTextField textFieldRole = node.semanticRole! as SemanticTextField; final DomHTMLInputElement inputElement = textFieldRole.editableElement as DomHTMLInputElement; @@ -2021,7 +1903,8 @@ void _testCheckables() { builder, actions: 0 | ui.SemanticsAction.tap.index, label: 'test label', - flags: 0 | + flags: + 0 | ui.SemanticsFlag.isEnabled.index | ui.SemanticsFlag.hasEnabledState.index | ui.SemanticsFlag.hasToggledState.index | @@ -2056,7 +1939,8 @@ void _testCheckables() { updateNode( builder, actions: 0 | ui.SemanticsAction.tap.index, - flags: 0 | + flags: + 0 | ui.SemanticsFlag.hasToggledState.index | ui.SemanticsFlag.isToggled.index | ui.SemanticsFlag.hasEnabledState.index, @@ -2081,7 +1965,8 @@ void _testCheckables() { updateNode( builder, actions: 0 | ui.SemanticsAction.tap.index, - flags: 0 | + flags: + 0 | ui.SemanticsFlag.hasToggledState.index | ui.SemanticsFlag.isEnabled.index | ui.SemanticsFlag.hasEnabledState.index, @@ -2106,7 +1991,8 @@ void _testCheckables() { updateNode( builder, actions: 0 | ui.SemanticsAction.tap.index, - flags: 0 | + flags: + 0 | ui.SemanticsFlag.isEnabled.index | ui.SemanticsFlag.hasEnabledState.index | ui.SemanticsFlag.hasCheckedState.index | @@ -2132,7 +2018,8 @@ void _testCheckables() { updateNode( builder, actions: 0 | ui.SemanticsAction.tap.index, - flags: 0 | + flags: + 0 | ui.SemanticsFlag.hasCheckedState.index | ui.SemanticsFlag.hasEnabledState.index | ui.SemanticsFlag.isChecked.index, @@ -2157,7 +2044,8 @@ void _testCheckables() { updateNode( builder, actions: 0 | ui.SemanticsAction.tap.index, - flags: 0 | + flags: + 0 | ui.SemanticsFlag.hasCheckedState.index | ui.SemanticsFlag.isEnabled.index | ui.SemanticsFlag.hasEnabledState.index, @@ -2182,7 +2070,8 @@ void _testCheckables() { updateNode( builder, actions: 0 | ui.SemanticsAction.tap.index, - flags: 0 | + flags: + 0 | ui.SemanticsFlag.isEnabled.index | ui.SemanticsFlag.hasEnabledState.index | ui.SemanticsFlag.hasCheckedState.index | @@ -2209,7 +2098,8 @@ void _testCheckables() { updateNode( builder, actions: 0 | ui.SemanticsAction.tap.index, - flags: 0 | + flags: + 0 | ui.SemanticsFlag.hasEnabledState.index | ui.SemanticsFlag.hasCheckedState.index | ui.SemanticsFlag.isInMutuallyExclusiveGroup.index | @@ -2235,7 +2125,8 @@ void _testCheckables() { updateNode( builder, actions: 0 | ui.SemanticsAction.tap.index, - flags: 0 | + flags: + 0 | ui.SemanticsFlag.isEnabled.index | ui.SemanticsFlag.hasEnabledState.index | ui.SemanticsFlag.hasCheckedState.index | @@ -2257,7 +2148,7 @@ void _testCheckables() { ..debugOverrideTimestampFunction(() => _testTime) ..semanticsEnabled = true; - void pumpSemantics({ required bool isFocused }) { + void pumpSemantics({required bool isFocused}) { final SemanticsTester tester = SemanticsTester(owner()); tester.updateNode( id: 0, @@ -2305,9 +2196,7 @@ void _testCheckables() { expect( reason: 'Browser-initiated focus even should be communicated to the framework.', capturedActions, - [ - (0, ui.SemanticsAction.focus, null), - ], + [(0, ui.SemanticsAction.focus, null)], ); capturedActions.clear(); @@ -2326,11 +2215,7 @@ void _testSelectables() { id: 0, rect: const ui.Rect.fromLTRB(0, 0, 100, 60), children: [ - tester.updateNode( - id: 1, - isSelectable: false, - rect: const ui.Rect.fromLTRB(0, 0, 100, 20), - ), + tester.updateNode(id: 1, isSelectable: false, rect: const ui.Rect.fromLTRB(0, 0, 100, 20)), tester.updateNode( id: 2, isSelectable: true, @@ -2406,17 +2291,11 @@ void _testSelectables() { ); tester.apply(); - expectSemanticsTree( - owner(), - '', - ); + expectSemanticsTree(owner(), ''); final node = owner().debugSemanticsTree![0]!; expect(node.semanticRole!.kind, SemanticRoleKind.checkable); - expect( - node.semanticRole!.debugSemanticBehaviorTypes, - isNot(contains(Selectable)), - ); + expect(node.semanticRole!.debugSemanticBehaviorTypes, isNot(contains(Selectable))); expect(node.element.getAttribute('aria-selected'), isNull); semantics().semanticsEnabled = false; @@ -2447,10 +2326,7 @@ void _testTappable() { final SemanticsObject node = owner().debugSemanticsTree![0]!; expect(node.semanticRole?.kind, SemanticRoleKind.button); - expect( - node.semanticRole?.debugSemanticBehaviorTypes, - containsAll([Focusable, Tappable]), - ); + expect(node.semanticRole?.debugSemanticBehaviorTypes, containsAll([Focusable, Tappable])); expect(tester.getSemanticsObject(0).element.tabIndex, 0); semantics().semanticsEnabled = false; @@ -2465,9 +2341,7 @@ void _testTappable() { updateNode( builder, actions: 0 | ui.SemanticsAction.tap.index, - flags: 0 | - ui.SemanticsFlag.hasEnabledState.index | - ui.SemanticsFlag.isButton.index, + flags: 0 | ui.SemanticsFlag.hasEnabledState.index | ui.SemanticsFlag.isButton.index, transform: Matrix4.identity().toFloat64(), rect: const ui.Rect.fromLTRB(0, 0, 100, 50), ); @@ -2499,28 +2373,16 @@ void _testTappable() { } updateTappable(enabled: false); - expectSemanticsTree( - owner(), - '' - ); + expectSemanticsTree(owner(), ''); updateTappable(enabled: true); - expectSemanticsTree( - owner(), - '', - ); + expectSemanticsTree(owner(), ''); updateTappable(enabled: false); - expectSemanticsTree( - owner(), - '', - ); + expectSemanticsTree(owner(), ''); updateTappable(enabled: true); - expectSemanticsTree( - owner(), - '', - ); + expectSemanticsTree(owner(), ''); semantics().semanticsEnabled = false; }); @@ -2552,7 +2414,7 @@ void _testTappable() { ..debugOverrideTimestampFunction(() => _testTime) ..semanticsEnabled = true; - void pumpSemantics({ required bool isFocused }) { + void pumpSemantics({required bool isFocused}) { final SemanticsTester tester = SemanticsTester(owner()); tester.updateNode( id: 0, @@ -2601,9 +2463,7 @@ void _testTappable() { expect( reason: 'Browser-initiated focus even should be communicated to the framework.', capturedActions, - [ - (0, ui.SemanticsAction.focus, null), - ], + [(0, ui.SemanticsAction.focus, null)], ); capturedActions.clear(); @@ -2664,14 +2524,14 @@ void _testTappable() { final DomElement element = tester.getSemanticsObject(0).element; final DomRect rect = element.getBoundingClientRect(); - element.dispatchEvent(createDomMouseEvent('click', { - 'clientX': (rect.left + (rect.right - rect.left) / 2).floor(), - 'clientY': (rect.top + (rect.bottom - rect.top) / 2).floor(), - })); + element.dispatchEvent( + createDomMouseEvent('click', { + 'clientX': (rect.left + (rect.right - rect.left) / 2).floor(), + 'clientY': (rect.top + (rect.bottom - rect.top) / 2).floor(), + }), + ); - expect(capturedActions, [ - (0, ui.SemanticsAction.tap, null), - ]); + expect(capturedActions, [(0, ui.SemanticsAction.tap, null)]); } // Tap on the inner element @@ -2680,17 +2540,17 @@ void _testTappable() { final DomElement element = tester.getSemanticsObject(1).element; final DomRect rect = element.getBoundingClientRect(); - element.dispatchEvent(createDomMouseEvent('click', { - 'bubbles': true, - 'clientX': (rect.left + (rect.right - rect.left) / 2).floor(), - 'clientY': (rect.top + (rect.bottom - rect.top) / 2).floor(), - })); + element.dispatchEvent( + createDomMouseEvent('click', { + 'bubbles': true, + 'clientX': (rect.left + (rect.right - rect.left) / 2).floor(), + 'clientY': (rect.top + (rect.bottom - rect.top) / 2).floor(), + }), + ); // The click on the inner element should not propagate to the parent to // avoid sending a second SemanticsAction.tap action to the framework. - expect(capturedActions, [ - (1, ui.SemanticsAction.tap, null), - ]); + expect(capturedActions, [(1, ui.SemanticsAction.tap, null)]); } semantics().semanticsEnabled = false; @@ -2768,10 +2628,7 @@ void _testImage() { ); owner().updateSemantics(builder.build()); - expectSemanticsTree( - owner(), - '', - ); + expectSemanticsTree(owner(), ''); semantics().semanticsEnabled = false; }); @@ -2820,14 +2677,12 @@ class MockAccessibilityAnnouncements implements AccessibilityAnnouncements { @override DomHTMLElement ariaLiveElementFor(Assertiveness assertiveness) { - throw UnsupportedError( - 'ariaLiveElementFor is not supported in MockAccessibilityAnnouncements'); + throw UnsupportedError('ariaLiveElementFor is not supported in MockAccessibilityAnnouncements'); } @override void handleMessage(StandardMessageCodec codec, ByteData? data) { - throw UnsupportedError( - 'handleMessage is not supported in MockAccessibilityAnnouncements!'); + throw UnsupportedError('handleMessage is not supported in MockAccessibilityAnnouncements!'); } } @@ -2925,31 +2780,17 @@ void _testPlatformView() { // Set. { final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder(); - updateNode( - builder, - platformViewId: 5, - rect: const ui.Rect.fromLTRB(0, 0, 100, 50), - ); + updateNode(builder, platformViewId: 5, rect: const ui.Rect.fromLTRB(0, 0, 100, 50)); owner().updateSemantics(builder.build()); - expectSemanticsTree( - owner(), - '', - ); + expectSemanticsTree(owner(), ''); } // Update. { final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder(); - updateNode( - builder, - platformViewId: 42, - rect: const ui.Rect.fromLTRB(0, 0, 100, 50), - ); + updateNode(builder, platformViewId: 42, rect: const ui.Rect.fromLTRB(0, 0, 100, 50)); owner().updateSemantics(builder.build()); - expectSemanticsTree( - owner(), - '', - ); + expectSemanticsTree(owner(), ''); } semantics().semanticsEnabled = false; @@ -2961,17 +2802,10 @@ void _testPlatformView() { ..semanticsEnabled = true; final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder(); - updateNode( - builder, - platformViewId: 5, - rect: const ui.Rect.fromLTRB(0, 0, 100, 50), - ); + updateNode(builder, platformViewId: 5, rect: const ui.Rect.fromLTRB(0, 0, 100, 50)); owner().updateSemantics(builder.build()); - expectSemanticsTree( - owner(), - '', - ); + expectSemanticsTree(owner(), ''); final DomElement element = owner().semanticsHost.querySelector('flt-semantics')!; expect(element.style.pointerEvents, 'none'); @@ -3009,34 +2843,28 @@ void _testPlatformView() { ui_web.platformViewRegistry.registerViewFactory( 'test-platform-view', - (int viewId) => createDomHTMLDivElement() - ..id = 'view-0' - ..style.width = '100%' - ..style.height = '100%', + (int viewId) => + createDomHTMLDivElement() + ..id = 'view-0' + ..style.width = '100%' + ..style.height = '100%', ); await createPlatformView(0, 'test-platform-view'); final ui.SceneBuilder sceneBuilder = ui.SceneBuilder(); - sceneBuilder.addPlatformView( - 0, - offset: const ui.Offset(0, 15), - width: 20, - height: 30, - ); + sceneBuilder.addPlatformView(0, offset: const ui.Offset(0, 15), width: 20, height: 30); await renderScene(sceneBuilder.build()); final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder(); final double dpr = EngineFlutterDisplay.instance.devicePixelRatio; - updateNode(builder, - rect: const ui.Rect.fromLTRB(0, 0, 20, 60), - childrenInTraversalOrder: Int32List.fromList([1, 2, 3]), - childrenInHitTestOrder: Int32List.fromList([1, 2, 3]), - transform: Float64List.fromList(Matrix4.diagonal3Values(dpr, dpr, 1).storage)); updateNode( builder, - id: 1, - rect: const ui.Rect.fromLTRB(0, 0, 20, 25), + rect: const ui.Rect.fromLTRB(0, 0, 20, 60), + childrenInTraversalOrder: Int32List.fromList([1, 2, 3]), + childrenInHitTestOrder: Int32List.fromList([1, 2, 3]), + transform: Float64List.fromList(Matrix4.diagonal3Values(dpr, dpr, 1).storage), ); + updateNode(builder, id: 1, rect: const ui.Rect.fromLTRB(0, 0, 20, 25)); updateNode( builder, id: 2, @@ -3044,11 +2872,7 @@ void _testPlatformView() { rect: const ui.Rect.fromLTRB(0, 15, 20, 45), platformViewId: 0, ); - updateNode( - builder, - id: 3, - rect: const ui.Rect.fromLTRB(0, 35, 20, 60), - ); + updateNode(builder, id: 3, rect: const ui.Rect.fromLTRB(0, 35, 20, 60)); owner().updateSemantics(builder.build()); expectSemanticsTree(owner(), ''' @@ -3063,8 +2887,7 @@ void _testPlatformView() { final DomElement root = owner().semanticsHost.querySelector('#flt-semantic-node-0')!; expect(root.style.pointerEvents, 'none'); - final DomElement child1 = - owner().semanticsHost.querySelector('#flt-semantic-node-1')!; + final DomElement child1 = owner().semanticsHost.querySelector('#flt-semantic-node-1')!; expect(child1.style.pointerEvents, 'all'); final DomRect child1Rect = child1.getBoundingClientRect(); expect(child1Rect.left, 0); @@ -3072,8 +2895,7 @@ void _testPlatformView() { expect(child1Rect.right, 20); expect(child1Rect.bottom, 25); - final DomElement child2 = - owner().semanticsHost.querySelector('#flt-semantic-node-2')!; + final DomElement child2 = owner().semanticsHost.querySelector('#flt-semantic-node-2')!; expect(child2.style.pointerEvents, 'none'); final DomRect child2Rect = child2.getBoundingClientRect(); expect(child2Rect.left, 0); @@ -3081,8 +2903,7 @@ void _testPlatformView() { expect(child2Rect.right, 20); expect(child2Rect.bottom, 45); - final DomElement child3 = - owner().semanticsHost.querySelector('#flt-semantic-node-3')!; + final DomElement child3 = owner().semanticsHost.querySelector('#flt-semantic-node-3')!; expect(child3.style.pointerEvents, 'all'); final DomRect child3Rect = child3.getBoundingClientRect(); expect(child3Rect.left, 0); @@ -3090,10 +2911,8 @@ void _testPlatformView() { expect(child3Rect.right, 20); expect(child3Rect.bottom, 60); - final DomElement platformViewElement = - platformViewsHost.querySelector('#view-0')!; - final DomRect platformViewRect = - platformViewElement.getBoundingClientRect(); + final DomElement platformViewElement = platformViewsHost.querySelector('#view-0')!; + final DomRect platformViewRect = platformViewElement.getBoundingClientRect(); expect(platformViewRect.left, 0); expect(platformViewRect.top, 15); expect(platformViewRect.right, 20); @@ -3191,10 +3010,7 @@ void _testRoute() { '''); - expect( - owner().debugSemanticsTree![0]!.semanticRole?.kind, - SemanticRoleKind.route, - ); + expect(owner().debugSemanticsTree![0]!.semanticRole?.kind, SemanticRoleKind.route); semantics().semanticsEnabled = false; }); @@ -3224,22 +3040,16 @@ void _testRoute() { ); owner().updateSemantics(builder.build()); - expect( - warnings, - [ - 'Semantic node 0 had both scopesRoute and namesRoute set, indicating a self-labelled route, but it is missing the label. A route should be labelled either by setting namesRoute on itself and providing a label, or by containing a child node with namesRoute that can describe it with its content.', - ], - ); + expect(warnings, [ + 'Semantic node 0 had both scopesRoute and namesRoute set, indicating a self-labelled route, but it is missing the label. A route should be labelled either by setting namesRoute on itself and providing a label, or by containing a child node with namesRoute that can describe it with its content.', + ]); // But still sets the dialog role. expectSemanticsTree(owner(), ''' '''); - expect( - owner().debugSemanticsTree![0]!.semanticRole?.kind, - SemanticRoleKind.route, - ); + expect(owner().debugSemanticsTree![0]!.semanticRole?.kind, SemanticRoleKind.route); semantics().semanticsEnabled = false; }); @@ -3249,7 +3059,7 @@ void _testRoute() { ..debugOverrideTimestampFunction(() => _testTime) ..semanticsEnabled = true; - void pumpSemantics({ required String label }) { + void pumpSemantics({required String label}) { final SemanticsTester tester = SemanticsTester(owner()); tester.updateNode( id: 0, @@ -3259,11 +3069,7 @@ void _testRoute() { tester.updateNode( id: 1, children: [ - tester.updateNode( - id: 2, - namesRoute: true, - label: label, - ), + tester.updateNode(id: 2, namesRoute: true, label: label), ], ), ], @@ -3285,14 +3091,8 @@ void _testRoute() { pumpSemantics(label: 'Route label'); - expect( - owner().debugSemanticsTree![0]!.semanticRole?.kind, - SemanticRoleKind.route, - ); - expect( - owner().debugSemanticsTree![2]!.semanticRole?.kind, - SemanticRoleKind.generic, - ); + expect(owner().debugSemanticsTree![0]!.semanticRole?.kind, SemanticRoleKind.route); + expect(owner().debugSemanticsTree![2]!.semanticRole?.kind, SemanticRoleKind.generic); expect( owner().debugSemanticsTree![2]!.semanticRole?.debugSemanticBehaviorTypes, contains(RouteName), @@ -3309,25 +3109,15 @@ void _testRoute() { ..semanticsEnabled = true; final SemanticsTester tester = SemanticsTester(owner()); - tester.updateNode( - id: 0, - scopesRoute: true, - transform: Matrix4.identity().toFloat64(), - ); + tester.updateNode(id: 0, scopesRoute: true, transform: Matrix4.identity().toFloat64()); tester.apply(); expectSemanticsTree(owner(), ''' '''); - expect( - owner().debugSemanticsTree![0]!.semanticRole?.kind, - SemanticRoleKind.route, - ); - expect( - owner().debugSemanticsTree![0]!.semanticRole?.behaviors, - isNot(contains(RouteName)), - ); + expect(owner().debugSemanticsTree![0]!.semanticRole?.kind, SemanticRoleKind.route); + expect(owner().debugSemanticsTree![0]!.semanticRole?.behaviors, isNot(contains(RouteName))); semantics().semanticsEnabled = false; }); @@ -3345,11 +3135,7 @@ void _testRoute() { tester.updateNode( id: 1, children: [ - tester.updateNode( - id: 2, - namesRoute: true, - label: 'Hello', - ), + tester.updateNode(id: 2, namesRoute: true, label: 'Hello'), ], ), ], @@ -3368,10 +3154,7 @@ void _testRoute() { '''); - expect( - owner().debugSemanticsTree![0]!.semanticRole?.kind, - SemanticRoleKind.generic, - ); + expect(owner().debugSemanticsTree![0]!.semanticRole?.kind, SemanticRoleKind.generic); expect( owner().debugSemanticsTree![2]!.semanticRole?.debugSemanticBehaviorTypes, contains(RouteName), @@ -3518,11 +3301,7 @@ void _testRoute() { tester.updateNode( id: 1, children: [ - tester.updateNode( - id: 2, - label: 'Heading', - rect: const ui.Rect.fromLTRB(0, 0, 100, 50), - ), + tester.updateNode(id: 2, label: 'Heading', rect: const ui.Rect.fromLTRB(0, 0, 100, 50)), tester.updateNode( id: 3, label: 'Click me!', @@ -3580,11 +3359,7 @@ void _testRoute() { }; final SemanticsTester tester = SemanticsTester(owner()); - tester.updateNode( - id: 0, - scopesRoute: true, - transform: Matrix4.identity().toFloat64(), - ); + tester.updateNode(id: 0, scopesRoute: true, transform: Matrix4.identity().toFloat64()); tester.apply(); expect(capturedActions, isEmpty); @@ -3667,7 +3442,9 @@ void _testFocusable() { expect(domDocument.activeElement, element); expect( reason: 'Nothing should be sent to the framework on focus re-request.', - capturedActions, isEmpty); + capturedActions, + isEmpty, + ); capturedActions.clear(); // Blur and emulate browser requesting focus @@ -3675,18 +3452,17 @@ void _testFocusable() { expect(domDocument.activeElement, isNot(element)); element.focusWithoutScroll(); expect(domDocument.activeElement, element); - expect(capturedActions, [ - (1, ui.SemanticsAction.focus, null), - ]); + expect(capturedActions, [(1, ui.SemanticsAction.focus, null)]); capturedActions.clear(); // Stop managing manager.stopManaging(); pumpSemantics(); // triggers post-update callbacks expect( - reason: 'There should be no notification to the framework because the ' - 'framework should already know. Otherwise, it would not have ' - 'asked to stop managing the node.', + reason: + 'There should be no notification to the framework because the ' + 'framework should already know. Otherwise, it would not have ' + 'asked to stop managing the node.', capturedActions, isEmpty, ); @@ -3697,8 +3473,9 @@ void _testFocusable() { manager.changeFocus(true); pumpSemantics(); // triggers post-update callbacks expect( - reason: 'Attempting to request focus on a node that is not managed should ' - 'not result in any notifications to the framework.', + reason: + 'Attempting to request focus on a node that is not managed should ' + 'not result in any notifications to the framework.', capturedActions, isEmpty, ); @@ -3739,14 +3516,8 @@ void _testFocusable() { final SemanticsObject node = owner().debugSemanticsTree![1]!; expect(node.isFocusable, isTrue); - expect( - node.semanticRole?.kind, - SemanticRoleKind.generic, - ); - expect( - node.semanticRole?.debugSemanticBehaviorTypes, - contains(Focusable), - ); + expect(node.semanticRole?.kind, SemanticRoleKind.generic); + expect(node.semanticRole?.debugSemanticBehaviorTypes, contains(Focusable)); final DomElement element = node.element; expect(domDocument.activeElement, isNot(element)); @@ -3777,11 +3548,7 @@ void _testLink() { SemanticsObject pumpSemantics() { final SemanticsTester tester = SemanticsTester(owner()); - tester.updateNode( - id: 0, - isLink: true, - rect: const ui.Rect.fromLTRB(0, 0, 100, 50), - ); + tester.updateNode(id: 0, isLink: true, rect: const ui.Rect.fromLTRB(0, 0, 100, 50)); tester.apply(); return tester.getSemanticsObject(0); } @@ -3842,11 +3609,9 @@ void updateNode( String value = '', List valueAttributes = const [], String increasedValue = '', - List increasedValueAttributes = - const [], + List increasedValueAttributes = const [], String decreasedValue = '', - List decreasedValueAttributes = - const [], + List decreasedValueAttributes = const [], String tooltip = '', ui.TextDirection textDirection = ui.TextDirection.ltr, Float64List? transform, @@ -3906,13 +3671,7 @@ 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, - }, - )), + codec.encodeMethodCall(MethodCall('create', {'id': id, 'viewType': viewType})), (dynamic _) => completer.complete(), ); return completer.future; diff --git a/lib/web_ui/test/engine/semantics/semantics_tester.dart b/lib/web_ui/test/engine/semantics/semantics_tester.dart index f003a7b27f151..dc424a6f49ab1 100644 --- a/lib/web_ui/test/engine/semantics/semantics_tester.dart +++ b/lib/web_ui/test/engine/semantics/semantics_tester.dart @@ -354,21 +354,16 @@ class SemanticsTester { /// Verifies the HTML structure of the current semantics tree. void expectSemanticsTree(EngineSemanticsOwner owner, String semanticsHtml) { - expect( - owner.semanticsHost.children.single, - hasHtml(semanticsHtml), - ); + expect(owner.semanticsHost.children.single, hasHtml(semanticsHtml)); } /// Finds the first HTML element in the semantics tree used for scrolling. DomElement findScrollable(EngineSemanticsOwner owner) { - return owner.semanticsHost.querySelectorAll('flt-semantics').singleWhere( - (DomElement? element) { - return element!.style.overflow == 'hidden' || + return owner.semanticsHost.querySelectorAll('flt-semantics').singleWhere((DomElement? element) { + return element!.style.overflow == 'hidden' || element.style.overflowY == 'scroll' || element.style.overflowX == 'scroll'; - }, - ); + }); } /// Logs semantics actions dispatched to [ui.PlatformDispatcher]. @@ -384,8 +379,7 @@ class SemanticsActionLogger { // fired. final Zone testZone = Zone.current; - ui.PlatformDispatcher.instance.onSemanticsActionEvent = - (ui.SemanticsActionEvent event) { + ui.PlatformDispatcher.instance.onSemanticsActionEvent = (ui.SemanticsActionEvent event) { _idLogController.add(event.nodeId); _actionLogController.add(event.type); testZone.run(() { @@ -408,5 +402,6 @@ class SemanticsActionLogger { extension SemanticRoleExtension on SemanticRole { /// Types of semantics behaviors used by this role. - List get debugSemanticBehaviorTypes => behaviors?.map((behavior) => behavior.runtimeType).toList() ?? const []; + List get debugSemanticBehaviorTypes => + behaviors?.map((behavior) => behavior.runtimeType).toList() ?? const []; } diff --git a/lib/web_ui/test/engine/semantics/semantics_text_test.dart b/lib/web_ui/test/engine/semantics/semantics_text_test.dart index 8c4a97f8f73de..0141621662947 100644 --- a/lib/web_ui/test/engine/semantics/semantics_text_test.dart +++ b/lib/web_ui/test/engine/semantics/semantics_text_test.dart @@ -43,8 +43,7 @@ Future testMain() async { tester.apply(); expectSemanticsTree(owner(), ''' - Hello''' - ); + Hello'''); final SemanticsObject node = owner().debugSemanticsTree![0]!; expect(node.semanticRole?.kind, SemanticRoleKind.generic); @@ -67,8 +66,7 @@ Future testMain() async { tester.apply(); expectSemanticsTree(owner(), ''' - World''' - ); + World'''); } // Empty the label - expect the to be removed. @@ -115,8 +113,7 @@ Future testMain() async { I am a child - ''' - ); + '''); semantics().semanticsEnabled = false; }); @@ -138,8 +135,7 @@ Future testMain() async { tester.apply(); expectSemanticsTree(owner(), ''' - I am a leaf''' - ); + I am a leaf'''); } // Add a child - expect to be removed from the parent. @@ -166,8 +162,7 @@ Future testMain() async { I am a child - ''' - ); + '''); } // Remove the child - expect the to be readded to the former parent. @@ -182,8 +177,7 @@ Future testMain() async { tester.apply(); expectSemanticsTree(owner(), ''' - I am a leaf again''' - ); + I am a leaf again'''); } semantics().semanticsEnabled = false; @@ -204,8 +198,7 @@ Future testMain() async { tester.apply(); expectSemanticsTree(owner(), ''' - Hello''' - ); + Hello'''); final SemanticsObject node = owner().debugSemanticsTree![0]!; final DomElement span = node.element.querySelector('span')!; @@ -240,8 +233,7 @@ Future testMain() async { lav.update(); expectSemanticsTree(owner(), ''' - Hello''' - ); + Hello'''); expect(node.element.getAttribute('tabindex'), isNull); node.semanticRole!.focusAsRouteDefault(); @@ -273,8 +265,7 @@ Future testMain() async { lav.update(); expectSemanticsTree(owner(), ''' - ''' - ); + '''); expect(node.element.getAttribute('tabindex'), isNull); node.semanticRole!.focusAsRouteDefault(); @@ -301,8 +292,7 @@ Future testMain() async { expectSemanticsTree(owner(), ''' Ignore pointer events - ''' - ); + '''); semantics().semanticsEnabled = false; }); diff --git a/lib/web_ui/test/engine/semantics/text_field_test.dart b/lib/web_ui/test/engine/semantics/text_field_test.dart index 458e4f1ed4685..82270feb5c156 100644 --- a/lib/web_ui/test/engine/semantics/text_field_test.dart +++ b/lib/web_ui/test/engine/semantics/text_field_test.dart @@ -14,8 +14,7 @@ import 'package:ui/ui_web/src/ui_web.dart' as ui_web; import '../../common/test_initialization.dart'; import 'semantics_tester.dart'; -final InputConfiguration singlelineConfig = - InputConfiguration(viewId: kImplicitViewId); +final InputConfiguration singlelineConfig = InputConfiguration(viewId: kImplicitViewId); final InputConfiguration multilineConfig = InputConfiguration( viewId: kImplicitViewId, @@ -58,8 +57,7 @@ void testMain() { value: 'hi', isFocused: true, ); - final SemanticTextField textField = - textFieldSemantics.semanticRole! as SemanticTextField; + final SemanticTextField textField = textFieldSemantics.semanticRole! as SemanticTextField; // ensureInitialized() isn't called prior to calling dispose() here. // Since we are conditionally calling dispose() on our @@ -93,10 +91,7 @@ void testMain() { test('renders a text field', () { createTextFieldSemantics(value: 'hello'); - expectSemanticsTree( - owner(), - '', - ); + expectSemanticsTree(owner(), ''); // TODO(yjbanov): this used to attempt to test that value="hello" but the // test was a false positive. We should revise this test and @@ -113,10 +108,7 @@ void testMain() { test('renders a password field', () { createTextFieldSemantics(value: 'secret', isObscured: true); - expectSemanticsTree( - owner(), - '', - ); + expectSemanticsTree(owner(), ''); final node = owner().debugSemanticsTree![0]!; final textFieldRole = node.semanticRole! as SemanticTextField; @@ -134,17 +126,14 @@ void testMain() { expect(inputElement.disabled, isTrue); }); - test('sends a SemanticsAction.focus action when browser requests focus', - () async { + test('sends a SemanticsAction.focus action when browser requests focus', () async { final logger = SemanticsActionLogger(); createTextFieldSemantics(value: 'hello'); - final textField = owner() - .semanticsHost - .querySelector('input[data-semantics-role="text-field"]')!; + final textField = + owner().semanticsHost.querySelector('input[data-semantics-role="text-field"]')!; - expect( - owner().semanticsHost.ownerDocument?.activeElement, isNot(textField)); + expect(owner().semanticsHost.ownerDocument?.activeElement, isNot(textField)); textField.focusWithoutScroll(); @@ -154,13 +143,11 @@ void testMain() { textField.blur(); - expect( - owner().semanticsHost.ownerDocument?.activeElement, isNot(textField)); + expect(owner().semanticsHost.ownerDocument?.activeElement, isNot(textField)); }); test('Syncs semantic state from framework', () async { - expect( - owner().semanticsHost.ownerDocument?.activeElement, domDocument.body); + expect(owner().semanticsHost.ownerDocument?.activeElement, domDocument.body); int changeCount = 0; int actionCount = 0; @@ -183,8 +170,7 @@ void testMain() { ); final textField = textFieldSemantics.semanticRole! as SemanticTextField; - expect(owner().semanticsHost.ownerDocument?.activeElement, - strategy.domElement); + expect(owner().semanticsHost.ownerDocument?.activeElement, strategy.domElement); expect(textField.editableElement, strategy.domElement); expect(textField.editableElement.getAttribute('aria-label'), 'greeting'); expect(textField.editableElement.style.width, '10px'); @@ -203,10 +189,7 @@ void testMain() { // readers. However, if another element gains focus (e.g. because the // framework focuses on a different widget), then the current element will // be blurred automatically, without needing to call DomElement.blur(). - expect( - owner().semanticsHost.ownerDocument?.activeElement, - strategy.domElement, - ); + expect(owner().semanticsHost.ownerDocument?.activeElement, strategy.domElement); expect(textField.editableElement.getAttribute('aria-label'), 'farewell'); expect(textField.editableElement.style.width, '12px'); expect(textField.editableElement.style.height, '17px'); @@ -218,16 +201,14 @@ void testMain() { // DefaultTextEditingStrategy.scheduleFocusFlutterView, which uses a timer // before shifting focus. So initially the editable DOM element should be // in place, and is cleared after the timer fires. - expect( - owner().semanticsHost.ownerDocument?.activeElement, - textField.editableElement, - ); + expect(owner().semanticsHost.ownerDocument?.activeElement, textField.editableElement); await Future.delayed(Duration.zero); expect( owner().semanticsHost.ownerDocument?.activeElement, flutterView.dom.rootElement, - reason: 'Focus should be returned to the root element of the Flutter view ' - 'after housekeeping DOM operations (blur/remove)', + reason: + 'Focus should be returned to the root element of the Flutter view ' + 'after housekeeping DOM operations (blur/remove)', ); // There was no user interaction with the element, @@ -236,14 +217,8 @@ void testMain() { expect(actionCount, 0); }); - test( - 'Does not overwrite text value and selection editing state on semantic updates', - () { - strategy.enable( - singlelineConfig, - onChange: (_, __) {}, - onAction: (_) {}, - ); + test('Does not overwrite text value and selection editing state on semantic updates', () { + strategy.enable(singlelineConfig, onChange: (_, __) {}, onAction: (_) {}); final textFieldSemantics = createTextFieldSemantics( value: 'hello', @@ -264,17 +239,10 @@ void testMain() { strategy.disable(); }); - test( - 'Updates editing state when receiving framework messages from the text input channel', - () { - expect( - owner().semanticsHost.ownerDocument?.activeElement, domDocument.body); + test('Updates editing state when receiving framework messages from the text input channel', () { + expect(owner().semanticsHost.ownerDocument?.activeElement, domDocument.body); - strategy.enable( - singlelineConfig, - onChange: (_, __) {}, - onAction: (_) {}, - ); + strategy.enable(singlelineConfig, onChange: (_, __) {}, onAction: (_) {}); final textFieldSemantics = createTextFieldSemantics( value: 'hello', @@ -294,14 +262,12 @@ void testMain() { expect(editableElement.selectionEnd, 0); // Update from framework - const setEditingState = - MethodCall('TextInput.setEditingState', { + const setEditingState = MethodCall('TextInput.setEditingState', { 'text': 'updated', 'selectionBase': 2, 'selectionExtent': 3, }); - sendFrameworkMessage( - codec.encodeMethodCall(setEditingState), testTextEditing); + sendFrameworkMessage(codec.encodeMethodCall(setEditingState), testTextEditing); // Editing state should now be updated expect(editableElement.value, 'updated'); @@ -312,50 +278,31 @@ void testMain() { }); test('Gives up focus after DOM blur', () { - expect( - owner().semanticsHost.ownerDocument?.activeElement, domDocument.body); + expect(owner().semanticsHost.ownerDocument?.activeElement, domDocument.body); - strategy.enable( - singlelineConfig, - onChange: (_, __) {}, - onAction: (_) {}, - ); - final textFieldSemantics = createTextFieldSemantics( - value: 'hello', - isFocused: true, - ); + strategy.enable(singlelineConfig, onChange: (_, __) {}, onAction: (_) {}); + final textFieldSemantics = createTextFieldSemantics(value: 'hello', isFocused: true); final textField = textFieldSemantics.semanticRole! as SemanticTextField; expect(textField.editableElement, strategy.domElement); - expect(owner().semanticsHost.ownerDocument?.activeElement, - strategy.domElement); + expect(owner().semanticsHost.ownerDocument?.activeElement, strategy.domElement); // The input should not refocus after blur. textField.editableElement.blur(); - expect( - owner().semanticsHost.ownerDocument?.activeElement, domDocument.body); + expect(owner().semanticsHost.ownerDocument?.activeElement, domDocument.body); strategy.disable(); }); - test('Does not dispose and recreate dom elements in persistent mode', - () async { - strategy.enable( - singlelineConfig, - onChange: (_, __) {}, - onAction: (_) {}, - ); + test('Does not dispose and recreate dom elements in persistent mode', () async { + strategy.enable(singlelineConfig, onChange: (_, __) {}, onAction: (_) {}); // It doesn't create a new DOM element. expect(strategy.domElement, isNull); // During the semantics update the DOM element is created and is focused on. - final textFieldSemantics = createTextFieldSemantics( - value: 'hello', - isFocused: true, - ); + final textFieldSemantics = createTextFieldSemantics(value: 'hello', isFocused: true); expect(strategy.domElement, isNotNull); - expect(owner().semanticsHost.ownerDocument?.activeElement, - strategy.domElement); + expect(owner().semanticsHost.ownerDocument?.activeElement, strategy.domElement); strategy.disable(); expect(strategy.domElement, isNull); @@ -369,30 +316,22 @@ void testMain() { expect( owner().semanticsHost.ownerDocument?.activeElement, flutterView.dom.rootElement, - reason: 'Focus should be returned to the root element of the Flutter view ' - 'after housekeeping DOM operations (blur/remove)', + reason: + 'Focus should be returned to the root element of the Flutter view ' + 'after housekeeping DOM operations (blur/remove)', ); }); test('Refocuses when setting editing state', () { - strategy.enable( - singlelineConfig, - onChange: (_, __) {}, - onAction: (_) {}, - ); + strategy.enable(singlelineConfig, onChange: (_, __) {}, onAction: (_) {}); - createTextFieldSemantics( - value: 'hello', - isFocused: true, - ); + createTextFieldSemantics(value: 'hello', isFocused: true); expect(strategy.domElement, isNotNull); - expect(owner().semanticsHost.ownerDocument?.activeElement, - strategy.domElement); + expect(owner().semanticsHost.ownerDocument?.activeElement, strategy.domElement); // Blur the element without telling the framework. strategy.activeDomElement.blur(); - expect( - owner().semanticsHost.ownerDocument?.activeElement, domDocument.body); + expect(owner().semanticsHost.ownerDocument?.activeElement, domDocument.body); // The input will have focus after editing state is set and semantics updated. strategy.setEditingState(EditingState(text: 'foo')); @@ -406,46 +345,25 @@ void testMain() { // createTextFieldSemantics. However, this is something for us to // keep in mind in case this causes issues in the future. - createTextFieldSemantics( - value: 'hello', - isFocused: true, - ); - expect(owner().semanticsHost.ownerDocument?.activeElement, - strategy.domElement); + createTextFieldSemantics(value: 'hello', isFocused: true); + expect(owner().semanticsHost.ownerDocument?.activeElement, strategy.domElement); strategy.disable(); }); test('Works in multi-line mode', () { - strategy.enable( - multilineConfig, - onChange: (_, __) {}, - onAction: (_) {}, - ); - createTextFieldSemantics( - value: 'hello', - isFocused: true, - isMultiline: true, - ); + strategy.enable(multilineConfig, onChange: (_, __) {}, onAction: (_) {}); + createTextFieldSemantics(value: 'hello', isFocused: true, isMultiline: true); final textArea = strategy.domElement! as DomHTMLTextAreaElement; - expect( - textArea.style.getPropertyValue('-webkit-text-security'), - '', - ); + expect(textArea.style.getPropertyValue('-webkit-text-security'), ''); - expect(owner().semanticsHost.ownerDocument?.activeElement, - strategy.domElement); + expect(owner().semanticsHost.ownerDocument?.activeElement, strategy.domElement); - strategy.enable( - singlelineConfig, - onChange: (_, __) {}, - onAction: (_) {}, - ); + strategy.enable(singlelineConfig, onChange: (_, __) {}, onAction: (_) {}); textArea.blur(); - expect( - owner().semanticsHost.ownerDocument?.activeElement, domDocument.body); + expect(owner().semanticsHost.ownerDocument?.activeElement, domDocument.body); strategy.disable(); // It doesn't remove the textarea from the DOM. @@ -455,11 +373,7 @@ void testMain() { }); test('multi-line and obscured', () { - strategy.enable( - multilineConfig, - onChange: (_, __) {}, - onAction: (_) {}, - ); + strategy.enable(multilineConfig, onChange: (_, __) {}, onAction: (_) {}); createTextFieldSemantics( value: 'hello', isFocused: true, @@ -477,11 +391,7 @@ void testMain() { }, skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.firefox); test('Does not position or size its DOM element', () { - strategy.enable( - singlelineConfig, - onChange: (_, __) {}, - onAction: (_) {}, - ); + strategy.enable(singlelineConfig, onChange: (_, __) {}, onAction: (_) {}); // Send width and height that are different from semantics values on // purpose. @@ -496,10 +406,7 @@ void testMain() { () {}, ); - createTextFieldSemantics( - value: 'hello', - isFocused: true, - ); + createTextFieldSemantics(value: 'hello', isFocused: true); // Checks that the placement attributes come from semantics and not from // EditableTextGeometry. @@ -514,8 +421,10 @@ void testMain() { checkPlacementIsSetBySemantics(); }); - Map createTwoFieldSemantics(SemanticsTester builder, - {int? focusFieldId}) { + Map createTwoFieldSemantics( + SemanticsTester builder, { + int? focusFieldId, + }) { builder.updateNode( id: 0, children: [ @@ -540,14 +449,8 @@ void testMain() { return builder.apply(); } - test( - 'Changes focus from one text field to another through a semantics update', - () { - strategy.enable( - singlelineConfig, - onChange: (_, __) {}, - onAction: (_) {}, - ); + test('Changes focus from one text field to another through a semantics update', () { + strategy.enable(singlelineConfig, onChange: (_, __) {}, onAction: (_) {}); // Switch between the two fields a few times. for (int i = 0; i < 5; i++) { @@ -555,14 +458,18 @@ void testMain() { createTwoFieldSemantics(tester, focusFieldId: 1); expect(tester.apply().length, 3); - expect(owner().semanticsHost.ownerDocument?.activeElement, - tester.getTextField(1).editableElement); + expect( + owner().semanticsHost.ownerDocument?.activeElement, + tester.getTextField(1).editableElement, + ); expect(strategy.domElement, tester.getTextField(1).editableElement); createTwoFieldSemantics(tester, focusFieldId: 2); expect(tester.apply().length, 3); - expect(owner().semanticsHost.ownerDocument?.activeElement, - tester.getTextField(2).editableElement); + expect( + owner().semanticsHost.ownerDocument?.activeElement, + tester.getTextField(2).editableElement, + ); expect(strategy.domElement, tester.getTextField(2).editableElement); } }); @@ -582,25 +489,25 @@ SemanticsObject createTextFieldSemantics({ }) { final tester = SemanticsTester(owner()); tester.updateNode( - id: 0, - isEnabled: isEnabled, - label: label, - value: value, - isTextField: true, - isFocused: isFocused, - isMultiline: isMultiline, - isObscured: isObscured, - hasTap: true, - rect: rect, - textDirection: ui.TextDirection.ltr, - textSelectionBase: textSelectionBase, - textSelectionExtent: textSelectionExtent); + id: 0, + isEnabled: isEnabled, + label: label, + value: value, + isTextField: true, + isFocused: isFocused, + isMultiline: isMultiline, + isObscured: isObscured, + hasTap: true, + rect: rect, + textDirection: ui.TextDirection.ltr, + textSelectionBase: textSelectionBase, + textSelectionExtent: textSelectionExtent, + ); tester.apply(); return tester.getSemanticsObject(0); } /// Emulates sending of a message by the framework to the engine. -void sendFrameworkMessage( - ByteData? message, HybridTextEditing testTextEditing) { +void sendFrameworkMessage(ByteData? message, HybridTextEditing testTextEditing) { testTextEditing.channel.handleTextInput(message, (ByteData? data) {}); } diff --git a/lib/web_ui/test/engine/services/serialization_test.dart b/lib/web_ui/test/engine/services/serialization_test.dart index 4234f7eef4512..fbb893b323c74 100644 --- a/lib/web_ui/test/engine/services/serialization_test.dart +++ b/lib/web_ui/test/engine/services/serialization_test.dart @@ -73,8 +73,7 @@ void testMain() { expect(read.getInt64List(3), equals(integers)); }, skip: !_kSupportsDartNumerics); test('of double list when unaligned', () { - final Float64List doubles = - Float64List.fromList([3.14, double.nan]); + final Float64List doubles = Float64List.fromList([3.14, double.nan]); final WriteBuffer write = WriteBuffer(); write.putUint8(9); write.putFloat64List(doubles); diff --git a/lib/web_ui/test/engine/surface/filters/image_filter_test.dart b/lib/web_ui/test/engine/surface/filters/image_filter_test.dart index 0a843cb30fb76..9fc236b65b4d9 100644 --- a/lib/web_ui/test/engine/surface/filters/image_filter_test.dart +++ b/lib/web_ui/test/engine/surface/filters/image_filter_test.dart @@ -59,9 +59,21 @@ void testMain() { }); test('blur tests all values on ==', () { - final ImageFilter filter1 = ImageFilter.blur(sigmaX: 2.0, sigmaY: 2.0, tileMode: TileMode.decal); - final ImageFilter filter2 = ImageFilter.blur(sigmaX: 2.0, sigmaY: 3.0, tileMode: TileMode.decal); - final ImageFilter filter3 = ImageFilter.blur(sigmaX: 2.0, sigmaY: 2.0, tileMode: TileMode.mirror); + final ImageFilter filter1 = ImageFilter.blur( + sigmaX: 2.0, + sigmaY: 2.0, + tileMode: TileMode.decal, + ); + final ImageFilter filter2 = ImageFilter.blur( + sigmaX: 2.0, + sigmaY: 3.0, + tileMode: TileMode.decal, + ); + final ImageFilter filter3 = ImageFilter.blur( + sigmaX: 2.0, + sigmaY: 2.0, + tileMode: TileMode.mirror, + ); expect(filter1, filter1); expect(filter1, isNot(equals(filter2))); diff --git a/lib/web_ui/test/engine/text_editing_test.dart b/lib/web_ui/test/engine/text_editing_test.dart index 41b88887bf68a..be8d0c715d2a0 100644 --- a/lib/web_ui/test/engine/text_editing_test.dart +++ b/lib/web_ui/test/engine/text_editing_test.dart @@ -22,11 +22,9 @@ const MethodCodec codec = JSONMethodCodec(); EnginePlatformDispatcher get dispatcher => EnginePlatformDispatcher.instance; -DomElement get defaultTextEditingRoot => - dispatcher.implicitView!.dom.textEditingHost; +DomElement get defaultTextEditingRoot => dispatcher.implicitView!.dom.textEditingHost; -DomElement get implicitViewRootElement => - dispatcher.implicitView!.dom.rootElement; +DomElement get implicitViewRootElement => dispatcher.implicitView!.dom.rootElement; /// Add unit tests for [FirefoxTextEditingStrategy]. // TODO(mdebbar): https://github.com/flutter/flutter/issues/46891 @@ -36,19 +34,15 @@ EditingState? lastEditingState; TextEditingDeltaState? editingDeltaState; String? lastInputAction; -final InputConfiguration singlelineConfig = InputConfiguration( - viewId: kImplicitViewId, -); -final Map flutterSinglelineConfig = - createFlutterConfig('text'); +final InputConfiguration singlelineConfig = InputConfiguration(viewId: kImplicitViewId); +final Map flutterSinglelineConfig = createFlutterConfig('text'); final InputConfiguration multilineConfig = InputConfiguration( viewId: kImplicitViewId, inputType: EngineInputType.multiline, inputAction: 'TextInputAction.newline', ); -final Map flutterMultilineConfig = - createFlutterConfig('multiline'); +final Map flutterMultilineConfig = createFlutterConfig('multiline'); void trackEditingState(EditingState? editingState, TextEditingDeltaState? textEditingDeltaState) { lastEditingState = editingState; @@ -91,17 +85,13 @@ Future testMain() async { }); test('Creates element when enabled and removes it when disabled', () async { - expect( - domDocument.getElementsByTagName('input'), - hasLength(0), - ); + expect(domDocument.getElementsByTagName('input'), hasLength(0)); expect( domDocument.activeElement, domDocument.body, reason: 'The focus should initially be on the body', ); - expect(defaultTextEditingRoot.ownerDocument?.activeElement, - domDocument.body); + expect(defaultTextEditingRoot.ownerDocument?.activeElement, domDocument.body); editingStrategy!.enable( singlelineConfig, @@ -109,10 +99,7 @@ Future testMain() async { onAction: trackInputAction, ); - expect( - defaultTextEditingRoot.querySelectorAll('input'), - hasLength(1), - ); + expect(defaultTextEditingRoot.querySelectorAll('input'), hasLength(1)); final DomElement input = defaultTextEditingRoot.querySelector('input')!; // Now the editing element should have focus. @@ -128,14 +115,10 @@ Future testMain() async { editingStrategy!.disable(); await waitForTextStrategyStopPropagation(); - expect( - defaultTextEditingRoot.querySelectorAll('input'), - hasLength(0), - ); + expect(defaultTextEditingRoot.querySelectorAll('input'), hasLength(0)); // The focus is back to the flutter view. expect(domDocument.activeElement, implicitViewRootElement); - expect(defaultTextEditingRoot.ownerDocument?.activeElement, - implicitViewRootElement); + expect(defaultTextEditingRoot.ownerDocument?.activeElement, implicitViewRootElement); }); test('inserts element in the correct view', () async { @@ -149,11 +132,7 @@ Future testMain() async { expect(textEditingHost.getElementsByTagName('input'), hasLength(0)); final InputConfiguration config = InputConfiguration(viewId: view.viewId); - editingStrategy!.enable( - config, - onChange: trackEditingState, - onAction: trackInputAction, - ); + editingStrategy!.enable(config, onChange: trackEditingState, onAction: trackInputAction); final DomElement input = editingStrategy!.domElement!; // Input is appended to the right view. @@ -169,15 +148,8 @@ Future testMain() async { }); test('Respects read-only config', () { - final InputConfiguration config = InputConfiguration( - viewId: kImplicitViewId, - readOnly: true, - ); - editingStrategy!.enable( - config, - onChange: trackEditingState, - onAction: trackInputAction, - ); + final InputConfiguration config = InputConfiguration(viewId: kImplicitViewId, readOnly: true); + editingStrategy!.enable(config, onChange: trackEditingState, onAction: trackInputAction); expect(defaultTextEditingRoot.querySelectorAll('input'), hasLength(1)); final DomElement input = defaultTextEditingRoot.querySelector('input')!; expect(editingStrategy!.domElement, input); @@ -191,11 +163,7 @@ Future testMain() async { viewId: kImplicitViewId, obscureText: true, ); - editingStrategy!.enable( - config, - onChange: trackEditingState, - onAction: trackInputAction, - ); + editingStrategy!.enable(config, onChange: trackEditingState, onAction: trackInputAction); expect(defaultTextEditingRoot.querySelectorAll('input'), hasLength(1)); final DomElement input = defaultTextEditingRoot.querySelector('input')!; expect(editingStrategy!.domElement, input); @@ -207,17 +175,14 @@ Future testMain() async { test('Knows how to create non-default text actions', () { final InputConfiguration config = InputConfiguration( viewId: kImplicitViewId, - inputAction: 'TextInputAction.send' - ); - editingStrategy!.enable( - config, - onChange: trackEditingState, - onAction: trackInputAction, + inputAction: 'TextInputAction.send', ); + editingStrategy!.enable(config, onChange: trackEditingState, onAction: trackInputAction); expect(defaultTextEditingRoot.querySelectorAll('input'), hasLength(1)); final DomElement input = defaultTextEditingRoot.querySelector('input')!; expect(editingStrategy!.domElement, input); - if (ui_web.browser.operatingSystem == ui_web.OperatingSystem.iOs || ui_web.browser.operatingSystem == ui_web.OperatingSystem.android){ + if (ui_web.browser.operatingSystem == ui_web.OperatingSystem.iOs || + ui_web.browser.operatingSystem == ui_web.OperatingSystem.android) { expect(input.getAttribute('enterkeyhint'), 'send'); } else { expect(input.getAttribute('enterkeyhint'), null); @@ -231,11 +196,7 @@ Future testMain() async { viewId: kImplicitViewId, autocorrect: false, ); - editingStrategy!.enable( - config, - onChange: trackEditingState, - onAction: trackInputAction, - ); + editingStrategy!.enable(config, onChange: trackEditingState, onAction: trackInputAction); expect(defaultTextEditingRoot.querySelectorAll('input'), hasLength(1)); final DomElement input = defaultTextEditingRoot.querySelector('input')!; expect(editingStrategy!.domElement, input); @@ -246,11 +207,7 @@ Future testMain() async { test('Knows to turn autocorrect on', () { final InputConfiguration config = InputConfiguration(viewId: kImplicitViewId); - editingStrategy!.enable( - config, - onChange: trackEditingState, - onAction: trackInputAction, - ); + editingStrategy!.enable(config, onChange: trackEditingState, onAction: trackInputAction); expect(defaultTextEditingRoot.querySelectorAll('input'), hasLength(1)); final DomElement input = defaultTextEditingRoot.querySelector('input')!; expect(editingStrategy!.domElement, input); @@ -261,11 +218,7 @@ Future testMain() async { test('Knows to turn autofill off', () { final InputConfiguration config = InputConfiguration(viewId: kImplicitViewId); - editingStrategy!.enable( - config, - onChange: trackEditingState, - onAction: trackInputAction, - ); + editingStrategy!.enable(config, onChange: trackEditingState, onAction: trackInputAction); expect(defaultTextEditingRoot.querySelectorAll('input'), hasLength(1)); final DomElement input = defaultTextEditingRoot.querySelector('input')!; expect(editingStrategy!.domElement, input); @@ -284,17 +237,11 @@ Future testMain() async { final DomHTMLInputElement input = editingStrategy!.domElement! as DomHTMLInputElement; input.value = 'foo bar'; input.dispatchEvent(createDomEvent('Event', 'input')); - expect( - lastEditingState, - EditingState(text: 'foo bar', baseOffset: 7, extentOffset: 7), - ); + expect(lastEditingState, EditingState(text: 'foo bar', baseOffset: 7, extentOffset: 7)); input.setSelectionRange(4, 6); domDocument.dispatchEvent(createDomEvent('Event', 'selectionchange')); - expect( - lastEditingState, - EditingState(text: 'foo bar', baseOffset: 4, extentOffset: 6), - ); + expect(lastEditingState, EditingState(text: 'foo bar', baseOffset: 4, extentOffset: 6)); // There should be no input action. expect(lastInputAction, isNull); @@ -307,7 +254,8 @@ Future testMain() async { onAction: trackInputAction, ); editingStrategy!.setEditingState( - EditingState(text: 'foo bar baz', baseOffset: 2, extentOffset: 7)); + EditingState(text: 'foo bar baz', baseOffset: 2, extentOffset: 7), + ); checkInputEditingState(editingStrategy!.domElement, 'foo bar baz', 2, 7); @@ -336,14 +284,12 @@ Future testMain() async { textarea.setSelectionRange(4, 6); domDocument.dispatchEvent(createDomEvent('Event', 'selectionchange')); // Can read textarea state correctly (and preserves new lines). - expect( - lastEditingState, - EditingState(text: 'foo\nbar', baseOffset: 4, extentOffset: 6), - ); + expect(lastEditingState, EditingState(text: 'foo\nbar', baseOffset: 4, extentOffset: 6)); // Can set textarea state correctly (and preserves new lines). editingStrategy!.setEditingState( - EditingState(text: 'bar\nbaz', baseOffset: 2, extentOffset: 7)); + EditingState(text: 'bar\nbaz', baseOffset: 2, extentOffset: 7), + ); checkTextAreaEditingState(textarea, 'bar\nbaz', 2, 7); editingStrategy!.disable(); @@ -405,61 +351,46 @@ Future testMain() async { test('Triggers input action', () { final InputConfiguration config = InputConfiguration(viewId: kImplicitViewId); - editingStrategy!.enable( - config, - onChange: trackEditingState, - onAction: trackInputAction, - ); + editingStrategy!.enable(config, onChange: trackEditingState, onAction: trackInputAction); // No input action so far. expect(lastInputAction, isNull); - dispatchKeyboardEvent( - editingStrategy!.domElement!, - 'keydown', - keyCode: _kReturnKeyCode, - ); + dispatchKeyboardEvent(editingStrategy!.domElement!, 'keydown', keyCode: _kReturnKeyCode); expect(lastInputAction, 'TextInputAction.done'); }); - test('handling keyboard event prevents triggering input action', () { + test('handling keyboard event prevents triggering input action', () { final ui.PlatformMessageCallback? savedCallback = dispatcher.onPlatformMessage; bool markTextEventHandled = false; - dispatcher.onPlatformMessage = (String channel, ByteData? data, - ui.PlatformMessageResponseCallback? callback) { - final ByteData response = const JSONMessageCodec() - .encodeMessage({'handled': markTextEventHandled})!; + dispatcher.onPlatformMessage = ( + String channel, + ByteData? data, + ui.PlatformMessageResponseCallback? callback, + ) { + final ByteData response = + const JSONMessageCodec().encodeMessage({ + 'handled': markTextEventHandled, + })!; callback!(response); }; RawKeyboard.initialize(); final InputConfiguration config = InputConfiguration(viewId: kImplicitViewId); - editingStrategy!.enable( - config, - onChange: trackEditingState, - onAction: trackInputAction, - ); + editingStrategy!.enable(config, onChange: trackEditingState, onAction: trackInputAction); // No input action so far. expect(lastInputAction, isNull); markTextEventHandled = true; - dispatchKeyboardEvent( - editingStrategy!.domElement!, - 'keydown', - keyCode: _kReturnKeyCode, - ); + dispatchKeyboardEvent(editingStrategy!.domElement!, 'keydown', keyCode: _kReturnKeyCode); // Input action prevented by platform message callback. expect(lastInputAction, isNull); markTextEventHandled = false; - dispatchKeyboardEvent( - editingStrategy!.domElement!, - 'keydown', - keyCode: _kReturnKeyCode, - ); + dispatchKeyboardEvent(editingStrategy!.domElement!, 'keydown', keyCode: _kReturnKeyCode); // Input action received. expect(lastInputAction, 'TextInputAction.done'); @@ -473,11 +404,7 @@ Future testMain() async { viewId: kImplicitViewId, inputType: EngineInputType.multiline, ); - editingStrategy!.enable( - config, - onChange: trackEditingState, - onAction: trackInputAction, - ); + editingStrategy!.enable(config, onChange: trackEditingState, onAction: trackInputAction); // No input action so far. expect(lastInputAction, isNull); @@ -503,11 +430,7 @@ Future testMain() async { inputAction: 'TextInputAction.newline', inputType: EngineInputType.multilineNone, ); - editingStrategy!.enable( - config, - onChange: trackEditingState, - onAction: trackInputAction, - ); + editingStrategy!.enable(config, onChange: trackEditingState, onAction: trackInputAction); // No input action so far. expect(lastInputAction, isNull); @@ -527,11 +450,7 @@ Future testMain() async { viewId: kImplicitViewId, inputType: EngineInputType.multilineNone, ); - editingStrategy!.enable( - config, - onChange: trackEditingState, - onAction: trackInputAction, - ); + editingStrategy!.enable(config, onChange: trackEditingState, onAction: trackInputAction); // No input action so far. expect(lastInputAction, isNull); @@ -553,11 +472,7 @@ Future testMain() async { test('Triggers input action and prevent new line key event for single line field', () { // Regression test for https://github.com/flutter/flutter/issues/113559 final InputConfiguration config = InputConfiguration(viewId: kImplicitViewId); - editingStrategy!.enable( - config, - onChange: trackEditingState, - onAction: trackInputAction, - ); + editingStrategy!.enable(config, onChange: trackEditingState, onAction: trackInputAction); // No input action so far. expect(lastInputAction, isNull); @@ -585,22 +500,28 @@ Future testMain() async { expect(editingStrategy!.domElement!.style.width, ''); expect(editingStrategy!.domElement!.style.height, ''); - testTextEditing.acceptCommand(TextInputSetEditableSizeAndTransform(geometry: EditableTextGeometry( - width: 13, - height: 12, - globalTransform: Matrix4.translationValues(14, 15, 0).storage, - )), () {}); + testTextEditing.acceptCommand( + TextInputSetEditableSizeAndTransform( + geometry: EditableTextGeometry( + width: 13, + height: 12, + globalTransform: Matrix4.translationValues(14, 15, 0).storage, + ), + ), + () {}, + ); // setEditableSizeAndTransform calls placeElement, so expecting geometry to be applied. - expect(editingStrategy!.domElement!.style.transform, - 'matrix(1, 0, 0, 1, 14, 15)'); + expect(editingStrategy!.domElement!.style.transform, 'matrix(1, 0, 0, 1, 14, 15)'); expect(editingStrategy!.domElement!.style.width, '13px'); expect(editingStrategy!.domElement!.style.height, '12px'); }); test('updateElementPlacement() should not call placeElement() when in mid-composition', () { final HybridTextEditing testTextEditing = HybridTextEditing(); - final GlobalTextEditingStrategySpy editingStrategy = GlobalTextEditingStrategySpy(testTextEditing); + final GlobalTextEditingStrategySpy editingStrategy = GlobalTextEditingStrategySpy( + testTextEditing, + ); testTextEditing.debugTextEditingStrategyOverride = editingStrategy; testTextEditing.configuration = singlelineConfig; @@ -622,36 +543,44 @@ Future testMain() async { // set some composing text. editingStrategy.composingText = '뮤'; - testTextEditing.acceptCommand(TextInputSetEditableSizeAndTransform(geometry: EditableTextGeometry( - width: 13, - height: 12, - globalTransform: Matrix4.translationValues(14, 15, 0).storage, - )), () {}); + testTextEditing.acceptCommand( + TextInputSetEditableSizeAndTransform( + geometry: EditableTextGeometry( + width: 13, + height: 12, + globalTransform: Matrix4.translationValues(14, 15, 0).storage, + ), + ), + () {}, + ); // placeElement() should not be called again. expect(editingStrategy.placeElementCount, 1); // geometry should be applied. - expect(editingStrategy.domElement!.style.transform, - 'matrix(1, 0, 0, 1, 14, 15)'); + expect(editingStrategy.domElement!.style.transform, 'matrix(1, 0, 0, 1, 14, 15)'); expect(editingStrategy.domElement!.style.width, '13px'); expect(editingStrategy.domElement!.style.height, '12px'); // set composing text to null. editingStrategy.composingText = null; - testTextEditing.acceptCommand(TextInputSetEditableSizeAndTransform(geometry: EditableTextGeometry( - width: 10, - height: 10, - globalTransform: Matrix4.translationValues(11, 12, 0).storage, - )), () {}); + testTextEditing.acceptCommand( + TextInputSetEditableSizeAndTransform( + geometry: EditableTextGeometry( + width: 10, + height: 10, + globalTransform: Matrix4.translationValues(11, 12, 0).storage, + ), + ), + () {}, + ); // placeElement() should be called again. expect(editingStrategy.placeElementCount, 2); // geometry should be updated. - expect(editingStrategy.domElement!.style.transform, - 'matrix(1, 0, 0, 1, 11, 12)'); + expect(editingStrategy.domElement!.style.transform, 'matrix(1, 0, 0, 1, 11, 12)'); expect(editingStrategy.domElement!.style.width, '10px'); expect(editingStrategy.domElement!.style.height, '10px'); }); @@ -680,20 +609,17 @@ Future testMain() async { bool isMultiline = false, bool autofillEnabled = true, }) { - final MethodCall setClient = MethodCall( - 'TextInput.setClient', - [ - ++clientId, - createFlutterConfig( - inputType, - viewId: viewId, - inputAction: inputAction, - decimal: decimal, - isMultiline: isMultiline, - autofillEnabled: autofillEnabled, - ), - ], - ); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + ++clientId, + createFlutterConfig( + inputType, + viewId: viewId, + inputAction: inputAction, + decimal: decimal, + isMultiline: isMultiline, + autofillEnabled: autofillEnabled, + ), + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); const MethodCall show = MethodCall('TextInput.show'); @@ -735,8 +661,10 @@ Future testMain() async { }); test('setClient, show, setEditingState, hide', () async { - final MethodCall setClient = MethodCall( - 'TextInput.setClient', [123, flutterSinglelineConfig]); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterSinglelineConfig, + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); // Editing shouldn't have started yet. @@ -749,23 +677,23 @@ Future testMain() async { // checkInputEditingState, since on some platforms (e.g. Desktop Safari) // we don't put the input element into the DOM until we get its correct // dimensions from the framework. - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); checkInputEditingState(textEditing!.strategy.domElement, '', 0, 0); - const MethodCall setEditingState = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState = MethodCall('TextInput.setEditingState', { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); - checkInputEditingState( - textEditing!.strategy.domElement, 'abcd', 2, 3); + checkInputEditingState(textEditing!.strategy.domElement, 'abcd', 2, 3); const MethodCall hide = MethodCall('TextInput.hide'); sendFrameworkMessage(codec.encodeMethodCall(hide)); @@ -783,12 +711,13 @@ Future testMain() async { }); test('setClient, setEditingState, show, clearClient', () async { - final MethodCall setClient = MethodCall( - 'TextInput.setClient', [123, flutterSinglelineConfig]); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterSinglelineConfig, + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); - const MethodCall setEditingState = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState = MethodCall('TextInput.setEditingState', { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, @@ -808,13 +737,14 @@ Future testMain() async { // checkInputEditingState, since on some platforms (e.g. Desktop Safari) // we don't put the input element into the DOM until we get its correct // dimensions from the framework. - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); - checkInputEditingState( - textEditing!.strategy.domElement, 'abcd', 2, 3); + checkInputEditingState(textEditing!.strategy.domElement, 'abcd', 2, 3); const MethodCall clearClient = MethodCall('TextInput.clearClient'); sendFrameworkMessage(codec.encodeMethodCall(clearClient)); @@ -831,39 +761,47 @@ Future testMain() async { expect(spy.messages, isEmpty); }); - test('setClient, setEditingState, setSizeAndTransform, show - input element is put into the DOM Safari Desktop', () async { - editingStrategy = SafariDesktopTextEditingStrategy(textEditing!); - textEditing!.debugTextEditingStrategyOverride = editingStrategy; - final MethodCall setClient = MethodCall( - 'TextInput.setClient', [123, flutterSinglelineConfig]); - sendFrameworkMessage(codec.encodeMethodCall(setClient)); - - const MethodCall show = MethodCall('TextInput.show'); - sendFrameworkMessage(codec.encodeMethodCall(show)); - - // Editing shouldn't have started yet. - expect(domDocument.activeElement, domDocument.body); - - // The "setSizeAndTransform" message has to be here before we call - // checkInputEditingState, since on some platforms (e.g. Desktop Safari) - // we don't put the input element into the DOM until we get its correct - // dimensions from the framework. - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); - sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); - - const MethodCall setEditingState = - MethodCall('TextInput.setEditingState', { - 'text': 'abcd', - 'selectionBase': 2, - 'selectionExtent': 3, - }); - sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); + test( + 'setClient, setEditingState, setSizeAndTransform, show - input element is put into the DOM Safari Desktop', + () async { + editingStrategy = SafariDesktopTextEditingStrategy(textEditing!); + textEditing!.debugTextEditingStrategyOverride = editingStrategy; + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterSinglelineConfig, + ]); + sendFrameworkMessage(codec.encodeMethodCall(setClient)); + + const MethodCall show = MethodCall('TextInput.show'); + sendFrameworkMessage(codec.encodeMethodCall(show)); + + // Editing shouldn't have started yet. + expect(domDocument.activeElement, domDocument.body); + + // The "setSizeAndTransform" message has to be here before we call + // checkInputEditingState, since on some platforms (e.g. Desktop Safari) + // we don't put the input element into the DOM until we get its correct + // dimensions from the framework. + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); + sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); + + const MethodCall setEditingState = MethodCall( + 'TextInput.setEditingState', + {'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3}, + ); + sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); - expect(defaultTextEditingRoot.ownerDocument?.activeElement, - textEditing!.strategy.domElement); - }, skip: !isSafari); + expect( + defaultTextEditingRoot.ownerDocument?.activeElement, + textEditing!.strategy.domElement, + ); + }, + skip: !isSafari, + ); test('setClient, setEditingState, show, updateConfig, clearClient', () { final MethodCall setClient = MethodCall('TextInput.setClient', [ @@ -872,14 +810,11 @@ Future testMain() async { ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); - const MethodCall setEditingState = MethodCall( - 'TextInput.setEditingState', - { - 'text': 'abcd', - 'selectionBase': 2, - 'selectionExtent': 3, - }, - ); + const MethodCall setEditingState = MethodCall('TextInput.setEditingState', { + 'text': 'abcd', + 'selectionBase': 2, + 'selectionExtent': 3, + }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); const MethodCall show = MethodCall('TextInput.show'); @@ -908,12 +843,13 @@ Future testMain() async { // In all the desktop browsers we are keeping the connection // open, keep the text editing element focused if it receives a blur // event. - final MethodCall setClient = MethodCall( - 'TextInput.setClient', [123, flutterSinglelineConfig]); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterSinglelineConfig, + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); - const MethodCall setEditingState = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState = MethodCall('TextInput.setEditingState', { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, @@ -933,13 +869,14 @@ Future testMain() async { // checkInputEditingState, since on some platforms (e.g. Desktop Safari) // we don't put the input element into the DOM until we get its correct // dimensions from the framework. - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); - checkInputEditingState( - textEditing!.strategy.domElement, 'abcd', 2, 3); + checkInputEditingState(textEditing!.strategy.domElement, 'abcd', 2, 3); expect(textEditing!.isEditing, isTrue); // No connection close message sent. @@ -947,69 +884,70 @@ Future testMain() async { await Future.delayed(Duration.zero); // DOM element still keeps the focus. - expect(defaultTextEditingRoot.ownerDocument?.activeElement, - textEditing!.strategy.domElement); + expect(defaultTextEditingRoot.ownerDocument?.activeElement, textEditing!.strategy.domElement); }); - test('focus and disconnection with delaying blur in iOS', () async { - final MethodCall setClient = MethodCall( - 'TextInput.setClient', [123, flutterSinglelineConfig]); - sendFrameworkMessage(codec.encodeMethodCall(setClient)); - - const MethodCall setEditingState = - MethodCall('TextInput.setEditingState', { - 'text': 'abcd', - 'selectionBase': 2, - 'selectionExtent': 3, - }); - sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); - - // Editing shouldn't have started yet. - expect(defaultTextEditingRoot.ownerDocument?.activeElement, - domDocument.body); - - const MethodCall show = MethodCall('TextInput.show'); - sendFrameworkMessage(codec.encodeMethodCall(show)); - - // The "setSizeAndTransform" message has to be here before we call - // checkInputEditingState, since on some platforms (e.g. Desktop Safari) - // we don't put the input element into the DOM until we get its correct - // dimensions from the framework. - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); - sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); - - checkInputEditingState( - textEditing!.strategy.domElement, 'abcd', 2, 3); - expect(textEditing!.isEditing, isTrue); - - // Delay for not to be a fast callback with blur. - await Future.delayed(const Duration(milliseconds: 200)); - // DOM element is blurred. - textEditing!.strategy.domElement!.blur(); + test( + 'focus and disconnection with delaying blur in iOS', + () async { + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterSinglelineConfig, + ]); + sendFrameworkMessage(codec.encodeMethodCall(setClient)); + + const MethodCall setEditingState = MethodCall( + 'TextInput.setEditingState', + {'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3}, + ); + sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); + + // Editing shouldn't have started yet. + expect(defaultTextEditingRoot.ownerDocument?.activeElement, domDocument.body); + + const MethodCall show = MethodCall('TextInput.show'); + sendFrameworkMessage(codec.encodeMethodCall(show)); + + // The "setSizeAndTransform" message has to be here before we call + // checkInputEditingState, since on some platforms (e.g. Desktop Safari) + // we don't put the input element into the DOM until we get its correct + // dimensions from the framework. + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); + sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); + + checkInputEditingState(textEditing!.strategy.domElement, 'abcd', 2, 3); + expect(textEditing!.isEditing, isTrue); + + // Delay for not to be a fast callback with blur. + await Future.delayed(const Duration(milliseconds: 200)); + // DOM element is blurred. + textEditing!.strategy.domElement!.blur(); + + expect(spy.messages, hasLength(1)); + expect(spy.messages[0].channel, 'flutter/textinput'); + expect(spy.messages[0].methodName, 'TextInputClient.onConnectionClosed'); + await Future.delayed(Duration.zero); + // DOM element loses the focus. + expect(defaultTextEditingRoot.ownerDocument?.activeElement, domDocument.body); + }, + // Test on ios-safari only. + skip: + ui_web.browser.browserEngine != ui_web.BrowserEngine.webkit || + ui_web.browser.operatingSystem != ui_web.OperatingSystem.iOs, + ); - expect(spy.messages, hasLength(1)); - expect(spy.messages[0].channel, 'flutter/textinput'); - expect( - spy.messages[0].methodName, 'TextInputClient.onConnectionClosed'); - await Future.delayed(Duration.zero); - // DOM element loses the focus. - expect(defaultTextEditingRoot.ownerDocument?.activeElement, - domDocument.body); - }, - // Test on ios-safari only. - skip: ui_web.browser.browserEngine != ui_web.BrowserEngine.webkit || - ui_web.browser.operatingSystem != ui_web.OperatingSystem.iOs); - - test('finishAutofillContext closes connection no autofill element', - () async { - final MethodCall setClient = MethodCall( - 'TextInput.setClient', [123, flutterSinglelineConfig]); + test('finishAutofillContext closes connection no autofill element', () async { + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterSinglelineConfig, + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); - const MethodCall setEditingState = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState = MethodCall('TextInput.setEditingState', { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, @@ -1017,8 +955,7 @@ Future testMain() async { sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); // Editing shouldn't have started yet. - expect(defaultTextEditingRoot.ownerDocument?.activeElement, - domDocument.body); + expect(defaultTextEditingRoot.ownerDocument?.activeElement, domDocument.body); const MethodCall show = MethodCall('TextInput.show'); sendFrameworkMessage(codec.encodeMethodCall(show)); @@ -1027,27 +964,24 @@ Future testMain() async { // checkInputEditingState, since on some platforms (e.g. Desktop Safari) // we don't put the input element into the DOM until we get its correct // dimensions from the framework. - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); - checkInputEditingState( - textEditing!.strategy.domElement, 'abcd', 2, 3); + checkInputEditingState(textEditing!.strategy.domElement, 'abcd', 2, 3); - const MethodCall finishAutofillContext = - MethodCall('TextInput.finishAutofillContext', false); + const MethodCall finishAutofillContext = MethodCall('TextInput.finishAutofillContext', false); sendFrameworkMessage(codec.encodeMethodCall(finishAutofillContext)); expect(spy.messages, hasLength(1)); expect(spy.messages[0].channel, 'flutter/textinput'); expect(spy.messages[0].methodName, 'TextInputClient.onConnectionClosed'); - expect( - spy.messages[0].methodArguments, - [ - 123, // Client ID - ], - ); + expect(spy.messages[0].methodArguments, [ + 123, // Client ID + ]); spy.messages.clear(); // Input element is removed from DOM. expect(defaultTextEditingRoot.querySelectorAll('input'), hasLength(0)); @@ -1055,23 +989,18 @@ Future testMain() async { test('finishAutofillContext removes form from DOM', () async { // Create a configuration with an AutofillGroup of four text fields. - final Map flutterMultiAutofillElementConfig = - createFlutterConfig( - 'text', - autofillHint: 'username', - autofillHintsForFields: [ - 'username', - 'email', - 'name', - 'telephoneNumber' - ], - ); - final MethodCall setClient = MethodCall('TextInput.setClient', - [123, flutterMultiAutofillElementConfig]); + final Map flutterMultiAutofillElementConfig = createFlutterConfig( + 'text', + autofillHint: 'username', + autofillHintsForFields: ['username', 'email', 'name', 'telephoneNumber'], + ); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterMultiAutofillElementConfig, + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); - const MethodCall setEditingState1 = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState1 = MethodCall('TextInput.setEditingState', { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, @@ -1083,9 +1012,11 @@ Future testMain() async { // The transform is changed. For example after a validation error, red // line appeared under the input field. - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); // Form is added to DOM. @@ -1100,8 +1031,7 @@ Future testMain() async { expect(defaultTextEditingRoot.querySelectorAll('form'), isNotEmpty); expect(formsOnTheDom, hasLength(1)); - const MethodCall finishAutofillContext = - MethodCall('TextInput.finishAutofillContext', false); + const MethodCall finishAutofillContext = MethodCall('TextInput.finishAutofillContext', false); sendFrameworkMessage(codec.encodeMethodCall(finishAutofillContext)); // Form element is removed from DOM. @@ -1111,21 +1041,18 @@ Future testMain() async { test('finishAutofillContext with save submits forms', () async { // Create a configuration with an AutofillGroup of four text fields. - final Map flutterMultiAutofillElementConfig = - createFlutterConfig('text', - autofillHint: 'username', - autofillHintsForFields: [ - 'username', - 'email', - 'name', - 'telephoneNumber' - ]); - final MethodCall setClient = MethodCall('TextInput.setClient', - [123, flutterMultiAutofillElementConfig]); + final Map flutterMultiAutofillElementConfig = createFlutterConfig( + 'text', + autofillHint: 'username', + autofillHintsForFields: ['username', 'email', 'name', 'telephoneNumber'], + ); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterMultiAutofillElementConfig, + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); - const MethodCall setEditingState1 = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState1 = MethodCall('TextInput.setEditingState', { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, @@ -1137,9 +1064,11 @@ Future testMain() async { // The transform is changed. For example after a validation error, red // line appeared under the input field. - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); // Form is added to DOM. @@ -1148,14 +1077,14 @@ Future testMain() async { defaultTextEditingRoot.querySelector('form')! as DomHTMLFormElement; final Completer submittedForm = Completer(); formElement.addEventListener( - 'submit', createDomEventListener((DomEvent event) => - submittedForm.complete(true))); + 'submit', + createDomEventListener((DomEvent event) => submittedForm.complete(true)), + ); const MethodCall clearClient = MethodCall('TextInput.clearClient'); sendFrameworkMessage(codec.encodeMethodCall(clearClient)); - const MethodCall finishAutofillContext = - MethodCall('TextInput.finishAutofillContext', true); + const MethodCall finishAutofillContext = MethodCall('TextInput.finishAutofillContext', true); sendFrameworkMessage(codec.encodeMethodCall(finishAutofillContext)); // `submit` action is called on form. @@ -1164,21 +1093,18 @@ Future testMain() async { test('forms submits for focused input', () async { // Create a configuration with an AutofillGroup of four text fields. - final Map flutterMultiAutofillElementConfig = - createFlutterConfig('text', - autofillHint: 'username', - autofillHintsForFields: [ - 'username', - 'email', - 'name', - 'telephoneNumber' - ]); - final MethodCall setClient = MethodCall('TextInput.setClient', - [123, flutterMultiAutofillElementConfig]); + final Map flutterMultiAutofillElementConfig = createFlutterConfig( + 'text', + autofillHint: 'username', + autofillHintsForFields: ['username', 'email', 'name', 'telephoneNumber'], + ); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterMultiAutofillElementConfig, + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); - const MethodCall setEditingState1 = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState1 = MethodCall('TextInput.setEditingState', { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, @@ -1190,9 +1116,11 @@ Future testMain() async { // The transform is changed. For example after a validation error, red // line appeared under the input field. - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); // Form is added to DOM. @@ -1201,12 +1129,12 @@ Future testMain() async { defaultTextEditingRoot.querySelector('form')! as DomHTMLFormElement; final Completer submittedForm = Completer(); formElement.addEventListener( - 'submit', createDomEventListener((DomEvent event) => - submittedForm.complete(true))); + 'submit', + createDomEventListener((DomEvent event) => submittedForm.complete(true)), + ); // Clear client is not called. The used requested context to be finalized. - const MethodCall finishAutofillContext = - MethodCall('TextInput.finishAutofillContext', true); + const MethodCall finishAutofillContext = MethodCall('TextInput.finishAutofillContext', true); sendFrameworkMessage(codec.encodeMethodCall(finishAutofillContext)); // Connection is closed by the engine. @@ -1225,16 +1153,15 @@ Future testMain() async { final List focusinEvents = []; final DomEventListener handleFocusIn = createDomEventListener(focusinEvents.add); - final MethodCall setClient1 = MethodCall( - 'TextInput.setClient', - [123, flutterSinglelineConfig], - ); - final MethodCall setClient2 = MethodCall( - 'TextInput.setClient', - [567, flutterSinglelineConfig], - ); - const MethodCall setEditingState = - MethodCall('TextInput.setEditingState', { + final MethodCall setClient1 = MethodCall('TextInput.setClient', [ + 123, + flutterSinglelineConfig, + ]); + final MethodCall setClient2 = MethodCall('TextInput.setClient', [ + 567, + flutterSinglelineConfig, + ]); + const MethodCall setEditingState = MethodCall('TextInput.setEditingState', { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, @@ -1274,12 +1201,13 @@ Future testMain() async { }); test('setClient, setEditingState, show, setClient', () async { - final MethodCall setClient = MethodCall( - 'TextInput.setClient', [123, flutterSinglelineConfig]); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterSinglelineConfig, + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); - const MethodCall setEditingState = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState = MethodCall('TextInput.setEditingState', { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, @@ -1299,16 +1227,19 @@ Future testMain() async { // checkInputEditingState, since on some platforms (e.g. Desktop Safari) // we don't put the input element into the DOM until we get its correct // dimensions from the framework. - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); - checkInputEditingState( - textEditing!.strategy.domElement, 'abcd', 2, 3); + checkInputEditingState(textEditing!.strategy.domElement, 'abcd', 2, 3); - final MethodCall setClient2 = MethodCall( - 'TextInput.setClient', [567, flutterSinglelineConfig]); + final MethodCall setClient2 = MethodCall('TextInput.setClient', [ + 567, + flutterSinglelineConfig, + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient2)); await waitForTextStrategyStopPropagation(); @@ -1316,7 +1247,8 @@ Future testMain() async { expect( domDocument.activeElement, implicitViewRootElement, - reason: 'Receiving another client via setClient should stop editing, hence should remove the previous active element.', + reason: + 'Receiving another client via setClient should stop editing, hence should remove the previous active element.', ); // Confirm that [HybridTextEditing] didn't send any messages. @@ -1326,12 +1258,13 @@ Future testMain() async { }); test('setClient, setEditingState, show, setEditingState, clearClient', () async { - final MethodCall setClient = MethodCall( - 'TextInput.setClient', [123, flutterSinglelineConfig]); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterSinglelineConfig, + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); - const MethodCall setEditingState1 = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState1 = MethodCall('TextInput.setEditingState', { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, @@ -1345,13 +1278,14 @@ Future testMain() async { // checkInputEditingState, since on some platforms (e.g. Desktop Safari) // we don't put the input element into the DOM until we get its correct // dimensions from the framework. - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); - const MethodCall setEditingState2 = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState2 = MethodCall('TextInput.setEditingState', { 'text': 'xyz', 'selectionBase': 0, 'selectionExtent': 2, @@ -1359,8 +1293,7 @@ Future testMain() async { sendFrameworkMessage(codec.encodeMethodCall(setEditingState2)); // The second [setEditingState] should override the first one. - checkInputEditingState( - textEditing!.strategy.domElement, 'xyz', 0, 2); + checkInputEditingState(textEditing!.strategy.domElement, 'xyz', 0, 2); const MethodCall clearClient = MethodCall('TextInput.clearClient'); sendFrameworkMessage(codec.encodeMethodCall(clearClient)); @@ -1369,18 +1302,20 @@ Future testMain() async { expect(spy.messages, isEmpty); }); - test( - 'singleTextField Autofill: setClient, setEditingState, show, ' + test('singleTextField Autofill: setClient, setEditingState, show, ' 'setSizeAndTransform, setEditingState, clearClient', () async { // Create a configuration with focused element has autofil hint. - final Map flutterSingleAutofillElementConfig = - createFlutterConfig('text', autofillHint: 'username'); - final MethodCall setClient = MethodCall('TextInput.setClient', - [123, flutterSingleAutofillElementConfig]); + final Map flutterSingleAutofillElementConfig = createFlutterConfig( + 'text', + autofillHint: 'username', + ); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterSingleAutofillElementConfig, + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); - const MethodCall setEditingState1 = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState1 = MethodCall('TextInput.setEditingState', { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, @@ -1394,14 +1329,15 @@ Future testMain() async { // checkInputEditingState, since on some platforms (e.g. Desktop Safari) // we don't put the input element into the DOM until we get its correct // dimensions from the framework. - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); // The second [setEditingState] should override the first one. - checkInputEditingState( - textEditing!.strategy.domElement, 'abcd', 2, 3); + checkInputEditingState(textEditing!.strategy.domElement, 'abcd', 2, 3); final DomHTMLFormElement formElement = defaultTextEditingRoot.querySelector('form')! as DomHTMLFormElement; @@ -1418,18 +1354,20 @@ Future testMain() async { expect(formsOnTheDom, hasLength(1)); }); - test( - 'singleTextField Autofill setEditableSizeAndTransform preserves' + test('singleTextField Autofill setEditableSizeAndTransform preserves' 'editing state', () async { // Create a configuration with focused element has autofil hint. - final Map flutterSingleAutofillElementConfig = - createFlutterConfig('text', autofillHint: 'username'); - final MethodCall setClient = MethodCall('TextInput.setClient', - [123, flutterSingleAutofillElementConfig]); + final Map flutterSingleAutofillElementConfig = createFlutterConfig( + 'text', + autofillHint: 'username', + ); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterSingleAutofillElementConfig, + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); - const MethodCall setEditingState1 = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState1 = MethodCall('TextInput.setEditingState', { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, @@ -1443,9 +1381,11 @@ Future testMain() async { // checkInputEditingState, since on some platforms (e.g. Desktop Safari) // we don't put the input element into the DOM until we get its correct // dimensions from the framework. - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(10, 10, - Matrix4.translationValues(10.0, 10.0, 10.0).storage.toList()); + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 10, + 10, + Matrix4.translationValues(10.0, 10.0, 10.0).storage.toList(), + ); sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); final DomHTMLInputElement inputElement = @@ -1456,26 +1396,25 @@ Future testMain() async { // In Safari Desktop Autofill menu appears as soon as an element is // focused, therefore the input element is only focused after the // location is received. - expect( - defaultTextEditingRoot.ownerDocument?.activeElement, inputElement); + expect(defaultTextEditingRoot.ownerDocument?.activeElement, inputElement); expect(inputElement.selectionStart, 2); expect(inputElement.selectionEnd, 3); } // The transform is changed. For example after a validation error, red // line appeared under the input field. - final MethodCall updateSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); + final MethodCall updateSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); sendFrameworkMessage(codec.encodeMethodCall(updateSizeAndTransform)); // Check the element still has focus. User can keep editing. - expect(defaultTextEditingRoot.ownerDocument?.activeElement, - textEditing!.strategy.domElement); + expect(defaultTextEditingRoot.ownerDocument?.activeElement, textEditing!.strategy.domElement); // Check the cursor location is the same. - checkInputEditingState( - textEditing!.strategy.domElement, 'abcd', 2, 3); + checkInputEditingState(textEditing!.strategy.domElement, 'abcd', 2, 3); const MethodCall clearClient = MethodCall('TextInput.clearClient'); sendFrameworkMessage(codec.encodeMethodCall(clearClient)); @@ -1487,25 +1426,21 @@ Future testMain() async { expect(formsOnTheDom, hasLength(1)); }); - test( - 'multiTextField Autofill: setClient, setEditingState, show, ' + test('multiTextField Autofill: setClient, setEditingState, show, ' 'setSizeAndTransform setEditingState, clearClient', () async { // Create a configuration with an AutofillGroup of four text fields. - final Map flutterMultiAutofillElementConfig = - createFlutterConfig('text', - autofillHint: 'username', - autofillHintsForFields: [ - 'username', - 'email', - 'name', - 'telephoneNumber' - ]); - final MethodCall setClient = MethodCall('TextInput.setClient', - [123, flutterMultiAutofillElementConfig]); + final Map flutterMultiAutofillElementConfig = createFlutterConfig( + 'text', + autofillHint: 'username', + autofillHintsForFields: ['username', 'email', 'name', 'telephoneNumber'], + ); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterMultiAutofillElementConfig, + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); - const MethodCall setEditingState1 = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState1 = MethodCall('TextInput.setEditingState', { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, @@ -1519,14 +1454,15 @@ Future testMain() async { // checkInputEditingState, since on some platforms (e.g. Desktop Safari) // we don't put the input element into the DOM until we get its correct // dimensions from the framework. - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); // The second [setEditingState] should override the first one. - checkInputEditingState( - textEditing!.strategy.domElement, 'abcd', 2, 3); + checkInputEditingState(textEditing!.strategy.domElement, 'abcd', 2, 3); final DomHTMLFormElement formElement = defaultTextEditingRoot.querySelector('form')! as DomHTMLFormElement; @@ -1545,14 +1481,14 @@ Future testMain() async { test('No capitalization: setClient, setEditingState, show', () { // Create a configuration with an AutofillGroup of four text fields. - final Map capitalizeWordsConfig = createFlutterConfig( - 'text'); - final MethodCall setClient = MethodCall( - 'TextInput.setClient', [123, capitalizeWordsConfig]); + final Map capitalizeWordsConfig = createFlutterConfig('text'); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + capitalizeWordsConfig, + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); - const MethodCall setEditingState1 = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState1 = MethodCall('TextInput.setEditingState', { 'text': '', 'selectionBase': 0, 'selectionExtent': 0, @@ -1567,15 +1503,9 @@ Future testMain() async { // mobile browsers. Check if `off` is added to the input element. if (ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit && ui_web.browser.operatingSystem == ui_web.OperatingSystem.iOs) { - expect( - textEditing!.strategy.domElement! - .getAttribute('autocapitalize'), - 'off'); + expect(textEditing!.strategy.domElement!.getAttribute('autocapitalize'), 'off'); } else { - expect( - textEditing!.strategy.domElement! - .getAttribute('autocapitalize'), - isNull); + expect(textEditing!.strategy.domElement!.getAttribute('autocapitalize'), isNull); } spy.messages.clear(); @@ -1585,14 +1515,16 @@ Future testMain() async { test('All characters capitalization: setClient, setEditingState, show', () { // Create a configuration with an AutofillGroup of four text fields. final Map capitalizeWordsConfig = createFlutterConfig( - 'text', - textCapitalization: 'TextCapitalization.characters'); - final MethodCall setClient = MethodCall( - 'TextInput.setClient', [123, capitalizeWordsConfig]); + 'text', + textCapitalization: 'TextCapitalization.characters', + ); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + capitalizeWordsConfig, + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); - const MethodCall setEditingState1 = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState1 = MethodCall('TextInput.setEditingState', { 'text': '', 'selectionBase': 0, 'selectionExtent': 0, @@ -1606,10 +1538,7 @@ Future testMain() async { // Test for mobile Safari. if (ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit && ui_web.browser.operatingSystem == ui_web.OperatingSystem.iOs) { - expect( - textEditing!.strategy.domElement! - .getAttribute('autocapitalize'), - 'characters'); + expect(textEditing!.strategy.domElement!.getAttribute('autocapitalize'), 'characters'); } spy.messages.clear(); @@ -1617,173 +1546,183 @@ Future testMain() async { }); test( - 'setClient, setEditableSizeAndTransform, setStyle, setEditingState, show, clearClient', - () { - final MethodCall setClient = MethodCall( - 'TextInput.setClient', [123, flutterSinglelineConfig]); - sendFrameworkMessage(codec.encodeMethodCall(setClient)); - - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); - sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); - - final MethodCall setStyle = - configureSetStyleMethodCall(12, 'sans-serif', 4, 4, 1); - sendFrameworkMessage(codec.encodeMethodCall(setStyle)); - - const MethodCall setEditingState = - MethodCall('TextInput.setEditingState', { - 'text': 'abcd', - 'selectionBase': 2, - 'selectionExtent': 3, - }); - sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); - - const MethodCall show = MethodCall('TextInput.show'); - sendFrameworkMessage(codec.encodeMethodCall(show)); - - final DomElement domElement = textEditing!.strategy.domElement!; - - checkInputEditingState(domElement, 'abcd', 2, 3); - - // Check if the location and styling is correct. - final DomRect boundingRect = domElement.getBoundingClientRect(); - expect(boundingRect.left, 10.0); - expect(boundingRect.top, 20.0); - expect(boundingRect.right, 160.0); - expect(boundingRect.bottom, 70.0); - expect(domElement.style.transform, - 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 20, 30, 1)'); - expect(textEditing!.strategy.domElement!.style.font, - '500 12px sans-serif'); + 'setClient, setEditableSizeAndTransform, setStyle, setEditingState, show, clearClient', + () { + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterSinglelineConfig, + ]); + sendFrameworkMessage(codec.encodeMethodCall(setClient)); + + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); + sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); + + final MethodCall setStyle = configureSetStyleMethodCall(12, 'sans-serif', 4, 4, 1); + sendFrameworkMessage(codec.encodeMethodCall(setStyle)); + + const MethodCall setEditingState = MethodCall( + 'TextInput.setEditingState', + {'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3}, + ); + sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); + + const MethodCall show = MethodCall('TextInput.show'); + sendFrameworkMessage(codec.encodeMethodCall(show)); + + final DomElement domElement = textEditing!.strategy.domElement!; + + checkInputEditingState(domElement, 'abcd', 2, 3); + + // Check if the location and styling is correct. + final DomRect boundingRect = domElement.getBoundingClientRect(); + expect(boundingRect.left, 10.0); + expect(boundingRect.top, 20.0); + expect(boundingRect.right, 160.0); + expect(boundingRect.bottom, 70.0); + expect( + domElement.style.transform, + 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 20, 30, 1)', + ); + expect(textEditing!.strategy.domElement!.style.font, '500 12px sans-serif'); - const MethodCall clearClient = MethodCall('TextInput.clearClient'); - sendFrameworkMessage(codec.encodeMethodCall(clearClient)); + const MethodCall clearClient = MethodCall('TextInput.clearClient'); + sendFrameworkMessage(codec.encodeMethodCall(clearClient)); - // Confirm that [HybridTextEditing] didn't send any messages. - expect(spy.messages, isEmpty); - }, - // TODO(mdebbar): https://github.com/flutter/flutter/issues/50590 - skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit); + // Confirm that [HybridTextEditing] didn't send any messages. + expect(spy.messages, isEmpty); + }, + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50590 + skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit, + ); test( - 'setClient, show, setEditableSizeAndTransform, setStyle, setEditingState, clearClient', - () { - final MethodCall setClient = MethodCall( - 'TextInput.setClient', [123, flutterSinglelineConfig]); - sendFrameworkMessage(codec.encodeMethodCall(setClient)); - - const MethodCall show = MethodCall('TextInput.show'); - sendFrameworkMessage(codec.encodeMethodCall(show)); - - // The "setSizeAndTransform" message has to be here before we call - // checkInputEditingState, since on some platforms (e.g. Desktop Safari) - // we don't put the input element into the DOM until we get its correct - // dimensions from the framework. - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall( - 150, - 50, - Matrix4.translationValues( - 10.0, - 20.0, - 30.0, - ).storage.toList()); - sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); - - final MethodCall setStyle = - configureSetStyleMethodCall(12, 'sans-serif', 4, 4, 1); - sendFrameworkMessage(codec.encodeMethodCall(setStyle)); - - const MethodCall setEditingState = - MethodCall('TextInput.setEditingState', { - 'text': 'abcd', - 'selectionBase': 2, - 'selectionExtent': 3, - }); - sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); - - final DomHTMLElement domElement = textEditing!.strategy.domElement!; - - checkInputEditingState(domElement, 'abcd', 2, 3); - - // Check if the position is correct. - final DomRect boundingRect = domElement.getBoundingClientRect(); - expect(boundingRect.left, 10.0); - expect(boundingRect.top, 20.0); - expect(boundingRect.right, 160.0); - expect(boundingRect.bottom, 70.0); - expect( - domElement.style.transform, - 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 20, 30, 1)', - ); - expect( - textEditing!.strategy.domElement!.style.font, - '500 12px sans-serif', - ); - - // For `blink` and `webkit` browser engines the overlay would be hidden. - if (ui_web.browser.browserEngine == ui_web.BrowserEngine.blink || - ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit) { - expect(textEditing!.strategy.domElement!.classList.contains('transparentTextEditing'), - isTrue); - } else { + 'setClient, show, setEditableSizeAndTransform, setStyle, setEditingState, clearClient', + () { + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterSinglelineConfig, + ]); + sendFrameworkMessage(codec.encodeMethodCall(setClient)); + + const MethodCall show = MethodCall('TextInput.show'); + sendFrameworkMessage(codec.encodeMethodCall(show)); + + // The "setSizeAndTransform" message has to be here before we call + // checkInputEditingState, since on some platforms (e.g. Desktop Safari) + // we don't put the input element into the DOM until we get its correct + // dimensions from the framework. + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); + sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); + + final MethodCall setStyle = configureSetStyleMethodCall(12, 'sans-serif', 4, 4, 1); + sendFrameworkMessage(codec.encodeMethodCall(setStyle)); + + const MethodCall setEditingState = MethodCall( + 'TextInput.setEditingState', + {'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3}, + ); + sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); + + final DomHTMLElement domElement = textEditing!.strategy.domElement!; + + checkInputEditingState(domElement, 'abcd', 2, 3); + + // Check if the position is correct. + final DomRect boundingRect = domElement.getBoundingClientRect(); + expect(boundingRect.left, 10.0); + expect(boundingRect.top, 20.0); + expect(boundingRect.right, 160.0); + expect(boundingRect.bottom, 70.0); expect( + domElement.style.transform, + 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 20, 30, 1)', + ); + expect(textEditing!.strategy.domElement!.style.font, '500 12px sans-serif'); + + // For `blink` and `webkit` browser engines the overlay would be hidden. + if (ui_web.browser.browserEngine == ui_web.BrowserEngine.blink || + ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit) { + expect( textEditing!.strategy.domElement!.classList.contains('transparentTextEditing'), - isFalse); - } - - const MethodCall clearClient = MethodCall('TextInput.clearClient'); - sendFrameworkMessage(codec.encodeMethodCall(clearClient)); - }, - // TODO(mdebbar): https://github.com/flutter/flutter/issues/50590 - skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit); - - test('input font set successfully with null fontWeightIndex', () { - final MethodCall setClient = MethodCall( - 'TextInput.setClient', [123, flutterSinglelineConfig]); - sendFrameworkMessage(codec.encodeMethodCall(setClient)); - - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); - sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); - - final MethodCall setStyle = configureSetStyleMethodCall( - 12, 'sans-serif', 4, null /* fontWeightIndex */, 1); - sendFrameworkMessage(codec.encodeMethodCall(setStyle)); - - const MethodCall setEditingState = - MethodCall('TextInput.setEditingState', { - 'text': 'abcd', - 'selectionBase': 2, - 'selectionExtent': 3, - }); - sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); - - const MethodCall show = MethodCall('TextInput.show'); - sendFrameworkMessage(codec.encodeMethodCall(show)); - - final DomHTMLElement domElement = textEditing!.strategy.domElement!; + isTrue, + ); + } else { + expect( + textEditing!.strategy.domElement!.classList.contains('transparentTextEditing'), + isFalse, + ); + } - checkInputEditingState(domElement, 'abcd', 2, 3); + const MethodCall clearClient = MethodCall('TextInput.clearClient'); + sendFrameworkMessage(codec.encodeMethodCall(clearClient)); + }, + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50590 + skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit, + ); - // Check if the location and styling is correct. - final DomRect boundingRect = domElement.getBoundingClientRect(); - expect(boundingRect.left, 10.0); - expect(boundingRect.top, 20.0); - expect(boundingRect.right, 160.0); - expect(boundingRect.bottom, 70.0); - expect(domElement.style.transform, - 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 20, 30, 1)'); - expect( - textEditing!.strategy.domElement!.style.font, '12px sans-serif'); + test( + 'input font set successfully with null fontWeightIndex', + () { + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterSinglelineConfig, + ]); + sendFrameworkMessage(codec.encodeMethodCall(setClient)); + + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); + sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); + + final MethodCall setStyle = configureSetStyleMethodCall( + 12, + 'sans-serif', + 4, + null /* fontWeightIndex */, + 1, + ); + sendFrameworkMessage(codec.encodeMethodCall(setStyle)); + + const MethodCall setEditingState = MethodCall( + 'TextInput.setEditingState', + {'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3}, + ); + sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); + + const MethodCall show = MethodCall('TextInput.show'); + sendFrameworkMessage(codec.encodeMethodCall(show)); + + final DomHTMLElement domElement = textEditing!.strategy.domElement!; + + checkInputEditingState(domElement, 'abcd', 2, 3); + + // Check if the location and styling is correct. + final DomRect boundingRect = domElement.getBoundingClientRect(); + expect(boundingRect.left, 10.0); + expect(boundingRect.top, 20.0); + expect(boundingRect.right, 160.0); + expect(boundingRect.bottom, 70.0); + expect( + domElement.style.transform, + 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 20, 30, 1)', + ); + expect(textEditing!.strategy.domElement!.style.font, '12px sans-serif'); - hideKeyboard(); - }, - // TODO(mdebbar): https://github.com/flutter/flutter/issues/50590 - skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit); + hideKeyboard(); + }, + // TODO(mdebbar): https://github.com/flutter/flutter/issues/50590 + skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit, + ); test('Canonicalizes font family', () { showKeyboard(inputType: 'text'); @@ -1807,15 +1746,14 @@ Future testMain() async { hideKeyboard(); }); - test( - 'negative base offset and selection extent values in editing state is handled', - () async { - final MethodCall setClient = MethodCall( - 'TextInput.setClient', [123, flutterSinglelineConfig]); + test('negative base offset and selection extent values in editing state is handled', () async { + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterSinglelineConfig, + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); - const MethodCall setEditingState1 = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState1 = MethodCall('TextInput.setEditingState', { 'text': 'xyz', 'selectionBase': 1, 'selectionExtent': 2, @@ -1829,17 +1767,17 @@ Future testMain() async { // checkInputEditingState, since on some platforms (e.g. Desktop Safari) // we don't put the input element into the DOM until we get its correct // dimensions from the framework. - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); // Check if the selection range is correct. - checkInputEditingState( - textEditing!.strategy.domElement, 'xyz', 1, 2); + checkInputEditingState(textEditing!.strategy.domElement, 'xyz', 1, 2); - const MethodCall setEditingState2 = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState2 = MethodCall('TextInput.setEditingState', { 'text': 'xyz', 'selectionBase': -1, 'selectionExtent': -1, @@ -1847,19 +1785,19 @@ Future testMain() async { sendFrameworkMessage(codec.encodeMethodCall(setEditingState2)); // The negative offset values are applied to the dom element as 0. - checkInputEditingState( - textEditing!.strategy.domElement, 'xyz', 0, 0); + checkInputEditingState(textEditing!.strategy.domElement, 'xyz', 0, 0); hideKeyboard(); }); test('Syncs the editing state back to Flutter', () { - final MethodCall setClient = MethodCall( - 'TextInput.setClient', [123, flutterSinglelineConfig]); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterSinglelineConfig, + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); - const MethodCall setEditingState = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState = MethodCall('TextInput.setEditingState', { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, @@ -1869,8 +1807,7 @@ Future testMain() async { const MethodCall show = MethodCall('TextInput.show'); sendFrameworkMessage(codec.encodeMethodCall(show)); - final DomHTMLInputElement input = textEditing!.strategy.domElement! as - DomHTMLInputElement; + final DomHTMLInputElement input = textEditing!.strategy.domElement! as DomHTMLInputElement; input.value = 'something'; input.dispatchEvent(createDomEvent('Event', 'input')); @@ -1878,19 +1815,16 @@ Future testMain() async { expect(spy.messages, hasLength(1)); expect(spy.messages[0].channel, 'flutter/textinput'); expect(spy.messages[0].methodName, 'TextInputClient.updateEditingState'); - expect( - spy.messages[0].methodArguments, - [ - 123, // Client ID - { - 'text': 'something', - 'selectionBase': 9, - 'selectionExtent': 9, - 'composingBase': -1, - 'composingExtent': -1 - } - ], - ); + expect(spy.messages[0].methodArguments, [ + 123, // Client ID + { + 'text': 'something', + 'selectionBase': 9, + 'selectionExtent': 9, + 'composingBase': -1, + 'composingExtent': -1, + }, + ]); spy.messages.clear(); input.setSelectionRange(2, 5); @@ -1904,31 +1838,29 @@ Future testMain() async { expect(spy.messages, hasLength(1)); expect(spy.messages[0].channel, 'flutter/textinput'); expect(spy.messages[0].methodName, 'TextInputClient.updateEditingState'); - expect( - spy.messages[0].methodArguments, - [ - 123, // Client ID - { - 'text': 'something', - 'selectionBase': 2, - 'selectionExtent': 5, - 'composingBase': -1, - 'composingExtent': -1 - } - ], - ); + expect(spy.messages[0].methodArguments, [ + 123, // Client ID + { + 'text': 'something', + 'selectionBase': 2, + 'selectionExtent': 5, + 'composingBase': -1, + 'composingExtent': -1, + }, + ]); spy.messages.clear(); hideKeyboard(); }); test('Syncs the editing state back to Flutter - delta model', () { - final MethodCall setClient = MethodCall( - 'TextInput.setClient', [123, createFlutterConfig('text', enableDeltaModel: true)]); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + createFlutterConfig('text', enableDeltaModel: true), + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); - const MethodCall setEditingState = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState = MethodCall('TextInput.setEditingState', { 'text': '', 'selectionBase': -1, 'selectionExtent': -1, @@ -1938,8 +1870,7 @@ Future testMain() async { const MethodCall show = MethodCall('TextInput.show'); sendFrameworkMessage(codec.encodeMethodCall(show)); - final DomHTMLInputElement input = textEditing!.strategy.domElement! as - DomHTMLInputElement; + final DomHTMLInputElement input = textEditing!.strategy.domElement! as DomHTMLInputElement; input.value = 'something'; input.dispatchEvent(createDomEvent('Event', 'input')); @@ -1957,38 +1888,36 @@ Future testMain() async { expect(spy.messages, hasLength(1)); expect(spy.messages[0].channel, 'flutter/textinput'); expect(spy.messages[0].methodName, 'TextInputClient.updateEditingStateWithDeltas'); - expect( - spy.messages[0].methodArguments, - [ - 123, // Client ID - { - 'deltas': >[ - { - 'oldText': 'something', - 'deltaText': '', - 'deltaStart': -1, - 'deltaEnd': -1, - 'selectionBase': 2, - 'selectionExtent': 5, - 'composingBase': -1, - 'composingExtent': -1 - } - ], - } - ], - ); + expect(spy.messages[0].methodArguments, [ + 123, // Client ID + { + 'deltas': >[ + { + 'oldText': 'something', + 'deltaText': '', + 'deltaStart': -1, + 'deltaEnd': -1, + 'selectionBase': 2, + 'selectionExtent': 5, + 'composingBase': -1, + 'composingExtent': -1, + }, + ], + }, + ]); spy.messages.clear(); hideKeyboard(); }); test('Supports deletion at inverted selection', () async { - final MethodCall setClient = MethodCall( - 'TextInput.setClient', [123, createFlutterConfig('text', enableDeltaModel: true)]); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + createFlutterConfig('text', enableDeltaModel: true), + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); - const MethodCall setEditingState = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState = MethodCall('TextInput.setEditingState', { 'text': 'Hello world', 'selectionBase': 9, 'selectionExtent': 3, @@ -2002,53 +1931,44 @@ Future testMain() async { // checkInputEditingState, since on some platforms (e.g. Desktop Safari) // we don't put the input element into the DOM until we get its correct // dimensions from the framework. - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); - final DomHTMLInputElement input = textEditing!.strategy.domElement! as - DomHTMLInputElement; + final DomHTMLInputElement input = textEditing!.strategy.domElement! as DomHTMLInputElement; - final DomInputEvent testEvent = createDomInputEvent( - 'beforeinput', - { - 'inputType': 'deleteContentBackward', - }, - ); + final DomInputEvent testEvent = createDomInputEvent('beforeinput', { + 'inputType': 'deleteContentBackward', + }); input.dispatchEvent(testEvent); - final EditingState editingState = EditingState( - text: 'Helld', - baseOffset: 3, - extentOffset: 3, - ); + final EditingState editingState = EditingState(text: 'Helld', baseOffset: 3, extentOffset: 3); editingState.applyToDomElement(input); input.dispatchEvent(createDomEvent('Event', 'input')); expect(spy.messages, hasLength(1)); expect(spy.messages[0].channel, 'flutter/textinput'); - expect(spy.messages[0].methodName, 'TextInputClient.updateEditingStateWithDeltas'); - expect( - spy.messages[0].methodArguments, - [ - 123, // Client ID - { - 'deltas': >[ - { - 'oldText': 'Hello world', - 'deltaText': '', - 'deltaStart': 3, - 'deltaEnd': 9, - 'selectionBase': 3, - 'selectionExtent': 3, - 'composingBase': -1, - 'composingExtent': -1 - } - ], - } - ], - ); + expect(spy.messages[0].methodName, 'TextInputClient.updateEditingStateWithDeltas'); + expect(spy.messages[0].methodArguments, [ + 123, // Client ID + { + 'deltas': >[ + { + 'oldText': 'Hello world', + 'deltaText': '', + 'deltaStart': 3, + 'deltaEnd': 9, + 'selectionBase': 3, + 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, + }, + ], + }, + ]); spy.messages.clear(); hideKeyboard(); @@ -2056,12 +1976,13 @@ Future testMain() async { }, skip: isSafari); test('Supports new line at inverted selection', () async { - final MethodCall setClient = MethodCall( - 'TextInput.setClient', [123, createFlutterConfig('text', enableDeltaModel: true)]); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + createFlutterConfig('text', enableDeltaModel: true), + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); - const MethodCall setEditingState = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState = MethodCall('TextInput.setEditingState', { 'text': 'Hello world', 'selectionBase': 9, 'selectionExtent': 3, @@ -2075,20 +1996,18 @@ Future testMain() async { // checkInputEditingState, since on some platforms (e.g. Desktop Safari) // we don't put the input element into the DOM until we get its correct // dimensions from the framework. - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); - final DomHTMLInputElement input = textEditing!.strategy.domElement! as - DomHTMLInputElement; + final DomHTMLInputElement input = textEditing!.strategy.domElement! as DomHTMLInputElement; - final DomInputEvent testEvent = createDomInputEvent( - 'beforeinput', - { - 'inputType': 'insertLineBreak', - }, - ); + final DomInputEvent testEvent = createDomInputEvent('beforeinput', { + 'inputType': 'insertLineBreak', + }); input.dispatchEvent(testEvent); final EditingState editingState = EditingState( @@ -2102,26 +2021,23 @@ Future testMain() async { expect(spy.messages, hasLength(1)); expect(spy.messages[0].channel, 'flutter/textinput'); expect(spy.messages[0].methodName, 'TextInputClient.updateEditingStateWithDeltas'); - expect( - spy.messages[0].methodArguments, - [ - 123, // Client ID - { - 'deltas': >[ - { - 'oldText': 'Hello world', - 'deltaText': '\n', - 'deltaStart': 3, - 'deltaEnd': 9, - 'selectionBase': 3, - 'selectionExtent': 3, - 'composingBase': -1, - 'composingExtent': -1 - } - ], - } - ], - ); + expect(spy.messages[0].methodArguments, [ + 123, // Client ID + { + 'deltas': >[ + { + 'oldText': 'Hello world', + 'deltaText': '\n', + 'deltaStart': 3, + 'deltaEnd': 9, + 'selectionBase': 3, + 'selectionExtent': 3, + 'composingBase': -1, + 'composingExtent': -1, + }, + ], + }, + ]); spy.messages.clear(); hideKeyboard(); @@ -2131,21 +2047,23 @@ Future testMain() async { test('multiTextField Autofill sync updates back to Flutter', () async { // Create a configuration with an AutofillGroup of four text fields. const String hintForFirstElement = 'familyName'; - final Map flutterMultiAutofillElementConfig = - createFlutterConfig('text', - autofillHint: 'email', - autofillHintsForFields: [ - hintForFirstElement, - 'email', - 'givenName', - 'telephoneNumber' - ]); - final MethodCall setClient = MethodCall('TextInput.setClient', - [123, flutterMultiAutofillElementConfig]); + final Map flutterMultiAutofillElementConfig = createFlutterConfig( + 'text', + autofillHint: 'email', + autofillHintsForFields: [ + hintForFirstElement, + 'email', + 'givenName', + 'telephoneNumber', + ], + ); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterMultiAutofillElementConfig, + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); - const MethodCall setEditingState1 = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState1 = MethodCall('TextInput.setEditingState', { 'text': 'abcd', 'selectionBase': 2, 'selectionExtent': 3, @@ -2159,14 +2077,15 @@ Future testMain() async { // checkInputEditingState, since on some platforms (e.g. Desktop Safari) // we don't put the input element into the DOM until we get its correct // dimensions from the framework. - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); // The second [setEditingState] should override the first one. - checkInputEditingState( - textEditing!.strategy.domElement, 'abcd', 2, 3); + checkInputEditingState(textEditing!.strategy.domElement, 'abcd', 2, 3); final DomHTMLFormElement formElement = defaultTextEditingRoot.querySelector('form')! as DomHTMLFormElement; @@ -2174,45 +2093,43 @@ Future testMain() async { expect(formElement.childNodes, hasLength(5)); // Autofill one of the form elements. - final DomHTMLInputElement element = formElement.childNodes.toList()[0] as - DomHTMLInputElement; + final DomHTMLInputElement element = formElement.childNodes.toList()[0] as DomHTMLInputElement; if (ui_web.browser.browserEngine == ui_web.BrowserEngine.firefox) { - expect(element.name, - BrowserAutofillHints.instance.flutterToEngine(hintForFirstElement)); + expect(element.name, BrowserAutofillHints.instance.flutterToEngine(hintForFirstElement)); } else { - expect(element.autocomplete, - BrowserAutofillHints.instance.flutterToEngine(hintForFirstElement)); + expect( + element.autocomplete, + BrowserAutofillHints.instance.flutterToEngine(hintForFirstElement), + ); } element.value = 'something'; element.dispatchEvent(createDomEvent('Event', 'input')); expect(spy.messages, hasLength(1)); expect(spy.messages[0].channel, 'flutter/textinput'); - expect(spy.messages[0].methodName, - 'TextInputClient.updateEditingStateWithTag'); - expect( - spy.messages[0].methodArguments, - [ - 0, // Client ID - { - hintForFirstElement: { - 'text': 'something', - 'selectionBase': 9, - 'selectionExtent': 9, - 'composingBase': -1, - 'composingExtent': -1 - } + expect(spy.messages[0].methodName, 'TextInputClient.updateEditingStateWithTag'); + expect(spy.messages[0].methodArguments, [ + 0, // Client ID + { + hintForFirstElement: { + 'text': 'something', + 'selectionBase': 9, + 'selectionExtent': 9, + 'composingBase': -1, + 'composingExtent': -1, }, - ], - ); + }, + ]); spy.messages.clear(); hideKeyboard(); }); test('Multi-line mode also works', () async { - final MethodCall setClient = MethodCall( - 'TextInput.setClient', [123, flutterMultilineConfig]); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterMultilineConfig, + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); expect( @@ -2228,23 +2145,24 @@ Future testMain() async { // checkInputEditingState, since on some platforms (e.g. Desktop Safari) // we don't put the input element into the DOM until we get its correct // dimensions from the framework. - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); - final DomHTMLTextAreaElement textarea = textEditing!.strategy.domElement! - as DomHTMLTextAreaElement; + final DomHTMLTextAreaElement textarea = + textEditing!.strategy.domElement! as DomHTMLTextAreaElement; checkTextAreaEditingState(textarea, '', 0, 0); // Can set editing state and preserve new lines. - const MethodCall setEditingState = - MethodCall('TextInput.setEditingState', { + const MethodCall setEditingState = MethodCall('TextInput.setEditingState', { 'text': 'foo\nbar', 'selectionBase': 2, 'selectionExtent': 3, 'composingBase': null, - 'composingExtent': null + 'composingExtent': null, }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); checkTextAreaEditingState(textarea, 'foo\nbar', 2, 3); @@ -2254,8 +2172,7 @@ Future testMain() async { textarea.dispatchEvent(createDomEvent('Event', 'input')); textarea.setSelectionRange(2, 5); if (ui_web.browser.browserEngine == ui_web.BrowserEngine.firefox) { - textEditing!.strategy.domElement! - .dispatchEvent(createDomEvent('Event', 'keyup')); + textEditing!.strategy.domElement!.dispatchEvent(createDomEvent('Event', 'keyup')); } else { domDocument.dispatchEvent(createDomEvent('Event', 'selectionchange')); } @@ -2266,35 +2183,29 @@ Future testMain() async { expect(spy.messages[0].channel, 'flutter/textinput'); expect(spy.messages[0].methodName, 'TextInputClient.updateEditingState'); - expect( - spy.messages[0].methodArguments, - [ - 123, // Client ID - { - 'text': 'something\nelse', - 'selectionBase': 14, - 'selectionExtent': 14, - 'composingBase': -1, - 'composingExtent': -1 - } - ], - ); + expect(spy.messages[0].methodArguments, [ + 123, // Client ID + { + 'text': 'something\nelse', + 'selectionBase': 14, + 'selectionExtent': 14, + 'composingBase': -1, + 'composingExtent': -1, + }, + ]); expect(spy.messages[1].channel, 'flutter/textinput'); expect(spy.messages[1].methodName, 'TextInputClient.updateEditingState'); - expect( - spy.messages[1].methodArguments, - [ - 123, // Client ID - { - 'text': 'something\nelse', - 'selectionBase': 2, - 'selectionExtent': 5, - 'composingBase': -1, - 'composingExtent': -1 - } - ], - ); + expect(spy.messages[1].methodArguments, [ + 123, // Client ID + { + 'text': 'something\nelse', + 'selectionBase': 2, + 'selectionExtent': 5, + 'composingBase': -1, + 'composingExtent': -1, + }, + ]); spy.messages.clear(); const MethodCall hide = MethodCall('TextInput.hide'); @@ -2313,8 +2224,10 @@ Future testMain() async { }); test('none mode works', () async { - final MethodCall setClient = MethodCall( - 'TextInput.setClient', [123, createFlutterConfig('none')]); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + createFlutterConfig('none'), + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); const MethodCall show = MethodCall('TextInput.show'); @@ -2324,9 +2237,11 @@ Future testMain() async { // checkInputEditingState, since on some platforms (e.g. Desktop Safari) // we don't put the input element into the DOM until we get its correct // dimensions from the framework. - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); expect(textEditing!.strategy.domElement!.tagName, 'INPUT'); @@ -2334,8 +2249,10 @@ Future testMain() async { }); test('none multiline mode works', () async { - final MethodCall setClient = MethodCall( - 'TextInput.setClient', [123, createFlutterConfig('none', isMultiline: true)]); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + createFlutterConfig('none', isMultiline: true), + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); const MethodCall show = MethodCall('TextInput.show'); @@ -2345,9 +2262,11 @@ Future testMain() async { // checkInputEditingState, since on some platforms (e.g. Desktop Safari) // we don't put the input element into the DOM until we get its correct // dimensions from the framework. - final MethodCall setSizeAndTransform = - configureSetSizeAndTransformMethodCall(150, 50, - Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList()); + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(), + ); sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); expect(textEditing!.strategy.domElement!.tagName, 'TEXTAREA'); @@ -2438,10 +2357,10 @@ Future testMain() async { /// text editing strategy for [OperatingSystem.android]. textEditing = HybridTextEditing(); - final MethodCall setClient = MethodCall( - 'TextInput.setClient', - [123, flutterMultilineConfig], - ); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterMultilineConfig, + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); // Editing shouldn't have started yet. @@ -2455,23 +2374,25 @@ Future testMain() async { // we don't put the input element into the DOM until we get its correct // dimensions from the framework. final List transform = Matrix4.translationValues(10.0, 20.0, 30.0).storage.toList(); - final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall(150, 50, transform); + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 150, + 50, + transform, + ); sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); - final DomHTMLTextAreaElement textarea = textEditing!.strategy.domElement! as DomHTMLTextAreaElement; + final DomHTMLTextAreaElement textarea = + textEditing!.strategy.domElement! as DomHTMLTextAreaElement; checkTextAreaEditingState(textarea, '', 0, 0); // Can set editing state and preserve new lines. - const MethodCall setEditingState = MethodCall( - 'TextInput.setEditingState', - { - 'text': '1\n2\n3\n4\n', - 'selectionBase': 8, - 'selectionExtent': 8, - 'composingBase': null, - 'composingExtent': null, - }, - ); + const MethodCall setEditingState = MethodCall('TextInput.setEditingState', { + 'text': '1\n2\n3\n4\n', + 'selectionBase': 8, + 'selectionExtent': 8, + 'composingBase': null, + 'composingExtent': null, + }); sendFrameworkMessage(codec.encodeMethodCall(setEditingState)); checkTextAreaEditingState(textarea, '1\n2\n3\n4\n', 8, 8); @@ -2525,34 +2446,21 @@ Future testMain() async { }); test('sends the correct input action as a platform message', () { - final int clientId = showKeyboard( - inputType: 'text', - inputAction: 'TextInputAction.next', - ); + final int clientId = showKeyboard(inputType: 'text', inputAction: 'TextInputAction.next'); // There should be no input action yet. expect(lastInputAction, isNull); - dispatchKeyboardEvent( - textEditing!.strategy.domElement!, - 'keydown', - keyCode: _kReturnKeyCode, - ); + dispatchKeyboardEvent(textEditing!.strategy.domElement!, 'keydown', keyCode: _kReturnKeyCode); expect(spy.messages, hasLength(1)); expect(spy.messages[0].channel, 'flutter/textinput'); expect(spy.messages[0].methodName, 'TextInputClient.performAction'); - expect( - spy.messages[0].methodArguments, - [clientId, 'TextInputAction.next'], - ); + expect(spy.messages[0].methodArguments, [clientId, 'TextInputAction.next']); }); test('sends input action in multi-line mode', () { - showKeyboard( - inputType: 'multiline', - inputAction: 'TextInputAction.next', - ); + showKeyboard(inputType: 'multiline', inputAction: 'TextInputAction.next'); final DomKeyboardEvent event = dispatchKeyboardEvent( textEditing!.strategy.domElement!, @@ -2564,10 +2472,7 @@ Future testMain() async { expect(spy.messages, hasLength(1)); expect(spy.messages[0].channel, 'flutter/textinput'); expect(spy.messages[0].methodName, 'TextInputClient.performAction'); - expect( - spy.messages[0].methodArguments, - [clientId, 'TextInputAction.next'], - ); + expect(spy.messages[0].methodArguments, [clientId, 'TextInputAction.next']); // And default behavior of keyboard event should have been prevented. // Only TextInputAction.newline should not prevent default behavior // for a multiline field. @@ -2585,7 +2490,11 @@ Future testMain() async { // The Safari strategy doesn't insert the input element into the DOM until // it has received the geometry information. final List transform = Matrix4.identity().storage.toList(); - final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall(10, 10, transform); + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 10, + 10, + transform, + ); sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); final DomElement input = textEditing!.strategy.domElement!; @@ -2619,10 +2528,14 @@ Future testMain() async { // Input is appended to view1. expect(view1.dom.textEditingHost.contains(input), isTrue); - sendFrameworkMessage(codec.encodeMethodCall(MethodCall( - 'TextInput.updateConfig', - createFlutterConfig('text', viewId: view2.viewId, autofillEnabled: false), - ))); + sendFrameworkMessage( + codec.encodeMethodCall( + MethodCall( + 'TextInput.updateConfig', + createFlutterConfig('text', viewId: view2.viewId, autofillEnabled: false), + ), + ), + ); // The input element is the same (no new element was created), but it has // moved to view2. @@ -2648,17 +2561,16 @@ Future testMain() async { textEditing = HybridTextEditing(); // Create a configuration with an AutofillGroup of three text fields. - final Map flutterMultiAutofillElementConfig = - createFlutterConfig( - 'text', - viewId: view.viewId, - autofillHint: 'username', - autofillHintsForFields: ['username', 'email', 'name'], - ); - final MethodCall setClient = MethodCall( - 'TextInput.setClient', - [123, flutterMultiAutofillElementConfig], + final Map flutterMultiAutofillElementConfig = createFlutterConfig( + 'text', + viewId: view.viewId, + autofillHint: 'username', + autofillHintsForFields: ['username', 'email', 'name'], ); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + flutterMultiAutofillElementConfig, + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); const MethodCall show = MethodCall('TextInput.show'); @@ -2666,7 +2578,11 @@ Future testMain() async { // The Safari strategy doesn't insert the input element into the DOM until // it has received the geometry information. final List transform = Matrix4.identity().storage.toList(); - final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall(10, 10, transform); + final MethodCall setSizeAndTransform = configureSetSizeAndTransformMethodCall( + 10, + 10, + transform, + ); sendFrameworkMessage(codec.encodeMethodCall(setSizeAndTransform)); final DomElement input = textEditing!.strategy.domElement!; @@ -2703,10 +2619,10 @@ Future testMain() async { autofillHint: 'username', autofillHintsForFields: ['username', 'email', 'name'], ); - final MethodCall setClient = MethodCall( - 'TextInput.setClient', - [123, autofillConfig1], - ); + final MethodCall setClient = MethodCall('TextInput.setClient', [ + 123, + autofillConfig1, + ]); sendFrameworkMessage(codec.encodeMethodCall(setClient)); const MethodCall show = MethodCall('TextInput.show'); @@ -2726,10 +2642,9 @@ Future testMain() async { autofillHint: 'username', autofillHintsForFields: ['username', 'email', 'name'], ); - sendFrameworkMessage(codec.encodeMethodCall(MethodCall( - 'TextInput.updateConfig', - autofillConfig2, - ))); + sendFrameworkMessage( + codec.encodeMethodCall(MethodCall('TextInput.updateConfig', autofillConfig2)), + ); // Input and form are in view2. expect(view2.dom.textEditingHost.contains(input), isTrue); @@ -2755,14 +2670,15 @@ Future testMain() async { group('EngineAutofillForm', () { test('validate multi element form', () { final List fields = createFieldValues( - ['username', 'password', 'newPassword'], - ['field1', 'field2', 'field3']); + ['username', 'password', 'newPassword'], + ['field1', 'field2', 'field3'], + ); final EngineAutofillForm autofillForm = EngineAutofillForm.fromFrameworkMessage( - kImplicitViewId, - createAutofillInfo('username', 'field1'), - fields, - )!; + kImplicitViewId, + createAutofillInfo('username', 'field1'), + fields, + )!; // Number of elements if number of fields sent to the constructor minus // one (for the focused text element). @@ -2777,20 +2693,18 @@ Future testMain() async { // 3 child nodes. expect(form.childNodes, hasLength(3)); - final DomHTMLInputElement firstElement = form.childNodes.toList()[0] as - DomHTMLInputElement; + final DomHTMLInputElement firstElement = form.childNodes.toList()[0] as DomHTMLInputElement; // Autofill value is applied to the element. - expect(firstElement.name, - BrowserAutofillHints.instance.flutterToEngine('password')); - expect(firstElement.id, - BrowserAutofillHints.instance.flutterToEngine('password')); + expect(firstElement.name, BrowserAutofillHints.instance.flutterToEngine('password')); + expect(firstElement.id, BrowserAutofillHints.instance.flutterToEngine('password')); expect(firstElement.type, 'password'); if (ui_web.browser.browserEngine == ui_web.BrowserEngine.firefox) { - expect(firstElement.name, - BrowserAutofillHints.instance.flutterToEngine('password')); + expect(firstElement.name, BrowserAutofillHints.instance.flutterToEngine('password')); } else { - expect(firstElement.autocomplete, - BrowserAutofillHints.instance.flutterToEngine('password')); + expect( + firstElement.autocomplete, + BrowserAutofillHints.instance.flutterToEngine('password'), + ); } // Editing state is applied to the element. @@ -2808,21 +2722,21 @@ Future testMain() async { ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit) { expect(firstElement.classList.contains('transparentTextEditing'), isTrue); } else { - expect(firstElement.classList.contains('transparentTextEditing'), - isFalse); + expect(firstElement.classList.contains('transparentTextEditing'), isFalse); } }); test('validate multi element form ids sorted for form id', () { final List fields = createFieldValues( - ['username', 'password', 'newPassword'], - ['zzyyxx', 'aabbcc', 'jjkkll']); + ['username', 'password', 'newPassword'], + ['zzyyxx', 'aabbcc', 'jjkkll'], + ); final EngineAutofillForm autofillForm = EngineAutofillForm.fromFrameworkMessage( - kImplicitViewId, - createAutofillInfo('username', 'field1'), - fields, - )!; + kImplicitViewId, + createAutofillInfo('username', 'field1'), + fields, + )!; expect(autofillForm.formIdentifier, 'aabbcc*jjkkll*zzyyxx'); }); @@ -2831,14 +2745,15 @@ Future testMain() async { expect(defaultTextEditingRoot.querySelectorAll('form'), isEmpty); final List fields = createFieldValues( - ['username', 'password', 'newPassword'], - ['field1', 'fields2', 'field3']); + ['username', 'password', 'newPassword'], + ['field1', 'fields2', 'field3'], + ); final EngineAutofillForm autofillForm = EngineAutofillForm.fromFrameworkMessage( - kImplicitViewId, - createAutofillInfo('username', 'field1'), - fields, - )!; + kImplicitViewId, + createAutofillInfo('username', 'field1'), + fields, + )!; final DomHTMLInputElement testInputElement = createDomHTMLInputElement(); autofillForm.placeForm(testInputElement); @@ -2859,16 +2774,13 @@ Future testMain() async { }); test('Validate single element form', () { - final List fields = createFieldValues( - ['username'], - ['field1'], - ); + final List fields = createFieldValues(['username'], ['field1']); final EngineAutofillForm autofillForm = EngineAutofillForm.fromFrameworkMessage( - kImplicitViewId, - createAutofillInfo('username', 'field1'), - fields, - )!; + kImplicitViewId, + createAutofillInfo('username', 'field1'), + fields, + )!; // The focused element is the only field. Form should be empty after // the initialization (focus element is appended later). @@ -2879,8 +2791,7 @@ Future testMain() async { final DomHTMLFormElement form = autofillForm.formElement; // Submit button is added to the form. expect(form.childNodes, isNotEmpty); - final DomHTMLInputElement inputElement = form.childNodes.toList()[0] as - DomHTMLInputElement; + final DomHTMLInputElement inputElement = form.childNodes.toList()[0] as DomHTMLInputElement; expect(inputElement.type, 'submit'); expect(inputElement.tabIndex, -1, reason: 'The input should not be reachable by keyboard'); @@ -2889,32 +2800,27 @@ Future testMain() async { }); test('Return null if no focused element', () { - final List fields = createFieldValues( - ['username'], - ['field1'], + final List fields = createFieldValues(['username'], ['field1']); + final EngineAutofillForm? autofillForm = EngineAutofillForm.fromFrameworkMessage( + kImplicitViewId, + null, + fields, ); - final EngineAutofillForm? autofillForm = - EngineAutofillForm.fromFrameworkMessage(kImplicitViewId, null, fields); expect(autofillForm, isNull); }); test('placeForm() should place element in correct position', () { - final List fields = createFieldValues([ - 'email', - 'username', - 'password', - ], [ - 'field1', - 'field2', - 'field3' - ]); + final List fields = createFieldValues( + ['email', 'username', 'password'], + ['field1', 'field2', 'field3'], + ); final EngineAutofillForm autofillForm = EngineAutofillForm.fromFrameworkMessage( - kImplicitViewId, - createAutofillInfo('email', 'field1'), - fields, - )!; + kImplicitViewId, + createAutofillInfo('email', 'field1'), + fields, + )!; expect(autofillForm.elements, hasLength(2)); @@ -2933,8 +2839,7 @@ Future testMain() async { testInputElement.name = 'email'; autofillForm.placeForm(testInputElement); - formChildNodes = autofillForm.formElement.childNodes.toList() - as List; + formChildNodes = autofillForm.formElement.childNodes.toList() as List; // email node should be placed before username expect(formChildNodes, hasLength(4)); expect(formChildNodes[0].name, 'email'); @@ -2944,61 +2849,49 @@ Future testMain() async { }); test( - 'hidden autofill elements should have a width and height of 0 on non-Safari browsers', - () { - final List fields = createFieldValues([ - 'email', - 'username', - 'password', - ], [ - 'field1', - 'field2', - 'field3' - ]); - final EngineAutofillForm autofillForm = - EngineAutofillForm.fromFrameworkMessage( - kImplicitViewId, - createAutofillInfo('email', 'field1'), - fields, - )!; - final List formChildNodes = - autofillForm.formElement.childNodes.toList() - as List; - final DomHTMLInputElement username = formChildNodes[0]; - final DomHTMLInputElement password = formChildNodes[1]; - - expect(username.name, 'username'); - expect(password.name, 'current-password'); - expect(username.style.width, '0px'); - expect(username.style.height, '0px'); - expect(username.style.pointerEvents, isNot('none')); - expect(password.style.width, '0px'); - expect(password.style.height, '0px'); - expect(password.style.pointerEvents, isNot('none')); - expect(autofillForm.formElement.style.pointerEvents, isNot('none')); - }, skip: isSafari); + 'hidden autofill elements should have a width and height of 0 on non-Safari browsers', + () { + final List fields = createFieldValues( + ['email', 'username', 'password'], + ['field1', 'field2', 'field3'], + ); + final EngineAutofillForm autofillForm = + EngineAutofillForm.fromFrameworkMessage( + kImplicitViewId, + createAutofillInfo('email', 'field1'), + fields, + )!; + final List formChildNodes = + autofillForm.formElement.childNodes.toList() as List; + final DomHTMLInputElement username = formChildNodes[0]; + final DomHTMLInputElement password = formChildNodes[1]; + + expect(username.name, 'username'); + expect(password.name, 'current-password'); + expect(username.style.width, '0px'); + expect(username.style.height, '0px'); + expect(username.style.pointerEvents, isNot('none')); + expect(password.style.width, '0px'); + expect(password.style.height, '0px'); + expect(password.style.pointerEvents, isNot('none')); + expect(autofillForm.formElement.style.pointerEvents, isNot('none')); + }, + skip: isSafari, + ); - test( - 'hidden autofill elements should not have a width and height of 0 on Safari', - () { - final List fields = createFieldValues([ - 'email', - 'username', - 'password', - ], [ - 'field1', - 'field2', - 'field3' - ]); + test('hidden autofill elements should not have a width and height of 0 on Safari', () { + final List fields = createFieldValues( + ['email', 'username', 'password'], + ['field1', 'field2', 'field3'], + ); final EngineAutofillForm autofillForm = EngineAutofillForm.fromFrameworkMessage( - kImplicitViewId, - createAutofillInfo('email', 'field1'), - fields, - )!; + kImplicitViewId, + createAutofillInfo('email', 'field1'), + fields, + )!; final List formChildNodes = - autofillForm.formElement.childNodes.toList() - as List; + autofillForm.formElement.childNodes.toList() as List; final DomHTMLInputElement username = formChildNodes[0]; final DomHTMLInputElement password = formChildNodes[1]; expect(username.name, 'username'); @@ -3013,47 +2906,43 @@ Future testMain() async { }, skip: !isSafari); test( - 'the focused element within a form should explicitly set pointer events on Safari', - () { - final List fields = createFieldValues([ - 'email', - 'username', - 'password', - ], [ - 'field1', - 'field2', - 'field3' - ]); - final EngineAutofillForm autofillForm = - EngineAutofillForm.fromFrameworkMessage( - kImplicitViewId, - createAutofillInfo('email', 'field1'), - fields, - )!; - - final DomHTMLInputElement testInputElement = createDomHTMLInputElement(); - testInputElement.name = 'email'; - autofillForm.placeForm(testInputElement); - - final List formChildNodes = - autofillForm.formElement.childNodes.toList() - as List; - final DomHTMLInputElement email = formChildNodes[0]; - final DomHTMLInputElement username = formChildNodes[1]; - final DomHTMLInputElement password = formChildNodes[2]; - - expect(email.name, 'email'); - expect(username.name, 'username'); - expect(password.name, 'current-password'); - - // pointer events are none on the form and all non-focused elements - expect(autofillForm.formElement.style.pointerEvents, 'none'); - expect(username.style.pointerEvents, 'none'); - expect(password.style.pointerEvents, 'none'); - - // pointer events are set to all on the activeDomElement - expect(email.style.pointerEvents, 'all'); - }, skip: !isSafari); + 'the focused element within a form should explicitly set pointer events on Safari', + () { + final List fields = createFieldValues( + ['email', 'username', 'password'], + ['field1', 'field2', 'field3'], + ); + final EngineAutofillForm autofillForm = + EngineAutofillForm.fromFrameworkMessage( + kImplicitViewId, + createAutofillInfo('email', 'field1'), + fields, + )!; + + final DomHTMLInputElement testInputElement = createDomHTMLInputElement(); + testInputElement.name = 'email'; + autofillForm.placeForm(testInputElement); + + final List formChildNodes = + autofillForm.formElement.childNodes.toList() as List; + final DomHTMLInputElement email = formChildNodes[0]; + final DomHTMLInputElement username = formChildNodes[1]; + final DomHTMLInputElement password = formChildNodes[2]; + + expect(email.name, 'email'); + expect(username.name, 'username'); + expect(password.name, 'current-password'); + + // pointer events are none on the form and all non-focused elements + expect(autofillForm.formElement.style.pointerEvents, 'none'); + expect(username.style.pointerEvents, 'none'); + expect(password.style.pointerEvents, 'none'); + + // pointer events are set to all on the activeDomElement + expect(email.style.pointerEvents, 'all'); + }, + skip: !isSafari, + ); tearDown(() { clearForms(); @@ -3067,110 +2956,117 @@ Future testMain() async { test('autofill has correct value', () { final AutofillInfo autofillInfo = AutofillInfo.fromFrameworkMessage( - createAutofillInfo(testHint, testId)); + createAutofillInfo(testHint, testId), + ); // Hint sent from the framework is converted to the hint compatible with // browsers. - expect(autofillInfo.autofillHint, - BrowserAutofillHints.instance.flutterToEngine(testHint)); + expect(autofillInfo.autofillHint, BrowserAutofillHints.instance.flutterToEngine(testHint)); expect(autofillInfo.uniqueIdentifier, testId); }); test('input with autofill hint', () { final AutofillInfo autofillInfo = AutofillInfo.fromFrameworkMessage( - createAutofillInfo(testHint, testId)); + createAutofillInfo(testHint, testId), + ); final DomHTMLInputElement testInputElement = createDomHTMLInputElement(); autofillInfo.applyToDomElement(testInputElement); // Hint sent from the framework is converted to the hint compatible with // browsers. - expect(testInputElement.name, - BrowserAutofillHints.instance.flutterToEngine(testHint)); - expect(testInputElement.id, - BrowserAutofillHints.instance.flutterToEngine(testHint)); + expect(testInputElement.name, BrowserAutofillHints.instance.flutterToEngine(testHint)); + expect(testInputElement.id, BrowserAutofillHints.instance.flutterToEngine(testHint)); expect(testInputElement.type, 'text'); if (ui_web.browser.browserEngine == ui_web.BrowserEngine.firefox) { - expect(testInputElement.name, - BrowserAutofillHints.instance.flutterToEngine(testHint)); + expect(testInputElement.name, BrowserAutofillHints.instance.flutterToEngine(testHint)); } else { - expect(testInputElement.autocomplete, - BrowserAutofillHints.instance.flutterToEngine(testHint)); + expect( + testInputElement.autocomplete, + BrowserAutofillHints.instance.flutterToEngine(testHint), + ); } }); test('textarea with autofill hint', () { final AutofillInfo autofillInfo = AutofillInfo.fromFrameworkMessage( - createAutofillInfo(testHint, testId)); + createAutofillInfo(testHint, testId), + ); final DomHTMLTextAreaElement testInputElement = createDomHTMLTextAreaElement(); autofillInfo.applyToDomElement(testInputElement); // Hint sent from the framework is converted to the hint compatible with // browsers. - expect(testInputElement.name, - BrowserAutofillHints.instance.flutterToEngine(testHint)); - expect(testInputElement.id, - BrowserAutofillHints.instance.flutterToEngine(testHint)); - expect(testInputElement.getAttribute('autocomplete'), - BrowserAutofillHints.instance.flutterToEngine(testHint)); + expect(testInputElement.name, BrowserAutofillHints.instance.flutterToEngine(testHint)); + expect(testInputElement.id, BrowserAutofillHints.instance.flutterToEngine(testHint)); + expect( + testInputElement.getAttribute('autocomplete'), + BrowserAutofillHints.instance.flutterToEngine(testHint), + ); }); test('password autofill hint', () { final AutofillInfo autofillInfo = AutofillInfo.fromFrameworkMessage( - createAutofillInfo(testPasswordHint, testId)); + createAutofillInfo(testPasswordHint, testId), + ); final DomHTMLInputElement testInputElement = createDomHTMLInputElement(); autofillInfo.applyToDomElement(testInputElement); // Hint sent from the framework is converted to the hint compatible with // browsers. - expect(testInputElement.name, - BrowserAutofillHints.instance.flutterToEngine(testPasswordHint)); - expect(testInputElement.id, - BrowserAutofillHints.instance.flutterToEngine(testPasswordHint)); + expect( + testInputElement.name, + BrowserAutofillHints.instance.flutterToEngine(testPasswordHint), + ); + expect(testInputElement.id, BrowserAutofillHints.instance.flutterToEngine(testPasswordHint)); expect(testInputElement.type, 'password'); - expect(testInputElement.getAttribute('autocomplete'), - BrowserAutofillHints.instance.flutterToEngine(testPasswordHint)); + expect( + testInputElement.getAttribute('autocomplete'), + BrowserAutofillHints.instance.flutterToEngine(testPasswordHint), + ); }); test('autofill with no hints', () { final AutofillInfo autofillInfo = AutofillInfo.fromFrameworkMessage( - createAutofillInfo(null, testId)); + createAutofillInfo(null, testId), + ); final DomHTMLInputElement testInputElement = createDomHTMLInputElement(); autofillInfo.applyToDomElement(testInputElement); - expect(testInputElement.autocomplete,'on'); + expect(testInputElement.autocomplete, 'on'); expect(testInputElement.placeholder, isEmpty); }); test('TextArea autofill with no hints', () { final AutofillInfo autofillInfo = AutofillInfo.fromFrameworkMessage( - createAutofillInfo(null, testId)); + createAutofillInfo(null, testId), + ); final DomHTMLTextAreaElement testInputElement = createDomHTMLTextAreaElement(); autofillInfo.applyToDomElement(testInputElement); - expect(testInputElement.getAttribute('autocomplete'),'on'); + expect(testInputElement.getAttribute('autocomplete'), 'on'); expect(testInputElement.placeholder, isEmpty); }); test('autofill with only placeholder', () { final AutofillInfo autofillInfo = AutofillInfo.fromFrameworkMessage( - createAutofillInfo(null, testId, placeholder: 'enter your password')); + createAutofillInfo(null, testId, placeholder: 'enter your password'), + ); final DomHTMLTextAreaElement testInputElement = createDomHTMLTextAreaElement(); autofillInfo.applyToDomElement(testInputElement); - expect(testInputElement.getAttribute('autocomplete'),'on'); + expect(testInputElement.getAttribute('autocomplete'), 'on'); expect(testInputElement.placeholder, 'enter your password'); }); // Regression test for https://github.com/flutter/flutter/issues/135542 test('autofill with middleName hint', () { - expect(BrowserAutofillHints.instance.flutterToEngine('middleName'), - 'additional-name'); + expect(BrowserAutofillHints.instance.flutterToEngine('middleName'), 'additional-name'); }); }); @@ -3178,8 +3074,7 @@ Future testMain() async { EditingState editingState; setUp(() { - editingStrategy = - GloballyPositionedTextEditingStrategy(HybridTextEditing()); + editingStrategy = GloballyPositionedTextEditingStrategy(HybridTextEditing()); editingStrategy!.enable( singlelineConfig, onChange: trackEditingState, @@ -3206,13 +3101,10 @@ Future testMain() async { }); test('Sets default composing offsets if none given', () { - final EditingState editingState = - EditingState(text: 'Test', baseOffset: 2, extentOffset: 4); - final EditingState editingStateFromFrameworkMsg = - EditingState.fromFrameworkMessage({ - 'selectionBase': 10, - 'selectionExtent': 4, - }); + final EditingState editingState = EditingState(text: 'Test', baseOffset: 2, extentOffset: 4); + final EditingState editingStateFromFrameworkMsg = EditingState.fromFrameworkMessage( + {'selectionBase': 10, 'selectionExtent': 4}, + ); expect(editingState.composingBaseOffset, -1); expect(editingState.composingExtentOffset, -1); @@ -3222,10 +3114,8 @@ Future testMain() async { }); test('Correctly identifies min and max offsets', () { - final EditingState flippedEditingState = - EditingState(baseOffset: 10, extentOffset: 4); - final EditingState normalEditingState = - EditingState(baseOffset: 2, extentOffset: 6); + final EditingState flippedEditingState = EditingState(baseOffset: 10, extentOffset: 4); + final EditingState normalEditingState = EditingState(baseOffset: 2, extentOffset: 6); expect(flippedEditingState.minOffset, 4); expect(flippedEditingState.maxOffset, 10); @@ -3236,8 +3126,7 @@ Future testMain() async { test('Configure input element from the editing state', () { final DomHTMLInputElement input = defaultTextEditingRoot.querySelector('input')! as DomHTMLInputElement; - editingState = - EditingState(text: 'Test', baseOffset: 1, extentOffset: 2); + editingState = EditingState(text: 'Test', baseOffset: 1, extentOffset: 2); editingState.applyToDomElement(input); @@ -3256,8 +3145,7 @@ Future testMain() async { final DomHTMLTextAreaElement textArea = defaultTextEditingRoot.querySelector('textarea')! as DomHTMLTextAreaElement; - editingState = - EditingState(text: 'Test', baseOffset: 1, extentOffset: 2); + editingState = EditingState(text: 'Test', baseOffset: 1, extentOffset: 2); editingState.applyToDomElement(textArea); @@ -3266,12 +3154,10 @@ Future testMain() async { expect(textArea.selectionEnd, 2); }); - test('Configure input element editing state for a flipped base and extent', - () { + test('Configure input element editing state for a flipped base and extent', () { final DomHTMLInputElement input = defaultTextEditingRoot.querySelector('input')! as DomHTMLInputElement; - editingState = - EditingState(text: 'Hello World', baseOffset: 10, extentOffset: 2); + editingState = EditingState(text: 'Hello World', baseOffset: 10, extentOffset: 2); editingState.applyToDomElement(input); @@ -3323,8 +3209,8 @@ Future testMain() async { group('comparing editing states', () { test('From dom element', () { - final DomHTMLInputElement input = defaultTextEditingRoot.querySelector('input')! - as DomHTMLInputElement; + final DomHTMLInputElement input = + defaultTextEditingRoot.querySelector('input')! as DomHTMLInputElement; input.value = 'Test'; input.selectionStart = 1; input.selectionEnd = 2; @@ -3341,10 +3227,8 @@ Future testMain() async { }); test('Takes flipped base and extent offsets into account', () { - final EditingState flippedEditingState = - EditingState(baseOffset: 10, extentOffset: 4); - final EditingState normalEditingState = - EditingState(baseOffset: 4, extentOffset: 10); + final EditingState flippedEditingState = EditingState(baseOffset: 10, extentOffset: 4); + final EditingState normalEditingState = EditingState(baseOffset: 4, extentOffset: 10); expect(normalEditingState, flippedEditingState); @@ -3352,12 +3236,21 @@ Future testMain() async { }); test('takes composition range into account', () { - final EditingState editingState1 = EditingState(composingBaseOffset: 1, composingExtentOffset: 2); - final EditingState editingState2 = EditingState(composingBaseOffset: 1, composingExtentOffset: 2); - final EditingState editingState3 = EditingState(composingBaseOffset: 4, composingExtentOffset: 8); - - expect(editingState1, editingState2); - expect(editingState1, isNot(editingState3)); + final EditingState editingState1 = EditingState( + composingBaseOffset: 1, + composingExtentOffset: 2, + ); + final EditingState editingState2 = EditingState( + composingBaseOffset: 1, + composingExtentOffset: 2, + ); + final EditingState editingState3 = EditingState( + composingBaseOffset: 4, + composingExtentOffset: 8, + ); + + expect(editingState1, editingState2); + expect(editingState1, isNot(editingState3)); }); }); }); @@ -3368,9 +3261,22 @@ Future testMain() async { test('Verify correct delta is inferred - insertion', () { final EditingState newEditState = EditingState(text: 'world', baseOffset: 5, extentOffset: 5); final EditingState lastEditState = EditingState(text: 'worl', baseOffset: 4, extentOffset: 4); - final TextEditingDeltaState deltaState = TextEditingDeltaState(oldText: 'worl', deltaText: 'd', deltaStart: 4, deltaEnd: 4, baseOffset: -1, extentOffset: -1, composingOffset: -1, composingExtent: -1); + final TextEditingDeltaState deltaState = TextEditingDeltaState( + oldText: 'worl', + deltaText: 'd', + deltaStart: 4, + deltaEnd: 4, + baseOffset: -1, + extentOffset: -1, + composingOffset: -1, + composingExtent: -1, + ); - final TextEditingDeltaState textEditingDeltaState = TextEditingDeltaState.inferDeltaState(newEditState, lastEditState, deltaState); + final TextEditingDeltaState textEditingDeltaState = TextEditingDeltaState.inferDeltaState( + newEditState, + lastEditState, + deltaState, + ); expect(textEditingDeltaState.oldText, 'worl'); expect(textEditingDeltaState.deltaText, 'd'); @@ -3384,7 +3290,11 @@ Future testMain() async { test('Verify correct delta is inferred - Backward deletion - Empty selection', () { final EditingState newEditState = EditingState(text: 'worl', baseOffset: 4, extentOffset: 4); - final EditingState lastEditState = EditingState(text: 'world', baseOffset: 5, extentOffset: 5); + final EditingState lastEditState = EditingState( + text: 'world', + baseOffset: 5, + extentOffset: 5, + ); // `deltaState.deltaEnd` is initialized accordingly to what is done in `DefaultTextEditingStrategy.handleBeforeInput` final TextEditingDeltaState deltaState = TextEditingDeltaState( oldText: 'world', @@ -3395,7 +3305,11 @@ Future testMain() async { composingExtent: -1, ); - final TextEditingDeltaState textEditingDeltaState = TextEditingDeltaState.inferDeltaState(newEditState, lastEditState, deltaState); + final TextEditingDeltaState textEditingDeltaState = TextEditingDeltaState.inferDeltaState( + newEditState, + lastEditState, + deltaState, + ); expect(textEditingDeltaState.oldText, 'world'); expect(textEditingDeltaState.deltaText, ''); @@ -3409,7 +3323,11 @@ Future testMain() async { test('Verify correct delta is inferred - Forward deletion - Empty selection', () { final EditingState newEditState = EditingState(text: 'worl', baseOffset: 4, extentOffset: 4); - final EditingState lastEditState = EditingState(text: 'world', baseOffset: 4, extentOffset: 4); + final EditingState lastEditState = EditingState( + text: 'world', + baseOffset: 4, + extentOffset: 4, + ); // `deltaState.deltaEnd` is initialized accordingly to what is done in `DefaultTextEditingStrategy.handleBeforeInput` final TextEditingDeltaState deltaState = TextEditingDeltaState( oldText: 'world', @@ -3420,7 +3338,11 @@ Future testMain() async { composingExtent: -1, ); - final TextEditingDeltaState textEditingDeltaState = TextEditingDeltaState.inferDeltaState(newEditState, lastEditState, deltaState); + final TextEditingDeltaState textEditingDeltaState = TextEditingDeltaState.inferDeltaState( + newEditState, + lastEditState, + deltaState, + ); expect(textEditingDeltaState.oldText, 'world'); expect(textEditingDeltaState.deltaText, ''); @@ -3434,7 +3356,11 @@ Future testMain() async { test('Verify correct delta is inferred - Deletion - Non-empty selection', () { final EditingState newEditState = EditingState(text: 'w', baseOffset: 1, extentOffset: 1); - final EditingState lastEditState = EditingState(text: 'world', baseOffset: 1, extentOffset: 5); + final EditingState lastEditState = EditingState( + text: 'world', + baseOffset: 1, + extentOffset: 5, + ); // `deltaState.deltaEnd` is initialized accordingly to what is done in `DefaultTextEditingStrategy.handleBeforeInput` final TextEditingDeltaState deltaState = TextEditingDeltaState( oldText: 'world', @@ -3445,7 +3371,11 @@ Future testMain() async { composingExtent: -1, ); - final TextEditingDeltaState textEditingDeltaState = TextEditingDeltaState.inferDeltaState(newEditState, lastEditState, deltaState); + final TextEditingDeltaState textEditingDeltaState = TextEditingDeltaState.inferDeltaState( + newEditState, + lastEditState, + deltaState, + ); expect(textEditingDeltaState.oldText, 'world'); expect(textEditingDeltaState.deltaText, ''); @@ -3459,10 +3389,27 @@ Future testMain() async { test('Verify correct delta is inferred - composing region replacement', () { final EditingState newEditState = EditingState(text: '你好吗', baseOffset: 3, extentOffset: 3); - final EditingState lastEditState = EditingState(text: 'ni hao ma', baseOffset: 9, extentOffset: 9); - final TextEditingDeltaState deltaState = TextEditingDeltaState(oldText: 'ni hao ma', deltaText: '你好吗', deltaStart: 9, deltaEnd: 9, baseOffset: -1, extentOffset: -1, composingOffset: 0, composingExtent: 9); + final EditingState lastEditState = EditingState( + text: 'ni hao ma', + baseOffset: 9, + extentOffset: 9, + ); + final TextEditingDeltaState deltaState = TextEditingDeltaState( + oldText: 'ni hao ma', + deltaText: '你好吗', + deltaStart: 9, + deltaEnd: 9, + baseOffset: -1, + extentOffset: -1, + composingOffset: 0, + composingExtent: 9, + ); - final TextEditingDeltaState textEditingDeltaState = TextEditingDeltaState.inferDeltaState(newEditState, lastEditState, deltaState); + final TextEditingDeltaState textEditingDeltaState = TextEditingDeltaState.inferDeltaState( + newEditState, + lastEditState, + deltaState, + ); expect(textEditingDeltaState.oldText, 'ni hao ma'); expect(textEditingDeltaState.deltaText, '你好吗'); @@ -3475,11 +3422,32 @@ Future testMain() async { }); test('Verify correct delta is inferred for double space to insert a period', () { - final EditingState newEditState = EditingState(text: 'hello. ', baseOffset: 7, extentOffset: 7); - final EditingState lastEditState = EditingState(text: 'hello ', baseOffset: 6, extentOffset: 6); - final TextEditingDeltaState deltaState = TextEditingDeltaState(oldText: 'hello ', deltaText: '. ', deltaStart: 6, deltaEnd: 6, baseOffset: -1, extentOffset: -1, composingOffset: -1, composingExtent: -1); + final EditingState newEditState = EditingState( + text: 'hello. ', + baseOffset: 7, + extentOffset: 7, + ); + final EditingState lastEditState = EditingState( + text: 'hello ', + baseOffset: 6, + extentOffset: 6, + ); + final TextEditingDeltaState deltaState = TextEditingDeltaState( + oldText: 'hello ', + deltaText: '. ', + deltaStart: 6, + deltaEnd: 6, + baseOffset: -1, + extentOffset: -1, + composingOffset: -1, + composingExtent: -1, + ); - final TextEditingDeltaState textEditingDeltaState = TextEditingDeltaState.inferDeltaState(newEditState, lastEditState, deltaState); + final TextEditingDeltaState textEditingDeltaState = TextEditingDeltaState.inferDeltaState( + newEditState, + lastEditState, + deltaState, + ); expect(textEditingDeltaState.oldText, 'hello '); expect(textEditingDeltaState.deltaText, '. '); @@ -3494,9 +3462,22 @@ Future testMain() async { test('Verify correct delta is inferred for accent menu', () { final EditingState newEditState = EditingState(text: 'à', baseOffset: 1, extentOffset: 1); final EditingState lastEditState = EditingState(text: 'a', baseOffset: 1, extentOffset: 1); - final TextEditingDeltaState deltaState = TextEditingDeltaState(oldText: 'a', deltaText: 'à', deltaStart: 1, deltaEnd: 1, baseOffset: -1, extentOffset: -1, composingOffset: -1, composingExtent: -1); + final TextEditingDeltaState deltaState = TextEditingDeltaState( + oldText: 'a', + deltaText: 'à', + deltaStart: 1, + deltaEnd: 1, + baseOffset: -1, + extentOffset: -1, + composingOffset: -1, + composingExtent: -1, + ); - final TextEditingDeltaState textEditingDeltaState = TextEditingDeltaState.inferDeltaState(newEditState, lastEditState, deltaState); + final TextEditingDeltaState textEditingDeltaState = TextEditingDeltaState.inferDeltaState( + newEditState, + lastEditState, + deltaState, + ); expect(textEditingDeltaState.oldText, 'a'); expect(textEditingDeltaState.deltaText, 'à'); @@ -3508,7 +3489,7 @@ Future testMain() async { expect(textEditingDeltaState.composingExtent, -1); }); - test('Delta state is cleared after setting editing state', (){ + test('Delta state is cleared after setting editing state', () { editingStrategy!.enable( multilineConfig, onChange: trackEditingState, @@ -3517,13 +3498,12 @@ Future testMain() async { final DomHTMLInputElement input = editingStrategy!.domElement! as DomHTMLInputElement; input.value = 'foo bar'; input.dispatchEvent(createDomEvent('Event', 'input')); - expect( - lastEditingState, - EditingState(text: 'foo bar', baseOffset: 7, extentOffset: 7), - ); + expect(lastEditingState, EditingState(text: 'foo bar', baseOffset: 7, extentOffset: 7)); expect(editingStrategy!.editingDeltaState.oldText, 'foo bar'); - editingStrategy!.setEditingState(EditingState(text: 'foo bar baz', baseOffset: 11, extentOffset: 11)); + editingStrategy!.setEditingState( + EditingState(text: 'foo bar baz', baseOffset: 11, extentOffset: 11), + ); input.dispatchEvent(createDomEvent('Event', 'input')); expect(editingStrategy?.editingDeltaState.oldText, 'foo bar baz'); }); @@ -3565,25 +3545,18 @@ Future testMain() async { final DomHTMLElement input = editingStrategy!.activeDomElement; expect(input.style.getPropertyValue('forced-color-adjust'), 'none'); - // TODO(hterkelsen): Firefox does not support forced-color-adjust even - // though it supports forced-colors. Safari doesn't support forced-colors - // so this isn't a problem there. + // TODO(hterkelsen): Firefox does not support forced-color-adjust even + // though it supports forced-colors. Safari doesn't support forced-colors + // so this isn't a problem there. }, skip: isFirefox || isSafari); }); } -DomKeyboardEvent dispatchKeyboardEvent( - DomEventTarget target, - String type, { - required int keyCode, -}) { +DomKeyboardEvent dispatchKeyboardEvent(DomEventTarget target, String type, {required int keyCode}) { final Object jsKeyboardEvent = js_util.getProperty(domWindow, 'KeyboardEvent'); final List eventArgs = [ type, - js_util.jsify({ - 'keyCode': keyCode, - 'cancelable': true, - }), + js_util.jsify({'keyCode': keyCode, 'cancelable': true}), ]; final DomKeyboardEvent event = js_util.callConstructor( jsKeyboardEvent, @@ -3594,8 +3567,13 @@ DomKeyboardEvent dispatchKeyboardEvent( return event; } -MethodCall configureSetStyleMethodCall(int fontSize, String fontFamily, - int textAlignIndex, int? fontWeightIndex, int textDirectionIndex) { +MethodCall configureSetStyleMethodCall( + int fontSize, + String fontFamily, + int textAlignIndex, + int? fontWeightIndex, + int textDirectionIndex, +) { return MethodCall('TextInput.setStyle', { 'fontSize': fontSize, 'fontFamily': fontFamily, @@ -3605,12 +3583,11 @@ MethodCall configureSetStyleMethodCall(int fontSize, String fontFamily, }); } -MethodCall configureSetSizeAndTransformMethodCall( - int width, int height, List transform) { +MethodCall configureSetSizeAndTransformMethodCall(int width, int height, List transform) { return MethodCall('TextInput.setEditableSizeAndTransform', { 'width': width, 'height': height, - 'transform': transform + 'transform': transform, }); } @@ -3628,8 +3605,7 @@ void cleanTestFlags() { ui_web.browser.debugOperatingSystemOverride = null; } -void checkInputEditingState( - DomElement? element, String text, int start, int end) { +void checkInputEditingState(DomElement? element, String text, int start, int end) { expect(element, isNotNull); expect(domInstanceOfString(element, 'HTMLInputElement'), true); final DomHTMLInputElement input = element! as DomHTMLInputElement; @@ -3646,19 +3622,16 @@ void clearBackUpDomElementIfExists() { domElementsToRemove.addAll(defaultTextEditingRoot.querySelectorAll('input').cast()); } if (defaultTextEditingRoot.querySelectorAll('textarea').isNotEmpty) { - domElementsToRemove.addAll(defaultTextEditingRoot.querySelectorAll('textarea').cast()); + domElementsToRemove.addAll( + defaultTextEditingRoot.querySelectorAll('textarea').cast(), + ); } domElementsToRemove.forEach(_removeNode); } -void _removeNode(DomElement n)=> n.remove(); +void _removeNode(DomElement n) => n.remove(); -void checkTextAreaEditingState( - DomHTMLTextAreaElement textarea, - String text, - int start, - int end, -) { +void checkTextAreaEditingState(DomHTMLTextAreaElement textarea, String text, int start, int end) { expect(defaultTextEditingRoot.ownerDocument?.activeElement, textarea); expect(textarea.value, text); expect(textarea.selectionStart, start); @@ -3698,15 +3671,18 @@ Map createFlutterConfig( 'inputAction': inputAction ?? 'TextInputAction.done', 'textCapitalization': textCapitalization, if (autofillEnabled) - 'autofill': createAutofillInfo(autofillHint, autofillHint ?? 'bogusId', placeholder: placeholderText), + 'autofill': createAutofillInfo( + autofillHint, + autofillHint ?? 'bogusId', + placeholder: placeholderText, + ), if (autofillEnabled && autofillHintsForFields != null) - 'fields': - createFieldValues(autofillHintsForFields, autofillHintsForFields), + 'fields': createFieldValues(autofillHintsForFields, autofillHintsForFields), 'enableDeltaModel': enableDeltaModel, }; } -Map createAutofillInfo(String? hint, String uniqueId, { String? placeholder }) => +Map createAutofillInfo(String? hint, String uniqueId, {String? placeholder}) => { 'uniqueIdentifier': uniqueId, if (hint != null) 'hints': [hint], @@ -3734,16 +3710,11 @@ List createFieldValues(List hints, List uniqueIds) { return testFields; } -Map createOneFieldValue(String hint, String uniqueId) => - { - 'inputType': { - 'name': 'TextInputType.text', - 'signed': null, - 'decimal': null - }, - 'textCapitalization': 'TextCapitalization.none', - 'autofill': createAutofillInfo(hint, uniqueId) - }; +Map createOneFieldValue(String hint, String uniqueId) => { + 'inputType': {'name': 'TextInputType.text', 'signed': null, 'decimal': null}, + 'textCapitalization': 'TextCapitalization.none', + 'autofill': createAutofillInfo(hint, uniqueId), +}; /// In order to not leak test state, clean up the forms from dom if any remains. void clearForms() { diff --git a/lib/web_ui/test/engine/util_test.dart b/lib/web_ui/test/engine/util_test.dart index 1578b6c393edb..9f93667e3283f 100644 --- a/lib/web_ui/test/engine/util_test.dart +++ b/lib/web_ui/test/engine/util_test.dart @@ -14,7 +14,11 @@ final Float32List identityTransform = Matrix4.identity().storage; final Float32List xTranslation = (Matrix4.identity()..translate(10)).storage; final Float32List yTranslation = (Matrix4.identity()..translate(0, 10)).storage; final Float32List zTranslation = (Matrix4.identity()..translate(0, 0, 10)).storage; -final Float32List scaleAndTranslate2d = (Matrix4.identity()..scale(2, 3, 1)..translate(4, 5)).storage; +final Float32List scaleAndTranslate2d = + (Matrix4.identity() + ..scale(2, 3, 1) + ..translate(4, 5)) + .storage; final Float32List rotation2d = (Matrix4.identity()..rotateZ(0.2)).storage; void main() { @@ -46,18 +50,9 @@ void testMain() { ui_web.browser.debugOperatingSystemOverride = ui_web.OperatingSystem.iOs; debugIsIOS15 = false; - expect( - canonicalizeFontFamily('sans-serif'), - 'sans-serif', - ); - expect( - canonicalizeFontFamily('foo'), - '"foo", -apple-system, BlinkMacSystemFont, sans-serif', - ); - expect( - canonicalizeFontFamily('.SF Pro Text'), - '-apple-system, BlinkMacSystemFont', - ); + expect(canonicalizeFontFamily('sans-serif'), 'sans-serif'); + expect(canonicalizeFontFamily('foo'), '"foo", -apple-system, BlinkMacSystemFont, sans-serif'); + expect(canonicalizeFontFamily('.SF Pro Text'), '-apple-system, BlinkMacSystemFont'); ui_web.browser.debugOperatingSystemOverride = null; debugIsIOS15 = null; @@ -67,18 +62,9 @@ void testMain() { ui_web.browser.debugOperatingSystemOverride = ui_web.OperatingSystem.iOs; debugIsIOS15 = true; - expect( - canonicalizeFontFamily('sans-serif'), - 'sans-serif', - ); - expect( - canonicalizeFontFamily('foo'), - '"foo", BlinkMacSystemFont, sans-serif', - ); - expect( - canonicalizeFontFamily('.SF Pro Text'), - 'BlinkMacSystemFont', - ); + expect(canonicalizeFontFamily('sans-serif'), 'sans-serif'); + expect(canonicalizeFontFamily('foo'), '"foo", BlinkMacSystemFont, sans-serif'); + expect(canonicalizeFontFamily('.SF Pro Text'), 'BlinkMacSystemFont'); ui_web.browser.debugOperatingSystemOverride = null; debugIsIOS15 = null; @@ -136,7 +122,7 @@ void testMain() { return 'this is an error'; }); fail('Expected it to throw'); - } on Exception catch(exception) { + } on Exception catch (exception) { expect('$exception', contains('this is an error')); } }); @@ -152,7 +138,7 @@ void testMain() { try { await stringFuture; fail('Expected it to throw'); - } on Exception catch(exception) { + } on Exception catch (exception) { expect('$exception', contains('operation failed')); } }); @@ -164,7 +150,7 @@ void testMain() { return null; }); fail('Expected it to throw'); - } on Exception catch(exception) { + } on Exception catch (exception) { expect('$exception', contains('operation failed')); } }); diff --git a/lib/web_ui/test/engine/vector_math_test.dart b/lib/web_ui/test/engine/vector_math_test.dart index 4afadd31fc245..51dfc1b3fbc24 100644 --- a/lib/web_ui/test/engine/vector_math_test.dart +++ b/lib/web_ui/test/engine/vector_math_test.dart @@ -15,28 +15,21 @@ void main() { void testMain() { test('toMatrix32', () { - final List data = [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 - ]; + final List data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; final Float32List m32 = toMatrix32(Float64List.fromList(data)); expect(m32, Float32List.fromList(data)); }); test('FastMatrix32.transform', () { - final FastMatrix32 fast = FastMatrix32(Float32List.fromList([ - 2, 1, 0, 0, - 1, 3, 0, 0, - 0, 0, 0, 0, - 4, 5, 0, 1 - ])); + final FastMatrix32 fast = FastMatrix32( + Float32List.fromList([2, 1, 0, 0, 1, 3, 0, 0, 0, 0, 0, 0, 4, 5, 0, 1]), + ); fast.transform(6, 7); // Just make sure that the fast version produces a result consistent with // the slow version. final Matrix4 slow = Matrix4.fromFloat32List(fast.matrix); - final Float32List slowTransformed = Float32List.fromList([ - 6, 7, 0 - ]); + final Float32List slowTransformed = Float32List.fromList([6, 7, 0]); slow.transform3(slowTransformed); expect(fast.transformedX, slowTransformed[0]); diff --git a/lib/web_ui/test/engine/view/view_constraints_test.dart b/lib/web_ui/test/engine/view/view_constraints_test.dart index e9039ac6cc8b8..044888e120c1e 100644 --- a/lib/web_ui/test/engine/view/view_constraints_test.dart +++ b/lib/web_ui/test/engine/view/view_constraints_test.dart @@ -21,121 +21,115 @@ Future testMain() async { group('ViewConstraints.fromJs', () { test('Negative min constraints -> Assertion error.', () async { expect( - () => ViewConstraints.fromJs( - JsViewConstraints( - minWidth: -1, - ), - size), - throwsAssertionError); + () => ViewConstraints.fromJs(JsViewConstraints(minWidth: -1), size), + throwsAssertionError, + ); expect( - () => ViewConstraints.fromJs( - JsViewConstraints( - minHeight: -1, - ), - size), - throwsAssertionError); + () => ViewConstraints.fromJs(JsViewConstraints(minHeight: -1), size), + throwsAssertionError, + ); }); test('Infinite min constraints -> Assertion error.', () async { expect( - () => ViewConstraints.fromJs( - JsViewConstraints( - minWidth: double.infinity, - ), - size), - throwsAssertionError); + () => ViewConstraints.fromJs(JsViewConstraints(minWidth: double.infinity), size), + throwsAssertionError, + ); expect( - () => ViewConstraints.fromJs( - JsViewConstraints( - minHeight: double.infinity, - ), - size), - throwsAssertionError); + () => ViewConstraints.fromJs(JsViewConstraints(minHeight: double.infinity), size), + throwsAssertionError, + ); }); test('Negative max constraints -> Assertion error.', () async { expect( - () => ViewConstraints.fromJs( - JsViewConstraints( - maxWidth: -1, - ), - size), - throwsAssertionError); + () => ViewConstraints.fromJs(JsViewConstraints(maxWidth: -1), size), + throwsAssertionError, + ); expect( - () => ViewConstraints.fromJs( - JsViewConstraints( - maxHeight: -1, - ), - size), - throwsAssertionError); + () => ViewConstraints.fromJs(JsViewConstraints(maxHeight: -1), size), + throwsAssertionError, + ); }); test('null JS Constraints -> Tight to size', () async { expect( - ViewConstraints.fromJs(null, size), - const ViewConstraints( - minWidth: 640, maxWidth: 640, // - minHeight: 480, maxHeight: 480, // - )); + ViewConstraints.fromJs(null, size), + const ViewConstraints( + minWidth: 640, + maxWidth: 640, // + minHeight: 480, + maxHeight: 480, // + ), + ); }); test('non-null JS Constraints -> Computes sizes', () async { final JsViewConstraints constraints = JsViewConstraints( - minWidth: 500, maxWidth: 600, // - minHeight: 300, maxHeight: 400, // + minWidth: 500, + maxWidth: 600, // + minHeight: 300, + maxHeight: 400, // ); expect( - ViewConstraints.fromJs(constraints, size), - const ViewConstraints( - minWidth: 500, maxWidth: 600, // - minHeight: 300, maxHeight: 400, // - )); + ViewConstraints.fromJs(constraints, size), + const ViewConstraints( + minWidth: 500, + maxWidth: 600, // + minHeight: 300, + maxHeight: 400, // + ), + ); }); test('null JS Width -> Tight to width. Computes height.', () async { - final JsViewConstraints constraints = JsViewConstraints( - minHeight: 200, - maxHeight: 320, - ); + final JsViewConstraints constraints = JsViewConstraints(minHeight: 200, maxHeight: 320); expect( - ViewConstraints.fromJs(constraints, size), - const ViewConstraints( - minWidth: 640, maxWidth: 640, // - minHeight: 200, maxHeight: 320, // - )); + ViewConstraints.fromJs(constraints, size), + const ViewConstraints( + minWidth: 640, + maxWidth: 640, // + minHeight: 200, + maxHeight: 320, // + ), + ); }); test('null JS Height -> Tight to height. Computed width.', () async { - final JsViewConstraints constraints = JsViewConstraints( - minWidth: 200, - maxWidth: 320, - ); + final JsViewConstraints constraints = JsViewConstraints(minWidth: 200, maxWidth: 320); expect( - ViewConstraints.fromJs(constraints, size), - const ViewConstraints( - minWidth: 200, maxWidth: 320, // - minHeight: 480, maxHeight: 480, // - )); + ViewConstraints.fromJs(constraints, size), + const ViewConstraints( + minWidth: 200, + maxWidth: 320, // + minHeight: 480, + maxHeight: 480, // + ), + ); }); test( - 'non-null JS Constraints -> Computes sizes. Max values can be greater than available size.', - () async { - final JsViewConstraints constraints = JsViewConstraints( - minWidth: 500, maxWidth: 1024, // - minHeight: 300, maxHeight: 768, // - ); - expect( + 'non-null JS Constraints -> Computes sizes. Max values can be greater than available size.', + () async { + final JsViewConstraints constraints = JsViewConstraints( + minWidth: 500, + maxWidth: 1024, // + minHeight: 300, + maxHeight: 768, // + ); + expect( ViewConstraints.fromJs(constraints, size), const ViewConstraints( - minWidth: 500, maxWidth: 1024, // - minHeight: 300, maxHeight: 768, // - )); - }); + minWidth: 500, + maxWidth: 1024, // + minHeight: 300, + maxHeight: 768, // + ), + ); + }, + ); - test( - 'non-null JS Constraints -> Computes sizes. Max values can be unconstrained.', - () async { + test('non-null JS Constraints -> Computes sizes. Max values can be unconstrained.', () async { final JsViewConstraints constraints = JsViewConstraints( minWidth: 500, maxWidth: double.infinity, @@ -143,15 +137,16 @@ Future testMain() async { maxHeight: double.infinity, ); expect( - ViewConstraints.fromJs(constraints, size), - const ViewConstraints( - minWidth: 500, - // ignore: avoid_redundant_argument_values - maxWidth: double.infinity, - minHeight: 300, - // ignore: avoid_redundant_argument_values - maxHeight: double.infinity, - )); + ViewConstraints.fromJs(constraints, size), + const ViewConstraints( + minWidth: 500, + // ignore: avoid_redundant_argument_values + maxWidth: double.infinity, + minHeight: 300, + // ignore: avoid_redundant_argument_values + maxHeight: double.infinity, + ), + ); }); }); } diff --git a/lib/web_ui/test/engine/view_embedder/dimensions_provider/custom_element_dimensions_provider_test.dart b/lib/web_ui/test/engine/view_embedder/dimensions_provider/custom_element_dimensions_provider_test.dart index 4416aa59bc56c..502a8f56f1ba0 100644 --- a/lib/web_ui/test/engine/view_embedder/dimensions_provider/custom_element_dimensions_provider_test.dart +++ b/lib/web_ui/test/engine/view_embedder/dimensions_provider/custom_element_dimensions_provider_test.dart @@ -14,8 +14,7 @@ void main() { } void doTests() { - final DomElement sizeSource = createDomElement('div') - ..style.display = 'block'; + final DomElement sizeSource = createDomElement('div')..style.display = 'block'; group('computePhysicalSize', () { late CustomElementDimensionsProvider provider; @@ -72,11 +71,9 @@ void doTests() { const double dpr = 2.5; EngineFlutterDisplay.instance.debugOverrideDevicePixelRatio(dpr); const double keyboardGap = 100; - final double physicalHeight = - (domWindow.visualViewport!.height! + keyboardGap) * dpr; + final double physicalHeight = (domWindow.visualViewport!.height! + keyboardGap) * dpr; - final ViewPadding computed = - provider.computeKeyboardInsets(physicalHeight, false); + final ViewPadding computed = provider.computeKeyboardInsets(physicalHeight, false); expect(computed.top, 0); expect(computed.right, 0); @@ -132,12 +129,10 @@ void doTests() { test('funnels DPR change events too', () async { // Override the source of DPR events... - final StreamController dprController = - StreamController.broadcast(); + final StreamController dprController = StreamController.broadcast(); // Inject the dprController stream into the CustomElementDimensionsProvider. - final CustomElementDimensionsProvider provider = - CustomElementDimensionsProvider( + final CustomElementDimensionsProvider provider = CustomElementDimensionsProvider( sizeSource, onDprChange: dprController.stream, ); @@ -153,9 +148,12 @@ void doTests() { test('closed by onHotRestart', () async { // Register an onDone listener for the stream final Completer completer = Completer(); - provider.onResize.listen(null, onDone: () { - completer.complete(true); - }); + provider.onResize.listen( + null, + onDone: () { + completer.complete(true); + }, + ); // Should close the stream provider.close(); diff --git a/lib/web_ui/test/engine/view_embedder/dimensions_provider/dimensions_provider_test.dart b/lib/web_ui/test/engine/view_embedder/dimensions_provider/dimensions_provider_test.dart index 7c355f94fdd17..d93eb7ee258f1 100644 --- a/lib/web_ui/test/engine/view_embedder/dimensions_provider/dimensions_provider_test.dart +++ b/lib/web_ui/test/engine/view_embedder/dimensions_provider/dimensions_provider_test.dart @@ -18,12 +18,9 @@ void doTests() { expect(provider, isA()); }); - test('Creates a CustomElement instance when hostElement is not null', - () async { + test('Creates a CustomElement instance when hostElement is not null', () async { final DomElement element = createDomElement('some-random-element'); - final DimensionsProvider provider = DimensionsProvider.create( - hostElement: element, - ); + final DimensionsProvider provider = DimensionsProvider.create(hostElement: element); expect(provider, isA()); }); diff --git a/lib/web_ui/test/engine/view_embedder/dimensions_provider/full_page_dimensions_provider_test.dart b/lib/web_ui/test/engine/view_embedder/dimensions_provider/full_page_dimensions_provider_test.dart index cd633da01526d..1078c387f5a6b 100644 --- a/lib/web_ui/test/engine/view_embedder/dimensions_provider/full_page_dimensions_provider_test.dart +++ b/lib/web_ui/test/engine/view_embedder/dimensions_provider/full_page_dimensions_provider_test.dart @@ -24,8 +24,10 @@ void doTests() { test('returns visualViewport physical size (width * dpr)', () { const double dpr = 2.5; EngineFlutterDisplay.instance.debugOverrideDevicePixelRatio(dpr); - final ui.Size expected = ui.Size(domWindow.visualViewport!.width! * dpr, - domWindow.visualViewport!.height! * dpr); + final ui.Size expected = ui.Size( + domWindow.visualViewport!.width! * dpr, + domWindow.visualViewport!.height! * dpr, + ); final ui.Size computed = provider.computePhysicalSize(); @@ -45,12 +47,10 @@ void doTests() { const double dpr = 2.5; EngineFlutterDisplay.instance.debugOverrideDevicePixelRatio(dpr); const double keyboardGap = 100; - final double physicalHeight = - (domWindow.visualViewport!.height! + keyboardGap) * dpr; + final double physicalHeight = (domWindow.visualViewport!.height! + keyboardGap) * dpr; const double expectedBottom = keyboardGap * dpr; - final ViewPadding computed = - provider.computeKeyboardInsets(physicalHeight, false); + final ViewPadding computed = provider.computeKeyboardInsets(physicalHeight, false); expect(computed.top, 0); expect(computed.right, 0); @@ -61,8 +61,7 @@ void doTests() { group('onResize Stream', () { // Needed to synthesize "resize" events - final DomEventTarget resizeEventTarget = - domWindow.visualViewport ?? domWindow; + final DomEventTarget resizeEventTarget = domWindow.visualViewport ?? domWindow; late FullPageDimensionsProvider provider; @@ -87,9 +86,12 @@ void doTests() { test('closed by onHotRestart', () { // Register an onDone listener for the stream final Completer completer = Completer(); - provider.onResize.listen(null, onDone: () { - completer.complete(true); - }); + provider.onResize.listen( + null, + onDone: () { + completer.complete(true); + }, + ); // Should close the stream provider.close(); diff --git a/lib/web_ui/test/engine/view_embedder/display_dpr_stream_test.dart b/lib/web_ui/test/engine/view_embedder/display_dpr_stream_test.dart index a32ff78620fcf..58c335249780c 100644 --- a/lib/web_ui/test/engine/view_embedder/display_dpr_stream_test.dart +++ b/lib/web_ui/test/engine/view_embedder/display_dpr_stream_test.dart @@ -19,17 +19,15 @@ void doTests() { late DisplayDprStream dprStream; setUp(() async { - dprStream = DisplayDprStream(EngineFlutterDisplay.instance, - overrides: DebugDisplayDprStreamOverrides( - getMediaQuery: (_) => eventTarget, - )); + dprStream = DisplayDprStream( + EngineFlutterDisplay.instance, + overrides: DebugDisplayDprStreamOverrides(getMediaQuery: (_) => eventTarget), + ); }); test('funnels display DPR on every mediaQuery "change" event.', () async { - final Future> dprs = dprStream.dprChanged - .take(3) - .timeout(const Duration(seconds: 1)) - .toList(); + final Future> dprs = + dprStream.dprChanged.take(3).timeout(const Duration(seconds: 1)).toList(); // Simulate the events EngineFlutterDisplay.instance.debugOverrideDevicePixelRatio(6.9); diff --git a/lib/web_ui/test/engine/view_embedder/dom_manager_test.dart b/lib/web_ui/test/engine/view_embedder/dom_manager_test.dart index f176c5fd6992c..6de075326feaf 100644 --- a/lib/web_ui/test/engine/view_embedder/dom_manager_test.dart +++ b/lib/web_ui/test/engine/view_embedder/dom_manager_test.dart @@ -4,6 +4,7 @@ @JS() library dom_manager_test; // We need this to mess with the ShadowDOM. + import 'dart:js_interop'; import 'package:test/bootstrap/browser.dart'; @@ -26,7 +27,10 @@ void doTests() { expect(domManager.rootElement.tagName, equalsIgnoringCase(DomManager.flutterViewTagName)); expect(domManager.platformViewsHost.tagName, equalsIgnoringCase(DomManager.glassPaneTagName)); - expect(domManager.textEditingHost.tagName, equalsIgnoringCase(DomManager.textEditingHostTagName)); + expect( + domManager.textEditingHost.tagName, + equalsIgnoringCase(DomManager.textEditingHostTagName), + ); expect(domManager.semanticsHost.tagName, equalsIgnoringCase(DomManager.semanticsHostTagName)); // Check parent-child relationships. @@ -38,44 +42,51 @@ void doTests() { expect(rootChildren[2], domManager.semanticsHost); expect(rootChildren[3].tagName, equalsIgnoringCase('style')); - final List shadowChildren = domManager.renderingHost.childNodes.cast().toList(); + final List shadowChildren = + domManager.renderingHost.childNodes.cast().toList(); expect(shadowChildren.length, 2); expect(shadowChildren[0], domManager.sceneHost); expect(shadowChildren[1].tagName, equalsIgnoringCase('style')); }); - test('hide placeholder text for textfield', () { - final DomManager domManager = DomManager(devicePixelRatio: 3.0); - domDocument.body!.append(domManager.rootElement); + test( + 'hide placeholder text for textfield', + () { + final DomManager domManager = DomManager(devicePixelRatio: 3.0); + domDocument.body!.append(domManager.rootElement); - final DomHTMLInputElement regularTextField = createDomHTMLInputElement(); - regularTextField.placeholder = 'Now you see me'; - domManager.rootElement.appendChild(regularTextField); + final DomHTMLInputElement regularTextField = createDomHTMLInputElement(); + regularTextField.placeholder = 'Now you see me'; + domManager.rootElement.appendChild(regularTextField); - regularTextField.focusWithoutScroll(); - DomCSSStyleDeclaration? style = domWindow.getComputedStyle( + regularTextField.focusWithoutScroll(); + DomCSSStyleDeclaration? style = domWindow.getComputedStyle( domManager.rootElement.querySelector('input')!, - '::placeholder'); - expect(style, isNotNull); - expect(style.opacity, isNot('0')); - - final DomHTMLInputElement textField = createDomHTMLInputElement(); - textField.placeholder = 'Now you dont'; - textField.classList.add('flt-text-editing'); - domManager.rootElement.appendChild(textField); - - textField.focusWithoutScroll(); - style = domWindow.getComputedStyle( + '::placeholder', + ); + expect(style, isNotNull); + expect(style.opacity, isNot('0')); + + final DomHTMLInputElement textField = createDomHTMLInputElement(); + textField.placeholder = 'Now you dont'; + textField.classList.add('flt-text-editing'); + domManager.rootElement.appendChild(textField); + + textField.focusWithoutScroll(); + style = domWindow.getComputedStyle( domManager.rootElement.querySelector('input.flt-text-editing')!, - '::placeholder'); - expect(style, isNotNull); - expect(style.opacity, '0'); - - domManager.rootElement.remove(); - - // For some reason, only Firefox is able to correctly compute styles for - // the `::placeholder` pseudo-element. - }, skip: ui_web.browser.browserEngine != ui_web.BrowserEngine.firefox); + '::placeholder', + ); + expect(style, isNotNull); + expect(style.opacity, '0'); + + domManager.rootElement.remove(); + + // For some reason, only Firefox is able to correctly compute styles for + // the `::placeholder` pseudo-element. + }, + skip: ui_web.browser.browserEngine != ui_web.BrowserEngine.firefox, + ); }); group('Shadow root', () { @@ -108,8 +119,7 @@ void doTests() { test('Attaches a stylesheet to the shadow root', () { final DomManager domManager = DomManager(devicePixelRatio: 3.0); - final DomElement? style = - domManager.renderingHost.querySelector('#flt-internals-stylesheet'); + final DomElement? style = domManager.renderingHost.querySelector('#flt-internals-stylesheet'); expect(style, isNotNull); expect(style!.tagName, equalsIgnoringCase('style')); @@ -119,8 +129,7 @@ void doTests() { test('setScene', () { final DomManager domManager = DomManager(devicePixelRatio: 3.0); - final DomElement sceneHost = - domManager.renderingHost.querySelector('flt-scene-host')!; + final DomElement sceneHost = domManager.renderingHost.querySelector('flt-scene-host')!; final DomElement scene1 = createDomElement('flt-scene'); domManager.setScene(scene1); diff --git a/lib/web_ui/test/engine/view_embedder/embedding_strategy/custom_element_embedding_strategy_test.dart b/lib/web_ui/test/engine/view_embedder/embedding_strategy/custom_element_embedding_strategy_test.dart index 0a954377f10c5..542be4c89d24d 100644 --- a/lib/web_ui/test/engine/view_embedder/embedding_strategy/custom_element_embedding_strategy_test.dart +++ b/lib/web_ui/test/engine/view_embedder/embedding_strategy/custom_element_embedding_strategy_test.dart @@ -30,9 +30,11 @@ void doTests() { }); test('Prepares target environment', () { - expect(target.getAttribute('flt-embedding'), 'custom-element', - reason: - 'Should identify itself as a specific key=value into the target element.'); + expect( + target.getAttribute('flt-embedding'), + 'custom-element', + reason: 'Should identify itself as a specific key=value into the target element.', + ); }); }); @@ -52,32 +54,32 @@ void doTests() { final DomCSSStyleDeclaration style = glassPane.style; expect(glassPane.isConnected, isFalse); - expect(style.position, '', - reason: 'Should not have any specific position.'); + expect(style.position, '', reason: 'Should not have any specific position.'); expect(style.width, '', reason: 'Should not have any size set.'); strategy.attachViewRoot(glassPane); // Assert injection into - expect(glassPane.isConnected, isTrue, - reason: 'Should inject glassPane into the document.'); - expect(glassPane.parent, target, - reason: 'Should inject glassPane into the target element'); + expect(glassPane.isConnected, isTrue, reason: 'Should inject glassPane into the document.'); + expect(glassPane.parent, target, reason: 'Should inject glassPane into the target element'); final DomCSSStyleDeclaration styleAfter = glassPane.style; // Assert required styling to cover the viewport - expect(styleAfter.position, 'relative', - reason: 'Should be relatively positioned.'); + expect(styleAfter.position, 'relative', reason: 'Should be relatively positioned.'); expect(styleAfter.display, 'block', reason: 'Should be display:block.'); - expect(styleAfter.width, '100%', - reason: 'Should take 100% of the available width'); - expect(styleAfter.height, '100%', - reason: 'Should take 100% of the available height'); - expect(styleAfter.overflow, 'hidden', - reason: 'Should hide the occasional oversized canvas elements.'); - expect(styleAfter.touchAction, 'none', - reason: 'Should disable browser handling of touch events.'); + expect(styleAfter.width, '100%', reason: 'Should take 100% of the available width'); + expect(styleAfter.height, '100%', reason: 'Should take 100% of the available height'); + expect( + styleAfter.overflow, + 'hidden', + reason: 'Should hide the occasional oversized canvas elements.', + ); + expect( + styleAfter.touchAction, + 'none', + reason: 'Should disable browser handling of touch events.', + ); }); }); } diff --git a/lib/web_ui/test/engine/view_embedder/embedding_strategy/embedding_strategy_test.dart b/lib/web_ui/test/engine/view_embedder/embedding_strategy/embedding_strategy_test.dart index f254455ecd58a..57ee08a3d0c6c 100644 --- a/lib/web_ui/test/engine/view_embedder/embedding_strategy/embedding_strategy_test.dart +++ b/lib/web_ui/test/engine/view_embedder/embedding_strategy/embedding_strategy_test.dart @@ -24,12 +24,9 @@ void doTests() { expect(strategy, isA()); }); - test('Creates a CustomElement instance when hostElement is not null', - () async { + test('Creates a CustomElement instance when hostElement is not null', () async { final DomElement element = createDomElement('some-random-element'); - final EmbeddingStrategy strategy = EmbeddingStrategy.create( - hostElement: element, - ); + final EmbeddingStrategy strategy = EmbeddingStrategy.create(hostElement: element); expect(strategy, isA()); }); diff --git a/lib/web_ui/test/engine/view_embedder/embedding_strategy/full_page_embedding_strategy_test.dart b/lib/web_ui/test/engine/view_embedder/embedding_strategy/full_page_embedding_strategy_test.dart index bb096227dbdd1..613e855047e6a 100644 --- a/lib/web_ui/test/engine/view_embedder/embedding_strategy/full_page_embedding_strategy_test.dart +++ b/lib/web_ui/test/engine/view_embedder/embedding_strategy/full_page_embedding_strategy_test.dart @@ -22,33 +22,36 @@ void doTests() { meta ..id = 'my_viewport_meta_for_testing' ..name = 'viewport' - ..content = 'width=device-width, initial-scale=1.0, ' + ..content = + 'width=device-width, initial-scale=1.0, ' 'maximum-scale=1.0, user-scalable=no'; domDocument.head!.append(meta); - DomElement? userMeta = - domDocument.querySelector('#my_viewport_meta_for_testing'); + DomElement? userMeta = domDocument.querySelector('#my_viewport_meta_for_testing'); expect(userMeta, isNotNull); // ignore: unused_local_variable final FullPageEmbeddingStrategy strategy = FullPageEmbeddingStrategy(); - expect(target.getAttribute('flt-embedding'), 'full-page', - reason: - 'Should identify itself as a specific key=value into the target element.'); + expect( + target.getAttribute('flt-embedding'), + 'full-page', + reason: 'Should identify itself as a specific key=value into the target element.', + ); // Locate the viewport metas again... userMeta = domDocument.querySelector('#my_viewport_meta_for_testing'); - final DomElement? flutterMeta = - domDocument.querySelector('meta[name="viewport"]'); + final DomElement? flutterMeta = domDocument.querySelector('meta[name="viewport"]'); - expect(userMeta, isNull, - reason: 'Should delete previously existing viewport meta tags.'); + expect(userMeta, isNull, reason: 'Should delete previously existing viewport meta tags.'); expect(flutterMeta, isNotNull); - expect(flutterMeta!.hasAttribute('flt-viewport'), isTrue, - reason: 'Should install flutter viewport meta tag.'); + expect( + flutterMeta!.hasAttribute('flt-viewport'), + isTrue, + reason: 'Should install flutter viewport meta tag.', + ); }); }); @@ -60,32 +63,23 @@ void doTests() { final DomCSSStyleDeclaration style = glassPane.style; expect(glassPane.isConnected, isFalse); - expect(style.position, '', - reason: 'Should not have any specific position.'); - expect(style.top, '', - reason: - 'Should not have any top/right/bottom/left positioning/inset.'); + expect(style.position, '', reason: 'Should not have any specific position.'); + expect(style.top, '', reason: 'Should not have any top/right/bottom/left positioning/inset.'); strategy.attachViewRoot(glassPane); // Assert injection into - expect(glassPane.isConnected, isTrue, - reason: 'Should inject glassPane into the document.'); - expect(glassPane.parent, domDocument.body, - reason: 'Should inject glassPane into the '); + expect(glassPane.isConnected, isTrue, reason: 'Should inject glassPane into the document.'); + expect(glassPane.parent, domDocument.body, reason: 'Should inject glassPane into the '); final DomCSSStyleDeclaration styleAfter = glassPane.style; // Assert required styling to cover the viewport - expect(styleAfter.position, 'absolute', - reason: 'Should be absolutely positioned.'); + expect(styleAfter.position, 'absolute', reason: 'Should be absolutely positioned.'); expect(styleAfter.top, '0px', reason: 'Should cover the whole viewport.'); - expect(styleAfter.right, '0px', - reason: 'Should cover the whole viewport.'); - expect(styleAfter.bottom, '0px', - reason: 'Should cover the whole viewport.'); - expect(styleAfter.left, '0px', - reason: 'Should cover the whole viewport.'); + expect(styleAfter.right, '0px', reason: 'Should cover the whole viewport.'); + expect(styleAfter.bottom, '0px', reason: 'Should cover the whole viewport.'); + expect(styleAfter.left, '0px', reason: 'Should cover the whole viewport.'); }); }); } diff --git a/lib/web_ui/test/engine/view_embedder/flutter_view_manager_test.dart b/lib/web_ui/test/engine/view_embedder/flutter_view_manager_test.dart index 3ff2cd027e1df..66bf71909b4ab 100644 --- a/lib/web_ui/test/engine/view_embedder/flutter_view_manager_test.dart +++ b/lib/web_ui/test/engine/view_embedder/flutter_view_manager_test.dart @@ -23,7 +23,10 @@ Future doTests() async { group('registerView', () { test('can register view', () { - final EngineFlutterView view = EngineFlutterView(platformDispatcher, createDomElement('div')); + final EngineFlutterView view = EngineFlutterView( + platformDispatcher, + createDomElement('div'), + ); final int viewId = view.viewId; viewManager.registerView(view); @@ -32,19 +35,27 @@ Future doTests() async { }); test('fails if the same viewId is already registered', () { - final EngineFlutterView view = EngineFlutterView(platformDispatcher, createDomElement('div')); + final EngineFlutterView view = EngineFlutterView( + platformDispatcher, + createDomElement('div'), + ); viewManager.registerView(view); - expect(() { viewManager.registerView(view); }, throwsAssertionError); + expect(() { + viewManager.registerView(view); + }, throwsAssertionError); }); test('stores JSOptions that getOptions can retrieve', () { - final EngineFlutterView view = EngineFlutterView(platformDispatcher, createDomElement('div')); + final EngineFlutterView view = EngineFlutterView( + platformDispatcher, + createDomElement('div'), + ); final int viewId = view.viewId; - final JsFlutterViewOptions expectedOptions = jsify({ - 'hostElement': createDomElement('div'), - }) as JsFlutterViewOptions; + final JsFlutterViewOptions expectedOptions = + jsify({'hostElement': createDomElement('div')}) + as JsFlutterViewOptions; viewManager.registerView(view, jsViewOptions: expectedOptions); @@ -55,7 +66,10 @@ Future doTests() async { group('unregisterView', () { test('unregisters a view', () { - final EngineFlutterView view = EngineFlutterView(platformDispatcher, createDomElement('div')); + final EngineFlutterView view = EngineFlutterView( + platformDispatcher, + createDomElement('div'), + ); final int viewId = view.viewId; viewManager.registerView(view); @@ -71,17 +85,24 @@ Future doTests() async { // can't hang "forever" waiting for this to complete. This stream will close // after 100ms of inactivity, regardless of what the test or the code do. final Stream onViewCreated = viewManager.onViewCreated.timeout( - const Duration(milliseconds: 100), onTimeout: (EventSink sink) { - sink.close(); - }); + const Duration(milliseconds: 100), + onTimeout: (EventSink sink) { + sink.close(); + }, + ); final Stream onViewDisposed = viewManager.onViewDisposed.timeout( - const Duration(milliseconds: 100), onTimeout: (EventSink sink) { - sink.close(); - }); + const Duration(milliseconds: 100), + onTimeout: (EventSink sink) { + sink.close(); + }, + ); test('on view registered/unregistered - fires event', () async { - final EngineFlutterView view = EngineFlutterView(platformDispatcher, createDomElement('div')); + final EngineFlutterView view = EngineFlutterView( + platformDispatcher, + createDomElement('div'), + ); final int viewId = view.viewId; final Future> viewCreatedEvents = onViewCreated.toList(); @@ -95,10 +116,16 @@ Future doTests() async { final List createdViewsList = await viewCreatedEvents; final List disposedViewsList = await viewCreatedEvents; - expect(createdViewsList, listEqual([viewId]), - reason: 'Should fire creation event for view'); - expect(disposedViewsList, listEqual([viewId]), - reason: 'Should fire dispose event for view'); + expect( + createdViewsList, + listEqual([viewId]), + reason: 'Should fire creation event for view', + ); + expect( + disposedViewsList, + listEqual([viewId]), + reason: 'Should fire dispose event for view', + ); }); }); diff --git a/lib/web_ui/test/engine/view_embedder/flutter_views_proxy_test.dart b/lib/web_ui/test/engine/view_embedder/flutter_views_proxy_test.dart index 0b35c5a771862..7a369744dd593 100644 --- a/lib/web_ui/test/engine/view_embedder/flutter_views_proxy_test.dart +++ b/lib/web_ui/test/engine/view_embedder/flutter_views_proxy_test.dart @@ -17,20 +17,16 @@ void main() { Future doTests() async { group('FlutterViewManagerProxy', () { - final EnginePlatformDispatcher platformDispatcher = - EnginePlatformDispatcher.instance; - final FlutterViewManager viewManager = - FlutterViewManager(platformDispatcher); - final FlutterViewManagerProxy views = - FlutterViewManagerProxy(viewManager: viewManager); + final EnginePlatformDispatcher platformDispatcher = EnginePlatformDispatcher.instance; + final FlutterViewManager viewManager = FlutterViewManager(platformDispatcher); + final FlutterViewManagerProxy views = FlutterViewManagerProxy(viewManager: viewManager); late EngineFlutterView view; late int viewId; late DomElement hostElement; int registerViewWithOptions(Map options) { - final JsFlutterViewOptions jsOptions = - options.toJSAnyDeep as JsFlutterViewOptions; + final JsFlutterViewOptions jsOptions = options.toJSAnyDeep as JsFlutterViewOptions; viewManager.registerView(view, jsViewOptions: jsOptions); return viewId; } @@ -52,9 +48,7 @@ Future doTests() async { }); test('can retrieve hostElement for a known view', () { - final int viewId = registerViewWithOptions({ - 'hostElement': hostElement, - }); + final int viewId = registerViewWithOptions({'hostElement': hostElement}); final JSAny? element = views.getHostElement(viewId); @@ -100,8 +94,7 @@ Future doTests() async { }, }); - final InitialData? element = - views.getInitialData(viewId) as InitialData?; + final InitialData? element = views.getInitialData(viewId) as InitialData?; expect(element, isNotNull); expect(element!.someInt, 42); @@ -124,6 +117,5 @@ extension InitialDataExtension on InitialData { @JS('decimals') external JSArray get _decimals; - List get decimals => - _decimals.toDart.map((JSNumber e) => e.toDartDouble).toList(); + List get decimals => _decimals.toDart.map((JSNumber e) => e.toDartDouble).toList(); } diff --git a/lib/web_ui/test/engine/view_embedder/hot_restart_cache_handler_test.dart b/lib/web_ui/test/engine/view_embedder/hot_restart_cache_handler_test.dart index fcceac2a8f2b3..5758198449bad 100644 --- a/lib/web_ui/test/engine/view_embedder/hot_restart_cache_handler_test.dart +++ b/lib/web_ui/test/engine/view_embedder/hot_restart_cache_handler_test.dart @@ -37,11 +37,7 @@ void doTests() { registerElementForCleanup(other); registerElementForCleanup(another); - expect(_jsHotRestartStore!.toDart, [ - toBeCached, - other, - another, - ]); + expect(_jsHotRestartStore!.toDart, [toBeCached, other, another]); }); }); @@ -77,8 +73,7 @@ void doTests() { expect(_jsHotRestartStore!.toDart, [element]); }); - test('Clears registered elements from the DOM and the cache upon restart', - () async { + test('Clears registered elements from the DOM and the cache upon restart', () async { final DomElement element = createDomElement('for-test'); final DomElement element2 = createDomElement('for-test-two'); domDocument.body!.append(element); diff --git a/lib/web_ui/test/engine/view_embedder/style_manager_test.dart b/lib/web_ui/test/engine/view_embedder/style_manager_test.dart index 6fa8c48230b73..a96988a25060d 100644 --- a/lib/web_ui/test/engine/view_embedder/style_manager_test.dart +++ b/lib/web_ui/test/engine/view_embedder/style_manager_test.dart @@ -25,19 +25,17 @@ void doTests() { styleNonce: 'testing', cssSelectorPrefix: DomManager.flutterViewTagName, ); - final String expected = ui_web.browser.browserEngine == ui_web.BrowserEngine.firefox - ? 'rgb(0, 0, 0) 0px' - : 'rgb(0, 0, 0) none 0px'; - final String got = domWindow.getComputedStyle(flutterViewElement, 'focus').outline; + final String expected = + ui_web.browser.browserEngine == ui_web.BrowserEngine.firefox + ? 'rgb(0, 0, 0) 0px' + : 'rgb(0, 0, 0) none 0px'; + final String got = domWindow.getComputedStyle(flutterViewElement, 'focus').outline; expect(got, expected); }); test('styleSceneHost', () { - expect( - () => StyleManager.styleSceneHost(createDomHTMLDivElement()), - throwsAssertionError, - ); + expect(() => StyleManager.styleSceneHost(createDomHTMLDivElement()), throwsAssertionError); final DomElement sceneHost = createDomElement('flt-scene-host'); StyleManager.styleSceneHost(sceneHost); @@ -54,7 +52,7 @@ void doTests() { expect( () => StyleManager.styleSemanticsHost(createDomHTMLDivElement(), 1.0), throwsAssertionError, - reason: 'Only accepts a element.' + reason: 'Only accepts a element.', ); final DomElement semanticsHost = createDomElement('flt-semantics-host'); @@ -68,7 +66,7 @@ void doTests() { expect( () => StyleManager.scaleSemanticsHost(createDomHTMLDivElement(), 1.0), throwsAssertionError, - reason: 'Only accepts a element.' + reason: 'Only accepts a element.', ); final DomElement semanticsHost = createDomElement('flt-semantics-host'); diff --git a/lib/web_ui/test/engine/window_test.dart b/lib/web_ui/test/engine/window_test.dart index 905c1416f7ba2..27fa2f493aa47 100644 --- a/lib/web_ui/test/engine/window_test.dart +++ b/lib/web_ui/test/engine/window_test.dart @@ -43,6 +43,7 @@ Future testMain() async { void callback() { expect(Zone.current, innerZone); } + myWindow.onTextScaleFactorChanged = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. @@ -59,6 +60,7 @@ Future testMain() async { void callback() { expect(Zone.current, innerZone); } + myWindow.onPlatformBrightnessChanged = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. @@ -75,6 +77,7 @@ Future testMain() async { void callback() { expect(Zone.current, innerZone); } + myWindow.onMetricsChanged = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. @@ -91,6 +94,7 @@ Future testMain() async { void callback() { expect(Zone.current, innerZone); } + myWindow.onLocaleChanged = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. @@ -107,6 +111,7 @@ Future testMain() async { void callback(Duration _) { expect(Zone.current, innerZone); } + myWindow.onBeginFrame = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. @@ -123,6 +128,7 @@ Future testMain() async { void callback(List _) { expect(Zone.current, innerZone); } + myWindow.onReportTimings = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. @@ -139,6 +145,7 @@ Future testMain() async { void callback() { expect(Zone.current, innerZone); } + myWindow.onDrawFrame = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. @@ -155,6 +162,7 @@ Future testMain() async { void callback(ui.PointerDataPacket _) { expect(Zone.current, innerZone); } + myWindow.onPointerDataPacket = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. @@ -165,7 +173,7 @@ Future testMain() async { }); test('invokeOnKeyData returns normally when onKeyData is null', () { - const ui.KeyData keyData = ui.KeyData( + const ui.KeyData keyData = ui.KeyData( timeStamp: Duration(milliseconds: 1), type: ui.KeyEventType.repeat, physical: kPhysicalKeyA, @@ -188,6 +196,7 @@ Future testMain() async { expect(Zone.current, innerZone); return false; } + myWindow.onKeyData = onKeyData; // Test that the getter returns the exact same onKeyData, e.g. it doesn't @@ -195,7 +204,7 @@ Future testMain() async { expect(myWindow.onKeyData, same(onKeyData)); }); - const ui.KeyData keyData = ui.KeyData( + const ui.KeyData keyData = ui.KeyData( timeStamp: Duration(milliseconds: 1), type: ui.KeyEventType.repeat, physical: kPhysicalKeyA, @@ -217,6 +226,7 @@ Future testMain() async { void callback() { expect(Zone.current, innerZone); } + myWindow.onSemanticsEnabledChanged = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. @@ -233,6 +243,7 @@ Future testMain() async { void callback(ui.SemanticsActionEvent _) { expect(Zone.current, innerZone); } + ui.PlatformDispatcher.instance.onSemanticsActionEvent = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. @@ -240,7 +251,11 @@ Future testMain() async { }); EnginePlatformDispatcher.instance.invokeOnSemanticsAction( - myWindow.viewId, 0, ui.SemanticsAction.tap, null); + myWindow.viewId, + 0, + ui.SemanticsAction.tap, + null, + ); }); test('onAccessibilityFeaturesChanged preserves the zone', () { @@ -250,6 +265,7 @@ Future testMain() async { void callback() { expect(Zone.current, innerZone); } + myWindow.onAccessibilityFeaturesChanged = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. @@ -266,6 +282,7 @@ Future testMain() async { void callback(String _, ByteData? __, void Function(ByteData?)? ___) { expect(Zone.current, innerZone); } + myWindow.onPlatformMessage = callback; // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. @@ -284,14 +301,10 @@ Future testMain() async { innerZone.runGuarded(() { final ByteData inputData = ByteData(4); inputData.setUint32(0, 42); - myWindow.sendPlatformMessage( - 'flutter/debug-echo', - inputData, - (ByteData? outputData) { - expect(Zone.current, innerZone); - completer.complete(); - }, - ); + myWindow.sendPlatformMessage('flutter/debug-echo', inputData, (ByteData? outputData) { + expect(Zone.current, innerZone); + completer.complete(); + }); }); await completer.future; @@ -302,14 +315,10 @@ Future testMain() async { final ByteData inputData = ByteData(4); inputData.setUint32(0, 42); - myWindow.sendPlatformMessage( - 'flutter/__unknown__channel__', - null, - (ByteData? outputData) { - responded = true; - expect(outputData, isNull); - }, - ); + myWindow.sendPlatformMessage('flutter/__unknown__channel__', null, (ByteData? outputData) { + responded = true; + expect(outputData, isNull); + }); await Future.delayed(const Duration(milliseconds: 1)); expect(responded, isTrue); @@ -318,20 +327,15 @@ Future testMain() async { // Emulates the framework sending a request for screen orientation lock. Future sendSetPreferredOrientations(List orientations) { final Completer completer = Completer(); - final ByteData? inputData = const JSONMethodCodec().encodeMethodCall(MethodCall( - 'SystemChrome.setPreferredOrientations', - orientations, - )); - - myWindow.sendPlatformMessage( - 'flutter/platform', - inputData, - (ByteData? outputData) { - const MethodCodec codec = JSONMethodCodec(); - completer.complete(codec.decodeEnvelope(outputData!) as bool); - }, + final ByteData? inputData = const JSONMethodCodec().encodeMethodCall( + MethodCall('SystemChrome.setPreferredOrientations', orientations), ); + myWindow.sendPlatformMessage('flutter/platform', inputData, (ByteData? outputData) { + const MethodCodec codec = JSONMethodCodec(); + completer.complete(codec.decodeEnvelope(outputData!) as bool); + }); + return completer.future; } @@ -344,20 +348,26 @@ Future testMain() async { bool simulateError = false; // The `orientation` property cannot be overridden, so this test overrides the entire `screen`. - js_util.setProperty(domWindow, 'screen', js_util.jsify({ - 'orientation': { - 'lock': (String lockType) { - lockCalls.add(lockType); - if (simulateError) { - throw Error(); - } - return Future.value(0.toJS).toJS; - }.toJS, - 'unlock': () { - unlockCount += 1; - }.toJS, - }, - })); + js_util.setProperty( + domWindow, + 'screen', + js_util.jsify({ + 'orientation': { + 'lock': + (String lockType) { + lockCalls.add(lockType); + if (simulateError) { + throw Error(); + } + return Future.value(0.toJS).toJS; + }.toJS, + 'unlock': + () { + unlockCount += 1; + }.toJS, + }, + }), + ); // Sanity-check the test setup. expect(lockCalls, []); @@ -381,13 +391,19 @@ Future testMain() async { lockCalls.clear(); unlockCount = 0; - expect(await sendSetPreferredOrientations(['DeviceOrientation.landscapeLeft']), isTrue); + expect( + await sendSetPreferredOrientations(['DeviceOrientation.landscapeLeft']), + isTrue, + ); expect(lockCalls, [ScreenOrientation.lockTypeLandscapePrimary]); expect(unlockCount, 0); lockCalls.clear(); unlockCount = 0; - expect(await sendSetPreferredOrientations(['DeviceOrientation.landscapeRight']), isTrue); + expect( + await sendSetPreferredOrientations(['DeviceOrientation.landscapeRight']), + isTrue, + ); expect(lockCalls, [ScreenOrientation.lockTypeLandscapeSecondary]); expect(unlockCount, 0); lockCalls.clear(); @@ -400,7 +416,10 @@ Future testMain() async { unlockCount = 0; simulateError = true; - expect(await sendSetPreferredOrientations(['DeviceOrientation.portraitDown']), isFalse); + expect( + await sendSetPreferredOrientations(['DeviceOrientation.portraitDown']), + isFalse, + ); expect(lockCalls, [ScreenOrientation.lockTypePortraitSecondary]); expect(unlockCount, 0); @@ -412,50 +431,57 @@ Future testMain() async { final DomScreen? original = domWindow.screen; // The `orientation` property cannot be overridden, so this test overrides the entire `screen`. - js_util.setProperty(domWindow, 'screen', js_util.jsify({ - 'orientation': null, - })); + js_util.setProperty( + domWindow, + 'screen', + js_util.jsify({'orientation': null}), + ); expect(domWindow.screen!.orientation, isNull); expect(await sendSetPreferredOrientations([]), isFalse); js_util.setProperty(domWindow, 'screen', original); }); - test('SingletonFlutterWindow implements locale, locales, and locale change notifications', () async { - // This will count how many times we notified about locale changes. - int localeChangedCount = 0; - myWindow.onLocaleChanged = () { - localeChangedCount += 1; - }; - - // We populate the initial list of locales automatically (only test that we - // got some locales; some contributors may be in different locales, so we - // can't test the exact contents). - expect(myWindow.locale, isA()); - expect(myWindow.locales, isNotEmpty); - - // Trigger a change notification (reset locales because the notification - // doesn't actually change the list of languages; the test only observes - // that the list is populated again). - EnginePlatformDispatcher.instance.debugResetLocales(); - expect(myWindow.locales, isEmpty); - expect(myWindow.locale, equals(const ui.Locale.fromSubtags())); - expect(localeChangedCount, 0); - domWindow.dispatchEvent(createDomEvent('Event', 'languagechange')); - expect(myWindow.locales, isNotEmpty); - expect(localeChangedCount, 1); - }); + test( + 'SingletonFlutterWindow implements locale, locales, and locale change notifications', + () async { + // This will count how many times we notified about locale changes. + int localeChangedCount = 0; + myWindow.onLocaleChanged = () { + localeChangedCount += 1; + }; + + // We populate the initial list of locales automatically (only test that we + // got some locales; some contributors may be in different locales, so we + // can't test the exact contents). + expect(myWindow.locale, isA()); + expect(myWindow.locales, isNotEmpty); + + // Trigger a change notification (reset locales because the notification + // doesn't actually change the list of languages; the test only observes + // that the list is populated again). + EnginePlatformDispatcher.instance.debugResetLocales(); + expect(myWindow.locales, isEmpty); + expect(myWindow.locale, equals(const ui.Locale.fromSubtags())); + expect(localeChangedCount, 0); + domWindow.dispatchEvent(createDomEvent('Event', 'languagechange')); + expect(myWindow.locales, isNotEmpty); + expect(localeChangedCount, 1); + }, + ); test('dispatches browser event on flutter/service_worker channel', () async { final Completer completer = Completer(); - domWindow.addEventListener('flutter-first-frame', - createDomEventListener((DomEvent e) => completer.complete())); + domWindow.addEventListener( + 'flutter-first-frame', + createDomEventListener((DomEvent e) => completer.complete()), + ); final Zone innerZone = Zone.current.fork(); innerZone.runGuarded(() { myWindow.sendPlatformMessage( 'flutter/service_worker', ByteData(0), - (ByteData? outputData) { }, + (ByteData? outputData) {}, ); }); @@ -473,9 +499,10 @@ Future testMain() async { }); test('in full-page mode, Flutter window replaces viewport meta tags', () { - final DomHTMLMetaElement existingMeta = createDomHTMLMetaElement() - ..name = 'viewport' - ..content = 'foo=bar'; + final DomHTMLMetaElement existingMeta = + createDomHTMLMetaElement() + ..name = 'viewport' + ..content = 'foo=bar'; domDocument.head!.append(existingMeta); expect(existingMeta.isConnected, isTrue); @@ -483,7 +510,8 @@ Future testMain() async { // The existing viewport meta tag should've been removed. expect(existingMeta.isConnected, isFalse); // And a new one should've been added. - final DomHTMLMetaElement? newMeta = domDocument.head!.querySelector('meta[name="viewport"]') as DomHTMLMetaElement?; + final DomHTMLMetaElement? newMeta = + domDocument.head!.querySelector('meta[name="viewport"]') as DomHTMLMetaElement?; expect(newMeta, isNotNull); newMeta!; expect(newMeta.getAttribute('flt-viewport'), isNotNull); @@ -542,8 +570,7 @@ Future testMain() async { test('dispose', () { final DomHTMLDivElement host = createDomHTMLDivElement(); - final EngineFlutterView view = - EngineFlutterView(EnginePlatformDispatcher.instance, host); + final EngineFlutterView view = EngineFlutterView(EnginePlatformDispatcher.instance, host); // First, let's make sure the view's root element was inserted into the // host, and the dimensions provider is active. @@ -557,16 +584,10 @@ Future testMain() async { expect(view.dimensionsProvider.isClosed, isTrue); // Can't render into a disposed view. - expect( - () => view.render(ui.SceneBuilder().build()), - throwsAssertionError, - ); + expect(() => view.render(ui.SceneBuilder().build()), throwsAssertionError); // Can't update semantics on a disposed view. - expect( - () => view.updateSemantics(ui.SemanticsUpdateBuilder().build()), - throwsAssertionError, - ); + expect(() => view.updateSemantics(ui.SemanticsUpdateBuilder().build()), throwsAssertionError); }); group('resizing', () { @@ -663,9 +684,10 @@ Future testMain() async { setUp(() async { EngineFlutterDisplay.instance.debugOverrideDevicePixelRatio(dpr); - host = createDomHTMLDivElement() - ..style.width = '640px' - ..style.height = '480px'; + host = + createDomHTMLDivElement() + ..style.width = '640px' + ..style.height = '480px'; domDocument.body!.append(host); }); @@ -678,22 +700,24 @@ Future testMain() async { view = EngineFlutterView( EnginePlatformDispatcher.instance, host, - viewConstraints: JsViewConstraints( - minHeight: 320, - maxHeight: double.infinity, - )); + viewConstraints: JsViewConstraints(minHeight: 320, maxHeight: double.infinity), + ); // All the metrics until now have been expressed in logical pixels, because // they're coming from CSS/the browser, which works in logical pixels. - expect(view.physicalConstraints, const ViewConstraints( - minHeight: 320, - // ignore: avoid_redundant_argument_values - maxHeight: double.infinity, - minWidth: 640, - maxWidth: 640, - // However the framework expects physical pixels, so we multiply our expectations - // by the current DPR (2.5) - ) * dpr); + expect( + view.physicalConstraints, + const ViewConstraints( + minHeight: 320, + // ignore: avoid_redundant_argument_values + maxHeight: double.infinity, + minWidth: 640, + maxWidth: 640, + // However the framework expects physical pixels, so we multiply our expectations + // by the current DPR (2.5) + ) * + dpr, + ); }); }); } diff --git a/lib/web_ui/test/fallbacks/fallbacks_test.dart b/lib/web_ui/test/fallbacks/fallbacks_test.dart index 36f71df6319ad..7ebfbc0de81d5 100644 --- a/lib/web_ui/test/fallbacks/fallbacks_test.dart +++ b/lib/web_ui/test/fallbacks/fallbacks_test.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - import 'dart:js_interop'; import 'package:test/bootstrap/browser.dart'; @@ -21,15 +20,14 @@ void main() { external JSBoolean get crossOriginIsolated; Future testMain() async { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpUnitTests(setUpTestViewDimensions: false); test('bootstrapper selects correct builds', () { if (ui_web.browser.browserEngine == ui_web.BrowserEngine.blink) { expect(isWasm, isTrue); expect(isSkwasm, isTrue); - final bool shouldBeMultiThreaded = crossOriginIsolated.toDart && !configuration.forceSingleThreadedSkwasm; + final bool shouldBeMultiThreaded = + crossOriginIsolated.toDart && !configuration.forceSingleThreadedSkwasm; expect(isMultiThreaded, shouldBeMultiThreaded); } else { expect(isWasm, isFalse); diff --git a/lib/web_ui/test/html/bitmap_canvas_golden_test.dart b/lib/web_ui/test/html/bitmap_canvas_golden_test.dart index e028c1907a1a3..4d17b2aac92dc 100644 --- a/lib/web_ui/test/html/bitmap_canvas_golden_test.dart +++ b/lib/web_ui/test/html/bitmap_canvas_golden_test.dart @@ -14,8 +14,9 @@ import '../common/test_initialization.dart'; import 'paragraph/helper.dart'; DomElement get sceneHost => - EnginePlatformDispatcher.instance.implicitView!.dom.renderingHost - .querySelector(DomManager.sceneHostTagName)!; + EnginePlatformDispatcher.instance.implicitView!.dom.renderingHost.querySelector( + DomManager.sceneHostTagName, + )!; void main() { internalBootstrapBrowserTest(() => testMain); @@ -57,40 +58,28 @@ Future testMain() async { /// 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 linePaint = + (SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1) + .paintData; - final SurfacePaintData fillPaint = - (SurfacePaint()..style = PaintingStyle.fill).paintData; + final SurfacePaintData fillPaint = (SurfacePaint()..style = PaintingStyle.fill).paintData; canvas.translate(10, 10); - canvas.drawRect( - const Rect.fromLTWH(0, 0, 40, 40), - linePaint, - ); + 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(10, 0), const Offset(10, 40), linePaint); - canvas.drawLine( - const Offset(20.5, 0), - const Offset(20, 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()); + canvas = BitmapCanvas(const Rect.fromLTWH(0, 0, 60, 60), RenderStrategy()); drawMisalignedLines(canvas); @@ -105,8 +94,7 @@ Future testMain() async { // 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 = BitmapCanvas(const Rect.fromLTWH(0.5, 0.5, 60, 60), RenderStrategy()); canvas.clipRect(const Rect.fromLTWH(0, 0, 50, 50), ClipOp.intersect); drawMisalignedLines(canvas); @@ -116,31 +104,29 @@ Future testMain() async { }); test('fill the whole canvas with color even when transformed', () async { - canvas = BitmapCanvas(const Rect.fromLTWH(0, 0, 50, 50), - RenderStrategy()); + 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); + 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 = 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); + 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); + await matchGoldenFile('bitmap_canvas_fills_paint_when_transformed.png', region: region); }); // This test reproduces text blurriness when two pieces of text appear inside @@ -159,8 +145,8 @@ Future testMain() async { // 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; + (ParagraphBuilder(ParagraphStyle(fontFamily: 'Roboto'))..addText('Am I blurry?')).build() + as CanvasParagraph; paragraph.layout(const ParagraphConstraints(width: 1000)); final Rect canvasSize = Rect.fromLTRB( @@ -169,10 +155,13 @@ Future testMain() async { 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); + 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; @@ -182,30 +171,30 @@ Future testMain() async { canvas.drawParagraph(paragraph, Offset(8.5, 8.5 + innerClip.top)); expect( - canvas.rootElement.querySelectorAll('flt-paragraph').map((DomElement e) => e.innerText).toList(), + 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, - ); + 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, - )); + 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.'; + 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; @@ -216,41 +205,52 @@ Future testMain() async { canvas = BitmapCanvas(canvasSize, RenderStrategy()); canvas.debugChildOverdraw = true; - final SurfacePaintData pathPaint = SurfacePaintData() - ..color = 0xFF7F7F7F - ..style = PaintingStyle.fill; + 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; + ..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(), + 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.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); @@ -270,10 +270,7 @@ Future testMain() async { sceneElement.querySelector('flt-clip')!.append(canvas.rootElement); sceneHost.append(sceneElement); - await matchGoldenFile( - 'bitmap_canvas_draws_text_on_top_of_canvas.png', - region: canvasSize, - ); + await matchGoldenFile('bitmap_canvas_draws_text_on_top_of_canvas.png', region: canvasSize); }); // Regression test for https://github.com/flutter/flutter/issues/96498. When @@ -288,13 +285,7 @@ Future testMain() async { 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', - ); + expect(canvas.rootElement.querySelectorAll('canvas'), isEmpty); + expect(canvas.rootElement.querySelectorAll('flt-paragraph').single.innerText, 'Hello'); }); } diff --git a/lib/web_ui/test/html/canvas_clip_path_golden_test.dart b/lib/web_ui/test/html/canvas_clip_path_golden_test.dart index a0ea6e9c4adf4..0c51dc5cee0a4 100644 --- a/lib/web_ui/test/html/canvas_clip_path_golden_test.dart +++ b/lib/web_ui/test/html/canvas_clip_path_golden_test.dart @@ -18,15 +18,12 @@ void main() { } Future testMain() async { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + 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)); + 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(); @@ -34,16 +31,19 @@ Future testMain() async { 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.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)); + final engine.RecordingCanvas rc = engine.RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); rc.save(); const double testWidth = 200; const double testHeight = 150; @@ -56,18 +56,18 @@ Future testMain() async { paintPath.lineTo(0, testHeight); paintPath.close(); rc.drawPath( - paintPath, - engine.SurfacePaint() - ..color = const Color(0xFF00FF00) - ..style = PaintingStyle.fill); + 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)); + final engine.RecordingCanvas rc = engine.RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); rc.save(); const double testWidth = 200; const double testHeight = 150; @@ -75,10 +75,12 @@ Future testMain() async { 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); + rc.drawPath( + paintPath, + engine.SurfacePaint() + ..color = const Color(0xFF000000) + ..style = PaintingStyle.stroke, + ); final Path path = Path(); path.moveTo(-200, 0); @@ -86,17 +88,23 @@ Future testMain() async { 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.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)); + 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)); + final engine.RecordingCanvas rc = engine.RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); rc.save(); const double testWidth = 200; const double testHeight = 150; @@ -104,19 +112,28 @@ Future testMain() async { 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); + 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.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)); + await canvasScreenshot( + rc, + 'image_clipped_by_oval_path', + region: const Rect.fromLTWH(0, 0, 600, 800), + ); }); test('Clips with fillType evenOdd', () async { @@ -126,8 +143,12 @@ Future testMain() async { 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()); + 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 @@ -140,8 +161,8 @@ Future testMain() async { const double bottom = 250; path ..moveTo(left, top) - ..lineTo(right,top) - ..lineTo(right,bottom) + ..lineTo(right, top) + ..lineTo(right, bottom) ..lineTo(left, bottom) ..close(); path.fillType = PathFillType.evenOdd; @@ -151,22 +172,29 @@ Future testMain() async { 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.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)); + 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.DomCanvasElement canvas = engine.createDomCanvasElement( + width: width, + height: height, + ); final engine.DomCanvasRenderingContext2D ctx = canvas.context2D; ctx.fillStyle = '#E04040'; ctx.fillRect(0, 0, width / 3, height); diff --git a/lib/web_ui/test/html/canvas_context_golden_test.dart b/lib/web_ui/test/html/canvas_context_golden_test.dart index 282bd2590d712..fbbd2dd7332a3 100644 --- a/lib/web_ui/test/html/canvas_context_golden_test.dart +++ b/lib/web_ui/test/html/canvas_context_golden_test.dart @@ -20,18 +20,16 @@ Future testMain() async { const double screenHeight = 800.0; const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - setUpUnitTests( - setUpTestViewDimensions: false, - ); + 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; + 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)); @@ -54,14 +52,15 @@ Future testMain() async { }); 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; + 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)); diff --git a/lib/web_ui/test/html/canvas_reuse_golden_test.dart b/lib/web_ui/test/html/canvas_reuse_golden_test.dart index ded21b4350e42..97def468e88eb 100644 --- a/lib/web_ui/test/html/canvas_reuse_golden_test.dart +++ b/lib/web_ui/test/html/canvas_reuse_golden_test.dart @@ -19,25 +19,25 @@ 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); + 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()); + 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); + 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); @@ -56,7 +56,7 @@ Future testMain() async { 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'); + expect(canvas.style.zIndex, '-1'); // Add id to canvas element to test for reuse. const String kTestId = 'test-id-5698'; @@ -69,11 +69,11 @@ Future testMain() async { // 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); + 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(); @@ -98,14 +98,14 @@ Future testMain() async { }); } -const String _base64Encoded20x20TestImage = 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA' +const String _base64Encoded20x20TestImage = + 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA' 'B3RJTUUH5AMFFBksg4i3gQAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAAj' 'SURBVDjLY2TAC/7jlWVioACMah4ZmhnxpyHG0QAb1UyZZgBjWAIm/clP0AAAAABJRU5ErkJggg=='; HtmlImage _createRealTestImage() { return HtmlImage( - createDomHTMLImageElement() - ..src = 'data:text/plain;base64,$_base64Encoded20x20TestImage', + createDomHTMLImageElement()..src = 'data:text/plain;base64,$_base64Encoded20x20TestImage', 20, 20, ); diff --git a/lib/web_ui/test/html/canvas_test.dart b/lib/web_ui/test/html/canvas_test.dart index 497bed0d34416..c1efa489e3cc3 100644 --- a/lib/web_ui/test/html/canvas_test.dart +++ b/lib/web_ui/test/html/canvas_test.dart @@ -36,47 +36,52 @@ Future testMain() async { }); } - 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)); + 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'); + 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)); - }); + 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'); - }); + 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/lib/web_ui/test/html/canvas_winding_rule_golden_test.dart b/lib/web_ui/test/html/canvas_winding_rule_golden_test.dart index ec3c23129dfea..815d54bbe4f8f 100644 --- a/lib/web_ui/test/html/canvas_winding_rule_golden_test.dart +++ b/lib/web_ui/test/html/canvas_winding_rule_golden_test.dart @@ -31,42 +31,47 @@ Future testMain() async { 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 + 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)); + 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); diff --git a/lib/web_ui/test/html/clip_op_golden_test.dart b/lib/web_ui/test/html/clip_op_golden_test.dart index cfbe996f3f1b4..6c30bf7903402 100644 --- a/lib/web_ui/test/html/clip_op_golden_test.dart +++ b/lib/web_ui/test/html/clip_op_golden_test.dart @@ -28,10 +28,11 @@ Future testMain() async { 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; + 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); @@ -41,8 +42,11 @@ Future testMain() async { canvas.restore(); } canvas.drawRect(titleRect, paint); - await canvasScreenshot(canvas, 'clip_op_difference', - region: const Rect.fromLTRB(0, 0, 420, 360)); + await canvasScreenshot( + canvas, + 'clip_op_difference', + region: const Rect.fromLTRB(0, 0, 420, 360), + ); }); /// Regression test for https://github.com/flutter/flutter/issues/86345 @@ -50,13 +54,15 @@ Future testMain() 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; + 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++) { @@ -64,15 +70,9 @@ Future testMain() async { final double y = 10 + j * 70; canvas.save(); // Clip. - canvas.clipRect( - Rect.fromLTWH(x, y, i * 25, j * 25), - ClipOp.intersect, - ); + 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, - ); + canvas.drawRect(Rect.fromLTWH(x, y, 50, 50), paint); final Paragraph p = plain( EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 34), '23', @@ -82,10 +82,7 @@ Future testMain() async { canvas.drawParagraph(p, Offset(x, y)); canvas.restore(); // Draw the red border. - canvas.drawRect( - Rect.fromLTWH(x, y, 50, 50), - borderPaint, - ); + canvas.drawRect(Rect.fromLTWH(x, y, 50, 50), borderPaint); } } await canvasScreenshot(canvas, 'clip_zero_width_height', region: region); diff --git a/lib/web_ui/test/html/compositing/backdrop_filter_golden_test.dart b/lib/web_ui/test/html/compositing/backdrop_filter_golden_test.dart index 8535dff556447..10d13e8b61952 100644 --- a/lib/web_ui/test/html/compositing/backdrop_filter_golden_test.dart +++ b/lib/web_ui/test/html/compositing/backdrop_filter_golden_test.dart @@ -40,15 +40,11 @@ Future testMain() async { final Picture backgroundPicture = _drawBackground(region); builder.addPicture(Offset.zero, backgroundPicture); - builder.pushClipRect( - const Rect.fromLTRB(10, 10, 180, 120), - ); + 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.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); @@ -56,9 +52,7 @@ Future testMain() async { builder.pop(); builder.pop(); - domDocument.body!.append(builder - .build() - .webOnlyRootElement!); + domDocument.body!.append(builder.build().webOnlyRootElement!); await matchGoldenFile('backdrop_filter_clip.png', region: region); }); @@ -77,8 +71,9 @@ Future testMain() async { 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 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(); @@ -89,25 +84,19 @@ Future testMain() async { // 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.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.pushClipRect(const Rect.fromLTRB(10, 75, 180, 120), oldLayer: clipEngineLayer2); + builder2.pushBackdropFilter( + ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), + oldLayer: oldBackdropFilterLayer, ); - 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!); + domDocument.body!.append(builder2.build().webOnlyRootElement!); await matchGoldenFile('backdrop_filter_clip_moved.png', region: region); }); @@ -121,26 +110,19 @@ Future testMain() async { final Picture backgroundPicture = _drawBackground(region); builder.addPicture(Offset.zero, backgroundPicture); - builder.pushClipRect( - const Rect.fromLTRB(10, 10, 180, 120), - ); + 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.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!); + domDocument.body!.append(builder.build().webOnlyRootElement!); - await matchGoldenFile('backdrop_filter_no_child_rendering.png', - region: region); + await matchGoldenFile('backdrop_filter_no_child_rendering.png', region: region); }); test('colorFilter as imageFilter', () async { const Rect region = Rect.fromLTWH(0, 0, 190, 130); @@ -149,65 +131,61 @@ Future testMain() async { final Picture backgroundPicture = _drawBackground(region); builder.addPicture(Offset.zero, backgroundPicture); - builder.pushClipRect( - const Rect.fromLTRB(10, 10, 180, 120), - ); + 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 - ); + 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!); + domDocument.body!.append(builder.build().webOnlyRootElement!); - await matchGoldenFile('backdrop_filter_colorFilter_as_imageFilter.png', - region: region); + 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); + final RecordingCanvas canvas = recorder.beginRecording(region); canvas.drawCircle( - Offset(offsetX + 10, offsetY + 10), 10, SurfacePaint()..style = PaintingStyle.fill); + 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)); + 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)); + 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)); + 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); + final RecordingCanvas canvas = recorder.beginRecording(region); canvas.drawRect( - region.deflate(8.0), - SurfacePaint() - ..style = PaintingStyle.fill - ..color = const Color(0xFFE0FFE0) - ); + region.deflate(8.0), + SurfacePaint() + ..style = PaintingStyle.fill + ..color = const Color(0xFFE0FFE0), + ); return recorder.endRecording(); } diff --git a/lib/web_ui/test/html/compositing/canvas_blend_golden_test.dart b/lib/web_ui/test/html/compositing/canvas_blend_golden_test.dart index 816219f879e7e..04b49b5f609eb 100644 --- a/lib/web_ui/test/html/compositing/canvas_blend_golden_test.dart +++ b/lib/web_ui/test/html/compositing/canvas_blend_golden_test.dart @@ -17,78 +17,85 @@ void main() { } Future testMain() async { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpUnitTests(setUpTestViewDimensions: false); test('Blend circles with difference and color', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + 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)); + 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); + 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)); + 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)); + 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)); + 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)); + 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); + 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)); + 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); + 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'); }); @@ -97,8 +104,7 @@ Future testMain() async { HtmlImage createTestImage() { const int width = 100; const int height = 50; - final DomCanvasElement canvas = - createDomCanvasElement(width: width, height: height); + final DomCanvasElement canvas = createDomCanvasElement(width: width, height: height); final DomCanvasRenderingContext2D ctx = canvas.context2D; ctx.fillStyle = '#E04040'; ctx.fillRect(0, 0, 33, 50); diff --git a/lib/web_ui/test/html/compositing/canvas_image_blend_mode_golden_test.dart b/lib/web_ui/test/html/compositing/canvas_image_blend_mode_golden_test.dart index 64e165f697fa0..a91bc152bbec2 100644 --- a/lib/web_ui/test/html/compositing/canvas_image_blend_mode_golden_test.dart +++ b/lib/web_ui/test/html/compositing/canvas_image_blend_mode_golden_test.dart @@ -18,10 +18,7 @@ void main() { SurfacePaint makePaint() => Paint() as SurfacePaint; Future testMain() async { - setUpUnitTests( - withImplicitView: true, - setUpTestViewDimensions: false, - ); + setUpUnitTests(withImplicitView: true, setUpTestViewDimensions: false); const Color red = Color(0xFFFF0000); const Color green = Color(0xFF00FF00); @@ -31,74 +28,106 @@ Future testMain() async { 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], + [ + 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)); + 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); + 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.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); + }, 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)); + 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)); + 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; @@ -112,15 +141,15 @@ Future testMain() async { }); test('Does not re-use styles with same image src', () async { - final RecordingCanvas rc = RecordingCanvas( - const Rect.fromLTRB(0, 0, 400, 400)); + 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)); + 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 @@ -132,12 +161,14 @@ Future testMain() async { } Paragraph createTestParagraph() { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 14.0, - )); + 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/lib/web_ui/test/html/compositing/canvas_image_filter_golden_test.dart b/lib/web_ui/test/html/compositing/canvas_image_filter_golden_test.dart index f1302dee9e5d8..c4b6f51a7a407 100644 --- a/lib/web_ui/test/html/compositing/canvas_image_filter_golden_test.dart +++ b/lib/web_ui/test/html/compositing/canvas_image_filter_golden_test.dart @@ -25,26 +25,26 @@ Future testMain() async { 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)); + 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, // - ])); + 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/lib/web_ui/test/html/compositing/canvas_mask_filter_golden_test.dart b/lib/web_ui/test/html/compositing/canvas_mask_filter_golden_test.dart index 840bb0c23332d..d3571a096b10b 100644 --- a/lib/web_ui/test/html/compositing/canvas_mask_filter_golden_test.dart +++ b/lib/web_ui/test/html/compositing/canvas_mask_filter_golden_test.dart @@ -18,9 +18,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpUnitTests(setUpTestViewDimensions: false); tearDown(() { ContextStateHandle.debugEmulateWebKitMaskFilter = false; @@ -39,14 +37,11 @@ Future testMain() async { final RecordingCanvas rc = RecordingCanvas(screenRect); rc.translate(0, 75); - final SurfacePaint paint = SurfacePaint() - ..maskFilter = const ui.MaskFilter.blur(ui.BlurStyle.normal, 5); + 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.drawRect(ui.Rect.fromCircle(center: ui.Offset.zero, radius: 30), paint); rc.translate(100, 0); paint.color = const ui.Color(0xFF00FF00); @@ -76,19 +71,12 @@ Future testMain() async { 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.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.drawLine(const ui.Offset(-20, -50), const ui.Offset(20, 50), paint); rc.translate(100, 0); paint.color = const ui.Color(0xFF888888); @@ -124,8 +112,8 @@ Future testMain() async { final RecordingCanvas rc = RecordingCanvas(screenRect); rc.translate(150, 150); - final SurfacePaint paint = SurfacePaint() - ..maskFilter = const ui.MaskFilter.blur(ui.BlurStyle.normal, 5); + final SurfacePaint paint = + SurfacePaint()..maskFilter = const ui.MaskFilter.blur(ui.BlurStyle.normal, 5); const List colors = [ ui.Color(0xFF000000), @@ -141,14 +129,10 @@ Future testMain() async { 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, - ); + rc.drawRect(ui.Rect.fromCircle(center: const ui.Offset(90, 0), radius: 20), paint); } - await canvasScreenshot(rc, 'mask_filter_transformed_$browser', - region: screenRect); + await canvasScreenshot(rc, 'mask_filter_transformed_$browser', region: screenRect); }); } @@ -163,17 +147,13 @@ Future testMain() async { final RecordingCanvas rc = RecordingCanvas(screenRect); rc.translate(0, 75); - final SurfacePaint paint = SurfacePaint() - ..maskFilter = const ui.MaskFilter.blur(ui.BlurStyle.normal, 5); + 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, - ); + rc.drawRect(ui.Rect.fromCircle(center: ui.Offset.zero, radius: 30), paint); - await canvasScreenshot(rc, 'mask_filter_blur_dpr_$testDpr', - region: screenRect); + await canvasScreenshot(rc, 'mask_filter_blur_dpr_$testDpr', region: screenRect); EngineFlutterDisplay.instance.debugOverrideDevicePixelRatio(1.0); }); } diff --git a/lib/web_ui/test/html/compositing/color_filter_golden_test.dart b/lib/web_ui/test/html/compositing/color_filter_golden_test.dart index 08436af35500e..c7b033b9ad45c 100644 --- a/lib/web_ui/test/html/compositing/color_filter_golden_test.dart +++ b/lib/web_ui/test/html/compositing/color_filter_golden_test.dart @@ -36,8 +36,7 @@ Future testMain() async { final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); final Picture backgroundPicture = _drawBackground(); builder.addPicture(Offset.zero, backgroundPicture); - builder.pushColorFilter( - const EngineColorFilter.mode(Color(0xF0000080), BlendMode.color)); + builder.pushColorFilter(const EngineColorFilter.mode(Color(0xF0000080), BlendMode.color)); final Picture circles1 = _drawTestPictureWithCircles(30, 30); builder.addPicture(Offset.zero, circles1); builder.pop(); @@ -58,8 +57,7 @@ Future testMain() async { final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); final Picture backgroundPicture = _drawBackground(); builder.addPicture(Offset.zero, backgroundPicture); - builder.pushColorFilter( - EngineColorFilter.matrix(colorMatrix)); + builder.pushColorFilter(EngineColorFilter.matrix(colorMatrix)); final Picture circles1 = _drawTestPictureWithCircles(30, 30); builder.addPicture(Offset.zero, circles1); builder.pop(); @@ -72,11 +70,7 @@ Future testMain() async { final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); final Picture backgroundPicture = _drawBackground(); builder.addPicture(Offset.zero, backgroundPicture); - builder.pushColorFilter( - const ColorFilter.mode( - Color(0xFFFF0000), - BlendMode.srcIn, - )); + builder.pushColorFilter(const ColorFilter.mode(Color(0xFFFF0000), BlendMode.srcIn)); final Picture circles1 = _drawTestPictureWithCircles(30, 30); builder.addPicture(Offset.zero, circles1); builder.pop(); @@ -86,42 +80,49 @@ Future testMain() async { } 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); + final EnginePictureRecorder recorder = PictureRecorder() as EnginePictureRecorder; + final RecordingCanvas canvas = recorder.beginRecording(const Rect.fromLTRB(0, 0, 400, 400)); canvas.drawCircle( - Offset(offsetX + 60, offsetY + 10), - 10, - (Paint() - ..style = PaintingStyle.fill - ..color = const Color.fromRGBO(255, 0, 0, 1)) as SurfacePaint); + 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); + 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); + 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)); + 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); + 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/lib/web_ui/test/html/compositing/compositing_golden_test.dart b/lib/web_ui/test/html/compositing/compositing_golden_test.dart index a47d3f44ece5c..f6d723915050d 100644 --- a/lib/web_ui/test/html/compositing/compositing_golden_test.dart +++ b/lib/web_ui/test/html/compositing/compositing_golden_test.dart @@ -38,9 +38,7 @@ Future testMain() async { test('pushClipRect', () async { final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushClipRect( - const ui.Rect.fromLTRB(10, 10, 60, 60), - ); + builder.pushClipRect(const ui.Rect.fromLTRB(10, 10, 60, 60)); _drawTestPicture(builder); builder.pop(); @@ -53,12 +51,8 @@ Future testMain() 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), - ); + builder.pushTransform(Matrix4.diagonal3Values(1, -1, 1).toFloat64()); + builder.pushClipRect(const ui.Rect.fromLTRB(10, 10, 60, 60)); _drawTestPicture(builder); builder.pop(); builder.pop(); @@ -66,20 +60,15 @@ Future testMain() async { domDocument.body!.append(builder.build().webOnlyRootElement!); - await matchGoldenFile('compositing_clip_rect_with_offset_and_transform.png', - region: region); + await matchGoldenFile('compositing_clip_rect_with_offset_and_transform.png', region: region); }); - test('pushClipRect with offset and transform ClipOp none should not clip', - () async { + 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); + 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(); @@ -87,24 +76,21 @@ Future testMain() async { domDocument.body!.append(builder.build().webOnlyRootElement!); - await matchGoldenFile('compositing_clip_rect_clipop_none.png', - region: region); + await matchGoldenFile('compositing_clip_rect_clipop_none.png', region: region); }); - test('pushClipRRect with offset and transform ClipOp none should not clip', - () async { + 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.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); + 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(); @@ -112,15 +98,12 @@ Future testMain() async { domDocument.body!.append(builder.build().webOnlyRootElement!); - await matchGoldenFile('compositing_clip_rrect_clipop_none.png', - region: region); + 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)), - ); + builder.pushClipRRect(ui.RRect.fromLTRBR(10, 10, 60, 60, const ui.Radius.circular(5))); _drawTestPicture(builder); builder.pop(); @@ -131,9 +114,7 @@ Future testMain() async { test('pushImageFilter blur', () async { final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); - builder.pushImageFilter( - ui.ImageFilter.blur(sigmaX: 1, sigmaY: 3), - ); + builder.pushImageFilter(ui.ImageFilter.blur(sigmaX: 1, sigmaY: 3)); _drawTestPicture(builder); builder.pop(); @@ -146,12 +127,12 @@ Future testMain() 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()), + (Matrix4.identity() + ..translate(40, 10) + ..rotateZ(math.pi / 6) + ..scale(0.75, 0.75)) + .toFloat64(), + ), ); _drawTestPicture(builder); builder.pop(); @@ -164,11 +145,7 @@ Future testMain() async { 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, - )); + builder.pushImageFilter(const ui.ColorFilter.mode(ui.Color(0xFFFF0000), ui.BlendMode.srcIn)); _drawTestPicture(builder); builder.pop(); @@ -204,19 +181,22 @@ Future testMain() async { 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)); - }, skip: ''' + 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)); + }, + skip: ''' TODO(https://github.com/flutter/flutter/issues/40395) - Needs ability to set iframe to 500,100 size. Current screen seems to be 500,500'''); + Needs ability to set iframe to 500,100 size. Current screen seems to be 500,500''', + ); // Draw a picture that overflows the screen. Verify that cull rect is the // intersection of screen bounds and paint bounds. @@ -237,7 +217,10 @@ void _testCullRectComputation() { final ui.SceneBuilder builder = ui.SceneBuilder(); drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { canvas.drawCircle( - const ui.Offset(-100, -100), 20, SurfacePaint()..style = ui.PaintingStyle.fill); + const ui.Offset(-100, -100), + 20, + SurfacePaint()..style = ui.PaintingStyle.fill, + ); }); builder.build(); @@ -251,8 +234,7 @@ void _testCullRectComputation() { 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); + canvas.drawCircle(const ui.Offset(50, 50), 10, SurfacePaint()..style = ui.PaintingStyle.fill); }); builder.build(); @@ -268,8 +250,7 @@ void _testCullRectComputation() { builder.pushOffset(10, 10); drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { - canvas.drawCircle( - const ui.Offset(50, 50), 10, SurfacePaint()..style = ui.PaintingStyle.fill); + canvas.drawCircle(const ui.Offset(50, 50), 10, SurfacePaint()..style = ui.PaintingStyle.fill); }); builder.pop(); @@ -282,48 +263,45 @@ void _testCullRectComputation() { // 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(); + 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.pushOffset(0, 90); + drawWithBitmapCanvas(builder, (RecordingCanvas canvas) { + canvas.drawCircle(ui.Offset.zero, 20, SurfacePaint()..style = ui.PaintingStyle.fill); + }); + builder.pop(); - builder.build(); + 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)); - }, skip: ''' + 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)); + }, + skip: ''' TODO(https://github.com/flutter/flutter/issues/40395) - Needs ability to set iframe to 500,100 size. Current screen seems to be 500,500'''); + Needs ability to set iframe to 500,100 size. Current screen seems to be 500,500''', + ); // 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(10, 10, 60, 60)); - builder.pushClipRect( - const ui.Rect.fromLTWH(40, 40, 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); + 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); + 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)); @@ -334,17 +312,12 @@ void _testCullRectComputation() { // 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(10, 10, 60, 60)); - builder.pushClipRect( - const ui.Rect.fromLTWH(40, 40, 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); + canvas.drawCircle(const ui.Offset(80, 55), 30, SurfacePaint()..style = ui.PaintingStyle.fill); }); builder.pop(); // pushClipRect @@ -352,8 +325,9 @@ void _testCullRectComputation() { domDocument.body!.append(builder.build().webOnlyRootElement!); await matchGoldenFile( - 'compositing_cull_rect_intersects_clip_and_paint_bounds.png', - region: region); + '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)); @@ -364,13 +338,9 @@ void _testCullRectComputation() { // 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(10, 10, 60, 60)); - builder.pushClipRect( - const ui.Rect.fromLTWH(40, 40, 60, 60), - ); + builder.pushClipRect(const ui.Rect.fromLTWH(40, 40, 60, 60)); builder.pushOffset(55, 70); @@ -383,12 +353,10 @@ void _testCullRectComputation() { builder.pop(); // pushClipRect domDocument.body!.append(builder.build().webOnlyRootElement!); - await matchGoldenFile('compositing_cull_rect_offset_inside_layer_clip.png', - region: region); + 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)); + 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 @@ -396,13 +364,9 @@ void _testCullRectComputation() { // 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(10, 10, 60, 60)); - builder.pushClipRect( - const ui.Rect.fromLTWH(40, 40, 60, 60), - ); + builder.pushClipRect(const ui.Rect.fromLTWH(40, 40, 60, 60)); builder.pushOffset(100, 50); @@ -427,22 +391,18 @@ void _testCullRectComputation() { final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); builder.pushOffset(80, 50); - builder.pushTransform( - Matrix4.rotationZ(-math.pi / 4).toFloat64(), - ); + builder.pushTransform(Matrix4.rotationZ(-math.pi / 4).toFloat64()); - builder.pushClipRect( - const ui.Rect.fromLTRB(-10, -10, 10, 10), - ); + builder.pushClipRect(const ui.Rect.fromLTRB(-10, -10, 10, 10)); - builder.pushTransform( - Matrix4.rotationZ(math.pi / 4).toFloat64(), - ); + 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.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() @@ -462,8 +422,7 @@ void _testCullRectComputation() { 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)), + within(distance: 0.05, from: const ui.Rect.fromLTRB(-14.1, -14.1, 14.1, 14.1)), ); }); @@ -471,9 +430,7 @@ void _testCullRectComputation() { final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); final ui.Path path = ui.Path(); path.addRect(const ui.Rect.fromLTRB(10, 10, 60, 60)); - builder.pushClipPath( - path, - ); + builder.pushClipPath(path); _drawTestPicture(builder); builder.pop(); @@ -487,40 +444,37 @@ void _testCullRectComputation() { 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()); + 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.pushTransform(scaleTransform.toFloat64()); builder.pushOffset(400, 200); - builder.pushClipRect( - const ui.Rect.fromLTRB(-200, -200, 200, 200), - ); + builder.pushClipRect(const ui.Rect.fromLTRB(-200, -200, 200, 200)); - builder - .pushTransform(Matrix4.rotationY(45.0 * math.pi / 180.0).toFloat64()); + builder.pushTransform(Matrix4.rotationY(45.0 * math.pi / 180.0).toFloat64()); - builder.pushClipRect( - const ui.Rect.fromLTRB(-140, -140, 140, 140), - ); + 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); + 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), @@ -618,117 +572,114 @@ void _testCullRectComputation() { // 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); + 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(); - await matchGoldenFile( - 'compositing_draw_high_quality_text.png', - region: canvasSize, - ); - }, - testOn: 'chrome', - ); + 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); + 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)); + 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)); + 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)); + 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, - ); + 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}) { +void drawWithBitmapCanvas( + ui.SceneBuilder builder, + PaintCallback callback, { + ui.Rect bounds = ui.Rect.largest, +}) { final EnginePictureRecorder recorder = EnginePictureRecorder(); final RecordingCanvas canvas = recorder.beginRecording(bounds); @@ -736,8 +687,5 @@ void drawWithBitmapCanvas(ui.SceneBuilder builder, PaintCallback callback, callback(canvas); final ui.Picture picture = recorder.endRecording(); - builder.addPicture( - ui.Offset.zero, - picture, - ); + builder.addPicture(ui.Offset.zero, picture); } diff --git a/lib/web_ui/test/html/compositing/dom_mask_filter_golden_test.dart b/lib/web_ui/test/html/compositing/dom_mask_filter_golden_test.dart index db41df3e346ad..9a258f3eeb17b 100644 --- a/lib/web_ui/test/html/compositing/dom_mask_filter_golden_test.dart +++ b/lib/web_ui/test/html/compositing/dom_mask_filter_golden_test.dart @@ -14,17 +14,15 @@ void main() { } Future testMain() async { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpUnitTests(setUpTestViewDimensions: false); test('Should blur rectangles based on sigma.', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + 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()); + 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/lib/web_ui/test/html/deprecation_test.dart b/lib/web_ui/test/html/deprecation_test.dart index f89e5a4d3a7a0..1e46b7cfb85c7 100644 --- a/lib/web_ui/test/html/deprecation_test.dart +++ b/lib/web_ui/test/html/deprecation_test.dart @@ -29,8 +29,9 @@ void testMain() { final Renderer chosenRenderer = renderer; expect(chosenRenderer, isA()); - expect(warnings, contains( - contains('See: https://docs.flutter.dev/to/web-html-renderer-deprecation') - )); + expect( + warnings, + contains(contains('See: https://docs.flutter.dev/to/web-html-renderer-deprecation')), + ); }); } diff --git a/lib/web_ui/test/html/dom_canvas_test.dart b/lib/web_ui/test/html/dom_canvas_test.dart index a81c36031dcdf..d4cdf753137fb 100644 --- a/lib/web_ui/test/html/dom_canvas_test.dart +++ b/lib/web_ui/test/html/dom_canvas_test.dart @@ -13,15 +13,16 @@ void main() { 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.fill), - rect, - ); - expect( - adjustRectForDom(rect, SurfacePaintData()..style=PaintingStyle.stroke..strokeWidth=0), + adjustRectForDom( + rect, + SurfacePaintData() + ..style = PaintingStyle.stroke + ..strokeWidth = 0, + ), rect, ); }); @@ -29,15 +30,30 @@ Future testMain() async { 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), + 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), + adjustRectForDom( + rect, + SurfacePaintData() + ..style = PaintingStyle.stroke + ..strokeWidth = 10, + ), const Rect.fromLTWH(5, 15, 130, 150), ); expect( - adjustRectForDom(rect, SurfacePaintData()..style=PaintingStyle.stroke..strokeWidth=15), + adjustRectForDom( + rect, + SurfacePaintData() + ..style = PaintingStyle.stroke + ..strokeWidth = 15, + ), const Rect.fromLTWH(2.5, 12.5, 125, 145), ); }); @@ -45,19 +61,19 @@ Future testMain() async { test('flips rect when necessary', () { Rect rect = const Rect.fromLTWH(100, 200, -40, -60); expect( - adjustRectForDom(rect, SurfacePaintData()..style=PaintingStyle.fill), + 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), + 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), + adjustRectForDom(rect, SurfacePaintData()..style = PaintingStyle.fill), const Rect.fromLTWH(60, 200, 40, 60), ); }); @@ -65,11 +81,21 @@ Future testMain() async { 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), + adjustRectForDom( + rect, + SurfacePaintData() + ..style = PaintingStyle.stroke + ..strokeWidth = 50, + ), const Rect.fromLTWH(75, 175, 0, 20), ); expect( - adjustRectForDom(rect, SurfacePaintData()..style=PaintingStyle.stroke..strokeWidth=80), + adjustRectForDom( + rect, + SurfacePaintData() + ..style = PaintingStyle.stroke + ..strokeWidth = 80, + ), const Rect.fromLTWH(60, 160, 0, 0), ); }); diff --git a/lib/web_ui/test/html/drawing/canvas_arc_golden_test.dart b/lib/web_ui/test/html/drawing/canvas_arc_golden_test.dart index e9bc105852915..ccfc77910463c 100644 --- a/lib/web_ui/test/html/drawing/canvas_arc_golden_test.dart +++ b/lib/web_ui/test/html/drawing/canvas_arc_golden_test.dart @@ -29,18 +29,13 @@ Future testMain() async { 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(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(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(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); - + 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); @@ -48,14 +43,22 @@ Future testMain() async { 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); + 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); @@ -65,45 +68,65 @@ Future testMain() 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.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.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); + 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); +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); + 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); + 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); + path, + SurfacePaintData() + ..strokeWidth = 2 + ..color = + 0x61000000 // black38 + ..style = PaintingStyle.stroke, + ); } diff --git a/lib/web_ui/test/html/drawing/canvas_draw_color_golden_test.dart b/lib/web_ui/test/html/drawing/canvas_draw_color_golden_test.dart index 14be3d83c1914..b6795d8c155b7 100644 --- a/lib/web_ui/test/html/drawing/canvas_draw_color_golden_test.dart +++ b/lib/web_ui/test/html/drawing/canvas_draw_color_golden_test.dart @@ -15,10 +15,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpUnitTests(emulateTesterEnvironment: false, setUpTestViewDimensions: false); setUp(() async { debugShowClipLayers = true; @@ -56,10 +53,10 @@ Picture _drawTestPicture(Rect region, {bool useColor = false}) { final RecordingCanvas canvas = recorder.beginRecording(r); canvas.drawRect( - region.deflate(8.0), - Paint() as SurfacePaint - ..style = PaintingStyle.fill - ..color = const Color(0xFFE0E0E0) + region.deflate(8.0), + Paint() as SurfacePaint + ..style = PaintingStyle.fill + ..color = const Color(0xFFE0E0E0), ); canvas.transform(Matrix4.translationValues(50, 50, 0).storage); @@ -67,16 +64,20 @@ Picture _drawTestPicture(Rect region, {bool useColor = false}) { 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.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)); + 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/lib/web_ui/test/html/drawing/canvas_draw_image_golden_test.dart b/lib/web_ui/test/html/drawing/canvas_draw_image_golden_test.dart index 5bcf1108f4eea..64ef6a1903495 100644 --- a/lib/web_ui/test/html/drawing/canvas_draw_image_golden_test.dart +++ b/lib/web_ui/test/html/drawing/canvas_draw_image_golden_test.dart @@ -23,44 +23,42 @@ void main() { } Future testMain() async { - setUpUnitTests( - withImplicitView: true, - setUpTestViewDimensions: false, - ); + setUpUnitTests(withImplicitView: true, setUpTestViewDimensions: false); test('Paints image', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + 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(); - - 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( + '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(); + + 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)); + 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); @@ -70,8 +68,7 @@ Future testMain() async { }); test('Paints image with transform and offset', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + 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); @@ -81,57 +78,60 @@ Future testMain() async { }); test('Paints image with transform using destination', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + 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.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)); + 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()); + 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 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.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()); + 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)); + 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); @@ -139,10 +139,11 @@ Future testMain() async { 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()); + 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'); }); @@ -150,20 +151,24 @@ Future testMain() async { // 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)); + 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.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)); + 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'); }); @@ -171,20 +176,24 @@ Future testMain() async { // 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)); + 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()); + 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'); }); @@ -192,21 +201,25 @@ Future testMain() async { // 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)); + 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.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)); + 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'); }); @@ -214,8 +227,7 @@ Future testMain() async { // 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)); + final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); rc.save(); final Image testImage = createTestImage(); final double testWidth = testImage.width.toDouble(); @@ -225,14 +237,19 @@ Future testMain() async { 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.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)); + 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'); }); @@ -240,8 +257,7 @@ Future testMain() async { // 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)); + final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); rc.save(); final Image testImage = createTestImage(); final double testWidth = testImage.width.toDouble(); @@ -252,14 +268,19 @@ Future testMain() async { 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.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)); + 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'); @@ -268,21 +289,25 @@ Future testMain() async { // 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)); + 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.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)); + 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'); }); @@ -290,8 +315,7 @@ Future testMain() async { // 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)); + final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); rc.save(); final Image testImage = createTestImage(); final double testWidth = testImage.width.toDouble(); @@ -302,14 +326,19 @@ Future testMain() async { 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.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)); + 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'); }); @@ -321,29 +350,35 @@ Future testMain() async { 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)); + 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); + '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.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 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); + '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(); @@ -361,8 +396,12 @@ Future testMain() async { 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()); + 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(); @@ -379,8 +418,7 @@ Future testMain() async { try { sceneElement.append(builder.build().webOnlyRootElement!); domDocument.body!.append(sceneElement); - await matchGoldenFile('draw_nine_slice.png', - region: region); + await matchGoldenFile('draw_nine_slice.png', region: region); } finally { // The page is reused across tests, so remove the element after taking the // screenshot. @@ -400,8 +438,12 @@ Future testMain() async { // 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()); + 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(); @@ -418,8 +460,7 @@ Future testMain() async { try { sceneElement.append(builder.build().webOnlyRootElement!); domDocument.body!.append(sceneElement); - await matchGoldenFile('draw_nine_slice_empty_center.png', - region: region); + 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. @@ -437,15 +478,16 @@ Future testMain() async { 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.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); + await canvasScreenshot(canvas, 'draw_clipped_and_transformed_image', region: region); }); /// Regression test for https://github.com/flutter/flutter/issues/61245 @@ -454,14 +496,13 @@ Future testMain() async { 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 + 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); + await canvasScreenshot(canvas, 'draw_3d_image', region: region, setupPerspective: true); }); /// Regression test for https://github.com/flutter/flutter/issues/61245 @@ -471,17 +512,22 @@ Future testMain() async { 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 + 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.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); + 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 { @@ -489,22 +535,33 @@ Future testMain() async { 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.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); + 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 { @@ -513,22 +570,27 @@ Future testMain() async { 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.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.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); + await canvasScreenshot(canvas, 'draw_3d_oval_clipped', region: region, setupPerspective: true); }); test('Should render path with perspective transform', () async { @@ -537,16 +599,23 @@ Future testMain() async { 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.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)); + 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); @@ -557,9 +626,7 @@ Future testMain() async { 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); + await canvasScreenshot(canvas, 'draw_3d_path', region: region, setupPerspective: true); }); test('Should render path with perspective transform', () async { @@ -568,17 +635,24 @@ Future testMain() async { 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.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)); + 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); @@ -589,13 +663,13 @@ Future testMain() async { 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); + 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' +const String base64PngData = + 'iVBORw0KGgoAAAANSUh' 'EUgAAADwAAAA8CAYAAAA6/NlyAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPo' 'AAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAApGVYSWZNTQAqAAAACAAFARIAAwAAAAEAAQA' 'AARoABQAAAAEAAABKARsABQAAAAEAAABSATEAAgAAACAAAABah2kABAAAAAEAAAB6AAAAAAAA' @@ -726,16 +800,11 @@ const String base64PngData = 'iVBORw0KGgoAAAANSUh' const String base64ImageUrl = 'data:image/png;base64,$base64PngData'; HtmlImage createNineSliceImage() { - return HtmlImage( - createDomHTMLImageElement()..src = base64ImageUrl, - 60, - 60, - ); + return HtmlImage(createDomHTMLImageElement()..src = base64ImageUrl, 60, 60); } HtmlImage createTestImage({int width = 100, int height = 50}) { - final DomCanvasElement canvas = - createDomCanvasElement(width: width, height: height); + final DomCanvasElement canvas = createDomCanvasElement(width: width, height: height); final DomCanvasRenderingContext2D ctx = canvas.context2D; ctx.fillStyle = '#E04040'; ctx.fillRect(0, 0, 33, 50); @@ -751,14 +820,15 @@ HtmlImage createTestImage({int width = 100, int height = 50}) { 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, - )); +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/lib/web_ui/test/html/drawing/canvas_draw_picture_golden_test.dart b/lib/web_ui/test/html/drawing/canvas_draw_picture_golden_test.dart index d40996ee5968b..1cd8dd8e10dbf 100644 --- a/lib/web_ui/test/html/drawing/canvas_draw_picture_golden_test.dart +++ b/lib/web_ui/test/html/drawing/canvas_draw_picture_golden_test.dart @@ -19,10 +19,7 @@ void main() { SurfacePaint makePaint() => Paint() as SurfacePaint; Future testMain() async { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpUnitTests(emulateTesterEnvironment: false, setUpTestViewDimensions: false); setUpAll(() async { debugShowClipLayers = true; @@ -35,23 +32,17 @@ Future testMain() async { 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), - ); + builder.pushClipRect(const Rect.fromLTRB(0, 0, 100, 100)); _drawTestPicture(builder, 100, false); builder.pop(); - final DomElement elm1 = builder - .build() - .webOnlyRootElement!; + 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), - ); + 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. @@ -59,35 +50,27 @@ Future testMain() async { builder2.pop(); elm1.remove(); - await sceneScreenshot(builder2, 'canvas_draw_picture_acrossframes', - region: region); + 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), - ); + builder.pushClipRect(const Rect.fromLTRB(0, 0, 100, 100)); _drawTestPicture(builder, 100, true); builder.pop(); - final DomElement elm1 = builder - .build() - .webOnlyRootElement!; + 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), - ); + 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); + await sceneScreenshot(builder2, 'canvas_draw_picture_acrossframes_clipped', region: region); }); test('PictureInPicture', () async { @@ -95,13 +78,11 @@ Future testMain() async { final Picture greenRectPicture = _drawGreenRectIntoPicture(); final EnginePictureRecorder recorder = PictureRecorder() as EnginePictureRecorder; - final RecordingCanvas canvas = - recorder.beginRecording(const Rect.fromLTRB(0, 0, 100, 100)); + 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); + await sceneScreenshot(builder, 'canvas_draw_picture_in_picture_rect', region: region); }); }); } @@ -111,28 +92,28 @@ 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)); + 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.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, + 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)); + 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(); } @@ -143,8 +124,7 @@ const String _base64Encoded20x20TestImage = HtmlImage _createRealTestImage() { return HtmlImage( - createDomHTMLImageElement() - ..src = 'data:text/plain;base64,$_base64Encoded20x20TestImage', + createDomHTMLImageElement()..src = 'data:text/plain;base64,$_base64Encoded20x20TestImage', 20, 20, ); diff --git a/lib/web_ui/test/html/drawing/canvas_lines_golden_test.dart b/lib/web_ui/test/html/drawing/canvas_lines_golden_test.dart index db0d0fcb148fc..859c71f64c5b6 100644 --- a/lib/web_ui/test/html/drawing/canvas_lines_golden_test.dart +++ b/lib/web_ui/test/html/drawing/canvas_lines_golden_test.dart @@ -31,14 +31,12 @@ Future testMain() async { }); 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); @@ -46,26 +44,34 @@ Future testMain() async { }); 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; + 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; + 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), + 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(); + final List shiftedPoints = + points.map((Offset point) => point.translate(20, 20)).toList(); paintLinesFromPoints(domCanvas, paintWithStyle, points); paintLinesFromPoints(domCanvas, paintWithoutStyle, shiftedPoints); @@ -75,29 +81,40 @@ Future testMain() async { }); 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; + 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), + 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(); + 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); @@ -109,21 +126,27 @@ Future testMain() async { } 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; + 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); diff --git a/lib/web_ui/test/html/drawing/canvas_rect_golden_test.dart b/lib/web_ui/test/html/drawing/canvas_rect_golden_test.dart index 42a6705e89f5b..a41312f97a5cf 100644 --- a/lib/web_ui/test/html/drawing/canvas_rect_golden_test.dart +++ b/lib/web_ui/test/html/drawing/canvas_rect_golden_test.dart @@ -27,7 +27,6 @@ Future testMain() async { }); test('draws rect with flipped coordinates L > R, T > B', () async { - paintRects(canvas); domDocument.body!.append(canvas.rootElement); @@ -36,29 +35,40 @@ Future testMain() async { } void paintRects(BitmapCanvas canvas) { + canvas.drawRect( + const Rect.fromLTRB(30, 40, 100, 50), + SurfacePaintData() + ..color = + 0xFF4CAF50 //Colors.green + ..strokeWidth = 1.0 + ..style = PaintingStyle.stroke, + ); - 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); + // 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); + // 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); + // swap left and right. + canvas.drawRect( + const Rect.fromLTRB(100, 350, 30, 340), + SurfacePaintData() + ..color = + 0xFFF44336 //Colors.red + ..style = PaintingStyle.fill, + ); } diff --git a/lib/web_ui/test/html/drawing/canvas_rrect_golden_test.dart b/lib/web_ui/test/html/drawing/canvas_rrect_golden_test.dart index 3a7e1359a9013..f3631c228b601 100644 --- a/lib/web_ui/test/html/drawing/canvas_rrect_golden_test.dart +++ b/lib/web_ui/test/html/drawing/canvas_rrect_golden_test.dart @@ -19,9 +19,10 @@ Future testMain() async { 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; + 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]; @@ -36,9 +37,12 @@ Future testMain() async { 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); + 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); }); @@ -47,47 +51,69 @@ Future testMain() async { 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)); + 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); + 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); + 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); + 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); + 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); + await canvasScreenshot( + rc, + 'canvas_drrect_overlapping_radius', + canvasRect: canvasRect, + region: region, + ); }); } diff --git a/lib/web_ui/test/html/drawing/canvas_stroke_joins_golden_test.dart b/lib/web_ui/test/html/drawing/canvas_stroke_joins_golden_test.dart index b0080f1afebfa..74a1aabef2614 100644 --- a/lib/web_ui/test/html/drawing/canvas_stroke_joins_golden_test.dart +++ b/lib/web_ui/test/html/drawing/canvas_stroke_joins_golden_test.dart @@ -27,20 +27,20 @@ Future testMain() async { }); 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 + 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); @@ -48,9 +48,16 @@ void paintStrokeJoins(BitmapCanvas canvas) { final List strokeCaps = [StrokeCap.butt, StrokeCap.round, StrokeCap.square]; for (final StrokeCap cap in strokeCaps) { - final List joints = [StrokeJoin.miter, StrokeJoin.bevel, StrokeJoin.round]; + final List joints = [ + StrokeJoin.miter, + StrokeJoin.bevel, + StrokeJoin.round, + ]; const List colors = [ - Color(0xFFF44336), Color(0xFF4CAF50), Color(0xFF2196F3)]; // red, green, blue + 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]; @@ -59,12 +66,15 @@ void paintStrokeJoins(BitmapCanvas canvas) { 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); + 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); diff --git a/lib/web_ui/test/html/drawing/canvas_stroke_rects_golden_test.dart b/lib/web_ui/test/html/drawing/canvas_stroke_rects_golden_test.dart index a02c509371ad9..f65e933d0166a 100644 --- a/lib/web_ui/test/html/drawing/canvas_stroke_rects_golden_test.dart +++ b/lib/web_ui/test/html/drawing/canvas_stroke_rects_golden_test.dart @@ -29,41 +29,49 @@ Future testMain() async { }); 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); + 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/lib/web_ui/test/html/drawing/conic_golden_test.dart b/lib/web_ui/test/html/drawing/conic_golden_test.dart index 851b97801de7e..1169d05e709de 100644 --- a/lib/web_ui/test/html/drawing/conic_golden_test.dart +++ b/lib/web_ui/test/html/drawing/conic_golden_test.dart @@ -18,20 +18,21 @@ Future testMain() async { Future testPath(Path path, String goldenFileName) async { const Rect canvasBounds = Rect.fromLTWH(0, 0, 600, 800); - final BitmapCanvas bitmapCanvas = BitmapCanvas(canvasBounds, - RenderStrategy()); + final BitmapCanvas bitmapCanvas = BitmapCanvas(canvasBounds, RenderStrategy()); final RecordingCanvas canvas = RecordingCanvas(canvasBounds); - SurfacePaint paint = SurfacePaint() - ..color = const Color(0x7F7F7F7F) - ..style = PaintingStyle.fill; + 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; + paint = + SurfacePaint() + ..strokeWidth = 2.0 + ..color = const Color(0xFF7F007F) + ..style = PaintingStyle.stroke; canvas.drawPath(path, paint); canvas.endRecording(); diff --git a/lib/web_ui/test/html/drawing/dom_clip_stroke_golden_test.dart b/lib/web_ui/test/html/drawing/dom_clip_stroke_golden_test.dart index d44c6cfa8dc0e..a97c122c0b354 100644 --- a/lib/web_ui/test/html/drawing/dom_clip_stroke_golden_test.dart +++ b/lib/web_ui/test/html/drawing/dom_clip_stroke_golden_test.dart @@ -17,8 +17,7 @@ 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); + final BitmapCanvas canvas = BitmapCanvas(region, RenderStrategy()..hasParagraphs = true); const Rect rect = Rect.fromLTWH(0, 0, 150, 150); @@ -57,8 +56,7 @@ Future testMain() async { 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 BitmapCanvas canvas = BitmapCanvas(region, RenderStrategy()..hasParagraphs = true); final RRect rrect = RRect.fromRectAndRadius( const Rect.fromLTWH(0, 0, 150, 150), @@ -100,8 +98,7 @@ Future testMain() async { 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); + final BitmapCanvas canvas = BitmapCanvas(region, RenderStrategy()..hasParagraphs = true); const Rect rect = Rect.fromLTWH(0, 0, 150, 150); diff --git a/lib/web_ui/test/html/drawing/draw_vertices_golden_test.dart b/lib/web_ui/test/html/drawing/draw_vertices_golden_test.dart index 94548a1745aad..cfc5ac4c7ba8a 100644 --- a/lib/web_ui/test/html/drawing/draw_vertices_golden_test.dart +++ b/lib/web_ui/test/html/drawing/draw_vertices_golden_test.dart @@ -23,9 +23,7 @@ Future testMain() async { const double screenHeight = 800.0; const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpUnitTests(setUpTestViewDimensions: false); setUp(() { GlContextCache.dispose(); @@ -33,38 +31,43 @@ Future testMain() async { }); 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); + 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 { + 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)); + 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' + 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([ @@ -79,48 +82,54 @@ Future testMain() async { 0xFF0000FF, 0xFFFF0000, 0xFF00FF00, - 0xFF0000FF + 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()); + 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( + '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, @@ -134,72 +143,87 @@ Future testMain() async { 20.0, 310.0, 200.0, - 420.0 - ])); - await testVertices( + 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); + ..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 - ])); + 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)); + 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)); + 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( + 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, @@ -213,25 +237,37 @@ Future testMain() async { 280.0, 30.0, 300.0, - 420.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]); + colors: colors, + ); - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + 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, + ); - final Vertices vertices = Vertices.raw( + 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, @@ -246,26 +282,29 @@ Future testMain() async { 30.0, ]), colors: colors, - indices: indices); + indices: indices, + ); - rc.drawVertices( - vertices as SurfaceVertices, BlendMode.srcOver, SurfacePaint()); + 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); + 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( + 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, @@ -279,26 +318,34 @@ Future testMain() async { 280.0, 30.0, 300.0, - 420.0 + 420.0, ]), - colors: colors); + 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); + 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( + 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, @@ -312,44 +359,44 @@ Future testMain() async { 20.0, 310.0, 200.0, - 420.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); + 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 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); + 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); + final EngineImageShader imgShader = EngineImageShader( + img, + tileMode, + tileMode, + Float64List.fromList(matrix4), + FilterQuality.high, + ); paint.shader = imgShader; @@ -361,28 +408,36 @@ Future testMain() async { 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); + 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 DomCanvasElement canvas = createDomCanvasElement(width: width, height: height); final DomCanvasRenderingContext2D ctx = canvas.context2D; ctx.fillStyle = '#E04040'; ctx.fillRect(0, 0, width / 3, height); @@ -395,9 +450,12 @@ Future createTestImage({int width = 50, int height = 40}) { ctx.fill(); final DomHTMLImageElement imageElement = createDomHTMLImageElement(); final Completer completer = Completer(); - imageElement.addEventListener('load', createDomEventListener((DomEvent event) { - completer.complete(HtmlImage(imageElement, width, height)); - })); + 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/lib/web_ui/test/html/image_test.dart b/lib/web_ui/test/html/image_test.dart index bd8883fc4e398..66b3ff8eb5649 100644 --- a/lib/web_ui/test/html/image_test.dart +++ b/lib/web_ui/test/html/image_test.dart @@ -14,20 +14,17 @@ void main() { } Matcher listEqual(List source, {int tolerance = 0}) { - return predicate( - (List target) { - if (source.length != target.length) { + 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; } - for (int i = 0; i < source.length; i += 1) { - if ((source[i] - target[i]).abs() > tolerance) { - return false; - } - } - return true; - }, - source.toString(), - ); + } + return true; + }, source.toString()); } // Converts `rawPixels` into a list of bytes that represent raw pixels in rgba8888. @@ -36,13 +33,12 @@ Matcher listEqual(List source, {int tolerance = 0}) { // 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 - ] + for (final int pixel in rawPixels) ...[ + (pixel >> 24) & 0xff, // r + (pixel >> 16) & 0xff, // g + (pixel >> 8) & 0xff, // b + (pixel >> 0) & 0xff, // a + ], ]); } @@ -80,10 +76,14 @@ Future _encodeToHtmlThenDecode( 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); + 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))!); + (await image.toByteData(format: ImageByteFormat.rawStraightRgba))!, + ); return resultPixels[0] == 0xED; } @@ -91,48 +91,57 @@ 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, + _pixelsToBytes([0xFF0102FF, 0x04FE05FF, 0x0708FDFF, 0x0A0B0C00]), + 2, + 2, + ); + final Uint8List actualPixels = Uint8List.sublistView( + (await sourceImage.toByteData(format: ImageByteFormat.rawStraightRgba))!, ); - 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], - ); + 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, + _pixelsToBytes([0xFF0102FF, 0x04FE05FF, 0x0708FDFF, 0x0A0B0C00]), + 2, + 2, + pixelFormat: PixelFormat.bgra8888, + ); + final Uint8List actualPixels = Uint8List.sublistView( + (await sourceImage.toByteData(format: ImageByteFormat.rawStraightRgba))!, ); - 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], - ); + 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, + _pixelsToBytes([0xFF800006, 0xFF800080, 0xFF8000C0, 0xFF8000FF]), + 2, + 2, ); final Image blueBackground = await _encodeToHtmlThenDecode( - _pixelsToBytes( - [0x0000FFFF, 0x0000FFFF, 0x0000FFFF, 0x0000FFFF], - ), 2, 2, + _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). @@ -145,9 +154,10 @@ Future testMain() async { // 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 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); @@ -155,9 +165,12 @@ Future testMain() async { final DomImageData imageData = ctx.getImageData(0, 0, 2, 2); final List actualPixels = imageData.data; - final Uint8List benchmarkPixels = _pixelsToBytes( - [0x0603F9FF, 0x80407FFF, 0xC0603FFF, 0xFF8000FF], - ); + final Uint8List benchmarkPixels = _pixelsToBytes([ + 0x0603F9FF, + 0x80407FFF, + 0xC0603FFF, + 0xFF8000FF, + ]); expect(actualPixels, listEqual(benchmarkPixels, tolerance: 1)); }); diff --git a/lib/web_ui/test/html/paragraph/general_golden_test.dart b/lib/web_ui/test/html/paragraph/general_golden_test.dart index ee3756841db53..88f4e51b0edda 100644 --- a/lib/web_ui/test/html/paragraph/general_golden_test.dart +++ b/lib/web_ui/test/html/paragraph/general_golden_test.dart @@ -36,23 +36,18 @@ Future testMain() async { paragraph = rich(EngineParagraphStyle(fontFamily: 'Roboto'), (CanvasParagraphBuilder builder) { builder.pushStyle(EngineTextStyle.only(color: blue)); builder.addText('Lorem '); - builder.pushStyle(EngineTextStyle.only( - color: green, - background: Paint()..color = red, - )); + builder.pushStyle(EngineTextStyle.only(color: green, background: Paint()..color = red)); builder.addText('ipsum '); builder.pop(); builder.addText('.'); - }) - ..layout(constrain(double.infinity)); + })..layout(constrain(double.infinity)); canvas.drawParagraph(paragraph, offset); offset = offset.translate(0, paragraph.height + 10); // Multi-line single-span. paragraph = rich(EngineParagraphStyle(fontFamily: 'Roboto'), (CanvasParagraphBuilder builder) { builder.addText('Lorem ipsum dolor sit'); - }) - ..layout(constrain(90.0)); + })..layout(constrain(90.0)); canvas.drawParagraph(paragraph, offset); offset = offset.translate(0, paragraph.height + 10); @@ -65,8 +60,7 @@ Future testMain() async { builder.addText('dolor '); builder.pop(); builder.addText('sit'); - }) - ..layout(constrain(90.0)); + })..layout(constrain(90.0)); canvas.drawParagraph(paragraph, offset); offset = offset.translate(0, paragraph.height + 10); @@ -90,24 +84,18 @@ Future testMain() async { builder.addText('sit'); } - paragraph = rich( - EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.left), - build, - )..layout(constrain(100.0)); + paragraph = rich(EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.left), build) + ..layout(constrain(100.0)); canvas.drawParagraph(paragraph, offset); offset = offset.translate(0, paragraph.height + 10); - paragraph = rich( - EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.center), - build, - )..layout(constrain(100.0)); + paragraph = rich(EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.center), build) + ..layout(constrain(100.0)); canvas.drawParagraph(paragraph, offset); offset = offset.translate(0, paragraph.height + 10); - paragraph = rich( - EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.right), - build, - )..layout(constrain(100.0)); + paragraph = rich(EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.right), build) + ..layout(constrain(100.0)); canvas.drawParagraph(paragraph, offset); offset = offset.translate(0, paragraph.height + 10); @@ -131,24 +119,18 @@ Future testMain() async { builder.addText('sit'); } - paragraph = rich( - EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.left), - build, - )..layout(constrain(100.0)); + paragraph = rich(EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.left), build) + ..layout(constrain(100.0)); canvas.drawParagraph(paragraph, offset); offset = offset.translate(0, paragraph.height + 10); - paragraph = rich( - EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.center), - build, - )..layout(constrain(100.0)); + paragraph = rich(EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.center), build) + ..layout(constrain(100.0)); canvas.drawParagraph(paragraph, offset); offset = offset.translate(0, paragraph.height + 10); - paragraph = rich( - EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.right), - build, - )..layout(constrain(100.0)); + paragraph = rich(EngineParagraphStyle(fontFamily: 'Roboto', textAlign: TextAlign.right), build) + ..layout(constrain(100.0)); canvas.drawParagraph(paragraph, offset); offset = offset.translate(0, paragraph.height + 10); @@ -237,16 +219,20 @@ Future testMain() async { final DomCanvas canvas = DomCanvas(domDocument.createElement('flt-picture')); Offset offset = const Offset(10.0, 10.0); - final CanvasParagraph paragraph = rich( - EngineParagraphStyle(fontFamily: 'Roboto'), - (CanvasParagraphBuilder builder) { - builder.pushStyle(EngineTextStyle.only(color: yellow, fontSize: 24.0)); - builder.addText('Lorem '); - builder.pushStyle(EngineTextStyle.only(color: red, fontSize: 48.0)); - builder.addText('ipsum'); - }, - )..layout(constrain(double.infinity)); - final Rect rect = Rect.fromLTWH(offset.dx, offset.dy, paragraph.maxIntrinsicWidth, paragraph.height); + final CanvasParagraph paragraph = rich(EngineParagraphStyle(fontFamily: 'Roboto'), ( + CanvasParagraphBuilder builder, + ) { + builder.pushStyle(EngineTextStyle.only(color: yellow, fontSize: 24.0)); + builder.addText('Lorem '); + builder.pushStyle(EngineTextStyle.only(color: red, fontSize: 48.0)); + builder.addText('ipsum'); + })..layout(constrain(double.infinity)); + final Rect rect = Rect.fromLTWH( + offset.dx, + offset.dy, + paragraph.maxIntrinsicWidth, + paragraph.height, + ); canvas.drawRect(rect, SurfacePaintData()..color = black.value); canvas.drawParagraph(paragraph, offset); offset = offset.translate(paragraph.maxIntrinsicWidth, 0.0); @@ -259,13 +245,22 @@ Future testMain() async { final double placeholderHeight = paragraph.height; final double placeholderWidth = paragraph.height * 2; - final CanvasParagraph paragraph2 = rich( - EngineParagraphStyle(), - (CanvasParagraphBuilder builder) { - builder.addPlaceholder(placeholderWidth, placeholderHeight, PlaceholderAlignment.baseline, baseline: TextBaseline.alphabetic); - }, - )..layout(constrain(double.infinity)); - final Rect rect2 = Rect.fromLTWH(offset.dx, offset.dy, paragraph2.maxIntrinsicWidth, paragraph2.height); + final CanvasParagraph paragraph2 = rich(EngineParagraphStyle(), ( + CanvasParagraphBuilder builder, + ) { + builder.addPlaceholder( + placeholderWidth, + placeholderHeight, + PlaceholderAlignment.baseline, + baseline: TextBaseline.alphabetic, + ); + })..layout(constrain(double.infinity)); + final Rect rect2 = Rect.fromLTWH( + offset.dx, + offset.dy, + paragraph2.maxIntrinsicWidth, + paragraph2.height, + ); canvas.drawRect(rect2, SurfacePaintData()..color = black.value); canvas.drawParagraph(paragraph2, offset); // Draw a rect in the placeholder. @@ -287,33 +282,24 @@ Future testMain() async { test('paints spans with varying heights/baselines', () { final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); - final CanvasParagraph paragraph = rich( - EngineParagraphStyle(fontFamily: 'Roboto'), - (CanvasParagraphBuilder builder) { - builder.pushStyle(EngineTextStyle.only(fontSize: 20.0)); - builder.addText('Lorem '); - builder.pushStyle(EngineTextStyle.only( - fontSize: 40.0, - background: Paint()..color = green, - )); - builder.addText('ipsum '); - builder.pushStyle(EngineTextStyle.only( - fontSize: 10.0, - color: white, - background: Paint()..color = black, - )); - builder.addText('dolor '); - builder.pushStyle(EngineTextStyle.only(fontSize: 30.0)); - builder.addText('sit '); - builder.pop(); - builder.pop(); - builder.pushStyle(EngineTextStyle.only( - fontSize: 20.0, - background: Paint()..color = blue, - )); - builder.addText('amet'); - }, - )..layout(constrain(220.0)); + final CanvasParagraph paragraph = rich(EngineParagraphStyle(fontFamily: 'Roboto'), ( + CanvasParagraphBuilder builder, + ) { + builder.pushStyle(EngineTextStyle.only(fontSize: 20.0)); + builder.addText('Lorem '); + builder.pushStyle(EngineTextStyle.only(fontSize: 40.0, background: Paint()..color = green)); + builder.addText('ipsum '); + builder.pushStyle( + EngineTextStyle.only(fontSize: 10.0, color: white, background: Paint()..color = black), + ); + builder.addText('dolor '); + builder.pushStyle(EngineTextStyle.only(fontSize: 30.0)); + builder.addText('sit '); + builder.pop(); + builder.pop(); + builder.pushStyle(EngineTextStyle.only(fontSize: 20.0, background: Paint()..color = blue)); + builder.addText('amet'); + })..layout(constrain(220.0)); canvas.drawParagraph(paragraph, Offset.zero); return takeScreenshot(canvas, bounds, 'canvas_paragraph_varying_heights'); @@ -322,17 +308,16 @@ Future testMain() async { test('respects letter-spacing', () { final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); - final CanvasParagraph paragraph = rich( - EngineParagraphStyle(fontFamily: 'Roboto'), - (CanvasParagraphBuilder builder) { - builder.pushStyle(EngineTextStyle.only(color: blue)); - builder.addText('Lorem '); - builder.pushStyle(EngineTextStyle.only(color: green, letterSpacing: 1)); - builder.addText('Lorem '); - builder.pushStyle(EngineTextStyle.only(color: red, letterSpacing: 3)); - builder.addText('Lorem'); - }, - )..layout(constrain(double.infinity)); + final CanvasParagraph paragraph = rich(EngineParagraphStyle(fontFamily: 'Roboto'), ( + CanvasParagraphBuilder builder, + ) { + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('Lorem '); + builder.pushStyle(EngineTextStyle.only(color: green, letterSpacing: 1)); + builder.addText('Lorem '); + builder.pushStyle(EngineTextStyle.only(color: red, letterSpacing: 3)); + builder.addText('Lorem'); + })..layout(constrain(double.infinity)); canvas.drawParagraph(paragraph, Offset.zero); return takeScreenshot(canvas, bounds, 'canvas_paragraph_letter_spacing'); @@ -370,24 +355,25 @@ Future testMain() async { TextDecorationStyle.wavy, ]; - final CanvasParagraph paragraph = rich( - EngineParagraphStyle(fontFamily: 'Roboto'), - (CanvasParagraphBuilder builder) { - for (final TextDecorationStyle decorationStyle in decorationStyles) { - builder.pushStyle(EngineTextStyle.only( + final CanvasParagraph paragraph = rich(EngineParagraphStyle(fontFamily: 'Roboto'), ( + CanvasParagraphBuilder builder, + ) { + for (final TextDecorationStyle decorationStyle in decorationStyles) { + builder.pushStyle( + EngineTextStyle.only( color: const Color.fromRGBO(50, 50, 255, 1.0), decoration: TextDecoration.underline, decorationStyle: decorationStyle, decorationColor: red, fontFamily: 'Roboto', fontSize: 30, - )); - builder.addText('Hello World'); - builder.pop(); - builder.addText(' '); - } - }, - )..layout(constrain(double.infinity)); + ), + ); + builder.addText('Hello World'); + builder.pop(); + builder.addText(' '); + } + })..layout(constrain(double.infinity)); canvas.drawParagraph(paragraph, Offset.zero); return takeScreenshot(canvas, bounds, 'canvas_paragraph_decoration'); @@ -403,76 +389,57 @@ Future testMain() async { const FontFeature disableLigatures = FontFeature('liga', 0); - final CanvasParagraph paragraph = rich( - EngineParagraphStyle(fontFamily: 'Roboto'), - (CanvasParagraphBuilder builder) { - // Small Caps - builder.pushStyle(EngineTextStyle.only( - height: 1.5, - color: black, - fontSize: 32.0, - )); - builder.pushStyle(EngineTextStyle.only( - color: blue, - fontFeatures: [enableSmallCaps], - )); - builder.addText(text); - // Make sure disabling a font feature also works. - builder.pushStyle(EngineTextStyle.only( - color: black, - fontFeatures: [disableSmallCaps], - )); - builder.addText(' (smcp)\n'); - builder.pop(); // disableSmallCaps - builder.pop(); // enableSmallCaps - - // No ligatures - builder.pushStyle(EngineTextStyle.only( - color: blue, - fontFeatures: [disableLigatures], - )); - builder.addText(text); - builder.pop(); // disableLigatures - builder.addText(' (no liga)\n'); - - // No font features - builder.pushStyle(EngineTextStyle.only( - color: blue, - )); - builder.addText(text); - builder.pop(); // color: blue - builder.addText(' (none)\n'); - - // Onum - builder.pushStyle(EngineTextStyle.only( - color: blue, - fontFeatures: [enableOnum], - )); - builder.addText(numeric); - builder.pop(); // enableOnum - builder.addText(' (onum)\n'); - - // No font features - builder.pushStyle(EngineTextStyle.only( - color: blue, - )); - builder.addText(numeric); - builder.pop(); // color: blue - builder.addText(' (none)\n\n'); - - // Multiple font features - builder.addText('Combined (smcp, onum):\n'); - builder.pushStyle(EngineTextStyle.only( - color: blue, - fontFeatures: [ - enableSmallCaps, - enableOnum, - ], - )); - builder.addText('$text $numeric'); - builder.pop(); // enableSmallCaps, enableOnum - }, - )..layout(constrain(double.infinity)); + final CanvasParagraph paragraph = rich(EngineParagraphStyle(fontFamily: 'Roboto'), ( + CanvasParagraphBuilder builder, + ) { + // Small Caps + builder.pushStyle(EngineTextStyle.only(height: 1.5, color: black, fontSize: 32.0)); + builder.pushStyle( + EngineTextStyle.only(color: blue, fontFeatures: [enableSmallCaps]), + ); + builder.addText(text); + // Make sure disabling a font feature also works. + builder.pushStyle( + EngineTextStyle.only(color: black, fontFeatures: [disableSmallCaps]), + ); + builder.addText(' (smcp)\n'); + builder.pop(); // disableSmallCaps + builder.pop(); // enableSmallCaps + + // No ligatures + builder.pushStyle( + EngineTextStyle.only(color: blue, fontFeatures: [disableLigatures]), + ); + builder.addText(text); + builder.pop(); // disableLigatures + builder.addText(' (no liga)\n'); + + // No font features + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText(text); + builder.pop(); // color: blue + builder.addText(' (none)\n'); + + // Onum + builder.pushStyle(EngineTextStyle.only(color: blue, fontFeatures: [enableOnum])); + builder.addText(numeric); + builder.pop(); // enableOnum + builder.addText(' (onum)\n'); + + // No font features + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText(numeric); + builder.pop(); // color: blue + builder.addText(' (none)\n\n'); + + // Multiple font features + builder.addText('Combined (smcp, onum):\n'); + builder.pushStyle( + EngineTextStyle.only(color: blue, fontFeatures: [enableSmallCaps, enableOnum]), + ); + builder.addText('$text $numeric'); + builder.pop(); // enableSmallCaps, enableOnum + })..layout(constrain(double.infinity)); canvas.drawParagraph(paragraph, Offset.zero); } @@ -494,28 +461,23 @@ Future testMain() async { const String text = 'ABCDE 12345\n'; FontVariation weight(double w) => FontVariation('wght', w); - final CanvasParagraph paragraph = rich( - EngineParagraphStyle(fontFamily: 'RobotoVariable'), - (CanvasParagraphBuilder builder) { - builder.pushStyle(EngineTextStyle.only( - fontSize: 48.0, - )); - builder.addText(text); - builder.pushStyle(EngineTextStyle.only( - fontSize: 48.0, - fontVariations: [weight(900)], - )); - builder.addText(text); - builder.pushStyle(EngineTextStyle.only( - fontSize: 48.0, - fontVariations: [weight(200)], - )); - builder.addText(text); - builder.pop(); - builder.pop(); - builder.pop(); - }, - )..layout(constrain(double.infinity)); + final CanvasParagraph paragraph = rich(EngineParagraphStyle(fontFamily: 'RobotoVariable'), ( + CanvasParagraphBuilder builder, + ) { + builder.pushStyle(EngineTextStyle.only(fontSize: 48.0)); + builder.addText(text); + builder.pushStyle( + EngineTextStyle.only(fontSize: 48.0, fontVariations: [weight(900)]), + ); + builder.addText(text); + builder.pushStyle( + EngineTextStyle.only(fontSize: 48.0, fontVariations: [weight(200)]), + ); + builder.addText(text); + builder.pop(); + builder.pop(); + builder.pop(); + })..layout(constrain(double.infinity)); canvas.drawParagraph(paragraph, Offset.zero); } @@ -576,16 +538,47 @@ Future testMain() async { builder.pushStyle(EngineTextStyle.only(color: blue)); builder.addText('Lorem'); builder.pop(); - builder.pushStyle(EngineTextStyle.only(foreground: Paint()..color = red..style = PaintingStyle.stroke)); + builder.pushStyle( + EngineTextStyle.only( + foreground: + Paint() + ..color = red + ..style = PaintingStyle.stroke, + ), + ); builder.addText('ipsum\n'); builder.pop(); - builder.pushStyle(EngineTextStyle.only(foreground: Paint()..color = blue..style = PaintingStyle.stroke..strokeWidth = 0.0)); + builder.pushStyle( + EngineTextStyle.only( + foreground: + Paint() + ..color = blue + ..style = PaintingStyle.stroke + ..strokeWidth = 0.0, + ), + ); builder.addText('dolor'); builder.pop(); - builder.pushStyle(EngineTextStyle.only(foreground: Paint()..color = green..style = PaintingStyle.stroke..strokeWidth = 2.0)); + builder.pushStyle( + EngineTextStyle.only( + foreground: + Paint() + ..color = green + ..style = PaintingStyle.stroke + ..strokeWidth = 2.0, + ), + ); builder.addText('sit\n'); builder.pop(); - builder.pushStyle(EngineTextStyle.only(foreground: Paint()..color = yellow..style = PaintingStyle.stroke..strokeWidth = 4.0)); + builder.pushStyle( + EngineTextStyle.only( + foreground: + Paint() + ..color = yellow + ..style = PaintingStyle.stroke + ..strokeWidth = 4.0, + ), + ); builder.addText('amet'); }, ); @@ -610,11 +603,9 @@ Future testMain() async { test('paragraph bounds hug the text inside the paragraph', () async { const Rect bounds = Rect.fromLTWH(0, 0, 150, 100); - final CanvasParagraphBuilder builder = CanvasParagraphBuilder(EngineParagraphStyle( - fontFamily: 'Ahem', - fontSize: 20, - textAlign: TextAlign.center, - )); + final CanvasParagraphBuilder builder = CanvasParagraphBuilder( + EngineParagraphStyle(fontFamily: 'Ahem', fontSize: 20, textAlign: TextAlign.center), + ); // Expected layout with center-alignment is something like this: // @@ -642,12 +633,7 @@ Future testMain() async { canvas.translate(20, 20); canvas.drawParagraph(paragraph, Offset.zero); canvas.drawRect( - Rect.fromLTRB( - 0, - 0, - paragraph.width, - paragraph.height, - ), + Rect.fromLTRB(0, 0, paragraph.width, paragraph.height), SurfacePaintData() ..style = PaintingStyle.stroke ..strokeWidth = 1, diff --git a/lib/web_ui/test/html/paragraph/helper.dart b/lib/web_ui/test/html/paragraph/helper.dart index 855da331fb625..f3e92424d8fcb 100644 --- a/lib/web_ui/test/html/paragraph/helper.dart +++ b/lib/web_ui/test/html/paragraph/helper.dart @@ -36,20 +36,13 @@ const Color blue = Color(0xFF0000FF); const Color yellow = Color(0xFFFFEB3B); const Color lightPurple = Color(0xFFE1BEE7); -final EngineParagraphStyle ahemStyle = EngineParagraphStyle( - fontFamily: 'Ahem', - fontSize: 10, -); +final EngineParagraphStyle ahemStyle = EngineParagraphStyle(fontFamily: 'Ahem', fontSize: 10); ParagraphConstraints constrain(double width) { return ParagraphConstraints(width: width); } -CanvasParagraph plain( - EngineParagraphStyle style, - String text, { - EngineTextStyle? textStyle, -}) { +CanvasParagraph plain(EngineParagraphStyle style, String text, {EngineTextStyle? textStyle}) { final CanvasParagraphBuilder builder = CanvasParagraphBuilder(style); if (textStyle != null) { builder.pushStyle(textStyle); @@ -58,20 +51,13 @@ CanvasParagraph plain( return builder.build(); } -CanvasParagraph rich( - EngineParagraphStyle style, - void Function(CanvasParagraphBuilder) callback, -) { +CanvasParagraph rich(EngineParagraphStyle style, void Function(CanvasParagraphBuilder) callback) { final CanvasParagraphBuilder builder = CanvasParagraphBuilder(style); callback(builder); return builder.build(); } -Future takeScreenshot( - EngineCanvas canvas, - Rect region, - String fileName, -) async { +Future takeScreenshot(EngineCanvas canvas, Rect region, String fileName) async { final DomElement sceneElement = createDomElement('flt-scene'); if (isIosSafari) { // Shrink to fit on the iPhone screen. @@ -95,17 +81,12 @@ Future takeScreenshot( /// The placeholder is filled relative to [offset]. /// /// Throws if the paragraph contains more than one placeholder. -void fillPlaceholder( - EngineCanvas canvas, - Offset offset, - CanvasParagraph paragraph, -) { +void fillPlaceholder(EngineCanvas canvas, Offset offset, CanvasParagraph paragraph) { final TextBox placeholderBox = paragraph.getBoxesForPlaceholders().single; final SurfacePaint paint = SurfacePaint()..color = red; canvas.drawRect(placeholderBox.toRect().shift(offset), paint.paintData); } - /// Fill the given [boxes] with rectangles of the given [color]. /// /// All rectangles are filled relative to [offset]. diff --git a/lib/web_ui/test/html/paragraph/justify_golden_test.dart b/lib/web_ui/test/html/paragraph/justify_golden_test.dart index 4538fe0a70fac..0692e082fe614 100644 --- a/lib/web_ui/test/html/paragraph/justify_golden_test.dart +++ b/lib/web_ui/test/html/paragraph/justify_golden_test.dart @@ -32,8 +32,7 @@ Future testMain() async { builder.pushStyle(EngineTextStyle.only(color: green)); builder.addText('adipiscing elit, sed do eiusmod tempor incididunt ut '); builder.pushStyle(EngineTextStyle.only(color: red)); - builder - .addText('labore et dolore magna aliqua. Ut enim ad minim veniam, '); + builder.addText('labore et dolore magna aliqua. Ut enim ad minim veniam, '); builder.pushStyle(EngineTextStyle.only(color: lightPurple)); builder.addText('quis nostrud exercitation ullamco '); builder.pushStyle(EngineTextStyle.only(color: blue)); @@ -41,11 +40,7 @@ Future testMain() async { } final CanvasParagraph paragraph = rich( - EngineParagraphStyle( - fontFamily: 'Roboto', - fontSize: 20.0, - textAlign: TextAlign.justify, - ), + EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 20.0, textAlign: TextAlign.justify), build, ); paragraph.layout(constrain(250.0)); @@ -80,11 +75,7 @@ Future testMain() async { } final CanvasParagraph paragraph = rich( - EngineParagraphStyle( - fontFamily: 'Roboto', - fontSize: 20.0, - textAlign: TextAlign.justify, - ), + EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 20.0, textAlign: TextAlign.justify), build, ); paragraph.layout(constrain(250.0)); @@ -95,16 +86,14 @@ Future testMain() async { const Rect bounds = Rect.fromLTWH(0, 0, 400, 400); final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); testJustifyWithEmptyLine(canvas); - return takeScreenshot( - canvas, bounds, 'canvas_paragraph_justify_empty_line'); + return takeScreenshot(canvas, bounds, 'canvas_paragraph_justify_empty_line'); }); test('TextAlign.justify with single space and empty line (DOM)', () { const Rect bounds = Rect.fromLTWH(0, 0, 400, 400); final DomCanvas canvas = DomCanvas(domDocument.createElement('flt-picture')); testJustifyWithEmptyLine(canvas); - return takeScreenshot( - canvas, bounds, 'canvas_paragraph_justify_empty_line_dom'); + return takeScreenshot(canvas, bounds, 'canvas_paragraph_justify_empty_line_dom'); }); void testJustifyWithEllipsis(EngineCanvas canvas) { @@ -115,25 +104,20 @@ Future testMain() async { maxLines: 4, ellipsis: '...', ); - final CanvasParagraph paragraph = rich( - paragraphStyle, - (CanvasParagraphBuilder builder) { - builder.pushStyle(EngineTextStyle.only(color: black)); - builder.addText('Lorem ipsum dolor sit '); - builder.pushStyle(EngineTextStyle.only(color: blue)); - builder.addText('amet, consectetur '); - builder.pushStyle(EngineTextStyle.only(color: green)); - builder - .addText('adipiscing elit, sed do eiusmod tempor incididunt ut '); - builder.pushStyle(EngineTextStyle.only(color: red)); - builder.addText( - 'labore et dolore magna aliqua. Ut enim ad minim veniam, '); - builder.pushStyle(EngineTextStyle.only(color: lightPurple)); - builder.addText('quis nostrud exercitation ullamco '); - builder.pushStyle(EngineTextStyle.only(color: blue)); - builder.addText('laboris nisi ut aliquip ex ea commodo consequat.'); - }, - ); + final CanvasParagraph paragraph = rich(paragraphStyle, (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: black)); + builder.addText('Lorem ipsum dolor sit '); + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('amet, consectetur '); + builder.pushStyle(EngineTextStyle.only(color: green)); + builder.addText('adipiscing elit, sed do eiusmod tempor incididunt ut '); + builder.pushStyle(EngineTextStyle.only(color: red)); + builder.addText('labore et dolore magna aliqua. Ut enim ad minim veniam, '); + builder.pushStyle(EngineTextStyle.only(color: lightPurple)); + builder.addText('quis nostrud exercitation ullamco '); + builder.pushStyle(EngineTextStyle.only(color: blue)); + builder.addText('laboris nisi ut aliquip ex ea commodo consequat.'); + }); paragraph.layout(constrain(250)); canvas.drawParagraph(paragraph, Offset.zero); } @@ -158,28 +142,23 @@ Future testMain() async { fontSize: 20.0, textAlign: TextAlign.justify, ); - final CanvasParagraph paragraph = rich( - paragraphStyle, - (CanvasParagraphBuilder builder) { - builder.pushStyle(EngineTextStyle.only(color: black)); - builder.pushStyle(bg(blue)); - builder.addText('Lorem ipsum dolor sit '); - builder.pushStyle(bg(black)); - builder.pushStyle(EngineTextStyle.only(color: white)); - builder.addText('amet, consectetur '); - builder.pop(); - builder.pushStyle(bg(green)); - builder - .addText('adipiscing elit, sed do eiusmod tempor incididunt ut '); - builder.pushStyle(bg(yellow)); - builder.addText( - 'labore et dolore magna aliqua. Ut enim ad minim veniam, '); - builder.pushStyle(bg(red)); - builder.addText('quis nostrud exercitation ullamco '); - builder.pushStyle(bg(green)); - builder.addText('laboris nisi ut aliquip ex ea commodo consequat.'); - }, - ); + final CanvasParagraph paragraph = rich(paragraphStyle, (CanvasParagraphBuilder builder) { + builder.pushStyle(EngineTextStyle.only(color: black)); + builder.pushStyle(bg(blue)); + builder.addText('Lorem ipsum dolor sit '); + builder.pushStyle(bg(black)); + builder.pushStyle(EngineTextStyle.only(color: white)); + builder.addText('amet, consectetur '); + builder.pop(); + builder.pushStyle(bg(green)); + builder.addText('adipiscing elit, sed do eiusmod tempor incididunt ut '); + builder.pushStyle(bg(yellow)); + builder.addText('labore et dolore magna aliqua. Ut enim ad minim veniam, '); + builder.pushStyle(bg(red)); + builder.addText('quis nostrud exercitation ullamco '); + builder.pushStyle(bg(green)); + builder.addText('laboris nisi ut aliquip ex ea commodo consequat.'); + }); paragraph.layout(constrain(250)); canvas.drawParagraph(paragraph, Offset.zero); } @@ -188,16 +167,14 @@ Future testMain() async { const Rect bounds = Rect.fromLTWH(0, 0, 400, 400); final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); testJustifyWithBackground(canvas); - return takeScreenshot( - canvas, bounds, 'canvas_paragraph_justify_background'); + return takeScreenshot(canvas, bounds, 'canvas_paragraph_justify_background'); }); test('TextAlign.justify with background (DOM)', () { const Rect bounds = Rect.fromLTWH(0, 0, 400, 400); final DomCanvas canvas = DomCanvas(domDocument.createElement('flt-picture')); testJustifyWithBackground(canvas); - return takeScreenshot( - canvas, bounds, 'canvas_paragraph_justify_background_dom'); + return takeScreenshot(canvas, bounds, 'canvas_paragraph_justify_background_dom'); }); void testJustifyWithPlaceholder(EngineCanvas canvas) { @@ -215,11 +192,7 @@ Future testMain() async { } final CanvasParagraph paragraph = rich( - EngineParagraphStyle( - fontFamily: 'Roboto', - fontSize: 20.0, - textAlign: TextAlign.justify, - ), + EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 20.0, textAlign: TextAlign.justify), build, ); paragraph.layout(constrain(250.0)); @@ -254,11 +227,7 @@ Future testMain() async { } final CanvasParagraph paragraph = rich( - EngineParagraphStyle( - fontFamily: 'Roboto', - fontSize: 20.0, - textAlign: TextAlign.justify, - ), + EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 20.0, textAlign: TextAlign.justify), build, ); paragraph.layout(constrain(250.0)); @@ -299,11 +268,7 @@ Future testMain() async { } final CanvasParagraph paragraph = rich( - EngineParagraphStyle( - fontFamily: 'Roboto', - fontSize: 20.0, - textAlign: TextAlign.justify, - ), + EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 20.0, textAlign: TextAlign.justify), build, ); paragraph.layout(constrain(250.0)); diff --git a/lib/web_ui/test/html/paragraph/overflow_golden_test.dart b/lib/web_ui/test/html/paragraph/overflow_golden_test.dart index bde5887d7d661..1e0b464fed858 100644 --- a/lib/web_ui/test/html/paragraph/overflow_golden_test.dart +++ b/lib/web_ui/test/html/paragraph/overflow_golden_test.dart @@ -30,9 +30,10 @@ Future testMain() async { const double fontSize = 22.0; const double width = 126.0; const double padding = 20.0; - final SurfacePaintData borderPaint = SurfacePaintData() - ..color = black.value - ..style = PaintingStyle.stroke; + final SurfacePaintData borderPaint = + SurfacePaintData() + ..color = black.value + ..style = PaintingStyle.stroke; paragraph = rich( EngineParagraphStyle(fontFamily: 'Roboto', fontSize: fontSize, ellipsis: '...'), diff --git a/lib/web_ui/test/html/paragraph/placeholders_golden_test.dart b/lib/web_ui/test/html/paragraph/placeholders_golden_test.dart index 17f08ed508e29..9da9d9d7bdf4e 100644 --- a/lib/web_ui/test/html/paragraph/placeholders_golden_test.dart +++ b/lib/web_ui/test/html/paragraph/placeholders_golden_test.dart @@ -62,11 +62,7 @@ Future testMain() async { test('draws paragraphs with placeholders and text align', () { final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); - const List aligns = [ - TextAlign.left, - TextAlign.center, - TextAlign.right, - ]; + const List aligns = [TextAlign.left, TextAlign.center, TextAlign.right]; Offset offset = Offset.zero; for (final TextAlign align in aligns) { @@ -96,11 +92,7 @@ Future testMain() async { test('draws paragraphs with placeholders and text align in DOM mode', () { final DomCanvas canvas = DomCanvas(domDocument.createElement('flt-picture')); - const List aligns = [ - TextAlign.left, - TextAlign.center, - TextAlign.right, - ]; + const List aligns = [TextAlign.left, TextAlign.center, TextAlign.right]; Offset offset = Offset.zero; for (final TextAlign align in aligns) { @@ -137,7 +129,12 @@ Future testMain() async { final CanvasParagraph paragraph1 = rich( EngineParagraphStyle(fontFamily: 'Roboto', fontSize: 24.0, textAlign: TextAlign.center), (CanvasParagraphBuilder builder) { - builder.addPlaceholder(80.0, 50.0, PlaceholderAlignment.baseline, baseline: TextBaseline.alphabetic); + builder.addPlaceholder( + 80.0, + 50.0, + PlaceholderAlignment.baseline, + baseline: TextBaseline.alphabetic, + ); builder.pushStyle(TextStyle(color: black)); builder.addText(' Lorem ipsum.'); }, @@ -156,7 +153,12 @@ Future testMain() async { (CanvasParagraphBuilder builder) { builder.pushStyle(TextStyle(color: black)); builder.addText('Lorem ipsum '); - builder.addPlaceholder(80.0, 50.0, PlaceholderAlignment.baseline, baseline: TextBaseline.alphabetic); + builder.addPlaceholder( + 80.0, + 50.0, + PlaceholderAlignment.baseline, + baseline: TextBaseline.alphabetic, + ); }, )..layout(constrain(400.0)); @@ -173,7 +175,12 @@ Future testMain() async { (CanvasParagraphBuilder builder) { builder.pushStyle(TextStyle(color: black)); builder.addText('Lorem ipsum '); - builder.addPlaceholder(80.0, 50.0, PlaceholderAlignment.baseline, baseline: TextBaseline.alphabetic); + builder.addPlaceholder( + 80.0, + 50.0, + PlaceholderAlignment.baseline, + baseline: TextBaseline.alphabetic, + ); }, )..layout(constrain(200.0)); @@ -186,12 +193,11 @@ Future testMain() async { }); } -void surroundParagraph( - EngineCanvas canvas, - Offset offset, - CanvasParagraph paragraph, -) { +void surroundParagraph(EngineCanvas canvas, Offset offset, CanvasParagraph paragraph) { final Rect rect = offset & Size(paragraph.width, paragraph.height); - final SurfacePaint paint = SurfacePaint()..color = blue..style = PaintingStyle.stroke; + final SurfacePaint paint = + SurfacePaint() + ..color = blue + ..style = PaintingStyle.stroke; canvas.drawRect(rect, paint.paintData); } diff --git a/lib/web_ui/test/html/paragraph/shadows_golden_test.dart b/lib/web_ui/test/html/paragraph/shadows_golden_test.dart index d9f37a0af2140..c73f478f1c4da 100644 --- a/lib/web_ui/test/html/paragraph/shadows_golden_test.dart +++ b/lib/web_ui/test/html/paragraph/shadows_golden_test.dart @@ -26,30 +26,31 @@ Future testMain() async { test('paints multiple shadows', () { final BitmapCanvas canvas = BitmapCanvas(bounds, RenderStrategy()); - final CanvasParagraph paragraph = rich( - EngineParagraphStyle(fontFamily: 'Roboto'), - (CanvasParagraphBuilder builder) { - builder.pushStyle(EngineTextStyle.only( + final CanvasParagraph paragraph = rich(EngineParagraphStyle(fontFamily: 'Roboto'), ( + CanvasParagraphBuilder builder, + ) { + builder.pushStyle( + EngineTextStyle.only( fontSize: 32.0, color: blue, shadows: [ - const Shadow(color: red, blurRadius:2.0, offset: Offset(4.0, 2.0)), + const Shadow(color: red, blurRadius: 2.0, offset: Offset(4.0, 2.0)), const Shadow(color: green, blurRadius: 3.0), ], - )); - builder.addText('Lorem '); - builder.pushStyle(EngineTextStyle.only( + ), + ); + builder.addText('Lorem '); + builder.pushStyle( + EngineTextStyle.only( color: green, background: Paint()..color = yellow, - shadows: [ - const Shadow(blurRadius: 10.0), - ], - )); - builder.addText('ipsum'); - builder.pop(); - builder.addText('dolor.'); - }, - )..layout(constrain(double.infinity)); + shadows: [const Shadow(blurRadius: 10.0)], + ), + ); + builder.addText('ipsum'); + builder.pop(); + builder.addText('dolor.'); + })..layout(constrain(double.infinity)); canvas.drawParagraph(paragraph, Offset.zero); return takeScreenshot(canvas, bounds, 'canvas_paragraph_shadows'); diff --git a/lib/web_ui/test/html/paragraph/text_goldens.dart b/lib/web_ui/test/html/paragraph/text_goldens.dart index f6542a54e8221..09521bbaa2879 100644 --- a/lib/web_ui/test/html/paragraph/text_goldens.dart +++ b/lib/web_ui/test/html/paragraph/text_goldens.dart @@ -19,13 +19,13 @@ class EngineGoldenTester { /// The size of the browser window used in this golden test. final ui.Size viewportSize; - static Future initialize( - {ui.Size viewportSize = const ui.Size(2400, 1800)}) async { + static Future initialize({ + ui.Size viewportSize = const ui.Size(2400, 1800), + }) async { assert(() { if (viewportSize.width.ceil() != viewportSize.width || viewportSize.height.ceil() != viewportSize.height) { - throw Exception( - 'Gold only supports integer screen sizes, but found: $viewportSize'); + throw Exception('Gold only supports integer screen sizes, but found: $viewportSize'); } return true; }()); @@ -33,28 +33,17 @@ class EngineGoldenTester { return EngineGoldenTester(viewportSize); } - ui.Rect get viewportRegion => - ui.Rect.fromLTWH(0, 0, viewportSize.width, viewportSize.height); + ui.Rect get viewportRegion => ui.Rect.fromLTWH(0, 0, viewportSize.width, viewportSize.height); - Future diffScreenshot( - String fileName, { - ui.Rect? region, - }) async { - await matchGoldenFile( - '$fileName.png', - region: region ?? viewportRegion, - ); + Future diffScreenshot(String fileName, {ui.Rect? region}) async { + await matchGoldenFile('$fileName.png', region: region ?? viewportRegion); } /// Prepares the DOM and inserts all the necessary nodes, then invokes Gold's /// screenshot diffing. /// /// It also cleans up the DOM after itself. - Future diffCanvasScreenshot( - EngineCanvas canvas, - String fileName, { - ui.Rect? region, - }) async { + Future diffCanvasScreenshot(EngineCanvas canvas, String fileName, {ui.Rect? region}) async { // Wrap in so that our CSS selectors kick in. final DomElement sceneElement = createDomElement('flt-scene'); if (isIosSafari) { @@ -70,10 +59,7 @@ class EngineGoldenTester { if (canvas is BitmapCanvas) { screenshotName += '+canvas_measurement'; } - await diffScreenshot( - screenshotName, - region: region, - ); + await diffScreenshot(screenshotName, region: region); } finally { // The page is reused across tests, so remove the element after taking the // screenshot. @@ -105,8 +91,7 @@ CanvasParagraph paragraph( ui.TextStyle? textStyle, double maxWidth = double.infinity, }) { - final ui.ParagraphBuilder builder = - ui.ParagraphBuilder(paragraphStyle ?? ui.ParagraphStyle()); + final ui.ParagraphBuilder builder = ui.ParagraphBuilder(paragraphStyle ?? ui.ParagraphStyle()); builder.pushStyle(textStyle ?? _defaultTextStyle); builder.addText(text); builder.pop(); diff --git a/lib/web_ui/test/html/paragraph/text_multiline_clipping_golden_test.dart b/lib/web_ui/test/html/paragraph/text_multiline_clipping_golden_test.dart index 855d68cde0747..da961f615a8ab 100644 --- a/lib/web_ui/test/html/paragraph/text_multiline_clipping_golden_test.dart +++ b/lib/web_ui/test/html/paragraph/text_multiline_clipping_golden_test.dart @@ -36,102 +36,85 @@ Future testMain() async { recordingCanvas.apply(canvas, screenRect); } - testEachCanvas( - 'clips multiline text against rectangle', - (EngineCanvas canvas) { - // [DomCanvas] doesn't support clip commands. - if (canvas is! DomCanvas) { - paintTest(canvas, paintTextWithClipRect); - return goldenTester.diffCanvasScreenshot( - canvas, 'multiline_text_clipping_rect'); - } - return null; - }, - ); - - testEachCanvas( - 'clips multiline text against rectangle with transform', - (EngineCanvas canvas) { - // [DomCanvas] doesn't support clip commands. - if (canvas is! DomCanvas) { - paintTest(canvas, paintTextWithClipRectTranslated); - return goldenTester.diffCanvasScreenshot( - canvas, 'multiline_text_clipping_rect_translate'); - } - return null; - }, - ); - - testEachCanvas( - 'clips multiline text against round rectangle', - (EngineCanvas canvas) { - // [DomCanvas] doesn't support clip commands. - if (canvas is! DomCanvas) { - paintTest(canvas, paintTextWithClipRoundRect); - return goldenTester.diffCanvasScreenshot( - canvas, 'multiline_text_clipping_roundrect'); - } - return null; - }, - ); - - testEachCanvas( - 'clips multiline text against path', - (EngineCanvas canvas) { - // [DomCanvas] doesn't support clip commands. - if (canvas is! DomCanvas) { - paintTest(canvas, paintTextWithClipPath); - return goldenTester.diffCanvasScreenshot( - canvas, 'multiline_text_clipping_path'); - } - return null; - }, - ); - - testEachCanvas( - 'clips multiline text against stack of rects', - (EngineCanvas canvas) { - // [DomCanvas] doesn't support clip commands. - if (canvas is! DomCanvas) { - paintTest(canvas, paintTextWithClipStack); - return goldenTester.diffCanvasScreenshot( - canvas, 'multiline_text_clipping_stack1'); - } - return null; - }, - ); + testEachCanvas('clips multiline text against rectangle', (EngineCanvas canvas) { + // [DomCanvas] doesn't support clip commands. + if (canvas is! DomCanvas) { + paintTest(canvas, paintTextWithClipRect); + return goldenTester.diffCanvasScreenshot(canvas, 'multiline_text_clipping_rect'); + } + return null; + }); + + testEachCanvas('clips multiline text against rectangle with transform', (EngineCanvas canvas) { + // [DomCanvas] doesn't support clip commands. + if (canvas is! DomCanvas) { + paintTest(canvas, paintTextWithClipRectTranslated); + return goldenTester.diffCanvasScreenshot(canvas, 'multiline_text_clipping_rect_translate'); + } + return null; + }); + + testEachCanvas('clips multiline text against round rectangle', (EngineCanvas canvas) { + // [DomCanvas] doesn't support clip commands. + if (canvas is! DomCanvas) { + paintTest(canvas, paintTextWithClipRoundRect); + return goldenTester.diffCanvasScreenshot(canvas, 'multiline_text_clipping_roundrect'); + } + return null; + }); + + testEachCanvas('clips multiline text against path', (EngineCanvas canvas) { + // [DomCanvas] doesn't support clip commands. + if (canvas is! DomCanvas) { + paintTest(canvas, paintTextWithClipPath); + return goldenTester.diffCanvasScreenshot(canvas, 'multiline_text_clipping_path'); + } + return null; + }); + + testEachCanvas('clips multiline text against stack of rects', (EngineCanvas canvas) { + // [DomCanvas] doesn't support clip commands. + if (canvas is! DomCanvas) { + paintTest(canvas, paintTextWithClipStack); + return goldenTester.diffCanvasScreenshot(canvas, 'multiline_text_clipping_stack1'); + } + return null; + }); } const Rect testBounds = Rect.fromLTRB(50, 50, 230, 220); void drawBackground(RecordingCanvas canvas) { canvas.drawRect( - testBounds, - SurfacePaint() - ..style = PaintingStyle.fill - ..color = const Color(0xFF9E9E9E)); + testBounds, + SurfacePaint() + ..style = PaintingStyle.fill + ..color = const Color(0xFF9E9E9E), + ); canvas.drawRect( - testBounds.inflate(-40), - SurfacePaint() - ..strokeWidth = 1 - ..style = PaintingStyle.stroke - ..color = const Color(0xFF009688)); + testBounds.inflate(-40), + SurfacePaint() + ..strokeWidth = 1 + ..style = PaintingStyle.stroke + ..color = const Color(0xFF009688), + ); } void drawQuickBrownFox(RecordingCanvas canvas) { canvas.drawParagraph( - paragraph( - 'The quick brown fox jumps over the lazy dog', - textStyle: TextStyle( - color: const Color(0xFF000000), - decoration: TextDecoration.none, - fontFamily: 'Roboto', - fontSize: 30, - background: Paint()..color = const Color.fromRGBO(50, 255, 50, 1.0), - ), - maxWidth: 180, + paragraph( + 'The quick brown fox jumps over the lazy dog', + textStyle: TextStyle( + color: const Color(0xFF000000), + decoration: TextDecoration.none, + fontFamily: 'Roboto', + fontSize: 30, + background: Paint()..color = const Color.fromRGBO(50, 255, 50, 1.0), ), - Offset(testBounds.left, testBounds.top)); + maxWidth: 180, + ), + Offset(testBounds.left, testBounds.top), + ); } void paintTextWithClipRect(RecordingCanvas canvas) { @@ -150,16 +133,19 @@ void paintTextWithClipRectTranslated(RecordingCanvas canvas) { const Color deepOrange = Color(0xFFFF5722); void paintTextWithClipRoundRect(RecordingCanvas canvas) { - final RRect roundRect = RRect.fromRectAndCorners(testBounds.inflate(-40), - topRight: const Radius.elliptical(45, 40), - bottomLeft: const Radius.elliptical(50, 40), - bottomRight: const Radius.circular(30)); + final RRect roundRect = RRect.fromRectAndCorners( + testBounds.inflate(-40), + topRight: const Radius.elliptical(45, 40), + bottomLeft: const Radius.elliptical(50, 40), + bottomRight: const Radius.circular(30), + ); drawBackground(canvas); canvas.drawRRect( - roundRect, - SurfacePaint() - ..color = deepOrange - ..style = PaintingStyle.fill); + roundRect, + SurfacePaint() + ..color = deepOrange + ..style = PaintingStyle.fill, + ); canvas.clipRRect(roundRect); drawQuickBrownFox(canvas); } @@ -178,10 +164,11 @@ void paintTextWithClipPath(RecordingCanvas canvas) { path.quadraticBezierTo(midX, midY, clipBounds.left - delta, midY); path.close(); canvas.drawPath( - path, - SurfacePaint() - ..color = deepOrange - ..style = PaintingStyle.fill); + path, + SurfacePaint() + ..color = deepOrange + ..style = PaintingStyle.fill, + ); canvas.clipPath(path); drawQuickBrownFox(canvas); } @@ -194,9 +181,10 @@ void paintTextWithClipStack(RecordingCanvas canvas) { canvas.translate(40, -40); canvas.clipRect(inflatedRect, ClipOp.intersect); canvas.drawRect( - inflatedRect, - SurfacePaint() - ..color = deepOrange - ..style = PaintingStyle.fill); + inflatedRect, + SurfacePaint() + ..color = deepOrange + ..style = PaintingStyle.fill, + ); drawQuickBrownFox(canvas); } diff --git a/lib/web_ui/test/html/paragraph/text_overflow_golden_test.dart b/lib/web_ui/test/html/paragraph/text_overflow_golden_test.dart index 0a308e0534558..d385cb9e79dbe 100644 --- a/lib/web_ui/test/html/paragraph/text_overflow_golden_test.dart +++ b/lib/web_ui/test/html/paragraph/text_overflow_golden_test.dart @@ -50,8 +50,7 @@ Future testMain() async { offset = offset.translate(0, p.height + 10); // Only the first two lines are rendered. - p = paragraph(veryLong, - paragraphStyle: ParagraphStyle(maxLines: 2), maxWidth: 200); + p = paragraph(veryLong, paragraphStyle: ParagraphStyle(maxLines: 2), maxWidth: 200); canvas.drawParagraph(p, offset); offset = offset.translate(0, p.height + 10); diff --git a/lib/web_ui/test/html/paragraph/text_placeholders_golden_test.dart b/lib/web_ui/test/html/paragraph/text_placeholders_golden_test.dart index ee96414f47086..769001920b821 100644 --- a/lib/web_ui/test/html/paragraph/text_placeholders_golden_test.dart +++ b/lib/web_ui/test/html/paragraph/text_placeholders_golden_test.dart @@ -30,8 +30,7 @@ Future testMain() async { final RecordingCanvas recordingCanvas = RecordingCanvas(screenRect); Offset offset = Offset.zero; - for (final PlaceholderAlignment placeholderAlignment - in PlaceholderAlignment.values) { + for (final PlaceholderAlignment placeholderAlignment in PlaceholderAlignment.values) { _paintTextWithPlaceholder( recordingCanvas, offset, @@ -101,10 +100,7 @@ void _paintTextWithPlaceholder( // Then fill the placeholders. final TextBox placeholderBox = paragraph.getBoxesForPlaceholders().single; - canvas.drawRect( - placeholderBox.toRect().shift(offset), - SurfacePaint()..color = red, - ); + canvas.drawRect(placeholderBox.toRect().shift(offset), SurfacePaint()..color = red); } Paragraph _createParagraphWithPlaceholder( @@ -113,10 +109,8 @@ Paragraph _createParagraphWithPlaceholder( PlaceholderAlignment placeholderAlignment, TextAlign textAlignment, ) { - final ParagraphBuilder builder = - ParagraphBuilder(ParagraphStyle(textAlign: textAlignment)); - builder - .pushStyle(TextStyle(color: black, fontFamily: 'Roboto', fontSize: 14)); + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(textAlign: textAlignment)); + builder.pushStyle(TextStyle(color: black, fontFamily: 'Roboto', fontSize: 14)); builder.addText(before); builder.addPlaceholder( placeholderSize.width, diff --git a/lib/web_ui/test/html/path_metrics_golden_test.dart b/lib/web_ui/test/html/path_metrics_golden_test.dart index 6ce6bd660da06..a536e7788a79b 100644 --- a/lib/web_ui/test/html/path_metrics_golden_test.dart +++ b/lib/web_ui/test/html/path_metrics_golden_test.dart @@ -23,9 +23,7 @@ Future testMain() async { const Color redAccentColor = Color(0xFFFF1744); const double kDashLength = 5.0; - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpUnitTests(setUpTestViewDimensions: false); test('Should calculate tangent on line', () async { final Path path = Path(); @@ -74,17 +72,18 @@ Future testMain() async { // 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 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); @@ -112,9 +111,7 @@ Future testMain() async { while (distance < measurePath.length * t1) { const double length = kDashLength; if (draw) { - dashedPath.addPath( - measurePath.extractPath(distance, distance + length), - Offset.zero); + dashedPath.addPath(measurePath.extractPath(distance, distance + length), Offset.zero); } distance += length; draw = !draw; @@ -126,17 +123,18 @@ Future testMain() async { // 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 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); @@ -166,9 +164,7 @@ Future testMain() async { while (distance < measurePath.length * t1) { const double length = kDashLength; if (draw) { - dashedPath.addPath( - measurePath.extractPath(distance, distance + length), - Offset.zero); + dashedPath.addPath(measurePath.extractPath(distance, distance + length), Offset.zero); } distance += length; draw = !draw; diff --git a/lib/web_ui/test/html/path_ref_test.dart b/lib/web_ui/test/html/path_ref_test.dart index 3fae267724f20..531ca97d45497 100644 --- a/lib/web_ui/test/html/path_ref_test.dart +++ b/lib/web_ui/test/html/path_ref_test.dart @@ -68,12 +68,12 @@ void testMain() { 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); + 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); + path.lineTo(10, 10); + path.lineTo(0, 10); expect(path.pathRef.getRect(), const Rect.fromLTWH(0, 0, 10, 10)); }); @@ -81,10 +81,10 @@ void testMain() { 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); + 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)); }); diff --git a/lib/web_ui/test/html/path_test.dart b/lib/web_ui/test/html/path_test.dart index 5663048c3fe7b..331bdfaa58728 100644 --- a/lib/web_ui/test/html/path_test.dart +++ b/lib/web_ui/test/html/path_test.dart @@ -2,7 +2,6 @@ // 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'; @@ -110,8 +109,7 @@ void testMain() { expect(path.toRect(), null); }); - test('Should detect non rectangular path if there are multiple subpaths', - () { + 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)); @@ -120,13 +118,19 @@ void testMain() { test('Should detect rounded rectangular path', () { final SurfacePath path = SurfacePath(); - path.addRRect(RRect.fromRectAndRadius( + path.addRRect( + RRect.fromRectAndRadius( const Rect.fromLTRB(1.0, 2.0, 30.0, 40.0), - const Radius.circular(2.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))); + 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', () { @@ -140,14 +144,21 @@ void testMain() { expect(path.toRoundedRect(), null); }); - test( - 'Should detect non rounded rectangular path if there are ' + 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))); + 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); }); @@ -173,52 +184,64 @@ void testMain() { 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)); + 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)); + 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)); + 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)); + 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)); + 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)); + 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); @@ -243,7 +266,7 @@ void testMain() { Offset(250, 100), Offset(152, 180), Offset(159, 200), - Offset(151, 190) + Offset(151, 190), ], true); expect(path.getBounds(), const Rect.fromLTRB(50, 100, 250, 200)); }); @@ -253,30 +276,27 @@ void testMain() { 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))); + 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))); + 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))); + path3.getBounds(), + within(distance: 0.1, from: const Rect.fromLTRB(468.3, 619.3, 605.9, 685.6)), + ); }); test('Should compute bounds for cubicTo', () { @@ -284,70 +304,63 @@ void testMain() { 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))); + 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))); + 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))); + 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))); + 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))); + 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))); + 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))); + 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. @@ -394,34 +407,39 @@ void testMain() { test('Computes contains for cubic curves', () { final Path path = Path(); path.moveTo(10, 25); - path.cubicTo(10, 20, 10, 20, 20, 15); + path.cubicTo(10, 20, 10, 20, 20, 15); path.lineTo(25, 20); - path.cubicTo(30, 20, 30, 20, 30, 25); + path.cubicTo(30, 20, 30, 20, 30, 25); path.lineTo(30, 35); - path.cubicTo(30, 40, 30, 40, 25, 40); + path.cubicTo(30, 40, 30, 40, 25, 40); path.lineTo(15, 40); - path.cubicTo(10, 40, 10, 40, 10, 35); + 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); + 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 @@ -431,8 +449,8 @@ void testMain() { // 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))); + 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); @@ -440,8 +458,8 @@ void testMain() { 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))); + 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); @@ -459,13 +477,19 @@ void testMain() { expect(path.pathRef.segmentMasks, 0); path.lineTo(200, 40); path.pathRef.computeSegmentMask(); - expect( - path.pathRef.segmentMasks, SPathSegmentMask.kLine_SkPathSegmentMask); + 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); + final Conic conic = Conic( + 120.0, + 20.0, + 160.99470420829266, + 20.0, + 190.19301120261332, + 34.38770865870253, + 0.9252691032413082, + ); expect(conic.toQuads().length, 3); }); @@ -555,15 +579,17 @@ void testMain() { /// 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 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()); + Matrix4.identity().scaled(0.5, 0.5).toFloat64(), + ); expect(transformedPath.pathRef.getBounds(), isNot(bounds)); }); }); diff --git a/lib/web_ui/test/html/path_to_svg_golden_test.dart b/lib/web_ui/test/html/path_to_svg_golden_test.dart index 2ed88dfd53d66..a2f8e7df5fef8 100644 --- a/lib/web_ui/test/html/path_to_svg_golden_test.dart +++ b/lib/web_ui/test/html/path_to_svg_golden_test.dart @@ -13,45 +13,43 @@ void main() { internalBootstrapBrowserTest(() => testMain); } -enum PaintMode { - kStrokeAndFill, - kStroke, - kFill, - kStrokeWidthOnly, -} +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 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 BitmapCanvas bitmapCanvas = BitmapCanvas(canvasBounds, RenderStrategy()); final RecordingCanvas canvas = RecordingCanvas(canvasBounds); - final bool enableFill = - mode == PaintMode.kStrokeAndFill || mode == PaintMode.kFill; + final bool enableFill = mode == PaintMode.kStrokeAndFill || mode == PaintMode.kFill; if (enableFill) { - paint ??= SurfacePaint() - ..color = const Color(0x807F7F7F) - ..style = PaintingStyle.fill; + 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; + 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; + paint = + SurfacePaint() + ..color = const Color(0xFF4060E0) + ..strokeWidth = 10; } canvas.drawPath(path, paint!); @@ -72,8 +70,7 @@ Future testMain() async { sceneElement.append(bitmapCanvas.rootElement); sceneElement.append(svgElement); - await matchGoldenFile('$goldenFileName.png', - region: region); + await matchGoldenFile('$goldenFileName.png', region: region); bitmapCanvas.rootElement.remove(); svgElement.remove(); @@ -87,11 +84,15 @@ Future testMain() 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); + await testPath( + path, + 'svg_stroke_line', + paint: + SurfacePaint() + ..color = const Color(0xFFFF0000) + ..strokeWidth = 2.0 + ..style = PaintingStyle.stroke, + ); }); test('render quad bezier curve', () async { @@ -111,17 +112,13 @@ Future testMain() async { test('render arcs', () async { final List arcs = [ ArcSample(Offset.zero, distance: 20), - ArcSample(const Offset(200, 0), - largeArc: true, 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(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(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(const Offset(200, 0), largeArc: true, clockwise: true, distance: -20), ]; int sampleIndex = 0; for (final ArcSample sample in arcs) { @@ -143,9 +140,7 @@ Future testMain() async { 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.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); @@ -192,8 +187,7 @@ DomElement pathToSvgElement(Path path, Paint paint, bool enableFill) { final SVGPathElement pathElement = createSVGPathElement(); root.append(pathElement); - if (paint.style == PaintingStyle.stroke || - paint.strokeWidth != 0.0) { + if (paint.style == PaintingStyle.stroke || paint.strokeWidth != 0.0) { pathElement.setAttribute('stroke', paint.color.toCssString()); pathElement.setAttribute('stroke-width', paint.strokeWidth); if (!enableFill) { @@ -203,13 +197,15 @@ DomElement pathToSvgElement(Path path, Paint paint, bool enableFill) { 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! + 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}); + ArcSample(this.offset, {this.largeArc = false, this.clockwise = false, this.distance = 0}); final Offset offset; final bool largeArc; @@ -217,17 +213,17 @@ class ArcSample { 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 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); + path.arcToPoint( + endP, + rotation: 60, + radius: const Radius.elliptical(40, 60), + largeArc: largeArc, + clockwise: clockwise, + ); return path; } } diff --git a/lib/web_ui/test/html/path_transform_golden_test.dart b/lib/web_ui/test/html/path_transform_golden_test.dart index 68a2b417c0a22..1e4c9d3075090 100644 --- a/lib/web_ui/test/html/path_transform_golden_test.dart +++ b/lib/web_ui/test/html/path_transform_golden_test.dart @@ -21,90 +21,87 @@ Future testMain() async { const double screenHeight = 800.0; const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpUnitTests(setUpTestViewDimensions: false); test('Should draw transformed line.', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + 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)); + 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()); + 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)); + 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 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)); + 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()); + 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)); + 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 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)); + 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()); + 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)); + 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)); + final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); const double yStart = 20; const Offset p0 = Offset(25, yStart + 25); @@ -120,83 +117,86 @@ Future testMain() async { path.close(); rc.drawPath( - path, - SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 2.0 - ..color = const Color(0xFF404000)); + 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()); + 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)); + 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 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.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)); + 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()); + 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)); + 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 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)); + 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()); + 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)); + 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/lib/web_ui/test/html/picture_golden_test.dart b/lib/web_ui/test/html/picture_golden_test.dart index 983b241b7678f..c99c9db3279b4 100644 --- a/lib/web_ui/test/html/picture_golden_test.dart +++ b/lib/web_ui/test/html/picture_golden_test.dart @@ -17,12 +17,13 @@ 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)); + 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), + 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; diff --git a/lib/web_ui/test/html/recording_canvas_golden_test.dart b/lib/web_ui/test/html/recording_canvas_golden_test.dart index 6e087a8196031..a2fc1b945fd31 100644 --- a/lib/web_ui/test/html/recording_canvas_golden_test.dart +++ b/lib/web_ui/test/html/recording_canvas_golden_test.dart @@ -20,10 +20,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - withImplicitView: true, - setUpTestViewDimensions: false, - ); + setUpUnitTests(withImplicitView: true, setUpTestViewDimensions: false); const double screenWidth = 600.0; const double screenHeight = 800.0; @@ -31,11 +28,12 @@ Future testMain() async { 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()); + 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 @@ -71,8 +69,7 @@ Future testMain() async { } test('Empty canvas reports correct paint bounds', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTWH(1, 2, 300, 400)); + final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTWH(1, 2, 300, 400)); rc.endRecording(); expect(rc.pictureBounds, Rect.zero); await checkScreenshot(rc, 'empty_canvas'); @@ -87,16 +84,13 @@ Future testMain() async { await checkScreenshot(rc, 'draw_line'); }); - test('Computes paint bounds for draw line when line exceeds limits', - () async { + 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.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)); + expect(rc.pictureBounds, const Rect.fromLTRB(49.0, 99.0, screenWidth, 141.0)); await checkScreenshot(rc, 'draw_line_exceeding_limits'); }); @@ -111,12 +105,9 @@ Future testMain() async { 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.drawRect(const Rect.fromLTRB(10, 20, 30 + screenWidth, 40 + screenHeight), testPaint); rc.endRecording(); - expect(rc.pictureBounds, - const Rect.fromLTRB(10, 20, screenWidth, screenHeight)); + expect(rc.pictureBounds, const Rect.fromLTRB(10, 20, screenWidth, screenHeight)); rc = RecordingCanvas(screenRect); rc.drawRect(const Rect.fromLTRB(-200, -100, 30, 40), testPaint); @@ -146,12 +137,10 @@ Future testMain() async { 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.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))); + expect(rc.pictureBounds, within(distance: 0.1, from: const Rect.fromLTRB(0, 0, 50.7, 50.7))); await checkScreenshot(rc, 'rotate'); }); @@ -161,9 +150,9 @@ Future testMain() async { 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))); + rc.pictureBounds, + within(distance: 0.1, from: const Rect.fromLTRB(40.0, 20.0, 80.0, 40.0)), + ); await checkScreenshot(rc, 'skew_horizontally'); }); @@ -173,9 +162,9 @@ Future testMain() async { 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))); + rc.pictureBounds, + within(distance: 0.1, from: const Rect.fromLTRB(20.0, 40.0, 40.0, 80.0)), + ); await checkScreenshot(rc, 'skew_vertically'); }); @@ -203,10 +192,9 @@ Future testMain() async { 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))); + rc.pictureBounds, + within(distance: 0.001, from: const Rect.fromLTRB(168.0, 283.6, 224.0, 368.4)), + ); await checkScreenshot(rc, 'complex_transform'); }); @@ -241,20 +229,22 @@ Future testMain() async { 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); + 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 ' + 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.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'); @@ -263,9 +253,10 @@ Future testMain() async { 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); + 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'); @@ -296,68 +287,82 @@ Future testMain() async { 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.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 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( + '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. @@ -417,8 +422,7 @@ Future testMain() async { expect( rc.pictureBounds, - within( - distance: 0.05, from: const Rect.fromLTRB(0.0, 8.5, 123.5, 134.1)), + within(distance: 0.05, from: const Rect.fromLTRB(0.0, 8.5, 123.5, 134.1)), ); await checkScreenshot(rc, 'path_with_shadow'); }); @@ -430,8 +434,7 @@ Future testMain() async { // 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)); + final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 100, 100)); rc ..translate(0, 100) ..scale(1, -1) @@ -444,8 +447,7 @@ Future testMain() async { }); test('Clip with a rotation reports correct paint bounds', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 100, 100)); + final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 100, 100)); rc ..translate(50, 50) ..rotate(math.pi / 4.0) @@ -456,16 +458,15 @@ Future testMain() async { expect( rc.pictureBounds, within( - distance: 0.001, - from: Rect.fromCircle( - center: const Offset(50, 50), radius: 20 * math.sqrt(2))), + 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)); + final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 100, 100)); rc ..translate(50, 50) ..rotate(math.pi / 4.0) @@ -484,10 +485,11 @@ Future testMain() async { final RecordingCanvas rc = RecordingCanvas(screenRect); final Path path = Path(); - final SurfacePaint paint = SurfacePaint() - ..style = PaintingStyle.stroke - ..strokeWidth = 0.0 - ..color = const Color(0xFFFF0000); + 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); @@ -497,43 +499,39 @@ Future testMain() async { 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)), - ); + 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)); + 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, + 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)); + 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)); + final RecordingCanvas rc = RecordingCanvas(const Rect.fromLTRB(0, 0, 200, 400)); rc.save(); rc.translate(50.0, 100.0); final Path path = Path(); @@ -541,14 +539,14 @@ Future testMain() async { 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))); + 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)); + path, + SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 2.0 + ..color = const Color(0xFF404000), + ); rc.restore(); rc.endRecording(); await checkScreenshot(rc, 'path_with_line_and_roundrect'); @@ -556,23 +554,22 @@ Future testMain() async { // 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)); + 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.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)); + path, + SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 2.0 + ..color = const Color(0xFF404000), + ); rc.restore(); rc.endRecording(); await checkScreenshot(rc, 'path_with_addpolygon'); @@ -583,23 +580,13 @@ Future testMain() async { final List painters = [ (RecordingCanvas canvas, SurfacePaint paint) { - canvas.drawLine( - Offset.zero, - const Offset(20.0, 20.0), - 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, - ); + 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, - ); + canvas.drawRRect(RRect.fromLTRBR(0.0, 0.0, 20.0, 20.0, const Radius.circular(7.0)), paint); }, (RecordingCanvas canvas, SurfacePaint paint) { canvas.drawDRRect( @@ -609,25 +596,19 @@ Future testMain() async { ); }, (RecordingCanvas canvas, SurfacePaint paint) { - canvas.drawOval( - const Rect.fromLTRB(0.0, 5.0, 20.0, 15.0), - 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, - ); + 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(); + final SurfacePath path = + SurfacePath() + ..moveTo(10, 0) + ..lineTo(20, 10) + ..lineTo(10, 20) + ..lineTo(0, 10) + ..close(); canvas.drawPath(path, paint); }, @@ -680,9 +661,10 @@ Future testMain() async { 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; + 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!)); @@ -694,8 +676,8 @@ Future testMain() async { 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); + 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!)); @@ -707,10 +689,11 @@ Future testMain() async { 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); + 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!)); @@ -723,28 +706,24 @@ Future testMain() async { 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), - ); + 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); +typedef PaintSpreadPainter = void Function(RecordingCanvas canvas, SurfacePaint paint); -const String _base64Encoded20x20TestImage = 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUC' +const String _base64Encoded20x20TestImage = + 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUC' 'AIAAAAC64paAAAACXBIWXMAAC4jAAAuIwF4pT92AAAA' 'B3RJTUUH5AMFFBksg4i3gQAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAAj' 'SURBVDjLY2TAC/7jlWVioACMah4ZmhnxpyHG0QAb1UyZZgBjWAIm/clP0AAAAABJRU5ErkJggg=='; HtmlImage _createRealTestImage() { return HtmlImage( - createDomHTMLImageElement() - ..src = 'data:text/plain;base64,$_base64Encoded20x20TestImage', + createDomHTMLImageElement()..src = 'data:text/plain;base64,$_base64Encoded20x20TestImage', 20, 20, ); @@ -758,8 +737,7 @@ class TestImage implements Image { int get height => 10; @override - Future toByteData( - {ImageByteFormat format = ImageByteFormat.rawRgba}) async { + Future toByteData({ImageByteFormat format = ImageByteFormat.rawRgba}) async { throw UnsupportedError('Cannot encode test image'); } @@ -779,19 +757,21 @@ class TestImage implements Image { bool isCloneOf(Image other) => other == this; @override - List/*?*/ debugGetOpenHandleStackTraces() => []; + 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, - )); + 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/lib/web_ui/test/html/recording_canvas_test.dart b/lib/web_ui/test/html/recording_canvas_test.dart index db122e5d8e682..190da34418b56 100644 --- a/lib/web_ui/test/html/recording_canvas_test.dart +++ b/lib/web_ui/test/html/recording_canvas_test.dart @@ -15,10 +15,7 @@ void main() { } void testMain() { - setUpUnitTests( - withImplicitView: true, - setUpTestViewDimensions: false, - ); + setUpUnitTests(withImplicitView: true, setUpTestViewDimensions: false); late RecordingCanvas underTest; late MockEngineCanvas mockCanvas; @@ -31,11 +28,9 @@ void testMain() { group('paragraph bounds', () { Paragraph paragraphForBoundsTest(TextAlign alignment) { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontSize: 20, - textAlign: alignment, - )); + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle(fontFamily: 'Ahem', fontSize: 20, textAlign: alignment), + ); builder.addText('A AAAAA AAA'); return builder.build(); } @@ -80,8 +75,7 @@ void testMain() { group('drawDRRect', () { final RRect rrect = RRect.fromLTRBR(10, 10, 50, 50, const Radius.circular(3)); - final SurfacePaint somePaint = SurfacePaint() - ..color = const Color(0xFFFF0000); + final SurfacePaint somePaint = SurfacePaint()..color = const Color(0xFFFF0000); test('Happy case', () { underTest.drawDRRect(rrect, rrect.deflate(1), somePaint); @@ -90,7 +84,7 @@ void testMain() { _expectDrawDRRectCall(mockCanvas, { 'path': - 'Path(' + 'Path(' 'MoveTo(${10.0}, ${47.0}) ' 'LineTo(${10.0}, ${13.0}) ' 'Conic(${10.0}, ${10.0}, ${10.0}, ${13.0}, w = ${0.7071067690849304}) ' @@ -111,7 +105,7 @@ void testMain() { 'LineTo(${13.0}, ${49.0}) ' 'Conic(${11.0}, ${49.0}, ${49.0}, ${11.0}, w = ${0.7071067690849304}) ' 'Close()' - ')', + ')', 'paint': somePaint.paintData, }); }); @@ -126,8 +120,7 @@ void testMain() { }); test('Inner RRect not completely inside Outer RRect', () { - underTest.drawDRRect( - rrect, rrect.deflate(1).shift(const Offset(0.0, 10)), somePaint); + underTest.drawDRRect(rrect, rrect.deflate(1).shift(const Offset(0.0, 10)), somePaint); underTest.endRecording(); underTest.apply(mockCanvas, screenRect); // Expect nothing to be called @@ -147,9 +140,10 @@ void testMain() { 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)); + 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)); @@ -162,7 +156,7 @@ void testMain() { // Expect to draw, even when inner has negative radii (which get ignored by canvas) _expectDrawDRRectCall(mockCanvas, { 'path': - 'Path(' + 'Path(' 'MoveTo(${0.0}, ${42.0}) ' 'LineTo(${0.0}, ${6.0}) ' 'Conic(${0.0}, ${0.0}, ${0.0}, ${6.0}, w = ${0.7071067690849304}) ' @@ -183,16 +177,14 @@ void testMain() { '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)); + 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(); @@ -200,7 +192,7 @@ void testMain() { _expectDrawDRRectCall(mockCanvas, { 'path': - 'Path(' + 'Path(' 'MoveTo(${10.0}, ${20.0}) ' 'LineTo(${30.0}, ${20.0}) ' 'LineTo(${30.0}, ${40.0}) ' @@ -211,7 +203,7 @@ void testMain() { 'LineTo(${28.0}, ${38.0}) ' 'LineTo(${12.0}, ${38.0}) ' 'Close()' - ')', + ')', 'paint': somePaint.paintData, }); }); @@ -319,8 +311,7 @@ void testMain() { } // Expect a drawDRRect call to be registered in the mock call log, with the expectedArguments -void _expectDrawDRRectCall( - MockEngineCanvas mock, Map expectedArguments) { +void _expectDrawDRRectCall(MockEngineCanvas mock, Map expectedArguments) { expect(mock.methodCallLog.length, equals(2)); final MockCanvasCall mockCall = mock.methodCallLog[0]; expect(mockCall.methodName, equals('drawPath')); diff --git a/lib/web_ui/test/html/resource_manager_test.dart b/lib/web_ui/test/html/resource_manager_test.dart index 7af930e91b759..4a864975dfa0c 100644 --- a/lib/web_ui/test/html/resource_manager_test.dart +++ b/lib/web_ui/test/html/resource_manager_test.dart @@ -43,10 +43,7 @@ void testMain() { resources.forEach(resourceManager.addResource); final DomElement resourcesHost = hostElement.firstElementChild!; - expect( - resourcesHost.tagName.toLowerCase(), - ResourceManager.resourcesHostTagName.toLowerCase(), - ); + expect(resourcesHost.tagName.toLowerCase(), ResourceManager.resourcesHostTagName.toLowerCase()); // Make sure the resources were correctly inserted into the host. expect(resourcesHost.children, resources); @@ -70,10 +67,7 @@ void testMain() { resources.forEach(resourceManager.addResource); final DomElement resourcesHost = domManager.renderingHost.firstElementChild!; - expect( - resourcesHost.tagName.toLowerCase(), - ResourceManager.resourcesHostTagName.toLowerCase(), - ); + expect(resourcesHost.tagName.toLowerCase(), ResourceManager.resourcesHostTagName.toLowerCase()); // Make sure the resources were correctly inserted into the host. expect(resourcesHost.children, resources); diff --git a/lib/web_ui/test/html/screenshot.dart b/lib/web_ui/test/html/screenshot.dart index f27177caab9b5..f56d594b3ab3d 100644 --- a/lib/web_ui/test/html/screenshot.dart +++ b/lib/web_ui/test/html/screenshot.dart @@ -44,8 +44,7 @@ Future canvasScreenshot( } sceneElement.append(engineCanvas.rootElement); domDocument.body!.append(sceneElement); - await matchGoldenFile('$fileName.png', - region: region); + await matchGoldenFile('$fileName.png', region: region); } finally { // The page is reused across tests, so remove the element after taking the // screenshot. @@ -53,16 +52,16 @@ Future canvasScreenshot( } } -Future sceneScreenshot(SurfaceSceneBuilder sceneBuilder, String fileName, - {ui.Rect region = const ui.Rect.fromLTWH(0, 0, 600, 800)}) async { +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; + sceneElement = sceneBuilder.build().webOnlyRootElement; domDocument.body!.append(sceneElement!); - await matchGoldenFile('$fileName.png', - region: region); + await matchGoldenFile('$fileName.png', region: region); } finally { // The page is reused across tests, so remove the element after taking the // screenshot. diff --git a/lib/web_ui/test/html/shaders/gradient_golden_test.dart b/lib/web_ui/test/html/shaders/gradient_golden_test.dart index 7196b6ebcf8c6..c75b513542ae4 100644 --- a/lib/web_ui/test/html/shaders/gradient_golden_test.dart +++ b/lib/web_ui/test/html/shaders/gradient_golden_test.dart @@ -29,14 +29,14 @@ Future testMain() async { setUpUnitTests(withImplicitView: true); test('Paints sweep gradient rectangles', () async { - final RecordingCanvas canvas = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + 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); + final SurfacePaint borderPaint = + SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = const Color(0xFF000000); const List colors = [ Color(0xFF000000), @@ -44,63 +44,108 @@ Future testMain() async { Color(0xFFFF8C42), Color(0xFFFFF275), Color(0xFF6699CC), - Color(0xFF656D78),]; + 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); + 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); + 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, + 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, + 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); + 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, + 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); + 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, + 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)); + 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(); @@ -108,14 +153,14 @@ Future testMain() async { }, skip: isFirefox); test('Paints sweep gradient ovals', () async { - final RecordingCanvas canvas = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + 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); + final SurfacePaint borderPaint = + SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = const Color(0xFF000000); const List colors = [ Color(0xFF000000), @@ -123,63 +168,108 @@ Future testMain() async { Color(0xFFFF8C42), Color(0xFFFFF275), Color(0xFF6699CC), - Color(0xFF656D78),]; + 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); + 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); + 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.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.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); + 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.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); + 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.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)); + 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(); @@ -187,14 +277,14 @@ Future testMain() async { }, skip: isFirefox); test('Paints sweep gradient paths', () async { - final RecordingCanvas canvas = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + 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); + final SurfacePaint borderPaint = + SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = const Color(0xFF000000); const List colors = [ Color(0xFF000000), @@ -202,68 +292,113 @@ Future testMain() async { Color(0xFFFF8C42), Color(0xFFFFF275), Color(0xFF6699CC), - Color(0xFF656D78),]; + 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); + 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); + 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.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.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); + 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.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); + 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.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); + 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.drawPath( + path, + SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds), + ); canvas.drawRect(rectBounds, borderPaint); canvas.restore(); @@ -272,14 +407,14 @@ Future testMain() async { /// 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)); + 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); + final SurfacePaint borderPaint = + SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = const Color(0xFF000000); const List colors = [ Color(0xFF000000), @@ -287,99 +422,120 @@ Future testMain() async { Color(0xFFFF8C42), Color(0xFFFFF275), Color(0xFF6699CC), - Color(0xFF656D78),]; + 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); + 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, + 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); + 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, + SurfacePaint()..shader = engineLinearGradientToShader(linearGradient, rectBounds), + ); canvas.drawRect(rectBounds, borderPaint); canvas.restore(); - await canvasScreenshot(canvas, 'linear_gradient_rect_shifted', canvasRect: screenRect, region: region); + 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)); + 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); + final SurfacePaint borderPaint = + SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = const Color(0xFF000000); - const List colors = [ - Color(0xFF000000), - Color(0xFFFF3C38)]; + 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); + 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, + SurfacePaint()..shader = engineLinearGradientToShader(linearGradient, rectBounds), + ); canvas.drawRect(rectBounds, borderPaint); canvas.restore(); - final EngineCanvas engineCanvas = BitmapCanvas(screenRect, - RenderStrategy()); + 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); + 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()); + 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), - ]; + 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); + Offset.zero, + const Offset(10, 10), + colors, + null, + TileMode.clamp, + Matrix4.identity().storage, + ); + engineCanvas.drawRect( + Rect.fromLTWH(x, y, 10, 10), + SurfacePaintData()..shader = linearGradient, + ); } } @@ -387,14 +543,14 @@ Future testMain() async { }, skip: isFirefox); test('Paints clamped, rotated and shifted linear gradient', () async { - final RecordingCanvas canvas = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + 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); + final SurfacePaint borderPaint = + SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = const Color(0xFF000000); const List colors = [ Color(0xFF000000), @@ -402,74 +558,99 @@ Future testMain() async { Color(0xFFFF8C42), Color(0xFFFFF275), Color(0xFF6699CC), - Color(0xFF656D78),]; + 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); + 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, + 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); + 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, + 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); + 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)); + 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); + final SurfacePaint borderPaint = + SurfacePaint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = const Color(0xFF000000); - const List colors = [ - Color(0xFFFF0000), - Color(0xFF0000FF), - ]; + 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); + 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, + SurfacePaint()..shader = engineLinearGradientToShader(linearGradient, rectBounds), + ); canvas.drawRect(rectBounds, borderPaint); canvas.restore(); - await canvasScreenshot(canvas, 'linear_gradient_in_svg_context', canvasRect: screenRect, region: region); + 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)); + final RecordingCanvas canvas = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); canvas.save(); const List colors = [ @@ -483,10 +664,11 @@ Future testMain() async { 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 Matrix4 transform = + Matrix4.identity() + ..translate(50, 50) + ..scale(0.3, 0.7) + ..rotateZ(0.5); final GradientLinear linearGradient = GradientLinear( const Offset(5, 5), @@ -503,15 +685,13 @@ Future testMain() async { Rect rectBounds = const Rect.fromLTWH(10, 20, kBoxWidth, kBoxHeight); canvas.drawRect( rectBounds, - SurfacePaint() - ..shader = engineLinearGradientToShader(linearGradient, rectBounds), + SurfacePaint()..shader = engineLinearGradientToShader(linearGradient, rectBounds), ); rectBounds = const Rect.fromLTWH(10, 110, kBoxWidth, kBoxHeight); canvas.drawOval( rectBounds, - SurfacePaint() - ..shader = engineLinearGradientToShader(linearGradient, rectBounds), + SurfacePaint()..shader = engineLinearGradientToShader(linearGradient, rectBounds), ); canvas.restore(); @@ -524,8 +704,7 @@ Future testMain() async { }, skip: isFirefox); test('Paints transformed sweep gradient', () async { - final RecordingCanvas canvas = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + final RecordingCanvas canvas = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); canvas.save(); const List colors = [ @@ -539,10 +718,11 @@ Future testMain() async { 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 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), @@ -560,15 +740,13 @@ Future testMain() async { Rect rectBounds = const Rect.fromLTWH(10, 20, kBoxWidth, kBoxHeight); canvas.drawRect( rectBounds, - SurfacePaint() - ..shader = engineGradientToShader(sweepGradient, rectBounds), + SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds), ); rectBounds = const Rect.fromLTWH(10, 110, kBoxWidth, kBoxHeight); canvas.drawOval( rectBounds, - SurfacePaint() - ..shader = engineGradientToShader(sweepGradient, rectBounds), + SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds), ); canvas.restore(); @@ -581,8 +759,7 @@ Future testMain() async { }, skip: isFirefox); test('Paints transformed radial gradient', () async { - final RecordingCanvas canvas = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + final RecordingCanvas canvas = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); canvas.save(); const List colors = [ @@ -596,10 +773,11 @@ Future testMain() async { 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 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), @@ -616,15 +794,13 @@ Future testMain() async { Rect rectBounds = const Rect.fromLTWH(10, 20, kBoxWidth, kBoxHeight); canvas.drawRect( rectBounds, - SurfacePaint() - ..shader = engineRadialGradientToShader(radialGradient, rectBounds), + SurfacePaint()..shader = engineRadialGradientToShader(radialGradient, rectBounds), ); rectBounds = const Rect.fromLTWH(10, 110, kBoxWidth, kBoxHeight); canvas.drawOval( rectBounds, - SurfacePaint() - ..shader = engineRadialGradientToShader(radialGradient, rectBounds), + SurfacePaint()..shader = engineRadialGradientToShader(radialGradient, rectBounds), ); canvas.restore(); @@ -637,8 +813,7 @@ Future testMain() async { }, skip: isFirefox); test('Paints two gradient with same width and different height', () async { - final RecordingCanvas canvas = - RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); + final RecordingCanvas canvas = RecordingCanvas(const Rect.fromLTRB(0, 0, 400, 300)); canvas.save(); const List colors = [ @@ -652,10 +827,11 @@ Future testMain() async { 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 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), @@ -675,13 +851,11 @@ Future testMain() async { const Rect rectBounds2 = Rect.fromLTWH(10, 80, kBoxWidth, kBoxHeight2); canvas.drawRect( rectBounds1, - SurfacePaint() - ..shader = engineGradientToShader(sweepGradient, rectBounds1), + SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds1), ); canvas.drawRect( rectBounds2, - SurfacePaint() - ..shader = engineGradientToShader(sweepGradient, rectBounds2), + SurfacePaint()..shader = engineGradientToShader(sweepGradient, rectBounds2), ); canvas.restore(); @@ -696,28 +870,36 @@ Future testMain() async { 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!), + 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), + 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), + Offset( + rect.left + gradient.center.dx * rect.width, + rect.top + gradient.center.dy * rect.height, + ), gradient.radius, gradient.colors, gradient.colorStops, @@ -727,9 +909,13 @@ Shader engineRadialGradientToShader(GradientRadial gradient, Rect rect) { } 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(); + 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/lib/web_ui/test/html/shaders/image_shader_golden_test.dart b/lib/web_ui/test/html/shaders/image_shader_golden_test.dart index ba7d7fc52ab23..c7a6d037c60af 100644 --- a/lib/web_ui/test/html/shaders/image_shader_golden_test.dart +++ b/lib/web_ui/test/html/shaders/image_shader_golden_test.dart @@ -25,9 +25,7 @@ Future testMain() async { const Rect screenRect = Rect.fromLTWH(0, 0, screenWidth, screenHeight); final HtmlImage testImage = createTestImage(); - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpUnitTests(setUpTestViewDimensions: false); void drawShapes(RecordingCanvas rc, SurfacePaint paint, Rect shaderRect) { /// Rect. @@ -39,15 +37,19 @@ Future testMain() async { shaderRect = shaderRect.translate(110, 0); /// Oval. - rc.drawOval(Rect.fromLTWH(shaderRect.left, shaderRect.top, shaderRect.width, shaderRect.height / 2), paint); + 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(); + 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); @@ -56,63 +58,61 @@ Future testMain() async { shaderRect = shaderRect.translate(110, 0); /// DRRect. - rc.drawDRRect(RRect.fromRectXY(shaderRect, 20, 30), - RRect.fromRectXY(shaderRect.deflate(24), 16, 24), - paint); + 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)); + 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); + 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); + await canvasScreenshot(rc, fileName, region: screenRect); } test('Should draw with tiled imageshader.', () async { - await testImageShader( - TileMode.repeated, TileMode.repeated, 'image_shader_tiled'); + 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'); + 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'); + 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'); + 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'); + 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'); + 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'); + await testImageShader(TileMode.clamp, TileMode.clamp, 'image_shader_clamp'); }, skip: isFirefox); } @@ -120,8 +120,7 @@ HtmlImage createTestImage() { const int width = 16; const int width2 = width ~/ 2; const int height = 16; - final DomCanvasElement canvas = - createDomCanvasElement(width: width, height: height); + final DomCanvasElement canvas = createDomCanvasElement(width: width, height: height); final DomCanvasRenderingContext2D ctx = canvas.context2D; ctx.fillStyle = '#E04040'; ctx.fillRect(0, 0, width2, width2); diff --git a/lib/web_ui/test/html/shaders/linear_gradient_golden_test.dart b/lib/web_ui/test/html/shaders/linear_gradient_golden_test.dart index 433d697348535..fea11c8216c24 100644 --- a/lib/web_ui/test/html/shaders/linear_gradient_golden_test.dart +++ b/lib/web_ui/test/html/shaders/linear_gradient_golden_test.dart @@ -19,18 +19,18 @@ void main() { } Future testMain() async { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpUnitTests(setUpTestViewDimensions: false); test('Should draw linear gradient using rectangle.', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + 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)]); + 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'); @@ -38,47 +38,49 @@ Future testMain() async { 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); + 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)]); + 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 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)); + 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)); + 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; } @@ -88,37 +90,37 @@ Future testMain() async { // 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)); + 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)]); + 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 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)); + 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; } @@ -127,24 +129,22 @@ Future testMain() async { }, skip: isFirefox); test('Should draw tiled mirrored linear gradient with transform.', () async { - final RecordingCanvas rc = - RecordingCanvas(const Rect.fromLTRB(0, 0, 500, 500)); + 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)); + 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; } diff --git a/lib/web_ui/test/html/shaders/radial_gradient_golden_test.dart b/lib/web_ui/test/html/shaders/radial_gradient_golden_test.dart index 27f64b3022a7a..b81299a04981e 100644 --- a/lib/web_ui/test/html/shaders/radial_gradient_golden_test.dart +++ b/lib/web_ui/test/html/shaders/radial_gradient_golden_test.dart @@ -14,14 +14,15 @@ void main() { } Future testMain() async { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + 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 { + 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(); @@ -33,29 +34,23 @@ Future testMain() async { 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) - ])); + '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) - ], - ), + Gradient.radial(Offset(shaderRect.right, shaderRect.bottom), shaderRect.width / 2, [ + const Color.fromARGB(255, 0, 0, 0), + const Color.fromARGB(255, 0, 0, 255), + ]), ); }); @@ -64,13 +59,9 @@ Future testMain() async { await testGradient( 'radial_gradient_tilemode_clamp', Gradient.radial( - Offset((shaderRect.left + shaderRect.right) / 2, - (shaderRect.top + shaderRect.bottom) / 2), + 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) - ], + [const Color.fromARGB(255, 0, 0, 0), const Color.fromARGB(255, 0, 0, 255)], [0.0, 1.0], ), shaderRect: shaderRect, @@ -83,40 +74,55 @@ Future testMain() async { Color(0xFFFF8C42), Color(0xFFFFF275), Color(0xFF6699CC), - Color(0xFF656D78),]; + 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( + 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), + 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); + 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( + 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), + 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); + region: const Rect.fromLTWH(0, 0, 600, 800), + ); + }, + // TODO(yjbanov): https://github.com/flutter/flutter/issues/86623 + skip: isFirefox, + ); } diff --git a/lib/web_ui/test/html/shaders/shader_mask_golden_test.dart b/lib/web_ui/test/html/shaders/shader_mask_golden_test.dart index 06ea0f32d8fa6..3c7e304b1f17e 100644 --- a/lib/web_ui/test/html/shaders/shader_mask_golden_test.dart +++ b/lib/web_ui/test/html/shaders/shader_mask_golden_test.dart @@ -4,8 +4,7 @@ import 'package:test/bootstrap/browser.dart'; import 'package:test/test.dart'; -import 'package:ui/src/engine.dart' - hide BackdropFilterEngineLayer, ClipRectEngineLayer; +import 'package:ui/src/engine.dart' hide BackdropFilterEngineLayer, ClipRectEngineLayer; import 'package:ui/ui.dart'; import 'package:web_engine_tester/golden_tester.dart'; @@ -18,8 +17,9 @@ import '../../common/test_initialization.dart'; const bool debugTest = false; DomElement get sceneHost => - EnginePlatformDispatcher.instance.implicitView!.dom.renderingHost - .querySelector(DomManager.sceneHostTagName)!; + EnginePlatformDispatcher.instance.implicitView!.dom.renderingHost.querySelector( + DomManager.sceneHostTagName, + )!; Future main() async { if (!debugTest) { @@ -56,86 +56,100 @@ Future testMain() async { /// 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)); + 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)); + 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)); + 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)); + 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)); + 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)); + 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)); + 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)); + 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)); + 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) { +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)); + 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)); + 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)); + Offset(offsetX + 110, offsetY + 110), + 30, + SurfacePaint() + ..style = PaintingStyle.fill + ..color = const Color(0xFF0000FF), + ); return recorder.endRecording(); } @@ -159,9 +173,13 @@ void _renderCirclesScene(BlendMode blendMode) { 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); + 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); @@ -171,8 +189,7 @@ void _renderCirclesScene(BlendMode blendMode) { sceneHost.append(builder.build().webOnlyRootElement!); } -Picture _drawTestPictureWithText( - Rect region, double offsetX, double offsetY) { +Picture _drawTestPictureWithText(Rect region, double offsetX, double offsetY) { final EnginePictureRecorder recorder = EnginePictureRecorder(); final RecordingCanvas canvas = recorder.beginRecording(region); const String text = 'Shader test'; @@ -213,9 +230,13 @@ void _renderTextScene(BlendMode blendMode) { 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); + 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); diff --git a/lib/web_ui/test/html/shadow_golden_test.dart b/lib/web_ui/test/html/shadow_golden_test.dart index ca0b7ccee366b..e527a183969cb 100644 --- a/lib/web_ui/test/html/shadow_golden_test.dart +++ b/lib/web_ui/test/html/shadow_golden_test.dart @@ -22,10 +22,7 @@ Future testMain() async { late SurfaceSceneBuilder builder; - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpUnitTests(emulateTesterEnvironment: false, setUpTestViewDimensions: false); setUp(() { builder = SurfaceSceneBuilder(); @@ -45,8 +42,7 @@ Future testMain() async { } void paintShadowBounds(SurfacePath path, double elevation) { - final Rect shadowBounds = - computePenumbraBounds(path.getBounds(), elevation); + final Rect shadowBounds = computePenumbraBounds(path.getBounds(), elevation); final EnginePictureRecorder recorder = EnginePictureRecorder(); final RecordingCanvas canvas = recorder.beginRecording(Rect.largest); canvas.drawRect( @@ -59,22 +55,14 @@ Future testMain() async { 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)); + 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, - ); + 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); @@ -83,24 +71,19 @@ Future testMain() async { } void paintBitmapCanvasComplexPathShadow(double elevation, Offset offset) { - final SurfacePath path = SurfacePath() - ..moveTo(10, 0) - ..lineTo(20, 10) - ..lineTo(10, 20) - ..lineTo(0, 10) - ..close(); + 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.debugEnforceArbitraryPaint(); // make sure DOM canvas doesn't take over + canvas.drawShadow(path, _kShadowColor, elevation, false); canvas.drawPath( path, SurfacePaint() @@ -114,39 +97,31 @@ Future testMain() async { builder.pop(); // offset } - test( - 'renders shadows correctly', - () async { - // Physical shape clips. We want to see that clipping in the screenshot. - debugShowClipLayers = false; + test('renders shadows correctly', () async { + // Physical shape clips. We want to see that clipping in the screenshot. + debugShowClipLayers = false; - builder.pushOffset(10, 20); + 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, 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++) { + paintBitmapCanvasShadow(i.toDouble(), Offset(50.0 * i, 120), true); + } - for (int i = 0; i < 10; i++) { - paintBitmapCanvasComplexPathShadow( - i.toDouble(), Offset(50.0 * i, 180)); - } + for (int i = 0; i < 10; i++) { + paintBitmapCanvasComplexPathShadow(i.toDouble(), Offset(50.0 * i, 180)); + } - builder.pop(); + builder.pop(); - final DomElement sceneElement = builder.build().webOnlyRootElement!; - domDocument.body!.append(sceneElement); + final DomElement sceneElement = builder.build().webOnlyRootElement!; + domDocument.body!.append(sceneElement); - await matchGoldenFile( - 'shadows.png', - region: region, - ); - }, - testOn: 'chrome', - ); + 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. diff --git a/lib/web_ui/test/html/surface/path/path_winding_test.dart b/lib/web_ui/test/html/surface/path/path_winding_test.dart index 9f0953004ba5b..8e3b059082b7a 100644 --- a/lib/web_ui/test/html/surface/path/path_winding_test.dart +++ b/lib/web_ui/test/html/surface/path/path_winding_test.dart @@ -2,7 +2,6 @@ // 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'; @@ -36,13 +35,11 @@ void testMain() { expect(path.isConvex, isTrue); path = SurfacePath(); - path.addRectWithDirection( - const Rect.fromLTRB(0, 0, 20, 20), SPathDirection.kCW, 0); + 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); + path.addRectWithDirection(const Rect.fromLTRB(0, 0, 20, 20), SPathDirection.kCCW, 0); expect(path.isConvex, isTrue); }); @@ -55,15 +52,11 @@ void testMain() { 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', 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 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), ]; @@ -87,7 +80,7 @@ void testMain() { Offset(double.infinity, double.negativeInfinity), Offset(double.nan, 0), Offset(0, double.nan), - Offset(double.nan, double.nan) + Offset(double.nan, double.nan), ]; final int nonFinitePointsCount = nonFinitePts.length; @@ -95,15 +88,13 @@ void testMain() { Offset(kScalarMax, 0), Offset(0, kScalarMax), Offset(kScalarMin, 0), - Offset(0, kScalarMin) + Offset(0, kScalarMin), ]; final int axisAlignedPointsCount = axisAlignedPts.length; final SurfacePath path = SurfacePath(); - for (int index = 0; - index < (13 * nonFinitePointsCount * axisAlignedPointsCount); - index++) { + 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; @@ -112,78 +103,98 @@ void testMain() { 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); } @@ -201,62 +212,80 @@ void testMain() { 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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; @@ -266,8 +295,7 @@ void testMain() { // in path. final SurfacePath path2 = SurfacePath.from(path); final int c = path2.convexityType; - assert(SPathConvexityType.kUnknown == c || - SPathConvexityType.kConcave == c); + assert(SPathConvexityType.kUnknown == c || SPathConvexityType.kConcave == c); } } }); diff --git a/lib/web_ui/test/html/surface/platform_view_test.dart b/lib/web_ui/test/html/surface/platform_view_test.dart index fe2a04508c264..4e66d6fddf728 100644 --- a/lib/web_ui/test/html/surface/platform_view_test.dart +++ b/lib/web_ui/test/html/surface/platform_view_test.dart @@ -44,7 +44,8 @@ Future testMain() async { group('update', () { test('throws assertion error if called with different viewIds', () { - final PersistedPlatformView differentView = PersistedPlatformView(1, 1, 1, 100, 100)..build(); + final PersistedPlatformView differentView = PersistedPlatformView(1, 1, 1, 100, 100) + ..build(); expect(() { view.update(differentView); }, throwsAssertionError); @@ -58,7 +59,8 @@ Future testMain() async { }); test('returns false when viewId is different', () { - final PersistedPlatformView differentView = PersistedPlatformView(1, 1, 1, 100, 100)..build(); + final PersistedPlatformView differentView = PersistedPlatformView(1, 1, 1, 100, 100) + ..build(); expect(view.canUpdateAsMatch(differentView), isFalse); }); @@ -84,13 +86,7 @@ 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, - }, - )), + codec.encodeMethodCall(MethodCall('create', {'id': id, 'viewType': viewType})), (dynamic _) => completer.complete(), ); return completer.future; diff --git a/lib/web_ui/test/html/surface/scene_builder_test.dart b/lib/web_ui/test/html/surface/scene_builder_test.dart index a058051b4017f..d17cd36687310 100644 --- a/lib/web_ui/test/html/surface/scene_builder_test.dart +++ b/lib/web_ui/test/html/surface/scene_builder_test.dart @@ -30,100 +30,126 @@ void testMain() { 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 ''''''; - }); + 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 ''''''; - }); + 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 ''' + 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( + 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 ''' + 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 ''' + 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 ''''''; - }); + 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 ''' + 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', - () { + 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 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(); @@ -147,9 +173,11 @@ void testMain() { // 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); + 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; @@ -166,9 +194,11 @@ void testMain() { // 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); + 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; @@ -203,10 +233,7 @@ void testMain() { // 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 - ); + 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(); @@ -230,10 +257,7 @@ void testMain() { // 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 - ); + 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(); @@ -267,17 +291,13 @@ void testMain() { // 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 - ); + 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(); + list = contentAfterReuse.querySelectorAll('img').cast().toList(); for (final DomHTMLImageElement image in list) { expect(image.alt, 'marked'); } @@ -309,7 +329,9 @@ void testMain() { { final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); builder.pushOffset(0, 0); - final PersistedContainerSurface clip = builder.pushClipRect(const ui.Rect.fromLTRB(1000, 1000, 2000, 2000)) as PersistedContainerSurface; + 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(); @@ -352,30 +374,22 @@ void testMain() { expect(content.querySelectorAll('flt-picture').single.children, isNotEmpty); }); - test( - 'skips painting picture when picture fully clipped out with' - ' transform and offset', () async { + 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.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); + 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 { @@ -392,7 +406,8 @@ void testMain() { // Frame 2: picture is clipped out after an update final SurfaceSceneBuilder builder2 = SurfaceSceneBuilder(); - final PersistedOffset offset2 = builder2.pushOffset(-10000, -10000, oldLayer: offset1) as PersistedOffset; + 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!; @@ -461,16 +476,27 @@ void testMain() { // 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(); + 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); + 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; @@ -487,20 +513,28 @@ void testMain() { // 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 }) { + 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, _) { + 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); + observer.observe(SurfaceSceneBuilder.debugLastFrameScene!.rootElement!, childList: true); final SurfaceSceneBuilder builder = SurfaceSceneBuilder(); for (int i = 0; i < string.length; i++) { @@ -512,7 +546,10 @@ void testMain() { scene.webOnlyRootElement!.querySelectorAll('flt-paragraph').toList(); expect(pTags, hasLength(string.length)); expect( - scene.webOnlyRootElement!.querySelectorAll('flt-paragraph').map((DomElement p) => p.innerText).join(), + scene.webOnlyRootElement! + .querySelectorAll('flt-paragraph') + .map((DomElement p) => p.innerText) + .join(), string, ); renderedLayers.removeWhere((String key, ui.EngineLayer value) => !string.contains(key)); @@ -532,11 +569,7 @@ void testMain() { 'deletions': actualDeletions.length - actualMoves, 'moves': actualMoves, }, - { - 'additions': additions, - 'deletions': deletions, - 'moves': moves, - }, + {'additions': additions, 'deletions': deletions, 'moves': moves}, ); }); } @@ -595,9 +628,7 @@ void testMain() { 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.pushClipRect(const ui.Rect.fromLTRB(10, 10, 300, 300)); builder2.addPicture(ui.Offset.zero, picture1); builder2.pop(); builder2.pop(); @@ -627,9 +658,7 @@ void testMain() { 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.pushClipRect(const ui.Rect.fromLTRB(10, 10, 300, 300)); builder2.addPicture(ui.Offset.zero, picture1); builder2.pop(); builder2.pop(); @@ -656,8 +685,8 @@ void testMain() { // 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 + const ui.Rect.fromLTRB(5, 10, 300, 300), + oldLayer: oldLayer, ); builder2.addPicture(ui.Offset.zero, _drawEmptyPicture()); builder2.pop(); @@ -666,10 +695,7 @@ void testMain() { expect(contentAfterReuse, isNotNull); final SurfaceSceneBuilder builder3 = SurfaceSceneBuilder(); - builder3.pushClipRect( - const ui.Rect.fromLTRB(25, 10, 300, 300), - oldLayer: oldLayer2 - ); + 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. @@ -678,12 +704,11 @@ void testMain() { }); } -typedef TestLayerBuilder = ui.EngineLayer Function( - ui.SceneBuilder sceneBuilder, ui.EngineLayer? oldLayer); +typedef TestLayerBuilder = + ui.EngineLayer Function(ui.SceneBuilder sceneBuilder, ui.EngineLayer? oldLayer); typedef ExpectedHtmlGetter = String Function(); -void testLayerLifeCycle( - TestLayerBuilder layerBuilder, ExpectedHtmlGetter expectedHtmlGetter) { +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(); @@ -698,9 +723,7 @@ void testLayerLifeCycle( tester.expectSceneHtml(expectedHtmlGetter()); PersistedSurface findSurface() { - return enumerateSurfaces() - .where((PersistedSurface s) => s.runtimeType == surfaceType) - .single; + return enumerateSurfaces().where((PersistedSurface s) => s.runtimeType == surfaceType).single; } final PersistedSurface surface1 = findSurface(); @@ -770,7 +793,10 @@ class MockPersistedPicture extends PersistedPicture { int updateCount = 0; int applyPaintCount = 0; - final BitmapCanvas _fakeCanvas = BitmapCanvas(const ui.Rect.fromLTRB(0, 0, 10, 10), RenderStrategy()); + final BitmapCanvas _fakeCanvas = BitmapCanvas( + const ui.Rect.fromLTRB(0, 0, 10, 10), + RenderStrategy(), + ); @override EngineCanvas get canvas { @@ -817,38 +843,39 @@ 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), - ], - ); + 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); + 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)); + 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)); + 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)); + 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(); } @@ -862,50 +889,52 @@ 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), - ], - ); + 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); + 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)); + 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)); + 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); + 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)); + 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 DomCanvasElement canvas = createDomCanvasElement(width: width, height: height); final DomCanvasRenderingContext2D ctx = canvas.context2D; ctx.fillStyle = '#E04040'; ctx.fillRect(0, 0, 33, 50); @@ -927,9 +956,6 @@ class SceneTester { final SurfaceScene scene; void expectSceneHtml(String expectedHtml) { - expect( - scene.webOnlyRootElement, - hasHtml(expectedHtml), - ); + expect(scene.webOnlyRootElement, hasHtml(expectedHtml)); } } diff --git a/lib/web_ui/test/html/surface/shaders/normalized_gradient_test.dart b/lib/web_ui/test/html/surface/shaders/normalized_gradient_test.dart index 2aae2af94a970..83a37e0e13c75 100644 --- a/lib/web_ui/test/html/surface/shaders/normalized_gradient_test.dart +++ b/lib/web_ui/test/html/surface/shaders/normalized_gradient_test.dart @@ -14,9 +14,10 @@ void main() { 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]); + 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); @@ -30,9 +31,10 @@ void testMain() { }); test('3 stop at end', () { - final NormalizedGradient gradient = NormalizedGradient(const [ - ui.Color(0xFF000000), ui.Color(0xFFFF7f3f) - ], stops: [0.5, 1.0]); + 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); @@ -46,9 +48,10 @@ void testMain() { }); test('4 stop', () { - final NormalizedGradient gradient = NormalizedGradient(const [ - ui.Color(0xFF000000), ui.Color(0xFFFF7f3f) - ], stops: [0.25, 0.5]); + 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); @@ -64,11 +67,16 @@ void testMain() { }); 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]); + 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); @@ -91,7 +99,8 @@ void testMain() { test('2 stops at ends', () { final NormalizedGradient gradient = NormalizedGradient(const [ - ui.Color(0x00000000), ui.Color(0xFFFFFFFF) + ui.Color(0x00000000), + ui.Color(0xFFFFFFFF), ]); int res = _computeColorAt(gradient, 0.0); assert(res == 0); @@ -114,11 +123,11 @@ int _computeColorAt(NormalizedGradient gradient, double t) { 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 <<= 8; val |= (r * 0xFF).toInt() & 0xFF; - val<<=8; + val <<= 8; val |= (g * 0xFF).toInt() & 0xFF; - val<<=8; + val <<= 8; val |= (b * 0xFF).toInt() & 0xFF; return val; } diff --git a/lib/web_ui/test/html/surface/shaders/shader_builder_test.dart b/lib/web_ui/test/html/surface/shaders/shader_builder_test.dart index f20467bbcc287..876ee74169104 100644 --- a/lib/web_ui/test/html/surface/shaders/shader_builder_test.dart +++ b/lib/web_ui/test/html/surface/shaders/shader_builder_test.dart @@ -14,11 +14,13 @@ void main() { 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' + 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' + 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' @@ -48,23 +50,24 @@ void testMain() { 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'); + 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', () { @@ -86,24 +89,25 @@ void testMain() { 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'); + 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', () { @@ -111,9 +115,10 @@ void testMain() { builder.addIn(ShaderType.kVec4, name: 'position'); builder.addIn(ShaderType.kVec4); expect( - builder.build(), - 'attribute vec4 position;\n' - 'attribute vec4 attr_0;\n'); + builder.build(), + 'attribute vec4 position;\n' + 'attribute vec4 attr_0;\n', + ); }); test('in declaration WebGL1', () { @@ -121,9 +126,10 @@ void testMain() { builder.addIn(ShaderType.kVec4, name: 'position'); builder.addIn(ShaderType.kVec4); expect( - builder.build(), - 'varying vec4 position;\n' - 'varying vec4 attr_0;\n'); + builder.build(), + 'varying vec4 position;\n' + 'varying vec4 attr_0;\n', + ); }); test('Attribute declaration WebGL2', () { @@ -131,39 +137,40 @@ void testMain() { 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'); + 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'); + 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'); + 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'); + 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'); + builder.build(), + '#version 300 es\n' + 'uniform vec4 v1;\n' + 'uniform vec4 uni_0;\n', + ); }); test('Float precision', () { @@ -171,10 +178,11 @@ void testMain() { 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'); + builder.build(), + '#version 300 es\n' + 'precision lowp float;\n' + 'uniform float f1;\n', + ); }); test('Integer precision', () { @@ -182,27 +190,28 @@ void testMain() { 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'); + 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 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'); + 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/lib/web_ui/test/html/surface/surface_test.dart b/lib/web_ui/test/html/surface/surface_test.dart index 6bcb3b5d07d14..2c1656762bfa3 100644 --- a/lib/web_ui/test/html/surface/surface_test.dart +++ b/lib/web_ui/test/html/surface/surface_test.dart @@ -28,14 +28,18 @@ void testMain() { final SceneBuilder builder = SceneBuilder(); final PersistedOpacity opacityLayer = builder.pushOpacity(100) as PersistedOpacity; try { - debugAssertSurfaceState(opacityLayer, PersistedSurfaceState.active, PersistedSurfaceState.pendingRetention); + 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', + 'Expected one of: PersistedSurfaceState.active, PersistedSurfaceState.pendingRetention\n' + 'But was: PersistedSurfaceState.created', ); } }); @@ -105,8 +109,7 @@ void testMain() { expect(opacityLayer1.isReleased, isTrue); expect(opacityLayer1.rootElement, isNull); expect(opacityLayer2.isActive, isTrue); - expect( - opacityLayer2.rootElement, element); // adopts old surface's element + expect(opacityLayer2.rootElement, element); // adopts old surface's element expect(opacityLayer2.oldLayer, isNull); }); @@ -158,58 +161,69 @@ void testMain() { // 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( + '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(); @@ -335,11 +349,7 @@ void testMain() { expect(d1.rootElement, elementD); expect( - [ - elementD.parent!, - elementC.parent!, - elementB.parent!, - ], + [elementD.parent!, elementC.parent!, elementB.parent!], [elementC, elementB, elementA], ); }); @@ -362,24 +372,32 @@ void testMain() { expect(opacityLayer1.isReleased, isTrue); expect(opacityLayer1.rootElement, isNull); expect(opacityLayer2.isActive, isTrue); - expect( - opacityLayer2.rootElement, element); // adopts old surface's element + expect(opacityLayer2.rootElement, element); // adopts old surface's element }); }); final Map layerFactories = { - 'ColorFilterEngineLayer': (SurfaceSceneBuilder builder) => builder.pushColorFilter(const ColorFilter.mode( - Color(0xFFFF0000), - BlendMode.srcIn, - )), + '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))), + '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)), + '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) { @@ -389,7 +407,10 @@ void testMain() { 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, + colors, + stops, + TileMode.clamp, + Matrix4.identity().storage, ); return builder.pushShaderMask(shader, shaderBounds, BlendMode.srcOver); }, diff --git a/lib/web_ui/test/html/testimage.dart b/lib/web_ui/test/html/testimage.dart index 319c199b7f2e8..e1ec4d41fa457 100644 --- a/lib/web_ui/test/html/testimage.dart +++ b/lib/web_ui/test/html/testimage.dart @@ -115,8 +115,7 @@ const String _flutterLogoBase64 = HtmlImage createFlutterLogoTestImage() { return HtmlImage( - createDomHTMLImageElement() - ..src = 'data:text/plain;base64,$_flutterLogoBase64', + createDomHTMLImageElement()..src = 'data:text/plain;base64,$_flutterLogoBase64', 50, 50, ); diff --git a/lib/web_ui/test/html/text/canvas_paragraph_builder_test.dart b/lib/web_ui/test/html/text/canvas_paragraph_builder_test.dart index e02fabe324039..70bc3a7fbc51c 100644 --- a/lib/web_ui/test/html/text/canvas_paragraph_builder_test.dart +++ b/lib/web_ui/test/html/text/canvas_paragraph_builder_test.dart @@ -45,12 +45,11 @@ Future testMain() async { expect(paragraph1.spans.single.start, 0); expect(paragraph1.spans.single.end, 0); - final CanvasParagraph paragraph2 = rich( - EngineParagraphStyle(), - (CanvasParagraphBuilder builder) { - builder.addText(''); - }, - ); + final CanvasParagraph paragraph2 = rich(EngineParagraphStyle(), ( + CanvasParagraphBuilder builder, + ) { + builder.addText(''); + }); expect(paragraph2.plainText, ''); expect(paragraph2.spans, hasLength(1)); expect(paragraph2.spans.single.start, 0); @@ -72,7 +71,7 @@ Future testMain() async { expectOuterHtml( paragraph, '' - '' + '' 'Hello' '' '', @@ -84,10 +83,10 @@ Future testMain() async { expectOuterHtml( paragraph, '' - '' + '' 'Hel' '' - '' + '' 'lo' '' '', @@ -114,7 +113,7 @@ Future testMain() async { expectOuterHtml( paragraph, '' - '' + '' 'Hello' '' '', @@ -139,7 +138,7 @@ Future testMain() async { expectOuterHtml( paragraph, '' - '' + '' 'Hello' '' '', @@ -161,10 +160,10 @@ Future testMain() async { expectOuterHtml( paragraph, '' - '' + '' 'Hell' '' - '' + '' '...' '' '', @@ -173,16 +172,14 @@ Future testMain() async { }); test('Builds a single-span paragraph with complex styles', () { - final EngineParagraphStyle style = - EngineParagraphStyle(fontSize: 13.0, height: 1.5); + final EngineParagraphStyle style = EngineParagraphStyle(fontSize: 13.0, height: 1.5); final CanvasParagraphBuilder builder = CanvasParagraphBuilder(style); builder.pushStyle(TextStyle(fontSize: 9.0)); builder.pushStyle(TextStyle(fontWeight: FontWeight.bold)); builder.pushStyle(TextStyle(fontSize: 40.0)); builder.pop(); - builder - .pushStyle(TextStyle(fontStyle: FontStyle.italic, letterSpacing: 2.0)); + builder.pushStyle(TextStyle(fontStyle: FontStyle.italic, letterSpacing: 2.0)); builder.addText('Hello'); final CanvasParagraph paragraph = builder.build(); @@ -193,7 +190,7 @@ Future testMain() async { expectOuterHtml( paragraph, '' - '' + '' 'Hello' '' '', @@ -232,13 +229,13 @@ Future testMain() async { expectOuterHtml( paragraph, '' - '' + '' 'Hello' '' - '' + '' ' ' '' - '' + '' 'world' '' '', @@ -250,13 +247,13 @@ Future testMain() async { expectOuterHtml( paragraph, '' - '' + '' 'Hello' '' '' ' ' '' - '' + '' 'world' '' '', @@ -265,23 +262,11 @@ Future testMain() async { final ParagraphSpan hello = paragraph.spans.first; expect(getSpanText(paragraph, hello), 'Hello'); - expect( - hello.style, - styleWithDefaults( - fontSize: 13.0, - fontWeight: FontWeight.bold, - ), - ); + expect(hello.style, styleWithDefaults(fontSize: 13.0, fontWeight: FontWeight.bold)); final ParagraphSpan world = paragraph.spans.last; expect(getSpanText(paragraph, world), ' world'); - expect( - world.style, - styleWithDefaults( - fontSize: 13.0, - fontStyle: FontStyle.italic, - ), - ); + expect(world.style, styleWithDefaults(fontSize: 13.0, fontStyle: FontStyle.italic)); }); test('Builds a multi-span paragraph with complex styles', () { @@ -305,16 +290,16 @@ Future testMain() async { expectOuterHtml( paragraph, '' - '' + '' 'Hello' '' - '' + '' ' ' '' - '' + '' 'world' '' - '' + '' '!' '' '', @@ -325,33 +310,21 @@ Future testMain() async { expect(getSpanText(paragraph, hello), 'Hello'); expect( hello.style, - styleWithDefaults( - fontSize: 13.0, - fontWeight: FontWeight.bold, - height: 2.0, - ), + styleWithDefaults(fontSize: 13.0, fontWeight: FontWeight.bold, height: 2.0), ); final ParagraphSpan world = paragraph.spans[1]; expect(getSpanText(paragraph, world), ' world'); expect( world.style, - styleWithDefaults( - fontSize: 13.0, - fontWeight: FontWeight.bold, - fontStyle: FontStyle.italic, - ), + styleWithDefaults(fontSize: 13.0, fontWeight: FontWeight.bold, fontStyle: FontStyle.italic), ); final ParagraphSpan bang = paragraph.spans[2]; expect(getSpanText(paragraph, bang), '!'); expect( bang.style, - styleWithDefaults( - fontSize: 13.0, - fontWeight: FontWeight.normal, - fontStyle: FontStyle.italic, - ), + styleWithDefaults(fontSize: 13.0, fontWeight: FontWeight.normal, fontStyle: FontStyle.italic), ); }); @@ -373,16 +346,16 @@ Future testMain() async { expectOuterHtml( paragraph, '' - '' + '' 'First' '' - '' + '' 'Second' '' - '' + '' ' ' '' - '' + '' 'ThirdLongLine' '' '', @@ -394,16 +367,16 @@ Future testMain() async { expectOuterHtml( paragraph, '' - '' + '' 'First' '' - '' + '' 'Second' '' - '' + '' ' ' '' - '' + '' 'ThirdLongLine' '' '', @@ -497,10 +470,7 @@ const String defaultFontFamily = 'FlutterTest'; const num defaultFontSize = 14; String paragraphStyle() { - return [ - 'position: absolute;', - 'white-space: pre;', - ].join(' '); + return ['position: absolute;', 'white-space: pre;'].join(' '); } String spanStyle({ diff --git a/lib/web_ui/test/html/text/canvas_paragraph_test.dart b/lib/web_ui/test/html/text/canvas_paragraph_test.dart index a00c6d16f9357..8d907f4efd85c 100644 --- a/lib/web_ui/test/html/text/canvas_paragraph_test.dart +++ b/lib/web_ui/test/html/text/canvas_paragraph_test.dart @@ -22,8 +22,7 @@ Future testMain() async { test('return empty list for invalid ranges', () { final CanvasParagraph paragraph = rich(ahemStyle, (CanvasParagraphBuilder builder) { builder.addText('Lorem ipsum'); - }) - ..layout(constrain(double.infinity)); + })..layout(constrain(double.infinity)); expect(paragraph.getBoxesForRange(-1, 0), []); expect(paragraph.getBoxesForRange(0, 0), []); @@ -40,24 +39,19 @@ Future testMain() async { builder.addText('ipsum '); builder.pop(); builder.addText('.'); - }) - ..layout(constrain(double.infinity)); + })..layout(constrain(double.infinity)); // Within the first span "Lorem ". expect( // "or" paragraph.getBoxesForRange(1, 3), - [ - box(10, 0, 30, 10), - ], + [box(10, 0, 30, 10)], ); expect( // "Lorem" paragraph.getBoxesForRange(0, 5), - [ - box(0, 0, 50, 10), - ], + [box(0, 0, 50, 10)], ); // Make sure the trailing space is also included in the box. expect( @@ -76,9 +70,7 @@ Future testMain() async { expect( // "psum" paragraph.getBoxesForRange(7, 11), - [ - box(70, 0, 110, 10), - ], + [box(70, 0, 110, 10)], ); expect( // "um " @@ -129,8 +121,7 @@ Future testMain() async { test('handles multi-line single-span paragraphs', () { final CanvasParagraph paragraph = rich(ahemStyle, (CanvasParagraphBuilder builder) { builder.addText('Lorem ipsum dolor sit'); - }) - ..layout(constrain(90.0)); + })..layout(constrain(90.0)); // Lines: // "Lorem " @@ -142,9 +133,7 @@ Future testMain() async { expect( // "or" paragraph.getBoxesForRange(1, 3), - [ - box(10, 0, 30, 10), - ], + [box(10, 0, 30, 10)], ); // Make sure the trailing space at the end of line is also included in the // box. @@ -206,8 +195,7 @@ Future testMain() async { builder.addText('dolor '); builder.pop(); builder.addText('sit'); - }) - ..layout(constrain(90.0)); + })..layout(constrain(90.0)); // Lines: // "Lorem " @@ -219,9 +207,7 @@ Future testMain() async { expect( // "ore" paragraph.getBoxesForRange(1, 4), - [ - box(10, 0, 40, 10), - ], + [box(10, 0, 40, 10)], ); expect( // "Lorem " @@ -310,8 +296,7 @@ Future testMain() async { // width = 20.0 * 4 = 80.0 // baseline = 20.0 * 80% = 16.0 builder.addText('amet'); - }) - ..layout(constrain(420.0)); + })..layout(constrain(420.0)); // Lines: // "Lorem ipsum dolor " (width: 420, height: 40, baseline: 32) @@ -427,8 +412,7 @@ Future testMain() async { builder.addText('ipsum '); builder.pop(); builder.addText('.'); - }) - ..layout(constrain(double.infinity)); + })..layout(constrain(double.infinity)); // Above the line, at the beginning. expect( @@ -436,10 +420,7 @@ Future testMain() async { pos(0, ui.TextAffinity.downstream), ); // At the top left corner of the line. - expect( - paragraph.getPositionForOffset(ui.Offset.zero), - pos(0, ui.TextAffinity.downstream), - ); + expect(paragraph.getPositionForOffset(ui.Offset.zero), pos(0, ui.TextAffinity.downstream)); // At the beginning of the line. expect( paragraph.getPositionForOffset(const ui.Offset(0, 5)), @@ -490,8 +471,7 @@ Future testMain() async { test('handles multi-line single-span paragraphs', () { final CanvasParagraph paragraph = rich(ahemStyle, (CanvasParagraphBuilder builder) { builder.addText('Lorem ipsum dolor sit'); - }) - ..layout(constrain(90.0)); + })..layout(constrain(90.0)); // Lines: // "Lorem " @@ -504,10 +484,7 @@ Future testMain() async { pos(0, ui.TextAffinity.downstream), ); // At the top left corner of the line. - expect( - paragraph.getPositionForOffset(ui.Offset.zero), - pos(0, ui.TextAffinity.downstream), - ); + expect(paragraph.getPositionForOffset(ui.Offset.zero), pos(0, ui.TextAffinity.downstream)); // At the beginning of the first line. expect( paragraph.getPositionForOffset(const ui.Offset(0, 5)), @@ -590,8 +567,7 @@ Future testMain() async { builder.addText('dolor '); builder.pop(); builder.addText('sit'); - }) - ..layout(constrain(90.0)); + })..layout(constrain(90.0)); // Lines: // "Lorem " @@ -677,8 +653,7 @@ Future testMain() async { test('single-line', () { final CanvasParagraph paragraph = rich(ahemStyle, (CanvasParagraphBuilder builder) { builder.addText('One single line'); - }) - ..layout(constrain(400.0)); + })..layout(constrain(400.0)); // "One single line".length == 15 for (int i = 0; i < 15; i++) { @@ -695,8 +670,7 @@ Future testMain() async { builder.addText('First line\n'); builder.addText('Second line\n'); builder.addText('Third line'); - }) - ..layout(constrain(400.0)); + })..layout(constrain(400.0)); // "First line\n".length == 11 for (int i = 0; i < 11; i++) { @@ -768,7 +742,12 @@ Future testMain() async { const ui.TextRange loremRange = ui.TextRange(start: 0, end: 5); expect(paragraph.getWordBoundary(const ui.TextPosition(offset: 4)), loremRange); - expect(paragraph.getWordBoundary(const ui.TextPosition(offset: 5, affinity: ui.TextAffinity.upstream)), loremRange); + expect( + paragraph.getWordBoundary( + const ui.TextPosition(offset: 5, affinity: ui.TextAffinity.upstream), + ), + loremRange, + ); }); test('$CanvasParagraph.longestLine', () { diff --git a/lib/web_ui/test/html/text/font_collection_test.dart b/lib/web_ui/test/html/text/font_collection_test.dart index e0f810de000c7..5702371779df1 100644 --- a/lib/web_ui/test/html/text/font_collection_test.dart +++ b/lib/web_ui/test/html/text/font_collection_test.dart @@ -40,13 +40,12 @@ void testMain() { const String testFontFamily = 'Ahem'; final List fontFamilyList = []; final HtmlFontCollection collection = HtmlFontCollection(); - await collection.loadAssetFonts(FontManifest([ - FontFamily(testFontFamily, [ - FontAsset(testFontUrl, {}) - ]) - ])); - domDocument.fonts! - .forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { + await collection.loadAssetFonts( + FontManifest([ + FontFamily(testFontFamily, [FontAsset(testFontUrl, {})]), + ]), + ); + domDocument.fonts!.forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { fontFamilyList.add(f.family!); }); @@ -54,39 +53,40 @@ void testMain() { expect(fontFamilyList.first, 'Ahem'); }); - test('Register Asset with white space in the family name', () async { - const String testFontFamily = 'Ahem ahem ahem'; - final List fontFamilyList = []; - - final HtmlFontCollection collection = HtmlFontCollection(); - await collection.loadAssetFonts(FontManifest([ - FontFamily(testFontFamily, [ - FontAsset(testFontUrl, {}) - ]) - ])); - domDocument.fonts! - .forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { - fontFamilyList.add(f.family!); - }); + test( + 'Register Asset with white space in the family name', + () async { + const String testFontFamily = 'Ahem ahem ahem'; + final List fontFamilyList = []; + + final HtmlFontCollection collection = HtmlFontCollection(); + await collection.loadAssetFonts( + FontManifest([ + FontFamily(testFontFamily, [FontAsset(testFontUrl, {})]), + ]), + ); + domDocument.fonts!.forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { + fontFamilyList.add(f.family!); + }); - expect(fontFamilyList.length, equals(1)); - expect(fontFamilyList.first, 'Ahem ahem ahem'); - }, - // TODO(mdebbar): https://github.com/flutter/flutter/issues/51142 - skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit); + expect(fontFamilyList.length, equals(1)); + expect(fontFamilyList.first, 'Ahem ahem ahem'); + }, + // TODO(mdebbar): https://github.com/flutter/flutter/issues/51142 + skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit, + ); test('Register Asset with capital case letters', () async { const String testFontFamily = 'AhEm'; final List fontFamilyList = []; final HtmlFontCollection collection = HtmlFontCollection(); - await collection.loadAssetFonts(FontManifest([ - FontFamily(testFontFamily, [ - FontAsset(testFontUrl, {}) - ]) - ])); - domDocument.fonts! - .forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { + await collection.loadAssetFonts( + FontManifest([ + FontFamily(testFontFamily, [FontAsset(testFontUrl, {})]), + ]), + ); + domDocument.fonts!.forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { fontFamilyList.add(f.family!); }); @@ -98,16 +98,15 @@ void testMain() { const String testFontFamily = 'Ahem'; final List fontFamilyList = []; final HtmlFontCollection collection = HtmlFontCollection(); - await collection.loadAssetFonts(FontManifest([ - FontFamily(testFontFamily, [ - FontAsset(testFontUrl, { - 'weight': 'bold' - }) - ]) - ])); - - domDocument.fonts! - .forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { + await collection.loadAssetFonts( + FontManifest([ + FontFamily(testFontFamily, [ + FontAsset(testFontUrl, {'weight': 'bold'}), + ]), + ]), + ); + + domDocument.fonts!.forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { expect(f.weight, 'bold'); expect(f2.weight, 'bold'); fontFamilyList.add(f.family!); @@ -119,114 +118,121 @@ void testMain() { }); group('fonts with special characters', () { - test('Register Asset twice with special character slash', () async { - const String testFontFamily = '/Ahem'; - final List fontFamilyList = []; - - final HtmlFontCollection collection = HtmlFontCollection(); - await collection.loadAssetFonts(FontManifest([ - FontFamily(testFontFamily, [ - FontAsset(testFontUrl, {}) - ]) - ])); - domDocument.fonts! - .forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { - fontFamilyList.add(f.family!); - }); - - if (ui_web.browser.browserEngine != ui_web.BrowserEngine.firefox) { - expect(fontFamilyList.length, equals(2)); - expect(fontFamilyList, contains("'/Ahem'")); - expect(fontFamilyList, contains('/Ahem')); - } else { - expect(fontFamilyList.length, equals(1)); - expect(fontFamilyList.first, '"/Ahem"'); - } - }, - // TODO(mdebbar): https://github.com/flutter/flutter/issues/51142 - skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit); - - test('Register Asset twice with exclamation mark', () async { - const String testFontFamily = 'Ahem!!ahem'; - final List fontFamilyList = []; - - final HtmlFontCollection collection = HtmlFontCollection(); - await collection.loadAssetFonts(FontManifest([ - FontFamily(testFontFamily, [ - FontAsset(testFontUrl, {}) - ]) - ])); - domDocument.fonts! - .forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { - fontFamilyList.add(f.family!); - }); - - if (ui_web.browser.browserEngine != ui_web.BrowserEngine.firefox) { - expect(fontFamilyList.length, equals(2)); - expect(fontFamilyList, contains("'Ahem!!ahem'")); - expect(fontFamilyList, contains('Ahem!!ahem')); - } else { - expect(fontFamilyList.length, equals(1)); - expect(fontFamilyList.first, '"Ahem!!ahem"'); - } - }, - // TODO(mdebbar): https://github.com/flutter/flutter/issues/51142 - skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit); - - test('Register Asset twice with comma', () async { - const String testFontFamily = 'Ahem ,ahem'; - final List fontFamilyList = []; - - final HtmlFontCollection collection = HtmlFontCollection(); - await collection.loadAssetFonts(FontManifest([ - FontFamily(testFontFamily, [ - FontAsset(testFontUrl, {}) - ]) - ])); - domDocument.fonts! - .forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { - fontFamilyList.add(f.family!); - }); - - if (ui_web.browser.browserEngine != ui_web.BrowserEngine.firefox) { - expect(fontFamilyList.length, equals(2)); - expect(fontFamilyList, contains("'Ahem ,ahem'")); - expect(fontFamilyList, contains('Ahem ,ahem')); - } else { - expect(fontFamilyList.length, equals(1)); - expect(fontFamilyList.first, '"Ahem ,ahem"'); - } - }, - // TODO(mdebbar): https://github.com/flutter/flutter/issues/51142 - skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit); - - test('Register Asset twice with a digit at the start of a token', - () async { - const String testFontFamily = 'Ahem 1998'; - final List fontFamilyList = []; - - final HtmlFontCollection collection = HtmlFontCollection(); - await collection.loadAssetFonts(FontManifest([ - FontFamily(testFontFamily, [ - FontAsset(testFontUrl, {}) - ]) - ])); - domDocument.fonts! - .forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { - fontFamilyList.add(f.family!); - }); - - if (ui_web.browser.browserEngine != ui_web.BrowserEngine.firefox) { - expect(fontFamilyList.length, equals(2)); - expect(fontFamilyList, contains('Ahem 1998')); - expect(fontFamilyList, contains("'Ahem 1998'")); - } else { - expect(fontFamilyList.length, equals(1)); - expect(fontFamilyList.first, '"Ahem 1998"'); - } - }, - // TODO(mdebbar): https://github.com/flutter/flutter/issues/51142 - skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit); + test( + 'Register Asset twice with special character slash', + () async { + const String testFontFamily = '/Ahem'; + final List fontFamilyList = []; + + final HtmlFontCollection collection = HtmlFontCollection(); + await collection.loadAssetFonts( + FontManifest([ + FontFamily(testFontFamily, [FontAsset(testFontUrl, {})]), + ]), + ); + domDocument.fonts!.forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { + fontFamilyList.add(f.family!); + }); + + if (ui_web.browser.browserEngine != ui_web.BrowserEngine.firefox) { + expect(fontFamilyList.length, equals(2)); + expect(fontFamilyList, contains("'/Ahem'")); + expect(fontFamilyList, contains('/Ahem')); + } else { + expect(fontFamilyList.length, equals(1)); + expect(fontFamilyList.first, '"/Ahem"'); + } + }, + // TODO(mdebbar): https://github.com/flutter/flutter/issues/51142 + skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit, + ); + + test( + 'Register Asset twice with exclamation mark', + () async { + const String testFontFamily = 'Ahem!!ahem'; + final List fontFamilyList = []; + + final HtmlFontCollection collection = HtmlFontCollection(); + await collection.loadAssetFonts( + FontManifest([ + FontFamily(testFontFamily, [FontAsset(testFontUrl, {})]), + ]), + ); + domDocument.fonts!.forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { + fontFamilyList.add(f.family!); + }); + + if (ui_web.browser.browserEngine != ui_web.BrowserEngine.firefox) { + expect(fontFamilyList.length, equals(2)); + expect(fontFamilyList, contains("'Ahem!!ahem'")); + expect(fontFamilyList, contains('Ahem!!ahem')); + } else { + expect(fontFamilyList.length, equals(1)); + expect(fontFamilyList.first, '"Ahem!!ahem"'); + } + }, + // TODO(mdebbar): https://github.com/flutter/flutter/issues/51142 + skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit, + ); + + test( + 'Register Asset twice with comma', + () async { + const String testFontFamily = 'Ahem ,ahem'; + final List fontFamilyList = []; + + final HtmlFontCollection collection = HtmlFontCollection(); + await collection.loadAssetFonts( + FontManifest([ + FontFamily(testFontFamily, [FontAsset(testFontUrl, {})]), + ]), + ); + domDocument.fonts!.forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { + fontFamilyList.add(f.family!); + }); + + if (ui_web.browser.browserEngine != ui_web.BrowserEngine.firefox) { + expect(fontFamilyList.length, equals(2)); + expect(fontFamilyList, contains("'Ahem ,ahem'")); + expect(fontFamilyList, contains('Ahem ,ahem')); + } else { + expect(fontFamilyList.length, equals(1)); + expect(fontFamilyList.first, '"Ahem ,ahem"'); + } + }, + // TODO(mdebbar): https://github.com/flutter/flutter/issues/51142 + skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit, + ); + + test( + 'Register Asset twice with a digit at the start of a token', + () async { + const String testFontFamily = 'Ahem 1998'; + final List fontFamilyList = []; + + final HtmlFontCollection collection = HtmlFontCollection(); + await collection.loadAssetFonts( + FontManifest([ + FontFamily(testFontFamily, [FontAsset(testFontUrl, {})]), + ]), + ); + domDocument.fonts!.forEach((DomFontFace f, DomFontFace f2, DomFontFaceSet s) { + fontFamilyList.add(f.family!); + }); + + if (ui_web.browser.browserEngine != ui_web.BrowserEngine.firefox) { + expect(fontFamilyList.length, equals(2)); + expect(fontFamilyList, contains('Ahem 1998')); + expect(fontFamilyList, contains("'Ahem 1998'")); + } else { + expect(fontFamilyList.length, equals(1)); + expect(fontFamilyList.first, '"Ahem 1998"'); + } + }, + // TODO(mdebbar): https://github.com/flutter/flutter/issues/51142 + skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit, + ); }); }); } diff --git a/lib/web_ui/test/html/text/font_loading_test.dart b/lib/web_ui/test/html/text/font_loading_test.dart index 42b00c5020a1e..460d4fa6f2d3a 100644 --- a/lib/web_ui/test/html/text/font_loading_test.dart +++ b/lib/web_ui/test/html/text/font_loading_test.dart @@ -27,79 +27,98 @@ Future testMain() async { domDocument.fonts!.clear(); }); - test('returns normally from invalid font buffer', () async { - await expectLater( - () async => ui.loadFontFromList(Uint8List(0), fontFamily: 'test-font'), - returnsNormally - ); - }, - // TODO(hterkelsen): https://github.com/flutter/flutter/issues/56702 - skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit); - - test('loads Blehm font from buffer', () async { - expect(_containsFontFamily('Blehm'), isFalse); - - final ByteBuffer response = await httpFetchByteBuffer(testFontUrl); - await ui.loadFontFromList(response.asUint8List(), fontFamily: 'Blehm'); - - expect(_containsFontFamily('Blehm'), isTrue); - }, - // TODO(hterkelsen): https://github.com/flutter/flutter/issues/56702 - skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit); - - test('loading font should clear measurement caches', () async { - final EngineParagraphStyle style = EngineParagraphStyle(); - const ui.ParagraphConstraints constraints = - ui.ParagraphConstraints(width: 30.0); - - final CanvasParagraphBuilder canvasBuilder = CanvasParagraphBuilder(style); - canvasBuilder.addText('test'); - // Triggers the measuring and verifies the ruler cache has been populated. - canvasBuilder.build().layout(constraints); - expect(Spanometer.rulers.length, 1); - - // Now, loads a new font using loadFontFromList. This should clear the - // cache - final ByteBuffer response = await httpFetchByteBuffer(testFontUrl); - await ui.loadFontFromList(response.asUint8List(), fontFamily: 'Blehm'); - - // Verifies the font is loaded, and the cache is cleaned. - expect(_containsFontFamily('Blehm'), isTrue); - expect(Spanometer.rulers.length, 0); - }, - // TODO(hterkelsen): https://github.com/flutter/flutter/issues/56702 - skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit); - - test('loading font should send font change message', () async { - final ui.PlatformMessageCallback? oldHandler = ui.PlatformDispatcher.instance.onPlatformMessage; - String? actualName; - String? message; - ui.PlatformDispatcher.instance.onPlatformMessage = (String name, ByteData? data, - ui.PlatformMessageResponseCallback? callback) { - actualName = name; - final ByteBuffer buffer = data!.buffer; - final Uint8List list = - buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - message = utf8.decode(list); - }; - final ByteBuffer response = await httpFetchByteBuffer(testFontUrl); - await ui.loadFontFromList(response.asUint8List(), fontFamily: 'Blehm'); - final Completer completer = Completer(); - domWindow.requestAnimationFrame((_) { completer.complete();}); - await completer.future; - ui.PlatformDispatcher.instance.onPlatformMessage = oldHandler; - expect(actualName, 'flutter/system'); - expect(message, '{"type":"fontsChange"}'); - }, - // TODO(hterkelsen): https://github.com/flutter/flutter/issues/56702 - skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit); + test( + 'returns normally from invalid font buffer', + () async { + await expectLater( + () async => ui.loadFontFromList(Uint8List(0), fontFamily: 'test-font'), + returnsNormally, + ); + }, + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/56702 + skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit, + ); + + test( + 'loads Blehm font from buffer', + () async { + expect(_containsFontFamily('Blehm'), isFalse); + + final ByteBuffer response = await httpFetchByteBuffer(testFontUrl); + await ui.loadFontFromList(response.asUint8List(), fontFamily: 'Blehm'); + + expect(_containsFontFamily('Blehm'), isTrue); + }, + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/56702 + skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit, + ); + + test( + 'loading font should clear measurement caches', + () async { + final EngineParagraphStyle style = EngineParagraphStyle(); + const ui.ParagraphConstraints constraints = ui.ParagraphConstraints(width: 30.0); + + final CanvasParagraphBuilder canvasBuilder = CanvasParagraphBuilder(style); + canvasBuilder.addText('test'); + // Triggers the measuring and verifies the ruler cache has been populated. + canvasBuilder.build().layout(constraints); + expect(Spanometer.rulers.length, 1); + + // Now, loads a new font using loadFontFromList. This should clear the + // cache + final ByteBuffer response = await httpFetchByteBuffer(testFontUrl); + await ui.loadFontFromList(response.asUint8List(), fontFamily: 'Blehm'); + + // Verifies the font is loaded, and the cache is cleaned. + expect(_containsFontFamily('Blehm'), isTrue); + expect(Spanometer.rulers.length, 0); + }, + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/56702 + skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit, + ); + + test( + 'loading font should send font change message', + () async { + final ui.PlatformMessageCallback? oldHandler = + ui.PlatformDispatcher.instance.onPlatformMessage; + String? actualName; + String? message; + ui.PlatformDispatcher.instance.onPlatformMessage = ( + String name, + ByteData? data, + ui.PlatformMessageResponseCallback? callback, + ) { + actualName = name; + final ByteBuffer buffer = data!.buffer; + final Uint8List list = buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); + message = utf8.decode(list); + }; + final ByteBuffer response = await httpFetchByteBuffer(testFontUrl); + await ui.loadFontFromList(response.asUint8List(), fontFamily: 'Blehm'); + final Completer completer = Completer(); + domWindow.requestAnimationFrame((_) { + completer.complete(); + }); + await completer.future; + ui.PlatformDispatcher.instance.onPlatformMessage = oldHandler; + expect(actualName, 'flutter/system'); + expect(message, '{"type":"fontsChange"}'); + }, + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/56702 + skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.webkit, + ); }); } bool _containsFontFamily(String family) { bool found = false; - domDocument.fonts!.forEach((DomFontFace fontFace, - DomFontFace fontFaceAgain, DomFontFaceSet fontFaceSet) { + domDocument.fonts!.forEach(( + DomFontFace fontFace, + DomFontFace fontFaceAgain, + DomFontFaceSet fontFaceSet, + ) { if (fontFace.family == family) { found = true; } diff --git a/lib/web_ui/test/html/text/layout_fragmenter_test.dart b/lib/web_ui/test/html/text/layout_fragmenter_test.dart index 056c52c859c81..c991c91bb0885 100644 --- a/lib/web_ui/test/html/text/layout_fragmenter_test.dart +++ b/lib/web_ui/test/html/text/layout_fragmenter_test.dart @@ -32,31 +32,29 @@ Future testMain() async { _Fragment('', endOfText, null, ffPrevious, defaultStyle), ]); - final CanvasParagraph paragraph2 = rich( - EngineParagraphStyle(), - (CanvasParagraphBuilder builder) { - builder.addText(''); - }, - ); + final CanvasParagraph paragraph2 = rich(EngineParagraphStyle(), ( + CanvasParagraphBuilder builder, + ) { + builder.addText(''); + }); expect(split(paragraph2), <_Fragment>[ _Fragment('', endOfText, null, ffPrevious, defaultStyle), ]); - final CanvasParagraph paragraph3 = rich( - EngineParagraphStyle(), - (CanvasParagraphBuilder builder) { - builder.pushStyle(style1); - builder.addText(''); - }, - ); - expect(split(paragraph3), <_Fragment>[ - _Fragment('', endOfText, null, ffPrevious, style1), - ]); + final CanvasParagraph paragraph3 = rich(EngineParagraphStyle(), ( + CanvasParagraphBuilder builder, + ) { + builder.pushStyle(style1); + builder.addText(''); + }); + expect(split(paragraph3), <_Fragment>[_Fragment('', endOfText, null, ffPrevious, style1)]); }); test('single span', () { - final CanvasParagraph paragraph = - plain(EngineParagraphStyle(), 'Lorem 12 $rtlWord1 ipsum34'); + final CanvasParagraph paragraph = plain( + EngineParagraphStyle(), + 'Lorem 12 $rtlWord1 ipsum34', + ); expect(split(paragraph), <_Fragment>[ _Fragment('Lorem', prohibited, ltr, ffLtr, defaultStyle), _Fragment(' ', opportunity, null, ffSandwich, defaultStyle, sp: 1), @@ -69,20 +67,19 @@ Future testMain() async { }); test('multi span', () { - final CanvasParagraph paragraph = rich( - EngineParagraphStyle(), - (CanvasParagraphBuilder builder) { - builder.pushStyle(style1); - builder.addText('Lorem'); - builder.pop(); - builder.pushStyle(style2); - builder.addText(' ipsum 12 '); - builder.pop(); - builder.pushStyle(style3); - builder.addText(' $rtlWord1 foo.'); - builder.pop(); - }, - ); + final CanvasParagraph paragraph = rich(EngineParagraphStyle(), ( + CanvasParagraphBuilder builder, + ) { + builder.pushStyle(style1); + builder.addText('Lorem'); + builder.pop(); + builder.pushStyle(style2); + builder.addText(' ipsum 12 '); + builder.pop(); + builder.pushStyle(style3); + builder.addText(' $rtlWord1 foo.'); + builder.pop(); + }); expect(split(paragraph), <_Fragment>[ _Fragment('Lorem', prohibited, ltr, ffLtr, style1), @@ -100,23 +97,22 @@ Future testMain() async { }); test('new lines', () { - final CanvasParagraph paragraph = rich( - EngineParagraphStyle(), - (CanvasParagraphBuilder builder) { - builder.pushStyle(style1); - builder.addText('Lor\nem \n'); - builder.pop(); - builder.pushStyle(style2); - builder.addText(' \n ipsum 12 '); - builder.pop(); - builder.pushStyle(style3); - builder.addText(' $rtlWord1 fo'); - builder.pop(); - builder.pushStyle(style1); - builder.addText('o.'); - builder.pop(); - }, - ); + final CanvasParagraph paragraph = rich(EngineParagraphStyle(), ( + CanvasParagraphBuilder builder, + ) { + builder.pushStyle(style1); + builder.addText('Lor\nem \n'); + builder.pop(); + builder.pushStyle(style2); + builder.addText(' \n ipsum 12 '); + builder.pop(); + builder.pushStyle(style3); + builder.addText(' $rtlWord1 fo'); + builder.pop(); + builder.pushStyle(style1); + builder.addText('o.'); + builder.pop(); + }); expect(split(paragraph), <_Fragment>[ _Fragment('Lor', prohibited, ltr, ffLtr, style1), @@ -139,17 +135,16 @@ Future testMain() async { }); test('last line is empty', () { - final CanvasParagraph paragraph = rich( - EngineParagraphStyle(), - (CanvasParagraphBuilder builder) { - builder.pushStyle(style1); - builder.addText('Lorem \n'); - builder.pop(); - builder.pushStyle(style2); - builder.addText(' \n ipsum \n'); - builder.pop(); - }, - ); + final CanvasParagraph paragraph = rich(EngineParagraphStyle(), ( + CanvasParagraphBuilder builder, + ) { + builder.pushStyle(style1); + builder.addText('Lorem \n'); + builder.pop(); + builder.pushStyle(style2); + builder.addText(' \n ipsum \n'); + builder.pop(); + }); expect(split(paragraph), <_Fragment>[ _Fragment('Lorem', prohibited, ltr, ffLtr, style1), @@ -163,19 +158,18 @@ Future testMain() async { }); test('space-only spans', () { - final CanvasParagraph paragraph = rich( - EngineParagraphStyle(), - (CanvasParagraphBuilder builder) { - builder.addText('Lorem '); - builder.pushStyle(style1); - builder.addText(' '); - builder.pop(); - builder.pushStyle(style2); - builder.addText(' '); - builder.pop(); - builder.addText('ipsum'); - }, - ); + final CanvasParagraph paragraph = rich(EngineParagraphStyle(), ( + CanvasParagraphBuilder builder, + ) { + builder.addText('Lorem '); + builder.pushStyle(style1); + builder.addText(' '); + builder.pop(); + builder.pushStyle(style2); + builder.addText(' '); + builder.pop(); + builder.addText('ipsum'); + }); expect(split(paragraph), <_Fragment>[ _Fragment('Lorem', prohibited, ltr, ffLtr, defaultStyle), @@ -187,24 +181,23 @@ Future testMain() async { }); test('placeholders', () { - final CanvasParagraph paragraph = rich( - EngineParagraphStyle(), - (CanvasParagraphBuilder builder) { - builder.pushStyle(style1); - builder.addPlaceholder(100, 100, PlaceholderAlignment.top); - builder.addText('Lorem'); - builder.addPlaceholder(100, 100, PlaceholderAlignment.top); - builder.addText('ipsum\n'); - builder.addPlaceholder(100, 100, PlaceholderAlignment.top); - builder.pop(); - builder.pushStyle(style2); - builder.addText('$rtlWord1 '); - builder.addPlaceholder(100, 100, PlaceholderAlignment.top); - builder.addText('\nsit'); - builder.pop(); - builder.addPlaceholder(100, 100, PlaceholderAlignment.top); - }, - ); + final CanvasParagraph paragraph = rich(EngineParagraphStyle(), ( + CanvasParagraphBuilder builder, + ) { + builder.pushStyle(style1); + builder.addPlaceholder(100, 100, PlaceholderAlignment.top); + builder.addText('Lorem'); + builder.addPlaceholder(100, 100, PlaceholderAlignment.top); + builder.addText('ipsum\n'); + builder.addPlaceholder(100, 100, PlaceholderAlignment.top); + builder.pop(); + builder.pushStyle(style2); + builder.addText('$rtlWord1 '); + builder.addPlaceholder(100, 100, PlaceholderAlignment.top); + builder.addText('\nsit'); + builder.pop(); + builder.addPlaceholder(100, 100, PlaceholderAlignment.top); + }); expect(split(paragraph), <_Fragment>[ _Fragment(placeholderChar, opportunity, ltr, ffLtr, style1), @@ -226,7 +219,12 @@ Future testMain() async { /// Holds information about how a fragment. class _Fragment { - _Fragment(this.text, this.type, this.textDirection, this.fragmentFlow, this.style, { + _Fragment( + this.text, + this.type, + this.textDirection, + this.fragmentFlow, + this.style, { this.nl = 0, this.sp = 0, }); @@ -278,9 +276,8 @@ class _Fragment { List<_Fragment> split(CanvasParagraph paragraph) { return <_Fragment>[ - for (final LayoutFragment layoutFragment - in computeLayoutFragments(paragraph)) - _Fragment._fromLayoutFragment(paragraph.plainText, layoutFragment) + for (final LayoutFragment layoutFragment in computeLayoutFragments(paragraph)) + _Fragment._fromLayoutFragment(paragraph.plainText, layoutFragment), ]; } diff --git a/lib/web_ui/test/html/text/layout_service_helper.dart b/lib/web_ui/test/html/text/layout_service_helper.dart index 545001c3a0312..804d82ef6dab9 100644 --- a/lib/web_ui/test/html/text/layout_service_helper.dart +++ b/lib/web_ui/test/html/text/layout_service_helper.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - import 'package:test/test.dart'; import 'package:ui/src/engine.dart'; @@ -103,15 +102,15 @@ void expectLines(CanvasParagraph paragraph, List expectedLines) { expect( line.left, expectedLine.left, - reason: - 'line #$i had a different `left` value: "${line.left}" vs. "${expectedLine.left}"', + reason: 'line #$i had a different `left` value: "${line.left}" vs. "${expectedLine.left}"', ); } if (expectedLine.baseline != null) { expect( line.baseline, expectedLine.baseline, - reason: 'line #$i had a different `baseline` value: "${line.baseline}" vs. "${expectedLine.baseline}"', + reason: + 'line #$i had a different `baseline` value: "${line.baseline}" vs. "${expectedLine.baseline}"', ); } } diff --git a/lib/web_ui/test/html/text/layout_service_plain_test.dart b/lib/web_ui/test/html/text/layout_service_plain_test.dart index 870f2163bab24..c0333c2f6e125 100644 --- a/lib/web_ui/test/html/text/layout_service_plain_test.dart +++ b/lib/web_ui/test/html/text/layout_service_plain_test.dart @@ -28,9 +28,7 @@ Future testMain() async { expect(paragraph.maxIntrinsicWidth, 0); expect(paragraph.minIntrinsicWidth, 0); expect(paragraph.height, 10); - expectLines(paragraph, [ - l('', 0, 0, width: 0.0, height: 10.0, baseline: 8.0), - ]); + expectLines(paragraph, [l('', 0, 0, width: 0.0, height: 10.0, baseline: 8.0)]); }); test('preserves whitespace when measuring', () { @@ -41,22 +39,17 @@ Future testMain() async { expect(paragraph.maxIntrinsicWidth, 60); expect(paragraph.minIntrinsicWidth, 30); expect(paragraph.height, 10); - expectLines(paragraph, [ - l(' abc', 0, 6, hardBreak: true, width: 60.0), - ]); + expectLines(paragraph, [l(' abc', 0, 6, hardBreak: true, width: 60.0)]); // trailing whitespaces paragraph = plain(ahemStyle, 'abc ')..layout(constrain(double.infinity)); expect(paragraph.maxIntrinsicWidth, 60); expect(paragraph.minIntrinsicWidth, 30); expect(paragraph.height, 10); - expectLines(paragraph, [ - l('abc ', 0, 6, hardBreak: true, width: 30.0), - ]); + expectLines(paragraph, [l('abc ', 0, 6, hardBreak: true, width: 30.0)]); // mixed whitespaces - paragraph = plain(ahemStyle, ' ab c ') - ..layout(constrain(double.infinity)); + paragraph = plain(ahemStyle, ' ab c ')..layout(constrain(double.infinity)); expect(paragraph.maxIntrinsicWidth, 100); expect(paragraph.minIntrinsicWidth, 20); expect(paragraph.height, 10); @@ -69,23 +62,18 @@ Future testMain() async { expect(paragraph.maxIntrinsicWidth, 10); expect(paragraph.minIntrinsicWidth, 0); expect(paragraph.height, 10); - expectLines(paragraph, [ - l(' ', 0, 1, hardBreak: true, width: 0.0, left: 0.0), - ]); + expectLines(paragraph, [l(' ', 0, 1, hardBreak: true, width: 0.0, left: 0.0)]); // whitespace only paragraph = plain(ahemStyle, ' ')..layout(constrain(double.infinity)); expect(paragraph.maxIntrinsicWidth, 50); expect(paragraph.minIntrinsicWidth, 0); expect(paragraph.height, 10); - expectLines(paragraph, [ - l(' ', 0, 5, hardBreak: true, width: 0.0, left: 0.0), - ]); + expectLines(paragraph, [l(' ', 0, 5, hardBreak: true, width: 0.0, left: 0.0)]); }); test('uses single-line when text can fit without wrapping', () { - final CanvasParagraph paragraph = plain(ahemStyle, '12345') - ..layout(constrain(50.0)); + final CanvasParagraph paragraph = plain(ahemStyle, '12345')..layout(constrain(50.0)); // Should fit on a single line. expect(paragraph.alphabeticBaseline, 8); @@ -99,8 +87,7 @@ Future testMain() async { }); test('simple multi-line text', () { - final CanvasParagraph paragraph = plain(ahemStyle, 'foo bar baz') - ..layout(constrain(70.0)); + final CanvasParagraph paragraph = plain(ahemStyle, 'foo bar baz')..layout(constrain(70.0)); expect(paragraph.alphabeticBaseline, 8); expect(paragraph.maxIntrinsicWidth, 110); expect(paragraph.minIntrinsicWidth, 30); @@ -194,8 +181,7 @@ Future testMain() async { }); test('uses multi-line for text that contains new-line', () { - final CanvasParagraph paragraph = plain(ahemStyle, '12\n34') - ..layout(constrain(50.0)); + final CanvasParagraph paragraph = plain(ahemStyle, '12\n34')..layout(constrain(50.0)); // Text containing newlines should always be drawn in multi-line mode. expect(paragraph.maxIntrinsicWidth, 20); @@ -245,45 +231,38 @@ Future testMain() async { ]); }); - test( - 'wraps multi-line text correctly when constraint width is infinite', - () { - final CanvasParagraph paragraph = plain(ahemStyle, '123\n456 789') - ..layout(constrain(double.infinity)); - - expect(paragraph.maxIntrinsicWidth, 70); - expect(paragraph.minIntrinsicWidth, 30); - expect(paragraph.width, double.infinity); - expect(paragraph.height, 20); - expectLines(paragraph, [ - l('123', 0, 4, hardBreak: true, width: 30.0, left: 0.0), - l('456 789', 4, 11, hardBreak: true, width: 70.0, left: 0.0), - ]); - }, - ); + test('wraps multi-line text correctly when constraint width is infinite', () { + final CanvasParagraph paragraph = plain(ahemStyle, '123\n456 789') + ..layout(constrain(double.infinity)); + + expect(paragraph.maxIntrinsicWidth, 70); + expect(paragraph.minIntrinsicWidth, 30); + expect(paragraph.width, double.infinity); + expect(paragraph.height, 20); + expectLines(paragraph, [ + l('123', 0, 4, hardBreak: true, width: 30.0, left: 0.0), + l('456 789', 4, 11, hardBreak: true, width: 70.0, left: 0.0), + ]); + }); test('takes letter spacing into account', () { - final EngineTextStyle spacedTextStyle = - EngineTextStyle.only(letterSpacing: 3); - final CanvasParagraph spacedText = - plain(ahemStyle, 'abc', textStyle: spacedTextStyle) - ..layout(constrain(100.0)); + final EngineTextStyle spacedTextStyle = EngineTextStyle.only(letterSpacing: 3); + final CanvasParagraph spacedText = plain(ahemStyle, 'abc', textStyle: spacedTextStyle) + ..layout(constrain(100.0)); expect(spacedText.minIntrinsicWidth, 39); expect(spacedText.maxIntrinsicWidth, 39); }); test('takes word spacing into account', () { - final CanvasParagraph normalText = plain(ahemStyle, 'a b c') - ..layout(constrain(100.0)); - final CanvasParagraph spacedText = plain(ahemStyle, 'a b c', - textStyle: EngineTextStyle.only(wordSpacing: 1.5)) - ..layout(constrain(100.0)); - - expect( - normalText.maxIntrinsicWidth < spacedText.maxIntrinsicWidth, - isTrue, - ); + final CanvasParagraph normalText = plain(ahemStyle, 'a b c')..layout(constrain(100.0)); + final CanvasParagraph spacedText = plain( + ahemStyle, + 'a b c', + textStyle: EngineTextStyle.only(wordSpacing: 1.5), + )..layout(constrain(100.0)); + + expect(normalText.maxIntrinsicWidth < spacedText.maxIntrinsicWidth, isTrue); }, skip: skipWordSpacing); test('minIntrinsicWidth', () { @@ -404,9 +383,7 @@ Future testMain() async { expect(longText.minIntrinsicWidth, 480); expect(longText.maxIntrinsicWidth, 480); expect(longText.height, 10); - expectLines(longText, [ - l('AA...', 0, 2, hardBreak: true, width: 50.0, left: 0.0), - ]); + expectLines(longText, [l('AA...', 0, 2, hardBreak: true, width: 50.0, left: 0.0)]); // The short prefix should make the text break into two lines, but the // second line should remain unbroken. @@ -424,8 +401,7 @@ Future testMain() async { // Constraints only enough to fit "AA" with the ellipsis, but not the // trailing white space. - final CanvasParagraph trailingSpace = plain(overflowStyle, 'AA AAA') - ..layout(constrain(50.0)); + final CanvasParagraph trailingSpace = plain(overflowStyle, 'AA AAA')..layout(constrain(50.0)); expect(trailingSpace.minIntrinsicWidth, 30); expect(trailingSpace.maxIntrinsicWidth, 60); expect(trailingSpace.height, 10); @@ -434,14 +410,11 @@ Future testMain() async { ]); // Tiny constraints. - final CanvasParagraph paragraph = plain(overflowStyle, 'AAAA') - ..layout(constrain(30.0)); + final CanvasParagraph paragraph = plain(overflowStyle, 'AAAA')..layout(constrain(30.0)); expect(paragraph.minIntrinsicWidth, 40); expect(paragraph.maxIntrinsicWidth, 40); expect(paragraph.height, 10); - expectLines(paragraph, [ - l('...', 0, 0, hardBreak: true, width: 30.0, left: 0.0), - ]); + expectLines(paragraph, [l('...', 0, 0, hardBreak: true, width: 30.0, left: 0.0)]); // Tinier constraints (not enough for the ellipsis). paragraph.layout(constrain(10.0)); @@ -453,9 +426,7 @@ Future testMain() async { // expectLines(paragraph, [ // l('.', 0, 0, hardBreak: false, width: 10.0, left: 0.0), // ]); - expectLines(paragraph, [ - l('...', 0, 0, hardBreak: true, width: 30.0, left: 0.0), - ]); + expectLines(paragraph, [l('...', 0, 0, hardBreak: true, width: 30.0, left: 0.0)]); }); test('respects max lines', () { @@ -469,14 +440,11 @@ Future testMain() async { final CanvasParagraph oneline = plain(maxlinesStyle, 'One line') ..layout(constrain(double.infinity)); expect(oneline.height, 10); - expectLines(oneline, [ - l('One line', 0, 8, hardBreak: true, width: 80.0, left: 0.0), - ]); + expectLines(oneline, [l('One line', 0, 8, hardBreak: true, width: 80.0, left: 0.0)]); // The height should respect max lines and be limited to two lines here. - final CanvasParagraph threelines = - plain(maxlinesStyle, 'First\nSecond\nThird') - ..layout(constrain(double.infinity)); + final CanvasParagraph threelines = plain(maxlinesStyle, 'First\nSecond\nThird') + ..layout(constrain(double.infinity)); expect(threelines.height, 20); expectLines(threelines, [ l('First', 0, 6, hardBreak: true, width: 50.0, left: 0.0), @@ -495,10 +463,8 @@ Future testMain() async { ]); // Case when last line is a long unbreakable word. - final CanvasParagraph veryLongLastLine = plain( - maxlinesStyle, - 'AAA AAAAAAAAAAAAAAAAAAA', - )..layout(constrain(50.0)); + final CanvasParagraph veryLongLastLine = plain(maxlinesStyle, 'AAA AAAAAAAAAAAAAAAAAAA') + ..layout(constrain(50.0)); expect(veryLongLastLine.height, 20); expectLines(veryLongLastLine, [ l('AAA ', 0, 4, hardBreak: false, width: 30.0, left: 0.0), @@ -525,23 +491,17 @@ Future testMain() async { // Simple no overflow case. paragraph = plain(onelineStyle, 'abcdef')..layout(constrain(60.0)); expect(paragraph.height, 10); - expectLines(paragraph, [ - l('abcdef', 0, 6, hardBreak: true, width: 60.0, left: 0.0), - ]); + expectLines(paragraph, [l('abcdef', 0, 6, hardBreak: true, width: 60.0, left: 0.0)]); // Simple overflow case. paragraph = plain(onelineStyle, 'abcd efg')..layout(constrain(60.0)); expect(paragraph.height, 10); - expectLines(paragraph, [ - l('abc...', 0, 3, hardBreak: true, width: 60.0, left: 0.0), - ]); + expectLines(paragraph, [l('abc...', 0, 3, hardBreak: true, width: 60.0, left: 0.0)]); // Another simple overflow case. paragraph = plain(onelineStyle, 'a bcde fgh')..layout(constrain(60.0)); expect(paragraph.height, 10); - expectLines(paragraph, [ - l('a b...', 0, 3, hardBreak: true, width: 60.0, left: 0.0), - ]); + expectLines(paragraph, [l('a b...', 0, 3, hardBreak: true, width: 60.0, left: 0.0)]); // The ellipsis is supposed to go on the second line, but because the // 2nd line doesn't overflow, no ellipsis is shown. @@ -562,8 +522,7 @@ Future testMain() async { // Even if the second line can be broken, we don't break it, we just // insert the ellipsis. - paragraph = plain(multilineStyle, 'abcde f gh ijk') - ..layout(constrain(60.0)); + paragraph = plain(multilineStyle, 'abcde f gh ijk')..layout(constrain(60.0)); expect(paragraph.height, 20); expectLines(paragraph, [ l('abcde ', 0, 6, hardBreak: false, width: 50.0, left: 0.0), @@ -579,8 +538,7 @@ Future testMain() async { ]); // Both first and second lines overflow. - paragraph = plain(multilineStyle, 'abcdefg hijklmnop') - ..layout(constrain(60.0)); + paragraph = plain(multilineStyle, 'abcdefg hijklmnop')..layout(constrain(60.0)); expect(paragraph.height, 20); expectLines(paragraph, [ l('abcdef', 0, 6, hardBreak: false, width: 60.0, left: 0.0), @@ -600,40 +558,35 @@ Future testMain() async { ); } - paragraph = plain(createStyle(ui.TextAlign.start), 'abc\ndefghi') - ..layout(constrain(50.0)); + paragraph = plain(createStyle(ui.TextAlign.start), 'abc\ndefghi')..layout(constrain(50.0)); expectLines(paragraph, [ l('abc', 0, 4, hardBreak: true, width: 30.0, left: 0.0), l('defgh', 4, 9, hardBreak: false, width: 50.0, left: 0.0), l('i', 9, 10, hardBreak: true, width: 10.0, left: 0.0), ]); - paragraph = plain(createStyle(ui.TextAlign.end), 'abc\ndefghi') - ..layout(constrain(50.0)); + paragraph = plain(createStyle(ui.TextAlign.end), 'abc\ndefghi')..layout(constrain(50.0)); expectLines(paragraph, [ l('abc', 0, 4, hardBreak: true, width: 30.0, left: 20.0), l('defgh', 4, 9, hardBreak: false, width: 50.0, left: 0.0), l('i', 9, 10, hardBreak: true, width: 10.0, left: 40.0), ]); - paragraph = plain(createStyle(ui.TextAlign.center), 'abc\ndefghi') - ..layout(constrain(50.0)); + paragraph = plain(createStyle(ui.TextAlign.center), 'abc\ndefghi')..layout(constrain(50.0)); expectLines(paragraph, [ l('abc', 0, 4, hardBreak: true, width: 30.0, left: 10.0), l('defgh', 4, 9, hardBreak: false, width: 50.0, left: 0.0), l('i', 9, 10, hardBreak: true, width: 10.0, left: 20.0), ]); - paragraph = plain(createStyle(ui.TextAlign.left), 'abc\ndefghi') - ..layout(constrain(50.0)); + paragraph = plain(createStyle(ui.TextAlign.left), 'abc\ndefghi')..layout(constrain(50.0)); expectLines(paragraph, [ l('abc', 0, 4, hardBreak: true, width: 30.0, left: 0.0), l('defgh', 4, 9, hardBreak: false, width: 50.0, left: 0.0), l('i', 9, 10, hardBreak: true, width: 10.0, left: 0.0), ]); - paragraph = plain(createStyle(ui.TextAlign.right), 'abc\ndefghi') - ..layout(constrain(50.0)); + paragraph = plain(createStyle(ui.TextAlign.right), 'abc\ndefghi')..layout(constrain(50.0)); expectLines(paragraph, [ l('abc', 0, 4, hardBreak: true, width: 30.0, left: 20.0), l('defgh', 4, 9, hardBreak: false, width: 50.0, left: 0.0), @@ -653,40 +606,35 @@ Future testMain() async { ); } - paragraph = plain(createStyle(ui.TextAlign.start), 'abc\ndefghi') - ..layout(constrain(50.0)); + paragraph = plain(createStyle(ui.TextAlign.start), 'abc\ndefghi')..layout(constrain(50.0)); expectLines(paragraph, [ l('abc', 0, 4, hardBreak: true, width: 30.0, left: 20.0), l('defgh', 4, 9, hardBreak: false, width: 50.0, left: 0.0), l('i', 9, 10, hardBreak: true, width: 10.0, left: 40.0), ]); - paragraph = plain(createStyle(ui.TextAlign.end), 'abc\ndefghi') - ..layout(constrain(50.0)); + paragraph = plain(createStyle(ui.TextAlign.end), 'abc\ndefghi')..layout(constrain(50.0)); expectLines(paragraph, [ l('abc', 0, 4, hardBreak: true, width: 30.0, left: 0.0), l('defgh', 4, 9, hardBreak: false, width: 50.0, left: 0.0), l('i', 9, 10, hardBreak: true, width: 10.0, left: 0.0), ]); - paragraph = plain(createStyle(ui.TextAlign.center), 'abc\ndefghi') - ..layout(constrain(50.0)); + paragraph = plain(createStyle(ui.TextAlign.center), 'abc\ndefghi')..layout(constrain(50.0)); expectLines(paragraph, [ l('abc', 0, 4, hardBreak: true, width: 30.0, left: 10.0), l('defgh', 4, 9, hardBreak: false, width: 50.0, left: 0.0), l('i', 9, 10, hardBreak: true, width: 10.0, left: 20.0), ]); - paragraph = plain(createStyle(ui.TextAlign.left), 'abc\ndefghi') - ..layout(constrain(50.0)); + paragraph = plain(createStyle(ui.TextAlign.left), 'abc\ndefghi')..layout(constrain(50.0)); expectLines(paragraph, [ l('abc', 0, 4, hardBreak: true, width: 30.0, left: 0.0), l('defgh', 4, 9, hardBreak: false, width: 50.0, left: 0.0), l('i', 9, 10, hardBreak: true, width: 10.0, left: 0.0), ]); - paragraph = plain(createStyle(ui.TextAlign.right), 'abc\ndefghi') - ..layout(constrain(50.0)); + paragraph = plain(createStyle(ui.TextAlign.right), 'abc\ndefghi')..layout(constrain(50.0)); expectLines(paragraph, [ l('abc', 0, 4, hardBreak: true, width: 30.0, left: 20.0), l('defgh', 4, 9, hardBreak: false, width: 50.0, left: 0.0), @@ -700,7 +648,10 @@ Future testMain() async { plain(ahemStyle, 'Lorem').layout(constrain(double.infinity)); plain(ahemStyle, 'ipsum dolor').layout(constrain(150.0)); // Try different styles too. - plain(EngineParagraphStyle(fontWeight: ui.FontWeight.bold), 'sit amet').layout(constrain(300.0)); + plain( + EngineParagraphStyle(fontWeight: ui.FontWeight.bold), + 'sit amet', + ).layout(constrain(300.0)); expect(textContext.canvas!.width, isZero); expect(textContext.canvas!.height, isZero); @@ -718,10 +669,7 @@ Future testMain() async { ui_web.debugEmulateFlutterTesterEnvironment = false; final CanvasParagraph p1 = plain( - EngineParagraphStyle( - fontSize: 20.0, - fontFamily: 'FontFamily1', - ), + EngineParagraphStyle(fontSize: 20.0, fontFamily: 'FontFamily1'), 'Lorem', )..layout(constrain(double.infinity)); // After the layout, the canvas should have the above style applied. @@ -729,10 +677,7 @@ Future testMain() async { expect(textContext.font, contains('FontFamily1')); final CanvasParagraph p2 = plain( - EngineParagraphStyle( - fontSize: 40.0, - fontFamily: 'FontFamily2', - ), + EngineParagraphStyle(fontSize: 40.0, fontFamily: 'FontFamily2'), 'ipsum dolor', )..layout(constrain(double.infinity)); // After the layout, the canvas should have the above style applied. diff --git a/lib/web_ui/test/html/text/layout_service_rich_test.dart b/lib/web_ui/test/html/text/layout_service_rich_test.dart index 01f358f22632d..5f8247a3ad05e 100644 --- a/lib/web_ui/test/html/text/layout_service_rich_test.dart +++ b/lib/web_ui/test/html/text/layout_service_rich_test.dart @@ -2,7 +2,6 @@ // 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'; @@ -113,7 +112,15 @@ Future testMain() async { expect(paragraph.minIntrinsicWidth, 50.0); // "Lorem" or "ipsum" expect(paragraph.width, 80.0); expectLines(paragraph, [ - l('Lorem ', 0, 11, hardBreak: false, width: 50.0, widthWithTrailingSpaces: 110.0, left: 0.0), + l( + 'Lorem ', + 0, + 11, + hardBreak: false, + width: 50.0, + widthWithTrailingSpaces: 110.0, + left: 0.0, + ), l('ipsum', 11, 16, hardBreak: true, width: 50.0, left: 0.0), ]); }); @@ -149,7 +156,12 @@ Future testMain() async { textAlign: ui.TextAlign.center, ); final CanvasParagraph paragraph = rich(paragraphStyle, (CanvasParagraphBuilder builder) { - builder.addPlaceholder(300.0, 50.0, ui.PlaceholderAlignment.baseline, baseline: ui.TextBaseline.alphabetic); + builder.addPlaceholder( + 300.0, + 50.0, + ui.PlaceholderAlignment.baseline, + baseline: ui.TextBaseline.alphabetic, + ); })..layout(constrain(500.0)); expect(paragraph.maxIntrinsicWidth, 300.0); diff --git a/lib/web_ui/test/html/text/line_breaker_test.dart b/lib/web_ui/test/html/text/line_breaker_test.dart index c59063f967e1f..d2430e3af08b4 100644 --- a/lib/web_ui/test/html/text/line_breaker_test.dart +++ b/lib/web_ui/test/html/text/line_breaker_test.dart @@ -23,21 +23,16 @@ void testMain() { isV8 ? V8LineBreakFragmenter(text) : FWLineBreakFragmenter(text); return [ for (final LineBreakFragment fragment in fragmenter.fragment()) - Line.fromLineBreakFragment(text, fragment) + Line.fromLineBreakFragment(text, fragment), ]; } test('empty string', () { - expect(split(''), [ - Line('', endOfText), - ]); + expect(split(''), [Line('', endOfText)]); }); test('whitespace', () { - expect(split('foo bar'), [ - Line('foo ', opportunity, sp: 1), - Line('bar', endOfText), - ]); + expect(split('foo bar'), [Line('foo ', opportunity, sp: 1), Line('bar', endOfText)]); expect(split(' foo bar '), [ Line(' ', opportunity, sp: 2), Line('foo ', opportunity, sp: 4), @@ -117,31 +112,13 @@ void testMain() { // New lines at the beginning and end. - expect(split('foo\n'), [ - Line('foo\n', mandatory, nl: 1, sp: 1), - Line('', endOfText), - ]); - expect(split('foo\r'), [ - Line('foo\r', mandatory, nl: 1, sp: 1), - Line('', endOfText), - ]); - expect(split('foo$bk'), [ - Line('foo$bk', mandatory, nl: 1, sp: 1), - Line('', endOfText), - ]); + expect(split('foo\n'), [Line('foo\n', mandatory, nl: 1, sp: 1), Line('', endOfText)]); + expect(split('foo\r'), [Line('foo\r', mandatory, nl: 1, sp: 1), Line('', endOfText)]); + expect(split('foo$bk'), [Line('foo$bk', mandatory, nl: 1, sp: 1), Line('', endOfText)]); - expect(split('\nfoo'), [ - Line('\n', mandatory, nl: 1, sp: 1), - Line('foo', endOfText), - ]); - expect(split('\rfoo'), [ - Line('\r', mandatory, nl: 1, sp: 1), - Line('foo', endOfText), - ]); - expect(split('${bk}foo'), [ - Line(bk, mandatory, nl: 1, sp: 1), - Line('foo', endOfText), - ]); + expect(split('\nfoo'), [Line('\n', mandatory, nl: 1, sp: 1), Line('foo', endOfText)]); + expect(split('\rfoo'), [Line('\r', mandatory, nl: 1, sp: 1), Line('foo', endOfText)]); + expect(split('${bk}foo'), [Line(bk, mandatory, nl: 1, sp: 1), Line('foo', endOfText)]); // Whitespace with new lines. @@ -175,72 +152,58 @@ void testMain() { test('trailing spaces and new lines', () { expect(split('foo bar '), [ - Line('foo ', opportunity, sp: 1), - Line('bar ', endOfText, sp: 2), - ], - ); + Line('foo ', opportunity, sp: 1), + Line('bar ', endOfText, sp: 2), + ]); expect(split('foo \nbar\nbaz \n'), [ - Line('foo \n', mandatory, nl: 1, sp: 3), - Line('bar\n', mandatory, nl: 1, sp: 1), - Line('baz \n', mandatory, nl: 1, sp: 4), - Line('', endOfText), - ], - ); + Line('foo \n', mandatory, nl: 1, sp: 3), + Line('bar\n', mandatory, nl: 1, sp: 1), + Line('baz \n', mandatory, nl: 1, sp: 4), + Line('', endOfText), + ]); }); test('leading spaces', () { - expect(split(' foo'), [ - Line(' ', opportunity, sp: 1), - Line('foo', endOfText), - ], - ); - - expect(split(' foo'), [ - Line(' ', opportunity, sp: 3), - Line('foo', endOfText), - ], - ); + expect(split(' foo'), [Line(' ', opportunity, sp: 1), Line('foo', endOfText)]); + + expect(split(' foo'), [Line(' ', opportunity, sp: 3), Line('foo', endOfText)]); expect(split(' foo bar'), [ - Line(' ', opportunity, sp: 2), - Line('foo ', opportunity, sp: 3), - Line('bar', endOfText), - ], - ); + Line(' ', opportunity, sp: 2), + Line('foo ', opportunity, sp: 3), + Line('bar', endOfText), + ]); expect(split(' \n foo'), [ - Line(' \n', mandatory, nl: 1, sp: 3), - Line(' ', opportunity, sp: 3), - Line('foo', endOfText), - ], - ); + Line(' \n', mandatory, nl: 1, sp: 3), + Line(' ', opportunity, sp: 3), + Line('foo', endOfText), + ]); }); test('whitespace before the last character', () { expect(split('Lorem sit .'), [ - Line('Lorem ', opportunity, sp: 1), - Line('sit ', opportunity, sp: 1), - Line('.', endOfText), - ], - ); + Line('Lorem ', opportunity, sp: 1), + Line('sit ', opportunity, sp: 1), + Line('.', endOfText), + ]); }); test('placeholders', () { - final CanvasParagraph paragraph = rich( - EngineParagraphStyle(), - (CanvasParagraphBuilder builder) { - builder.addPlaceholder(100, 100, PlaceholderAlignment.top); - builder.addText('Lorem'); - builder.addPlaceholder(100, 100, PlaceholderAlignment.top); - builder.addText('ipsum\n'); - builder.addPlaceholder(100, 100, PlaceholderAlignment.top); - builder.addText('dolor'); - builder.addPlaceholder(100, 100, PlaceholderAlignment.top); - builder.addText('\nsit'); - builder.addPlaceholder(100, 100, PlaceholderAlignment.top); - }, - ); + final CanvasParagraph paragraph = rich(EngineParagraphStyle(), ( + CanvasParagraphBuilder builder, + ) { + builder.addPlaceholder(100, 100, PlaceholderAlignment.top); + builder.addText('Lorem'); + builder.addPlaceholder(100, 100, PlaceholderAlignment.top); + builder.addText('ipsum\n'); + builder.addPlaceholder(100, 100, PlaceholderAlignment.top); + builder.addText('dolor'); + builder.addPlaceholder(100, 100, PlaceholderAlignment.top); + builder.addText('\nsit'); + builder.addPlaceholder(100, 100, PlaceholderAlignment.top); + }); final String placeholderChar = String.fromCharCode(0xFFFC); @@ -258,70 +221,55 @@ void testMain() { }); test('single placeholder', () { - final CanvasParagraph paragraph = rich( - EngineParagraphStyle(), - (CanvasParagraphBuilder builder) { - builder.addPlaceholder(100, 100, PlaceholderAlignment.top); - }, - ); + final CanvasParagraph paragraph = rich(EngineParagraphStyle(), ( + CanvasParagraphBuilder builder, + ) { + builder.addPlaceholder(100, 100, PlaceholderAlignment.top); + }); final String placeholderChar = String.fromCharCode(0xFFFC); - expect(split(paragraph.plainText), [ - Line(placeholderChar, endOfText), - ]); + expect(split(paragraph.plainText), [Line(placeholderChar, endOfText)]); }); test('placeholders surrounded by spaces and new lines', () { - final CanvasParagraph paragraph = rich( - EngineParagraphStyle(), - (CanvasParagraphBuilder builder) { - builder.addPlaceholder(100, 100, PlaceholderAlignment.top); - builder.addText(' Lorem '); - builder.addPlaceholder(100, 100, PlaceholderAlignment.top); - builder.addText(' \nipsum \n'); - builder.addPlaceholder(100, 100, PlaceholderAlignment.top); - builder.addText('\n'); - builder.addPlaceholder(100, 100, PlaceholderAlignment.top); - }, - ); + final CanvasParagraph paragraph = rich(EngineParagraphStyle(), ( + CanvasParagraphBuilder builder, + ) { + builder.addPlaceholder(100, 100, PlaceholderAlignment.top); + builder.addText(' Lorem '); + builder.addPlaceholder(100, 100, PlaceholderAlignment.top); + builder.addText(' \nipsum \n'); + builder.addPlaceholder(100, 100, PlaceholderAlignment.top); + builder.addText('\n'); + builder.addPlaceholder(100, 100, PlaceholderAlignment.top); + }); expect(split(paragraph.plainText), [ - Line('$placeholderChar ', opportunity, sp: 2), - Line('Lorem ', opportunity, sp: 2), - Line('$placeholderChar \n', mandatory, nl: 1, sp: 3), - Line('ipsum \n', mandatory, nl: 1, sp: 2), - Line('$placeholderChar\n', mandatory, nl: 1, sp: 1), - Line(placeholderChar, endOfText), - ], - ); + Line('$placeholderChar ', opportunity, sp: 2), + Line('Lorem ', opportunity, sp: 2), + Line('$placeholderChar \n', mandatory, nl: 1, sp: 3), + Line('ipsum \n', mandatory, nl: 1, sp: 2), + Line('$placeholderChar\n', mandatory, nl: 1, sp: 1), + Line(placeholderChar, endOfText), + ]); }); test('surrogates', () { - expect(split('A\u{1F600}'), [ - Line('A', opportunity), - Line('\u{1F600}', endOfText), - ], - ); - - expect(split('\u{1F600}A'), [ - Line('\u{1F600}', opportunity), - Line('A', endOfText), - ], - ); + expect(split('A\u{1F600}'), [Line('A', opportunity), Line('\u{1F600}', endOfText)]); + + expect(split('\u{1F600}A'), [Line('\u{1F600}', opportunity), Line('A', endOfText)]); expect(split('\u{1F600}\u{1F600}'), [ - Line('\u{1F600}', opportunity), - Line('\u{1F600}', endOfText), - ], - ); + Line('\u{1F600}', opportunity), + Line('\u{1F600}', endOfText), + ]); expect(split('A \u{1F600} \u{1F600}'), [ - Line('A ', opportunity, sp: 1), - Line('\u{1F600} ', opportunity, sp: 1), - Line('\u{1F600}', endOfText), - ], - ); + Line('A ', opportunity, sp: 1), + Line('\u{1F600} ', opportunity, sp: 1), + Line('\u{1F600}', endOfText), + ]); }); test('comprehensive test', () { @@ -330,9 +278,8 @@ void testMain() { final TestCase testCase = testCollection[t]; final String text = testCase.toText(); - final LineBreakFragmenter fragmenter = isV8 - ? V8LineBreakFragmenter(text) - : FWLineBreakFragmenter(text); + final LineBreakFragmenter fragmenter = + isV8 ? V8LineBreakFragmenter(text) : FWLineBreakFragmenter(text); final List fragments = fragmenter.fragment(); // `f` is the index in the `fragments` list. @@ -350,7 +297,8 @@ void testMain() { expect( currentFragment.end, i, - reason: 'Failed at test case number $t:\n' + reason: + 'Failed at test case number $t:\n' '$testCase\n' '"$text"\n' '\nExpected fragment to end at {$i} but ended at {${currentFragment.end}}.', @@ -360,7 +308,8 @@ void testMain() { expect( currentFragment.end, greaterThan(i), - reason: 'Failed at test case number $t:\n' + reason: + 'Failed at test case number $t:\n' '$testCase\n' '"$text"\n' '\nFragment ended in early at {${currentFragment.end}}.', @@ -382,7 +331,8 @@ void testMain() { expect( fragments, hasLength(f + 2), - reason: 'Failed at test case number $t:\n' + reason: + 'Failed at test case number $t:\n' '$testCase\n' '"$text"\n' "\nExpected an extra fragment for endOfText but there wasn't one.", @@ -394,7 +344,8 @@ void testMain() { expect( currentFragment.type, endOfText, - reason: 'Failed at test case number $t:\n' + reason: + 'Failed at test case number $t:\n' '$testCase\n' '"$text"\n\n' 'Expected an endOfText fragment but found: $currentFragment', @@ -402,7 +353,8 @@ void testMain() { expect( currentFragment.end, text.length, - reason: 'Failed at test case number $t:\n' + reason: + 'Failed at test case number $t:\n' '$testCase\n' '"$text"\n\n' 'Expected an endOfText fragment ending at {${text.length}} but found: $currentFragment', @@ -435,8 +387,7 @@ void testMain() { }); test('khmer text with hard line breaks', () { - const String khmerText = - '\u179B\u1792\u17D2\u179C\u17BE\u17B2\u17D2\u1799'; + const String khmerText = '\u179B\u1792\u17D2\u179C\u17BE\u17B2\u17D2\u1799'; expect(split(khmerText), [ Line('\u179B', opportunity), Line('\u1792\u17D2\u179C\u17BE', opportunity), @@ -455,16 +406,10 @@ void testMain() { typedef GroupBody = void Function({required bool isV8}); void groupForEachFragmenter(GroupBody callback) { - group( - '$FWLineBreakFragmenter', - () => callback(isV8: false), - ); + group('$FWLineBreakFragmenter', () => callback(isV8: false)); if (domIntl.v8BreakIterator != null) { - group( - '$V8LineBreakFragmenter', - () => callback(isV8: true), - ); + group('$V8LineBreakFragmenter', () => callback(isV8: true)); } } diff --git a/lib/web_ui/test/html/text/line_breaker_test_helper.dart b/lib/web_ui/test/html/text/line_breaker_test_helper.dart index 8f92e11c77a00..4ea876d79e0d1 100644 --- a/lib/web_ui/test/html/text/line_breaker_test_helper.dart +++ b/lib/web_ui/test/html/text/line_breaker_test_helper.dart @@ -68,39 +68,36 @@ String _checkReplacement(String line, {required bool isV8}) { // v8BreakIterator deviates from the spec around Hiragana and Katakana // letters. - final RegExp hiragana21Regex = RegExp(r' × \[21\.03\] (HIRAGANA LETTER|KATAKANA LETTER|KATAKANA-HIRAGANA)'); - if (replacement.contains(hiragana21Regex) && !replacement.contains('(BB)') && !replacement.contains('(PR)')) { + final RegExp hiragana21Regex = RegExp( + r' × \[21\.03\] (HIRAGANA LETTER|KATAKANA LETTER|KATAKANA-HIRAGANA)', + ); + if (replacement.contains(hiragana21Regex) && + !replacement.contains('(BB)') && + !replacement.contains('(PR)')) { replacement = replacement .replaceAll(' × 3041', ' ÷ 3041') // HIRAGANA LETTER (CJ) .replaceAll(' × 30E5', ' ÷ 30E5') // KATAKANA LETTER (CJ) .replaceAll(' × 30FC', ' ÷ 30FC') // KATAKANA-HIRAGANA PROLONGED SOUND MARK (CJ) - .replaceAllMapped( - hiragana21Regex, - (Match m) => ' ÷ [21.03] ${m.group(1)}', - ); + .replaceAllMapped(hiragana21Regex, (Match m) => ' ÷ [21.03] ${m.group(1)}'); } if (replacement.contains(' × [16.0] HIRAGANA LETTER')) { replacement = replacement .replaceAll(' × 3041', ' ÷ 3041') // HIRAGANA LETTER (CJ) - .replaceAll( - ' × [16.0] HIRAGANA LETTER', - ' ÷ [16.0] HIRAGANA LETTER', - ); + .replaceAll(' × [16.0] HIRAGANA LETTER', ' ÷ [16.0] HIRAGANA LETTER'); } final RegExp hiraganaPercentRegex = RegExp(r'HIRAGANA .*? ÷ \[999\.0\] PERCENT'); if (replacement.contains(hiraganaPercentRegex)) { replacement = replacement .replaceAll(' ÷ 0025', ' × 0025') // PERCENT SIGN (PO) - .replaceAll( - ' ÷ [999.0] PERCENT', - ' × [999.0] PERCENT', - ); + .replaceAll(' ÷ [999.0] PERCENT', ' × [999.0] PERCENT'); } // v8BreakIterator also deviates from the spec around hyphens, commas and // full stops. - final RegExp hyphenRegex = RegExp(r'\((HY|IS)\)(.*?) ÷ \[999\.0\] (DIGIT|NUMBER|SECTION|THAI|)'); + final RegExp hyphenRegex = RegExp( + r'\((HY|IS)\)(.*?) ÷ \[999\.0\] (DIGIT|NUMBER|SECTION|THAI|)', + ); if (replacement.contains(hyphenRegex)) { replacement = replacement .replaceAll(' ÷ 0030', ' × 0030') // DIGIT ZERO (NU) @@ -131,8 +128,7 @@ final RegExp charWithBracketsRegex = RegExp( TestCase _parse(String line) { final int hashIndex = line.indexOf('#'); - final List sequence = - line.substring(0, hashIndex).trim().split(spaceRegex); + final List sequence = line.substring(0, hashIndex).trim().split(spaceRegex); final String explanation = line.substring(hashIndex + 1).trim(); final List signs = []; @@ -143,15 +139,12 @@ TestCase _parse(String line) { int i = signMatch.group(0)!.length; while (i < explanation.length) { - final Match charMatch = explanation[i] == '<' - ? charWithBracketsRegex.matchAsPrefix(explanation, i)! - : charRegex.matchAsPrefix(explanation, i)!; + final Match charMatch = + explanation[i] == '<' + ? charWithBracketsRegex.matchAsPrefix(explanation, i)! + : charRegex.matchAsPrefix(explanation, i)!; final int charCode = int.parse(sequence[2 * chars.length + 1], radix: 16); - chars.add(Char._( - code: charCode, - name: charMatch.group(1)!, - property: charMatch.group(2)!, - )); + chars.add(Char._(code: charCode, name: charMatch.group(1)!, property: charMatch.group(2)!)); i += charMatch.group(0)!.length; final Match signMatch = signRegex.matchAsPrefix(explanation, i)!; diff --git a/lib/web_ui/test/html/text/line_breaker_test_raw_data.dart b/lib/web_ui/test/html/text/line_breaker_test_raw_data.dart index c0c667d03ac9b..e76b9080f9021 100644 --- a/lib/web_ui/test/html/text/line_breaker_test_raw_data.dart +++ b/lib/web_ui/test/html/text/line_breaker_test_raw_data.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. - /// The list of test cases from: /// - https://www.unicode.org/Public/13.0.0/ucd/auxiliary/LineBreakTest.txt const String rawLineBreakTestData = ''' diff --git a/lib/web_ui/test/html/text/text_direction_test.dart b/lib/web_ui/test/html/text/text_direction_test.dart index 9049c0948ef54..172c35aa9f9f1 100644 --- a/lib/web_ui/test/html/text/text_direction_test.dart +++ b/lib/web_ui/test/html/text/text_direction_test.dart @@ -16,9 +16,7 @@ void main() { Future testMain() async { group('$BidiFragmenter', () { test('empty string', () { - expect(split(''), <_Bidi>[ - _Bidi('', null, ffPrevious), - ]); + expect(split(''), <_Bidi>[_Bidi('', null, ffPrevious)]); }); test('basic cases', () { @@ -73,9 +71,7 @@ Future testMain() async { }); test('spaces', () { - expect(split(' '), <_Bidi>[ - _Bidi(' ', null, ffSandwich), - ]); + expect(split(' '), <_Bidi>[_Bidi(' ', null, ffSandwich)]); }); test('symbols', () { @@ -193,6 +189,6 @@ class _Bidi { List<_Bidi> split(String text) { return <_Bidi>[ for (final BidiFragment bidiFragment in BidiFragmenter(text).fragment()) - _Bidi.fromBidiFragment(text, bidiFragment) + _Bidi.fromBidiFragment(text, bidiFragment), ]; } diff --git a/lib/web_ui/test/html/text/word_breaker_test.dart b/lib/web_ui/test/html/text/word_breaker_test.dart index 4b37f85d25d11..c7ef49dd81ebb 100644 --- a/lib/web_ui/test/html/text/word_breaker_test.dart +++ b/lib/web_ui/test/html/text/word_breaker_test.dart @@ -40,22 +40,22 @@ void testMain() { final String punctuationSpace = String.fromCharCode(0x2008); final String mathSpace = String.fromCharCode(0x205F); final String ideographicSpace = String.fromCharCode(0x3000); - expectWords( - 'foo$oghamSpace ${punctuationSpace}bar', - ['foo', '$oghamSpace $punctuationSpace', 'bar'], - ); - expectWords( - '$mathSpace$ideographicSpace${oghamSpace}foo', - ['$mathSpace$ideographicSpace$oghamSpace', 'foo'], - ); - expectWords( - 'foo$punctuationSpace$mathSpace ', - ['foo', '$punctuationSpace$mathSpace '], - ); - expectWords( + expectWords('foo$oghamSpace ${punctuationSpace}bar', [ + 'foo', + '$oghamSpace $punctuationSpace', + 'bar', + ]); + expectWords('$mathSpace$ideographicSpace${oghamSpace}foo', [ + '$mathSpace$ideographicSpace$oghamSpace', + 'foo', + ]); + expectWords('foo$punctuationSpace$mathSpace ', [ + 'foo', + '$punctuationSpace$mathSpace ', + ]); + expectWords('$oghamSpace $punctuationSpace$mathSpace$ideographicSpace', [ '$oghamSpace $punctuationSpace$mathSpace$ideographicSpace', - ['$oghamSpace $punctuationSpace$mathSpace$ideographicSpace'], - ); + ]); }); test('Punctuation', () { @@ -73,10 +73,18 @@ void testMain() { test('Quotes', () { expectWords("Mike's bike", ["Mike's", ' ', 'bike']); expectWords("Students' grades", ['Students', "'", ' ', 'grades']); - expectWords( - 'Joe said: "I\'m here"', - ['Joe', ' ', 'said', ':', ' ', '"', "I'm", ' ', 'here', '"'], - ); + expectWords('Joe said: "I\'m here"', [ + 'Joe', + ' ', + 'said', + ':', + ' ', + '"', + "I'm", + ' ', + 'here', + '"', + ]); }); // Hebrew letters have the same rules as other letters, except @@ -110,8 +118,7 @@ void testMain() { expectWords('foo\n\nbar', ['foo', '\n', '\n', 'bar']); expectWords('foo\r\rbar', ['foo', '\r', '\r', 'bar']); - expectWords( - 'foo$newline${newline}bar', ['foo', newline, newline, 'bar']); + expectWords('foo$newline${newline}bar', ['foo', newline, newline, 'bar']); expectWords('foo\n\rbar', ['foo', '\n', '\r', 'bar']); expectWords('foo$newline\rbar', ['foo', newline, '\r', 'bar']); @@ -143,8 +150,9 @@ void expectWords(String text, List expectedWords) { for (final String word in expectedWords) { final int nextBreak = WordBreaker.nextBreakIndex(text, strIndex); expect( - nextBreak, strIndex + word.length, - reason: 'Forward word break lookup: expecting to move to the end of "$word" in "$text".' + nextBreak, + strIndex + word.length, + reason: 'Forward word break lookup: expecting to move to the end of "$word" in "$text".', ); strIndex += word.length; } @@ -154,8 +162,9 @@ void expectWords(String text, List expectedWords) { for (final String word in expectedWords.reversed) { final int prevBreak = WordBreaker.prevBreakIndex(text, strIndex); expect( - prevBreak, strIndex - word.length, - reason: 'Backward word break lookup: expecting to move to the start of "$word" in "$text".' + prevBreak, + strIndex - word.length, + reason: 'Backward word break lookup: expecting to move to the start of "$word" in "$text".', ); strIndex -= word.length; } diff --git a/lib/web_ui/test/html/text_test.dart b/lib/web_ui/test/html/text_test.dart index 4186417e15c46..f90dcf15440ec 100644 --- a/lib/web_ui/test/html/text_test.dart +++ b/lib/web_ui/test/html/text_test.dart @@ -40,12 +40,14 @@ Future testMain() async { test('predictably lays out a single-line paragraph', () { for (final double fontSize in [10.0, 20.0, 30.0, 40.0]) { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: fontSize, - )); + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle( + fontFamily: 'Ahem', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: fontSize, + ), + ); builder.addText('Test'); final Paragraph paragraph = builder.build(); paragraph.layout(const ParagraphConstraints(width: 400.0)); @@ -57,21 +59,21 @@ Future testMain() async { expect(paragraph.alphabeticBaseline, fontSize * .8); expect( paragraph.ideographicBaseline, - within( - distance: 0.001, - from: paragraph.alphabeticBaseline * baselineRatio), + within(distance: 0.001, from: paragraph.alphabeticBaseline * baselineRatio), ); } }); test('predictably lays out a multi-line paragraph', () { for (final double fontSize in [10.0, 20.0, 30.0, 40.0]) { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: fontSize, - )); + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle( + fontFamily: 'Ahem', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: fontSize, + ), + ); builder.addText('Test Ahem'); final Paragraph paragraph = builder.build(); paragraph.layout(ParagraphConstraints(width: fontSize * 5.0)); @@ -83,22 +85,22 @@ Future testMain() async { expect(paragraph.alphabeticBaseline, fontSize * .8); expect( paragraph.ideographicBaseline, - within( - distance: 0.001, - from: paragraph.alphabeticBaseline * baselineRatio), + within(distance: 0.001, from: paragraph.alphabeticBaseline * baselineRatio), ); } }); test('Basic line related metrics', () { const double fontSize = 10; - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: fontSize, - maxLines: 1, - ellipsis: 'BBB', - ))..addText('A' * 100); + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle( + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: fontSize, + maxLines: 1, + ellipsis: 'BBB', + ), + )..addText('A' * 100); final Paragraph paragraph = builder.build(); paragraph.layout(const ParagraphConstraints(width: 100.0)); @@ -117,31 +119,38 @@ Future testMain() async { test('Basic glyph metrics', () { const double fontSize = 10; - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: fontSize, - maxLines: 1, - ellipsis: 'BBB', - ))..addText('A' * 100); + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle( + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: fontSize, + maxLines: 1, + ellipsis: 'BBB', + ), + )..addText('A' * 100); final Paragraph paragraph = builder.build(); paragraph.layout(const ParagraphConstraints(width: 100.0)); expect(paragraph.getGlyphInfoAt(-1), isNull); // The last 3 characters on the first line are ellipsized with BBB. - expect(paragraph.getGlyphInfoAt(0)?.graphemeClusterCodeUnitRange, const TextRange(start: 0, end: 1)); - expect(paragraph.getGlyphInfoAt(6)?.graphemeClusterCodeUnitRange, const TextRange(start: 6, end: 7)); + expect( + paragraph.getGlyphInfoAt(0)?.graphemeClusterCodeUnitRange, + const TextRange(start: 0, end: 1), + ); + expect( + paragraph.getGlyphInfoAt(6)?.graphemeClusterCodeUnitRange, + const TextRange(start: 6, end: 7), + ); expect(paragraph.getGlyphInfoAt(7), isNull); expect(paragraph.getGlyphInfoAt(200), isNull); }); test('Basic glyph metrics - hit test', () { const double fontSize = 10.0; - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontSize: fontSize, - fontFamily: 'FlutterTest', - ))..addText('Test\nTest'); + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle(fontSize: fontSize, fontFamily: 'FlutterTest'), + )..addText('Test\nTest'); final Paragraph paragraph = builder.build(); paragraph.layout(const ParagraphConstraints(width: double.infinity)); @@ -157,18 +166,24 @@ Future testMain() async { test('Basic glyph metrics - hit test - center aligned text in separate fragments', () { const double fontSize = 10.0; - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontSize: fontSize, - textAlign: TextAlign.center, - fontFamily: 'FlutterTest', - ))..addText('12345\n') - ..addText('1') - ..addText('2') - ..addText('3'); + final ParagraphBuilder builder = + ParagraphBuilder( + ParagraphStyle( + fontSize: fontSize, + textAlign: TextAlign.center, + fontFamily: 'FlutterTest', + ), + ) + ..addText('12345\n') + ..addText('1') + ..addText('2') + ..addText('3'); final Paragraph paragraph = builder.build(); paragraph.layout(const ParagraphConstraints(width: 50)); - final GlyphInfo? bottomCenter = paragraph.getClosestGlyphInfoForOffset(const Offset(25.0, 99.0)); + final GlyphInfo? bottomCenter = paragraph.getClosestGlyphInfoForOffset( + const Offset(25.0, 99.0), + ); final GlyphInfo? expected = paragraph.getGlyphInfoAt(7); expect(bottomCenter, equals(expected)); expect(bottomCenter, isNot(paragraph.getGlyphInfoAt(8))); @@ -178,27 +193,51 @@ Future testMain() async { expect(bottomCenter?.writingDirection, TextDirection.ltr); }); - test('Glyph metrics with grapheme split into different runs', () { - const double fontSize = 10; - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: fontSize, - maxLines: 1, - ))..addText('A') // The base charater A. - ..addText('̀'); // The diacritical grave accent, which should combine with the base character to form a single grapheme. - final Paragraph paragraph = builder.build(); - paragraph.layout(const ParagraphConstraints(width: double.infinity)); + test( + 'Glyph metrics with grapheme split into different runs', + () { + const double fontSize = 10; + final ParagraphBuilder builder = + ParagraphBuilder( + ParagraphStyle( + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: fontSize, + maxLines: 1, + ), + ) + ..addText('A') // The base charater A. + ..addText( + '̀', + ); // The diacritical grave accent, which should combine with the base character to form a single grapheme. + final Paragraph paragraph = builder.build(); + paragraph.layout(const ParagraphConstraints(width: double.infinity)); - expect(paragraph.getGlyphInfoAt(0)?.graphemeClusterCodeUnitRange, const TextRange(start: 0, end: 2)); - expect(paragraph.getGlyphInfoAt(0)?.graphemeClusterLayoutBounds, const Rect.fromLTWH(0.0, 0.0, 10.0, 10.0)); - expect(paragraph.getGlyphInfoAt(1)?.graphemeClusterCodeUnitRange, const TextRange(start: 0, end: 2)); - expect(paragraph.getGlyphInfoAt(1)?.graphemeClusterLayoutBounds, const Rect.fromLTWH(0.0, 0.0, 10.0, 10.0)); + expect( + paragraph.getGlyphInfoAt(0)?.graphemeClusterCodeUnitRange, + const TextRange(start: 0, end: 2), + ); + expect( + paragraph.getGlyphInfoAt(0)?.graphemeClusterLayoutBounds, + const Rect.fromLTWH(0.0, 0.0, 10.0, 10.0), + ); + expect( + paragraph.getGlyphInfoAt(1)?.graphemeClusterCodeUnitRange, + const TextRange(start: 0, end: 2), + ); + expect( + paragraph.getGlyphInfoAt(1)?.graphemeClusterLayoutBounds, + const Rect.fromLTWH(0.0, 0.0, 10.0, 10.0), + ); - final GlyphInfo? bottomRight = paragraph.getClosestGlyphInfoForOffset(const Offset(99.0, 99.0)); - expect(bottomRight?.graphemeClusterCodeUnitRange, const TextRange(start: 0, end: 2)); - expect(bottomRight?.graphemeClusterLayoutBounds, const Rect.fromLTWH(0.0, 0.0, 10.0, 10.0)); - }, skip: domIntl.v8BreakIterator == null); // Intended: Intl.v8breakiterator is needed for correctly breaking grapheme clusters. + final GlyphInfo? bottomRight = paragraph.getClosestGlyphInfoForOffset( + const Offset(99.0, 99.0), + ); + expect(bottomRight?.graphemeClusterCodeUnitRange, const TextRange(start: 0, end: 2)); + expect(bottomRight?.graphemeClusterLayoutBounds, const Rect.fromLTWH(0.0, 0.0, 10.0, 10.0)); + }, + skip: domIntl.v8BreakIterator == null, + ); // Intended: Intl.v8breakiterator is needed for correctly breaking grapheme clusters. test('disable rounding hack', () { const double fontSize = 1; @@ -206,11 +245,13 @@ Future testMain() async { const double letterSpacing = 0.25; const double expectedIntrinsicWidth = text.length * (fontSize + letterSpacing); assert(expectedIntrinsicWidth.truncate() != expectedIntrinsicWidth); - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(fontSize: fontSize, fontFamily: 'FlutterTest')); + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle(fontSize: fontSize, fontFamily: 'FlutterTest'), + ); builder.pushStyle(TextStyle(letterSpacing: letterSpacing)); builder.addText(text); - final Paragraph paragraph = builder.build() - ..layout(const ParagraphConstraints(width: expectedIntrinsicWidth)); + final Paragraph paragraph = + builder.build()..layout(const ParagraphConstraints(width: expectedIntrinsicWidth)); expect(paragraph.maxIntrinsicWidth, expectedIntrinsicWidth); switch (paragraph.computeLineMetrics()) { @@ -222,12 +263,15 @@ Future testMain() async { }); test('lay out unattached paragraph', () { - final CanvasParagraph paragraph = plain(EngineParagraphStyle( - fontFamily: 'sans-serif', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 14.0, - ), 'How do you do this fine morning?'); + final CanvasParagraph paragraph = plain( + EngineParagraphStyle( + fontFamily: 'sans-serif', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: 14.0, + ), + 'How do you do this fine morning?', + ); expect(paragraph.height, 0.0); expect(paragraph.width, -1.0); @@ -247,14 +291,15 @@ Future testMain() async { expect(paragraph.ideographicBaseline, greaterThan(0.0)); }); - Paragraph measure( - {String text = 'Hello', double fontSize = 14.0, double width = 50.0}) { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'sans-serif', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: fontSize, - )); + Paragraph measure({String text = 'Hello', double fontSize = 14.0, double width = 50.0}) { + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle( + fontFamily: 'sans-serif', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: fontSize, + ), + ); builder.addText(text); final Paragraph paragraph = builder.build(); paragraph.layout(ParagraphConstraints(width: width)); @@ -266,10 +311,8 @@ Future testMain() async { for (int i = 0; i < 6; i++) { final double fontSize = 20.0 + 10.0 * i; final Paragraph paragraph = measure(fontSize: fontSize); - expect(paragraph.alphabeticBaseline, - greaterThan(previousParagraph.alphabeticBaseline)); - expect(paragraph.ideographicBaseline, - greaterThan(previousParagraph.ideographicBaseline)); + expect(paragraph.alphabeticBaseline, greaterThan(previousParagraph.alphabeticBaseline)); + expect(paragraph.ideographicBaseline, greaterThan(previousParagraph.ideographicBaseline)); previousParagraph = paragraph; } }); @@ -284,23 +327,27 @@ Future testMain() async { }); test('$ParagraphBuilder detects plain text', () { - ParagraphBuilder builder = ParagraphBuilder(EngineParagraphStyle( - fontFamily: 'sans-serif', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 15.0, - )); + ParagraphBuilder builder = ParagraphBuilder( + EngineParagraphStyle( + fontFamily: 'sans-serif', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: 15.0, + ), + ); builder.addText('hi'); CanvasParagraph paragraph = builder.build() as CanvasParagraph; expect(paragraph.plainText, isNotNull); expect(paragraph.paragraphStyle.fontWeight, FontWeight.normal); - builder = ParagraphBuilder(EngineParagraphStyle( - fontFamily: 'sans-serif', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 15.0, - )); + builder = ParagraphBuilder( + EngineParagraphStyle( + fontFamily: 'sans-serif', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: 15.0, + ), + ); builder.pushStyle(TextStyle(fontWeight: FontWeight.bold)); builder.addText('hi'); paragraph = builder.build() as CanvasParagraph; @@ -308,12 +355,14 @@ Future testMain() async { }); test('$ParagraphBuilder detects rich text', () { - final ParagraphBuilder builder = ParagraphBuilder(EngineParagraphStyle( - fontFamily: 'sans-serif', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 15.0, - )); + final ParagraphBuilder builder = ParagraphBuilder( + EngineParagraphStyle( + fontFamily: 'sans-serif', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: 15.0, + ), + ); builder.addText('h'); builder.pushStyle(TextStyle(fontWeight: FontWeight.bold)); builder.addText('i'); @@ -322,12 +371,14 @@ Future testMain() async { }); test('$ParagraphBuilder treats empty text as plain', () { - final ParagraphBuilder builder = ParagraphBuilder(EngineParagraphStyle( - fontFamily: 'sans-serif', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 15.0, - )); + final ParagraphBuilder builder = ParagraphBuilder( + EngineParagraphStyle( + fontFamily: 'sans-serif', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: 15.0, + ), + ); builder.pushStyle(TextStyle(fontWeight: FontWeight.bold)); final CanvasParagraph paragraph = builder.build() as CanvasParagraph; expect(paragraph.plainText, ''); @@ -335,59 +386,65 @@ Future testMain() async { // Regression test for https://github.com/flutter/flutter/issues/38972 test( - 'should not set fontFamily to effectiveFontFamily for spans in rich text', - () { - final ParagraphBuilder builder = ParagraphBuilder(EngineParagraphStyle( - fontFamily: 'Roboto', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 15.0, - )); - builder - .pushStyle(TextStyle(fontFamily: 'Menlo', fontWeight: FontWeight.bold)); - const String firstSpanText = 'abc'; - builder.addText(firstSpanText); - builder.pushStyle(TextStyle(fontSize: 30.0, fontWeight: FontWeight.normal)); - const String secondSpanText = 'def'; - builder.addText(secondSpanText); - final CanvasParagraph paragraph = builder.build() as CanvasParagraph; - paragraph.layout(const ParagraphConstraints(width: 800.0)); - expect(paragraph.plainText, 'abcdef'); - final List spans = - paragraph.toDomElement().querySelectorAll('flt-span').toList(); - expect(spans[0].style.fontFamily, 'FlutterTest, $fallback, sans-serif'); - // The nested span here should not set it's family to default sans-serif. - expect(spans[1].style.fontFamily, 'FlutterTest, $fallback, sans-serif'); - }, - // TODO(mdebbar): https://github.com/flutter/flutter/issues/46638 - skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.firefox); - - test('adds Arial and sans-serif as fallback fonts', () { - // Set this to false so it doesn't default to the test font. - ui_web.debugEmulateFlutterTesterEnvironment = false; + 'should not set fontFamily to effectiveFontFamily for spans in rich text', + () { + final ParagraphBuilder builder = ParagraphBuilder( + EngineParagraphStyle( + fontFamily: 'Roboto', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: 15.0, + ), + ); + builder.pushStyle(TextStyle(fontFamily: 'Menlo', fontWeight: FontWeight.bold)); + const String firstSpanText = 'abc'; + builder.addText(firstSpanText); + builder.pushStyle(TextStyle(fontSize: 30.0, fontWeight: FontWeight.normal)); + const String secondSpanText = 'def'; + builder.addText(secondSpanText); + final CanvasParagraph paragraph = builder.build() as CanvasParagraph; + paragraph.layout(const ParagraphConstraints(width: 800.0)); + expect(paragraph.plainText, 'abcdef'); + final List spans = paragraph.toDomElement().querySelectorAll('flt-span').toList(); + expect(spans[0].style.fontFamily, 'FlutterTest, $fallback, sans-serif'); + // The nested span here should not set it's family to default sans-serif. + expect(spans[1].style.fontFamily, 'FlutterTest, $fallback, sans-serif'); + }, + // TODO(mdebbar): https://github.com/flutter/flutter/issues/46638 + skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.firefox, + ); - final CanvasParagraph paragraph = plain(EngineParagraphStyle( - fontFamily: 'SomeFont', - fontSize: 12.0, - ), 'Hello'); + test( + 'adds Arial and sans-serif as fallback fonts', + () { + // Set this to false so it doesn't default to the test font. + ui_web.debugEmulateFlutterTesterEnvironment = false; + + final CanvasParagraph paragraph = plain( + EngineParagraphStyle(fontFamily: 'SomeFont', fontSize: 12.0), + 'Hello', + ); - paragraph.layout(constrain(double.infinity)); - expect(paragraph.toDomElement().children.single.style.fontFamily, - 'SomeFont, $fallback, sans-serif'); + paragraph.layout(constrain(double.infinity)); + expect( + paragraph.toDomElement().children.single.style.fontFamily, + 'SomeFont, $fallback, sans-serif', + ); - ui_web.debugEmulateFlutterTesterEnvironment = true; - }, - // TODO(mdebbar): https://github.com/flutter/flutter/issues/46638 - skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.firefox); + ui_web.debugEmulateFlutterTesterEnvironment = true; + }, + // TODO(mdebbar): https://github.com/flutter/flutter/issues/46638 + skip: ui_web.browser.browserEngine == ui_web.BrowserEngine.firefox, + ); test('does not add fallback fonts to generic families', () { // Set this to false so it doesn't default to the default test font. ui_web.debugEmulateFlutterTesterEnvironment = false; - final CanvasParagraph paragraph = plain(EngineParagraphStyle( - fontFamily: 'serif', - fontSize: 12.0, - ), 'Hello'); + final CanvasParagraph paragraph = plain( + EngineParagraphStyle(fontFamily: 'serif', fontSize: 12.0), + 'Hello', + ); paragraph.layout(constrain(double.infinity)); expect(paragraph.toDomElement().children.single.style.fontFamily, 'serif'); @@ -399,14 +456,16 @@ Future testMain() async { // Set this to false so it doesn't default to the default test font. ui_web.debugEmulateFlutterTesterEnvironment = false; - final CanvasParagraph paragraph = plain(EngineParagraphStyle( - fontFamily: 'MyFont 2000', - fontSize: 12.0, - ), 'Hello'); + final CanvasParagraph paragraph = plain( + EngineParagraphStyle(fontFamily: 'MyFont 2000', fontSize: 12.0), + 'Hello', + ); paragraph.layout(constrain(double.infinity)); - expect(paragraph.toDomElement().children.single.style.fontFamily, - '"MyFont 2000", $fallback, sans-serif'); + expect( + paragraph.toDomElement().children.single.style.fontFamily, + '"MyFont 2000", $fallback, sans-serif', + ); ui_web.debugEmulateFlutterTesterEnvironment = true; }); @@ -446,32 +505,24 @@ Future testMain() async { }); test('textBefore works', () { expect(const TextRange(start: 0, end: 0).textBefore('hello'), isEmpty); - expect( - const TextRange(start: 1, end: 1).textBefore('hello'), equals('h')); - expect( - const TextRange(start: 1, end: 2).textBefore('hello'), equals('h')); - expect(const TextRange(start: 5, end: 5).textBefore('hello'), - equals('hello')); + expect(const TextRange(start: 1, end: 1).textBefore('hello'), equals('h')); + expect(const TextRange(start: 1, end: 2).textBefore('hello'), equals('h')); + expect(const TextRange(start: 5, end: 5).textBefore('hello'), equals('hello')); expect(const TextRange(start: 0, end: 5).textBefore('hello'), isEmpty); }); test('textAfter works', () { - expect(const TextRange(start: 0, end: 0).textAfter('hello'), - equals('hello')); - expect( - const TextRange(start: 1, end: 1).textAfter('hello'), equals('ello')); - expect( - const TextRange(start: 1, end: 2).textAfter('hello'), equals('llo')); + expect(const TextRange(start: 0, end: 0).textAfter('hello'), equals('hello')); + expect(const TextRange(start: 1, end: 1).textAfter('hello'), equals('ello')); + expect(const TextRange(start: 1, end: 2).textAfter('hello'), equals('llo')); expect(const TextRange(start: 5, end: 5).textAfter('hello'), isEmpty); expect(const TextRange(start: 0, end: 5).textAfter('hello'), isEmpty); }); test('textInside works', () { expect(const TextRange(start: 0, end: 0).textInside('hello'), isEmpty); expect(const TextRange(start: 1, end: 1).textInside('hello'), isEmpty); - expect( - const TextRange(start: 1, end: 2).textInside('hello'), equals('e')); + expect(const TextRange(start: 1, end: 2).textInside('hello'), equals('e')); expect(const TextRange(start: 5, end: 5).textInside('hello'), isEmpty); - expect(const TextRange(start: 0, end: 5).textInside('hello'), - equals('hello')); + expect(const TextRange(start: 0, end: 5).textInside('hello'), equals('hello')); }); }); @@ -495,12 +546,18 @@ Future testMain() async { }); const List testFonts = ['FlutterTest', 'Ahem']; - test('The default test font is used when a non-test fontFamily is specified, or fontFamily is not specified', () { - final String defaultTestFontFamily = testFonts.first; - - expect(EngineTextStyle.only().effectiveFontFamily, defaultTestFontFamily); - expect(EngineTextStyle.only(fontFamily: 'BogusFontFamily').effectiveFontFamily, defaultTestFontFamily); - }); + test( + 'The default test font is used when a non-test fontFamily is specified, or fontFamily is not specified', + () { + final String defaultTestFontFamily = testFonts.first; + + expect(EngineTextStyle.only().effectiveFontFamily, defaultTestFontFamily); + expect( + EngineTextStyle.only(fontFamily: 'BogusFontFamily').effectiveFontFamily, + defaultTestFontFamily, + ); + }, + ); test('Can specify test fontFamily to use', () { for (final String testFont in testFonts) { diff --git a/lib/web_ui/test/ui/canvas_curves_golden_test.dart b/lib/web_ui/test/ui/canvas_curves_golden_test.dart index cdbd05935a0de..9543b71415cad 100644 --- a/lib/web_ui/test/ui/canvas_curves_golden_test.dart +++ b/lib/web_ui/test/ui/canvas_curves_golden_test.dart @@ -17,10 +17,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - withImplicitView: true, - setUpTestViewDimensions: false, - ); + setUpUnitTests(withImplicitView: true, setUpTestViewDimensions: false); const Rect region = Rect.fromLTWH(0, 0, 300, 300); @@ -35,7 +32,7 @@ Future testMain() async { Paint() ..style = PaintingStyle.stroke ..strokeWidth = 3.0 - ..color = const Color(0xFFFF00FF) + ..color = const Color(0xFFFF00FF), ); await drawPictureUsingCurrentRenderer(recorder.endRecording()); @@ -52,7 +49,7 @@ Future testMain() async { Paint() ..style = PaintingStyle.stroke ..strokeWidth = 3.0 - ..color = const Color(0xFFFF0000) + ..color = const Color(0xFFFF0000), ); await drawPictureUsingCurrentRenderer(recorder.endRecording()); @@ -68,7 +65,7 @@ Future testMain() async { Paint() ..style = PaintingStyle.stroke ..strokeWidth = 3.0 - ..color = const Color(0xFF00FFFF) + ..color = const Color(0xFF00FFFF), ); await drawPictureUsingCurrentRenderer(recorder.endRecording()); diff --git a/lib/web_ui/test/ui/canvas_draw_points_golden_test.dart b/lib/web_ui/test/ui/canvas_draw_points_golden_test.dart index ca2540245cb82..0603159befe7a 100644 --- a/lib/web_ui/test/ui/canvas_draw_points_golden_test.dart +++ b/lib/web_ui/test/ui/canvas_draw_points_golden_test.dart @@ -19,10 +19,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - withImplicitView: true, - setUpTestViewDimensions: false, - ); + setUpUnitTests(withImplicitView: true, setUpTestViewDimensions: false); const ui.Rect region = ui.Rect.fromLTWH(0, 0, 400, 600); @@ -34,8 +31,7 @@ Future testMain() async { canvas = ui.Canvas(recorder, region); }); - tearDown(() { - }); + tearDown(() {}); test('draws points in all 3 modes', () async { final ui.Paint paint = ui.Paint(); @@ -45,21 +41,21 @@ Future testMain() async { ui.Offset(10, 10), ui.Offset(50, 10), ui.Offset(70, 70), - ui.Offset(170, 70) + ui.Offset(170, 70), ]; canvas.drawPoints(ui.PointMode.points, points, paint); const List points2 = [ ui.Offset(10, 110), ui.Offset(50, 110), ui.Offset(70, 170), - ui.Offset(170, 170) + ui.Offset(170, 170), ]; canvas.drawPoints(ui.PointMode.lines, points2, paint); const List points3 = [ ui.Offset(10, 210), ui.Offset(50, 210), ui.Offset(70, 270), - ui.Offset(170, 270) + ui.Offset(170, 270), ]; canvas.drawPoints(ui.PointMode.polygon, points3, paint); @@ -75,21 +71,21 @@ Future testMain() async { ui.Offset(10, 10), ui.Offset(50, 10), ui.Offset(70, 70), - ui.Offset(170, 70) + ui.Offset(170, 70), ]); canvas.drawRawPoints(ui.PointMode.points, points, paint); final Float32List points2 = offsetListToFloat32List(const [ ui.Offset(10, 110), ui.Offset(50, 110), ui.Offset(70, 170), - ui.Offset(170, 170) + ui.Offset(170, 170), ]); canvas.drawRawPoints(ui.PointMode.lines, points2, paint); final Float32List points3 = offsetListToFloat32List(const [ ui.Offset(10, 210), ui.Offset(50, 210), ui.Offset(70, 270), - ui.Offset(170, 270) + ui.Offset(170, 270), ]); canvas.drawRawPoints(ui.PointMode.polygon, points3, paint); @@ -98,22 +94,35 @@ Future testMain() async { }); test('Should draw points with strokeWidth', () async { - final ui.Paint nullStrokePaint = - ui.Paint()..color = const ui.Color(0xffff0000); - canvas.drawRawPoints(ui.PointMode.lines, Float32List.fromList([ - 30.0, 20.0, 200.0, 20.0]), nullStrokePaint); - final ui.Paint strokePaint1 = ui.Paint() - ..strokeWidth = 1.0 - ..color = const ui.Color(0xff0000ff); - canvas.drawRawPoints(ui.PointMode.lines, Float32List.fromList([ - 30.0, 30.0, 200.0, 30.0]), strokePaint1); - final ui.Paint strokePaint3 = ui.Paint() - ..strokeWidth = 3.0 - ..color = const ui.Color(0xff00a000); - canvas.drawRawPoints(ui.PointMode.lines, Float32List.fromList([ - 30.0, 40.0, 200.0, 40.0]), strokePaint3); - canvas.drawRawPoints(ui.PointMode.points, Float32List.fromList([ - 30.0, 50.0, 40.0, 50.0, 50.0, 50.0]), strokePaint3); + final ui.Paint nullStrokePaint = ui.Paint()..color = const ui.Color(0xffff0000); + canvas.drawRawPoints( + ui.PointMode.lines, + Float32List.fromList([30.0, 20.0, 200.0, 20.0]), + nullStrokePaint, + ); + final ui.Paint strokePaint1 = + ui.Paint() + ..strokeWidth = 1.0 + ..color = const ui.Color(0xff0000ff); + canvas.drawRawPoints( + ui.PointMode.lines, + Float32List.fromList([30.0, 30.0, 200.0, 30.0]), + strokePaint1, + ); + final ui.Paint strokePaint3 = + ui.Paint() + ..strokeWidth = 3.0 + ..color = const ui.Color(0xff00a000); + canvas.drawRawPoints( + ui.PointMode.lines, + Float32List.fromList([30.0, 40.0, 200.0, 40.0]), + strokePaint3, + ); + canvas.drawRawPoints( + ui.PointMode.points, + Float32List.fromList([30.0, 50.0, 40.0, 50.0, 50.0, 50.0]), + strokePaint3, + ); await drawPictureUsingCurrentRenderer(recorder.endRecording()); await matchGoldenFile('ui_canvas_draw_points_stroke.png', region: region); }); diff --git a/lib/web_ui/test/ui/canvas_lines_golden_test.dart b/lib/web_ui/test/ui/canvas_lines_golden_test.dart index 46c39e8cd9664..5bde6c9c740de 100644 --- a/lib/web_ui/test/ui/canvas_lines_golden_test.dart +++ b/lib/web_ui/test/ui/canvas_lines_golden_test.dart @@ -15,10 +15,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - withImplicitView: true, - setUpTestViewDimensions: false, - ); + setUpUnitTests(withImplicitView: true, setUpTestViewDimensions: false); const Rect region = Rect.fromLTWH(0, 0, 300, 300); @@ -37,26 +34,32 @@ Future testMain() async { final Canvas canvas = Canvas(recorder, region); // test rendering lines correctly with negative offset when using DOM - final Paint paintWithStyle = Paint() - ..color = const Color(0xFFE91E63) // Colors.pink - ..style = PaintingStyle.stroke - ..strokeWidth = 16 - ..strokeCap = StrokeCap.round; + final Paint paintWithStyle = + Paint() + ..color = const 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 Paint paintWithoutStyle = Paint() - ..color = const Color(0xFF4CAF50) // Colors.green - ..strokeWidth = 16 - ..strokeCap = StrokeCap.round; + final Paint paintWithoutStyle = + Paint() + ..color = const 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), + 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(); + final List shiftedPoints = + points.map((Offset point) => point.translate(20, 20)).toList(); paintLinesFromPoints(canvas, paintWithStyle, points); paintLinesFromPoints(canvas, paintWithoutStyle, shiftedPoints); @@ -69,29 +72,37 @@ Future testMain() async { final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder, region); - final Paint paintStrokeCapRound = Paint() - ..color = const Color(0xFFE91E63) // Colors.pink - ..strokeWidth = 16 - ..strokeCap = StrokeCap.round; + final Paint paintStrokeCapRound = + Paint() + ..color = const Color(0xFFE91E63) // Colors.pink + ..strokeWidth = 16 + ..strokeCap = StrokeCap.round; - final Paint paintStrokeCapSquare = Paint() - ..color = const Color(0xFF4CAF50) // Colors.green - ..strokeWidth = 16 - ..strokeCap = StrokeCap.square; + final Paint paintStrokeCapSquare = + Paint() + ..color = const Color(0xFF4CAF50) // Colors.green + ..strokeWidth = 16 + ..strokeCap = StrokeCap.square; - final Paint paintStrokeCapButt = Paint() - ..color = const Color(0xFFFF9800) // Colors.orange - ..strokeWidth = 16 - ..strokeCap = StrokeCap.butt; + final Paint paintStrokeCapButt = + Paint() + ..color = const 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), + 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(); + 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(canvas, paintStrokeCapRound, points); paintLinesFromPoints(canvas, paintStrokeCapSquare, shiftedPoints); @@ -103,21 +114,25 @@ Future testMain() async { } void paintLines(Canvas canvas) { - final Paint nullPaint = Paint() - ..strokeWidth = 1.0 - ..style = PaintingStyle.stroke; - final Paint paint1 = Paint() - ..color = const Color(0xFF9E9E9E) // Colors.grey - ..strokeWidth = 1.0 - ..style = PaintingStyle.stroke; - final Paint paint2 = Paint() - ..color = const Color(0x7fff0000) - ..strokeWidth = 1.0 - ..style = PaintingStyle.stroke; - final Paint paint3 = Paint() - ..color = const Color(0xFF4CAF50) //Colors.green - ..strokeWidth = 1.0 - ..style = PaintingStyle.stroke; + final Paint nullPaint = + Paint() + ..strokeWidth = 1.0 + ..style = PaintingStyle.stroke; + final Paint paint1 = + Paint() + ..color = const Color(0xFF9E9E9E) // Colors.grey + ..strokeWidth = 1.0 + ..style = PaintingStyle.stroke; + final Paint paint2 = + Paint() + ..color = const Color(0x7fff0000) + ..strokeWidth = 1.0 + ..style = PaintingStyle.stroke; + final Paint paint3 = + Paint() + ..color = const 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); diff --git a/lib/web_ui/test/ui/canvas_test.dart b/lib/web_ui/test/ui/canvas_test.dart index 57faf92ecb77d..15059fff384d1 100644 --- a/lib/web_ui/test/ui/canvas_test.dart +++ b/lib/web_ui/test/ui/canvas_test.dart @@ -18,9 +18,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - setUpTestViewDimensions: false, - ); + setUpUnitTests(setUpTestViewDimensions: false); final bool deviceClipRoundsOut = renderer is! HtmlRenderer; runCanvasTests(deviceClipRoundsOut: deviceClipRoundsOut); @@ -37,7 +35,7 @@ void runCanvasTests({required bool deviceClipRoundsOut}) { expect(value.length, equals(16)); for (int r = 0; r < 4; r++) { for (int c = 0; c < 4; c++) { - expect(value[r*4 + c], within(from: expected[r*4 + c])); + expect(value[r * 4 + c], within(from: expected[r * 4 + c])); } } } @@ -99,7 +97,11 @@ void runCanvasTests({required bool deviceClipRoundsOut}) { final ui.PictureRecorder recorder = ui.PictureRecorder(); final ui.Canvas canvas = ui.Canvas(recorder); canvas.skew(12, 14.5); - final Float64List matrix = (Matrix4.identity()..setEntry(0, 1, 12)..setEntry(1, 0, 14.5)).toFloat64(); + final Float64List matrix = + (Matrix4.identity() + ..setEntry(0, 1, 12) + ..setEntry(1, 0, 14.5)) + .toFloat64(); final Float64List curMatrix = canvas.getTransform(); transformsClose(curMatrix, matrix); canvas.skew(10, 10); @@ -111,7 +113,11 @@ void runCanvasTests({required bool deviceClipRoundsOut}) { test('Canvas.transform affects canvas.getTransform', () async { final ui.PictureRecorder recorder = ui.PictureRecorder(); final ui.Canvas canvas = ui.Canvas(recorder); - final Float64List matrix = (Matrix4.identity()..translate(12.0, 14.5)..scale(12.0, 14.5)).toFloat64(); + final Float64List matrix = + (Matrix4.identity() + ..translate(12.0, 14.5) + ..scale(12.0, 14.5)) + .toFloat64(); canvas.transform(matrix); final Float64List curMatrix = canvas.getTransform(); transformsClose(curMatrix, matrix); @@ -123,9 +129,9 @@ void runCanvasTests({required bool deviceClipRoundsOut}) { }); void rectsClose(ui.Rect value, ui.Rect expected) { - expect(value.left, closeTo(expected.left, 1e-6)); - expect(value.top, closeTo(expected.top, 1e-6)); - expect(value.right, closeTo(expected.right, 1e-6)); + expect(value.left, closeTo(expected.left, 1e-6)); + expect(value.top, closeTo(expected.top, 1e-6)); + expect(value.right, closeTo(expected.right, 1e-6)); expect(value.bottom, closeTo(expected.bottom, 1e-6)); } @@ -228,7 +234,10 @@ void runCanvasTests({required bool deviceClipRoundsOut}) { 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)..addOval(clipRawBounds); + final ui.Path clip = + ui.Path() + ..addRect(clipRawBounds) + ..addOval(clipRawBounds); canvas.clipPath(clip); // Save initial return values for testing restored values diff --git a/lib/web_ui/test/ui/color_test.dart b/lib/web_ui/test/ui/color_test.dart index 6ee06e7f8a734..c49f30e682aef 100644 --- a/lib/web_ui/test/ui/color_test.dart +++ b/lib/web_ui/test/ui/color_test.dart @@ -8,7 +8,6 @@ import 'package:ui/ui.dart'; import '../common/test_initialization.dart'; - class _ColorMatcher extends Matcher { _ColorMatcher(this._target, this._threshold); @@ -75,8 +74,7 @@ Future testMain() async { test('two colors are only == if they have the same runtime type', () { expect(const Color(123), equals(const Color(123))); - expect(const Color(123), - equals(const Color(123))); + expect(const Color(123), equals(const Color(123))); expect(const Color(123), isNot(equals(const Color(321)))); expect(const Color(123), isNot(equals(const NotAColor(123)))); expect(const NotAColor(123), isNot(equals(const Color(123)))); diff --git a/lib/web_ui/test/ui/draw_atlas_golden_test.dart b/lib/web_ui/test/ui/draw_atlas_golden_test.dart index 77b54f6204a80..cdcc05e177a48 100644 --- a/lib/web_ui/test/ui/draw_atlas_golden_test.dart +++ b/lib/web_ui/test/ui/draw_atlas_golden_test.dart @@ -35,7 +35,7 @@ ui.Image generateAtlas() { canvas.drawRect( // Inset the square by one pixel so it doesn't bleed into the other sprites. kBlueSquareRegion.deflate(1.0), - ui.Paint()..color = const ui.Color(0xFF0000FF) + ui.Paint()..color = const ui.Color(0xFF0000FF), ); canvas.restore(); @@ -47,7 +47,8 @@ ui.Image generateAtlas() { // Make the circle one pixel smaller than the bounds to it doesn't bleed // into the other shapes. (kRedCircleRegion.width / 2.0) - 1.0, - ui.Paint()..color = const ui.Color(0xFFFF0000)); + ui.Paint()..color = const ui.Color(0xFFFF0000), + ); canvas.restore(); // Draw the star @@ -67,20 +68,20 @@ ui.Image generateAtlas() { final ui.Path starPath = ui.Path(); starPath.moveTo( starCenter.dx + radius * math.cos(theta), - starCenter.dy + radius * math.sin(theta) + starCenter.dy + radius * math.sin(theta), ); for (int i = 0; i < 5; i++) { theta += rotation; starPath.lineTo( starCenter.dx + radius * math.cos(theta), - starCenter.dy + radius * math.sin(theta) + starCenter.dy + radius * math.sin(theta), ); } canvas.drawPath( starPath, ui.Paint() ..color = const ui.Color(0xFFFF00FF) - ..style = ui.PaintingStyle.fill + ..style = ui.PaintingStyle.fill, ); canvas.restore(); @@ -90,31 +91,28 @@ ui.Image generateAtlas() { final ui.Path squigglePath = ui.Path(); squigglePath.moveTo(kGreenSquiggleRegion.topCenter.dx, kGreenSquiggleRegion.topCenter.dy + 2.0); squigglePath.cubicTo( - kGreenSquiggleRegion.left - 10.0, kGreenSquiggleRegion.top + kGreenSquiggleRegion.height * 0.33, - kGreenSquiggleRegion.right + 10.0, kGreenSquiggleRegion.top + kGreenSquiggleRegion.height * 0.66, - kGreenSquiggleRegion.bottomCenter.dx, kGreenSquiggleRegion.bottomCenter.dy - 2.0 + kGreenSquiggleRegion.left - 10.0, + kGreenSquiggleRegion.top + kGreenSquiggleRegion.height * 0.33, + kGreenSquiggleRegion.right + 10.0, + kGreenSquiggleRegion.top + kGreenSquiggleRegion.height * 0.66, + kGreenSquiggleRegion.bottomCenter.dx, + kGreenSquiggleRegion.bottomCenter.dy - 2.0, ); canvas.drawPath( squigglePath, ui.Paint() ..color = const ui.Color(0xFF00FF00) ..style = ui.PaintingStyle.stroke - ..strokeWidth = 5.0 + ..strokeWidth = 5.0, ); canvas.restore(); final ui.Picture picture = recorder.endRecording(); - return picture.toImageSync( - kTotalAtlasRegion.width.toInt(), - kTotalAtlasRegion.height.toInt() - ); + return picture.toImageSync(kTotalAtlasRegion.width.toInt(), kTotalAtlasRegion.height.toInt()); } Future testMain() async { - setUpUnitTests( - withImplicitView: true, - setUpTestViewDimensions: false, - ); + setUpUnitTests(withImplicitView: true, setUpTestViewDimensions: false); const ui.Rect region = ui.Rect.fromLTWH(0, 0, 300, 300); @@ -131,21 +129,19 @@ Future testMain() async { final ui.PictureRecorder recorder = ui.PictureRecorder(); final ui.Canvas canvas = ui.Canvas(recorder, region); final ui.Image atlas = generateAtlas(); - final List transforms = List.generate( - 12, (int index) { - const double radius = 100; - const double rotation = math.pi / 6.0; - final double angle = rotation * index; - final double scos = math.sin(angle); - final double ssin = math.cos(angle); - return ui.RSTransform( - scos, - ssin, - region.center.dx + radius * scos, - region.center.dy + radius * ssin, - ); - } - ); + final List transforms = List.generate(12, (int index) { + const double radius = 100; + const double rotation = math.pi / 6.0; + final double angle = rotation * index; + final double scos = math.sin(angle); + final double ssin = math.cos(angle); + return ui.RSTransform( + scos, + ssin, + region.center.dx + radius * scos, + region.center.dy + radius * ssin, + ); + }); final List rects = [ kBlueSquareRegion, kRedCircleRegion, @@ -160,8 +156,7 @@ Future testMain() async { kMagentaStarRegion, kGreenSquiggleRegion, ]; - canvas.drawAtlas( - atlas, transforms, rects, null, null, null, ui.Paint()); + canvas.drawAtlas(atlas, transforms, rects, null, null, null, ui.Paint()); await drawPictureUsingCurrentRenderer(recorder.endRecording()); await matchGoldenFile('ui_draw_atlas.png', region: region); @@ -210,15 +205,7 @@ Future testMain() async { final int rgb = 0xFF << (8 * (i % 3)); colors[i] = 0xFF000000 | rgb; } - canvas.drawRawAtlas( - atlas, - transforms, - rawRects, - colors, - ui.BlendMode.dstIn, - null, - ui.Paint() - ); + canvas.drawRawAtlas(atlas, transforms, rawRects, colors, ui.BlendMode.dstIn, null, ui.Paint()); await drawPictureUsingCurrentRenderer(recorder.endRecording()); await matchGoldenFile('ui_draw_atlas_raw.png', region: region); diff --git a/lib/web_ui/test/ui/fallback_fonts_golden_test.dart b/lib/web_ui/test/ui/fallback_fonts_golden_test.dart index c97678683b6f7..2cf002c37d62a 100644 --- a/lib/web_ui/test/ui/fallback_fonts_golden_test.dart +++ b/lib/web_ui/test/ui/fallback_fonts_golden_test.dart @@ -22,593 +22,608 @@ 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, - ); - - 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/', + group( + 'Font fallbacks', + () { + setUpUnitTests( + withImplicitView: true, + emulateTesterEnvironment: false, + setUpTestViewDimensions: false, ); - debugOverrideJsConfiguration({ - 'fontFallbackBaseUrl': 'http://my-special-fonts.com/', - }.jsify() as JsFlutterConfiguration?); - - expect( - renderer.fontCollection.fontFallbackManager!.downloadQueue - .fallbackFontUrlPrefix, - 'http://my-special-fonts.com/', - ); - }); - test('will download Noto Sans Arabic if Arabic text is added', () async { - expect(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, - ['Roboto']); + setUp(() { + debugDisableFontFallbacks = false; + }); - // 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)); + /// Used to save and restore [ui.PlatformDispatcher.onPlatformMessage] after each test. + ui.PlatformMessageCallback? savedCallback; - await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); + final List downloadedFontFamilies = []; - expect(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, - contains('Noto Sans Arabic')); + 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; + }); - final ui.PictureRecorder recorder = ui.PictureRecorder(); - final ui.Canvas canvas = ui.Canvas(recorder); + tearDown(() { + downloadedFontFamilies.clear(); + ui.PlatformDispatcher.instance.onPlatformMessage = savedCallback; + }); - 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)); - - canvas.drawParagraph(paragraph, ui.Offset.zero); - await drawPictureUsingCurrentRenderer(recorder.endRecording()); - - await matchGoldenFile( - 'ui_font_fallback_arabic.png', - region: kDefaultRegion, - ); - // TODO(hterkelsen): https://github.com/flutter/flutter/issues/71520 - }); - - test( - 'will put the Noto Color Emoji font before other fallback fonts in the list', - () 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)); + 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?, + ); - await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); + expect( + renderer.fontCollection.fontFallbackManager!.downloadQueue.fallbackFontUrlPrefix, + 'http://my-special-fonts.com/', + ); + }); - expect(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, - ['Roboto', 'Noto Sans Arabic']); + test('will download Noto Sans Arabic if Arabic text is added', () async { + expect(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, [ + 'Roboto', + ]); - 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)); + // 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)); - await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); + await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); - expect( + expect( renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, - [ - 'Roboto', - 'Noto Color Emoji 9', - 'Noto Sans Arabic', - ]); - }); - - 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 - // fallback font. - ui.ParagraphBuilder pb = ui.ParagraphBuilder( - ui.ParagraphStyle(), - ); - pb.addText('Hello 😊'); - pb.build().layout(const ui.ParagraphConstraints(width: 1000)); + 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, - contains('Noto Color Emoji 9')); + 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)); - final ui.PictureRecorder recorder = ui.PictureRecorder(); - final ui.Canvas canvas = ui.Canvas(recorder); + canvas.drawParagraph(paragraph, ui.Offset.zero); + await drawPictureUsingCurrentRenderer(recorder.endRecording()); - 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 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, - ); + await matchGoldenFile('ui_font_fallback_arabic.png', region: kDefaultRegion); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/71520 + }); - // 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); - } - - /// 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(); - - final fallbackManager = renderer.fontCollection.fontFallbackManager!; - final oldLanguage = fallbackManager.debugUserPreferredLanguage; - if (userPreferredLanguage != null) { - fallbackManager.debugUserPreferredLanguage = userPreferredLanguage; - } + test('will put the Noto Color Emoji font before other fallback fonts in the list', () 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)); + + await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); + + expect(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, [ + 'Roboto', + 'Noto Sans Arabic', + ]); + + 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)); + + await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); + + expect(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, [ + 'Roboto', + 'Noto Color Emoji 9', + 'Noto Sans Arabic', + ]); + }); - // 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)); + test('will download Noto Color Emojis and Noto Symbols if no matching Noto Font', () async { + expect(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, [ + 'Roboto', + ]); - await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); - if (userPreferredLanguage != null) { - fallbackManager.debugUserPreferredLanguage = oldLanguage; - } + // 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)); - 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), - ); - } - - // 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', - ]); - }); - - test('can find glyph for 2/3 symbol', () async { - await checkDownloadedFamiliesForString('⅔', [ - 'Noto Sans', - ]); - }); - - // 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/148797 - test('can find Tibetan script glyphs (Dzongkha)', () async { - await checkDownloadedFamiliesForString('འཛམ་གླིང་སྤྱི་ཚོགས', [ - 'Noto Serif Tibetan', - ]); - }); - - // 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/157763 - test('prioritizes Noto Color Emoji over Noto Sans Symbols', () async { - await checkDownloadedFamilyForCharCode(0x1f3d5, 'Noto Color Emoji'); - }); - - // 0x700b is a CJK Unified Ideograph code point that exists in all of our - // CJK fonts. - - // 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'); - }); - - // 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'); - }); - - // 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'); - }); - - // Hong Kong - test('prioritizes Noto Sans HK for lang=zh-HK', () async { - await checkDownloadedFamilyForCharCode(0x700b, 'Noto Sans HK', userPreferredLanguage: 'zh-HK'); - }); - - // Japanese - test('prioritizes Noto Sans JP for lang=ja', () async { - await checkDownloadedFamilyForCharCode(0x700b, 'Noto Sans JP', userPreferredLanguage: 'ja'); - }); - - // Korean - test('prioritizes Noto Sans KR for lang=ko', () async { - await checkDownloadedFamilyForCharCode(0x700b, 'Noto Sans KR', userPreferredLanguage: 'ko'); - }); - - 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); - } - } + await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); + + expect( + renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, + contains('Noto Color Emoji 9'), + ); + + final ui.PictureRecorder recorder = ui.PictureRecorder(); + final ui.Canvas canvas = ui.Canvas(recorder); + + 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 matchGoldenFile('ui_font_fallback_emoji.png', region: kDefaultRegion); + // TODO(hterkelsen): https://github.com/flutter/flutter/issues/71520 }); - 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)}', - ); + /// 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); + } - // 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); + /// 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(); + + final fallbackManager = renderer.fontCollection.fontFallbackManager!; + final oldLanguage = fallbackManager.debugUserPreferredLanguage; + if (userPreferredLanguage != null) { + fallbackManager.debugUserPreferredLanguage = userPreferredLanguage; } - 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; + // 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)); } - }); - test('fallback fonts do not download when debugDisableFontFallbacks is set', () async { - debugDisableFontFallbacks = true; + // 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(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, ['Roboto']); + test('can find glyph for 2/3 symbol', () async { + await checkDownloadedFamiliesForString('⅔', ['Noto Sans']); + }); - // 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)); + // https://github.com/flutter/devtools/issues/6149 + test('can find glyph for treble clef', () async { + await checkDownloadedFamiliesForString('𝄞', ['Noto Music']); + }); - await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); + // 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/149616 + test('can find Ethiopic script glyphs ()', () async { + await checkDownloadedFamiliesForString('ኢትዮጵያ', ['Noto Sans Ethiopic']); + }); + + // https://github.com/flutter/flutter/issues/157763 + test('prioritizes Noto Color Emoji over Noto Sans Symbols', () async { + await checkDownloadedFamilyForCharCode(0x1f3d5, 'Noto Color Emoji'); + }); + + // 0x700b is a CJK Unified Ideograph code point that exists in all of our + // CJK fonts. + + // 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', + ); + }); + + // 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', + ); + }); + + // 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', + ); + }); + + // Hong Kong + test('prioritizes Noto Sans HK for lang=zh-HK', () async { + await checkDownloadedFamilyForCharCode( + 0x700b, + 'Noto Sans HK', + userPreferredLanguage: 'zh-HK', + ); + }); - // Make sure we didn't download the fallback font. - expect(renderer.fontCollection.fontFallbackManager!.globalFontFallbacks, - isNot(contains('Noto Color Emoji 9'))); - }); + // Japanese + test('prioritizes Noto Sans JP for lang=ja', () async { + await checkDownloadedFamilyForCharCode(0x700b, 'Noto Sans JP', userPreferredLanguage: 'ja'); + }); - test('only woff2 fonts are used for fallback', () { - final fonts = getFallbackFontList(); + // Korean + test('prioritizes Noto Sans KR for lang=ko', () async { + await checkDownloadedFamilyForCharCode(0x700b, 'Noto Sans KR', userPreferredLanguage: 'ko'); + }); - for (final font in fonts) { + 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); + } + } + }); + + 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( - font.url, - endsWith('.woff2'), - reason: 'Expected all fallback fonts to be WOFF2, but found ' - '"${font.name}" was not a WOFF2 font: ${font.url}', + testedFonts, + unorderedEquals(allFonts), + reason: + 'Found mismatch in fonts.\n' + 'Missing fonts: ${allFonts.difference(testedFonts)}\n' + 'Extra fonts: ${testedFonts.difference(allFonts)}', ); - } - }); - }, - // HTML renderer doesn't use the fallback font manager. - skip: isHtml, - timeout: const Timeout.factor(4)); + + // 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('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}', + ); + } + }); + }, + // HTML renderer doesn't use the fallback font manager. + skip: isHtml, + timeout: const Timeout.factor(4), + ); } diff --git a/lib/web_ui/test/ui/filters_test.dart b/lib/web_ui/test/ui/filters_test.dart index 980e53e9bb615..3a736c9888f37 100644 --- a/lib/web_ui/test/ui/filters_test.dart +++ b/lib/web_ui/test/ui/filters_test.dart @@ -19,16 +19,13 @@ void main() { } Future testMain() async { - setUpUnitTests( - withImplicitView: true, - setUpTestViewDimensions: false, - ); + setUpUnitTests(withImplicitView: true, setUpTestViewDimensions: false); const ui.Rect region = ui.Rect.fromLTWH(0, 0, 128, 128); Future drawTestImageWithPaint(ui.Paint paint) async { final ui.Codec codec = await renderer.instantiateImageCodecFromUrl( - Uri(path: '/test_images/mandrill_128.png') + Uri(path: '/test_images/mandrill_128.png'), ); expect(codec.frameCount, 1); @@ -38,55 +35,52 @@ Future testMain() async { expect(image.height, 128); final ui.PictureRecorder recorder = ui.PictureRecorder(); final ui.Canvas canvas = ui.Canvas(recorder, region); - canvas.drawImage( - image, - ui.Offset.zero, - paint, - ); + canvas.drawImage(image, ui.Offset.zero, paint); await drawPictureUsingCurrentRenderer(recorder.endRecording()); } test('blur filter', () async { - await drawTestImageWithPaint(ui.Paint()..imageFilter = ui.ImageFilter.blur( - sigmaX: 5.0, - sigmaY: 5.0, - )); + await drawTestImageWithPaint( + ui.Paint()..imageFilter = ui.ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0), + ); await matchGoldenFile('ui_filter_blur_imagefilter.png', region: region); }); test('dilate filter', () async { - await drawTestImageWithPaint(ui.Paint()..imageFilter = ui.ImageFilter.dilate( - radiusX: 5.0, - radiusY: 5.0, - )); + await drawTestImageWithPaint( + 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 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(ui.Paint()..imageFilter = ui.ImageFilter.matrix( - Matrix4.rotationZ(math.pi / 6).toFloat64(), - filterQuality: ui.FilterQuality.high, - )); + await drawTestImageWithPaint( + ui.Paint() + ..imageFilter = ui.ImageFilter.matrix( + Matrix4.rotationZ(math.pi / 6).toFloat64(), + filterQuality: ui.FilterQuality.high, + ), + ); await matchGoldenFile('ui_filter_matrix_imagefilter.png', region: region); }); test('resizing matrix filter', () async { - await drawTestImageWithPaint(ui.Paint() - ..imageFilter = ui.ImageFilter.matrix( - Matrix4.diagonal3Values(0.5, 0.5, 1).toFloat64(), - filterQuality: ui.FilterQuality.high, - )); - await matchGoldenFile('ui_filter_matrix_imagefilter_scaled.png', - region: region); + await drawTestImageWithPaint( + ui.Paint() + ..imageFilter = ui.ImageFilter.matrix( + Matrix4.diagonal3Values(0.5, 0.5, 1).toFloat64(), + filterQuality: ui.FilterQuality.high, + ), + ); + await matchGoldenFile('ui_filter_matrix_imagefilter_scaled.png', region: region); }); test('composed filters', () async { @@ -95,10 +89,7 @@ Future testMain() async { Matrix4.rotationZ(math.pi / 6).toFloat64(), filterQuality: ui.FilterQuality.high, ), - inner: ui.ImageFilter.blur( - sigmaX: 5.0, - sigmaY: 5.0, - ) + inner: ui.ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0), ); await drawTestImageWithPaint(ui.Paint()..imageFilter = filter); await matchGoldenFile('ui_filter_composed_imagefilters.png', region: region); @@ -106,14 +97,8 @@ Future testMain() async { test('compose with colorfilter', () async { final ui.ImageFilter filter = ui.ImageFilter.compose( - outer: const ui.ColorFilter.mode( - ui.Color.fromRGBO(0, 0, 255, 128), - ui.BlendMode.srcOver, - ), - inner: ui.ImageFilter.blur( - sigmaX: 5.0, - sigmaY: 5.0, - ) + outer: const ui.ColorFilter.mode(ui.Color.fromRGBO(0, 0, 255, 128), ui.BlendMode.srcOver), + inner: ui.ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0), ); await drawTestImageWithPaint(ui.Paint()..imageFilter = filter); await matchGoldenFile('ui_filter_composed_colorfilter.png', region: region); @@ -126,7 +111,10 @@ Future testMain() async { ); await drawTestImageWithPaint(ui.Paint()..imageFilter = colorFilter); await matchGoldenFile('ui_filter_colorfilter_as_imagefilter.png', region: region); - expect(colorFilter.toString(), 'ColorFilter.mode(${const ui.Color(0x800000ff)}, BlendMode.srcOver)'); + expect( + colorFilter.toString(), + 'ColorFilter.mode(${const ui.Color(0x800000ff)}, BlendMode.srcOver)', + ); }); test('mode color filter', () async { @@ -136,7 +124,10 @@ Future testMain() async { ); await drawTestImageWithPaint(ui.Paint()..colorFilter = colorFilter); await matchGoldenFile('ui_filter_mode_colorfilter.png', region: region); - expect(colorFilter.toString(), 'ColorFilter.mode(${const ui.Color(0x800000ff)}, BlendMode.srcOver)'); + expect( + colorFilter.toString(), + 'ColorFilter.mode(${const ui.Color(0x800000ff)}, BlendMode.srcOver)', + ); }); test('linearToSRGBGamma color filter', () async { @@ -155,10 +146,26 @@ Future testMain() async { test('matrix color filter', () async { const ui.ColorFilter sepia = ui.ColorFilter.matrix([ - 0.393, 0.769, 0.189, 0, 0, - 0.349, 0.686, 0.168, 0, 0, - 0.272, 0.534, 0.131, 0, 0, - 0, 0, 0, 1, 0, + 0.393, + 0.769, + 0.189, + 0, + 0, + 0.349, + 0.686, + 0.168, + 0, + 0, + 0.272, + 0.534, + 0.131, + 0, + 0, + 0, + 0, + 0, + 1, + 0, ]); await drawTestImageWithPaint(ui.Paint()..colorFilter = sepia); await matchGoldenFile('ui_filter_matrix_colorfilter.png', region: region); @@ -172,15 +179,33 @@ Future testMain() async { test('invert colors with color filter', () async { const ui.ColorFilter sepia = ui.ColorFilter.matrix([ - 0.393, 0.769, 0.189, 0, 0, - 0.349, 0.686, 0.168, 0, 0, - 0.272, 0.534, 0.131, 0, 0, - 0, 0, 0, 1, 0, + 0.393, + 0.769, + 0.189, + 0, + 0, + 0.349, + 0.686, + 0.168, + 0, + 0, + 0.272, + 0.534, + 0.131, + 0, + 0, + 0, + 0, + 0, + 1, + 0, ]); - await drawTestImageWithPaint(ui.Paint() - ..invertColors = true - ..colorFilter = sepia); + await drawTestImageWithPaint( + ui.Paint() + ..invertColors = true + ..colorFilter = sepia, + ); await matchGoldenFile('ui_filter_invert_colors_with_colorfilter.png', region: region); }); @@ -202,14 +227,22 @@ Future testMain() async { final double centerY = height * 0.5; final double bottom = height.toDouble(); - canvas.drawRect(ui.Rect.fromLTRB(left, top, centerX, centerY), - ui.Paint()..color = const ui.Color.fromARGB(255, 0, 255, 0)); - canvas.drawRect(ui.Rect.fromLTRB(centerX, top, right, centerY), - ui.Paint()..color = const ui.Color.fromARGB(255, 255, 255, 0)); - canvas.drawRect(ui.Rect.fromLTRB(left, centerY, centerX, bottom), - ui.Paint()..color = const ui.Color.fromARGB(255, 0, 0, 255)); - canvas.drawRect(ui.Rect.fromLTRB(centerX, centerY, right, bottom), - ui.Paint()..color = const ui.Color.fromARGB(255, 255, 0, 0)); + canvas.drawRect( + ui.Rect.fromLTRB(left, top, centerX, centerY), + ui.Paint()..color = const ui.Color.fromARGB(255, 0, 255, 0), + ); + canvas.drawRect( + ui.Rect.fromLTRB(centerX, top, right, centerY), + ui.Paint()..color = const ui.Color.fromARGB(255, 255, 255, 0), + ); + canvas.drawRect( + ui.Rect.fromLTRB(left, centerY, centerX, bottom), + ui.Paint()..color = const ui.Color.fromARGB(255, 0, 0, 255), + ); + canvas.drawRect( + ui.Rect.fromLTRB(centerX, centerY, right, bottom), + ui.Paint()..color = const ui.Color.fromARGB(255, 255, 0, 0), + ); final picture = recorder.endRecording(); return picture.toImageSync(width, height); @@ -227,25 +260,21 @@ Future testMain() async { final gradient = ui.Gradient.linear( zone.topLeft, zone.bottomRight, - [ - const ui.Color.fromARGB(255, 0, 255, 0), - const ui.Color.fromARGB(255, 0, 0, 255), - ], + [const ui.Color.fromARGB(255, 0, 255, 0), const ui.Color.fromARGB(255, 0, 0, 255)], [0, 1], ); final filter = ui.ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0, tileMode: tileMode); final ui.Paint white = ui.Paint()..color = const ui.Color.fromARGB(255, 255, 255, 255); final ui.Paint grey = ui.Paint()..color = const ui.Color.fromARGB(255, 127, 127, 127); final ui.Paint unblurredFill = ui.Paint()..shader = gradient; - final ui.Paint blurredFill = ui.Paint.from(unblurredFill) - ..imageFilter = filter; - final ui.Paint unblurredStroke = ui.Paint.from(unblurredFill) - ..style = ui.PaintingStyle.stroke - ..strokeCap = ui.StrokeCap.round - ..strokeJoin = ui.StrokeJoin.round - ..strokeWidth = 10; - final ui.Paint blurredStroke = ui.Paint.from(unblurredStroke) - ..imageFilter = filter; + final ui.Paint blurredFill = ui.Paint.from(unblurredFill)..imageFilter = filter; + final ui.Paint unblurredStroke = + ui.Paint.from(unblurredFill) + ..style = ui.PaintingStyle.stroke + ..strokeCap = ui.StrokeCap.round + ..strokeJoin = ui.StrokeJoin.round + ..strokeWidth = 10; + final ui.Paint blurredStroke = ui.Paint.from(unblurredStroke)..imageFilter = filter; final ui.Image image = makeCheckerBoard(20, 20); const ui.Rect imageBounds = ui.Rect.fromLTRB(0, 0, 20, 20); const ui.Rect imageCenter = ui.Rect.fromLTRB(5, 5, 9, 9); @@ -262,7 +291,7 @@ Future testMain() async { ]; final vertices = ui.Vertices( ui.VertexMode.triangles, - [ + [ zone.topLeft, zone.bottomRight, zone.topRight, @@ -332,39 +361,46 @@ Future testMain() async { const double pad = 10; final double offset = arena.width + pad; const int columns = 5; - final ui.Rect pairArena = ui.Rect.fromLTRB(arena.left - 3, arena.top - 3, - arena.right + 3, arena.bottom + offset + 3); + final ui.Rect pairArena = ui.Rect.fromLTRB( + arena.left - 3, + arena.top - 3, + arena.right + 3, + arena.bottom + offset + 3, + ); final List renderers = [ - (canvas, fill, stroke) { + (canvas, fill, stroke) { canvas.saveLayer(zone.inflate(5), fill); canvas.drawLine(zone.topLeft, zone.bottomRight, unblurredStroke); canvas.drawLine(zone.topRight, zone.bottomLeft, unblurredStroke); canvas.restore(); }, - (canvas, fill, stroke) => canvas.drawLine(zone.topLeft, zone.bottomRight, stroke), - (canvas, fill, stroke) => canvas.drawRect(zone, fill), - (canvas, fill, stroke) => canvas.drawOval(ovalZone, fill), - (canvas, fill, stroke) => canvas.drawCircle(zone.center, zone.width * 0.5, fill), - (canvas, fill, stroke) => canvas.drawRRect(ui.RRect.fromRectXY(zone, 4.0, 4.0), fill), - (canvas, fill, stroke) => canvas.drawDRRect( - ui.RRect.fromRectXY(zone, 4.0, 4.0), - ui.RRect.fromRectXY(zone.deflate(4), 4.0, 4.0), - fill), - (canvas, fill, stroke) => canvas.drawArc(zone, math.pi / 4, math.pi * 3 / 2, true, fill), - (canvas, fill, stroke) => canvas.drawPath(ui.Path() - ..moveTo(zone.left, zone.top) - ..lineTo(zone.right, zone.top) - ..lineTo(zone.left, zone.bottom) - ..lineTo(zone.right, zone.bottom), - stroke), - (canvas, fill, stroke) => canvas.drawImage(image, zone.topLeft, fill), - (canvas, fill, stroke) => canvas.drawImageRect(image, imageBounds, zone.inflate(2), fill), - (canvas, fill, stroke) => canvas.drawImageNine(image, imageCenter, zone.inflate(2), fill), - (canvas, fill, stroke) => canvas.drawPoints(ui.PointMode.points, points, stroke), - (canvas, fill, stroke) => canvas.drawVertices(vertices, ui.BlendMode.dstOver, fill), - (canvas, fill, stroke) => canvas.drawAtlas(image, atlasXforms, atlasRects, - null, null, null, fill), + (canvas, fill, stroke) => canvas.drawLine(zone.topLeft, zone.bottomRight, stroke), + (canvas, fill, stroke) => canvas.drawRect(zone, fill), + (canvas, fill, stroke) => canvas.drawOval(ovalZone, fill), + (canvas, fill, stroke) => canvas.drawCircle(zone.center, zone.width * 0.5, fill), + (canvas, fill, stroke) => canvas.drawRRect(ui.RRect.fromRectXY(zone, 4.0, 4.0), fill), + (canvas, fill, stroke) => canvas.drawDRRect( + ui.RRect.fromRectXY(zone, 4.0, 4.0), + ui.RRect.fromRectXY(zone.deflate(4), 4.0, 4.0), + fill, + ), + (canvas, fill, stroke) => canvas.drawArc(zone, math.pi / 4, math.pi * 3 / 2, true, fill), + (canvas, fill, stroke) => canvas.drawPath( + ui.Path() + ..moveTo(zone.left, zone.top) + ..lineTo(zone.right, zone.top) + ..lineTo(zone.left, zone.bottom) + ..lineTo(zone.right, zone.bottom), + stroke, + ), + (canvas, fill, stroke) => canvas.drawImage(image, zone.topLeft, fill), + (canvas, fill, stroke) => canvas.drawImageRect(image, imageBounds, zone.inflate(2), fill), + (canvas, fill, stroke) => canvas.drawImageNine(image, imageCenter, zone.inflate(2), fill), + (canvas, fill, stroke) => canvas.drawPoints(ui.PointMode.points, points, stroke), + (canvas, fill, stroke) => canvas.drawVertices(vertices, ui.BlendMode.dstOver, fill), + (canvas, fill, stroke) => + canvas.drawAtlas(image, atlasXforms, atlasRects, null, null, null, fill), ]; canvas.save(); @@ -395,38 +431,62 @@ 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 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 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 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 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 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 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 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 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 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, + ); } diff --git a/lib/web_ui/test/ui/font_collection_test.dart b/lib/web_ui/test/ui/font_collection_test.dart index ee9e975166d14..f5e2e6e3710f1 100644 --- a/lib/web_ui/test/ui/font_collection_test.dart +++ b/lib/web_ui/test/ui/font_collection_test.dart @@ -30,21 +30,24 @@ Future testMain() async { fakeAssetManager.popAssetScope(testScope); }); - test('Loading valid font from data succeeds without family name (except in HTML renderer)', () 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 - ); - }); + test( + 'Loading valid font from data succeeds without family name (except in HTML renderer)', + () 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 + ); + }, + ); test('Loading valid font from data succeeds with family name', () async { final FlutterFontCollection collection = renderer.fontCollection; final ByteBuffer ahemData = await httpFetchByteBuffer('/assets/fonts/ahem.ttf'); expect( await collection.loadFontFromList(ahemData.asUint8List(), fontFamily: 'FamilyName'), - true + true, ); }); @@ -52,8 +55,11 @@ Future testMain() async { final FlutterFontCollection collection = renderer.fontCollection; final List invalidFontData = utf8.encode('This is not valid font data'); expect( - await collection.loadFontFromList(Uint8List.fromList(invalidFontData), fontFamily: 'FamilyName'), - false + await collection.loadFontFromList( + Uint8List.fromList(invalidFontData), + fontFamily: 'FamilyName', + ), + false, ); }); @@ -63,20 +69,16 @@ Future testMain() async { testScope.setAssetPassthrough(ahemFontUrl); final FlutterFontCollection collection = renderer.fontCollection; - final AssetFontsResult result = await collection.loadAssetFonts(FontManifest([ - FontFamily(robotoFontFamily, [ - FontAsset(robotoVariableFontUrl, {}), - FontAsset(robotoTestFontUrl, {'weight': 'bold'}), - ]), - FontFamily(ahemFontFamily, [ - FontAsset(ahemFontUrl, {}) + final AssetFontsResult result = await collection.loadAssetFonts( + FontManifest([ + FontFamily(robotoFontFamily, [ + FontAsset(robotoVariableFontUrl, {}), + FontAsset(robotoTestFontUrl, {'weight': 'bold'}), + ]), + FontFamily(ahemFontFamily, [FontAsset(ahemFontUrl, {})]), ]), - ])); - expect(result.loadedFonts, [ - robotoVariableFontUrl, - robotoTestFontUrl, - ahemFontUrl, - ]); + ); + expect(result.loadedFonts, [robotoVariableFontUrl, robotoTestFontUrl, ahemFontUrl]); expect(result.fontFailures, isEmpty); }); @@ -87,19 +89,16 @@ Future testMain() async { const String invalidFontUrl = 'assets/invalid_font_url.ttf'; final FlutterFontCollection collection = renderer.fontCollection; - final AssetFontsResult result = await collection.loadAssetFonts(FontManifest([ - FontFamily(robotoFontFamily, [ - FontAsset(robotoVariableFontUrl, {}), - FontAsset(robotoTestFontUrl, {'weight': 'bold'}), - ]), - FontFamily(ahemFontFamily, [ - FontAsset(invalidFontUrl, {}) + final AssetFontsResult result = await collection.loadAssetFonts( + FontManifest([ + FontFamily(robotoFontFamily, [ + FontAsset(robotoVariableFontUrl, {}), + FontAsset(robotoTestFontUrl, {'weight': 'bold'}), + ]), + FontFamily(ahemFontFamily, [FontAsset(invalidFontUrl, {})]), ]), - ])); - expect(result.loadedFonts, [ - robotoVariableFontUrl, - robotoTestFontUrl, - ]); + ); + 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 @@ -123,7 +122,7 @@ Future testMain() async { url: url, status: 200, payload: MockHttpFetchPayload( - byteBuffer: stringAsUtf8Data('this is invalid data').buffer + byteBuffer: stringAsUtf8Data('this is invalid data').buffer, ), ); } @@ -131,19 +130,16 @@ Future testMain() async { }; final FlutterFontCollection collection = renderer.fontCollection; - final AssetFontsResult result = await collection.loadAssetFonts(FontManifest([ - FontFamily(robotoFontFamily, [ - FontAsset(robotoVariableFontUrl, {}), - FontAsset(robotoTestFontUrl, {'weight': 'bold'}), + final AssetFontsResult result = await collection.loadAssetFonts( + FontManifest([ + FontFamily(robotoFontFamily, [ + FontAsset(robotoVariableFontUrl, {}), + FontAsset(robotoTestFontUrl, {'weight': 'bold'}), + ]), + FontFamily(ahemFontFamily, [FontAsset(invalidFontUrl, {})]), ]), - FontFamily(ahemFontFamily, [ - FontAsset(invalidFontUrl, {}) - ]), - ])); - expect(result.loadedFonts, [ - robotoVariableFontUrl, - robotoTestFontUrl, - ]); + ); + expect(result.loadedFonts, [robotoVariableFontUrl, robotoTestFontUrl]); expect(result.fontFailures, hasLength(1)); if (isHtml) { // The HTML renderer doesn't have a way to differentiate invalid data @@ -155,7 +151,9 @@ Future testMain() async { }); test('Font manifest with numeric and string descriptor values parses correctly', () async { - testScope.setAsset('FontManifest.json', stringAsUtf8Data(r''' + testScope.setAsset( + 'FontManifest.json', + stringAsUtf8Data(r''' [ { "family": "FakeFont", @@ -168,7 +166,8 @@ Future testMain() async { ] } ] -''')); +'''), + ); final FontManifest manifest = await fetchFontManifest(fakeAssetManager); expect(manifest.families.length, 1); diff --git a/lib/web_ui/test/ui/fragment_shader_test.dart b/lib/web_ui/test/ui/fragment_shader_test.dart index 307989fef528e..0c83994615473 100644 --- a/lib/web_ui/test/ui/fragment_shader_test.dart +++ b/lib/web_ui/test/ui/fragment_shader_test.dart @@ -42,20 +42,14 @@ const String kVoronoiShaderSksl = r''' '''; Future testMain() async { - setUpUnitTests( - withImplicitView: true, - setUpTestViewDimensions: false, - ); + setUpUnitTests(withImplicitView: true, setUpTestViewDimensions: false); const ui.Rect region = ui.Rect.fromLTWH(0, 0, 300, 300); late FakeAssetScope assetScope; setUp(() { assetScope = fakeAssetManager.pushAssetScope(); - assetScope.setAsset( - 'voronoi_shader', - ByteData.sublistView(utf8.encode(kVoronoiShaderSksl)) - ); + assetScope.setAsset('voronoi_shader', ByteData.sublistView(utf8.encode(kVoronoiShaderSksl))); }); tearDown(() { diff --git a/lib/web_ui/test/ui/frame_timings_test.dart b/lib/web_ui/test/ui/frame_timings_test.dart index 62f83b71d4d09..f0d39bc596430 100644 --- a/lib/web_ui/test/ui/frame_timings_test.dart +++ b/lib/web_ui/test/ui/frame_timings_test.dart @@ -21,7 +21,8 @@ void testMain() { }); test('collects frame timings', () async { - final EnginePlatformDispatcher dispatcher = ui.PlatformDispatcher.instance as EnginePlatformDispatcher; + final EnginePlatformDispatcher dispatcher = + ui.PlatformDispatcher.instance as EnginePlatformDispatcher; List? timings; dispatcher.onReportTimings = (List data) { timings = data; diff --git a/lib/web_ui/test/ui/gradient_golden_test.dart b/lib/web_ui/test/ui/gradient_golden_test.dart index 2587e99c067f0..dd065978fc4b1 100644 --- a/lib/web_ui/test/ui/gradient_golden_test.dart +++ b/lib/web_ui/test/ui/gradient_golden_test.dart @@ -18,10 +18,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - withImplicitView: true, - setUpTestViewDimensions: false, - ); + setUpUnitTests(withImplicitView: true, setUpTestViewDimensions: false); const Rect region = Rect.fromLTWH(0, 0, 300, 300); @@ -35,14 +32,10 @@ Future testMain() async { ..shader = Gradient.linear( const Offset(50, 50), const Offset(250, 250), - [ - const Color(0xFFFF0000), - const Color(0xFF00FF00), - const Color(0xFF0000FF), - ], + [const Color(0xFFFF0000), const Color(0xFF00FF00), const Color(0xFF0000FF)], [0.0, 0.5, 1.0], - ) - ); + ), + ); await drawPictureUsingCurrentRenderer(recorder.endRecording()); @@ -58,14 +51,10 @@ Future testMain() async { ..shader = Gradient.radial( const Offset(150, 150), 100, - [ - const Color(0xFFFF0000), - const Color(0xFF00FF00), - const Color(0xFF0000FF), - ], + [const Color(0xFFFF0000), const Color(0xFF00FF00), const Color(0xFF0000FF)], [0.0, 0.5, 1.0], - ) - ); + ), + ); await drawPictureUsingCurrentRenderer(recorder.endRecording()); @@ -81,18 +70,14 @@ Future testMain() async { ..shader = Gradient.radial( const Offset(200, 200), 100, - [ - const Color(0xFFFF0000), - const Color(0xFF00FF00), - const Color(0xFF0000FF), - ], + [const Color(0xFFFF0000), const Color(0xFF00FF00), const Color(0xFF0000FF)], [0.0, 0.5, 1.0], TileMode.clamp, null, const Offset(50, 50), 5, - ) - ); + ), + ); await drawPictureUsingCurrentRenderer(recorder.endRecording()); @@ -107,17 +92,13 @@ Future testMain() async { Paint() ..shader = Gradient.sweep( const Offset(150, 150), - [ - const Color(0xFFFF0000), - const Color(0xFF00FF00), - const Color(0xFF0000FF), - ], + [const Color(0xFFFF0000), const Color(0xFF00FF00), const Color(0xFF0000FF)], [0.0, 0.5, 1.0], TileMode.clamp, math.pi / 3.0, 4.0 * math.pi / 3.0, - ) - ); + ), + ); await drawPictureUsingCurrentRenderer(recorder.endRecording()); diff --git a/lib/web_ui/test/ui/gradient_test.dart b/lib/web_ui/test/ui/gradient_test.dart index 3ae0f886a0793..9238887ad20f0 100644 --- a/lib/web_ui/test/ui/gradient_test.dart +++ b/lib/web_ui/test/ui/gradient_test.dart @@ -19,59 +19,60 @@ Future testMain() async { test('Gradient.radial with no focal point', () { expect( Gradient.radial( - Offset.zero, - 5.0, - [const Color(0xFFFFFFFF), const Color(0xFFFFFFFF)], - [0.0, 1.0], - TileMode.mirror), + Offset.zero, + 5.0, + [const Color(0xFFFFFFFF), const Color(0xFFFFFFFF)], + [0.0, 1.0], + TileMode.mirror, + ), isNotNull, ); }); // this is just a radial gradient, focal point is discarded. - test('radial center and focal == Offset.zero and focalRadius == 0.0 is ok', - () { + test('radial center and focal == Offset.zero and focalRadius == 0.0 is ok', () { expect( - () => Gradient.radial( - Offset.zero, - 0.0, - [const Color(0xFFFFFFFF), const Color(0xFFFFFFFF)], - [0.0, 1.0], - TileMode.mirror, - null, - Offset.zero, - ), - isNotNull); + () => Gradient.radial( + Offset.zero, + 0.0, + [const Color(0xFFFFFFFF), const Color(0xFFFFFFFF)], + [0.0, 1.0], + TileMode.mirror, + null, + Offset.zero, + ), + isNotNull, + ); }); test('radial center != focal and focalRadius == 0.0 is ok', () { expect( - () => Gradient.radial( - Offset.zero, - 0.0, - [const Color(0xFFFFFFFF), const Color(0xFFFFFFFF)], - [0.0, 1.0], - TileMode.mirror, - null, - const Offset(2.0, 2.0), - ), - isNotNull); + () => Gradient.radial( + Offset.zero, + 0.0, + [const Color(0xFFFFFFFF), const Color(0xFFFFFFFF)], + [0.0, 1.0], + TileMode.mirror, + null, + const Offset(2.0, 2.0), + ), + isNotNull, + ); }); // this would result in div/0 on skia side. - test('radial center and focal == Offset.zero and focalRadius != 0.0 assert', - () { + test('radial center and focal == Offset.zero and focalRadius != 0.0 assert', () { expect( () => Gradient.radial( - Offset.zero, - 0.0, - [const Color(0xFFFFFFFF), const Color(0xFFFFFFFF)], - [0.0, 1.0], - TileMode.mirror, - null, - Offset.zero, - 1.0, - ), + Offset.zero, + 0.0, + [const Color(0xFFFFFFFF), const Color(0xFFFFFFFF)], + [0.0, 1.0], + TileMode.mirror, + null, + Offset.zero, + 1.0, + ), throwsA(const TypeMatcher()), ); }); @@ -81,39 +82,27 @@ Future testMain() async { () => Gradient.linear( Offset.zero, const Offset(1.0, 1.0), - const [ - Color(0x11111111), - Color(0x22222222), - Color(0x33333333), - ], + const [Color(0x11111111), Color(0x22222222), Color(0x33333333)], const [0.0, 1.0], ), - throwsArgumentError + throwsArgumentError, ); expect( () => Gradient.radial( Offset.zero, 5.0, - const [ - Color(0x11111111), - Color(0x22222222), - Color(0x33333333), - ], + const [Color(0x11111111), Color(0x22222222), Color(0x33333333)], const [0.0, 1.0], ), - throwsArgumentError + throwsArgumentError, ); expect( () => Gradient.sweep( Offset.zero, - const [ - Color(0x11111111), - Color(0x22222222), - Color(0x33333333), - ], + const [Color(0x11111111), Color(0x22222222), Color(0x33333333)], const [0.0, 1.0], ), - throwsArgumentError + throwsArgumentError, ); }); } diff --git a/lib/web_ui/test/ui/image/html_image_element_codec_test.dart b/lib/web_ui/test/ui/image/html_image_element_codec_test.dart index 772aeeb31cabb..ade463ffbe8fc 100644 --- a/lib/web_ui/test/ui/image/html_image_element_codec_test.dart +++ b/lib/web_ui/test/ui/image/html_image_element_codec_test.dart @@ -86,9 +86,11 @@ Future testMain() async { test('provides image loading progress', () async { final StringBuffer buffer = StringBuffer(); final HtmlImageElementCodec codec = createImageElementCodec( - 'sample_image1.png', chunkCallback: (int loaded, int total) { - buffer.write('$loaded/$total,'); - }); + 'sample_image1.png', + chunkCallback: (int loaded, int total) { + buffer.write('$loaded/$total,'); + }, + ); await codec.getNextFrame(); expect(buffer.toString(), '0/100,100/100,'); }); @@ -97,16 +99,17 @@ Future testMain() async { /// https://github.com/flutter/flutter/issues/66412 test('Returns nonzero natural width/height', () async { final HtmlImageElementCodec codec = createImageElementCodec( - 'data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHZpZXdCb3g9I' - 'jAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dG' - 'l0bGU+QWJzdHJhY3QgaWNvbjwvdGl0bGU+PHBhdGggZD0iTTEyIDBjOS42MDEgMCAx' - 'MiAyLjM5OSAxMiAxMiAwIDkuNjAxLTIuMzk5IDEyLTEyIDEyLTkuNjAxIDAtMTItMi' - '4zOTktMTItMTJDMCAyLjM5OSAyLjM5OSAwIDEyIDB6bS0xLjk2OSAxOC41NjRjMi41' - 'MjQuMDAzIDQuNjA0LTIuMDcgNC42MDktNC41OTUgMC0yLjUyMS0yLjA3NC00LjU5NS' - '00LjU5NS00LjU5NVM1LjQ1IDExLjQ0OSA1LjQ1IDEzLjk2OWMwIDIuNTE2IDIuMDY1' - 'IDQuNTg4IDQuNTgxIDQuNTk1em04LjM0NC0uMTg5VjUuNjI1SDUuNjI1djIuMjQ3aD' - 'EwLjQ5OHYxMC41MDNoMi4yNTJ6bS04LjM0NC02Ljc0OGEyLjM0MyAyLjM0MyAwIDEx' - 'LS4wMDIgNC42ODYgMi4zNDMgMi4zNDMgMCAwMS4wMDItNC42ODZ6Ii8+PC9zdmc+'); + 'data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHZpZXdCb3g9I' + 'jAgMCAyNCAyNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dG' + 'l0bGU+QWJzdHJhY3QgaWNvbjwvdGl0bGU+PHBhdGggZD0iTTEyIDBjOS42MDEgMCAx' + 'MiAyLjM5OSAxMiAxMiAwIDkuNjAxLTIuMzk5IDEyLTEyIDEyLTkuNjAxIDAtMTItMi' + '4zOTktMTItMTJDMCAyLjM5OSAyLjM5OSAwIDEyIDB6bS0xLjk2OSAxOC41NjRjMi41' + 'MjQuMDAzIDQuNjA0LTIuMDcgNC42MDktNC41OTUgMC0yLjUyMS0yLjA3NC00LjU5NS' + '00LjU5NS00LjU5NVM1LjQ1IDExLjQ0OSA1LjQ1IDEzLjk2OWMwIDIuNTE2IDIuMDY1' + 'IDQuNTg4IDQuNTgxIDQuNTk1em04LjM0NC0uMTg5VjUuNjI1SDUuNjI1djIuMjQ3aD' + 'EwLjQ5OHYxMC41MDNoMi4yNTJ6bS04LjM0NC02Ljc0OGEyLjM0MyAyLjM0MyAwIDEx' + 'LS4wMDIgNC42ODYgMi4zNDMgMi4zNDMgMCAwMS4wMDItNC42ODZ6Ii8+PC9zdmc+', + ); final ui.FrameInfo frameInfo = await codec.getNextFrame(); expect(frameInfo.image.width, isNot(0)); }); @@ -130,10 +133,14 @@ Future testMain() async { test('provides image loading progress from web', () async { final Uri uri = Uri.base.resolve('sample_image1.png'); final StringBuffer buffer = StringBuffer(); - final HtmlImageElementCodec codec = await ui_web - .createImageCodecFromUrl(uri, chunkCallback: (int loaded, int total) { - buffer.write('$loaded/$total,'); - }) as HtmlImageElementCodec; + final HtmlImageElementCodec codec = + await ui_web.createImageCodecFromUrl( + uri, + chunkCallback: (int loaded, int total) { + buffer.write('$loaded/$total,'); + }, + ) + as HtmlImageElementCodec; await codec.getNextFrame(); expect(buffer.toString(), '0/100,100/100,'); }); diff --git a/lib/web_ui/test/ui/image_golden_test.dart b/lib/web_ui/test/ui/image_golden_test.dart index 0ac49d2ab09d5..1a81b1ef848ea 100644 --- a/lib/web_ui/test/ui/image_golden_test.dart +++ b/lib/web_ui/test/ui/image_golden_test.dart @@ -61,18 +61,12 @@ void main() { } Future testMain() async { - setUpUnitTests( - withImplicitView: true, - setUpTestViewDimensions: false, - ); + setUpUnitTests(withImplicitView: true, setUpTestViewDimensions: false); late FakeAssetScope assetScope; setUp(() { assetScope = fakeAssetManager.pushAssetScope(); - assetScope.setAsset( - 'glitch_shader', - ByteData.sublistView(utf8.encode(kGlitchShaderSksl)) - ); + assetScope.setAsset('glitch_shader', ByteData.sublistView(utf8.encode(kGlitchShaderSksl))); }); tearDown(() { @@ -107,9 +101,21 @@ Future testMain() async { final ui.PictureRecorder recorder = ui.PictureRecorder(); final ui.Canvas canvas = ui.Canvas(recorder, drawRegion); canvas.drawImage(image, ui.Offset.zero, ui.Paint()..filterQuality = ui.FilterQuality.none); - canvas.drawImage(image, const ui.Offset(150, 0), ui.Paint()..filterQuality = ui.FilterQuality.low); - canvas.drawImage(image, const ui.Offset(0, 150), ui.Paint()..filterQuality = ui.FilterQuality.medium); - canvas.drawImage(image, const ui.Offset(150, 150), ui.Paint()..filterQuality = ui.FilterQuality.high); + canvas.drawImage( + image, + const ui.Offset(150, 0), + ui.Paint()..filterQuality = ui.FilterQuality.low, + ); + canvas.drawImage( + image, + const ui.Offset(0, 150), + ui.Paint()..filterQuality = ui.FilterQuality.medium, + ); + canvas.drawImage( + image, + const ui.Offset(150, 150), + ui.Paint()..filterQuality = ui.FilterQuality.high, + ); await drawPictureUsingCurrentRenderer(recorder.endRecording()); @@ -126,25 +132,25 @@ Future testMain() async { image, srcRect, const ui.Rect.fromLTRB(0, 0, 150, 150), - ui.Paint()..filterQuality = ui.FilterQuality.none + ui.Paint()..filterQuality = ui.FilterQuality.none, ); canvas.drawImageRect( image, srcRect, const ui.Rect.fromLTRB(150, 0, 300, 150), - ui.Paint()..filterQuality = ui.FilterQuality.low + ui.Paint()..filterQuality = ui.FilterQuality.low, ); canvas.drawImageRect( image, srcRect, const ui.Rect.fromLTRB(0, 150, 150, 300), - ui.Paint()..filterQuality = ui.FilterQuality.medium + ui.Paint()..filterQuality = ui.FilterQuality.medium, ); canvas.drawImageRect( image, srcRect, const ui.Rect.fromLTRB(150, 150, 300, 300), - ui.Paint()..filterQuality = ui.FilterQuality.high + ui.Paint()..filterQuality = ui.FilterQuality.high, ); await drawPictureUsingCurrentRenderer(recorder.endRecording()); @@ -161,7 +167,7 @@ Future testMain() async { image, const ui.Rect.fromLTRB(50, 50, 100, 100), drawRegion, - ui.Paint() + ui.Paint(), ); await drawPictureUsingCurrentRenderer(recorder.endRecording()); @@ -182,10 +188,7 @@ Future testMain() async { matrix, filterQuality: quality, ); - canvas.drawOval( - rect, - ui.Paint()..shader = shader - ); + canvas.drawOval(rect, ui.Paint()..shader = shader); } // Draw image shader with all four qualities. @@ -303,26 +306,22 @@ Future testMain() async { final ui.Offset center = ui.Offset(x * 10 + 5, y * 10 + 5); final ui.Color color = ui.Color.fromRGBO( (center.dx * 256 / 150).round(), - (center.dy * 256 / 150).round(), 0, 1); + (center.dy * 256 / 150).round(), + 0, + 1, + ); canvas.drawCircle(center, 5, ui.Paint()..color = color); } } return recorder.endRecording().toImage(150, 150); }); - Uint8List generatePixelData( - int width, - int height, - ui.Color Function(double, double) generator - ) { + Uint8List generatePixelData(int width, int height, ui.Color Function(double, double) generator) { final Uint8List data = Uint8List(width * height * 4); int outputIndex = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - final ui.Color pixelColor = generator( - (2.0 * x / width) - 1.0, - (2.0 * y / height) - 1.0, - ); + final ui.Color pixelColor = generator((2.0 * x / width) - 1.0, (2.0 * y / height) - 1.0); data[outputIndex++] = pixelColor.red; data[outputIndex++] = pixelColor.green; data[outputIndex++] = pixelColor.blue; @@ -377,7 +376,7 @@ Future testMain() async { emitImageTests('codec_uri', () async { final ui.Codec codec = await renderer.instantiateImageCodecFromUrl( - Uri(path: '/test_images/mandrill_128.png') + Uri(path: '/test_images/mandrill_128.png'), ); expect(codec.frameCount, 1); @@ -389,7 +388,7 @@ Future testMain() async { // This image (from skia's test images) has a rotated orientation in its exif data. // This should result in a 3024x4032 image, not 4032x3024 image. final ui.Codec codec = await renderer.instantiateImageCodecFromUrl( - Uri(path: '/test_images/iphone_15.jpeg') + Uri(path: '/test_images/iphone_15.jpeg'), ); expect(codec.frameCount, 1); @@ -402,13 +401,16 @@ Future testMain() async { // See https://github.com/flutter/flutter/issues/109265 if (!isFirefox) { emitImageTests('svg_image_bitmap', () async { - final DomBlob svgBlob = createDomBlob([ - ''' + final DomBlob svgBlob = createDomBlob( + [ + ''' - ''' - ], {'type': 'image/svg+xml'}); + ''', + ], + {'type': 'image/svg+xml'}, + ); final String url = domWindow.URL.createObjectURL(svgBlob); final DomHTMLImageElement image = createDomHTMLImageElement(); final Completer completer = Completer(); @@ -441,15 +443,16 @@ Future testMain() async { // See https://github.com/flutter/flutter/issues/109265 if (!isFirefox && !isHtml) { emitImageTests('svg_image_bitmap_texture_source', () async { - final DomBlob svgBlob = createDomBlob([ - ''' + final DomBlob svgBlob = createDomBlob( + [ + ''' - ''' - ], { - 'type': 'image/svg+xml' - }); + ''', + ], + {'type': 'image/svg+xml'}, + ); final String url = domWindow.URL.createObjectURL(svgBlob); final DomHTMLImageElement image = createDomHTMLImageElement(); final Completer completer = Completer(); @@ -462,8 +465,12 @@ Future testMain() async { image.src = url; await completer.future; - final ui.Image uiImage = - await renderer.createImageFromTextureSource(image.toJSAnyShallow, width: 150, height: 150, transferOwnership: false); + final ui.Image uiImage = await renderer.createImageFromTextureSource( + image.toJSAnyShallow, + width: 150, + height: 150, + transferOwnership: false, + ); return uiImage; }); } diff --git a/lib/web_ui/test/ui/line_metrics_test.dart b/lib/web_ui/test/ui/line_metrics_test.dart index 38b56561930c6..902944814a66a 100644 --- a/lib/web_ui/test/ui/line_metrics_test.dart +++ b/lib/web_ui/test/ui/line_metrics_test.dart @@ -15,16 +15,12 @@ void main() { } Future testMain() async { - setUpUnitTests( - withImplicitView: true, - setUpTestViewDimensions: false, - ); + setUpUnitTests(withImplicitView: true, setUpTestViewDimensions: false); test('empty paragraph', () { const double fontSize = 10.0; - final ui.Paragraph paragraph = ui.ParagraphBuilder(ui.ParagraphStyle( - fontSize: fontSize, - )).build(); + final ui.Paragraph paragraph = + ui.ParagraphBuilder(ui.ParagraphStyle(fontSize: fontSize)).build(); paragraph.layout(const ui.ParagraphConstraints(width: double.infinity)); expect(paragraph.getLineMetricsAt(0), isNull); @@ -34,13 +30,15 @@ Future testMain() async { test('Basic line related metrics', () { const double fontSize = 10; - final ui.ParagraphBuilder builder = ui.ParagraphBuilder(ui.ParagraphStyle( - fontStyle: ui.FontStyle.normal, - fontWeight: ui.FontWeight.normal, - fontSize: fontSize, - maxLines: 1, - ellipsis: 'BBB', - ))..addText('A' * 100); + final ui.ParagraphBuilder builder = ui.ParagraphBuilder( + ui.ParagraphStyle( + fontStyle: ui.FontStyle.normal, + fontWeight: ui.FontWeight.normal, + fontSize: fontSize, + maxLines: 1, + ellipsis: 'BBB', + ), + )..addText('A' * 100); final ui.Paragraph paragraph = builder.build(); paragraph.layout(const ui.ParagraphConstraints(width: 100.0)); @@ -58,10 +56,9 @@ Future testMain() async { }); test('respects paragraph height', () { - final ui.ParagraphBuilder builder = ui.ParagraphBuilder(ui.ParagraphStyle( - fontSize: 10, - height: 1.5, - ))..addText('A' * 10); + final ui.ParagraphBuilder builder = ui.ParagraphBuilder( + ui.ParagraphStyle(fontSize: 10, height: 1.5), + )..addText('A' * 10); final ui.Paragraph paragraph = builder.build(); paragraph.layout(const ui.ParagraphConstraints(width: double.infinity)); @@ -73,36 +70,45 @@ Future testMain() async { test('Basic glyph metrics', () { const double fontSize = 10; - final ui.ParagraphBuilder builder = ui.ParagraphBuilder(ui.ParagraphStyle( - fontStyle: ui.FontStyle.normal, - fontWeight: ui.FontWeight.normal, - fontFamily: 'FlutterTest', - fontSize: fontSize, - maxLines: 1, - ellipsis: 'BBB', - ))..addText('A' * 100); + final ui.ParagraphBuilder builder = ui.ParagraphBuilder( + ui.ParagraphStyle( + fontStyle: ui.FontStyle.normal, + fontWeight: ui.FontWeight.normal, + fontFamily: 'FlutterTest', + fontSize: fontSize, + maxLines: 1, + ellipsis: 'BBB', + ), + )..addText('A' * 100); final ui.Paragraph paragraph = builder.build(); paragraph.layout(const ui.ParagraphConstraints(width: 100.0)); expect(paragraph.getGlyphInfoAt(-1), isNull); // The last 3 characters on the first line are ellipsized with BBB. - expect(paragraph.getGlyphInfoAt(0)?.graphemeClusterCodeUnitRange, const ui.TextRange(start: 0, end: 1)); - expect(paragraph.getGlyphInfoAt(6)?.graphemeClusterCodeUnitRange, const ui.TextRange(start: 6, end: 7)); + expect( + paragraph.getGlyphInfoAt(0)?.graphemeClusterCodeUnitRange, + const ui.TextRange(start: 0, end: 1), + ); + expect( + paragraph.getGlyphInfoAt(6)?.graphemeClusterCodeUnitRange, + const ui.TextRange(start: 6, end: 7), + ); expect(paragraph.getGlyphInfoAt(7), isNull); expect(paragraph.getGlyphInfoAt(200), isNull); }); test('Basic glyph metrics - hit test', () { const double fontSize = 10.0; - final ui.ParagraphBuilder builder = ui.ParagraphBuilder(ui.ParagraphStyle( - fontSize: fontSize, - fontFamily: 'FlutterTest', - ))..addText('Test\nTest'); + final ui.ParagraphBuilder builder = ui.ParagraphBuilder( + ui.ParagraphStyle(fontSize: fontSize, fontFamily: 'FlutterTest'), + )..addText('Test\nTest'); final ui.Paragraph paragraph = builder.build(); paragraph.layout(const ui.ParagraphConstraints(width: double.infinity)); - final ui.GlyphInfo? bottomRight = paragraph.getClosestGlyphInfoForOffset(const ui.Offset(99.0, 99.0)); + final ui.GlyphInfo? bottomRight = paragraph.getClosestGlyphInfoForOffset( + const ui.Offset(99.0, 99.0), + ); final ui.GlyphInfo? last = paragraph.getGlyphInfoAt(8); expect(bottomRight, equals(last)); expect(bottomRight, isNot(paragraph.getGlyphInfoAt(0))); @@ -120,8 +126,8 @@ Future testMain() async { ui.ParagraphStyle(fontSize: fontSize, fontFamily: 'FlutterTest'), ); builder.addText(text); - final ui.Paragraph paragraph = builder.build() - ..layout(const ui.ParagraphConstraints(width: text.length * fontSize)); + final ui.Paragraph paragraph = + builder.build()..layout(const ui.ParagraphConstraints(width: text.length * fontSize)); expect(paragraph.maxIntrinsicWidth, text.length * fontSize); switch (paragraph.computeLineMetrics()) { @@ -134,10 +140,7 @@ Future testMain() async { test('overrides with flutter test font when debugEmulateFlutterTesterEnvironment is enabled', () { final ui.ParagraphBuilder builder = ui.ParagraphBuilder(ui.ParagraphStyle()); - builder.pushStyle(ui.TextStyle( - fontSize: 10.0, - fontFamily: 'Roboto', - )); + builder.pushStyle(ui.TextStyle(fontSize: 10.0, fontFamily: 'Roboto')); builder.addText('XXXX'); final ui.Paragraph paragraph = builder.build(); paragraph.layout(const ui.ParagraphConstraints(width: 400)); @@ -152,33 +155,31 @@ Future testMain() async { expect(metrics!.width, 40.0); }); - test('uses flutter test font by default when debugEmulateFlutterTesterEnvironment is enabled', () { - final ui.ParagraphBuilder builder = ui.ParagraphBuilder(ui.ParagraphStyle()); - builder.pushStyle(ui.TextStyle( - fontSize: 10.0, - )); - builder.addText('XXXX'); - final ui.Paragraph paragraph = builder.build(); - paragraph.layout(const ui.ParagraphConstraints(width: 400)); + test( + 'uses flutter test font by default when debugEmulateFlutterTesterEnvironment is enabled', + () { + final ui.ParagraphBuilder builder = ui.ParagraphBuilder(ui.ParagraphStyle()); + builder.pushStyle(ui.TextStyle(fontSize: 10.0)); + builder.addText('XXXX'); + final ui.Paragraph paragraph = builder.build(); + paragraph.layout(const ui.ParagraphConstraints(width: 400)); - expect(paragraph.numberOfLines, 1); - expect(paragraph.height, 10); + expect(paragraph.numberOfLines, 1); + expect(paragraph.height, 10); - final ui.LineMetrics? metrics = paragraph.getLineMetricsAt(0); - expect(metrics, isNotNull); + final ui.LineMetrics? metrics = paragraph.getLineMetricsAt(0); + expect(metrics, isNotNull); - // FlutterTest font's 'X' character is a square, so it's the font size (10.0) * 4 characters. - expect(metrics!.width, 40.0); - }); + // FlutterTest font's 'X' character is a square, so it's the font size (10.0) * 4 characters. + expect(metrics!.width, 40.0); + }, + ); test('uses specified font when debugEmulateFlutterTesterEnvironment is disabled', () { debugEmulateFlutterTesterEnvironment = false; final ui.ParagraphBuilder builder = ui.ParagraphBuilder(ui.ParagraphStyle()); - builder.pushStyle(ui.TextStyle( - fontSize: 16.0, - fontFamily: 'Roboto', - )); + builder.pushStyle(ui.TextStyle(fontSize: 16.0, fontFamily: 'Roboto')); builder.addText('O'); final ui.Paragraph paragraph = builder.build(); paragraph.layout(const ui.ParagraphConstraints(width: 400)); diff --git a/lib/web_ui/test/ui/paint_test.dart b/lib/web_ui/test/ui/paint_test.dart index 253ba3d17d38e..fd9db922890cd 100644 --- a/lib/web_ui/test/ui/paint_test.dart +++ b/lib/web_ui/test/ui/paint_test.dart @@ -13,10 +13,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpUnitTests(emulateTesterEnvironment: false, setUpTestViewDimensions: false); test('default field values are as documented on api.flutter.dev', () { final paint = ui.Paint(); @@ -49,10 +46,7 @@ Future testMain() async { paint.shader = ui.Gradient.linear( const ui.Offset(0.1, 0.2), const ui.Offset(1.5, 1.6), - const [ - ui.Color(0xaabbccdd), - ui.Color(0xbbccddee), - ], + const [ui.Color(0xaabbccdd), ui.Color(0xbbccddee)], [0.3, 0.4], ui.TileMode.decal, ); @@ -60,11 +54,7 @@ Future testMain() async { paint.filterQuality = ui.FilterQuality.high; paint.colorFilter = const ui.ColorFilter.linearToSrgbGamma(); paint.strokeMiterLimit = 1.8; - paint.imageFilter = ui.ImageFilter.blur( - sigmaX: 1.9, - sigmaY: 2.1, - tileMode: ui.TileMode.mirror, - ); + paint.imageFilter = ui.ImageFilter.blur(sigmaX: 1.9, sigmaY: 2.1, tileMode: ui.TileMode.mirror); expect( paint.toString(), @@ -94,10 +84,7 @@ Future testMain() async { paint.shader = ui.Gradient.linear( const ui.Offset(0.1, 0.2), const ui.Offset(1.5, 1.6), - const [ - ui.Color(0xaabbccdd), - ui.Color(0xbbccddee), - ], + const [ui.Color(0xaabbccdd), ui.Color(0xbbccddee)], [0.3, 0.4], ui.TileMode.decal, ); @@ -105,11 +92,7 @@ Future testMain() async { paint.filterQuality = ui.FilterQuality.high; paint.colorFilter = const ui.ColorFilter.linearToSrgbGamma(); paint.strokeMiterLimit = 1.8; - paint.imageFilter = ui.ImageFilter.blur( - sigmaX: 1.9, - sigmaY: 2.1, - tileMode: ui.TileMode.mirror, - ); + paint.imageFilter = ui.ImageFilter.blur(sigmaX: 1.9, sigmaY: 2.1, tileMode: ui.TileMode.mirror); final ui.Paint copy = ui.Paint.from(paint); diff --git a/lib/web_ui/test/ui/paragraph_builder_test.dart b/lib/web_ui/test/ui/paragraph_builder_test.dart index c522b8851563e..226201566955e 100644 --- a/lib/web_ui/test/ui/paragraph_builder_test.dart +++ b/lib/web_ui/test/ui/paragraph_builder_test.dart @@ -33,9 +33,7 @@ Future testMain() async { test('the presence of foreground style should not throw', () { final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle()); - builder.pushStyle(TextStyle( - foreground: Paint()..color = const Color(0xFFABCDEF), - )); + builder.pushStyle(TextStyle(foreground: Paint()..color = const Color(0xFFABCDEF))); builder.addText('hi'); expect(() => builder.build(), returnsNormally); @@ -48,15 +46,14 @@ Future testMain() async { final Paragraph paragraph = builder.build(); paragraph.layout(const ParagraphConstraints(width: double.infinity)); - final TextRange downstreamWordBoundary = paragraph.getWordBoundary(const TextPosition( - offset: 5, - )); + final TextRange downstreamWordBoundary = paragraph.getWordBoundary( + const TextPosition(offset: 5), + ); expect(downstreamWordBoundary, const TextRange(start: 5, end: 6)); - final TextRange upstreamWordBoundary = paragraph.getWordBoundary(const TextPosition( - offset: 5, - affinity: TextAffinity.upstream, - )); + final TextRange upstreamWordBoundary = paragraph.getWordBoundary( + const TextPosition(offset: 5, affinity: TextAffinity.upstream), + ); expect(upstreamWordBoundary, const TextRange(start: 0, end: 5)); }); @@ -67,9 +64,7 @@ Future testMain() async { final Paragraph paragraph = builder.build(); paragraph.layout(const ParagraphConstraints(width: double.infinity)); - final TextRange lineBoundary = paragraph.getLineBoundary(const TextPosition( - offset: 11, - )); + final TextRange lineBoundary = paragraph.getLineBoundary(const TextPosition(offset: 11)); expect(lineBoundary, const TextRange(start: 0, end: 11)); }); @@ -86,11 +81,12 @@ Future testMain() async { test('kTextHeightNone unsets the height multiplier', () { const double fontSize = 10; const String text = 'A'; - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(fontSize: fontSize, height: 10)); + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle(fontSize: fontSize, height: 10), + ); builder.pushStyle(TextStyle(height: kTextHeightNone)); builder.addText(text); - final Paragraph paragraph = builder.build() - ..layout(const ParagraphConstraints(width: 1000)); + 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)); }); @@ -101,27 +97,31 @@ Future testMain() async { ParagraphStyle(fontSize: fontSize, height: kTextHeightNone, fontFamily: 'FlutterTest'), ); builder.addText('A'); - final Paragraph paragraph = builder.build() - ..layout(const ParagraphConstraints(width: 1000)); + 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)); }); - 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)); - }, + 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. ); } diff --git a/lib/web_ui/test/ui/paragraph_style_test.dart b/lib/web_ui/test/ui/paragraph_style_test.dart index 26e573eefd707..5d7d977c99337 100644 --- a/lib/web_ui/test/ui/paragraph_style_test.dart +++ b/lib/web_ui/test/ui/paragraph_style_test.dart @@ -13,10 +13,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpUnitTests(emulateTesterEnvironment: false, setUpTestViewDimensions: false); test('blanks are equal to each other', () { final ui.ParagraphStyle a = ui.ParagraphStyle(); @@ -99,35 +96,85 @@ Future testMain() async { typedef _ParagraphStylePropertyPopulator = void Function(_TestParagraphStyleBuilder builder); -final Map _populatorsA = { - 'textAlign': (_TestParagraphStyleBuilder builder) { builder.textAlign = ui.TextAlign.left; }, - 'textDirection': (_TestParagraphStyleBuilder builder) { builder.textDirection = ui.TextDirection.rtl; }, - 'fontWeight': (_TestParagraphStyleBuilder builder) { builder.fontWeight = ui.FontWeight.w400; }, - 'fontStyle': (_TestParagraphStyleBuilder builder) { builder.fontStyle = ui.FontStyle.normal; }, - 'maxLines': (_TestParagraphStyleBuilder builder) { builder.maxLines = 1; }, - 'fontFamily': (_TestParagraphStyleBuilder builder) { builder.fontFamily = 'Arial'; }, - 'fontSize': (_TestParagraphStyleBuilder builder) { builder.fontSize = 12; }, - 'height': (_TestParagraphStyleBuilder builder) { builder.height = 13; }, - 'textHeightBehavior': (_TestParagraphStyleBuilder builder) { builder.textHeightBehavior = const ui.TextHeightBehavior(); }, - 'strutStyle': (_TestParagraphStyleBuilder builder) { builder.strutStyle = ui.StrutStyle(fontFamily: 'Times New Roman'); }, - 'ellipsis': (_TestParagraphStyleBuilder builder) { builder.ellipsis = '...'; }, - 'locale': (_TestParagraphStyleBuilder builder) { builder.locale = const ui.Locale('en', 'US'); }, -}; - -final Map _populatorsB = { - 'textAlign': (_TestParagraphStyleBuilder builder) { builder.textAlign = ui.TextAlign.right; }, - 'textDirection': (_TestParagraphStyleBuilder builder) { builder.textDirection = ui.TextDirection.ltr; }, - 'fontWeight': (_TestParagraphStyleBuilder builder) { builder.fontWeight = ui.FontWeight.w600; }, - 'fontStyle': (_TestParagraphStyleBuilder builder) { builder.fontStyle = ui.FontStyle.italic; }, - 'maxLines': (_TestParagraphStyleBuilder builder) { builder.maxLines = 2; }, - 'fontFamily': (_TestParagraphStyleBuilder builder) { builder.fontFamily = 'Noto'; }, - 'fontSize': (_TestParagraphStyleBuilder builder) { builder.fontSize = 12.1; }, - 'height': (_TestParagraphStyleBuilder builder) { builder.height = 13.1; }, - 'textHeightBehavior': (_TestParagraphStyleBuilder builder) { builder.textHeightBehavior = const ui.TextHeightBehavior(applyHeightToFirstAscent: false); }, - 'strutStyle': (_TestParagraphStyleBuilder builder) { builder.strutStyle = ui.StrutStyle(fontFamily: 'sans-serif'); }, - 'ellipsis': (_TestParagraphStyleBuilder builder) { builder.ellipsis = '___'; }, - 'locale': (_TestParagraphStyleBuilder builder) { builder.locale = const ui.Locale('fr', 'CA'); }, -}; +final Map _populatorsA = + { + 'textAlign': (_TestParagraphStyleBuilder builder) { + builder.textAlign = ui.TextAlign.left; + }, + 'textDirection': (_TestParagraphStyleBuilder builder) { + builder.textDirection = ui.TextDirection.rtl; + }, + 'fontWeight': (_TestParagraphStyleBuilder builder) { + builder.fontWeight = ui.FontWeight.w400; + }, + 'fontStyle': (_TestParagraphStyleBuilder builder) { + builder.fontStyle = ui.FontStyle.normal; + }, + 'maxLines': (_TestParagraphStyleBuilder builder) { + builder.maxLines = 1; + }, + 'fontFamily': (_TestParagraphStyleBuilder builder) { + builder.fontFamily = 'Arial'; + }, + 'fontSize': (_TestParagraphStyleBuilder builder) { + builder.fontSize = 12; + }, + 'height': (_TestParagraphStyleBuilder builder) { + builder.height = 13; + }, + 'textHeightBehavior': (_TestParagraphStyleBuilder builder) { + builder.textHeightBehavior = const ui.TextHeightBehavior(); + }, + 'strutStyle': (_TestParagraphStyleBuilder builder) { + builder.strutStyle = ui.StrutStyle(fontFamily: 'Times New Roman'); + }, + 'ellipsis': (_TestParagraphStyleBuilder builder) { + builder.ellipsis = '...'; + }, + 'locale': (_TestParagraphStyleBuilder builder) { + builder.locale = const ui.Locale('en', 'US'); + }, + }; + +final Map _populatorsB = + { + 'textAlign': (_TestParagraphStyleBuilder builder) { + builder.textAlign = ui.TextAlign.right; + }, + 'textDirection': (_TestParagraphStyleBuilder builder) { + builder.textDirection = ui.TextDirection.ltr; + }, + 'fontWeight': (_TestParagraphStyleBuilder builder) { + builder.fontWeight = ui.FontWeight.w600; + }, + 'fontStyle': (_TestParagraphStyleBuilder builder) { + builder.fontStyle = ui.FontStyle.italic; + }, + 'maxLines': (_TestParagraphStyleBuilder builder) { + builder.maxLines = 2; + }, + 'fontFamily': (_TestParagraphStyleBuilder builder) { + builder.fontFamily = 'Noto'; + }, + 'fontSize': (_TestParagraphStyleBuilder builder) { + builder.fontSize = 12.1; + }, + 'height': (_TestParagraphStyleBuilder builder) { + builder.height = 13.1; + }, + 'textHeightBehavior': (_TestParagraphStyleBuilder builder) { + builder.textHeightBehavior = const ui.TextHeightBehavior(applyHeightToFirstAscent: false); + }, + 'strutStyle': (_TestParagraphStyleBuilder builder) { + builder.strutStyle = ui.StrutStyle(fontFamily: 'sans-serif'); + }, + 'ellipsis': (_TestParagraphStyleBuilder builder) { + builder.ellipsis = '___'; + }, + 'locale': (_TestParagraphStyleBuilder builder) { + builder.locale = const ui.Locale('fr', 'CA'); + }, + }; class _TestParagraphStyleBuilder { ui.TextAlign? textAlign; diff --git a/lib/web_ui/test/ui/path_metrics_test.dart b/lib/web_ui/test/ui/path_metrics_test.dart index ba820f7590c59..67b963d8b4db7 100644 --- a/lib/web_ui/test/ui/path_metrics_test.dart +++ b/lib/web_ui/test/ui/path_metrics_test.dart @@ -52,8 +52,7 @@ Future testMain() async { path.lineTo(200.0, 50.0); path.lineTo(100.0, 200.0); expect(path.computeMetrics(forceClosed: true).isEmpty, isFalse); - final List metrics = - path.computeMetrics(forceClosed: true).toList(); + final List metrics = path.computeMetrics(forceClosed: true).toList(); expect(metrics.length, 1); expect(metrics[0].length, within(distance: kTolerance, from: 430.277)); }); @@ -128,11 +127,14 @@ Future testMain() async { final double endY = cy + (ry * math.sin(endRad)); final bool largeArc = (endAngle - startAngle).abs() > 180.0; - final Path path = Path() - ..moveTo(startX, startY) - ..arcToPoint(Offset(endX, endY), - radius: const Radius.elliptical(rx, ry), - largeArc: largeArc); + final Path path = + Path() + ..moveTo(startX, startY) + ..arcToPoint( + Offset(endX, endY), + radius: const Radius.elliptical(rx, ry), + largeArc: largeArc, + ); final List contourLengths = computeLengths(path.computeMetrics()); expect(contourLengths.length, 1); expect(contourLengths[0], within(distance: kTolerance, from: 156.827)); @@ -154,11 +156,14 @@ Future testMain() async { final double endY = cy + (ry * math.sin(endRad)); final bool largeArc = (endAngle - startAngle).abs() > 180.0; - final Path path = Path() - ..moveTo(startX, startY) - ..arcToPoint(Offset(endX, endY), - radius: const Radius.elliptical(rx, ry), - largeArc: largeArc); + final Path path = + Path() + ..moveTo(startX, startY) + ..arcToPoint( + Offset(endX, endY), + radius: const Radius.elliptical(rx, ry), + largeArc: largeArc, + ); final List contourLengths = computeLengths(path.computeMetrics()); expect(contourLengths.length, 1); expect(contourLengths[0], within(distance: kTolerance, from: 313.654)); @@ -180,11 +185,14 @@ Future testMain() async { final double endY = cy + (ry * math.sin(endRad)); final bool largeArc = (endAngle - startAngle).abs() > 180.0; - final Path path = Path() - ..moveTo(startX, startY) - ..arcToPoint(Offset(endX, endY), - radius: const Radius.elliptical(rx, ry), - largeArc: largeArc); + final Path path = + Path() + ..moveTo(startX, startY) + ..arcToPoint( + Offset(endX, endY), + radius: const Radius.elliptical(rx, ry), + largeArc: largeArc, + ); final List contourLengths = computeLengths(path.computeMetrics()); expect(contourLengths.length, 1); expect(contourLengths[0], within(distance: kTolerance, from: 470.482)); @@ -206,11 +214,14 @@ Future testMain() async { final double endY = cy + (ry * math.sin(endRad)); final bool largeArc = (endAngle - startAngle).abs() > 180.0; - final Path path = Path() - ..moveTo(startX, startY) - ..arcToPoint(Offset(endX, endY), - radius: const Radius.elliptical(rx, ry), - largeArc: largeArc); + final Path path = + Path() + ..moveTo(startX, startY) + ..arcToPoint( + Offset(endX, endY), + radius: const Radius.elliptical(rx, ry), + largeArc: largeArc, + ); final List contourLengths = computeLengths(path.computeMetrics()); expect(contourLengths.length, 1); expect(contourLengths[0], within(distance: kTolerance, from: 362.733)); diff --git a/lib/web_ui/test/ui/path_test.dart b/lib/web_ui/test/ui/path_test.dart index 03c15fcd2d741..fcd15035f2e5e 100644 --- a/lib/web_ui/test/ui/path_test.dart +++ b/lib/web_ui/test/ui/path_test.dart @@ -36,7 +36,11 @@ Future testMain() async { final Path difference = Path.combine(PathOperation.difference, pathCircle1, pathCircle2); expect(difference.getBounds(), equals(c1)); - final Path reverseDifference = Path.combine(PathOperation.reverseDifference, pathCircle1, pathCircle2); + final Path reverseDifference = Path.combine( + PathOperation.reverseDifference, + pathCircle1, + pathCircle2, + ); expect(reverseDifference.getBounds(), equals(c2)); final Path union = Path.combine(PathOperation.union, pathCircle1, pathCircle2); @@ -48,7 +52,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 + // TODO(hterkelsen): Implement Path.combine in the HTML renderer, https://github.com/flutter/flutter/issues/44572 }, skip: isHtml); test('path combine oval', () { @@ -63,7 +67,11 @@ Future testMain() async { expect(difference.getBounds().top, closeTo(0.88, 0.01)); - final Path reverseDifference = Path.combine(PathOperation.reverseDifference, pathCircle1, pathCircle2); + final Path reverseDifference = Path.combine( + PathOperation.reverseDifference, + pathCircle1, + pathCircle2, + ); expect(reverseDifference.getBounds().right, closeTo(14.11, 0.01)); final Path union = Path.combine(PathOperation.union, pathCircle1, pathCircle2); @@ -75,7 +83,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 + // TODO(hterkelsen): Implement Path.combine in the HTML renderer, https://github.com/flutter/flutter/issues/44572 }, skip: isHtml); test('path clone', () { @@ -109,8 +117,7 @@ Future testMain() async { expect(p.getBounds(), equals(bounds)); final Path pTransformed = p.transform(scaleMatrix); - expect(pTransformed.getBounds(), - equals(const Rect.fromLTRB(0.0, 0.0, 10 * 2.5, 10 * 0.5))); + expect(pTransformed.getBounds(), equals(const Rect.fromLTRB(0.0, 0.0, 10 * 2.5, 10 * 0.5))); final Path p2 = Path()..lineTo(10.0, 10.0); @@ -118,8 +125,7 @@ Future testMain() async { expect(p.getBounds(), equals(const Rect.fromLTRB(0.0, 0.0, 20.0, 20.0))); p.addPath(p2, const Offset(20.0, 20.0), matrix4: scaleMatrix); - expect(p.getBounds(), - equals(const Rect.fromLTRB(0.0, 0.0, 20 + (10 * 2.5), 20 + (10 * .5)))); + expect(p.getBounds(), equals(const Rect.fromLTRB(0.0, 0.0, 20 + (10 * 2.5), 20 + (10 * .5)))); p.extendWithPath(p2, Offset.zero); expect(p.getBounds(), equals(const Rect.fromLTRB(0.0, 0.0, 45.0, 25.0))); @@ -160,24 +166,28 @@ Future testMain() async { // test getTangentForOffset with vertical line final Path simpleVerticalLine = Path()..lineTo(0.0, 10.0); - final PathMetrics simpleMetricsVertical = simpleVerticalLine.computeMetrics()..iterator.moveNext(); + final PathMetrics simpleMetricsVertical = + simpleVerticalLine.computeMetrics()..iterator.moveNext(); final Tangent posTanVertical = simpleMetricsVertical.iterator.current.getTangentForOffset(5.0)!; expect(posTanVertical.position, equals(const Offset(0.0, 5.0))); expect(posTanVertical.angle, closeTo(-1.5708, .0001)); // 90 degrees // test getTangentForOffset with diagonal line final Path simpleDiagonalLine = Path()..lineTo(10.0, 10.0); - final PathMetrics simpleMetricsDiagonal = simpleDiagonalLine.computeMetrics()..iterator.moveNext(); + final PathMetrics simpleMetricsDiagonal = + simpleDiagonalLine.computeMetrics()..iterator.moveNext(); final double midPoint = simpleMetricsDiagonal.iterator.current.length / 2; - final Tangent posTanDiagonal = simpleMetricsDiagonal.iterator.current.getTangentForOffset(midPoint)!; + final Tangent posTanDiagonal = + simpleMetricsDiagonal.iterator.current.getTangentForOffset(midPoint)!; expect(posTanDiagonal.position, equals(const Offset(5.0, 5.0))); expect(posTanDiagonal.angle, closeTo(-0.7853981633974483, .00001)); // ~45 degrees // test a multi-contour path - final Path multiContour = Path() - ..lineTo(0.0, 10.0) - ..moveTo(10.0, 10.0) - ..lineTo(10.0, 15.0); + final Path multiContour = + Path() + ..lineTo(0.0, 10.0) + ..moveTo(10.0, 10.0) + ..lineTo(10.0, 15.0); final PathMetrics multiContourMetric = multiContour.computeMetrics(); expect(() => multiContourMetric.iterator.current, throwsRangeError); @@ -189,14 +199,16 @@ 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 + // 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 = Path()..lineTo(0, 10) - ..close() - ..moveTo(0, 15) - ..lineTo(10, 15); + final Path path = + Path() + ..lineTo(0, 10) + ..close() + ..moveTo(0, 15) + ..lineTo(10, 15); final List metrics = path.computeMetrics().toList(); expect(metrics.length, 2); expect(metrics[0].length, 20); @@ -207,7 +219,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 + // 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', () { @@ -221,7 +233,10 @@ Future testMain() async { expect(firstMetric.getTangentForOffset(4.0)!.vector, const Offset(0.0, 1.0)); expect(firstMetric.extractPath(4.0, 10.0).computeMetrics().first.length, 6.0); - path..lineTo(10, 10)..lineTo(10, 0)..close(); + path + ..lineTo(10, 10) + ..lineTo(10, 0) + ..close(); // mutating the path shouldn't have added anything to the iterator. expect(metrics, isEmpty); expect(firstMetric.length, 10); @@ -237,7 +252,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 + // TODO(hterkelsen): isClosed always returns false in the HTML renderer, https://github.com/flutter/flutter/issues/114446 }, skip: isHtml); test('path relativeLineTo', () { diff --git a/lib/web_ui/test/ui/picture_test.dart b/lib/web_ui/test/ui/picture_test.dart index 297d8b50f403a..e9df90db1870d 100644 --- a/lib/web_ui/test/ui/picture_test.dart +++ b/lib/web_ui/test/ui/picture_test.dart @@ -38,8 +38,7 @@ Future testMain() async { test('approximateBytesUsed is available for onCreate', () async { int pictureSize = -1; - ui.Picture.onCreate = (ui.Picture picture) => - pictureSize = picture.approximateBytesUsed; + ui.Picture.onCreate = (ui.Picture picture) => pictureSize = picture.approximateBytesUsed; _createPicture(); diff --git a/lib/web_ui/test/ui/platform_view_test.dart b/lib/web_ui/test/ui/platform_view_test.dart index d18b5d8975d96..a7ebb70a1588b 100644 --- a/lib/web_ui/test/ui/platform_view_test.dart +++ b/lib/web_ui/test/ui/platform_view_test.dart @@ -29,16 +29,13 @@ Future testMain() async { const String platformViewType = 'test-platform-view'; setUp(() { - ui_web.platformViewRegistry.registerViewFactory( - platformViewType, - (int viewId) { - final DomElement element = createDomHTMLDivElement(); - element.style.backgroundColor = 'blue'; - element.style.width = '100%'; - element.style.height = '100%'; - return element; - } - ); + ui_web.platformViewRegistry.registerViewFactory(platformViewType, (int viewId) { + final DomElement element = createDomHTMLDivElement(); + element.style.backgroundColor = 'blue'; + element.style.width = '100%'; + element.style.height = '100%'; + return element; + }); }); tearDown(() { @@ -55,19 +52,14 @@ Future testMain() async { 50, ui.Paint() ..style = ui.PaintingStyle.fill - ..color = const ui.Color(0xFFFF0000) + ..color = const ui.Color(0xFFFF0000), ); final ui.SceneBuilder sb = ui.SceneBuilder(); sb.pushOffset(0, 0); sb.addPicture(const ui.Offset(100, 100), recorder.endRecording()); - sb.addPlatformView( - 1, - offset: const ui.Offset(125, 125), - width: 50, - height: 50, - ); + sb.addPlatformView(1, offset: const ui.Offset(125, 125), width: 50, height: 50); await renderScene(sb.build()); await matchGoldenFile('picture_platformview_overlap.png', region: region); @@ -83,7 +75,7 @@ Future testMain() async { 50, ui.Paint() ..style = ui.PaintingStyle.fill - ..color = const ui.Color(0xFF00FF00) + ..color = const ui.Color(0xFF00FF00), ); final ui.Picture picture = recorder.endRecording(); @@ -92,12 +84,7 @@ Future testMain() async { sb.pushOffset(0, 0); sb.addPicture(const ui.Offset(75, 75), picture); - sb.addPlatformView( - 1, - offset: const ui.Offset(100, 100), - width: 100, - height: 100, - ); + sb.addPlatformView(1, offset: const ui.Offset(100, 100), width: 100, height: 100); sb.addPicture(const ui.Offset(125, 125), picture); await renderScene(sb.build()); @@ -115,7 +102,7 @@ Future testMain() async { 50, ui.Paint() ..style = ui.PaintingStyle.fill - ..color = const ui.Color(0xFFFF0000) + ..color = const ui.Color(0xFFFF0000), ); final ui.SceneBuilder sb = ui.SceneBuilder(); @@ -123,12 +110,7 @@ Future testMain() async { sb.addPicture(const ui.Offset(100, 100), recorder.endRecording()); sb.pushTransform(Matrix4.rotationZ(0.1).toFloat64()); - sb.addPlatformView( - 1, - offset: const ui.Offset(125, 125), - width: 50, - height: 50, - ); + sb.addPlatformView(1, offset: const ui.Offset(125, 125), width: 50, height: 50); await renderScene(sb.build()); await matchGoldenFile('platformview_transformed.png', region: region); @@ -144,7 +126,7 @@ Future testMain() async { 50, ui.Paint() ..style = ui.PaintingStyle.fill - ..color = const ui.Color(0xFFFF0000) + ..color = const ui.Color(0xFFFF0000), ); final ui.SceneBuilder sb = ui.SceneBuilder(); @@ -156,12 +138,7 @@ Future testMain() async { sb.pushOffset(50, 50); sb.pushTransform(Matrix4.rotationZ(0.1).toFloat64()); sb.pushOffset(25, 25); - sb.addPlatformView( - 1, - offset: const ui.Offset(50, 50), - width: 50, - height: 50, - ); + sb.addPlatformView(1, offset: const ui.Offset(50, 50), width: 50, height: 50); await renderScene(sb.build()); await matchGoldenFile('platformview_transformed_offset.png', region: region); @@ -177,7 +154,7 @@ Future testMain() async { 50, ui.Paint() ..style = ui.PaintingStyle.fill - ..color = const ui.Color(0xFFFF0000) + ..color = const ui.Color(0xFFFF0000), ); final ui.Picture picture = recorder.endRecording(); @@ -187,12 +164,7 @@ Future testMain() async { sb.addPicture(const ui.Offset(100, 100), picture); final ui.EngineLayer retainedPlatformView = sb.pushOffset(50, 50); - sb.addPlatformView( - 1, - offset: const ui.Offset(125, 125), - width: 50, - height: 50, - ); + sb.addPlatformView(1, offset: const ui.Offset(125, 125), width: 50, height: 50); await renderScene(sb.build()); await matchGoldenFile('platformview_offset.png', region: region); @@ -207,7 +179,6 @@ Future testMain() async { await matchGoldenFile('platformview_offset_moved.png', region: region); }); - test('platformview with opacity', () async { await _createPlatformView(1, platformViewType); @@ -218,7 +189,7 @@ Future testMain() async { 50, ui.Paint() ..style = ui.PaintingStyle.fill - ..color = const ui.Color(0xFFFF0000) + ..color = const ui.Color(0xFFFF0000), ); final ui.SceneBuilder sb = ui.SceneBuilder(); @@ -226,12 +197,7 @@ Future testMain() async { sb.addPicture(const ui.Offset(100, 100), recorder.endRecording()); sb.pushOpacity(127, offset: const ui.Offset(50, 50)); - sb.addPlatformView( - 1, - offset: const ui.Offset(125, 125), - width: 50, - height: 50, - ); + sb.addPlatformView(1, offset: const ui.Offset(125, 125), width: 50, height: 50); await renderScene(sb.build()); await matchGoldenFile('platformview_opacity.png', region: region); @@ -247,7 +213,7 @@ Future testMain() async { 50, ui.Paint() ..style = ui.PaintingStyle.fill - ..color = const ui.Color(0xFFFF0000) + ..color = const ui.Color(0xFFFF0000), ); final ui.SceneBuilder sb = ui.SceneBuilder(); @@ -255,12 +221,7 @@ Future testMain() async { sb.pushClipRect(const ui.Rect.fromLTRB(60, 60, 100, 100)); sb.addPicture(const ui.Offset(50, 50), recorder.endRecording()); - sb.addPlatformView( - 1, - offset: const ui.Offset(75, 75), - width: 50, - height: 50, - ); + sb.addPlatformView(1, offset: const ui.Offset(75, 75), width: 50, height: 50); await renderScene(sb.build()); await matchGoldenFile('platformview_cliprect.png', region: region); @@ -276,23 +237,18 @@ Future testMain() async { 50, ui.Paint() ..style = ui.PaintingStyle.fill - ..color = const ui.Color(0xFFFF0000) + ..color = const ui.Color(0xFFFF0000), ); final ui.SceneBuilder sb = ui.SceneBuilder(); sb.pushOffset(50, 50); sb.pushClipRRect( const ui.RRect.fromLTRBXY(60, 60, 100, 100, 5, 10), - clipBehavior: ui.Clip.antiAlias + clipBehavior: ui.Clip.antiAlias, ); sb.addPicture(const ui.Offset(50, 50), recorder.endRecording()); - sb.addPlatformView( - 1, - offset: const ui.Offset(75, 75), - width: 50, - height: 50, - ); + sb.addPlatformView(1, offset: const ui.Offset(75, 75), width: 50, height: 50); await renderScene(sb.build()); await matchGoldenFile('platformview_cliprrect.png', region: region); @@ -308,7 +264,7 @@ Future testMain() async { 50, ui.Paint() ..style = ui.PaintingStyle.fill - ..color = const ui.Color(0xFFFF0000) + ..color = const ui.Color(0xFFFF0000), ); final ui.SceneBuilder sb = ui.SceneBuilder(); @@ -317,17 +273,12 @@ Future testMain() async { // The rrect should completely cover the rect for this test case. sb.pushClipRRect( const ui.RRect.fromLTRBXY(50, 50, 110, 110, 5, 10), - clipBehavior: ui.Clip.antiAlias + clipBehavior: ui.Clip.antiAlias, ); sb.pushClipRect(const ui.Rect.fromLTRB(60, 60, 100, 100)); sb.addPicture(const ui.Offset(50, 50), recorder.endRecording()); - sb.addPlatformView( - 1, - offset: const ui.Offset(75, 75), - width: 50, - height: 50, - ); + sb.addPlatformView(1, offset: const ui.Offset(75, 75), width: 50, height: 50); await renderScene(sb.build()); await matchGoldenFile('platformview_covered_clip.png', region: region); @@ -343,7 +294,7 @@ Future testMain() async { 50, ui.Paint() ..style = ui.PaintingStyle.fill - ..color = const ui.Color(0xFFFF0000) + ..color = const ui.Color(0xFFFF0000), ); final ui.SceneBuilder sb = ui.SceneBuilder(); @@ -358,12 +309,7 @@ Future testMain() async { sb.pushClipPath(path); sb.addPicture(const ui.Offset(50, 50), recorder.endRecording()); - sb.addPlatformView( - 1, - offset: const ui.Offset(75, 75), - width: 50, - height: 50, - ); + sb.addPlatformView(1, offset: const ui.Offset(75, 75), width: 50, height: 50); await renderScene(sb.build()); await matchGoldenFile('platformview_clippath.png', region: region); @@ -376,13 +322,7 @@ Future _createPlatformView(int id, String viewType) { const MethodCodec codec = StandardMethodCodec(); ui.PlatformDispatcher.instance.sendPlatformMessage( 'flutter/platform_views', - codec.encodeMethodCall(MethodCall( - 'create', - { - 'id': id, - 'viewType': viewType, - }, - )), + codec.encodeMethodCall(MethodCall('create', {'id': id, 'viewType': viewType})), (dynamic _) => completer.complete(), ); return completer.future; diff --git a/lib/web_ui/test/ui/renderer_test.dart b/lib/web_ui/test/ui/renderer_test.dart index 243557c854a01..a790dac1b6c84 100644 --- a/lib/web_ui/test/ui/renderer_test.dart +++ b/lib/web_ui/test/ui/renderer_test.dart @@ -42,7 +42,6 @@ Future testMain() async { final view3 = EngineFlutterView(EnginePlatformDispatcher.instance, host3); EnginePlatformDispatcher.instance.viewManager.registerView(view3); - await Future.wait([ renderer.renderScene(paintRect(rect, red), view1), renderer.renderScene(paintRect(rect, green), view2), diff --git a/lib/web_ui/test/ui/rrect_test.dart b/lib/web_ui/test/ui/rrect_test.dart index 958c7aa137cfa..172b4d3fa0d55 100644 --- a/lib/web_ui/test/ui/rrect_test.dart +++ b/lib/web_ui/test/ui/rrect_test.dart @@ -16,10 +16,11 @@ Future testMain() async { setUpUnitTests(); test('RRect.contains()', () { final RRect rrect = RRect.fromRectAndCorners( - const Rect.fromLTRB(1.0, 1.0, 2.0, 2.0), - topLeft: const Radius.circular(0.5), - topRight: const Radius.circular(0.25), - bottomRight: const Radius.elliptical(0.25, 0.75)); + const Rect.fromLTRB(1.0, 1.0, 2.0, 2.0), + topLeft: const Radius.circular(0.5), + topRight: const Radius.circular(0.25), + bottomRight: const Radius.elliptical(0.25, 0.75), + ); expect(rrect.contains(const Offset(1.0, 1.0)), isFalse); expect(rrect.contains(const Offset(1.1, 1.1)), isFalse); @@ -33,10 +34,11 @@ Future testMain() async { test('RRect.contains() large radii', () { final RRect rrect = RRect.fromRectAndCorners( - const Rect.fromLTRB(1.0, 1.0, 2.0, 2.0), - topLeft: const Radius.circular(5000.0), - topRight: const Radius.circular(2500.0), - bottomRight: const Radius.elliptical(2500.0, 7500.0)); + const Rect.fromLTRB(1.0, 1.0, 2.0, 2.0), + topLeft: const Radius.circular(5000.0), + topRight: const Radius.circular(2500.0), + bottomRight: const Radius.elliptical(2500.0, 7500.0), + ); expect(rrect.contains(const Offset(1.0, 1.0)), isFalse); expect(rrect.contains(const Offset(1.1, 1.1)), isFalse); @@ -48,14 +50,13 @@ Future testMain() async { expect(rrect.contains(const Offset(1.0, 1.99)), isTrue); }); - test('RRect.webOnlyUniformRadii returns true when all corner radii are equal', - () { + test('RRect.webOnlyUniformRadii returns true when all corner radii are equal', () { final RRect rect1 = RRect.fromRectAndCorners( - const Rect.fromLTWH(1.0, 2.0, 3.0, 4.0), - topLeft: const Radius.elliptical(5, 5), - topRight: const Radius.elliptical(5, 5), - bottomLeft: const Radius.elliptical(5, 5), - bottomRight: const Radius.elliptical(5, 5), + const Rect.fromLTWH(1.0, 2.0, 3.0, 4.0), + topLeft: const Radius.elliptical(5, 5), + topRight: const Radius.elliptical(5, 5), + bottomLeft: const Radius.elliptical(5, 5), + bottomRight: const Radius.elliptical(5, 5), ); expect(rect1.webOnlyUniformRadii, isTrue); diff --git a/lib/web_ui/test/ui/scene_builder_test.dart b/lib/web_ui/test/ui/scene_builder_test.dart index 3ffe8e0ccdf88..421d7bb6f4bb9 100644 --- a/lib/web_ui/test/ui/scene_builder_test.dart +++ b/lib/web_ui/test/ui/scene_builder_test.dart @@ -31,14 +31,15 @@ Future testMain() async { test('Test offset layer', () async { final ui.SceneBuilder sceneBuilder = ui.SceneBuilder(); sceneBuilder.pushOffset(150, 150); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - canvas.drawCircle( - ui.Offset.zero, 50, ui.Paint()..color = const ui.Color(0xFF00FF00)); - })); + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + canvas.drawCircle(ui.Offset.zero, 50, ui.Paint()..color = const ui.Color(0xFF00FF00)); + }), + ); await renderScene(sceneBuilder.build()); - await matchGoldenFile('scene_builder_centered_circle.png', - region: region); + await matchGoldenFile('scene_builder_centered_circle.png', region: region); }); test('Test transform layer', () async { @@ -51,30 +52,39 @@ Future testMain() async { transform.translate(150, 150); transform.rotate(kUnitZ, math.pi / 3); sceneBuilder.pushTransform(transform.toFloat64()); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - canvas.drawRRect( + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + canvas.drawRRect( ui.RRect.fromRectAndRadius( - ui.Rect.fromCircle(center: ui.Offset.zero, radius: 50), - const ui.Radius.circular(10)), - ui.Paint()..color = const ui.Color(0xFF0000FF)); - })); + ui.Rect.fromCircle(center: ui.Offset.zero, radius: 50), + const ui.Radius.circular(10), + ), + ui.Paint()..color = const ui.Color(0xFF0000FF), + ); + }), + ); await renderScene(sceneBuilder.build()); - await matchGoldenFile('scene_builder_rotated_rounded_square.png', - region: region); + await matchGoldenFile('scene_builder_rotated_rounded_square.png', region: region); }); test('Test clipRect layer', () async { final ui.SceneBuilder sceneBuilder = ui.SceneBuilder(); sceneBuilder.pushClipRect(const ui.Rect.fromLTRB(0, 0, 150, 150)); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - canvas.drawCircle(const ui.Offset(150, 150), 50, - ui.Paint()..color = const ui.Color(0xFFFF0000)); - })); + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + canvas.drawCircle( + const ui.Offset(150, 150), + 50, + ui.Paint()..color = const ui.Color(0xFFFF0000), + ); + }), + ); await renderScene(sceneBuilder.build()); - await matchGoldenFile('scene_builder_circle_clip_rect.png', - region: region); + await matchGoldenFile('scene_builder_circle_clip_rect.png', region: region); }); test('Devtools rendering regression test', () async { @@ -82,173 +92,200 @@ Future testMain() async { final ui.SceneBuilder sceneBuilder = ui.SceneBuilder(); sceneBuilder.pushClipRect(const ui.Rect.fromLTRB(12.0, 0.0, 300.0, 27.0)); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - canvas.drawOval( - const ui.Rect.fromLTRB(15.0, 5.0, 64.0, 21.0), - ui.Paint()..color = const ui.Color(0xFF0000FF), - ); - })); + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + canvas.drawOval( + const ui.Rect.fromLTRB(15.0, 5.0, 64.0, 21.0), + ui.Paint()..color = const ui.Color(0xFF0000FF), + ); + }), + ); await renderScene(sceneBuilder.build()); - await matchGoldenFile('scene_builder_oval_clip_rect.png', - region: region); + await matchGoldenFile('scene_builder_oval_clip_rect.png', region: region); }); test('Test clipRRect layer', () async { final ui.SceneBuilder sceneBuilder = ui.SceneBuilder(); sceneBuilder.pushClipRRect( - ui.RRect.fromRectAndRadius( - const ui.Rect.fromLTRB(0, 0, 150, 150), - const ui.Radius.circular(25), - ), - clipBehavior: ui.Clip.antiAlias); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - canvas.drawCircle(const ui.Offset(150, 150), 50, - ui.Paint()..color = const ui.Color(0xFFFF00FF)); - })); + ui.RRect.fromRectAndRadius( + const ui.Rect.fromLTRB(0, 0, 150, 150), + const ui.Radius.circular(25), + ), + clipBehavior: ui.Clip.antiAlias, + ); + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + canvas.drawCircle( + const ui.Offset(150, 150), + 50, + ui.Paint()..color = const ui.Color(0xFFFF00FF), + ); + }), + ); await renderScene(sceneBuilder.build()); - await matchGoldenFile('scene_builder_circle_clip_rrect.png', - region: region); + await matchGoldenFile('scene_builder_circle_clip_rrect.png', region: region); }); test('Test clipPath layer', () async { final ui.SceneBuilder sceneBuilder = ui.SceneBuilder(); final ui.Path path = ui.Path(); - path.addOval( - ui.Rect.fromCircle(center: const ui.Offset(150, 150), radius: 60)); + path.addOval(ui.Rect.fromCircle(center: const ui.Offset(150, 150), radius: 60)); sceneBuilder.pushClipPath(path); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - canvas.drawRect( + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + canvas.drawRect( ui.Rect.fromCircle(center: const ui.Offset(150, 150), radius: 50), - ui.Paint()..color = const ui.Color(0xFF00FFFF)); - })); + ui.Paint()..color = const ui.Color(0xFF00FFFF), + ); + }), + ); await renderScene(sceneBuilder.build()); - await matchGoldenFile('scene_builder_rectangle_clip_circular_path.png', - region: region); + await matchGoldenFile('scene_builder_rectangle_clip_circular_path.png', region: region); }); test('Test opacity layer', () async { final ui.SceneBuilder sceneBuilder = ui.SceneBuilder(); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - canvas.drawRect( + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + canvas.drawRect( ui.Rect.fromCircle(center: const ui.Offset(150, 150), radius: 50), - ui.Paint()..color = const ui.Color(0xFF00FF00)); - })); + ui.Paint()..color = const ui.Color(0xFF00FF00), + ); + }), + ); sceneBuilder.pushOpacity(0x7F, offset: const ui.Offset(150, 150)); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - final ui.Paint paint = ui.Paint()..color = const ui.Color(0xFFFF0000); - canvas.drawCircle(const ui.Offset(-25, 0), 50, paint); - canvas.drawCircle(const ui.Offset(25, 0), 50, paint); - })); + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + final ui.Paint paint = ui.Paint()..color = const ui.Color(0xFFFF0000); + canvas.drawCircle(const ui.Offset(-25, 0), 50, paint); + canvas.drawCircle(const ui.Offset(25, 0), 50, paint); + }), + ); await renderScene(sceneBuilder.build()); - await matchGoldenFile('scene_builder_opacity_circles_on_square.png', - region: region); + await matchGoldenFile('scene_builder_opacity_circles_on_square.png', region: region); }); test('shader mask layer', () async { final ui.SceneBuilder sceneBuilder = ui.SceneBuilder(); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - final ui.Paint paint = ui.Paint()..color = const ui.Color(0xFFFF0000); - canvas.drawCircle(const ui.Offset(125, 150), 50, paint); - canvas.drawCircle(const ui.Offset(175, 150), 50, paint); - })); + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + final ui.Paint paint = ui.Paint()..color = const ui.Color(0xFFFF0000); + canvas.drawCircle(const ui.Offset(125, 150), 50, paint); + canvas.drawCircle(const ui.Offset(175, 150), 50, paint); + }), + ); final ui.Shader shader = ui.Gradient.linear( - ui.Offset.zero, const ui.Offset(50, 50), [ - const ui.Color(0xFFFFFFFF), - const ui.Color(0x00000000), - ]); - sceneBuilder.pushShaderMask(shader, - const ui.Rect.fromLTRB(125, 125, 175, 175), ui.BlendMode.srcATop); + ui.Offset.zero, + const ui.Offset(50, 50), + [const ui.Color(0xFFFFFFFF), const ui.Color(0x00000000)], + ); + sceneBuilder.pushShaderMask( + shader, + const ui.Rect.fromLTRB(125, 125, 175, 175), + ui.BlendMode.srcATop, + ); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - canvas.drawRect( + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + canvas.drawRect( ui.Rect.fromCircle(center: const ui.Offset(150, 150), radius: 50), - ui.Paint()..color = const ui.Color(0xFF00FF00)); - })); + ui.Paint()..color = const ui.Color(0xFF00FF00), + ); + }), + ); await renderScene(sceneBuilder.build()); await matchGoldenFile('scene_builder_shader_mask.png', region: region); - }, - skip: isFirefox && - isHtml); // https://github.com/flutter/flutter/issues/86623 + }, skip: isFirefox && isHtml); // https://github.com/flutter/flutter/issues/86623 test('backdrop filter layer', () async { final ui.SceneBuilder sceneBuilder = ui.SceneBuilder(); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - // Create a red and blue checkerboard pattern - final ui.Paint redPaint = ui.Paint() - ..color = const ui.Color(0xFFFF0000); - final ui.Paint bluePaint = ui.Paint() - ..color = const ui.Color(0xFF0000FF); - for (double y = 0; y < 300; y += 10) { - for (double x = 0; x < 300; x += 10) { - final ui.Paint paint = ((x + y) % 20 == 0) ? redPaint : bluePaint; - canvas.drawRect(ui.Rect.fromLTWH(x, y, 10, 10), paint); + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + // Create a red and blue checkerboard pattern + final ui.Paint redPaint = ui.Paint()..color = const ui.Color(0xFFFF0000); + final ui.Paint bluePaint = ui.Paint()..color = const ui.Color(0xFF0000FF); + for (double y = 0; y < 300; y += 10) { + for (double x = 0; x < 300; x += 10) { + final ui.Paint paint = ((x + y) % 20 == 0) ? redPaint : bluePaint; + canvas.drawRect(ui.Rect.fromLTWH(x, y, 10, 10), paint); + } } - } - })); - - sceneBuilder.pushBackdropFilter(ui.ImageFilter.blur( - sigmaX: 3.0, - sigmaY: 3.0, - )); + }), + ); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - canvas.drawCircle(const ui.Offset(150, 150), 50, - ui.Paint()..color = const ui.Color(0xFF00FF00)); - })); + sceneBuilder.pushBackdropFilter(ui.ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0)); + + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + canvas.drawCircle( + const ui.Offset(150, 150), + 50, + ui.Paint()..color = const ui.Color(0xFF00FF00), + ); + }), + ); await renderScene(sceneBuilder.build()); - await matchGoldenFile('scene_builder_backdrop_filter.png', - region: region); + await matchGoldenFile('scene_builder_backdrop_filter.png', region: region); }); test('empty backdrop filter layer with clip', () async { final ui.SceneBuilder sceneBuilder = ui.SceneBuilder(); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - // Create a red and blue checkerboard pattern - final ui.Paint redPaint = ui.Paint() - ..color = const ui.Color(0xFFFF0000); - final ui.Paint bluePaint = ui.Paint() - ..color = const ui.Color(0xFF0000FF); - for (double y = 0; y < 300; y += 10) { - for (double x = 0; x < 300; x += 10) { - final ui.Paint paint = ((x + y) % 20 == 0) ? redPaint : bluePaint; - canvas.drawRect(ui.Rect.fromLTWH(x, y, 10, 10), paint); + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + // Create a red and blue checkerboard pattern + final ui.Paint redPaint = ui.Paint()..color = const ui.Color(0xFFFF0000); + final ui.Paint bluePaint = ui.Paint()..color = const ui.Color(0xFF0000FF); + for (double y = 0; y < 300; y += 10) { + for (double x = 0; x < 300; x += 10) { + final ui.Paint paint = ((x + y) % 20 == 0) ? redPaint : bluePaint; + canvas.drawRect(ui.Rect.fromLTWH(x, y, 10, 10), paint); + } } - } - })); + }), + ); sceneBuilder.pushClipRect(const ui.Rect.fromLTRB(100, 100, 200, 200)); - sceneBuilder.pushBackdropFilter(ui.ImageFilter.blur( - sigmaX: 3.0, - sigmaY: 3.0, - )); + sceneBuilder.pushBackdropFilter(ui.ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0)); await renderScene(sceneBuilder.build()); - await matchGoldenFile('scene_builder_empty_backdrop_filter_with_clip.png', - region: region); + await matchGoldenFile('scene_builder_empty_backdrop_filter_with_clip.png', region: region); }); test('blur image filter layer', () async { final ui.SceneBuilder sceneBuilder = ui.SceneBuilder(); - sceneBuilder.pushImageFilter(ui.ImageFilter.blur( - sigmaX: 5.0, - sigmaY: 5.0, - )); - - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - canvas.drawCircle(const ui.Offset(150, 150), 50, - ui.Paint()..color = const ui.Color(0xFF00FF00)); - })); + sceneBuilder.pushImageFilter(ui.ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0)); + + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + canvas.drawCircle( + const ui.Offset(150, 150), + 50, + ui.Paint()..color = const ui.Color(0xFF00FF00), + ); + }), + ); await renderScene(sceneBuilder.build()); await matchGoldenFile('scene_builder_image_filter.png', region: region); @@ -261,12 +298,12 @@ Future testMain() async { final Matrix4 matrix = Matrix4.rotationZ(math.pi / 18); final ui.ImageFilter matrixFilter = ui.ImageFilter.matrix(toMatrix64(matrix.storage)); sceneBuilder.pushImageFilter(matrixFilter); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - canvas.drawRect( - region, - ui.Paint()..color = const ui.Color(0xFF00FF00) - ); - })); + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + canvas.drawRect(region, ui.Paint()..color = const ui.Color(0xFF00FF00)); + }), + ); await renderScene(sceneBuilder.build()); await matchGoldenFile('scene_builder_matrix_image_filter.png', region: region); }); @@ -277,23 +314,23 @@ Future testMain() async { sceneBuilder.pushClipRect(const ui.Rect.fromLTWH(100, 100, 100, 100)); sceneBuilder.pushImageFilter( - ui.ImageFilter.blur( - sigmaX: 5.0, - sigmaY: 5.0, - ), + ui.ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0), offset: const ui.Offset(100, 100), ); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - canvas.drawCircle(const ui.Offset(50, 50), 25, - ui.Paint()..color = const ui.Color(0xFF00FF00)); - })); + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + canvas.drawCircle( + const ui.Offset(50, 50), + 25, + ui.Paint()..color = const ui.Color(0xFF00FF00), + ); + }), + ); await renderScene(sceneBuilder.build()); - await matchGoldenFile( - 'scene_builder_image_filter_with_offset.png', - region: region, - ); + await matchGoldenFile('scene_builder_image_filter_with_offset.png', region: region); }); test('color filter layer', () async { @@ -322,10 +359,16 @@ Future testMain() async { ]); sceneBuilder.pushColorFilter(sepia); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - canvas.drawCircle(const ui.Offset(150, 150), 50, - ui.Paint()..color = const ui.Color(0xFF00FF00)); - })); + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + canvas.drawCircle( + const ui.Offset(150, 150), + 50, + ui.Paint()..color = const ui.Color(0xFF00FF00), + ); + }), + ); await renderScene(sceneBuilder.build()); await matchGoldenFile('scene_builder_color_filter.png', region: region); @@ -334,37 +377,59 @@ Future testMain() async { test('overlapping pictures in opacity layer', () async { final ui.SceneBuilder sceneBuilder = ui.SceneBuilder(); sceneBuilder.pushOpacity(128); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - canvas.drawCircle(const ui.Offset(100, 150), 100, - ui.Paint()..color = const ui.Color(0xFFFF0000)); - })); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - canvas.drawCircle(const ui.Offset(200, 150), 100, - ui.Paint()..color = const ui.Color(0xFFFF0000)); - })); + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + canvas.drawCircle( + const ui.Offset(100, 150), + 100, + ui.Paint()..color = const ui.Color(0xFFFF0000), + ); + }), + ); + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + canvas.drawCircle( + const ui.Offset(200, 150), + 100, + ui.Paint()..color = const ui.Color(0xFFFF0000), + ); + }), + ); sceneBuilder.pop(); await renderScene(sceneBuilder.build()); - await matchGoldenFile('scene_builder_overlapping_pictures_in_opacity.png', - region: region); + await matchGoldenFile('scene_builder_overlapping_pictures_in_opacity.png', region: region); }); test('picture clipped out in final scene', () async { final ui.SceneBuilder sceneBuilder = ui.SceneBuilder(); sceneBuilder.pushClipRect(const ui.Rect.fromLTRB(0, 0, 125, 300)); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - canvas.drawCircle(const ui.Offset(50, 150), 50, - ui.Paint()..color = const ui.Color(0xFFFF0000)); - })); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - canvas.drawCircle(const ui.Offset(200, 150), 50, - ui.Paint()..color = const ui.Color(0xFFFF0000)); - })); + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + canvas.drawCircle( + const ui.Offset(50, 150), + 50, + ui.Paint()..color = const ui.Color(0xFFFF0000), + ); + }), + ); + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + canvas.drawCircle( + const ui.Offset(200, 150), + 50, + ui.Paint()..color = const ui.Color(0xFFFF0000), + ); + }), + ); sceneBuilder.pop(); await renderScene(sceneBuilder.build()); - await matchGoldenFile('scene_builder_picture_clipped_out.png', - region: region); + await matchGoldenFile('scene_builder_picture_clipped_out.png', region: region); }); test('picture clipped but scrolls back in', () async { @@ -374,14 +439,26 @@ Future testMain() async { // Save this offsetLayer to add back in so we are using the same // picture layers on the next scene. final ui.OffsetEngineLayer offsetLayer = sceneBuilder.pushOffset(0, 0); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - canvas.drawCircle(const ui.Offset(50, 150), 50, - ui.Paint()..color = const ui.Color(0xFFFF0000)); - })); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - canvas.drawCircle(const ui.Offset(200, 150), 50, - ui.Paint()..color = const ui.Color(0xFFFF0000)); - })); + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + canvas.drawCircle( + const ui.Offset(50, 150), + 50, + ui.Paint()..color = const ui.Color(0xFFFF0000), + ); + }), + ); + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + canvas.drawCircle( + const ui.Offset(200, 150), + 50, + ui.Paint()..color = const ui.Color(0xFFFF0000), + ); + }), + ); sceneBuilder.pop(); sceneBuilder.pop(); await renderScene(sceneBuilder.build()); @@ -401,40 +478,56 @@ Future testMain() async { await renderScene(sceneBuilder3.build()); await matchGoldenFile( - 'scene_builder_picture_clipped_out_then_clipped_in.png', - region: region); + 'scene_builder_picture_clipped_out_then_clipped_in.png', + region: region, + ); }); test('shader mask parent of clipped out picture', () async { final ui.SceneBuilder sceneBuilder = ui.SceneBuilder(); final ui.Shader shader = ui.Gradient.linear( - ui.Offset.zero, const ui.Offset(50, 50), [ - const ui.Color(0xFFFFFFFF), - const ui.Color(0x00000000), - ]); + ui.Offset.zero, + const ui.Offset(50, 50), + [const ui.Color(0xFFFFFFFF), const ui.Color(0x00000000)], + ); sceneBuilder.pushClipRect(const ui.Rect.fromLTRB(0, 0, 125, 300)); - sceneBuilder.pushShaderMask(shader, - const ui.Rect.fromLTRB(25, 125, 75, 175), ui.BlendMode.srcATop); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - canvas.drawCircle(const ui.Offset(50, 150), 50, - ui.Paint()..color = const ui.Color(0xFFFF0000)); - })); + sceneBuilder.pushShaderMask( + shader, + const ui.Rect.fromLTRB(25, 125, 75, 175), + ui.BlendMode.srcATop, + ); + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + canvas.drawCircle( + const ui.Offset(50, 150), + 50, + ui.Paint()..color = const ui.Color(0xFFFF0000), + ); + }), + ); sceneBuilder.pop(); - sceneBuilder.pushShaderMask(shader, - const ui.Rect.fromLTRB(175, 125, 225, 175), ui.BlendMode.srcATop); - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - canvas.drawCircle(const ui.Offset(200, 150), 50, - ui.Paint()..color = const ui.Color(0xFFFF0000)); - })); + sceneBuilder.pushShaderMask( + shader, + const ui.Rect.fromLTRB(175, 125, 225, 175), + ui.BlendMode.srcATop, + ); + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + canvas.drawCircle( + const ui.Offset(200, 150), + 50, + ui.Paint()..color = const ui.Color(0xFFFF0000), + ); + }), + ); sceneBuilder.pop(); sceneBuilder.pop(); 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 + 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(); @@ -477,16 +570,20 @@ Future testMain() async { 45.20, 0.86, ]); - sceneBuilder - .pushTransform(Matrix4.identity().scaled(0.3, 0.3).toFloat64()); + sceneBuilder.pushTransform(Matrix4.identity().scaled(0.3, 0.3).toFloat64()); sceneBuilder.pushTransform(transform1); sceneBuilder.pushTransform(transform2); - sceneBuilder.addPicture(const ui.Offset(20, 20), - drawPicture((ui.Canvas canvas) { - canvas.drawCircle(const ui.Offset(25, 75), 25, - ui.Paint()..color = const ui.Color(0xFFFF0000)); - })); + sceneBuilder.addPicture( + const ui.Offset(20, 20), + drawPicture((ui.Canvas canvas) { + canvas.drawCircle( + const ui.Offset(25, 75), + 25, + ui.Paint()..color = const ui.Color(0xFFFF0000), + ); + }), + ); sceneBuilder.pop(); sceneBuilder.pop(); sceneBuilder.pop(); @@ -495,64 +592,85 @@ Future testMain() async { await renderScene(sceneBuilder.build()); await matchGoldenFile( - 'scene_builder_opacity_layer_with_transformed_children.png', - region: region); + 'scene_builder_opacity_layer_with_transformed_children.png', + region: region, + ); }); - 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( + 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); - - test('backdrop layer with clamp blur tile mode', () async { - final scene = backdropBlurWithTileMode(ui.TileMode.clamp, 10, 50); - await renderScene(scene); - - await matchGoldenFile( + region: const ui.Rect.fromLTWH(0, 0, 10 * 50, 10 * 50), + ); + }, + // HTML renderer doesn't have tile modes + skip: isHtml, + ); + + 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); - - test('backdrop layer with mirror blur tile mode', () async { - final scene = backdropBlurWithTileMode(ui.TileMode.mirror, 10, 50); - await renderScene(scene); - - await matchGoldenFile( + region: const ui.Rect.fromLTWH(0, 0, 10 * 50, 10 * 50), + ); + }, + // HTML renderer doesn't have tile modes + skip: isHtml, + ); + + 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); - - test('backdrop layer with repeated blur tile mode', () async { - final scene = backdropBlurWithTileMode(ui.TileMode.repeated, 10, 50); - await renderScene(scene); - - await matchGoldenFile( + region: const ui.Rect.fromLTWH(0, 0, 10 * 50, 10 * 50), + ); + }, + // HTML renderer doesn't have tile modes + skip: isHtml, + ); + + 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); - - test('backdrop layer with decal blur tile mode', () async { - final scene = backdropBlurWithTileMode(ui.TileMode.decal, 10, 50); - await renderScene(scene); - - await matchGoldenFile( + region: const ui.Rect.fromLTWH(0, 0, 10 * 50, 10 * 50), + ); + }, + // HTML renderer doesn't have tile modes + skip: isHtml, + ); + + 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); + region: const ui.Rect.fromLTWH(0, 0, 10 * 50, 10 * 50), + ); + }, + // HTML renderer doesn't have tile modes + skip: isHtml, + ); }); } @@ -563,9 +681,7 @@ ui.Picture drawPicture(void Function(ui.Canvas) drawCommands) { return recorder.endRecording(); } -ui.Scene backdropBlurWithTileMode(ui.TileMode? tileMode, - final double rectSize, - final int count) { +ui.Scene backdropBlurWithTileMode(ui.TileMode? tileMode, final double rectSize, final int count) { final double imgSize = rectSize * count; const ui.Color white = ui.Color(0xFFFFFFFF); @@ -580,11 +696,12 @@ ui.Scene backdropBlurWithTileMode(ui.TileMode? tileMode, for (int i = 0; i < count; i++) { for (int j = 0; j < count; j++) { final bool rectOdd = (i + j) & 1 == 0; - final ui.Color fg = (i < count / 2) - ? ((j < count / 2) ? green : blue) - : ((j < count / 2) ? yellow : red); - canvas.drawRect(ui.Rect.fromLTWH(i * rectSize, j * rectSize, rectSize, rectSize), - ui.Paint()..color = rectOdd ? fg : white); + final ui.Color fg = + (i < count / 2) ? ((j < count / 2) ? green : blue) : ((j < count / 2) ? yellow : red); + canvas.drawRect( + ui.Rect.fromLTWH(i * rectSize, j * rectSize, rectSize, rectSize), + ui.Paint()..color = rectOdd ? fg : white, + ); } } canvas.drawRect(ui.Rect.fromLTWH(0, 0, imgSize, 1), ui.Paint()..color = purple); @@ -597,8 +714,10 @@ ui.Scene backdropBlurWithTileMode(ui.TileMode? tileMode, // We push a clipRect layer with the SaveLayer behavior so that it creates // a layer of predetermined size in which the backdrop filter will apply // its filter to show the edge effects on predictable edges. - sceneBuilder.pushClipRect(ui.Rect.fromLTWH(0, 0, imgSize, imgSize), - clipBehavior: ui.Clip.antiAliasWithSaveLayer); + sceneBuilder.pushClipRect( + ui.Rect.fromLTWH(0, 0, imgSize, imgSize), + clipBehavior: ui.Clip.antiAliasWithSaveLayer, + ); sceneBuilder.addPicture(ui.Offset.zero, blueGreenGridPicture); sceneBuilder.pushBackdropFilter(ui.ImageFilter.blur(sigmaX: 20, sigmaY: 20, tileMode: tileMode)); // The following picture prevents the saveLayer in the backdrop filter from @@ -608,10 +727,17 @@ ui.Scene backdropBlurWithTileMode(ui.TileMode? tileMode, // it are opaque and dstOver is a NOP in that case, but it is unlikely that // a recording process would be able to figure that out without extensive // analysis between the pictures and layers. - sceneBuilder.addPicture(ui.Offset.zero, drawPicture((ui.Canvas canvas) { - canvas.drawRect(ui.Rect.fromLTWH(imgSize * 0.5 - 10, imgSize * 0.5 - 10, 20, 20), - ui.Paint()..color = purple..blendMode = ui.BlendMode.dstOver); - })); + sceneBuilder.addPicture( + ui.Offset.zero, + drawPicture((ui.Canvas canvas) { + canvas.drawRect( + ui.Rect.fromLTWH(imgSize * 0.5 - 10, imgSize * 0.5 - 10, 20, 20), + ui.Paint() + ..color = purple + ..blendMode = ui.BlendMode.dstOver, + ); + }), + ); sceneBuilder.pop(); sceneBuilder.pop(); diff --git a/lib/web_ui/test/ui/shadow_test.dart b/lib/web_ui/test/ui/shadow_test.dart index cff3021780271..60396ffe242ee 100644 --- a/lib/web_ui/test/ui/shadow_test.dart +++ b/lib/web_ui/test/ui/shadow_test.dart @@ -29,11 +29,7 @@ Future testMain() async { path.moveTo(50, 150); path.cubicTo(100, 50, 200, 250, 250, 150); - canvas.drawShadow( - path, - const ui.Color(0xFF000000), - 5, - false); + canvas.drawShadow(path, const ui.Color(0xFF000000), 5, false); canvas.drawPath(path, ui.Paint()..color = const ui.Color(0xFFFF00FF)); }); await drawPictureUsingCurrentRenderer(picture); @@ -46,11 +42,7 @@ Future testMain() async { path.moveTo(50, 150); path.cubicTo(100, 250, 200, 50, 250, 150); - canvas.drawShadow( - path, - const ui.Color(0xFF000000), - 5, - true); + canvas.drawShadow(path, const ui.Color(0xFF000000), 5, true); canvas.drawPath(path, ui.Paint()..color = const ui.Color(0x8F00FFFF)); }); await drawPictureUsingCurrentRenderer(picture); diff --git a/lib/web_ui/test/ui/strut_style_test.dart b/lib/web_ui/test/ui/strut_style_test.dart index 4d182e6f65ce3..def15aa9646fc 100644 --- a/lib/web_ui/test/ui/strut_style_test.dart +++ b/lib/web_ui/test/ui/strut_style_test.dart @@ -13,10 +13,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpUnitTests(emulateTesterEnvironment: false, setUpTestViewDimensions: false); test('blanks are equal to each other', () { final ui.StrutStyle a = ui.StrutStyle(); @@ -99,31 +96,69 @@ Future testMain() async { typedef _StrutStylePropertyPopulator = void Function(_TestStrutStyleBuilder builder); -final Map _populatorsA = { - 'fontFamily': (_TestStrutStyleBuilder builder) { builder.fontFamily = 'Arial'; }, - // Intentionally do not use const List to make sure Object.hashAll is used to compute hashCode - 'fontFamilyFallback': (_TestStrutStyleBuilder builder) { builder.fontFamilyFallback = ['Roboto']; }, - 'fontSize': (_TestStrutStyleBuilder builder) { builder.fontSize = 12; }, - 'height': (_TestStrutStyleBuilder builder) { builder.height = 13; }, - 'leading': (_TestStrutStyleBuilder builder) { builder.leading = 0.1; }, - 'fontWeight': (_TestStrutStyleBuilder builder) { builder.fontWeight = ui.FontWeight.w400; }, - 'fontStyle': (_TestStrutStyleBuilder builder) { builder.fontStyle = ui.FontStyle.normal; }, - 'forceStrutHeight': (_TestStrutStyleBuilder builder) { builder.forceStrutHeight = false; }, - 'leadingDistribution': (_TestStrutStyleBuilder builder) { builder.leadingDistribution = ui.TextLeadingDistribution.proportional; }, -}; - -final Map _populatorsB = { - 'fontFamily': (_TestStrutStyleBuilder builder) { builder.fontFamily = 'Noto'; }, - // Intentionally do not use const List to make sure Object.hashAll is used to compute hashCode - 'fontFamilyFallback': (_TestStrutStyleBuilder builder) { builder.fontFamilyFallback = ['Verdana']; }, - 'fontSize': (_TestStrutStyleBuilder builder) { builder.fontSize = 12.1; }, - 'height': (_TestStrutStyleBuilder builder) { builder.height = 13.1; }, - 'leading': (_TestStrutStyleBuilder builder) { builder.leading = 0.2; }, - 'fontWeight': (_TestStrutStyleBuilder builder) { builder.fontWeight = ui.FontWeight.w600; }, - 'fontStyle': (_TestStrutStyleBuilder builder) { builder.fontStyle = ui.FontStyle.italic; }, - 'forceStrutHeight': (_TestStrutStyleBuilder builder) { builder.forceStrutHeight = true; }, - 'leadingDistribution': (_TestStrutStyleBuilder builder) { builder.leadingDistribution = ui.TextLeadingDistribution.even; }, -}; +final Map _populatorsA = + { + 'fontFamily': (_TestStrutStyleBuilder builder) { + builder.fontFamily = 'Arial'; + }, + // Intentionally do not use const List to make sure Object.hashAll is used to compute hashCode + 'fontFamilyFallback': (_TestStrutStyleBuilder builder) { + builder.fontFamilyFallback = ['Roboto']; + }, + 'fontSize': (_TestStrutStyleBuilder builder) { + builder.fontSize = 12; + }, + 'height': (_TestStrutStyleBuilder builder) { + builder.height = 13; + }, + 'leading': (_TestStrutStyleBuilder builder) { + builder.leading = 0.1; + }, + 'fontWeight': (_TestStrutStyleBuilder builder) { + builder.fontWeight = ui.FontWeight.w400; + }, + 'fontStyle': (_TestStrutStyleBuilder builder) { + builder.fontStyle = ui.FontStyle.normal; + }, + 'forceStrutHeight': (_TestStrutStyleBuilder builder) { + builder.forceStrutHeight = false; + }, + 'leadingDistribution': (_TestStrutStyleBuilder builder) { + builder.leadingDistribution = ui.TextLeadingDistribution.proportional; + }, + }; + +final Map _populatorsB = + { + 'fontFamily': (_TestStrutStyleBuilder builder) { + builder.fontFamily = 'Noto'; + }, + // Intentionally do not use const List to make sure Object.hashAll is used to compute hashCode + 'fontFamilyFallback': (_TestStrutStyleBuilder builder) { + builder.fontFamilyFallback = ['Verdana']; + }, + 'fontSize': (_TestStrutStyleBuilder builder) { + builder.fontSize = 12.1; + }, + 'height': (_TestStrutStyleBuilder builder) { + builder.height = 13.1; + }, + 'leading': (_TestStrutStyleBuilder builder) { + builder.leading = 0.2; + }, + 'fontWeight': (_TestStrutStyleBuilder builder) { + builder.fontWeight = ui.FontWeight.w600; + }, + 'fontStyle': (_TestStrutStyleBuilder builder) { + builder.fontStyle = ui.FontStyle.italic; + }, + 'forceStrutHeight': (_TestStrutStyleBuilder builder) { + builder.forceStrutHeight = true; + }, + 'leadingDistribution': (_TestStrutStyleBuilder builder) { + builder.leadingDistribution = ui.TextLeadingDistribution.even; + }, + }; class _TestStrutStyleBuilder { String? fontFamily; diff --git a/lib/web_ui/test/ui/text_golden_test.dart b/lib/web_ui/test/ui/text_golden_test.dart index a099a1fffda7a..f0d2c0acadc8a 100644 --- a/lib/web_ui/test/ui/text_golden_test.dart +++ b/lib/web_ui/test/ui/text_golden_test.dart @@ -29,13 +29,11 @@ Future testMain() async { }); test('text styles - center aligned', () async { - await testTextStyle('center aligned', - paragraphTextAlign: ui.TextAlign.center); + await testTextStyle('center aligned', paragraphTextAlign: ui.TextAlign.center); }); test('text styles - right aligned', () async { - await testTextStyle('right aligned', - paragraphTextAlign: ui.TextAlign.right); + await testTextStyle('right aligned', paragraphTextAlign: ui.TextAlign.right); }); test('text styles - rtl', () async { @@ -51,8 +49,12 @@ Future testMain() async { }); test('text styles - ellipsis', () async { - await testTextStyle('ellipsis', - paragraphMaxLines: 1, paragraphEllipsis: '...', layoutWidth: 60); + await testTextStyle( + 'ellipsis', + paragraphMaxLines: 1, + paragraphEllipsis: '...', + layoutWidth: 60, + ); }); test('text styles - paragraph font family', () async { @@ -64,35 +66,36 @@ Future testMain() async { }); test('text styles - paragraph height', () async { - await testTextStyle('paragraph height', - layoutWidth: 50, paragraphHeight: 1.5); + await testTextStyle('paragraph height', layoutWidth: 50, paragraphHeight: 1.5); }); test('text styles - text style height overriding paragraph height', () async { - await testTextStyle('text style height and paragraph style height', - layoutWidth: 50, paragraphHeight: 1.5, height: 2.0); + await testTextStyle( + 'text style height and paragraph style height', + layoutWidth: 50, + paragraphHeight: 1.5, + height: 2.0, + ); }); test('text styles - paragraph text height behavior', () async { - await testTextStyle('paragraph text height behavior', - layoutWidth: 50, - paragraphHeight: 1.5, - paragraphTextHeightBehavior: const ui.TextHeightBehavior( - applyHeightToFirstAscent: false, - applyHeightToLastDescent: false, - )); + await testTextStyle( + 'paragraph text height behavior', + layoutWidth: 50, + paragraphHeight: 1.5, + paragraphTextHeightBehavior: const ui.TextHeightBehavior( + applyHeightToFirstAscent: false, + applyHeightToLastDescent: false, + ), + ); }); test('text styles - paragraph weight', () async { - await testTextStyle('paragraph weight', - paragraphFontWeight: ui.FontWeight.w900); + await testTextStyle('paragraph weight', paragraphFontWeight: ui.FontWeight.w900); }); test('text style - paragraph font style', () async { - await testTextStyle( - 'paragraph font style', - paragraphFontStyle: ui.FontStyle.italic, - ); + await testTextStyle('paragraph font style', paragraphFontStyle: ui.FontStyle.italic); }); // TODO(yjbanov): locales specified in paragraph styles don't work: @@ -100,31 +103,39 @@ Future testMain() async { // TODO(yjbanov): spaces are not rendered correctly: // https://github.com/flutter/flutter/issues/74742 test('text styles - paragraph locale zh_CN', () async { - await testTextStyle('paragraph locale zh_CN', - outerText: '次 化 刃 直 入 令', - innerText: '', - paragraphLocale: const ui.Locale('zh', 'CN')); + await testTextStyle( + 'paragraph locale zh_CN', + outerText: '次 化 刃 直 入 令', + innerText: '', + paragraphLocale: const ui.Locale('zh', 'CN'), + ); }); test('text styles - paragraph locale zh_TW', () async { - await testTextStyle('paragraph locale zh_TW', - outerText: '次 化 刃 直 入 令', - innerText: '', - paragraphLocale: const ui.Locale('zh', 'TW')); + await testTextStyle( + 'paragraph locale zh_TW', + outerText: '次 化 刃 直 入 令', + innerText: '', + paragraphLocale: const ui.Locale('zh', 'TW'), + ); }); test('text styles - paragraph locale ja', () async { - await testTextStyle('paragraph locale ja', - outerText: '次 化 刃 直 入 令', - innerText: '', - paragraphLocale: const ui.Locale('ja')); + await testTextStyle( + 'paragraph locale ja', + outerText: '次 化 刃 直 入 令', + innerText: '', + paragraphLocale: const ui.Locale('ja'), + ); }); test('text styles - paragraph locale ko', () async { - await testTextStyle('paragraph locale ko', - outerText: '次 化 刃 直 入 令', - innerText: '', - paragraphLocale: const ui.Locale('ko')); + await testTextStyle( + 'paragraph locale ko', + outerText: '次 化 刃 直 入 令', + innerText: '', + paragraphLocale: const ui.Locale('ko'), + ); }); test('text styles - color', () async { @@ -132,19 +143,23 @@ Future testMain() async { }); test('text styles - decoration', () async { - await testTextStyle('decoration', - decoration: ui.TextDecoration.underline); + await testTextStyle('decoration', decoration: ui.TextDecoration.underline); }); test('text styles - decoration style', () async { - await testTextStyle('decoration style', - decoration: ui.TextDecoration.underline, - decorationStyle: ui.TextDecorationStyle.dashed); + await testTextStyle( + 'decoration style', + decoration: ui.TextDecoration.underline, + decorationStyle: ui.TextDecorationStyle.dashed, + ); }); test('text styles - decoration thickness', () async { - await testTextStyle('decoration thickness', - decoration: ui.TextDecoration.underline, decorationThickness: 5.0); + await testTextStyle( + 'decoration thickness', + decoration: ui.TextDecoration.underline, + decorationThickness: 5.0, + ); }); test('text styles - font weight', () async { @@ -157,8 +172,7 @@ Future testMain() async { // TODO(yjbanov): not sure how to test this. test('text styles - baseline', () async { - await testTextStyle('baseline', - textBaseline: ui.TextBaseline.ideographic); + await testTextStyle('baseline', textBaseline: ui.TextBaseline.ideographic); }); test('text styles - font family', () async { @@ -166,13 +180,15 @@ Future testMain() async { }); test('text styles - non-existent font family', () async { - await testTextStyle('non-existent font family', - fontFamily: 'DoesNotExist'); + await testTextStyle('non-existent font family', fontFamily: 'DoesNotExist'); }); test('text styles - family fallback', () async { - await testTextStyle('family fallback', - fontFamily: 'DoesNotExist', fontFamilyFallback: ['Ahem']); + await testTextStyle( + 'family fallback', + fontFamily: 'DoesNotExist', + fontFamilyFallback: ['Ahem'], + ); }); test('text styles - font size', () async { @@ -200,8 +216,7 @@ Future testMain() async { }); test('text styles - word spacing', () async { - await testTextStyle('word spacing', - innerText: 'Beautiful World!', wordSpacing: 25); + await testTextStyle('word spacing', innerText: 'Beautiful World!', wordSpacing: 25); }); test('text styles - height', () async { @@ -209,10 +224,12 @@ Future testMain() async { }); test('text styles - leading distribution', () async { - await testTextStyle('half leading', - height: 20, - fontSize: 10, - leadingDistribution: ui.TextLeadingDistribution.even); + await testTextStyle( + 'half leading', + height: 20, + fontSize: 10, + leadingDistribution: ui.TextLeadingDistribution.even, + ); await testTextStyle( 'half leading inherited from paragraph', height: 20, @@ -237,41 +254,47 @@ Future testMain() async { // TODO(yjbanov): spaces are not rendered correctly: // https://github.com/flutter/flutter/issues/74742 test('text styles - locale zh_CN', () async { - await testTextStyle('locale zh_CN', - innerText: '次 化 刃 直 入 令', - outerText: '', - locale: const ui.Locale('zh', 'CN')); + await testTextStyle( + 'locale zh_CN', + innerText: '次 化 刃 直 入 令', + outerText: '', + locale: const ui.Locale('zh', 'CN'), + ); }); test('text styles - locale zh_TW', () async { - await testTextStyle('locale zh_TW', - innerText: '次 化 刃 直 入 令', - outerText: '', - locale: const ui.Locale('zh', 'TW')); + await testTextStyle( + 'locale zh_TW', + innerText: '次 化 刃 直 入 令', + outerText: '', + locale: const ui.Locale('zh', 'TW'), + ); }); test('text styles - locale ja', () async { - await testTextStyle('locale ja', - innerText: '次 化 刃 直 入 令', - outerText: '', - locale: const ui.Locale('ja')); + await testTextStyle( + 'locale ja', + innerText: '次 化 刃 直 入 令', + outerText: '', + locale: const ui.Locale('ja'), + ); }); test('text styles - locale ko', () async { - await testTextStyle('locale ko', - innerText: '次 化 刃 直 入 令', - outerText: '', - locale: const ui.Locale('ko')); + await testTextStyle( + 'locale ko', + innerText: '次 化 刃 直 入 令', + outerText: '', + locale: const ui.Locale('ko'), + ); }); test('text styles - background', () async { - await testTextStyle('background', - background: ui.Paint()..color = const ui.Color(0xFF00FF00)); + await testTextStyle('background', background: ui.Paint()..color = const ui.Color(0xFF00FF00)); }); test('text styles - foreground', () async { - await testTextStyle('foreground', - foreground: ui.Paint()..color = const ui.Color(0xFF0000FF)); + await testTextStyle('foreground', foreground: ui.Paint()..color = const ui.Color(0xFF0000FF)); }); test('text styles - foreground and background', () async { @@ -291,18 +314,13 @@ Future testMain() async { }); test('text styles - shadows', () async { - await testTextStyle('shadows', shadows: [ - const ui.Shadow( - color: ui.Color(0xFF999900), - offset: ui.Offset(10, 10), - blurRadius: 5, - ), - const ui.Shadow( - color: ui.Color(0xFF009999), - offset: ui.Offset(-10, -10), - blurRadius: 10, - ), - ]); + await testTextStyle( + 'shadows', + shadows: [ + const ui.Shadow(color: ui.Color(0xFF999900), offset: ui.Offset(10, 10), blurRadius: 5), + const ui.Shadow(color: ui.Color(0xFF009999), offset: ui.Offset(-10, -10), blurRadius: 10), + ], + ); }); test('text styles - old style figures', () async { @@ -339,19 +357,11 @@ Future testMain() async { }); test('text styles - override font family', () async { - await testTextStyle( - 'override font family', - paragraphFontFamily: 'Ahem', - fontFamily: 'Roboto', - ); + await testTextStyle('override font family', paragraphFontFamily: 'Ahem', fontFamily: 'Roboto'); }); test('text styles - override font size', () async { - await testTextStyle( - 'override font size', - paragraphFontSize: 36, - fontSize: 18, - ); + await testTextStyle('override font size', paragraphFontSize: 36, fontSize: 18); }); test('text style - override font weight', () async { @@ -385,20 +395,13 @@ Future testMain() async { // some of these symbols. To make sure the test produces predictable // results we reset the fallback data forcing the engine to reload // fallbacks, which for this test will only load Noto Symbols. - await testTextStyle( - 'symbols', - outerText: '← ↑ → ↓ ', - innerText: '', - ); + await testTextStyle('symbols', outerText: '← ↑ → ↓ ', innerText: ''); }); test('strut style - override height', () async { await testTextStyle( 'strut style', - paragraphStrutStyle: ui.StrutStyle( - forceStrutHeight: true, - height: 2, - ), + paragraphStrutStyle: ui.StrutStyle(forceStrutHeight: true, height: 2), ); }); @@ -492,10 +495,7 @@ Future testMain() async { }); test('sample Bengali text', () async { - await testSampleText( - 'bengali', - 'ঈদের জামাত মসজিদে, মানতে হবে স্বাস্থ্যবিধি: ধর্ম মন্ত্রণালয়', - ); + await testSampleText('bengali', 'ঈদের জামাত মসজিদে, মানতে হবে স্বাস্থ্যবিধি: ধর্ম মন্ত্রণালয়'); }); test('hindi svayan test', () async { @@ -531,30 +531,24 @@ Future testMain() async { const double testWidth = 300; final ui.PictureRecorder recorder = ui.PictureRecorder(); final ui.Canvas canvas = ui.Canvas(recorder); - final ui.ParagraphBuilder builder = - ui.ParagraphBuilder(ui.ParagraphStyle( - fontSize: 40.0, - textDirection: ui.TextDirection.ltr, - )); - - builder.pushStyle(ui.TextStyle( - fontFamily: 'RobotoVariable', - )); + final ui.ParagraphBuilder builder = ui.ParagraphBuilder( + ui.ParagraphStyle(fontSize: 40.0, textDirection: ui.TextDirection.ltr), + ); + + builder.pushStyle(ui.TextStyle(fontFamily: 'RobotoVariable')); builder.addText('Normal\n'); builder.pop(); ui.FontVariation weight(double w) => ui.FontVariation('wght', w); - builder.pushStyle(ui.TextStyle( - fontFamily: 'RobotoVariable', - fontVariations: [weight(900)], - )); + builder.pushStyle( + ui.TextStyle(fontFamily: 'RobotoVariable', fontVariations: [weight(900)]), + ); builder.addText('Heavy\n'); builder.pop(); - builder.pushStyle(ui.TextStyle( - fontFamily: 'RobotoVariable', - fontVariations: [weight(100)], - )); + builder.pushStyle( + ui.TextStyle(fontFamily: 'RobotoVariable', fontVariations: [weight(100)]), + ); builder.addText('Light\n'); builder.pop(); @@ -570,12 +564,7 @@ Future testMain() async { }); test('text style - woff2 font', () async { - await testTextStyle( - 'emoji woff2', - outerText: '🙂 🇺🇸 🙋‍♂️', - innerText: '', - fontSize: 24, - ); + await testTextStyle('emoji woff2', outerText: '🙂 🇺🇸 🙋‍♂️', innerText: '', fontSize: 24); }); } @@ -643,54 +632,56 @@ Future testTextStyle( final ui.PictureRecorder recorder = ui.PictureRecorder(); final ui.Canvas canvas = ui.Canvas(recorder); canvas.translate(30, 10); - final ui.ParagraphBuilder descriptionBuilder = - ui.ParagraphBuilder(ui.ParagraphStyle()); + final ui.ParagraphBuilder descriptionBuilder = ui.ParagraphBuilder(ui.ParagraphStyle()); descriptionBuilder.addText(name); final ui.Paragraph descriptionParagraph = descriptionBuilder.build(); - descriptionParagraph - .layout(const ui.ParagraphConstraints(width: testWidth / 2 - 70)); + descriptionParagraph.layout(const ui.ParagraphConstraints(width: testWidth / 2 - 70)); const ui.Offset descriptionOffset = ui.Offset(testWidth / 2 + 30, 0); canvas.drawParagraph(descriptionParagraph, descriptionOffset); - final ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle( - textAlign: paragraphTextAlign, - textDirection: paragraphTextDirection, - maxLines: paragraphMaxLines, - fontFamily: paragraphFontFamily, - fontSize: paragraphFontSize, - height: paragraphHeight, - textHeightBehavior: paragraphTextHeightBehavior, - fontWeight: paragraphFontWeight, - fontStyle: paragraphFontStyle, - strutStyle: paragraphStrutStyle, - ellipsis: paragraphEllipsis, - locale: paragraphLocale, - )); + final ui.ParagraphBuilder pb = ui.ParagraphBuilder( + ui.ParagraphStyle( + textAlign: paragraphTextAlign, + textDirection: paragraphTextDirection, + maxLines: paragraphMaxLines, + fontFamily: paragraphFontFamily, + fontSize: paragraphFontSize, + height: paragraphHeight, + textHeightBehavior: paragraphTextHeightBehavior, + fontWeight: paragraphFontWeight, + fontStyle: paragraphFontStyle, + strutStyle: paragraphStrutStyle, + ellipsis: paragraphEllipsis, + locale: paragraphLocale, + ), + ); pb.addText(outerText); - pb.pushStyle(ui.TextStyle( - color: color, - decoration: decoration, - decorationColor: decorationColor, - decorationStyle: decorationStyle, - decorationThickness: decorationThickness, - fontWeight: fontWeight, - fontStyle: fontStyle, - textBaseline: textBaseline, - fontFamily: fontFamily, - fontFamilyFallback: fontFamilyFallback, - fontSize: fontSize, - letterSpacing: letterSpacing, - wordSpacing: wordSpacing, - height: height, - leadingDistribution: leadingDistribution, - locale: locale, - background: background, - foreground: foreground, - shadows: shadows, - fontFeatures: fontFeatures, - )); + pb.pushStyle( + ui.TextStyle( + color: color, + decoration: decoration, + decorationColor: decorationColor, + decorationStyle: decorationStyle, + decorationThickness: decorationThickness, + fontWeight: fontWeight, + fontStyle: fontStyle, + textBaseline: textBaseline, + fontFamily: fontFamily, + fontFamilyFallback: fontFamilyFallback, + fontSize: fontSize, + letterSpacing: letterSpacing, + wordSpacing: wordSpacing, + height: height, + leadingDistribution: leadingDistribution, + locale: locale, + background: background, + foreground: foreground, + shadows: shadows, + fontFeatures: fontFeatures, + ), + ); pb.addText(innerText); pb.pop(); final ui.Paragraph p = pb.build(); @@ -722,10 +713,7 @@ Future testTextStyle( 0, 0, testWidth, - math.max( - descriptionOffset.dy + descriptionParagraph.height + padding, - p.height + padding, - ), + math.max(descriptionOffset.dy + descriptionParagraph.height + padding, p.height + padding), ); return recorder.endRecording(); } @@ -736,23 +724,22 @@ Future testTextStyle( final ui.Picture picture = renderPicture(); await drawPictureUsingCurrentRenderer(picture); - await matchGoldenFile( - 'ui_text_styles_${name.replaceAll(' ', '_')}.png', - region: region, - ); + await matchGoldenFile('ui_text_styles_${name.replaceAll(' ', '_')}.png', region: region); } -Future testSampleText(String language, String text, - {ui.TextDirection textDirection = ui.TextDirection.ltr}) async { +Future testSampleText( + String language, + String text, { + ui.TextDirection textDirection = ui.TextDirection.ltr, +}) async { const double testWidth = 300; double paragraphHeight = 0; ui.Picture renderPicture() { final ui.PictureRecorder recorder = ui.PictureRecorder(); final ui.Canvas canvas = ui.Canvas(recorder); - final ui.ParagraphBuilder paragraphBuilder = - ui.ParagraphBuilder(ui.ParagraphStyle( - textDirection: textDirection, - )); + final ui.ParagraphBuilder paragraphBuilder = ui.ParagraphBuilder( + ui.ParagraphStyle(textDirection: textDirection), + ); paragraphBuilder.addText(text); final ui.Paragraph paragraph = paragraphBuilder.build(); paragraph.layout(const ui.ParagraphConstraints(width: testWidth - 20)); @@ -760,6 +747,7 @@ Future testSampleText(String language, String text, paragraphHeight = paragraph.height; return recorder.endRecording(); } + // Render once to trigger font downloads. renderPicture(); await renderer.fontCollection.fontFallbackManager?.debugWhenIdle(); diff --git a/lib/web_ui/test/ui/text_style_test.dart b/lib/web_ui/test/ui/text_style_test.dart index 98b5241946703..49242115a9b01 100644 --- a/lib/web_ui/test/ui/text_style_test.dart +++ b/lib/web_ui/test/ui/text_style_test.dart @@ -13,10 +13,7 @@ void main() { } Future testMain() async { - setUpUnitTests( - emulateTesterEnvironment: false, - setUpTestViewDimensions: false, - ); + setUpUnitTests(emulateTesterEnvironment: false, setUpTestViewDimensions: false); test('blanks are equal to each other', () { final ui.TextStyle a = ui.TextStyle(); @@ -200,52 +197,136 @@ final ui.Paint _foregroundB = ui.Paint(); // Intentionally do not use const List expressions to make sure Object.hashAll is used to compute hashCode final Map _populatorsA = { - 'color': (_TestTextStyleBuilder builder) { builder.color = const ui.Color(0xff000000); }, - 'decoration': (_TestTextStyleBuilder builder) { builder.decoration = ui.TextDecoration.none; }, - 'decorationColor': (_TestTextStyleBuilder builder) { builder.decorationColor = const ui.Color(0xffaa0000); }, - 'decorationStyle': (_TestTextStyleBuilder builder) { builder.decorationStyle = ui.TextDecorationStyle.solid; }, - 'decorationThickness': (_TestTextStyleBuilder builder) { builder.decorationThickness = 1.0; }, - 'fontWeight': (_TestTextStyleBuilder builder) { builder.fontWeight = ui.FontWeight.w400; }, - 'fontStyle': (_TestTextStyleBuilder builder) { builder.fontStyle = ui.FontStyle.normal; }, - 'textBaseline': (_TestTextStyleBuilder builder) { builder.textBaseline = ui.TextBaseline.alphabetic; }, - 'fontFamily': (_TestTextStyleBuilder builder) { builder.fontFamily = 'Arial'; }, - 'fontFamilyFallback': (_TestTextStyleBuilder builder) { builder.fontFamilyFallback = ['Roboto']; }, - 'fontSize': (_TestTextStyleBuilder builder) { builder.fontSize = 12; }, - 'letterSpacing': (_TestTextStyleBuilder builder) { builder.letterSpacing = 1.2; }, - 'wordSpacing': (_TestTextStyleBuilder builder) { builder.wordSpacing = 2.3; }, - 'height': (_TestTextStyleBuilder builder) { builder.height = 13; }, - 'leadingDistribution': (_TestTextStyleBuilder builder) { builder.leadingDistribution = ui.TextLeadingDistribution.proportional; }, - 'locale': (_TestTextStyleBuilder builder) { builder.locale = const ui.Locale('en', 'US'); }, - 'background': (_TestTextStyleBuilder builder) { builder.background = _backgroundA; }, - 'foreground': (_TestTextStyleBuilder builder) { builder.foreground = _foregroundA; }, - 'shadows': (_TestTextStyleBuilder builder) { builder.shadows = [const ui.Shadow()]; }, - 'fontFeatures': (_TestTextStyleBuilder builder) { builder.fontFeatures = [const ui.FontFeature.caseSensitiveForms()]; }, - 'fontVariations': (_TestTextStyleBuilder builder) { builder.fontVariations = [ const ui.FontVariation.italic(0.1)]; }, + 'color': (_TestTextStyleBuilder builder) { + builder.color = const ui.Color(0xff000000); + }, + 'decoration': (_TestTextStyleBuilder builder) { + builder.decoration = ui.TextDecoration.none; + }, + 'decorationColor': (_TestTextStyleBuilder builder) { + builder.decorationColor = const ui.Color(0xffaa0000); + }, + 'decorationStyle': (_TestTextStyleBuilder builder) { + builder.decorationStyle = ui.TextDecorationStyle.solid; + }, + 'decorationThickness': (_TestTextStyleBuilder builder) { + builder.decorationThickness = 1.0; + }, + 'fontWeight': (_TestTextStyleBuilder builder) { + builder.fontWeight = ui.FontWeight.w400; + }, + 'fontStyle': (_TestTextStyleBuilder builder) { + builder.fontStyle = ui.FontStyle.normal; + }, + 'textBaseline': (_TestTextStyleBuilder builder) { + builder.textBaseline = ui.TextBaseline.alphabetic; + }, + 'fontFamily': (_TestTextStyleBuilder builder) { + builder.fontFamily = 'Arial'; + }, + 'fontFamilyFallback': (_TestTextStyleBuilder builder) { + builder.fontFamilyFallback = ['Roboto']; + }, + 'fontSize': (_TestTextStyleBuilder builder) { + builder.fontSize = 12; + }, + 'letterSpacing': (_TestTextStyleBuilder builder) { + builder.letterSpacing = 1.2; + }, + 'wordSpacing': (_TestTextStyleBuilder builder) { + builder.wordSpacing = 2.3; + }, + 'height': (_TestTextStyleBuilder builder) { + builder.height = 13; + }, + 'leadingDistribution': (_TestTextStyleBuilder builder) { + builder.leadingDistribution = ui.TextLeadingDistribution.proportional; + }, + 'locale': (_TestTextStyleBuilder builder) { + builder.locale = const ui.Locale('en', 'US'); + }, + 'background': (_TestTextStyleBuilder builder) { + builder.background = _backgroundA; + }, + 'foreground': (_TestTextStyleBuilder builder) { + builder.foreground = _foregroundA; + }, + 'shadows': (_TestTextStyleBuilder builder) { + builder.shadows = [const ui.Shadow()]; + }, + 'fontFeatures': (_TestTextStyleBuilder builder) { + builder.fontFeatures = [const ui.FontFeature.caseSensitiveForms()]; + }, + 'fontVariations': (_TestTextStyleBuilder builder) { + builder.fontVariations = [const ui.FontVariation.italic(0.1)]; + }, }; // Intentionally do not use const List expressions to make sure Object.hashAll is used to compute hashCode final Map _populatorsB = { - 'color': (_TestTextStyleBuilder builder) { builder.color = const ui.Color(0xffbb0000); }, - 'decoration': (_TestTextStyleBuilder builder) { builder.decoration = ui.TextDecoration.lineThrough; }, - 'decorationColor': (_TestTextStyleBuilder builder) { builder.decorationColor = const ui.Color(0xffcc0000); }, - 'decorationStyle': (_TestTextStyleBuilder builder) { builder.decorationStyle = ui.TextDecorationStyle.dotted; }, - 'decorationThickness': (_TestTextStyleBuilder builder) { builder.decorationThickness = 1.4; }, - 'fontWeight': (_TestTextStyleBuilder builder) { builder.fontWeight = ui.FontWeight.w600; }, - 'fontStyle': (_TestTextStyleBuilder builder) { builder.fontStyle = ui.FontStyle.italic; }, - 'textBaseline': (_TestTextStyleBuilder builder) { builder.textBaseline = ui.TextBaseline.ideographic; }, - 'fontFamily': (_TestTextStyleBuilder builder) { builder.fontFamily = 'Noto'; }, - 'fontFamilyFallback': (_TestTextStyleBuilder builder) { builder.fontFamilyFallback = ['Verdana']; }, - 'fontSize': (_TestTextStyleBuilder builder) { builder.fontSize = 12.1; }, - 'letterSpacing': (_TestTextStyleBuilder builder) { builder.letterSpacing = 1.25; }, - 'wordSpacing': (_TestTextStyleBuilder builder) { builder.wordSpacing = 2.35; }, - 'height': (_TestTextStyleBuilder builder) { builder.height = 13.1; }, - 'leadingDistribution': (_TestTextStyleBuilder builder) { builder.leadingDistribution = ui.TextLeadingDistribution.even; }, - 'locale': (_TestTextStyleBuilder builder) { builder.locale = const ui.Locale('fr', 'CA'); }, - 'background': (_TestTextStyleBuilder builder) { builder.background = _backgroundB; }, - 'foreground': (_TestTextStyleBuilder builder) { builder.foreground = _foregroundB; }, - 'shadows': (_TestTextStyleBuilder builder) { builder.shadows = [const ui.Shadow(blurRadius: 5)]; }, - 'fontFeatures': (_TestTextStyleBuilder builder) { builder.fontFeatures = [const ui.FontFeature.alternative(2)]; }, - 'fontVariations': (_TestTextStyleBuilder builder) { builder.fontVariations = [ const ui.FontVariation.italic(0.4)]; }, + 'color': (_TestTextStyleBuilder builder) { + builder.color = const ui.Color(0xffbb0000); + }, + 'decoration': (_TestTextStyleBuilder builder) { + builder.decoration = ui.TextDecoration.lineThrough; + }, + 'decorationColor': (_TestTextStyleBuilder builder) { + builder.decorationColor = const ui.Color(0xffcc0000); + }, + 'decorationStyle': (_TestTextStyleBuilder builder) { + builder.decorationStyle = ui.TextDecorationStyle.dotted; + }, + 'decorationThickness': (_TestTextStyleBuilder builder) { + builder.decorationThickness = 1.4; + }, + 'fontWeight': (_TestTextStyleBuilder builder) { + builder.fontWeight = ui.FontWeight.w600; + }, + 'fontStyle': (_TestTextStyleBuilder builder) { + builder.fontStyle = ui.FontStyle.italic; + }, + 'textBaseline': (_TestTextStyleBuilder builder) { + builder.textBaseline = ui.TextBaseline.ideographic; + }, + 'fontFamily': (_TestTextStyleBuilder builder) { + builder.fontFamily = 'Noto'; + }, + 'fontFamilyFallback': (_TestTextStyleBuilder builder) { + builder.fontFamilyFallback = ['Verdana']; + }, + 'fontSize': (_TestTextStyleBuilder builder) { + builder.fontSize = 12.1; + }, + 'letterSpacing': (_TestTextStyleBuilder builder) { + builder.letterSpacing = 1.25; + }, + 'wordSpacing': (_TestTextStyleBuilder builder) { + builder.wordSpacing = 2.35; + }, + 'height': (_TestTextStyleBuilder builder) { + builder.height = 13.1; + }, + 'leadingDistribution': (_TestTextStyleBuilder builder) { + builder.leadingDistribution = ui.TextLeadingDistribution.even; + }, + 'locale': (_TestTextStyleBuilder builder) { + builder.locale = const ui.Locale('fr', 'CA'); + }, + 'background': (_TestTextStyleBuilder builder) { + builder.background = _backgroundB; + }, + 'foreground': (_TestTextStyleBuilder builder) { + builder.foreground = _foregroundB; + }, + 'shadows': (_TestTextStyleBuilder builder) { + builder.shadows = [const ui.Shadow(blurRadius: 5)]; + }, + 'fontFeatures': (_TestTextStyleBuilder builder) { + builder.fontFeatures = [const ui.FontFeature.alternative(2)]; + }, + 'fontVariations': (_TestTextStyleBuilder builder) { + builder.fontVariations = [const ui.FontVariation.italic(0.4)]; + }, }; class _TestTextStyleBuilder { diff --git a/lib/web_ui/test/ui/text_style_test_env_test.dart b/lib/web_ui/test/ui/text_style_test_env_test.dart index b65946aed1dc8..b519ee201082b 100644 --- a/lib/web_ui/test/ui/text_style_test_env_test.dart +++ b/lib/web_ui/test/ui/text_style_test_env_test.dart @@ -21,9 +21,7 @@ Future testMain() async { final ui.TextStyle style1 = ui.TextStyle(); expect(style1.toString(), contains('fontFamily: unspecified')); - final ui.TextStyle style2 = ui.TextStyle( - fontFamily: 'Hello', - ); + final ui.TextStyle style2 = ui.TextStyle(fontFamily: 'Hello'); expect(style2.toString(), contains('fontFamily: Hello')); }); @@ -31,9 +29,7 @@ Future testMain() async { final ui.ParagraphStyle style1 = ui.ParagraphStyle(); expect(style1.toString(), contains('fontFamily: unspecified')); - final ui.ParagraphStyle style2 = ui.ParagraphStyle( - fontFamily: 'Hello', - ); + final ui.ParagraphStyle style2 = ui.ParagraphStyle(fontFamily: 'Hello'); expect(style2.toString(), contains('fontFamily: Hello')); }); } diff --git a/lib/web_ui/test/ui/url_strategy_test.dart b/lib/web_ui/test/ui/url_strategy_test.dart index 46299accbac94..31a84fa0ce3cd 100644 --- a/lib/web_ui/test/ui/url_strategy_test.dart +++ b/lib/web_ui/test/ui/url_strategy_test.dart @@ -39,23 +39,17 @@ Future testMain() async { expect(ui_web.urlStrategy, customUrlStrategy); expect(ui_web.isCustomUrlStrategySet, isTrue); // Does not allow custom URL strategy to be set again. - expect( - () { - ui_web.urlStrategy = customUrlStrategy; - }, - throwsAssertionError, - ); + expect(() { + ui_web.urlStrategy = customUrlStrategy; + }, throwsAssertionError); }); test('custom URL strategy can be prevented manually', () { ui_web.preventCustomUrlStrategy(); expect(ui_web.isCustomUrlStrategySet, isFalse); - expect( - () { - ui_web.urlStrategy = TestUrlStrategy(); - }, - throwsAssertionError, - ); + expect(() { + ui_web.urlStrategy = TestUrlStrategy(); + }, throwsAssertionError); }); } diff --git a/lib/web_ui/test/ui/utils.dart b/lib/web_ui/test/ui/utils.dart index e160d07dcd674..225fb31b7377d 100644 --- a/lib/web_ui/test/ui/utils.dart +++ b/lib/web_ui/test/ui/utils.dart @@ -5,7 +5,8 @@ import 'dart:async'; import 'package:ui/src/engine.dart'; -import 'package:ui/src/engine/skwasm/skwasm_impl.dart' if (dart.library.html) 'package:ui/src/engine/skwasm/skwasm_stub.dart'; +import 'package:ui/src/engine/skwasm/skwasm_impl.dart' + if (dart.library.html) 'package:ui/src/engine/skwasm/skwasm_stub.dart'; import 'package:ui/ui.dart'; import '../common/rendering.dart'; diff --git a/lib/web_ui/test/ui/vertices_test.dart b/lib/web_ui/test/ui/vertices_test.dart index defceaf6707e5..338d903658f2c 100644 --- a/lib/web_ui/test/ui/vertices_test.dart +++ b/lib/web_ui/test/ui/vertices_test.dart @@ -25,15 +25,8 @@ void testMain() { expect(vertices.debugDisposed, isFalse); final ui.PictureRecorder recorder = ui.PictureRecorder(); - final ui.Canvas canvas = ui.Canvas( - recorder, - const ui.Rect.fromLTRB(0, 0, 100, 100) - ); - canvas.drawVertices( - vertices, - ui.BlendMode.srcOver, - ui.Paint(), - ); + final ui.Canvas canvas = ui.Canvas(recorder, const ui.Rect.fromLTRB(0, 0, 100, 100)); + canvas.drawVertices(vertices, ui.BlendMode.srcOver, ui.Paint()); vertices.dispose(); expect(vertices.debugDisposed, isTrue); }); @@ -63,16 +56,8 @@ void testMain() { ui.Vertices _testVertices() { return ui.Vertices( ui.VertexMode.triangles, - const [ - ui.Offset.zero, - ui.Offset(10, 10), - ui.Offset(0, 20), - ], - textureCoordinates: const [ - ui.Offset.zero, - ui.Offset(10, 10), - ui.Offset(0, 20), - ], + const [ui.Offset.zero, ui.Offset(10, 10), ui.Offset(0, 20)], + textureCoordinates: const [ui.Offset.zero, ui.Offset(10, 10), ui.Offset(0, 20)], colors: const [ ui.Color.fromRGBO(255, 0, 0, 1.0), ui.Color.fromRGBO(0, 255, 0, 1.0), @@ -264,5 +249,5 @@ const List _circularVertexIndices = [ 33, 35, 34, - 36 + 36, ]; diff --git a/runtime/fixtures/dart_tool/flutter_build/dart_plugin_registrant.dart b/runtime/fixtures/dart_tool/flutter_build/dart_plugin_registrant.dart index 71075630145fc..207e71003dd02 100644 --- a/runtime/fixtures/dart_tool/flutter_build/dart_plugin_registrant.dart +++ b/runtime/fixtures/dart_tool/flutter_build/dart_plugin_registrant.dart @@ -48,19 +48,12 @@ void registerBackgroundIsolate(List args) { @pragma('vm:entry-point') Future callDartPluginRegistrantFromBackgroundIsolate() async { final receivePort = ReceivePort(); - final isolate = await Isolate.spawn( - dartPluginRegistrantIsolate, - receivePort.sendPort, - ); + final isolate = await Isolate.spawn(dartPluginRegistrantIsolate, receivePort.sendPort); final didCallEntrypoint = await receivePort.first as bool; if (didCallEntrypoint) { - passMessage( - '_PluginRegistrant.register() was called on background isolate', - ); + passMessage('_PluginRegistrant.register() was called on background isolate'); } else { - passMessage( - '_PluginRegistrant.register() was not called on background isolate', - ); + passMessage('_PluginRegistrant.register() was not called on background isolate'); } isolate.kill(); } @@ -72,19 +65,12 @@ void noDartPluginRegistrantIsolate(SendPort sendPort) { @pragma('vm:entry-point') Future dontCallDartPluginRegistrantFromBackgroundIsolate() async { final receivePort = ReceivePort(); - final isolate = await Isolate.spawn( - noDartPluginRegistrantIsolate, - receivePort.sendPort, - ); + final isolate = await Isolate.spawn(noDartPluginRegistrantIsolate, receivePort.sendPort); final didCallEntrypoint = await receivePort.first as bool; if (didCallEntrypoint) { - passMessage( - '_PluginRegistrant.register() was called on background isolate', - ); + passMessage('_PluginRegistrant.register() was called on background isolate'); } else { - passMessage( - '_PluginRegistrant.register() was not called on background isolate', - ); + passMessage('_PluginRegistrant.register() was not called on background isolate'); } isolate.kill(); } @@ -98,13 +84,9 @@ Future registerBackgroundIsolateCallsDartPluginRegistrant() async { ]); final didCallEntrypoint = await receivePort.first as bool; if (didCallEntrypoint) { - passMessage( - '_PluginRegistrant.register() was called on background isolate', - ); + passMessage('_PluginRegistrant.register() was called on background isolate'); } else { - passMessage( - '_PluginRegistrant.register() was not called on background isolate', - ); + passMessage('_PluginRegistrant.register() was not called on background isolate'); } isolate.kill(); } diff --git a/runtime/fixtures/runtime_test.dart b/runtime/fixtures/runtime_test.dart index 435c00430a1af..0a85f5f97d95a 100644 --- a/runtime/fixtures/runtime_test.dart +++ b/runtime/fixtures/runtime_test.dart @@ -33,13 +33,16 @@ Future? splitLoadFuture; @pragma('vm:entry-point') void canCallDeferredLibrary() { print('In function canCallDeferredLibrary'); - splitLoadFuture = splitlib.loadLibrary().then((_) { - print('Deferred load complete'); - notifySuccess(splitlib.splitAdd(10, 23) == 33); - }).catchError((_) { - print('Deferred load error'); - notifySuccess(false); - }); + splitLoadFuture = splitlib + .loadLibrary() + .then((_) { + print('Deferred load complete'); + notifySuccess(splitlib.splitAdd(10, 23) == 33); + }) + .catchError((_) { + print('Deferred load error'); + notifySuccess(false); + }); notifyNative(); } @@ -65,8 +68,7 @@ void testCanLaunchSecondaryIsolate() { final onExit = RawReceivePort((_) { notifyNative(); }); - Isolate.spawn(secondaryIsolateMain, 'Hello from root isolate.', - onExit: onExit.sendPort); + Isolate.spawn(secondaryIsolateMain, 'Hello from root isolate.', onExit: onExit.sendPort); } @pragma('vm:entry-point') @@ -80,8 +82,10 @@ Future testIsolateStartupFailure() async { final exits = StreamIterator(onExit); await Isolate.spawn( - (SendPort port) => port.send('good'), onMessage.sendPort, - onExit: onExit.sendPort); + (SendPort port) => port.send('good'), + onMessage.sendPort, + onExit: onExit.sendPort, + ); if (!await messages.moveNext()) { throw AssertionError('Failed to receive message'); } @@ -141,29 +145,25 @@ void testCanConvertEmptyList(List args) { @pragma('vm:entry-point') void testCanConvertListOfStrings(List args) { - notifySuccess(args.length == 4 && - args[0] == 'tinker' && - args[1] == 'tailor' && - args[2] == 'soldier' && - args[3] == 'sailor'); + notifySuccess( + args.length == 4 && + args[0] == 'tinker' && + args[1] == 'tailor' && + args[2] == 'soldier' && + args[3] == 'sailor', + ); } @pragma('vm:entry-point') void testCanConvertListOfDoubles(List args) { - notifySuccess(args.length == 4 && - args[0] == 1.0 && - args[1] == 2.0 && - args[2] == 3.0 && - args[3] == 4.0); + notifySuccess( + args.length == 4 && args[0] == 1.0 && args[1] == 2.0 && args[2] == 3.0 && args[3] == 4.0, + ); } @pragma('vm:entry-point') void testCanConvertListOfInts(List args) { - notifySuccess(args.length == 4 && - args[0] == 1 && - args[1] == 2 && - args[2] == 3 && - args[3] == 4); + notifySuccess(args.length == 4 && args[0] == 1 && args[1] == 2 && args[2] == 3 && args[3] == 4); } bool didCallRegistrantBeforeEntrypoint = false; diff --git a/shell/common/fixtures/shell_test.dart b/shell/common/fixtures/shell_test.dart index 24dc227aa8f40..c217114682aee 100644 --- a/shell/common/fixtures/shell_test.dart +++ b/shell/common/fixtures/shell_test.dart @@ -87,8 +87,7 @@ void reportTimingsMain() { } } nativeReportTimingsCallback(timestamps); - PlatformDispatcher.instance.onReportTimings = - (List timings) {}; + PlatformDispatcher.instance.onReportTimings = (List timings) {}; }; } @@ -117,17 +116,12 @@ void emptyMain() {} @pragma('vm:entry-point') void reportMetrics() { window.onMetricsChanged = () { - _reportMetrics( - window.devicePixelRatio, - window.physicalSize.width, - window.physicalSize.height, - ); + _reportMetrics(window.devicePixelRatio, window.physicalSize.width, window.physicalSize.height); }; } @pragma('vm:external-name', 'ReportMetrics') -external void _reportMetrics( - double devicePixelRatio, double width, double height); +external void _reportMetrics(double devicePixelRatio, double width, double height); @pragma('vm:entry-point') void dummyReportTimingsMain() { @@ -197,42 +191,28 @@ external void notifyWidthHeight(int width, int height); void canCreateImageFromDecompressedData() { const int imageWidth = 10; const int imageHeight = 10; - final Uint8List pixels = Uint8List.fromList(List.generate( - imageWidth * imageHeight * 4, - (int i) => i % 4 < 2 ? 0x00 : 0xFF, - )); - - decodeImageFromPixels( - pixels, - imageWidth, - imageHeight, - PixelFormat.rgba8888, - (Image image) { - notifyWidthHeight(image.width, image.height); - }, + final Uint8List pixels = Uint8List.fromList( + List.generate(imageWidth * imageHeight * 4, (int i) => i % 4 < 2 ? 0x00 : 0xFF), ); + + decodeImageFromPixels(pixels, imageWidth, imageHeight, PixelFormat.rgba8888, (Image image) { + notifyWidthHeight(image.width, image.height); + }); } @pragma('vm:entry-point') void canAccessIsolateLaunchData() { notifyMessage( - utf8.decode( - PlatformDispatcher.instance - .getPersistentIsolateData()! - .buffer - .asUint8List(), - ), + utf8.decode(PlatformDispatcher.instance.getPersistentIsolateData()!.buffer.asUint8List()), ); } @pragma('vm:entry-point') void performanceModeImpactsNotifyIdle() { notifyNativeBool(false); - PlatformDispatcher.instance - .requestDartPerformanceMode(DartPerformanceMode.latency); + PlatformDispatcher.instance.requestDartPerformanceMode(DartPerformanceMode.latency); notifyNativeBool(true); - PlatformDispatcher.instance - .requestDartPerformanceMode(DartPerformanceMode.balanced); + PlatformDispatcher.instance.requestDartPerformanceMode(DartPerformanceMode.balanced); } @pragma('vm:entry-point') @@ -309,14 +289,12 @@ external void notifyNativeWhenEngineSpawn(bool success); @pragma('vm:entry-point') void canReceiveArgumentsWhenEngineRun(List args) { - notifyNativeWhenEngineRun( - args.length == 2 && args[0] == 'foo' && args[1] == 'bar'); + notifyNativeWhenEngineRun(args.length == 2 && args[0] == 'foo' && args[1] == 'bar'); } @pragma('vm:entry-point') void canReceiveArgumentsWhenEngineSpawn(List args) { - notifyNativeWhenEngineSpawn( - args.length == 2 && args[0] == 'arg1' && args[1] == 'arg2'); + notifyNativeWhenEngineSpawn(args.length == 2 && args[0] == 'arg1' && args[1] == 'arg2'); } @pragma('vm:entry-point') @@ -328,8 +306,7 @@ void onBeginFrameWithNotifyNativeMain() { } @pragma('vm:entry-point') -void frameCallback( - Object? image, int durationMilliseconds, String decodeError) { +void frameCallback(Object? image, int durationMilliseconds, String decodeError) { if (image == null) { throw Exception('Expeccted image in frame callback to be non-null'); } @@ -391,8 +368,7 @@ class IsolateParam { Future runCallback(IsolateParam param) async { try { final Future Function() func = - PluginUtilities.getCallbackFromHandle( - CallbackHandle.fromRawHandle(param.rawHandle))! + PluginUtilities.getCallbackFromHandle(CallbackHandle.fromRawHandle(param.rawHandle))! as Future Function(); await func.call(); param.sendPort.send(true); @@ -411,10 +387,10 @@ external void notifyDestroyed(); Future testPluginUtilitiesCallbackHandle() async { ReceivePort port = ReceivePort(); await Isolate.spawn( - runCallback, - IsolateParam(port.sendPort, - PluginUtilities.getCallbackHandle(included)!.toRawHandle()), - onError: port.sendPort); + runCallback, + IsolateParam(port.sendPort, PluginUtilities.getCallbackHandle(included)!.toRawHandle()), + onError: port.sendPort, + ); final dynamic result1 = await port.first; if (result1 != true) { print('Expected $result1 to == true'); @@ -425,10 +401,10 @@ Future testPluginUtilitiesCallbackHandle() async { if (const bool.fromEnvironment('dart.vm.product')) { port = ReceivePort(); await Isolate.spawn( - runCallback, - IsolateParam(port.sendPort, - PluginUtilities.getCallbackHandle(excluded)!.toRawHandle()), - onError: port.sendPort); + runCallback, + IsolateParam(port.sendPort, PluginUtilities.getCallbackHandle(excluded)!.toRawHandle()), + onError: port.sendPort, + ); final dynamic result2 = await port.first; if (result2 != false) { print('Expected $result2 to == false'); @@ -444,21 +420,19 @@ Future testPluginUtilitiesCallbackHandle() async { Future testThatAssetLoadingHappensOnWorkerThread() async { try { await ImmutableBuffer.fromAsset('DoesNotExist'); - } catch (err) {/* Do nothing */} + } catch (err) { + /* Do nothing */ + } notifyNative(); } @pragma('vm:external-name', 'NativeReportViewIdsCallback') -external void nativeReportViewIdsCallback( - bool hasImplicitView, List viewIds); +external void nativeReportViewIdsCallback(bool hasImplicitView, List viewIds); List getCurrentViewIds() { - final List result = PlatformDispatcher.instance.views - .map((FlutterView view) => view.viewId) - .toList() - ..sort(); - assert(result.toSet().length == result.length, - 'Unexpected duplicate view ID found: $result'); + final List result = + PlatformDispatcher.instance.views.map((FlutterView view) => view.viewId).toList()..sort(); + assert(result.toSet().length == result.length, 'Unexpected duplicate view ID found: $result'); return result; } @@ -480,14 +454,12 @@ bool listEquals(List a, List b) { @pragma('vm:entry-point') void testReportViewIds() { List viewIds = getCurrentViewIds(); - nativeReportViewIdsCallback( - PlatformDispatcher.instance.implicitView != null, viewIds); + nativeReportViewIdsCallback(PlatformDispatcher.instance.implicitView != null, viewIds); PlatformDispatcher.instance.onMetricsChanged = () { final List newViewIds = getCurrentViewIds(); if (!listEquals(viewIds, newViewIds)) { viewIds = newViewIds; - nativeReportViewIdsCallback( - PlatformDispatcher.instance.implicitView != null, viewIds); + nativeReportViewIdsCallback(PlatformDispatcher.instance.implicitView != null, viewIds); } }; } @@ -619,16 +591,19 @@ void renderWarmUpImplicitView() { void renderWarmUpView1and2() { bool beginFrameCalled = false; - PlatformDispatcher.instance.scheduleWarmUpFrame(beginFrame: () { - expect(beginFrameCalled, false); - beginFrameCalled = true; - }, drawFrame: () { - expect(beginFrameCalled, true); + PlatformDispatcher.instance.scheduleWarmUpFrame( + beginFrame: () { + expect(beginFrameCalled, false); + beginFrameCalled = true; + }, + drawFrame: () { + expect(beginFrameCalled, true); - for (final int viewId in [1, 2]) { - renderDummyToView(PlatformDispatcher.instance.view(id: viewId)!); - } - }); + for (final int viewId in [1, 2]) { + renderDummyToView(PlatformDispatcher.instance.view(id: viewId)!); + } + }, + ); } @pragma('vm:entry-point') diff --git a/shell/platform/darwin/macos/framework/Source/fixtures/flutter_desktop_test.dart b/shell/platform/darwin/macos/framework/Source/fixtures/flutter_desktop_test.dart index fce10360dea47..408c32151984d 100644 --- a/shell/platform/darwin/macos/framework/Source/fixtures/flutter_desktop_test.dart +++ b/shell/platform/darwin/macos/framework/Source/fixtures/flutter_desktop_test.dart @@ -83,6 +83,5 @@ void backgroundTest() { @pragma('vm:entry-point') void sendFooMessage() { - PlatformDispatcher.instance - .sendPlatformMessage('foo', null, (ByteData? result) {}); + PlatformDispatcher.instance.sendPlatformMessage('foo', null, (ByteData? result) {}); } diff --git a/shell/platform/embedder/fixtures/main.dart b/shell/platform/embedder/fixtures/main.dart index dabbb1e96ab7e..a2015e765e85f 100644 --- a/shell/platform/embedder/fixtures/main.dart +++ b/shell/platform/embedder/fixtures/main.dart @@ -102,8 +102,7 @@ external void ffiSignalNativeTest(); /// `PlatformDispatcher.instance.onSemanticsEnabledChanged` fires. Future get semanticsChanged { final Completer semanticsChanged = Completer(); - PlatformDispatcher.instance.onSemanticsEnabledChanged = - semanticsChanged.complete; + PlatformDispatcher.instance.onSemanticsEnabledChanged = semanticsChanged.complete; return semanticsChanged.future; } @@ -111,16 +110,13 @@ Future get semanticsChanged { /// `PlatformDispatcher.instance.onAccessibilityFeaturesChanged` fires. Future get accessibilityFeaturesChanged { final Completer featuresChanged = Completer(); - PlatformDispatcher.instance.onAccessibilityFeaturesChanged = - featuresChanged.complete; + PlatformDispatcher.instance.onAccessibilityFeaturesChanged = featuresChanged.complete; return featuresChanged.future; } Future get semanticsActionEvent { - final Completer actionReceived = - Completer(); - PlatformDispatcher.instance.onSemanticsActionEvent = - (SemanticsActionEvent action) { + final Completer actionReceived = Completer(); + PlatformDispatcher.instance.onSemanticsActionEvent = (SemanticsActionEvent action) { actionReceived.complete(action); }; return actionReceived.future; @@ -137,161 +133,156 @@ Future a11y_main() async { notifySemanticsEnabled(PlatformDispatcher.instance.semanticsEnabled); // 3: Return initial state of accessibility features. - notifyAccessibilityFeatures( - PlatformDispatcher.instance.accessibilityFeatures.reduceMotion); + notifyAccessibilityFeatures(PlatformDispatcher.instance.accessibilityFeatures.reduceMotion); // 4: Await accessibility features changed from embedder. await accessibilityFeaturesChanged; - notifyAccessibilityFeatures( - PlatformDispatcher.instance.accessibilityFeatures.reduceMotion); + notifyAccessibilityFeatures(PlatformDispatcher.instance.accessibilityFeatures.reduceMotion); // 5: Fire semantics update. - final SemanticsUpdateBuilder builder = SemanticsUpdateBuilder() - ..updateNode( - id: 42, - identifier: '', - label: 'A: root', - labelAttributes: [], - rect: const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0), - transform: kTestTransform, - childrenInTraversalOrder: Int32List.fromList([84, 96]), - childrenInHitTestOrder: Int32List.fromList([96, 84]), - actions: 0, - flags: 0, - maxValueLength: 0, - currentValueLength: 0, - textSelectionBase: 0, - textSelectionExtent: 0, - platformViewId: 0, - scrollChildren: 0, - scrollIndex: 0, - scrollPosition: 0.0, - scrollExtentMax: 0.0, - scrollExtentMin: 0.0, - elevation: 0.0, - thickness: 0.0, - hint: '', - hintAttributes: [], - value: '', - valueAttributes: [], - increasedValue: '', - increasedValueAttributes: [], - decreasedValue: '', - decreasedValueAttributes: [], - tooltip: 'tooltip', - textDirection: TextDirection.ltr, - additionalActions: Int32List(0), - ) - ..updateNode( - id: 84, - identifier: '', - label: 'B: leaf', - labelAttributes: [], - rect: const Rect.fromLTRB(40.0, 40.0, 80.0, 80.0), - transform: kTestTransform, - actions: 0, - flags: 0, - maxValueLength: 0, - currentValueLength: 0, - textSelectionBase: 0, - textSelectionExtent: 0, - platformViewId: 0, - scrollChildren: 0, - scrollIndex: 0, - scrollPosition: 0.0, - scrollExtentMax: 0.0, - scrollExtentMin: 0.0, - elevation: 0.0, - thickness: 0.0, - hint: '', - hintAttributes: [], - value: '', - valueAttributes: [], - increasedValue: '', - increasedValueAttributes: [], - decreasedValue: '', - decreasedValueAttributes: [], - tooltip: 'tooltip', - textDirection: TextDirection.ltr, - additionalActions: Int32List(0), - childrenInHitTestOrder: Int32List(0), - childrenInTraversalOrder: Int32List(0), - ) - ..updateNode( - id: 96, - identifier: '', - label: 'C: branch', - labelAttributes: [], - rect: const Rect.fromLTRB(40.0, 40.0, 80.0, 80.0), - transform: kTestTransform, - childrenInTraversalOrder: Int32List.fromList([128]), - childrenInHitTestOrder: Int32List.fromList([128]), - actions: 0, - flags: 0, - maxValueLength: 0, - currentValueLength: 0, - textSelectionBase: 0, - textSelectionExtent: 0, - platformViewId: 0, - scrollChildren: 0, - scrollIndex: 0, - scrollPosition: 0.0, - scrollExtentMax: 0.0, - scrollExtentMin: 0.0, - elevation: 0.0, - thickness: 0.0, - hint: '', - hintAttributes: [], - value: '', - valueAttributes: [], - increasedValue: '', - increasedValueAttributes: [], - decreasedValue: '', - decreasedValueAttributes: [], - tooltip: 'tooltip', - textDirection: TextDirection.ltr, - additionalActions: Int32List(0), - ) - ..updateNode( - id: 128, - identifier: '', - label: 'D: leaf', - labelAttributes: [], - rect: const Rect.fromLTRB(40.0, 40.0, 80.0, 80.0), - transform: kTestTransform, - additionalActions: Int32List.fromList([21]), - platformViewId: 0x3f3, - actions: 0, - flags: 0, - maxValueLength: 0, - currentValueLength: 0, - textSelectionBase: 0, - textSelectionExtent: 0, - scrollChildren: 0, - scrollIndex: 0, - scrollPosition: 0.0, - scrollExtentMax: 0.0, - scrollExtentMin: 0.0, - elevation: 0.0, - thickness: 0.0, - hint: '', - hintAttributes: [], - value: '', - valueAttributes: [], - increasedValue: '', - increasedValueAttributes: [], - decreasedValue: '', - decreasedValueAttributes: [], - tooltip: 'tooltip', - textDirection: TextDirection.ltr, - childrenInHitTestOrder: Int32List(0), - childrenInTraversalOrder: Int32List(0), - ) - ..updateCustomAction( - id: 21, - label: 'Archive', - hint: 'archive message', - ); + final SemanticsUpdateBuilder builder = + SemanticsUpdateBuilder() + ..updateNode( + id: 42, + identifier: '', + label: 'A: root', + labelAttributes: [], + rect: const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0), + transform: kTestTransform, + childrenInTraversalOrder: Int32List.fromList([84, 96]), + childrenInHitTestOrder: Int32List.fromList([96, 84]), + actions: 0, + flags: 0, + maxValueLength: 0, + currentValueLength: 0, + textSelectionBase: 0, + textSelectionExtent: 0, + platformViewId: 0, + scrollChildren: 0, + scrollIndex: 0, + scrollPosition: 0.0, + scrollExtentMax: 0.0, + scrollExtentMin: 0.0, + elevation: 0.0, + thickness: 0.0, + hint: '', + hintAttributes: [], + value: '', + valueAttributes: [], + increasedValue: '', + increasedValueAttributes: [], + decreasedValue: '', + decreasedValueAttributes: [], + tooltip: 'tooltip', + textDirection: TextDirection.ltr, + additionalActions: Int32List(0), + ) + ..updateNode( + id: 84, + identifier: '', + label: 'B: leaf', + labelAttributes: [], + rect: const Rect.fromLTRB(40.0, 40.0, 80.0, 80.0), + transform: kTestTransform, + actions: 0, + flags: 0, + maxValueLength: 0, + currentValueLength: 0, + textSelectionBase: 0, + textSelectionExtent: 0, + platformViewId: 0, + scrollChildren: 0, + scrollIndex: 0, + scrollPosition: 0.0, + scrollExtentMax: 0.0, + scrollExtentMin: 0.0, + elevation: 0.0, + thickness: 0.0, + hint: '', + hintAttributes: [], + value: '', + valueAttributes: [], + increasedValue: '', + increasedValueAttributes: [], + decreasedValue: '', + decreasedValueAttributes: [], + tooltip: 'tooltip', + textDirection: TextDirection.ltr, + additionalActions: Int32List(0), + childrenInHitTestOrder: Int32List(0), + childrenInTraversalOrder: Int32List(0), + ) + ..updateNode( + id: 96, + identifier: '', + label: 'C: branch', + labelAttributes: [], + rect: const Rect.fromLTRB(40.0, 40.0, 80.0, 80.0), + transform: kTestTransform, + childrenInTraversalOrder: Int32List.fromList([128]), + childrenInHitTestOrder: Int32List.fromList([128]), + actions: 0, + flags: 0, + maxValueLength: 0, + currentValueLength: 0, + textSelectionBase: 0, + textSelectionExtent: 0, + platformViewId: 0, + scrollChildren: 0, + scrollIndex: 0, + scrollPosition: 0.0, + scrollExtentMax: 0.0, + scrollExtentMin: 0.0, + elevation: 0.0, + thickness: 0.0, + hint: '', + hintAttributes: [], + value: '', + valueAttributes: [], + increasedValue: '', + increasedValueAttributes: [], + decreasedValue: '', + decreasedValueAttributes: [], + tooltip: 'tooltip', + textDirection: TextDirection.ltr, + additionalActions: Int32List(0), + ) + ..updateNode( + id: 128, + identifier: '', + label: 'D: leaf', + labelAttributes: [], + rect: const Rect.fromLTRB(40.0, 40.0, 80.0, 80.0), + transform: kTestTransform, + additionalActions: Int32List.fromList([21]), + platformViewId: 0x3f3, + actions: 0, + flags: 0, + maxValueLength: 0, + currentValueLength: 0, + textSelectionBase: 0, + textSelectionExtent: 0, + scrollChildren: 0, + scrollIndex: 0, + scrollPosition: 0.0, + scrollExtentMax: 0.0, + scrollExtentMin: 0.0, + elevation: 0.0, + thickness: 0.0, + hint: '', + hintAttributes: [], + value: '', + valueAttributes: [], + increasedValue: '', + increasedValueAttributes: [], + decreasedValue: '', + decreasedValueAttributes: [], + tooltip: 'tooltip', + textDirection: TextDirection.ltr, + childrenInHitTestOrder: Int32List(0), + childrenInTraversalOrder: Int32List(0), + ) + ..updateCustomAction(id: 21, label: 'Archive', hint: 'archive message'); PlatformDispatcher.instance.views.first.updateSemantics(builder.build()); @@ -301,7 +292,7 @@ Future a11y_main() async { final SemanticsActionEvent data = await semanticsActionEvent; final List actionArgs = [ (data.arguments! as ByteData).getInt8(0), - (data.arguments! as ByteData).getInt8(1) + (data.arguments! as ByteData).getInt8(1), ]; notifySemanticsAction(data.nodeId, data.type.index, actionArgs); @@ -319,72 +310,65 @@ Future a11y_string_attributes() async { } // 2: Update semantics with string attributes. - final SemanticsUpdateBuilder builder = SemanticsUpdateBuilder() - ..updateNode( - id: 42, - identifier: 'identifier', - label: 'What is the meaning of life?', - labelAttributes: [ - LocaleStringAttribute( - range: const TextRange( - start: 0, end: 'What is the meaning of life?'.length), - locale: const Locale('en'), - ), - SpellOutStringAttribute( - range: const TextRange(start: 0, end: 1), - ), - ], - rect: const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0), - transform: kTestTransform, - childrenInTraversalOrder: Int32List.fromList([84, 96]), - childrenInHitTestOrder: Int32List.fromList([96, 84]), - actions: 0, - flags: 0, - maxValueLength: 0, - currentValueLength: 0, - textSelectionBase: 0, - textSelectionExtent: 0, - platformViewId: 0, - scrollChildren: 0, - scrollIndex: 0, - scrollPosition: 0.0, - scrollExtentMax: 0.0, - scrollExtentMin: 0.0, - elevation: 0.0, - thickness: 0.0, - hint: "It's a number", - hintAttributes: [ - LocaleStringAttribute( - range: const TextRange(start: 0, end: 1), - locale: const Locale('en'), - ), - LocaleStringAttribute( - range: const TextRange(start: 2, end: 3), - locale: const Locale('fr'), - ), - ], - value: '42', - valueAttributes: [ - LocaleStringAttribute( - range: const TextRange(start: 0, end: '42'.length), - locale: const Locale('en', 'US'), - ), - ], - increasedValue: '43', - increasedValueAttributes: [ - SpellOutStringAttribute( - range: const TextRange(start: 0, end: 1), - ), - SpellOutStringAttribute( - range: const TextRange(start: 1, end: 2), - ), - ], - decreasedValue: '41', - decreasedValueAttributes: [], - tooltip: 'tooltip', - textDirection: TextDirection.ltr, - additionalActions: Int32List(0), - ); + final SemanticsUpdateBuilder builder = + SemanticsUpdateBuilder()..updateNode( + id: 42, + identifier: 'identifier', + label: 'What is the meaning of life?', + labelAttributes: [ + LocaleStringAttribute( + range: const TextRange(start: 0, end: 'What is the meaning of life?'.length), + locale: const Locale('en'), + ), + SpellOutStringAttribute(range: const TextRange(start: 0, end: 1)), + ], + rect: const Rect.fromLTRB(0.0, 0.0, 10.0, 10.0), + transform: kTestTransform, + childrenInTraversalOrder: Int32List.fromList([84, 96]), + childrenInHitTestOrder: Int32List.fromList([96, 84]), + actions: 0, + flags: 0, + maxValueLength: 0, + currentValueLength: 0, + textSelectionBase: 0, + textSelectionExtent: 0, + platformViewId: 0, + scrollChildren: 0, + scrollIndex: 0, + scrollPosition: 0.0, + scrollExtentMax: 0.0, + scrollExtentMin: 0.0, + elevation: 0.0, + thickness: 0.0, + hint: "It's a number", + hintAttributes: [ + LocaleStringAttribute( + range: const TextRange(start: 0, end: 1), + locale: const Locale('en'), + ), + LocaleStringAttribute( + range: const TextRange(start: 2, end: 3), + locale: const Locale('fr'), + ), + ], + value: '42', + valueAttributes: [ + LocaleStringAttribute( + range: const TextRange(start: 0, end: '42'.length), + locale: const Locale('en', 'US'), + ), + ], + increasedValue: '43', + increasedValueAttributes: [ + SpellOutStringAttribute(range: const TextRange(start: 0, end: 1)), + SpellOutStringAttribute(range: const TextRange(start: 1, end: 2)), + ], + decreasedValue: '41', + decreasedValueAttributes: [], + tooltip: 'tooltip', + textDirection: TextDirection.ltr, + additionalActions: Int32List(0), + ); PlatformDispatcher.instance.views.first.updateSemantics(builder.build()); signalNativeTest(); @@ -393,8 +377,11 @@ Future a11y_string_attributes() async { @pragma('vm:entry-point') // ignore: non_constant_identifier_names void platform_messages_response() { - PlatformDispatcher.instance.onPlatformMessage = - (String name, ByteData? data, PlatformMessageResponseCallback? callback) { + PlatformDispatcher.instance.onPlatformMessage = ( + String name, + ByteData? data, + PlatformMessageResponseCallback? callback, + ) { callback!(data); }; signalNativeTest(); @@ -403,10 +390,12 @@ void platform_messages_response() { @pragma('vm:entry-point') // ignore: non_constant_identifier_names void platform_messages_no_response() { - PlatformDispatcher.instance.onPlatformMessage = - (String name, ByteData? data, PlatformMessageResponseCallback? callback) { - final Uint8List list = - data!.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); + PlatformDispatcher.instance.onPlatformMessage = ( + String name, + ByteData? data, + PlatformMessageResponseCallback? callback, + ) { + final Uint8List list = data!.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); signalNativeMessage(utf8.decode(list)); // This does nothing because no one is listening on the other side. But complete the loop anyway // to make sure all null checking on response handles in the engine is in place. @@ -418,8 +407,11 @@ void platform_messages_no_response() { @pragma('vm:entry-point') // ignore: non_constant_identifier_names void null_platform_messages() { - PlatformDispatcher.instance.onPlatformMessage = - (String name, ByteData? data, PlatformMessageResponseCallback? callback) { + PlatformDispatcher.instance.onPlatformMessage = ( + String name, + ByteData? data, + PlatformMessageResponseCallback? callback, + ) { // This checks if the platform_message null data is converted to Flutter null. signalNativeMessage((null == data).toString()); callback!(data); @@ -429,8 +421,7 @@ void null_platform_messages() { Picture createSimplePicture() { final Paint blackPaint = Paint(); - final Paint whitePaint = Paint() - ..color = const Color.fromARGB(255, 255, 255, 255); + final Paint whitePaint = Paint()..color = const Color.fromARGB(255, 255, 255, 255); final PictureRecorder baseRecorder = PictureRecorder(); final Canvas canvas = Canvas(baseRecorder); canvas.drawRect(const Rect.fromLTRB(0.0, 0.0, 1000.0, 1000.0), blackPaint); @@ -524,28 +515,23 @@ void can_composite_platform_views_with_known_scene() { builder.pushOffset(0.0, 0.0); // 10 (Index 0) - builder.addPicture( - const Offset(10.0, 10.0), createColoredBox(red, size)); // red - flutter + builder.addPicture(const Offset(10.0, 10.0), createColoredBox(red, size)); // red - flutter builder.pushOffset(20.0, 20.0); // 20 (Index 1) - builder.addPlatformView(1, - width: size.width, height: size.height); // green - platform + builder.addPlatformView(1, width: size.width, height: size.height); // green - platform builder.pop(); // 30 (Index 2) - builder.addPicture(const Offset(30.0, 30.0), - createColoredBox(blue, size)); // blue - flutter + builder.addPicture(const Offset(30.0, 30.0), createColoredBox(blue, size)); // blue - flutter builder.pushOffset(40.0, 40.0); // 40 (Index 3) - builder.addPlatformView(2, - width: size.width, height: size.height); // magenta - platform + builder.addPlatformView(2, width: size.width, height: size.height); // magenta - platform builder.pop(); // 50 (Index 4) - builder.addPicture(const Offset(50.0, 50.0), - createColoredBox(gray, size)); // gray - flutter + builder.addPicture(const Offset(50.0, 50.0), createColoredBox(gray, size)); // gray - flutter builder.pop(); @@ -570,20 +556,18 @@ void can_composite_platform_views_transparent_overlay() { builder.pushOffset(0.0, 0.0); // 10 (Index 0) - builder.addPicture( - const Offset(10.0, 10.0), createColoredBox(red, size)); // red - flutter + builder.addPicture(const Offset(10.0, 10.0), createColoredBox(red, size)); // red - flutter builder.pushOffset(20.0, 20.0); // 20 (Index 1) - builder.addPlatformView(1, - width: size.width, height: size.height); // green - platform + builder.addPlatformView(1, width: size.width, height: size.height); // green - platform builder.pop(); // 30 (Index 2) builder.addPicture( - const Offset(30.0, 30.0), - createColoredBox(transparent, - size)); // transparent picture, no layer should be created. + const Offset(30.0, 30.0), + createColoredBox(transparent, size), + ); // transparent picture, no layer should be created. builder.pop(); @@ -606,13 +590,11 @@ void can_composite_platform_views_no_overlay() { builder.pushOffset(0.0, 0.0); // 10 (Index 0) - builder.addPicture( - const Offset(10.0, 10.0), createColoredBox(red, size)); // red - flutter + builder.addPicture(const Offset(10.0, 10.0), createColoredBox(red, size)); // red - flutter builder.pushOffset(20.0, 20.0); // 20 (Index 1) - builder.addPlatformView(1, - width: size.width, height: size.height); // green - platform + builder.addPlatformView(1, width: size.width, height: size.height); // green - platform builder.pop(); builder.pop(); @@ -635,8 +617,7 @@ void can_composite_platform_views_with_root_layer_only() { builder.pushOffset(0.0, 0.0); // 10 (Index 0) - builder.addPicture( - const Offset(10.0, 10.0), createColoredBox(red, size)); // red - flutter + builder.addPicture(const Offset(10.0, 10.0), createColoredBox(red, size)); // red - flutter builder.pop(); PlatformDispatcher.instance.views.first.render(builder.build()); @@ -658,13 +639,11 @@ void can_composite_platform_views_with_platform_layer_on_bottom() { builder.pushOffset(0.0, 0.0); // 10 (Index 0) - builder.addPicture( - const Offset(10.0, 10.0), createColoredBox(red, size)); // red - flutter + builder.addPicture(const Offset(10.0, 10.0), createColoredBox(red, size)); // red - flutter builder.pushOffset(20.0, 20.0); // 20 (Index 1) - builder.addPlatformView(1, - width: size.width, height: size.height); // green - platform + builder.addPlatformView(1, width: size.width, height: size.height); // green - platform builder.pop(); builder.pop(); @@ -682,15 +661,14 @@ external void signalBeginFrame(); @pragma('vm:entry-point') Future - // ignore: non_constant_identifier_names - texture_destruction_callback_called_without_custom_compositor() async { +// ignore: non_constant_identifier_names +texture_destruction_callback_called_without_custom_compositor() async { PlatformDispatcher.instance.onBeginFrame = (Duration duration) { const Color red = Color.fromARGB(127, 255, 0, 0); const Size size = Size(50.0, 150.0); final SceneBuilder builder = SceneBuilder(); builder.pushOffset(0.0, 0.0); - builder.addPicture( - const Offset(10.0, 10.0), createColoredBox(red, size)); // red - flutter + builder.addPicture(const Offset(10.0, 10.0), createColoredBox(red, size)); // red - flutter builder.pop(); PlatformDispatcher.instance.views.first.render(builder.build()); }; @@ -713,20 +691,18 @@ void can_render_scene_without_custom_compositor() { builder.pushOffset(0.0, 0.0); - builder.addPicture( - const Offset(10.0, 10.0), createColoredBox(red, size)); // red - flutter + builder.addPicture(const Offset(10.0, 10.0), createColoredBox(red, size)); // red - flutter - builder.addPicture(const Offset(20.0, 20.0), - createColoredBox(green, size)); // green - flutter + builder.addPicture(const Offset(20.0, 20.0), createColoredBox(green, size)); // green - flutter - builder.addPicture(const Offset(30.0, 30.0), - createColoredBox(blue, size)); // blue - flutter + builder.addPicture(const Offset(30.0, 30.0), createColoredBox(blue, size)); // blue - flutter - builder.addPicture(const Offset(40.0, 40.0), - createColoredBox(magenta, size)); // magenta - flutter + builder.addPicture( + const Offset(40.0, 40.0), + createColoredBox(magenta, size), + ); // magenta - flutter - builder.addPicture(const Offset(50.0, 50.0), - createColoredBox(gray, size)); // gray - flutter + builder.addPicture(const Offset(50.0, 50.0), createColoredBox(gray, size)); // gray - flutter builder.pop(); @@ -755,8 +731,7 @@ Picture createGradientBox(Size size) { (6.0 / 7.0), (7.0 / 7.0), ]; - paint.shader = Gradient.linear( - Offset.zero, Offset(size.width, size.height), rainbow, stops); + paint.shader = Gradient.linear(Offset.zero, Offset(size.width, size.height), rainbow, stops); final PictureRecorder baseRecorder = PictureRecorder(); final Canvas canvas = Canvas(baseRecorder); canvas.drawRect(Rect.fromLTRB(0.0, 0.0, size.width, size.height), paint); @@ -764,8 +739,15 @@ Picture createGradientBox(Size size) { } @pragma('vm:external-name', 'EchoKeyEvent') -external void _echoKeyEvent(int change, int timestamp, int physical, - int logical, int charCode, bool synthesized, int deviceType); +external void _echoKeyEvent( + int change, + int timestamp, + int physical, + int logical, + int charCode, + bool synthesized, + int deviceType, +); // Convert `kind` in enum form to its integer form. // @@ -825,8 +807,10 @@ Future key_data_echo() async { @pragma('vm:entry-point') // ignore: non_constant_identifier_names Future key_data_late_echo() async { - channelBuffers.setListener('test/starts_echo', - (ByteData? data, PlatformMessageResponseCallback callback) { + channelBuffers.setListener('test/starts_echo', ( + ByteData? data, + PlatformMessageResponseCallback callback, + ) { PlatformDispatcher.instance.onKeyData = (KeyData data) { _echoKeyEvent( _serializeKeyEventType(data.type), @@ -896,8 +880,7 @@ void render_gradient() { builder.pushOffset(0.0, 0.0); - builder.addPicture( - Offset.zero, createGradientBox(size)); // gradient - flutter + builder.addPicture(Offset.zero, createGradientBox(size)); // gradient - flutter builder.pop(); @@ -937,13 +920,11 @@ void render_gradient_on_non_root_backing_store() { builder.pushOffset(0.0, 0.0); // Even though this is occluded, add something so it is not elided. - builder.addPicture( - const Offset(10.0, 10.0), createColoredBox(red, size)); // red - flutter + builder.addPicture(const Offset(10.0, 10.0), createColoredBox(red, size)); // red - flutter builder.addPlatformView(1, width: 100, height: 200); // undefined - platform - builder.addPicture( - Offset.zero, createGradientBox(size)); // gradient - flutter + builder.addPicture(Offset.zero, createGradientBox(size)); // gradient - flutter builder.pop(); @@ -965,13 +946,12 @@ void verify_b141980393() { final SceneBuilder builder = SceneBuilder(); builder.pushOffset( - 0.0, // x - topMargin // y - ); + 0.0, // x + topMargin, // y + ); // The web view in example. - builder.addPlatformView(1337, - width: platformViewSize.width, height: platformViewSize.height); + builder.addPlatformView(1337, width: platformViewSize.width, height: platformViewSize.height); builder.pop(); @@ -985,33 +965,34 @@ void verify_b141980393() { void can_display_platform_view_with_pixel_ratio() { PlatformDispatcher.instance.onBeginFrame = (Duration duration) { final SceneBuilder builder = SceneBuilder(); - builder.pushTransform(Float64List.fromList([ - 2.0, - 0.0, - 0.0, - 0.0, - 0.0, - 2.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0 - ])); // base - builder.addPicture( - Offset.zero, createGradientBox(const Size(400.0, 300.0))); + builder.pushTransform( + Float64List.fromList([ + 2.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + ]), + ); // base + builder.addPicture(Offset.zero, createGradientBox(const Size(400.0, 300.0))); builder.pushOffset(0.0, 20.0); // offset builder.addPlatformView(42, width: 400.0, height: 280.0); builder.pop(); // offset builder.addPicture( - Offset.zero, - createColoredBox( - const Color.fromARGB(128, 255, 0, 0), const Size(400.0, 300.0))); + Offset.zero, + createColoredBox(const Color.fromARGB(128, 255, 0, 0), const Size(400.0, 300.0)), + ); builder.pop(); // base PlatformDispatcher.instance.views.first.render(builder.build()); }; @@ -1037,15 +1018,15 @@ void verify_b143464703() { // Background builder.addPicture( - Offset.zero, - createColoredBox(const Color.fromARGB(255, 128, 128, 128), - const Size(1024.0, 600.0))); + Offset.zero, + createColoredBox(const Color.fromARGB(255, 128, 128, 128), const Size(1024.0, 600.0)), + ); builder.pushOpacity(128); builder.addPicture( - const Offset(10.0, 10.0), - createColoredBox( - const Color.fromARGB(255, 0, 0, 255), const Size(25.0, 25.0))); + const Offset(10.0, 10.0), + createColoredBox(const Color.fromARGB(255, 0, 0, 255), const Size(25.0, 25.0)), + ); builder.pop(); // opacity 128 // The top bar and the platform view are pushed to the side. @@ -1058,8 +1039,7 @@ void verify_b143464703() { builder.pop(); // 2 // Top bar - builder.addPicture( - Offset.zero, createGradientBox(const Size(1024.0, 60.0))); + builder.addPicture(Offset.zero, createGradientBox(const Size(1024.0, 60.0))); builder.pop(); // opacity builder.pop(); // 1 @@ -1077,9 +1057,9 @@ void push_frames_over_and_over() { final SceneBuilder builder = SceneBuilder(); builder.pushOffset(0.0, 0.0); builder.addPicture( - Offset.zero, - createColoredBox(const Color.fromARGB(255, 128, 128, 128), - const Size(1024.0, 600.0))); + Offset.zero, + createColoredBox(const Color.fromARGB(255, 128, 128, 128), const Size(1024.0, 600.0)), + ); builder.pushOpacity(128); builder.addPlatformView(42, width: 1024.0, height: 540.0); builder.pop(); @@ -1097,14 +1077,13 @@ void platform_view_mutators() { PlatformDispatcher.instance.onBeginFrame = (Duration duration) { final SceneBuilder builder = SceneBuilder(); builder.pushOffset(0.0, 0.0); // base - builder.addPicture( - Offset.zero, createGradientBox(const Size(800.0, 600.0))); + builder.addPicture(Offset.zero, createGradientBox(const Size(800.0, 600.0))); builder.pushOpacity(128); - builder.pushClipRect( - const Rect.fromLTWH(10.0, 10.0, 800.0 - 20.0, 600.0 - 20.0)); - builder.pushClipRRect(RRect.fromLTRBR( - 10.0, 10.0, 800.0 - 10.0, 600.0 - 10.0, const Radius.circular(14.0))); + builder.pushClipRect(const Rect.fromLTWH(10.0, 10.0, 800.0 - 20.0, 600.0 - 20.0)); + builder.pushClipRRect( + RRect.fromLTRBR(10.0, 10.0, 800.0 - 10.0, 600.0 - 10.0, const Radius.circular(14.0)), + ); builder.addPlatformView(42, width: 800.0, height: 600.0); builder.pop(); // clip rrect builder.pop(); // clip rect @@ -1122,14 +1101,13 @@ void platform_view_mutators_with_pixel_ratio() { PlatformDispatcher.instance.onBeginFrame = (Duration duration) { final SceneBuilder builder = SceneBuilder(); builder.pushOffset(0.0, 0.0); // base - builder.addPicture( - Offset.zero, createGradientBox(const Size(400.0, 300.0))); + builder.addPicture(Offset.zero, createGradientBox(const Size(400.0, 300.0))); builder.pushOpacity(128); - builder.pushClipRect( - const Rect.fromLTWH(5.0, 5.0, 400.0 - 10.0, 300.0 - 10.0)); - builder.pushClipRRect(RRect.fromLTRBR( - 5.0, 5.0, 400.0 - 5.0, 300.0 - 5.0, const Radius.circular(7.0))); + builder.pushClipRect(const Rect.fromLTWH(5.0, 5.0, 400.0 - 10.0, 300.0 - 10.0)); + builder.pushClipRRect( + RRect.fromLTRBR(5.0, 5.0, 400.0 - 5.0, 300.0 - 5.0, const Radius.circular(7.0)), + ); builder.addPlatformView(42, width: 400.0, height: 300.0); builder.pop(); // clip rrect builder.pop(); // clip rect @@ -1156,8 +1134,7 @@ void empty_scene() { void scene_with_no_container() { PlatformDispatcher.instance.onBeginFrame = (Duration duration) { final SceneBuilder builder = SceneBuilder(); - builder.addPicture( - Offset.zero, createGradientBox(const Size(400.0, 300.0))); + builder.addPicture(Offset.zero, createGradientBox(const Size(400.0, 300.0))); PlatformDispatcher.instance.views.first.render(builder.build()); signalNativeTest(); }; @@ -1168,15 +1145,15 @@ Picture createArcEndCapsPicture() { final PictureRecorder baseRecorder = PictureRecorder(); final Canvas canvas = Canvas(baseRecorder); - final style = Paint() - ..strokeWidth = 12.0 - ..style = PaintingStyle.stroke - ..strokeCap = StrokeCap.round - ..strokeJoin = StrokeJoin.miter; + final style = + Paint() + ..strokeWidth = 12.0 + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round + ..strokeJoin = StrokeJoin.miter; style.color = const Color.fromARGB(255, 255, 0, 0); - canvas.drawArc( - const Rect.fromLTRB(0.0, 0.0, 500.0, 500.0), 1.57, 1.0, false, style); + canvas.drawArc(const Rect.fromLTRB(0.0, 0.0, 500.0, 500.0), 1.57, 1.0, false, style); return baseRecorder.endRecording(); } @@ -1199,8 +1176,7 @@ void scene_builder_with_clips() { final SceneBuilder builder = SceneBuilder(); builder.pushClipRect(const Rect.fromLTRB(10.0, 10.0, 390.0, 290.0)); builder.addPlatformView(42, width: 400.0, height: 300.0); - builder.addPicture( - Offset.zero, createGradientBox(const Size(400.0, 300.0))); + builder.addPicture(Offset.zero, createGradientBox(const Size(400.0, 300.0))); PlatformDispatcher.instance.views.first.render(builder.build()); }; PlatformDispatcher.instance.scheduleFrame(); @@ -1219,8 +1195,7 @@ void scene_builder_with_complex_clips() { builder.pushClipRect(const Rect.fromLTRB(0.0, 0.0, 1024.0, 600.0)); builder.addPlatformView(42, width: 1024.0, height: 600.0); - builder.addPicture( - Offset.zero, createGradientBox(const Size(1024.0, 600.0))); + builder.addPicture(Offset.zero, createGradientBox(const Size(1024.0, 600.0))); PlatformDispatcher.instance.views.first.render(builder.build()); }; PlatformDispatcher.instance.scheduleFrame(); @@ -1270,8 +1245,7 @@ void render_targets_are_recycled() { PlatformDispatcher.instance.onBeginFrame = (Duration duration) { final SceneBuilder builder = SceneBuilder(); for (int i = 0; i < 10; i++) { - builder.addPicture( - Offset.zero, createGradientBox(const Size(30.0, 20.0))); + builder.addPicture(Offset.zero, createGradientBox(const Size(30.0, 20.0))); builder.addPlatformView(42 + i, width: 30.0, height: 20.0); } PlatformDispatcher.instance.views.first.render(builder.build()); @@ -1292,8 +1266,7 @@ void render_targets_are_in_stable_order() { PlatformDispatcher.instance.onBeginFrame = (Duration duration) { final SceneBuilder builder = SceneBuilder(); for (int i = 0; i < 10; i++) { - builder.addPicture( - Offset.zero, createGradientBox(const Size(30.0, 20.0))); + builder.addPicture(Offset.zero, createGradientBox(const Size(30.0, 20.0))); builder.addPlatformView(42 + i, width: 30.0, height: 20.0); } PlatformDispatcher.instance.views.first.render(builder.build()); @@ -1350,18 +1323,12 @@ Future snapshot_large_scene(int maxSize) async { const smallHeight = 64.0; recorder = PictureRecorder(); { - final Canvas canvas = Canvas( - recorder, - const Rect.fromLTWH(0, 0, smallWidth, smallHeight), - ); + final Canvas canvas = Canvas(recorder, const Rect.fromLTWH(0, 0, smallWidth, smallHeight)); canvas.scale(smallWidth / bigImage.width); canvas.drawImage(bigImage, Offset.zero, Paint()); } picture = recorder.endRecording(); - final Image smallImage = await picture.toImage( - smallWidth.toInt(), - smallHeight.toInt(), - ); + final Image smallImage = await picture.toImage(smallWidth.toInt(), smallHeight.toInt()); snapshotsCallback(bigImage, smallImage); } @@ -1374,8 +1341,7 @@ void invalid_backingstore() { const Size size = Size(50.0, 150.0); final SceneBuilder builder = SceneBuilder(); builder.pushOffset(0.0, 0.0); - builder.addPicture( - const Offset(10.0, 10.0), createColoredBox(red, size)); // red - flutter + builder.addPicture(const Offset(10.0, 10.0), createColoredBox(red, size)); // red - flutter builder.pop(); PlatformDispatcher.instance.views.first.render(builder.build()); }; @@ -1412,9 +1378,9 @@ void drawSolidColor(Color c) { final SceneBuilder builder = SceneBuilder(); builder.pushOffset(0.0, 0.0); builder.addPicture( - Offset.zero, - createColoredBox( - c, PlatformDispatcher.instance.views.first.physicalSize)); + Offset.zero, + createColoredBox(c, PlatformDispatcher.instance.views.first.physicalSize), + ); builder.pop(); PlatformDispatcher.instance.views.first.render(builder.build()); }; @@ -1508,8 +1474,7 @@ void window_metrics_event_view_id() { // ignore: non_constant_identifier_names void window_metrics_event_all_view_ids() { PlatformDispatcher.instance.onMetricsChanged = () { - final List viewIds = - PlatformDispatcher.instance.views.map((view) => view.viewId).toList(); + final List viewIds = PlatformDispatcher.instance.views.map((view) => view.viewId).toList(); viewIds.sort(); @@ -1522,8 +1487,10 @@ void window_metrics_event_all_view_ids() { @pragma('vm:entry-point') // ignore: non_constant_identifier_names Future channel_listener_response() async { - channelBuffers.setListener('test/listen', - (ByteData? data, PlatformMessageResponseCallback callback) { + channelBuffers.setListener('test/listen', ( + ByteData? data, + PlatformMessageResponseCallback callback, + ) { callback(null); }); signalNativeTest(); @@ -1581,12 +1548,11 @@ void render_impeller_text_test() { final PictureRecorder baseRecorder = PictureRecorder(); final Canvas canvas = Canvas(baseRecorder); - final ParagraphBuilder paragraphBuilder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'sans-serif' - )) - ..addText('Flutter is the best!'); - final Paragraph paragraph = paragraphBuilder.build() - ..layout(const ParagraphConstraints(width: 400)); + final ParagraphBuilder paragraphBuilder = ParagraphBuilder( + ParagraphStyle(fontFamily: 'sans-serif'), + )..addText('Flutter is the best!'); + final Paragraph paragraph = + paragraphBuilder.build()..layout(const ParagraphConstraints(width: 400)); canvas.drawParagraph(paragraph, const Offset(20, 20)); builder.addPicture(Offset.zero, baseRecorder.endRecording()); diff --git a/shell/platform/fuchsia/dart-pkg/fuchsia/lib/fuchsia.dart b/shell/platform/fuchsia/dart-pkg/fuchsia/lib/fuchsia.dart index cfd2f2e7e071e..78387f906795c 100644 --- a/shell/platform/fuchsia/dart-pkg/fuchsia/lib/fuchsia.dart +++ b/shell/platform/fuchsia/dart-pkg/fuchsia/lib/fuchsia.dart @@ -26,8 +26,7 @@ class MxStartupInfo { // https://github.com/flutter/flutter/issues/49439 static Handle takeEnvironment() { if (_environment == null && Platform.isFuchsia) { - throw Exception( - 'Attempting to call takeEnvironment more than once per process'); + throw Exception('Attempting to call takeEnvironment more than once per process'); } final handle = _environment; _environment = null; @@ -38,8 +37,7 @@ class MxStartupInfo { // https://github.com/flutter/flutter/issues/49439 static Handle takeOutgoingServices() { if (_outgoingServices == null && Platform.isFuchsia) { - throw Exception( - 'Attempting to call takeOutgoingServices more than once per process'); + throw Exception('Attempting to call takeOutgoingServices more than once per process'); } final handle = _outgoingServices; _outgoingServices = null; @@ -50,8 +48,7 @@ class MxStartupInfo { // https://github.com/flutter/flutter/issues/49439 static Handle takeViewRef() { if (_viewRef == null && Platform.isFuchsia) { - throw Exception( - 'Attempting to call takeViewRef more than once per process'); + throw Exception('Attempting to call takeViewRef more than once per process'); } final handle = _viewRef; _viewRef = null; @@ -79,8 +76,7 @@ typedef _ListStringArgFunction(List args); // The code does not catch any exceptions since this is handled in the dart // runner calling code. @pragma('vm:entry-point') -void _runUserMainForDartRunner(Function userMainFunction, - List args) { +void _runUserMainForDartRunner(Function userMainFunction, List args) { if (userMainFunction is _ListStringArgFunction) { (userMainFunction as dynamic)(args); } else { diff --git a/shell/platform/fuchsia/dart-pkg/zircon/lib/src/handle.dart b/shell/platform/fuchsia/dart-pkg/zircon/lib/src/handle.dart index 81a6300f25ddd..eea972780fbbf 100644 --- a/shell/platform/fuchsia/dart-pkg/zircon/lib/src/handle.dart +++ b/shell/platform/fuchsia/dart-pkg/zircon/lib/src/handle.dart @@ -31,8 +31,7 @@ base class Handle extends NativeFieldWrapperClass1 { @override bool operator ==(Object other) { - return other is Handle - && other.handle == handle; + return other is Handle && other.handle == handle; } @override diff --git a/shell/platform/fuchsia/dart-pkg/zircon/lib/src/system.dart b/shell/platform/fuchsia/dart-pkg/zircon/lib/src/system.dart index ccf1b693608ce..c7e4b90f12961 100644 --- a/shell/platform/fuchsia/dart-pkg/zircon/lib/src/system.dart +++ b/shell/platform/fuchsia/dart-pkg/zircon/lib/src/system.dart @@ -62,11 +62,9 @@ class HandlePairResult extends _Result { Handle get second => _second!; @pragma('vm:entry-point') - const HandlePairResult(final int status, [this._first, this._second]) - : super(status); + const HandlePairResult(final int status, [this._first, this._second]) : super(status); @override - String toString() => - 'HandlePairResult(status=$status, first=$_first, second=$_second)'; + String toString() => 'HandlePairResult(status=$status, first=$_first, second=$_second)'; } @pragma('vm:entry-point') @@ -80,8 +78,7 @@ class ReadResult extends _Result { List get handles => _handles!; @pragma('vm:entry-point') - const ReadResult(final int status, [this._bytes, this._numBytes, this._handles]) - : super(status); + const ReadResult(final int status, [this._bytes, this._numBytes, this._handles]) : super(status); /// Returns the bytes as a Uint8List. If status != OK this will throw /// an exception. @@ -109,8 +106,7 @@ class HandleInfo { const HandleInfo(this.handle, this.type, this.rights); @override - String toString() => - 'HandleInfo(handle=$handle, type=$type, rights=$rights)'; + String toString() => 'HandleInfo(handle=$handle, type=$type, rights=$rights)'; } @pragma('vm:entry-point') @@ -125,7 +121,7 @@ class ReadEtcResult extends _Result { @pragma('vm:entry-point') const ReadEtcResult(final int status, [this._bytes, this._numBytes, this._handleInfos]) - : super(status); + : super(status); /// Returns the bytes as a Uint8List. If status != OK this will throw /// an exception. @@ -174,11 +170,9 @@ class FromFileResult extends _Result { int get numBytes => _numBytes!; @pragma('vm:entry-point') - const FromFileResult(final int status, [this._handle, this._numBytes]) - : super(status); + const FromFileResult(final int status, [this._handle, this._numBytes]) : super(status); @override - String toString() => - 'FromFileResult(status=$status, handle=$_handle, numBytes=$_numBytes)'; + String toString() => 'FromFileResult(status=$status, handle=$_handle, numBytes=$_numBytes)'; } @pragma('vm:entry-point') @@ -207,7 +201,11 @@ base class System extends NativeFieldWrapperClass1 { @pragma('vm:external-name', 'System_ChannelWrite') external static int channelWrite(Handle channel, ByteData data, List handles); @pragma('vm:external-name', 'System_ChannelWriteEtc') - external static int channelWriteEtc(Handle channel, ByteData data, List handleDispositions); + external static int channelWriteEtc( + Handle channel, + ByteData data, + List handleDispositions, + ); @pragma('vm:external-name', 'System_ChannelQueryAndRead') external static ReadResult channelQueryAndRead(Handle channel); @pragma('vm:external-name', 'System_ChannelQueryAndReadEtc') @@ -254,5 +252,7 @@ base class System extends NativeFieldWrapperClass1 { external static int _nativeClockGetMonotonic(); // TODO(edcoyne): Remove this, it is required to safely do an API transition across repos. - static int reboot() { return -2; /*ZX_ERR_NOT_SUPPORTED*/ } + static int reboot() { + return -2; /*ZX_ERR_NOT_SUPPORTED*/ + } } diff --git a/shell/platform/fuchsia/dart-pkg/zircon/lib/src/zd_channel.dart b/shell/platform/fuchsia/dart-pkg/zircon/lib/src/zd_channel.dart index 4551e13f3a546..884eb6e223c6b 100644 --- a/shell/platform/fuchsia/dart-pkg/zircon/lib/src/zd_channel.dart +++ b/shell/platform/fuchsia/dart-pkg/zircon/lib/src/zd_channel.dart @@ -7,8 +7,8 @@ part of zircon; @pragma('vm:entry-point') class ZDChannel { static ZDChannel? create([int options = 0]) { - final Pointer? channelPtr = - zirconFFIBindings?.zircon_dart_channel_create(options); + final Pointer? channelPtr = zirconFFIBindings + ?.zircon_dart_channel_create(options); if (channelPtr == null || channelPtr.address == 0) { throw Exception('Unable to create a channel'); } @@ -19,19 +19,16 @@ class ZDChannel { final Pointer handleList = zirconFFIBindings!.zircon_dart_handle_list_create(); handles.forEach((ZDHandle handle) { - zirconFFIBindings! - .zircon_dart_handle_list_append(handleList, handle._ptr); + zirconFFIBindings!.zircon_dart_handle_list_append(handleList, handle._ptr); }); final Uint8List dataAsBytes = data.buffer.asUint8List(); - final Pointer byteArray = - zirconFFIBindings!.zircon_dart_byte_array_create(dataAsBytes.length); + final Pointer byteArray = zirconFFIBindings! + .zircon_dart_byte_array_create(dataAsBytes.length); for (int i = 0; i < dataAsBytes.length; i++) { - zirconFFIBindings!.zircon_dart_byte_array_set_value( - byteArray, i, dataAsBytes.elementAt(i)); + zirconFFIBindings!.zircon_dart_byte_array_set_value(byteArray, i, dataAsBytes.elementAt(i)); } - int ret = zirconFFIBindings! - .zircon_dart_channel_write(channel._ptr, byteArray, handleList); + int ret = zirconFFIBindings!.zircon_dart_channel_write(channel._ptr, byteArray, handleList); zirconFFIBindings!.zircon_dart_byte_array_free(byteArray); zirconFFIBindings!.zircon_dart_handle_list_free(handleList); diff --git a/shell/platform/fuchsia/dart-pkg/zircon/lib/src/zd_handle.dart b/shell/platform/fuchsia/dart-pkg/zircon/lib/src/zd_handle.dart index 2090d78599535..24ae20c6d7c26 100644 --- a/shell/platform/fuchsia/dart-pkg/zircon/lib/src/zd_handle.dart +++ b/shell/platform/fuchsia/dart-pkg/zircon/lib/src/zd_handle.dart @@ -13,8 +13,7 @@ class ZDHandle { void _attachFinalizer() { // TODO (kaushikiska): fix external allocation size. - final int? ret = zirconFFIBindings?.zircon_dart_handle_attach_finalizer( - this, _ptr.cast(), 128); + final int? ret = zirconFFIBindings?.zircon_dart_handle_attach_finalizer(this, _ptr.cast(), 128); if (ret != 1) { throw Exception('Unable to attach finalizer to handle'); } @@ -53,16 +52,17 @@ class ZDHandle { @pragma('vm:entry-point') class ZDHandlePair { @pragma('vm:entry-point') - ZDHandlePair._(this._ptr) - : left = ZDHandle._(_ptr.ref.left), - right = ZDHandle._(_ptr.ref.right) { + ZDHandlePair._(this._ptr) : left = ZDHandle._(_ptr.ref.left), right = ZDHandle._(_ptr.ref.right) { _attachFinalizer(); } void _attachFinalizer() { // TODO (kaushikiska): fix external allocation size. - final int? ret = zirconFFIBindings - ?.zircon_dart_handle_pair_attach_finalizer(this, _ptr.cast(), 128); + final int? ret = zirconFFIBindings?.zircon_dart_handle_pair_attach_finalizer( + this, + _ptr.cast(), + 128, + ); if (ret != 1) { throw Exception('Unable to attach finalizer to handle'); } diff --git a/shell/platform/fuchsia/dart-pkg/zircon/test/zircon_tests.dart b/shell/platform/fuchsia/dart-pkg/zircon/test/zircon_tests.dart index 183295251c9b6..7b2d5befa50b5 100644 --- a/shell/platform/fuchsia/dart-pkg/zircon/test/zircon_tests.dart +++ b/shell/platform/fuchsia/dart-pkg/zircon/test/zircon_tests.dart @@ -42,13 +42,11 @@ abstract class ZX { static const int RIGHT_WAIT = 1 << 14; static const int RIGHT_INSPECT = 1 << 15; static const int RIGHT_SAME_RIGHTS = 1 << 31; - static const int RIGHTS_BASIC = RIGHT_TRANSFER | RIGHT_DUPLICATE | - RIGHT_WAIT | RIGHT_INSPECT; + static const int RIGHTS_BASIC = RIGHT_TRANSFER | RIGHT_DUPLICATE | RIGHT_WAIT | RIGHT_INSPECT; static const int RIGHTS_IO = RIGHT_READ | RIGHT_WRITE; static const int RIGHTS_PROPERTY = RIGHT_GET_PROPERTY | RIGHT_SET_PROPERTY; - static const int DEFAULT_VMO_RIGHTS = RIGHTS_BASIC | RIGHTS_IO | - RIGHTS_PROPERTY | RIGHT_MAP | - RIGHT_SIGNAL; + static const int DEFAULT_VMO_RIGHTS = + RIGHTS_BASIC | RIGHTS_IO | RIGHTS_PROPERTY | RIGHT_MAP | RIGHT_SIGNAL; static const int OBJ_TYPE_VMO = 3; static const int OBJ_TYPE_CHANNEL = 4; static const int HANDLE_OP_MOVE = 0; @@ -172,8 +170,7 @@ void main() { expect(vmo.status, equals(ZX.OK)); // Replace the first handle. - final Handle duplicate = - vmo.handle.replace(ZX.RIGHTS_BASIC | ZX.RIGHT_READ); + final Handle duplicate = vmo.handle.replace(ZX.RIGHTS_BASIC | ZX.RIGHT_READ); expect(duplicate.isValid, isTrue); // Write bytes to the original handle. @@ -230,18 +227,15 @@ void main() { expect(pair.first.close(), equals(0)); expect(pair.first.isValid, isFalse); expect(pair.second.isValid, isTrue); - expect(System.channelWrite(pair.first, ByteData(1), []), - equals(ZX.ERR_BAD_HANDLE)); - expect(System.channelWrite(pair.second, ByteData(1), []), - equals(ZX.ERR_PEER_CLOSED)); + expect(System.channelWrite(pair.first, ByteData(1), []), equals(ZX.ERR_BAD_HANDLE)); + expect(System.channelWrite(pair.second, ByteData(1), []), equals(ZX.ERR_PEER_CLOSED)); }); test('channel bytes', () { final HandlePairResult pair = System.channelCreate(); // When no data is available, ZX.ERR_SHOULD_WAIT is returned. - expect(System.channelQueryAndRead(pair.second).status, - equals(ZX.ERR_SHOULD_WAIT)); + expect(System.channelQueryAndRead(pair.second).status, equals(ZX.ERR_SHOULD_WAIT)); // Write bytes. final ByteData data = utf8Bytes('Hello, world'); @@ -261,8 +255,7 @@ void main() { final HandlePairResult pair = System.channelCreate(); final ByteData data = utf8Bytes(''); final HandlePairResult eventPair = System.eventpairCreate(); - final int status = - System.channelWrite(pair.first, data, [eventPair.first]); + final int status = System.channelWrite(pair.first, data, [eventPair.first]); expect(status, equals(ZX.OK)); expect(eventPair.first.isValid, isFalse); @@ -288,8 +281,7 @@ void main() { final List result = await completer.future; expect(result[0], equals(ZX.OK)); // status - expect(result[1] & ZX.CHANNEL_READABLE, - equals(ZX.CHANNEL_READABLE)); // pending + expect(result[1] & ZX.CHANNEL_READABLE, equals(ZX.CHANNEL_READABLE)); // pending }); test('async wait channel closed', () async { @@ -314,16 +306,18 @@ void main() { final ByteData data = utf8Bytes(''); final HandlePairResult transferred = System.channelCreate(); - final HandleDisposition disposition = HandleDisposition(ZX.HANDLE_OP_MOVE, - transferred.first, ZX.OBJ_TYPE_CHANNEL, ZX.RIGHTS_IO); - final int status = System.channelWriteEtc( - pair.first, data, [disposition]); + final HandleDisposition disposition = HandleDisposition( + ZX.HANDLE_OP_MOVE, + transferred.first, + ZX.OBJ_TYPE_CHANNEL, + ZX.RIGHTS_IO, + ); + final int status = System.channelWriteEtc(pair.first, data, [disposition]); expect(status, equals(ZX.OK)); expect(disposition.result, equals(ZX.OK)); expect(transferred.first.isValid, isFalse); - final ReadEtcResult readResult = - System.channelQueryAndReadEtc(pair.second); + final ReadEtcResult readResult = System.channelQueryAndReadEtc(pair.second); expect(readResult.status, equals(ZX.OK)); expect(readResult.numBytes, equals(0)); expect(readResult.bytes.lengthInBytes, equals(0)); @@ -341,18 +335,17 @@ void main() { final HandleResult vmo = System.vmoCreate(0); final HandleDisposition disposition = HandleDisposition( - ZX.HANDLE_OP_DUPLICATE, - vmo.handle, - ZX.OBJ_TYPE_VMO, - ZX.RIGHT_SAME_RIGHTS); - final int status = System.channelWriteEtc( - pair.first, data, [disposition]); + ZX.HANDLE_OP_DUPLICATE, + vmo.handle, + ZX.OBJ_TYPE_VMO, + ZX.RIGHT_SAME_RIGHTS, + ); + final int status = System.channelWriteEtc(pair.first, data, [disposition]); expect(status, equals(ZX.OK)); expect(disposition.result, equals(ZX.OK)); expect(vmo.handle.isValid, isTrue); - final ReadEtcResult readResult = - System.channelQueryAndReadEtc(pair.second); + final ReadEtcResult readResult = System.channelQueryAndReadEtc(pair.second); expect(readResult.status, equals(ZX.OK)); expect(readResult.numBytes, equals(0)); expect(readResult.bytes.lengthInBytes, equals(0)); @@ -369,17 +362,19 @@ void main() { final ByteData data = utf8Bytes(''); final HandlePairResult closed = System.channelCreate(); - final HandleDisposition disposition = HandleDisposition(ZX.HANDLE_OP_MOVE, - closed.first, ZX.OBJ_TYPE_CHANNEL, ZX.RIGHT_SAME_RIGHTS); + final HandleDisposition disposition = HandleDisposition( + ZX.HANDLE_OP_MOVE, + closed.first, + ZX.OBJ_TYPE_CHANNEL, + ZX.RIGHT_SAME_RIGHTS, + ); closed.first.close(); - final int status = System.channelWriteEtc( - pair.first, data, [disposition]); + final int status = System.channelWriteEtc(pair.first, data, [disposition]); expect(status, equals(ZX.ERR_BAD_HANDLE)); expect(disposition.result, equals(ZX.ERR_BAD_HANDLE)); expect(closed.first.isValid, isFalse); - final ReadEtcResult readResult = - System.channelQueryAndReadEtc(pair.second); + final ReadEtcResult readResult = System.channelQueryAndReadEtc(pair.second); expect(readResult.status, equals(ZX.ERR_SHOULD_WAIT)); }); @@ -390,10 +385,13 @@ void main() { final HandleResult vmo = System.vmoCreate(0); final List dispositions = [ - HandleDisposition(ZX.HANDLE_OP_MOVE, transferred.first, - ZX.OBJ_TYPE_CHANNEL, ZX.RIGHTS_IO), - HandleDisposition(ZX.HANDLE_OP_DUPLICATE, vmo.handle, ZX.OBJ_TYPE_VMO, - ZX.RIGHT_SAME_RIGHTS) + HandleDisposition(ZX.HANDLE_OP_MOVE, transferred.first, ZX.OBJ_TYPE_CHANNEL, ZX.RIGHTS_IO), + HandleDisposition( + ZX.HANDLE_OP_DUPLICATE, + vmo.handle, + ZX.OBJ_TYPE_VMO, + ZX.RIGHT_SAME_RIGHTS, + ), ]; final int status = System.channelWriteEtc(pair.first, data, dispositions); expect(status, equals(ZX.OK)); @@ -402,8 +400,7 @@ void main() { expect(transferred.first.isValid, isFalse); expect(vmo.handle.isValid, isTrue); - final ReadEtcResult readResult = - System.channelQueryAndReadEtc(pair.second); + final ReadEtcResult readResult = System.channelQueryAndReadEtc(pair.second); expect(readResult.status, equals(ZX.OK)); expect(readResult.numBytes, equals(0)); expect(readResult.bytes.lengthInBytes, equals(0)); @@ -479,11 +476,9 @@ void main() { expect(pair.first.close(), equals(0)); expect(pair.first.isValid, isFalse); expect(pair.second.isValid, isTrue); - final WriteResult firstResult = - System.socketWrite(pair.first, ByteData(1), 0); + final WriteResult firstResult = System.socketWrite(pair.first, ByteData(1), 0); expect(firstResult.status, equals(ZX.ERR_BAD_HANDLE)); - final WriteResult secondResult = - System.socketWrite(pair.second, ByteData(1), 0); + final WriteResult secondResult = System.socketWrite(pair.second, ByteData(1), 0); expect(secondResult.status, equals(ZX.ERR_PEER_CLOSED)); }); @@ -491,15 +486,13 @@ void main() { final HandlePairResult pair = System.socketCreate(); // When no data is available, ZX.ERR_SHOULD_WAIT is returned. - expect( - System.socketRead(pair.second, 1).status, equals(ZX.ERR_SHOULD_WAIT)); + expect(System.socketRead(pair.second, 1).status, equals(ZX.ERR_SHOULD_WAIT)); final ByteData data = utf8Bytes('Hello, world'); final WriteResult writeResult = System.socketWrite(pair.first, data, 0); expect(writeResult.status, equals(ZX.OK)); - final ReadResult readResult = - System.socketRead(pair.second, data.lengthInBytes); + final ReadResult readResult = System.socketRead(pair.second, data.lengthInBytes); expect(readResult.status, equals(ZX.OK)); expect(readResult.numBytes, equals(data.lengthInBytes)); expect(readResult.bytes.lengthInBytes, equals(data.lengthInBytes)); @@ -513,16 +506,14 @@ void main() { expect(writeResult.status, equals(ZX.OK)); const int shortLength = 'Hello'.length; - final ReadResult shortReadResult = - System.socketRead(pair.second, shortLength); + final ReadResult shortReadResult = System.socketRead(pair.second, shortLength); expect(shortReadResult.status, equals(ZX.OK)); expect(shortReadResult.numBytes, equals(shortLength)); expect(shortReadResult.bytes.lengthInBytes, equals(shortLength)); expect(shortReadResult.bytesAsUTF8String(), equals('Hello')); final int longLength = data.lengthInBytes * 2; - final ReadResult longReadResult = - System.socketRead(pair.second, longLength); + final ReadResult longReadResult = System.socketRead(pair.second, longLength); expect(longReadResult.status, equals(ZX.OK)); expect(longReadResult.numBytes, equals(data.lengthInBytes - shortLength)); expect(longReadResult.bytes.lengthInBytes, equals(longLength)); @@ -531,11 +522,9 @@ void main() { test('partial write socket', () { final HandlePairResult pair = System.socketCreate(); - final WriteResult writeResult1 = - System.socketWrite(pair.first, utf8Bytes('Hello, '), 0); + final WriteResult writeResult1 = System.socketWrite(pair.first, utf8Bytes('Hello, '), 0); expect(writeResult1.status, equals(ZX.OK)); - final WriteResult writeResult2 = - System.socketWrite(pair.first, utf8Bytes('world'), 0); + final WriteResult writeResult2 = System.socketWrite(pair.first, utf8Bytes('world'), 0); expect(writeResult2.status, equals(ZX.OK)); final ReadResult readResult = System.socketRead(pair.second, 100); @@ -579,9 +568,10 @@ void main() { group('vmo', () { test('fromFile', () { const String fuchsia = 'Fuchsia'; - File f = File('tmp/testdata') - ..createSync() - ..writeAsStringSync(fuchsia); + File f = + File('tmp/testdata') + ..createSync() + ..writeAsStringSync(fuchsia); String readFuchsia = f.readAsStringSync(); expect(readFuchsia, equals(fuchsia)); @@ -599,12 +589,11 @@ void main() { Uint8List data = Uint8List.fromList(fuchsia.codeUnits); HandleResult createResult = System.vmoCreate(data.length); expect(createResult.status, equals(ZX.OK)); - int writeResult = System.vmoWrite(createResult.handle, 0, - data.buffer.asByteData()); + int writeResult = System.vmoWrite(createResult.handle, 0, data.buffer.asByteData()); expect(writeResult, equals(ZX.OK)); - Handle duplicate = createResult.handle.duplicate(ZX.RIGHTS_BASIC | - ZX.RIGHT_READ | - ZX.RIGHT_MAP); + Handle duplicate = createResult.handle.duplicate( + ZX.RIGHTS_BASIC | ZX.RIGHT_READ | ZX.RIGHT_MAP, + ); expect(duplicate.isValid, isTrue); // Read from the duplicate. diff --git a/shell/platform/fuchsia/dart-pkg/zircon_ffi/lib/zircon_ffi.dart b/shell/platform/fuchsia/dart-pkg/zircon_ffi/lib/zircon_ffi.dart index ad265ff8d0220..cc4c86ade2629 100644 --- a/shell/platform/fuchsia/dart-pkg/zircon_ffi/lib/zircon_ffi.dart +++ b/shell/platform/fuchsia/dart-pkg/zircon_ffi/lib/zircon_ffi.dart @@ -6,67 +6,50 @@ import 'dart:ffi' as ffi; /// Bindings for `dart:zircon_ffi`. class ZirconFFIBindings { /// Holds the symbol lookup function. - final ffi.Pointer Function(String symbolName) - _lookup; + final ffi.Pointer Function(String symbolName) _lookup; /// The symbols are looked up in [dynamicLibrary]. - ZirconFFIBindings(ffi.DynamicLibrary dynamicLibrary) - : _lookup = dynamicLibrary.lookup; + ZirconFFIBindings(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup; /// The symbols are looked up with [lookup]. ZirconFFIBindings.fromLookup( - ffi.Pointer Function(String symbolName) - lookup) - : _lookup = lookup; + ffi.Pointer Function(String symbolName) lookup, + ) : _lookup = lookup; - ffi.Pointer zircon_dart_byte_array_create( - int size, - ) { - return _zircon_dart_byte_array_create( - size, - ); + ffi.Pointer zircon_dart_byte_array_create(int size) { + return _zircon_dart_byte_array_create(size); } late final _zircon_dart_byte_array_create_ptr = _lookup>( - 'zircon_dart_byte_array_create'); - late final _dart_zircon_dart_byte_array_create - _zircon_dart_byte_array_create = _zircon_dart_byte_array_create_ptr - .asFunction<_dart_zircon_dart_byte_array_create>(); + 'zircon_dart_byte_array_create', + ); + late final _dart_zircon_dart_byte_array_create _zircon_dart_byte_array_create = + _zircon_dart_byte_array_create_ptr.asFunction<_dart_zircon_dart_byte_array_create>(); void zircon_dart_byte_array_set_value( ffi.Pointer arr, int index, int value, ) { - return _zircon_dart_byte_array_set_value( - arr, - index, - value, - ); + return _zircon_dart_byte_array_set_value(arr, index, value); } late final _zircon_dart_byte_array_set_value_ptr = _lookup>( - 'zircon_dart_byte_array_set_value'); - late final _dart_zircon_dart_byte_array_set_value - _zircon_dart_byte_array_set_value = _zircon_dart_byte_array_set_value_ptr - .asFunction<_dart_zircon_dart_byte_array_set_value>(); + 'zircon_dart_byte_array_set_value', + ); + late final _dart_zircon_dart_byte_array_set_value _zircon_dart_byte_array_set_value = + _zircon_dart_byte_array_set_value_ptr.asFunction<_dart_zircon_dart_byte_array_set_value>(); - void zircon_dart_byte_array_free( - ffi.Pointer arr, - ) { - return _zircon_dart_byte_array_free( - arr, - ); + void zircon_dart_byte_array_free(ffi.Pointer arr) { + return _zircon_dart_byte_array_free(arr); } late final _zircon_dart_byte_array_free_ptr = - _lookup>( - 'zircon_dart_byte_array_free'); + _lookup>('zircon_dart_byte_array_free'); late final _dart_zircon_dart_byte_array_free _zircon_dart_byte_array_free = - _zircon_dart_byte_array_free_ptr - .asFunction<_dart_zircon_dart_byte_array_free>(); + _zircon_dart_byte_array_free_ptr.asFunction<_dart_zircon_dart_byte_array_free>(); ffi.Pointer zircon_dart_handle_list_create() { return _zircon_dart_handle_list_create(); @@ -74,84 +57,59 @@ class ZirconFFIBindings { late final _zircon_dart_handle_list_create_ptr = _lookup>( - 'zircon_dart_handle_list_create'); - late final _dart_zircon_dart_handle_list_create - _zircon_dart_handle_list_create = _zircon_dart_handle_list_create_ptr - .asFunction<_dart_zircon_dart_handle_list_create>(); + 'zircon_dart_handle_list_create', + ); + late final _dart_zircon_dart_handle_list_create _zircon_dart_handle_list_create = + _zircon_dart_handle_list_create_ptr.asFunction<_dart_zircon_dart_handle_list_create>(); void zircon_dart_handle_list_append( ffi.Pointer list, ffi.Pointer handle, ) { - return _zircon_dart_handle_list_append( - list, - handle, - ); + return _zircon_dart_handle_list_append(list, handle); } late final _zircon_dart_handle_list_append_ptr = _lookup>( - 'zircon_dart_handle_list_append'); - late final _dart_zircon_dart_handle_list_append - _zircon_dart_handle_list_append = _zircon_dart_handle_list_append_ptr - .asFunction<_dart_zircon_dart_handle_list_append>(); + 'zircon_dart_handle_list_append', + ); + late final _dart_zircon_dart_handle_list_append _zircon_dart_handle_list_append = + _zircon_dart_handle_list_append_ptr.asFunction<_dart_zircon_dart_handle_list_append>(); - void zircon_dart_handle_list_free( - ffi.Pointer list, - ) { - return _zircon_dart_handle_list_free( - list, - ); + void zircon_dart_handle_list_free(ffi.Pointer list) { + return _zircon_dart_handle_list_free(list); } late final _zircon_dart_handle_list_free_ptr = - _lookup>( - 'zircon_dart_handle_list_free'); + _lookup>('zircon_dart_handle_list_free'); late final _dart_zircon_dart_handle_list_free _zircon_dart_handle_list_free = - _zircon_dart_handle_list_free_ptr - .asFunction<_dart_zircon_dart_handle_list_free>(); + _zircon_dart_handle_list_free_ptr.asFunction<_dart_zircon_dart_handle_list_free>(); - int zircon_dart_handle_is_valid( - ffi.Pointer handle, - ) { - return _zircon_dart_handle_is_valid( - handle, - ); + int zircon_dart_handle_is_valid(ffi.Pointer handle) { + return _zircon_dart_handle_is_valid(handle); } late final _zircon_dart_handle_is_valid_ptr = - _lookup>( - 'zircon_dart_handle_is_valid'); + _lookup>('zircon_dart_handle_is_valid'); late final _dart_zircon_dart_handle_is_valid _zircon_dart_handle_is_valid = - _zircon_dart_handle_is_valid_ptr - .asFunction<_dart_zircon_dart_handle_is_valid>(); + _zircon_dart_handle_is_valid_ptr.asFunction<_dart_zircon_dart_handle_is_valid>(); - int zircon_dart_handle_close( - ffi.Pointer handle, - ) { - return _zircon_dart_handle_close( - handle, - ); + int zircon_dart_handle_close(ffi.Pointer handle) { + return _zircon_dart_handle_close(handle); } late final _zircon_dart_handle_close_ptr = - _lookup>( - 'zircon_dart_handle_close'); + _lookup>('zircon_dart_handle_close'); late final _dart_zircon_dart_handle_close _zircon_dart_handle_close = - _zircon_dart_handle_close_ptr - .asFunction<_dart_zircon_dart_handle_close>(); + _zircon_dart_handle_close_ptr.asFunction<_dart_zircon_dart_handle_close>(); - void zircon_dart_handle_free( - ffi.Pointer handle, - ) { - return _zircon_dart_handle_free( - handle, - ); + void zircon_dart_handle_free(ffi.Pointer handle) { + return _zircon_dart_handle_free(handle); } - late final _zircon_dart_handle_free_ptr = - _lookup>( - 'zircon_dart_handle_free'); + late final _zircon_dart_handle_free_ptr = _lookup>( + 'zircon_dart_handle_free', + ); late final _dart_zircon_dart_handle_free _zircon_dart_handle_free = _zircon_dart_handle_free_ptr.asFunction<_dart_zircon_dart_handle_free>(); @@ -160,18 +118,15 @@ class ZirconFFIBindings { ffi.Pointer pointer, int external_allocation_size, ) { - return _zircon_dart_handle_pair_attach_finalizer( - object, - pointer, - external_allocation_size, - ); + return _zircon_dart_handle_pair_attach_finalizer(object, pointer, external_allocation_size); } late final _zircon_dart_handle_pair_attach_finalizer_ptr = _lookup>( - 'zircon_dart_handle_pair_attach_finalizer'); + 'zircon_dart_handle_pair_attach_finalizer', + ); late final _dart_zircon_dart_handle_pair_attach_finalizer - _zircon_dart_handle_pair_attach_finalizer = + _zircon_dart_handle_pair_attach_finalizer = _zircon_dart_handle_pair_attach_finalizer_ptr .asFunction<_dart_zircon_dart_handle_pair_attach_finalizer>(); @@ -180,54 +135,38 @@ class ZirconFFIBindings { ffi.Pointer pointer, int external_allocation_size, ) { - return _zircon_dart_handle_attach_finalizer( - object, - pointer, - external_allocation_size, - ); + return _zircon_dart_handle_attach_finalizer(object, pointer, external_allocation_size); } late final _zircon_dart_handle_attach_finalizer_ptr = _lookup>( - 'zircon_dart_handle_attach_finalizer'); - late final _dart_zircon_dart_handle_attach_finalizer - _zircon_dart_handle_attach_finalizer = + 'zircon_dart_handle_attach_finalizer', + ); + late final _dart_zircon_dart_handle_attach_finalizer _zircon_dart_handle_attach_finalizer = _zircon_dart_handle_attach_finalizer_ptr .asFunction<_dart_zircon_dart_handle_attach_finalizer>(); - ffi.Pointer zircon_dart_channel_create( - int options, - ) { - return _zircon_dart_channel_create( - options, - ); + ffi.Pointer zircon_dart_channel_create(int options) { + return _zircon_dart_channel_create(options); } late final _zircon_dart_channel_create_ptr = - _lookup>( - 'zircon_dart_channel_create'); + _lookup>('zircon_dart_channel_create'); late final _dart_zircon_dart_channel_create _zircon_dart_channel_create = - _zircon_dart_channel_create_ptr - .asFunction<_dart_zircon_dart_channel_create>(); + _zircon_dart_channel_create_ptr.asFunction<_dart_zircon_dart_channel_create>(); int zircon_dart_channel_write( ffi.Pointer handle, ffi.Pointer bytes, ffi.Pointer handles, ) { - return _zircon_dart_channel_write( - handle, - bytes, - handles, - ); + return _zircon_dart_channel_write(handle, bytes, handles); } late final _zircon_dart_channel_write_ptr = - _lookup>( - 'zircon_dart_channel_write'); + _lookup>('zircon_dart_channel_write'); late final _dart_zircon_dart_channel_write _zircon_dart_channel_write = - _zircon_dart_channel_write_ptr - .asFunction<_dart_zircon_dart_channel_write>(); + _zircon_dart_channel_write_ptr.asFunction<_dart_zircon_dart_channel_write>(); int zircon_dart_clock_get_monotonic() { return _zircon_dart_clock_get_monotonic(); @@ -235,25 +174,19 @@ class ZirconFFIBindings { late final _zircon_dart_clock_get_monotonic_ptr = _lookup>( - 'zircon_dart_clock_get_monotonic'); - late final _dart_zircon_dart_clock_get_monotonic - _zircon_dart_clock_get_monotonic = _zircon_dart_clock_get_monotonic_ptr - .asFunction<_dart_zircon_dart_clock_get_monotonic>(); + 'zircon_dart_clock_get_monotonic', + ); + late final _dart_zircon_dart_clock_get_monotonic _zircon_dart_clock_get_monotonic = + _zircon_dart_clock_get_monotonic_ptr.asFunction<_dart_zircon_dart_clock_get_monotonic>(); - int zircon_dart_dl_initialize( - ffi.Pointer initialize_api_dl_data, - ) { - return _zircon_dart_dl_initialize( - initialize_api_dl_data, - ); + int zircon_dart_dl_initialize(ffi.Pointer initialize_api_dl_data) { + return _zircon_dart_dl_initialize(initialize_api_dl_data); } late final _zircon_dart_dl_initialize_ptr = - _lookup>( - 'zircon_dart_dl_initialize'); + _lookup>('zircon_dart_dl_initialize'); late final _dart_zircon_dart_dl_initialize _zircon_dart_dl_initialize = - _zircon_dart_dl_initialize_ptr - .asFunction<_dart_zircon_dart_dl_initialize>(); + _zircon_dart_dl_initialize_ptr.asFunction<_dart_zircon_dart_dl_initialize>(); } final class zircon_dart_byte_array_t extends ffi.Struct { @@ -283,138 +216,105 @@ final class zircon_dart_handle_list_t extends ffi.Struct { final class _Dart_Handle extends ffi.Opaque {} -typedef _c_zircon_dart_byte_array_create = ffi.Pointer - Function( - ffi.Uint32 size, -); - -typedef _dart_zircon_dart_byte_array_create - = ffi.Pointer Function( - int size, -); - -typedef _c_zircon_dart_byte_array_set_value = ffi.Void Function( - ffi.Pointer arr, - ffi.Uint32 index, - ffi.Uint8 value, -); - -typedef _dart_zircon_dart_byte_array_set_value = void Function( - ffi.Pointer arr, - int index, - int value, -); - -typedef _c_zircon_dart_byte_array_free = ffi.Void Function( - ffi.Pointer arr, -); - -typedef _dart_zircon_dart_byte_array_free = void Function( - ffi.Pointer arr, -); - -typedef _c_zircon_dart_handle_list_create - = ffi.Pointer Function(); - -typedef _dart_zircon_dart_handle_list_create - = ffi.Pointer Function(); - -typedef _c_zircon_dart_handle_list_append = ffi.Void Function( - ffi.Pointer list, - ffi.Pointer handle, -); - -typedef _dart_zircon_dart_handle_list_append = void Function( - ffi.Pointer list, - ffi.Pointer handle, -); - -typedef _c_zircon_dart_handle_list_free = ffi.Void Function( - ffi.Pointer list, -); - -typedef _dart_zircon_dart_handle_list_free = void Function( - ffi.Pointer list, -); - -typedef _c_zircon_dart_handle_is_valid = ffi.Int32 Function( - ffi.Pointer handle, -); - -typedef _dart_zircon_dart_handle_is_valid = int Function( - ffi.Pointer handle, -); - -typedef _c_zircon_dart_handle_close = ffi.Int32 Function( - ffi.Pointer handle, -); - -typedef _dart_zircon_dart_handle_close = int Function( - ffi.Pointer handle, -); - -typedef _c_zircon_dart_handle_free = ffi.Void Function( - ffi.Pointer handle, -); - -typedef _dart_zircon_dart_handle_free = void Function( - ffi.Pointer handle, -); - -typedef _c_zircon_dart_handle_pair_attach_finalizer = ffi.Int32 Function( - ffi.Handle object, - ffi.Pointer pointer, - ffi.IntPtr external_allocation_size, -); - -typedef _dart_zircon_dart_handle_pair_attach_finalizer = int Function( - Object object, - ffi.Pointer pointer, - int external_allocation_size, -); - -typedef _c_zircon_dart_handle_attach_finalizer = ffi.Int32 Function( - ffi.Handle object, - ffi.Pointer pointer, - ffi.IntPtr external_allocation_size, -); - -typedef _dart_zircon_dart_handle_attach_finalizer = int Function( - Object object, - ffi.Pointer pointer, - int external_allocation_size, -); - -typedef _c_zircon_dart_channel_create = ffi.Pointer - Function( - ffi.Uint32 options, -); - -typedef _dart_zircon_dart_channel_create - = ffi.Pointer Function( - int options, -); - -typedef _c_zircon_dart_channel_write = ffi.Int32 Function( - ffi.Pointer handle, - ffi.Pointer bytes, - ffi.Pointer handles, -); - -typedef _dart_zircon_dart_channel_write = int Function( - ffi.Pointer handle, - ffi.Pointer bytes, - ffi.Pointer handles, -); +typedef _c_zircon_dart_byte_array_create = + ffi.Pointer Function(ffi.Uint32 size); + +typedef _dart_zircon_dart_byte_array_create = + ffi.Pointer Function(int size); + +typedef _c_zircon_dart_byte_array_set_value = + ffi.Void Function(ffi.Pointer arr, ffi.Uint32 index, ffi.Uint8 value); + +typedef _dart_zircon_dart_byte_array_set_value = + void Function(ffi.Pointer arr, int index, int value); + +typedef _c_zircon_dart_byte_array_free = + ffi.Void Function(ffi.Pointer arr); + +typedef _dart_zircon_dart_byte_array_free = + void Function(ffi.Pointer arr); + +typedef _c_zircon_dart_handle_list_create = ffi.Pointer Function(); + +typedef _dart_zircon_dart_handle_list_create = ffi.Pointer Function(); + +typedef _c_zircon_dart_handle_list_append = + ffi.Void Function( + ffi.Pointer list, + ffi.Pointer handle, + ); + +typedef _dart_zircon_dart_handle_list_append = + void Function( + ffi.Pointer list, + ffi.Pointer handle, + ); + +typedef _c_zircon_dart_handle_list_free = + ffi.Void Function(ffi.Pointer list); + +typedef _dart_zircon_dart_handle_list_free = + void Function(ffi.Pointer list); + +typedef _c_zircon_dart_handle_is_valid = + ffi.Int32 Function(ffi.Pointer handle); + +typedef _dart_zircon_dart_handle_is_valid = int Function(ffi.Pointer handle); + +typedef _c_zircon_dart_handle_close = ffi.Int32 Function(ffi.Pointer handle); + +typedef _dart_zircon_dart_handle_close = int Function(ffi.Pointer handle); + +typedef _c_zircon_dart_handle_free = ffi.Void Function(ffi.Pointer handle); + +typedef _dart_zircon_dart_handle_free = void Function(ffi.Pointer handle); + +typedef _c_zircon_dart_handle_pair_attach_finalizer = + ffi.Int32 Function( + ffi.Handle object, + ffi.Pointer pointer, + ffi.IntPtr external_allocation_size, + ); + +typedef _dart_zircon_dart_handle_pair_attach_finalizer = + int Function(Object object, ffi.Pointer pointer, int external_allocation_size); + +typedef _c_zircon_dart_handle_attach_finalizer = + ffi.Int32 Function( + ffi.Handle object, + ffi.Pointer pointer, + ffi.IntPtr external_allocation_size, + ); + +typedef _dart_zircon_dart_handle_attach_finalizer = + int Function(Object object, ffi.Pointer pointer, int external_allocation_size); + +typedef _c_zircon_dart_channel_create = + ffi.Pointer Function(ffi.Uint32 options); + +typedef _dart_zircon_dart_channel_create = + ffi.Pointer Function(int options); + +typedef _c_zircon_dart_channel_write = + ffi.Int32 Function( + ffi.Pointer handle, + ffi.Pointer bytes, + ffi.Pointer handles, + ); + +typedef _dart_zircon_dart_channel_write = + int Function( + ffi.Pointer handle, + ffi.Pointer bytes, + ffi.Pointer handles, + ); typedef _c_zircon_dart_clock_get_monotonic = ffi.Uint64 Function(); typedef _dart_zircon_dart_clock_get_monotonic = int Function(); -typedef _c_zircon_dart_dl_initialize = ffi.Int32 Function( - ffi.Pointer initialize_api_dl_data, -); +typedef _c_zircon_dart_dl_initialize = + ffi.Int32 Function(ffi.Pointer initialize_api_dl_data); -typedef _dart_zircon_dart_dl_initialize = int Function( - ffi.Pointer initialize_api_dl_data, -); +typedef _dart_zircon_dart_dl_initialize = + int Function(ffi.Pointer initialize_api_dl_data); diff --git a/shell/platform/fuchsia/dart/compiler.dart b/shell/platform/fuchsia/dart/compiler.dart index c955e218812c8..ffd755884c1fa 100644 --- a/shell/platform/fuchsia/dart/compiler.dart +++ b/shell/platform/fuchsia/dart/compiler.dart @@ -11,10 +11,12 @@ import 'package:crypto/crypto.dart'; import 'package:vm/kernel_front_end.dart' show createCompilerArgParser, runCompiler, successExitCode; -final ArgParser _argParser = createCompilerArgParser() - ..addFlag('train', +final ArgParser _argParser = + createCompilerArgParser()..addFlag( + 'train', help: 'Run through sample command line to produce snapshot', - negatable: false); + negatable: false, + ); String _usage = ''' Usage: compiler [options] input.dart @@ -29,13 +31,9 @@ Future main(List args) async { options = _argParser.parse(args); if (options['train']) { - final Directory temp = - Directory.systemTemp.createTempSync('train_kernel_compiler'); + final Directory temp = Directory.systemTemp.createTempSync('train_kernel_compiler'); try { - options = _argParser.parse([ - '--manifest=flutter', - '--data-dir=${temp.absolute}', - ]); + options = _argParser.parse(['--manifest=flutter', '--data-dir=${temp.absolute}']); await runCompiler(options, _usage); return; diff --git a/shell/platform/fuchsia/flutter/tests/integration/embedder/child-view/lib/child_view.dart b/shell/platform/fuchsia/flutter/tests/integration/embedder/child-view/lib/child_view.dart index 5b71294b700d5..0bd2156e7789d 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/embedder/child-view/lib/child_view.dart +++ b/shell/platform/fuchsia/flutter/tests/integration/embedder/child-view/lib/child_view.dart @@ -41,10 +41,11 @@ class TestApp { final paint = Paint()..color = this._backgroundColor; canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), paint); final picture = recorder.endRecording(); - final sceneBuilder = SceneBuilder() - ..pushClipRect(physicalBounds) - ..addPicture(Offset.zero, picture) - ..pop(); + final sceneBuilder = + SceneBuilder() + ..pushClipRect(physicalBounds) + ..addPicture(Offset.zero, picture) + ..pop(); window.render(sceneBuilder.build()); } diff --git a/shell/platform/fuchsia/flutter/tests/integration/embedder/parent-view/lib/parent_view.dart b/shell/platform/fuchsia/flutter/tests/integration/embedder/parent-view/lib/parent_view.dart index 918bef8260b54..cf937836c2e9c 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/embedder/parent-view/lib/parent_view.dart +++ b/shell/platform/fuchsia/flutter/tests/integration/embedder/parent-view/lib/parent_view.dart @@ -17,9 +17,10 @@ void main(List args) async { print('parent-view: starting'); args = args + _GetArgsFromConfigFile(); - final parser = ArgParser() - ..addFlag('showOverlay', defaultsTo: false) - ..addFlag('focusable', defaultsTo: true); + final parser = + ArgParser() + ..addFlag('showOverlay', defaultsTo: false) + ..addFlag('focusable', defaultsTo: true); final arguments = parser.parse(args); for (final option in arguments.options) { print('parent-view: $option: ${arguments[option]}'); @@ -45,9 +46,7 @@ class TestApp { Color _backgroundColor = _blue; - TestApp(this.childView, - {this.showOverlay = false, - this.focusable = true}) {} + TestApp(this.childView, {this.showOverlay = false, this.focusable = true}) {} void run() { childView.create(focusable, (ByteData? reply) { @@ -86,23 +85,30 @@ class TestApp { canvas.drawRect(windowBounds, paint); final picture = recorder.endRecording(); - final sceneBuilder = SceneBuilder() - ..pushClipRect(windowPhysicalBounds) - ..addPicture(Offset.zero, picture); + final sceneBuilder = + SceneBuilder() + ..pushClipRect(windowPhysicalBounds) + ..addPicture(Offset.zero, picture); final childPhysicalSize = window.physicalSize * 0.33; // Alignment.center final windowCenter = windowSize.center(Offset.zero); final windowPhysicalCenter = window.physicalSize.center(Offset.zero); - final childPhysicalOffset = - windowPhysicalCenter - childPhysicalSize.center(Offset.zero); + final childPhysicalOffset = windowPhysicalCenter - childPhysicalSize.center(Offset.zero); sceneBuilder - ..pushTransform(vector_math_64.Matrix4.translationValues( - childPhysicalOffset.dx, childPhysicalOffset.dy, 0.0) - .storage) - ..addPlatformView(childView.viewId, - width: childPhysicalSize.width, height: childPhysicalSize.height) + ..pushTransform( + vector_math_64.Matrix4.translationValues( + childPhysicalOffset.dx, + childPhysicalOffset.dy, + 0.0, + ).storage, + ) + ..addPlatformView( + childView.viewId, + width: childPhysicalSize.width, + height: childPhysicalSize.height, + ) ..pop(); if (showOverlay) { @@ -113,16 +119,16 @@ class TestApp { final overlaySize = containerSize * 0.5; // Alignment.topRight final overlayOffset = Offset( - containerOffset.dx + containerSize.width - overlaySize.width, - containerOffset.dy); + containerOffset.dx + containerSize.width - overlaySize.width, + containerOffset.dy, + ); final overlayPhysicalSize = overlaySize * pixelRatio; final overlayPhysicalOffset = overlayOffset * pixelRatio; final overlayPhysicalBounds = overlayPhysicalOffset & overlayPhysicalSize; final recorder = PictureRecorder(); - final overlayCullRect = - Offset.zero & overlayPhysicalSize; // in canvas physical coordinates + final overlayCullRect = Offset.zero & overlayPhysicalSize; // in canvas physical coordinates final canvas = Canvas(recorder, overlayCullRect); canvas.scale(pixelRatio); final paint = Paint()..color = Color.fromARGB(255, 0, 255, 0); @@ -144,8 +150,7 @@ class ChildView { ChildView(this.viewId); - void create(bool focusable, - PlatformMessageResponseCallback callback) { + void create(bool focusable, PlatformMessageResponseCallback callback) { // Construct the dart:ui platform message to create the view, and when the // return callback is invoked, build the scene. At that point, it is safe // to embed the child-view2 in the scene. @@ -160,20 +165,21 @@ class ChildView { viewOcclusionHint.left, viewOcclusionHint.top, viewOcclusionHint.right, - viewOcclusionHint.bottom + viewOcclusionHint.bottom, ], }; - final ByteData createViewMessage = - ByteData.sublistView(utf8.encode(json.encode({ - 'method': 'View.create', - 'args': args, - }))); + final ByteData createViewMessage = ByteData.sublistView( + utf8.encode(json.encode({'method': 'View.create', 'args': args})), + ); final platformViewsChannel = 'flutter/platform_views'; - PlatformDispatcher.instance - .sendPlatformMessage(platformViewsChannel, createViewMessage, callback); + PlatformDispatcher.instance.sendPlatformMessage( + platformViewsChannel, + createViewMessage, + callback, + ); } } @@ -181,12 +187,14 @@ Future _launchChildView() async { final message = Int8List.fromList([0x31]); final completer = new Completer(); PlatformDispatcher.instance.sendPlatformMessage( - 'fuchsia/child_view', ByteData.sublistView(message), (ByteData? reply) { - completer.complete(reply!); - }); + 'fuchsia/child_view', + ByteData.sublistView(message), + (ByteData? reply) { + completer.complete(reply!); + }, + ); - return int.parse( - ascii.decode(((await completer.future).buffer.asUint8List()))); + return int.parse(ascii.decode(((await completer.future).buffer.asUint8List()))); } List _GetArgsFromConfigFile() { diff --git a/shell/platform/fuchsia/flutter/tests/integration/mouse-input/mouse-input-view/lib/mouse-input-view.dart b/shell/platform/fuchsia/flutter/tests/integration/mouse-input/mouse-input-view/lib/mouse-input-view.dart index b3f2104c5747c..e1b92eb00e333 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/mouse-input/mouse-input-view/lib/mouse-input-view.dart +++ b/shell/platform/fuchsia/flutter/tests/integration/mouse-input/mouse-input-view/lib/mouse-input-view.dart @@ -21,14 +21,7 @@ class MyApp { static const _blue = Color.fromARGB(255, 33, 150, 143); static const _purple = Color.fromARGB(255, 156, 39, 176); - final List _colors = [ - _red, - _orange, - _yellow, - _green, - _blue, - _purple, - ]; + final List _colors = [_red, _orange, _yellow, _green, _blue, _purple]; // Each tap will increment the counter, we then determine what color to choose int _touchCounter = 0; @@ -66,10 +59,11 @@ class MyApp { canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), paint); // Build the scene final picture = recorder.endRecording(); - final sceneBuilder = SceneBuilder() - ..pushClipRect(physicalBounds) - ..addPicture(Offset.zero, picture) - ..pop(); + final sceneBuilder = + SceneBuilder() + ..pushClipRect(physicalBounds) + ..addPicture(Offset.zero, picture) + ..pop(); window.render(sceneBuilder.build()); } @@ -99,27 +93,31 @@ class MyApp { window.scheduleFrame(); } - void _reportMouseInput( - {required double localX, - required double localY, - required int timeReceived, - required int buttons, - required String phase, - required double wheelXPhysicalPixel, - required double wheelYPhysicalPixel}) { + void _reportMouseInput({ + required double localX, + required double localY, + required int timeReceived, + required int buttons, + required String phase, + required double wheelXPhysicalPixel, + required double wheelYPhysicalPixel, + }) { print('mouse-input-view reporting mouse input to MouseInputListener'); - final message = ByteData.sublistView(utf8.encode(json.encode({ - 'method': 'MouseInputListener.ReportMouseInput', - 'local_x': localX, - 'local_y': localY, - 'time_received': timeReceived, - 'component_name': 'touch-input-view', - 'buttons': buttons, - 'phase': phase, - 'wheel_x_physical_pixel': wheelXPhysicalPixel, - 'wheel_y_physical_pixel': wheelYPhysicalPixel, - }))); - PlatformDispatcher.instance - .sendPlatformMessage('fuchsia/input_test', message, null); + final message = ByteData.sublistView( + utf8.encode( + json.encode({ + 'method': 'MouseInputListener.ReportMouseInput', + 'local_x': localX, + 'local_y': localY, + 'time_received': timeReceived, + 'component_name': 'touch-input-view', + 'buttons': buttons, + 'phase': phase, + 'wheel_x_physical_pixel': wheelXPhysicalPixel, + 'wheel_y_physical_pixel': wheelYPhysicalPixel, + }), + ), + ); + PlatformDispatcher.instance.sendPlatformMessage('fuchsia/input_test', message, null); } } diff --git a/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/lib/text_input_view.dart b/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/lib/text_input_view.dart index d3a318e3b248d..f3e8b38d12ebb 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/lib/text_input_view.dart +++ b/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/lib/text_input_view.dart @@ -39,8 +39,11 @@ class TestApp { void run() { // Set up window callbacks - window.onPlatformMessage = - (String name, ByteData? data, PlatformMessageResponseCallback? callback) { + window.onPlatformMessage = ( + String name, + ByteData? data, + PlatformMessageResponseCallback? callback, + ) { this.decodeAndReportPlatformMessage(name, data!); }; window.onMetricsChanged = () { @@ -68,10 +71,11 @@ class TestApp { canvas.drawRect(windowBounds, paint); // Build the scene final picture = recorder.endRecording(); - final sceneBuilder = SceneBuilder() - ..pushClipRect(physicalBounds) - ..addPicture(Offset.zero, picture) - ..pop(); + final sceneBuilder = + SceneBuilder() + ..pushClipRect(physicalBounds) + ..addPicture(Offset.zero, picture) + ..pop(); window.render(sceneBuilder.build()); } @@ -94,14 +98,11 @@ class TestApp { void _reportTextInput(String text) { print('text-input-view reporting keyboard input to KeyboardInputListener'); - final message = utf8 - .encode(json.encode({ - 'method': 'KeyboardInputListener.ReportTextInput', - 'text': text, - })) - .buffer - .asByteData(); - PlatformDispatcher.instance - .sendPlatformMessage('fuchsia/input_test', message, null); + final message = + utf8 + .encode(json.encode({'method': 'KeyboardInputListener.ReportTextInput', 'text': text})) + .buffer + .asByteData(); + PlatformDispatcher.instance.sendPlatformMessage('fuchsia/input_test', message, null); } } diff --git a/shell/platform/fuchsia/flutter/tests/integration/touch-input/embedding-flutter-view/lib/embedding-flutter-view.dart b/shell/platform/fuchsia/flutter/tests/integration/touch-input/embedding-flutter-view/lib/embedding-flutter-view.dart index 39a161a05ee44..ac0448c3aa6a4 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/touch-input/embedding-flutter-view/lib/embedding-flutter-view.dart +++ b/shell/platform/fuchsia/flutter/tests/integration/touch-input/embedding-flutter-view/lib/embedding-flutter-view.dart @@ -18,9 +18,10 @@ void main(List args) async { print('Launching embedding-flutter-view'); args = args + _GetArgsFromConfigFile(); - final parser = ArgParser() - ..addFlag('showOverlay', defaultsTo: false) - ..addFlag('focusable', defaultsTo: true); + final parser = + ArgParser() + ..addFlag('showOverlay', defaultsTo: false) + ..addFlag('focusable', defaultsTo: true); final arguments = parser.parse(args); for (final option in arguments.options) { @@ -46,29 +47,25 @@ class TestApp { Color _backgroundColor = _blue; - TestApp( - this.childView, - {this.showOverlay = false, - this.focusable = true}) { - } + TestApp(this.childView, {this.showOverlay = false, this.focusable = true}) {} void run() { childView.create(focusable, (ByteData? reply) { - // Set up window callbacks. - window.onPointerDataPacket = (PointerDataPacket packet) { - this.pointerDataPacket(packet); - }; - window.onMetricsChanged = () { - window.scheduleFrame(); - }; - window.onBeginFrame = (Duration duration) { - this.beginFrame(duration); - }; - - // The child view should be attached to Scenic now. - // Ready to build the scene. + // Set up window callbacks. + window.onPointerDataPacket = (PointerDataPacket packet) { + this.pointerDataPacket(packet); + }; + window.onMetricsChanged = () { window.scheduleFrame(); - }); + }; + window.onBeginFrame = (Duration duration) { + this.beginFrame(duration); + }; + + // The child view should be attached to Scenic now. + // Ready to build the scene. + window.scheduleFrame(); + }); } void beginFrame(Duration duration) { @@ -86,9 +83,10 @@ class TestApp { canvas.drawRect(windowBounds, paint); final picture = recorder.endRecording(); // Build the scene - final sceneBuilder = SceneBuilder() - ..pushClipRect(physicalBounds) - ..addPicture(Offset.zero, picture); + final sceneBuilder = + SceneBuilder() + ..pushClipRect(physicalBounds) + ..addPicture(Offset.zero, picture); final childPhysicalSize = window.physicalSize * 0.25; // Alignment.center @@ -98,12 +96,17 @@ class TestApp { sceneBuilder ..pushTransform( - vector_math_64.Matrix4.translationValues(childPhysicalOffset.dx, - childPhysicalOffset.dy, - 0.0).storage) - ..addPlatformView(childView.viewId, - width: childPhysicalSize.width, - height: childPhysicalSize.height) + vector_math_64.Matrix4.translationValues( + childPhysicalOffset.dx, + childPhysicalOffset.dy, + 0.0, + ).storage, + ) + ..addPlatformView( + childView.viewId, + width: childPhysicalSize.width, + height: childPhysicalSize.height, + ) ..pop(); if (showOverlay) { @@ -115,7 +118,8 @@ class TestApp { // Alignment.topRight final overlayOffset = Offset( containerOffset.dx + containerSize.width - overlaySize.width, - containerOffset.dy); + containerOffset.dy, + ); final overlayPhysicalSize = overlaySize * pixelRatio; final overlayPhysicalOffset = overlayOffset * pixelRatio; final overlayPhysicalBounds = overlayPhysicalOffset & overlayPhysicalSize; @@ -150,26 +154,32 @@ class TestApp { } if (data.change == PointerChange.down || data.change == PointerChange.move) { - _reportTouchInput( - localX: data.physicalX, - localY: data.physicalY, - timeReceived: nowNanos, - ); + _reportTouchInput(localX: data.physicalX, localY: data.physicalY, timeReceived: nowNanos); } } window.scheduleFrame(); } - void _reportTouchInput({required double localX, required double localY, required int timeReceived}) { + void _reportTouchInput({ + required double localX, + required double localY, + required int timeReceived, + }) { print('embedding-flutter-view reporting touch input to TouchInputListener'); - final message = utf8.encode(json.encode({ - 'method': 'TouchInputListener.ReportTouchInput', - 'local_x': localX, - 'local_y': localY, - 'time_received': timeReceived, - 'component_name': 'embedding-flutter-view', - })).buffer.asByteData(); + final message = + utf8 + .encode( + json.encode({ + 'method': 'TouchInputListener.ReportTouchInput', + 'local_x': localX, + 'local_y': localY, + 'time_received': timeReceived, + 'component_name': 'embedding-flutter-view', + }), + ) + .buffer + .asByteData(); PlatformDispatcher.instance.sendPlatformMessage('fuchsia/input_test', message, null); } } @@ -179,9 +189,7 @@ class ChildView { ChildView(this.viewId); - void create( - bool focusable, - PlatformMessageResponseCallback callback) { + void create(bool focusable, PlatformMessageResponseCallback callback) { // Construct the dart:ui platform message to create the view, and when the // return callback is invoked, build the scene. At that point, it is safe // to embed the child view in the scene. @@ -195,23 +203,23 @@ class ChildView { viewOcclusionHint.left, viewOcclusionHint.top, viewOcclusionHint.right, - viewOcclusionHint.bottom + viewOcclusionHint.bottom, ], }; - final ByteData createViewMessage = utf8.encode( - json.encode({ - 'method': 'View.create', - 'args': args, - }) - ).buffer.asByteData(); + final ByteData createViewMessage = + utf8 + .encode(json.encode({'method': 'View.create', 'args': args})) + .buffer + .asByteData(); final platformViewsChannel = 'flutter/platform_views'; PlatformDispatcher.instance.sendPlatformMessage( platformViewsChannel, createViewMessage, - callback); + callback, + ); } } @@ -219,15 +227,16 @@ Future _launchChildView() async { final message = Int8List.fromList([0x31]); final completer = new Completer(); PlatformDispatcher.instance.sendPlatformMessage( - 'fuchsia/child_view', ByteData.sublistView(message), (ByteData? reply) { - completer.complete(reply!); - }); + 'fuchsia/child_view', + ByteData.sublistView(message), + (ByteData? reply) { + completer.complete(reply!); + }, + ); - return int.parse( - ascii.decode(((await completer.future).buffer.asUint8List()))); + return int.parse(ascii.decode(((await completer.future).buffer.asUint8List()))); } - List _GetArgsFromConfigFile() { List args; final f = File(_argsCsvFilePath); diff --git a/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/lib/touch-input-view.dart b/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/lib/touch-input-view.dart index 8032b1e4ba20b..ced8882ace8b1 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/lib/touch-input-view.dart +++ b/shell/platform/fuchsia/flutter/tests/integration/touch-input/touch-input-view/lib/touch-input-view.dart @@ -51,10 +51,11 @@ class TestApp { canvas.drawRect(windowBounds, paint); // Build the scene final picture = recorder.endRecording(); - final sceneBuilder = SceneBuilder() - ..pushClipRect(physicalBounds) - ..addPicture(Offset.zero, picture) - ..pop(); + final sceneBuilder = + SceneBuilder() + ..pushClipRect(physicalBounds) + ..addPicture(Offset.zero, picture) + ..pop(); window.render(sceneBuilder.build()); } @@ -69,26 +70,32 @@ class TestApp { } if (data.change == PointerChange.down || data.change == PointerChange.move) { - _reportTouchInput( - localX: data.physicalX, - localY: data.physicalY, - timeReceived: nowNanos, - ); + _reportTouchInput(localX: data.physicalX, localY: data.physicalY, timeReceived: nowNanos); } } window.scheduleFrame(); } - void _reportTouchInput({required double localX, required double localY, required int timeReceived}) { + void _reportTouchInput({ + required double localX, + required double localY, + required int timeReceived, + }) { print('touch-input-view reporting touch input to TouchInputListener'); - final message = utf8.encode(json.encode({ - 'method': 'TouchInputListener.ReportTouchInput', - 'local_x': localX, - 'local_y': localY, - 'time_received': timeReceived, - 'component_name': 'touch-input-view', - })).buffer.asByteData(); + final message = + utf8 + .encode( + json.encode({ + 'method': 'TouchInputListener.ReportTouchInput', + 'local_x': localX, + 'local_y': localY, + 'time_received': timeReceived, + 'component_name': 'touch-input-view', + }), + ) + .buffer + .asByteData(); PlatformDispatcher.instance.sendPlatformMessage('fuchsia/input_test', message, null); } } diff --git a/shell/platform/fuchsia/runtime/dart/profiler_symbols/dart_profiler_symbols.dart b/shell/platform/fuchsia/runtime/dart/profiler_symbols/dart_profiler_symbols.dart index b78736ffe4490..da3eed0e09bd9 100644 --- a/shell/platform/fuchsia/runtime/dart/profiler_symbols/dart_profiler_symbols.dart +++ b/shell/platform/fuchsia/runtime/dart/profiler_symbols/dart_profiler_symbols.dart @@ -30,8 +30,10 @@ import "package:path/path.dart" as path; Future main(List args) async { final parser = new ArgParser(); parser.addOption("nm", help: "Path to `nm` tool"); - parser.addOption("binary", - help: "Path to the ELF file to extract symbols from"); + parser.addOption( + "binary", + help: "Path to the ELF file to extract symbols from", + ); parser.addOption("output", help: "Path to output symbol table"); final usage = """ Usage: dart_profiler_symbols.dart [options] @@ -84,8 +86,13 @@ class Symbol { Symbol({required this.offset, required this.size, required this.name}); } -Future run(String? buildIdDir, String? buildIdScript, String nm, - String binary, String output) async { +Future run( + String? buildIdDir, + String? buildIdScript, + String nm, + String binary, + String output, +) async { final unstrippedFile = binary; final args = ["--demangle", "--numeric-sort", "--print-size", unstrippedFile]; final result = await Process.run(nm, args); @@ -105,9 +112,11 @@ Future run(String? buildIdDir, String? buildIdScript, String nm, } // Note that capture groups start at 1. - final symbol = new Symbol(offset: int.parse(match[1]!, radix: 16), - size: int.parse(match[2]!, radix: 16), - name: match[4]!.split("(")[0]); + final symbol = new Symbol( + offset: int.parse(match[1]!, radix: 16), + size: int.parse(match[2]!, radix: 16), + name: match[4]!.split("(")[0], + ); if (symbol.name.startsWith("\$")) { continue; // Ignore compiler/assembler temps. diff --git a/shell/platform/windows/fixtures/main.dart b/shell/platform/windows/fixtures/main.dart index 721ade4b45f46..5430e86020125 100644 --- a/shell/platform/windows/fixtures/main.dart +++ b/shell/platform/windows/fixtures/main.dart @@ -32,12 +32,12 @@ void main() {} @pragma('vm:entry-point') void hiPlatformChannels() { - ui.channelBuffers.setListener('hi', - (ByteData? data, ui.PlatformMessageResponseCallback callback) async { - ui.PlatformDispatcher.instance.sendPlatformMessage('hi', data, - (ByteData? reply) { - ui.PlatformDispatcher.instance - .sendPlatformMessage('hi', reply, (ByteData? reply) {}); + ui.channelBuffers.setListener('hi', ( + ByteData? data, + ui.PlatformMessageResponseCallback callback, + ) async { + ui.PlatformDispatcher.instance.sendPlatformMessage('hi', data, (ByteData? reply) { + ui.PlatformDispatcher.instance.sendPlatformMessage('hi', reply, (ByteData? reply) {}); }); callback(data); }); @@ -47,8 +47,7 @@ void hiPlatformChannels() { /// `PlatformDispatcher.instance.onSemanticsEnabledChanged` fires. Future get semanticsChanged { final Completer semanticsChanged = Completer(); - ui.PlatformDispatcher.instance.onSemanticsEnabledChanged = - semanticsChanged.complete; + ui.PlatformDispatcher.instance.onSemanticsEnabledChanged = semanticsChanged.complete; return semanticsChanged.future; } @@ -131,10 +130,12 @@ Future sendAccessibilityTooltipEvent() async { @pragma('vm:entry-point') Future exitTestExit() async { final Completer closed = Completer(); - ui.channelBuffers.setListener('flutter/platform', - (ByteData? data, ui.PlatformMessageResponseCallback callback) async { + ui.channelBuffers.setListener('flutter/platform', ( + ByteData? data, + ui.PlatformMessageResponseCallback callback, + ) async { final String jsonString = json.encode(>[ - {'response': 'exit'} + {'response': 'exit'}, ]); final ByteData responseData = ByteData.sublistView(utf8.encode(jsonString)); callback(responseData); @@ -146,10 +147,12 @@ Future exitTestExit() async { @pragma('vm:entry-point') Future exitTestCancel() async { final Completer closed = Completer(); - ui.channelBuffers.setListener('flutter/platform', - (ByteData? data, ui.PlatformMessageResponseCallback callback) async { + ui.channelBuffers.setListener('flutter/platform', ( + ByteData? data, + ui.PlatformMessageResponseCallback callback, + ) async { final String jsonString = json.encode(>[ - {'response': 'cancel'} + {'response': 'cancel'}, ]); final ByteData responseData = ByteData.sublistView(utf8.encode(jsonString)); callback(responseData); @@ -161,24 +164,29 @@ Future exitTestCancel() async { final Completer exited = Completer(); final String jsonString = json.encode({ 'method': 'System.exitApplication', - 'args': {'type': 'required', 'exitCode': 0} + 'args': {'type': 'required', 'exitCode': 0}, }); ui.PlatformDispatcher.instance.sendPlatformMessage( - 'flutter/platform', ByteData.sublistView(utf8.encode(jsonString)), - (ByteData? reply) { - exited.complete(reply); - }); + 'flutter/platform', + ByteData.sublistView(utf8.encode(jsonString)), + (ByteData? reply) { + exited.complete(reply); + }, + ); await exited.future; } @pragma('vm:entry-point') Future enableLifecycleTest() async { final Completer finished = Completer(); - ui.channelBuffers.setListener('flutter/lifecycle', - (ByteData? data, ui.PlatformMessageResponseCallback callback) async { + ui.channelBuffers.setListener('flutter/lifecycle', ( + ByteData? data, + ui.PlatformMessageResponseCallback callback, + ) async { if (data != null) { - ui.PlatformDispatcher.instance - .sendPlatformMessage('flutter/unittest', data, (ByteData? reply) { + ui.PlatformDispatcher.instance.sendPlatformMessage('flutter/unittest', data, ( + ByteData? reply, + ) { finished.complete(); }); } @@ -188,21 +196,26 @@ Future enableLifecycleTest() async { @pragma('vm:entry-point') Future enableLifecycleToFrom() async { - ui.channelBuffers.setListener('flutter/lifecycle', - (ByteData? data, ui.PlatformMessageResponseCallback callback) async { + ui.channelBuffers.setListener('flutter/lifecycle', ( + ByteData? data, + ui.PlatformMessageResponseCallback callback, + ) async { if (data != null) { - ui.PlatformDispatcher.instance - .sendPlatformMessage('flutter/unittest', data, (ByteData? reply) {}); + ui.PlatformDispatcher.instance.sendPlatformMessage( + 'flutter/unittest', + data, + (ByteData? reply) {}, + ); } }); final Completer enabledLifecycle = Completer(); ui.PlatformDispatcher.instance.sendPlatformMessage( - 'flutter/platform', - ByteData.sublistView( - utf8.encode('{"method":"System.initializationComplete"}')), - (ByteData? data) { - enabledLifecycle.complete(data); - }); + 'flutter/platform', + ByteData.sublistView(utf8.encode('{"method":"System.initializationComplete"}')), + (ByteData? data) { + enabledLifecycle.complete(data); + }, + ); } @pragma('vm:entry-point') @@ -230,8 +243,9 @@ Future sendCreatePlatformViewMethod() async { final Completer completed = Completer(); final ByteData bytes = ByteData.sublistView(Uint8List.fromList(data)); - ui.PlatformDispatcher.instance.sendPlatformMessage( - 'flutter/platform_views', bytes, (ByteData? response) { + ui.PlatformDispatcher.instance.sendPlatformMessage('flutter/platform_views', bytes, ( + ByteData? response, + ) { completed.complete(response); }); await completed.future; @@ -256,8 +270,9 @@ Future sendGetKeyboardState() async { final Completer completer = Completer(); final ByteData bytes = ByteData.sublistView(Uint8List.fromList(data)); - ui.PlatformDispatcher.instance.sendPlatformMessage('flutter/keyboard', bytes, - (ByteData? response) { + ui.PlatformDispatcher.instance.sendPlatformMessage('flutter/keyboard', bytes, ( + ByteData? response, + ) { // For magic numbers for decoding a reply envelope, see: // https://github.com/flutter/flutter/blob/67271f69f7f88a4edba6d8023099e3bd27a072d2/packages/flutter/lib/src/services/message_codecs.dart#L577-L587 const int replyEnvelopeSuccess = 0; @@ -266,14 +281,11 @@ Future sendGetKeyboardState() async { if (response == null) { signalStringValue('Unexpected null response'); } else if (response.lengthInBytes < 2) { - signalStringValue( - 'Unexpected response length of ${response.lengthInBytes} bytes'); + signalStringValue('Unexpected response length of ${response.lengthInBytes} bytes'); } else if (response.getUint8(0) != replyEnvelopeSuccess) { - signalStringValue( - 'Unexpected response envelope status: ${response.getUint8(0)}'); + signalStringValue('Unexpected response envelope status: ${response.getUint8(0)}'); } else if (response.getUint8(1) != valueMap) { - signalStringValue( - 'Unexpected response value magic number: ${response.getUint8(1)}'); + signalStringValue('Unexpected response value magic number: ${response.getUint8(1)}'); } else { signalStringValue('Success'); } @@ -309,8 +321,8 @@ void readPlatformExecutable() { @pragma('vm:entry-point') void drawHelloWorld() { ui.PlatformDispatcher.instance.onBeginFrame = (Duration duration) { - final ui.ParagraphBuilder paragraphBuilder = - ui.ParagraphBuilder(ui.ParagraphStyle())..addText('Hello world'); + final ui.ParagraphBuilder paragraphBuilder = ui.ParagraphBuilder(ui.ParagraphStyle()) + ..addText('Hello world'); final ui.Paragraph paragraph = paragraphBuilder.build(); paragraph.layout(const ui.ParagraphConstraints(width: 800.0)); @@ -321,9 +333,10 @@ void drawHelloWorld() { canvas.drawParagraph(paragraph, ui.Offset.zero); final ui.Picture picture = recorder.endRecording(); - final ui.SceneBuilder sceneBuilder = ui.SceneBuilder() - ..addPicture(ui.Offset.zero, picture) - ..pop(); + final ui.SceneBuilder sceneBuilder = + ui.SceneBuilder() + ..addPicture(ui.Offset.zero, picture) + ..pop(); ui.PlatformDispatcher.instance.implicitView?.render(sceneBuilder.build()); }; @@ -363,8 +376,7 @@ void renderImplicitView() { @pragma('vm:entry-point') void signalViewIds() { final Iterable views = ui.PlatformDispatcher.instance.views; - final List viewIds = - views.map((ui.FlutterView view) => view.viewId).toList(); + final List viewIds = views.map((ui.FlutterView view) => view.viewId).toList(); viewIds.sort(); diff --git a/shell/testing/observatory/empty_main.dart b/shell/testing/observatory/empty_main.dart index 94e557c8cadc7..f9b0dd79fed95 100644 --- a/shell/testing/observatory/empty_main.dart +++ b/shell/testing/observatory/empty_main.dart @@ -2,5 +2,4 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -void main() { -} +void main() {} diff --git a/shell/testing/observatory/launcher.dart b/shell/testing/observatory/launcher.dart index ee734c87d7973..c99427f42d388 100644 --- a/shell/testing/observatory/launcher.dart +++ b/shell/testing/observatory/launcher.dart @@ -11,10 +11,7 @@ import 'dart:io'; class ShellProcess { ShellProcess(this._process) { // Scan stdout and scrape the VM Service Uri. - _process.stdout - .transform(utf8.decoder) - .transform(const LineSplitter()) - .listen((String line) { + _process.stdout.transform(utf8.decoder).transform(const LineSplitter()).listen((String line) { final uri = _extractVMServiceUri(line); if (uri != null) { _vmServiceUriCompleter.complete(uri); @@ -74,10 +71,7 @@ class ShellLauncher { } shellArguments.addAll(args); print('Launching $shellExecutablePath $shellArguments'); - final Process process = await Process.start( - shellExecutablePath, - shellArguments, - ); + final Process process = await Process.start(shellExecutablePath, shellArguments); return ShellProcess(process); } catch (e) { print('Error launching shell: $e'); diff --git a/shell/testing/observatory/service_client.dart b/shell/testing/observatory/service_client.dart index f9d74e31d4d0a..3f712630c4ba8 100644 --- a/shell/testing/observatory/service_client.dart +++ b/shell/testing/observatory/service_client.dart @@ -9,12 +9,7 @@ import 'dart:convert'; import 'dart:io'; class ServiceClient { - ServiceClient( - this.client, { - this.isolateStartedId, - this.isolatePausedId, - this.isolateResumeId, - }) { + ServiceClient(this.client, {this.isolateStartedId, this.isolatePausedId, this.isolateResumeId}) { client.listen(_onData, onError: _onError, cancelOnError: true); } @@ -22,10 +17,7 @@ class ServiceClient { Completer? isolatePausedId; Completer? isolateResumeId; - Future> invokeRPC( - String method, [ - Map? params, - ]) async { + Future> invokeRPC(String method, [Map? params]) async { final String key = _createKey(); final String request = json.encode({ 'jsonrpc': '2.0', diff --git a/shell/testing/observatory/test.dart b/shell/testing/observatory/test.dart index 2262769ff9880..1318ccced530a 100644 --- a/shell/testing/observatory/test.dart +++ b/shell/testing/observatory/test.dart @@ -37,8 +37,7 @@ Future testHttpProtocolRequest(Uri uri) async { final HttpClientRequest request = await client.getUrl(uri); final HttpClientResponse response = await request.close(); Expect.equals(response.statusCode, 200); - final responseAsMap = - json.decode(await readResponse(response)) as Map; + final responseAsMap = json.decode(await readResponse(response)) as Map; Expect.equals(responseAsMap['jsonrpc'], '2.0'); client.close(); } @@ -75,7 +74,7 @@ typedef TestFunction = Future Function(Uri uri); final List basicTests = [ testHttpProtocolRequest, testWebSocketProtocolRequest, - testHttpAssetRequest + testHttpAssetRequest, ]; Future runTests(ShellLauncher launcher, List tests) async { @@ -99,17 +98,17 @@ Future runTests(ShellLauncher launcher, List tests) async { Future main(List args) async { if (args.length < 2) { - print('Usage: dart ${Platform.script} ' - ' ...'); + print( + 'Usage: dart ${Platform.script} ' + ' ...', + ); return; } final String shellExecutablePath = args[0]; final String mainDartPath = args[1]; - final List extraArgs = - args.length <= 2 ? [] : args.sublist(2); + final List extraArgs = args.length <= 2 ? [] : args.sublist(2); - final ShellLauncher launcher = - ShellLauncher(shellExecutablePath, mainDartPath, false, extraArgs); + final ShellLauncher launcher = ShellLauncher(shellExecutablePath, mainDartPath, false, extraArgs); await runTests(launcher, basicTests); } diff --git a/testing/benchmark/bin/parse_and_send.dart b/testing/benchmark/bin/parse_and_send.dart index 705ee49cfa2f9..92b5674558681 100644 --- a/testing/benchmark/bin/parse_and_send.dart +++ b/testing/benchmark/bin/parse_and_send.dart @@ -9,26 +9,20 @@ import 'package:args/args.dart'; import 'package:metrics_center/metrics_center.dart'; import 'package:path/path.dart' as p; -Future runGit( - List args, { - String? processWorkingDir, -}) async { - return Process.run( - 'git', - args, - workingDirectory: processWorkingDir, - runInShell: true, - ); +Future runGit(List args, {String? processWorkingDir}) async { + return Process.run('git', args, workingDirectory: processWorkingDir, runInShell: true); } Future> getGitLog() async { final String gitRoot = p.absolute('../..'); // Somehow gitDir.currentBranch() doesn't work in Cirrus with "fatal: 'HEAD' - // not a valid ref". Therefore, we use "git log" to get the revision manually. - final ProcessResult logResult = await runGit( - ['log', '--pretty=format:%H %ct', '-n', '1'], - processWorkingDir: gitRoot, - ); + final ProcessResult logResult = await runGit([ + 'log', + '--pretty=format:%H %ct', + '-n', + '1', + ], processWorkingDir: gitRoot); if (logResult.exitCode != 0) { throw 'Unexpected exit code ${logResult.exitCode}'; } @@ -46,17 +40,17 @@ Future parse(String jsonFileName) async { final List gitLog = await getGitLog(); final String gitRevision = gitLog[0]; final String gitCommitDate = gitLog[1]; - final List rawPoints = await GoogleBenchmarkParser.parse( - jsonFileName, - ); + final List rawPoints = await GoogleBenchmarkParser.parse(jsonFileName); final List points = []; for (final MetricPoint rawPoint in rawPoints) { - points.add(FlutterEngineMetricPoint( - rawPoint.tags[kNameKey]!, - rawPoint.value!, - gitRevision, - moreTags: rawPoint.tags, - )); + points.add( + FlutterEngineMetricPoint( + rawPoint.tags[kNameKey]!, + rawPoint.value!, + gitRevision, + moreTags: rawPoint.tags, + ), + ); } return PointsAndDate(points, gitCommitDate); } @@ -74,23 +68,15 @@ Future connectFlutterDestination() async { ); } return FlutterDestination.makeFromCredentialsJson( - jsonDecode(Platform.environment['BENCHMARK_GCP_CREDENTIALS']!) - as Map, + jsonDecode(Platform.environment['BENCHMARK_GCP_CREDENTIALS']!) as Map, isTesting: isTesting, ); } ArgParser _serupOptions() { final ArgParser parser = ArgParser(); - parser.addOption( - 'json', - mandatory: true, - help: 'Path to the benchmarks json file.', - ); - parser.addFlag( - 'no-upload', - help: 'Upload the parsed benchmarks.', - ); + parser.addOption('json', mandatory: true, help: 'Path to the benchmarks json file.'); + parser.addFlag('no-upload', help: 'Upload the parsed benchmarks.'); return parser; } @@ -114,10 +100,7 @@ Future main(List args) async { final FlutterDestination destination = await connectFlutterDestination(); await destination.update( pointsAndDate.points, - DateTime.fromMillisecondsSinceEpoch( - int.parse(pointsAndDate.date) * 1000, - isUtc: true, - ), + DateTime.fromMillisecondsSinceEpoch(int.parse(pointsAndDate.date) * 1000, isUtc: true), 'flutter_engine_benchmark', ); } diff --git a/testing/dart/assets_test.dart b/testing/dart/assets_test.dart index 8626c6cd3fce9..67be3e10e08e5 100644 --- a/testing/dart/assets_test.dart +++ b/testing/dart/assets_test.dart @@ -39,14 +39,18 @@ void main() { }); test('returns the bytes of a file', () async { - final ImmutableBuffer buffer = await ImmutableBuffer.fromFilePath('flutter/lib/ui/fixtures/DashInNooglerHat.jpg'); + final ImmutableBuffer buffer = await ImmutableBuffer.fromFilePath( + 'flutter/lib/ui/fixtures/DashInNooglerHat.jpg', + ); expect(buffer.length == 354679, true); }); test('Can load an asset with a space in the key', () async { // This assets actual path is "fixtures/DashInNooglerHat%20WithSpace.jpg" - final ImmutableBuffer buffer = await ImmutableBuffer.fromAsset('DashInNooglerHat WithSpace.jpg'); + final ImmutableBuffer buffer = await ImmutableBuffer.fromAsset( + 'DashInNooglerHat WithSpace.jpg', + ); expect(buffer.length == 354679, true); }); @@ -70,7 +74,9 @@ void main() { /// Manually load font asset through dart. final Uint8List encoded = utf8.encode(Uri(path: Uri.encodeFull('Roboto-Medium.ttf')).path); final Completer result = Completer(); - PlatformDispatcher.instance.sendPlatformMessage('flutter/assets', encoded.buffer.asByteData(), (ByteData? data) { + PlatformDispatcher.instance.sendPlatformMessage('flutter/assets', encoded.buffer.asByteData(), ( + ByteData? data, + ) { result.complete(data!.buffer.asUint8List()); }); @@ -86,12 +92,14 @@ void main() { } Future> _createPictureFromFont(String fontFamily) async { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: fontFamily, - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 20, - )); + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle( + fontFamily: fontFamily, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: 20, + ), + ); builder.addText('Test'); final Paragraph paragraph = builder.build(); paragraph.layout(const ParagraphConstraints(width: 20 * 5.0)); diff --git a/testing/dart/canvas_test.dart b/testing/dart/canvas_test.dart index 604a6ce483285..f68c26a47b348 100644 --- a/testing/dart/canvas_test.dart +++ b/testing/dart/canvas_test.dart @@ -20,10 +20,7 @@ typedef CanvasCallback = void Function(Canvas canvas); Future createImage(int width, int height) { final Completer completer = Completer(); decodeImageFromPixels( - Uint8List.fromList(List.generate( - width * height * 4, - (int pixel) => pixel % 255, - )), + Uint8List.fromList(List.generate(width * height * 4, (int pixel) => pixel % 255)), width, height, PixelFormat.rgba8888, @@ -43,8 +40,7 @@ void testCanvas(CanvasCallback callback) { Future toImage(CanvasCallback callback, int width, int height) { final PictureRecorder recorder = PictureRecorder(); - final Canvas canvas = Canvas( - recorder, Rect.fromLTRB(0, 0, width.toDouble(), height.toDouble())); + final Canvas canvas = Canvas(recorder, Rect.fromLTRB(0, 0, width.toDouble(), height.toDouble())); callback(canvas); final Picture picture = recorder.endRecording(); return picture.toImage(width, height); @@ -53,8 +49,7 @@ Future toImage(CanvasCallback callback, int width, int height) { void testNoCrashes() { test('canvas APIs should not crash', () async { final Paint paint = Paint(); - const Rect rect = - Rect.fromLTRB(double.nan, double.nan, double.nan, double.nan); + const Rect rect = Rect.fromLTRB(double.nan, double.nan, double.nan, double.nan); final RRect rrect = RRect.fromRectAndCorners(rect); const Offset offset = Offset(double.nan, double.nan); final Path path = Path(); @@ -85,36 +80,43 @@ void testNoCrashes() { testCanvas((Canvas canvas) => canvas.clipRect(rect)); testCanvas((Canvas canvas) => canvas.clipRRect(rrect)); testCanvas((Canvas canvas) => canvas.drawArc(rect, 0.0, 0.0, false, paint)); - testCanvas((Canvas canvas) => canvas.drawAtlas(image, [], - [], [], BlendMode.src, rect, paint)); + testCanvas( + (Canvas canvas) => + canvas.drawAtlas(image, [], [], [], BlendMode.src, rect, paint), + ); testCanvas((Canvas canvas) => canvas.drawCircle(offset, double.nan, paint)); testCanvas((Canvas canvas) => canvas.drawColor(color, BlendMode.src)); testCanvas((Canvas canvas) => canvas.drawDRRect(rrect, rrect, paint)); testCanvas((Canvas canvas) => canvas.drawImage(image, offset, paint)); - testCanvas( - (Canvas canvas) => canvas.drawImageNine(image, rect, rect, paint)); - testCanvas( - (Canvas canvas) => canvas.drawImageRect(image, rect, rect, paint)); + testCanvas((Canvas canvas) => canvas.drawImageNine(image, rect, rect, paint)); + testCanvas((Canvas canvas) => canvas.drawImageRect(image, rect, rect, paint)); testCanvas((Canvas canvas) => canvas.drawLine(offset, offset, paint)); testCanvas((Canvas canvas) => canvas.drawOval(rect, paint)); testCanvas((Canvas canvas) => canvas.drawPaint(paint)); testCanvas((Canvas canvas) => canvas.drawParagraph(paragraph, offset)); testCanvas((Canvas canvas) => canvas.drawPath(path, paint)); testCanvas((Canvas canvas) => canvas.drawPicture(picture)); - testCanvas((Canvas canvas) => - canvas.drawPoints(PointMode.points, [], paint)); - testCanvas((Canvas canvas) => canvas.drawRawAtlas(image, Float32List(0), - Float32List(0), Int32List(0), BlendMode.src, rect, paint)); - testCanvas((Canvas canvas) => - canvas.drawRawPoints(PointMode.points, Float32List(0), paint)); + testCanvas((Canvas canvas) => canvas.drawPoints(PointMode.points, [], paint)); + testCanvas( + (Canvas canvas) => canvas.drawRawAtlas( + image, + Float32List(0), + Float32List(0), + Int32List(0), + BlendMode.src, + rect, + paint, + ), + ); + testCanvas((Canvas canvas) => canvas.drawRawPoints(PointMode.points, Float32List(0), paint)); testCanvas((Canvas canvas) => canvas.drawRect(rect, paint)); testCanvas((Canvas canvas) => canvas.drawRRect(rrect, paint)); + testCanvas((Canvas canvas) => canvas.drawShadow(path, color, double.nan, false)); + testCanvas((Canvas canvas) => canvas.drawShadow(path, color, double.nan, true)); testCanvas( - (Canvas canvas) => canvas.drawShadow(path, color, double.nan, false)); - testCanvas( - (Canvas canvas) => canvas.drawShadow(path, color, double.nan, true)); - testCanvas((Canvas canvas) => canvas.drawVertices( - Vertices(VertexMode.triangles, []), BlendMode.screen, paint)); + (Canvas canvas) => + canvas.drawVertices(Vertices(VertexMode.triangles, []), BlendMode.screen, paint), + ); testCanvas((Canvas canvas) => canvas.getSaveCount()); testCanvas((Canvas canvas) => canvas.restore()); testCanvas((Canvas canvas) => canvas.rotate(double.nan)); @@ -125,25 +127,38 @@ void testNoCrashes() { testCanvas((Canvas canvas) => canvas.skew(double.nan, double.nan)); testCanvas((Canvas canvas) => canvas.transform(Float64List(16))); testCanvas((Canvas canvas) => canvas.translate(double.nan, double.nan)); - testCanvas((Canvas canvas) => canvas.drawVertices( + testCanvas( + (Canvas canvas) => canvas.drawVertices( Vertices(VertexMode.triangles, [], indices: []), BlendMode.screen, - paint)); - testCanvas((Canvas canvas) => canvas.drawVertices( + paint, + ), + ); + testCanvas( + (Canvas canvas) => canvas.drawVertices( Vertices(VertexMode.triangles, [])..dispose(), BlendMode.screen, - paint)); + paint, + ), + ); // Regression test for https://github.com/flutter/flutter/issues/115143 - testCanvas((Canvas canvas) => canvas.drawPaint(Paint() - ..imageFilter = - const ColorFilter.mode(Color(0x00000000), BlendMode.xor))); + testCanvas( + (Canvas canvas) => canvas.drawPaint( + Paint()..imageFilter = const ColorFilter.mode(Color(0x00000000), BlendMode.xor), + ), + ); // Regression test for https://github.com/flutter/flutter/issues/120278 - testCanvas((Canvas canvas) => canvas.drawPaint(Paint() - ..imageFilter = ImageFilter.compose( - outer: ImageFilter.matrix(Matrix4.identity().storage), - inner: ImageFilter.blur()))); + testCanvas( + (Canvas canvas) => canvas.drawPaint( + Paint() + ..imageFilter = ImageFilter.compose( + outer: ImageFilter.matrix(Matrix4.identity().storage), + inner: ImageFilter.blur(), + ), + ), + ); }); } @@ -163,33 +178,40 @@ void main() async { testNoCrashes(); test('Simple .toImage', () async { - final Image image = await toImage((Canvas canvas) { - final Path circlePath = Path() - ..addOval( - Rect.fromCircle(center: const Offset(40.0, 40.0), radius: 20.0)); - final Paint paint = Paint() - ..isAntiAlias = false - ..style = PaintingStyle.fill; - canvas.drawPath(circlePath, paint); - }, 100, 100); + final Image image = await toImage( + (Canvas canvas) { + final Path circlePath = + Path()..addOval(Rect.fromCircle(center: const Offset(40.0, 40.0), radius: 20.0)); + final Paint paint = + Paint() + ..isAntiAlias = false + ..style = PaintingStyle.fill; + canvas.drawPath(circlePath, paint); + }, + 100, + 100, + ); expect(image.width, equals(100)); expect(image.height, equals(100)); await comparer.addGoldenImage(image, 'canvas_test_toImage.png'); }); Gradient makeGradient() { - return Gradient.linear( - Offset.zero, - const Offset(100, 100), - const [Color(0xFF4C4D52), Color(0xFF202124)], - ); + return Gradient.linear(Offset.zero, const Offset(100, 100), const [ + Color(0xFF4C4D52), + Color(0xFF202124), + ]); } test('Simple gradient, which is implicitly dithered', () async { - final Image image = await toImage((Canvas canvas) { - final Paint paint = Paint()..shader = makeGradient(); - canvas.drawPaint(paint); - }, 100, 100); + final Image image = await toImage( + (Canvas canvas) { + final Paint paint = Paint()..shader = makeGradient(); + canvas.drawPaint(paint); + }, + 100, + 100, + ); expect(image.width, equals(100)); expect(image.height, equals(100)); @@ -204,24 +226,56 @@ void main() async { final RSTransform transform = RSTransform(1, 0, 0, 0); const Color color = Color(0x00000000); final Paint paint = Paint(); - canvas.drawAtlas(image, [transform], [rect], - [color], BlendMode.src, rect, paint); - canvas.drawAtlas(image, [transform], [rect], - [color], BlendMode.src, null, paint); - canvas.drawAtlas(image, [transform], [rect], [], - null, rect, paint); canvas.drawAtlas( - image, [transform], [rect], null, null, rect, paint); - canvas.drawRawAtlas(image, Float32List(0), Float32List(0), Int32List(0), - BlendMode.src, rect, paint); - canvas.drawRawAtlas(image, Float32List(0), Float32List(0), Int32List(0), - BlendMode.src, null, paint); + image, + [transform], + [rect], + [color], + BlendMode.src, + rect, + paint, + ); + canvas.drawAtlas( + image, + [transform], + [rect], + [color], + BlendMode.src, + null, + paint, + ); + canvas.drawAtlas(image, [transform], [rect], [], null, rect, paint); + canvas.drawAtlas(image, [transform], [rect], null, null, rect, paint); + canvas.drawRawAtlas( + image, + Float32List(0), + Float32List(0), + Int32List(0), + BlendMode.src, + rect, + paint, + ); canvas.drawRawAtlas( - image, Float32List(0), Float32List(0), null, null, rect, paint); + image, + Float32List(0), + Float32List(0), + Int32List(0), + BlendMode.src, + null, + paint, + ); + canvas.drawRawAtlas(image, Float32List(0), Float32List(0), null, null, rect, paint); expect( - () => canvas.drawAtlas(image, [transform], [rect], - [color], null, rect, paint), + () => canvas.drawAtlas( + image, + [transform], + [rect], + [color], + null, + rect, + paint, + ), throwsA(isA()), ); }); @@ -234,57 +288,130 @@ void main() async { final RSTransform transform = RSTransform(1, 0, 0, 0); const Color color = Color(0x00000000); final Paint paint = Paint(); - canvas.drawAtlas(image, [transform], [rect], - [color], BlendMode.src, rect, paint); - canvas.drawAtlas(image, [transform, transform], - [rect, rect], [color, color], BlendMode.src, rect, paint); - canvas.drawAtlas(image, [transform], [rect], [], - null, rect, paint); canvas.drawAtlas( - image, [transform], [rect], null, null, rect, paint); - canvas.drawRawAtlas(image, Float32List(0), Float32List(0), Int32List(0), - BlendMode.src, rect, paint); - canvas.drawRawAtlas(image, Float32List(4), Float32List(4), Int32List(1), - BlendMode.src, rect, paint); + image, + [transform], + [rect], + [color], + BlendMode.src, + rect, + paint, + ); + canvas.drawAtlas( + image, + [transform, transform], + [rect, rect], + [color, color], + BlendMode.src, + rect, + paint, + ); + canvas.drawAtlas(image, [transform], [rect], [], null, rect, paint); + canvas.drawAtlas(image, [transform], [rect], null, null, rect, paint); + canvas.drawRawAtlas( + image, + Float32List(0), + Float32List(0), + Int32List(0), + BlendMode.src, + rect, + paint, + ); canvas.drawRawAtlas( - image, Float32List(4), Float32List(4), null, null, rect, paint); + image, + Float32List(4), + Float32List(4), + Int32List(1), + BlendMode.src, + rect, + paint, + ); + canvas.drawRawAtlas(image, Float32List(4), Float32List(4), null, null, rect, paint); expect( - () => canvas.drawAtlas(image, [transform], [], - [color], BlendMode.src, rect, paint), - throwsArgumentError); + () => canvas.drawAtlas( + image, + [transform], + [], + [color], + BlendMode.src, + rect, + paint, + ), + throwsArgumentError, + ); expect( - () => canvas.drawAtlas(image, [], [rect], - [color], BlendMode.src, rect, paint), - throwsArgumentError); + () => canvas.drawAtlas( + image, + [], + [rect], + [color], + BlendMode.src, + rect, + paint, + ), + throwsArgumentError, + ); expect( - () => canvas.drawAtlas(image, [transform], [rect], - [color, color], BlendMode.src, rect, paint), - throwsArgumentError); + () => canvas.drawAtlas( + image, + [transform], + [rect], + [color, color], + BlendMode.src, + rect, + paint, + ), + throwsArgumentError, + ); expect( - () => canvas.drawAtlas(image, [transform], - [rect, rect], [color], BlendMode.src, rect, paint), - throwsArgumentError); + () => canvas.drawAtlas( + image, + [transform], + [rect, rect], + [color], + BlendMode.src, + rect, + paint, + ), + throwsArgumentError, + ); expect( - () => canvas.drawAtlas(image, [transform, transform], - [rect], [color], BlendMode.src, rect, paint), - throwsArgumentError); + () => canvas.drawAtlas( + image, + [transform, transform], + [rect], + [color], + BlendMode.src, + rect, + paint, + ), + throwsArgumentError, + ); expect( - () => canvas.drawRawAtlas( - image, Float32List(3), Float32List(3), null, null, rect, paint), - throwsArgumentError); + () => canvas.drawRawAtlas(image, Float32List(3), Float32List(3), null, null, rect, paint), + throwsArgumentError, + ); expect( - () => canvas.drawRawAtlas( - image, Float32List(4), Float32List(0), null, null, rect, paint), - throwsArgumentError); + () => canvas.drawRawAtlas(image, Float32List(4), Float32List(0), null, null, rect, paint), + throwsArgumentError, + ); expect( - () => canvas.drawRawAtlas( - image, Float32List(0), Float32List(4), null, null, rect, paint), - throwsArgumentError); + () => canvas.drawRawAtlas(image, Float32List(0), Float32List(4), null, null, rect, paint), + throwsArgumentError, + ); expect( - () => canvas.drawRawAtlas(image, Float32List(4), Float32List(4), - Int32List(2), BlendMode.src, rect, paint), - throwsArgumentError); + () => canvas.drawRawAtlas( + image, + Float32List(4), + Float32List(4), + Int32List(2), + BlendMode.src, + rect, + paint, + ), + throwsArgumentError, + ); }); test('Canvas preserves perspective data in Matrix4', () async { @@ -300,135 +427,154 @@ void main() async { const double width3 = width / 3.0; const double width5 = width / 5.0; const double width10 = width / 10.0; - canvas.drawRect(const Rect.fromLTRB(-width3, -width3, width3, width3), - Paint()..color = green); - canvas.drawRect(const Rect.fromLTRB(-width5, -width5, -width10, width5), - Paint()..color = black); - canvas.drawRect(const Rect.fromLTRB(-width5, -width5, width5, -width10), - Paint()..color = black); + canvas.drawRect( + const Rect.fromLTRB(-width3, -width3, width3, width3), + Paint()..color = green, + ); + canvas.drawRect( + const Rect.fromLTRB(-width5, -width5, -width10, width5), + Paint()..color = black, + ); + canvas.drawRect( + const Rect.fromLTRB(-width5, -width5, width5, -width10), + Paint()..color = black, + ); } - final Image incrementalMatrixImage = await toImage((Canvas canvas) { - paint(canvas, (Canvas canvas) { - final Matrix4 matrix = Matrix4.identity(); - matrix.setEntry(3, 2, 0.001); - canvas.transform(matrix.storage); - matrix.setRotationX(rotateAroundX); - canvas.transform(matrix.storage); - matrix.setRotationY(rotateAroundY); - canvas.transform(matrix.storage); - }); - }, width, height); - final Image combinedMatrixImage = await toImage((Canvas canvas) { - paint(canvas, (Canvas canvas) { - final Matrix4 matrix = Matrix4.identity(); - matrix.setEntry(3, 2, 0.001); - matrix.rotateX(rotateAroundX); - matrix.rotateY(rotateAroundY); - canvas.transform(matrix.storage); - }); - }, width, height); + final Image incrementalMatrixImage = await toImage( + (Canvas canvas) { + paint(canvas, (Canvas canvas) { + final Matrix4 matrix = Matrix4.identity(); + matrix.setEntry(3, 2, 0.001); + canvas.transform(matrix.storage); + matrix.setRotationX(rotateAroundX); + canvas.transform(matrix.storage); + matrix.setRotationY(rotateAroundY); + canvas.transform(matrix.storage); + }); + }, + width, + height, + ); + final Image combinedMatrixImage = await toImage( + (Canvas canvas) { + paint(canvas, (Canvas canvas) { + final Matrix4 matrix = Matrix4.identity(); + matrix.setEntry(3, 2, 0.001); + matrix.rotateX(rotateAroundX); + matrix.rotateY(rotateAroundY); + canvas.transform(matrix.storage); + }); + }, + width, + height, + ); final bool areEqual = await comparer.fuzzyCompareImages( - incrementalMatrixImage, combinedMatrixImage); + incrementalMatrixImage, + combinedMatrixImage, + ); expect(areEqual, true); }); - test('Path effects from Paragraphs do not affect further rendering', - () async { - void drawText(Canvas canvas, String content, Offset offset, - {TextDecorationStyle style = TextDecorationStyle.solid}) { + test('Path effects from Paragraphs do not affect further rendering', () async { + void drawText( + Canvas canvas, + String content, + Offset offset, { + TextDecorationStyle style = TextDecorationStyle.solid, + }) { final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle()); - builder.pushStyle(TextStyle( - decoration: TextDecoration.underline, - decorationColor: const Color(0xFF0000FF), - fontFamily: 'Ahem', - fontSize: 10, - color: const Color(0xFF000000), - decorationStyle: style, - )); + builder.pushStyle( + TextStyle( + decoration: TextDecoration.underline, + decorationColor: const Color(0xFF0000FF), + fontFamily: 'Ahem', + fontSize: 10, + color: const Color(0xFF000000), + decorationStyle: style, + ), + ); builder.addText(content); final Paragraph paragraph = builder.build(); paragraph.layout(const ParagraphConstraints(width: 100)); canvas.drawParagraph(paragraph, offset); } - final Image image = await toImage((Canvas canvas) { - canvas.drawColor(const Color(0xFFFFFFFF), BlendMode.srcOver); - final Paint paint = Paint() - ..style = PaintingStyle.stroke - ..strokeWidth = 5; - drawText(canvas, 'Hello World', const Offset(20, 10)); - canvas.drawCircle( - const Offset(150, 25), 15, paint..color = const Color(0xFF00FF00)); - drawText(canvas, 'Regular text', const Offset(20, 60)); - canvas.drawCircle( - const Offset(150, 75), 15, paint..color = const Color(0xFFFFFF00)); - drawText(canvas, 'Dotted text', const Offset(20, 110), - style: TextDecorationStyle.dotted); - canvas.drawCircle( - const Offset(150, 125), 15, paint..color = const Color(0xFFFF0000)); - drawText(canvas, 'Dashed text', const Offset(20, 160), - style: TextDecorationStyle.dashed); - canvas.drawCircle( - const Offset(150, 175), 15, paint..color = const Color(0xFFFF0000)); - drawText(canvas, 'Wavy text', const Offset(20, 210), - style: TextDecorationStyle.wavy); - canvas.drawCircle( - const Offset(150, 225), 15, paint..color = const Color(0xFFFF0000)); - }, 200, 250); + final Image image = await toImage( + (Canvas canvas) { + canvas.drawColor(const Color(0xFFFFFFFF), BlendMode.srcOver); + final Paint paint = + Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 5; + drawText(canvas, 'Hello World', const Offset(20, 10)); + canvas.drawCircle(const Offset(150, 25), 15, paint..color = const Color(0xFF00FF00)); + drawText(canvas, 'Regular text', const Offset(20, 60)); + canvas.drawCircle(const Offset(150, 75), 15, paint..color = const Color(0xFFFFFF00)); + drawText(canvas, 'Dotted text', const Offset(20, 110), style: TextDecorationStyle.dotted); + canvas.drawCircle(const Offset(150, 125), 15, paint..color = const Color(0xFFFF0000)); + drawText(canvas, 'Dashed text', const Offset(20, 160), style: TextDecorationStyle.dashed); + canvas.drawCircle(const Offset(150, 175), 15, paint..color = const Color(0xFFFF0000)); + drawText(canvas, 'Wavy text', const Offset(20, 210), style: TextDecorationStyle.wavy); + canvas.drawCircle(const Offset(150, 225), 15, paint..color = const Color(0xFFFF0000)); + }, + 200, + 250, + ); expect(image.width, equals(200)); expect(image.height, equals(250)); - await comparer.addGoldenImage( - image, 'dotted_path_effect_mixed_with_stroked_geometry.png'); + await comparer.addGoldenImage(image, 'dotted_path_effect_mixed_with_stroked_geometry.png'); }); test('Gradients with matrices in Paragraphs render correctly', () async { - final Image image = await toImage((Canvas canvas) { - final Paint p = Paint(); - final Float64List transform = Float64List.fromList([ - 86.80000129342079, - 0.0, - 0.0, - 0.0, - 0.0, - 94.5, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, - 60.0, - 224.310302734375, - 0.0, - 1.0 - ]); - p.shader = Gradient.radial( + final Image image = await toImage( + (Canvas canvas) { + final Paint p = Paint(); + final Float64List transform = Float64List.fromList([ + 86.80000129342079, + 0.0, + 0.0, + 0.0, + 0.0, + 94.5, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 60.0, + 224.310302734375, + 0.0, + 1.0, + ]); + p.shader = Gradient.radial( const Offset(2.5, 0.33), 0.8, [ const Color(0xffff0000), const Color(0xff00ff00), const Color(0xff0000ff), - const Color(0xffff00ff) + const Color(0xffff00ff), ], [0.0, 0.3, 0.7, 0.9], TileMode.mirror, transform, - const Offset(2.55, 0.4)); - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle()); - builder.pushStyle(TextStyle( - foreground: p, - fontSize: 200, - )); - builder.addText('Woodstock!'); - final Paragraph paragraph = builder.build(); - paragraph.layout(const ParagraphConstraints(width: 1000)); - canvas.drawParagraph(paragraph, const Offset(10, 150)); - }, 600, 400); + const Offset(2.55, 0.4), + ); + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle()); + builder.pushStyle(TextStyle(foreground: p, fontSize: 200)); + builder.addText('Woodstock!'); + final Paragraph paragraph = builder.build(); + paragraph.layout(const ParagraphConstraints(width: 1000)); + canvas.drawParagraph(paragraph, const Offset(10, 150)); + }, + 600, + 400, + ); expect(image.width, equals(600)); expect(image.height, equals(400)); @@ -467,7 +613,8 @@ void main() async { expect( e.message, contains( - 'unable to create bitmap render target at specified size ${image.width}x${image.height}'), + 'unable to create bitmap render target at specified size ${image.width}x${image.height}', + ), ); break; } @@ -475,17 +622,10 @@ void main() async { await Future.delayed(const Duration(milliseconds: 1)); } expect(caughtException, true); + expect(() => canvas.drawImageRect(image, Rect.zero, Rect.zero, Paint()), throwsException); + expect(() => canvas.drawImageNine(image, Rect.zero, Rect.zero, Paint()), throwsException); expect( - () => canvas.drawImageRect(image, Rect.zero, Rect.zero, Paint()), - throwsException, - ); - expect( - () => canvas.drawImageNine(image, Rect.zero, Rect.zero, Paint()), - throwsException, - ); - expect( - () => canvas.drawAtlas( - image, [], [], null, null, null, Paint()), + () => canvas.drawAtlas(image, [], [], null, null, null, Paint()), throwsException, ); }); @@ -503,21 +643,11 @@ void main() async { recorder = PictureRecorder(); canvas = Canvas(recorder); + expect(() => canvas.drawImage(image, Offset.zero, Paint()), returnsNormally); + expect(() => canvas.drawImageRect(image, Rect.zero, Rect.zero, Paint()), returnsNormally); + expect(() => canvas.drawImageNine(image, Rect.zero, Rect.zero, Paint()), returnsNormally); expect( - () => canvas.drawImage(image, Offset.zero, Paint()), - returnsNormally, - ); - expect( - () => canvas.drawImageRect(image, Rect.zero, Rect.zero, Paint()), - returnsNormally, - ); - expect( - () => canvas.drawImageNine(image, Rect.zero, Rect.zero, Paint()), - returnsNormally, - ); - expect( - () => canvas.drawAtlas( - image, [], [], null, null, null, Paint()), + () => canvas.drawAtlas(image, [], [], null, null, null, Paint()), returnsNormally, ); }); @@ -577,43 +707,53 @@ void main() async { expect(data.buffer.asUint8List(), equals(dataSync.buffer.asUint8List())); }); - test('Canvas.drawParagraph throws when Paragraph.layout was not called', - () async { + test('Canvas.drawParagraph throws when Paragraph.layout was not called', () async { // Regression test for https://github.com/flutter/flutter/issues/97172 expect(() { - toImage((Canvas canvas) { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle()); - builder.addText('Woodstock!'); - final Paragraph woodstock = builder.build(); - canvas.drawParagraph(woodstock, const Offset(0, 50)); - }, 100, 100); + toImage( + (Canvas canvas) { + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle()); + builder.addText('Woodstock!'); + final Paragraph woodstock = builder.build(); + canvas.drawParagraph(woodstock, const Offset(0, 50)); + }, + 100, + 100, + ); }, throwsA(isA())); }); Future drawText(String text) { - return toImage((Canvas canvas) { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'RobotoSerif', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: 15.0, - )); - builder.pushStyle(TextStyle(color: const Color(0xFF0000FF))); - builder.addText(text); + return toImage( + (Canvas canvas) { + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle( + fontFamily: 'RobotoSerif', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: 15.0, + ), + ); + builder.pushStyle(TextStyle(color: const Color(0xFF0000FF))); + builder.addText(text); - final Paragraph paragraph = builder.build(); - paragraph.layout(const ParagraphConstraints(width: 20 * 5.0)); + final Paragraph paragraph = builder.build(); + paragraph.layout(const ParagraphConstraints(width: 20 * 5.0)); - canvas.drawParagraph(paragraph, Offset.zero); - }, 100, 100); + canvas.drawParagraph(paragraph, Offset.zero); + }, + 100, + 100, + ); } test('Canvas.drawParagraph renders tab as space instead of tofu', () async { // Skia renders a tofu if the font does not have a glyph for a character. // However, Flutter opts-in to a Skia feature to render tabs as a single space. // See: https://github.com/flutter/flutter/issues/79153 - final File file = File(path.join(_flutterBuildPath, 'flutter', - 'third_party', 'txt', 'assets', 'Roboto-Regular.ttf')); + final File file = File( + path.join(_flutterBuildPath, 'flutter', 'third_party', 'txt', 'assets', 'Roboto-Regular.ttf'), + ); final Uint8List fontData = await file.readAsBytes(); await loadFontFromList(fontData, fontFamily: 'RobotoSerif'); @@ -623,26 +763,24 @@ void main() async { final Image tofuImage = await drawText('>\b<'); // The tab's image should be identical to the space's image but not the tofu's image. - final bool tabToSpaceComparison = - await comparer.fuzzyCompareImages(tabImage, spaceImage); - final bool tabToTofuComparison = - await comparer.fuzzyCompareImages(tabImage, tofuImage); + final bool tabToSpaceComparison = await comparer.fuzzyCompareImages(tabImage, spaceImage); + final bool tabToTofuComparison = await comparer.fuzzyCompareImages(tabImage, tofuImage); expect(tabToSpaceComparison, isTrue); expect(tabToTofuComparison, isFalse); }); - test('drawRect, drawOval, and clipRect render with unsorted rectangles', - () async { + test('drawRect, drawOval, and clipRect render with unsorted rectangles', () async { final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); canvas.drawColor(const Color(0xFFE0E0E0), BlendMode.src); void draw(Rect rect, double x, double y, Color color) { - final Paint paint = Paint() - ..color = color - ..strokeWidth = 5.0; + final Paint paint = + Paint() + ..color = color + ..strokeWidth = 5.0; final Rect tallThin = Rect.fromLTRB( min(rect.left, rect.right) - 10, @@ -715,8 +853,7 @@ void main() async { draw(const Rect.fromLTRB(10, 10, 40, 40), 50, 50, const Color(0xFF2196F3)); draw(const Rect.fromLTRB(40, 10, 10, 40), 250, 50, const Color(0xFF4CAF50)); draw(const Rect.fromLTRB(10, 40, 40, 10), 50, 250, const Color(0xFF9C27B0)); - draw( - const Rect.fromLTRB(40, 40, 10, 10), 250, 250, const Color(0xFFFF9800)); + draw(const Rect.fromLTRB(40, 40, 10, 10), 250, 250, const Color(0xFFFF9800)); final Picture picture = recorder.endRecording(); final Image image = await picture.toImage(450, 450); @@ -766,10 +903,11 @@ void main() async { final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); canvas.skew(12, 14.5); - final Float64List matrix = (Matrix4.identity() - ..setEntry(0, 1, 12) - ..setEntry(1, 0, 14.5)) - .storage; + final Float64List matrix = + (Matrix4.identity() + ..setEntry(0, 1, 12) + ..setEntry(1, 0, 14.5)) + .storage; final Float64List curMatrix = canvas.getTransform(); expect(curMatrix, closeToTransform(matrix)); canvas.skew(10, 10); @@ -781,10 +919,11 @@ void main() async { test('Canvas.transform affects canvas.getTransform', () async { final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - final Float64List matrix = (Matrix4.identity() - ..translate(12.0, 14.5) - ..scale(12.0, 14.5)) - .storage; + final Float64List matrix = + (Matrix4.identity() + ..translate(12.0, 14.5) + ..scale(12.0, 14.5)) + .storage; canvas.transform(matrix); final Float64List curMatrix = canvas.getTransform(); expect(curMatrix, closeToTransform(matrix)); @@ -827,10 +966,8 @@ void main() async { canvas.save(); canvas.clipRect(const Rect.fromLTRB(0, 0, 15, 15)); // Both clip bounds have changed - expect( - canvas.getLocalClipBounds(), isNot(closeToRect(clipExpandedBounds))); - expect(canvas.getDestinationClipBounds(), - isNot(closeToRect(clipExpandedBounds))); + expect(canvas.getLocalClipBounds(), isNot(closeToRect(clipExpandedBounds))); + expect(canvas.getDestinationClipBounds(), isNot(closeToRect(clipExpandedBounds))); // Previous return values have not changed expect(initialLocalBounds, closeToRect(clipExpandedBounds)); expect(initialDestinationBounds, closeToRect(clipExpandedBounds)); @@ -850,8 +987,7 @@ void main() async { ); expect(canvas.getLocalClipBounds(), closeToRect(scaledExpandedBounds)); // Destination bounds are unaffected by transform - expect( - canvas.getDestinationClipBounds(), closeToRect(clipExpandedBounds)); + expect(canvas.getDestinationClipBounds(), closeToRect(clipExpandedBounds)); canvas.restore(); // save/restore returned the values to their original values @@ -901,8 +1037,7 @@ void main() async { final Canvas canvas = Canvas(recorder); const Rect clipBounds = Rect.fromLTRB(10.2, 11.3, 20.4, 25.7); const Rect clipExpandedBounds = Rect.fromLTRB(10, 11, 21, 26); - final RRect clip = - RRect.fromRectAndRadius(clipBounds, const Radius.circular(3)); + final RRect clip = RRect.fromRectAndRadius(clipBounds, const Radius.circular(3)); canvas.clipRRect(clip); // Save initial return values for testing restored values @@ -915,8 +1050,7 @@ void main() async { canvas.clipRect(const Rect.fromLTRB(0, 0, 15, 15)); // Both clip bounds have changed expect(canvas.getLocalClipBounds(), isNot(closeToRect(clipExpandedBounds))); - expect(canvas.getDestinationClipBounds(), - isNot(closeToRect(clipExpandedBounds))); + expect(canvas.getDestinationClipBounds(), isNot(closeToRect(clipExpandedBounds))); // Previous return values have not changed expect(initialLocalBounds, closeToRect(clipExpandedBounds)); expect(initialDestinationBounds, closeToRect(clipExpandedBounds)); @@ -943,8 +1077,7 @@ void main() async { final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); const Rect clipBounds = Rect.fromLTRB(10.2, 11.3, 20.4, 25.7); - final RRect clip = - RRect.fromRectAndRadius(clipBounds, const Radius.circular(3)); + final RRect clip = RRect.fromRectAndRadius(clipBounds, const Radius.circular(3)); canvas.clipRRect(clip, doAntiAlias: false); // Save initial return values for testing restored values @@ -985,10 +1118,8 @@ void main() async { final Canvas canvas = Canvas(recorder); const Rect clipBounds1 = Rect.fromLTRB(0.0, 0.0, 10.0, 10.0); const Rect clipBounds2 = Rect.fromLTRB(10.0, 10.0, 20.0, 20.0); - final RRect clip1 = - RRect.fromRectAndRadius(clipBounds1, const Radius.circular(3)); - final RRect clip2 = - RRect.fromRectAndRadius(clipBounds2, const Radius.circular(3)); + final RRect clip1 = RRect.fromRectAndRadius(clipBounds1, const Radius.circular(3)); + final RRect clip2 = RRect.fromRectAndRadius(clipBounds2, const Radius.circular(3)); canvas.save(); canvas.clipRRect(clip1, doAntiAlias: false); @@ -1010,9 +1141,10 @@ void main() async { final Canvas canvas = Canvas(recorder); const Rect clipBounds = Rect.fromLTRB(10.2, 11.3, 20.4, 25.7); const Rect clipExpandedBounds = Rect.fromLTRB(10, 11, 21, 26); - final Path clip = Path() - ..addRect(clipBounds) - ..addOval(clipBounds); + final Path clip = + Path() + ..addRect(clipBounds) + ..addOval(clipBounds); canvas.clipPath(clip); // Save initial return values for testing restored values @@ -1025,8 +1157,7 @@ void main() async { canvas.clipRect(const Rect.fromLTRB(0, 0, 15, 15)); // Both clip bounds have changed expect(canvas.getLocalClipBounds(), isNot(closeToRect(clipExpandedBounds))); - expect(canvas.getDestinationClipBounds(), - isNot(closeToRect(clipExpandedBounds))); + expect(canvas.getDestinationClipBounds(), isNot(closeToRect(clipExpandedBounds))); // Previous return values have not changed expect(initialLocalBounds, closeToRect(clipExpandedBounds)); expect(initialDestinationBounds, closeToRect(clipExpandedBounds)); @@ -1053,9 +1184,10 @@ void main() async { final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); const Rect clipBounds = Rect.fromLTRB(10.2, 11.3, 20.4, 25.7); - final Path clip = Path() - ..addRect(clipBounds) - ..addOval(clipBounds); + final Path clip = + Path() + ..addRect(clipBounds) + ..addOval(clipBounds); canvas.clipPath(clip, doAntiAlias: false); // Save initial return values for testing restored values @@ -1096,12 +1228,14 @@ void main() async { final Canvas canvas = Canvas(recorder); const Rect clipBounds1 = Rect.fromLTRB(0.0, 0.0, 10.0, 10.0); const Rect clipBounds2 = Rect.fromLTRB(10.0, 10.0, 20.0, 20.0); - final Path clip1 = Path() - ..addRect(clipBounds1) - ..addOval(clipBounds1); - final Path clip2 = Path() - ..addRect(clipBounds2) - ..addOval(clipBounds2); + final Path clip1 = + Path() + ..addRect(clipBounds1) + ..addOval(clipBounds1); + final Path clip2 = + Path() + ..addRect(clipBounds2) + ..addOval(clipBounds2); canvas.save(); canvas.clipPath(clip1, doAntiAlias: false); @@ -1130,8 +1264,11 @@ void main() async { expect(initialLocalBounds, closeToRect(clipBounds)); expect(initialDestinationBounds, closeToRect(clipBounds)); - canvas.clipRect(const Rect.fromLTRB(0, 0, 15, 15), - clipOp: ClipOp.difference, doAntiAlias: false); + canvas.clipRect( + const Rect.fromLTRB(0, 0, 15, 15), + clipOp: ClipOp.difference, + doAntiAlias: false, + ); expect(canvas.getLocalClipBounds(), initialLocalBounds); expect(canvas.getDestinationClipBounds(), initialDestinationBounds); }); @@ -1164,9 +1301,7 @@ void main() async { expect(canvas.getSaveCount(), equals(1)); }); - test( - 'RestoreToCount count greater than current [getSaveCount], nothing would happend', - () async { + test('RestoreToCount count greater than current [getSaveCount], nothing would happend', () async { final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); canvas.save(); @@ -1180,26 +1315,28 @@ void main() async { }); test('TextDecoration renders non-solid lines', () async { - final File file = File(path.join(_flutterBuildPath, 'flutter', - 'third_party', 'txt', 'assets', 'Roboto-Regular.ttf')); + final File file = File( + path.join(_flutterBuildPath, 'flutter', 'third_party', 'txt', 'assets', 'Roboto-Regular.ttf'), + ); final Uint8List fontData = await file.readAsBytes(); await loadFontFromList(fontData, fontFamily: 'RobotoSlab'); final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - for (final (int index, TextDecorationStyle style) - in TextDecorationStyle.values.indexed) { + for (final (int index, TextDecorationStyle style) in TextDecorationStyle.values.indexed) { final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle()); - builder.pushStyle(TextStyle( - decoration: TextDecoration.underline, - decorationStyle: style, - decorationThickness: 1.0, - decorationColor: const Color(0xFFFF0000), - fontFamily: 'RobotoSlab', - fontSize: 24.0, - foreground: Paint()..color = const Color(0xFF0000FF), - )); + builder.pushStyle( + TextStyle( + decoration: TextDecoration.underline, + decorationStyle: style, + decorationThickness: 1.0, + decorationColor: const Color(0xFFFF0000), + fontFamily: 'RobotoSlab', + fontSize: 24.0, + foreground: Paint()..color = const Color(0xFF0000FF), + ), + ); builder.addText(style.name); final Paragraph paragraph = builder.build(); @@ -1215,17 +1352,18 @@ void main() async { }); test('Paint, when copied, has equivalent fields', () { - final Paint paint = Paint() - ..color = const Color(0xFF0000FF) - ..strokeWidth = 10.0 - ..strokeCap = StrokeCap.round - ..strokeJoin = StrokeJoin.round - ..style = PaintingStyle.stroke - ..blendMode = BlendMode.srcOver - ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 10.0) - ..filterQuality = FilterQuality.high - ..colorFilter = const ColorFilter.mode(Color(0xFF00FF00), BlendMode.color) - ..imageFilter = ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0); + final Paint paint = + Paint() + ..color = const Color(0xFF0000FF) + ..strokeWidth = 10.0 + ..strokeCap = StrokeCap.round + ..strokeJoin = StrokeJoin.round + ..style = PaintingStyle.stroke + ..blendMode = BlendMode.srcOver + ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 10.0) + ..filterQuality = FilterQuality.high + ..colorFilter = const ColorFilter.mode(Color(0xFF00FF00), BlendMode.color) + ..imageFilter = ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0); final Paint paintCopy = Paint.from(paint); expect(paintCopy.color, equals(const Color(0xFF0000FF))); @@ -1234,27 +1372,28 @@ void main() async { expect(paintCopy.strokeJoin, equals(StrokeJoin.round)); expect(paintCopy.style, equals(PaintingStyle.stroke)); expect(paintCopy.blendMode, equals(BlendMode.srcOver)); - expect(paintCopy.maskFilter, - equals(const MaskFilter.blur(BlurStyle.normal, 10.0))); + expect(paintCopy.maskFilter, equals(const MaskFilter.blur(BlurStyle.normal, 10.0))); expect(paintCopy.filterQuality, equals(FilterQuality.high)); - expect(paintCopy.colorFilter, - equals(const ColorFilter.mode(Color(0xFF00FF00), BlendMode.color))); - expect(paintCopy.imageFilter, - equals(ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0))); + expect( + paintCopy.colorFilter, + equals(const ColorFilter.mode(Color(0xFF00FF00), BlendMode.color)), + ); + expect(paintCopy.imageFilter, equals(ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0))); }); test('Paint, when copied, does not mutate the original instance', () { - final Paint paint = Paint() - ..color = const Color(0xFF0000FF) - ..strokeWidth = 10.0 - ..strokeCap = StrokeCap.round - ..strokeJoin = StrokeJoin.round - ..style = PaintingStyle.stroke - ..blendMode = BlendMode.srcOver - ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 10.0) - ..filterQuality = FilterQuality.high - ..colorFilter = const ColorFilter.mode(Color(0xFF00FF00), BlendMode.color) - ..imageFilter = ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0); + final Paint paint = + Paint() + ..color = const Color(0xFF0000FF) + ..strokeWidth = 10.0 + ..strokeCap = StrokeCap.round + ..strokeJoin = StrokeJoin.round + ..style = PaintingStyle.stroke + ..blendMode = BlendMode.srcOver + ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 10.0) + ..filterQuality = FilterQuality.high + ..colorFilter = const ColorFilter.mode(Color(0xFF00FF00), BlendMode.color) + ..imageFilter = ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0); // Make a copy, and change every field of the copy. Paint.from(paint) @@ -1266,8 +1405,7 @@ void main() async { ..blendMode = BlendMode.srcIn ..maskFilter = const MaskFilter.blur(BlurStyle.solid, 20.0) ..filterQuality = FilterQuality.none - ..colorFilter = - const ColorFilter.mode(Color(0xFFFF0000), BlendMode.modulate) + ..colorFilter = const ColorFilter.mode(Color(0xFFFF0000), BlendMode.modulate) ..imageFilter = ImageFilter.blur(sigmaX: 20.0, sigmaY: 20.0); // The original paint should not have changed. @@ -1277,28 +1415,25 @@ void main() async { expect(paint.strokeJoin, equals(StrokeJoin.round)); expect(paint.style, equals(PaintingStyle.stroke)); expect(paint.blendMode, equals(BlendMode.srcOver)); - expect(paint.maskFilter, - equals(const MaskFilter.blur(BlurStyle.normal, 10.0))); + expect(paint.maskFilter, equals(const MaskFilter.blur(BlurStyle.normal, 10.0))); expect(paint.filterQuality, equals(FilterQuality.high)); - expect(paint.colorFilter, - equals(const ColorFilter.mode(Color(0xFF00FF00), BlendMode.color))); - expect(paint.imageFilter, - equals(ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0))); + expect(paint.colorFilter, equals(const ColorFilter.mode(Color(0xFF00FF00), BlendMode.color))); + expect(paint.imageFilter, equals(ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0))); }); - test('Paint, when copied, the original changing does not mutate the copy', - () { - final Paint paint = Paint() - ..color = const Color(0xFF0000FF) - ..strokeWidth = 10.0 - ..strokeCap = StrokeCap.round - ..strokeJoin = StrokeJoin.round - ..style = PaintingStyle.stroke - ..blendMode = BlendMode.srcOver - ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 10.0) - ..filterQuality = FilterQuality.high - ..colorFilter = const ColorFilter.mode(Color(0xFF00FF00), BlendMode.color) - ..imageFilter = ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0); + test('Paint, when copied, the original changing does not mutate the copy', () { + final Paint paint = + Paint() + ..color = const Color(0xFF0000FF) + ..strokeWidth = 10.0 + ..strokeCap = StrokeCap.round + ..strokeJoin = StrokeJoin.round + ..style = PaintingStyle.stroke + ..blendMode = BlendMode.srcOver + ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 10.0) + ..filterQuality = FilterQuality.high + ..colorFilter = const ColorFilter.mode(Color(0xFF00FF00), BlendMode.color) + ..imageFilter = ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0); // Make a copy, and change every field of the original. final Paint paintCopy = Paint.from(paint); @@ -1311,8 +1446,7 @@ void main() async { ..blendMode = BlendMode.srcIn ..maskFilter = const MaskFilter.blur(BlurStyle.solid, 20.0) ..filterQuality = FilterQuality.none - ..colorFilter = - const ColorFilter.mode(Color(0xFFFF0000), BlendMode.modulate) + ..colorFilter = const ColorFilter.mode(Color(0xFFFF0000), BlendMode.modulate) ..imageFilter = ImageFilter.blur(sigmaX: 20.0, sigmaY: 20.0); // The copy should not have changed. @@ -1322,17 +1456,16 @@ void main() async { expect(paintCopy.strokeJoin, equals(StrokeJoin.round)); expect(paintCopy.style, equals(PaintingStyle.stroke)); expect(paintCopy.blendMode, equals(BlendMode.srcOver)); - expect(paintCopy.maskFilter, - equals(const MaskFilter.blur(BlurStyle.normal, 10.0))); + expect(paintCopy.maskFilter, equals(const MaskFilter.blur(BlurStyle.normal, 10.0))); expect(paintCopy.filterQuality, equals(FilterQuality.high)); - expect(paintCopy.colorFilter, - equals(const ColorFilter.mode(Color(0xFF00FF00), BlendMode.color))); - expect(paintCopy.imageFilter, - equals(ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0))); + expect( + paintCopy.colorFilter, + equals(const ColorFilter.mode(Color(0xFF00FF00), BlendMode.color)), + ); + expect(paintCopy.imageFilter, equals(ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0))); }); - test('DrawAtlas correctly copies color values into display list format', - () async { + test('DrawAtlas correctly copies color values into display list format', () async { final Image testImage = await createTestImage(); final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); @@ -1365,8 +1498,7 @@ void main() async { expect(rgba, 0xFF0000FF); }); - test('DrawAtlas with no colors does not crash', - () async { + test('DrawAtlas with no colors does not crash', () async { final Image testImage = await createTestImage(); final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); @@ -1407,14 +1539,22 @@ void main() async { final double centerY = height * 0.5; final double bottom = height.toDouble(); - canvas.drawRect(Rect.fromLTRB(left, top, centerX, centerY), - Paint()..color = const Color.fromARGB(255, 0, 255, 0)); - canvas.drawRect(Rect.fromLTRB(centerX, top, right, centerY), - Paint()..color = const Color.fromARGB(255, 255, 255, 0)); - canvas.drawRect(Rect.fromLTRB(left, centerY, centerX, bottom), - Paint()..color = const Color.fromARGB(255, 0, 0, 255)); - canvas.drawRect(Rect.fromLTRB(centerX, centerY, right, bottom), - Paint()..color = const Color.fromARGB(255, 255, 0, 0)); + canvas.drawRect( + Rect.fromLTRB(left, top, centerX, centerY), + Paint()..color = const Color.fromARGB(255, 0, 255, 0), + ); + canvas.drawRect( + Rect.fromLTRB(centerX, top, right, centerY), + Paint()..color = const Color.fromARGB(255, 255, 255, 0), + ); + canvas.drawRect( + Rect.fromLTRB(left, centerY, centerX, bottom), + Paint()..color = const Color.fromARGB(255, 0, 0, 255), + ); + canvas.drawRect( + Rect.fromLTRB(centerX, centerY, right, bottom), + Paint()..color = const Color.fromARGB(255, 255, 0, 0), + ); final picture = recorder.endRecording(); return picture.toImageSync(width, height); @@ -1432,25 +1572,21 @@ void main() async { final gradient = Gradient.linear( zone.topLeft, zone.bottomRight, - [ - const Color.fromARGB(255, 0, 255, 0), - const Color.fromARGB(255, 0, 0, 255), - ], + [const Color.fromARGB(255, 0, 255, 0), const Color.fromARGB(255, 0, 0, 255)], [0, 1], ); final filter = ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0, tileMode: tileMode); final Paint white = Paint()..color = const Color.fromARGB(255, 255, 255, 255); final Paint grey = Paint()..color = const Color.fromARGB(255, 127, 127, 127); final Paint unblurredFill = Paint()..shader = gradient; - final Paint blurredFill = Paint.from(unblurredFill) - ..imageFilter = filter; - final Paint unblurredStroke = Paint.from(unblurredFill) - ..style = PaintingStyle.stroke - ..strokeCap = StrokeCap.round - ..strokeJoin = StrokeJoin.round - ..strokeWidth = 10; - final Paint blurredStroke = Paint.from(unblurredStroke) - ..imageFilter = filter; + final Paint blurredFill = Paint.from(unblurredFill)..imageFilter = filter; + final Paint unblurredStroke = + Paint.from(unblurredFill) + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round + ..strokeJoin = StrokeJoin.round + ..strokeWidth = 10; + final Paint blurredStroke = Paint.from(unblurredStroke)..imageFilter = filter; final Image image = makeCheckerBoard(20, 20); const Rect imageBounds = Rect.fromLTRB(0, 0, 20, 20); const Rect imageCenter = Rect.fromLTRB(5, 5, 9, 9); @@ -1467,7 +1603,7 @@ void main() async { ]; final vertices = Vertices( VertexMode.triangles, - [ + [ zone.topLeft, zone.bottomRight, zone.topRight, @@ -1537,8 +1673,12 @@ void main() async { const double pad = 10; final double offset = arena.width + pad; const int columns = 5; - final Rect pairArena = Rect.fromLTRB(arena.left - 3, arena.top - 3, - arena.right + 3, arena.bottom + offset + 3); + final Rect pairArena = Rect.fromLTRB( + arena.left - 3, + arena.top - 3, + arena.right + 3, + arena.bottom + offset + 3, + ); final List renderers = [ (canvas, fill, stroke) { @@ -1552,23 +1692,27 @@ void main() async { (canvas, fill, stroke) => canvas.drawOval(ovalZone, fill), (canvas, fill, stroke) => canvas.drawCircle(zone.center, zone.width * 0.5, fill), (canvas, fill, stroke) => canvas.drawRRect(RRect.fromRectXY(zone, 4.0, 4.0), fill), - (canvas, fill, stroke) => canvas.drawDRRect(RRect.fromRectXY(zone, 4.0, 4.0), - RRect.fromRectXY(zone.deflate(4), 4.0, 4.0), - fill), + (canvas, fill, stroke) => canvas.drawDRRect( + RRect.fromRectXY(zone, 4.0, 4.0), + RRect.fromRectXY(zone.deflate(4), 4.0, 4.0), + fill, + ), (canvas, fill, stroke) => canvas.drawArc(zone, pi / 4, pi * 3 / 2, true, fill), - (canvas, fill, stroke) => canvas.drawPath(Path() - ..moveTo(zone.left, zone.top) - ..lineTo(zone.right, zone.top) - ..lineTo(zone.left, zone.bottom) - ..lineTo(zone.right, zone.bottom), - stroke), + (canvas, fill, stroke) => canvas.drawPath( + Path() + ..moveTo(zone.left, zone.top) + ..lineTo(zone.right, zone.top) + ..lineTo(zone.left, zone.bottom) + ..lineTo(zone.right, zone.bottom), + stroke, + ), (canvas, fill, stroke) => canvas.drawImage(image, zone.topLeft, fill), (canvas, fill, stroke) => canvas.drawImageRect(image, imageBounds, zone.inflate(2), fill), (canvas, fill, stroke) => canvas.drawImageNine(image, imageCenter, zone.inflate(2), fill), (canvas, fill, stroke) => canvas.drawPoints(PointMode.points, points, stroke), (canvas, fill, stroke) => canvas.drawVertices(vertices, BlendMode.dstOver, fill), - (canvas, fill, stroke) => canvas.drawAtlas(image, atlasXforms, atlasRects, - null, null, null, fill), + (canvas, fill, stroke) => + canvas.drawAtlas(image, atlasXforms, atlasRects, null, null, null, fill), ]; canvas.save(); @@ -1596,13 +1740,15 @@ void main() async { canvas.restore(); final picture = recorder.endRecording(); - return picture.toImageSync((offset * columns + pad).round(), - (offset * rows + pad).round()); + return picture.toImageSync((offset * columns + pad).round(), (offset * rows + pad).round()); } test('Rendering ops with ImageFilter blur with default tile mode', () async { final image = renderingOpsWithTileMode(null); - await comparer.addGoldenImage(image, 'canvas_test_blurred_rendering_with_default_tile_mode.png'); + await comparer.addGoldenImage( + image, + 'canvas_test_blurred_rendering_with_default_tile_mode.png', + ); }); test('Rendering ops with ImageFilter blur with clamp tile mode', () async { @@ -1617,7 +1763,10 @@ void main() async { test('Rendering ops with ImageFilter blur with repeated tile mode', () async { final image = renderingOpsWithTileMode(TileMode.repeated); - await comparer.addGoldenImage(image, 'canvas_test_blurred_rendering_with_repeated_tile_mode.png'); + await comparer.addGoldenImage( + image, + 'canvas_test_blurred_rendering_with_repeated_tile_mode.png', + ); }); test('Rendering ops with ImageFilter blur with decal tile mode', () async { @@ -1657,8 +1806,7 @@ final class _CloseToRectMatcher extends Matcher { } } -Matcher closeToTransform(Float64List expected) => - _CloseToTransformMatcher(expected); +Matcher closeToTransform(Float64List expected) => _CloseToTransformMatcher(expected); final class _CloseToTransformMatcher extends Matcher { _CloseToTransformMatcher(this._expected); diff --git a/testing/dart/channel_buffers_test.dart b/testing/dart/channel_buffers_test.dart index 2b6a17c34b6f1..74493600c29e6 100644 --- a/testing/dart/channel_buffers_test.dart +++ b/testing/dart/channel_buffers_test.dart @@ -30,8 +30,12 @@ void main() { void callback(ByteData? responseData) { called = true; } + buffers.push(channel, data, callback); - await buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) async { + await buffers.drain(channel, ( + ByteData? drainedData, + ui.PlatformMessageResponseCallback drainedCallback, + ) async { expect(drainedData, equals(data)); expect(called, isFalse); drainedCallback(drainedData); @@ -47,10 +51,15 @@ void main() { buffers.push(channel, data, callback); final List log = []; final Completer completer = Completer(); - scheduleMicrotask(() { log.add('before drain, microtask'); }); + scheduleMicrotask(() { + log.add('before drain, microtask'); + }); log.add('before drain'); - buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) async { + buffers.drain(channel, ( + ByteData? drainedData, + ui.PlatformMessageResponseCallback drainedCallback, + ) async { log.add('callback'); completer.complete(); }); @@ -62,20 +71,22 @@ void main() { 'callback', 'after drain, before await', 'before drain, microtask', - 'after await' + 'after await', ]); }); test('push drain zero', () async { const String channel = 'foo'; final ByteData data = _makeByteData('bar'); - final - ui.ChannelBuffers buffers = ui.ChannelBuffers(); + final ui.ChannelBuffers buffers = ui.ChannelBuffers(); void callback(ByteData? responseData) {} _resize(buffers, channel, 0); buffers.push(channel, data, callback); bool didCall = false; - await buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) async { + await buffers.drain(channel, ( + ByteData? drainedData, + ui.PlatformMessageResponseCallback drainedCallback, + ) async { didCall = true; }); expect(didCall, equals(false)); @@ -85,7 +96,10 @@ void main() { const String channel = 'foo'; final ui.ChannelBuffers buffers = ui.ChannelBuffers(); bool didCall = false; - await buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) async { + await buffers.drain(channel, ( + ByteData? drainedData, + ui.PlatformMessageResponseCallback drainedCallback, + ) async { didCall = true; }); expect(didCall, equals(false)); @@ -105,7 +119,10 @@ void main() { buffers.push(channel, three, callback); buffers.push(channel, four, callback); int counter = 0; - await buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) async { + await buffers.drain(channel, ( + ByteData? drainedData, + ui.PlatformMessageResponseCallback drainedCallback, + ) async { switch (counter) { case 0: expect(drainedData, equals(two)); @@ -130,7 +147,10 @@ void main() { buffers.push(channel, two, callback); _resize(buffers, channel, 1); int counter = 0; - await buffers.drain(channel, (ByteData? drainedData, ui.PlatformMessageResponseCallback drainedCallback) async { + await buffers.drain(channel, ( + ByteData? drainedData, + ui.PlatformMessageResponseCallback drainedCallback, + ) async { switch (counter) { case 0: expect(drainedData, equals(two)); @@ -150,9 +170,11 @@ void main() { expect(responseData, isNull); didCallCallback = true; } + void twoCallback(ByteData? responseData) { fail('wrong callback called'); } + _resize(buffers, channel, 100); buffers.push(channel, one, oneCallback); buffers.push(channel, two, twoCallback); @@ -171,9 +193,11 @@ void main() { expect(responseData, isNull); didCallCallback = true; } + void twoCallback(ByteData? responseData) { fail('wrong callback called'); } + _resize(buffers, channel, 1); buffers.push(channel, one, oneCallback); buffers.push(channel, two, twoCallback); @@ -182,14 +206,12 @@ void main() { test('handle garbage', () async { final ui.ChannelBuffers buffers = ui.ChannelBuffers(); - expect(() => buffers.handleMessage(_makeByteData('asdfasdf')), - throwsException); + expect(() => buffers.handleMessage(_makeByteData('asdfasdf')), throwsException); }); test('handle resize garbage', () async { final ui.ChannelBuffers buffers = ui.ChannelBuffers(); - expect(() => buffers.handleMessage(_makeByteData('resize\rfoo\rbar')), - throwsException); + expect(() => buffers.handleMessage(_makeByteData('resize\rfoo\rbar')), throwsException); }); test('ChannelBuffers.setListener', () async { @@ -202,9 +224,9 @@ void main() { final ByteData five = _makeByteData('five'); final ByteData six = _makeByteData('six'); final ByteData seven = _makeByteData('seven'); - buffers.push('a', one, (ByteData? data) { }); - buffers.push('b', two, (ByteData? data) { }); - buffers.push('a', three, (ByteData? data) { }); + buffers.push('a', one, (ByteData? data) {}); + buffers.push('b', two, (ByteData? data) {}); + buffers.push('a', three, (ByteData? data) {}); log.add('top'); buffers.setListener('a', (ByteData? data, ui.PlatformMessageResponseCallback callback) { expect(data, isNotNull); @@ -228,14 +250,14 @@ void main() { await null; // first microtask after setting listener drains the first message await null; // second microtask ends the draining. log.add('-6'); - buffers.push('b', four, (ByteData? data) { }); - buffers.push('a', five, (ByteData? data) { }); + buffers.push('b', four, (ByteData? data) {}); + buffers.push('a', five, (ByteData? data) {}); log.add('-7'); await null; log.add('-8'); buffers.clearListener('a'); - buffers.push('a', six, (ByteData? data) { }); - buffers.push('b', seven, (ByteData? data) { }); + buffers.push('a', six, (ByteData? data) {}); + buffers.push('b', seven, (ByteData? data) {}); await null; log.add('-9'); expect(log, [ @@ -265,9 +287,9 @@ void main() { final ByteData three = _makeByteData('three'); final ByteData four = _makeByteData('four'); buffers.handleMessage(_makeByteData('resize\ra\r10')); - buffers.push('a', one, (ByteData? data) { }); - buffers.push('a', two, (ByteData? data) { }); - buffers.push('a', three, (ByteData? data) { }); + buffers.push('a', one, (ByteData? data) {}); + buffers.push('a', two, (ByteData? data) {}); + buffers.push('a', three, (ByteData? data) {}); log.add('-1'); buffers.setListener('a', (ByteData? data, ui.PlatformMessageResponseCallback callback) { expect(data, isNotNull); @@ -284,7 +306,7 @@ void main() { }); log.add('-4'); await null; - buffers.push('a', four, (ByteData? data) { }); + buffers.push('a', four, (ByteData? data) {}); log.add('-5'); await null; log.add('-6'); @@ -312,7 +334,52 @@ void main() { // Created as follows: // print(StandardMethodCodec().encodeMethodCall(MethodCall('resize', ['abcdef', 12345])).buffer.asUint8List()); // ...with three 0xFF bytes on either side to ensure the method works with an offset on the underlying buffer. - buffers.handleMessage(ByteData.sublistView(Uint8List.fromList([255, 255, 255, 7, 6, 114, 101, 115, 105, 122, 101, 12, 2, 7, 6, 97, 98, 99, 100, 101, 102, 3, 57, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255]), 3, 27)); + buffers.handleMessage( + ByteData.sublistView( + Uint8List.fromList([ + 255, + 255, + 255, + 7, + 6, + 114, + 101, + 115, + 105, + 122, + 101, + 12, + 2, + 7, + 6, + 97, + 98, + 99, + 100, + 101, + 102, + 3, + 57, + 48, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 255, + 255, + 255, + ]), + 3, + 27, + ), + ); expect(log, const ['resize abcdef 12345']); }); @@ -322,7 +389,52 @@ void main() { // Created as follows: // print(StandardMethodCodec().encodeMethodCall(MethodCall('overflow', ['abcdef', false])).buffer.asUint8List()); // ...with three 0xFF bytes on either side to ensure the method works with an offset on the underlying buffer. - buffers.handleMessage(ByteData.sublistView(Uint8List.fromList([255, 255, 255, 7, 8, 111, 118, 101, 114, 102, 108, 111, 119, 12, 2, 7, 6, 97, 98, 99, 100, 101, 102, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255]), 3, 24)); + buffers.handleMessage( + ByteData.sublistView( + Uint8List.fromList([ + 255, + 255, + 255, + 7, + 8, + 111, + 118, + 101, + 114, + 102, + 108, + 111, + 119, + 12, + 2, + 7, + 6, + 97, + 98, + 99, + 100, + 101, + 102, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 255, + 255, + 255, + ]), + 3, + 24, + ), + ); expect(log, const ['allowOverflow abcdef false']); }); @@ -359,13 +471,28 @@ void main() { final ui.ChannelBuffers buffers = ui.ChannelBuffers(); expect( - () => buffers.push(channel, blabla, (ByteData? data) { }), - throwsA(isA().having((AssertionError e) => e.toString(), 'toString', contains('U+0000 NULL'))), + () => buffers.push(channel, blabla, (ByteData? data) {}), + throwsA( + isA().having( + (AssertionError e) => e.toString(), + 'toString', + contains('U+0000 NULL'), + ), + ), ); expect( - () => buffers.setListener(channel, (ByteData? data, ui.PlatformMessageResponseCallback callback) { }), - throwsA(isA().having((AssertionError e) => e.toString(), 'toString', contains('U+0000 NULL'))), + () => buffers.setListener( + channel, + (ByteData? data, ui.PlatformMessageResponseCallback callback) {}, + ), + throwsA( + isA().having( + (AssertionError e) => e.toString(), + 'toString', + contains('U+0000 NULL'), + ), + ), ); }); } diff --git a/testing/dart/codec_test.dart b/testing/dart/codec_test.dart index 7d0a2a23d2758..7c0b1325a7e88 100644 --- a/testing/dart/codec_test.dart +++ b/testing/dart/codec_test.dart @@ -12,7 +12,6 @@ import 'package:test/test.dart'; import 'impeller_enabled.dart'; void main() { - test('Animation metadata', () async { Uint8List data = await _getSkiaResource('alphabetAnim.gif').readAsBytes(); ui.Codec codec = await ui.instantiateImageCodec(data); @@ -65,13 +64,16 @@ void main() { frameInfo.image.height, ]); } - expect(decodedFrameInfos, equals(>[ - [200, 640, 479], - [200, 640, 479], - [200, 640, 479], - [200, 640, 479], - [200, 640, 479], - ])); + expect( + decodedFrameInfos, + equals(>[ + [200, 640, 479], + [200, 640, 479], + [200, 640, 479], + [200, 640, 479], + [200, 640, 479], + ]), + ); }); test('non animated image', () async { @@ -86,10 +88,13 @@ void main() { frameInfo.image.height, ]); } - expect(decodedFrameInfos, equals(>[ - [0, 240, 246], - [0, 240, 246], - ])); + expect( + decodedFrameInfos, + equals(>[ + [0, 240, 246], + [0, 240, 246], + ]), + ); }); test('with size', () async { @@ -98,10 +103,7 @@ void main() { final ui.Codec codec = await ui.instantiateImageCodecWithSize( buffer, getTargetSize: (int intrinsicWidth, int intrinsicHeight) { - return ui.TargetImageSize( - width: intrinsicWidth ~/ 2, - height: intrinsicHeight ~/ 2, - ); + return ui.TargetImageSize(width: intrinsicWidth ~/ 2, height: intrinsicHeight ~/ 2); }, ); final List> decodedFrameInfos = >[]; @@ -113,10 +115,13 @@ void main() { frameInfo.image.height, ]); } - expect(decodedFrameInfos, equals(>[ - [0, 120, 123], - [0, 120, 123], - ])); + expect( + decodedFrameInfos, + equals(>[ + [0, 120, 123], + [0, 120, 123], + ]), + ); }); test('disposed decoded image', () async { @@ -136,9 +141,10 @@ void main() { test('Animated gif can reuse across multiple frames', () async { // Regression test for b/271947267 and https://github.com/flutter/flutter/issues/122134 - final Uint8List data = File( - path.join('flutter', 'lib', 'ui', 'fixtures', 'four_frame_with_reuse.gif'), - ).readAsBytesSync(); + final Uint8List data = + File( + path.join('flutter', 'lib', 'ui', 'fixtures', 'four_frame_with_reuse.gif'), + ).readAsBytesSync(); final ui.Codec codec = await ui.instantiateImageCodec(data); // Capture the final frame of animation. If we have not composited @@ -151,10 +157,12 @@ void main() { final ui.Image image = frameInfo.image; final ByteData imageData = (await image.toByteData(format: ui.ImageByteFormat.png))!; - final String fileName = impellerEnabled ? 'impeller_four_frame_with_reuse_end.png' : 'four_frame_with_reuse_end.png'; - final Uint8List goldenData = File( - path.join('flutter', 'lib', 'ui', 'fixtures', fileName), - ).readAsBytesSync(); + final String fileName = + impellerEnabled + ? 'impeller_four_frame_with_reuse_end.png' + : 'four_frame_with_reuse_end.png'; + final Uint8List goldenData = + File(path.join('flutter', 'lib', 'ui', 'fixtures', fileName)).readAsBytesSync(); expect(imageData.buffer.asUint8List(), goldenData); }); @@ -162,16 +170,15 @@ void main() { test('Animated webp can reuse across multiple frames', () async { // Regression test for https://github.com/flutter/flutter/issues/61150#issuecomment-679055858 - final Uint8List data = File( - path.join('flutter', 'lib', 'ui', 'fixtures', 'heart.webp'), - ).readAsBytesSync(); + final Uint8List data = + File(path.join('flutter', 'lib', 'ui', 'fixtures', 'heart.webp')).readAsBytesSync(); final ui.Codec codec = await ui.instantiateImageCodec(data); // Capture the final frame of animation. If we have not composited // correctly, the hearts will be incorrectly repeated in the image. late ui.FrameInfo frameInfo; for (int i = 0; i < 69; i++) { - frameInfo = await codec.getNextFrame(); + frameInfo = await codec.getNextFrame(); } final ui.Image image = frameInfo.image; @@ -179,20 +186,19 @@ void main() { final String fileName = impellerEnabled ? 'impeller_heart_end.png' : 'heart_end.png'; - final Uint8List goldenData = File( - path.join('flutter', 'lib', 'ui', 'fixtures', fileName), - ).readAsBytesSync(); + final Uint8List goldenData = + File(path.join('flutter', 'lib', 'ui', 'fixtures', fileName)).readAsBytesSync(); expect(imageData.buffer.asUint8List(), goldenData); - }); test('Animated apng can reuse pre-pre-frame', () async { // https://github.com/flutter/engine/pull/42153 - final Uint8List data = File( - path.join('flutter', 'lib', 'ui', 'fixtures', '2_dispose_op_restore_previous.apng'), - ).readAsBytesSync(); + final Uint8List data = + File( + path.join('flutter', 'lib', 'ui', 'fixtures', '2_dispose_op_restore_previous.apng'), + ).readAsBytesSync(); final ui.Codec codec = await ui.instantiateImageCodec(data); // Capture the 67,68,69 frames of animation and then compare the pixels. @@ -203,11 +209,13 @@ void main() { final ui.Image image = frameInfo.image; final ByteData imageData = (await image.toByteData(format: ui.ImageByteFormat.png))!; - final String fileName = impellerEnabled ? 'impeller_2_dispose_op_restore_previous.apng.$i.png' : '2_dispose_op_restore_previous.apng.$i.png'; + final String fileName = + impellerEnabled + ? 'impeller_2_dispose_op_restore_previous.apng.$i.png' + : '2_dispose_op_restore_previous.apng.$i.png'; - final Uint8List goldenData = File( - path.join('flutter', 'lib', 'ui', 'fixtures', fileName), - ).readAsBytesSync(); + final Uint8List goldenData = + File(path.join('flutter', 'lib', 'ui', 'fixtures', fileName)).readAsBytesSync(); expect(imageData.buffer.asUint8List(), goldenData); } @@ -215,9 +223,10 @@ void main() { }); test('Animated apng alpha type handling', () async { - final Uint8List data = File( - path.join('flutter', 'lib', 'ui', 'fixtures', 'alpha_animated.apng'), - ).readAsBytesSync(); + final Uint8List data = + File( + path.join('flutter', 'lib', 'ui', 'fixtures', 'alpha_animated.apng'), + ).readAsBytesSync(); final ui.Codec codec = await ui.instantiateImageCodec(data); // The test image contains two frames of solid red. The first has @@ -231,9 +240,10 @@ void main() { }); test('Animated apng background color restore', () async { - final Uint8List data = File( - path.join('flutter', 'lib', 'ui', 'fixtures', 'dispose_op_background.apng'), - ).readAsBytesSync(); + final Uint8List data = + File( + path.join('flutter', 'lib', 'ui', 'fixtures', 'dispose_op_background.apng'), + ).readAsBytesSync(); final ui.Codec codec = await ui.instantiateImageCodec(data); // First frame is solid red @@ -253,12 +263,9 @@ void main() { expect(imageData.getUint32(imageData.lengthInBytes - 4), 0x00000000); }); - test( - 'Animated apng frame decode does not crash with invalid destination region', - () async { - final Uint8List data = File( - path.join('flutter', 'lib', 'ui', 'fixtures', 'out_of_bounds.apng'), - ).readAsBytesSync(); + test('Animated apng frame decode does not crash with invalid destination region', () async { + final Uint8List data = + File(path.join('flutter', 'lib', 'ui', 'fixtures', 'out_of_bounds.apng')).readAsBytesSync(); final ui.Codec codec = await ui.instantiateImageCodec(data); try { @@ -274,24 +281,26 @@ void main() { }); test( - 'Animated apng frame decode does not crash with invalid destination region and bounds wrapping', - () async { - final Uint8List data = File( - path.join('flutter', 'lib', 'ui', 'fixtures', 'out_of_bounds_wrapping.apng'), - ).readAsBytesSync(); - - final ui.Codec codec = await ui.instantiateImageCodec(data); - try { - await codec.getNextFrame(); - fail('exception not thrown'); - } on Exception catch (e) { - if (impellerEnabled) { - expect(e.toString(), contains('Could not decompress image.')); - } else { - expect(e.toString(), contains('Codec failed')); + 'Animated apng frame decode does not crash with invalid destination region and bounds wrapping', + () async { + final Uint8List data = + File( + path.join('flutter', 'lib', 'ui', 'fixtures', 'out_of_bounds_wrapping.apng'), + ).readAsBytesSync(); + + final ui.Codec codec = await ui.instantiateImageCodec(data); + try { + await codec.getNextFrame(); + fail('exception not thrown'); + } on Exception catch (e) { + if (impellerEnabled) { + expect(e.toString(), contains('Could not decompress image.')); + } else { + expect(e.toString(), contains('Codec failed')); + } } - } - }); + }, + ); } /// Returns a File handle to a file in the skia/resources directory. @@ -302,7 +311,12 @@ File _getSkiaResource(String fileName) { // This is fragile and should be changed once the Platform.script issue is // resolved. final String assetPath = path.join( - 'flutter', 'third_party', 'skia', 'resources', 'images', fileName, + 'flutter', + 'third_party', + 'skia', + 'resources', + 'images', + fileName, ); return File(assetPath); } diff --git a/testing/dart/color_filter_test.dart b/testing/dart/color_filter_test.dart index 12cd7f6ae8816..917dd0c51a4ec 100644 --- a/testing/dart/color_filter_test.dart +++ b/testing/dart/color_filter_test.dart @@ -28,13 +28,29 @@ const List greyscaleColorMatrix = [ 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, // + 0, 0, 0, 1, 0, // ]; const List identityColorMatrix = [ - 1, 0, 0, 0, 0, - 0, 1, 0, 0, 0, - 0, 0, 1, 0, 0, - 0, 0, 0, 1, 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, ]; void main() { @@ -51,9 +67,10 @@ void main() { } test('ColorFilter - mode', () async { - final Paint paint = Paint() - ..color = green - ..colorFilter = const ColorFilter.mode(red, BlendMode.color); + final Paint paint = + Paint() + ..color = green + ..colorFilter = const ColorFilter.mode(red, BlendMode.color); Uint32List bytes = await getBytesForPaint(paint); expect(bytes[0], greenRedColorBlend); @@ -71,9 +88,10 @@ void main() { test('ColorFilter - NOP mode does not crash', () async { final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - final Paint paint = Paint() - ..color = green - ..colorFilter = const ColorFilter.mode(transparent, BlendMode.srcOver); + final Paint paint = + Paint() + ..color = green + ..colorFilter = const ColorFilter.mode(transparent, BlendMode.srcOver); canvas.saveLayer(const Rect.fromLTRB(-100, -100, 200, 200), paint); canvas.drawRect(const Rect.fromLTRB(0, 0, 100, 100), Paint()); canvas.restore(); @@ -87,9 +105,10 @@ void main() { }); test('ColorFilter - matrix', () async { - final Paint paint = Paint() - ..color = green - ..colorFilter = const ColorFilter.matrix(greyscaleColorMatrix); + final Paint paint = + Paint() + ..color = green + ..colorFilter = const ColorFilter.matrix(greyscaleColorMatrix); Uint32List bytes = await getBytesForPaint(paint); expect(bytes[0], greenGreyscaled); @@ -107,9 +126,10 @@ void main() { test('ColorFilter - NOP matrix does not crash', () async { final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - final Paint paint = Paint() - ..color = const Color(0xff00AA00) - ..colorFilter = const ColorFilter.matrix(identityColorMatrix); + final Paint paint = + Paint() + ..color = const Color(0xff00AA00) + ..colorFilter = const ColorFilter.matrix(identityColorMatrix); canvas.saveLayer(const Rect.fromLTRB(-100, -100, 200, 200), paint); canvas.drawRect(const Rect.fromLTRB(0, 0, 100, 100), Paint()); canvas.restore(); @@ -123,9 +143,10 @@ void main() { }); test('ColorFilter - linearToSrgbGamma', () async { - final Paint paint = Paint() - ..color = green - ..colorFilter = const ColorFilter.linearToSrgbGamma(); + final Paint paint = + Paint() + ..color = green + ..colorFilter = const ColorFilter.linearToSrgbGamma(); Uint32List bytes = await getBytesForPaint(paint); expect(bytes[0], greenLinearToSrgbGamma); @@ -140,9 +161,10 @@ void main() { }); test('ColorFilter - srgbToLinearGamma', () async { - final Paint paint = Paint() - ..color = green - ..colorFilter = const ColorFilter.srgbToLinearGamma(); + final Paint paint = + Paint() + ..color = green + ..colorFilter = const ColorFilter.srgbToLinearGamma(); Uint32List bytes = await getBytesForPaint(paint); expect(bytes[0], greenSrgbToLinearGamma); @@ -156,5 +178,4 @@ void main() { bytes = await getBytesForPaint(paint); expect(bytes[0], greenSrgbToLinearGammaInverted); }); - } diff --git a/testing/dart/color_test.dart b/testing/dart/color_test.dart index a23f485aadeca..e828e72197522 100644 --- a/testing/dart/color_test.dart +++ b/testing/dart/color_test.dart @@ -28,7 +28,7 @@ class _ColorMatcher extends Matcher { } } -Matcher colorMatches(Color color, {double threshold = 1/255}) { +Matcher colorMatches(Color color, {double threshold = 1 / 255}) { return _ColorMatcher(color, threshold); } @@ -106,15 +106,10 @@ void main() { bool didThrow = false; try { Color.lerp( - const Color.from( - alpha: 1, - red: 1, - green: 0, - blue: 0, - colorSpace: ColorSpace.displayP3), - const Color.from( - alpha: 1, red: 1, green: 0, blue: 0), - 0.0); + const Color.from(alpha: 1, red: 1, green: 0, blue: 0, colorSpace: ColorSpace.displayP3), + const Color.from(alpha: 1, red: 1, green: 0, blue: 0), + 0.0, + ); } catch (ex) { didThrow = true; } @@ -123,28 +118,15 @@ void main() { test('Color.lerp same colorspaces', () { expect( - Color.lerp( - const Color.from( - alpha: 1, - red: 0, - green: 0, - blue: 0, - colorSpace: ColorSpace.displayP3), - const Color.from( - alpha: 1, - red: 1, - green: 0, - blue: 0, - colorSpace: ColorSpace.displayP3), - 0.2), - equals( - const Color.from( - alpha: 1, - red: 0.2, - green: 0, - blue: 0, - colorSpace: ColorSpace.displayP3), - )); + Color.lerp( + const Color.from(alpha: 1, red: 0, green: 0, blue: 0, colorSpace: ColorSpace.displayP3), + const Color.from(alpha: 1, red: 1, green: 0, blue: 0, colorSpace: ColorSpace.displayP3), + 0.2, + ), + equals( + const Color.from(alpha: 1, red: 0.2, green: 0, blue: 0, colorSpace: ColorSpace.displayP3), + ), + ); }); test('Color.alphaBlend', () { @@ -192,25 +174,12 @@ void main() { test('Color.alphaBlend keeps colorspace', () { expect( - Color.alphaBlend( - const Color.from( - alpha: 0.5, - red: 1, - green: 1, - blue: 1, - colorSpace: ColorSpace.displayP3), - const Color.from( - alpha: 1, - red: 0, - green: 0, - blue: 0, - colorSpace: ColorSpace.displayP3)), - const Color.from( - alpha: 1, - red: 0.5, - green: 0.5, - blue: 0.5, - colorSpace: ColorSpace.displayP3)); + Color.alphaBlend( + const Color.from(alpha: 0.5, red: 1, green: 1, blue: 1, colorSpace: ColorSpace.displayP3), + const Color.from(alpha: 1, red: 0, green: 0, blue: 0, colorSpace: ColorSpace.displayP3), + ), + const Color.from(alpha: 1, red: 0.5, green: 0.5, blue: 0.5, colorSpace: ColorSpace.displayP3), + ); }); test('compute gray luminance', () { @@ -263,7 +232,12 @@ void main() { test('p3 to extended srgb', () { const Color p3 = Color.from( - alpha: 1, red: 1, green: 0, blue: 0, colorSpace: ColorSpace.displayP3); + alpha: 1, + red: 1, + green: 0, + blue: 0, + colorSpace: ColorSpace.displayP3, + ); final Color srgb = p3.withValues(colorSpace: ColorSpace.extendedSRGB); expect(srgb.a, equals(1.0)); expect(srgb.r, closeTo(1.0931, 1e-4)); @@ -274,7 +248,12 @@ void main() { test('p3 to srgb', () { const Color p3 = Color.from( - alpha: 1, red: 1, green: 0, blue: 0, colorSpace: ColorSpace.displayP3); + alpha: 1, + red: 1, + green: 0, + blue: 0, + colorSpace: ColorSpace.displayP3, + ); final Color srgb = p3.withValues(colorSpace: ColorSpace.sRGB); expect(srgb.a, equals(1.0)); expect(srgb.r, closeTo(1, 1e-4)); @@ -285,11 +264,12 @@ void main() { test('extended srgb to p3', () { const Color srgb = Color.from( - alpha: 1, - red: 1.0931, - green: -0.2268, - blue: -0.1501, - colorSpace: ColorSpace.extendedSRGB); + alpha: 1, + red: 1.0931, + green: -0.2268, + blue: -0.1501, + colorSpace: ColorSpace.extendedSRGB, + ); final Color p3 = srgb.withValues(colorSpace: ColorSpace.displayP3); expect(p3.a, equals(1.0)); expect(p3.r, closeTo(1, 1e-4)); @@ -300,11 +280,12 @@ void main() { test('extended srgb to p3 clamped', () { const Color srgb = Color.from( - alpha: 1, - red: 2, - green: 0, - blue: 0, - colorSpace: ColorSpace.extendedSRGB); + alpha: 1, + red: 2, + green: 0, + blue: 0, + colorSpace: ColorSpace.extendedSRGB, + ); final Color p3 = srgb.withValues(colorSpace: ColorSpace.displayP3); expect(srgb.a, equals(1.0)); expect(p3.r <= 1.0, isTrue); @@ -316,18 +297,26 @@ void main() { }); test('hash considers colorspace', () { - const Color srgb = Color.from( - alpha: 1, red: 1, green: 0, blue: 0); + const Color srgb = Color.from(alpha: 1, red: 1, green: 0, blue: 0); const Color p3 = Color.from( - alpha: 1, red: 1, green: 0, blue: 0, colorSpace: ColorSpace.displayP3); + alpha: 1, + red: 1, + green: 0, + blue: 0, + colorSpace: ColorSpace.displayP3, + ); expect(srgb.hashCode, isNot(p3.hashCode)); }); test('equality considers colorspace', () { - const Color srgb = Color.from( - alpha: 1, red: 1, green: 0, blue: 0); + const Color srgb = Color.from(alpha: 1, red: 1, green: 0, blue: 0); const Color p3 = Color.from( - alpha: 1, red: 1, green: 0, blue: 0, colorSpace: ColorSpace.displayP3); + alpha: 1, + red: 1, + green: 0, + blue: 0, + colorSpace: ColorSpace.displayP3, + ); expect(srgb, isNot(p3)); }); diff --git a/testing/dart/compositing_test.dart b/testing/dart/compositing_test.dart index 44377b042980a..cfeea1c90d6b3 100644 --- a/testing/dart/compositing_test.dart +++ b/testing/dart/compositing_test.dart @@ -80,45 +80,82 @@ void main() { test('pushTransform validates the matrix', () { final SceneBuilder builder = SceneBuilder(); final Float64List matrix4 = Float64List.fromList([ - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, ]); expect(builder.pushTransform(matrix4), isNotNull); final Float64List matrix4WrongLength = Float64List.fromList([ - 1, 0, 0, 0, - 0, 1, 0, - 0, 0, 1, 0, - 0, 0, 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, ]); - expect( - () => builder.pushTransform(matrix4WrongLength), - throwsA(isA()), - ); + expect(() => builder.pushTransform(matrix4WrongLength), throwsA(isA())); final Float64List matrix4NaN = Float64List.fromList([ - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, double.nan, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + double.nan, ]); - expect( - () => builder.pushTransform(matrix4NaN), - throwsA(isA()), - ); + expect(() => builder.pushTransform(matrix4NaN), throwsA(isA())); final Float64List matrix4Infinity = Float64List.fromList([ - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, double.infinity, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + double.infinity, ]); - expect( - () => builder.pushTransform(matrix4Infinity), - throwsA(isA()), - ); + expect(() => builder.pushTransform(matrix4Infinity), throwsA(isA())); }); test('SceneBuilder accepts typed layers', () { @@ -146,11 +183,18 @@ void main() { pushFunction(builder2, layer); builder2.pop(); - expect(() { - builder2.addRetained(layer); - }, throwsA(isA() - .having((AssertionError e) => e.toString(), 'toString', - contains('The layer is already being used')))); + expect( + () { + builder2.addRetained(layer); + }, + throwsA( + isA().having( + (AssertionError e) => e.toString(), + 'toString', + contains('The layer is already being used'), + ), + ), + ); builder2.build(); } @@ -164,11 +208,18 @@ void main() { final SceneBuilder builder2 = SceneBuilder(); builder2.addRetained(layer); - expect(() { - pushFunction(builder2, layer); - }, throwsA(isA() - .having((AssertionError e) => e.toString(), 'toString', - contains('The layer is already being used')))); + expect( + () { + pushFunction(builder2, layer); + }, + throwsA( + isA().having( + (AssertionError e) => e.toString(), + 'toString', + contains('The layer is already being used'), + ), + ), + ); builder2.build(); } @@ -181,11 +232,18 @@ void main() { final SceneBuilder builder2 = SceneBuilder(); builder2.addRetained(layer); - expect(() { - builder2.addRetained(layer); - }, throwsA(isA() - .having((AssertionError e) => e.toString(), 'toString', - contains('The layer is already being used')))); + expect( + () { + builder2.addRetained(layer); + }, + throwsA( + isA().having( + (AssertionError e) => e.toString(), + 'toString', + contains('The layer is already being used'), + ), + ), + ); builder2.build(); } @@ -198,11 +256,18 @@ void main() { final SceneBuilder builder2 = SceneBuilder(); pushFunction(builder2, layer); - expect(() { - pushFunction(builder2, layer); - }, throwsA(isA() - .having((AssertionError e) => e.toString(), 'toString', - contains('was previously used as oldLayer')))); + expect( + () { + pushFunction(builder2, layer); + }, + throwsA( + isA().having( + (AssertionError e) => e.toString(), + 'toString', + contains('was previously used as oldLayer'), + ), + ), + ); builder2.build(); } @@ -217,11 +282,18 @@ void main() { final SceneBuilder builder2 = SceneBuilder(); builder2.addRetained(layer); - expect(() { - builder2.pushOpacity(321, oldLayer: childLayer); - }, throwsA(isA() - .having((AssertionError e) => e.toString(), 'toString', - contains('The layer is already being used')))); + expect( + () { + builder2.pushOpacity(321, oldLayer: childLayer); + }, + throwsA( + isA().having( + (AssertionError e) => e.toString(), + 'toString', + contains('The layer is already being used'), + ), + ), + ); builder2.build(); } @@ -237,11 +309,18 @@ void main() { final SceneBuilder builder2 = SceneBuilder(); builder2.pushOpacity(234, oldLayer: childLayer); builder2.pop(); - expect(() { - builder2.addRetained(layer); - }, throwsA(isA() - .having((AssertionError e) => e.toString(), 'toString', - contains('The layer is already being used')))); + expect( + () { + builder2.addRetained(layer); + }, + throwsA( + isA().having( + (AssertionError e) => e.toString(), + 'toString', + contains('The layer is already being used'), + ), + ), + ); builder2.build(); } @@ -255,12 +334,19 @@ void main() { final SceneBuilder builder2 = SceneBuilder(); pushFunction(builder2, layer); builder2.pop(); - expect(() { - final SceneBuilder builder3 = SceneBuilder(); - builder3.addRetained(layer); - }, throwsA(isA() - .having((AssertionError e) => e.toString(), 'toString', - contains('was previously used as oldLayer')))); + expect( + () { + final SceneBuilder builder3 = SceneBuilder(); + builder3.addRetained(layer); + }, + throwsA( + isA().having( + (AssertionError e) => e.toString(), + 'toString', + contains('was previously used as oldLayer'), + ), + ), + ); builder2.build(); } @@ -274,12 +360,19 @@ void main() { final SceneBuilder builder2 = SceneBuilder(); pushFunction(builder2, layer); builder2.pop(); - expect(() { - final SceneBuilder builder3 = SceneBuilder(); - pushFunction(builder3, layer); - }, throwsA(isA() - .having((AssertionError e) => e.toString(), 'toString', - contains('was previously used as oldLayer')))); + expect( + () { + final SceneBuilder builder3 = SceneBuilder(); + pushFunction(builder3, layer); + }, + throwsA( + isA().having( + (AssertionError e) => e.toString(), + 'toString', + contains('was previously used as oldLayer'), + ), + ), + ); builder2.build(); } @@ -295,12 +388,19 @@ void main() { final SceneBuilder builder2 = SceneBuilder(); builder2.pushOpacity(321, oldLayer: childLayer); builder2.pop(); - expect(() { - final SceneBuilder builder3 = SceneBuilder(); - builder3.addRetained(parentLayer); - }, throwsA(isA() - .having((AssertionError e) => e.toString(), 'toString', - contains('was previously used as oldLayer')))); + expect( + () { + final SceneBuilder builder3 = SceneBuilder(); + builder3.addRetained(parentLayer); + }, + throwsA( + isA().having( + (AssertionError e) => e.toString(), + 'toString', + contains('was previously used as oldLayer'), + ), + ), + ); builder2.build(); } @@ -336,15 +436,17 @@ void main() { return builder.pushOpacity(100, oldLayer: oldLayer as OpacityEngineLayer?); }); testNoSharing((SceneBuilder builder, EngineLayer? oldLayer) { - return builder.pushBackdropFilter(ImageFilter.blur(sigmaX: 1.0), oldLayer: oldLayer as BackdropFilterEngineLayer?); + return builder.pushBackdropFilter( + ImageFilter.blur(sigmaX: 1.0), + oldLayer: oldLayer as BackdropFilterEngineLayer?, + ); }); testNoSharing((SceneBuilder builder, EngineLayer? oldLayer) { return builder.pushShaderMask( - Gradient.radial( - Offset.zero, - 10, - const [Color.fromARGB(0, 0, 0, 0), Color.fromARGB(0, 255, 255, 255)], - ), + Gradient.radial(Offset.zero, 10, const [ + Color.fromARGB(0, 0, 0, 0), + Color.fromARGB(0, 255, 255, 255), + ]), Rect.zero, BlendMode.color, oldLayer: oldLayer as ShaderMaskEngineLayer?, @@ -352,20 +454,33 @@ void main() { }); testNoSharing((SceneBuilder builder, EngineLayer? oldLayer) { return builder.pushColorFilter( - const ColorFilter.mode( - Color.fromARGB(0, 0, 0, 0), - BlendMode.color, - ), + const ColorFilter.mode(Color.fromARGB(0, 0, 0, 0), BlendMode.color), oldLayer: oldLayer as ColorFilterEngineLayer?, ); }); testNoSharing((SceneBuilder builder, EngineLayer? oldLayer) { return builder.pushColorFilter( const ColorFilter.matrix([ - 1, 0, 0, 0, 0, - 0, 1, 0, 0, 0, - 0, 0, 1, 0, 0, - 0, 0, 0, 1, 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 0, ]), oldLayer: oldLayer as ColorFilterEngineLayer?, ); @@ -402,12 +517,9 @@ void main() { }); testNoSharing((SceneBuilder builder, EngineLayer? oldLayer) { return builder.pushImageFilter( - ImageFilter.matrix(Float64List.fromList([ - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1, - ])), + ImageFilter.matrix( + Float64List.fromList([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]), + ), oldLayer: oldLayer as ImageFilterEngineLayer?, ); }); diff --git a/testing/dart/dart_test.dart b/testing/dart/dart_test.dart index bf0c6d495fefd..1f696be3d0f91 100644 --- a/testing/dart/dart_test.dart +++ b/testing/dart/dart_test.dart @@ -14,6 +14,7 @@ void main() { await Future.value(); greeting += ' 2'; } + test('execution of async method starts synchronously', () async { expect(greeting, 'hello'); final Future future = changeGreeting(); diff --git a/testing/dart/encoding_test.dart b/testing/dart/encoding_test.dart index 64c965983860b..fedef69f7261d 100644 --- a/testing/dart/encoding_test.dart +++ b/testing/dart/encoding_test.dart @@ -43,15 +43,14 @@ void main() { } final Completer completer = Completer(); - decodeImageFromPixels( - Uint8List.view(pixels.buffer), width, height, PixelFormat.rgbaFloat32, - (Image result) { + decodeImageFromPixels(Uint8List.view(pixels.buffer), width, height, PixelFormat.rgbaFloat32, ( + Image result, + ) { completer.complete(result); }); final Image image = await completer.future; - final ByteData data = - (await image.toByteData(format: ImageByteFormat.rawStraightRgba))!; + final ByteData data = (await image.toByteData(format: ImageByteFormat.rawStraightRgba))!; final Uint32List readPixels = Uint32List.view(data.buffer); expect(width * height, readPixels.length); expect(readPixels[0], 0xff0000ff); @@ -144,26 +143,25 @@ class Square4x4Image { final double innerWidth = (_kWidth - 2 * _kRadius).toDouble(); final PictureRecorder recorder = PictureRecorder(); - final Canvas canvas = - Canvas(recorder, Rect.fromLTWH(0.0, 0.0, width, width)); + final Canvas canvas = Canvas(recorder, Rect.fromLTWH(0.0, 0.0, width, width)); - final Paint black = Paint() - ..strokeWidth = 1.0 - ..color = _kBlack; - final Paint green = Paint() - ..strokeWidth = 1.0 - ..color = _kGreen; + final Paint black = + Paint() + ..strokeWidth = 1.0 + ..color = _kBlack; + final Paint green = + Paint() + ..strokeWidth = 1.0 + ..color = _kGreen; canvas.drawRect(Rect.fromLTWH(0.0, 0.0, width, width), black); - canvas.drawRect( - Rect.fromLTWH(radius, radius, innerWidth, innerWidth), green); + canvas.drawRect(Rect.fromLTWH(radius, radius, innerWidth, innerWidth), green); return recorder.endRecording().toImage(_kWidth, _kWidth); } static List get bytes { const int bytesPerChannel = 4; - final List result = List.filled( - _kWidth * _kWidth * bytesPerChannel, 0); + final List result = List.filled(_kWidth * _kWidth * bytesPerChannel, 0); void fillWithColor(Color color, int min, int max) { for (int i = min; i < max; i++) { @@ -195,19 +193,14 @@ class GrayscaleImage { } static List get bytesAsRgba { - return [ - 255, 255, 255, 255, - 127, 127, 127, 255, - 127, 127, 127, 255, - 0, 0, 0, 255, - ]; + return [255, 255, 255, 255, 127, 127, 127, 255, 127, 127, 127, 255, 0, 0, 0, 255]; } static List get bytesUnmodified => [255, 127, 127, 0]; } class TransparentImage { - TransparentImage._(); + TransparentImage._(); static Future load() async { final Uint8List bytes = await readFile('transparent_image.png'); @@ -219,56 +212,50 @@ class TransparentImage { static List get bytesAsPremultipliedRgba { return [ //First raw, solid colors - 255, 0, 0, 255, // red - 0, 255, 0, 255, // green - 0, 0, 255, 255, // blue + 255, 0, 0, 255, // red + 0, 255, 0, 255, // green + 0, 0, 255, 255, // blue 136, 136, 136, 255, // grey - //Second raw, 50% transparent - 127, 0, 0, 127, // red - 0, 127, 0, 127, // green - 0, 0, 127, 127, // blue - 67, 67, 67, 127, // grey - + 127, 0, 0, 127, // red + 0, 127, 0, 127, // green + 0, 0, 127, 127, // blue + 67, 67, 67, 127, // grey //Third raw, 25% transparent - 63, 0, 0, 63, // red - 0, 63, 0, 63, // green - 0, 0, 63, 63, // blue - 33, 33, 33, 63, // grey - + 63, 0, 0, 63, // red + 0, 63, 0, 63, // green + 0, 0, 63, 63, // blue + 33, 33, 33, 63, // grey //Fourth raw, transparent - 0, 0, 0, 0, // red - 0, 0, 0, 0, // green - 0, 0, 0, 0, // blue - 0, 0, 0, 0, // grey + 0, 0, 0, 0, // red + 0, 0, 0, 0, // green + 0, 0, 0, 0, // blue + 0, 0, 0, 0, // grey ]; } static List get bytesAsStraightRgba { return [ //First raw, solid colors - 255, 0, 0, 255, // red - 0, 255, 0, 255, // green - 0, 0, 255, 255, // blue + 255, 0, 0, 255, // red + 0, 255, 0, 255, // green + 0, 0, 255, 255, // blue 136, 136, 136, 255, // grey - //Second raw, 50% transparent - 255, 0, 0, 127, // red - 0, 255, 0, 127, // green - 0, 0, 255, 127, // blue + 255, 0, 0, 127, // red + 0, 255, 0, 127, // green + 0, 0, 255, 127, // blue 135, 135, 135, 127, // grey - //Third raw, 25% transparent - 255, 0, 0, 63, // red - 0, 255, 0, 63, // green - 0, 0, 255, 63, // blue - 134, 134, 134, 63, // grey - + 255, 0, 0, 63, // red + 0, 255, 0, 63, // green + 0, 0, 255, 63, // blue + 134, 134, 134, 63, // grey //Fourth raw, transparent - 0, 0, 0, 0, // red - 0, 0, 0, 0, // green - 0, 0, 0, 0, // blue - 0, 0, 0, 0, // grey + 0, 0, 0, 0, // red + 0, 0, 0, 0, // green + 0, 0, 0, 0, // blue + 0, 0, 0, 0, // grey ]; } } diff --git a/testing/dart/fragment_shader_test.dart b/testing/dart/fragment_shader_test.dart index e065a2eb7d186..c2f0dec0f5652 100644 --- a/testing/dart/fragment_shader_test.dart +++ b/testing/dart/fragment_shader_test.dart @@ -19,7 +19,8 @@ void main() async { test('impellerc produces reasonable JSON encoded IPLR files', () async { final Directory directory = shaderDirectory('iplr-json'); final Object? rawData = convert.json.decode( - File(path.join(directory.path, 'ink_sparkle.frag.iplr')).readAsStringSync()); + File(path.join(directory.path, 'ink_sparkle.frag.iplr')).readAsStringSync(), + ); expect(rawData is Map, true); @@ -56,9 +57,7 @@ void main() async { }); test('FragmentShader setSampler throws with out-of-bounds index', () async { - final FragmentProgram program = await FragmentProgram.fromAsset( - 'blue_green_sampler.frag.iplr', - ); + final FragmentProgram program = await FragmentProgram.fromAsset('blue_green_sampler.frag.iplr'); final Image blueGreenImage = await _createBlueGreenImage(); final FragmentShader fragmentShader = program.fragmentShader(); @@ -73,75 +72,102 @@ void main() async { } }); - test('FragmentShader with sampler asserts if sampler is missing when assigned to paint', () async { - final FragmentProgram program = await FragmentProgram.fromAsset( - 'blue_green_sampler.frag.iplr', - ); - final FragmentShader fragmentShader = program.fragmentShader(); + test( + 'FragmentShader with sampler asserts if sampler is missing when assigned to paint', + () async { + final FragmentProgram program = await FragmentProgram.fromAsset( + 'blue_green_sampler.frag.iplr', + ); + final FragmentShader fragmentShader = program.fragmentShader(); - try { - Paint().shader = fragmentShader; - fail('Expected to throw'); - } catch (err) { - expect(err.toString(), contains('Invalid FragmentShader blue_green_sampler.frag.iplr')); - } finally { - fragmentShader.dispose(); - } - }); + try { + Paint().shader = fragmentShader; + fail('Expected to throw'); + } catch (err) { + expect(err.toString(), contains('Invalid FragmentShader blue_green_sampler.frag.iplr')); + } finally { + fragmentShader.dispose(); + } + }, + ); test('Disposed FragmentShader on Paint', () async { - final FragmentProgram program = await FragmentProgram.fromAsset( - 'blue_green_sampler.frag.iplr', - ); + final FragmentProgram program = await FragmentProgram.fromAsset('blue_green_sampler.frag.iplr'); final Image blueGreenImage = await _createBlueGreenImage(); - final FragmentShader shader = program.fragmentShader() - ..setImageSampler(0, blueGreenImage); + final FragmentShader shader = program.fragmentShader()..setImageSampler(0, blueGreenImage); shader.dispose(); - expect(() { - Paint().shader = shader; - }, throwsA(isA().having((AssertionError e) => e.message, 'message', contains('Attempted to set a disposed shader')))); + expect( + () { + Paint().shader = shader; + }, + throwsA( + isA().having( + (AssertionError e) => e.message, + 'message', + contains('Attempted to set a disposed shader'), + ), + ), + ); blueGreenImage.dispose(); }); test('Disposed FragmentShader setFloat', () async { - final FragmentProgram program = await FragmentProgram.fromAsset( - 'uniforms.frag.iplr', - ); - final FragmentShader shader = program.fragmentShader() - ..setFloat(0, 0.0); + final FragmentProgram program = await FragmentProgram.fromAsset('uniforms.frag.iplr'); + final FragmentShader shader = program.fragmentShader()..setFloat(0, 0.0); shader.dispose(); - expect(() { - shader.setFloat(0, 0.0); - }, throwsA(isA().having((AssertionError e) => e.message, 'message', contains('Tried to accesss uniforms on a disposed Shader')))); + expect( + () { + shader.setFloat(0, 0.0); + }, + throwsA( + isA().having( + (AssertionError e) => e.message, + 'message', + contains('Tried to accesss uniforms on a disposed Shader'), + ), + ), + ); }); test('Disposed FragmentShader setImageSampler', () async { - final FragmentProgram program = await FragmentProgram.fromAsset( - 'blue_green_sampler.frag.iplr', - ); + final FragmentProgram program = await FragmentProgram.fromAsset('blue_green_sampler.frag.iplr'); final Image blueGreenImage = await _createBlueGreenImage(); - final FragmentShader shader = program.fragmentShader() - ..setImageSampler(0, blueGreenImage); + final FragmentShader shader = program.fragmentShader()..setImageSampler(0, blueGreenImage); shader.dispose(); - expect(() { - shader.setImageSampler(0, blueGreenImage); - }, throwsA(isA().having((AssertionError e) => e.message, 'message', contains('Tried to access uniforms on a disposed Shader')))); + expect( + () { + shader.setImageSampler(0, blueGreenImage); + }, + throwsA( + isA().having( + (AssertionError e) => e.message, + 'message', + contains('Tried to access uniforms on a disposed Shader'), + ), + ), + ); blueGreenImage.dispose(); }); test('Disposed FragmentShader dispose', () async { - final FragmentProgram program = await FragmentProgram.fromAsset( - 'uniforms.frag.iplr', - ); - final FragmentShader shader = program.fragmentShader() - ..setFloat(0, 0.0); + final FragmentProgram program = await FragmentProgram.fromAsset('uniforms.frag.iplr'); + final FragmentShader shader = program.fragmentShader()..setFloat(0, 0.0); shader.dispose(); - expect(() { - shader.dispose(); - }, throwsA(isA().having((AssertionError e) => e.message, 'message', contains('Shader cannot be disposed more than once')))); + expect( + () { + shader.dispose(); + }, + throwsA( + isA().having( + (AssertionError e) => e.message, + 'message', + contains('Shader cannot be disposed more than once'), + ), + ), + ); }); test('FragmentShader simple shader renders correctly', () async { @@ -149,11 +175,8 @@ void main() async { print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823'); return; } - final FragmentProgram program = await FragmentProgram.fromAsset( - 'functions.frag.iplr', - ); - final FragmentShader shader = program.fragmentShader() - ..setFloat(0, 1.0); + final FragmentProgram program = await FragmentProgram.fromAsset('functions.frag.iplr'); + final FragmentShader shader = program.fragmentShader()..setFloat(0, 1.0); await _expectShaderRendersGreen(shader); shader.dispose(); }); @@ -163,11 +186,8 @@ void main() async { print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823'); return; } - final FragmentProgram program = await FragmentProgram.fromAsset( - 'functions.frag.iplr', - ); - final FragmentShader shader = program.fragmentShader() - ..setFloat(0, 1.0); + final FragmentProgram program = await FragmentProgram.fromAsset('functions.frag.iplr'); + final FragmentShader shader = program.fragmentShader()..setFloat(0, 1.0); await _expectShaderRendersGreen(shader); shader.setFloat(0, 0.0); @@ -181,12 +201,9 @@ void main() async { print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823'); return; } - final FragmentProgram program = await FragmentProgram.fromAsset( - 'blue_green_sampler.frag.iplr', - ); + final FragmentProgram program = await FragmentProgram.fromAsset('blue_green_sampler.frag.iplr'); final Image blueGreenImage = await _createBlueGreenImage(); - final FragmentShader shader = program.fragmentShader() - ..setImageSampler(0, blueGreenImage); + final FragmentShader shader = program.fragmentShader()..setImageSampler(0, blueGreenImage); await _expectShaderRendersGreen(shader); shader.dispose(); blueGreenImage.dispose(); @@ -197,12 +214,9 @@ void main() async { print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823'); return; } - final FragmentProgram program = await FragmentProgram.fromAsset( - 'blue_green_sampler.frag.iplr', - ); + final FragmentProgram program = await FragmentProgram.fromAsset('blue_green_sampler.frag.iplr'); final Image blueGreenImage = _createBlueGreenImageSync(); - final FragmentShader shader = program.fragmentShader() - ..setImageSampler(0, blueGreenImage); + final FragmentShader shader = program.fragmentShader()..setImageSampler(0, blueGreenImage); await _expectShaderRendersGreen(shader); shader.dispose(); blueGreenImage.dispose(); @@ -213,22 +227,19 @@ void main() async { print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823'); return; } - final FragmentProgram program = await FragmentProgram.fromAsset( - 'uniforms.frag.iplr', - ); + final FragmentProgram program = await FragmentProgram.fromAsset('uniforms.frag.iplr'); - final FragmentShader shader = program.fragmentShader() - ..setFloat(0, 0.0) - ..setFloat(1, 0.25) - ..setFloat(2, 0.75) - ..setFloat(3, 0.0) - ..setFloat(4, 0.0) - ..setFloat(5, 0.0) - ..setFloat(6, 1.0); + final FragmentShader shader = + program.fragmentShader() + ..setFloat(0, 0.0) + ..setFloat(1, 0.25) + ..setFloat(2, 0.75) + ..setFloat(3, 0.0) + ..setFloat(4, 0.0) + ..setFloat(5, 0.0) + ..setFloat(6, 1.0); - final ByteData renderedBytes = (await _imageByteDataFromShader( - shader: shader, - ))!; + final ByteData renderedBytes = (await _imageByteDataFromShader(shader: shader))!; expect(toFloat(renderedBytes.getUint8(0)), closeTo(0.0, epsilon)); expect(toFloat(renderedBytes.getUint8(1)), closeTo(0.25, epsilon)); @@ -243,9 +254,7 @@ void main() async { print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823'); return; } - final FragmentProgram program = await FragmentProgram.fromAsset( - 'uniform_arrays.frag.iplr', - ); + final FragmentProgram program = await FragmentProgram.fromAsset('uniform_arrays.frag.iplr'); final FragmentShader shader = program.fragmentShader(); for (int i = 0; i < 20; i++) { @@ -261,9 +270,7 @@ void main() async { print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823'); return; } - final FragmentProgram program = await FragmentProgram.fromAsset( - 'ink_sparkle.frag.iplr', - ); + final FragmentProgram program = await FragmentProgram.fromAsset('ink_sparkle.frag.iplr'); final FragmentShader shader = program.fragmentShader(); await _imageByteDataFromShader(shader: shader); @@ -278,9 +285,7 @@ void main() async { print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823'); return; } - final FragmentProgram program = await FragmentProgram.fromAsset( - 'uniforms_sorted.frag.iplr', - ); + final FragmentProgram program = await FragmentProgram.fromAsset('uniforms_sorted.frag.iplr'); // The shader will not render green if the compiler doesn't keep the // uniforms in the right order. @@ -297,9 +302,7 @@ void main() async { test('fromAsset throws an exception on invalid assetKey', () async { bool throws = false; try { - await FragmentProgram.fromAsset( - '', - ); + await FragmentProgram.fromAsset(''); } catch (e) { throws = true; } @@ -309,9 +312,7 @@ void main() async { test('fromAsset throws an exception on invalid data', () async { bool throws = false; try { - await FragmentProgram.fromAsset( - 'DashInNooglerHat.jpg', - ); + await FragmentProgram.fromAsset('DashInNooglerHat.jpg'); } catch (e) { throws = true; } @@ -326,8 +327,7 @@ void main() async { final FragmentProgram program = await FragmentProgram.fromAsset( 'no_builtin_redefinition.frag.iplr', ); - final FragmentShader shader = program.fragmentShader() - ..setFloat(0, 1.0); + final FragmentShader shader = program.fragmentShader()..setFloat(0, 1.0); await _expectShaderRendersGreen(shader); shader.dispose(); }); @@ -337,9 +337,7 @@ void main() async { print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823'); return; } - final FragmentProgram program = await FragmentProgram.fromAsset( - 'no_uniforms.frag.iplr', - ); + final FragmentProgram program = await FragmentProgram.fromAsset('no_uniforms.frag.iplr'); final FragmentShader shader = program.fragmentShader(); await _expectShaderRendersGreen(shader); shader.dispose(); @@ -353,18 +351,12 @@ void main() async { const List shaders = [ 'no_uniforms.frag.iplr', 'missing_size.frag.iplr', - 'missing_texture.frag.iplr' - ]; - const List<(bool, bool)> errors = [ - (true, true), - (true, false), - (false, false) + 'missing_texture.frag.iplr', ]; + const List<(bool, bool)> errors = [(true, true), (true, false), (false, false)]; for (int i = 0; i < 3; i++) { final String fileName = shaders[i]; - final FragmentProgram program = await FragmentProgram.fromAsset( - fileName - ); + final FragmentProgram program = await FragmentProgram.fromAsset(fileName); final FragmentShader shader = program.fragmentShader(); Object? error; @@ -389,16 +381,14 @@ void main() async { print('Skipped for Skia'); return; } - final FragmentProgram program = await FragmentProgram.fromAsset( - 'filter_shader.frag.iplr', - ); + final FragmentProgram program = await FragmentProgram.fromAsset('filter_shader.frag.iplr'); final FragmentShader shader = program.fragmentShader(); final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); canvas.drawPaint( Paint() ..color = const Color(0xFFFF0000) - ..imageFilter = ImageFilter.shader(shader) + ..imageFilter = ImageFilter.shader(shader), ); final Image image = await recorder.endRecording().toImage(1, 1); final ByteData data = (await image.toByteData())!; @@ -437,8 +427,7 @@ void _expectFragmentShadersRenderGreen(Map programs) { for (final String key in programs.keys) { test('FragmentProgram $key renders green', () async { final FragmentProgram program = programs[key]!; - final FragmentShader shader = program.fragmentShader() - ..setFloat(0, 1.0); + final FragmentShader shader = program.fragmentShader()..setFloat(0, 1.0); await _expectShaderRendersGreen(shader); shader.dispose(); }); @@ -446,10 +435,8 @@ void _expectFragmentShadersRenderGreen(Map programs) { } Future _expectShaderRendersColor(Shader shader, Color color) async { - final ByteData renderedBytes = (await _imageByteDataFromShader( - shader: shader, - imageDimension: _shaderImageDimension, - ))!; + final ByteData renderedBytes = + (await _imageByteDataFromShader(shader: shader, imageDimension: _shaderImageDimension))!; for (final int c in renderedBytes.buffer.asUint32List()) { expect(toHexString(c), toHexString(color.value)); } @@ -473,10 +460,7 @@ Future _imageByteDataFromShader({ final Paint paint = Paint()..shader = shader; canvas.drawPaint(paint); final Picture picture = recorder.endRecording(); - final Image image = await picture.toImage( - imageDimension, - imageDimension, - ); + final Image image = await picture.toImage(imageDimension, imageDimension); return image.toByteData(); } @@ -484,10 +468,7 @@ Future _imageByteDataFromShader({ // $FLUTTER_BUILD_DIRECTORY/gen/flutter/lib/spirv/test/$leafFolderName // This is synchronous so that tests can be inside of a loop with // the proper test name. -Future> _loadShaderAssets( - String leafFolderName, - String ext, - ) async { +Future> _loadShaderAssets(String leafFolderName, String ext) async { final Map out = SplayTreeMap(); final Directory directory = shaderDirectory(leafFolderName); @@ -496,14 +477,10 @@ Future> _loadShaderAssets( } await Future.forEach( - directory - .listSync() - .where((FileSystemEntity entry) => path.extension(entry.path) == ext), + directory.listSync().where((FileSystemEntity entry) => path.extension(entry.path) == ext), (FileSystemEntity entry) async { final String key = path.basenameWithoutExtension(entry.path); - out[key] = await FragmentProgram.fromAsset( - path.basename(entry.path), - ); + out[key] = await FragmentProgram.fromAsset(path.basename(entry.path)); }, ); return out; @@ -532,12 +509,12 @@ Future _createBlueGreenImage() async { int i = 0; for (int y = 0; y < length; y++) { for (int x = 0; x < length; x++) { - if (x < length/2) { - pixels[i+2] = 0xFF; // blue channel + if (x < length / 2) { + pixels[i + 2] = 0xFF; // blue channel } else { - pixels[i+1] = 0xFF; // green channel + pixels[i + 1] = 0xFF; // green channel } - pixels[i+3] = 0xFF; // alpha channel + pixels[i + 3] = 0xFF; // alpha channel i += bytesPerPixel; } } diff --git a/testing/dart/geometry_test.dart b/testing/dart/geometry_test.dart index 3f28dd517bb13..0ea4dd804bf2f 100644 --- a/testing/dart/geometry_test.dart +++ b/testing/dart/geometry_test.dart @@ -65,7 +65,10 @@ void main() { test('Offset.fromDirection', () { expect(Offset.fromDirection(0.0, 0.0), const Offset(0.0, 0.0)); - expect(Offset.fromDirection(pi / 2.0).dx, closeTo(0.0, 1e-12)); // aah, floating point math. i love you so. + expect( + Offset.fromDirection(pi / 2.0).dx, + closeTo(0.0, 1e-12), + ); // aah, floating point math. i love you so. expect(Offset.fromDirection(pi / 2.0).dy, 1.0); expect(Offset.fromDirection(-pi / 2.0).dx, closeTo(0.0, 1e-12)); expect(Offset.fromDirection(-pi / 2.0).dy, -1.0); @@ -244,12 +247,13 @@ void main() { }); test('RRect.scaleRadii() properly constrained radii should remain unchanged', () { - final RRect rrect = RRect.fromRectAndCorners( - const Rect.fromLTRB(1.0, 1.0, 2.0, 2.0), - topLeft: const Radius.circular(0.5), - topRight: const Radius.circular(0.25), - bottomRight: const Radius.elliptical(0.25, 0.75), - ).scaleRadii(); + final RRect rrect = + RRect.fromRectAndCorners( + const Rect.fromLTRB(1.0, 1.0, 2.0, 2.0), + topLeft: const Radius.circular(0.5), + topRight: const Radius.circular(0.25), + bottomRight: const Radius.elliptical(0.25, 0.75), + ).scaleRadii(); // check sides expect(rrect.left, 1.0); @@ -269,12 +273,13 @@ void main() { }); test('RRect.scaleRadii() sum of radii that exceed side length should properly scale', () { - final RRect rrect = RRect.fromRectAndCorners( - const Rect.fromLTRB(1.0, 1.0, 2.0, 2.0), - topLeft: const Radius.circular(5000.0), - topRight: const Radius.circular(2500.0), - bottomRight: const Radius.elliptical(2500.0, 7500.0), - ).scaleRadii(); + final RRect rrect = + RRect.fromRectAndCorners( + const Rect.fromLTRB(1.0, 1.0, 2.0, 2.0), + topLeft: const Radius.circular(5000.0), + topRight: const Radius.circular(2500.0), + bottomRight: const Radius.elliptical(2500.0, 7500.0), + ).scaleRadii(); // check sides expect(rrect.left, 1.0); @@ -294,8 +299,13 @@ void main() { }); test('Radius.clamp() operates as expected', () { - final RRect rrectMin = RRect.fromLTRBR(1, 3, 5, 7, - const Radius.circular(-100).clamp(minimum: Radius.zero)); + final RRect rrectMin = RRect.fromLTRBR( + 1, + 3, + 5, + 7, + const Radius.circular(-100).clamp(minimum: Radius.zero), + ); expect(rrectMin.left, 1); expect(rrectMin.top, 3); @@ -304,8 +314,13 @@ void main() { expect(rrectMin.trRadius, equals(const Radius.circular(0))); expect(rrectMin.blRadius, equals(const Radius.circular(0))); - final RRect rrectMax = RRect.fromLTRBR(1, 3, 5, 7, - const Radius.circular(100).clamp(maximum: const Radius.circular(10))); + final RRect rrectMax = RRect.fromLTRBR( + 1, + 3, + 5, + 7, + const Radius.circular(100).clamp(maximum: const Radius.circular(10)), + ); expect(rrectMax.left, 1); expect(rrectMax.top, 3); @@ -314,8 +329,16 @@ void main() { expect(rrectMax.trRadius, equals(const Radius.circular(10))); expect(rrectMax.blRadius, equals(const Radius.circular(10))); - final RRect rrectMix = RRect.fromLTRBR(1, 3, 5, 7, - const Radius.elliptical(-100, 100).clamp(minimum: Radius.zero, maximum: const Radius.circular(10))); + final RRect rrectMix = RRect.fromLTRBR( + 1, + 3, + 5, + 7, + const Radius.elliptical( + -100, + 100, + ).clamp(minimum: Radius.zero, maximum: const Radius.circular(10)), + ); expect(rrectMix.left, 1); expect(rrectMix.top, 3); @@ -324,8 +347,16 @@ void main() { expect(rrectMix.trRadius, equals(const Radius.elliptical(0, 10))); expect(rrectMix.blRadius, equals(const Radius.elliptical(0, 10))); - final RRect rrectMix1 = RRect.fromLTRBR(1, 3, 5, 7, - const Radius.elliptical(100, -100).clamp(minimum: Radius.zero, maximum: const Radius.circular(10))); + final RRect rrectMix1 = RRect.fromLTRBR( + 1, + 3, + 5, + 7, + const Radius.elliptical( + 100, + -100, + ).clamp(minimum: Radius.zero, maximum: const Radius.circular(10)), + ); expect(rrectMix1.left, 1); expect(rrectMix1.top, 3); @@ -336,8 +367,13 @@ void main() { }); test('Radius.clampValues() operates as expected', () { - final RRect rrectMin = RRect.fromLTRBR(1, 3, 5, 7, - const Radius.circular(-100).clampValues(minimumX: 0, minimumY: 0)); + final RRect rrectMin = RRect.fromLTRBR( + 1, + 3, + 5, + 7, + const Radius.circular(-100).clampValues(minimumX: 0, minimumY: 0), + ); expect(rrectMin.left, 1); expect(rrectMin.top, 3); @@ -346,8 +382,13 @@ void main() { expect(rrectMin.trRadius, equals(const Radius.circular(0))); expect(rrectMin.blRadius, equals(const Radius.circular(0))); - final RRect rrectMax = RRect.fromLTRBR(1, 3, 5, 7, - const Radius.circular(100).clampValues(maximumX: 10, maximumY: 20)); + final RRect rrectMax = RRect.fromLTRBR( + 1, + 3, + 5, + 7, + const Radius.circular(100).clampValues(maximumX: 10, maximumY: 20), + ); expect(rrectMax.left, 1); expect(rrectMax.top, 3); @@ -356,8 +397,16 @@ void main() { expect(rrectMax.trRadius, equals(const Radius.elliptical(10, 20))); expect(rrectMax.blRadius, equals(const Radius.elliptical(10, 20))); - final RRect rrectMix = RRect.fromLTRBR(1, 3, 5, 7, - const Radius.elliptical(-100, 100).clampValues(minimumX: 5, minimumY: 6, maximumX: 10, maximumY: 20)); + final RRect rrectMix = RRect.fromLTRBR( + 1, + 3, + 5, + 7, + const Radius.elliptical( + -100, + 100, + ).clampValues(minimumX: 5, minimumY: 6, maximumX: 10, maximumY: 20), + ); expect(rrectMix.left, 1); expect(rrectMix.top, 3); @@ -366,8 +415,16 @@ void main() { expect(rrectMix.trRadius, equals(const Radius.elliptical(5, 20))); expect(rrectMix.blRadius, equals(const Radius.elliptical(5, 20))); - final RRect rrectMix2 = RRect.fromLTRBR(1, 3, 5, 7, - const Radius.elliptical(100, -100).clampValues(minimumX: 5, minimumY: 6, maximumX: 10, maximumY: 20)); + final RRect rrectMix2 = RRect.fromLTRBR( + 1, + 3, + 5, + 7, + const Radius.elliptical( + 100, + -100, + ).clampValues(minimumX: 5, minimumY: 6, maximumX: 10, maximumY: 20), + ); expect(rrectMix2.left, 1); expect(rrectMix2.top, 3); @@ -456,7 +513,7 @@ void main() { expect(rrect.brRadiusY, 0); }); - test('infinity lerp', (){ + test('infinity lerp', () { const Offset a = Offset(double.infinity, double.infinity); const Offset b = Offset(4, 4); final Offset? result = Offset.lerp(a, b, 0.5); diff --git a/testing/dart/gesture_settings_test.dart b/testing/dart/gesture_settings_test.dart index 8446b3174abfc..6d3949fdc0803 100644 --- a/testing/dart/gesture_settings_test.dart +++ b/testing/dart/gesture_settings_test.dart @@ -8,17 +8,32 @@ import 'package:test/test.dart'; void main() { test('GestureSettings has a reasonable toString', () { - const GestureSettings gestureSettings = GestureSettings(physicalDoubleTapSlop: 2.0, physicalTouchSlop: 1.0); - - expect(gestureSettings.toString(), 'GestureSettings(physicalTouchSlop: 1.0, physicalDoubleTapSlop: 2.0)'); + const GestureSettings gestureSettings = GestureSettings( + physicalDoubleTapSlop: 2.0, + physicalTouchSlop: 1.0, + ); + + expect( + gestureSettings.toString(), + 'GestureSettings(physicalTouchSlop: 1.0, physicalDoubleTapSlop: 2.0)', + ); }); test('GestureSettings has a correct equality', () { // don't refactor these to be const, that defeats the point! final double value = nonconst(2.0); - final GestureSettings settingsA = GestureSettings(physicalDoubleTapSlop: value, physicalTouchSlop: 1.0); - final GestureSettings settingsB = GestureSettings(physicalDoubleTapSlop: value, physicalTouchSlop: 3.0); - final GestureSettings settingsC = GestureSettings(physicalDoubleTapSlop: value, physicalTouchSlop: 1.0); + final GestureSettings settingsA = GestureSettings( + physicalDoubleTapSlop: value, + physicalTouchSlop: 1.0, + ); + final GestureSettings settingsB = GestureSettings( + physicalDoubleTapSlop: value, + physicalTouchSlop: 3.0, + ); + final GestureSettings settingsC = GestureSettings( + physicalDoubleTapSlop: value, + physicalTouchSlop: 1.0, + ); expect(settingsA, equals(settingsC)); expect(settingsC, equals(settingsA)); @@ -31,20 +46,26 @@ void main() { }); test('GestureSettings copyWith preserves already set values', () { - const GestureSettings initial = GestureSettings(physicalDoubleTapSlop: 1.0, physicalTouchSlop: 1.0); + const GestureSettings initial = GestureSettings( + physicalDoubleTapSlop: 1.0, + physicalTouchSlop: 1.0, + ); final GestureSettings copyA = initial.copyWith(); expect(copyA.physicalDoubleTapSlop, 1.0); expect(copyA.physicalTouchSlop, 1.0); - final GestureSettings copyB = copyA.copyWith(physicalDoubleTapSlop: 2.0, physicalTouchSlop: 2.0); + final GestureSettings copyB = copyA.copyWith( + physicalDoubleTapSlop: 2.0, + physicalTouchSlop: 2.0, + ); expect(copyB.physicalDoubleTapSlop, 2.0); expect(copyB.physicalTouchSlop, 2.0); }); - test('GestureSettings constructor defaults to null', () { + test('GestureSettings constructor defaults to null', () { const GestureSettings settings = GestureSettings(); expect(settings.physicalDoubleTapSlop, null); diff --git a/testing/dart/goldens.dart b/testing/dart/goldens.dart index 84addf8e20d69..2b13ec65fbbc9 100644 --- a/testing/dart/goldens.dart +++ b/testing/dart/goldens.dart @@ -18,23 +18,16 @@ const String _kSkiaGoldWorkDirectoryKey = 'kSkiaGoldWorkDirectory'; /// Contains utilities for comparing two images in memory that are expected to /// be identical, or for adding images to Skia gold for comparison. class ImageComparer { - ImageComparer._({ - required SkiaGoldClient client, - }) : _client = client; + ImageComparer._({required SkiaGoldClient client}) : _client = client; // Avoid talking to Skia gold for the force-multithreading variants. - static bool get _useSkiaGold => - !Platform.executableArguments.contains('--force-multithreading'); + static bool get _useSkiaGold => !Platform.executableArguments.contains('--force-multithreading'); /// Creates an image comparer and authorizes. - static Future create({ - bool verbose = false, - }) async { - const String workDirectoryPath = - String.fromEnvironment(_kSkiaGoldWorkDirectoryKey); + static Future create({bool verbose = false}) async { + const String workDirectoryPath = String.fromEnvironment(_kSkiaGoldWorkDirectoryKey); if (workDirectoryPath.isEmpty) { - throw UnsupportedError( - 'Using ImageComparer requries defining kSkiaGoldWorkDirectoryKey.'); + throw UnsupportedError('Using ImageComparer requries defining kSkiaGoldWorkDirectoryKey.'); } final Directory workDirectory = Directory( @@ -43,10 +36,10 @@ class ImageComparer { final Map dimensions = { 'impeller_enabled': impellerEnabled.toString(), }; - final SkiaGoldClient client = SkiaGoldClient.isAvailable() && _useSkiaGold - ? SkiaGoldClient(workDirectory, - dimensions: dimensions, verbose: verbose) - : _FakeSkiaGoldClient(workDirectory, dimensions, verbose: verbose); + final SkiaGoldClient client = + SkiaGoldClient.isAvailable() && _useSkiaGold + ? SkiaGoldClient(workDirectory, dimensions: dimensions, verbose: verbose) + : _FakeSkiaGoldClient(workDirectory, dimensions, verbose: verbose); await client.auth(); return ImageComparer._(client: client); @@ -58,16 +51,13 @@ class ImageComparer { /// /// The [fileName] must be unique. Future addGoldenImage(Image image, String fileName) async { - final ByteData data = - (await image.toByteData(format: ImageByteFormat.png))!; + final ByteData data = (await image.toByteData(format: ImageByteFormat.png))!; final File file = File(path.join(_client.workDirectory.path, fileName)) ..writeAsBytesSync(data.buffer.asUint8List()); - await _client.addImg( - fileName, - file, - screenshotSize: image.width * image.height, - ).catchError((dynamic error) { + await _client.addImg(fileName, file, screenshotSize: image.width * image.height).catchError(( + dynamic error, + ) { print('Skia gold comparison failed: $error'); throw Exception('Failed comparison: $fileName'); }); @@ -77,8 +67,7 @@ class ImageComparer { if (golden.width != testImage.width || golden.height != testImage.height) { return false; } - int getPixel(ByteData data, int x, int y) => - data.getUint32((x + y * golden.width) * 4); + int getPixel(ByteData data, int x, int y) => data.getUint32((x + y * golden.width) * 4); final ByteData goldenData = (await golden.toByteData())!; final ByteData testImageData = (await testImage.toByteData())!; for (int y = 0; y < golden.height; y++) { @@ -95,11 +84,7 @@ class ImageComparer { // TODO(dnfield): add local comparison against baseline, // https://github.com/flutter/flutter/issues/136831 class _FakeSkiaGoldClient implements SkiaGoldClient { - _FakeSkiaGoldClient( - this.workDirectory, - this.dimensions, { - this.verbose = false, - }); + _FakeSkiaGoldClient(this.workDirectory, this.dimensions, {this.verbose = false}); @override final Directory workDirectory; diff --git a/testing/dart/gpu_test.dart b/testing/dart/gpu_test.dart index 1317a5fbb4f6e..3d5c82d142791 100644 --- a/testing/dart/gpu_test.dart +++ b/testing/dart/gpu_test.dart @@ -33,8 +33,7 @@ ByteData unlitUBO(Matrix4 mvp, Vector4 color) { } gpu.RenderPipeline createUnlitRenderPipeline() { - final gpu.ShaderLibrary? library = - gpu.ShaderLibrary.fromAsset('test.shaderbundle'); + final gpu.ShaderLibrary? library = gpu.ShaderLibrary.fromAsset('test.shaderbundle'); assert(library != null); final gpu.Shader? vertex = library!['UnlitVertex']; assert(vertex != null); @@ -53,24 +52,29 @@ class RenderPassState { /// Create a simple RenderPass with simple color and depth-stencil attachments. RenderPassState createSimpleRenderPass({Vector4? clearColor}) { - final gpu.Texture? renderTexture = - gpu.gpuContext.createTexture(gpu.StorageMode.devicePrivate, 100, 100); + final gpu.Texture? renderTexture = gpu.gpuContext.createTexture( + gpu.StorageMode.devicePrivate, + 100, + 100, + ); assert(renderTexture != null); final gpu.Texture? depthStencilTexture = gpu.gpuContext.createTexture( - gpu.StorageMode.deviceTransient, 100, 100, - format: gpu.gpuContext.defaultDepthStencilFormat); + gpu.StorageMode.deviceTransient, + 100, + 100, + format: gpu.gpuContext.defaultDepthStencilFormat, + ); assert(depthStencilTexture != null); final gpu.CommandBuffer commandBuffer = gpu.gpuContext.createCommandBuffer(); final gpu.RenderTarget renderTarget = gpu.RenderTarget.singleColor( - gpu.ColorAttachment(texture: renderTexture!, clearValue: clearColor), - depthStencilAttachment: - gpu.DepthStencilAttachment(texture: depthStencilTexture!)); + gpu.ColorAttachment(texture: renderTexture!, clearValue: clearColor), + depthStencilAttachment: gpu.DepthStencilAttachment(texture: depthStencilTexture!), + ); - final gpu.RenderPass renderPass = - commandBuffer.createRenderPass(renderTarget); + final gpu.RenderPass renderPass = commandBuffer.createRenderPass(renderTarget); return RenderPassState(renderTexture, commandBuffer, renderPass); } @@ -80,35 +84,46 @@ RenderPassState createSimpleRenderPassWithMSAA() { // for most GPUs. final gpu.Texture? renderTexture = gpu.gpuContext.createTexture( - gpu.StorageMode.deviceTransient, 100, 100, - format: gpu.gpuContext.defaultColorFormat, sampleCount: 4); + gpu.StorageMode.deviceTransient, + 100, + 100, + format: gpu.gpuContext.defaultColorFormat, + sampleCount: 4, + ); assert(renderTexture != null); final gpu.Texture? depthStencilTexture = gpu.gpuContext.createTexture( - gpu.StorageMode.deviceTransient, 100, 100, - format: gpu.gpuContext.defaultDepthStencilFormat, sampleCount: 4); + gpu.StorageMode.deviceTransient, + 100, + 100, + format: gpu.gpuContext.defaultDepthStencilFormat, + sampleCount: 4, + ); assert(depthStencilTexture != null); // Create the single-sample resolve texture that live in DRAM and will be // drawn to the screen. final gpu.Texture? resolveTexture = gpu.gpuContext.createTexture( - gpu.StorageMode.devicePrivate, 100, 100, - format: gpu.gpuContext.defaultColorFormat); + gpu.StorageMode.devicePrivate, + 100, + 100, + format: gpu.gpuContext.defaultColorFormat, + ); assert(resolveTexture != null); final gpu.CommandBuffer commandBuffer = gpu.gpuContext.createCommandBuffer(); final gpu.RenderTarget renderTarget = gpu.RenderTarget.singleColor( - gpu.ColorAttachment( - texture: renderTexture!, - resolveTexture: resolveTexture, - storeAction: gpu.StoreAction.multisampleResolve), - depthStencilAttachment: - gpu.DepthStencilAttachment(texture: depthStencilTexture!)); + gpu.ColorAttachment( + texture: renderTexture!, + resolveTexture: resolveTexture, + storeAction: gpu.StoreAction.multisampleResolve, + ), + depthStencilAttachment: gpu.DepthStencilAttachment(texture: depthStencilTexture!), + ); - final gpu.RenderPass renderPass = - commandBuffer.createRenderPass(renderTarget); + final gpu.RenderPass renderPass = commandBuffer.createRenderPass(renderTarget); return RenderPassState(resolveTexture!, commandBuffer, renderPass); } @@ -119,17 +134,17 @@ void drawTriangle(RenderPassState state, Vector4 color) { state.renderPass.bindPipeline(pipeline); final gpu.HostBuffer transients = gpu.gpuContext.createHostBuffer(); - final gpu.BufferView vertices = transients.emplace(float32([ - -0.5, 0.5, // - 0.0, -0.5, // - 0.5, 0.5, // - ])); - final gpu.BufferView vertInfoData = - transients.emplace(unlitUBO(Matrix4.identity(), color)); + final gpu.BufferView vertices = transients.emplace( + float32([ + -0.5, 0.5, // + 0.0, -0.5, // + 0.5, 0.5, // + ]), + ); + final gpu.BufferView vertInfoData = transients.emplace(unlitUBO(Matrix4.identity(), color)); state.renderPass.bindVertexBuffer(vertices, 3); - final gpu.UniformSlot vertInfo = - pipeline.vertexShader.getUniformSlot('VertInfo'); + final gpu.UniformSlot vertInfo = pipeline.vertexShader.getUniformSlot('VertInfo'); state.renderPass.bindUniform(vertInfo, vertInfoData); state.renderPass.draw(); @@ -150,9 +165,9 @@ void main() async { fail('Exception thrown even though Impeller is enabled.'); } expect( - e.toString(), - contains( - 'Flutter GPU requires the Impeller rendering backend to be enabled.')); + e.toString(), + contains('Flutter GPU requires the Impeller rendering backend to be enabled.'), + ); } }); @@ -164,30 +179,33 @@ void main() async { test('HostBuffer.emplace', () async { final gpu.HostBuffer hostBuffer = gpu.gpuContext.createHostBuffer(); - final gpu.BufferView view0 = hostBuffer - .emplace(Int8List.fromList([0, 1, 2, 3]).buffer.asByteData()); + final gpu.BufferView view0 = hostBuffer.emplace( + Int8List.fromList([0, 1, 2, 3]).buffer.asByteData(), + ); expect(view0.offsetInBytes, 0); expect(view0.lengthInBytes, 4); - final gpu.BufferView view1 = hostBuffer - .emplace(Int8List.fromList([0, 1, 2, 3]).buffer.asByteData()); - expect(view1.offsetInBytes, - equals(gpu.gpuContext.minimumUniformByteAlignment)); + final gpu.BufferView view1 = hostBuffer.emplace( + Int8List.fromList([0, 1, 2, 3]).buffer.asByteData(), + ); + expect(view1.offsetInBytes, equals(gpu.gpuContext.minimumUniformByteAlignment)); expect(view1.lengthInBytes, 4); }, skip: !impellerEnabled); test('HostBuffer.reset', () async { final gpu.HostBuffer hostBuffer = gpu.gpuContext.createHostBuffer(); - final gpu.BufferView view0 = hostBuffer - .emplace(Int8List.fromList([0, 1, 2, 3]).buffer.asByteData()); + final gpu.BufferView view0 = hostBuffer.emplace( + Int8List.fromList([0, 1, 2, 3]).buffer.asByteData(), + ); expect(view0.offsetInBytes, 0); expect(view0.lengthInBytes, 4); hostBuffer.reset(); - final gpu.BufferView view1 = hostBuffer - .emplace(Int8List.fromList([0, 1, 2, 3]).buffer.asByteData()); + final gpu.BufferView view1 = hostBuffer.emplace( + Int8List.fromList([0, 1, 2, 3]).buffer.asByteData(), + ); expect(view1.offsetInBytes, 0); expect(view1.lengthInBytes, 4); }, skip: !impellerEnabled); @@ -195,75 +213,88 @@ void main() async { test('HostBuffer reuses DeviceBuffers after N frames', () async { final gpu.HostBuffer hostBuffer = gpu.gpuContext.createHostBuffer(); - final gpu.BufferView view0 = hostBuffer - .emplace(Int8List.fromList([0, 1, 2, 3]).buffer.asByteData()); + final gpu.BufferView view0 = hostBuffer.emplace( + Int8List.fromList([0, 1, 2, 3]).buffer.asByteData(), + ); for (int i = 0; i < hostBuffer.frameCount; i++) { hostBuffer.reset(); } - final gpu.BufferView view1 = hostBuffer - .emplace(Int8List.fromList([0, 1, 2, 3]).buffer.asByteData()); + final gpu.BufferView view1 = hostBuffer.emplace( + Int8List.fromList([0, 1, 2, 3]).buffer.asByteData(), + ); expect(view0.buffer, equals(view1.buffer)); }, skip: !impellerEnabled); test('GpuContext.createDeviceBuffer', () async { - final gpu.DeviceBuffer? deviceBuffer = - gpu.gpuContext.createDeviceBuffer(gpu.StorageMode.hostVisible, 4); + final gpu.DeviceBuffer? deviceBuffer = gpu.gpuContext.createDeviceBuffer( + gpu.StorageMode.hostVisible, + 4, + ); assert(deviceBuffer != null); expect(deviceBuffer!.sizeInBytes, 4); }, skip: !impellerEnabled); test('DeviceBuffer.overwrite', () async { - final gpu.DeviceBuffer? deviceBuffer = - gpu.gpuContext.createDeviceBuffer(gpu.StorageMode.hostVisible, 4); + final gpu.DeviceBuffer? deviceBuffer = gpu.gpuContext.createDeviceBuffer( + gpu.StorageMode.hostVisible, + 4, + ); assert(deviceBuffer != null); - final bool success = deviceBuffer! - .overwrite(Int8List.fromList([0, 1, 2, 3]).buffer.asByteData()); + final bool success = deviceBuffer!.overwrite( + Int8List.fromList([0, 1, 2, 3]).buffer.asByteData(), + ); deviceBuffer.flush(); expect(success, true); }, skip: !impellerEnabled); test('DeviceBuffer.overwrite fails when out of bounds', () async { - final gpu.DeviceBuffer? deviceBuffer = - gpu.gpuContext.createDeviceBuffer(gpu.StorageMode.hostVisible, 4); + final gpu.DeviceBuffer? deviceBuffer = gpu.gpuContext.createDeviceBuffer( + gpu.StorageMode.hostVisible, + 4, + ); assert(deviceBuffer != null); final bool success = deviceBuffer!.overwrite( - Int8List.fromList([0, 1, 2, 3]).buffer.asByteData(), - destinationOffsetInBytes: 1); + Int8List.fromList([0, 1, 2, 3]).buffer.asByteData(), + destinationOffsetInBytes: 1, + ); deviceBuffer.flush(); expect(success, false); }, skip: !impellerEnabled); - test('DeviceBuffer.overwrite throws for negative destination offset', - () async { - final gpu.DeviceBuffer? deviceBuffer = - gpu.gpuContext.createDeviceBuffer(gpu.StorageMode.hostVisible, 4); + test('DeviceBuffer.overwrite throws for negative destination offset', () async { + final gpu.DeviceBuffer? deviceBuffer = gpu.gpuContext.createDeviceBuffer( + gpu.StorageMode.hostVisible, + 4, + ); assert(deviceBuffer != null); try { deviceBuffer!.overwrite( - Int8List.fromList([0, 1, 2, 3]).buffer.asByteData(), - destinationOffsetInBytes: -1); + Int8List.fromList([0, 1, 2, 3]).buffer.asByteData(), + destinationOffsetInBytes: -1, + ); deviceBuffer.flush(); fail('Exception not thrown for negative destination offset.'); } catch (e) { - expect( - e.toString(), contains('destinationOffsetInBytes must be positive')); + expect(e.toString(), contains('destinationOffsetInBytes must be positive')); } }, skip: !impellerEnabled); test('GpuContext.createTexture', () async { - final gpu.Texture? texture = - gpu.gpuContext.createTexture(gpu.StorageMode.hostVisible, 100, 100); + final gpu.Texture? texture = gpu.gpuContext.createTexture( + gpu.StorageMode.hostVisible, + 100, + 100, + ); assert(texture != null); // Check the defaults. - expect( - texture!.coordinateSystem, gpu.TextureCoordinateSystem.renderToTexture); + expect(texture!.coordinateSystem, gpu.TextureCoordinateSystem.renderToTexture); expect(texture.width, 100); expect(texture.height, 100); expect(texture.storageMode, gpu.StorageMode.hostVisible); @@ -277,43 +308,48 @@ void main() async { }, skip: !impellerEnabled); test('Texture.overwrite', () async { - final gpu.Texture? texture = - gpu.gpuContext.createTexture(gpu.StorageMode.hostVisible, 2, 2); + final gpu.Texture? texture = gpu.gpuContext.createTexture(gpu.StorageMode.hostVisible, 2, 2); assert(texture != null); const ui.Color red = ui.Color.fromARGB(0xFF, 0xFF, 0, 0); const ui.Color green = ui.Color.fromARGB(0xFF, 0, 0xFF, 0); - final bool success = texture!.overwrite(Int32List.fromList( - [red.value, green.value, green.value, red.value]) - .buffer - .asByteData()); + final bool success = texture!.overwrite( + Int32List.fromList([red.value, green.value, green.value, red.value]).buffer.asByteData(), + ); expect(success, true); }, skip: !impellerEnabled); test('Texture.overwrite throws for wrong buffer size', () async { - final gpu.Texture? texture = - gpu.gpuContext.createTexture(gpu.StorageMode.hostVisible, 100, 100); + final gpu.Texture? texture = gpu.gpuContext.createTexture( + gpu.StorageMode.hostVisible, + 100, + 100, + ); assert(texture != null); const ui.Color red = ui.Color.fromARGB(0xFF, 0xFF, 0, 0); try { texture!.overwrite( - Int32List.fromList([red.value, red.value, red.value, red.value]) - .buffer - .asByteData()); + Int32List.fromList([red.value, red.value, red.value, red.value]).buffer.asByteData(), + ); fail('Exception not thrown for wrong buffer size.'); } catch (e) { expect( - e.toString(), - contains( - 'The length of sourceBytes (bytes: 16) must exactly match the size of the base mip level (bytes: 40000)')); + e.toString(), + contains( + 'The length of sourceBytes (bytes: 16) must exactly match the size of the base mip level (bytes: 40000)', + ), + ); } }, skip: !impellerEnabled); test('Texture.asImage returns a valid ui.Image handle', () async { - final gpu.Texture? texture = - gpu.gpuContext.createTexture(gpu.StorageMode.hostVisible, 100, 100); + final gpu.Texture? texture = gpu.gpuContext.createTexture( + gpu.StorageMode.hostVisible, + 100, + 100, + ); assert(texture != null); final ui.Image image = texture!.asImage(); @@ -323,8 +359,11 @@ void main() async { test('Texture.asImage throws when not shader readable', () async { final gpu.Texture? texture = gpu.gpuContext.createTexture( - gpu.StorageMode.hostVisible, 100, 100, - enableShaderReadUsage: false); + gpu.StorageMode.hostVisible, + 100, + 100, + enableShaderReadUsage: false, + ); assert(texture != null); try { @@ -332,14 +371,13 @@ void main() async { fail('Exception not thrown when not shader readable.'); } catch (e) { expect( - e.toString(), - contains( - 'Only shader readable Flutter GPU textures can be used as UI Images')); + e.toString(), + contains('Only shader readable Flutter GPU textures can be used as UI Images'), + ); } }, skip: !impellerEnabled); - test('RenderPass.setStencilReference doesnt throw for valid values', - () async { + test('RenderPass.setStencilReference doesnt throw for valid values', () async { final state = createSimpleRenderPass(); state.renderPass.setStencilReference(0); @@ -353,16 +391,14 @@ void main() async { state.renderPass.setStencilReference(-1); fail('Exception not thrown for out of bounds stencil reference.'); } catch (e) { - expect(e.toString(), - contains('The stencil reference value must be in the range')); + expect(e.toString(), contains('The stencil reference value must be in the range')); } try { state.renderPass.setStencilReference(2 << 31); fail('Exception not thrown for out of bounds stencil reference.'); } catch (e) { - expect(e.toString(), - contains('The stencil reference value must be in the range')); + expect(e.toString(), contains('The stencil reference value must be in the range')); } }, skip: !impellerEnabled); @@ -371,14 +407,16 @@ void main() async { state.renderPass.setStencilConfig(gpu.StencilConfig()); state.renderPass.setStencilConfig( - gpu.StencilConfig( - compareFunction: gpu.CompareFunction.notEqual, - depthFailureOperation: gpu.StencilOperation.decrementWrap, - depthStencilPassOperation: gpu.StencilOperation.incrementWrap, - stencilFailureOperation: gpu.StencilOperation.invert, - readMask: 0, - writeMask: 0), - targetFace: gpu.StencilFace.back); + gpu.StencilConfig( + compareFunction: gpu.CompareFunction.notEqual, + depthFailureOperation: gpu.StencilOperation.decrementWrap, + depthStencilPassOperation: gpu.StencilOperation.incrementWrap, + stencilFailureOperation: gpu.StencilOperation.invert, + readMask: 0, + writeMask: 0, + ), + targetFace: gpu.StencilFace.back, + ); }, skip: !impellerEnabled); test('RenderPass.setStencilConfig throws for invalid masks', () async { @@ -388,32 +426,26 @@ void main() async { state.renderPass.setStencilConfig(gpu.StencilConfig(readMask: -1)); fail('Exception not thrown for invalid stencil read mask.'); } catch (e) { - expect( - e.toString(), contains('The stencil read mask must be in the range')); + expect(e.toString(), contains('The stencil read mask must be in the range')); } try { - state.renderPass - .setStencilConfig(gpu.StencilConfig(readMask: 0xFFFFFFFF + 1)); + state.renderPass.setStencilConfig(gpu.StencilConfig(readMask: 0xFFFFFFFF + 1)); fail('Exception not thrown for invalid stencil read mask.'); } catch (e) { - expect( - e.toString(), contains('The stencil read mask must be in the range')); + expect(e.toString(), contains('The stencil read mask must be in the range')); } try { state.renderPass.setStencilConfig(gpu.StencilConfig(writeMask: -1)); fail('Exception not thrown for invalid stencil write mask.'); } catch (e) { - expect(e.toString(), - contains('The stencil write mask must be in the range')); + expect(e.toString(), contains('The stencil write mask must be in the range')); } try { - state.renderPass - .setStencilConfig(gpu.StencilConfig(writeMask: 0xFFFFFFFF + 1)); + state.renderPass.setStencilConfig(gpu.StencilConfig(writeMask: 0xFFFFFFFF + 1)); fail('Exception not thrown for invalid stencil write mask.'); } catch (e) { - expect(e.toString(), - contains('The stencil write mask must be in the range')); + expect(e.toString(), contains('The stencil write mask must be in the range')); } }, skip: !impellerEnabled); @@ -423,20 +455,19 @@ void main() async { final gpu.RenderPipeline pipeline = createUnlitRenderPipeline(); // Although this is a non-texture uniform slot, it'll work fine for the // purposes of testing this error. - final gpu.UniformSlot vertInfo = - pipeline.vertexShader.getUniformSlot('VertInfo'); + final gpu.UniformSlot vertInfo = pipeline.vertexShader.getUniformSlot('VertInfo'); - final gpu.Texture texture = gpu.gpuContext - .createTexture(gpu.StorageMode.deviceTransient, 100, 100)!; + final gpu.Texture texture = + gpu.gpuContext.createTexture(gpu.StorageMode.deviceTransient, 100, 100)!; try { state.renderPass.bindTexture(vertInfo, texture); fail('Exception not thrown when binding a transient texture.'); } catch (e) { expect( - e.toString(), - contains( - 'Textures with StorageMode.deviceTransient cannot be bound to a RenderPass')); + e.toString(), + contains('Textures with StorageMode.deviceTransient cannot be bound to a RenderPass'), + ); } }, skip: !impellerEnabled); @@ -455,8 +486,7 @@ void main() async { final state = createSimpleRenderPass(); final gpu.RenderPipeline pipeline = createUnlitRenderPipeline(); - final gpu.UniformSlot vertInfo = - pipeline.vertexShader.getUniformSlot('VertInfo'); + final gpu.UniformSlot vertInfo = pipeline.vertexShader.getUniformSlot('VertInfo'); final ByteData vertInfoData = float32([ 1, 0, 0, 0, // mvp @@ -465,8 +495,7 @@ void main() async { 0, 0, 0, 1, // mvp 0, 1, 0, 1, // color ]); - final uniformBuffer = - gpu.gpuContext.createDeviceBufferWithCopy(vertInfoData)!; + final uniformBuffer = gpu.gpuContext.createDeviceBufferWithCopy(vertInfoData)!; final gooduniformBufferView = gpu.BufferView( uniformBuffer, offsetInBytes: 0, @@ -512,57 +541,67 @@ void main() async { state.renderPass.setPolygonMode(gpu.PolygonMode.line); final gpu.HostBuffer transients = gpu.gpuContext.createHostBuffer(); - final gpu.BufferView vertices = transients.emplace(float32([ - -0.5, 0.5, // - 0.0, -0.5, // - 0.5, 0.5, // - ])); - final gpu.BufferView vertInfoData = transients.emplace(float32([ - 1, 0, 0, 0, // mvp - 0, 1, 0, 0, // mvp - 0, 0, 1, 0, // mvp - 0, 0, 0, 1, // mvp - 0, 1, 0, 1, // color - ])); + final gpu.BufferView vertices = transients.emplace( + float32([ + -0.5, 0.5, // + 0.0, -0.5, // + 0.5, 0.5, // + ]), + ); + final gpu.BufferView vertInfoData = transients.emplace( + float32([ + 1, 0, 0, 0, // mvp + 0, 1, 0, 0, // mvp + 0, 0, 1, 0, // mvp + 0, 0, 0, 1, // mvp + 0, 1, 0, 1, // color + ]), + ); state.renderPass.bindVertexBuffer(vertices, 3); - final gpu.UniformSlot vertInfo = - pipeline.vertexShader.getUniformSlot('VertInfo'); + final gpu.UniformSlot vertInfo = pipeline.vertexShader.getUniformSlot('VertInfo'); state.renderPass.bindUniform(vertInfo, vertInfoData); state.renderPass.draw(); state.commandBuffer.submit(); final ui.Image image = state.renderTexture.asImage(); - await comparer.addGoldenImage( - image, 'flutter_gpu_test_triangle_polygon_mode.png'); + await comparer.addGoldenImage(image, 'flutter_gpu_test_triangle_polygon_mode.png'); }, skip: !impellerEnabled); // Renders a green triangle pointing downwards, with 4xMSAA. - test('Can render triangle with MSAA', () async { - final state = createSimpleRenderPassWithMSAA(); - drawTriangle(state, Colors.lime); - state.commandBuffer.submit(); - - final ui.Image image = state.renderTexture.asImage(); - await comparer.addGoldenImage(image, 'flutter_gpu_test_triangle_msaa.png'); - }, skip: !(impellerEnabled && gpu.gpuContext.doesSupportOffscreenMSAA)); - test( - 'Rendering with MSAA throws exception when offscreen MSAA is not supported', - () async { - try { + 'Can render triangle with MSAA', + () async { final state = createSimpleRenderPassWithMSAA(); drawTriangle(state, Colors.lime); state.commandBuffer.submit(); - fail('Exception not thrown when offscreen MSAA is not supported.'); - } catch (e) { - expect( + + final ui.Image image = state.renderTexture.asImage(); + await comparer.addGoldenImage(image, 'flutter_gpu_test_triangle_msaa.png'); + }, + skip: !(impellerEnabled && gpu.gpuContext.doesSupportOffscreenMSAA), + ); + + test( + 'Rendering with MSAA throws exception when offscreen MSAA is not supported', + () async { + try { + final state = createSimpleRenderPassWithMSAA(); + drawTriangle(state, Colors.lime); + state.commandBuffer.submit(); + fail('Exception not thrown when offscreen MSAA is not supported.'); + } catch (e) { + expect( e.toString(), contains( - 'The backend does not support multisample anti-aliasing for offscreen color and stencil attachments')); - } - }, skip: !(impellerEnabled && !gpu.gpuContext.doesSupportOffscreenMSAA)); + 'The backend does not support multisample anti-aliasing for offscreen color and stencil attachments', + ), + ); + } + }, + skip: !(impellerEnabled && !gpu.gpuContext.doesSupportOffscreenMSAA), + ); // Renders a hollow green triangle pointing downwards. test('Can render hollowed out triangle using stencil ops', () async { @@ -576,31 +615,34 @@ void main() async { state.renderPass.setColorBlendEquation(gpu.ColorBlendEquation()); final gpu.HostBuffer transients = gpu.gpuContext.createHostBuffer(); - final gpu.BufferView vertices = transients.emplace(float32([ - -0.5, 0.5, // - 0.0, -0.5, // - 0.5, 0.5, // - ])); - final gpu.BufferView innerClipVertInfo = - transients.emplace(float32([ - 0.5, 0, 0, 0, // mvp - 0, 0.5, 0, 0, // mvp - 0, 0, 0.5, 0, // mvp - 0, 0, 0, 1, // mvp - 0, 1, 0, 1, // color - ])); - final gpu.BufferView outerGreenVertInfo = - transients.emplace(float32([ - 1, 0, 0, 0, // mvp - 0, 1, 0, 0, // mvp - 0, 0, 1, 0, // mvp - 0, 0, 0, 1, // mvp - 0, 1, 0, 1, // color - ])); + final gpu.BufferView vertices = transients.emplace( + float32([ + -0.5, 0.5, // + 0.0, -0.5, // + 0.5, 0.5, // + ]), + ); + final gpu.BufferView innerClipVertInfo = transients.emplace( + float32([ + 0.5, 0, 0, 0, // mvp + 0, 0.5, 0, 0, // mvp + 0, 0, 0.5, 0, // mvp + 0, 0, 0, 1, // mvp + 0, 1, 0, 1, // color + ]), + ); + final gpu.BufferView outerGreenVertInfo = transients.emplace( + float32([ + 1, 0, 0, 0, // mvp + 0, 1, 0, 0, // mvp + 0, 0, 1, 0, // mvp + 0, 0, 0, 1, // mvp + 0, 1, 0, 1, // color + ]), + ); state.renderPass.bindVertexBuffer(vertices, 3); - final gpu.UniformSlot vertInfo = - pipeline.vertexShader.getUniformSlot('VertInfo'); + final gpu.UniformSlot vertInfo = pipeline.vertexShader.getUniformSlot('VertInfo'); // First, punch out a scaled down triangle in the stencil buffer. // Since the stencil buffer is initialized to 0, we set the stencil ref to 1 @@ -610,9 +652,12 @@ void main() async { state.renderPass.bindUniform(vertInfo, innerClipVertInfo); state.renderPass.setStencilReference(1); - state.renderPass.setStencilConfig(gpu.StencilConfig( + state.renderPass.setStencilConfig( + gpu.StencilConfig( compareFunction: gpu.CompareFunction.equal, - stencilFailureOperation: gpu.StencilOperation.incrementClamp)); + stencilFailureOperation: gpu.StencilOperation.incrementClamp, + ), + ); state.renderPass.draw(); // Next, render the outer triangle with the stencil ref set to zero, so that @@ -624,15 +669,15 @@ void main() async { // we technically don't need to do this, but we do it here just to exercise // the API. state.renderPass.setStencilConfig( - gpu.StencilConfig(compareFunction: gpu.CompareFunction.equal)); + gpu.StencilConfig(compareFunction: gpu.CompareFunction.equal), + ); state.renderPass.bindUniform(vertInfo, outerGreenVertInfo); state.renderPass.draw(); state.commandBuffer.submit(); final ui.Image image = state.renderTexture.asImage(); - await comparer.addGoldenImage( - image, 'flutter_gpu_test_triangle_stencil.png'); + await comparer.addGoldenImage(image, 'flutter_gpu_test_triangle_stencil.png'); }, skip: !impellerEnabled); test('Drawing respects cull mode', () async { @@ -654,11 +699,11 @@ void main() async { final gpu.BufferView vertices = transients.emplace(float32(triangle)); void drawTriangle(Vector4 color) { - final gpu.BufferView vertInfoUboFront = - transients.emplace(unlitUBO(Matrix4.identity(), color)); + final gpu.BufferView vertInfoUboFront = transients.emplace( + unlitUBO(Matrix4.identity(), color), + ); - final gpu.UniformSlot vertInfo = - pipeline.vertexShader.getUniformSlot('VertInfo'); + final gpu.UniformSlot vertInfo = pipeline.vertexShader.getUniformSlot('VertInfo'); state.renderPass.bindVertexBuffer(vertices, 3); state.renderPass.bindUniform(vertInfo, vertInfoUboFront); state.renderPass.draw(); @@ -700,41 +745,28 @@ void main() async { state.renderPass.setPrimitiveType(gpu.PrimitiveType.lineStrip); final gpu.HostBuffer transients = gpu.gpuContext.createHostBuffer(); - final gpu.BufferView vertices = transients.emplace(float32([ - 1.0, - 0.0, - 0.5, - 0.8, - -0.5, - 0.8, - -1.0, - 0.0, - -0.5, - -0.8, - 0.5, - -0.8, - 1.0, - 0.0 - ])); - final gpu.BufferView vertInfoData = transients.emplace(float32([ - 1, 0, 0, 0, // mvp - 0, 1, 0, 0, // mvp - 0, 0, 1, 0, // mvp - 0, 0, 0, 1, // mvp - 0, 1, 0, 1, // color - ])); + final gpu.BufferView vertices = transients.emplace( + float32([1.0, 0.0, 0.5, 0.8, -0.5, 0.8, -1.0, 0.0, -0.5, -0.8, 0.5, -0.8, 1.0, 0.0]), + ); + final gpu.BufferView vertInfoData = transients.emplace( + float32([ + 1, 0, 0, 0, // mvp + 0, 1, 0, 0, // mvp + 0, 0, 1, 0, // mvp + 0, 0, 0, 1, // mvp + 0, 1, 0, 1, // color + ]), + ); state.renderPass.bindVertexBuffer(vertices, 7); - final gpu.UniformSlot vertInfo = - pipeline.vertexShader.getUniformSlot('VertInfo'); + final gpu.UniformSlot vertInfo = pipeline.vertexShader.getUniformSlot('VertInfo'); state.renderPass.bindUniform(vertInfo, vertInfoData); state.renderPass.draw(); state.commandBuffer.submit(); final ui.Image image = state.renderTexture.asImage(); - await comparer.addGoldenImage( - image, 'flutter_gpu_test_hexgon_line_strip.png'); + await comparer.addGoldenImage(image, 'flutter_gpu_test_hexgon_line_strip.png'); }, skip: !impellerEnabled); // Renders the middle part triangle using scissor. @@ -755,36 +787,31 @@ void main() async { state.renderPass.setScissor(gpu.Scissor(x: 25, width: 50, height: 100)); final gpu.HostBuffer transients = gpu.gpuContext.createHostBuffer(); - final gpu.BufferView vertices = transients.emplace(float32([ - -1.0, - -1.0, - 0.0, - 1.0, - 1.0, - -1.0])); - final gpu.BufferView vertInfoData = transients.emplace(float32([ - 1, 0, 0, 0, // mvp - 0, 1, 0, 0, // mvp - 0, 0, 1, 0, // mvp - 0, 0, 0, 1, // mvp - 0, 1, 0, 1, // color - ])); + final gpu.BufferView vertices = transients.emplace( + float32([-1.0, -1.0, 0.0, 1.0, 1.0, -1.0]), + ); + final gpu.BufferView vertInfoData = transients.emplace( + float32([ + 1, 0, 0, 0, // mvp + 0, 1, 0, 0, // mvp + 0, 0, 1, 0, // mvp + 0, 0, 0, 1, // mvp + 0, 1, 0, 1, // color + ]), + ); state.renderPass.bindVertexBuffer(vertices, 3); - final gpu.UniformSlot vertInfo = - pipeline.vertexShader.getUniformSlot('VertInfo'); + final gpu.UniformSlot vertInfo = pipeline.vertexShader.getUniformSlot('VertInfo'); state.renderPass.bindUniform(vertInfo, vertInfoData); state.renderPass.draw(); state.commandBuffer.submit(); final ui.Image image = state.renderTexture.asImage(); - await comparer.addGoldenImage( - image, 'flutter_gpu_test_scissor.png'); + await comparer.addGoldenImage(image, 'flutter_gpu_test_scissor.png'); }, skip: !impellerEnabled); - test('RenderPass.setScissor doesnt throw for valid values', - () async { + test('RenderPass.setScissor doesnt throw for valid values', () async { final state = createSimpleRenderPass(); state.renderPass.setScissor(gpu.Scissor(x: 25, width: 50, height: 100)); @@ -798,16 +825,14 @@ void main() async { state.renderPass.setScissor(gpu.Scissor(x: -1, width: 50, height: 100)); fail('Exception not thrown for invalid scissor.'); } catch (e) { - expect(e.toString(), - contains('Invalid values for scissor. All values should be positive.')); + expect(e.toString(), contains('Invalid values for scissor. All values should be positive.')); } try { state.renderPass.setScissor(gpu.Scissor(width: 50, height: -100)); fail('Exception not thrown for invalid scissor.'); } catch (e) { - expect(e.toString(), - contains('Invalid values for scissor. All values should be positive.')); + expect(e.toString(), contains('Invalid values for scissor. All values should be positive.')); } }, skip: !impellerEnabled); } diff --git a/testing/dart/gradient_test.dart b/testing/dart/gradient_test.dart index b274645cd02ee..ec9931599d4c3 100644 --- a/testing/dart/gradient_test.dart +++ b/testing/dart/gradient_test.dart @@ -10,60 +10,61 @@ void main() { test('Gradient.radial with no focal point', () { expect( Gradient.radial( - Offset.zero, - 0.0, - [const Color(0xFFFFFFFF), const Color(0xFFFFFFFF)], - [0.0, 1.0], - TileMode.mirror), + Offset.zero, + 0.0, + [const Color(0xFFFFFFFF), const Color(0xFFFFFFFF)], + [0.0, 1.0], + TileMode.mirror, + ), isNotNull, ); }); // this is just a radial gradient, focal point is discarded. - test('radial center and focal == Offset.zero and focalRadius == 0.0 is ok', - () { + test('radial center and focal == Offset.zero and focalRadius == 0.0 is ok', () { expect( - () => Gradient.radial( - Offset.zero, - 0.0, - [const Color(0xFFFFFFFF), const Color(0xFFFFFFFF)], - [0.0, 1.0], - TileMode.mirror, - null, - Offset.zero, - ), - isNotNull); + () => Gradient.radial( + Offset.zero, + 0.0, + [const Color(0xFFFFFFFF), const Color(0xFFFFFFFF)], + [0.0, 1.0], + TileMode.mirror, + null, + Offset.zero, + ), + isNotNull, + ); }); test('radial center != focal and focalRadius == 0.0 is ok', () { expect( - () => Gradient.radial( - Offset.zero, - 0.0, - [const Color(0xFFFFFFFF), const Color(0xFFFFFFFF)], - [0.0, 1.0], - TileMode.mirror, - null, - const Offset(2.0, 2.0), - ), - isNotNull); + () => Gradient.radial( + Offset.zero, + 0.0, + [const Color(0xFFFFFFFF), const Color(0xFFFFFFFF)], + [0.0, 1.0], + TileMode.mirror, + null, + const Offset(2.0, 2.0), + ), + isNotNull, + ); }); // this would result in div/0 on skia side. - test('radial center and focal == Offset.zero and focalRadius != 0.0 assert', - () { - expect( - () => Gradient.radial( - Offset.zero, - 0.0, - [const Color(0xFFFFFFFF), const Color(0xFFFFFFFF)], - [0.0, 1.0], - TileMode.mirror, - null, - Offset.zero, - 1.0, - ), - throwsA(isA()), - ); + test('radial center and focal == Offset.zero and focalRadius != 0.0 assert', () { + expect( + () => Gradient.radial( + Offset.zero, + 0.0, + [const Color(0xFFFFFFFF), const Color(0xFFFFFFFF)], + [0.0, 1.0], + TileMode.mirror, + null, + Offset.zero, + 1.0, + ), + throwsA(isA()), + ); }); } diff --git a/testing/dart/http_allow_http_connections_test.dart b/testing/dart/http_allow_http_connections_test.dart index 09afaae598182..5c5f2e4c0d087 100644 --- a/testing/dart/http_allow_http_connections_test.dart +++ b/testing/dart/http_allow_http_connections_test.dart @@ -21,8 +21,11 @@ void main() { final String host = await getLocalHostIP(); await bindServerAndTest(host, (HttpClient httpClient, Uri uri) async { asyncExpectThrows( - () async => runZoned(() => httpClient.getUrl(uri), - zoneValues: {#flutter.io.allow_http: false})); + () async => runZoned( + () => httpClient.getUrl(uri), + zoneValues: {#flutter.io.allow_http: false}, + ), + ); }); }); } diff --git a/testing/dart/http_disallow_http_connections_test.dart b/testing/dart/http_disallow_http_connections_test.dart index d2f9634348d55..48a545c0d3fa0 100644 --- a/testing/dart/http_disallow_http_connections_test.dart +++ b/testing/dart/http_disallow_http_connections_test.dart @@ -25,12 +25,15 @@ Future asyncExpectThrows(FutureFunction callback) async { Future getLocalHostIP() async { final List interfaces = await NetworkInterface.list( - type: InternetAddressType.IPv4); + type: InternetAddressType.IPv4, + ); return interfaces.first.addresses.first.address; } -Future bindServerAndTest(String serverHost, - Future Function(HttpClient client, Uri uri) testCode) async { +Future bindServerAndTest( + String serverHost, + Future Function(HttpClient client, Uri uri) testCode, +) async { final HttpClient httpClient = HttpClient(); final HttpServer server = await HttpServer.bind(serverHost, 0); final Uri uri = Uri(scheme: 'http', host: serverHost, port: server.port); @@ -56,39 +59,53 @@ Future _supportsIPv6() async { void main() { test('testWithLocalIP', () async { await bindServerAndTest(await getLocalHostIP(), (HttpClient httpClient, Uri httpUri) async { + asyncExpectThrows(() async => httpClient.getUrl(httpUri)); asyncExpectThrows( - () async => httpClient.getUrl(httpUri)); + () async => runZoned( + () => httpClient.getUrl(httpUri), + zoneValues: {#flutter.io.allow_http: 'foo'}, + ), + ); asyncExpectThrows( - () async => runZoned(() => httpClient.getUrl(httpUri), - zoneValues: {#flutter.io.allow_http: 'foo'})); - asyncExpectThrows( - () async => runZoned(() => httpClient.getUrl(httpUri), - zoneValues: {#flutter.io.allow_http: false})); - await runZoned(() => httpClient.getUrl(httpUri), - zoneValues: {#flutter.io.allow_http: true}); + () async => runZoned( + () => httpClient.getUrl(httpUri), + zoneValues: {#flutter.io.allow_http: false}, + ), + ); + await runZoned( + () => httpClient.getUrl(httpUri), + zoneValues: {#flutter.io.allow_http: true}, + ); }); }); test('testWithHostname', () async { await bindServerAndTest(Platform.localHostname, (HttpClient httpClient, Uri httpUri) async { - asyncExpectThrows( - () async => httpClient.getUrl(httpUri)); + asyncExpectThrows(() async => httpClient.getUrl(httpUri)); final _MockZoneValue mockFoo = _MockZoneValue('foo'); asyncExpectThrows( - () async => runZoned(() => httpClient.getUrl(httpUri), - zoneValues: {#flutter.io.allow_http: mockFoo})); + () async => runZoned( + () => httpClient.getUrl(httpUri), + zoneValues: {#flutter.io.allow_http: mockFoo}, + ), + ); expect(mockFoo.checked, isTrue); final _MockZoneValue mockFalse = _MockZoneValue(false); asyncExpectThrows( - () async => runZoned(() => httpClient.getUrl(httpUri), - zoneValues: {#flutter.io.allow_http: mockFalse})); + () async => runZoned( + () => httpClient.getUrl(httpUri), + zoneValues: {#flutter.io.allow_http: mockFalse}, + ), + ); expect(mockFalse.checked, isTrue); final _MockZoneValue mockTrue = _MockZoneValue(true); - await runZoned(() => httpClient.getUrl(httpUri), - zoneValues: {#flutter.io.allow_http: mockTrue}); + await runZoned( + () => httpClient.getUrl(httpUri), + zoneValues: {#flutter.io.allow_http: mockTrue}, + ); expect(mockFalse.checked, isTrue); }); }, skip: Platform.isMacOS); // https://github.com/flutter/flutter/issues/141149 @@ -118,10 +135,10 @@ class _MockZoneValue { @override bool operator ==(Object o) { - if(o == true) { + if (o == true) { _trueChecked = true; } - if(o == false) { + if (o == false) { _falseChecked = true; } return _value == o; diff --git a/testing/dart/image_descriptor_test.dart b/testing/dart/image_descriptor_test.dart index 8160f12bd8a8d..2e9ba876b93ee 100644 --- a/testing/dart/image_descriptor_test.dart +++ b/testing/dart/image_descriptor_test.dart @@ -84,9 +84,8 @@ void main() { }, skip: !(Platform.isAndroid || Platform.isIOS || Platform.isMacOS || Platform.isWindows)); } -Future readFile(String fileName, ) async { - final File file = - File(path.join('flutter', 'testing', 'resources', fileName)); +Future readFile(String fileName) async { + final File file = File(path.join('flutter', 'testing', 'resources', fileName)); return file.readAsBytes(); } @@ -98,7 +97,12 @@ File _getSkiaResource(String fileName) { // This is fragile and should be changed once the Platform.script issue is // resolved. final String assetPath = path.join( - 'flutter', 'third_party', 'skia', 'resources', 'images', fileName, + 'flutter', + 'third_party', + 'skia', + 'resources', + 'images', + fileName, ); return File(assetPath); } diff --git a/testing/dart/image_dispose_test.dart b/testing/dart/image_dispose_test.dart index 5612e30207024..c013d1bc8f7d1 100644 --- a/testing/dart/image_dispose_test.dart +++ b/testing/dart/image_dispose_test.dart @@ -46,7 +46,15 @@ void main() { canvas.drawImageRect(handle1, rect, rect, Paint()); canvas.drawImageNine(handle1, rect, rect, Paint()); canvas.drawAtlas(handle1, [], [], [], BlendMode.src, rect, Paint()); - canvas.drawRawAtlas(handle1, Float32List(0), Float32List(0), Int32List(0), BlendMode.src, rect, Paint()); + canvas.drawRawAtlas( + handle1, + Float32List(0), + Float32List(0), + Int32List(0), + BlendMode.src, + rect, + Paint(), + ); final Picture picture = recorder.endRecording(); @@ -121,11 +129,6 @@ void main() { } Future _readFile(String fileName) async { - final File file = File(path.join( - 'flutter', - 'testing', - 'resources', - fileName, - )); + final File file = File(path.join('flutter', 'testing', 'resources', fileName)); return file.readAsBytes(); } diff --git a/testing/dart/image_events_test.dart b/testing/dart/image_events_test.dart index 1d4bfb429eb86..424c21bf09504 100644 --- a/testing/dart/image_events_test.dart +++ b/testing/dart/image_events_test.dart @@ -22,14 +22,18 @@ void main() { disposedImage = image; }; - final Image image1 = await _createImage()..dispose(); + final Image image1 = + await _createImage() + ..dispose(); expect(onCreateInvokedCount, 1); expect(createdImage, image1); expect(onDisposeInvokedCount, 1); expect(disposedImage, image1); - final Image image2 = await _createImage()..dispose(); + final Image image2 = + await _createImage() + ..dispose(); expect(onCreateInvokedCount, 2); expect(createdImage, image2); diff --git a/testing/dart/image_filter_test.dart b/testing/dart/image_filter_test.dart index 6216acb2c8fb5..a73e53b8c31fc 100644 --- a/testing/dart/image_filter_test.dart +++ b/testing/dart/image_filter_test.dart @@ -23,24 +23,72 @@ const int greenSideScaled = 0x80005500; const int greenCornerScaled = 0x40002B00; const List grayscaleColorMatrix = [ - 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, + 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, ]; const List constValueColorMatrix = [ - 0, 0, 0, 0, 2, - 0, 0, 0, 0, 2, - 0, 0, 0, 0, 2, - 0, 0, 0, 0, 255, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + 0, + 0, + 255, ]; const List halvesBrightnessColorMatrix = [ - 0.5, 0, 0, 0, 0, - 0, 0.5, 0, 0, 0, - 0, 0, 0.5, 0, 0, - 0, 0, 0, 1, 0, + 0.5, + 0, + 0, + 0, + 0, + 0, + 0.5, + 0, + 0, + 0, + 0, + 0, + 0.5, + 0, + 0, + 0, + 0, + 0, + 1, + 0, ]; void main() async { @@ -69,37 +117,56 @@ void main() async { } ImageFilter makeBlur(double sigmaX, double sigmaY, [TileMode? tileMode]) => - ImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode); + ImageFilter.blur(sigmaX: sigmaX, sigmaY: sigmaY, tileMode: tileMode); ImageFilter makeDilate(double radiusX, double radiusY) => - ImageFilter.dilate(radiusX: radiusX, radiusY: radiusY); + ImageFilter.dilate(radiusX: radiusX, radiusY: radiusY); ImageFilter makeErode(double radiusX, double radiusY) => - ImageFilter.erode(radiusX: radiusX, radiusY: radiusY); - - ImageFilter makeScale(double scX, double scY, - [double trX = 0.0, double trY = 0.0, - FilterQuality quality = FilterQuality.low]) { + ImageFilter.erode(radiusX: radiusX, radiusY: radiusY); + + ImageFilter makeScale( + double scX, + double scY, [ + double trX = 0.0, + double trY = 0.0, + FilterQuality quality = FilterQuality.low, + ]) { trX *= 1.0 - scX; trY *= 1.0 - scY; - return ImageFilter.matrix(Float64List.fromList([ - scX, 0.0, 0.0, 0.0, - 0.0, scY, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - trX, trY, 0.0, 1.0, - ]), filterQuality: quality); + return ImageFilter.matrix( + Float64List.fromList([ + scX, + 0.0, + 0.0, + 0.0, + 0.0, + scY, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + trX, + trY, + 0.0, + 1.0, + ]), + filterQuality: quality, + ); } List colorFilters() { // Create new color filter instances on each invocation. // ignore: prefer_const_constructors - return [ - ColorFilter.mode(green, BlendMode.color), // ignore: prefer_const_constructors - ColorFilter.mode(red, BlendMode.color), // ignore: prefer_const_constructors - ColorFilter.mode(red, BlendMode.screen), // ignore: prefer_const_constructors - ColorFilter.matrix(grayscaleColorMatrix), // ignore: prefer_const_constructors - ColorFilter.linearToSrgbGamma(), // ignore: prefer_const_constructors - ColorFilter.srgbToLinearGamma(), // ignore: prefer_const_constructors + return [ + ColorFilter.mode(green, BlendMode.color), // ignore: prefer_const_constructors + ColorFilter.mode(red, BlendMode.color), // ignore: prefer_const_constructors + ColorFilter.mode(red, BlendMode.screen), // ignore: prefer_const_constructors + ColorFilter.matrix(grayscaleColorMatrix), // ignore: prefer_const_constructors + ColorFilter.linearToSrgbGamma(), // ignore: prefer_const_constructors + ColorFilter.srgbToLinearGamma(), // ignore: prefer_const_constructors ]; } @@ -131,7 +198,7 @@ void main() async { void checkEquality(List a, List b) { for (int i = 0; i < a.length; i++) { - for(int j = 0; j < a.length; j++) { + for (int j = 0; j < a.length; j++) { if (i == j) { expect(a[i], equals(b[j])); expect(a[i].hashCode, equals(b[j].hashCode)); @@ -146,7 +213,10 @@ void main() async { } List composed(List a, List b) { - return [for (final ImageFilter x in a) for (final ImageFilter y in b) ImageFilter.compose(outer: x, inner: y)]; + return [ + for (final ImageFilter x in a) + for (final ImageFilter y in b) ImageFilter.compose(outer: x, inner: y), + ]; } test('ImageFilter - equals', () async { @@ -179,16 +249,16 @@ void main() async { print('Disabled - see https://github.com/flutter/flutter/issues/135712'); return; } - final Paint paint = Paint() - ..color = green - ..imageFilter = makeBlur(1.0, 1.0, TileMode.decal); + final Paint paint = + Paint() + ..color = green + ..imageFilter = makeBlur(1.0, 1.0, TileMode.decal); final Uint32List bytes = await getBytesForPaint(paint); checkBytes(bytes, greenCenterBlurred, greenSideBlurred, greenCornerBlurred); }); test('ImageFilter - blur toString', () async { - var filter = makeBlur(1.9, 2.1); expect(filter.toString(), 'ImageFilter.blur(1.9, 2.1, unspecified)'); @@ -206,18 +276,20 @@ void main() async { }); test('ImageFilter - dilate', () async { - final Paint paint = Paint() - ..color = green - ..imageFilter = makeDilate(1.0, 1.0); + final Paint paint = + Paint() + ..color = green + ..imageFilter = makeDilate(1.0, 1.0); final Uint32List bytes = await getBytesForPaint(paint); checkBytes(bytes, green.value, green.value, green.value); }); test('ImageFilter - erode', () async { - final Paint paint = Paint() - ..color = green - ..imageFilter = makeErode(1.0, 1.0); + final Paint paint = + Paint() + ..color = green + ..imageFilter = makeErode(1.0, 1.0); final Uint32List bytes = await getBytesForPaint(paint); checkBytes(bytes, 0, 0, 0); @@ -229,9 +301,10 @@ void main() async { return; } - final Paint paint = Paint() - ..color = green - ..imageFilter = makeScale(2.0, 2.0, 1.5, 1.5); + final Paint paint = + Paint() + ..color = green + ..imageFilter = makeScale(2.0, 2.0, 1.5, 1.5); final Uint32List bytes = await getBytesForPaint(paint); checkBytes(bytes, greenCenterScaled, greenSideScaled, greenCornerScaled); @@ -239,10 +312,22 @@ void main() async { test('ImageFilter - matrix: copies the list', () async { final Float64List matrix = Float64List.fromList([ - 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, ]); final ImageFilter filter = ImageFilter.matrix(matrix); @@ -250,7 +335,10 @@ void main() async { // Modify the matrix. matrix[0] = 12345; - expect(filter.toString(), contains('[1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]')); + expect( + filter.toString(), + contains('[1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]'), + ); expect(filter.toString(), originalDescription); }); @@ -260,9 +348,10 @@ void main() async { return; } - final Paint paint = Paint() - ..color = green - ..imageFilter = const ColorFilter.matrix(constValueColorMatrix); + final Paint paint = + Paint() + ..color = green + ..imageFilter = const ColorFilter.matrix(constValueColorMatrix); final Uint32List bytes = await getBytesForColorPaint(paint); expect(bytes[0], 0xFF020202); @@ -284,9 +373,10 @@ void main() async { inner: const ColorFilter.matrix(halvesBrightnessColorMatrix), ); - final Paint paint = Paint() - ..color = green - ..imageFilter = compOrder1; + final Paint paint = + Paint() + ..color = green + ..imageFilter = compOrder1; Uint32List bytes = await getBytesForColorPaint(paint); expect(bytes[0], 0xFF010101); @@ -300,14 +390,20 @@ void main() async { test('Composite ImageFilter toString', () { expect( - ImageFilter.compose(outer: makeBlur(20.0, 20.0, TileMode.decal), inner: makeBlur(10.0, 10.0)).toString(), + ImageFilter.compose( + outer: makeBlur(20.0, 20.0, TileMode.decal), + inner: makeBlur(10.0, 10.0), + ).toString(), contains('blur(10.0, 10.0, unspecified) -> blur(20.0, 20.0, decal)'), ); // Produces a flat list of filters expect( ImageFilter.compose( - outer: ImageFilter.compose(outer: makeBlur(30.0, 30.0, TileMode.mirror), inner: makeBlur(20.0, 20.0, TileMode.repeated)), + outer: ImageFilter.compose( + outer: makeBlur(30.0, 30.0, TileMode.mirror), + inner: makeBlur(20.0, 20.0, TileMode.repeated), + ), inner: ImageFilter.compose( outer: const ColorFilter.mode(Color(0xFFABCDEF), BlendMode.color), inner: makeScale(10.0, 10.0), @@ -317,7 +413,7 @@ void main() async { 'matrix([10.0, 0.0, 0.0, 0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -0.0, -0.0, 0.0, 1.0], FilterQuality.low) -> ' 'ColorFilter.mode(${const Color(0xFFABCDEF)}, BlendMode.color) -> ' 'blur(20.0, 20.0, repeated) -> ' - 'blur(30.0, 30.0, mirror)' + 'blur(30.0, 30.0, mirror)', ), ); }); @@ -325,10 +421,7 @@ void main() async { // Tests that FilterQuality. produces the expected golden file. group('ImageFilter|FilterQuality', () { /// Draw a red-green checkerboard pattern with 1x1 squares (pixels). - Future drawCheckerboard({ - int width = 100, - int height = 100, - }) async { + Future drawCheckerboard({int width = 100, int height = 100}) async { final Completer completer = Completer(); final Uint32List pixels = Uint32List.fromList( List.generate(width * height, (int index) { @@ -377,7 +470,10 @@ void main() async { final ImageComparer comparer = await ImageComparer.create(); final Image base = await redGreenCheckerboard; final Image scaled = await shrinkAndScaleImage(base, FilterQuality.none); - await comparer.addGoldenImage(scaled, 'dart_ui_filter_quality_none_scale_1x1_red_green_checkerboard.png'); + await comparer.addGoldenImage( + scaled, + 'dart_ui_filter_quality_none_scale_1x1_red_green_checkerboard.png', + ); }); }); } diff --git a/testing/dart/image_resize_test.dart b/testing/dart/image_resize_test.dart index 839978d44f048..262966668e6d9 100644 --- a/testing/dart/image_resize_test.dart +++ b/testing/dart/image_resize_test.dart @@ -63,8 +63,7 @@ void main() { test('upscale image varying width and height', () async { final Uint8List bytes = await readFile('2x2.png'); - final Codec codec = - await instantiateImageCodec(bytes, targetWidth: 10, targetHeight: 1); + final Codec codec = await instantiateImageCodec(bytes, targetWidth: 10, targetHeight: 1); final FrameInfo frame = await codec.getNextFrame(); final int codecHeight = frame.image.height; final int codecWidth = frame.image.width; @@ -74,8 +73,12 @@ void main() { test('upscale image varying width and height - no upscaling', () async { final Uint8List bytes = await readFile('2x2.png'); - final Codec codec = - await instantiateImageCodec(bytes, targetWidth: 10, targetHeight: 1, allowUpscaling: false); + final Codec codec = await instantiateImageCodec( + bytes, + targetWidth: 10, + targetHeight: 1, + allowUpscaling: false, + ); final FrameInfo frame = await codec.getNextFrame(); final int codecHeight = frame.image.height; final int codecWidth = frame.image.width; @@ -128,8 +131,11 @@ void main() { test('pixels: upscale image varying width and height', () async { final BlackSquare blackSquare = BlackSquare.create(); - final Image resized = - await blackSquare.resize(targetHeight: 1, targetWidth: 10, allowUpscaling: true); + final Image resized = await blackSquare.resize( + targetHeight: 1, + targetWidth: 10, + allowUpscaling: true, + ); expect(resized.height, 1); expect(resized.width, 10); }); @@ -162,8 +168,7 @@ class BlackSquare { BlackSquare._(this.width, this.height, this.pixels); factory BlackSquare.create({int width = 2, int height = 2}) { - final Uint8List pixels = - Uint8List.fromList(List.filled(width * height * 4, 0)); + final Uint8List pixels = Uint8List.fromList(List.filled(width * height * 4, 0)); return BlackSquare._(width, height, pixels); } @@ -188,7 +193,6 @@ class BlackSquare { } Future readFile(String fileName) async { - final File file = - File(path.join('flutter', 'testing', 'resources', fileName)); + final File file = File(path.join('flutter', 'testing', 'resources', fileName)); return file.readAsBytes(); } diff --git a/testing/dart/image_shader_test.dart b/testing/dart/image_shader_test.dart index 1394edfae10f0..af3c1f0c358db 100644 --- a/testing/dart/image_shader_test.dart +++ b/testing/dart/image_shader_test.dart @@ -51,7 +51,7 @@ void main() { picture.dispose(); final ImageShader shader = ImageShader(image, TileMode.clamp, TileMode.clamp, Float64List(16)); - final Paint paint = Paint()..shader=shader; + final Paint paint = Paint()..shader = shader; const Rect rect = Rect.fromLTRB(0, 0, 100, 100); testCanvas((Canvas canvas) => canvas.drawRect(rect, paint)); diff --git a/testing/dart/isolate_name_server_test.dart b/testing/dart/isolate_name_server_test.dart index 7e344bbbcf462..b819badfa78c2 100644 --- a/testing/dart/isolate_name_server_test.dart +++ b/testing/dart/isolate_name_server_test.dart @@ -69,8 +69,7 @@ void main() { // Check we can't register the same name twice. final ReceivePort receivePort2 = ReceivePort(); final SendPort sendPort2 = receivePort2.sendPort; - expect( - IsolateNameServer.registerPortWithName(sendPort2, portName), isFalse); + expect(IsolateNameServer.registerPortWithName(sendPort2, portName), isFalse); expect(IsolateNameServer.lookupPortByName(portName), sendPort); // Remove the mapping. @@ -78,8 +77,7 @@ void main() { expect(IsolateNameServer.lookupPortByName(portName), isNull); // Ensure registering a new port with the old name returns the new port. - expect( - IsolateNameServer.registerPortWithName(sendPort2, portName), isTrue); + expect(IsolateNameServer.registerPortWithName(sendPort2, portName), isTrue); expect(IsolateNameServer.lookupPortByName(portName), sendPort2); // Close so the test runner doesn't hang. @@ -101,31 +99,37 @@ void main() { // Test driver. final ReceivePort testReceivePort = ReceivePort(); final Completer testPortCompleter = Completer(); - testReceivePort.listen(expectAsync1((dynamic response) { - final List typedResponse = response as List; - final int code = typedResponse[0] as int; - final String message = typedResponse[1] as String; - switch (code) { - case kStartCode: - break; - case kCloseCode: - receivePort.close(); - case kDeletedCode: - expect(IsolateNameServer.lookupPortByName(portName), isNull); - // Test is done, close the last ReceivePort. - testReceivePort.close(); - case kErrorCode: - throw message; - default: - throw 'UNREACHABLE'; - } - }, count: 3), onDone: testPortCompleter.complete); + testReceivePort.listen( + expectAsync1((dynamic response) { + final List typedResponse = response as List; + final int code = typedResponse[0] as int; + final String message = typedResponse[1] as String; + switch (code) { + case kStartCode: + break; + case kCloseCode: + receivePort.close(); + case kDeletedCode: + expect(IsolateNameServer.lookupPortByName(portName), isNull); + // Test is done, close the last ReceivePort. + testReceivePort.close(); + case kErrorCode: + throw message; + default: + throw 'UNREACHABLE'; + } + }, count: 3), + onDone: testPortCompleter.complete, + ); final Completer portCompleter = Completer(); - receivePort.listen(expectAsync1((dynamic message) { - // If we don't get this message, we timeout and fail. - expect(message, portName); - }), onDone: portCompleter.complete); + receivePort.listen( + expectAsync1((dynamic message) { + // If we don't get this message, we timeout and fail. + expect(message, portName); + }), + onDone: portCompleter.complete, + ); // Run the test. await Isolate.spawn( diff --git a/testing/dart/isolate_test.dart b/testing/dart/isolate_test.dart index 379a15b069a6e..ad872353b075b 100644 --- a/testing/dart/isolate_test.dart +++ b/testing/dart/isolate_test.dart @@ -11,11 +11,7 @@ void main() { test('Invalid isolate URI', () async { bool threw = false; try { - await Isolate.spawnUri( - Uri.parse('http://127.0.0.1/foo.dart'), - [], - null, - ); + await Isolate.spawnUri(Uri.parse('http://127.0.0.1/foo.dart'), [], null); } on IsolateSpawnException { threw = true; } @@ -26,6 +22,7 @@ void main() { void callUiApi(void message) { PlatformDispatcher.instance.onReportTimings = (_) {}; } + final ReceivePort errorPort = ReceivePort(); await Isolate.spawn(callUiApi, null, onError: errorPort.sendPort); final List isolateError = await errorPort.first as List; diff --git a/testing/dart/lerp_test.dart b/testing/dart/lerp_test.dart index e02aa660758eb..b5259c0f14d30 100644 --- a/testing/dart/lerp_test.dart +++ b/testing/dart/lerp_test.dart @@ -108,10 +108,13 @@ void main() { expect(() => lerpDouble(0.0, 10.0, double.nan), throwsA(isA())); }); - test('lerpDouble should throw AssertionError if interpolation value is +/- infinity and a != b', () { - expect(() => lerpDouble(0.0, 10.0, double.infinity), throwsA(isA())); - expect(() => lerpDouble(0.0, 10.0, -double.infinity), throwsA(isA())); - }); + test( + 'lerpDouble should throw AssertionError if interpolation value is +/- infinity and a != b', + () { + expect(() => lerpDouble(0.0, 10.0, double.infinity), throwsA(isA())); + expect(() => lerpDouble(0.0, 10.0, -double.infinity), throwsA(isA())); + }, + ); test('lerpDouble should throw AssertionError if either start or end are NaN', () { expect(() => lerpDouble(double.nan, 10.0, 5.0), throwsA(isA())); diff --git a/testing/dart/locale_test.dart b/testing/dart/locale_test.dart index d5e3cef187974..55d7367cfd9f0 100644 --- a/testing/dart/locale_test.dart +++ b/testing/dart/locale_test.dart @@ -33,21 +33,42 @@ void main() { expect(const Locale.fromSubtags(countryCode: 'US').toString(), 'und_US'); expect(const Locale.fromSubtags(countryCode: 'US').countryCode, 'US'); - expect(const Locale.fromSubtags(languageCode: 'es', countryCode: '419').toLanguageTag(), 'es-419'); + expect( + const Locale.fromSubtags(languageCode: 'es', countryCode: '419').toLanguageTag(), + 'es-419', + ); expect(const Locale.fromSubtags(languageCode: 'es', countryCode: '419').toString(), 'es_419'); expect(const Locale.fromSubtags(languageCode: 'es', countryCode: '419').languageCode, 'es'); expect(const Locale.fromSubtags(languageCode: 'es', countryCode: '419').scriptCode, null); expect(const Locale.fromSubtags(languageCode: 'es', countryCode: '419').countryCode, '419'); - expect(const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN').toLanguageTag(), 'zh-Hans-CN'); - expect(const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN').toString(), 'zh_Hans_CN'); + expect( + const Locale.fromSubtags( + languageCode: 'zh', + scriptCode: 'Hans', + countryCode: 'CN', + ).toLanguageTag(), + 'zh-Hans-CN', + ); + expect( + const Locale.fromSubtags( + languageCode: 'zh', + scriptCode: 'Hans', + countryCode: 'CN', + ).toString(), + 'zh_Hans_CN', + ); }); test('Locale equality', () { - expect(const Locale.fromSubtags(languageCode: 'en'), - isNot(const Locale.fromSubtags(languageCode: 'en', scriptCode: 'Latn'))); - expect(const Locale.fromSubtags(languageCode: 'en').hashCode, - isNot(const Locale.fromSubtags(languageCode: 'en', scriptCode: 'Latn').hashCode)); + expect( + const Locale.fromSubtags(languageCode: 'en'), + isNot(const Locale.fromSubtags(languageCode: 'en', scriptCode: 'Latn')), + ); + expect( + const Locale.fromSubtags(languageCode: 'en').hashCode, + isNot(const Locale.fromSubtags(languageCode: 'en', scriptCode: 'Latn').hashCode), + ); expect(const Locale('en', ''), const Locale('en')); expect(const Locale('en'), const Locale('en', '')); diff --git a/testing/dart/mask_filter_test.dart b/testing/dart/mask_filter_test.dart index 8e65e01a98791..1ab021770623b 100644 --- a/testing/dart/mask_filter_test.dart +++ b/testing/dart/mask_filter_test.dart @@ -10,9 +10,10 @@ void main() { test('MaskFilter - NOP blur does not crash', () async { final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - final Paint paint = Paint() - ..color = const Color(0xff00AA00) - ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 0); + final Paint paint = + Paint() + ..color = const Color(0xff00AA00) + ..maskFilter = const MaskFilter.blur(BlurStyle.normal, 0); canvas.saveLayer(const Rect.fromLTRB(-100, -100, 200, 200), paint); canvas.drawRect(const Rect.fromLTRB(0, 0, 100, 100), Paint()); canvas.restore(); diff --git a/testing/dart/observatory/shader_reload_test.dart b/testing/dart/observatory/shader_reload_test.dart index bcd70818fe7ab..f66935b37e3e7 100644 --- a/testing/dart/observatory/shader_reload_test.dart +++ b/testing/dart/observatory/shader_reload_test.dart @@ -20,9 +20,7 @@ void main() { vms.VmService? vmService; FragmentShader? shader; try { - final FragmentProgram program = await FragmentProgram.fromAsset( - 'functions.frag.iplr', - ); + final FragmentProgram program = await FragmentProgram.fromAsset('functions.frag.iplr'); shader = program.fragmentShader()..setFloat(0, 1.0); _use(shader); @@ -42,9 +40,7 @@ void main() { final vms.Response response = await vmService.callServiceExtension( 'ext.ui.window.reinitializeShader', isolateId: isolateRef.id, - args: { - 'assetKey': 'functions.frag.iplr', - }, + args: {'assetKey': 'functions.frag.iplr'}, ); expect(response.type == 'Success', true); } @@ -55,6 +51,4 @@ void main() { }); } -void _use(Shader shader) { - -} +void _use(Shader shader) {} diff --git a/testing/dart/observatory/skp_test.dart b/testing/dart/observatory/skp_test.dart index b63d46e21ccbd..7ba1b84bad5ff 100644 --- a/testing/dart/observatory/skp_test.dart +++ b/testing/dart/observatory/skp_test.dart @@ -57,7 +57,6 @@ void main() { expect(e.toString(), contains('Cannot capture SKP screenshot with Impeller enabled.')); } - await vmService.dispose(); }); } diff --git a/testing/dart/observatory/tracing_test.dart b/testing/dart/observatory/tracing_test.dart index 4fe5751024c02..00027a662e113 100644 --- a/testing/dart/observatory/tracing_test.dart +++ b/testing/dart/observatory/tracing_test.dart @@ -42,8 +42,7 @@ Future _testChromeFormatTrace(vms.VmService vmService) async { Future _testPerfettoFormatTrace(vms.VmService vmService) async { final vms.PerfettoTimeline response = await vmService.getPerfettoVMTimeline(); - final List packets = - Trace.fromBuffer(base64Decode(response.trace!)).packet; + final List packets = Trace.fromBuffer(base64Decode(response.trace!)).packet; final Iterable events = packets .where((TracePacket packet) => packet.hasTrackEvent()) .map((TracePacket packet) => packet.trackEvent); diff --git a/testing/dart/observatory/vmservice_methods_test.dart b/testing/dart/observatory/vmservice_methods_test.dart index 34d2f3c9232ca..2364fa9f505f0 100644 --- a/testing/dart/observatory/vmservice_methods_test.dart +++ b/testing/dart/observatory/vmservice_methods_test.dart @@ -43,7 +43,6 @@ void main() { } }); - test('Can return whether or not impeller is enabled', () async { vms.VmService? vmService; try { @@ -70,21 +69,20 @@ void main() { test('Reload fonts request sends font change notification', () async { vms.VmService? vmService; try { - final developer.ServiceProtocolInfo info = - await developer.Service.getInfo(); + final developer.ServiceProtocolInfo info = await developer.Service.getInfo(); if (info.serverUri == null) { fail('This test must not be run with --disable-vm-service.'); } final Completer completer = Completer(); - ui.channelBuffers.setListener( - 'flutter/system', - (ByteData? data, ui.PlatformMessageResponseCallback callback) { - final ByteBuffer buffer = data!.buffer; - final Uint8List list = buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - completer.complete(utf8.decode(list)); - }, - ); + ui.channelBuffers.setListener('flutter/system', ( + ByteData? data, + ui.PlatformMessageResponseCallback callback, + ) { + final ByteBuffer buffer = data!.buffer; + final Uint8List list = buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); + completer.complete(utf8.decode(list)); + }); vmService = await vmServiceConnectUri( 'ws://localhost:${info.serverUri!.port}${info.serverUri!.path}ws', @@ -97,10 +95,7 @@ void main() { ); expect(fontChangeResponse.type, 'Success'); - expect( - await completer.future, - '{"type":"fontsChange"}', - ); + expect(await completer.future, '{"type":"fontsChange"}'); } finally { await vmService?.dispose(); ui.channelBuffers.clearListener('flutter/system'); diff --git a/testing/dart/painting_test.dart b/testing/dart/painting_test.dart index 58ea2a5dd0fb5..ea9ae161b0423 100644 --- a/testing/dart/painting_test.dart +++ b/testing/dart/painting_test.dart @@ -15,33 +15,53 @@ typedef CanvasCallback = void Function(Canvas canvas); void main() { test('Vertices checks', () { try { - Vertices( - VertexMode.triangles, - const [Offset.zero, Offset.zero, Offset.zero], - indices: Uint16List.fromList(const [0, 2, 5]), - ); + Vertices(VertexMode.triangles, const [ + Offset.zero, + Offset.zero, + Offset.zero, + ], indices: Uint16List.fromList(const [0, 2, 5])); throw 'Vertices did not throw the expected error.'; } on ArgumentError catch (e) { - expect('$e', 'Invalid argument(s): "indices" values must be valid indices in the positions list (i.e. numbers in the range 0..2), but indices[2] is 5, which is too big.'); + expect( + '$e', + 'Invalid argument(s): "indices" values must be valid indices in the positions list (i.e. numbers in the range 0..2), but indices[2] is 5, which is too big.', + ); } - Vertices( // This one does not throw. + Vertices( + // This one does not throw. VertexMode.triangles, const [Offset.zero], ).dispose(); - Vertices( // This one should not throw. + Vertices( + // This one should not throw. VertexMode.triangles, const [Offset.zero, Offset.zero, Offset.zero], - indices: Uint16List.fromList(const [0, 2, 1, 2, 0, 1, 2, 0]), // Uint16List implements List so this is ok. + indices: Uint16List.fromList(const [ + 0, + 2, + 1, + 2, + 0, + 1, + 2, + 0, + ]), // Uint16List implements List so this is ok. ).dispose(); }); test('Vertices.raw checks', () { - expect(() { - Vertices.raw( - VertexMode.triangles, - Float32List.fromList(const [0.0]), - ); - }, throwsA(isA().having((ArgumentError e) => '$e', 'message', 'Invalid argument(s): "positions" must have an even number of entries (each coordinate is an x,y pair).'))); + expect( + () { + Vertices.raw(VertexMode.triangles, Float32List.fromList(const [0.0])); + }, + throwsA( + isA().having( + (ArgumentError e) => '$e', + 'message', + 'Invalid argument(s): "positions" must have an even number of entries (each coordinate is an x,y pair).', + ), + ), + ); Object? indicesError; try { @@ -54,13 +74,18 @@ void main() { } on ArgumentError catch (e) { indicesError = e; } - expect('$indicesError', 'Invalid argument(s): "indices" values must be valid indices in the positions list (i.e. numbers in the range 0..2), but indices[2] is 5, which is too big.'); + expect( + '$indicesError', + 'Invalid argument(s): "indices" values must be valid indices in the positions list (i.e. numbers in the range 0..2), but indices[2] is 5, which is too big.', + ); - Vertices.raw( // This one does not throw. + Vertices.raw( + // This one does not throw. VertexMode.triangles, Float32List.fromList(const [0.0, 0.0]), ).dispose(); - Vertices.raw( // This one should not throw. + Vertices.raw( + // This one should not throw. VertexMode.triangles, Float32List.fromList(const [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]), indices: Uint16List.fromList(const [0, 2, 1, 2, 0, 1, 2, 0]), @@ -75,6 +100,7 @@ void main() { callback(canvas); return recorder.endRecording(); } + final SceneBuilder sceneBuilder = SceneBuilder(); final Picture redClippedPicture = makePicture((Canvas canvas) { @@ -99,7 +125,7 @@ void main() { final ByteData data = (await image.toByteData())!; expect(data.buffer.asUint32List().length, 20 * 20); // If clipping went wrong as in the linked issue, there will be red pixels. - for (final int color in data.buffer.asUint32List()) { + for (final int color in data.buffer.asUint32List()) { expect(color, 0xFFFFFFFF); } @@ -132,11 +158,12 @@ void main() { for (int i = 0; i < count; i++) { for (int j = 0; j < count; j++) { final bool rectOdd = (i + j) & 1 == 0; - final Color fg = (i < count / 2) - ? ((j < count / 2) ? green : blue) - : ((j < count / 2) ? yellow : red); - canvas.drawRect(Rect.fromLTWH(i * rectSize, j * rectSize, rectSize, rectSize), - Paint()..color = rectOdd ? fg : white); + final Color fg = + (i < count / 2) ? ((j < count / 2) ? green : blue) : ((j < count / 2) ? yellow : red); + canvas.drawRect( + Rect.fromLTWH(i * rectSize, j * rectSize, rectSize, rectSize), + Paint()..color = rectOdd ? fg : white, + ); } } canvas.drawRect(const Rect.fromLTWH(0, 0, imgSize, 1), Paint()..color = purple); @@ -210,10 +237,7 @@ void main() { test('ImageFilter.matrix defaults to FilterQuality.medium', () { final Float64List data = Matrix4.identity().storage; - expect( - ImageFilter.matrix(data).toString(), - 'ImageFilter.matrix($data, FilterQuality.medium)', - ); + expect(ImageFilter.matrix(data).toString(), 'ImageFilter.matrix($data, FilterQuality.medium)'); }); test('Picture.toImage generates mip maps', () async { diff --git a/testing/dart/paragraph_builder_test.dart b/testing/dart/paragraph_builder_test.dart index 4e90d5d471df4..26e1db8798c5d 100644 --- a/testing/dart/paragraph_builder_test.dart +++ b/testing/dart/paragraph_builder_test.dart @@ -19,10 +19,11 @@ void main() { }); test('PushStyle should not segfault after build()', () { - final ParagraphBuilder paragraphBuilder = - ParagraphBuilder(ParagraphStyle()); + final ParagraphBuilder paragraphBuilder = ParagraphBuilder(ParagraphStyle()); paragraphBuilder.build(); - expect(() { paragraphBuilder.pushStyle(TextStyle()); }, throwsStateError); + expect(() { + paragraphBuilder.pushStyle(TextStyle()); + }, throwsStateError); }); test('GetRectsForRange smoke test', () { diff --git a/testing/dart/paragraph_test.dart b/testing/dart/paragraph_test.dart index 8e153ed603cfa..82d6c92b4ca6f 100644 --- a/testing/dart/paragraph_test.dart +++ b/testing/dart/paragraph_test.dart @@ -12,12 +12,14 @@ void main() { test('predictably lays out a single-line paragraph - Ahem', () { for (final double fontSize in [10.0, 20.0, 30.0, 40.0]) { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: fontSize, - )); + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle( + fontFamily: 'Ahem', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: fontSize, + ), + ); builder.addText('Test'); final Paragraph paragraph = builder.build(); paragraph.layout(const ParagraphConstraints(width: 400.0)); @@ -36,12 +38,14 @@ void main() { test('predictably lays out a single-line paragraph - FlutterTest', () { for (final double fontSize in [10.0, 20.0, 30.0, 40.0]) { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'FlutterTest', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: fontSize, - )); + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle( + fontFamily: 'FlutterTest', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: fontSize, + ), + ); builder.addText('Test'); final Paragraph paragraph = builder.build(); paragraph.layout(const ParagraphConstraints(width: 400.0)); @@ -57,12 +61,14 @@ void main() { test('predictably lays out a multi-line paragraph', () { for (final double fontSize in [10.0, 20.0, 30.0, 40.0]) { - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: fontSize, - )); + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle( + fontFamily: 'Ahem', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: fontSize, + ), + ); builder.addText('Test Ahem'); final Paragraph paragraph = builder.build(); paragraph.layout(ParagraphConstraints(width: fontSize * 5.0)); @@ -83,12 +89,14 @@ void main() { test('getLineBoundary', () { const double fontSize = 10.0; - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: fontSize, - )); + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle( + fontFamily: 'Ahem', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: fontSize, + ), + ); builder.addText('Test Ahem'); final Paragraph paragraph = builder.build(); paragraph.layout(const ParagraphConstraints(width: fontSize * 5.0)); @@ -96,31 +104,22 @@ void main() { // Wraps to two lines. expect(paragraph.height, closeTo(fontSize * 2.0, 0.001)); - const TextPosition wrapPositionDown = TextPosition( - offset: 5, - ); + const TextPosition wrapPositionDown = TextPosition(offset: 5); TextRange line = paragraph.getLineBoundary(wrapPositionDown); expect(line.start, 5); expect(line.end, 9); - const TextPosition wrapPositionUp = TextPosition( - offset: 5, - affinity: TextAffinity.upstream, - ); + const TextPosition wrapPositionUp = TextPosition(offset: 5, affinity: TextAffinity.upstream); line = paragraph.getLineBoundary(wrapPositionUp); expect(line.start, 0); expect(line.end, 5); - const TextPosition wrapPositionStart = TextPosition( - offset: 0, - ); + const TextPosition wrapPositionStart = TextPosition(offset: 0); line = paragraph.getLineBoundary(wrapPositionStart); expect(line.start, 0); expect(line.end, 5); - const TextPosition wrapPositionEnd = TextPosition( - offset: 9, - ); + const TextPosition wrapPositionEnd = TextPosition(offset: 9); line = paragraph.getLineBoundary(wrapPositionEnd); expect(line.start, 5); expect(line.end, 9); @@ -128,13 +127,15 @@ void main() { test('getLineBoundary RTL', () { const double fontSize = 10.0; - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: fontSize, - textDirection: TextDirection.rtl, - )); + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle( + fontFamily: 'Ahem', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: fontSize, + textDirection: TextDirection.rtl, + ), + ); builder.addText('القاهرةالقاهرة'); final Paragraph paragraph = builder.build(); paragraph.layout(const ParagraphConstraints(width: fontSize * 5.0)); @@ -142,31 +143,22 @@ void main() { // Wraps to three lines. expect(paragraph.height, closeTo(fontSize * 3.0, 0.001)); - const TextPosition wrapPositionDown = TextPosition( - offset: 5, - ); + const TextPosition wrapPositionDown = TextPosition(offset: 5); TextRange line = paragraph.getLineBoundary(wrapPositionDown); expect(line.start, 5); expect(line.end, 10); - const TextPosition wrapPositionUp = TextPosition( - offset: 5, - affinity: TextAffinity.upstream, - ); + const TextPosition wrapPositionUp = TextPosition(offset: 5, affinity: TextAffinity.upstream); line = paragraph.getLineBoundary(wrapPositionUp); expect(line.start, 0); expect(line.end, 5); - const TextPosition wrapPositionStart = TextPosition( - offset: 0, - ); + const TextPosition wrapPositionStart = TextPosition(offset: 0); line = paragraph.getLineBoundary(wrapPositionStart); expect(line.start, 0); expect(line.end, 5); - const TextPosition wrapPositionEnd = TextPosition( - offset: 9, - ); + const TextPosition wrapPositionEnd = TextPosition(offset: 9); line = paragraph.getLineBoundary(wrapPositionEnd); expect(line.start, 5); expect(line.end, 10); @@ -174,13 +166,15 @@ void main() { test('getLineBoundary empty line', () { const double fontSize = 10.0; - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'Ahem', - fontStyle: FontStyle.normal, - fontWeight: FontWeight.normal, - fontSize: fontSize, - textDirection: TextDirection.rtl, - )); + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle( + fontFamily: 'Ahem', + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + fontSize: fontSize, + textDirection: TextDirection.rtl, + ), + ); builder.addText('Test\n\nAhem'); final Paragraph paragraph = builder.build(); paragraph.layout(const ParagraphConstraints(width: fontSize * 5.0)); @@ -188,9 +182,7 @@ void main() { // Three lines due to line breaks, with the middle line being empty. expect(paragraph.height, closeTo(fontSize * 3.0, 0.001)); - const TextPosition emptyLinePosition = TextPosition( - offset: 5, - ); + const TextPosition emptyLinePosition = TextPosition(offset: 5); TextRange line = paragraph.getLineBoundary(emptyLinePosition); expect(line.start, 5); expect(line.end, 5); @@ -204,16 +196,12 @@ void main() { expect(line.start, 5); expect(line.end, 5); - const TextPosition endOfFirstLinePosition = TextPosition( - offset: 4, - ); + const TextPosition endOfFirstLinePosition = TextPosition(offset: 4); line = paragraph.getLineBoundary(endOfFirstLinePosition); expect(line.start, 0); expect(line.end, 4); - const TextPosition startOfLastLinePosition = TextPosition( - offset: 6, - ); + const TextPosition startOfLastLinePosition = TextPosition(offset: 6); line = paragraph.getLineBoundary(startOfLastLinePosition); expect(line.start, 6); expect(line.end, 10); @@ -221,11 +209,9 @@ void main() { test('getLineMetricsAt', () { const double fontSize = 10.0; - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontSize: fontSize, - textDirection: TextDirection.rtl, - height: 2.0, - )); + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle(fontSize: fontSize, textDirection: TextDirection.rtl, height: 2.0), + ); builder.addText('Test\npppp'); final Paragraph paragraph = builder.build(); paragraph.layout(const ParagraphConstraints(width: 100.0)); @@ -254,9 +240,7 @@ void main() { test('empty paragraph', () { const double fontSize = 10.0; - final Paragraph paragraph = ParagraphBuilder(ParagraphStyle( - fontSize: fontSize, - )).build(); + final Paragraph paragraph = ParagraphBuilder(ParagraphStyle(fontSize: fontSize)).build(); paragraph.layout(const ParagraphConstraints(width: double.infinity)); expect(paragraph.getClosestGlyphInfoForOffset(Offset.zero), isNull); @@ -272,11 +256,9 @@ void main() { test('OOB indices as input', () { const double fontSize = 10.0; - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontSize: fontSize, - maxLines: 1, - ellipsis: 'BBB', - ))..addText('A' * 100); + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle(fontSize: fontSize, maxLines: 1, ellipsis: 'BBB'), + )..addText('A' * 100); final Paragraph paragraph = builder.build(); paragraph.layout(const ParagraphConstraints(width: 100)); @@ -293,17 +275,21 @@ void main() { expect(paragraph.getLineMetricsAt(7), isNull); expect(paragraph.getGlyphInfoAt(-1), isNull); - expect(paragraph.getGlyphInfoAt(0)?.graphemeClusterCodeUnitRange, const TextRange(start: 0, end: 1)); - expect(paragraph.getGlyphInfoAt(6)?.graphemeClusterCodeUnitRange, const TextRange(start: 6, end: 7)); + expect( + paragraph.getGlyphInfoAt(0)?.graphemeClusterCodeUnitRange, + const TextRange(start: 0, end: 1), + ); + expect( + paragraph.getGlyphInfoAt(6)?.graphemeClusterCodeUnitRange, + const TextRange(start: 6, end: 7), + ); expect(paragraph.getGlyphInfoAt(7), isNull); expect(paragraph.getGlyphInfoAt(200), isNull); }); test('querying glyph info', () { const double fontSize = 10.0; - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontSize: fontSize, - )); + final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(fontSize: fontSize)); builder.addText('Test\nTest'); final Paragraph paragraph = builder.build(); paragraph.layout(const ParagraphConstraints(width: double.infinity)); @@ -325,7 +311,9 @@ void main() { final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - void callback() { canvas.drawParagraph(paragraph, Offset.zero); } + void callback() { + canvas.drawParagraph(paragraph, Offset.zero); + } expect(callback, throwsA(isA())); }); @@ -337,8 +325,8 @@ void main() { expect((fontSize * text.length).truncate(), isNot(fontSize * text.length)); final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(fontSize: fontSize)); builder.addText(text); - final Paragraph paragraph = builder.build() - ..layout(const ParagraphConstraints(width: text.length * fontSize)); + final Paragraph paragraph = + builder.build()..layout(const ParagraphConstraints(width: text.length * fontSize)); expect(paragraph.maxIntrinsicWidth, text.length * fontSize); switch (paragraph.computeLineMetrics()) { case [LineMetrics(width: final double width)]: @@ -350,32 +338,35 @@ void main() { test('kTextHeightNone unsets the height multiplier', () { const double fontSize = 10; - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(fontSize: fontSize, height: 10)); + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle(fontSize: fontSize, height: 10), + ); builder.pushStyle(TextStyle(height: kTextHeightNone)); builder.addText('A'); - final Paragraph paragraph = builder.build() - ..layout(const ParagraphConstraints(width: 1000)); + final Paragraph paragraph = builder.build()..layout(const ParagraphConstraints(width: 1000)); expect(paragraph.height, fontSize); }); test('kTextHeightNone ParagraphStyle', () { const double fontSize = 10; - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle(fontSize: fontSize, height: kTextHeightNone)); + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle(fontSize: fontSize, height: kTextHeightNone), + ); builder.addText('A'); - final Paragraph paragraph = builder.build() - ..layout(const ParagraphConstraints(width: 1000)); + final Paragraph paragraph = builder.build()..layout(const ParagraphConstraints(width: 1000)); expect(paragraph.height, fontSize); }); test('kTextHeightNone StrutStyle', () { const double fontSize = 10; - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontSize: 100, - strutStyle: StrutStyle(forceStrutHeight: true, height: kTextHeightNone, fontSize: fontSize), - )); + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle( + fontSize: 100, + strutStyle: StrutStyle(forceStrutHeight: true, height: kTextHeightNone, fontSize: fontSize), + ), + ); builder.addText('A'); - final Paragraph paragraph = builder.build() - ..layout(const ParagraphConstraints(width: 1000)); + final Paragraph paragraph = builder.build()..layout(const ParagraphConstraints(width: 1000)); expect(paragraph.height, fontSize); }); } diff --git a/testing/dart/path_test.dart b/testing/dart/path_test.dart index e5ca66c8f0b37..297729ce75da9 100644 --- a/testing/dart/path_test.dart +++ b/testing/dart/path_test.dart @@ -27,7 +27,11 @@ void main() { final Path difference = Path.combine(PathOperation.difference, pathCircle1, pathCircle2); expect(difference.getBounds(), equals(c1)); - final Path reverseDifference = Path.combine(PathOperation.reverseDifference, pathCircle1, pathCircle2); + final Path reverseDifference = Path.combine( + PathOperation.reverseDifference, + pathCircle1, + pathCircle2, + ); expect(reverseDifference.getBounds(), equals(c2)); final Path union = Path.combine(PathOperation.union, pathCircle1, pathCircle2); @@ -53,7 +57,11 @@ void main() { expect(difference.getBounds().top, closeTo(0.88, 0.01)); - final Path reverseDifference = Path.combine(PathOperation.reverseDifference, pathCircle1, pathCircle2); + final Path reverseDifference = Path.combine( + PathOperation.reverseDifference, + pathCircle1, + pathCircle2, + ); expect(reverseDifference.getBounds().right, closeTo(14.11, 0.01)); final Path union = Path.combine(PathOperation.union, pathCircle1, pathCircle2); @@ -98,8 +106,7 @@ void main() { expect(p.getBounds(), equals(bounds)); final Path pTransformed = p.transform(scaleMatrix); - expect(pTransformed.getBounds(), - equals(const Rect.fromLTRB(0.0, 0.0, 10 * 2.5, 10 * 0.5))); + expect(pTransformed.getBounds(), equals(const Rect.fromLTRB(0.0, 0.0, 10 * 2.5, 10 * 0.5))); final Path p2 = Path()..lineTo(10.0, 10.0); @@ -107,8 +114,7 @@ void main() { expect(p.getBounds(), equals(const Rect.fromLTRB(0.0, 0.0, 20.0, 20.0))); p.addPath(p2, const Offset(20.0, 20.0), matrix4: scaleMatrix); - expect(p.getBounds(), - equals(const Rect.fromLTRB(0.0, 0.0, 20 + (10 * 2.5), 20 + (10 * .5)))); + expect(p.getBounds(), equals(const Rect.fromLTRB(0.0, 0.0, 20 + (10 * 2.5), 20 + (10 * .5)))); p.extendWithPath(p2, Offset.zero); expect(p.getBounds(), equals(const Rect.fromLTRB(0.0, 0.0, 45.0, 25.0))); @@ -149,24 +155,28 @@ void main() { // test getTangentForOffset with vertical line final Path simpleVerticalLine = Path()..lineTo(0.0, 10.0); - final PathMetrics simpleMetricsVertical = simpleVerticalLine.computeMetrics()..iterator.moveNext(); + final PathMetrics simpleMetricsVertical = + simpleVerticalLine.computeMetrics()..iterator.moveNext(); final Tangent posTanVertical = simpleMetricsVertical.iterator.current.getTangentForOffset(5.0)!; expect(posTanVertical.position, equals(const Offset(0.0, 5.0))); expect(posTanVertical.angle, closeTo(-1.5708, .0001)); // 90 degrees // test getTangentForOffset with diagonal line final Path simpleDiagonalLine = Path()..lineTo(10.0, 10.0); - final PathMetrics simpleMetricsDiagonal = simpleDiagonalLine.computeMetrics()..iterator.moveNext(); + final PathMetrics simpleMetricsDiagonal = + simpleDiagonalLine.computeMetrics()..iterator.moveNext(); final double midPoint = simpleMetricsDiagonal.iterator.current.length / 2; - final Tangent posTanDiagonal = simpleMetricsDiagonal.iterator.current.getTangentForOffset(midPoint)!; + final Tangent posTanDiagonal = + simpleMetricsDiagonal.iterator.current.getTangentForOffset(midPoint)!; expect(posTanDiagonal.position, equals(const Offset(5.0, 5.0))); expect(posTanDiagonal.angle, closeTo(-0.7853981633974483, .00001)); // ~45 degrees // test a multi-contour path - final Path multiContour = Path() - ..lineTo(0.0, 10.0) - ..moveTo(10.0, 10.0) - ..lineTo(10.0, 15.0); + final Path multiContour = + Path() + ..lineTo(0.0, 10.0) + ..moveTo(10.0, 10.0) + ..lineTo(10.0, 15.0); final PathMetrics multiContourMetric = multiContour.computeMetrics(); expect(() => multiContourMetric.iterator.current, throwsRangeError); @@ -181,10 +191,12 @@ void main() { }); test('PathMetrics can remember lengths and isClosed', () { - final Path path = Path()..lineTo(0, 10) - ..close() - ..moveTo(0, 15) - ..lineTo(10, 15); + final Path path = + Path() + ..lineTo(0, 10) + ..close() + ..moveTo(0, 15) + ..lineTo(10, 15); final List metrics = path.computeMetrics().toList(); expect(metrics.length, 2); expect(metrics[0].length, 20); @@ -208,7 +220,10 @@ void main() { expect(firstMetric.getTangentForOffset(4.0)!.vector, const Offset(0.0, 1.0)); expect(firstMetric.extractPath(4.0, 10.0).computeMetrics().first.length, 6.0); - path..lineTo(10, 10)..lineTo(10, 0)..close(); + path + ..lineTo(10, 10) + ..lineTo(10, 0) + ..close(); // mutating the path shouldn't have added anything to the iterator. expect(metrics, isEmpty); expect(firstMetric.length, 10); @@ -227,17 +242,19 @@ void main() { }); test('PathMetrics on a mutated path', () { - final Path path = Path() - ..lineTo(0, 30) - ..lineTo(40, 30) - ..moveTo(100, 0) - ..lineTo(100, 30) - ..lineTo(140, 30) - ..close(); + final Path path = + Path() + ..lineTo(0, 30) + ..lineTo(40, 30) + ..moveTo(100, 0) + ..lineTo(100, 30) + ..lineTo(140, 30) + ..close(); final PathMetrics metrics = path.computeMetrics(); - expect(metrics.toString(), + expect( + metrics.toString(), '(PathMetric(length: 70.0, isClosed: false, contourIndex: 0), ' - 'PathMetric(length: 120.0, isClosed: true, contourIndex: 1))', + 'PathMetric(length: 120.0, isClosed: true, contourIndex: 1))', ); }); } diff --git a/testing/dart/picture_test.dart b/testing/dart/picture_test.dart index d1472e97bd324..e18b0b3b93340 100644 --- a/testing/dart/picture_test.dart +++ b/testing/dart/picture_test.dart @@ -30,8 +30,7 @@ void main() { test('approximateBytesUsed is available for onCreate', () async { int pictureSize = -1; - Picture.onCreate = (Picture picture) => - pictureSize = picture.approximateBytesUsed; + Picture.onCreate = (Picture picture) => pictureSize = picture.approximateBytesUsed; _createPicture(); diff --git a/testing/dart/platform_dispatcher_test.dart b/testing/dart/platform_dispatcher_test.dart index c6b4546ba094c..7eedb36a4a22a 100644 --- a/testing/dart/platform_dispatcher_test.dart +++ b/testing/dart/platform_dispatcher_test.dart @@ -34,9 +34,13 @@ void main() { expect(defaultValues / 2, const ViewConstraints()); }); - test('ViewConstraints', () { - const ViewConstraints constraints = ViewConstraints(minWidth: 100, maxWidth: 200, minHeight: 300, maxHeight: 400); + const ViewConstraints constraints = ViewConstraints( + minWidth: 100, + maxWidth: 200, + minHeight: 300, + maxHeight: 400, + ); expect(constraints.minWidth, 100); expect(constraints.maxWidth, 200); expect(constraints.minHeight, 300); @@ -45,30 +49,36 @@ void main() { expect(constraints.isTight, false); expect(constraints.isSatisfiedBy(const Size(200, 300)), true); expect(constraints.isSatisfiedBy(const Size(400, 500)), false); - expect(constraints / 2, const ViewConstraints(minWidth: 50, maxWidth: 100, minHeight: 150, maxHeight: 200)); + expect( + constraints / 2, + const ViewConstraints(minWidth: 50, maxWidth: 100, minHeight: 150, maxHeight: 200), + ); }); test('scheduleWarmupFrame should call both callbacks and flush microtasks', () async { bool microtaskFlushed = false; bool beginFrameCalled = false; final Completer drawFrameCalled = Completer(); - PlatformDispatcher.instance.scheduleWarmUpFrame(beginFrame: () { - expect(microtaskFlushed, false); - expect(drawFrameCalled.isCompleted, false); - expect(beginFrameCalled, false); - beginFrameCalled = true; - scheduleMicrotask(() { + PlatformDispatcher.instance.scheduleWarmUpFrame( + beginFrame: () { + expect(microtaskFlushed, false); + expect(drawFrameCalled.isCompleted, false); + expect(beginFrameCalled, false); + beginFrameCalled = true; + scheduleMicrotask(() { + expect(microtaskFlushed, false); + expect(drawFrameCalled.isCompleted, false); + microtaskFlushed = true; + }); expect(microtaskFlushed, false); + }, + drawFrame: () { + expect(beginFrameCalled, true); + expect(microtaskFlushed, true); expect(drawFrameCalled.isCompleted, false); - microtaskFlushed = true; - }); - expect(microtaskFlushed, false); - }, drawFrame: () { - expect(beginFrameCalled, true); - expect(microtaskFlushed, true); - expect(drawFrameCalled.isCompleted, false); - drawFrameCalled.complete(); - }); + drawFrameCalled.complete(); + }, + ); await drawFrameCalled.future; expect(beginFrameCalled, true); expect(drawFrameCalled.isCompleted, true); diff --git a/testing/dart/platform_isolate_test.dart b/testing/dart/platform_isolate_test.dart index 8ffba9f5d9bc5..8d3d55c3f07ae 100644 --- a/testing/dart/platform_isolate_test.dart +++ b/testing/dart/platform_isolate_test.dart @@ -12,14 +12,12 @@ int counter = 0; void main() { test('PlatformIsolate isRunningOnPlatformThread, false cases', () async { - final bool isPlatThread = - await Isolate.run(() => isRunningOnPlatformThread); + final bool isPlatThread = await Isolate.run(() => isRunningOnPlatformThread); expect(isPlatThread, isFalse); }); test('PlatformIsolate runOnPlatformThread', () async { - final bool isPlatThread = - await runOnPlatformThread(() => isRunningOnPlatformThread); + final bool isPlatThread = await runOnPlatformThread(() => isRunningOnPlatformThread); expect(isPlatThread, isTrue); }); @@ -133,8 +131,7 @@ void main() { expect(throws, true); }); - test('PlatformIsolate runOnPlatformThread, disabled on helper isolates', - () async { + test('PlatformIsolate runOnPlatformThread, disabled on helper isolates', () async { await expectLater(() async { await Isolate.run(() { return runOnPlatformThread(() => print('Unreachable')); @@ -143,8 +140,9 @@ void main() { }); test('PlatformIsolate runOnPlatformThread, on platform isolate', () async { - final int result = await runOnPlatformThread(() => runOnPlatformThread( - () => runOnPlatformThread(() => runOnPlatformThread(() => 123)))); + final int result = await runOnPlatformThread( + () => runOnPlatformThread(() => runOnPlatformThread(() => runOnPlatformThread(() => 123))), + ); expect(result, 123); }); @@ -162,8 +160,7 @@ void main() { expect(throws, true); }); - test('PlatformIsolate runOnPlatformThread, unsendable object async', - () async { + test('PlatformIsolate runOnPlatformThread, unsendable object async', () async { bool throws = false; try { await runOnPlatformThread(() async { @@ -186,8 +183,7 @@ void main() { expect(throws, true); }); - test('PlatformIsolate runOnPlatformThread, throws unsendable async', - () async { + test('PlatformIsolate runOnPlatformThread, throws unsendable async', () async { bool throws = false; try { await runOnPlatformThread(() async { diff --git a/testing/dart/semantics_test.dart b/testing/dart/semantics_test.dart index 8b2f88921054c..55c90353d3eb2 100644 --- a/testing/dart/semantics_test.dart +++ b/testing/dart/semantics_test.dart @@ -33,10 +33,19 @@ void main() { }); test('SpellOutStringAttribute.toString', () async { - expect(SpellOutStringAttribute(range: const TextRange(start: 2, end: 5)).toString(), 'SpellOutStringAttribute(TextRange(start: 2, end: 5))'); + expect( + SpellOutStringAttribute(range: const TextRange(start: 2, end: 5)).toString(), + 'SpellOutStringAttribute(TextRange(start: 2, end: 5))', + ); }); test('LocaleStringAttribute.toString', () async { - expect(LocaleStringAttribute(range: const TextRange(start: 2, end: 5), locale: const Locale('test')).toString(), 'LocaleStringAttribute(TextRange(start: 2, end: 5), test)'); + expect( + LocaleStringAttribute( + range: const TextRange(start: 2, end: 5), + locale: const Locale('test'), + ).toString(), + 'LocaleStringAttribute(TextRange(start: 2, end: 5), test)', + ); }); } diff --git a/testing/dart/shader_test_file_utils.dart b/testing/dart/shader_test_file_utils.dart index 7acf7038f18a2..137df221ad8fd 100644 --- a/testing/dart/shader_test_file_utils.dart +++ b/testing/dart/shader_test_file_utils.dart @@ -18,9 +18,11 @@ const String _testPath = 'gen/flutter/lib/ui/fixtures/shaders'; /// /// `folderName` is a leaf folder within the generated directory. Directory shaderDirectory(String leafFolderName) { - return Directory(path.joinAll([ - ...path.split(_flutterBuildDirectoryPath()), - ...path.split(_testPath), - leafFolderName, - ])); + return Directory( + path.joinAll([ + ...path.split(_flutterBuildDirectoryPath()), + ...path.split(_testPath), + leafFolderName, + ]), + ); } diff --git a/testing/dart/spawn_test.dart b/testing/dart/spawn_test.dart index 548cfe60aad95..d5ccc669cffab 100644 --- a/testing/dart/spawn_test.dart +++ b/testing/dart/spawn_test.dart @@ -22,11 +22,7 @@ external Object _lookupEntryPoint(Pointer library, Pointer name); @Native, Pointer)>(symbol: 'Spawn') external void _spawn(Pointer entrypoint, Pointer route); -void spawn({ - required SendPort port, - String entrypoint = 'main', - String route = '/', -}) { +void spawn({required SendPort port, String entrypoint = 'main', String route = '/'}) { assert( entrypoint != 'main' || route != '/', 'Spawn should not be used to spawn main with the default route name', @@ -43,7 +39,9 @@ const String kTestEntrypointRouteName = 'testEntrypoint'; @pragma('vm:entry-point') void testEntrypoint() { - IsolateNameServer.lookupPortByName(kTestEntrypointRouteName)!.send(PlatformDispatcher.instance.defaultRouteName); + IsolateNameServer.lookupPortByName( + kTestEntrypointRouteName, + )!.send(PlatformDispatcher.instance.defaultRouteName); } void main() { @@ -58,25 +56,20 @@ void main() { }); test('Lookup entrypoint and execute', () { - final Pointer libraryPath = 'file://${const String.fromEnvironment('kFlutterSrcDirectory')}/testing/dart/spawn_helper.dart'.toNativeUtf8(); + final Pointer libraryPath = + 'file://${const String.fromEnvironment('kFlutterSrcDirectory')}/testing/dart/spawn_helper.dart' + .toNativeUtf8(); final Pointer entryPoint = 'echoInt'.toNativeUtf8(); - expect( - (_lookupEntryPoint( - libraryPath, - entryPoint, - ) as int Function(int))(42), - 42, - ); + expect((_lookupEntryPoint(libraryPath, entryPoint) as int Function(int))(42), 42); malloc.free(libraryPath); malloc.free(entryPoint); }); test('Load from kernel', () { - final Pointer kernelPath = '${const String.fromEnvironment('kFlutterBuildDirectory')}/spawn_helper.dart.dill'.toNativeUtf8(); - expect( - _loadLibraryFromKernel(kernelPath) is void Function(), - true, - ); + final Pointer kernelPath = + '${const String.fromEnvironment('kFlutterBuildDirectory')}/spawn_helper.dart.dill' + .toNativeUtf8(); + expect(_loadLibraryFromKernel(kernelPath) is void Function(), true); malloc.free(kernelPath); final Pointer fakePath = 'fake-path'.toNativeUtf8(); diff --git a/testing/dart/stringification_test.dart b/testing/dart/stringification_test.dart index 2a6eceee86f33..b428393ca7e44 100644 --- a/testing/dart/stringification_test.dart +++ b/testing/dart/stringification_test.dart @@ -7,9 +7,25 @@ import 'dart:ui'; import 'package:test/test.dart'; -final Uint8List imageData = Uint8List.fromList([ // Small WebP file - 0x52, 0x49, 0x46, 0x46, 0x12, 0x00, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50, 0x56, 0x50, 0x38, 0x4c, // |RIFF....WEBPVP8L| - 0x06, 0x00, 0x00, 0x00, 0x2f, 0x41, 0x6c, 0x6f, 0x00, 0x6b, // |..../Alo.k| +final Uint8List imageData = Uint8List.fromList([ + // Small WebP file + 0x52, + 0x49, + 0x46, + 0x46, + 0x12, + 0x00, + 0x00, + 0x00, + 0x57, + 0x45, + 0x42, + 0x50, + 0x56, + 0x50, + 0x38, + 0x4c, // |RIFF....WEBPVP8L| + 0x06, 0x00, 0x00, 0x00, 0x2f, 0x41, 0x6c, 0x6f, 0x00, 0x6b, // |..../Alo.k| ]); void main() { @@ -35,7 +51,8 @@ void main() { width: 1, height: 1, pixelFormat: PixelFormat.rgba8888, - ).toString(), 'ImageDescriptor(width: 1, height: 1, bytes per pixel: 4)', + ).toString(), + 'ImageDescriptor(width: 1, height: 1, bytes per pixel: 4)', ); expect(SceneBuilder().toString(), 'SceneBuilder'); expect(SceneBuilder().build().toString(), 'Scene'); diff --git a/testing/dart/text_test.dart b/testing/dart/text_test.dart index 46f176bca0d67..2db009e0be9a0 100644 --- a/testing/dart/text_test.dart +++ b/testing/dart/text_test.dart @@ -52,51 +52,109 @@ void testFontWeight() { void testParagraphStyle() { final ParagraphStyle ps0 = ParagraphStyle(textDirection: TextDirection.ltr, fontSize: 14.0); final ParagraphStyle ps1 = ParagraphStyle(textDirection: TextDirection.rtl, fontSize: 14.0); - final ParagraphStyle ps2 = ParagraphStyle(textAlign: TextAlign.center, fontWeight: FontWeight.w800, fontSize: 10.0, height: 100.0); - final ParagraphStyle ps3 = ParagraphStyle(fontWeight: FontWeight.w700, fontSize: 12.0, height: 123.0); - final ParagraphStyle ps4 = ParagraphStyle(fontWeight: FontWeight.w700, fontSize: 12.0, height: kTextHeightNone); + final ParagraphStyle ps2 = ParagraphStyle( + textAlign: TextAlign.center, + fontWeight: FontWeight.w800, + fontSize: 10.0, + height: 100.0, + ); + final ParagraphStyle ps3 = ParagraphStyle( + fontWeight: FontWeight.w700, + fontSize: 12.0, + height: 123.0, + ); + final ParagraphStyle ps4 = ParagraphStyle( + fontWeight: FontWeight.w700, + fontSize: 12.0, + height: kTextHeightNone, + ); test('ParagraphStyle toString works', () { - expect(ps0.toString(), equals('ParagraphStyle(textAlign: unspecified, textDirection: TextDirection.ltr, fontWeight: unspecified, fontStyle: unspecified, maxLines: unspecified, textHeightBehavior: unspecified, fontFamily: unspecified, fontSize: 14.0, height: unspecified, strutStyle: unspecified, ellipsis: unspecified, locale: unspecified)')); - expect(ps1.toString(), equals('ParagraphStyle(textAlign: unspecified, textDirection: TextDirection.rtl, fontWeight: unspecified, fontStyle: unspecified, maxLines: unspecified, textHeightBehavior: unspecified, fontFamily: unspecified, fontSize: 14.0, height: unspecified, strutStyle: unspecified, ellipsis: unspecified, locale: unspecified)')); - expect(ps2.toString(), equals('ParagraphStyle(textAlign: TextAlign.center, textDirection: unspecified, fontWeight: FontWeight.w800, fontStyle: unspecified, maxLines: unspecified, textHeightBehavior: unspecified, fontFamily: unspecified, fontSize: 10.0, height: 100.0x, strutStyle: unspecified, ellipsis: unspecified, locale: unspecified)')); - expect(ps3.toString(), equals('ParagraphStyle(textAlign: unspecified, textDirection: unspecified, fontWeight: FontWeight.w700, fontStyle: unspecified, maxLines: unspecified, textHeightBehavior: unspecified, fontFamily: unspecified, fontSize: 12.0, height: 123.0x, strutStyle: unspecified, ellipsis: unspecified, locale: unspecified)')); - expect(ps4.toString(), equals('ParagraphStyle(textAlign: unspecified, textDirection: unspecified, fontWeight: FontWeight.w700, fontStyle: unspecified, maxLines: unspecified, textHeightBehavior: unspecified, fontFamily: unspecified, fontSize: 12.0, height: unspecified, strutStyle: unspecified, ellipsis: unspecified, locale: unspecified)')); + expect( + ps0.toString(), + equals( + 'ParagraphStyle(textAlign: unspecified, textDirection: TextDirection.ltr, fontWeight: unspecified, fontStyle: unspecified, maxLines: unspecified, textHeightBehavior: unspecified, fontFamily: unspecified, fontSize: 14.0, height: unspecified, strutStyle: unspecified, ellipsis: unspecified, locale: unspecified)', + ), + ); + expect( + ps1.toString(), + equals( + 'ParagraphStyle(textAlign: unspecified, textDirection: TextDirection.rtl, fontWeight: unspecified, fontStyle: unspecified, maxLines: unspecified, textHeightBehavior: unspecified, fontFamily: unspecified, fontSize: 14.0, height: unspecified, strutStyle: unspecified, ellipsis: unspecified, locale: unspecified)', + ), + ); + expect( + ps2.toString(), + equals( + 'ParagraphStyle(textAlign: TextAlign.center, textDirection: unspecified, fontWeight: FontWeight.w800, fontStyle: unspecified, maxLines: unspecified, textHeightBehavior: unspecified, fontFamily: unspecified, fontSize: 10.0, height: 100.0x, strutStyle: unspecified, ellipsis: unspecified, locale: unspecified)', + ), + ); + expect( + ps3.toString(), + equals( + 'ParagraphStyle(textAlign: unspecified, textDirection: unspecified, fontWeight: FontWeight.w700, fontStyle: unspecified, maxLines: unspecified, textHeightBehavior: unspecified, fontFamily: unspecified, fontSize: 12.0, height: 123.0x, strutStyle: unspecified, ellipsis: unspecified, locale: unspecified)', + ), + ); + expect( + ps4.toString(), + equals( + 'ParagraphStyle(textAlign: unspecified, textDirection: unspecified, fontWeight: FontWeight.w700, fontStyle: unspecified, maxLines: unspecified, textHeightBehavior: unspecified, fontFamily: unspecified, fontSize: 12.0, height: unspecified, strutStyle: unspecified, ellipsis: unspecified, locale: unspecified)', + ), + ); }); } void testTextStyle() { final TextStyle ts0 = TextStyle(fontWeight: FontWeight.w700, fontSize: 12.0, height: 123.0); - final TextStyle ts1 = TextStyle(color: const Color(0xFF00FF00), fontWeight: FontWeight.w800, fontSize: 10.0, height: 100.0); + final TextStyle ts1 = TextStyle( + color: const Color(0xFF00FF00), + fontWeight: FontWeight.w800, + fontSize: 10.0, + height: 100.0, + ); final TextStyle ts2 = TextStyle(fontFamily: 'test'); - final TextStyle ts3 = TextStyle(fontFamily: 'foo', fontFamilyFallback: ['Roboto', 'test']); + final TextStyle ts3 = TextStyle( + fontFamily: 'foo', + fontFamilyFallback: ['Roboto', 'test'], + ); final TextStyle ts4 = TextStyle(leadingDistribution: TextLeadingDistribution.even); final TextStyle ts5 = TextStyle(height: kTextHeightNone); test('TextStyle toString works', () { expect( ts0.toString(), - equals('TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: FontWeight.w700, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: 12.0, letterSpacing: unspecified, wordSpacing: unspecified, height: 123.0x, leadingDistribution: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified, fontVariations: unspecified)'), + equals( + 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: FontWeight.w700, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: 12.0, letterSpacing: unspecified, wordSpacing: unspecified, height: 123.0x, leadingDistribution: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified, fontVariations: unspecified)', + ), ); expect( ts1.toString(), - equals('TextStyle(color: ${const Color(0xFF00FF00)}, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: FontWeight.w800, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: 10.0, letterSpacing: unspecified, wordSpacing: unspecified, height: 100.0x, leadingDistribution: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified, fontVariations: unspecified)'), + equals( + 'TextStyle(color: ${const Color(0xFF00FF00)}, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: FontWeight.w800, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: 10.0, letterSpacing: unspecified, wordSpacing: unspecified, height: 100.0x, leadingDistribution: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified, fontVariations: unspecified)', + ), ); expect( ts2.toString(), - equals('TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: test, fontFamilyFallback: unspecified, fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, leadingDistribution: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified, fontVariations: unspecified)'), + equals( + 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: test, fontFamilyFallback: unspecified, fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, leadingDistribution: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified, fontVariations: unspecified)', + ), ); expect( ts3.toString(), - equals('TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: foo, fontFamilyFallback: [Roboto, test], fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, leadingDistribution: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified, fontVariations: unspecified)'), + equals( + 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: foo, fontFamilyFallback: [Roboto, test], fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, leadingDistribution: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified, fontVariations: unspecified)', + ), ); expect( ts4.toString(), - equals('TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, leadingDistribution: TextLeadingDistribution.even, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified, fontVariations: unspecified)'), + equals( + 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: unspecified, leadingDistribution: TextLeadingDistribution.even, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified, fontVariations: unspecified)', + ), ); expect( ts5.toString(), - equals('TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: kTextHeightNone, leadingDistribution: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified, fontVariations: unspecified)'), + equals( + 'TextStyle(color: unspecified, decoration: unspecified, decorationColor: unspecified, decorationStyle: unspecified, decorationThickness: unspecified, fontWeight: unspecified, fontStyle: unspecified, textBaseline: unspecified, fontFamily: unspecified, fontFamilyFallback: unspecified, fontSize: unspecified, letterSpacing: unspecified, wordSpacing: unspecified, height: kTextHeightNone, leadingDistribution: unspecified, locale: unspecified, background: unspecified, foreground: unspecified, shadows: unspecified, fontFeatures: unspecified, fontVariations: unspecified)', + ), ); }); } @@ -105,14 +163,10 @@ void testTextHeightBehavior() { const TextHeightBehavior behavior0 = TextHeightBehavior(); const TextHeightBehavior behavior1 = TextHeightBehavior( applyHeightToFirstAscent: false, - applyHeightToLastDescent: false - ); - const TextHeightBehavior behavior2 = TextHeightBehavior( - applyHeightToFirstAscent: false, - ); - const TextHeightBehavior behavior3 = TextHeightBehavior( - applyHeightToLastDescent: false + applyHeightToLastDescent: false, ); + const TextHeightBehavior behavior2 = TextHeightBehavior(applyHeightToFirstAscent: false); + const TextHeightBehavior behavior3 = TextHeightBehavior(applyHeightToLastDescent: false); const TextHeightBehavior behavior4 = TextHeightBehavior( applyHeightToLastDescent: false, leadingDistribution: TextLeadingDistribution.even, @@ -136,11 +190,36 @@ void testTextHeightBehavior() { }); test('TextHeightBehavior toString works', () { - expect(behavior0.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: true, leadingDistribution: TextLeadingDistribution.proportional)')); - expect(behavior1.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: false, applyHeightToLastDescent: false, leadingDistribution: TextLeadingDistribution.proportional)')); - expect(behavior2.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: false, applyHeightToLastDescent: true, leadingDistribution: TextLeadingDistribution.proportional)')); - expect(behavior3.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: false, leadingDistribution: TextLeadingDistribution.proportional)')); - expect(behavior4.toString(), equals('TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: false, leadingDistribution: TextLeadingDistribution.even)')); + expect( + behavior0.toString(), + equals( + 'TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: true, leadingDistribution: TextLeadingDistribution.proportional)', + ), + ); + expect( + behavior1.toString(), + equals( + 'TextHeightBehavior(applyHeightToFirstAscent: false, applyHeightToLastDescent: false, leadingDistribution: TextLeadingDistribution.proportional)', + ), + ); + expect( + behavior2.toString(), + equals( + 'TextHeightBehavior(applyHeightToFirstAscent: false, applyHeightToLastDescent: true, leadingDistribution: TextLeadingDistribution.proportional)', + ), + ); + expect( + behavior3.toString(), + equals( + 'TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: false, leadingDistribution: TextLeadingDistribution.proportional)', + ), + ); + expect( + behavior4.toString(), + equals( + 'TextHeightBehavior(applyHeightToFirstAscent: true, applyHeightToLastDescent: false, leadingDistribution: TextLeadingDistribution.even)', + ), + ); }); } @@ -215,14 +294,14 @@ void testGlyphInfo() { void testLoadFontFromList() { test('loadFontFromList will send platform message after font is loaded', () async { late String message; - channelBuffers.setListener( - 'flutter/system', - (ByteData? data, PlatformMessageResponseCallback? callback) { - assert(data != null); - final Uint8List list = data!.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - message = utf8.decode(list); - }, - ); + channelBuffers.setListener('flutter/system', ( + ByteData? data, + PlatformMessageResponseCallback? callback, + ) { + assert(data != null); + final Uint8List list = data!.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); + message = utf8.decode(list); + }); final Uint8List fontData = Uint8List(0); await loadFontFromList(fontData, fontFamily: 'fake'); expect(message, '{"type":"fontsChange"}'); @@ -237,9 +316,9 @@ void testFontFeatureClass() { expect(const FontFeature.alternativeFractions(), const FontFeature('afrc')); expect(const FontFeature.contextualAlternates(), const FontFeature('calt')); expect(const FontFeature.caseSensitiveForms(), const FontFeature('case')); - expect( FontFeature.characterVariant(1), const FontFeature('cv01')); - expect( FontFeature.characterVariant(18), const FontFeature('cv18')); - expect( FontFeature.characterVariant(99), const FontFeature('cv99')); + expect(FontFeature.characterVariant(1), const FontFeature('cv01')); + expect(FontFeature.characterVariant(18), const FontFeature('cv18')); + expect(FontFeature.characterVariant(99), const FontFeature('cv99')); expect(const FontFeature.denominator(), const FontFeature('dnom')); expect(const FontFeature.fractions(), const FontFeature('frac')); expect(const FontFeature.historicalForms(), const FontFeature('hist')); @@ -257,8 +336,8 @@ void testFontFeatureClass() { expect(const FontFeature.randomize(), const FontFeature('rand')); expect(const FontFeature.stylisticAlternates(), const FontFeature('salt')); expect(const FontFeature.scientificInferiors(), const FontFeature('sinf')); - expect( FontFeature.stylisticSet(1), const FontFeature('ss01')); - expect( FontFeature.stylisticSet(18), const FontFeature('ss18')); + expect(FontFeature.stylisticSet(1), const FontFeature('ss01')); + expect(FontFeature.stylisticSet(18), const FontFeature('ss18')); expect(const FontFeature.subscripts(), const FontFeature('subs')); expect(const FontFeature.superscripts(), const FontFeature('sups')); expect(const FontFeature.swash(), const FontFeature('swsh')); @@ -279,24 +358,24 @@ void testFontVariation() { final Uint8List fontData = await readFile('RobotoSlab-VariableFont_wght.ttf'); await loadFontFromList(fontData, fontFamily: 'RobotoSerif'); - final ParagraphBuilder baseBuilder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'RobotoSerif', - fontSize: 40.0, - )); + final ParagraphBuilder baseBuilder = ParagraphBuilder( + ParagraphStyle(fontFamily: 'RobotoSerif', fontSize: 40.0), + ); baseBuilder.addText('Hello'); final Paragraph baseParagraph = baseBuilder.build(); baseParagraph.layout(const ParagraphConstraints(width: double.infinity)); final double baseWidth = baseParagraph.minIntrinsicWidth; - final ParagraphBuilder wideBuilder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'RobotoSerif', - fontSize: 40.0, - )); - wideBuilder.pushStyle(TextStyle( - fontFamily: 'RobotoSerif', - fontSize: 40.0, - fontVariations: [const FontVariation('wght', 900.0)], - )); + final ParagraphBuilder wideBuilder = ParagraphBuilder( + ParagraphStyle(fontFamily: 'RobotoSerif', fontSize: 40.0), + ); + wideBuilder.pushStyle( + TextStyle( + fontFamily: 'RobotoSerif', + fontSize: 40.0, + fontVariations: [const FontVariation('wght', 900.0)], + ), + ); wideBuilder.addText('Hello'); final Paragraph wideParagraph = wideBuilder.build(); wideParagraph.layout(const ParagraphConstraints(width: double.infinity)); @@ -319,10 +398,22 @@ void testFontVariation() { }); test('FontVariation.lerp', () async { - expect(FontVariation.lerp(const FontVariation.weight(100.0), const FontVariation.weight(300.0), 0.5), const FontVariation.weight(200.0)); - expect(FontVariation.lerp(const FontVariation.slant(0.0), const FontVariation.slant(-80.0), 0.25), const FontVariation.slant(-20.0)); - expect(FontVariation.lerp(const FontVariation.width(90.0), const FontVariation.italic(0.2), 0.1), const FontVariation.width(90.0)); - expect(FontVariation.lerp(const FontVariation.width(90.0), const FontVariation.italic(0.2), 0.9), const FontVariation.italic(0.2)); + expect( + FontVariation.lerp(const FontVariation.weight(100.0), const FontVariation.weight(300.0), 0.5), + const FontVariation.weight(200.0), + ); + expect( + FontVariation.lerp(const FontVariation.slant(0.0), const FontVariation.slant(-80.0), 0.25), + const FontVariation.slant(-20.0), + ); + expect( + FontVariation.lerp(const FontVariation.width(90.0), const FontVariation.italic(0.2), 0.1), + const FontVariation.width(90.0), + ); + expect( + FontVariation.lerp(const FontVariation.width(90.0), const FontVariation.italic(0.2), 0.9), + const FontVariation.italic(0.2), + ); }); } @@ -331,15 +422,16 @@ void testGetWordBoundary() { final Uint8List fontData = await readFile('RobotoSlab-VariableFont_wght.ttf'); await loadFontFromList(fontData, fontFamily: 'RobotoSerif'); - final ParagraphBuilder builder = ParagraphBuilder(ParagraphStyle( - fontFamily: 'RobotoSerif', - fontSize: 40.0, - )); + final ParagraphBuilder builder = ParagraphBuilder( + ParagraphStyle(fontFamily: 'RobotoSerif', fontSize: 40.0), + ); builder.addText('Hello team'); final Paragraph paragraph = builder.build(); paragraph.layout(const ParagraphConstraints(width: double.infinity)); - TextRange range = paragraph.getWordBoundary(const TextPosition(offset: 5, affinity: TextAffinity.upstream)); + TextRange range = paragraph.getWordBoundary( + const TextPosition(offset: 5, affinity: TextAffinity.upstream), + ); expect(range.start, 0); expect(range.end, 5); diff --git a/testing/dart/window_test.dart b/testing/dart/window_test.dart index 0b8070592ed47..0c9b1ed67a79d 100644 --- a/testing/dart/window_test.dart +++ b/testing/dart/window_test.dart @@ -12,11 +12,15 @@ void main() { test('window.sendPlatformMessage preserves callback zone', () { runZoned(() { final Zone innerZone = Zone.current; - PlatformDispatcher.instance.sendPlatformMessage('test', ByteData.view(Uint8List(0).buffer), expectAsync1((ByteData? data) { - final Zone runZone = Zone.current; - expect(runZone, isNotNull); - expect(runZone, same(innerZone)); - })); + PlatformDispatcher.instance.sendPlatformMessage( + 'test', + ByteData.view(Uint8List(0).buffer), + expectAsync1((ByteData? data) { + final Zone runZone = Zone.current; + expect(runZone, isNotNull); + expect(runZone, same(innerZone)); + }), + ); }); }); @@ -30,16 +34,18 @@ void main() { rasterFinishWallTime: 19501, frameNumber: 23, ); - expect(timing.toString(), - 'FrameTiming(buildDuration: 7.0ms, ' - 'rasterDuration: 10.5ms, ' - 'vsyncOverhead: 0.5ms, ' - 'totalSpan: 19.0ms, ' - 'layerCacheCount: 0, ' - 'layerCacheBytes: 0, ' - 'pictureCacheCount: 0, ' - 'pictureCacheBytes: 0, ' - 'frameNumber: 23)'); + expect( + timing.toString(), + 'FrameTiming(buildDuration: 7.0ms, ' + 'rasterDuration: 10.5ms, ' + 'vsyncOverhead: 0.5ms, ' + 'totalSpan: 19.0ms, ' + 'layerCacheCount: 0, ' + 'layerCacheBytes: 0, ' + 'pictureCacheCount: 0, ' + 'pictureCacheBytes: 0, ' + 'frameNumber: 23)', + ); }); test('FrameTiming.toString with cache statistics has the correct format', () { @@ -56,16 +62,18 @@ void main() { pictureCacheBytes: 300000, frameNumber: 29, ); - expect(timing.toString(), - 'FrameTiming(buildDuration: 7.0ms, ' - 'rasterDuration: 10.5ms, ' - 'vsyncOverhead: 0.5ms, ' - 'totalSpan: 19.0ms, ' - 'layerCacheCount: 5, ' - 'layerCacheBytes: 200000, ' - 'pictureCacheCount: 3, ' - 'pictureCacheBytes: 300000, ' - 'frameNumber: 29)'); + expect( + timing.toString(), + 'FrameTiming(buildDuration: 7.0ms, ' + 'rasterDuration: 10.5ms, ' + 'vsyncOverhead: 0.5ms, ' + 'totalSpan: 19.0ms, ' + 'layerCacheCount: 5, ' + 'layerCacheBytes: 200000, ' + 'pictureCacheCount: 3, ' + 'pictureCacheBytes: 300000, ' + 'frameNumber: 29)', + ); }); test('computePlatformResolvedLocale basic', () { @@ -76,7 +84,9 @@ void main() { const Locale.fromSubtags(languageCode: 'en'), ]; // The default implementation returns null due to lack of a real platform. - final Locale? result = PlatformDispatcher.instance.computePlatformResolvedLocale(supportedLocales); + final Locale? result = PlatformDispatcher.instance.computePlatformResolvedLocale( + supportedLocales, + ); expect(result, null); }); diff --git a/testing/scenario_app/bin/run_android_tests.dart b/testing/scenario_app/bin/run_android_tests.dart index 75982fdc1b183..7b79e8e326e2e 100644 --- a/testing/scenario_app/bin/run_android_tests.dart +++ b/testing/scenario_app/bin/run_android_tests.dart @@ -33,27 +33,17 @@ void main(List args) async { // Show usage if requested. if (Options.showUsage(args)) { - stdout.writeln(Options.usage( - environment: environment, - localEngineDir: localEngineDir, - )); + stdout.writeln(Options.usage(environment: environment, localEngineDir: localEngineDir)); return; } // Parse the command line arguments. final Options options; try { - options = Options.parse( - args, - environment: environment, - localEngine: localEngineDir, - ); + options = Options.parse(args, environment: environment, localEngine: localEngineDir); } on FormatException catch (error) { stderr.writeln(error); - stderr.writeln(Options.usage( - environment: environment, - localEngineDir: localEngineDir, - )); + stderr.writeln(Options.usage(environment: environment, localEngineDir: localEngineDir)); exitCode = 1; return; } @@ -67,6 +57,7 @@ void main(List args) async { onSigint.cancel(); onSigterm.cancel(); } + runZonedGuarded( () async { onSigint = ProcessSignal.sigint.watch().listen((_) { @@ -160,14 +151,14 @@ Future _run({ if (!testApk.existsSync()) { panic([ 'test apk does not exist: ${testApk.path}', - 'make sure to build the selected engine variant' + 'make sure to build the selected engine variant', ]); } if (!appApk.existsSync()) { panic([ 'app apk does not exist: ${appApk.path}', - 'make sure to build the selected engine variant' + 'make sure to build the selected engine variant', ]); } @@ -190,54 +181,63 @@ Future _run({ stdout.writeln('client connected ${client.remoteAddress.address}:${client.remotePort}'); } pendingConnections.add(client); - client.transform(const ScreenshotBlobTransformer()).listen((Screenshot screenshot) async { - final String fileName = screenshot.filename; - final String filePath = join(screenshotPath, fileName); - { - const String remotePath = '/data/local/tmp/flutter_screenshot.png'; - ProcessResult result = await pm.run(['adb', 'shell', 'screencap', '-p', remotePath]); - if (result.exitCode != 0) { - panic(['Failed to capture screenshot']); - } - result = await pm.run( - ['adb', 'pull', remotePath, filePath], + client + .transform(const ScreenshotBlobTransformer()) + .listen( + (Screenshot screenshot) async { + final String fileName = screenshot.filename; + final String filePath = join(screenshotPath, fileName); + { + const String remotePath = '/data/local/tmp/flutter_screenshot.png'; + ProcessResult result = await pm.run([ + 'adb', + 'shell', + 'screencap', + '-p', + remotePath, + ]); + if (result.exitCode != 0) { + panic(['Failed to capture screenshot']); + } + result = await pm.run(['adb', 'pull', remotePath, filePath]); + if (result.exitCode != 0) { + panic(['Failed to pull screenshot']); + } + result = await pm.run(['adb', 'shell', 'rm', remotePath]); + if (result.exitCode != 0) { + stderr.writeln('Warning: failed to delete old screenshot on device.'); + } + } + // Write a single byte into the socket as a signal to ScreenshotUtil.java + // that the screenshot was taken. + client.write(0x8); + + assert(skiaGoldClient != null, 'expected Skia Gold client'); + final File goldenFile = File(filePath); + if (verbose) { + log('wrote ${goldenFile.absolute.path}'); + } + if (SkiaGoldClient.isAvailable()) { + final Future comparison = skiaGoldClient! + .addImg( + fileName, + goldenFile, + screenshotSize: screenshot.pixelCount, + // Each color channel can be off by 2. + pixelColorDelta: 8, + ) + .then((_) => logImportant('skia gold comparison succeeded: $fileName')) + .catchError((Object error) { + logWarning('skia gold comparison failed: $error'); + comparisonsFailed++; + }); + pendingComparisons.add(comparison); + } + }, + onDone: () { + pendingConnections.remove(client); + }, ); - if (result.exitCode != 0) { - panic(['Failed to pull screenshot']); - } - result = await pm.run(['adb', 'shell', 'rm', remotePath]); - if (result.exitCode != 0) { - stderr.writeln('Warning: failed to delete old screenshot on device.'); - } - } - // Write a single byte into the socket as a signal to ScreenshotUtil.java - // that the screenshot was taken. - client.write(0x8); - - assert(skiaGoldClient != null, 'expected Skia Gold client'); - final File goldenFile = File(filePath); - if (verbose) { - log('wrote ${goldenFile.absolute.path}'); - } - if (SkiaGoldClient.isAvailable()) { - final Future comparison = skiaGoldClient! - .addImg( - fileName, - goldenFile, - screenshotSize: screenshot.pixelCount, - // Each color channel can be off by 2. - pixelColorDelta: 8, - ) - .then((_) => logImportant('skia gold comparison succeeded: $fileName')) - .catchError((Object error) { - logWarning('skia gold comparison failed: $error'); - comparisonsFailed++; - }); - pendingComparisons.add(comparison); - } - }, onDone: () { - pendingConnections.remove(client); - }); }); }); @@ -259,54 +259,61 @@ Future _run({ } logcatProcess = await pm.start([adb.path, 'logcat', '-T', '1']); - final (Future logcatExitCode, Stream logcatOutput) = getProcessStreams(logcatProcess); + final (Future logcatExitCode, Stream logcatOutput) = getProcessStreams( + logcatProcess, + ); logcatProcessExitCode = logcatExitCode; String? filterProcessId; - logcatOutput.listen((String line) { - // Always write to the full log. - logcat.writeln(line); - if (enableImpeller && actualImpellerBackend == null && line.contains('Using the Impeller rendering backend')) { - if (line.contains('OpenGLES')) { - actualImpellerBackend = _ImpellerBackend.opengles; - } else if (line.contains('Vulkan')) { - actualImpellerBackend = _ImpellerBackend.vulkan; - } else { - panic([ - 'Impeller was enabled, but $line did not contain "OpenGLES" or "Vulkan".', - ]); + logcatOutput.listen( + (String line) { + // Always write to the full log. + logcat.writeln(line); + if (enableImpeller && + actualImpellerBackend == null && + line.contains('Using the Impeller rendering backend')) { + if (line.contains('OpenGLES')) { + actualImpellerBackend = _ImpellerBackend.opengles; + } else if (line.contains('Vulkan')) { + actualImpellerBackend = _ImpellerBackend.vulkan; + } else { + panic([ + 'Impeller was enabled, but $line did not contain "OpenGLES" or "Vulkan".', + ]); + } } - } - // Conditionally parse and write to stderr. - final AdbLogLine? adbLogLine = AdbLogLine.tryParse(line); - if (verbose || adbLogLine == null) { - log(line); - return; - } + // Conditionally parse and write to stderr. + final AdbLogLine? adbLogLine = AdbLogLine.tryParse(line); + if (verbose || adbLogLine == null) { + log(line); + return; + } - // If we haven't already found a process ID, try to find one. - // The process ID will help us filter out logs from other processes. - filterProcessId ??= adbLogLine.tryParseProcess(); + // If we haven't already found a process ID, try to find one. + // The process ID will help us filter out logs from other processes. + filterProcessId ??= adbLogLine.tryParseProcess(); + + // If this is a "verbose" log, possibly skip it. + final bool isVerbose = adbLogLine.isVerbose(filterProcessId: filterProcessId); + if (isVerbose || filterProcessId == null) { + // We've requested verbose output, so print everything. + if (verbose) { + adbLogLine.printFormatted(); + } + return; + } - // If this is a "verbose" log, possibly skip it. - final bool isVerbose = adbLogLine.isVerbose(filterProcessId: filterProcessId); - if (isVerbose || filterProcessId == null) { - // We've requested verbose output, so print everything. + // It's a non-verbose log, so print it. + adbLogLine.printFormatted(); + }, + onError: (Object? err) { if (verbose) { - adbLogLine.printFormatted(); + logWarning('logcat stream error: $err'); } - return; - } - - // It's a non-verbose log, so print it. - adbLogLine.printFormatted(); - }, onError: (Object? err) { - if (verbose) { - logWarning('logcat stream error: $err'); - } - }); + }, + ); }); await step('Configuring emulator...', () async { @@ -325,7 +332,12 @@ Future _run({ }); await step('Get API level of connected device...', () async { - final ProcessResult apiLevelProcessResult = await pm.run([adb.path, 'shell', 'getprop', 'ro.build.version.sdk']); + final ProcessResult apiLevelProcessResult = await pm.run([ + adb.path, + 'shell', + 'getprop', + 'ro.build.version.sdk', + ]); if (apiLevelProcessResult.exitCode != 0) { panic(['could not get API level of the connected device']); } @@ -333,13 +345,10 @@ Future _run({ final Map dimensions = { 'AndroidAPILevel': connectedDeviceAPILevel, 'GraphicsBackend': enableImpeller ? 'impeller-${impellerBackend!.name}' : 'skia', - 'ForceSurfaceProducerSurfaceTexture': '$forceSurfaceProducerSurfaceTexture' + 'ForceSurfaceProducerSurfaceTexture': '$forceSurfaceProducerSurfaceTexture', }; log('using dimensions: ${json.encode(dimensions)}'); - skiaGoldClient = SkiaGoldClient( - outDir, - dimensions: dimensions, - ); + skiaGoldClient = SkiaGoldClient(outDir, dimensions: dimensions); }); await step('Skia Gold auth...', () async { @@ -356,7 +365,12 @@ Future _run({ }); await step('Reverse port...', () async { - final int exitCode = await pm.runAndForward([adb.path, 'reverse', 'tcp:3000', 'tcp:$_tcpPort']); + final int exitCode = await pm.runAndForward([ + adb.path, + 'reverse', + 'tcp:3000', + 'tcp:$_tcpPort', + ]); if (exitCode != 0) { panic(['could not forward port']); } @@ -389,11 +403,7 @@ Future _run({ if (exitCode != 0) { panic(['could not create /tmp directory on device']); } - final String screenRecordingPath = join( - _emulatorStoragePath, - 'tmp', - 'screen.mp4', - ); + final String screenRecordingPath = join(_emulatorStoragePath, 'tmp', 'screen.mp4'); screenRecordProcess = await pm.start([ adb.path, 'shell', @@ -414,16 +424,10 @@ Future _run({ 'instrument', '-w', '--no-window-animation', - if (smokeTestFullPath != null) - '-e class $smokeTestFullPath', - if (enableImpeller) - '-e enable-impeller true' - else - '-e enable-impeller false', - if (impellerBackend != null) - '-e impeller-backend ${impellerBackend.name}', - if (forceSurfaceProducerSurfaceTexture) - '-e force-surface-producer-surface-texture true', + if (smokeTestFullPath != null) '-e class $smokeTestFullPath', + if (enableImpeller) '-e enable-impeller true' else '-e enable-impeller false', + if (impellerBackend != null) '-e impeller-backend ${impellerBackend.name}', + if (forceSurfaceProducerSurfaceTexture) '-e force-surface-producer-surface-texture true', 'dev.flutter.scenarios.test/dev.flutter.TestRunner', ]); if (exitCode != 0) { @@ -440,7 +444,6 @@ Future _run({ } }); - if (enableImpeller) { await step('Validating Impeller...', () async { final _ImpellerBackend expectedImpellerBackend = impellerBackend ?? _ImpellerBackend.vulkan; @@ -488,7 +491,13 @@ Future _run({ } await step('Killing test app and test runner...', () async { - final int exitCode = await pm.runAndForward([adb.path, 'shell', 'am', 'force-stop', 'dev.flutter.scenarios']); + final int exitCode = await pm.runAndForward([ + adb.path, + 'shell', + 'am', + 'force-stop', + 'dev.flutter.scenarios', + ]); if (exitCode != 0) { logError('could not kill test app'); } @@ -501,15 +510,8 @@ Future _run({ await screenRecordProcess!.exitCode; // Pull the screen recording from the device. - final String screenRecordingPath = join( - _emulatorStoragePath, - 'tmp', - 'screen.mp4', - ); - final String screenRecordingLocalPath = join( - logsDir.path, - 'screen.mp4', - ); + final String screenRecordingPath = join(_emulatorStoragePath, 'tmp', 'screen.mp4'); + final String screenRecordingLocalPath = join(logsDir.path, 'screen.mp4'); final int exitCode = await pm.runAndForward([ adb.path, 'pull', @@ -566,23 +568,17 @@ Future _run({ } prefix.write('.'); } - _copyFiles( - source: logsDir, - destination: finalLogsDir, - prefix: prefix.toString(), - ); + _copyFiles(source: logsDir, destination: finalLogsDir, prefix: prefix.toString()); }); await step('Symbolize stack traces', () async { - final ProcessResult result = await pm.run( - [ - ndkStack, - '-sym', - outDir.path, - '-dump', - logcatPath, - ], - ); + final ProcessResult result = await pm.run([ + ndkStack, + '-sym', + outDir.path, + '-dump', + logcatPath, + ]); if (result.exitCode != 0) { panic(['Failed to symbolize stack traces']); } @@ -654,11 +650,7 @@ int _getAndIncrementRerunNumber(String logsDir) { /// Copies the contents of [source] to [destination], optionally adding a [prefix] to the destination path. /// /// This function is used to copy the screenshots from the device to the logs directory. -void _copyFiles({ - required Directory source, - required Directory destination, - String prefix = '', -}) { +void _copyFiles({required Directory source, required Directory destination, String prefix = ''}) { for (final FileSystemEntity entity in source.listSync()) { if (entity is File) { entity.copySync(join(destination.path, prefix + basename(entity.path))); diff --git a/testing/scenario_app/bin/run_ios_tests.dart b/testing/scenario_app/bin/run_ios_tests.dart index eef2ab76eca6b..24c0a3b2cc721 100644 --- a/testing/scenario_app/bin/run_ios_tests.dart +++ b/testing/scenario_app/bin/run_ios_tests.dart @@ -54,29 +54,32 @@ void main(List args) async { // Run the actual script. final completer = Completer(); - runZonedGuarded(() async { - await _run( - cleanup, - engine, - iosEngineVariant: iosEngineVariant, - deviceName: results.option('device-name')!, - deviceIdentifier: results.option('device-identifier')!, - osRuntime: results.option('os-runtime')!, - osVersion: results.option('os-version')!, - withImpeller: results.flag('with-impeller'), - dumpXcresultOnFailure: dumpXcresultOnFailurePath, - ); - completer.complete(); - }, (e, s) { - if (e is _ToolFailure) { - io.stderr.writeln(e); - io.exitCode = 1; - } else { - io.stderr.writeln('Uncaught exception: $e\n$s'); - io.exitCode = 255; - } - completer.complete(); - }); + runZonedGuarded( + () async { + await _run( + cleanup, + engine, + iosEngineVariant: iosEngineVariant, + deviceName: results.option('device-name')!, + deviceIdentifier: results.option('device-identifier')!, + osRuntime: results.option('os-runtime')!, + osVersion: results.option('os-version')!, + withImpeller: results.flag('with-impeller'), + dumpXcresultOnFailure: dumpXcresultOnFailurePath, + ); + completer.complete(); + }, + (e, s) { + if (e is _ToolFailure) { + io.stderr.writeln(e); + io.exitCode = 1; + } else { + io.stderr.writeln('Uncaught exception: $e\n$s'); + io.exitCode = 255; + } + completer.complete(); + }, + ); // We can't await the result of runZonedGuarded becauase async errors in futures never cross different errorZone boundaries. await completer.future; @@ -120,11 +123,7 @@ Future _run( _ensureSimulatorsRotateAutomaticallyForPlatformViewRotationTest(); _deleteAnyExistingDevices(deviceName: deviceName); - _createDevice( - deviceName: deviceName, - deviceIdentifier: deviceIdentifier, - osRuntime: osRuntime, - ); + _createDevice(deviceName: deviceName, deviceIdentifier: deviceIdentifier, osRuntime: osRuntime); final (scenarioPath, resultBundle) = _buildResultBundlePath( engine: engine, @@ -176,75 +175,61 @@ final class _ToolFailure implements Exception { String toString() => message; } -final _args = ArgParser() - ..addFlag( - 'help', - abbr: 'h', - help: 'Prints usage information.', - negatable: false, - ) - ..addOption( - 'device-name', - help: 'The name of the iOS simulator device to use.', - defaultsTo: 'iPhone SE (3rd generation)', - ) - ..addOption( - 'device-identifier', - help: 'The identifier of the iOS simulator device to use.', - defaultsTo: - 'com.apple.CoreSimulator.SimDeviceType.iPhone-SE-3rd-generation', - ) - ..addOption( - 'os-runtime', - help: 'The OS runtime of the iOS simulator device to use.', - defaultsTo: 'com.apple.CoreSimulator.SimRuntime.iOS-17-0', - ) - ..addOption( - 'os-version', - help: 'The OS version of the iOS simulator device to use.', - defaultsTo: '17.0', - ) - ..addFlag( - 'with-impeller', - help: 'Whether to use the Impeller backend to run the tests.', - defaultsTo: true, - ) - ..addOption( - 'dump-xcresult-on-failure', - help: 'The path to dump the xcresult bundle to if the test fails.\n\n' - 'Defaults to the environment variable FLUTTER_TEST_OUTPUTS_DIR, ' - 'otherwise to a randomly generated temporary directory.', - defaultsTo: io.Platform.environment['FLUTTER_TEST_OUTPUTS_DIR'], - ); +final _args = + ArgParser() + ..addFlag('help', abbr: 'h', help: 'Prints usage information.', negatable: false) + ..addOption( + 'device-name', + help: 'The name of the iOS simulator device to use.', + defaultsTo: 'iPhone SE (3rd generation)', + ) + ..addOption( + 'device-identifier', + help: 'The identifier of the iOS simulator device to use.', + defaultsTo: 'com.apple.CoreSimulator.SimDeviceType.iPhone-SE-3rd-generation', + ) + ..addOption( + 'os-runtime', + help: 'The OS runtime of the iOS simulator device to use.', + defaultsTo: 'com.apple.CoreSimulator.SimRuntime.iOS-17-0', + ) + ..addOption( + 'os-version', + help: 'The OS version of the iOS simulator device to use.', + defaultsTo: '17.0', + ) + ..addFlag( + 'with-impeller', + help: 'Whether to use the Impeller backend to run the tests.', + defaultsTo: true, + ) + ..addOption( + 'dump-xcresult-on-failure', + help: + 'The path to dump the xcresult bundle to if the test fails.\n\n' + 'Defaults to the environment variable FLUTTER_TEST_OUTPUTS_DIR, ' + 'otherwise to a randomly generated temporary directory.', + defaultsTo: io.Platform.environment['FLUTTER_TEST_OUTPUTS_DIR'], + ); void _ensureSimulatorsRotateAutomaticallyForPlatformViewRotationTest() { // Can also be set via Simulator Device > Rotate Device Automatically. - final result = io.Process.runSync( - 'defaults', - const [ - 'write', - 'com.apple.iphonesimulator', - 'RotateWindowWhenSignaledByGuest', - '-int 1', - ], - ); + final result = io.Process.runSync('defaults', const [ + 'write', + 'com.apple.iphonesimulator', + 'RotateWindowWhenSignaledByGuest', + '-int 1', + ]); if (result.exitCode != 0) { - throw Exception( - 'Failed to enable automatic rotation for iOS simulator: ${result.stderr}', - ); + throw Exception('Failed to enable automatic rotation for iOS simulator: ${result.stderr}'); } } void _deleteAnyExistingDevices({required String deviceName}) { - io.stderr.writeln( - 'Deleting any existing simulator devices named $deviceName...', - ); + io.stderr.writeln('Deleting any existing simulator devices named $deviceName...'); bool deleteSimulator() { - final result = io.Process.runSync( - 'xcrun', - ['simctl', 'delete', deviceName], - ); + final result = io.Process.runSync('xcrun', ['simctl', 'delete', deviceName]); if (result.exitCode == 0) { io.stderr.writeln('Deleted $deviceName'); return true; @@ -262,16 +247,13 @@ void _createDevice({ required String osRuntime, }) { io.stderr.writeln('Creating $deviceName $deviceIdentifier $osRuntime...'); - final result = io.Process.runSync( - 'xcrun', - [ - 'simctl', - 'create', - deviceName, - deviceIdentifier, - osRuntime, - ], - ); + final result = io.Process.runSync('xcrun', [ + 'simctl', + 'create', + deviceName, + deviceIdentifier, + osRuntime, + ]); if (result.exitCode != 0) { throw Exception('Failed to create simulator device: ${result.stderr}'); } @@ -282,17 +264,12 @@ void _createDevice({ required Engine engine, required String iosEngineVariant, }) { - final scenarioPath = path.normalize(path.join( - engine.outDir.path, - iosEngineVariant, - 'scenario_app', - 'Scenarios', - )); + final scenarioPath = path.normalize( + path.join(engine.outDir.path, iosEngineVariant, 'scenario_app', 'Scenarios'), + ); // Create a temporary directory to store the test results. - final result = io.Directory(scenarioPath).createTempSync( - 'ios_scenario_xcresult', - ); + final result = io.Directory(scenarioPath).createTempSync('ios_scenario_xcresult'); return (scenarioPath, result); } @@ -305,26 +282,22 @@ Future _runTests({ required String iosEngineVariant, List xcodeBuildExtraArgs = const [], }) async { - return io.Process.start( - 'xcodebuild', - [ - '-project', - path.join(outScenariosPath, 'Scenarios.xcodeproj'), - '-sdk', - 'iphonesimulator', - '-scheme', - 'Scenarios', - '-resultBundlePath', - path.join(resultBundlePath, 'ios_scenario.xcresult'), - '-destination', - 'platform=iOS Simulator,OS=$osVersion,name=$deviceName', - 'clean', - 'test', - 'FLUTTER_ENGINE=$iosEngineVariant', - ...xcodeBuildExtraArgs, - ], - mode: io.ProcessStartMode.inheritStdio, - ); + return io.Process.start('xcodebuild', [ + '-project', + path.join(outScenariosPath, 'Scenarios.xcodeproj'), + '-sdk', + 'iphonesimulator', + '-scheme', + 'Scenarios', + '-resultBundlePath', + path.join(resultBundlePath, 'ios_scenario.xcresult'), + '-destination', + 'platform=iOS Simulator,OS=$osVersion,name=$deviceName', + 'clean', + 'test', + 'FLUTTER_ENGINE=$iosEngineVariant', + ...xcodeBuildExtraArgs, + ], mode: io.ProcessStartMode.inheritStdio); } @useResult @@ -334,15 +307,7 @@ String _zipAndStoreFailedTestResults({ required String storePath, }) { final outputPath = path.join(storePath, '${iosEngineVariant.replaceAll('/', '_')}.zip'); - final result = io.Process.runSync( - 'zip', - [ - '-q', - '-r', - outputPath, - resultBundle.path, - ], - ); + final result = io.Process.runSync('zip', ['-q', '-r', outputPath, resultBundle.path]); if (result.exitCode != 0) { throw Exception( 'Failed to zip the test results (exit code = ${result.exitCode}).\n\n' diff --git a/testing/scenario_app/bin/utils/adb_logcat_filtering.dart b/testing/scenario_app/bin/utils/adb_logcat_filtering.dart index f029f136dc85e..f1534a26e7fc8 100644 --- a/testing/scenario_app/bin/utils/adb_logcat_filtering.dart +++ b/testing/scenario_app/bin/utils/adb_logcat_filtering.dart @@ -56,7 +56,9 @@ extension type const AdbLogLine._(Match _match) { // 6. The actual log message. // // This regex is simple versus being more precise. Feel free to improve it. - static final RegExp _pattern = RegExp(r'(\d+-\d+\s[\d|:]+\.\d+)\s+(\d+)\s+(\d+)\s(\w)\s(\S+)\s*:\s*(.*)'); + static final RegExp _pattern = RegExp( + r'(\d+-\d+\s[\d|:]+\.\d+)\s+(\d+)\s+(\d+)\s(\w)\s(\S+)\s*:\s*(.*)', + ); /// Parses the given [adbLogCatLine] into a structured form. /// @@ -70,7 +72,9 @@ extension type const AdbLogLine._(Match _match) { String? tryParseProcess() { if (name == activityManagerTag && message.startsWith('Start proc')) { // ActivityManager: Start proc 4475:dev.flutter.scenarios/u0a190 for added application ... - final RegExpMatch? match = RegExp('Start proc (\\d+):$flutterProcessName').firstMatch(message); + final RegExpMatch? match = RegExp( + 'Start proc (\\d+):$flutterProcessName', + ).firstMatch(message); return match?.group(1); } return null; @@ -100,15 +104,10 @@ extension type const AdbLogLine._(Match _match) { }; @visibleForTesting - static const Set knownUsefulTags = { - activityManagerTag - }; + static const Set knownUsefulTags = {activityManagerTag}; @visibleForTesting - static const Set knownUsefulErrorTags = { - 'androidemu', - 'THREAD_STATE', - }; + static const Set knownUsefulErrorTags = {'androidemu', 'THREAD_STATE'}; /// Returns `true` if the log line is verbose. bool isVerbose({String? filterProcessId}) => !_isRelevant(filterProcessId: filterProcessId); @@ -138,8 +137,7 @@ extension type const AdbLogLine._(Match _match) { // If a process ID is specified, exclude logs _not_ from that process. if (filterProcessId == null) { // YOLO, let's keep it anyway. - return name.toLowerCase().contains('flutter') || - message.toLowerCase().contains('flutter'); + return name.toLowerCase().contains('flutter') || message.toLowerCase().contains('flutter'); } return process == filterProcessId; diff --git a/testing/scenario_app/bin/utils/environment.dart b/testing/scenario_app/bin/utils/environment.dart index 9ad5561f83e24..0d2ffa93c7ae2 100644 --- a/testing/scenario_app/bin/utils/environment.dart +++ b/testing/scenario_app/bin/utils/environment.dart @@ -8,11 +8,7 @@ import 'package:meta/meta.dart'; @immutable final class Environment { /// Creates a new environment from the given values. - const Environment({ - required this.isCi, - required this.showVerbose, - required this.logsDir, - }); + const Environment({required this.isCi, required this.showVerbose, required this.logsDir}); /// Whether the current program is running on a CI environment. /// diff --git a/testing/scenario_app/bin/utils/logs.dart b/testing/scenario_app/bin/utils/logs.dart index e082211cefabb..34e1715ae7e45 100644 --- a/testing/scenario_app/bin/utils/logs.dart +++ b/testing/scenario_app/bin/utils/logs.dart @@ -9,7 +9,7 @@ String _green = _supportsAnsi ? '\u001b[1;32m' : ''; String _red = _supportsAnsi ? '\u001b[31m' : ''; String _yellow = _supportsAnsi ? '\u001b[33m' : ''; String _gray = _supportsAnsi ? '\u001b[90m' : ''; -String _reset = _supportsAnsi? '\u001B[0m' : ''; +String _reset = _supportsAnsi ? '\u001B[0m' : ''; Future step(String msg, Future Function() fn) async { stdout.writeln('-> $_green$msg$_reset'); diff --git a/testing/scenario_app/bin/utils/options.dart b/testing/scenario_app/bin/utils/options.dart index c28fd8e0c6c5a..d7f3407de48fd 100644 --- a/testing/scenario_app/bin/utils/options.dart +++ b/testing/scenario_app/bin/utils/options.dart @@ -23,27 +23,21 @@ extension type const Options._(ArgResults _args) { if (results['adb'] == null) { throw const FormatException('The --adb option must be set.'); } else if (!io.File(options.adb).existsSync()) { - throw FormatException( - 'The adb tool does not exist at ${options.adb}.', - ); + throw FormatException('The adb tool does not exist at ${options.adb}.'); } // The 'ndk-stack' tool must exist. if (results['ndk-stack'] == null) { throw const FormatException('The --ndk-stack option must be set.'); } else if (!io.File(options.ndkStack).existsSync()) { - throw FormatException( - 'The ndk-stack tool does not exist at ${options.ndkStack}.', - ); + throw FormatException('The ndk-stack tool does not exist at ${options.ndkStack}.'); } // The 'out-dir' must exist. if (results['out-dir'] == null) { throw const FormatException('The --out-dir option must be set.'); } else if (!io.Directory(options.outDir).existsSync()) { - throw FormatException( - 'The out directory does not exist at ${options.outDir}.', - ); + throw FormatException('The out directory does not exist at ${options.outDir}.'); } return options; @@ -66,9 +60,10 @@ extension type const Options._(ArgResults _args) { /// ``` static bool showUsage(List args) { // If any of the arguments are '--help' or -'h'. - return args.isNotEmpty && args.any((String arg) { - return arg == '--help' || arg == '-h'; - }); + return args.isNotEmpty && + args.any((String arg) { + return arg == '--help' || arg == '-h'; + }); } /// Whether verbose logging should be enabled based on command line [args]. @@ -85,18 +80,16 @@ extension type const Options._(ArgResults _args) { /// ``` static bool showVerbose(List args) { // If any of the arguments are '--verbose' or -'v'. - return args.isNotEmpty && args.any((String arg) { - return arg == '--verbose' || arg == '-v'; - }); + return args.isNotEmpty && + args.any((String arg) { + return arg == '--verbose' || arg == '-v'; + }); } /// Returns usage information for the `scenario_app` test runner. /// /// If [verbose] is `true`, then additional options are shown. - static String usage({ - required Environment environment, - required Engine? localEngineDir, - }) { + static String usage({required Environment environment, required Engine? localEngineDir}) { return _parser(environment, localEngineDir).usage; } @@ -106,18 +99,8 @@ extension type const Options._(ArgResults _args) { static ArgParser _parser(Environment environment, Engine? localEngine) { final bool hideUnusualOptions = !environment.showVerbose; return ArgParser(usageLineLength: 120) - ..addFlag( - 'verbose', - abbr: 'v', - help: 'Enable verbose logging', - negatable: false, - ) - ..addFlag( - 'help', - abbr: 'h', - help: 'Print usage information', - negatable: false, - ) + ..addFlag('verbose', abbr: 'v', help: 'Enable verbose logging', negatable: false) + ..addFlag('help', abbr: 'h', help: 'Print usage information', negatable: false) ..addFlag( 'use-skia-gold', help: @@ -142,7 +125,7 @@ extension type const Options._(ArgResults _args) { 'devices that do not support ImageReader, or to explicitly test ' 'SurfaceTexture path for rendering plugins still using the older ' 'createSurfaceTexture() API.', - negatable: false + negatable: false, ) ..addFlag( 'prefix-logs-per-run', @@ -150,13 +133,11 @@ extension type const Options._(ArgResults _args) { defaultsTo: environment.isCi, hide: hideUnusualOptions, ) - ..addFlag( - 'record-screen', - help: 'Whether to record the screen during the test run.', - ) + ..addFlag('record-screen', help: 'Whether to record the screen during the test run.') ..addOption( 'impeller-backend', - help: 'The graphics backend to use when --enable-impeller is true. ' + help: + 'The graphics backend to use when --enable-impeller is true. ' 'Unlike the similar option when launching an app, there is no ' 'fallback; that is, either Vulkan or OpenGLES must be specified. ', allowed: ['vulkan', 'opengles'], @@ -169,21 +150,23 @@ extension type const Options._(ArgResults _args) { ) ..addOption( 'adb', - help: 'Path to the Android Debug Bridge (adb) executable. ' + help: + 'Path to the Android Debug Bridge (adb) executable. ' 'If the current working directory is within the engine repository, ' 'defaults to ' './flutter/third_party/android_tools/sdk/platform-tools/adb.', - defaultsTo: localEngine != null - ? p.join( - localEngine.srcDir.path, - 'flutter', - 'third_party', - 'android_tools', - 'sdk', - 'platform-tools', - 'adb', - ) - : null, + defaultsTo: + localEngine != null + ? p.join( + localEngine.srcDir.path, + 'flutter', + 'third_party', + 'android_tools', + 'sdk', + 'platform-tools', + 'adb', + ) + : null, valueHelp: 'path/to/adb', hide: hideUnusualOptions, ) @@ -194,75 +177,81 @@ extension type const Options._(ArgResults _args) { 'flutter/third_party/android_tools if the current working ' 'directory is within the engine repository on a supported ' 'platform.', - defaultsTo: localEngine != null && - (io.Platform.isLinux || - io.Platform.isMacOS || - io.Platform.isWindows) - ? p.join( - localEngine.srcDir.path, - 'flutter', - 'third_party', - 'android_tools', - 'ndk', - 'prebuilt', - () { - if (io.Platform.isLinux) { - return 'linux-x86_64'; - } else if (io.Platform.isMacOS) { - return 'darwin-x86_64'; - } else if (io.Platform.isWindows) { - return 'windows-x86_64'; - } else { - // Unreachable. - throw UnsupportedError( - 'Unsupported platform: ${io.Platform.operatingSystem}', - ); - } - }(), - 'bin', - 'ndk-stack', - ) - : null, + defaultsTo: + localEngine != null && + (io.Platform.isLinux || io.Platform.isMacOS || io.Platform.isWindows) + ? p.join( + localEngine.srcDir.path, + 'flutter', + 'third_party', + 'android_tools', + 'ndk', + 'prebuilt', + () { + if (io.Platform.isLinux) { + return 'linux-x86_64'; + } else if (io.Platform.isMacOS) { + return 'darwin-x86_64'; + } else if (io.Platform.isWindows) { + return 'windows-x86_64'; + } else { + // Unreachable. + throw UnsupportedError( + 'Unsupported platform: ${io.Platform.operatingSystem}', + ); + } + }(), + 'bin', + 'ndk-stack', + ) + : null, valueHelp: 'path/to/ndk-stack', hide: hideUnusualOptions, ) ..addOption( 'out-dir', - help: 'Path to a out/{variant} directory where the APKs are built. ' + help: + 'Path to a out/{variant} directory where the APKs are built. ' 'Defaults to the latest updated out/ directory that starts with ' '"android_" if the current working directory is within the engine ' 'repository.', - defaultsTo: environment.isCi ? null : localEngine - ?.outputs() - .where((Output o) => p.basename(o.path.path).startsWith('android_')) - .firstOrNull - ?.path - .path, + defaultsTo: + environment.isCi + ? null + : localEngine + ?.outputs() + .where((Output o) => p.basename(o.path.path).startsWith('android_')) + .firstOrNull + ?.path + .path, mandatory: environment.isCi, valueHelp: 'path/to/out/android_variant', ) ..addOption( 'smoke-test', - help: 'Fully qualified class name of a single test to run. For example ' + help: + 'Fully qualified class name of a single test to run. For example ' 'try "dev.flutter.scenarios.EngineLaunchE2ETest" or ' '"dev.flutter.scenariosui.ExternalTextureTests".', valueHelp: 'package.ClassName', ) ..addOption( 'output-contents-golden', - help: 'Path to a file that contains the expected filenames of golden ' + help: + 'Path to a file that contains the expected filenames of golden ' 'files. If the current working directory is within the engine ' 'repository, defaults to ./testing/scenario_app/android/' 'expected_golden_output.txt.', - defaultsTo: localEngine != null - ? p.join( - localEngine.flutterDir.path, - 'testing', - 'scenario_app', - 'android', - 'expected_golden_output.txt', - ) - : null, + defaultsTo: + localEngine != null + ? p.join( + localEngine.flutterDir.path, + 'testing', + 'scenario_app', + 'android', + 'expected_golden_output.txt', + ) + : null, valueHelp: 'path/to/golden.txt', ); } diff --git a/testing/scenario_app/bin/utils/process_manager_extension.dart b/testing/scenario_app/bin/utils/process_manager_extension.dart index 666b09043aad3..e23e4e7b7e309 100644 --- a/testing/scenario_app/bin/utils/process_manager_extension.dart +++ b/testing/scenario_app/bin/utils/process_manager_extension.dart @@ -8,17 +8,15 @@ import 'dart:io'; import 'package:process/process.dart'; -(Future exitCode, Stream output) getProcessStreams( - Process process, -) { +(Future exitCode, Stream output) getProcessStreams(Process process) { final Completer stdoutCompleter = Completer(); final Completer stderrCompleter = Completer(); final StreamController outputController = StreamController(); final StreamSubscription stdoutSub = process.stdout - .transform(utf8.decoder) - .transform(const LineSplitter()) - .listen(outputController.add, onDone: stdoutCompleter.complete); + .transform(utf8.decoder) + .transform(const LineSplitter()) + .listen(outputController.add, onDone: stdoutCompleter.complete); // From looking at historic logs, it seems that the stderr output is rare. // Instead of prefacing every line with [stdout] unnecessarily, we'll just @@ -27,10 +25,10 @@ import 'package:process/process.dart'; // For example, a historic log which has 0 occurrences of stderr: // https://gist.github.com/matanlurey/84cf9c903ef6d507dcb63d4c303ca45f final StreamSubscription stderrSub = process.stderr - .transform(utf8.decoder) - .transform(const LineSplitter()) - .map((String line) => '[stderr] $line') - .listen(outputController.add, onDone: stderrCompleter.complete); + .transform(utf8.decoder) + .transform(const LineSplitter()) + .map((String line) => '[stderr] $line') + .listen(outputController.add, onDone: stderrCompleter.complete); final Future exitCode = process.exitCode.then((int code) async { await (stdoutSub.cancel(), stderrSub.cancel()).wait; @@ -44,10 +42,7 @@ import 'package:process/process.dart'; /// Pipes the [process] streams and writes them to [out] sink. /// /// If [out] is null, then the current [Process.stdout] is used as the sink. -Future pipeProcessStreams( - Process process, { - StringSink? out, -}) async { +Future pipeProcessStreams(Process process, {StringSink? out}) async { out ??= stdout; final (Future exitCode, Stream output) = getProcessStreams(process); diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index 636cd3b1521de..7665f3c97aa93 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -25,10 +25,7 @@ void main() { // Asserting that this is greater than zero since this app runs on different // platforms with different sizes. If it is greater than zero, it has been // initialized to some meaningful value at least. - assert( - view.display.size > Offset.zero, - 'Expected ${view.display} to be initialized.', - ); + assert(view.display.size > Offset.zero, 'Expected ${view.display} to be initialized.'); final ByteData data = ByteData(1); data.setUint8(0, 1); @@ -39,7 +36,8 @@ void main() { FlutterView get _view => PlatformDispatcher.instance.implicitView!; void _handleDriverMessage(ByteData? data, PlatformMessageResponseCallback? callback) { - final Map call = json.decode(utf8.decode(data!.buffer.asUint8List())) as Map; + final Map call = + json.decode(utf8.decode(data!.buffer.asUint8List())) as Map; final String? methodName = call['method'] as String?; switch (methodName) { case 'set_scenario': diff --git a/testing/scenario_app/lib/src/animated_color_square.dart b/testing/scenario_app/lib/src/animated_color_square.dart index 90096168c1f42..90d2dc07c8a76 100644 --- a/testing/scenario_app/lib/src/animated_color_square.dart +++ b/testing/scenario_app/lib/src/animated_color_square.dart @@ -17,6 +17,7 @@ class AnimatedColorSquareScenario extends Scenario { AnimatedColorSquareScenario(super.view); static const double _squareSize = 200; + /// Used to animate the red value in the color of the square. final _NumberSwinger _r = _NumberSwinger(0, 255); late _NumberSwinger _top = _NumberSwinger( @@ -41,11 +42,7 @@ class AnimatedColorSquareScenario extends Scenario { final Picture picture = recorder.endRecording(); builder.pushOffset(_left.swing(), _top.swing()); - builder.addPicture( - Offset.zero, - picture, - willChangeHint: true, - ); + builder.addPicture(Offset.zero, picture, willChangeHint: true); final Scene scene = builder.build(); view.render(scene); scene.dispose(); @@ -72,8 +69,7 @@ class AnimatedColorSquareScenario extends Scenario { } class _NumberSwinger { - _NumberSwinger(this._begin, this._end, [T? current]) - : _up = _begin < _end { + _NumberSwinger(this._begin, this._end, [T? current]) : _up = _begin < _end { _current = current ?? _begin; } diff --git a/testing/scenario_app/lib/src/bogus_font_text.dart b/testing/scenario_app/lib/src/bogus_font_text.dart index 57061c0f8cdd2..f8d8433009c2c 100644 --- a/testing/scenario_app/lib/src/bogus_font_text.dart +++ b/testing/scenario_app/lib/src/bogus_font_text.dart @@ -34,11 +34,7 @@ class BogusFontText extends Scenario { canvas.drawParagraph(paragraph, const Offset(50, 80)); final Picture picture = recorder.endRecording(); - builder.addPicture( - Offset.zero, - picture, - willChangeHint: true, - ); + builder.addPicture(Offset.zero, picture, willChangeHint: true); final Scene scene = builder.build(); view.render(scene); scene.dispose(); @@ -46,9 +42,7 @@ class BogusFontText extends Scenario { sendJsonMessage( dispatcher: view.platformDispatcher, channel: 'display_data', - json: { - 'data': 'ready', - }, + json: {'data': 'ready'}, ); } } diff --git a/testing/scenario_app/lib/src/channel_util.dart b/testing/scenario_app/lib/src/channel_util.dart index ee2cfda304e13..8fa24d0ee6379 100644 --- a/testing/scenario_app/lib/src/channel_util.dart +++ b/testing/scenario_app/lib/src/channel_util.dart @@ -17,10 +17,7 @@ void sendJsonMethodCall({ sendJsonMessage( dispatcher: dispatcher, channel: channel, - json: { - 'method': method, - 'args': arguments, - }, + json: {'method': method, 'args': arguments}, ); } @@ -35,9 +32,7 @@ void sendJsonMessage({ channel, // This recreates a combination of OptionalMethodChannel, JSONMethodCodec, // and _DefaultBinaryMessenger in the framework. - utf8.encode( - const JsonCodec().encode(json) - ).buffer.asByteData(), + utf8.encode(const JsonCodec().encode(json)).buffer.asByteData(), callback, ); } diff --git a/testing/scenario_app/lib/src/darwin_app_extension_scenario.dart b/testing/scenario_app/lib/src/darwin_app_extension_scenario.dart index 354810a5d8d23..1269e9ec0f830 100644 --- a/testing/scenario_app/lib/src/darwin_app_extension_scenario.dart +++ b/testing/scenario_app/lib/src/darwin_app_extension_scenario.dart @@ -32,11 +32,7 @@ class DarwinAppExtensionScenario extends Scenario { canvas.drawParagraph(paragraph, const Offset(50, 80)); final Picture picture = recorder.endRecording(); - builder.addPicture( - Offset.zero, - picture, - willChangeHint: true, - ); + builder.addPicture(Offset.zero, picture, willChangeHint: true); final Scene scene = builder.build(); view.render(scene); scene.dispose(); diff --git a/testing/scenario_app/lib/src/darwin_system_font.dart b/testing/scenario_app/lib/src/darwin_system_font.dart index 92bc1357625b7..edeee37d806f9 100644 --- a/testing/scenario_app/lib/src/darwin_system_font.dart +++ b/testing/scenario_app/lib/src/darwin_system_font.dart @@ -42,11 +42,7 @@ class DarwinSystemFont extends Scenario { final Picture picture = recorder.endRecording(); - builder.addPicture( - Offset.zero, - picture, - willChangeHint: true, - ); + builder.addPicture(Offset.zero, picture, willChangeHint: true); final Scene scene = builder.build(); view.render(scene); scene.dispose(); @@ -54,9 +50,7 @@ class DarwinSystemFont extends Scenario { sendJsonMessage( dispatcher: view.platformDispatcher, channel: 'display_data', - json: { - 'data': 'ready', - }, + json: {'data': 'ready'}, ); } } diff --git a/testing/scenario_app/lib/src/get_bitmap_scenario.dart b/testing/scenario_app/lib/src/get_bitmap_scenario.dart index 73c7ed03c62ec..3d4aef7147b27 100644 --- a/testing/scenario_app/lib/src/get_bitmap_scenario.dart +++ b/testing/scenario_app/lib/src/get_bitmap_scenario.dart @@ -15,12 +15,14 @@ class GetBitmapScenario extends Scenario { void onBeginFrame(Duration duration) { final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - canvas.drawRect(Rect.fromLTWH(0, 0, view.physicalSize.width, 300), - Paint()..color = const Color(0xFFFF0000)); canvas.drawRect( - Rect.fromLTWH(0, view.physicalSize.height - 300, - view.physicalSize.width, 300), - Paint()..color = const Color(0xFF0000FF)); + Rect.fromLTWH(0, 0, view.physicalSize.width, 300), + Paint()..color = const Color(0xFFFF0000), + ); + canvas.drawRect( + Rect.fromLTWH(0, view.physicalSize.height - 300, view.physicalSize.width, 300), + Paint()..color = const Color(0xFF0000FF), + ); final Picture picture = recorder.endRecording(); final SceneBuilder builder = SceneBuilder(); builder.addPicture(Offset.zero, picture); diff --git a/testing/scenario_app/lib/src/locale_initialization.dart b/testing/scenario_app/lib/src/locale_initialization.dart index 74eceaa53a526..e13dfab39829a 100644 --- a/testing/scenario_app/lib/src/locale_initialization.dart +++ b/testing/scenario_app/lib/src/locale_initialization.dart @@ -30,10 +30,7 @@ class LocaleInitialization extends Scenario { ); final Picture picture = recorder.endRecording(); - builder.addPicture( - Offset.zero, - picture, - ); + builder.addPicture(Offset.zero, picture); final Scene scene = builder.build(); view.render(scene); scene.dispose(); @@ -42,43 +39,43 @@ class LocaleInitialization extends Scenario { // corresponding semantics tree comprised of 1 node with the locale data // as the label. final SemanticsUpdateBuilder semanticsUpdateBuilder = - SemanticsUpdateBuilder()..updateNode( - id: 0, - // SemanticsFlag.isTextField. - flags: 16, - // SemanticsAction.tap. - actions: 1, - rect: const Rect.fromLTRB(0.0, 0.0, 414.0, 48.0), - identifier: '', - label: view.platformDispatcher.locales.toString(), - labelAttributes: [], - textDirection: TextDirection.ltr, - textSelectionBase: -1, - textSelectionExtent: -1, - platformViewId: -1, - maxValueLength: -1, - currentValueLength: 0, - scrollChildren: 0, - scrollIndex: 0, - scrollPosition: 0.0, - scrollExtentMax: 0.0, - scrollExtentMin: 0.0, - transform: Matrix4.identity().storage, - elevation: 0.0, - thickness: 0.0, - hint: '', - hintAttributes: [], - value: '', - valueAttributes: [], - increasedValue: '', - increasedValueAttributes: [], - decreasedValue: '', - decreasedValueAttributes: [], - tooltip: '', - childrenInTraversalOrder: Int32List(0), - childrenInHitTestOrder: Int32List(0), - additionalActions: Int32List(0), - ); + SemanticsUpdateBuilder()..updateNode( + id: 0, + // SemanticsFlag.isTextField. + flags: 16, + // SemanticsAction.tap. + actions: 1, + rect: const Rect.fromLTRB(0.0, 0.0, 414.0, 48.0), + identifier: '', + label: view.platformDispatcher.locales.toString(), + labelAttributes: [], + textDirection: TextDirection.ltr, + textSelectionBase: -1, + textSelectionExtent: -1, + platformViewId: -1, + maxValueLength: -1, + currentValueLength: 0, + scrollChildren: 0, + scrollIndex: 0, + scrollPosition: 0.0, + scrollExtentMax: 0.0, + scrollExtentMin: 0.0, + transform: Matrix4.identity().storage, + elevation: 0.0, + thickness: 0.0, + hint: '', + hintAttributes: [], + value: '', + valueAttributes: [], + increasedValue: '', + increasedValueAttributes: [], + decreasedValue: '', + decreasedValueAttributes: [], + tooltip: '', + childrenInTraversalOrder: Int32List(0), + childrenInHitTestOrder: Int32List(0), + additionalActions: Int32List(0), + ); final SemanticsUpdate semanticsUpdate = semanticsUpdateBuilder.build(); @@ -91,53 +88,54 @@ class LocaleInitialization extends Scenario { @override void onPointerDataPacket(PointerDataPacket packet) { String label = ''; - switch(_tapCount) { - case 1: { - // Set label to string data we wish to pass on first frame. - label = '1'; - break; - } + switch (_tapCount) { + case 1: + { + // Set label to string data we wish to pass on first frame. + label = '1'; + break; + } // Expand for other test cases. } final SemanticsUpdateBuilder semanticsUpdateBuilder = - SemanticsUpdateBuilder()..updateNode( - id: 0, - // SemanticsFlag.isTextField. - flags: 16, - // SemanticsAction.tap. - actions: 1, - rect: const Rect.fromLTRB(0.0, 0.0, 414.0, 48.0), - identifier: '', - label: label, - labelAttributes: [], - textDirection: TextDirection.ltr, - textSelectionBase: 0, - textSelectionExtent: 0, - platformViewId: -1, - maxValueLength: -1, - currentValueLength: 0, - scrollChildren: 0, - scrollIndex: 0, - scrollPosition: 0.0, - scrollExtentMax: 0.0, - scrollExtentMin: 0.0, - transform: Matrix4.identity().storage, - elevation: 0.0, - thickness: 0.0, - hint: '', - hintAttributes: [], - value: '', - valueAttributes: [], - increasedValue: '', - increasedValueAttributes: [], - decreasedValue: '', - decreasedValueAttributes: [], - tooltip: '', - childrenInTraversalOrder: Int32List(0), - childrenInHitTestOrder: Int32List(0), - additionalActions: Int32List(0), - ); + SemanticsUpdateBuilder()..updateNode( + id: 0, + // SemanticsFlag.isTextField. + flags: 16, + // SemanticsAction.tap. + actions: 1, + rect: const Rect.fromLTRB(0.0, 0.0, 414.0, 48.0), + identifier: '', + label: label, + labelAttributes: [], + textDirection: TextDirection.ltr, + textSelectionBase: 0, + textSelectionExtent: 0, + platformViewId: -1, + maxValueLength: -1, + currentValueLength: 0, + scrollChildren: 0, + scrollIndex: 0, + scrollPosition: 0.0, + scrollExtentMax: 0.0, + scrollExtentMin: 0.0, + transform: Matrix4.identity().storage, + elevation: 0.0, + thickness: 0.0, + hint: '', + hintAttributes: [], + value: '', + valueAttributes: [], + increasedValue: '', + increasedValueAttributes: [], + decreasedValue: '', + decreasedValueAttributes: [], + tooltip: '', + childrenInTraversalOrder: Int32List(0), + childrenInHitTestOrder: Int32List(0), + additionalActions: Int32List(0), + ); final SemanticsUpdate semanticsUpdate = semanticsUpdateBuilder.build(); diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 2dc040da3c9c3..a13785de0d901 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -37,13 +37,9 @@ List _encodeString(String value) { } /// A simple platform view. -class PlatformViewScenario extends Scenario - with _BasePlatformViewScenarioMixin { +class PlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. - PlatformViewScenario( - super.view, { - required this.id, - }); + PlatformViewScenario(super.view, {required this.id}); /// The platform view identifier. final int id; @@ -51,11 +47,7 @@ class PlatformViewScenario extends Scenario @override void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder(); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); finishBuilder(builder); } } @@ -64,10 +56,7 @@ class PlatformViewScenario extends Scenario class NonFullScreenFlutterViewPlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. - NonFullScreenFlutterViewPlatformViewScenario( - super.view, { - required this.id, - }); + NonFullScreenFlutterViewPlatformViewScenario(super.view, {required this.id}); /// The platform view identifier. final int id; @@ -75,11 +64,7 @@ class NonFullScreenFlutterViewPlatformViewScenario extends Scenario @override void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder(); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); finishBuilder(builder); } } @@ -88,10 +73,7 @@ class NonFullScreenFlutterViewPlatformViewScenario extends Scenario class PlatformViewNoOverlayIntersectionScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. - PlatformViewNoOverlayIntersectionScenario( - super.view, { - required this.id, - }); + PlatformViewNoOverlayIntersectionScenario(super.view, {required this.id}); /// The platform view identifier. final int id; @@ -100,29 +82,18 @@ class PlatformViewNoOverlayIntersectionScenario extends Scenario void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder(); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); - finishBuilder( - builder, - overlayOffset: const Offset(150, 350), - ); + finishBuilder(builder, overlayOffset: const Offset(150, 350)); } } /// A platform view that is larger than the display size. /// This is only applicable on Android while using virtual displays. /// Related issue: https://github.com/flutter/flutter/issues/28978. -class PlatformViewLargerThanDisplaySize extends Scenario - with _BasePlatformViewScenarioMixin { +class PlatformViewLargerThanDisplaySize extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. - PlatformViewLargerThanDisplaySize( - super.view, { - required this.id, - }); + PlatformViewLargerThanDisplaySize(super.view, {required this.id}); /// The platform view identifier. final int id; @@ -139,20 +110,14 @@ class PlatformViewLargerThanDisplaySize extends Scenario height: 60000, ); - finishBuilder( - builder, - ); + finishBuilder(builder); } } /// A simple platform view with an overlay that partially intersects with the platform view. -class PlatformViewPartialIntersectionScenario extends Scenario - with _BasePlatformViewScenarioMixin { +class PlatformViewPartialIntersectionScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. - PlatformViewPartialIntersectionScenario( - super.view, { - required this.id, - }); + PlatformViewPartialIntersectionScenario(super.view, {required this.id}); /// The platform view identifier . final int id; @@ -161,16 +126,9 @@ class PlatformViewPartialIntersectionScenario extends Scenario void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder(); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); - finishBuilder( - builder, - overlayOffset: const Offset(150, 240), - ); + finishBuilder(builder, overlayOffset: const Offset(150, 240)); } } @@ -178,10 +136,7 @@ class PlatformViewPartialIntersectionScenario extends Scenario class PlatformViewTwoIntersectingOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. - PlatformViewTwoIntersectingOverlaysScenario( - super.view, { - required this.id, - }); + PlatformViewTwoIntersectingOverlaysScenario(super.view, {required this.id}); /// The platform view identifier. final int id; @@ -190,23 +145,11 @@ class PlatformViewTwoIntersectingOverlaysScenario extends Scenario void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder(); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - canvas.drawCircle( - const Offset(50, 50), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(100, 100), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); + canvas.drawCircle(const Offset(50, 50), 50, Paint()..color = const Color(0xFFABCDEF)); + canvas.drawCircle(const Offset(100, 100), 50, Paint()..color = const Color(0xFFABCDEF)); final Picture picture = recorder.endRecording(); builder.addPicture(const Offset(300, 300), picture); final Scene scene = builder.build(); @@ -219,10 +162,7 @@ class PlatformViewTwoIntersectingOverlaysScenario extends Scenario class PlatformViewOneOverlayTwoIntersectingOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. - PlatformViewOneOverlayTwoIntersectingOverlaysScenario( - super.view, { - required this.id, - }); + PlatformViewOneOverlayTwoIntersectingOverlaysScenario(super.view, {required this.id}); /// The platform view identifier. final int id; @@ -231,29 +171,13 @@ class PlatformViewOneOverlayTwoIntersectingOverlaysScenario extends Scenario void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder(); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - canvas.drawCircle( - const Offset(50, 50), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(100, 100), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(-100, 200), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); + canvas.drawCircle(const Offset(50, 50), 50, Paint()..color = const Color(0xFFABCDEF)); + canvas.drawCircle(const Offset(100, 100), 50, Paint()..color = const Color(0xFFABCDEF)); + canvas.drawCircle(const Offset(-100, 200), 50, Paint()..color = const Color(0xFFABCDEF)); final Picture picture = recorder.endRecording(); builder.addPicture(const Offset(300, 300), picture); final Scene scene = builder.build(); @@ -302,10 +226,7 @@ class MultiPlatformViewWithoutOverlaysScenario extends Scenario final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - canvas.drawRect( - const Rect.fromLTRB(0, 0, 100, 1000), - Paint()..color = const Color(0xFFFF0000), - ); + canvas.drawRect(const Rect.fromLTRB(0, 0, 100, 1000), Paint()..color = const Color(0xFFFF0000)); final Picture picture = recorder.endRecording(); builder.addPicture(const Offset(580, 0), picture); @@ -317,13 +238,9 @@ class MultiPlatformViewWithoutOverlaysScenario extends Scenario } /// A simple platform view with too many overlays result in a single native view. -class PlatformViewMaxOverlaysScenario extends Scenario - with _BasePlatformViewScenarioMixin { +class PlatformViewMaxOverlaysScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. - PlatformViewMaxOverlaysScenario( - super.view, { - required this.id, - }); + PlatformViewMaxOverlaysScenario(super.view, {required this.id}); /// The platform view identifier. final int id; @@ -332,33 +249,13 @@ class PlatformViewMaxOverlaysScenario extends Scenario void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder(); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - canvas.drawCircle( - const Offset(50, 50), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(100, 100), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(-100, 200), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); - canvas.drawCircle( - const Offset(-100, -80), - 50, - Paint()..color = const Color(0xFFABCDEF), - ); + canvas.drawCircle(const Offset(50, 50), 50, Paint()..color = const Color(0xFFABCDEF)); + canvas.drawCircle(const Offset(100, 100), 50, Paint()..color = const Color(0xFFABCDEF)); + canvas.drawCircle(const Offset(-100, 200), 50, Paint()..color = const Color(0xFFABCDEF)); + canvas.drawCircle(const Offset(-100, -80), 50, Paint()..color = const Color(0xFFABCDEF)); final Picture picture = recorder.endRecording(); builder.addPicture(const Offset(300, 300), picture); final Scene scene = builder.build(); @@ -371,10 +268,7 @@ class PlatformViewMaxOverlaysScenario extends Scenario class PlatformViewSurroundingLayersFractionalCoordinateScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. - PlatformViewSurroundingLayersFractionalCoordinateScenario( - super.view, { - required this.id, - }); + PlatformViewSurroundingLayersFractionalCoordinateScenario(super.view, {required this.id}); /// The platform view identifier. final int id; @@ -404,28 +298,16 @@ class PlatformViewSurroundingLayersFractionalCoordinateScenario extends Scenario const Rect rect = Rect.fromLTWH(100, 100, 100, 100); // Rect at the left of platform view - canvas.drawRect( - rect.shift(const Offset(-100, 0)), - Paint()..color = const Color(0x22FF0000), - ); + canvas.drawRect(rect.shift(const Offset(-100, 0)), Paint()..color = const Color(0x22FF0000)); // Rect at the right of platform view - canvas.drawRect( - rect.shift(const Offset(100, 0)), - Paint()..color = const Color(0x22FF0000), - ); + canvas.drawRect(rect.shift(const Offset(100, 0)), Paint()..color = const Color(0x22FF0000)); // Rect at the top of platform view - canvas.drawRect( - rect.shift(const Offset(0, -100)), - Paint()..color = const Color(0x22FF0000), - ); + canvas.drawRect(rect.shift(const Offset(0, -100)), Paint()..color = const Color(0x22FF0000)); // Rect at the bottom of platform view - canvas.drawRect( - rect.shift(const Offset(0, 100)), - Paint()..color = const Color(0x22FF0000), - ); + canvas.drawRect(rect.shift(const Offset(0, 100)), Paint()..color = const Color(0x22FF0000)); final Picture picture = recorder.endRecording(); builder.addPicture(Offset.zero, picture); @@ -443,10 +325,7 @@ class PlatformViewSurroundingLayersFractionalCoordinateScenario extends Scenario class PlatformViewPartialIntersectionFractionalCoordinateScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. - PlatformViewPartialIntersectionFractionalCoordinateScenario( - super.view, { - required this.id, - }); + PlatformViewPartialIntersectionFractionalCoordinateScenario(super.view, {required this.id}); /// The platform view identifier. final int id; @@ -489,14 +368,9 @@ class PlatformViewPartialIntersectionFractionalCoordinateScenario extends Scenar } /// Builds a scene with 2 platform views. -class MultiPlatformViewScenario extends Scenario - with _BasePlatformViewScenarioMixin { +class MultiPlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. - MultiPlatformViewScenario( - super.view, { - required this.firstId, - required this.secondId, - }); + MultiPlatformViewScenario(super.view, {required this.firstId, required this.secondId}); /// The platform view identifier to use for the first platform view. final int firstId; @@ -587,10 +461,7 @@ class MultiPlatformViewBackgroundForegroundScenario extends Scenario final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - canvas.drawRect( - const Rect.fromLTRB(0, 0, 500, 1000), - Paint()..color = const Color(0xFFFF0000), - ); + canvas.drawRect(const Rect.fromLTRB(0, 0, 500, 1000), Paint()..color = const Color(0xFFFF0000)); final Picture picture = recorder.endRecording(); builder.addPicture(Offset.zero, picture); @@ -627,10 +498,7 @@ class MultiPlatformViewBackgroundForegroundScenario extends Scenario String _lastLifecycleState = ''; - void _onPlatformMessage( - ByteData? data, - PlatformMessageResponseCallback? callback, - ) { + void _onPlatformMessage(ByteData? data, PlatformMessageResponseCallback? callback) { final String message = utf8.decode(data!.buffer.asUint8List()); // The expected first event should be 'AppLifecycleState.resumed', but @@ -658,51 +526,39 @@ class MultiPlatformViewBackgroundForegroundScenario extends Scenario /// Platform view with clip rect. class PlatformViewClipRectScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Constructs a platform view with clip rect scenario. - PlatformViewClipRectScenario( - super.view, { - required this.id, - }); + PlatformViewClipRectScenario(super.view, {required this.id}); /// The platform view identifier. final int id; @override void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder() - ..pushClipRect(const Rect.fromLTRB(100, 100, 400, 400)); + final SceneBuilder builder = + SceneBuilder()..pushClipRect(const Rect.fromLTRB(100, 100, 400, 400)); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); finishBuilder(builder); } } /// Platform view with clip rect, with multiple clips. -class PlatformViewClipRectMultipleClipsScenario extends Scenario with _BasePlatformViewScenarioMixin { +class PlatformViewClipRectMultipleClipsScenario extends Scenario + with _BasePlatformViewScenarioMixin { /// Constructs a platform view with clip rect scenario. - PlatformViewClipRectMultipleClipsScenario( - super.view, { - required this.id, - }); + PlatformViewClipRectMultipleClipsScenario(super.view, {required this.id}); /// The platform view identifier. final int id; @override void onBeginFrame(Duration duration) { - final SceneBuilder builder = SceneBuilder() - ..pushClipRect(const Rect.fromLTRB(100, 100, 400, 400)) - ..pushClipRect(const Rect.fromLTRB(200, 200, 600, 600)); + final SceneBuilder builder = + SceneBuilder() + ..pushClipRect(const Rect.fromLTRB(100, 100, 400, 400)) + ..pushClipRect(const Rect.fromLTRB(200, 200, 600, 600)); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); finishBuilder(builder); } @@ -713,10 +569,7 @@ class PlatformViewClipRectMultipleClipsScenario extends Scenario with _BasePlatf /// The clip rect moves with the same transform matrix with the PlatformView. class PlatformViewClipRectAfterMovedScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Constructs a platform view with clip rect scenario. - PlatformViewClipRectAfterMovedScenario( - super.view, { - required this.id, - }); + PlatformViewClipRectAfterMovedScenario(super.view, {required this.id}); /// The platform view identifier. final int id; @@ -728,12 +581,13 @@ class PlatformViewClipRectAfterMovedScenario extends Scenario with _BasePlatform @override void onBeginFrame(Duration duration) { final Matrix4 translateMatrix = Matrix4.identity()..translate(0.0, _y); - final SceneBuilder builder = SceneBuilder() - ..pushTransform(translateMatrix.storage) - ..pushClipRect(const Rect.fromLTRB(100, 100, 400, 400)); + final SceneBuilder builder = + SceneBuilder() + ..pushTransform(translateMatrix.storage) + ..pushClipRect(const Rect.fromLTRB(100, 100, 400, 400)); addPlatformView( - _numberOfFrames == 10? 10000: id, + _numberOfFrames == 10 ? 10000 : id, dispatcher: view.platformDispatcher, sceneBuilder: builder, ); @@ -741,10 +595,7 @@ class PlatformViewClipRectAfterMovedScenario extends Scenario with _BasePlatform // Add a translucent rect that has the same size of PlatformView. final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - canvas.drawRect( - const Rect.fromLTWH(0, 0, 500, 500), - Paint()..color = const Color(0x22FF0000), - ); + canvas.drawRect(const Rect.fromLTWH(0, 0, 500, 500), Paint()..color = const Color(0x22FF0000)); final Picture picture = recorder.endRecording(); builder.addPicture(Offset.zero, picture); @@ -755,7 +606,7 @@ class PlatformViewClipRectAfterMovedScenario extends Scenario with _BasePlatform @override void onDrawFrame() { if (_numberOfFrames < 10) { - _numberOfFrames ++; + _numberOfFrames++; _y -= 10; view.platformDispatcher.scheduleFrame(); } @@ -763,16 +614,13 @@ class PlatformViewClipRectAfterMovedScenario extends Scenario with _BasePlatform } } - /// Platform view with clip rect with multiple clips then the PlatformView is moved for 10 frames. /// /// The clip rect moves with the same transform matrix with the PlatformView. -class PlatformViewClipRectAfterMovedMultipleClipsScenario extends Scenario with _BasePlatformViewScenarioMixin { +class PlatformViewClipRectAfterMovedMultipleClipsScenario extends Scenario + with _BasePlatformViewScenarioMixin { /// Constructs a platform view with clip rect scenario. - PlatformViewClipRectAfterMovedMultipleClipsScenario( - super.view, { - required this.id, - }); + PlatformViewClipRectAfterMovedMultipleClipsScenario(super.view, {required this.id}); /// The platform view identifier. final int id; @@ -784,13 +632,14 @@ class PlatformViewClipRectAfterMovedMultipleClipsScenario extends Scenario with @override void onBeginFrame(Duration duration) { final Matrix4 translateMatrix = Matrix4.identity()..translate(0.0, _y); - final SceneBuilder builder = SceneBuilder() - ..pushTransform(translateMatrix.storage) - ..pushClipRect(const Rect.fromLTRB(100, 100, 400, 400)) - ..pushClipRect(const Rect.fromLTRB(200, 200, 600, 600)); + final SceneBuilder builder = + SceneBuilder() + ..pushTransform(translateMatrix.storage) + ..pushClipRect(const Rect.fromLTRB(100, 100, 400, 400)) + ..pushClipRect(const Rect.fromLTRB(200, 200, 600, 600)); addPlatformView( - _numberOfFrames == 10? 10000: id, + _numberOfFrames == 10 ? 10000 : id, dispatcher: view.platformDispatcher, sceneBuilder: builder, ); @@ -798,10 +647,7 @@ class PlatformViewClipRectAfterMovedMultipleClipsScenario extends Scenario with // Add a translucent rect that has the same size of PlatformView. final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - canvas.drawRect( - const Rect.fromLTWH(0, 0, 500, 500), - Paint()..color = const Color(0x22FF0000), - ); + canvas.drawRect(const Rect.fromLTWH(0, 0, 500, 500), Paint()..color = const Color(0x22FF0000)); final Picture picture = recorder.endRecording(); builder.addPicture(Offset.zero, picture); @@ -812,7 +658,7 @@ class PlatformViewClipRectAfterMovedMultipleClipsScenario extends Scenario with @override void onDrawFrame() { if (_numberOfFrames < 10) { - _numberOfFrames ++; + _numberOfFrames++; _y -= 10; view.platformDispatcher.scheduleFrame(); } @@ -820,14 +666,10 @@ class PlatformViewClipRectAfterMovedMultipleClipsScenario extends Scenario with } } - /// Platform view with clip rrect. class PlatformViewClipRRectScenario extends PlatformViewScenario { /// Constructs a platform view with clip rrect scenario. - PlatformViewClipRRectScenario( - super.view, { - super.id = 0, - }); + PlatformViewClipRRectScenario(super.view, {super.id = 0}); @override void onBeginFrame(Duration duration) { @@ -844,11 +686,7 @@ class PlatformViewClipRRectScenario extends PlatformViewScenario { ), ); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); finishBuilder(builder); } @@ -857,32 +695,26 @@ class PlatformViewClipRRectScenario extends PlatformViewScenario { /// Platform view with clip rrect, with multiple clips. class PlatformViewClipRRectMultipleClipsScenario extends PlatformViewScenario { /// Constructs a platform view with clip rrect scenario. - PlatformViewClipRRectMultipleClipsScenario( - super.view, { - super.id = 0, - }); + PlatformViewClipRRectMultipleClipsScenario(super.view, {super.id = 0}); @override void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder(); - builder..pushClipRRect( - RRect.fromLTRBAndCorners( - 100, - 100, - 400, - 400, - topLeft: const Radius.circular(15), - topRight: const Radius.circular(50), - bottomLeft: const Radius.circular(50), - ), - ) + builder + ..pushClipRRect( + RRect.fromLTRBAndCorners( + 100, + 100, + 400, + 400, + topLeft: const Radius.circular(15), + topRight: const Radius.circular(50), + bottomLeft: const Radius.circular(50), + ), + ) ..pushClipRect(const Rect.fromLTRB(200, 0, 600, 600)); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); finishBuilder(builder); } @@ -892,10 +724,7 @@ class PlatformViewClipRRectMultipleClipsScenario extends PlatformViewScenario { /// The bounding rect of the rrect is the same as PlatformView and only the corner radii clips the PlatformView. class PlatformViewLargeClipRRectScenario extends PlatformViewScenario { /// Constructs a platform view with large clip rrect scenario. - PlatformViewLargeClipRRectScenario( - super.view, { - super.id = 0, - }); + PlatformViewLargeClipRRectScenario(super.view, {super.id = 0}); @override void onBeginFrame(Duration duration) { @@ -912,11 +741,7 @@ class PlatformViewLargeClipRRectScenario extends PlatformViewScenario { ), ); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); finishBuilder(builder); } @@ -926,32 +751,26 @@ class PlatformViewLargeClipRRectScenario extends PlatformViewScenario { /// The bounding rect of the rrect is the same as PlatformView and only the corner radii clips the PlatformView. class PlatformViewLargeClipRRectMultipleClipsScenario extends PlatformViewScenario { /// Constructs a platform view with large clip rrect scenario. - PlatformViewLargeClipRRectMultipleClipsScenario( - super.view, { - super.id = 0, - }); + PlatformViewLargeClipRRectMultipleClipsScenario(super.view, {super.id = 0}); @override void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder(); - builder..pushClipRRect( - RRect.fromLTRBAndCorners( - 0, - 0, - 500, - 500, - topLeft: const Radius.circular(15), - topRight: const Radius.circular(50), - bottomLeft: const Radius.circular(50), - ), - ) + builder + ..pushClipRRect( + RRect.fromLTRBAndCorners( + 0, + 0, + 500, + 500, + topLeft: const Radius.circular(15), + topRight: const Radius.circular(50), + bottomLeft: const Radius.circular(50), + ), + ) ..pushClipRect(const Rect.fromLTRB(200, 0, 600, 600)); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); finishBuilder(builder); } @@ -960,26 +779,20 @@ class PlatformViewLargeClipRRectMultipleClipsScenario extends PlatformViewScenar /// Platform view with clip path. class PlatformViewClipPathScenario extends PlatformViewScenario { /// Constructs a platform view with clip path scenario. - PlatformViewClipPathScenario( - super.view, { - super.id = 0, - }); + PlatformViewClipPathScenario(super.view, {super.id = 0}); @override void onBeginFrame(Duration duration) { - final Path path = Path() - ..moveTo(100, 100) - ..quadraticBezierTo(50, 250, 100, 400) - ..lineTo(350, 400) - ..cubicTo(400, 300, 300, 200, 350, 100) - ..close(); + final Path path = + Path() + ..moveTo(100, 100) + ..quadraticBezierTo(50, 250, 100, 400) + ..lineTo(350, 400) + ..cubicTo(400, 300, 300, 200, 350, 100) + ..close(); final SceneBuilder builder = SceneBuilder()..pushClipPath(path); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); finishBuilder(builder); } } @@ -987,28 +800,24 @@ class PlatformViewClipPathScenario extends PlatformViewScenario { /// Platform view with clip path, with multiple clips. class PlatformViewClipPathMultipleClipsScenario extends PlatformViewScenario { /// Constructs a platform view with clip path scenario. - PlatformViewClipPathMultipleClipsScenario( - super.view, { - super.id = 0, - }); + PlatformViewClipPathMultipleClipsScenario(super.view, {super.id = 0}); @override void onBeginFrame(Duration duration) { - final Path path = Path() - ..moveTo(100, 100) - ..quadraticBezierTo(50, 250, 100, 400) - ..lineTo(350, 400) - ..cubicTo(400, 300, 300, 200, 350, 100) - ..close(); - - final SceneBuilder builder = SceneBuilder()..pushClipPath(path) - ..pushClipRect(const Rect.fromLTRB(200, 200, 600, 600)); - - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + final Path path = + Path() + ..moveTo(100, 100) + ..quadraticBezierTo(50, 250, 100, 400) + ..lineTo(350, 400) + ..cubicTo(400, 300, 300, 200, 350, 100) + ..close(); + + final SceneBuilder builder = + SceneBuilder() + ..pushClipPath(path) + ..pushClipRect(const Rect.fromLTRB(200, 200, 600, 600)); + + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); finishBuilder(builder); } } @@ -1016,34 +825,25 @@ class PlatformViewClipPathMultipleClipsScenario extends PlatformViewScenario { /// Platform view with clip rect after transformed. class PlatformViewClipRectWithTransformScenario extends PlatformViewScenario { /// Constructs a platform view with clip rect with transform scenario. - PlatformViewClipRectWithTransformScenario( - super.view, { - super.id = 0, - }); + PlatformViewClipRectWithTransformScenario(super.view, {super.id = 0}); @override void onBeginFrame(Duration duration) { - final Matrix4 matrix4 = Matrix4.identity() - ..rotateZ(1) - ..scale(0.5, 0.5, 1.0) - ..translate(1000.0, 100.0); + final Matrix4 matrix4 = + Matrix4.identity() + ..rotateZ(1) + ..scale(0.5, 0.5, 1.0) + ..translate(1000.0, 100.0); final SceneBuilder builder = SceneBuilder()..pushTransform(matrix4.storage); builder.pushClipRect(const Rect.fromLTRB(100, 100, 400, 400)); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); // Add a translucent rect that has the same size of PlatformView. final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - canvas.drawRect( - const Rect.fromLTWH(0, 0, 500, 500), - Paint()..color = const Color(0x22FF0000), - ); + canvas.drawRect(const Rect.fromLTWH(0, 0, 500, 500), Paint()..color = const Color(0x22FF0000)); final Picture picture = recorder.endRecording(); builder.addPicture(Offset.zero, picture); @@ -1054,35 +854,27 @@ class PlatformViewClipRectWithTransformScenario extends PlatformViewScenario { /// Platform view with clip rect after transformed, with multiple clips. class PlatformViewClipRectWithTransformMultipleClipsScenario extends PlatformViewScenario { /// Constructs a platform view with clip rect with transform scenario. - PlatformViewClipRectWithTransformMultipleClipsScenario( - super.view, { - super.id = 0, - }); + PlatformViewClipRectWithTransformMultipleClipsScenario(super.view, {super.id = 0}); @override void onBeginFrame(Duration duration) { - final Matrix4 matrix4 = Matrix4.identity() - ..rotateZ(1) - ..scale(0.5, 0.5, 1.0) - ..translate(1000.0, 100.0); + final Matrix4 matrix4 = + Matrix4.identity() + ..rotateZ(1) + ..scale(0.5, 0.5, 1.0) + ..translate(1000.0, 100.0); final SceneBuilder builder = SceneBuilder()..pushTransform(matrix4.storage); - builder..pushClipRect(const Rect.fromLTRB(100, 100, 400, 400)) + builder + ..pushClipRect(const Rect.fromLTRB(100, 100, 400, 400)) ..pushClipRect(const Rect.fromLTRB(200, 200, 600, 600)); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); // Add a translucent rect that has the same size of PlatformView. final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - canvas.drawRect( - const Rect.fromLTWH(0, 0, 500, 500), - Paint()..color = const Color(0x22FF0000), - ); + canvas.drawRect(const Rect.fromLTWH(0, 0, 500, 500), Paint()..color = const Color(0x22FF0000)); final Picture picture = recorder.endRecording(); builder.addPicture(Offset.zero, picture); @@ -1093,17 +885,15 @@ class PlatformViewClipRectWithTransformMultipleClipsScenario extends PlatformVie /// Platform view with clip rrect after transformed. class PlatformViewClipRRectWithTransformScenario extends PlatformViewScenario { /// Constructs a platform view with clip rrect with transform scenario. - PlatformViewClipRRectWithTransformScenario( - super.view, { - super.id = 0, - }); + PlatformViewClipRRectWithTransformScenario(super.view, {super.id = 0}); @override void onBeginFrame(Duration duration) { - final Matrix4 matrix4 = Matrix4.identity() - ..rotateZ(1) - ..scale(0.5, 0.5, 1.0) - ..translate(1000.0, 100.0); + final Matrix4 matrix4 = + Matrix4.identity() + ..rotateZ(1) + ..scale(0.5, 0.5, 1.0) + ..translate(1000.0, 100.0); final SceneBuilder builder = SceneBuilder()..pushTransform(matrix4.storage); builder.pushClipRRect( @@ -1117,19 +907,12 @@ class PlatformViewClipRRectWithTransformScenario extends PlatformViewScenario { bottomLeft: const Radius.circular(50), ), ); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); // Add a translucent rect that has the same size of PlatformView. final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - canvas.drawRect( - const Rect.fromLTWH(0, 0, 500, 500), - Paint()..color = const Color(0x22FF0000), - ); + canvas.drawRect(const Rect.fromLTWH(0, 0, 500, 500), Paint()..color = const Color(0x22FF0000)); final Picture picture = recorder.endRecording(); builder.addPicture(Offset.zero, picture); @@ -1140,45 +923,37 @@ class PlatformViewClipRRectWithTransformScenario extends PlatformViewScenario { /// Platform view with clip rrect after transformed, with multiple clips. class PlatformViewClipRRectWithTransformMultipleClipsScenario extends PlatformViewScenario { /// Constructs a platform view with clip rrect with transform scenario. - PlatformViewClipRRectWithTransformMultipleClipsScenario( - super.view, { - super.id = 0, - }); + PlatformViewClipRRectWithTransformMultipleClipsScenario(super.view, {super.id = 0}); @override void onBeginFrame(Duration duration) { - final Matrix4 matrix4 = Matrix4.identity() - ..rotateZ(1) - ..scale(0.5, 0.5, 1.0) - ..translate(1000.0, 100.0); + final Matrix4 matrix4 = + Matrix4.identity() + ..rotateZ(1) + ..scale(0.5, 0.5, 1.0) + ..translate(1000.0, 100.0); final SceneBuilder builder = SceneBuilder()..pushTransform(matrix4.storage); - builder..pushClipRRect( - RRect.fromLTRBAndCorners( - 100, - 100, - 400, - 400, - topLeft: const Radius.circular(15), - topRight: const Radius.circular(50), - bottomLeft: const Radius.circular(50), - ), - ) + builder + ..pushClipRRect( + RRect.fromLTRBAndCorners( + 100, + 100, + 400, + 400, + topLeft: const Radius.circular(15), + topRight: const Radius.circular(50), + bottomLeft: const Radius.circular(50), + ), + ) ..pushClipRect(const Rect.fromLTRB(200, 0, 600, 600)); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); // Add a translucent rect that has the same size of PlatformView. final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - canvas.drawRect( - const Rect.fromLTWH(0, 0, 500, 500), - Paint()..color = const Color(0x22FF0000), - ); + canvas.drawRect(const Rect.fromLTWH(0, 0, 500, 500), Paint()..color = const Color(0x22FF0000)); final Picture picture = recorder.endRecording(); builder.addPicture(Offset.zero, picture); @@ -1190,17 +965,15 @@ class PlatformViewClipRRectWithTransformMultipleClipsScenario extends PlatformVi /// The bounding rect of the rrect is the same as PlatformView and only the corner radii clips the PlatformView. class PlatformViewLargeClipRRectWithTransformScenario extends PlatformViewScenario { /// Constructs a platform view with large clip rrect with transform scenario. - PlatformViewLargeClipRRectWithTransformScenario( - super.view, { - super.id = 0, - }); + PlatformViewLargeClipRRectWithTransformScenario(super.view, {super.id = 0}); @override void onBeginFrame(Duration duration) { - final Matrix4 matrix4 = Matrix4.identity() - ..rotateZ(1) - ..scale(0.5, 0.5, 1.0) - ..translate(1000.0, 100.0); + final Matrix4 matrix4 = + Matrix4.identity() + ..rotateZ(1) + ..scale(0.5, 0.5, 1.0) + ..translate(1000.0, 100.0); final SceneBuilder builder = SceneBuilder()..pushTransform(matrix4.storage); builder.pushClipRRect( @@ -1214,19 +987,12 @@ class PlatformViewLargeClipRRectWithTransformScenario extends PlatformViewScenar bottomLeft: const Radius.circular(50), ), ); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); // Add a translucent rect that has the same size of PlatformView. final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - canvas.drawRect( - const Rect.fromLTWH(0, 0, 500, 500), - Paint()..color = const Color(0x22FF0000), - ); + canvas.drawRect(const Rect.fromLTWH(0, 0, 500, 500), Paint()..color = const Color(0x22FF0000)); final Picture picture = recorder.endRecording(); builder.addPicture(Offset.zero, picture); @@ -1238,45 +1004,37 @@ class PlatformViewLargeClipRRectWithTransformScenario extends PlatformViewScenar /// The bounding rect of the rrect is the same as PlatformView and only the corner radii clips the PlatformView. class PlatformViewLargeClipRRectWithTransformMultipleClipsScenario extends PlatformViewScenario { /// Constructs a platform view with large clip rrect with transform scenario. - PlatformViewLargeClipRRectWithTransformMultipleClipsScenario( - super.view, { - super.id = 0, - }); + PlatformViewLargeClipRRectWithTransformMultipleClipsScenario(super.view, {super.id = 0}); @override void onBeginFrame(Duration duration) { - final Matrix4 matrix4 = Matrix4.identity() - ..rotateZ(1) - ..scale(0.5, 0.5, 1.0) - ..translate(1000.0, 100.0); + final Matrix4 matrix4 = + Matrix4.identity() + ..rotateZ(1) + ..scale(0.5, 0.5, 1.0) + ..translate(1000.0, 100.0); final SceneBuilder builder = SceneBuilder()..pushTransform(matrix4.storage); - builder..pushClipRRect( - RRect.fromLTRBAndCorners( - 0, - 0, - 500, - 500, - topLeft: const Radius.circular(15), - topRight: const Radius.circular(50), - bottomLeft: const Radius.circular(50), - ), - ) + builder + ..pushClipRRect( + RRect.fromLTRBAndCorners( + 0, + 0, + 500, + 500, + topLeft: const Radius.circular(15), + topRight: const Radius.circular(50), + bottomLeft: const Radius.circular(50), + ), + ) ..pushClipRect(const Rect.fromLTRB(200, 0, 600, 600)); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); // Add a translucent rect that has the same size of PlatformView. final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - canvas.drawRect( - const Rect.fromLTWH(0, 0, 500, 500), - Paint()..color = const Color(0x22FF0000), - ); + canvas.drawRect(const Rect.fromLTWH(0, 0, 500, 500), Paint()..color = const Color(0x22FF0000)); final Picture picture = recorder.endRecording(); builder.addPicture(Offset.zero, picture); @@ -1287,40 +1045,32 @@ class PlatformViewLargeClipRRectWithTransformMultipleClipsScenario extends Platf /// Platform view with clip path after transformed. class PlatformViewClipPathWithTransformScenario extends PlatformViewScenario { /// Constructs a platform view with clip path with transform scenario. - PlatformViewClipPathWithTransformScenario( - super.view, { - super.id = 0, - }); + PlatformViewClipPathWithTransformScenario(super.view, {super.id = 0}); @override void onBeginFrame(Duration duration) { - final Matrix4 matrix4 = Matrix4.identity() - ..rotateZ(1) - ..scale(0.5, 0.5, 1.0) - ..translate(1000.0, 100.0); + final Matrix4 matrix4 = + Matrix4.identity() + ..rotateZ(1) + ..scale(0.5, 0.5, 1.0) + ..translate(1000.0, 100.0); final SceneBuilder builder = SceneBuilder()..pushTransform(matrix4.storage); - final Path path = Path() - ..moveTo(100, 100) - ..quadraticBezierTo(50, 250, 100, 400) - ..lineTo(350, 400) - ..cubicTo(400, 300, 300, 200, 350, 100) - ..close(); + final Path path = + Path() + ..moveTo(100, 100) + ..quadraticBezierTo(50, 250, 100, 400) + ..lineTo(350, 400) + ..cubicTo(400, 300, 300, 200, 350, 100) + ..close(); builder.pushClipPath(path); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); // Add a translucent rect that has the same size of PlatformView. final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - canvas.drawRect( - const Rect.fromLTWH(0, 0, 500, 500), - Paint()..color = const Color(0x22FF0000), - ); + canvas.drawRect(const Rect.fromLTWH(0, 0, 500, 500), Paint()..color = const Color(0x22FF0000)); final Picture picture = recorder.endRecording(); builder.addPicture(Offset.zero, picture); @@ -1331,42 +1081,35 @@ class PlatformViewClipPathWithTransformScenario extends PlatformViewScenario { /// Platform view with clip path after transformed, with multiple clips. class PlatformViewClipPathWithTransformMultipleClipsScenario extends PlatformViewScenario { /// Constructs a platform view with clip path with transform scenario. - PlatformViewClipPathWithTransformMultipleClipsScenario( - super.view, { - super.id = 0, - }); + PlatformViewClipPathWithTransformMultipleClipsScenario(super.view, {super.id = 0}); @override void onBeginFrame(Duration duration) { - final Matrix4 matrix4 = Matrix4.identity() - ..rotateZ(1) - ..scale(0.5, 0.5, 1.0) - ..translate(1000.0, 100.0); + final Matrix4 matrix4 = + Matrix4.identity() + ..rotateZ(1) + ..scale(0.5, 0.5, 1.0) + ..translate(1000.0, 100.0); final SceneBuilder builder = SceneBuilder()..pushTransform(matrix4.storage); - final Path path = Path() - ..moveTo(100, 100) - ..quadraticBezierTo(50, 250, 100, 400) - ..lineTo(350, 400) - ..cubicTo(400, 300, 300, 200, 350, 100) - ..close(); - - builder..pushClipPath(path) + final Path path = + Path() + ..moveTo(100, 100) + ..quadraticBezierTo(50, 250, 100, 400) + ..lineTo(350, 400) + ..cubicTo(400, 300, 300, 200, 350, 100) + ..close(); + + builder + ..pushClipPath(path) ..pushClipRect(const Rect.fromLTRB(200, 200, 600, 600)); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); // Add a translucent rect that has the same size of PlatformView. final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - canvas.drawRect( - const Rect.fromLTWH(0, 0, 500, 500), - Paint()..color = const Color(0x22FF0000), - ); + canvas.drawRect(const Rect.fromLTWH(0, 0, 500, 500), Paint()..color = const Color(0x22FF0000)); final Picture picture = recorder.endRecording(); builder.addPicture(Offset.zero, picture); @@ -1375,14 +1118,9 @@ class PlatformViewClipPathWithTransformMultipleClipsScenario extends PlatformVie } /// Two platform views, both have clip rects -class TwoPlatformViewClipRect extends Scenario - with _BasePlatformViewScenarioMixin { +class TwoPlatformViewClipRect extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. - TwoPlatformViewClipRect( - super.view, { - required this.firstId, - required this.secondId, - }); + TwoPlatformViewClipRect(super.view, {required this.firstId, required this.secondId}); /// The platform view identifier to use for the first platform view. final int firstId; @@ -1424,14 +1162,9 @@ class TwoPlatformViewClipRect extends Scenario } /// Two platform views, both have clip rects, with multiple clips. -class TwoPlatformViewClipRectMultipleClips extends Scenario - with _BasePlatformViewScenarioMixin { +class TwoPlatformViewClipRectMultipleClips extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. - TwoPlatformViewClipRectMultipleClips( - super.view, { - required this.firstId, - required this.secondId, - }); + TwoPlatformViewClipRectMultipleClips(super.view, {required this.firstId, required this.secondId}); /// The platform view identifier to use for the first platform view. final int firstId; @@ -1443,7 +1176,8 @@ class TwoPlatformViewClipRectMultipleClips extends Scenario void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder(); builder.pushOffset(0, 600); - builder..pushClipRect(const Rect.fromLTRB(100, 100, 400, 400)) + builder + ..pushClipRect(const Rect.fromLTRB(100, 100, 400, 400)) ..pushClipRect(const Rect.fromLTRB(200, 200, 600, 600)); addPlatformView( @@ -1458,7 +1192,8 @@ class TwoPlatformViewClipRectMultipleClips extends Scenario builder.pop(); // Use a different rect to differentiate from the 1st clip rect. - builder..pushClipRect(const Rect.fromLTRB(100, 100, 300, 300)) + builder + ..pushClipRect(const Rect.fromLTRB(100, 100, 300, 300)) ..pushClipRect(const Rect.fromLTRB(200, 200, 600, 600)); addPlatformView( @@ -1477,14 +1212,9 @@ class TwoPlatformViewClipRectMultipleClips extends Scenario } /// Two platform views, both have clip rrects -class TwoPlatformViewClipRRect extends Scenario - with _BasePlatformViewScenarioMixin { +class TwoPlatformViewClipRRect extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. - TwoPlatformViewClipRRect( - super.view, { - required this.firstId, - required this.secondId, - }); + TwoPlatformViewClipRRect(super.view, {required this.firstId, required this.secondId}); /// The platform view identifier to use for the first platform view. final int firstId; @@ -1546,8 +1276,7 @@ class TwoPlatformViewClipRRect extends Scenario } /// Two platform views, both have clip rrects, with multiple clips. -class TwoPlatformViewClipRRectMultipleClips extends Scenario - with _BasePlatformViewScenarioMixin { +class TwoPlatformViewClipRRectMultipleClips extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. TwoPlatformViewClipRRectMultipleClips( super.view, { @@ -1565,20 +1294,20 @@ class TwoPlatformViewClipRRectMultipleClips extends Scenario void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder(); builder.pushOffset(0, 600); - builder..pushClipRRect( - RRect.fromLTRBAndCorners( - 0, - 0, - 500, - 500, - topLeft: const Radius.circular(15), - topRight: const Radius.circular(50), - bottomLeft: const Radius.circular(50), - ), - ) + builder + ..pushClipRRect( + RRect.fromLTRBAndCorners( + 0, + 0, + 500, + 500, + topLeft: const Radius.circular(15), + topRight: const Radius.circular(50), + bottomLeft: const Radius.circular(50), + ), + ) ..pushClipRect(const Rect.fromLTRB(200, 0, 600, 600)); - addPlatformView( firstId, dispatcher: view.platformDispatcher, @@ -1591,20 +1320,20 @@ class TwoPlatformViewClipRRectMultipleClips extends Scenario builder.pop(); // Use a different rrect to differentiate from the 1st clip rrect. - builder..pushClipRRect( - RRect.fromLTRBAndCorners( - 0, - 0, - 500, - 500, - topLeft: const Radius.circular(100), - topRight: const Radius.circular(50), - bottomLeft: const Radius.circular(50), - ), - ) + builder + ..pushClipRRect( + RRect.fromLTRBAndCorners( + 0, + 0, + 500, + 500, + topLeft: const Radius.circular(100), + topRight: const Radius.circular(50), + bottomLeft: const Radius.circular(50), + ), + ) ..pushClipRect(const Rect.fromLTRB(200, 0, 600, 600)); - addPlatformView( secondId, dispatcher: view.platformDispatcher, @@ -1621,14 +1350,9 @@ class TwoPlatformViewClipRRectMultipleClips extends Scenario } /// Two platform views, both have clip path -class TwoPlatformViewClipPath extends Scenario - with _BasePlatformViewScenarioMixin { +class TwoPlatformViewClipPath extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. - TwoPlatformViewClipPath( - super.view, { - required this.firstId, - required this.secondId, - }); + TwoPlatformViewClipPath(super.view, {required this.firstId, required this.secondId}); /// The platform view identifier to use for the first platform view. final int firstId; @@ -1640,12 +1364,13 @@ class TwoPlatformViewClipPath extends Scenario void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder(); builder.pushOffset(0, 600); - final Path path = Path() - ..moveTo(100, 100) - ..quadraticBezierTo(50, 250, 100, 400) - ..lineTo(350, 400) - ..cubicTo(400, 300, 300, 200, 350, 100) - ..close(); + final Path path = + Path() + ..moveTo(100, 100) + ..quadraticBezierTo(50, 250, 100, 400) + ..lineTo(350, 400) + ..cubicTo(400, 300, 300, 200, 350, 100) + ..close(); builder.pushClipPath(path); @@ -1660,12 +1385,13 @@ class TwoPlatformViewClipPath extends Scenario builder.pop(); // Use a different path to differentiate from the 1st clip path. - final Path path2 = Path() - ..moveTo(100, 100) - ..quadraticBezierTo(100, 150, 100, 400) - ..lineTo(350, 350) - ..cubicTo(400, 300, 300, 200, 350, 200) - ..close(); + final Path path2 = + Path() + ..moveTo(100, 100) + ..quadraticBezierTo(100, 150, 100, 400) + ..lineTo(350, 350) + ..cubicTo(400, 300, 300, 200, 350, 200) + ..close(); builder.pushClipPath(path2); @@ -1683,16 +1409,10 @@ class TwoPlatformViewClipPath extends Scenario } } - /// Two platform views, both have clip path, with multiple clips. -class TwoPlatformViewClipPathMultipleClips extends Scenario - with _BasePlatformViewScenarioMixin { +class TwoPlatformViewClipPathMultipleClips extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. - TwoPlatformViewClipPathMultipleClips( - super.view, { - required this.firstId, - required this.secondId, - }); + TwoPlatformViewClipPathMultipleClips(super.view, {required this.firstId, required this.secondId}); /// The platform view identifier to use for the first platform view. final int firstId; @@ -1704,17 +1424,18 @@ class TwoPlatformViewClipPathMultipleClips extends Scenario void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder(); builder.pushOffset(0, 600); - final Path path = Path() - ..moveTo(100, 100) - ..quadraticBezierTo(50, 250, 100, 400) - ..lineTo(350, 400) - ..cubicTo(400, 300, 300, 200, 350, 100) - ..close(); - - builder..pushClipPath(path) + final Path path = + Path() + ..moveTo(100, 100) + ..quadraticBezierTo(50, 250, 100, 400) + ..lineTo(350, 400) + ..cubicTo(400, 300, 300, 200, 350, 100) + ..close(); + + builder + ..pushClipPath(path) ..pushClipRect(const Rect.fromLTRB(200, 200, 600, 600)); - addPlatformView( firstId, dispatcher: view.platformDispatcher, @@ -1727,17 +1448,18 @@ class TwoPlatformViewClipPathMultipleClips extends Scenario builder.pop(); // Use a different path to differentiate from the 1st clip path. - final Path path2 = Path() - ..moveTo(100, 100) - ..quadraticBezierTo(100, 150, 100, 400) - ..lineTo(350, 350) - ..cubicTo(400, 300, 300, 200, 350, 200) - ..close(); - - builder..pushClipPath(path2) + final Path path2 = + Path() + ..moveTo(100, 100) + ..quadraticBezierTo(100, 150, 100, 400) + ..lineTo(350, 350) + ..cubicTo(400, 300, 300, 200, 350, 200) + ..close(); + + builder + ..pushClipPath(path2) ..pushClipRect(const Rect.fromLTRB(200, 200, 600, 600)); - addPlatformView( secondId, dispatcher: view.platformDispatcher, @@ -1756,24 +1478,18 @@ class TwoPlatformViewClipPathMultipleClips extends Scenario /// Platform view with transform. class PlatformViewTransformScenario extends PlatformViewScenario { /// Constructs a platform view with transform scenario. - PlatformViewTransformScenario( - super.view, { - super.id = 0, - }); + PlatformViewTransformScenario(super.view, {super.id = 0}); @override void onBeginFrame(Duration duration) { - final Matrix4 matrix4 = Matrix4.identity() - ..rotateZ(1) - ..scale(0.5, 0.5, 1.0) - ..translate(1000.0, 100.0); + final Matrix4 matrix4 = + Matrix4.identity() + ..rotateZ(1) + ..scale(0.5, 0.5, 1.0) + ..translate(1000.0, 100.0); final SceneBuilder builder = SceneBuilder()..pushTransform(matrix4.storage); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); finishBuilder(builder); } } @@ -1781,26 +1497,18 @@ class PlatformViewTransformScenario extends PlatformViewScenario { /// Platform view with opacity. class PlatformViewOpacityScenario extends PlatformViewScenario { /// Constructs a platform view with transform scenario. - PlatformViewOpacityScenario( - super.view, { - super.id = 0, - }); + PlatformViewOpacityScenario(super.view, {super.id = 0}); @override void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder()..pushOpacity(150); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); finishBuilder(builder); } } /// A simple platform view for testing touch events from iOS. -class PlatformViewForTouchIOSScenario extends Scenario - with _BasePlatformViewScenarioMixin { +class PlatformViewForTouchIOSScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. PlatformViewForTouchIOSScenario( super.view, { @@ -1878,11 +1586,7 @@ class PlatformViewForTouchIOSScenario extends Scenario viewType: 'scenarios/textPlatformView_blockPolicyUntilTouchesEnded', ); } else { - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); } finishBuilder(builder); } @@ -1897,11 +1601,7 @@ class PlatformViewForTouchIOSScenario extends Scenario viewType: 'scenarios/textPlatformView_blockPolicyUntilTouchesEnded', ); } else { - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); } finishBuilder(builder); } @@ -1916,7 +1616,6 @@ class PlatformViewForTouchIOSScenario extends Scenario /// class PlatformViewForOverlappingPlatformViewsScenario extends Scenario with _BasePlatformViewScenarioMixin { - /// Creates the PlatformViewForOverlappingPlatformViewsScenario. PlatformViewForOverlappingPlatformViewsScenario( super.view, { @@ -2036,23 +1735,15 @@ class PlatformViewForOverlappingPlatformViewsScenario extends Scenario /// For example, it simulates a video being played. class PlatformViewWithContinuousTexture extends PlatformViewScenario { /// Constructs a platform view with continuous texture layer. - PlatformViewWithContinuousTexture( - super.view, { - super.id = 0, - }); + PlatformViewWithContinuousTexture(super.view, {super.id = 0}); @override void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder(); - builder.addTexture(0, - width: 300, height: 300, offset: const Offset(200, 200)); + builder.addTexture(0, width: 300, height: 300, offset: const Offset(200, 200)); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); finishBuilder(builder); } @@ -2066,10 +1757,7 @@ class PlatformViewWithContinuousTexture extends PlatformViewScenario { /// See: https://github.com/flutter/flutter/issues/80766 class PlatformViewWithOtherBackDropFilter extends PlatformViewScenario { /// Constructs the scenario. - PlatformViewWithOtherBackDropFilter( - super.view, { - super.id = 0, - }); + PlatformViewWithOtherBackDropFilter(super.view, {super.id = 0}); @override void onBeginFrame(Duration duration) { @@ -2078,15 +1766,9 @@ class PlatformViewWithOtherBackDropFilter extends PlatformViewScenario { final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); // This is just a background picture to make the result more viewable. - canvas.drawRect( - const Rect.fromLTRB(0, 0, 500, 400), - Paint()..color = const Color(0xFFFF0000), - ); + canvas.drawRect(const Rect.fromLTRB(0, 0, 500, 400), Paint()..color = const Color(0xFFFF0000)); // This rect should look blur due to the backdrop filter. - canvas.drawRect( - const Rect.fromLTRB(0, 0, 300, 300), - Paint()..color = const Color(0xFF00FF00), - ); + canvas.drawRect(const Rect.fromLTRB(0, 0, 300, 300), Paint()..color = const Color(0xFF00FF00)); final Picture picture = recorder.endRecording(); builder.addPicture(Offset.zero, picture); @@ -2096,11 +1778,7 @@ class PlatformViewWithOtherBackDropFilter extends PlatformViewScenario { final PictureRecorder recorder2 = PictureRecorder(); final Canvas canvas2 = Canvas(recorder2); // This circle should not look blur. - canvas2.drawCircle( - const Offset(200, 100), - 50, - Paint()..color = const Color(0xFF0000EF), - ); + canvas2.drawCircle(const Offset(200, 100), 50, Paint()..color = const Color(0xFF0000EF)); final Picture picture2 = recorder2.endRecording(); builder.addPicture(const Offset(100, 100), picture2); @@ -2108,11 +1786,7 @@ class PlatformViewWithOtherBackDropFilter extends PlatformViewScenario { builder.pushOffset(0, 600); - addPlatformView( - id, - dispatcher: view.platformDispatcher, - sceneBuilder: builder, - ); + addPlatformView(id, dispatcher: view.platformDispatcher, sceneBuilder: builder); builder.pop(); @@ -2120,11 +1794,7 @@ class PlatformViewWithOtherBackDropFilter extends PlatformViewScenario { final Canvas canvas3 = Canvas(recorder3); // Add another picture layer so an overlay UIView is created, which was // the root cause of the original issue. - canvas3.drawCircle( - const Offset(300, 200), - 50, - Paint()..color = const Color(0xFF0000EF), - ); + canvas3.drawCircle(const Offset(300, 200), 50, Paint()..color = const Color(0xFF0000EF)); final Picture picture3 = recorder3.endRecording(); builder.addPicture(const Offset(100, 100), picture3); @@ -2137,15 +1807,11 @@ class PlatformViewWithOtherBackDropFilter extends PlatformViewScenario { /// A simple platform view for testing backDropFilter with a platform view in the scene. /// /// The stack would look like: picture 1 -> pv1 -> picture 2 -> filter -> pv2 - > picture 3. -class TwoPlatformViewsWithOtherBackDropFilter extends Scenario - with _BasePlatformViewScenarioMixin { +class TwoPlatformViewsWithOtherBackDropFilter extends Scenario with _BasePlatformViewScenarioMixin { /// Constructs the scenario. - TwoPlatformViewsWithOtherBackDropFilter( - super.view, { - required int firstId, - required int secondId, - }) : _firstId = firstId, - _secondId = secondId; + TwoPlatformViewsWithOtherBackDropFilter(super.view, {required int firstId, required int secondId}) + : _firstId = firstId, + _secondId = secondId; final int _firstId; final int _secondId; @@ -2157,15 +1823,9 @@ class TwoPlatformViewsWithOtherBackDropFilter extends Scenario final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); // This is just a background picture to make the result more viewable. - canvas.drawRect( - const Rect.fromLTRB(0, 0, 600, 1000), - Paint()..color = const Color(0xFFFF0000), - ); + canvas.drawRect(const Rect.fromLTRB(0, 0, 600, 1000), Paint()..color = const Color(0xFFFF0000)); // This rect should look blur due to the backdrop filter. - canvas.drawRect( - const Rect.fromLTRB(0, 0, 300, 300), - Paint()..color = const Color(0xFF00FF00), - ); + canvas.drawRect(const Rect.fromLTRB(0, 0, 300, 300), Paint()..color = const Color(0xFF00FF00)); final Picture picture1 = recorder.endRecording(); builder.addPicture(Offset.zero, picture1); @@ -2177,17 +1837,13 @@ class TwoPlatformViewsWithOtherBackDropFilter extends Scenario sceneBuilder: builder, width: 100, height: 100, - text: 'platform view 1' + text: 'platform view 1', ); final PictureRecorder recorder2 = PictureRecorder(); final Canvas canvas2 = Canvas(recorder2); // This circle should look blur due to the backdrop filter. - canvas2.drawCircle( - const Offset(200, 100), - 50, - Paint()..color = const Color(0xFF0000EF), - ); + canvas2.drawCircle(const Offset(200, 100), 50, Paint()..color = const Color(0xFF0000EF)); final Picture picture2 = recorder2.endRecording(); builder.addPicture(const Offset(100, 100), picture2); @@ -2211,11 +1867,7 @@ class TwoPlatformViewsWithOtherBackDropFilter extends Scenario final Canvas canvas3 = Canvas(recorder3); // Add another picture layer so an overlay UIView is created, which was // the root cause of the original issue. - canvas3.drawCircle( - const Offset(300, 200), - 50, - Paint()..color = const Color(0xFF0000EF), - ); + canvas3.drawCircle(const Offset(300, 200), 50, Paint()..color = const Color(0xFF0000EF)); final Picture picture3 = recorder3.endRecording(); builder.addPicture(const Offset(100, 100), picture3); @@ -2229,13 +1881,9 @@ class TwoPlatformViewsWithOtherBackDropFilter extends Scenario /// /// The backdrop filter sigma value is negative, which tries to reproduce a crash, see: /// https://github.com/flutter/flutter/issues/127095 -class PlatformViewWithNegativeBackDropFilter extends Scenario - with _BasePlatformViewScenarioMixin { +class PlatformViewWithNegativeBackDropFilter extends Scenario with _BasePlatformViewScenarioMixin { /// Constructs the scenario. - PlatformViewWithNegativeBackDropFilter( - super.view, { - required int id, - }) : _id = id; + PlatformViewWithNegativeBackDropFilter(super.view, {required int id}) : _id = id; final int _id; @@ -2246,14 +1894,8 @@ class PlatformViewWithNegativeBackDropFilter extends Scenario final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); // This is just a background picture to make the result more viewable. - canvas.drawRect( - const Rect.fromLTRB(0, 0, 600, 1000), - Paint()..color = const Color(0xFFFF0000), - ); - canvas.drawRect( - const Rect.fromLTRB(0, 0, 300, 300), - Paint()..color = const Color(0xFF00FF00), - ); + canvas.drawRect(const Rect.fromLTRB(0, 0, 600, 1000), Paint()..color = const Color(0xFFFF0000)); + canvas.drawRect(const Rect.fromLTRB(0, 0, 300, 300), Paint()..color = const Color(0xFF00FF00)); final Picture picture1 = recorder.endRecording(); builder.addPicture(Offset.zero, picture1); @@ -2265,16 +1907,12 @@ class PlatformViewWithNegativeBackDropFilter extends Scenario sceneBuilder: builder, width: 100, height: 100, - text: 'platform view 1' + text: 'platform view 1', ); final PictureRecorder recorder2 = PictureRecorder(); final Canvas canvas2 = Canvas(recorder2); - canvas2.drawCircle( - const Offset(200, 100), - 50, - Paint()..color = const Color(0xFF0000EF), - ); + canvas2.drawCircle(const Offset(200, 100), 50, Paint()..color = const Color(0xFF0000EF)); final Picture picture2 = recorder2.endRecording(); builder.addPicture(const Offset(100, 100), picture2); @@ -2288,14 +1926,13 @@ class PlatformViewWithNegativeBackDropFilter extends Scenario } /// Builds a scenario where many platform views are scrolling and pass under a picture. -class PlatformViewScrollingUnderWidget extends Scenario - with _BasePlatformViewScenarioMixin { +class PlatformViewScrollingUnderWidget extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. PlatformViewScrollingUnderWidget( super.view, { required int firstPlatformViewId, required int lastPlatformViewId, - }) : _firstPlatformViewId = firstPlatformViewId, + }) : _firstPlatformViewId = firstPlatformViewId, _lastPlatformViewId = lastPlatformViewId; final int _firstPlatformViewId; @@ -2366,14 +2003,13 @@ class PlatformViewScrollingUnderWidget extends Scenario } /// Builds a scenario where many platform views with clips scrolling. -class PlatformViewsWithClipsScrolling extends Scenario - with _BasePlatformViewScenarioMixin { +class PlatformViewsWithClipsScrolling extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. PlatformViewsWithClipsScrolling( super.view, { required int firstPlatformViewId, required int lastPlatformViewId, - }) : _firstPlatformViewId = firstPlatformViewId, + }) : _firstPlatformViewId = firstPlatformViewId, _lastPlatformViewId = lastPlatformViewId; final int _firstPlatformViewId; @@ -2459,7 +2095,7 @@ class PlatformViewsWithClipsScrollingMultipleClips extends Scenario super.view, { required int firstPlatformViewId, required int lastPlatformViewId, - }) : _firstPlatformViewId = firstPlatformViewId, + }) : _firstPlatformViewId = firstPlatformViewId, _lastPlatformViewId = lastPlatformViewId; final int _firstPlatformViewId; @@ -2504,19 +2140,19 @@ class PlatformViewsWithClipsScrollingMultipleClips extends Scenario bool addedClipRRect = false; if (localOffset > -1) { addedClipRRect = true; - builder..pushClipRRect( - RRect.fromLTRBAndCorners( - 100, - 100, - 400, - 400, - topLeft: const Radius.circular(15), - topRight: const Radius.circular(50), - bottomLeft: const Radius.circular(50), - ), - ) - ..pushClipRect(const Rect.fromLTRB(200, 0, 600, 600)); - + builder + ..pushClipRRect( + RRect.fromLTRBAndCorners( + 100, + 100, + 400, + 400, + topLeft: const Radius.circular(15), + topRight: const Radius.circular(50), + bottomLeft: const Radius.circular(50), + ), + ) + ..pushClipRect(const Rect.fromLTRB(200, 0, 600, 600)); } addPlatformView( i, @@ -2539,8 +2175,8 @@ class PlatformViewsWithClipsScrollingMultipleClips extends Scenario } } -final Map _createdPlatformViews = {}; -final Map _calledToBeCreatedPlatformViews = {}; +final Map _createdPlatformViews = {}; +final Map _calledToBeCreatedPlatformViews = {}; /// Adds the platform view to the scene. /// @@ -2623,8 +2259,7 @@ void addPlatformView( ..._to32(0), // LTR valueString, ..._encodeString('hybridFallback'), - if (expectAndroidHybridCompositionFallback) valueTrue - else valueFalse, + if (expectAndroidHybridCompositionFallback) valueTrue else valueFalse, ], if (Platform.isAndroid && usesAndroidHybridComposition) ...[ valueString, @@ -2641,33 +2276,29 @@ void addPlatformView( ..._encodeString(text), ]); - dispatcher.sendPlatformMessage( - 'flutter/platform_views', - message.buffer.asByteData(), - (ByteData? response) { - late int textureId; - if (response != null && - Platform.isAndroid && - !usesAndroidHybridComposition) { - assert(response.getUint8(0) == 0, 'expected envelope'); - final int type = response.getUint8(1); - if (expectAndroidHybridCompositionFallback) { - // Fallback is indicated with a null return. - assert(type == 0, 'expected null'); - textureId = -1; - } else { - // This is the texture ID. - assert(type == 4, 'expected int64'); - textureId = response.getInt64(2, Endian.host); - } - } else { - // There no texture ID. + dispatcher.sendPlatformMessage('flutter/platform_views', message.buffer.asByteData(), ( + ByteData? response, + ) { + late int textureId; + if (response != null && Platform.isAndroid && !usesAndroidHybridComposition) { + assert(response.getUint8(0) == 0, 'expected envelope'); + final int type = response.getUint8(1); + if (expectAndroidHybridCompositionFallback) { + // Fallback is indicated with a null return. + assert(type == 0, 'expected null'); textureId = -1; + } else { + // This is the texture ID. + assert(type == 4, 'expected int64'); + textureId = response.getInt64(2, Endian.host); } - _createdPlatformViews[platformViewKey] = textureId; - dispatcher.scheduleFrame(); - }, - ); + } else { + // There no texture ID. + textureId = -1; + } + _createdPlatformViews[platformViewKey] = textureId; + dispatcher.scheduleFrame(); + }); } /// Adds the platform view to the scene builder. @@ -2682,10 +2313,10 @@ Future addPlatformViewToSceneBuilder( sceneBuilder.addPlatformView(id, width: width, height: height); } else if (Platform.isAndroid) { final bool expectAndroidHybridCompositionFallback = - scenarioParams['expect_android_view_fallback'] as bool? ?? false; + scenarioParams['expect_android_view_fallback'] as bool? ?? false; final bool usesAndroidHybridComposition = - (scenarioParams['use_android_view'] as bool? ?? false) || - expectAndroidHybridCompositionFallback; + (scenarioParams['use_android_view'] as bool? ?? false) || + expectAndroidHybridCompositionFallback; if (usesAndroidHybridComposition) { sceneBuilder.addPlatformView(id, width: width, height: height); } else if (textureId != -1) { @@ -2694,25 +2325,17 @@ Future addPlatformViewToSceneBuilder( throw UnsupportedError('Invalid texture id $textureId'); } } else { - throw UnsupportedError( - 'Platform ${Platform.operatingSystem} is not supported'); + throw UnsupportedError('Platform ${Platform.operatingSystem} is not supported'); } } mixin _BasePlatformViewScenarioMixin on Scenario { // Add a picture and finishes the `sceneBuilder`. - void finishBuilder( - SceneBuilder sceneBuilder, { - Offset? overlayOffset, - }) { + void finishBuilder(SceneBuilder sceneBuilder, {Offset? overlayOffset}) { overlayOffset ??= const Offset(50, 50); final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); - canvas.drawCircle( - overlayOffset, - 50, - Paint()..color = const Color(0xFFABCDEF), - ); + canvas.drawCircle(overlayOffset, 50, Paint()..color = const Color(0xFFABCDEF)); final Picture picture = recorder.endRecording(); sceneBuilder.addPicture(const Offset(300, 300), picture); final Scene scene = sceneBuilder.build(); diff --git a/testing/scenario_app/lib/src/poppable_screen.dart b/testing/scenario_app/lib/src/poppable_screen.dart index 8f633bffbd9b7..cc7ac552fa4a3 100644 --- a/testing/scenario_app/lib/src/poppable_screen.dart +++ b/testing/scenario_app/lib/src/poppable_screen.dart @@ -28,10 +28,7 @@ class PoppableScreenScenario extends Scenario { canvas.drawPaint(Paint()..color = const Color.fromARGB(255, 255, 255, 255)); if (_buttonRect != null) { - canvas.drawRect( - _buttonRect!, - Paint()..color = const Color.fromARGB(255, 255, 0, 0), - ); + canvas.drawRect(_buttonRect!, Paint()..color = const Color.fromARGB(255, 255, 0, 0)); } final Picture picture = recorder.endRecording(); @@ -62,8 +59,7 @@ class PoppableScreenScenario extends Scenario { void onPointerDataPacket(PointerDataPacket packet) { for (final PointerData data in packet.data) { if (data.change == PointerChange.up && - (_buttonRect?.contains(Offset(data.physicalX, data.physicalY)) ?? false) - ) { + (_buttonRect?.contains(Offset(data.physicalX, data.physicalY)) ?? false)) { _pop(); } } diff --git a/testing/scenario_app/lib/src/scenarios.dart b/testing/scenario_app/lib/src/scenarios.dart index db50779fb2292..5da957d8eb2fd 100644 --- a/testing/scenario_app/lib/src/scenarios.dart +++ b/testing/scenario_app/lib/src/scenarios.dart @@ -27,61 +27,146 @@ Map _scenarios = { 'solid_blue': (FlutterView view) => SolidBlueScenario(view), 'locale_initialization': (FlutterView view) => LocaleInitialization(view), 'platform_view': (FlutterView view) => PlatformViewScenario(view, id: _viewId++), - 'platform_view_no_overlay_intersection': (FlutterView view) => PlatformViewNoOverlayIntersectionScenario(view, id: _viewId++), - 'platform_view_larger_than_display_size': (FlutterView view) => PlatformViewLargerThanDisplaySize(view, id: _viewId++), - 'platform_view_partial_intersection': (FlutterView view) => PlatformViewPartialIntersectionScenario(view, id: _viewId++), - 'platform_view_two_intersecting_overlays': (FlutterView view) => PlatformViewTwoIntersectingOverlaysScenario(view, id: _viewId++), - 'platform_view_one_overlay_two_intersecting_overlays': (FlutterView view) => PlatformViewOneOverlayTwoIntersectingOverlaysScenario(view, id: _viewId++), - 'platform_view_multiple_without_overlays': (FlutterView view) => MultiPlatformViewWithoutOverlaysScenario(view, firstId: _viewId++, secondId: _viewId++), - 'platform_view_max_overlays': (FlutterView view) => PlatformViewMaxOverlaysScenario(view, id: _viewId++), - 'platform_view_surrounding_layers_fractional_coordinate': (FlutterView view) => PlatformViewSurroundingLayersFractionalCoordinateScenario(view, id: _viewId++), - 'platform_view_partial_intersection_fractional_coordinate': (FlutterView view) => PlatformViewPartialIntersectionFractionalCoordinateScenario(view, id: _viewId++), + 'platform_view_no_overlay_intersection': + (FlutterView view) => PlatformViewNoOverlayIntersectionScenario(view, id: _viewId++), + 'platform_view_larger_than_display_size': + (FlutterView view) => PlatformViewLargerThanDisplaySize(view, id: _viewId++), + 'platform_view_partial_intersection': + (FlutterView view) => PlatformViewPartialIntersectionScenario(view, id: _viewId++), + 'platform_view_two_intersecting_overlays': + (FlutterView view) => PlatformViewTwoIntersectingOverlaysScenario(view, id: _viewId++), + 'platform_view_one_overlay_two_intersecting_overlays': + (FlutterView view) => + PlatformViewOneOverlayTwoIntersectingOverlaysScenario(view, id: _viewId++), + 'platform_view_multiple_without_overlays': + (FlutterView view) => + MultiPlatformViewWithoutOverlaysScenario(view, firstId: _viewId++, secondId: _viewId++), + 'platform_view_max_overlays': + (FlutterView view) => PlatformViewMaxOverlaysScenario(view, id: _viewId++), + 'platform_view_surrounding_layers_fractional_coordinate': + (FlutterView view) => + PlatformViewSurroundingLayersFractionalCoordinateScenario(view, id: _viewId++), + 'platform_view_partial_intersection_fractional_coordinate': + (FlutterView view) => + PlatformViewPartialIntersectionFractionalCoordinateScenario(view, id: _viewId++), 'platform_view_cliprect': (FlutterView view) => PlatformViewClipRectScenario(view, id: _viewId++), - 'platform_view_cliprect_multiple_clips': (FlutterView view) => PlatformViewClipRectMultipleClipsScenario(view, id: _viewId++), - 'platform_view_cliprect_with_transform': (FlutterView view) => PlatformViewClipRectWithTransformScenario(view, id: _viewId++), - 'platform_view_cliprect_with_transform_multiple_clips': (FlutterView view) => PlatformViewClipRectWithTransformMultipleClipsScenario(view, id: _viewId++), - 'platform_view_cliprect_after_moved': (FlutterView view) => PlatformViewClipRectAfterMovedScenario(view, id: _viewId++), - 'platform_view_cliprect_after_moved_multiple_clips': (FlutterView view) => PlatformViewClipRectAfterMovedMultipleClipsScenario(view, id: _viewId++), - 'platform_view_cliprrect': (FlutterView view) => PlatformViewClipRRectScenario(view, id: _viewId++), - 'platform_view_cliprrect_multiple_clips': (FlutterView view) => PlatformViewClipRRectMultipleClipsScenario(view, id: _viewId++), - 'platform_view_large_cliprrect': (FlutterView view) => PlatformViewLargeClipRRectScenario(view, id: _viewId++), - 'platform_view_large_cliprrect_multiple_clips': (FlutterView view) => PlatformViewLargeClipRRectMultipleClipsScenario(view, id: _viewId++), - 'platform_view_cliprrect_with_transform': (FlutterView view) => PlatformViewClipRRectWithTransformScenario(view, id: _viewId++), - 'platform_view_cliprrect_with_transform_multiple_clips': (FlutterView view) => PlatformViewClipRRectWithTransformMultipleClipsScenario(view, id: _viewId++), - 'platform_view_large_cliprrect_with_transform': (FlutterView view) => PlatformViewLargeClipRRectWithTransformScenario(view, id: _viewId++), - 'platform_view_large_cliprrect_with_transform_multiple_clips': (FlutterView view) => PlatformViewLargeClipRRectWithTransformMultipleClipsScenario(view, id: _viewId++), + 'platform_view_cliprect_multiple_clips': + (FlutterView view) => PlatformViewClipRectMultipleClipsScenario(view, id: _viewId++), + 'platform_view_cliprect_with_transform': + (FlutterView view) => PlatformViewClipRectWithTransformScenario(view, id: _viewId++), + 'platform_view_cliprect_with_transform_multiple_clips': + (FlutterView view) => + PlatformViewClipRectWithTransformMultipleClipsScenario(view, id: _viewId++), + 'platform_view_cliprect_after_moved': + (FlutterView view) => PlatformViewClipRectAfterMovedScenario(view, id: _viewId++), + 'platform_view_cliprect_after_moved_multiple_clips': + (FlutterView view) => + PlatformViewClipRectAfterMovedMultipleClipsScenario(view, id: _viewId++), + 'platform_view_cliprrect': + (FlutterView view) => PlatformViewClipRRectScenario(view, id: _viewId++), + 'platform_view_cliprrect_multiple_clips': + (FlutterView view) => PlatformViewClipRRectMultipleClipsScenario(view, id: _viewId++), + 'platform_view_large_cliprrect': + (FlutterView view) => PlatformViewLargeClipRRectScenario(view, id: _viewId++), + 'platform_view_large_cliprrect_multiple_clips': + (FlutterView view) => PlatformViewLargeClipRRectMultipleClipsScenario(view, id: _viewId++), + 'platform_view_cliprrect_with_transform': + (FlutterView view) => PlatformViewClipRRectWithTransformScenario(view, id: _viewId++), + 'platform_view_cliprrect_with_transform_multiple_clips': + (FlutterView view) => + PlatformViewClipRRectWithTransformMultipleClipsScenario(view, id: _viewId++), + 'platform_view_large_cliprrect_with_transform': + (FlutterView view) => PlatformViewLargeClipRRectWithTransformScenario(view, id: _viewId++), + 'platform_view_large_cliprrect_with_transform_multiple_clips': + (FlutterView view) => + PlatformViewLargeClipRRectWithTransformMultipleClipsScenario(view, id: _viewId++), 'platform_view_clippath': (FlutterView view) => PlatformViewClipPathScenario(view, id: _viewId++), - 'platform_view_clippath_multiple_clips': (FlutterView view) => PlatformViewClipPathMultipleClipsScenario(view, id: _viewId++), - 'platform_view_clippath_with_transform': (FlutterView view) => PlatformViewClipPathWithTransformScenario(view, id: _viewId++), - 'platform_view_clippath_with_transform_multiple_clips': (FlutterView view) => PlatformViewClipPathWithTransformMultipleClipsScenario(view, id: _viewId++), - 'platform_view_transform': (FlutterView view) => PlatformViewTransformScenario(view, id: _viewId++), + 'platform_view_clippath_multiple_clips': + (FlutterView view) => PlatformViewClipPathMultipleClipsScenario(view, id: _viewId++), + 'platform_view_clippath_with_transform': + (FlutterView view) => PlatformViewClipPathWithTransformScenario(view, id: _viewId++), + 'platform_view_clippath_with_transform_multiple_clips': + (FlutterView view) => + PlatformViewClipPathWithTransformMultipleClipsScenario(view, id: _viewId++), + 'platform_view_transform': + (FlutterView view) => PlatformViewTransformScenario(view, id: _viewId++), 'platform_view_opacity': (FlutterView view) => PlatformViewOpacityScenario(view, id: _viewId++), - 'platform_view_with_other_backdrop_filter': (FlutterView view) => PlatformViewWithOtherBackDropFilter(view, id: _viewId++), - 'two_platform_views_with_other_backdrop_filter': (FlutterView view) => TwoPlatformViewsWithOtherBackDropFilter(view, firstId: _viewId++, secondId: _viewId++), - 'platform_view_with_negative_backdrop_filter': (FlutterView view) => PlatformViewWithNegativeBackDropFilter(view, id: _viewId++), - 'platform_view_multiple': (FlutterView view) => MultiPlatformViewScenario(view, firstId: _viewId++, secondId: _viewId++), - 'platform_view_multiple_background_foreground': (FlutterView view) => MultiPlatformViewBackgroundForegroundScenario(view, firstId: _viewId++, secondId: _viewId++), - 'non_full_screen_flutter_view_platform_view': (FlutterView view) => NonFullScreenFlutterViewPlatformViewScenario(view, id: _viewId++), + 'platform_view_with_other_backdrop_filter': + (FlutterView view) => PlatformViewWithOtherBackDropFilter(view, id: _viewId++), + 'two_platform_views_with_other_backdrop_filter': + (FlutterView view) => + TwoPlatformViewsWithOtherBackDropFilter(view, firstId: _viewId++, secondId: _viewId++), + 'platform_view_with_negative_backdrop_filter': + (FlutterView view) => PlatformViewWithNegativeBackDropFilter(view, id: _viewId++), + 'platform_view_multiple': + (FlutterView view) => + MultiPlatformViewScenario(view, firstId: _viewId++, secondId: _viewId++), + 'platform_view_multiple_background_foreground': + (FlutterView view) => MultiPlatformViewBackgroundForegroundScenario( + view, + firstId: _viewId++, + secondId: _viewId++, + ), + 'non_full_screen_flutter_view_platform_view': + (FlutterView view) => NonFullScreenFlutterViewPlatformViewScenario(view, id: _viewId++), 'poppable_screen': (FlutterView view) => PoppableScreenScenario(view), 'platform_view_rotate': (FlutterView view) => PlatformViewScenario(view, id: _viewId++), - 'platform_view_gesture_reject_eager': (FlutterView view) => PlatformViewForTouchIOSScenario(view, id: _viewId++, accept: false), - 'platform_view_gesture_accept': (FlutterView view) => PlatformViewForTouchIOSScenario(view, id: _viewId++, accept: true), - 'platform_view_gesture_reject_after_touches_ended': (FlutterView view) => PlatformViewForTouchIOSScenario(view, id: _viewId++, accept: false, rejectUntilTouchesEnded: true), - 'platform_view_gesture_accept_with_overlapping_platform_views': (FlutterView view) => PlatformViewForOverlappingPlatformViewsScenario(view, foregroundId: _viewId++, backgroundId: _viewId++), - 'platform_view_scrolling_under_widget':(FlutterView view) => PlatformViewScrollingUnderWidget(view, firstPlatformViewId: _viewId++, lastPlatformViewId: _viewId+=16), - 'platform_views_with_clips_scrolling':(FlutterView view) => PlatformViewsWithClipsScrolling(view, firstPlatformViewId: _viewId++, lastPlatformViewId: _viewId+=16), - 'platform_views_with_clips_scrolling_multiple_clips':(FlutterView view) => PlatformViewsWithClipsScrollingMultipleClips(view, firstPlatformViewId: _viewId++, lastPlatformViewId: _viewId+=16), - 'two_platform_view_clip_rect': (FlutterView view) => TwoPlatformViewClipRect(view, firstId: _viewId++, secondId: _viewId++), - 'two_platform_view_clip_rect_multiple_clips': (FlutterView view) => TwoPlatformViewClipRectMultipleClips(view, firstId: _viewId++, secondId: _viewId++), - 'two_platform_view_clip_rrect': (FlutterView view) => TwoPlatformViewClipRRect(view, firstId: _viewId++, secondId: _viewId++), - 'two_platform_view_clip_rrect_multiple_clips': (FlutterView view) => TwoPlatformViewClipRRectMultipleClips(view, firstId: _viewId++, secondId: _viewId++), - 'two_platform_view_clip_path': (FlutterView view) => TwoPlatformViewClipPath(view, firstId: _viewId++, secondId: _viewId++), - 'two_platform_view_clip_path_multiple_clips': (FlutterView view) => TwoPlatformViewClipPathMultipleClips(view, firstId: _viewId++, secondId: _viewId++), + 'platform_view_gesture_reject_eager': + (FlutterView view) => PlatformViewForTouchIOSScenario(view, id: _viewId++, accept: false), + 'platform_view_gesture_accept': + (FlutterView view) => PlatformViewForTouchIOSScenario(view, id: _viewId++, accept: true), + 'platform_view_gesture_reject_after_touches_ended': + (FlutterView view) => PlatformViewForTouchIOSScenario( + view, + id: _viewId++, + accept: false, + rejectUntilTouchesEnded: true, + ), + 'platform_view_gesture_accept_with_overlapping_platform_views': + (FlutterView view) => PlatformViewForOverlappingPlatformViewsScenario( + view, + foregroundId: _viewId++, + backgroundId: _viewId++, + ), + 'platform_view_scrolling_under_widget': + (FlutterView view) => PlatformViewScrollingUnderWidget( + view, + firstPlatformViewId: _viewId++, + lastPlatformViewId: _viewId += 16, + ), + 'platform_views_with_clips_scrolling': + (FlutterView view) => PlatformViewsWithClipsScrolling( + view, + firstPlatformViewId: _viewId++, + lastPlatformViewId: _viewId += 16, + ), + 'platform_views_with_clips_scrolling_multiple_clips': + (FlutterView view) => PlatformViewsWithClipsScrollingMultipleClips( + view, + firstPlatformViewId: _viewId++, + lastPlatformViewId: _viewId += 16, + ), + 'two_platform_view_clip_rect': + (FlutterView view) => TwoPlatformViewClipRect(view, firstId: _viewId++, secondId: _viewId++), + 'two_platform_view_clip_rect_multiple_clips': + (FlutterView view) => + TwoPlatformViewClipRectMultipleClips(view, firstId: _viewId++, secondId: _viewId++), + 'two_platform_view_clip_rrect': + (FlutterView view) => TwoPlatformViewClipRRect(view, firstId: _viewId++, secondId: _viewId++), + 'two_platform_view_clip_rrect_multiple_clips': + (FlutterView view) => + TwoPlatformViewClipRRectMultipleClips(view, firstId: _viewId++, secondId: _viewId++), + 'two_platform_view_clip_path': + (FlutterView view) => TwoPlatformViewClipPath(view, firstId: _viewId++, secondId: _viewId++), + 'two_platform_view_clip_path_multiple_clips': + (FlutterView view) => + TwoPlatformViewClipPathMultipleClips(view, firstId: _viewId++, secondId: _viewId++), 'tap_status_bar': (FlutterView view) => TouchesScenario(view), 'initial_route_reply': (FlutterView view) => InitialRouteReply(view), - 'platform_view_with_continuous_texture': (FlutterView view) => PlatformViewWithContinuousTexture(view, id: _viewId++), + 'platform_view_with_continuous_texture': + (FlutterView view) => PlatformViewWithContinuousTexture(view, id: _viewId++), 'bogus_font_text': (FlutterView view) => BogusFontText(view), - 'spawn_engine_works' : (FlutterView view) => BogusFontText(view), + 'spawn_engine_works': (FlutterView view) => BogusFontText(view), 'pointer_events': (FlutterView view) => TouchesScenario(view), 'display_texture': (FlutterView view) => DisplayTexture(view), 'get_bitmap': (FlutterView view) => GetBitmapScenario(view), diff --git a/testing/scenario_app/lib/src/solid_blue.dart b/testing/scenario_app/lib/src/solid_blue.dart index ac94311ebcd3c..8c8960266f644 100644 --- a/testing/scenario_app/lib/src/solid_blue.dart +++ b/testing/scenario_app/lib/src/solid_blue.dart @@ -20,11 +20,7 @@ class SolidBlueScenario extends Scenario { canvas.drawPaint(Paint()..color = const Color(0xFF0000FF)); final Picture picture = recorder.endRecording(); - builder.addPicture( - Offset.zero, - picture, - willChangeHint: true, - ); + builder.addPicture(Offset.zero, picture, willChangeHint: true); final Scene scene = builder.build(); view.render(scene); scene.dispose(); diff --git a/testing/scenario_app/lib/src/texture.dart b/testing/scenario_app/lib/src/texture.dart index c03628cb7df22..21be7b1716aff 100644 --- a/testing/scenario_app/lib/src/texture.dart +++ b/testing/scenario_app/lib/src/texture.dart @@ -14,20 +14,15 @@ class DisplayTexture extends Scenario { DisplayTexture(super.view); int get _textureId => scenarioParams['texture_id'] as int; - double get _textureWidth => - (scenarioParams['texture_width'] as num).toDouble(); - double get _textureHeight => - (scenarioParams['texture_height'] as num).toDouble(); + double get _textureWidth => (scenarioParams['texture_width'] as num).toDouble(); + double get _textureHeight => (scenarioParams['texture_height'] as num).toDouble(); @override void onBeginFrame(Duration duration) { final SceneBuilder builder = SceneBuilder(); builder.addTexture( _textureId, - offset: Offset( - (view.physicalSize.width / 2.0) - (_textureWidth / 2.0), - 0.0, - ), + offset: Offset((view.physicalSize.width / 2.0) - (_textureWidth / 2.0), 0.0), width: _textureWidth, height: _textureHeight, ); @@ -38,9 +33,7 @@ class DisplayTexture extends Scenario { sendJsonMessage( dispatcher: view.platformDispatcher, channel: 'display_data', - json: { - 'data': 'ready', - }, + json: {'data': 'ready'}, ); } } diff --git a/testing/scenario_app/lib/src/touches_scenario.dart b/testing/scenario_app/lib/src/touches_scenario.dart index 139f2cf6df8a7..9f36441eb38cf 100644 --- a/testing/scenario_app/lib/src/touches_scenario.dart +++ b/testing/scenario_app/lib/src/touches_scenario.dart @@ -26,13 +26,13 @@ class TouchesScenario extends Scenario { @override void onPointerDataPacket(PointerDataPacket packet) { for (final PointerData datum in packet.data) { - final int deviceId = - _knownDevices.putIfAbsent(datum.device, () => _knownDevices.length); + final int deviceId = _knownDevices.putIfAbsent(datum.device, () => _knownDevices.length); sendJsonMessage( dispatcher: view.platformDispatcher, channel: 'display_data', json: { - 'data': '$_sequenceNo,${datum.change},device=$deviceId,buttons=${datum.buttons},signalKind=${datum.signalKind}', + 'data': + '$_sequenceNo,${datum.change},device=$deviceId,buttons=${datum.buttons},signalKind=${datum.signalKind}', }, ); _sequenceNo++; diff --git a/testing/scenario_app/test/adb_log_filter_test.dart b/testing/scenario_app/test/adb_log_filter_test.dart index 086fa342dce10..42f0e00ac90a9 100644 --- a/testing/scenario_app/test/adb_log_filter_test.dart +++ b/testing/scenario_app/test/adb_log_filter_test.dart @@ -111,14 +111,20 @@ void main() { final FakeAdbProcess unrelated = logcat.process(); final FakeAdbProcess flutter = logcat.process(); - device.info(AdbLogLine.activityManagerTag, 'Start proc ${unrelated.processId}:com.example.unrelated'); + device.info( + AdbLogLine.activityManagerTag, + 'Start proc ${unrelated.processId}:com.example.unrelated', + ); List rawLines = logcat.drain(); expect(rawLines, hasLength(1)); AdbLogLine parsedLogLine = AdbLogLine.tryParse(rawLines.single)!; expect(parsedLogLine.tryParseProcess(), isNull); - device.info(AdbLogLine.activityManagerTag, 'Start proc ${flutter.processId}:${AdbLogLine.flutterProcessName}'); + device.info( + AdbLogLine.activityManagerTag, + 'Start proc ${flutter.processId}:${AdbLogLine.flutterProcessName}', + ); rawLines = logcat.drain(); expect(rawLines, hasLength(1)); diff --git a/testing/scenario_app/test/src/fake_adb_logcat.dart b/testing/scenario_app/test/src/fake_adb_logcat.dart index a96d7db8eb689..40ba5bebb5f6a 100644 --- a/testing/scenario_app/test/src/fake_adb_logcat.dart +++ b/testing/scenario_app/test/src/fake_adb_logcat.dart @@ -63,10 +63,7 @@ final class FakeAdbLogcat { /// simple default is used (sequential numbers starting from 1000). FakeAdbProcess process({int? processId}) { processId ??= 1000 + _processById.length; - return _processById.putIfAbsent( - processId, - () => _createProcess(processId: processId!), - ); + return _processById.putIfAbsent(processId, () => _createProcess(processId: processId!)); } FakeAdbProcess _createProcess({required int processId}) { @@ -78,10 +75,7 @@ final class FakeAdbLogcat { /// /// See [FakeAdbLogcat.process] for how to create this fixture. final class FakeAdbProcess { - const FakeAdbProcess._( - this._logcat, { - required this.processId, - }); + const FakeAdbProcess._(this._logcat, {required this.processId}); final FakeAdbLogcat _logcat; diff --git a/testing/scenario_app/tool/logcat_reader.dart b/testing/scenario_app/tool/logcat_reader.dart index 909204a1af342..f00f2b6f2e047 100644 --- a/testing/scenario_app/tool/logcat_reader.dart +++ b/testing/scenario_app/tool/logcat_reader.dart @@ -15,13 +15,14 @@ import '../bin/utils/adb_logcat_filtering.dart'; /// `adb logcat` and explain what log tag names are most common. void main(List args) { if (args case [final String path]) { - final List parsed = io.File(path) - .readAsLinesSync() - .map(AdbLogLine.tryParse) - .whereType() - // Filter out all debug logs. - .where((AdbLogLine line) => line.severity != 'D') - .toList(); + final List parsed = + io.File(path) + .readAsLinesSync() + .map(AdbLogLine.tryParse) + .whereType() + // Filter out all debug logs. + .where((AdbLogLine line) => line.severity != 'D') + .toList(); final Map tagCounts = {}; for (final AdbLogLine line in parsed) { @@ -29,8 +30,9 @@ void main(List args) { } // Print in order of most common to least common. - final List> sorted = tagCounts.entries.toList() - ..sort((MapEntry a, MapEntry b) => b.value.compareTo(a.value)); + final List> sorted = + tagCounts.entries.toList() + ..sort((MapEntry a, MapEntry b) => b.value.compareTo(a.value)); for (final MapEntry entry in sorted) { print("'${entry.key}', // ${entry.value}"); } diff --git a/testing/skia_gold_client/lib/skia_gold_client.dart b/testing/skia_gold_client/lib/skia_gold_client.dart index 5d64eb76423e1..75827a9db8eee 100644 --- a/testing/skia_gold_client/lib/skia_gold_client.dart +++ b/testing/skia_gold_client/lib/skia_gold_client.dart @@ -50,11 +50,7 @@ interface class SkiaGoldClient { Map? dimensions, bool verbose = false, }) { - return SkiaGoldClient.forTesting( - workDirectory, - dimensions: dimensions, - verbose: verbose, - ); + return SkiaGoldClient.forTesting(workDirectory, dimensions: dimensions, verbose: verbose); } /// Creates a [SkiaGoldClient] for testing. @@ -88,10 +84,9 @@ interface class SkiaGoldClient { _environment = environment ?? io.Platform.environment, _engineRoot = engineRoot ?? Engine.findWithin() { // Lookup the release version from the engine repository. - final io.File releaseVersionFile = io.File(path.join( - _engineRoot.flutterDir.path, - '.engine-release.version', - )); + final io.File releaseVersionFile = io.File( + path.join(_engineRoot.flutterDir.path, '.engine-release.version'), + ); // If the file is not found or cannot be read, we are in an invalid state. try { @@ -108,32 +103,26 @@ interface class SkiaGoldClient { ReleaseVersion? _releaseVersion; /// Whether the client is available and can be used in this environment. - static bool isAvailable({ - Map? environment, - }) { + static bool isAvailable({Map? environment}) { final String? result = (environment ?? io.Platform.environment)[_kGoldctlKey]; return result != null && result.isNotEmpty; } /// Returns true if the current environment is a LUCI builder. - static bool isLuciEnv({ - Map? environment, - }) { + static bool isLuciEnv({Map? environment}) { return (environment ?? io.Platform.environment).containsKey(_kLuciEnvName); } /// Whether the current environment is a presubmit job. bool get _isPresubmit { - return - isLuciEnv(environment: _environment) && + return isLuciEnv(environment: _environment) && isAvailable(environment: _environment) && _environment.containsKey(_kPresubmitEnvName); } /// Whether the current environment is a postsubmit job. bool get _isPostsubmit { - return - isLuciEnv(environment: _environment) && + return isLuciEnv(environment: _environment) && isAvailable(environment: _environment) && !_environment.containsKey(_kPresubmitEnvName); } @@ -208,18 +197,22 @@ interface class SkiaGoldClient { _goldctl, 'auth', if (verbose) '--verbose', - '--work-dir', _tempPath, + '--work-dir', + _tempPath, '--luci', ]; final io.ProcessResult result = await _runCommand(authCommand); if (result.exitCode != 0) { - final StringBuffer buf = StringBuffer() - ..writeln('Skia Gold authorization failed.') - ..writeln('Luci environments authenticate using the file provided ' - 'by LUCI_CONTEXT. There may be an error with this file or Gold ' - 'authentication.'); + final StringBuffer buf = + StringBuffer() + ..writeln('Skia Gold authorization failed.') + ..writeln( + 'Luci environments authenticate using the file provided ' + 'by LUCI_CONTEXT. There may be an error with this file or Gold ' + 'authentication.', + ); throw SkiaGoldProcessError( command: authCommand, stdout: result.stdout.toString(), @@ -250,23 +243,30 @@ interface class SkiaGoldClient { final List imgtestInitCommand = [ _goldctl, - 'imgtest', 'init', + 'imgtest', + 'init', if (verbose) '--verbose', - '--instance', _instance, - '--work-dir', _tempPath, - '--commit', commitHash, - '--keys-file', keys.path, - '--failure-file', failures.path, + '--instance', + _instance, + '--work-dir', + _tempPath, + '--commit', + commitHash, + '--keys-file', + keys.path, + '--failure-file', + failures.path, '--passfail', ]; final io.ProcessResult result = await _runCommand(imgtestInitCommand); if (result.exitCode != 0) { - final StringBuffer buf = StringBuffer() - ..writeln('Skia Gold imgtest init failed.') - ..writeln('An error occurred when initializing golden file test with ') - ..writeln('goldctl.'); + final StringBuffer buf = + StringBuffer() + ..writeln('Skia Gold imgtest init failed.') + ..writeln('An error occurred when initializing golden file test with ') + ..writeln('goldctl.'); throw SkiaGoldProcessError( command: imgtestInitCommand, stdout: result.stdout.toString(), @@ -277,7 +277,6 @@ interface class SkiaGoldClient { _stderr.writeln('stdout:\n${result.stdout}'); _stderr.writeln('stderr:\n${result.stderr}'); } - } /// Executes the `imgtest add` command in the `goldctl` tool. @@ -362,8 +361,7 @@ interface class SkiaGoldClient { _goldctl, 'imgtest', 'add', - if (verbose) - '--verbose', + if (verbose) '--verbose', '--work-dir', _tempPath, '--test-name', @@ -372,21 +370,29 @@ interface class SkiaGoldClient { goldenFile.path, // Otherwise post submit will not fail. '--passfail', - ..._getMatchingArguments(testName, screenshotSize, pixelDeltaThreshold, maxDifferentPixelsRate), + ..._getMatchingArguments( + testName, + screenshotSize, + pixelDeltaThreshold, + maxDifferentPixelsRate, + ), ]; final io.ProcessResult result = await _runCommand(imgtestCommand); if (result.exitCode != 0) { - final StringBuffer buf = StringBuffer() - ..writeln('Skia Gold received an unapproved image in post-submit ') - ..writeln('testing. Golden file images in flutter/engine are triaged ') - ..writeln('in pre-submit during code review for the given PR.') - ..writeln() - ..writeln('Visit https://flutter-engine-gold.skia.org/ to view and approve ') - ..writeln('the image(s), or revert the associated change. For more ') - ..writeln('information, visit the wiki: ') - ..writeln('https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package:flutter'); + final StringBuffer buf = + StringBuffer() + ..writeln('Skia Gold received an unapproved image in post-submit ') + ..writeln('testing. Golden file images in flutter/engine are triaged ') + ..writeln('in pre-submit during code review for the given PR.') + ..writeln() + ..writeln('Visit https://flutter-engine-gold.skia.org/ to view and approve ') + ..writeln('the image(s), or revert the associated change. For more ') + ..writeln('information, visit the wiki: ') + ..writeln( + 'https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package:flutter', + ); throw SkiaGoldProcessError( command: imgtestCommand, stdout: result.stdout.toString(), @@ -413,26 +419,35 @@ interface class SkiaGoldClient { final List tryjobInitCommand = [ _goldctl, - 'imgtest', 'init', + 'imgtest', + 'init', if (verbose) '--verbose', - '--instance', _instance, - '--work-dir', _tempPath, - '--commit', commitHash, - '--keys-file', keys.path, - '--failure-file', failures.path, + '--instance', + _instance, + '--work-dir', + _tempPath, + '--commit', + commitHash, + '--keys-file', + keys.path, + '--failure-file', + failures.path, '--passfail', - '--crs', 'github', - '--patchset_id', commitHash, + '--crs', + 'github', + '--patchset_id', + commitHash, ..._getCIArguments(), ]; final io.ProcessResult result = await _runCommand(tryjobInitCommand); if (result.exitCode != 0) { - final StringBuffer buf = StringBuffer() - ..writeln('Skia Gold tryjobInit failure.') - ..writeln('An error occurred when initializing golden file tryjob with ') - ..writeln('goldctl.'); + final StringBuffer buf = + StringBuffer() + ..writeln('Skia Gold tryjobInit failure.') + ..writeln('An error occurred when initializing golden file tryjob with ') + ..writeln('goldctl.'); throw SkiaGoldProcessError( command: tryjobInitCommand, stdout: result.stdout.toString(), @@ -490,10 +505,11 @@ interface class SkiaGoldClient { final bool isUntriaged = resultStdout.contains('Untriaged'); final bool isNegative = resultStdout.contains('negative image'); if (!isUntriaged && !isNegative) { - final StringBuffer buf = StringBuffer() - ..writeln('Unexpected Gold tryjobAdd failure.') - ..writeln('Tryjob execution for golden file test $testName failed for') - ..writeln('a reason unrelated to pixel comparison.'); + final StringBuffer buf = + StringBuffer() + ..writeln('Unexpected Gold tryjobAdd failure.') + ..writeln('Tryjob execution for golden file test $testName failed for') + ..writeln('a reason unrelated to pixel comparison.'); throw SkiaGoldProcessError( command: tryjobCommand, stdout: resultStdout, @@ -531,9 +547,12 @@ interface class SkiaGoldClient { // `pixelDeltaThreshold` below. final int maxDifferentPixels = (screenshotSize * differentPixelsRate).toInt(); return [ - '--add-test-optional-key', 'image_matching_algorithm:$algorithm', - '--add-test-optional-key', 'fuzzy_max_different_pixels:$maxDifferentPixels', - '--add-test-optional-key', 'fuzzy_pixel_delta_threshold:$pixelDeltaThreshold', + '--add-test-optional-key', + 'image_matching_algorithm:$algorithm', + '--add-test-optional-key', + 'fuzzy_max_different_pixels:$maxDifferentPixels', + '--add-test-optional-key', + 'fuzzy_pixel_delta_threshold:$pixelDeltaThreshold', ]; } @@ -543,7 +562,7 @@ interface class SkiaGoldClient { late String? expectation; final String traceID = getTraceID(testName); final Uri requestForExpectations = Uri.parse( - '$_skiaGoldHost/json/v2/latestpositivedigest/$traceID' + '$_skiaGoldHost/json/v2/latestpositivedigest/$traceID', ); late String rawResponse; try { @@ -560,7 +579,7 @@ interface class SkiaGoldClient { 'Formatting error detected requesting expectations from Flutter Gold.\n' 'error: $error\n' 'url: $requestForExpectations\n' - 'response: $rawResponse' + 'response: $rawResponse', ); rethrow; } @@ -570,10 +589,11 @@ interface class SkiaGoldClient { /// Returns the current commit hash of the engine repository. Future _getCurrentCommit() async { final String engineCheckout = _engineRoot.flutterDir.path; - final io.ProcessResult revParse = await process.run( - ['git', 'rev-parse', 'HEAD'], - workingDirectory: engineCheckout, - ); + final io.ProcessResult revParse = await process.run([ + 'git', + 'rev-parse', + 'HEAD', + ], workingDirectory: engineCheckout); if (revParse.exitCode != 0) { throw StateError('Current commit of the engine can not be found from path $engineCheckout.'); } @@ -608,11 +628,7 @@ interface class SkiaGoldClient { final List refs = _environment['GOLD_TRYJOB']!.split('/'); final String pullRequest = refs[refs.length - 2]; - return [ - '--changelist', pullRequest, - '--cis', 'buildbucket', - '--jobid', jobId, - ]; + return ['--changelist', pullRequest, '--cis', 'buildbucket', '--jobid', jobId]; } /// Returns a trace id based on the current testing environment to lookup diff --git a/testing/skia_gold_client/lib/src/release_version.dart b/testing/skia_gold_client/lib/src/release_version.dart index a62953e349c60..32cfdf0a7dd95 100644 --- a/testing/skia_gold_client/lib/src/release_version.dart +++ b/testing/skia_gold_client/lib/src/release_version.dart @@ -27,10 +27,7 @@ final class ReleaseVersion { /// ``` /// /// Each number must be non-negative. - ReleaseVersion({ - required this.major, - required this.minor, - }) { + ReleaseVersion({required this.major, required this.minor}) { RangeError.checkNotNegative(major, 'major'); RangeError.checkNotNegative(minor, 'minor'); } @@ -70,10 +67,7 @@ final class ReleaseVersion { } final List parts = trimmed.split('.'); if (parts.length != 2) { - throw FormatException( - 'Expected "major.minor" format, got "$trimmed"', - fileContents, - ); + throw FormatException('Expected "major.minor" format, got "$trimmed"', fileContents); } final int? major = int.tryParse(parts[0]); final int? minor = int.tryParse(parts[1]); @@ -84,21 +78,12 @@ final class ReleaseVersion { ); } if (parsed != null) { - throw FormatException( - 'Multiple "major.minor" versions found', - fileContents, - ); + throw FormatException('Multiple "major.minor" versions found', fileContents); } - parsed = ReleaseVersion( - major: major, - minor: minor, - ); + parsed = ReleaseVersion(major: major, minor: minor); } if (parsed != null && parsedNone) { - throw FormatException( - 'Both "none" and a "major.minor" version found', - fileContents, - ); + throw FormatException('Both "none" and a "major.minor" version found', fileContents); } if (parsed != null) { return parsed; @@ -117,9 +102,7 @@ final class ReleaseVersion { @override bool operator ==(Object other) { - return other is ReleaseVersion && - other.major == major && - other.minor == minor; + return other is ReleaseVersion && other.major == major && other.minor == minor; } @override diff --git a/testing/skia_gold_client/test/release_version_test.dart b/testing/skia_gold_client/test/release_version_test.dart index 36fc156abe2fb..968d742f2ea73 100644 --- a/testing/skia_gold_client/test/release_version_test.dart +++ b/testing/skia_gold_client/test/release_version_test.dart @@ -32,12 +32,8 @@ void main() { }); test('should ignore comments and empty lines', () { - final ReleaseVersion version = ReleaseVersion.parse([ - '# This is a comment', - '', - '3.21', - '', - ].join('\n'))!; + final ReleaseVersion version = + ReleaseVersion.parse(['# This is a comment', '', '3.21', ''].join('\n'))!; expect(version.major, equals(3)); expect(version.minor, equals(21)); }); diff --git a/testing/skia_gold_client/test/skia_gold_client_test.dart b/testing/skia_gold_client/test/skia_gold_client_test.dart index 5365369326ba1..041b8eaf8cf9c 100644 --- a/testing/skia_gold_client/test/skia_gold_client_test.dart +++ b/testing/skia_gold_client/test/skia_gold_client_test.dart @@ -30,7 +30,7 @@ void main() { const Map postsubmitEnv = { 'GOLDCTL': 'python tools/goldctl.py', 'LOGDOG_STREAM_PREFIX': 'buildbucket/cr-buildbucket.appspot.com/1234567890/+/logdog', - 'LUCI_CONTEXT': '{}' + 'LUCI_CONTEXT': '{}', }; /// Simulating what a local environment would look like. @@ -56,9 +56,7 @@ void main() { dimensions: dimensions, engineRoot: Engine.fromSrcPath(fixture.engineSrcDir.path), httpClient: fixture.httpClient, - processManager: FakeProcessManager( - onRun: onRun, - ), + processManager: FakeProcessManager(onRun: onRun), verbose: verbose, stderr: fixture.outputSink, environment: environment, @@ -77,10 +75,7 @@ void main() { test('fails if GOLDCTL is not set', () async { final _TestFixture fixture = _TestFixture(); try { - final SkiaGoldClient client = createClient( - fixture, - environment: localEnv, - ); + final SkiaGoldClient client = createClient(fixture, environment: localEnv); try { await client.auth(); fail('auth should fail if GOLDCTL is not set'); @@ -245,10 +240,7 @@ void main() { // Adds a suffix of "_Release_3_21" to the test name. final _TestFixture fixture = _TestFixture( // Creates a file called "engine/src/fluter/.engine-release.version" with the contents "3.21". - engineVersion: ReleaseVersion( - major: 3, - minor: 21, - ), + engineVersion: ReleaseVersion(major: 3, minor: 21), ); try { final SkiaGoldClient client = createClient( @@ -578,9 +570,7 @@ void main() { final String hash = client.getTraceID('test-name'); fixture.httpClient.setJsonResponse( Uri.parse('https://flutter-engine-gold.skia.org/json/v2/latestpositivedigest/$hash'), - { - 'digest': 'digest', - }, + {'digest': 'digest'}, ); final String? digest = await client.getExpectationForTest('test-name'); @@ -592,9 +582,7 @@ void main() { } final class _TestFixture { - _TestFixture({ - ReleaseVersion? engineVersion, - }) { + _TestFixture({ReleaseVersion? engineVersion}) { workDirectory = rootDirectory.createTempSync('working'); // Create the engine/src directory. @@ -609,7 +597,9 @@ final class _TestFixture { io.File(p.join(flutterDir.path, '.engine-release.version')).writeAsStringSync(version); } - final io.Directory rootDirectory = io.Directory.systemTemp.createTempSync('skia_gold_client_test'); + final io.Directory rootDirectory = io.Directory.systemTemp.createTempSync( + 'skia_gold_client_test', + ); late final io.Directory workDirectory; late final io.Directory engineSrcDir; @@ -676,8 +666,7 @@ final class _FakeHttpClientRequest implements io.HttpClientRequest { } } -final class _FakeHttpClientResponse extends Stream> - implements io.HttpClientResponse { +final class _FakeHttpClientResponse extends Stream> implements io.HttpClientResponse { _FakeHttpClientResponse(this._bytes); final Uint8List _bytes; @@ -689,12 +678,9 @@ final class _FakeHttpClientResponse extends Stream> void Function()? onDone, bool? cancelOnError, }) { - return Stream>.fromIterable(>[_bytes]).listen( - onData, - onError: onError, - onDone: onDone, - cancelOnError: cancelOnError, - ); + return Stream>.fromIterable(>[ + _bytes, + ]).listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError); } @override diff --git a/testing/skia_gold_client/tool/e2e_test.dart b/testing/skia_gold_client/tool/e2e_test.dart index 79e23e65ba806..b3d162f859257 100644 --- a/testing/skia_gold_client/tool/e2e_test.dart +++ b/testing/skia_gold_client/tool/e2e_test.dart @@ -48,9 +48,7 @@ void main() async { } // Create a client. - final SkiaGoldClient skiaGoldClient = SkiaGoldClient( - engine.flutterDir, - ); + final SkiaGoldClient skiaGoldClient = SkiaGoldClient(engine.flutterDir); // Authenticate the client. await skiaGoldClient.auth(); @@ -79,13 +77,9 @@ void main() async { for (final _Digest digest in digests) { final String digestPath = digest.source; final String digestName = digest.name; - final File digestFile = File(path.join( - engine.flutterDir.path, - 'testing', - 'skia_gold_client', - 'tool', - digestPath, - )); + final File digestFile = File( + path.join(engine.flutterDir.path, 'testing', 'skia_gold_client', 'tool', digestPath), + ); if (!digestFile.existsSync()) { stderr.writeln('The digest file "$digestPath" does not exist.'); exitCode = 1; @@ -95,11 +89,7 @@ void main() async { print('Uploading digest: $digestName ($digestPath): ${digest.pixelCount} pixels...'); try { - await skiaGoldClient.addImg( - digestName, - digestFile, - screenshotSize: 0, - ); + await skiaGoldClient.addImg(digestName, digestFile, screenshotSize: 0); stderr.writeln('Comparison success: $digestName'); } on Exception catch (e) { stderr.writeln('Comparison failure: $digestName: $e'); @@ -118,11 +108,7 @@ void main() async { } final class _Digest { - const _Digest({ - required this.name, - required this.source, - required this.pixelCount, - }); + const _Digest({required this.name, required this.source, required this.pixelCount}); /// The name of the digest/test. final String name; diff --git a/testing/skia_gold_client/tool/generate.dart b/testing/skia_gold_client/tool/generate.dart index a9753ae178b77..3c08378eb18da 100644 --- a/testing/skia_gold_client/tool/generate.dart +++ b/testing/skia_gold_client/tool/generate.dart @@ -15,55 +15,54 @@ import 'package:path/path.dart' as path; void main(List args) async { final Engine? engine = Engine.tryFindWithin(); - final ArgParser parser = ArgParser() - ..addFlag( - 'help', - abbr: 'h', - help: 'Prints usage information.', - negatable: false, - ) - ..addOption( - 'image-magick-convert-bin', - help: 'The path to the ImageMagick `convert` executable.', - defaultsTo: 'convert', - hide: true, - ) - ..addOption( - 'annotation', - abbr: 'a', - help: 'The text to write on the images.', - defaultsTo: engine == null - ? null - : await GitRepo.fromRoot(engine.flutterDir).headSha(short: true), - ) - ..addOption( - 'source', - abbr: 's', - help: 'The directory containing the images to be modified.', - defaultsTo: engine == null - ? null - : path.join( - engine.flutterDir.path, - 'testing', - 'skia_gold_client', - 'tool', - 'source_images', - ), - ) - ..addOption( - 'output', - abbr: 'o', - help: 'The directory to save the modified images in.', - defaultsTo: engine == null - ? null - : path.join( - engine.flutterDir.path, - 'testing', - 'skia_gold_client', - 'tool', - 'e2e_fixtures', - ), - ); + final ArgParser parser = + ArgParser() + ..addFlag('help', abbr: 'h', help: 'Prints usage information.', negatable: false) + ..addOption( + 'image-magick-convert-bin', + help: 'The path to the ImageMagick `convert` executable.', + defaultsTo: 'convert', + hide: true, + ) + ..addOption( + 'annotation', + abbr: 'a', + help: 'The text to write on the images.', + defaultsTo: + engine == null + ? null + : await GitRepo.fromRoot(engine.flutterDir).headSha(short: true), + ) + ..addOption( + 'source', + abbr: 's', + help: 'The directory containing the images to be modified.', + defaultsTo: + engine == null + ? null + : path.join( + engine.flutterDir.path, + 'testing', + 'skia_gold_client', + 'tool', + 'source_images', + ), + ) + ..addOption( + 'output', + abbr: 'o', + help: 'The directory to save the modified images in.', + defaultsTo: + engine == null + ? null + : path.join( + engine.flutterDir.path, + 'testing', + 'skia_gold_client', + 'tool', + 'e2e_fixtures', + ), + ); final ArgResults results = parser.parse(args); if (results['help'] as bool) { @@ -83,11 +82,8 @@ void main(List args) async { '${path.relative(output, from: relativeDir)}.', ); - final List sourceImages = Directory(source) - .listSync() - .whereType() - .map((File file) => file.path) - .toList(); + final List sourceImages = + Directory(source).listSync().whereType().map((File file) => file.path).toList(); // For each source image, write the annotation and save it in the output directory. for (final String sourceImage in sourceImages) { @@ -96,24 +92,21 @@ void main(List args) async { '${path.basenameWithoutExtension(sourceImage)}.png', ); print('Writing to ${path.relative(outputImage, from: relativeDir)}'); - await Process.run( - imageMagickConvertBin, - [ - sourceImage, - '-fill', - 'white', - '-undercolor', - 'black', - '-gravity', - 'SouthEast', - '-pointsize', - '24', - '-annotate', - '+10+10', - annotation, - outputImage, - ], - ); + await Process.run(imageMagickConvertBin, [ + sourceImage, + '-fill', + 'white', + '-undercolor', + 'black', + '-gravity', + 'SouthEast', + '-pointsize', + '24', + '-annotate', + '+10+10', + annotation, + outputImage, + ]); } print('Done: wrote ${sourceImages.length} image.'); diff --git a/testing/symbols/verify_exported.dart b/testing/symbols/verify_exported.dart index 7f4e5e844d179..7f8e457c97ea3 100644 --- a/testing/symbols/verify_exported.dart +++ b/testing/symbols/verify_exported.dart @@ -39,9 +39,8 @@ void main(List arguments) { final String engineCheckoutPath = Platform.environment['ENGINE_CHECKOUT_PATH']!; outPath = p.join(engineCheckoutPath, outPath); } - final String buildToolsPath = arguments.length == 1 - ? p.join(p.dirname(outPath), 'flutter', 'buildtools') - : arguments[1]; + final String buildToolsPath = + arguments.length == 1 ? p.join(p.dirname(outPath), 'flutter', 'buildtools') : arguments[1]; String platform; if (Platform.isLinux) { @@ -57,17 +56,19 @@ void main(List arguments) { exit(1); } - final Iterable releaseBuilds = Directory(outPath).listSync() + final Iterable releaseBuilds = Directory(outPath) + .listSync() .whereType() .map((FileSystemEntity dir) => p.basename(dir.path)) .where((String s) => s.contains('_release')); - final Iterable iosReleaseBuilds = releaseBuilds - .where((String s) => s.startsWith('ios_')); - final Iterable androidReleaseBuilds = releaseBuilds - .where((String s) => s.startsWith('android_')); - final Iterable hostReleaseBuilds = releaseBuilds - .where((String s) => s.startsWith('host_')); + final Iterable iosReleaseBuilds = releaseBuilds.where((String s) => s.startsWith('ios_')); + final Iterable androidReleaseBuilds = releaseBuilds.where( + (String s) => s.startsWith('android_'), + ); + final Iterable hostReleaseBuilds = releaseBuilds.where( + (String s) => s.startsWith('host_'), + ); int failures = 0; failures += _checkIos(outPath, nmPath, iosReleaseBuilds); @@ -93,19 +94,27 @@ int _checkIos(String outPath, String nmPath, Iterable builds) { failures++; continue; } - final Iterable unexpectedEntries = NmEntry.parse(nmResult.stdout as String).where((NmEntry entry) { - final bool cSymbol = (entry.type == '(__DATA,__common)' || entry.type == '(__DATA,__const)') - && entry.name.startsWith('_Flutter'); - final bool cInternalSymbol = entry.type == '(__TEXT,__text)' && entry.name.startsWith('_InternalFlutter'); - final bool objcSymbol = entry.type == '(__DATA,__objc_data)' - && (entry.name.startsWith(r'_OBJC_METACLASS_$_Flutter') || entry.name.startsWith(r'_OBJC_CLASS_$_Flutter')); + final Iterable unexpectedEntries = NmEntry.parse(nmResult.stdout as String).where(( + NmEntry entry, + ) { + final bool cSymbol = + (entry.type == '(__DATA,__common)' || entry.type == '(__DATA,__const)') && + entry.name.startsWith('_Flutter'); + final bool cInternalSymbol = + entry.type == '(__TEXT,__text)' && entry.name.startsWith('_InternalFlutter'); + final bool objcSymbol = + entry.type == '(__DATA,__objc_data)' && + (entry.name.startsWith(r'_OBJC_METACLASS_$_Flutter') || + entry.name.startsWith(r'_OBJC_CLASS_$_Flutter')); return !(cSymbol || cInternalSymbol || objcSymbol); }); if (unexpectedEntries.isNotEmpty) { print('ERROR: $libFlutter exports unexpected symbols:'); - print(unexpectedEntries.fold('', (String previous, NmEntry entry) { - return '${previous == '' ? '' : '$previous\n'} ${entry.type} ${entry.name}'; - })); + print( + unexpectedEntries.fold('', (String previous, NmEntry entry) { + return '${previous == '' ? '' : '$previous\n'} ${entry.type} ${entry.name}'; + }), + ); failures++; } else { print('OK: $libFlutter'); @@ -130,8 +139,7 @@ int _checkAndroid(String outPath, String nmPath, Iterable builds) { } final Iterable entries = NmEntry.parse(nmResult.stdout as String); final Map entryMap = { - for (final NmEntry entry in entries) - entry.name: entry.type, + for (final NmEntry entry in entries) entry.name: entry.type, }; final Map expectedSymbols = { 'JNI_OnLoad': 'T', @@ -180,11 +188,11 @@ int _checkLinux(String outPath, String nmPath, Iterable builds) { failures++; break; } - if (!(entry.name.startsWith('Flutter') - || entry.name.startsWith('__Flutter') - || entry.name.startsWith('kFlutter') - || entry.name.startsWith('InternalFlutter') - || entry.name.startsWith('kInternalFlutter'))) { + if (!(entry.name.startsWith('Flutter') || + entry.name.startsWith('__Flutter') || + entry.name.startsWith('kFlutter') || + entry.name.startsWith('InternalFlutter') || + entry.name.startsWith('kInternalFlutter'))) { print('ERROR: $libFlutter exports an unexpected symbol name: ($entry)'); print(' Library has $entries.'); failures++; diff --git a/tools/android_lint/bin/main.dart b/tools/android_lint/bin/main.dart index 657cdb22b85e9..6c7434043f642 100644 --- a/tools/android_lint/bin/main.dart +++ b/tools/android_lint/bin/main.dart @@ -33,16 +33,14 @@ Future main(List args) async { Future runLint(ArgParser argParser, ArgResults argResults) async { final String inArgument = argResults['in'] as String; - final Directory androidDir = Directory(path.join( - inArgument, - 'flutter', - 'shell', - 'platform', - 'android', - )); + final Directory androidDir = Directory( + path.join(inArgument, 'flutter', 'shell', 'platform', 'android'), + ); if (!androidDir.existsSync()) { - print('This command must be run from the engine/src directory, ' - 'or be passed that directory as the --in parameter.\n'); + print( + 'This command must be run from the engine/src directory, ' + 'or be passed that directory as the --in parameter.\n', + ); print(argParser.usage); return -1; } @@ -52,9 +50,11 @@ Future runLint(ArgParser argParser, ArgResults argResults) async { ); if (!androidSdkDir.existsSync()) { - print('The Android SDK for this engine is missing from the ' - 'flutter/third_party/android_tools directory. Have you run gclient ' - 'sync?\n'); + print( + 'The Android SDK for this engine is missing from the ' + 'flutter/third_party/android_tools directory. Have you run gclient ' + 'sync?\n', + ); print(argParser.usage); return -1; } @@ -112,9 +112,7 @@ Future runLint(ArgParser argParser, ArgResults argResults) async { print('Using JAVA_HOME=$javahome'); final Process lintProcess = await processManager.start( lintArgs, - environment: { - 'JAVA_HOME': javahome, - }, + environment: {'JAVA_HOME': javahome}, ); lintProcess.stdout.pipe(stdout); lintProcess.stderr.pipe(stderr); @@ -128,35 +126,27 @@ ArgParser setupOptions() { ..addOption( 'in', help: 'The path to `engine/src`.', - defaultsTo: path.relative( - path.join( - projectDir, - '..', - '..', - '..', - ), - ), - ) - ..addFlag( - 'help', - help: 'Print usage of the command.', - negatable: false, + defaultsTo: path.relative(path.join(projectDir, '..', '..', '..')), ) + ..addFlag('help', help: 'Print usage of the command.', negatable: false) ..addFlag( 'rebaseline', - help: 'Recalculates the baseline for errors and warnings ' + help: + 'Recalculates the baseline for errors and warnings ' 'in this project.', negatable: false, ) ..addFlag( 'html', - help: 'Creates an HTML output for this report instead of printing ' + help: + 'Creates an HTML output for this report instead of printing ' 'command line output.', negatable: false, ) ..addOption( 'out', - help: 'The path to write the generated HTML report. Ignored if ' + help: + 'The path to write the generated HTML report. Ignored if ' '--html is not also true.', defaultsTo: path.join(projectDir, 'lint_report'), ); @@ -172,11 +162,7 @@ String getJavaHome(String src) { } /// The root directory of this project. -String get projectDir => path.dirname( - path.dirname( - path.fromUri(Platform.script), - ), - ); +String get projectDir => path.dirname(path.dirname(path.fromUri(Platform.script))); /// The path to use for project.xml, which tells the linter where to find source /// files. diff --git a/tools/api_check/lib/apicheck.dart b/tools/api_check/lib/apicheck.dart index 6e6ecb93be692..7f0698461d3c9 100644 --- a/tools/api_check/lib/apicheck.dart +++ b/tools/api_check/lib/apicheck.dart @@ -17,12 +17,11 @@ import 'package:analyzer/dart/ast/ast.dart'; /// /// Field names are expected to be of the form `kFooBarIndex`; prefixed with a /// `k` and terminated in `Index`. -List getDartClassFields({ - required String sourcePath, - required String className, -}) { +List getDartClassFields({required String sourcePath, required String className}) { final List includedPaths = [sourcePath]; - final AnalysisContextCollection collection = AnalysisContextCollection(includedPaths: includedPaths); + final AnalysisContextCollection collection = AnalysisContextCollection( + includedPaths: includedPaths, + ); final AnalysisContext context = collection.contextFor(sourcePath); final AnalysisSession session = context.currentSession; @@ -56,10 +55,7 @@ List getDartClassFields({ /// /// Enum values are expected to be of the form `kEnumNameFooBar`; prefixed with /// `kEnumName`. -List getCppEnumValues({ - required String sourcePath, - required String enumName, -}) { +List getCppEnumValues({required String sourcePath, required String enumName}) { final List lines = File(sourcePath).readAsLinesSync(); final int enumEnd = lines.indexOf('} $enumName;'); if (enumEnd < 0) { @@ -70,19 +66,13 @@ List getCppEnumValues({ return []; } final RegExp valueExp = RegExp('^\\s*k$enumName(\\w*)'); - return _extractMatchingExpression( - lines: lines.sublist(enumStart + 1, enumEnd), - regexp: valueExp, - ); + return _extractMatchingExpression(lines: lines.sublist(enumStart + 1, enumEnd), regexp: valueExp); } /// Returns all values in [enumName]. /// /// Enum values are expected to be of the form `kFooBar`; prefixed with `k`. -List getCppEnumClassValues({ - required String sourcePath, - required String enumName, -}) { +List getCppEnumClassValues({required String sourcePath, required String enumName}) { final List lines = _getBlockStartingWith( source: File(sourcePath).readAsStringSync(), startExp: RegExp('enum class $enumName .* {'), @@ -95,10 +85,7 @@ List getCppEnumClassValues({ /// /// Enum value declarations are expected to be of the form `FOO_BAR(1 << N)`; /// in all caps. -List getJavaEnumValues({ - required String sourcePath, - required String enumName, -}) { +List getJavaEnumValues({required String sourcePath, required String enumName}) { final List lines = _getBlockStartingWith( source: File(sourcePath).readAsStringSync(), startExp: RegExp('enum $enumName {'), @@ -111,10 +98,7 @@ List getJavaEnumValues({ /// /// The contents of the first match group in [regexp] is returned; therefore /// it must contain a match group. -List _extractMatchingExpression({ - required Iterable lines, - required RegExp regexp, -}) { +List _extractMatchingExpression({required Iterable lines, required RegExp regexp}) { final List values = []; for (final String line in lines) { final RegExpMatch? match = regexp.firstMatch(line); @@ -128,10 +112,7 @@ List _extractMatchingExpression({ /// Returns all lines of the block starting with [startString]. /// /// [startString] MUST end with '{'. -List _getBlockStartingWith({ - required String source, - required RegExp startExp, -}) { +List _getBlockStartingWith({required String source, required RegExp startExp}) { assert(startExp.pattern.endsWith('{')); final int blockStart = source.indexOf(startExp); @@ -163,10 +144,16 @@ List _getBlockStartingWith({ void visitUIUnits(String flutterRoot, AstVisitor visitor) { final String uiRoot = '$flutterRoot/lib/ui'; final FeatureSet analyzerFeatures = FeatureSet.latestLanguageVersion(); - final ParseStringResult uiResult = parseFile(path: '$uiRoot/ui.dart', featureSet: analyzerFeatures); + final ParseStringResult uiResult = parseFile( + path: '$uiRoot/ui.dart', + featureSet: analyzerFeatures, + ); for (final PartDirective part in uiResult.unit.directives.whereType()) { final String partPath = part.uri.stringValue!; - final ParseStringResult partResult = parseFile(path: '$uiRoot/$partPath', featureSet: analyzerFeatures); + final ParseStringResult partResult = parseFile( + path: '$uiRoot/$partPath', + featureSet: analyzerFeatures, + ); for (final CompilationUnitMember unitMember in partResult.unit.declarations) { unitMember.accept(visitor); diff --git a/tools/api_check/test/apicheck_test.dart b/tools/api_check/test/apicheck_test.dart index 8658f966040cc..c0ed59ca7f333 100644 --- a/tools/api_check/test/apicheck_test.dart +++ b/tools/api_check/test/apicheck_test.dart @@ -39,22 +39,29 @@ void checkApiConsistency(String flutterRoot) { ); // C values: kFlutterAccessibilityFeatureFooBar = 1 << N, final List embedderEnumValues = getCppEnumValues( - sourcePath: - path.join(flutterRoot, 'shell', 'platform', 'embedder', 'embedder.h'), + sourcePath: path.join(flutterRoot, 'shell', 'platform', 'embedder', 'embedder.h'), enumName: 'FlutterAccessibilityFeature', ); // C++ values: kFooBar = 1 << N, final List internalEnumValues = getCppEnumClassValues( - sourcePath: path.join( - flutterRoot, 'lib', 'ui', 'window', 'platform_configuration.h'), + sourcePath: path.join(flutterRoot, 'lib', 'ui', 'window', 'platform_configuration.h'), enumName: 'AccessibilityFeatureFlag', ); // Java values: FOO_BAR(1 << N). - final List javaEnumValues = getJavaEnumValues( - sourcePath: path.join(flutterRoot, 'shell', 'platform', 'android', 'io', - 'flutter', 'view', 'AccessibilityBridge.java'), - enumName: 'AccessibilityFeature', - ).map(allCapsToCamelCase).toList(); + final List javaEnumValues = + getJavaEnumValues( + sourcePath: path.join( + flutterRoot, + 'shell', + 'platform', + 'android', + 'io', + 'flutter', + 'view', + 'AccessibilityBridge.java', + ), + enumName: 'AccessibilityFeature', + ).map(allCapsToCamelCase).toList(); expect(embedderEnumValues, uiFields); expect(internalEnumValues, uiFields); @@ -68,28 +75,34 @@ void checkApiConsistency(String flutterRoot) { className: 'SemanticsAction', ); final List webuiFields = getDartClassFields( - sourcePath: - path.join(flutterRoot, 'lib', 'web_ui', 'lib', 'semantics.dart'), + sourcePath: path.join(flutterRoot, 'lib', 'web_ui', 'lib', 'semantics.dart'), className: 'SemanticsAction', ); // C values: kFlutterSemanticsActionFooBar = 1 << N. final List embedderEnumValues = getCppEnumValues( - sourcePath: - path.join(flutterRoot, 'shell', 'platform', 'embedder', 'embedder.h'), + sourcePath: path.join(flutterRoot, 'shell', 'platform', 'embedder', 'embedder.h'), enumName: 'FlutterSemanticsAction', ); // C++ values: kFooBar = 1 << N. final List internalEnumValues = getCppEnumClassValues( - sourcePath: - path.join(flutterRoot, 'lib', 'ui', 'semantics', 'semantics_node.h'), + sourcePath: path.join(flutterRoot, 'lib', 'ui', 'semantics', 'semantics_node.h'), enumName: 'SemanticsAction', ); // Java values: FOO_BAR(1 << N). - final List javaEnumValues = getJavaEnumValues( - sourcePath: path.join(flutterRoot, 'shell', 'platform', 'android', 'io', - 'flutter', 'view', 'AccessibilityBridge.java'), - enumName: 'Action', - ).map(allCapsToCamelCase).toList(); + final List javaEnumValues = + getJavaEnumValues( + sourcePath: path.join( + flutterRoot, + 'shell', + 'platform', + 'android', + 'io', + 'flutter', + 'view', + 'AccessibilityBridge.java', + ), + enumName: 'Action', + ).map(allCapsToCamelCase).toList(); expect(webuiFields, uiFields); expect(embedderEnumValues, uiFields); @@ -100,36 +113,35 @@ void checkApiConsistency(String flutterRoot) { test('AppLifecycleState enums match', () { // Dart values: _kFooBarIndex = 1 << N. final List uiFields = getDartClassFields( - sourcePath: - path.join(flutterRoot, 'lib', 'ui', 'platform_dispatcher.dart'), + sourcePath: path.join(flutterRoot, 'lib', 'ui', 'platform_dispatcher.dart'), className: 'AppLifecycleState', ); final List webuiFields = getDartClassFields( - sourcePath: path.join( - flutterRoot, 'lib', 'web_ui', 'lib', 'platform_dispatcher.dart'), + sourcePath: path.join(flutterRoot, 'lib', 'web_ui', 'lib', 'platform_dispatcher.dart'), className: 'AppLifecycleState', ); // C++ values: kFooBar = 1 << N. final List internalEnumValues = getCppEnumClassValues( - sourcePath: path.join( - flutterRoot, 'shell', 'platform', 'common', 'app_lifecycle_state.h'), + sourcePath: path.join(flutterRoot, 'shell', 'platform', 'common', 'app_lifecycle_state.h'), enumName: 'AppLifecycleState', ); // Java values: FOO_BAR(1 << N). - final List javaEnumValues = getJavaEnumValues( - sourcePath: path.join( - flutterRoot, - 'shell', - 'platform', - 'android', - 'io', - 'flutter', - 'embedding', - 'engine', - 'systemchannels', - 'LifecycleChannel.java'), - enumName: 'AppLifecycleState', - ).map(allCapsToCamelCase).toList(); + final List javaEnumValues = + getJavaEnumValues( + sourcePath: path.join( + flutterRoot, + 'shell', + 'platform', + 'android', + 'io', + 'flutter', + 'embedding', + 'engine', + 'systemchannels', + 'LifecycleChannel.java', + ), + enumName: 'AppLifecycleState', + ).map(allCapsToCamelCase).toList(); expect(webuiFields, uiFields); expect(internalEnumValues, uiFields); @@ -148,22 +160,29 @@ void checkApiConsistency(String flutterRoot) { ); // C values: kFlutterSemanticsFlagFooBar = 1 << N. final List embedderEnumValues = getCppEnumValues( - sourcePath: - path.join(flutterRoot, 'shell', 'platform', 'embedder', 'embedder.h'), + sourcePath: path.join(flutterRoot, 'shell', 'platform', 'embedder', 'embedder.h'), enumName: 'FlutterSemanticsFlag', ); // C++ values: kFooBar = 1 << N. final List internalEnumValues = getCppEnumClassValues( - sourcePath: - path.join(flutterRoot, 'lib', 'ui', 'semantics', 'semantics_node.h'), + sourcePath: path.join(flutterRoot, 'lib', 'ui', 'semantics', 'semantics_node.h'), enumName: 'SemanticsFlags', ); // Java values: FOO_BAR(1 << N). - final List javaEnumValues = getJavaEnumValues( - sourcePath: path.join(flutterRoot, 'shell', 'platform', 'android', 'io', - 'flutter', 'view', 'AccessibilityBridge.java'), - enumName: 'Flag', - ).map(allCapsToCamelCase).toList(); + final List javaEnumValues = + getJavaEnumValues( + sourcePath: path.join( + flutterRoot, + 'shell', + 'platform', + 'android', + 'io', + 'flutter', + 'view', + 'AccessibilityBridge.java', + ), + enumName: 'Flag', + ).map(allCapsToCamelCase).toList(); expect(webuiFields, uiFields); expect(embedderEnumValues, uiFields); @@ -198,8 +217,7 @@ class NativeFunctionVisitor extends RecursiveAstVisitor { @override void visitNativeFunctionBody(NativeFunctionBody node) { - final MethodDeclaration? method = - node.thisOrAncestorOfType(); + final MethodDeclaration? method = node.thisOrAncestorOfType(); if (method != null) { if (method.parameters != null) { check(method.toString(), method.parameters!); @@ -207,8 +225,7 @@ class NativeFunctionVisitor extends RecursiveAstVisitor { return; } - final FunctionDeclaration? func = - node.thisOrAncestorOfType(); + final FunctionDeclaration? func = node.thisOrAncestorOfType(); if (func != null) { final FunctionExpression funcExpr = func.functionExpression; if (funcExpr.parameters != null) { diff --git a/tools/build_bucket_golden_scraper/lib/build_bucket_golden_scraper.dart b/tools/build_bucket_golden_scraper/lib/build_bucket_golden_scraper.dart index a0f661f023465..491958c0593fa 100644 --- a/tools/build_bucket_golden_scraper/lib/build_bucket_golden_scraper.dart +++ b/tools/build_bucket_golden_scraper/lib/build_bucket_golden_scraper.dart @@ -20,11 +20,11 @@ final class BuildBucketGoldenScraper { this.dryRun = false, String? engineSrcPath, StringSink? outSink, - }) : - engine = engineSrcPath != null ? - Engine.fromSrcPath(engineSrcPath) : - Engine.findWithin(p.dirname(p.fromUri(io.Platform.script))), - _outSink = outSink ?? io.stdout; + }) : engine = + engineSrcPath != null + ? Engine.fromSrcPath(engineSrcPath) + : Engine.findWithin(p.dirname(p.fromUri(io.Platform.script))), + _outSink = outSink ?? io.stdout; /// Creates a scraper from the command line arguments. /// @@ -61,23 +61,19 @@ final class BuildBucketGoldenScraper { throw FormatException(output.toString(), args.join(' ')); } - static final ArgParser _argParser = ArgParser() - ..addFlag( - 'help', - abbr: 'h', - help: 'Print this help message.', - negatable: false, - ) - ..addFlag( - 'dry-run', - help: "If true, don't write any files to disk (other than temporary files).", - negatable: false, - ) - ..addOption( - 'engine-src-path', - help: 'The path to the engine source code.', - valueHelp: 'path/that/contains/src (defaults to the directory containing this script)', - ); + static final ArgParser _argParser = + ArgParser() + ..addFlag('help', abbr: 'h', help: 'Print this help message.', negatable: false) + ..addFlag( + 'dry-run', + help: "If true, don't write any files to disk (other than temporary files).", + negatable: false, + ) + ..addOption( + 'engine-src-path', + help: 'The path to the engine source code.', + valueHelp: 'path/that/contains/src (defaults to the directory containing this script)', + ); /// A local path or a URL to a buildbucket log file. final String pathOrUrl; @@ -153,7 +149,8 @@ final class BuildBucketGoldenScraper { // Write the goldens to disk (or pretend to in dry-run mode). _outSink.writeln('${dryRun ? 'Found' : 'Wrote'} ${uniqueGoldens.length} golden file changes:'); for (final _Golden golden in uniqueGoldens) { - final String truncatedPathAfterFlutterDir = golden.outFile.path.split('flutter${p.separator}').last; + final String truncatedPathAfterFlutterDir = + golden.outFile.path.split('flutter${p.separator}').last; _outSink.writeln(' $truncatedPathAfterFlutterDir'); if (!dryRun) { await golden.outFile.writeAsBytes(golden.bytes); diff --git a/tools/build_bucket_golden_scraper/test/build_bucket_golden_scraper_test.dart b/tools/build_bucket_golden_scraper/test/build_bucket_golden_scraper_test.dart index 04d4065eaf361..42100b8d3b9bb 100644 --- a/tools/build_bucket_golden_scraper/test/build_bucket_golden_scraper_test.dart +++ b/tools/build_bucket_golden_scraper/test/build_bucket_golden_scraper_test.dart @@ -11,7 +11,9 @@ import 'package:test/test.dart'; void main() { test('parses command-line arguments', () { // Create a fake engine directory. - final io.Directory buildRoot = io.Directory.systemTemp.createTempSync('build_bucket_golden_scraper_test_engine'); + final io.Directory buildRoot = io.Directory.systemTemp.createTempSync( + 'build_bucket_golden_scraper_test_engine', + ); final io.Directory srcDir = io.Directory(p.join(buildRoot.path, 'src'))..createSync(); io.Directory(p.join(srcDir.path, 'flutter')).createSync(); @@ -26,7 +28,10 @@ void main() { expect(scraper.dryRun, isTrue); expect(scraper.engine.srcDir.path, srcDir.path); - expect(scraper.pathOrUrl, 'https://ci.chromium.org/raw/buildbucket/v1/builders/flutter/flutter-linux/builder:linux_bare'); + expect( + scraper.pathOrUrl, + 'https://ci.chromium.org/raw/buildbucket/v1/builders/flutter/flutter-linux/builder:linux_bare', + ); } finally { buildRoot.deleteSync(recursive: true); } @@ -34,12 +39,15 @@ void main() { test('finds diffs', () async { // Create a fake engine directory. - final io.Directory buildRoot = io.Directory.systemTemp.createTempSync('build_bucket_golden_scraper_test_engine'); + final io.Directory buildRoot = io.Directory.systemTemp.createTempSync( + 'build_bucket_golden_scraper_test_engine', + ); final io.Directory srcDir = io.Directory(p.join(buildRoot.path, 'src'))..createSync(); io.Directory(p.join(srcDir.path, 'flutter', 'docs')).createSync(recursive: true); // Create an empty logo in docs/flutter_logo.png. - final io.File logo = io.File(p.join(srcDir.path, 'flutter', 'docs', 'flutter_logo.png'))..createSync(); + final io.File logo = io.File(p.join(srcDir.path, 'flutter', 'docs', 'flutter_logo.png')) + ..createSync(); // Create a fake log file. try { diff --git a/tools/clang_tidy/lib/clang_tidy.dart b/tools/clang_tidy/lib/clang_tidy.dart index b9caee879e629..d086a9ab83a99 100644 --- a/tools/clang_tidy/lib/clang_tidy.dart +++ b/tools/clang_tidy/lib/clang_tidy.dart @@ -32,10 +32,7 @@ class _ComputeJobsResult { final bool sawMalformed; } -enum _SetStatus { - Intersection, - Difference, -} +enum _SetStatus { Intersection, Difference } class _SetStatusCommand { _SetStatusCommand(this.setStatus, this.command); @@ -85,18 +82,17 @@ class ClangTidy { StringSink? outSink, StringSink? errSink, ProcessManager processManager = const LocalProcessManager(), - }) : - options = Options( - buildCommandsPath: buildCommandsPath, - checksArg: checksArg, - lintTarget: lintTarget, - fix: fix, - errSink: errSink, - ), - _outSink = outSink ?? io.stdout, - _errSink = errSink ?? io.stderr, - _processManager = processManager, - _engine = null; + }) : options = Options( + buildCommandsPath: buildCommandsPath, + checksArg: checksArg, + lintTarget: lintTarget, + fix: fix, + errSink: errSink, + ), + _outSink = outSink ?? io.stdout, + _errSink = errSink ?? io.stderr, + _processManager = processManager, + _engine = null; /// Builds an instance of [ClangTidy] from a command line. ClangTidy.fromCommandLine( @@ -105,12 +101,11 @@ class ClangTidy { StringSink? outSink, StringSink? errSink, ProcessManager processManager = const LocalProcessManager(), - }) : - options = Options.fromCommandLine(args, errSink: errSink, engine: engine), - _outSink = outSink ?? io.stdout, - _errSink = errSink ?? io.stderr, - _processManager = processManager, - _engine = engine; + }) : options = Options.fromCommandLine(args, errSink: errSink, engine: engine), + _outSink = outSink ?? io.stdout, + _errSink = errSink ?? io.stderr, + _processManager = processManager, + _engine = engine; /// The [Options] that specify how this [ClangTidy] operates. final Options options; @@ -159,20 +154,16 @@ class ClangTidy { 'HEAD.', ); case LintRegex(:final String regex): - _outSink.writeln( - 'Checking $changedFilesCount files that match the regex "$regex".', - ); + _outSink.writeln('Checking $changedFilesCount files that match the regex "$regex".'); } } - final List buildCommandsData = jsonDecode( - options.buildCommandsPath.readAsStringSync(), - ) as List; - final List> shardBuildCommandsData = options - .shardCommandsPaths - .map((io.File file) => - jsonDecode(file.readAsStringSync()) as List) - .toList(); + final List buildCommandsData = + jsonDecode(options.buildCommandsPath.readAsStringSync()) as List; + final List> shardBuildCommandsData = + options.shardCommandsPaths + .map((io.File file) => jsonDecode(file.readAsStringSync()) as List) + .toList(); final List changedFileBuildCommands = await getLintCommandsForFiles( buildCommandsData, filesOfInterest, @@ -220,17 +211,14 @@ class ClangTidy { Future> computeFilesOfInterest() async { switch (options.lintTarget) { case LintAll(): - return options.repoPath - .listSync(recursive: true) - .whereType() - .toList(); + return options.repoPath.listSync(recursive: true).whereType().toList(); case LintRegex(:final String regex): final RegExp pattern = RegExp(regex); return options.repoPath - .listSync(recursive: true) - .whereType() - .where((io.File file) => pattern.hasMatch(file.path)) - .toList(); + .listSync(recursive: true) + .whereType() + .where((io.File file) => pattern.hasMatch(file.path)) + .toList(); case LintChanged(): final GitRepo repo = GitRepo.fromRoot( options.repoPath, @@ -263,7 +251,9 @@ class ClangTidy { /// `Intersection` if the Command shows up in [items] and its filePath in all /// [filePathSets], otherwise `Difference`. Iterable<_SetStatusCommand> _calcIntersection( - Iterable items, Iterable> filePathSets) sync* { + Iterable items, + Iterable> filePathSets, + ) sync* { bool allSetsContain(Command command) { for (final Set filePathSet in filePathSets) { if (!filePathSet.contains(command.filePath)) { @@ -272,6 +262,7 @@ class ClangTidy { } return true; } + for (final Command command in items) { if (allSetsContain(command)) { yield _SetStatusCommand(_SetStatus.Intersection, command); @@ -297,17 +288,19 @@ class ClangTidy { if (sharedBuildCommandsData.isNotEmpty) { final List buildCommands = [ for (final Object? data in buildCommandsData) - Command.fromMap((data as Map?)!) + Command.fromMap((data as Map?)!), ]; final List> shardFilePaths = >[ for (final List list in sharedBuildCommandsData) { for (final Object? data in list) - Command.fromMap((data as Map?)!).filePath - } + Command.fromMap((data as Map?)!).filePath, + }, ]; - final Iterable<_SetStatusCommand> intersectionResults = - _calcIntersection(buildCommands, shardFilePaths); + final Iterable<_SetStatusCommand> intersectionResults = _calcIntersection( + buildCommands, + shardFilePaths, + ); for (final _SetStatusCommand result in intersectionResults) { if (result.setStatus == _SetStatus.Difference) { totalCommands.add(result.command); @@ -315,18 +308,16 @@ class ClangTidy { } final List intersection = [ for (final _SetStatusCommand result in intersectionResults) - if (result.setStatus == _SetStatus.Intersection) result.command + if (result.setStatus == _SetStatus.Intersection) result.command, ]; // Make sure to sort results so the sharding scheme is guaranteed to work // since we are not sure if there is a defined order in the json file. - intersection - .sort((Command x, Command y) => x.filePath.compareTo(y.filePath)); - totalCommands.addAll( - _takeShard(intersection, shardId!, 1 + sharedBuildCommandsData.length)); + intersection.sort((Command x, Command y) => x.filePath.compareTo(y.filePath)); + totalCommands.addAll(_takeShard(intersection, shardId!, 1 + sharedBuildCommandsData.length)); } else { totalCommands.addAll([ for (final Object? data in buildCommandsData) - Command.fromMap((data as Map?)!) + Command.fromMap((data as Map?)!), ]); } return () async { @@ -334,8 +325,7 @@ class ClangTidy { for (final Command command in totalCommands) { final LintAction lintAction = await command.lintAction; // Short-circuit the expensive containsAny call for the many third_party files. - if (lintAction != LintAction.skipThirdParty && - command.containsAny(files)) { + if (lintAction != LintAction.skipThirdParty && command.containsAny(files)) { result.add(command); } } @@ -343,10 +333,7 @@ class ClangTidy { }(); } - Future<_ComputeJobsResult> _computeJobs( - List commands, - Options options, - ) async { + Future<_ComputeJobsResult> _computeJobs(List commands, Options options) async { bool sawMalformed = false; final List jobs = []; for (final Command command in commands) { @@ -360,9 +347,7 @@ class ClangTidy { _outSink.writeln('🔷 ignoring $relativePath (FLUTTER_NOLINT)'); case LintAction.failMalformedNoLint: _errSink.writeln('❌ malformed opt-out $relativePath'); - _errSink.writeln( - ' Required format: // FLUTTER_NOLINT: $issueUrlPrefix/ISSUE_ID', - ); + _errSink.writeln(' Required format: // FLUTTER_NOLINT: $issueUrlPrefix/ISSUE_ID'); sawMalformed = true; case LintAction.lint: _outSink.writeln('🔶 linting $relativePath'); @@ -385,7 +370,7 @@ class ClangTidy { isPrintingError = true; yield line; } else if (line == ':') { - isPrintingError = false; + isPrintingError = false; } else if (isPrintingError) { yield line; } @@ -403,8 +388,15 @@ class ClangTidy { final Set pendingJobs = {for (final WorkerJob job in jobs) job.name}; void reporter(int totalJobs, int completed, int inProgress, int pending, int failed) { - return _logWithTimestamp(ProcessPool.defaultReportToString( - totalJobs, completed, inProgress, pending, failed - ignoredFailures)); + return _logWithTimestamp( + ProcessPool.defaultReportToString( + totalJobs, + completed, + inProgress, + pending, + failed - ignoredFailures, + ), + ); } final ProcessPool pool = ProcessPool( diff --git a/tools/clang_tidy/lib/src/command.dart b/tools/clang_tidy/lib/src/command.dart index b2e10b5472754..2c80ab19a0264 100644 --- a/tools/clang_tidy/lib/src/command.dart +++ b/tools/clang_tidy/lib/src/command.dart @@ -37,20 +37,17 @@ enum LintAction { /// it. class Command { /// Generate a [Command] from a [Map]. - Command.fromMap(Map map) : - directory = io.Directory(map['directory'] as String).absolute, - command = map['command'] as String { - filePath = path.normalize(path.join( - directory.path, - map['file'] as String, - )); + Command.fromMap(Map map) + : directory = io.Directory(map['directory'] as String).absolute, + command = map['command'] as String { + filePath = path.normalize(path.join(directory.path, map['file'] as String)); } /// The working directory of the command. final io.Directory directory; /// The compilation command. - final String command ; + final String command; /// The file on which the command operates. late final String filePath; @@ -65,35 +62,34 @@ class Command { /// The command line arguments of the command. String get tidyArgs { - return _tidyArgs ??= (() { - String result = command; - result = result.replaceAll(r'\s+', ' '); - // Remove everything that comes before the compiler command. - result = result.split(' ') - .skipWhile((String s) => !_pathRegex.hasMatch(s)) - .join(' '); - result = result.replaceAll(_pathRegex, ''); - result = result.replaceAll(_argRegex, ''); - result = result.replaceAll(_extraCommandRegex, ''); - return result; - })(); + return _tidyArgs ??= + (() { + String result = command; + result = result.replaceAll(r'\s+', ' '); + // Remove everything that comes before the compiler command. + result = result.split(' ').skipWhile((String s) => !_pathRegex.hasMatch(s)).join(' '); + result = result.replaceAll(_pathRegex, ''); + result = result.replaceAll(_argRegex, ''); + result = result.replaceAll(_extraCommandRegex, ''); + return result; + })(); } String? _tidyPath; /// The command but with clang-tidy instead of clang. String get tidyPath { - return _tidyPath ??= _pathRegex.stringMatch(command)?.replaceAll( - 'clang/bin/clang', - 'clang/bin/clang-tidy', - ).replaceAll('clang-tidy++', 'clang-tidy') ?? ''; + return _tidyPath ??= + _pathRegex + .stringMatch(command) + ?.replaceAll('clang/bin/clang', 'clang/bin/clang-tidy') + .replaceAll('clang-tidy++', 'clang-tidy') ?? + ''; } /// Whether this command operates on any of the files in `queries`. bool containsAny(List queries) { - return queries.indexWhere( - (io.File query) => path.equals(query.path, filePath), - ) != -1; + return queries.indexWhere((io.File query) => path.equals(query.path, filePath)) != -1; } static final RegExp _nolintRegex = RegExp( @@ -114,9 +110,10 @@ class Command { if (!file.existsSync()) { return LintAction.skipMissing; } - final Stream lines = file.openRead() - .transform(utf8.decoder) - .transform(const LineSplitter()); + final Stream lines = file + .openRead() + .transform(utf8.decoder) + .transform(const LineSplitter()); return lintActionFromContents(lines); } @@ -127,9 +124,7 @@ class Command { await for (final String line in lines) { final RegExpMatch? match = _nolintRegex.firstMatch(line); if (match != null) { - return match.group(1) != null - ? LintAction.skipNoLint - : LintAction.failMalformedNoLint; + return match.group(1) != null ? LintAction.skipNoLint : LintAction.failMalformedNoLint; } else if (line.isNotEmpty && line[0] != '\n' && line[0] != '/') { // Quick out once we find a line that isn't empty or a comment. The // FLUTTER_NOLINT must show up before the first real code. @@ -144,14 +139,9 @@ class Command { final List args = [ filePath, '--warnings-as-errors=${options.warningsAsErrors ?? '*'}', - if (options.checks != null) - options.checks!, - if (options.fix) ...[ - '--fix', - '--format-style=file', - ], - if (options.enableCheckProfile) - '--enable-check-profile', + if (options.checks != null) options.checks!, + if (options.fix) ...['--fix', '--format-style=file'], + if (options.enableCheckProfile) '--enable-check-profile', '--', ]; args.addAll(tidyArgs.split(' ')); diff --git a/tools/clang_tidy/lib/src/options.dart b/tools/clang_tidy/lib/src/options.dart index c4d1d8d476576..ff1239a13937d 100644 --- a/tools/clang_tidy/lib/src/options.dart +++ b/tools/clang_tidy/lib/src/options.dart @@ -20,7 +20,6 @@ String? _platformSpecificWarningsAsErrors(ArgResults options) { return null; } - /// A class for organizing the options to the Engine linter, and the files /// that it operates on. class Options { @@ -42,25 +41,12 @@ class Options { }) : checks = checksArg.isNotEmpty ? '--checks=$checksArg' : null, _errSink = errSink ?? io.stderr; - factory Options._error( - String message, { - StringSink? errSink, - }) { - return Options( - errorMessage: message, - buildCommandsPath: io.File('none'), - errSink: errSink, - ); + factory Options._error(String message, {StringSink? errSink}) { + return Options(errorMessage: message, buildCommandsPath: io.File('none'), errSink: errSink); } - factory Options._help({ - StringSink? errSink, - }) { - return Options( - help: true, - buildCommandsPath: io.File('none'), - errSink: errSink, - ); + factory Options._help({StringSink? errSink}) { + return Options(help: true, buildCommandsPath: io.File('none'), errSink: errSink); } /// Builds an [Options] instance with an [ArgResults] instance. @@ -99,11 +85,7 @@ class Options { } /// Builds an instance of [Options] from the given `arguments`. - factory Options.fromCommandLine( - List arguments, { - StringSink? errSink, - Engine? engine, - }) { + factory Options.fromCommandLine(List arguments, {StringSink? errSink, Engine? engine}) { // TODO(matanlurey): Refactor this further, ideally moving all of the engine // resolution logic (i.e. --src-dir, --target-variant, --compile-commands) // into a separate method, and perhaps also adding `engine.output(name)` @@ -113,12 +95,7 @@ class Options { String? buildCommandsPath = argResults['compile-commands'] as String?; String variantToBuildCommandsFilePath(String variant) => - path.join( - argResults['src-dir'] as String, - 'out', - variant, - 'compile_commands.json', - ); + path.join(argResults['src-dir'] as String, 'out', variant, 'compile_commands.json'); // path/to/engine/src/out/variant/compile_commands.json buildCommandsPath ??= variantToBuildCommandsFilePath(argResults['target-variant'] as String); final io.File buildCommands = io.File(buildCommandsPath); @@ -126,8 +103,7 @@ class Options { (argResults['shard-variants'] as String? ?? '') .split(',') .where((String element) => element.isNotEmpty) - .map((String variant) => - io.File(variantToBuildCommandsFilePath(variant))) + .map((String variant) => io.File(variantToBuildCommandsFilePath(variant))) .toList(); final String? message = _checkArguments(argResults, buildCommands); if (message != null) { @@ -141,9 +117,9 @@ class Options { if (shardId != null && (shardId > shardCommands.length || shardId < 0)) { return Options._error('Invalid shard-id value: $shardId.', errSink: errSink); } - final io.File? clangTidyPath = ((String? path) => path == null - ? null - : io.File(path))(argResults['clang-tidy'] as String?); + final io.File? clangTidyPath = ((String? path) => path == null ? null : io.File(path))( + argResults['clang-tidy'] as String?, + ); return Options._fromArgResults( argResults, buildCommandsPath: buildCommands, @@ -158,35 +134,19 @@ class Options { defaultEngine ??= _engineRoot; final io.Directory? latestBuild = defaultEngine.latestOutput()?.path; return ArgParser() - ..addFlag( - 'help', - abbr: 'h', - help: 'Print help.', - negatable: false, - ) + ..addFlag('help', abbr: 'h', help: 'Print help.', negatable: false) ..addOption( 'lint-regex', - help: 'Lint all files, regardless of FLUTTER_NOLINT. Provide a regex ' - 'to filter files. For example, `--lint-regex=".*impeller.*"` will ' - 'lint all files within a path that contains "impeller".', + help: + 'Lint all files, regardless of FLUTTER_NOLINT. Provide a regex ' + 'to filter files. For example, `--lint-regex=".*impeller.*"` will ' + 'lint all files within a path that contains "impeller".', valueHelp: 'regex', ) - ..addFlag( - 'lint-all', - help: 'Lint all files, regardless of FLUTTER_NOLINT.', - ) - ..addFlag( - 'lint-head', - help: 'Lint files changed in the tip-of-tree commit.', - ) - ..addFlag( - 'fix', - help: 'Apply suggested fixes.', - ) - ..addFlag( - 'verbose', - help: 'Print verbose output.', - ) + ..addFlag('lint-all', help: 'Lint all files, regardless of FLUTTER_NOLINT.') + ..addFlag('lint-head', help: 'Lint files changed in the tip-of-tree commit.') + ..addFlag('fix', help: 'Apply suggested fixes.') + ..addFlag('verbose', help: 'Print verbose output.') ..addOption( 'shard-id', help: 'When used with the shard-commands option this identifies which shard will execute.', @@ -194,50 +154,57 @@ class Options { ) ..addOption( 'shard-variants', - help: 'Comma separated list of other targets, this invocation ' - 'will only execute a subset of the intersection and the difference of the ' - 'compile commands. Use with `shard-id`.' + help: + 'Comma separated list of other targets, this invocation ' + 'will only execute a subset of the intersection and the difference of the ' + 'compile commands. Use with `shard-id`.', ) ..addOption( 'compile-commands', - help: 'Use the given path as the source of compile_commands.json. This ' - 'file is created by running "tools/gn". Cannot be used with --target-variant ' - 'or --src-dir.', + help: + 'Use the given path as the source of compile_commands.json. This ' + 'file is created by running "tools/gn". Cannot be used with --target-variant ' + 'or --src-dir.', ) ..addOption( 'target-variant', aliases: ['variant'], - help: 'The engine variant directory name containing compile_commands.json ' - 'created by running "tools/gn".\n\nIf not provided, the default is ' - 'the latest build in the engine defined by --src-dir (or the ' - 'default path, see --src-dir for details).\n\n' - 'Cannot be used with --compile-commands.', + help: + 'The engine variant directory name containing compile_commands.json ' + 'created by running "tools/gn".\n\nIf not provided, the default is ' + 'the latest build in the engine defined by --src-dir (or the ' + 'default path, see --src-dir for details).\n\n' + 'Cannot be used with --compile-commands.', valueHelp: 'host_debug|android_debug_unopt|ios_debug|ios_debug_sim_unopt', defaultsTo: latestBuild == null ? 'host_debug' : path.basename(latestBuild.path), ) - ..addOption('mac-host-warnings-as-errors', - help: - 'checks that will be treated as errors when running debug_host on mac.') + ..addOption( + 'mac-host-warnings-as-errors', + help: 'checks that will be treated as errors when running debug_host on mac.', + ) ..addOption( 'src-dir', help: - 'Path to the engine src directory.\n\n' - 'If not provided, the default is the engine root directory that ' - 'contains the `clang_tidy` tool.\n\n' - 'Cannot be used with --compile-commands.', + 'Path to the engine src directory.\n\n' + 'If not provided, the default is the engine root directory that ' + 'contains the `clang_tidy` tool.\n\n' + 'Cannot be used with --compile-commands.', valueHelp: 'path/to/engine/src', defaultsTo: _engineRoot.srcDir.path, ) ..addOption( 'checks', - help: 'Perform the given checks on the code. Defaults to the empty ' - 'string, indicating all checks should be performed.', + help: + 'Perform the given checks on the code. Defaults to the empty ' + 'string, indicating all checks should be performed.', defaultsTo: '', ) - ..addOption('clang-tidy', - help: - 'Path to the clang-tidy executable. Defaults to deriving the path\n' - 'from compile_commands.json.') + ..addOption( + 'clang-tidy', + help: + 'Path to the clang-tidy executable. Defaults to deriving the path\n' + 'from compile_commands.json.', + ) ..addFlag( 'enable-check-profile', help: 'Enable per-check timing profiles and print a report to stderr.', @@ -318,7 +285,8 @@ class Options { return 'ERROR: --compile-commands option cannot be used with --src-dir.'; } - if (const ['lint-all', 'lint-head', 'lint-regex'].where(argResults.wasParsed).length > 1) { + if (const ['lint-all', 'lint-head', 'lint-regex'].where(argResults.wasParsed).length > + 1) { return 'ERROR: At most one of --lint-all, --lint-head, --lint-regex can be passed.'; } diff --git a/tools/clang_tidy/test/clang_tidy_test.dart b/tools/clang_tidy/test/clang_tidy_test.dart index f00833841f9d2..46d37ab4306ef 100644 --- a/tools/clang_tidy/test/clang_tidy_test.dart +++ b/tools/clang_tidy/test/clang_tidy_test.dart @@ -19,43 +19,45 @@ import 'package:test/test.dart'; /// A test fixture for the `clang-tidy` tool. final class Fixture { /// Simulates running the tool with the given [args]. - factory Fixture.fromCommandLine(List args, { + factory Fixture.fromCommandLine( + List args, { ProcessManager? processManager, Engine? engine, }) { processManager ??= FakeProcessManager(); final StringBuffer outBuffer = StringBuffer(); final StringBuffer errBuffer = StringBuffer(); - return Fixture._(ClangTidy.fromCommandLine( - args, - outSink: outBuffer, - errSink: errBuffer, - processManager: processManager, - engine: engine, - ), errBuffer); + return Fixture._( + ClangTidy.fromCommandLine( + args, + outSink: outBuffer, + errSink: errBuffer, + processManager: processManager, + engine: engine, + ), + errBuffer, + ); } /// Simulates running the tool with the given [options]. - factory Fixture.fromOptions(Options options, { - ProcessManager? processManager, - }) { + factory Fixture.fromOptions(Options options, {ProcessManager? processManager}) { processManager ??= FakeProcessManager(); final StringBuffer outBuffer = StringBuffer(); final StringBuffer errBuffer = StringBuffer(); - return Fixture._(ClangTidy( - buildCommandsPath: options.buildCommandsPath, - lintTarget: options.lintTarget, - fix: options.fix, - outSink: outBuffer, - errSink: errBuffer, - processManager: processManager, - ), errBuffer); + return Fixture._( + ClangTidy( + buildCommandsPath: options.buildCommandsPath, + lintTarget: options.lintTarget, + fix: options.fix, + outSink: outBuffer, + errSink: errBuffer, + processManager: processManager, + ), + errBuffer, + ); } - Fixture._( - this.tool, - this.errBuffer, - ); + Fixture._(this.tool, this.errBuffer); /// The `clang-tidy` tool. final ClangTidy tool; @@ -129,10 +131,9 @@ void main() { test('--help gives help, and uses host_debug by default outside of an engine root', () async { final io.Directory rootDir = io.Directory.systemTemp.createTempSync('clang_tidy_test'); try { - final Fixture fixture = Fixture.fromCommandLine( - ['--help'], - engine: TestEngine.createTemp(rootDir: rootDir) - ); + final Fixture fixture = Fixture.fromCommandLine([ + '--help', + ], engine: TestEngine.createTemp(rootDir: rootDir)); final int result = await fixture.tool.run(); expect(fixture.tool.options.help, isTrue); @@ -146,75 +147,79 @@ void main() { } }); - test('--help gives help, and uses the latest build by default outside in an engine root', () async { - final io.Directory rootDir = io.Directory.systemTemp.createTempSync('clang_tidy_test'); - final io.Directory buildDir = io.Directory(path.join(rootDir.path, 'out', 'host_debug_unopt_arm64'))..createSync(recursive: true); - try { - final Fixture fixture = Fixture.fromCommandLine( - ['--help'], - engine: TestEngine.createTemp(rootDir: rootDir, outputs: [ - TestOutput(buildDir), - ]) - ); - final int result = await fixture.tool.run(); - - expect(fixture.tool.options.help, isTrue); - expect(result, equals(0)); - - final String errors = fixture.errBuffer.toString(); - expect(errors, contains('Usage: ')); - expect(errors, contains('defaults to "host_debug_unopt_arm64"')); - } finally { - rootDir.deleteSync(recursive: true); - } - }); + test( + '--help gives help, and uses the latest build by default outside in an engine root', + () async { + final io.Directory rootDir = io.Directory.systemTemp.createTempSync('clang_tidy_test'); + final io.Directory buildDir = io.Directory( + path.join(rootDir.path, 'out', 'host_debug_unopt_arm64'), + )..createSync(recursive: true); + try { + final Fixture fixture = Fixture.fromCommandLine( + ['--help'], + engine: TestEngine.createTemp( + rootDir: rootDir, + outputs: [TestOutput(buildDir)], + ), + ); + final int result = await fixture.tool.run(); + + expect(fixture.tool.options.help, isTrue); + expect(result, equals(0)); + + final String errors = fixture.errBuffer.toString(); + expect(errors, contains('Usage: ')); + expect(errors, contains('defaults to "host_debug_unopt_arm64"')); + } finally { + rootDir.deleteSync(recursive: true); + } + }, + ); test('trimmed clang-tidy output', () { expect(_tidyTrimmedOutput, equals(ClangTidy.trimOutput(_tidyOutput))); }); test('Error when --compile-commands and --target-variant are used together', () async { - final Fixture fixture = Fixture.fromCommandLine( - [ - '--compile-commands', - '/unused', - '--target-variant', - 'unused' - ], - ); + final Fixture fixture = Fixture.fromCommandLine([ + '--compile-commands', + '/unused', + '--target-variant', + 'unused', + ]); final int result = await fixture.tool.run(); expect(result, equals(1)); - expect(fixture.errBuffer.toString(), contains( - 'ERROR: --compile-commands option cannot be used with --target-variant.', - )); + expect( + fixture.errBuffer.toString(), + contains('ERROR: --compile-commands option cannot be used with --target-variant.'), + ); }); test('Error when --compile-commands and --src-dir are used together', () async { - final Fixture fixture = Fixture.fromCommandLine( - [ - '--compile-commands', - '/unused', - '--src-dir', - '/unused', - ], - ); + final Fixture fixture = Fixture.fromCommandLine([ + '--compile-commands', + '/unused', + '--src-dir', + '/unused', + ]); final int result = await fixture.tool.run(); expect(result, equals(1)); - expect(fixture.errBuffer.toString(), contains( - 'ERROR: --compile-commands option cannot be used with --src-dir.', - )); + expect( + fixture.errBuffer.toString(), + contains('ERROR: --compile-commands option cannot be used with --src-dir.'), + ); }); test('shard-id valid', () async { _withTempFile('shard-id-valid', (String path) { - final Options options = Options.fromCommandLine( [ - '--compile-commands=$path', - '--shard-variants=variant', - '--shard-id=1', - ],); + final Options options = Options.fromCommandLine([ + '--compile-commands=$path', + '--shard-variants=variant', + '--shard-id=1', + ]); expect(options.errorMessage, isNull); expect(options.shardId, equals(1)); }); @@ -222,10 +227,10 @@ void main() { test('clang-tidy specified', () async { _withTempFile('shard-id-valid', (String path) { - final Options options = Options.fromCommandLine( [ - '--compile-commands=$path', - '--clang-tidy=foo/bar', - ],); + final Options options = Options.fromCommandLine([ + '--compile-commands=$path', + '--clang-tidy=foo/bar', + ]); expect(options.clangTidyPath, isNotNull); expect(options.clangTidyPath!.path, equals('foo/bar')); }); @@ -233,7 +238,7 @@ void main() { test('clang-tidy unspecified', () async { _withTempFile('shard-id-valid', (String path) { - final Options options = Options.fromCommandLine( [],); + final Options options = Options.fromCommandLine([]); expect(options.clangTidyPath, isNull); }); }); @@ -253,79 +258,74 @@ void main() { }); test('Error when --compile-commands path does not exist', () async { - final Fixture fixture = Fixture.fromCommandLine( - [ - '--compile-commands', - '/does/not/exist', - ], - ); + final Fixture fixture = Fixture.fromCommandLine([ + '--compile-commands', + '/does/not/exist', + ]); final int result = await fixture.tool.run(); expect(result, equals(1)); - expect(fixture.errBuffer.toString().split('\n')[0], matches( - r"ERROR: Build commands path .*/does/not/exist doesn't exist.", - )); + expect( + fixture.errBuffer.toString().split('\n')[0], + matches(r"ERROR: Build commands path .*/does/not/exist doesn't exist."), + ); }); test('Error when --src-dir path does not exist, uses target variant in path', () async { - final Fixture fixture = Fixture.fromCommandLine( - [ - '--src-dir', - '/does/not/exist', - '--target-variant', - 'ios_debug_unopt', - ], - ); + final Fixture fixture = Fixture.fromCommandLine([ + '--src-dir', + '/does/not/exist', + '--target-variant', + 'ios_debug_unopt', + ]); final int result = await fixture.tool.run(); expect(result, equals(1)); - expect(fixture.errBuffer.toString().split('\n')[0], matches( - r'ERROR: Build commands path .*/does/not/exist' - r'[/\\]out[/\\]ios_debug_unopt[/\\]compile_commands.json' - r" doesn't exist.", - )); + expect( + fixture.errBuffer.toString().split('\n')[0], + matches( + r'ERROR: Build commands path .*/does/not/exist' + r'[/\\]out[/\\]ios_debug_unopt[/\\]compile_commands.json' + r" doesn't exist.", + ), + ); }); test('Error when --lint-all and --lint-head are used together', () async { - final Fixture fixture = Fixture.fromCommandLine( - [ - '--compile-commands', - '/unused', - '--lint-all', - '--lint-head', - ], - ); + final Fixture fixture = Fixture.fromCommandLine([ + '--compile-commands', + '/unused', + '--lint-all', + '--lint-head', + ]); final int result = await fixture.tool.run(); expect(result, equals(1)); - expect(fixture.errBuffer.toString(), contains( - 'ERROR: At most one of --lint-all, --lint-head, --lint-regex can be passed.', - )); + expect( + fixture.errBuffer.toString(), + contains('ERROR: At most one of --lint-all, --lint-head, --lint-regex can be passed.'), + ); }); test('Error when --lint-all and --lint-regex are used together', () async { - final Fixture fixture = Fixture.fromCommandLine( - [ - '--compile-commands', - '/unused', - '--lint-all', - '--lint-regex=".*"', - ], - ); + final Fixture fixture = Fixture.fromCommandLine([ + '--compile-commands', + '/unused', + '--lint-all', + '--lint-regex=".*"', + ]); final int result = await fixture.tool.run(); expect(result, equals(1)); - expect(fixture.errBuffer.toString(), contains( - 'ERROR: At most one of --lint-all, --lint-head, --lint-regex can be passed.', - )); + expect( + fixture.errBuffer.toString(), + contains('ERROR: At most one of --lint-all, --lint-head, --lint-regex can be passed.'), + ); }); test('lintAll=true checks all files', () async { final Fixture fixture = Fixture.fromOptions( - Options( - buildCommandsPath: io.File(buildCommands), - lintTarget: const LintAll(), - ), + Options(buildCommandsPath: io.File(buildCommands), lintTarget: const LintAll()), ); final List fileList = await fixture.tool.computeFilesOfInterest(); expect(fileList.length, greaterThan(1000)); @@ -375,10 +375,7 @@ void main() { test('Sharding', () async { final Fixture fixture = Fixture.fromOptions( - Options( - buildCommandsPath: io.File(buildCommands), - lintTarget: const LintAll(), - ), + Options(buildCommandsPath: io.File(buildCommands), lintTarget: const LintAll()), processManager: FakeProcessManager( onStart: (List command) { if (command.first == 'git') { @@ -399,12 +396,12 @@ void main() { } final List filePaths = [ - for (int i = 0; i < 10; ++i) '/path/to/a/source_file_$i.cc' + for (int i = 0; i < 10; ++i) '/path/to/a/source_file_$i.cc', ]; final List> buildCommandsData = filePaths.map((String e) => makeBuildCommandEntry(e)).toList(); final List> shardBuildCommandsData = - filePaths.sublist(6).map((String e) => makeBuildCommandEntry(e)).toList(); + filePaths.sublist(6).map((String e) => makeBuildCommandEntry(e)).toList(); { final List commands = await fixture.tool.getLintCommandsForFiles( @@ -451,10 +448,7 @@ void main() { test('No Commands are produced when no files changed', () async { final Fixture fixture = Fixture.fromOptions( - Options( - buildCommandsPath: io.File(buildCommands), - lintTarget: const LintAll(), - ), + Options(buildCommandsPath: io.File(buildCommands), lintTarget: const LintAll()), ); const String filePath = '/path/to/a/source_file.cc'; @@ -477,10 +471,7 @@ void main() { test('A Command is produced when a file is changed', () async { final Fixture fixture = Fixture.fromOptions( - Options( - buildCommandsPath: io.File(buildCommands), - lintTarget: const LintAll(), - ), + Options(buildCommandsPath: io.File(buildCommands), lintTarget: const LintAll()), ); // This file needs to exist, and be UTF8 line-parsable. @@ -539,9 +530,7 @@ void main() { }); test('Command getLintAction flags missing files', () async { - final LintAction lintAction = await Command.getLintAction( - '/does/not/exist', - ); + final LintAction lintAction = await Command.getLintAction('/does/not/exist'); expect(lintAction, equals(LintAction.skipMissing)); }); @@ -594,45 +583,41 @@ void main() { test('Command filters out sed command after a compile command', () { final Command command = Command.fromMap({ - 'directory': '/unused', - 'command': + 'directory': '/unused', + 'command': '../../buildtools/mac-x64/clang/bin/clang filename ' "&& sed -i 's@/b/f/w@../..@g' filename", - 'file': 'unused', + 'file': 'unused', }); expect(command.tidyArgs.trim(), 'filename'); }); test('Command filters out the -MF flag', () { final Command command = Command.fromMap({ - 'directory': '/unused', - 'command': - '../../buildtools/mac-x64/clang/bin/clang -MF stuff filename ', - 'file': 'unused', + 'directory': '/unused', + 'command': '../../buildtools/mac-x64/clang/bin/clang -MF stuff filename ', + 'file': 'unused', }); expect(command.tidyArgs.trim(), 'filename'); }); test('Command filters out rewrapper command before a compile command', () { final Command command = Command.fromMap({ - 'directory': '/unused', - 'command': + 'directory': '/unused', + 'command': 'flutter/engine/src/buildtools/mac-arm64/reclient/rewrapper ' '--cfg=flutter/engine/src/flutter/build/rbe/rewrapper-mac-arm64.cfg ' '--exec_root=flutter/engine/src/ ' '--labels=type=compile,compiler=clang,lang=cpp ' '../../buildtools/mac-x64/clang/bin/clang++ filename ', - 'file': 'unused', + 'file': 'unused', }); expect(command.tidyArgs.trim(), 'filename'); }); test('Files that cause clang-tidy to segfault are skipped', () async { final Fixture fileListFixture = Fixture.fromOptions( - Options( - buildCommandsPath: io.File(buildCommands), - lintTarget: const LintAll(), - ), + Options(buildCommandsPath: io.File(buildCommands), lintTarget: const LintAll()), ); final String firstFilePath = (await fileListFixture.tool.computeFilesOfInterest()).first.path; @@ -653,16 +638,14 @@ void main() { }, ]; - final io.File commands = io.File(path.join( - io.Directory.systemTemp.path, 'test_compile_commands.json')); + final io.File commands = io.File( + path.join(io.Directory.systemTemp.path, 'test_compile_commands.json'), + ); int result; try { commands.writeAsStringSync(jsonEncode(commandsData)); final Fixture fixture = Fixture.fromOptions( - Options( - buildCommandsPath: commands, - lintTarget: const LintAll(), - ), + Options(buildCommandsPath: commands, lintTarget: const LintAll()), processManager: fakeProcessManager, ); result = await fixture.tool.run(); diff --git a/tools/clangd_check/bin/main.dart b/tools/clangd_check/bin/main.dart index d5badb009dc34..e024ac2cd6b9b 100644 --- a/tools/clangd_check/bin/main.dart +++ b/tools/clangd_check/bin/main.dart @@ -11,22 +11,18 @@ import 'package:path/path.dart' as p; void main(List args) { final Engine? engine = Engine.tryFindWithin(); - final ArgParser parser = ArgParser() - ..addFlag( - 'help', - abbr: 'h', - help: 'Print this usage information.', - negatable: false, - ) - ..addOption( - 'clangd', - help: 'Path to clangd. Defaults to deriving the path from compile_commands.json.', - ) - ..addOption( - 'compile-commands-dir', - help: 'Path to a directory containing compile_commands.json.', - defaultsTo: engine?.latestOutput()?.compileCommandsJson.parent.path, - ); + final ArgParser parser = + ArgParser() + ..addFlag('help', abbr: 'h', help: 'Print this usage information.', negatable: false) + ..addOption( + 'clangd', + help: 'Path to clangd. Defaults to deriving the path from compile_commands.json.', + ) + ..addOption( + 'compile-commands-dir', + help: 'Path to a directory containing compile_commands.json.', + defaultsTo: engine?.latestOutput()?.compileCommandsJson.parent.path, + ); final ArgResults results = parser.parse(args); if (results['help'] as bool) { io.stdout.writeln(parser.usage); @@ -46,7 +42,8 @@ void main(List args) { return; } - final List compileCommands = json.decode(compileCommandsFile.readAsStringSync()) as List; + final List compileCommands = + json.decode(compileCommandsFile.readAsStringSync()) as List; if (compileCommands.isEmpty) { io.stderr.writeln('Unexpected: compile_commands.json is empty'); io.exitCode = 1; @@ -75,14 +72,10 @@ void main(List args) { // Find the canonical path to the command (i.e. resolve "../" and ".") // // This now looks like "/path/to/engine/src/flutter/buildtools/{platform}/{...}" - final String path = p.canonicalize( - p.join(compileCommandsDir, commandPath), - ); + final String path = p.canonicalize(p.join(compileCommandsDir, commandPath)); // Extract which platform we're building for (e.g. linux-x64, mac-arm64, mac-x64). - final String platform = RegExp( - r'buildtools/([^/]+)/', - ).firstMatch(path)!.group(1)!; + final String platform = RegExp(r'buildtools/([^/]+)/').firstMatch(path)!.group(1)!; // Find the engine root and derive the clangd path from there. final Engine compileCommandsEngineRoot = Engine.findWithin(path); @@ -117,7 +110,7 @@ void main(List args) { clangdConfig.writeAsStringSync( 'CompileFlags:\n' ' Add: -Wno-unknown-warning-option\n' - ' Remove: [-m*, -f*]\n' + ' Remove: [-m*, -f*]\n', ); // Run clangd. @@ -128,7 +121,9 @@ void main(List args) { ]); io.stdout.write(result.stdout); io.stderr.write(result.stderr); - if ((result.stderr as String).contains('Path specified by --compile-commands-dir does not exist')) { + if ((result.stderr as String).contains( + 'Path specified by --compile-commands-dir does not exist', + )) { io.stdout.writeln('clangd_check failed: --compile-commands-dir does not exist'); io.exitCode = 1; } else if ((result.stderr as String).contains('Failed to resolve path')) { diff --git a/tools/compare_goldens/lib/compare_goldens.dart b/tools/compare_goldens/lib/compare_goldens.dart index 8a865e5b7eca2..fd8ed5c6387e9 100644 --- a/tools/compare_goldens/lib/compare_goldens.dart +++ b/tools/compare_goldens/lib/compare_goldens.dart @@ -10,7 +10,7 @@ bool _hasCommandOnPath(String name) { } List _findPairs(Set as, Set bs) { - final List result = []; + final List result = []; for (final String a in as) { if (bs.contains(a)) { result.add(a); @@ -31,10 +31,11 @@ String _basename(String path) { } Set _grabPngFilenames(Directory dir) { - return dir.listSync() - .map((FileSystemEntity e) => _basename(e.path)) - .where((String e) => e.endsWith('.png')) - .toSet(); + return dir + .listSync() + .map((FileSystemEntity e) => _basename(e.path)) + .where((String e) => e.endsWith('.png')) + .toSet(); } /// The main entry point to the tool, execute it like `main`. Returns the @@ -72,8 +73,15 @@ int run(List args) { final String pathB = [dirB.path, name].join(Platform.pathSeparator); final String output = 'diff_$name'; print('compare ($count / ${pairs.length}) $name'); - final ProcessResult result = Process.runSync('compare', - ['-metric', 'RMSE', '-fuzz', '5%', pathA, pathB, output]); + final ProcessResult result = Process.runSync('compare', [ + '-metric', + 'RMSE', + '-fuzz', + '5%', + pathA, + pathB, + output, + ]); if (result.exitCode != 0) { print('DIFF FOUND: saved to $output'); returnCode = 1; diff --git a/tools/const_finder/bin/main.dart b/tools/const_finder/bin/main.dart index 0a64dc452f8b6..5176344d2a2d9 100644 --- a/tools/const_finder/bin/main.dart +++ b/tools/const_finder/bin/main.dart @@ -11,8 +11,10 @@ import 'package:kernel/const_finder.dart'; void main(List args) { final ArgParser parser = ArgParser(); parser - ..addSeparator('Finds constant instances of a specified class from the\n' - 'specified package, and outputs JSON like the following:') + ..addSeparator( + 'Finds constant instances of a specified class from the\n' + 'specified package, and outputs JSON like the following:', + ) ..addSeparator(''' { "constantInstances": [ @@ -31,43 +33,53 @@ void main(List args) { } ] }''') - ..addSeparator('Where the "constantInstances" is a list of objects containing\n' - 'the properties passed to the const constructor of the class, and\n' - '"nonConstantInstances" is a list of source locations of non-constant\n' - 'creation of the specified class. Non-constant creation cannot be\n' - 'statically evaluated by this tool, and callers may wish to treat them\n' - 'as errors. The non-constant creation may include entries that are not\n' - 'reachable at runtime.') + ..addSeparator( + 'Where the "constantInstances" is a list of objects containing\n' + 'the properties passed to the const constructor of the class, and\n' + '"nonConstantInstances" is a list of source locations of non-constant\n' + 'creation of the specified class. Non-constant creation cannot be\n' + 'statically evaluated by this tool, and callers may wish to treat them\n' + 'as errors. The non-constant creation may include entries that are not\n' + 'reachable at runtime.', + ) ..addSeparator('Required arguments:') - ..addOption('kernel-file', - valueHelp: 'path/to/main.dill', - help: 'The path to a kernel file to parse, which was created from the ' - 'main-package-uri library.', - mandatory: true) - ..addOption('class-library-uri', - mandatory: true, - help: 'The package: URI of the class to find.', - valueHelp: 'package:flutter/src/widgets/icon_data.dart') - ..addOption('class-name', - help: 'The class name for the class to find.', - valueHelp: 'IconData', - mandatory: true) + ..addOption( + 'kernel-file', + valueHelp: 'path/to/main.dill', + help: + 'The path to a kernel file to parse, which was created from the ' + 'main-package-uri library.', + mandatory: true, + ) + ..addOption( + 'class-library-uri', + mandatory: true, + help: 'The package: URI of the class to find.', + valueHelp: 'package:flutter/src/widgets/icon_data.dart', + ) + ..addOption( + 'class-name', + help: 'The class name for the class to find.', + valueHelp: 'IconData', + mandatory: true, + ) ..addSeparator('Optional arguments:') - ..addFlag('pretty', - negatable: false, - help: 'Pretty print JSON output (defaults to false).') - ..addFlag('help', - abbr: 'h', - negatable: false, - help: 'Print usage and exit') - ..addOption('annotation-class-name', - help: 'The class name of the annotation for classes that should be ' - 'ignored.', - valueHelp: 'StaticIconProvider') - ..addOption('annotation-class-library-uri', - help: 'The package: URI of the class of the annotation for classes ' - 'that should be ignored.', - valueHelp: 'package:flutter/src/material/icons.dart'); + ..addFlag('pretty', negatable: false, help: 'Pretty print JSON output (defaults to false).') + ..addFlag('help', abbr: 'h', negatable: false, help: 'Print usage and exit') + ..addOption( + 'annotation-class-name', + help: + 'The class name of the annotation for classes that should be ' + 'ignored.', + valueHelp: 'StaticIconProvider', + ) + ..addOption( + 'annotation-class-library-uri', + help: + 'The package: URI of the class of the annotation for classes ' + 'that should be ignored.', + valueHelp: 'package:flutter/src/material/icons.dart', + ); final ArgResults argResults = parser.parse(args); T getArg(String name) => argResults[name] as T; @@ -96,9 +108,8 @@ void main(List args) { annotationClassLibraryUri: annotationClassLibraryUri, ); - final JsonEncoder encoder = getArg('pretty') - ? const JsonEncoder.withIndent(' ') - : const JsonEncoder(); + final JsonEncoder encoder = + getArg('pretty') ? const JsonEncoder.withIndent(' ') : const JsonEncoder(); stdout.writeln(encoder.convert(finder.findInstances())); } diff --git a/tools/const_finder/test/const_finder_test.dart b/tools/const_finder/test/const_finder_test.dart index 5c5f314dceebc..c488211e2b409 100644 --- a/tools/const_finder/test/const_finder_test.dart +++ b/tools/const_finder/test/const_finder_test.dart @@ -25,53 +25,30 @@ void main() { 'test', 'fixtures', ); - final String fixturesUrl = io.Platform.isWindows - ? '/$fixturesPath'.replaceAll(io.Platform.pathSeparator, '/') - : fixturesPath; - - final frontendServerSnapshot = path.join( - buildDir, - 'gen', - 'frontend_server_aot.dart.snapshot', - ); - final flutterPatchedSdk = path.join( - buildDir, - 'flutter_patched_sdk', - ); - final librariesDotJson = path.join( - flutterPatchedSdk, - 'lib', - 'libraries.json', - ); - final String packageConfig = path.join( - fixturesPath, - '.dart_tool', - 'package_config.json', - ); + final String fixturesUrl = + io.Platform.isWindows + ? '/$fixturesPath'.replaceAll(io.Platform.pathSeparator, '/') + : fixturesPath; - final dart = io.Platform.resolvedExecutable; - final dartaotruntime = path.join( - path.dirname(io.Platform.resolvedExecutable), - 'dartaotruntime', - ); + final frontendServerSnapshot = path.join(buildDir, 'gen', 'frontend_server_aot.dart.snapshot'); + final flutterPatchedSdk = path.join(buildDir, 'flutter_patched_sdk'); + final librariesDotJson = path.join(flutterPatchedSdk, 'lib', 'libraries.json'); + final String packageConfig = path.join(fixturesPath, '.dart_tool', 'package_config.json'); - void compileAOTDill({ - required String sourcePath, - required String dillPath, - }) { - final result = io.Process.runSync( - dartaotruntime, - [ - frontendServerSnapshot, - '--sdk-root=$flutterPatchedSdk', - '--target=flutter', - '--aot', - '--tfa', - '--packages=$packageConfig', - '--output-dill=$dillPath', - sourcePath, - ], - ); + final dart = io.Platform.resolvedExecutable; + final dartaotruntime = path.join(path.dirname(io.Platform.resolvedExecutable), 'dartaotruntime'); + + void compileAOTDill({required String sourcePath, required String dillPath}) { + final result = io.Process.runSync(dartaotruntime, [ + frontendServerSnapshot, + '--sdk-root=$flutterPatchedSdk', + '--target=flutter', + '--aot', + '--tfa', + '--packages=$packageConfig', + '--output-dill=$dillPath', + sourcePath, + ]); printOnFailure(result.stdout.toString()); printOnFailure(result.stderr.toString()); if (result.exitCode != 0) { @@ -80,24 +57,18 @@ void main() { addTearDown(() => io.File(dillPath).deleteSync()); } - void compileDart2JSDill({ - required String sourcePath, - required String dillPath, - }) { - final result = io.Process.runSync( - dart, - [ - 'compile', - 'js', - '--libraries-spec=$librariesDotJson', - '-Ddart.vm.product=true', - '-o', - dillPath, - '--packages=$packageConfig', - '--cfe-only', - sourcePath, - ], - ); + void compileDart2JSDill({required String sourcePath, required String dillPath}) { + final result = io.Process.runSync(dart, [ + 'compile', + 'js', + '--libraries-spec=$librariesDotJson', + '-Ddart.vm.product=true', + '-o', + dillPath, + '--packages=$packageConfig', + '--cfe-only', + sourcePath, + ]); printOnFailure(result.stdout.toString()); printOnFailure(result.stderr.toString()); if (result.exitCode != 0) { @@ -143,10 +114,10 @@ void main() { 'constantInstances': List constantInstances, 'nonConstantLocations': List nonConstantLocations, } = ConstFinder( - kernelFilePath: dillPath, - classLibraryUri: 'package:const_finder_fixtures/target.dart', - className: 'Target', - ).findInstances(); + kernelFilePath: dillPath, + classLibraryUri: 'package:const_finder_fixtures/target.dart', + className: 'Target', + ).findInstances(); expect( constantInstances, @@ -185,10 +156,10 @@ void main() { 'constantInstances': List constantInstances, 'nonConstantLocations': List nonConstantLocations, } = ConstFinder( - kernelFilePath: dillPath, - classLibraryUri: 'package:const_finder_fixtures/target.dart', - className: 'Target', - ).findInstances(); + kernelFilePath: dillPath, + classLibraryUri: 'package:const_finder_fixtures/target.dart', + className: 'Target', + ).findInstances(); expect( constantInstances, @@ -217,11 +188,7 @@ void main() { ); expect(nonConstantLocations, [ - { - 'file': 'file://$fixturesUrl/pkg/package.dart', - 'line': 14, - 'column': 25, - } + {'file': 'file://$fixturesUrl/pkg/package.dart', 'line': 14, 'column': 25}, ]); }); @@ -234,10 +201,10 @@ void main() { 'constantInstances': List constantInstances, 'nonConstantLocations': List nonConstantLocations, } = ConstFinder( - kernelFilePath: dillPath, - classLibraryUri: 'package:const_finder_fixtures/target.dart', - className: 'Target', - ).findInstances(); + kernelFilePath: dillPath, + classLibraryUri: 'package:const_finder_fixtures/target.dart', + className: 'Target', + ).findInstances(); expect( constantInstances, @@ -252,31 +219,11 @@ void main() { ]), ); expect(nonConstantLocations, [ - { - 'file': 'file://$fixturesUrl/lib/consts_and_non.dart', - 'line': 14, - 'column': 26, - }, - { - 'file': 'file://$fixturesUrl/lib/consts_and_non.dart', - 'line': 16, - 'column': 26, - }, - { - 'file': 'file://$fixturesUrl/lib/consts_and_non.dart', - 'line': 16, - 'column': 41, - }, - { - 'file': 'file://$fixturesUrl/lib/consts_and_non.dart', - 'line': 17, - 'column': 26, - }, - { - 'file': 'file://$fixturesUrl/pkg/package.dart', - 'line': 14, - 'column': 25, - } + {'file': 'file://$fixturesUrl/lib/consts_and_non.dart', 'line': 14, 'column': 26}, + {'file': 'file://$fixturesUrl/lib/consts_and_non.dart', 'line': 16, 'column': 26}, + {'file': 'file://$fixturesUrl/lib/consts_and_non.dart', 'line': 16, 'column': 41}, + {'file': 'file://$fixturesUrl/lib/consts_and_non.dart', 'line': 17, 'column': 26}, + {'file': 'file://$fixturesUrl/pkg/package.dart', 'line': 14, 'column': 25}, ]); }); @@ -289,10 +236,10 @@ void main() { 'constantInstances': List constantInstances, 'nonConstantLocations': List nonConstantLocations, } = ConstFinder( - kernelFilePath: dillPath, - classLibraryUri: 'package:const_finder_fixtures/target.dart', - className: 'Target', - ).findInstances(); + kernelFilePath: dillPath, + classLibraryUri: 'package:const_finder_fixtures/target.dart', + className: 'Target', + ).findInstances(); expect( constantInstances, @@ -309,52 +256,24 @@ void main() { ); expect(nonConstantLocations, [ - { - 'file': 'file://$fixturesUrl/lib/consts_and_non.dart', - 'line': 14, - 'column': 26, - }, - { - 'file': 'file://$fixturesUrl/lib/consts_and_non.dart', - 'line': 16, - 'column': 26, - }, - { - 'file': 'file://$fixturesUrl/lib/consts_and_non.dart', - 'line': 16, - 'column': 41, - }, - { - 'file': 'file://$fixturesUrl/lib/consts_and_non.dart', - 'line': 17, - 'column': 26, - }, - { - 'file': 'file://$fixturesUrl/pkg/package.dart', - 'line': 14, - 'column': 25, - } + {'file': 'file://$fixturesUrl/lib/consts_and_non.dart', 'line': 14, 'column': 26}, + {'file': 'file://$fixturesUrl/lib/consts_and_non.dart', 'line': 16, 'column': 26}, + {'file': 'file://$fixturesUrl/lib/consts_and_non.dart', 'line': 16, 'column': 41}, + {'file': 'file://$fixturesUrl/lib/consts_and_non.dart', 'line': 17, 'column': 26}, + {'file': 'file://$fixturesUrl/pkg/package.dart', 'line': 14, 'column': 25}, ]); }); test('static_icon_provider_frontend (aot)', () { - final sourcePath = path.join( - fixturesPath, - 'lib', - 'static_icon_provider.dart', - ); - final dillPath = path.join( - fixturesPath, - 'static_icon_provider_frontend.dill', - ); + final sourcePath = path.join(fixturesPath, 'lib', 'static_icon_provider.dart'); + final dillPath = path.join(fixturesPath, 'static_icon_provider_frontend.dill'); compileAOTDill(sourcePath: sourcePath, dillPath: dillPath); final finder = ConstFinder( kernelFilePath: dillPath, classLibraryUri: 'package:const_finder_fixtures/target.dart', className: 'Target', annotationClassName: 'StaticIconProvider', - annotationClassLibraryUri: - 'package:const_finder_fixtures/static_icon_provider.dart', + annotationClassLibraryUri: 'package:const_finder_fixtures/static_icon_provider.dart', ); final { @@ -364,16 +283,8 @@ void main() { expect( constantInstances, unorderedEquals([ - { - 'stringValue': 'used1', - 'intValue': 1, - 'targetValue': null, - }, - { - 'stringValue': 'used2', - 'intValue': 2, - 'targetValue': null, - }, + {'stringValue': 'used1', 'intValue': 1, 'targetValue': null}, + {'stringValue': 'used2', 'intValue': 2, 'targetValue': null}, ]), ); @@ -383,23 +294,15 @@ void main() { }); test('static_icon_provider_web (dart2js)', () { - final sourcePath = path.join( - fixturesPath, - 'lib', - 'static_icon_provider.dart', - ); - final dillPath = path.join( - fixturesPath, - 'static_icon_provider_web.dill', - ); + final sourcePath = path.join(fixturesPath, 'lib', 'static_icon_provider.dart'); + final dillPath = path.join(fixturesPath, 'static_icon_provider_web.dill'); compileDart2JSDill(sourcePath: sourcePath, dillPath: dillPath); final finder = ConstFinder( kernelFilePath: dillPath, classLibraryUri: 'package:const_finder_fixtures/target.dart', className: 'Target', annotationClassName: 'StaticIconProvider', - annotationClassLibraryUri: - 'package:const_finder_fixtures/static_icon_provider.dart', + annotationClassLibraryUri: 'package:const_finder_fixtures/static_icon_provider.dart', ); final { @@ -409,16 +312,8 @@ void main() { expect( constantInstances, unorderedEquals([ - { - 'stringValue': 'used1', - 'intValue': 1, - 'targetValue': null, - }, - { - 'stringValue': 'used2', - 'intValue': 2, - 'targetValue': null, - }, + {'stringValue': 'used1', 'intValue': 1, 'targetValue': null}, + {'stringValue': 'used2', 'intValue': 2, 'targetValue': null}, ]), ); diff --git a/tools/const_finder/test/fixtures/lib/consts.dart b/tools/const_finder/test/fixtures/lib/consts.dart index 8270db9b5ab9e..2e4ecd52c6a8b 100644 --- a/tools/const_finder/test/fixtures/lib/consts.dart +++ b/tools/const_finder/test/fixtures/lib/consts.dart @@ -59,7 +59,6 @@ class StaticConstInitializer { Target('104', 104, Target('105', 105, null)), }; - static const Map targetMap = { 0: Target('106', 106, null), 1: Target('107', 107, Target('108', 108, null)), diff --git a/tools/dir_contents_diff/lib/dir_contents_diff.dart b/tools/dir_contents_diff/lib/dir_contents_diff.dart index c9329beaa0b6c..f1cd3177d4405 100644 --- a/tools/dir_contents_diff/lib/dir_contents_diff.dart +++ b/tools/dir_contents_diff/lib/dir_contents_diff.dart @@ -12,15 +12,11 @@ String _basename(String path) { String _generateDirListing(String dirPath) { final Directory dir = Directory(dirPath); final List entities = dir.listSync(); - entities.sort( - (FileSystemEntity a, FileSystemEntity b) => a.path.compareTo(b.path)); - return entities - .map((FileSystemEntity entity) => _basename(entity.path)) - .join('\n'); + entities.sort((FileSystemEntity a, FileSystemEntity b) => a.path.compareTo(b.path)); + return entities.map((FileSystemEntity entity) => _basename(entity.path)).join('\n'); } -String _strReplaceRange( - String inputStr, int start, int end, String replacement) { +String _strReplaceRange(String inputStr, int start, int end, String replacement) { return inputStr.substring(0, start) + replacement + inputStr.substring(end); } @@ -85,8 +81,7 @@ int dirContentsDiff(String goldenPath, String dirPath) { stdoutEncoding: utf8, ); if (diffResult.exitCode != 0) { - print( - 'Unexpected diff in $goldenPath, use `git apply` with the following patch.\n'); + print('Unexpected diff in $goldenPath, use `git apply` with the following patch.\n'); print(_redirectPatch(diffResult.stdout as String)); result = 1; } diff --git a/tools/dir_contents_diff/test/dir_contents_diff_test.dart b/tools/dir_contents_diff/test/dir_contents_diff_test.dart index b8172f839d438..bf3e8732033f7 100644 --- a/tools/dir_contents_diff/test/dir_contents_diff_test.dart +++ b/tools/dir_contents_diff/test/dir_contents_diff_test.dart @@ -10,16 +10,8 @@ import 'package:test/test.dart'; void main() { // Find a path to `dir_contents_diff.dart` from the working directory. - final String pkgPath = p.join( - Engine.findWithin().flutterDir.path, - 'tools', - 'dir_contents_diff', - ); - final String binPath = p.join( - pkgPath, - 'bin', - 'dir_contents_diff.dart', - ); + final String pkgPath = p.join(Engine.findWithin().flutterDir.path, 'tools', 'dir_contents_diff'); + final String binPath = p.join(pkgPath, 'bin', 'dir_contents_diff.dart'); // As a sanity check, ensure that the file exists. if (!io.File(binPath).existsSync()) { @@ -30,10 +22,11 @@ void main() { // Runs `../bin/dir_contents_diff.dart` with the given arguments. (int, String) runSync(String goldenPath, String dirPath) { - final io.ProcessResult result = io.Process.runSync( - io.Platform.resolvedExecutable, - [binPath, goldenPath, dirPath], - ); + final io.ProcessResult result = io.Process.runSync(io.Platform.resolvedExecutable, [ + binPath, + goldenPath, + dirPath, + ]); return (result.exitCode, result.stdout ?? result.stderr); } @@ -49,8 +42,7 @@ void main() { }); test('lists files and diffs successfully, even with an EOF newline', () { - final String goldenPath = - p.join(pkgPath, 'test', 'file_ok_eof_newline.txt'); + final String goldenPath = p.join(pkgPath, 'test', 'file_ok_eof_newline.txt'); final String dirPath = p.join(pkgPath, 'test', 'fixtures'); final (int exitCode, String output) = runSync(goldenPath, dirPath); if (exitCode != 0) { @@ -73,8 +65,7 @@ void main() { }); test('diff fails when an unexpected file is present', () { - final String goldenPath = - p.join(pkgPath, 'test', 'file_bad_unexpected.txt'); + final String goldenPath = p.join(pkgPath, 'test', 'file_bad_unexpected.txt'); final String dirPath = p.join(pkgPath, 'test', 'fixtures'); final (int exitCode, String output) = runSync(goldenPath, dirPath); if (exitCode == 0) { diff --git a/tools/engine_tool/lib/main.dart b/tools/engine_tool/lib/main.dart index e524e07ad0ed5..eebf301f5f401 100644 --- a/tools/engine_tool/lib/main.dart +++ b/tools/engine_tool/lib/main.dart @@ -22,8 +22,7 @@ void main(List args) async { } final bool verbose = args.contains('--verbose') || args.contains('-v'); - final bool help = args.contains('help') || args.contains('--help') || - args.contains('-h'); + final bool help = args.contains('help') || args.contains('--help') || args.contains('-h'); // Find the engine repo. final Engine engine; @@ -36,22 +35,16 @@ void main(List args) async { } // Find and parse the engine build configs. - final io.Directory buildConfigsDir = io.Directory(p.join( - engine.flutterDir.path, - 'ci', - 'builders', - )); - final BuildConfigLoader loader = BuildConfigLoader( - buildConfigsDir: buildConfigsDir, + final io.Directory buildConfigsDir = io.Directory( + p.join(engine.flutterDir.path, 'ci', 'builders'), ); + final BuildConfigLoader loader = BuildConfigLoader(buildConfigsDir: buildConfigsDir); // Treat it as an error if no build configs were found. The caller likely // expected to find some. final Map configs = loader.configs; if (configs.isEmpty) { - io.stderr.writeln( - 'Error: No build configs found under ${buildConfigsDir.path}', - ); + io.stderr.writeln('Error: No build configs found under ${buildConfigsDir.path}'); io.exitCode = 1; return; } @@ -85,9 +78,11 @@ void main(List args) async { try { io.exitCode = await runner.run(args); } on FatalError catch (e, st) { - environment.logger.error('FatalError caught in main. Please file a bug\n' - 'error: $e\n' - 'stack: $st'); + environment.logger.error( + 'FatalError caught in main. Please file a bug\n' + 'error: $e\n' + 'stack: $st', + ); io.exitCode = 1; } return; diff --git a/tools/engine_tool/lib/src/build_plan.dart b/tools/engine_tool/lib/src/build_plan.dart index 3aab6ec0f02b6..90dcb4eb99e25 100644 --- a/tools/engine_tool/lib/src/build_plan.dart +++ b/tools/engine_tool/lib/src/build_plan.dart @@ -38,9 +38,7 @@ final class BuildPlan { }) { final build = () { final name = args.option(_flagConfig) ?? defaultBuild(); - final config = builds.firstWhereOrNull( - (b) => mangleConfigName(environment, b.name) == name, - ); + final config = builds.firstWhereOrNull((b) => mangleConfigName(environment, b.name) == name); if (config == null) { if (name == null) { throw FatalError('No build configuration specified.'); @@ -55,9 +53,7 @@ final class BuildPlan { useRbe: () { final useRbe = args.flag(_flagRbe); if (useRbe && !environment.hasRbeConfigInTree()) { - throw FatalError( - 'RBE requested but configuration not found.\n\n$_rbeInstructions', - ); + throw FatalError('RBE requested but configuration not found.\n\n$_rbeInstructions'); } return useRbe; }(), @@ -94,9 +90,7 @@ final class BuildPlan { required Iterable extraGnArgs, }) : extraGnArgs = List.unmodifiable(extraGnArgs) { if (!useRbe && strategy == BuildStrategy.remote) { - throw FatalError( - 'Cannot use remote builds without RBE enabled.\n\n$_rbeInstructions', - ); + throw FatalError('Cannot use remote builds without RBE enabled.\n\n$_rbeInstructions'); } } @@ -165,17 +159,15 @@ final class BuildPlan { // Add --config. final builds = _extractBuilds( environment.platform, - runnableConfigs: _runnableBuildConfigs( - environment.platform, - configsByName: configs, - ), + runnableConfigs: _runnableBuildConfigs(environment.platform, configsByName: configs), hideCiSpecificBuilds: help && !environment.verbose, ); debugCheckBuilds(builds); parser.addOption( _flagConfig, abbr: 'c', - help: '' + help: + '' 'Selects a build configuration for the current platform.\n' '\n' 'If omitted, et attempts ' @@ -184,25 +176,25 @@ final class BuildPlan { 'suitable build when targeting (via "et run") a flutter app.\n' '\n' '${environment.verbose ? '' - 'Since verbose mode was selected, both local development ' - 'configurations and configurations that are typically only ' - 'used on CI will be visible, including possible duplicates.' : '' - 'Configurations include (use --verbose for more details):'}', - allowed: [ - for (final config in builds) mangleConfigName(environment, config.name), - ]..sort(), - allowedHelp: environment.verbose - ? { - for (final config in builds) - mangleConfigName(environment, config.name): config.description, - } - : null, + 'Since verbose mode was selected, both local development ' + 'configurations and configurations that are typically only ' + 'used on CI will be visible, including possible duplicates.' : '' + 'Configurations include (use --verbose for more details):'}', + allowed: [for (final config in builds) mangleConfigName(environment, config.name)]..sort(), + allowedHelp: + environment.verbose + ? { + for (final config in builds) + mangleConfigName(environment, config.name): config.description, + } + : null, ); // Add --lto. parser.addFlag( _flagLto, - help: '' + help: + '' 'Whether LTO should be enabled for a build.\n' "If omitted, defaults to the configuration's specified value, " 'which is typically (but not always) --no-lto.', @@ -229,24 +221,19 @@ final class BuildPlan { _flagStrategy, defaultsTo: _defaultStrategy.name, allowed: BuildStrategy.values.map((e) => e.name), - allowedHelp: { - for (final e in BuildStrategy.values) e.name: e._help, - }, + allowedHelp: {for (final e in BuildStrategy.values) e.name: e._help}, help: 'How to prefer remote or local builds.', hide: !hasRbeConfigInTree && !environment.verbose, ); // Add --concurrency. - parser.addOption( - _flagConcurrency, - abbr: 'j', - help: 'How many jobs to run in parallel.', - ); + parser.addOption(_flagConcurrency, abbr: 'j', help: 'How many jobs to run in parallel.'); // Add --gn-args. parser.addMultiOption( _flagExtraGnArgs, - help: '' + help: + '' 'Additional arguments to provide to "gn".\n' 'GN arguments change the parameters of the compiler and invalidate ' 'the current build, and should be used sparingly. If there is an ' @@ -267,7 +254,8 @@ final class BuildPlan { /// Whether to configure the build plan to use RBE (remote build execution). final bool useRbe; - static const _rbeInstructions = '' + static const _rbeInstructions = + '' 'Google employees can follow the instructions at ' 'https://flutter.dev/to/engine-rbe to enable RBE, which can ' 'parallelize builds and reduce build times on faster internet ' @@ -317,10 +305,7 @@ final class BuildPlan { case BuildStrategy.auto: return const RbeConfig(); case BuildStrategy.local: - return const RbeConfig( - execStrategy: RbeExecStrategy.local, - remoteDisabled: true, - ); + return const RbeConfig(execStrategy: RbeExecStrategy.local, remoteDisabled: true); case BuildStrategy.remote: return const RbeConfig(execStrategy: RbeExecStrategy.remote); } @@ -328,11 +313,7 @@ final class BuildPlan { /// Converts this build plan into extra GN arguments to pass to the build. List toGnArgs() { - return [ - if (!useRbe) '--no-rbe', - if (useLto) '--lto' else '--no-lto', - ...extraGnArgs, - ]; + return [if (!useRbe) '--no-rbe', if (useLto) '--lto' else '--no-lto', ...extraGnArgs]; } @override @@ -353,9 +334,7 @@ final class BuildPlan { /// User-specified strategy for executing a build. enum BuildStrategy { /// Automatically determine the best build strategy. - auto( - 'Prefer remote builds and fallback silently to local builds.', - ), + auto('Prefer remote builds and fallback silently to local builds.'), /// Build locally. local( @@ -399,16 +378,14 @@ List _extractBuilds( }) { return [ for (final buildConfig in runnableConfigs) - ...buildConfig.value.builds.where( - (build) { - if (!build.canRunOn(platform)) { - return false; - } - if (!hideCiSpecificBuilds) { - return true; - } - return build.name.startsWith(platform.operatingSystem); - }, - ), + ...buildConfig.value.builds.where((build) { + if (!build.canRunOn(platform)) { + return false; + } + if (!hideCiSpecificBuilds) { + return true; + } + return build.name.startsWith(platform.operatingSystem); + }), ]; } diff --git a/tools/engine_tool/lib/src/build_utils.dart b/tools/engine_tool/lib/src/build_utils.dart index 5a99a0f3578c0..f58160fcbea6e 100644 --- a/tools/engine_tool/lib/src/build_utils.dart +++ b/tools/engine_tool/lib/src/build_utils.dart @@ -17,8 +17,7 @@ void debugCheckBuilds(List builds) { final Set names = {}; for (final Build build in builds) { - assert(!names.contains(build.name), - 'More than one build has the name ${build.name}'); + assert(!names.contains(build.name), 'More than one build has the name ${build.name}'); names.add(build.name); } } @@ -86,10 +85,7 @@ Future runBuild( int concurrency = 0, RbeConfig rbeConfig = const RbeConfig(), }) async { - final List gnArgs = [ - if (!enableRbe) '--no-rbe', - ...extraGnArgs, - ]; + final List gnArgs = [if (!enableRbe) '--no-rbe', ...extraGnArgs]; // TODO(loic-sharma): Fetch dependencies if needed. final BuildRunner buildRunner = BuildRunner( @@ -152,10 +148,7 @@ Future ensureBuildDir( }) async { // TODO(matanlurey): https://github.com/flutter/flutter/issues/148442. final io.Directory buildDir = io.Directory( - p.join( - environment.engine.outDir.path, - build.ninja.config, - ), + p.join(environment.engine.outDir.path, build.ninja.config), ); if (buildDir.existsSync()) { return true; @@ -183,10 +176,7 @@ Future _runGn( List extraGnArgs = const [], required bool enableRbe, }) async { - final List gnArgs = [ - if (!enableRbe) '--no-rbe', - ...extraGnArgs, - ]; + final List gnArgs = [if (!enableRbe) '--no-rbe', ...extraGnArgs]; final BuildRunner buildRunner = BuildRunner( platform: environment.platform, diff --git a/tools/engine_tool/lib/src/commands/build_command.dart b/tools/engine_tool/lib/src/commands/build_command.dart index 6094ffd2db0a1..ac8df2658cde6 100644 --- a/tools/engine_tool/lib/src/commands/build_command.dart +++ b/tools/engine_tool/lib/src/commands/build_command.dart @@ -19,12 +19,7 @@ final class BuildCommand extends CommandBase { super.help = false, super.usageLineLength, }) { - builds = BuildPlan.configureArgParser( - argParser, - environment, - configs: configs, - help: help, - ); + builds = BuildPlan.configureArgParser(argParser, environment, configs: configs, help: help); } /// List of compatible builds. @@ -43,19 +38,11 @@ et build //flutter/fml:fml_benchmarks # Build a specific target in `//flutter/f @override Future run() async { - final plan = BuildPlan.fromArgResults( - argResults!, - environment, - builds: builds, - ); + final plan = BuildPlan.fromArgResults(argResults!, environment, builds: builds); final commandLineTargets = argResults!.rest; if (commandLineTargets.isNotEmpty && - !await ensureBuildDir( - environment, - plan.build, - enableRbe: plan.useRbe, - )) { + !await ensureBuildDir(environment, plan.build, enableRbe: plan.useRbe)) { return 1; } @@ -65,10 +52,7 @@ et build //flutter/fml:fml_benchmarks # Build a specific target in `//flutter/f final allTargets =