• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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