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 "bridge/declarative_frontend/jsview/canvas/js_offscreen_rendering_context.h"
17 
18 #include "base/utils/utils.h"
19 #include "bridge/common/utils/engine_helper.h"
20 #include "bridge/declarative_frontend/engine/js_converter.h"
21 #include "bridge/declarative_frontend/jsview/models/canvas/offscreen_canvas_rendering_context_2d_model_impl.h"
22 #include "core/components_ng/pattern/canvas/offscreen_canvas_pattern.h"
23 #include "core/components_ng/pattern/canvas/offscreen_canvas_rendering_context_2d_model_ng.h"
24 
25 namespace OHOS::Ace::Framework {
26 std::mutex JSOffscreenRenderingContext::mutex_;
27 std::unordered_map<uint32_t, RefPtr<AceType>> JSOffscreenRenderingContext::offscreenPatternMap_;
28 uint32_t JSOffscreenRenderingContext::offscreenPatternCount_ = 0;
29 
JSOffscreenRenderingContext()30 JSOffscreenRenderingContext::JSOffscreenRenderingContext()
31 {
32     id_ = offscreenPatternCount_;
33 #ifdef NG_BUILD
34     renderingContext2DModel_ = AceType::MakeRefPtr<NG::OffscreenCanvasRenderingContext2DModelNG>();
35 #else
36     if (Container::IsCurrentUseNewPipeline()) {
37         renderingContext2DModel_ = AceType::MakeRefPtr<NG::OffscreenCanvasRenderingContext2DModelNG>();
38     } else {
39         renderingContext2DModel_ = AceType::MakeRefPtr<Framework::OffscreenCanvasRenderingContext2DModelImpl>();
40     }
41 #endif
42 }
43 
JSBind(BindingTarget globalObj)44 void JSOffscreenRenderingContext::JSBind(BindingTarget globalObj)
45 {
46     // Define the class "OffscreenCanvasRenderingContext2D"
47     JSClass<JSOffscreenRenderingContext>::Declare("OffscreenCanvasRenderingContext2D");
48 
49     // Define all properties of the "OffscreenCanvasRenderingContext2D"
50     JSClass<JSOffscreenRenderingContext>::CustomProperty(
51         "filter", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetFilter);
52     JSClass<JSOffscreenRenderingContext>::CustomProperty(
53         "direction", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetDirection);
54     JSClass<JSOffscreenRenderingContext>::CustomProperty(
55         "fillStyle", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetFillStyle);
56     JSClass<JSOffscreenRenderingContext>::CustomProperty(
57         "strokeStyle", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetStrokeStyle);
58     JSClass<JSOffscreenRenderingContext>::CustomProperty(
59         "lineCap", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetLineCap);
60     JSClass<JSOffscreenRenderingContext>::CustomProperty(
61         "lineJoin", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetLineJoin);
62     JSClass<JSOffscreenRenderingContext>::CustomProperty(
63         "miterLimit", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetMiterLimit);
64     JSClass<JSOffscreenRenderingContext>::CustomProperty(
65         "lineWidth", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetLineWidth);
66     JSClass<JSOffscreenRenderingContext>::CustomProperty(
67         "font", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetFont);
68     JSClass<JSOffscreenRenderingContext>::CustomProperty(
69         "textAlign", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetTextAlign);
70     JSClass<JSOffscreenRenderingContext>::CustomProperty(
71         "textBaseline", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetTextBaseline);
72     JSClass<JSOffscreenRenderingContext>::CustomProperty(
73         "globalAlpha", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetGlobalAlpha);
74     JSClass<JSOffscreenRenderingContext>::CustomProperty(
75         "globalCompositeOperation", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetGlobalCompositeOperation);
76     JSClass<JSOffscreenRenderingContext>::CustomProperty(
77         "lineDashOffset", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetLineDashOffset);
78     JSClass<JSOffscreenRenderingContext>::CustomProperty(
79         "shadowBlur", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetShadowBlur);
80     JSClass<JSOffscreenRenderingContext>::CustomProperty(
81         "shadowColor", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetShadowColor);
82     JSClass<JSOffscreenRenderingContext>::CustomProperty(
83         "shadowOffsetX", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetShadowOffsetX);
84     JSClass<JSOffscreenRenderingContext>::CustomProperty(
85         "shadowOffsetY", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetShadowOffsetY);
86     JSClass<JSOffscreenRenderingContext>::CustomProperty(
87         "imageSmoothingEnabled", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetImageSmoothingEnabled);
88     JSClass<JSOffscreenRenderingContext>::CustomProperty(
89         "imageSmoothingQuality", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetImageSmoothingQuality);
90 
91     // Define all methods of the "OffscreenCanvasRenderingContext2D"
92     JSClass<JSOffscreenRenderingContext>::CustomMethod(
93         "transferToImageBitmap", &JSOffscreenRenderingContext::JsTransferToImageBitmap);
94     JSClass<JSOffscreenRenderingContext>::CustomMethod("toDataURL", &JSCanvasRenderer::JsToDataUrl);
95     JSClass<JSOffscreenRenderingContext>::CustomMethod(
96         "createRadialGradient", &JSCanvasRenderer::JsCreateRadialGradient);
97     JSClass<JSOffscreenRenderingContext>::CustomMethod("fillRect", &JSCanvasRenderer::JsFillRect);
98     JSClass<JSOffscreenRenderingContext>::CustomMethod("strokeRect", &JSCanvasRenderer::JsStrokeRect);
99     JSClass<JSOffscreenRenderingContext>::CustomMethod("clearRect", &JSCanvasRenderer::JsClearRect);
100     JSClass<JSOffscreenRenderingContext>::CustomMethod(
101         "createLinearGradient", &JSCanvasRenderer::JsCreateLinearGradient);
102     JSClass<JSOffscreenRenderingContext>::CustomMethod("fillText", &JSCanvasRenderer::JsFillText);
103     JSClass<JSOffscreenRenderingContext>::CustomMethod("strokeText", &JSCanvasRenderer::JsStrokeText);
104     JSClass<JSOffscreenRenderingContext>::CustomMethod("measureText", &JSCanvasRenderer::JsMeasureText);
105     JSClass<JSOffscreenRenderingContext>::CustomMethod("moveTo", &JSCanvasRenderer::JsMoveTo);
106     JSClass<JSOffscreenRenderingContext>::CustomMethod("lineTo", &JSCanvasRenderer::JsLineTo);
107     JSClass<JSOffscreenRenderingContext>::CustomMethod("bezierCurveTo", &JSCanvasRenderer::JsBezierCurveTo);
108     JSClass<JSOffscreenRenderingContext>::CustomMethod("quadraticCurveTo", &JSCanvasRenderer::JsQuadraticCurveTo);
109     JSClass<JSOffscreenRenderingContext>::CustomMethod("arcTo", &JSCanvasRenderer::JsArcTo);
110     JSClass<JSOffscreenRenderingContext>::CustomMethod("arc", &JSCanvasRenderer::JsArc);
111     JSClass<JSOffscreenRenderingContext>::CustomMethod("ellipse", &JSCanvasRenderer::JsEllipse);
112     JSClass<JSOffscreenRenderingContext>::CustomMethod("fill", &JSCanvasRenderer::JsFill);
113     JSClass<JSOffscreenRenderingContext>::CustomMethod("stroke", &JSCanvasRenderer::JsStroke);
114     JSClass<JSOffscreenRenderingContext>::CustomMethod("clip", &JSCanvasRenderer::JsClip);
115     JSClass<JSOffscreenRenderingContext>::CustomMethod("rect", &JSCanvasRenderer::JsRect);
116     JSClass<JSOffscreenRenderingContext>::CustomMethod("beginPath", &JSCanvasRenderer::JsBeginPath);
117     JSClass<JSOffscreenRenderingContext>::CustomMethod("closePath", &JSCanvasRenderer::JsClosePath);
118     JSClass<JSOffscreenRenderingContext>::CustomMethod("restore", &JSCanvasRenderer::JsRestore);
119     JSClass<JSOffscreenRenderingContext>::CustomMethod("save", &JSCanvasRenderer::JsSave);
120     JSClass<JSOffscreenRenderingContext>::CustomMethod("rotate", &JSCanvasRenderer::JsRotate);
121     JSClass<JSOffscreenRenderingContext>::CustomMethod("scale", &JSCanvasRenderer::JsScale);
122     JSClass<JSOffscreenRenderingContext>::CustomMethod("getTransform", &JSCanvasRenderer::JsGetTransform);
123     JSClass<JSOffscreenRenderingContext>::CustomMethod("setTransform", &JSCanvasRenderer::JsSetTransform);
124     JSClass<JSOffscreenRenderingContext>::CustomMethod("resetTransform", &JSCanvasRenderer::JsResetTransform);
125     JSClass<JSOffscreenRenderingContext>::CustomMethod("transform", &JSCanvasRenderer::JsTransform);
126     JSClass<JSOffscreenRenderingContext>::CustomMethod("translate", &JSCanvasRenderer::JsTranslate);
127     JSClass<JSOffscreenRenderingContext>::CustomMethod("setLineDash", &JSCanvasRenderer::JsSetLineDash);
128     JSClass<JSOffscreenRenderingContext>::CustomMethod("getLineDash", &JSCanvasRenderer::JsGetLineDash);
129     JSClass<JSOffscreenRenderingContext>::CustomMethod("drawImage", &JSCanvasRenderer::JsDrawImage);
130     JSClass<JSOffscreenRenderingContext>::CustomMethod("createPattern", &JSCanvasRenderer::JsCreatePattern);
131     JSClass<JSOffscreenRenderingContext>::CustomMethod("createImageData", &JSCanvasRenderer::JsCreateImageData);
132     JSClass<JSOffscreenRenderingContext>::CustomMethod("putImageData", &JSCanvasRenderer::JsPutImageData);
133     JSClass<JSOffscreenRenderingContext>::CustomMethod("getImageData", &JSCanvasRenderer::JsGetImageData);
134     JSClass<JSOffscreenRenderingContext>::CustomMethod("getJsonData", &JSCanvasRenderer::JsGetJsonData);
135     JSClass<JSOffscreenRenderingContext>::CustomMethod("getPixelMap", &JSCanvasRenderer::JsGetPixelMap);
136     JSClass<JSOffscreenRenderingContext>::CustomMethod("setPixelMap", &JSCanvasRenderer::JsSetPixelMap);
137     JSClass<JSOffscreenRenderingContext>::CustomMethod("createConicGradient", &JSCanvasRenderer::JsCreateConicGradient);
138     JSClass<JSOffscreenRenderingContext>::CustomMethod("saveLayer", &JSCanvasRenderer::JsSaveLayer);
139     JSClass<JSOffscreenRenderingContext>::CustomMethod("restoreLayer", &JSCanvasRenderer::JsRestoreLayer);
140     JSClass<JSOffscreenRenderingContext>::CustomMethod("reset", &JSCanvasRenderer::JsReset);
141 
142     // Register the "OffscreenCanvasRenderingContext2D" to the golbal object of the vm
143     JSClass<JSOffscreenRenderingContext>::Bind(
144         globalObj, JSOffscreenRenderingContext::Constructor, JSOffscreenRenderingContext::Destructor);
145 }
146 
147 // OffscreenCanvasRenderingContext2D(width: number, height: number, settings?: RenderingContextSettings,
148 //                                   unit?: LengthMetricsUnit)
Constructor(const JSCallbackInfo & args)149 void JSOffscreenRenderingContext::Constructor(const JSCallbackInfo& args)
150 {
151     auto jsRenderContext = Referenced::MakeRefPtr<JSOffscreenRenderingContext>();
152     jsRenderContext->IncRefCount();
153     args.SetReturnValue(Referenced::RawPtr(jsRenderContext));
154 
155     double width = 0.0;
156     double height = 0.0;
157     double density = jsRenderContext->GetDensity();
158     if (args.GetDoubleArg(0, width) && args.GetDoubleArg(1, height)) {
159         width *= density;
160         height *= density;
161         jsRenderContext->SetWidth(width);
162         jsRenderContext->SetHeight(height);
163         auto renderingContext =
164             AceType::DynamicCast<OffscreenCanvasRenderingContext2DModel>(jsRenderContext->renderingContext2DModel_);
165         auto offscreenPattern =
166             renderingContext->CreateOffscreenPattern(round(width), round(height));
167         CHECK_NULL_VOID(offscreenPattern);
168         size_t bitmapSize = renderingContext->GetBitmapSize(offscreenPattern);
169         args.SetSize(bitmapSize);
170         jsRenderContext->SetOffscreenPattern(offscreenPattern);
171         std::lock_guard<std::mutex> lock(mutex_);
172         offscreenPatternMap_[offscreenPatternCount_++] = offscreenPattern;
173     }
174     auto* jsContextSetting = args.UnwrapArg<JSRenderingContextSettings>(2);
175     if (jsContextSetting) {
176         bool anti = jsContextSetting->GetAntialias();
177         jsRenderContext->SetAnti(anti);
178         jsRenderContext->SetAntiAlias();
179 
180         int32_t unit = 0;
181         if (args.GetInt32Arg(3, unit) && (static_cast<CanvasUnit>(unit) == CanvasUnit::PX)) { // 3: index of parameter
182             jsRenderContext->SetUnit(CanvasUnit::PX);
183         }
184     }
185     jsRenderContext->SetDensity();
186 }
187 
Destructor(JSOffscreenRenderingContext * context)188 void JSOffscreenRenderingContext::Destructor(JSOffscreenRenderingContext* context)
189 {
190     CHECK_NULL_VOID(context);
191     uint32_t contextId = context->GetId();
192     context->DecRefCount();
193     std::lock_guard<std::mutex> lock(mutex_);
194     offscreenPatternMap_.erase(contextId);
195 }
196 
JsTransferToImageBitmap(const JSCallbackInfo & info)197 void JSOffscreenRenderingContext::JsTransferToImageBitmap(const JSCallbackInfo& info)
198 {
199     ContainerScope scope(instanceId_);
200 #if !defined(PREVIEW)
201     auto engine = std::static_pointer_cast<ArkJSRuntime>(JsiDeclarativeEngineInstance::GetCurrentRuntime());
202 #else
203     auto engine = EngineHelper::GetCurrentEngineSafely();
204 #endif
205     CHECK_NULL_VOID(engine);
206     NativeEngine* nativeEngine = engine->GetNativeEngine();
207     CHECK_NULL_VOID(nativeEngine);
208     napi_env env = reinterpret_cast<napi_env>(nativeEngine);
209     napi_value renderImage = nullptr;
210     napi_create_object(env, &renderImage);
211     auto offscreenCanvasPattern = AceType::DynamicCast<NG::OffscreenCanvasPattern>(GetOffscreenPattern(id_));
212     CHECK_NULL_VOID(offscreenCanvasPattern);
213     auto pixelMap = offscreenCanvasPattern->TransferToImageBitmap();
214     if (!JSRenderImage::CreateJSRenderImage(env, pixelMap, renderImage)) {
215         return;
216     }
217     void* nativeObj = nullptr;
218     napi_status status = napi_unwrap(env, renderImage, &nativeObj);
219     if (status != napi_ok) {
220         return;
221     }
222     auto jsImage = (JSRenderImage*)nativeObj;
223     CHECK_NULL_VOID(jsImage);
224 #ifndef PIXEL_MAP_SUPPORTED
225     auto imageData = offscreenCanvasPattern->GetImageData(0, 0, width_, height_);
226     CHECK_NULL_VOID(imageData);
227     jsImage->SetImageData(std::make_shared<Ace::ImageData>(*imageData));
228 #endif
229     jsImage->SetUnit(GetUnit());
230     jsImage->SetWidth(GetWidth());
231     jsImage->SetHeight(GetHeight());
232     info.SetReturnValue(JsConverter::ConvertNapiValueToJsVal(renderImage));
233 }
234 } // namespace OHOS::Ace::Framework
235