• 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 "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(WINDOWS_PLATFORM) || defined(MAC_PLATFORM)
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(WINDOWS_PLATFORM) || defined(MAC_PLATFORM)
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(WINDOWS_PLATFORM) || defined(MAC_PLATFORM)
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(WINDOWS_PLATFORM) || defined(MAC_PLATFORM)
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(WINDOWS_PLATFORM) || defined(MAC_PLATFORM)
153     ResetTransformToAccessibilityNode();
154 #endif
155 }
156 
157 #if defined(WINDOWS_PLATFORM) || defined(MAC_PLATFORM)
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     }
337 }
338 
UpdateTransform()339 void RenderTransform::UpdateTransform()
340 {
341     if (!needUpdateTransform_) {
342         return;
343     }
344     needUpdateTransform_ = false;
345     ParseTransformEffects(transformEffects_.GetOperations(), transform_);
346     auto context = context_.Upgrade();
347     if (!context) {
348         LOGE("Update transform failed. context is null.");
349         return;
350     }
351     if (pendingAppearing_ && hasAppearTransition_) {
352         if (transformAnimation_.GetAnimationStatus() != Animator::Status::RUNNING) {
353             for (auto& effect : transformEffectsAppearing_) {
354                 ParseDimension(effect);
355             }
356             transformAnimation_.SetTransformOperations(transformEffectsAppearing_);
357         }
358         transformAnimation_.SetAnimationStopCallback(nullptr);
359         // transform from appearing Transform to identity matrix
360         transformAnimation_.PlayTransformAnimation(transitionOption_, std::vector<TransformOperation>());
361         pendingAppearing_ = false;
362     }
363 }
364 
SetTouchable(bool enable)365 void RenderTransform::SetTouchable(bool enable)
366 {
367     LOGD("set transform touchable status: %{public}d", enable);
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     LOGD("RenderTransform::PerformLayout");
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.IsValid()) {
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     Size layoutSize = GetLayoutSize();
432     const auto& renderBoxBase = AceType::DynamicCast<RenderBoxBase>(child);
433     if (renderBoxBase) {
434         auto margin = renderBoxBase->GetMargin();
435         double marginTop = margin.TopPx();
436         double marginLeft = margin.LeftPx();
437         double marginBottom = margin.BottomPx();
438         double marginRight = margin.RightPx();
439         double paintWidthSize = layoutSize.Width() - marginLeft - marginRight;
440         double paintHeightSize = layoutSize.Height() - marginTop - marginBottom;
441         origin_.SetX(CovertDimensionToPxBySize(originX_, paintWidthSize) + marginLeft);
442         origin_.SetY(CovertDimensionToPxBySize(originY_, paintHeightSize) + marginTop);
443     } else {
444         origin_.SetX(CovertDimensionToPxBySize(originX_, layoutSize.Width()));
445         origin_.SetY(CovertDimensionToPxBySize(originY_, layoutSize.Height()));
446     }
447 }
448 
CovertDimensionToPxBySize(const Dimension & dimension,double size)449 double RenderTransform::CovertDimensionToPxBySize(const Dimension& dimension, double size)
450 {
451     double result = 0.0;
452     if (dimension.Unit() == DimensionUnit::PERCENT) {
453         result = dimension.Value() * size;
454     } else if (dimension.Unit() == DimensionUnit::VP) {
455         result = NormalizeToPx(dimension);
456     } else {
457         result = dimension.Value();
458     }
459     return result;
460 };
461 
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)462 void RenderTransform::OnTouchTestHit(
463     const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
464 {
465     if (rawRecognizer_) {
466         rawRecognizer_->SetCoordinateOffset(coordinateOffset);
467         result.emplace_back(rawRecognizer_);
468     }
469 }
470 
SetTouchHandle(ClickSpringEffectType type)471 void RenderTransform::SetTouchHandle(ClickSpringEffectType type)
472 {
473     if (type == ClickSpringEffectType::NONE) {
474         if (rawRecognizer_) {
475             rawRecognizer_->SetOnTouchUp(nullptr);
476             rawRecognizer_->SetOnTouchDown(nullptr);
477             rawRecognizer_->SetOnTouchCancel(nullptr);
478         }
479     } else {
480         if (!rawRecognizer_) {
481             rawRecognizer_ = AceType::MakeRefPtr<RawRecognizer>();
482         }
483         if (!clickSpringEffect_) {
484             clickSpringEffect_ = AceType::MakeRefPtr<ClickSpringEffect>(GetContext());
485             clickSpringEffect_->SetRenderNode(WeakClaim(this));
486         }
487         auto touchHandle = [weak = AceType::WeakClaim(this)](
488                                const TouchEventInfo&, TouchType touchType, ClickSpringEffectType effectType) {
489             auto transform = weak.Upgrade();
490             if (transform && transform->clickSpringEffect_) {
491                 transform->clickSpringEffect_->ShowAnimation(touchType, effectType);
492             }
493         };
494         rawRecognizer_->SetOnTouchDown(std::bind(touchHandle, std::placeholders::_1, TouchType::DOWN, type));
495         rawRecognizer_->SetOnTouchUp(std::bind(touchHandle, std::placeholders::_1, TouchType::UP, type));
496         rawRecognizer_->SetOnTouchCancel(std::bind(touchHandle, std::placeholders::_1, TouchType::CANCEL, type));
497     }
498 }
499 
UpdateWithEffectMatrix(Matrix4 matrix)500 Matrix4 RenderTransform::UpdateWithEffectMatrix(Matrix4 matrix)
501 {
502     auto animationMatrix = transformAnimation_.ComputerBlendedMatrix4();
503     if (!animationMatrix.IsIdentityMatrix()) {
504         matrix = matrix * animationMatrix;
505     }
506     if (clickSpringEffect_ && !disableClickEffect_) {
507         double scale = clickSpringEffect_->GetScale();
508         if (!NearEqual(scale, 1.0)) {
509             return matrix * Matrix4::CreateScale(scale, scale, 1.0);
510         }
511     }
512 
513     return matrix;
514 }
515 
GetGlobalOffsetExternal() const516 Offset RenderTransform::GetGlobalOffsetExternal() const
517 {
518     auto renderNode = GetParent().Upgrade();
519     auto offset = renderNode ? GetPosition() + renderNode->GetGlobalOffsetExternal() : GetPosition();
520 
521     // transformPaint_[12] is translation of x,transformPaint_[13] is translation of y.
522     offset += Offset(transformPaint_[12], transformPaint_[13]);
523     return offset;
524 }
525 
HasDisappearingTransition(int32_t nodeId)526 bool RenderTransform::HasDisappearingTransition(int32_t nodeId)
527 {
528     return hasDisappearTransition_ || RenderNode::HasDisappearingTransition(nodeId);
529 }
530 
OnTransition(TransitionType type,int32_t id)531 void RenderTransform::OnTransition(TransitionType type, int32_t id)
532 {
533     LOGD("OnTransition. type: %{public}d, id: %{public}d", type, id);
534     if (type == TransitionType::APPEARING) {
535         pendingAppearing_ = true;
536         needUpdateTransform_ = true;
537     } else if (type == TransitionType::DISAPPEARING && hasDisappearTransition_) {
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         transformAnimation_.SetAnimationStopCallback([weak = AceType::WeakClaim(this)]() {
545             auto renderNode = weak.Upgrade();
546             if (renderNode) {
547                 renderNode->OnTransformDisappearingCallback();
548             }
549         });
550         for (auto& effect : transformEffectsDisappearing_) {
551             ParseDimension(effect);
552         }
553         transformAnimation_.PlayTransformAnimation(option, transformEffectsDisappearing_);
554     }
555 }
556 
ClearRenderObject()557 void RenderTransform::ClearRenderObject()
558 {
559     RenderNode::ClearRenderObject();
560     transform_ = Matrix4::CreateIdentity();
561     transformAnimation_.SetTransformOperations(std::vector<TransformOperation>());
562     transformEffects_.Clear();
563 
564     needUpdateTransform_ = false;
565     isFirstAnimation_ = true;
566     origin_ = Offset();
567     originX_ = Dimension();
568     originY_ = Dimension();
569     needUpdateOrigin_ = false;
570 
571     enableTouchTest_ = true;
572     transformPaint_ = Matrix4::CreateIdentity();
573     transitionOption_ = AnimationOption();
574     transformEffectsAppearing_.clear();
575     transformEffectsDisappearing_.clear();
576     hasDisappearTransition_ = false;
577     hasAppearTransition_ = false;
578     pendingAppearing_ = false;
579 }
580 
OnTransformDisappearingCallback()581 void RenderTransform::OnTransformDisappearingCallback()
582 {
583     LOGD("OnTransformDisappearingCallback");
584     RefPtr<RenderNode> child = AceType::Claim(this);
585     while (child && !child->IsDisappearing()) {
586         child = child->GetParent().Upgrade();
587     }
588     if (!child) {
589         return;
590     }
591     auto parent = child->GetParent().Upgrade();
592     if (parent) {
593         parent->ClearDisappearingNode(child);
594     }
595 }
596 
GetTransformByOffset(Matrix4 matrix,const Offset & offset)597 Matrix4 RenderTransform::GetTransformByOffset(Matrix4 matrix, const Offset& offset)
598 {
599     LOGD("Offset(%{public}lf, %{public}lf)", offset.GetX(), offset.GetY());
600     if (offset.IsZero()) {
601         return matrix;
602     }
603 
604     Matrix4 transform =
605         Matrix4::CreateTranslate(static_cast<float>(-offset.GetX()), static_cast<float>(-offset.GetY()), 0.0f);
606     transform = matrix * transform;
607     transform = Matrix4::CreateTranslate(static_cast<float>(offset.GetX()), static_cast<float>(offset.GetY()), 0.0f) *
608                 transform;
609     return transform;
610 }
611 
612 } // namespace OHOS::Ace
613