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