1 /*
2  * Copyright (c) 2021 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 "bridge/declarative_frontend/jsview/canvas/js_render_image.h"
17 
18 #include "napi/native_api.h"
19 #include "napi/native_node_api.h"
20 
21 #include "bridge/declarative_frontend/jsview/canvas/js_rendering_context.h"
22 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
23 #include "core/common/container.h"
24 
25 #ifdef PIXEL_MAP_SUPPORTED
26 #include "pixel_map.h"
27 #include "pixel_map_napi.h"
28 #endif
29 
30 namespace OHOS::Ace::Framework {
31 
BindNativeFunction(napi_env env,napi_value object,const char * name,napi_callback func)32 void BindNativeFunction(napi_env env, napi_value object, const char* name, napi_callback func)
33 {
34     std::string funcName(name);
35     napi_value result = nullptr;
36     napi_create_function(env, funcName.c_str(), funcName.length(), func, nullptr, &result);
37     napi_set_named_property(env, object, name, result);
38 }
39 
GetNapiCallbackInfoAndThis(napi_env env,napi_callback_info info)40 void* GetNapiCallbackInfoAndThis(napi_env env, napi_callback_info info)
41 {
42     napi_value value = nullptr;
43     napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, &value, nullptr);
44     if (status != napi_ok) {
45         return nullptr;
46     }
47     void* result = nullptr;
48     status = napi_unwrap(env, value, &result);
49     if (status != napi_ok) {
50         return nullptr;
51     }
52     return result;
53 }
54 
DetachImageBitmap(napi_env env,void * value,void * hint)55 void* DetachImageBitmap(napi_env env, void* value, void* hint)
56 {
57     return value;
58 }
59 
AttachImageBitmap(napi_env env,void * value,void *)60 napi_value AttachImageBitmap(napi_env env, void* value, void*)
61 {
62     if (value == nullptr) {
63         LOGW("Invalid parameter.");
64         return nullptr;
65     }
66     auto* image = (JSRenderImage*)value;
67     if (image == nullptr) {
68         LOGW("Invalid context.");
69         return nullptr;
70     }
71 
72     napi_value imageBitmap = nullptr;
73     napi_create_object(env, &imageBitmap);
74     napi_value isImageBitmap = nullptr;
75     napi_create_int32(env, 1, &isImageBitmap);
76     napi_property_descriptor desc[] = {
77         DECLARE_NAPI_GETTER_SETTER("width", JSRenderImage::JsGetWidth, JSRenderImage::JsSetWidth),
78         DECLARE_NAPI_GETTER_SETTER("height", JSRenderImage::JsGetHeight, JSRenderImage::JsSetHeight),
79         DECLARE_NAPI_FUNCTION("close", JSRenderImage::JsClose),
80         DECLARE_NAPI_PROPERTY("isImageBitmap", isImageBitmap),
81     };
82     napi_define_properties(env, imageBitmap, sizeof(desc) / sizeof(*desc), desc);
83 
84     napi_coerce_to_native_binding_object(env, imageBitmap, DetachImageBitmap, AttachImageBitmap, value, nullptr);
85     napi_wrap_with_size(env, imageBitmap, value, JSRenderImage::Finalizer, nullptr, nullptr, image->GetBindingSize());
86     image->AddNativeRef();
87     return imageBitmap;
88 }
89 
JSRenderImage()90 JSRenderImage::JSRenderImage() {}
91 
Finalizer(napi_env env,void * data,void * hint)92 void JSRenderImage::Finalizer(napi_env env, void* data, void* hint)
93 {
94     auto wrapper = reinterpret_cast<JSRenderImage*>(data);
95     if (wrapper) {
96         wrapper->Release();
97     }
98 }
99 
Constructor(napi_env env,napi_callback_info info)100 napi_value JSRenderImage::Constructor(napi_env env, napi_callback_info info)
101 {
102     ContainerScope scope(Container::CurrentIdSafely());
103     size_t argc = 0;
104     napi_value thisVar = nullptr;
105     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, nullptr, &thisVar, nullptr));
106     auto wrapper = new (std::nothrow) JSRenderImage();
107     wrapper->SetInstanceId(OHOS::Ace::Container::CurrentId());
108     if (argc <= 0) {
109         napi_coerce_to_native_binding_object(env, thisVar, DetachImageBitmap, AttachImageBitmap, wrapper, nullptr);
110         napi_wrap(env, thisVar, wrapper, Finalizer, nullptr, nullptr);
111         wrapper->AddNativeRef();
112         return thisVar;
113     }
114     napi_value argv[2] = { nullptr };
115     napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
116     if (argc == 2) { // 2: args count
117         int32_t unit = 0;
118         napi_get_value_int32(env, argv[1], &unit);
119         if (static_cast<CanvasUnit>(unit) == CanvasUnit::PX) {
120             wrapper->SetUnit(CanvasUnit::PX);
121         }
122     }
123     size_t textLen = 0;
124     auto status = napi_get_value_string_utf8(env, argv[0], nullptr, 0, &textLen);
125     if (status == napi_ok) {
126         auto context = PipelineBase::GetCurrentContext();
127         if (!context) {
128             DELETE_RETURN_NULL(wrapper);
129         }
130         std::string textString = GetSrcString(env, argv[0], textLen);
131         if (context->IsFormRender() && NotFormSupport(textString)) {
132             LOGE("Not supported src : %{public}s when form render", textString.c_str());
133             DELETE_RETURN_NULL(wrapper);
134         }
135         wrapper->LoadImage(textString);
136     } else {
137 #ifdef PIXEL_MAP_SUPPORTED
138         auto pixelMap = GetPixelMap(env, argv[0]);
139         if (!pixelMap) {
140             DELETE_RETURN_NULL(wrapper);
141         }
142         wrapper->LoadImage(pixelMap);
143 #endif
144     }
145     napi_coerce_to_native_binding_object(env, thisVar, DetachImageBitmap, AttachImageBitmap, wrapper, nullptr);
146     napi_wrap_with_size(env, thisVar, wrapper, Finalizer, nullptr, nullptr, wrapper->GetBindingSize());
147     wrapper->AddNativeRef();
148     return thisVar;
149 }
150 
NotFormSupport(const std::string & textString)151 bool JSRenderImage::NotFormSupport(const std::string& textString)
152 {
153     SrcType srcType = ImageSourceInfo::ResolveURIType(textString);
154     return (srcType == SrcType::NETWORK || srcType == SrcType::FILE || srcType == SrcType::DATA_ABILITY);
155 }
156 
GetSrcString(napi_env env,napi_value value,size_t textLen)157 std::string JSRenderImage::GetSrcString(napi_env env, napi_value value, size_t textLen)
158 {
159     std::unique_ptr<char[]> text = std::make_unique<char[]>(textLen + 1);
160     auto status = napi_get_value_string_utf8(env, value, text.get(), textLen + 1, &textLen);
161     if ((status == napi_ok) && (text != nullptr)) {
162         return text.get();
163     }
164     return "";
165 }
166 
167 #ifdef PIXEL_MAP_SUPPORTED
GetPixelMap(napi_env env,napi_value value)168 RefPtr<PixelMap> JSRenderImage::GetPixelMap(napi_env env, napi_value value)
169 {
170     Media::PixelMapNapi* napiPixelMap = nullptr;
171     auto status = napi_unwrap(env, value, reinterpret_cast<void**>(&napiPixelMap));
172     if ((status != napi_ok) || (napiPixelMap == nullptr)) {
173         return nullptr;
174     }
175     return PixelMap::CreatePixelMap(napiPixelMap->GetPixelMap());
176 }
177 #endif
178 
InitImageBitmap(napi_env env)179 napi_value JSRenderImage::InitImageBitmap(napi_env env)
180 {
181     napi_value object = nullptr;
182     napi_create_object(env, &object);
183     napi_value isImageBitmap = nullptr;
184     napi_create_int32(env, 1, &isImageBitmap);
185 
186     napi_property_descriptor desc[] = {
187         DECLARE_NAPI_GETTER_SETTER("width", JsGetWidth, JsSetWidth),
188         DECLARE_NAPI_GETTER_SETTER("height", JsGetHeight, JsSetHeight),
189         DECLARE_NAPI_FUNCTION("close", JsClose),
190         DECLARE_NAPI_PROPERTY("isImageBitmap", isImageBitmap),
191     };
192     napi_status status = napi_define_class(
193         env, "ImageBitmap", NAPI_AUTO_LENGTH, Constructor, nullptr, sizeof(desc) / sizeof(*desc), desc, &object);
194     if (status != napi_ok) {
195         LOGW("Initialize image bitmap failed");
196         return nullptr;
197     }
198     return object;
199 }
200 
JSBind(BindingTarget globalObj,void * nativeEngine)201 void JSRenderImage::JSBind(BindingTarget globalObj, void* nativeEngine)
202 {
203     if (!nativeEngine) {
204         return;
205     }
206     napi_env env = reinterpret_cast<napi_env>(nativeEngine);
207 
208     napi_value jsGlobalObj = nullptr;
209     napi_get_global(env, &jsGlobalObj);
210 
211     napi_value result = InitImageBitmap(env);
212     napi_set_named_property(env, jsGlobalObj, "ImageBitmap", result);
213 }
214 
JsGetWidth(napi_env env,napi_callback_info info)215 napi_value JSRenderImage::JsGetWidth(napi_env env, napi_callback_info info)
216 {
217     JSRenderImage* me = static_cast<JSRenderImage*>(GetNapiCallbackInfoAndThis(env, info));
218     return (me != nullptr) ? me->OnGetWidth(env) : nullptr;
219 }
220 
JsGetHeight(napi_env env,napi_callback_info info)221 napi_value JSRenderImage::JsGetHeight(napi_env env, napi_callback_info info)
222 {
223     JSRenderImage* me = static_cast<JSRenderImage*>(GetNapiCallbackInfoAndThis(env, info));
224     return (me != nullptr) ? me->OnGetHeight(env) : nullptr;
225 }
226 
JsClose(napi_env env,napi_callback_info info)227 napi_value JSRenderImage::JsClose(napi_env env, napi_callback_info info)
228 {
229     JSRenderImage* me = static_cast<JSRenderImage*>(GetNapiCallbackInfoAndThis(env, info));
230     return (me != nullptr) ? me->OnClose() : nullptr;
231 }
232 
JsSetWidth(napi_env env,napi_callback_info info)233 napi_value JSRenderImage::JsSetWidth(napi_env env, napi_callback_info info)
234 {
235     JSRenderImage* me = static_cast<JSRenderImage*>(GetNapiCallbackInfoAndThis(env, info));
236     return (me != nullptr) ? me->OnSetWidth() : nullptr;
237 }
238 
JsSetHeight(napi_env env,napi_callback_info info)239 napi_value JSRenderImage::JsSetHeight(napi_env env, napi_callback_info info)
240 {
241     JSRenderImage* me = static_cast<JSRenderImage*>(GetNapiCallbackInfoAndThis(env, info));
242     return (me != nullptr) ? me->OnSetHeight() : nullptr;
243 }
244 
OnGetWidth(napi_env env)245 napi_value JSRenderImage::OnGetWidth(napi_env env)
246 {
247     double width = 0.0;
248     double density = GetDensity();
249     width = width_;
250     width /= density;
251     napi_value jsWidth = nullptr;
252     napi_create_double(env, width, &jsWidth);
253     return jsWidth;
254 }
255 
OnGetHeight(napi_env env)256 napi_value JSRenderImage::OnGetHeight(napi_env env)
257 {
258     double height = 0.0;
259     double density = GetDensity();
260     height = height_;
261     height /= density;
262     napi_value jsHeight = nullptr;
263     napi_create_double(env, height, &jsHeight);
264     return jsHeight;
265 }
266 
OnSetWidth()267 napi_value JSRenderImage::OnSetWidth()
268 {
269     return nullptr;
270 }
271 
OnSetHeight()272 napi_value JSRenderImage::OnSetHeight()
273 {
274     return nullptr;
275 }
276 
OnClose()277 napi_value JSRenderImage::OnClose()
278 {
279     for (const auto& closeCallback : closeCallbacks_) {
280         if (!closeCallback) {
281             continue;
282         }
283         closeCallback();
284     }
285     width_ = 0;
286     height_ = 0;
287     return nullptr;
288 }
289 
OnImageDataReady()290 void JSRenderImage::OnImageDataReady()
291 {
292     CHECK_NULL_VOID(loadingCtx_);
293     width_ = loadingCtx_->GetImageSize().Width();
294     height_ = loadingCtx_->GetImageSize().Height();
295     loadingCtx_->MakeCanvasImageIfNeed(loadingCtx_->GetImageSize(), true, ImageFit::NONE);
296 }
297 
OnImageLoadSuccess()298 void JSRenderImage::OnImageLoadSuccess()
299 {
300     CHECK_NULL_VOID(loadingCtx_);
301     image_ = loadingCtx_->MoveCanvasImage();
302     CHECK_NULL_VOID(image_);
303     imageObj_ = loadingCtx_->MoveImageObject();
304     CHECK_NULL_VOID(imageObj_);
305     pixelMap_ = image_->GetPixelMap();
306     svgDom_ = imageObj_->GetSVGDom();
307     imageFit_ = loadingCtx_->GetImageFit();
308     imageSize_ = loadingCtx_->GetImageSize();
309     bindingSize_ = pixelMap_ ? static_cast<size_t>(pixelMap_->GetByteCount()) : 0;
310 }
311 
OnImageLoadFail(const std::string & errorMsg)312 void JSRenderImage::OnImageLoadFail(const std::string& errorMsg)
313 {
314     width_ = 0;
315     height_ = 0;
316     pixelMap_ = nullptr;
317     svgDom_ = nullptr;
318 }
319 
LoadImage(const std::string & src)320 void JSRenderImage::LoadImage(const std::string& src)
321 {
322     src_ = src;
323     auto sourceInfo = ImageSourceInfo(src);
324     sourceInfo_ = sourceInfo;
325     LoadImage(sourceInfo);
326 }
327 
LoadImage(const RefPtr<PixelMap> & pixmap)328 void JSRenderImage::LoadImage(const RefPtr<PixelMap>& pixmap)
329 {
330     auto sourceInfo = ImageSourceInfo(pixmap);
331     sourceInfo_ = sourceInfo;
332     LoadImage(sourceInfo);
333 }
334 
LoadImage(const ImageSourceInfo & sourceInfo)335 void JSRenderImage::LoadImage(const ImageSourceInfo& sourceInfo)
336 {
337     auto dataReadyCallback = [jsRenderImage = this](const ImageSourceInfo& sourceInfo) {
338         CHECK_NULL_VOID(jsRenderImage);
339         jsRenderImage->OnImageDataReady();
340     };
341     auto loadSuccessCallback = [jsRenderImage = this](const ImageSourceInfo& sourceInfo) {
342         CHECK_NULL_VOID(jsRenderImage);
343         jsRenderImage->OnImageLoadSuccess();
344     };
345     auto loadFailCallback = [jsRenderImage = this](const ImageSourceInfo& sourceInfo, const std::string& errorMsg) {
346         CHECK_NULL_VOID(jsRenderImage);
347         jsRenderImage->OnImageLoadFail(errorMsg);
348     };
349     NG::LoadNotifier loadNotifier(dataReadyCallback, loadSuccessCallback, loadFailCallback);
350     loadingCtx_ = AceType::MakeRefPtr<NG::ImageLoadingContext>(sourceInfo, std::move(loadNotifier), true);
351     loadingCtx_->LoadImageData();
352 }
353 
GetSrc()354 std::string JSRenderImage::GetSrc()
355 {
356     return src_;
357 }
358 
GetWidth()359 double JSRenderImage::GetWidth()
360 {
361     return width_;
362 }
363 
SetWidth(double width)364 void JSRenderImage::SetWidth(double width)
365 {
366     width_ = width;
367 }
368 
GetHeight()369 double JSRenderImage::GetHeight()
370 {
371     return height_;
372 }
373 
SetHeight(double height)374 void JSRenderImage::SetHeight(double height)
375 {
376     height_ = height;
377 }
378 
SetCloseCallback(std::function<void ()> && callback)379 void JSRenderImage::SetCloseCallback(std::function<void()>&& callback)
380 {
381     closeCallbacks_.emplace_back(std::move(callback));
382 }
383 
CreateJSRenderImage(napi_env env,RefPtr<PixelMap> pixelMap,napi_value & renderImage)384 bool JSRenderImage::CreateJSRenderImage(napi_env env, RefPtr<PixelMap> pixelMap, napi_value& renderImage)
385 {
386     napi_value global = nullptr;
387     napi_status status = napi_get_global(env, &global);
388     if (status != napi_ok) {
389         return false;
390     }
391     napi_value constructor = nullptr;
392     status = napi_get_named_property(env, global, "ImageBitmap", &constructor);
393     if (status != napi_ok) {
394         return false;
395     }
396 #ifdef PIXEL_MAP_SUPPORTED
397     CHECK_NULL_RETURN(pixelMap, false);
398     auto pixelmapSharedPtr = pixelMap->GetPixelMapSharedPtr();
399     napi_value napiValue = OHOS::Media::PixelMapNapi::CreatePixelMap(env, pixelmapSharedPtr);
400     status = napi_new_instance(env, constructor, 1, &napiValue, &renderImage);
401 #else
402     status = napi_new_instance(env, constructor, 0, nullptr, &renderImage);
403 #endif
404     return status == napi_ok;
405 }
406 } // namespace OHOS::Ace::Framework