未验证 提交 477a3359 编写于 作者: H Harry Terkelsen 提交者: GitHub

Move CanvasKit files from parts to libraries (#26139)

上级 3a94c1e5
......@@ -15,9 +15,9 @@ library engine;
// - https://github.com/flutter/engine/blob/master/web_sdk/sdk_rewriter.dart
import 'dart:async';
// Some of these names are used in services/buffers.dart for example.
// ignore: unused_import
import 'dart:collection'
// Some of these names are used in services/buffers.dart for example.
// ignore: unused_shown_name
show ListBase, IterableBase, DoubleLinkedQueue, DoubleLinkedQueueEntry;
import 'dart:convert' hide Codec;
import 'dart:developer' as developer;
......@@ -147,39 +147,74 @@ export 'engine/vector_math.dart';
import 'engine/web_experiments.dart';
export 'engine/web_experiments.dart';
export 'engine/canvaskit/canvas.dart';
import 'engine/canvaskit/canvaskit_api.dart';
export 'engine/canvaskit/canvaskit_api.dart';
export 'engine/canvaskit/canvaskit_canvas.dart';
import 'engine/canvaskit/color_filter.dart';
export 'engine/canvaskit/color_filter.dart';
import 'engine/canvaskit/embedded_views.dart';
export 'engine/canvaskit/embedded_views.dart';
export 'engine/canvaskit/fonts.dart';
export 'engine/canvaskit/font_fallbacks.dart';
export 'engine/canvaskit/image.dart';
export 'engine/canvaskit/image_filter.dart';
import 'engine/canvaskit/initialization.dart';
export 'engine/canvaskit/initialization.dart';
export 'engine/canvaskit/interval_tree.dart';
import 'engine/canvaskit/layer.dart';
export 'engine/canvaskit/layer.dart';
import 'engine/canvaskit/layer_scene_builder.dart';
export 'engine/canvaskit/layer_scene_builder.dart';
export 'engine/canvaskit/layer_tree.dart';
export 'engine/canvaskit/mask_filter.dart';
export 'engine/canvaskit/n_way_canvas.dart';
export 'engine/canvaskit/painting.dart';
export 'engine/canvaskit/path.dart';
export 'engine/canvaskit/path_metrics.dart';
export 'engine/canvaskit/picture.dart';
export 'engine/canvaskit/picture_recorder.dart';
import 'engine/canvaskit/rasterizer.dart';
export 'engine/canvaskit/rasterizer.dart';
export 'engine/canvaskit/raster_cache.dart';
export 'engine/canvaskit/shader.dart';
export 'engine/canvaskit/skia_object_cache.dart';
import 'engine/canvaskit/surface.dart';
export 'engine/canvaskit/surface.dart';
export 'engine/canvaskit/text.dart';
export 'engine/canvaskit/util.dart';
export 'engine/canvaskit/vertices.dart';
part 'engine/assets.dart';
part 'engine/html/bitmap_canvas.dart';
part 'engine/canvaskit/canvas.dart';
part 'engine/canvaskit/canvaskit_canvas.dart';
part 'engine/canvaskit/canvaskit_api.dart';
part 'engine/canvaskit/color_filter.dart';
part 'engine/canvaskit/embedded_views.dart';
part 'engine/canvaskit/fonts.dart';
part 'engine/canvaskit/font_fallbacks.dart';
part 'engine/canvaskit/image.dart';
part 'engine/canvaskit/image_filter.dart';
part 'engine/canvaskit/initialization.dart';
part 'engine/canvaskit/interval_tree.dart';
part 'engine/canvaskit/layer.dart';
part 'engine/canvaskit/layer_scene_builder.dart';
part 'engine/canvaskit/layer_tree.dart';
part 'engine/canvaskit/mask_filter.dart';
part 'engine/canvaskit/n_way_canvas.dart';
part 'engine/canvaskit/path.dart';
part 'engine/canvaskit/painting.dart';
part 'engine/canvaskit/path_metrics.dart';
part 'engine/canvaskit/picture.dart';
part 'engine/canvaskit/picture_recorder.dart';
part 'engine/canvaskit/platform_message.dart';
part 'engine/canvaskit/raster_cache.dart';
part 'engine/canvaskit/rasterizer.dart';
part 'engine/canvaskit/shader.dart';
part 'engine/canvaskit/skia_object_cache.dart';
part 'engine/canvaskit/surface.dart';
part 'engine/canvaskit/text.dart';
part 'engine/canvaskit/util.dart';
part 'engine/canvaskit/vertices.dart';
part 'engine/canvaskit/viewport_metrics.dart';
part 'engine/canvas_pool.dart';
part 'engine/clipboard.dart';
part 'engine/color_filter.dart';
......@@ -251,8 +286,11 @@ const bool kReleaseMode =
const bool kProfileMode =
bool.fromEnvironment('dart.vm.profile', defaultValue: false);
const bool kDebugMode = !kReleaseMode && !kProfileMode;
String get buildMode =>
kReleaseMode ? 'release' : kProfileMode ? 'profile' : 'debug';
String get buildMode => kReleaseMode
? 'release'
: kProfileMode
? 'profile'
: 'debug';
/// A benchmark metric that includes frame-related computations prior to
/// submitting layer and picture operations to the underlying renderer, such as
......@@ -370,7 +408,7 @@ void _addUrlStrategyListener() {
});
}
class _NullTreeSanitizer implements html.NodeTreeSanitizer {
class NullTreeSanitizer implements html.NodeTreeSanitizer {
@override
void sanitizeTree(html.Node node) {}
}
......
......@@ -2,7 +2,20 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'dart:math' as math;
import 'dart:typed_data';
import 'package:ui/ui.dart' as ui;
import 'canvaskit_api.dart';
import 'image.dart';
import 'image_filter.dart';
import 'painting.dart';
import 'path.dart';
import 'picture.dart';
import 'text.dart';
import 'util.dart';
import 'vertices.dart';
/// Memoized value for ClipOp.Intersect, so we don't have to hit JS-interop
/// every time we need it.
......@@ -158,7 +171,8 @@ class CkCanvas {
}
}
void drawImageNine(CkImage image, ui.Rect center, ui.Rect dst, CkPaint paint) {
void drawImageNine(
CkImage image, ui.Rect center, ui.Rect dst, CkPaint paint) {
skCanvas.drawImageNine(
image.skImage,
toSkRect(center),
......@@ -268,13 +282,14 @@ class CkCanvas {
skCanvas.saveLayer(paint?.skiaObject, null, null, null);
}
void saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter, [ CkPaint? paint ]) {
final _CkManagedSkImageFilterConvertible convertible =
filter as _CkManagedSkImageFilterConvertible;
void saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter,
[CkPaint? paint]) {
final CkManagedSkImageFilterConvertible convertible =
filter as CkManagedSkImageFilterConvertible;
return skCanvas.saveLayer(
paint?.skiaObject,
toSkRect(bounds),
convertible._imageFilter.skiaObject,
convertible.imageFilter.skiaObject,
0,
);
}
......@@ -504,7 +519,8 @@ class RecordingCkCanvas extends CkCanvas {
}
@override
void saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter, [ CkPaint? paint ]) {
void saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter,
[CkPaint? paint]) {
super.saveLayerWithFilter(bounds, filter, paint);
_addCommand(CkSaveLayerWithFilterCommand(bounds, filter, paint));
}
......@@ -1128,12 +1144,12 @@ class CkSaveLayerWithFilterCommand extends CkPaintCommand {
@override
void apply(SkCanvas canvas) {
final _CkManagedSkImageFilterConvertible convertible =
filter as _CkManagedSkImageFilterConvertible;
final CkManagedSkImageFilterConvertible convertible =
filter as CkManagedSkImageFilterConvertible;
return canvas.saveLayer(
paint?.skiaObject,
toSkRect(bounds),
convertible._imageFilter.skiaObject,
convertible.imageFilter.skiaObject,
0,
);
}
......
......@@ -6,8 +6,17 @@
///
/// Prefer keeping the original CanvasKit names so it is easier to locate
/// the API behind these bindings in the Skia source code.
@JS()
library canvaskit_api;
import 'dart:async';
import 'dart:html' as html;
import 'dart:js' as js;
import 'dart:typed_data';
part of engine;
import 'package:js/js.dart';
import 'package:ui/src/engine.dart';
import 'package:ui/ui.dart' as ui;
/// Entrypoint into the CanvasKit API.
late CanvasKit canvasKit;
......
......@@ -2,7 +2,22 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'dart:typed_data';
import 'package:ui/src/engine.dart' show toMatrix32;
import 'package:ui/ui.dart' as ui;
import 'canvas.dart';
import 'canvaskit_api.dart';
import 'image.dart';
import 'painting.dart';
import 'path.dart';
import 'picture.dart';
import 'picture_recorder.dart';
import 'text.dart';
import 'vertices.dart';
import '../validators.dart';
/// An implementation of [ui.Canvas] that is backed by a CanvasKit canvas.
class CanvasKitCanvas implements ui.Canvas {
......
......@@ -2,18 +2,25 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'dart:typed_data';
import 'package:ui/src/engine.dart' show listEquals, EngineColorFilter;
import 'package:ui/ui.dart' as ui;
import 'canvaskit_api.dart';
import 'image_filter.dart';
import 'skia_object_cache.dart';
/// A concrete [ManagedSkiaObject] subclass that owns a [SkColorFilter] and
/// manages its lifecycle.
///
/// Seealso:
///
/// * [CkPaint.colorFilter], which uses a [_ManagedSkColorFilter] to manage
/// * [CkPaint.colorFilter], which uses a [ManagedSkColorFilter] to manage
/// the lifecycle of its [SkColorFilter].
class _ManagedSkColorFilter extends ManagedSkiaObject<SkColorFilter> {
_ManagedSkColorFilter(CkColorFilter ckColorFilter)
: this.ckColorFilter = ckColorFilter;
class ManagedSkColorFilter extends ManagedSkiaObject<SkColorFilter> {
ManagedSkColorFilter(CkColorFilter ckColorFilter)
: this.ckColorFilter = ckColorFilter;
final CkColorFilter ckColorFilter;
......@@ -33,10 +40,11 @@ class _ManagedSkColorFilter extends ManagedSkiaObject<SkColorFilter> {
@override
bool operator ==(Object other) {
if (runtimeType != other.runtimeType)
if (runtimeType != other.runtimeType) {
return false;
return other is _ManagedSkColorFilter
&& other.ckColorFilter == ckColorFilter;
}
return other is ManagedSkColorFilter &&
other.ckColorFilter == ckColorFilter;
}
@override
......@@ -47,24 +55,29 @@ class _ManagedSkColorFilter extends ManagedSkiaObject<SkColorFilter> {
///
/// Additionally, this class provides the interface for converting itself to a
/// [ManagedSkiaObject] that manages a skia image filter.
abstract class CkColorFilter implements _CkManagedSkImageFilterConvertible<SkImageFilter>, EngineColorFilter {
abstract class CkColorFilter
implements
CkManagedSkImageFilterConvertible<SkImageFilter>,
EngineColorFilter {
const CkColorFilter();
/// Called by [ManagedSkiaObject.createDefault] and
/// [ManagedSkiaObject.resurrect] to create a new [SKImageFilter], when this
/// filter is used as an [ImageFilter].
SkImageFilter _initRawImageFilter() => canvasKit.ImageFilter.MakeColorFilter(_initRawColorFilter(), null);
SkImageFilter initRawImageFilter() =>
canvasKit.ImageFilter.MakeColorFilter(_initRawColorFilter(), null);
/// Called by [ManagedSkiaObject.createDefault] and
/// [ManagedSkiaObject.resurrect] to create a new [SKColorFilter], when this
/// filter is used as a [ColorFilter].
SkColorFilter _initRawColorFilter();
ManagedSkiaObject<SkImageFilter> get _imageFilter => _CkColorFilterImageFilter(colorFilter: this);
ManagedSkiaObject<SkImageFilter> get imageFilter =>
CkColorFilterImageFilter(colorFilter: this);
}
class _CkBlendModeColorFilter extends CkColorFilter {
const _CkBlendModeColorFilter(this.color, this.blendMode);
class CkBlendModeColorFilter extends CkColorFilter {
const CkBlendModeColorFilter(this.color, this.blendMode);
final ui.Color color;
final ui.BlendMode blendMode;
......@@ -82,28 +95,30 @@ class _CkBlendModeColorFilter extends CkColorFilter {
@override
bool operator ==(Object other) {
if (runtimeType != other.runtimeType)
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
String toString() => 'ColorFilter.mode($color, $blendMode)';
}
class _CkMatrixColorFilter extends CkColorFilter {
const _CkMatrixColorFilter(this.matrix);
class CkMatrixColorFilter extends CkColorFilter {
const CkMatrixColorFilter(this.matrix);
final List<double> matrix;
@override
SkColorFilter _initRawColorFilter() {
assert(this.matrix.length == 20, 'Color Matrix must have 20 entries.');
assert(this.matrix.length == 20, 'Color Matrix must have 20 entries.');
final List<double> matrix = this.matrix;
if (matrix is Float32List)
if (matrix is Float32List) {
return canvasKit.ColorFilter.MakeMatrix(matrix);
}
final Float32List float32Matrix = Float32List(20);
for (int i = 0; i < 20; i++) {
float32Matrix[i] = matrix[i];
......@@ -116,19 +131,20 @@ class _CkMatrixColorFilter extends CkColorFilter {
@override
bool operator ==(Object other) {
return runtimeType == other.runtimeType
&& other is _CkMatrixColorFilter
&& _listEquals<double>(matrix, other.matrix);
return runtimeType == other.runtimeType &&
other is CkMatrixColorFilter &&
listEquals<double>(matrix, other.matrix);
}
@override
String toString() => 'ColorFilter.matrix($matrix)';
}
class _CkLinearToSrgbGammaColorFilter extends CkColorFilter {
const _CkLinearToSrgbGammaColorFilter();
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;
......@@ -137,10 +153,11 @@ class _CkLinearToSrgbGammaColorFilter extends CkColorFilter {
String toString() => 'ColorFilter.linearToSrgbGamma()';
}
class _CkSrgbToLinearGammaColorFilter extends CkColorFilter {
const _CkSrgbToLinearGammaColorFilter();
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;
......
......@@ -2,7 +2,21 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'dart:html' as html;
import 'dart:typed_data';
import 'package:ui/src/engine.dart' show window, NullTreeSanitizer;
import 'package:ui/ui.dart' as ui;
import '../html/path_to_svg_clip.dart';
import '../services.dart';
import '../util.dart';
import '../vector_math.dart';
import 'canvas.dart';
import 'initialization.dart';
import 'path.dart';
import 'picture_recorder.dart';
import 'surface.dart';
/// This composites HTML views into the [ui.Scene].
class HtmlViewEmbedder {
......@@ -130,11 +144,11 @@ class HtmlViewEmbedder {
callback(codec.encodeSuccessEnvelope(null));
}
List<CkCanvas?> getCurrentCanvases() {
final List<CkCanvas?> canvases = <CkCanvas?>[];
List<CkCanvas> getCurrentCanvases() {
final List<CkCanvas> canvases = <CkCanvas>[];
for (int i = 0; i < _compositionOrder.length; i++) {
final int viewId = _compositionOrder[i];
canvases.add(_pictureRecorders[viewId]!.recordingCanvas);
canvases.add(_pictureRecorders[viewId]!.recordingCanvas!);
}
return canvases;
}
......@@ -268,7 +282,7 @@ class HtmlViewEmbedder {
'<clipPath id="svgClip$_clipPathCount">'
'<path d="${path.toSvgString()}">'
'</path></clipPath>',
treeSanitizer: _NullTreeSanitizer(),
treeSanitizer: NullTreeSanitizer(),
);
pathDefs.append(newClipPath);
clipView.style.clipPath = 'url(#svgClip$_clipPathCount)';
......@@ -282,7 +296,7 @@ class HtmlViewEmbedder {
'<clipPath id="svgClip$_clipPathCount">'
'<path d="${path.toSvgString()}">'
'</path></clipPath>',
treeSanitizer: _NullTreeSanitizer(),
treeSanitizer: NullTreeSanitizer(),
);
pathDefs.append(newClipPath);
clipView.style.clipPath = 'url(#svgClip$_clipPathCount)';
......@@ -304,7 +318,8 @@ class HtmlViewEmbedder {
// pixels, so scale down the head element to match the logical resolution.
final double scale = window.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);
}
......@@ -330,7 +345,7 @@ class HtmlViewEmbedder {
}
_svgPathDefs = html.Element.html(
'$kSvgResourceHeader<defs id="sk_path_defs"></defs></svg>',
treeSanitizer: _NullTreeSanitizer(),
treeSanitizer: NullTreeSanitizer(),
);
skiaSceneHost!.append(_svgPathDefs!);
}
......@@ -341,8 +356,7 @@ class HtmlViewEmbedder {
for (int i = 0; i < _compositionOrder.length; i++) {
int viewId = _compositionOrder[i];
_ensureOverlayInitialized(viewId);
final SurfaceFrame frame =
_overlays[viewId]!.acquireFrame(_frameSize);
final SurfaceFrame frame = _overlays[viewId]!.acquireFrame(_frameSize);
final CkCanvas canvas = frame.skiaCanvas;
canvas.drawPicture(
_pictureRecorders[viewId]!.endRecording(),
......@@ -350,7 +364,7 @@ class HtmlViewEmbedder {
frame.submit();
}
_pictureRecorders.clear();
if (_listEquals(_compositionOrder, _activeCompositionOrder)) {
if (listEquals(_compositionOrder, _activeCompositionOrder)) {
_compositionOrder.clear();
return;
}
......@@ -628,7 +642,7 @@ class MutatorsStack extends Iterable<Mutator> {
return true;
}
return other is MutatorsStack &&
_listEquals<Mutator>(other._mutators, _mutators);
listEquals<Mutator>(other._mutators, _mutators);
}
int get hashCode => ui.hashList(_mutators);
......
......@@ -2,7 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'dart:async';
import 'dart:convert';
import 'dart:html' as html;
import 'dart:typed_data';
import 'package:ui/src/engine.dart' show sendFontChangeMessage;
import 'package:ui/ui.dart' as ui;
import '../util.dart';
import 'canvaskit_api.dart';
import 'fonts.dart';
import 'initialization.dart';
import 'interval_tree.dart';
/// Global static font fallback data.
class FontFallbackData {
......@@ -52,7 +64,7 @@ class FontFallbackData {
}
/// Fallback fonts which have been registered and loaded.
final List<_RegisteredFont> registeredFallbackFonts = <_RegisteredFont>[];
final List<RegisteredFont> registeredFallbackFonts = <RegisteredFont>[];
final List<String> globalFontFallbacks = <String>['Roboto'];
......@@ -69,8 +81,7 @@ class FontFallbackData {
int fontFallbackTag = fontFallbackCounts[family]!;
fontFallbackCounts[family] = fontFallbackCounts[family]! + 1;
String countedFamily = '$family $fontFallbackTag';
registeredFallbackFonts
.add(_RegisteredFont(bytes, countedFamily, typeface));
registeredFallbackFonts.add(RegisteredFont(bytes, countedFamily, typeface));
globalFontFallbacks.add(countedFamily);
}
}
......
......@@ -2,7 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'dart:convert';
import 'dart:html' as html;
import 'dart:typed_data';
import 'package:ui/src/engine.dart' show AssetManager, AssetManagerException;
import '../util.dart';
import 'canvaskit_api.dart';
import 'font_fallbacks.dart';
// This URL was found by using the Google Fonts Developer API to find the URL
// for Roboto. The API warns that this URL is not stable. In order to update
......@@ -15,11 +23,11 @@ const String _robotoUrl =
/// Manages the fonts used in the Skia-based backend.
class SkiaFontCollection {
/// Fonts that have been registered but haven't been loaded yet.
final List<Future<_RegisteredFont?>> _unloadedFonts =
<Future<_RegisteredFont?>>[];
final List<Future<RegisteredFont?>> _unloadedFonts =
<Future<RegisteredFont?>>[];
/// Fonts which have been registered and loaded.
final List<_RegisteredFont> _registeredFonts = <_RegisteredFont>[];
final List<RegisteredFont> _registeredFonts = <RegisteredFont>[];
final Map<String, List<SkTypeface>> familyToTypefaceMap =
<String, List<SkTypeface>>{};
......@@ -55,9 +63,8 @@ class SkiaFontCollection {
if (_unloadedFonts.isEmpty) {
return;
}
final List<_RegisteredFont?> loadedFonts =
await Future.wait(_unloadedFonts);
for (_RegisteredFont? font in loadedFonts) {
final List<RegisteredFont?> loadedFonts = await Future.wait(_unloadedFonts);
for (RegisteredFont? font in loadedFonts) {
if (font != null) {
_registeredFonts.add(font);
}
......@@ -77,7 +84,7 @@ class SkiaFontCollection {
final SkTypeface? typeface =
canvasKit.FontMgr.RefDefault().MakeTypefaceFromData(list);
if (typeface != null) {
_registeredFonts.add(_RegisteredFont(list, fontFamily, typeface));
_registeredFonts.add(RegisteredFont(list, fontFamily, typeface));
await ensureFontsLoaded();
} else {
printWarning('Failed to parse font family "$fontFamily"');
......@@ -134,7 +141,7 @@ class SkiaFontCollection {
}
}
Future<_RegisteredFont?> _registerFont(String url, String family) async {
Future<RegisteredFont?> _registerFont(String url, String family) async {
ByteBuffer buffer;
try {
buffer = await html.window.fetch(url).then(_getArrayBuffer);
......@@ -148,7 +155,7 @@ class SkiaFontCollection {
SkTypeface? typeface =
canvasKit.FontMgr.RefDefault().MakeTypefaceFromData(bytes);
if (typeface != null) {
return _RegisteredFont(bytes, family, typeface);
return RegisteredFont(bytes, family, typeface);
} else {
printWarning('Failed to load font $family at $url');
printWarning('Verify that $url contains a valid font.');
......@@ -175,7 +182,7 @@ class SkiaFontCollection {
}
/// Represents a font that has been registered.
class _RegisteredFont {
class RegisteredFont {
/// The font family name for this font.
final String family;
......@@ -187,7 +194,7 @@ class _RegisteredFont {
/// This is used to determine which code points are supported by this font.
final SkTypeface typeface;
_RegisteredFont(this.bytes, this.family, this.typeface) {
RegisteredFont(this.bytes, this.family, this.typeface) {
// This is a hack which causes Skia to cache the decoded font.
SkFont skFont = SkFont(typeface);
skFont.getGlyphBounds([0], null, null);
......
......@@ -2,7 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'dart:async';
import 'dart:html' as html;
import 'dart:typed_data';
import 'package:ui/src/engine.dart' show WebOnlyImageCodecChunkCallback;
import 'package:ui/ui.dart' as ui;
import '../util.dart';
import 'canvaskit_api.dart';
import 'skia_object_cache.dart';
/// Instantiates a [ui.Codec] backed by an `SkAnimatedImage` from Skia.
ui.Codec skiaInstantiateImageCodec(Uint8List list,
......
......@@ -2,7 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'package:ui/ui.dart' as ui;
import 'canvaskit_api.dart';
import 'color_filter.dart';
import 'skia_object_cache.dart';
/// An [ImageFilter] that can create a managed skia [SkImageFilter] object.
///
......@@ -11,21 +15,27 @@ part of engine;
/// whenever possible.
///
/// Currently implemented by [CkImageFilter] and [CkColorFilter].
abstract class _CkManagedSkImageFilterConvertible<T extends Object> implements ui.ImageFilter {
ManagedSkiaObject<SkImageFilter> get _imageFilter;
abstract class CkManagedSkImageFilterConvertible<T extends Object>
implements ui.ImageFilter {
ManagedSkiaObject<SkImageFilter> get imageFilter;
}
/// The CanvasKit implementation of [ui.ImageFilter].
///
/// Currently only supports `blur`.
abstract class CkImageFilter extends ManagedSkiaObject<SkImageFilter> implements _CkManagedSkImageFilterConvertible<SkImageFilter> {
factory CkImageFilter.blur({ required double sigmaX, required double sigmaY, required ui.TileMode tileMode }) = _CkBlurImageFilter;
factory CkImageFilter.color({ required CkColorFilter colorFilter }) = _CkColorFilterImageFilter;
abstract class CkImageFilter extends ManagedSkiaObject<SkImageFilter>
implements CkManagedSkImageFilterConvertible<SkImageFilter> {
factory CkImageFilter.blur(
{required double sigmaX,
required double sigmaY,
required ui.TileMode tileMode}) = _CkBlurImageFilter;
factory CkImageFilter.color({required CkColorFilter colorFilter}) =
CkColorFilterImageFilter;
CkImageFilter._();
@override
ManagedSkiaObject<SkImageFilter> get _imageFilter => this;
ManagedSkiaObject<SkImageFilter> get imageFilter => this;
SkImageFilter _initSkiaObject();
......@@ -41,23 +51,24 @@ abstract class CkImageFilter extends ManagedSkiaObject<SkImageFilter> implements
}
}
class _CkColorFilterImageFilter extends CkImageFilter {
_CkColorFilterImageFilter({ required this.colorFilter }) : super._();
class CkColorFilterImageFilter extends CkImageFilter {
CkColorFilterImageFilter({required this.colorFilter}) : super._();
final CkColorFilter colorFilter;
@override
SkImageFilter _initSkiaObject() => colorFilter._initRawImageFilter();
SkImageFilter _initSkiaObject() => colorFilter.initRawImageFilter();
@override
int get hashCode => colorFilter.hashCode;
@override
bool operator ==(Object other) {
if (runtimeType != other.runtimeType)
if (runtimeType != other.runtimeType) {
return false;
return other is _CkColorFilterImageFilter
&& other.colorFilter == colorFilter;
}
return other is CkColorFilterImageFilter &&
other.colorFilter == colorFilter;
}
@override
......@@ -65,7 +76,9 @@ 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;
......@@ -73,10 +86,14 @@ class _CkBlurImageFilter extends CkImageFilter {
String get _modeString {
switch (tileMode) {
case ui.TileMode.clamp: return 'clamp';
case ui.TileMode.mirror: return 'mirror';
case ui.TileMode.repeated: return 'repeated';
case ui.TileMode.decal: return 'decal';
case ui.TileMode.clamp:
return 'clamp';
case ui.TileMode.mirror:
return 'mirror';
case ui.TileMode.repeated:
return 'repeated';
case ui.TileMode.decal:
return 'decal';
}
}
......@@ -92,12 +109,13 @@ class _CkBlurImageFilter extends CkImageFilter {
@override
bool operator ==(Object other) {
if (runtimeType != other.runtimeType)
if (runtimeType != other.runtimeType) {
return false;
return other is _CkBlurImageFilter
&& other.sigmaX == sigmaX
&& other.sigmaY == sigmaY
&& other.tileMode == tileMode;
}
return other is _CkBlurImageFilter &&
other.sigmaX == sigmaX &&
other.sigmaY == sigmaY &&
other.tileMode == tileMode;
}
@override
......@@ -108,4 +126,3 @@ class _CkBlurImageFilter extends CkImageFilter {
return 'ImageFilter.blur($sigmaX, $sigmaY, $_modeString)';
}
}
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@JS()
library canvaskit_initialization;
part of engine;
import 'dart:async';
import 'dart:html' as html;
import 'dart:js' as js;
import 'package:js/js.dart';
import 'package:ui/src/engine.dart' show isDesktop, kProfileMode, domRenderer;
import 'canvaskit_api.dart';
import 'fonts.dart';
/// A JavaScript entrypoint that allows developer to set rendering backend
/// at runtime before launching the application.
......@@ -10,7 +20,7 @@ part of engine;
external String? get requestedRendererType;
/// Whether to use CanvasKit as the rendering backend.
bool get useCanvasKit => _autoDetect ? _detectRenderer() : _useSkia;
bool get useCanvasKit => flutterWebAutoDetect ? _detectRenderer() : _useSkia;
/// Returns true if CanvasKit is used.
///
......@@ -28,7 +38,7 @@ bool _detectRenderer() {
///
/// Using flutter tools option "--web-render=auto" or not specifying one
/// would set the value to true. Otherwise, it would be false.
const bool _autoDetect =
const bool flutterWebAutoDetect =
bool.fromEnvironment('FLUTTER_WEB_AUTO_DETECT', defaultValue: true);
/// Enable the Skia-based rendering backend.
......
......@@ -2,7 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'dart:math' as math;
import 'font_fallbacks.dart' show CodeunitRange;
/// A tree which stores a set of intervals that can be queried for intersection.
class IntervalTree<T> {
......
......@@ -2,7 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'package:ui/src/engine.dart' show transformRect, Matrix4;
import 'package:ui/ui.dart' as ui;
import 'canvas.dart';
import 'embedded_views.dart';
import 'n_way_canvas.dart';
import 'painting.dart';
import 'path.dart';
import 'picture.dart';
import 'raster_cache.dart';
import 'util.dart';
/// A layer to be composed into a scene.
///
......@@ -149,7 +159,8 @@ class RootLayer extends ContainerLayer {
}
}
class BackdropFilterEngineLayer extends ContainerLayer implements ui.BackdropFilterEngineLayer {
class BackdropFilterEngineLayer extends ContainerLayer
implements ui.BackdropFilterEngineLayer {
final ui.ImageFilter _filter;
final ui.BlendMode _blendMode;
......@@ -164,14 +175,16 @@ class BackdropFilterEngineLayer extends ContainerLayer implements ui.BackdropFil
@override
void paint(PaintContext context) {
CkPaint paint = CkPaint()..blendMode = _blendMode;
context.internalNodesCanvas.saveLayerWithFilter(paintBounds, _filter, paint);
context.internalNodesCanvas
.saveLayerWithFilter(paintBounds, _filter, paint);
paintChildren(context);
context.internalNodesCanvas.restore();
}
}
/// A layer that clips its child layers by a given [Path].
class ClipPathEngineLayer extends ContainerLayer implements ui.ClipPathEngineLayer {
class ClipPathEngineLayer extends ContainerLayer
implements ui.ClipPathEngineLayer {
/// The path used to clip child layers.
final CkPath _clipPath;
final ui.Clip _clipBehavior;
......@@ -210,7 +223,8 @@ class ClipPathEngineLayer extends ContainerLayer implements ui.ClipPathEngineLay
}
/// A layer that clips its child layers by a given [Rect].
class ClipRectEngineLayer extends ContainerLayer implements ui.ClipRectEngineLayer {
class ClipRectEngineLayer extends ContainerLayer
implements ui.ClipRectEngineLayer {
/// The rectangle used to clip child layers.
final ui.Rect _clipRect;
final ui.Clip _clipBehavior;
......@@ -250,7 +264,8 @@ class ClipRectEngineLayer extends ContainerLayer implements ui.ClipRectEngineLay
}
/// A layer that clips its child layers by a given [RRect].
class ClipRRectEngineLayer extends ContainerLayer implements ui.ClipRRectEngineLayer {
class ClipRRectEngineLayer extends ContainerLayer
implements ui.ClipRRectEngineLayer {
/// The rounded rectangle used to clip child layers.
final ui.RRect _clipRRect;
final ui.Clip? _clipBehavior;
......@@ -287,7 +302,8 @@ class ClipRRectEngineLayer extends ContainerLayer implements ui.ClipRRectEngineL
}
/// A layer that paints its children with the given opacity.
class OpacityEngineLayer extends ContainerLayer implements ui.OpacityEngineLayer {
class OpacityEngineLayer extends ContainerLayer
implements ui.OpacityEngineLayer {
final int _alpha;
final ui.Offset _offset;
......@@ -327,7 +343,8 @@ class OpacityEngineLayer extends ContainerLayer implements ui.OpacityEngineLayer
}
/// 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 {
/// The matrix with which to transform the child layers.
final Matrix4 _transform;
......@@ -358,12 +375,15 @@ class TransformEngineLayer extends ContainerLayer implements ui.TransformEngineL
/// 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));
}
/// 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);
final ui.ImageFilter _filter;
......@@ -379,8 +399,10 @@ class ImageFilterEngineLayer extends ContainerLayer implements ui.ImageFilterEng
}
}
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;
......@@ -467,7 +489,8 @@ class PhysicalShapeEngineLayer extends ContainerLayer
@override
void preroll(PrerollContext prerollContext, Matrix4 matrix) {
prerollChildren(prerollContext, matrix);
paintBounds = computeSkShadowBounds(_path, _elevation, ui.window.devicePixelRatio, matrix);
paintBounds = computeSkShadowBounds(
_path, _elevation, ui.window.devicePixelRatio, matrix);
}
@override
......@@ -524,7 +547,8 @@ class PhysicalShapeEngineLayer extends ContainerLayer
}
/// 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;
......
......@@ -2,7 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'dart:typed_data';
import 'package:ui/src/engine.dart' show toMatrix32, Matrix4;
import 'package:ui/ui.dart' as ui;
import 'layer.dart';
import 'layer_tree.dart';
import 'picture.dart';
import 'path.dart';
class LayerScene implements ui.Scene {
final LayerTree layerTree;
......@@ -115,7 +123,8 @@ class LayerSceneBuilder implements ui.SceneBuilder {
ui.Clip clipBehavior = ui.Clip.antiAlias,
ui.EngineLayer? oldLayer,
}) {
return pushLayer<ClipPathEngineLayer>(ClipPathEngineLayer(path as CkPath, clipBehavior));
return pushLayer<ClipPathEngineLayer>(
ClipPathEngineLayer(path as CkPath, clipBehavior));
}
@override
......@@ -124,7 +133,8 @@ class LayerSceneBuilder implements ui.SceneBuilder {
ui.Clip? clipBehavior,
ui.EngineLayer? oldLayer,
}) {
return pushLayer<ClipRRectEngineLayer>(ClipRRectEngineLayer(rrect, clipBehavior));
return pushLayer<ClipRRectEngineLayer>(
ClipRRectEngineLayer(rrect, clipBehavior));
}
@override
......@@ -133,7 +143,8 @@ class LayerSceneBuilder implements ui.SceneBuilder {
ui.Clip clipBehavior = ui.Clip.antiAlias,
ui.EngineLayer? oldLayer,
}) {
return pushLayer<ClipRectEngineLayer>(ClipRectEngineLayer(rect, clipBehavior));
return pushLayer<ClipRectEngineLayer>(
ClipRectEngineLayer(rect, clipBehavior));
}
@override
......@@ -197,8 +208,8 @@ class LayerSceneBuilder implements ui.SceneBuilder {
ui.EngineLayer? oldLayer,
ui.FilterQuality filterQuality = ui.FilterQuality.low,
}) {
return pushLayer<ShaderMaskEngineLayer>(ShaderMaskEngineLayer(
shader, maskRect, blendMode, filterQuality));
return pushLayer<ShaderMaskEngineLayer>(
ShaderMaskEngineLayer(shader, maskRect, blendMode, filterQuality));
}
@override
......
......@@ -2,7 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'package:ui/src/engine.dart' show timeAction;
import 'package:ui/ui.dart' as ui;
import '../../engine.dart' show kProfilePrerollFrame, kProfileApplyFrame;
import '../vector_math.dart';
import 'canvas.dart';
import 'embedded_views.dart';
import 'layer.dart';
import 'n_way_canvas.dart';
import 'picture_recorder.dart';
import 'raster_cache.dart';
/// A tree of [Layer]s that, together with a [Size] compose a frame.
class LayerTree {
......@@ -38,7 +48,7 @@ class LayerTree {
void paint(Frame frame, {bool ignoreRasterCache = false}) {
final CkNWayCanvas internalNodesCanvas = CkNWayCanvas();
internalNodesCanvas.addCanvas(frame.canvas);
final List<CkCanvas?> overlayCanvases =
final List<CkCanvas> overlayCanvases =
frame.viewEmbedder!.getCurrentCanvases();
for (int i = 0; i < overlayCanvases.length; i++) {
internalNodesCanvas.addCanvas(overlayCanvases[i]);
......
......@@ -2,7 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'canvaskit_api.dart';
import 'skia_object_cache.dart';
import 'package:ui/ui.dart' as ui;
/// The CanvasKit implementation of [ui.MaskFilter].
class CkMaskFilter extends ManagedSkiaObject<SkMaskFilter> {
......
......@@ -2,14 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'dart:typed_data';
import 'package:ui/ui.dart' as ui;
import 'canvas.dart';
import 'painting.dart';
import 'path.dart';
/// A virtual canvas that applies operations to multiple canvases at once.
class CkNWayCanvas {
// TODO(yjbanov): make this List<CkCanvas>
final List<CkCanvas?> _canvases = <CkCanvas?>[];
final List<CkCanvas> _canvases = <CkCanvas>[];
void addCanvas(CkCanvas? canvas) {
void addCanvas(CkCanvas canvas) {
_canvases.add(canvas);
}
......@@ -17,7 +22,7 @@ class CkNWayCanvas {
int save() {
int saveCount = 0;
for (int i = 0; i < _canvases.length; i++) {
saveCount = _canvases[i]!.save();
saveCount = _canvases[i].save();
}
return saveCount;
}
......@@ -25,63 +30,64 @@ class CkNWayCanvas {
/// Calls [saveLayer] on all canvases.
void saveLayer(ui.Rect bounds, CkPaint? paint) {
for (int i = 0; i < _canvases.length; i++) {
_canvases[i]!.saveLayer(bounds, paint);
_canvases[i].saveLayer(bounds, paint);
}
}
/// 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);
_canvases[i].saveLayerWithFilter(bounds, filter, paint);
}
}
/// Calls [restore] on all canvases.
void restore() {
for (int i = 0; i < _canvases.length; i++) {
_canvases[i]!.restore();
_canvases[i].restore();
}
}
/// Calls [restoreToCount] on all canvases.
void restoreToCount(int count) {
for (int i = 0; i < _canvases.length; i++) {
_canvases[i]!.restoreToCount(count);
_canvases[i].restoreToCount(count);
}
}
/// Calls [translate] on all canvases.
void translate(double dx, double dy) {
for (int i = 0; i < _canvases.length; i++) {
_canvases[i]!.translate(dx, dy);
_canvases[i].translate(dx, dy);
}
}
/// Calls [transform] on all canvases.
void transform(Float32List matrix) {
for (int i = 0; i < _canvases.length; i++) {
_canvases[i]!.transform(matrix);
_canvases[i].transform(matrix);
}
}
/// Calls [clipPath] on all canvases.
void clipPath(CkPath path, bool doAntiAlias) {
for (int i = 0; i < _canvases.length; i++) {
_canvases[i]!.clipPath(path, doAntiAlias);
_canvases[i].clipPath(path, doAntiAlias);
}
}
/// Calls [clipRect] on all canvases.
void clipRect(ui.Rect rect, ui.ClipOp clipOp, bool doAntiAlias) {
for (int i = 0; i < _canvases.length; i++) {
_canvases[i]!.clipRect(rect, clipOp, doAntiAlias);
_canvases[i].clipRect(rect, clipOp, doAntiAlias);
}
}
/// Calls [clipRRect] on all canvases.
void clipRRect(ui.RRect rrect, bool doAntiAlias) {
for (int i = 0; i < _canvases.length; i++) {
_canvases[i]!.clipRRect(rrect, doAntiAlias);
_canvases[i].clipRRect(rrect, doAntiAlias);
}
}
}
......@@ -2,7 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'package:ui/ui.dart' as ui;
import 'canvaskit_api.dart';
import 'color_filter.dart';
import 'image_filter.dart';
import 'mask_filter.dart';
import 'shader.dart';
import 'skia_object_cache.dart';
/// The implementation of [ui.Paint] used by the CanvasKit backend.
///
......@@ -175,12 +182,12 @@ class CkPaint extends ManagedSkiaObject<SkPaint> implements ui.Paint {
if (value == null) {
_managedColorFilter = null;
} else {
_managedColorFilter = _ManagedSkColorFilter(value as CkColorFilter);
_managedColorFilter = ManagedSkColorFilter(value as CkColorFilter);
}
skiaObject.setColorFilter(_managedColorFilter?.skiaObject);
}
_ManagedSkColorFilter? _managedColorFilter;
ManagedSkColorFilter? _managedColorFilter;
@override
double get strokeMiterLimit => _strokeMiterLimit;
......@@ -203,12 +210,12 @@ class CkPaint extends ManagedSkiaObject<SkPaint> implements ui.Paint {
return;
}
_imageFilter = value as _CkManagedSkImageFilterConvertible?;
_managedImageFilter = _imageFilter?._imageFilter;
_imageFilter = value as CkManagedSkImageFilterConvertible?;
_managedImageFilter = _imageFilter?.imageFilter;
skiaObject.setImageFilter(_managedImageFilter?.skiaObject);
}
_CkManagedSkImageFilterConvertible? _imageFilter;
CkManagedSkImageFilterConvertible? _imageFilter;
ManagedSkiaObject<SkImageFilter>? _managedImageFilter;
@override
......
......@@ -2,7 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'dart:math' as math;
import 'dart:typed_data';
import 'package:ui/src/engine.dart' show Matrix4;
import 'package:ui/ui.dart' as ui;
import 'canvaskit_api.dart';
import 'path_metrics.dart';
import 'skia_object_cache.dart';
/// An implementation of [ui.Path] which is backed by an `SkPath`.
///
......@@ -16,7 +24,7 @@ class CkPath extends ManagedSkiaObject<SkPath> implements ui.Path {
skiaObject.setFillType(toSkFillType(_fillType));
}
CkPath._fromSkPath(SkPath skPath, this._fillType) : super(skPath) {
CkPath.fromSkPath(SkPath skPath, this._fillType) : super(skPath) {
skiaObject.setFillType(toSkFillType(_fillType));
}
......@@ -254,7 +262,7 @@ class CkPath extends ManagedSkiaObject<SkPath> implements ui.Path {
// path and call `transform` on it.
final SkPath newPath = skiaObject.copy();
newPath.transform(1.0, 0.0, offset.dx, 0.0, 1.0, offset.dy, 0.0, 0.0, 0.0);
return CkPath._fromSkPath(newPath, _fillType);
return CkPath.fromSkPath(newPath, _fillType);
}
static CkPath combine(
......@@ -269,7 +277,7 @@ class CkPath extends ManagedSkiaObject<SkPath> implements ui.Path {
path2.skiaObject,
toSkPathOp(operation),
);
return CkPath._fromSkPath(newPath, path1._fillType);
return CkPath.fromSkPath(newPath, path1._fillType);
}
@override
......@@ -287,7 +295,7 @@ class CkPath extends ManagedSkiaObject<SkPath> implements ui.Path {
m[7],
m[8],
);
return CkPath._fromSkPath(newPath, _fillType);
return CkPath.fromSkPath(newPath, _fillType);
}
String? toSvgString() {
......
......@@ -2,7 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'dart:collection';
import 'dart:typed_data';
import 'package:ui/ui.dart' as ui;
import 'canvaskit_api.dart';
import 'path.dart';
import 'skia_object_cache.dart';
class CkPathMetrics extends IterableBase<ui.PathMetric>
implements ui.PathMetrics {
......@@ -14,11 +21,12 @@ class CkPathMetrics extends IterableBase<ui.PathMetric>
/// The [CkPath.isEmpty] case is special-cased to avoid booting the WASM machinery just to find out there are no contours.
@override
Iterator<ui.PathMetric> get iterator => _path.isEmpty
? const CkPathMetricIteratorEmpty._()
: CkContourMeasureIter(this);
? const CkPathMetricIteratorEmpty._()
: CkContourMeasureIter(this);
}
class CkContourMeasureIter extends ManagedSkiaObject<SkContourMeasureIter> implements Iterator<ui.PathMetric> {
class CkContourMeasureIter extends ManagedSkiaObject<SkContourMeasureIter>
implements Iterator<ui.PathMetric> {
CkContourMeasureIter(this._metrics);
final CkPathMetrics _metrics;
......@@ -33,13 +41,13 @@ class CkContourMeasureIter extends ManagedSkiaObject<SkContourMeasureIter> imple
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.'
'- 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.'
'- The iterator ran out of elements. If so, check that "moveNext" returns true prior to calling "current".');
}
return currentMetric;
}
CkContourMeasure? _current;
@override
......@@ -50,7 +58,8 @@ class CkContourMeasureIter extends ManagedSkiaObject<SkContourMeasureIter> imple
return false;
}
_current = CkContourMeasure(_metrics, skContourMeasure, _contourIndexCounter);
_current =
CkContourMeasure(_metrics, skContourMeasure, _contourIndexCounter);
_contourIndexCounter += 1;
return true;
}
......@@ -83,9 +92,10 @@ class CkContourMeasureIter extends ManagedSkiaObject<SkContourMeasureIter> imple
}
}
class CkContourMeasure extends ManagedSkiaObject<SkContourMeasure> implements ui.PathMetric {
class CkContourMeasure extends ManagedSkiaObject<SkContourMeasure>
implements ui.PathMetric {
CkContourMeasure(this._metrics, SkContourMeasure jsObject, this.contourIndex)
: super(jsObject);
: super(jsObject);
/// The path metrics used to create this measure.
///
......@@ -98,7 +108,7 @@ class CkContourMeasure extends ManagedSkiaObject<SkContourMeasure> implements ui
@override
ui.Path extractPath(double start, double end, {bool startWithMoveTo = true}) {
final SkPath skPath = skiaObject.getSegment(start, end, startWithMoveTo);
return CkPath._fromSkPath(skPath, _metrics._path._fillType);
return CkPath.fromSkPath(skPath, _metrics._path.fillType);
}
@override
......@@ -130,7 +140,8 @@ class CkContourMeasure extends ManagedSkiaObject<SkContourMeasure> implements ui
@override
SkContourMeasure resurrect() {
final CkContourMeasureIter iterator = _metrics.iterator as CkContourMeasureIter;
final CkContourMeasureIter iterator =
_metrics.iterator as CkContourMeasureIter;
final SkContourMeasureIter skIterator = iterator.skiaObject;
// When resurrecting we must advance the iterator to the last known
......
......@@ -2,7 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'package:ui/ui.dart' as ui;
import 'canvas.dart';
import 'canvaskit_api.dart';
import 'image.dart';
import 'skia_object_cache.dart';
class CkPicture extends ManagedSkiaObject<SkPicture> implements ui.Picture {
final ui.Rect? cullRect;
......@@ -10,7 +15,8 @@ class CkPicture extends ManagedSkiaObject<SkPicture> implements ui.Picture {
CkPicture(SkPicture picture, this.cullRect, this._snapshot) : super(picture) {
assert(
browserSupportsFinalizationRegistry && _snapshot == null || _snapshot != null,
browserSupportsFinalizationRegistry && _snapshot == null ||
_snapshot != null,
'If the browser does not support FinalizationRegistry (WeakRef), then we must have a picture snapshot to be able to resurrect it.',
);
}
......
......@@ -2,7 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'dart:typed_data';
import 'package:ui/ui.dart' as ui;
import 'canvas.dart';
import 'canvaskit_api.dart';
import 'picture.dart';
class CkPictureRecorder implements ui.PictureRecorder {
ui.Rect? _cullRect;
......@@ -15,8 +21,8 @@ class CkPictureRecorder implements ui.PictureRecorder {
final Float32List skRect = toSkRect(bounds);
final SkCanvas skCanvas = recorder.beginRecording(skRect);
return _recordingCanvas = browserSupportsFinalizationRegistry
? CkCanvas(skCanvas)
: RecordingCkCanvas(skCanvas, bounds);
? CkCanvas(skCanvas)
: RecordingCkCanvas(skCanvas, bounds);
}
CkCanvas? get recordingCanvas => _recordingCanvas;
......
......@@ -2,8 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'dart:typed_data';
// TODO(hterkelsen): Delete this once the slots change lands?
class PlatformMessage {
final String channel;
final ByteData data;
......
......@@ -2,7 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'package:ui/src/engine.dart' show Matrix4;
import 'package:ui/ui.dart' as ui;
import 'canvas.dart';
/// A cache of [Picture]s that have already been rasterized.
///
......
......@@ -2,7 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'package:ui/src/engine.dart' show frameReferences;
import 'package:ui/ui.dart' as ui;
import 'canvas.dart';
import 'layer_tree.dart';
import 'surface.dart';
/// A class that can rasterize [LayerTree]s into a given [Surface].
class Rasterizer {
......@@ -13,7 +18,7 @@ class Rasterizer {
Rasterizer(this.surface);
void setSkiaResourceCacheMaxBytes(int bytes) =>
surface.setSkiaResourceCacheMaxBytes(bytes);
surface.setSkiaResourceCacheMaxBytes(bytes);
/// Creates a new frame from this rasterizer's surface, draws the given
/// [LayerTree] into it, and then submits the frame.
......@@ -48,9 +53,9 @@ class Rasterizer {
final ui.VoidCallback callback = _postFrameCallbacks[i];
callback();
}
for (int i = 0; i < _frameReferences.length; i++) {
_frameReferences[i].value = null;
for (int i = 0; i < frameReferences.length; i++) {
frameReferences[i].value = null;
}
_frameReferences.clear();
frameReferences.clear();
}
}
......@@ -2,7 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'dart:math' as math;
import 'dart:typed_data';
import 'package:ui/ui.dart' as ui;
import '../util.dart';
import '../validators.dart';
import 'canvaskit_api.dart';
import 'image.dart';
import 'initialization.dart';
import 'skia_object_cache.dart';
abstract class CkShader extends ManagedSkiaObject<SkShader>
implements ui.Shader {
......@@ -165,7 +175,8 @@ class CkGradientConical extends CkShader implements ui.Gradient {
}
class CkImageShader extends CkShader implements ui.ImageShader {
CkImageShader(ui.Image image, this.tileModeX, this.tileModeY, this.matrix4, this.filterQuality)
CkImageShader(ui.Image image, this.tileModeX, this.tileModeY, this.matrix4,
this.filterQuality)
: _image = image as CkImage;
final ui.TileMode tileModeX;
......
......@@ -2,7 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'dart:collection';
import 'package:meta/meta.dart';
import '../../engine.dart' show EnginePlatformDispatcher, Instrumentation;
import 'canvaskit_api.dart';
import '../util.dart';
/// A cache of Skia objects whose memory Flutter manages.
///
......@@ -248,7 +254,8 @@ typedef Resurrector<T> = T Function();
/// prior to being deleted permanently. A temporary deletion may effectively
/// be permanent if this object is garbage collected. This is safe because a
/// temporarily deleted object has no C++ resources to collect.
class SkiaObjectBox<R extends StackTraceDebugger, T extends Object> extends SkiaObject<T> {
class SkiaObjectBox<R extends StackTraceDebugger, T extends Object>
extends SkiaObject<T> {
/// Creates an object box that's memory-managed using [SkFinalizationRegistry].
///
/// This constructor must only be used if [browserSupportsFinalizationRegistry] is true.
......@@ -261,7 +268,8 @@ class SkiaObjectBox<R extends StackTraceDebugger, T extends Object> extends Skia
/// Creates an object box that's memory-managed using a [Resurrector].
///
/// This constructor must only be used if [browserSupportsFinalizationRegistry] is false.
SkiaObjectBox.resurrectable(R debugReferrer, T initialValue, this._resurrector) {
SkiaObjectBox.resurrectable(
R debugReferrer, T initialValue, this._resurrector) {
assert(!browserSupportsFinalizationRegistry);
_initialize(debugReferrer, initialValue);
SkiaObjects.manageExpensive(this);
......@@ -362,14 +370,16 @@ class SkiaObjectBox<R extends StackTraceDebugger, T extends Object> extends Skia
/// Whether the underlying [rawSkiaObject] has been deleted, but it may still
/// be resurrected (see [SkiaObjectBox.resurrectable]).
bool get isDeletedTemporarily => rawSkiaObject == null && !_isDeletedPermanently;
bool get isDeletedTemporarily =>
rawSkiaObject == null && !_isDeletedPermanently;
/// Increases the reference count of this box because a new object began
/// sharing ownership of the underlying [skiaObject].
///
/// Clones must be [dispose]d when finished.
void ref(R debugReferrer) {
assert(!_isDeletedPermanently, 'Cannot increment ref count on a deleted handle.');
assert(!_isDeletedPermanently,
'Cannot increment ref count on a deleted handle.');
assert(_refCount > 0);
assert(
debugReferrers.add(debugReferrer),
......@@ -386,7 +396,8 @@ class SkiaObjectBox<R extends StackTraceDebugger, T extends Object> extends Skia
/// If this causes the reference count to drop to zero, deletes the
/// [skObject].
void unref(R debugReferrer) {
assert(!_isDeletedPermanently, 'Attempted to unref an already deleted Skia object.');
assert(!_isDeletedPermanently,
'Attempted to unref an already deleted Skia object.');
assert(
debugReferrers.remove(debugReferrer),
'Attempted to decrement ref count by the same referrer more than once.',
......@@ -435,7 +446,11 @@ class SkiaObjects {
if (_addedCleanupCallback) {
return;
}
EnginePlatformDispatcher.instance.rasterizer!.addPostFrameCallback(postFrameCleanUp);
// This method is @visibleForTesting but we're getting a warning about
// using a @visibleForTesting member.
// ignore: invalid_use_of_visible_for_testing_member
EnginePlatformDispatcher.instance.rasterizer!
.addPostFrameCallback(postFrameCleanUp);
_addedCleanupCallback = true;
}
......
......@@ -2,7 +2,18 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'dart:html' as html;
import 'package:ui/src/engine.dart' show window, EnginePlatformDispatcher;
import 'package:ui/ui.dart' as ui;
import '../browser_detection.dart';
import '../util.dart';
import 'canvas.dart';
import 'canvaskit_api.dart';
import 'embedded_views.dart';
import 'initialization.dart';
import 'util.dart';
typedef SubmitCallback = bool Function(SurfaceFrame, CkCanvas);
......
......@@ -2,7 +2,18 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'dart:typed_data';
import 'package:meta/meta.dart';
import 'package:ui/ui.dart' as ui;
import '../util.dart';
import 'canvaskit_api.dart';
import 'font_fallbacks.dart';
import 'initialization.dart';
import 'painting.dart';
import 'skia_object_cache.dart';
import 'util.dart';
@immutable
class CkParagraphStyle implements ui.ParagraphStyle {
......@@ -76,7 +87,7 @@ class CkParagraphStyle implements ui.ParagraphStyle {
}
static SkStrutStyleProperties toSkStrutStyleProperties(ui.StrutStyle value, ui.TextHeightBehavior? paragraphHeightBehavior) {
EngineStrutStyle style = value as EngineStrutStyle;
CkStrutStyle style = value as CkStrutStyle;
final SkStrutStyleProperties skStrutStyle = SkStrutStyleProperties();
skStrutStyle.fontFamilies =
_getEffectiveFontFamilies(style._fontFamily, style._fontFamilyFallback);
......@@ -439,6 +450,69 @@ class CkTextStyle implements ui.TextStyle {
}();
}
class CkStrutStyle implements ui.StrutStyle {
CkStrutStyle({
String? fontFamily,
List<String>? fontFamilyFallback,
double? fontSize,
double? height,
//TODO(LongCatIsLooong): implement leadingDistribution.
ui.TextLeadingDistribution? leadingDistribution,
double? leading,
ui.FontWeight? fontWeight,
ui.FontStyle? fontStyle,
bool? forceStrutHeight,
}) : _fontFamily = fontFamily,
_fontFamilyFallback = fontFamilyFallback,
_fontSize = fontSize,
_height = height,
_leadingDistribution = leadingDistribution,
_leading = leading,
_fontWeight = fontWeight,
_fontStyle = fontStyle,
_forceStrutHeight = forceStrutHeight;
final String? _fontFamily;
final List<String>? _fontFamilyFallback;
final double? _fontSize;
final double? _height;
final double? _leading;
final ui.FontWeight? _fontWeight;
final ui.FontStyle? _fontStyle;
final bool? _forceStrutHeight;
final ui.TextLeadingDistribution? _leadingDistribution;
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) {
return false;
}
return other is CkStrutStyle &&
other._fontFamily == _fontFamily &&
other._fontSize == _fontSize &&
other._height == _height &&
other._leading == _leading &&
other._leadingDistribution == _leadingDistribution &&
other._fontWeight == _fontWeight &&
other._fontStyle == _fontStyle &&
other._forceStrutHeight == _forceStrutHeight &&
listEquals<String>(other._fontFamilyFallback, _fontFamilyFallback);
}
@override
int get hashCode => ui.hashValues(
_fontFamily,
_fontFamilyFallback,
_fontSize,
_height,
_leading,
_leadingDistribution,
_fontWeight,
_fontStyle,
_forceStrutHeight,
);
}
SkFontStyle toSkFontStyle(ui.FontWeight? fontWeight, ui.FontStyle? fontStyle) {
final style = SkFontStyle();
if (fontWeight != null) {
......
......@@ -2,7 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'dart:math' as math;
import 'dart:typed_data';
import 'package:ui/src/engine.dart' show transformRect, Matrix4;
import 'package:ui/ui.dart' as ui;
import 'canvaskit_api.dart';
import 'path.dart';
/// An error related to the CanvasKit rendering backend.
class CanvasKitError extends Error {
......@@ -26,7 +33,8 @@ Float32List makeFreshSkColor(ui.Color color) {
}
ui.TextPosition fromPositionWithAffinity(SkTextPosition positionWithAffinity) {
final ui.TextAffinity affinity = ui.TextAffinity.values[positionWithAffinity.affinity.value];
final ui.TextAffinity affinity =
ui.TextAffinity.values[positionWithAffinity.affinity.value];
return ui.TextPosition(
offset: positionWithAffinity.pos,
affinity: affinity,
......@@ -53,10 +61,12 @@ 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 = kDirectionalLight_ShadowFlag | kTransparentOccluder_ShadowFlag;
static const int kTransparentOccluderShadowFlags =
kDirectionalLight_ShadowFlag | kTransparentOccluder_ShadowFlag;
}
// These numbers have been chosen empirically to give a result closest to the
......@@ -115,7 +125,8 @@ 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);
......@@ -134,10 +145,12 @@ 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(
......@@ -149,10 +162,11 @@ void drawSkShadow(
double devicePixelRatio,
) {
final int flags = transparentOccluder
? SkiaShadowFlags.kTransparentOccluderShadowFlags
: SkiaShadowFlags.kDefaultShadowFlags;
? SkiaShadowFlags.kTransparentOccluderShadowFlags
: SkiaShadowFlags.kDefaultShadowFlags;
ui.Color inAmbient = color.withAlpha((color.alpha * ckShadowAmbientAlpha).round());
ui.Color inAmbient =
color.withAlpha((color.alpha * ckShadowAmbientAlpha).round());
ui.Color inSpot = color.withAlpha((color.alpha * ckShadowSpotAlpha).round());
final SkTonalColors inTonalColors = SkTonalColors(
......@@ -160,13 +174,11 @@ void drawSkShadow(
spot: makeFreshSkColor(inSpot),
);
final SkTonalColors tonalColors =
canvasKit.computeTonalColors(inTonalColors);
final SkTonalColors tonalColors = canvasKit.computeTonalColors(inTonalColors);
skCanvas.drawShadow(
path.skiaObject,
Float32List(3)
..[2] = devicePixelRatio * elevation,
Float32List(3)..[2] = devicePixelRatio * elevation,
Float32List(3)
..[0] = ckShadowLightXOffset
..[1] = ckShadowLightYOffset
......
......@@ -2,7 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
import 'dart:typed_data';
import 'package:ui/ui.dart' as ui;
import 'canvaskit_api.dart';
import 'skia_object_cache.dart';
class CkVertices extends ManagedSkiaObject<SkVertices> implements ui.Vertices {
factory CkVertices(
......
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of engine;
class ViewportMetrics {
final double devicePixelRatio;
final double physicalWidth;
......
......@@ -20,7 +20,8 @@ class EngineColorFilter implements ui.ColorFilter {
/// The output of this filter is then composited into the background according
/// to the [Paint.blendMode], using the output of this filter as the source
/// and the background as the destination.
const factory EngineColorFilter.mode(ui.Color color, ui.BlendMode blendMode) = _CkBlendModeColorFilter;
const factory EngineColorFilter.mode(ui.Color color, ui.BlendMode blendMode) =
CkBlendModeColorFilter;
/// Construct a color filter that transforms a color by a 5x5 matrix, where
/// the fifth row is implicitly added in an identity configuration.
......@@ -82,13 +83,16 @@ class EngineColorFilter implements ui.ColorFilter {
/// 0, 0, 0, 1, 0,
/// ]);
/// ```
const factory EngineColorFilter.matrix(List<double> matrix) = _CkMatrixColorFilter;
const factory EngineColorFilter.matrix(List<double> matrix) =
CkMatrixColorFilter;
/// Construct a color filter that applies the sRGB gamma curve to the RGB
/// channels.
const factory EngineColorFilter.linearToSrgbGamma() = _CkLinearToSrgbGammaColorFilter;
const factory EngineColorFilter.linearToSrgbGamma() =
CkLinearToSrgbGammaColorFilter;
/// Creates a color filter that applies the inverse of the sRGB gamma curve
/// to the RGB channels.
const factory EngineColorFilter.srgbToLinearGamma() = _CkSrgbToLinearGammaColorFilter;
const factory EngineColorFilter.srgbToLinearGamma() =
CkSrgbToLinearGammaColorFilter;
}
......@@ -366,7 +366,7 @@ flt-glass-pane * {
setElementAttribute(
bodyElement,
'flt-renderer',
'${useCanvasKit ? 'canvaskit' : 'html'} (${_autoDetect ? 'auto-selected' : 'requested explicitly'})',
'${useCanvasKit ? 'canvaskit' : 'html'} (${flutterWebAutoDetect ? 'auto-selected' : 'requested explicitly'})',
);
setElementAttribute(bodyElement, 'flt-build-mode', buildMode);
......
......@@ -9,7 +9,7 @@ part of engine;
/// Used for debugging only.
int _debugFrameNumber = 1;
List<FrameReference<dynamic>> _frameReferences = <FrameReference<dynamic>>[];
List<FrameReference<dynamic>> frameReferences = <FrameReference<dynamic>>[];
/// A temporary reference to a value of type [V].
///
......@@ -21,7 +21,7 @@ List<FrameReference<dynamic>> _frameReferences = <FrameReference<dynamic>>[];
class FrameReference<V> {
/// Creates a frame reference to a value.
FrameReference([this.value]) {
_frameReferences.add(this);
frameReferences.add(this);
}
/// The current value of this reference.
......
......@@ -630,12 +630,12 @@ class BitmapCanvas extends EngineCanvas {
final EngineColorFilter? colorFilter =
paint.colorFilter as EngineColorFilter?;
html.HtmlElement imgElement;
if (colorFilter is _CkBlendModeColorFilter) {
imgElement = _createImageElementWithBlend(image,
colorFilter.color, colorFilter.blendMode, paint);
} else if (colorFilter is _CkMatrixColorFilter) {
if (colorFilter is CkBlendModeColorFilter) {
imgElement = _createImageElementWithBlend(
image, colorFilter.color, colorFilter.blendMode, paint);
} else if (colorFilter is CkMatrixColorFilter) {
imgElement = _createImageElementWithSvgColorMatrixFilter(
image, colorFilter.matrix , paint);
image, colorFilter.matrix, paint);
} else {
// No Blending, create an image by cloning original loaded image.
imgElement = _reuseOrCreateImage(htmlImage);
......@@ -665,8 +665,8 @@ class BitmapCanvas extends EngineCanvas {
return imgElement;
}
html.HtmlElement _createImageElementWithBlend(HtmlImage image,
ui.Color color, ui.BlendMode blendMode, SurfacePaintData paint) {
html.HtmlElement _createImageElementWithBlend(HtmlImage image, ui.Color color,
ui.BlendMode blendMode, SurfacePaintData paint) {
switch (blendMode) {
case ui.BlendMode.colorBurn:
case ui.BlendMode.colorDodge:
......@@ -685,8 +685,7 @@ class BitmapCanvas extends EngineCanvas {
return _createImageElementWithSvgBlendFilter(
image, color, blendMode, paint);
default:
return _createBackgroundImageWithBlend(
image, color, blendMode, paint);
return _createBackgroundImageWithBlend(image, color, blendMode, paint);
}
}
......@@ -827,7 +826,7 @@ class BitmapCanvas extends EngineCanvas {
String? svgFilter =
svgFilterFromBlendMode(filterColor, colorFilterBlendMode);
final html.Element filterElement =
html.Element.html(svgFilter, treeSanitizer: _NullTreeSanitizer());
html.Element.html(svgFilter, treeSanitizer: NullTreeSanitizer());
rootElement.append(filterElement);
_children.add(filterElement);
final html.HtmlElement imgElement = _reuseOrCreateImage(image);
......@@ -840,13 +839,11 @@ class BitmapCanvas extends EngineCanvas {
// Creates an image element and an svg color matrix filter to apply on the element.
html.HtmlElement _createImageElementWithSvgColorMatrixFilter(
HtmlImage image,
List<double> matrix,
SurfacePaintData paint) {
HtmlImage image, List<double> matrix, SurfacePaintData paint) {
// For srcIn blendMode, we use an svg filter to apply to image element.
String? svgFilter = svgFilterFromColorMatrix(matrix);
final html.Element filterElement =
html.Element.html(svgFilter, treeSanitizer: _NullTreeSanitizer());
html.Element.html(svgFilter, treeSanitizer: NullTreeSanitizer());
rootElement.append(filterElement);
_children.add(filterElement);
final html.HtmlElement imgElement = _reuseOrCreateImage(image);
......@@ -968,7 +965,8 @@ class BitmapCanvas extends EngineCanvas {
final Int32List? colors = vertices.colors;
final ui.VertexMode mode = vertices.mode;
html.CanvasRenderingContext2D? ctx = _canvasPool.context;
if (colors == null && paint.style != ui.PaintingStyle.fill &&
if (colors == null &&
paint.style != ui.PaintingStyle.fill &&
paint.shader == null) {
final Float32List positions = mode == ui.VertexMode.triangles
? vertices.positions
......@@ -1240,7 +1238,7 @@ List<html.Element> _clipContent(List<_SaveClipEntry> clipStack,
String svgClipPath =
createSvgClipDef(curElement as html.HtmlElement, entry.path!);
final html.Element clipElement =
html.Element.html(svgClipPath, treeSanitizer: _NullTreeSanitizer());
html.Element.html(svgClipPath, treeSanitizer: NullTreeSanitizer());
clipDefs.add(clipElement);
}
}
......
......@@ -342,7 +342,7 @@ class PersistedPhysicalShape extends PersistedContainerSurface
_clipElement?.remove();
_svgElement?.remove();
_clipElement =
html.Element.html(svgClipPath, treeSanitizer: _NullTreeSanitizer());
html.Element.html(svgClipPath, treeSanitizer: NullTreeSanitizer());
domRenderer.append(rootElement!, _clipElement!);
if (elevation == 0.0) {
DomRenderer.setClipPath(rootElement!, createSvgClipUrl());
......@@ -469,7 +469,7 @@ class PersistedClipPath extends PersistedContainerSurface
final String svgClipPath =
createSvgClipDef(childContainer as html.HtmlElement, clipPath);
_clipElement =
html.Element.html(svgClipPath, treeSanitizer: _NullTreeSanitizer());
html.Element.html(svgClipPath, treeSanitizer: NullTreeSanitizer());
domRenderer.append(childContainer!, _clipElement!);
}
......
......@@ -59,16 +59,16 @@ class PersistedColorFilter extends PersistedContainerSurface
childContainer?.style.visibility = 'visible';
return;
}
if (engineValue is _CkBlendModeColorFilter) {
if (engineValue is CkBlendModeColorFilter) {
_applyBlendModeFilter(engineValue);
} else if (engineValue is _CkMatrixColorFilter) {
} else if (engineValue is CkMatrixColorFilter) {
_applyMatrixColorFilter(engineValue);
} else {
childContainer?.style.visibility = 'visible';
}
}
void _applyBlendModeFilter(_CkBlendModeColorFilter colorFilter) {
void _applyBlendModeFilter(CkBlendModeColorFilter colorFilter) {
ui.Color filterColor = colorFilter.color;
ui.BlendMode colorFilterBlendMode = colorFilter.blendMode;
html.CssStyleDeclaration style = rootElement!.style;
......@@ -119,7 +119,7 @@ class PersistedColorFilter extends PersistedContainerSurface
svgFilterFromBlendMode(filterColor, colorFilterBlendMode);
if (svgFilter != null) {
_filterElement =
html.Element.html(svgFilter, treeSanitizer: _NullTreeSanitizer());
html.Element.html(svgFilter, treeSanitizer: NullTreeSanitizer());
rootElement!.append(_filterElement!);
rootElement!.style.filter = 'url(#_fcf${_filterIdCounter})';
if (colorFilterBlendMode == ui.BlendMode.saturation ||
......@@ -130,11 +130,11 @@ class PersistedColorFilter extends PersistedContainerSurface
}
}
void _applyMatrixColorFilter(_CkMatrixColorFilter colorFilter) {
void _applyMatrixColorFilter(CkMatrixColorFilter colorFilter) {
String? svgFilter = svgFilterFromColorMatrix(colorFilter.matrix);
if (svgFilter != null) {
_filterElement =
html.Element.html(svgFilter, treeSanitizer: _NullTreeSanitizer());
html.Element.html(svgFilter, treeSanitizer: NullTreeSanitizer());
rootElement!.append(_filterElement!);
rootElement!.style.filter = 'url(#_fcf${_filterIdCounter})';
}
......
......@@ -6,7 +6,6 @@ part of engine;
/// A canvas that renders to DOM elements and CSS properties.
class DomCanvas extends EngineCanvas with SaveElementStackTracking {
@override
final html.Element rootElement;
......@@ -61,14 +60,14 @@ class DomCanvas extends EngineCanvas with SaveElementStackTracking {
@override
void drawRect(ui.Rect rect, SurfacePaintData 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) {
html.Element element = _buildDrawRectElement(rrect.outerRect,
paint, 'draw-rrect', currentTransform);
html.Element element = _buildDrawRectElement(
rrect.outerRect, paint, 'draw-rrect', currentTransform);
_applyRRectBorderRadius(element.style, rrect);
currentElement.append(element);
}
......@@ -112,8 +111,9 @@ class DomCanvas extends EngineCanvas with SaveElementStackTracking {
@override
void drawParagraph(ui.Paragraph paragraph, ui.Offset offset) {
final html.Element paragraphElement =
_drawParagraphElement(paragraph as EngineParagraph, offset, transform: currentTransform);
final html.Element paragraphElement = _drawParagraphElement(
paragraph as EngineParagraph, offset,
transform: currentTransform);
currentElement.append(paragraphElement);
}
......@@ -124,7 +124,8 @@ class DomCanvas extends EngineCanvas with SaveElementStackTracking {
}
@override
void drawPoints(ui.PointMode pointMode, Float32List points, SurfacePaintData paint) {
void drawPoints(
ui.PointMode pointMode, Float32List points, SurfacePaintData paint) {
throw UnimplementedError();
}
......@@ -139,16 +140,16 @@ class DomCanvas extends EngineCanvas with SaveElementStackTracking {
///
/// Returns a color for box-shadow based on blur filter at sigma.
ui.Color blurColor(ui.Color color, double sigma) {
final double strength = math.min(
math.sqrt(sigma) / (math.pi * 2.0), 1.0);
final double strength = math.min(math.sqrt(sigma) / (math.pi * 2.0), 1.0);
final int reducedAlpha = ((1.0 - strength) * color.alpha).round();
return ui.Color((reducedAlpha & 0xff) << 24 | (color.value & 0x00ffffff));
}
html.HtmlElement _buildDrawRectElement(ui.Rect rect, SurfacePaintData paint, String tagName,
Matrix4 transform) {
html.HtmlElement _buildDrawRectElement(
ui.Rect rect, SurfacePaintData paint, String tagName, Matrix4 transform) {
assert(paint.shader == null);
final html.HtmlElement rectangle = domRenderer.createElement(tagName) as html.HtmlElement;
final html.HtmlElement rectangle =
domRenderer.createElement(tagName) as html.HtmlElement;
assert(() {
rectangle.setAttribute('flt-rect', '$rect');
rectangle.setAttribute('flt-paint', '$paint');
......@@ -164,7 +165,7 @@ html.HtmlElement _buildDrawRectElement(ui.Rect rect, SurfacePaintData paint, Str
if (transform.isIdentity()) {
if (isStroke) {
effectiveTransform =
'translate(${left - (strokeWidth / 2.0)}px, ${top - (strokeWidth / 2.0)}px)';
'translate(${left - (strokeWidth / 2.0)}px, ${top - (strokeWidth / 2.0)}px)';
} else {
effectiveTransform = 'translate(${left}px, ${top}px)';
}
......@@ -245,8 +246,8 @@ String _borderStrokeToCssUnit(double value) {
return '${value.toStringAsFixed(3)}px';
}
html.Element _pathToSvgElement(SurfacePath path, SurfacePaintData paint,
String width, String height) {
html.Element _pathToSvgElement(
SurfacePath path, SurfacePaintData paint, String width, String height) {
final StringBuffer sb = StringBuffer();
sb.write(
'<svg viewBox="0 0 $width $height" width="${width}px" height="${height}px">');
......@@ -254,7 +255,8 @@ html.Element _pathToSvgElement(SurfacePath path, SurfacePaintData paint,
final ui.Color color = paint.color ?? const ui.Color(0xFF000000);
if (paint.style == ui.PaintingStyle.stroke ||
(paint.style != ui.PaintingStyle.fill &&
paint.strokeWidth != 0 && paint.strokeWidth != null)) {
paint.strokeWidth != 0 &&
paint.strokeWidth != null)) {
sb.write('stroke="${colorToCssString(color)}" ');
sb.write('stroke-width="${paint.strokeWidth ?? 1.0}" ');
sb.write('fill="none" ');
......@@ -270,5 +272,5 @@ html.Element _pathToSvgElement(SurfacePath path, SurfacePaintData paint,
pathToSvg(path.pathRef, sb);
sb.write('"></path>');
sb.write('</svg>');
return html.Element.html(sb.toString(), treeSanitizer: _NullTreeSanitizer());
return html.Element.html(sb.toString(), treeSanitizer: NullTreeSanitizer());
}
......@@ -149,7 +149,7 @@ class PersistedShaderMask extends PersistedContainerSurface
imageUrl, blendModeTemp, maskRect.width, maskRect.height)!;
_shaderElement =
html.Element.html(code, treeSanitizer: _NullTreeSanitizer());
html.Element.html(code, treeSanitizer: NullTreeSanitizer());
if (isWebKit) {
_childContainer!.style.filter = 'url(#_fmf${_maskFilterIdCounter}';
} else {
......
......@@ -5,6 +5,7 @@
/// When `true` prints statistics about what happened to the surface tree when
part of engine;
/// it was composited.
///
/// Also paints an on-screen overlay with the numbers visualized as a timeline.
......@@ -77,10 +78,10 @@ void commitScene(PersistedScene scene) {
return true;
}());
for (int i = 0; i < _frameReferences.length; i++) {
_frameReferences[i].value = null;
for (int i = 0; i < frameReferences.length; i++) {
frameReferences[i].value = null;
}
_frameReferences = <FrameReference<dynamic>>[];
frameReferences = <FrameReference<dynamic>>[];
if (_debugExplainSurfaceStats) {
_surfaceStats = <PersistedSurface, _DebugSurfaceStats>{};
......@@ -187,7 +188,7 @@ bool debugAssertSurfaceState(
abstract class PersistedSurface implements ui.EngineLayer {
/// Creates a persisted surface.
PersistedSurface(PersistedSurface? oldLayer)
: _oldLayer = FrameReference<PersistedSurface>(
: _oldLayer = FrameReference<PersistedSurface>(
oldLayer != null && oldLayer.isActive ? oldLayer : null);
/// The surface that is being updated using this surface.
......@@ -380,7 +381,8 @@ abstract class PersistedSurface implements ui.EngineLayer {
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;
......@@ -568,7 +570,8 @@ abstract class PersistedSurface implements ui.EngineLayer {
buffer.writeln('>');
debugPrintChildren(buffer, indent);
if (rootElement != null) {
buffer.writeln('${' ' * indent}</${rootElement!.tagName.toLowerCase()}>');
buffer
.writeln('${' ' * indent}</${rootElement!.tagName.toLowerCase()}>');
} else {
buffer.writeln('${' ' * indent}</$runtimeType>');
}
......@@ -773,7 +776,8 @@ abstract class PersistedContainerSurface extends PersistedSurface {
newChild, PersistedSurfaceState.pendingRetention));
} else if (newChild is PersistedContainerSurface &&
newChild.oldLayer != null) {
final PersistedContainerSurface oldLayer = newChild.oldLayer as PersistedContainerSurface;
final PersistedContainerSurface oldLayer =
newChild.oldLayer as PersistedContainerSurface;
assert(debugAssertSurfaceState(
oldLayer, PersistedSurfaceState.pendingUpdate));
newChild.update(oldLayer);
......@@ -837,7 +841,8 @@ abstract class PersistedContainerSurface extends PersistedSurface {
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) {
......@@ -936,7 +941,8 @@ abstract class PersistedContainerSurface extends PersistedSurface {
newChild, PersistedSurfaceState.pendingRetention));
} else if (newChild is PersistedContainerSurface &&
newChild.oldLayer != null) {
final PersistedContainerSurface oldLayer = newChild.oldLayer as PersistedContainerSurface;
final PersistedContainerSurface oldLayer =
newChild.oldLayer as PersistedContainerSurface;
isReparenting = oldLayer.rootElement!.parent != containerElement;
matchedOldChild = oldLayer;
assert(debugAssertSurfaceState(
......@@ -950,7 +956,8 @@ abstract class PersistedContainerSurface extends PersistedSurface {
if (matchedOldChild != null) {
assert(debugAssertSurfaceState(
matchedOldChild, PersistedSurfaceState.active));
isReparenting = matchedOldChild.rootElement!.parent != containerElement;
isReparenting =
matchedOldChild.rootElement!.parent != containerElement;
newChild.update(matchedOldChild);
assert(debugAssertSurfaceState(
matchedOldChild, PersistedSurfaceState.released));
......@@ -1018,7 +1025,8 @@ 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<int>? indexMapNew, List<int> indexMapOld) {
final List<int?> stationaryIndices = longestIncreasingSubsequence(indexMapOld);
final List<int?> stationaryIndices =
longestIncreasingSubsequence(indexMapOld);
// Convert to stationary new indices
for (int i = 0; i < stationaryIndices.length; i++) {
......@@ -1029,9 +1037,11 @@ abstract class PersistedContainerSurface extends PersistedSurface {
final html.Element? 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 html.HtmlElement childElement = child.rootElement as html.HtmlElement;
final html.HtmlElement childElement =
child.rootElement as html.HtmlElement;
assert(childElement != null); // ignore: unnecessary_null_comparison
if (!isStationary) {
if (refNode == null) {
......
......@@ -37,7 +37,7 @@ class EngineLineMetrics implements ui.LineMetrics {
required this.widthWithTrailingSpaces,
required this.left,
required this.lineNumber,
}) : assert(displayText != null), // ignore: unnecessary_null_comparison
}) : assert(displayText != null), // ignore: unnecessary_null_comparison,
assert(startIndex != null), // ignore: unnecessary_null_comparison
assert(endIndex != null), // ignore: unnecessary_null_comparison
assert(endIndexWithoutNewlines != null), // ignore: unnecessary_null_comparison
......@@ -167,33 +167,33 @@ class EngineLineMetrics implements ui.LineMetrics {
if (other.runtimeType != runtimeType) {
return false;
}
return other is EngineLineMetrics
&& other.displayText == displayText
&& other.startIndex == startIndex
&& other.endIndex == endIndex
&& 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 EngineLineMetrics &&
other.displayText == displayText &&
other.startIndex == startIndex &&
other.endIndex == endIndex &&
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
String toString() {
if (assertionsEnabled) {
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)';
} else {
return super.toString();
}
......@@ -367,7 +367,8 @@ class DomParagraph implements EngineParagraph {
_measurementResult = _measurementService.measure(this, constraints);
if (Profiler.isBenchmarkMode) {
stopwatch.stop();
Profiler.instance.benchmark('text_layout', stopwatch.elapsedMicroseconds.toDouble());
Profiler.instance
.benchmark('text_layout', stopwatch.elapsedMicroseconds.toDouble());
}
_lastUsedConstraints = constraints;
......@@ -413,7 +414,8 @@ class DomParagraph implements EngineParagraph {
// Paint the background first.
final SurfacePaint? background = _background;
if (background != null) {
final ui.Rect rect = ui.Rect.fromLTWH(offset.dx, offset.dy, width, height);
final ui.Rect rect =
ui.Rect.fromLTWH(offset.dx, offset.dy, width, height);
canvas.drawRect(rect, background.paintData);
}
......@@ -837,7 +839,8 @@ 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;
String get _effectiveFontFamily {
if (assertionsEnabled) {
......@@ -880,18 +883,18 @@ class EngineParagraphStyle implements ui.ParagraphStyle {
if (other.runtimeType != runtimeType) {
return false;
}
return other is EngineParagraphStyle
&& other._textAlign == _textAlign
&& other._textDirection == _textDirection
&& other._fontWeight == _fontWeight
&& other._fontStyle == _fontStyle
&& other._maxLines == _maxLines
&& other._fontFamily == _fontFamily
&& other._fontSize == _fontSize
&& other._height == _height
&& other._textHeightBehavior == _textHeightBehavior
&& other._ellipsis == _ellipsis
&& other._locale == _locale;
return other is EngineParagraphStyle &&
other._textAlign == _textAlign &&
other._textDirection == _textDirection &&
other._fontWeight == _fontWeight &&
other._fontStyle == _fontStyle &&
other._maxLines == _maxLines &&
other._fontFamily == _fontFamily &&
other._fontSize == _fontSize &&
other._height == _height &&
other._textHeightBehavior == _textHeightBehavior &&
other._ellipsis == _ellipsis &&
other._locale == _locale;
}
@override
......@@ -1096,24 +1099,24 @@ class EngineTextStyle implements ui.TextStyle {
if (other.runtimeType != runtimeType) {
return false;
}
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._fontFamily == _fontFamily
&& other._fontSize == _fontSize
&& other._letterSpacing == _letterSpacing
&& other._wordSpacing == _wordSpacing
&& other._height == _height
&& other._locale == _locale
&& other._background == _background
&& other._foreground == _foreground
&& _listEquals<ui.Shadow>(other._shadows, _shadows)
&& _listEquals<String>(other._fontFamilyFallback, _fontFamilyFallback);
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._fontFamily == _fontFamily &&
other._fontSize == _fontSize &&
other._letterSpacing == _letterSpacing &&
other._wordSpacing == _wordSpacing &&
other._height == _height &&
other._locale == _locale &&
other._background == _background &&
other._foreground == _foreground &&
listEquals<ui.Shadow>(other._shadows, _shadows) &&
listEquals<String>(other._fontFamilyFallback, _fontFamilyFallback);
}
@override
......@@ -1170,41 +1173,6 @@ class EngineTextStyle implements ui.TextStyle {
/// The web implementation of [ui.StrutStyle].
class EngineStrutStyle implements ui.StrutStyle {
/// Creates a new StrutStyle object.
///
/// * `fontFamily`: The name of the font to use when painting the text (e.g.,
/// Roboto).
///
/// * `fontFamilyFallback`: An ordered list of font family names that will be searched for when
/// the font in `fontFamily` cannot be found.
///
/// * `fontSize`: The size of glyphs (in logical pixels) to use when painting
/// the text.
///
/// * `lineHeight`: The minimum height of the line boxes, as a multiple of the
/// font size. The lines of the paragraph will be at least
/// `(lineHeight + leading) * fontSize` tall when fontSize
/// is not null. When fontSize is null, there is no minimum line height. Tall
/// glyphs due to baseline alignment or large [TextStyle.fontSize] may cause
/// the actual line height after layout to be taller than specified here.
/// [fontSize] must be provided for this property to take effect.
///
/// * `leading`: The minimum amount of leading between lines as a multiple of
/// the font size. [fontSize] must be provided for this property to take effect.
///
/// * `fontWeight`: The typeface thickness to use when painting the text
/// (e.g., bold).
///
/// * `fontStyle`: The typeface variant to use when drawing the letters (e.g.,
/// italics).
///
/// * `forceStrutHeight`: When true, the paragraph will force all lines to be exactly
/// `(lineHeight + leading) * fontSize` tall from baseline to baseline.
/// [TextStyle] is no longer able to influence the line height, and any tall
/// glyphs may overlap with lines above. If a [fontFamily] is specified, the
/// total ascent of the first line will be the min of the `Ascent + half-leading`
/// of the [fontFamily] and `(lineHeight + leading) * fontSize`. Otherwise, it
/// will be determined by the Ascent + half-leading of the first text.
EngineStrutStyle({
String? fontFamily,
List<String>? fontFamilyFallback,
......@@ -1243,16 +1211,16 @@ class EngineStrutStyle implements ui.StrutStyle {
if (other.runtimeType != runtimeType) {
return false;
}
return other is EngineStrutStyle
&& other._fontFamily == _fontFamily
&& other._fontSize == _fontSize
&& other._height == _height
&& other._leading == _leading
&& other._leadingDistribution == _leadingDistribution
&& other._fontWeight == _fontWeight
&& other._fontStyle == _fontStyle
&& other._forceStrutHeight == _forceStrutHeight
&& _listEquals<String>(other._fontFamilyFallback, _fontFamilyFallback);
return other is EngineStrutStyle &&
other._fontFamily == _fontFamily &&
other._fontSize == _fontSize &&
other._height == _height &&
other._leading == _leading &&
other._leadingDistribution == _leadingDistribution &&
other._fontWeight == _fontWeight &&
other._fontStyle == _fontStyle &&
other._forceStrutHeight == _forceStrutHeight &&
listEquals<String>(other._fontFamilyFallback, _fontFamilyFallback);
}
@override
......@@ -1274,7 +1242,8 @@ class DomParagraphBuilder implements ui.ParagraphBuilder {
/// Marks a call to the [pop] method in the [_ops] list.
static final Object _paragraphBuilderPop = Object();
final html.HtmlElement _paragraphElement = domRenderer.createElement('p') as html.HtmlElement;
final html.HtmlElement _paragraphElement =
domRenderer.createElement('p') as html.HtmlElement;
final EngineParagraphStyle _paragraphStyle;
final List<dynamic> _ops = <dynamic>[];
......@@ -1324,7 +1293,9 @@ class DomParagraphBuilder implements ui.ParagraphBuilder {
// 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 : true);
alignment == ui.PlaceholderAlignment.baseline)
? baseline != null
: true);
_placeholderCount++;
_placeholderScales.add(scale);
......@@ -1394,12 +1365,14 @@ class DomParagraphBuilder implements ui.ParagraphBuilder {
ui.FontWeight? fontWeight = _paragraphStyle._fontWeight;
ui.FontStyle? fontStyle = _paragraphStyle._fontStyle;
ui.TextBaseline? textBaseline;
String fontFamily = _paragraphStyle._fontFamily ?? DomRenderer.defaultFontFamily;
String fontFamily =
_paragraphStyle._fontFamily ?? DomRenderer.defaultFontFamily;
List<String>? fontFamilyFallback;
List<ui.FontFeature>? fontFeatures;
double fontSize = _paragraphStyle._fontSize ?? DomRenderer.defaultFontSize;
final ui.TextAlign textAlign = _paragraphStyle._effectiveTextAlign;
final ui.TextDirection textDirection = _paragraphStyle._effectiveTextDirection;
final ui.TextDirection textDirection =
_paragraphStyle._effectiveTextDirection;
double? letterSpacing;
double? wordSpacing;
double? height;
......@@ -1604,7 +1577,8 @@ class DomParagraphBuilder implements ui.ParagraphBuilder {
for (int i = 0; i < _ops.length; i++) {
final dynamic op = _ops[i];
if (op is EngineTextStyle) {
final html.SpanElement span = domRenderer.createElement('span') as html.SpanElement;
final html.SpanElement span =
domRenderer.createElement('span') as html.SpanElement;
_applyTextStyleToElement(element: span, style: op, isSpan: true);
if (op._background != null) {
_applyTextBackgroundToElement(element: span, style: op);
......@@ -1789,8 +1763,7 @@ void _applyTextStyleToElement({
if (isSpan && !ui.debugEmulateFlutterTesterEnvironment) {
cssStyle.fontFamily = canonicalizeFontFamily(style._fontFamily);
} else {
cssStyle.fontFamily =
canonicalizeFontFamily(style._effectiveFontFamily);
cssStyle.fontFamily = canonicalizeFontFamily(style._effectiveFontFamily);
}
if (style._letterSpacing != null) {
cssStyle.letterSpacing = '${style._letterSpacing}px';
......@@ -2002,7 +1975,8 @@ String? textDirectionIndexToCss(int textDirectionIndex) {
/// ```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';
......@@ -2031,23 +2005,3 @@ String textAlignToCssValue(ui.TextAlign? align, ui.TextDirection textDirection)
return '';
}
}
/// Determines if lists [a] and [b] are deep equivalent.
///
/// Returns true if the lists are both null, or if they are both non-null, have
/// the same length, and contain the same elements in the same order. Returns
/// false otherwise.
bool _listEquals<T>(List<T>? a, List<T>? b) {
if (a == null) {
return b == null;
}
if (b == null || a.length != b.length) {
return false;
}
for (int index = 0; index < a.length; index += 1) {
if (a[index] != b[index]) {
return false;
}
}
return true;
}
......@@ -293,21 +293,25 @@ void transformLTRB(Matrix4 transform, Float32List ltrb) {
}
ltrb[0] = math.min(
math.min(
math.min(_tempPointData[0], _tempPointData[1]), _tempPointData[2]),
_tempPointData[3]) / w;
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]) / w;
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]) / w;
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]) / w;
math.max(math.max(_tempPointData[4], _tempPointData[5]),
_tempPointData[6]),
_tempPointData[7]) /
w;
}
/// Returns true if [rect] contains every point that is also contained by the
......@@ -528,3 +532,23 @@ int clampInt(int value, int min, int max) {
/// This function can be overridden in tests. This could be useful, for example,
/// to verify that warnings are printed under certain circumstances.
void Function(String) printWarning = html.window.console.warn;
/// Determines if lists [a] and [b] are deep equivalent.
///
/// Returns true if the lists are both null, or if they are both non-null, have
/// the same length, and contain the same elements in the same order. Returns
/// false otherwise.
bool listEquals<T>(List<T>? a, List<T>? b) {
if (a == null) {
return b == null;
}
if (b == null || a.length != b.length) {
return false;
}
for (int index = 0; index < a.length; index += 1) {
if (a[index] != b[index]) {
return false;
}
}
return true;
}
......@@ -87,7 +87,8 @@ abstract class Canvas {
if (engine.useCanvasKit) {
return engine.CanvasKitCanvas(recorder, cullRect);
} else {
return engine.SurfaceCanvas(recorder as engine.EnginePictureRecorder, cullRect);
return engine.SurfaceCanvas(
recorder as engine.EnginePictureRecorder, cullRect);
}
}
void save();
......@@ -99,7 +100,8 @@ abstract class Canvas {
void rotate(double radians);
void skew(double sx, double sy);
void transform(Float64List matrix4);
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});
void drawColor(Color color, BlendMode blendMode);
......@@ -110,7 +112,8 @@ 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);
......
......@@ -45,9 +45,15 @@ class FontWeight {
];
static FontWeight? lerp(FontWeight? a, FontWeight? b, double t) {
assert(t != null); // ignore: unnecessary_null_comparison
if (a == null && b == null)
if (a == null && b == null) {
return null;
return values[engine.clampInt(lerpDouble(a?.index ?? normal.index, b?.index ?? normal.index, t)!.round(), 0, 8)];
}
return values[engine.clampInt(
lerpDouble(a?.index ?? normal.index, b?.index ?? normal.index, t)!
.round(),
0,
8,
)];
}
@override
......@@ -67,59 +73,103 @@ class FontWeight {
}
class FontFeature {
const FontFeature(
this.feature,
[ this.value = 1 ]
) : assert(feature != null), // ignore: unnecessary_null_comparison
assert(feature.length == 4, 'Feature tag must be exactly four characters long.'),
assert(value != null), // ignore: unnecessary_null_comparison
assert(value >= 0, 'Feature value must be zero or a positive integer.');
const FontFeature(this.feature, [this.value = 1])
: assert(feature != null), // ignore: unnecessary_null_comparison
assert(feature.length == 4,
'Feature tag must be exactly four characters long.'),
assert(value != null), // ignore: unnecessary_null_comparison
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;
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType)
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
......@@ -166,8 +216,7 @@ class TextDecoration {
@override
bool operator ==(Object other) {
return other is TextDecoration
&& other._mask == _mask;
return other is TextDecoration && other._mask == _mask;
}
@override
......@@ -195,13 +244,7 @@ class TextDecoration {
}
}
enum TextDecorationStyle {
solid,
double,
dotted,
dashed,
wavy
}
enum TextDecorationStyle { solid, double, dotted, dashed, wavy }
enum TextLeadingDistribution {
proportional,
......@@ -220,12 +263,13 @@ class TextHeightBehavior {
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType)
return false;
return other is TextHeightBehavior
&& other.applyHeightToFirstAscent == applyHeightToFirstAscent
&& other.applyHeightToLastDescent == applyHeightToLastDescent
&& other.leadingDistribution == leadingDistribution;
if (other.runtimeType != runtimeType) {
return false;
}
return other is TextHeightBehavior &&
other.applyHeightToFirstAscent == applyHeightToFirstAscent &&
other.applyHeightToLastDescent == applyHeightToLastDescent &&
other.leadingDistribution == leadingDistribution;
}
@override
......@@ -239,10 +283,10 @@ class TextHeightBehavior {
@override
String toString() {
return 'TextHeightBehavior('
'applyHeightToFirstAscent: $applyHeightToFirstAscent, '
'applyHeightToLastDescent: $applyHeightToLastDescent, '
'leadingDistribution: $leadingDistribution'
')';
'applyHeightToFirstAscent: $applyHeightToFirstAscent, '
'applyHeightToLastDescent: $applyHeightToLastDescent, '
'leadingDistribution: $leadingDistribution'
')';
}
}
......@@ -369,6 +413,41 @@ abstract class ParagraphStyle {
}
abstract class StrutStyle {
/// Creates a new StrutStyle object.
///
/// * `fontFamily`: The name of the font to use when painting the text (e.g.,
/// Roboto).
///
/// * `fontFamilyFallback`: An ordered list of font family names that will be searched for when
/// the font in `fontFamily` cannot be found.
///
/// * `fontSize`: The size of glyphs (in logical pixels) to use when painting
/// the text.
///
/// * `lineHeight`: The minimum height of the line boxes, as a multiple of the
/// font size. The lines of the paragraph will be at least
/// `(lineHeight + leading) * fontSize` tall when fontSize
/// is not null. When fontSize is null, there is no minimum line height. Tall
/// glyphs due to baseline alignment or large [TextStyle.fontSize] may cause
/// the actual line height after layout to be taller than specified here.
/// [fontSize] must be provided for this property to take effect.
///
/// * `leading`: The minimum amount of leading between lines as a multiple of
/// the font size. [fontSize] must be provided for this property to take effect.
///
/// * `fontWeight`: The typeface thickness to use when painting the text
/// (e.g., bold).
///
/// * `fontStyle`: The typeface variant to use when drawing the letters (e.g.,
/// italics).
///
/// * `forceStrutHeight`: When true, the paragraph will force all lines to be exactly
/// `(lineHeight + leading) * fontSize` tall from baseline to baseline.
/// [TextStyle] is no longer able to influence the line height, and any tall
/// glyphs may overlap with lines above. If a [fontFamily] is specified, the
/// total ascent of the first line will be the min of the `Ascent + half-leading`
/// of the [fontFamily] and `(lineHeight + leading) * fontSize`. Otherwise, it
/// will be determined by the Ascent + half-leading of the first text.
factory StrutStyle({
String? fontFamily,
List<String>? fontFamilyFallback,
......@@ -379,7 +458,33 @@ abstract class StrutStyle {
FontWeight? fontWeight,
FontStyle? fontStyle,
bool? forceStrutHeight,
}) = engine.EngineStrutStyle;
}) {
if (engine.useCanvasKit) {
return engine.CkStrutStyle(
fontFamily: fontFamily,
fontFamilyFallback: fontFamilyFallback,
fontSize: fontSize,
height: height,
leadingDistribution: leadingDistribution,
leading: leading,
fontWeight: fontWeight,
fontStyle: fontStyle,
forceStrutHeight: forceStrutHeight,
);
} else {
return engine.EngineStrutStyle(
fontFamily: fontFamily,
fontFamilyFallback: fontFamilyFallback,
fontSize: fontSize,
height: height,
leadingDistribution: leadingDistribution,
leading: leading,
fontWeight: fontWeight,
fontStyle: fontStyle,
forceStrutHeight: forceStrutHeight,
);
}
}
}
// The order of this enum must match the order of the values in TextDirection.h's TextDirection.
......@@ -418,12 +523,12 @@ 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
......@@ -454,9 +559,9 @@ 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
......@@ -472,10 +577,10 @@ class TextRange {
const TextRange({
required this.start,
required this.end,
}) : assert(start != null && start >= -1), // ignore: unnecessary_null_comparison
assert(end != null && end >= -1); // ignore: unnecessary_null_comparison
}) : assert(start >= -1),
assert(end >= -1);
const TextRange.collapsed(int offset)
: assert(offset != null && offset >= -1), // ignore: unnecessary_null_comparison
: assert(offset >= -1),
start = offset,
end = offset;
static const TextRange empty = TextRange(start: -1, end: -1);
......@@ -504,9 +609,7 @@ 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
......@@ -530,8 +633,7 @@ class ParagraphConstraints {
if (other.runtimeType != runtimeType) {
return false;
}
return other is ParagraphConstraints
&& other.width == width;
return other is ParagraphConstraints && other.width == width;
}
@override
......@@ -605,7 +707,8 @@ abstract class ParagraphBuilder {
if (engine.useCanvasKit) {
return engine.CkParagraphBuilder(style);
} else if (engine.WebExperiments.instance!.useCanvasRichText) {
return engine.CanvasParagraphBuilder(style as engine.EngineParagraphStyle);
return engine.CanvasParagraphBuilder(
style as engine.EngineParagraphStyle);
} else {
return engine.DomParagraphBuilder(style as engine.EngineParagraphStyle);
}
......@@ -628,12 +731,12 @@ abstract class ParagraphBuilder {
Future<void> loadFontFromList(Uint8List list, {String? fontFamily}) {
if (engine.useCanvasKit) {
return engine.skiaFontCollection.loadFontFromList(list, fontFamily: fontFamily).then(
(_) => engine.sendFontChangeMessage()
);
return engine.skiaFontCollection
.loadFontFromList(list, fontFamily: fontFamily)
.then((_) => engine.sendFontChangeMessage());
} else {
return _fontCollection!.loadFontFromList(list, fontFamily: fontFamily!).then(
(_) => engine.sendFontChangeMessage()
);
return _fontCollection!
.loadFontFromList(list, fontFamily: fontFamily!)
.then((_) => engine.sendFontChangeMessage());
}
}
......@@ -24,18 +24,21 @@ enum PaintMode {
}
void testMain() async {
final Rect region = Rect.fromLTWH(8, 8, 600, 400); // Compensate for old scuba tester padding
final Rect region =
Rect.fromLTWH(8, 8, 600, 400); // Compensate for old scuba tester padding
Future<void> testPath(Path path, String scubaFileName,
{Paint paint, double maxDiffRatePercent = null, bool write = false,
{Paint paint,
double maxDiffRatePercent = null,
bool write = false,
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);
bool enableFill = mode == PaintMode.kStrokeAndFill ||
mode == PaintMode.kFill;
bool enableFill =
mode == PaintMode.kStrokeAndFill || mode == PaintMode.kFill;
if (enableFill) {
paint ??= Paint()
..color = const Color(0x807F7F7F)
......@@ -46,8 +49,7 @@ void testMain() async {
if (mode == PaintMode.kStrokeAndFill || mode == PaintMode.kStroke) {
paint = Paint()
..strokeWidth = 2
..color = enableFill ? const Color(0xFFFF0000) :
const Color(0xFF000000)
..color = enableFill ? const Color(0xFFFF0000) : const Color(0xFF000000)
..style = PaintingStyle.stroke;
}
......@@ -59,8 +61,7 @@ void testMain() async {
canvas.drawPath(path, paint);
final html.Element svgElement = pathToSvgElement(path, paint,
enableFill);
final html.Element svgElement = pathToSvgElement(path, paint, enableFill);
html.document.body.append(bitmapCanvas.rootElement);
html.document.body.append(svgElement);
......@@ -68,8 +69,8 @@ void testMain() async {
canvas.endRecording();
canvas.apply(bitmapCanvas, canvasBounds);
await matchGoldenFile('$scubaFileName.png', region: region,
maxDiffRatePercent: maxDiffRatePercent, write: write);
await matchGoldenFile('$scubaFileName.png',
region: region, maxDiffRatePercent: maxDiffRatePercent, write: write);
bitmapCanvas.rootElement.remove();
svgElement.remove();
......@@ -180,18 +181,15 @@ void testMain() async {
final Path path = Path();
path.moveTo(20, 20);
path.lineTo(200, 200);
await testPath(path, 'svg_stroke_width',
mode: PaintMode.kStrokeWidthOnly);
await testPath(path, 'svg_stroke_width', mode: PaintMode.kStrokeWidthOnly);
});
}
html.Element pathToSvgElement(Path path, Paint paint,
bool enableFill) {
html.Element pathToSvgElement(Path path, Paint paint, bool enableFill) {
final Rect bounds = path.getBounds();
final StringBuffer sb = StringBuffer();
sb.write(
'<svg viewBox="0 0 ${bounds.right} ${bounds.bottom}" '
'width="${bounds.right}" height="${bounds.bottom}">');
sb.write('<svg viewBox="0 0 ${bounds.right} ${bounds.bottom}" '
'width="${bounds.right}" height="${bounds.bottom}">');
sb.write('<path ');
if (paint.style == PaintingStyle.stroke ||
(paint.strokeWidth != null && paint.strokeWidth != 0.0)) {
......@@ -209,16 +207,11 @@ html.Element pathToSvgElement(Path path, Paint paint,
sb.write('"></path>');
sb.write('</svg>');
final html.Element svgElement =
html.Element.html(sb.toString(), treeSanitizer: _NullTreeSanitizer());
html.Element.html(sb.toString(), treeSanitizer: NullTreeSanitizer());
svgElement.style.transform = 'translate(200px, 0px)';
return svgElement;
}
class _NullTreeSanitizer implements html.NodeTreeSanitizer {
@override
void sanitizeTree(html.Node node) {}
}
class ArcSample {
final Offset offset;
final bool largeArc;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册