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 "bridge/declarative_frontend/engine/js_types.h"
20 #include "bridge/declarative_frontend/engine/js_ref_ptr.h"
21 #include "core/components_ng/base/view_abstract_model_ng.h"
22 #include "core/components_ng/render/render_context.h"
23 #include "frameworks/base/geometry/ng/point_t.h"
24 #include "frameworks/bridge/js_frontend/engine/common/js_constants.h"
25 #include "frameworks/core/components_ng/render/adapter/matrix2d.h"
26 
27 namespace OHOS::Ace::Framework {
28 
29 namespace {
30 
31 constexpr int32_t MATRIX_LENGTH = 16;
32 constexpr int32_t ARGS_COUNT_TWO = 2;
33 
ConvertToMatrix(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & value)34 Matrix4 ConvertToMatrix(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& value)
35 {
36     Matrix4 result = Matrix4::CreateIdentity();
37     if (value->GetArrayLength(runtime) != MATRIX_LENGTH) {
38         return result;
39     }
40     // in column order
41     for (int32_t i = 0; i < Matrix4::DIMENSION; i++) {
42         for (int32_t j = 0; j < Matrix4::DIMENSION; j++) {
43             auto index = i * Matrix4::DIMENSION + j;
44             auto itemJSValue = value->GetProperty(runtime, index);
45             if (!itemJSValue->IsNumber(runtime)) {
46                 return result;
47             }
48             result.Set(j, i, static_cast<float>(itemJSValue->ToDouble(runtime)));
49         }
50     }
51     return result;
52 }
53 
ConvertToJSValue(const shared_ptr<JsRuntime> & runtime,const Matrix4 & matrix)54 shared_ptr<JsValue> ConvertToJSValue(const shared_ptr<JsRuntime>& runtime, const Matrix4& matrix)
55 {
56     shared_ptr<JsValue> result = runtime->NewArray();
57     for (int32_t i = 0; i < Matrix4::DIMENSION; i++) {
58         for (int32_t j = 0; j < Matrix4::DIMENSION; j++) {
59             int32_t index = i * Matrix4::DIMENSION + j;
60             result->SetProperty(runtime, runtime->NewInt32(index), runtime->NewNumber(matrix.Get(j, i)));
61         }
62     }
63     return result;
64 }
65 
ConvertToDouble(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & value,double defaultValue)66 double ConvertToDouble(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& value, double defaultValue)
67 {
68     if (value == nullptr || !value->IsNumber(runtime)) {
69         return defaultValue;
70     }
71     return value->ToDouble(runtime);
72 }
73 
74 }
75 
Combine(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)76 shared_ptr<JsValue> Combine(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
77     const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
78 {
79     if (argc != 1) {
80         return runtime->NewNull();
81     }
82     if (!argv[0]->IsObject(runtime)) {
83         return runtime->NewNull();
84     }
85 
86     auto objA = argv[0]->GetProperty(runtime, MATRIX_4X4);
87     auto objB = thisObj->GetProperty(runtime, MATRIX_4X4);
88     auto matrixA = ConvertToMatrix(runtime, objA);
89     auto matrixB = ConvertToMatrix(runtime, objB);
90     auto newArrayJSValue = ConvertToJSValue(runtime, matrixA * matrixB);
91     thisObj->SetProperty(runtime, MATRIX_4X4, newArrayJSValue);
92     return thisObj;
93 }
94 
Invert(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)95 shared_ptr<JsValue> Invert(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
96     const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
97 {
98     auto matrixArray = thisObj->GetProperty(runtime, MATRIX_4X4);
99     auto matrix = ConvertToMatrix(runtime, matrixArray);
100     matrix = Matrix4::Invert(matrix);
101     matrixArray = ConvertToJSValue(runtime, matrix);
102     thisObj->SetProperty(runtime, MATRIX_4X4, matrixArray);
103     return thisObj;
104 }
105 
Translate(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)106 shared_ptr<JsValue> Translate(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
107     const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
108 {
109     if (argc != 1) {
110         return runtime->NewNull();
111     }
112     if (!argv[0]->IsObject(runtime)) {
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 
Skew(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)130 shared_ptr<JsValue> Skew(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 != ARGS_COUNT_TWO) {
134         return runtime->NewNull();
135     }
136 
137     auto matrixArray = thisObj->GetProperty(runtime, MATRIX_4X4);
138     auto matrix = ConvertToMatrix(runtime, matrixArray);
139     double sx = ConvertToDouble(runtime, argv[0], 0.0);
140     double sy = ConvertToDouble(runtime, argv[1], 0.0);
141 
142     matrix = Matrix4::CreateFactorSkew(static_cast<float>(sx), static_cast<float>(sy)) * matrix;
143     thisObj->SetProperty(runtime, MATRIX_4X4, ConvertToJSValue(runtime, matrix));
144     return thisObj;
145 }
146 
Scale(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)147 shared_ptr<JsValue> Scale(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
148     const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
149 {
150     if (argc != 1) {
151         return runtime->NewNull();
152     }
153     if (!argv[0]->IsObject(runtime)) {
154         return runtime->NewNull();
155     }
156 
157     auto matrixArray = thisObj->GetProperty(runtime, MATRIX_4X4);
158     auto matrix = ConvertToMatrix(runtime, matrixArray);
159     auto dxJSValue = argv[0]->GetProperty(runtime, "x");
160     double dx = ConvertToDouble(runtime, dxJSValue, 1.0);
161     auto dyJSValue = argv[0]->GetProperty(runtime, "y");
162     double dy = ConvertToDouble(runtime, dyJSValue, 1.0);
163     auto dzJSValue = argv[0]->GetProperty(runtime, "z");
164     double dz = ConvertToDouble(runtime, dzJSValue, 1.0);
165     auto centerXJSValue = argv[0]->GetProperty(runtime, "centerX");
166     double centerX = ConvertToDouble(runtime, centerXJSValue, 0.0);
167     auto centerYJSValue = argv[0]->GetProperty(runtime, "centerY");
168     double centerY = ConvertToDouble(runtime, centerYJSValue, 0.0);
169 
170     auto scaleMatrix = Matrix4::CreateScale(dx, dy, dz);
171     if (!NearZero(centerX) || !NearZero(centerY)) {
172         auto translate1 = Matrix4::CreateTranslate(centerX, centerY, 0.0);
173         auto translate2 = Matrix4::CreateTranslate(-centerX, -centerY, 0.0);
174         scaleMatrix = scaleMatrix * translate2;
175         scaleMatrix = translate1 * scaleMatrix;
176     }
177     matrix = scaleMatrix * matrix;
178     thisObj->SetProperty(runtime, MATRIX_4X4, ConvertToJSValue(runtime, matrix));
179     return thisObj;
180 }
181 
Rotate(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)182 shared_ptr<JsValue> Rotate(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
183     const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
184 {
185     if (argc != 1) {
186         return runtime->NewNull();
187     }
188     if (!argv[0]->IsObject(runtime)) {
189         return runtime->NewNull();
190     }
191 
192     auto matrixArray = thisObj->GetProperty(runtime, MATRIX_4X4);
193     auto matrix = ConvertToMatrix(runtime, matrixArray);
194     auto dxJSValue = argv[0]->GetProperty(runtime, "x");
195     double dx = ConvertToDouble(runtime, dxJSValue, 0.0);
196     auto dyJSValue = argv[0]->GetProperty(runtime, "y");
197     double dy = ConvertToDouble(runtime, dyJSValue, 0.0);
198     auto dzJSValue = argv[0]->GetProperty(runtime, "z");
199     double dz = ConvertToDouble(runtime, dzJSValue, 0.0);
200     auto angleJSValue = argv[0]->GetProperty(runtime, "angle");
201     double angle = ConvertToDouble(runtime, angleJSValue, 0.0);
202     auto centerXJSValue = argv[0]->GetProperty(runtime, "centerX");
203     double centerX = ConvertToDouble(runtime, centerXJSValue, 0.0);
204     auto centerYJSValue = argv[0]->GetProperty(runtime, "centerY");
205     double centerY = ConvertToDouble(runtime, centerYJSValue, 0.0);
206 
207     auto rotateMatrix = Matrix4::CreateRotate(angle, dx, dy, dz);
208     if (!NearZero(centerX) || !NearZero(centerY)) {
209         auto translate1 = Matrix4::CreateTranslate(centerX, centerY, 0.0);
210         auto translate2 = Matrix4::CreateTranslate(-centerX, -centerY, 0.0);
211         rotateMatrix = rotateMatrix * translate2;
212         rotateMatrix = translate1 * rotateMatrix;
213     }
214     matrix = rotateMatrix * matrix;
215     matrixArray = ConvertToJSValue(runtime, matrix);
216     thisObj->SetProperty(runtime, MATRIX_4X4, ConvertToJSValue(runtime, matrix));
217     return thisObj;
218 }
219 
TransformPoint(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)220 shared_ptr<JsValue> TransformPoint(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
221     const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
222 {
223     if (argc != 1) {
224         return runtime->NewNull();
225     }
226     if (!argv[0]->IsArray(runtime) || argv[0]->GetArrayLength(runtime) != 2) {
227         return runtime->NewNull();
228     }
229     auto matrix = ConvertToMatrix(runtime, thisObj->GetProperty(runtime, MATRIX_4X4));
230 
231     auto pointXJSValue = argv[0]->GetProperty(runtime, 0);
232     double pointX = ConvertToDouble(runtime, pointXJSValue, 0.0);
233 
234     auto pointYJSValue = argv[0]->GetProperty(runtime, 1);
235     double pointY = ConvertToDouble(runtime, pointYJSValue, 0.0);
236 
237     Point point { pointX, pointY };
238     Point target = matrix * point;
239     shared_ptr<JsValue> result = runtime->NewArray();
240     result->SetProperty(runtime, runtime->NewInt32(0), runtime->NewNumber(target.GetX()));
241     result->SetProperty(runtime, runtime->NewInt32(1), runtime->NewNumber(target.GetY()));
242     return result;
243 }
244 
ParsePoint(shared_ptr<JsValue> array,std::vector<OHOS::Ace::NG::PointT<int32_t>> & vector,const shared_ptr<JsRuntime> & runtime)245 void ParsePoint(shared_ptr<JsValue> array, std::vector<OHOS::Ace::NG::PointT<int32_t>>& vector,
246     const shared_ptr<JsRuntime>& runtime)
247 {
248     for (int i = 0; i < array->GetArrayLength(runtime); i++) {
249         auto value = array->GetElement(runtime, i);
250         if (!value->IsObject(runtime)) {
251             continue;
252         }
253         shared_ptr<JsValue> xJsValue = value->GetProperty(runtime, "x");
254         shared_ptr<JsValue> yJsValue = value->GetProperty(runtime, "y");
255         int32_t x = xJsValue->ToInt32(runtime);
256         int32_t y = yJsValue->ToInt32(runtime);
257         vector.push_back(OHOS::Ace::NG::PointT(x, y));
258     }
259 }
260 
SetPolyToPoly(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)261 shared_ptr<JsValue> SetPolyToPoly(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
262     const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
263 {
264     if (argc != 1 || !argv[0]->IsObject(runtime)) {
265         return thisObj;
266     }
267     auto matrix = ConvertToMatrix(runtime, thisObj->GetProperty(runtime, MATRIX_4X4));
268     auto pointCountJSValue = argv[0]->GetProperty(runtime, "pointCount");
269     shared_ptr<JsValue> srcJsValue = argv[0]->GetProperty(runtime, "src");
270     shared_ptr<JsValue> dstJsValue = argv[0]->GetProperty(runtime, "dst");
271     if (!srcJsValue->IsArray(runtime) || !dstJsValue->IsArray(runtime)) {
272         LOGE("setpPolyToPoly src or dst is not array");
273         return thisObj;
274     }
275     int32_t srcIndex = argv[0]->GetProperty(runtime, "srcIndex")->ToInt32(runtime);
276     int32_t dstIndex = argv[0]->GetProperty(runtime, "dstIndex")->ToInt32(runtime);
277     int32_t pointCount = srcJsValue->GetArrayLength(runtime)/2;
278     if (pointCountJSValue->IsInt32(runtime)) {
279         pointCount = pointCountJSValue->ToInt32(runtime);
280     }
281     std::vector<OHOS::Ace::NG::PointT<int32_t>> srcPoint, dstPoint;
282     ParsePoint(srcJsValue, srcPoint, runtime);
283     ParsePoint(dstJsValue, dstPoint, runtime);
284     if (pointCount <= 0 || pointCount > static_cast<int32_t>(srcPoint.size()) ||
285         pointCount > static_cast<int32_t>(dstPoint.size())) {
286         LOGE("setpPolyToPoly pointCount out of range pointCount:%{public}d, src size:%{public}d, dst size:%{public}d",
287             pointCount, static_cast<int>(srcPoint.size()), static_cast<int>(dstPoint.size()));
288         return thisObj;
289     }
290     if (srcIndex < 0 || (pointCount + srcIndex) > static_cast<int32_t>(srcPoint.size())) {
291         LOGE("setpPolyToPoly srcIndex out of range srcIndex:%{public}d, pointCount:%{public}d, src size%{public}d",
292             srcIndex, pointCount, static_cast<int>(srcPoint.size()));
293         return thisObj;
294     }
295     if (dstIndex < 0 || (pointCount + dstIndex) > static_cast<int32_t>(dstPoint.size())) {
296         LOGE("setpPolyToPoly dstIndex out of range dstIndex:%{public}d, pointCount:%{public}d, dst size%{public}d",
297             dstIndex, pointCount, static_cast<int>(dstPoint.size()));
298         return thisObj;
299     }
300     std::vector<OHOS::Ace::NG::PointT<int32_t>> totalPoint;
301     int srcLastIndex = pointCount + srcIndex;
302     for (int i = srcIndex; i < srcLastIndex; i++) {
303         totalPoint.push_back(srcPoint[i]);
304     }
305     int dstLastIndex = pointCount + dstIndex;
306     for (int i = dstIndex; i < dstLastIndex; i++) {
307         totalPoint.push_back(dstPoint[i]);
308     }
309     Matrix4 ret = OHOS::Ace::NG::SetMatrixPolyToPoly(matrix, totalPoint);
310     thisObj->SetProperty(runtime, MATRIX_4X4, ConvertToJSValue(runtime, ret));
311     return thisObj;
312 }
313 
314 shared_ptr<JsValue> Copy(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
315     const std::vector<shared_ptr<JsValue>>& argv, int32_t argc);
316 
AddCommonMatrixProperties(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & obj)317 void AddCommonMatrixProperties(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& obj)
318 {
319     static auto jsCopy = runtime->NewFunction(Copy);
320     static auto jsCombine = runtime->NewFunction(Combine);
321     static auto jsInvert = runtime->NewFunction(Invert);
322     static auto jsTranslate = runtime->NewFunction(Translate);
323     static auto jsScale = runtime->NewFunction(Scale);
324     static auto jsSkew = runtime->NewFunction(Skew);
325     static auto jsRotate = runtime->NewFunction(Rotate);
326     static auto jsTransformPoint = runtime->NewFunction(TransformPoint);
327     static auto jsSetPolyToPoly = runtime->NewFunction(SetPolyToPoly);
328     obj->SetProperty(runtime, MATRIX_COPY, jsCopy);
329     obj->SetProperty(runtime, MATRIX_COMBINE, jsCombine);
330     obj->SetProperty(runtime, MATRIX_INVERT, jsInvert);
331     obj->SetProperty(runtime, MATRIX_TRANSLATE, jsTranslate);
332     obj->SetProperty(runtime, MATRIX_SCALE, jsScale);
333     obj->SetProperty(runtime, MATRIX_SKEW, jsSkew);
334     obj->SetProperty(runtime, MATRIX_ROTATE, jsRotate);
335     obj->SetProperty(runtime, MATRIX_TRANSFORM_POINT, jsTransformPoint);
336     obj->SetProperty(runtime, MATRIX_SET_POLY_TO_POLY, jsSetPolyToPoly);
337 }
338 
Copy(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)339 shared_ptr<JsValue> Copy(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
340     const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
341 {
342     auto matrix = ConvertToMatrix(runtime, thisObj->GetProperty(runtime, MATRIX_4X4));
343     // create new object
344     shared_ptr<JsValue> other = runtime->NewObject();
345     // init functions
346     InitMatrix4Module(runtime, other);
347     // update matrix4x4
348     other->SetProperty(runtime, MATRIX_4X4, ConvertToJSValue(runtime, matrix));
349     AddCommonMatrixProperties(runtime, other);
350     return other;
351 }
352 
Init(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)353 shared_ptr<JsValue> Init(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
354     const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
355 {
356     if (argc != 1) {
357         return runtime->NewNull();
358     }
359 
360     auto matrix = ConvertToMatrix(runtime, argv[0]);
361     shared_ptr<JsValue> matrixObj = runtime->NewObject();
362     matrixObj->SetProperty(runtime, MATRIX_4X4, ConvertToJSValue(runtime, matrix));
363     AddCommonMatrixProperties(runtime, matrixObj);
364     return matrixObj;
365 }
366 
Identity(const shared_ptr<JsRuntime> & runtime,const shared_ptr<JsValue> & thisObj,const std::vector<shared_ptr<JsValue>> & argv,int32_t argc)367 shared_ptr<JsValue> Identity(const shared_ptr<JsRuntime>& runtime, const shared_ptr<JsValue>& thisObj,
368     const std::vector<shared_ptr<JsValue>>& argv, int32_t argc)
369 {
370     panda::JsiFastNativeScope scope(runtime->GetEcmaVm());
371     shared_ptr<JsValue> matrixObj = runtime->NewObject();
372     matrixObj->SetProperty(runtime, MATRIX_4X4, ConvertToJSValue(runtime, Matrix4::CreateIdentity()));
373     AddCommonMatrixProperties(runtime, matrixObj);
374     return matrixObj;
375 }
376 
InitMatrix4Module(const shared_ptr<JsRuntime> & runtime,shared_ptr<JsValue> & moduleObj)377 void InitMatrix4Module(const shared_ptr<JsRuntime>& runtime, shared_ptr<JsValue>& moduleObj)
378 {
379     moduleObj->SetProperty(runtime, MATRIX_4X4, ConvertToJSValue(runtime, Matrix4::CreateIdentity()));
380     moduleObj->SetProperty(runtime, MATRIX_INIT, runtime->NewFunction(Init));
381     moduleObj->SetProperty(runtime, MATRIX_IDENTITY, runtime->NewFunction(Identity));
382 }
383 
384 } // namespace OHOS::Ace::Framework
385