• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "bridge/declarative_frontend/jsview/canvas/js_canvas_renderer.h"
17 
18 #include <cstdint>
19 #include <memory>
20 
21 #include "base/utils/utils.h"
22 #include "bridge/common/utils/engine_helper.h"
23 #include "bridge/declarative_frontend/engine/js_converter.h"
24 #include "bridge/declarative_frontend/engine/jsi/jsi_types.h"
25 #include "bridge/declarative_frontend/jsview/canvas/js_canvas_pattern.h"
26 #include "bridge/declarative_frontend/jsview/canvas/js_offscreen_rendering_context.h"
27 #include "bridge/declarative_frontend/jsview/js_utils.h"
28 #include "core/components/common/properties/paint_state.h"
29 #include "core/pipeline/pipeline_base.h"
30 #include "core/pipeline/pipeline_context.h"
31 
32 #ifdef PIXEL_MAP_SUPPORTED
33 #include "pixel_map.h"
34 #include "pixel_map_napi.h"
35 #endif
36 
37 namespace OHOS::Ace {
38 constexpr uint32_t PIXEL_SIZE = 4;
39 constexpr int32_t ALPHA_INDEX = 3;
40 } // namespace OHOS::Ace
41 
42 namespace OHOS::Ace::Framework {
43 std::unordered_map<int32_t, std::shared_ptr<Pattern>> JSCanvasRenderer::pattern_;
44 unsigned int JSCanvasRenderer::patternCount_ = 0;
45 namespace {
46 
47 const std::set<std::string> FONT_WEIGHTS = { "normal", "bold", "lighter", "bolder", "100", "200", "300", "400", "500",
48     "600", "700", "800", "900" };
49 const std::set<std::string> FONT_STYLES = { "italic", "oblique", "normal" };
50 const std::set<std::string> FONT_FAMILIES = { "sans-serif", "serif", "monospace" };
51 const std::set<std::string> QUALITY_TYPE = { "low", "medium", "high" }; // Default value is low.
52 constexpr double DEFAULT_QUALITY = 0.92;
53 constexpr uint32_t COLOR_ALPHA_OFFSET = 24;
54 constexpr uint32_t COLOR_ALPHA_VALUE = 0xFF000000;
55 constexpr double DIFF = 1e-10;
56 template<typename T>
ConvertStrToEnum(const char * key,const LinearMapNode<T> * map,size_t length,T defaultValue)57 inline T ConvertStrToEnum(const char* key, const LinearMapNode<T>* map, size_t length, T defaultValue)
58 {
59     int64_t index = BinarySearchFindIndex(map, length, key);
60     return index != -1 ? map[index].value : defaultValue;
61 }
62 
ParseJsDoubleArray(const JSRef<JSVal> & jsValue,std::vector<double> & result)63 inline bool ParseJsDoubleArray(const JSRef<JSVal>& jsValue, std::vector<double>& result)
64 {
65     if (jsValue->IsArray()) {
66         JSRef<JSArray> array = JSRef<JSArray>::Cast(jsValue);
67         for (size_t i = 0; i < array->Length(); i++) {
68             JSRef<JSVal> value = array->GetValueAt(i);
69             if (value->IsNumber()) {
70                 result.emplace_back(value->ToNumber<double>());
71             } else if (value->IsObject()) {
72                 double singleResInt;
73                 if (JSViewAbstract::ParseJsDouble(value, singleResInt)) {
74                     result.emplace_back(singleResInt);
75                 } else {
76                     return false;
77                 }
78             } else {
79                 return false;
80             }
81         }
82         return true;
83     }
84     return false;
85 }
86 
87 const LinearMapNode<TextBaseline> BASELINE_TABLE[] = {
88     { "alphabetic", TextBaseline::ALPHABETIC },
89     { "bottom", TextBaseline::BOTTOM },
90     { "hanging", TextBaseline::HANGING },
91     { "ideographic", TextBaseline::IDEOGRAPHIC },
92     { "middle", TextBaseline::MIDDLE },
93     { "top", TextBaseline::TOP },
94 };
95 
ColorAlphaAdapt(uint32_t origin)96 uint32_t ColorAlphaAdapt(uint32_t origin)
97 {
98     uint32_t result = origin;
99     if ((origin >> COLOR_ALPHA_OFFSET) == 0) {
100         result = origin | COLOR_ALPHA_VALUE;
101     }
102     return result;
103 }
104 
105 } // namespace
106 
JSCanvasRenderer()107 JSCanvasRenderer::JSCanvasRenderer()
108 {
109     SetInstanceId(Container::CurrentIdSafely());
110     density_ = PipelineBase::GetCurrentDensity();
111     auto pipeline = PipelineBase::GetCurrentContextSafely();
112     if (pipeline) {
113         densityCallbackId_ = pipeline->RegisterDensityChangedCallback([self = WeakClaim(this)](double density) {
114             auto canvasRender = self.Upgrade();
115             CHECK_NULL_VOID(canvasRender);
116             canvasRender->density_ = density;
117             canvasRender->SetDensity();
118         });
119     }
120 }
121 
~JSCanvasRenderer()122 JSCanvasRenderer::~JSCanvasRenderer()
123 {
124     ContainerScope scope(instanceId_);
125     auto pipeline = PipelineBase::GetCurrentContextSafely();
126     if (pipeline) {
127         pipeline->UnregisterDensityChangedCallback(densityCallbackId_);
128     }
129 }
130 
131 // A helper fucntion to create GradientObj
createGradientObj(const std::shared_ptr<Gradient> & gradient)132 JSRef<JSObject> JSCanvasRenderer::createGradientObj(const std::shared_ptr<Gradient>& gradient)
133 {
134     JSRef<JSObject> pasteObj = JSClass<JSCanvasGradient>::NewInstance();
135     pasteObj->SetProperty("__type", "gradient");
136     auto pasteData = Referenced::Claim(pasteObj->Unwrap<JSCanvasGradient>());
137     pasteData->SetGradient(gradient);
138     return pasteObj;
139 }
140 
141 // createLinearGradient(x0: number, y0: number, x1: number, y1: number): CanvasGradient
JsCreateLinearGradient(const JSCallbackInfo & info)142 void JSCanvasRenderer::JsCreateLinearGradient(const JSCallbackInfo& info)
143 {
144     double x0 = 0.0;
145     double y0 = 0.0;
146     double x1 = 0.0;
147     double y1 = 0.0;
148     if (info.GetDoubleArg(0, x0) && info.GetDoubleArg(1, y0) && info.GetDoubleArg(2, x1) && info.GetDoubleArg(3, y1)) {
149         double density = GetDensity();
150         auto gradient = std::make_shared<Gradient>();
151         gradient->SetType(GradientType::LINEAR);
152         gradient->SetBeginOffset(Offset(x0 * density, y0 * density));
153         gradient->SetEndOffset(Offset(x1 * density, y1 * density));
154         JSRef<JSObject> pasteObj = createGradientObj(gradient);
155         info.SetReturnValue(pasteObj);
156     }
157 }
158 
159 // createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number, r1: number): CanvasGradient
JsCreateRadialGradient(const JSCallbackInfo & info)160 void JSCanvasRenderer::JsCreateRadialGradient(const JSCallbackInfo& info)
161 {
162     double startX = 0.0;
163     double startY = 0.0;
164     double startRadial = 0.0;
165     double endX = 0.0;
166     double endY = 0.0;
167     double endRadial = 0.0;
168     if (info.GetDoubleArg(0, startX) && info.GetDoubleArg(1, startY) && info.GetDoubleArg(2, startRadial) &&
169         info.GetDoubleArg(3, endX) && info.GetDoubleArg(4, endY) && info.GetDoubleArg(5, endRadial)) {
170         double density = GetDensity();
171         auto gradient = std::make_shared<Gradient>();
172         gradient->SetType(GradientType::RADIAL);
173         gradient->SetBeginOffset(Offset(startX * density, startY * density));
174         gradient->SetEndOffset(Offset(endX * density, endY * density));
175         gradient->SetInnerRadius(startRadial * density);
176         gradient->SetOuterRadius(endRadial * density);
177         JSRef<JSObject> pasteObj = createGradientObj(gradient);
178         info.SetReturnValue(pasteObj);
179     }
180 }
181 
182 // createConicGradient(startAngle: number, x: number, y: number): CanvasGradient
JsCreateConicGradient(const JSCallbackInfo & info)183 void JSCanvasRenderer::JsCreateConicGradient(const JSCallbackInfo& info)
184 {
185     if (info.Length() != 3) {
186         return;
187     }
188     // in radian
189     double startAngle = 0.0;
190     double x = 0.0;
191     double y = 0.0;
192     info.GetDoubleArg(0, startAngle);
193     info.GetDoubleArg(1, x);
194     info.GetDoubleArg(2, y);
195     double density = GetDensity();
196     auto gradient = std::make_shared<Gradient>();
197     gradient->SetType(GradientType::CONIC);
198     gradient->GetConicGradient().startAngle = AnimatableDimension(Dimension(fmod(startAngle, (2 * M_PI))));
199     gradient->GetConicGradient().centerX = AnimatableDimension(Dimension(x * density));
200     gradient->GetConicGradient().centerY = AnimatableDimension(Dimension(y * density));
201     JSRef<JSObject> pasteObj = createGradientObj(gradient);
202     info.SetReturnValue(pasteObj);
203 }
204 
205 // fillText(text: string, x: number, y: number, maxWidth?: number): void
JsFillText(const JSCallbackInfo & info)206 void JSCanvasRenderer::JsFillText(const JSCallbackInfo& info)
207 {
208     FillTextInfo textInfo;
209     if (info.GetStringArg(0, textInfo.text) && info.GetDoubleArg(1, textInfo.x) && info.GetDoubleArg(2, textInfo.y)) {
210         double density = GetDensity();
211         textInfo.x *= density;
212         textInfo.y *= density;
213         if (info.Length() >= 4) {
214             double maxWidth = 0.0;
215             if (info.GetDoubleArg(3, maxWidth)) { // Parse the 3rd parameter.
216                 maxWidth *= density;
217             } else if (info[3]->IsUndefined()) { // Is the 3rd parameter undefined.
218                 maxWidth = FLT_MAX;
219             }
220             if (maxWidth < 0) {
221                 return;
222             }
223             textInfo.maxWidth = maxWidth;
224         }
225         renderingContext2DModel_->SetFillText(paintState_, textInfo);
226     }
227 }
228 
229 // strokeText(text: string, x: number, y: number, maxWidth?:number): void
JsStrokeText(const JSCallbackInfo & info)230 void JSCanvasRenderer::JsStrokeText(const JSCallbackInfo& info)
231 {
232     FillTextInfo textInfo;
233     double density = GetDensity();
234     if (info.GetStringArg(0, textInfo.text) && info.GetDoubleArg(1, textInfo.x) && info.GetDoubleArg(2, textInfo.y)) {
235         textInfo.x *= density;
236         textInfo.y *= density;
237         if (info.Length() >= 4) {
238             double maxWidth = 0.0;
239             if (info.GetDoubleArg(3, maxWidth)) { // Parse the 3rd parameter.
240                 maxWidth *= density;
241             } else if (info[3]->IsUndefined()) { // Is the 3rd parameter undefined.
242                 maxWidth = FLT_MAX;
243             }
244             if (maxWidth < 0) {
245                 return;
246             }
247             textInfo.maxWidth = maxWidth;
248         }
249         renderingContext2DModel_->SetStrokeText(paintState_, textInfo);
250     }
251 }
252 
SetAntiAlias()253 void JSCanvasRenderer::SetAntiAlias()
254 {
255     renderingContext2DModel_->SetAntiAlias(anti_);
256 }
257 
SetDensity()258 void JSCanvasRenderer::SetDensity()
259 {
260     double density = GetDensity(true);
261     renderingContext2DModel_->SetDensity(density);
262 }
263 
264 // font: string
JsSetFont(const JSCallbackInfo & info)265 void JSCanvasRenderer::JsSetFont(const JSCallbackInfo& info)
266 {
267     std::string fontStr;
268     if (!info.GetStringArg(0, fontStr)) {
269         return;
270     }
271     std::vector<std::string> fontProps;
272     StringUtils::StringSplitter(fontStr.c_str(), ' ', fontProps);
273     bool updateFontweight = false;
274     bool updateFontStyle = false;
275     for (const auto& fontProp : fontProps) {
276         if (FONT_WEIGHTS.find(fontProp) != FONT_WEIGHTS.end()) {
277             updateFontweight = true;
278             auto weight = ConvertStrToFontWeight(fontProp);
279             paintState_.SetFontWeight(weight);
280             renderingContext2DModel_->SetFontWeight(weight);
281         } else if (FONT_STYLES.find(fontProp) != FONT_STYLES.end()) {
282             updateFontStyle = true;
283             auto fontStyle = ConvertStrToFontStyle(fontProp);
284             paintState_.SetFontStyle(fontStyle);
285             renderingContext2DModel_->SetFontStyle(fontStyle);
286         } else if (FONT_FAMILIES.find(fontProp) != FONT_FAMILIES.end()) {
287             auto families = ConvertStrToFontFamilies(fontProp);
288             paintState_.SetFontFamilies(families);
289             renderingContext2DModel_->SetFontFamilies(families);
290         } else if (fontProp.find("px") != std::string::npos || fontProp.find("vp") != std::string::npos) {
291             Dimension size;
292             if (fontProp.find("vp") != std::string::npos) {
293                 size = GetDimensionValue(fontProp);
294             } else {
295                 std::string fontSize = fontProp.substr(0, fontProp.size() - 2);
296                 size = Dimension(StringToDouble(fontProp));
297             }
298             paintState_.SetFontSize(size);
299             renderingContext2DModel_->SetFontSize(size);
300         }
301     }
302     if (!updateFontStyle) {
303         renderingContext2DModel_->SetFontStyle(FontStyle::NORMAL);
304     }
305     if (!updateFontweight) {
306         renderingContext2DModel_->SetFontWeight(FontWeight::NORMAL);
307     }
308 }
309 
310 // getLineDash(): number[]
JsGetLineDash(const JSCallbackInfo & info)311 void JSCanvasRenderer::JsGetLineDash(const JSCallbackInfo& info)
312 {
313     std::vector<double> lineDash = renderingContext2DModel_->GetLineDash();
314     JSRef<JSArray> lineDashObj = JSRef<JSArray>::New();
315     double density = GetDensity();
316     for (auto i = 0U; i < lineDash.size(); i++) {
317         lineDashObj->SetValueAt(i, JSRef<JSVal>::Make(ToJSValue(lineDash[i] / density)));
318     }
319     info.SetReturnValue(lineDashObj);
320 }
321 
322 // fillStyle: string | number | CanvasGradient | CanvasPattern
JsSetFillStyle(const JSCallbackInfo & info)323 void JSCanvasRenderer::JsSetFillStyle(const JSCallbackInfo& info)
324 {
325     Color color;
326     std::string colorStr;
327     if (info.GetStringArg(0, colorStr) && Color::ParseColorString(colorStr, color)) {
328         renderingContext2DModel_->SetFillColor(color, true);
329         return;
330     }
331 
332     uint32_t colorNum;
333     if (info.GetUint32Arg(0, colorNum)) {
334         renderingContext2DModel_->SetFillColor(Color(ColorAlphaAdapt(colorNum)), false);
335         return;
336     }
337 
338     if (!info[0]->IsObject()) {
339         return;
340     }
341     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
342     JSRef<JSVal> typeValue = obj->GetProperty("__type");
343     std::string type;
344     JSViewAbstract::ParseJsString(typeValue, type);
345     if (type == "gradient") {
346         auto* jSCanvasGradient = info.UnwrapArg<JSCanvasGradient>(0);
347         CHECK_NULL_VOID(jSCanvasGradient);
348         auto gradient = jSCanvasGradient->GetGradient();
349         CHECK_NULL_VOID(gradient);
350         renderingContext2DModel_->SetFillGradient(gradient);
351     } else if (type == "pattern") {
352         auto* jSCanvasPattern = info.UnwrapArg<JSCanvasPattern>(0);
353         CHECK_NULL_VOID(jSCanvasPattern);
354         int32_t id = jSCanvasPattern->GetId();
355         renderingContext2DModel_->SetFillPattern(GetPatternPtr(id));
356     }
357 }
358 
359 // strokeStyle: string | number | CanvasGradient | CanvasPattern
JsSetStrokeStyle(const JSCallbackInfo & info)360 void JSCanvasRenderer::JsSetStrokeStyle(const JSCallbackInfo& info)
361 {
362     Color color;
363     std::string colorStr;
364     if (info.GetStringArg(0, colorStr) && Color::ParseColorString(colorStr, color)) {
365         renderingContext2DModel_->SetStrokeColor(color, true);
366         return;
367     }
368 
369     uint32_t colorNum;
370     if (info.GetUint32Arg(0, colorNum)) {
371         renderingContext2DModel_->SetStrokeColor(Color(ColorAlphaAdapt(colorNum)), false);
372         return;
373     }
374 
375     if (!info[0]->IsObject()) {
376         return;
377     }
378     JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
379     JSRef<JSVal> typeValue = obj->GetProperty("__type");
380     std::string type;
381     JSViewAbstract::ParseJsString(typeValue, type);
382     if (type == "gradient") {
383         auto* jSCanvasGradient = info.UnwrapArg<JSCanvasGradient>(0);
384         CHECK_NULL_VOID(jSCanvasGradient);
385         auto gradient = jSCanvasGradient->GetGradient();
386         CHECK_NULL_VOID(gradient);
387         renderingContext2DModel_->SetStrokeGradient(gradient);
388     } else if (type == "pattern") {
389         auto* jSCanvasPattern = info.UnwrapArg<JSCanvasPattern>(0);
390         CHECK_NULL_VOID(jSCanvasPattern);
391         int32_t id = jSCanvasPattern->GetId();
392         renderingContext2DModel_->SetStrokePattern(GetPatternPtr(id));
393     }
394 }
395 
JsMakePath2D(const JSCallbackInfo & info)396 RefPtr<CanvasPath2D> JSCanvasRenderer::JsMakePath2D(const JSCallbackInfo& info)
397 {
398     if (info.Length() >= 1) {
399         if (info[0]->IsString()) {
400             std::string capStr = "";
401             JSViewAbstract::ParseJsString(info[0], capStr);
402             return AceType::MakeRefPtr<CanvasPath2D>(capStr);
403         }
404 
405         if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE) && info[0]->IsObject()) {
406             auto* jsPath2d = info.UnwrapArg<JSPath2D>(0);
407             CHECK_NULL_RETURN(jsPath2d, AceType::MakeRefPtr<CanvasPath2D>());
408             auto canvasPath2D = jsPath2d->GetCanvasPath2d();
409             return AceType::MakeRefPtr<CanvasPath2D>(canvasPath2D);
410         }
411     }
412     // Example: ctx.createPath2D()
413     return AceType::MakeRefPtr<CanvasPath2D>();
414 }
415 
UnwrapNapiImage(const EcmaVM * vm,const JSRef<JSObject> jsObject)416 JSRenderImage* JSCanvasRenderer::UnwrapNapiImage(const EcmaVM* vm, const JSRef<JSObject> jsObject)
417 {
418     ContainerScope scope(instanceId_);
419     CHECK_NULL_RETURN(vm, nullptr);
420     panda::Local<JsiValue> value = jsObject.Get().GetLocalHandle();
421     JSValueWrapper valueWrapper = value;
422     Global<JSValueRef> arkValue = valueWrapper;
423     napi_value napiValue = reinterpret_cast<napi_value>(*arkValue.ToLocal(vm));
424     panda::Local<panda::JSValueRef> nativeValue(reinterpret_cast<uintptr_t>(napiValue));
425     auto nativeObject = nativeValue->ToObject(vm);
426 
427     napi_value isImageBitmap = nullptr;
428     Local<panda::StringRef> keyType = panda::StringRef::NewFromUtf8(vm, "isImageBitmap");
429     Local<panda::JSValueRef> valueType = nativeObject->Get(vm, keyType);
430     isImageBitmap = reinterpret_cast<napi_value>(*valueType);
431     if (isImageBitmap == nullptr) {
432         return nullptr;
433     }
434     int32_t type = 0;
435     panda::Local<panda::JSValueRef> localType(reinterpret_cast<uintptr_t>(isImageBitmap));
436     type = localType->Int32Value(vm);
437     if (!type) {
438         return nullptr;
439     }
440 
441     JSRenderImage* jsImage = nullptr;
442     Local<panda::StringRef> keyObj = panda::StringRef::GetNapiWrapperString(vm);
443     Local<panda::JSValueRef> valObj = nativeObject->Get(vm, keyObj);
444     if (valObj->IsObject(vm)) {
445         Local<panda::ObjectRef> ext(valObj);
446         auto ref = reinterpret_cast<NativeReference*>(ext->GetNativePointerField(vm, 0));
447         jsImage = ref != nullptr ? reinterpret_cast<JSRenderImage*>(ref->GetData()) : nullptr;
448     }
449     return jsImage;
450 }
451 
DrawSvgImage(const JSCallbackInfo & info,JSRenderImage * jsImage)452 void JSCanvasRenderer::DrawSvgImage(const JSCallbackInfo& info, JSRenderImage* jsImage)
453 {
454     CanvasImage image;
455     ExtractInfoToImage(image, info, true);
456     image.instanceId = jsImage->GetInstanceId();
457 
458     ImageInfo imageInfo;
459     imageInfo.image = image;
460     imageInfo.svgDom = jsImage->GetSvgDom();
461     CHECK_NULL_VOID(imageInfo.svgDom);
462     imageInfo.imageFit = jsImage->GetImageFit();
463     renderingContext2DModel_->DrawSvgImage(imageInfo);
464 }
465 
DrawImage(const JSCallbackInfo & info,JSRenderImage * jsImage)466 void JSCanvasRenderer::DrawImage(const JSCallbackInfo& info, JSRenderImage* jsImage)
467 {
468     CanvasImage image;
469     ExtractInfoToImage(image, info, true);
470     image.instanceId = jsImage->GetInstanceId();
471     ImageInfo imageInfo;
472 
473 #if !defined(PREVIEW)
474     imageInfo.pixelMap = jsImage->GetPixelMap();
475     CHECK_NULL_VOID(imageInfo.pixelMap);
476     imageInfo.image = image;
477     renderingContext2DModel_->DrawPixelMap(imageInfo);
478 #else
479     image.src = jsImage->GetSrc();
480     image.imageData = jsImage->GetImageData();
481     imageInfo.image = image;
482     imageInfo.imgWidth = jsImage->GetWidth();
483     imageInfo.imgHeight = jsImage->GetHeight();
484     renderingContext2DModel_->DrawImage(imageInfo);
485 #endif
486 }
487 
DrawPixelMap(const JSCallbackInfo & info)488 void JSCanvasRenderer::DrawPixelMap(const JSCallbackInfo& info)
489 {
490 #if !defined(PREVIEW)
491     CanvasImage image;
492     ExtractInfoToImage(image, info, false);
493 
494     ImageInfo imageInfo;
495     imageInfo.image = image;
496     auto runtime = std::static_pointer_cast<ArkJSRuntime>(JsiDeclarativeEngineInstance::GetCurrentRuntime());
497     CHECK_NULL_VOID(runtime);
498     imageInfo.pixelMap = CreatePixelMapFromNapiValue(info[0], runtime->GetNativeEngine());
499     CHECK_NULL_VOID(imageInfo.pixelMap);
500     renderingContext2DModel_->DrawPixelMap(imageInfo);
501 #endif
502 }
503 
504 // drawImage(image: ImageBitmap | PixelMap, dx: number, dy: number): void
505 // drawImage(image: ImageBitmap | PixelMap, dx: number, dy: number, dw: number, dh: number): void
506 // drawImage(image: ImageBitmap | PixelMap, sx: number, sy: number, sw: number, sh: number, dx: number,
507 //           dy: number, dw: number, dh: number):void
JsDrawImage(const JSCallbackInfo & info)508 void JSCanvasRenderer::JsDrawImage(const JSCallbackInfo& info)
509 {
510     ContainerScope scope(instanceId_);
511     if (!info[0]->IsObject()) {
512         return;
513     }
514     auto* jsImage = UnwrapNapiImage(info.GetVm(), info[0]);
515     if (jsImage) {
516         if (jsImage->IsSvg()) {
517             DrawSvgImage(info, jsImage);
518         } else {
519             DrawImage(info, jsImage);
520         }
521     } else {
522         DrawPixelMap(info);
523     }
524 }
525 
ExtractInfoToImage(CanvasImage & image,const JSCallbackInfo & info,bool isImage)526 void JSCanvasRenderer::ExtractInfoToImage(CanvasImage& image, const JSCallbackInfo& info, bool isImage)
527 {
528     double density = GetDensity();
529     switch (info.Length()) {
530         case 3:
531             image.flag = 0;
532             info.GetDoubleArg(1, image.dx);
533             info.GetDoubleArg(2, image.dy);
534             image.dx *= density;
535             image.dy *= density;
536             break;
537         // 5 parameters: drawImage(image, dx, dy, dWidth, dHeight)
538         case 5:
539             image.flag = 1;
540             info.GetDoubleArg(1, image.dx);
541             info.GetDoubleArg(2, image.dy);
542             info.GetDoubleArg(3, image.dWidth);
543             info.GetDoubleArg(4, image.dHeight);
544             image.dx *= density;
545             image.dy *= density;
546             image.dWidth *= density;
547             image.dHeight *= density;
548             break;
549         // 9 parameters: drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
550         case 9:
551             image.flag = 2;
552             info.GetDoubleArg(1, image.sx);
553             info.GetDoubleArg(2, image.sy);
554             info.GetDoubleArg(3, image.sWidth);
555             info.GetDoubleArg(4, image.sHeight);
556             info.GetDoubleArg(5, image.dx);
557             info.GetDoubleArg(6, image.dy);
558             info.GetDoubleArg(7, image.dWidth);
559             info.GetDoubleArg(8, image.dHeight);
560             if (isImage) {
561                 image.sx *= density;
562                 image.sy *= density;
563                 image.sWidth *= density;
564                 image.sHeight *= density;
565             }
566             image.dx *= density;
567             image.dy *= density;
568             image.dWidth *= density;
569             image.dHeight *= density;
570             break;
571         default:
572             break;
573     }
574 }
575 
576 // createPattern(image: ImageBitmap, repetition: string | null): CanvasPattern | null
JsCreatePattern(const JSCallbackInfo & info)577 void JSCanvasRenderer::JsCreatePattern(const JSCallbackInfo& info)
578 {
579     auto arg0 = info[0];
580     if (arg0->IsObject()) {
581         auto* jsImage = UnwrapNapiImage(info.GetVm(), info[0]);
582         CHECK_NULL_VOID(jsImage);
583         std::string repeat;
584         info.GetStringArg(1, repeat);
585         auto pattern = std::make_shared<Pattern>();
586         pattern->SetImgSrc(jsImage->GetSrc());
587         pattern->SetImageWidth(jsImage->GetWidth());
588         pattern->SetImageHeight(jsImage->GetHeight());
589         pattern->SetRepetition(repeat);
590 #if !defined(PREVIEW)
591         auto pixelMap = jsImage->GetPixelMap();
592         pattern->SetPixelMap(pixelMap);
593 #endif
594         pattern_[patternCount_] = pattern;
595 
596         JSRef<JSObject> obj = JSClass<JSCanvasPattern>::NewInstance();
597         obj->SetProperty("__type", "pattern");
598         auto canvasPattern = Referenced::Claim(obj->Unwrap<JSCanvasPattern>());
599         canvasPattern->SetCanvasRenderer(AceType::WeakClaim(this));
600         canvasPattern->SetId(patternCount_);
601         canvasPattern->SetUnit(GetUnit());
602         patternCount_++;
603         info.SetReturnValue(obj);
604     }
605 }
606 
607 // createImageData(sw: number, sh: number): ImageData
608 // createImageData(imageData: ImageData): ImageData
JsCreateImageData(const JSCallbackInfo & info)609 void JSCanvasRenderer::JsCreateImageData(const JSCallbackInfo& info)
610 {
611     double density = GetDensity();
612     double fWidth = 0.0;
613     double fHeight = 0.0;
614     auto retObj = JSRef<JSObject>::New();
615     info.SetReturnValue(retObj);
616     if (info.Length() == 2) {
617         info.GetDoubleArg(0, fWidth);
618         info.GetDoubleArg(1, fHeight);
619         fWidth *= density;
620         fHeight *= density;
621     } else if (info.Length() == 1 && info[0]->IsObject()) {
622         JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
623         JSRef<JSVal> widthValue = obj->GetProperty("width");
624         JSRef<JSVal> heightValue = obj->GetProperty("height");
625         JSViewAbstract::ParseJsDouble(widthValue, fWidth);
626         JSViewAbstract::ParseJsDouble(heightValue, fHeight);
627     }
628     uint32_t finalWidth = static_cast<uint32_t>(std::abs(fWidth + DIFF));
629     uint32_t finalHeight = static_cast<uint32_t>(std::abs(fHeight + DIFF));
630     JSRef<JSArrayBuffer> arrayBuffer = JSRef<JSArrayBuffer>::New(finalWidth * finalHeight * PIXEL_SIZE);
631     // return the black image
632     auto* buffer = static_cast<uint32_t*>(arrayBuffer->GetBuffer());
633     // Height or Width is ZERO or Overflow.
634     if (!buffer || (finalHeight > 0 && finalWidth > (UINT32_MAX / finalHeight))) {
635         JSRef<JSArrayBuffer> zeroArrayBuffer = JSRef<JSArrayBuffer>::New(0);
636         auto zeroColorArray =
637             JSRef<JSUint8ClampedArray>::New(zeroArrayBuffer->GetLocalHandle(), 0, zeroArrayBuffer->ByteLength());
638         retObj->SetProperty("width", 0);
639         retObj->SetProperty("height", 0);
640         retObj->SetPropertyObject("data", zeroColorArray);
641         return;
642     }
643     for (uint32_t idx = 0; idx < finalWidth * finalHeight; ++idx) {
644         buffer[idx] = 0xffffffff;
645     }
646 
647     JSRef<JSUint8ClampedArray> colorArray =
648         JSRef<JSUint8ClampedArray>::New(arrayBuffer->GetLocalHandle(), 0, arrayBuffer->ByteLength());
649     retObj->SetProperty("width", finalWidth);
650     retObj->SetProperty("height", finalHeight);
651     retObj->SetPropertyObject("data", colorArray);
652 }
653 
654 // putImageData(imageData: ImageData, dx: number | string, dy: number | string): void
655 // putImageData(imageData: ImageData, dx: number | string, dy: number | string, dirtyX: number | string,
656 //              dirtyY: number | string, dirtyWidth: number | string, dirtyHeight: number | string): void
JsPutImageData(const JSCallbackInfo & info)657 void JSCanvasRenderer::JsPutImageData(const JSCallbackInfo& info)
658 {
659     auto arg0 = info[0]; // store the variable to avoid it being constructed multiple time.
660     if (!arg0->IsObject()) {
661         return;
662     }
663     // Parse the first parameter with type ImageData
664     JSRef<JSObject> jsImageData = JSRef<JSObject>::Cast(arg0);
665     JSRef<JSVal> jsImgWidth = jsImageData->GetProperty("width");
666     JSRef<JSVal> jsImgHeight = jsImageData->GetProperty("height");
667     JSRef<JSVal> jsImgData = jsImageData->GetProperty("data");
668     if ((!jsImgWidth->IsNumber()) || (!jsImgHeight->IsNumber()) || (!jsImgData->IsUint8ClampedArray())) {
669         return;
670     }
671     int32_t imgWidth = jsImgWidth->ToNumber<int32_t>();
672     int32_t imgHeight = jsImgHeight->ToNumber<int32_t>();
673 
674     // Parse other parameters
675     ImageData imageData = { .dirtyWidth = imgWidth, .dirtyHeight = imgHeight };
676     ParseImageData(info, imageData);
677     imageData.dirtyWidth = imageData.dirtyX < 0 ? std::min(imageData.dirtyX + imageData.dirtyWidth, imgWidth)
678                                                 : std::min(imgWidth - imageData.dirtyX, imageData.dirtyWidth);
679     imageData.dirtyHeight = imageData.dirtyY < 0 ? std::min(imageData.dirtyY + imageData.dirtyHeight, imgHeight)
680                                                  : std::min(imgHeight - imageData.dirtyY, imageData.dirtyHeight);
681 
682     // copy the data from the image data.
683     JSRef<JSUint8ClampedArray> colorArray = JSRef<JSUint8ClampedArray>::Cast(jsImgData);
684     auto arrayBuffer = colorArray->GetArrayBuffer();
685     auto* buffer = static_cast<uint8_t*>(arrayBuffer->GetBuffer());
686     CHECK_NULL_VOID(buffer);
687     int32_t bufferLength = arrayBuffer->ByteLength();
688     imageData.data = std::vector<uint32_t>();
689     for (int32_t i = std::max(imageData.dirtyY, 0); i < imageData.dirtyY + imageData.dirtyHeight; ++i) {
690         for (int32_t j = std::max(imageData.dirtyX, 0); j < imageData.dirtyX + imageData.dirtyWidth; ++j) {
691             uint32_t idx = static_cast<uint32_t>(4 * (j + imgWidth * i));
692             if (bufferLength > static_cast<int32_t>(idx + ALPHA_INDEX)) {
693                 uint8_t alpha = buffer[idx + 3]; // idx + 3: The 4th byte format: alpha
694                 uint8_t red = buffer[idx]; // idx: the 1st byte format: red
695                 uint8_t green = buffer[idx + 1]; // idx + 1: The 2nd byte format: green
696                 uint8_t blue = buffer[idx + 2]; // idx + 2: The 3rd byte format: blue
697                 imageData.data.emplace_back(Color::FromARGB(alpha, red, green, blue).GetValue());
698             }
699         }
700     }
701 
702     renderingContext2DModel_->PutImageData(imageData);
703 }
704 
ParseImageData(const JSCallbackInfo & info,ImageData & imageData)705 void JSCanvasRenderer::ParseImageData(const JSCallbackInfo& info, ImageData& imageData)
706 {
707     double density = GetDensity();
708     std::string argStr;
709     if (info.GetStringArg(1, argStr)) {
710         imageData.x = static_cast<int32_t>(GetDimensionValue(argStr).Value());
711     } else if (info.GetInt32Arg(1, imageData.x)) {
712         imageData.x = static_cast<int32_t>(imageData.x * density);
713     }
714 
715     if (info.GetStringArg(2, argStr)) {
716         imageData.y = static_cast<int32_t>(GetDimensionValue(argStr).Value());
717     } else if (info.GetInt32Arg(2, imageData.y)) {
718         imageData.y = static_cast<int32_t>(imageData.y * density);
719     }
720     if (info.Length() != 7) {
721         return;
722     }
723     if (info.GetStringArg(3, argStr)) {
724         imageData.dirtyX = static_cast<int32_t>(GetDimensionValue(argStr).Value());
725     } else if (info.GetInt32Arg(3, imageData.dirtyX)) {
726         imageData.dirtyX = static_cast<int32_t>(imageData.dirtyX * density);
727     }
728 
729     if (info.GetStringArg(4, argStr)) {
730         imageData.dirtyY = static_cast<int32_t>(GetDimensionValue(argStr).Value());
731     } else if (info.GetInt32Arg(4, imageData.dirtyY)) {
732         imageData.dirtyY = static_cast<int32_t>(imageData.dirtyY * density);
733     }
734 
735     if (info.GetStringArg(5, argStr)) {
736         imageData.dirtyWidth = static_cast<int32_t>(GetDimensionValue(argStr).Value());
737     } else if (info.GetInt32Arg(5, imageData.dirtyWidth)) {
738         imageData.dirtyWidth = static_cast<int32_t>(imageData.dirtyWidth * density);
739     }
740 
741     if (info.GetStringArg(6, argStr)) {
742         imageData.dirtyHeight = static_cast<int32_t>(GetDimensionValue(argStr).Value());
743     } else if (info.GetInt32Arg(6, imageData.dirtyHeight)) {
744         imageData.dirtyHeight = static_cast<int32_t>(imageData.dirtyHeight * density);
745     }
746 }
747 
748 // Abandoned
JsCloseImageBitmap(const std::string & src)749 void JSCanvasRenderer::JsCloseImageBitmap(const std::string& src)
750 {
751     renderingContext2DModel_->CloseImageBitmap(src);
752 }
753 
754 // getImageData(sx: number, sy: number, sw: number, sh: number): ImageData
JsGetImageData(const JSCallbackInfo & info)755 void JSCanvasRenderer::JsGetImageData(const JSCallbackInfo& info)
756 {
757     double density = GetDensity();
758     ImageSize imageSize;
759     auto retObj = JSRef<JSObject>::New();
760     info.SetReturnValue(retObj);
761     info.GetDoubleArg(0, imageSize.left);
762     info.GetDoubleArg(1, imageSize.top);
763     info.GetDoubleArg(2, imageSize.width);
764     info.GetDoubleArg(3, imageSize.height);
765     imageSize.left *= density;
766     imageSize.top *= density;
767     imageSize.width = imageSize.width * density + DIFF;
768     imageSize.height = imageSize.height * density + DIFF;
769 
770     uint32_t finalWidth = static_cast<uint32_t>(std::abs(imageSize.width));
771     uint32_t finalHeight = static_cast<uint32_t>(std::abs(imageSize.height));
772     int32_t length = finalHeight * finalWidth * 4;
773     JSRef<JSArrayBuffer> arrayBuffer = JSRef<JSArrayBuffer>::New(length);
774     auto* buffer = static_cast<uint8_t*>(arrayBuffer->GetBuffer());
775     // Height or Width is ZERO or Overflow.
776     if (!buffer || (finalHeight > 0 && finalWidth > (UINT32_MAX / finalHeight))) {
777         JSRef<JSArrayBuffer> zeroArrayBuffer = JSRef<JSArrayBuffer>::New(0);
778         auto zeroColorArray =
779             JSRef<JSUint8ClampedArray>::New(zeroArrayBuffer->GetLocalHandle(), 0, zeroArrayBuffer->ByteLength());
780         retObj->SetProperty("width", 0);
781         retObj->SetProperty("height", 0);
782         retObj->SetPropertyObject("data", zeroColorArray);
783         return;
784     }
785     renderingContext2DModel_->GetImageDataModel(imageSize, buffer);
786     auto colorArray = JSRef<JSUint8ClampedArray>::New(arrayBuffer->GetLocalHandle(), 0, arrayBuffer->ByteLength());
787     retObj->SetProperty("width", finalWidth);
788     retObj->SetProperty("height", finalHeight);
789     retObj->SetPropertyObject("data", colorArray);
790 }
791 
792 // getPixelMap(sx: number, sy: number, sw: number, sh: number): PixelMap
JsGetPixelMap(const JSCallbackInfo & info)793 void JSCanvasRenderer::JsGetPixelMap(const JSCallbackInfo& info)
794 {
795 #ifdef PIXEL_MAP_SUPPORTED
796     double density = GetDensity();
797     ImageSize imageSize;
798     info.GetDoubleArg(0, imageSize.left);
799     info.GetDoubleArg(1, imageSize.top);
800     info.GetDoubleArg(2, imageSize.width);
801     info.GetDoubleArg(3, imageSize.height);
802     imageSize.left *= density;
803     imageSize.top *= density;
804     imageSize.width = imageSize.width * density + DIFF;
805     imageSize.height = imageSize.height * density + DIFF;
806     auto height = static_cast<uint32_t>(std::abs(imageSize.height));
807     auto width = static_cast<uint32_t>(std::abs(imageSize.width));
808     if (height > 0 && width > (UINT32_MAX / height)) {
809         return;
810     }
811     auto pixelmap = renderingContext2DModel_->GetPixelMap(imageSize);
812     CHECK_NULL_VOID(pixelmap);
813 
814     // pixelmap to NapiValue
815     ContainerScope scope(instanceId_);
816     auto runtime = std::static_pointer_cast<ArkJSRuntime>(JsiDeclarativeEngineInstance::GetCurrentRuntime());
817     CHECK_NULL_VOID(runtime);
818     NativeEngine* nativeEngine = runtime->GetNativeEngine();
819     CHECK_NULL_VOID(nativeEngine);
820     napi_env env = reinterpret_cast<napi_env>(nativeEngine);
821     auto pixelmapSharedPtr = pixelmap->GetPixelMapSharedPtr();
822     napi_value napiValue = OHOS::Media::PixelMapNapi::CreatePixelMap(env, pixelmapSharedPtr);
823     auto jsValue = JsConverter::ConvertNapiValueToJsVal(napiValue);
824     info.SetReturnValue(jsValue);
825 #else
826     TAG_LOGI(
827         AceLogTag::ACE_CANVAS, "[Engine Log] The function 'getPixelMap' is not supported on the current platform.");
828 #endif
829 }
830 
831 // setPixelMap(value?: PixelMap): void
JsSetPixelMap(const JSCallbackInfo & info)832 void JSCanvasRenderer::JsSetPixelMap(const JSCallbackInfo& info)
833 {
834     ContainerScope scope(instanceId_);
835 #if !defined(PREVIEW)
836     if (info[0]->IsObject()) {
837         ImageInfo imageInfo;
838         auto runtime = std::static_pointer_cast<ArkJSRuntime>(JsiDeclarativeEngineInstance::GetCurrentRuntime());
839         CHECK_NULL_VOID(runtime);
840         imageInfo.pixelMap = CreatePixelMapFromNapiValue(info[0], runtime->GetNativeEngine());
841         CHECK_NULL_VOID(imageInfo.pixelMap);
842         renderingContext2DModel_->DrawPixelMap(imageInfo);
843     }
844 #else
845     TAG_LOGI(
846         AceLogTag::ACE_CANVAS, "[Engine Log] The function 'setPixelMap' is not supported on the current platform.");
847 #endif
848 }
849 
850 // Abandoned
JsDrawBitmapMesh(const JSCallbackInfo & info)851 void JSCanvasRenderer::JsDrawBitmapMesh(const JSCallbackInfo& info)
852 {
853     ContainerScope scope(instanceId_);
854     RefPtr<AceType> OffscreenPattern;
855 
856     if (info.Length() != 4) {
857         return;
858     }
859 
860     if (info[0]->IsObject()) {
861         uint32_t id = 0;
862         JSRef<JSObject> obj = JSRef<JSObject>::Cast(info[0]);
863         JSRef<JSVal> jsId = obj->GetProperty("__id");
864         JSViewAbstract::ParseJsInteger(jsId, id);
865         OffscreenPattern = JSOffscreenRenderingContext::GetOffscreenPattern(id);
866     } else {
867         return;
868     }
869 
870     std::vector<double> mesh;
871     double column;
872     double row;
873     if (!ParseJsDoubleArray(info[1], mesh)) {
874         return;
875     }
876     if (!JSViewAbstract::ParseJsDouble(info[2], column)) {
877         return;
878     }
879     if (!JSViewAbstract::ParseJsDouble(info[3], row)) {
880         return;
881     }
882 
883     BitmapMeshInfo bitmapMeshInfo;
884     bitmapMeshInfo.offscreenPattern = OffscreenPattern;
885     bitmapMeshInfo.mesh = mesh;
886     bitmapMeshInfo.column = column;
887     bitmapMeshInfo.row = row;
888     renderingContext2DModel_->DrawBitmapMesh(bitmapMeshInfo);
889 }
890 
891 // filter: string
JsSetFilter(const JSCallbackInfo & info)892 void JSCanvasRenderer::JsSetFilter(const JSCallbackInfo& info)
893 {
894     std::string filterStr = "none";
895     if (info.GetStringArg(0, filterStr) && !filterStr.empty()) {
896         renderingContext2DModel_->SetFilterParam(filterStr);
897     }
898 }
899 
900 // direction: CanvasDirection
JsSetDirection(const JSCallbackInfo & info)901 void JSCanvasRenderer::JsSetDirection(const JSCallbackInfo& info)
902 {
903     std::string directionStr;
904     if (info.GetStringArg(0, directionStr)) {
905         auto direction = ConvertStrToTextDirection(directionStr);
906         renderingContext2DModel_->SetTextDirection(direction);
907     }
908 }
909 // getJsonData(path: string): string
JsGetJsonData(const JSCallbackInfo & info)910 void JSCanvasRenderer::JsGetJsonData(const JSCallbackInfo& info)
911 {
912     std::string path;
913     if (info.GetStringArg(0, path)) {
914         std::string jsonData = renderingContext2DModel_->GetJsonData(path);
915         auto returnValue = JSVal(ToJSValue(jsonData));
916         auto returnPtr = JSRef<JSVal>::Make(returnValue);
917         info.SetReturnValue(returnPtr);
918     }
919 }
920 
921 // toDataURL(type?: string, quality?: number): string
JsToDataUrl(const JSCallbackInfo & info)922 void JSCanvasRenderer::JsToDataUrl(const JSCallbackInfo& info)
923 {
924     std::string dataUrl;
925     double quality = DEFAULT_QUALITY;
926     info.GetStringArg(0, dataUrl);
927     info.GetDoubleArg(1, quality);
928     std::string result = renderingContext2DModel_->ToDataURL(dataUrl, quality);
929 
930     auto returnValue = JSVal(ToJSValue(result));
931     auto returnPtr = JSRef<JSVal>::Make(returnValue);
932     info.SetReturnValue(returnPtr);
933 }
934 
935 // lineCap: CanvasLineCap
JsSetLineCap(const JSCallbackInfo & info)936 void JSCanvasRenderer::JsSetLineCap(const JSCallbackInfo& info)
937 {
938     static const LinearMapNode<LineCapStyle> lineCapTable[] = {
939         { "butt", LineCapStyle::BUTT },
940         { "round", LineCapStyle::ROUND },
941         { "square", LineCapStyle::SQUARE },
942     };
943     std::string capStr;
944     if (info.GetStringArg(0, capStr)) {
945         auto lineCap = ConvertStrToEnum(capStr.c_str(), lineCapTable, ArraySize(lineCapTable), LineCapStyle::BUTT);
946         renderingContext2DModel_->SetLineCap(lineCap);
947     }
948 }
949 
950 // lineJoin: CanvasLineJoin
JsSetLineJoin(const JSCallbackInfo & info)951 void JSCanvasRenderer::JsSetLineJoin(const JSCallbackInfo& info)
952 {
953     static const LinearMapNode<LineJoinStyle> lineJoinTable[3] = {
954         { "bevel", LineJoinStyle::BEVEL },
955         { "miter", LineJoinStyle::MITER },
956         { "round", LineJoinStyle::ROUND },
957     };
958     std::string joinStr;
959     if (info.GetStringArg(0, joinStr)) {
960         auto lineJoin =
961             ConvertStrToEnum(joinStr.c_str(), lineJoinTable, ArraySize(lineJoinTable), LineJoinStyle::MITER);
962         renderingContext2DModel_->SetLineJoin(lineJoin);
963     }
964 }
965 
966 // miterLimit: number
JsSetMiterLimit(const JSCallbackInfo & info)967 void JSCanvasRenderer::JsSetMiterLimit(const JSCallbackInfo& info)
968 {
969     double limit = 0.0;
970     if (info.GetDoubleArg(0, limit)) {
971         renderingContext2DModel_->SetMiterLimit(limit);
972     }
973 }
974 
975 // lineWidth: number
JsSetLineWidth(const JSCallbackInfo & info)976 void JSCanvasRenderer::JsSetLineWidth(const JSCallbackInfo& info)
977 {
978     double lineWidth = 0.0;
979     if (info.GetDoubleArg(0, lineWidth)) {
980         renderingContext2DModel_->SetLineWidth(lineWidth * GetDensity());
981     }
982 }
983 
984 // globalAlpha: number
JsSetGlobalAlpha(const JSCallbackInfo & info)985 void JSCanvasRenderer::JsSetGlobalAlpha(const JSCallbackInfo& info)
986 {
987     double alpha = 0.0;
988     if (info.GetDoubleArg(0, alpha)) {
989         renderingContext2DModel_->SetGlobalAlpha(alpha);
990     }
991 }
992 
993 // globalCompositeOperation: string
JsSetGlobalCompositeOperation(const JSCallbackInfo & info)994 void JSCanvasRenderer::JsSetGlobalCompositeOperation(const JSCallbackInfo& info)
995 {
996     static const LinearMapNode<CompositeOperation> compositeOperationTable[] = {
997         { "copy", CompositeOperation::COPY },
998         { "destination-atop", CompositeOperation::DESTINATION_ATOP },
999         { "destination-in", CompositeOperation::DESTINATION_IN },
1000         { "destination-out", CompositeOperation::DESTINATION_OUT },
1001         { "destination-over", CompositeOperation::DESTINATION_OVER },
1002         { "lighter", CompositeOperation::LIGHTER },
1003         { "source-atop", CompositeOperation::SOURCE_ATOP },
1004 
1005         { "source-in", CompositeOperation::SOURCE_IN },
1006         { "source-out", CompositeOperation::SOURCE_OUT },
1007         { "source-over", CompositeOperation::SOURCE_OVER },
1008         { "xor", CompositeOperation::XOR },
1009     };
1010     std::string compositeStr;
1011     if (info.GetStringArg(0, compositeStr)) {
1012         auto type = ConvertStrToEnum(compositeStr.c_str(), compositeOperationTable, ArraySize(compositeOperationTable),
1013             CompositeOperation::SOURCE_OVER);
1014         renderingContext2DModel_->SetCompositeType(type);
1015     }
1016 }
1017 
1018 // lineDashOffset: number
JsSetLineDashOffset(const JSCallbackInfo & info)1019 void JSCanvasRenderer::JsSetLineDashOffset(const JSCallbackInfo& info)
1020 {
1021     double lineDashOffset = 0.0;
1022     if (info.GetDoubleArg(0, lineDashOffset)) {
1023         renderingContext2DModel_->SetLineDashOffset(lineDashOffset * GetDensity());
1024     }
1025 }
1026 
1027 // shadowBlur: number
JsSetShadowBlur(const JSCallbackInfo & info)1028 void JSCanvasRenderer::JsSetShadowBlur(const JSCallbackInfo& info)
1029 {
1030     double blur = 0.0;
1031     if (info.GetDoubleArg(0, blur)) {
1032         renderingContext2DModel_->SetShadowBlur(blur);
1033     }
1034 }
1035 
1036 // shadowColor: string
JsSetShadowColor(const JSCallbackInfo & info)1037 void JSCanvasRenderer::JsSetShadowColor(const JSCallbackInfo& info)
1038 {
1039     std::string colorStr;
1040     if (info.GetStringArg(0, colorStr)) {
1041         renderingContext2DModel_->SetShadowColor(Color::FromString(colorStr));
1042     }
1043 }
1044 
1045 // shadowOffsetX: number
JsSetShadowOffsetX(const JSCallbackInfo & info)1046 void JSCanvasRenderer::JsSetShadowOffsetX(const JSCallbackInfo& info)
1047 {
1048     double offsetX = 0.0;
1049     if (info.GetDoubleArg(0, offsetX)) {
1050         renderingContext2DModel_->SetShadowOffsetX(offsetX * GetDensity());
1051     }
1052 }
1053 
1054 // shadowOffsetY: number
JsSetShadowOffsetY(const JSCallbackInfo & info)1055 void JSCanvasRenderer::JsSetShadowOffsetY(const JSCallbackInfo& info)
1056 {
1057     double offsetY = 0.0;
1058     if (info.GetDoubleArg(0, offsetY)) {
1059         renderingContext2DModel_->SetShadowOffsetY(offsetY * GetDensity());
1060     }
1061 }
1062 
1063 // imageSmoothingEnabled: boolean
JsSetImageSmoothingEnabled(const JSCallbackInfo & info)1064 void JSCanvasRenderer::JsSetImageSmoothingEnabled(const JSCallbackInfo& info)
1065 {
1066     bool enabled = false;
1067     if (info.GetBooleanArg(0, enabled)) {
1068         renderingContext2DModel_->SetSmoothingEnabled(enabled);
1069     }
1070 }
1071 
1072 // imageSmoothingQuality: ImageSmoothingQuality
JsSetImageSmoothingQuality(const JSCallbackInfo & info)1073 void JSCanvasRenderer::JsSetImageSmoothingQuality(const JSCallbackInfo& info)
1074 {
1075     std::string quality;
1076     if (info.GetStringArg(0, quality) && (QUALITY_TYPE.find(quality) != QUALITY_TYPE.end())) {
1077         renderingContext2DModel_->SetSmoothingQuality(quality);
1078     }
1079 }
1080 
1081 // moveTo(x: number, y: number): void
JsMoveTo(const JSCallbackInfo & info)1082 void JSCanvasRenderer::JsMoveTo(const JSCallbackInfo& info)
1083 {
1084     double x = 0.0;
1085     double y = 0.0;
1086     if (info.GetDoubleArg(0, x) && info.GetDoubleArg(1, y)) {
1087         double density = GetDensity();
1088         renderingContext2DModel_->MoveTo(x * density, y * density);
1089     }
1090 }
1091 
1092 // lineTo(x: number, y: number): void
JsLineTo(const JSCallbackInfo & info)1093 void JSCanvasRenderer::JsLineTo(const JSCallbackInfo& info)
1094 {
1095     double x = 0.0;
1096     double y = 0.0;
1097     if (info.GetDoubleArg(0, x) && info.GetDoubleArg(1, y)) {
1098         double density = GetDensity();
1099         renderingContext2DModel_->LineTo(x * density, y * density);
1100     }
1101 }
1102 
1103 // bezierCurveTo(cp1x: number, cp1y: number, cp2x: number, cp2y: number, x: number, y: number): void
JsBezierCurveTo(const JSCallbackInfo & info)1104 void JSCanvasRenderer::JsBezierCurveTo(const JSCallbackInfo& info)
1105 {
1106     BezierCurveParam param;
1107     if (info.GetDoubleArg(0, param.cp1x) && info.GetDoubleArg(1, param.cp1y) && info.GetDoubleArg(2, param.cp2x) &&
1108         info.GetDoubleArg(3, param.cp2y) && info.GetDoubleArg(4, param.x) && info.GetDoubleArg(5, param.y)) {
1109         double density = GetDensity();
1110         param.cp1x *= density;
1111         param.cp1y *= density;
1112         param.cp2x *= density;
1113         param.cp2y *= density;
1114         param.x *= density;
1115         param.y *= density;
1116         renderingContext2DModel_->BezierCurveTo(param);
1117     }
1118 }
1119 
1120 // quadraticCurveTo(cpx: number, cpy: number, x: number, y: number): void
JsQuadraticCurveTo(const JSCallbackInfo & info)1121 void JSCanvasRenderer::JsQuadraticCurveTo(const JSCallbackInfo& info)
1122 {
1123     QuadraticCurveParam param;
1124     if (info.GetDoubleArg(0, param.cpx) && info.GetDoubleArg(1, param.cpy) && info.GetDoubleArg(2, param.x) &&
1125         info.GetDoubleArg(3, param.y)) {
1126         double density = GetDensity();
1127         param.cpx *= density;
1128         param.cpy *= density;
1129         param.x *= density;
1130         param.y *= density;
1131         renderingContext2DModel_->QuadraticCurveTo(param);
1132     }
1133 }
1134 
1135 // arcTo(x1: number, y1: number, x2: number, y2: number, radius: number): void
JsArcTo(const JSCallbackInfo & info)1136 void JSCanvasRenderer::JsArcTo(const JSCallbackInfo& info)
1137 {
1138     ArcToParam param;
1139     if (info.GetDoubleArg(0, param.x1) && info.GetDoubleArg(1, param.y1) && info.GetDoubleArg(2, param.x2) &&
1140         info.GetDoubleArg(3, param.y2) && info.GetDoubleArg(4, param.radius)) {
1141         double density = GetDensity();
1142         param.x1 *= density;
1143         param.y1 *= density;
1144         param.x2 *= density;
1145         param.y2 *= density;
1146         param.radius *= density;
1147         renderingContext2DModel_->ArcTo(param);
1148     }
1149 }
1150 
1151 // arc(x: number, y: number, radius: number, startAngle: number, endAngle: number, counterclockwise?: boolean): void
JsArc(const JSCallbackInfo & info)1152 void JSCanvasRenderer::JsArc(const JSCallbackInfo& info)
1153 {
1154     ArcParam param;
1155     if (info.GetDoubleArg(0, param.x) && info.GetDoubleArg(1, param.y) && info.GetDoubleArg(2, param.radius) &&
1156         info.GetDoubleArg(3, param.startAngle) && info.GetDoubleArg(4, param.endAngle)) {
1157         info.GetBooleanArg(5, param.anticlockwise); // Non mandatory parameter with default value 'false'
1158         double density = GetDensity();
1159         param.x *= density;
1160         param.y *= density;
1161         param.radius *= density;
1162         renderingContext2DModel_->Arc(param);
1163     }
1164 }
1165 
1166 // ellipse(x: number, y: number, radiusX: number, radiusY: number, rotation: number, startAngle: number,
1167 //         endAngle: number, counterclockwise?: boolean): void
JsEllipse(const JSCallbackInfo & info)1168 void JSCanvasRenderer::JsEllipse(const JSCallbackInfo& info)
1169 {
1170     EllipseParam param;
1171     if (info.GetDoubleArg(0, param.x) && info.GetDoubleArg(1, param.y) && info.GetDoubleArg(2, param.radiusX) &&
1172         info.GetDoubleArg(3, param.radiusY) && info.GetDoubleArg(4, param.rotation) &&
1173         info.GetDoubleArg(5, param.startAngle) && info.GetDoubleArg(6, param.endAngle)) {
1174         info.GetBooleanArg(7, param.anticlockwise); // Non mandatory parameter with default value 'false'
1175         double density = GetDensity();
1176         param.x *= density;
1177         param.y *= density;
1178         param.radiusX *= density;
1179         param.radiusY *= density;
1180         renderingContext2DModel_->Ellipse(param);
1181     }
1182 }
1183 
1184 // fill(fillRule?: CanvasFillRule): void
1185 // fill(path: Path2D, fillRule?: CanvasFillRule): void
JsFill(const JSCallbackInfo & info)1186 void JSCanvasRenderer::JsFill(const JSCallbackInfo& info)
1187 {
1188     std::string ruleStr;
1189     auto fillRule = CanvasFillRule::NONZERO;
1190 
1191     // fill(fillRule?: CanvasFillRule): void
1192     if (info.Length() == 0 || info.GetStringArg(0, ruleStr)) {
1193         fillRule = ruleStr == "evenodd" ? CanvasFillRule::EVENODD : CanvasFillRule::NONZERO;
1194         renderingContext2DModel_->SetFillRuleForPath(fillRule);
1195         return;
1196     }
1197 
1198     // fill(path: Path2D, fillRule?: CanvasFillRule): void
1199     JSPath2D* jsCanvasPath = info.UnwrapArg<JSPath2D>(0);
1200     CHECK_NULL_VOID(jsCanvasPath);
1201     auto path = jsCanvasPath->GetCanvasPath2d();
1202     if (info.GetStringArg(1, ruleStr) && ruleStr == "evenodd") {
1203         fillRule = CanvasFillRule::EVENODD;
1204     }
1205     renderingContext2DModel_->SetFillRuleForPath2D(fillRule, path);
1206 }
1207 
1208 // stroke(path?: Path2D): void
JsStroke(const JSCallbackInfo & info)1209 void JSCanvasRenderer::JsStroke(const JSCallbackInfo& info)
1210 {
1211     auto* jsCanvasPath = info.UnwrapArg<JSPath2D>(0);
1212     if (jsCanvasPath) {
1213         auto path = jsCanvasPath->GetCanvasPath2d();
1214         renderingContext2DModel_->SetStrokeRuleForPath2D(CanvasFillRule::NONZERO, path);
1215         return;
1216     }
1217     renderingContext2DModel_->SetStrokeRuleForPath(CanvasFillRule::NONZERO);
1218 }
1219 
1220 // clip(fillRule?: CanvasFillRule): void
1221 // clip(path: Path2D, fillRule?: CanvasFillRule): void
JsClip(const JSCallbackInfo & info)1222 void JSCanvasRenderer::JsClip(const JSCallbackInfo& info)
1223 {
1224     std::string ruleStr;
1225     auto fillRule = CanvasFillRule::NONZERO;
1226 
1227     // clip(fillRule?: CanvasFillRule): void
1228     if (info.Length() == 0 || info.GetStringArg(0, ruleStr)) {
1229         fillRule = ruleStr == "evenodd" ? CanvasFillRule::EVENODD : CanvasFillRule::NONZERO;
1230         renderingContext2DModel_->SetClipRuleForPath(fillRule);
1231         return;
1232     }
1233 
1234     // clip(path: Path2D, fillRule?: CanvasFillRule): void
1235     JSPath2D* jsCanvasPath = info.UnwrapArg<JSPath2D>(0);
1236     CHECK_NULL_VOID(jsCanvasPath);
1237     auto path = jsCanvasPath->GetCanvasPath2d();
1238     if (info.GetStringArg(1, ruleStr) && ruleStr == "evenodd") {
1239         fillRule = CanvasFillRule::EVENODD;
1240     }
1241     renderingContext2DModel_->SetClipRuleForPath2D(fillRule, path);
1242 }
1243 
1244 // rect(x: number, y: number, w: number, h: number): void
JsRect(const JSCallbackInfo & info)1245 void JSCanvasRenderer::JsRect(const JSCallbackInfo& info)
1246 {
1247     double x = 0.0;
1248     double y = 0.0;
1249     double width = 0.0;
1250     double height = 0.0;
1251     if (info.GetDoubleArg(0, x) && info.GetDoubleArg(1, y) && info.GetDoubleArg(2, width) &&
1252         info.GetDoubleArg(3, height)) {
1253         renderingContext2DModel_->AddRect(Rect(x, y, width, height) * GetDensity());
1254     }
1255 }
1256 
1257 // beginPath(): void
JsBeginPath(const JSCallbackInfo & info)1258 void JSCanvasRenderer::JsBeginPath(const JSCallbackInfo& info)
1259 {
1260     renderingContext2DModel_->BeginPath();
1261 }
1262 
1263 // closePath(): void
JsClosePath(const JSCallbackInfo & info)1264 void JSCanvasRenderer::JsClosePath(const JSCallbackInfo& info)
1265 {
1266     renderingContext2DModel_->ClosePath();
1267 }
1268 
1269 // restore(): void
JsRestore(const JSCallbackInfo & info)1270 void JSCanvasRenderer::JsRestore(const JSCallbackInfo& info)
1271 {
1272     if (!savePaintState_.empty()) {
1273         paintState_ = savePaintState_.back();
1274         savePaintState_.pop_back();
1275     }
1276     renderingContext2DModel_->Restore();
1277 }
1278 
1279 // save(): void
JsSave(const JSCallbackInfo & info)1280 void JSCanvasRenderer::JsSave(const JSCallbackInfo& info)
1281 {
1282     savePaintState_.push_back(paintState_);
1283     renderingContext2DModel_->CanvasRendererSave();
1284 }
1285 
1286 // rotate(angle: number): void
JsRotate(const JSCallbackInfo & info)1287 void JSCanvasRenderer::JsRotate(const JSCallbackInfo& info)
1288 {
1289     double angle = 0.0;
1290     info.GetDoubleArg(0, angle);
1291     renderingContext2DModel_->CanvasRendererRotate(angle);
1292 }
1293 
1294 // scale(x: number, y: number): void
JsScale(const JSCallbackInfo & info)1295 void JSCanvasRenderer::JsScale(const JSCallbackInfo& info)
1296 {
1297     double x = 0.0;
1298     double y = 0.0;
1299     if (info.GetDoubleArg(0, x) && info.GetDoubleArg(1, y)) {
1300         renderingContext2DModel_->CanvasRendererScale(x, y);
1301     }
1302 }
1303 
1304 // getTransform(): Matrix2D
JsGetTransform(const JSCallbackInfo & info)1305 void JSCanvasRenderer::JsGetTransform(const JSCallbackInfo& info)
1306 {
1307     ContainerScope scope(instanceId_);
1308     JSRef<JSObject> obj = JSClass<JSMatrix2d>::NewInstance();
1309     obj->SetProperty("__type", "Matrix2D");
1310     if (Container::IsCurrentUseNewPipeline()) {
1311         TransformParam param = renderingContext2DModel_->GetTransform();
1312         auto matrix = Referenced::Claim(obj->Unwrap<JSMatrix2d>());
1313         CHECK_NULL_VOID(matrix);
1314         matrix->SetTransform(param);
1315     }
1316     info.SetReturnValue(obj);
1317 }
1318 
1319 // setTransform(a: number, b: number, c: number, d: number, e: number, f: number): void
1320 // setTransform(transform?: Matrix2D): void
JsSetTransform(const JSCallbackInfo & info)1321 void JSCanvasRenderer::JsSetTransform(const JSCallbackInfo& info)
1322 {
1323     if (info.GetSize() == 0) {
1324         renderingContext2DModel_->ResetTransform();
1325     }
1326     double density = GetDensity();
1327     TransformParam param;
1328     // setTransform(a: number, b: number, c: number, d: number, e: number, f: number): void
1329     if (info.GetDoubleArg(0, param.scaleX) && info.GetDoubleArg(1, param.skewY) && info.GetDoubleArg(2, param.skewX) &&
1330         info.GetDoubleArg(3, param.scaleY) && info.GetDoubleArg(4, param.translateX) &&
1331         info.GetDoubleArg(5, param.translateY)) {
1332         param.translateX *= density;
1333         param.translateY *= density;
1334         renderingContext2DModel_->SetTransform(param, true);
1335         return;
1336     }
1337 
1338     // >= API10: setTransform(transform?: Matrix2D): void
1339     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TEN)) {
1340         auto* jsMatrix2d = info.UnwrapArg<JSMatrix2d>(0);
1341         CHECK_NULL_VOID(jsMatrix2d);
1342         param = jsMatrix2d->GetTransform();
1343         renderingContext2DModel_->SetTransform(param, false);
1344         return;
1345     }
1346 
1347     // old version: setTransform(transform?: Matrix2D): void
1348     if (info[0]->IsObject()) {
1349         JSRef<JSObject> jsObj = JSRef<JSObject>::Cast(info[0]);
1350         TransformParam param = JSMatrix2d::GetTransformInfo(jsObj);
1351         param.translateX *= density;
1352         param.translateY *= density;
1353         renderingContext2DModel_->SetTransform(param, false);
1354     }
1355 }
1356 
1357 // resetTransform(): void
JsResetTransform(const JSCallbackInfo & info)1358 void JSCanvasRenderer::JsResetTransform(const JSCallbackInfo& info)
1359 {
1360     renderingContext2DModel_->ResetTransform();
1361 }
1362 
1363 // transform(a: number, b: number, c: number, d: number, e: number, f: number): void
JsTransform(const JSCallbackInfo & info)1364 void JSCanvasRenderer::JsTransform(const JSCallbackInfo& info)
1365 {
1366     TransformParam param;
1367     if (info.GetDoubleArg(0, param.scaleX) && info.GetDoubleArg(1, param.skewX) && info.GetDoubleArg(2, param.skewY) &&
1368         info.GetDoubleArg(3, param.scaleY) && info.GetDoubleArg(4, param.translateX) &&
1369         info.GetDoubleArg(5, param.translateY)) {
1370         double density = GetDensity();
1371         param.translateX *= density;
1372         param.translateY *= density;
1373         renderingContext2DModel_->Transform(param);
1374     }
1375 }
1376 
1377 // translate(x: number, y: number): void
JsTranslate(const JSCallbackInfo & info)1378 void JSCanvasRenderer::JsTranslate(const JSCallbackInfo& info)
1379 {
1380     double x = 0.0;
1381     double y = 0.0;
1382     if (info.GetDoubleArg(0, x) && info.GetDoubleArg(1, y)) {
1383         double density = GetDensity();
1384         renderingContext2DModel_->Translate(x * density, y * density);
1385     }
1386 }
1387 
1388 // setLineDash(segments: number[]): void
JsSetLineDash(const JSCallbackInfo & info)1389 void JSCanvasRenderer::JsSetLineDash(const JSCallbackInfo& info)
1390 {
1391     std::vector<double> lineDash;
1392     info.GetDoubleArrayArg(0, lineDash);
1393     if (lineDash.size() % 2 != 0) {
1394         lineDash.insert(lineDash.end(), lineDash.begin(), lineDash.end());
1395     }
1396     double density = GetDensity();
1397     if (!Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TEN)) {
1398         for (auto i = 0U; i < lineDash.size(); i++) {
1399             lineDash[i] *= density;
1400         }
1401     }
1402     renderingContext2DModel_->SetLineDash(lineDash);
1403 }
1404 
GetPattern(unsigned int id)1405 Pattern JSCanvasRenderer::GetPattern(unsigned int id)
1406 {
1407     if (id < 0 || id >= pattern_.size()) {
1408         return Pattern();
1409     }
1410     return *(pattern_[id].get());
1411 }
1412 
GetPatternNG(int32_t id)1413 std::weak_ptr<Ace::Pattern> JSCanvasRenderer::GetPatternNG(int32_t id)
1414 {
1415     if (id < 0 || id >= static_cast<int32_t>(pattern_.size())) {
1416         return std::shared_ptr<Pattern>();
1417     }
1418     return pattern_[id];
1419 }
1420 
GetPatternPtr(int32_t id)1421 std::shared_ptr<Pattern> JSCanvasRenderer::GetPatternPtr(int32_t id)
1422 {
1423     if (id < 0 || id >= static_cast<int32_t>(pattern_.size())) {
1424         return std::shared_ptr<Pattern>();
1425     }
1426     return pattern_[id];
1427 }
1428 
SetTransform(unsigned int id,const TransformParam & transform)1429 void JSCanvasRenderer::SetTransform(unsigned int id, const TransformParam& transform)
1430 {
1431     if (id >= 0 && id <= patternCount_) {
1432         pattern_[id]->SetScaleX(transform.scaleX);
1433         pattern_[id]->SetScaleY(transform.scaleY);
1434         pattern_[id]->SetSkewX(transform.skewX);
1435         pattern_[id]->SetSkewY(transform.skewY);
1436         pattern_[id]->SetTranslateX(transform.translateX);
1437         pattern_[id]->SetTranslateY(transform.translateY);
1438     }
1439 }
1440 
1441 // textAlign: CanvasTextAlign
JsSetTextAlign(const JSCallbackInfo & info)1442 void JSCanvasRenderer::JsSetTextAlign(const JSCallbackInfo& info)
1443 {
1444     std::string value;
1445     if (info.GetStringArg(0, value)) {
1446         auto align = ConvertStrToTextAlign(value);
1447         paintState_.SetTextAlign(align);
1448         renderingContext2DModel_->SetTextAlign(align);
1449     }
1450 }
1451 
1452 // textBaseline: CanvasTextBaseline
JsSetTextBaseline(const JSCallbackInfo & info)1453 void JSCanvasRenderer::JsSetTextBaseline(const JSCallbackInfo& info)
1454 {
1455     std::string textBaseline;
1456     if (info.GetStringArg(0, textBaseline)) {
1457         auto baseline =
1458             ConvertStrToEnum(textBaseline.c_str(), BASELINE_TABLE, ArraySize(BASELINE_TABLE), TextBaseline::ALPHABETIC);
1459         paintState_.SetTextBaseline(baseline);
1460         renderingContext2DModel_->SetTextBaseline(baseline);
1461     }
1462 }
1463 
1464 // measureText(text: string): TextMetrics
JsMeasureText(const JSCallbackInfo & info)1465 void JSCanvasRenderer::JsMeasureText(const JSCallbackInfo& info)
1466 {
1467     std::string text;
1468     double density = GetDensity();
1469     if (Positive(density) && info.GetStringArg(0, text)) {
1470         TextMetrics textMetrics = renderingContext2DModel_->GetMeasureTextMetrics(paintState_, text);
1471         auto vm = info.GetVm();
1472         CHECK_NULL_VOID(vm);
1473         static const char* keysOfMeasureText[] = { "width", "height", "actualBoundingBoxLeft", "actualBoundingBoxRight",
1474             "actualBoundingBoxAscent", "actualBoundingBoxDescent", "hangingBaseline", "alphabeticBaseline",
1475             "ideographicBaseline", "emHeightAscent", "emHeightDescent", "fontBoundingBoxAscent",
1476             "fontBoundingBoxDescent" };
1477         Local<JSValueRef> valuesOfMeasureText[] = { panda::NumberRef::New(vm, (textMetrics.width / density)),
1478             panda::NumberRef::New(vm, (textMetrics.height / density)),
1479             panda::NumberRef::New(vm, (textMetrics.actualBoundingBoxLeft / density)),
1480             panda::NumberRef::New(vm, (textMetrics.actualBoundingBoxRight / density)),
1481             panda::NumberRef::New(vm, (textMetrics.actualBoundingBoxAscent / density)),
1482             panda::NumberRef::New(vm, (textMetrics.actualBoundingBoxDescent / density)),
1483             panda::NumberRef::New(vm, (textMetrics.hangingBaseline / density)),
1484             panda::NumberRef::New(vm, (textMetrics.alphabeticBaseline / density)),
1485             panda::NumberRef::New(vm, (textMetrics.ideographicBaseline / density)),
1486             panda::NumberRef::New(vm, (textMetrics.emHeightAscent / density)),
1487             panda::NumberRef::New(vm, (textMetrics.emHeightDescent / density)),
1488             panda::NumberRef::New(vm, (textMetrics.fontBoundingBoxAscent / density)),
1489             panda::NumberRef::New(vm, (textMetrics.fontBoundingBoxDescent / density)) };
1490         auto obj = panda::ObjectRef::NewWithNamedProperties(
1491             vm, ArraySize(keysOfMeasureText), keysOfMeasureText, valuesOfMeasureText);
1492         info.SetReturnValue(JsiRef<JsiObject>(JsiObject(obj)));
1493     }
1494 }
1495 
1496 // fillRect(x: number, y: number, w: number, h: number): void
JsFillRect(const JSCallbackInfo & info)1497 void JSCanvasRenderer::JsFillRect(const JSCallbackInfo& info)
1498 {
1499     double x = 0.0;
1500     double y = 0.0;
1501     double width = 0.0;
1502     double height = 0.0;
1503     if (info.GetDoubleArg(0, x) && info.GetDoubleArg(1, y) && info.GetDoubleArg(2, width) &&
1504         info.GetDoubleArg(3, height)) {
1505         renderingContext2DModel_->FillRect(Rect(x, y, width, height) * GetDensity());
1506     }
1507 }
1508 
1509 // strokeRect(x: number, y: number, w: number, h: number): void
JsStrokeRect(const JSCallbackInfo & info)1510 void JSCanvasRenderer::JsStrokeRect(const JSCallbackInfo& info)
1511 {
1512     double x = 0.0;
1513     double y = 0.0;
1514     double width = 0.0;
1515     double height = 0.0;
1516     if (info.GetDoubleArg(0, x) && info.GetDoubleArg(1, y) && info.GetDoubleArg(2, width) &&
1517         info.GetDoubleArg(3, height)) {
1518         renderingContext2DModel_->StrokeRect(Rect(x, y, width, height) * GetDensity());
1519     }
1520 }
1521 
1522 // clearRect(x: number, y: number, w: number, h: number): void
JsClearRect(const JSCallbackInfo & info)1523 void JSCanvasRenderer::JsClearRect(const JSCallbackInfo& info)
1524 {
1525     double x = 0.0;
1526     double y = 0.0;
1527     double width = 0.0;
1528     double height = 0.0;
1529     if (info.GetDoubleArg(0, x) && info.GetDoubleArg(1, y) && info.GetDoubleArg(2, width) &&
1530         info.GetDoubleArg(3, height)) {
1531         renderingContext2DModel_->ClearRect(Rect(x, y, width, height) * GetDensity());
1532     }
1533 }
1534 
1535 // saveLayer(): void
JsSaveLayer(const JSCallbackInfo & info)1536 void JSCanvasRenderer::JsSaveLayer(const JSCallbackInfo& info)
1537 {
1538     renderingContext2DModel_->SaveLayer();
1539 }
1540 
1541 // restoreLayer(): void
JsRestoreLayer(const JSCallbackInfo & info)1542 void JSCanvasRenderer::JsRestoreLayer(const JSCallbackInfo& info)
1543 {
1544     renderingContext2DModel_->RestoreLayer();
1545 }
1546 
1547 // reset(): void
JsReset(const JSCallbackInfo & info)1548 void JSCanvasRenderer::JsReset(const JSCallbackInfo& info)
1549 {
1550     paintState_ = PaintState();
1551     isInitializeShadow_ = false;
1552     isOffscreenInitializeShadow_ = false;
1553     renderingContext2DModel_->Reset();
1554 }
1555 
GetDimensionValue(const std::string & str)1556 Dimension JSCanvasRenderer::GetDimensionValue(const std::string& str)
1557 {
1558     Dimension dimension = StringToDimension(str);
1559     if ((dimension.Unit() == DimensionUnit::NONE) || (dimension.Unit() == DimensionUnit::PX)) {
1560         return Dimension(dimension.Value());
1561     }
1562     if (dimension.Unit() == DimensionUnit::VP) {
1563         return Dimension(dimension.Value() * GetDensity(true));
1564     }
1565     return Dimension(0.0);
1566 }
1567 } // namespace OHOS::Ace::Framework
1568