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