1 /* 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 <android-base/file.h> 19 #include <android/bitmap.h> 20 #include <android/data_space.h> 21 #include <android/imagedecoder.h> 22 #include <gui/AidlUtil.h> 23 #include <gui/SyncScreenCaptureListener.h> 24 #include <private/gui/ComposerServiceAIDL.h> 25 #include <ui/FenceResult.h> 26 #include <ui/PixelFormat.h> 27 #include <ui/Rect.h> 28 #include <utils/String8.h> 29 #include <functional> 30 #include "TransactionUtils.h" 31 32 #include <filesystem> 33 #include <fstream> 34 35 namespace android { 36 37 using gui::aidl_utils::statusTFromBinderStatus; 38 39 namespace { 40 41 // A ScreenCapture is a screenshot from SurfaceFlinger that can be used to check 42 // individual pixel values for testing purposes. 43 class ScreenCapture : public RefBase { 44 public: captureDisplay(DisplayCaptureArgs & captureArgs,ScreenCaptureResults & captureResults)45 static status_t captureDisplay(DisplayCaptureArgs& captureArgs, 46 ScreenCaptureResults& captureResults) { 47 const auto sf = ComposerServiceAIDL::getComposerService(); 48 SurfaceComposerClient::Transaction().apply(true); 49 50 captureArgs.captureArgs.dataspace = static_cast<int32_t>(ui::Dataspace::V0_SRGB); 51 const sp<SyncScreenCaptureListener> captureListener = sp<SyncScreenCaptureListener>::make(); 52 binder::Status status = sf->captureDisplay(captureArgs, captureListener); 53 status_t err = statusTFromBinderStatus(status); 54 if (err != NO_ERROR) { 55 return err; 56 } 57 captureResults = captureListener->waitForResults(); 58 return fenceStatus(captureResults.fenceResult); 59 } 60 captureScreen(std::unique_ptr<ScreenCapture> * sc)61 static void captureScreen(std::unique_ptr<ScreenCapture>* sc) { 62 const auto ids = SurfaceComposerClient::getPhysicalDisplayIds(); 63 // TODO(b/248317436): extend to cover all displays for multi-display devices 64 const auto display = 65 ids.empty() ? nullptr : SurfaceComposerClient::getPhysicalDisplayToken(ids.front()); 66 captureScreen(sc, display); 67 } 68 captureScreen(std::unique_ptr<ScreenCapture> * sc,sp<IBinder> displayToken)69 static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) { 70 DisplayCaptureArgs args; 71 args.displayToken = displayToken; 72 captureDisplay(sc, args); 73 } 74 captureDisplay(std::unique_ptr<ScreenCapture> * sc,DisplayCaptureArgs & captureArgs)75 static void captureDisplay(std::unique_ptr<ScreenCapture>* sc, 76 DisplayCaptureArgs& captureArgs) { 77 ScreenCaptureResults captureResults; 78 ASSERT_EQ(NO_ERROR, captureDisplay(captureArgs, captureResults)); 79 *sc = std::make_unique<ScreenCapture>(captureResults.buffer, 80 captureResults.capturedHdrLayers); 81 } 82 captureLayers(LayerCaptureArgs & captureArgs,ScreenCaptureResults & captureResults)83 static status_t captureLayers(LayerCaptureArgs& captureArgs, 84 ScreenCaptureResults& captureResults) { 85 const auto sf = ComposerServiceAIDL::getComposerService(); 86 SurfaceComposerClient::Transaction().apply(true); 87 88 captureArgs.captureArgs.dataspace = static_cast<int32_t>(ui::Dataspace::V0_SRGB); 89 const sp<SyncScreenCaptureListener> captureListener = sp<SyncScreenCaptureListener>::make(); 90 binder::Status status = sf->captureLayers(captureArgs, captureListener); 91 status_t err = statusTFromBinderStatus(status); 92 if (err != NO_ERROR) { 93 return err; 94 } 95 captureResults = captureListener->waitForResults(); 96 return fenceStatus(captureResults.fenceResult); 97 } 98 captureLayers(std::unique_ptr<ScreenCapture> * sc,LayerCaptureArgs & captureArgs)99 static void captureLayers(std::unique_ptr<ScreenCapture>* sc, LayerCaptureArgs& captureArgs) { 100 ScreenCaptureResults captureResults; 101 ASSERT_EQ(NO_ERROR, captureLayers(captureArgs, captureResults)); 102 *sc = std::make_unique<ScreenCapture>(captureResults.buffer, 103 captureResults.capturedHdrLayers); 104 } 105 capturedHdrLayers()106 bool capturedHdrLayers() const { return mContainsHdr; } 107 108 void expectColor(const Rect& rect, const Color& color, uint8_t tolerance = 0) { 109 ASSERT_NE(nullptr, mOutBuffer); 110 ASSERT_NE(nullptr, mPixels); 111 ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat()); 112 TransactionUtils::expectBufferColor(mOutBuffer, mPixels, rect, color, tolerance); 113 } 114 115 void expectBorder(const Rect& rect, const Color& color, uint8_t tolerance = 0) { 116 ASSERT_NE(nullptr, mOutBuffer); 117 ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat()); 118 const bool leftBorder = rect.left > 0; 119 const bool topBorder = rect.top > 0; 120 const bool rightBorder = rect.right < int32_t(mOutBuffer->getWidth()); 121 const bool bottomBorder = rect.bottom < int32_t(mOutBuffer->getHeight()); 122 123 if (topBorder) { 124 Rect top(rect.left, rect.top - 1, rect.right, rect.top); 125 if (leftBorder) { 126 top.left -= 1; 127 } 128 if (rightBorder) { 129 top.right += 1; 130 } 131 expectColor(top, color, tolerance); 132 } 133 if (leftBorder) { 134 Rect left(rect.left - 1, rect.top, rect.left, rect.bottom); 135 expectColor(left, color, tolerance); 136 } 137 if (rightBorder) { 138 Rect right(rect.right, rect.top, rect.right + 1, rect.bottom); 139 expectColor(right, color, tolerance); 140 } 141 if (bottomBorder) { 142 Rect bottom(rect.left, rect.bottom, rect.right, rect.bottom + 1); 143 if (leftBorder) { 144 bottom.left -= 1; 145 } 146 if (rightBorder) { 147 bottom.right += 1; 148 } 149 expectColor(bottom, color, tolerance); 150 } 151 } 152 153 void expectQuadrant(const Rect& rect, const Color& topLeft, const Color& topRight, 154 const Color& bottomLeft, const Color& bottomRight, bool filtered = false, 155 uint8_t tolerance = 0) { 156 ASSERT_TRUE((rect.right - rect.left) % 2 == 0 && (rect.bottom - rect.top) % 2 == 0); 157 158 const int32_t centerX = rect.left + (rect.right - rect.left) / 2; 159 const int32_t centerY = rect.top + (rect.bottom - rect.top) / 2; 160 // avoid checking borders due to unspecified filtering behavior 161 const int32_t offsetX = filtered ? 2 : 0; 162 const int32_t offsetY = filtered ? 2 : 0; 163 expectColor(Rect(rect.left, rect.top, centerX - offsetX, centerY - offsetY), topLeft, 164 tolerance); 165 expectColor(Rect(centerX + offsetX, rect.top, rect.right, centerY - offsetY), topRight, 166 tolerance); 167 expectColor(Rect(rect.left, centerY + offsetY, centerX - offsetX, rect.bottom), bottomLeft, 168 tolerance); 169 expectColor(Rect(centerX + offsetX, centerY + offsetY, rect.right, rect.bottom), 170 bottomRight, tolerance); 171 } 172 checkPixel(uint32_t x,uint32_t y,uint8_t r,uint8_t g,uint8_t b)173 void checkPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t b) { 174 ASSERT_NE(nullptr, mOutBuffer); 175 ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat()); 176 const uint8_t* pixel = mPixels + (4 * (y * mOutBuffer->getStride() + x)); 177 if (r != pixel[0] || g != pixel[1] || b != pixel[2]) { 178 String8 err(String8::format("pixel @ (%3d, %3d): " 179 "expected [%3d, %3d, %3d], got [%3d, %3d, %3d]", 180 x, y, r, g, b, pixel[0], pixel[1], pixel[2])); 181 EXPECT_EQ(String8(), err) << err.c_str(); 182 } 183 } 184 writePng(const std::filesystem::path & path,const void * pixels,uint32_t width,uint32_t height,uint32_t stride)185 static void writePng(const std::filesystem::path& path, const void* pixels, uint32_t width, 186 uint32_t height, uint32_t stride) { 187 AndroidBitmapInfo info{ 188 .width = width, 189 .height = height, 190 .stride = stride, 191 .format = ANDROID_BITMAP_FORMAT_RGBA_8888, 192 .flags = ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE, 193 }; 194 195 std::ofstream file(path, std::ios::binary); 196 ASSERT_TRUE(file.is_open()); 197 198 auto writeFunc = [](void* filePtr, const void* data, size_t size) -> bool { 199 auto file = reinterpret_cast<std::ofstream*>(filePtr); 200 file->write(reinterpret_cast<const char*>(data), size); 201 return file->good(); 202 }; 203 204 int compressResult = AndroidBitmap_compress(&info, ADATASPACE_SRGB, pixels, 205 ANDROID_BITMAP_COMPRESS_FORMAT_PNG, 206 /*(ignored) quality=*/100, &file, writeFunc); 207 ASSERT_EQ(compressResult, ANDROID_BITMAP_RESULT_SUCCESS); 208 file.close(); 209 } 210 readImage(const std::filesystem::path & filename,std::vector<uint8_t> & outBytes,int & outWidth,int & outHeight)211 static void readImage(const std::filesystem::path& filename, std::vector<uint8_t>& outBytes, 212 int& outWidth, int& outHeight) { 213 std::ifstream file(filename, std::ios::binary | std::ios::ate); 214 ASSERT_TRUE(file.is_open()) << "Failed to open " << filename; 215 216 size_t fileSize = file.tellg(); 217 file.seekg(0, std::ios::beg); 218 std::vector<char> fileData(fileSize); 219 file.read(fileData.data(), fileSize); 220 file.close(); 221 222 AImageDecoder* decoder = nullptr; 223 int createResult = AImageDecoder_createFromBuffer(fileData.data(), fileSize, &decoder); 224 225 ASSERT_EQ(createResult, ANDROID_IMAGE_DECODER_SUCCESS); 226 227 const AImageDecoderHeaderInfo* headerInfo = AImageDecoder_getHeaderInfo(decoder); 228 outWidth = AImageDecoderHeaderInfo_getWidth(headerInfo); 229 outHeight = AImageDecoderHeaderInfo_getHeight(headerInfo); 230 int32_t format = AImageDecoderHeaderInfo_getAndroidBitmapFormat(headerInfo); 231 ASSERT_EQ(format, ANDROID_BITMAP_FORMAT_RGBA_8888); 232 233 size_t stride = outWidth * 4; // Assuming RGBA format 234 size_t bufferSize = stride * outHeight; 235 236 outBytes.resize(bufferSize); 237 int decodeResult = AImageDecoder_decodeImage(decoder, outBytes.data(), stride, bufferSize); 238 ASSERT_EQ(decodeResult, ANDROID_IMAGE_DECODER_SUCCESS); 239 AImageDecoder_delete(decoder); 240 } 241 writeGraphicBufferToPng(const std::string & path,const sp<GraphicBuffer> & buffer)242 static void writeGraphicBufferToPng(const std::string& path, const sp<GraphicBuffer>& buffer) { 243 base::unique_fd fd{open(path.c_str(), O_WRONLY | O_CREAT, S_IWUSR)}; 244 ASSERT_GE(fd.get(), 0); 245 246 void* pixels = nullptr; 247 int32_t stride = 0; 248 auto lockStatus = buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, &pixels, 249 nullptr /*outBytesPerPixel*/, &stride); 250 ASSERT_GE(lockStatus, 0); 251 252 writePng(path, pixels, buffer->getWidth(), buffer->getHeight(), stride); 253 254 auto unlockStatus = buffer->unlock(); 255 ASSERT_GE(unlockStatus, 0); 256 } 257 258 // Tries to read an image from executable directory 259 // If the test fails, the screenshot is written to $TMPDIR expectBufferMatchesImageFromFile(const Rect & rect,const std::filesystem::path & pathRelativeToExeDir)260 void expectBufferMatchesImageFromFile(const Rect& rect, 261 const std::filesystem::path& pathRelativeToExeDir) { 262 ASSERT_NE(nullptr, mOutBuffer); 263 ASSERT_EQ(HAL_PIXEL_FORMAT_RGBA_8888, mOutBuffer->getPixelFormat()); 264 265 int bufferWidth = int32_t(mOutBuffer->getWidth()); 266 int bufferHeight = int32_t(mOutBuffer->getHeight()); 267 int bufferStride = mOutBuffer->getStride() * 4; 268 269 std::vector<uint8_t> imagePixels; 270 int imageWidth; 271 int imageHeight; 272 readImage(android::base::GetExecutableDirectory() / pathRelativeToExeDir, imagePixels, 273 imageWidth, imageHeight); 274 int imageStride = 4 * imageWidth; 275 276 ASSERT_TRUE(rect.isValid()); 277 278 ASSERT_GE(rect.left, 0); 279 ASSERT_GE(rect.bottom, 0); 280 281 ASSERT_LE(rect.right, bufferWidth); 282 ASSERT_LE(rect.bottom, bufferHeight); 283 284 ASSERT_LE(rect.right, imageWidth); 285 ASSERT_LE(rect.bottom, imageHeight); 286 287 int tolerance = 4; // arbitrary 288 for (int32_t y = rect.top; y < rect.bottom; y++) { 289 for (int32_t x = rect.left; x < rect.right; x++) { 290 const uint8_t* bufferPixel = mPixels + y * bufferStride + x * 4; 291 const uint8_t* imagePixel = 292 imagePixels.data() + (y - rect.top) * imageStride + (x - rect.left) * 4; 293 294 int dr = bufferPixel[0] - imagePixel[0]; 295 int dg = bufferPixel[1] - imagePixel[1]; 296 int db = bufferPixel[2] - imagePixel[2]; 297 int da = bufferPixel[3] - imagePixel[3]; 298 int dist = std::abs(dr) + std::abs(dg) + std::abs(db) + std::abs(da); 299 300 bool pixelMatches = dist < tolerance; 301 302 if (!pixelMatches) { 303 std::filesystem::path outFilename = pathRelativeToExeDir.filename(); 304 outFilename.replace_extension(); 305 outFilename += "_actual.png"; 306 std::filesystem::path outPath = std::filesystem::temp_directory_path() / 307 "SurfaceFlinger_test_screenshots" / outFilename; 308 writeGraphicBufferToPng(outPath, mOutBuffer); 309 310 ASSERT_TRUE(pixelMatches) 311 << String8::format("pixel @ (%3d, %3d): " 312 "expected [%3d, %3d, %3d, %3d], got [%3d, %3d, %3d, " 313 "%3d], " 314 "wrote screenshot to '%s'", 315 x, y, imagePixel[0], imagePixel[1], imagePixel[2], 316 imagePixel[3], bufferPixel[0], bufferPixel[1], 317 bufferPixel[2], bufferPixel[3], outPath.c_str()) 318 .c_str(); 319 return; 320 } 321 } 322 } 323 } 324 getPixelColor(uint32_t x,uint32_t y)325 Color getPixelColor(uint32_t x, uint32_t y) { 326 if (!mOutBuffer || mOutBuffer->getPixelFormat() != HAL_PIXEL_FORMAT_RGBA_8888) { 327 return {0, 0, 0, 0}; 328 } 329 330 const uint8_t* pixel = mPixels + (4 * (y * mOutBuffer->getStride() + x)); 331 return {pixel[0], pixel[1], pixel[2], pixel[3]}; 332 } 333 expectFGColor(uint32_t x,uint32_t y)334 void expectFGColor(uint32_t x, uint32_t y) { checkPixel(x, y, 195, 63, 63); } 335 expectBGColor(uint32_t x,uint32_t y)336 void expectBGColor(uint32_t x, uint32_t y) { checkPixel(x, y, 63, 63, 195); } 337 expectChildColor(uint32_t x,uint32_t y)338 void expectChildColor(uint32_t x, uint32_t y) { checkPixel(x, y, 200, 200, 200); } 339 expectSize(uint32_t width,uint32_t height)340 void expectSize(uint32_t width, uint32_t height) { 341 EXPECT_EQ(width, mOutBuffer->getWidth()); 342 EXPECT_EQ(height, mOutBuffer->getHeight()); 343 } 344 ScreenCapture(const sp<GraphicBuffer> & outBuffer,bool containsHdr)345 explicit ScreenCapture(const sp<GraphicBuffer>& outBuffer, bool containsHdr) 346 : mOutBuffer(outBuffer), mContainsHdr(containsHdr) { 347 if (mOutBuffer) { 348 mOutBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&mPixels)); 349 } 350 } 351 ~ScreenCapture()352 ~ScreenCapture() { 353 if (mOutBuffer) mOutBuffer->unlock(); 354 } 355 356 private: 357 sp<GraphicBuffer> mOutBuffer; 358 bool mContainsHdr = mContainsHdr; 359 uint8_t* mPixels = nullptr; 360 }; 361 } // namespace 362 } // namespace android 363