• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 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_offscreen_canvas.h"
17 
18 #include "base/utils/utils.h"
19 #include "bridge/declarative_frontend/engine/bindings.h"
20 #include "bridge/declarative_frontend/jsview/js_canvas_gradient.h"
21 #include "bridge/declarative_frontend/jsview/js_canvas_pattern.h"
22 #include "bridge/declarative_frontend/jsview/js_matrix2d.h"
23 #include "bridge/declarative_frontend/jsview/js_render_image.h"
24 #include "napi/native_api.h"
25 #include "napi/native_node_api.h"
26 
27 namespace OHOS::Ace::Framework {
28 constexpr int32_t ARGS_COUNT_ONE = 1;
29 constexpr int32_t ARGS_COUNT_TWO = 2;
30 
DetachOffscreenCanvas(napi_env env,void * value,void * hint)31 void* DetachOffscreenCanvas(napi_env env, void* value, void* hint)
32 {
33     if (value == nullptr) {
34         LOGW("Invalid parameter.");
35         return nullptr;
36     }
37     JSOffscreenCanvas* workCanvas = (JSOffscreenCanvas*)value;
38     if (workCanvas->IsGetContext()) {
39         JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s",
40             "An OffscreenCanvas could not be transferred because it had a rendering context.");
41         return nullptr;
42     }
43     if (workCanvas->IsDetached()) {
44         JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s",
45             "An OffscreenCanvas could not be transferred because the object is detached.");
46         return nullptr;
47     }
48     workCanvas->SetDetachStatus(true);
49 
50     auto result = new (std::nothrow) JSOffscreenCanvas();
51     result->SetWidth(workCanvas->GetWidth());
52     result->SetHeight(workCanvas->GetHeight());
53     return result;
54 }
55 
AttachOffscreenCanvas(napi_env env,void * value,void *)56 napi_value AttachOffscreenCanvas(napi_env env, void* value, void*)
57 {
58     if (value == nullptr) {
59         LOGW("Invalid parameter.");
60         return nullptr;
61     }
62     JSOffscreenCanvas* workCanvas = (JSOffscreenCanvas*)value;
63     if (workCanvas == nullptr) {
64         LOGW("Invalid context.");
65         return nullptr;
66     }
67 
68     napi_value offscreenCanvas = nullptr;
69     napi_create_object(env, &offscreenCanvas);
70     napi_property_descriptor desc[] = {
71         DECLARE_NAPI_GETTER_SETTER("width", JSOffscreenCanvas::JsGetWidth, JSOffscreenCanvas::JsSetWidth),
72         DECLARE_NAPI_GETTER_SETTER("height", JSOffscreenCanvas::JsGetHeight, JSOffscreenCanvas::JsSetHeight),
73         DECLARE_NAPI_FUNCTION("transferToImageBitmap", JSOffscreenCanvas::JsTransferToImageBitmap),
74         DECLARE_NAPI_FUNCTION("getContext", JSOffscreenCanvas::JsGetContext),
75     };
76     napi_define_properties(env, offscreenCanvas, sizeof(desc) / sizeof(*desc), desc);
77     napi_coerce_to_native_binding_object(
78         env, offscreenCanvas, DetachOffscreenCanvas, AttachOffscreenCanvas, value, nullptr);
79     napi_wrap(
80         env, offscreenCanvas, value,
81         [](napi_env env, void* data, void* hint) {
82             LOGD("Finalizer for offscreen canvas is called");
83             auto wrapper = reinterpret_cast<JSOffscreenCanvas*>(data);
84             delete wrapper;
85             wrapper = nullptr;
86         },
87         nullptr, nullptr);
88     return offscreenCanvas;
89 }
90 
InitOffscreenCanvas(napi_env env)91 napi_value JSOffscreenCanvas::InitOffscreenCanvas(napi_env env)
92 {
93     napi_value object = nullptr;
94     napi_create_object(env, &object);
95 
96     napi_property_descriptor desc[] = {
97         DECLARE_NAPI_GETTER_SETTER("width", JsGetWidth, JsSetWidth),
98         DECLARE_NAPI_GETTER_SETTER("height", JsGetHeight, JsSetHeight),
99         DECLARE_NAPI_FUNCTION("transferToImageBitmap", JsTransferToImageBitmap),
100         DECLARE_NAPI_FUNCTION("getContext", JsGetContext),
101     };
102     napi_status status = napi_define_class(
103         env, "OffscreenCanvas", NAPI_AUTO_LENGTH, Constructor, nullptr, sizeof(desc) / sizeof(*desc), desc, &object);
104     if (status != napi_ok) {
105         LOGW("Initialize offscreen canvas failed");
106         return nullptr;
107     }
108     return object;
109 }
110 
JSBind(BindingTarget globalObj,void * nativeEngine)111 void JSOffscreenCanvas::JSBind(BindingTarget globalObj, void* nativeEngine)
112 {
113     if (!nativeEngine) {
114         return;
115     }
116     napi_env env = reinterpret_cast<napi_env>(nativeEngine);
117 
118     napi_value jsGlobalObj = nullptr;
119     napi_get_global(env, &jsGlobalObj);
120 
121     napi_value result = InitOffscreenCanvas(env);
122     napi_set_named_property(env, jsGlobalObj, "OffscreenCanvas", result);
123 }
124 
Constructor(napi_env env,napi_callback_info info)125 napi_value JSOffscreenCanvas::Constructor(napi_env env, napi_callback_info info)
126 {
127     ContainerScope scope(Container::CurrentIdSafely());
128     size_t argc = 2;
129     napi_value thisVar = nullptr;
130     napi_value argv[2] = { nullptr };
131     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
132     if (argc < ARGS_COUNT_TWO || argv[0] == nullptr || argv[1] == nullptr) {
133         LOGW("Invalid args.");
134         return nullptr;
135     }
136     double fWidth = 0.0;
137     double fHeight = 0.0;
138     auto workCanvas = new (std::nothrow) JSOffscreenCanvas();
139     auto context = PipelineBase::GetCurrentContext();
140     if (context != nullptr) {
141         workCanvas->instanceId_ = context->GetInstanceId();
142     }
143     if (napi_get_value_double(env, argv[0], &fWidth) == napi_ok) {
144         fWidth = PipelineBase::Vp2PxWithCurrentDensity(fWidth);
145         workCanvas->SetWidth(fWidth);
146     }
147     if (napi_get_value_double(env, argv[1], &fHeight) == napi_ok) {
148         fHeight = PipelineBase::Vp2PxWithCurrentDensity(fHeight);
149         workCanvas->SetHeight(fHeight);
150     }
151 
152     napi_coerce_to_native_binding_object(
153         env, thisVar, DetachOffscreenCanvas, AttachOffscreenCanvas, workCanvas, nullptr);
154     napi_wrap(
155         env, thisVar, workCanvas,
156         [](napi_env env, void* data, void* hint) {
157             LOGD("Finalizer for offscreen canvas is called");
158             auto workCanvas = reinterpret_cast<JSOffscreenCanvas*>(data);
159             delete workCanvas;
160             workCanvas = nullptr;
161         },
162         nullptr, nullptr);
163     return thisVar;
164 }
165 
JsGetWidth(napi_env env,napi_callback_info info)166 napi_value JSOffscreenCanvas::JsGetWidth(napi_env env, napi_callback_info info)
167 {
168     ContainerScope scope(Container::CurrentIdSafely());
169     JSOffscreenCanvas* me = static_cast<JSOffscreenCanvas*>(GetNapiCallbackInfoAndThis(env, info));
170     napi_value defaultWidth = nullptr;
171     napi_create_double(env, 0.0, &defaultWidth);
172     return (me != nullptr && !me->isDetached_) ? me->OnGetWidth(env) : defaultWidth;
173 }
174 
JsGetHeight(napi_env env,napi_callback_info info)175 napi_value JSOffscreenCanvas::JsGetHeight(napi_env env, napi_callback_info info)
176 {
177     ContainerScope scope(Container::CurrentIdSafely());
178     JSOffscreenCanvas* me = static_cast<JSOffscreenCanvas*>(GetNapiCallbackInfoAndThis(env, info));
179     napi_value defaultHeight = nullptr;
180     napi_create_double(env, 0.0, &defaultHeight);
181     return (me != nullptr && !me->isDetached_) ? me->OnGetHeight(env) : defaultHeight;
182 }
183 
JsSetWidth(napi_env env,napi_callback_info info)184 napi_value JSOffscreenCanvas::JsSetWidth(napi_env env, napi_callback_info info)
185 {
186     ContainerScope scope(Container::CurrentIdSafely());
187     JSOffscreenCanvas* me = static_cast<JSOffscreenCanvas*>(GetNapiCallbackInfoAndThis(env, info));
188     return (me != nullptr && !me->isDetached_) ? me->OnSetWidth(env, info) : nullptr;
189 }
190 
JsSetHeight(napi_env env,napi_callback_info info)191 napi_value JSOffscreenCanvas::JsSetHeight(napi_env env, napi_callback_info info)
192 {
193     ContainerScope scope(Container::CurrentIdSafely());
194     JSOffscreenCanvas* me = static_cast<JSOffscreenCanvas*>(GetNapiCallbackInfoAndThis(env, info));
195     return (me != nullptr && !me->isDetached_) ? me->OnSetHeight(env, info) : nullptr;
196 }
JsTransferToImageBitmap(napi_env env,napi_callback_info info)197 napi_value JSOffscreenCanvas::JsTransferToImageBitmap(napi_env env, napi_callback_info info)
198 {
199     ContainerScope scope(Container::CurrentIdSafely());
200     JSOffscreenCanvas* me = static_cast<JSOffscreenCanvas*>(GetNapiCallbackInfoAndThis(env, info));
201     if (me->isDetached_) {
202         JSException::Throw("%s", "Failed to execute 'transferToImageBitmap' on 'OffscreenCanvas': Cannot transfer an "
203                                  "ImageBitmap from a detached OffscreenCanvas");
204         return nullptr;
205     }
206     napi_value defaultImage = nullptr;
207     napi_create_object(env, &defaultImage);
208     return (me != nullptr) ? me->onTransferToImageBitmap(env) : defaultImage;
209 }
210 
JsGetContext(napi_env env,napi_callback_info info)211 napi_value JSOffscreenCanvas::JsGetContext(napi_env env, napi_callback_info info)
212 {
213     ContainerScope scope(Container::CurrentIdSafely());
214     JSOffscreenCanvas* me = static_cast<JSOffscreenCanvas*>(GetNapiCallbackInfoAndThis(env, info));
215     if (me->isDetached_) {
216         JSException::Throw(
217             "%s", "Failed to execute 'getContext' on 'OffscreenCanvas': OffscreenCanvas object is detached");
218         return nullptr;
219     }
220     napi_value defaultContext = nullptr;
221     napi_create_object(env, &defaultContext);
222     return (me != nullptr) ? me->onGetContext(env, info) : defaultContext;
223 }
224 
OnGetWidth(napi_env env)225 napi_value JSOffscreenCanvas::OnGetWidth(napi_env env)
226 {
227     double fWidth = PipelineBase::Px2VpWithCurrentDensity(GetWidth());
228     napi_value width = nullptr;
229     napi_create_double(env, fWidth, &width);
230     return width;
231 }
232 
OnGetHeight(napi_env env)233 napi_value JSOffscreenCanvas::OnGetHeight(napi_env env)
234 {
235     double fHeight = PipelineBase::Px2VpWithCurrentDensity(GetHeight());
236     napi_value height = nullptr;
237     napi_create_double(env, fHeight, &height);
238     return height;
239 }
240 
OnSetWidth(napi_env env,napi_callback_info info)241 napi_value JSOffscreenCanvas::OnSetWidth(napi_env env, napi_callback_info info)
242 {
243     CHECK_NULL_RETURN(offscreenCanvasPattern_, nullptr);
244     size_t argc = 0;
245     napi_value argv = nullptr;
246     napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr);
247     if (argc != ARGS_COUNT_ONE) {
248         LOGD("Invalid args.");
249         return nullptr;
250     }
251     napi_get_cb_info(env, info, &argc, &argv, nullptr, nullptr);
252     if (argv == nullptr) {
253         return nullptr;
254     }
255     double width = 0.0;
256     if (napi_get_value_double(env, argv, &width) == napi_ok) {
257         width = PipelineBase::Vp2PxWithCurrentDensity(width);
258     } else {
259         return nullptr;
260     }
261 
262     if (width_ != width) {
263         width_ = width;
264         offscreenCanvasPattern_->UpdateSize(width_, height_);
265     }
266     return nullptr;
267 }
268 
OnSetHeight(napi_env env,napi_callback_info info)269 napi_value JSOffscreenCanvas::OnSetHeight(napi_env env, napi_callback_info info)
270 {
271     CHECK_NULL_RETURN(offscreenCanvasPattern_, nullptr);
272     size_t argc = 0;
273     napi_value argv = nullptr;
274     napi_get_cb_info(env, info, &argc, nullptr, nullptr, nullptr);
275     if (argc != ARGS_COUNT_ONE) {
276         LOGD("Invalid args.");
277         return nullptr;
278     }
279     napi_get_cb_info(env, info, &argc, &argv, nullptr, nullptr);
280     if (argv == nullptr) {
281         return nullptr;
282     }
283     double height = 0.0;
284     if (napi_get_value_double(env, argv, &height) == napi_ok) {
285         height = PipelineBase::Vp2PxWithCurrentDensity(height);
286     } else {
287         return nullptr;
288     }
289 
290     if (height_ != height) {
291         height_ = height;
292         offscreenCanvasPattern_->UpdateSize(width_, height_);
293     }
294     return nullptr;
295 }
296 
onTransferToImageBitmap(napi_env env)297 napi_value JSOffscreenCanvas::onTransferToImageBitmap(napi_env env)
298 {
299     std::string type = "ImageBitmap";
300     if (offscreenCanvasContext_ == nullptr) {
301         return nullptr;
302     }
303     uint32_t id = offscreenCanvasContext_->GetId();
304     auto final_height = static_cast<uint32_t>(GetHeight());
305     auto final_width = static_cast<uint32_t>(GetWidth());
306     napi_value renderImage = nullptr;
307     napi_value jsType = nullptr;
308     napi_value jsId = nullptr;
309     napi_value jsHeight = nullptr;
310     napi_value jsWidth = nullptr;
311     napi_create_object(env, &renderImage);
312     napi_create_string_utf8(env, type.c_str(), type.length(), &jsType);
313     napi_create_uint32(env, id, &jsId);
314     napi_create_double(env, final_height, &jsHeight);
315     napi_create_double(env, final_width, &jsWidth);
316     napi_set_named_property(env, renderImage, "__type", jsType);
317     napi_set_named_property(env, renderImage, "__id", jsId);
318     napi_set_named_property(env, renderImage, "height", jsHeight);
319     napi_set_named_property(env, renderImage, "width", jsWidth);
320     return renderImage;
321 }
322 
onGetContext(napi_env env,napi_callback_info info)323 napi_value JSOffscreenCanvas::onGetContext(napi_env env, napi_callback_info info)
324 {
325     isGetContext_ = true;
326     size_t argc = 2;
327     napi_value argv[2] = { nullptr };
328     napi_value offscreenCanvas = nullptr;
329     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &offscreenCanvas, nullptr));
330     if (argc < ARGS_COUNT_ONE || argc > ARGS_COUNT_TWO) {
331         LOGD("Invalid args.");
332         return nullptr;
333     }
334     if (argv[0] == nullptr) {
335         return nullptr;
336     }
337     if (!Container::IsCurrentUseNewPipeline()) {
338         return nullptr;
339     }
340 
341     size_t textLen = 0;
342     std::string contextType = "";
343     napi_get_value_string_utf8(env, argv[0], nullptr, 0, &textLen);
344     std::unique_ptr<char[]> text = std::make_unique<char[]>(textLen + 1);
345     napi_get_value_string_utf8(env, argv[0], text.get(), textLen + 1, &textLen);
346     contextType = text.get();
347     if (contextType == "2d") {
348         contextType_ = ContextType::CONTEXT_2D;
349         napi_value contextObj = CreateContext2d(env, GetWidth(), GetHeight());
350         if (contextObj == nullptr) {
351             return nullptr;
352         }
353         napi_value isSucceed = nullptr;
354         if (napi_get_named_property(env, contextObj, "__isSucceed", &isSucceed) == napi_ok) {
355             bool value = true;
356             napi_get_value_bool(env, isSucceed, &value);
357             if (!value) {
358                 return nullptr;
359             }
360         } else {
361             return nullptr;
362         }
363         if (argv[1] != nullptr) {
364             panda::Local<panda::ObjectRef> localValue = NapiValueToLocalValue(argv[1]);
365             JSObject jsObject(localValue);
366             offscreenCanvasSettings_ = jsObject.Unwrap<JSRenderingContextSettings>();
367             if (offscreenCanvasSettings_ != nullptr && offscreenCanvasContext_ != nullptr) {
368                 bool anti = offscreenCanvasSettings_->GetAntialias();
369                 offscreenCanvasContext_->SetAnti(anti);
370                 offscreenCanvasContext_->SetAntiAlias();
371             }
372         }
373         return contextObj;
374     }
375     return nullptr;
376 }
377 
CreateContext2d(napi_env env,double width,double height)378 napi_value JSOffscreenCanvas::CreateContext2d(napi_env env, double width, double height)
379 {
380     napi_value global = nullptr;
381     napi_status status = napi_get_global(env, &global);
382     if (status != napi_ok) {
383         return nullptr;
384     }
385     napi_value constructor = nullptr;
386     status = napi_get_named_property(env, global, "OffscreenCanvasRenderingContext2D", &constructor);
387     if (status != napi_ok) {
388         return nullptr;
389     }
390 
391     napi_value thisVal = nullptr;
392     napi_create_object(env, &thisVal);
393     status = napi_new_instance(env, constructor, 0, nullptr, &thisVal);
394     if (status != napi_ok) {
395         return nullptr;
396     }
397     if (instanceId_ != -1) {
398         offscreenCanvasPattern_ = AceType::MakeRefPtr<NG::OffscreenCanvasPattern>(
399             GetContext(), static_cast<int32_t>(width), static_cast<int32_t>(height));
400     } else {
401         offscreenCanvasPattern_ = AceType::MakeRefPtr<NG::OffscreenCanvasPattern>(
402             static_cast<int32_t>(width), static_cast<int32_t>(height));
403     }
404     if (offscreenCanvasPattern_ == nullptr) {
405         return thisVal;
406     }
407     bool isSucceed = true;
408     napi_value value = nullptr;
409     if (!offscreenCanvasPattern_->IsSucceed()) {
410         isSucceed = false;
411         napi_create_int32(env, isSucceed, &value);
412         napi_set_named_property(env, thisVal, "__isSucceed", value);
413         return thisVal;
414     }
415     napi_create_int32(env, isSucceed, &value);
416     napi_set_named_property(env, thisVal, "__isSucceed", value);
417 
418     panda::Local<panda::ObjectRef> localValue = NapiValueToLocalValue(thisVal);
419     JSObject jsObject(localValue);
420     offscreenCanvasContext_ = Referenced::Claim(jsObject.Unwrap<JSOffscreenRenderingContext>());
421     offscreenCanvasContext_->SetInstanceId(Container::CurrentId());
422     offscreenCanvasContext_->SetOffscreenPattern(offscreenCanvasPattern_);
423     offscreenCanvasContext_->AddOffscreenCanvasPattern(offscreenCanvasPattern_);
424     return thisVal;
425 }
426 } // namespace OHOS::Ace::Framework
427