void RenderSVGShape::layout() { StackStats::LayoutCheckPoint layoutCheckPoint; LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(this) && selfNeedsLayout()); SVGGraphicsElement* element = toSVGGraphicsElement(node()); bool updateCachedBoundariesInParents = false; if (m_needsShapeUpdate || m_needsBoundariesUpdate) { updateShapeFromElement(); m_needsShapeUpdate = false; updateRepaintBoundingBox(); m_needsBoundariesUpdate = false; updateCachedBoundariesInParents = true; } if (m_needsTransformUpdate) { m_localTransform = element->animatedLocalTransform(); m_needsTransformUpdate = false; updateCachedBoundariesInParents = true; } // Invalidate all resources of this client if our layout changed. if (everHadLayout() && selfNeedsLayout()) SVGResourcesCache::clientLayoutChanged(this); // If our bounds changed, notify the parents. if (updateCachedBoundariesInParents) RenderSVGModelObject::setNeedsBoundariesUpdate(); repainter.repaintAfterLayout(); setNeedsLayout(false); }
bool RenderSVGTransformableContainer::calculateLocalTransform() { SVGGraphicsElement* element = toSVGGraphicsElement(node()); // If we're either the renderer for a <use> element, or for any <g> element inside the shadow // tree, that was created during the use/symbol/svg expansion in SVGUseElement. These containers // need to respect the translations induced by their corresponding use elements x/y attributes. SVGUseElement* useElement = 0; if (element->hasTagName(SVGNames::useTag)) useElement = toSVGUseElement(element); else if (element->isInShadowTree() && element->hasTagName(SVGNames::gTag)) { SVGElement* correspondingElement = element->correspondingElement(); if (correspondingElement && correspondingElement->hasTagName(SVGNames::useTag)) useElement = toSVGUseElement(correspondingElement); } if (useElement) { SVGLengthContext lengthContext(useElement); FloatSize translation(useElement->x().value(lengthContext), useElement->y().value(lengthContext)); if (translation != m_lastTranslation) m_needsTransformUpdate = true; m_lastTranslation = translation; } m_didTransformToRootUpdate = m_needsTransformUpdate || SVGRenderSupport::transformToRootChanged(parent()); if (!m_needsTransformUpdate) return false; m_localTransform = element->animatedLocalTransform(); m_localTransform.translate(m_lastTranslation.width(), m_lastTranslation.height()); m_needsTransformUpdate = false; return true; }
bool SVGSVGElement::checkIntersectionOrEnclosure( const SVGElement& element, const FloatRect& rect, CheckIntersectionOrEnclosure mode) const { LayoutObject* layoutObject = element.layoutObject(); ASSERT(!layoutObject || layoutObject->style()); if (!layoutObject || layoutObject->style()->pointerEvents() == EPointerEvents::None) return false; if (!isIntersectionOrEnclosureTarget(layoutObject)) return false; AffineTransform ctm = toSVGGraphicsElement(element).computeCTM( AncestorScope, DisallowStyleUpdate, this); FloatRect mappedRepaintRect = ctm.mapRect(layoutObject->visualRectInLocalSVGCoordinates()); bool result = false; switch (mode) { case CheckIntersection: result = intersectsAllowingEmpty(rect, mappedRepaintRect); break; case CheckEnclosure: result = rect.contains(mappedRepaintRect); break; default: ASSERT_NOT_REACHED(); break; } return result; }
void RenderSVGShape::layout() { bool updateCachedBoundariesInParents = false; if (m_needsShapeUpdate || m_needsBoundariesUpdate) { updateShapeFromElement(); m_needsShapeUpdate = false; updatePaintInvalidationBoundingBox(); m_needsBoundariesUpdate = false; updateCachedBoundariesInParents = true; } if (m_needsTransformUpdate) { m_localTransform = toSVGGraphicsElement(element())->animatedLocalTransform(); m_needsTransformUpdate = false; updateCachedBoundariesInParents = true; } // Invalidate all resources of this client if our layout changed. if (everHadLayout() && selfNeedsLayout()) SVGResourcesCache::clientLayoutChanged(this); // If our bounds changed, notify the parents. if (updateCachedBoundariesInParents) RenderSVGModelObject::setNeedsBoundariesUpdate(); clearNeedsLayout(); }
void LayoutSVGShape::createPath() { clearPath(); m_path = adoptPtr(new Path); ASSERT(LayoutSVGShape::isShapeEmpty()); updatePathFromGraphicsElement(toSVGGraphicsElement(element()), path()); }
bool SVGElement::getBoundingBox(FloatRect& rect) { if (!isSVGGraphicsElement()) return false; rect = toSVGGraphicsElement(this)->getBBox(); return true; }
AffineTransform LayoutSVGShape::nonScalingStrokeTransform() const { AffineTransform t = toSVGGraphicsElement(element())->getScreenCTM(SVGGraphicsElement::DisallowStyleUpdate); // Width of non-scaling stroke is independent of translation, so zero it out here. t.setE(0); t.setF(0); return t; }
void LayoutSVGShape::updateLocalTransform() { SVGGraphicsElement* graphicsElement = toSVGGraphicsElement(element()); if (graphicsElement->hasTransform(SVGElement::IncludeMotionTransform)) { m_localTransform.setTransform(graphicsElement->calculateTransform( SVGElement::IncludeMotionTransform)); } else { m_localTransform = AffineTransform(); } }
// FIXME: This does not belong here. AffineTransform RenderSVGResourceContainer::transformOnNonScalingStroke(RenderObject* object, const AffineTransform& resourceTransform) { if (!object->isSVGShape()) return resourceTransform; SVGGraphicsElement* element = toSVGGraphicsElement(object->node()); AffineTransform transform = element->getScreenCTM(SVGGraphicsElement::DisallowStyleUpdate); transform *= resourceTransform; return transform; }
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 = clipPathElement().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)->isSVGGraphicsElement()) continue; SVGGraphicsElement* styled = toSVGGraphicsElement(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 (clipPathElement().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; }
void RenderSVGShape::updateShapeFromElement() { m_path.clear(); m_path = adoptPtr(new Path); ASSERT(RenderSVGShape::isShapeEmpty()); updatePathFromGraphicsElement(toSVGGraphicsElement(element()), path()); processMarkerPositions(); m_fillBoundingBox = calculateObjectBoundingBox(); m_strokeBoundingBox = calculateStrokeBoundingBox(); }
void LayoutSVGShape::updateLocalTransform() { SVGGraphicsElement* graphicsElement = toSVGGraphicsElement(element()); if (graphicsElement->hasAnimatedLocalTransform()) { if (m_localTransform) m_localTransform->setTransform(graphicsElement->calculateAnimatedLocalTransform()); else m_localTransform = adoptPtr(new AffineTransform(graphicsElement->calculateAnimatedLocalTransform())); } else { m_localTransform = 0; } }
void SVGRenderSupport::updateMaskedAncestorShouldIsolateBlending(const RenderElement& renderer) { ASSERT(renderer.element()); ASSERT(renderer.element()->isSVGElement()); bool maskedAncestorShouldIsolateBlending = renderer.style().hasBlendMode(); for (auto* ancestor = renderer.element()->parentElement(); ancestor && ancestor->isSVGElement(); ancestor = ancestor->parentElement()) { if (!toSVGElement(ancestor)->isSVGGraphicsElement() || !isolatesBlending(*ancestor->computedStyle())) continue; if (ancestor->computedStyle()->svgStyle().hasMasker()) toSVGGraphicsElement(ancestor)->setShouldIsolateBlending(maskedAncestorShouldIsolateBlending); return; } }
void SVGUseElement::toClipPath(Path& path) { ASSERT(path.isEmpty()); Node* n = m_targetElementInstance ? m_targetElementInstance->shadowTreeElement() : 0; if (!n) return; if (n->isSVGElement() && toSVGElement(*n).isSVGGraphicsElement()) { if (!isDirectReference(toSVGElement(*n))) { // Spec: Indirect references are an error (14.3.5) document().accessSVGExtensions()->reportError("Not allowed to use indirect reference in <clip-path>"); } else { toSVGGraphicsElement(*n).toClipPath(path); // FIXME: Avoid manual resolution of x/y here. Its potentially harmful. SVGLengthContext lengthContext(this); path.translate(FloatSize(x().value(lengthContext), y().value(lengthContext))); path.transform(animatedLocalTransform()); } } }
SVGGraphicsElement* SVGUseElement::targetGraphicsElementForClipping() const { Node* n = userAgentShadowRoot()->firstChild(); if (!n || !n->isSVGElement()) return nullptr; SVGElement& element = toSVGElement(*n); if (!element.isSVGGraphicsElement()) return nullptr; // Spec: "If a <use> element is a child of a clipPath element, it must directly // reference <path>, <text> or basic shapes elements. Indirect references are an // error and the clipPath element must be ignored." // http://dev.w3.org/fxtf/css-masking-1/#the-clip-path if (!isDirectReference(element)) { // Spec: Indirect references are an error (14.3.5) document().accessSVGExtensions().reportError("Not allowed to use indirect reference in <clip-path>"); return nullptr; } return &toSVGGraphicsElement(element); }
bool LayoutSVGTransformableContainer::calculateLocalTransform() { SVGGraphicsElement* element = toSVGGraphicsElement(this->element()); ASSERT(element); // If we're either the layoutObject for a <use> element, or for any <g> element inside the shadow // tree, that was created during the use/symbol/svg expansion in SVGUseElement. These containers // need to respect the translations induced by their corresponding use elements x/y attributes. SVGUseElement* useElement = nullptr; if (isSVGUseElement(*element)) { useElement = toSVGUseElement(element); } else if (isSVGGElement(*element) && toSVGGElement(element)->inUseShadowTree()) { SVGElement* correspondingElement = element->correspondingElement(); if (isSVGUseElement(correspondingElement)) useElement = toSVGUseElement(correspondingElement); } if (useElement) { SVGLengthContext lengthContext(useElement); FloatSize translation( useElement->x()->currentValue()->value(lengthContext), useElement->y()->currentValue()->value(lengthContext)); if (translation != m_additionalTranslation) m_needsTransformUpdate = true; m_additionalTranslation = translation; } m_didTransformToRootUpdate = m_needsTransformUpdate || SVGLayoutSupport::transformToRootChanged(parent()); if (!m_needsTransformUpdate) return false; m_localTransform = element->calculateAnimatedLocalTransform(); m_localTransform.translate(m_additionalTranslation.width(), m_additionalTranslation.height()); m_needsTransformUpdate = false; return true; }
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; WindRule clipRule = RULE_NONZERO; Path clipPath = Path(); for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement)) { RenderObject* renderer = childElement->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 (!childElement->isSVGGraphicsElement()) continue; SVGGraphicsElement* styled = toSVGGraphicsElement(childElement); 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; if (clipPath.isEmpty()) { // First clip shape. styled->toClipPath(clipPath); clipRule = svgStyle.clipRule(); clipPath.setWindRule(clipRule); continue; } if (RuntimeEnabledFeatures::pathOpsSVGClippingEnabled()) { // Attempt to generate a combined clip path, fall back to masking if not possible. Path subPath; styled->toClipPath(subPath); subPath.setWindRule(svgStyle.clipRule()); if (!clipPath.unionPath(subPath)) return false; } else { return false; } } // Only one visible shape/path was found. Directly continue clipping and transform the content to userspace if necessary. if (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; }