Krita Source Code Documentation
Loading...
Searching...
No Matches
KisBezierGradientMesh.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2020 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
10#include "kis_debug.h"
11#include "kis_dom_utils.h"
12
14
16{
17 QImageGradientOp(const std::array<QColor, 4> &colors, QImage &dstImage,
18 const QPointF &dstImageOffset)
19 : m_colors(colors), m_dstImage(dstImage),
20 m_dstImageOffset(dstImageOffset),
22 {
23 }
24
25 void operator() (const QPolygonF &srcPolygon, const QPolygonF &dstPolygon) {
26 this->operator() (srcPolygon, dstPolygon, dstPolygon);
27 }
28
29 void operator() (const QPolygonF &srcPolygon, const QPolygonF &dstPolygon, const QPolygonF &clipDstPolygon) {
30 QRect boundRect = clipDstPolygon.boundingRect().toAlignedRect();
31 KisFourPointInterpolatorBackward interp(srcPolygon, dstPolygon);
32
33 for (int y = boundRect.top(); y <= boundRect.bottom(); y++) {
34 interp.setY(y);
35 for (int x = boundRect.left(); x <= boundRect.right(); x++) {
36
37 QPointF srcPoint(x, y);
38 if (clipDstPolygon.containsPoint(srcPoint, Qt::OddEvenFill)) {
39
40 interp.setX(srcPoint.x());
41 QPointF dstPoint = interp.getValue();
42
43 // about srcPoint/dstPoint hell please see a
44 // comment in PaintDevicePolygonOp::operator() ()
45
46 srcPoint -= m_dstImageOffset;
47
48 QPoint srcPointI = srcPoint.toPoint();
49
50 if (!m_dstImageRect.contains(srcPointI)) continue;
51
52 // TODO: move vertical calculation into the upper loop
53 const QColor c1 = lerp(m_colors[0], m_colors[1], qBound(0.0, dstPoint.x(), 1.0));
54 const QColor c2 = lerp(m_colors[2], m_colors[3], qBound(0.0, dstPoint.x(), 1.0));
55
56 m_dstImage.setPixelColor(srcPointI, lerp(c1, c2, qBound(0.0, dstPoint.y(), 1.0)));
57 }
58 }
59 }
60 }
61
62 void finalize() {}
63
64 const std::array<QColor, 4> &m_colors;
65 QImage &m_dstImage;
68};
69
70void saveValue(QDomElement *parent, const QString &tag, const GradientMeshNode &node)
71{
72 QDomDocument doc = parent->ownerDocument();
73 QDomElement e = doc.createElement(tag);
74 parent->appendChild(e);
75
76 e.setAttribute("type", "gradient-mesh-node");
77 KisDomUtils::saveValue(&e, "color", node.color);
78 KisDomUtils::saveValue(&e, "node", node.node);
79 KisDomUtils::saveValue(&e, "left-control", node.leftControl);
80 KisDomUtils::saveValue(&e, "right-control", node.rightControl);
81 KisDomUtils::saveValue(&e, "top-control", node.topControl);
82 KisDomUtils::saveValue(&e, "bottom-control", node.bottomControl);
83
84}
85
86bool loadValue(const QDomElement &parent, GradientMeshNode *node)
87{
88 if (!KisDomUtils::Private::checkType(parent, "gradient-mesh-node")) return false;
89
90 KisDomUtils::loadValue(parent, "node", &node->node);
91 KisDomUtils::loadValue(parent, "left-control", &node->leftControl);
92 KisDomUtils::loadValue(parent, "right-control", &node->rightControl);
93 KisDomUtils::loadValue(parent, "top-control", &node->topControl);
94 KisDomUtils::loadValue(parent, "bottom-control", &node->bottomControl);
95
96 return true;
97}
98
99void saveValue(QDomElement *parent, const QString &tag, const KisBezierGradientMesh &mesh)
100{
101 QDomDocument doc = parent->ownerDocument();
102 QDomElement e = doc.createElement(tag);
103 parent->appendChild(e);
104
105 e.setAttribute("type", "gradient-mesh");
106
107 KisDomUtils::saveValue(&e, "size", mesh.m_size);
108 KisDomUtils::saveValue(&e, "srcRect", mesh.m_originalRect);
109 KisDomUtils::saveValue(&e, "columns", mesh.m_columns);
110 KisDomUtils::saveValue(&e, "rows", mesh.m_rows);
111 KisDomUtils::saveValue(&e, "nodes", mesh.m_nodes);
112}
113
114bool loadValue(const QDomElement &parent, const QString &tag, KisBezierGradientMesh *mesh)
115{
116 QDomElement e;
117 if (!KisDomUtils::findOnlyElement(parent, tag, &e)) return false;
118
119 if (!KisDomUtils::Private::checkType(e, "gradient-mesh")) return false;
120
121 mesh->m_columns.clear();
122 mesh->m_rows.clear();
123 mesh->m_nodes.clear();
124
125 KisDomUtils::loadValue(e, "size", &mesh->m_size);
126 KisDomUtils::loadValue(e, "srcRect", &mesh->m_originalRect);
127 KisDomUtils::loadValue(e, "columns", &mesh->m_columns);
128 KisDomUtils::loadValue(e, "rows", &mesh->m_rows);
129 KisDomUtils::loadValue(e, "nodes", &mesh->m_nodes);
130
131 return true;
132}
133
134}
135
136KisBezierGradientMesh::PatchIndex KisBezierGradientMesh::hitTestPatch(const QPointF &pt, QPointF *localPointResult) const {
137 auto result = endPatches();
138
139 const QRectF unitRect(0, 0, 1, 1);
140
141 for (auto it = beginPatches(); it != endPatches(); ++it) {
142 Patch patch = *it;
143
144 if (patch.dstBoundingRect().contains(pt)) {
145 const QPointF localPos = KisBezierUtils::calculateLocalPosSVG2(patch.points, pt);
146
147 if (unitRect.contains(localPos)) {
148
149 if (localPointResult) {
150 *localPointResult = localPos;
151 }
152
153 result = it;
154 break;
155 }
156 }
157 }
158
159 return result.patchIndex();
160}
161
163 const QPoint &dstQImageOffset,
164 QImage *dstImage)
165{
166 QVector<QPointF> originalPointsLocal;
167 QVector<QPointF> transformedPointsLocal;
168 QSize gridSize;
169
170 patch.sampleRegularGridSVG2(gridSize, originalPointsLocal, transformedPointsLocal, QPointF(8,8));
171
172 const QRect dstBoundsI = patch.dstBoundingRect().toAlignedRect();
173 const QRect imageSize = QRect(dstQImageOffset, dstImage->size());
174 KIS_SAFE_ASSERT_RECOVER_NOOP(imageSize.contains(dstBoundsI));
175
176 {
177 QImageGradientOp polygonOp(patch.colors, *dstImage, dstQImageOffset);
178
179
183 gridSize,
184 originalPointsLocal,
185 transformedPointsLocal);
186
187 }
188}
189
190void KisBezierGradientMesh::renderMesh(const QPoint &dstQImageOffset,
191 QImage *dstImage) const
192{
193 for (auto it = beginPatches(); it != endPatches(); ++it) {
194 renderPatch(*it, dstQImageOffset, dstImage);
195 }
196}
QPointF dstPoint
void renderMesh(const QPoint &dstQImageOffset, QImage *dstImage) const
PatchIndex hitTestPatch(const QPointF &pt, QPointF *localPointResult) const
static void renderPatch(const GradientMeshPatch &patch, const QPoint &dstQImageOffset, QImage *dstImage)
std::vector< qreal > m_rows
patch_iterator endPatches()
std::vector< qreal > m_columns
patch_iterator beginPatches()
std::vector< Node > m_nodes
QRectF dstBoundingRect() const
void sampleRegularGridSVG2(QSize &gridSize, QVector< QPointF > &origPoints, QVector< QPointF > &transfPoints, const QPointF &dstStep) const
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
qreal interp(qreal r, qreal a, qreal b)
private functions
void iterateThroughGrid(PolygonOp &polygonOp, IndexesOp &indexesOp, const QSize &gridSize, const QVector< QPointF > &originalPoints, const QVector< QPointF > &transformedPoints)
QColor lerp(const QColor &c1, const QColor &c2, qreal t)
bool loadValue(const QDomElement &parent, GradientMeshNode *node)
void saveValue(QDomElement *parent, const QString &tag, const GradientMeshNode &node)
QPointF calculateLocalPosSVG2(const std::array< QPointF, 12 > &points, const QPointF &globalPoint)
calculates local (u,v) coordinates of the patch corresponding to globalPoint
bool checkType(const QDomElement &e, const QString &expectedType)
void saveValue(QDomElement *parent, const QString &tag, const QSize &size)
bool findOnlyElement(const QDomElement &parent, const QString &tag, QDomElement *el, QStringList *errorMessages)
bool loadValue(const QDomElement &e, float *v)
QImageGradientOp(const std::array< QColor, 4 > &colors, QImage &dstImage, const QPointF &dstImageOffset)
void operator()(const QPolygonF &srcPolygon, const QPolygonF &dstPolygon)