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