1 /*
2 * Copyright (c) 2021-2022 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 "core/components/custom_paint/flutter_render_offscreen_canvas.h"
17
18 #include <cmath>
19 #include <sstream>
20
21 #include "txt/paragraph_builder.h"
22 #include "txt/paragraph_style.h"
23 #include "include/core/SkBlendMode.h"
24 #include "include/core/SkCanvas.h"
25 #include "include/core/SkColor.h"
26 #include "include/core/SkMaskFilter.h"
27 #include "include/core/SkPoint.h"
28 #ifndef NEW_SKIA
29 #include "include/effects/SkBlurImageFilter.h"
30 #else
31 #include "include/effects/SkImageFilters.h"
32 #endif
33 #include "include/effects/SkDashPathEffect.h"
34 #include "include/effects/SkGradientShader.h"
35 #include "include/encode/SkJpegEncoder.h"
36 #include "include/encode/SkPngEncoder.h"
37 #include "include/encode/SkWebpEncoder.h"
38 #include "include/utils/SkBase64.h"
39 #include "include/utils/SkParsePath.h"
40
41 #include "base/i18n/localization.h"
42 #include "base/image/pixel_map.h"
43 #include "base/log/log.h"
44 #include "base/utils/string_utils.h"
45 #include "core/components/common/painter/flutter_decoration_painter.h"
46 #include "core/components/font/constants_converter.h"
47 #include "core/components/font/flutter_font_collection.h"
48 #include "core/image/image_provider.h"
49
50 namespace OHOS::Ace {
51 namespace {
52 constexpr double HANGING_PERCENT = 0.8;
53 template<typename T, typename N>
ConvertEnumToSkEnum(T key,const LinearEnumMapNode<T,N> * map,size_t length,N defaultValue)54 N ConvertEnumToSkEnum(T key, const LinearEnumMapNode<T, N>* map, size_t length, N defaultValue)
55 {
56 int64_t index = BinarySearchFindIndex(map, length, key);
57 return index != -1 ? map[index].value : defaultValue;
58 }
59
60 constexpr double DEFAULT_QUALITY = 0.92;
61 constexpr int32_t MAX_LENGTH = 2048 * 2048;
62 const std::string UNSUPPORTED = "data:image/png";
63 const std::string URL_PREFIX = "data:";
64 const std::string URL_SYMBOL = ";base64,";
65 const std::string IMAGE_PNG = "image/png";
66 const std::string IMAGE_JPEG = "image/jpeg";
67 const std::string IMAGE_WEBP = "image/webp";
68 constexpr double HALF_CIRCLE_ANGLE = 180.0;
69 constexpr double FULL_CIRCLE_ANGLE = 360.0;
70
71 // If args is empty or invalid format, use default: image/png
GetMimeType(const std::string & args)72 std::string GetMimeType(const std::string& args)
73 {
74 std::string type = args;
75 for (size_t i = 0; i < type.size(); ++i) {
76 type[i] = static_cast<uint8_t>(tolower(type[i]));
77 }
78 return type;
79 }
80
81 // Quality need between 0.0 and 1.0 for MimeType jpeg and webp
GetQuality(const std::string & args,const double quality)82 double GetQuality(const std::string& args, const double quality)
83 {
84 std::string type = args;
85 auto mimeType = GetMimeType(type);
86 if (mimeType != IMAGE_JPEG && mimeType != IMAGE_WEBP) {
87 return DEFAULT_QUALITY;
88 }
89 if (quality < 0.0 || quality > 1.0) {
90 return DEFAULT_QUALITY;
91 }
92 return quality;
93 }
94
95 const LinearEnumMapNode<CompositeOperation, SkBlendMode> SK_BLEND_MODE_TABLE[] = {
96 { CompositeOperation::SOURCE_OVER, SkBlendMode::kSrcOver },
97 { CompositeOperation::SOURCE_ATOP, SkBlendMode::kSrcATop },
98 { CompositeOperation::SOURCE_IN, SkBlendMode::kSrcIn },
99 { CompositeOperation::SOURCE_OUT, SkBlendMode::kSrcOut },
100 { CompositeOperation::DESTINATION_OVER, SkBlendMode::kDstOver },
101 { CompositeOperation::DESTINATION_ATOP, SkBlendMode::kDstATop },
102 { CompositeOperation::DESTINATION_IN, SkBlendMode::kDstIn },
103 { CompositeOperation::DESTINATION_OUT, SkBlendMode::kDstOut },
104 { CompositeOperation::LIGHTER, SkBlendMode::kLighten },
105 { CompositeOperation::COPY, SkBlendMode::kSrc },
106 { CompositeOperation::XOR, SkBlendMode::kXor },
107 };
108 constexpr size_t BLEND_MODE_SIZE = ArraySize(SK_BLEND_MODE_TABLE);
109 } // namespace
110
FlutterRenderOffscreenCanvas(const WeakPtr<PipelineBase> & context,int32_t width,int32_t height)111 FlutterRenderOffscreenCanvas::FlutterRenderOffscreenCanvas(
112 const WeakPtr<PipelineBase>& context, int32_t width, int32_t height)
113 {
114 pipelineContext_ = context;
115 width_ = width;
116 height_ = height;
117 auto imageInfo =
118 SkImageInfo::Make(width, height, SkColorType::kRGBA_8888_SkColorType, SkAlphaType::kOpaque_SkAlphaType);
119 skBitmap_.allocPixels(imageInfo);
120 cacheBitmap_.allocPixels(imageInfo);
121 skBitmap_.eraseColor(SK_ColorTRANSPARENT);
122 cacheBitmap_.eraseColor(SK_ColorTRANSPARENT);
123 skCanvas_ = std::make_unique<SkCanvas>(skBitmap_);
124 cacheCanvas_ = std::make_unique<SkCanvas>(cacheBitmap_);
125
126 InitFilterFunc();
127 }
128
AddRect(const Rect & rect)129 void FlutterRenderOffscreenCanvas::AddRect(const Rect& rect)
130 {
131 SkRect skRect = SkRect::MakeLTRB(rect.Left(), rect.Top(), rect.Right(), rect.Bottom());
132 skPath_.addRect(skRect);
133 }
134
SetFillRuleForPath(const CanvasFillRule & rule)135 void FlutterRenderOffscreenCanvas::SetFillRuleForPath(const CanvasFillRule& rule)
136 {
137 if (rule == CanvasFillRule::NONZERO) {
138 skPath_.setFillType(SkPath::FillType::kWinding_FillType);
139 } else if (rule == CanvasFillRule::EVENODD) {
140 skPath_.setFillType(SkPath::FillType::kEvenOdd_FillType);
141 }
142 }
143
SetFillRuleForPath2D(const CanvasFillRule & rule)144 void FlutterRenderOffscreenCanvas::SetFillRuleForPath2D(const CanvasFillRule& rule)
145 {
146 if (rule == CanvasFillRule::NONZERO) {
147 skPath2d_.setFillType(SkPath::FillType::kWinding_FillType);
148 } else if (rule == CanvasFillRule::EVENODD) {
149 skPath2d_.setFillType(SkPath::FillType::kEvenOdd_FillType);
150 }
151 }
152
ParsePath2D(const RefPtr<CanvasPath2D> & path)153 void FlutterRenderOffscreenCanvas::ParsePath2D(const RefPtr<CanvasPath2D>& path)
154 {
155 for (const auto& [cmd, args] : path->GetCaches()) {
156 switch (cmd) {
157 case PathCmd::CMDS: {
158 Path2DAddPath(args);
159 break;
160 }
161 case PathCmd::TRANSFORM: {
162 Path2DSetTransform(args);
163 break;
164 }
165 case PathCmd::MOVE_TO: {
166 Path2DMoveTo(args);
167 break;
168 }
169 case PathCmd::LINE_TO: {
170 Path2DLineTo(args);
171 break;
172 }
173 case PathCmd::ARC: {
174 Path2DArc(args);
175 break;
176 }
177 case PathCmd::ARC_TO: {
178 Path2DArcTo(args);
179 break;
180 }
181 case PathCmd::QUADRATIC_CURVE_TO: {
182 Path2DQuadraticCurveTo(args);
183 break;
184 }
185 case PathCmd::BEZIER_CURVE_TO: {
186 Path2DBezierCurveTo(args);
187 break;
188 }
189 case PathCmd::ELLIPSE: {
190 Path2DEllipse(args);
191 break;
192 }
193 case PathCmd::RECT: {
194 Path2DRect(args);
195 break;
196 }
197 case PathCmd::CLOSE_PATH: {
198 Path2DClosePath(args);
199 break;
200 }
201 default: {
202 break;
203 }
204 }
205 }
206 }
207
Fill()208 void FlutterRenderOffscreenCanvas::Fill()
209 {
210 SkPaint paint;
211 paint.setAntiAlias(antiAlias_);
212 paint.setColor(fillState_.GetColor().GetValue());
213 paint.setStyle(SkPaint::Style::kFill_Style);
214 if (HasShadow()) {
215 FlutterDecorationPainter::PaintShadow(skPath_, shadow_, skCanvas_.get());
216 }
217 if (fillState_.GetGradient().IsValid()) {
218 UpdatePaintShader(paint, fillState_.GetGradient());
219 }
220 if (fillState_.GetPattern().IsValid()) {
221 UpdatePaintShader(fillState_.GetPattern(), paint);
222 }
223 if (globalState_.HasGlobalAlpha()) {
224 paint.setAlphaf(globalState_.GetAlpha());
225 }
226 if (globalState_.GetType() == CompositeOperation::SOURCE_OVER) {
227 skCanvas_->drawPath(skPath_, paint);
228 } else {
229 InitCachePaint();
230 cacheCanvas_->drawPath(skPath_, paint);
231 skCanvas_->drawBitmap(cacheBitmap_, 0, 0, &cachePaint_);
232 cacheBitmap_.eraseColor(0);
233 }
234 }
235
Fill(const RefPtr<CanvasPath2D> & path)236 void FlutterRenderOffscreenCanvas::Fill(const RefPtr<CanvasPath2D>& path)
237 {
238 if (path == nullptr) {
239 LOGE("Fill failed in offscreenCanvas, target path is null.");
240 return;
241 }
242 ParsePath2D(path);
243 Path2DFill();
244 skPath2d_.reset();
245 }
246
Clip()247 void FlutterRenderOffscreenCanvas::Clip()
248 {
249 skCanvas_->clipPath(skPath_);
250 }
251
Clip(const RefPtr<CanvasPath2D> & path)252 void FlutterRenderOffscreenCanvas::Clip(const RefPtr<CanvasPath2D>& path)
253 {
254 if (path == nullptr) {
255 LOGE("Clip failed in offscreenCanvas, target path is null.");
256 return;
257 }
258 ParsePath2D(path);
259 Path2DClip();
260 skPath2d_.reset();
261 }
262
FillRect(Rect rect)263 void FlutterRenderOffscreenCanvas::FillRect(Rect rect)
264 {
265 SkPaint paint;
266 paint.setAntiAlias(antiAlias_);
267 paint.setColor(fillState_.GetColor().GetValue());
268 paint.setStyle(SkPaint::Style::kFill_Style);
269 SkRect skRect = SkRect::MakeLTRB(rect.Left(), rect.Top(), rect.Right(), rect.Bottom());
270 if (HasShadow()) {
271 SkPath path;
272 path.addRect(skRect);
273 FlutterDecorationPainter::PaintShadow(path, shadow_, skCanvas_.get());
274 }
275 if (fillState_.GetGradient().IsValid()) {
276 UpdatePaintShader(paint, fillState_.GetGradient());
277 }
278 if (fillState_.GetPattern().IsValid()) {
279 UpdatePaintShader(fillState_.GetPattern(), paint);
280 }
281 if (globalState_.HasGlobalAlpha()) {
282 paint.setAlphaf(globalState_.GetAlpha()); // update the global alpha after setting the color
283 }
284 if (globalState_.GetType() == CompositeOperation::SOURCE_OVER) {
285 skCanvas_->drawRect(skRect, paint);
286 } else {
287 InitCachePaint();
288 cacheCanvas_->drawRect(skRect, paint);
289 skCanvas_->drawBitmap(cacheBitmap_, 0, 0, &cachePaint_);
290 cacheBitmap_.eraseColor(0);
291 }
292 }
293
PutImageData(const ImageData & imageData)294 void FlutterRenderOffscreenCanvas::PutImageData(const ImageData& imageData)
295 {
296 if (imageData.data.empty()) {
297 return;
298 }
299 uint32_t* data = new (std::nothrow) uint32_t[imageData.data.size()];
300 if (data == nullptr) {
301 return;
302 }
303
304 for (uint32_t i = 0; i < imageData.data.size(); ++i) {
305 data[i] = imageData.data[i].GetValue();
306 }
307 SkBitmap skBitmap;
308 auto imageInfo = SkImageInfo::Make(imageData.dirtyWidth, imageData.dirtyHeight, SkColorType::kBGRA_8888_SkColorType,
309 SkAlphaType::kOpaque_SkAlphaType);
310 skBitmap.allocPixels(imageInfo);
311 skBitmap.setPixels(data);
312 skCanvas_->drawBitmap(skBitmap, imageData.x, imageData.y);
313 delete[] data;
314 }
315
SetPaintImage()316 void FlutterRenderOffscreenCanvas::SetPaintImage()
317 {
318 float matrix[20] = { 0 };
319 matrix[0] = matrix[6] = matrix[12] = matrix[18] = 1.0f;
320 #ifdef USE_SYSTEM_SKIA
321 imagePaint_.setColorFilter(SkColorFilter::MakeMatrixFilterRowMajor255(matrix));
322 #else
323 imagePaint_.setColorFilter(SkColorFilters::Matrix(matrix));
324 #endif
325
326 imagePaint_.setMaskFilter(SkMaskFilter::MakeBlur(SkBlurStyle::kNormal_SkBlurStyle, 0));
327 imagePaint_.setImageFilter(SkBlurImageFilter::Make(0, 0, nullptr));
328
329 SetDropShadowFilter("0px 0px 0px black");
330 std::string filterType, filterParam;
331 if (!GetFilterType(filterType, filterParam)) {
332 return;
333 }
334 if (filterFunc_.find(filterType) != filterFunc_.end()) {
335 filterFunc_[filterType](filterParam);
336 }
337 }
338
InitImagePaint()339 void FlutterRenderOffscreenCanvas::InitImagePaint()
340 {
341 if (smoothingEnabled_) {
342 if (smoothingQuality_ == "low") {
343 imagePaint_.setFilterQuality(SkFilterQuality::kLow_SkFilterQuality);
344 } else if (smoothingQuality_ == "medium") {
345 imagePaint_.setFilterQuality(SkFilterQuality::kMedium_SkFilterQuality);
346 } else if (smoothingQuality_ == "high") {
347 imagePaint_.setFilterQuality(SkFilterQuality::kHigh_SkFilterQuality);
348 } else {
349 LOGE("Unsupported Quality type:%{public}s", smoothingQuality_.c_str());
350 }
351 } else {
352 imagePaint_.setFilterQuality(SkFilterQuality::kNone_SkFilterQuality);
353 }
354 SetPaintImage();
355 }
356
DrawImage(const CanvasImage & canvasImage,double width,double height)357 void FlutterRenderOffscreenCanvas::DrawImage(const CanvasImage& canvasImage, double width, double height)
358 {
359 auto context = pipelineContext_.Upgrade();
360 if (!context) {
361 return;
362 }
363
364 auto image = GreatOrEqual(width, 0) && GreatOrEqual(height, 0)
365 ? ImageProvider::GetSkImage(canvasImage.src, context, Size(width, height))
366 : ImageProvider::GetSkImage(canvasImage.src, context);
367 if (!image) {
368 LOGE("image is null");
369 return;
370 }
371 InitCachePaint();
372 const auto skCanvas =
373 globalState_.GetType() == CompositeOperation::SOURCE_OVER ? skCanvas_.get() : cacheCanvas_.get();
374 InitImagePaint();
375 if (HasImageShadow()) {
376 SkRect skRect = SkRect::MakeXYWH(canvasImage.dx, canvasImage.dy, canvasImage.dWidth, canvasImage.dHeight);
377 SkPath path;
378 path.addRect(skRect);
379 FlutterDecorationPainter::PaintShadow(path, imageShadow_, skCanvas);
380 }
381 switch (canvasImage.flag) {
382 case 0:
383 skCanvas->drawImage(image, canvasImage.dx, canvasImage.dy);
384 break;
385 case 1: {
386 SkRect rect = SkRect::MakeXYWH(canvasImage.dx, canvasImage.dy, canvasImage.dWidth, canvasImage.dHeight);
387 skCanvas->drawImageRect(image, rect, &imagePaint_);
388 break;
389 }
390 case 2: {
391 SkRect dstRect = SkRect::MakeXYWH(canvasImage.dx, canvasImage.dy, canvasImage.dWidth, canvasImage.dHeight);
392 SkRect srcRect = SkRect::MakeXYWH(canvasImage.sx, canvasImage.sy, canvasImage.sWidth, canvasImage.sHeight);
393 skCanvas->drawImageRect(image, srcRect, dstRect, &imagePaint_);
394 break;
395 }
396 default:
397 break;
398 }
399 if (globalState_.GetType() != CompositeOperation::SOURCE_OVER) {
400 skCanvas_->drawBitmap(cacheBitmap_, 0, 0, &cachePaint_);
401 cacheBitmap_.eraseColor(0);
402 }
403 }
404
DrawPixelMap(RefPtr<PixelMap> pixelMap,const CanvasImage & canvasImage)405 void FlutterRenderOffscreenCanvas::DrawPixelMap(RefPtr<PixelMap> pixelMap, const CanvasImage& canvasImage)
406 {
407 auto context = pipelineContext_.Upgrade();
408 if (!context) {
409 return;
410 }
411
412 // get skImage form pixelMap
413 auto imageInfo = ImageProvider::MakeSkImageInfoFromPixelMap(pixelMap);
414 SkPixmap imagePixmap(imageInfo, reinterpret_cast<const void*>(pixelMap->GetPixels()), pixelMap->GetRowBytes());
415
416 // Step2: Create SkImage and draw it, using gpu or cpu
417 sk_sp<SkImage> image =
418 SkImage::MakeFromRaster(imagePixmap, &PixelMap::ReleaseProc, PixelMap::GetReleaseContext(pixelMap));
419 if (!image) {
420 LOGE("image is null");
421 return;
422 }
423
424 InitCachePaint();
425 const auto skCanvas =
426 globalState_.GetType() == CompositeOperation::SOURCE_OVER ? skCanvas_.get() : cacheCanvas_.get();
427 InitImagePaint();
428 switch (canvasImage.flag) {
429 case 0:
430 skCanvas->drawImage(image, canvasImage.dx, canvasImage.dy);
431 break;
432 case 1: {
433 SkRect rect = SkRect::MakeXYWH(canvasImage.dx, canvasImage.dy, canvasImage.dWidth, canvasImage.dHeight);
434 skCanvas->drawImageRect(image, rect, &imagePaint_);
435 break;
436 }
437 case 2: {
438 SkRect dstRect = SkRect::MakeXYWH(canvasImage.dx, canvasImage.dy, canvasImage.dWidth, canvasImage.dHeight);
439 SkRect srcRect = SkRect::MakeXYWH(canvasImage.sx, canvasImage.sy, canvasImage.sWidth, canvasImage.sHeight);
440 skCanvas->drawImageRect(image, srcRect, dstRect, &imagePaint_);
441 break;
442 }
443 default:
444 break;
445 }
446 if (globalState_.GetType() != CompositeOperation::SOURCE_OVER) {
447 skCanvas_->drawBitmap(cacheBitmap_, 0, 0, &cachePaint_);
448 cacheBitmap_.eraseColor(0);
449 }
450 }
451
GetImageData(double left,double top,double width,double height)452 std::unique_ptr<ImageData> FlutterRenderOffscreenCanvas::GetImageData(
453 double left, double top, double width, double height)
454 {
455 double viewScale = 1.0;
456 auto pipeline = pipelineContext_.Upgrade();
457 if (pipeline) {
458 viewScale = pipeline->GetViewScale();
459 }
460 // copy the bitmap to tempCanvas
461 auto imageInfo =
462 SkImageInfo::Make(width, height, SkColorType::kBGRA_8888_SkColorType, SkAlphaType::kOpaque_SkAlphaType);
463 double scaledLeft = left * viewScale;
464 double scaledTop = top * viewScale;
465 double dirtyWidth = width >= 0 ? width : 0;
466 double dirtyHeight = height >= 0 ? height : 0;
467 int32_t size = dirtyWidth * dirtyHeight;
468 auto srcRect = SkRect::MakeXYWH(scaledLeft, scaledTop, width * viewScale, height * viewScale);
469 auto dstRect = SkRect::MakeXYWH(0.0, 0.0, dirtyWidth, dirtyHeight);
470 SkBitmap tempCache;
471 tempCache.allocPixels(imageInfo);
472 SkCanvas tempCanvas(tempCache);
473 #ifdef USE_SYSTEM_SKIA_S
474 tempCanvas.drawImageRect(
475 skBitmap_.asImage(), srcRect, dstRect, SkSamplingOptions(), nullptr, SkCanvas::kFast_SrcRectConstraint);
476 #else
477 tempCanvas.drawBitmapRect(skBitmap_, srcRect, dstRect, nullptr);
478 #endif
479 // write color
480 std::unique_ptr<uint8_t[]> pixels = std::make_unique<uint8_t[]>(size * 4);
481 tempCanvas.readPixels(imageInfo, pixels.get(), dirtyWidth * imageInfo.bytesPerPixel(), 0, 0);
482 std::unique_ptr<ImageData> imageData = std::make_unique<ImageData>();
483 imageData->dirtyWidth = dirtyWidth;
484 imageData->dirtyHeight = dirtyHeight;
485 // a pixel include 4 data blue, green, red, alpha
486 for (int i = 0; i < size * 4; i += 4) {
487 auto blue = pixels[i];
488 auto green = pixels[i + 1];
489 auto red = pixels[i + 2];
490 auto alpha = pixels[i + 3];
491 imageData->data.emplace_back(Color::FromARGB(alpha, red, green, blue));
492 }
493 return imageData;
494 }
495
Save()496 void FlutterRenderOffscreenCanvas::Save()
497 {
498 SaveStates();
499 skCanvas_->save();
500 }
501
Restore()502 void FlutterRenderOffscreenCanvas::Restore()
503 {
504 RestoreStates();
505 skCanvas_->restore();
506 }
507
ToDataURL(const std::string & type,const double quality)508 std::string FlutterRenderOffscreenCanvas::ToDataURL(const std::string& type, const double quality)
509 {
510 auto pipeline = pipelineContext_.Upgrade();
511 if (!pipeline) {
512 return UNSUPPORTED;
513 }
514 std::string mimeType = GetMimeType(type);
515 double qua = GetQuality(type, quality);
516 SkBitmap tempCache;
517 tempCache.allocPixels(SkImageInfo::Make(width_, height_, SkColorType::kBGRA_8888_SkColorType,
518 (mimeType == IMAGE_JPEG) ? SkAlphaType::kOpaque_SkAlphaType : SkAlphaType::kUnpremul_SkAlphaType));
519 SkCanvas tempCanvas(tempCache);
520 double viewScale = pipeline->GetViewScale();
521 tempCanvas.clear(SK_ColorTRANSPARENT);
522 tempCanvas.scale(1.0 / viewScale, 1.0 / viewScale);
523 #ifdef USE_SYSTEM_SKIA_S
524 // The return value of the dual framework interface has no alpha
525 tempCanvas.drawImage(skBitmap_.asImage(), 0.0f, 0.0f);
526 #else
527 tempCanvas.drawBitmap(skBitmap_, 0.0f, 0.0f);
528 #endif
529 SkPixmap src;
530 bool success = tempCache.peekPixels(&src);
531 if (!success) {
532 LOGE("ToDataURL failed,the bitmap does not have access to pixel data");
533 return UNSUPPORTED;
534 }
535 SkDynamicMemoryWStream dst;
536 if (mimeType == IMAGE_JPEG) {
537 SkJpegEncoder::Options options;
538 options.fQuality = qua;
539 success = SkJpegEncoder::Encode(&dst, src, options);
540 } else if (mimeType == IMAGE_WEBP) {
541 SkWebpEncoder::Options options;
542 options.fQuality = qua * 100.0;
543 success = SkWebpEncoder::Encode(&dst, src, options);
544 } else {
545 mimeType = IMAGE_PNG;
546 SkPngEncoder::Options options;
547 success = SkPngEncoder::Encode(&dst, src, options);
548 }
549 if (!success) {
550 LOGE("ToDataURL failed,image encoding failed");
551 return UNSUPPORTED;
552 }
553 auto result = dst.detachAsData();
554 if (result == nullptr) {
555 LOGE("DetachAsData failed when ToDataURL.");
556 return UNSUPPORTED;
557 }
558 size_t len = SkBase64::Encode(result->data(), result->size(), nullptr);
559 if (len > MAX_LENGTH) {
560 LOGE("ToDataURL failed,The resolution of the image is greater than the maximum allowed resolution");
561 return UNSUPPORTED;
562 }
563 SkString info(len);
564 SkBase64::Encode(result->data(), result->size(), info.writable_str());
565 return std::string(URL_PREFIX).append(mimeType).append(URL_SYMBOL).append(info.c_str());
566 }
567
UpdatePaintShader(SkPaint & paint,const Gradient & gradient)568 void FlutterRenderOffscreenCanvas::UpdatePaintShader(SkPaint& paint, const Gradient& gradient)
569 {
570 SkPoint beginPoint = SkPoint::Make(
571 SkDoubleToScalar(gradient.GetBeginOffset().GetX()), SkDoubleToScalar(gradient.GetBeginOffset().GetY()));
572 SkPoint endPoint = SkPoint::Make(
573 SkDoubleToScalar(gradient.GetEndOffset().GetX()), SkDoubleToScalar(gradient.GetEndOffset().GetY()));
574 SkPoint pts[2] = { beginPoint, endPoint };
575 std::vector<GradientColor> gradientColors = gradient.GetColors();
576 std::stable_sort(gradientColors.begin(), gradientColors.end(),
577 [](auto& colorA, auto& colorB) { return colorA.GetDimension() < colorB.GetDimension(); });
578 uint32_t colorsSize = gradientColors.size();
579 SkColor colors[gradientColors.size()];
580 float pos[gradientColors.size()];
581 for (uint32_t i = 0; i < colorsSize; ++i) {
582 const auto& gradientColor = gradientColors[i];
583 colors[i] = gradientColor.GetColor().GetValue();
584 pos[i] = gradientColor.GetDimension().Value();
585 }
586
587 #ifdef USE_SYSTEM_SKIA
588 auto mode = SkShader::kClamp_TileMode;
589 #else
590 auto mode = SkTileMode::kClamp;
591 #endif
592
593 sk_sp<SkShader> skShader = nullptr;
594 if (gradient.GetType() == GradientType::LINEAR) {
595 skShader = SkGradientShader::MakeLinear(pts, colors, pos, gradientColors.size(), mode);
596 } else {
597 if (gradient.GetInnerRadius() <= 0.0 && beginPoint == endPoint) {
598 skShader = SkGradientShader::MakeRadial(
599 endPoint, gradient.GetOuterRadius(), colors, pos, gradientColors.size(), mode);
600 } else {
601 skShader = SkGradientShader::MakeTwoPointConical(beginPoint, gradient.GetInnerRadius(), endPoint,
602 gradient.GetOuterRadius(), colors, pos, gradientColors.size(), mode);
603 }
604 }
605 paint.setShader(skShader);
606 }
607
BeginPath()608 void FlutterRenderOffscreenCanvas::BeginPath()
609 {
610 skPath_.reset();
611 }
612
ResetTransform()613 void FlutterRenderOffscreenCanvas::ResetTransform()
614 {
615 skCanvas_->resetMatrix();
616 }
617
UpdatePaintShader(const Pattern & pattern,SkPaint & paint)618 void FlutterRenderOffscreenCanvas::UpdatePaintShader(const Pattern& pattern, SkPaint& paint)
619 {
620 auto context = pipelineContext_.Upgrade();
621 if (!context) {
622 return;
623 }
624
625 auto width = pattern.GetImageWidth();
626 auto height = pattern.GetImageHeight();
627 auto image = GreatOrEqual(width, 0) && GreatOrEqual(height, 0)
628 ? ImageProvider::GetSkImage(pattern.GetImgSrc(), context, Size(width, height))
629 : ImageProvider::GetSkImage(pattern.GetImgSrc(), context);
630 if (!image) {
631 LOGE("image is null");
632 return;
633 }
634 static const LinearMapNode<void (*)(sk_sp<SkImage>, SkPaint&)> staticPattern[] = {
635 { "no-repeat",
636 [](sk_sp<SkImage> image, SkPaint& paint) {
637 #ifdef USE_SYSTEM_SKIA
638 paint.setShader(image->makeShader(SkShader::kDecal_TileMode, SkShader::kDecal_TileMode, nullptr));
639 #else
640 paint.setShader(image->makeShader(SkTileMode::kDecal, SkTileMode::kDecal, nullptr));
641 #endif
642 } },
643 { "repeat",
644 [](sk_sp<SkImage> image, SkPaint& paint) {
645 #ifdef USE_SYSTEM_SKIA
646 paint.setShader(image->makeShader(SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, nullptr));
647 #else
648 paint.setShader(image->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, nullptr));
649 #endif
650 } },
651 { "repeat-x",
652 [](sk_sp<SkImage> image, SkPaint& paint) {
653 #ifdef USE_SYSTEM_SKIA
654 paint.setShader(image->makeShader(SkShader::kRepeat_TileMode, SkShader::kDecal_TileMode, nullptr));
655 #else
656 paint.setShader(image->makeShader(SkTileMode::kRepeat, SkTileMode::kDecal, nullptr));
657 #endif
658 } },
659 { "repeat-y",
660 [](sk_sp<SkImage> image, SkPaint& paint) {
661 #ifdef USE_SYSTEM_SKIA
662 paint.setShader(image->makeShader(SkShader::kDecal_TileMode, SkShader::kRepeat_TileMode, nullptr));
663 #else
664 paint.setShader(image->makeShader(SkTileMode::kDecal, SkTileMode::kRepeat, nullptr));
665 #endif
666 } },
667 };
668 auto operatorIter = BinarySearchFindIndex(staticPattern, ArraySize(staticPattern), pattern.GetRepetition().c_str());
669 if (operatorIter != -1) {
670 staticPattern[operatorIter].value(image, paint);
671 }
672 }
Arc(const ArcParam & param)673 void FlutterRenderOffscreenCanvas::Arc(const ArcParam& param)
674 {
675 double left = param.x - param.radius;
676 double top = param.y - param.radius;
677 double right = param.x + param.radius;
678 double bottom = param.y + param.radius;
679 double startAngle = param.startAngle * HALF_CIRCLE_ANGLE / M_PI;
680 double endAngle = param.endAngle * HALF_CIRCLE_ANGLE / M_PI;
681 double sweepAngle = endAngle - startAngle;
682 if (param.anticlockwise) {
683 sweepAngle =
684 endAngle > startAngle ? (std::fmod(sweepAngle, FULL_CIRCLE_ANGLE) - FULL_CIRCLE_ANGLE) : sweepAngle;
685 } else {
686 sweepAngle =
687 endAngle > startAngle ? sweepAngle : (std::fmod(sweepAngle, FULL_CIRCLE_ANGLE) + FULL_CIRCLE_ANGLE);
688 }
689 auto rect = SkRect::MakeLTRB(left, top, right, bottom);
690 if (NearEqual(std::fmod(sweepAngle, FULL_CIRCLE_ANGLE), 0.0) && !NearEqual(startAngle, endAngle)) {
691 // draw circle
692 double half = GreatNotEqual(sweepAngle, 0.0) ? HALF_CIRCLE_ANGLE : -HALF_CIRCLE_ANGLE;
693 skPath_.arcTo(rect, SkDoubleToScalar(startAngle), SkDoubleToScalar(half), false);
694 skPath_.arcTo(rect, SkDoubleToScalar(half + startAngle), SkDoubleToScalar(half), false);
695 } else if (!NearEqual(std::fmod(sweepAngle, FULL_CIRCLE_ANGLE), 0.0) && abs(sweepAngle) > FULL_CIRCLE_ANGLE) {
696 double half = GreatNotEqual(sweepAngle, 0.0) ? HALF_CIRCLE_ANGLE : -HALF_CIRCLE_ANGLE;
697 skPath_.arcTo(rect, SkDoubleToScalar(startAngle), SkDoubleToScalar(half), false);
698 skPath_.arcTo(rect, SkDoubleToScalar(half + startAngle), SkDoubleToScalar(half), false);
699 skPath_.arcTo(rect, SkDoubleToScalar(half + half + startAngle), SkDoubleToScalar(sweepAngle), false);
700 } else {
701 skPath_.arcTo(rect, SkDoubleToScalar(startAngle), SkDoubleToScalar(sweepAngle), false);
702 }
703 }
704
ClearRect(Rect rect)705 void FlutterRenderOffscreenCanvas::ClearRect(Rect rect)
706 {
707 SkPaint paint;
708 paint.setAntiAlias(antiAlias_);
709 paint.setBlendMode(SkBlendMode::kClear);
710 auto skRect = SkRect::MakeLTRB(rect.Left(), rect.Top(), rect.Right(), rect.Bottom());
711 skCanvas_->drawRect(skRect, paint);
712 }
713
StrokeRect(Rect rect)714 void FlutterRenderOffscreenCanvas::StrokeRect(Rect rect)
715 {
716 SkPaint paint = GetStrokePaint();
717 paint.setAntiAlias(antiAlias_);
718 SkRect skRect = SkRect::MakeLTRB(rect.Left(), rect.Top(), rect.Right(), rect.Bottom());
719 if (HasShadow()) {
720 SkPath path;
721 path.addRect(skRect);
722 FlutterDecorationPainter::PaintShadow(path, shadow_, skCanvas_.get());
723 }
724 if (strokeState_.GetGradient().IsValid()) {
725 UpdatePaintShader(paint, strokeState_.GetGradient());
726 }
727 if (strokeState_.GetPattern().IsValid()) {
728 UpdatePaintShader(strokeState_.GetPattern(), paint);
729 }
730 if (globalState_.GetType() == CompositeOperation::SOURCE_OVER) {
731 skCanvas_->drawRect(skRect, paint);
732 } else {
733 InitCachePaint();
734 cacheCanvas_->drawRect(skRect, paint);
735 skCanvas_->drawBitmap(cacheBitmap_, 0, 0, &cachePaint_);
736 cacheBitmap_.eraseColor(0);
737 }
738 }
739
Stroke()740 void FlutterRenderOffscreenCanvas::Stroke()
741 {
742 SkPaint paint = GetStrokePaint();
743 paint.setAntiAlias(antiAlias_);
744 if (HasShadow()) {
745 FlutterDecorationPainter::PaintShadow(skPath_, shadow_, skCanvas_.get());
746 }
747 if (strokeState_.GetGradient().IsValid()) {
748 UpdatePaintShader(paint, strokeState_.GetGradient());
749 }
750 if (strokeState_.GetPattern().IsValid()) {
751 UpdatePaintShader(strokeState_.GetPattern(), paint);
752 }
753 if (globalState_.GetType() == CompositeOperation::SOURCE_OVER) {
754 skCanvas_->drawPath(skPath_, paint);
755 } else {
756 InitCachePaint();
757 cacheCanvas_->drawPath(skPath_, paint);
758 skCanvas_->drawBitmap(cacheBitmap_, 0, 0, &cachePaint_);
759 cacheBitmap_.eraseColor(0);
760 }
761 }
762
Stroke(const RefPtr<CanvasPath2D> & path)763 void FlutterRenderOffscreenCanvas::Stroke(const RefPtr<CanvasPath2D>& path)
764 {
765 if (path == nullptr) {
766 return;
767 }
768 ParsePath2D(path);
769 Path2DStroke();
770 skPath2d_.reset();
771 }
GetStrokePaint()772 SkPaint FlutterRenderOffscreenCanvas::GetStrokePaint()
773 {
774 static const LinearEnumMapNode<LineJoinStyle, SkPaint::Join> skLineJoinTable[] = {
775 { LineJoinStyle::MITER, SkPaint::Join::kMiter_Join },
776 { LineJoinStyle::ROUND, SkPaint::Join::kRound_Join },
777 { LineJoinStyle::BEVEL, SkPaint::Join::kBevel_Join },
778 };
779 static const LinearEnumMapNode<LineCapStyle, SkPaint::Cap> skLineCapTable[] = {
780 { LineCapStyle::BUTT, SkPaint::Cap::kButt_Cap },
781 { LineCapStyle::ROUND, SkPaint::Cap::kRound_Cap },
782 { LineCapStyle::SQUARE, SkPaint::Cap::kSquare_Cap },
783 };
784 SkPaint paint;
785 paint.setColor(strokeState_.GetColor().GetValue());
786 paint.setStyle(SkPaint::Style::kStroke_Style);
787 paint.setStrokeJoin(ConvertEnumToSkEnum(
788 strokeState_.GetLineJoin(), skLineJoinTable, ArraySize(skLineJoinTable), SkPaint::Join::kMiter_Join));
789 paint.setStrokeCap(ConvertEnumToSkEnum(
790 strokeState_.GetLineCap(), skLineCapTable, ArraySize(skLineCapTable), SkPaint::Cap::kButt_Cap));
791 paint.setStrokeWidth(static_cast<SkScalar>(strokeState_.GetLineWidth()));
792 paint.setStrokeMiter(static_cast<SkScalar>(strokeState_.GetMiterLimit()));
793
794 // set line Dash
795 UpdateLineDash(paint);
796
797 // set global alpha
798 if (globalState_.HasGlobalAlpha()) {
799 paint.setAlphaf(globalState_.GetAlpha());
800 }
801 return paint;
802 }
SetAntiAlias(bool isEnabled)803 void FlutterRenderOffscreenCanvas::SetAntiAlias(bool isEnabled)
804 {
805 antiAlias_ = isEnabled;
806 }
HasShadow() const807 bool FlutterRenderOffscreenCanvas::HasShadow() const
808 {
809 return !(NearZero(shadow_.GetOffset().GetX()) && NearZero(shadow_.GetOffset().GetY()) &&
810 NearZero(shadow_.GetBlurRadius()));
811 }
812
HasImageShadow() const813 bool FlutterRenderOffscreenCanvas::HasImageShadow() const
814 {
815 return !(NearZero(imageShadow_.GetOffset().GetX()) && NearZero(imageShadow_.GetOffset().GetY()) &&
816 NearZero(imageShadow_.GetBlurRadius()));
817 }
818
Path2DAddPath(const PathArgs & args)819 void FlutterRenderOffscreenCanvas::Path2DAddPath(const PathArgs& args)
820 {
821 SkPath out;
822 SkParsePath::FromSVGString(args.cmds.c_str(), &out);
823 skPath2d_.addPath(out);
824 }
825
Path2DSetTransform(const PathArgs & args)826 void FlutterRenderOffscreenCanvas::Path2DSetTransform(const PathArgs& args)
827 {
828 SkMatrix skMatrix;
829 double scaleX = args.para1;
830 double skewX = args.para2;
831 double skewY = args.para3;
832 double scaleY = args.para4;
833 double translateX = args.para5;
834 double translateY = args.para6;
835 skMatrix.setAll(scaleX, skewY, translateX, skewX, scaleY, translateY, 0, 0, 1);
836 skPath2d_.transform(skMatrix);
837 }
838
Path2DMoveTo(const PathArgs & args)839 void FlutterRenderOffscreenCanvas::Path2DMoveTo(const PathArgs& args)
840 {
841 double x = args.para1;
842 double y = args.para2;
843 skPath2d_.moveTo(x, y);
844 }
845
Path2DLineTo(const PathArgs & args)846 void FlutterRenderOffscreenCanvas::Path2DLineTo(const PathArgs& args)
847 {
848 double x = args.para1;
849 double y = args.para2;
850 skPath2d_.lineTo(x, y);
851 }
852
Path2DArc(const PathArgs & args)853 void FlutterRenderOffscreenCanvas::Path2DArc(const PathArgs& args)
854 {
855 double x = args.para1;
856 double y = args.para2;
857 double r = args.para3;
858 auto rect = SkRect::MakeLTRB(x - r, y - r, x + r, y + r);
859 double startAngle = args.para4 * HALF_CIRCLE_ANGLE / M_PI;
860 double endAngle = args.para5 * HALF_CIRCLE_ANGLE / M_PI;
861 double sweepAngle = endAngle - startAngle;
862 if (!NearZero(args.para6)) {
863 sweepAngle =
864 endAngle > startAngle ? (std::fmod(sweepAngle, FULL_CIRCLE_ANGLE) - FULL_CIRCLE_ANGLE) : sweepAngle;
865 } else {
866 sweepAngle =
867 endAngle > startAngle ? sweepAngle : (std::fmod(sweepAngle, FULL_CIRCLE_ANGLE) + FULL_CIRCLE_ANGLE);
868 }
869 if (NearEqual(std::fmod(sweepAngle, FULL_CIRCLE_ANGLE), 0.0) && !NearEqual(startAngle, endAngle)) {
870 skPath2d_.arcTo(rect, startAngle, HALF_CIRCLE_ANGLE, false);
871 skPath2d_.arcTo(rect, startAngle + HALF_CIRCLE_ANGLE, HALF_CIRCLE_ANGLE, false);
872 } else if (!NearEqual(std::fmod(sweepAngle, FULL_CIRCLE_ANGLE), 0.0) && abs(sweepAngle) > FULL_CIRCLE_ANGLE) {
873 skPath2d_.arcTo(rect, startAngle, HALF_CIRCLE_ANGLE, false);
874 skPath2d_.arcTo(rect, startAngle + HALF_CIRCLE_ANGLE, HALF_CIRCLE_ANGLE, false);
875 skPath2d_.arcTo(rect, startAngle + HALF_CIRCLE_ANGLE + HALF_CIRCLE_ANGLE, sweepAngle, false);
876 } else {
877 skPath2d_.arcTo(rect, startAngle, sweepAngle, false);
878 }
879 }
880
Path2DArcTo(const PathArgs & args)881 void FlutterRenderOffscreenCanvas::Path2DArcTo(const PathArgs& args)
882 {
883 double x1 = args.para1;
884 double y1 = args.para2;
885 double x2 = args.para3;
886 double y2 = args.para4;
887 double r = args.para5;
888 skPath2d_.arcTo(x1, y1, x2, y2, r);
889 }
890
Path2DQuadraticCurveTo(const PathArgs & args)891 void FlutterRenderOffscreenCanvas::Path2DQuadraticCurveTo(const PathArgs& args)
892 {
893 double cpx = args.para1;
894 double cpy = args.para2;
895 double x = args.para3;
896 double y = args.para4;
897 skPath2d_.quadTo(cpx, cpy, x, y);
898 }
899
Path2DBezierCurveTo(const PathArgs & args)900 void FlutterRenderOffscreenCanvas::Path2DBezierCurveTo(const PathArgs& args)
901 {
902 double cp1x = args.para1;
903 double cp1y = args.para2;
904 double cp2x = args.para3;
905 double cp2y = args.para4;
906 double x = args.para5;
907 double y = args.para6;
908 skPath2d_.cubicTo(cp1x, cp1y, cp2x, cp2y, x, y);
909 }
910
Path2DEllipse(const PathArgs & args)911 void FlutterRenderOffscreenCanvas::Path2DEllipse(const PathArgs& args)
912 {
913 if (NearEqual(args.para6, args.para7)) {
914 return; // Just return when startAngle is same as endAngle.
915 }
916
917 double x = args.para1;
918 double y = args.para2;
919 double rx = args.para3;
920 double ry = args.para4;
921 double rotation = args.para5 * HALF_CIRCLE_ANGLE / M_PI;
922 double startAngle = std::fmod(args.para6, M_PI * 2.0);
923 double endAngle = std::fmod(args.para7, M_PI * 2.0);
924 bool anticlockwise = NearZero(args.para8) ? false : true;
925 startAngle = (startAngle < 0.0 ? startAngle + M_PI * 2.0 : startAngle) * HALF_CIRCLE_ANGLE / M_PI;
926 endAngle = (endAngle < 0.0 ? endAngle + M_PI * 2.0 : endAngle) * HALF_CIRCLE_ANGLE / M_PI;
927 double sweepAngle = endAngle - startAngle;
928 if (anticlockwise) {
929 if (sweepAngle > 0.0) { // Make sure the sweepAngle is negative when anticlockwise.
930 sweepAngle -= FULL_CIRCLE_ANGLE;
931 }
932 } else {
933 if (sweepAngle < 0.0) { // Make sure the sweepAngle is positive when clockwise.
934 sweepAngle += FULL_CIRCLE_ANGLE;
935 }
936 }
937 auto rect = SkRect::MakeLTRB(x - rx, y - ry, x + rx, y + ry);
938
939 if (!NearZero(rotation)) {
940 SkMatrix matrix;
941 matrix.setRotate(-rotation, x, y);
942 skPath2d_.transform(matrix);
943 }
944 if (NearZero(sweepAngle) && !NearZero(args.para6 - args.para7)) {
945 // The entire ellipse needs to be drawn with two arcTo.
946 skPath2d_.arcTo(rect, startAngle, HALF_CIRCLE_ANGLE, false);
947 skPath2d_.arcTo(rect, startAngle + HALF_CIRCLE_ANGLE, HALF_CIRCLE_ANGLE, false);
948 } else {
949 skPath2d_.arcTo(rect, startAngle, sweepAngle, false);
950 }
951 if (!NearZero(rotation)) {
952 SkMatrix matrix;
953 matrix.setRotate(rotation, x, y);
954 skPath2d_.transform(matrix);
955 }
956 }
957
Path2DRect(const PathArgs & args)958 void FlutterRenderOffscreenCanvas::Path2DRect(const PathArgs& args)
959 {
960 double left = args.para1;
961 double top = args.para2;
962 double right = args.para3 + args.para1;
963 double bottom = args.para4 + args.para2;
964 skPath2d_.addRect(SkRect::MakeLTRB(left, top, right, bottom));
965 }
966
Path2DClosePath(const PathArgs & args)967 void FlutterRenderOffscreenCanvas::Path2DClosePath(const PathArgs& args)
968 {
969 skPath2d_.close();
970 }
Path2DStroke()971 void FlutterRenderOffscreenCanvas::Path2DStroke()
972 {
973 SkPaint paint = GetStrokePaint();
974 paint.setAntiAlias(antiAlias_);
975 if (HasShadow()) {
976 FlutterDecorationPainter::PaintShadow(skPath2d_, shadow_, skCanvas_.get());
977 }
978 if (strokeState_.GetGradient().IsValid()) {
979 UpdatePaintShader(paint, strokeState_.GetGradient());
980 }
981 if (strokeState_.GetPattern().IsValid()) {
982 UpdatePaintShader(strokeState_.GetPattern(), paint);
983 }
984 if (globalState_.GetType() == CompositeOperation::SOURCE_OVER) {
985 skCanvas_->drawPath(skPath2d_, paint);
986 } else {
987 InitCachePaint();
988 cacheCanvas_->drawPath(skPath2d_, paint);
989 skCanvas_->drawBitmap(cacheBitmap_, 0, 0, &cachePaint_);
990 cacheBitmap_.eraseColor(0);
991 }
992 }
993
Path2DFill()994 void FlutterRenderOffscreenCanvas::Path2DFill()
995 {
996 SkPaint paint;
997 paint.setAntiAlias(antiAlias_);
998 paint.setColor(fillState_.GetColor().GetValue());
999 paint.setStyle(SkPaint::Style::kFill_Style);
1000 if (HasShadow()) {
1001 FlutterDecorationPainter::PaintShadow(skPath2d_, shadow_, skCanvas_.get());
1002 }
1003 if (fillState_.GetGradient().IsValid()) {
1004 UpdatePaintShader(paint, fillState_.GetGradient());
1005 }
1006 if (fillState_.GetPattern().IsValid()) {
1007 UpdatePaintShader(fillState_.GetPattern(), paint);
1008 }
1009 if (globalState_.HasGlobalAlpha()) {
1010 paint.setAlphaf(globalState_.GetAlpha());
1011 }
1012 if (globalState_.GetType() == CompositeOperation::SOURCE_OVER) {
1013 skCanvas_->drawPath(skPath2d_, paint);
1014 } else {
1015 InitCachePaint();
1016 cacheCanvas_->drawPath(skPath2d_, paint);
1017 skCanvas_->drawBitmap(cacheBitmap_, 0, 0, &cachePaint_);
1018 cacheBitmap_.eraseColor(0);
1019 }
1020 }
1021
Path2DClip()1022 void FlutterRenderOffscreenCanvas::Path2DClip()
1023 {
1024 skCanvas_->clipPath(skPath2d_);
1025 }
1026
UpdateLineDash(SkPaint & paint)1027 void FlutterRenderOffscreenCanvas::UpdateLineDash(SkPaint& paint)
1028 {
1029 if (!strokeState_.GetLineDash().lineDash.empty()) {
1030 auto lineDashState = strokeState_.GetLineDash().lineDash;
1031 SkScalar intervals[lineDashState.size()];
1032 for (size_t i = 0; i < lineDashState.size(); ++i) {
1033 intervals[i] = SkDoubleToScalar(lineDashState[i]);
1034 }
1035 SkScalar phase = SkDoubleToScalar(strokeState_.GetLineDash().dashOffset);
1036 paint.setPathEffect(SkDashPathEffect::Make(intervals, lineDashState.size(), phase));
1037 }
1038 }
ArcTo(const ArcToParam & param)1039 void FlutterRenderOffscreenCanvas::ArcTo(const ArcToParam& param)
1040 {
1041 double x1 = param.x1;
1042 double y1 = param.y1;
1043 double x2 = param.x2;
1044 double y2 = param.y2;
1045 double radius = param.radius;
1046 skPath_.arcTo(SkDoubleToScalar(x1), SkDoubleToScalar(y1), SkDoubleToScalar(x2), SkDoubleToScalar(y2),
1047 SkDoubleToScalar(radius));
1048 }
MoveTo(double x,double y)1049 void FlutterRenderOffscreenCanvas::MoveTo(double x, double y)
1050 {
1051 skPath_.moveTo(SkDoubleToScalar(x), SkDoubleToScalar(y));
1052 }
ClosePath()1053 void FlutterRenderOffscreenCanvas::ClosePath()
1054 {
1055 skPath_.close();
1056 }
1057
Rotate(double angle)1058 void FlutterRenderOffscreenCanvas::Rotate(double angle)
1059 {
1060 skCanvas_->rotate(angle * 180 / M_PI);
1061 }
Scale(double x,double y)1062 void FlutterRenderOffscreenCanvas::Scale(double x, double y)
1063 {
1064 skCanvas_->scale(x, y);
1065 }
1066
FillText(const std::string & text,double x,double y,const PaintState & state)1067 void FlutterRenderOffscreenCanvas::FillText(const std::string& text, double x, double y, const PaintState& state)
1068 {
1069 if (!UpdateOffParagraph(text, false, state, HasShadow())) {
1070 return;
1071 }
1072 PaintText(text, x, y, false, HasShadow());
1073 }
1074
StrokeText(const std::string & text,double x,double y,const PaintState & state)1075 void FlutterRenderOffscreenCanvas::StrokeText(const std::string& text, double x, double y, const PaintState& state)
1076 {
1077 if (HasShadow()) {
1078 if (!UpdateOffParagraph(text, true, state, true)) {
1079 return;
1080 }
1081 PaintText(text, x, y, true, true);
1082 }
1083
1084 if (!UpdateOffParagraph(text, true, state)) {
1085 return;
1086 }
1087 PaintText(text, x, y, true);
1088 }
1089
MeasureText(const std::string & text,const PaintState & state)1090 double FlutterRenderOffscreenCanvas::MeasureText(const std::string& text, const PaintState& state)
1091 {
1092 using namespace Constants;
1093 txt::ParagraphStyle style;
1094 style.text_align = ConvertTxtTextAlign(state.GetTextAlign());
1095 style.text_direction = ConvertTxtTextDirection(state.GetOffTextDirection());
1096 auto fontCollection = FlutterFontCollection::GetInstance().GetFontCollection();
1097 if (!fontCollection) {
1098 LOGW("MeasureText: fontCollection is null");
1099 return 0.0;
1100 }
1101 std::unique_ptr<txt::ParagraphBuilder> builder = txt::ParagraphBuilder::CreateTxtBuilder(style, fontCollection);
1102 txt::TextStyle txtStyle;
1103 ConvertTxtStyle(state.GetTextStyle(), pipelineContext_, txtStyle);
1104 txtStyle.font_size = state.GetTextStyle().GetFontSize().Value();
1105 builder->PushStyle(txtStyle);
1106 builder->AddText(StringUtils::Str8ToStr16(text));
1107 auto paragraph = builder->Build();
1108 paragraph->Layout(Size::INFINITE_SIZE);
1109 return paragraph->GetMaxIntrinsicWidth();
1110 }
1111
MeasureTextHeight(const std::string & text,const PaintState & state)1112 double FlutterRenderOffscreenCanvas::MeasureTextHeight(const std::string& text, const PaintState& state)
1113 {
1114 using namespace Constants;
1115 txt::ParagraphStyle style;
1116 style.text_align = ConvertTxtTextAlign(state.GetTextAlign());
1117 style.text_direction = ConvertTxtTextDirection(state.GetOffTextDirection());
1118 auto fontCollection = FlutterFontCollection::GetInstance().GetFontCollection();
1119 if (!fontCollection) {
1120 LOGW("MeasureText: fontCollection is null");
1121 return 0.0;
1122 }
1123 std::unique_ptr<txt::ParagraphBuilder> builder = txt::ParagraphBuilder::CreateTxtBuilder(style, fontCollection);
1124 txt::TextStyle txtStyle;
1125 ConvertTxtStyle(state.GetTextStyle(), pipelineContext_, txtStyle);
1126 txtStyle.font_size = state.GetTextStyle().GetFontSize().Value();
1127 builder->PushStyle(txtStyle);
1128 builder->AddText(StringUtils::Str8ToStr16(text));
1129 auto paragraph = builder->Build();
1130 paragraph->Layout(Size::INFINITE_SIZE);
1131 return paragraph->GetHeight();
1132 }
1133
MeasureTextMetrics(const std::string & text,const PaintState & state)1134 TextMetrics FlutterRenderOffscreenCanvas::MeasureTextMetrics(const std::string& text, const PaintState& state)
1135 {
1136 using namespace Constants;
1137 txt::ParagraphStyle style;
1138 style.text_align = ConvertTxtTextAlign(state.GetTextAlign());
1139 style.text_direction = ConvertTxtTextDirection(state.GetOffTextDirection());
1140 auto fontCollection = FlutterFontCollection::GetInstance().GetFontCollection();
1141 if (!fontCollection) {
1142 LOGW("MeasureText: fontCollection is null");
1143 return { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
1144 }
1145 std::unique_ptr<txt::ParagraphBuilder> builder = txt::ParagraphBuilder::CreateTxtBuilder(style, fontCollection);
1146 txt::TextStyle txtStyle;
1147 ConvertTxtStyle(state.GetTextStyle(), pipelineContext_, txtStyle);
1148 txtStyle.font_size = state.GetTextStyle().GetFontSize().Value();
1149 builder->PushStyle(txtStyle);
1150 builder->AddText(StringUtils::Str8ToStr16(text));
1151 auto paragraph = builder->Build();
1152 paragraph->Layout(Size::INFINITE_SIZE);
1153
1154 auto textAlign = state.GetTextAlign();
1155 auto textBaseLine = state.GetTextStyle().GetTextBaseline();
1156
1157 auto width = paragraph->GetMaxIntrinsicWidth();
1158 auto height = paragraph->GetHeight();
1159
1160 auto actualBoundingBoxLeft = -GetAlignOffset(text, textAlign, paragraph);
1161 auto actualBoundingBoxRight = width - actualBoundingBoxLeft;
1162 auto actualBoundingBoxAscent = -GetBaselineOffset(textBaseLine, paragraph);
1163 auto actualBoundingBoxDescent = height - actualBoundingBoxAscent;
1164
1165 return { width, height, actualBoundingBoxLeft, actualBoundingBoxRight, actualBoundingBoxAscent,
1166 actualBoundingBoxDescent };
1167 }
1168
PaintText(const std::string & text,double x,double y,bool isStroke,bool hasShadow)1169 void FlutterRenderOffscreenCanvas::PaintText(const std::string& text, double x, double y, bool isStroke, bool hasShadow)
1170 {
1171 paragraph_->Layout(width_);
1172 if (width_ > paragraph_->GetMaxIntrinsicWidth()) {
1173 paragraph_->Layout(std::ceil(paragraph_->GetMaxIntrinsicWidth()));
1174 }
1175 auto align = isStroke ? strokeState_.GetTextAlign() : fillState_.GetTextAlign();
1176 double dx = x + GetAlignOffset(text, align, paragraph_);
1177 auto baseline =
1178 isStroke ? strokeState_.GetTextStyle().GetTextBaseline() : fillState_.GetTextStyle().GetTextBaseline();
1179 double dy = y + GetBaselineOffset(baseline, paragraph_);
1180
1181 if (hasShadow) {
1182 skCanvas_->save();
1183 auto shadowOffsetX = shadow_.GetOffset().GetX();
1184 auto shadowOffsetY = shadow_.GetOffset().GetY();
1185 paragraph_->Paint(skCanvas_.get(), dx + shadowOffsetX, dy + shadowOffsetY);
1186 skCanvas_->restore();
1187 return;
1188 }
1189 paragraph_->Paint(skCanvas_.get(), dx, dy);
1190 }
1191
GetAlignOffset(const std::string & text,TextAlign align,std::unique_ptr<txt::Paragraph> & paragraph)1192 double FlutterRenderOffscreenCanvas::GetAlignOffset(
1193 const std::string& text, TextAlign align, std::unique_ptr<txt::Paragraph>& paragraph)
1194 {
1195 double x = 0.0;
1196 switch (align) {
1197 case TextAlign::LEFT:
1198 x = 0.0;
1199 break;
1200 case TextAlign::START:
1201 x = (GetTextDirection(text) == TextDirection::LTR) ? 0.0 : -paragraph->GetMaxIntrinsicWidth();
1202 break;
1203 case TextAlign::RIGHT:
1204 x = -paragraph->GetMaxIntrinsicWidth();
1205 break;
1206 case TextAlign::END:
1207 x = (GetTextDirection(text) == TextDirection::LTR) ? -paragraph->GetMaxIntrinsicWidth() : 0.0;
1208 break;
1209 case TextAlign::CENTER:
1210 x = -paragraph->GetMaxIntrinsicWidth() / 2;
1211 break;
1212 default:
1213 x = 0.0;
1214 break;
1215 }
1216 return x;
1217 }
1218
GetTextDirection(const std::string & text)1219 TextDirection FlutterRenderOffscreenCanvas::GetTextDirection(const std::string& text)
1220 {
1221 auto wstring = StringUtils::ToWstring(text);
1222 // Find first strong direction char.
1223 for (const auto& charInStr : wstring) {
1224 auto direction = u_charDirection(charInStr);
1225 if (direction == UCharDirection::U_LEFT_TO_RIGHT) {
1226 return TextDirection::LTR;
1227 }
1228 if (direction == UCharDirection::U_RIGHT_TO_LEFT || direction == UCharDirection::U_RIGHT_TO_LEFT_ARABIC) {
1229 return TextDirection::RTL;
1230 }
1231 }
1232 return TextDirection::INHERIT;
1233 }
1234
InitCachePaint()1235 void FlutterRenderOffscreenCanvas::InitCachePaint()
1236 {
1237 cachePaint_.setBlendMode(
1238 ConvertEnumToSkEnum(globalState_.GetType(), SK_BLEND_MODE_TABLE, BLEND_MODE_SIZE, SkBlendMode::kSrcOver));
1239 }
1240
UpdateOffParagraph(const std::string & text,bool isStroke,const PaintState & state,bool hasShadow)1241 bool FlutterRenderOffscreenCanvas::UpdateOffParagraph(
1242 const std::string& text, bool isStroke, const PaintState& state, bool hasShadow)
1243 {
1244 using namespace Constants;
1245 txt::ParagraphStyle style;
1246 if (isStroke) {
1247 style.text_align = ConvertTxtTextAlign(strokeState_.GetTextAlign());
1248 } else {
1249 style.text_align = ConvertTxtTextAlign(fillState_.GetTextAlign());
1250 }
1251 style.text_direction = ConvertTxtTextDirection(state.GetOffTextDirection());
1252 auto fontCollection = FlutterFontCollection::GetInstance().GetFontCollection();
1253 if (!fontCollection) {
1254 return false;
1255 }
1256 std::unique_ptr<txt::ParagraphBuilder> builder = txt::ParagraphBuilder::CreateTxtBuilder(style, fontCollection);
1257 txt::TextStyle txtStyle;
1258 if (!isStroke && hasShadow) {
1259 txt::TextShadow txtShadow;
1260 txtShadow.color = shadow_.GetColor().GetValue();
1261 txtShadow.offset.fX = shadow_.GetOffset().GetX();
1262 txtShadow.offset.fY = shadow_.GetOffset().GetY();
1263 txtShadow.blur_radius = shadow_.GetBlurRadius();
1264 txtStyle.text_shadows.emplace_back(txtShadow);
1265 }
1266 txtStyle.locale = Localization::GetInstance()->GetFontLocale();
1267 UpdateTextStyleForeground(isStroke, txtStyle, hasShadow);
1268 builder->PushStyle(txtStyle);
1269 builder->AddText(StringUtils::Str8ToStr16(text));
1270 paragraph_ = builder->Build();
1271 return true;
1272 }
1273
UpdateTextStyleForeground(bool isStroke,txt::TextStyle & txtStyle,bool hasShadow)1274 void FlutterRenderOffscreenCanvas::UpdateTextStyleForeground(bool isStroke, txt::TextStyle& txtStyle, bool hasShadow)
1275 {
1276 using namespace Constants;
1277 if (!isStroke) {
1278 txtStyle.color = ConvertSkColor(fillState_.GetColor());
1279 txtStyle.font_size = fillState_.GetTextStyle().GetFontSize().Value();
1280 ConvertTxtStyle(fillState_.GetTextStyle(), pipelineContext_, txtStyle);
1281 if (fillState_.GetGradient().IsValid()) {
1282 SkPaint paint;
1283 paint.setStyle(SkPaint::Style::kFill_Style);
1284 UpdatePaintShader(paint, fillState_.GetGradient());
1285 txtStyle.foreground = paint;
1286 txtStyle.has_foreground = true;
1287 }
1288 if (globalState_.HasGlobalAlpha()) {
1289 if (txtStyle.has_foreground) {
1290 txtStyle.foreground.setColor(fillState_.GetColor().GetValue());
1291 txtStyle.foreground.setAlphaf(globalState_.GetAlpha()); // set alpha after color
1292 } else {
1293 SkPaint paint;
1294 paint.setColor(fillState_.GetColor().GetValue());
1295 paint.setAlphaf(globalState_.GetAlpha()); // set alpha after color
1296 txtStyle.foreground = paint;
1297 txtStyle.has_foreground = true;
1298 }
1299 }
1300 } else {
1301 // use foreground to draw stroke
1302 SkPaint paint = GetStrokePaint();
1303 ConvertTxtStyle(strokeState_.GetTextStyle(), pipelineContext_, txtStyle);
1304 txtStyle.font_size = strokeState_.GetTextStyle().GetFontSize().Value();
1305 if (strokeState_.GetGradient().IsValid()) {
1306 UpdatePaintShader(paint, strokeState_.GetGradient());
1307 }
1308 if (hasShadow) {
1309 paint.setColor(shadow_.GetColor().GetValue());
1310 paint.setMaskFilter(SkMaskFilter::MakeBlur(SkBlurStyle::kNormal_SkBlurStyle,
1311 FlutterDecorationPainter::ConvertRadiusToSigma(shadow_.GetBlurRadius())));
1312 }
1313 txtStyle.foreground = paint;
1314 txtStyle.has_foreground = true;
1315 }
1316 }
1317
GetBaselineOffset(TextBaseline baseline,std::unique_ptr<txt::Paragraph> & paragraph)1318 double FlutterRenderOffscreenCanvas::GetBaselineOffset(
1319 TextBaseline baseline, std::unique_ptr<txt::Paragraph>& paragraph)
1320 {
1321 double y = 0.0;
1322 switch (baseline) {
1323 case TextBaseline::ALPHABETIC:
1324 y = -paragraph->GetAlphabeticBaseline();
1325 break;
1326 case TextBaseline::IDEOGRAPHIC:
1327 y = -paragraph->GetIdeographicBaseline();
1328 break;
1329 case TextBaseline::BOTTOM:
1330 y = -paragraph->GetHeight();
1331 break;
1332 case TextBaseline::TOP:
1333 y = 0.0;
1334 break;
1335 case TextBaseline::MIDDLE:
1336 y = -paragraph->GetHeight() / 2;
1337 break;
1338 case TextBaseline::HANGING:
1339 y = -HANGING_PERCENT * (paragraph->GetHeight() - paragraph->GetAlphabeticBaseline());
1340 break;
1341 default:
1342 y = -paragraph->GetAlphabeticBaseline();
1343 break;
1344 }
1345 return y;
1346 }
LineTo(double x,double y)1347 void FlutterRenderOffscreenCanvas::LineTo(double x, double y)
1348 {
1349 skPath_.lineTo(SkDoubleToScalar(x), SkDoubleToScalar(y));
1350 }
BezierCurveTo(const BezierCurveParam & param)1351 void FlutterRenderOffscreenCanvas::BezierCurveTo(const BezierCurveParam& param)
1352 {
1353 skPath_.cubicTo(SkDoubleToScalar(param.cp1x), SkDoubleToScalar(param.cp1y), SkDoubleToScalar(param.cp2x),
1354 SkDoubleToScalar(param.cp2y), SkDoubleToScalar(param.x), SkDoubleToScalar(param.y));
1355 }
QuadraticCurveTo(const QuadraticCurveParam & param)1356 void FlutterRenderOffscreenCanvas::QuadraticCurveTo(const QuadraticCurveParam& param)
1357 {
1358 skPath_.quadTo(
1359 SkDoubleToScalar(param.cpx), SkDoubleToScalar(param.cpy), SkDoubleToScalar(param.x), SkDoubleToScalar(param.y));
1360 }
Ellipse(const EllipseParam & param)1361 void FlutterRenderOffscreenCanvas::Ellipse(const EllipseParam& param)
1362 {
1363 // Init the start and end angle, then calculated the sweepAngle.
1364 double startAngle = std::fmod(param.startAngle, M_PI * 2.0);
1365 double endAngle = std::fmod(param.endAngle, M_PI * 2.0);
1366 startAngle = (startAngle < 0.0 ? startAngle + M_PI * 2.0 : startAngle) * HALF_CIRCLE_ANGLE / M_PI;
1367 endAngle = (endAngle < 0.0 ? endAngle + M_PI * 2.0 : endAngle) * HALF_CIRCLE_ANGLE / M_PI;
1368 if (NearEqual(param.startAngle, param.endAngle)) {
1369 return; // Just return when startAngle is same as endAngle.
1370 }
1371 double rotation = param.rotation * HALF_CIRCLE_ANGLE / M_PI;
1372 double sweepAngle = endAngle - startAngle;
1373 if (param.anticlockwise) {
1374 if (sweepAngle > 0.0) { // Make sure the sweepAngle is negative when anticlockwise.
1375 sweepAngle -= FULL_CIRCLE_ANGLE;
1376 }
1377 } else {
1378 if (sweepAngle < 0.0) { // Make sure the sweepAngle is positive when clockwise.
1379 sweepAngle += FULL_CIRCLE_ANGLE;
1380 }
1381 }
1382
1383 // Init the oval Rect(left, top, right, bottom).
1384 double left = param.x - param.radiusX;
1385 double top = param.y - param.radiusY;
1386 double right = param.x + param.radiusX;
1387 double bottom = param.y + param.radiusY;
1388 auto rect = SkRect::MakeLTRB(left, top, right, bottom);
1389 if (!NearZero(rotation)) {
1390 SkMatrix matrix;
1391 matrix.setRotate(-rotation, param.x, param.y);
1392 skPath_.transform(matrix);
1393 }
1394 if (NearZero(sweepAngle) && !NearZero(param.endAngle - param.startAngle)) {
1395 // The entire ellipse needs to be drawn with two arcTo.
1396 skPath_.arcTo(rect, startAngle, HALF_CIRCLE_ANGLE, false);
1397 skPath_.arcTo(rect, startAngle + HALF_CIRCLE_ANGLE, HALF_CIRCLE_ANGLE, false);
1398 } else {
1399 skPath_.arcTo(rect, startAngle, sweepAngle, false);
1400 }
1401 if (!NearZero(rotation)) {
1402 SkMatrix matrix;
1403 matrix.setRotate(rotation, param.x, param.y);
1404 skPath_.transform(matrix);
1405 }
1406 }
SetTransform(const TransformParam & param)1407 void FlutterRenderOffscreenCanvas::SetTransform(const TransformParam& param)
1408 {
1409 auto pipeline = pipelineContext_.Upgrade();
1410 if (!pipeline) {
1411 return;
1412 }
1413
1414 // use physical pixel to store bitmap
1415 double viewScale = pipeline->GetViewScale();
1416
1417 SkMatrix skMatrix;
1418 skMatrix.setAll(param.scaleX * viewScale, param.skewY * viewScale, param.translateX, param.skewX * viewScale,
1419 param.scaleY * viewScale, param.translateY, 0, 0, 1);
1420 skCanvas_->setMatrix(skMatrix);
1421 }
Transform(const TransformParam & param)1422 void FlutterRenderOffscreenCanvas::Transform(const TransformParam& param)
1423 {
1424 SkMatrix skMatrix;
1425 skMatrix.setAll(param.scaleX, param.skewY, param.translateX, param.skewX, param.scaleY, param.translateY, 0, 0, 1);
1426 skCanvas_->concat(skMatrix);
1427 }
Translate(double x,double y)1428 void FlutterRenderOffscreenCanvas::Translate(double x, double y)
1429 {
1430 skCanvas_->translate(x, y);
1431 }
1432
TranspareCmdToPath(const RefPtr<CanvasPath2D> & path)1433 void FlutterRenderOffscreenCanvas::TranspareCmdToPath(const RefPtr<CanvasPath2D>& path)
1434 {
1435 skPath2d_.reset();
1436 for (const auto& [cmd, args] : path->GetCaches()) {
1437 switch (cmd) {
1438 case PathCmd::CMDS: {
1439 Path2DAddPath(args);
1440 break;
1441 }
1442 case PathCmd::TRANSFORM: {
1443 Path2DSetTransform(args);
1444 break;
1445 }
1446 case PathCmd::MOVE_TO: {
1447 Path2DMoveTo(args);
1448 break;
1449 }
1450 case PathCmd::LINE_TO: {
1451 Path2DLineTo(args);
1452 break;
1453 }
1454 case PathCmd::ARC: {
1455 Path2DArc(args);
1456 break;
1457 }
1458 case PathCmd::ARC_TO: {
1459 Path2DArcTo(args);
1460 break;
1461 }
1462 case PathCmd::QUADRATIC_CURVE_TO: {
1463 Path2DQuadraticCurveTo(args);
1464 break;
1465 }
1466 case PathCmd::BEZIER_CURVE_TO: {
1467 Path2DBezierCurveTo(args);
1468 break;
1469 }
1470 case PathCmd::ELLIPSE: {
1471 Path2DEllipse(args);
1472 break;
1473 }
1474 case PathCmd::RECT: {
1475 Path2DRect(args);
1476 break;
1477 }
1478 case PathCmd::CLOSE_PATH: {
1479 Path2DClosePath(args);
1480 break;
1481 }
1482 default: {
1483 break;
1484 }
1485 }
1486 }
1487 }
1488
IsPointInPathByColor(double x,double y,SkPath & path,SkColor colorMatch)1489 bool FlutterRenderOffscreenCanvas::IsPointInPathByColor(double x, double y, SkPath& path, SkColor colorMatch)
1490 {
1491 auto imageInfo =
1492 SkImageInfo::Make(width_, height_, SkColorType::kRGBA_8888_SkColorType, SkAlphaType::kOpaque_SkAlphaType);
1493 SkBitmap skBitmap;
1494 skBitmap.allocPixels(imageInfo);
1495 std::unique_ptr<SkCanvas> skCanvas = std::make_unique<SkCanvas>(skBitmap);
1496
1497 SkPaint paint;
1498 paint.setColor(SK_ColorRED);
1499 paint.setStyle(SkPaint::Style::kFill_Style);
1500 skCanvas->drawPath(path, paint);
1501
1502 paint.setColor(SK_ColorBLUE);
1503 paint.setStyle(SkPaint::Style::kStroke_Style);
1504 paint.setStrokeWidth(static_cast<SkScalar>(strokeState_.GetLineWidth()));
1505 skCanvas->drawPath(path, paint);
1506
1507 SkColor color = skBitmap.getColor(x, y);
1508 if (color == colorMatch) {
1509 return true;
1510 }
1511 return false;
1512 }
1513
IsPointInPath(double x,double y)1514 bool FlutterRenderOffscreenCanvas::IsPointInPath(double x, double y)
1515 {
1516 return IsPointInPathByColor(x, y, skPath_, SK_ColorRED);
1517 }
1518
IsPointInPath(const RefPtr<CanvasPath2D> & path,double x,double y)1519 bool FlutterRenderOffscreenCanvas::IsPointInPath(const RefPtr<CanvasPath2D>& path, double x, double y)
1520 {
1521 TranspareCmdToPath(path);
1522 return IsPointInPathByColor(x, y, skPath2d_, SK_ColorRED);
1523 }
1524
IsPointInStroke(double x,double y)1525 bool FlutterRenderOffscreenCanvas::IsPointInStroke(double x, double y)
1526 {
1527 return IsPointInPathByColor(x, y, skPath_, SK_ColorBLUE);
1528 }
1529
IsPointInStroke(const RefPtr<CanvasPath2D> & path,double x,double y)1530 bool FlutterRenderOffscreenCanvas::IsPointInStroke(const RefPtr<CanvasPath2D>& path, double x, double y)
1531 {
1532 TranspareCmdToPath(path);
1533 return IsPointInPathByColor(x, y, skPath2d_, SK_ColorBLUE);
1534 }
1535
InitFilterFunc()1536 void FlutterRenderOffscreenCanvas::InitFilterFunc()
1537 {
1538 filterFunc_["grayscale"] = [&](const std::string& percentage) { SetGrayFilter(percentage); };
1539 filterFunc_["sepia"] = [&](const std::string& percentage) { SetSepiaFilter(percentage); };
1540 filterFunc_["invert"] = [&](const std::string& percentage) { SetInvertFilter(percentage); };
1541 filterFunc_["opacity"] = [&](const std::string& percentage) { SetOpacityFilter(percentage); };
1542 filterFunc_["brightness"] = [&](const std::string& percentage) { SetBrightnessFilter(percentage); };
1543 filterFunc_["contrast"] = [&](const std::string& percentage) { SetContrastFilter(percentage); };
1544 filterFunc_["blur"] = [&](const std::string& percentage) { SetBlurFilter(percentage); };
1545 filterFunc_["drop-shadow"] = [&](const std::string& percentage) { SetDropShadowFilter(percentage); };
1546 filterFunc_["saturate"] = [&](const std::string& percentage) { SetSaturateFilter(percentage); };
1547 filterFunc_["hue-rotate"] = [&](const std::string& percentage) { SetHueRotateFilter(percentage); };
1548 }
1549
GetFilterType(std::string & filterType,std::string & filterParam)1550 bool FlutterRenderOffscreenCanvas::GetFilterType(std::string& filterType, std::string& filterParam)
1551 {
1552 std::string paramData = filterParam_;
1553 size_t index = paramData.find("(");
1554 if (index == std::string::npos) {
1555 return false;
1556 }
1557 filterType = paramData.substr(0, index);
1558 filterParam = paramData.substr(index + 1);
1559 size_t endeIndex = filterParam.find(")");
1560 if (endeIndex == std::string::npos) {
1561 return false;
1562 }
1563 filterParam.erase(endeIndex, 1);
1564 return true;
1565 }
1566
IsPercentStr(std::string & percent)1567 bool FlutterRenderOffscreenCanvas::IsPercentStr(std::string& percent)
1568 {
1569 if (percent.find("%") != std::string::npos) {
1570 size_t index = percent.find("%");
1571 percent = percent.substr(0, index);
1572 return true;
1573 }
1574 return false;
1575 }
1576
PxStrToDouble(const std::string & str)1577 double FlutterRenderOffscreenCanvas::PxStrToDouble(const std::string& str)
1578 {
1579 double ret = 0;
1580 size_t index = str.find("px");
1581 if (index != std::string::npos) {
1582 std::string result = str.substr(0, index);
1583 std::istringstream iss(result);
1584 iss >> ret;
1585 }
1586 return ret;
1587 }
1588
BlurStrToDouble(const std::string & str)1589 double FlutterRenderOffscreenCanvas::BlurStrToDouble(const std::string& str)
1590 {
1591 double ret = 0;
1592 size_t index = str.find("px");
1593 size_t index1 = str.find("rem");
1594 size_t demIndex = std::string::npos;
1595 if (index != std::string::npos) {
1596 demIndex = index;
1597 }
1598 if (index1 != std::string::npos) {
1599 demIndex = index1;
1600 }
1601 if (demIndex != std::string::npos) {
1602 std::string result = str.substr(0, demIndex);
1603 std::istringstream iss(result);
1604 iss >> ret;
1605 }
1606 if (str.find("rem") != std::string::npos) {
1607 return ret * 15;
1608 }
1609 return ret;
1610 }
1611
SetGrayFilter(const std::string & percent)1612 void FlutterRenderOffscreenCanvas::SetGrayFilter(const std::string& percent)
1613 {
1614 std::string percentage = percent;
1615 bool hasPercent = IsPercentStr(percentage);
1616 float percentNum = 0.0f;
1617 std::istringstream iss(percentage);
1618 iss >> percentNum;
1619 if (hasPercent) {
1620 percentNum = percentNum / 100;
1621 }
1622 if (percentNum > 1) {
1623 percentNum = 1;
1624 }
1625 float otherColor = percentNum / 3;
1626 float primColor = 1 - 2 * otherColor;
1627 float matrix[20] = { 0 };
1628 matrix[0] = matrix[6] = matrix[12] = primColor;
1629 matrix[1] = matrix[2] = matrix[5] = matrix[7] = matrix[10] = matrix[11] = otherColor;
1630 matrix[18] = 1.0f;
1631 SetColorFilter(matrix);
1632 }
1633
SetSepiaFilter(const std::string & percent)1634 void FlutterRenderOffscreenCanvas::SetSepiaFilter(const std::string& percent)
1635 {
1636 std::string percentage = percent;
1637 bool hasPercent = IsPercentStr(percentage);
1638 float percentNum = 0.0f;
1639 std::istringstream iss(percentage);
1640 iss >> percentNum;
1641 if (hasPercent) {
1642 percentNum = percentNum / 100;
1643 }
1644 if (percentNum > 1) {
1645 percentNum = 1;
1646 }
1647 float matrix[20] = { 0 };
1648 matrix[0] = 1.0f - percentNum * 0.6412f;
1649 matrix[1] = percentNum * 0.7044f;
1650 matrix[2] = percentNum * 0.1368f;
1651 matrix[5] = percentNum * 0.2990f;
1652 matrix[6] = 1.0f - percentNum * 0.4130f;
1653 matrix[7] = percentNum * 0.1140f;
1654 matrix[10] = percentNum * 0.2392f;
1655 matrix[11] = percentNum * 0.4696f;
1656 matrix[12] = 1.0f - percentNum * 0.9088f;
1657 matrix[18] = 1.0f;
1658 SetColorFilter(matrix);
1659 }
1660
SetInvertFilter(const std::string & filterParam)1661 void FlutterRenderOffscreenCanvas::SetInvertFilter(const std::string& filterParam)
1662 {
1663 std::string percent = filterParam;
1664 bool hasPercent = IsPercentStr(percent);
1665 float percentage = 0.0f;
1666 std::istringstream iss(percent);
1667 iss >> percentage;
1668 if (hasPercent) {
1669 percentage = percentage / 100;
1670 }
1671
1672 float matrix[20] = { 0 };
1673 matrix[0] = matrix[6] = matrix[12] = 1.0 - 2.0 * percentage;
1674 matrix[4] = matrix[9] = matrix[14] = percentage;
1675 matrix[18] = 1.0f;
1676 SetColorFilter(matrix);
1677 }
1678
SetOpacityFilter(const std::string & filterParam)1679 void FlutterRenderOffscreenCanvas::SetOpacityFilter(const std::string& filterParam)
1680 {
1681 std::string percent = filterParam;
1682 bool hasPercent = IsPercentStr(percent);
1683 float percentage = 0.0f;
1684 std::istringstream iss(percent);
1685 iss >> percentage;
1686 if (hasPercent) {
1687 percentage = percentage / 100;
1688 }
1689
1690 float matrix[20] = { 0 };
1691 matrix[0] = matrix[6] = matrix[12] = 1.0f;
1692 matrix[18] = percentage;
1693 SetColorFilter(matrix);
1694 }
1695
SetBrightnessFilter(const std::string & percent)1696 void FlutterRenderOffscreenCanvas::SetBrightnessFilter(const std::string& percent)
1697 {
1698 std::string perStr = percent;
1699 bool hasPercent = IsPercentStr(perStr);
1700 float percentage = 0.0f;
1701 std::istringstream iss(perStr);
1702 iss >> percentage;
1703 if (hasPercent) {
1704 percentage = percentage / 100;
1705 }
1706
1707 if (percentage < 0) {
1708 return;
1709 }
1710 float matrix[20] = { 0 };
1711 matrix[0] = matrix[6] = matrix[12] = percentage;
1712 matrix[18] = 1.0f;
1713 SetColorFilter(matrix);
1714 }
1715
SetContrastFilter(const std::string & percent)1716 void FlutterRenderOffscreenCanvas::SetContrastFilter(const std::string& percent)
1717 {
1718 std::string perStr = percent;
1719 float percentage = 0.0f;
1720 bool hasPercent = IsPercentStr(perStr);
1721 std::istringstream iss(perStr);
1722 iss >> percentage;
1723 if (hasPercent) {
1724 percentage = percentage / 100;
1725 }
1726
1727 float matrix[20] = { 0 };
1728 matrix[0] = matrix[6] = matrix[12] = percentage;
1729 matrix[4] = matrix[9] = matrix[14] = 0.5f * (1 - percentage);
1730 matrix[18] = 1;
1731 SetColorFilter(matrix);
1732 }
1733
SetBlurFilter(const std::string & percent)1734 void FlutterRenderOffscreenCanvas::SetBlurFilter(const std::string& percent)
1735 {
1736 imagePaint_.setImageFilter(SkBlurImageFilter::Make(BlurStrToDouble(percent), BlurStrToDouble(percent), nullptr));
1737 }
1738
SetDropShadowFilter(const std::string & percent)1739 void FlutterRenderOffscreenCanvas::SetDropShadowFilter(const std::string& percent)
1740 {
1741 std::vector<std::string> offsets;
1742 StringUtils::StringSplitter(percent, ' ', offsets);
1743 if (offsets.empty() || offsets.size() != 4) {
1744 return;
1745 }
1746 imageShadow_.SetOffsetX(PxStrToDouble(offsets[0]));
1747 imageShadow_.SetOffsetY(PxStrToDouble(offsets[1]));
1748 imageShadow_.SetBlurRadius(PxStrToDouble(offsets[2]));
1749 imageShadow_.SetColor(Color::FromString(offsets[3]));
1750 }
1751
SetSaturateFilter(const std::string & filterParam)1752 void FlutterRenderOffscreenCanvas::SetSaturateFilter(const std::string& filterParam)
1753 {
1754 std::string percent = filterParam;
1755 bool hasPercent = IsPercentStr(percent);
1756 float percentage = 0.0f;
1757 std::istringstream iss(percent);
1758 iss >> percentage;
1759 if (hasPercent) {
1760 percentage = percentage / 100;
1761 }
1762 double N = percentage;
1763 float matrix[20] = { 0 };
1764 matrix[0] = 0.3086f * (1 - N) + N;
1765 matrix[1] = matrix[11] = 0.6094f * (1 - N);
1766 matrix[2] = matrix[7] = 0.0820f * (1 - N);
1767 matrix[5] = matrix[10] = 0.3086f * (1 - N);
1768 matrix[6] = 0.6094f * (1 - N) + N;
1769 matrix[12] = 0.0820f * (1 - N) + N;
1770 matrix[18] = 1.0f;
1771 SetColorFilter(matrix);
1772 }
1773
SetHueRotateFilter(const std::string & filterParam)1774 void FlutterRenderOffscreenCanvas::SetHueRotateFilter(const std::string& filterParam)
1775 {
1776 std::string percent = filterParam;
1777 float degree = 0.0f;
1778 if (percent.find("deg") != std::string::npos) {
1779 size_t index = percent.find("deg");
1780 percent = percent.substr(0, index);
1781 std::istringstream iss(percent);
1782 iss >> degree;
1783 }
1784 if (percent.find("turn") != std::string::npos) {
1785 size_t index = percent.find("turn");
1786 percent = percent.substr(0, index);
1787 std::istringstream iss(percent);
1788 iss >> degree;
1789 degree = degree * 360;
1790 }
1791 if (percent.find("rad") != std::string::npos) {
1792 size_t index = percent.find("rad");
1793 percent = percent.substr(0, index);
1794 std::istringstream iss(percent);
1795 iss >> degree;
1796 degree = degree * 180 / 3.142f;
1797 }
1798
1799 while (GreatOrEqual(degree, 360)) {
1800 degree -= 360;
1801 }
1802
1803 float matrix[20] = { 0 };
1804 int32_t type = degree / 120;
1805 float N = (degree - 120 * type) / 120;
1806 switch (type) {
1807 case 0:
1808 // degree is in 0-120
1809 matrix[0] = matrix[6] = matrix[12] = 1 - N;
1810 matrix[2] = matrix[5] = matrix[11] = N;
1811 matrix[18] = 1.0f;
1812 break;
1813 case 1:
1814 // degree is in 120-240
1815 matrix[1] = matrix[7] = matrix[10] = N;
1816 matrix[2] = matrix[5] = matrix[11] = 1 - N;
1817 matrix[18] = 1.0f;
1818 break;
1819 case 2:
1820 // degree is in 240-360
1821 matrix[0] = matrix[6] = matrix[11] = N;
1822 matrix[1] = matrix[7] = matrix[10] = 1 - N;
1823 matrix[18] = 1.0f;
1824 break;
1825 default:
1826 break;
1827 }
1828 SetColorFilter(matrix);
1829 }
1830
SetColorFilter(float matrix[20])1831 void FlutterRenderOffscreenCanvas::SetColorFilter(float matrix[20])
1832 {
1833 #ifdef USE_SYSTEM_SKIA
1834 matrix[4] *= 255;
1835 matrix[9] *= 255;
1836 matrix[14] *= 255;
1837 matrix[19] *= 255;
1838 imagePaint_.setColorFilter(SkColorFilter::MakeMatrixFilterRowMajor255(matrix));
1839 #else
1840 imagePaint_.setColorFilter(SkColorFilters::Matrix(matrix));
1841 #endif
1842 }
1843 } // namespace OHOS::Ace
1844