Beispiel #1
0
void RenderSVGShape::layout()
{
    StackStats::LayoutCheckPoint layoutCheckPoint;
    LayoutRepainter repainter(*this, SVGRenderSupport::checkForSVGRepaintDuringLayout(this) && selfNeedsLayout());
    SVGStyledTransformableElement* element = toSVGStyledTransformableElement(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);
}
Beispiel #2
0
void RenderPath::layout()
{
    LayoutRepainter repainter(*this, m_everHadLayout && checkForRepaintDuringLayout());
    SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());

    bool needsPathUpdate = m_needsPathUpdate;
    if (needsPathUpdate) {
        m_path = element->toPathData();
        m_needsPathUpdate = false;
    }

    if (m_needsTransformUpdate) {
        m_localTransform = element->animatedLocalTransform();
        m_needsTransformUpdate = false;
    }

    // Invalidate all resources of this client if our layout changed.
    if (m_everHadLayout && selfNeedsLayout())
        SVGResourcesCache::clientLayoutChanged(this);

    // At this point LayoutRepainter already grabbed the old bounds,
    // recalculate them now so repaintAfterLayout() uses the new bounds
    if (needsPathUpdate || m_needsBoundariesUpdate) {
        updateCachedBoundaries();
        m_needsBoundariesUpdate = false;
    }

    repainter.repaintAfterLayout();
    setNeedsLayout(false);
}
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)
            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;
}
Beispiel #4
0
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();
}
Beispiel #5
0
// FIXME: This does not belong here.
AffineTransform RenderSVGResourceContainer::transformOnNonScalingStroke(RenderObject* object, const AffineTransform& resourceTransform)
{
    if (!object->isSVGShape())
        return resourceTransform;

    SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(object->node());
    AffineTransform transform = element->getScreenCTM(SVGLocatable::DisallowStyleUpdate);
    transform *= resourceTransform;
    return transform;
}
Beispiel #6
0
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;
}
Beispiel #7
0
void RenderPath::layout()
{
    LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfNeedsLayout());

    SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());
    m_localTransform = element->animatedLocalTransform();
    setPath(element->toPathData());

    repainter.repaintAfterLayout();
    setNeedsLayout(false);
}
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;
}
Beispiel #9
0
void RenderSVGPath::fillAndStrokePath(GraphicsContext* context)
{
    RenderStyle* style = this->style();

    Color fallbackColor;
    if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(this, style, fallbackColor)) {
        if (fillPaintingResource->applyResource(this, style, context, ApplyToFillMode))
            fillPaintingResource->postApplyResource(this, context, ApplyToFillMode, &m_path);
        else if (fallbackColor.isValid()) {
            RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
            fallbackResource->setColor(fallbackColor);
            if (fallbackResource->applyResource(this, style, context, ApplyToFillMode))
                fallbackResource->postApplyResource(this, context, ApplyToFillMode, &m_path);
        }
    }

    fallbackColor = Color();
    RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(this, style, fallbackColor);
    if (!strokePaintingResource)
        return;

    Path path;

    bool nonScalingStroke = style->svgStyle()->vectorEffect() == VE_NON_SCALING_STROKE;

    GraphicsContextStateSaver stateSaver(*context, false);
    if (nonScalingStroke) {
        SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());
        AffineTransform nonScalingStrokeTransform = element->getScreenCTM(SVGLocatable::DisallowStyleUpdate);
        if (!nonScalingStrokeTransform.isInvertible())
            return;

        path = m_path;
        path.transform(nonScalingStrokeTransform);

        stateSaver.save();
        context->concatCTM(nonScalingStrokeTransform.inverse());
    }

    if (strokePaintingResource->applyResource(this, style, context, ApplyToStrokeMode))
        strokePaintingResource->postApplyResource(this, context, ApplyToStrokeMode, nonScalingStroke ? &path : &m_path);
    else if (fallbackColor.isValid()) {
        RenderSVGResourceSolidColor* fallbackResource = RenderSVGResource::sharedSolidPaintingResource();
        fallbackResource->setColor(fallbackColor);
        if (fallbackResource->applyResource(this, style, context, ApplyToStrokeMode))
            fallbackResource->postApplyResource(this, context, ApplyToStrokeMode, nonScalingStroke ? &path : &m_path);
    }
}
Beispiel #10
0
void RenderSVGPath::layout()
{
    LayoutRepainter repainter(*this, checkForRepaintDuringLayout() && selfNeedsLayout());
    SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());

    bool updateCachedBoundariesInParents = false;

    bool needsPathUpdate = m_needsPathUpdate;
    if (needsPathUpdate) {
        m_path.clear();
        element->toPathData(m_path);
        m_needsPathUpdate = false;
        updateCachedBoundariesInParents = true;
    }

    if (m_needsTransformUpdate) {
        m_localTransform = element->animatedLocalTransform();
        m_needsTransformUpdate = false;
        updateCachedBoundariesInParents = true;
    }

    if (m_needsBoundariesUpdate)
        updateCachedBoundariesInParents = true;

    // Invalidate all resources of this client if our layout changed.
    if (m_everHadLayout && selfNeedsLayout()) {
        SVGResourcesCache::clientLayoutChanged(this);
        m_markerLayoutInfo.clear();
    }

    // At this point LayoutRepainter already grabbed the old bounds,
    // recalculate them now so repaintAfterLayout() uses the new bounds.
    if (needsPathUpdate || m_needsBoundariesUpdate) {
        updateCachedBoundaries();
        m_needsBoundariesUpdate = false;
    }

    // If our bounds changed, notify the parents.
    if (updateCachedBoundariesInParents)
        RenderSVGModelObject::setNeedsBoundariesUpdate();

    repainter.repaintAfterLayout();
    setNeedsLayout(false);
}
bool RenderSVGTransformableContainer::calculateLocalTransform()
{
    SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(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 = static_cast<SVGUseElement*>(element);
    else if (element->isInShadowTree() && element->hasTagName(SVGNames::gTag)) {
        SVGElement* correspondingElement = element->correspondingElement();
        if (correspondingElement && correspondingElement->hasTagName(SVGNames::useTag))
            useElement = static_cast<SVGUseElement*>(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;
}
void SVGAnimateMotionElement::applyAnimatedValueToElement()
{
    if (!targetElement()->isStyledTransformable())
        return;
    
    SVGStyledTransformableElement* transformableElement = static_cast<SVGStyledTransformableElement*>(targetElement());
    RefPtr<SVGTransformList> transformList = transformableElement->transform();
    if (!transformList)
        return;
    
    ExceptionCode ec;
    if (!isAdditive())
        transformList->clear(ec);
    
    AffineTransform transform;
    transform.rotate(m_animatedAngle);
    transform.translate(m_animatedTranslation.width(), m_animatedTranslation.height());
    if (!transform.isIdentity()) {
        transformList->appendItem(SVGTransform(transform), ec);
        transformableElement->updateLocalTransform(transformList.get());
    }
}
Beispiel #13
0
void RenderPath::fillAndStrokePath(GraphicsContext* context)
{
    context->beginPath();
    RenderStyle* style = this->style();

    if (RenderSVGResource* fillPaintingResource = RenderSVGResource::fillPaintingResource(this, style)) {
        context->addPath(m_path);
        if (fillPaintingResource->applyResource(this, style, context, ApplyToFillMode))
            fillPaintingResource->postApplyResource(this, context, ApplyToFillMode);
    }

    RenderSVGResource* strokePaintingResource = RenderSVGResource::strokePaintingResource(this, style);
    if (!strokePaintingResource)
        return;

    bool restoreContext = false;
    if (style->svgStyle()->vectorEffect() == VE_NON_SCALING_STROKE) {
        SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());
        AffineTransform nonScalingStrokeTransform = element->getScreenCTM(SVGLocatable::DisallowStyleUpdate);
        if (!nonScalingStrokeTransform.isInvertible())
            return;

        Path transformedPath = m_path;
        transformedPath.transform(nonScalingStrokeTransform);

        context->save();
        context->concatCTM(nonScalingStrokeTransform.inverse());
        context->addPath(transformedPath);
        restoreContext = true;
    } else
        context->addPath(m_path);

    if (strokePaintingResource->applyResource(this, style, context, ApplyToStrokeMode))
        strokePaintingResource->postApplyResource(this, context, ApplyToStrokeMode);

    if (restoreContext)
        context->restore();
}
Beispiel #14
0
bool RenderSVGTransformableContainer::calculateLocalTransform()
{
    SVGStyledTransformableElement* element = static_cast<SVGStyledTransformableElement*>(node());

    bool needsUpdate = m_needsTransformUpdate;
    if (needsUpdate) {
        m_localTransform = element->animatedLocalTransform();
        m_needsTransformUpdate = false;
    }

    if (!element->hasTagName(SVGNames::gTag) || !static_cast<SVGGElement*>(element)->isShadowTreeContainerElement())
        return needsUpdate;

    FloatSize translation = static_cast<SVGShadowTreeContainerElement*>(element)->containerTranslation();
    if (translation.width() == 0 && translation.height() == 0)
        return needsUpdate;

    // FIXME: Could optimize this case for use to avoid refetching the animatedLocalTransform() here, if only the containerTranslation() changed.
    if (!needsUpdate)
        m_localTransform = element->animatedLocalTransform();

    m_localTransform.translate(translation.width(), translation.height());
    return true;
}