• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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