未验证 提交 e1cf837a 编写于 作者: L liyuqian 提交者: GitHub

Add ClipMode to ClipPath/ClipRRect and PhysicalShape layers (#5647)

For flutter/flutter#18057
上级 3eb8c04e
......@@ -12,7 +12,7 @@
namespace flow {
ClipPathLayer::ClipPathLayer() = default;
ClipPathLayer::ClipPathLayer(ClipMode clip_mode) : clip_mode_(clip_mode) {}
ClipPathLayer::~ClipPathLayer() = default;
......@@ -38,6 +38,7 @@ void ClipPathLayer::UpdateScene(SceneUpdateContext& context) {
bounds.height() // height
);
// TODO(liyuqian): respect clip_mode_
SceneUpdateContext::Clip clip(context, shape, bounds);
UpdateSceneChildren(context);
}
......@@ -49,8 +50,14 @@ void ClipPathLayer::Paint(PaintContext& context) const {
FXL_DCHECK(needs_painting());
SkAutoCanvasRestore save(&context.canvas, true);
context.canvas.clipPath(clip_path_, true);
context.canvas.clipPath(clip_path_, clip_mode_ != ClipMode::hardEdge);
if (clip_mode_ == ClipMode::antiAliasWithSaveLayer) {
context.canvas.saveLayer(paint_bounds(), nullptr);
}
PaintChildren(context);
if (clip_mode_ == ClipMode::antiAliasWithSaveLayer) {
context.canvas.restore();
}
}
} // namespace flow
......@@ -11,7 +11,7 @@ namespace flow {
class ClipPathLayer : public ContainerLayer {
public:
ClipPathLayer();
ClipPathLayer(ClipMode clip_mode = ClipMode::antiAlias);
~ClipPathLayer() override;
void set_clip_path(const SkPath& clip_path) { clip_path_ = clip_path; }
......@@ -26,6 +26,7 @@ class ClipPathLayer : public ContainerLayer {
private:
SkPath clip_path_;
ClipMode clip_mode_;
FXL_DISALLOW_COPY_AND_ASSIGN(ClipPathLayer);
};
......
......@@ -6,7 +6,7 @@
namespace flow {
ClipRectLayer::ClipRectLayer() = default;
ClipRectLayer::ClipRectLayer(ClipMode clip_mode) : clip_mode_(clip_mode) {}
ClipRectLayer::~ClipRectLayer() = default;
......@@ -29,6 +29,7 @@ void ClipRectLayer::UpdateScene(SceneUpdateContext& context) {
clip_rect_.height() // height
);
// TODO(liyuqian): respect clip_mode_
SceneUpdateContext::Clip clip(context, shape, clip_rect_);
UpdateSceneChildren(context);
}
......@@ -39,9 +40,15 @@ void ClipRectLayer::Paint(PaintContext& context) const {
TRACE_EVENT0("flutter", "ClipRectLayer::Paint");
FXL_DCHECK(needs_painting());
SkAutoCanvasRestore save(&context.canvas, true);
SkAutoCanvasRestore save(&context.canvas, clip_mode_ != ClipMode::hardEdge);
context.canvas.clipRect(paint_bounds());
if (clip_mode_ == ClipMode::antiAliasWithSaveLayer) {
context.canvas.saveLayer(paint_bounds(), nullptr);
}
PaintChildren(context);
if (clip_mode_ == ClipMode::antiAliasWithSaveLayer) {
context.canvas.restore();
}
}
} // namespace flow
......@@ -11,7 +11,7 @@ namespace flow {
class ClipRectLayer : public ContainerLayer {
public:
ClipRectLayer();
ClipRectLayer(ClipMode clip_mode);
~ClipRectLayer() override;
void set_clip_rect(const SkRect& clip_rect) { clip_rect_ = clip_rect; }
......@@ -25,6 +25,7 @@ class ClipRectLayer : public ContainerLayer {
private:
SkRect clip_rect_;
ClipMode clip_mode_;
FXL_DISALLOW_COPY_AND_ASSIGN(ClipRectLayer);
};
......
......@@ -6,7 +6,7 @@
namespace flow {
ClipRRectLayer::ClipRRectLayer() = default;
ClipRRectLayer::ClipRRectLayer(ClipMode clip_mode) : clip_mode_(clip_mode) {}
ClipRRectLayer::~ClipRRectLayer() = default;
......@@ -36,6 +36,7 @@ void ClipRRectLayer::UpdateScene(SceneUpdateContext& context) {
clip_rrect_.radii(SkRRect::kLowerLeft_Corner).x() // bottom_left_radius
);
// TODO(liyuqian): respect clip_mode_
SceneUpdateContext::Clip clip(context, shape, clip_rrect_.getBounds());
UpdateSceneChildren(context);
}
......@@ -47,8 +48,14 @@ void ClipRRectLayer::Paint(PaintContext& context) const {
FXL_DCHECK(needs_painting());
SkAutoCanvasRestore save(&context.canvas, true);
context.canvas.clipRRect(clip_rrect_, true);
context.canvas.clipRRect(clip_rrect_, clip_mode_ != ClipMode::hardEdge);
if (clip_mode_ == ClipMode::antiAliasWithSaveLayer) {
context.canvas.saveLayer(paint_bounds(), nullptr);
}
PaintChildren(context);
if (clip_mode_ == ClipMode::antiAliasWithSaveLayer) {
context.canvas.restore();
}
}
} // namespace flow
......@@ -11,7 +11,7 @@ namespace flow {
class ClipRRectLayer : public ContainerLayer {
public:
ClipRRectLayer();
ClipRRectLayer(ClipMode clip_mode);
~ClipRRectLayer() override;
void set_clip_rrect(const SkRRect& clip_rrect) { clip_rrect_ = clip_rrect; }
......@@ -26,6 +26,7 @@ class ClipRRectLayer : public ContainerLayer {
private:
SkRRect clip_rrect_;
ClipMode clip_mode_;
FXL_DISALLOW_COPY_AND_ASSIGN(ClipRRectLayer);
};
......
......@@ -49,32 +49,33 @@ void DefaultLayerBuilder::PushTransform(const SkMatrix& sk_matrix) {
PushLayer(std::move(layer), cullRect);
}
void DefaultLayerBuilder::PushClipRect(const SkRect& clipRect) {
void DefaultLayerBuilder::PushClipRect(const SkRect& clipRect, ClipMode clip_mode) {
SkRect cullRect;
if (!cullRect.intersect(clipRect, cull_rects_.top())) {
cullRect = SkRect::MakeEmpty();
}
auto layer = std::make_unique<flow::ClipRectLayer>();
auto layer = std::make_unique<flow::ClipRectLayer>(clip_mode);
layer->set_clip_rect(clipRect);
PushLayer(std::move(layer), cullRect);
}
void DefaultLayerBuilder::PushClipRoundedRect(const SkRRect& rrect) {
void DefaultLayerBuilder::PushClipRoundedRect(const SkRRect& rrect, ClipMode clip_mode) {
SkRect cullRect;
if (!cullRect.intersect(rrect.rect(), cull_rects_.top())) {
cullRect = SkRect::MakeEmpty();
}
auto layer = std::make_unique<flow::ClipRRectLayer>();
auto layer = std::make_unique<flow::ClipRRectLayer>(clip_mode);
layer->set_clip_rrect(rrect);
PushLayer(std::move(layer), cullRect);
}
void DefaultLayerBuilder::PushClipPath(const SkPath& path) {
void DefaultLayerBuilder::PushClipPath(const SkPath& path, ClipMode clip_mode) {
FXL_DCHECK(clip_mode != ClipMode::none);
SkRect cullRect;
if (!cullRect.intersect(path.getBounds(), cull_rects_.top())) {
cullRect = SkRect::MakeEmpty();
}
auto layer = std::make_unique<flow::ClipPathLayer>();
auto layer = std::make_unique<flow::ClipPathLayer>(clip_mode);
layer->set_clip_path(path);
PushLayer(std::move(layer), cullRect);
}
......@@ -113,12 +114,13 @@ void DefaultLayerBuilder::PushPhysicalShape(const SkPath& sk_path,
double elevation,
SkColor color,
SkColor shadow_color,
SkScalar device_pixel_ratio) {
SkScalar device_pixel_ratio,
ClipMode clip_mode) {
SkRect cullRect;
if (!cullRect.intersect(sk_path.getBounds(), cull_rects_.top())) {
cullRect = SkRect::MakeEmpty();
}
auto layer = std::make_unique<flow::PhysicalShapeLayer>();
auto layer = std::make_unique<flow::PhysicalShapeLayer>(clip_mode);
layer->set_path(sk_path);
layer->set_elevation(elevation);
layer->set_color(color);
......
......@@ -24,13 +24,13 @@ class DefaultLayerBuilder final : public LayerBuilder {
void PushTransform(const SkMatrix& matrix) override;
// |flow::LayerBuilder|
void PushClipRect(const SkRect& rect) override;
void PushClipRect(const SkRect& rect, ClipMode clip_mode = ClipMode::antiAlias) override;
// |flow::LayerBuilder|
void PushClipRoundedRect(const SkRRect& rect) override;
void PushClipRoundedRect(const SkRRect& rect, ClipMode clip_mode = ClipMode::antiAlias) override;
// |flow::LayerBuilder|
void PushClipPath(const SkPath& path) override;
void PushClipPath(const SkPath& path, ClipMode clip_mode = ClipMode::antiAlias) override;
// |flow::LayerBuilder|
void PushOpacity(int alpha) override;
......@@ -51,7 +51,8 @@ class DefaultLayerBuilder final : public LayerBuilder {
double elevation,
SkColor color,
SkColor shadow_color,
SkScalar device_pixel_ratio) override;
SkScalar device_pixel_ratio,
ClipMode clip_mode) override;
// |flow::LayerBuilder|
void PushPerformanceOverlay(uint64_t enabled_options,
......
......@@ -34,6 +34,19 @@
namespace flow {
// This should be an exact copy of the Clip enum in painting.dart.
//
// We call it Clip in public Dart API to provide our developers the shortest
// name and the best experience. We call it ClipMode in C++ because we want to
// avoid name conflicts and refactoring C++ names without a nice IDE function
// is tedious.
enum ClipMode {
none,
hardEdge,
antiAlias,
antiAliasWithSaveLayer
};
class ContainerLayer;
// Represents a single composited layer. Created on the UI thread but then
......
......@@ -32,11 +32,11 @@ class LayerBuilder {
virtual void PushTransform(const SkMatrix& matrix) = 0;
virtual void PushClipRect(const SkRect& rect) = 0;
virtual void PushClipRect(const SkRect& rect, ClipMode clip_mode = ClipMode::antiAlias) = 0;
virtual void PushClipRoundedRect(const SkRRect& rect) = 0;
virtual void PushClipRoundedRect(const SkRRect& rect, ClipMode clip_mode = ClipMode::antiAlias) = 0;
virtual void PushClipPath(const SkPath& path) = 0;
virtual void PushClipPath(const SkPath& path, ClipMode clip_mode = ClipMode::antiAlias) = 0;
virtual void PushOpacity(int alpha) = 0;
......@@ -52,7 +52,8 @@ class LayerBuilder {
double elevation,
SkColor color,
SkColor shadow_color,
SkScalar device_pixel_ratio) = 0;
SkScalar device_pixel_ratio,
ClipMode clip_mode) = 0;
virtual void PushPerformanceOverlay(uint64_t enabled_options,
const SkRect& rect) = 0;
......
......@@ -9,7 +9,7 @@
namespace flow {
PhysicalShapeLayer::PhysicalShapeLayer() : isRect_(false) {}
PhysicalShapeLayer::PhysicalShapeLayer(ClipMode clip_mode) : isRect_(false), clip_mode_(clip_mode) {}
PhysicalShapeLayer::~PhysicalShapeLayer() = default;
......@@ -90,12 +90,25 @@ void PhysicalShapeLayer::Paint(PaintContext& context) const {
paint.setColor(color_);
context.canvas.drawPath(path_, paint);
SkAutoCanvasRestore save(&context.canvas, false);
context.canvas.save();
context.canvas.clipPath(path_, true);
int saveCount = context.canvas.save();
switch(clip_mode_) {
case ClipMode::hardEdge:
context.canvas.clipPath(path_, false);
break;
case ClipMode::antiAlias:
context.canvas.clipPath(path_, true);
break;
case ClipMode::antiAliasWithSaveLayer:
context.canvas.clipPath(path_, true);
context.canvas.saveLayer(paint_bounds(), nullptr);
break;
case ClipMode::none:
break;
}
PaintChildren(context);
if (context.checkerboard_offscreen_layers && !isRect_)
DrawCheckerboard(&context.canvas, path_.getBounds());
context.canvas.restoreToCount(saveCount);
}
void PhysicalShapeLayer::DrawShadow(SkCanvas* canvas,
......
......@@ -11,7 +11,7 @@ namespace flow {
class PhysicalShapeLayer : public ContainerLayer {
public:
PhysicalShapeLayer();
PhysicalShapeLayer(ClipMode clip_mode);
~PhysicalShapeLayer() override;
void set_path(const SkPath& path);
......@@ -44,6 +44,7 @@ class PhysicalShapeLayer : public ContainerLayer {
SkPath path_;
bool isRect_;
SkRRect frameRRect_;
ClipMode clip_mode_;
};
} // namespace flow
......
......@@ -66,29 +66,44 @@ class SceneBuilder extends NativeFieldWrapperClass2 {
///
/// Rasterization outside the given rectangle is discarded.
///
/// See [pop] for details about the operation stack.
void pushClipRect(Rect rect) {
_pushClipRect(rect.left, rect.right, rect.top, rect.bottom);
/// See [pop] for details about the operation stack, and [Clip] for different clip modes.
/// By default, the clip will be anti-aliased (clip = [Clip.antiAlias]).
void pushClipRect(Rect rect, {Clip clip = Clip.antiAlias}) {
assert(clip != null);
assert(clip != Clip.none);
_pushClipRect(rect.left, rect.right, rect.top, rect.bottom, clip.index);
}
void _pushClipRect(double left,
double right,
double top,
double bottom) native 'SceneBuilder_pushClipRect';
double bottom,
int clipMode) native 'SceneBuilder_pushClipRect';
/// Pushes a rounded-rectangular clip operation onto the operation stack.
///
/// Rasterization outside the given rounded rectangle is discarded.
///
/// See [pop] for details about the operation stack.
void pushClipRRect(RRect rrect) => _pushClipRRect(rrect._value);
void _pushClipRRect(Float32List rrect) native 'SceneBuilder_pushClipRRect';
/// See [pop] for details about the operation stack, and [Clip] for different clip modes.
/// By default, the clip will be anti-aliased (clip = [Clip.antiAlias]).
void pushClipRRect(RRect rrect, {Clip clip = Clip.antiAlias}) {
assert(clip != null);
assert(clip != Clip.none);
_pushClipRRect(rrect._value, clip.index);
}
void _pushClipRRect(Float32List rrect, int clipMode) native 'SceneBuilder_pushClipRRect';
/// Pushes a path clip operation onto the operation stack.
///
/// Rasterization outside the given path is discarded.
///
/// See [pop] for details about the operation stack.
void pushClipPath(Path path) native 'SceneBuilder_pushClipPath';
/// See [pop] for details about the operation stack. See [Clip] for different clip modes.
/// By default, the clip will be anti-aliased (clip = [Clip.antiAlias]).
void pushClipPath(Path path, {Clip clip = Clip.antiAlias}) {
assert(clip != null);
assert(clip != Clip.none);
_pushClipPath(path, clip.index);
}
void _pushClipPath(Path path, int clipMode) native 'SceneBuilder_pushClipPath';
/// Pushes an opacity operation onto the operation stack.
///
......@@ -143,16 +158,20 @@ class SceneBuilder extends NativeFieldWrapperClass2 {
/// Pushes a physical layer operation for an arbitrary shape onto the
/// operation stack.
///
/// Rasterization will be clipped to the given shape defined by [path]. If
/// [elevation] is greater than 0.0, then a shadow is drawn around the layer.
/// By default, the layer's content will not be clipped (clip = [Clip.none]).
/// If clip equals [Clip.hardEdge], [Clip.antiAlias], or [Clip.antiAliasWithSaveLayer],
/// then the content is clipped to the given shape defined by [path].
///
/// If [elevation] is greater than 0.0, then a shadow is drawn around the layer.
/// [shadowColor] defines the color of the shadow if present and [color] defines the
/// color of the layer background.
///
/// See [pop] for details about the operation stack.
void pushPhysicalShape({ Path path, double elevation, Color color, Color shadowColor}) {
_pushPhysicalShape(path, elevation, color.value, shadowColor?.value ?? 0xFF000000);
/// See [pop] for details about the operation stack, and [Clip] for different clip modes.
// ignore: deprecated_member_use
void pushPhysicalShape({ Path path, double elevation, Color color, Color shadowColor, Clip clip = defaultClipBehavior}) {
_pushPhysicalShape(path, elevation, color.value, shadowColor?.value ?? 0xFF000000, clip.index);
}
void _pushPhysicalShape(Path path, double elevation, int color, int shadowColor) native
void _pushPhysicalShape(Path path, double elevation, int color, int shadowColor, int clipMode) native
'SceneBuilder_pushPhysicalShape';
/// Ends the effect of the most recently pushed operation.
......
......@@ -62,16 +62,17 @@ void SceneBuilder::pushTransform(const tonic::Float64List& matrix4) {
void SceneBuilder::pushClipRect(double left,
double right,
double top,
double bottom) {
layer_builder_->PushClipRect(SkRect::MakeLTRB(left, top, right, bottom));
double bottom,
int clipMode) {
layer_builder_->PushClipRect(SkRect::MakeLTRB(left, top, right, bottom), static_cast<flow::ClipMode>(clipMode));
}
void SceneBuilder::pushClipRRect(const RRect& rrect) {
layer_builder_->PushClipRoundedRect(rrect.sk_rrect);
void SceneBuilder::pushClipRRect(const RRect& rrect, int clipMode) {
layer_builder_->PushClipRoundedRect(rrect.sk_rrect, static_cast<flow::ClipMode>(clipMode));
}
void SceneBuilder::pushClipPath(const CanvasPath* path) {
layer_builder_->PushClipPath(path->path());
void SceneBuilder::pushClipPath(const CanvasPath* path, int clipMode) {
layer_builder_->PushClipPath(path->path(), static_cast<flow::ClipMode>(clipMode));
}
void SceneBuilder::pushOpacity(int alpha) {
......@@ -103,13 +104,15 @@ void SceneBuilder::pushShaderMask(Shader* shader,
void SceneBuilder::pushPhysicalShape(const CanvasPath* path,
double elevation,
int color,
int shadow_color) {
int shadow_color,
int clip_mode) {
layer_builder_->PushPhysicalShape(
path->path(), //
elevation, //
static_cast<SkColor>(color), //
static_cast<SkColor>(shadow_color),
UIDartState::Current()->window()->viewport_metrics().device_pixel_ratio);
UIDartState::Current()->window()->viewport_metrics().device_pixel_ratio,
static_cast<flow::ClipMode>(clip_mode));
}
void SceneBuilder::pop() {
......
......@@ -35,9 +35,9 @@ class SceneBuilder : public fxl::RefCountedThreadSafe<SceneBuilder>,
~SceneBuilder() override;
void pushTransform(const tonic::Float64List& matrix4);
void pushClipRect(double left, double right, double top, double bottom);
void pushClipRRect(const RRect& rrect);
void pushClipPath(const CanvasPath* path);
void pushClipRect(double left, double right, double top, double bottom, int clipMode);
void pushClipRRect(const RRect& rrect, int clipMode);
void pushClipPath(const CanvasPath* path, int clipMode);
void pushOpacity(int alpha);
void pushColorFilter(int color, int blendMode);
void pushBackdropFilter(ImageFilter* filter);
......@@ -47,7 +47,7 @@ class SceneBuilder : public fxl::RefCountedThreadSafe<SceneBuilder>,
double maskRectTop,
double maskRectBottom,
int blendMode);
void pushPhysicalShape(const CanvasPath* path, double elevation, int color, int shadowColor);
void pushPhysicalShape(const CanvasPath* path, double elevation, int color, int shadowColor, int clipMode);
void pop();
......
......@@ -942,6 +942,89 @@ enum PaintingStyle {
stroke,
}
/// Different ways to clip a widget's content.
enum Clip {
/// No clip at all.
///
/// This is the default option for most widgets: if the content does not
/// overflow the widget boundary, don't pay any performance cost for clipping.
///
/// If the content does overflow, please explicitly specify the following
/// [Clip] options:
/// * [hardEdge], which is the fastest clipping, but with lower fidelity.
/// * [antiAlias], which is a little slower than [hardEdge], but with smoothed edges.
/// * [antiAliasWithSaveLayer], which is much slower than [antiAlias], and should
/// rarely be used.
none,
/// Clip, but do not apply anti-aliasing.
///
/// This mode enables clipping, but curves and non-axis-aligned straight lines will be
/// jagged as no effort is made to anti-alias.
///
/// Faster than other clipping modes, but slower than [none].
///
/// This is a reasonable choice when clipping is needed, if the container is an axis-
/// aligned rectangle or an axis-aligned rounded rectangle with very small corner radii.
///
/// See also:
///
/// * [antiAlias], which is more reasonable when clipping is needed and the shape is not
/// an axis-aligned rectangle.
hardEdge,
/// Clip with anti-aliasing.
///
/// This mode has anti-aliased clipping edges to achieve a smoother look.
///
/// It' s much faster than [antiAliasWithSaveLayer], but slower than [hardEdge].
///
/// This will be the common case when dealing with circles and arcs.
///
/// Different from [hardEdge] and [antiAliasWithSaveLayer], this clipping may have
/// bleeding edge artifacts.
/// (See https://fiddle.skia.org/c/21cb4c2b2515996b537f36e7819288ae for an example.)
///
/// See also:
///
/// * [hardEdge], which is a little faster, but with lower fidelity.
/// * [antiAliasWithSaveLayer], which is much slower, but can avoid the
/// bleeding edges if there's no other way.
/// * [Paint.isAntiAlias], which is the anti-aliasing switch for general draw operations.
antiAlias,
/// Clip with anti-aliasing and saveLayer immediately following the clip.
///
/// This mode not only clips with anti-aliasing, but also allocates an offscreen
/// buffer. All subsequent paints are carried out on that buffer before finally
/// being clipped and composited back.
///
/// This is very slow. It has no bleeding edge artifacts (that [antiAlias] has)
/// but it changes the semantics as an offscreen buffer is now introduced.
/// (See https://github.com/flutter/flutter/issues/18057#issuecomment-394197336
/// for a difference between paint without saveLayer and paint with saveLayer.)
///
/// This will be only rarely needed. One case where you might need this is if
/// you have an image overlaid on a very different background color. In these
/// cases, consider whether you can avoid overlaying multiple colors in one
/// spot (e.g. by having the background color only present where the image is
/// absent). If you can, [antiAlias] would be fine and much faster.
///
/// See also:
///
/// * [antiAlias], which is much faster, and has similar clipping results.
antiAliasWithSaveLayer,
}
/// The global default value of whether and how to clip a widget. This is only for
/// temporary migration from default-to-clip to default-to-NOT-clip.
///
// TODO(liyuqian): Set it to Clip.none. (https://github.com/flutter/flutter/issues/18057)
// We currently have Clip.antiAlias to preserve our old behaviors.
@Deprecated("Do not use this as it'll soon be removed after we set the default behavior to Clip.none.")
const Clip defaultClipBehavior = Clip.antiAlias;
// If we actually run on big endian machines, we'll need to do something smarter
// here. We don't use [Endian.Host] because it's not a compile-time
// constant and can't propagate into the set/get calls.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册