...
 
Commits (2)
......@@ -87,3 +87,13 @@ bool KoMeshGradientBackground::loadStyle(KoOdfLoadingContext &context, const QSi
{
return false;
}
SvgMeshGradient* KoMeshGradientBackground::gradient()
{
return d->gradient;
}
QTransform KoMeshGradientBackground::transform()
{
return d->matrix;
}
......@@ -19,6 +19,9 @@ public:
bool loadStyle(KoOdfLoadingContext &context, const QSizeF &shapeSize) override;
SvgMeshGradient* gradient();
QTransform transform();
private:
class Private;
QSharedDataPointer<Private> d;
......
......@@ -184,34 +184,33 @@ QRectF SvgMeshArray::boundingRect() const
{
KIS_ASSERT(numRows() > 0 && numColumns() > 0);
// because meshpatches are adjacent and share sides. If combined, it *should* form a rectangle
qreal width = 0, height = 0;
QPointF start = m_array[0][0]->boundingRect().topLeft();
QPointF topLeft = m_array[0][0]->boundingRect().topLeft();
QPointF bottomRight = m_array.last().last()->boundingRect().bottomRight();
// mesharray may be backwards, in which case we might get the right most value
// but we need topLeft for things to work as expected
for (int i = 0; i < numRows(); ++i) {
for (int j = 0; j < numColumns(); ++j) {
QPointF tmp = m_array[i][j]->boundingRect().topLeft();
if (tmp.x() < start.x()) {
start.rx() = tmp.x();
QPointF left = m_array[i][j]->boundingRect().topLeft();
if (left.x() < topLeft.x()) {
topLeft.rx() = left.x();
}
if ( tmp.y() < start.y()) {
start.ry() = tmp.y();
if ( left.y() < topLeft.y()) {
topLeft.ry() = left.y();
}
}
}
for (int row = 0; row < numRows(); ++row) {
height += m_array[row][0]->boundingRect().height();
}
for (int col = 0; col < numColumns(); ++col) {
width += m_array[0][col]->boundingRect().width();
QPointF right = m_array[i][j]->boundingRect().bottomRight();
if (bottomRight.x() < right.x()) {
bottomRight.rx() = right.x();
}
if (bottomRight.y() < right.y()) {
bottomRight.ry() = right.y();
}
}
}
return QRectF(start, QSizeF(width, height));
// return extremas
return QRectF(topLeft, bottomRight);
}
QColor SvgMeshArray::getColor(SvgMeshPatch::Type edge, int row, int col) const
{
......
......@@ -39,10 +39,12 @@
#include <KoShape.h>
#include <KoPathShape.h>
#include <KoPathSegment.h>
#include <KoFilterEffect.h>
#include <KoFilterEffectStack.h>
#include <KoColorBackground.h>
#include <KoGradientBackground.h>
#include <KoMeshGradientBackground.h>
#include <KoPatternBackground.h>
#include <KoVectorPatternBackground.h>
#include <KoShapeStroke.h>
......@@ -102,6 +104,11 @@ void SvgStyleWriter::saveSvgFill(KoShape *shape, SvgSavingContext &context)
QString gradientId = saveSvgGradient(gbg->gradient(), gbg->transform(), context);
context.shapeWriter().addAttribute("fill", "url(#" + gradientId + ")");
}
QSharedPointer<KoMeshGradientBackground> mgbg = qSharedPointerDynamicCast<KoMeshGradientBackground>(shape->background());
if (mgbg) {
QString gradientId = saveSvgMeshGradient(mgbg->gradient(), mgbg->transform(), context);
context.shapeWriter().addAttribute("fill", "url(#" + gradientId + ")");
}
QSharedPointer<KoPatternBackground> pbg = qSharedPointerDynamicCast<KoPatternBackground>(shape->background());
if (pbg) {
const QString patternId = saveSvgPattern(pbg, shape, context);
......@@ -405,6 +412,93 @@ QString SvgStyleWriter::saveSvgGradient(const QGradient *gradient, const QTransf
return uid;
}
QString SvgStyleWriter::saveSvgMeshGradient(SvgMeshGradient *gradient,
const QTransform& transform,
SvgSavingContext &context)
{
if (!gradient && gradient->isValid())
return QString();
const QString uid = context.createUID("meshgradient");
context.styleWriter().startElement("meshgradient");
context.styleWriter().addAttribute("id", uid);
// TODO: we convert obb to userSpaceOnUse, while reading
context.styleWriter().addAttribute("gradientUnits", "userSpaceOnUse");
SvgUtil::writeTransformAttributeLazy("transform", transform, context.styleWriter());
SvgMeshArray *mesharray = gradient->getMeshArray().data();
QPointF start = mesharray->getPatch(0, 0)->getStop(SvgMeshPatch::Top).point;
context.styleWriter().addAttribute("x", start.x());
context.styleWriter().addAttribute("y", start.y());
if (gradient->type() == SvgMeshGradient::BILINEAR) {
context.styleWriter().addAttribute("type", "bilinear");
} else {
context.styleWriter().addAttribute("type", "bicubic");
}
for (int row = 0; row < mesharray->numRows(); ++row) {
const QString uid = context.createUID("meshrow");
context.styleWriter().startElement("meshrow");
context.styleWriter().addAttribute("id", uid);
for (int col = 0; col < mesharray->numColumns(); ++col) {
const QString uid = context.createUID("meshpatch");
context.styleWriter().startElement("meshpatch");
context.styleWriter().addAttribute("id", uid);
SvgMeshPatch *patch = mesharray->getPatch(row, col);
for (int s = 1; s <= 4; ++s) {
SvgMeshPatch::Type type = static_cast<SvgMeshPatch::Type> (s);
// only first row and first col have Top and Left stop, respectively
if ((row != 0 && s == SvgMeshPatch::Top) ||
(col != 0 && s == SvgMeshPatch::Left)) {
continue;
}
context.styleWriter().startElement("stop");
QList<QPointF> segment = patch->getPathSegment(type).controlPoints();
QString pathstr;
QTextStream stream(&pathstr);
// TODO: other path type?
stream << "C "
<< segment[1].x() << "," << segment[1].y() << " "
<< segment[2].x() << "," << segment[2].y() << " "
<< segment[3].x() << "," << segment[3].y(); // I don't see any harm, inkscape does this too
context.styleWriter().addAttribute("path", pathstr);
// don't add color/opacity if stop is in first row and stop == Top (or)
// don't add color/opacity if stop is not in first row and stop == Right
if ((row != 0 || col == 0 || s != SvgMeshPatch::Top) &&
(row == 0 || s != SvgMeshPatch::Right)) {
SvgMeshStop stop = patch->getStop(type);
context.styleWriter().addAttribute("stop-color", stop.color.name());
context.styleWriter().addAttribute("stop-opacity", stop.color.alphaF());
}
context.styleWriter().endElement(); // stop
}
context.styleWriter().endElement(); // meshpatch
}
context.styleWriter().endElement(); // meshrow
}
context.styleWriter().endElement(); // meshgradient
return uid;
}
QString SvgStyleWriter::saveSvgPattern(QSharedPointer<KoPatternBackground> pattern, KoShape *shape, SvgSavingContext &context)
{
const QString uid = context.createUID("pattern");
......
......@@ -39,6 +39,7 @@ class KoPatternBackground;
class KoVectorPatternBackground;
class QTransform;
class QGradient;
class SvgMeshGradient;
/// Helper class to save svg styles
class KRITAFLAKE_EXPORT SvgStyleWriter
......@@ -68,6 +69,7 @@ protected:
static void saveSvgColorStops(const QGradientStops &colorStops, SvgSavingContext &context);
/// Saves gradient
static QString saveSvgGradient(const QGradient *gradient, const QTransform &gradientTransform, SvgSavingContext &context);
static QString saveSvgMeshGradient(SvgMeshGradient* gradient, const QTransform &transform, SvgSavingContext &context);
/// Saves pattern
static QString saveSvgPattern(QSharedPointer<KoPatternBackground> pattern, KoShape *shape, SvgSavingContext &context);
static QString saveSvgVectorPattern(QSharedPointer<KoVectorPatternBackground> pattern, KoShape *shape, SvgSavingContext &context);
......