| // 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/flow/display_list_canvas.h" |
| |
| #include "flutter/flow/layers/physical_shape_layer.h" |
| |
| #include "third_party/skia/include/core/SkMaskFilter.h" |
| #include "third_party/skia/include/core/SkTextBlob.h" |
| |
| namespace flutter { |
| |
| void DisplayListCanvasDispatcher::save() { |
| canvas_->save(); |
| } |
| void DisplayListCanvasDispatcher::restore() { |
| canvas_->restore(); |
| } |
| void DisplayListCanvasDispatcher::saveLayer(const SkRect* bounds, |
| bool restore_with_paint) { |
| canvas_->saveLayer(bounds, restore_with_paint ? &paint() : nullptr); |
| } |
| |
| void DisplayListCanvasDispatcher::translate(SkScalar tx, SkScalar ty) { |
| canvas_->translate(tx, ty); |
| } |
| void DisplayListCanvasDispatcher::scale(SkScalar sx, SkScalar sy) { |
| canvas_->scale(sx, sy); |
| } |
| void DisplayListCanvasDispatcher::rotate(SkScalar degrees) { |
| canvas_->rotate(degrees); |
| } |
| void DisplayListCanvasDispatcher::skew(SkScalar sx, SkScalar sy) { |
| canvas_->skew(sx, sy); |
| } |
| void DisplayListCanvasDispatcher::transform2x3(SkScalar mxx, |
| SkScalar mxy, |
| SkScalar mxt, |
| SkScalar myx, |
| SkScalar myy, |
| SkScalar myt) { |
| canvas_->concat(SkMatrix::MakeAll(mxx, mxy, mxt, myx, myy, myt, 0, 0, 1)); |
| } |
| void DisplayListCanvasDispatcher::transform3x3(SkScalar mxx, |
| SkScalar mxy, |
| SkScalar mxt, |
| SkScalar myx, |
| SkScalar myy, |
| SkScalar myt, |
| SkScalar px, |
| SkScalar py, |
| SkScalar pt) { |
| canvas_->concat(SkMatrix::MakeAll(mxx, mxy, mxt, myx, myy, myt, px, py, pt)); |
| } |
| |
| void DisplayListCanvasDispatcher::clipRect(const SkRect& rect, |
| bool isAA, |
| SkClipOp clip_op) { |
| canvas_->clipRect(rect, clip_op, isAA); |
| } |
| void DisplayListCanvasDispatcher::clipRRect(const SkRRect& rrect, |
| bool isAA, |
| SkClipOp clip_op) { |
| canvas_->clipRRect(rrect, clip_op, isAA); |
| } |
| void DisplayListCanvasDispatcher::clipPath(const SkPath& path, |
| bool isAA, |
| SkClipOp clip_op) { |
| canvas_->clipPath(path, clip_op, isAA); |
| } |
| |
| void DisplayListCanvasDispatcher::drawPaint() { |
| canvas_->drawPaint(paint()); |
| } |
| void DisplayListCanvasDispatcher::drawColor(SkColor color, SkBlendMode mode) { |
| canvas_->drawColor(color, mode); |
| } |
| void DisplayListCanvasDispatcher::drawLine(const SkPoint& p0, |
| const SkPoint& p1) { |
| canvas_->drawLine(p0, p1, paint()); |
| } |
| void DisplayListCanvasDispatcher::drawRect(const SkRect& rect) { |
| canvas_->drawRect(rect, paint()); |
| } |
| void DisplayListCanvasDispatcher::drawOval(const SkRect& bounds) { |
| canvas_->drawOval(bounds, paint()); |
| } |
| void DisplayListCanvasDispatcher::drawCircle(const SkPoint& center, |
| SkScalar radius) { |
| canvas_->drawCircle(center, radius, paint()); |
| } |
| void DisplayListCanvasDispatcher::drawRRect(const SkRRect& rrect) { |
| canvas_->drawRRect(rrect, paint()); |
| } |
| void DisplayListCanvasDispatcher::drawDRRect(const SkRRect& outer, |
| const SkRRect& inner) { |
| canvas_->drawDRRect(outer, inner, paint()); |
| } |
| void DisplayListCanvasDispatcher::drawPath(const SkPath& path) { |
| canvas_->drawPath(path, paint()); |
| } |
| void DisplayListCanvasDispatcher::drawArc(const SkRect& bounds, |
| SkScalar start, |
| SkScalar sweep, |
| bool useCenter) { |
| canvas_->drawArc(bounds, start, sweep, useCenter, paint()); |
| } |
| void DisplayListCanvasDispatcher::drawPoints(SkCanvas::PointMode mode, |
| uint32_t count, |
| const SkPoint pts[]) { |
| canvas_->drawPoints(mode, count, pts, paint()); |
| } |
| void DisplayListCanvasDispatcher::drawVertices(const sk_sp<SkVertices> vertices, |
| SkBlendMode mode) { |
| canvas_->drawVertices(vertices, mode, paint()); |
| } |
| void DisplayListCanvasDispatcher::drawImage(const sk_sp<SkImage> image, |
| const SkPoint point, |
| const SkSamplingOptions& sampling) { |
| canvas_->drawImage(image, point.fX, point.fY, sampling, &paint()); |
| } |
| void DisplayListCanvasDispatcher::drawImageRect( |
| const sk_sp<SkImage> image, |
| const SkRect& src, |
| const SkRect& dst, |
| const SkSamplingOptions& sampling, |
| SkCanvas::SrcRectConstraint constraint) { |
| canvas_->drawImageRect(image, src, dst, sampling, &paint(), constraint); |
| } |
| void DisplayListCanvasDispatcher::drawImageNine(const sk_sp<SkImage> image, |
| const SkIRect& center, |
| const SkRect& dst, |
| SkFilterMode filter) { |
| canvas_->drawImageNine(image.get(), center, dst, filter, &paint()); |
| } |
| void DisplayListCanvasDispatcher::drawImageLattice( |
| const sk_sp<SkImage> image, |
| const SkCanvas::Lattice& lattice, |
| const SkRect& dst, |
| SkFilterMode filter, |
| bool with_paint) { |
| canvas_->drawImageLattice(image.get(), lattice, dst, filter, |
| with_paint ? &paint() : nullptr); |
| } |
| void DisplayListCanvasDispatcher::drawAtlas(const sk_sp<SkImage> atlas, |
| const SkRSXform xform[], |
| const SkRect tex[], |
| const SkColor colors[], |
| int count, |
| SkBlendMode mode, |
| const SkSamplingOptions& sampling, |
| const SkRect* cullRect) { |
| canvas_->drawAtlas(atlas.get(), xform, tex, colors, count, mode, sampling, |
| cullRect, &paint()); |
| } |
| void DisplayListCanvasDispatcher::drawPicture(const sk_sp<SkPicture> picture, |
| const SkMatrix* matrix, |
| bool with_save_layer) { |
| canvas_->drawPicture(picture, matrix, with_save_layer ? &paint() : nullptr); |
| } |
| void DisplayListCanvasDispatcher::drawDisplayList( |
| const sk_sp<DisplayList> display_list) { |
| int save_count = canvas_->save(); |
| { |
| DisplayListCanvasDispatcher dispatcher(canvas_); |
| display_list->Dispatch(dispatcher); |
| } |
| canvas_->restoreToCount(save_count); |
| } |
| void DisplayListCanvasDispatcher::drawTextBlob(const sk_sp<SkTextBlob> blob, |
| SkScalar x, |
| SkScalar y) { |
| canvas_->drawTextBlob(blob, x, y, paint()); |
| } |
| void DisplayListCanvasDispatcher::drawShadow(const SkPath& path, |
| const SkColor color, |
| const SkScalar elevation, |
| bool occludes, |
| SkScalar dpr) { |
| flutter::PhysicalShapeLayer::DrawShadow(canvas_, path, color, elevation, |
| occludes, dpr); |
| } |
| |
| DisplayListCanvasRecorder::DisplayListCanvasRecorder(const SkRect& bounds) |
| : SkCanvasVirtualEnforcer(bounds.width(), bounds.height()), |
| builder_(sk_make_sp<DisplayListBuilder>(bounds)) {} |
| |
| sk_sp<DisplayList> DisplayListCanvasRecorder::Build() { |
| sk_sp<DisplayList> display_list = builder_->Build(); |
| builder_.reset(); |
| return display_list; |
| } |
| |
| void DisplayListCanvasRecorder::didConcat44(const SkM44& m44) { |
| SkMatrix m = m44.asM33(); |
| if (m.hasPerspective()) { |
| builder_->transform3x3(m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], |
| m[8]); |
| } else { |
| builder_->transform2x3(m[0], m[1], m[2], m[3], m[4], m[5]); |
| } |
| } |
| void DisplayListCanvasRecorder::didTranslate(SkScalar tx, SkScalar ty) { |
| builder_->translate(tx, ty); |
| } |
| void DisplayListCanvasRecorder::didScale(SkScalar sx, SkScalar sy) { |
| builder_->scale(sx, sy); |
| } |
| |
| void DisplayListCanvasRecorder::onClipRect(const SkRect& rect, |
| SkClipOp clip_op, |
| ClipEdgeStyle edgeStyle) { |
| builder_->clipRect(rect, edgeStyle == ClipEdgeStyle::kSoft_ClipEdgeStyle, |
| clip_op); |
| } |
| void DisplayListCanvasRecorder::onClipRRect(const SkRRect& rrect, |
| SkClipOp clip_op, |
| ClipEdgeStyle edgeStyle) { |
| builder_->clipRRect(rrect, edgeStyle == ClipEdgeStyle::kSoft_ClipEdgeStyle, |
| clip_op); |
| } |
| void DisplayListCanvasRecorder::onClipPath(const SkPath& path, |
| SkClipOp clip_op, |
| ClipEdgeStyle edgeStyle) { |
| builder_->clipPath(path, edgeStyle == ClipEdgeStyle::kSoft_ClipEdgeStyle, |
| clip_op); |
| } |
| |
| void DisplayListCanvasRecorder::willSave() { |
| builder_->save(); |
| } |
| SkCanvas::SaveLayerStrategy DisplayListCanvasRecorder::getSaveLayerStrategy( |
| const SaveLayerRec& rec) { |
| if (rec.fPaint) { |
| RecordPaintAttributes(rec.fPaint, DrawType::kSaveLayerOpType); |
| builder_->saveLayer(rec.fBounds, true); |
| } else { |
| builder_->saveLayer(rec.fBounds, false); |
| } |
| return SaveLayerStrategy::kNoLayer_SaveLayerStrategy; |
| } |
| void DisplayListCanvasRecorder::didRestore() { |
| builder_->restore(); |
| } |
| |
| void DisplayListCanvasRecorder::onDrawPaint(const SkPaint& paint) { |
| RecordPaintAttributes(&paint, DrawType::kFillOpType); |
| builder_->drawPaint(); |
| } |
| void DisplayListCanvasRecorder::onDrawRect(const SkRect& rect, |
| const SkPaint& paint) { |
| RecordPaintAttributes(&paint, DrawType::kDrawOpType); |
| builder_->drawRect(rect); |
| } |
| void DisplayListCanvasRecorder::onDrawRRect(const SkRRect& rrect, |
| const SkPaint& paint) { |
| RecordPaintAttributes(&paint, DrawType::kDrawOpType); |
| builder_->drawRRect(rrect); |
| } |
| void DisplayListCanvasRecorder::onDrawDRRect(const SkRRect& outer, |
| const SkRRect& inner, |
| const SkPaint& paint) { |
| RecordPaintAttributes(&paint, DrawType::kDrawOpType); |
| builder_->drawDRRect(outer, inner); |
| } |
| void DisplayListCanvasRecorder::onDrawOval(const SkRect& rect, |
| const SkPaint& paint) { |
| RecordPaintAttributes(&paint, DrawType::kDrawOpType); |
| builder_->drawOval(rect); |
| } |
| void DisplayListCanvasRecorder::onDrawArc(const SkRect& rect, |
| SkScalar startAngle, |
| SkScalar sweepAngle, |
| bool useCenter, |
| const SkPaint& paint) { |
| RecordPaintAttributes(&paint, DrawType::kDrawOpType); |
| builder_->drawArc(rect, startAngle, sweepAngle, useCenter); |
| } |
| void DisplayListCanvasRecorder::onDrawPath(const SkPath& path, |
| const SkPaint& paint) { |
| RecordPaintAttributes(&paint, DrawType::kDrawOpType); |
| builder_->drawPath(path); |
| } |
| |
| void DisplayListCanvasRecorder::onDrawPoints(SkCanvas::PointMode mode, |
| size_t count, |
| const SkPoint pts[], |
| const SkPaint& paint) { |
| RecordPaintAttributes(&paint, DrawType::kStrokeOpType); |
| if (mode == SkCanvas::PointMode::kLines_PointMode && count == 2) { |
| builder_->drawLine(pts[0], pts[1]); |
| } else { |
| uint32_t count32 = static_cast<uint32_t>(count); |
| // TODO(flar): depending on the mode we could break it down into |
| // multiple calls to drawPoints, but how much do we really want |
| // to support more than a couple billion points? |
| FML_DCHECK(count32 == count); |
| builder_->drawPoints(mode, count32, pts); |
| } |
| } |
| void DisplayListCanvasRecorder::onDrawVerticesObject(const SkVertices* vertices, |
| SkBlendMode mode, |
| const SkPaint& paint) { |
| RecordPaintAttributes(&paint, DrawType::kDrawOpType); |
| builder_->drawVertices(sk_ref_sp(vertices), mode); |
| } |
| |
| void DisplayListCanvasRecorder::onDrawImage2(const SkImage* image, |
| SkScalar dx, |
| SkScalar dy, |
| const SkSamplingOptions& sampling, |
| const SkPaint* paint) { |
| RecordPaintAttributes(paint, DrawType::kImageOpType); |
| builder_->drawImage(sk_ref_sp(image), SkPoint::Make(dx, dy), sampling); |
| } |
| void DisplayListCanvasRecorder::onDrawImageRect2( |
| const SkImage* image, |
| const SkRect& src, |
| const SkRect& dst, |
| const SkSamplingOptions& sampling, |
| const SkPaint* paint, |
| SrcRectConstraint constraint) { |
| RecordPaintAttributes(paint, DrawType::kImageRectOpType); |
| builder_->drawImageRect(sk_ref_sp(image), src, dst, sampling, constraint); |
| } |
| void DisplayListCanvasRecorder::onDrawImageLattice2(const SkImage* image, |
| const Lattice& lattice, |
| const SkRect& dst, |
| SkFilterMode filter, |
| const SkPaint* paint) { |
| if (paint != nullptr) { |
| // SkCanvas will always construct a paint, |
| // though it is a default paint most of the time |
| SkPaint default_paint; |
| if (*paint == default_paint) { |
| paint = nullptr; |
| } else { |
| RecordPaintAttributes(paint, DrawType::kImageOpType); |
| } |
| } |
| builder_->drawImageLattice(sk_ref_sp(image), lattice, dst, filter, |
| paint != nullptr); |
| } |
| void DisplayListCanvasRecorder::onDrawAtlas2(const SkImage* image, |
| const SkRSXform xform[], |
| const SkRect src[], |
| const SkColor colors[], |
| int count, |
| SkBlendMode mode, |
| const SkSamplingOptions& sampling, |
| const SkRect* cull, |
| const SkPaint* paint) { |
| RecordPaintAttributes(paint, DrawType::kImageOpType); |
| builder_->drawAtlas(sk_ref_sp(image), xform, src, colors, count, mode, |
| sampling, cull); |
| } |
| |
| void DisplayListCanvasRecorder::onDrawTextBlob(const SkTextBlob* blob, |
| SkScalar x, |
| SkScalar y, |
| const SkPaint& paint) { |
| RecordPaintAttributes(&paint, DrawType::kDrawOpType); |
| builder_->drawTextBlob(sk_ref_sp(blob), x, y); |
| } |
| void DisplayListCanvasRecorder::onDrawShadowRec(const SkPath& path, |
| const SkDrawShadowRec& rec) { |
| // Skia does not expose the SkDrawShadowRec structure in a public |
| // header file so we cannot record this operation. |
| // See: https://bugs.chromium.org/p/skia/issues/detail?id=12125 |
| FML_DCHECK(false); |
| } |
| |
| void DisplayListCanvasRecorder::onDrawPicture(const SkPicture* picture, |
| const SkMatrix* matrix, |
| const SkPaint* paint) { |
| if (paint) { |
| RecordPaintAttributes(paint, DrawType::kSaveLayerOpType); |
| } |
| builder_->drawPicture(sk_ref_sp(picture), matrix, paint != nullptr); |
| } |
| |
| void DisplayListCanvasRecorder::RecordPaintAttributes(const SkPaint* paint, |
| DrawType type) { |
| int dataNeeded; |
| switch (type) { |
| case DrawType::kDrawOpType: |
| dataNeeded = kDrawMask_; |
| break; |
| case DrawType::kFillOpType: |
| dataNeeded = kPaintMask_; |
| break; |
| case DrawType::kStrokeOpType: |
| dataNeeded = kStrokeMask_; |
| break; |
| case DrawType::kImageOpType: |
| dataNeeded = kImageMask_; |
| break; |
| case DrawType::kImageRectOpType: |
| dataNeeded = kImageRectMask_; |
| break; |
| case DrawType::kSaveLayerOpType: |
| dataNeeded = kSaveLayerMask_; |
| break; |
| default: |
| FML_DCHECK(false); |
| return; |
| } |
| if (paint == nullptr) { |
| paint = new SkPaint(); |
| } |
| if ((dataNeeded & kAaNeeded_) != 0 && current_aa_ != paint->isAntiAlias()) { |
| builder_->setAA(current_aa_ = paint->isAntiAlias()); |
| } |
| if ((dataNeeded & kDitherNeeded_) != 0 && |
| current_dither_ != paint->isDither()) { |
| builder_->setDither(current_dither_ = paint->isDither()); |
| } |
| if ((dataNeeded & kColorNeeded_) != 0 && |
| current_color_ != paint->getColor()) { |
| builder_->setColor(current_color_ = paint->getColor()); |
| } |
| if ((dataNeeded & kBlendNeeded_)) { |
| skstd::optional<SkBlendMode> mode_optional = paint->asBlendMode(); |
| if (mode_optional) { |
| SkBlendMode mode = mode_optional.value(); |
| if (current_blender_ || current_blend_ != mode) { |
| builder_->setBlendMode(current_blend_ = mode); |
| current_blender_ = nullptr; |
| } |
| } else { |
| if (current_blender_.get() != paint->getBlender()) { |
| builder_->setBlender(current_blender_ = sk_ref_sp(paint->getBlender())); |
| } |
| } |
| } |
| // invert colors is a Flutter::Paint thing, not an SkPaint thing |
| // if ((dataNeeded & invertColorsNeeded_) != 0 && |
| // currentInvertColors_ != paint->???) { |
| // currentInvertColors_ = paint->invertColors; |
| // addOp_(currentInvertColors_ |
| // ? _CanvasOp.setInvertColors |
| // : _CanvasOp.clearInvertColors, 0); |
| // } |
| if ((dataNeeded & kPaintStyleNeeded_) != 0) { |
| if (current_style_ != paint->getStyle()) { |
| builder_->setDrawStyle(current_style_ = paint->getStyle()); |
| } |
| if (current_style_ == SkPaint::Style::kStroke_Style) { |
| dataNeeded |= kStrokeStyleNeeded_; |
| } |
| } |
| if ((dataNeeded & kStrokeStyleNeeded_) != 0) { |
| if (current_stroke_width_ != paint->getStrokeWidth()) { |
| builder_->setStrokeWidth(current_stroke_width_ = paint->getStrokeWidth()); |
| } |
| if (current_cap_ != paint->getStrokeCap()) { |
| builder_->setCaps(current_cap_ = paint->getStrokeCap()); |
| } |
| if (current_join_ != paint->getStrokeJoin()) { |
| builder_->setJoins(current_join_ = paint->getStrokeJoin()); |
| } |
| if (current_miter_limit_ != paint->getStrokeMiter()) { |
| builder_->setMiterLimit(current_miter_limit_ = paint->getStrokeMiter()); |
| } |
| } |
| if ((dataNeeded & kShaderNeeded_) != 0 && |
| current_shader_.get() != paint->getShader()) { |
| builder_->setShader(current_shader_ = sk_ref_sp(paint->getShader())); |
| } |
| if ((dataNeeded & kColorFilterNeeded_) != 0 && |
| current_color_filter_.get() != paint->getColorFilter()) { |
| builder_->setColorFilter(current_color_filter_ = |
| sk_ref_sp(paint->getColorFilter())); |
| } |
| if ((dataNeeded & kImageFilterNeeded_) != 0 && |
| current_image_filter_.get() != paint->getImageFilter()) { |
| builder_->setImageFilter(current_image_filter_ = |
| sk_ref_sp(paint->getImageFilter())); |
| } |
| if ((dataNeeded & kPathEffectNeeded_) != 0 && |
| current_path_effect_.get() != paint->getPathEffect()) { |
| builder_->setPathEffect(current_path_effect_ = |
| sk_ref_sp(paint->getPathEffect())); |
| } |
| if ((dataNeeded & kMaskFilterNeeded_) != 0 && |
| current_mask_filter_.get() != paint->getMaskFilter()) { |
| builder_->setMaskFilter(current_mask_filter_ = |
| sk_ref_sp(paint->getMaskFilter())); |
| } |
| } |
| |
| } // namespace flutter |