mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Update CanvasKit to 0.7.0 and flesh out painting (flutter/engine#13240)
* Update CanvasKit to 0.7.0 and flesh out painting This allows us to fix some bugs in the CanvasKit backend. - Implement RRect where the radii are different - Implement drawDRRect - Implement ColorFilter - Implement the correct `arcTo` for `arcToPoint` * update licenses * Respond to review comments - Add TODO to avoid unnecessary conversions - Don't set CanvasKit to default - Fix licenses file * Add ==, hashCode, and toString back to ColorFilter API
This commit is contained in:
parent
3018e7bcac
commit
02dccbb30e
@ -354,7 +354,9 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/assets.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/bitmap_canvas.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/browser_detection.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/browser_location.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/color_filter.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/canvas.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/color_filter.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/engine_delegate.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/fonts.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/image.dart
|
||||
|
||||
@ -23,7 +23,9 @@ part 'engine/assets.dart';
|
||||
part 'engine/bitmap_canvas.dart';
|
||||
part 'engine/browser_detection.dart';
|
||||
part 'engine/browser_location.dart';
|
||||
part 'engine/color_filter.dart';
|
||||
part 'engine/compositor/canvas.dart';
|
||||
part 'engine/compositor/color_filter.dart';
|
||||
part 'engine/compositor/engine_delegate.dart';
|
||||
part 'engine/compositor/fonts.dart';
|
||||
part 'engine/compositor/image.dart';
|
||||
|
||||
185
engine/src/flutter/lib/web_ui/lib/src/engine/color_filter.dart
Normal file
185
engine/src/flutter/lib/web_ui/lib/src/engine/color_filter.dart
Normal file
@ -0,0 +1,185 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
part of engine;
|
||||
|
||||
/// A description of a color filter to apply when drawing a shape or compositing
|
||||
/// a layer with a particular [Paint]. A color filter is a function that takes
|
||||
/// two colors, and outputs one color. When applied during compositing, it is
|
||||
/// independently applied to each pixel of the layer being drawn before the
|
||||
/// entire layer is merged with the destination.
|
||||
///
|
||||
/// Instances of this class are used with [Paint.colorFilter] on [Paint]
|
||||
/// objects.
|
||||
class EngineColorFilter implements ui.ColorFilter {
|
||||
/// Creates a color filter that applies the blend mode given as the second
|
||||
/// argument. The source color is the one given as the first argument, and the
|
||||
/// destination color is the one from the layer being composited.
|
||||
///
|
||||
/// 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 EngineColorFilter.mode(ui.Color color, ui.BlendMode blendMode)
|
||||
: _color = color,
|
||||
_blendMode = blendMode,
|
||||
_matrix = null,
|
||||
_type = _TypeMode;
|
||||
|
||||
/// Construct a color filter that transforms a color by a 5x5 matrix, where
|
||||
/// the fifth row is implicitly added in an identity configuration.
|
||||
///
|
||||
/// Every pixel's color value, repsented as an `[R, G, B, A]`, is matrix
|
||||
/// multiplied to create a new color:
|
||||
///
|
||||
/// ```text
|
||||
/// | R' | | a00 a01 a02 a03 a04 | | R |
|
||||
/// | G' | | a10 a11 a22 a33 a44 | | G |
|
||||
/// | B' | = | a20 a21 a22 a33 a44 | * | B |
|
||||
/// | A' | | a30 a31 a22 a33 a44 | | A |
|
||||
/// | 1 | | 0 0 0 0 1 | | 1 |
|
||||
/// ```
|
||||
///
|
||||
/// The matrix is in row-major order and the translation column is specified
|
||||
/// in unnormalized, 0...255, space. For example, the identity matrix is:
|
||||
///
|
||||
/// ```
|
||||
/// const ColorMatrix identity = ColorFilter.matrix(<double>[
|
||||
/// 1, 0, 0, 0, 0,
|
||||
/// 0, 1, 0, 0, 0,
|
||||
/// 0, 0, 1, 0, 0,
|
||||
/// 0, 0, 0, 1, 0,
|
||||
/// ]);
|
||||
/// ```
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// An inversion color matrix:
|
||||
///
|
||||
/// ```
|
||||
/// const ColorFilter invert = ColorFilter.matrix(<double>[
|
||||
/// -1, 0, 0, 0, 255,
|
||||
/// 0, -1, 0, 0, 255,
|
||||
/// 0, 0, -1, 0, 255,
|
||||
/// 0, 0, 0, 1, 0,
|
||||
/// ]);
|
||||
/// ```
|
||||
///
|
||||
/// A sepia-toned color matrix (values based on the [Filter Effects Spec](https://www.w3.org/TR/filter-effects-1/#sepiaEquivalent)):
|
||||
///
|
||||
/// ```
|
||||
/// const ColorFilter sepia = ColorFilter.matrix(<double>[
|
||||
/// 0.393, 0.769, 0.189, 0, 0,
|
||||
/// 0.349, 0.686, 0.168, 0, 0,
|
||||
/// 0.272, 0.534, 0.131, 0, 0,
|
||||
/// 0, 0, 0, 1, 0,
|
||||
/// ]);
|
||||
/// ```
|
||||
///
|
||||
/// A greyscale color filter (values based on the [Filter Effects Spec](https://www.w3.org/TR/filter-effects-1/#grayscaleEquivalent)):
|
||||
///
|
||||
/// ```
|
||||
/// const ColorFilter greyscale = ColorFilter.matrix(<double>[
|
||||
/// 0.2126, 0.7152, 0.0722, 0, 0,
|
||||
/// 0.2126, 0.7152, 0.0722, 0, 0,
|
||||
/// 0.2126, 0.7152, 0.0722, 0, 0,
|
||||
/// 0, 0, 0, 1, 0,
|
||||
/// ]);
|
||||
/// ```
|
||||
const EngineColorFilter.matrix(List<double> matrix)
|
||||
: _color = null,
|
||||
_blendMode = null,
|
||||
_matrix = matrix,
|
||||
_type = _TypeMatrix;
|
||||
|
||||
/// Construct a color filter that applies the sRGB gamma curve to the RGB
|
||||
/// channels.
|
||||
const EngineColorFilter.linearToSrgbGamma()
|
||||
: _color = null,
|
||||
_blendMode = null,
|
||||
_matrix = null,
|
||||
_type = _TypeLinearToSrgbGamma;
|
||||
|
||||
/// Creates a color filter that applies the inverse of the sRGB gamma curve
|
||||
/// to the RGB channels.
|
||||
const EngineColorFilter.srgbToLinearGamma()
|
||||
: _color = null,
|
||||
_blendMode = null,
|
||||
_matrix = null,
|
||||
_type = _TypeSrgbToLinearGamma;
|
||||
|
||||
final ui.Color _color;
|
||||
final ui.BlendMode _blendMode;
|
||||
final List<double> _matrix;
|
||||
final int _type;
|
||||
|
||||
// The type of SkColorFilter class to create for Skia.
|
||||
// These constants must be kept in sync with ColorFilterType in paint.cc.
|
||||
static const int _TypeNone = 0; // null
|
||||
static const int _TypeMode = 1; // MakeModeFilter
|
||||
static const int _TypeMatrix = 2; // MakeMatrixFilterRowMajor255
|
||||
static const int _TypeLinearToSrgbGamma = 3; // MakeLinearToSRGBGamma
|
||||
static const int _TypeSrgbToLinearGamma = 4; // MakeSRGBToLinearGamma
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
if (other is! EngineColorFilter) {
|
||||
return false;
|
||||
}
|
||||
final EngineColorFilter typedOther = other;
|
||||
|
||||
if (_type != typedOther._type) {
|
||||
return false;
|
||||
}
|
||||
if (!_listEquals<double>(_matrix, typedOther._matrix)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _color == typedOther._color && _blendMode == typedOther._blendMode;
|
||||
}
|
||||
|
||||
SkColorFilter _toSkColorFilter() {
|
||||
switch (_type) {
|
||||
case _TypeMode:
|
||||
if (_color == null || _blendMode == null) {
|
||||
return null;
|
||||
}
|
||||
return SkColorFilter.mode(this);
|
||||
case _TypeMatrix:
|
||||
if (_matrix == null) {
|
||||
return null;
|
||||
}
|
||||
assert(_matrix.length == 20, 'Color Matrix must have 20 entries.');
|
||||
return SkColorFilter.matrix(this);
|
||||
case _TypeLinearToSrgbGamma:
|
||||
return SkColorFilter.linearToSrgbGamma(this);
|
||||
case _TypeSrgbToLinearGamma:
|
||||
return SkColorFilter.srgbToLinearGamma(this);
|
||||
default:
|
||||
throw StateError('Unknown mode $_type for ColorFilter.');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => ui.hashValues(_color, _blendMode, ui.hashList(_matrix), _type);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
switch (_type) {
|
||||
case _TypeMode:
|
||||
return 'ColorFilter.mode($_color, $_blendMode)';
|
||||
case _TypeMatrix:
|
||||
return 'ColorFilter.matrix($_matrix)';
|
||||
case _TypeLinearToSrgbGamma:
|
||||
return 'ColorFilter.linearToSrgbGamma()';
|
||||
case _TypeSrgbToLinearGamma:
|
||||
return 'ColorFilter.srgbToLinearGamma()';
|
||||
default:
|
||||
return 'Unknown ColorFilter type. This is an error. If you\'re seeing this, please file an issue at https://github.com/flutter/flutter/issues/new.';
|
||||
}
|
||||
}
|
||||
|
||||
List<dynamic> webOnlySerializeToCssPaint() {
|
||||
throw UnsupportedError('ColorFilter for CSS paint not yet supported');
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
part of engine;
|
||||
|
||||
/// A [ui.ColorFilter] backed by Skia's [SkColorFilter].
|
||||
class SkColorFilter {
|
||||
js.JsObject skColorFilter;
|
||||
|
||||
SkColorFilter.mode(EngineColorFilter filter) {
|
||||
skColorFilter =
|
||||
canvasKit['SkColorFilter'].callMethod('MakeBlend', <dynamic>[
|
||||
filter._color.value,
|
||||
makeSkBlendMode(filter._blendMode),
|
||||
]);
|
||||
}
|
||||
|
||||
SkColorFilter.matrix(EngineColorFilter filter) {
|
||||
// TODO(het): Find a way to remove these array conversions.
|
||||
final js.JsArray colorMatrix = js.JsArray();
|
||||
colorMatrix.length = 20;
|
||||
for (int i = 0; i < 20; i++) {
|
||||
colorMatrix[i] = filter._matrix[i];
|
||||
}
|
||||
skColorFilter = canvasKit['SkColorFilter']
|
||||
.callMethod('MakeMatrix', <js.JsArray>[colorMatrix]);
|
||||
}
|
||||
|
||||
SkColorFilter.linearToSrgbGamma(EngineColorFilter filter) {
|
||||
skColorFilter = canvasKit['SkColorFilter'].callMethod('MakeLinearToSRGBGamma');
|
||||
}
|
||||
|
||||
SkColorFilter.srgbToLinearGamma(EngineColorFilter filter) {
|
||||
skColorFilter = canvasKit['SkColorFilter'].callMethod('MakeSRGBToLinearGamma');
|
||||
}
|
||||
}
|
||||
@ -9,7 +9,7 @@ const bool experimentalUseSkia =
|
||||
bool.fromEnvironment('FLUTTER_WEB_USE_SKIA', defaultValue: false);
|
||||
|
||||
/// The URL to use when downloading the CanvasKit script and associated wasm.
|
||||
const String canvasKitBaseUrl = 'https://unpkg.com/canvaskit-wasm@0.6.0/bin/';
|
||||
const String canvasKitBaseUrl = 'https://unpkg.com/canvaskit-wasm@0.7.0/bin/';
|
||||
|
||||
/// Initialize the Skia backend.
|
||||
///
|
||||
|
||||
@ -114,26 +114,15 @@ class SkPath implements ui.Path {
|
||||
double rotation = 0.0,
|
||||
bool largeArc = false,
|
||||
bool clockwise = true}) {
|
||||
assert(rotation == 0.0,
|
||||
'Skia backend does not support `arcToPoint` rotation.');
|
||||
assert(!largeArc, 'Skia backend does not support `arcToPoint` largeArc.');
|
||||
assert(radius.x == radius.y,
|
||||
'Skia backend does not support `arcToPoint` with elliptical radius.');
|
||||
|
||||
// TODO(het): Remove asserts above and use the correct override of `arcTo`
|
||||
// when it is available in CanvasKit.
|
||||
// The only `arcTo` method exposed in CanvasKit is:
|
||||
// arcTo(x1, y1, x2, y2, radius)
|
||||
final ui.Offset lastPoint = _getCurrentPoint();
|
||||
_skPath.callMethod('arcTo',
|
||||
<double>[lastPoint.dx, lastPoint.dy, arcEnd.dx, arcEnd.dy, radius.x]);
|
||||
}
|
||||
|
||||
ui.Offset _getCurrentPoint() {
|
||||
final int pointCount = _skPath.callMethod('countPoints');
|
||||
final js.JsObject lastPoint =
|
||||
_skPath.callMethod('getPoint', <int>[pointCount - 1]);
|
||||
return ui.Offset(lastPoint[0], lastPoint[1]);
|
||||
_skPath.callMethod('arcTo', <dynamic>[
|
||||
radius.x,
|
||||
radius.y,
|
||||
rotation,
|
||||
!largeArc,
|
||||
!clockwise,
|
||||
arcEnd.dx,
|
||||
arcEnd.dy,
|
||||
]);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@ -99,12 +99,12 @@ class SkRecordingCanvas implements RecordingCanvas {
|
||||
|
||||
@override
|
||||
void drawCircle(ui.Offset c, double radius, ui.Paint paint) {
|
||||
final js.JsObject skPaint = makeSkPaint(paint);
|
||||
// TODO(het): Use `drawCircle` when CanvasKit makes it available.
|
||||
// Since CanvasKit does not expose `drawCircle`, use `drawOval` instead.
|
||||
final js.JsObject skRect = makeSkRect(ui.Rect.fromLTWH(
|
||||
c.dx - radius, c.dy - radius, 2.0 * radius, 2.0 * radius));
|
||||
skCanvas.callMethod('drawOval', <js.JsObject>[skRect, skPaint]);
|
||||
skCanvas.callMethod('drawCircle', <dynamic>[
|
||||
c.dx,
|
||||
c.dy,
|
||||
radius,
|
||||
makeSkPaint(paint),
|
||||
]);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -114,7 +114,11 @@ class SkRecordingCanvas implements RecordingCanvas {
|
||||
|
||||
@override
|
||||
void drawDRRect(ui.RRect outer, ui.RRect inner, ui.Paint paint) {
|
||||
throw 'drawDRRect';
|
||||
skCanvas.callMethod('drawDRRect', <js.JsObject>[
|
||||
makeSkRRect(outer),
|
||||
makeSkRRect(inner),
|
||||
makeSkPaint(paint),
|
||||
]);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -190,19 +194,8 @@ class SkRecordingCanvas implements RecordingCanvas {
|
||||
|
||||
@override
|
||||
void drawRRect(ui.RRect rrect, ui.Paint paint) {
|
||||
// Since CanvasKit does not expose `drawRRect` we have to make do with
|
||||
// `drawRoundRect`. The downside of `drawRoundRect` is that all of the
|
||||
// corner radii must be the same.
|
||||
assert(
|
||||
rrect.tlRadius == rrect.trRadius &&
|
||||
rrect.tlRadius == rrect.brRadius &&
|
||||
rrect.tlRadius == rrect.blRadius,
|
||||
'CanvasKit only supports drawing RRects where the radii are all the same.',
|
||||
);
|
||||
skCanvas.callMethod('drawRoundRect', <dynamic>[
|
||||
makeSkRect(rrect.outerRect),
|
||||
rrect.tlRadiusX,
|
||||
rrect.tlRadiusY,
|
||||
skCanvas.callMethod('drawRRect', <js.JsObject>[
|
||||
makeSkRRect(rrect),
|
||||
makeSkPaint(paint),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -9,6 +9,20 @@ js.JsObject makeSkRect(ui.Rect rect) {
|
||||
<double>[rect.left, rect.top, rect.right, rect.bottom]);
|
||||
}
|
||||
|
||||
js.JsObject makeSkRRect(ui.RRect rrect) {
|
||||
return js.JsObject.jsify({
|
||||
'rect': makeSkRect(rrect.outerRect),
|
||||
'rx1': rrect.tlRadiusX,
|
||||
'ry1': rrect.tlRadiusY,
|
||||
'rx2': rrect.trRadiusX,
|
||||
'ry2': rrect.trRadiusY,
|
||||
'rx3': rrect.brRadiusX,
|
||||
'ry3': rrect.brRadiusY,
|
||||
'rx4': rrect.blRadiusX,
|
||||
'ry4': rrect.blRadiusY,
|
||||
});
|
||||
}
|
||||
|
||||
js.JsArray<double> makeSkPoint(ui.Offset point) {
|
||||
final js.JsArray<double> skPoint = js.JsArray<double>();
|
||||
skPoint.length = 2;
|
||||
@ -142,6 +156,12 @@ js.JsObject makeSkPaint(ui.Paint paint) {
|
||||
skPaint.callMethod('setMaskFilter', <js.JsObject>[skMaskFilter]);
|
||||
}
|
||||
|
||||
if (paint.colorFilter != null) {
|
||||
EngineColorFilter engineFilter = paint.colorFilter;
|
||||
SkColorFilter skFilter = engineFilter._toSkColorFilter();
|
||||
skPaint.callMethod('setColorFilter', <js.JsObject>[skFilter.skColorFilter]);
|
||||
}
|
||||
|
||||
return skPaint;
|
||||
}
|
||||
|
||||
|
||||
@ -1388,7 +1388,7 @@ abstract class Image {
|
||||
///
|
||||
/// Instances of this class are used with [Paint.colorFilter] on [Paint]
|
||||
/// objects.
|
||||
class ColorFilter {
|
||||
abstract class ColorFilter {
|
||||
/// Creates a color filter that applies the blend mode given as the second
|
||||
/// argument. The source color is the one given as the first argument, and the
|
||||
/// destination color is the one from the layer being composited.
|
||||
@ -1396,9 +1396,7 @@ class 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 ColorFilter.mode(Color color, BlendMode blendMode)
|
||||
: _color = color,
|
||||
_blendMode = blendMode;
|
||||
const factory ColorFilter.mode(Color color, BlendMode blendMode) = engine.EngineColorFilter.mode;
|
||||
|
||||
/// Construct a color filter that transforms a color by a 4x5 matrix.
|
||||
///
|
||||
@ -1458,43 +1456,28 @@ class ColorFilter {
|
||||
/// 0, 0, 0, 1, 0,
|
||||
/// ]);
|
||||
/// ```
|
||||
const ColorFilter.matrix(List<double> matrix)
|
||||
: _color = null,
|
||||
_blendMode = null;
|
||||
const factory ColorFilter.matrix(List<double> matrix) = engine.EngineColorFilter.matrix;
|
||||
|
||||
/// Construct a color filter that applies the sRGB gamma curve to the RGB
|
||||
/// channels.
|
||||
const ColorFilter.linearToSrgbGamma()
|
||||
: _color = null,
|
||||
_blendMode = null;
|
||||
const factory ColorFilter.linearToSrgbGamma() = engine.EngineColorFilter.linearToSrgbGamma;
|
||||
|
||||
/// Creates a color filter that applies the inverse of the sRGB gamma curve
|
||||
/// to the RGB channels.
|
||||
const ColorFilter.srgbToLinearGamma()
|
||||
: _color = null,
|
||||
_blendMode = null;
|
||||
|
||||
final Color _color;
|
||||
final BlendMode _blendMode;
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
if (other is! ColorFilter) {
|
||||
return false;
|
||||
}
|
||||
final ColorFilter typedOther = other;
|
||||
return _color == typedOther._color && _blendMode == typedOther._blendMode;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => hashValues(_color, _blendMode);
|
||||
const factory ColorFilter.srgbToLinearGamma() = engine.EngineColorFilter.srgbToLinearGamma;
|
||||
|
||||
List<dynamic> webOnlySerializeToCssPaint() {
|
||||
throw UnsupportedError('ColorFilter for CSS paint not yet supported');
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => 'ColorFilter($_color, $_blendMode)';
|
||||
bool operator==(dynamic other);
|
||||
|
||||
@override
|
||||
int get hashCode;
|
||||
|
||||
@override
|
||||
String toString();
|
||||
}
|
||||
|
||||
/// Styles to use for blurs in [MaskFilter] objects.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user