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 "frameworks/bridge/declarative_frontend/engine/jsi/modules/jsi_matrix4_module.h"
17
18 #include "base/geometry/matrix4.h"
19 #include "base/log/log.h"
20 #include "frameworks/bridge/js_frontend/engine/common/js_constants.h"
21
22 namespace OHOS::Ace::Framework {
23
24 namespace {
25
26 constexpr int32_t MATRIX_LENGTH = 16;
27
ConvertToMatrix(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & value)28 Matrix4 ConvertToMatrix(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& value)
29 {
30 Matrix4 result = Matrix4::CreateIdentity();
31 if (value->GetArrayLength(runtime) != MATRIX_LENGTH) {
32 LOGE("ConvertToMatrix failed, JSValueConst length != %d", MATRIX_LENGTH);
33 return result;
34 }
35 // in column order
36 for (int32_t i = 0; i < Matrix4::DIMENSION; i++) {
37 for (int32_t j = 0; j < Matrix4::DIMENSION; j++) {
38 auto index = i * Matrix4::DIMENSION + j;
39 auto itemJSValue = value->GetProperty(runtime, index);
40 if (!itemJSValue->IsNumber(runtime)) {
41 LOGE("ConvertToMatrix failed, %s is not number", itemJSValue->ToString(runtime).c_str());
42 return result;
43 }
44 result.Set(j, i, static_cast<float>(itemJSValue->ToDouble(runtime)));
45 }
46 }
47 return result;
48 }
49
ConvertToJSValue(const shared_ptr<JsRuntime> & runtime,const Matrix4 & matrix)50 shared_ptr<JsValue> ConvertToJSValue(const shared_ptr<JsRuntime>& runtime, const Matrix4& matrix)
51 {
52 shared_ptr<JsValue> result = runtime->NewArray();
53 for (int32_t i = 0; i < Matrix4::DIMENSION; i++) {
54 for (int32_t j = 0; j < Matrix4::DIMENSION; j++) {
55 int32_t index = i * Matrix4::DIMENSION + j;
56 result->SetProperty(runtime, runtime->NewInt32(index), runtime->NewNumber(matrix.Get(j, i)));
57 }
58 }
59 return result;
60 }
61
ConvertToDouble(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & value,double defaultValue)62 double ConvertToDouble(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& value, double defaultValue)
63 {
64 if (value == nullptr || !value->IsNumber(runtime)) {
65 return defaultValue;
66 }
67 return value->ToDouble(runtime);
68 }
69
70 }
71
Combine(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)72 shared_ptr<JsValue> Combine(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
73 const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
74 {
75 if (argc != 1) {
76 LOGE("Combine failed, argc != 1");
77 return runtime->NewNull();
78 }
79 if (!argv[0]->IsObject(runtime)) {
80 LOGE("Combine failed, argv[0] is not object");
81 return runtime->NewNull();
82 }
83
84 auto objA = argv[0]->GetProperty(runtime, MATRIX_4X4);
85 auto objB = thisObj->GetProperty(runtime, MATRIX_4X4);
86 auto matrixA = ConvertToMatrix(runtime, objA);
87 auto matrixB = ConvertToMatrix(runtime, objB);
88 auto newArrayJSValue = ConvertToJSValue(runtime, matrixA * matrixB);
89 thisObj->SetProperty(runtime, MATRIX_4X4, newArrayJSValue);
90 return thisObj;
91 }
92
Invert(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)93 shared_ptr<JsValue> Invert(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
94 const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
95 {
96 auto matrixArray = thisObj->GetProperty(runtime, MATRIX_4X4);
97 auto matrix = ConvertToMatrix(runtime, matrixArray);
98 matrix = Matrix4::Invert(matrix);
99 matrixArray = ConvertToJSValue(runtime, matrix);
100 thisObj->SetProperty(runtime, MATRIX_4X4, matrixArray);
101 return thisObj;
102 }
103
Translate(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)104 shared_ptr<JsValue> Translate(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
105 const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
106 {
107 if (argc != 1) {
108 LOGE("Translate failed, argc != 1");
109 return runtime->NewNull();
110 }
111 if (!argv[0]->IsObject(runtime)) {
112 LOGE("Translate failed, argv[0] is not object");
113 return runtime->NewNull();
114 }
115
116 auto matrixArray = thisObj->GetProperty(runtime, MATRIX_4X4);
117 auto matrix = ConvertToMatrix(runtime, matrixArray);
118 auto dxJSValue = argv[0]->GetProperty(runtime, "x");
119 double dx = ConvertToDouble(runtime, dxJSValue, 0.0);
120 auto dyJSValue = argv[0]->GetProperty(runtime, "y");
121 double dy = ConvertToDouble(runtime, dyJSValue, 0.0);
122 auto dzJSValue = argv[0]->GetProperty(runtime, "z");
123 double dz = ConvertToDouble(runtime, dzJSValue, 0.0);
124
125 matrix = Matrix4::CreateTranslate(static_cast<float>(dx), static_cast<float>(dy), static_cast<float>(dz)) * matrix;
126 thisObj->SetProperty(runtime, MATRIX_4X4, ConvertToJSValue(runtime, matrix));
127 return thisObj;
128 }
129
Scale(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)130 shared_ptr<JsValue> Scale(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
131 const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
132 {
133 if (argc != 1) {
134 LOGE("Scale failed, argc != 1");
135 return runtime->NewNull();
136 }
137 if (!argv[0]->IsObject(runtime)) {
138 LOGE("Scale failed, argv[0] is not object");
139 return runtime->NewNull();
140 }
141
142 auto matrixArray = thisObj->GetProperty(runtime, MATRIX_4X4);
143 auto matrix = ConvertToMatrix(runtime, matrixArray);
144 auto dxJSValue = argv[0]->GetProperty(runtime, "x");
145 double dx = ConvertToDouble(runtime, dxJSValue, 1.0);
146 auto dyJSValue = argv[0]->GetProperty(runtime, "y");
147 double dy = ConvertToDouble(runtime, dyJSValue, 1.0);
148 auto dzJSValue = argv[0]->GetProperty(runtime, "z");
149 double dz = ConvertToDouble(runtime, dzJSValue, 1.0);
150 auto centerXJSValue = argv[0]->GetProperty(runtime, "centerX");
151 double centerX = ConvertToDouble(runtime, centerXJSValue, 0.0);
152 auto centerYJSValue = argv[0]->GetProperty(runtime, "centerY");
153 double centerY = ConvertToDouble(runtime, centerYJSValue, 0.0);
154
155 auto scaleMatrix = Matrix4::CreateScale(dx, dy, dz);
156 if (!NearZero(centerX) || !NearZero(centerY)) {
157 auto translate1 = Matrix4::CreateTranslate(centerX, centerY, 0.0);
158 auto translate2 = Matrix4::CreateTranslate(-centerX, -centerY, 0.0);
159 scaleMatrix = scaleMatrix * translate2;
160 scaleMatrix = translate1 * scaleMatrix;
161 }
162 matrix = scaleMatrix * matrix;
163 thisObj->SetProperty(runtime, MATRIX_4X4, ConvertToJSValue(runtime, matrix));
164 return thisObj;
165 }
166
Rotate(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)167 shared_ptr<JsValue> Rotate(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
168 const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
169 {
170 if (argc != 1) {
171 LOGE("Rotate failed, argc != 1");
172 return runtime->NewNull();
173 }
174 if (!argv[0]->IsObject(runtime)) {
175 LOGE("Rotate failed, argv[0] is not object");
176 return runtime->NewNull();
177 }
178
179 auto matrixArray = thisObj->GetProperty(runtime, MATRIX_4X4);
180 auto matrix = ConvertToMatrix(runtime, matrixArray);
181 auto dxJSValue = argv[0]->GetProperty(runtime, "x");
182 double dx = ConvertToDouble(runtime, dxJSValue, 0.0);
183 auto dyJSValue = argv[0]->GetProperty(runtime, "y");
184 double dy = ConvertToDouble(runtime, dyJSValue, 0.0);
185 auto dzJSValue = argv[0]->GetProperty(runtime, "z");
186 double dz = ConvertToDouble(runtime, dzJSValue, 0.0);
187 auto angleJSValue = argv[0]->GetProperty(runtime, "angle");
188 double angle = ConvertToDouble(runtime, angleJSValue, 0.0);
189 auto centerXJSValue = argv[0]->GetProperty(runtime, "centerX");
190 double centerX = ConvertToDouble(runtime, centerXJSValue, 0.0);
191 auto centerYJSValue = argv[0]->GetProperty(runtime, "centerY");
192 double centerY = ConvertToDouble(runtime, centerYJSValue, 0.0);
193
194 auto rotateMatrix = Matrix4::CreateRotate(angle, dx, dy, dz);
195 if (!NearZero(centerX) || !NearZero(centerY)) {
196 auto translate1 = Matrix4::CreateTranslate(centerX, centerY, 0.0);
197 auto translate2 = Matrix4::CreateTranslate(-centerX, -centerY, 0.0);
198 rotateMatrix = rotateMatrix * translate2;
199 rotateMatrix = translate1 * rotateMatrix;
200 }
201 matrix = rotateMatrix * matrix;
202 matrixArray = ConvertToJSValue(runtime, matrix);
203 thisObj->SetProperty(runtime, MATRIX_4X4, ConvertToJSValue(runtime, matrix));
204 return thisObj;
205 }
206
TransformPoint(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)207 shared_ptr<JsValue> TransformPoint(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
208 const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
209 {
210 if (argc != 1) {
211 LOGE("TransformPoint failed, argc != 1");
212 return runtime->NewNull();
213 }
214 if (!argv[0]->IsArray(runtime) || argv[0]->GetArrayLength(runtime) != 2) {
215 LOGE("TransformPoint failed, argv[0] is not array or length != 2");
216 return runtime->NewNull();
217 }
218 auto matrix = ConvertToMatrix(runtime, thisObj->GetProperty(runtime, MATRIX_4X4));
219
220 auto pointXJSValue = argv[0]->GetProperty(runtime, 0);
221 double pointX = ConvertToDouble(runtime, pointXJSValue, 0.0);
222
223 auto pointYJSValue = argv[0]->GetProperty(runtime, 1);
224 double pointY = ConvertToDouble(runtime, pointYJSValue, 0.0);
225
226 Point point { pointX, pointY };
227 Point target = matrix * point;
228 shared_ptr<JsValue> result = runtime->NewArray();
229 result->SetProperty(runtime, runtime->NewInt32(0), runtime->NewNumber(target.GetX()));
230 result->SetProperty(runtime, runtime->NewInt32(1), runtime->NewNumber(target.GetY()));
231 return result;
232 }
233
234 shared_ptr<JsValue> Copy(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
235 const std::vector<shared_ptr<JsValue>>& argv, int32_t argc);
236
AddCommonMatrixProperties(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & obj)237 void AddCommonMatrixProperties(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& obj)
238 {
239 obj->SetProperty(runtime, MATRIX_COPY, runtime->NewFunction(Copy));
240 obj->SetProperty(runtime, MATRIX_COMBINE, runtime->NewFunction(Combine));
241 obj->SetProperty(runtime, MATRIX_INVERT, runtime->NewFunction(Invert));
242 obj->SetProperty(runtime, MATRIX_TRANSLATE, runtime->NewFunction(Translate));
243 obj->SetProperty(runtime, MATRIX_SCALE, runtime->NewFunction(Scale));
244 obj->SetProperty(runtime, MATRIX_ROTATE, runtime->NewFunction(Rotate));
245 obj->SetProperty(runtime, MATRIX_TRANSFORM_POINT, runtime->NewFunction(TransformPoint));
246 }
247
Copy(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)248 shared_ptr<JsValue> Copy(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
249 const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
250 {
251 auto matrix = ConvertToMatrix(runtime, thisObj->GetProperty(runtime, MATRIX_4X4));
252 // create new object
253 shared_ptr<JsValue> other = runtime->NewObject();
254 // init functions
255 InitMatrix4Module(runtime, other);
256 // update matrix4x4
257 other->SetProperty(runtime, MATRIX_4X4, ConvertToJSValue(runtime, matrix));
258 AddCommonMatrixProperties(runtime, other);
259 return other;
260 }
261
Init(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)262 shared_ptr<JsValue> Init(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
263 const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
264 {
265 if (argc != 1) {
266 LOGE("PagePush args count is invalid");
267 return runtime->NewNull();
268 }
269
270 auto matrix = ConvertToMatrix(runtime, argv[0]);
271 shared_ptr<JsValue> matrixObj = runtime->NewObject();
272 matrixObj->SetProperty(runtime, MATRIX_4X4, ConvertToJSValue(runtime, matrix));
273 AddCommonMatrixProperties(runtime, matrixObj);
274 return matrixObj;
275 }
276
Identity(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)277 shared_ptr<JsValue> Identity(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
278 const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
279 {
280 shared_ptr<JsValue> matrixObj = runtime->NewObject();
281 matrixObj->SetProperty(runtime, MATRIX_4X4, ConvertToJSValue(runtime, Matrix4::CreateIdentity()));
282 AddCommonMatrixProperties(runtime, matrixObj);
283 return matrixObj;
284 }
285
InitMatrix4Module(const shared_ptr<JsRuntime> & runtime,shared_ptr<JsValue> & moduleObj)286 void InitMatrix4Module(const shared_ptr<JsRuntime>& runtime, shared_ptr<JsValue>& moduleObj)
287 {
288 moduleObj->SetProperty(runtime, MATRIX_4X4, ConvertToJSValue(runtime, Matrix4::CreateIdentity()));
289 moduleObj->SetProperty(runtime, MATRIX_INIT, runtime->NewFunction(Init));
290 moduleObj->SetProperty(runtime, MATRIX_IDENTITY, runtime->NewFunction(Identity));
291 }
292
293 } // namespace OHOS::Ace::Framework
294