• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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