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