1 /*
2  *  Copyright (c) 2020 Dmitry Kazakov <dimula73@gmail.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 #include "KisBezierTransformMesh.h"
20 
21 #include "kis_grid_interpolation_tools.h"
22 #include "kis_debug.h"
23 
hitTestPatch(const QPointF & pt,QPointF * localPointResult) const24 KisBezierTransformMesh::PatchIndex KisBezierTransformMesh::hitTestPatch(const QPointF &pt, QPointF *localPointResult) const {
25     auto result = endPatches();
26 
27     const QRectF unitRect(0, 0, 1, 1);
28 
29     for (auto it = beginPatches(); it != endPatches(); ++it) {
30         Patch patch = *it;
31 
32         if (patch.dstBoundingRect().contains(pt)) {
33             const QPointF localPos = KisBezierUtils::calculateLocalPos(patch.points, pt);
34 
35             if (unitRect.contains(localPos)) {
36 
37                 if (localPointResult) {
38                     *localPointResult = localPos;
39                 }
40 
41                 result = it;
42                 break;
43             }
44         }
45     }
46 
47     return result.patchIndex();
48 }
49 
transformPatch(const KisBezierPatch & patch,const QPoint & srcQImageOffset,const QImage & srcImage,const QPoint & dstQImageOffset,QImage * dstImage)50 void KisBezierTransformMesh::transformPatch(const KisBezierPatch &patch, const QPoint &srcQImageOffset, const QImage &srcImage, const QPoint &dstQImageOffset, QImage *dstImage)
51 {
52     QVector<QPointF> originalPointsLocal;
53     QVector<QPointF> transformedPointsLocal;
54     QSize gridSize;
55 
56     patch.sampleRegularGrid(gridSize, originalPointsLocal, transformedPointsLocal, QPointF(8,8));
57 
58     const QRect dstBoundsI = patch.dstBoundingRect().toAlignedRect();
59     const QRect imageSize = QRect(dstQImageOffset, dstImage->size());
60     KIS_SAFE_ASSERT_RECOVER_NOOP(imageSize.contains(dstBoundsI));
61 
62     {
63         GridIterationTools::QImagePolygonOp polygonOp(srcImage, *dstImage, srcQImageOffset, dstQImageOffset);
64 
65         GridIterationTools::RegularGridIndexesOp indexesOp(gridSize);
66         GridIterationTools::iterateThroughGrid
67                 <GridIterationTools::AlwaysCompletePolygonPolicy>(polygonOp, indexesOp,
68                                                                   gridSize,
69                                                                   originalPointsLocal,
70                                                                   transformedPointsLocal);
71     }
72 }
73 
transformPatch(const KisBezierPatch & patch,KisPaintDeviceSP srcDevice,KisPaintDeviceSP dstDevice)74 void KisBezierTransformMesh::transformPatch(const KisBezierPatch &patch, KisPaintDeviceSP srcDevice, KisPaintDeviceSP dstDevice)
75 {
76     QVector<QPointF> originalPointsLocal;
77     QVector<QPointF> transformedPointsLocal;
78     QSize gridSize;
79 
80     patch.sampleRegularGrid(gridSize, originalPointsLocal, transformedPointsLocal, QPointF(8,8));
81 
82     {
83         GridIterationTools::PaintDevicePolygonOp polygonOp(srcDevice, dstDevice);
84 
85         GridIterationTools::RegularGridIndexesOp indexesOp(gridSize);
86         GridIterationTools::iterateThroughGrid
87                 <GridIterationTools::AlwaysCompletePolygonPolicy>(polygonOp, indexesOp,
88                                                                   gridSize,
89                                                                   originalPointsLocal,
90                                                                   transformedPointsLocal);
91     }
92 }
93 
transformMesh(const QPoint & srcQImageOffset,const QImage & srcImage,const QPoint & dstQImageOffset,QImage * dstImage) const94 void KisBezierTransformMesh::transformMesh(const QPoint &srcQImageOffset, const QImage &srcImage, const QPoint &dstQImageOffset, QImage *dstImage) const
95 {
96     for (auto it = beginPatches(); it != endPatches(); ++it) {
97         transformPatch(*it, srcQImageOffset, srcImage, dstQImageOffset, dstImage);
98     }
99 }
100 
transformMesh(KisPaintDeviceSP srcDevice,KisPaintDeviceSP dstDevice) const101 void KisBezierTransformMesh::transformMesh(KisPaintDeviceSP srcDevice, KisPaintDeviceSP dstDevice) const
102 {
103     for (auto it = beginPatches(); it != endPatches(); ++it) {
104         transformPatch(*it, srcDevice, dstDevice);
105     }
106 }
107 
approxNeedRect(const QRect & rc) const108 QRect KisBezierTransformMesh::approxNeedRect(const QRect &rc) const
109 {
110     QRect result = rc;
111 
112     for (auto it = beginPatches(); it != endPatches(); ++it) {
113         KisBezierPatch patch = *it;
114 
115         if (patch.dstBoundingRect().intersects(rc)) {
116             result |= patch.srcBoundingRect().toAlignedRect();
117         }
118     }
119 
120     return result;
121 }
122 
approxChangeRect(const QRect & rc) const123 QRect KisBezierTransformMesh::approxChangeRect(const QRect &rc) const
124 {
125     QRect result = rc;
126 
127     for (auto it = beginPatches(); it != endPatches(); ++it) {
128         const KisBezierPatch patch = *it;
129 
130         if (patch.srcBoundingRect().intersects(rc)) {
131             result |= patch.dstBoundingRect().toAlignedRect();
132         }
133     }
134 
135     return result;
136 }
137 
138 #include <kis_dom_utils.h>
139 
saveValue(QDomElement * parent,const QString & tag,const KisBezierTransformMesh & mesh)140 void KisBezierTransformMeshDetail::saveValue(QDomElement *parent, const QString &tag, const KisBezierTransformMesh &mesh)
141 {
142     QDomDocument doc = parent->ownerDocument();
143     QDomElement e = doc.createElement(tag);
144     parent->appendChild(e);
145 
146     e.setAttribute("type", "transform-mesh");
147 
148     KisDomUtils::saveValue(&e, "size", mesh.m_size);
149     KisDomUtils::saveValue(&e, "srcRect", mesh.m_originalRect);
150     KisDomUtils::saveValue(&e, "columns", mesh.m_columns);
151     KisDomUtils::saveValue(&e, "rows", mesh.m_rows);
152     KisDomUtils::saveValue(&e, "nodes", mesh.m_nodes);
153 }
154 
loadValue(const QDomElement & e,KisBezierTransformMesh * mesh)155 bool KisBezierTransformMeshDetail::loadValue(const QDomElement &e, KisBezierTransformMesh *mesh)
156 {
157     if (!KisDomUtils::Private::checkType(e, "transform-mesh")) return false;
158 
159     mesh->m_columns.clear();
160     mesh->m_rows.clear();
161     mesh->m_nodes.clear();
162 
163     KisDomUtils::loadValue(e, "size", &mesh->m_size);
164     KisDomUtils::loadValue(e, "srcRect", &mesh->m_originalRect);
165     KisDomUtils::loadValue(e, "columns", &mesh->m_columns);
166     KisDomUtils::loadValue(e, "rows", &mesh->m_rows);
167     KisDomUtils::loadValue(e, "nodes", &mesh->m_nodes);
168 
169     return true;
170 }
171