1/*
2 * Copyright (c) 2022 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#include "display_zoom_controller.h"
16#include "display_group_info.h"
17#include "window_helper.h"
18
19namespace OHOS::Rosen {
20namespace {
21constexpr HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HILOG_DOMAIN_WINDOW, "DisplayZoomController"};
22}
23
24void DisplayZoomController::SetAnchorAndScale(int32_t x, int32_t y, float scale)
25{
26    WLOGFD("DisplayZoom: On, anchor x:%{public}d, y:%{public}d, scale:%{public}f", x, y, scale);
27    if (scale <= 0) {
28        return;
29    } else if (zoomInfo_.scale * scale < DISPLAY_ZOOM_MIN_SCALE) {
30        scale = DISPLAY_ZOOM_MIN_SCALE / zoomInfo_.scale;
31    } else if (zoomInfo_.scale * scale > DISPLAY_ZOOM_MAX_SCALE) {
32        scale = DISPLAY_ZOOM_MAX_SCALE / zoomInfo_.scale;
33    }
34    DisplayId displayId = DisplayGroupInfo::GetInstance().GetDefaultDisplayId();
35    sptr<WindowNodeContainer> windowNodeContainer = windowRoot_->GetOrCreateWindowNodeContainer(displayId);
36    if (windowNodeContainer == nullptr) {
37        return;
38    }
39    std::vector<sptr<WindowNode>> windowNodes;
40    windowNodeContainer->TraverseContainer(windowNodes);
41    bool isAlreadyCalcu = false;
42    for (auto& node : windowNodes) {
43        if (displayZoomWindowTypeSkipped_.find(node->GetWindowProperty()->GetWindowType()) !=
44            displayZoomWindowTypeSkipped_.end()) {
45            continue;
46        }
47        Transform zoomTrans;
48        if (!isAlreadyCalcu) {
49            zoomTrans = CalcuZoomTrans(node, {x, y, scale, 0, 0});
50            zoomInfo_.scale *= scale;
51            Rect rect = node->GetWindowRect();
52            zoomInfo_.pivotX = rect.posX_ + zoomTrans.pivotX_ * rect.width_;
53            zoomInfo_.pivotY = rect.posY_ + zoomTrans.pivotY_ * rect.height_;
54            zoomInfo_.translateX = zoomTrans.translateX_;
55            zoomInfo_.translateY = zoomTrans.translateY_;
56            isAlreadyCalcu = true;
57        } else {
58            zoomTrans = CalcuZoomTransByZoomInfo(node);
59        }
60        UpdateClientAndSurfaceZoomInfo(node, zoomTrans);
61    }
62}
63
64void DisplayZoomController::SetAnchorOffset(int32_t deltaX, int32_t deltaY)
65{
66    WLOGFD("DisplayZoom: SetAnchorOffset");
67    DisplayId displayId = DisplayGroupInfo::GetInstance().GetDefaultDisplayId();
68    sptr<WindowNodeContainer> windowNodeContainer = windowRoot_->GetOrCreateWindowNodeContainer(displayId);
69    if (windowNodeContainer == nullptr) {
70        return;
71    }
72    if (!UpdateZoomTranslateInfo(windowNodeContainer, displayId, deltaX, deltaY)) {
73        return;
74    }
75    WindowNodeOperationFunc translateFunc = [this, deltaX, deltaY](sptr<WindowNode> node) {
76        if (displayZoomWindowTypeSkipped_.find(node->GetWindowProperty()->GetWindowType()) !=
77            displayZoomWindowTypeSkipped_.end()) {
78            return false;
79        }
80        Transform zoomTrans = node->GetZoomTransform();
81        zoomTrans.translateX_ += static_cast<float>(deltaX);
82        zoomTrans.translateY_ += static_cast<float>(deltaY);
83        UpdateClientAndSurfaceZoomInfo(node, zoomTrans);
84        return false;
85    };
86    windowNodeContainer->TraverseWindowTree(translateFunc, false);
87}
88
89void DisplayZoomController::OffWindowZoom()
90{
91    WLOGFD("DisplayZoom: Off");
92    DisplayId displayId = DisplayGroupInfo::GetInstance().GetDefaultDisplayId();
93    sptr<WindowNodeContainer> windowNodeContainer = windowRoot_->GetOrCreateWindowNodeContainer(displayId);
94    if (windowNodeContainer == nullptr) {
95        return;
96    }
97    zoomInfo_ = {0, 0, 1.0, 0, 0};
98    std::vector<sptr<WindowNode>> windowNodes;
99    windowNodeContainer->TraverseContainer(windowNodes);
100    for (auto& node : windowNodes) {
101        if (displayZoomWindowTypeSkipped_.find(node->GetWindowProperty()->GetWindowType()) !=
102            displayZoomWindowTypeSkipped_.end()) {
103            continue;
104        }
105        ClearZoomTransformInner(node);
106    }
107}
108
109void DisplayZoomController::UpdateAllWindowsZoomInfo(DisplayId displayId)
110{
111    if (zoomInfo_.scale == DISPLAY_ZOOM_OFF_SCALE) {
112        return;
113    }
114    DisplayId defaultDisplayId = DisplayGroupInfo::GetInstance().GetDefaultDisplayId();
115    if (defaultDisplayId != displayId) {
116        return;
117    }
118    sptr<WindowNodeContainer> windowNodeContainer = windowRoot_->GetOrCreateWindowNodeContainer(displayId);
119    if (windowNodeContainer == nullptr) {
120        return;
121    }
122    int32_t deltaX, deltaY;
123    deltaX = deltaY = 0;
124    if (UpdateZoomTranslateInfo(windowNodeContainer, displayId, deltaX, deltaY)) {
125        WLOGFD("Change ZoomInfo translation, deltaX:%{public}d, deltaY:%{public}d", deltaX, deltaY);
126    }
127    std::vector<sptr<WindowNode>> windowNodes;
128    windowNodeContainer->TraverseContainer(windowNodes);
129    for (auto& node: windowNodes) {
130        HandleUpdateWindowZoomInfo(node);
131    }
132}
133
134void DisplayZoomController::UpdateWindowZoomInfo(uint32_t windowId)
135{
136    if (zoomInfo_.scale == DISPLAY_ZOOM_OFF_SCALE) {
137        return;
138    }
139    auto node = windowRoot_->GetWindowNode(windowId);
140    if (node == nullptr) {
141        return;
142    }
143    if (!node->currentVisibility_) {
144        return;
145    }
146    DisplayId displayId = DisplayGroupInfo::GetInstance().GetDefaultDisplayId();
147    if (node->GetDisplayId() != displayId) {
148        return;
149    }
150    std::vector<sptr<WindowNode>> windowNodes;
151    windowNodes.push_back(node);
152    if (node->GetWindowType() == WindowType::WINDOW_TYPE_DOCK_SLICE) {
153        windowNodes = windowRoot_->GetSplitScreenWindowNodes(node->GetDisplayId());
154    }
155    for (auto& windowNode: windowNodes) {
156        HandleUpdateWindowZoomInfo(windowNode);
157    }
158}
159
160void DisplayZoomController::ClearZoomTransform(std::vector<sptr<WindowNode>> nodes)
161{
162    if (zoomInfo_.scale == DISPLAY_ZOOM_OFF_SCALE) {
163        return;
164    }
165    for (auto& node: nodes) {
166        ClearZoomTransformInner(node);
167    }
168}
169
170void DisplayZoomController::ClearZoomTransformInner(sptr<WindowNode> node)
171{
172    Transform recoverTrans;
173    node->UpdateZoomTransform(recoverTrans, false);
174    auto surfaceNode = node->leashWinSurfaceNode_ ? node->leashWinSurfaceNode_ : node->surfaceNode_;
175    if (!node->GetWindowProperty()->IsAnimateWindow()) {
176        TransformSurfaceNode(surfaceNode, recoverTrans);
177    }
178}
179
180bool DisplayZoomController::UpdateZoomTranslateInfo(sptr<WindowNodeContainer> windowNodeContainer,
181    DisplayId displayId, int32_t& deltaX, int32_t& deltaY)
182{
183    sptr<WindowNode> deskTop = windowNodeContainer->GetDeskTopWindow();
184    if (deskTop == nullptr) {
185        WLOGFE("DisplayZoom: can't find deskTop windowNode");
186        return false;
187    }
188    Transform zoomTrans = deskTop->GetZoomTransform();
189    Rect originalRect = deskTop->GetWindowRect();
190    Rect zoomRect = originalRect;
191    if (zoomTrans != Transform::Identity()) {
192        deskTop->ComputeTransform();
193        zoomRect = WindowHelper::TransformRect(deskTop->GetWindowProperty()->GetTransformMat(), originalRect);
194    }
195    sptr<DisplayInfo> displayInfo = DisplayGroupInfo::GetInstance().GetDisplayInfo(displayId);
196    if (displayInfo == nullptr) {
197        WLOGFE("DisplayZoom: can't get displayInfo");
198        return false;
199    }
200    int32_t deltaXMax = displayInfo->GetOffsetX() - zoomRect.posX_;
201    int32_t deltaXMin = displayInfo->GetOffsetX() + displayInfo->GetWidth() - zoomRect.posX_
202        - static_cast<int32_t>(zoomRect.width_);
203    int32_t deltaYMax = displayInfo->GetOffsetY() - zoomRect.posY_;
204    int32_t deltaYMin = displayInfo->GetOffsetY() + displayInfo->GetHeight() - zoomRect.posY_
205        - static_cast<int32_t>(zoomRect.height_);
206    deltaX = MathHelper::Clamp(deltaX, deltaXMin, deltaXMax);
207    deltaY = MathHelper::Clamp(deltaY, deltaYMin, deltaYMax);
208    if (deltaX == 0 && deltaY == 0) {
209        return false;
210    }
211    zoomInfo_.translateX += deltaX;
212    zoomInfo_.translateY += deltaY;
213    return true;
214}
215
216Transform DisplayZoomController::CalcuAnimateZoomTrans(sptr<WindowNode> node)
217{
218    Rect rect = node->GetWindowRect();
219    if (rect.width_ == 0 || rect.height_ == 0) {
220        return Transform::Identity();
221    }
222    Transform lastZoomTrans = CalcuZoomTransByZoomInfo(node);
223    TransformHelper::Vector3 lastPivotPos = { rect.posX_ + lastZoomTrans.pivotX_ * rect.width_,
224        rect.posY_ + lastZoomTrans.pivotY_ * rect.height_, 0 };
225    TransformHelper::Matrix4 lastWorldMat = TransformHelper::CreateTranslation(-lastPivotPos) *
226        WindowHelper::ComputeWorldTransformMat4(lastZoomTrans) *
227        TransformHelper::CreateTranslation(lastPivotPos);
228
229    Transform animateTrans = node->GetWindowProperty()->GetTransform();
230    if (animateTrans.translateZ_ != 0.f) {
231        node->GetWindowProperty()->ClearTransformZAxisOffset(animateTrans);
232    }
233    TransformHelper::Vector3 animatePivotPos = { rect.posX_ + animateTrans.pivotX_ * rect.width_,
234        rect.posY_ + animateTrans.pivotY_ * rect.height_, 0 };
235    TransformHelper::Matrix4 animateWorldMat = TransformHelper::CreateTranslation(-animatePivotPos) *
236        WindowHelper::ComputeWorldTransformMat4(animateTrans) *
237        TransformHelper::CreateTranslation(animatePivotPos);
238
239    TransformHelper::Matrix4 finalWorldMat = animateWorldMat * lastWorldMat;
240    Transform finalZoomTrans;
241    finalZoomTrans.pivotX_ = (0 - rect.posX_) * 1.0 / rect.width_;
242    finalZoomTrans.pivotY_ = (0 - rect.posY_) * 1.0 / rect.height_;
243    TransformHelper::Vector3 scale = finalWorldMat.GetScale();
244    TransformHelper::Vector3 translation = finalWorldMat.GetTranslation();
245    finalZoomTrans.scaleX_ = scale.x_;
246    finalZoomTrans.scaleY_ = scale.y_;
247    finalZoomTrans.translateX_ = translation.x_;
248    finalZoomTrans.translateY_ = translation.y_;
249    finalZoomTrans.translateZ_ = translation.z_;
250    finalZoomTrans.rotationX_ = animateTrans.rotationX_;
251    finalZoomTrans.rotationY_ = animateTrans.rotationY_;
252    finalZoomTrans.rotationZ_ = animateTrans.rotationZ_;
253
254    return finalZoomTrans;
255}
256
257Transform DisplayZoomController::CalcuZoomTransByZoomInfo(sptr<WindowNode> node)
258{
259    Transform zoomTrans;
260    Rect rect = node->GetWindowRect();
261    if (rect.width_ == 0 || rect.height_ == 0) {
262        return zoomTrans;
263    }
264    zoomTrans.pivotX_ = (zoomInfo_.pivotX - rect.posX_) * 1.0 / rect.width_;
265    zoomTrans.pivotY_ = (zoomInfo_.pivotY - rect.posY_) * 1.0 / rect.height_;
266    zoomTrans.scaleX_ = zoomTrans.scaleY_ = zoomInfo_.scale;
267    zoomTrans.translateX_ = zoomInfo_.translateX;
268    zoomTrans.translateY_ = zoomInfo_.translateY;
269    return zoomTrans;
270}
271
272Transform DisplayZoomController::CalcuZoomTrans(sptr<WindowNode> node, const DisplayZoomInfo& zoomInfo)
273{
274    Rect rect = node->GetWindowRect();
275    if (rect.width_ == 0 || rect.height_ == 0) {
276        return Transform::Identity();
277    }
278    Transform lastZoomTrans = node->GetZoomTransform();
279    TransformHelper::Vector3 lastPivotPos = { rect.posX_ + lastZoomTrans.pivotX_ * rect.width_,
280        rect.posY_ + lastZoomTrans.pivotY_ * rect.height_, 0 };
281    TransformHelper::Matrix4 lastWorldMat = TransformHelper::CreateTranslation(-lastPivotPos) *
282        WindowHelper::ComputeWorldTransformMat4(lastZoomTrans) *
283        TransformHelper::CreateTranslation(lastPivotPos);
284
285    Transform zoomTrans;
286    zoomTrans.scaleX_ = zoomTrans.scaleY_ = zoomInfo.scale;
287    zoomTrans.translateX_ = zoomInfo.translateX;
288    zoomTrans.translateY_ = zoomInfo.translateY;
289    TransformHelper::Vector3 pivotPos = { zoomInfo.pivotX, zoomInfo.pivotY, 0 };
290    TransformHelper::Matrix4 worldMat = TransformHelper::CreateTranslation(-pivotPos) *
291        WindowHelper::ComputeWorldTransformMat4(zoomTrans) *
292        TransformHelper::CreateTranslation(pivotPos);
293
294    TransformHelper::Matrix4 finalWorldMat = lastWorldMat * worldMat;
295    Transform finalZoomTrans;
296    finalZoomTrans.pivotX_ = (0 - rect.posX_) * 1.0 / rect.width_;
297    finalZoomTrans.pivotY_ = (0 - rect.posY_) * 1.0 / rect.height_;
298    TransformHelper::Vector3 scale = finalWorldMat.GetScale();
299    TransformHelper::Vector3 translation = finalWorldMat.GetTranslation();
300    finalZoomTrans.scaleX_ = scale.x_;
301    finalZoomTrans.scaleY_ = scale.y_;
302    finalZoomTrans.translateX_ = translation.x_;
303    finalZoomTrans.translateY_ = translation.y_;
304
305    return finalZoomTrans;
306}
307
308void DisplayZoomController::UpdateClientAndSurfaceZoomInfo(sptr<WindowNode> node, const Transform& zoomTrans)
309{
310    node->UpdateZoomTransform(zoomTrans, true);
311    auto surfaceNode = node->leashWinSurfaceNode_ ? node->leashWinSurfaceNode_ : node->surfaceNode_;
312    if (!node->GetWindowProperty()->IsAnimateWindow()) {
313        TransformSurfaceNode(surfaceNode, zoomTrans);
314    }
315    WLOGFD("%{public}s zoomTrans, pivotX:%{public}f, pivotY:%{public}f, scaleX:%{public}f, scaleY:%{public}f"
316        ", transX:%{public}f, transY:%{public}f, transZ:%{public}f, rotateX:%{public}f, rotateY:%{public}f "
317        "rotateZ:%{public}f", node->GetWindowName().c_str(), zoomTrans.pivotX_, zoomTrans.pivotY_, zoomTrans.scaleX_,
318        zoomTrans.scaleY_, zoomTrans.translateX_, zoomTrans.translateY_, zoomTrans.translateZ_, zoomTrans.rotationX_,
319        zoomTrans.rotationY_, zoomTrans.rotationZ_);
320}
321
322void DisplayZoomController::HandleUpdateWindowZoomInfo(sptr<WindowNode> node)
323{
324    if (displayZoomWindowTypeSkipped_.find(node->GetWindowProperty()->GetWindowType()) !=
325        displayZoomWindowTypeSkipped_.end()) {
326        return;
327    }
328    Transform zoomTrans;
329    if (node->GetWindowProperty()->IsAnimateWindow()) {
330        zoomTrans = CalcuAnimateZoomTrans(node);
331    } else {
332        zoomTrans = CalcuZoomTransByZoomInfo(node);
333    }
334    UpdateClientAndSurfaceZoomInfo(node, zoomTrans);
335}
336
337void DisplayZoomController::TransformSurfaceNode(std::shared_ptr<RSSurfaceNode> surfaceNode, const Transform& trans)
338{
339    if (surfaceNode == nullptr) {
340        return;
341    }
342    surfaceNode->SetPivotX(trans.pivotX_);
343    surfaceNode->SetPivotY(trans.pivotY_);
344    surfaceNode->SetScaleX(trans.scaleX_);
345    surfaceNode->SetScaleY(trans.scaleY_);
346    surfaceNode->SetTranslateX(trans.translateX_);
347    surfaceNode->SetTranslateY(trans.translateY_);
348    surfaceNode->SetTranslateZ(trans.translateZ_);
349    surfaceNode->SetRotationX(trans.rotationX_);
350    surfaceNode->SetRotationY(trans.rotationY_);
351    surfaceNode->SetRotation(trans.rotationZ_);
352}
353}