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); }
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 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; } }
gfxMatrix nsSVGGFrame::GetCanvasTM(uint32_t aFor) { if (!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) { if ((aFor == FOR_PAINTING && NS_SVGDisplayListPaintingEnabled()) || (aFor == FOR_HIT_TESTING && NS_SVGDisplayListHitTestingEnabled())) { return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(this); } } if (!mCanvasTM) { NS_ASSERTION(mParent, "null parent"); nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(mParent); SVGGraphicsElement *content = static_cast<SVGGraphicsElement*>(mContent); gfxMatrix tm = content->PrependLocalTransformsTo(parent->GetCanvasTM(aFor)); mCanvasTM = new gfxMatrix(tm); } return *mCanvasTM; }
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 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; }
already_AddRefed<SVGMatrix> SVGTransformableElement::GetTransformToElement( SVGGraphicsElement& aElement, ErrorResult& rv) { // the easiest way to do this (if likely to increase rounding error): RefPtr<SVGMatrix> ourScreenCTM = GetScreenCTM(); RefPtr<SVGMatrix> targetScreenCTM = aElement.GetScreenCTM(); if (!ourScreenCTM || !targetScreenCTM) { rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return nullptr; } RefPtr<SVGMatrix> tmp = targetScreenCTM->Inverse(rv); if (rv.Failed()) return nullptr; RefPtr<SVGMatrix> mat = tmp->Multiply(*ourScreenCTM); return mat.forget(); }
gfxMatrix nsSVGGFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot) { if (!(GetStateBits() & NS_FRAME_IS_NONDISPLAY) && !aTransformRoot) { if (aFor == FOR_PAINTING && NS_SVGDisplayListPaintingEnabled()) { return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(this); } if (aFor == FOR_HIT_TESTING && NS_SVGDisplayListHitTestingEnabled()) { return gfxMatrix(); } } if (!mCanvasTM) { NS_ASSERTION(GetParent(), "null parent"); nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(GetParent()); SVGGraphicsElement *content = static_cast<SVGGraphicsElement*>(mContent); gfxMatrix tm = content->PrependLocalTransformsTo( this == aTransformRoot ? gfxMatrix() : parent->GetCanvasTM(aFor, aTransformRoot)); mCanvasTM = new gfxMatrix(tm); } return *mCanvasTM; }
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; }