/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wextra" #include #include #include #include #include #include #include #include #include #include "ColorUtils.h" namespace android { namespace { using namespace std::chrono_literals; using Transaction = SurfaceComposerClient::Transaction; std::ostream& operator<<(std::ostream& os, const Color& color) { os << int(color.r) << ", " << int(color.g) << ", " << int(color.b) << ", " << int(color.a); return os; } class TransactionUtils { public: // Fill a region with the specified color. static void fillANativeWindowBufferColor(const ANativeWindow_Buffer& buffer, const Rect& rect, const Color& color) { Rect r(0, 0, buffer.width, buffer.height); if (!r.intersect(rect, &r)) { return; } int32_t width = r.right - r.left; int32_t height = r.bottom - r.top; for (int32_t row = 0; row < height; row++) { uint8_t* dst = static_cast(buffer.bits) + (buffer.stride * (r.top + row) + r.left) * 4; for (int32_t column = 0; column < width; column++) { dst[0] = color.r; dst[1] = color.g; dst[2] = color.b; dst[3] = color.a; dst += 4; } } } // Fill a region with the specified color. static void fillGraphicBufferColor(const sp& buffer, const Rect& rect, const Color& color) { Rect r(0, 0, buffer->width, buffer->height); if (!r.intersect(rect, &r)) { return; } int32_t width = r.right - r.left; int32_t height = r.bottom - r.top; uint8_t* pixels; buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN, reinterpret_cast(&pixels)); for (int32_t row = 0; row < height; row++) { uint8_t* dst = pixels + (buffer->getStride() * (r.top + row) + r.left) * 4; for (int32_t column = 0; column < width; column++) { dst[0] = color.r; dst[1] = color.g; dst[2] = color.b; dst[3] = color.a; dst += 4; } } buffer->unlock(); } // Check if a region has the specified color. static void expectBufferColor(const sp& outBuffer, uint8_t* pixels, const Rect& rect, const Color& color, uint8_t tolerance) { int32_t x = rect.left; int32_t y = rect.top; int32_t width = rect.right - rect.left; int32_t height = rect.bottom - rect.top; int32_t bufferWidth = int32_t(outBuffer->getWidth()); int32_t bufferHeight = int32_t(outBuffer->getHeight()); if (x + width > bufferWidth) { x = std::min(x, bufferWidth); width = bufferWidth - x; } if (y + height > bufferHeight) { y = std::min(y, bufferHeight); height = bufferHeight - y; } auto colorCompare = [tolerance](uint8_t a, uint8_t b) { uint8_t tmp = a >= b ? a - b : b - a; return tmp <= tolerance; }; for (int32_t j = 0; j < height; j++) { const uint8_t* src = pixels + (outBuffer->getStride() * (y + j) + x) * 4; for (int32_t i = 0; i < width; i++) { const uint8_t expected[4] = {color.r, color.g, color.b, color.a}; ASSERT_TRUE(std::equal(src, src + 4, expected, colorCompare)) << "pixel @ (" << x + i << ", " << y + j << "): " << "expected (" << color << "), " << "got (" << Color{src[0], src[1], src[2], src[3]} << ")"; src += 4; } } } static void fillSurfaceRGBA8(const sp& sc, const Color& color, bool unlock = true) { fillSurfaceRGBA8(sc, color.r, color.g, color.b, unlock); } // Fill an RGBA_8888 formatted surface with a single color. static void fillSurfaceRGBA8(const sp& sc, uint8_t r, uint8_t g, uint8_t b, bool unlock = true) { ANativeWindow_Buffer outBuffer; sp s = sc->getSurface(); ASSERT_TRUE(s != nullptr); ASSERT_EQ(NO_ERROR, s->lock(&outBuffer, nullptr)); uint8_t* img = reinterpret_cast(outBuffer.bits); for (int y = 0; y < outBuffer.height; y++) { for (int x = 0; x < outBuffer.width; x++) { uint8_t* pixel = img + (4 * (y * outBuffer.stride + x)); pixel[0] = r; pixel[1] = g; pixel[2] = b; pixel[3] = 255; } } if (unlock) { ASSERT_EQ(NO_ERROR, s->unlockAndPost()); } } static void setFrame(Transaction& t, const sp& sc, Rect source, Rect dest, int32_t transform = 0) { uint32_t sourceWidth = source.getWidth(); uint32_t sourceHeight = source.getHeight(); if (transform & ui::Transform::ROT_90) { std::swap(sourceWidth, sourceHeight); } float dsdx = dest.getWidth() / static_cast(sourceWidth); float dsdy = dest.getHeight() / static_cast(sourceHeight); t.setMatrix(sc, dsdx, 0, 0, dsdy); t.setPosition(sc, dest.left, dest.top); } }; enum class RenderPath { SCREENSHOT, VIRTUAL_DISPLAY }; // Environment for starting up binder threads. This is required for testing // virtual displays, as BufferQueue parameters may be queried over binder. class BinderEnvironment : public ::testing::Environment { public: void SetUp() override { ProcessState::self()->startThreadPool(); } }; /** RAII Wrapper around get/seteuid */ class UIDFaker { uid_t oldId; public: UIDFaker(uid_t uid) { oldId = geteuid(); seteuid(uid); } ~UIDFaker() { seteuid(oldId); } }; } // namespace } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues #pragma clang diagnostic pop // ignored "-Wconversion -Wextra"