1 /*
2  * Copyright (c) 2022-2023 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 #include "include/core/SkString.h"
20 #include "include/utils/SkParsePath.h"
21 
22 #include "base/utils/utils.h"
23 #include "core/common/ace_application_info.h"
24 #include "core/components/common/painter/rosen_svg_painter.h"
25 #include "core/components/common/properties/decoration.h"
26 #include "core/components_ng/render/drawing.h"
27 #include "core/components_ng/svg/parse/svg_animation.h"
28 #include "core/components_ng/svg/parse/svg_attributes_parser.h"
29 #include "core/components_ng/svg/parse/svg_gradient.h"
30 #include "core/pipeline_ng/pipeline_context.h"
31 
32 namespace OHOS::Ace::NG {
33 namespace {
34 
35 constexpr size_t SVG_ATTR_ID_FLAG_NUMS = 6;
36 const char VALUE_NONE[] = "none";
37 const char ATTR_NAME_OPACITY[] = "opacity";
38 const char DOM_SVG_SRC_FILL_OPACITY[] = "fill-opacity";
39 const char DOM_SVG_SRC_FILL_RULE[] = "fill-rule";
40 const char DOM_SVG_SRC_STROKE_DASHARRAY[] = "stroke-dasharray";
41 const char DOM_SVG_SRC_STROKE_DASHOFFSET[] = "stroke-dashoffset";
42 const char DOM_SVG_SRC_STROKE_LINECAP[] = "stroke-linecap";
43 const char DOM_SVG_SRC_STROKE_LINEJOIN[] = "stroke-linejoin";
44 const char DOM_SVG_SRC_STROKE_MITERLIMIT[] = "stroke-miterlimit";
45 const char DOM_SVG_SRC_STROKE_OPACITY[] = "stroke-opacity";
46 const char DOM_SVG_SRC_STROKE_WIDTH[] = "stroke-width";
47 const char DOM_SVG_SRC_CLIP_PATH[] = "clip-path";
48 const char DOM_SVG_SRC_CLIP_RULE[] = "clip-rule";
49 const char DOM_SVG_SRC_TRANSFORM_ORIGIN[] = "transform-origin";
50 
ParseIdFromUrl(const std::string & url)51 std::string ParseIdFromUrl(const std::string& url)
52 {
53     if (url.size() > SVG_ATTR_ID_FLAG_NUMS) {
54         std::string::size_type start = url.find("url(#");
55         if (start != std::string::npos) {
56             start += std::strlen("url(#");
57             std::string::size_type end = url.find_first_of(')', start);
58             if (end != std::string::npos) {
59                 return url.substr(start, end - start);
60             }
61         }
62     }
63     return "";
64 }
65 
66 const std::unordered_map<std::string, std::function<Color(SvgBaseAttribute&)>> COLOR_GETTERS = {
67     { ATTR_NAME_FILL,
__anonbac6909f0202() 68         [](SvgBaseAttribute& attr) -> Color { return attr.fillState.GetColor(); } },
69     { ATTR_NAME_STROKE,
__anonbac6909f0302() 70         [](SvgBaseAttribute& attr) -> Color { return attr.strokeState.GetColor(); } },
71 };
72 
73 const std::unordered_map<std::string, std::function<Dimension(SvgBaseAttribute&)>> DIMENSION_GETTERS = {
74     { ATTR_NAME_STROKE_WIDTH,
__anonbac6909f0402() 75         [](SvgBaseAttribute& attr) -> Dimension {
76             return attr.strokeState.GetLineWidth();
77         } },
78 };
79 
80 const std::unordered_map<std::string, std::function<double(SvgBaseAttribute&)>> DOUBLE_GETTERS = {
81     { ATTR_NAME_FILL_OPACITY,
__anonbac6909f0502() 82         [](SvgBaseAttribute& attr) -> double {
83             return attr.fillState.GetOpacity().GetValue();
84         } },
85     { ATTR_NAME_STROKE_OPACITY,
__anonbac6909f0602() 86         [](SvgBaseAttribute& attr) -> double {
87             return attr.strokeState.GetOpacity().GetValue();
88         } },
89     { ATTR_NAME_MITER_LIMIT,
__anonbac6909f0702() 90         [](SvgBaseAttribute& attr) -> double {
91             return attr.strokeState.GetMiterLimit();
92         } },
93     { ATTR_NAME_STROKE_DASH_OFFSET,
__anonbac6909f0802() 94         [](SvgBaseAttribute& attr) -> double {
95             return attr.strokeState.GetLineDash().dashOffset;
96         } },
97     { ATTR_NAME_OPACITY,
__anonbac6909f0902() 98         [](SvgBaseAttribute& attr) -> double {
99             return attr.opacity * (1.0 / UINT8_MAX);
100         } },
101 };
102 
103 } // namespace
104 
OpacityDoubleToUint8(double opacity)105 uint8_t OpacityDoubleToUint8(double opacity)
106 {
107     return static_cast<uint8_t>(std::round(opacity * UINT8_MAX));
108 }
109 
SetAttr(const std::string & name,const std::string & value)110 void SvgNode::SetAttr(const std::string& name, const std::string& value)
111 {
112     if (ParseAndSetSpecializedAttr(name, value)) {
113         return;
114     }
115     static const LinearMapNode<void (*)(const std::string&, SvgBaseAttribute&)> SVG_BASE_ATTRS[] = {
116         { DOM_SVG_SRC_CLIP_PATH,
117             [](const std::string& val, SvgBaseAttribute& attrs) {
118                 auto value = StringUtils::TrimStr(val);
119                 if (value.find("url(") == 0) {
120                     auto src = std::regex_replace(value,
121                         std::regex(R"(^url\(\s*['"]?\s*#([^()]+?)\s*['"]?\s*\)$)"), "$1");
122                     attrs.clipState.SetHref(src);
123                 }
124             } },
125         { DOM_SVG_SRC_CLIP_RULE,
126             [](const std::string& val, SvgBaseAttribute& attrs) {
127                 attrs.clipState.SetClipRule(val);
128             } },
129         { DOM_CLIP_PATH,
130             [](const std::string& val, SvgBaseAttribute& attrs) {
131                 auto value = StringUtils::TrimStr(val);
132                 if (value.find("url(") == 0) {
133                     auto src = std::regex_replace(value,
134                         std::regex(R"(^url\(\s*['"]?\s*#([^()]+?)\s*['"]?\s*\)$)"), "$1");
135                     attrs.clipState.SetHref(src);
136                 }
137             } },
138         { DOM_SVG_CLIP_RULE,
139             [](const std::string& val, SvgBaseAttribute& attrs) {
140                 attrs.clipState.SetClipRule(val);
141             } },
142         { DOM_SVG_FILL,
143             [](const std::string& val, SvgBaseAttribute& attrs) {
144                 auto value = StringUtils::TrimStr(val);
145                 if (value.find("url(") == 0) {
146                     auto src = std::regex_replace(value,
147                         std::regex(R"(^url\(\s*['"]?\s*#([^()]+?)\s*['"]?\s*\)$)"), "$1");
148                     attrs.fillState.SetHref(src);
149                 } else {
150                     Color fill = (val == VALUE_NONE ? Color::TRANSPARENT : SvgAttributesParser::GetColor(value));
151                     attrs.fillState.SetColor(fill);
152                 }
153             } },
154         { DOM_SVG_SRC_FILL_OPACITY,
155             [](const std::string& val, SvgBaseAttribute& attrs) {
156                 attrs.fillState.SetOpacity(std::clamp(StringUtils::StringToDouble(val), 0.0, 1.0));
157             } },
158         { DOM_SVG_SRC_FILL_RULE,
159             [](const std::string& val, SvgBaseAttribute& attrs) {
160                 attrs.fillState.SetFillRule(val);
161             } },
162         { DOM_SVG_FILL_OPACITY,
163             [](const std::string& val, SvgBaseAttribute& attrs) {
164                 attrs.fillState.SetOpacity(std::clamp(StringUtils::StringToDouble(val), 0.0, 1.0));
165             } },
166         { DOM_SVG_FILL_RULE,
167             [](const std::string& val, SvgBaseAttribute& attrs) {
168                 attrs.fillState.SetFillRule(val);
169             } },
170         { DOM_SVG_FILTER,
171             [](const std::string& val, SvgBaseAttribute& attrs) {
172                 attrs.filterId = val;
173             } },
174         { DOM_SVG_FONT_SIZE,
175             [](const std::string& val, SvgBaseAttribute& attrs) {
176                 Dimension fontSize = StringUtils::StringToDimension(val);
177                 if (GreatOrEqual(fontSize.Value(), 0.0)) {
178                     attrs.textStyle.SetFontSize(fontSize);
179                 }
180             } },
181         { DOM_SVG_HREF,
182             [](const std::string& val, SvgBaseAttribute& attrs) {
183                 auto value = StringUtils::TrimStr(val);
184                 if (value.substr(0, 1) == "#") {
185                     attrs.href = value.substr(1);
186                 }
187             } },
188         { DOM_SVG_MASK,
189             [](const std::string& val, SvgBaseAttribute& attrs) {
190                 attrs.maskId = val;
191             } },
192         { DOM_SVG_OPACITY,
193             [](const std::string& val, SvgBaseAttribute& attrs) {
194                 attrs.hasOpacity = true;
195                 attrs.opacity = std::clamp(StringUtils::StringToDouble(val), 0.0, 1.0);
196             } },
197         { DOM_SVG_PATTERN_TRANSFORM,
198             [](const std::string& val, SvgBaseAttribute& attrs) {
199                 attrs.transform = val;
200             } },
201         { DOM_SVG_STROKE,
202             [](const std::string& val, SvgBaseAttribute& attrs) {
203                 auto value = StringUtils::TrimStr(val);
204                 if (value.find("url(") == 0) {
205                     auto src = std::regex_replace(value,
206                         std::regex(R"(^url\(\s*['"]?\s*#([^()]+?)\s*['"]?\s*\)$)"), "$1");
207                     attrs.strokeState.SetHref(src);
208                 } else {
209                     Color stroke = (val == VALUE_NONE ? Color::TRANSPARENT : SvgAttributesParser::GetColor(val));
210                     attrs.strokeState.SetColor(stroke);
211                 }
212             } },
213         { DOM_SVG_SRC_STROKE_DASHARRAY,
214             [](const std::string& val, SvgBaseAttribute& attrs) {
215                 if (val.empty()) {
216                     return;
217                 }
218                 std::vector<double> lineDashVector;
219                 std::string handledStr = StringUtils::ReplaceChar(val, ',', ' ');
220                 StringUtils::StringSplitter(handledStr, ' ', lineDashVector);
221                 attrs.strokeState.SetLineDash(lineDashVector);
222             } },
223         { DOM_SVG_SRC_STROKE_DASHOFFSET,
224             [](const std::string& val, SvgBaseAttribute& attrs) {
225                 attrs.strokeState.SetLineDashOffset(StringUtils::StringToDouble(val));
226             } },
227         { DOM_SVG_SRC_STROKE_LINECAP,
228             [](const std::string& val, SvgBaseAttribute& attrs) {
229                 attrs.strokeState.SetLineCap(SvgAttributesParser::GetLineCapStyle(val));
230             } },
231         { DOM_SVG_SRC_STROKE_LINEJOIN,
232             [](const std::string& val, SvgBaseAttribute& attrs) {
233                 attrs.strokeState.SetLineJoin(SvgAttributesParser::GetLineJoinStyle(val));
234             } },
235         { DOM_SVG_SRC_STROKE_MITERLIMIT,
236             [](const std::string& val, SvgBaseAttribute& attrs) {
237                 double miterLimit = StringUtils::StringToDouble(val);
238                 if (GreatOrEqual(miterLimit, 1.0)) {
239                     attrs.strokeState.SetMiterLimit(miterLimit);
240                 }
241             } },
242         { DOM_SVG_SRC_STROKE_OPACITY,
243             [](const std::string& val, SvgBaseAttribute& attrs) {
244                 attrs.strokeState.SetOpacity(std::clamp(StringUtils::StringToDouble(val), 0.0, 1.0));
245             } },
246         { DOM_SVG_SRC_STROKE_WIDTH,
247             [](const std::string& val, SvgBaseAttribute& attrs) {
248                 Dimension lineWidth = StringUtils::StringToDimension(val);
249                 if (GreatOrEqual(lineWidth.Value(), 0.0)) {
250                     attrs.strokeState.SetLineWidth(lineWidth);
251                 }
252             } },
253         { DOM_SVG_STROKE_DASHARRAY,
254             [](const std::string& val, SvgBaseAttribute& attrs) {
255                 if (val.empty()) {
256                     return;
257                 }
258                 std::vector<double> lineDashVector;
259                 StringUtils::StringSplitter(val, ' ', lineDashVector);
260                 attrs.strokeState.SetLineDash(lineDashVector);
261             } },
262         { DOM_SVG_STROKE_DASHOFFSET,
263             [](const std::string& val, SvgBaseAttribute& attrs) {
264                 attrs.strokeState.SetLineDashOffset(StringUtils::StringToDouble(val));
265             } },
266         { DOM_SVG_STROKE_LINECAP,
267             [](const std::string& val, SvgBaseAttribute& attrs) {
268                 attrs.strokeState.SetLineCap(SvgAttributesParser::GetLineCapStyle(val));
269             } },
270         { DOM_SVG_STROKE_LINEJOIN,
271             [](const std::string& val, SvgBaseAttribute& attrs) {
272                 attrs.strokeState.SetLineJoin(SvgAttributesParser::GetLineJoinStyle(val));
273             } },
274         { DOM_SVG_STROKE_MITERLIMIT,
275             [](const std::string& val, SvgBaseAttribute& attrs) {
276                 double miterLimit = StringUtils::StringToDouble(val);
277                 if (GreatOrEqual(miterLimit, 1.0)) {
278                     attrs.strokeState.SetMiterLimit(miterLimit);
279                 }
280             } },
281         { DOM_SVG_STROKE_OPACITY,
282             [](const std::string& val, SvgBaseAttribute& attrs) {
283                 attrs.strokeState.SetOpacity(std::clamp(StringUtils::StringToDouble(val), 0.0, 1.0));
284             } },
285         { DOM_SVG_STROKE_WIDTH,
286             [](const std::string& val, SvgBaseAttribute& attrs) {
287                 Dimension lineWidth = StringUtils::StringToDimension(val);
288                 if (GreatOrEqual(lineWidth.Value(), 0.0)) {
289                     attrs.strokeState.SetLineWidth(lineWidth);
290                 }
291             } },
292         { DOM_TRANSFORM,
293             [](const std::string& val, SvgBaseAttribute& attrs) {
294                 attrs.transform = val;
295             } },
296         { DOM_SVG_SRC_TRANSFORM_ORIGIN,
297             [](const std::string& val, SvgBaseAttribute& attrs) {
298                 attrs.transformOrigin = val;
299             } },
300         { DOM_SVG_XLINK_HREF,
301             [](const std::string& val, SvgBaseAttribute& attrs) {
302                 auto value = StringUtils::TrimStr(val);
303                 if (value.substr(0, 1) == "#") {
304                     attrs.href = value.substr(1);
305                 }
306             } }
307     };
308     auto attrIter = BinarySearchFindIndex(SVG_BASE_ATTRS, ArraySize(SVG_BASE_ATTRS), name.c_str());
309     if (attrIter != -1) {
310         SVG_BASE_ATTRS[attrIter].value(value, attributes_);
311     }
312 }
313 
314 RSPath SvgNode::AsRSPath(const Size& viewPort) const
315 {
316     return {};
317 }
318 
319 void SvgNode::InitStyle(const SvgBaseAttribute& attr)
320 {
321     InheritAttr(attr);
322     if (hrefFill_) {
323         auto href = attributes_.fillState.GetHref();
324         if (!href.empty()) {
325             auto gradient = GetGradient(href);
326             if (gradient) {
327                 attributes_.fillState.SetGradient(gradient.value());
328             }
329         }
330         href = attributes_.strokeState.GetHref();
331         if (!href.empty()) {
332             auto gradient = GetGradient(href);
333             if (gradient) {
334                 attributes_.strokeState.SetGradient(gradient.value());
335             }
336         }
337     }
338     if (hrefRender_) {
339         hrefClipPath_ = attributes_.clipState.GetHref();
340         opacity_ = OpacityDoubleToUint8(attributes_.opacity);
341         transform_ = attributes_.transform;
342         hrefMaskId_ = ParseIdFromUrl(attributes_.maskId);
343         hrefFilterId_ = ParseIdFromUrl(attributes_.filterId);
344     }
345     OnInitStyle();
346     if (passStyle_) {
347         for (auto& node : children_) {
348             CHECK_NULL_VOID(node);
349             // pass down style only if child inheritStyle_ is true
350             node->InitStyle((node->inheritStyle_) ? attributes_ : SvgBaseAttribute());
351         }
352     }
353     for (auto& child : children_) {
354         auto svgAnimate = DynamicCast<SvgAnimation>(child);
355         if (svgAnimate) {
356             svgAnimate->UpdateAttr();
357             PrepareAnimation(svgAnimate);
358         }
359     }
360 }
361 
362 void SvgNode::Draw(RSCanvas& canvas, const Size& viewPort, const std::optional<Color>& color)
363 {
364     if (!OnCanvas(canvas)) {
365         TAG_LOGW(AceLogTag::ACE_IMAGE, "Svg Draw failed(Reason: Canvas is null).");
366         return;
367     }
368     // mask and filter create extra layers, need to record initial layer count
369 #ifndef USE_ROSEN_DRAWING
370     auto count = skCanvas_->getSaveCount();
371     skCanvas_->save();
372 #else
373     auto count = rsCanvas_->GetSaveCount();
374     rsCanvas_->Save();
375 #endif
376     if (!hrefClipPath_.empty()) {
377         OnClipPath(canvas, viewPort);
378     } else if (isRootNode_) {
379         AdjustContentAreaByViewBox(canvas, viewPort);
380     }
381     if (!transform_.empty() || !animateTransform_.empty()) {
382         OnTransform(canvas, viewPort);
383     }
384     if (!hrefMaskId_.empty()) {
385         OnMask(canvas, viewPort);
386     }
387     if (!hrefFilterId_.empty()) {
388         OnFilter(canvas, viewPort);
389     }
390 
391     OnDraw(canvas, viewPort, color);
392     OnDrawTraversed(canvas, viewPort, color);
393 #ifndef USE_ROSEN_DRAWING
394     skCanvas_->restoreToCount(count);
395 #else
396     rsCanvas_->RestoreToCount(count);
397 #endif
398 }
399 
400 void SvgNode::OnDrawTraversed(RSCanvas& canvas, const Size& viewPort, const std::optional<Color>& color)
401 {
402     auto smoothEdge = GetSmoothEdge();
403     auto colorFilter = GetColorFilter();
404     for (auto& node : children_) {
405         if (node && node->drawTraversed_) {
406             if (GreatNotEqual(smoothEdge, 0.0f)) {
407                 node->SetSmoothEdge(smoothEdge);
408             }
409             node->SetColorFilter(colorFilter);
410             node->Draw(canvas, viewPort, color);
411         }
412     }
413 }
414 
415 bool SvgNode::OnCanvas(RSCanvas& canvas)
416 {
417 #ifndef USE_ROSEN_DRAWING
418     // drawing.h api 不完善,直接取用SkCanvas,后续要重写
419     auto rsCanvas = canvas.GetImpl<RSSkCanvas>();
420     CHECK_NULL_RETURN(rsCanvas, false);
421     skCanvas_ = rsCanvas->ExportSkCanvas();
422     return skCanvas_ != nullptr;
423 #else
424     rsCanvas_ = &canvas;
425     return true;
426 #endif
427 }
428 
429 void SvgNode::OnClipPath(RSCanvas& canvas, const Size& viewPort)
430 {
431     auto svgContext = svgContext_.Upgrade();
432     CHECK_NULL_VOID(svgContext);
433     auto refSvgNode = svgContext->GetSvgNodeById(hrefClipPath_);
434     CHECK_NULL_VOID(refSvgNode);
435     auto clipPath = refSvgNode->AsPath(viewPort);
436 #ifndef USE_ROSEN_DRAWING
437     if (clipPath.isEmpty()) {
438         LOGW("OnClipPath abandon, clipPath is empty");
439         return;
440     }
441     skCanvas_->clipPath(clipPath, SkClipOp::kIntersect, true);
442 #else
443     if (!clipPath.IsValid()) {
444         LOGW("OnClipPath abandon, clipPath is empty");
445         return;
446     }
447     rsCanvas_->ClipPath(clipPath, RSClipOp::INTERSECT, true);
448 #endif
449 }
450 
451 void SvgNode::OnFilter(RSCanvas& canvas, const Size& viewPort)
452 {
453     if (!AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
454         return;
455     }
456     auto svgContext = svgContext_.Upgrade();
457     CHECK_NULL_VOID(svgContext);
458     auto refFilter = svgContext->GetSvgNodeById(hrefFilterId_);
459     CHECK_NULL_VOID(refFilter);
460     auto effectPath = AsPath(viewPort);
461     auto bounds = effectPath.GetBounds();
462     refFilter->SetEffectFilterArea({
463         useOffsetX_ + bounds.GetLeft(), useOffsetY_ + bounds.GetTop(),
464         bounds.GetWidth(), bounds.GetHeight()
465     });
466     refFilter->Draw(canvas, viewPort, std::nullopt);
467     return;
468 }
469 
470 void SvgNode::OnMask(RSCanvas& canvas, const Size& viewPort)
471 {
472     auto svgContext = svgContext_.Upgrade();
473     CHECK_NULL_VOID(svgContext);
474     auto refMask = svgContext->GetSvgNodeById(hrefMaskId_);
475     CHECK_NULL_VOID(refMask);
476     refMask->Draw(canvas, viewPort, std::nullopt);
477     return;
478 }
479 
480 void SvgNode::OnTransform(RSCanvas& canvas, const Size& viewPort)
481 {
482     auto matrix = (animateTransform_.empty()) ? SvgTransform::CreateMatrix4(transform_)
483                                               : SvgTransform::CreateMatrixFromMap(animateTransform_);
484 #ifndef USE_ROSEN_DRAWING
485 
486     skCanvas_->concat(RosenSvgPainter::ToSkMatrix(matrix));
487 #else
488     rsCanvas_->ConcatMatrix(RosenSvgPainter::ToDrawingMatrix(matrix));
489 #endif
490 }
491 
492 double SvgNode::ConvertDimensionToPx(const Dimension& value, const Size& viewPort, SvgLengthType type) const
493 {
494     switch (value.Unit()) {
495         case DimensionUnit::PERCENT: {
496             if (type == SvgLengthType::HORIZONTAL) {
497                 return value.Value() * viewPort.Width();
498             }
499             if (type == SvgLengthType::VERTICAL) {
500                 return value.Value() * viewPort.Height();
501             }
502             if (type == SvgLengthType::OTHER) {
503                 return value.Value() * sqrt(viewPort.Width() * viewPort.Height());
504             }
505             return 0.0;
506         }
507         case DimensionUnit::PX:
508             return value.Value();
509         default:
510             auto svgContext = svgContext_.Upgrade();
511             if (svgContext) {
512                 return svgContext->NormalizeToPx(value);
513             }
514             return 0.0;
515     }
516 }
517 
518 double SvgNode::ConvertDimensionToPx(const Dimension& value, double baseValue) const
519 {
520     if (value.Unit() == DimensionUnit::PERCENT) {
521         return value.Value() * baseValue;
522     }
523     if (value.Unit() == DimensionUnit::PX) {
524         return value.Value();
525     }
526     auto svgContext = svgContext_.Upgrade();
527     if (svgContext) {
528         return svgContext->NormalizeToPx(value);
529     }
530     return 0.0;
531 }
532 
533 std::optional<Ace::Gradient> SvgNode::GetGradient(const std::string& href)
534 {
535     auto svgContext = svgContext_.Upgrade();
536     CHECK_NULL_RETURN(svgContext, std::nullopt);
537     if (href.empty()) {
538         return std::nullopt;
539     }
540     auto refSvgNode = svgContext->GetSvgNodeById(href);
541     CHECK_NULL_RETURN(refSvgNode, std::nullopt);
542     auto svgGradient = DynamicCast<SvgGradient>(refSvgNode);
543     if (svgGradient) {
544         return std::make_optional(svgGradient->GetGradient());
545     }
546     return std::nullopt;
547 }
548 
549 const Rect& SvgNode::GetRootViewBox() const
550 {
551     auto svgContext = svgContext_.Upgrade();
552     if (!svgContext) {
553         LOGE("Gradient failed, svgContext is null");
554         static Rect empty;
555         return empty;
556     }
557     return svgContext->GetRootViewBox();
558 }
559 
560 void SvgNode::PrepareAnimation(const RefPtr<SvgAnimation>& animate)
561 {
562     auto attrName = animate->GetAttributeName();
563     if (COLOR_GETTERS.find(attrName) != COLOR_GETTERS.end()) {
564         Color originalValue = COLOR_GETTERS.find(attrName)->second(attributes_);
565         AnimateOnAttribute(animate, originalValue);
566     } else if (DIMENSION_GETTERS.find(attrName) != DIMENSION_GETTERS.end()) {
567         Dimension originalValue = DIMENSION_GETTERS.find(attrName)->second(attributes_);
568         AnimateOnAttribute(animate, originalValue);
569     } else if (DOUBLE_GETTERS.find(attrName) != DOUBLE_GETTERS.end()) {
570         double originalValue = DOUBLE_GETTERS.find(attrName)->second(attributes_);
571         AnimateOnAttribute(animate, originalValue);
572     } else if (attrName.find(TRANSFORM) != std::string::npos) {
573         AnimateTransform(animate, 0.0f);
574     } else {
575         LOGW("animation attrName not valid: %s", attrName.c_str());
576     }
577 }
578 
579 // create animation callback
580 template<typename T>
581 void SvgNode::AnimateOnAttribute(const RefPtr<SvgAnimation>& animate, const T& originalValue)
582 {
583     std::function<void(T)> callback;
584     callback = [weak = WeakClaim(this), attrName = animate->GetAttributeName()](T value) {
585         auto self = weak.Upgrade();
586         CHECK_NULL_VOID(self);
587         self->UpdateAttr(attrName, value);
588         auto context = self->svgContext_.Upgrade();
589         CHECK_NULL_VOID(context);
590         context->AnimateFlush();
591     };
592     animate->CreatePropertyAnimation(originalValue, std::move(callback));
593 }
594 
595 // update attribute for svgNode and its children
596 void SvgNode::UpdateAttrHelper(const std::string& name, const std::string& val)
597 {
598     SetAttr(name, val);
599     if (!passStyle_) {
600         return;
601     }
602     for (auto&& child : children_) {
603         if (child->inheritStyle_) {
604             child->UpdateAttrHelper(name, val);
605         }
606     }
607 }
608 
609 template<typename T>
610 void SvgNode::UpdateAttr(const std::string& /* name */, const T& /* val */)
611 {
612     LOGW("data type not supported");
613 }
614 
615 template<>
616 void SvgNode::UpdateAttr(const std::string& name, const Color& val)
617 {
618     UpdateAttrHelper(name, val.ColorToString());
619 }
620 
621 template<>
622 void SvgNode::UpdateAttr(const std::string& name, const Dimension& val)
623 {
624     UpdateAttrHelper(name, val.ToString());
625 }
626 
627 template<>
628 void SvgNode::UpdateAttr(const std::string& name, const double& val)
629 {
630     UpdateAttrHelper(name, std::to_string(val));
631 }
632 
633 void SvgNode::AnimateTransform(const RefPtr<SvgAnimation>& animate, double originalValue)
634 {
635     if (!animate->GetValues().empty()) {
636         AnimateFrameTransform(animate, originalValue);
637     } else {
638         AnimateFromToTransform(animate, originalValue);
639     }
640 }
641 
642 void SvgNode::AnimateFrameTransform(const RefPtr<SvgAnimation>& animate, double originalValue)
643 {
644     std::vector<std::vector<float>> frames;
645     std::string type;
646     if (!animate->GetFrames(frames, type)) {
647         LOGE("invalid animate keys info of type %{public}s", type.c_str());
648         return;
649     }
650     if (frames.size() <= 1) {
651         LOGE("invalid frames numbers %{public}s", type.c_str());
652         return;
653     }
654 
655     // change Values to frame indices to create an index-based animation
656     // property values of each frame are stored in [frames]
657     std::vector<std::string> indices;
658     uint32_t size = animate->GetValues().size();
659     for (uint32_t i = 0; i < size; i++) {
660         indices.emplace_back(std::to_string(i));
661     }
662     animate->SetValues(indices);
663 
664     std::function<void(double)> callback = [weak = WeakClaim(this), type, frames](double value) {
665         auto self = weak.Upgrade();
666         CHECK_NULL_VOID(self);
667         // use index and rate to locate frame and progress
668         auto index = static_cast<uint32_t>(value);
669         double rate = value - index;
670         if (index >= frames.size() - 1) {
671             index = frames.size() - 2;
672             rate = 1.0;
673         }
674         if (!SvgTransform::SetProperty(type, frames[index], frames[index + 1], rate, self->animateTransform_)) {
675             LOGE("set property failed: property %{public}s not in map", type.c_str());
676             return;
677         }
678         auto context = self->svgContext_.Upgrade();
679         CHECK_NULL_VOID(context);
680         context->AnimateFlush();
681     };
682     animate->CreatePropertyAnimation(originalValue, std::move(callback));
683 }
684 
685 void SvgNode::AnimateFromToTransform(const RefPtr<SvgAnimation>& animate, double originalValue)
686 {
687     std::vector<float> fromVec;
688     std::vector<float> toVec;
689     std::string type;
690     if (!animate->GetValuesRange(fromVec, toVec, type)) {
691         LOGE("invalid animate info of type %{public}s", type.c_str());
692         return;
693     }
694 
695     std::function<void(double)> callback = [weak = WeakClaim(this), type, fromVec, toVec](double value) {
696         auto self = weak.Upgrade();
697         CHECK_NULL_VOID(self);
698         if (!SvgTransform::SetProperty(type, fromVec, toVec, value, self->animateTransform_)) {
699             LOGE("set property failed: property %{public}s not in map", type.c_str());
700             return;
701         }
702         auto context = self->svgContext_.Upgrade();
703         CHECK_NULL_VOID(context);
704         context->AnimateFlush();
705     };
706     animate->CreatePropertyAnimation(originalValue, std::move(callback));
707 }
708 
709 } // namespace OHOS::Ace::NG