Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_liquify_transform_strategy.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2014 Dmitry Kazakov <dimula73@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
8
9#include <algorithm>
10
11#include <QPointF>
12#include <QPainter>
13#include <QPainterPath>
14
15#include "KoPointerEvent.h"
16
18#include "tool_transform_args.h"
20#include "krita_utils.h"
21#include "kis_cursor.h"
22#include "kis_transform_utils.h"
23#include "kis_algebra_2d.h"
27#include "kis_tool_utils.h"
28
29
31{
33 const KisCoordinatesConverter *_converter,
34 ToolTransformArgs &_currentArgs,
36 const KoCanvasResourceProvider *_manager)
37 : manager(_manager),
38 q(_q),
39 converter(_converter),
40 currentArgs(_currentArgs),
41 transaction(_transaction),
42 helper(_converter),
44 {
45 }
46
48
50
52
54
59
62
63 QTransform handlesTransform;
64
66
68
69 // size-gesture-related
73
75
77
79 inline QPointF imageToThumb(const QPointF &pt, bool useFlakeOptimization);
80};
81
83 ToolTransformArgs &currentArgs,
85 const KoCanvasResourceProvider *manager)
86
87 : m_d(new Private(this, converter, currentArgs, transaction, manager))
88{
89}
90
94
96{
97 return m_d->helper.brushOutline(*m_d->currentArgs.liquifyProperties());
98}
99
100void KisLiquifyTransformStrategy::setTransformFunction(const QPointF &mousePos, bool perspectiveModifierActive, bool shiftModifierActive)
101{
102 Q_UNUSED(mousePos);
103 Q_UNUSED(perspectiveModifierActive);
104 Q_UNUSED(shiftModifierActive);
105}
106
108{
109 return Qt::BlankCursor;
110}
111
113{
114 // Draw preview image
115
116 if (m_d->recalculateOnNextRedraw) {
117 m_d->recalculateTransformations();
118 m_d->recalculateOnNextRedraw = false;
119 }
120
121 gc.save();
122
123 gc.setOpacity(m_d->transaction.basePreviewOpacity());
124 gc.setTransform(m_d->paintingTransform, true);
125 gc.drawImage(m_d->paintingOffset, m_d->transformedImage);
126
127 gc.restore();
128}
129
131{
132 if (!m_d->currentArgs.liquifyWorker()) return;
133 m_d->recalculateTransformations();
134}
135
137{
138 return true;
139}
140
142{
143 m_d->helper.configurePaintOp(*m_d->currentArgs.liquifyProperties(), m_d->currentArgs.liquifyWorker());
144 m_d->helper.startPaint(event, m_d->manager);
145
146 m_d->recalculateTransformations();
147
148 return true;
149}
150
152{
153 m_d->helper.continuePaint(event);
154
155 // the updates should be compressed
156 m_d->recalculateOnNextRedraw = true;
157 Q_EMIT requestCanvasUpdate();
158}
159
161{
162 if (m_d->helper.endPaint(event)) {
163 m_d->recalculateTransformations();
164 Q_EMIT requestCanvasUpdate();
165 }
166
167 return true;
168}
169
171{
172 m_d->helper.hoverPaint(event);
173}
174
176{
177 if (action == KisTool::SampleFgNode || action == KisTool::SampleBgNode ||
178 action == KisTool::SampleFgImage || action == KisTool::SampleBgImage) {
179
180 KisLiquifyProperties *props = m_d->currentArgs.liquifyProperties();
181 props->setReverseDirection(!props->reverseDirection());
183 }
184}
185
187{
188 if (action == KisTool::SampleFgNode || action == KisTool::SampleBgNode ||
189 action == KisTool::SampleFgImage || action == KisTool::SampleBgImage) {
190
191 KisLiquifyProperties *props = m_d->currentArgs.liquifyProperties();
192 props->setReverseDirection(!props->reverseDirection());
194 }
195}
196
198{
199 if (action == KisTool::ChangeSize || action == KisTool::ChangeSizeSnap) {
200 QPointF widgetPoint = m_d->converter->documentToWidget(event->point);
201 m_d->lastMouseWidgetPos = widgetPoint;
202 m_d->startResizeImagePos = m_d->converter->documentToImage(event->point);
203 m_d->startResizeGlobalCursorPos = QCursor::pos();
204 return true;
205 } else if (action == KisTool::SampleFgNode || action == KisTool::SampleBgNode ||
206 action == KisTool::SampleFgImage || action == KisTool::SampleBgImage) {
207
208 return beginPrimaryAction(event);
209 }
210
211 return false;
212}
213
215{
216 if (action == KisTool::ChangeSize || action == KisTool::ChangeSizeSnap) {
217 QPointF widgetPoint = m_d->converter->documentToWidget(event->point);
218
219 QPointF diff = widgetPoint - m_d->lastMouseWidgetPos;
220
221 KisLiquifyProperties *props = m_d->currentArgs.liquifyProperties();
222 const qreal linearizedOffset = diff.x() / KisTransformUtils::scaleFromAffineMatrix(m_d->converter->imageToWidgetTransform());
223 const qreal newSize = qBound(props->minSize(), props->size() + linearizedOffset, props->maxSize());
224 if (action == KisTool::ChangeSizeSnap) {
225 props->setSize(floor(newSize));
226 } else {
227 props->setSize(newSize);
228 }
229 m_d->currentArgs.saveLiquifyTransformMode();
230
231 m_d->lastMouseWidgetPos = widgetPoint;
232
233 Q_EMIT requestCursorOutlineUpdate(m_d->startResizeImagePos);
234 } else if (action == KisTool::SampleFgNode || action == KisTool::SampleBgNode ||
235 action == KisTool::SampleFgImage || action == KisTool::SampleBgImage) {
236
237 return continuePrimaryAction(event);
238 }
239}
240
242{
243 Q_UNUSED(event);
244
245 if (action == KisTool::ChangeSize || action == KisTool::ChangeSizeSnap) {
246 KisToolUtils::setCursorPos(m_d->startResizeGlobalCursorPos);
247 return true;
248 } else if (action == KisTool::SampleFgNode || action == KisTool::SampleBgNode ||
249 action == KisTool::SampleFgImage || action == KisTool::SampleBgImage) {
250 return endPrimaryAction(event);
251 }
252
253 return false;
254}
255
256inline QPointF KisLiquifyTransformStrategy::Private::imageToThumb(const QPointF &pt, bool useFlakeOptimization)
257{
258 return useFlakeOptimization ? converter->imageToDocument(converter->documentToFlake((pt))) : q->thumbToImageTransform().inverted().map(pt);
259}
260
262{
263 KIS_ASSERT_RECOVER_RETURN(currentArgs.liquifyWorker());
264
265 QTransform scaleTransform = KisTransformUtils::imageToFlakeTransform(converter);
266
267 QTransform resultThumbTransform = q->thumbToImageTransform() * scaleTransform;
268 qreal scale = KisTransformUtils::scaleFromAffineMatrix(resultThumbTransform);
269 bool useFlakeOptimization = scale < 1.0 &&
270 !KisTransformUtils::thumbnailTooSmall(resultThumbTransform, q->originalImage().rect());
271
272 paintingOffset = transaction.originalTopLeft();
273 if (!q->originalImage().isNull()) {
274 if (useFlakeOptimization) {
275 transformedImage = q->originalImage().transformed(resultThumbTransform);
276 paintingTransform = QTransform();
277 } else {
278 transformedImage = q->originalImage();
279 paintingTransform = resultThumbTransform;
280 }
281
282 QTransform imageToRealThumbTransform =
283 useFlakeOptimization ?
284 scaleTransform :
285 q->thumbToImageTransform().inverted();
286
287 QPointF origTLInFlake =
288 imageToRealThumbTransform.map(transaction.originalTopLeft());
289
290 transformedImage =
291 currentArgs.liquifyWorker()->runOnQImage(transformedImage,
292 origTLInFlake,
293 imageToRealThumbTransform,
294 &paintingOffset);
295 } else {
296 transformedImage = q->originalImage();
297 paintingOffset = imageToThumb(transaction.originalTopLeft(), false);
298 paintingTransform = resultThumbTransform;
299 }
300
301 handlesTransform = scaleTransform;
302 Q_EMIT q->requestImageRecalculation();
303}
304
_Private::Traits< T >::Result documentToFlake(const T &obj) const
_Private::Traits< T >::Result imageToDocument(const T &obj) const
void setReverseDirection(bool value)
void activateAlternateAction(KisTool::AlternateAction action) override
void deactivateAlternateAction(KisTool::AlternateAction action) override
void hoverActionCommon(KoPointerEvent *event) override
void continueAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action) override
bool beginAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action) override
bool endAlternateAction(KoPointerEvent *event, KisTool::AlternateAction action) override
const QScopedPointer< Private > m_d
void setTransformFunction(const QPointF &mousePos, bool perspectiveModifierActive, bool shiftModifierActive)
void continuePrimaryAction(KoPointerEvent *event) override
KisLiquifyTransformStrategy(const KisCoordinatesConverter *converter, ToolTransformArgs &currentArgs, TransformTransactionProperties &transaction, const KoCanvasResourceProvider *manager)
void requestCursorOutlineUpdate(const QPointF &imagePoint)
QPainterPath getCursorOutline() const override
bool endPrimaryAction(KoPointerEvent *event) override
bool beginPrimaryAction(KoPointerEvent *event) override
static qreal scaleFromAffineMatrix(const QTransform &t)
static QTransform imageToFlakeTransform(const KisCoordinatesConverter *converter)
static bool thumbnailTooSmall(const QTransform &resultThumbTransform, const QRect &originalImageRect)
QPointF point
The point in document coordinates.
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
void KRITAUI_EXPORT setCursorPos(const QPoint &point)
const KisCoordinatesConverter * converter
standard members ///
QPointF imageToThumb(const QPointF &pt, bool useFlakeOptimization)
Private(KisLiquifyTransformStrategy *_q, const KisCoordinatesConverter *_converter, ToolTransformArgs &_currentArgs, TransformTransactionProperties &_transaction, const KoCanvasResourceProvider *_manager)
AlternateAction
Definition kis_tool.h:134
@ SampleFgImage
Definition kis_tool.h:139
@ SampleFgNode
Definition kis_tool.h:137
@ ChangeSizeSnap
Definition kis_tool.h:136
@ SampleBgImage
Definition kis_tool.h:140
@ SampleBgNode
Definition kis_tool.h:138
@ ChangeSize
Definition kis_tool.h:135