• 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/v8/v8_js_matrix4.h"
17 
18 #include <utility>
19 
20 #include "frameworks/base/geometry/matrix4.h"
21 #include "frameworks/base/json/json_util.h"
22 #include "frameworks/bridge/declarative_frontend/engine/v8/v8_utils.h"
23 #include "frameworks/bridge/js_frontend/engine/common/js_constants.h"
24 
25 namespace OHOS::Ace::Framework {
26 namespace {
27 
ArrayToMatrix4(const v8::Local<v8::Array> & array,v8::Local<v8::Context> context)28 Matrix4 ArrayToMatrix4(const v8::Local<v8::Array>& array, v8::Local<v8::Context> context)
29 {
30     Matrix4 ret = Matrix4::CreateIdentity();
31     if (array->Length() != Matrix4::DIMENSION * Matrix4::DIMENSION) {
32         LOGE("ArrayToMatrix4 array length != 16");
33         return ret;
34     }
35 
36     // in column order
37     for (int32_t i = 0; i < Matrix4::DIMENSION; i++) {
38         for (int32_t j = 0; j < Matrix4::DIMENSION; j++) {
39             auto num =
40                 array->Get(context, i * Matrix4::DIMENSION + j).ToLocalChecked()->ToNumber(context).ToLocalChecked();
41             ret.Set(j, i, static_cast<float>(num->Value()));
42         }
43     }
44     return ret;
45 }
46 
Matrix4ToArray(const Matrix4 & matrix,v8::Isolate * isolate,v8::Local<v8::Array> & out)47 void Matrix4ToArray(const Matrix4& matrix, v8::Isolate* isolate, v8::Local<v8::Array>& out)
48 {
49     auto context = isolate->GetCurrentContext();
50     for (int32_t i = 0; i < Matrix4::DIMENSION; i++) {
51         for (int32_t j = 0; j < Matrix4::DIMENSION; j++) {
52             // output in column order
53             auto success = out->Set(context, i * Matrix4::DIMENSION + j, v8::Number::New(isolate, matrix.Get(j, i)));
54             if (!success.ToChecked()) {
55                 LOGE("Matrix4ToArray Set failed! %{public}d, %{public}d", i, j);
56             }
57         }
58     }
59 }
60 
61 } // namespace
62 
CreateMatrix4Object(v8::Local<v8::Object> moduleObj,v8::Isolate * isolate)63 void JSMatrix4::CreateMatrix4Object(v8::Local<v8::Object> moduleObj, v8::Isolate* isolate)
64 {
65     if (moduleObj.IsEmpty() || isolate == nullptr) {
66         return;
67     }
68 
69     auto context = isolate->GetCurrentContext();
70 
71     Matrix4 matrix;
72     v8::Local<v8::Array> arr = v8::Array::New(isolate);
73     Matrix4ToArray(matrix, isolate, arr);
74 
75     moduleObj->Set(context, v8::String::NewFromUtf8(isolate, MATRIX_4X4).ToLocalChecked(), arr).ToChecked();
76 
77     using FunctionType = std::pair<std::string, void (*)(const v8::FunctionCallbackInfo<v8::Value>&)>;
78     static FunctionType functions[] = {
79         { MATRIX_INIT, &JSMatrix4::Init },
80         { MATRIX_IDENTITY, &JSMatrix4::Identity },
81         { MATRIX_COPY, &JSMatrix4::Copy },
82         { MATRIX_COMBINE, &JSMatrix4::Combine },
83         { MATRIX_INVERT, &JSMatrix4::Invert },
84         { MATRIX_TRANSLATE, &JSMatrix4::Translate },
85         { MATRIX_SCALE, &JSMatrix4::Scale },
86         { MATRIX_ROTATE, &JSMatrix4::Rotate },
87         { MATRIX_TRANSFORM_POINT, &JSMatrix4::TransformPoint },
88     };
89     for (const auto& func : functions) {
90         moduleObj
91             ->Set(context, v8::String::NewFromUtf8(isolate, func.first.c_str()).ToLocalChecked(),
92                   v8::Function::New(context, func.second).ToLocalChecked())
93             .ToChecked();
94     }
95 }
96 
Init(const v8::FunctionCallbackInfo<v8::Value> & info)97 void JSMatrix4::Init(const v8::FunctionCallbackInfo<v8::Value>& info)
98 {
99     LOGD("JSMatrix4::Init");
100     auto isolate = info.GetIsolate();
101     v8::HandleScope scp(isolate);
102     auto context = isolate->GetCurrentContext();
103 
104     auto copy = v8::Object::New(isolate);
105     CreateMatrix4Object(copy, isolate);
106 
107     if (info.Length() == 1 && info[0]->IsArray()) {
108         v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(info[0]);
109         if (array->Length() == Matrix4::DIMENSION * Matrix4::DIMENSION) {
110             auto success =
111                 copy->Set(context, v8::String::NewFromUtf8(isolate, MATRIX_4X4).ToLocalChecked(), array);
112             if (!success.ToChecked()) {
113                 LOGE("JSMatrix4::Init Set failed!");
114             }
115         }
116     }
117     info.GetReturnValue().Set(copy);
118 }
119 
Identity(const v8::FunctionCallbackInfo<v8::Value> & info)120 void JSMatrix4::Identity(const v8::FunctionCallbackInfo<v8::Value>& info)
121 {
122     LOGD("JSMatrix4::Identity");
123     auto isolate = info.GetIsolate();
124     v8::HandleScope scp(isolate);
125 
126     auto context = isolate->GetCurrentContext();
127     auto copy = v8::Object::New(isolate);
128     CreateMatrix4Object(copy, isolate);
129     v8::Local<v8::Array> array = v8::Array::New(isolate);
130     Matrix4 matrix = Matrix4::CreateIdentity();
131     Matrix4ToArray(matrix, isolate, array);
132     auto success = copy->Set(context, v8::String::NewFromUtf8(isolate, MATRIX_4X4).ToLocalChecked(), array);
133     if (!success.ToChecked()) {
134         LOGE("JSMatrix4::Identity Set failed!");
135     }
136 
137     info.GetReturnValue().Set(copy);
138 }
139 
Copy(const v8::FunctionCallbackInfo<v8::Value> & info)140 void JSMatrix4::Copy(const v8::FunctionCallbackInfo<v8::Value>& info)
141 {
142     LOGD("JSMatrix4::Copy");
143     auto isolate = info.GetIsolate();
144     v8::HandleScope scp(isolate);
145     auto context = isolate->GetCurrentContext();
146 
147     auto copy = v8::Object::New(isolate);
148     CreateMatrix4Object(copy, isolate);
149     auto object = info.This()->Get(context,
150                                    v8::String::NewFromUtf8(isolate, MATRIX_4X4).ToLocalChecked()).ToLocalChecked();
151     if (object->IsArray()) {
152         auto success = copy->Set(context, v8::String::NewFromUtf8(isolate, MATRIX_4X4).ToLocalChecked(), object);
153         if (!success.ToChecked()) {
154             LOGE("JSMatrix4::Copy failed!");
155         }
156     }
157 
158     info.GetReturnValue().Set(copy);
159 }
160 
Combine(const v8::FunctionCallbackInfo<v8::Value> & info)161 void JSMatrix4::Combine(const v8::FunctionCallbackInfo<v8::Value>& info)
162 {
163     LOGD("JSMatrix4::Combine");
164     auto isolate = info.GetIsolate();
165     v8::HandleScope scp(isolate);
166     auto context = isolate->GetCurrentContext();
167 
168     if (info.Length() == 1 && info[0]->IsObject()) {
169         auto arg = v8::Local<v8::Object>::Cast(info[0]);
170         auto objA =
171             arg->Get(context, v8::String::NewFromUtf8(isolate, MATRIX_4X4).ToLocalChecked()).ToLocalChecked();
172         auto objB = info.This()
173                         ->Get(context, v8::String::NewFromUtf8(isolate, MATRIX_4X4).ToLocalChecked())
174                         .ToLocalChecked();
175         auto matrixA = ArrayToMatrix4(v8::Local<v8::Array>::Cast(objA), context);
176         auto matrixB = ArrayToMatrix4(v8::Local<v8::Array>::Cast(objB), context);
177 
178         v8::Local<v8::Array> arr = v8::Array::New(isolate);
179         Matrix4ToArray(matrixA * matrixB, isolate, arr);
180         auto success = info.This()->Set(context, v8::String::NewFromUtf8(isolate, MATRIX_4X4).ToLocalChecked(), arr);
181         if (!success.ToChecked()) {
182             LOGE("JSMatrix4::Combine set failed");
183         }
184     }
185     info.GetReturnValue().Set(info.This());
186 }
187 
Invert(const v8::FunctionCallbackInfo<v8::Value> & info)188 void JSMatrix4::Invert(const v8::FunctionCallbackInfo<v8::Value>& info)
189 {
190     LOGD("JSMatrix4::Invert");
191     auto isolate = info.GetIsolate();
192     v8::HandleScope scp(isolate);
193     auto context = isolate->GetCurrentContext();
194 
195     auto obj =
196         info.This()->Get(context, v8::String::NewFromUtf8(isolate, MATRIX_4X4).ToLocalChecked()).ToLocalChecked();
197     if (obj->IsArray()) {
198         auto array = v8::Local<v8::Array>::Cast(obj);
199         auto m = ArrayToMatrix4(array, context);
200         m = Matrix4::Invert(m);
201         v8::Local<v8::Array> arr = v8::Array::New(isolate);
202         Matrix4ToArray(m, isolate, arr);
203         auto success = info.This()->Set(context, v8::String::NewFromUtf8(isolate, MATRIX_4X4).ToLocalChecked(), arr);
204         if (!success.ToChecked()) {
205             LOGE("JSMatrix4::Invert set failed");
206         }
207     }
208     return info.GetReturnValue().Set(info.This());
209 }
210 
Translate(const v8::FunctionCallbackInfo<v8::Value> & info)211 void JSMatrix4::Translate(const v8::FunctionCallbackInfo<v8::Value>& info)
212 {
213     LOGD("JSMatrix4::Translate");
214     auto isolate = info.GetIsolate();
215     v8::HandleScope scp(isolate);
216     auto context = isolate->GetCurrentContext();
217 
218     if (info.Length() == 1 && info[0]->IsObject()) {
219         auto args = JsonUtil::ParseJsonString(
220             V8Utils::ScopedString(v8::JSON::Stringify(context, info[0]).ToLocalChecked()).get());
221         if (args) {
222             auto dx = args->GetDouble("x", 0.0);
223             auto dy = args->GetDouble("y", 0.0);
224             auto dz = args->GetDouble("z", 0.0);
225 
226             Matrix4 translateMatrix = Matrix4::CreateTranslate(dx, dy, dz);
227             auto obj =
228                 info.This()->Get(context,
229                                  v8::String::NewFromUtf8(isolate, MATRIX_4X4).ToLocalChecked()).ToLocalChecked();
230             if (obj->IsArray()) {
231                 auto array = v8::Local<v8::Array>::Cast(obj);
232                 Matrix4 matrix = ArrayToMatrix4(array, context);
233                 matrix = translateMatrix * matrix;
234                 v8::Local<v8::Array> arr = v8::Array::New(isolate);
235                 Matrix4ToArray(matrix, isolate, arr);
236                 auto success =
237                     info.This()->Set(context, v8::String::NewFromUtf8(isolate, MATRIX_4X4).ToLocalChecked(), arr);
238                 if (!success.ToChecked()) {
239                     LOGE("JSMatrix4::Translate set failed");
240                 }
241             }
242         }
243     }
244     info.GetReturnValue().Set(info.This());
245 }
246 
Scale(const v8::FunctionCallbackInfo<v8::Value> & info)247 void JSMatrix4::Scale(const v8::FunctionCallbackInfo<v8::Value>& info)
248 {
249     LOGD("JSMatrix4::Scale");
250     auto isolate = info.GetIsolate();
251     v8::HandleScope scp(isolate);
252     auto context = isolate->GetCurrentContext();
253 
254     if (info.Length() == 1 && info[0]->IsObject()) {
255         auto args = JsonUtil::ParseJsonString(
256             V8Utils::ScopedString(v8::JSON::Stringify(context, info[0]).ToLocalChecked()).get());
257         if (args) {
258             auto dx = args->GetDouble("x", 1.0);
259             auto dy = args->GetDouble("y", 1.0);
260             auto dz = args->GetDouble("z", 1.0);
261             auto centerX = args->GetDouble("centerX", 0.0);
262             auto centerY = args->GetDouble("centerY", 0.0);
263             auto obj =
264                 info.This()->Get(context,
265                                  v8::String::NewFromUtf8(isolate, MATRIX_4X4).ToLocalChecked()).ToLocalChecked();
266             if (obj->IsArray()) {
267                 auto array = v8::Local<v8::Array>::Cast(obj);
268                 auto matrix = ArrayToMatrix4(array, context);
269                 auto scaleMatrix = Matrix4::CreateScale(dx, dy, dz);
270 
271                 if (!NearZero(centerX) || !NearZero(centerY)) {
272                     auto translate1 = Matrix4::CreateTranslate(centerX, centerY, 0.0);
273                     auto translate2 = Matrix4::CreateTranslate(-centerX, -centerY, 0.0);
274                     scaleMatrix = scaleMatrix * translate2;
275                     scaleMatrix = translate1 * scaleMatrix;
276                 }
277                 matrix = scaleMatrix * matrix;
278                 v8::Local<v8::Array> arr = v8::Array::New(isolate);
279                 Matrix4ToArray(matrix, isolate, arr);
280 
281                 auto success =
282                     info.This()->Set(context, v8::String::NewFromUtf8(isolate, MATRIX_4X4).ToLocalChecked(), arr);
283                 if (!success.ToChecked()) {
284                     LOGE("JSMatrix4::Translate set failed");
285                 }
286             }
287         }
288     }
289     info.GetReturnValue().Set(info.This());
290 }
291 
Rotate(const v8::FunctionCallbackInfo<v8::Value> & info)292 void JSMatrix4::Rotate(const v8::FunctionCallbackInfo<v8::Value>& info)
293 {
294     LOGD("JSMatrix4::Rotate");
295     auto isolate = info.GetIsolate();
296     v8::HandleScope scp(isolate);
297     auto context = isolate->GetCurrentContext();
298 
299     if (info.Length() == 1 && info[0]->IsObject()) {
300         auto args = JsonUtil::ParseJsonString(
301             V8Utils::ScopedString(v8::JSON::Stringify(context, info[0]).ToLocalChecked()).get());
302         if (args) {
303             auto dx = args->GetDouble("x", 0.0);
304             auto dy = args->GetDouble("y", 0.0);
305             auto dz = args->GetDouble("z", 0.0);
306             auto angle = args->GetDouble("angle", 0.0);
307             auto centerX = args->GetDouble("centerX", 0.0);
308             auto centerY = args->GetDouble("centerY", 0.0);
309             auto obj =
310                 info.This()->Get(context,
311                                  v8::String::NewFromUtf8(isolate, MATRIX_4X4).ToLocalChecked()).ToLocalChecked();
312             if (obj->IsArray()) {
313                 auto array = v8::Local<v8::Array>::Cast(obj);
314                 auto matrix = ArrayToMatrix4(array, context);
315                 auto rotateMatrix = Matrix4::CreateRotate(angle, dx, dy, dz);
316 
317                 if (!NearZero(centerX) || !NearZero(centerY)) {
318                     auto translate1 = Matrix4::CreateTranslate(centerX, centerY, 0.0);
319                     auto translate2 = Matrix4::CreateTranslate(-centerX, -centerY, 0.0);
320                     rotateMatrix = rotateMatrix * translate2;
321                     rotateMatrix = translate1 * rotateMatrix;
322                 }
323                 matrix = rotateMatrix * matrix;
324                 v8::Local<v8::Array> arr = v8::Array::New(isolate);
325                 Matrix4ToArray(matrix, isolate, arr);
326                 auto success =
327                     info.This()->Set(context, v8::String::NewFromUtf8(isolate, MATRIX_4X4).ToLocalChecked(), arr);
328                 if (!success.ToChecked()) {
329                     LOGE("JSMatrix4::Translate set failed");
330                 }
331             }
332         }
333     }
334     info.GetReturnValue().Set(info.This());
335 }
336 
TransformPoint(const v8::FunctionCallbackInfo<v8::Value> & info)337 void JSMatrix4::TransformPoint(const v8::FunctionCallbackInfo<v8::Value>& info)
338 {
339     LOGD("JSMatrix4::TransformPoint");
340     auto isolate = info.GetIsolate();
341     v8::HandleScope scp(isolate);
342     auto context = isolate->GetCurrentContext();
343     v8::Local<v8::Array> arr = v8::Array::New(isolate);
344 
345     if (info.Length() == 1 && info[0]->IsArray()) {
346         auto args = v8::Local<v8::Array>::Cast(info[0]);
347         if (args->Length() == 2) {
348             auto x = args->Get(context, 0).ToLocalChecked()->ToNumber(context).ToLocalChecked();
349             auto y = args->Get(context, 1).ToLocalChecked()->ToNumber(context).ToLocalChecked();
350 
351             Point p { x->Value(), y->Value() };
352 
353             auto obj =
354                 info.This()->Get(context,
355                                  v8::String::NewFromUtf8(isolate, MATRIX_4X4).ToLocalChecked()).ToLocalChecked();
356             if (obj->IsArray()) {
357                 auto array = v8::Local<v8::Array>::Cast(obj);
358                 auto matrix = ArrayToMatrix4(array, context);
359 
360                 Point target = matrix * p;
361                 auto success = arr->Set(context, 0, v8::Number::New(isolate, target.GetX()));
362                 if (!success.ToChecked()) {
363                     LOGE("TransformPoint failed!");
364                 }
365                 success = arr->Set(context, 1, v8::Number::New(isolate, target.GetY()));
366                 if (!success.ToChecked()) {
367                     LOGE("TransformPoint failed!");
368                 }
369             }
370         }
371     }
372     info.GetReturnValue().Set(arr);
373 }
374 
375 } // namespace OHOS::Ace::Framework
376