xref: /aosp_15_r20/frameworks/native/services/surfaceflinger/HdrSdrRatioOverlay.cpp (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
1*38e8c45fSAndroid Build Coastguard Worker /**
2*38e8c45fSAndroid Build Coastguard Worker  * Copyright (C) 2023 The Android Open Source Project
3*38e8c45fSAndroid Build Coastguard Worker  *
4*38e8c45fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*38e8c45fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*38e8c45fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*38e8c45fSAndroid Build Coastguard Worker  *
8*38e8c45fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*38e8c45fSAndroid Build Coastguard Worker  *
10*38e8c45fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*38e8c45fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*38e8c45fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*38e8c45fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*38e8c45fSAndroid Build Coastguard Worker  * limitations under the License.
15*38e8c45fSAndroid Build Coastguard Worker  */
16*38e8c45fSAndroid Build Coastguard Worker // #define LOG_NDEBUG 0
17*38e8c45fSAndroid Build Coastguard Worker #include <algorithm>
18*38e8c45fSAndroid Build Coastguard Worker 
19*38e8c45fSAndroid Build Coastguard Worker #include "HdrSdrRatioOverlay.h"
20*38e8c45fSAndroid Build Coastguard Worker 
21*38e8c45fSAndroid Build Coastguard Worker #include <SkSurface.h>
22*38e8c45fSAndroid Build Coastguard Worker 
23*38e8c45fSAndroid Build Coastguard Worker #undef LOG_TAG
24*38e8c45fSAndroid Build Coastguard Worker #define LOG_TAG "HdrSdrRatioOverlay"
25*38e8c45fSAndroid Build Coastguard Worker 
26*38e8c45fSAndroid Build Coastguard Worker namespace android {
27*38e8c45fSAndroid Build Coastguard Worker 
drawNumber(float number,int left,SkColor color,SkCanvas & canvas)28*38e8c45fSAndroid Build Coastguard Worker void HdrSdrRatioOverlay::drawNumber(float number, int left, SkColor color, SkCanvas& canvas) {
29*38e8c45fSAndroid Build Coastguard Worker     if (!isfinite(number) || number >= 10.f) return;
30*38e8c45fSAndroid Build Coastguard Worker     // We assume that the number range is [1.f, 10.f)
31*38e8c45fSAndroid Build Coastguard Worker     // and the decimal places are 2.
32*38e8c45fSAndroid Build Coastguard Worker     int value = static_cast<int>(number * 100);
33*38e8c45fSAndroid Build Coastguard Worker     SegmentDrawer::drawDigit(value / 100, left, color, canvas);
34*38e8c45fSAndroid Build Coastguard Worker 
35*38e8c45fSAndroid Build Coastguard Worker     left += kDigitWidth + kDigitSpace;
36*38e8c45fSAndroid Build Coastguard Worker     SegmentDrawer::drawSegment(SegmentDrawer::Segment::DecimalPoint, left, color, canvas);
37*38e8c45fSAndroid Build Coastguard Worker     left += kDigitWidth + kDigitSpace;
38*38e8c45fSAndroid Build Coastguard Worker 
39*38e8c45fSAndroid Build Coastguard Worker     SegmentDrawer::drawDigit((value / 10) % 10, left, color, canvas);
40*38e8c45fSAndroid Build Coastguard Worker     left += kDigitWidth + kDigitSpace;
41*38e8c45fSAndroid Build Coastguard Worker     SegmentDrawer::drawDigit(value % 10, left, color, canvas);
42*38e8c45fSAndroid Build Coastguard Worker }
43*38e8c45fSAndroid Build Coastguard Worker 
draw(float currentHdrSdrRatio,SkColor color,ui::Transform::RotationFlags rotation,sp<GraphicBuffer> & ringBuffer)44*38e8c45fSAndroid Build Coastguard Worker sp<GraphicBuffer> HdrSdrRatioOverlay::draw(float currentHdrSdrRatio, SkColor color,
45*38e8c45fSAndroid Build Coastguard Worker                                            ui::Transform::RotationFlags rotation,
46*38e8c45fSAndroid Build Coastguard Worker                                            sp<GraphicBuffer>& ringBuffer) {
47*38e8c45fSAndroid Build Coastguard Worker     const int32_t bufferWidth = kBufferWidth;
48*38e8c45fSAndroid Build Coastguard Worker     const int32_t bufferHeight = kBufferWidth;
49*38e8c45fSAndroid Build Coastguard Worker 
50*38e8c45fSAndroid Build Coastguard Worker     const auto kUsageFlags = static_cast<uint64_t>(
51*38e8c45fSAndroid Build Coastguard Worker             GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE);
52*38e8c45fSAndroid Build Coastguard Worker 
53*38e8c45fSAndroid Build Coastguard Worker     // ring buffers here to do double-buffered rendering to avoid
54*38e8c45fSAndroid Build Coastguard Worker     // possible tearing and also to reduce memory take-up.
55*38e8c45fSAndroid Build Coastguard Worker     if (ringBuffer == nullptr) {
56*38e8c45fSAndroid Build Coastguard Worker         ringBuffer = sp<GraphicBuffer>::make(static_cast<uint32_t>(bufferWidth),
57*38e8c45fSAndroid Build Coastguard Worker                                              static_cast<uint32_t>(bufferHeight),
58*38e8c45fSAndroid Build Coastguard Worker                                              HAL_PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags,
59*38e8c45fSAndroid Build Coastguard Worker                                              "HdrSdrRatioOverlayBuffer");
60*38e8c45fSAndroid Build Coastguard Worker     }
61*38e8c45fSAndroid Build Coastguard Worker 
62*38e8c45fSAndroid Build Coastguard Worker     auto& buffer = ringBuffer;
63*38e8c45fSAndroid Build Coastguard Worker 
64*38e8c45fSAndroid Build Coastguard Worker     SkMatrix canvasTransform = SkMatrix();
65*38e8c45fSAndroid Build Coastguard Worker     switch (rotation) {
66*38e8c45fSAndroid Build Coastguard Worker         case ui::Transform::ROT_90:
67*38e8c45fSAndroid Build Coastguard Worker             canvasTransform.setTranslate(bufferHeight, 0);
68*38e8c45fSAndroid Build Coastguard Worker             canvasTransform.preRotate(90.f);
69*38e8c45fSAndroid Build Coastguard Worker             break;
70*38e8c45fSAndroid Build Coastguard Worker         case ui::Transform::ROT_270:
71*38e8c45fSAndroid Build Coastguard Worker             canvasTransform.setRotate(270.f, bufferWidth / 2.f, bufferWidth / 2.f);
72*38e8c45fSAndroid Build Coastguard Worker             break;
73*38e8c45fSAndroid Build Coastguard Worker         default:
74*38e8c45fSAndroid Build Coastguard Worker             break;
75*38e8c45fSAndroid Build Coastguard Worker     }
76*38e8c45fSAndroid Build Coastguard Worker 
77*38e8c45fSAndroid Build Coastguard Worker     const status_t bufferStatus = buffer->initCheck();
78*38e8c45fSAndroid Build Coastguard Worker     LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "HdrSdrRatioOverlay: Buffer failed to allocate: %d",
79*38e8c45fSAndroid Build Coastguard Worker                         bufferStatus);
80*38e8c45fSAndroid Build Coastguard Worker 
81*38e8c45fSAndroid Build Coastguard Worker     sk_sp<SkSurface> surface =
82*38e8c45fSAndroid Build Coastguard Worker             SkSurfaces::Raster(SkImageInfo::MakeN32Premul(bufferWidth, bufferHeight));
83*38e8c45fSAndroid Build Coastguard Worker     SkCanvas* canvas = surface->getCanvas();
84*38e8c45fSAndroid Build Coastguard Worker     canvas->setMatrix(canvasTransform);
85*38e8c45fSAndroid Build Coastguard Worker 
86*38e8c45fSAndroid Build Coastguard Worker     drawNumber(currentHdrSdrRatio, 0, color, *canvas);
87*38e8c45fSAndroid Build Coastguard Worker 
88*38e8c45fSAndroid Build Coastguard Worker     void* pixels = nullptr;
89*38e8c45fSAndroid Build Coastguard Worker     buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
90*38e8c45fSAndroid Build Coastguard Worker 
91*38e8c45fSAndroid Build Coastguard Worker     const SkImageInfo& imageInfo = surface->imageInfo();
92*38e8c45fSAndroid Build Coastguard Worker     const size_t dstRowBytes = buffer->getStride() * static_cast<size_t>(imageInfo.bytesPerPixel());
93*38e8c45fSAndroid Build Coastguard Worker 
94*38e8c45fSAndroid Build Coastguard Worker     canvas->readPixels(imageInfo, pixels, dstRowBytes, 0, 0);
95*38e8c45fSAndroid Build Coastguard Worker     buffer->unlock();
96*38e8c45fSAndroid Build Coastguard Worker     return buffer;
97*38e8c45fSAndroid Build Coastguard Worker }
98*38e8c45fSAndroid Build Coastguard Worker 
create()99*38e8c45fSAndroid Build Coastguard Worker std::unique_ptr<HdrSdrRatioOverlay> HdrSdrRatioOverlay::create() {
100*38e8c45fSAndroid Build Coastguard Worker     std::unique_ptr<HdrSdrRatioOverlay> overlay =
101*38e8c45fSAndroid Build Coastguard Worker             std::make_unique<HdrSdrRatioOverlay>(ConstructorTag{});
102*38e8c45fSAndroid Build Coastguard Worker     if (overlay->initCheck()) {
103*38e8c45fSAndroid Build Coastguard Worker         return overlay;
104*38e8c45fSAndroid Build Coastguard Worker     }
105*38e8c45fSAndroid Build Coastguard Worker 
106*38e8c45fSAndroid Build Coastguard Worker     ALOGE("%s: Failed to create HdrSdrRatioOverlay", __func__);
107*38e8c45fSAndroid Build Coastguard Worker     return {};
108*38e8c45fSAndroid Build Coastguard Worker }
109*38e8c45fSAndroid Build Coastguard Worker 
HdrSdrRatioOverlay(ConstructorTag)110*38e8c45fSAndroid Build Coastguard Worker HdrSdrRatioOverlay::HdrSdrRatioOverlay(ConstructorTag)
111*38e8c45fSAndroid Build Coastguard Worker       : mSurfaceControl(
112*38e8c45fSAndroid Build Coastguard Worker                 SurfaceControlHolder::createSurfaceControlHolder(String8("HdrSdrRatioOverlay"))) {
113*38e8c45fSAndroid Build Coastguard Worker     if (!mSurfaceControl) {
114*38e8c45fSAndroid Build Coastguard Worker         ALOGE("%s: Failed to create buffer state layer", __func__);
115*38e8c45fSAndroid Build Coastguard Worker         return;
116*38e8c45fSAndroid Build Coastguard Worker     }
117*38e8c45fSAndroid Build Coastguard Worker     createTransaction()
118*38e8c45fSAndroid Build Coastguard Worker             .setLayer(mSurfaceControl->get(), INT32_MAX - 2)
119*38e8c45fSAndroid Build Coastguard Worker             .setTrustedOverlay(mSurfaceControl->get(), true)
120*38e8c45fSAndroid Build Coastguard Worker             .apply();
121*38e8c45fSAndroid Build Coastguard Worker }
122*38e8c45fSAndroid Build Coastguard Worker 
initCheck() const123*38e8c45fSAndroid Build Coastguard Worker bool HdrSdrRatioOverlay::initCheck() const {
124*38e8c45fSAndroid Build Coastguard Worker     return mSurfaceControl != nullptr;
125*38e8c45fSAndroid Build Coastguard Worker }
126*38e8c45fSAndroid Build Coastguard Worker 
changeHdrSdrRatio(float currentHdrSdrRatio)127*38e8c45fSAndroid Build Coastguard Worker void HdrSdrRatioOverlay::changeHdrSdrRatio(float currentHdrSdrRatio) {
128*38e8c45fSAndroid Build Coastguard Worker     mCurrentHdrSdrRatio = currentHdrSdrRatio;
129*38e8c45fSAndroid Build Coastguard Worker     animate();
130*38e8c45fSAndroid Build Coastguard Worker }
131*38e8c45fSAndroid Build Coastguard Worker 
setLayerStack(ui::LayerStack stack)132*38e8c45fSAndroid Build Coastguard Worker void HdrSdrRatioOverlay::setLayerStack(ui::LayerStack stack) {
133*38e8c45fSAndroid Build Coastguard Worker     createTransaction().setLayerStack(mSurfaceControl->get(), stack).apply();
134*38e8c45fSAndroid Build Coastguard Worker }
135*38e8c45fSAndroid Build Coastguard Worker 
setViewport(ui::Size viewport)136*38e8c45fSAndroid Build Coastguard Worker void HdrSdrRatioOverlay::setViewport(ui::Size viewport) {
137*38e8c45fSAndroid Build Coastguard Worker     constexpr int32_t kMaxWidth = 1000;
138*38e8c45fSAndroid Build Coastguard Worker     const auto width = std::min({kMaxWidth, viewport.width, viewport.height});
139*38e8c45fSAndroid Build Coastguard Worker     const auto height = 2 * width;
140*38e8c45fSAndroid Build Coastguard Worker     Rect frame((5 * width) >> 4, height >> 5);
141*38e8c45fSAndroid Build Coastguard Worker     // set the ratio frame to the top right of the screen
142*38e8c45fSAndroid Build Coastguard Worker     frame.offsetBy(viewport.width - frame.width(), height >> 4);
143*38e8c45fSAndroid Build Coastguard Worker 
144*38e8c45fSAndroid Build Coastguard Worker     createTransaction()
145*38e8c45fSAndroid Build Coastguard Worker             .setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth),
146*38e8c45fSAndroid Build Coastguard Worker                        0, 0, frame.getHeight() / static_cast<float>(kBufferHeight))
147*38e8c45fSAndroid Build Coastguard Worker             .setPosition(mSurfaceControl->get(), frame.left, frame.top)
148*38e8c45fSAndroid Build Coastguard Worker             .apply();
149*38e8c45fSAndroid Build Coastguard Worker }
150*38e8c45fSAndroid Build Coastguard Worker 
getOrCreateBuffers(float currentHdrSdrRatio)151*38e8c45fSAndroid Build Coastguard Worker auto HdrSdrRatioOverlay::getOrCreateBuffers(float currentHdrSdrRatio) -> const sp<GraphicBuffer> {
152*38e8c45fSAndroid Build Coastguard Worker     static const sp<GraphicBuffer> kNoBuffer;
153*38e8c45fSAndroid Build Coastguard Worker     if (!mSurfaceControl) return kNoBuffer;
154*38e8c45fSAndroid Build Coastguard Worker 
155*38e8c45fSAndroid Build Coastguard Worker     const auto transformHint =
156*38e8c45fSAndroid Build Coastguard Worker             static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint());
157*38e8c45fSAndroid Build Coastguard Worker 
158*38e8c45fSAndroid Build Coastguard Worker     // Tell SurfaceFlinger about the pre-rotation on the buffer.
159*38e8c45fSAndroid Build Coastguard Worker     const auto transform = [&] {
160*38e8c45fSAndroid Build Coastguard Worker         switch (transformHint) {
161*38e8c45fSAndroid Build Coastguard Worker             case ui::Transform::ROT_90:
162*38e8c45fSAndroid Build Coastguard Worker                 return ui::Transform::ROT_270;
163*38e8c45fSAndroid Build Coastguard Worker             case ui::Transform::ROT_270:
164*38e8c45fSAndroid Build Coastguard Worker                 return ui::Transform::ROT_90;
165*38e8c45fSAndroid Build Coastguard Worker             default:
166*38e8c45fSAndroid Build Coastguard Worker                 return ui::Transform::ROT_0;
167*38e8c45fSAndroid Build Coastguard Worker         }
168*38e8c45fSAndroid Build Coastguard Worker     }();
169*38e8c45fSAndroid Build Coastguard Worker 
170*38e8c45fSAndroid Build Coastguard Worker     createTransaction().setTransform(mSurfaceControl->get(), transform).apply();
171*38e8c45fSAndroid Build Coastguard Worker 
172*38e8c45fSAndroid Build Coastguard Worker     constexpr SkColor kMinRatioColor = SK_ColorBLUE;
173*38e8c45fSAndroid Build Coastguard Worker     constexpr SkColor kMaxRatioColor = SK_ColorGREEN;
174*38e8c45fSAndroid Build Coastguard Worker     constexpr float kAlpha = 0.8f;
175*38e8c45fSAndroid Build Coastguard Worker 
176*38e8c45fSAndroid Build Coastguard Worker     // 9.f is picked here as ratio range, given that we assume that
177*38e8c45fSAndroid Build Coastguard Worker     // hdr/sdr ratio is [1.f, 10.f)
178*38e8c45fSAndroid Build Coastguard Worker     const float scale = currentHdrSdrRatio / 9.f;
179*38e8c45fSAndroid Build Coastguard Worker 
180*38e8c45fSAndroid Build Coastguard Worker     SkColor4f colorBase = SkColor4f::FromColor(kMaxRatioColor) * scale;
181*38e8c45fSAndroid Build Coastguard Worker     const SkColor4f minRatioColor = SkColor4f::FromColor(kMinRatioColor) * (1 - scale);
182*38e8c45fSAndroid Build Coastguard Worker 
183*38e8c45fSAndroid Build Coastguard Worker     colorBase.fR = colorBase.fR + minRatioColor.fR;
184*38e8c45fSAndroid Build Coastguard Worker     colorBase.fG = colorBase.fG + minRatioColor.fG;
185*38e8c45fSAndroid Build Coastguard Worker     colorBase.fB = colorBase.fB + minRatioColor.fB;
186*38e8c45fSAndroid Build Coastguard Worker     colorBase.fA = kAlpha;
187*38e8c45fSAndroid Build Coastguard Worker 
188*38e8c45fSAndroid Build Coastguard Worker     const SkColor color = colorBase.toSkColor();
189*38e8c45fSAndroid Build Coastguard Worker 
190*38e8c45fSAndroid Build Coastguard Worker     auto buffer = draw(currentHdrSdrRatio, color, transformHint, mRingBuffer[mIndex]);
191*38e8c45fSAndroid Build Coastguard Worker     mIndex = (mIndex + 1) % 2;
192*38e8c45fSAndroid Build Coastguard Worker     return buffer;
193*38e8c45fSAndroid Build Coastguard Worker }
194*38e8c45fSAndroid Build Coastguard Worker 
animate()195*38e8c45fSAndroid Build Coastguard Worker void HdrSdrRatioOverlay::animate() {
196*38e8c45fSAndroid Build Coastguard Worker     if (!std::isfinite(mCurrentHdrSdrRatio) || mCurrentHdrSdrRatio < 1.0f) return;
197*38e8c45fSAndroid Build Coastguard Worker     createTransaction()
198*38e8c45fSAndroid Build Coastguard Worker             .setBuffer(mSurfaceControl->get(), getOrCreateBuffers(mCurrentHdrSdrRatio))
199*38e8c45fSAndroid Build Coastguard Worker             .apply();
200*38e8c45fSAndroid Build Coastguard Worker }
201*38e8c45fSAndroid Build Coastguard Worker 
createTransaction() const202*38e8c45fSAndroid Build Coastguard Worker SurfaceComposerClient::Transaction HdrSdrRatioOverlay::createTransaction() const {
203*38e8c45fSAndroid Build Coastguard Worker     constexpr float kFrameRate = 0.f;
204*38e8c45fSAndroid Build Coastguard Worker     constexpr int8_t kCompatibility = ANATIVEWINDOW_FRAME_RATE_NO_VOTE;
205*38e8c45fSAndroid Build Coastguard Worker     constexpr int8_t kSeamlessness = ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS;
206*38e8c45fSAndroid Build Coastguard Worker 
207*38e8c45fSAndroid Build Coastguard Worker     const sp<SurfaceControl>& surface = mSurfaceControl->get();
208*38e8c45fSAndroid Build Coastguard Worker 
209*38e8c45fSAndroid Build Coastguard Worker     SurfaceComposerClient::Transaction transaction;
210*38e8c45fSAndroid Build Coastguard Worker     transaction.setFrameRate(surface, kFrameRate, kCompatibility, kSeamlessness);
211*38e8c45fSAndroid Build Coastguard Worker     return transaction;
212*38e8c45fSAndroid Build Coastguard Worker }
213*38e8c45fSAndroid Build Coastguard Worker 
214*38e8c45fSAndroid Build Coastguard Worker } // namespace android