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