Skip to content

Commit df1982d

Browse files
committed
Add box shadow API to surface control
See go/sf-box-shadows-api for more details. Bug: b/367464660 Flag: com.android.window.flags.enable_box_shadow_settings Test: atest SurfaceFlinger_test Change-Id: Ic3b253a0d9d1e61121104acadc0b29af9aca1872
1 parent 65fb1c6 commit df1982d

27 files changed

Lines changed: 384 additions & 75 deletions

libs/gui/Android.bp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ filegroup {
9494
"android/**/TouchOcclusionMode.aidl",
9595
"android/gui/TrustedOverlay.aidl",
9696
"android/gui/BorderSettings.aidl",
97+
"android/gui/BoxShadowSettings.aidl",
9798
],
9899
}
99100

libs/gui/LayerState.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ status_t layer_state_t::write(Parcel& output) const
181181
}
182182
SAFE_PARCEL(output.writeFloat, shadowRadius);
183183
SAFE_PARCEL(output.writeParcelable, borderSettings);
184+
SAFE_PARCEL(output.writeParcelable, boxShadowSettings);
184185
SAFE_PARCEL(output.writeInt32, frameRateSelectionPriority);
185186
SAFE_PARCEL(output.writeFloat, frameRate);
186187
SAFE_PARCEL(output.writeByte, frameRateCompatibility);
@@ -330,6 +331,7 @@ status_t layer_state_t::read(const Parcel& input)
330331
}
331332
SAFE_PARCEL(input.readFloat, &shadowRadius);
332333
SAFE_PARCEL(input.readParcelable, &borderSettings);
334+
SAFE_PARCEL(input.readParcelable, &boxShadowSettings);
333335

