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