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