1 /*
2 * Copyright (c) 2021-2022 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 "frameworks/bridge/js_frontend/engine/quickjs/canvas_bridge.h"
17
18 #include "base/utils/linear_map.h"
19 #include "core/components/custom_paint/offscreen_canvas.h"
20 #include "frameworks/bridge/common/utils/utils.h"
21 #include "frameworks/bridge/js_frontend/engine/quickjs/offscreen_canvas_bridge.h"
22 #include "frameworks/bridge/js_frontend/engine/quickjs/qjs_engine.h"
23
24 namespace OHOS::Ace::Framework {
25
26 namespace {
27
28 constexpr char IMAGE_SRC[] = "src";
29 constexpr char IMAGE_WIDTH[] = "width";
30 constexpr char IMAGE_HEIGHT[] = "height";
31
32 #if !defined(PREVIEW)
33 constexpr char CANVAS_TYPE_WEBGL[] = "webgl";
34 constexpr char CANVAS_TYPE_WEBGL2[] = "webgl2";
35 constexpr char CANVAS_WEBGL_SO[] = "webglnapi";
36 #endif
37
38 template<typename T>
ConvertStrToEnum(const char * key,const LinearMapNode<T> * map,size_t length,T defaultValue)39 inline T ConvertStrToEnum(const char* key, const LinearMapNode<T>* map, size_t length, T defaultValue)
40 {
41 int64_t index = BinarySearchFindIndex(map, length, key);
42 return index != -1 ? map[index].value : defaultValue;
43 }
44
45 const LinearMapNode<TextBaseline> BASELINE_TABLE[] = {
46 { "alphabetic", TextBaseline::ALPHABETIC },
47 { "bottom", TextBaseline::BOTTOM },
48 { "hanging", TextBaseline::HANGING },
49 { "ideographic", TextBaseline::IDEOGRAPHIC },
50 { "middle", TextBaseline::MIDDLE },
51 { "top", TextBaseline::TOP },
52 };
53
54 const std::set<std::string> FONT_WEIGHTS = { "normal", "bold", "lighter", "bolder",
55 "100", "200", "300", "400", "500", "600", "700", "800", "900" };
56 const std::set<std::string> FONT_STYLES = { "italic", "oblique", "normal" };
57 const std::set<std::string> FONT_FAMILIES = { "sans-serif", "serif", "monospace" };
58 const std::set<std::string> QUALITY_TYPE = { "low", "medium", "high" }; // Default value is low.
59
GetJsDoubleVal(JSContext * ctx,JSValueConst value)60 inline double GetJsDoubleVal(JSContext* ctx, JSValueConst value)
61 {
62 // use ScopedString to parse double
63 ScopedString scopedVal(ctx, value);
64 auto jsonVal = JsonUtil::ParseJsonData(scopedVal.get());
65 if (jsonVal && jsonVal->IsNumber()) {
66 return jsonVal->GetDouble();
67 }
68 return 0.0;
69 }
70
GetJsDashValue(JSContext * ctx,JSValueConst value)71 inline std::vector<double> GetJsDashValue(JSContext* ctx, JSValueConst value)
72 {
73 ScopedString scopedString(ctx, value);
74 std::vector<std::string> props;
75 StringUtils::StringSplitter(scopedString.get(), ',', props);
76 std::vector<double> segments;
77
78 for (const auto& prop : props) {
79 auto val = StringToDouble(prop);
80 // if there only exists 0 in props, it means that there is no dash style
81 if (NearZero(val) && props.size() == 1) {
82 return segments;
83 }
84 segments.emplace_back(val);
85 }
86 // if segment size is odd, copy one more to even
87 if (segments.size() % 2 != 0) {
88 segments.insert(segments.end(), segments.begin(), segments.end());
89 }
90 return segments;
91 }
92
GetJsRectParam(JSContext * ctx,int32_t argc,JSValueConst * argv)93 inline Rect GetJsRectParam(JSContext* ctx, int32_t argc, JSValueConst* argv)
94 {
95 // 4 parameters: rect(x, y, width, height)
96 if ((!argv) || (argc != 4)) {
97 LOGE("argc error, argc = %{private}d", argc);
98 return Rect();
99 }
100 double x = GetJsDoubleVal(ctx, argv[0]);
101 double y = GetJsDoubleVal(ctx, argv[1]);
102 double width = GetJsDoubleVal(ctx, argv[2]);
103 double height = GetJsDoubleVal(ctx, argv[3]);
104 Rect rect = Rect(x, y, width, height);
105 return rect;
106 }
107
GetCurrentNodeId(JSContext * ctx,JSValueConst value)108 inline NodeId GetCurrentNodeId(JSContext* ctx, JSValueConst value)
109 {
110 NodeId id = 0;
111 JSValue nodeId = JS_GetPropertyStr(ctx, value, "__nodeId");
112 if (JS_IsInteger(nodeId) && (JS_ToInt32(ctx, &id, nodeId)) < 0) {
113 id = 0;
114 }
115 JS_FreeValue(ctx, nodeId);
116 return id;
117 }
118
PushTaskToPage(JSContext * ctx,JSValueConst value,const std::function<void (const RefPtr<CanvasTaskPool> &)> & task)119 void PushTaskToPage(JSContext* ctx, JSValueConst value, const std::function<void(const RefPtr<CanvasTaskPool>&)>& task)
120 {
121 // get node id
122 NodeId id = GetCurrentNodeId(ctx, value);
123 auto command = Referenced::MakeRefPtr<JsCommandContextOperation>(id, task);
124 // push command
125 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
126 ACE_DCHECK(instance);
127 auto page = instance->GetRunningPage();
128 if (!page) {
129 return;
130 }
131 page->PushCommand(command);
132 }
133
134 #if !defined(PREVIEW)
PushTaskToPageById(JSContext * ctx,NodeId id,const std::function<void (const RefPtr<CanvasTaskPool> &)> & task)135 void PushTaskToPageById(JSContext* ctx, NodeId id, const std::function<void(const RefPtr<CanvasTaskPool>&)>& task)
136 {
137 auto command = Referenced::MakeRefPtr<JsCommandContextOperation>(id, task);
138 // push command
139 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
140 ACE_DCHECK(instance);
141 auto page = instance->GetRunningPage();
142 if (!page) {
143 return;
144 }
145 page->PushCommand(command);
146 }
147 #endif
148
JsParseTextState(JSContext * ctx,JSValue value)149 inline PaintState JsParseTextState(JSContext* ctx, JSValue value)
150 {
151 PaintState state;
152 JSValue fontStyle = JS_GetPropertyStr(ctx, value, "font");
153 JSValue textAlign = JS_GetPropertyStr(ctx, value, "textAlign");
154 JSValue textBaseline = JS_GetPropertyStr(ctx, value, "textBaseline");
155
156 // parse font styles
157 ScopedString alignStr(ctx, textAlign);
158 ScopedString baselineStr(ctx, textBaseline);
159 state.SetTextAlign(ConvertStrToTextAlign(alignStr.get()));
160 TextStyle style;
161 style.SetTextBaseline(
162 ConvertStrToEnum(baselineStr.get(), BASELINE_TABLE, ArraySize(BASELINE_TABLE), TextBaseline::ALPHABETIC));
163 ScopedString font(ctx, fontStyle);
164 std::vector<std::string> fontProps;
165 StringUtils::StringSplitter(font.get(), ' ', fontProps);
166 for (const auto& fontProp : fontProps) {
167 if (FONT_WEIGHTS.find(fontProp) != FONT_WEIGHTS.end()) {
168 style.SetFontWeight(ConvertStrToFontWeight(fontProp));
169 } else if (FONT_STYLES.find(fontProp) != FONT_STYLES.end()) {
170 style.SetFontStyle(ConvertStrToFontStyle(fontProp));
171 } else if (FONT_FAMILIES.find(fontProp) != FONT_FAMILIES.end()) {
172 style.SetFontFamilies(ConvertStrToFontFamilies(fontProp));
173 } else if (fontProp.find("px") != std::string::npos) {
174 std::string fontSize = fontProp.substr(0, fontProp.size() - 2);
175 style.SetFontSize(Dimension(StringToDouble(fontProp)));
176 } else {
177 LOGW("parse text error");
178 }
179 }
180 state.SetTextStyle(style);
181 JS_FreeValue(ctx, fontStyle);
182 JS_FreeValue(ctx, textAlign);
183 JS_FreeValue(ctx, textBaseline);
184
185 return state;
186 }
187
188 } // namespace
189
190 int32_t CanvasBridge::gradientCount_ = 0;
191 int32_t CanvasBridge::patternCount_ = 0;
192 int32_t CanvasBridge::path2dCount_ = 0;
193 std::unordered_map<int32_t, Pattern> CanvasBridge::pattern_;
194 std::unordered_map<int32_t, Gradient> CanvasBridge::gradientColors_;
195 std::unordered_map<int32_t, RefPtr<CanvasPath2D>> CanvasBridge::path2Ds_;
196
197 const JSCFunctionListEntry JS_ANIMATION_FUNCS[] = {
198 JS_CGETSET_DEF("fillStyle", CanvasBridge::JsFillStyleGetter, CanvasBridge::JsFillStyleSetter),
199 JS_CGETSET_DEF("strokeStyle", CanvasBridge::JsStrokeStyleGetter, CanvasBridge::JsStrokeStyleSetter),
200 JS_CGETSET_DEF("lineCap", CanvasBridge::JsLineCapGetter, CanvasBridge::JsLineCapSetter),
201 JS_CGETSET_DEF("lineJoin", CanvasBridge::JsLineJoinGetter, CanvasBridge::JsLineJoinSetter),
202 JS_CGETSET_DEF("miterLimit", CanvasBridge::JsMiterLimitGetter, CanvasBridge::JsMiterLimitSetter),
203 JS_CGETSET_DEF("lineWidth", CanvasBridge::JsLineWidthGetter, CanvasBridge::JsLineWidthSetter),
204 JS_CGETSET_DEF("textAlign", CanvasBridge::JsTextAlignGetter, CanvasBridge::JsTextAlignSetter),
205 JS_CGETSET_DEF("textBaseline", CanvasBridge::JsTextBaselineGetter, CanvasBridge::JsTextBaselineSetter),
206 JS_CGETSET_DEF("font", CanvasBridge::JsFontGetter, CanvasBridge::JsFontSetter),
207 JS_CGETSET_DEF("globalAlpha", CanvasBridge::JsAlphaGetter, CanvasBridge::JsAlphaSetter),
208 JS_CGETSET_DEF("globalCompositeOperation",
209 CanvasBridge::JsCompositeOperationGetter, CanvasBridge::JsCompositeOperationSetter),
210 JS_CGETSET_DEF("lineDashOffset", CanvasBridge::JsLineDashOffsetGetter, CanvasBridge::JsLineDashOffsetSetter),
211 JS_CGETSET_DEF("shadowBlur", CanvasBridge::JsShadowBlurGetter, CanvasBridge::JsShadowBlurSetter),
212 JS_CGETSET_DEF("shadowColor", CanvasBridge::JsShadowColorGetter, CanvasBridge::JsShadowColorSetter),
213 JS_CGETSET_DEF("shadowOffsetX", CanvasBridge::JsShadowOffsetXGetter, CanvasBridge::JsShadowOffsetXSetter),
214 JS_CGETSET_DEF("shadowOffsetY", CanvasBridge::JsShadowOffsetYGetter, CanvasBridge::JsShadowOffsetYSetter),
215 JS_CGETSET_DEF("imageSmoothingEnabled",
216 CanvasBridge::JsSmoothingEnabledGetter, CanvasBridge::JsSmoothingEnabledSetter),
217 JS_CGETSET_DEF("width", CanvasBridge::JsWidthGetter, nullptr),
218 JS_CGETSET_DEF("height", CanvasBridge::JsHeightGetter, nullptr),
219 };
220
~CanvasBridge()221 CanvasBridge::~CanvasBridge()
222 {
223 #if !defined(PREVIEW)
224 if (webglRenderContext_) {
225 delete webglRenderContext_;
226 webglRenderContext_ = nullptr;
227 }
228 if (webgl2RenderContext_) {
229 delete webgl2RenderContext_;
230 webgl2RenderContext_ = nullptr;
231 }
232 #endif
233 }
234
HandleJsContext(JSContext * ctx,NodeId id,const std::string & args,JsEngineInstance * engine)235 void CanvasBridge::HandleJsContext(JSContext* ctx, NodeId id, const std::string& args, JsEngineInstance* engine)
236 {
237 LOGD("CanvasBridge::HandleJsContext");
238 #if !defined(PREVIEW)
239 std::unique_ptr<JsonValue> argsValue = JsonUtil::ParseJsonString(args);
240 if (argsValue && argsValue->IsArray() && argsValue->GetArraySize() > 0) {
241 auto typeArg = argsValue->GetArrayItem(0);
242 if (typeArg && typeArg->IsString()) {
243 std::string type = typeArg->GetString();
244 if (type == std::string(CANVAS_TYPE_WEBGL)) {
245 JSHandleWebglContext(ctx, id, args, engine, webglRenderContext_);
246 return;
247 } else if (type == std::string(CANVAS_TYPE_WEBGL2)) {
248 JSHandleWebglContext(ctx, id, args, engine, webgl2RenderContext_);
249 return;
250 }
251 }
252 }
253 #endif
254
255 renderContext_ = JS_NewObject(ctx);
256 const std::vector<std::pair<const char*, JSValue>> contextTable = {
257 { "__nodeId", JS_NewInt32(ctx, id) },
258 { "createLinearGradient", JS_NewCFunction(ctx, JsCreateLinearGradient, "createLinearGradient", 4) },
259 { "createRadialGradient", JS_NewCFunction(ctx, JsCreateRadialGradient, "createRadialGradient", 6) },
260 { "fillRect", JS_NewCFunction(ctx, JsFillRect, "fillRect", 4) },
261 { "strokeRect", JS_NewCFunction(ctx, JsStrokeRect, "strokeRect", 4) },
262 { "clearRect", JS_NewCFunction(ctx, JsClearRect, "clearRect", 4) },
263 { "fillText", JS_NewCFunction(ctx, JsFillText, "fillText", 3) },
264 { "strokeText", JS_NewCFunction(ctx, JsStrokeText, "strokeText", 3) },
265 { "measureText", JS_NewCFunction(ctx, JsMeasureText, "measureText", 1) },
266 { "moveTo", JS_NewCFunction(ctx, JsMoveTo, "moveTo", 2) },
267 { "lineTo", JS_NewCFunction(ctx, JsLineTo, "lineTo", 2) },
268 { "bezierCurveTo", JS_NewCFunction(ctx, JsBezierCurveTo, "bezierCurveTo", 6) },
269 { "quadraticCurveTo", JS_NewCFunction(ctx, JsQuadraticCurveTo, "quadraticCurveTo", 4) },
270 { "arcTo", JS_NewCFunction(ctx, JsArcTo, "arcTo", 5) },
271 { "arc", JS_NewCFunction(ctx, JsArc, "arc", 6) },
272 { "ellipse", JS_NewCFunction(ctx, JsEllipse, "ellipse", 4) },
273 { "fill", JS_NewCFunction(ctx, JsFill, "fill", 0) },
274 { "stroke", JS_NewCFunction(ctx, JsStroke, "stroke", 0) },
275 { "clip", JS_NewCFunction(ctx, JsClip, "clip", 0) },
276 { "rect", JS_NewCFunction(ctx, JsRect, "rect", 4) },
277 { "beginPath", JS_NewCFunction(ctx, JsBeginPath, "beginPath", 0) },
278 { "closePath", JS_NewCFunction(ctx, JsClosePath, "closePath", 0) },
279 { "restore", JS_NewCFunction(ctx, JsRestore, "restore", 0) },
280 { "save", JS_NewCFunction(ctx, JsSave, "save", 0) },
281 { "rotate", JS_NewCFunction(ctx, JsRotate, "rotate", 1) },
282 { "scale", JS_NewCFunction(ctx, JsScale, "scale", 2) },
283 { "setTransform", JS_NewCFunction(ctx, JsSetTransform, "setTransform", 6) },
284 { "transform", JS_NewCFunction(ctx, JsTransform, "transform", 6) },
285 { "translate", JS_NewCFunction(ctx, JsTranslate, "translate", 2) },
286 { "getLineDash", JS_NewCFunction(ctx, JsGetLineDash, "getLineDash", 0) },
287 { "setLineDash", JS_NewCFunction(ctx, JsSetLineDash, "setLineDash", 1) },
288 { "drawImage", JS_NewCFunction(ctx, JsDrawImage, "drawImage", 9) },
289 { "createPath2D", JS_NewCFunction(ctx, JsCreatePath2D, "createPath2D", 0) },
290 { "createPattern", JS_NewCFunction(ctx, JsCreatePattern, "createPattern", 2) },
291 { "createImageData", JS_NewCFunction(ctx, JsCreateImageData, "createImageData", 2) },
292 { "putImageData", JS_NewCFunction(ctx, JsPutImageData, "putImageData", 7) },
293 { "getImageData", JS_NewCFunction(ctx, JsGetImageData, "getImageData", 4) },
294 { "getJsonData", JS_NewCFunction(ctx, JsGetJsonData, "getJsonData", 1)},
295 { "transferFromImageBitmap", JS_NewCFunction(ctx, JsTransferFromImageBitmap, "transferFromImageBitmap", 1)},
296 };
297
298 for (const auto& iter : contextTable) {
299 JS_SetPropertyStr(ctx, renderContext_, iter.first, iter.second);
300 }
301 // getter and setter
302 JS_SetPropertyFunctionList(ctx, renderContext_, JS_ANIMATION_FUNCS, countof(JS_ANIMATION_FUNCS));
303 JsSetAntiAlias(ctx, id, args);
304 }
305
JSHandleWebglContext(JSContext * ctx,NodeId id,const std::string & args,JsEngineInstance * engine,CanvasRenderContextBase * & canvasRenderContext)306 void CanvasBridge::JSHandleWebglContext(
307 JSContext* ctx, NodeId id, const std::string& args, JsEngineInstance* engine,
308 CanvasRenderContextBase*& canvasRenderContext)
309 {
310 LOGD("CanvasBridge::HandleWebglContext");
311 if (engine == nullptr) {
312 LOGE("engine is null");
313 return;
314 }
315 #if !defined(PREVIEW)
316 QuickJSNativeEngine* nativeEngine = static_cast<QjsEngineInstance*>(engine)->GetQuickJSNativeEngine();
317 if (nativeEngine == nullptr) {
318 LOGE("nativeEngine is null");
319 return;
320 }
321 std::string moduleName(CANVAS_WEBGL_SO);
322 std::string pluginId(std::to_string(id));
323 renderContext_ = nativeEngine->GetModuleFromName(
324 moduleName, false, pluginId, args, WEBGL_RENDER_CONTEXT_NAME, reinterpret_cast<void**>(&canvasRenderContext));
325 if (!canvasRenderContext) {
326 LOGE("CanvasBridge invalid canvasRenderContext");
327 return;
328 }
329
330 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
331 if (!instance) {
332 return;
333 }
334 auto page = instance->GetRunningPage();
335 if (!page) {
336 LOGE("page is null.");
337 return;
338 }
339 auto task = [canvasRenderContext, page, id]() {
340 auto canvas = AceType::DynamicCast<DOMCanvas>(page->GetDomDocument()->GetDOMNodeById(id));
341 if (!canvas) {
342 LOGE("DOMCanvas is null.");
343 return;
344 }
345 auto paintChild = AceType::DynamicCast<CustomPaintComponent>(canvas->GetSpecializedComponent());
346 auto pool = paintChild->GetTaskPool();
347 if (!pool) {
348 LOGE("TaskPool is null.");
349 return;
350 }
351 pool->WebGLInit(canvasRenderContext);
352 };
353 instance->GetDelegate()->PostSyncTaskToPage(task);
354
355 canvasRenderContext->Init();
356
357 auto onWebGLUpdateCallback = [ctx, id]() {
358 auto task = [](const RefPtr<CanvasTaskPool>& pool) {
359 pool->WebGLUpdate();
360 };
361 PushTaskToPageById(ctx, id, task);
362 };
363 canvasRenderContext->SetUpdateCallback(onWebGLUpdateCallback);
364 #endif
365 }
366
HandleToDataURL(JSContext * ctx,NodeId id,const std::string & args)367 void CanvasBridge::HandleToDataURL(JSContext* ctx, NodeId id, const std::string& args)
368 {
369 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
370 auto page = instance->GetRunningPage();
371 if (!page) {
372 LOGE("ToDataURL failed, page is null.");
373 return;
374 }
375 std::string dataUrl;
376 auto task = [id, page, args, &dataUrl]() {
377 auto canvas = AceType::DynamicCast<DOMCanvas>(page->GetDomDocument()->GetDOMNodeById(id));
378 if (!canvas) {
379 LOGE("ToDataURL failed, DOMCanvas is null.");
380 return;
381 }
382 auto paintChild = AceType::DynamicCast<CustomPaintComponent>(canvas->GetSpecializedComponent());
383 auto pool = paintChild->GetTaskPool();
384 if (!pool) {
385 LOGE("ToDataURL failed, TaskPool is null.");
386 return;
387 }
388 dataUrl = pool->ToDataURL(args);
389 };
390 instance->GetDelegate()->PostSyncTaskToPage(task);
391 dataURL_ = JS_NewString(ctx, dataUrl.c_str());
392 }
393
JsSetAntiAlias(JSContext * ctx,NodeId id,const std::string & args)394 void CanvasBridge::JsSetAntiAlias(JSContext* ctx, NodeId id, const std::string& args)
395 {
396 bool isEnabled = args.find("\"antialias\":true") != std::string::npos;
397 auto task = [isEnabled](const RefPtr<CanvasTaskPool>& pool) { pool->SetAntiAlias(isEnabled); };
398 auto command = Referenced::MakeRefPtr<JsCommandContextOperation>(id, task);
399 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
400 if (instance == nullptr) {
401 return;
402 }
403 auto page = instance->GetRunningPage();
404 if (!page) {
405 return;
406 }
407 page->PushCommand(command);
408 if (page->CheckPageCreated()) {
409 instance->GetDelegate()->TriggerPageUpdate(page->GetPageId());
410 }
411 }
412
JsCreateLinearGradient(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)413 JSValue CanvasBridge::JsCreateLinearGradient(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
414 {
415 LOGD("CanvasBridge::JsCreateLinearGradient");
416 // 4 parameters: createLinearGradient(x0, y0, x1, y1)
417 if ((!argv) || (argc != 4)) {
418 LOGE("argc error, argc = %{private}d", argc);
419 return JS_NULL;
420 }
421 JSValue gradient = JS_NewObject(ctx);
422 JS_SetPropertyStr(ctx, gradient, "__type", JS_NewString(ctx, "gradient"));
423 JS_SetPropertyStr(ctx, gradient, "__id", JS_NewInt32(ctx, gradientCount_)); // global linear gradients
424 JS_SetPropertyStr(ctx, gradient, "addColorStop", JS_NewCFunction(ctx, JsAddColorStop, "addColorStop", 2));
425 Offset beginOffset = Offset(GetJsDoubleVal(ctx, argv[0]), GetJsDoubleVal(ctx, argv[1]));
426 Offset endOffset = Offset(GetJsDoubleVal(ctx, argv[2]), GetJsDoubleVal(ctx, argv[3]));
427 gradientColors_[gradientCount_].SetType(GradientType::LINEAR);
428 gradientColors_[gradientCount_].SetBeginOffset(beginOffset);
429 gradientColors_[gradientCount_].SetEndOffset(endOffset);
430 ++gradientCount_;
431 return gradient;
432 }
433
JsCreateRadialGradient(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)434 JSValue CanvasBridge::JsCreateRadialGradient(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
435 {
436 LOGD("CanvasBridge::JsCreateRadialGradient");
437 // 6 parameters: createRadialGradient(x0, y0, r0, x1, y1, r1)
438 if ((!argv) || (argc != 6)) {
439 LOGE("argc error, argc = %{private}d", argc);
440 return JS_NULL;
441 }
442 JSValue gradient = JS_NewObject(ctx);
443 JS_SetPropertyStr(ctx, gradient, "__type", JS_NewString(ctx, "gradient"));
444 JS_SetPropertyStr(ctx, gradient, "__id", JS_NewInt32(ctx, gradientCount_)); // global linear gradients
445 JS_SetPropertyStr(ctx, gradient, "addColorStop", JS_NewCFunction(ctx, JsAddColorStop, "addColorStop", 2));
446 Offset innerCenter = Offset(GetJsDoubleVal(ctx, argv[0]), GetJsDoubleVal(ctx, argv[1]));
447 Offset outerCenter = Offset(GetJsDoubleVal(ctx, argv[3]), GetJsDoubleVal(ctx, argv[4]));
448 double innerRadius = GetJsDoubleVal(ctx, argv[2]);
449 double outerRadius = GetJsDoubleVal(ctx, argv[5]);
450 gradientColors_[gradientCount_].SetType(GradientType::RADIAL);
451 gradientColors_[gradientCount_].SetBeginOffset(innerCenter);
452 gradientColors_[gradientCount_].SetEndOffset(outerCenter);
453 gradientColors_[gradientCount_].SetInnerRadius(innerRadius);
454 gradientColors_[gradientCount_].SetOuterRadius(outerRadius);
455 ++gradientCount_;
456 return gradient;
457 }
458
JsAddColorStop(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)459 JSValue CanvasBridge::JsAddColorStop(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
460 {
461 // 2 parameters: addColorStop(offset, color)
462 if ((!argv) || (argc != 2)) {
463 LOGE("argc error, argc = %{private}d", argc);
464 return JS_NULL;
465 }
466
467 GradientColor color;
468 auto jsColor = JS_ToCString(ctx, argv[1]);
469 color.SetColor(Color::FromString(jsColor));
470 color.SetDimension(GetJsDoubleVal(ctx, argv[0]));
471 int32_t id = 0;
472 JSValue nodeId = JS_GetPropertyStr(ctx, value, "__id");
473 if (JS_IsInteger(nodeId) && JS_ToInt32(ctx, &id, nodeId) >= 0) {
474 gradientColors_[id].AddColor(color);
475 }
476 JS_FreeValue(ctx, nodeId);
477 JS_FreeCString(ctx, jsColor);
478 return JS_NULL;
479 }
480
GetGradient(JSContext * ctx,JSValueConst value)481 Gradient CanvasBridge::GetGradient(JSContext* ctx, JSValueConst value)
482 {
483 int32_t id = 0;
484 JSValue nodeId = JS_GetPropertyStr(ctx, value, "__id");
485 if (JS_IsInteger(nodeId) && (JS_ToInt32(ctx, &id, nodeId)) < 0) {
486 JS_FreeValue(ctx, nodeId);
487 return Gradient();
488 }
489 JS_FreeValue(ctx, nodeId);
490 return gradientColors_[id];
491 }
492
JsTransferFromImageBitmap(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)493 JSValue CanvasBridge::JsTransferFromImageBitmap(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
494 {
495 if ((!argv) || (argc != 1)) {
496 LOGE("argc error, argc = %{private}d", argc);
497 return JS_NULL;
498 }
499
500 int32_t bridgeId = GetCurrentBridgeId(ctx, argv[0]);
501
502 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
503 if (!instance) {
504 return JS_NULL;
505 }
506 auto page = instance->GetRunningPage();
507 if (page) {
508 RefPtr<OffscreenCanvasBridge> bridge = AceType::DynamicCast<OffscreenCanvasBridge>(
509 page->GetOffscreenCanvasBridgeById(bridgeId));
510 if (bridge) {
511 RefPtr<OffscreenCanvas> offscreenCanvas = bridge->offscreenCanvas_;
512 auto task = [offscreenCanvas](const RefPtr<CanvasTaskPool>& pool) {
513 pool->TransferFromImageBitmap(offscreenCanvas);
514 };
515 PushTaskToPage(ctx, value, std::move(task));
516 }
517 }
518 return JS_NULL;
519 }
520
JsHandleRect(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv,FunctionCode functionCode)521 JSValue CanvasBridge::JsHandleRect(
522 JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv, FunctionCode functionCode)
523 {
524 LOGD("CanvasBridge::JsHandleRect function code: %{public}d", functionCode);
525 // 4 parameters: rect(x, y, width, height)
526 if ((!argv) || (argc != 4)) {
527 LOGE("argc error, argc = %{private}d", argc);
528 return JS_NULL;
529 }
530 Rect rect = GetJsRectParam(ctx, argc, argv);
531 auto task = [rect, functionCode](const RefPtr<CanvasTaskPool>& pool) {
532 if (functionCode == FunctionCode::FILL_RECT) {
533 pool->FillRect(rect);
534 } else if (functionCode == FunctionCode::STROKE_RECT) {
535 pool->StrokeRect(rect);
536 } else if (functionCode == FunctionCode::CLEAR_RECT) {
537 pool->ClearRect(rect);
538 } else {
539 LOGE("not supported");
540 }
541 };
542 PushTaskToPage(ctx, value, std::move(task));
543 return JS_NULL;
544 }
545
JsFillRect(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)546 JSValue CanvasBridge::JsFillRect(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
547 {
548 return JsHandleRect(ctx, value, argc, argv, FunctionCode::FILL_RECT);
549 }
550
JsStrokeRect(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)551 JSValue CanvasBridge::JsStrokeRect(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
552 {
553 return JsHandleRect(ctx, value, argc, argv, FunctionCode::STROKE_RECT);
554 }
555
JsClearRect(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)556 JSValue CanvasBridge::JsClearRect(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
557 {
558 return JsHandleRect(ctx, value, argc, argv, FunctionCode::CLEAR_RECT);
559 }
560
JsHandleText(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv,FunctionCode functionCode)561 JSValue CanvasBridge::JsHandleText(
562 JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv, FunctionCode functionCode)
563 {
564 LOGD("CanvasBridge::JsHandleText function code: %{public}d", functionCode);
565 // 3 parameters: fillText(text, x, y, [maxWidth])
566 if ((!argv) || (argc != 3)) {
567 LOGE("argc error, argc = %{private}d", argc);
568 return JS_NULL;
569 }
570 ScopedString arg(ctx, argv[0]);
571 if (!arg.get()) {
572 return JS_NULL;
573 }
574 std::string text = arg.get();
575 double x = GetJsDoubleVal(ctx, argv[1]);
576 double y = GetJsDoubleVal(ctx, argv[2]);
577 auto task = [text, x, y, functionCode](const RefPtr<CanvasTaskPool>& pool) {
578 if (functionCode == FunctionCode::FILL_TEXT) {
579 pool->FillText(text, Offset(x, y));
580 } else if (functionCode == FunctionCode::STROKE_TEXT) {
581 pool->StrokeText(text, Offset(x, y));
582 } else {
583 LOGE("not supported");
584 }
585 };
586 PushTaskToPage(ctx, value, task);
587 return JS_NULL;
588 }
589
JsFillText(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)590 JSValue CanvasBridge::JsFillText(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
591 {
592 return JsHandleText(ctx, value, argc, argv, FunctionCode::FILL_TEXT);
593 }
594
JsStrokeText(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)595 JSValue CanvasBridge::JsStrokeText(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
596 {
597 return JsHandleText(ctx, value, argc, argv, FunctionCode::STROKE_TEXT);
598 }
599
JsMeasureText(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)600 JSValue CanvasBridge::JsMeasureText(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
601 {
602 // this func should return TextMetrics, including the width of the text
603 LOGD("Js Measure Text");
604 // 1 parameter: measureText(text)
605 if ((!argv) || (argc != 1)) {
606 LOGE("argc error, argc = %{private}d", argc);
607 return JS_NULL;
608 }
609 ScopedString arg(ctx, argv[0]);
610 if (!arg.get()) {
611 LOGW("no value for text");
612 return JS_NULL;
613 }
614 std::string text = arg.get();
615 auto textState = JsParseTextState(ctx, value);
616 NodeId id = GetCurrentNodeId(ctx, value);
617 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
618 auto page = instance->GetRunningPage();
619 if (!page) {
620 return JS_NULL;
621 }
622 double width = 0.0;
623 double height = 0.0;
624 auto task = [&text, &textState, id, page, &width, &height]() {
625 auto canvas = AceType::DynamicCast<DOMCanvas>(page->GetDomDocument()->GetDOMNodeById(id));
626 if (!canvas) {
627 return;
628 }
629 auto paintChild = AceType::DynamicCast<CustomPaintComponent>(canvas->GetSpecializedComponent());
630 auto canvasTask = paintChild->GetTaskPool();
631 if (!canvasTask) {
632 return;
633 }
634 width = canvasTask->MeasureText(text, textState);
635 height = canvasTask->MeasureTextHeight(text, textState);
636 };
637 instance->GetDelegate()->PostSyncTaskToPage(task);
638 JSValue textMetrics = JS_NewObject(ctx);
639 JS_SetPropertyStr(ctx, textMetrics, "width", JS_NewFloat64(ctx, width));
640 JS_SetPropertyStr(ctx, textMetrics, "height", JS_NewFloat64(ctx, height));
641 return textMetrics;
642 }
643
JsBeginPath(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)644 JSValue CanvasBridge::JsBeginPath(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
645 {
646 LOGD("Js Begin path");
647 // 0 parameter: beginPath()
648 if ((!argv) || (argc != 0)) {
649 LOGE("argc error, argc = %{private}d", argc);
650 return JS_NULL;
651 }
652 auto task = [](const RefPtr<CanvasTaskPool>& pool) { pool->BeginPath(); };
653 PushTaskToPage(ctx, value, task);
654 return JS_NULL;
655 }
656
JsClosePath(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)657 JSValue CanvasBridge::JsClosePath(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
658 {
659 LOGD("js close path");
660 // 0 parameter: closePath()
661 if ((!argv) || (argc != 0)) {
662 LOGE("argc error, argc = %{private}d", argc);
663 return JS_NULL;
664 }
665 auto task = [](const RefPtr<CanvasTaskPool>& pool) { pool->ClosePath(); };
666 PushTaskToPage(ctx, value, task);
667 return JS_NULL;
668 }
669
JsMoveTo(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)670 JSValue CanvasBridge::JsMoveTo(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
671 {
672 LOGD("CanvasBridge::JsMoveTo");
673 // 2 parameters: moveTo(x, y)
674 if ((!argv) || (argc != 2)) {
675 LOGE("argc error, argc = %{private}d", argc);
676 return JS_NULL;
677 }
678 double x = GetJsDoubleVal(ctx, argv[0]);
679 double y = GetJsDoubleVal(ctx, argv[1]);
680 auto task = [x, y](const RefPtr<CanvasTaskPool>& pool) { pool->MoveTo(x, y); };
681 PushTaskToPage(ctx, value, task);
682 return JS_NULL;
683 }
684
JsLineTo(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)685 JSValue CanvasBridge::JsLineTo(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
686 {
687 LOGD("CanvasBridge::JsLineTo");
688 // 2 parameters: lineTo(x, y)
689 if ((!argv) || (argc != 2)) {
690 LOGE("argc error, argc = %{private}d", argc);
691 return JS_NULL;
692 }
693 double x = GetJsDoubleVal(ctx, argv[0]);
694 double y = GetJsDoubleVal(ctx, argv[1]);
695 auto task = [x, y](const RefPtr<CanvasTaskPool>& pool) { pool->LineTo(x, y); };
696 PushTaskToPage(ctx, value, task);
697 return JS_NULL;
698 }
699
JsBezierCurveTo(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)700 JSValue CanvasBridge::JsBezierCurveTo(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
701 {
702 LOGD("JsBezierCurveTo");
703 // 6 parameters: bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
704 if ((!argv) || (argc != 6)) {
705 LOGE("argc error, argc = %{private}d", argc);
706 return JS_NULL;
707 }
708 BezierCurveParam param;
709 param.cp1x = GetJsDoubleVal(ctx, argv[0]);
710 param.cp1y = GetJsDoubleVal(ctx, argv[1]);
711 param.cp2x = GetJsDoubleVal(ctx, argv[2]);
712 param.cp2y = GetJsDoubleVal(ctx, argv[3]);
713 param.x = GetJsDoubleVal(ctx, argv[4]);
714 param.y = GetJsDoubleVal(ctx, argv[5]);
715 auto task = [param](const RefPtr<CanvasTaskPool>& pool) { pool->BezierCurveTo(param); };
716 PushTaskToPage(ctx, value, task);
717 return JS_NULL;
718 }
719
JsQuadraticCurveTo(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)720 JSValue CanvasBridge::JsQuadraticCurveTo(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
721 {
722 LOGD("JsQuadraticCurveTo");
723 // 4 parameters: quadraticCurveTo(cpx, cpy, x, y)
724 if ((!argv) || (argc != 4)) {
725 LOGE("argc error, argc = %{private}d", argc);
726 return JS_NULL;
727 }
728 QuadraticCurveParam param;
729 param.cpx = GetJsDoubleVal(ctx, argv[0]);
730 param.cpy = GetJsDoubleVal(ctx, argv[1]);
731 param.x = GetJsDoubleVal(ctx, argv[2]);
732 param.y = GetJsDoubleVal(ctx, argv[3]);
733 auto task = [param](const RefPtr<CanvasTaskPool>& pool) { pool->QuadraticCurveTo(param); };
734 PushTaskToPage(ctx, value, task);
735 return JS_NULL;
736 }
737
JsArc(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)738 JSValue CanvasBridge::JsArc(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
739 {
740 LOGD("JsArc");
741 // 5 or 6 parameters: arc(x, y, radius, startAngle, endAngle, anticlockwise?)
742 if ((!argv) || (argc < 5 || argc > 6)) {
743 LOGE("argc error, argc = %{private}d", argc);
744 return JS_NULL;
745 }
746 ArcParam param;
747 param.x = GetJsDoubleVal(ctx, argv[0]);
748 param.y = GetJsDoubleVal(ctx, argv[1]);
749 param.radius = GetJsDoubleVal(ctx, argv[2]);
750 param.startAngle = GetJsDoubleVal(ctx, argv[3]);
751 param.endAngle = GetJsDoubleVal(ctx, argv[4]);
752 if (argc == 6) {
753 ScopedString arg(ctx, argv[5]);
754 if (!arg.get()) {
755 LOGW("no value");
756 return JS_NULL;
757 }
758 std::unique_ptr<JsonValue> argPtr = JsonUtil::ParseJsonString(arg.get());
759 if (argPtr && argPtr->IsBool()) {
760 param.anticlockwise = argPtr->GetBool();
761 }
762 }
763 auto task = [param](const RefPtr<CanvasTaskPool>& pool) { pool->Arc(param); };
764 PushTaskToPage(ctx, value, task);
765 return JS_NULL;
766 }
767
JsArcTo(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)768 JSValue CanvasBridge::JsArcTo(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
769 {
770 LOGD("JsArcTo");
771 // 5 parameters: arcTo(x1, y1, x2, y2, radius)
772 if ((!argv) || (argc != 5)) {
773 LOGE("argc error, argc = %{private}d", argc);
774 return JS_NULL;
775 }
776 ArcToParam param;
777 param.x1 = GetJsDoubleVal(ctx, argv[0]);
778 param.y1 = GetJsDoubleVal(ctx, argv[1]);
779 param.x2 = GetJsDoubleVal(ctx, argv[2]);
780 param.y2 = GetJsDoubleVal(ctx, argv[3]);
781 param.radius = GetJsDoubleVal(ctx, argv[4]);
782 auto task = [param](const RefPtr<CanvasTaskPool>& pool) { pool->ArcTo(param); };
783 PushTaskToPage(ctx, value, task);
784 return JS_NULL;
785 }
786
JsEllipse(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)787 JSValue CanvasBridge::JsEllipse(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
788 {
789 LOGD("JsEllipse");
790 // 7 or 8 parameters: ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise?)
791 if ((!argv) || (argc < 7 || argc > 8)) {
792 LOGE("argc error, argc = %{private}d", argc);
793 return JS_NULL;
794 }
795 EllipseParam param;
796 param.x = GetJsDoubleVal(ctx, argv[0]);
797 param.y = GetJsDoubleVal(ctx, argv[1]);
798 param.radiusX = GetJsDoubleVal(ctx, argv[2]);
799 param.radiusY = GetJsDoubleVal(ctx, argv[3]);
800 param.rotation = GetJsDoubleVal(ctx, argv[4]);
801 param.startAngle = GetJsDoubleVal(ctx, argv[5]);
802 param.endAngle = GetJsDoubleVal(ctx, argv[6]);
803 if (argc == 8) {
804 int32_t anti = static_cast<int32_t>(GetJsDoubleVal(ctx, argv[7]));
805 param.anticlockwise = (anti == 1);
806 }
807 auto task = [param](const RefPtr<CanvasTaskPool>& pool) { pool->Ellipse(param); };
808 PushTaskToPage(ctx, value, task);
809 return JS_NULL;
810 }
811
JsRect(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)812 JSValue CanvasBridge::JsRect(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
813 {
814 LOGD("JsRect");
815 Rect rect = GetJsRectParam(ctx, argc, argv);
816 auto task = [rect](const RefPtr<CanvasTaskPool>& pool) { pool->AddRect(rect); };
817 PushTaskToPage(ctx, value, std::move(task));
818 return JS_NULL;
819 }
820
JsFill(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)821 JSValue CanvasBridge::JsFill(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
822 {
823 LOGD("JsFill");
824 auto task = [](const RefPtr<CanvasTaskPool>& pool) { pool->Fill(); };
825 PushTaskToPage(ctx, value, task);
826 return JS_NULL;
827 }
828
JsStroke(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)829 JSValue CanvasBridge::JsStroke(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
830 {
831 LOGD("JsStroke");
832 // 0 or 1 parameter: ctx.stroke() / ctx.stroke(path)
833 if (argv != nullptr && argc == 1) {
834 JSValue typeVal = JS_GetPropertyStr(ctx, argv[0], "__type");
835 ScopedString type(ctx, typeVal);
836 if (std::strcmp(type.get(), "path2d") != 0) {
837 LOGE("Stroke Path2D failed, target is not path.");
838 return JS_NULL;
839 }
840 auto path = GetPath2D(ctx, argv[0]);
841 if (path == nullptr) {
842 LOGE("Stroke Path2D failed, target path is null.");
843 return JS_NULL;
844 }
845 auto task = [path](const RefPtr<CanvasTaskPool>& pool) { pool->Stroke(path); };
846 PushTaskToPage(ctx, value, task);
847 return JS_NULL;
848 }
849 auto task = [](const RefPtr<CanvasTaskPool>& pool) { pool->Stroke(); };
850 PushTaskToPage(ctx, value, task);
851 return JS_NULL;
852 }
853
JsClip(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)854 JSValue CanvasBridge::JsClip(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
855 {
856 LOGD("JsClip");
857 auto task = [](const RefPtr<CanvasTaskPool>& pool) { pool->Clip(); };
858 PushTaskToPage(ctx, value, task);
859 return JS_NULL;
860 }
861
JsRestore(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)862 JSValue CanvasBridge::JsRestore(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
863 {
864 LOGD("CanvasBridge::JsRestore");
865 // 0 parameter: restore()
866 if ((!argv) || (argc != 0)) {
867 LOGE("argc error, argc = %{private}d", argc);
868 return JS_NULL;
869 }
870 auto task = [](const RefPtr<CanvasTaskPool>& pool) { pool->Restore(); };
871 PushTaskToPage(ctx, value, task);
872 return JS_NULL;
873 }
874
JsSave(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)875 JSValue CanvasBridge::JsSave(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
876 {
877 // 0 parameter: save()
878 if ((!argv) || (argc != 0)) {
879 LOGE("argc error, argc = %{private}d", argc);
880 return JS_NULL;
881 }
882 auto task = [](const RefPtr<CanvasTaskPool>& pool) { pool->Save(); };
883 PushTaskToPage(ctx, value, task);
884 return JS_NULL;
885 }
886
JsRotate(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)887 JSValue CanvasBridge::JsRotate(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
888 {
889 LOGD("CanvasBridge::JsRotate");
890 // 1 parameter: rotate(angle)
891 if ((!argv) || (argc != 1)) {
892 LOGE("argc error, argc = %{private}d", argc);
893 return JS_NULL;
894 }
895 double angle = GetJsDoubleVal(ctx, argv[0]);
896 auto task = [angle](const RefPtr<CanvasTaskPool>& pool) { pool->Rotate(angle); };
897 PushTaskToPage(ctx, value, task);
898 return JS_NULL;
899 }
900
JsScale(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)901 JSValue CanvasBridge::JsScale(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
902 {
903 LOGD("CanvasBridge::JsScale");
904 // 2 parameters: scale(x, y)
905 if ((!argv) || (argc != 2)) {
906 LOGE("argc error, argc = %{private}d", argc);
907 return JS_NULL;
908 }
909 double x = GetJsDoubleVal(ctx, argv[0]);
910 double y = GetJsDoubleVal(ctx, argv[1]);
911 auto task = [x, y](const RefPtr<CanvasTaskPool>& pool) { pool->Scale(x, y); };
912 PushTaskToPage(ctx, value, task);
913 return JS_NULL;
914 }
915
JsSetTransform(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)916 JSValue CanvasBridge::JsSetTransform(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
917 {
918 LOGD("CanvasBridge::JsSetTransform");
919 // 6 parameters: setTransform(a, b, c, d, e, f)
920 if ((!argv) || (argc != 6)) {
921 LOGE("argc error, argc = %{private}d", argc);
922 return JS_NULL;
923 }
924 TransformParam param;
925 param.scaleX = GetJsDoubleVal(ctx, argv[0]);
926 param.skewX = GetJsDoubleVal(ctx, argv[1]);
927 param.skewY = GetJsDoubleVal(ctx, argv[2]);
928 param.scaleY = GetJsDoubleVal(ctx, argv[3]);
929 param.translateX = GetJsDoubleVal(ctx, argv[4]);
930 param.translateY = GetJsDoubleVal(ctx, argv[5]);
931 auto task = [param](const RefPtr<CanvasTaskPool>& pool) { pool->SetTransform(param); };
932 PushTaskToPage(ctx, value, task);
933 return JS_NULL;
934 }
935
JsTransform(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)936 JSValue CanvasBridge::JsTransform(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
937 {
938 LOGD("CanvasBridge::JsTransform");
939 // 6 parameters: transform(a, b, c, d, e, f)
940 if ((!argv) || (argc != 6)) {
941 LOGE("argc error, argc = %{private}d", argc);
942 return JS_NULL;
943 }
944 TransformParam param;
945 param.scaleX = GetJsDoubleVal(ctx, argv[0]);
946 param.skewX = GetJsDoubleVal(ctx, argv[1]);
947 param.skewY = GetJsDoubleVal(ctx, argv[2]);
948 param.scaleY = GetJsDoubleVal(ctx, argv[3]);
949 param.translateX = GetJsDoubleVal(ctx, argv[4]);
950 param.translateY = GetJsDoubleVal(ctx, argv[5]);
951 auto task = [param](const RefPtr<CanvasTaskPool>& pool) { pool->Transform(param); };
952 PushTaskToPage(ctx, value, task);
953 return JS_NULL;
954 }
955
JsTranslate(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)956 JSValue CanvasBridge::JsTranslate(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
957 {
958 LOGD("CanvasBridge::JsTranslate");
959 // 2 parameters: translate(x, y)
960 if ((!argv) || (argc != 2)) {
961 LOGE("argc error, argc = %{private}d", argc);
962 return JS_NULL;
963 }
964 double x = GetJsDoubleVal(ctx, argv[0]);
965 double y = GetJsDoubleVal(ctx, argv[1]);
966 auto task = [x, y](const RefPtr<CanvasTaskPool>& pool) { pool->Translate(x, y); };
967 PushTaskToPage(ctx, value, task);
968 return JS_NULL;
969 }
970
JsSetLineDash(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)971 JSValue CanvasBridge::JsSetLineDash(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
972 {
973 LOGD("CanvasBridge::JsSetLineDash");
974 // 1 parameter: setLineDash(segments)
975 if ((!argv) || (argc != 1)) {
976 LOGE("argc error, argc = %{private}d", argc);
977 return JS_NULL;
978 }
979 ScopedString dash(ctx, argv[0]);
980 JS_SetPropertyStr(ctx, value, "lineDash", JS_NewString(ctx, dash.get()));
981 auto segments = GetJsDashValue(ctx, argv[0]);
982 auto task = [segments](const RefPtr<CanvasTaskPool>& pool) { pool->UpdateLineDash(segments); };
983 PushTaskToPage(ctx, value, task);
984 return JS_NULL;
985 }
986
JsGetLineDash(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)987 JSValue CanvasBridge::JsGetLineDash(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
988 {
989 // 0 parameter: getLineDash()
990 if ((!argv) || (argc != 0)) {
991 LOGE("argc error, argc = %{private}d", argc);
992 return JS_NULL;
993 }
994 auto val = JS_GetPropertyStr(ctx, value, "lineDash");
995 return val;
996 }
997
ParseDomImage(JSContext * ctx,JSValueConst value,double & width,double & height,std::string & src)998 void CanvasBridge::ParseDomImage(JSContext* ctx, JSValueConst value, double& width, double& height, std::string& src)
999 {
1000 auto jsAttr = JS_GetPropertyStr(ctx, value, "attr");
1001 auto jsSrc = JS_GetPropertyStr(ctx, jsAttr, IMAGE_SRC);
1002 auto imgSrc = JS_ToCString(ctx, jsSrc);
1003 src = imgSrc;
1004
1005 auto jsStyle = JS_GetPropertyStr(ctx, value, "style");
1006 auto jsWidth = JS_GetPropertyStr(ctx, jsStyle, IMAGE_WIDTH);
1007 auto jsHeight = JS_GetPropertyStr(ctx, jsStyle, IMAGE_HEIGHT);
1008 auto cWidth = JS_ToCString(ctx, jsWidth);
1009 auto cHeight = JS_ToCString(ctx, jsHeight);
1010 std::string strWidth = cWidth;
1011 std::string strHeight = cHeight;
1012 width = StringToDouble(strWidth);
1013 height = StringToDouble(strHeight);
1014
1015 if (NearZero(width)) {
1016 width = StringToDouble(strWidth.substr(0, strWidth.size() - 2)); // remove px units
1017 }
1018 if (NearZero(height)) {
1019 height = StringToDouble(strHeight.substr(0, strHeight.size() - 2));
1020 }
1021
1022 JS_FreeValue(ctx, jsAttr);
1023 JS_FreeValue(ctx, jsSrc);
1024 JS_FreeValue(ctx, jsStyle);
1025 JS_FreeValue(ctx, jsWidth);
1026 JS_FreeValue(ctx, jsHeight);
1027 JS_FreeCString(ctx, cWidth);
1028 JS_FreeCString(ctx, cHeight);
1029 JS_FreeCString(ctx, imgSrc);
1030 }
1031
JsDrawImage(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)1032 JSValue CanvasBridge::JsDrawImage(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
1033 {
1034 if (!argv || !JS_IsObject(argv[0])) {
1035 return JS_NULL;
1036 }
1037
1038 CanvasImage image;
1039 double width = 0.0;
1040 double height = 0.0;
1041 auto src = JS_GetPropertyStr(ctx, argv[0], IMAGE_SRC);
1042
1043 if (!JS_ToBool(ctx, src)) {
1044 ParseDomImage(ctx, argv[0], width, height, image.src);
1045 } else {
1046 auto imgSrc = JS_ToCString(ctx, src);
1047 image.src = imgSrc;
1048 auto jsWidth = JS_GetPropertyStr(ctx, argv[0], IMAGE_WIDTH);
1049 auto jsHeight = JS_GetPropertyStr(ctx, argv[0], IMAGE_HEIGHT);
1050 JS_ToFloat64(ctx, &width, jsWidth);
1051 JS_ToFloat64(ctx, &height, jsHeight);
1052 JS_FreeValue(ctx, jsWidth);
1053 JS_FreeValue(ctx, jsHeight);
1054 JS_FreeCString(ctx, imgSrc);
1055 }
1056 switch (argc) {
1057 // 3 parameters: drawImage(image, dx, dy)
1058 case 3:
1059 image.flag = 0;
1060 image.dx = GetJsDoubleVal(ctx, argv[1]);
1061 image.dy = GetJsDoubleVal(ctx, argv[2]);
1062 break;
1063 // 5 parameters: drawImage(image, dx, dy, dWidth, dHeight)
1064 case 5:
1065 image.flag = 1;
1066 image.dx = GetJsDoubleVal(ctx, argv[1]);
1067 image.dy = GetJsDoubleVal(ctx, argv[2]);
1068 image.dWidth = GetJsDoubleVal(ctx, argv[3]);
1069 image.dHeight = GetJsDoubleVal(ctx, argv[4]);
1070 break;
1071 // 9 parameters: drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
1072 case 9:
1073 image.flag = 2;
1074 image.sx = GetJsDoubleVal(ctx, argv[1]);
1075 image.sy = GetJsDoubleVal(ctx, argv[2]);
1076 image.sWidth = GetJsDoubleVal(ctx, argv[3]);
1077 image.sHeight = GetJsDoubleVal(ctx, argv[4]);
1078 image.dx = GetJsDoubleVal(ctx, argv[5]);
1079 image.dy = GetJsDoubleVal(ctx, argv[6]);
1080 image.dWidth = GetJsDoubleVal(ctx, argv[7]);
1081 image.dHeight = GetJsDoubleVal(ctx, argv[8]);
1082 break;
1083 default:
1084 break;
1085 }
1086 auto task = [image, width, height](const RefPtr<CanvasTaskPool>& pool) { pool->DrawImage(image, width, height); };
1087 PushTaskToPage(ctx, value, task);
1088
1089 JS_FreeValue(ctx, src);
1090 return JS_NULL;
1091 }
1092
JsCreatePath2D(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)1093 JSValue CanvasBridge::JsCreatePath2D(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
1094 {
1095 JSValue path2D = JS_NewObject(ctx);
1096 JS_SetPropertyStr(ctx, path2D, "__type", JS_NewString(ctx, "path2d"));
1097 JS_SetPropertyStr(ctx, path2D, "__id", JS_NewInt32(ctx, path2dCount_));
1098 JS_SetPropertyStr(ctx, path2D, "addPath", JS_NewCFunction(ctx, JsPath2DAddPath, "addPath", 1));
1099 JS_SetPropertyStr(ctx, path2D, "setTransform", JS_NewCFunction(ctx, JsPath2DSetTransform, "setTransform", 6));
1100 JS_SetPropertyStr(ctx, path2D, "moveTo", JS_NewCFunction(ctx, JsPath2DMoveTo, "moveTo", 2));
1101 JS_SetPropertyStr(ctx, path2D, "lineTo", JS_NewCFunction(ctx, JsPath2DLineTo, "addColorStop", 2));
1102 JS_SetPropertyStr(ctx, path2D, "arc", JS_NewCFunction(ctx, JsPath2DArc, "arc", 6));
1103 JS_SetPropertyStr(ctx, path2D, "arcTo", JS_NewCFunction(ctx, JsPath2DArcTo, "arcTo", 5));
1104 JS_SetPropertyStr(ctx, path2D, "quadraticCurveTo",
1105 JS_NewCFunction(ctx, JsPath2DQuadraticCurveTo, "quadraticCurveTo", 4));
1106 JS_SetPropertyStr(ctx, path2D, "bezierCurveTo", JS_NewCFunction(ctx, JsPath2DBezierCurveTo, "bezierCurveTo", 6));
1107 JS_SetPropertyStr(ctx, path2D, "ellipse", JS_NewCFunction(ctx, JsPath2DEllipse, "ellipse", 8));
1108 JS_SetPropertyStr(ctx, path2D, "rect", JS_NewCFunction(ctx, JsPath2DRect, "rect", 4));
1109 JS_SetPropertyStr(ctx, path2D, "closePath", JS_NewCFunction(ctx, JsPath2DClosePath, "closePath", 0));
1110 path2Ds_[path2dCount_] = JsMakePath2D(ctx, value, argc, argv);
1111 ++path2dCount_;
1112 return path2D;
1113 }
1114
JsPath2DAddPath(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)1115 JSValue CanvasBridge::JsPath2DAddPath(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
1116 {
1117 // 1 parameter: addPath(path)
1118 if ((!argv) || (argc != 1)) {
1119 LOGE("AddPath to Path2D failed, invalid args.");
1120 return JS_NULL;
1121 }
1122 int32_t id = 0;
1123 JSValue nodeId = JS_GetPropertyStr(ctx, value, "__id");
1124 if (JS_IsInteger(nodeId) && JS_ToInt32(ctx, &id, nodeId) < 0) {
1125 LOGE("AddPath to Path2D failed, unknown holder path.");
1126 return JS_NULL;
1127 }
1128 auto holderPath = path2Ds_[id];
1129 if (holderPath == nullptr) {
1130 LOGE("AddPath to Path2D failed, holderPath is null.");
1131 return JS_NULL;
1132 }
1133 JSValue typeVal = JS_GetPropertyStr(ctx, argv[0], "__type");
1134 ScopedString type(ctx, typeVal);
1135 if (std::strcmp(type.get(), "path2d") != 0) {
1136 LOGE("AddPath to Path2D failed, to be added is not path.");
1137 return JS_NULL;
1138 }
1139 auto toBeAdd = GetPath2D(ctx, argv[0]);
1140 if (toBeAdd == nullptr) {
1141 LOGE("AddPath to Path2D failed, to be added path is null.");
1142 return JS_NULL;
1143 }
1144 holderPath->AddPath(toBeAdd);
1145 return JS_NULL;
1146 }
1147
JsPath2DSetTransform(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)1148 JSValue CanvasBridge::JsPath2DSetTransform(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
1149 {
1150 // 6 parameters: setTransform(a, b, c, d, e, f)
1151 if ((!argv) || (argc != 6)) {
1152 LOGE("Call Path2D SetTransform failed, invalid args.");
1153 return JS_NULL;
1154 }
1155 int32_t id = 0;
1156 JSValue nodeId = JS_GetPropertyStr(ctx, value, "__id");
1157 if (JS_IsInteger(nodeId) && JS_ToInt32(ctx, &id, nodeId) < 0) {
1158 LOGE("Call Path2D SetTransform failed, unknown holder path.");
1159 return JS_NULL;
1160 }
1161 auto holderPath = path2Ds_[id];
1162 if (holderPath == nullptr) {
1163 LOGE("Call Path2D SetTransform failed, holderPath is null.");
1164 return JS_NULL;
1165 }
1166 holderPath->SetTransform(GetJsDoubleVal(ctx, argv[0]), GetJsDoubleVal(ctx, argv[1]),
1167 GetJsDoubleVal(ctx, argv[2]), GetJsDoubleVal(ctx, argv[3]),
1168 GetJsDoubleVal(ctx, argv[4]), GetJsDoubleVal(ctx, argv[5]));
1169 return JS_NULL;
1170 }
1171
JsPath2DMoveTo(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)1172 JSValue CanvasBridge::JsPath2DMoveTo(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
1173 {
1174 // 2 parameters: moveTo(x, y)
1175 if ((!argv) || (argc != 2)) {
1176 LOGE("Call Path2D Arc MoveTo, invalid args.");
1177 return JS_NULL;
1178 }
1179 int32_t id = 0;
1180 JSValue nodeId = JS_GetPropertyStr(ctx, value, "__id");
1181 if (JS_IsInteger(nodeId) && JS_ToInt32(ctx, &id, nodeId) < 0) {
1182 LOGE("Call Path2D MoveTo failed, unknown holder path.");
1183 return JS_NULL;
1184 }
1185 auto holderPath = path2Ds_[id];
1186 if (holderPath == nullptr) {
1187 LOGE("Call Path2D MoveTo failed, holderPath is null.");
1188 return JS_NULL;
1189 }
1190 holderPath->MoveTo(GetJsDoubleVal(ctx, argv[0]), GetJsDoubleVal(ctx, argv[1]));
1191 return JS_NULL;
1192 }
1193
JsPath2DLineTo(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)1194 JSValue CanvasBridge::JsPath2DLineTo(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
1195 {
1196 // 2 parameters: lineTo(x, y)
1197 if ((!argv) || (argc != 2)) {
1198 LOGE("Call Path2D LineTo failed, invalid args.");
1199 return JS_NULL;
1200 }
1201 int32_t id = 0;
1202 JSValue nodeId = JS_GetPropertyStr(ctx, value, "__id");
1203 if (JS_IsInteger(nodeId) && JS_ToInt32(ctx, &id, nodeId) < 0) {
1204 LOGE("Call Path2D LineTo failed, unknown holder path.");
1205 return JS_NULL;
1206 }
1207 auto holderPath = path2Ds_[id];
1208 if (holderPath == nullptr) {
1209 LOGE("Call Path2D LineTo failed, holderPath is null.");
1210 return JS_NULL;
1211 }
1212 holderPath->LineTo(GetJsDoubleVal(ctx, argv[0]), GetJsDoubleVal(ctx, argv[1]));
1213 return JS_NULL;
1214 }
1215
JsPath2DArc(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)1216 JSValue CanvasBridge::JsPath2DArc(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
1217 {
1218 // 5 or 6 parameters: arc(x, y, radius, startAngle, endAngle, anticlockwise?)
1219 if ((!argv) || (argc < 5) || (argc > 6)) {
1220 LOGE("Call Path2D Arc failed, invalid args.");
1221 return JS_NULL;
1222 }
1223 int32_t id = 0;
1224 JSValue nodeId = JS_GetPropertyStr(ctx, value, "__id");
1225 if (JS_IsInteger(nodeId) && JS_ToInt32(ctx, &id, nodeId) < 0) {
1226 LOGE("Call Path2D Arc failed, unknown holder path.");
1227 return JS_NULL;
1228 }
1229 auto holderPath = path2Ds_[id];
1230 if (holderPath == nullptr) {
1231 LOGE("Call Path2D Arc failed, holderPath is null.");
1232 return JS_NULL;
1233 }
1234 bool anticlockwise = false;
1235 if (argc == 6) {
1236 int32_t anti = static_cast<int32_t>(GetJsDoubleVal(ctx, argv[7]));
1237 anticlockwise = (anti == 1);
1238 }
1239 holderPath->Arc(GetJsDoubleVal(ctx, argv[0]), GetJsDoubleVal(ctx, argv[1]),
1240 GetJsDoubleVal(ctx, argv[2]), GetJsDoubleVal(ctx, argv[3]),
1241 GetJsDoubleVal(ctx, argv[4]), anticlockwise);
1242 return JS_NULL;
1243 }
1244
JsPath2DArcTo(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)1245 JSValue CanvasBridge::JsPath2DArcTo(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
1246 {
1247 // 5 parameters: arcTo(x1, y1, x2, y2, radius)
1248 if ((!argv) || (argc != 5)) {
1249 LOGE("Call Path2D ArcTo failed, invalid args.");
1250 return JS_NULL;
1251 }
1252 int32_t id = 0;
1253 JSValue nodeId = JS_GetPropertyStr(ctx, value, "__id");
1254 if (JS_IsInteger(nodeId) && JS_ToInt32(ctx, &id, nodeId) < 0) {
1255 LOGE("Call Path2D ArcTo failed, unknown holder path.");
1256 return JS_NULL;
1257 }
1258 auto holderPath = path2Ds_[id];
1259 if (holderPath == nullptr) {
1260 LOGE("Call Path2D ArcTo failed, holderPath is null.");
1261 return JS_NULL;
1262 }
1263 holderPath->ArcTo(GetJsDoubleVal(ctx, argv[0]), GetJsDoubleVal(ctx, argv[1]), GetJsDoubleVal(ctx, argv[2]),
1264 GetJsDoubleVal(ctx, argv[3]), GetJsDoubleVal(ctx, argv[4]));
1265 return JS_NULL;
1266 }
1267
JsPath2DQuadraticCurveTo(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)1268 JSValue CanvasBridge::JsPath2DQuadraticCurveTo(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
1269 {
1270 // 4 parameters: quadraticCurveTo(cpx, cpy, x, y)
1271 if ((!argv) || (argc != 4)) {
1272 LOGE("Call Path2D QuadraticCurveTo failed, invalid args.");
1273 return JS_NULL;
1274 }
1275 int32_t id = 0;
1276 JSValue nodeId = JS_GetPropertyStr(ctx, value, "__id");
1277 if (JS_IsInteger(nodeId) && JS_ToInt32(ctx, &id, nodeId) < 0) {
1278 LOGE("Call Path2D QuadraticCurveTo failed, unknown holder path.");
1279 return JS_NULL;
1280 }
1281 auto holderPath = path2Ds_[id];
1282 if (holderPath == nullptr) {
1283 LOGE("Call Path2D QuadraticCurveTo failed, holderPath is null.");
1284 return JS_NULL;
1285 }
1286 holderPath->QuadraticCurveTo(GetJsDoubleVal(ctx, argv[0]), GetJsDoubleVal(ctx, argv[1]),
1287 GetJsDoubleVal(ctx, argv[2]), GetJsDoubleVal(ctx, argv[3]));
1288 return JS_NULL;
1289 }
1290
JsPath2DBezierCurveTo(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)1291 JSValue CanvasBridge::JsPath2DBezierCurveTo(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
1292 {
1293 // 6 parameters: bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
1294 if ((!argv) || (argc != 6)) {
1295 LOGE("Call Path2D BezierCurveTo failed, invalid args.");
1296 return JS_NULL;
1297 }
1298 int32_t id = 0;
1299 JSValue nodeId = JS_GetPropertyStr(ctx, value, "__id");
1300 if (JS_IsInteger(nodeId) && JS_ToInt32(ctx, &id, nodeId) < 0) {
1301 LOGE("Call Path2D BezierCurveTo failed, unknown holder path.");
1302 return JS_NULL;
1303 }
1304 auto holderPath = path2Ds_[id];
1305 if (holderPath == nullptr) {
1306 LOGE("Call Path2D BezierCurveTo failed, holderPath is null.");
1307 return JS_NULL;
1308 }
1309 holderPath->BezierCurveTo(GetJsDoubleVal(ctx, argv[0]), GetJsDoubleVal(ctx, argv[1]),
1310 GetJsDoubleVal(ctx, argv[2]), GetJsDoubleVal(ctx, argv[3]),
1311 GetJsDoubleVal(ctx, argv[4]), GetJsDoubleVal(ctx, argv[5]));
1312 return JS_NULL;
1313 }
1314
JsPath2DEllipse(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)1315 JSValue CanvasBridge::JsPath2DEllipse(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
1316 {
1317 // 7 or 8 parameters: ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise?)
1318 if ((!argv) || (argc < 7) || (argc > 8)) {
1319 LOGE("Call Path2D Ellipse failed, invalid args.");
1320 return JS_NULL;
1321 }
1322 int32_t id = 0;
1323 JSValue nodeId = JS_GetPropertyStr(ctx, value, "__id");
1324 if (JS_IsInteger(nodeId) && JS_ToInt32(ctx, &id, nodeId) < 0) {
1325 LOGE("Call Path2D Ellipse failed, unknown holder path.");
1326 return JS_NULL;
1327 }
1328 auto holderPath = path2Ds_[id];
1329 if (holderPath == nullptr) {
1330 LOGE("Call Path2D Ellipse failed, holderPath is null.");
1331 return JS_NULL;
1332 }
1333 bool anticlockwise = false;
1334 if (argc == 8) {
1335 int32_t anti = static_cast<int32_t>(GetJsDoubleVal(ctx, argv[7]));
1336 anticlockwise = (anti == 1);
1337 }
1338 holderPath->Ellipse(GetJsDoubleVal(ctx, argv[0]), GetJsDoubleVal(ctx, argv[1]),
1339 GetJsDoubleVal(ctx, argv[2]), GetJsDoubleVal(ctx, argv[3]),
1340 GetJsDoubleVal(ctx, argv[4]), GetJsDoubleVal(ctx, argv[5]),
1341 GetJsDoubleVal(ctx, argv[6]), anticlockwise);
1342 return JS_NULL;
1343 }
1344
JsPath2DRect(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)1345 JSValue CanvasBridge::JsPath2DRect(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
1346 {
1347 // 4 parameters: rect(x, y, width, height)
1348 if ((!argv) || (argc != 4)) {
1349 LOGE("Call Path2D Rect failed, invalid args.");
1350 return JS_NULL;
1351 }
1352 int32_t id = 0;
1353 JSValue nodeId = JS_GetPropertyStr(ctx, value, "__id");
1354 if (JS_IsInteger(nodeId) && JS_ToInt32(ctx, &id, nodeId) < 0) {
1355 LOGE("Call Path2D Rect failed, unknown holder path.");
1356 return JS_NULL;
1357 }
1358 auto holderPath = path2Ds_[id];
1359 if (holderPath == nullptr) {
1360 LOGE("Call Path2D Rect failed, holderPath is null.");
1361 return JS_NULL;
1362 }
1363 holderPath->Rect(GetJsDoubleVal(ctx, argv[0]), GetJsDoubleVal(ctx, argv[1]),
1364 GetJsDoubleVal(ctx, argv[2]), GetJsDoubleVal(ctx, argv[3]));
1365 return JS_NULL;
1366 }
1367
JsPath2DClosePath(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)1368 JSValue CanvasBridge::JsPath2DClosePath(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
1369 {
1370 int32_t id = 0;
1371 JSValue nodeId = JS_GetPropertyStr(ctx, value, "__id");
1372 if (JS_IsInteger(nodeId) && JS_ToInt32(ctx, &id, nodeId) < 0) {
1373 LOGE("Call Path2D ClosePath failed, unknown holder path.");
1374 return JS_NULL;
1375 }
1376 auto holderPath = path2Ds_[id];
1377 if (holderPath == nullptr) {
1378 LOGE("Call Path2D ClosePath failed, holderPath is null.");
1379 return JS_NULL;
1380 }
1381 holderPath->ClosePath();
1382 return JS_NULL;
1383 }
1384
JsMakePath2D(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)1385 RefPtr<CanvasPath2D> CanvasBridge::JsMakePath2D(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
1386 {
1387 if (argv != nullptr && argc == 1) {
1388 if (JS_IsString(argv[0])) {
1389 // Example: ctx.createPath2D("M250 150 L150 350 L350 350 Z")
1390 return AceType::MakeRefPtr<CanvasPath2D>(JS_ToCString(ctx, argv[0]));
1391 } else {
1392 JSValue typeVal = JS_GetPropertyStr(ctx, argv[0], "__type");
1393 ScopedString type(ctx, typeVal);
1394 if (std::strcmp(type.get(), "path2d") == 0) {
1395 // Example: ctx.createPath2D(path1)
1396 return AceType::MakeRefPtr<CanvasPath2D>(GetPath2D(ctx, argv[0]));
1397 }
1398 }
1399 }
1400 // Example: ctx.createPath2D()
1401 return AceType::MakeRefPtr<CanvasPath2D>();
1402 }
1403
GetPath2D(JSContext * ctx,JSValueConst value)1404 RefPtr<CanvasPath2D> CanvasBridge::GetPath2D(JSContext* ctx, JSValueConst value)
1405 {
1406 int32_t id = 0;
1407 JSValue nodeId = JS_GetPropertyStr(ctx, value, "__id");
1408 if (JS_IsInteger(nodeId) && (JS_ToInt32(ctx, &id, nodeId)) < 0) {
1409 return nullptr;
1410 }
1411 JS_FreeValue(ctx, nodeId);
1412 return path2Ds_[id];
1413 }
1414
JsCreatePattern(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)1415 JSValue CanvasBridge::JsCreatePattern(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
1416 {
1417 LOGD("CanvasBridge::JsCreatePattern");
1418 // 2 parameters: createPattern(image, repetition)
1419 if ((!argv) || (argc != 2)) {
1420 LOGE("argc error, argc = %{private}d", argc);
1421 return JS_NULL;
1422 }
1423
1424 JSValue pattern = JS_NewObject(ctx);
1425 JS_SetPropertyStr(ctx, pattern, "__id", JS_NewFloat64(ctx, patternCount_));
1426 JS_SetPropertyStr(ctx, pattern, "__type", JS_NewString(ctx, "pattern"));
1427
1428 if (!JS_IsObject(argv[0])) {
1429 return JS_NULL;
1430 }
1431 double width = 0.0;
1432 double height = 0.0;
1433 std::string imageSrc;
1434 auto jsSrc = JS_GetPropertyStr(ctx, argv[0], IMAGE_SRC);
1435 if (!JS_ToBool(ctx, jsSrc)) {
1436 ParseDomImage(ctx, argv[0], width, height, imageSrc);
1437 } else {
1438 auto jsWidth = JS_GetPropertyStr(ctx, argv[0], IMAGE_WIDTH);
1439 auto jsHeight = JS_GetPropertyStr(ctx, argv[0], IMAGE_HEIGHT);
1440 auto cImageSrc = JS_ToCString(ctx, jsSrc);
1441 imageSrc = cImageSrc;
1442 JS_ToFloat64(ctx, &width, jsWidth);
1443 JS_ToFloat64(ctx, &height, jsHeight);
1444 JS_FreeValue(ctx, jsWidth);
1445 JS_FreeValue(ctx, jsHeight);
1446 JS_FreeCString(ctx, cImageSrc);
1447 }
1448 auto repeat = JS_ToCString(ctx, argv[1]);
1449 pattern_[patternCount_].SetImgSrc(imageSrc);
1450 pattern_[patternCount_].SetImageWidth(width);
1451 pattern_[patternCount_].SetImageHeight(height);
1452 pattern_[patternCount_].SetRepetition(repeat);
1453 ++patternCount_;
1454
1455 JS_FreeValue(ctx, jsSrc);
1456 JS_FreeCString(ctx, repeat);
1457 return pattern;
1458 }
1459
GetPattern(JSContext * ctx,JSValueConst value)1460 Pattern CanvasBridge::GetPattern(JSContext* ctx, JSValueConst value)
1461 {
1462 int32_t id = 0;
1463 JSValue nodeId = JS_GetPropertyStr(ctx, value, "__id");
1464 if (JS_IsInteger(nodeId) && (JS_ToInt32(ctx, &id, nodeId)) < 0) {
1465 return Pattern();
1466 }
1467 JS_FreeValue(ctx, nodeId);
1468 return pattern_[id];
1469 }
1470
JsCreateImageData(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)1471 JSValue CanvasBridge::JsCreateImageData(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
1472 {
1473 if (!argv) {
1474 return JS_NULL;
1475 }
1476
1477 // 1 or 2 parameters: createImageData(imagedata) / createImageData(width, height)
1478 if (argc != 1 && argc != 2) {
1479 return JS_NULL;
1480 }
1481
1482 JSValue imageData = JS_NewObject(ctx);
1483 int32_t width = 0;
1484 int32_t height = 0;
1485
1486 if (argc == 2) {
1487 JS_ToInt32(ctx, &width, argv[0]);
1488 JS_ToInt32(ctx, &height, argv[1]);
1489 }
1490
1491 if (argc == 1 && JS_IsObject(argv[0])) {
1492 auto jsWidth = JS_GetPropertyStr(ctx, argv[0], "width");
1493 auto jsHeight = JS_GetPropertyStr(ctx, argv[0], "height");
1494 JS_ToInt32(ctx, &width, jsWidth);
1495 JS_ToInt32(ctx, &height, jsHeight);
1496 JS_FreeValue(ctx, jsWidth);
1497 JS_FreeValue(ctx, jsHeight);
1498 }
1499
1500 JSValue colorArray = JS_NewArray(ctx);
1501 uint32_t count = 0;
1502 for (auto i = 0; i < width; i++) {
1503 for (auto j = 0; j < height; j++) {
1504 JS_SetPropertyUint32(ctx, colorArray, count, JS_NewInt32(ctx, 255));
1505 JS_SetPropertyUint32(ctx, colorArray, count + 1, JS_NewInt32(ctx, 255));
1506 JS_SetPropertyUint32(ctx, colorArray, count + 2, JS_NewInt32(ctx, 255));
1507 JS_SetPropertyUint32(ctx, colorArray, count + 3, JS_NewInt32(ctx, 255));
1508 count += 4;
1509 }
1510 }
1511
1512 JS_SetPropertyStr(ctx, imageData, "width", JS_NewInt32(ctx, width));
1513 JS_SetPropertyStr(ctx, imageData, "height", JS_NewInt32(ctx, height));
1514 JS_SetPropertyStr(ctx, imageData, "data", colorArray);
1515 return imageData;
1516 }
1517
JsPutImageData(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)1518 JSValue CanvasBridge::JsPutImageData(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
1519 {
1520 if (!argv) {
1521 return JS_NULL;
1522 }
1523
1524 // 3 or 7 parameters: putImageData(data, dx, dy) / putImageData(data, dx, dy, dirtyX, dirtyY, dirtyW, dirtyH)
1525 if (argc != 3 && argc != 7 && !JS_IsObject(argv[0])) {
1526 return JS_NULL;
1527 }
1528
1529 int32_t width = 0;
1530 int32_t height = 0;
1531 auto jsWidth = JS_GetPropertyStr(ctx, argv[0], "width");
1532 auto jsHeight = JS_GetPropertyStr(ctx, argv[0], "height");
1533 JS_ToInt32(ctx, &width, jsWidth);
1534 JS_ToInt32(ctx, &height, jsHeight);
1535 ImageData imageData;
1536 std::vector<std::string> array;
1537 ParseImageData(ctx, argv, argc, array, imageData);
1538
1539 int64_t num = 0;
1540 for (int32_t i = 0; i < height; ++i) {
1541 for (int32_t j = 0; j < width; ++j) {
1542 if ((i >= imageData.dirtyY) && (i - imageData.dirtyY < imageData.dirtyHeight) && (j >= imageData.dirtyX) &&
1543 (j - imageData.dirtyX < imageData.dirtyWidth)) {
1544 int32_t flag = j + width * i;
1545 if (array.size() > static_cast<uint32_t>(4 * flag + 3)) {
1546 auto red = StringUtils::StringToInt(array[4 * flag]);
1547 auto green = StringUtils::StringToInt(array[4 * flag + 1]);
1548 auto blue = StringUtils::StringToInt(array[4 * flag + 2]);
1549 auto alpha = StringUtils::StringToInt(array[4 * flag + 3]);
1550 if (num < imageData.dirtyWidth * imageData.dirtyHeight) {
1551 imageData.data.emplace_back(Color::FromARGB(alpha, red, green, blue));
1552 }
1553 num++;
1554 }
1555 }
1556 }
1557 }
1558
1559 auto task = [imageData](const RefPtr<CanvasTaskPool>& pool) { pool->PutImageData(imageData); };
1560 PushTaskToPage(ctx, value, task);
1561
1562 JS_FreeValue(ctx, jsWidth);
1563 JS_FreeValue(ctx, jsHeight);
1564 return JS_NULL;
1565 }
1566
ParseImageData(JSContext * ctx,JSValueConst * argv,int32_t argc,std::vector<std::string> & array,ImageData & imageData)1567 void CanvasBridge::ParseImageData(
1568 JSContext* ctx, JSValueConst* argv, int32_t argc, std::vector<std::string>& array, ImageData& imageData)
1569 {
1570 int32_t width = 0;
1571 int32_t height = 0;
1572 auto jsWidth = JS_GetPropertyStr(ctx, argv[0], "width");
1573 auto jsHeight = JS_GetPropertyStr(ctx, argv[0], "height");
1574 auto jsData = JS_GetPropertyStr(ctx, argv[0], "data");
1575 JS_ToInt32(ctx, &width, jsWidth);
1576 JS_ToInt32(ctx, &height, jsHeight);
1577 ScopedString scopedString(ctx, jsData);
1578 StringUtils::StringSplitter(scopedString.get(), ',', array);
1579
1580 JS_ToInt32(ctx, &imageData.x, argv[1]);
1581 JS_ToInt32(ctx, &imageData.y, argv[2]);
1582 imageData.dirtyWidth = width;
1583 imageData.dirtyHeight = height;
1584
1585 if (argc == 7) {
1586 JS_ToInt32(ctx, &imageData.dirtyX, argv[3]);
1587 JS_ToInt32(ctx, &imageData.dirtyY, argv[4]);
1588 JS_ToInt32(ctx, &imageData.dirtyWidth, argv[5]);
1589 JS_ToInt32(ctx, &imageData.dirtyHeight, argv[6]);
1590 }
1591
1592 imageData.dirtyWidth = imageData.dirtyX < 0 ? std::min(imageData.dirtyX + imageData.dirtyWidth, width)
1593 : std::min(width - imageData.dirtyX, imageData.dirtyWidth);
1594 imageData.dirtyHeight = imageData.dirtyY < 0 ? std::min(imageData.dirtyY + imageData.dirtyHeight, height)
1595 : std::min(height - imageData.dirtyY, imageData.dirtyHeight);
1596
1597 JS_FreeValue(ctx, jsWidth);
1598 JS_FreeValue(ctx, jsHeight);
1599 JS_FreeValue(ctx, jsData);
1600 }
1601
JsGetImageData(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)1602 JSValue CanvasBridge::JsGetImageData(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
1603 {
1604 // 4 parameters: getImageData(sx, sy, sw, sh)
1605 if (!argv || (argc != 4)) {
1606 return JS_NULL;
1607 }
1608 Rect rect = GetJsRectParam(ctx, argc, argv);
1609 rect.SetLeft(static_cast<int32_t>(rect.Left()));
1610 rect.SetTop(static_cast<int32_t>(rect.Top()));
1611 rect.SetWidth(static_cast<int32_t>(rect.Width()));
1612 rect.SetHeight(static_cast<int32_t>(rect.Height()));
1613 NodeId id = GetCurrentNodeId(ctx, value);
1614 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1615 auto page = instance->GetRunningPage();
1616 if (!page) {
1617 return JS_NULL;
1618 }
1619 std::unique_ptr<ImageData> data;
1620 auto task = [id, page, &rect, &data]() {
1621 auto canvas = AceType::DynamicCast<DOMCanvas>(page->GetDomDocument()->GetDOMNodeById(id));
1622 if (!canvas) {
1623 return;
1624 }
1625 auto paintChild = AceType::DynamicCast<CustomPaintComponent>(canvas->GetSpecializedComponent());
1626 auto canvasTask = paintChild->GetTaskPool();
1627 if (!canvasTask) {
1628 return;
1629 }
1630 data = canvasTask->GetImageData(rect.Left(), rect.Top(), rect.Width(), rect.Height());
1631 };
1632 instance->GetDelegate()->PostSyncTaskToPage(task);
1633
1634 JSValue imageData = JS_NewObject(ctx);
1635 JS_SetPropertyStr(ctx, imageData, "width", JS_NewInt32(ctx, data->dirtyWidth));
1636 JS_SetPropertyStr(ctx, imageData, "height", JS_NewInt32(ctx, data->dirtyHeight));
1637 JSValue colorArray = JS_NewArray(ctx);
1638 uint32_t count = 0;
1639 // travel data
1640 for (auto i = 0; i < data->dirtyHeight; i++) {
1641 for (auto j = 0; j < data->dirtyWidth; j++) {
1642 // a pixel includes 4 data: red/green/blue/alpha
1643 int32_t idx = i * data->dirtyWidth + j;
1644 auto pixel = data->data[idx];
1645 JS_SetPropertyUint32(ctx, colorArray, count, JS_NewInt32(ctx, pixel.GetRed()));
1646 JS_SetPropertyUint32(ctx, colorArray, count + 1, JS_NewInt32(ctx, pixel.GetGreen()));
1647 JS_SetPropertyUint32(ctx, colorArray, count + 2, JS_NewInt32(ctx, pixel.GetBlue()));
1648 JS_SetPropertyUint32(ctx, colorArray, count + 3, JS_NewInt32(ctx, pixel.GetAlpha()));
1649 count += 4;
1650 }
1651 }
1652 JS_SetPropertyStr(ctx, imageData, "data", colorArray);
1653 return imageData;
1654 }
1655
JsGetJsonData(JSContext * ctx,JSValueConst value,int32_t argc,JSValueConst * argv)1656 JSValue CanvasBridge::JsGetJsonData(JSContext* ctx, JSValueConst value, int32_t argc, JSValueConst* argv)
1657 {
1658 // 1 parameters: JsGetJsonData(path)
1659 if (!argv || (argc != 1)) {
1660 return JS_NULL;
1661 }
1662 NodeId id = GetCurrentNodeId(ctx, value);
1663 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
1664 auto page = instance->GetRunningPage();
1665 if (!page) {
1666 return JS_NULL;
1667 }
1668
1669 std::string path = JS_ToCString(ctx, argv[0]);
1670 std::string jsonData;
1671 auto task = [id, page, path, &jsonData]() {
1672 auto canvas = AceType::DynamicCast<DOMCanvas>(page->GetDomDocument()->GetDOMNodeById(id));
1673 if (!canvas) {
1674 return;
1675 }
1676 auto paintChild = AceType::DynamicCast<CustomPaintComponent>(canvas->GetSpecializedComponent());
1677 auto canvasTask = paintChild->GetTaskPool();
1678 if (!canvasTask) {
1679 return;
1680 }
1681 jsonData = canvasTask->GetJsonData(path);
1682 };
1683 instance->GetDelegate()->PostSyncTaskToPage(task);
1684
1685 return JS_NewString(ctx, jsonData.c_str());
1686 }
1687
JsFillStyleGetter(JSContext * ctx,JSValueConst value)1688 JSValue CanvasBridge::JsFillStyleGetter(JSContext* ctx, JSValueConst value)
1689 {
1690 return JS_GetPropertyStr(ctx, value, "__fillStyle");
1691 }
1692
JsHandleStyleSetter(JSContext * ctx,JSValueConst value,JSValueConst proto,FunctionCode functionCode)1693 JSValue CanvasBridge::JsHandleStyleSetter(
1694 JSContext* ctx, JSValueConst value, JSValueConst proto, FunctionCode functionCode)
1695 {
1696 if (JS_IsString(proto)) {
1697 auto colorStr = JS_ToCString(ctx, proto);
1698 auto color = Color::FromString(colorStr);
1699 auto task = [color, functionCode](const RefPtr<CanvasTaskPool>& pool) {
1700 if (functionCode == FunctionCode::STROKE_STYLE_SETTER) {
1701 pool->UpdateStrokeColor(color);
1702 } else if (functionCode == FunctionCode::FILL_STYLE_SETTER) {
1703 pool->UpdateFillColor(color);
1704 } else {
1705 LOGW("unsupported function for update color.");
1706 }
1707 };
1708 PushTaskToPage(ctx, value, task);
1709 JS_FreeCString(ctx, colorStr);
1710 } else {
1711 JSValue typeVal = JS_GetPropertyStr(ctx, proto, "__type");
1712 ScopedString type(ctx, typeVal);
1713 if (std::strcmp(type.get(), "gradient") == 0) {
1714 auto gradient = GetGradient(ctx, proto);
1715 auto task = [gradient, functionCode](const RefPtr<CanvasTaskPool>& pool) {
1716 if (functionCode == FunctionCode::STROKE_STYLE_SETTER) {
1717 pool->UpdateStrokeGradient(gradient);
1718 } else if (functionCode == FunctionCode::FILL_STYLE_SETTER) {
1719 pool->UpdateFillGradient(gradient);
1720 } else {
1721 LOGW("unsupported function for update gradient.");
1722 }
1723 };
1724 PushTaskToPage(ctx, value, task);
1725 } else if (std::strcmp(type.get(), "pattern") == 0) {
1726 auto pattern = GetPattern(ctx, proto);
1727 auto task = [pattern, functionCode](const RefPtr<CanvasTaskPool>& pool) {
1728 if (functionCode == FunctionCode::STROKE_STYLE_SETTER) {
1729 pool->UpdateStrokePattern(pattern);
1730 } else if (functionCode == FunctionCode::FILL_STYLE_SETTER) {
1731 pool->UpdateFillPattern(pattern);
1732 } else {
1733 LOGW("No such type for update pattern.");
1734 }
1735 };
1736 PushTaskToPage(ctx, value, task);
1737 } else {
1738 LOGW("unsupported function for stroke style.");
1739 }
1740 }
1741
1742 if (functionCode == FunctionCode::STROKE_STYLE_SETTER) {
1743 JS_SetPropertyStr(ctx, value, "__strokeStyle", JS_DupValue(ctx, proto));
1744 } else if (functionCode == FunctionCode::FILL_STYLE_SETTER) {
1745 JS_SetPropertyStr(ctx, value, "__fillStyle", JS_DupValue(ctx, proto));
1746 } else {
1747 LOGW("No such type for set property.");
1748 }
1749 return JS_NULL;
1750 }
1751
JsFillStyleSetter(JSContext * ctx,JSValueConst value,JSValueConst proto)1752 JSValue CanvasBridge::JsFillStyleSetter(JSContext* ctx, JSValueConst value, JSValueConst proto)
1753 {
1754 return JsHandleStyleSetter(ctx, value, proto, FunctionCode::FILL_STYLE_SETTER);
1755 }
1756
JsStrokeStyleGetter(JSContext * ctx,JSValueConst value)1757 JSValue CanvasBridge::JsStrokeStyleGetter(JSContext* ctx, JSValueConst value)
1758 {
1759 return JS_GetPropertyStr(ctx, value, "__strokeStyle");
1760 }
1761
JsStrokeStyleSetter(JSContext * ctx,JSValueConst value,JSValueConst proto)1762 JSValue CanvasBridge::JsStrokeStyleSetter(JSContext* ctx, JSValueConst value, JSValueConst proto)
1763 {
1764 return JsHandleStyleSetter(ctx, value, proto, FunctionCode::STROKE_STYLE_SETTER);
1765 }
1766
JsLineCapGetter(JSContext * ctx,JSValueConst value)1767 JSValue CanvasBridge::JsLineCapGetter(JSContext* ctx, JSValueConst value)
1768 {
1769 return JS_GetPropertyStr(ctx, value, "__lineCap");
1770 }
1771
JsLineCapSetter(JSContext * ctx,JSValueConst value,JSValueConst proto)1772 JSValue CanvasBridge::JsLineCapSetter(JSContext* ctx, JSValueConst value, JSValueConst proto)
1773 {
1774 ScopedString capStr(ctx, proto);
1775 static const LinearMapNode<LineCapStyle> lineCapTable[] = {
1776 { "butt", LineCapStyle::BUTT },
1777 { "round", LineCapStyle::ROUND },
1778 { "square", LineCapStyle::SQUARE },
1779 };
1780 auto lineCap = ConvertStrToEnum(capStr.get(), lineCapTable, ArraySize(lineCapTable), LineCapStyle::BUTT);
1781 auto task = [lineCap](const RefPtr<CanvasTaskPool>& pool) { pool->UpdateLineCap(lineCap); };
1782 PushTaskToPage(ctx, value, task);
1783 JS_SetPropertyStr(ctx, value, "__lineCap", JS_DupValue(ctx, proto));
1784 return JS_NULL;
1785 }
1786
JsLineJoinGetter(JSContext * ctx,JSValueConst value)1787 JSValue CanvasBridge::JsLineJoinGetter(JSContext* ctx, JSValueConst value)
1788 {
1789 return JS_GetPropertyStr(ctx, value, "__lineJoin");
1790 }
1791
JsLineJoinSetter(JSContext * ctx,JSValueConst value,JSValueConst proto)1792 JSValue CanvasBridge::JsLineJoinSetter(JSContext* ctx, JSValueConst value, JSValueConst proto)
1793 {
1794 ScopedString joinStr(ctx, proto);
1795 static const LinearMapNode<LineJoinStyle> lineJoinTable[3] = {
1796 { "bevel", LineJoinStyle::BEVEL },
1797 { "miter", LineJoinStyle::MITER },
1798 { "round", LineJoinStyle::ROUND },
1799 };
1800 auto lineJoin = ConvertStrToEnum(joinStr.get(), lineJoinTable, ArraySize(lineJoinTable), LineJoinStyle::MITER);
1801 auto task = [lineJoin](const RefPtr<CanvasTaskPool>& pool) { pool->UpdateLineJoin(lineJoin); };
1802 PushTaskToPage(ctx, value, task);
1803 JS_SetPropertyStr(ctx, value, "__lineJoin", JS_DupValue(ctx, proto));
1804 return JS_NULL;
1805 }
1806
JsMiterLimitGetter(JSContext * ctx,JSValueConst value)1807 JSValue CanvasBridge::JsMiterLimitGetter(JSContext* ctx, JSValueConst value)
1808 {
1809 return JS_GetPropertyStr(ctx, value, "__miterLimit");
1810 }
1811
JsMiterLimitSetter(JSContext * ctx,JSValueConst value,JSValueConst proto)1812 JSValue CanvasBridge::JsMiterLimitSetter(JSContext* ctx, JSValueConst value, JSValueConst proto)
1813 {
1814 double limit = GetJsDoubleVal(ctx, proto);
1815 auto task = [limit](const RefPtr<CanvasTaskPool>& pool) { pool->UpdateMiterLimit(limit); };
1816 PushTaskToPage(ctx, value, task);
1817 JS_SetPropertyStr(ctx, value, "__miterLimit", JS_DupValue(ctx, proto));
1818 return JS_NULL;
1819 }
1820
JsLineWidthGetter(JSContext * ctx,JSValueConst value)1821 JSValue CanvasBridge::JsLineWidthGetter(JSContext* ctx, JSValueConst value)
1822 {
1823 return JS_GetPropertyStr(ctx, value, "__lineWidth");
1824 }
1825
JsLineWidthSetter(JSContext * ctx,JSValueConst value,JSValueConst proto)1826 JSValue CanvasBridge::JsLineWidthSetter(JSContext* ctx, JSValueConst value, JSValueConst proto)
1827 {
1828 double lineWidth = GetJsDoubleVal(ctx, proto);
1829 auto task = [lineWidth](const RefPtr<CanvasTaskPool>& pool) { pool->UpdateLineWidth(lineWidth); };
1830 PushTaskToPage(ctx, value, task);
1831 JS_SetPropertyStr(ctx, value, "__lineWidth", JS_DupValue(ctx, proto));
1832 return JS_NULL;
1833 }
1834
JsTextAlignGetter(JSContext * ctx,JSValueConst value)1835 JSValue CanvasBridge::JsTextAlignGetter(JSContext* ctx, JSValueConst value)
1836 {
1837 return JS_GetPropertyStr(ctx, value, "__textAlign");
1838 }
1839
JsTextAlignSetter(JSContext * ctx,JSValueConst value,JSValueConst proto)1840 JSValue CanvasBridge::JsTextAlignSetter(JSContext* ctx, JSValueConst value, JSValueConst proto)
1841 {
1842 ScopedString alignStr(ctx, proto);
1843 auto align = ConvertStrToTextAlign(alignStr.get());
1844 auto task = [align](const RefPtr<CanvasTaskPool>& pool) { pool->UpdateTextAlign(align); };
1845 PushTaskToPage(ctx, value, task);
1846 JS_SetPropertyStr(ctx, value, "__textAlign", JS_DupValue(ctx, proto));
1847 return JS_NULL;
1848 }
1849
JsTextBaselineGetter(JSContext * ctx,JSValueConst value)1850 JSValue CanvasBridge::JsTextBaselineGetter(JSContext* ctx, JSValueConst value)
1851 {
1852 return JS_GetPropertyStr(ctx, value, "__textBaseline");
1853 }
1854
JsTextBaselineSetter(JSContext * ctx,JSValueConst value,JSValueConst proto)1855 JSValue CanvasBridge::JsTextBaselineSetter(JSContext* ctx, JSValueConst value, JSValueConst proto)
1856 {
1857 ScopedString baselineStr(ctx, proto);
1858 auto baseline =
1859 ConvertStrToEnum(baselineStr.get(), BASELINE_TABLE, ArraySize(BASELINE_TABLE), TextBaseline::ALPHABETIC);
1860 auto task = [baseline](const RefPtr<CanvasTaskPool>& pool) { pool->UpdateTextBaseline(baseline); };
1861 PushTaskToPage(ctx, value, task);
1862 JS_SetPropertyStr(ctx, value, "__textBaseline", JS_DupValue(ctx, proto));
1863 return JS_NULL;
1864 }
1865
JsFontGetter(JSContext * ctx,JSValueConst value)1866 JSValue CanvasBridge::JsFontGetter(JSContext* ctx, JSValueConst value)
1867 {
1868 return JS_GetPropertyStr(ctx, value, "__font");
1869 }
1870
JsFontSetter(JSContext * ctx,JSValueConst value,JSValueConst proto)1871 JSValue CanvasBridge::JsFontSetter(JSContext* ctx, JSValueConst value, JSValueConst proto)
1872 {
1873 ScopedString fontStr(ctx, proto);
1874 std::vector<std::string> fontProps;
1875 StringUtils::StringSplitter(fontStr.get(), ' ', fontProps);
1876 bool updateFontStyle = false;
1877 for (const auto& fontProp : fontProps) {
1878 if (FONT_WEIGHTS.find(fontProp) != FONT_WEIGHTS.end()) {
1879 auto weight = ConvertStrToFontWeight(fontProp);
1880 auto task = [weight](const RefPtr<CanvasTaskPool>& pool) { pool->UpdateFontWeight(weight); };
1881 PushTaskToPage(ctx, value, task);
1882 } else if (FONT_STYLES.find(fontProp) != FONT_STYLES.end()) {
1883 updateFontStyle = true;
1884 auto fontStyle = ConvertStrToFontStyle(fontProp);
1885 auto task = [fontStyle](const RefPtr<CanvasTaskPool>& pool) { pool->UpdateFontStyle(fontStyle); };
1886 PushTaskToPage(ctx, value, task);
1887 } else if (FONT_FAMILIES.find(fontProp) != FONT_FAMILIES.end()) {
1888 auto families = ConvertStrToFontFamilies(fontProp);
1889 auto task = [families](const RefPtr<CanvasTaskPool>& pool) { pool->UpdateFontFamilies(families); };
1890 PushTaskToPage(ctx, value, task);
1891 } else if (fontProp.find("px") != std::string::npos) {
1892 std::string fontSize = fontProp.substr(0, fontProp.size() - 2);
1893 auto size = Dimension(StringToDouble(fontProp));
1894 auto task = [size](const RefPtr<CanvasTaskPool>& pool) { pool->UpdateFontSize(size); };
1895 PushTaskToPage(ctx, value, task);
1896 } else {
1897 LOGW("parse text error");
1898 }
1899 }
1900 if (!updateFontStyle) {
1901 auto task = [](const RefPtr<CanvasTaskPool>& pool) { pool->UpdateFontStyle(FontStyle::NORMAL); };
1902 PushTaskToPage(ctx, value, task);
1903 }
1904 JS_SetPropertyStr(ctx, value, "__font", JS_DupValue(ctx, proto));
1905 return JS_UNDEFINED;
1906 }
1907
JsAlphaGetter(JSContext * ctx,JSValueConst value)1908 JSValue CanvasBridge::JsAlphaGetter(JSContext* ctx, JSValueConst value)
1909 {
1910 return JS_GetPropertyStr(ctx, value, "__globalAlpha");
1911 }
1912
JsAlphaSetter(JSContext * ctx,JSValueConst value,JSValueConst proto)1913 JSValue CanvasBridge::JsAlphaSetter(JSContext* ctx, JSValueConst value, JSValueConst proto)
1914 {
1915 double alpha = GetJsDoubleVal(ctx, proto);
1916 auto task = [alpha](const RefPtr<CanvasTaskPool>& pool) { pool->UpdateGlobalAlpha(alpha); };
1917 PushTaskToPage(ctx, value, task);
1918 JS_SetPropertyStr(ctx, value, "__globalAlpha", JS_DupValue(ctx, proto));
1919 return JS_NULL;
1920 }
1921
JsCompositeOperationGetter(JSContext * ctx,JSValueConst value)1922 JSValue CanvasBridge::JsCompositeOperationGetter(JSContext* ctx, JSValueConst value)
1923 {
1924 return JS_GetPropertyStr(ctx, value, "__globalCompositeOperation");
1925 }
1926
JsCompositeOperationSetter(JSContext * ctx,JSValueConst value,JSValueConst proto)1927 JSValue CanvasBridge::JsCompositeOperationSetter(JSContext* ctx, JSValueConst value, JSValueConst proto)
1928 {
1929 ScopedString typeStr(ctx, proto);
1930 // this map must be sorted by key.
1931 static const LinearMapNode<CompositeOperation> compositeOperationTable[] = {
1932 { "copy", CompositeOperation::COPY },
1933 { "destination-atop", CompositeOperation::DESTINATION_ATOP },
1934 { "destination-in", CompositeOperation::DESTINATION_IN },
1935 { "destination-out", CompositeOperation::DESTINATION_OUT },
1936 { "destination-over", CompositeOperation::DESTINATION_OVER },
1937 { "lighter", CompositeOperation::LIGHTER },
1938 { "source-atop", CompositeOperation::SOURCE_ATOP },
1939
1940 { "source-in", CompositeOperation::SOURCE_IN },
1941 { "source-out", CompositeOperation::SOURCE_OUT },
1942 { "source-over", CompositeOperation::SOURCE_OVER },
1943 { "xor", CompositeOperation::XOR },
1944 };
1945 auto type = ConvertStrToEnum(
1946 typeStr.get(), compositeOperationTable, ArraySize(compositeOperationTable), CompositeOperation::SOURCE_OVER);
1947 auto task = [type](const RefPtr<CanvasTaskPool>& pool) { pool->UpdateCompositeOperation(type); };
1948 PushTaskToPage(ctx, value, task);
1949 JS_SetPropertyStr(ctx, value, "__globalCompositeOperation", JS_DupValue(ctx, proto));
1950 return JS_NULL;
1951 }
1952
JsLineDashOffsetGetter(JSContext * ctx,JSValueConst value)1953 JSValue CanvasBridge::JsLineDashOffsetGetter(JSContext* ctx, JSValueConst value)
1954 {
1955 return JS_GetPropertyStr(ctx, value, "__lineDash");
1956 }
1957
JsLineDashOffsetSetter(JSContext * ctx,JSValueConst value,JSValueConst proto)1958 JSValue CanvasBridge::JsLineDashOffsetSetter(JSContext* ctx, JSValueConst value, JSValueConst proto)
1959 {
1960 double dashoffset = GetJsDoubleVal(ctx, proto);
1961 auto task = [dashoffset](const RefPtr<CanvasTaskPool>& pool) { pool->UpdateLineDashOffset(dashoffset); };
1962 PushTaskToPage(ctx, value, task);
1963 JS_SetPropertyStr(ctx, value, "__lineDash", JS_DupValue(ctx, proto));
1964 return JS_NULL;
1965 }
1966
JsShadowBlurGetter(JSContext * ctx,JSValueConst value)1967 JSValue CanvasBridge::JsShadowBlurGetter(JSContext* ctx, JSValueConst value)
1968 {
1969 return JS_GetPropertyStr(ctx, value, "__shadowBlur");
1970 }
1971
JsShadowBlurSetter(JSContext * ctx,JSValueConst value,JSValueConst proto)1972 JSValue CanvasBridge::JsShadowBlurSetter(JSContext* ctx, JSValueConst value, JSValueConst proto)
1973 {
1974 double blur = GetJsDoubleVal(ctx, proto);
1975 auto task = [blur](const RefPtr<CanvasTaskPool>& pool) { pool->UpdateShadowBlur(blur); };
1976 PushTaskToPage(ctx, value, task);
1977 JS_SetPropertyStr(ctx, value, "__shadowBlur", JS_DupValue(ctx, proto));
1978 return JS_NULL;
1979 }
1980
JsShadowColorGetter(JSContext * ctx,JSValueConst value)1981 JSValue CanvasBridge::JsShadowColorGetter(JSContext* ctx, JSValueConst value)
1982 {
1983 return JS_GetPropertyStr(ctx, value, "__shadowColor");
1984 }
1985
JsShadowColorSetter(JSContext * ctx,JSValueConst value,JSValueConst proto)1986 JSValue CanvasBridge::JsShadowColorSetter(JSContext* ctx, JSValueConst value, JSValueConst proto)
1987 {
1988 ScopedString colorStr(ctx, proto);
1989 auto color = Color::FromString(colorStr.get());
1990 auto task = [color](const RefPtr<CanvasTaskPool>& pool) { pool->UpdateShadowColor(color); };
1991 PushTaskToPage(ctx, value, task);
1992 JS_SetPropertyStr(ctx, value, "__shadowColor", JS_DupValue(ctx, proto));
1993 return JS_NULL;
1994 }
1995
JsShadowOffsetXGetter(JSContext * ctx,JSValueConst value)1996 JSValue CanvasBridge::JsShadowOffsetXGetter(JSContext* ctx, JSValueConst value)
1997 {
1998 return JS_GetPropertyStr(ctx, value, "__shadowOffsetX");
1999 }
2000
JsShadowOffsetXSetter(JSContext * ctx,JSValueConst value,JSValueConst proto)2001 JSValue CanvasBridge::JsShadowOffsetXSetter(JSContext* ctx, JSValueConst value, JSValueConst proto)
2002 {
2003 double offsetX = GetJsDoubleVal(ctx, proto);
2004 auto task = [offsetX](const RefPtr<CanvasTaskPool>& pool) { pool->UpdateShadowOffsetX(offsetX); };
2005 PushTaskToPage(ctx, value, task);
2006 JS_SetPropertyStr(ctx, value, "__shadowOffsetX", JS_DupValue(ctx, proto));
2007 return JS_NULL;
2008 }
2009
JsShadowOffsetYGetter(JSContext * ctx,JSValueConst value)2010 JSValue CanvasBridge::JsShadowOffsetYGetter(JSContext* ctx, JSValueConst value)
2011 {
2012 return JS_GetPropertyStr(ctx, value, "__shadowOffsetY");
2013 }
2014
JsShadowOffsetYSetter(JSContext * ctx,JSValueConst value,JSValueConst proto)2015 JSValue CanvasBridge::JsShadowOffsetYSetter(JSContext* ctx, JSValueConst value, JSValueConst proto)
2016 {
2017 double offsetY = GetJsDoubleVal(ctx, proto);
2018 auto task = [offsetY](const RefPtr<CanvasTaskPool>& pool) { pool->UpdateShadowOffsetY(offsetY); };
2019 PushTaskToPage(ctx, value, task);
2020 JS_SetPropertyStr(ctx, value, "__shadowOffsetY", JS_DupValue(ctx, proto));
2021 return JS_NULL;
2022 }
2023
JsSmoothingEnabledGetter(JSContext * ctx,JSValueConst value)2024 JSValue CanvasBridge::JsSmoothingEnabledGetter(JSContext* ctx, JSValueConst value)
2025 {
2026 return JS_GetPropertyStr(ctx, value, "__imageSmoothingEnabled");
2027 }
2028
JsSmoothingEnabledSetter(JSContext * ctx,JSValueConst value,JSValueConst proto)2029 JSValue CanvasBridge::JsSmoothingEnabledSetter(JSContext* ctx, JSValueConst value, JSValueConst proto)
2030 {
2031 ScopedString enabledStr(ctx, proto);
2032 if (!enabledStr.get()) {
2033 return JS_NULL;
2034 }
2035 bool enabled = std::string(enabledStr.get()) == "true";
2036 auto task = [enabled](const RefPtr<CanvasTaskPool>& pool) { pool->UpdateSmoothingEnabled(enabled); };
2037 PushTaskToPage(ctx, value, task);
2038 JS_SetPropertyStr(ctx, value, "__imageSmoothingEnabled", JS_DupValue(ctx, proto));
2039 return JS_NULL;
2040 }
2041
JsSmoothingQualityGetter(JSContext * ctx,JSValueConst value)2042 JSValue CanvasBridge::JsSmoothingQualityGetter(JSContext* ctx, JSValueConst value)
2043 {
2044 return JS_GetPropertyStr(ctx, value, "__imageSmoothingQuality");
2045 }
2046
JsSmoothingQualitySetter(JSContext * ctx,JSValueConst value,JSValueConst proto)2047 JSValue CanvasBridge::JsSmoothingQualitySetter(JSContext* ctx, JSValueConst value, JSValueConst proto)
2048 {
2049 ScopedString qualityStr(ctx, proto);
2050 if (!qualityStr.get()) {
2051 return JS_NULL;
2052 }
2053 std::string quality = qualityStr.get();
2054 if (QUALITY_TYPE.find(quality) == QUALITY_TYPE.end()) {
2055 return JS_NULL;
2056 }
2057 auto task = [quality](const RefPtr<CanvasTaskPool>& pool) { pool->UpdateSmoothingQuality(quality); };
2058 PushTaskToPage(ctx, value, task);
2059 JS_SetPropertyStr(ctx, value, "__imageSmoothingQuality", JS_DupValue(ctx, proto));
2060 return JS_NULL;
2061 }
2062
JsWidthGetter(JSContext * ctx,JSValueConst value)2063 JSValue CanvasBridge::JsWidthGetter(JSContext* ctx, JSValueConst value)
2064 {
2065 NodeId id = GetCurrentNodeId(ctx, value);
2066 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
2067 auto page = instance->GetRunningPage();
2068 if (!page) {
2069 return JS_NULL;
2070 }
2071
2072 double width = 0.0;
2073 auto task = [id, page, &width]() {
2074 auto canvas = AceType::DynamicCast<DOMCanvas>(page->GetDomDocument()->GetDOMNodeById(id));
2075 if (!canvas) {
2076 return;
2077 }
2078 auto paintChild = AceType::DynamicCast<CustomPaintComponent>(canvas->GetSpecializedComponent());
2079 if (!paintChild) {
2080 return;
2081 }
2082 auto canvasTask = paintChild->GetTaskPool();
2083 if (!canvasTask) {
2084 return;
2085 }
2086 width = canvasTask->GetWidth();
2087 };
2088 instance->GetDelegate()->PostSyncTaskToPage(task);
2089
2090 return JS_NewFloat64(ctx, width);
2091 }
2092
JsHeightGetter(JSContext * ctx,JSValueConst value)2093 JSValue CanvasBridge::JsHeightGetter(JSContext* ctx, JSValueConst value)
2094 {
2095 NodeId id = GetCurrentNodeId(ctx, value);
2096 auto instance = static_cast<QjsEngineInstance*>(JS_GetContextOpaque(ctx));
2097 auto page = instance->GetRunningPage();
2098 if (!page) {
2099 return JS_NULL;
2100 }
2101
2102 double height = 0.0;
2103 auto task = [id, page, &height]() {
2104 auto canvas = AceType::DynamicCast<DOMCanvas>(page->GetDomDocument()->GetDOMNodeById(id));
2105 if (!canvas) {
2106 return;
2107 }
2108 auto paintChild = AceType::DynamicCast<CustomPaintComponent>(canvas->GetSpecializedComponent());
2109 if (!paintChild) {
2110 return;
2111 }
2112 auto canvasTask = paintChild->GetTaskPool();
2113 if (!canvasTask) {
2114 return;
2115 }
2116 height = canvasTask->GetHeight();
2117 };
2118 instance->GetDelegate()->PostSyncTaskToPage(task);
2119
2120 return JS_NewFloat64(ctx, height);
2121 }
2122
2123 } // namespace OHOS::Ace::Framework
2124