/* * Copyright (c) 2022-2023 Huawei Device Co., Ltd. * 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. */ #include "display_cutout_controller.h" #include #include "display_manager_service_inner.h" #include "dm_common.h" namespace OHOS { namespace Rosen { namespace { constexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_DISPLAY, "DisplayCutoutController"}; const uint32_t NO_WATERFALL_DISPLAY_COMPRESSION_SIZE = 0; } bool DisplayCutoutController::isWaterfallDisplay_ = false; bool DisplayCutoutController::isWaterfallAreaCompressionEnableWhenHorizontal_ = false; uint32_t DisplayCutoutController::waterfallAreaCompressionSizeWhenHorizontal_ = 0; void DisplayCutoutController::SetBuiltInDisplayCutoutSvgPath(const std::string& svgPath) { SetCutoutSvgPath(0, svgPath); } void DisplayCutoutController::SetIsWaterfallDisplay(bool isWaterfallDisplay) { WLOGFI("Set isWaterfallDisplay: %{public}u", isWaterfallDisplay); isWaterfallDisplay_ = isWaterfallDisplay; } bool DisplayCutoutController::IsWaterfallDisplay() { return isWaterfallDisplay_; } void DisplayCutoutController::SetCurvedScreenBoundary(std::vector curvedScreenBoundary) { while (curvedScreenBoundary.size() < 4) { // 4 directions. curvedScreenBoundary.emplace_back(0); } WLOGFI("Set curvedScreenBoundary"); curvedScreenBoundary_ = curvedScreenBoundary; } void DisplayCutoutController::SetCutoutSvgPath(DisplayId displayId, const std::string& svgPath) { WLOGFI("Set SvgPath: %{public}s", svgPath.c_str()); if (svgPaths_.count(displayId) == 1) { svgPaths_[displayId].emplace_back(svgPath); } else { std::vector pathVec; pathVec.emplace_back(svgPath); svgPaths_[displayId] = pathVec; } DMRect boundingRect = CalcCutoutBoundingRect(svgPath); if (boundingRects_.count(displayId) == 1) { boundingRects_[displayId].emplace_back(boundingRect); } else { std::vector rectVec; rectVec.emplace_back(boundingRect); boundingRects_[displayId] = rectVec; } } sptr DisplayCutoutController::GetCutoutInfo(DisplayId displayId) { WLOGFD("Get Cutout Info"); std::vector boundingRects; WaterfallDisplayAreaRects waterfallDisplayAreaRects; if (boundingRects_.count(displayId) == 1) { TransferBoundingRectsByRotation(displayId, boundingRects); } if (displayId == DisplayManagerServiceInner::GetInstance().GetDefaultDisplayId()) { CalcBuiltInDisplayWaterfallRects(); waterfallDisplayAreaRects = waterfallDisplayAreaRects_; } sptr cutoutInfo(new CutoutInfo(boundingRects, waterfallDisplayAreaRects)); return cutoutInfo; } void DisplayCutoutController::CheckBoundingRectsBoundary(DisplayId displayId, std::vector& boundingRects) { sptr modes = DisplayManagerServiceInner::GetInstance().GetScreenModesByDisplayId(displayId); if (modes == nullptr) { WLOGFE("DisplayId is invalid"); return; } uint32_t displayHeight = modes->height_; uint32_t displayWidth = modes->width_; for (auto iter = boundingRects.begin(); iter != boundingRects.end();) { DMRect boundingRect = *iter; if (boundingRect.posX_ < 0 || boundingRect.posY_ < 0 || static_cast(boundingRect.width_) + boundingRect.posX_ > static_cast(displayWidth) || static_cast(boundingRect.height_) + boundingRect.posY_ > static_cast(displayHeight) || boundingRect.width_ > displayWidth || boundingRect.height_ > displayHeight || boundingRect.IsUninitializedRect()) { WLOGFE("boundingRect boundary is invalid"); iter = boundingRects.erase(iter); } else { iter++; } } } DMRect DisplayCutoutController::CalcCutoutBoundingRect(std::string svgPath) { DMRect emptyRect = {0, 0, 0, 0}; SkPath skCutoutSvgPath; if (!SkParsePath::FromSVGString(svgPath.c_str(), &skCutoutSvgPath)) { WLOGFE("Parse svg string path failed."); return emptyRect; } SkRect skRect = skCutoutSvgPath.computeTightBounds(); if (skRect.isEmpty()) { WLOGFW("Get empty skRect"); return emptyRect; } SkIRect skiRect = skRect.roundOut(); if (skiRect.isEmpty()) { WLOGFW("Get empty skiRect"); return emptyRect; } int32_t left = static_cast(skiRect.left()); int32_t top = static_cast(skiRect.top()); uint32_t width = static_cast(skiRect.width()); uint32_t height = static_cast(skiRect.height()); WLOGFI("calc rect from path,[%{public}d, %{public}d, %{public}u, %{public}u]", left, top, width, height); DMRect cutoutMinOuterRect = {.posX_ = left, .posY_ = top, .width_ = width, .height_ = height}; return cutoutMinOuterRect; } void DisplayCutoutController::CalcBuiltInDisplayWaterfallRects() { WaterfallDisplayAreaRects emptyRects = {}; if (!isWaterfallDisplay_) { WLOGFI("not waterfall display"); waterfallDisplayAreaRects_ = emptyRects; return; } if (curvedScreenBoundary_.empty()) { WLOGFI("curved screen boundary is empty"); waterfallDisplayAreaRects_ = emptyRects; return; } uint32_t left = static_cast(curvedScreenBoundary_[0]); uint32_t top = static_cast(curvedScreenBoundary_[1]); uint32_t right = static_cast(curvedScreenBoundary_[2]); uint32_t bottom = static_cast(curvedScreenBoundary_[3]); if (left == 0 && top == 0 && right == 0 && bottom == 0) { waterfallDisplayAreaRects_ = emptyRects; return; } sptr modes = DisplayManagerServiceInner::GetInstance().GetScreenModesByDisplayId( DisplayManagerServiceInner::GetInstance().GetDefaultDisplayId()); if (!modes) { WLOGE("support screen modes get failed"); waterfallDisplayAreaRects_ = emptyRects; return; } uint32_t displayHeight = modes->height_; uint32_t displayWidth = modes->width_; if ((left > displayWidth / 2) || (right > displayWidth / 2) || // invalid if more than 1/2 width (top > displayHeight / 2) || (bottom > displayHeight / 2)) { // invalid if more than 1/2 height WLOGFE("Curved screen boundary data is not valid."); waterfallDisplayAreaRects_ = emptyRects; return; } CalcBuiltInDisplayWaterfallRectsByRotation( DisplayManagerServiceInner::GetInstance().GetDefaultDisplay()->GetRotation(), displayHeight, displayWidth); } void DisplayCutoutController::CalcBuiltInDisplayWaterfallRectsByRotation( Rotation rotation, uint32_t displayHeight, uint32_t displayWidth) { uint32_t left = static_cast(curvedScreenBoundary_[0]); uint32_t top = static_cast(curvedScreenBoundary_[1]); uint32_t right = static_cast(curvedScreenBoundary_[2]); uint32_t bottom = static_cast(curvedScreenBoundary_[3]); switch (rotation) { case Rotation::ROTATION_0: { DMRect leftRect = CreateWaterfallRect(0, 0, left, displayHeight); DMRect topRect = CreateWaterfallRect(0, 0, displayWidth, top); DMRect rightRect = CreateWaterfallRect(displayWidth - right, 0, right, displayHeight); DMRect bottomRect = CreateWaterfallRect(0, displayHeight - bottom, displayWidth, bottom); waterfallDisplayAreaRects_ = WaterfallDisplayAreaRects {leftRect, topRect, rightRect, bottomRect}; return; } case Rotation::ROTATION_90: { DMRect leftRect = CreateWaterfallRect(0, 0, bottom, displayWidth); DMRect topRect = CreateWaterfallRect(0, 0, displayHeight, left); DMRect rightRect = CreateWaterfallRect(displayHeight - top, 0, top, displayWidth); DMRect bottomRect = CreateWaterfallRect(0, displayWidth - right, displayHeight, right); waterfallDisplayAreaRects_ = WaterfallDisplayAreaRects {leftRect, topRect, rightRect, bottomRect}; return; } case Rotation::ROTATION_180: { DMRect leftRect = CreateWaterfallRect(0, 0, right, displayHeight); DMRect topRect = CreateWaterfallRect(0, 0, bottom, displayWidth); DMRect rightRect = CreateWaterfallRect(displayWidth - left, 0, left, displayHeight); DMRect bottomRect = CreateWaterfallRect(0, displayHeight - top, displayWidth, top); waterfallDisplayAreaRects_ = WaterfallDisplayAreaRects {leftRect, topRect, rightRect, bottomRect}; return; } case Rotation::ROTATION_270: { DMRect leftRect = CreateWaterfallRect(0, 0, top, displayWidth); DMRect topRect = CreateWaterfallRect(0, 0, displayHeight, right); DMRect rightRect = CreateWaterfallRect(displayHeight - bottom, 0, bottom, displayWidth); DMRect bottomRect = CreateWaterfallRect(0, displayWidth - left, displayHeight, left); waterfallDisplayAreaRects_ = WaterfallDisplayAreaRects {leftRect, topRect, rightRect, bottomRect}; return; } default: { } } } void DisplayCutoutController::TransferBoundingRectsByRotation(DisplayId displayId, std::vector& boundingRects) { std::vector resultVec; if (boundingRects_.count(displayId) == 0) { boundingRects = resultVec; return; } std::vector displayBoundingRects = boundingRects_[displayId]; if (displayBoundingRects.empty()) { boundingRects = resultVec; return; } sptr displayInfo = DisplayManagerServiceInner::GetInstance().GetDisplayById(displayId); if (!displayInfo) { WLOGFE("display invaild"); return; } Rotation currentRotation = displayInfo->GetRotation(); CheckBoundingRectsBoundary(displayId, displayBoundingRects); if (currentRotation == Rotation::ROTATION_0) { boundingRects = displayBoundingRects; return; } sptr modes = DisplayManagerServiceInner::GetInstance().GetScreenModesByDisplayId(displayId); if (!modes) { WLOGE("support screen modes get failed"); return; } uint32_t displayHeight = modes->height_; uint32_t displayWidth = modes->width_; switch (currentRotation) { case Rotation::ROTATION_90: { CurrentRotation90(resultVec, displayBoundingRects, displayHeight); break; } case Rotation::ROTATION_180: { CurrentRotation180(resultVec, displayBoundingRects, displayHeight, displayWidth); break; } case Rotation::ROTATION_270: { for (DMRect rect : displayBoundingRects) { resultVec.emplace_back(DMRect {rect.posY_, displayWidth - rect.posX_ - rect.width_, rect.height_, rect.width_}); } break; } default: { } } boundingRects = resultVec; } void DisplayCutoutController::CurrentRotation90(std::vector resultVec, std::vector displayBoundingRects, uint32_t displayHeight) { for (DMRect rect : displayBoundingRects) { resultVec.emplace_back(DMRect {.posX_ = displayHeight - rect.posY_ - rect.height_, .posY_ = rect.posX_, .width_ = rect.height_, .height_ = rect.width_}); } } void DisplayCutoutController::CurrentRotation180(std::vector resultVec, std::vector displayBoundingRects, uint32_t displayHeight, uint32_t displayWidth) { for (DMRect rect : displayBoundingRects) { resultVec.emplace_back(DMRect {displayWidth - rect.posX_ - rect.width_, displayHeight - rect.posY_ - rect.height_, rect.width_, rect.height_}); } } DMRect DisplayCutoutController::CreateWaterfallRect(uint32_t left, uint32_t top, uint32_t width, uint32_t height) { if (width == 0 || height == 0) { return DMRect {0, 0, 0, 0}; } return DMRect {left, top, width, height}; } void DisplayCutoutController::SetWaterfallAreaCompressionEnableWhenHorzontal(bool isEnable) { isWaterfallAreaCompressionEnableWhenHorizontal_ = isEnable; } void DisplayCutoutController::SetWaterfallAreaCompressionSizeWhenHorizontal(uint32_t size) { waterfallAreaCompressionSizeWhenHorizontal_ = size; } bool DisplayCutoutController::IsWaterfallAreaCompressionEnableWhenHorizontal() { return isWaterfallDisplay_ && isWaterfallAreaCompressionEnableWhenHorizontal_; } uint32_t DisplayCutoutController::GetWaterfallAreaCompressionSizeWhenHorizontal() { if (!isWaterfallDisplay_ || !isWaterfallAreaCompressionEnableWhenHorizontal_) { WLOGFW("Not waterfall display or not enable waterfall compression"); return NO_WATERFALL_DISPLAY_COMPRESSION_SIZE; } return waterfallAreaCompressionSizeWhenHorizontal_; } } // Rosen } // OHOS