• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2025 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/components_ng/svg/parse/svg_node.h"
17 
18 #include "include/core/SkClipOp.h"
19 
20 #include "core/components/common/painter/rosen_svg_painter.h"
21 #include "core/components/common/properties/decoration.h"
22 #include "core/components_ng/render/drawing.h"
23 #include "core/components_ng/svg/base/svg_length_scale_rule.h"
24 #include "core/components_ng/svg/parse/svg_animation.h"
25 #include "core/components_ng/svg/parse/svg_attributes_parser.h"
26 #include "core/components_ng/svg/parse/svg_constants.h"
27 #include "core/components_ng/svg/parse/svg_filter.h"
28 #include "core/components_ng/svg/parse/svg_mask.h"
29 #include "core/components_ng/svg/parse/svg_clip_path.h"
30 #include "core/components_ng/svg/parse/svg_gradient.h"
31 #include "core/components_ng/svg/parse/svg_transform.h"
32 #include "core/pipeline_ng/pipeline_context.h"
33 
34 namespace OHOS::Ace::NG {
35 namespace {
36 
37 constexpr size_t SVG_ATTR_ID_FLAG_NUMS = 6;
38 const char VALUE_NONE[] = "none";
39 const char ATTR_NAME_OPACITY[] = "opacity";
40 const char DOM_SVG_SRC_FILL_OPACITY[] = "fill-opacity";
41 const char DOM_SVG_SRC_FILL_RULE[] = "fill-rule";
42 const char DOM_SVG_SRC_STROKE_DASHARRAY[] = "stroke-dasharray";
43 const char DOM_SVG_SRC_STROKE_DASHOFFSET[] = "stroke-dashoffset";
44 const char DOM_SVG_SRC_STROKE_LINECAP[] = "stroke-linecap";
45 const char DOM_SVG_SRC_STROKE_LINEJOIN[] = "stroke-linejoin";
46 const char DOM_SVG_SRC_STROKE_MITERLIMIT[] = "stroke-miterlimit";
47 const char DOM_SVG_SRC_STROKE_OPACITY[] = "stroke-opacity";
48 const char DOM_SVG_SRC_STROKE_WIDTH[] = "stroke-width";
49 const char DOM_SVG_SRC_CLIP_PATH[] = "clip-path";
50 const char DOM_SVG_SRC_CLIP_RULE[] = "clip-rule";
51 const char DOM_SVG_SRC_TRANSFORM_ORIGIN[] = "transform-origin";
52 
ParseIdFromUrl(const std::string & url)53 std::string ParseIdFromUrl(const std::string& url)
54 {
55     if (url.size() > SVG_ATTR_ID_FLAG_NUMS) {
56         std::string::size_type start = url.find("url(#");
57         if (start != std::string::npos) {
58             start += std::strlen("url(#");
59             std::string::size_type end = url.find_first_of(')', start);
60             if (end != std::string::npos) {
61                 return url.substr(start, end - start);
62             }
63         }
64     }
65     return "";
66 }
67 
68 const std::unordered_map<std::string, std::function<Color(SvgBaseAttribute&)>> COLOR_GETTERS = {
69     { ATTR_NAME_FILL,
__anon08a12ca70202() 70         [](SvgBaseAttribute& attr) -> Color { return attr.fillState.GetColor(); } },
71     { ATTR_NAME_STROKE,
__anon08a12ca70302() 72         [](SvgBaseAttribute& attr) -> Color { return attr.strokeState.GetColor(); } },
73 };
74 
75 const std::unordered_map<std::string, std::function<Dimension(SvgBaseAttribute&)>> DIMENSION_GETTERS = {
76     { ATTR_NAME_STROKE_WIDTH,
__anon08a12ca70402() 77         [](SvgBaseAttribute& attr) -> Dimension {
78             return attr.strokeState.GetLineWidth();
79         } },
80 };
81 
82 const std::unordered_map<std::string, std::function<double(SvgBaseAttribute&)>> DOUBLE_GETTERS = {
83     { ATTR_NAME_FILL_OPACITY,
__anon08a12ca70502() 84         [](SvgBaseAttribute& attr) -> double {
85             return attr.fillState.GetOpacity().GetValue();
86         } },
87     { ATTR_NAME_STROKE_OPACITY,
__anon08a12ca70602() 88         [](SvgBaseAttribute& attr) -> double {
89             return attr.strokeState.GetOpacity().GetValue();
90         } },
91     { ATTR_NAME_MITER_LIMIT,
__anon08a12ca70702() 92         [](SvgBaseAttribute& attr) -> double {
93             return attr.strokeState.GetMiterLimit();
94         } },
95     { ATTR_NAME_STROKE_DASH_OFFSET,
__anon08a12ca70802() 96         [](SvgBaseAttribute& attr) -> double {
97             return attr.strokeState.GetLineDash().dashOffset;
98         } },
99     { ATTR_NAME_OPACITY,
__anon08a12ca70902() 100         [](SvgBaseAttribute& attr) -> double {
101             return attr.opacity * (1.0 / UINT8_MAX);
102         } },
103 };
104 
GetNodeIdFromUrl(const std::string & url)105 std::string GetNodeIdFromUrl(const std::string& url)
106 {
107     return std::regex_replace(url, std::regex(R"(^url\(\s*['"]?\s*#([^()]+?)\s*['"]?\s*\)$)"), "$1");
108 }
109 
110 void SetCompatibleFill(const std::string& value, SvgBaseAttribute& attrs)
111 {
112     if (value.find("url(") == 0) {
113         attrs.fillState.SetHref(GetNodeIdFromUrl(value));
114         return;
115     }
116     attrs.fillState.SetColor((value == VALUE_NONE ? Color::TRANSPARENT : SvgAttributesParser::GetColor(value)));
117 }
118 
119 void SetCompatibleStroke(const std::string& value, SvgBaseAttribute& attrs)
120 {
121     if (value.find("url(") == 0) {
122         attrs.strokeState.SetHref(GetNodeIdFromUrl(value));
123         return;
124     }
125     attrs.strokeState.SetColor((value == VALUE_NONE ? Color::TRANSPARENT : SvgAttributesParser::GetColor(value)));
126 }
127 } // namespace
128 
129 uint8_t OpacityDoubleToUint8(double opacity)
130 {
131     return static_cast<uint8_t>(std::round(opacity * UINT8_MAX));
132 }
133 
134 void SvgNode::SetAttr(const std::string& name, const std::string& value)
135 {
136     if (ParseAndSetSpecializedAttr(name, value)) {
137         return;
138     }
139     static const LinearMapNode<void (*)(const std::string&, SvgBaseAttribute&)> SVG_BASE_ATTRS[] = {
140         { DOM_SVG_SRC_CLIP_PATH,
141             [](const std::string& val, SvgBaseAttribute& attrs) {
142                 auto value = StringUtils::TrimStr(val);
143                 if (value.find("url(") == 0) {
144                     auto src = std::regex_replace(value,
145                         std::regex(R"(^url\(\s*['"]?\s*#([^()]+?)\s*['"]?\s*\)$)"), "$1");
146                     attrs.clipState.SetHref(src);
147                 }
148             } },
149         { DOM_SVG_SRC_CLIP_RULE,
150             [](const std::string& val, SvgBaseAttribute& attrs) {
151                 attrs.clipState.SetClipRule(val == "evenodd" ? SvgRuleType::SVG_RULE_EVENODD :
152                     SvgRuleType::SVG_RULE_NONEZERO);
153             } },
154         { DOM_CLIP_PATH,
155             [](const std::string& val, SvgBaseAttribute& attrs) {
156                 auto value = StringUtils::TrimStr(val);
157                 if (value.find("url(") == 0) {
158                     auto src = std::regex_replace(value,
159                         std::regex(R"(^url\(\s*['"]?\s*#([^()]+?)\s*['"]?\s*\)$)"), "$1");
160                     attrs.clipState.SetHref(src);
161                 }
162             } },
163         { SVG_CLIP_PATH_UNITS,
__anon08a12ca70d02() 164             [](const std::string& val, SvgBaseAttribute& attrs) {
165                 attrs.clipState.SetClipPathUnits((val == "objectBoundingBox") ?
166                     SvgLengthScaleUnit::OBJECT_BOUNDING_BOX : SvgLengthScaleUnit::USER_SPACE_ON_USE);
167             } },
168         { SVG_CLIP_RULE,
__anon08a12ca70e02() 169             [](const std::string& val, SvgBaseAttribute& attrs) {
170                 attrs.clipState.SetClipRule(val == "evenodd" ? SvgRuleType::SVG_RULE_EVENODD :
171                     SvgRuleType::SVG_RULE_NONEZERO);
172             } },
173         { SVG_FILL,
__anon08a12ca70f02() 174             [](const std::string& val, SvgBaseAttribute& attrs) {
175                 auto value = StringUtils::TrimStr(val);
176                 if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_FOURTEEN)) {
177                     SetCompatibleFill(value, attrs);
178                     return;
179                 }
180                 Color color;
181                 if (val == VALUE_NONE || SvgAttributesParser::ParseColor(value, color)) {
182                     attrs.fillState.SetColor((val == VALUE_NONE ? Color::TRANSPARENT : color));
183                     attrs.fillState.SetIsFillNone(val == VALUE_NONE);
184                     return;
185                 }
186                 if (value.find("url(") == 0) {
187                     auto src = std::regex_replace(value,
188                         std::regex(R"(^url\(\s*['"]?\s*#([^()]+?)\s*['"]?\s*\)$)"), "$1");
189                     attrs.fillState.SetHref(src);
190                 }
191             } },
192         { DOM_SVG_SRC_FILL_OPACITY,
193             [](const std::string& val, SvgBaseAttribute& attrs) {
194                 attrs.fillState.SetOpacity(std::clamp(StringUtils::StringToDouble(val), 0.0, 1.0));
195             } },
196         { DOM_SVG_SRC_FILL_RULE,
197             [](const std::string& val, SvgBaseAttribute& attrs) {
198                 attrs.fillState.SetFillRule(val);
199             } },
200         { SVG_FILL_OPACITY,
201             [](const std::string& val, SvgBaseAttribute& attrs) {
202                 attrs.fillState.SetOpacity(std::clamp(StringUtils::StringToDouble(val), 0.0, 1.0));
203             } },
204         { SVG_FILL_RULE,
205             [](const std::string& val, SvgBaseAttribute& attrs) {
206                 attrs.fillState.SetFillRule(val);
207             } },
208         { SVG_FILTER,
209             [](const std::string& val, SvgBaseAttribute& attrs) {
210                 attrs.filterId = val;
211             } },
212         { SVG_FONT_SIZE,
213             [](const std::string& val, SvgBaseAttribute& attrs) {
214                 Dimension fontSize = StringUtils::StringToDimension(val);
215                 if (GreatOrEqual(fontSize.Value(), 0.0)) {
216                     attrs.textStyle.SetFontSize(fontSize);
217                 }
218             } },
219         { SVG_HREF,
220             [](const std::string& val, SvgBaseAttribute& attrs) {
221                 auto value = StringUtils::TrimStr(val);
222                 if (value.substr(0, 1) == "#") {
223                     attrs.href = value.substr(1);
224                 }
225             } },
226         { SVG_MASK,
227             [](const std::string& val, SvgBaseAttribute& attrs) {
228                 attrs.maskId = val;
229             } },
230         { SVG_OPACITY,
231             [](const std::string& val, SvgBaseAttribute& attrs) {
232                 attrs.hasOpacity = true;
233                 attrs.opacity = std::clamp(StringUtils::StringToDouble(val), 0.0, 1.0);
234             } },
235         { SVG_PATTERN_TRANSFORM,
236             [](const std::string& val, SvgBaseAttribute& attrs) {
237                 attrs.transform = val;
238             } },
239         { SVG_STROKE,
240             [](const std::string& val, SvgBaseAttribute& attrs) {
241                 auto value = StringUtils::TrimStr(val);
242                 if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_FOURTEEN)) {
243                     SetCompatibleStroke(value, attrs);
244                     return;
245                 }
246                 Color color;
247                 if (val == VALUE_NONE || SvgAttributesParser::ParseColor(value, color)) {
248                     attrs.strokeState.SetColor((val == VALUE_NONE ? Color::TRANSPARENT : color));
249                     return;
250                 }
251                 if (value.find("url(") == 0) {
252                     auto src = std::regex_replace(value,
253                         std::regex(R"(^url\(\s*['"]?\s*#([^()]+?)\s*['"]?\s*\)$)"), "$1");
254                     attrs.strokeState.SetHref(src);
255                 }
256             } },
257         { DOM_SVG_SRC_STROKE_DASHARRAY,
258             [](const std::string& val, SvgBaseAttribute& attrs) {
259                 if (val.empty()) {
260                     return;
261                 }
262                 std::vector<double> lineDashVector;
263                 std::string handledStr = StringUtils::ReplaceChar(val, ',', ' ');
264                 StringUtils::StringSplitter(handledStr, ' ', lineDashVector);
265                 attrs.strokeState.SetLineDash(lineDashVector);
266             } },
267         { DOM_SVG_SRC_STROKE_DASHOFFSET,
268             [](const std::string& val, SvgBaseAttribute& attrs) {
269                 attrs.strokeState.SetLineDashOffset(StringUtils::StringToDouble(val));
270             } },
271         { DOM_SVG_SRC_STROKE_LINECAP,
272             [](const std::string& val, SvgBaseAttribute& attrs) {
273                 attrs.strokeState.SetLineCap(SvgAttributesParser::GetLineCapStyle(val));
274             } },
275         { DOM_SVG_SRC_STROKE_LINEJOIN,
276             [](const std::string& val, SvgBaseAttribute& attrs) {
277                 attrs.strokeState.SetLineJoin(SvgAttributesParser::GetLineJoinStyle(val));
278             } },
279         { DOM_SVG_SRC_STROKE_MITERLIMIT,
280             [](const std::string& val, SvgBaseAttribute& attrs) {
281                 double miterLimit = StringUtils::StringToDouble(val);
282                 if (GreatOrEqual(miterLimit, 1.0)) {
283                     attrs.strokeState.SetMiterLimit(miterLimit);
284                 }
285             } },
286         { DOM_SVG_SRC_STROKE_OPACITY,
287             [](const std::string& val, SvgBaseAttribute& attrs) {
288                 attrs.strokeState.SetOpacity(std::clamp(StringUtils::StringToDouble(val), 0.0, 1.0));
289             } },
290         { DOM_SVG_SRC_STROKE_WIDTH,
291             [](const std::string& val, SvgBaseAttribute& attrs) {
292                 Dimension lineWidth = StringUtils::StringToDimension(val);
293                 if (GreatOrEqual(lineWidth.Value(), 0.0)) {
294                     attrs.strokeState.SetLineWidth(lineWidth);
295                 }
296             } },
297         { SVG_STROKE_DASHARRAY,
298             [](const std::string& val, SvgBaseAttribute& attrs) {
299                 if (val.empty()) {
300                     return;
301                 }
302                 std::vector<double> lineDashVector;
303                 StringUtils::StringSplitter(val, ' ', lineDashVector);
304                 attrs.strokeState.SetLineDash(lineDashVector);
305             } },
306         { SVG_STROKE_DASHOFFSET,
307             [](const std::string& val, SvgBaseAttribute& attrs) {
308                 attrs.strokeState.SetLineDashOffset(StringUtils::StringToDouble(val));
309             } },
310         { SVG_STROKE_LINECAP,
311             [](const std::string& val, SvgBaseAttribute& attrs) {
312                 attrs.strokeState.SetLineCap(SvgAttributesParser::GetLineCapStyle(val));
313             } },
314         { SVG_STROKE_LINEJOIN,
315             [](const std::string& val, SvgBaseAttribute& attrs) {
316                 attrs.strokeState.SetLineJoin(SvgAttributesParser::GetLineJoinStyle(val));
317             } },
318         { SVG_STROKE_MITERLIMIT,
319             [](const std::string& val, SvgBaseAttribute& attrs) {
320                 double miterLimit = StringUtils::StringToDouble(val);
321                 if (GreatOrEqual(miterLimit, 1.0)) {
322                     attrs.strokeState.SetMiterLimit(miterLimit);
323                 }
324             } },
325         { SVG_STROKE_OPACITY,
326             [](const std::string& val, SvgBaseAttribute& attrs) {
327                 attrs.strokeState.SetOpacity(std::clamp(StringUtils::StringToDouble(val), 0.0, 1.0));
328             } },
329         { SVG_STROKE_WIDTH,
330             [](const std::string& val, SvgBaseAttribute& attrs) {
331                 Dimension lineWidth = StringUtils::StringToDimension(val);
332                 if (GreatOrEqual(lineWidth.Value(), 0.0)) {
333                     attrs.strokeState.SetLineWidth(lineWidth);
334                 }
335             } },
336         { SVG_TRANSFORM,
337             [](const std::string& val, SvgBaseAttribute& attrs) {
338                 attrs.transform = val;
339                 attrs.transformVec = SvgAttributesParser::GetTransformInfo(val);
340                 if (attrs.transformVec.empty()) {
341                     TAG_LOGW(AceLogTag::ACE_IMAGE, "Set transformVec failed. transform:[%{public}s]", val.c_str());
342                 }
343             } },
344         { DOM_SVG_SRC_TRANSFORM_ORIGIN,
345             [](const std::string& val, SvgBaseAttribute& attrs) {
346                 attrs.transformOrigin = SvgAttributesParser::GetTransformOrigin(val);
347             } },
348         { SVG_XLINK_HREF,
349             [](const std::string& val, SvgBaseAttribute& attrs) {
350                 auto value = StringUtils::TrimStr(val);
351                 if (value.substr(0, 1) == "#") {
352                     attrs.href = value.substr(1);
353                 }
354             } }
355     };
356     auto attrIter = BinarySearchFindIndex(SVG_BASE_ATTRS, ArraySize(SVG_BASE_ATTRS), name.c_str());
357     if (attrIter != -1) {
358         SVG_BASE_ATTRS[attrIter].value(value, attributes_);
359     }
360 }
361 
362 RSPath SvgNode::AsRSPath(const Size& viewPort) const
363 {
364     return {};
365 }
366 
367 void SvgNode::ProcessSvgStyle(RefPtr<SvgNode> svgNode, const SvgBaseAttribute& attr)
368 {
369     svgNode->InheritAttr(attr);
370     auto& svgAttributes = svgNode->attributes_;
371     if (svgNode->hrefFill_) {
372         auto href = svgAttributes.fillState.GetHref();
373         if (!href.empty()) {
374             auto gradient = svgNode->GetGradient(href);
375             if (gradient) {
376                 svgAttributes.fillState.SetGradient(gradient.value());
377             }
378         }
379         href = svgAttributes.strokeState.GetHref();
380         if (!href.empty()) {
381             auto gradient = svgNode->GetGradient(href);
382             if (gradient) {
383                 svgAttributes.strokeState.SetGradient(gradient.value());
384             }
385         }
386     }
387     if (svgNode->hrefRender_) {
388         svgNode->hrefClipPath_ = svgAttributes.clipState.GetHref();
389         svgNode->opacity_ = OpacityDoubleToUint8(svgAttributes.opacity);
390         svgNode->transform_ = svgAttributes.transform;
391         svgNode->hrefMaskId_ = ParseIdFromUrl(svgAttributes.maskId);
392         svgNode->hrefFilterId_ = ParseIdFromUrl(svgAttributes.filterId);
393     }
394     svgNode->OnInitStyle();
395 }
396 
397 void SvgNode::ProcessChildAnimations(const RefPtr<SvgNode>& currentSvgNode)
398 {
399     for (auto& child : currentSvgNode->children_) {
400         auto svgAnimate = DynamicCast<SvgAnimation>(child);
401         if (svgAnimate) {
402             svgAnimate->UpdateAttr();
403             currentSvgNode->PrepareAnimation(svgAnimate);
404         }
405     }
406 }
407 
408 bool SvgNode::ProcessChildStyle(SvgInitStyleProcessInfo& currentSvgNodeInfo,
409     std::stack<std::pair<SvgInitStyleProcessInfo, const SvgBaseAttribute*>>& initStyleTaskSt)
410 {
411     auto currentSvgNode = currentSvgNodeInfo.svgNode;
412     if (currentSvgNode->passStyle_ && currentSvgNodeInfo.childIndex < currentSvgNode->children_.size()) {
413         auto child = currentSvgNode->children_[currentSvgNodeInfo.childIndex];
414         if (child) {
415             // pass down style only if child inheritStyle_ is true
416             initStyleTaskSt.emplace(
417                 child, (child->inheritStyle_) ? &(currentSvgNode->attributes_) : nullptr);
418         }
419         ++currentSvgNodeInfo.childIndex;
420         return false;
421     }
422     return true;
423 }
424 
425 void SvgNode::InitStyleDfs(const WeakPtr<SvgNode>& root, const SvgBaseAttribute& attr)
426 {
427     auto parentNode = root.Upgrade();
428     if (!parentNode) {
429         return;
430     }
431     std::stack<std::pair<SvgInitStyleProcessInfo, const SvgBaseAttribute*>> initStyleTaskSt;
432     initStyleTaskSt.emplace(parentNode, &attr);
433     while (!initStyleTaskSt.empty()) {
434         auto& [currentSvgNodeInfo, currentAttr] = initStyleTaskSt.top();
435         if (currentSvgNodeInfo.childIndex == 0) {
436             currentSvgNodeInfo.svgNode->ProcessSvgStyle(
437                 currentSvgNodeInfo.svgNode, currentAttr ? *currentAttr : SvgBaseAttribute());
438         }
439         if (currentSvgNodeInfo.svgNode->ProcessChildStyle(currentSvgNodeInfo, initStyleTaskSt)) {
440             currentSvgNodeInfo.svgNode->ProcessChildAnimations(currentSvgNodeInfo.svgNode);
441             initStyleTaskSt.pop();
442         }
443     }
444 }
445 
446 void SvgNode::InitStyle(const SvgBaseAttribute& attr)
447 {
448     InitStyleDfs(WeakClaim(this), attr);
449 }
450 
451 void SvgNode::Draw(RSCanvas& canvas, const Size& viewPort, const std::optional<Color>& color)
452 {
453     if (isDrawing_) {
454         TAG_LOGW(AceLogTag::ACE_IMAGE,
455             "The current node is already in the process of being drawn in the SVG rendering flow.");
456         return;
457     }
458     if (!OnCanvas(canvas)) {
459         TAG_LOGW(AceLogTag::ACE_IMAGE, "Svg Draw failed(Reason: Canvas is null).");
460         return;
461     }
462     isDrawing_ = true;
463     // mask and filter create extra layers, need to record initial layer count
464     auto count = rsCanvas_->GetSaveCount();
465     rsCanvas_->Save();
466     if (!hrefClipPath_.empty()) {
467         OnClipPath(canvas, viewPort);
468     } else if (isRootNode_) {
469         AdjustContentAreaByViewBox(canvas, viewPort);
470     }
471     if (!transform_.empty() || !animateTransform_.empty()) {
472         OnTransform(canvas, viewPort);
473     }
474     if (!hrefMaskId_.empty()) {
475         OnMask(canvas, viewPort);
476     }
477     if (!hrefFilterId_.empty()) {
478         OnFilter(canvas, viewPort);
479     }
480 
481     OnDraw(canvas, viewPort, color);
482     OnDrawTraversed(canvas, viewPort, color);
483     rsCanvas_->RestoreToCount(count);
484     isDrawing_ = false; // end the drawing process.
485 }
486 
487 void SvgNode::Draw(RSCanvas& canvas, const SvgLengthScaleRule& lengthRule)
488 {
489     // mask and filter create extra layers, need to record initial layer count
490     auto count = canvas.GetSaveCount();
491     if (isDrawing_) {
492         TAG_LOGW(AceLogTag::ACE_IMAGE,
493             "The current node is already in the process of being drawn in the SVG rendering flow.");
494         return;
495     }
496     canvas.Save();
497     isDrawing_ = true;
498     auto rsBounds = AsPath(lengthRule).GetBounds();
499     Rect containerRect(rsBounds.GetLeft(), rsBounds.GetTop(), rsBounds.GetWidth(), rsBounds.GetHeight());
500     // rect use rsBounds, other use lengthRule containerRect
501     if (LessOrEqual(rsBounds.GetWidth(), 0.0f) || LessOrEqual(rsBounds.GetHeight(), 0.0f)) {
502         containerRect = lengthRule.GetContainerRect();
503     }
504     SvgCoordinateSystemContext svgCoordinateSystemContext(containerRect, lengthRule.GetViewPort());
505     TAG_LOGD(AceLogTag::ACE_IMAGE, "l:%{public}lf, t:%{public}lf, r:%{public}lf, b:%{public}lf, units:%{public}d",
506         rsBounds.GetLeft(), rsBounds.GetTop(), rsBounds.GetRight(), rsBounds.GetBottom(),
507         (int)lengthRule.GetLengthScaleUnit());
508     if (!hrefClipPath_.empty()) {
509         OnClipPath(canvas, svgCoordinateSystemContext);
510     } else if (isRootNode_) {
511         auto svgContext = svgContext_.Upgrade();
512         if (svgContext) {
513             AdjustContentAreaByViewBox(canvas, svgContext->GetViewPort());
514         }
515     }
516     if (!transform_.empty() || !animateTransform_.empty()) {
517         OnTransform(canvas, lengthRule);
518     }
519     if (!hrefMaskId_.empty()) {
520         OnMask(canvas, svgCoordinateSystemContext);
521     }
522     if (!hrefFilterId_.empty()) {
523         OnFilter(canvas, svgCoordinateSystemContext);
524     }
525 
526     OnDraw(canvas, lengthRule);
527     OnDrawTraversed(canvas, lengthRule);
528     canvas.RestoreToCount(count);
529     isDrawing_ = false;
530 }
531 
532 void SvgNode::OnDrawTraversed(RSCanvas& canvas, const Size& viewPort, const std::optional<Color>& color)
533 {
534     auto smoothEdge = GetSmoothEdge();
535     auto colorFilter = GetColorFilter();
536     for (auto& node : children_) {
537         if (node && node->drawTraversed_) {
538             if (GreatNotEqual(smoothEdge, 0.0f)) {
539                 node->SetSmoothEdge(smoothEdge);
540             }
541             node->SetColorFilter(colorFilter);
542             node->Draw(canvas, viewPort, color);
543         }
544     }
545 }
546 
547 void SvgNode::OnDrawTraversed(RSCanvas& canvas, const SvgLengthScaleRule& lengthRule)
548 {
549     auto smoothEdge = GetSmoothEdge();
550     auto colorFilter = GetColorFilter();
551     for (auto& node : children_) {
552         if (node && node->drawTraversed_) {
553             if (GreatNotEqual(smoothEdge, 0.0f)) {
554                 node->SetSmoothEdge(smoothEdge);
555             }
556             node->SetColorFilter(colorFilter);
557             node->Draw(canvas, lengthRule);
558         }
559     }
560 }
561 
562 bool SvgNode::OnCanvas(RSCanvas& canvas)
563 {
564     rsCanvas_ = &canvas;
565     return true;
566 }
567 
568 void SvgNode::OnClipPath(RSCanvas& canvas, const Size& viewPort)
569 {
570     auto svgContext = svgContext_.Upgrade();
571     CHECK_NULL_VOID(svgContext);
572     auto refSvgNode = svgContext->GetSvgNodeById(hrefClipPath_);
573     CHECK_NULL_VOID(refSvgNode);
574     auto clipPath = refSvgNode->AsPath(viewPort);
575     if (!clipPath.IsValid()) {
576         LOGW("OnClipPath abandon, clipPath is empty");
577         return;
578     }
579     rsCanvas_->ClipPath(clipPath, RSClipOp::INTERSECT, true);
580 }
581 
582 void SvgNode::OnClipPath(RSCanvas& canvas, const SvgCoordinateSystemContext& svgCoordinateSystemContext)
583 {
584     auto svgContext = svgContext_.Upgrade();
585     CHECK_NULL_VOID(svgContext);
586     auto refSvgNode = svgContext->GetSvgNodeById(hrefClipPath_);
587     CHECK_NULL_VOID(refSvgNode);
588     if (!AceType::InstanceOf<SvgClipPath>(refSvgNode)) {
589         return;
590     }
591     refSvgNode->OnClipEffect(canvas, svgCoordinateSystemContext);
592 }
593 
594 void SvgNode::OnFilter(RSCanvas& canvas, const Size& viewPort)
595 {
596     if (!AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
597         return;
598     }
599     auto svgContext = svgContext_.Upgrade();
600     CHECK_NULL_VOID(svgContext);
601     auto refFilter = svgContext->GetSvgNodeById(hrefFilterId_);
602     CHECK_NULL_VOID(refFilter);
603     auto effectPath = AsPath(viewPort);
604     auto bounds = effectPath.GetBounds();
605     refFilter->SetEffectFilterArea({
606         useOffsetX_ + bounds.GetLeft(), useOffsetY_ + bounds.GetTop(),
607         bounds.GetWidth(), bounds.GetHeight()
608     });
609     refFilter->Draw(canvas, viewPort, std::nullopt);
610     return;
611 }
612 
613 void SvgNode::OnFilter(RSCanvas& canvas, const SvgCoordinateSystemContext& svgCoordinateSystemContext)
614 {
615     auto svgContext = svgContext_.Upgrade();
616     CHECK_NULL_VOID(svgContext);
617     auto refFilter = svgContext->GetSvgNodeById(hrefFilterId_);
618     CHECK_NULL_VOID(refFilter);
619     if (!AceType::InstanceOf<SvgFilter>(refFilter)) {
620         return;
621     }
622     refFilter->OnFilterEffect(canvas, svgCoordinateSystemContext, useOffsetX_, useOffsetY_);
623 }
624 
625 void SvgNode::OnMask(RSCanvas& canvas, const Size& viewPort)
626 {
627     auto svgContext = svgContext_.Upgrade();
628     CHECK_NULL_VOID(svgContext);
629     auto refMask = svgContext->GetSvgNodeById(hrefMaskId_);
630     CHECK_NULL_VOID(refMask);
631     refMask->Draw(canvas, viewPort, std::nullopt);
632     return;
633 }
634 
635 void SvgNode::OnMask(RSCanvas& canvas, const SvgCoordinateSystemContext& svgCoordinateSystemContext)
636 {
637     auto svgContext = svgContext_.Upgrade();
638     CHECK_NULL_VOID(svgContext);
639     auto refMask = svgContext->GetSvgNodeById(hrefMaskId_);
640     CHECK_NULL_VOID(refMask);
641     if (!AceType::InstanceOf<SvgMask>(refMask)) {
642         return;
643     }
644     refMask->OnMaskEffect(canvas, svgCoordinateSystemContext);
645 }
646 
647 void SvgNode::OnTransform(RSCanvas& canvas, const Size& viewPort)
648 {
649     auto matrix = (animateTransform_.empty()) ? SvgTransform::CreateMatrix4(transform_)
650                                               : SvgTransform::CreateMatrixFromMap(animateTransform_);
651     canvas.ConcatMatrix(RosenSvgPainter::ToDrawingMatrix(matrix));
652 }
653 
654 void SvgNode::OnTransform(RSCanvas& canvas, const SvgLengthScaleRule& lengthRule)
655 {
656     auto containerRect = lengthRule.GetContainerRect();
657     Offset globalPivot = CalcGlobalPivot(attributes_.transformOrigin, containerRect);
658     auto matrix = (animateTransform_.empty()) ? NGSvgTransform::CreateMatrix4(attributes_.transformVec, globalPivot)
659                                               : SvgTransform::CreateMatrixFromMap(animateTransform_);
660     auto svgContext = svgContext_.Upgrade();
661     if (svgContext != nullptr && !attributes_.href.empty() &&
662         lengthRule.GetLengthScaleUnit() == SvgLengthScaleUnit::OBJECT_BOUNDING_BOX) {
663         auto refSvgNode = svgContext->GetSvgNodeById(attributes_.href);
664         if (refSvgNode != nullptr) {
665             auto scaleX = matrix.GetScaleX();
666             auto scaleY = matrix.GetScaleY();
667             matrix.SetScale(scaleX * containerRect.Width(), scaleY * containerRect.Height(), 1.0);
668         }
669     }
670     canvas.ConcatMatrix(RosenSvgPainter::ToDrawingMatrix(matrix));
671 }
672 
673 float SvgNode::GetRegionLength(Dimension origin, const SvgLengthScaleRule& rule, SvgLengthType lengthType)
674 {
675     float length = 0.0f;
676     switch (lengthType) {
677         case SvgLengthType::HORIZONTAL:
678             length = (rule.GetLengthScaleUnit() == SvgLengthScaleUnit::USER_SPACE_ON_USE) ?
679                      ConvertDimensionToPx(origin, rule.GetViewPort().Width()) :
680                      origin.Value() * rule.GetContainerRect().Width();
681             break;
682         case SvgLengthType::VERTICAL:
683             length = (rule.GetLengthScaleUnit() == SvgLengthScaleUnit::USER_SPACE_ON_USE) ?
684                      ConvertDimensionToPx(origin, rule.GetViewPort().Height()) :
685                      origin.Value() * rule.GetContainerRect().Height();
686             break;
687         case SvgLengthType::OTHER:
688             auto width = rule.GetContainerRect().Width();
689             auto height = rule.GetContainerRect().Height();
690             auto baseLength = std::sqrt(width * width + height * height) / std::sqrt(2.0f);
691             length = rule.GetLengthScaleUnit() == SvgLengthScaleUnit::USER_SPACE_ON_USE ?
692                 ConvertDimensionToPx(origin, baseLength) : origin.Value() * baseLength;
693             break;
694     }
695     return length;
696 }
697 
698 float SvgNode::GetRegionPosition(Dimension origin, const SvgLengthScaleRule& rule,
699     SvgLengthType lengthType)
700 {
701     auto position = 0.0f;
702     switch (lengthType) {
703         case SvgLengthType::HORIZONTAL:
704             position = (rule.GetLengthScaleUnit() == SvgLengthScaleUnit::USER_SPACE_ON_USE) ?
705                      ConvertDimensionToPx(origin, rule.GetViewPort().Width()) :
706                      origin.Value() * rule.GetContainerRect().Width() + rule.GetContainerRect().Left();
707             break;
708         case SvgLengthType::VERTICAL:
709             position = (rule.GetLengthScaleUnit() == SvgLengthScaleUnit::USER_SPACE_ON_USE) ?
710                      ConvertDimensionToPx(origin, rule.GetViewPort().Height()) :
711                      origin.Value() * rule.GetContainerRect().Height() + rule.GetContainerRect().Top();
712             break;
713         default:
714             break;
715     }
716     return position;
717 }
718 
719 float SvgNode::GetMeasuredLength(Dimension origin, const SvgLengthScaleRule& rule,
720     SvgLengthType lengthType)
721 {
722     float ContainerLength = 0.0f;
723     switch (lengthType) {
724         case SvgLengthType::HORIZONTAL:
725             ContainerLength = rule.GetContainerRect().Width();
726             break;
727         case SvgLengthType::VERTICAL:
728             ContainerLength = rule.GetContainerRect().Height();
729             break;
730         /*using the original definition, radius*/
731         case SvgLengthType::OTHER:
732             ContainerLength = std::min(rule.GetContainerRect().Width(), rule.GetContainerRect().Height());
733             break;
734     }
735     auto length = ConvertDimensionToPx(origin, rule.GetViewPort(), lengthType);
736     if (rule.GetLengthScaleUnit() == SvgLengthScaleUnit::OBJECT_BOUNDING_BOX) {
737         length *= ContainerLength;
738     }
739     return length;
740 }
741 
742 float SvgNode::GetMeasuredPosition(Dimension origin, const SvgLengthScaleRule& rule,
743     SvgLengthType lengthType)
744 {
745     float offset = 0.0f;
746     float ContainerLength = 0.0f;
747     switch (lengthType) {
748         case SvgLengthType::HORIZONTAL:
749             ContainerLength = rule.GetContainerRect().Width();
750             offset = rule.GetContainerRect().Left();
751             break;
752         case SvgLengthType::VERTICAL:
753             ContainerLength = rule.GetContainerRect().Height();
754             offset = rule.GetContainerRect().Top();
755             break;
756         default:
757             return 0.0f;
758     }
759     auto position = ConvertDimensionToPx(origin, rule.GetViewPort(), lengthType);
760     if (rule.GetLengthScaleUnit() == SvgLengthScaleUnit::OBJECT_BOUNDING_BOX) {
761         position *= ContainerLength;
762         position += offset;
763     }
764     return position;
765 }
766 
767 double SvgNode::ConvertDimensionToPx(const Dimension& value, const Size& viewPort, SvgLengthType type) const
768 {
769     auto width = viewPort.Width();
770     auto height = viewPort.Height();
771     switch (value.Unit()) {
772         case DimensionUnit::PERCENT: {
773             if (type == SvgLengthType::HORIZONTAL) {
774                 return value.Value() * width;
775             }
776             if (type == SvgLengthType::VERTICAL) {
777                 return value.Value() * height;
778             }
779             if (type == SvgLengthType::OTHER) {
780                 if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
781                     return value.Value() * sqrt(width * height);
782                 }
783                 return value.Value() * std::sqrt(width * width + height * height) / std::sqrt(2.0f);
784             }
785             return 0.0;
786         }
787         case DimensionUnit::PX:
788             return value.Value();
789         default:
790             auto svgContext = svgContext_.Upgrade();
791             if (svgContext) {
792                 return svgContext->NormalizeToPx(value);
793             }
794             return 0.0;
795     }
796 }
797 
798 double SvgNode::ConvertDimensionToPx(const Dimension& value, double baseValue) const
799 {
800     if (value.Unit() == DimensionUnit::PERCENT) {
801         return value.Value() * baseValue;
802     }
803     if (value.Unit() == DimensionUnit::PX) {
804         return value.Value();
805     }
806     auto svgContext = svgContext_.Upgrade();
807     if (svgContext) {
808         return svgContext->NormalizeToPx(value);
809     }
810     return 0.0;
811 }
812 
813 std::optional<Ace::Gradient> SvgNode::GetGradient(const std::string& href)
814 {
815     auto svgContext = svgContext_.Upgrade();
816     CHECK_NULL_RETURN(svgContext, std::nullopt);
817     if (href.empty()) {
818         return std::nullopt;
819     }
820     auto refSvgNode = svgContext->GetSvgNodeById(href);
821     CHECK_NULL_RETURN(refSvgNode, std::nullopt);
822     auto svgGradient = DynamicCast<SvgGradient>(refSvgNode);
823     if (svgGradient) {
824         return std::make_optional(svgGradient->GetGradient());
825     }
826     return std::nullopt;
827 }
828 
829 Rect SvgNode::GetSvgContainerRect() const
830 {
831     auto svgContext = svgContext_.Upgrade();
832     if (!svgContext) {
833         LOGE("Gradient failed, svgContext is null");
834         static Rect empty;
835         return empty;
836     }
837     return Rect(0, 0, svgContext->GetViewPort().Width(), svgContext->GetViewPort().Height());
838 }
839 
840 const Rect& SvgNode::GetRootViewBox() const
841 {
842     auto svgContext = svgContext_.Upgrade();
843     if (!svgContext) {
844         LOGE("Gradient failed, svgContext is null");
845         static Rect empty;
846         return empty;
847     }
848     return svgContext->GetRootViewBox();
849 }
850 
851 void SvgNode::PrepareAnimation(const RefPtr<SvgAnimation>& animate)
852 {
853     auto attrName = animate->GetAttributeName();
854     if (COLOR_GETTERS.find(attrName) != COLOR_GETTERS.end()) {
855         Color originalValue = COLOR_GETTERS.find(attrName)->second(attributes_);
856         AnimateOnAttribute(animate, originalValue);
857     } else if (DIMENSION_GETTERS.find(attrName) != DIMENSION_GETTERS.end()) {
858         Dimension originalValue = DIMENSION_GETTERS.find(attrName)->second(attributes_);
859         AnimateOnAttribute(animate, originalValue);
860     } else if (DOUBLE_GETTERS.find(attrName) != DOUBLE_GETTERS.end()) {
861         double originalValue = DOUBLE_GETTERS.find(attrName)->second(attributes_);
862         AnimateOnAttribute(animate, originalValue);
863     } else if (attrName.find(TRANSFORM) != std::string::npos) {
864         AnimateTransform(animate, 0.0f);
865     } else {
866         LOGW("animation attrName not valid: %s", attrName.c_str());
867     }
868 }
869 
870 // create animation callback
871 template<typename T>
872 void SvgNode::AnimateOnAttribute(const RefPtr<SvgAnimation>& animate, const T& originalValue)
873 {
874     std::function<void(T)> callback;
875     callback = [weak = WeakClaim(this), attrName = animate->GetAttributeName()](T value) {
876         auto self = weak.Upgrade();
877         CHECK_NULL_VOID(self);
878         self->UpdateAttr(attrName, value);
879         auto context = self->svgContext_.Upgrade();
880         CHECK_NULL_VOID(context);
881         context->AnimateFlush();
882     };
883     animate->CreatePropertyAnimation(originalValue, std::move(callback));
884 }
885 
886 // update attribute for svgNode and its children
887 void SvgNode::UpdateAttrHelper(const std::string& name, const std::string& val)
888 {
889     SetAttr(name, val);
890     if (!passStyle_) {
891         return;
892     }
893     for (auto&& child : children_) {
894         if (child->inheritStyle_) {
895             child->UpdateAttrHelper(name, val);
896         }
897     }
898 }
899 
900 template<typename T>
901 void SvgNode::UpdateAttr(const std::string& /* name */, const T& /* val */)
902 {
903     LOGW("data type not supported");
904 }
905 
906 template<>
907 void SvgNode::UpdateAttr(const std::string& name, const Color& val)
908 {
909     UpdateAttrHelper(name, val.ColorToString());
910 }
911 
912 template<>
913 void SvgNode::UpdateAttr(const std::string& name, const Dimension& val)
914 {
915     UpdateAttrHelper(name, val.ToString());
916 }
917 
918 template<>
919 void SvgNode::UpdateAttr(const std::string& name, const double& val)
920 {
921     UpdateAttrHelper(name, std::to_string(val));
922 }
923 
924 void SvgNode::AnimateTransform(const RefPtr<SvgAnimation>& animate, double originalValue)
925 {
926     if (!animate->GetValues().empty()) {
927         AnimateFrameTransform(animate, originalValue);
928     } else {
929         AnimateFromToTransform(animate, originalValue);
930     }
931 }
932 
933 void SvgNode::AnimateFrameTransform(const RefPtr<SvgAnimation>& animate, double originalValue)
934 {
935     std::vector<std::vector<float>> frames;
936     std::string type;
937     if (!animate->GetFrames(frames, type)) {
938         LOGE("invalid animate keys info of type %{public}s", type.c_str());
939         return;
940     }
941     if (frames.size() <= 1) {
942         LOGE("invalid frames numbers %{public}s", type.c_str());
943         return;
944     }
945 
946     // change Values to frame indices to create an index-based animation
947     // property values of each frame are stored in [frames]
948     std::vector<std::string> indices;
949     uint32_t size = animate->GetValues().size();
950     for (uint32_t i = 0; i < size; i++) {
951         indices.emplace_back(std::to_string(i));
952     }
953     animate->SetValues(indices);
954 
955     std::function<void(double)> callback = [weak = WeakClaim(this), type, frames](double value) {
956         auto self = weak.Upgrade();
957         CHECK_NULL_VOID(self);
958         // use index and rate to locate frame and progress
959         auto index = static_cast<uint32_t>(value);
960         double rate = value - index;
961         if (index >= frames.size() - 1) {
962             index = frames.size() - 2;
963             rate = 1.0;
964         }
965         if (!SvgTransform::SetProperty(type, frames[index], frames[index + 1], rate, self->animateTransform_)) {
966             LOGE("set property failed: property %{public}s not in map", type.c_str());
967             return;
968         }
969         auto context = self->svgContext_.Upgrade();
970         CHECK_NULL_VOID(context);
971         context->AnimateFlush();
972     };
973     animate->CreatePropertyAnimation(originalValue, std::move(callback));
974 }
975 
976 void SvgNode::AnimateFromToTransform(const RefPtr<SvgAnimation>& animate, double originalValue)
977 {
978     std::vector<float> fromVec;
979     std::vector<float> toVec;
980     std::string type;
981     if (!animate->GetValuesRange(fromVec, toVec, type)) {
982         LOGE("invalid animate info of type %{public}s", type.c_str());
983         return;
984     }
985 
986     std::function<void(double)> callback = [weak = WeakClaim(this), type, fromVec, toVec](double value) {
987         auto self = weak.Upgrade();
988         CHECK_NULL_VOID(self);
989         if (!SvgTransform::SetProperty(type, fromVec, toVec, value, self->animateTransform_)) {
990             LOGE("set property failed: property %{public}s not in map", type.c_str());
991             return;
992         }
993         auto context = self->svgContext_.Upgrade();
994         CHECK_NULL_VOID(context);
995         context->AnimateFlush();
996     };
997     animate->CreatePropertyAnimation(originalValue, std::move(callback));
998 }
999 
1000 Offset SvgNode::CalcGlobalPivot(const std::pair<Dimension, Dimension>& transformOrigin, const Rect& baseRect)
1001 {
1002     Rect RectForX = baseRect;
1003     Rect RectForY = baseRect;
1004     Rect ReferenceBox = GetRootViewBox();
1005     if (ReferenceBox == Rect(0, 0, 0, 0)) {
1006         ReferenceBox = baseRect;
1007     }
1008     if (transformOrigin.first.Unit() == DimensionUnit::PERCENT) {
1009         RectForX = ReferenceBox;
1010     }
1011     if (transformOrigin.second.Unit() == DimensionUnit::PERCENT) {
1012         RectForY = ReferenceBox;
1013     }
1014     double x = ConvertDimensionToPx(transformOrigin.first, RectForX.GetSize(), SvgLengthType::HORIZONTAL);
1015     double y = ConvertDimensionToPx(transformOrigin.second, RectForY.GetSize(), SvgLengthType::VERTICAL);
1016     return Offset(x, y);
1017 }
1018 
1019 SvgLengthScaleRule SvgNode::TransformForCurrentOBB(RSCanvas& canvas,
1020     const SvgCoordinateSystemContext& context, SvgLengthScaleUnit contentUnits, float offsetX, float offsetY)
1021 {
1022     if (contentUnits == SvgLengthScaleUnit::USER_SPACE_ON_USE) {
1023         return context.BuildScaleRule(SvgLengthScaleUnit::USER_SPACE_ON_USE);
1024     }
1025     float scaleX = 0.0f;
1026     float scaleY = 0.0f;
1027     float translateX = 0.0f;
1028     float translateY = 0.0f;
1029     // create default rect to draw graphic
1030     auto squareWH = std::min(context.GetContainerRect().Width(), context.GetContainerRect().Height());
1031     Rect defaultRect(0, 0, squareWH, squareWH);
1032     SvgLengthScaleRule ContentRule = SvgLengthScaleRule(defaultRect, context.GetViewPort(),
1033         SvgLengthScaleUnit::OBJECT_BOUNDING_BOX);
1034 
1035     SvgPreserveAspectRatio preserveAspectRatio;
1036     preserveAspectRatio.svgAlign = SvgAlign::ALIGN_NONE;
1037     SvgAttributesParser::ComputeScale(defaultRect.GetSize(), context.GetContainerRect().GetSize(),
1038         preserveAspectRatio, scaleX, scaleY);
1039     SvgAttributesParser::ComputeTranslate(defaultRect.GetSize(), context.GetContainerRect().GetSize(), scaleX, scaleY,
1040         preserveAspectRatio.svgAlign, translateX, translateY);
1041     // scale the graphic content of the given element non-uniformly
1042     canvas.Translate(translateX  + offsetX, translateY + offsetY);
1043     canvas.Scale(scaleX, scaleY);
1044     return ContentRule;
1045 }
1046 } // namespace OHOS::Ace::NG