Add new SF backdoor option: hdr/sdr ratio overlay

 - follow how RefreshRateOverlay did in general.
 - 1043 as new opcode to enable hdr/sdr ratio overlay.
 - decouple SevenSegmentDrawer and SurfaceControlHandle helper classes
   from RefreshRateOverlay.
 - add corresponding SF backdoor test for validation.
 - for non-HDR-supported device, we don't reject them to use SF backdoor
   but 1.00 will be always shown on the screen if enabling.

Test: adb shell call service call SurfaceFlinger 1043 1; atest
SurfaceFlinger_test
Bug: 277957056

Change-Id: Ifce2d435f127b53ab9d01aba4e24ffd4fdb010a7
diff --git a/services/surfaceflinger/HdrSdrRatioOverlay.cpp b/services/surfaceflinger/HdrSdrRatioOverlay.cpp
new file mode 100644
index 0000000..2c0f518
--- /dev/null
+++ b/services/surfaceflinger/HdrSdrRatioOverlay.cpp
@@ -0,0 +1,178 @@
+/**
+ * Copyright (C) 2023 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.
+ */
+// #define LOG_NDEBUG 0
+#include <algorithm>
+
+#include "HdrSdrRatioOverlay.h"
+
+#include <SkSurface.h>
+
+#undef LOG_TAG
+#define LOG_TAG "HdrSdrRatioOverlay"
+
+namespace android {
+
+void HdrSdrRatioOverlay::drawNumber(float number, int left, SkColor color, SkCanvas& canvas) {
+    if (!isfinite(number) || number >= 10.f) return;
+    // We assume that the number range is [1.f, 10.f)
+    // and the decimal places are 2.
+    int value = static_cast<int>(number * 100);
+    SegmentDrawer::drawDigit(value / 100, left, color, canvas);
+
+    left += kDigitWidth + kDigitSpace;
+    SegmentDrawer::drawSegment(SegmentDrawer::Segment::DecimalPoint, left, color, canvas);
+    left += kDigitWidth + kDigitSpace;
+
+    SegmentDrawer::drawDigit((value / 10) % 10, left, color, canvas);
+    left += kDigitWidth + kDigitSpace;
+    SegmentDrawer::drawDigit(value % 10, left, color, canvas);
+}
+
+sp<GraphicBuffer> HdrSdrRatioOverlay::draw(float currentHdrSdrRatio, SkColor color,
+                                           ui::Transform::RotationFlags rotation) {
+    SkMatrix canvasTransform = SkMatrix();
+    const auto [bufferWidth, bufferHeight] = [&]() -> std::pair<int, int> {
+        switch (rotation) {
+            case ui::Transform::ROT_90:
+                canvasTransform.setTranslate(kBufferHeight, 0);
+                canvasTransform.preRotate(90.f);
+                return {kBufferHeight, kBufferWidth};
+            case ui::Transform::ROT_270:
+                canvasTransform.setRotate(270.f, kBufferWidth / 2.f, kBufferWidth / 2.f);
+                return {kBufferHeight, kBufferWidth};
+            default:
+                return {kBufferWidth, kBufferHeight};
+        }
+    }();
+
+    const auto kUsageFlags = static_cast<uint64_t>(
+            GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE);
+    sp<GraphicBuffer> buffer =
+            sp<GraphicBuffer>::make(static_cast<uint32_t>(bufferWidth),
+                                    static_cast<uint32_t>(bufferHeight), HAL_PIXEL_FORMAT_RGBA_8888,
+                                    1u, kUsageFlags, "HdrSdrRatioOverlay");
+
+    const status_t bufferStatus = buffer->initCheck();
+    LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "HdrSdrRatioOverlay: Buffer failed to allocate: %d",
+                        bufferStatus);
+
+    sk_sp<SkSurface> surface =
+            SkSurfaces::Raster(SkImageInfo::MakeN32Premul(bufferWidth, bufferHeight));
+    SkCanvas* canvas = surface->getCanvas();
+    canvas->setMatrix(canvasTransform);
+
+    drawNumber(currentHdrSdrRatio, 0, color, *canvas);
+
+    void* pixels = nullptr;
+    buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
+
+    const SkImageInfo& imageInfo = surface->imageInfo();
+    const size_t dstRowBytes = buffer->getStride() * static_cast<size_t>(imageInfo.bytesPerPixel());
+
+    canvas->readPixels(imageInfo, pixels, dstRowBytes, 0, 0);
+    buffer->unlock();
+    return buffer;
+}
+
+HdrSdrRatioOverlay::HdrSdrRatioOverlay()
+      : mSurfaceControl(
+                SurfaceControlHolder::createSurfaceControlHolder(String8("HdrSdrRatioOverlay"))) {
+    if (!mSurfaceControl) {
+        ALOGE("%s: Failed to create buffer state layer", __func__);
+        return;
+    }
+    SurfaceComposerClient::Transaction()
+            .setLayer(mSurfaceControl->get(), INT32_MAX - 2)
+            .setTrustedOverlay(mSurfaceControl->get(), true)
+            .apply();
+}
+
+void HdrSdrRatioOverlay::changeHdrSdrRatio(float currentHdrSdrRatio) {
+    mCurrentHdrSdrRatio = currentHdrSdrRatio;
+    animate();
+}
+
+void HdrSdrRatioOverlay::setLayerStack(ui::LayerStack stack) {
+    SurfaceComposerClient::Transaction().setLayerStack(mSurfaceControl->get(), stack).apply();
+}
+
+void HdrSdrRatioOverlay::setViewport(ui::Size viewport) {
+    constexpr int32_t kMaxWidth = 1000;
+    const auto width = std::min({kMaxWidth, viewport.width, viewport.height});
+    const auto height = 2 * width;
+    Rect frame((5 * width) >> 4, height >> 5);
+    // set the ratio frame to the top right of the screen
+    frame.offsetBy(viewport.width - frame.width(), height >> 4);
+
+    SurfaceComposerClient::Transaction()
+            .setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth),
+                       0, 0, frame.getHeight() / static_cast<float>(kBufferHeight))
+            .setPosition(mSurfaceControl->get(), frame.left, frame.top)
+            .apply();
+}
+
+auto HdrSdrRatioOverlay::getOrCreateBuffers(float currentHdrSdrRatio) -> const sp<GraphicBuffer> {
+    static const sp<GraphicBuffer> kNoBuffer;
+    if (!mSurfaceControl) return kNoBuffer;
+
+    const auto transformHint =
+            static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint());
+
+    // Tell SurfaceFlinger about the pre-rotation on the buffer.
+    const auto transform = [&] {
+        switch (transformHint) {
+            case ui::Transform::ROT_90:
+                return ui::Transform::ROT_270;
+            case ui::Transform::ROT_270:
+                return ui::Transform::ROT_90;
+            default:
+                return ui::Transform::ROT_0;
+        }
+    }();
+
+    SurfaceComposerClient::Transaction().setTransform(mSurfaceControl->get(), transform).apply();
+
+    constexpr SkColor kMinRatioColor = SK_ColorBLUE;
+    constexpr SkColor kMaxRatioColor = SK_ColorGREEN;
+    constexpr float kAlpha = 0.8f;
+
+    // 9.f is picked here as ratio range, given that we assume that
+    // hdr/sdr ratio is [1.f, 10.f)
+    const float scale = currentHdrSdrRatio / 9.f;
+
+    SkColor4f colorBase = SkColor4f::FromColor(kMaxRatioColor) * scale;
+    const SkColor4f minRatioColor = SkColor4f::FromColor(kMinRatioColor) * (1 - scale);
+
+    colorBase.fR = colorBase.fR + minRatioColor.fR;
+    colorBase.fG = colorBase.fG + minRatioColor.fG;
+    colorBase.fB = colorBase.fB + minRatioColor.fB;
+    colorBase.fA = kAlpha;
+
+    const SkColor color = colorBase.toSkColor();
+
+    auto buffer = draw(currentHdrSdrRatio, color, transformHint);
+    return buffer;
+}
+
+void HdrSdrRatioOverlay::animate() {
+    if (!std::isfinite(mCurrentHdrSdrRatio) || mCurrentHdrSdrRatio < 1.0f) return;
+
+    SurfaceComposerClient::Transaction()
+            .setBuffer(mSurfaceControl->get(), getOrCreateBuffers(mCurrentHdrSdrRatio))
+            .apply();
+}
+
+} // namespace android
\ No newline at end of file