SVGResource* SVGClipPathElement::canvasResource() { if (!m_clipper) m_clipper = SVGResourceClipper::create(); else m_clipper->resetClipData(); bool bbox = clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX; RenderStyle* clipPathStyle = styleForRenderer(parent()->renderer()); // FIXME: Manual style resolution is a hack for (Node* n = firstChild(); n; n = n->nextSibling()) { if (n->isSVGElement() && static_cast<SVGElement*>(n)->isStyledTransformable()) { SVGStyledTransformableElement* styled = static_cast<SVGStyledTransformableElement*>(n); RenderStyle* pathStyle = document()->styleSelector()->styleForElement(styled, clipPathStyle); if (pathStyle->display() != NONE) { Path pathData = styled->toClipPath(); // FIXME: How do we know the element has done a layout? pathData.transform(styled->animatedLocalTransform()); if (!pathData.isEmpty()) m_clipper->addClipData(pathData, pathStyle->svgStyle()->clipRule(), bbox); } pathStyle->deref(document()->renderArena()); } } if (m_clipper->clipData().isEmpty()) { Path pathData; pathData.addRect(FloatRect()); m_clipper->addClipData(pathData, RULE_EVENODD, bbox); } clipPathStyle->deref(document()->renderArena()); return m_clipper.get(); }
FloatRect RenderSVGResourceClipper::resourceBoundingBox(const FloatRect& objectBoundingBox) const { FloatRect clipRect; for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) { if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyledTransformable()) continue; SVGStyledTransformableElement* styled = static_cast<SVGStyledTransformableElement*>(childNode); RenderStyle* style = styled->renderer() ? styled->renderer()->style() : 0; if (!style || style->display() == NONE || styled->toClipPath().isEmpty()) continue; clipRect.unite(styled->renderer()->objectBoundingBox()); } if (clipRect.isEmpty()) return FloatRect(); if (static_cast<SVGClipPathElement*>(node())->clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) { AffineTransform obbTransform; obbTransform.translate(objectBoundingBox.x(), objectBoundingBox.y()); obbTransform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); return obbTransform.mapRect(clipRect); } return clipRect; }
bool RenderSVGResourceClipper::pathOnlyClipping(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(); // If clip-path only contains one visible shape or path, we can use path-based clipping. Invisible // shapes don't affect the clipping and can be ignored. If clip-path contains more than one // visible shape, the additive clipping may not work, caused by the clipRule. EvenOdd // as well as NonZero can cause self-clipping of the elements. // See also http://www.w3.org/TR/SVG/painting.html#FillRuleProperty for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) { RenderObject* renderer = childNode->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 (!childNode->isSVGElement() || !toSVGElement(childNode)->isStyledTransformable()) continue; SVGStyledTransformableElement* styled = toSVGStyledTransformableElement(childNode); RenderStyle* style = renderer->style(); if (!style || style->display() == NONE || style->visibility() != VISIBLE) continue; const SVGRenderStyle* svgStyle = style->svgStyle(); // Current shape in clip-path gets clipped too. Fallback to masking. if (!svgStyle->clipperResource().isEmpty()) return false; // Fallback to masking, if there is more than one clipping path. if (clipPath.isEmpty()) { styled->toClipPath(clipPath); clipRule = svgStyle->clipRule(); } else return false; } // Only one visible shape/path was found. Directly continue clipping and transform the content to userspace if necessary. if (static_cast<SVGClipPathElement*>(node())->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()); context->clipPath(clipPath, clipRule); return true; }
bool RenderSVGResourceClipper::applyResource(RenderObject* object, GraphicsContext* context) { ASSERT(object); ASSERT(context); m_clipper.add(object); context->beginPath(); AffineTransform obbTransform; FloatRect objectBoundingBox = object->objectBoundingBox(); bool bbox = static_cast<SVGClipPathElement*>(node())->clipPathUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX; if (bbox) { obbTransform.translate(objectBoundingBox.x(), objectBoundingBox.y()); obbTransform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); } bool hasClipPath = false; WindRule clipRule = RULE_EVENODD; for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) { if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyledTransformable()) continue; SVGStyledTransformableElement* styled = static_cast<SVGStyledTransformableElement*>(childNode); RenderStyle* style = styled->renderer() ? styled->renderer()->style() : 0; if (!style || style->display() == NONE) continue; Path pathData = styled->toClipPath(); if (pathData.isEmpty()) continue; if (bbox) pathData.transform(obbTransform); hasClipPath = true; context->addPath(pathData); clipRule = style->svgStyle()->clipRule(); } if (!hasClipPath) { Path clipPath; clipPath.addRect(FloatRect()); context->addPath(clipPath); } // FIXME! // We don't currently allow for heterogenous clip rules. // we would have to detect such, draw to a mask, and then clip // to that mask context->clipPath(clipRule); return true; }