• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-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_rendering_context.h"
17 
18 #include <cstdint>
19 
20 #include "interfaces/inner_api/ace/ai/image_analyzer.h"
21 #include "js_native_api.h"
22 #include "js_native_api_types.h"
23 
24 #include "base/error/error_code.h"
25 #include "base/memory/referenced.h"
26 #include "base/log/ace_scoring_log.h"
27 #include "base/utils/utils.h"
28 #include "bridge/common/utils/engine_helper.h"
29 #include "bridge/declarative_frontend/engine/bindings.h"
30 #include "bridge/declarative_frontend/engine/js_converter.h"
31 #include "bridge/declarative_frontend/engine/js_types.h"
32 #include "bridge/declarative_frontend/jsview/canvas/js_offscreen_rendering_context.h"
33 #include "bridge/declarative_frontend/jsview/js_utils.h"
34 #include "bridge/declarative_frontend/jsview/models/canvas/canvas_rendering_context_2d_model_impl.h"
35 #include "core/common/container_scope.h"
36 #include "core/components_ng/pattern/canvas/canvas_rendering_context_2d_model_ng.h"
37 
38 namespace OHOS::Ace {
39 struct CanvasAsyncCxt {
40     napi_env env = nullptr;
41     napi_deferred deferred = nullptr;
42 };
43 } // namespace OHOS::Ace
44 
45 namespace OHOS::Ace::Framework {
46 
JSRenderingContext()47 JSRenderingContext::JSRenderingContext()
48 {
49 #ifdef NG_BUILD
50     renderingContext2DModel_ = AceType::MakeRefPtr<NG::CanvasRenderingContext2DModelNG>();
51 #else
52     if (Container::IsCurrentUseNewPipeline()) {
53         renderingContext2DModel_ = AceType::MakeRefPtr<NG::CanvasRenderingContext2DModelNG>();
54         auto onAttach = [weakCtx = WeakClaim(this)]() {
55             auto ctx = weakCtx.Upgrade();
56             CHECK_NULL_VOID(ctx);
57             ctx->OnAttachToCanvas();
58         };
59         auto onDetach = [weakCtx = WeakClaim(this)]() {
60             auto ctx = weakCtx.Upgrade();
61             CHECK_NULL_VOID(ctx);
62             ctx->OnDetachFromCanvas();
63         };
64         auto canvasRenderingContext2DModel =
65             AceType::DynamicCast<CanvasRenderingContext2DModel>(renderingContext2DModel_);
66         CHECK_NULL_VOID(canvasRenderingContext2DModel);
67         canvasRenderingContext2DModel->SetOnAttach(onAttach);
68         canvasRenderingContext2DModel->SetOnDetach(onDetach);
69     } else {
70         renderingContext2DModel_ = AceType::MakeRefPtr<Framework::CanvasRenderingContext2DModelImpl>();
71     }
72 #endif
73 }
74 
JSBind(BindingTarget globalObj)75 void JSRenderingContext::JSBind(BindingTarget globalObj)
76 {
77     // Define the class "CanvasRenderingContext2D"
78     JSClass<JSRenderingContext>::Declare("CanvasRenderingContext2D");
79 
80     // Define all properties of the "CanvasRenderingContext2D"
81     JSClass<JSRenderingContext>::CustomProperty(
82         "canvas", &JSRenderingContext::JsGetCanvas, &JSRenderingContext::JsSetCanvas);
83     JSClass<JSRenderingContext>::CustomProperty(
84         "width", &JSRenderingContext::JsGetWidth, &JSRenderingContext::JsSetWidth);
85     JSClass<JSRenderingContext>::CustomProperty(
86         "height", &JSRenderingContext::JsGetHeight, &JSRenderingContext::JsSetHeight);
87     JSClass<JSRenderingContext>::CustomProperty(
88         "filter", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetFilter);
89     JSClass<JSRenderingContext>::CustomProperty(
90         "direction", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetDirection);
91     JSClass<JSRenderingContext>::CustomProperty(
92         "fillStyle", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetFillStyle);
93     JSClass<JSRenderingContext>::CustomProperty(
94         "strokeStyle", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetStrokeStyle);
95     JSClass<JSRenderingContext>::CustomProperty(
96         "lineCap", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetLineCap);
97     JSClass<JSRenderingContext>::CustomProperty(
98         "lineJoin", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetLineJoin);
99     JSClass<JSRenderingContext>::CustomProperty(
100         "miterLimit", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetMiterLimit);
101     JSClass<JSRenderingContext>::CustomProperty(
102         "lineWidth", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetLineWidth);
103     JSClass<JSRenderingContext>::CustomProperty("font", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetFont);
104     JSClass<JSRenderingContext>::CustomProperty(
105         "textAlign", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetTextAlign);
106     JSClass<JSRenderingContext>::CustomProperty(
107         "textBaseline", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetTextBaseline);
108     JSClass<JSRenderingContext>::CustomProperty(
109         "globalAlpha", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetGlobalAlpha);
110     JSClass<JSRenderingContext>::CustomProperty(
111         "globalCompositeOperation", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetGlobalCompositeOperation);
112     JSClass<JSRenderingContext>::CustomProperty(
113         "lineDashOffset", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetLineDashOffset);
114     JSClass<JSRenderingContext>::CustomProperty(
115         "shadowBlur", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetShadowBlur);
116     JSClass<JSRenderingContext>::CustomProperty(
117         "shadowColor", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetShadowColor);
118     JSClass<JSRenderingContext>::CustomProperty(
119         "shadowOffsetX", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetShadowOffsetX);
120     JSClass<JSRenderingContext>::CustomProperty(
121         "shadowOffsetY", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetShadowOffsetY);
122     JSClass<JSRenderingContext>::CustomProperty(
123         "imageSmoothingEnabled", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetImageSmoothingEnabled);
124     JSClass<JSRenderingContext>::CustomProperty(
125         "imageSmoothingQuality", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetImageSmoothingQuality);
126     JSClass<JSRenderingContext>::CustomProperty(
127         "letterSpacing", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetLetterSpacing);
128 
129     // Define all methods of the "CanvasRenderingContext2D"
130     JSClass<JSRenderingContext>::CustomMethod("toDataURL", &JSCanvasRenderer::JsToDataUrl);
131     JSClass<JSRenderingContext>::CustomMethod("createRadialGradient", &JSCanvasRenderer::JsCreateRadialGradient);
132     JSClass<JSRenderingContext>::CustomMethod("fillRect", &JSCanvasRenderer::JsFillRect);
133     JSClass<JSRenderingContext>::CustomMethod("strokeRect", &JSCanvasRenderer::JsStrokeRect);
134     JSClass<JSRenderingContext>::CustomMethod("clearRect", &JSCanvasRenderer::JsClearRect);
135     JSClass<JSRenderingContext>::CustomMethod("createLinearGradient", &JSCanvasRenderer::JsCreateLinearGradient);
136     JSClass<JSRenderingContext>::CustomMethod("fillText", &JSCanvasRenderer::JsFillText);
137     JSClass<JSRenderingContext>::CustomMethod("strokeText", &JSCanvasRenderer::JsStrokeText);
138     JSClass<JSRenderingContext>::CustomMethod("measureText", &JSCanvasRenderer::JsMeasureText);
139     JSClass<JSRenderingContext>::CustomMethod("moveTo", &JSCanvasRenderer::JsMoveTo);
140     JSClass<JSRenderingContext>::CustomMethod("lineTo", &JSCanvasRenderer::JsLineTo);
141     JSClass<JSRenderingContext>::CustomMethod("bezierCurveTo", &JSCanvasRenderer::JsBezierCurveTo);
142     JSClass<JSRenderingContext>::CustomMethod("quadraticCurveTo", &JSCanvasRenderer::JsQuadraticCurveTo);
143     JSClass<JSRenderingContext>::CustomMethod("arcTo", &JSCanvasRenderer::JsArcTo);
144     JSClass<JSRenderingContext>::CustomMethod("arc", &JSCanvasRenderer::JsArc);
145     JSClass<JSRenderingContext>::CustomMethod("ellipse", &JSCanvasRenderer::JsEllipse);
146     JSClass<JSRenderingContext>::CustomMethod("fill", &JSCanvasRenderer::JsFill);
147     JSClass<JSRenderingContext>::CustomMethod("stroke", &JSCanvasRenderer::JsStroke);
148     JSClass<JSRenderingContext>::CustomMethod("clip", &JSCanvasRenderer::JsClip);
149     JSClass<JSRenderingContext>::CustomMethod("rect", &JSCanvasRenderer::JsRect);
150     JSClass<JSRenderingContext>::CustomMethod("roundRect", &JSRenderingContext::JsRoundRect);
151     JSClass<JSRenderingContext>::CustomMethod("beginPath", &JSCanvasRenderer::JsBeginPath);
152     JSClass<JSRenderingContext>::CustomMethod("closePath", &JSCanvasRenderer::JsClosePath);
153     JSClass<JSRenderingContext>::CustomMethod("restore", &JSCanvasRenderer::JsRestore);
154     JSClass<JSRenderingContext>::CustomMethod("save", &JSCanvasRenderer::JsSave);
155     JSClass<JSRenderingContext>::CustomMethod("rotate", &JSCanvasRenderer::JsRotate);
156     JSClass<JSRenderingContext>::CustomMethod("scale", &JSCanvasRenderer::JsScale);
157     JSClass<JSRenderingContext>::CustomMethod("getTransform", &JSCanvasRenderer::JsGetTransform);
158     JSClass<JSRenderingContext>::CustomMethod("setTransform", &JSCanvasRenderer::JsSetTransform);
159     JSClass<JSRenderingContext>::CustomMethod("resetTransform", &JSCanvasRenderer::JsResetTransform);
160     JSClass<JSRenderingContext>::CustomMethod("transform", &JSCanvasRenderer::JsTransform);
161     JSClass<JSRenderingContext>::CustomMethod("translate", &JSCanvasRenderer::JsTranslate);
162     JSClass<JSRenderingContext>::CustomMethod("setLineDash", &JSCanvasRenderer::JsSetLineDash);
163     JSClass<JSRenderingContext>::CustomMethod("getLineDash", &JSCanvasRenderer::JsGetLineDash);
164     JSClass<JSRenderingContext>::CustomMethod("drawImage", &JSCanvasRenderer::JsDrawImage);
165     JSClass<JSRenderingContext>::CustomMethod("createPattern", &JSCanvasRenderer::JsCreatePattern);
166     JSClass<JSRenderingContext>::CustomMethod("createImageData", &JSCanvasRenderer::JsCreateImageData);
167     JSClass<JSRenderingContext>::CustomMethod("putImageData", &JSCanvasRenderer::JsPutImageData);
168     JSClass<JSRenderingContext>::CustomMethod("getImageData", &JSCanvasRenderer::JsGetImageData);
169     JSClass<JSRenderingContext>::CustomMethod("getJsonData", &JSCanvasRenderer::JsGetJsonData);
170     JSClass<JSRenderingContext>::CustomMethod("getPixelMap", &JSCanvasRenderer::JsGetPixelMap);
171     JSClass<JSRenderingContext>::CustomMethod("setPixelMap", &JSCanvasRenderer::JsSetPixelMap);
172     JSClass<JSRenderingContext>::CustomMethod("drawBitmapMesh", &JSCanvasRenderer::JsDrawBitmapMesh);
173     JSClass<JSRenderingContext>::CustomMethod(
174         "transferFromImageBitmap", &JSRenderingContext::JsTransferFromImageBitmap);
175     JSClass<JSRenderingContext>::CustomMethod("createConicGradient", &JSCanvasRenderer::JsCreateConicGradient);
176     JSClass<JSRenderingContext>::CustomMethod("saveLayer", &JSCanvasRenderer::JsSaveLayer);
177     JSClass<JSRenderingContext>::CustomMethod("restoreLayer", &JSCanvasRenderer::JsRestoreLayer);
178     JSClass<JSRenderingContext>::CustomMethod("reset", &JSCanvasRenderer::JsReset);
179     JSClass<JSRenderingContext>::CustomMethod("startImageAnalyzer", &JSRenderingContext::JsStartImageAnalyzer);
180     JSClass<JSRenderingContext>::CustomMethod("stopImageAnalyzer", &JSRenderingContext::JsStopImageAnalyzer);
181     JSClass<JSRenderingContext>::CustomMethod("on", &JSRenderingContext::JsOn);
182     JSClass<JSRenderingContext>::CustomMethod("off", &JSRenderingContext::JsOff);
183 
184     // Register the "CanvasRenderingContext2D" to the global object of the vm
185     JSClass<JSRenderingContext>::Bind(globalObj, JSRenderingContext::Constructor, JSRenderingContext::Destructor);
186 }
187 
Constructor(const JSCallbackInfo & args)188 void JSRenderingContext::Constructor(const JSCallbackInfo& args)
189 {
190     auto jsRenderContext = Referenced::MakeRefPtr<JSRenderingContext>();
191     jsRenderContext->IncRefCount();
192     args.SetReturnValue(Referenced::RawPtr(jsRenderContext));
193 
194     auto* jsContextSetting = args.UnwrapArg<JSRenderingContextSettings>(0);
195     if (jsContextSetting) {
196         bool anti = jsContextSetting->GetAntialias();
197         jsRenderContext->SetAnti(anti);
198 
199         int32_t unit = 0;
200         if (args.GetInt32Arg(1, unit) && (static_cast<CanvasUnit>(unit) == CanvasUnit::PX)) {
201             jsRenderContext->SetUnit(CanvasUnit::PX);
202         }
203     }
204 }
205 
Destructor(JSRenderingContext * controller)206 void JSRenderingContext::Destructor(JSRenderingContext* controller)
207 {
208     if (controller != nullptr) {
209         controller->DecRefCount();
210     }
211 }
212 
OnAttachToCanvas()213 void JSRenderingContext::OnAttachToCanvas()
214 {
215     ContainerScope scope(instanceId_);
216     for (const auto& iter : attachCallback_) {
217         auto callback = iter.second;
218         if (callback) {
219             callback();
220         }
221     }
222 }
223 
OnDetachFromCanvas()224 void JSRenderingContext::OnDetachFromCanvas()
225 {
226     ContainerScope scope(instanceId_);
227     for (const auto& iter : detachCallback_) {
228         auto callback = iter.second;
229         if (callback) {
230             callback();
231         }
232     }
233 }
234 
JsGetCanvas(const JSCallbackInfo & info)235 void JSRenderingContext::JsGetCanvas(const JSCallbackInfo& info)
236 {
237     auto canvasRenderingContext2DModel = AceType::DynamicCast<CanvasRenderingContext2DModel>(renderingContext2DModel_);
238     CHECK_NULL_VOID(canvasRenderingContext2DModel);
239     auto nodeId = canvasRenderingContext2DModel->GetId();
240 
241     auto vm = info.GetVm();
242     auto globalObj = JSNApi::GetGlobalObject(vm);
243     auto globalFunc = globalObj->Get(vm, panda::StringRef::NewFromUtf8(vm, "__getFrameNodeByNodeId__"));
244     JsiValue jsiValue(globalFunc);
245     JsiRef<JsiValue> globalFuncRef = JsiRef<JsiValue>::Make(jsiValue);
246     if (!globalFuncRef->IsFunction()) {
247         return;
248     }
249     RefPtr<JsFunction> jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(globalFuncRef));
250     JSRef<JSVal> params[2]; // The count of the function's params is 2.
251     params[0] = JSRef<JSVal>::Make(ToJSValue(instanceId_));
252     params[1] = JSRef<JSVal>::Make(ToJSValue(nodeId));
253     auto returnPtr = jsFunc->ExecuteJS(2, params);
254     info.SetReturnValue(returnPtr);
255     return;
256 }
257 
JsSetCanvas(const JSCallbackInfo & info)258 void JSRenderingContext::JsSetCanvas(const JSCallbackInfo& info)
259 {
260     return;
261 }
262 
JsGetWidth(const JSCallbackInfo & info)263 void JSRenderingContext::JsGetWidth(const JSCallbackInfo& info)
264 {
265     double width = 0.0;
266     auto canvasRenderingContext2DModel = AceType::DynamicCast<CanvasRenderingContext2DModel>(renderingContext2DModel_);
267     CHECK_NULL_VOID(canvasRenderingContext2DModel);
268     canvasRenderingContext2DModel->GetWidth(width);
269     double density = GetDensity();
270     width /= density;
271     auto returnValue = JSVal(ToJSValue(width));
272     auto returnPtr = JSRef<JSVal>::Make(returnValue);
273     info.SetReturnValue(returnPtr);
274 }
275 
JsSetWidth(const JSCallbackInfo & info)276 void JSRenderingContext::JsSetWidth(const JSCallbackInfo& info)
277 {
278     return;
279 }
280 
JsSetHeight(const JSCallbackInfo & info)281 void JSRenderingContext::JsSetHeight(const JSCallbackInfo& info)
282 {
283     return;
284 }
285 
JsGetHeight(const JSCallbackInfo & info)286 void JSRenderingContext::JsGetHeight(const JSCallbackInfo& info)
287 {
288     double height = 0.0;
289     auto canvasRenderingContext2DModel = AceType::DynamicCast<CanvasRenderingContext2DModel>(renderingContext2DModel_);
290     CHECK_NULL_VOID(canvasRenderingContext2DModel);
291     canvasRenderingContext2DModel->GetHeight(height);
292     double density = GetDensity();
293     height /= density;
294     auto returnValue = JSVal(ToJSValue(height));
295     auto returnPtr = JSRef<JSVal>::Make(returnValue);
296     info.SetReturnValue(returnPtr);
297 }
298 
JsTransferFromImageBitmap(const JSCallbackInfo & info)299 void JSRenderingContext::JsTransferFromImageBitmap(const JSCallbackInfo& info)
300 {
301     ContainerScope scope(Container::CurrentIdSafely());
302     if (info.Length() == 0) {
303         return;
304     }
305     if (!info[0]->IsObject()) {
306         return;
307     }
308     auto engine = EngineHelper::GetCurrentEngine();
309     CHECK_NULL_VOID(engine);
310     NativeEngine* nativeEngine = engine->GetNativeEngine();
311     napi_env env = reinterpret_cast<napi_env>(nativeEngine);
312     panda::Local<JsiValue> value = info[0].Get().GetLocalHandle();
313     JSValueWrapper valueWrapper = value;
314     napi_value napiValue = nativeEngine->ValueToNapiValue(valueWrapper);
315     void* nativeObj = nullptr;
316     NAPI_CALL_RETURN_VOID(env, napi_unwrap(env, napiValue, &nativeObj));
317     auto jsImage = (JSRenderImage*)nativeObj;
318     CHECK_NULL_VOID(jsImage);
319     auto canvasRenderingContext2DModel = AceType::DynamicCast<CanvasRenderingContext2DModel>(renderingContext2DModel_);
320     CHECK_NULL_VOID(canvasRenderingContext2DModel);
321 #ifdef PIXEL_MAP_SUPPORTED
322     auto pixelMap = jsImage->GetPixelMap();
323     CHECK_NULL_VOID(pixelMap);
324     canvasRenderingContext2DModel->TransferFromImageBitmap(pixelMap);
325 #else
326     auto imageData = jsImage->GetImageData();
327     CHECK_NULL_VOID(imageData);
328     canvasRenderingContext2DModel->TransferFromImageBitmap(imageData);
329 #endif
330 }
331 
CreateErrorValue(napi_env env,int32_t errCode,const std::string & errMsg="")332 napi_value CreateErrorValue(napi_env env, int32_t errCode, const std::string& errMsg = "")
333 {
334     napi_value code = nullptr;
335     std::string codeStr = std::to_string(errCode);
336     napi_create_string_utf8(env, codeStr.c_str(), codeStr.length(), &code);
337     napi_value msg = nullptr;
338     napi_create_string_utf8(env, errMsg.c_str(), errMsg.length(), &msg);
339     napi_value error = nullptr;
340     napi_create_error(env, code, msg, &error);
341     return error;
342 }
343 
HandleDeferred(const shared_ptr<CanvasAsyncCxt> & asyncCtx,ImageAnalyzerState state)344 void HandleDeferred(const shared_ptr<CanvasAsyncCxt>& asyncCtx, ImageAnalyzerState state)
345 {
346     auto env = asyncCtx->env;
347     CHECK_NULL_VOID(env);
348     auto deferred = asyncCtx->deferred;
349     CHECK_NULL_VOID(deferred);
350 
351     napi_handle_scope scope = nullptr;
352     NAPI_CALL_RETURN_VOID(env, napi_open_handle_scope(env, &scope));
353 
354     napi_value result = nullptr;
355     switch (state) {
356         case ImageAnalyzerState::UNSUPPORTED:
357             result = CreateErrorValue(env, ERROR_CODE_AI_ANALYSIS_UNSUPPORTED);
358             napi_reject_deferred(env, deferred, result);
359             break;
360         case ImageAnalyzerState::ONGOING:
361             result = CreateErrorValue(env, ERROR_CODE_AI_ANALYSIS_IS_ONGOING);
362             napi_reject_deferred(env, deferred, result);
363             break;
364         case ImageAnalyzerState::STOPPED:
365             result = CreateErrorValue(env, ERROR_CODE_AI_ANALYSIS_IS_STOPPED);
366             napi_reject_deferred(env, deferred, result);
367             break;
368         case ImageAnalyzerState::FINISHED:
369             napi_get_null(env, &result);
370             napi_resolve_deferred(env, deferred, result);
371             break;
372         default:
373             break;
374     }
375     napi_close_handle_scope(env, scope);
376 }
377 
ReturnPromise(const JSCallbackInfo & info,napi_value result)378 void ReturnPromise(const JSCallbackInfo& info, napi_value result)
379 {
380     CHECK_NULL_VOID(result);
381     auto jsPromise = JsConverter::ConvertNapiValueToJsVal(result);
382     if (!jsPromise->IsObject()) {
383         return;
384     }
385     info.SetReturnValue(JSRef<JSObject>::Cast(jsPromise));
386 }
387 
JsStartImageAnalyzer(const JSCallbackInfo & info)388 void JSRenderingContext::JsStartImageAnalyzer(const JSCallbackInfo& info)
389 {
390     ContainerScope scope(instanceId_);
391     auto engine = EngineHelper::GetCurrentEngine();
392     CHECK_NULL_VOID(engine);
393     NativeEngine* nativeEngine = engine->GetNativeEngine();
394     auto env = reinterpret_cast<napi_env>(nativeEngine);
395     auto asyncCtx = std::make_shared<CanvasAsyncCxt>();
396     asyncCtx->env = env;
397     napi_value promise = nullptr;
398     napi_create_promise(env, &asyncCtx->deferred, &promise);
399     if (info.Length() < 1 || !info[0]->IsObject()) {
400         ReturnPromise(info, promise);
401         return;
402     }
403 
404     ScopeRAII scopeRaii(env);
405     panda::Local<JsiValue> value = info[0].Get().GetLocalHandle();
406     JSValueWrapper valueWrapper = value;
407     napi_value configNativeValue = nativeEngine->ValueToNapiValue(valueWrapper);
408 
409     if (isImageAnalyzing_) {
410         napi_value result = CreateErrorValue(env, ERROR_CODE_AI_ANALYSIS_IS_ONGOING);
411         napi_reject_deferred(env, asyncCtx->deferred, result);
412         ReturnPromise(info, promise);
413         return;
414     }
415 
416     OnAnalyzedCallback onAnalyzed_ = [asyncCtx, weakCtx = WeakClaim(this)](ImageAnalyzerState state) {
417         CHECK_NULL_VOID(asyncCtx);
418         HandleDeferred(asyncCtx, state);
419         auto ctx = weakCtx.Upgrade();
420         CHECK_NULL_VOID(ctx);
421         ctx->isImageAnalyzing_ = false;
422     };
423     isImageAnalyzing_ = true;
424     auto canvasRenderingContext2DModel = AceType::DynamicCast<CanvasRenderingContext2DModel>(renderingContext2DModel_);
425     CHECK_NULL_VOID(canvasRenderingContext2DModel);
426     canvasRenderingContext2DModel->StartImageAnalyzer(configNativeValue, onAnalyzed_);
427     ReturnPromise(info, promise);
428 }
429 
JsStopImageAnalyzer(const JSCallbackInfo & info)430 void JSRenderingContext::JsStopImageAnalyzer(const JSCallbackInfo& info)
431 {
432     ContainerScope scope(instanceId_);
433     auto canvasRenderingContext2DModel = AceType::DynamicCast<CanvasRenderingContext2DModel>(renderingContext2DModel_);
434     CHECK_NULL_VOID(canvasRenderingContext2DModel);
435     canvasRenderingContext2DModel->StopImageAnalyzer();
436 }
437 
FindCbList(napi_env env,napi_value cb,CanvasCallbackFuncPairList & callbackFuncPairList)438 CanvasCallbackFuncPairList::const_iterator JSRenderingContext::FindCbList(
439     napi_env env, napi_value cb, CanvasCallbackFuncPairList& callbackFuncPairList)
440 {
441     return std::find_if(callbackFuncPairList.begin(), callbackFuncPairList.end(), [env, cb](const auto& item) -> bool {
442         bool result = false;
443         napi_value refItem;
444         napi_get_reference_value(env, item.first, &refItem);
445         napi_strict_equals(env, refItem, cb, &result);
446         return result;
447     });
448 }
449 
AddCallbackToList(napi_env env,napi_value cb,CanvasCallbackType type,const std::function<void ()> && onFunc)450 void JSRenderingContext::AddCallbackToList(
451     napi_env env, napi_value cb, CanvasCallbackType type, const std::function<void()>&& onFunc)
452 {
453     if (type == CanvasCallbackType::ON_ATTACH) {
454         auto iter = FindCbList(env, cb, attachCallback_);
455         if (iter == attachCallback_.end()) {
456             napi_ref ref = nullptr;
457             napi_create_reference(env, cb, 1, &ref);
458             attachCallback_.emplace_back(ref, onFunc);
459         }
460     } else if (type == CanvasCallbackType::ON_DETACH) {
461         auto iter = FindCbList(env, cb, detachCallback_);
462         if (iter == detachCallback_.end()) {
463             napi_ref ref = nullptr;
464             napi_create_reference(env, cb, 1, &ref);
465             detachCallback_.emplace_back(ref, onFunc);
466         }
467     }
468 }
469 
DeleteCallbackFromList(uint32_t argc,napi_env env,napi_value cb,CanvasCallbackType type)470 void JSRenderingContext::DeleteCallbackFromList(uint32_t argc, napi_env env, napi_value cb, CanvasCallbackType type)
471 {
472     if (argc == 1) {
473         if (type == CanvasCallbackType::ON_ATTACH) {
474             for (const auto& item : attachCallback_) {
475                 napi_delete_reference(env, item.first);
476             }
477             attachCallback_.clear();
478         } else if (type == CanvasCallbackType::ON_DETACH) {
479             for (const auto& item : detachCallback_) {
480                 napi_delete_reference(env, item.first);
481             }
482             detachCallback_.clear();
483         }
484     } else if (argc == 2) { // The count of params is 2.
485         if (type == CanvasCallbackType::ON_ATTACH) {
486             auto iter = FindCbList(env, cb, attachCallback_);
487             if (iter != attachCallback_.end()) {
488                 napi_delete_reference(env, iter->first);
489                 attachCallback_.erase(iter);
490             }
491         } else if (type == CanvasCallbackType::ON_DETACH) {
492             auto iter = FindCbList(env, cb, detachCallback_);
493             if (iter != detachCallback_.end()) {
494                 napi_delete_reference(env, iter->first);
495                 detachCallback_.erase(iter);
496             }
497         }
498     }
499 }
500 
GetCanvasCallbackType(const std::string & strType)501 CanvasCallbackType JSRenderingContext::GetCanvasCallbackType(const std::string& strType)
502 {
503     CanvasCallbackType type = CanvasCallbackType::UNKNOWN;
504     static constexpr char attachType[] = "onAttach";
505     static constexpr char detachType[] = "onDetach";
506     if (strType.compare(attachType) == 0) {
507         type = CanvasCallbackType::ON_ATTACH;
508     } else if (strType.compare(detachType) == 0) {
509         type = CanvasCallbackType::ON_DETACH;
510     }
511     return type;
512 }
513 
JsOn(const JSCallbackInfo & info)514 void JSRenderingContext::JsOn(const JSCallbackInfo& info)
515 {
516     if (!info[0]->IsString() || !info[1]->IsFunction()) {
517         return;
518     }
519     const CanvasCallbackType type = GetCanvasCallbackType(info[0]->ToString());
520     if (type == CanvasCallbackType::UNKNOWN) {
521         JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter error.");
522         return;
523     }
524     auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[1]));
525     std::function<void()> onFunc = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc),
526                                        id = instanceId_]() {
527         ContainerScope scope(id);
528         JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
529         func->Execute();
530     };
531     ContainerScope scope(instanceId_);
532 #if !defined(PREVIEW)
533     auto engine = std::static_pointer_cast<ArkJSRuntime>(JsiDeclarativeEngineInstance::GetCurrentRuntime());
534 #else
535     auto engine = EngineHelper::GetCurrentEngineSafely();
536 #endif
537     CHECK_NULL_VOID(engine);
538     NativeEngine* nativeEngine = engine->GetNativeEngine();
539     CHECK_NULL_VOID(nativeEngine);
540     auto env = reinterpret_cast<napi_env>(nativeEngine);
541     ScopeRAII scopeNapi(env);
542     panda::Local<JsiValue> value = info[1].Get().GetLocalHandle();
543     JSValueWrapper valueWrapper = value;
544     napi_value cb = nativeEngine->ValueToNapiValue(valueWrapper);
545     napi_handle_scope napiScope = nullptr;
546     napi_open_handle_scope(env, &napiScope);
547     CHECK_NULL_VOID(napiScope);
548 
549     AddCallbackToList(env, cb, type, std::move(onFunc));
550     napi_close_handle_scope(env, napiScope);
551 }
552 
JsOff(const JSCallbackInfo & info)553 void JSRenderingContext::JsOff(const JSCallbackInfo& info)
554 {
555     if (!info[0]->IsString()) {
556         return;
557     }
558     const CanvasCallbackType type = GetCanvasCallbackType(info[0]->ToString());
559     if (type == CanvasCallbackType::UNKNOWN) {
560         JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter error.");
561         return;
562     }
563     ContainerScope scope(instanceId_);
564 #if !defined(PREVIEW)
565     auto engine = std::static_pointer_cast<ArkJSRuntime>(JsiDeclarativeEngineInstance::GetCurrentRuntime());
566 #else
567     auto engine = EngineHelper::GetCurrentEngineSafely();
568 #endif
569     CHECK_NULL_VOID(engine);
570     NativeEngine* nativeEngine = engine->GetNativeEngine();
571     CHECK_NULL_VOID(nativeEngine);
572     auto env = reinterpret_cast<napi_env>(nativeEngine);
573     ScopeRAII scopeNapi(env);
574     napi_value cb = nullptr;
575     if (info[1]->IsFunction()) {
576         panda::Local<JsiValue> value = info[1].Get().GetLocalHandle();
577         JSValueWrapper valueWrapper = value;
578         cb = nativeEngine->ValueToNapiValue(valueWrapper);
579     }
580 
581     DeleteCallbackFromList(info.Length(), env, cb, type);
582 }
583 } // namespace OHOS::Ace::Framework
584