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