void SVGRadialGradientElement::buildGradient() const
{
    RadialGradientAttributes attributes = collectGradientProperties();

    RefPtr<SVGPaintServerRadialGradient> radialGradient = WTF::static_pointer_cast<SVGPaintServerRadialGradient>(m_resource);

    double adjustedFocusX = attributes.fx();
    double adjustedFocusY = attributes.fy();

    double fdx = attributes.fx() - attributes.cx();
    double fdy = attributes.fy() - attributes.cy();

    // Spec: If (fx, fy) lies outside the circle defined by (cx, cy) and
    // r, set (fx, fy) to the point of intersection of the line through
    // (fx, fy) and the circle.
    if (sqrt(fdx * fdx + fdy * fdy) > attributes.r()) {
        double angle = atan2(attributes.fy() * 100.0, attributes.fx() * 100.0);
        adjustedFocusX = cos(angle) * attributes.r();
        adjustedFocusY = sin(angle) * attributes.r();
    }

    FloatPoint focalPoint = FloatPoint::narrowPrecision(attributes.fx(), attributes.fy());
    FloatPoint centerPoint = FloatPoint::narrowPrecision(attributes.cx(), attributes.cy());

    RefPtr<Gradient> gradient = Gradient::create(
        FloatPoint::narrowPrecision(adjustedFocusX, adjustedFocusY),
        0.f, // SVG does not support a "focus radius"
        centerPoint,
        narrowPrecisionToFloat(attributes.r()));
    gradient->setSpreadMethod(attributes.spreadMethod());

    Vector<SVGGradientStop> stops = attributes.stops();
    float previousOffset = 0.0f;
    for (unsigned i = 0; i < stops.size(); ++i) {
         float offset = std::min(std::max(previousOffset, stops[i].first), 1.0f);
         previousOffset = offset;
         gradient->addColorStop(offset, stops[i].second);
    }

    radialGradient->setGradient(gradient);

    if (attributes.stops().isEmpty())
        return;

    radialGradient->setBoundingBoxMode(attributes.boundingBoxMode());
    radialGradient->setGradientTransform(attributes.gradientTransform());
    radialGradient->setGradientCenter(centerPoint);
    radialGradient->setGradientFocal(focalPoint);
    radialGradient->setGradientRadius(narrowPrecisionToFloat(attributes.r()));
    radialGradient->setGradientStops(attributes.stops());
}
void SVGRadialGradientElement::buildGradient() const
{
    RadialGradientAttributes attributes = collectGradientProperties();

    // If we didn't find any gradient containing stop elements, ignore the request.
    if (attributes.stops().isEmpty())
        return;

    RefPtr<SVGPaintServerRadialGradient> radialGradient = WTF::static_pointer_cast<SVGPaintServerRadialGradient>(m_resource);

    radialGradient->setGradientStops(attributes.stops());
    radialGradient->setBoundingBoxMode(attributes.boundingBoxMode());
    radialGradient->setGradientSpreadMethod(attributes.spreadMethod()); 
    radialGradient->setGradientTransform(attributes.gradientTransform());
    radialGradient->setGradientCenter(FloatPoint::narrowPrecision(attributes.cx(), attributes.cy()));
    radialGradient->setGradientFocal(FloatPoint::narrowPrecision(attributes.fx(), attributes.fy()));
    radialGradient->setGradientRadius(narrowPrecisionToFloat(attributes.r()));
}
Exemplo n.º 3
0
bool SVGRadialGradientElement::collectGradientAttributes(RadialGradientAttributes& attributes)
{
    if (!renderer())
        return false;

    WillBeHeapHashSet<RawPtrWillBeMember<SVGGradientElement> > processedGradients;
    SVGGradientElement* current = this;

    setGradientAttributes(current, attributes);
    processedGradients.add(current);

    while (true) {
        // Respect xlink:href, take attributes from referenced element
        Node* refNode = SVGURIReference::targetElementFromIRIString(current->href()->currentValue()->value(), treeScope());
        if (refNode && isSVGGradientElement(*refNode)) {
            current = toSVGGradientElement(refNode);

            // Cycle detection
            if (processedGradients.contains(current))
                break;

            if (!current->renderer())
                return false;

            setGradientAttributes(current, attributes, isSVGRadialGradientElement(*current));
            processedGradients.add(current);
        } else {
            break;
        }
    }

    // Handle default values for fx/fy
    if (!attributes.hasFx())
        attributes.setFx(attributes.cx());

    if (!attributes.hasFy())
        attributes.setFy(attributes.cy());

    return true;
}
bool SVGRadialGradientElement::collectGradientAttributes(RadialGradientAttributes& attributes)
{
    if (!renderer())
        return false;

    HashSet<SVGGradientElement*> processedGradients;
    SVGGradientElement* current = this;

    setGradientAttributes(*current, attributes);
    processedGradients.add(current);

    while (true) {
        // Respect xlink:href, take attributes from referenced element
        Node* refNode = SVGURIReference::targetElementFromIRIString(current->href(), document());
        if (is<SVGGradientElement>(refNode)) {
            current = downcast<SVGGradientElement>(refNode);

            // Cycle detection
            if (processedGradients.contains(current))
                break;

            if (!current->renderer())
                return false;

            setGradientAttributes(*current, attributes, current->hasTagName(SVGNames::radialGradientTag));
            processedGradients.add(current);
        } else
            break;
    }

    // Handle default values for fx/fy
    if (!attributes.hasFx())
        attributes.setFx(attributes.cx());

    if (!attributes.hasFy())
        attributes.setFy(attributes.cy());

    return true;
}
RadialGradientAttributes SVGRadialGradientElement::collectGradientProperties() const
{
    RadialGradientAttributes attributes;
    HashSet<const SVGGradientElement*> processedGradients;

    bool isRadial = true;
    const SVGGradientElement* current = this;

    while (current) {
        if (!attributes.hasSpreadMethod() && current->hasAttribute(SVGNames::spreadMethodAttr))
            attributes.setSpreadMethod((GradientSpreadMethod) current->spreadMethod());

        if (!attributes.hasBoundingBoxMode() && current->hasAttribute(SVGNames::gradientUnitsAttr))
            attributes.setBoundingBoxMode(current->gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);

        if (!attributes.hasGradientTransform() && current->hasAttribute(SVGNames::gradientTransformAttr))
            attributes.setGradientTransform(current->gradientTransform()->consolidate().matrix());

        if (!attributes.hasStops()) {
            const Vector<SVGGradientStop>& stops(current->buildStops());
            if (!stops.isEmpty())
                attributes.setStops(stops);
        }

        if (isRadial) {
            const SVGRadialGradientElement* radial = static_cast<const SVGRadialGradientElement*>(current);

            if (!attributes.hasCx() && current->hasAttribute(SVGNames::cxAttr))
                attributes.setCx(radial->cx().valueAsPercentage());

            if (!attributes.hasCy() && current->hasAttribute(SVGNames::cyAttr))
                attributes.setCy(radial->cy().valueAsPercentage());

            if (!attributes.hasR() && current->hasAttribute(SVGNames::rAttr))
                attributes.setR(radial->r().valueAsPercentage());

            if (!attributes.hasFx() && current->hasAttribute(SVGNames::fxAttr))
                attributes.setFx(radial->fx().valueAsPercentage());

            if (!attributes.hasFy() && current->hasAttribute(SVGNames::fyAttr))
                attributes.setFy(radial->fy().valueAsPercentage());
        }

        processedGradients.add(current);

        // Respect xlink:href, take attributes from referenced element
        Node* refNode = ownerDocument()->getElementById(SVGURIReference::getTarget(current->href()));
        if (refNode && (refNode->hasTagName(SVGNames::radialGradientTag) || refNode->hasTagName(SVGNames::linearGradientTag))) {
            current = static_cast<const SVGGradientElement*>(const_cast<const Node*>(refNode));

            // Cycle detection
            if (processedGradients.contains(current))
                return RadialGradientAttributes();

            isRadial = current->gradientType() == RadialGradientPaintServer;
        } else
            current = 0;
    }

    // Handle default values for fx/fy
    if (!attributes.hasFx())
        attributes.setFx(attributes.cx());

    if (!attributes.hasFy())
        attributes.setFy(attributes.cy());

    return attributes;
}
FloatPoint RenderSVGResourceRadialGradient::centerPoint(const RadialGradientAttributes& attributes) const
{
    return SVGLengthContext::resolvePoint(static_cast<const SVGElement*>(node()), attributes.gradientUnits(), attributes.cx(), attributes.cy());
}
FloatPoint LayoutSVGResourceRadialGradient::centerPoint(const RadialGradientAttributes& attributes) const
{
    return SVGLengthContext::resolvePoint(element(), attributes.gradientUnits(), *attributes.cx(), *attributes.cy());
}