blob: 121cb0270fc426df5bd09b996381dba6ec38c8a1 [file] [log] [blame] [edit]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/lib/ui/painting/canvas.h"
#include <cmath>
#include "flutter/display_list/dl_builder.h"
#include "flutter/lib/ui/floating_point.h"
#include "flutter/lib/ui/painting/image.h"
#include "flutter/lib/ui/painting/image_filter.h"
#include "flutter/lib/ui/painting/paint.h"
#include "flutter/lib/ui/ui_dart_state.h"
#include "flutter/lib/ui/window/platform_configuration.h"
using tonic::ToDart;
namespace flutter {
IMPLEMENT_WRAPPERTYPEINFO(ui, Canvas);
void Canvas::Create(Dart_Handle wrapper,
PictureRecorder* recorder,
double left,
double top,
double right,
double bottom) {
UIDartState::ThrowIfUIOperationsProhibited();
if (!recorder) {
Dart_ThrowException(
ToDart("Canvas constructor called with non-genuine PictureRecorder."));
return;
}
fml::RefPtr<Canvas> canvas =
fml::MakeRefCounted<Canvas>(recorder->BeginRecording(
DlRect::MakeLTRB(SafeNarrow(left), SafeNarrow(top), SafeNarrow(right),
SafeNarrow(bottom))));
recorder->set_canvas(canvas);
canvas->AssociateWithDartWrapper(wrapper);
}
Canvas::Canvas(sk_sp<DisplayListBuilder> builder)
: display_list_builder_(std::move(builder)) {}
Canvas::~Canvas() {}
void Canvas::save() {
if (display_list_builder_) {
builder()->Save();
}
}
void Canvas::saveLayerWithoutBounds(Dart_Handle paint_objects,
Dart_Handle paint_data) {
Paint paint(paint_objects, paint_data);
FML_DCHECK(paint.isNotNull());
if (display_list_builder_) {
DlPaint dl_paint;
const DlPaint* save_paint =
paint.paint(dl_paint, kSaveLayerWithPaintFlags, DlTileMode::kDecal);
FML_DCHECK(save_paint);
TRACE_EVENT0("flutter", "ui.Canvas::saveLayer (Recorded)");
builder()->SaveLayer(nullptr, save_paint);
}
}
void Canvas::saveLayer(double left,
double top,
double right,
double bottom,
Dart_Handle paint_objects,
Dart_Handle paint_data) {
Paint paint(paint_objects, paint_data);
FML_DCHECK(paint.isNotNull());
DlRect bounds = DlRect::MakeLTRB(SafeNarrow(left), SafeNarrow(top),
SafeNarrow(right), SafeNarrow(bottom));
if (display_list_builder_) {
DlPaint dl_paint;
const DlPaint* save_paint =
paint.paint(dl_paint, kSaveLayerWithPaintFlags, DlTileMode::kDecal);
FML_DCHECK(save_paint);
TRACE_EVENT0("flutter", "ui.Canvas::saveLayer (Recorded)");
builder()->SaveLayer(bounds, save_paint);
}
}
void Canvas::restore() {
if (display_list_builder_) {
builder()->Restore();
}
}
int Canvas::getSaveCount() {
if (display_list_builder_) {
return builder()->GetSaveCount();
} else {
return 0;
}
}
void Canvas::restoreToCount(int count) {
if (display_list_builder_ && count < getSaveCount()) {
builder()->RestoreToCount(count);
}
}
void Canvas::translate(double dx, double dy) {
if (display_list_builder_) {
builder()->Translate(SafeNarrow(dx), SafeNarrow(dy));
}
}
void Canvas::scale(double sx, double sy) {
if (display_list_builder_) {
builder()->Scale(SafeNarrow(sx), SafeNarrow(sy));
}
}
void Canvas::rotate(double radians) {
if (display_list_builder_) {
builder()->Rotate(SafeNarrow(radians) * 180.0f / static_cast<float>(M_PI));
}
}
void Canvas::skew(double sx, double sy) {
if (display_list_builder_) {
builder()->Skew(SafeNarrow(sx), SafeNarrow(sy));
}
}
void Canvas::transform(const tonic::Float64List& matrix4) {
// The Float array stored by Dart Matrix4 is in column-major order
// DisplayList TransformFullPerspective takes row-major matrix order
if (display_list_builder_) {
// clang-format off
builder()->TransformFullPerspective(
SafeNarrow(matrix4[ 0]), SafeNarrow(matrix4[ 4]), SafeNarrow(matrix4[ 8]), SafeNarrow(matrix4[12]),
SafeNarrow(matrix4[ 1]), SafeNarrow(matrix4[ 5]), SafeNarrow(matrix4[ 9]), SafeNarrow(matrix4[13]),
SafeNarrow(matrix4[ 2]), SafeNarrow(matrix4[ 6]), SafeNarrow(matrix4[10]), SafeNarrow(matrix4[14]),
SafeNarrow(matrix4[ 3]), SafeNarrow(matrix4[ 7]), SafeNarrow(matrix4[11]), SafeNarrow(matrix4[15]));
// clang-format on
}
}
void Canvas::getTransform(Dart_Handle matrix4_handle) {
if (display_list_builder_) {
// The Float array stored by DlMatrix is in column-major order
DlMatrix matrix = builder()->GetMatrix();
auto matrix4 = tonic::Float64List(matrix4_handle);
for (int i = 0; i < 16; i++) {
matrix4[i] = matrix.m[i];
}
}
}
void Canvas::clipRect(double left,
double top,
double right,
double bottom,
DlCanvas::ClipOp clipOp,
bool doAntiAlias) {
if (display_list_builder_) {
builder()->ClipRect(DlRect::MakeLTRB(SafeNarrow(left), SafeNarrow(top),
SafeNarrow(right), SafeNarrow(bottom)),
clipOp, doAntiAlias);
}
}
void Canvas::clipRRect(const RRect& rrect, bool doAntiAlias) {
if (display_list_builder_) {
builder()->ClipRoundRect(rrect.rrect, DlCanvas::ClipOp::kIntersect,
doAntiAlias);
}
}
void Canvas::clipPath(const CanvasPath* path, bool doAntiAlias) {
if (!path) {
Dart_ThrowException(
ToDart("Canvas.clipPath called with non-genuine Path."));
return;
}
if (display_list_builder_) {
builder()->ClipPath(path->path(), DlCanvas::ClipOp::kIntersect,
doAntiAlias);
}
}
void Canvas::getDestinationClipBounds(Dart_Handle rect_handle) {
if (display_list_builder_) {
auto rect = tonic::Float64List(rect_handle);
DlRect bounds = builder()->GetDestinationClipCoverage();
rect[0] = bounds.GetLeft();
rect[1] = bounds.GetTop();
rect[2] = bounds.GetRight();
rect[3] = bounds.GetBottom();
}
}
void Canvas::getLocalClipBounds(Dart_Handle rect_handle) {
if (display_list_builder_) {
auto rect = tonic::Float64List(rect_handle);
DlRect bounds = builder()->GetLocalClipCoverage();
rect[0] = bounds.GetLeft();
rect[1] = bounds.GetTop();
rect[2] = bounds.GetRight();
rect[3] = bounds.GetBottom();
}
}
void Canvas::drawColor(uint32_t color, DlBlendMode blend_mode) {
if (display_list_builder_) {
builder()->DrawColor(DlColor(color), blend_mode);
}
}
void Canvas::drawLine(double x1,
double y1,
double x2,
double y2,
Dart_Handle paint_objects,
Dart_Handle paint_data) {
Paint paint(paint_objects, paint_data);
FML_DCHECK(paint.isNotNull());
if (display_list_builder_) {
DlPaint dl_paint;
paint.paint(dl_paint, kDrawLineFlags, DlTileMode::kDecal);
builder()->DrawLine(DlPoint(SafeNarrow(x1), SafeNarrow(y1)),
DlPoint(SafeNarrow(x2), SafeNarrow(y2)), dl_paint);
}
}
void Canvas::drawPaint(Dart_Handle paint_objects, Dart_Handle paint_data) {
Paint paint(paint_objects, paint_data);
FML_DCHECK(paint.isNotNull());
if (display_list_builder_) {
DlPaint dl_paint;
paint.paint(dl_paint, kDrawPaintFlags, DlTileMode::kClamp);
std::shared_ptr<DlImageFilter> filter = dl_paint.getImageFilter();
if (filter && !filter->asColorFilter()) {
// drawPaint does an implicit saveLayer if an DlImageFilter is
// present that cannot be replaced by an DlColorFilter.
TRACE_EVENT0("flutter", "ui.Canvas::saveLayer (Recorded)");
}
builder()->DrawPaint(dl_paint);
}
}
void Canvas::drawRect(double left,
double top,
double right,
double bottom,
Dart_Handle paint_objects,
Dart_Handle paint_data) {
Paint paint(paint_objects, paint_data);
FML_DCHECK(paint.isNotNull());
if (display_list_builder_) {
DlPaint dl_paint;
paint.paint(dl_paint, kDrawRectFlags, DlTileMode::kDecal);
builder()->DrawRect(DlRect::MakeLTRB(SafeNarrow(left), SafeNarrow(top),
SafeNarrow(right), SafeNarrow(bottom)),
dl_paint);
}
}
void Canvas::drawRRect(const RRect& rrect,
Dart_Handle paint_objects,
Dart_Handle paint_data) {
Paint paint(paint_objects, paint_data);
FML_DCHECK(paint.isNotNull());
if (display_list_builder_) {
DlPaint dl_paint;
paint.paint(dl_paint, kDrawRRectFlags, DlTileMode::kDecal);
builder()->DrawRoundRect(rrect.rrect, dl_paint);
}
}
void Canvas::drawDRRect(const RRect& outer,
const RRect& inner,
Dart_Handle paint_objects,
Dart_Handle paint_data) {
Paint paint(paint_objects, paint_data);
FML_DCHECK(paint.isNotNull());
if (display_list_builder_) {
DlPaint dl_paint;
paint.paint(dl_paint, kDrawDRRectFlags, DlTileMode::kDecal);
builder()->DrawDiffRoundRect(outer.rrect, inner.rrect, dl_paint);
}
}
void Canvas::drawOval(double left,
double top,
double right,
double bottom,
Dart_Handle paint_objects,
Dart_Handle paint_data) {
Paint paint(paint_objects, paint_data);
FML_DCHECK(paint.isNotNull());
if (display_list_builder_) {
DlPaint dl_paint;
paint.paint(dl_paint, kDrawOvalFlags, DlTileMode::kDecal);
builder()->DrawOval(DlRect::MakeLTRB(SafeNarrow(left), SafeNarrow(top),
SafeNarrow(right), SafeNarrow(bottom)),
dl_paint);
}
}
void Canvas::drawCircle(double x,
double y,
double radius,
Dart_Handle paint_objects,
Dart_Handle paint_data) {
Paint paint(paint_objects, paint_data);
FML_DCHECK(paint.isNotNull());
if (display_list_builder_) {
DlPaint dl_paint;
paint.paint(dl_paint, kDrawCircleFlags, DlTileMode::kDecal);
builder()->DrawCircle(DlPoint(SafeNarrow(x), SafeNarrow(y)),
SafeNarrow(radius), dl_paint);
}
}
void Canvas::drawArc(double left,
double top,
double right,
double bottom,
double startAngle,
double sweepAngle,
bool useCenter,
Dart_Handle paint_objects,
Dart_Handle paint_data) {
Paint paint(paint_objects, paint_data);
FML_DCHECK(paint.isNotNull());
if (display_list_builder_) {
DlPaint dl_paint;
paint.paint(dl_paint,
useCenter ? kDrawArcWithCenterFlags : kDrawArcNoCenterFlags,
DlTileMode::kDecal);
builder()->DrawArc(
DlRect::MakeLTRB(SafeNarrow(left), SafeNarrow(top), SafeNarrow(right),
SafeNarrow(bottom)),
SafeNarrow(startAngle) * 180.0f / static_cast<float>(M_PI),
SafeNarrow(sweepAngle) * 180.0f / static_cast<float>(M_PI), useCenter,
dl_paint);
}
}
void Canvas::drawPath(const CanvasPath* path,
Dart_Handle paint_objects,
Dart_Handle paint_data) {
Paint paint(paint_objects, paint_data);
FML_DCHECK(paint.isNotNull());
if (!path) {
Dart_ThrowException(
ToDart("Canvas.drawPath called with non-genuine Path."));
return;
}
if (display_list_builder_) {
DlPaint dl_paint;
paint.paint(dl_paint, kDrawPathFlags, DlTileMode::kDecal);
builder()->DrawPath(path->path(), dl_paint);
}
}
Dart_Handle Canvas::drawImage(const CanvasImage* image,
double x,
double y,
Dart_Handle paint_objects,
Dart_Handle paint_data,
int filterQualityIndex) {
Paint paint(paint_objects, paint_data);
FML_DCHECK(paint.isNotNull());
if (!image) {
return ToDart("Canvas.drawImage called with non-genuine Image.");
}
auto dl_image = image->image();
if (!dl_image) {
return Dart_Null();
}
auto error = dl_image->get_error();
if (error) {
return ToDart(error.value());
}
auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex);
if (display_list_builder_) {
DlPaint dl_paint;
const DlPaint* opt_paint =
paint.paint(dl_paint, kDrawImageWithPaintFlags, DlTileMode::kClamp);
builder()->DrawImage(dl_image, DlPoint(SafeNarrow(x), SafeNarrow(y)),
sampling, opt_paint);
}
return Dart_Null();
}
Dart_Handle Canvas::drawImageRect(const CanvasImage* image,
double src_left,
double src_top,
double src_right,
double src_bottom,
double dst_left,
double dst_top,
double dst_right,
double dst_bottom,
Dart_Handle paint_objects,
Dart_Handle paint_data,
int filterQualityIndex) {
Paint paint(paint_objects, paint_data);
FML_DCHECK(paint.isNotNull());
if (!image) {
return ToDart("Canvas.drawImageRect called with non-genuine Image.");
}
auto dl_image = image->image();
if (!dl_image) {
return Dart_Null();
}
auto error = dl_image->get_error();
if (error) {
return ToDart(error.value());
}
DlRect src = DlRect::MakeLTRB(SafeNarrow(src_left), SafeNarrow(src_top),
SafeNarrow(src_right), SafeNarrow(src_bottom));
DlRect dst = DlRect::MakeLTRB(SafeNarrow(dst_left), SafeNarrow(dst_top),
SafeNarrow(dst_right), SafeNarrow(dst_bottom));
auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex);
if (display_list_builder_) {
DlPaint dl_paint;
const DlPaint* opt_paint =
paint.paint(dl_paint, kDrawImageRectWithPaintFlags, DlTileMode::kClamp);
builder()->DrawImageRect(dl_image, src, dst, sampling, opt_paint,
DlCanvas::SrcRectConstraint::kFast);
}
return Dart_Null();
}
Dart_Handle Canvas::drawImageNine(const CanvasImage* image,
double center_left,
double center_top,
double center_right,
double center_bottom,
double dst_left,
double dst_top,
double dst_right,
double dst_bottom,
Dart_Handle paint_objects,
Dart_Handle paint_data,
int bitmapSamplingIndex) {
Paint paint(paint_objects, paint_data);
FML_DCHECK(paint.isNotNull());
if (!image) {
return ToDart("Canvas.drawImageNine called with non-genuine Image.");
}
auto dl_image = image->image();
if (!dl_image) {
return Dart_Null();
}
auto error = dl_image->get_error();
if (error) {
return ToDart(error.value());
}
DlRect center =
DlRect::MakeLTRB(SafeNarrow(center_left), SafeNarrow(center_top),
SafeNarrow(center_right), SafeNarrow(center_bottom));
DlIRect icenter = DlIRect::Round(center);
DlRect dst = DlRect::MakeLTRB(SafeNarrow(dst_left), SafeNarrow(dst_top),
SafeNarrow(dst_right), SafeNarrow(dst_bottom));
auto filter = ImageFilter::FilterModeFromIndex(bitmapSamplingIndex);
if (display_list_builder_) {
DlPaint dl_paint;
const DlPaint* opt_paint =
paint.paint(dl_paint, kDrawImageNineWithPaintFlags, DlTileMode::kClamp);
builder()->DrawImageNine(dl_image, icenter, dst, filter, opt_paint);
}
return Dart_Null();
}
void Canvas::drawPicture(Picture* picture) {
if (!picture) {
Dart_ThrowException(
ToDart("Canvas.drawPicture called with non-genuine Picture."));
return;
}
if (picture->display_list()) {
if (display_list_builder_) {
builder()->DrawDisplayList(picture->display_list());
}
} else {
FML_DCHECK(false);
}
}
void Canvas::drawPoints(Dart_Handle paint_objects,
Dart_Handle paint_data,
DlCanvas::PointMode point_mode,
const tonic::Float32List& points) {
Paint paint(paint_objects, paint_data);
static_assert(sizeof(DlPoint) == sizeof(float) * 2,
"DlPoint doesn't use floats.");
FML_DCHECK(paint.isNotNull());
if (display_list_builder_) {
DlPaint dl_paint;
switch (point_mode) {
case DlCanvas::PointMode::kPoints:
paint.paint(dl_paint, kDrawPointsAsPointsFlags, DlTileMode::kDecal);
break;
case DlCanvas::PointMode::kLines:
paint.paint(dl_paint, kDrawPointsAsLinesFlags, DlTileMode::kDecal);
break;
case DlCanvas::PointMode::kPolygon:
paint.paint(dl_paint, kDrawPointsAsPolygonFlags, DlTileMode::kDecal);
break;
}
builder()->DrawPoints(point_mode,
points.num_elements() / 2, // DlPoints have 2 floats
reinterpret_cast<const DlPoint*>(points.data()),
dl_paint);
}
}
void Canvas::drawVertices(const Vertices* vertices,
DlBlendMode blend_mode,
Dart_Handle paint_objects,
Dart_Handle paint_data) {
Paint paint(paint_objects, paint_data);
if (!vertices) {
Dart_ThrowException(
ToDart("Canvas.drawVertices called with non-genuine Vertices."));
return;
}
FML_DCHECK(paint.isNotNull());
if (display_list_builder_) {
DlPaint dl_paint;
paint.paint(dl_paint, kDrawVerticesFlags, DlTileMode::kDecal);
builder()->DrawVertices(vertices->vertices(), blend_mode, dl_paint);
}
}
Dart_Handle Canvas::drawAtlas(Dart_Handle paint_objects,
Dart_Handle paint_data,
int filterQualityIndex,
CanvasImage* atlas,
Dart_Handle transforms_handle,
Dart_Handle rects_handle,
Dart_Handle colors_handle,
DlBlendMode blend_mode,
Dart_Handle cull_rect_handle) {
Paint paint(paint_objects, paint_data);
if (!atlas) {
return ToDart(
"Canvas.drawAtlas or Canvas.drawRawAtlas called with "
"non-genuine Image.");
}
auto dl_image = atlas->image();
auto error = dl_image->get_error();
if (error) {
return ToDart(error.value());
}
static_assert(sizeof(SkRSXform) == sizeof(float) * 4,
"SkRSXform doesn't use floats.");
static_assert(sizeof(DlRect) == sizeof(float) * 4,
"DlRect doesn't use floats.");
auto sampling = ImageFilter::SamplingFromIndex(filterQualityIndex);
FML_DCHECK(paint.isNotNull());
if (display_list_builder_) {
tonic::Float32List transforms(transforms_handle);
tonic::Float32List rects(rects_handle);
tonic::Int32List colors(colors_handle);
tonic::Float32List cull_rect(cull_rect_handle);
std::vector<DlColor> dl_color(colors.num_elements());
size_t count = colors.num_elements();
for (size_t i = 0; i < count; i++) {
dl_color[i] = DlColor(colors[i]);
}
DlPaint dl_paint;
const DlPaint* opt_paint =
paint.paint(dl_paint, kDrawAtlasWithPaintFlags, DlTileMode::kClamp);
builder()->DrawAtlas(
dl_image, reinterpret_cast<const SkRSXform*>(transforms.data()),
reinterpret_cast<const DlRect*>(rects.data()), dl_color.data(),
rects.num_elements() / 4, // DlRect have four floats.
blend_mode, sampling, reinterpret_cast<const DlRect*>(cull_rect.data()),
opt_paint);
}
return Dart_Null();
}
void Canvas::drawShadow(const CanvasPath* path,
uint32_t color,
double elevation,
bool transparentOccluder) {
if (!path) {
Dart_ThrowException(
ToDart("Canvas.drawShader called with non-genuine Path."));
return;
}
// Not using SafeNarrow because DPR will always be a relatively small number.
const ViewportMetrics* metrics =
UIDartState::Current()->platform_configuration()->GetMetrics(0);
DlScalar dpr;
// TODO(dkwingsmt): We should support rendering shadows on non-implicit views.
// However, currently this method has no way to get the target view ID.
if (metrics == nullptr) {
dpr = 1.0f;
} else {
dpr = static_cast<float>(metrics->device_pixel_ratio);
}
if (display_list_builder_) {
builder()->DrawShadow(path->path(), DlColor(color), SafeNarrow(elevation),
transparentOccluder, dpr);
}
}
void Canvas::Invalidate() {
display_list_builder_ = nullptr;
if (dart_wrapper()) {
ClearDartWrapper();
}
}
} // namespace flutter