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