1/*
2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "display_cutout_controller.h"
17#include <screen_manager/screen_types.h>
18#include "display_manager_service_inner.h"
19#include "dm_common.h"
20
21namespace OHOS {
22namespace Rosen {
23namespace {
24constexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_DISPLAY, "DisplayCutoutController"};
25const uint32_t NO_WATERFALL_DISPLAY_COMPRESSION_SIZE = 0;
26}
27
28bool DisplayCutoutController::isWaterfallDisplay_ = false;
29bool DisplayCutoutController::isWaterfallAreaCompressionEnableWhenHorizontal_ = false;
30uint32_t DisplayCutoutController::waterfallAreaCompressionSizeWhenHorizontal_ = 0;
31
32void DisplayCutoutController::SetBuiltInDisplayCutoutSvgPath(const std::string& svgPath)
33{
34    SetCutoutSvgPath(0, svgPath);
35}
36
37void DisplayCutoutController::SetIsWaterfallDisplay(bool isWaterfallDisplay)
38{
39    WLOGFI("Set isWaterfallDisplay: %{public}u", isWaterfallDisplay);
40    isWaterfallDisplay_ = isWaterfallDisplay;
41}
42
43bool DisplayCutoutController::IsWaterfallDisplay()
44{
45    return isWaterfallDisplay_;
46}
47
48void DisplayCutoutController::SetCurvedScreenBoundary(std::vector<int> curvedScreenBoundary)
49{
50    while (curvedScreenBoundary.size() < 4) { // 4 directions.
51        curvedScreenBoundary.emplace_back(0);
52    }
53    WLOGFI("Set curvedScreenBoundary");
54    curvedScreenBoundary_ = curvedScreenBoundary;
55}
56
57void DisplayCutoutController::SetCutoutSvgPath(DisplayId displayId, const std::string& svgPath)
58{
59    WLOGFI("Set SvgPath: %{public}s", svgPath.c_str());
60    if (svgPaths_.count(displayId) == 1) {
61        svgPaths_[displayId].emplace_back(svgPath);
62    } else {
63        std::vector<std::string> pathVec;
64        pathVec.emplace_back(svgPath);
65        svgPaths_[displayId] = pathVec;
66    }
67    DMRect boundingRect = CalcCutoutBoundingRect(svgPath);
68    if (boundingRects_.count(displayId) == 1) {
69        boundingRects_[displayId].emplace_back(boundingRect);
70    } else {
71        std::vector<DMRect> rectVec;
72        rectVec.emplace_back(boundingRect);
73        boundingRects_[displayId] = rectVec;
74    }
75}
76
77sptr<CutoutInfo> DisplayCutoutController::GetCutoutInfo(DisplayId displayId)
78{
79    WLOGFD("Get Cutout Info");
80    std::vector<DMRect> boundingRects;
81    WaterfallDisplayAreaRects waterfallDisplayAreaRects;
82    if (boundingRects_.count(displayId) == 1) {
83        TransferBoundingRectsByRotation(displayId, boundingRects);
84    }
85    if (displayId == DisplayManagerServiceInner::GetInstance().GetDefaultDisplayId()) {
86        CalcBuiltInDisplayWaterfallRects();
87        waterfallDisplayAreaRects = waterfallDisplayAreaRects_;
88    }
89    sptr<CutoutInfo> cutoutInfo(new CutoutInfo(boundingRects, waterfallDisplayAreaRects));
90    return cutoutInfo;
91}
92
93void DisplayCutoutController::CheckBoundingRectsBoundary(DisplayId displayId, std::vector<DMRect>& boundingRects)
94{
95    sptr<SupportedScreenModes> modes =
96        DisplayManagerServiceInner::GetInstance().GetScreenModesByDisplayId(displayId);
97    if (modes == nullptr) {
98        WLOGFE("DisplayId is invalid");
99        return;
100    }
101    uint32_t displayHeight = modes->height_;
102    uint32_t displayWidth = modes->width_;
103    for (auto iter = boundingRects.begin(); iter != boundingRects.end();) {
104        DMRect boundingRect = *iter;
105        if (boundingRect.posX_ < 0 || boundingRect.posY_ < 0 ||
106            static_cast<int32_t>(boundingRect.width_) + boundingRect.posX_ > static_cast<int32_t>(displayWidth) ||
107            static_cast<int32_t>(boundingRect.height_) + boundingRect.posY_ > static_cast<int32_t>(displayHeight) ||
108            boundingRect.width_ > displayWidth || boundingRect.height_ > displayHeight ||
109            boundingRect.IsUninitializedRect()) {
110            WLOGFE("boundingRect boundary is invalid");
111            iter = boundingRects.erase(iter);
112        } else {
113            iter++;
114        }
115    }
116}
117
118DMRect DisplayCutoutController::CalcCutoutBoundingRect(std::string svgPath)
119{
120    DMRect emptyRect = {0, 0, 0, 0};
121    SkPath skCutoutSvgPath;
122    if (!SkParsePath::FromSVGString(svgPath.c_str(), &skCutoutSvgPath)) {
123        WLOGFE("Parse svg string path failed.");
124        return emptyRect;
125    }
126    SkRect skRect = skCutoutSvgPath.computeTightBounds();
127    if (skRect.isEmpty()) {
128        WLOGFW("Get empty skRect");
129        return emptyRect;
130    }
131    SkIRect skiRect = skRect.roundOut();
132    if (skiRect.isEmpty()) {
133        WLOGFW("Get empty skiRect");
134        return emptyRect;
135    }
136    int32_t left = static_cast<int32_t>(skiRect.left());
137    int32_t top = static_cast<int32_t>(skiRect.top());
138    uint32_t width = static_cast<uint32_t>(skiRect.width());
139    uint32_t height = static_cast<uint32_t>(skiRect.height());
140    WLOGFI("calc rect from path,[%{public}d, %{public}d, %{public}u, %{public}u]", left, top, width, height);
141    DMRect cutoutMinOuterRect = {.posX_ = left, .posY_ = top, .width_ = width, .height_ = height};
142    return cutoutMinOuterRect;
143}
144
145void DisplayCutoutController::CalcBuiltInDisplayWaterfallRects()
146{
147    WaterfallDisplayAreaRects emptyRects = {};
148    if (!isWaterfallDisplay_) {
149        WLOGFI("not waterfall display");
150        waterfallDisplayAreaRects_ = emptyRects;
151        return;
152    }
153    if (curvedScreenBoundary_.empty()) {
154        WLOGFI("curved screen boundary is empty");
155        waterfallDisplayAreaRects_ = emptyRects;
156        return;
157    }
158    uint32_t left = static_cast<uint32_t>(curvedScreenBoundary_[0]);
159    uint32_t top = static_cast<uint32_t>(curvedScreenBoundary_[1]);
160    uint32_t right = static_cast<uint32_t>(curvedScreenBoundary_[2]);
161    uint32_t bottom = static_cast<uint32_t>(curvedScreenBoundary_[3]);
162    if (left == 0 && top == 0 && right == 0 && bottom == 0) {
163        waterfallDisplayAreaRects_ = emptyRects;
164        return;
165    }
166    sptr<SupportedScreenModes> modes =
167        DisplayManagerServiceInner::GetInstance().GetScreenModesByDisplayId(
168            DisplayManagerServiceInner::GetInstance().GetDefaultDisplayId());
169    if (!modes) {
170        WLOGE("support screen modes get failed");
171        waterfallDisplayAreaRects_ = emptyRects;
172        return;
173    }
174    uint32_t displayHeight = modes->height_;
175    uint32_t displayWidth = modes->width_;
176
177    if ((left > displayWidth / 2) || (right > displayWidth / 2) || // invalid if more than 1/2 width
178        (top > displayHeight / 2) || (bottom > displayHeight / 2)) { // invalid if more than 1/2 height
179        WLOGFE("Curved screen boundary data is not valid.");
180        waterfallDisplayAreaRects_ = emptyRects;
181        return;
182    }
183    CalcBuiltInDisplayWaterfallRectsByRotation(
184        DisplayManagerServiceInner::GetInstance().GetDefaultDisplay()->GetRotation(),
185        displayHeight, displayWidth);
186}
187
188void DisplayCutoutController::CalcBuiltInDisplayWaterfallRectsByRotation(
189    Rotation rotation, uint32_t displayHeight, uint32_t displayWidth)
190{
191    uint32_t left = static_cast<uint32_t>(curvedScreenBoundary_[0]);
192    uint32_t top = static_cast<uint32_t>(curvedScreenBoundary_[1]);
193    uint32_t right = static_cast<uint32_t>(curvedScreenBoundary_[2]);
194    uint32_t bottom = static_cast<uint32_t>(curvedScreenBoundary_[3]);
195    switch (rotation) {
196        case Rotation::ROTATION_0: {
197            DMRect leftRect = CreateWaterfallRect(0, 0, left, displayHeight);
198            DMRect topRect = CreateWaterfallRect(0, 0, displayWidth, top);
199            DMRect rightRect = CreateWaterfallRect(displayWidth - right, 0, right, displayHeight);
200            DMRect bottomRect = CreateWaterfallRect(0, displayHeight - bottom, displayWidth, bottom);
201            waterfallDisplayAreaRects_ = WaterfallDisplayAreaRects {leftRect, topRect, rightRect, bottomRect};
202            return;
203        }
204        case Rotation::ROTATION_90: {
205            DMRect leftRect = CreateWaterfallRect(0, 0, bottom, displayWidth);
206            DMRect topRect = CreateWaterfallRect(0, 0, displayHeight, left);
207            DMRect rightRect = CreateWaterfallRect(displayHeight - top, 0, top, displayWidth);
208            DMRect bottomRect = CreateWaterfallRect(0, displayWidth - right, displayHeight, right);
209            waterfallDisplayAreaRects_ = WaterfallDisplayAreaRects {leftRect, topRect, rightRect, bottomRect};
210            return;
211        }
212        case Rotation::ROTATION_180: {
213            DMRect leftRect = CreateWaterfallRect(0, 0, right, displayHeight);
214            DMRect topRect = CreateWaterfallRect(0, 0, bottom, displayWidth);
215            DMRect rightRect = CreateWaterfallRect(displayWidth - left, 0, left, displayHeight);
216            DMRect bottomRect = CreateWaterfallRect(0, displayHeight - top, displayWidth, top);
217            waterfallDisplayAreaRects_ = WaterfallDisplayAreaRects {leftRect, topRect, rightRect, bottomRect};
218            return;
219        }
220        case Rotation::ROTATION_270: {
221            DMRect leftRect = CreateWaterfallRect(0, 0, top, displayWidth);
222            DMRect topRect = CreateWaterfallRect(0, 0, displayHeight, right);
223            DMRect rightRect = CreateWaterfallRect(displayHeight - bottom, 0, bottom, displayWidth);
224            DMRect bottomRect = CreateWaterfallRect(0, displayWidth - left, displayHeight, left);
225            waterfallDisplayAreaRects_ = WaterfallDisplayAreaRects {leftRect, topRect, rightRect, bottomRect};
226            return;
227        }
228        default: {
229        }
230    }
231}
232
233void DisplayCutoutController::TransferBoundingRectsByRotation(DisplayId displayId, std::vector<DMRect>& boundingRects)
234{
235    std::vector<DMRect> resultVec;
236    if (boundingRects_.count(displayId) == 0) {
237        boundingRects = resultVec;
238        return;
239    }
240    std::vector<DMRect> displayBoundingRects = boundingRects_[displayId];
241    if (displayBoundingRects.empty()) {
242        boundingRects = resultVec;
243        return;
244    }
245    sptr<DisplayInfo> displayInfo = DisplayManagerServiceInner::GetInstance().GetDisplayById(displayId);
246    if (!displayInfo) {
247        WLOGFE("display invaild");
248        return;
249    }
250    Rotation currentRotation = displayInfo->GetRotation();
251    CheckBoundingRectsBoundary(displayId, displayBoundingRects);
252    if (currentRotation == Rotation::ROTATION_0) {
253        boundingRects = displayBoundingRects;
254        return;
255    }
256    sptr<SupportedScreenModes> modes =
257        DisplayManagerServiceInner::GetInstance().GetScreenModesByDisplayId(displayId);
258    if (!modes) {
259        WLOGE("support screen modes get failed");
260        return;
261    }
262    uint32_t displayHeight = modes->height_;
263    uint32_t displayWidth = modes->width_;
264
265    switch (currentRotation) {
266        case Rotation::ROTATION_90: {
267            CurrentRotation90(resultVec, displayBoundingRects, displayHeight);
268            break;
269        }
270        case Rotation::ROTATION_180: {
271            CurrentRotation180(resultVec, displayBoundingRects, displayHeight, displayWidth);
272            break;
273        }
274        case Rotation::ROTATION_270: {
275            for (DMRect rect : displayBoundingRects) {
276                resultVec.emplace_back(DMRect {rect.posY_, displayWidth - rect.posX_ - rect.width_,
277                    rect.height_, rect.width_});
278            }
279            break;
280        }
281        default: {
282        }
283    }
284    boundingRects = resultVec;
285}
286
287void DisplayCutoutController::CurrentRotation90(std::vector<DMRect> resultVec,
288    std::vector<DMRect> displayBoundingRects, uint32_t displayHeight)
289{
290    for (DMRect rect : displayBoundingRects) {
291        resultVec.emplace_back(DMRect {.posX_ = displayHeight - rect.posY_ - rect.height_,
292            .posY_ = rect.posX_, .width_ = rect.height_, .height_ = rect.width_});
293    }
294}
295
296void DisplayCutoutController::CurrentRotation180(std::vector<DMRect> resultVec,
297    std::vector<DMRect> displayBoundingRects, uint32_t displayHeight, uint32_t displayWidth)
298{
299    for (DMRect rect : displayBoundingRects) {
300        resultVec.emplace_back(DMRect {displayWidth - rect.posX_ - rect.width_,
301            displayHeight - rect.posY_ - rect.height_, rect.width_, rect.height_});
302    }
303}
304
305DMRect DisplayCutoutController::CreateWaterfallRect(uint32_t left, uint32_t top, uint32_t width, uint32_t height)
306{
307    if (width == 0 || height == 0) {
308        return DMRect {0, 0, 0, 0};
309    }
310    return DMRect {left, top, width, height};
311}
312
313void DisplayCutoutController::SetWaterfallAreaCompressionEnableWhenHorzontal(bool isEnable)
314{
315    isWaterfallAreaCompressionEnableWhenHorizontal_ = isEnable;
316}
317
318void DisplayCutoutController::SetWaterfallAreaCompressionSizeWhenHorizontal(uint32_t size)
319{
320    waterfallAreaCompressionSizeWhenHorizontal_ = size;
321}
322
323bool DisplayCutoutController::IsWaterfallAreaCompressionEnableWhenHorizontal()
324{
325    return isWaterfallDisplay_ && isWaterfallAreaCompressionEnableWhenHorizontal_;
326}
327
328uint32_t DisplayCutoutController::GetWaterfallAreaCompressionSizeWhenHorizontal()
329{
330    if (!isWaterfallDisplay_ || !isWaterfallAreaCompressionEnableWhenHorizontal_) {
331        WLOGFW("Not waterfall display or not enable waterfall compression");
332        return NO_WATERFALL_DISPLAY_COMPRESSION_SIZE;
333    }
334    return waterfallAreaCompressionSizeWhenHorizontal_;
335}
336} // Rosen
337} // OHOS
338