This source file includes following definitions.
- clippedOverflowRectForRepaint
- computeFloatRectForRepaint
- mapLocalToContainer
- pushMappingToContainer
- checkForSVGRepaintDuringLayout
- updateObjectBoundingBox
- computeContainerBoundingBoxes
- paintInfoIntersectsRepaintRect
- findTreeRootObject
- invalidateResourcesOfChildren
- layoutSizeOfNearestViewportChanged
- transformToRootChanged
- layoutChildren
- layoutResourcesIfNeeded
- isOverflowHidden
- intersectRepaintRectWithResources
- filtersForceContainerLayout
- pointInClippingArea
- applyStrokeStyleToContext
- applyStrokeStyleToStrokeData
- isRenderableTextNode
#include "config.h"
#include "core/rendering/svg/SVGRenderSupport.h"
#include "core/rendering/RenderGeometryMap.h"
#include "core/rendering/RenderLayer.h"
#include "core/rendering/SubtreeLayoutScope.h"
#include "core/rendering/svg/RenderSVGInlineText.h"
#include "core/rendering/svg/RenderSVGResourceClipper.h"
#include "core/rendering/svg/RenderSVGResourceFilter.h"
#include "core/rendering/svg/RenderSVGResourceMasker.h"
#include "core/rendering/svg/RenderSVGRoot.h"
#include "core/rendering/svg/RenderSVGText.h"
#include "core/rendering/svg/RenderSVGViewportContainer.h"
#include "core/rendering/svg/SVGResources.h"
#include "core/rendering/svg/SVGResourcesCache.h"
#include "core/svg/SVGElement.h"
#include "platform/geometry/TransformState.h"
namespace WebCore {
LayoutRect SVGRenderSupport::clippedOverflowRectForRepaint(const RenderObject* object, const RenderLayerModelObject* repaintContainer)
{
if (object->style()->visibility() != VISIBLE && !object->enclosingLayer()->hasVisibleContent())
return LayoutRect();
FloatRect repaintRect = object->repaintRectInLocalCoordinates();
object->computeFloatRectForRepaint(repaintContainer, repaintRect);
return enclosingLayoutRect(repaintRect);
}
void SVGRenderSupport::computeFloatRectForRepaint(const RenderObject* object, const RenderLayerModelObject* repaintContainer, FloatRect& repaintRect, bool fixed)
{
repaintRect.inflate(object->style()->outlineWidth());
repaintRect = object->localToParentTransform().mapRect(repaintRect);
object->parent()->computeFloatRectForRepaint(repaintContainer, repaintRect, fixed);
}
void SVGRenderSupport::mapLocalToContainer(const RenderObject* object, const RenderLayerModelObject* repaintContainer, TransformState& transformState, bool* wasFixed)
{
transformState.applyTransform(object->localToParentTransform());
RenderObject* parent = object->parent();
if (parent->isSVGRoot())
transformState.applyTransform(toRenderSVGRoot(parent)->localToBorderBoxTransform());
MapCoordinatesFlags mode = UseTransforms;
parent->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed);
}
const RenderObject* SVGRenderSupport::pushMappingToContainer(const RenderObject* object, const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap)
{
ASSERT_UNUSED(ancestorToStopAt, ancestorToStopAt != object);
RenderObject* parent = object->parent();
if (parent->isSVGRoot()) {
TransformationMatrix matrix(object->localToParentTransform());
matrix.multiply(toRenderSVGRoot(parent)->localToBorderBoxTransform());
geometryMap.push(object, matrix);
} else
geometryMap.push(object, object->localToParentTransform());
return parent;
}
bool SVGRenderSupport::checkForSVGRepaintDuringLayout(RenderObject* object)
{
if (!object->checkForRepaintDuringLayout())
return false;
RenderObject* parent = object->parent();
return !(parent && parent->isSVGContainer() && toRenderSVGContainer(parent)->didTransformToRootUpdate());
}
static inline void updateObjectBoundingBox(FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, RenderObject* other, FloatRect otherBoundingBox)
{
bool otherValid = other->isSVGContainer() ? toRenderSVGContainer(other)->isObjectBoundingBoxValid() : true;
if (!otherValid)
return;
if (!objectBoundingBoxValid) {
objectBoundingBox = otherBoundingBox;
objectBoundingBoxValid = true;
return;
}
objectBoundingBox.uniteEvenIfEmpty(otherBoundingBox);
}
void SVGRenderSupport::computeContainerBoundingBoxes(const RenderObject* container, FloatRect& objectBoundingBox, bool& objectBoundingBoxValid, FloatRect& strokeBoundingBox, FloatRect& repaintBoundingBox)
{
objectBoundingBox = FloatRect();
objectBoundingBoxValid = false;
strokeBoundingBox = FloatRect();
for (RenderObject* current = container->firstChild(); current; current = current->nextSibling()) {
if (current->isSVGHiddenContainer())
continue;
const AffineTransform& transform = current->localToParentTransform();
updateObjectBoundingBox(objectBoundingBox, objectBoundingBoxValid, current,
transform.mapRect(current->objectBoundingBox()));
strokeBoundingBox.unite(transform.mapRect(current->repaintRectInLocalCoordinates()));
}
repaintBoundingBox = strokeBoundingBox;
}
bool SVGRenderSupport::paintInfoIntersectsRepaintRect(const FloatRect& localRepaintRect, const AffineTransform& localTransform, const PaintInfo& paintInfo)
{
return localTransform.mapRect(localRepaintRect).intersects(paintInfo.rect);
}
const RenderSVGRoot* SVGRenderSupport::findTreeRootObject(const RenderObject* start)
{
while (start && !start->isSVGRoot())
start = start->parent();
ASSERT(start);
ASSERT(start->isSVGRoot());
return toRenderSVGRoot(start);
}
static inline void invalidateResourcesOfChildren(RenderObject* start)
{
ASSERT(!start->needsLayout());
if (SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(start))
resources->removeClientFromCache(start, false);
for (RenderObject* child = start->firstChild(); child; child = child->nextSibling())
invalidateResourcesOfChildren(child);
}
static inline bool layoutSizeOfNearestViewportChanged(const RenderObject* start)
{
while (start && !start->isSVGRoot() && !start->isSVGViewportContainer())
start = start->parent();
ASSERT(start);
ASSERT(start->isSVGRoot() || start->isSVGViewportContainer());
if (start->isSVGViewportContainer())
return toRenderSVGViewportContainer(start)->isLayoutSizeChanged();
return toRenderSVGRoot(start)->isLayoutSizeChanged();
}
bool SVGRenderSupport::transformToRootChanged(RenderObject* ancestor)
{
while (ancestor && !ancestor->isSVGRoot()) {
if (ancestor->isSVGTransformableContainer())
return toRenderSVGContainer(ancestor)->didTransformToRootUpdate();
if (ancestor->isSVGViewportContainer())
return toRenderSVGViewportContainer(ancestor)->didTransformToRootUpdate();
ancestor = ancestor->parent();
}
return false;
}
void SVGRenderSupport::layoutChildren(RenderObject* start, bool selfNeedsLayout)
{
bool layoutSizeChanged = layoutSizeOfNearestViewportChanged(start);
bool transformChanged = transformToRootChanged(start);
HashSet<RenderObject*> notlayoutedObjects;
for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
bool needsLayout = selfNeedsLayout;
bool childEverHadLayout = child->everHadLayout();
if (transformChanged) {
if (child->isSVGText())
toRenderSVGText(child)->setNeedsTextMetricsUpdate();
needsLayout = true;
}
if (layoutSizeChanged) {
if (SVGElement* element = child->node()->isSVGElement() ? toSVGElement(child->node()) : 0) {
if (element->hasRelativeLengths()) {
if (child->isSVGShape())
toRenderSVGShape(child)->setNeedsShapeUpdate();
else if (child->isSVGText()) {
toRenderSVGText(child)->setNeedsTextMetricsUpdate();
toRenderSVGText(child)->setNeedsPositioningValuesUpdate();
}
needsLayout = true;
}
}
}
SubtreeLayoutScope layoutScope(child);
if (needsLayout && !child->isSVGResourceContainer())
layoutScope.setNeedsLayout(child);
layoutResourcesIfNeeded(child);
if (child->needsLayout()) {
child->layout();
if (!childEverHadLayout && !RuntimeEnabledFeatures::repaintAfterLayoutEnabled())
child->repaint();
} else if (layoutSizeChanged)
notlayoutedObjects.add(child);
}
if (!layoutSizeChanged) {
ASSERT(notlayoutedObjects.isEmpty());
return;
}
HashSet<RenderObject*>::iterator end = notlayoutedObjects.end();
for (HashSet<RenderObject*>::iterator it = notlayoutedObjects.begin(); it != end; ++it)
invalidateResourcesOfChildren(*it);
}
void SVGRenderSupport::layoutResourcesIfNeeded(const RenderObject* object)
{
ASSERT(object);
SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
if (resources)
resources->layoutIfNeeded();
}
bool SVGRenderSupport::isOverflowHidden(const RenderObject* object)
{
ASSERT(object->style()->overflowX() == object->style()->overflowY());
ASSERT(object->style()->overflowX() != OSCROLL);
ASSERT(!object->isRoot());
return object->style()->overflowX() == OHIDDEN;
}
void SVGRenderSupport::intersectRepaintRectWithResources(const RenderObject* renderer, FloatRect& repaintRect)
{
ASSERT(renderer);
SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(renderer);
if (!resources)
return;
if (RenderSVGResourceFilter* filter = resources->filter())
repaintRect = filter->resourceBoundingBox(renderer);
if (RenderSVGResourceClipper* clipper = resources->clipper())
repaintRect.intersect(clipper->resourceBoundingBox(renderer));
if (RenderSVGResourceMasker* masker = resources->masker())
repaintRect.intersect(masker->resourceBoundingBox(renderer));
}
bool SVGRenderSupport::filtersForceContainerLayout(RenderObject* object)
{
if (!object->normalChildNeedsLayout())
return false;
SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
if (!resources || !resources->filter())
return false;
return true;
}
bool SVGRenderSupport::pointInClippingArea(RenderObject* object, const FloatPoint& point)
{
ASSERT(object);
SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(object);
if (!resources)
return true;
if (RenderSVGResourceClipper* clipper = resources->clipper())
return clipper->hitTestClipContent(object->objectBoundingBox(), point);
return true;
}
void SVGRenderSupport::applyStrokeStyleToContext(GraphicsContext* context, const RenderStyle* style, const RenderObject* object)
{
ASSERT(context);
ASSERT(style);
ASSERT(object);
ASSERT(object->node());
ASSERT(object->node()->isSVGElement());
const SVGRenderStyle* svgStyle = style->svgStyle();
ASSERT(svgStyle);
SVGLengthContext lengthContext(toSVGElement(object->node()));
context->setStrokeThickness(svgStyle->strokeWidth()->value(lengthContext));
context->setLineCap(svgStyle->capStyle());
context->setLineJoin(svgStyle->joinStyle());
context->setMiterLimit(svgStyle->strokeMiterLimit());
RefPtr<SVGLengthList> dashes = svgStyle->strokeDashArray();
if (dashes->isEmpty())
return;
DashArray dashArray;
SVGLengthList::ConstIterator it = dashes->begin();
SVGLengthList::ConstIterator itEnd = dashes->end();
for (; it != itEnd; ++it)
dashArray.append(it->value(lengthContext));
context->setLineDash(dashArray, svgStyle->strokeDashOffset()->value(lengthContext));
}
void SVGRenderSupport::applyStrokeStyleToStrokeData(StrokeData* strokeData, const RenderStyle* style, const RenderObject* object)
{
ASSERT(strokeData);
ASSERT(style);
ASSERT(object);
ASSERT(object->node());
ASSERT(object->node()->isSVGElement());
const SVGRenderStyle* svgStyle = style->svgStyle();
ASSERT(svgStyle);
SVGLengthContext lengthContext(toSVGElement(object->node()));
strokeData->setThickness(svgStyle->strokeWidth()->value(lengthContext));
strokeData->setLineCap(svgStyle->capStyle());
strokeData->setLineJoin(svgStyle->joinStyle());
strokeData->setMiterLimit(svgStyle->strokeMiterLimit());
RefPtr<SVGLengthList> dashes = svgStyle->strokeDashArray();
if (dashes->isEmpty())
return;
DashArray dashArray;
size_t length = dashes->length();
for (size_t i = 0; i < length; ++i)
dashArray.append(dashes->at(i)->value(lengthContext));
strokeData->setLineDash(dashArray, svgStyle->strokeDashOffset()->value(lengthContext));
}
bool SVGRenderSupport::isRenderableTextNode(const RenderObject* object)
{
ASSERT(object->isText());
return object->isSVGInlineText() && !toRenderSVGInlineText(object)->hasEmptyText();
}
}