/src/3rdparty/webkit/Source/WebCore/rendering/svg/RenderSVGResourceClipper.cpp

https://bitbucket.org/ultra_iter/qt-vtl · C++ · 346 lines · 250 code · 51 blank · 45 comment · 75 complexity · 8155c4c96d18d0c8e46331974be25334 MD5 · raw file

  1. /*
  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>
  4. * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Library General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2 of the License, or (at your option) any later version.
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Library General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Library General Public License
  17. * along with this library; see the file COPYING.LIB. If not, write to
  18. * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  19. * Boston, MA 02110-1301, USA.
  20. */
  21. #include "config.h"
  22. #if ENABLE(SVG)
  23. #include "RenderSVGResourceClipper.h"
  24. #include "AffineTransform.h"
  25. #include "FloatRect.h"
  26. #include "GraphicsContext.h"
  27. #include "HitTestRequest.h"
  28. #include "HitTestResult.h"
  29. #include "ImageBuffer.h"
  30. #include "IntRect.h"
  31. #include "RenderObject.h"
  32. #include "RenderSVGResource.h"
  33. #include "RenderStyle.h"
  34. #include "SVGClipPathElement.h"
  35. #include "SVGElement.h"
  36. #include "SVGImageBufferTools.h"
  37. #include "SVGNames.h"
  38. #include "SVGRenderSupport.h"
  39. #include "SVGResources.h"
  40. #include "SVGStyledElement.h"
  41. #include "SVGStyledTransformableElement.h"
  42. #include "SVGUnitTypes.h"
  43. #include "SVGUseElement.h"
  44. #include <wtf/UnusedParam.h>
  45. namespace WebCore {
  46. RenderSVGResourceType RenderSVGResourceClipper::s_resourceType = ClipperResourceType;
  47. RenderSVGResourceClipper::RenderSVGResourceClipper(SVGClipPathElement* node)
  48. : RenderSVGResourceContainer(node)
  49. , m_invalidationBlocked(false)
  50. {
  51. }
  52. RenderSVGResourceClipper::~RenderSVGResourceClipper()
  53. {
  54. if (m_clipper.isEmpty())
  55. return;
  56. deleteAllValues(m_clipper);
  57. m_clipper.clear();
  58. }
  59. void RenderSVGResourceClipper::removeAllClientsFromCache(bool markForInvalidation)
  60. {
  61. if (m_invalidationBlocked)
  62. return;
  63. m_clipBoundaries = FloatRect();
  64. if (!m_clipper.isEmpty()) {
  65. deleteAllValues(m_clipper);
  66. m_clipper.clear();
  67. }
  68. markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
  69. }
  70. void RenderSVGResourceClipper::removeClientFromCache(RenderObject* client, bool markForInvalidation)
  71. {
  72. ASSERT(client);
  73. if (m_invalidationBlocked)
  74. return;
  75. if (m_clipper.contains(client))
  76. delete m_clipper.take(client);
  77. markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
  78. }
  79. bool RenderSVGResourceClipper::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
  80. {
  81. ASSERT(object);
  82. ASSERT(context);
  83. #ifndef NDEBUG
  84. ASSERT(resourceMode == ApplyToDefaultMode);
  85. #else
  86. UNUSED_PARAM(resourceMode);
  87. #endif
  88. return applyClippingToContext(object, object->objectBoundingBox(), object->repaintRectInLocalCoordinates(), context);
  89. }
  90. bool RenderSVGResourceClipper::pathOnlyClipping(GraphicsContext* context, const FloatRect& objectBoundingBox)
  91. {
  92. // If the current clip-path gets clipped itself, we have to fallback to masking.
  93. if (!style()->svgStyle()->clipperResource().isEmpty())
  94. return false;
  95. WindRule clipRule = RULE_NONZERO;
  96. Path clipPath = Path();
  97. // If clip-path only contains one visible shape or path, we can use path-based clipping. Invisible
  98. // shapes don't affect the clipping and can be ignored. If clip-path contains more than one
  99. // visible shape, the additive clipping may not work, caused by the clipRule. EvenOdd
  100. // as well as NonZero can cause self-clipping of the elements.
  101. // See also http://www.w3.org/TR/SVG/painting.html#FillRuleProperty
  102. for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) {
  103. RenderObject* renderer = childNode->renderer();
  104. if (!renderer)
  105. continue;
  106. // Only shapes or paths are supported for direct clipping. We need to fallback to masking for texts.
  107. if (renderer->isSVGText())
  108. return false;
  109. if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyledTransformable())
  110. continue;
  111. SVGStyledTransformableElement* styled = static_cast<SVGStyledTransformableElement*>(childNode);
  112. RenderStyle* style = renderer->style();
  113. if (!style || style->display() == NONE || style->visibility() != VISIBLE)
  114. continue;
  115. const SVGRenderStyle* svgStyle = style->svgStyle();
  116. // Current shape in clip-path gets clipped too. Fallback to masking.
  117. if (!svgStyle->clipperResource().isEmpty())
  118. return false;
  119. // Fallback to masking, if there is more than one clipping path.
  120. if (clipPath.isEmpty()) {
  121. styled->toClipPath(clipPath);
  122. clipRule = svgStyle->clipRule();
  123. } else
  124. return false;
  125. }
  126. // Only one visible shape/path was found. Directly continue clipping and transform the content to userspace if necessary.
  127. if (static_cast<SVGClipPathElement*>(node())->clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
  128. AffineTransform transform;
  129. transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
  130. transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
  131. clipPath.transform(transform);
  132. }
  133. // The SVG specification wants us to clip everything, if clip-path doesn't have a child.
  134. if (clipPath.isEmpty())
  135. clipPath.addRect(FloatRect());
  136. context->clipPath(clipPath, clipRule);
  137. return true;
  138. }
  139. bool RenderSVGResourceClipper::applyClippingToContext(RenderObject* object, const FloatRect& objectBoundingBox,
  140. const FloatRect& repaintRect, GraphicsContext* context)
  141. {
  142. if (!m_clipper.contains(object))
  143. m_clipper.set(object, new ClipperData);
  144. bool shouldCreateClipData = false;
  145. ClipperData* clipperData = m_clipper.get(object);
  146. if (!clipperData->clipMaskImage) {
  147. if (pathOnlyClipping(context, objectBoundingBox))
  148. return true;
  149. shouldCreateClipData = true;
  150. }
  151. AffineTransform absoluteTransform;
  152. SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform);
  153. FloatRect absoluteTargetRect = absoluteTransform.mapRect(repaintRect);
  154. FloatRect clampedAbsoluteTargetRect = SVGImageBufferTools::clampedAbsoluteTargetRectForRenderer(object, absoluteTargetRect);
  155. if (shouldCreateClipData && !clampedAbsoluteTargetRect.isEmpty()) {
  156. if (!SVGImageBufferTools::createImageBuffer(absoluteTargetRect, clampedAbsoluteTargetRect, clipperData->clipMaskImage, ColorSpaceDeviceRGB))
  157. return false;
  158. GraphicsContext* maskContext = clipperData->clipMaskImage->context();
  159. ASSERT(maskContext);
  160. // The save/restore pair is needed for clipToImageBuffer - it doesn't work without it on non-Cg platforms.
  161. GraphicsContextStateSaver stateSaver(*maskContext);
  162. maskContext->translate(-clampedAbsoluteTargetRect.x(), -clampedAbsoluteTargetRect.y());
  163. maskContext->concatCTM(absoluteTransform);
  164. // clipPath can also be clipped by another clipPath.
  165. if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(this)) {
  166. if (RenderSVGResourceClipper* clipper = resources->clipper()) {
  167. if (!clipper->applyClippingToContext(this, objectBoundingBox, repaintRect, maskContext))
  168. return false;
  169. }
  170. }
  171. drawContentIntoMaskImage(clipperData, objectBoundingBox);
  172. }
  173. if (!clipperData->clipMaskImage)
  174. return false;
  175. SVGImageBufferTools::clipToImageBuffer(context, absoluteTransform, clampedAbsoluteTargetRect, clipperData->clipMaskImage);
  176. return true;
  177. }
  178. bool RenderSVGResourceClipper::drawContentIntoMaskImage(ClipperData* clipperData, const FloatRect& objectBoundingBox)
  179. {
  180. ASSERT(clipperData);
  181. ASSERT(clipperData->clipMaskImage);
  182. GraphicsContext* maskContext = clipperData->clipMaskImage->context();
  183. ASSERT(maskContext);
  184. AffineTransform maskContentTransformation;
  185. SVGClipPathElement* clipPath = static_cast<SVGClipPathElement*>(node());
  186. if (clipPath->clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
  187. maskContentTransformation.translate(objectBoundingBox.x(), objectBoundingBox.y());
  188. maskContentTransformation.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
  189. maskContext->concatCTM(maskContentTransformation);
  190. }
  191. // Draw all clipPath children into a global mask.
  192. for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) {
  193. RenderObject* renderer = childNode->renderer();
  194. if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyled() || !renderer)
  195. continue;
  196. RenderStyle* style = renderer->style();
  197. if (!style || style->display() == NONE || style->visibility() != VISIBLE)
  198. continue;
  199. WindRule newClipRule = style->svgStyle()->clipRule();
  200. bool isUseElement = renderer->isSVGShadowTreeRootContainer();
  201. if (isUseElement) {
  202. SVGUseElement* useElement = static_cast<SVGUseElement*>(childNode);
  203. renderer = useElement->rendererClipChild();
  204. if (!renderer)
  205. continue;
  206. if (!useElement->hasAttribute(SVGNames::clip_ruleAttr))
  207. newClipRule = renderer->style()->svgStyle()->clipRule();
  208. }
  209. // Only shapes, paths and texts are allowed for clipping.
  210. if (!renderer->isSVGPath() && !renderer->isSVGText())
  211. continue;
  212. // Save the old RenderStyle of the current object for restoring after drawing
  213. // it to the MaskImage. The new intermediate RenderStyle needs to inherit from
  214. // the old one.
  215. RefPtr<RenderStyle> oldRenderStyle = renderer->style();
  216. RefPtr<RenderStyle> newRenderStyle = RenderStyle::clone(oldRenderStyle.get());
  217. SVGRenderStyle* svgStyle = newRenderStyle.get()->accessSVGStyle();
  218. svgStyle->setFillPaint(SVGRenderStyle::initialFillPaintType(), SVGRenderStyle::initialFillPaintColor(), SVGRenderStyle::initialFillPaintUri());
  219. svgStyle->setStrokePaint(SVGRenderStyle::initialStrokePaintType(), SVGRenderStyle::initialStrokePaintColor(), SVGRenderStyle::initialStrokePaintUri());
  220. svgStyle->setFillRule(newClipRule);
  221. newRenderStyle.get()->setOpacity(1.0f);
  222. svgStyle->setFillOpacity(1.0f);
  223. svgStyle->setStrokeOpacity(1.0f);
  224. svgStyle->setFilterResource(String());
  225. svgStyle->setMaskerResource(String());
  226. // The setStyle() call results in a styleDidChange() call, which in turn invalidations the resources.
  227. // As we're mutating the resource on purpose, block updates until we've resetted the style again.
  228. m_invalidationBlocked = true;
  229. renderer->setStyle(newRenderStyle.release());
  230. // In the case of a <use> element, we obtained its renderere above, to retrieve its clipRule.
  231. // We have to pass the <use> renderer itself to renderSubtreeToImageBuffer() to apply it's x/y/transform/etc. values when rendering.
  232. // So if isUseElement is true, refetch the childNode->renderer(), as renderer got overriden above.
  233. SVGImageBufferTools::renderSubtreeToImageBuffer(clipperData->clipMaskImage.get(), isUseElement ? childNode->renderer() : renderer, maskContentTransformation);
  234. renderer->setStyle(oldRenderStyle.release());
  235. m_invalidationBlocked = false;
  236. }
  237. return true;
  238. }
  239. void RenderSVGResourceClipper::calculateClipContentRepaintRect()
  240. {
  241. // This is a rough heuristic to appraise the clip size and doesn't consider clip on clip.
  242. for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) {
  243. RenderObject* renderer = childNode->renderer();
  244. if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyled() || !renderer)
  245. continue;
  246. if (!renderer->isSVGPath() && !renderer->isSVGText() && !renderer->isSVGShadowTreeRootContainer())
  247. continue;
  248. RenderStyle* style = renderer->style();
  249. if (!style || style->display() == NONE || style->visibility() != VISIBLE)
  250. continue;
  251. m_clipBoundaries.unite(renderer->localToParentTransform().mapRect(renderer->repaintRectInLocalCoordinates()));
  252. }
  253. }
  254. bool RenderSVGResourceClipper::hitTestClipContent(const FloatRect& objectBoundingBox, const FloatPoint& nodeAtPoint)
  255. {
  256. FloatPoint point = nodeAtPoint;
  257. if (!SVGRenderSupport::pointInClippingArea(this, point))
  258. return false;
  259. if (static_cast<SVGClipPathElement*>(node())->clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
  260. AffineTransform transform;
  261. transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
  262. transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
  263. point = transform.inverse().mapPoint(point);
  264. }
  265. for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) {
  266. RenderObject* renderer = childNode->renderer();
  267. if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyled() || !renderer)
  268. continue;
  269. if (!renderer->isSVGPath() && !renderer->isSVGText() && !renderer->isSVGShadowTreeRootContainer())
  270. continue;
  271. IntPoint hitPoint;
  272. HitTestResult result(hitPoint);
  273. if (renderer->nodeAtFloatPoint(HitTestRequest(HitTestRequest::SVGClipContent), result, point, HitTestForeground))
  274. return true;
  275. }
  276. return false;
  277. }
  278. FloatRect RenderSVGResourceClipper::resourceBoundingBox(RenderObject* object)
  279. {
  280. // Resource was not layouted yet. Give back the boundingBox of the object.
  281. if (selfNeedsLayout())
  282. return object->objectBoundingBox();
  283. if (m_clipBoundaries.isEmpty())
  284. calculateClipContentRepaintRect();
  285. if (static_cast<SVGClipPathElement*>(node())->clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
  286. FloatRect objectBoundingBox = object->objectBoundingBox();
  287. AffineTransform transform;
  288. transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
  289. transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
  290. return transform.mapRect(m_clipBoundaries);
  291. }
  292. return m_clipBoundaries;
  293. }
  294. }
  295. #endif // ENABLE(SVG)