blob: f558919acbc9ec89d6ea3e3fe2747b5255bdc2d7 [file] [log] [blame]
/*
Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org>
2004, 2005 Rob Buis <buis@kde.org>
This file is part of the KDE project
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <kdom/core/AttrImpl.h>
#include <kcanvas/KCanvas.h>
#include <kcanvas/KCanvasMatrix.h>
#include <kcanvas/KCanvasRegistry.h>
#include <kcanvas/KCanvasContainer.h>
#include <kcanvas/KCanvasCreator.h>
#include <kcanvas/KCanvasImage.h>
#include <kcanvas/device/KRenderingDevice.h>
#include <kcanvas/device/KRenderingPaintServerPattern.h>
#include "ksvg.h"
#include "svgattrs.h"
#include "SVGHelper.h"
#include "SVGMatrixImpl.h"
#include "SVGDocumentImpl.h"
#include "SVGSVGElementImpl.h"
#include "SVGTransformableImpl.h"
#include "SVGTransformListImpl.h"
#include "KCanvasRenderingStyle.h"
#include "SVGPatternElementImpl.h"
#include "SVGAnimatedStringImpl.h"
#include "SVGAnimatedLengthImpl.h"
#include "SVGDOMImplementationImpl.h"
#include "SVGAnimatedEnumerationImpl.h"
#include "SVGAnimatedTransformListImpl.h"
using namespace KSVG;
SVGPatternElementImpl::SVGPatternElementImpl(KDOM::DocumentPtr *doc, KDOM::NodeImpl::Id id, KDOM::DOMStringImpl *prefix) : SVGStyledElementImpl(doc, id, prefix), SVGURIReferenceImpl(), SVGTestsImpl(), SVGLangSpaceImpl(), SVGExternalResourcesRequiredImpl(), SVGFitToViewBoxImpl(), KCanvasResourceListener()
{
m_patternUnits = 0;
m_patternTransform = 0;
m_patternContentUnits = 0;
m_x = m_y = m_width = m_height = 0;
m_tile = 0;
m_paintServer = 0;
m_ignoreAttributeChanges = false;
}
SVGPatternElementImpl::~SVGPatternElementImpl()
{
if(m_x)
m_x->deref();
if(m_y)
m_y->deref();
if(m_width)
m_width->deref();
if(m_height)
m_height->deref();
if(m_patternUnits)
m_patternUnits->deref();
if(m_patternContentUnits)
m_patternContentUnits->deref();
if(m_patternTransform)
m_patternTransform->deref();
if (m_canvasItem)
delete m_canvasItem;
}
SVGAnimatedEnumerationImpl *SVGPatternElementImpl::patternUnits() const
{
if(!m_patternUnits)
{
lazy_create<SVGAnimatedEnumerationImpl>(m_patternUnits, this);
m_patternUnits->setBaseVal(SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
}
return m_patternUnits;
}
SVGAnimatedEnumerationImpl *SVGPatternElementImpl::patternContentUnits() const
{
if(!m_patternContentUnits)
{
lazy_create<SVGAnimatedEnumerationImpl>(m_patternContentUnits, this);
m_patternContentUnits->setBaseVal(SVG_UNIT_TYPE_USERSPACEONUSE);
}
return m_patternContentUnits;
}
SVGAnimatedLengthImpl *SVGPatternElementImpl::x() const
{
return lazy_create<SVGAnimatedLengthImpl>(m_x, this, LM_WIDTH, viewportElement());
}
SVGAnimatedLengthImpl *SVGPatternElementImpl::y() const
{
return lazy_create<SVGAnimatedLengthImpl>(m_y, this, LM_HEIGHT, viewportElement());
}
SVGAnimatedLengthImpl *SVGPatternElementImpl::width() const
{
return lazy_create<SVGAnimatedLengthImpl>(m_width, this, LM_WIDTH, viewportElement());
}
SVGAnimatedLengthImpl *SVGPatternElementImpl::height() const
{
return lazy_create<SVGAnimatedLengthImpl>(m_height, this, LM_HEIGHT, viewportElement());
}
SVGAnimatedTransformListImpl *SVGPatternElementImpl::patternTransform() const
{
return lazy_create<SVGAnimatedTransformListImpl>(m_patternTransform, this);
}
void SVGPatternElementImpl::parseAttribute(KDOM::AttributeImpl *attr)
{
int id = (attr->id() & NodeImpl_IdLocalMask);
KDOM::DOMString value(attr->value());
switch(id)
{
case ATTR_PATTERNUNITS:
{
if(value == "userSpaceOnUse")
patternUnits()->setBaseVal(SVG_UNIT_TYPE_USERSPACEONUSE);
else if(value == "objectBoundingBox")
patternUnits()->setBaseVal(SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
break;
}
case ATTR_PATTERNCONTENTUNITS:
{
if(value == "userSpaceOnUse")
patternContentUnits()->setBaseVal(SVG_UNIT_TYPE_USERSPACEONUSE);
else if(value == "objectBoundingBox")
patternContentUnits()->setBaseVal(SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
break;
}
case ATTR_PATTERNTRANSFORM:
{
SVGTransformListImpl *patternTransforms = patternTransform()->baseVal();
SVGTransformableImpl::parseTransformAttribute(patternTransforms, attr->value());
break;
}
case ATTR_X:
{
x()->baseVal()->setValueAsString(value.handle());
break;
}
case ATTR_Y:
{
y()->baseVal()->setValueAsString(value.handle());
break;
}
case ATTR_WIDTH:
{
width()->baseVal()->setValueAsString(value.handle());
break;
}
case ATTR_HEIGHT:
{
height()->baseVal()->setValueAsString(value.handle());
break;
}
default:
{
if(SVGURIReferenceImpl::parseAttribute(attr)) return;
if(SVGTestsImpl::parseAttribute(attr)) return;
if(SVGLangSpaceImpl::parseAttribute(attr)) return;
if(SVGExternalResourcesRequiredImpl::parseAttribute(attr)) return;
if(SVGFitToViewBoxImpl::parseAttribute(attr)) return;
SVGStyledElementImpl::parseAttribute(attr);
}
};
}
const SVGStyledElementImpl *SVGPatternElementImpl::pushAttributeContext(const SVGStyledElementImpl *context)
{
// All attribute's contexts are equal (so just take the one from 'x').
const SVGStyledElementImpl *restore = x()->baseVal()->context();
x()->baseVal()->setContext(context);
y()->baseVal()->setContext(context);
width()->baseVal()->setContext(context);
height()->baseVal()->setContext(context);
return restore;
}
void SVGPatternElementImpl::resourceNotification() const
{
// We're referenced by a "client", calculate the tile now...
notifyAttributeChange();
}
void SVGPatternElementImpl::notifyAttributeChange() const
{
if(!m_paintServer || !m_paintServer->activeClient())
return;
if(m_ignoreAttributeChanges)
return;
float w = width()->baseVal()->value();
float h = height()->baseVal()->value();
QSize newSize = QSize(qRound(w), qRound(h));
if(m_tile && (m_tile->size() == newSize))
return;
m_ignoreAttributeChanges = true;
// Find first pattern def that has children
const KDOM::ElementImpl *target = this;
const KDOM::NodeImpl *test = this;
while(test && !test->hasChildNodes())
{
QString ref = KDOM::DOMString(href()->baseVal()).string();
test = ownerDocument()->getElementById(KDOM::DOMString(ref.mid(1)).handle());
if(test && test->id() == ID_PATTERN)
target = static_cast<const KDOM::ElementImpl *>(test);
}
unsigned short savedPatternUnits = patternUnits()->baseVal();
unsigned short savedPatternContentUnits = patternContentUnits()->baseVal();
KRenderingPaintServer *refServer = 0;
QString ref = KDOM::DOMString(href()->baseVal()).string();
if(!ref.isEmpty())
{
SVGDocumentImpl *document = static_cast<SVGDocumentImpl *>(ownerDocument());
KCanvas *canvas = (document ? document->canvas() : 0);
Q_ASSERT(canvas != 0);
refServer = canvas->registry()->getPaintServerById(ref.mid(1));
}
KCanvasMatrix patternTransformMatrix;
if(patternTransform()->baseVal()->numberOfItems() > 0)
patternTransformMatrix = KCanvasMatrix(patternTransform()->baseVal()->consolidate()->matrix()->qmatrix());
if(refServer && refServer->type() == PS_PATTERN)
{
KRenderingPaintServerPattern *refPattern = static_cast<KRenderingPaintServerPattern *>(refServer);
if(!hasAttribute(KDOM::DOMString("patternUnits").handle()))
{
KDOM::DOMString value(target->getAttribute(KDOM::DOMString("patternUnits").handle()));
if(value == "userSpaceOnUse")
patternUnits()->setBaseVal(SVG_UNIT_TYPE_USERSPACEONUSE);
else if(value == "objectBoundingBox")
patternUnits()->setBaseVal(SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
}
if(!hasAttribute(KDOM::DOMString("patternContentUnits").handle()))
{
KDOM::DOMString value(target->getAttribute(KDOM::DOMString("patternContentUnits").handle()));
if(value == "userSpaceOnUse")
patternContentUnits()->setBaseVal(SVG_UNIT_TYPE_USERSPACEONUSE);
else if(value == "objectBoundingBox")
patternContentUnits()->setBaseVal(SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
}
if(!hasAttribute(KDOM::DOMString("patternTransform").handle()))
patternTransformMatrix = refPattern->patternTransform();
}
SVGStyledElementImpl *activeElement = static_cast<SVGStyledElementImpl *>(m_paintServer->activeClient()->userData());
bool bbox = (patternUnits()->baseVal() == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
const SVGStyledElementImpl *savedContext = 0;
if(bbox)
{
if(width()->baseVal()->unitType() != SVG_LENGTHTYPE_PERCENTAGE)
width()->baseVal()->newValueSpecifiedUnits(SVG_LENGTHTYPE_PERCENTAGE, width()->baseVal()->value() * 100.);
if(height()->baseVal()->unitType() != SVG_LENGTHTYPE_PERCENTAGE)
height()->baseVal()->newValueSpecifiedUnits(SVG_LENGTHTYPE_PERCENTAGE, height()->baseVal()->value() * 100.);
if(activeElement)
savedContext = const_cast<SVGPatternElementImpl *>(this)->pushAttributeContext(activeElement);
}
delete m_tile;
m_tile = static_cast<KCanvasImage *>(canvas()->renderingDevice()->createResource(RS_IMAGE));
m_tile->init(newSize);
KRenderingDeviceContext *patternContext = canvas()->renderingDevice()->contextForImage(m_tile);
canvas()->renderingDevice()->pushContext(patternContext);
// KCanvasCommonArgs args;
// args.setCanvas(canvas());
// args.setStyle(item->style());
KRenderingPaintServerPattern *pattern = static_cast<KRenderingPaintServerPattern *>(m_paintServer);
pattern->setX(x()->baseVal()->value());
pattern->setY(y()->baseVal()->value());
pattern->setWidth(width()->baseVal()->value());
pattern->setHeight(height()->baseVal()->value());
pattern->setPatternTransform(patternTransformMatrix);
pattern->setTile(m_tile);
for(KDOM::NodeImpl *n = target->firstChild(); n != 0; n = n->nextSibling())
{
SVGStyledElementImpl *e = dynamic_cast<SVGStyledElementImpl *>(n);
KCanvasItem *item = (e ? e->canvasItem() : 0);
if(item && item->style())
{
KCanvasMatrix savedMatrix = item->style()->objectMatrix();
const SVGStyledElementImpl *savedContext = 0;
if(patternContentUnits()->baseVal() == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
{
if(activeElement)
savedContext = e->pushAttributeContext(activeElement);
}
// Take into account viewportElement's viewBox, if existant...
if(viewportElement() && viewportElement()->id() == ID_SVG)
{
SVGSVGElementImpl *svgElement = static_cast<SVGSVGElementImpl *>(viewportElement());
SVGMatrixImpl *svgCTM = svgElement->getCTM();
svgCTM->ref();
SVGMatrixImpl *ctm = SVGLocatableImpl::getCTM();
ctm->ref();
KCanvasMatrix newMatrix(svgCTM->qmatrix());
newMatrix.multiply(savedMatrix);
newMatrix.scale(1.0 / ctm->a(), 1.0 / ctm->d());
item->style()->setObjectMatrix(newMatrix);
ctm->deref();
svgCTM->deref();
}
item->draw(QRect());
if(savedContext)
e->pushAttributeContext(savedContext);
item->style()->setObjectMatrix(savedMatrix);
}
}
if(savedContext)
const_cast<SVGPatternElementImpl *>(this)->pushAttributeContext(savedContext);
canvas()->renderingDevice()->popContext();
delete patternContext;
patternUnits()->setBaseVal(savedPatternUnits);
patternContentUnits()->setBaseVal(savedPatternContentUnits);
// Update all users of this resource...
const KCanvasItemList &clients = pattern->clients();
KCanvasItemList::ConstIterator it = clients.begin();
KCanvasItemList::ConstIterator end = clients.end();
for(; it != end; ++it)
{
const KCanvasItem *current = (*it);
SVGStyledElementImpl *styled = (current ? static_cast<SVGStyledElementImpl *>(current->userData()) : 0);
if(styled)
{
styled->setChanged(true);
if(styled->canvasItem())
styled->canvasItem()->invalidate();
}
}
m_ignoreAttributeChanges = false;
}
KCanvasItem *SVGPatternElementImpl::createCanvasItem(KCanvas *canvas, KRenderingStyle *) const
{
m_paintServer = canvas->renderingDevice()->createPaintServer(KCPaintServerType(PS_PATTERN));
KRenderingPaintServerPattern *pserver = static_cast<KRenderingPaintServerPattern *>(m_paintServer);
pserver->setListener(const_cast<SVGPatternElementImpl *>(this));
canvas->registry()->addPaintServerById(KDOM::DOMString(getId()).string(), pserver);
return 0;
}
SVGMatrixImpl *SVGPatternElementImpl::getCTM() const
{
SVGMatrixImpl *mat = SVGSVGElementImpl::createSVGMatrix();
if(mat)
{
SVGMatrixImpl *viewBox = viewBoxToViewTransform(width()->baseVal()->value(),
height()->baseVal()->value());
viewBox->ref();
mat->multiply(viewBox);
viewBox->deref();
}
return mat;
}
// vim:ts=4:noet