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
Init(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)72 shared_ptr<JsValue> Init(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("PagePush args count is invalid");
77 return runtime->NewNull();
78 }
79
80 int32_t length = argv[0]->GetArrayLength(runtime);
81 if (length != MATRIX_LENGTH) {
82 LOGE("matrix init array length %d not equal %d", length, MATRIX_LENGTH);
83 return runtime->NewNull();
84 }
85
86 auto matrix = ConvertToMatrix(runtime, argv[0]);
87 thisObj->SetProperty(runtime, MATRIX_4X4, ConvertToJSValue(runtime, matrix));
88 return thisObj;
89 }
90
Combine(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)91 shared_ptr<JsValue> Combine(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
92 const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
93 {
94 if (argc != 1) {
95 LOGE("Combine failed, argc != 1");
96 return runtime->NewNull();
97 }
98 if (!argv[0]->IsObject(runtime)) {
99 LOGE("Combine failed, argv[0] is not object");
100 return runtime->NewNull();
101 }
102
103 auto objA = argv[0]->GetProperty(runtime, MATRIX_4X4);
104 auto objB = thisObj->GetProperty(runtime, MATRIX_4X4);
105 auto matrixA = ConvertToMatrix(runtime, objA);
106 auto matrixB = ConvertToMatrix(runtime, objB);
107 auto newArrayJSValue = ConvertToJSValue(runtime, matrixA * matrixB);
108 thisObj->SetProperty(runtime, MATRIX_4X4, newArrayJSValue);
109 return thisObj;
110 }
111
Invert(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)112 shared_ptr<JsValue> Invert(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
113 const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
114 {
115 auto matrixArray = thisObj->GetProperty(runtime, MATRIX_4X4);
116 auto matrix = ConvertToMatrix(runtime, matrixArray);
117 matrix = Matrix4::Invert(matrix);
118 matrixArray = ConvertToJSValue(runtime, matrix);
119 thisObj->SetProperty(runtime, MATRIX_4X4, matrixArray);
120 return thisObj;
121 }
122
Translate(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)123 shared_ptr<JsValue> Translate(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
124 const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
125 {
126 if (argc != 1) {
127 LOGE("Translate failed, argc != 1");
128 return runtime->NewNull();
129 }
130 if (!argv[0]->IsObject(runtime)) {
131 LOGE("Translate failed, argv[0] is not object");
132 return runtime->NewNull();
133 }
134
135 auto matrixArray = thisObj->GetProperty(runtime, MATRIX_4X4);
136 auto matrix = ConvertToMatrix(runtime, matrixArray);
137 auto dxJSValue = argv[0]->GetProperty(runtime, "x");
138 double dx = ConvertToDouble(runtime, dxJSValue, 0.0);
139 auto dyJSValue = argv[0]->GetProperty(runtime, "y");
140 double dy = ConvertToDouble(runtime, dyJSValue, 0.0);
141 auto dzJSValue = argv[0]->GetProperty(runtime, "z");
142 double dz = ConvertToDouble(runtime, dzJSValue, 0.0);
143
144 matrix = Matrix4::CreateTranslate(static_cast<float>(dx), static_cast<float>(dy), static_cast<float>(dz)) * matrix;
145 thisObj->SetProperty(runtime, MATRIX_4X4, ConvertToJSValue(runtime, matrix));
146 return thisObj;
147 }
148
Scale(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)149 shared_ptr<JsValue> Scale(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
150 const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
151 {
152 if (argc != 1) {
153 LOGE("Scale failed, argc != 1");
154 return runtime->NewNull();
155 }
156 if (!argv[0]->IsObject(runtime)) {
157 LOGE("Scale failed, argv[0] is not object");
158 return runtime->NewNull();
159 }
160
161 auto matrixArray = thisObj->GetProperty(runtime, MATRIX_4X4);
162 auto matrix = ConvertToMatrix(runtime, matrixArray);
163 auto dxJSValue = argv[0]->GetProperty(runtime, "x");
164 double dx = ConvertToDouble(runtime, dxJSValue, 1.0);
165 auto dyJSValue = argv[0]->GetProperty(runtime, "y");
166 double dy = ConvertToDouble(runtime, dyJSValue, 1.0);
167 auto dzJSValue = argv[0]->GetProperty(runtime, "z");
168 double dz = ConvertToDouble(runtime, dzJSValue, 1.0);
169 auto centerXJSValue = argv[0]->GetProperty(runtime, "centerX");
170 double centerX = ConvertToDouble(runtime, centerXJSValue, 0.0);
171 auto centerYJSValue = argv[0]->GetProperty(runtime, "centerY");
172 double centerY = ConvertToDouble(runtime, centerYJSValue, 0.0);
173
174 auto scaleMatrix = Matrix4::CreateScale(dx, dy, dz);
175 if (!NearZero(centerX) || !NearZero(centerY)) {
176 auto translate1 = Matrix4::CreateTranslate(centerX, centerY, 0.0);
177 auto translate2 = Matrix4::CreateTranslate(-centerX, -centerY, 0.0);
178 scaleMatrix = scaleMatrix * translate2;
179 scaleMatrix = translate1 * scaleMatrix;
180 }
181 matrix = scaleMatrix * matrix;
182 thisObj->SetProperty(runtime, MATRIX_4X4, ConvertToJSValue(runtime, matrix));
183 return thisObj;
184 }
185
Rotate(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)186 shared_ptr<JsValue> Rotate(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
187 const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
188 {
189 if (argc != 1) {
190 LOGE("Rotate failed, argc != 1");
191 return runtime->NewNull();
192 }
193 if (!argv[0]->IsObject(runtime)) {
194 LOGE("Rotate failed, argv[0] is not object");
195 return runtime->NewNull();
196 }
197
198 auto matrixArray = thisObj->GetProperty(runtime, MATRIX_4X4);
199 auto matrix = ConvertToMatrix(runtime, matrixArray);
200 auto dxJSValue = argv[0]->GetProperty(runtime, "x");
201 double dx = ConvertToDouble(runtime, dxJSValue, 0.0);
202 auto dyJSValue = argv[0]->GetProperty(runtime, "y");
203 double dy = ConvertToDouble(runtime, dyJSValue, 0.0);
204 auto dzJSValue = argv[0]->GetProperty(runtime, "z");
205 double dz = ConvertToDouble(runtime, dzJSValue, 0.0);
206 auto angleJSValue = argv[0]->GetProperty(runtime, "angle");
207 double angle = ConvertToDouble(runtime, angleJSValue, 0.0);
208 auto centerXJSValue = argv[0]->GetProperty(runtime, "centerX");
209 double centerX = ConvertToDouble(runtime, centerXJSValue, 0.0);
210 auto centerYJSValue = argv[0]->GetProperty(runtime, "centerY");
211 double centerY = ConvertToDouble(runtime, centerYJSValue, 0.0);
212
213 auto rotateMatrix = Matrix4::CreateRotate(angle, dx, dy, dz);
214 if (!NearZero(centerX) || !NearZero(centerY)) {
215 auto translate1 = Matrix4::CreateTranslate(centerX, centerY, 0.0);
216 auto translate2 = Matrix4::CreateTranslate(-centerX, -centerY, 0.0);
217 rotateMatrix = rotateMatrix * translate2;
218 rotateMatrix = translate1 * rotateMatrix;
219 }
220 matrix = rotateMatrix * matrix;
221 matrixArray = ConvertToJSValue(runtime, matrix);
222 thisObj->SetProperty(runtime, MATRIX_4X4, ConvertToJSValue(runtime, matrix));
223 return thisObj;
224 }
225
TransformPoint(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)226 shared_ptr<JsValue> TransformPoint(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
227 const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
228 {
229 if (argc != 1) {
230 LOGE("TransformPoint failed, argc != 1");
231 return runtime->NewNull();
232 }
233 if (!argv[0]->IsArray(runtime) || argv[0]->GetArrayLength(runtime) != 2) {
234 LOGE("TransformPoint failed, argv[0] is not array or length != 2");
235 return runtime->NewNull();
236 }
237 auto matrix = ConvertToMatrix(runtime, thisObj->GetProperty(runtime, MATRIX_4X4));
238
239 auto pointXJSValue = argv[0]->GetProperty(runtime, 0);
240 double pointX = ConvertToDouble(runtime, pointXJSValue, 0.0);
241
242 auto pointYJSValue = argv[0]->GetProperty(runtime, 1);
243 double pointY = ConvertToDouble(runtime, pointYJSValue, 0.0);
244
245 Point point { pointX, pointY };
246 Point target = matrix * point;
247 shared_ptr<JsValue> result = runtime->NewArray();
248 result->SetProperty(runtime, runtime->NewInt32(0), runtime->NewNumber(target.GetX()));
249 result->SetProperty(runtime, runtime->NewInt32(1), runtime->NewNumber(target.GetY()));
250 return result;
251 }
252
Copy(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)253 shared_ptr<JsValue> Copy(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
254 const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
255 {
256 auto matrix = ConvertToMatrix(runtime, thisObj->GetProperty(runtime, MATRIX_4X4));
257 // create new object
258 shared_ptr<JsValue> other = runtime->NewObject();
259 // init functions
260 InitMatrix4Module(runtime, other);
261 // update matrix4x4
262 other->SetProperty(runtime, MATRIX_4X4, ConvertToJSValue(runtime, matrix));
263 other->SetProperty(runtime, MATRIX_COPY, runtime->NewFunction(Copy));
264 other->SetProperty(runtime, MATRIX_COMBINE, runtime->NewFunction(Combine));
265 other->SetProperty(runtime, MATRIX_INVERT, runtime->NewFunction(Invert));
266 other->SetProperty(runtime, MATRIX_TRANSLATE, runtime->NewFunction(Translate));
267 other->SetProperty(runtime, MATRIX_SCALE, runtime->NewFunction(Scale));
268 other->SetProperty(runtime, MATRIX_ROTATE, runtime->NewFunction(Rotate));
269 other->SetProperty(runtime, MATRIX_TRANSFORM_POINT, runtime->NewFunction(TransformPoint));
270 return other;
271 }
272
Identity(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)273 shared_ptr<JsValue> Identity(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
274 const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
275 {
276 shared_ptr<JsValue> matrixObj = runtime->NewObject();
277 matrixObj->SetProperty(runtime, MATRIX_4X4, ConvertToJSValue(runtime, Matrix4::CreateIdentity()));
278 matrixObj->SetProperty(runtime, MATRIX_COPY, runtime->NewFunction(Copy));
279 matrixObj->SetProperty(runtime, MATRIX_COMBINE, runtime->NewFunction(Combine));
280 matrixObj->SetProperty(runtime, MATRIX_INVERT, runtime->NewFunction(Invert));
281 matrixObj->SetProperty(runtime, MATRIX_TRANSLATE, runtime->NewFunction(Translate));
282 matrixObj->SetProperty(runtime, MATRIX_SCALE, runtime->NewFunction(Scale));
283 matrixObj->SetProperty(runtime, MATRIX_ROTATE, runtime->NewFunction(Rotate));
284 matrixObj->SetProperty(runtime, MATRIX_TRANSFORM_POINT, runtime->NewFunction(TransformPoint));
285 return matrixObj;
286 }
287
InitMatrix4Module(const shared_ptr<JsRuntime> & runtime,shared_ptr<JsValue> & moduleObj)288 void InitMatrix4Module(const shared_ptr<JsRuntime>& runtime, shared_ptr<JsValue>& moduleObj)
289 {
290 moduleObj->SetProperty(runtime, MATRIX_4X4, ConvertToJSValue(runtime, Matrix4::CreateIdentity()));
291 moduleObj->SetProperty(runtime, MATRIX_INIT, runtime->NewFunction(Init));
292 moduleObj->SetProperty(runtime, MATRIX_IDENTITY, runtime->NewFunction(Identity));
293 }
294
295 } // namespace OHOS::Ace::Framework
296