bool SVGClipPainter::applyClippingToContext(const LayoutObject& target, const FloatRect& targetBoundingBox, const FloatRect& paintInvalidationRect, GraphicsContext* context, ClipperState& clipperState) { ASSERT(context); ASSERT(clipperState == ClipperNotApplied); ASSERT_WITH_SECURITY_IMPLICATION(!m_clip.needsLayout()); if (paintInvalidationRect.isEmpty() || m_clip.hasCycle()) return false; SVGClipExpansionCycleHelper inClipExpansionChange(m_clip); AffineTransform animatedLocalTransform = toSVGClipPathElement(m_clip.element())->calculateAnimatedLocalTransform(); // 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 (!target.isSVG() && m_clip.clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) { ASSERT(m_clip.style()); animatedLocalTransform.scale(m_clip.style()->effectiveZoom()); } // First, try to apply the clip as a clipPath. if (m_clip.tryPathOnlyClipping(target, context, animatedLocalTransform, targetBoundingBox)) { clipperState = ClipperAppliedPath; return true; } // Fall back to masking. clipperState = ClipperAppliedMask; // Begin compositing the clip mask. CompositingRecorder::beginCompositing(*context, target, SkXfermode::kSrcOver_Mode, 1, &paintInvalidationRect); { TransformRecorder recorder(*context, target, animatedLocalTransform); // clipPath can also be clipped by another clipPath. SVGResources* resources = SVGResourcesCache::cachedResourcesForLayoutObject(&m_clip); LayoutSVGResourceClipper* clipPathClipper = resources ? resources->clipper() : 0; ClipperState clipPathClipperState = ClipperNotApplied; if (clipPathClipper && !SVGClipPainter(*clipPathClipper).applyClippingToContext(m_clip, targetBoundingBox, paintInvalidationRect, context, clipPathClipperState)) { // End the clip mask's compositor. CompositingRecorder::endCompositing(*context, target); return false; } drawClipMaskContent(context, target, targetBoundingBox); if (clipPathClipper) SVGClipPainter(*clipPathClipper).postApplyStatefulResource(m_clip, context, clipPathClipperState); } // Masked content layer start. CompositingRecorder::beginCompositing(*context, target, SkXfermode::kSrcIn_Mode, 1, &paintInvalidationRect); return true; }
SVGPaintContext::~SVGPaintContext() { if (m_filter) { ASSERT(SVGResourcesCache::cachedResourcesForLayoutObject(m_object)); ASSERT(SVGResourcesCache::cachedResourcesForLayoutObject(m_object)->filter() == m_filter); ASSERT(m_filterRecordingContext); SVGFilterPainter(*m_filter).finishEffect(*m_object, *m_filterRecordingContext); // Reset the paint info after the filter effect has been completed. // This isn't strictly required (e.g., m_paintInfo.rect is not used // after this). m_paintInfo.context = m_originalPaintInfo->context; m_paintInfo.m_cullRect.m_rect = m_originalPaintInfo->m_cullRect.m_rect; } if (m_masker) { ASSERT(SVGResourcesCache::cachedResourcesForLayoutObject(m_object)); ASSERT(SVGResourcesCache::cachedResourcesForLayoutObject(m_object)->masker() == m_masker); SVGMaskPainter(*m_masker).finishEffect(*m_object, m_paintInfo.context); } if (m_clipper) { ASSERT(SVGResourcesCache::cachedResourcesForLayoutObject(m_object)); ASSERT(SVGResourcesCache::cachedResourcesForLayoutObject(m_object)->clipper() == m_clipper); SVGClipPainter(*m_clipper).finishEffect(*m_object, m_paintInfo.context, m_clipperState); } }
bool SVGClipPainter::drawClipAsMask(GraphicsContext& context, const LayoutObject& layoutObject, const FloatRect& targetBoundingBox, const FloatRect& targetPaintInvalidationRect, const AffineTransform& localTransform) { if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(context, layoutObject, DisplayItem::SVGClip)) return true; SkPictureBuilder maskPictureBuilder(targetPaintInvalidationRect, nullptr, &context); GraphicsContext& maskContext = maskPictureBuilder.context(); { TransformRecorder recorder(maskContext, layoutObject, localTransform); // Create a clipPathClipper if this clipPath is clipped by another clipPath. SVGResources* resources = SVGResourcesCache::cachedResourcesForLayoutObject(&m_clip); LayoutSVGResourceClipper* clipPathClipper = resources ? resources->clipper() : nullptr; ClipperState clipPathClipperState = ClipperNotApplied; if (clipPathClipper && !SVGClipPainter(*clipPathClipper).prepareEffect(m_clip, targetBoundingBox, targetPaintInvalidationRect, maskContext, clipPathClipperState)) return false; { AffineTransform contentTransform; if (m_clip.clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { contentTransform.translate(targetBoundingBox.x(), targetBoundingBox.y()); contentTransform.scaleNonUniform(targetBoundingBox.width(), targetBoundingBox.height()); } SubtreeContentTransformScope contentTransformScope(contentTransform); TransformRecorder contentTransformRecorder(maskContext, layoutObject, contentTransform); RefPtr<const SkPicture> clipContentPicture = m_clip.createContentPicture(); maskContext.getPaintController().createAndAppend<DrawingDisplayItem>(layoutObject, DisplayItem::SVGClip, clipContentPicture.get()); } if (clipPathClipper) SVGClipPainter(*clipPathClipper).finishEffect(m_clip, maskContext, clipPathClipperState); } LayoutObjectDrawingRecorder drawingRecorder(context, layoutObject, DisplayItem::SVGClip, targetPaintInvalidationRect); RefPtr<SkPicture> maskPicture = maskPictureBuilder.endRecording(); context.drawPicture(maskPicture.get()); return true; }
ClipPathHelper(GraphicsContext* context, const PaintLayer& paintLayer, PaintLayerPaintingInfo& paintingInfo, LayoutRect& rootRelativeBounds, bool& rootRelativeBoundsComputed, const LayoutPoint& offsetFromRoot, PaintLayerFlags paintFlags) : m_resourceClipper(0), m_paintLayer(paintLayer), m_context(context) { const ComputedStyle& style = paintLayer.layoutObject()->styleRef(); // Clip-path, like border radius, must not be applied to the contents of a composited-scrolling container. // It must, however, still be applied to the mask layer, so that the compositor can properly mask the // scrolling contents and scrollbars. if (!paintLayer.layoutObject()->hasClipPath() || (paintLayer.needsCompositedScrolling() && !(paintFlags & PaintLayerPaintingChildClippingMaskPhase))) return; m_clipperState = SVGClipPainter::ClipperNotApplied; paintingInfo.ancestorHasClipPathClipping = true; ASSERT(style.clipPath()); if (style.clipPath()->type() == ClipPathOperation::SHAPE) { ShapeClipPathOperation* clipPath = toShapeClipPathOperation(style.clipPath()); if (clipPath->isValid()) { if (!rootRelativeBoundsComputed) { rootRelativeBounds = paintLayer.physicalBoundingBoxIncludingReflectionAndStackingChildren(offsetFromRoot); rootRelativeBoundsComputed = true; } m_clipPathRecorder.emplace(*context, *paintLayer.layoutObject(), clipPath->path(FloatRect(rootRelativeBounds))); } } else if (style.clipPath()->type() == ClipPathOperation::REFERENCE) { ReferenceClipPathOperation* referenceClipPathOperation = toReferenceClipPathOperation(style.clipPath()); Document& document = paintLayer.layoutObject()->document(); // FIXME: It doesn't work with forward or external SVG references (https://bugs.webkit.org/show_bug.cgi?id=90405) Element* element = document.getElementById(referenceClipPathOperation->fragment()); if (isSVGClipPathElement(element) && element->layoutObject()) { if (!rootRelativeBoundsComputed) { rootRelativeBounds = paintLayer.physicalBoundingBoxIncludingReflectionAndStackingChildren(offsetFromRoot); rootRelativeBoundsComputed = true; } m_resourceClipper = toLayoutSVGResourceClipper(toLayoutSVGResourceContainer(element->layoutObject())); if (!SVGClipPainter(*m_resourceClipper).prepareEffect(*paintLayer.layoutObject(), FloatRect(rootRelativeBounds), FloatRect(rootRelativeBounds), context, m_clipperState)) { // No need to post-apply the clipper if this failed. m_resourceClipper = 0; } } } }
bool SVGPaintContext::applyClipIfNecessary(SVGResources* resources) { // resources->clipper() corresponds to the non-prefixed 'clip-path' whereas // m_object->style()->clipPath() corresponds to '-webkit-clip-path'. // FIXME: We should unify the clip-path and -webkit-clip-path codepaths. if (LayoutSVGResourceClipper* clipper = resources ? resources->clipper() : nullptr) { if (!SVGClipPainter(*clipper).prepareEffect(*m_object, m_object->objectBoundingBox(), m_object->paintInvalidationRectInLocalCoordinates(), m_paintInfo.context, m_clipperState)) return false; m_clipper = clipper; } else { ClipPathOperation* clipPathOperation = m_object->style()->clipPath(); if (clipPathOperation && clipPathOperation->type() == ClipPathOperation::SHAPE) { ShapeClipPathOperation* clipPath = toShapeClipPathOperation(clipPathOperation); if (!clipPath->isValid()) return false; m_clipPathRecorder = adoptPtr(new ClipPathRecorder(*m_paintInfo.context, *m_object, clipPath->path(m_object->objectBoundingBox()))); } } return true; }
~ClipPathHelper() { if (m_resourceClipper) SVGClipPainter(*m_resourceClipper).finishEffect(*m_paintLayer.layoutObject(), m_context, m_clipperState); }
~ClipPathHelper() { if (m_resourceClipper) SVGClipPainter(*m_resourceClipper).postApplyStatefulResource(*m_paintLayer.layoutObject(), m_context, m_clipperState); }