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