...
 
Commits (4)
#include "KoMeshGradientBackground.h"
#include <KoColorSpaceRegistry.h>
#include <KoMixColorsOp.h>
#include <QRegion>
#include <QPainter>
#include <QPainterPath>
#include <QDebug>
......@@ -31,41 +35,72 @@ KoMeshGradientBackground::~KoMeshGradientBackground()
}
void KoMeshGradientBackground::paint(QPainter &painter,
KoShapePaintingContext &context,
const QPainterPath &fillPath) const
KoShapePaintingContext &,
const QPainterPath &) const
{
if (!d->gradient || !d->gradient->isValid()) return;
for (int row = 0; row < d->gradient->getMeshArray()->numRows(); ++row) {
for (int col = 0; col < d->gradient->getMeshArray()->numColumns(); ++col) {
SvgMeshPatch *patch = d->gradient->getMeshArray()->getPatch(row, col);
fillPatch(painter, patch, 0);
fillPatch(painter, patch);
}
}
}
void KoMeshGradientBackground::fillPatch(QPainter &painter, const SvgMeshPatch *patch, int i) const
void KoMeshGradientBackground::fillPatch(QPainter &painter, const SvgMeshPatch *patch) const
{
// TODO check for color variation
if (i <= 5) {
QRegion clipRegion = painter.clipRegion();
KoPathShape *patchPath = patch->getPath();
// if patch is outside the bounding box of the clipped region, don't render
if (!clipRegion.contains(patchPath->boundingRect().toRect()))
return;
QColor color0 = patch->getStop(SvgMeshPatch::Top).color;
QColor color1 = patch->getStop(SvgMeshPatch::Right).color;
QColor color2 = patch->getStop(SvgMeshPatch::Bottom).color;
QColor color3 = patch->getStop(SvgMeshPatch::Left).color;
const KoColorSpace* cs = KoColorSpaceRegistry::instance()->rgb8();
quint8 c[4][4];
cs->fromQColor(color0, c[0]);
cs->fromQColor(color1, c[1]);
cs->fromQColor(color2, c[2]);
cs->fromQColor(color3, c[3]);
const quint8 threshold = 0;
// check if color variation is acceptable and patch size is less than ~pixel width/heigh
if ((cs->difference(c[0], c[1]) > threshold || cs->difference(c[1], c[2]) > threshold ||
cs->difference(c[2], c[3]) > threshold || cs->difference(c[3], c[0]) > threshold) &&
patch->size().width() > 1 && patch->size().height() > 1) {
QVector<SvgMeshPatch*> patches;
patch->subdivide(patches);
++i;
for (const auto& p: patches) {
fillPatch(painter, p, i);
fillPatch(painter, p);
}
for (auto& p: patches) {
delete p;
}
} else {
QPen pen(patch->getStop(SvgMeshPatch::Bottom).color);
quint8 mixed[4];
cs->mixColorsOp()->mixColors(c[0], 4, mixed);
QColor average;
cs->toQColor(mixed, &average);
QPen pen(average);
painter.setPen(pen);
painter.drawPath(patch->getPath()->outline());
QBrush brush(patch->getStop(SvgMeshPatch::Bottom).color);
painter.fillPath(patch->getPath()->outline(), brush);
painter.drawPath(patchPath->outline());
QBrush brush(average);
painter.fillPath(patchPath->outline(), brush);
}
}
......
......@@ -13,7 +13,7 @@ public:
void paint(QPainter &painter, KoShapePaintingContext &context, const QPainterPath &fillPath) const override;
void fillPatch(QPainter &painter, const SvgMeshPatch *patch, int i) const;
void fillPatch(QPainter &painter, const SvgMeshPatch *patch) const;
bool compareTo(const KoShapeBackground *other) const override;
......
......@@ -171,6 +171,14 @@ int SvgMeshArray::numColumns() const
return m_array.first().size();
}
void SvgMeshArray::setTransform(const QTransform& matrix)
{
for (auto& row: m_array) {
for (auto& patch: row) {
patch->setTransform(matrix);
}
}
}
QColor SvgMeshArray::getColor(SvgMeshPatch::Type edge, int row, int col) const
{
return getStop(edge, row, col).color;
......
......@@ -46,6 +46,8 @@ public:
int numRows() const;
int numColumns() const;
void setTransform(const QTransform& matrix);
// get color of a stop
QColor getColor(SvgMeshPatch::Type edge, int row, int col) const;
......
......@@ -38,6 +38,11 @@ SvgMeshGradient::Type SvgMeshGradient::type() const
return m_type;
}
void SvgMeshGradient::setTransform(const QTransform& matrix)
{
m_mesharray->setTransform(matrix);
}
bool SvgMeshGradient::isValid() const
{
return m_mesharray->numRows() != 0;
......
......@@ -37,6 +37,7 @@ public:
void setType(Type type);
SvgMeshGradient::Type type() const;
void setTransform(const QTransform& matrix);
bool isValid() const;
QScopedPointer<SvgMeshArray>& getMeshArray();
......
......@@ -58,6 +58,11 @@ KoPathShape* SvgMeshPatch::getPath() const
return m_path.get();
}
QSizeF SvgMeshPatch::size() const
{
return m_path->size();
}
KoPathSegment SvgMeshPatch::getMidCurve(bool isVertical) const
{
QList<QPointF> curvedBoundary0;
......@@ -247,6 +252,32 @@ void SvgMeshPatch::addStop(const QList<QPointF>& pathPoints, QColor color, Type
m_startingPoint = pathPoints.last();
}
void transformShape(const QTransform& matrix, QScopedPointer<KoPathShape>& shape)
{
// apparently, you can't transform KoPathShape
for (int i = 0; i < shape->pointCount(); ++i) {
KoPathPointIndex index(0, i);
KoPathPoint *point = shape->pointByIndex(index);
QPointF previous = point->point();
QPointF cp1 = point->controlPoint1();
QPointF cp2 = point->controlPoint2();
point->setPoint(matrix.map(previous));
point->setControlPoint1(matrix.map(cp1));
point->setControlPoint2(matrix.map(cp2));
}
}
void SvgMeshPatch::setTransform(const QTransform& matrix)
{
transformShape(matrix, m_path);
m_startingPoint = matrix.map(m_startingPoint);
for (int i = 1; i < Size; ++i) {
m_nodes[static_cast<Type>(i)].point = matrix.map(m_nodes[static_cast<Type>(i)].point);
}
}
int SvgMeshPatch::countPoints() const
{
return m_nodes.size();
......
......@@ -64,6 +64,9 @@ public:
/// Get full (closed) meshpath
KoPathShape* getPath() const;
/// Get size swept by mesh in pts
QSizeF size() const;
/// Gets the curve passing through the middle of meshpatch
KoPathSegment getMidCurve(bool isVertical) const;
......@@ -80,6 +83,8 @@ public:
/// Adds path to the shape
void addStop(const QList<QPointF>& pathPoints, QColor color, Type edge);
void setTransform(const QTransform& matrix);
private:
/* Parses path and adds it to m_path and returns the last point of the curve/line
* see also: SvgMeshPatch::addStop
......
......@@ -1097,6 +1097,44 @@ QGradient* prepareGradientForShape(const SvgGradientHelper *gradient,
return resultGradient;
}
SvgMeshGradient* prepareMeshGradientForShape(SvgGradientHelper *gradient,
const KoShape *shape,
const SvgGraphicsContext *gc) {
SvgMeshGradient *resultGradient;
if (gradient->gradientUnits() == KoFlake::ObjectBoundingBox) {
resultGradient = new SvgMeshGradient(*gradient->meshgradient());
const QRectF boundingRect = shape->outline().boundingRect();
const QTransform relativeToShape(boundingRect.width(), 0, 0, boundingRect.height(),
boundingRect.x(), boundingRect.y());
// NOTE: we apply translation right away, because caching hasn't been implemented for rendering, yet.
// So, transform is called multiple times on the mesh and that's not nice
resultGradient->setTransform(relativeToShape * gradient->transform());
} else {
// NOTE: Krita's shapes use their own coordinate system. Where origin is at the top left
// of the SHAPE. All the mesh patches will be rendered in the global 'user' coorindate system
// where the origin is at the top left of the LAYER/DOCUMENT.
// Get the user coordinates of the shape
const QTransform shapeglobal = shape->absoluteTransformation() * gc->matrix.inverted();
// Get the translation offset to shift the origin from "Shape" to "User"
const QTransform translationOffset = QTransform::fromTranslate(-shapeglobal.dx(), -shapeglobal.dy());
resultGradient = new SvgMeshGradient(*gradient->meshgradient());
// NOTE: we apply translation right away, because caching hasn't been implemented for rendering, yet.
// So, transform is called multiple times on the mesh and that's not nice
resultGradient->setTransform(gradient->transform() * translationOffset);
}
return resultGradient;
}
void SvgParser::applyFillStyle(KoShape *shape)
{
SvgGraphicsContext *gc = m_context.currentGC();
......@@ -1115,9 +1153,9 @@ void SvgParser::applyFillStyle(KoShape *shape)
if (gradient->isMeshGradient()) {
QSharedPointer<KoMeshGradientBackground> bg;
SvgMeshGradient *result = new SvgMeshGradient(*gradient->meshgradient());
// TODO handle transform
SvgMeshGradient *result = prepareMeshGradientForShape(gradient, shape, gc);
bg = toQShared(new KoMeshGradientBackground(result, transform));
shape->setBackground(bg);
} else {
......