Update canvaskit backend (#12318)

* Improve the CanvasKit backend for Flutter Web

- Improve font handling by trying to load a "normal" font face
  instead of using the first face matching the family.
- Implement Vertices and drawVertices

* Add license header to vertices.dart

* Remove unused 'encodedPositions'

* Delete commented old code. Don't use Skia by default

* Add `vertices.dart` to licenses file
This commit is contained in:
Harry Terkelsen 2019-09-17 14:42:18 -07:00 committed by GitHub
parent b790d48ebc
commit d1692d4cc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 264 additions and 130 deletions

View File

@ -372,6 +372,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/recording_canvas.dar
FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/runtime_delegate.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/surface.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/util.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/vertices.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/compositor/viewport_metrics.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/conic.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/dom_canvas.dart

View File

@ -41,6 +41,7 @@ part 'engine/compositor/recording_canvas.dart';
part 'engine/compositor/runtime_delegate.dart';
part 'engine/compositor/surface.dart';
part 'engine/compositor/util.dart';
part 'engine/compositor/vertices.dart';
part 'engine/compositor/viewport_metrics.dart';
part 'engine/conic.dart';
part 'engine/dom_canvas.dart';

View File

@ -73,13 +73,35 @@ class SkiaFontCollection {
js.JsObject getFont(String family, double size) {
if (_registeredTypefaces[family] == null) {
if (family == 'sans-serif') {
// If it's the default font, return a default SkFont
return js.JsObject(canvasKit['SkFont'], <dynamic>[null, size]);
if (assertionsEnabled) {
html.window.console.warn('Using unregistered font: $family');
}
throw Exception('Unregistered font: $family');
return js.JsObject(canvasKit['SkFont'], <dynamic>[null, size]);
}
final js.JsObject skTypeface = _registeredTypefaces[family].values.first;
// We don't attempt to find a Typeface matching the text style. Instead, we
// try to find the "default" typeface. The default typeface either has no
// descriptors, or only has a descriptor of font-weight 400 (the default).
final Map<Map<String, String>, js.JsObject> typefaces =
_registeredTypefaces[family];
js.JsObject skTypeface;
for (MapEntry<Map<String, String>, js.JsObject> entry
in typefaces.entries) {
final Map<String, String> descriptors = entry.key;
if (descriptors.isEmpty ||
(descriptors.length == 1 && descriptors['weight'] == '400')) {
skTypeface = entry.value;
break;
}
}
// If we couldn't find a suitable default, just use any typeface in the
// family.
if (skTypeface == null) {
skTypeface = typefaces.values.first;
}
return js.JsObject(canvasKit['SkFont'], <dynamic>[skTypeface, size]);
}

View File

@ -220,6 +220,17 @@ class SkRecordingCanvas implements RecordingCanvas {
drawSkShadow(skCanvas, path, color, elevation, transparentOccluder);
}
@override
void drawVertices(
ui.Vertices vertices, ui.BlendMode blendMode, ui.Paint paint) {
SkVertices skVertices = vertices;
skCanvas.callMethod('drawVertices', <js.JsObject>[
skVertices.skVertices,
makeSkBlendMode(blendMode),
makeSkPaint(paint)
]);
}
@override
bool get hasArbitraryPaint => true;

View File

@ -17,6 +17,71 @@ js.JsArray<double> makeSkPoint(ui.Offset point) {
return skPoint;
}
js.JsObject makeSkBlendMode(ui.BlendMode blendMode) {
switch (blendMode) {
case ui.BlendMode.clear:
return canvasKit['BlendMode']['Clear'];
case ui.BlendMode.src:
return canvasKit['BlendMode']['Src'];
case ui.BlendMode.dst:
return canvasKit['BlendMode']['Dst'];
case ui.BlendMode.srcOver:
return canvasKit['BlendMode']['SrcOver'];
case ui.BlendMode.dstOver:
return canvasKit['BlendMode']['DstOver'];
case ui.BlendMode.srcIn:
return canvasKit['BlendMode']['SrcIn'];
case ui.BlendMode.dstIn:
return canvasKit['BlendMode']['DstIn'];
case ui.BlendMode.srcOut:
return canvasKit['BlendMode']['SrcOut'];
case ui.BlendMode.dstOut:
return canvasKit['BlendMode']['DstOut'];
case ui.BlendMode.srcATop:
return canvasKit['BlendMode']['SrcATop'];
case ui.BlendMode.dstATop:
return canvasKit['BlendMode']['DstATop'];
case ui.BlendMode.xor:
return canvasKit['BlendMode']['Xor'];
case ui.BlendMode.plus:
return canvasKit['BlendMode']['Plus'];
case ui.BlendMode.modulate:
return canvasKit['BlendMode']['Modulate'];
case ui.BlendMode.screen:
return canvasKit['BlendMode']['Screen'];
case ui.BlendMode.overlay:
return canvasKit['BlendMode']['Overlay'];
case ui.BlendMode.darken:
return canvasKit['BlendMode']['Darken'];
case ui.BlendMode.lighten:
return canvasKit['BlendMode']['Lighten'];
case ui.BlendMode.colorDodge:
return canvasKit['BlendMode']['ColorDodge'];
case ui.BlendMode.colorBurn:
return canvasKit['BlendMode']['ColorBurn'];
case ui.BlendMode.hardLight:
return canvasKit['BlendMode']['HardLight'];
case ui.BlendMode.softLight:
return canvasKit['BlendMode']['SoftLight'];
case ui.BlendMode.difference:
return canvasKit['BlendMode']['Difference'];
case ui.BlendMode.exclusion:
return canvasKit['BlendMode']['Exclusion'];
case ui.BlendMode.multiply:
return canvasKit['BlendMode']['Multiply'];
case ui.BlendMode.hue:
return canvasKit['BlendMode']['Hue'];
case ui.BlendMode.saturation:
return canvasKit['BlendMode']['Saturation'];
case ui.BlendMode.color:
return canvasKit['BlendMode']['Color'];
case ui.BlendMode.luminosity:
return canvasKit['BlendMode']['Luminosity'];
default:
return null;
}
}
js.JsObject makeSkPaint(ui.Paint paint) {
final dynamic skPaint = js.JsObject(canvasKit['SkPaint']);
@ -41,124 +106,7 @@ js.JsObject makeSkPaint(ui.Paint paint) {
}
skPaint.callMethod('setStyle', <js.JsObject>[skPaintStyle]);
js.JsObject skBlendMode;
switch (paint.blendMode) {
case ui.BlendMode.clear:
skBlendMode = canvasKit['BlendMode']['Clear'];
break;
case ui.BlendMode.src:
skBlendMode = canvasKit['BlendMode']['Src'];
break;
case ui.BlendMode.dst:
skBlendMode = canvasKit['BlendMode']['Dst'];
break;
case ui.BlendMode.srcOver:
skBlendMode = canvasKit['BlendMode']['SrcOver'];
break;
case ui.BlendMode.dstOver:
skBlendMode = canvasKit['BlendMode']['DstOver'];
break;
case ui.BlendMode.srcIn:
skBlendMode = canvasKit['BlendMode']['SrcIn'];
break;
case ui.BlendMode.dstIn:
skBlendMode = canvasKit['BlendMode']['DstIn'];
break;
case ui.BlendMode.srcOut:
skBlendMode = canvasKit['BlendMode']['SrcOut'];
break;
case ui.BlendMode.dstOut:
skBlendMode = canvasKit['BlendMode']['DstOut'];
break;
case ui.BlendMode.srcATop:
skBlendMode = canvasKit['BlendMode']['SrcATop'];
break;
case ui.BlendMode.dstATop:
skBlendMode = canvasKit['BlendMode']['DstATop'];
break;
case ui.BlendMode.xor:
skBlendMode = canvasKit['BlendMode']['Xor'];
break;
case ui.BlendMode.plus:
skBlendMode = canvasKit['BlendMode']['Plus'];
break;
case ui.BlendMode.modulate:
skBlendMode = canvasKit['BlendMode']['Modulate'];
break;
case ui.BlendMode.screen:
skBlendMode = canvasKit['BlendMode']['Screen'];
break;
case ui.BlendMode.overlay:
skBlendMode = canvasKit['BlendMode']['Overlay'];
break;
case ui.BlendMode.darken:
skBlendMode = canvasKit['BlendMode']['Darken'];
break;
case ui.BlendMode.lighten:
skBlendMode = canvasKit['BlendMode']['Lighten'];
break;
case ui.BlendMode.colorDodge:
skBlendMode = canvasKit['BlendMode']['ColorDodge'];
break;
case ui.BlendMode.colorBurn:
skBlendMode = canvasKit['BlendMode']['ColorBurn'];
break;
case ui.BlendMode.hardLight:
skBlendMode = canvasKit['BlendMode']['HardLight'];
break;
case ui.BlendMode.softLight:
skBlendMode = canvasKit['BlendMode']['SoftLight'];
break;
case ui.BlendMode.difference:
skBlendMode = canvasKit['BlendMode']['Difference'];
break;
case ui.BlendMode.exclusion:
skBlendMode = canvasKit['BlendMode']['Exclusion'];
break;
case ui.BlendMode.multiply:
skBlendMode = canvasKit['BlendMode']['Multiply'];
break;
case ui.BlendMode.hue:
skBlendMode = canvasKit['BlendMode']['Hue'];
break;
case ui.BlendMode.saturation:
skBlendMode = canvasKit['BlendMode']['Saturation'];
break;
case ui.BlendMode.color:
skBlendMode = canvasKit['BlendMode']['Color'];
break;
case ui.BlendMode.luminosity:
skBlendMode = canvasKit['BlendMode']['Luminosity'];
break;
}
js.JsObject skBlendMode = makeSkBlendMode(paint.blendMode);
if (skBlendMode != null) {
skPaint.callMethod('setBlendMode', <js.JsObject>[skBlendMode]);
}

View File

@ -0,0 +1,133 @@
// 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;
Int32List _encodeColorList(List<ui.Color> colors) {
final int colorCount = colors.length;
final Int32List result = Int32List(colorCount);
for (int i = 0; i < colorCount; ++i) result[i] = colors[i].value;
return result;
}
Float32List _encodePointList(List<ui.Offset> points) {
assert(points != null);
final int pointCount = points.length;
final Float32List result = Float32List(pointCount * 2);
for (int i = 0; i < pointCount; ++i) {
final int xIndex = i * 2;
final int yIndex = xIndex + 1;
final ui.Offset point = points[i];
assert(_offsetIsValid(point));
result[xIndex] = point.dx;
result[yIndex] = point.dy;
}
return result;
}
class SkVertices implements ui.Vertices {
js.JsObject skVertices;
SkVertices(
ui.VertexMode mode,
List<ui.Offset> positions, {
List<ui.Offset> textureCoordinates,
List<ui.Color> colors,
List<int> indices,
}) : assert(mode != null),
assert(positions != null) {
if (textureCoordinates != null &&
textureCoordinates.length != positions.length)
throw ArgumentError(
'"positions" and "textureCoordinates" lengths must match.');
if (colors != null && colors.length != positions.length)
throw ArgumentError('"positions" and "colors" lengths must match.');
if (indices != null &&
indices.any((int i) => i < 0 || i >= positions.length))
throw ArgumentError(
'"indices" values must be valid indices in the positions list.');
final Float32List encodedPositions = _encodePointList(positions);
final Float32List encodedTextureCoordinates = (textureCoordinates != null)
? _encodePointList(textureCoordinates)
: null;
final Int32List encodedColors =
colors != null ? _encodeColorList(colors) : null;
final Uint16List encodedIndices =
indices != null ? Uint16List.fromList(indices) : null;
if (!_init(mode, encodedPositions, encodedTextureCoordinates, encodedColors,
encodedIndices))
throw ArgumentError('Invalid configuration for vertices.');
}
SkVertices.raw(
ui.VertexMode mode,
Float32List positions, {
Float32List textureCoordinates,
Int32List colors,
Uint16List indices,
}) : assert(mode != null),
assert(positions != null) {
if (textureCoordinates != null &&
textureCoordinates.length != positions.length)
throw ArgumentError(
'"positions" and "textureCoordinates" lengths must match.');
if (colors != null && colors.length * 2 != positions.length)
throw ArgumentError('"positions" and "colors" lengths must match.');
if (indices != null &&
indices.any((int i) => i < 0 || i >= positions.length))
throw ArgumentError(
'"indices" values must be valid indices in the positions list.');
if (!_init(mode, positions, textureCoordinates, colors, indices))
throw ArgumentError('Invalid configuration for vertices.');
}
bool _init(ui.VertexMode mode, Float32List positions,
Float32List textureCoordinates, Int32List colors, Uint16List indices) {
js.JsObject skVertexMode;
switch (mode) {
case ui.VertexMode.triangles:
skVertexMode = canvasKit['VertexMode']['Triangles'];
break;
case ui.VertexMode.triangleStrip:
skVertexMode = canvasKit['VertexMode']['TrianglesStrip'];
break;
case ui.VertexMode.triangleFan:
skVertexMode = canvasKit['VertexMode']['TriangleFan'];
break;
}
final js.JsObject vertices =
canvasKit.callMethod('MakeSkVertices', <dynamic>[
skVertexMode,
_encodePoints(positions),
_encodePoints(textureCoordinates),
colors,
null,
null,
indices,
]);
if (vertices != null) {
skVertices = vertices;
return true;
} else {
return false;
}
}
static _encodePoints(List<double> points) {
if (points == null) return null;
js.JsArray<js.JsArray<double>> encodedPoints =
js.JsArray<js.JsArray<double>>();
encodedPoints.length = points.length ~/ 2;
for (int i = 0; i < points.length; i += 2) {
encodedPoints[i ~/ 2] = makeSkPoint(ui.Offset(points[i], points[i + 1]));
}
return encodedPoints;
}
}

View File

@ -347,6 +347,10 @@ class RecordingCanvas {
_commands.add(PaintDrawShadow(path, color, elevation, transparentOccluder));
}
void drawVertices(ui.Vertices vertices, ui.BlendMode blendMode, ui.Paint paint) {
throw new UnimplementedError();
}
int saveCount = 1;
/// Prints the commands recorded by this canvas to the console.

View File

@ -59,23 +59,37 @@ enum VertexMode {
/// A set of vertex data used by [Canvas.drawVertices].
class Vertices {
Vertices(
factory Vertices(
VertexMode mode,
List<Offset> positions, {
List<Offset> textureCoordinates,
List<Color> colors,
List<int> indices,
}) : assert(mode != null),
assert(positions != null);
}) {
if (engine.experimentalUseSkia) {
return engine.SkVertices(mode, positions,
textureCoordinates: textureCoordinates,
colors: colors,
indices: indices);
}
return null;
}
Vertices.raw(
factory Vertices.raw(
VertexMode mode,
Float32List positions, {
Float32List textureCoordinates,
Int32List colors,
Uint16List indices,
}) : assert(mode != null),
assert(positions != null);
}) {
if (engine.experimentalUseSkia) {
return engine.SkVertices.raw(mode, positions,
textureCoordinates: textureCoordinates,
colors: colors,
indices: indices);
}
return null;
}
}
/// Records a [Picture] containing a sequence of graphical operations.
@ -877,7 +891,7 @@ class Canvas {
assert(vertices != null); // vertices is checked on the engine side
assert(paint != null);
assert(blendMode != null);
throw UnimplementedError();
_canvas.drawVertices(vertices, blendMode, paint);
}
//