Allow using custom widths and colors for layer borders.
Update the enableBorder API in SurfaceComposerClient so
that width and color can be specified for the border.
The information propagates through the pipe all the
way to the render engine so it can render the correct color
and width for the border
Test:go/wm-smoke.
Test: LayerBorder_test
Bug: 226529222
Change-Id: Id3ab853d5b4d6899a915f729b0d7be701cb5b167
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 0d3b412..7f0f638 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -102,6 +102,11 @@
SAFE_PARCEL(output.writeUint32, transform);
SAFE_PARCEL(output.writeBool, transformToDisplayInverse);
SAFE_PARCEL(output.writeBool, borderEnabled);
+ SAFE_PARCEL(output.writeFloat, borderWidth);
+ SAFE_PARCEL(output.writeFloat, borderColor.r);
+ SAFE_PARCEL(output.writeFloat, borderColor.g);
+ SAFE_PARCEL(output.writeFloat, borderColor.b);
+ SAFE_PARCEL(output.writeFloat, borderColor.a);
SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dataspace));
SAFE_PARCEL(output.write, hdrMetadata);
SAFE_PARCEL(output.write, surfaceDamageRegion);
@@ -202,6 +207,16 @@
SAFE_PARCEL(input.readUint32, &transform);
SAFE_PARCEL(input.readBool, &transformToDisplayInverse);
SAFE_PARCEL(input.readBool, &borderEnabled);
+ SAFE_PARCEL(input.readFloat, &tmpFloat);
+ borderWidth = tmpFloat;
+ SAFE_PARCEL(input.readFloat, &tmpFloat);
+ borderColor.r = tmpFloat;
+ SAFE_PARCEL(input.readFloat, &tmpFloat);
+ borderColor.g = tmpFloat;
+ SAFE_PARCEL(input.readFloat, &tmpFloat);
+ borderColor.b = tmpFloat;
+ SAFE_PARCEL(input.readFloat, &tmpFloat);
+ borderColor.a = tmpFloat;
uint32_t tmpUint32 = 0;
SAFE_PARCEL(input.readUint32, &tmpUint32);
@@ -555,6 +570,8 @@
if (other.what & eRenderBorderChanged) {
what |= eRenderBorderChanged;
borderEnabled = other.borderEnabled;
+ borderWidth = other.borderWidth;
+ borderColor = other.borderColor;
}
if (other.what & eFrameRateSelectionPriority) {
what |= eFrameRateSelectionPriority;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index caab506..6fc813b 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1940,7 +1940,7 @@
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::enableBorder(
- const sp<SurfaceControl>& sc, bool shouldEnable) {
+ const sp<SurfaceControl>& sc, bool shouldEnable, float width, const half4& color) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
@@ -1949,6 +1949,8 @@
s->what |= layer_state_t::eRenderBorderChanged;
s->borderEnabled = shouldEnable;
+ s->borderWidth = width;
+ s->borderColor = color;
registerSurfaceControlForCallback(sc);
return *this;
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 4621d2b..37a1595 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -295,6 +295,9 @@
// Flag to indicate if border needs to be enabled on the layer
bool borderEnabled;
+ float borderWidth;
+ half4 borderColor;
+
// Stretch effect to be applied to this layer
StretchEffect stretchEffect;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 2b24c3f..569dbf8 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -625,7 +625,8 @@
const Rect& destinationFrame);
Transaction& setDropInputMode(const sp<SurfaceControl>& sc, gui::DropInputMode mode);
- Transaction& enableBorder(const sp<SurfaceControl>& sc, bool shouldEnable);
+ Transaction& enableBorder(const sp<SurfaceControl>& sc, bool shouldEnable, float width,
+ const half4& color);
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
diff --git a/libs/renderengine/include/renderengine/BorderRenderInfo.h b/libs/renderengine/include/renderengine/BorderRenderInfo.h
index 85d55fc..0ee6661 100644
--- a/libs/renderengine/include/renderengine/BorderRenderInfo.h
+++ b/libs/renderengine/include/renderengine/BorderRenderInfo.h
@@ -22,10 +22,13 @@
namespace renderengine {
struct BorderRenderInfo {
+ float width = 0;
+ half4 color;
Region combinedRegion;
bool operator==(const BorderRenderInfo& rhs) const {
- return (combinedRegion.hasSameRects(rhs.combinedRegion));
+ return (width == rhs.width && color == rhs.color &&
+ combinedRegion.hasSameRects(rhs.combinedRegion));
}
};
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index ec9ad54..c9f9ec3 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -1247,15 +1247,11 @@
}
for (const auto& borderRenderInfo : display.borderInfoList) {
SkPaint p;
- // TODO (b/225977175): Use specified color
- p.setColor(SkColor4f{.fR = 255 / 255.0f,
- .fG = 128 / 255.0f,
- .fB = 0 / 255.0f,
- .fA = 255 / 255.0f});
+ p.setColor(SkColor4f{borderRenderInfo.color.r, borderRenderInfo.color.g,
+ borderRenderInfo.color.b, borderRenderInfo.color.a});
p.setAntiAlias(true);
p.setStyle(SkPaint::kStroke_Style);
- // TODO (b/225977175): Use specified width
- p.setStrokeWidth(20);
+ p.setStrokeWidth(borderRenderInfo.width);
SkRegion sk_region;
SkPath path;
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index df1b985..61af698 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -2433,6 +2433,8 @@
display.borderInfoList.clear();
renderengine::BorderRenderInfo info;
info.combinedRegion = Region(Rect(99, 99, 199, 199));
+ info.width = 20.0f;
+ info.color = half4{1.0f, 128.0f / 255.0f, 0.0f, 1.0f};
display.borderInfoList.emplace_back(info);
const auto greenBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 255, 0, 255));
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 8039bba..f861fc9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -33,6 +33,8 @@
using Outputs = std::vector<std::shared_ptr<compositionengine::Output>>;
struct BorderRenderInfo {
+ float width = 0;
+ half4 color;
std::vector<int32_t> layerIds;
};
/**
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 430d673..c65191c 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -753,8 +753,13 @@
for (const auto& id : borderInfo.layerIds) {
info.combinedRegion.orSelf(*(layerVisibleRegionMap[id]));
}
- outputCompositionState.borderInfoList.emplace_back(std::move(info));
- clientComposeTopLayer |= !info.combinedRegion.isEmpty();
+
+ if (!info.combinedRegion.isEmpty()) {
+ info.width = borderInfo.width;
+ info.color = borderInfo.color;
+ outputCompositionState.borderInfoList.emplace_back(std::move(info));
+ clientComposeTopLayer = true;
+ }
}
// In this situation we must client compose the top layer instead of using hwc
@@ -1218,6 +1223,8 @@
clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix;
for (auto& info : outputState.borderInfoList) {
renderengine::BorderRenderInfo borderInfo;
+ borderInfo.width = info.width;
+ borderInfo.color = info.color;
borderInfo.combinedRegion = info.combinedRegion;
clientCompositionDisplay.borderInfoList.emplace_back(std::move(borderInfo));
}
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index d47e423..2d3b237 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1157,11 +1157,13 @@
return StretchEffect{};
}
-bool Layer::enableBorder(bool shouldEnable) {
- if (mBorderEnabled == shouldEnable) {
+bool Layer::enableBorder(bool shouldEnable, float width, const half4& color) {
+ if (mBorderEnabled == shouldEnable && mBorderWidth == width && mBorderColor == color) {
return false;
}
mBorderEnabled = shouldEnable;
+ mBorderWidth = width;
+ mBorderColor = color;
return true;
}
@@ -1169,6 +1171,14 @@
return mBorderEnabled;
}
+float Layer::getBorderWidth() {
+ return mBorderWidth;
+}
+
+const half4& Layer::getBorderColor() {
+ return mBorderColor;
+}
+
bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool* transactionNeeded) {
// The frame rate for layer tree is this layer's frame rate if present, or the parent frame rate
const auto frameRate = [&] {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 85187e1..60c97f9 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -895,8 +895,10 @@
bool setStretchEffect(const StretchEffect& effect);
StretchEffect getStretchEffect() const;
- bool enableBorder(bool shouldEnable);
+ bool enableBorder(bool shouldEnable, float width, const half4& color);
bool isBorderEnabled();
+ float getBorderWidth();
+ const half4& getBorderColor();
virtual bool setBufferCrop(const Rect& /* bufferCrop */) { return false; }
virtual bool setDestinationFrame(const Rect& /* destinationFrame */) { return false; }
@@ -1149,6 +1151,8 @@
bool findInHierarchy(const sp<Layer>&);
bool mBorderEnabled = false;
+ float mBorderWidth;
+ half4 mBorderColor;
};
std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1d5d353..82d8580 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2179,6 +2179,8 @@
mDrawingState.traverse([&refreshArgs](Layer* layer) {
if (layer->isBorderEnabled()) {
compositionengine::BorderRenderInfo info;
+ info.width = layer->getBorderWidth();
+ info.color = layer->getBorderColor();
layer->traverse(LayerVector::StateSet::Drawing, [&info](Layer* ilayer) {
info.layerIds.push_back(ilayer->getSequence());
});
@@ -4462,7 +4464,7 @@
if (layer->setBlurRegions(s.blurRegions)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eRenderBorderChanged) {
- if (layer->enableBorder(s.borderEnabled)) {
+ if (layer->enableBorder(s.borderEnabled, s.borderWidth, s.borderColor)) {
flags |= eTraversalNeeded;
}
}
diff --git a/services/surfaceflinger/tests/LayerBorder_test.cpp b/services/surfaceflinger/tests/LayerBorder_test.cpp
index 4b38214..f80c705 100644
--- a/services/surfaceflinger/tests/LayerBorder_test.cpp
+++ b/services/surfaceflinger/tests/LayerBorder_test.cpp
@@ -36,7 +36,7 @@
const auto display = SurfaceComposerClient::getInternalDisplayToken();
ASSERT_FALSE(display == nullptr);
-
+ mColorOrange = toHalf4({255, 140, 0, 255});
mParentLayer = createColorLayer("Parent layer", Color::RED);
mContainerLayer = mClient->createSurface(String8("Container Layer"), 0 /* width */,
@@ -82,6 +82,7 @@
std::function<half3(Color)> toHalf3;
std::function<half4(Color)> toHalf4;
sp<SurfaceControl> mParentLayer, mContainerLayer, mEffectLayer1, mEffectLayer2;
+ half4 mColorOrange;
};
TEST_F(LayerBorderTest, OverlappingVisibleRegions) {
@@ -89,7 +90,7 @@
t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
- t.enableBorder(mContainerLayer, true);
+ t.enableBorder(mContainerLayer, true, 20, mColorOrange);
t.show(mEffectLayer1);
t.show(mEffectLayer2);
t.show(mContainerLayer);
@@ -101,7 +102,7 @@
t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
- t.enableBorder(mEffectLayer1, true);
+ t.enableBorder(mEffectLayer1, true, 20, mColorOrange);
t.show(mEffectLayer1);
t.show(mEffectLayer2);
t.show(mContainerLayer);
@@ -113,7 +114,7 @@
t.setCrop(mEffectLayer1, Rect(0, 0, 200, 200));
t.setCrop(mEffectLayer2, Rect(400, 400, 600, 600));
- t.enableBorder(mContainerLayer, true);
+ t.enableBorder(mContainerLayer, true, 20, mColorOrange);
t.show(mEffectLayer1);
t.show(mEffectLayer2);
t.show(mContainerLayer);
@@ -125,7 +126,7 @@
t.setCrop(mEffectLayer1, Rect(200, 200, 400, 400));
t.setCrop(mEffectLayer2, Rect(0, 0, 600, 600));
- t.enableBorder(mEffectLayer1, true);
+ t.enableBorder(mEffectLayer1, true, 20, mColorOrange);
t.show(mEffectLayer1);
t.show(mEffectLayer2);
t.show(mContainerLayer);
@@ -140,7 +141,7 @@
t.setLayer(mEffectLayer1, 30);
t.setLayer(mEffectLayer2, 20);
- t.enableBorder(mEffectLayer1, true);
+ t.enableBorder(mEffectLayer1, true, 20, mColorOrange);
t.show(mEffectLayer1);
t.show(mEffectLayer2);
t.show(mContainerLayer);
@@ -169,7 +170,7 @@
t.setCrop(effectLayer3, Rect(400, 400, 800, 800));
t.setColor(effectLayer3, toHalf3(Color::BLUE));
- t.enableBorder(mContainerLayer, true);
+ t.enableBorder(mContainerLayer, true, 20, mColorOrange);
t.show(mEffectLayer1);
t.show(mEffectLayer2);
t.show(effectLayer3);
@@ -183,7 +184,7 @@
t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
t.setAlpha(mEffectLayer1, 0.0f);
- t.enableBorder(mEffectLayer1, true);
+ t.enableBorder(mContainerLayer, true, 20, mColorOrange);
t.show(mEffectLayer1);
t.show(mEffectLayer2);
t.show(mContainerLayer);
@@ -196,7 +197,7 @@
t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
t.setAlpha(mEffectLayer2, 0.5f);
- t.enableBorder(mEffectLayer2, true);
+ t.enableBorder(mEffectLayer2, true, 20, mColorOrange);
t.show(mEffectLayer1);
t.show(mEffectLayer2);
t.show(mContainerLayer);
@@ -208,7 +209,7 @@
t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
- t.enableBorder(mContainerLayer, true);
+ t.enableBorder(mContainerLayer, true, 20, mColorOrange);
t.hide(mEffectLayer2);
t.show(mContainerLayer);
});
@@ -237,7 +238,43 @@
t.setBuffer(bufferStateLayer, buffer);
t.setPosition(bufferStateLayer, 100, 100);
t.show(bufferStateLayer);
- t.enableBorder(mContainerLayer, true);
+ t.enableBorder(mContainerLayer, true, 20, mColorOrange);
+ });
+}
+
+TEST_F(LayerBorderTest, CustomWidth) {
+ asTransaction([&](Transaction& t) {
+ t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
+ t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
+
+ t.enableBorder(mContainerLayer, true, 50, mColorOrange);
+ t.show(mEffectLayer1);
+ t.show(mEffectLayer2);
+ t.show(mContainerLayer);
+ });
+}
+
+TEST_F(LayerBorderTest, CustomColor) {
+ asTransaction([&](Transaction& t) {
+ t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
+ t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
+
+ t.enableBorder(mContainerLayer, true, 20, toHalf4({255, 0, 255, 255}));
+ t.show(mEffectLayer1);
+ t.show(mEffectLayer2);
+ t.show(mContainerLayer);
+ });
+}
+
+TEST_F(LayerBorderTest, CustomWidthAndColorAndOpacity) {
+ asTransaction([&](Transaction& t) {
+ t.setCrop(mEffectLayer1, Rect(0, 0, 200, 200));
+ t.setCrop(mEffectLayer2, Rect(400, 400, 600, 600));
+
+ t.enableBorder(mContainerLayer, true, 40, toHalf4({255, 255, 0, 128}));
+ t.show(mEffectLayer1);
+ t.show(mEffectLayer2);
+ t.show(mContainerLayer);
});
}