void LayoutSVGShape::createPath() { if (!m_path) m_path = WTF::makeUnique<Path>(); *m_path = toSVGGeometryElement(element())->asPath(); if (m_rareData.get()) m_rareData->m_cachedNonScalingStrokePath.clear(); }
void LayoutSVGShape::createPath() { if (!m_path) m_path = adoptPtr(new Path()); *m_path = toSVGGeometryElement(element())->asPath(); if (m_rareData.get()) m_rareData->m_cachedNonScalingStrokePath.clear(); }
void SVGUseElement::toClipPath(Path& path) const { ASSERT(path.isEmpty()); const SVGGraphicsElement* element = targetGraphicsElementForClipping(); if (!element) return; if (element->isSVGGeometryElement()) { toSVGGeometryElement(*element).toClipPath(path); // FIXME: Avoid manual resolution of x/y here. Its potentially harmful. SVGLengthContext lengthContext(this); path.translate(FloatSize(m_x->currentValue()->value(lengthContext), m_y->currentValue()->value(lengthContext))); path.transform(calculateAnimatedLocalTransform()); } }
bool LayoutSVGResourceClipper::calculateClipContentPathIfNeeded() { if (!m_clipContentPath.isEmpty()) return true; // If the current clip-path gets clipped itself, we have to fallback to masking. if (style()->svgStyle().hasClipper()) return false; unsigned opCount = 0; bool usingBuilder = false; SkOpBuilder clipPathBuilder; for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement)) { LayoutObject* childLayoutObject = childElement->layoutObject(); if (!childLayoutObject) continue; // Only shapes or paths are supported for direct clipping. We need to fallback to masking for texts. if (childLayoutObject->isSVGText()) { m_clipContentPath.clear(); return false; } if (!childElement->isSVGGraphicsElement()) continue; const ComputedStyle* style = childLayoutObject->style(); if (!style || style->display() == NONE || style->visibility() != VISIBLE) continue; // Current shape in clip-path gets clipped too. Fallback to masking. if (style->svgStyle().hasClipper()) { m_clipContentPath.clear(); return false; } // First clip shape. if (m_clipContentPath.isEmpty()) { if (isSVGGeometryElement(childElement)) toSVGGeometryElement(childElement)->toClipPath(m_clipContentPath); else if (isSVGUseElement(childElement)) toSVGUseElement(childElement)->toClipPath(m_clipContentPath); continue; } // Multiple shapes require PathOps. In some degenerate cases PathOps can exhibit quadratic // behavior, so we cap the number of ops to a reasonable count. const unsigned kMaxOps = 42; if (!RuntimeEnabledFeatures::pathOpsSVGClippingEnabled() || ++opCount > kMaxOps) { m_clipContentPath.clear(); return false; } // Second clip shape => start using the builder. if (!usingBuilder) { clipPathBuilder.add(m_clipContentPath.skPath(), kUnion_SkPathOp); usingBuilder = true; } Path subPath; if (isSVGGeometryElement(childElement)) toSVGGeometryElement(childElement)->toClipPath(subPath); else if (isSVGUseElement(childElement)) toSVGUseElement(childElement)->toClipPath(subPath); clipPathBuilder.add(subPath.skPath(), kUnion_SkPathOp); } if (usingBuilder) { SkPath resolvedPath; clipPathBuilder.resolve(&resolvedPath); m_clipContentPath = resolvedPath; } return true; }
float LayoutSVGShape::dashScaleFactor() const { if (!styleRef().svgStyle().strokeDashArray()->size()) return 1; return toSVGGeometryElement(*element()).pathLengthScaleFactor(); }