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