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 | |
24 | using tonic::ToDart; |
25 | |
26 | namespace flutter { |
27 | |
28 | static void Canvas_constructor(Dart_NativeArguments args) { |
29 | UIDartState::ThrowIfUIOperationsProhibited(); |
30 | DartCallConstructor(&Canvas::Create, args); |
31 | } |
32 | |
33 | IMPLEMENT_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 | |
68 | FOR_EACH_BINDING(DART_NATIVE_CALLBACK) |
69 | |
70 | void Canvas::RegisterNatives(tonic::DartLibraryNatives* natives) { |
71 | natives->Register({{"Canvas_constructor" , Canvas_constructor, 6, true}, |
72 | FOR_EACH_BINDING(DART_REGISTER_NATIVE)}); |
73 | } |
74 | |
75 | fml::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 | |
91 | Canvas::Canvas(SkCanvas* canvas) : canvas_(canvas) {} |
92 | |
93 | Canvas::~Canvas() {} |
94 | |
95 | void Canvas::save() { |
96 | if (!canvas_) { |
97 | return; |
98 | } |
99 | canvas_->save(); |
100 | } |
101 | |
102 | void Canvas::saveLayerWithoutBounds(const Paint& paint, |
103 | const PaintData& paint_data) { |
104 | if (!canvas_) { |
105 | return; |
106 | } |
107 | canvas_->saveLayer(nullptr, paint.paint()); |
108 | } |
109 | |
110 | void 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 | |
123 | void Canvas::restore() { |
124 | if (!canvas_) { |
125 | return; |
126 | } |
127 | canvas_->restore(); |
128 | } |
129 | |
130 | int Canvas::getSaveCount() { |
131 | if (!canvas_) { |
132 | return 0; |
133 | } |
134 | return canvas_->getSaveCount(); |
135 | } |
136 | |
137 | void Canvas::translate(double dx, double dy) { |
138 | if (!canvas_) { |
139 | return; |
140 | } |
141 | canvas_->translate(dx, dy); |
142 | } |
143 | |
144 | void Canvas::scale(double sx, double sy) { |
145 | if (!canvas_) { |
146 | return; |
147 | } |
148 | canvas_->scale(sx, sy); |
149 | } |
150 | |
151 | void Canvas::rotate(double radians) { |
152 | if (!canvas_) { |
153 | return; |
154 | } |
155 | canvas_->rotate(radians * 180.0 / M_PI); |
156 | } |
157 | |
158 | void Canvas::skew(double sx, double sy) { |
159 | if (!canvas_) { |
160 | return; |
161 | } |
162 | canvas_->skew(sx, sy); |
163 | } |
164 | |
165 | void Canvas::transform(const tonic::Float64List& matrix4) { |
166 | if (!canvas_) { |
167 | return; |
168 | } |
169 | canvas_->concat(ToSkMatrix(matrix4)); |
170 | } |
171 | |
172 | void 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 | |
185 | void Canvas::clipRRect(const RRect& rrect, bool doAntiAlias) { |
186 | if (!canvas_) { |
187 | return; |
188 | } |
189 | canvas_->clipRRect(rrect.sk_rrect, doAntiAlias); |
190 | } |
191 | |
192 | void 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 | |
205 | void Canvas::drawColor(SkColor color, SkBlendMode blend_mode) { |
206 | if (!canvas_) { |
207 | return; |
208 | } |
209 | canvas_->drawColor(color, blend_mode); |
210 | } |
211 | |
212 | void 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 | |
224 | void Canvas::drawPaint(const Paint& paint, const PaintData& paint_data) { |
225 | if (!canvas_) { |
226 | return; |
227 | } |
228 | canvas_->drawPaint(*paint.paint()); |
229 | } |
230 | |
231 | void 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 | |
243 | void 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 | |
252 | void 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 | |
262 | void 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 | |
274 | void 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 | |
285 | void 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 | |
302 | void 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 | |
317 | void 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 | |
334 | void 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 | |
360 | void 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 | |
388 | void 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 | |
401 | void 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 | |
418 | void 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 | |
434 | void 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 | |
469 | void 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 | |
488 | void Canvas::Invalidate() { |
489 | canvas_ = nullptr; |
490 | if (dart_wrapper()) { |
491 | ClearDartWrapper(); |
492 | } |
493 | } |
494 | |
495 | } // namespace flutter |
496 | |