Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1339)

Unified Diff: Source/core/rendering/svg/RenderSVGResourceClipper.cpp

Issue 23643003: ImageBuffer-less SVG masking and clipping. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Fix the Win build. Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: Source/core/rendering/svg/RenderSVGResourceClipper.cpp
diff --git a/Source/core/rendering/svg/RenderSVGResourceClipper.cpp b/Source/core/rendering/svg/RenderSVGResourceClipper.cpp
index 9da09f9d9f0c299df32978dd0d74385de5bcab12..7a0528220aa73d1d2f34db1007e1bc71355b0474 100644
--- a/Source/core/rendering/svg/RenderSVGResourceClipper.cpp
+++ b/Source/core/rendering/svg/RenderSVGResourceClipper.cpp
@@ -33,6 +33,7 @@
#include "core/rendering/svg/SVGResources.h"
#include "core/rendering/svg/SVGResourcesCache.h"
#include "core/svg/SVGUseElement.h"
+#include "wtf/TemporaryChange.h"
namespace WebCore {
@@ -40,35 +41,25 @@ RenderSVGResourceType RenderSVGResourceClipper::s_resourceType = ClipperResource
RenderSVGResourceClipper::RenderSVGResourceClipper(SVGClipPathElement* node)
: RenderSVGResourceContainer(node)
+ , m_inClipExpansion(false)
{
}
RenderSVGResourceClipper::~RenderSVGResourceClipper()
{
- if (m_clipper.isEmpty())
- return;
-
- deleteAllValues(m_clipper);
- m_clipper.clear();
}
void RenderSVGResourceClipper::removeAllClientsFromCache(bool markForInvalidation)
{
m_clipBoundaries = FloatRect();
- if (!m_clipper.isEmpty()) {
- deleteAllValues(m_clipper);
- m_clipper.clear();
- }
-
+ m_rendererToClipperMap.clear();
markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
}
void RenderSVGResourceClipper::removeClientFromCache(RenderObject* client, bool markForInvalidation)
{
ASSERT(client);
- if (m_clipper.contains(client))
- delete m_clipper.take(client);
-
+ m_rendererToClipperMap.remove(client);
markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
}
@@ -81,8 +72,8 @@ bool RenderSVGResourceClipper::applyResource(RenderObject* object, RenderStyle*,
return applyClippingToContext(object, object->objectBoundingBox(), object->repaintRectInLocalCoordinates(), context);
}
-bool RenderSVGResourceClipper::pathOnlyClipping(GraphicsContext* context, const AffineTransform& animatedLocalTransform, const FloatRect& objectBoundingBox)
-{
+bool RenderSVGResourceClipper::tryPathOnlyClipping(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;
@@ -144,74 +135,93 @@ bool RenderSVGResourceClipper::pathOnlyClipping(GraphicsContext* context, const
return true;
}
-bool RenderSVGResourceClipper::applyClippingToContext(RenderObject* object, const FloatRect& objectBoundingBox,
+bool RenderSVGResourceClipper::applyClippingToContext(RenderObject* target, const FloatRect& targetBoundingBox,
const FloatRect& repaintRect, GraphicsContext* context)
{
- bool missingClipperData = !m_clipper.contains(object);
- if (missingClipperData)
- m_clipper.set(object, new ClipperData);
+ ASSERT(target);
+ ASSERT(context);
+ ASSERT_WITH_SECURITY_IMPLICATION(!needsLayout());
- bool shouldCreateClipData = false;
- AffineTransform animatedLocalTransform = toSVGClipPathElement(node())->animatedLocalTransform();
- ClipperData* clipperData = m_clipper.get(object);
- if (!clipperData->clipMaskImage) {
- if (pathOnlyClipping(context, animatedLocalTransform, objectBoundingBox))
- return true;
- shouldCreateClipData = true;
- }
+ if (repaintRect.isEmpty() || m_inClipExpansion)
+ return false;
+ TemporaryChange<bool> inClipExpansionChange(m_inClipExpansion, true);
- AffineTransform absoluteTransform;
- SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(object, absoluteTransform);
+ // add(obj, 0) idiom -> lookup & add if not found.
+ OwnPtr<ClipperData>& clipperData = m_rendererToClipperMap.add(target, nullptr).iterator->value;
+ if (!clipperData)
+ clipperData = adoptPtr(new ClipperData);
- if (shouldCreateClipData && !repaintRect.isEmpty()) {
- if (!SVGRenderingContext::createImageBuffer(repaintRect, absoluteTransform, clipperData->clipMaskImage, Unaccelerated))
- return false;
+ // First, try to apply the clip as a clipPath.
+ AffineTransform animatedLocalTransform = toSVGClipPathElement(node())->animatedLocalTransform();
+ if (tryPathOnlyClipping(context, animatedLocalTransform, targetBoundingBox)) {
+ clipperData->clipMode = ClipperData::PathOnlyClipMode;
+ return true;
+ }
- GraphicsContext* maskContext = clipperData->clipMaskImage->context();
- ASSERT(maskContext);
+ // Fall back to masking.
+ clipperData->clipMode = ClipperData::MaskClipMode;
- maskContext->concatCTM(animatedLocalTransform);
+ // 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* clipper;
- bool succeeded;
- if (resources && (clipper = resources->clipper())) {
- GraphicsContextStateSaver stateSaver(*maskContext);
-
- if (!clipper->applyClippingToContext(this, objectBoundingBox, repaintRect, maskContext))
+ RenderSVGResourceClipper* clipPathClipper = 0;
+ if (resources && (clipPathClipper = resources->clipper())) {
+ if (!clipPathClipper->applyClippingToContext(this, targetBoundingBox, repaintRect, context)) {
+ // 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;
+ }
+ }
- succeeded = drawContentIntoMaskImage(clipperData, objectBoundingBox);
- // The context restore applies the clipping on non-CG platforms.
- } else
- succeeded = drawContentIntoMaskImage(clipperData, objectBoundingBox);
+ drawMaskContent(context, targetBoundingBox);
- if (!succeeded)
- clipperData->clipMaskImage.clear();
+ if (clipPathClipper)
+ clipPathClipper->postApplyResource(this, context, ApplyToDefaultMode, 0, 0);
}
- if (!clipperData->clipMaskImage)
- return false;
+ // Masked content layer start.
+ context->beginMaskedLayer(repaintRect);
- SVGRenderingContext::clipToImageBuffer(context, absoluteTransform, repaintRect, clipperData->clipMaskImage, missingClipperData);
return true;
}
-bool RenderSVGResourceClipper::drawContentIntoMaskImage(ClipperData* clipperData, const FloatRect& objectBoundingBox)
-{
- ASSERT(frame());
+void RenderSVGResourceClipper::postApplyResource(RenderObject* object, GraphicsContext*& context,
+ unsigned short resourceMode, const Path*, const RenderSVGShape*) {
+ ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
+
+ ClipperData* clipperData = m_rendererToClipperMap.get(object);
ASSERT(clipperData);
- ASSERT(clipperData->clipMaskImage);
- GraphicsContext* maskContext = clipperData->clipMaskImage->context();
- ASSERT(maskContext);
+ // Path-only clipping, no layers to restore.
+ if (clipperData->clipMode == ClipperData::PathOnlyClipMode)
+ return;
+
+ // Transfer content layer -> mask layer (SrcIn)
+ context->endLayer();
+ // Transfer mask layer -> bg layer (SrcOver)
+ context->endLayer();
+}
+
+void RenderSVGResourceClipper::drawMaskContent(GraphicsContext* context, const FloatRect& targetBoundingBox)
+{
+ ASSERT(frame());
+ ASSERT(context);
+ // Adjust the mask image context according to the target objectBoundingBox.
AffineTransform maskContentTransformation;
if (toSVGClipPathElement(node())->clipPathUnitsCurrentValue() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
- maskContentTransformation.translate(objectBoundingBox.x(), objectBoundingBox.y());
- maskContentTransformation.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
- maskContext->concatCTM(maskContentTransformation);
+ maskContentTransformation.translate(targetBoundingBox.x(), targetBoundingBox.y());
+ maskContentTransformation.scaleNonUniform(targetBoundingBox.width(), targetBoundingBox.height());
+ context->concatCTM(maskContentTransformation);
}
// Switch to a paint behavior where all children of this <clipPath> will be rendered using special constraints:
@@ -222,15 +232,11 @@ bool RenderSVGResourceClipper::drawContentIntoMaskImage(ClipperData* clipperData
PaintBehavior oldBehavior = frame()->view()->paintBehavior();
frame()->view()->setPaintBehavior(oldBehavior | PaintBehaviorRenderingSVGMask);
- // Draw all clipPath children into a global mask.
for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) {
RenderObject* renderer = childNode->renderer();
if (!childNode->isSVGElement() || !renderer)
continue;
- if (renderer->needsLayout()) {
- frame()->view()->setPaintBehavior(oldBehavior);
- return false;
- }
+
RenderStyle* style = renderer->style();
if (!style || style->display() == NONE || style->visibility() != VISIBLE)
continue;
@@ -250,16 +256,15 @@ bool RenderSVGResourceClipper::drawContentIntoMaskImage(ClipperData* clipperData
if (!renderer->isSVGShape() && !renderer->isSVGText())
continue;
- maskContext->setFillRule(newClipRule);
+ context->setFillRule(newClipRule);
- // In the case of a <use> element, we obtained its renderere above, to retrieve its clipRule.
- // We have to pass the <use> renderer itself to renderSubtreeToImageBuffer() to apply it's x/y/transform/etc. values when rendering.
- // So if isUseElement is true, refetch the childNode->renderer(), as renderer got overriden above.
- SVGRenderingContext::renderSubtreeToImageBuffer(clipperData->clipMaskImage.get(), isUseElement ? childNode->renderer() : renderer, maskContentTransformation);
+ if (isUseElement)
+ renderer = childNode->renderer();
+
+ SVGRenderingContext::renderSubtree(context, renderer, maskContentTransformation);
}
frame()->view()->setPaintBehavior(oldBehavior);
- return true;
}
void RenderSVGResourceClipper::calculateClipContentRepaintRect()
« no previous file with comments | « Source/core/rendering/svg/RenderSVGResourceClipper.h ('k') | Source/core/rendering/svg/RenderSVGResourceMasker.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698