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,
__anon712ba1890202() 70 [](SvgBaseAttribute& attr) -> Color { return attr.fillState.GetColor(); } },
71 { ATTR_NAME_STROKE,
__anon712ba1890302() 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,
__anon712ba1890402() 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,
__anon712ba1890502() 84 [](SvgBaseAttribute& attr) -> double {
85 return attr.fillState.GetOpacity().GetValue();
86 } },
87 { ATTR_NAME_STROKE_OPACITY,
__anon712ba1890602() 88 [](SvgBaseAttribute& attr) -> double {
89 return attr.strokeState.GetOpacity().GetValue();
90 } },
91 { ATTR_NAME_MITER_LIMIT,
__anon712ba1890702() 92 [](SvgBaseAttribute& attr) -> double {
93 return attr.strokeState.GetMiterLimit();
94 } },
95 { ATTR_NAME_STROKE_DASH_OFFSET,
__anon712ba1890802() 96 [](SvgBaseAttribute& attr) -> double {
97 return attr.strokeState.GetLineDash().dashOffset;
98 } },
99 { ATTR_NAME_OPACITY,
__anon712ba1890902() 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,
__anon712ba1890d02() 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,
__anon712ba1890e02() 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,
__anon712ba1890f02() 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_ &&
413 currentSvgNodeInfo.childIndex < static_cast<int32_t>(currentSvgNode->children_.size())) {
414 auto child = currentSvgNode->children_[currentSvgNodeInfo.childIndex];
415 if (child) {
416 // pass down style only if child inheritStyle_ is true
417 initStyleTaskSt.emplace(
418 child, (child->inheritStyle_) ? &(currentSvgNode->attributes_) : nullptr);
419 }
420 ++currentSvgNodeInfo.childIndex;
421 return false;
422 }
423 return true;
424 }
425
426 void SvgNode::InitStyleDfs(const WeakPtr<SvgNode>& root, const SvgBaseAttribute& attr)
427 {
428 auto parentNode = root.Upgrade();
429 if (!parentNode) {
430 return;
431 }
432 std::stack<std::pair<SvgInitStyleProcessInfo, const SvgBaseAttribute*>> initStyleTaskSt;
433 initStyleTaskSt.emplace(parentNode, &attr);
434 while (!initStyleTaskSt.empty()) {
435 auto& [currentSvgNodeInfo, currentAttr] = initStyleTaskSt.top();
436 if (currentSvgNodeInfo.childIndex == 0) {
437 currentSvgNodeInfo.svgNode->ProcessSvgStyle(
438 currentSvgNodeInfo.svgNode, currentAttr ? *currentAttr : SvgBaseAttribute());
439 }
440 if (currentSvgNodeInfo.svgNode->ProcessChildStyle(currentSvgNodeInfo, initStyleTaskSt)) {
441 currentSvgNodeInfo.svgNode->ProcessChildAnimations(currentSvgNodeInfo.svgNode);
442 initStyleTaskSt.pop();
443 }
444 }
445 }
446
447 void SvgNode::InitStyle(const SvgBaseAttribute& attr)
448 {
449 InitStyleDfs(WeakClaim(this), attr);
450 }
451
452 void SvgNode::Draw(RSCanvas& canvas, const Size& viewPort, const std::optional<Color>& color)
453 {
454 if (isDrawing_) {
455 TAG_LOGW(AceLogTag::ACE_IMAGE,
456 "The current node is already in the process of being drawn in the SVG rendering flow.");
457 return;
458 }
459 if (!OnCanvas(canvas)) {
460 TAG_LOGW(AceLogTag::ACE_IMAGE, "Svg Draw failed(Reason: Canvas is null).");
461 return;
462 }
463 isDrawing_ = true;
464 // mask and filter create extra layers, need to record initial layer count
465 auto count = rsCanvas_->GetSaveCount();
466 rsCanvas_->Save();
467 if (!hrefClipPath_.empty()) {
468 OnClipPath(canvas, viewPort);
469 } else if (isRootNode_) {
470 AdjustContentAreaByViewBox(canvas, viewPort);
471 }
472 if (!transform_.empty() || !animateTransform_.empty()) {
473 OnTransform(canvas, viewPort);
474 }
475 if (!hrefMaskId_.empty()) {
476 OnMask(canvas, viewPort);
477 }
478 if (!hrefFilterId_.empty()) {
479 OnFilter(canvas, viewPort);
480 }
481
482 OnDraw(canvas, viewPort, color);
483 OnDrawTraversed(canvas, viewPort, color);
484 rsCanvas_->RestoreToCount(count);
485 isDrawing_ = false; // end the drawing process.
486 }
487
488 void SvgNode::Draw(RSCanvas& canvas, const SvgLengthScaleRule& lengthRule)
489 {
490 // mask and filter create extra layers, need to record initial layer count
491 auto count = canvas.GetSaveCount();
492 if (isDrawing_) {
493 TAG_LOGW(AceLogTag::ACE_IMAGE,
494 "The current node is already in the process of being drawn in the SVG rendering flow.");
495 return;
496 }
497 canvas.Save();
498 isDrawing_ = true;
499 auto rsBounds = AsPath(lengthRule).GetBounds();
500 Rect containerRect(rsBounds.GetLeft(), rsBounds.GetTop(), rsBounds.GetWidth(), rsBounds.GetHeight());
501 // rect use rsBounds, other use lengthRule containerRect
502 if (LessOrEqual(rsBounds.GetWidth(), 0.0f) || LessOrEqual(rsBounds.GetHeight(), 0.0f)) {
503 containerRect = lengthRule.GetContainerRect();
504 }
505 SvgCoordinateSystemContext svgCoordinateSystemContext(containerRect, lengthRule.GetViewPort());
506 TAG_LOGD(AceLogTag::ACE_IMAGE, "l:%{public}lf, t:%{public}lf, r:%{public}lf, b:%{public}lf, units:%{public}d",
507 rsBounds.GetLeft(), rsBounds.GetTop(), rsBounds.GetRight(), rsBounds.GetBottom(),
508 (int)lengthRule.GetLengthScaleUnit());
509 if (!hrefClipPath_.empty()) {
510 OnClipPath(canvas, svgCoordinateSystemContext);
511 } else if (isRootNode_) {
512 auto svgContext = svgContext_.Upgrade();
513 if (svgContext) {
514 AdjustContentAreaByViewBox(canvas, svgContext->GetViewPort());
515 }
516 }
517 if (!transform_.empty() || !animateTransform_.empty()) {
518 OnTransform(canvas, lengthRule);
519 }
520 if (!hrefMaskId_.empty()) {
521 OnMask(canvas, svgCoordinateSystemContext);
522 }
523 if (!hrefFilterId_.empty()) {
524 OnFilter(canvas, svgCoordinateSystemContext);
525 }
526
527 OnDraw(canvas, lengthRule);
528 OnDrawTraversed(canvas, lengthRule);
529 canvas.RestoreToCount(count);
530 isDrawing_ = false;
531 }
532
533 void SvgNode::OnDrawTraversed(RSCanvas& canvas, const Size& viewPort, const std::optional<Color>& color)
534 {
535 auto smoothEdge = GetSmoothEdge();
536 auto colorFilter = GetColorFilter();
537 for (auto& node : children_) {
538 if (node && node->drawTraversed_) {
539 if (GreatNotEqual(smoothEdge, 0.0f)) {
540 node->SetSmoothEdge(smoothEdge);
541 }
542 node->SetColorFilter(colorFilter);
543 node->Draw(canvas, viewPort, color);
544 }
545 }
546 }
547
548 void SvgNode::OnDrawTraversed(RSCanvas& canvas, const SvgLengthScaleRule& lengthRule)
549 {
550 auto smoothEdge = GetSmoothEdge();
551 auto colorFilter = GetColorFilter();
552 for (auto& node : children_) {
553 if (node && node->drawTraversed_) {
554 if (GreatNotEqual(smoothEdge, 0.0f)) {
555 node->SetSmoothEdge(smoothEdge);
556 }
557 node->SetColorFilter(colorFilter);
558 node->Draw(canvas, lengthRule);
559 }
560 }
561 }
562
563 bool SvgNode::OnCanvas(RSCanvas& canvas)
564 {
565 rsCanvas_ = &canvas;
566 return true;
567 }
568
569 void SvgNode::OnClipPath(RSCanvas& canvas, const Size& viewPort)
570 {
571 auto svgContext = svgContext_.Upgrade();
572 CHECK_NULL_VOID(svgContext);
573 auto refSvgNode = svgContext->GetSvgNodeById(hrefClipPath_);
574 CHECK_NULL_VOID(refSvgNode);
575 auto clipPath = refSvgNode->AsPath(viewPort);
576 if (!clipPath.IsValid()) {
577 LOGW("OnClipPath abandon, clipPath is empty");
578 return;
579 }
580 rsCanvas_->ClipPath(clipPath, RSClipOp::INTERSECT, true);
581 }
582
583 void SvgNode::OnClipPath(RSCanvas& canvas, const SvgCoordinateSystemContext& svgCoordinateSystemContext)
584 {
585 auto svgContext = svgContext_.Upgrade();
586 CHECK_NULL_VOID(svgContext);
587 auto refSvgNode = svgContext->GetSvgNodeById(hrefClipPath_);
588 CHECK_NULL_VOID(refSvgNode);
589 if (!AceType::InstanceOf<SvgClipPath>(refSvgNode)) {
590 return;
591 }
592 refSvgNode->OnClipEffect(canvas, svgCoordinateSystemContext);
593 }
594
595 void SvgNode::OnFilter(RSCanvas& canvas, const Size& viewPort)
596 {
597 if (!AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
598 return;
599 }
600 auto svgContext = svgContext_.Upgrade();
601 CHECK_NULL_VOID(svgContext);
602 auto refFilter = svgContext->GetSvgNodeById(hrefFilterId_);
603 CHECK_NULL_VOID(refFilter);
604 auto effectPath = AsPath(viewPort);
605 auto bounds = effectPath.GetBounds();
606 refFilter->SetEffectFilterArea({
607 useOffsetX_ + bounds.GetLeft(), useOffsetY_ + bounds.GetTop(),
608 bounds.GetWidth(), bounds.GetHeight()
609 });
610 refFilter->Draw(canvas, viewPort, std::nullopt);
611 return;
612 }
613
614 void SvgNode::OnFilter(RSCanvas& canvas, const SvgCoordinateSystemContext& svgCoordinateSystemContext)
615 {
616 auto svgContext = svgContext_.Upgrade();
617 CHECK_NULL_VOID(svgContext);
618 auto refFilter = svgContext->GetSvgNodeById(hrefFilterId_);
619 CHECK_NULL_VOID(refFilter);
620 if (!AceType::InstanceOf<SvgFilter>(refFilter)) {
621 return;
622 }
623 refFilter->OnFilterEffect(canvas, svgCoordinateSystemContext, useOffsetX_, useOffsetY_);
624 }
625
626 void SvgNode::OnMask(RSCanvas& canvas, const Size& viewPort)
627 {
628 auto svgContext = svgContext_.Upgrade();
629 CHECK_NULL_VOID(svgContext);
630 auto refMask = svgContext->GetSvgNodeById(hrefMaskId_);
631 CHECK_NULL_VOID(refMask);
632 refMask->Draw(canvas, viewPort, std::nullopt);
633 return;
634 }
635
636 void SvgNode::OnMask(RSCanvas& canvas, const SvgCoordinateSystemContext& svgCoordinateSystemContext)
637 {
638 auto svgContext = svgContext_.Upgrade();
639 CHECK_NULL_VOID(svgContext);
640 auto refMask = svgContext->GetSvgNodeById(hrefMaskId_);
641 CHECK_NULL_VOID(refMask);
642 if (!AceType::InstanceOf<SvgMask>(refMask)) {
643 return;
644 }
645 refMask->OnMaskEffect(canvas, svgCoordinateSystemContext);
646 }
647
648 void SvgNode::OnTransform(RSCanvas& canvas, const Size& viewPort)
649 {
650 auto matrix = (animateTransform_.empty()) ? SvgTransform::CreateMatrix4(transform_)
651 : SvgTransform::CreateMatrixFromMap(animateTransform_);
652 canvas.ConcatMatrix(RosenSvgPainter::ToDrawingMatrix(matrix));
653 }
654
655 void SvgNode::OnTransform(RSCanvas& canvas, const SvgLengthScaleRule& lengthRule)
656 {
657 Offset globalPivot = CalcGlobalPivot(attributes_.transformOrigin, lengthRule);
658 auto matrix = (animateTransform_.empty()) ?
659 NGSvgTransform::CreateMatrix4(attributes_.transformVec, globalPivot, lengthRule)
660 : SvgTransform::CreateMatrixFromMap(animateTransform_);
661 canvas.ConcatMatrix(RosenSvgPainter::ToDrawingMatrix(matrix));
662 }
663
664 float SvgNode::GetRegionLength(Dimension origin, const SvgLengthScaleRule& rule, SvgLengthType lengthType)
665 {
666 float length = 0.0f;
667 switch (lengthType) {
668 case SvgLengthType::HORIZONTAL:
669 length = (rule.GetLengthScaleUnit() == SvgLengthScaleUnit::USER_SPACE_ON_USE) ?
670 ConvertDimensionToPx(origin, rule.GetViewPort().Width()) :
671 origin.Value() * rule.GetContainerRect().Width();
672 break;
673 case SvgLengthType::VERTICAL:
674 length = (rule.GetLengthScaleUnit() == SvgLengthScaleUnit::USER_SPACE_ON_USE) ?
675 ConvertDimensionToPx(origin, rule.GetViewPort().Height()) :
676 origin.Value() * rule.GetContainerRect().Height();
677 break;
678 case SvgLengthType::OTHER:
679 auto width = rule.GetContainerRect().Width();
680 auto height = rule.GetContainerRect().Height();
681 auto baseLength = std::sqrt(width * width + height * height) / std::sqrt(2.0f);
682 length = rule.GetLengthScaleUnit() == SvgLengthScaleUnit::USER_SPACE_ON_USE ?
683 ConvertDimensionToPx(origin, baseLength) : origin.Value() * baseLength;
684 break;
685 }
686 return length;
687 }
688
689 float SvgNode::GetRegionPosition(Dimension origin, const SvgLengthScaleRule& rule,
690 SvgLengthType lengthType)
691 {
692 auto position = 0.0f;
693 switch (lengthType) {
694 case SvgLengthType::HORIZONTAL:
695 position = (rule.GetLengthScaleUnit() == SvgLengthScaleUnit::USER_SPACE_ON_USE) ?
696 ConvertDimensionToPx(origin, rule.GetViewPort().Width()) :
697 origin.Value() * rule.GetContainerRect().Width() + rule.GetContainerRect().Left();
698 break;
699 case SvgLengthType::VERTICAL:
700 position = (rule.GetLengthScaleUnit() == SvgLengthScaleUnit::USER_SPACE_ON_USE) ?
701 ConvertDimensionToPx(origin, rule.GetViewPort().Height()) :
702 origin.Value() * rule.GetContainerRect().Height() + rule.GetContainerRect().Top();
703 break;
704 default:
705 break;
706 }
707 return position;
708 }
709
710 float SvgNode::GetMeasuredLength(Dimension origin, const SvgLengthScaleRule& rule,
711 SvgLengthType lengthType)
712 {
713 float ContainerLength = 0.0f;
714 switch (lengthType) {
715 case SvgLengthType::HORIZONTAL:
716 ContainerLength = rule.GetContainerRect().Width();
717 break;
718 case SvgLengthType::VERTICAL:
719 ContainerLength = rule.GetContainerRect().Height();
720 break;
721 /*using the original definition, radius*/
722 case SvgLengthType::OTHER:
723 ContainerLength = std::min(rule.GetContainerRect().Width(), rule.GetContainerRect().Height());
724 break;
725 }
726 auto length = ConvertDimensionToPx(origin, rule.GetViewPort(), lengthType);
727 if (rule.GetLengthScaleUnit() == SvgLengthScaleUnit::OBJECT_BOUNDING_BOX) {
728 length *= ContainerLength;
729 }
730 return length;
731 }
732
733 float SvgNode::GetMeasuredPosition(Dimension origin, const SvgLengthScaleRule& rule,
734 SvgLengthType lengthType)
735 {
736 float offset = 0.0f;
737 float ContainerLength = 0.0f;
738 switch (lengthType) {
739 case SvgLengthType::HORIZONTAL:
740 ContainerLength = rule.GetContainerRect().Width();
741 offset = rule.GetContainerRect().Left();
742 break;
743 case SvgLengthType::VERTICAL:
744 ContainerLength = rule.GetContainerRect().Height();
745 offset = rule.GetContainerRect().Top();
746 break;
747 default:
748 return 0.0f;
749 }
750 auto position = ConvertDimensionToPx(origin, rule.GetViewPort(), lengthType);
751 if (rule.GetLengthScaleUnit() == SvgLengthScaleUnit::OBJECT_BOUNDING_BOX) {
752 position *= ContainerLength;
753 position += offset;
754 }
755 return position;
756 }
757
758 double SvgNode::ConvertDimensionToPx(const Dimension& value, const Size& viewPort, SvgLengthType type) const
759 {
760 auto width = viewPort.Width();
761 auto height = viewPort.Height();
762 switch (value.Unit()) {
763 case DimensionUnit::PERCENT: {
764 if (type == SvgLengthType::HORIZONTAL) {
765 return value.Value() * width;
766 }
767 if (type == SvgLengthType::VERTICAL) {
768 return value.Value() * height;
769 }
770 if (type == SvgLengthType::OTHER) {
771 if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_EIGHTEEN)) {
772 return value.Value() * sqrt(width * height);
773 }
774 return value.Value() * std::sqrt(width * width + height * height) / std::sqrt(2.0f);
775 }
776 return 0.0;
777 }
778 case DimensionUnit::PX:
779 return value.Value();
780 default:
781 auto svgContext = svgContext_.Upgrade();
782 if (svgContext) {
783 return svgContext->NormalizeToPx(value);
784 }
785 return 0.0;
786 }
787 }
788
789 double SvgNode::ConvertDimensionToPx(const Dimension& value, double baseValue) const
790 {
791 if (value.Unit() == DimensionUnit::PERCENT) {
792 return value.Value() * baseValue;
793 }
794 if (value.Unit() == DimensionUnit::PX) {
795 return value.Value();
796 }
797 auto svgContext = svgContext_.Upgrade();
798 if (svgContext) {
799 return svgContext->NormalizeToPx(value);
800 }
801 return 0.0;
802 }
803
804 std::optional<Ace::Gradient> SvgNode::GetGradient(const std::string& href)
805 {
806 auto svgContext = svgContext_.Upgrade();
807 CHECK_NULL_RETURN(svgContext, std::nullopt);
808 if (href.empty()) {
809 return std::nullopt;
810 }
811 auto refSvgNode = svgContext->GetSvgNodeById(href);
812 CHECK_NULL_RETURN(refSvgNode, std::nullopt);
813 auto svgGradient = DynamicCast<SvgGradient>(refSvgNode);
814 if (svgGradient) {
815 return std::make_optional(svgGradient->GetGradient());
816 }
817 return std::nullopt;
818 }
819
820 Rect SvgNode::GetSvgContainerRect() const
821 {
822 auto svgContext = svgContext_.Upgrade();
823 if (!svgContext) {
824 LOGE("Gradient failed, svgContext is null");
825 static Rect empty;
826 return empty;
827 }
828 return Rect(0, 0, svgContext->GetViewPort().Width(), svgContext->GetViewPort().Height());
829 }
830
831 const Rect& SvgNode::GetRootViewBox() const
832 {
833 auto svgContext = svgContext_.Upgrade();
834 if (!svgContext) {
835 LOGE("Gradient failed, svgContext is null");
836 static Rect empty;
837 return empty;
838 }
839 return svgContext->GetRootViewBox();
840 }
841
842 void SvgNode::PrepareAnimation(const RefPtr<SvgAnimation>& animate)
843 {
844 auto attrName = animate->GetAttributeName();
845 if (COLOR_GETTERS.find(attrName) != COLOR_GETTERS.end()) {
846 Color originalValue = COLOR_GETTERS.find(attrName)->second(attributes_);
847 AnimateOnAttribute(animate, originalValue);
848 } else if (DIMENSION_GETTERS.find(attrName) != DIMENSION_GETTERS.end()) {
849 Dimension originalValue = DIMENSION_GETTERS.find(attrName)->second(attributes_);
850 AnimateOnAttribute(animate, originalValue);
851 } else if (DOUBLE_GETTERS.find(attrName) != DOUBLE_GETTERS.end()) {
852 double originalValue = DOUBLE_GETTERS.find(attrName)->second(attributes_);
853 AnimateOnAttribute(animate, originalValue);
854 } else if (attrName.find(TRANSFORM) != std::string::npos) {
855 AnimateTransform(animate, 0.0f);
856 } else {
857 LOGW("animation attrName not valid: %s", attrName.c_str());
858 }
859 }
860
861 // create animation callback
862 template<typename T>
863 void SvgNode::AnimateOnAttribute(const RefPtr<SvgAnimation>& animate, const T& originalValue)
864 {
865 std::function<void(T)> callback;
866 callback = [weak = WeakClaim(this), attrName = animate->GetAttributeName()](T value) {
867 auto self = weak.Upgrade();
868 CHECK_NULL_VOID(self);
869 self->UpdateAttr(attrName, value);
870 auto context = self->svgContext_.Upgrade();
871 CHECK_NULL_VOID(context);
872 context->AnimateFlush();
873 };
874 animate->CreatePropertyAnimation(originalValue, std::move(callback));
875 }
876
877 // update attribute for svgNode and its children
878 void SvgNode::UpdateAttrHelper(const std::string& name, const std::string& val)
879 {
880 SetAttr(name, val);
881 if (!passStyle_) {
882 return;
883 }
884 for (auto&& child : children_) {
885 if (child->inheritStyle_) {
886 child->UpdateAttrHelper(name, val);
887 }
888 }
889 }
890
891 template<typename T>
892 void SvgNode::UpdateAttr(const std::string& /* name */, const T& /* val */)
893 {
894 LOGW("data type not supported");
895 }
896
897 template<>
898 void SvgNode::UpdateAttr(const std::string& name, const Color& val)
899 {
900 UpdateAttrHelper(name, val.ColorToString());
901 }
902
903 template<>
904 void SvgNode::UpdateAttr(const std::string& name, const Dimension& val)
905 {
906 UpdateAttrHelper(name, val.ToString());
907 }
908
909 template<>
910 void SvgNode::UpdateAttr(const std::string& name, const double& val)
911 {
912 UpdateAttrHelper(name, std::to_string(val));
913 }
914
915 void SvgNode::AnimateTransform(const RefPtr<SvgAnimation>& animate, double originalValue)
916 {
917 if (!animate->GetValues().empty()) {
918 AnimateFrameTransform(animate, originalValue);
919 } else {
920 AnimateFromToTransform(animate, originalValue);
921 }
922 }
923
924 void SvgNode::AnimateFrameTransform(const RefPtr<SvgAnimation>& animate, double originalValue)
925 {
926 std::vector<std::vector<float>> frames;
927 std::string type;
928 if (!animate->GetFrames(frames, type)) {
929 LOGE("invalid animate keys info of type %{public}s", type.c_str());
930 return;
931 }
932 if (frames.size() <= 1) {
933 LOGE("invalid frames numbers %{public}s", type.c_str());
934 return;
935 }
936
937 // change Values to frame indices to create an index-based animation
938 // property values of each frame are stored in [frames]
939 std::vector<std::string> indices;
940 uint32_t size = animate->GetValues().size();
941 for (uint32_t i = 0; i < size; i++) {
942 indices.emplace_back(std::to_string(i));
943 }
944 animate->SetValues(indices);
945
946 std::function<void(double)> callback = [weak = WeakClaim(this), type, frames](double value) {
947 auto self = weak.Upgrade();
948 CHECK_NULL_VOID(self);
949 // use index and rate to locate frame and progress
950 auto index = static_cast<uint32_t>(value);
951 double rate = value - index;
952 if (index >= frames.size() - 1) {
953 index = frames.size() - 2;
954 rate = 1.0;
955 }
956 if (!SvgTransform::SetProperty(type, frames[index], frames[index + 1], rate, self->animateTransform_)) {
957 LOGE("set property failed: property %{public}s not in map", type.c_str());
958 return;
959 }
960 auto context = self->svgContext_.Upgrade();
961 CHECK_NULL_VOID(context);
962 context->AnimateFlush();
963 };
964 animate->CreatePropertyAnimation(originalValue, std::move(callback));
965 }
966
967 void SvgNode::AnimateFromToTransform(const RefPtr<SvgAnimation>& animate, double originalValue)
968 {
969 std::vector<float> fromVec;
970 std::vector<float> toVec;
971 std::string type;
972 if (!animate->GetValuesRange(fromVec, toVec, type)) {
973 LOGE("invalid animate info of type %{public}s", type.c_str());
974 return;
975 }
976
977 std::function<void(double)> callback = [weak = WeakClaim(this), type, fromVec, toVec](double value) {
978 auto self = weak.Upgrade();
979 CHECK_NULL_VOID(self);
980 if (!SvgTransform::SetProperty(type, fromVec, toVec, value, self->animateTransform_)) {
981 LOGE("set property failed: property %{public}s not in map", type.c_str());
982 return;
983 }
984 auto context = self->svgContext_.Upgrade();
985 CHECK_NULL_VOID(context);
986 context->AnimateFlush();
987 };
988 animate->CreatePropertyAnimation(originalValue, std::move(callback));
989 }
990
991 Offset SvgNode::CalcGlobalPivot(const std::pair<Dimension, Dimension>& transformOrigin,
992 const SvgLengthScaleRule& lengthRule)
993 {
994 auto x = GetRegionLength(transformOrigin.first, lengthRule, SvgLengthType::HORIZONTAL);
995 auto y = GetRegionLength(transformOrigin.second, lengthRule, SvgLengthType::VERTICAL);
996 return Offset(x, y);
997 }
998
999 SvgLengthScaleRule SvgNode::BuildContentScaleRule(const SvgCoordinateSystemContext& parentContext,
1000 SvgLengthScaleUnit contentUnits)
1001 {
1002 if (contentUnits == SvgLengthScaleUnit::USER_SPACE_ON_USE) {
1003 return parentContext.BuildScaleRule(SvgLengthScaleUnit::USER_SPACE_ON_USE);
1004 }
1005 // create default rect to draw graphic
1006 auto squareWH = std::min(parentContext.GetContainerRect().Width(), parentContext.GetContainerRect().Height());
1007 Rect defaultRect(0, 0, squareWH, squareWH);
1008 SvgLengthScaleRule ContentRule (defaultRect,
1009 parentContext.GetViewPort(), SvgLengthScaleUnit::OBJECT_BOUNDING_BOX);
1010 return ContentRule;
1011 }
1012
1013 void SvgNode::TransformForCurrentOBB(RSCanvas& canvas, const SvgLengthScaleRule& contentRule,
1014 const Size& ContainerSize, const Offset& offset)
1015 {
1016 if (contentRule.GetLengthScaleUnit() == SvgLengthScaleUnit::USER_SPACE_ON_USE) {
1017 return;
1018 }
1019 float scaleX = 0.0f;
1020 float scaleY = 0.0f;
1021 float translateX = 0.0f;
1022 float translateY = 0.0f;
1023 SvgPreserveAspectRatio preserveAspectRatio;
1024 preserveAspectRatio.svgAlign = SvgAlign::ALIGN_NONE;
1025 SvgAttributesParser::ComputeScale(contentRule.GetContainerRect().GetSize(), ContainerSize,
1026 preserveAspectRatio, scaleX, scaleY);
1027 SvgAttributesParser::ComputeTranslate(contentRule.GetContainerRect().GetSize(),
1028 ContainerSize, scaleX, scaleY, preserveAspectRatio.svgAlign,
1029 translateX, translateY);
1030 // scale the graphic content of the given element non-uniformly
1031 canvas.Translate(translateX + offset.GetX(), translateY + offset.GetY());
1032 canvas.Scale(scaleX, scaleY);
1033 }
1034
1035 void SvgNode::ApplyTransform(RSRecordingPath& path, const SvgLengthScaleRule& lengthRule)
1036 {
1037 if (!attributes_.transformVec.size()) {
1038 return;
1039 }
1040 Offset globalPivot = CalcGlobalPivot(attributes_.transformOrigin, lengthRule);
1041 auto matrix = NGSvgTransform::CreateMatrix4(attributes_.transformVec, globalPivot, lengthRule);
1042 path.Transform(RosenSvgPainter::ToDrawingMatrix(matrix));
1043 }
1044 } // namespace OHOS::Ace::NG