blob: 724d038beddc88a32de413e71e41449b82316cda [file] [log] [blame]
// 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.
import 'dart:typed_data';
import 'package:ui/ui.dart' as ui;
import '../../engine.dart' show toMatrix32;
import '../validators.dart';
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';
/// An implementation of [ui.Canvas] that is backed by a CanvasKit canvas.
class CanvasKitCanvas implements ui.Canvas {
final CkCanvas _canvas;
factory CanvasKitCanvas(ui.PictureRecorder recorder, [ui.Rect? cullRect]) {
assert(recorder != null); // ignore: unnecessary_null_comparison
if (recorder.isRecording) {
throw ArgumentError(
'"recorder" must not already be associated with another Canvas.');
}
cullRect ??= ui.Rect.largest;
final CkPictureRecorder ckRecorder = recorder as CkPictureRecorder;
return CanvasKitCanvas._(ckRecorder.beginRecording(cullRect));
}
CanvasKitCanvas._(this._canvas);
@override
void save() {
_canvas.save();
}
@override
void saveLayer(ui.Rect? bounds, ui.Paint paint) {
assert(paint != null); // ignore: unnecessary_null_comparison
if (bounds == null) {
_saveLayerWithoutBounds(paint);
} else {
assert(rectIsValid(bounds));
_saveLayer(bounds, paint);
}
}
void _saveLayerWithoutBounds(ui.Paint paint) {
_canvas.saveLayerWithoutBounds(paint as CkPaint);
}
void _saveLayer(ui.Rect bounds, ui.Paint paint) {
_canvas.saveLayer(bounds, paint as CkPaint);
}
@override
void restore() {
_canvas.restore();
}
@override
int getSaveCount() {
return _canvas.saveCount!;
}
@override
void translate(double dx, double dy) {
_canvas.translate(dx, dy);
}
@override
void scale(double sx, [double? sy]) => _scale(sx, sy ?? sx);
void _scale(double sx, double sy) {
_canvas.scale(sx, sy);
}
@override
void rotate(double radians) {
_canvas.rotate(radians);
}
@override
void skew(double sx, double sy) {
_canvas.skew(sx, sy);
}
@override
void transform(Float64List matrix4) {
assert(matrix4 != null); // ignore: unnecessary_null_comparison
if (matrix4.length != 16) {
throw ArgumentError('"matrix4" must have 16 entries.');
}
_transform(toMatrix32(matrix4));
}
void _transform(Float32List matrix4) {
_canvas.transform(matrix4);
}
@override
void clipRect(ui.Rect rect,
{ui.ClipOp clipOp = ui.ClipOp.intersect, bool doAntiAlias = true}) {
assert(rectIsValid(rect));
assert(clipOp != null); // ignore: unnecessary_null_comparison
assert(doAntiAlias != null); // ignore: unnecessary_null_comparison
_clipRect(rect, clipOp, doAntiAlias);
}
void _clipRect(ui.Rect rect, ui.ClipOp clipOp, bool doAntiAlias) {
_canvas.clipRect(rect, clipOp, doAntiAlias);
}
@override
void clipRRect(ui.RRect rrect, {bool doAntiAlias = true}) {
assert(rrectIsValid(rrect));
assert(doAntiAlias != null); // ignore: unnecessary_null_comparison
_clipRRect(rrect, doAntiAlias);
}
void _clipRRect(ui.RRect rrect, bool doAntiAlias) {
_canvas.clipRRect(rrect, doAntiAlias);
}
@override
void clipPath(ui.Path path, {bool doAntiAlias = true}) {
// ignore: unnecessary_null_comparison
assert(path != null); // path is checked on the engine side
assert(doAntiAlias != null); // ignore: unnecessary_null_comparison
_canvas.clipPath(path as CkPath, doAntiAlias);
}
@override
void drawColor(ui.Color color, ui.BlendMode blendMode) {
assert(color != null); // ignore: unnecessary_null_comparison
assert(blendMode != null); // ignore: unnecessary_null_comparison
_drawColor(color, blendMode);
}
void _drawColor(ui.Color color, ui.BlendMode blendMode) {
_canvas.drawColor(color, blendMode);
}
@override
void drawLine(ui.Offset p1, ui.Offset p2, ui.Paint paint) {
assert(offsetIsValid(p1));
assert(offsetIsValid(p2));
assert(paint != null); // ignore: unnecessary_null_comparison
_drawLine(p1, p2, paint);
}
void _drawLine(ui.Offset p1, ui.Offset p2, ui.Paint paint) {
_canvas.drawLine(p1, p2, paint as CkPaint);
}
@override
void drawPaint(ui.Paint paint) {
assert(paint != null); // ignore: unnecessary_null_comparison
_drawPaint(paint);
}
void _drawPaint(ui.Paint paint) {
_canvas.drawPaint(paint as CkPaint);
}
@override
void drawRect(ui.Rect rect, ui.Paint paint) {
assert(rectIsValid(rect));
assert(paint != null); // ignore: unnecessary_null_comparison
_drawRect(rect, paint);
}
void _drawRect(ui.Rect rect, ui.Paint paint) {
_canvas.drawRect(rect, paint as CkPaint);
}
@override
void drawRRect(ui.RRect rrect, ui.Paint paint) {
assert(rrectIsValid(rrect));
assert(paint != null); // ignore: unnecessary_null_comparison
_drawRRect(rrect, paint);
}
void _drawRRect(ui.RRect rrect, ui.Paint paint) {
_canvas.drawRRect(rrect, paint as CkPaint);
}
@override
void drawDRRect(ui.RRect outer, ui.RRect inner, ui.Paint paint) {
assert(rrectIsValid(outer));
assert(rrectIsValid(inner));
assert(paint != null); // ignore: unnecessary_null_comparison
_drawDRRect(outer, inner, paint);
}
void _drawDRRect(ui.RRect outer, ui.RRect inner, ui.Paint paint) {
_canvas.drawDRRect(outer, inner, paint as CkPaint);
}
@override
void drawOval(ui.Rect rect, ui.Paint paint) {
assert(rectIsValid(rect));
assert(paint != null); // ignore: unnecessary_null_comparison
_drawOval(rect, paint);
}
void _drawOval(ui.Rect rect, ui.Paint paint) {
_canvas.drawOval(rect, paint as CkPaint);
}
@override
void drawCircle(ui.Offset c, double radius, ui.Paint paint) {
assert(offsetIsValid(c));
assert(paint != null); // ignore: unnecessary_null_comparison
_drawCircle(c, radius, paint);
}
void _drawCircle(ui.Offset c, double radius, ui.Paint paint) {
_canvas.drawCircle(c, radius, paint as CkPaint);
}
@override
void drawArc(ui.Rect rect, double startAngle, double sweepAngle,
bool useCenter, ui.Paint paint) {
assert(rectIsValid(rect));
assert(paint != null); // ignore: unnecessary_null_comparison
_drawArc(rect, startAngle, sweepAngle, useCenter, paint);
}
void _drawArc(ui.Rect rect, double startAngle, double sweepAngle,
bool useCenter, ui.Paint paint) {
_canvas.drawArc(rect, startAngle, sweepAngle, useCenter, paint as CkPaint);
}
@override
void drawPath(ui.Path path, ui.Paint paint) {
// ignore: unnecessary_null_comparison
assert(path != null); // path is checked on the engine side
assert(paint != null); // ignore: unnecessary_null_comparison
_canvas.drawPath(path as CkPath, paint as CkPaint);
}
@override
void drawImage(ui.Image image, ui.Offset p, ui.Paint paint) {
// ignore: unnecessary_null_comparison
assert(image != null); // image is checked on the engine side
assert(offsetIsValid(p));
assert(paint != null); // ignore: unnecessary_null_comparison
_canvas.drawImage(image as CkImage, p, paint as CkPaint);
}
@override
void drawImageRect(ui.Image image, ui.Rect src, ui.Rect dst, ui.Paint paint) {
// ignore: unnecessary_null_comparison
assert(image != null); // image is checked on the engine side
assert(rectIsValid(src));
assert(rectIsValid(dst));
assert(paint != null); // ignore: unnecessary_null_comparison
_canvas.drawImageRect(image as CkImage, src, dst, paint as CkPaint);
}
@override
void drawImageNine(
ui.Image image, ui.Rect center, ui.Rect dst, ui.Paint paint) {
// ignore: unnecessary_null_comparison
assert(image != null); // image is checked on the engine side
assert(rectIsValid(center));
assert(rectIsValid(dst));
assert(paint != null); // ignore: unnecessary_null_comparison
_canvas.drawImageNine(image as CkImage, center, dst, paint as CkPaint);
}
@override
void drawPicture(ui.Picture picture) {
// ignore: unnecessary_null_comparison
assert(picture != null); // picture is checked on the engine side
_canvas.drawPicture(picture as CkPicture);
}
@override
void drawParagraph(ui.Paragraph paragraph, ui.Offset offset) {
assert(paragraph != null); // ignore: unnecessary_null_comparison
assert(offsetIsValid(offset));
_drawParagraph(paragraph, offset);
}
void _drawParagraph(ui.Paragraph paragraph, ui.Offset offset) {
_canvas.drawParagraph(paragraph as CkParagraph, offset);
}
@override
void drawPoints(
ui.PointMode pointMode, List<ui.Offset> points, ui.Paint paint) {
assert(pointMode != null); // ignore: unnecessary_null_comparison
assert(points != null); // ignore: unnecessary_null_comparison
assert(paint != null); // ignore: unnecessary_null_comparison
final SkFloat32List skPoints = toMallocedSkPoints(points);
_canvas.drawPoints(
paint as CkPaint,
pointMode,
skPoints.toTypedArray(),
);
freeFloat32List(skPoints);
}
@override
void drawRawPoints(
ui.PointMode pointMode, Float32List points, ui.Paint paint) {
assert(pointMode != null); // ignore: unnecessary_null_comparison
assert(points != null); // ignore: unnecessary_null_comparison
assert(paint != null); // ignore: unnecessary_null_comparison
if (points.length % 2 != 0) {
throw ArgumentError('"points" must have an even number of values.');
}
_canvas.drawPoints(
paint as CkPaint,
pointMode,
points,
);
}
@override
void drawVertices(
ui.Vertices vertices, ui.BlendMode blendMode, ui.Paint paint) {
// ignore: unnecessary_null_comparison
assert(vertices != null); // vertices is checked on the engine side
assert(paint != null); // ignore: unnecessary_null_comparison
assert(blendMode != null); // ignore: unnecessary_null_comparison
_canvas.drawVertices(vertices as CkVertices, blendMode, paint as CkPaint);
}
@override
void drawAtlas(
ui.Image atlas,
List<ui.RSTransform> transforms,
List<ui.Rect> rects,
List<ui.Color>? colors,
ui.BlendMode? blendMode,
ui.Rect? cullRect,
ui.Paint paint) {
// ignore: unnecessary_null_comparison
assert(atlas != null); // atlas is checked on the engine side
assert(transforms != null); // ignore: unnecessary_null_comparison
assert(rects != null); // ignore: unnecessary_null_comparison
assert(colors == null || colors.isEmpty || blendMode != null);
assert(paint != null); // ignore: unnecessary_null_comparison
final int rectCount = rects.length;
if (transforms.length != rectCount) {
throw ArgumentError('"transforms" and "rects" lengths must match.');
}
if (colors != null && colors.isNotEmpty && colors.length != rectCount) {
throw ArgumentError(
'If non-null, "colors" length must match that of "transforms" and "rects".');
}
final Float32List rstTransformBuffer = Float32List(rectCount * 4);
final Float32List rectBuffer = Float32List(rectCount * 4);
for (int i = 0; i < rectCount; ++i) {
final int index0 = i * 4;
final int index1 = index0 + 1;
final int index2 = index0 + 2;
final int index3 = index0 + 3;
final ui.RSTransform rstTransform = transforms[i];
final ui.Rect rect = rects[i];
assert(rectIsValid(rect));
rstTransformBuffer[index0] = rstTransform.scos;
rstTransformBuffer[index1] = rstTransform.ssin;
rstTransformBuffer[index2] = rstTransform.tx;
rstTransformBuffer[index3] = rstTransform.ty;
rectBuffer[index0] = rect.left;
rectBuffer[index1] = rect.top;
rectBuffer[index2] = rect.right;
rectBuffer[index3] = rect.bottom;
}
final Uint32List? colorBuffer =
(colors == null || colors.isEmpty) ? null : toFlatColors(colors);
_drawAtlas(paint, atlas, rstTransformBuffer, rectBuffer, colorBuffer,
blendMode ?? ui.BlendMode.src);
}
@override
void drawRawAtlas(
ui.Image atlas,
Float32List rstTransforms,
Float32List rects,
Int32List? colors,
ui.BlendMode? blendMode,
ui.Rect? cullRect,
ui.Paint paint) {
// ignore: unnecessary_null_comparison
assert(atlas != null); // atlas is checked on the engine side
assert(rstTransforms != null); // ignore: unnecessary_null_comparison
assert(rects != null); // ignore: unnecessary_null_comparison
assert(colors == null || blendMode != null);
assert(paint != null); // ignore: unnecessary_null_comparison
final int rectCount = rects.length;
if (rstTransforms.length != rectCount)
throw ArgumentError('"rstTransforms" and "rects" lengths must match.');
if (rectCount % 4 != 0)
throw ArgumentError(
'"rstTransforms" and "rects" lengths must be a multiple of four.');
if (colors != null && colors.length * 4 != rectCount)
throw ArgumentError(
'If non-null, "colors" length must be one fourth the length of "rstTransforms" and "rects".');
_drawAtlas(paint, atlas, rstTransforms, rects,
colors?.buffer.asUint32List(), blendMode ?? ui.BlendMode.src);
}
// TODO(hterkelsen): Pass a cull_rect once CanvasKit supports that.
void _drawAtlas(
ui.Paint paint,
ui.Image atlas,
Float32List rstTransforms,
Float32List rects,
Uint32List? colors,
ui.BlendMode blendMode,
) {
_canvas.drawAtlasRaw(
paint as CkPaint,
atlas as CkImage,
rstTransforms,
rects,
colors,
blendMode,
);
}
@override
void drawShadow(ui.Path path, ui.Color color, double elevation,
bool transparentOccluder) {
// ignore: unnecessary_null_comparison
assert(path != null); // path is checked on the engine side
assert(color != null); // ignore: unnecessary_null_comparison
assert(transparentOccluder != null); // ignore: unnecessary_null_comparison
_drawShadow(path, color, elevation, transparentOccluder);
}
void _drawShadow(ui.Path path, ui.Color color, double elevation,
bool transparentOccluder) {
_canvas.drawShadow(path as CkPath, color, elevation, transparentOccluder);
}
}