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