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