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/flutter_render_context.h"
17
18 #include "core/components/plugin/render_plugin.h"
19 #include "core/pipeline/base/render_node.h"
20 #include "core/pipeline/base/render_sub_container.h"
21
22 namespace OHOS::Ace {
23 namespace {
24
25 constexpr int32_t OVERFLOW_PLATFORM_VERSION = 7;
26
ShouldPaint(const RefPtr<RenderNode> & node)27 inline bool ShouldPaint(const RefPtr<RenderNode>& node)
28 {
29 return node != nullptr && node->GetVisible() && !node->GetHidden();
30 }
31
32 } // namespace
33
34 using namespace Flutter;
35
~FlutterRenderContext()36 FlutterRenderContext::~FlutterRenderContext()
37 {
38 StopRecordingIfNeeded();
39 }
40
Repaint(const RefPtr<RenderNode> & node)41 void FlutterRenderContext::Repaint(const RefPtr<RenderNode>& node)
42 {
43 if (!ShouldPaint(node) || !node->NeedRender() || !node->GetRenderLayer()) {
44 return;
45 }
46 InitContext(node->GetRenderLayer(), node->GetRectWithShadow());
47 node->RenderWithContext(*this, Offset::Zero());
48 StopRecordingIfNeeded();
49 }
50
PaintChild(const RefPtr<RenderNode> & child,const Offset & offset)51 void FlutterRenderContext::PaintChild(const RefPtr<RenderNode>& child, const Offset& offset)
52 {
53 if (!ShouldPaint(child)) {
54 return;
55 }
56
57 bool canChildOverflow = false;
58 auto pipeline = child->GetContext().Upgrade();
59 if (pipeline) {
60 canChildOverflow = pipeline->GetMinPlatformVersion() >= OVERFLOW_PLATFORM_VERSION;
61 }
62
63 Rect rect = child->GetTransitionPaintRect() + offset;
64 if (!(child->IsPaintOutOfParent() || canChildOverflow) && !estimatedRect_.IsIntersectWith(rect)) {
65 #if defined(PREVIEW)
66 child->ClearAccessibilityRect();
67 #endif
68 return;
69 }
70
71 if (child->GetRenderLayer()) {
72 StopRecordingIfNeeded();
73 std::string name = AceType::TypeName(child);
74 if (name != "FlutterRenderForm" && name != "FlutterRenderPlugin") {
75 if (child->NeedRender()) {
76 FlutterRenderContext context;
77 auto pipelineContext = child->GetContext().Upgrade();
78 auto transparentHole = pipelineContext->GetTransparentHole();
79 if (transparentHole.IsValid() && child->GetNeedClip()) {
80 Offset childOffset = rect.GetOffset();
81 Rect hole = transparentHole - childOffset;
82 context.SetClipHole(hole);
83 }
84 context.Repaint(child);
85 } else {
86 // No need to repaint, notify to update AccessibilityNode info.
87 child->NotifyPaintFinish();
88 }
89 }
90 // add child layer to parent layer
91 OffsetLayer* layer = CastLayerAs<OffsetLayer>(child->GetRenderLayer());
92 if (!layer) {
93 LOGE("layer is null");
94 return;
95 }
96 Offset pos = rect.GetOffset();
97 if (name != "FlutterRenderForm" && name != "FlutterRenderPlugin") {
98 layer->SetOffset(pos.GetX(), pos.GetY());
99 } else {
100 SetOffSet(child, layer, pos, name);
101 }
102 containerLayer_->AddChildren(AceType::Claim(layer));
103 } else {
104 child->RenderWithContext(*this, rect.GetOffset());
105 }
106 }
107
SetOffSet(const RefPtr<RenderNode> & child,OffsetLayer * layer,const Offset & pos,const std::string & name)108 void FlutterRenderContext::SetOffSet(
109 const RefPtr<RenderNode>& child, OffsetLayer* layer, const Offset& pos, const std::string& name)
110 {
111 if (!child || !layer) {
112 LOGE("child is nullptr, or layer is nullptr");
113 return;
114 }
115
116 auto renderPost = child->GetGlobalOffset();
117 auto context = child->GetContext().Upgrade();
118 if (context) {
119 auto density = context->GetDensity();
120 auto parent = child->GetParent();
121 Offset pluginOffset = { 0, 0 };
122 if (!NearZero(density)) {
123 if (parent.Upgrade() && parent.Upgrade()->GetRenderLayer()) {
124 layer->SetOffset(
125 renderPost.GetX() / density - renderPost.GetX(), renderPost.GetY() / density - renderPost.GetY());
126 pluginOffset = { renderPost.GetX() / density - renderPost.GetX(),
127 renderPost.GetY() / density - renderPost.GetY() };
128 } else {
129 layer->SetOffset(pos.GetX() / density, pos.GetY() / density);
130 pluginOffset = { pos.GetX() / density, pos.GetY() / density };
131 }
132 }
133 // plugin offset
134 if (name == "FlutterRenderPlugin" || name == "FlutterRenderForm") {
135 auto renderPlugin = AceType::DynamicCast<RenderSubContainer>(child);
136 if (!renderPlugin) {
137 return;
138 }
139 auto pluginContext = renderPlugin->GetSubPipelineContext();
140 if (!pluginContext) {
141 return;
142 }
143 pluginContext->SetPluginEventOffset(renderPost);
144 pluginContext->SetPluginOffset(pluginOffset);
145 }
146 }
147 }
148
StartRecording()149 void FlutterRenderContext::StartRecording()
150 {
151 currentLayer_ = AceType::MakeRefPtr<PictureLayer>();
152 recorder_ = flutter::PictureRecorder::Create();
153 canvas_ = flutter::Canvas::Create(
154 recorder_.get(), estimatedRect_.Left(), estimatedRect_.Top(), estimatedRect_.Right(), estimatedRect_.Bottom());
155 if (clipHole_.IsValid()) {
156 canvas_->save();
157 needRestoreHole_ = true;
158 canvas_->clipRect(
159 clipHole_.Left(), clipHole_.Top(), clipHole_.Right(), clipHole_.Bottom(), SkClipOp::kDifference);
160 }
161 containerLayer_->AddChildren(currentLayer_);
162 }
163
StopRecordingIfNeeded()164 void FlutterRenderContext::StopRecordingIfNeeded()
165 {
166 if (!IsRecording()) {
167 return;
168 }
169
170 if (needRestoreHole_) {
171 canvas_->restore();
172 needRestoreHole_ = false;
173 }
174 currentLayer_->SetPicture(recorder_->endRecording());
175 currentLayer_ = nullptr;
176 recorder_ = nullptr;
177 canvas_ = nullptr;
178 }
179
IsIntersectWith(const RefPtr<RenderNode> & child,Offset & offset)180 bool FlutterRenderContext::IsIntersectWith(const RefPtr<RenderNode>& child, Offset& offset)
181 {
182 if (!ShouldPaint(child)) {
183 return false;
184 }
185
186 Rect rect = child->GetTransitionPaintRect() + offset;
187 if (!estimatedRect_.IsIntersectWith(rect)) {
188 #if defined(PREVIEW)
189 child->ClearAccessibilityRect();
190 #endif
191 return false;
192 }
193
194 offset = rect.GetOffset();
195 return true;
196 }
197
InitContext(RenderLayer layer,const Rect & rect)198 void FlutterRenderContext::InitContext(RenderLayer layer, const Rect& rect)
199 {
200 estimatedRect_ = rect;
201 containerLayer_ = CastLayerAs<ContainerLayer>(layer);
202 containerLayer_->RemoveChildren();
203 }
204
GetCanvas()205 flutter::Canvas* FlutterRenderContext::GetCanvas()
206 {
207 if (!IsRecording()) {
208 StartRecording();
209 }
210 return canvas_.get();
211 }
212
Restore()213 void FlutterRenderContext::Restore()
214 {
215 auto canvas = GetCanvas();
216 if (canvas != nullptr) {
217 canvas->restore();
218 }
219 }
220
221 } // namespace OHOS::Ace
222