Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_duplicateop.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2002 Patrick Julien <freak@codepimps.org>
3 * SPDX-FileCopyrightText: 2004-2008 Boudewijn Rempt <boud@valdyas.org>
4 * SPDX-FileCopyrightText: 2004 Clarence Dang <dang@kde.org>
5 * SPDX-FileCopyrightText: 2004 Adrian Page <adrian@pagenet.plus.com>
6 * SPDX-FileCopyrightText: 2004, 2010 Cyrille Berger <cberger@cberger.net>
7 *
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11#include "kis_duplicateop.h"
12#include "kis_duplicateop_p.h"
13
14#include <string.h>
15#include <memory>
16
17#include <QRect>
18#include <QLayout>
19#include <QCheckBox>
20#include <QDomElement>
21#include <QHBoxLayout>
22
23#include <kis_image.h>
24#include <kis_debug.h>
25
27#include <KoColor.h>
28#include <KoColorSpace.h>
31
32#include <kis_brush.h>
33#include <kis_datamanager.h>
34#include <kis_global.h>
35#include <kis_paint_device.h>
36#include <kis_painter.h>
39#include <kis_selection.h>
44#include <kis_iterator_ng.h>
46
50
52 : KisBrushBasedPaintOp(settings, painter)
53 , m_image(image)
54 , m_node(node)
55 , m_settings(static_cast<KisDuplicateOpSettings*>(const_cast<KisPaintOpSettings*>(settings.data())))
56 , m_sizeOption(settings.data())
57 , m_opacityOption(settings.data(), node)
58 , m_rotationOption(settings.data())
59{
60 Q_ASSERT(settings);
61 Q_ASSERT(painter);
62
65}
66
70
71#define CLAMP(x,l,u) ((x)<(l)?(l):((x)>(u)?(u):(x)))
72
74{
75 if (!painter()->device()) return KisSpacingInformation(1.0);
76
77 KisBrushSP brush = m_brush;
78 if (!brush)
79 return KisSpacingInformation(1.0);
80
81 if (!brush->canPaintFor(info))
82 return KisSpacingInformation(1.0);
83
86 m_duplicateStart = info.pos();
87 }
88
89 KisPaintDeviceSP realSourceDevice;
90
92 realSourceDevice = m_image->projection();
93 }
94 else {
95 KisNodeSP externalSourceNode = m_settings->sourceNode();
96
101 if (!externalSourceNode || !externalSourceNode->graphListener()) {
102 externalSourceNode = m_node;
103 }
104
105 realSourceDevice = externalSourceNode->projection();
106 }
107
108 qreal rotation = m_rotationOption.apply(info);
109
111
112 qreal scale = m_sizeOption.apply(info);
113
114 if (checkSizeTooSmall(scale)) return KisSpacingInformation();
115 KisDabShape shape(scale, 1.0, rotation);
116
117
118 static const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8();
119 static KoColor color(Qt::black, cs);
120
121 QRect dstRect;
123 m_dabCache->fetchDab(cs, color, info.pos(),
124 shape,
125 info, 1.0,
126 &dstRect);
127
128 if (dstRect.isEmpty()) return KisSpacingInformation(1.0);
129
130
131 QPoint srcPoint;
132
134 srcPoint = (dstRect.topLeft() - m_settings->offset()).toPoint();
135 }
136 else {
137 QPointF hotSpot = brush->hotSpot(shape, info);
138 srcPoint = (m_settings->position() - hotSpot).toPoint();
139 }
140
141 qint32 sw = dstRect.width();
142 qint32 sh = dstRect.height();
143
144 // Perspective correction ?
145
146
147 // if (m_perspectiveCorrection && m_image && m_image->perspectiveGrid()->countSubGrids() == 1) {
148 // Matrix3qreal startM = Matrix3qreal::Identity();
149 // Matrix3qreal endM = Matrix3qreal::Identity();
150
151 // // First look for the grid corresponding to the start point
152 // KisSubPerspectiveGrid* subGridStart = *m_image->perspectiveGrid()->begin();
153 // QRect r = QRect(0, 0, m_image->width(), m_image->height());
154
155 // if (subGridStart) {
156 // startM = KisPerspectiveMath::computeMatrixTransfoFromPerspective(r, *subGridStart->topLeft(), *subGridStart->topRight(), *subGridStart->bottomLeft(), *subGridStart->bottomRight());
157 // }
158
159 // // Second look for the grid corresponding to the end point
160 // KisSubPerspectiveGrid* subGridEnd = *m_image->perspectiveGrid()->begin();
161 // if (subGridEnd) {
162 // endM = KisPerspectiveMath::computeMatrixTransfoToPerspective(*subGridEnd->topLeft(), *subGridEnd->topRight(), *subGridEnd->bottomLeft(), *subGridEnd->bottomRight(), r);
163 // }
164
165 // // Compute the translation in the perspective transformation space:
166 // QPointF positionStartPaintingT = KisPerspectiveMath::matProd(endM, QPointF(m_duplicateStart));
167 // QPointF duplicateStartPositionT = KisPerspectiveMath::matProd(endM, QPointF(m_duplicateStart) - QPointF(m_settings->offset()));
168 // QPointF translate = duplicateStartPositionT - positionStartPaintingT;
169
170 // KisSequentialIterator dstIt(m_srcdev, QRect(0, 0, sw, sh));
171 // KisRandomSubAccessorSP srcAcc = realSourceDevice->createRandomSubAccessor();
172
173 // //Action
174 // while (dstIt.nextPixel()) {
175 // QPointF p = KisPerspectiveMath::matProd(startM, KisPerspectiveMath::matProd(endM, QPointF(dstIt.x() + dstRect.x(), dstIt.y() + dstRect.y())) + translate);
176 // srcAcc->moveTo(p);
177 // srcAcc->sampledOldRawData(dstIt.rawData());
178 // }
179
180
181 // }
182 // else
183 {
184 KisPainter copyPainter(m_srcdev);
185 copyPainter.setCompositeOpId(COMPOSITE_COPY);
186 copyPainter.bitBltOldData(0, 0, realSourceDevice, srcPoint.x(), srcPoint.y(), sw, sh);
187 copyPainter.end();
188 }
189
190 // heal ?
192 QRect healRect(dstRect);
193
194 const bool smallWidth = healRect.width() < 3;
195 const bool smallHeight = healRect.height() < 3;
196
197 if (smallWidth || smallHeight) {
198 healRect.adjust(-1, -1, 1, 1);
199 }
200
201 const int healSW = healRect.width();
202 const int healSH = healRect.height();
203
204
205 quint16 srcData[4];
206 quint16 tmpData[4];
207 std::unique_ptr<qreal[]> matrix(new qreal[ 3 * healSW * healSH ]);
208 // First divide
209 const KoColorSpace* srcCs = realSourceDevice->colorSpace();
210 const KoColorSpace* tmpCs = m_srcdev->colorSpace();
211 KisHLineConstIteratorSP srcIt = realSourceDevice->createHLineConstIteratorNG(healRect.x(), healRect.y() , healSW);
213 qreal* matrixIt = matrix.get();
214 for (int j = 0; j < healSH; j++) {
215 for (int i = 0; i < healSW; i++) {
216 srcCs->toLabA16(srcIt->oldRawData(), (quint8*)srcData, 1);
217 tmpCs->toLabA16(tmpIt->rawData(), (quint8*)tmpData, 1);
218 // Division
219 for (int k = 0; k < 3; k++) {
220 matrixIt[k] = srcData[k] / (qreal)qMax((int)tmpData [k], 1);
221 }
222 srcIt->nextPixel();
223 tmpIt->nextPixel();
224 matrixIt += 3;
225 }
226 srcIt->nextRow();
227 tmpIt->nextRow();
228 }
229 // Minimize energy
230 {
231 int iter = 0;
232 qreal err;
233 std::unique_ptr<qreal[]> solution(new qreal[ 3 * healSW * healSH ]);
234
235 do {
236 err = DuplicateOpUtils::minimizeEnergy(matrix.get(), solution.get(), healSW, healSH);
237
238 solution.swap(matrix);
239
240 iter++;
241 } while (err > 0.00001 && iter < 100);
242 }
243
244 // Finally multiply
245 KisHLineIteratorSP tmpIt2 = m_srcdev->createHLineIteratorNG(0, 0, healSW);
246 matrixIt = &matrix[0];
247 for (int j = 0; j < healSH; j++) {
248 for (int i = 0; i < healSW; i++) {
249 tmpCs->toLabA16(tmpIt2->rawData(), (quint8*)tmpData, 1);
250 // Multiplication
251 for (int k = 0; k < 3; k++) {
252 tmpData[k] = (int)CLAMP(matrixIt[k] * qMax((int) tmpData[k], 1), 0, 65535);
253 }
254 tmpCs->fromLabA16((quint8*)tmpData, tmpIt2->rawData(), 1);
255 tmpIt2->nextPixel();
256 matrixIt += 3;
257 }
258 tmpIt2->nextRow();
259 }
260 }
261
262 painter()->bitBltWithFixedSelection(dstRect.x(), dstRect.y(),
263 m_srcdev, dab,
264 dstRect.width(),
265 dstRect.height());
266
267 painter()->renderMirrorMaskSafe(dstRect, m_srcdev, 0, 0, dab,
269
270 return effectiveSpacing(scale);
271}
272
const QString COMPOSITE_COPY
virtual const quint8 * oldRawData() const =0
virtual bool nextPixel()=0
KisSpacingInformation effectiveSpacing(qreal scale) const
bool needSeparateOriginal() const
KisFixedPaintDeviceSP fetchDab(const KoColorSpace *cs, KisColorSource *colorSource, const QPointF &cursorPoint, KisDabShape const &, const KisPaintInformation &info, qreal softnessFactor, QRect *dstDabRect, qreal lightnessStrength=1.0)
KisDuplicateOpSettingsSP m_settings
KisSpacingInformation updateSpacingImpl(const KisPaintInformation &info) const override
KisSizeOption m_sizeOption
KisSpacingInformation paintAt(const KisPaintInformation &info) override
KisPaintDeviceSP m_srcdev
KisRotationOption m_rotationOption
KisDuplicateOp(const KisPaintOpSettingsSP settings, KisPainter *painter, KisNodeSP node, KisImageSP image)
KisOpacityOption m_opacityOption
KisImageSP m_image
QPointF m_duplicateStart
~KisDuplicateOp() override
KisDuplicateOptionData m_duplicateOptionData
virtual void nextRow()=0
KisPaintDeviceSP projection() const
void apply(KisPainter *painter, const KisPaintInformation &info) const
KisPaintDeviceSP createCompositionSourceDevice() const
KisHLineIteratorSP createHLineIteratorNG(qint32 x, qint32 y, qint32 w)
const KoColorSpace * colorSpace() const
KisHLineConstIteratorSP createHLineConstIteratorNG(qint32 x, qint32 y, qint32 w) const
const QPointF & pos() const
void bitBltOldData(qint32 dstX, qint32 dstY, const KisPaintDeviceSP srcDev, qint32 srcX, qint32 srcY, qint32 srcWidth, qint32 srcHeight)
void renderMirrorMaskSafe(QRect rc, KisFixedPaintDeviceSP dab, bool preserveDab)
void bitBltWithFixedSelection(qint32 dstX, qint32 dstY, const KisPaintDeviceSP srcDev, const KisFixedPaintDeviceSP selection, qint32 selX, qint32 selY, qint32 srcX, qint32 srcY, qint32 srcWidth, qint32 srcHeight)
void setCompositeOpId(const KoCompositeOp *op)
qreal apply(const KisPaintInformation &info) const
qreal apply(const KisPaintInformation &info) const
virtual void toLabA16(const quint8 *src, quint8 *dst, quint32 nPixels) const
virtual void fromLabA16(const quint8 *src, quint8 *dst, quint32 nPixels) const
#define CLAMP(x, l, h)
qreal minimizeEnergy(const qreal *m, qreal *sol, int w, int h)
virtual KisPaintDeviceSP projection() const =0
bool read(const KisPropertiesConfiguration *setting)
KisNodeGraphListener * graphListener
Definition kis_node.cpp:87
KisPainter * painter
KisFixedPaintDeviceSP dab
KisPaintDeviceSP source() const
static KoColorSpaceRegistry * instance()
const KoColorSpace * alpha8()