Chromium Code Reviews| Index: third_party/WebKit/Source/core/paint/ClipPathClipper.cpp |
| diff --git a/third_party/WebKit/Source/core/paint/ClipPathClipper.cpp b/third_party/WebKit/Source/core/paint/ClipPathClipper.cpp |
| index bf8dd25eaedc6c1b2dd16b66078c90629e99c041..58bb12919ff0b5f86c303e23960faae416fd0dcd 100644 |
| --- a/third_party/WebKit/Source/core/paint/ClipPathClipper.cpp |
| +++ b/third_party/WebKit/Source/core/paint/ClipPathClipper.cpp |
| @@ -4,17 +4,36 @@ |
| #include "core/paint/ClipPathClipper.h" |
| +#include "core/dom/ElementTraversal.h" |
| #include "core/layout/svg/LayoutSVGResourceClipper.h" |
| +#include "core/layout/svg/SVGLayoutSupport.h" |
| #include "core/layout/svg/SVGResources.h" |
| #include "core/layout/svg/SVGResourcesCache.h" |
| -#include "core/paint/SVGClipPainter.h" |
| +#include "core/paint/LayoutObjectDrawingRecorder.h" |
| +#include "core/paint/PaintInfo.h" |
| +#include "core/paint/TransformRecorder.h" |
| #include "core/style/ClipPathOperation.h" |
| +#include "platform/graphics/paint/ClipPathDisplayItem.h" |
| #include "platform/graphics/paint/ClipPathRecorder.h" |
| +#include "platform/graphics/paint/DrawingDisplayItem.h" |
| +#include "platform/graphics/paint/PaintController.h" |
| +#include "platform/graphics/paint/PaintRecordBuilder.h" |
| namespace blink { |
| namespace { |
| +class SVGClipExpansionCycleHelper { |
| + public: |
| + SVGClipExpansionCycleHelper(LayoutSVGResourceClipper& clip) : m_clip(clip) { |
| + clip.beginClipExpansion(); |
| + } |
| + ~SVGClipExpansionCycleHelper() { m_clip.endClipExpansion(); } |
| + |
| + private: |
| + LayoutSVGResourceClipper& m_clip; |
| +}; |
| + |
| LayoutSVGResourceClipper* resolveElementReference( |
| const LayoutObject& layoutObject, |
| const ReferenceClipPathOperation& referenceClipPathOperation) { |
| @@ -57,33 +76,165 @@ ClipPathClipper::ClipPathClipper(GraphicsContext& context, |
| m_clipperState = ClipperState::AppliedPath; |
| } else { |
| DCHECK_EQ(clipPathOperation.type(), ClipPathOperation::REFERENCE); |
| - LayoutSVGResourceClipper* clipper = resolveElementReference( |
| + m_resourceClipper = resolveElementReference( |
| layoutObject, toReferenceClipPathOperation(clipPathOperation)); |
| - if (!clipper) |
| + if (!m_resourceClipper) |
| return; |
| // Compute the (conservative) bounds of the clip-path. |
| - FloatRect clipPathBounds = clipper->resourceBoundingBox(referenceBox); |
| + FloatRect clipPathBounds = |
| + m_resourceClipper->resourceBoundingBox(referenceBox); |
| // When SVG applies the clip, and the coordinate system is "userspace on |
| // use", we must explicitly pass in the offset to have the clip paint in the |
| // correct location. When the coordinate system is "object bounding box" the |
| // offset should already be accounted for in the reference box. |
| FloatPoint originTranslation; |
| - if (clipper->clipPathUnits() == SVGUnitTypes::kSvgUnitTypeUserspaceonuse) { |
| + if (m_resourceClipper->clipPathUnits() == |
| + SVGUnitTypes::kSvgUnitTypeUserspaceonuse) { |
| clipPathBounds.moveBy(origin); |
| originTranslation = origin; |
| } |
| - if (!SVGClipPainter(*clipper).prepareEffect( |
| - layoutObject, referenceBox, clipPathBounds, originTranslation, |
| - context, m_clipperState)) |
| + if (!prepareEffect(referenceBox, clipPathBounds, originTranslation)) { |
| + // Indicate there is no cleanup to do. |
| + m_resourceClipper = nullptr; |
| return; |
| - m_resourceClipper = clipper; |
| + } |
| } |
| } |
| ClipPathClipper::~ClipPathClipper() { |
| if (m_resourceClipper) |
| - SVGClipPainter(*m_resourceClipper) |
| - .finishEffect(m_layoutObject, m_context, m_clipperState); |
| + finishEffect(); |
| +} |
| + |
| +bool ClipPathClipper::prepareEffect(const FloatRect& targetBoundingBox, |
| + const FloatRect& visualRect, |
| + const FloatPoint& layerPositionOffset) { |
| + DCHECK_EQ(m_clipperState, ClipperState::NotApplied); |
| + SECURITY_DCHECK(!m_resourceClipper->needsLayout()); |
| + |
| + m_resourceClipper->clearInvalidationMask(); |
| + |
| + if (m_resourceClipper->hasCycle()) |
| + return false; |
| + |
| + SVGClipExpansionCycleHelper inClipExpansionChange(*m_resourceClipper); |
| + |
| + AffineTransform animatedLocalTransform = |
| + toSVGClipPathElement(m_resourceClipper->element()) |
| + ->calculateTransform(SVGElement::IncludeMotionTransform); |
| + // When drawing a clip for non-SVG elements, the CTM does not include the zoom |
| + // factor. In this case, we need to apply the zoom scale explicitly - but |
| + // only for clips with userSpaceOnUse units (the zoom is accounted for |
| + // objectBoundingBox-resolved lengths). |
| + if (!m_layoutObject.isSVG() && m_resourceClipper->clipPathUnits() == |
| + SVGUnitTypes::kSvgUnitTypeUserspaceonuse) { |
| + DCHECK(m_resourceClipper->style()); |
| + animatedLocalTransform.scale(m_resourceClipper->style()->effectiveZoom()); |
| + } |
| + |
| + // First, try to apply the clip as a clipPath. |
| + Path clipPath; |
| + if (m_resourceClipper->asPath(animatedLocalTransform, targetBoundingBox, |
| + clipPath)) { |
| + AffineTransform positionTransform; |
| + positionTransform.translate(layerPositionOffset.x(), |
| + layerPositionOffset.y()); |
| + clipPath.transform(positionTransform); |
| + m_clipperState = ClipperState::AppliedPath; |
| + m_context.getPaintController().createAndAppend<BeginClipPathDisplayItem>( |
| + m_layoutObject, clipPath); |
| + return true; |
| + } |
| + |
| + // Fall back to masking. |
| + m_clipperState = ClipperState::AppliedMask; |
| + |
| + // Begin compositing the clip mask. |
| + m_maskClipRecorder.emplace(m_context, m_layoutObject, SkBlendMode::kSrcOver, |
| + 1, &visualRect); |
| + { |
| + if (!drawClipAsMask(targetBoundingBox, visualRect, animatedLocalTransform, |
| + layerPositionOffset)) { |
| + // End the clip mask's compositor. |
| + m_maskClipRecorder.reset(); |
| + return false; |
| + } |
| + } |
| + |
| + // Masked content layer start. |
| + m_maskContentRecorder.emplace(m_context, m_layoutObject, SkBlendMode::kSrcIn, |
| + 1, &visualRect); |
| + |
| + return true; |
| +} |
| + |
| +bool ClipPathClipper::drawClipAsMask(const FloatRect& targetBoundingBox, |
| + const FloatRect& targetVisualRect, |
| + const AffineTransform& localTransform, |
| + const FloatPoint& layerPositionOffset) { |
| + if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible( |
| + m_context, m_layoutObject, DisplayItem::kSVGClip)) |
| + return true; |
| + |
| + PaintRecordBuilder maskBuilder(targetVisualRect, nullptr, &m_context); |
| + GraphicsContext& maskContext = maskBuilder.context(); |
| + { |
| + TransformRecorder recorder(maskContext, m_layoutObject, localTransform); |
| + |
| + // Apply any clip-path clipping this clipPath (nested shape/clipPath.) |
| + Optional<ClipPathClipper> nestedClipPathClipper; |
| + if (ClipPathOperation* clipPathOperation = |
| + m_resourceClipper->styleRef().clipPath()) { |
| + nestedClipPathClipper.emplace(maskContext, *clipPathOperation, |
| + *m_resourceClipper, targetBoundingBox, |
| + layerPositionOffset); |
| + } |
| + |
| + { |
| + AffineTransform contentTransform; |
| + if (m_resourceClipper->clipPathUnits() == |
| + SVGUnitTypes::kSvgUnitTypeObjectboundingbox) { |
| + contentTransform.translate(targetBoundingBox.x(), |
| + targetBoundingBox.y()); |
| + contentTransform.scaleNonUniform(targetBoundingBox.width(), |
| + targetBoundingBox.height()); |
| + } |
| + SubtreeContentTransformScope contentTransformScope(contentTransform); |
| + |
| + TransformRecorder contentTransformRecorder(maskContext, m_layoutObject, |
| + contentTransform); |
| + maskContext.getPaintController().createAndAppend<DrawingDisplayItem>( |
| + m_layoutObject, DisplayItem::kSVGClip, |
| + m_resourceClipper->createPaintRecord()); |
| + } |
| + } |
| + |
| + LayoutObjectDrawingRecorder drawingRecorder( |
| + m_context, m_layoutObject, DisplayItem::kSVGClip, targetVisualRect); |
| + sk_sp<PaintRecord> maskPaintRecord = maskBuilder.endRecording(); |
| + m_context.drawRecord(maskPaintRecord.get()); |
| + return true; |
| +} |
| + |
| +void ClipPathClipper::finishEffect() { |
| + switch (m_clipperState) { |
| + case ClipperState::AppliedPath: |
| + // Path-only clipping, no layers to restore but we need to emit an end to |
| + // the clip path display item. |
| + m_context.getPaintController().endItem<EndClipPathDisplayItem>( |
| + m_layoutObject); |
| + break; |
| + case ClipperState::AppliedMask: |
| + // Transfer content -> clip mask (SrcIn) |
| + m_maskContentRecorder.reset(); |
| + |
| + // Transfer clip mask -> bg (SrcOver) |
| + m_maskClipRecorder.reset(); |
| + break; |
| + case ClipperState::NotApplied: |
|
danakj
2017/03/07 15:46:57
I made one little change here since I noticed it.
|
| + NOTREACHED(); |
| + break; |
| + } |
| } |
| } // namespace blink |