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