1 /**
2  * Copyright (C) 2023 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 // #define LOG_NDEBUG 0
17 #include <algorithm>
18 
19 #include "HdrSdrRatioOverlay.h"
20 
21 #include <SkSurface.h>
22 
23 #undef LOG_TAG
24 #define LOG_TAG "HdrSdrRatioOverlay"
25 
26 namespace android {
27 
drawNumber(float number,int left,SkColor color,SkCanvas & canvas)28 void HdrSdrRatioOverlay::drawNumber(float number, int left, SkColor color, SkCanvas& canvas) {
29     if (!isfinite(number) || number >= 10.f) return;
30     // We assume that the number range is [1.f, 10.f)
31     // and the decimal places are 2.
32     int value = static_cast<int>(number * 100);
33     SegmentDrawer::drawDigit(value / 100, left, color, canvas);
34 
35     left += kDigitWidth + kDigitSpace;
36     SegmentDrawer::drawSegment(SegmentDrawer::Segment::DecimalPoint, left, color, canvas);
37     left += kDigitWidth + kDigitSpace;
38 
39     SegmentDrawer::drawDigit((value / 10) % 10, left, color, canvas);
40     left += kDigitWidth + kDigitSpace;
41     SegmentDrawer::drawDigit(value % 10, left, color, canvas);
42 }
43 
draw(float currentHdrSdrRatio,SkColor color,ui::Transform::RotationFlags rotation,sp<GraphicBuffer> & ringBuffer)44 sp<GraphicBuffer> HdrSdrRatioOverlay::draw(float currentHdrSdrRatio, SkColor color,
45                                            ui::Transform::RotationFlags rotation,
46                                            sp<GraphicBuffer>& ringBuffer) {
47     const int32_t bufferWidth = kBufferWidth;
48     const int32_t bufferHeight = kBufferWidth;
49 
50     const auto kUsageFlags = static_cast<uint64_t>(
51             GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE);
52 
53     // ring buffers here to do double-buffered rendering to avoid
54     // possible tearing and also to reduce memory take-up.
55     if (ringBuffer == nullptr) {
56         ringBuffer = sp<GraphicBuffer>::make(static_cast<uint32_t>(bufferWidth),
57                                              static_cast<uint32_t>(bufferHeight),
58                                              HAL_PIXEL_FORMAT_RGBA_8888, 1u, kUsageFlags,
59                                              "HdrSdrRatioOverlayBuffer");
60     }
61 
62     auto& buffer = ringBuffer;
63 
64     SkMatrix canvasTransform = SkMatrix();
65     switch (rotation) {
66         case ui::Transform::ROT_90:
67             canvasTransform.setTranslate(bufferHeight, 0);
68             canvasTransform.preRotate(90.f);
69             break;
70         case ui::Transform::ROT_270:
71             canvasTransform.setRotate(270.f, bufferWidth / 2.f, bufferWidth / 2.f);
72             break;
73         default:
74             break;
75     }
76 
77     const status_t bufferStatus = buffer->initCheck();
78     LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "HdrSdrRatioOverlay: Buffer failed to allocate: %d",
79                         bufferStatus);
80 
81     sk_sp<SkSurface> surface =
82             SkSurfaces::Raster(SkImageInfo::MakeN32Premul(bufferWidth, bufferHeight));
83     SkCanvas* canvas = surface->getCanvas();
84     canvas->setMatrix(canvasTransform);
85 
86     drawNumber(currentHdrSdrRatio, 0, color, *canvas);
87 
88     void* pixels = nullptr;
89     buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
90 
91     const SkImageInfo& imageInfo = surface->imageInfo();
92     const size_t dstRowBytes = buffer->getStride() * static_cast<size_t>(imageInfo.bytesPerPixel());
93 
94     canvas->readPixels(imageInfo, pixels, dstRowBytes, 0, 0);
95     buffer->unlock();
96     return buffer;
97 }
98 
create()99 std::unique_ptr<HdrSdrRatioOverlay> HdrSdrRatioOverlay::create() {
100     std::unique_ptr<HdrSdrRatioOverlay> overlay =
101             std::make_unique<HdrSdrRatioOverlay>(ConstructorTag{});
102     if (overlay->initCheck()) {
103         return overlay;
104     }
105 
106     ALOGE("%s: Failed to create HdrSdrRatioOverlay", __func__);
107     return {};
108 }
109 
HdrSdrRatioOverlay(ConstructorTag)110 HdrSdrRatioOverlay::HdrSdrRatioOverlay(ConstructorTag)
111       : mSurfaceControl(
112                 SurfaceControlHolder::createSurfaceControlHolder(String8("HdrSdrRatioOverlay"))) {
113     if (!mSurfaceControl) {
114         ALOGE("%s: Failed to create buffer state layer", __func__);
115         return;
116     }
117     SurfaceComposerClient::Transaction()
118             .setLayer(mSurfaceControl->get(), INT32_MAX - 2)
119             .setTrustedOverlay(mSurfaceControl->get(), true)
120             .apply();
121 }
122 
initCheck() const123 bool HdrSdrRatioOverlay::initCheck() const {
124     return mSurfaceControl != nullptr;
125 }
126 
changeHdrSdrRatio(float currentHdrSdrRatio)127 void HdrSdrRatioOverlay::changeHdrSdrRatio(float currentHdrSdrRatio) {
128     mCurrentHdrSdrRatio = currentHdrSdrRatio;
129     animate();
130 }
131 
setLayerStack(ui::LayerStack stack)132 void HdrSdrRatioOverlay::setLayerStack(ui::LayerStack stack) {
133     SurfaceComposerClient::Transaction().setLayerStack(mSurfaceControl->get(), stack).apply();
134 }
135 
setViewport(ui::Size viewport)136 void HdrSdrRatioOverlay::setViewport(ui::Size viewport) {
137     constexpr int32_t kMaxWidth = 1000;
138     const auto width = std::min({kMaxWidth, viewport.width, viewport.height});
139     const auto height = 2 * width;
140     Rect frame((5 * width) >> 4, height >> 5);
141     // set the ratio frame to the top right of the screen
142     frame.offsetBy(viewport.width - frame.width(), height >> 4);
143 
144     SurfaceComposerClient::Transaction()
145             .setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth),
146                        0, 0, frame.getHeight() / static_cast<float>(kBufferHeight))
147             .setPosition(mSurfaceControl->get(), frame.left, frame.top)
148             .apply();
149 }
150 
getOrCreateBuffers(float currentHdrSdrRatio)151 auto HdrSdrRatioOverlay::getOrCreateBuffers(float currentHdrSdrRatio) -> const sp<GraphicBuffer> {
152     static const sp<GraphicBuffer> kNoBuffer;
153     if (!mSurfaceControl) return kNoBuffer;
154 
155     const auto transformHint =
156             static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint());
157 
158     // Tell SurfaceFlinger about the pre-rotation on the buffer.
159     const auto transform = [&] {
160         switch (transformHint) {
161             case ui::Transform::ROT_90:
162                 return ui::Transform::ROT_270;
163             case ui::Transform::ROT_270:
164                 return ui::Transform::ROT_90;
165             default:
166                 return ui::Transform::ROT_0;
167         }
168     }();
169 
170     SurfaceComposerClient::Transaction().setTransform(mSurfaceControl->get(), transform).apply();
171 
172     constexpr SkColor kMinRatioColor = SK_ColorBLUE;
173     constexpr SkColor kMaxRatioColor = SK_ColorGREEN;
174     constexpr float kAlpha = 0.8f;
175 
176     // 9.f is picked here as ratio range, given that we assume that
177     // hdr/sdr ratio is [1.f, 10.f)
178     const float scale = currentHdrSdrRatio / 9.f;
179 
180     SkColor4f colorBase = SkColor4f::FromColor(kMaxRatioColor) * scale;
181     const SkColor4f minRatioColor = SkColor4f::FromColor(kMinRatioColor) * (1 - scale);
182 
183     colorBase.fR = colorBase.fR + minRatioColor.fR;
184     colorBase.fG = colorBase.fG + minRatioColor.fG;
185     colorBase.fB = colorBase.fB + minRatioColor.fB;
186     colorBase.fA = kAlpha;
187 
188     const SkColor color = colorBase.toSkColor();
189 
190     auto buffer = draw(currentHdrSdrRatio, color, transformHint, mRingBuffer[mIndex]);
191     mIndex = (mIndex + 1) % 2;
192     return buffer;
193 }
194 
animate()195 void HdrSdrRatioOverlay::animate() {
196     if (!std::isfinite(mCurrentHdrSdrRatio) || mCurrentHdrSdrRatio < 1.0f) return;
197     SurfaceComposerClient::Transaction()
198             .setBuffer(mSurfaceControl->get(), getOrCreateBuffers(mCurrentHdrSdrRatio))
199             .apply();
200 }
201 
202 } // namespace android