| Index: Source/core/rendering/svg/RenderSVGResourceClipper.cpp
|
| diff --git a/Source/core/rendering/svg/RenderSVGResourceClipper.cpp b/Source/core/rendering/svg/RenderSVGResourceClipper.cpp
|
| deleted file mode 100644
|
| index 049d674450be0a93612f37ec78c65bcbe511600b..0000000000000000000000000000000000000000
|
| --- a/Source/core/rendering/svg/RenderSVGResourceClipper.cpp
|
| +++ /dev/null
|
| @@ -1,375 +0,0 @@
|
| -/*
|
| - * Copyright (C) 2004, 2005, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
|
| - * Copyright (C) 2004, 2005, 2006, 2007, 2008 Rob Buis <buis@kde.org>
|
| - * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
|
| - * Copyright (C) 2011 Dirk Schulze <krit@webkit.org>
|
| - *
|
| - * This library is free software; you can redistribute it and/or
|
| - * modify it under the terms of the GNU Library General Public
|
| - * License as published by the Free Software Foundation; either
|
| - * version 2 of the License, or (at your option) any later version.
|
| - *
|
| - * This library is distributed in the hope that it will be useful,
|
| - * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
| - * Library General Public License for more details.
|
| - *
|
| - * You should have received a copy of the GNU Library General Public License
|
| - * along with this library; see the file COPYING.LIB. If not, write to
|
| - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
| - * Boston, MA 02110-1301, USA.
|
| - */
|
| -
|
| -#include "config.h"
|
| -#include "core/rendering/svg/RenderSVGResourceClipper.h"
|
| -
|
| -#include "core/SVGNames.h"
|
| -#include "core/dom/ElementTraversal.h"
|
| -#include "core/layout/HitTestResult.h"
|
| -#include "core/layout/PaintInfo.h"
|
| -#include "core/layout/svg/SVGLayoutSupport.h"
|
| -#include "core/layout/svg/SVGResources.h"
|
| -#include "core/layout/svg/SVGResourcesCache.h"
|
| -#include "core/svg/SVGUseElement.h"
|
| -#include "platform/RuntimeEnabledFeatures.h"
|
| -#include "platform/graphics/GraphicsContextStateSaver.h"
|
| -#include "platform/graphics/paint/ClipPathDisplayItem.h"
|
| -#include "platform/graphics/paint/DisplayItemList.h"
|
| -#include "third_party/skia/include/core/SkPicture.h"
|
| -#include "wtf/TemporaryChange.h"
|
| -
|
| -namespace blink {
|
| -
|
| -RenderSVGResourceClipper::RenderSVGResourceClipper(SVGClipPathElement* node)
|
| - : RenderSVGResourceContainer(node)
|
| - , m_inClipExpansion(false)
|
| -{
|
| -}
|
| -
|
| -RenderSVGResourceClipper::~RenderSVGResourceClipper()
|
| -{
|
| -}
|
| -
|
| -void RenderSVGResourceClipper::removeAllClientsFromCache(bool markForInvalidation)
|
| -{
|
| - m_clipContentPicture.clear();
|
| - m_clipBoundaries = FloatRect();
|
| - markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
|
| -}
|
| -
|
| -void RenderSVGResourceClipper::removeClientFromCache(LayoutObject* client, bool markForInvalidation)
|
| -{
|
| - ASSERT(client);
|
| - markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
|
| -}
|
| -
|
| -bool RenderSVGResourceClipper::applyStatefulResource(LayoutObject* object, GraphicsContext*& context, ClipperState& clipperState)
|
| -{
|
| - ASSERT(object);
|
| - ASSERT(context);
|
| -
|
| - clearInvalidationMask();
|
| -
|
| - return applyClippingToContext(object, object->objectBoundingBox(), object->paintInvalidationRectInLocalCoordinates(), context, clipperState);
|
| -}
|
| -
|
| -bool RenderSVGResourceClipper::tryPathOnlyClipping(DisplayItemClient client, GraphicsContext* context,
|
| - const AffineTransform& animatedLocalTransform, const FloatRect& objectBoundingBox) {
|
| - // If the current clip-path gets clipped itself, we have to fallback to masking.
|
| - if (!style()->svgStyle().clipperResource().isEmpty())
|
| - return false;
|
| - WindRule clipRule = RULE_NONZERO;
|
| - Path clipPath = Path();
|
| -
|
| - for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement)) {
|
| - LayoutObject* renderer = childElement->renderer();
|
| - if (!renderer)
|
| - continue;
|
| - // Only shapes or paths are supported for direct clipping. We need to fallback to masking for texts.
|
| - if (renderer->isSVGText())
|
| - return false;
|
| - if (!childElement->isSVGGraphicsElement())
|
| - continue;
|
| - SVGGraphicsElement* styled = toSVGGraphicsElement(childElement);
|
| - const LayoutStyle* style = renderer->style();
|
| - if (!style || style->display() == NONE || style->visibility() != VISIBLE)
|
| - continue;
|
| - const SVGLayoutStyle& svgStyle = style->svgStyle();
|
| - // Current shape in clip-path gets clipped too. Fallback to masking.
|
| - if (!svgStyle.clipperResource().isEmpty())
|
| - return false;
|
| -
|
| - if (clipPath.isEmpty()) {
|
| - // First clip shape.
|
| - styled->toClipPath(clipPath);
|
| - clipRule = svgStyle.clipRule();
|
| - clipPath.setWindRule(clipRule);
|
| - continue;
|
| - }
|
| -
|
| - if (RuntimeEnabledFeatures::pathOpsSVGClippingEnabled()) {
|
| - // Attempt to generate a combined clip path, fall back to masking if not possible.
|
| - Path subPath;
|
| - styled->toClipPath(subPath);
|
| - subPath.setWindRule(svgStyle.clipRule());
|
| - if (!clipPath.unionPath(subPath))
|
| - return false;
|
| - } else {
|
| - return false;
|
| - }
|
| - }
|
| - // Only one visible shape/path was found. Directly continue clipping and transform the content to userspace if necessary.
|
| - if (clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
|
| - AffineTransform transform;
|
| - transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
|
| - transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
|
| - clipPath.transform(transform);
|
| - }
|
| -
|
| - // Transform path by animatedLocalTransform.
|
| - clipPath.transform(animatedLocalTransform);
|
| -
|
| - // The SVG specification wants us to clip everything, if clip-path doesn't have a child.
|
| - if (clipPath.isEmpty())
|
| - clipPath.addRect(FloatRect());
|
| -
|
| - if (RuntimeEnabledFeatures::slimmingPaintEnabled()) {
|
| - context->displayItemList()->add(BeginClipPathDisplayItem::create(client, clipPath, clipRule));
|
| - } else {
|
| - BeginClipPathDisplayItem clipPathDisplayItem(client, clipPath, clipRule);
|
| - clipPathDisplayItem.replay(context);
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -bool RenderSVGResourceClipper::applyClippingToContext(LayoutObject* 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())->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() && clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) {
|
| - ASSERT(style());
|
| - animatedLocalTransform.scale(style()->effectiveZoom());
|
| - }
|
| -
|
| - // First, try to apply the clip as a clipPath.
|
| - if (tryPathOnlyClipping(target->displayItemClient(), 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::cachedResourcesForLayoutObject(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->endLayer();
|
| - return false;
|
| - }
|
| -
|
| - drawClipMaskContent(context, targetBoundingBox);
|
| -
|
| - if (clipPathClipper)
|
| - clipPathClipper->postApplyStatefulResource(this, context, clipPathClipperState);
|
| - }
|
| -
|
| - // Masked content layer start.
|
| - context->beginLayer(1, SkXfermode::kSrcIn_Mode, &paintInvalidationRect);
|
| -
|
| - return true;
|
| -}
|
| -
|
| -void RenderSVGResourceClipper::postApplyStatefulResource(LayoutObject* target, GraphicsContext*& context, ClipperState& clipperState)
|
| -{
|
| - switch (clipperState) {
|
| - case ClipperAppliedPath:
|
| - // Path-only clipping, no layers to restore but we need to emit an end to the clip path display item.
|
| - {
|
| - if (RuntimeEnabledFeatures::slimmingPaintEnabled()) {
|
| - context->displayItemList()->add(EndClipPathDisplayItem::create(target->displayItemClient()));
|
| - } else {
|
| - EndClipPathDisplayItem endClipPathDisplayItem(target->displayItemClient());
|
| - endClipPathDisplayItem.replay(context);
|
| - }
|
| - }
|
| - break;
|
| - case ClipperAppliedMask:
|
| - // Transfer content layer -> mask layer (SrcIn)
|
| - context->endLayer();
|
| - // Transfer mask layer -> bg layer (SrcOver)
|
| - context->endLayer();
|
| - break;
|
| - default:
|
| - ASSERT_NOT_REACHED();
|
| - }
|
| -}
|
| -
|
| -void RenderSVGResourceClipper::drawClipMaskContent(GraphicsContext* context, const FloatRect& targetBoundingBox)
|
| -{
|
| - ASSERT(context);
|
| -
|
| - AffineTransform contentTransformation;
|
| - if (clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
|
| - contentTransformation.translate(targetBoundingBox.x(), targetBoundingBox.y());
|
| - contentTransformation.scaleNonUniform(targetBoundingBox.width(), targetBoundingBox.height());
|
| - context->concatCTM(contentTransformation);
|
| - }
|
| -
|
| - if (!m_clipContentPicture) {
|
| - SubtreeContentTransformScope contentTransformScope(contentTransformation);
|
| - createPicture(context);
|
| - }
|
| -
|
| - context->drawPicture(m_clipContentPicture.get());
|
| -}
|
| -
|
| -void RenderSVGResourceClipper::createPicture(GraphicsContext* context)
|
| -{
|
| - ASSERT(context);
|
| - ASSERT(frame());
|
| -
|
| - // Using strokeBoundingBox (instead of paintInvalidationRectInLocalCoordinates) to avoid the intersection
|
| - // with local clips/mask, which may yield incorrect results when mixing objectBoundingBox and
|
| - // userSpaceOnUse units (http://crbug.com/294900).
|
| - FloatRect bounds = strokeBoundingBox();
|
| - context->beginRecording(bounds);
|
| -
|
| - for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement)) {
|
| - LayoutObject* renderer = childElement->renderer();
|
| - if (!renderer)
|
| - continue;
|
| -
|
| - const LayoutStyle* style = renderer->style();
|
| - if (!style || style->display() == NONE || style->visibility() != VISIBLE)
|
| - continue;
|
| -
|
| - WindRule newClipRule = style->svgStyle().clipRule();
|
| - bool isUseElement = isSVGUseElement(*childElement);
|
| - if (isUseElement) {
|
| - SVGUseElement& useElement = toSVGUseElement(*childElement);
|
| - renderer = useElement.rendererClipChild();
|
| - if (!renderer)
|
| - continue;
|
| - if (!useElement.hasAttribute(SVGNames::clip_ruleAttr))
|
| - newClipRule = renderer->style()->svgStyle().clipRule();
|
| - }
|
| -
|
| - // Only shapes, paths and texts are allowed for clipping.
|
| - if (!renderer->isSVGShape() && !renderer->isSVGText())
|
| - continue;
|
| -
|
| - context->setFillRule(newClipRule);
|
| -
|
| - if (isUseElement)
|
| - renderer = childElement->renderer();
|
| -
|
| - // Switch to a paint behavior where all children of this <clipPath> will be rendered using special constraints:
|
| - // - fill-opacity/stroke-opacity/opacity set to 1
|
| - // - masker/filter not applied when rendering the children
|
| - // - fill is set to the initial fill paint server (solid, black)
|
| - // - stroke is set to the initial stroke paint server (none)
|
| - PaintInfo info(context, LayoutRect::infiniteIntRect(), PaintPhaseForeground, PaintBehaviorRenderingClipPathAsMask);
|
| - renderer->paint(info, IntPoint());
|
| - }
|
| -
|
| - m_clipContentPicture = context->endRecording();
|
| -}
|
| -
|
| -void RenderSVGResourceClipper::calculateClipContentPaintInvalidationRect()
|
| -{
|
| - // This is a rough heuristic to appraise the clip size and doesn't consider clip on clip.
|
| - for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement)) {
|
| - LayoutObject* renderer = childElement->renderer();
|
| - if (!renderer)
|
| - continue;
|
| - if (!renderer->isSVGShape() && !renderer->isSVGText() && !isSVGUseElement(*childElement))
|
| - continue;
|
| - const LayoutStyle* style = renderer->style();
|
| - if (!style || style->display() == NONE || style->visibility() != VISIBLE)
|
| - continue;
|
| - m_clipBoundaries.unite(renderer->localToParentTransform().mapRect(renderer->paintInvalidationRectInLocalCoordinates()));
|
| - }
|
| - m_clipBoundaries = toSVGClipPathElement(element())->calculateAnimatedLocalTransform().mapRect(m_clipBoundaries);
|
| -}
|
| -
|
| -bool RenderSVGResourceClipper::hitTestClipContent(const FloatRect& objectBoundingBox, const FloatPoint& nodeAtPoint)
|
| -{
|
| - FloatPoint point = nodeAtPoint;
|
| - if (!SVGLayoutSupport::pointInClippingArea(this, point))
|
| - return false;
|
| -
|
| - if (clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
|
| - AffineTransform transform;
|
| - transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
|
| - transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
|
| - point = transform.inverse().mapPoint(point);
|
| - }
|
| -
|
| - AffineTransform animatedLocalTransform = toSVGClipPathElement(element())->calculateAnimatedLocalTransform();
|
| - if (!animatedLocalTransform.isInvertible())
|
| - return false;
|
| -
|
| - point = animatedLocalTransform.inverse().mapPoint(point);
|
| -
|
| - for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement)) {
|
| - LayoutObject* renderer = childElement->renderer();
|
| - if (!renderer)
|
| - continue;
|
| - if (!renderer->isSVGShape() && !renderer->isSVGText() && !isSVGUseElement(*childElement))
|
| - continue;
|
| - IntPoint hitPoint;
|
| - HitTestResult result(hitPoint);
|
| - if (renderer->nodeAtFloatPoint(HitTestRequest(HitTestRequest::SVGClipContent), result, point, HitTestForeground))
|
| - return true;
|
| - }
|
| -
|
| - return false;
|
| -}
|
| -
|
| -FloatRect RenderSVGResourceClipper::resourceBoundingBox(const LayoutObject* object)
|
| -{
|
| - // Resource was not layouted yet. Give back the boundingBox of the object.
|
| - if (selfNeedsLayout())
|
| - return object->objectBoundingBox();
|
| -
|
| - if (m_clipBoundaries.isEmpty())
|
| - calculateClipContentPaintInvalidationRect();
|
| -
|
| - if (clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
|
| - FloatRect objectBoundingBox = object->objectBoundingBox();
|
| - AffineTransform transform;
|
| - transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
|
| - transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
|
| - return transform.mapRect(m_clipBoundaries);
|
| - }
|
| -
|
| - return m_clipBoundaries;
|
| -}
|
| -
|
| -}
|
|
|