1 /*
2 * Copyright (c) 2021-2022 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/transform/render_transform.h"
17
18 #include "core/components/box/render_box_base.h"
19 #include "core/components/transform/transform_component.h"
20
21 namespace OHOS::Ace {
22
23 // Effect translate to Matrix at the end for percent needs to be calculated after layout.
Translate(const Dimension & x,const Dimension & y)24 void RenderTransform::Translate(const Dimension& x, const Dimension& y)
25 {
26 Translate(x, y, Dimension {});
27 }
28
Translate(const Dimension & x,const Dimension & y,const Dimension & z)29 void RenderTransform::Translate(const Dimension& x, const Dimension& y, const Dimension& z)
30 {
31 auto dx = CovertDimensionToPxBySize(x, GetLayoutSize().Width());
32 auto dy = CovertDimensionToPxBySize(y, GetLayoutSize().Height());
33 // not support percent
34 auto dz = CovertDimensionToPxBySize(z, 0.0);
35
36 transform_ = transform_ * Matrix4::CreateTranslate(dx, dy, dz);
37 UpdateTransformLayer();
38 auto context = context_.Upgrade();
39 if (context) {
40 context->MarkForcedRefresh();
41 }
42 #if defined(PREVIEW)
43 UpdateTranslateToAccessibilityNode(x.Value(), y.Value());
44 #endif
45 }
46
Scale(float value)47 void RenderTransform::Scale(float value)
48 {
49 Scale(value, value);
50 }
51
Scale(float x,float y)52 void RenderTransform::Scale(float x, float y)
53 {
54 Scale(x, y, 1.0f);
55 }
56
Scale(float x,float y,float z)57 void RenderTransform::Scale(float x, float y, float z)
58 {
59 transform_ = transform_ * Matrix4::CreateScale(x, y, z);
60 UpdateTransformLayer();
61 auto context = context_.Upgrade();
62 if (context) {
63 context->MarkForcedRefresh();
64 }
65 #if defined(PREVIEW)
66 if (!NearEqual(maxScaleXY_, -1.0)) {
67 UpdateScaleToAccessibilityNode(maxScaleXY_);
68 }
69 #endif
70 }
71
Skew(float x,float y)72 void RenderTransform::Skew(float x, float y)
73 {
74 if (!NearZero(x) || !NearZero(y)) {
75 Matrix4 skew = Matrix4::CreateSkew(x, y);
76 transform_ = transform_ * skew;
77 }
78 UpdateTransformLayer();
79 auto context = context_.Upgrade();
80 if (context) {
81 context->MarkForcedRefresh();
82 }
83 }
84
Rotate(float angle,float x,float y,float z)85 void RenderTransform::Rotate(float angle, float x, float y, float z)
86 {
87 if (!NearZero(angle) && !NearZero(fmod(angle, 360.0f))) {
88 Matrix4 rotate = Matrix4::CreateRotate(angle, x, y, z);
89 transform_ = transform_ * rotate;
90 }
91 UpdateTransformLayer();
92 auto context = context_.Upgrade();
93 if (context) {
94 context->MarkForcedRefresh();
95 }
96 #if defined(PREVIEW)
97 if (!NearEqual(angle, 0.0) && !NearEqual(z, 0.0)) {
98 UpdateRotateToAccessibilityNode(angle, RotateAxis::AXIS_Z);
99 }
100 #endif
101 }
102
RotateX(float angle)103 void RenderTransform::RotateX(float angle)
104 {
105 Rotate(angle, 1.0f, 0.0f, 0.0f);
106 }
107
RotateY(float angle)108 void RenderTransform::RotateY(float angle)
109 {
110 Rotate(angle, 0.0f, 1.0f, 0.0f);
111 }
112
RotateZ(float angle)113 void RenderTransform::RotateZ(float angle)
114 {
115 Rotate(angle, 0.0f, 0.0f, 1.0f);
116 #if defined(PREVIEW)
117 if (!NearEqual(angle, 0.0)) {
118 UpdateRotateToAccessibilityNode(angle, RotateAxis::AXIS_Z);
119 }
120 #endif
121 }
122
Matrix3D(Matrix4 m)123 void RenderTransform::Matrix3D(Matrix4 m)
124 {
125 if (!m.IsIdentityMatrix()) {
126 transform_ = transform_ * m;
127 UpdateTransformLayer();
128 auto context = context_.Upgrade();
129 if (context) {
130 context->MarkForcedRefresh();
131 }
132 }
133 }
134
Perspective(const Dimension & distance)135 void RenderTransform::Perspective(const Dimension& distance)
136 {
137 if (!NearZero(distance.Value())) {
138 auto dx = CovertDimensionToPxBySize(distance, 0);
139 transform_ = transform_ * Matrix4::CreatePerspective(static_cast<float>(dx));
140 UpdateTransformLayer();
141 auto context = context_.Upgrade();
142 if (context) {
143 context->MarkForcedRefresh();
144 }
145 }
146 }
147
ResetTransform()148 void RenderTransform::ResetTransform()
149 {
150 transform_ = Matrix4::CreateIdentity();
151 #if defined(PREVIEW)
152 ResetTransformToAccessibilityNode();
153 #endif
154 }
155
156 #if defined(PREVIEW)
ResetTransformToAccessibilityNode()157 void RenderTransform::ResetTransformToAccessibilityNode()
158 {
159 const auto& context = context_.Upgrade();
160 if (!context) {
161 return;
162 }
163 auto accessibilityManager = context->GetAccessibilityManager();
164 if (!accessibilityManager) {
165 LOGE("accessibilityManager is null");
166 return;
167 }
168 auto accessibilityNode = accessibilityManager->GetAccessibilityNodeById(GetNodeId());
169 if (!accessibilityNode) {
170 LOGE("RenderTransform is null");
171 return;
172 }
173 accessibilityNode->SetScaleToChild(1.0);
174 accessibilityNode->SetTranslateOffsetToChild(Offset(0.0, 0.0));
175 accessibilityNode->SetRotateToChild(0.0, RotateAxis::AXIS_Z);
176 for (const auto& item : GetChildren()) {
177 item->NotifyPaintFinish();
178 }
179 }
180
UpdateScaleToAccessibilityNode(float maxScale)181 void RenderTransform::UpdateScaleToAccessibilityNode(float maxScale)
182 {
183 const auto& context = context_.Upgrade();
184 if (!context) {
185 return;
186 }
187 auto accessibilityManager = context->GetAccessibilityManager();
188 if (!accessibilityManager) {
189 LOGE("accessibilityManager is null");
190 return;
191 }
192 auto accessibilityNode = accessibilityManager->GetAccessibilityNodeById(GetNodeId());
193 if (!accessibilityNode) {
194 LOGE("RenderTransform is null");
195 return;
196 }
197
198 if (!NearEqual(maxScale, 1.0)) {
199 Size size = GetLayoutSize();
200 Offset globalOffset = GetGlobalOffset();
201 Offset scaleCenter =
202 Offset(globalOffset.GetX() + size.Width() / 2.0, globalOffset.GetY() + size.Height() / 2.0);
203 accessibilityNode->SetScaleToChild(maxScale);
204 accessibilityNode->SetScaleCenterToChild(scaleCenter);
205 for (const auto& item : GetChildren()) {
206 item->NotifyPaintFinish();
207 }
208 }
209 }
210
UpdateTranslateToAccessibilityNode(double translateX,double translateY)211 void RenderTransform::UpdateTranslateToAccessibilityNode(double translateX, double translateY)
212 {
213 const auto& context = context_.Upgrade();
214 if (!context) {
215 return;
216 }
217 auto accessibilityManager = context->GetAccessibilityManager();
218 if (!accessibilityManager) {
219 LOGE("accessibilityManager is null");
220 return;
221 }
222 auto accessibilityNode = accessibilityManager->GetAccessibilityNodeById(GetNodeId());
223 if (!accessibilityNode) {
224 LOGE("RenderTransform is null");
225 return;
226 }
227 if (!NearEqual(translateX, 0.0) || !NearEqual(translateY, 0.0)) {
228 Offset translateOffset(translateX, translateY);
229 accessibilityNode->SetTranslateOffsetToChild(translateOffset);
230 for (const auto& child : GetChildren()) {
231 child->NotifyPaintFinish();
232 }
233 }
234 }
235
UpdateRotateToAccessibilityNode(float angle,RotateAxis rotateAxis)236 void RenderTransform::UpdateRotateToAccessibilityNode(float angle, RotateAxis rotateAxis)
237 {
238 const auto& context = context_.Upgrade();
239 if (!context) {
240 return;
241 }
242 auto accessibilityManager = context->GetAccessibilityManager();
243 if (!accessibilityManager) {
244 LOGE("accessibilityManager is null");
245 return;
246 }
247 auto accessibilityNode = accessibilityManager->GetAccessibilityNodeById(GetNodeId());
248 if (!accessibilityNode) {
249 LOGE("RenderTransform is null");
250 return;
251 }
252 if (!NearEqual(angle, 0.0)) {
253 accessibilityNode->SetRotateToChild(angle, rotateAxis);
254 Size size = GetLayoutSize();
255 Offset globalOffset = GetGlobalOffset();
256 Offset scaleCenter =
257 Offset(globalOffset.GetX() + size.Width() / 2.0, globalOffset.GetY() + size.Height() / 2.0);
258 accessibilityNode->SetScaleCenterToChild(scaleCenter);
259 for (const auto& item : GetChildren()) {
260 item->NotifyPaintFinish();
261 }
262 }
263 }
264 #endif
265
ParseTransformEffects(const std::vector<AnimatableTransformOperation> & transformEffects,Matrix4 & transform)266 void RenderTransform::ParseTransformEffects(
267 const std::vector<AnimatableTransformOperation>& transformEffects, Matrix4& transform)
268 {
269 for (const auto& effect : transformEffects) {
270 transform = transform * ParseTransformEffect(effect);
271 }
272 }
273
ParseTransformEffect(const TransformOperation & effect)274 Matrix4 RenderTransform::ParseTransformEffect(const TransformOperation& effect)
275 {
276 switch (effect.type_) {
277 case TransformOperationType::TRANSLATE: {
278 auto& translate = effect.translateOperation_;
279 float dx = CovertDimensionToPxBySize(translate.dx, GetLayoutSize().Width());
280 float dy = CovertDimensionToPxBySize(translate.dy, GetLayoutSize().Height());
281 // dz not support percent
282 float dz = CovertDimensionToPxBySize(translate.dz, 0.0);
283 return Matrix4::CreateTranslate(dx, dy, dz);
284 }
285 case TransformOperationType::SCALE: {
286 auto& scale = effect.scaleOperation_;
287 return Matrix4::CreateScale(scale.scaleX, scale.scaleY, scale.scaleZ);
288 }
289 case TransformOperationType::SKEW: {
290 auto& skew = effect.skewOperation_;
291 return Matrix4::CreateSkew(skew.skewX, skew.skewY);
292 }
293 case TransformOperationType::ROTATE: {
294 auto& rotate = effect.rotateOperation_;
295 return Matrix4::CreateRotate(rotate.angle, rotate.dx, rotate.dy, rotate.dz);
296 }
297 case TransformOperationType::MATRIX: {
298 auto& matrix = effect.matrix4_;
299 return matrix;
300 }
301 case TransformOperationType::PERSPECTIVE: {
302 auto& perspective = effect.perspectiveOperation_;
303 double distance = CovertDimensionToPxBySize(perspective.distance, 0.0);
304 return Matrix4::CreatePerspective(distance);
305 }
306 case TransformOperationType::UNDEFINED:
307 default:
308 LOGE("unknown transform operation type %{public}d", static_cast<int32_t>(effect.type_));
309 return Matrix4::CreateIdentity();
310 }
311 }
312
ParseDimension(TransformOperation & effect)313 void RenderTransform::ParseDimension(TransformOperation& effect)
314 {
315 switch (effect.type_) {
316 case TransformOperationType::TRANSLATE: {
317 auto& translate = effect.translateOperation_;
318 translate.dx = Dimension(CovertDimensionToPxBySize(translate.dx, GetLayoutSize().Width()));
319 translate.dy = Dimension(CovertDimensionToPxBySize(translate.dy, GetLayoutSize().Height()));
320 // dz not support percent
321 translate.dz = Dimension(CovertDimensionToPxBySize(translate.dz, 0.0));
322 break;
323 }
324 case TransformOperationType::PERSPECTIVE: {
325 auto& perspective = effect.perspectiveOperation_;
326 perspective.distance = Dimension(CovertDimensionToPxBySize(perspective.distance, 0.0));
327 break;
328 }
329 case TransformOperationType::SCALE:
330 case TransformOperationType::SKEW:
331 case TransformOperationType::ROTATE:
332 case TransformOperationType::MATRIX:
333 case TransformOperationType::UNDEFINED:
334 break;
335 default:
336 break;
337 }
338 }
339
UpdateTransform()340 void RenderTransform::UpdateTransform()
341 {
342 if (!needUpdateTransform_) {
343 return;
344 }
345 needUpdateTransform_ = false;
346 ParseTransformEffects(transformEffects_.GetOperations(), transform_);
347 auto context = context_.Upgrade();
348 if (!context) {
349 LOGE("Update transform failed. context is null.");
350 return;
351 }
352 if (pendingAppearing_ && hasAppearTransition_) {
353 if (transformAnimation_.GetAnimationStatus() != Animator::Status::RUNNING) {
354 for (auto& effect : transformEffectsAppearing_) {
355 ParseDimension(effect);
356 }
357 transformAnimation_.SetTransformOperations(transformEffectsAppearing_);
358 }
359 transformAnimation_.SetAnimationStopCallback(nullptr);
360 // transform from appearing Transform to identity matrix
361 transformAnimation_.PlayTransformAnimation(transitionOption_, std::vector<TransformOperation>());
362 pendingAppearing_ = false;
363 }
364 }
365
SetTouchable(bool enable)366 void RenderTransform::SetTouchable(bool enable)
367 {
368 enableTouchTest_ = enable;
369 }
370
Update(const RefPtr<Component> & component)371 void RenderTransform::Update(const RefPtr<Component>& component)
372 {
373 auto transform = AceType::DynamicCast<TransformComponent>(component);
374 if (transform == nullptr) {
375 LOGE("transform component is nullptr.");
376 return;
377 }
378 ResetTransform();
379 needUpdateTransform_ = true;
380 transform_ = transform->GetTransform();
381 transformEffects_ = transform->GetTransformEffects();
382 hasAppearTransition_ = transform->HasAppearTransition();
383 if (hasAppearTransition_) {
384 transformEffectsAppearing_ = transform->GetTransformEffectsAppearing();
385 } else {
386 transformEffectsAppearing_.clear();
387 }
388 hasDisappearTransition_ = transform->HasDisappearTransition();
389 if (hasDisappearTransition_) {
390 transformEffectsDisappearing_ = transform->GetTransformEffectsDisappearing();
391 } else {
392 transformEffectsDisappearing_.clear();
393 }
394 transitionOption_ = context_.Upgrade()->GetExplicitAnimationOption();
395 originX_ = transform->GetOriginDimension().GetX();
396 originY_ = transform->GetOriginDimension().GetY();
397 SetTouchHandle(transform->GetClickSpringEffectType());
398 SetShadow(transform->GetShadow());
399 MarkNeedLayout();
400 }
401
PerformLayout()402 void RenderTransform::PerformLayout()
403 {
404 auto child = GetFirstChild();
405 if (child == nullptr) {
406 LOGE("child component is nullptr.");
407 return;
408 }
409
410 Size layoutSize;
411 LayoutParam innerLayout;
412 Size maxLayoutSize = GetLayoutParam().GetMaxSize();
413 if (!(maxLayoutSize < Size())) {
414 innerLayout.SetMaxSize(maxLayoutSize);
415 child->Layout(innerLayout);
416 layoutSize = child->GetLayoutSize();
417 }
418 SetLayoutSize(layoutSize);
419 needUpdateOrigin_ = true;
420 MarkNeedSyncGeometryProperties();
421 }
422
UpdateTransformOrigin()423 void RenderTransform::UpdateTransformOrigin()
424 {
425 auto child = GetFirstChild();
426 if (child == nullptr) {
427 LOGE("child component is nullptr.");
428 return;
429 }
430
431 if (AceType::InstanceOf<RenderBoxBase>(child) && AceType::InstanceOf<RenderBoxBase>(child->GetFirstChild())) {
432 child = child->GetFirstChild();
433 }
434
435 Size layoutSize = GetLayoutSize();
436 const auto& renderBoxBase = AceType::DynamicCast<RenderBoxBase>(child);
437 if (renderBoxBase) {
438 auto margin = renderBoxBase->GetMargin();
439 double marginTop = margin.TopPx();
440 double marginLeft = margin.LeftPx();
441 double marginBottom = margin.BottomPx();
442 double marginRight = margin.RightPx();
443 double paintWidthSize = layoutSize.Width() - marginLeft - marginRight;
444 double paintHeightSize = layoutSize.Height() - marginTop - marginBottom;
445 origin_.SetX(CovertDimensionToPxBySize(originX_, paintWidthSize) + marginLeft);
446 origin_.SetY(CovertDimensionToPxBySize(originY_, paintHeightSize) + marginTop);
447 } else {
448 origin_.SetX(CovertDimensionToPxBySize(originX_, layoutSize.Width()));
449 origin_.SetY(CovertDimensionToPxBySize(originY_, layoutSize.Height()));
450 }
451 }
452
CovertDimensionToPxBySize(const Dimension & dimension,double size)453 double RenderTransform::CovertDimensionToPxBySize(const Dimension& dimension, double size)
454 {
455 double result = 0.0;
456 if (dimension.Unit() == DimensionUnit::PERCENT) {
457 result = dimension.Value() * size;
458 } else if (dimension.Unit() == DimensionUnit::VP) {
459 result = NormalizeToPx(dimension);
460 } else {
461 result = dimension.Value();
462 }
463 return result;
464 };
465
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)466 void RenderTransform::OnTouchTestHit(
467 const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
468 {
469 if (rawRecognizer_) {
470 rawRecognizer_->SetCoordinateOffset(coordinateOffset);
471 result.emplace_back(rawRecognizer_);
472 }
473 }
474
SetTouchHandle(ClickSpringEffectType type)475 void RenderTransform::SetTouchHandle(ClickSpringEffectType type)
476 {
477 if (type == ClickSpringEffectType::NONE) {
478 if (rawRecognizer_) {
479 rawRecognizer_->SetOnTouchUp(nullptr);
480 rawRecognizer_->SetOnTouchDown(nullptr);
481 rawRecognizer_->SetOnTouchCancel(nullptr);
482 }
483 } else {
484 if (!rawRecognizer_) {
485 rawRecognizer_ = AceType::MakeRefPtr<RawRecognizer>();
486 }
487 if (!clickSpringEffect_) {
488 clickSpringEffect_ = AceType::MakeRefPtr<ClickSpringEffect>(GetContext());
489 clickSpringEffect_->SetRenderNode(WeakClaim(this));
490 }
491 auto touchHandle = [weak = AceType::WeakClaim(this)](
492 const TouchEventInfo&, TouchType touchType, ClickSpringEffectType effectType) {
493 auto transform = weak.Upgrade();
494 if (transform && transform->clickSpringEffect_) {
495 transform->clickSpringEffect_->ShowAnimation(touchType, effectType);
496 }
497 };
498 rawRecognizer_->SetOnTouchDown(std::bind(touchHandle, std::placeholders::_1, TouchType::DOWN, type));
499 rawRecognizer_->SetOnTouchUp(std::bind(touchHandle, std::placeholders::_1, TouchType::UP, type));
500 rawRecognizer_->SetOnTouchCancel(std::bind(touchHandle, std::placeholders::_1, TouchType::CANCEL, type));
501 }
502 }
503
UpdateWithEffectMatrix(Matrix4 matrix)504 Matrix4 RenderTransform::UpdateWithEffectMatrix(Matrix4 matrix)
505 {
506 auto animationMatrix = transformAnimation_.ComputerBlendedMatrix4();
507 if (!animationMatrix.IsIdentityMatrix()) {
508 matrix = matrix * animationMatrix;
509 }
510 if (clickSpringEffect_ && !disableClickEffect_) {
511 double scale = clickSpringEffect_->GetScale();
512 if (!NearEqual(scale, 1.0)) {
513 return matrix * Matrix4::CreateScale(scale, scale, 1.0);
514 }
515 }
516
517 return matrix;
518 }
519
GetGlobalOffsetExternal() const520 Offset RenderTransform::GetGlobalOffsetExternal() const
521 {
522 auto renderNode = GetParent().Upgrade();
523 auto offset = renderNode ? GetPosition() + renderNode->GetGlobalOffsetExternal() : GetPosition();
524
525 // transformPaint_[12] is translation of x,transformPaint_[13] is translation of y.
526 offset += Offset(transform_[12], transform_[13]);
527 return offset;
528 }
529
HasDisappearingTransition(int32_t nodeId)530 bool RenderTransform::HasDisappearingTransition(int32_t nodeId)
531 {
532 return hasDisappearTransition_ || RenderNode::HasDisappearingTransition(nodeId);
533 }
534
OnTransition(TransitionType type,int32_t id)535 void RenderTransform::OnTransition(TransitionType type, int32_t id)
536 {
537 auto context = context_.Upgrade();
538 if (!context) {
539 LOGE("OnTransition failed, context_ is null.");
540 return;
541 }
542 const auto& option = context->GetExplicitAnimationOption();
543 if (!option.IsValid()) {
544 LOGE("transition option is not valid.");
545 return;
546 }
547 if (type == TransitionType::APPEARING) {
548 pendingAppearing_ = true;
549 needUpdateTransform_ = true;
550 } else if (type == TransitionType::DISAPPEARING && hasDisappearTransition_) {
551 transformAnimation_.SetAnimationStopCallback([weak = AceType::WeakClaim(this)]() {
552 auto renderNode = weak.Upgrade();
553 if (renderNode) {
554 renderNode->OnTransformDisappearingCallback();
555 }
556 });
557 for (auto& effect : transformEffectsDisappearing_) {
558 ParseDimension(effect);
559 }
560 transformAnimation_.PlayTransformAnimation(option, transformEffectsDisappearing_);
561 }
562 }
563
ClearRenderObject()564 void RenderTransform::ClearRenderObject()
565 {
566 RenderNode::ClearRenderObject();
567 transform_ = Matrix4::CreateIdentity();
568 transformAnimation_.SetTransformOperations(std::vector<TransformOperation>());
569 transformEffects_.Clear();
570
571 needUpdateTransform_ = false;
572 isFirstAnimation_ = true;
573 origin_ = Offset();
574 originX_ = Dimension();
575 originY_ = Dimension();
576 needUpdateOrigin_ = false;
577
578 enableTouchTest_ = true;
579 transformPaint_ = Matrix4::CreateIdentity();
580 transitionOption_ = AnimationOption();
581 transformEffectsAppearing_.clear();
582 transformEffectsDisappearing_.clear();
583 hasDisappearTransition_ = false;
584 hasAppearTransition_ = false;
585 pendingAppearing_ = false;
586 }
587
OnTransformDisappearingCallback()588 void RenderTransform::OnTransformDisappearingCallback()
589 {
590 RefPtr<RenderNode> child = AceType::Claim(this);
591 while (child && !child->IsDisappearing()) {
592 child = child->GetParent().Upgrade();
593 }
594 if (!child) {
595 return;
596 }
597 auto parent = child->GetParent().Upgrade();
598 if (parent) {
599 parent->ClearDisappearingNode(child);
600 }
601 }
602
GetTransformByOffset(Matrix4 matrix,const Offset & offset)603 Matrix4 RenderTransform::GetTransformByOffset(Matrix4 matrix, const Offset& offset)
604 {
605 if (offset.IsZero()) {
606 return matrix;
607 }
608
609 Matrix4 transform =
610 Matrix4::CreateTranslate(static_cast<float>(-offset.GetX()), static_cast<float>(-offset.GetY()), 0.0f);
611 transform = matrix * transform;
612 transform = Matrix4::CreateTranslate(static_cast<float>(offset.GetX()), static_cast<float>(offset.GetY()), 0.0f) *
613 transform;
614 return transform;
615 }
616
617 } // namespace OHOS::Ace
618