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 const std::array<QColor, 4> &m_colors;
63 QImage &m_dstImage;
66};
67
68void saveValue(QDomElement *parent, const QString &tag, const GradientMeshNode &node)
69{
70 QDomDocument doc = parent->ownerDocument();
71 QDomElement e = doc.createElement(tag);
72 parent->appendChild(e);
73
74 e.setAttribute("type", "gradient-mesh-node");
75 KisDomUtils::saveValue(&e, "color", node.color);
76 KisDomUtils::saveValue(&e, "node", node.node);
77 KisDomUtils::saveValue(&e, "left-control", node.leftControl);
78 KisDomUtils::saveValue(&e, "right-control", node.rightControl);
79 KisDomUtils::saveValue(&e, "top-control", node.topControl);
80 KisDomUtils::saveValue(&e, "bottom-control", node.bottomControl);
81
82}
83
84bool loadValue(const QDomElement &parent, GradientMeshNode *node)
85{
86 if (!KisDomUtils::Private::checkType(parent, "gradient-mesh-node")) return false;
87
88 KisDomUtils::loadValue(parent, "node", &node->node);
89 KisDomUtils::loadValue(parent, "left-control", &node->leftControl);
90 KisDomUtils::loadValue(parent, "right-control", &node->rightControl);
91 KisDomUtils::loadValue(parent, "top-control", &node->topControl);
92 KisDomUtils::loadValue(parent, "bottom-control", &node->bottomControl);
93
94 return true;
95}
96
97void saveValue(QDomElement *parent, const QString &tag, const KisBezierGradientMesh &mesh)
98{
99 QDomDocument doc = parent->ownerDocument();
100 QDomElement e = doc.createElement(tag);
101 parent->appendChild(e);
102
103 e.setAttribute("type", "gradient-mesh");
104
105 KisDomUtils::saveValue(&e, "size", mesh.m_size);
106 KisDomUtils::saveValue(&e, "srcRect", mesh.m_originalRect);
107 KisDomUtils::saveValue(&e, "columns", mesh.m_columns);
108 KisDomUtils::saveValue(&e, "rows", mesh.m_rows);
109 KisDomUtils::saveValue(&e, "nodes", mesh.m_nodes);
110}
111
112bool loadValue(const QDomElement &parent, const QString &tag, KisBezierGradientMesh *mesh)
113{
114 QDomElement e;
115 if (!KisDomUtils::findOnlyElement(parent, tag, &e)) return false;
116
117 if (!KisDomUtils::Private::checkType(e, "gradient-mesh")) return false;
118
119 mesh->m_columns.clear();
120 mesh->m_rows.clear();
121 mesh->m_nodes.clear();
122
123 KisDomUtils::loadValue(e, "size", &mesh->m_size);
124 KisDomUtils::loadValue(e, "srcRect", &mesh->m_originalRect);
125 KisDomUtils::loadValue(e, "columns", &mesh->m_columns);
126 KisDomUtils::loadValue(e, "rows", &mesh->m_rows);
127 KisDomUtils::loadValue(e, "nodes", &mesh->m_nodes);
128
129 return true;
130}
131
132}
133
134KisBezierGradientMesh::PatchIndex KisBezierGradientMesh::hitTestPatch(const QPointF &pt, QPointF *localPointResult) const {
135 auto result = endPatches();
136
137 const QRectF unitRect(0, 0, 1, 1);
138
139 for (auto it = beginPatches(); it != endPatches(); ++it) {
140 Patch patch = *it;
141
142 if (patch.dstBoundingRect().contains(pt)) {
143 const QPointF localPos = KisBezierUtils::calculateLocalPosSVG2(patch.points, pt);
144
145 if (unitRect.contains(localPos)) {
146
147 if (localPointResult) {
148 *localPointResult = localPos;
149 }
150
151 result = it;
152 break;
153 }
154 }
155 }
156
157 return result.patchIndex();
158}
159
161 const QPoint &dstQImageOffset,
162 QImage *dstImage)
163{
164 QVector<QPointF> originalPointsLocal;
165 QVector<QPointF> transformedPointsLocal;
166 QSize gridSize;
167
168 patch.sampleRegularGridSVG2(gridSize, originalPointsLocal, transformedPointsLocal, QPointF(8,8));
169
170 const QRect dstBoundsI = patch.dstBoundingRect().toAlignedRect();
171 const QRect imageSize = QRect(dstQImageOffset, dstImage->size());
172 KIS_SAFE_ASSERT_RECOVER_NOOP(imageSize.contains(dstBoundsI));
173
174 {
175 QImageGradientOp polygonOp(patch.colors, *dstImage, dstQImageOffset);
176
177
181 gridSize,
182 originalPointsLocal,
183 transformedPointsLocal);
184
185 }
186}
187
188void KisBezierGradientMesh::renderMesh(const QPoint &dstQImageOffset,
189 QImage *dstImage) const
190{
191 for (auto it = beginPatches(); it != endPatches(); ++it) {
192 renderPatch(*it, dstQImageOffset, dstImage);
193 }
194}
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)