Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_tool_gradient.cc
Go to the documentation of this file.
1/*
2 * kis_tool_gradient.cc - part of Krita
3 *
4 * SPDX-FileCopyrightText: 2002 Patrick Julien <freak@codepimps.org>
5 * SPDX-FileCopyrightText: 2003 Boudewijn Rempt <boud@valdyas.org>
6 * SPDX-FileCopyrightText: 2004-2007 Adrian Page <adrian@pagenet.plus.com>
7 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12#include "kis_tool_gradient.h"
13
14#include <cfloat>
15
16#include <QApplication>
17#include <QPainter>
18#include <QLabel>
19#include <QLayout>
20#include <QCheckBox>
21
22#include <kis_transaction.h>
23#include <kis_debug.h>
24#include <klocalizedstring.h>
25#include <kcombobox.h>
26
27
28#include <KoPointerEvent.h>
29#include <KoCanvasBase.h>
30#include <KoViewConverter.h>
31#include <KoUpdater.h>
32#include <KoProgressUpdater.h>
33
35#include <kis_painter.h>
37#include <kis_layer.h>
38#include <kis_selection.h>
39#include <kis_paint_layer.h>
40
41#include <canvas/kis_canvas2.h>
42#include <KisViewManager.h>
44#include <kis_slider_spin_box.h>
45#include <kis_cursor.h>
46#include <kis_config.h>
48#include "kis_command_utils.h"
51
52
54 : KisToolPaint(canvas, KisCursor::load("tool_gradient_cursor.png", 6, 6))
55{
56 setObjectName("tool_gradient");
57
58 m_startPos = QPointF(0, 0);
59 m_endPos = QPointF(0, 0);
60
61 m_dither = false;
62 m_reverse = false;
66
67 KisCanvas2 *kritaCanvas = dynamic_cast<KisCanvas2*>(canvas);
68
69 connect(kritaCanvas->viewManager()->canvasResourceProvider(), SIGNAL(sigEffectiveCompositeOpChanged()), SLOT(resetCursorStyle()));
70}
71
75
77{
78 if (isEraser()) {
79 useCursor(KisCursor::load("tool_gradient_eraser_cursor.png", 6, 6));
80 } else {
82 }
83
85}
86
87void KisToolGradient::activate(const QSet<KoShape*> &shapes)
88{
90 m_configGroup = KSharedConfig::openConfig()->group(toolId());
91}
92
93void KisToolGradient::paint(QPainter &painter, const KoViewConverter &converter)
94{
96 paintLine(painter);
97 }
98 KisToolPaint::paint(painter, converter);
99}
100
102{
103 if (!nodeEditable()) {
104 event->ignore();
105 return;
106 }
107
109
110 m_startPos = convertToPixelCoordAndSnap(event, QPointF(), false);
112}
113
115{
122 //CHECK_MODE_SANITY_OR_RETURN(KisTool::PAINT_MODE);
123
124 // First ensure the old guideline is deleted
126
127 QPointF pos = convertToPixelCoordAndSnap(event, QPointF(), false);
128
129 if (event->modifiers() == Qt::ShiftModifier) {
130 m_endPos = straightLine(pos);
131 } else {
132 m_endPos = pos;
133 }
134
136}
137
139{
140 Q_UNUSED(event);
143
144 if (!currentNode())
145 return;
146
147 if (m_startPos == m_endPos) {
148 return;
149 }
150
151 KisImageSP image = this->image();
152
153 KisResourcesSnapshotSP resources =
154 new KisResourcesSnapshot(image, currentNode(), this->canvas()->resourceManager());
155
156 if (image && resources->currentNode()->paintDevice()) {
157 // TODO: refactor out local variables when we switch to C++14
158 QPointF startPos = m_startPos;
159 QPointF endPos = m_endPos;
162 bool reverse = m_reverse;
163 double antiAliasThreshold = m_antiAliasThreshold;
164 bool dither = m_dither;
165
166 KUndo2MagicString actionName = kundo2_i18n("Gradient");
167 KisProcessingApplicator applicator(image, resources->currentNode(),
170 actionName);
171
172 applicator.applyCommand(
174 [resources, startPos, endPos,
175 shape, repeat, reverse, antiAliasThreshold, dither] () mutable {
176
177 KisNodeSP node = resources->currentNode();
178 KisPaintDeviceSP device = node->paintDevice();
180 const QRect bounds = device->defaultBounds()->bounds();
181
182 KisGradientPainter painter(device, resources->activeSelection());
183 resources->setupPainter(&painter);
184 painter.setProgress(helper.updater());
185
186 painter.beginTransaction();
187
188 painter.setGradientShape(shape);
189 painter.paintGradient(startPos, endPos,
190 repeat, antiAliasThreshold,
191 reverse, 0, 0,
192 bounds.width(), bounds.height(),
193 dither);
194
195 return painter.endAndTakeTransaction();
196 }));
197 applicator.end();
198 }
199
201}
202
203QPointF KisToolGradient::straightLine(QPointF point)
204{
205 QPointF comparison = point - m_startPos;
206 QPointF result;
207
208 if (fabs(comparison.x()) > fabs(comparison.y())) {
209 result.setX(point.x());
210 result.setY(m_startPos.y());
211 } else {
212 result.setX(m_startPos.x());
213 result.setY(point.y());
214 }
215
216 return result;
217}
218
220{
221 QPointF viewStartPos = pixelToView(m_startPos);
222 QPointF viewStartEnd = pixelToView(m_endPos);
223
224 if (canvas()) {
225 QPainterPath path;
226 path.moveTo(viewStartPos);
227 path.lineTo(viewStartEnd);
228 paintToolOutline(&gc, path);
229 }
230}
231
233{
234 if (canvas()) {
235 QRectF bound(m_startPos, m_endPos);
236 canvas()->updateCanvas(convertToPt(bound.normalized().adjusted(-3, -3, 3, 3)));
237 }
238}
239
241{
242 QWidget *widget = KisToolPaint::createOptionWidget();
243 Q_CHECK_PTR(widget);
244 widget->setObjectName(toolId() + " option widget");
245
246
247 // Make sure to create the connections last after everything is set up. The initialized values
248 // won't be loaded from the configuration file if you add the widget before the connection
249 m_lbShape = new QLabel(i18n("Shape:"), widget);
250 m_cmbShape = new KComboBox(widget);
251 m_cmbShape->setObjectName("shape_combo");
252 m_cmbShape->addItem(i18nc("the gradient will be drawn linearly", "Linear"));
253 m_cmbShape->addItem(i18nc("the gradient will be drawn bilinearly", "Bi-Linear"));
254 m_cmbShape->addItem(i18nc("the gradient will be drawn radially", "Radial"));
255 m_cmbShape->addItem(i18nc("the gradient will be drawn in a square around a centre", "Square"));
256 m_cmbShape->addItem(i18nc("the gradient will be drawn as an asymmetric cone", "Conical"));
257 m_cmbShape->addItem(i18nc("the gradient will be drawn as a symmetric cone", "Conical Symmetric"));
258 m_cmbShape->addItem(i18nc("the gradient will be drawn as a spiral", "Spiral"));
259 m_cmbShape->addItem(i18nc("the gradient will be drawn as a reverse spiral", "Reverse Spiral"));
260 m_cmbShape->addItem(i18nc("the gradient will be drawn in a selection outline", "Shaped"));
262 connect(m_cmbShape, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSetShape(int)));
263
264 m_lbRepeat = new QLabel(i18n("Repeat:"), widget);
265 m_cmbRepeat = new KComboBox(widget);
266 m_cmbRepeat->setObjectName("repeat_combo");
267 m_cmbRepeat->addItem(i18nc("The gradient will not repeat", "None"));
268 m_cmbRepeat->addItem(i18nc("The gradient will repeat forwards", "Forwards"));
269 m_cmbRepeat->addItem(i18nc("The gradient will repeat alternatingly", "Alternating"));
271 connect(m_cmbRepeat, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSetRepeat(int)));
272
273
274 m_lbAntiAliasThreshold = new QLabel(i18n("Anti-alias threshold:"), widget);
276 m_slAntiAliasThreshold->setObjectName("threshold_slider");
278 m_slAntiAliasThreshold->setSingleStep(0.001);
280 connect(m_slAntiAliasThreshold, SIGNAL(valueChanged(qreal)), this, SLOT(slotSetAntiAliasThreshold(qreal)));
281
282 m_ckReverse = new QCheckBox(i18nc("the gradient will be drawn with the color order reversed", "Reverse"), widget);
283 m_ckReverse->setObjectName("reverse_check");
284 connect(m_ckReverse, SIGNAL(toggled(bool)), this, SLOT(slotSetReverse(bool)));
286
287 m_ckDither = new QCheckBox(i18nc("the gradient will be dithered", "Dither"), widget);
288 m_ckDither->setObjectName("dither_check");
289 connect(m_ckDither, SIGNAL(toggled(bool)), this, SLOT(slotSetDither(bool)));
291
292 widget->setFixedHeight(widget->sizeHint().height());
293
294
295 // load configuration settings into widget (updating UI will update internal variables from signals/slots)
296 m_ckDither->setChecked(m_configGroup.readEntry<bool>("dither", false));
297 m_ckReverse->setChecked((bool)m_configGroup.readEntry("reverse", false));
298 m_cmbShape->setCurrentIndex((int)m_configGroup.readEntry("shape", 0));
299 m_cmbRepeat->setCurrentIndex((int)m_configGroup.readEntry("repeat", 0));
300 m_slAntiAliasThreshold->setValue((qreal)m_configGroup.readEntry("antialiasThreshold", 0.0));
301
302 return widget;
303}
304
306{
308 m_configGroup.writeEntry("shape", shape);
309}
310
312{
314 m_configGroup.writeEntry("repeat", repeat);
315}
316
318{
319 m_reverse = state;
320 m_configGroup.writeEntry("reverse", state);
321}
322
324{
325 m_dither = state;
326 m_configGroup.writeEntry("dither", state);
327}
328
330{
332 m_configGroup.writeEntry("antialiasThreshold", value);
333}
334
335
float value(const T *src, size_t ch)
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
void updateCanvas(const QRectF &rc) override
KisViewManager * viewManager() const
static QCursor load(const QString &cursorName, int hotspotX=-1, int hotspotY=-1)
virtual QRect bounds() const =0
This class is a spinbox in which you can click and drag to set the value. A slider like bar is displa...
void setValue(qreal newValue)
void setRange(qreal newMinimum, qreal newMaximum, int newNumberOfDecimals=0, bool computeNewFastSliderStep=true)
Set the minimum and the maximum values of the range.
KisDefaultBoundsBaseSP defaultBounds() const
KUndo2Command * endAndTakeTransaction()
void beginTransaction(const KUndo2MagicString &transactionName=KUndo2MagicString(), int timedID=-1)
Begin an undoable paint operation.
void setProgress(KoUpdater *progressUpdater)
void applyCommand(KUndo2Command *command, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
The KisResourcesSnapshot class takes a snapshot of the various resources like colors and settings use...
void setupPainter(KisPainter *painter)
KisSelectionSP activeSelection() const
void endPrimaryAction(KoPointerEvent *event) override
void resetCursorStyle() override
KisToolGradient(KoCanvasBase *canvas)
QLabel * m_lbAntiAliasThreshold
void continuePrimaryAction(KoPointerEvent *event) override
QWidget * createOptionWidget() override
KComboBox * m_cmbRepeat
void activate(const QSet< KoShape * > &shapes) override
KConfigGroup m_configGroup
KisGradientPainter::enumGradientShape m_shape
void beginPrimaryAction(KoPointerEvent *event) override
void paintLine(QPainter &gc)
KisDoubleSliderSpinBox * m_slAntiAliasThreshold
~KisToolGradient() override
QCheckBox * m_ckDither
QCheckBox * m_ckReverse
void slotSetAntiAliasThreshold(qreal)
KComboBox * m_cmbShape
QPointF straightLine(QPointF point)
void paint(QPainter &painter, const KoViewConverter &converter) override
KisGradientPainter::enumGradientRepeat m_repeat
QWidget * createOptionWidget() override
void activate(const QSet< KoShape * > &shapes) override
void paint(QPainter &gc, const KoViewConverter &converter) override
void setMode(ToolMode mode) override
virtual void addOptionWidgetOption(QWidget *control, QWidget *label=nullptr)
Add a widget and a label to the current option widget layout.
bool isEraser() const
KisCanvasResourceProvider * canvasResourceProvider()
Qt::KeyboardModifiers modifiers() const
Q_INVOKABLE QString toolId() const
void useCursor(const QCursor &cursor)
#define bounds(x, a, b)
#define CHECK_MODE_SANITY_OR_RETURN(_mode)
Definition kis_tool.h:27
KUndo2MagicString kundo2_i18n(const char *text)
virtual KisPaintDeviceSP paintDevice() const =0
The LambdaCommand struct is a shorthand for creation of AggregateCommand commands using C++ lambda fe...
bool paintGradient(const QPointF &gradientVectorStart, const QPointF &gradientVectorEnd, enumGradientRepeat repeat, double antiAliasThreshold, bool reverseGradient, qint32 startx, qint32 starty, qint32 width, qint32 height, bool useDithering=false)
void setGradientShape(enumGradientShape shape)
virtual ToolMode mode() const
Definition kis_tool.cc:407
virtual void resetCursorStyle()
Definition kis_tool.cc:613
bool nodeEditable()
Checks checks if the current node is editable.
Definition kis_tool.cc:651
QPointF pixelToView(const QPoint &pixelCoord) const
Definition kis_tool.cc:269
KisNodeSP currentNode() const
Definition kis_tool.cc:370
bool overrideCursorIfNotEditable()
Override the cursor appropriately if current node is not editable.
Definition kis_tool.cc:618
void paintToolOutline(QPainter *painter, const KisOptimizedBrushOutline &path)
Definition kis_tool.cc:589
QRectF convertToPt(const QRectF &rect)
Definition kis_tool.cc:252
KisImageWSP image() const
Definition kis_tool.cc:332
@ PAINT_MODE
Definition kis_tool.h:300
@ HOVER_MODE
Definition kis_tool.h:299
QPointF convertToPixelCoordAndSnap(KoPointerEvent *e, const QPointF &offset=QPointF(), bool useModifiers=true)
Definition kis_tool.cc:214
KisCanvas2 * canvas