void SVGRenderSupport::intersectRepaintRectWithResources(const RenderObject* object, FloatRect& repaintRect) { ASSERT(object); RenderStyle* style = object->style(); ASSERT(style); const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(svgStyle); RenderObject* renderer = const_cast<RenderObject*>(object); SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer); if (!resources) { if (const ShadowData* shadow = svgStyle->shadow()) shadow->adjustRectForShadow(repaintRect); return; } #if ENABLE(FILTERS) if (RenderSVGResourceFilter* filter = resources->filter()) repaintRect = filter->resourceBoundingBox(renderer); #endif if (RenderSVGResourceClipper* clipper = resources->clipper()) repaintRect.intersect(clipper->resourceBoundingBox(renderer)); if (RenderSVGResourceMasker* masker = resources->masker()) repaintRect.intersect(masker->resourceBoundingBox(renderer)); if (const ShadowData* shadow = svgStyle->shadow()) shadow->adjustRectForShadow(repaintRect); }
bool SVGLayoutSupport::pointInClippingArea(const LayoutObject& object, const FloatPoint& point) { ClipPathOperation* clipPathOperation = object.styleRef().clipPath(); if (!clipPathOperation) return true; if (clipPathOperation->type() == ClipPathOperation::SHAPE) { ShapeClipPathOperation& clipPath = toShapeClipPathOperation(*clipPathOperation); return clipPath.path(object.objectBoundingBox()).contains(point); } DCHECK_EQ(clipPathOperation->type(), ClipPathOperation::REFERENCE); SVGResources* resources = SVGResourcesCache::cachedResourcesForLayoutObject(&object); if (!resources || !resources->clipper()) return true; return resources->clipper()->hitTestClipContent(object.objectBoundingBox(), point); }
bool SVGRenderSupport::prepareToRenderSVGContent(RenderObject* object, PaintInfo& paintInfo) { ASSERT(object); RenderStyle* style = object->style(); ASSERT(style); const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(svgStyle); // Setup transparency layers before setting up SVG resources! float opacity = style->opacity(); const ShadowData* shadow = svgStyle->shadow(); if (opacity < 1 || shadow) { FloatRect repaintRect = object->repaintRectInLocalCoordinates(); if (opacity < 1) { paintInfo.context->clip(repaintRect); paintInfo.context->beginTransparencyLayer(opacity); } if (shadow) { paintInfo.context->clip(repaintRect); paintInfo.context->setShadow(IntSize(shadow->x(), shadow->y()), shadow->blur(), shadow->color(), style->colorSpace()); paintInfo.context->beginTransparencyLayer(1); } } SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object); if (!resources) { #if ENABLE(FILTERS) if (svgStyle->hasFilter()) return false; #endif return true; } if (RenderSVGResourceMasker* masker = resources->masker()) { if (!masker->applyResource(object, style, paintInfo.context, ApplyToDefaultMode)) return false; } if (RenderSVGResourceClipper* clipper = resources->clipper()) { if (!clipper->applyResource(object, style, paintInfo.context, ApplyToDefaultMode)) return false; } #if ENABLE(FILTERS) if (RenderSVGResourceFilter* filter = resources->filter()) { if (!filter->applyResource(object, style, paintInfo.context, ApplyToDefaultMode)) return false; } #endif return true; }
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; }
bool RenderSVGResourceClipper::applyClippingToContext(RenderObject* target, const FloatRect& targetBoundingBox, const FloatRect& repaintRect, GraphicsContext* context, ClipperContext& clipperContext) { ASSERT(target); ASSERT(context); ASSERT(clipperContext.state == ClipperContext::NotAppliedState); ASSERT_WITH_SECURITY_IMPLICATION(!needsLayout()); if (repaintRect.isEmpty() || m_inClipExpansion) return false; TemporaryChange<bool> inClipExpansionChange(m_inClipExpansion, true); // First, try to apply the clip as a clipPath. AffineTransform animatedLocalTransform = toSVGClipPathElement(element())->animatedLocalTransform(); if (tryPathOnlyClipping(context, animatedLocalTransform, targetBoundingBox)) { clipperContext.state = ClipperContext::AppliedPathState; return true; } // Fall back to masking. clipperContext.state = ClipperContext::AppliedMaskState; // Mask layer start context->beginTransparencyLayer(1, &repaintRect); { GraphicsContextStateSaver maskContentSaver(*context); context->concatCTM(animatedLocalTransform); // clipPath can also be clipped by another clipPath. SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this); RenderSVGResourceClipper* clipPathClipper = 0; ClipperContext clipPathClipperContext; if (resources && (clipPathClipper = resources->clipper())) { if (!clipPathClipper->applyClippingToContext(this, targetBoundingBox, repaintRect, context, clipPathClipperContext)) { // FIXME: Awkward state micro-management. Ideally, GraphicsContextStateSaver should // a) pop saveLayers also // b) pop multiple states if needed (similarly to SkCanvas::restoreToCount()) // Then we should be able to replace this mess with a single, top-level GCSS. maskContentSaver.restore(); context->restoreLayer(); return false; } } drawClipMaskContent(context, targetBoundingBox); if (clipPathClipper) clipPathClipper->postApplyStatefulResource(this, context, clipPathClipperContext); } // Masked content layer start. context->beginLayer(1, CompositeSourceIn, &repaintRect); return true; }
bool RenderSVGResourceClipper::applyClippingToContext(RenderObject* object, const FloatRect& objectBoundingBox, const FloatRect& repaintRect, GraphicsContext* context) { bool missingClipperData = !m_clipper.contains(object); if (missingClipperData) m_clipper.set(object, new ClipperData); bool shouldCreateClipData = false; AffineTransform animatedLocalTransform = static_cast<SVGClipPathElement*>(node())->animatedLocalTransform(); ClipperData* clipperData = m_clipper.get(object); if (!clipperData->clipMaskImage) { if (pathOnlyClipping(context, animatedLocalTransform, objectBoundingBox)) return true; shouldCreateClipData = true; } AffineTransform absoluteTransform; SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(object, absoluteTransform); if (shouldCreateClipData && !repaintRect.isEmpty()) { if (!SVGRenderingContext::createImageBuffer(repaintRect, absoluteTransform, clipperData->clipMaskImage, ColorSpaceDeviceRGB, Unaccelerated)) return false; GraphicsContext* maskContext = clipperData->clipMaskImage->context(); ASSERT(maskContext); maskContext->concatCTM(animatedLocalTransform); // clipPath can also be clipped by another clipPath. SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this); RenderSVGResourceClipper* clipper; bool succeeded; if (resources && (clipper = resources->clipper())) { GraphicsContextStateSaver stateSaver(*maskContext); if (!clipper->applyClippingToContext(this, objectBoundingBox, repaintRect, maskContext)) return false; succeeded = drawContentIntoMaskImage(clipperData, objectBoundingBox); // The context restore applies the clipping on non-CG platforms. } else succeeded = drawContentIntoMaskImage(clipperData, objectBoundingBox); if (!succeeded) clipperData->clipMaskImage.clear(); } if (!clipperData->clipMaskImage) return false; SVGRenderingContext::clipToImageBuffer(context, absoluteTransform, repaintRect, clipperData->clipMaskImage, missingClipperData); return true; }
bool SVGRenderSupport::pointInClippingArea(const RenderElement& renderer, const FloatPoint& point) { // We just take clippers into account to determine if a point is on the node. The Specification may // change later and we also need to check maskers. SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer); if (!resources) return true; if (RenderSVGResourceClipper* clipper = resources->clipper()) return clipper->hitTestClipContent(renderer.objectBoundingBox(), point); return true; }
bool SVGLayoutSupport::pointInClippingArea(LayoutObject* object, const FloatPoint& point) { ASSERT(object); // We just take clippers into account to determine if a point is on the node. The Specification may // change later and we also need to check maskers. SVGResources* resources = SVGResourcesCache::cachedResourcesForLayoutObject(object); if (!resources) return true; if (LayoutSVGResourceClipper* clipper = resources->clipper()) return clipper->hitTestClipContent(object->objectBoundingBox(), point); return true; }
void SVGRenderSupport::intersectRepaintRectWithResources(const RenderObject* renderer, FloatRect& repaintRect) { ASSERT(renderer); SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer); if (!resources) return; if (RenderSVGResourceFilter* filter = resources->filter()) repaintRect = filter->resourceBoundingBox(renderer); if (RenderSVGResourceClipper* clipper = resources->clipper()) repaintRect.intersect(clipper->resourceBoundingBox(renderer)); if (RenderSVGResourceMasker* masker = resources->masker()) repaintRect.intersect(masker->resourceBoundingBox(renderer)); }
void SVGLayoutSupport::intersectPaintInvalidationRectWithResources(const LayoutObject* layoutObject, FloatRect& paintInvalidationRect) { ASSERT(layoutObject); SVGResources* resources = SVGResourcesCache::cachedResourcesForLayoutObject(layoutObject); if (!resources) return; if (LayoutSVGResourceFilter* filter = resources->filter()) paintInvalidationRect = filter->resourceBoundingBox(layoutObject); if (LayoutSVGResourceClipper* clipper = resources->clipper()) paintInvalidationRect.intersect(clipper->resourceBoundingBox(layoutObject)); if (LayoutSVGResourceMasker* masker = resources->masker()) paintInvalidationRect.intersect(masker->resourceBoundingBox(layoutObject)); }
void SVGRenderSupport::intersectRepaintRectWithResources(const RenderElement& renderer, FloatRect& repaintRect) { SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(&renderer); if (!resources) return; #if ENABLE(FILTERS) if (RenderSVGResourceFilter* filter = resources->filter()) repaintRect = filter->resourceBoundingBox(renderer); #endif if (RenderSVGResourceClipper* clipper = resources->clipper()) repaintRect.intersect(clipper->resourceBoundingBox(renderer)); if (RenderSVGResourceMasker* masker = resources->masker()) repaintRect.intersect(masker->resourceBoundingBox(renderer)); }
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; }
bool RenderSVGResourceClipper::applyClippingToContext(RenderObject* target, const FloatRect& targetBoundingBox, const FloatRect& paintInvalidationRect, GraphicsContext* context, ClipperState& clipperState) { ASSERT(target); ASSERT(context); ASSERT(clipperState == ClipperNotApplied); ASSERT_WITH_SECURITY_IMPLICATION(!needsLayout()); if (paintInvalidationRect.isEmpty() || m_inClipExpansion) return false; TemporaryChange<bool> inClipExpansionChange(m_inClipExpansion, true); AffineTransform animatedLocalTransform = toSVGClipPathElement(element())->animatedLocalTransform(); // 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() && clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) { ASSERT(style()); animatedLocalTransform.scale(style()->effectiveZoom()); } // First, try to apply the clip as a clipPath. if (tryPathOnlyClipping(context, animatedLocalTransform, targetBoundingBox)) { clipperState = ClipperAppliedPath; return true; } // Fall back to masking. clipperState = ClipperAppliedMask; // Mask layer start context->beginTransparencyLayer(1, &paintInvalidationRect); { GraphicsContextStateSaver maskContentSaver(*context); context->concatCTM(animatedLocalTransform); // clipPath can also be clipped by another clipPath. SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this); RenderSVGResourceClipper* clipPathClipper = resources ? resources->clipper() : 0; ClipperState clipPathClipperState = ClipperNotApplied; if (clipPathClipper && !clipPathClipper->applyClippingToContext(this, targetBoundingBox, paintInvalidationRect, context, clipPathClipperState)) { // FIXME: Awkward state micro-management. Ideally, GraphicsContextStateSaver should // a) pop saveLayers also // b) pop multiple states if needed (similarly to SkCanvas::restoreToCount()) // Then we should be able to replace this mess with a single, top-level GCSS. maskContentSaver.restore(); context->restoreLayer(); return false; } drawClipMaskContent(context, targetBoundingBox); if (clipPathClipper) clipPathClipper->postApplyStatefulResource(this, context, clipPathClipperState); } // Masked content layer start. context->beginLayer(1, CompositeSourceIn, &paintInvalidationRect); return true; }