From f74d2439ea2918c35f74a1b36be8a2adfe69b76a Mon Sep 17 00:00:00 2001 From: xiaomiao Date: Thu, 1 Sep 2022 21:09:02 +0800 Subject: [PATCH] Add function restoreToCount to Canvas. (#35798) --- display_list/display_list_builder.cc | 2 +- lib/ui/dart_ui.cc | 1 + lib/ui/painting.dart | 12 ++++++ lib/ui/painting/canvas.cc | 6 +++ lib/ui/painting/canvas.h | 1 + lib/web_ui/lib/canvas.dart | 1 + .../engine/canvaskit/canvaskit_canvas.dart | 5 +++ lib/web_ui/lib/src/engine/html/canvas.dart | 5 +++ .../lib/src/engine/html/recording_canvas.dart | 6 +++ lib/web_ui/test/engine/canvas_test.dart | 43 +++++++++++++++++++ testing/dart/canvas_test.dart | 41 ++++++++++++++++++ 11 files changed, 122 insertions(+), 1 deletion(-) diff --git a/display_list/display_list_builder.cc b/display_list/display_list_builder.cc index 788938e4b05b7..a71542fc93054 100644 --- a/display_list/display_list_builder.cc +++ b/display_list/display_list_builder.cc @@ -476,7 +476,7 @@ void DisplayListBuilder::restore() { } void DisplayListBuilder::restoreToCount(int restore_count) { FML_DCHECK(restore_count <= getSaveCount()); - while (restore_count < getSaveCount()) { + while (restore_count < getSaveCount() && getSaveCount() > 1) { restore(); } } diff --git a/lib/ui/dart_ui.cc b/lib/ui/dart_ui.cc index b82c69fd978d6..fead4f06814a6 100644 --- a/lib/ui/dart_ui.cc +++ b/lib/ui/dart_ui.cc @@ -150,6 +150,7 @@ typedef CanvasPath Path; V(Canvas, getSaveCount, 1) \ V(Canvas, getTransform, 2) \ V(Canvas, restore, 1) \ + V(Canvas, restoreToCount, 2) \ V(Canvas, rotate, 2) \ V(Canvas, save, 1) \ V(Canvas, saveLayer, 7) \ diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index 1be62147f900c..7b44af766c4f3 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -4763,6 +4763,18 @@ class Canvas extends NativeFieldWrapperClass1 { @FfiNative)>('Canvas::restore', isLeaf: true) external void restore(); + /// Restores the save stack to a previous level as might be obtained from [getSaveCount]. + /// If [count] is less than 1, the stack is restored to its initial state. + /// If [count] is greater than the current [getSaveCount] then nothing happens. + /// + /// Use [save] and [saveLayer] to push state onto the stack. + /// + /// If any of the state stack levels restored by this call were pushed with + /// [saveLayer], then this call will also cause those layers to be composited + /// into their previous layers. + @FfiNative, Int32)>('Canvas::restoreToCount', isLeaf: true) + external void restoreToCount(int count); + /// Returns the number of items on the save stack, including the /// initial state. This means it returns 1 for a clean canvas, and /// that each call to [save] and [saveLayer] increments it, and that diff --git a/lib/ui/painting/canvas.cc b/lib/ui/painting/canvas.cc index ee39d8a9489b5..733b8a2903e71 100644 --- a/lib/ui/painting/canvas.cc +++ b/lib/ui/painting/canvas.cc @@ -117,6 +117,12 @@ int Canvas::getSaveCount() { } } +void Canvas::restoreToCount(int count) { + if (display_list_recorder_ && count < getSaveCount()) { + builder()->restoreToCount(count); + } +} + void Canvas::translate(double dx, double dy) { if (display_list_recorder_) { builder()->translate(dx, dy); diff --git a/lib/ui/painting/canvas.h b/lib/ui/painting/canvas.h index 0fbe2d8b7f5f9..22ef44315ca09 100644 --- a/lib/ui/painting/canvas.h +++ b/lib/ui/painting/canvas.h @@ -48,6 +48,7 @@ class Canvas : public RefCountedDartWrappable, DisplayListOpFlags { void restore(); int getSaveCount(); + void restoreToCount(int count); void translate(double dx, double dy); void scale(double sx, double sy); diff --git a/lib/web_ui/lib/canvas.dart b/lib/web_ui/lib/canvas.dart index ac90121158c2b..4dc01537db414 100644 --- a/lib/web_ui/lib/canvas.dart +++ b/lib/web_ui/lib/canvas.dart @@ -66,6 +66,7 @@ abstract class Canvas { void saveLayer(Rect? bounds, Paint paint); void restore(); int getSaveCount(); + void restoreToCount(int count); void translate(double dx, double dy); void scale(double sx, [double? sy]); void rotate(double radians); diff --git a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_canvas.dart b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_canvas.dart index 87c94858dd2c7..444860bae5649 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_canvas.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_canvas.dart @@ -65,6 +65,11 @@ class CanvasKitCanvas implements ui.Canvas { _canvas.restore(); } + @override + void restoreToCount(int count) { + _canvas.restoreToCount(count); + } + @override int getSaveCount() { return _canvas.saveCount!; diff --git a/lib/web_ui/lib/src/engine/html/canvas.dart b/lib/web_ui/lib/src/engine/html/canvas.dart index 8f310f3865207..b40df52a28ffa 100644 --- a/lib/web_ui/lib/src/engine/html/canvas.dart +++ b/lib/web_ui/lib/src/engine/html/canvas.dart @@ -58,6 +58,11 @@ class SurfaceCanvas implements ui.Canvas { _canvas.restore(); } + @override + void restoreToCount(int count) { + _canvas.restoreToCount(count); + } + @override int getSaveCount() => _canvas.saveCount; diff --git a/lib/web_ui/lib/src/engine/html/recording_canvas.dart b/lib/web_ui/lib/src/engine/html/recording_canvas.dart index 970009ef563f8..b1f86da42f8fe 100644 --- a/lib/web_ui/lib/src/engine/html/recording_canvas.dart +++ b/lib/web_ui/lib/src/engine/html/recording_canvas.dart @@ -244,6 +244,12 @@ class RecordingCanvas { _saveCount--; } + void restoreToCount(int count) { + while (count < _saveCount && _saveCount > 1) { + restore(); + } + } + void translate(double dx, double dy) { assert(!_recordingEnded); _paintBounds.translate(dx, dy); diff --git a/lib/web_ui/test/engine/canvas_test.dart b/lib/web_ui/test/engine/canvas_test.dart index ef76c8676df33..053cd9c155b6b 100644 --- a/lib/web_ui/test/engine/canvas_test.dart +++ b/lib/web_ui/test/engine/canvas_test.dart @@ -277,4 +277,47 @@ void runCanvasTests({required bool deviceClipRoundsOut}) { expect(canvas.getDestinationClipBounds(), initialDestinationBounds); }); }); + + group('RestoreToCount function tests', () { + test('RestoreToCount can work', () async { + final ui.PictureRecorder recorder = ui.PictureRecorder(); + final ui.Canvas canvas = ui.Canvas(recorder); + canvas.save(); + canvas.save(); + canvas.save(); + canvas.save(); + canvas.save(); + expect(canvas.getSaveCount(), 6); + canvas.restoreToCount(2); + expect(canvas.getSaveCount(), 2); + canvas.restore(); + expect(canvas.getSaveCount(), 1); + }); + + test('RestoreToCount count less than 1, the stack should be reset', () async { + final ui.PictureRecorder recorder = ui.PictureRecorder(); + final ui.Canvas canvas = ui.Canvas(recorder); + canvas.save(); + canvas.save(); + canvas.save(); + canvas.save(); + canvas.save(); + expect(canvas.getSaveCount(), equals(6)); + canvas.restoreToCount(0); + expect(canvas.getSaveCount(), equals(1)); + }); + + test('RestoreToCount count greater than current [getSaveCount]', () async { + final ui.PictureRecorder recorder = ui.PictureRecorder(); + final ui.Canvas canvas = ui.Canvas(recorder); + canvas.save(); + canvas.save(); + canvas.save(); + canvas.save(); + canvas.save(); + expect(canvas.getSaveCount(), equals(6)); + canvas.restoreToCount(canvas.getSaveCount() + 1); + expect(canvas.getSaveCount(), equals(6)); + }); + }); } diff --git a/testing/dart/canvas_test.dart b/testing/dart/canvas_test.dart index 76c9c30dae250..7505012fbc350 100644 --- a/testing/dart/canvas_test.dart +++ b/testing/dart/canvas_test.dart @@ -929,6 +929,47 @@ void main() { expect(canvas.getLocalClipBounds(), initialLocalBounds); expect(canvas.getDestinationClipBounds(), initialDestinationBounds); }); + + test('RestoreToCount can work', () async { + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + canvas.save(); + canvas.save(); + canvas.save(); + canvas.save(); + canvas.save(); + expect(canvas.getSaveCount(), equals(6)); + canvas.restoreToCount(2); + expect(canvas.getSaveCount(), equals(2)); + canvas.restore(); + expect(canvas.getSaveCount(), equals(1)); + }); + + test('RestoreToCount count less than 1, the stack should be reset', () async { + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + canvas.save(); + canvas.save(); + canvas.save(); + canvas.save(); + canvas.save(); + expect(canvas.getSaveCount(), equals(6)); + canvas.restoreToCount(0); + expect(canvas.getSaveCount(), equals(1)); + }); + + test('RestoreToCount count greater than current [getSaveCount], nothing would happend', () async { + final PictureRecorder recorder = PictureRecorder(); + final Canvas canvas = Canvas(recorder); + canvas.save(); + canvas.save(); + canvas.save(); + canvas.save(); + canvas.save(); + expect(canvas.getSaveCount(), equals(6)); + canvas.restoreToCount(canvas.getSaveCount() + 1); + expect(canvas.getSaveCount(), equals(6)); + }); } Matcher listEquals(ByteData expected) => (dynamic v) {