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