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/js_render_image.h"
17
18 #include "frameworks/core/common/container.h"
19 #include "bridge/declarative_frontend/jsview/js_rendering_context.h"
20 #include "bridge/declarative_frontend/jsview/js_view_common_def.h"
21 #include "napi/native_api.h"
22 #include "napi/native_node_api.h"
23
24 namespace OHOS::Ace::Framework {
25
BindNativeFunction(napi_env env,napi_value object,const char * name,napi_callback func)26 void BindNativeFunction(napi_env env, napi_value object, const char* name, napi_callback func)
27 {
28 std::string funcName(name);
29 napi_value result = nullptr;
30 napi_create_function(env, funcName.c_str(), funcName.length(), func, nullptr, &result);
31 napi_set_named_property(env, object, name, result);
32 }
33
GetNapiCallbackInfoAndThis(napi_env env,napi_callback_info info)34 void* GetNapiCallbackInfoAndThis(napi_env env, napi_callback_info info)
35 {
36 napi_value value = nullptr;
37 napi_status status = napi_get_cb_info(env, info, nullptr, nullptr, &value, nullptr);
38 if (status != napi_ok) {
39 return nullptr;
40 }
41 void* result = nullptr;
42 status = napi_unwrap(env, value, &result);
43 if (status != napi_ok) {
44 return nullptr;
45 }
46 return result;
47 }
48
DetachImageBitmap(napi_env env,void * value,void * hint)49 void* DetachImageBitmap(napi_env env, void* value, void* hint)
50 {
51 return value;
52 }
53
AttachImageBitmap(napi_env env,void * value,void *)54 napi_value AttachImageBitmap(napi_env env, void* value, void*)
55 {
56 if (value == nullptr) {
57 LOGW("Invalid parameter.");
58 return nullptr;
59 }
60 auto image = reinterpret_cast<std::weak_ptr<JSRenderImage>*>(value)->lock();
61 if (image == nullptr) {
62 LOGW("Invalid context.");
63 return nullptr;
64 }
65
66 napi_value imageBitmap = nullptr;
67 napi_create_object(env, &imageBitmap);
68 double width = image->GetWidth();
69 napi_value jsWidth = nullptr;
70 napi_create_double(env, width, &jsWidth);
71 double height = image->GetHeight();
72 napi_value jsHeight = nullptr;
73 napi_create_double(env, height, &jsHeight);
74 napi_value isImageBitmap = nullptr;
75 napi_create_int32(env, 1, &isImageBitmap);
76
77 napi_set_named_property(env, imageBitmap, "width", jsWidth);
78 napi_set_named_property(env, imageBitmap, "height", jsHeight);
79 napi_set_named_property(env, imageBitmap, "isImageBitmap", isImageBitmap);
80 BindNativeFunction(env, imageBitmap, "close", JSRenderImage::JsClose);
81
82 napi_coerce_to_native_binding_object(env, imageBitmap, DetachImageBitmap, AttachImageBitmap, value, nullptr);
83 napi_wrap(
84 env, imageBitmap, value,
85 [](napi_env env, void* data, void* hint) {
86 LOGD("Finalizer for image bitmap is called");
87 auto wrapper = reinterpret_cast<JSRenderImage*>(data);
88 delete wrapper;
89 wrapper = nullptr;
90 },
91 nullptr, nullptr);
92 return imageBitmap;
93 }
94
JSRenderImage()95 JSRenderImage::JSRenderImage() {}
96
Constructor(napi_env env,napi_callback_info info)97 napi_value JSRenderImage::Constructor(napi_env env, napi_callback_info info)
98 {
99 size_t argc = 1;
100 napi_value argv = nullptr;
101 napi_value thisVar = nullptr;
102 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, &argv, &thisVar, nullptr));
103 if (argc <= 0 || argv == nullptr) {
104 LOGW("Invalid args.");
105 return nullptr;
106 }
107 size_t textLen = 0;
108 std::string textString = "";
109 napi_get_value_string_utf8(env, argv, nullptr, 0, &textLen);
110 std::unique_ptr<char[]> text = std::make_unique<char[]>(textLen + 1);
111 napi_get_value_string_utf8(env, argv, text.get(), textLen + 1, &textLen);
112 textString = text.get();
113 auto context = PipelineBase::GetCurrentContext();
114 if (!context) {
115 LOGW("Invalid context.");
116 return nullptr;
117 }
118 if (context->IsFormRender()) {
119 SrcType srcType = ImageSourceInfo::ResolveURIType(textString);
120 bool notSupport = (srcType == SrcType::NETWORK || srcType == SrcType::FILE || srcType == SrcType::DATA_ABILITY);
121 if (notSupport) {
122 LOGE("Not supported src : %{public}s when form render", textString.c_str());
123 return nullptr;
124 }
125 }
126 auto wrapper = new (std::nothrow) JSRenderImage();
127 wrapper->SetInstanceId(OHOS::Ace::Container::CurrentId());
128 wrapper->LoadImage(textString);
129 napi_coerce_to_native_binding_object(env, thisVar, DetachImageBitmap, AttachImageBitmap, wrapper, nullptr);
130 napi_wrap(
131 env, thisVar, wrapper,
132 [](napi_env env, void* data, void* hint) {
133 LOGD("Finalizer for image bitmap is called");
134 auto wrapper = reinterpret_cast<JSRenderImage*>(data);
135 delete wrapper;
136 wrapper = nullptr;
137 },
138 nullptr, nullptr);
139 return thisVar;
140 }
141
InitImageBitmap(napi_env env)142 napi_value JSRenderImage::InitImageBitmap(napi_env env)
143 {
144 napi_value object = nullptr;
145 napi_create_object(env, &object);
146 napi_value isImageBitmap = nullptr;
147 napi_create_object(env, &isImageBitmap);
148 napi_create_int32(env, 1, &isImageBitmap);
149
150 napi_property_descriptor desc[] = {
151 DECLARE_NAPI_GETTER_SETTER("width", JsGetWidth, JsSetWidth),
152 DECLARE_NAPI_GETTER_SETTER("height", JsGetHeight, JsSetHeight),
153 DECLARE_NAPI_FUNCTION("close", JsClose),
154 DECLARE_NAPI_PROPERTY("isImageBitmap", isImageBitmap),
155 };
156 napi_status status = napi_define_class(
157 env, "ImageBitmap", NAPI_AUTO_LENGTH, Constructor, nullptr, sizeof(desc) / sizeof(*desc), desc, &object);
158 if (status != napi_ok) {
159 LOGW("Initialize image bitmap failed");
160 return nullptr;
161 }
162 return object;
163 }
164
JSBind(BindingTarget globalObj,void * nativeEngine)165 void JSRenderImage::JSBind(BindingTarget globalObj, void* nativeEngine)
166 {
167 if (!nativeEngine) {
168 return;
169 }
170 napi_env env = reinterpret_cast<napi_env>(nativeEngine);
171
172 napi_value jsGlobalObj = nullptr;
173 napi_get_global(env, &jsGlobalObj);
174
175 napi_value result = InitImageBitmap(env);
176 napi_set_named_property(env, jsGlobalObj, "ImageBitmap", result);
177 }
178
JsGetWidth(napi_env env,napi_callback_info info)179 napi_value JSRenderImage::JsGetWidth(napi_env env, napi_callback_info info)
180 {
181 JSRenderImage* me = static_cast<JSRenderImage*>(GetNapiCallbackInfoAndThis(env, info));
182 return (me != nullptr) ? me->OnGetWidth(env) : nullptr;
183 }
184
JsGetHeight(napi_env env,napi_callback_info info)185 napi_value JSRenderImage::JsGetHeight(napi_env env, napi_callback_info info)
186 {
187 JSRenderImage* me = static_cast<JSRenderImage*>(GetNapiCallbackInfoAndThis(env, info));
188 return (me != nullptr) ? me->OnGetHeight(env) : nullptr;
189 }
190
JsClose(napi_env env,napi_callback_info info)191 napi_value JSRenderImage::JsClose(napi_env env, napi_callback_info info)
192 {
193 JSRenderImage* me = static_cast<JSRenderImage*>(GetNapiCallbackInfoAndThis(env, info));
194 return (me != nullptr) ? me->OnClose() : nullptr;
195 }
196
JsSetWidth(napi_env env,napi_callback_info info)197 napi_value JSRenderImage::JsSetWidth(napi_env env, napi_callback_info info)
198 {
199 JSRenderImage* me = static_cast<JSRenderImage*>(GetNapiCallbackInfoAndThis(env, info));
200 return (me != nullptr) ? me->OnSetWidth() : nullptr;
201 }
202
JsSetHeight(napi_env env,napi_callback_info info)203 napi_value JSRenderImage::JsSetHeight(napi_env env, napi_callback_info info)
204 {
205 JSRenderImage* me = static_cast<JSRenderImage*>(GetNapiCallbackInfoAndThis(env, info));
206 return (me != nullptr) ? me->OnSetHeight() : nullptr;
207 }
208
OnGetWidth(napi_env env)209 napi_value JSRenderImage::OnGetWidth(napi_env env)
210 {
211 double width = 0.0;
212 width = width_;
213 width = PipelineBase::Px2VpWithCurrentDensity(width);
214 napi_value jsWidth = nullptr;
215 napi_create_double(env, width, &jsWidth);
216 return jsWidth;
217 }
218
OnGetHeight(napi_env env)219 napi_value JSRenderImage::OnGetHeight(napi_env env)
220 {
221 double height = 0.0;
222 height = height_;
223 height = PipelineBase::Px2VpWithCurrentDensity(height);
224 napi_value jsHeight = nullptr;
225 napi_create_double(env, height, &jsHeight);
226 return jsHeight;
227 }
228
OnSetWidth()229 napi_value JSRenderImage::OnSetWidth()
230 {
231 return nullptr;
232 }
233
OnSetHeight()234 napi_value JSRenderImage::OnSetHeight()
235 {
236 return nullptr;
237 }
238
OnClose()239 napi_value JSRenderImage::OnClose()
240 {
241 for (const auto& closeCallback : closeCallbacks_) {
242 if (!closeCallback) {
243 continue;
244 }
245 closeCallback();
246 }
247 width_ = 0;
248 height_ = 0;
249 return nullptr;
250 }
251
OnImageDataReady()252 void JSRenderImage::OnImageDataReady()
253 {
254 CHECK_NULL_VOID(loadingCtx_);
255 width_ = loadingCtx_->GetImageSize().Width();
256 height_ = loadingCtx_->GetImageSize().Height();
257 loadingCtx_->MakeCanvasImageIfNeed(loadingCtx_->GetImageSize(), true, ImageFit::NONE);
258 }
259
OnImageLoadSuccess()260 void JSRenderImage::OnImageLoadSuccess()
261 {
262 CHECK_NULL_VOID(loadingCtx_);
263 image_ = loadingCtx_->MoveCanvasImage();
264 CHECK_NULL_VOID(image_);
265 imageObj_ = loadingCtx_->MoveImageObject();
266 CHECK_NULL_VOID(imageObj_);
267 pixelMap_ = image_->GetPixelMap();
268 svgDom_ = imageObj_->GetSVGDom();
269 imageFit_ = loadingCtx_->GetImageFit();
270 imageSize_ = loadingCtx_->GetImageSize();
271 }
272
OnImageLoadFail(const std::string & errorMsg)273 void JSRenderImage::OnImageLoadFail(const std::string& errorMsg)
274 {
275 width_ = 0;
276 height_ = 0;
277 pixelMap_ = nullptr;
278 svgDom_ = nullptr;
279 }
280
LoadImage(const std::string & src)281 void JSRenderImage::LoadImage(const std::string& src)
282 {
283 src_ = src;
284 auto sourceInfo = ImageSourceInfo(src);
285 sourceInfo_ = sourceInfo;
286 LoadImage(sourceInfo);
287 }
288
LoadImage(const ImageSourceInfo & sourceInfo)289 void JSRenderImage::LoadImage(const ImageSourceInfo& sourceInfo)
290 {
291 auto dataReadyCallback = [jsRenderImage = this](const ImageSourceInfo& sourceInfo) {
292 CHECK_NULL_VOID(jsRenderImage);
293 jsRenderImage->OnImageDataReady();
294 };
295 auto loadSuccessCallback = [jsRenderImage = this](const ImageSourceInfo& sourceInfo) {
296 CHECK_NULL_VOID(jsRenderImage);
297 jsRenderImage->OnImageLoadSuccess();
298 };
299 auto loadFailCallback = [jsRenderImage = this](const ImageSourceInfo& sourceInfo, const std::string& errorMsg) {
300 CHECK_NULL_VOID(jsRenderImage);
301 jsRenderImage->OnImageLoadFail(errorMsg);
302 };
303 NG::LoadNotifier loadNotifier(dataReadyCallback, loadSuccessCallback, loadFailCallback);
304 loadingCtx_ = AceType::MakeRefPtr<NG::ImageLoadingContext>(sourceInfo, std::move(loadNotifier), true);
305 loadingCtx_->LoadImageData();
306 }
307
GetSrc()308 std::string JSRenderImage::GetSrc()
309 {
310 return src_;
311 }
312
GetWidth()313 double JSRenderImage::GetWidth()
314 {
315 return width_;
316 }
317
GetHeight()318 double JSRenderImage::GetHeight()
319 {
320 return height_;
321 }
322
SetCloseCallback(std::function<void ()> && callback)323 void JSRenderImage::SetCloseCallback(std::function<void()>&& callback)
324 {
325 closeCallbacks_.emplace_back(std::move(callback));
326 }
327 } // namespace OHOS::Ace::Framework
328