Loading services/surfaceflinger/RegionSamplingThread.cpp +52 −9 Original line number Diff line number Diff line Loading @@ -26,6 +26,8 @@ #include <utils/Trace.h> #include <string> #include <compositionengine/Display.h> #include <compositionengine/impl/OutputCompositionState.h> #include "DisplayDevice.h" #include "Layer.h" #include "SurfaceFlinger.h" Loading Loading @@ -264,7 +266,22 @@ float getLuma(float r, float g, float b) { } } // anonymous namespace float sampleArea(const uint32_t* data, int32_t stride, const Rect& area) { float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t stride, uint32_t orientation, const Rect& sample_area) { if (!sample_area.isValid() || (sample_area.getWidth() > width) || (sample_area.getHeight() > height)) { ALOGE("invalid sampling region requested"); return 0.0f; } // (b/133849373) ROT_90 screencap images produced upside down auto area = sample_area; if (orientation & ui::Transform::ROT_90) { area.top = height - area.top; area.bottom = height - area.bottom; std::swap(area.top, area.bottom); } std::array<int32_t, 256> brightnessBuckets = {}; const int32_t majoritySampleNum = area.getWidth() * area.getHeight() / 2; Loading Loading @@ -293,18 +310,21 @@ float sampleArea(const uint32_t* data, int32_t stride, const Rect& area) { std::vector<float> RegionSamplingThread::sampleBuffer( const sp<GraphicBuffer>& buffer, const Point& leftTop, const std::vector<RegionSamplingThread::Descriptor>& descriptors) { const std::vector<RegionSamplingThread::Descriptor>& descriptors, uint32_t orientation) { void* data_raw = nullptr; buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, &data_raw); std::shared_ptr<uint32_t> data(reinterpret_cast<uint32_t*>(data_raw), [&buffer](auto) { buffer->unlock(); }); if (!data) return {}; const int32_t width = buffer->getWidth(); const int32_t height = buffer->getHeight(); const int32_t stride = buffer->getStride(); std::vector<float> lumas(descriptors.size()); std::transform(descriptors.begin(), descriptors.end(), lumas.begin(), [&](auto const& descriptor) { return sampleArea(data.get(), stride, descriptor.area - leftTop); return sampleArea(data.get(), width, height, stride, orientation, descriptor.area - leftTop); }); return lumas; } Loading @@ -317,6 +337,11 @@ void RegionSamplingThread::captureSample() { return; } const auto device = mFlinger.getDefaultDisplayDevice(); const auto display = device->getCompositionDisplay(); const auto state = display->getState(); const auto orientation = static_cast<ui::Transform::orientation_flags>(state.orientation); std::vector<RegionSamplingThread::Descriptor> descriptors; Region sampleRegion; for (const auto& [listener, descriptor] : mDescriptors) { Loading @@ -326,10 +351,28 @@ void RegionSamplingThread::captureSample() { const Rect sampledArea = sampleRegion.bounds(); sp<const DisplayDevice> device = mFlinger.getDefaultDisplayDevice(); DisplayRenderArea renderArea(device, sampledArea, sampledArea.getWidth(), sampledArea.getHeight(), ui::Dataspace::V0_SRGB, ui::Transform::ROT_0); auto dx = 0; auto dy = 0; switch (orientation) { case ui::Transform::ROT_90: dx = device->getWidth(); break; case ui::Transform::ROT_180: dx = device->getWidth(); dy = device->getHeight(); break; case ui::Transform::ROT_270: dy = device->getHeight(); break; default: break; } ui::Transform t(orientation); auto screencapRegion = t.transform(sampleRegion); screencapRegion = screencapRegion.translate(dx, dy); DisplayRenderArea renderArea(device, screencapRegion.bounds(), sampledArea.getWidth(), sampledArea.getHeight(), ui::Dataspace::V0_SRGB, orientation); std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners; Loading Loading @@ -395,8 +438,8 @@ void RegionSamplingThread::captureSample() { } ALOGV("Sampling %zu descriptors", activeDescriptors.size()); std::vector<float> lumas = sampleBuffer(buffer, sampledArea.leftTop(), activeDescriptors); std::vector<float> lumas = sampleBuffer(buffer, sampledArea.leftTop(), activeDescriptors, orientation); if (lumas.size() != activeDescriptors.size()) { ALOGW("collected %zu median luma values for %zu descriptors", lumas.size(), activeDescriptors.size()); Loading services/surfaceflinger/RegionSamplingThread.h +3 −2 Original line number Diff line number Diff line Loading @@ -37,7 +37,8 @@ class Scheduler; class SurfaceFlinger; struct SamplingOffsetCallback; float sampleArea(const uint32_t* data, int32_t stride, const Rect& area); float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t stride, uint32_t orientation, const Rect& area); class RegionSamplingThread : public IBinder::DeathRecipient { public: Loading Loading @@ -94,7 +95,7 @@ private: }; std::vector<float> sampleBuffer( const sp<GraphicBuffer>& buffer, const Point& leftTop, const std::vector<RegionSamplingThread::Descriptor>& descriptors); const std::vector<RegionSamplingThread::Descriptor>& descriptors, uint32_t orientation); void doSample(); void binderDied(const wp<IBinder>& who) override; Loading services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp +67 −5 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ #undef LOG_TAG #define LOG_TAG "RegionSamplingTest" #include <ui/Transform.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <array> Loading @@ -33,18 +35,21 @@ public: static int constexpr kWidth = 98; static int constexpr kStride = 100; static int constexpr kHeight = 29; static int constexpr kOrientation = ui::Transform::ROT_0; std::array<uint32_t, kHeight * kStride> buffer; Rect const whole_area{0, 0, kWidth, kHeight}; }; TEST_F(RegionSamplingTest, calculate_mean_white) { std::fill(buffer.begin(), buffer.end(), kWhite); EXPECT_THAT(sampleArea(buffer.data(), kStride, whole_area), testing::FloatEq(1.0f)); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area), testing::FloatEq(1.0f)); } TEST_F(RegionSamplingTest, calculate_mean_black) { std::fill(buffer.begin(), buffer.end(), kBlack); EXPECT_THAT(sampleArea(buffer.data(), kStride, whole_area), testing::FloatEq(0.0f)); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area), testing::FloatEq(0.0f)); } TEST_F(RegionSamplingTest, calculate_mean_partial_region) { Loading @@ -54,7 +59,8 @@ TEST_F(RegionSamplingTest, calculate_mean_partial_region) { whole_area.top + halfway_down}; std::fill(buffer.begin(), buffer.begin() + half, 0); std::fill(buffer.begin() + half, buffer.end(), kWhite); EXPECT_THAT(sampleArea(buffer.data(), kStride, partial_region), testing::FloatEq(0.0f)); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, partial_region), testing::FloatEq(0.0f)); } TEST_F(RegionSamplingTest, calculate_mean_mixed_values) { Loading @@ -63,15 +69,71 @@ TEST_F(RegionSamplingTest, calculate_mean_mixed_values) { n++; return pixel; }); EXPECT_THAT(sampleArea(buffer.data(), kStride, whole_area), testing::FloatNear(0.083f, 0.01f)); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area), testing::FloatNear(0.083f, 0.01f)); } TEST_F(RegionSamplingTest, bimodal_tiebreaker) { std::generate(buffer.begin(), buffer.end(), [n = 0]() mutable { return (n++ % 2) ? kBlack : kWhite; }); // presently there's no tiebreaking strategy in place, accept either of the means EXPECT_THAT(sampleArea(buffer.data(), kStride, whole_area), EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area), testing::AnyOf(testing::FloatEq(1.0), testing::FloatEq(0.0f))); } TEST_F(RegionSamplingTest, bounds_checking) { std::generate(buffer.begin(), buffer.end(), [n = 0]() mutable { return (n++ > (kStride * kHeight >> 1)) ? kBlack : kWhite; }); Rect invalid_region{0, 0, 4, kHeight + 1}; EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, invalid_region), testing::Eq(0.0)); invalid_region = Rect{0, 0, -4, kHeight}; EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, invalid_region), testing::Eq(0.0)); invalid_region = Rect{3, 0, 2, 0}; EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, invalid_region), testing::Eq(0.0)); invalid_region = Rect{0, 3, 0, 2}; EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, invalid_region), testing::Eq(0.0)); } // workaround for b/133849373 TEST_F(RegionSamplingTest, orientation_90) { std::generate(buffer.begin(), buffer.end(), [n = 0]() mutable { return (n++ > (kStride * kHeight >> 1)) ? kBlack : kWhite; }); Rect tl_region{0, 0, 4, 4}; EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_0, tl_region), testing::Eq(1.0)); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_180, tl_region), testing::Eq(1.0)); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_90, tl_region), testing::Eq(0.0)); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_270, tl_region), testing::Eq(0.0)); Rect br_region{kWidth - 4, kHeight - 4, kWidth, kHeight}; EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_0, br_region), testing::Eq(0.0)); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_180, br_region), testing::Eq(0.0)); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_90, br_region), testing::Eq(1.0)); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_270, br_region), testing::Eq(1.0)); } } // namespace android Loading
services/surfaceflinger/RegionSamplingThread.cpp +52 −9 Original line number Diff line number Diff line Loading @@ -26,6 +26,8 @@ #include <utils/Trace.h> #include <string> #include <compositionengine/Display.h> #include <compositionengine/impl/OutputCompositionState.h> #include "DisplayDevice.h" #include "Layer.h" #include "SurfaceFlinger.h" Loading Loading @@ -264,7 +266,22 @@ float getLuma(float r, float g, float b) { } } // anonymous namespace float sampleArea(const uint32_t* data, int32_t stride, const Rect& area) { float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t stride, uint32_t orientation, const Rect& sample_area) { if (!sample_area.isValid() || (sample_area.getWidth() > width) || (sample_area.getHeight() > height)) { ALOGE("invalid sampling region requested"); return 0.0f; } // (b/133849373) ROT_90 screencap images produced upside down auto area = sample_area; if (orientation & ui::Transform::ROT_90) { area.top = height - area.top; area.bottom = height - area.bottom; std::swap(area.top, area.bottom); } std::array<int32_t, 256> brightnessBuckets = {}; const int32_t majoritySampleNum = area.getWidth() * area.getHeight() / 2; Loading Loading @@ -293,18 +310,21 @@ float sampleArea(const uint32_t* data, int32_t stride, const Rect& area) { std::vector<float> RegionSamplingThread::sampleBuffer( const sp<GraphicBuffer>& buffer, const Point& leftTop, const std::vector<RegionSamplingThread::Descriptor>& descriptors) { const std::vector<RegionSamplingThread::Descriptor>& descriptors, uint32_t orientation) { void* data_raw = nullptr; buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, &data_raw); std::shared_ptr<uint32_t> data(reinterpret_cast<uint32_t*>(data_raw), [&buffer](auto) { buffer->unlock(); }); if (!data) return {}; const int32_t width = buffer->getWidth(); const int32_t height = buffer->getHeight(); const int32_t stride = buffer->getStride(); std::vector<float> lumas(descriptors.size()); std::transform(descriptors.begin(), descriptors.end(), lumas.begin(), [&](auto const& descriptor) { return sampleArea(data.get(), stride, descriptor.area - leftTop); return sampleArea(data.get(), width, height, stride, orientation, descriptor.area - leftTop); }); return lumas; } Loading @@ -317,6 +337,11 @@ void RegionSamplingThread::captureSample() { return; } const auto device = mFlinger.getDefaultDisplayDevice(); const auto display = device->getCompositionDisplay(); const auto state = display->getState(); const auto orientation = static_cast<ui::Transform::orientation_flags>(state.orientation); std::vector<RegionSamplingThread::Descriptor> descriptors; Region sampleRegion; for (const auto& [listener, descriptor] : mDescriptors) { Loading @@ -326,10 +351,28 @@ void RegionSamplingThread::captureSample() { const Rect sampledArea = sampleRegion.bounds(); sp<const DisplayDevice> device = mFlinger.getDefaultDisplayDevice(); DisplayRenderArea renderArea(device, sampledArea, sampledArea.getWidth(), sampledArea.getHeight(), ui::Dataspace::V0_SRGB, ui::Transform::ROT_0); auto dx = 0; auto dy = 0; switch (orientation) { case ui::Transform::ROT_90: dx = device->getWidth(); break; case ui::Transform::ROT_180: dx = device->getWidth(); dy = device->getHeight(); break; case ui::Transform::ROT_270: dy = device->getHeight(); break; default: break; } ui::Transform t(orientation); auto screencapRegion = t.transform(sampleRegion); screencapRegion = screencapRegion.translate(dx, dy); DisplayRenderArea renderArea(device, screencapRegion.bounds(), sampledArea.getWidth(), sampledArea.getHeight(), ui::Dataspace::V0_SRGB, orientation); std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners; Loading Loading @@ -395,8 +438,8 @@ void RegionSamplingThread::captureSample() { } ALOGV("Sampling %zu descriptors", activeDescriptors.size()); std::vector<float> lumas = sampleBuffer(buffer, sampledArea.leftTop(), activeDescriptors); std::vector<float> lumas = sampleBuffer(buffer, sampledArea.leftTop(), activeDescriptors, orientation); if (lumas.size() != activeDescriptors.size()) { ALOGW("collected %zu median luma values for %zu descriptors", lumas.size(), activeDescriptors.size()); Loading
services/surfaceflinger/RegionSamplingThread.h +3 −2 Original line number Diff line number Diff line Loading @@ -37,7 +37,8 @@ class Scheduler; class SurfaceFlinger; struct SamplingOffsetCallback; float sampleArea(const uint32_t* data, int32_t stride, const Rect& area); float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t stride, uint32_t orientation, const Rect& area); class RegionSamplingThread : public IBinder::DeathRecipient { public: Loading Loading @@ -94,7 +95,7 @@ private: }; std::vector<float> sampleBuffer( const sp<GraphicBuffer>& buffer, const Point& leftTop, const std::vector<RegionSamplingThread::Descriptor>& descriptors); const std::vector<RegionSamplingThread::Descriptor>& descriptors, uint32_t orientation); void doSample(); void binderDied(const wp<IBinder>& who) override; Loading
services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp +67 −5 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ #undef LOG_TAG #define LOG_TAG "RegionSamplingTest" #include <ui/Transform.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <array> Loading @@ -33,18 +35,21 @@ public: static int constexpr kWidth = 98; static int constexpr kStride = 100; static int constexpr kHeight = 29; static int constexpr kOrientation = ui::Transform::ROT_0; std::array<uint32_t, kHeight * kStride> buffer; Rect const whole_area{0, 0, kWidth, kHeight}; }; TEST_F(RegionSamplingTest, calculate_mean_white) { std::fill(buffer.begin(), buffer.end(), kWhite); EXPECT_THAT(sampleArea(buffer.data(), kStride, whole_area), testing::FloatEq(1.0f)); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area), testing::FloatEq(1.0f)); } TEST_F(RegionSamplingTest, calculate_mean_black) { std::fill(buffer.begin(), buffer.end(), kBlack); EXPECT_THAT(sampleArea(buffer.data(), kStride, whole_area), testing::FloatEq(0.0f)); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area), testing::FloatEq(0.0f)); } TEST_F(RegionSamplingTest, calculate_mean_partial_region) { Loading @@ -54,7 +59,8 @@ TEST_F(RegionSamplingTest, calculate_mean_partial_region) { whole_area.top + halfway_down}; std::fill(buffer.begin(), buffer.begin() + half, 0); std::fill(buffer.begin() + half, buffer.end(), kWhite); EXPECT_THAT(sampleArea(buffer.data(), kStride, partial_region), testing::FloatEq(0.0f)); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, partial_region), testing::FloatEq(0.0f)); } TEST_F(RegionSamplingTest, calculate_mean_mixed_values) { Loading @@ -63,15 +69,71 @@ TEST_F(RegionSamplingTest, calculate_mean_mixed_values) { n++; return pixel; }); EXPECT_THAT(sampleArea(buffer.data(), kStride, whole_area), testing::FloatNear(0.083f, 0.01f)); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area), testing::FloatNear(0.083f, 0.01f)); } TEST_F(RegionSamplingTest, bimodal_tiebreaker) { std::generate(buffer.begin(), buffer.end(), [n = 0]() mutable { return (n++ % 2) ? kBlack : kWhite; }); // presently there's no tiebreaking strategy in place, accept either of the means EXPECT_THAT(sampleArea(buffer.data(), kStride, whole_area), EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, whole_area), testing::AnyOf(testing::FloatEq(1.0), testing::FloatEq(0.0f))); } TEST_F(RegionSamplingTest, bounds_checking) { std::generate(buffer.begin(), buffer.end(), [n = 0]() mutable { return (n++ > (kStride * kHeight >> 1)) ? kBlack : kWhite; }); Rect invalid_region{0, 0, 4, kHeight + 1}; EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, invalid_region), testing::Eq(0.0)); invalid_region = Rect{0, 0, -4, kHeight}; EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, invalid_region), testing::Eq(0.0)); invalid_region = Rect{3, 0, 2, 0}; EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, invalid_region), testing::Eq(0.0)); invalid_region = Rect{0, 3, 0, 2}; EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, kOrientation, invalid_region), testing::Eq(0.0)); } // workaround for b/133849373 TEST_F(RegionSamplingTest, orientation_90) { std::generate(buffer.begin(), buffer.end(), [n = 0]() mutable { return (n++ > (kStride * kHeight >> 1)) ? kBlack : kWhite; }); Rect tl_region{0, 0, 4, 4}; EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_0, tl_region), testing::Eq(1.0)); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_180, tl_region), testing::Eq(1.0)); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_90, tl_region), testing::Eq(0.0)); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_270, tl_region), testing::Eq(0.0)); Rect br_region{kWidth - 4, kHeight - 4, kWidth, kHeight}; EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_0, br_region), testing::Eq(0.0)); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_180, br_region), testing::Eq(0.0)); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_90, br_region), testing::Eq(1.0)); EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_270, br_region), testing::Eq(1.0)); } } // namespace android