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