1 /*
2  * Copyright (c) 2023-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 #include "js_drawable_descriptor.h"
17 
18 #include "drawable_descriptor.h"
19 #include "interfaces/inner_api/drawable_descriptor/drawable_descriptor.h"
20 #include "js_native_api.h"
21 #include "js_native_api_types.h"
22 #include "napi/native_api.h"
23 #include "napi/native_common.h"
24 #include "napi/native_node_api.h"
25 #ifndef PREVIEW
26 #include "pixel_map_napi.h"
27 #endif
28 
29 namespace {
30 constexpr char DRAWABLE_BASE[] = "DrawableDescriptor";
31 constexpr char DRAWABLE_LAYERED[] = "LayeredDrawableDescriptor";
32 constexpr char DRAWABLE_ANIMATED[] = "AnimatedDrawableDescriptor";
33 constexpr char DRAWABLE_PIXELMAP[] = "PixelMapDrawableDescriptor";
34 constexpr int32_t PARAMS_NUM_ONE = 1;
35 constexpr int32_t PARAMS_NUM_THREE = 3;
36 
37 constexpr int32_t FOREGROUND_INDEX = 0;
38 constexpr int32_t BACKGROUND_INDEX = 1;
39 constexpr int32_t MASK_INDEX = 2;
40 
UpdateLayeredParam(OHOS::Ace::Napi::LayeredDrawableDescriptor * layeredDrawable,int32_t pos,std::shared_ptr<OHOS::Media::PixelMap> pixelMap)41 void UpdateLayeredParam(OHOS::Ace::Napi::LayeredDrawableDescriptor* layeredDrawable, int32_t pos,
42     std::shared_ptr<OHOS::Media::PixelMap> pixelMap)
43 {
44     if (!layeredDrawable || !pixelMap) {
45         return;
46     }
47     switch (pos) {
48         case FOREGROUND_INDEX:
49             layeredDrawable->SetForeground(pixelMap);
50             break;
51         case BACKGROUND_INDEX:
52             layeredDrawable->SetBackground(pixelMap);
53             break;
54         case MASK_INDEX:
55             layeredDrawable->SetMask(pixelMap);
56             break;
57         default:
58             HILOGW("Arg[%{public}d] index error", pos);
59     }
60 }
61 } // namespace
62 
63 namespace OHOS::Ace::Napi {
64 thread_local napi_ref JsDrawableDescriptor::baseConstructor_;
65 thread_local napi_ref JsDrawableDescriptor::layeredConstructor_;
66 thread_local napi_ref JsDrawableDescriptor::animatedConstructor_;
67 thread_local napi_ref JsDrawableDescriptor::pixelMapConstructor_;
68 
GetPixelMapArray(napi_env env,napi_value arg,std::vector<std::shared_ptr<Media::PixelMap>> & pixelMaps)69 static bool GetPixelMapArray(
70     napi_env env, napi_value arg, std::vector<std::shared_ptr<Media::PixelMap>>& pixelMaps)
71 {
72     bool isArray = false;
73     uint32_t length = 0;
74     napi_is_array(env, arg, &isArray);
75     if (!isArray) {
76         return false;
77     }
78     napi_get_array_length(env, arg, &length);
79     if (length < 1) {
80         return false;
81     }
82     for (uint32_t i = 0; i < length; i++) {
83         napi_value pixelMapValue = nullptr;
84         napi_get_element(env, arg, i, &pixelMapValue);
85         if (pixelMapValue == nullptr) {
86             continue;
87         }
88         Media::PixelMapNapi* pixmapNapi = nullptr;
89         napi_unwrap(env, pixelMapValue, reinterpret_cast<void**>(&pixmapNapi));
90         if (pixmapNapi == nullptr) {
91             continue;
92         }
93         pixelMaps.push_back(*(pixmapNapi->GetPixelMap()));
94     }
95     if (pixelMaps.size() <= 0) {
96         return false;
97     }
98     return true;
99 }
100 
AnimatedConstructor(napi_env env,napi_callback_info info)101 napi_value JsDrawableDescriptor::AnimatedConstructor(napi_env env, napi_callback_info info)
102 {
103     napi_escapable_handle_scope scope = nullptr;
104     napi_open_escapable_handle_scope(env, &scope);
105 
106     size_t argc = 2;
107     napi_value argv[2] = {nullptr};
108     napi_value thisVar = nullptr;
109     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
110     if (argc < 1) {
111         return nullptr;
112     }
113     std::vector<std::shared_ptr<Media::PixelMap>> pixelMaps;
114     if (!GetPixelMapArray(env, argv[0], pixelMaps)) {
115         return nullptr;
116     }
117 
118     //napi_get_named_property
119     napi_value napiDuration;
120     napi_value napiIterations;
121     int32_t duration = -1;
122     int32_t iterations = 1;
123     if (argc > 1 && argv[1]) {
124         napi_get_named_property(env, argv[1], "duration", &napiDuration);
125         napi_get_named_property(env, argv[1], "iterations", &napiIterations);
126         napi_get_value_int32(env, napiDuration, &duration);
127         napi_get_value_int32(env, napiIterations, &iterations);
128     }
129     // create JsDrawable
130     auto* animatedDrawable = new AnimatedDrawableDescriptor(pixelMaps, duration, iterations);
131     // wrap to napi_value
132     napi_wrap(env, thisVar, animatedDrawable, Destructor, nullptr, nullptr);
133     napi_escape_handle(env, scope, thisVar, &thisVar);
134     napi_close_escapable_handle_scope(env, scope);
135     return thisVar;
136 }
137 
Constructor(napi_env env,napi_callback_info info)138 napi_value JsDrawableDescriptor::Constructor(napi_env env, napi_callback_info info)
139 {
140     napi_escapable_handle_scope scope = nullptr;
141     napi_open_escapable_handle_scope(env, &scope);
142     napi_value thisVar = nullptr;
143     NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr));
144     // create JsDrawable
145     auto* drawable = new DrawableDescriptor;
146     // wrap to napi_value
147     napi_wrap(env, thisVar, drawable, Destructor, nullptr, nullptr);
148     napi_escape_handle(env, scope, thisVar, &thisVar);
149     napi_close_escapable_handle_scope(env, scope);
150     return thisVar;
151 }
152 
PixelMapConstructor(napi_env env,napi_callback_info info)153 napi_value JsDrawableDescriptor::PixelMapConstructor(napi_env env, napi_callback_info info)
154 {
155     napi_escapable_handle_scope scope = nullptr;
156     napi_open_escapable_handle_scope(env, &scope);
157     size_t argc = PARAMS_NUM_ONE;
158     napi_value argv[argc];
159     napi_value thisVar = nullptr;
160     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
161     auto* drawable = new DrawableDescriptor;
162     if (argc == 0) {
163         HILOGW("JsDrawableDescriptor::PixelMapConstructor get null pixelMap param");
164         napi_wrap(env, thisVar, drawable, Destructor, nullptr, nullptr);
165         napi_escape_handle(env, scope, thisVar, &thisVar);
166         napi_close_escapable_handle_scope(env, scope);
167         return thisVar;
168     }
169     napi_value argPixelMap = argv[0];
170     napi_valuetype type;
171     napi_typeof(env, argPixelMap, &type);
172     if (type == napi_object) {
173         auto pixelMap = GetPixelMapFromNapi(env, argPixelMap);
174         if (pixelMap) {
175             drawable->SetPixelMap(pixelMap);
176         }
177     }
178 
179     // wrap to napi_value
180     napi_wrap(env, thisVar, drawable, Destructor, nullptr, nullptr);
181     napi_escape_handle(env, scope, thisVar, &thisVar);
182     napi_close_escapable_handle_scope(env, scope);
183     return thisVar;
184 }
185 
LayeredConstructor(napi_env env,napi_callback_info info)186 napi_value JsDrawableDescriptor::LayeredConstructor(napi_env env, napi_callback_info info)
187 {
188     napi_escapable_handle_scope scope = nullptr;
189     napi_open_escapable_handle_scope(env, &scope);
190     size_t argc = PARAMS_NUM_THREE;
191     napi_value argv[argc];
192     napi_value thisVar = nullptr;
193     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
194     auto pos = -1;
195     auto* layeredDrawable = new LayeredDrawableDescriptor;
196     if (argc == 0) {
197         napi_wrap(env, thisVar, layeredDrawable, Destructor, nullptr, nullptr);
198         napi_escape_handle(env, scope, thisVar, &thisVar);
199         napi_close_escapable_handle_scope(env, scope);
200         return thisVar;
201     }
202     std::shared_ptr<Media::PixelMap> foregroundPixelMap = nullptr;
203     auto updateUndefinedPixelMap = [&pos, &layeredDrawable, &foregroundPixelMap]() -> void {
204         if (pos == MASK_INDEX) {
205             std::shared_ptr<Global::Resource::ResourceManager> resMgr(Global::Resource::CreateResourceManager());
206             layeredDrawable->InitialMask(resMgr);
207         } else if (pos == BACKGROUND_INDEX && foregroundPixelMap) {
208             UpdateLayeredParam(layeredDrawable, pos, foregroundPixelMap);
209         }
210     };
211     napi_valuetype type;
212     for (const auto& arg : argv) {
213         pos++;
214         napi_typeof(env, arg, &type);
215         if (type == napi_undefined) {
216             updateUndefinedPixelMap();
217             continue;
218         }
219         auto pixelMap = GetPixelMapFromDrawableNapi(env, arg);
220         if (!pixelMap) {
221             updateUndefinedPixelMap();
222         } else {
223             UpdateLayeredParam(layeredDrawable, pos, pixelMap);
224             if (pos == FOREGROUND_INDEX) foregroundPixelMap = std::move(pixelMap);
225         }
226     }
227     napi_wrap(env, thisVar, layeredDrawable, Destructor, nullptr, nullptr);
228     napi_escape_handle(env, scope, thisVar, &thisVar);
229     napi_close_escapable_handle_scope(env, scope);
230     return thisVar;
231 }
232 
GetPixelMapFromNapi(napi_env env,napi_value napiValue)233 std::shared_ptr<Media::PixelMap> JsDrawableDescriptor::GetPixelMapFromNapi(napi_env env, napi_value napiValue)
234 {
235     Media::PixelMapNapi* pixelMapNapi = nullptr;
236     napi_unwrap(env, napiValue, reinterpret_cast<void**>(&pixelMapNapi));
237     if (pixelMapNapi == nullptr) {
238         HILOGI("JsDrawableDescriptor::GetPixelMapFromNapi Argv is invalid");
239         return nullptr;
240     }
241     auto pixelMap = *(pixelMapNapi->GetPixelMap());
242     if (pixelMap == nullptr) {
243         HILOGI("JsDrawableDescriptor::GetPixelMapFromNapi GetPixelNapiInner from media is nullptr");
244         return nullptr;
245     }
246     return pixelMap;
247 }
248 
GetPixelMapFromDrawableNapi(napi_env env,napi_value napiValue)249 std::shared_ptr<Media::PixelMap> JsDrawableDescriptor::GetPixelMapFromDrawableNapi(napi_env env, napi_value napiValue)
250 {
251     DrawableDescriptor* drawableNapi = nullptr;
252     napi_unwrap(env, napiValue, reinterpret_cast<void**>(&drawableNapi));
253     if (drawableNapi == nullptr) {
254         HILOGW("JsDrawableDescriptor::GetPixelMapFromDrawableNapi Argv is invalid");
255         return nullptr;
256     }
257     auto pixelMap = drawableNapi->GetPixelMap();
258     if (pixelMap == nullptr) {
259         HILOGW("JsDrawableDescriptor::GetPixelMapFromDrawableNapi GetPixelNapiInner from media is nullptr");
260         return nullptr;
261     }
262     return pixelMap;
263 }
264 
Destructor(napi_env,void * data,void *)265 void JsDrawableDescriptor::Destructor(napi_env /* env */, void* data, void* /* hint */)
266 {
267     auto* field = reinterpret_cast<DrawableDescriptor*>(data);
268     delete field;
269 }
270 
InitDrawable(napi_env env)271 napi_value JsDrawableDescriptor::InitDrawable(napi_env env)
272 {
273     napi_value cons = nullptr;
274     napi_property_descriptor baseDes[] = {
275         DECLARE_NAPI_FUNCTION("getPixelMap", GetPixelMap),
276     };
277     NAPI_CALL(env, napi_define_class(env, DRAWABLE_BASE, NAPI_AUTO_LENGTH, Constructor, nullptr,
278                        sizeof(baseDes) / sizeof(napi_property_descriptor), baseDes, &cons));
279     NAPI_CALL(env, napi_create_reference(env, cons, 1, &baseConstructor_));
280     return cons;
281 }
282 
InitPixelMapDrawable(napi_env env)283 napi_value JsDrawableDescriptor::InitPixelMapDrawable(napi_env env)
284 {
285     napi_value cons = nullptr;
286     napi_property_descriptor pixelDes[] = {
287         DECLARE_NAPI_FUNCTION("getPixelMap", GetPixelMap),
288     };
289     NAPI_CALL(env, napi_define_class(env, DRAWABLE_PIXELMAP, NAPI_AUTO_LENGTH, PixelMapConstructor, nullptr,
290                        sizeof(pixelDes) / sizeof(napi_property_descriptor), pixelDes, &cons));
291     NAPI_CALL(env, napi_create_reference(env, cons, 1, &pixelMapConstructor_));
292     return cons;
293 }
294 
InitLayeredDrawable(napi_env env)295 napi_value JsDrawableDescriptor::InitLayeredDrawable(napi_env env)
296 {
297     napi_value cons = nullptr;
298     napi_property_descriptor layeredDes[] = {
299         DECLARE_NAPI_FUNCTION("getPixelMap", GetPixelMap),
300         DECLARE_NAPI_FUNCTION("getForeground", GetForeground),
301         DECLARE_NAPI_FUNCTION("getBackground", GetBackground),
302         DECLARE_NAPI_FUNCTION("getMask", GetMask),
303         DECLARE_NAPI_STATIC_FUNCTION("getMaskClipPath", GetMaskClipPath),
304     };
305     NAPI_CALL(env, napi_define_class(env, DRAWABLE_LAYERED, NAPI_AUTO_LENGTH, LayeredConstructor, nullptr,
306                        sizeof(layeredDes) / sizeof(napi_property_descriptor), layeredDes, &cons));
307     NAPI_CALL(env, napi_create_reference(env, cons, 1, &layeredConstructor_));
308     return cons;
309 }
310 
InitAnimatedDrawable(napi_env env)311 napi_value JsDrawableDescriptor::InitAnimatedDrawable(napi_env env)
312 {
313     napi_value cons = nullptr;
314     napi_property_descriptor animatedDes[] = {
315         DECLARE_NAPI_FUNCTION("getPixelMap", GetPixelMap),
316     };
317     NAPI_CALL(env, napi_define_class(env, DRAWABLE_ANIMATED, NAPI_AUTO_LENGTH, AnimatedConstructor, nullptr,
318                        sizeof(animatedDes) / sizeof(napi_property_descriptor), animatedDes, &cons));
319     NAPI_CALL(env, napi_create_reference(env, cons, 1, &animatedConstructor_));
320     return cons;
321 }
322 
ToNapi(napi_env env,DrawableDescriptor * drawable,DrawableDescriptor::DrawableType type)323 napi_value JsDrawableDescriptor::ToNapi(
324     napi_env env, DrawableDescriptor* drawable, DrawableDescriptor::DrawableType type)
325 {
326     if (!drawable) {
327         return nullptr;
328     }
329     if (!baseConstructor_ || !layeredConstructor_ || !animatedConstructor_ || !pixelMapConstructor_) {
330         // init js class constructor by importing module manually
331         napi_value globalValue;
332         napi_get_global(env, &globalValue);
333         napi_value func;
334         napi_get_named_property(env, globalValue, "requireNapi", &func);
335 
336         napi_value module;
337         napi_create_string_utf8(env, MODULE_NAME, NAPI_AUTO_LENGTH, &module);
338         napi_value returnValue;
339         napi_call_function(env, globalValue, func, 1, &module, &returnValue);
340     }
341 
342     napi_value constructor = nullptr;
343     napi_value result = nullptr;
344     napi_status status;
345     switch (type) {
346         case DrawableDescriptor::DrawableType::ANIMATED:
347             status = napi_get_reference_value(env, animatedConstructor_, &constructor);
348             break;
349         case DrawableDescriptor::DrawableType::LAYERED:
350             status = napi_get_reference_value(env, layeredConstructor_, &constructor);
351             break;
352         case DrawableDescriptor::DrawableType::BASE:
353             status = napi_get_reference_value(env, baseConstructor_, &constructor);
354             break;
355         case DrawableDescriptor::DrawableType::PIXELMAP:
356             status = napi_get_reference_value(env, pixelMapConstructor_, &constructor);
357             break;
358         default:
359             status = napi_status::napi_invalid_arg;
360             break;
361     }
362     if (status == napi_status::napi_ok) {
363         NAPI_CALL(env, napi_new_instance(env, constructor, 0, nullptr, &result));
364         NAPI_CALL(env, napi_wrap(env, result, drawable, Destructor, nullptr, nullptr));
365     } else {
366         HILOGI("create reference failed, drawable constructor is null");
367     }
368 
369     return result;
370 }
371 
GetPixelMap(napi_env env,napi_callback_info info)372 napi_value JsDrawableDescriptor::GetPixelMap(napi_env env, napi_callback_info info)
373 {
374     napi_escapable_handle_scope scope = nullptr;
375     napi_open_escapable_handle_scope(env, &scope);
376     napi_value thisVar = nullptr;
377     NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr));
378     void* native = nullptr;
379     napi_unwrap(env, thisVar, &native);
380     auto* drawable = reinterpret_cast<DrawableDescriptor*>(native);
381     auto pixmap = drawable->GetPixelMap();
382 
383     napi_value result = Media::PixelMapNapi::CreatePixelMap(env, pixmap);
384     napi_escape_handle(env, scope, result, &result);
385     napi_close_escapable_handle_scope(env, scope);
386     return result;
387 }
388 
GetForeground(napi_env env,napi_callback_info info)389 napi_value JsDrawableDescriptor::GetForeground(napi_env env, napi_callback_info info)
390 {
391     napi_escapable_handle_scope scope = nullptr;
392     napi_open_escapable_handle_scope(env, &scope);
393     napi_value thisVar = nullptr;
394     NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr));
395     void* native = nullptr;
396     napi_unwrap(env, thisVar, &native);
397     auto* drawable = reinterpret_cast<LayeredDrawableDescriptor*>(native);
398     if (!drawable) {
399         return nullptr;
400     }
401 
402     auto foreground = drawable->GetForeground();
403     napi_value result = ToNapi(env, foreground.release(), DrawableDescriptor::DrawableType::BASE);
404     napi_escape_handle(env, scope, result, &result);
405     napi_close_escapable_handle_scope(env, scope);
406     return result;
407 }
408 
GetBackground(napi_env env,napi_callback_info info)409 napi_value JsDrawableDescriptor::GetBackground(napi_env env, napi_callback_info info)
410 {
411     napi_escapable_handle_scope scope = nullptr;
412     napi_open_escapable_handle_scope(env, &scope);
413     napi_value thisVar = nullptr;
414     NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr));
415     void* native = nullptr;
416     napi_unwrap(env, thisVar, &native);
417     auto* drawable = reinterpret_cast<LayeredDrawableDescriptor*>(native);
418     if (!drawable) {
419         return nullptr;
420     }
421 
422     auto background = drawable->GetBackground();
423     napi_value result = ToNapi(env, background.release(), DrawableDescriptor::DrawableType::BASE);
424     napi_escape_handle(env, scope, result, &result);
425     napi_close_escapable_handle_scope(env, scope);
426     return result;
427 }
428 
GetMask(napi_env env,napi_callback_info info)429 napi_value JsDrawableDescriptor::GetMask(napi_env env, napi_callback_info info)
430 {
431     napi_escapable_handle_scope scope = nullptr;
432     napi_open_escapable_handle_scope(env, &scope);
433     napi_value thisVar = nullptr;
434     NAPI_CALL(env, napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr));
435     void* native = nullptr;
436     napi_unwrap(env, thisVar, &native);
437     auto* drawable = reinterpret_cast<LayeredDrawableDescriptor*>(native);
438     if (!drawable) {
439         return nullptr;
440     }
441 
442     auto mask = drawable->GetMask();
443     napi_value result = ToNapi(env, mask.release(), DrawableDescriptor::DrawableType::BASE);
444     napi_escape_handle(env, scope, result, &result);
445     napi_close_escapable_handle_scope(env, scope);
446     return result;
447 }
448 
GetMaskClipPath(napi_env env,napi_callback_info info)449 napi_value JsDrawableDescriptor::GetMaskClipPath(napi_env env, napi_callback_info info)
450 {
451     auto path = OHOS::Ace::Napi::LayeredDrawableDescriptor::GetStaticMaskClipPath();
452     napi_value result = nullptr;
453     if (napi_ok != napi_create_string_utf8(env, path.c_str(), NAPI_AUTO_LENGTH, &result)) {
454         HILOGI("JsDrawableDescriptor Failed");
455     }
456     return result;
457 }
458 
Export(napi_env env,napi_value exports)459 napi_value JsDrawableDescriptor::Export(napi_env env, napi_value exports)
460 {
461     // export base class
462     napi_value cons = InitDrawable(env);
463     NAPI_CALL(env, napi_set_named_property(env, exports, DRAWABLE_BASE, cons));
464 
465     cons = InitPixelMapDrawable(env);
466     NAPI_CALL(env, napi_set_named_property(env, exports, DRAWABLE_PIXELMAP, cons));
467 
468     // export child class
469     cons = InitLayeredDrawable(env);
470     NAPI_CALL(env, napi_set_named_property(env, exports, DRAWABLE_LAYERED, cons));
471 
472     //export child class
473     cons = InitAnimatedDrawable(env);
474     NAPI_CALL(env, napi_set_named_property(env, exports, DRAWABLE_ANIMATED, cons));
475 
476     return exports;
477 }
478 } // namespace OHOS::Ace::Napi
479