Add SkiaCanvas::captureCanvasState()
Test: Verified bug is fixed. Wrote unit test.
Bug: 37268771
Change-Id: I9deb7db353cd2129ad245e7f65419670463bb717
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index c902a73..ece0b47 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -23,6 +23,7 @@
#include "hwui/MinikinUtils.h"
#include "pipeline/skia/AnimatedDrawables.h"
+#include <SkCanvasStateUtils.h>
#include <SkColorSpaceXformCanvas.h>
#include <SkDrawable.h>
#include <SkDeque.h>
@@ -411,6 +412,30 @@
}
// ----------------------------------------------------------------------------
+// Canvas state operations: Capture
+// ----------------------------------------------------------------------------
+
+SkCanvasState* SkiaCanvas::captureCanvasState() const {
+ SkCanvas* canvas = mCanvas;
+ if (mCanvasOwned) {
+ // Important to use the underlying SkCanvas, not the wrapper.
+ canvas = mCanvasOwned.get();
+ }
+
+ // Workarounds for http://crbug.com/271096: SW draw only supports
+ // translate & scale transforms, and a simple rectangular clip.
+ // (This also avoids significant wasted time in calling
+ // SkCanvasStateUtils::CaptureCanvasState when the clip is complex).
+ if (!canvas->isClipRect() ||
+ (canvas->getTotalMatrix().getType() &
+ ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask))) {
+ return nullptr;
+ }
+
+ return SkCanvasStateUtils::CaptureCanvasState(canvas);
+}
+
+// ----------------------------------------------------------------------------
// Canvas draw operations
// ----------------------------------------------------------------------------
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 45924f6..af2c23e 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -102,6 +102,8 @@
virtual SkDrawFilter* getDrawFilter() override;
virtual void setDrawFilter(SkDrawFilter* drawFilter) override;
+ virtual SkCanvasState* captureCanvasState() const override;
+
virtual void drawColor(int color, SkBlendMode mode) override;
virtual void drawPaint(const SkPaint& paint) override;
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 2267945..ac8a081 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -27,6 +27,7 @@
#include <SkCanvas.h>
#include <SkMatrix.h>
+class SkCanvasState;
class SkVertices;
namespace minikin {
@@ -204,6 +205,9 @@
virtual SkDrawFilter* getDrawFilter() = 0;
virtual void setDrawFilter(SkDrawFilter* drawFilter) = 0;
+ // WebView only
+ virtual SkCanvasState* captureCanvasState() const { return nullptr; }
+
// ----------------------------------------------------------------------------
// Canvas draw operations
// ----------------------------------------------------------------------------
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index 44476af..0d1eba4 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -19,6 +19,7 @@
#include <gtest/gtest.h>
#include <RecordingCanvas.h>
#include <SkBlurDrawLooper.h>
+#include <SkCanvasStateUtils.h>
#include <SkPicture.h>
#include <SkPictureRecorder.h>
@@ -128,3 +129,32 @@
canvas.asSkCanvas()->drawPicture(picture);
ASSERT_EQ(0xFF0000FF, *skBitmap.getAddr32(0, 0));
}
+
+TEST(SkiaCanvas, captureCanvasState) {
+ // Create a software canvas.
+ SkImageInfo info = SkImageInfo::Make(1, 1, kN32_SkColorType, kOpaque_SkAlphaType);
+ sk_sp<Bitmap> bitmap = Bitmap::allocateHeapBitmap(info);
+ SkBitmap skBitmap;
+ bitmap->getSkBitmap(&skBitmap);
+ skBitmap.eraseColor(0);
+ SkiaCanvas canvas(skBitmap);
+
+ // Translate, then capture and verify the CanvasState.
+ canvas.translate(1.0f, 1.0f);
+ SkCanvasState* state = canvas.captureCanvasState();
+ ASSERT_NE(state, nullptr);
+ std::unique_ptr<SkCanvas> newCanvas = SkCanvasStateUtils::MakeFromCanvasState(state);
+ ASSERT_NE(newCanvas.get(), nullptr);
+ newCanvas->translate(-1.0f, -1.0f);
+ ASSERT_TRUE(newCanvas->getTotalMatrix().isIdentity());
+ SkCanvasStateUtils::ReleaseCanvasState(state);
+
+ // Create a picture canvas.
+ SkPictureRecorder recorder;
+ SkCanvas* skPicCanvas = recorder.beginRecording(1, 1, NULL, 0);
+ SkiaCanvas picCanvas(skPicCanvas, Canvas::XformToSRGB::kDefer);
+ state = picCanvas.captureCanvasState();
+
+ // Verify that we cannot get the CanvasState.
+ ASSERT_EQ(state, nullptr);
+}