• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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