Chromium Code Reviews| 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 10 matching lines...) Expand all Loading... | |
| 21 */ | 21 */ |
| 22 | 22 |
| 23 #include "config.h" | 23 #include "config.h" |
| 24 | 24 |
| 25 #include "core/rendering/svg/RenderSVGResourceClipper.h" | 25 #include "core/rendering/svg/RenderSVGResourceClipper.h" |
| 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/platform/graphics/skia/SkiaUtils.h" | |
|
Stephen White
2013/08/28 14:11:10
We're calling into skia-specific code from outside
f(malita)
2013/08/28 19:54:38
Fixed, see other comment.
| |
| 31 #include "core/rendering/HitTestResult.h" | 32 #include "core/rendering/HitTestResult.h" |
| 32 #include "core/rendering/svg/SVGRenderingContext.h" | 33 #include "core/rendering/svg/SVGRenderingContext.h" |
| 33 #include "core/rendering/svg/SVGResources.h" | 34 #include "core/rendering/svg/SVGResources.h" |
| 34 #include "core/rendering/svg/SVGResourcesCache.h" | 35 #include "core/rendering/svg/SVGResourcesCache.h" |
| 35 #include "core/svg/SVGUseElement.h" | 36 #include "core/svg/SVGUseElement.h" |
| 37 #include "wtf/TemporaryChange.h" | |
| 36 | 38 |
| 37 namespace WebCore { | 39 namespace WebCore { |
| 38 | 40 |
| 39 RenderSVGResourceType RenderSVGResourceClipper::s_resourceType = ClipperResource Type; | 41 RenderSVGResourceType RenderSVGResourceClipper::s_resourceType = ClipperResource Type; |
| 40 | 42 |
| 41 RenderSVGResourceClipper::RenderSVGResourceClipper(SVGClipPathElement* node) | 43 RenderSVGResourceClipper::RenderSVGResourceClipper(SVGClipPathElement* node) |
| 42 : RenderSVGResourceContainer(node) | 44 : RenderSVGResourceContainer(node) |
| 45 , m_inClipExpansion(false) | |
| 43 { | 46 { |
| 44 } | 47 } |
| 45 | 48 |
| 46 RenderSVGResourceClipper::~RenderSVGResourceClipper() | 49 RenderSVGResourceClipper::~RenderSVGResourceClipper() |
| 47 { | 50 { |
| 48 if (m_clipper.isEmpty()) | |
| 49 return; | |
| 50 | |
| 51 deleteAllValues(m_clipper); | |
| 52 m_clipper.clear(); | |
| 53 } | 51 } |
| 54 | 52 |
| 55 void RenderSVGResourceClipper::removeAllClientsFromCache(bool markForInvalidatio n) | 53 void RenderSVGResourceClipper::removeAllClientsFromCache(bool markForInvalidatio n) |
| 56 { | 54 { |
| 57 m_clipBoundaries = FloatRect(); | 55 m_clipBoundaries = FloatRect(); |
| 58 if (!m_clipper.isEmpty()) { | 56 m_clipperMap.clear(); |
| 59 deleteAllValues(m_clipper); | |
| 60 m_clipper.clear(); | |
| 61 } | |
| 62 | |
| 63 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInval idation : ParentOnlyInvalidation); | 57 markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInval idation : ParentOnlyInvalidation); |
| 64 } | 58 } |
| 65 | 59 |
| 66 void RenderSVGResourceClipper::removeClientFromCache(RenderObject* client, bool markForInvalidation) | 60 void RenderSVGResourceClipper::removeClientFromCache(RenderObject* client, bool markForInvalidation) |
| 67 { | 61 { |
| 68 ASSERT(client); | 62 ASSERT(client); |
| 69 if (m_clipper.contains(client)) | 63 m_clipperMap.remove(client); |
| 70 delete m_clipper.take(client); | |
| 71 | |
| 72 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidati on : ParentOnlyInvalidation); | 64 markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidati on : ParentOnlyInvalidation); |
| 73 } | 65 } |
| 74 | 66 |
| 75 bool RenderSVGResourceClipper::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode) | 67 bool RenderSVGResourceClipper::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode) |
| 76 { | 68 { |
| 77 ASSERT(object); | 69 ASSERT(object); |
| 78 ASSERT(context); | 70 ASSERT(context); |
| 79 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode); | 71 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode); |
|
pdr.
2013/08/28 03:32:36
Can you add ASSERT_WITH_SECURITY_IMPLICATION(!need
| |
| 80 | 72 |
| 81 return applyClippingToContext(object, object->objectBoundingBox(), object->r epaintRectInLocalCoordinates(), context); | 73 return applyClippingToContext(object, object->objectBoundingBox(), object->r epaintRectInLocalCoordinates(), context); |
| 82 } | 74 } |
| 83 | 75 |
| 84 bool RenderSVGResourceClipper::pathOnlyClipping(GraphicsContext* context, const AffineTransform& animatedLocalTransform, const FloatRect& objectBoundingBox) | 76 bool RenderSVGResourceClipper::pathOnlyClipping(GraphicsContext* context, const AffineTransform& animatedLocalTransform, const FloatRect& objectBoundingBox) |
| 85 { | 77 { |
| 86 // If the current clip-path gets clipped itself, we have to fallback to mask ing. | 78 // If the current clip-path gets clipped itself, we have to fallback to mask ing. |
| 87 if (!style()->svgStyle()->clipperResource().isEmpty()) | 79 if (!style()->svgStyle()->clipperResource().isEmpty()) |
| 88 return false; | 80 return false; |
| 89 WindRule clipRule = RULE_NONZERO; | 81 WindRule clipRule = RULE_NONZERO; |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 137 // Transform path by animatedLocalTransform. | 129 // Transform path by animatedLocalTransform. |
| 138 clipPath.transform(animatedLocalTransform); | 130 clipPath.transform(animatedLocalTransform); |
| 139 | 131 |
| 140 // The SVG specification wants us to clip everything, if clip-path doesn't h ave a child. | 132 // The SVG specification wants us to clip everything, if clip-path doesn't h ave a child. |
| 141 if (clipPath.isEmpty()) | 133 if (clipPath.isEmpty()) |
| 142 clipPath.addRect(FloatRect()); | 134 clipPath.addRect(FloatRect()); |
| 143 context->clipPath(clipPath, clipRule); | 135 context->clipPath(clipPath, clipRule); |
| 144 return true; | 136 return true; |
| 145 } | 137 } |
| 146 | 138 |
| 147 bool RenderSVGResourceClipper::applyClippingToContext(RenderObject* object, cons t FloatRect& objectBoundingBox, | 139 bool RenderSVGResourceClipper::applyClippingToContext(RenderObject* target, cons t FloatRect& targetBoundingBox, |
| 148 const FloatRect& repaintRe ct, GraphicsContext* context) | 140 const FloatRect& repaintRe ct, GraphicsContext* context) |
| 149 { | 141 { |
| 150 bool missingClipperData = !m_clipper.contains(object); | 142 ASSERT(target); |
| 151 if (missingClipperData) | 143 ASSERT(context); |
| 152 m_clipper.set(object, new ClipperData); | 144 ASSERT_WITH_SECURITY_IMPLICATION(!needsLayout()); |
| 153 | 145 |
| 154 bool shouldCreateClipData = false; | 146 if (repaintRect.isEmpty() || m_inClipExpansion) |
| 147 return false; | |
| 148 TemporaryChange<bool> inClipExpansionChange(m_inClipExpansion, true); | |
| 149 | |
| 150 // add(obj, 0) idiom -> lookup & add if not found. | |
|
pdr.
2013/08/28 03:32:36
(not for this patch)
We should add HashMap::lookup
| |
| 151 OwnPtr<ClipperData>& clipperData = m_clipperMap.add(target, nullptr).iterato r->value; | |
| 152 if (!clipperData) | |
| 153 clipperData = adoptPtr(new ClipperData); | |
| 154 | |
| 155 // First, try to apply the clip as a clipPath. | |
| 155 AffineTransform animatedLocalTransform = toSVGClipPathElement(node())->anima tedLocalTransform(); | 156 AffineTransform animatedLocalTransform = toSVGClipPathElement(node())->anima tedLocalTransform(); |
| 156 ClipperData* clipperData = m_clipper.get(object); | 157 if (pathOnlyClipping(context, animatedLocalTransform, targetBoundingBox)) { |
|
pdr.
2013/08/28 03:32:36
Nit: could you rename this tryPathOnlyClipping?
f(malita)
2013/08/28 19:54:38
Done.
| |
| 157 if (!clipperData->clipMaskImage) { | 158 clipperData->pathOnly = true; |
| 158 if (pathOnlyClipping(context, animatedLocalTransform, objectBoundingBox) ) | 159 return true; |
| 159 return true; | |
| 160 shouldCreateClipData = true; | |
| 161 } | 160 } |
| 162 | 161 |
| 163 AffineTransform absoluteTransform; | 162 // Fall back to masking. |
| 164 SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(obje ct, absoluteTransform); | 163 clipperData->pathOnly = false; |
| 164 SkRect layerRect = WebCoreFloatRectToSKRect(repaintRect); | |
| 165 | 165 |
| 166 if (shouldCreateClipData && !repaintRect.isEmpty()) { | 166 // Mask layer start |
| 167 if (!SVGRenderingContext::createImageBuffer(repaintRect, absoluteTransfo rm, clipperData->clipMaskImage, Unaccelerated)) | 167 context->saveLayer(&layerRect, 0); |
| 168 return false; | 168 { |
| 169 | 169 GraphicsContextStateSaver maskContentSaver(*context); |
| 170 GraphicsContext* maskContext = clipperData->clipMaskImage->context(); | 170 context->concatCTM(animatedLocalTransform); |
| 171 ASSERT(maskContext); | |
| 172 | |
| 173 maskContext->concatCTM(animatedLocalTransform); | |
| 174 | 171 |
| 175 // clipPath can also be clipped by another clipPath. | 172 // clipPath can also be clipped by another clipPath. |
| 176 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObj ect(this); | 173 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObj ect(this); |
| 177 RenderSVGResourceClipper* clipper; | 174 RenderSVGResourceClipper* selfClipper = 0; |
|
pdr.
2013/08/28 03:32:36
Nit: clipPathClipper? or clipOnClip?
I was confuse
f(malita)
2013/08/28 19:54:38
Done.
| |
| 178 bool succeeded; | 175 if (resources && (selfClipper = resources->clipper())) { |
| 179 if (resources && (clipper = resources->clipper())) { | 176 if (!selfClipper->applyClippingToContext(this, targetBoundingBox, re paintRect, context)) { |
| 180 GraphicsContextStateSaver stateSaver(*maskContext); | 177 // FIXME: Awkward state micro-management. Ideally, GraphicsConte xtStateSaver should |
| 178 // a) pop saveLayers also | |
| 179 // b) pop multiple states if needed (similarly to SkCanvas::re storeToCount()) | |
| 180 // Then we should be able to replace this mess with a single, to p-level GCSS. | |
| 181 maskContentSaver.restore(); | |
| 182 context->restoreLayer(); | |
| 183 return false; | |
| 184 } | |
| 185 } | |
| 181 | 186 |
| 182 if (!clipper->applyClippingToContext(this, objectBoundingBox, repain tRect, maskContext)) | 187 drawMaskContent(context, targetBoundingBox); |
| 183 return false; | |
| 184 | 188 |
| 185 succeeded = drawContentIntoMaskImage(clipperData, objectBoundingBox) ; | 189 if (selfClipper) |
| 186 // The context restore applies the clipping on non-CG platforms. | 190 selfClipper->postApplyResource(this, context, ApplyToDefaultMode, 0, 0); |
| 187 } else | |
| 188 succeeded = drawContentIntoMaskImage(clipperData, objectBoundingBox) ; | |
| 189 | |
| 190 if (!succeeded) | |
| 191 clipperData->clipMaskImage.clear(); | |
| 192 } | 191 } |
| 193 | 192 |
| 194 if (!clipperData->clipMaskImage) | 193 // Start the content layer in SrcIn mode. This ensures that it gets masked a gainst the current |
| 195 return false; | 194 // (mask content) layer on restore. |
| 195 SkPaint contentPaint; | |
| 196 contentPaint.setXfermodeMode(SkXfermode::kSrcIn_Mode); | |
| 197 context->saveLayer(&layerRect, &contentPaint); | |
| 196 | 198 |
| 197 SVGRenderingContext::clipToImageBuffer(context, absoluteTransform, repaintRe ct, clipperData->clipMaskImage, missingClipperData); | |
| 198 return true; | 199 return true; |
| 199 } | 200 } |
| 200 | 201 |
| 201 bool RenderSVGResourceClipper::drawContentIntoMaskImage(ClipperData* clipperData , const FloatRect& objectBoundingBox) | 202 void RenderSVGResourceClipper::postApplyResource(RenderObject* object, GraphicsC ontext*& context, |
| 203 unsigned short resourceMode, const Path*, const RenderSVGShape*) { | |
| 204 ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode); | |
| 205 | |
| 206 ClipperData* clipperData = m_clipperMap.get(object); | |
| 207 ASSERT(clipperData); | |
| 208 | |
| 209 // Path-only clipping, no layers to restore. | |
| 210 if (clipperData->pathOnly) | |
| 211 return; | |
| 212 | |
| 213 // Transfer content layer -> mask layer (SrcIn) | |
| 214 context->restoreLayer(); | |
| 215 // Transfer mask layer -> bg layer (SrcOver) | |
| 216 context->restoreLayer(); | |
| 217 } | |
| 218 | |
| 219 void RenderSVGResourceClipper::drawMaskContent(GraphicsContext* context, const F loatRect& targetBoundingBox) | |
| 202 { | 220 { |
| 203 ASSERT(frame()); | 221 ASSERT(frame()); |
| 204 ASSERT(clipperData); | 222 ASSERT(context); |
| 205 ASSERT(clipperData->clipMaskImage); | |
| 206 | 223 |
| 207 GraphicsContext* maskContext = clipperData->clipMaskImage->context(); | 224 // Adjust the mask image context according to the target objectBoundingBox. |
| 208 ASSERT(maskContext); | |
| 209 | |
| 210 AffineTransform maskContentTransformation; | 225 AffineTransform maskContentTransformation; |
| 211 if (toSVGClipPathElement(node())->clipPathUnitsCurrentValue() == SVGUnitType s::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { | 226 if (toSVGClipPathElement(node())->clipPathUnitsCurrentValue() == SVGUnitType s::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { |
| 212 maskContentTransformation.translate(objectBoundingBox.x(), objectBoundin gBox.y()); | 227 maskContentTransformation = makeMapBetweenRects(FloatRect(0, 0, 1, 1), t argetBoundingBox); |
|
pdr.
2013/08/28 03:32:36
Can you switch this back to the previous method? m
f(malita)
2013/08/28 19:54:38
Done.
| |
| 213 maskContentTransformation.scaleNonUniform(objectBoundingBox.width(), obj ectBoundingBox.height()); | 228 context->concatCTM(maskContentTransformation); |
| 214 maskContext->concatCTM(maskContentTransformation); | |
| 215 } | 229 } |
| 216 | 230 |
| 217 // Switch to a paint behavior where all children of this <clipPath> will be rendered using special constraints: | 231 // 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 | 232 // - fill-opacity/stroke-opacity/opacity set to 1 |
| 219 // - masker/filter not applied when rendering the children | 233 // - masker/filter not applied when rendering the children |
| 220 // - fill is set to the initial fill paint server (solid, black) | 234 // - fill is set to the initial fill paint server (solid, black) |
| 221 // - stroke is set to the initial stroke paint server (none) | 235 // - stroke is set to the initial stroke paint server (none) |
| 222 PaintBehavior oldBehavior = frame()->view()->paintBehavior(); | 236 PaintBehavior oldBehavior = frame()->view()->paintBehavior(); |
| 223 frame()->view()->setPaintBehavior(oldBehavior | PaintBehaviorRenderingSVGMas k); | 237 frame()->view()->setPaintBehavior(oldBehavior | PaintBehaviorRenderingSVGMas k); |
| 224 | 238 |
| 225 // Draw all clipPath children into a global mask. | |
| 226 for (Node* childNode = node()->firstChild(); childNode; childNode = childNod e->nextSibling()) { | 239 for (Node* childNode = node()->firstChild(); childNode; childNode = childNod e->nextSibling()) { |
| 227 RenderObject* renderer = childNode->renderer(); | 240 RenderObject* renderer = childNode->renderer(); |
| 228 if (!childNode->isSVGElement() || !renderer) | 241 if (!childNode->isSVGElement() || !renderer) |
| 229 continue; | 242 continue; |
| 230 if (renderer->needsLayout()) { | 243 |
| 231 frame()->view()->setPaintBehavior(oldBehavior); | |
| 232 return false; | |
| 233 } | |
| 234 RenderStyle* style = renderer->style(); | 244 RenderStyle* style = renderer->style(); |
| 235 if (!style || style->display() == NONE || style->visibility() != VISIBLE ) | 245 if (!style || style->display() == NONE || style->visibility() != VISIBLE ) |
| 236 continue; | 246 continue; |
| 237 | 247 |
| 238 WindRule newClipRule = style->svgStyle()->clipRule(); | 248 WindRule newClipRule = style->svgStyle()->clipRule(); |
| 239 bool isUseElement = childNode->hasTagName(SVGNames::useTag); | 249 bool isUseElement = childNode->hasTagName(SVGNames::useTag); |
| 240 if (isUseElement) { | 250 if (isUseElement) { |
| 241 SVGUseElement* useElement = toSVGUseElement(childNode); | 251 SVGUseElement* useElement = toSVGUseElement(childNode); |
| 242 renderer = useElement->rendererClipChild(); | 252 renderer = useElement->rendererClipChild(); |
| 243 if (!renderer) | 253 if (!renderer) |
| 244 continue; | 254 continue; |
| 245 if (!useElement->hasAttribute(SVGNames::clip_ruleAttr)) | 255 if (!useElement->hasAttribute(SVGNames::clip_ruleAttr)) |
| 246 newClipRule = renderer->style()->svgStyle()->clipRule(); | 256 newClipRule = renderer->style()->svgStyle()->clipRule(); |
| 247 } | 257 } |
| 248 | 258 |
| 249 // Only shapes, paths and texts are allowed for clipping. | 259 // Only shapes, paths and texts are allowed for clipping. |
| 250 if (!renderer->isSVGShape() && !renderer->isSVGText()) | 260 if (!renderer->isSVGShape() && !renderer->isSVGText()) |
| 251 continue; | 261 continue; |
| 252 | 262 |
| 253 maskContext->setFillRule(newClipRule); | 263 context->setFillRule(newClipRule); |
| 254 | 264 |
| 255 // In the case of a <use> element, we obtained its renderere above, to r etrieve its clipRule. | 265 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. | 266 renderer = childNode->renderer(); |
| 257 // So if isUseElement is true, refetch the childNode->renderer(), as ren derer got overriden above. | 267 |
| 258 SVGRenderingContext::renderSubtreeToImageBuffer(clipperData->clipMaskIma ge.get(), isUseElement ? childNode->renderer() : renderer, maskContentTransforma tion); | 268 SVGRenderingContext::renderSubtree(context, renderer, maskContentTransfo rmation); |
| 259 } | 269 } |
| 260 | 270 |
| 261 frame()->view()->setPaintBehavior(oldBehavior); | 271 frame()->view()->setPaintBehavior(oldBehavior); |
| 262 return true; | |
| 263 } | 272 } |
| 264 | 273 |
| 265 void RenderSVGResourceClipper::calculateClipContentRepaintRect() | 274 void RenderSVGResourceClipper::calculateClipContentRepaintRect() |
| 266 { | 275 { |
| 267 // This is a rough heuristic to appraise the clip size and doesn't consider clip on clip. | 276 // 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()) { | 277 for (Node* childNode = node()->firstChild(); childNode; childNode = childNod e->nextSibling()) { |
| 269 RenderObject* renderer = childNode->renderer(); | 278 RenderObject* renderer = childNode->renderer(); |
| 270 if (!childNode->isSVGElement() || !renderer) | 279 if (!childNode->isSVGElement() || !renderer) |
| 271 continue; | 280 continue; |
| 272 if (!renderer->isSVGShape() && !renderer->isSVGText() && !childNode->has TagName(SVGNames::useTag)) | 281 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; | 333 AffineTransform transform; |
| 325 transform.translate(objectBoundingBox.x(), objectBoundingBox.y()); | 334 transform.translate(objectBoundingBox.x(), objectBoundingBox.y()); |
| 326 transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.h eight()); | 335 transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.h eight()); |
| 327 return transform.mapRect(m_clipBoundaries); | 336 return transform.mapRect(m_clipBoundaries); |
| 328 } | 337 } |
| 329 | 338 |
| 330 return m_clipBoundaries; | 339 return m_clipBoundaries; |
| 331 } | 340 } |
| 332 | 341 |
| 333 } | 342 } |
| OLD | NEW |