Add captureLayers function to capture a layer and its children.
The captureLayers function gets a root layer as its argument.
It will capture the content for that layer and its descendants. The
capture will set the root layer's transform back to (0, 0).
Test: Transaction_test ScreenCaptureTest
Change-Id: I84fb66a65cd91434cddc99506b1924cf9f950935
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index f61a978..ec87eee 100644
--- a/services/surfaceflinger/tests/Transaction_test.cpp
+++ b/services/surfaceflinger/tests/Transaction_test.cpp
@@ -116,6 +116,60 @@
CpuConsumer::LockedBuffer mBuf;
};
+class CaptureLayer {
+public:
+ static void captureScreen(std::unique_ptr<CaptureLayer>* sc, sp<IBinder>& parentHandle) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+ sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1);
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ sp<IBinder> display(sf->getBuiltInDisplay(
+ ISurfaceComposer::eDisplayIdMain));
+ SurfaceComposerClient::Transaction().apply(true);
+ ASSERT_EQ(NO_ERROR, sf->captureLayers(parentHandle, producer));
+ *sc = std::make_unique<CaptureLayer>(cpuConsumer);
+ }
+
+ void checkPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b) {
+ ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mBuffer.format);
+ const uint8_t* img = static_cast<const uint8_t*>(mBuffer.data);
+ const uint8_t* pixel = img + (4 * (y * mBuffer.stride + x));
+ if (r != pixel[0] || g != pixel[1] || b != pixel[2]) {
+ String8 err(String8::format("pixel @ (%3d, %3d): "
+ "expected [%3d, %3d, %3d], got [%3d, %3d, %3d]",
+ x, y, r, g, b, pixel[0], pixel[1], pixel[2]));
+ EXPECT_EQ(String8(), err) << err.string();
+ }
+ }
+
+ void expectFGColor(uint32_t x, uint32_t y) {
+ checkPixel(x, y, 195, 63, 63);
+ }
+
+ void expectBGColor(uint32_t x, uint32_t y) {
+ checkPixel(x, y, 63, 63, 195);
+ }
+
+ void expectChildColor(uint32_t x, uint32_t y) {
+ checkPixel(x, y, 200, 200, 200);
+ }
+
+ CaptureLayer(const sp<CpuConsumer>& cc) :
+ mCC(cc) {
+ EXPECT_EQ(NO_ERROR, mCC->lockNextBuffer(&mBuffer));
+ }
+
+ ~CaptureLayer() {
+ mCC->unlockBuffer(mBuffer);
+ }
+
+private:
+ sp<CpuConsumer> mCC;
+ CpuConsumer::LockedBuffer mBuffer;
+};
+
+
class LayerUpdateTest : public ::testing::Test {
protected:
virtual void SetUp() {
@@ -1431,4 +1485,109 @@
}
}
+class ScreenCaptureTest : public LayerUpdateTest {
+protected:
+ std::unique_ptr<CaptureLayer> mCapture;
+};
+
+TEST_F(ScreenCaptureTest, CaptureSingleLayer) {
+ auto bgHandle = mBGSurfaceControl->getHandle();
+ CaptureLayer::captureScreen(&mCapture, bgHandle);
+ mCapture->expectBGColor(0, 0);
+ // Doesn't capture FG layer which is at 64, 64
+ mCapture->expectBGColor(64, 64);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerWithChild) {
+ auto fgHandle = mFGSurfaceControl->getHandle();
+
+ sp<SurfaceControl> child = mComposerClient->createSurface(
+ String8("Child surface"),
+ 10, 10, PIXEL_FORMAT_RGBA_8888,
+ 0, mFGSurfaceControl.get());
+ fillSurfaceRGBA8(child, 200, 200, 200);
+
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ .apply(true);
+
+ // Captures mFGSurfaceControl layer and its child.
+ CaptureLayer::captureScreen(&mCapture, fgHandle);
+ mCapture->expectFGColor(10, 10);
+ mCapture->expectChildColor(0, 0);
+}
+
+TEST_F(ScreenCaptureTest, CaptureLayerWithGrandchild) {
+ auto fgHandle = mFGSurfaceControl->getHandle();
+
+ sp<SurfaceControl> child = mComposerClient->createSurface(
+ String8("Child surface"),
+ 10, 10, PIXEL_FORMAT_RGBA_8888,
+ 0, mFGSurfaceControl.get());
+ fillSurfaceRGBA8(child, 200, 200, 200);
+
+ sp<SurfaceControl> grandchild = mComposerClient->createSurface(
+ String8("Grandchild surface"), 5, 5,
+ PIXEL_FORMAT_RGBA_8888, 0, child.get());
+
+ fillSurfaceRGBA8(grandchild, 50, 50, 50);
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ .setPosition(grandchild, 5, 5)
+ .show(grandchild)
+ .apply(true);
+
+ // Captures mFGSurfaceControl, its child, and the grandchild.
+ CaptureLayer::captureScreen(&mCapture, fgHandle);
+ mCapture->expectFGColor(10, 10);
+ mCapture->expectChildColor(0, 0);
+ mCapture->checkPixel(5, 5, 50, 50, 50);
+}
+
+TEST_F(ScreenCaptureTest, CaptureChildOnly) {
+ sp<SurfaceControl> child = mComposerClient->createSurface(
+ String8("Child surface"),
+ 10, 10, PIXEL_FORMAT_RGBA_8888,
+ 0, mFGSurfaceControl.get());
+ fillSurfaceRGBA8(child, 200, 200, 200);
+ auto childHandle = child->getHandle();
+
+ SurfaceComposerClient::Transaction()
+ .setPosition(child, 5, 5)
+ .show(child)
+ .apply(true);
+
+ // Captures only the child layer, and not the parent.
+ CaptureLayer::captureScreen(&mCapture, childHandle);
+ mCapture->expectChildColor(0, 0);
+ mCapture->expectChildColor(9, 9);
+}
+
+TEST_F(ScreenCaptureTest, CaptureGrandchildOnly) {
+ sp<SurfaceControl> child = mComposerClient->createSurface(
+ String8("Child surface"),
+ 10, 10, PIXEL_FORMAT_RGBA_8888,
+ 0, mFGSurfaceControl.get());
+ fillSurfaceRGBA8(child, 200, 200, 200);
+ auto childHandle = child->getHandle();
+
+ sp<SurfaceControl> grandchild = mComposerClient->createSurface(
+ String8("Grandchild surface"), 5, 5,
+ PIXEL_FORMAT_RGBA_8888, 0, child.get());
+ fillSurfaceRGBA8(grandchild, 50, 50, 50);
+
+ SurfaceComposerClient::Transaction()
+ .show(child)
+ .setPosition(grandchild, 5, 5)
+ .show(grandchild)
+ .apply(true);
+
+ auto grandchildHandle = grandchild->getHandle();
+
+ // Captures only the grandchild.
+ CaptureLayer::captureScreen(&mCapture, grandchildHandle);
+ mCapture->checkPixel(0, 0, 50, 50, 50);
+ mCapture->checkPixel(4, 4, 50, 50, 50);
+}
+
}