| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2004, 2005, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org> | 2 * Copyright (C) 2004, 2005, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org> |
| 3 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Rob Buis <buis@kde.org> | 3 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Rob Buis <buis@kde.org> |
| 4 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. | 4 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. |
| 5 * Copyright (C) 2011 Dirk Schulze <krit@webkit.org> | 5 * Copyright (C) 2011 Dirk Schulze <krit@webkit.org> |
| 6 * | 6 * |
| 7 * This library is free software; you can redistribute it and/or | 7 * This library is free software; you can redistribute it and/or |
| 8 * modify it under the terms of the GNU Library General Public | 8 * modify it under the terms of the GNU Library General Public |
| 9 * License as published by the Free Software Foundation; either | 9 * License as published by the Free Software Foundation; either |
| 10 * version 2 of the License, or (at your option) any later version. | 10 * version 2 of the License, or (at your option) any later version. |
| (...skipping 15 matching lines...) Expand all Loading... |
| 26 | 26 |
| 27 #include "RuntimeEnabledFeatures.h" | 27 #include "RuntimeEnabledFeatures.h" |
| 28 #include "SVGNames.h" | 28 #include "SVGNames.h" |
| 29 #include "core/page/FrameView.h" | 29 #include "core/page/FrameView.h" |
| 30 #include "core/platform/graphics/GraphicsContextStateSaver.h" | 30 #include "core/platform/graphics/GraphicsContextStateSaver.h" |
| 31 #include "core/rendering/HitTestResult.h" | 31 #include "core/rendering/HitTestResult.h" |
| 32 #include "core/rendering/svg/SVGRenderingContext.h" | 32 #include "core/rendering/svg/SVGRenderingContext.h" |
| 33 #include "core/rendering/svg/SVGResources.h" | 33 #include "core/rendering/svg/SVGResources.h" |
| 34 #include "core/rendering/svg/SVGResourcesCache.h" | 34 #include "core/rendering/svg/SVGResourcesCache.h" |
| 35 #include "core/svg/SVGUseElement.h" | 35 #include "core/svg/SVGUseElement.h" |
| 36 #include "wtf/TemporaryChange.h" |
| 36 | 37 |
| 37 namespace WebCore { | 38 namespace WebCore { |
| 38 | 39 |
| 39 RenderSVGResourceType RenderSVGResourceClipper::s_resourceType = ClipperResource
Type; | 40 RenderSVGResourceType RenderSVGResourceClipper::s_resourceType = ClipperResource
Type; |
| 40 | 41 |
| 41 RenderSVGResourceClipper::RenderSVGResourceClipper(SVGClipPathElement* node) | 42 RenderSVGResourceClipper::RenderSVGResourceClipper(SVGClipPathElement* node) |
| 42 : RenderSVGResourceContainer(node) | 43 : RenderSVGResourceContainer(node) |
| 44 , m_inClipExpansion(false) |
| 43 { | 45 { |
| 44 } | 46 } |
| 45 | 47 |
| 46 RenderSVGResourceClipper::~RenderSVGResourceClipper() | 48 RenderSVGResourceClipper::~RenderSVGResourceClipper() |
| 47 { | 49 { |
| 48 if (m_clipper.isEmpty()) | |
| 49 return; | |
| 50 | |
| 51 deleteAllValues(m_clipper); | |
| 52 m_clipper.clear(); | |
| 53 } | 50 } |
| 54 | 51 |
| 55 void RenderSVGResourceClipper::removeAllClientsFromCache(bool markForInvalidatio
n) | 52 void RenderSVGResourceClipper::removeAllClientsFromCache(bool markForInvalidatio
n) |
| 56 { | 53 { |
| 57 m_clipBoundaries = FloatRect(); | 54 m_clipBoundaries = FloatRect(); |
| 58 if (!m_clipper.isEmpty()) { | 55 m_rendererToClipperMap.clear(); |
| 59 deleteAllValues(m_clipper); | |
| 60 m_clipper.clear(); | |
| 61 } | |
| 62 | |
| 63 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInval
idation : ParentOnlyInvalidation); | 56 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInval
idation : ParentOnlyInvalidation); |
| 64 } | 57 } |
| 65 | 58 |
| 66 void RenderSVGResourceClipper::removeClientFromCache(RenderObject* client, bool
markForInvalidation) | 59 void RenderSVGResourceClipper::removeClientFromCache(RenderObject* client, bool
markForInvalidation) |
| 67 { | 60 { |
| 68 ASSERT(client); | 61 ASSERT(client); |
| 69 if (m_clipper.contains(client)) | 62 m_rendererToClipperMap.remove(client); |
| 70 delete m_clipper.take(client); | |
| 71 | |
| 72 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidati
on : ParentOnlyInvalidation); | 63 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidati
on : ParentOnlyInvalidation); |
| 73 } | 64 } |
| 74 | 65 |
| 75 bool RenderSVGResourceClipper::applyResource(RenderObject* object, RenderStyle*,
GraphicsContext*& context, unsigned short resourceMode) | 66 bool RenderSVGResourceClipper::applyResource(RenderObject* object, RenderStyle*,
GraphicsContext*& context, unsigned short resourceMode) |
| 76 { | 67 { |
| 77 ASSERT(object); | 68 ASSERT(object); |
| 78 ASSERT(context); | 69 ASSERT(context); |
| 79 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode); | 70 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode); |
| 80 | 71 |
| 81 return applyClippingToContext(object, object->objectBoundingBox(), object->r
epaintRectInLocalCoordinates(), context); | 72 return applyClippingToContext(object, object->objectBoundingBox(), object->r
epaintRectInLocalCoordinates(), context); |
| 82 } | 73 } |
| 83 | 74 |
| 84 bool RenderSVGResourceClipper::pathOnlyClipping(GraphicsContext* context, const
AffineTransform& animatedLocalTransform, const FloatRect& objectBoundingBox) | 75 bool RenderSVGResourceClipper::tryPathOnlyClipping(GraphicsContext* context, |
| 85 { | 76 const AffineTransform& animatedLocalTransform, const FloatRect& objectBoundi
ngBox) { |
| 86 // If the current clip-path gets clipped itself, we have to fallback to mask
ing. | 77 // If the current clip-path gets clipped itself, we have to fallback to mask
ing. |
| 87 if (!style()->svgStyle()->clipperResource().isEmpty()) | 78 if (!style()->svgStyle()->clipperResource().isEmpty()) |
| 88 return false; | 79 return false; |
| 89 WindRule clipRule = RULE_NONZERO; | 80 WindRule clipRule = RULE_NONZERO; |
| 90 Path clipPath = Path(); | 81 Path clipPath = Path(); |
| 91 | 82 |
| 92 for (Node* childNode = node()->firstChild(); childNode; childNode = childNod
e->nextSibling()) { | 83 for (Node* childNode = node()->firstChild(); childNode; childNode = childNod
e->nextSibling()) { |
| 93 RenderObject* renderer = childNode->renderer(); | 84 RenderObject* renderer = childNode->renderer(); |
| 94 if (!renderer) | 85 if (!renderer) |
| 95 continue; | 86 continue; |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 137 // Transform path by animatedLocalTransform. | 128 // Transform path by animatedLocalTransform. |
| 138 clipPath.transform(animatedLocalTransform); | 129 clipPath.transform(animatedLocalTransform); |
| 139 | 130 |
| 140 // The SVG specification wants us to clip everything, if clip-path doesn't h
ave a child. | 131 // The SVG specification wants us to clip everything, if clip-path doesn't h
ave a child. |
| 141 if (clipPath.isEmpty()) | 132 if (clipPath.isEmpty()) |
| 142 clipPath.addRect(FloatRect()); | 133 clipPath.addRect(FloatRect()); |
| 143 context->clipPath(clipPath, clipRule); | 134 context->clipPath(clipPath, clipRule); |
| 144 return true; | 135 return true; |
| 145 } | 136 } |
| 146 | 137 |
| 147 bool RenderSVGResourceClipper::applyClippingToContext(RenderObject* object, cons
t FloatRect& objectBoundingBox, | 138 bool RenderSVGResourceClipper::applyClippingToContext(RenderObject* target, cons
t FloatRect& targetBoundingBox, |
| 148 const FloatRect& repaintRe
ct, GraphicsContext* context) | 139 const FloatRect& repaintRe
ct, GraphicsContext* context) |
| 149 { | 140 { |
| 150 bool missingClipperData = !m_clipper.contains(object); | 141 ASSERT(target); |
| 151 if (missingClipperData) | 142 ASSERT(context); |
| 152 m_clipper.set(object, new ClipperData); | 143 ASSERT_WITH_SECURITY_IMPLICATION(!needsLayout()); |
| 153 | 144 |
| 154 bool shouldCreateClipData = false; | 145 if (repaintRect.isEmpty() || m_inClipExpansion) |
| 146 return false; |
| 147 TemporaryChange<bool> inClipExpansionChange(m_inClipExpansion, true); |
| 148 |
| 149 // add(obj, 0) idiom -> lookup & add if not found. |
| 150 OwnPtr<ClipperData>& clipperData = m_rendererToClipperMap.add(target, nullpt
r).iterator->value; |
| 151 if (!clipperData) |
| 152 clipperData = adoptPtr(new ClipperData); |
| 153 |
| 154 // First, try to apply the clip as a clipPath. |
| 155 AffineTransform animatedLocalTransform = toSVGClipPathElement(node())->anima
tedLocalTransform(); | 155 AffineTransform animatedLocalTransform = toSVGClipPathElement(node())->anima
tedLocalTransform(); |
| 156 ClipperData* clipperData = m_clipper.get(object); | 156 if (tryPathOnlyClipping(context, animatedLocalTransform, targetBoundingBox))
{ |
| 157 if (!clipperData->clipMaskImage) { | 157 clipperData->clipMode = ClipperData::PathOnlyClipMode; |
| 158 if (pathOnlyClipping(context, animatedLocalTransform, objectBoundingBox)
) | 158 return true; |
| 159 return true; | |
| 160 shouldCreateClipData = true; | |
| 161 } | 159 } |
| 162 | 160 |
| 163 AffineTransform absoluteTransform; | 161 // Fall back to masking. |
| 164 SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(obje
ct, absoluteTransform); | 162 clipperData->clipMode = ClipperData::MaskClipMode; |
| 165 | 163 |
| 166 if (shouldCreateClipData && !repaintRect.isEmpty()) { | 164 // Mask layer start |
| 167 if (!SVGRenderingContext::createImageBuffer(repaintRect, absoluteTransfo
rm, clipperData->clipMaskImage, Unaccelerated)) | 165 context->beginTransparencyLayer(1, &repaintRect); |
| 168 return false; | 166 { |
| 169 | 167 GraphicsContextStateSaver maskContentSaver(*context); |
| 170 GraphicsContext* maskContext = clipperData->clipMaskImage->context(); | 168 context->concatCTM(animatedLocalTransform); |
| 171 ASSERT(maskContext); | |
| 172 | |
| 173 maskContext->concatCTM(animatedLocalTransform); | |
| 174 | 169 |
| 175 // clipPath can also be clipped by another clipPath. | 170 // clipPath can also be clipped by another clipPath. |
| 176 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObj
ect(this); | 171 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObj
ect(this); |
| 177 RenderSVGResourceClipper* clipper; | 172 RenderSVGResourceClipper* clipPathClipper = 0; |
| 178 bool succeeded; | 173 if (resources && (clipPathClipper = resources->clipper())) { |
| 179 if (resources && (clipper = resources->clipper())) { | 174 if (!clipPathClipper->applyClippingToContext(this, targetBoundingBox
, repaintRect, context)) { |
| 180 GraphicsContextStateSaver stateSaver(*maskContext); | 175 // FIXME: Awkward state micro-management. Ideally, GraphicsConte
xtStateSaver should |
| 176 // a) pop saveLayers also |
| 177 // b) pop multiple states if needed (similarly to SkCanvas::re
storeToCount()) |
| 178 // Then we should be able to replace this mess with a single, to
p-level GCSS. |
| 179 maskContentSaver.restore(); |
| 180 context->restoreLayer(); |
| 181 return false; |
| 182 } |
| 183 } |
| 181 | 184 |
| 182 if (!clipper->applyClippingToContext(this, objectBoundingBox, repain
tRect, maskContext)) | 185 drawMaskContent(context, targetBoundingBox); |
| 183 return false; | |
| 184 | 186 |
| 185 succeeded = drawContentIntoMaskImage(clipperData, objectBoundingBox)
; | 187 if (clipPathClipper) |
| 186 // The context restore applies the clipping on non-CG platforms. | 188 clipPathClipper->postApplyResource(this, context, ApplyToDefaultMode
, 0, 0); |
| 187 } else | |
| 188 succeeded = drawContentIntoMaskImage(clipperData, objectBoundingBox)
; | |
| 189 | |
| 190 if (!succeeded) | |
| 191 clipperData->clipMaskImage.clear(); | |
| 192 } | 189 } |
| 193 | 190 |
| 194 if (!clipperData->clipMaskImage) | 191 // Masked content layer start. |
| 195 return false; | 192 context->beginMaskedLayer(repaintRect); |
| 196 | 193 |
| 197 SVGRenderingContext::clipToImageBuffer(context, absoluteTransform, repaintRe
ct, clipperData->clipMaskImage, missingClipperData); | |
| 198 return true; | 194 return true; |
| 199 } | 195 } |
| 200 | 196 |
| 201 bool RenderSVGResourceClipper::drawContentIntoMaskImage(ClipperData* clipperData
, const FloatRect& objectBoundingBox) | 197 void RenderSVGResourceClipper::postApplyResource(RenderObject* object, GraphicsC
ontext*& context, |
| 198 unsigned short resourceMode, const Path*, const RenderSVGShape*) { |
| 199 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode); |
| 200 |
| 201 ClipperData* clipperData = m_rendererToClipperMap.get(object); |
| 202 ASSERT(clipperData); |
| 203 |
| 204 // Path-only clipping, no layers to restore. |
| 205 if (clipperData->clipMode == ClipperData::PathOnlyClipMode) |
| 206 return; |
| 207 |
| 208 // Transfer content layer -> mask layer (SrcIn) |
| 209 context->endLayer(); |
| 210 // Transfer mask layer -> bg layer (SrcOver) |
| 211 context->endLayer(); |
| 212 } |
| 213 |
| 214 void RenderSVGResourceClipper::drawMaskContent(GraphicsContext* context, const F
loatRect& targetBoundingBox) |
| 202 { | 215 { |
| 203 ASSERT(frame()); | 216 ASSERT(frame()); |
| 204 ASSERT(clipperData); | 217 ASSERT(context); |
| 205 ASSERT(clipperData->clipMaskImage); | |
| 206 | 218 |
| 207 GraphicsContext* maskContext = clipperData->clipMaskImage->context(); | 219 // Adjust the mask image context according to the target objectBoundingBox. |
| 208 ASSERT(maskContext); | |
| 209 | |
| 210 AffineTransform maskContentTransformation; | 220 AffineTransform maskContentTransformation; |
| 211 if (toSVGClipPathElement(node())->clipPathUnitsCurrentValue() == SVGUnitType
s::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { | 221 if (toSVGClipPathElement(node())->clipPathUnitsCurrentValue() == SVGUnitType
s::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { |
| 212 maskContentTransformation.translate(objectBoundingBox.x(), objectBoundin
gBox.y()); | 222 maskContentTransformation.translate(targetBoundingBox.x(), targetBoundin
gBox.y()); |
| 213 maskContentTransformation.scaleNonUniform(objectBoundingBox.width(), obj
ectBoundingBox.height()); | 223 maskContentTransformation.scaleNonUniform(targetBoundingBox.width(), tar
getBoundingBox.height()); |
| 214 maskContext->concatCTM(maskContentTransformation); | 224 context->concatCTM(maskContentTransformation); |
| 215 } | 225 } |
| 216 | 226 |
| 217 // Switch to a paint behavior where all children of this <clipPath> will be
rendered using special constraints: | 227 // Switch to a paint behavior where all children of this <clipPath> will be
rendered using special constraints: |
| 218 // - fill-opacity/stroke-opacity/opacity set to 1 | 228 // - fill-opacity/stroke-opacity/opacity set to 1 |
| 219 // - masker/filter not applied when rendering the children | 229 // - masker/filter not applied when rendering the children |
| 220 // - fill is set to the initial fill paint server (solid, black) | 230 // - fill is set to the initial fill paint server (solid, black) |
| 221 // - stroke is set to the initial stroke paint server (none) | 231 // - stroke is set to the initial stroke paint server (none) |
| 222 PaintBehavior oldBehavior = frame()->view()->paintBehavior(); | 232 PaintBehavior oldBehavior = frame()->view()->paintBehavior(); |
| 223 frame()->view()->setPaintBehavior(oldBehavior | PaintBehaviorRenderingSVGMas
k); | 233 frame()->view()->setPaintBehavior(oldBehavior | PaintBehaviorRenderingSVGMas
k); |
| 224 | 234 |
| 225 // Draw all clipPath children into a global mask. | |
| 226 for (Node* childNode = node()->firstChild(); childNode; childNode = childNod
e->nextSibling()) { | 235 for (Node* childNode = node()->firstChild(); childNode; childNode = childNod
e->nextSibling()) { |
| 227 RenderObject* renderer = childNode->renderer(); | 236 RenderObject* renderer = childNode->renderer(); |
| 228 if (!childNode->isSVGElement() || !renderer) | 237 if (!childNode->isSVGElement() || !renderer) |
| 229 continue; | 238 continue; |
| 230 if (renderer->needsLayout()) { | 239 |
| 231 frame()->view()->setPaintBehavior(oldBehavior); | |
| 232 return false; | |
| 233 } | |
| 234 RenderStyle* style = renderer->style(); | 240 RenderStyle* style = renderer->style(); |
| 235 if (!style || style->display() == NONE || style->visibility() != VISIBLE
) | 241 if (!style || style->display() == NONE || style->visibility() != VISIBLE
) |
| 236 continue; | 242 continue; |
| 237 | 243 |
| 238 WindRule newClipRule = style->svgStyle()->clipRule(); | 244 WindRule newClipRule = style->svgStyle()->clipRule(); |
| 239 bool isUseElement = childNode->hasTagName(SVGNames::useTag); | 245 bool isUseElement = childNode->hasTagName(SVGNames::useTag); |
| 240 if (isUseElement) { | 246 if (isUseElement) { |
| 241 SVGUseElement* useElement = toSVGUseElement(childNode); | 247 SVGUseElement* useElement = toSVGUseElement(childNode); |
| 242 renderer = useElement->rendererClipChild(); | 248 renderer = useElement->rendererClipChild(); |
| 243 if (!renderer) | 249 if (!renderer) |
| 244 continue; | 250 continue; |
| 245 if (!useElement->hasAttribute(SVGNames::clip_ruleAttr)) | 251 if (!useElement->hasAttribute(SVGNames::clip_ruleAttr)) |
| 246 newClipRule = renderer->style()->svgStyle()->clipRule(); | 252 newClipRule = renderer->style()->svgStyle()->clipRule(); |
| 247 } | 253 } |
| 248 | 254 |
| 249 // Only shapes, paths and texts are allowed for clipping. | 255 // Only shapes, paths and texts are allowed for clipping. |
| 250 if (!renderer->isSVGShape() && !renderer->isSVGText()) | 256 if (!renderer->isSVGShape() && !renderer->isSVGText()) |
| 251 continue; | 257 continue; |
| 252 | 258 |
| 253 maskContext->setFillRule(newClipRule); | 259 context->setFillRule(newClipRule); |
| 254 | 260 |
| 255 // In the case of a <use> element, we obtained its renderere above, to r
etrieve its clipRule. | 261 if (isUseElement) |
| 256 // We have to pass the <use> renderer itself to renderSubtreeToImageBuff
er() to apply it's x/y/transform/etc. values when rendering. | 262 renderer = childNode->renderer(); |
| 257 // So if isUseElement is true, refetch the childNode->renderer(), as ren
derer got overriden above. | 263 |
| 258 SVGRenderingContext::renderSubtreeToImageBuffer(clipperData->clipMaskIma
ge.get(), isUseElement ? childNode->renderer() : renderer, maskContentTransforma
tion); | 264 SVGRenderingContext::renderSubtree(context, renderer, maskContentTransfo
rmation); |
| 259 } | 265 } |
| 260 | 266 |
| 261 frame()->view()->setPaintBehavior(oldBehavior); | 267 frame()->view()->setPaintBehavior(oldBehavior); |
| 262 return true; | |
| 263 } | 268 } |
| 264 | 269 |
| 265 void RenderSVGResourceClipper::calculateClipContentRepaintRect() | 270 void RenderSVGResourceClipper::calculateClipContentRepaintRect() |
| 266 { | 271 { |
| 267 // This is a rough heuristic to appraise the clip size and doesn't consider
clip on clip. | 272 // This is a rough heuristic to appraise the clip size and doesn't consider
clip on clip. |
| 268 for (Node* childNode = node()->firstChild(); childNode; childNode = childNod
e->nextSibling()) { | 273 for (Node* childNode = node()->firstChild(); childNode; childNode = childNod
e->nextSibling()) { |
| 269 RenderObject* renderer = childNode->renderer(); | 274 RenderObject* renderer = childNode->renderer(); |
| 270 if (!childNode->isSVGElement() || !renderer) | 275 if (!childNode->isSVGElement() || !renderer) |
| 271 continue; | 276 continue; |
| 272 if (!renderer->isSVGShape() && !renderer->isSVGText() && !childNode->has
TagName(SVGNames::useTag)) | 277 if (!renderer->isSVGShape() && !renderer->isSVGText() && !childNode->has
TagName(SVGNames::useTag)) |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 324 AffineTransform transform; | 329 AffineTransform transform; |
| 325 transform.translate(objectBoundingBox.x(), objectBoundingBox.y()); | 330 transform.translate(objectBoundingBox.x(), objectBoundingBox.y()); |
| 326 transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.h
eight()); | 331 transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.h
eight()); |
| 327 return transform.mapRect(m_clipBoundaries); | 332 return transform.mapRect(m_clipBoundaries); |
| 328 } | 333 } |
| 329 | 334 |
| 330 return m_clipBoundaries; | 335 return m_clipBoundaries; |
| 331 } | 336 } |
| 332 | 337 |
| 333 } | 338 } |
| OLD | NEW |