1 /*
2  * Copyright (c) 2021-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 
16 #include "core/pipeline/base/rosen_render_context.h"
17 
18 #include "core/pipeline/base/render_sub_container.h"
19 #include "render_service_client/core/ui/rs_canvas_node.h"
20 #ifndef USE_ROSEN_DRAWING
21 #include "include/core/SkImage.h"
22 #include "include/core/SkPictureRecorder.h"
23 #endif
24 
25 namespace OHOS::Ace {
26 namespace {
27 
28 constexpr int32_t OVERFLOW_PLATFORM_VERSION = 7;
29 
ShouldPaint(const RefPtr<RenderNode> & node)30 inline bool ShouldPaint(const RefPtr<RenderNode>& node)
31 {
32     return node != nullptr && node->GetVisible() && !node->GetHidden();
33 }
34 
35 } // namespace
36 
~RosenRenderContext()37 RosenRenderContext::~RosenRenderContext()
38 {
39     StopRecordingIfNeeded();
40 }
41 
Repaint(const RefPtr<RenderNode> & node)42 void RosenRenderContext::Repaint(const RefPtr<RenderNode>& node)
43 {
44     if (!ShouldPaint(node) || !node->NeedRender() || node->GetRSNode() == nullptr) {
45         return;
46     }
47 
48     auto rsNode = node->GetRSNode();
49     auto offset =
50         node->GetTransitionPaintRect().GetOffset() -
51         Offset(rsNode->GetStagingProperties().GetFrame().x_, rsNode->GetStagingProperties().GetFrame().y_);
52 
53     std::string name = AceType::TypeName(node);
54     if (name != "RosenRenderForm" && name != "RosenRenderPlugin") {
55         InitContext(rsNode, node->GetRectWithShadow(), offset);
56         node->RenderWithContext(*this, offset);
57         UpdateChildren(rsNode);
58     } else {
59         node->RenderWithContext(*this, offset);
60     }
61     StopRecordingIfNeeded();
62 }
63 
PaintChild(const RefPtr<RenderNode> & child,const Offset & offset)64 void RosenRenderContext::PaintChild(const RefPtr<RenderNode>& child, const Offset& offset)
65 {
66     if (!ShouldPaint(child)) {
67         return;
68     }
69     auto pipelineContext = child->GetContext().Upgrade();
70     if (!pipelineContext) {
71         LOGE("pipelineContext is null.");
72         return;
73     }
74     bool canChildOverflow = pipelineContext->GetMinPlatformVersion() >= OVERFLOW_PLATFORM_VERSION;
75     Rect rect = child->GetTransitionPaintRect() + offset;
76     if (!(child->IsPaintOutOfParent() || canChildOverflow) && !estimatedRect_.IsIntersectWith(rect)) {
77 #if defined(PREVIEW)
78         child->ClearAccessibilityRect();
79 #endif
80         return;
81     }
82 
83     auto childRSNode = child->GetRSNode();
84     if (childRSNode && childRSNode != rsNode_) {
85         childNodes_.emplace_back(childRSNode);
86         std::string name = AceType::TypeName(child);
87         if (name != "RosenRenderForm" && name != "RosenRenderPlugin") {
88             if (child->NeedRender()) {
89                 RosenRenderContext context;
90                 auto transparentHole = pipelineContext->GetTransparentHole();
91                 if (transparentHole.IsValid() && child->GetNeedClip()) {
92                     Offset childOffset = rect.GetOffset();
93                     Rect hole = transparentHole - childOffset;
94                     context.SetClipHole(hole);
95                 }
96                 context.Repaint(child);
97             } else {
98                 // No need to repaint, notify to update AccessibilityNode info.
99                 child->NotifyPaintFinish();
100             }
101         }
102         Offset pos = rect.GetOffset();
103         if (name == "RosenRenderPlugin") {
104             auto renderPlugin = AceType::DynamicCast<RenderSubContainer>(child);
105             if (!renderPlugin) {
106                 return;
107             }
108             auto pluginContext = renderPlugin->GetSubPipelineContext();
109             if (!pluginContext) {
110                 return;
111             }
112             auto density = pipelineContext->GetDensity();
113             Offset pluginOffset = {pos.GetX() / density, pos.GetY() / density};
114             pluginContext->SetPluginEventOffset(child->GetGlobalOffset());
115         }
116     } else {
117         child->RenderWithContext(*this, rect.GetOffset());
118     }
119 }
120 
StartRecording()121 void RosenRenderContext::StartRecording()
122 {
123 #ifndef USE_ROSEN_DRAWING
124     recorder_ = new SkPictureRecorder();
125     recordingCanvas_ = recorder_->beginRecording(
126         SkRect::MakeXYWH(estimatedRect_.Left(), estimatedRect_.Top(), estimatedRect_.Width(), estimatedRect_.Height()));
127     if (clipHole_.IsValid()) {
128         recordingCanvas_->save();
129         needRestoreHole_ = true;
130         recordingCanvas_->clipRect(
131             SkRect::MakeXYWH(clipHole_.Left(), clipHole_.Top(), clipHole_.Right(), clipHole_.Bottom()),
132             SkClipOp::kDifference, true);
133     }
134 #else
135     recordingCanvas_ = new Rosen::Drawing::RecordingCanvas(estimatedRect_.Width(), estimatedRect_.Height());
136     if (clipHole_.IsValid()) {
137         recordingCanvas_->Save();
138         needRestoreHole_ = true;
139         recordingCanvas_->ClipRect(
140             RSRect(clipHole_.Left(), clipHole_.Top(), clipHole_.Right(), clipHole_.Bottom()),
141             RSClipOp::DIFFERENCE, true);
142     }
143 #endif
144 }
145 
StopRecordingIfNeeded()146 void RosenRenderContext::StopRecordingIfNeeded()
147 {
148     auto rsCanvasNode = RSNode::ReinterpretCast<Rosen::RSCanvasNode>(rsNode_);
149     if (rosenCanvas_ && rsCanvasNode) {
150         rsCanvasNode->FinishRecording();
151         rosenCanvas_ = nullptr;
152     }
153 
154     if (needRestoreHole_) {
155 #ifndef USE_ROSEN_DRAWING
156         recordingCanvas_->restore();
157 #else
158         recordingCanvas_->Restore();
159 #endif
160         needRestoreHole_ = false;
161     }
162 
163     if (IsRecording()) {
164 #ifndef USE_ROSEN_DRAWING
165         delete recorder_;
166         recorder_ = nullptr;
167         recordingCanvas_ = nullptr;
168 #else
169         delete recordingCanvas_;
170         recordingCanvas_ = nullptr;
171 #endif
172     }
173 }
174 
IsIntersectWith(const RefPtr<RenderNode> & child,Offset & offset)175 bool RosenRenderContext::IsIntersectWith(const RefPtr<RenderNode>& child, Offset& offset)
176 {
177     if (!ShouldPaint(child)) {
178         return false;
179     }
180 
181     Rect rect = child->GetTransitionPaintRect() + offset;
182     if (!estimatedRect_.IsIntersectWith(rect)) {
183 #if defined(PREVIEW)
184         child->ClearAccessibilityRect();
185 #endif
186         return false;
187     }
188 
189     offset = rect.GetOffset();
190     return true;
191 }
192 
InitContext(const std::shared_ptr<RSNode> & rsNode,const Rect & rect,const Offset & initialOffset)193 void RosenRenderContext::InitContext(
194     const std::shared_ptr<RSNode>& rsNode, const Rect& rect, const Offset& initialOffset)
195 {
196     rsNode_ = rsNode;
197     estimatedRect_ = rect + initialOffset;
198     if (rsNode_ == nullptr) {
199         return;
200     }
201     childNodes_.clear();
202     if (auto rsCanvasNode = rsNode_->ReinterpretCastTo<Rosen::RSCanvasNode>()) {
203         rosenCanvas_ = rsCanvasNode->BeginRecording(rsCanvasNode->GetPaintWidth(), rsCanvasNode->GetPaintHeight());
204     }
205 }
206 
207 #ifndef USE_ROSEN_DRAWING
GetCanvas()208 SkCanvas* RosenRenderContext::GetCanvas()
209 #else
210 RSCanvas* RosenRenderContext::GetCanvas()
211 #endif
212 {
213     // if recording, return recording canvas
214     return recordingCanvas_ ? recordingCanvas_ : rosenCanvas_;
215 }
216 
GetRSNode()217 const std::shared_ptr<RSNode>& RosenRenderContext::GetRSNode()
218 {
219     return rsNode_;
220 }
221 
222 #ifndef USE_ROSEN_DRAWING
FinishRecordingAsPicture()223 sk_sp<SkPicture> RosenRenderContext::FinishRecordingAsPicture()
224 {
225     if (!recorder_) {
226         return nullptr;
227     }
228     return recorder_->finishRecordingAsPicture();
229 }
230 
FinishRecordingAsImage()231 sk_sp<SkImage> RosenRenderContext::FinishRecordingAsImage()
232 {
233     if (!recorder_) {
234     return nullptr;
235     }
236     auto picture = recorder_->finishRecordingAsPicture();
237     if (!picture) {
238         return nullptr;
239     }
240     auto image = SkImage::MakeFromPicture(picture, { estimatedRect_.Width(), estimatedRect_.Height() }, nullptr,
241         nullptr, SkImage::BitDepth::kU8, nullptr);
242     return image;
243 }
244 #else
FinishRecordingAsPicture()245 std::shared_ptr<RSPicture> RosenRenderContext::FinishRecordingAsPicture()
246 {
247     LOGE("Drawing is not supported");
248     return nullptr;
249 }
250 
FinishRecordingAsImage()251 std::shared_ptr<RSImage> RosenRenderContext::FinishRecordingAsImage()
252 {
253     LOGE("Drawing is not supported");
254     return nullptr;
255 }
256 #endif
257 
Restore()258 void RosenRenderContext::Restore()
259 {
260     auto canvas = GetCanvas();
261     if (canvas != nullptr) {
262 #ifndef USE_ROSEN_DRAWING
263         canvas->restore();
264 #else
265         canvas->Restore();
266 #endif
267     }
268 }
269 
UpdateChildren(const std::shared_ptr<RSNode> & rsNode)270 void RosenRenderContext::UpdateChildren(const std::shared_ptr<RSNode>& rsNode)
271 {
272     std::vector<OHOS::Rosen::NodeId> childNodeIds;
273     for (auto& child : childNodes_) {
274         if (auto childNode = child.lock()) {
275             childNodeIds.emplace_back(childNode->GetId());
276         }
277     }
278     if (childNodeIds != rsNode->GetChildren()) {
279         rsNode->ClearChildren();
280         for (auto& child : childNodes_) {
281             rsNode->AddChild(child.lock());
282         }
283     }
284 }
285 } // namespace OHOS::Ace
286