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