1 /*
2 * Copyright (c) 2024 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 #ifndef OHOS_JS_DRAWING_UTILS_H
17 #define OHOS_JS_DRAWING_UTILS_H
18
19 #include <map>
20 #ifdef ROSEN_OHOS
21 #include "hilog/log.h"
22 #endif
23
24 #include "common/rs_common_def.h"
25 #include "draw/color.h"
26 #include "draw/shadow.h"
27 #include "native_engine/native_engine.h"
28 #include "native_engine/native_value.h"
29 #include "text/font_metrics.h"
30 #include "text/font_types.h"
31 #include "utils/point.h"
32 #include "utils/point3.h"
33 #include "utils/rect.h"
34
35 namespace OHOS::Rosen {
36
37 // used for test
38 class JsDrawingTestUtils {
39 public:
GetDrawingTestDisabled()40 static bool GetDrawingTestDisabled() { return closeDrawingTest_; }
41 private:
42 static bool closeDrawingTest_;
43 };
44
45 #ifdef JS_DRAWING_TEST
46 #define JS_CALL_DRAWING_FUNC(func) \
47 do { \
48 if (LIKELY(JsDrawingTestUtils::GetDrawingTestDisabled())) { \
49 func; \
50 } \
51 } while (0)
52 #else
53 #define JS_CALL_DRAWING_FUNC(func) \
54 do { \
55 func; \
56 } while (0)
57 #endif
58
59 #define CHECK_PARAM_NUMBER_WITH_OPTIONAL_PARAMS(argv, argc, minNum, maxNum) \
60 do { \
61 if (napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr) != napi_ok || argc < minNum || argc > maxNum) { \
62 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, \
63 std::string("Incorrect number of ") + __FUNCTION__ + " parameters."); \
64 } \
65 } while (0)
66
67 #define CHECK_PARAM_NUMBER_WITHOUT_OPTIONAL_PARAMS(argv, paramNum) \
68 do { \
69 size_t argc = paramNum; \
70 if (napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr) != napi_ok || argc != paramNum) { \
71 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, \
72 std::string("Incorrect number of ") + __FUNCTION__ + " parameters."); \
73 } \
74 } while (0)
75
76 #define GET_DOUBLE_PARAM(argc, value) \
77 do { \
78 if (napi_get_value_double(env, argv[argc], &value) != napi_ok) { \
79 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, \
80 std::string("Incorrect ") + __FUNCTION__ + " parameter" + std::to_string(argc) + " type."); \
81 } \
82 } while (0)
83
84 #define GET_UINT32_PARAM(argc, value) \
85 do { \
86 if (napi_get_value_uint32(env, argv[argc], &value) != napi_ok) { \
87 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, \
88 std::string("Incorrect ") + __FUNCTION__ + " parameter" + std::to_string(argc) + " type."); \
89 } \
90 } while (0)
91
92 // get int32 number and check >= 0
93 #define GET_INT32_CHECK_GE_ZERO_PARAM(argc, value) \
94 do { \
95 if (napi_get_value_int32(env, argv[argc], &value) != napi_ok || value < 0) { \
96 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, \
97 std::string("Incorrect ") + __FUNCTION__ + " parameter" + std::to_string(argc) + " type."); \
98 } \
99 } while (0)
100
101 #define GET_DOUBLE_CHECK_GT_ZERO_PARAM(argc, value) \
102 do { \
103 if (napi_get_value_double(env, argv[argc], &value) != napi_ok) { \
104 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, \
105 std::string("Incorrect ") + __FUNCTION__ + " parameter" + std::to_string(argc) + " type."); \
106 } \
107 if (value <= 0.0) { \
108 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, \
109 std::string("Incorrect ") + __FUNCTION__ + " parameter" + std::to_string(argc) + \
110 " range. It should be greater than 0."); \
111 } \
112 } while (0)
113
114 #define GET_INT32_PARAM(argc, value) \
115 do { \
116 if (napi_get_value_int32(env, argv[argc], &value) != napi_ok) { \
117 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, \
118 std::string("Incorrect ") + __FUNCTION__ + " parameter" + std::to_string(argc) + " type."); \
119 } \
120 } while (0)
121
122 #define GET_COLOR_PARAM(argc, value) \
123 do { \
124 if (napi_get_value_int32(env, argv[argc], &value) != napi_ok || value < 0 || value > 255) { \
125 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, \
126 std::string("Incorrect ") + __FUNCTION__ + " parameter" + std::to_string(argc) + " type."); \
127 } \
128 } while (0)
129
130 #define GET_BOOLEAN_PARAM(argc, value) \
131 do { \
132 if (napi_get_value_bool(env, argv[argc], &value) != napi_ok) { \
133 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, \
134 std::string("Incorrect ") + __FUNCTION__ + " parameter" + std::to_string(argc) + " type."); \
135 } \
136 } while (0)
137
138 #define GET_UNWRAP_PARAM(argc, value) \
139 do { \
140 if ((napi_unwrap(env, argv[argc], reinterpret_cast<void**>(&value)) != napi_ok) || value == nullptr) { \
141 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, \
142 std::string("Incorrect ") + __FUNCTION__ + " parameter" + std::to_string(argc) + " type."); \
143 } \
144 } while (0)
145
146 #define GET_UNWRAP_PARAM_OR_NULL(argc, value) \
147 do { \
148 napi_valuetype valueType = napi_undefined; \
149 if (napi_typeof(env, argv[argc], &valueType) != napi_ok) { \
150 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, \
151 std::string("Incorrect ") + __FUNCTION__ + " parameter" + std::to_string(argc) + " type."); \
152 } \
153 if (valueType != napi_null && valueType != napi_object) { \
154 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, \
155 std::string("Incorrect valueType ") + __FUNCTION__ + " parameter" + std::to_string(argc) + " type."); \
156 } \
157 if (valueType == napi_object && napi_unwrap(env, argv[argc], reinterpret_cast<void**>(&value)) != napi_ok) { \
158 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, \
159 std::string("Incorrect unwrap ") + __FUNCTION__ + " parameter" + std::to_string(argc) + " type."); \
160 } \
161 } while (0)
162
163 #define GET_JSVALUE_PARAM(argc, value) \
164 do { \
165 if (!ConvertFromJsValue(env, argv[argc], value)) { \
166 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, \
167 std::string("Incorrect ") + __FUNCTION__ + " parameter" + std::to_string(argc) + " type."); \
168 } \
169 } while (0)
170
171 #define GET_ENUM_PARAM(argc, value, lo, hi) \
172 do { \
173 GET_INT32_PARAM(argc, value); \
174 if (value < lo || value > hi) { \
175 return NapiThrowError(env, DrawingErrorCode::ERROR_INVALID_PARAM, \
176 std::string("Incorrect ") + __FUNCTION__ + " parameter" + std::to_string(argc) + " range."); \
177 } \
178 } while (0)
179
180 namespace Drawing {
181 constexpr size_t ARGC_ZERO = 0;
182 constexpr size_t ARGC_ONE = 1;
183 constexpr size_t ARGC_TWO = 2;
184 constexpr size_t ARGC_THREE = 3;
185 constexpr size_t ARGC_FOUR = 4;
186 constexpr size_t ARGC_FIVE = 5;
187 constexpr size_t ARGC_SIX = 6;
188 constexpr size_t ARGC_SEVEN = 7;
189 constexpr size_t ARGC_EIGHT = 8;
190 constexpr size_t ARGC_NINE = 9;
191 constexpr int NUMBER_TWO = 2;
192
193 enum class DrawingErrorCode : int32_t {
194 OK = 0,
195 ERROR_NO_PERMISSION = 201, // the value do not change. It is defined on all system
196 ERROR_INVALID_PARAM = 401, // the value do not change. It is defined on all system
197 ERROR_DEVICE_NOT_SUPPORT = 801, // the value do not change. It is defined on all system
198 ERROR_ABNORMAL_PARAM_VALUE = 18600001, // the value do not change. It is defined on color manager system
199 };
200
201 template<class T>
202 T* CheckParamsAndGetThis(const napi_env env, napi_callback_info info, const char* name = nullptr)
203 {
204 if (env == nullptr || info == nullptr) {
205 return nullptr;
206 }
207 napi_value object = nullptr;
208 napi_value propertyNameValue = nullptr;
209 napi_value pointerValue = nullptr;
210 napi_get_cb_info(env, info, nullptr, nullptr, &object, nullptr);
211 if (object != nullptr && name != nullptr) {
212 napi_create_string_utf8(env, name, NAPI_AUTO_LENGTH, &propertyNameValue);
213 }
214 napi_value& resObject = propertyNameValue ? propertyNameValue : object;
215 if (resObject) {
216 return napi_unwrap(env, resObject, (void **)(&pointerValue)) == napi_ok ?
217 reinterpret_cast<T*>(pointerValue) : nullptr;
218 }
219 return nullptr;
220 }
221
222 template<typename T, size_t N>
ArraySize(T (&)[N])223 inline constexpr size_t ArraySize(T (&)[N]) noexcept
224 {
225 return N;
226 }
227
CreateJsUndefined(napi_env env)228 inline napi_value CreateJsUndefined(napi_env env)
229 {
230 napi_value result = nullptr;
231 napi_get_undefined(env, &result);
232 return result;
233 }
234
CreateJsNull(napi_env env)235 inline napi_value CreateJsNull(napi_env env)
236 {
237 napi_value result = nullptr;
238 napi_get_null(env, &result);
239 return result;
240 }
241
CreateJsNumber(napi_env env,int32_t value)242 inline napi_value CreateJsNumber(napi_env env, int32_t value)
243 {
244 napi_value result = nullptr;
245 napi_create_int32(env, value, &result);
246 return result;
247 }
248
CreateJsNumber(napi_env env,uint32_t value)249 inline napi_value CreateJsNumber(napi_env env, uint32_t value)
250 {
251 napi_value result = nullptr;
252 napi_create_uint32(env, value, &result);
253 return result;
254 }
255
CreateJsNumber(napi_env env,int64_t value)256 inline napi_value CreateJsNumber(napi_env env, int64_t value)
257 {
258 napi_value result = nullptr;
259 napi_create_int64(env, value, &result);
260 return result;
261 }
262
CreateJsNumber(napi_env env,double value)263 inline napi_value CreateJsNumber(napi_env env, double value)
264 {
265 napi_value result = nullptr;
266 napi_create_double(env, value, &result);
267 return result;
268 }
269
270 template<class T>
CreateJsValue(napi_env env,const T & value)271 napi_value CreateJsValue(napi_env env, const T& value)
272 {
273 using ValueType = std::remove_cv_t<std::remove_reference_t<T>>;
274 napi_value result = nullptr;
275 if constexpr (std::is_same_v<ValueType, bool>) {
276 napi_get_boolean(env, value, &result);
277 return result;
278 } else if constexpr (std::is_arithmetic_v<ValueType>) {
279 return CreateJsNumber(env, value);
280 } else if constexpr (std::is_same_v<ValueType, std::string>) {
281 napi_create_string_utf8(env, value.c_str(), value.length(), &result);
282 return result;
283 } else if constexpr (std::is_enum_v<ValueType>) {
284 return CreateJsNumber(env, static_cast<std::make_signed_t<ValueType>>(value));
285 } else if constexpr (std::is_same_v<ValueType, const char*>) {
286 (value != nullptr) ? napi_create_string_utf8(env, value, strlen(value), &result) :
287 napi_get_undefined(env, &result);
288 return result;
289 }
290 }
291
ConvertFromJsNumber(napi_env env,napi_value jsValue,int32_t & value)292 inline bool ConvertFromJsNumber(napi_env env, napi_value jsValue, int32_t& value)
293 {
294 return napi_get_value_int32(env, jsValue, &value) == napi_ok;
295 }
296
ConvertFromJsNumber(napi_env env,napi_value jsValue,uint32_t & value)297 inline bool ConvertFromJsNumber(napi_env env, napi_value jsValue, uint32_t& value)
298 {
299 return napi_get_value_uint32(env, jsValue, &value) == napi_ok;
300 }
301
ConvertFromJsNumber(napi_env env,napi_value jsValue,int64_t & value)302 inline bool ConvertFromJsNumber(napi_env env, napi_value jsValue, int64_t& value)
303 {
304 return napi_get_value_int64(env, jsValue, &value) == napi_ok;
305 }
306
ConvertFromJsNumber(napi_env env,napi_value jsValue,double & value)307 inline bool ConvertFromJsNumber(napi_env env, napi_value jsValue, double& value)
308 {
309 return napi_get_value_double(env, jsValue, &value) == napi_ok;
310 }
311
ConvertFromJsNumber(napi_env env,napi_value jsValue,bool & value)312 inline bool ConvertFromJsNumber(napi_env env, napi_value jsValue, bool& value)
313 {
314 return napi_get_value_bool(env, jsValue, &value) == napi_ok;
315 }
316
317 template<class T>
ConvertFromJsValue(napi_env env,napi_value jsValue,T & value)318 bool ConvertFromJsValue(napi_env env, napi_value jsValue, T& value)
319 {
320 if (jsValue == nullptr) {
321 return false;
322 }
323
324 using ValueType = std::remove_cv_t<std::remove_reference_t<T>>;
325 if constexpr (std::is_same_v<ValueType, bool>) {
326 return napi_get_value_bool(env, jsValue, &value) == napi_ok;
327 } else if constexpr (std::is_arithmetic_v<ValueType>) {
328 return ConvertFromJsNumber(env, jsValue, value);
329 } else if constexpr (std::is_same_v<ValueType, std::string>) {
330 size_t len = 0;
331 if (napi_get_value_string_utf8(env, jsValue, nullptr, 0, &len) != napi_ok) {
332 return false;
333 }
334 auto buffer = std::make_unique<char[]>(len + 1);
335 size_t strLength = 0;
336 if (napi_get_value_string_utf8(env, jsValue, buffer.get(), len + 1, &strLength) == napi_ok) {
337 value = buffer.get();
338 return true;
339 }
340 return false;
341 } else if constexpr (std::is_enum_v<ValueType>) {
342 std::make_signed_t<ValueType> numberValue = 0;
343 if (!ConvertFromJsNumber(env, jsValue, numberValue)) {
344 return false;
345 }
346 value = static_cast<ValueType>(numberValue);
347 return true;
348 }
349 return false;
350 }
351
352 bool ConvertFromJsColor(napi_env env, napi_value jsValue, int32_t* argb, size_t size);
353
354 bool ConvertFromJsRect(napi_env env, napi_value jsValue, double* ltrb, size_t size);
355
356 bool ConvertFromJsIRect(napi_env env, napi_value jsValue, int32_t* ltrb, size_t size);
357
358 bool ConvertFromJsPoint(napi_env env, napi_value jsValue, double* point, size_t size);
359
360 bool ConvertFromJsPoint3d(napi_env env, napi_value src, Point3& point3d);
361
362 bool ConvertFromJsShadowFlag(
363 napi_env env, napi_value src, ShadowFlags& shadowFlag, ShadowFlags defaultFlag = ShadowFlags::NONE);
364
ConvertFromJsNumber(napi_env env,napi_value jsValue,int32_t & value,int32_t lo,int32_t hi)365 inline bool ConvertFromJsNumber(napi_env env, napi_value jsValue, int32_t& value, int32_t lo, int32_t hi)
366 {
367 return napi_get_value_int32(env, jsValue, &value) == napi_ok && value >= lo && value <= hi;
368 }
369
GetDrawingPointXFromJsNumber(napi_env env,napi_value argValue,Drawing::Point & point)370 inline bool GetDrawingPointXFromJsNumber(napi_env env, napi_value argValue, Drawing::Point& point)
371 {
372 napi_value objValue = nullptr;
373 double targetX = 0;
374 if (napi_get_named_property(env, argValue, "x", &objValue) != napi_ok ||
375 napi_get_value_double(env, objValue, &targetX) != napi_ok) {
376 return false;
377 }
378 point.SetX(targetX);
379 return true;
380 }
381
GetDrawingPointYFromJsNumber(napi_env env,napi_value argValue,Drawing::Point & point)382 inline bool GetDrawingPointYFromJsNumber(napi_env env, napi_value argValue, Drawing::Point& point)
383 {
384 napi_value objValue = nullptr;
385 double targetY = 0;
386 if (napi_get_named_property(env, argValue, "y", &objValue) != napi_ok ||
387 napi_get_value_double(env, objValue, &targetY) != napi_ok) {
388 return false;
389 }
390 point.SetY(targetY);
391 return true;
392 }
393
GetDrawingPointFromJsValue(napi_env env,napi_value argValue,Drawing::Point & point)394 inline bool GetDrawingPointFromJsValue(napi_env env, napi_value argValue, Drawing::Point& point)
395 {
396 return GetDrawingPointXFromJsNumber(env, argValue, point) &&
397 GetDrawingPointYFromJsNumber(env, argValue, point);
398 }
399
400 bool ConvertFromJsPointsArray(napi_env env, napi_value array, Drawing::Point* points, uint32_t count);
401
GetDoubleAndConvertToJsValue(napi_env env,double d)402 inline napi_value GetDoubleAndConvertToJsValue(napi_env env, double d)
403 {
404 napi_value value = nullptr;
405 (void)napi_create_double(env, d, &value);
406 return value;
407 }
408
GetStringAndConvertToJsValue(napi_env env,std::string str)409 inline napi_value GetStringAndConvertToJsValue(napi_env env, std::string str)
410 {
411 napi_value objValue = nullptr;
412 napi_create_string_utf8(env, str.c_str(), str.length(), &objValue);
413 return objValue;
414 }
415
416 napi_value GetFontMetricsAndConvertToJsValue(napi_env env, FontMetrics* metrics);
417
GetRectAndConvertToJsValue(napi_env env,std::shared_ptr<Rect> rect)418 inline napi_value GetRectAndConvertToJsValue(napi_env env, std::shared_ptr<Rect> rect)
419 {
420 napi_value objValue = nullptr;
421 napi_create_object(env, &objValue);
422 if (rect != nullptr && objValue != nullptr) {
423 napi_set_named_property(env, objValue, "left", CreateJsNumber(env, rect->GetLeft()));
424 napi_set_named_property(env, objValue, "top", CreateJsNumber(env, rect->GetTop()));
425 napi_set_named_property(env, objValue, "right", CreateJsNumber(env, rect->GetRight()));
426 napi_set_named_property(env, objValue, "bottom", CreateJsNumber(env, rect->GetBottom()));
427 }
428 return objValue;
429 }
430
ConvertPointToJsValue(napi_env env,Drawing::Point & point)431 inline napi_value ConvertPointToJsValue(napi_env env, Drawing::Point& point)
432 {
433 napi_value objValue = nullptr;
434 napi_create_object(env, &objValue);
435 if (objValue != nullptr) {
436 if (napi_set_named_property(env, objValue, "x", CreateJsNumber(env, point.GetX())) != napi_ok ||
437 napi_set_named_property(env, objValue, "y", CreateJsNumber(env, point.GetY())) != napi_ok) {
438 return nullptr;
439 }
440 }
441 return objValue;
442 }
443
NapiGetUndefined(napi_env env)444 inline napi_value NapiGetUndefined(napi_env env)
445 {
446 napi_value result = nullptr;
447 napi_get_undefined(env, &result);
448 return result;
449 }
450
451 void BindNativeFunction(napi_env env, napi_value object, const char* name, const char* moduleName, napi_callback func);
452 napi_value CreateJsError(napi_env env, int32_t errCode, const std::string& message);
453
454 bool ConvertFromJsTextEncoding(napi_env env, TextEncoding& textEncoding, napi_value nativeType);
455
GetColorAndConvertToJsValue(napi_env env,const Color & color)456 inline napi_value GetColorAndConvertToJsValue(napi_env env, const Color& color)
457 {
458 napi_value objValue = nullptr;
459 napi_create_object(env, &objValue);
460 if (objValue != nullptr) {
461 napi_set_named_property(env, objValue, "alpha", CreateJsNumber(env, color.GetAlpha()));
462 napi_set_named_property(env, objValue, "red", CreateJsNumber(env, color.GetRed()));
463 napi_set_named_property(env, objValue, "green", CreateJsNumber(env, color.GetGreen()));
464 napi_set_named_property(env, objValue, "blue", CreateJsNumber(env, color.GetBlue()));
465 }
466 return objValue;
467 }
468
469 napi_value NapiThrowError(napi_env env, DrawingErrorCode err, const std::string& message);
470 } // namespace Drawing
471 } // namespace OHOS::Rosen
472
473 #ifdef ROSEN_OHOS
474
475 #undef LOG_DOMAIN
476 #define LOG_DOMAIN 0xD001400
477
478 #undef LOG_TAG
479 #define LOG_TAG "JsDrawing"
480
481 #define ROSEN_LOGI(format, ...) \
482 HILOG_INFO(LOG_CORE, format, ##__VA_ARGS__)
483 #define ROSEN_LOGD(format, ...) \
484 HILOG_DEBUG(LOG_CORE, format, ##__VA_ARGS__)
485 #define ROSEN_LOGE(format, ...) \
486 HILOG_ERROR(LOG_CORE, format, ##__VA_ARGS__)
487 #else
488 #define ROSEN_LOGI(format, ...)
489 #define ROSEN_LOGD(format, ...)
490 #define ROSEN_LOGE(format, ...)
491 #endif
492
493 #endif // OHOS_JS_DRAWING_UTILS_H