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