blob: 1d94f21fb2b6bd71af18eb57de063a0906dbe5fc [file] [log] [blame]
Valerie Haue9137b72019-08-27 13:22:18 -07001/*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#pragma once
17
18#include <ui/Rect.h>
19#include <utils/String8.h>
chaviw8ffc7b82020-08-18 11:25:37 -070020#include <functional>
Chavi Weingarten99eeeb82020-09-10 20:55:11 +000021#include <future>
Valerie Haue9137b72019-08-27 13:22:18 -070022#include "TransactionUtils.h"
23
24namespace android {
25
26namespace {
27
Chavi Weingarten99eeeb82020-09-10 20:55:11 +000028class SyncScreenCaptureListener : public BnScreenCaptureListener {
29public:
30 status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) override {
31 resultsPromise.set_value(captureResults);
32 return NO_ERROR;
33 }
34
35 ScreenCaptureResults waitForResults() {
36 std::future<ScreenCaptureResults> resultsFuture = resultsPromise.get_future();
37 return resultsFuture.get();
38 }
39
40private:
41 std::promise<ScreenCaptureResults> resultsPromise;
42};
43
Valerie Haue9137b72019-08-27 13:22:18 -070044// A ScreenCapture is a screenshot from SurfaceFlinger that can be used to check
45// individual pixel values for testing purposes.
46class ScreenCapture : public RefBase {
47public:
chaviw8ffc7b82020-08-18 11:25:37 -070048 static status_t captureDisplay(DisplayCaptureArgs& captureArgs,
49 ScreenCaptureResults& captureResults) {
Valerie Haue9137b72019-08-27 13:22:18 -070050 const auto sf = ComposerService::getComposerService();
51 SurfaceComposerClient::Transaction().apply(true);
52
arthurhung79e81aa2020-08-28 00:09:19 +080053 captureArgs.useRGBColorSpace = true;
chaviw8ffc7b82020-08-18 11:25:37 -070054 const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
55 status_t status = sf->captureDisplay(captureArgs, captureListener);
arthurhung79e81aa2020-08-28 00:09:19 +080056
chaviw8ffc7b82020-08-18 11:25:37 -070057 if (status != NO_ERROR) {
58 return status;
59 }
60 captureResults = captureListener->waitForResults();
61 return captureResults.result;
62 }
63
64 static void captureScreen(std::unique_ptr<ScreenCapture>* sc) {
65 captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken());
chaviw4b9d5e12020-08-04 18:30:35 -070066 }
67
68 static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) {
chaviwd2432892020-07-24 17:42:39 -070069 DisplayCaptureArgs args;
70 args.displayToken = displayToken;
chaviw4b9d5e12020-08-04 18:30:35 -070071 captureDisplay(sc, args);
Valerie Haue9137b72019-08-27 13:22:18 -070072 }
73
chaviw8ffc7b82020-08-18 11:25:37 -070074 static void captureDisplay(std::unique_ptr<ScreenCapture>* sc,
75 DisplayCaptureArgs& captureArgs) {
76 ScreenCaptureResults captureResults;
77 ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults));
78 *sc = std::make_unique<ScreenCapture>(captureResults.buffer);
79 }
80
81 static status_t captureLayers(LayerCaptureArgs& captureArgs,
82 ScreenCaptureResults& captureResults) {
83 const auto sf = ComposerService::getComposerService();
Valerie Haue9137b72019-08-27 13:22:18 -070084 SurfaceComposerClient::Transaction().apply(true);
85
arthurhung79e81aa2020-08-28 00:09:19 +080086 captureArgs.useRGBColorSpace = true;
chaviw8ffc7b82020-08-18 11:25:37 -070087 const sp<SyncScreenCaptureListener> captureListener = new SyncScreenCaptureListener();
88 status_t status = sf->captureLayers(captureArgs, captureListener);
89 if (status != NO_ERROR) {
90 return status;
91 }
92 captureResults = captureListener->waitForResults();
93 return captureResults.result;
94 }
95
96 static void captureLayers(std::unique_ptr<ScreenCapture>* sc, LayerCaptureArgs& captureArgs) {
chaviw3efadb12020-07-27 10:07:15 -070097 ScreenCaptureResults captureResults;
chaviw8ffc7b82020-08-18 11:25:37 -070098 ASSERT_EQ(NO_ERROR, captureLayers(captureArgs, captureResults));
chaviw3efadb12020-07-27 10:07:15 -070099 *sc = std::make_unique<ScreenCapture>(captureResults.buffer);
Valerie Haue9137b72019-08-27 13:22:18 -0700100 }
101
102 void expectColor(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
103 ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
Valerie Haue271df92019-09-06 09:23:22 -0700104 TransactionUtils::expectBufferColor(mOutBuffer, mPixels, rect, color, tolerance);
Valerie Haue9137b72019-08-27 13:22:18 -0700105 }
106
107 void expectBorder(const Rect& rect, const Color& color, uint8_t tolerance = 0) {
108 ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
109 const bool leftBorder = rect.left > 0;
110 const bool topBorder = rect.top > 0;
111 const bool rightBorder = rect.right < int32_t(mOutBuffer->getWidth());
112 const bool bottomBorder = rect.bottom < int32_t(mOutBuffer->getHeight());
113
114 if (topBorder) {
115 Rect top(rect.left, rect.top - 1, rect.right, rect.top);
116 if (leftBorder) {
117 top.left -= 1;
118 }
119 if (rightBorder) {
120 top.right += 1;
121 }
122 expectColor(top, color, tolerance);
123 }
124 if (leftBorder) {
125 Rect left(rect.left - 1, rect.top, rect.left, rect.bottom);
126 expectColor(left, color, tolerance);
127 }
128 if (rightBorder) {
129 Rect right(rect.right, rect.top, rect.right + 1, rect.bottom);
130 expectColor(right, color, tolerance);
131 }
132 if (bottomBorder) {
133 Rect bottom(rect.left, rect.bottom, rect.right, rect.bottom + 1);
134 if (leftBorder) {
135 bottom.left -= 1;
136 }
137 if (rightBorder) {
138 bottom.right += 1;
139 }
140 expectColor(bottom, color, tolerance);
141 }
142 }
143
144 void expectQuadrant(const Rect& rect, const Color& topLeft, const Color& topRight,
145 const Color& bottomLeft, const Color& bottomRight, bool filtered = false,
146 uint8_t tolerance = 0) {
147 ASSERT_TRUE((rect.right - rect.left) % 2 == 0 && (rect.bottom - rect.top) % 2 == 0);
148
149 const int32_t centerX = rect.left + (rect.right - rect.left) / 2;
150 const int32_t centerY = rect.top + (rect.bottom - rect.top) / 2;
151 // avoid checking borders due to unspecified filtering behavior
152 const int32_t offsetX = filtered ? 2 : 0;
153 const int32_t offsetY = filtered ? 2 : 0;
154 expectColor(Rect(rect.left, rect.top, centerX - offsetX, centerY - offsetY), topLeft,
155 tolerance);
156 expectColor(Rect(centerX + offsetX, rect.top, rect.right, centerY - offsetY), topRight,
157 tolerance);
158 expectColor(Rect(rect.left, centerY + offsetY, centerX - offsetX, rect.bottom), bottomLeft,
159 tolerance);
160 expectColor(Rect(centerX + offsetX, centerY + offsetY, rect.right, rect.bottom),
161 bottomRight, tolerance);
162 }
163
164 void checkPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b) {
165 ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat());
166 const uint8_t* pixel = mPixels + (4 * (y * mOutBuffer->getStride() + x));
167 if (r != pixel[0] || g != pixel[1] || b != pixel[2]) {
168 String8 err(String8::format("pixel @ (%3d, %3d): "
169 "expected [%3d, %3d, %3d], got [%3d, %3d, %3d]",
170 x, y, r, g, b, pixel[0], pixel[1], pixel[2]));
171 EXPECT_EQ(String8(), err) << err.string();
172 }
173 }
174
175 void expectFGColor(uint32_t x, uint32_t y) { checkPixel(x, y, 195, 63, 63); }
176
177 void expectBGColor(uint32_t x, uint32_t y) { checkPixel(x, y, 63, 63, 195); }
178
179 void expectChildColor(uint32_t x, uint32_t y) { checkPixel(x, y, 200, 200, 200); }
180
181 explicit ScreenCapture(const sp<GraphicBuffer>& outBuffer) : mOutBuffer(outBuffer) {
182 mOutBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&mPixels));
183 }
184
185 ~ScreenCapture() { mOutBuffer->unlock(); }
186
187private:
188 sp<GraphicBuffer> mOutBuffer;
189 uint8_t* mPixels = nullptr;
190};
191} // namespace
192} // namespace android