From 17ac24b6c1252f88ccaf7089fa68bee3e1ef7ed9 Mon Sep 17 00:00:00 2001 From: chaviw Date: Thu, 28 Jan 2021 18:50:05 -0800 Subject: [PATCH] Added new arguments for screenshot request Added frameScaleX and frameScaleY to replace frameScale to allow callers to specify an X and Y scale separately. Added grayscale flag to allow the caller to take the screenshot in grayscale. Test: ScreenCaptureTest.CaptureWithGrayscale Bug: 155825630 Change-Id: Iea043b7074707df897d80bf057d7cc3870afad89 --- libs/gui/LayerState.cpp | 8 ++- libs/gui/include/gui/LayerState.h | 5 +- .../surfaceflinger/RegionSamplingThread.cpp | 2 +- services/surfaceflinger/SurfaceFlinger.cpp | 59 +++++++++++-------- services/surfaceflinger/SurfaceFlinger.h | 9 ++- .../surfaceflinger/tests/LayerState_test.cpp | 16 +++-- .../tests/ScreenCapture_test.cpp | 39 +++++++++++- .../tests/unittests/TestableSurfaceFlinger.h | 3 +- 8 files changed, 102 insertions(+), 39 deletions(-) diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 2946aaed37..ab20725936 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -644,11 +644,13 @@ bool ValidateFrameRate(float frameRate, int8_t compatibility, const char* inFunc status_t CaptureArgs::write(Parcel& output) const { SAFE_PARCEL(output.writeInt32, static_cast(pixelFormat)); SAFE_PARCEL(output.write, sourceCrop); - SAFE_PARCEL(output.writeFloat, frameScale); + SAFE_PARCEL(output.writeFloat, frameScaleX); + SAFE_PARCEL(output.writeFloat, frameScaleY); SAFE_PARCEL(output.writeBool, captureSecureLayers); SAFE_PARCEL(output.writeInt32, uid); SAFE_PARCEL(output.writeInt32, static_cast(dataspace)); SAFE_PARCEL(output.writeBool, allowProtected); + SAFE_PARCEL(output.writeBool, grayscale); return NO_ERROR; } @@ -657,12 +659,14 @@ status_t CaptureArgs::read(const Parcel& input) { SAFE_PARCEL(input.readInt32, &value); pixelFormat = static_cast(value); SAFE_PARCEL(input.read, sourceCrop); - SAFE_PARCEL(input.readFloat, &frameScale); + SAFE_PARCEL(input.readFloat, &frameScaleX); + SAFE_PARCEL(input.readFloat, &frameScaleY); SAFE_PARCEL(input.readBool, &captureSecureLayers); SAFE_PARCEL(input.readInt32, &uid); SAFE_PARCEL(input.readInt32, &value); dataspace = static_cast(value); SAFE_PARCEL(input.readBool, &allowProtected); + SAFE_PARCEL(input.readBool, &grayscale); return NO_ERROR; } diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index b1305c6607..f643ab50a0 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -328,7 +328,8 @@ struct CaptureArgs { ui::PixelFormat pixelFormat{ui::PixelFormat::RGBA_8888}; Rect sourceCrop; - float frameScale{1}; + float frameScaleX{1}; + float frameScaleY{1}; bool captureSecureLayers{false}; int32_t uid{UNSET_UID}; // Force capture to be in a color space. If the value is ui::Dataspace::UNKNOWN, the captured @@ -346,6 +347,8 @@ struct CaptureArgs { // the contents being accessed/captured by screenshot or unsecure display. bool allowProtected = false; + bool grayscale = false; + virtual status_t write(Parcel& output) const; virtual status_t read(const Parcel& input); }; diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp index 19b3d6e1a5..9186538c0a 100644 --- a/services/surfaceflinger/RegionSamplingThread.cpp +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -451,7 +451,7 @@ void RegionSamplingThread::captureSample() { const sp captureListener = new SyncScreenCaptureListener(); mFlinger.captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer, - true /* regionSampling */, captureListener); + true /* regionSampling */, false /* grayscale */, captureListener); ScreenCaptureResults captureResults = captureListener->waitForResults(); std::vector activeDescriptors; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 27df232472..f89b199636 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -4900,23 +4900,24 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co result.append("\n"); } -void SurfaceFlinger::updateColorMatrixLocked() { - mat4 colorMatrix; - if (mGlobalSaturationFactor != 1.0f) { - // Rec.709 luma coefficients - float3 luminance{0.213f, 0.715f, 0.072f}; - luminance *= 1.0f - mGlobalSaturationFactor; - mat4 saturationMatrix = mat4( - vec4{luminance.r + mGlobalSaturationFactor, luminance.r, luminance.r, 0.0f}, - vec4{luminance.g, luminance.g + mGlobalSaturationFactor, luminance.g, 0.0f}, - vec4{luminance.b, luminance.b, luminance.b + mGlobalSaturationFactor, 0.0f}, - vec4{0.0f, 0.0f, 0.0f, 1.0f} - ); - colorMatrix = mClientColorMatrix * saturationMatrix * mDaltonizer(); - } else { - colorMatrix = mClientColorMatrix * mDaltonizer(); +mat4 SurfaceFlinger::calculateColorMatrix(float saturation) { + if (saturation == 1) { + return mat4(); } + float3 luminance{0.213f, 0.715f, 0.072f}; + luminance *= 1.0f - saturation; + mat4 saturationMatrix = mat4(vec4{luminance.r + saturation, luminance.r, luminance.r, 0.0f}, + vec4{luminance.g, luminance.g + saturation, luminance.g, 0.0f}, + vec4{luminance.b, luminance.b, luminance.b + saturation, 0.0f}, + vec4{0.0f, 0.0f, 0.0f, 1.0f}); + return saturationMatrix; +} + +void SurfaceFlinger::updateColorMatrixLocked() { + mat4 colorMatrix = + mClientColorMatrix * calculateColorMatrix(mGlobalSaturationFactor) * mDaltonizer(); + if (mCurrentState.colorMatrix != colorMatrix) { mCurrentState.colorMatrix = colorMatrix; mCurrentState.colorMatrixChanged = true; @@ -5628,7 +5629,8 @@ status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, }; return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize, - args.pixelFormat, args.allowProtected, captureListener); + args.pixelFormat, args.allowProtected, args.grayscale, + captureListener); } status_t SurfaceFlinger::captureDisplay(uint64_t displayOrLayerStack, @@ -5664,7 +5666,7 @@ status_t SurfaceFlinger::captureDisplay(uint64_t displayOrLayerStack, return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, size, ui::PixelFormat::RGBA_8888, false /* allowProtected */, - captureListener); + false /* grayscale */, captureListener); } status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, @@ -5710,12 +5712,12 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, crop.bottom = parentSourceBounds.getHeight(); } - if (crop.isEmpty() || args.frameScale <= 0.0f) { + if (crop.isEmpty() || args.frameScaleX <= 0.0f || args.frameScaleY <= 0.0f) { // Error out if the layer has no source bounds (i.e. they are boundless) and a source // crop was not specified, or an invalid frame scale was provided. return BAD_VALUE; } - reqSize = ui::Size(crop.width() * args.frameScale, crop.height() * args.frameScale); + reqSize = ui::Size(crop.width() * args.frameScaleX, crop.height() * args.frameScaleY); for (const auto& handle : args.excludeHandles) { sp excludeLayer = fromHandleLocked(handle).promote(); @@ -5785,13 +5787,14 @@ status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, }; return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize, - args.pixelFormat, args.allowProtected, captureListener); + args.pixelFormat, args.allowProtected, args.grayscale, + captureListener); } status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture, TraverseLayersFunction traverseLayers, ui::Size bufferSize, ui::PixelFormat reqPixelFormat, - const bool allowProtected, + bool allowProtected, bool grayscale, const sp& captureListener) { ATRACE_CALL(); @@ -5822,12 +5825,13 @@ status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture, static_cast(reqPixelFormat), 1 /* layerCount */, usage, "screenshot"); return captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer, - false /* regionSampling */, captureListener); + false /* regionSampling */, grayscale, captureListener); } status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture, TraverseLayersFunction traverseLayers, - sp& buffer, const bool regionSampling, + sp& buffer, bool regionSampling, + bool grayscale, const sp& captureListener) { ATRACE_CALL(); @@ -5843,7 +5847,7 @@ status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture, if (mRefreshPending) { ALOGW("Skipping screenshot for now"); captureScreenCommon(std::move(renderAreaFuture), traverseLayers, buffer, regionSampling, - captureListener); + grayscale, captureListener); return; } ScreenCaptureResults captureResults; @@ -5858,7 +5862,7 @@ status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture, status_t result = NO_ERROR; renderArea->render([&] { result = renderScreenImplLocked(*renderArea, traverseLayers, buffer, forSystem, - regionSampling, captureResults); + regionSampling, grayscale, captureResults); }); captureResults.result = result; @@ -5871,7 +5875,7 @@ status_t SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture, status_t SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers, const sp& buffer, bool forSystem, - bool regionSampling, + bool regionSampling, bool grayscale, ScreenCaptureResults& captureResults) { ATRACE_CALL(); @@ -5912,6 +5916,9 @@ status_t SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea, clientCompositionDisplay.outputDataspace = renderArea.getReqDataSpace(); clientCompositionDisplay.maxLuminance = DisplayDevice::sDefaultMaxLumiance; + const float colorSaturation = grayscale ? 0 : 1; + clientCompositionDisplay.colorTransform = calculateColorMatrix(colorSaturation); + const float alpha = RenderArea::getCaptureFillValue(renderArea.getCaptureFill()); compositionengine::LayerFE::LayerSettings fillLayer; diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 50d6099698..dc92f9817a 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -810,13 +810,14 @@ class SurfaceFlinger : public BnSurfaceComposer, void startBootAnim(); status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, ui::Size bufferSize, - ui::PixelFormat, const bool allowProtected, + ui::PixelFormat, bool allowProtected, bool grayscale, const sp&); status_t captureScreenCommon(RenderAreaFuture, TraverseLayersFunction, sp&, - bool regionSampling, const sp&); + bool regionSampling, bool grayscale, + const sp&); status_t renderScreenImplLocked(const RenderArea&, TraverseLayersFunction, const sp&, bool forSystem, bool regionSampling, - ScreenCaptureResults&); + bool grayscale, ScreenCaptureResults&); sp getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack) REQUIRES(mStateLock); sp getDisplayByLayerStack(uint64_t layerStack) REQUIRES(mStateLock); @@ -1019,6 +1020,8 @@ class SurfaceFlinger : public BnSurfaceComposer, void onFrameRateFlexibilityTokenReleased(); + static mat4 calculateColorMatrix(float saturation); + void updateColorMatrixLocked(); // Verify that transaction is being called by an approved process: diff --git a/services/surfaceflinger/tests/LayerState_test.cpp b/services/surfaceflinger/tests/LayerState_test.cpp index 93d5f2f8ec..fa1a5ed6b0 100644 --- a/services/surfaceflinger/tests/LayerState_test.cpp +++ b/services/surfaceflinger/tests/LayerState_test.cpp @@ -30,12 +30,14 @@ TEST(LayerStateTest, ParcellingDisplayCaptureArgs) { DisplayCaptureArgs args; args.pixelFormat = ui::PixelFormat::RGB_565; args.sourceCrop = Rect(0, 0, 500, 200); - args.frameScale = 2; + args.frameScaleX = 2; + args.frameScaleY = 4; args.captureSecureLayers = true; args.displayToken = new BBinder(); args.width = 10; args.height = 20; args.useIdentityTransform = true; + args.grayscale = true; Parcel p; args.write(p); @@ -46,23 +48,27 @@ TEST(LayerStateTest, ParcellingDisplayCaptureArgs) { ASSERT_EQ(args.pixelFormat, args2.pixelFormat); ASSERT_EQ(args.sourceCrop, args2.sourceCrop); - ASSERT_EQ(args.frameScale, args2.frameScale); + ASSERT_EQ(args.frameScaleX, args2.frameScaleX); + ASSERT_EQ(args.frameScaleY, args2.frameScaleY); ASSERT_EQ(args.captureSecureLayers, args2.captureSecureLayers); ASSERT_EQ(args.displayToken, args2.displayToken); ASSERT_EQ(args.width, args2.width); ASSERT_EQ(args.height, args2.height); ASSERT_EQ(args.useIdentityTransform, args2.useIdentityTransform); + ASSERT_EQ(args.grayscale, args2.grayscale); } TEST(LayerStateTest, ParcellingLayerCaptureArgs) { LayerCaptureArgs args; args.pixelFormat = ui::PixelFormat::RGB_565; args.sourceCrop = Rect(0, 0, 500, 200); - args.frameScale = 2; + args.frameScaleX = 2; + args.frameScaleY = 4; args.captureSecureLayers = true; args.layerHandle = new BBinder(); args.excludeHandles = {new BBinder(), new BBinder()}; args.childrenOnly = false; + args.grayscale = true; Parcel p; args.write(p); @@ -73,11 +79,13 @@ TEST(LayerStateTest, ParcellingLayerCaptureArgs) { ASSERT_EQ(args.pixelFormat, args2.pixelFormat); ASSERT_EQ(args.sourceCrop, args2.sourceCrop); - ASSERT_EQ(args.frameScale, args2.frameScale); + ASSERT_EQ(args.frameScaleX, args2.frameScaleX); + ASSERT_EQ(args.frameScaleY, args2.frameScaleY); ASSERT_EQ(args.captureSecureLayers, args2.captureSecureLayers); ASSERT_EQ(args.layerHandle, args2.layerHandle); ASSERT_EQ(args.excludeHandles, args2.excludeHandles); ASSERT_EQ(args.childrenOnly, args2.childrenOnly); + ASSERT_EQ(args.grayscale, args2.grayscale); } TEST(LayerStateTest, ParcellingScreenCaptureResults) { diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp index 214a0cd276..51ce1d3ff9 100644 --- a/services/surfaceflinger/tests/ScreenCapture_test.cpp +++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp @@ -487,7 +487,9 @@ TEST_F(ScreenCaptureTest, CaptureSize) { // red area to the right of the blue area mCapture->expectColor(Rect(30, 0, 59, 59), Color::RED); - captureArgs.frameScale = 0.5f; + captureArgs.frameScaleX = 0.5f; + captureArgs.frameScaleY = 0.5f; + ScreenCapture::captureLayers(&mCapture, captureArgs); // Capturing the downsized area (30x30) should leave both red and blue but in a smaller area. mCapture->expectColor(Rect(0, 0, 14, 14), Color::BLUE); @@ -768,6 +770,41 @@ TEST_F(ScreenCaptureTest, CaptureLayerWithUid) { mCapture->expectBorder(Rect(128, 128, 160, 160), {63, 63, 195, 255}); } +TEST_F(ScreenCaptureTest, CaptureWithGrayscale) { + sp layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32, + ISurfaceComposerClient::eFXSurfaceBufferState, + mBGSurfaceControl.get())); + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); + Transaction().show(layer).setLayer(layer, INT32_MAX).apply(); + + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = layer->getHandle(); + + ScreenCapture::captureLayers(&mCapture, captureArgs); + mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED); + + captureArgs.grayscale = true; + + const uint8_t tolerance = 1; + + // Values based on SurfaceFlinger::calculateColorMatrix + float3 luminance{0.213f, 0.715f, 0.072f}; + + ScreenCapture::captureLayers(&mCapture, captureArgs); + + uint8_t expectedColor = luminance.r * 255; + mCapture->expectColor(Rect(0, 0, 32, 32), + Color{expectedColor, expectedColor, expectedColor, 255}, tolerance); + + ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32)); + ScreenCapture::captureLayers(&mCapture, captureArgs); + + expectedColor = luminance.b * 255; + mCapture->expectColor(Rect(0, 0, 32, 32), + Color{expectedColor, expectedColor, expectedColor, 255}, tolerance); +} + // In the following tests we verify successful skipping of a parent layer, // so we use the same verification logic and only change how we mutate // the parent layer to verify that various properties are ignored. diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 2701f472aa..eda8d79fd5 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -353,7 +353,8 @@ class TestableSurfaceFlinger final : private ISchedulerCallback { bool regionSampling) { ScreenCaptureResults captureResults; return mFlinger->renderScreenImplLocked(renderArea, traverseLayers, buffer, forSystem, - regionSampling, captureResults); + regionSampling, false /* grayscale */, + captureResults); } auto traverseLayersInLayerStack(ui::LayerStack layerStack, int32_t uid,