SVGPatternElement.cpp [plain text]
#include "config.h"
#if SVG_SUPPORT
#include "SVGPatternElement.h"
#include "Attr.h"
#include "Document.h"
#include "GraphicsContext.h"
#include "RenderSVGContainer.h"
#include "KCanvasCreator.h"
#include "KCanvasImage.h"
#include "KCanvasMatrix.h"
#include "KCanvasRenderingStyle.h"
#include "KRenderingDevice.h"
#include "KRenderingPaintServerPattern.h"
#include "SVGAnimatedEnumeration.h"
#include "SVGAnimatedLength.h"
#include "SVGAnimatedString.h"
#include "SVGAnimatedTransformList.h"
#include "SVGDOMImplementation.h"
#include "SVGHelper.h"
#include "SVGMatrix.h"
#include "SVGNames.h"
#include "SVGSVGElement.h"
#include "SVGTransformList.h"
#include "SVGTransformable.h"
#include "ksvg.h"
#include <wtf/OwnPtr.h>
namespace WebCore {
SVGPatternElement::SVGPatternElement(const QualifiedName& tagName, Document *doc) : SVGStyledLocatableElement(tagName, doc), SVGURIReference(), SVGTests(), SVGLangSpace(), SVGExternalResourcesRequired(), SVGFitToViewBox(), KCanvasResourceListener()
{
m_tile = 0;
m_paintServer = 0;
m_ignoreAttributeChanges = false;
}
SVGPatternElement::~SVGPatternElement()
{
delete m_paintServer;
}
SVGAnimatedEnumeration *SVGPatternElement::patternUnits() const
{
if (!m_patternUnits) {
lazy_create<SVGAnimatedEnumeration>(m_patternUnits, this);
m_patternUnits->setBaseVal(SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
}
return m_patternUnits.get();
}
SVGAnimatedEnumeration *SVGPatternElement::patternContentUnits() const
{
if (!m_patternContentUnits) {
lazy_create<SVGAnimatedEnumeration>(m_patternContentUnits, this);
m_patternContentUnits->setBaseVal(SVG_UNIT_TYPE_USERSPACEONUSE);
}
return m_patternContentUnits.get();
}
SVGAnimatedLength* SVGPatternElement::x() const
{
return lazy_create<SVGAnimatedLength>(m_x, this, LM_WIDTH, viewportElement());
}
SVGAnimatedLength* SVGPatternElement::y() const
{
return lazy_create<SVGAnimatedLength>(m_y, this, LM_HEIGHT, viewportElement());
}
SVGAnimatedLength* SVGPatternElement::width() const
{
return lazy_create<SVGAnimatedLength>(m_width, this, LM_WIDTH, viewportElement());
}
SVGAnimatedLength* SVGPatternElement::height() const
{
return lazy_create<SVGAnimatedLength>(m_height, this, LM_HEIGHT, viewportElement());
}
SVGAnimatedTransformList *SVGPatternElement::patternTransform() const
{
return lazy_create<SVGAnimatedTransformList>(m_patternTransform, this);
}
void SVGPatternElement::parseMappedAttribute(MappedAttribute *attr)
{
const AtomicString &value = attr->value();
if (attr->name() == SVGNames::patternUnitsAttr) {
if (value == "userSpaceOnUse")
patternUnits()->setBaseVal(SVG_UNIT_TYPE_USERSPACEONUSE);
else if (value == "objectBoundingBox")
patternUnits()->setBaseVal(SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
} else if (attr->name() == SVGNames::patternContentUnitsAttr) {
if (value == "userSpaceOnUse")
patternContentUnits()->setBaseVal(SVG_UNIT_TYPE_USERSPACEONUSE);
else if (value == "objectBoundingBox")
patternContentUnits()->setBaseVal(SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
} else if (attr->name() == SVGNames::patternTransformAttr) {
SVGTransformList *patternTransforms = patternTransform()->baseVal();
SVGTransformable::parseTransformAttribute(patternTransforms, value);
} else if (attr->name() == SVGNames::xAttr)
x()->baseVal()->setValueAsString(value.impl());
else if (attr->name() == SVGNames::yAttr)
y()->baseVal()->setValueAsString(value.impl());
else if (attr->name() == SVGNames::widthAttr)
width()->baseVal()->setValueAsString(value.impl());
else if (attr->name() == SVGNames::heightAttr)
height()->baseVal()->setValueAsString(value.impl());
else {
if (SVGURIReference::parseMappedAttribute(attr))
return;
if (SVGTests::parseMappedAttribute(attr))
return;
if (SVGLangSpace::parseMappedAttribute(attr))
return;
if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
return;
if (SVGFitToViewBox::parseMappedAttribute(attr))
return;
SVGStyledElement::parseMappedAttribute(attr);
}
}
const SVGStyledElement* SVGPatternElement::pushAttributeContext(const SVGStyledElement* context)
{
const SVGStyledElement* restore = x()->baseVal()->context();
x()->baseVal()->setContext(context);
y()->baseVal()->setContext(context);
width()->baseVal()->setContext(context);
height()->baseVal()->setContext(context);
return restore;
}
void SVGPatternElement::resourceNotification() const
{
notifyAttributeChange();
}
void SVGPatternElement::fillAttributesFromReferencePattern(const SVGPatternElement* target, KCanvasMatrix& patternTransformMatrix) const
{
DeprecatedString ref = String(href()->baseVal()).deprecatedString();
KRenderingPaintServer *refServer = getPaintServerById(document(), ref.mid(1));
if (!refServer || refServer->type() != PS_PATTERN)
return;
KRenderingPaintServerPattern *refPattern = static_cast<KRenderingPaintServerPattern *>(refServer);
if (!hasAttribute(SVGNames::patternUnitsAttr)) {
const AtomicString& value = target->getAttribute(SVGNames::patternUnitsAttr);
if (value == "userSpaceOnUse")
patternUnits()->setBaseVal(SVG_UNIT_TYPE_USERSPACEONUSE);
else if (value == "objectBoundingBox")
patternUnits()->setBaseVal(SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
}
if (!hasAttribute(SVGNames::patternContentUnitsAttr)) {
const AtomicString& value = target->getAttribute(SVGNames::patternContentUnitsAttr);
if (value == "userSpaceOnUse")
patternContentUnits()->setBaseVal(SVG_UNIT_TYPE_USERSPACEONUSE);
else if (value == "objectBoundingBox")
patternContentUnits()->setBaseVal(SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
}
if (!hasAttribute(SVGNames::patternTransformAttr))
patternTransformMatrix = refPattern->patternTransform();
}
void SVGPatternElement::drawPatternContentIntoTile(const SVGPatternElement* target, const IntSize& newSize, KCanvasMatrix patternTransformMatrix) const
{
KRenderingDevice* device = renderingDevice();
SVGStyledElement* activeElement = static_cast<SVGStyledElement*>(m_paintServer->activeClient()->element());
bool bbox = (patternUnits()->baseVal() == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
const SVGStyledElement* savedContext = 0;
if (bbox) {
if (width()->baseVal()->unitType() != SVGLength::SVG_LENGTHTYPE_PERCENTAGE)
width()->baseVal()->newValueSpecifiedUnits(SVGLength::SVG_LENGTHTYPE_PERCENTAGE, width()->baseVal()->value() * 100.);
if (height()->baseVal()->unitType() != SVGLength::SVG_LENGTHTYPE_PERCENTAGE)
height()->baseVal()->newValueSpecifiedUnits(SVGLength::SVG_LENGTHTYPE_PERCENTAGE, height()->baseVal()->value() * 100.);
if (activeElement)
savedContext = const_cast<SVGPatternElement* >(this)->pushAttributeContext(activeElement);
}
delete m_tile;
m_tile = static_cast<KCanvasImage*>(device->createResource(RS_IMAGE));
m_tile->init(newSize);
KRenderingDeviceContext* patternContext = device->contextForImage(m_tile);
device->pushContext(patternContext);
FloatRect rect(x()->baseVal()->value(), y()->baseVal()->value(), width()->baseVal()->value(), height()->baseVal()->value());
m_paintServer->setBbox(rect);
m_paintServer->setPatternTransform(patternTransformMatrix);
m_paintServer->setTile(m_tile);
OwnPtr<GraphicsContext> context(patternContext->createGraphicsContext());
for(Node *n = target->firstChild(); n != 0; n = n->nextSibling())
{
SVGElement* elem = svg_dynamic_cast(n);
if (!elem || !elem->isStyled())
continue;
SVGStyledElement* e = static_cast<SVGStyledElement* >(elem);
RenderObject *item = e->renderer();
if (!item)
continue;
#if 0
KCanvasMatrix savedMatrix = item->localTransform();
const SVGStyledElement* savedContext = 0;
if (patternContentUnits()->baseVal() == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
{
if (activeElement)
savedContext = e->pushAttributeContext(activeElement);
}
if (viewportElement() && viewportElement()->hasTagName(SVGNames::svgTag))
{
SVGSVGElement* svgElement = static_cast<SVGSVGElement* >(viewportElement());
RefPtr<SVGMatrix> svgCTM = svgElement->getCTM();
RefPtr<SVGMatrix> ctm = getCTM();
KCanvasMatrix newMatrix(svgCTM->matrix());
newMatrix.multiply(savedMatrix);
newMatrix.scale(1.0 / ctm->a(), 1.0 / ctm->d());
item->setLocalTransform(newMatrix.matrix());
}
#endif
RenderObject::PaintInfo info(context.get(), IntRect(), PaintPhaseForeground, 0, 0, 0);
item->paint(info, 0, 0);
#if 0
if (savedContext)
e->pushAttributeContext(savedContext);
item->setLocalTransform(savedMatrix.matrix());
#endif
}
if (savedContext)
const_cast<SVGPatternElement* >(this)->pushAttributeContext(savedContext);
device->popContext();
delete patternContext;
}
void SVGPatternElement::notifyClientsToRepaint() const
{
const KCanvasItemList &clients = m_paintServer->clients();
KCanvasItemList::ConstIterator it = clients.begin();
KCanvasItemList::ConstIterator end = clients.end();
for(; it != end; ++it)
{
const RenderPath *current = (*it);
SVGStyledElement* styled = (current ? static_cast<SVGStyledElement* >(current->element()) : 0);
if (styled)
{
styled->setChanged(true);
if (styled->renderer())
styled->renderer()->repaint();
}
}
}
void SVGPatternElement::notifyAttributeChange() const
{
if (!m_paintServer || !m_paintServer->activeClient() || m_ignoreAttributeChanges)
return;
IntSize newSize = IntSize(lroundf(width()->baseVal()->value()), lroundf(height()->baseVal()->value()));
if (m_tile && (m_tile->size() == newSize) || newSize.width() < 1 || newSize.height() < 1)
return;
m_ignoreAttributeChanges = true;
const SVGPatternElement* target = this;
const Node *test = this;
while(test && !test->hasChildNodes())
{
DeprecatedString ref = String(target->href()->baseVal()).deprecatedString();
test = ownerDocument()->getElementById(String(ref.mid(1)).impl());
if (test && test->hasTagName(SVGNames::patternTag))
target = static_cast<const SVGPatternElement* >(test);
}
unsigned short savedPatternUnits = patternUnits()->baseVal();
unsigned short savedPatternContentUnits = patternContentUnits()->baseVal();
KCanvasMatrix patternTransformMatrix;
if (patternTransform()->baseVal()->numberOfItems() > 0)
patternTransformMatrix = KCanvasMatrix(patternTransform()->baseVal()->consolidate()->matrix()->matrix());
fillAttributesFromReferencePattern(target, patternTransformMatrix);
drawPatternContentIntoTile(target, newSize, patternTransformMatrix);
patternUnits()->setBaseVal(savedPatternUnits);
patternContentUnits()->setBaseVal(savedPatternContentUnits);
notifyClientsToRepaint();
m_ignoreAttributeChanges = false;
}
RenderObject* SVGPatternElement::createRenderer(RenderArena* arena, RenderStyle*)
{
RenderSVGContainer* patternContainer = new (arena) RenderSVGContainer(this);
patternContainer->setDrawsContents(false);
return patternContainer;
}
KRenderingPaintServerPattern *SVGPatternElement::canvasResource()
{
if (!m_paintServer) {
KRenderingPaintServer *pserver = renderingDevice()->createPaintServer(KCPaintServerType(PS_PATTERN));
m_paintServer = static_cast<KRenderingPaintServerPattern *>(pserver);
m_paintServer->setListener(const_cast<SVGPatternElement* >(this));
}
return m_paintServer;
}
SVGMatrix *SVGPatternElement::getCTM() const
{
SVGMatrix *mat = SVGSVGElement::createSVGMatrix();
if (mat)
{
RefPtr<SVGMatrix> viewBox = viewBoxToViewTransform(width()->baseVal()->value(),
height()->baseVal()->value());
mat->multiply(viewBox.get());
}
return mat;
}
}
#endif // SVG_SUPPORT