Add border API to surface control
See go/sf-box-shadows-api for more details
Bug: b/367464660
Flag: com.android.window.flags.enable_border_settings
Test: atest SurfaceFlinger_test
Change-Id: I1190edb97693004d9f46058fd0165451470a65b3
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 158c548..2117c98 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -93,6 +93,7 @@
"android/gui/StalledTransactionInfo.aidl",
"android/**/TouchOcclusionMode.aidl",
"android/gui/TrustedOverlay.aidl",
+ "android/gui/BorderSettings.aidl",
],
}
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index ad95d1a..86bc97e 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -180,6 +180,7 @@
SAFE_PARCEL(output.writeParcelableVector, listener.callbackIds);
}
SAFE_PARCEL(output.writeFloat, shadowRadius);
+ SAFE_PARCEL(output.writeParcelable, borderSettings);
SAFE_PARCEL(output.writeInt32, frameRateSelectionPriority);
SAFE_PARCEL(output.writeFloat, frameRate);
SAFE_PARCEL(output.writeByte, frameRateCompatibility);
@@ -328,6 +329,8 @@
listeners.emplace_back(listener, callbackIds);
}
SAFE_PARCEL(input.readFloat, &shadowRadius);
+ SAFE_PARCEL(input.readParcelable, &borderSettings);
+
SAFE_PARCEL(input.readInt32, &frameRateSelectionPriority);
SAFE_PARCEL(input.readFloat, &frameRate);
SAFE_PARCEL(input.readByte, &frameRateCompatibility);
@@ -727,6 +730,10 @@
what |= eShadowRadiusChanged;
shadowRadius = other.shadowRadius;
}
+ if (other.what & eBorderSettingsChanged) {
+ what |= eBorderSettingsChanged;
+ borderSettings = other.borderSettings;
+ }
if (other.what & eLutsChanged) {
what |= eLutsChanged;
luts = other.luts;
@@ -881,6 +888,7 @@
CHECK_DIFF2(diff, eBackgroundColorChanged, other, bgColor, bgColorDataspace);
if (other.what & eMetadataChanged) diff |= eMetadataChanged;
CHECK_DIFF(diff, eShadowRadiusChanged, other, shadowRadius);
+ CHECK_DIFF(diff, eBorderSettingsChanged, other, borderSettings);
CHECK_DIFF(diff, eDefaultFrameRateCompatibilityChanged, other, defaultFrameRateCompatibility);
CHECK_DIFF(diff, eFrameRateSelectionPriority, other, frameRateSelectionPriority);
CHECK_DIFF3(diff, eFrameRateChanged, other, frameRate, frameRateCompatibility,
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 9854274..60a2bf7 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -2222,6 +2222,19 @@
return *this;
}
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBorderSettings(
+ const sp<SurfaceControl>& sc, gui::BorderSettings settings) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ mStatus = BAD_INDEX;
+ return *this;
+ }
+
+ s->what |= layer_state_t::eBorderSettingsChanged;
+ s->borderSettings = settings;
+ return *this;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrameRate(
const sp<SurfaceControl>& sc, float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy) {
diff --git a/libs/gui/android/gui/BorderSettings.aidl b/libs/gui/android/gui/BorderSettings.aidl
new file mode 100644
index 0000000..547f57f
--- /dev/null
+++ b/libs/gui/android/gui/BorderSettings.aidl
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2025, 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.
+ */
+
+package android.gui;
+
+/** @hide */
+parcelable BorderSettings {
+ float strokeWidth;
+ // Space is sRGB, not premultiplied, bit pattern is 0xAARRGGBB.
+ int color;
+}
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 369d3d1..e2d27ac 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -21,6 +21,7 @@
#include <sys/types.h>
#include <span>
+#include <android/gui/BorderSettings.h>
#include <android/gui/DisplayCaptureArgs.h>
#include <android/gui/IWindowInfosReportedListener.h>
#include <android/gui/LayerCaptureArgs.h>
@@ -250,6 +251,7 @@
ePictureProfileHandleChanged = 0x80000'00000000,
eAppContentPriorityChanged = 0x100000'00000000,
eClientDrawnCornerRadiusChanged = 0x200000'00000000,
+ eBorderSettingsChanged = 0x400000'00000000,
};
layer_state_t();
@@ -293,8 +295,8 @@
layer_state_t::eColorSpaceAgnosticChanged | layer_state_t::eColorTransformChanged |
layer_state_t::eCornerRadiusChanged | layer_state_t::eDimmingEnabledChanged |
layer_state_t::eHdrMetadataChanged | layer_state_t::eShadowRadiusChanged |
- layer_state_t::eStretchChanged |
- layer_state_t::ePictureProfileHandleChanged | layer_state_t::eAppContentPriorityChanged;
+ layer_state_t::eStretchChanged | layer_state_t::ePictureProfileHandleChanged |
+ layer_state_t::eAppContentPriorityChanged | layer_state_t::eBorderSettingsChanged;
// Changes which invalidates the layer's visible region in CE.
static constexpr uint64_t CONTENT_DIRTY = layer_state_t::CONTENT_CHANGES |
@@ -322,7 +324,8 @@
// Changes that force GPU composition.
static constexpr uint64_t COMPOSITION_EFFECTS = layer_state_t::eBackgroundBlurRadiusChanged |
layer_state_t::eBlurRegionsChanged | layer_state_t::eCornerRadiusChanged |
- layer_state_t::eShadowRadiusChanged | layer_state_t::eStretchChanged;
+ layer_state_t::eShadowRadiusChanged | layer_state_t::eStretchChanged |
+ layer_state_t::eBorderSettingsChanged;
bool hasValidBuffer() const;
void sanitize(int32_t permissions);
@@ -411,6 +414,9 @@
// Draws a shadow around the surface.
float shadowRadius;
+ // Draws an outline around the layer.
+ gui::BorderSettings borderSettings;
+
// Priority of the layer assigned by Window Manager.
int32_t frameRateSelectionPriority;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 4fda8de..1ad9f95 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -717,6 +717,8 @@
const Rect& source, const Rect& dst, int transform);
Transaction& setShadowRadius(const sp<SurfaceControl>& sc, float cornerRadius);
+ Transaction& setBorderSettings(const sp<SurfaceControl>& sc, gui::BorderSettings settings);
+
Transaction& setFrameRate(const sp<SurfaceControl>& sc, float frameRate,
int8_t compatibility, int8_t changeFrameRateStrategy);
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index f9b84fa..39182aa 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -52,6 +52,7 @@
"libtonemap",
"libsurfaceflinger_common",
"libsurfaceflingerflags",
+ "libgui_window_info_static",
],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
@@ -122,7 +123,13 @@
"skia_renderengine_deps",
"libsurfaceflinger_common_deps",
],
- static_libs: ["libskia_renderengine"],
+ static_libs: [
+ "libgui_window_info_static",
+ "libskia_renderengine",
+ ],
+ shared_libs: [
+ "libbinder",
+ ],
}
// Note: if compilation fails when adding librenderengine as a dependency, try adding
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index ecb16b2..3523497 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -16,6 +16,7 @@
#pragma once
+#include <android/gui/BorderSettings.h>
#include <gui/DisplayLuts.h>
#include <math/mat4.h>
#include <math/vec3.h>
@@ -71,6 +72,10 @@
// Boundaries of the layer.
FloatRect boundaries = FloatRect();
+ // Boundaries of the layer before transparent region hint is subtracted.
+ // Effects like shadows and outline ignore the transparent region hint.
+ FloatRect originalBounds = FloatRect();
+
// Transform matrix to apply to mesh coordinates.
mat4 positionTransform = mat4();
@@ -127,6 +132,8 @@
ShadowSettings shadow;
+ gui::BorderSettings borderSettings;
+
int backgroundBlurRadius = 0;
std::vector<BlurRegion> blurRegions;
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 9e1c226..5b6edb4 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -986,6 +986,30 @@
drawShadow(canvas, rrect, layer.shadow);
}
+ // Similar to shadows, do the rendering before the clip is applied because even when the
+ // layer is occluded it should have an outline.
+ if (layer.borderSettings.strokeWidth > 0) {
+ // TODO(b/367464660): Move this code to the parent scope and
+ // update shadow rendering above to use these bounds since they should be
+ // identical.
+ SkRRect originalBounds, originalClip;
+ std::tie(originalBounds, originalClip) =
+ getBoundsAndClip(layer.geometry.boundaries, layer.geometry.roundedCornersCrop,
+ layer.geometry.roundedCornersRadius);
+ const SkRRect& preferredOriginalBounds =
+ originalBounds.isRect() && !originalClip.isEmpty() ? originalClip
+ : originalBounds;
+
+ SkRRect outlineRect = preferredOriginalBounds;
+ outlineRect.outset(layer.borderSettings.strokeWidth, layer.borderSettings.strokeWidth);
+
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setColor(layer.borderSettings.color);
+ paint.setStyle(SkPaint::kFill_Style);
+ canvas->drawDRRect(outlineRect, preferredOriginalBounds, paint);
+ }
+
const float layerDimmingRatio = layer.whitePointNits <= 0.f
? displayDimmingRatio
: (layer.whitePointNits / maxLayerWhitePoint) * displayDimmingRatio;