未验证 提交 c99deb00 编写于 作者: Y Yegor 提交者: GitHub

CkPaint uses SkPaint (#19562)

上级 80639232
......@@ -5,10 +5,65 @@
/// Bindings for CanvasKit JavaScript API.
part of engine;
final js.JsObject _jsWindow = js.JsObject.fromBrowserObject(html.window);
/// This and [_jsObjectWrapper] below are used to convert `@JS`-backed
/// objects to [js.JsObject]s. To do that we use `@JS` to pass the object
/// to JavaScript (see [JsObjectWrapper]), then use this variable (which
/// uses `dart:js`) to read the value back, causing it to be wrapped in
/// [js.JsObject].
///
// TODO(yjbanov): this is a temporary hack until we fully migrate to @JS.
final js.JsObject _jsObjectWrapperLegacy = js.JsObject(js.context['Object']);
@JS('window.flutter_js_object_wrapper')
external JsObjectWrapper get _jsObjectWrapper;
void initializeCanvasKitBindings(js.JsObject canvasKit) {
// Because JsObject cannot be cast to a @JS type, we stash CanvasKit into
// a global and use the [canvasKitJs] getter to access it.
js.JsObject.fromBrowserObject(html.window)['flutter_canvas_kit'] = canvasKit;
_jsWindow['flutter_canvas_kit'] = canvasKit;
_jsWindow['flutter_js_object_wrapper'] = _jsObjectWrapperLegacy;
}
@JS()
class JsObjectWrapper {
external set skPaint(SkPaint? paint);
external set skMaskFilter(SkMaskFilter? filter);
external set skColorFilter(SkColorFilter? filter);
external set skImageFilter(SkImageFilter? filter);
}
/// Specific methods that wrap `@JS`-backed objects into a [js.JsObject]
/// for use with legacy `dart:js` API.
extension JsObjectWrappers on JsObjectWrapper {
js.JsObject wrapSkPaint(SkPaint paint) {
_jsObjectWrapper.skPaint = paint;
js.JsObject wrapped = _jsObjectWrapperLegacy['skPaint'];
_jsObjectWrapper.skPaint = null;
return wrapped;
}
js.JsObject wrapSkMaskFilter(SkMaskFilter filter) {
_jsObjectWrapper.skMaskFilter = filter;
js.JsObject wrapped = _jsObjectWrapperLegacy['skMaskFilter'];
_jsObjectWrapper.skMaskFilter = null;
return wrapped;
}
js.JsObject wrapSkColorFilter(SkColorFilter filter) {
_jsObjectWrapper.skColorFilter = filter;
js.JsObject wrapped = _jsObjectWrapperLegacy['skColorFilter'];
_jsObjectWrapper.skColorFilter = null;
return wrapped;
}
js.JsObject wrapSkImageFilter(SkImageFilter filter) {
_jsObjectWrapper.skImageFilter = filter;
js.JsObject wrapped = _jsObjectWrapperLegacy['skImageFilter'];
_jsObjectWrapper.skImageFilter = null;
return wrapped;
}
}
@JS('window.flutter_canvas_kit')
......@@ -317,12 +372,12 @@ class SkPaint {
external void setStrokeJoin(SkStrokeJoin join);
external void setAntiAlias(bool isAntiAlias);
external void setColorInt(int color);
external void setShader(SkShader shader);
external void setMaskFilter(SkMaskFilter maskFilter);
external void setShader(SkShader? shader);
external void setMaskFilter(SkMaskFilter? maskFilter);
external void setFilterQuality(SkFilterQuality filterQuality);
external void setColorFilter(SkColorFilter colorFilter);
external void setColorFilter(SkColorFilter? colorFilter);
external void setStrokeMiter(double miterLimit);
external void setImageFilter(SkImageFilter imageFilter);
external void setImageFilter(SkImageFilter? imageFilter);
}
@JS()
......@@ -373,3 +428,109 @@ Float32List toSkMatrixFromFloat32(Float32List matrix4) {
}
return skMatrix;
}
/// Converts an [offset] into an `[x, y]` pair stored in a `Float32List`.
///
/// The returned list can be passed to CanvasKit API that take points.
Float32List toSkPoint(ui.Offset offset) {
final Float32List point = Float32List(2);
point[0] = offset.dx;
point[1] = offset.dy;
return point;
}
/// Color stops used when the framework specifies `null`.
final Float32List _kDefaultSkColorStops = Float32List(2)
..[0] = 0
..[1] = 1;
/// Converts a list of color stops into a Skia-compatible JS array or color stops.
///
/// In Flutter `null` means two color stops `[0, 1]` that in Skia must be specified explicitly.
Float32List toSkColorStops(List<double>? colorStops) {
if (colorStops == null) {
return _kDefaultSkColorStops;
}
final int len = colorStops.length;
final Float32List skColorStops = Float32List(len);
for (int i = 0; i < len; i++) {
skColorStops[i] = colorStops[i];
}
return skColorStops;
}
@JS('Float32Array')
external _NativeFloat32ArrayType get _nativeFloat32ArrayType;
@JS()
class _NativeFloat32ArrayType {}
@JS('window.flutter_canvas_kit.Malloc')
external SkFloat32List _mallocFloat32List(
_NativeFloat32ArrayType float32ListType,
int size,
);
/// Allocates a [Float32List] backed by WASM memory, managed by
/// a [SkFloat32List].
SkFloat32List mallocFloat32List(int size) {
return _mallocFloat32List(_nativeFloat32ArrayType, size);
}
/// Wraps a [Float32List] backed by WASM memory.
///
/// This wrapper is necessary because the raw [Float32List] will get detached
/// when WASM grows its memory. Call [toTypedArray] to get a new instance
/// that's attached to the current WASM memory block.
@JS()
class SkFloat32List {
/// Returns the [Float32List] object backed by WASM memory.
///
/// Do not reuse the returned list across multiple WASM function/method
/// invocations that may lead to WASM memory to grow. When WASM memory
/// grows the [Float32List] object becomes "detached" and is no longer
/// usable. Instead, call this method every time you need to read from
/// or write to the list.
external Float32List toTypedArray();
}
/// Writes [color] information into the given [skColor] buffer.
Float32List _populateSkColor(SkFloat32List skColor, ui.Color color) {
final Float32List array = skColor.toTypedArray();
array[0] = color.red / 255.0;
array[1] = color.green / 255.0;
array[2] = color.blue / 255.0;
array[3] = color.alpha / 255.0;
return array;
}
/// Unpacks the [color] into CanvasKit-compatible representation stored
/// in a shared memory location #1.
///
/// Use this only for passing transient data to CanvasKit. Because the
/// memory is shared the value will not persist.
Float32List toSharedSkColor1(ui.Color color) {
return _populateSkColor(_sharedSkColor1, color);
}
final SkFloat32List _sharedSkColor1 = mallocFloat32List(4);
/// Unpacks the [color] into CanvasKit-compatible representation stored
/// in a shared memory location #2.
///
/// Use this only for passing transient data to CanvasKit. Because the
/// memory is shared the value will not persist.
Float32List toSharedSkColor2(ui.Color color) {
return _populateSkColor(_sharedSkColor2, color);
}
final SkFloat32List _sharedSkColor2 = mallocFloat32List(4);
/// Unpacks the [color] into CanvasKit-compatible representation stored
/// in a shared memory location #3.
///
/// Use this only for passing transient data to CanvasKit. Because the
/// memory is shared the value will not persist.
Float32List toSharedSkColor3(ui.Color color) {
return _populateSkColor(_sharedSkColor3, color);
}
final SkFloat32List _sharedSkColor3 = mallocFloat32List(4);
......@@ -19,30 +19,37 @@ class CkColorFilter extends ResurrectableSkiaObject {
CkColorFilter.srgbToLinearGamma(EngineColorFilter filter)
: _engineFilter = filter;
SkColorFilter? _skColorFilter;
js.JsObject _createSkiaObjectFromFilter() {
SkColorFilter skColorFilter;
switch (_engineFilter._type) {
case EngineColorFilter._TypeMode:
setSharedSkColor1(_engineFilter._color!);
return canvasKit['SkColorFilter'].callMethod('MakeBlend', <dynamic>[
sharedSkColor1,
makeSkBlendMode(_engineFilter._blendMode),
]);
skColorFilter = canvasKitJs.SkColorFilter.MakeBlend(
toSharedSkColor1(_engineFilter._color!),
toSkBlendMode(_engineFilter._blendMode!),
);
break;
case EngineColorFilter._TypeMatrix:
final js.JsArray<double> colorMatrix = js.JsArray<double>();
colorMatrix.length = 20;
final Float32List colorMatrix = Float32List(20);
final List<double> matrix = _engineFilter._matrix!;
for (int i = 0; i < 20; i++) {
colorMatrix[i] = _engineFilter._matrix![i];
colorMatrix[i] = matrix[i];
}
return canvasKit['SkColorFilter']
.callMethod('MakeMatrix', <js.JsArray>[colorMatrix]);
skColorFilter = canvasKitJs.SkColorFilter.MakeMatrix(colorMatrix);
break;
case EngineColorFilter._TypeLinearToSrgbGamma:
return canvasKit['SkColorFilter'].callMethod('MakeLinearToSRGBGamma');
skColorFilter = canvasKitJs.SkColorFilter.MakeLinearToSRGBGamma();
break;
case EngineColorFilter._TypeSrgbToLinearGamma:
return canvasKit['SkColorFilter'].callMethod('MakeSRGBToLinearGamma');
skColorFilter = canvasKitJs.SkColorFilter.MakeSRGBToLinearGamma();
break;
default:
throw StateError(
'Unknown mode ${_engineFilter._type} for ColorFilter.');
}
_skColorFilter = skColorFilter;
return _jsObjectWrapper.wrapSkColorFilter(skColorFilter);
}
@override
......
......@@ -8,8 +8,7 @@ part of engine;
/// Instantiates a [ui.Codec] backed by an `SkImage` from Skia.
void skiaInstantiateImageCodec(Uint8List list, Callback<ui.Codec> callback,
[int? width, int? height, int? format, int? rowBytes]) {
final js.JsObject? skAnimatedImage =
canvasKit.callMethod('MakeAnimatedImageFromEncoded', <Uint8List>[list]);
final SkAnimatedImage skAnimatedImage = canvasKitJs.MakeAnimatedImageFromEncoded(list);
final CkAnimatedImage animatedImage = CkAnimatedImage(skAnimatedImage);
final CkAnimatedImageCodec codec = CkAnimatedImageCodec(animatedImage);
callback(codec);
......@@ -17,36 +16,34 @@ void skiaInstantiateImageCodec(Uint8List list, Callback<ui.Codec> callback,
/// A wrapper for `SkAnimatedImage`.
class CkAnimatedImage implements ui.Image {
final js.JsObject? _skAnimatedImage;
final SkAnimatedImage _skAnimatedImage;
CkAnimatedImage(this._skAnimatedImage);
@override
void dispose() {
_skAnimatedImage!.callMethod('delete');
_skAnimatedImage.delete();
}
int? get frameCount => _skAnimatedImage!.callMethod('getFrameCount');
int get frameCount => _skAnimatedImage.getFrameCount();
/// Decodes the next frame and returns the frame duration.
Duration decodeNextFrame() {
final int durationMillis = _skAnimatedImage!.callMethod('decodeNextFrame');
final int durationMillis = _skAnimatedImage.decodeNextFrame();
return Duration(milliseconds: durationMillis);
}
int? get repetitionCount => _skAnimatedImage!.callMethod('getRepetitionCount');
int get repetitionCount => _skAnimatedImage.getRepetitionCount();
CkImage get currentFrameAsImage {
final js.JsObject? _currentFrame =
_skAnimatedImage!.callMethod('getCurrentFrame');
return CkImage(_currentFrame);
return CkImage(_skAnimatedImage.getCurrentFrame());
}
@override
int get width => _skAnimatedImage!.callMethod('width');
int get width => _skAnimatedImage.width();
@override
int get height => _skAnimatedImage!.callMethod('height');
int get height => _skAnimatedImage.height();
@override
Future<ByteData> toByteData(
......@@ -57,21 +54,20 @@ class CkAnimatedImage implements ui.Image {
/// A [ui.Image] backed by an `SkImage` from Skia.
class CkImage implements ui.Image {
js.JsObject? skImage;
SkImage skImage;
CkImage(this.skImage);
@override
void dispose() {
skImage!.callMethod('delete');
skImage = null;
skImage.delete();
}
@override
int get width => skImage!.callMethod('width');
int get width => skImage.width();
@override
int get height => skImage!.callMethod('height');
int get height => skImage.height();
@override
Future<ByteData> toByteData(
......@@ -93,10 +89,10 @@ class CkAnimatedImageCodec implements ui.Codec {
}
@override
int get frameCount => animatedImage!.frameCount!;
int get frameCount => animatedImage!.frameCount;
@override
int get repetitionCount => animatedImage!.repetitionCount!;
int get repetitionCount => animatedImage!.repetitionCount;
@override
Future<ui.FrameInfo> getNextFrame() {
......
......@@ -15,21 +15,24 @@ class CkImageFilter extends ResurrectableSkiaObject implements ui.ImageFilter {
final double _sigmaX;
final double _sigmaY;
SkImageFilter? _skImageFilter;
@override
js.JsObject createDefault() => _initSkiaObject();
@override
js.JsObject resurrect() => _initSkiaObject();
js.JsObject _initSkiaObject() => canvasKit['SkImageFilter'].callMethod(
'MakeBlur',
<dynamic>[
_sigmaX,
_sigmaY,
canvasKit['TileMode']['Clamp'],
null,
],
);
js.JsObject _initSkiaObject() {
final SkImageFilter skImageFilter = canvasKitJs.SkImageFilter.MakeBlur(
_sigmaX,
_sigmaY,
canvasKitJs.TileMode.Clamp,
null,
);
_skImageFilter = skImageFilter;
return _jsObjectWrapper.wrapSkImageFilter(skImageFilter);
}
@override
bool operator ==(Object other) {
......
......@@ -13,6 +13,8 @@ class CkMaskFilter extends ResurrectableSkiaObject {
final ui.BlurStyle _blurStyle;
final double _sigma;
SkMaskFilter? _skMaskFilter;
@override
js.JsObject createDefault() => _initSkiaObject();
......@@ -20,23 +22,12 @@ class CkMaskFilter extends ResurrectableSkiaObject {
js.JsObject resurrect() => _initSkiaObject();
js.JsObject _initSkiaObject() {
js.JsObject skBlurStyle;
switch (_blurStyle) {
case ui.BlurStyle.normal:
skBlurStyle = canvasKit['BlurStyle']['Normal'];
break;
case ui.BlurStyle.solid:
skBlurStyle = canvasKit['BlurStyle']['Solid'];
break;
case ui.BlurStyle.outer:
skBlurStyle = canvasKit['BlurStyle']['Outer'];
break;
case ui.BlurStyle.inner:
skBlurStyle = canvasKit['BlurStyle']['Inner'];
break;
}
return canvasKit
.callMethod('MakeBlurMaskFilter', <dynamic>[skBlurStyle, _sigma, true]);
final SkMaskFilter skMaskFilter = canvasKitJs.MakeBlurMaskFilter(
toSkBlurStyle(_blurStyle),
_sigma,
true,
);
_skMaskFilter = skMaskFilter;
return _jsObjectWrapper.wrapSkMaskFilter(skMaskFilter);
}
}
......@@ -2,7 +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;
/// The implementation of [ui.Paint] used by the CanvasKit backend.
......@@ -13,21 +12,16 @@ class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
CkPaint();
static const ui.Color _defaultPaintColor = ui.Color(0xFF000000);
static final js.JsObject? _skPaintStyleStroke =
canvasKit['PaintStyle']['Stroke'];
static final js.JsObject? _skPaintStyleFill = canvasKit['PaintStyle']['Fill'];
@override
ui.BlendMode get blendMode => _blendMode;
@override
set blendMode(ui.BlendMode value) {
if (_blendMode == value) {
return;
}
_blendMode = value;
_syncBlendMode(skiaObject);
}
void _syncBlendMode(js.JsObject object) {
final js.JsObject? skBlendMode = makeSkBlendMode(_blendMode);
object.callMethod('setBlendMode', <js.JsObject?>[skBlendMode]);
_skPaint.setBlendMode(toSkBlendMode(value));
}
ui.BlendMode _blendMode = ui.BlendMode.srcOver;
......@@ -37,21 +31,11 @@ class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
@override
set style(ui.PaintingStyle value) {
_style = value;
_syncStyle(skiaObject);
}
void _syncStyle(js.JsObject object) {
js.JsObject? skPaintStyle;
switch (_style) {
case ui.PaintingStyle.stroke:
skPaintStyle = _skPaintStyleStroke;
break;
case ui.PaintingStyle.fill:
skPaintStyle = _skPaintStyleFill;
break;
if (_style == value) {
return;
}
object.callMethod('setStyle', <js.JsObject?>[skPaintStyle]);
_style = value;
_skPaint.setStyle(toSkPaintStyle(value));
}
ui.PaintingStyle _style = ui.PaintingStyle.fill;
......@@ -60,32 +44,37 @@ class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
double get strokeWidth => _strokeWidth;
@override
set strokeWidth(double value) {
if (_strokeWidth == value) {
return;
}
_strokeWidth = value;
_syncStrokeWidth(skiaObject);
}
void _syncStrokeWidth(js.JsObject object) {
object.callMethod('setStrokeWidth', <double>[strokeWidth]);
_skPaint.setStrokeWidth(value);
}
double _strokeWidth = 0.0;
// TODO(yjbanov): implement
@override
ui.StrokeCap get strokeCap => _strokeCap;
@override
set strokeCap(ui.StrokeCap value) {
if (_strokeCap == value) {
return;
}
_strokeCap = value;
_skPaint.setStrokeCap(toSkStrokeCap(value));
}
ui.StrokeCap _strokeCap = ui.StrokeCap.butt;
// TODO(yjbanov): implement
@override
ui.StrokeJoin get strokeJoin => _strokeJoin;
@override
set strokeJoin(ui.StrokeJoin value) {
if (_strokeJoin == value) {
return;
}
_strokeJoin = value;
_skPaint.setStrokeJoin(toSkStrokeJoin(value));
}
ui.StrokeJoin _strokeJoin = ui.StrokeJoin.miter;
......@@ -94,12 +83,11 @@ class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
bool get isAntiAlias => _isAntiAlias;
@override
set isAntiAlias(bool value) {
if (_isAntiAlias == value) {
return;
}
_isAntiAlias = value;
_syncAntiAlias(skiaObject);
}
void _syncAntiAlias(js.JsObject object) {
object.callMethod('setAntiAlias', <bool>[_isAntiAlias]);
_skPaint.setAntiAlias(value);
}
bool _isAntiAlias = true;
......@@ -108,14 +96,11 @@ class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
ui.Color get color => _color;
@override
set color(ui.Color value) {
if (_color == value) {
return;
}
_color = value;
_syncColor(skiaObject);
}
void _syncColor(js.JsObject object) {
object.callMethod('setColorInt', <int>[
_color.value,
]);
_skPaint.setColorInt(value.value);
}
ui.Color _color = _defaultPaintColor;
......@@ -134,16 +119,11 @@ class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
ui.Shader? get shader => _shader as ui.Shader?;
@override
set shader(ui.Shader? value) {
_shader = value as EngineShader?;
_syncShader(skiaObject);
}
void _syncShader(js.JsObject object) {
js.JsObject? skShader;
if (_shader != null) {
skShader = _shader!.createSkiaShader();
if (_shader == value) {
return;
}
object.callMethod('setShader', <js.JsObject?>[skShader]);
_shader = value as EngineShader?;
_skPaint.setShader(_shader?.createSkiaShader());
}
EngineShader? _shader;
......@@ -152,48 +132,33 @@ class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
ui.MaskFilter? get maskFilter => _maskFilter;
@override
set maskFilter(ui.MaskFilter? value) {
if (value == _maskFilter) {
return;
}
_maskFilter = value;
_syncMaskFilter(skiaObject);
}
void _syncMaskFilter(js.JsObject object) {
CkMaskFilter? skMaskFilter;
if (_maskFilter != null) {
final ui.BlurStyle blurStyle = _maskFilter!.webOnlyBlurStyle;
final double sigma = _maskFilter!.webOnlySigma;
skMaskFilter = CkMaskFilter.blur(blurStyle, sigma);
if (value != null) {
_ckMaskFilter = CkMaskFilter.blur(
value.webOnlyBlurStyle,
value.webOnlySigma,
);
} else {
_ckMaskFilter = null;
}
object.callMethod('setMaskFilter', <js.JsObject?>[skMaskFilter?.skiaObject]);
_skPaint.setMaskFilter(_ckMaskFilter?._skMaskFilter);
}
ui.MaskFilter? _maskFilter;
CkMaskFilter? _ckMaskFilter;
@override
ui.FilterQuality get filterQuality => _filterQuality;
@override
set filterQuality(ui.FilterQuality value) {
_filterQuality = value;
_syncFilterQuality(skiaObject);
}
void _syncFilterQuality(js.JsObject? object) {
js.JsObject? skFilterQuality;
switch (_filterQuality) {
case ui.FilterQuality.none:
skFilterQuality = canvasKit['FilterQuality']['None'];
break;
case ui.FilterQuality.low:
skFilterQuality = canvasKit['FilterQuality']['Low'];
break;
case ui.FilterQuality.medium:
skFilterQuality = canvasKit['FilterQuality']['Medium'];
break;
case ui.FilterQuality.high:
skFilterQuality = canvasKit['FilterQuality']['High'];
break;
if (_filterQuality == value) {
return;
}
object!.callMethod('setFilterQuality', <js.JsObject?>[skFilterQuality]);
_filterQuality = value;
_skPaint.setFilterQuality(toSkFilterQuality(value));
}
ui.FilterQuality _filterQuality = ui.FilterQuality.none;
......@@ -202,27 +167,27 @@ class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
ui.ColorFilter? get colorFilter => _colorFilter;
@override
set colorFilter(ui.ColorFilter? value) {
_colorFilter = value as EngineColorFilter?;
_syncColorFilter(skiaObject);
}
void _syncColorFilter(js.JsObject object) {
js.JsObject? skColorFilterJs;
if (_colorFilter != null) {
CkColorFilter? skFilter = _colorFilter!._toCkColorFilter();
skColorFilterJs = skFilter!.skiaObject;
if (_colorFilter == value) {
return;
}
object.callMethod('setColorFilter', <js.JsObject?>[skColorFilterJs]);
final EngineColorFilter? engineValue = value as EngineColorFilter?;
_colorFilter = engineValue;
_ckColorFilter = engineValue?._toCkColorFilter();
_skPaint.setColorFilter(_ckColorFilter?._skColorFilter);
}
EngineColorFilter? _colorFilter;
CkColorFilter? _ckColorFilter;
// TODO(yjbanov): implement
@override
double get strokeMiterLimit => _strokeMiterLimit;
@override
set strokeMiterLimit(double value) {
if (_strokeMiterLimit == value) {
return;
}
_strokeMiterLimit = value;
_skPaint.setStrokeMiter(value);
}
double _strokeMiterLimit = 0.0;
......@@ -231,42 +196,38 @@ class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
ui.ImageFilter? get imageFilter => _imageFilter;
@override
set imageFilter(ui.ImageFilter? value) {
_imageFilter = value as CkImageFilter?;
_syncImageFilter(skiaObject);
}
void _syncImageFilter(js.JsObject object) {
js.JsObject? imageFilterJs;
if (_imageFilter != null) {
imageFilterJs = _imageFilter!.skiaObject;
if (_imageFilter == value) {
return;
}
object.callMethod('setImageFilter', <js.JsObject?>[imageFilterJs]);
_imageFilter = value as CkImageFilter?;
_skPaint.setImageFilter(_imageFilter?._skImageFilter);
}
CkImageFilter? _imageFilter;
late SkPaint _skPaint;
@override
js.JsObject createDefault() {
final obj = js.JsObject(canvasKit['SkPaint']);
// Sync fields whose Skia defaults are different from Flutter's.
_syncAntiAlias(obj);
_syncColor(obj);
return obj;
_skPaint = SkPaint();
_skPaint.setAntiAlias(_isAntiAlias);
_skPaint.setColorInt(_color.value);
return _jsObjectWrapper.wrapSkPaint(_skPaint);
}
@override
js.JsObject resurrect() {
final obj = js.JsObject(canvasKit['SkPaint']);
_syncBlendMode(obj);
_syncStyle(obj);
_syncStrokeWidth(obj);
_syncAntiAlias(obj);
_syncColor(obj);
_syncShader(obj);
_syncMaskFilter(obj);
_syncColorFilter(obj);
_syncImageFilter(obj);
_syncFilterQuality(obj);
return obj;
_skPaint = SkPaint();
_skPaint.setBlendMode(toSkBlendMode(_blendMode));
_skPaint.setStyle(toSkPaintStyle(_style));
_skPaint.setStrokeWidth(_strokeWidth);
_skPaint.setAntiAlias(_isAntiAlias);
_skPaint.setColorInt(_color.value);
_skPaint.setShader(_shader?.createSkiaShader());
_skPaint.setMaskFilter(_ckMaskFilter?._skMaskFilter);
_skPaint.setColorFilter(_ckColorFilter?._skColorFilter);
_skPaint.setImageFilter(_imageFilter?._skImageFilter);
_skPaint.setFilterQuality(toSkFilterQuality(_filterQuality));
return _jsObjectWrapper.wrapSkPaint(_skPaint);
}
}
......@@ -20,7 +20,7 @@ bool _matrix4IsValid(Float32List matrix4) {
abstract class EngineShader {
/// Create a shader for use in the Skia backend.
js.JsObject? createSkiaShader();
SkShader createSkiaShader();
}
abstract class EngineGradient implements ui.Gradient, EngineShader {
......@@ -63,7 +63,7 @@ class GradientSweep extends EngineGradient {
final Float32List? matrix4;
@override
js.JsObject createSkiaShader() {
SkShader createSkiaShader() {
throw UnimplementedError();
}
}
......@@ -155,18 +155,17 @@ class GradientLinear extends EngineGradient {
}
@override
js.JsObject? createSkiaShader() {
SkShader createSkiaShader() {
assert(experimentalUseSkia);
var jsColors = makeColorList(colors);
return canvasKit['SkShader'].callMethod('MakeLinearGradient', <dynamic>[
makeSkPoint(from),
makeSkPoint(to),
return canvasKitJs.SkShader.MakeLinearGradient(
toSkPoint(from),
toSkPoint(to),
jsColors,
makeSkiaColorStops(colorStops),
tileMode.index,
]);
toSkColorStops(colorStops),
toSkTileMode(tileMode),
);
}
}
......@@ -211,20 +210,20 @@ class GradientRadial extends EngineGradient {
}
@override
js.JsObject? createSkiaShader() {
SkShader createSkiaShader() {
assert(experimentalUseSkia);
var jsColors = makeColorList(colors);
return canvasKit['SkShader'].callMethod('MakeRadialGradient', <dynamic>[
makeSkPoint(center),
return canvasKitJs.SkShader.MakeRadialGradient(
toSkPoint(center),
radius,
jsColors,
makeSkiaColorStops(colorStops),
tileMode.index,
matrix4 != null ? makeSkMatrixFromFloat32(matrix4) : null,
toSkColorStops(colorStops),
toSkTileMode(tileMode),
matrix4 != null ? toSkMatrixFromFloat32(matrix4!) : null,
0,
]);
);
}
}
......@@ -248,23 +247,22 @@ class GradientConical extends EngineGradient {
}
@override
js.JsObject? createSkiaShader() {
SkShader createSkiaShader() {
assert(experimentalUseSkia);
var jsColors = makeColorList(colors);
return canvasKit['SkShader']
.callMethod('MakeTwoPointConicalGradient', <dynamic>[
makeSkPoint(focal),
return canvasKitJs.SkShader.MakeTwoPointConicalGradient(
toSkPoint(focal),
focalRadius,
makeSkPoint(center),
toSkPoint(center),
radius,
jsColors,
makeSkiaColorStops(colorStops),
tileMode.index,
matrix4 != null ? makeSkMatrixFromFloat32(matrix4) : null,
toSkColorStops(colorStops),
toSkTileMode(tileMode),
matrix4 != null ? toSkMatrixFromFloat32(matrix4!) : null,
0,
]);
);
}
}
......@@ -293,18 +291,6 @@ class EngineImageFilter implements ui.ImageFilter {
}
}
js.JsObject? _skTileMode(ui.TileMode tileMode) {
switch (tileMode) {
case ui.TileMode.clamp:
return canvasKit['TileMode']['Clamp'];
case ui.TileMode.repeated:
return canvasKit['TileMode']['Repeat'];
case ui.TileMode.mirror:
default:
return canvasKit['TileMode']['Mirror'];
}
}
/// Backend implementation of [ui.ImageShader].
class EngineImageShader implements ui.ImageShader, EngineShader {
EngineImageShader(
......@@ -316,6 +302,8 @@ class EngineImageShader implements ui.ImageShader, EngineShader {
final Float64List matrix4;
final CkImage _skImage;
js.JsObject? createSkiaShader() => _skImage.skImage!.callMethod(
'makeShader', <dynamic>[_skTileMode(tileModeX), _skTileMode(tileModeY)]);
SkShader createSkiaShader() => _skImage.skImage.makeShader(
toSkTileMode(tileModeX),
toSkTileMode(tileModeY),
);
}
......@@ -33,6 +33,11 @@ void main() {
_maskFilterTests();
_colorFilterTests();
_imageFilterTests();
_mallocTests();
_sharedColorTests();
_toSkPointTests();
_toSkColorStopsTests();
_toSkMatrixFromFloat32Tests();
},
// This test failed on iOS Safari.
// TODO: https://github.com/flutter/flutter/issues/60040
......@@ -258,11 +263,20 @@ void _paintTests() {
paint.setAntiAlias(true);
paint.setColorInt(0x00FFCCAA);
paint.setShader(_makeTestShader());
// TODO(yjbanov): paint.setMaskFilter
paint.setMaskFilter(canvasKitJs.MakeBlurMaskFilter(
canvasKitJs.BlurStyle.Outer,
2.0,
true,
));
paint.setFilterQuality(canvasKitJs.FilterQuality.High);
// TODO(yjbanov): paint.setColorFilter
paint.setColorFilter(canvasKitJs.SkColorFilter.MakeLinearToSRGBGamma());
paint.setStrokeMiter(1.4);
// TODO(yjbanov): paint.setImageFilter
paint.setImageFilter(canvasKitJs.SkImageFilter.MakeBlur(
1,
2,
canvasKitJs.TileMode.Repeat,
null,
));
});
}
......@@ -331,6 +345,104 @@ void _imageFilterTests() {
});
}
void _mallocTests() {
test('SkFloat32List', () {
for (int size = 0; size < 1000; size++) {
final SkFloat32List skList = mallocFloat32List(4);
expect(skList, isNotNull);
expect(skList.toTypedArray().length, 4);
}
});
}
void _sharedColorTests() {
test('toSharedSkColor1', () {
expect(
toSharedSkColor1(const ui.Color(0xAABBCCDD)),
Float32List(4)
..[0] = 0xBB / 255.0
..[1] = 0xCC / 255.0
..[2] = 0xDD / 255.0
..[3] = 0xAA / 255.0,
);
});
test('toSharedSkColor2', () {
expect(
toSharedSkColor2(const ui.Color(0xAABBCCDD)),
Float32List(4)
..[0] = 0xBB / 255.0
..[1] = 0xCC / 255.0
..[2] = 0xDD / 255.0
..[3] = 0xAA / 255.0,
);
});
test('toSharedSkColor3', () {
expect(
toSharedSkColor3(const ui.Color(0xAABBCCDD)),
Float32List(4)
..[0] = 0xBB / 255.0
..[1] = 0xCC / 255.0
..[2] = 0xDD / 255.0
..[3] = 0xAA / 255.0,
);
});
}
void _toSkPointTests() {
test('toSkPoint', () {
expect(
toSkPoint(const ui.Offset(4, 5)),
Float32List(2)
..[0] = 4.0
..[1] = 5.0,
);
});
}
void _toSkColorStopsTests() {
test('toSkColorStops default', () {
expect(
toSkColorStops(null),
Float32List(2)
..[0] = 0
..[1] = 1,
);
});
test('toSkColorStops custom', () {
expect(
toSkColorStops(<double>[1, 2, 3, 4]),
Float32List(4)
..[0] = 1
..[1] = 2
..[2] = 3
..[3] = 4,
);
});
}
void _toSkMatrixFromFloat32Tests() {
test('toSkMatrixFromFloat32', () {
final Matrix4 matrix = Matrix4.identity()
..translate(1, 2, 3)
..rotateZ(4);
expect(
toSkMatrixFromFloat32(matrix.storage),
Float32List.fromList(<double>[
-0.6536436080932617,
0.756802499294281,
1,
-0.756802499294281,
-0.6536436080932617,
2,
-0.0,
0,
1,
])
);
});
}
final Uint8List kTransparentImage = Uint8List.fromList(<int>[
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49,
0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册