| // 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; |
| |
| class SurfaceCanvas implements ui.Canvas { |
| RecordingCanvas _canvas; |
| |
| factory SurfaceCanvas(EnginePictureRecorder recorder, [ui.Rect? cullRect]) { |
| if (recorder.isRecording) { |
| throw ArgumentError( |
| '"recorder" must not already be associated with another Canvas.'); |
| } |
| cullRect ??= ui.Rect.largest; |
| return SurfaceCanvas._(recorder.beginRecording(cullRect)); |
| } |
| |
| SurfaceCanvas._(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 SurfacePaint); |
| } |
| |
| void _saveLayer(ui.Rect bounds, ui.Paint paint) { |
| _canvas.saveLayer(bounds, paint as SurfacePaint); |
| } |
| |
| @override |
| void restore() { |
| _canvas.restore(); |
| } |
| |
| @override |
| int getSaveCount() => _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); |
| } |
| |
| @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); |
| } |
| |
| @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 |
| _clipPath(path, doAntiAlias); |
| } |
| |
| void _clipPath(ui.Path path, bool doAntiAlias) { |
| _canvas.clipPath(path, doAntiAlias: 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 SurfacePaint); |
| } |
| |
| @override |
| void drawPaint(ui.Paint paint) { |
| assert(paint != null); // ignore: unnecessary_null_comparison |
| _drawPaint(paint); |
| } |
| |
| void _drawPaint(ui.Paint paint) { |
| _canvas.drawPaint(paint as SurfacePaint); |
| } |
| |
| @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 SurfacePaint); |
| } |
| |
| @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 SurfacePaint); |
| } |
| |
| @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 SurfacePaint); |
| } |
| |
| @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 SurfacePaint); |
| } |
| |
| @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 SurfacePaint); |
| } |
| |
| @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 |
| const double pi = math.pi; |
| const double pi2 = 2.0 * pi; |
| |
| final ui.Path path = ui.Path(); |
| if (useCenter) { |
| path.moveTo( |
| (rect.left + rect.right) / 2.0, (rect.top + rect.bottom) / 2.0); |
| } |
| bool forceMoveTo = !useCenter; |
| if (sweepAngle <= -pi2) { |
| path.arcTo(rect, startAngle, -pi, forceMoveTo); |
| startAngle -= pi; |
| path.arcTo(rect, startAngle, -pi, false); |
| startAngle -= pi; |
| forceMoveTo = false; |
| sweepAngle += pi2; |
| } |
| while (sweepAngle >= pi2) { |
| path.arcTo(rect, startAngle, pi, forceMoveTo); |
| startAngle += pi; |
| path.arcTo(rect, startAngle, pi, false); |
| startAngle += pi; |
| forceMoveTo = false; |
| sweepAngle -= pi2; |
| } |
| path.arcTo(rect, startAngle, sweepAngle, forceMoveTo); |
| if (useCenter) { |
| path.close(); |
| } |
| _canvas.drawPath(path, paint as SurfacePaint); |
| } |
| |
| @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 |
| _drawPath(path, paint); |
| } |
| |
| void _drawPath(ui.Path path, ui.Paint paint) { |
| _canvas.drawPath(path, paint as SurfacePaint); |
| } |
| |
| @override |
| void drawImage(ui.Image image, ui.Offset offset, ui.Paint paint) { |
| // ignore: unnecessary_null_comparison |
| assert(image != null); // image is checked on the engine side |
| assert(offsetIsValid(offset)); |
| assert(paint != null); // ignore: unnecessary_null_comparison |
| _drawImage(image, offset, paint); |
| } |
| |
| void _drawImage(ui.Image image, ui.Offset p, ui.Paint paint) { |
| _canvas.drawImage(image, p, paint as SurfacePaint); |
| } |
| |
| @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 |
| _drawImageRect(image, src, dst, paint); |
| } |
| |
| void _drawImageRect( |
| ui.Image image, ui.Rect src, ui.Rect dst, ui.Paint paint) { |
| _canvas.drawImageRect(image, src, dst, paint as SurfacePaint); |
| } |
| |
| @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 |
| |
| // Assert you can fit the scaled part of the image (exluding the |
| // center source). |
| assert(image.width - center.width < dst.width); |
| assert(image.height - center.height < dst.height); |
| |
| // The four unscaled corner rectangles in the from the src. |
| final ui.Rect srcTopLeft = ui.Rect.fromLTWH( |
| 0, |
| 0, |
| center.left, |
| center.top, |
| ); |
| final ui.Rect srcTopRight = ui.Rect.fromLTWH( |
| center.right, |
| 0, |
| image.width - center.right, |
| center.top, |
| ); |
| final ui.Rect srcBottomLeft = ui.Rect.fromLTWH( |
| 0, |
| center.bottom, |
| center.left, |
| image.height - center.bottom, |
| ); |
| final ui.Rect srcBottomRight = ui.Rect.fromLTWH( |
| center.right, |
| center.bottom, |
| image.width - center.right, |
| image.height - center.bottom, |
| ); |
| |
| final ui.Rect dstTopLeft = srcTopLeft.shift(dst.topLeft); |
| |
| // The center rectangle in the dst region |
| final ui.Rect dstCenter = ui.Rect.fromLTWH( |
| dstTopLeft.right, |
| dstTopLeft.bottom, |
| dst.width - (srcTopLeft.width + srcTopRight.width), |
| dst.height - (srcTopLeft.height + srcBottomLeft.height), |
| ); |
| |
| drawImageRect(image, srcTopLeft, dstTopLeft, paint); |
| |
| final ui.Rect dstTopRight = ui.Rect.fromLTWH( |
| dstCenter.right, |
| dst.top, |
| srcTopRight.width, |
| srcTopRight.height, |
| ); |
| drawImageRect(image, srcTopRight, dstTopRight, paint); |
| |
| final ui.Rect dstBottomLeft = ui.Rect.fromLTWH( |
| dst.left, |
| dstCenter.bottom, |
| srcBottomLeft.width, |
| srcBottomLeft.height, |
| ); |
| drawImageRect(image, srcBottomLeft, dstBottomLeft, paint); |
| |
| final ui.Rect dstBottomRight = ui.Rect.fromLTWH( |
| dstCenter.right, |
| dstCenter.bottom, |
| srcBottomRight.width, |
| srcBottomRight.height, |
| ); |
| drawImageRect(image, srcBottomRight, dstBottomRight, paint); |
| |
| // Draw the top center rectangle. |
| drawImageRect( |
| image, |
| ui.Rect.fromLTRB( |
| srcTopLeft.right, |
| srcTopLeft.top, |
| srcTopRight.left, |
| srcTopRight.bottom, |
| ), |
| ui.Rect.fromLTRB( |
| dstTopLeft.right, |
| dstTopLeft.top, |
| dstTopRight.left, |
| dstTopRight.bottom, |
| ), |
| paint, |
| ); |
| |
| // Draw the middle left rectangle. |
| drawImageRect( |
| image, |
| ui.Rect.fromLTRB( |
| srcTopLeft.left, |
| srcTopLeft.bottom, |
| srcBottomLeft.right, |
| srcBottomLeft.top, |
| ), |
| ui.Rect.fromLTRB( |
| dstTopLeft.left, |
| dstTopLeft.bottom, |
| dstBottomLeft.right, |
| dstBottomLeft.top, |
| ), |
| paint, |
| ); |
| |
| // Draw the center rectangle. |
| drawImageRect(image, center, dstCenter, paint); |
| |
| // Draw the middle right rectangle. |
| drawImageRect( |
| image, |
| ui.Rect.fromLTRB( |
| srcTopRight.left, |
| srcTopRight.bottom, |
| srcBottomRight.right, |
| srcBottomRight.top, |
| ), |
| ui.Rect.fromLTRB( |
| dstTopRight.left, |
| dstTopRight.bottom, |
| dstBottomRight.right, |
| dstBottomRight.top, |
| ), |
| paint, |
| ); |
| |
| // Draw the bottom center rectangle. |
| drawImageRect( |
| image, |
| ui.Rect.fromLTRB( |
| srcBottomLeft.right, |
| srcBottomLeft.top, |
| srcBottomRight.left, |
| srcBottomRight.bottom, |
| ), |
| ui.Rect.fromLTRB( |
| dstBottomLeft.right, |
| dstBottomLeft.top, |
| dstBottomRight.left, |
| dstBottomRight.bottom, |
| ), |
| paint, |
| ); |
| } |
| |
| @override |
| void drawPicture(ui.Picture picture) { |
| // ignore: unnecessary_null_comparison |
| assert(picture != null); // picture is checked on the engine side |
| _canvas.drawPicture(picture); |
| } |
| |
| @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, 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 Float32List pointList = offsetListToFloat32List(points); |
| drawRawPoints(pointMode, pointList, paint); |
| } |
| |
| @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.drawRawPoints(pointMode, points, paint as SurfacePaint); |
| } |
| |
| @override |
| void drawVertices( |
| ui.Vertices vertices, ui.BlendMode blendMode, ui.Paint paint) { |
| //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 SurfaceVertices, blendMode, paint as SurfacePaint); |
| } |
| |
| @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".'); |
| } |
| |
| // TODO(het): Do we need to support this? |
| throw UnimplementedError(); |
| } |
| |
| @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".'); |
| } |
| |
| // TODO(het): Do we need to support this? |
| throw UnimplementedError(); |
| } |
| |
| @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 |
| _canvas.drawShadow(path, color, elevation, transparentOccluder); |
| } |
| } |