334336
SAFE_PARCEL(input.readInt32, &frameRateSelectionPriority);
335337
SAFE_PARCEL(input.readFloat, &frameRate);
@@ -734,6 +736,10 @@ void layer_state_t::merge(const layer_state_t& other) {
734736
what |= eBorderSettingsChanged;
735737
borderSettings = other.borderSettings;
736738
}
739+
if (other.what & eBoxShadowSettingsChanged) {
740+
what |= eBoxShadowSettingsChanged;
741+
boxShadowSettings = other.boxShadowSettings;
742+
}
737743
if (other.what & eLutsChanged) {
738744
what |= eLutsChanged;
739745
luts = other.luts;
@@ -889,6 +895,7 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const {
889895
if (other.what & eMetadataChanged) diff |= eMetadataChanged;
890896
CHECK_DIFF(diff, eShadowRadiusChanged, other, shadowRadius);
891897
CHECK_DIFF(diff, eBorderSettingsChanged, other, borderSettings);
898+
CHECK_DIFF(diff, eBoxShadowSettingsChanged, other, boxShadowSettings);
892899
CHECK_DIFF(diff, eDefaultFrameRateCompatibilityChanged, other, defaultFrameRateCompatibility);
893900
CHECK_DIFF(diff, eFrameRateSelectionPriority, other, frameRateSelectionPriority);
894901
CHECK_DIFF3(diff, eFrameRateChanged, other, frameRate, frameRateCompatibility,

libs/gui/SurfaceComposerClient.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2235,6 +2235,19 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBorde
22352235
return *this;
22362236
}
22372237

2238+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBoxShadowSettings(
2239+
const sp<SurfaceControl>& sc, gui::BoxShadowSettings settings) {
2240+
layer_state_t* s = getLayerState(sc);
2241+
if (!s) {
2242+
mStatus = BAD_INDEX;
2243+
return *this;
2244+
}
2245+
2246+
s->what |= layer_state_t::eBoxShadowSettingsChanged;
2247+
s->boxShadowSettings = settings;
2248+
return *this;
2249+
}
2250+
22382251
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameRate(
22392252
const sp<SurfaceControl>& sc, float frameRate, int8_t compatibility,
22402253
int8_t changeFrameRateStrategy) {
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* Copyright (c) 2025, 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+
17+
package android.gui;
18+
19+
/** @hide */
20+
parcelable BoxShadowSettings {
21+
parcelable BoxShadowParams
22+
{
23+
float blurRadius;
24+
float spreadRadius;
25+
// Space is sRGB, not premultiplied, bit pattern is 0xAARRGGBB.
26+
int color;
27+
float offsetX;
28+
float offsetY;
29+
}
30+
31+
BoxShadowParams []boxShadows = {};
32+
}

libs/gui/include/gui/LayerState.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <span>
2323

2424
#include <android/gui/BorderSettings.h>
25+
#include <android/gui/BoxShadowSettings.h>
2526
#include <android/gui/DisplayCaptureArgs.h>
2627
#include <android/gui/IWindowInfosReportedListener.h>
2728
#include <android/gui/LayerCaptureArgs.h>
@@ -252,6 +253,7 @@ struct layer_state_t {
252253
eAppContentPriorityChanged = 0x100000'00000000,
253254
eClientDrawnCornerRadiusChanged = 0x200000'00000000,
254255
eBorderSettingsChanged = 0x400000'00000000,
256+
eBoxShadowSettingsChanged = 0x800000'00000000,
255257
};
256258

257259
layer_state_t();
@@ -296,7 +298,8 @@ struct layer_state_t {
296298
layer_state_t::eCornerRadiusChanged | layer_state_t::eDimmingEnabledChanged |
297299
layer_state_t::eHdrMetadataChanged | layer_state_t::eShadowRadiusChanged |
298300
layer_state_t::eStretchChanged | layer_state_t::ePictureProfileHandleChanged |
299-
layer_state_t::eAppContentPriorityChanged | layer_state_t::eBorderSettingsChanged;
301+
layer_state_t::eAppContentPriorityChanged | layer_state_t::eBorderSettingsChanged |
302+
layer_state_t::eBoxShadowSettingsChanged;
300303

301304
// Changes which invalidates the layer's visible region in CE.
302305
static constexpr uint64_t CONTENT_DIRTY = layer_state_t::CONTENT_CHANGES |
@@ -325,7 +328,7 @@ struct layer_state_t {
325328
static constexpr uint64_t COMPOSITION_EFFECTS = layer_state_t::eBackgroundBlurRadiusChanged |
326329
layer_state_t::eBlurRegionsChanged | layer_state_t::eCornerRadiusChanged |
327330
layer_state_t::eShadowRadiusChanged | layer_state_t::eStretchChanged |
328-
layer_state_t::eBorderSettingsChanged;
331+
layer_state_t::eBorderSettingsChanged | layer_state_t::eBoxShadowSettingsChanged;
329332

330333
bool hasValidBuffer() const;
331334
void sanitize(int32_t permissions);
@@ -417,6 +420,9 @@ struct layer_state_t {
417420
// Draws an outline around the layer.
418421
gui::BorderSettings borderSettings;
419422

423+
// Draws a sequence of box shadows under the layer.
424+
gui::BoxShadowSettings boxShadowSettings;
425+
420426
// Priority of the layer assigned by Window Manager.
421427
int32_t frameRateSelectionPriority;
422428

libs/gui/include/gui/SurfaceComposerClient.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,9 @@ class SurfaceComposerClient : public RefBase
719719

720720
Transaction& setBorderSettings(const sp<SurfaceControl>& sc, gui::BorderSettings settings);
721721

722+
Transaction& setBoxShadowSettings(const sp<SurfaceControl>& sc,
723+
gui::BoxShadowSettings settings);
724+
722725
Transaction& setFrameRate(const sp<SurfaceControl>& sc, float frameRate,
723726
int8_t compatibility, int8_t changeFrameRateStrategy);
724727

libs/renderengine/include/renderengine/LayerSettings.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#pragma once
1818

1919
#include <android/gui/BorderSettings.h>
20+
#include <android/gui/BoxShadowSettings.h>
2021
#include <gui/DisplayLuts.h>
2122
#include <math/mat4.h>
2223
#include <math/vec3.h>
@@ -134,6 +135,8 @@ struct LayerSettings {
134135

135136
gui::BorderSettings borderSettings;
136137

138+
gui::BoxShadowSettings boxShadowSettings;
139+
137140
int backgroundBlurRadius = 0;
138141

139142
std::vector<BlurRegion> blurRegions;

libs/renderengine/skia/SkiaRenderEngine.cpp

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "SkiaRenderEngine.h"
2222

2323
#include <SkBlendMode.h>
24+
#include <SkBlurTypes.h>
2425
#include <SkCanvas.h>
2526
#include <SkColor.h>
2627
#include <SkColorFilter.h>
@@ -32,6 +33,7 @@
3233
#include <SkImageFilters.h>
3334
#include <SkImageInfo.h>
3435
#include <SkM44.h>
36+
#include <SkMaskFilter.h>
3537
#include <SkMatrix.h>
3638
#include <SkPaint.h>
3739
#include <SkPath.h>
@@ -986,20 +988,19 @@ void SkiaRenderEngine::drawLayersInternal(
986988
drawShadow(canvas, rrect, layer.shadow);
987989
}
988990

991+
// TODO(b/367464660): Move this code above and
992+
// update elevation shadow rendering to use these bounds since they should be
993+
// identical.
994+
SkRRect originalBounds, originalClip;
995+
std::tie(originalBounds, originalClip) =
996+
getBoundsAndClip(layer.geometry.originalBounds, layer.geometry.roundedCornersCrop,
997+
layer.geometry.roundedCornersRadius);
998+
const SkRRect& preferredOriginalBounds =
999+
originalBounds.isRect() && !originalClip.isEmpty() ? originalClip : originalBounds;
1000+
9891001
// Similar to shadows, do the rendering before the clip is applied because even when the
9901002
// layer is occluded it should have an outline.
9911003
if (layer.borderSettings.strokeWidth > 0) {
992-
// TODO(b/367464660): Move this code to the parent scope and
993-
// update shadow rendering above to use these bounds since they should be
994-
// identical.
995-
SkRRect originalBounds, originalClip;
996-
std::tie(originalBounds, originalClip) =
997-
getBoundsAndClip(layer.geometry.boundaries, layer.geometry.roundedCornersCrop,
998-
layer.geometry.roundedCornersRadius);
999-
const SkRRect& preferredOriginalBounds =
1000-
originalBounds.isRect() && !originalClip.isEmpty() ? originalClip
1001-
: originalBounds;
1002-
10031004
SkRRect outlineRect = preferredOriginalBounds;
10041005
outlineRect.outset(layer.borderSettings.strokeWidth, layer.borderSettings.strokeWidth);
10051006

@@ -1010,6 +1011,21 @@ void SkiaRenderEngine::drawLayersInternal(
10101011
canvas->drawDRRect(outlineRect, preferredOriginalBounds, paint);
10111012
}
10121013

1014+
if (!layer.boxShadowSettings.boxShadows.empty()) {
1015+
for (const gui::BoxShadowSettings::BoxShadowParams& box :
1016+
layer.boxShadowSettings.boxShadows) {
1017+
SkRRect boxRect = preferredOriginalBounds;
1018+
boxRect.outset(box.spreadRadius, box.spreadRadius);
1019+
boxRect.offset(box.offsetX, box.offsetY);
1020+
float sigma = convertBlurUserRadiusToSigma(box.blurRadius);
1021+
SkPaint blur;
1022+
blur.setAntiAlias(true);
1023+
blur.setColor(box.color);
1024+
blur.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, sigma, false));
1025+
canvas->drawRRect(boxRect, blur);
1026+
}
1027+
}
1028+
10131029
const float layerDimmingRatio = layer.whitePointNits <= 0.f
10141030
? displayDimmingRatio
10151031
: (layer.whitePointNits / maxLayerWhitePoint) * displayDimmingRatio;

libs/ui/include/ui/BlurRegion.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,15 @@ static inline void PrintTo(const BlurRegion& blurRegion, ::std::ostream* os) {
6262
*os << "\n}";
6363
}
6464

65+
// copied from skia/src/core/SkBlurMask.cpp
66+
inline float convertBlurUserRadiusToSigma(float radius) {
67+
return radius > 0 ? 0.57735f * radius + 0.5f : 0.0f;
68+
}
69+
// copied from skia/src/core/SkBlurEngine.h
70+
inline float convertBlurSigmaToKernelRadius(float sigma) {
71+
return sigma <= 0.03f ? 0 : ceilf(3.f * sigma);
72+
}
73+
6574
} // namespace android
6675

6776
namespace std {

services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <cstdint>
2020

2121
#include <android/gui/BorderSettings.h>
22+
#include <android/gui/BoxShadowSettings.h>
2223
#include <android/gui/CachingHint.h>
2324
#include <gui/DisplayLuts.h>
2425
#include <gui/HdrMetadata.h>
@@ -145,6 +146,9 @@ struct LayerFECompositionState {
145146
// The settings to configure the outline of a layer.
146147
gui::BorderSettings borderSettings;
147148

149+
// The settings to configure box shadows of a layer.
150+
gui::BoxShadowSettings boxShadowSettings;
151+
148152
// List of regions that require blur
149153
std::vector<BlurRegion> blurRegions;
150154

@@ -240,6 +244,43 @@ struct LayerFECompositionState {
240244

241245
// Debugging
242246
virtual void dump(std::string& out) const;
247+
248+
FloatRect outsetRectForShadow(const FloatRect& input) const {
249+
FloatRect output = input;
250+
251+
// RenderEngine currently blurs shadows to smooth out edges, so outset by
252+
// 2x the length instead of 1x to compensate
253+
float outset = shadowSettings.length * 2;
254+
255+
// Stroke antialiasing should never add more than 2 pixels.
256+
if (borderSettings.strokeWidth > 0) {
257+
outset = std::max(outset, borderSettings.strokeWidth + 2);
258+
}
259+
260+
output.left -= outset;
261+
output.top -= outset;
262+
output.right += outset;
263+
output.bottom += outset;
264+
265+
for (const gui::BoxShadowSettings::BoxShadowParams& boxShadow :
266+
boxShadowSettings.boxShadows) {
267+
float radius = convertBlurSigmaToKernelRadius(
268+
convertBlurUserRadiusToSigma(boxShadow.blurRadius)) +
269+
boxShadow.spreadRadius;
270+
271+
float shadowLeft = input.left + boxShadow.offsetX - radius;
272+
float shadowTop = input.top + boxShadow.offsetY - radius;
273+
float shadowRight = input.right + boxShadow.offsetX + radius;
274+
float shadowBottom = input.bottom + boxShadow.offsetY + radius;
275+
276+
output.left = std::min(shadowLeft, output.left);
277+
output.top = std::min(shadowTop, output.top);
278+
output.right = std::max(shadowRight, output.right);
279+
output.bottom = std::max(shadowBottom, output.bottom);
280+
}
281+
282+
return output;
283+
}
243284
};
244285

245286
} // namespace android::compositionengine

0 commit comments

Comments
 (0)