1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "flutter/lib/ui/painting/canvas.h"
6
7#define _USE_MATH_DEFINES
8#include <math.h>
9
10#include "flutter/flow/layers/physical_shape_layer.h"
11#include "flutter/lib/ui/painting/image.h"
12#include "flutter/lib/ui/painting/matrix.h"
13#include "flutter/lib/ui/ui_dart_state.h"
14#include "flutter/lib/ui/window/platform_configuration.h"
15#include "flutter/lib/ui/window/window.h"
16#include "third_party/skia/include/core/SkBitmap.h"
17#include "third_party/skia/include/core/SkCanvas.h"
18#include "third_party/skia/include/core/SkRSXform.h"
19#include "third_party/tonic/converter/dart_converter.h"
20#include "third_party/tonic/dart_args.h"
21#include "third_party/tonic/dart_binding_macros.h"
22#include "third_party/tonic/dart_library_natives.h"
23
24using tonic::ToDart;
25
26namespace flutter {
27
28static void Canvas_constructor(Dart_NativeArguments args) {
29 UIDartState::ThrowIfUIOperationsProhibited();
30 DartCallConstructor(&Canvas::Create, args);
31}
32
33IMPLEMENT_WRAPPERTYPEINFO(ui, Canvas);
34
35#define FOR_EACH_BINDING(V) \
36 V(Canvas, save) \
37 V(Canvas, saveLayerWithoutBounds) \
38 V(Canvas, saveLayer) \
39 V(Canvas, restore) \
40 V(Canvas, getSaveCount) \
41 V(Canvas, translate) \
42 V(Canvas, scale) \
43 V(Canvas, rotate) \
44 V(Canvas, skew) \
45 V(Canvas, transform) \
46 V(Canvas, clipRect) \
47 V(Canvas, clipRRect) \
48 V(Canvas, clipPath) \
49 V(Canvas, drawColor) \
50 V(Canvas, drawLine) \
51 V(Canvas, drawPaint) \
52 V(Canvas, drawRect) \
53 V(Canvas, drawRRect) \
54 V(Canvas, drawDRRect) \
55 V(Canvas, drawOval) \
56 V(Canvas, drawCircle) \
57 V(Canvas, drawArc) \
58 V(Canvas, drawPath) \
59 V(Canvas, drawImage) \
60 V(Canvas, drawImageRect) \
61 V(Canvas, drawImageNine) \
62 V(Canvas, drawPicture) \
63 V(Canvas, drawPoints) \
64 V(Canvas, drawVertices) \
65 V(Canvas, drawAtlas) \
66 V(Canvas, drawShadow)
67
68FOR_EACH_BINDING(DART_NATIVE_CALLBACK)
69
70void Canvas::RegisterNatives(tonic::DartLibraryNatives* natives) {
71 natives->Register({{"Canvas_constructor", Canvas_constructor, 6, true},
72 FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
73}
74
75fml::RefPtr<Canvas> Canvas::Create(PictureRecorder* recorder,
76 double left,
77 double top,
78 double right,
79 double bottom) {
80 if (!recorder) {
81 Dart_ThrowException(
82 ToDart("Canvas constructor called with non-genuine PictureRecorder."));
83 return nullptr;
84 }
85 fml::RefPtr<Canvas> canvas = fml::MakeRefCounted<Canvas>(
86 recorder->BeginRecording(SkRect::MakeLTRB(left, top, right, bottom)));
87 recorder->set_canvas(canvas);
88 return canvas;
89}
90
91Canvas::Canvas(SkCanvas* canvas) : canvas_(canvas) {}
92
93Canvas::~Canvas() {}
94
95void Canvas::save() {
96 if (!canvas_) {
97 return;
98 }
99 canvas_->save();
100}
101
102void Canvas::saveLayerWithoutBounds(const Paint& paint,
103 const PaintData& paint_data) {
104 if (!canvas_) {
105 return;
106 }
107 canvas_->saveLayer(nullptr, paint.paint());
108}
109
110void Canvas::saveLayer(double left,
111 double top,
112 double right,
113 double bottom,
114 const Paint& paint,
115 const PaintData& paint_data) {
116 if (!canvas_) {
117 return;
118 }
119 SkRect bounds = SkRect::MakeLTRB(left, top, right, bottom);
120 canvas_->saveLayer(&bounds, paint.paint());
121}
122
123void Canvas::restore() {
124 if (!canvas_) {
125 return;
126 }
127 canvas_->restore();
128}
129
130int Canvas::getSaveCount() {
131 if (!canvas_) {
132 return 0;
133 }
134 return canvas_->getSaveCount();
135}
136
137void Canvas::translate(double dx, double dy) {
138 if (!canvas_) {
139 return;
140 }
141 canvas_->translate(dx, dy);
142}
143
144void Canvas::scale(double sx, double sy) {
145 if (!canvas_) {
146 return;
147 }
148 canvas_->scale(sx, sy);
149}
150
151void Canvas::rotate(double radians) {
152 if (!canvas_) {
153 return;
154 }
155 canvas_->rotate(radians * 180.0 / M_PI);
156}
157
158void Canvas::skew(double sx, double sy) {
159 if (!canvas_) {
160 return;
161 }
162 canvas_->skew(sx, sy);
163}
164
165void Canvas::transform(const tonic::Float64List& matrix4) {
166 if (!canvas_) {
167 return;
168 }
169 canvas_->concat(ToSkMatrix(matrix4));
170}
171
172void Canvas::clipRect(double left,
173 double top,
174 double right,
175 double bottom,
176 SkClipOp clipOp,
177 bool doAntiAlias) {
178 if (!canvas_) {
179 return;
180 }
181 canvas_->clipRect(SkRect::MakeLTRB(left, top, right, bottom), clipOp,
182 doAntiAlias);
183}
184
185void Canvas::clipRRect(const RRect& rrect, bool doAntiAlias) {
186 if (!canvas_) {
187 return;
188 }
189 canvas_->clipRRect(rrect.sk_rrect, doAntiAlias);
190}
191
192void Canvas::clipPath(const CanvasPath* path, bool doAntiAlias) {
193 if (!canvas_) {
194 return;
195 }
196 if (!path) {
197 Dart_ThrowException(
198 ToDart("Canvas.clipPath called with non-genuine Path."));
199 return;
200 }
201 external_allocation_size_ += path->path().approximateBytesUsed();
202 canvas_->clipPath(path->path(), doAntiAlias);
203}
204
205void Canvas::drawColor(SkColor color, SkBlendMode blend_mode) {
206 if (!canvas_) {
207 return;
208 }
209 canvas_->drawColor(color, blend_mode);
210}
211
212void Canvas::drawLine(double x1,
213 double y1,
214 double x2,
215 double y2,
216 const Paint& paint,
217 const PaintData& paint_data) {
218 if (!canvas_) {
219 return;
220 }
221 canvas_->drawLine(x1, y1, x2, y2, *paint.paint());
222}
223
224void Canvas::drawPaint(const Paint& paint, const PaintData& paint_data) {
225 if (!canvas_) {
226 return;
227 }
228 canvas_->drawPaint(*paint.paint());
229}
230
231void Canvas::drawRect(double left,
232 double top,
233 double right,
234 double bottom,
235 const Paint& paint,
236 const PaintData& paint_data) {
237 if (!canvas_) {
238 return;
239 }
240 canvas_->drawRect(SkRect::MakeLTRB(left, top, right, bottom), *paint.paint());
241}
242
243void Canvas::drawRRect(const RRect& rrect,
244 const Paint& paint,
245 const PaintData& paint_data) {
246 if (!canvas_) {
247 return;
248 }
249 canvas_->drawRRect(rrect.sk_rrect, *paint.paint());
250}
251
252void Canvas::drawDRRect(const RRect& outer,
253 const RRect& inner,
254 const Paint& paint,
255 const PaintData& paint_data) {
256 if (!canvas_) {
257 return;
258 }
259 canvas_->drawDRRect(outer.sk_rrect, inner.sk_rrect, *paint.paint());
260}
261
262void Canvas::drawOval(double left,
263 double top,
264 double right,
265 double bottom,
266 const Paint& paint,
267 const PaintData& paint_data) {
268 if (!canvas_) {
269 return;
270 }
271 canvas_->drawOval(SkRect::MakeLTRB(left, top, right, bottom), *paint.paint());
272}
273
274void Canvas::drawCircle(double x,
275 double y,
276 double radius,
277 const Paint& paint,
278 const PaintData& paint_data) {
279 if (!canvas_) {
280 return;
281 }
282 canvas_->drawCircle(x, y, radius, *paint.paint());
283}
284
285void Canvas::drawArc(double left,
286 double top,
287 double right,
288 double bottom,
289 double startAngle,
290 double sweepAngle,
291 bool useCenter,
292 const Paint& paint,
293 const PaintData& paint_data) {
294 if (!canvas_) {
295 return;
296 }
297 canvas_->drawArc(SkRect::MakeLTRB(left, top, right, bottom),
298 startAngle * 180.0 / M_PI, sweepAngle * 180.0 / M_PI,
299 useCenter, *paint.paint());
300}
301
302void Canvas::drawPath(const CanvasPath* path,
303 const Paint& paint,
304 const PaintData& paint_data) {
305 if (!canvas_) {
306 return;
307 }
308 if (!path) {
309 Dart_ThrowException(
310 ToDart("Canvas.drawPath called with non-genuine Path."));
311 return;
312 }
313 external_allocation_size_ += path->path().approximateBytesUsed();
314 canvas_->drawPath(path->path(), *paint.paint());
315}
316
317void Canvas::drawImage(const CanvasImage* image,
318 double x,
319 double y,
320 const Paint& paint,
321 const PaintData& paint_data) {
322 if (!canvas_) {
323 return;
324 }
325 if (!image) {
326 Dart_ThrowException(
327 ToDart("Canvas.drawImage called with non-genuine Image."));
328 return;
329 }
330 external_allocation_size_ += image->GetAllocationSize();
331 canvas_->drawImage(image->image(), x, y, paint.paint());
332}
333
334void Canvas::drawImageRect(const CanvasImage* image,
335 double src_left,
336 double src_top,
337 double src_right,
338 double src_bottom,
339 double dst_left,
340 double dst_top,
341 double dst_right,
342 double dst_bottom,
343 const Paint& paint,
344 const PaintData& paint_data) {
345 if (!canvas_) {
346 return;
347 }
348 if (!image) {
349 Dart_ThrowException(
350 ToDart("Canvas.drawImageRect called with non-genuine Image."));
351 return;
352 }
353 SkRect src = SkRect::MakeLTRB(src_left, src_top, src_right, src_bottom);
354 SkRect dst = SkRect::MakeLTRB(dst_left, dst_top, dst_right, dst_bottom);
355 external_allocation_size_ += image->GetAllocationSize();
356 canvas_->drawImageRect(image->image(), src, dst, paint.paint(),
357 SkCanvas::kFast_SrcRectConstraint);
358}
359
360void Canvas::drawImageNine(const CanvasImage* image,
361 double center_left,
362 double center_top,
363 double center_right,
364 double center_bottom,
365 double dst_left,
366 double dst_top,
367 double dst_right,
368 double dst_bottom,
369 const Paint& paint,
370 const PaintData& paint_data) {
371 if (!canvas_) {
372 return;
373 }
374 if (!image) {
375 Dart_ThrowException(
376 ToDart("Canvas.drawImageNine called with non-genuine Image."));
377 return;
378 }
379 SkRect center =
380 SkRect::MakeLTRB(center_left, center_top, center_right, center_bottom);
381 SkIRect icenter;
382 center.round(&icenter);
383 SkRect dst = SkRect::MakeLTRB(dst_left, dst_top, dst_right, dst_bottom);
384 external_allocation_size_ += image->GetAllocationSize();
385 canvas_->drawImageNine(image->image(), icenter, dst, paint.paint());
386}
387
388void Canvas::drawPicture(Picture* picture) {
389 if (!canvas_) {
390 return;
391 }
392 if (!picture) {
393 Dart_ThrowException(
394 ToDart("Canvas.drawPicture called with non-genuine Picture."));
395 return;
396 }
397 external_allocation_size_ += picture->GetAllocationSize();
398 canvas_->drawPicture(picture->picture().get());
399}
400
401void Canvas::drawPoints(const Paint& paint,
402 const PaintData& paint_data,
403 SkCanvas::PointMode point_mode,
404 const tonic::Float32List& points) {
405 if (!canvas_) {
406 return;
407 }
408
409 static_assert(sizeof(SkPoint) == sizeof(float) * 2,
410 "SkPoint doesn't use floats.");
411
412 canvas_->drawPoints(point_mode,
413 points.num_elements() / 2, // SkPoints have two floats.
414 reinterpret_cast<const SkPoint*>(points.data()),
415 *paint.paint());
416}
417
418void Canvas::drawVertices(const Vertices* vertices,
419 SkBlendMode blend_mode,
420 const Paint& paint,
421 const PaintData& paint_data) {
422 if (!canvas_) {
423 return;
424 }
425 if (!vertices) {
426 Dart_ThrowException(
427 ToDart("Canvas.drawVertices called with non-genuine Vertices."));
428 return;
429 }
430 external_allocation_size_ += vertices->GetAllocationSize();
431 canvas_->drawVertices(vertices->vertices(), blend_mode, *paint.paint());
432}
433
434void Canvas::drawAtlas(const Paint& paint,
435 const PaintData& paint_data,
436 CanvasImage* atlas,
437 const tonic::Float32List& transforms,
438 const tonic::Float32List& rects,
439 const tonic::Int32List& colors,
440 SkBlendMode blend_mode,
441 const tonic::Float32List& cull_rect) {
442 if (!canvas_) {
443 return;
444 }
445 if (!atlas) {
446 Dart_ThrowException(
447 ToDart("Canvas.drawAtlas or Canvas.drawRawAtlas called with "
448 "non-genuine Image."));
449 return;
450 }
451
452 sk_sp<SkImage> skImage = atlas->image();
453
454 static_assert(sizeof(SkRSXform) == sizeof(float) * 4,
455 "SkRSXform doesn't use floats.");
456 static_assert(sizeof(SkRect) == sizeof(float) * 4,
457 "SkRect doesn't use floats.");
458
459 external_allocation_size_ += atlas->GetAllocationSize();
460 canvas_->drawAtlas(
461 skImage.get(), reinterpret_cast<const SkRSXform*>(transforms.data()),
462 reinterpret_cast<const SkRect*>(rects.data()),
463 reinterpret_cast<const SkColor*>(colors.data()),
464 rects.num_elements() / 4, // SkRect have four floats.
465 blend_mode, reinterpret_cast<const SkRect*>(cull_rect.data()),
466 paint.paint());
467}
468
469void Canvas::drawShadow(const CanvasPath* path,
470 SkColor color,
471 double elevation,
472 bool transparentOccluder) {
473 if (!path) {
474 Dart_ThrowException(
475 ToDart("Canvas.drawShader called with non-genuine Path."));
476 return;
477 }
478 SkScalar dpr = UIDartState::Current()
479 ->platform_configuration()
480 ->window()
481 ->viewport_metrics()
482 .device_pixel_ratio;
483 external_allocation_size_ += path->path().approximateBytesUsed();
484 flutter::PhysicalShapeLayer::DrawShadow(canvas_, path->path(), color,
485 elevation, transparentOccluder, dpr);
486}
487
488void Canvas::Invalidate() {
489 canvas_ = nullptr;
490 if (dart_wrapper()) {
491 ClearDartWrapper();
492 }
493}
494
495} // namespace flutter
496