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
195 enum class DrawingErrorCode : int32_t {
196 OK = 0,
197 ERROR_NO_PERMISSION = 201, // the value do not change. It is defined on all system
198 ERROR_INVALID_PARAM = 401, // the value do not change. It is defined on all system
199 ERROR_DEVICE_NOT_SUPPORT = 801, // the value do not change. It is defined on all system
200 ERROR_ABNORMAL_PARAM_VALUE = 18600001, // the value do not change. It is defined on color manager system
201 };
202
203 template<class T>
204 T* CheckParamsAndGetThis(const napi_env env, napi_callback_info info, const char* name = nullptr)
205 {
206 if (env == nullptr || info == nullptr) {
207 return nullptr;
208 }
209 napi_value object = nullptr;
210 napi_value propertyNameValue = nullptr;
211 napi_value pointerValue = nullptr;
212 napi_get_cb_info(env, info, nullptr, nullptr, &object, nullptr);
213 if (object != nullptr && name != nullptr) {
214 napi_create_string_utf8(env, name, NAPI_AUTO_LENGTH, &propertyNameValue);
215 }
216 napi_value& resObject = propertyNameValue ? propertyNameValue : object;
217 if (resObject) {
218 return napi_unwrap(env, resObject, (void **)(&pointerValue)) == napi_ok ?
219 reinterpret_cast<T*>(pointerValue) : nullptr;
220 }
221 return nullptr;
222 }
223
224 template<typename T, size_t N>
ArraySize(T (&)[N])225 inline constexpr size_t ArraySize(T (&)[N]) noexcept
226 {
227 return N;
228 }
229
CreateJsUndefined(napi_env env)230 inline napi_value CreateJsUndefined(napi_env env)
231 {
232 napi_value result = nullptr;
233 napi_get_undefined(env, &result);
234 return result;
235 }
236
CreateJsNull(napi_env env)237 inline napi_value CreateJsNull(napi_env env)
238 {
239 napi_value result = nullptr;
240 napi_get_null(env, &result);
241 return result;
242 }
243
CreateJsNumber(napi_env env,int32_t value)244 inline napi_value CreateJsNumber(napi_env env, int32_t value)
245 {
246 napi_value result = nullptr;
247 napi_create_int32(env, value, &result);
248 return result;
249 }
250
CreateJsNumber(napi_env env,uint32_t value)251 inline napi_value CreateJsNumber(napi_env env, uint32_t value)
252 {
253 napi_value result = nullptr;
254 napi_create_uint32(env, value, &result);
255 return result;
256 }
257
CreateJsNumber(napi_env env,int64_t value)258 inline napi_value CreateJsNumber(napi_env env, int64_t value)
259 {
260 napi_value result = nullptr;
261 napi_create_int64(env, value, &result);
262 return result;
263 }
264
CreateJsNumber(napi_env env,double value)265 inline napi_value CreateJsNumber(napi_env env, double value)
266 {
267 napi_value result = nullptr;
268 napi_create_double(env, value, &result);
269 return result;
270 }
271
272 template<class T>
CreateJsValue(napi_env env,const T & value)273 napi_value CreateJsValue(napi_env env, const T& value)
274 {
275 using ValueType = std::remove_cv_t<std::remove_reference_t<T>>;
276 napi_value result = nullptr;
277 if constexpr (std::is_same_v<ValueType, bool>) {
278 napi_get_boolean(env, value, &result);
279 return result;
280 } else if constexpr (std::is_arithmetic_v<ValueType>) {
281 return CreateJsNumber(env, value);
282 } else if constexpr (std::is_same_v<ValueType, std::string>) {
283 napi_create_string_utf8(env, value.c_str(), value.length(), &result);
284 return result;
285 } else if constexpr (std::is_enum_v<ValueType>) {
286 return CreateJsNumber(env, static_cast<std::make_signed_t<ValueType>>(value));
287 } else if constexpr (std::is_same_v<ValueType, const char*>) {
288 (value != nullptr) ? napi_create_string_utf8(env, value, strlen(value), &result) :
289 napi_get_undefined(env, &result);
290 return result;
291 }
292 }
293
ConvertFromJsNumber(napi_env env,napi_value jsValue,int32_t & value)294 inline bool ConvertFromJsNumber(napi_env env, napi_value jsValue, int32_t& value)
295 {
296 return napi_get_value_int32(env, jsValue, &value) == napi_ok;
297 }
298
ConvertFromJsNumber(napi_env env,napi_value jsValue,uint32_t & value)299 inline bool ConvertFromJsNumber(napi_env env, napi_value jsValue, uint32_t& value)
300 {
301 return napi_get_value_uint32(env, jsValue, &value) == napi_ok;
302 }
303
ConvertFromJsNumber(napi_env env,napi_value jsValue,int64_t & value)304 inline bool ConvertFromJsNumber(napi_env env, napi_value jsValue, int64_t& value)
305 {
306 return napi_get_value_int64(env, jsValue, &value) == napi_ok;
307 }
308
ConvertFromJsNumber(napi_env env,napi_value jsValue,double & value)309 inline bool ConvertFromJsNumber(napi_env env, napi_value jsValue, double& value)
310 {
311 return napi_get_value_double(env, jsValue, &value) == napi_ok;
312 }
313
ConvertFromJsNumber(napi_env env,napi_value jsValue,bool & value)314 inline bool ConvertFromJsNumber(napi_env env, napi_value jsValue, bool& value)
315 {
316 return napi_get_value_bool(env, jsValue, &value) == napi_ok;
317 }
318
319 template<class T>
ConvertFromJsValue(napi_env env,napi_value jsValue,T & value)320 bool ConvertFromJsValue(napi_env env, napi_value jsValue, T& value)
321 {
322 if (jsValue == nullptr) {
323 return false;
324 }
325
326 using ValueType = std::remove_cv_t<std::remove_reference_t<T>>;
327 if constexpr (std::is_same_v<ValueType, bool>) {
328 return napi_get_value_bool(env, jsValue, &value) == napi_ok;
329 } else if constexpr (std::is_arithmetic_v<ValueType>) {
330 return ConvertFromJsNumber(env, jsValue, value);
331 } else if constexpr (std::is_same_v<ValueType, std::string>) {
332 size_t len = 0;
333 if (napi_get_value_string_utf8(env, jsValue, nullptr, 0, &len) != napi_ok) {
334 return false;
335 }
336 auto buffer = std::make_unique<char[]>(len + 1);
337 size_t strLength = 0;
338 if (napi_get_value_string_utf8(env, jsValue, buffer.get(), len + 1, &strLength) == napi_ok) {
339 value = buffer.get();
340 return true;
341 }
342 return false;
343 } else if constexpr (std::is_enum_v<ValueType>) {
344 std::make_signed_t<ValueType> numberValue = 0;
345 if (!ConvertFromJsNumber(env, jsValue, numberValue)) {
346 return false;
347 }
348 value = static_cast<ValueType>(numberValue);
349 return true;
350 }
351 return false;
352 }
353
354 bool ConvertFromJsColor(napi_env env, napi_value jsValue, int32_t* argb, size_t size);
355
356 bool ConvertFromJsRect(napi_env env, napi_value jsValue, double* ltrb, size_t size);
357
358 bool ConvertFromJsIRect(napi_env env, napi_value jsValue, int32_t* ltrb, size_t size);
359
360 bool ConvertFromJsPoint(napi_env env, napi_value jsValue, double* point, size_t size);
361
362 bool ConvertFromJsPoint3d(napi_env env, napi_value src, Point3& point3d);
363
364 bool ConvertFromJsShadowFlag(
365 napi_env env, napi_value src, ShadowFlags& shadowFlag, ShadowFlags defaultFlag = ShadowFlags::NONE);
366
ConvertFromJsNumber(napi_env env,napi_value jsValue,int32_t & value,int32_t lo,int32_t hi)367 inline bool ConvertFromJsNumber(napi_env env, napi_value jsValue, int32_t& value, int32_t lo, int32_t hi)
368 {
369 return napi_get_value_int32(env, jsValue, &value) == napi_ok && value >= lo && value <= hi;
370 }
371
GetDrawingPointXFromJsNumber(napi_env env,napi_value argValue,Drawing::Point & point)372 inline bool GetDrawingPointXFromJsNumber(napi_env env, napi_value argValue, Drawing::Point& point)
373 {
374 napi_value objValue = nullptr;
375 double targetX = 0;
376 if (napi_get_named_property(env, argValue, "x", &objValue) != napi_ok ||
377 napi_get_value_double(env, objValue, &targetX) != napi_ok) {
378 return false;
379 }
380 point.SetX(targetX);
381 return true;
382 }
383
GetDrawingPointYFromJsNumber(napi_env env,napi_value argValue,Drawing::Point & point)384 inline bool GetDrawingPointYFromJsNumber(napi_env env, napi_value argValue, Drawing::Point& point)
385 {
386 napi_value objValue = nullptr;
387 double targetY = 0;
388 if (napi_get_named_property(env, argValue, "y", &objValue) != napi_ok ||
389 napi_get_value_double(env, objValue, &targetY) != napi_ok) {
390 return false;
391 }
392 point.SetY(targetY);
393 return true;
394 }
395
GetDrawingPointFromJsValue(napi_env env,napi_value argValue,Drawing::Point & point)396 inline bool GetDrawingPointFromJsValue(napi_env env, napi_value argValue, Drawing::Point& point)
397 {
398 return GetDrawingPointXFromJsNumber(env, argValue, point) &&
399 GetDrawingPointYFromJsNumber(env, argValue, point);
400 }
401
402 bool ConvertFromJsPointsArray(napi_env env, napi_value array, Drawing::Point* points, uint32_t count);
403
GetDoubleAndConvertToJsValue(napi_env env,double d)404 inline napi_value GetDoubleAndConvertToJsValue(napi_env env, double d)
405 {
406 napi_value value = nullptr;
407 (void)napi_create_double(env, d, &value);
408 return value;
409 }
410
GetStringAndConvertToJsValue(napi_env env,std::string str)411 inline napi_value GetStringAndConvertToJsValue(napi_env env, std::string str)
412 {
413 napi_value objValue = nullptr;
414 napi_create_string_utf8(env, str.c_str(), str.length(), &objValue);
415 return objValue;
416 }
417
418 napi_value GetFontMetricsAndConvertToJsValue(napi_env env, FontMetrics* metrics);
419
GetRectAndConvertToJsValue(napi_env env,std::shared_ptr<Rect> rect)420 inline napi_value GetRectAndConvertToJsValue(napi_env env, std::shared_ptr<Rect> rect)
421 {
422 napi_value objValue = nullptr;
423 napi_create_object(env, &objValue);
424 if (rect != nullptr && objValue != nullptr) {
425 napi_set_named_property(env, objValue, "left", CreateJsNumber(env, rect->GetLeft()));
426 napi_set_named_property(env, objValue, "top", CreateJsNumber(env, rect->GetTop()));
427 napi_set_named_property(env, objValue, "right", CreateJsNumber(env, rect->GetRight()));
428 napi_set_named_property(env, objValue, "bottom", CreateJsNumber(env, rect->GetBottom()));
429 }
430 return objValue;
431 }
432
ConvertPointToJsValue(napi_env env,Drawing::Point & point)433 inline napi_value ConvertPointToJsValue(napi_env env, Drawing::Point& point)
434 {
435 napi_value objValue = nullptr;
436 napi_create_object(env, &objValue);
437 if (objValue != nullptr) {
438 if (napi_set_named_property(env, objValue, "x", CreateJsNumber(env, point.GetX())) != napi_ok ||
439 napi_set_named_property(env, objValue, "y", CreateJsNumber(env, point.GetY())) != napi_ok) {
440 return nullptr;
441 }
442 }
443 return objValue;
444 }
445
NapiGetUndefined(napi_env env)446 inline napi_value NapiGetUndefined(napi_env env)
447 {
448 napi_value result = nullptr;
449 napi_get_undefined(env, &result);
450 return result;
451 }
452
453 void BindNativeFunction(napi_env env, napi_value object, const char* name, const char* moduleName, napi_callback func);
454 napi_value CreateJsError(napi_env env, int32_t errCode, const std::string& message);
455
456 bool ConvertFromJsTextEncoding(napi_env env, TextEncoding& textEncoding, napi_value nativeType);
457
GetColorAndConvertToJsValue(napi_env env,const Color & color)458 inline napi_value GetColorAndConvertToJsValue(napi_env env, const Color& color)
459 {
460 napi_value objValue = nullptr;
461 napi_create_object(env, &objValue);
462 if (objValue != nullptr) {
463 napi_set_named_property(env, objValue, "alpha", CreateJsNumber(env, color.GetAlpha()));
464 napi_set_named_property(env, objValue, "red", CreateJsNumber(env, color.GetRed()));
465 napi_set_named_property(env, objValue, "green", CreateJsNumber(env, color.GetGreen()));
466 napi_set_named_property(env, objValue, "blue", CreateJsNumber(env, color.GetBlue()));
467 }
468 return objValue;
469 }
470
471 napi_value NapiThrowError(napi_env env, DrawingErrorCode err, const std::string& message);
472
473 std::shared_ptr<Font> GetThemeFont(std::shared_ptr<Font> font);
474 } // namespace Drawing
475 } // namespace OHOS::Rosen
476
477 #ifdef ROSEN_OHOS
478
479 #undef LOG_DOMAIN
480 #define LOG_DOMAIN 0xD001400
481
482 #undef LOG_TAG
483 #define LOG_TAG "JsDrawing"
484
485 #define ROSEN_LOGI(format, ...) \
486 HILOG_INFO(LOG_CORE, format, ##__VA_ARGS__)
487 #define ROSEN_LOGD(format, ...) \
488 HILOG_DEBUG(LOG_CORE, format, ##__VA_ARGS__)
489 #define ROSEN_LOGE(format, ...) \
490 HILOG_ERROR(LOG_CORE, format, ##__VA_ARGS__)
491 #else
492 #define ROSEN_LOGI(format, ...)
493 #define ROSEN_LOGD(format, ...)
494 #define ROSEN_LOGE(format, ...)
495 #endif
496
497 #endif // OHOS_JS_DRAWING_UTILS_H