• 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(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     LOGD("set transform touchable status: %{public}d", enable);
370     enableTouchTest_ = enable;
371 }
372 
Update(const RefPtr<Component> & component)373 void RenderTransform::Update(const RefPtr<Component>& component)
374 {
375     auto transform = AceType::DynamicCast<TransformComponent>(component);
376     if (transform == nullptr) {
377         LOGE("transform component is nullptr.");
378         return;
379     }
380     ResetTransform();
381     needUpdateTransform_ = true;
382     transform_ = transform->GetTransform();
383     transformEffects_ = transform->GetTransformEffects();
384     hasAppearTransition_ = transform->HasAppearTransition();
385     if (hasAppearTransition_) {
386         transformEffectsAppearing_ = transform->GetTransformEffectsAppearing();
387     } else {
388         transformEffectsAppearing_.clear();
389     }
390     hasDisappearTransition_ = transform->HasDisappearTransition();
391     if (hasDisappearTransition_) {
392         transformEffectsDisappearing_ = transform->GetTransformEffectsDisappearing();
393     } else {
394         transformEffectsDisappearing_.clear();
395     }
396     transitionOption_ = context_.Upgrade()->GetExplicitAnimationOption();
397     originX_ = transform->GetOriginDimension().GetX();
398     originY_ = transform->GetOriginDimension().GetY();
399     SetTouchHandle(transform->GetClickSpringEffectType());
400     SetShadow(transform->GetShadow());
401     MarkNeedLayout();
402 }
403 
PerformLayout()404 void RenderTransform::PerformLayout()
405 {
406     LOGD("RenderTransform::PerformLayout");
407     auto child = GetFirstChild();
408     if (child == nullptr) {
409         LOGE("child component is nullptr.");
410         return;
411     }
412 
413     Size layoutSize;
414     LayoutParam innerLayout;
415     Size maxLayoutSize = GetLayoutParam().GetMaxSize();
416     if (!(maxLayoutSize < Size())) {
417         innerLayout.SetMaxSize(maxLayoutSize);
418         child->Layout(innerLayout);
419         layoutSize = child->GetLayoutSize();
420     }
421     SetLayoutSize(layoutSize);
422     needUpdateOrigin_ = true;
423     MarkNeedSyncGeometryProperties();
424 }
425 
UpdateTransformOrigin()426 void RenderTransform::UpdateTransformOrigin()
427 {
428     auto child = GetFirstChild();
429     if (child == nullptr) {
430         LOGE("child component is nullptr.");
431         return;
432     }
433 
434     if (AceType::InstanceOf<RenderBoxBase>(child) && AceType::InstanceOf<RenderBoxBase>(child->GetFirstChild())) {
435         child = child->GetFirstChild();
436     }
437 
438     Size layoutSize = GetLayoutSize();
439     const auto& renderBoxBase = AceType::DynamicCast<RenderBoxBase>(child);
440     if (renderBoxBase) {
441         auto margin = renderBoxBase->GetMargin();
442         double marginTop = margin.TopPx();
443         double marginLeft = margin.LeftPx();
444         double marginBottom = margin.BottomPx();
445         double marginRight = margin.RightPx();
446         double paintWidthSize = layoutSize.Width() - marginLeft - marginRight;
447         double paintHeightSize = layoutSize.Height() - marginTop - marginBottom;
448         origin_.SetX(CovertDimensionToPxBySize(originX_, paintWidthSize) + marginLeft);
449         origin_.SetY(CovertDimensionToPxBySize(originY_, paintHeightSize) + marginTop);
450     } else {
451         origin_.SetX(CovertDimensionToPxBySize(originX_, layoutSize.Width()));
452         origin_.SetY(CovertDimensionToPxBySize(originY_, layoutSize.Height()));
453     }
454 }
455 
CovertDimensionToPxBySize(const Dimension & dimension,double size)456 double RenderTransform::CovertDimensionToPxBySize(const Dimension& dimension, double size)
457 {
458     double result = 0.0;
459     if (dimension.Unit() == DimensionUnit::PERCENT) {
460         result = dimension.Value() * size;
461     } else if (dimension.Unit() == DimensionUnit::VP) {
462         result = NormalizeToPx(dimension);
463     } else {
464         result = dimension.Value();
465     }
466     return result;
467 };
468 
OnTouchTestHit(const Offset & coordinateOffset,const TouchRestrict & touchRestrict,TouchTestResult & result)469 void RenderTransform::OnTouchTestHit(
470     const Offset& coordinateOffset, const TouchRestrict& touchRestrict, TouchTestResult& result)
471 {
472     if (rawRecognizer_) {
473         rawRecognizer_->SetCoordinateOffset(coordinateOffset);
474         result.emplace_back(rawRecognizer_);
475     }
476 }
477 
SetTouchHandle(ClickSpringEffectType type)478 void RenderTransform::SetTouchHandle(ClickSpringEffectType type)
479 {
480     if (type == ClickSpringEffectType::NONE) {
481         if (rawRecognizer_) {
482             rawRecognizer_->SetOnTouchUp(nullptr);
483             rawRecognizer_->SetOnTouchDown(nullptr);
484             rawRecognizer_->SetOnTouchCancel(nullptr);
485         }
486     } else {
487         if (!rawRecognizer_) {
488             rawRecognizer_ = AceType::MakeRefPtr<RawRecognizer>();
489         }
490         if (!clickSpringEffect_) {
491             clickSpringEffect_ = AceType::MakeRefPtr<ClickSpringEffect>(GetContext());
492             clickSpringEffect_->SetRenderNode(WeakClaim(this));
493         }
494         auto touchHandle = [weak = AceType::WeakClaim(this)](
495                                const TouchEventInfo&, TouchType touchType, ClickSpringEffectType effectType) {
496             auto transform = weak.Upgrade();
497             if (transform && transform->clickSpringEffect_) {
498                 transform->clickSpringEffect_->ShowAnimation(touchType, effectType);
499             }
500         };
501         rawRecognizer_->SetOnTouchDown(std::bind(touchHandle, std::placeholders::_1, TouchType::DOWN, type));
502         rawRecognizer_->SetOnTouchUp(std::bind(touchHandle, std::placeholders::_1, TouchType::UP, type));
503         rawRecognizer_->SetOnTouchCancel(std::bind(touchHandle, std::placeholders::_1, TouchType::CANCEL, type));
504     }
505 }
506 
UpdateWithEffectMatrix(Matrix4 matrix)507 Matrix4 RenderTransform::UpdateWithEffectMatrix(Matrix4 matrix)
508 {
509     auto animationMatrix = transformAnimation_.ComputerBlendedMatrix4();
510     if (!animationMatrix.IsIdentityMatrix()) {
511         matrix = matrix * animationMatrix;
512     }
513     if (clickSpringEffect_ && !disableClickEffect_) {
514         double scale = clickSpringEffect_->GetScale();
515         if (!NearEqual(scale, 1.0)) {
516             return matrix * Matrix4::CreateScale(scale, scale, 1.0);
517         }
518     }
519 
520     return matrix;
521 }
522 
GetGlobalOffsetExternal() const523 Offset RenderTransform::GetGlobalOffsetExternal() const
524 {
525     auto renderNode = GetParent().Upgrade();
526     auto offset = renderNode ? GetPosition() + renderNode->GetGlobalOffsetExternal() : GetPosition();
527 
528     // transformPaint_[12] is translation of x,transformPaint_[13] is translation of y.
529     offset += Offset(transform_[12], transform_[13]);
530     return offset;
531 }
532 
HasDisappearingTransition(int32_t nodeId)533 bool RenderTransform::HasDisappearingTransition(int32_t nodeId)
534 {
535     return hasDisappearTransition_ || RenderNode::HasDisappearingTransition(nodeId);
536 }
537 
OnTransition(TransitionType type,int32_t id)538 void RenderTransform::OnTransition(TransitionType type, int32_t id)
539 {
540     LOGD("OnTransition. type: %{public}d, id: %{public}d", type, id);
541     auto context = context_.Upgrade();
542     if (!context) {
543         LOGE("OnTransition failed, context_ is null.");
544         return;
545     }
546     const auto& option = context->GetExplicitAnimationOption();
547     if (!option.IsValid()) {
548         LOGE("transition option is not valid.");
549         return;
550     }
551     if (type == TransitionType::APPEARING) {
552         pendingAppearing_ = true;
553         needUpdateTransform_ = true;
554     } else if (type == TransitionType::DISAPPEARING && hasDisappearTransition_) {
555         transformAnimation_.SetAnimationStopCallback([weak = AceType::WeakClaim(this)]() {
556             auto renderNode = weak.Upgrade();
557             if (renderNode) {
558                 renderNode->OnTransformDisappearingCallback();
559             }
560         });
561         for (auto& effect : transformEffectsDisappearing_) {
562             ParseDimension(effect);
563         }
564         transformAnimation_.PlayTransformAnimation(option, transformEffectsDisappearing_);
565     }
566 }
567 
ClearRenderObject()568 void RenderTransform::ClearRenderObject()
569 {
570     RenderNode::ClearRenderObject();
571     transform_ = Matrix4::CreateIdentity();
572     transformAnimation_.SetTransformOperations(std::vector<TransformOperation>());
573     transformEffects_.Clear();
574 
575     needUpdateTransform_ = false;
576     isFirstAnimation_ = true;
577     origin_ = Offset();
578     originX_ = Dimension();
579     originY_ = Dimension();
580     needUpdateOrigin_ = false;
581 
582     enableTouchTest_ = true;
583     transformPaint_ = Matrix4::CreateIdentity();
584     transitionOption_ = AnimationOption();
585     transformEffectsAppearing_.clear();
586     transformEffectsDisappearing_.clear();
587     hasDisappearTransition_ = false;
588     hasAppearTransition_ = false;
589     pendingAppearing_ = false;
590 }
591 
OnTransformDisappearingCallback()592 void RenderTransform::OnTransformDisappearingCallback()
593 {
594     LOGD("OnTransformDisappearingCallback");
595     RefPtr<RenderNode> child = AceType::Claim(this);
596     while (child && !child->IsDisappearing()) {
597         child = child->GetParent().Upgrade();
598     }
599     if (!child) {
600         return;
601     }
602     auto parent = child->GetParent().Upgrade();
603     if (parent) {
604         parent->ClearDisappearingNode(child);
605     }
606 }
607 
GetTransformByOffset(Matrix4 matrix,const Offset & offset)608 Matrix4 RenderTransform::GetTransformByOffset(Matrix4 matrix, const Offset& offset)
609 {
610     LOGD("Offset(%{public}lf, %{public}lf)", offset.GetX(), offset.GetY());
611     if (offset.IsZero()) {
612         return matrix;
613     }
614 
615     Matrix4 transform =
616         Matrix4::CreateTranslate(static_cast<float>(-offset.GetX()), static_cast<float>(-offset.GetY()), 0.0f);
617     transform = matrix * transform;
618     transform = Matrix4::CreateTranslate(static_cast<float>(offset.GetX()), static_cast<float>(offset.GetY()), 0.0f) *
619                 transform;
620     return transform;
621 }
622 
623 } // namespace OHOS::Ace
624