KoFilterEffectStack * FilterEffectResource::toFilterStack() const { KoFilterEffectStack * filterStack = new KoFilterEffectStack(); if (!filterStack) return 0; QByteArray data = m_data.toByteArray(); KoXmlDocument doc; doc.setContent(data); KoXmlElement e = doc.documentElement(); // only allow obect bounding box units if (e.hasAttribute("filterUnits") && e.attribute("filterUnits") != "objectBoundingBox") return 0; if (e.attribute("primitiveUnits") != "objectBoundingBox") return 0; // parse filter region rectangle QRectF filterRegion; filterRegion.setX(fromPercentage(e.attribute("x", "-0.1"))); filterRegion.setY(fromPercentage(e.attribute("y", "-0.1"))); filterRegion.setWidth(fromPercentage(e.attribute("width", "1.2"))); filterRegion.setHeight(fromPercentage(e.attribute("height", "1.2"))); filterStack->setClipRect(filterRegion); KoFilterEffectLoadingContext context(QString("")); KoFilterEffectRegistry * registry = KoFilterEffectRegistry::instance(); // create the filter effects and add them to the shape for (KoXmlNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { KoXmlElement primitive = n.toElement(); KoFilterEffect * filterEffect = registry->createFilterEffectFromXml(primitive, context); if (!filterEffect) { qWarning() << "filter effect" << primitive.tagName() << "is not implemented yet"; continue; } // parse subregion qreal x = fromPercentage(primitive.attribute("x", "0")); qreal y = fromPercentage(primitive.attribute("y", "0")); qreal w = fromPercentage(primitive.attribute("width", "1")); qreal h = fromPercentage(primitive.attribute("height", "1")); QRectF subRegion(QPointF(x, y), QSizeF(w, h)); if (primitive.hasAttribute("in")) filterEffect->setInput(0, primitive.attribute("in")); if (primitive.hasAttribute("result")) filterEffect->setOutput(primitive.attribute("result")); filterEffect->setFilterRect(subRegion); filterStack->appendFilterEffect(filterEffect); } return filterStack; }
void SvgParser::applyFilter(KoShape *shape) { SvgGraphicsContext *gc = m_context.currentGC(); if (! gc) return; if (gc->filterId.isEmpty()) return; SvgFilterHelper *filter = findFilter(gc->filterId); if (! filter) return; KoXmlElement content = filter->content(); // parse filter region QRectF bound(shape->position(), shape->size()); // work on bounding box without viewbox tranformation applied // so user space coordinates of bounding box and filter region match up bound = gc->viewboxTransform.inverted().mapRect(bound); QRectF filterRegion(filter->position(bound), filter->size(bound)); // convert filter region to boundingbox units QRectF objectFilterRegion; objectFilterRegion.setTopLeft(SvgUtil::userSpaceToObject(filterRegion.topLeft(), bound)); objectFilterRegion.setSize(SvgUtil::userSpaceToObject(filterRegion.size(), bound)); KoFilterEffectLoadingContext context(m_context.xmlBaseDir()); context.setShapeBoundingBox(bound); // enable units conversion context.enableFilterUnitsConversion(filter->filterUnits() == SvgFilterHelper::UserSpaceOnUse); context.enableFilterPrimitiveUnitsConversion(filter->primitiveUnits() == SvgFilterHelper::UserSpaceOnUse); KoFilterEffectRegistry *registry = KoFilterEffectRegistry::instance(); KoFilterEffectStack *filterStack = 0; QSet<QString> stdInputs; stdInputs << "SourceGraphic" << "SourceAlpha"; stdInputs << "BackgroundImage" << "BackgroundAlpha"; stdInputs << "FillPaint" << "StrokePaint"; QMap<QString, KoFilterEffect*> inputs; // create the filter effects and add them to the shape for (KoXmlNode n = content.firstChild(); !n.isNull(); n = n.nextSibling()) { KoXmlElement primitive = n.toElement(); KoFilterEffect *filterEffect = registry->createFilterEffectFromXml(primitive, context); if (!filterEffect) { debugFlake << "filter effect" << primitive.tagName() << "is not implemented yet"; continue; } const QString input = primitive.attribute("in"); if (!input.isEmpty()) { filterEffect->setInput(0, input); } const QString output = primitive.attribute("result"); if (!output.isEmpty()) { filterEffect->setOutput(output); } QRectF subRegion; // parse subregion if (filter->primitiveUnits() == SvgFilterHelper::UserSpaceOnUse) { const QString xa = primitive.attribute("x"); const QString ya = primitive.attribute("y"); const QString wa = primitive.attribute("width"); const QString ha = primitive.attribute("height"); if (xa.isEmpty() || ya.isEmpty() || wa.isEmpty() || ha.isEmpty()) { bool hasStdInput = false; bool isFirstEffect = filterStack == 0; // check if one of the inputs is a standard input foreach(const QString &input, filterEffect->inputs()) { if ((isFirstEffect && input.isEmpty()) || stdInputs.contains(input)) { hasStdInput = true; break; } } if (hasStdInput || primitive.tagName() == "feImage") { // default to 0%, 0%, 100%, 100% subRegion.setTopLeft(QPointF(0, 0)); subRegion.setSize(QSizeF(1, 1)); } else { // defaults to bounding rect of all referenced nodes foreach(const QString &input, filterEffect->inputs()) { if (!inputs.contains(input)) continue; KoFilterEffect *inputFilter = inputs[input]; if (inputFilter) subRegion |= inputFilter->filterRect(); } } } else { const qreal x = parseUnitX(xa); const qreal y = parseUnitY(ya); const qreal w = parseUnitX(wa); const qreal h = parseUnitY(ha); subRegion.setTopLeft(SvgUtil::userSpaceToObject(QPointF(x, y), bound)); subRegion.setSize(SvgUtil::userSpaceToObject(QSizeF(w, h), bound)); } } else {