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