1 /*
2  * Copyright (c) 2021 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 "frameworks/core/components/svg/render_svg.h"
17 
18 #include "frameworks/core/components/display/render_display.h"
19 #include "frameworks/core/components/transform/render_transform.h"
20 
21 namespace OHOS::Ace {
22 
23 namespace {
24 
25 const char TRANSFORM_ROTATE[] = "rotate";
26 const char TRANSFORM_SCALE[] = "scale";
27 const char TRANSFORM_SKEW[] = "skew";
28 const char TRANSFORM_TRANSLATE[] = "translate";
29 
30 } // namespace
31 
~RenderSvg()32 RenderSvg::~RenderSvg()
33 {
34     svgAnimates_.clear();
35 }
36 
Update(const RefPtr<Component> & component)37 void RenderSvg::Update(const RefPtr<Component>& component)
38 {
39     const RefPtr<SvgComponent> svgComponent = AceType::DynamicCast<SvgComponent>(component);
40     if (!svgComponent) {
41         LOGW("svg component is null");
42         return;
43     }
44     isSvgNode_ = true;
45     x_ = svgComponent->GetX();
46     y_ = svgComponent->GetY();
47     width_ = svgComponent->GetWidth();
48     height_ = svgComponent->GetHeight();
49     viewBox_ = svgComponent->GetViewBox();
50     isRoot_ = svgComponent->IsRoot();
51     autoMirror_ = svgComponent->GetAutoMirror();
52     RenderSvgBase::SetPresentationAttrs(svgComponent->GetDeclaration());
53     AddSvgAnimations(svgComponent);
54     MarkNeedLayout();
55 }
56 
AddSvgAnimations(const RefPtr<SvgComponent> & svgComponent)57 void RenderSvg::AddSvgAnimations(const RefPtr<SvgComponent>& svgComponent)
58 {
59     if (!svgComponent) {
60         LOGW("svg component is null");
61         return;
62     }
63     svgAnimates_.clear();
64     hasUpdated_ = true;
65     const auto& componentChildren = svgComponent->GetChildren();
66     for (const auto& childComponent : componentChildren) {
67         auto svgAnimateComponent = AceType::DynamicCast<SvgAnimate>(childComponent);
68         if (!svgAnimateComponent || svgAnimateComponent->GetSvgAnimateType() == SvgAnimateType::MOTION) {
69             continue;
70         }
71         auto svgAnimate = AceType::MakeRefPtr<SvgAnimate>();
72         svgAnimateComponent->Copy(svgAnimate);
73         svgAnimates_.emplace_back(svgAnimate);
74     }
75 }
76 
PrepareAnimations()77 void RenderSvg::PrepareAnimations()
78 {
79     if (!hasUpdated_) {
80         return;
81     }
82     hasUpdated_ = false;
83     for (const auto& svgAnimate : svgAnimates_) {
84         RenderSvgBase::PreparePropertyAnimation(svgAnimate);
85     }
86 }
87 
PrepareSelfAnimation(const RefPtr<SvgAnimate> & svgAnimate)88 bool RenderSvg::PrepareSelfAnimation(const RefPtr<SvgAnimate>& svgAnimate)
89 {
90     if (OpacityAnimation(svgAnimate)) {
91         return true;
92     }
93     Dimension originalValue;
94     if (!GetProperty(svgAnimate->GetAttributeName(), originalValue)) {
95         return false;
96     }
97     std::function<void(Dimension)> callback;
98     callback = [weak = AceType::WeakClaim(this), attrName = svgAnimate->GetAttributeName()](Dimension value) {
99         auto svg = weak.Upgrade();
100         if (!svg) {
101             LOGE("svg is null");
102             return;
103         }
104         bool ret = svg->SetProperty(attrName, value);
105         if (ret) {
106             svg->MarkNeedLayout(true);
107         }
108     };
109     CreatePropertyAnimation(svgAnimate, originalValue, std::move(callback));
110     return true;
111 }
112 
OpacityAnimation(const RefPtr<SvgAnimate> & svgAnimate)113 bool RenderSvg::OpacityAnimation(const RefPtr<SvgAnimate>& svgAnimate)
114 {
115     if (svgAnimate->GetAttributeName() != ATTR_NAME_OPACITY) {
116         return false;
117     }
118     SetOpacityCallback();
119     if (opacityCallback_) {
120         double originalValue = opacity_ * (1.0 / UINT8_MAX);
121         CreatePropertyAnimation(svgAnimate, originalValue, std::move(opacityCallback_));
122     }
123     return true;
124 }
125 
SetOpacityCallback()126 void RenderSvg::SetOpacityCallback()
127 {
128     int32_t nodeId = GetNodeId();
129     auto parent = GetParent().Upgrade();
130     while (parent) {
131         if (parent->GetNodeId() != nodeId) {
132             break;
133         }
134         auto displayRender = AceType::DynamicCast<RenderDisplay>(parent);
135         if (displayRender) {
136             opacityCallback_ = [weak = AceType::WeakClaim(AceType::RawPtr(displayRender))](double value) {
137                 auto display = weak.Upgrade();
138                 if (!display) {
139                     LOGE("display is null");
140                     return;
141                 }
142                 display->UpdateOpacity(static_cast<uint8_t>(round(value * UINT8_MAX)));
143             };
144             break;
145         }
146         parent = parent->GetParent().Upgrade();
147     }
148 }
149 
SetProperty(const std::string & attrName,const Dimension & value)150 bool RenderSvg::SetProperty(const std::string& attrName, const Dimension& value)
151 {
152     if (attrName == ATTR_NAME_WIDTH) {
153         width_ = value;
154     } else if (attrName == ATTR_NAME_HEIGHT) {
155         height_ = value;
156     } else {
157         LOGE("invalid attrName");
158         return false;
159     }
160     return true;
161 }
162 
GetProperty(const std::string & attrName,Dimension & dimension) const163 bool RenderSvg::GetProperty(const std::string& attrName, Dimension& dimension) const
164 {
165     if (attrName == ATTR_NAME_WIDTH) {
166         dimension = width_;
167     } else if (attrName == ATTR_NAME_HEIGHT) {
168         dimension = height_;
169     } else {
170         LOGE("invalid attrName");
171         return false;
172     }
173     return true;
174 }
175 
PerformLayout()176 void RenderSvg::PerformLayout()
177 {
178     auto context = context_.Upgrade();
179     if (!context) {
180         LOGE("context is null");
181         return;
182     }
183     const auto& children = GetChildren();
184     LayoutParam layoutParam = GetLayoutParam();
185     Size layoutSize;
186     if (LessNotEqual(width_.Value(), 0.0)) {
187         if (layoutParam.GetMaxSize().IsWidthInfinite()) {
188             SetLayoutSize(Size(0.0, 0.0));
189             return;
190         }
191         layoutSize.SetWidth(layoutParam.GetMaxSize().Width());
192     } else {
193         if (isFixSize_) {
194             layoutSize.SetWidth(ConvertDimensionToPx(width_, LengthType::HORIZONTAL, isRoot_));
195         } else {
196             layoutSize.SetWidth(std::clamp(ConvertDimensionToPx(width_, LengthType::HORIZONTAL, isRoot_),
197                 layoutParam.GetMinSize().Width(), layoutParam.GetMaxSize().Width()));
198         }
199     }
200     if (LessNotEqual(height_.Value(), 0.0)) {
201         if (layoutParam.GetMaxSize().IsHeightInfinite()) {
202             SetLayoutSize(Size(0.0, 0.0));
203             return;
204         }
205         layoutSize.SetHeight(layoutParam.GetMaxSize().Height());
206     } else {
207         if (isFixSize_) {
208             layoutSize.SetHeight(ConvertDimensionToPx(height_, LengthType::VERTICAL, isRoot_));
209         } else {
210             layoutSize.SetHeight(std::clamp(ConvertDimensionToPx(height_, LengthType::VERTICAL, isRoot_),
211                 layoutParam.GetMinSize().Height(), layoutParam.GetMaxSize().Height()));
212         }
213     }
214     SetLayoutSize(layoutSize);
215     for (const auto& child : children) {
216         child->Layout(LayoutParam(layoutSize, Size()));
217     }
218     UpdateTransform();
219     PrepareAnimations();
220 }
221 
UpdateTransform()222 void RenderSvg::UpdateTransform()
223 {
224     int32_t nodeId = GetNodeId();
225     RefPtr<RenderTransform> transform = nullptr;
226     auto parent = GetParent().Upgrade();
227     while (parent && parent->GetNodeId() == nodeId) {
228         transform = AceType::DynamicCast<RenderTransform>(parent);
229         if (transform) {
230             break;
231         }
232         parent = parent->GetParent().Upgrade();
233     }
234     if (!transform) {
235         return;
236     }
237     if (!isRoot_ && (GreatNotEqual(x_.Value(), 0.0) || GreatNotEqual(y_.Value(), 0.0))) {
238         transform->Translate(Dimension(ConvertDimensionToPx(x_, LengthType::VERTICAL)),
239             Dimension(ConvertDimensionToPx(y_, LengthType::VERTICAL)));
240     }
241     if (isRoot_ && !NearZero(rootRotate_)) {
242         transform->RotateZ(rootRotate_);
243     }
244     auto& transformAttr = (animateTransformAttrs_.empty()) ? transformAttrs_ : animateTransformAttrs_;
245     for (auto& [type, values] : transformAttr) {
246         if (type == TRANSFORM_TRANSLATE && values.size() >= 2) {
247             transform->Translate(Dimension(values[0]), Dimension(values[1]));
248         } else if (type == TRANSFORM_SCALE && values.size() >= 2) {
249             transform->Scale(values[0], values[1]);
250         } else if (type == TRANSFORM_ROTATE && values.size() >= 1) {
251             transform->RotateZ(values[0]);
252         } else if (type == TRANSFORM_SKEW && values.size() >= 2) {
253             transform->Skew(values[0], values[1]);
254         }
255     }
256 
257     if (isRoot_ && autoMirror_) {
258         auto context = context_.Upgrade();
259         if (context && context->IsRightToLeft()) {
260             Offset center = GetGlobalOffset() + GetLayoutSize() * 0.5;
261             transform->Mirror(center, GetGlobalOffset());
262         }
263     }
264 }
265 
266 } // namespace OHOS::Ace
267