Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_custom_brush_widget.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2005 Bart Coppens <kde@bartcoppens.be>
3 * SPDX-FileCopyrightText: 2010 Lukáš Tvrdý <lukast.dev@gmail.com>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
9
10#include <kis_debug.h>
11#include <QImage>
12#include <QCheckBox>
13#include <QUuid>
14
15#include <QPixmap>
16#include <QPushButton>
17#include <QShowEvent>
18
19#include <KoResourcePaths.h>
20
21#include "kis_image.h"
22#include "kis_layer.h"
23#include "kis_paint_device.h"
24#include "kis_gbr_brush.h"
25#include "kis_imagepipe_brush.h"
27
29#include "kis_paint_layer.h"
30#include "kis_group_layer.h"
31#include <kis_selection.h>
32#include <KoProperties.h>
33#include "kis_iterator_ng.h"
34#include "KisImageBarrierLock.h"
36
37#include <kstandardguiitem.h>
38
39KisCustomBrushWidget::KisCustomBrushWidget(QWidget *parent, const QString& caption, KisImageWSP image)
40 : KisWdgCustomBrush(parent)
41 , m_image(image)
42{
43 setWindowTitle(caption);
44 preview->setScaledContents(false);
45 preview->setFixedSize(preview->size());
46 preview->setStyleSheet("border: 2px solid #222; border-radius: 4px; padding: 5px; font: normal 10px;");
47
49
50 m_brush = 0;
51
52 connect(this, SIGNAL(accepted()), SLOT(slotAddPredefined()));
53 connect(brushStyle, SIGNAL(activated(int)), this, SLOT(slotUpdateCurrentBrush(int)));
54 connect(colorAsMask, SIGNAL(toggled(bool)), this, SLOT(slotUpdateUseColorAsMask(bool)));
55 connect(preserveAlpha, SIGNAL(toggled(bool)), this, SLOT(slotUpdateCurrentBrush()));
56 connect(comboBox2, SIGNAL(currentIndexChanged(int)), this, SLOT(slotUpdateCurrentBrush(int)));
57
58
59 colorAsMask->setChecked(true); // use color as mask by default. This is by far the most common way to make tip.
60 spacingWidget->setSpacing(true, 1.0);
61 connect(spacingWidget, SIGNAL(sigSpacingChanged()), SLOT(slotSpacingChanged()));
62
63 KGuiItem::assign(buttonBox->button(QDialogButtonBox::Save), KStandardGuiItem::save());
64 KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel());
65}
66
70
71
76
82
87
89{
90 QImage brushImage = m_brush ? m_brush->brushTipImage() : QImage();
91
92 if (!brushImage.isNull()) {
93 int w = preview->size().width() - 10; // 10 for the padding...
94 brushImage = brushImage.scaled(w, w, Qt::KeepAspectRatio);
95 }
96
97 preview->setPixmap(QPixmap::fromImage(brushImage));
98}
99
101{
102 if (brushStyle->currentIndex() == 0) {
103 comboBox2->setEnabled(false);
104 } else {
105 comboBox2->setEnabled(true);
106 }
107 if (m_image) {
108 createBrush();
110 }
111}
112
114{
115 if (m_brush) {
116 m_brush->setSpacing(spacingWidget->spacing());
117 m_brush->setAutoSpacing(spacingWidget->autoSpacingActive(), spacingWidget->autoSpacingCoeff());
118 }
119}
120
122{
123 preserveAlpha->setEnabled(useColorAsMask);
125}
126
128{
129 QString suffix = ".gbr";
130 if (brushStyle->currentIndex() != 0) {
131 suffix = ".gih";
132 }
133 if (QFileInfo(m_rServer->saveLocation() + "/" + nameLineEdit->text().split(" ").join("_")
134 + suffix).exists()) {
135 buttonBox->button(QDialogButtonBox::Save)->setText(i18n("Overwrite"));
136 } else {
137 buttonBox->button(QDialogButtonBox::Save)->setText(i18n("Save"));
138 }
139}
140
141
143{
145
146 QString name = nameLineEdit->text();
147
148 if (nameLineEdit->text().isEmpty()) {
149 name = QUuid::createUuid().toByteArray().toHex();
150 }
151
152 // Add it to the brush server, so that it automatically gets to the mediators, and
153 // so to the other brush choosers can pick it up, if they want to
154 if (m_rServer && m_brush) {
155
156
157 if (m_brush->clone().dynamicCast<KisGbrBrush>()) {
158 KisGbrBrushSP resource = m_brush->clone().dynamicCast<KisGbrBrush>();
159 resource->setName(name);
160 resource->setFilename(resource->name().split(" ").join("_") + resource->defaultFileExtension());
162 Q_EMIT sigNewPredefinedBrush(resource);
163 }
164 }
165 else {
166 KisImagePipeBrushSP resource = m_brush->clone().dynamicCast<KisImagePipeBrush>();
167 resource->setName(name);
168 resource->setFilename(resource->name().split(" ").join("_") + resource->defaultFileExtension());
170 Q_EMIT sigNewPredefinedBrush(resource);
171 }
172 }
173 }
174
175 close();
176}
177
179{
180 if (!m_image)
181 return;
182
183 if (brushStyle->currentIndex() == 0) {
185
186 // create copy of the data
189 m_image->unlock();
190
191 if (!selection) {
192 m_brush = KisBrushSP(new KisGbrBrush(dev, 0, 0, m_image->width(), m_image->height()));
193 }
194 else {
195 // apply selection mask
196 QRect r = selection->selectedExactRect();
197
198 KisHLineIteratorSP pixelIt = dev->createHLineIteratorNG(r.x(), r.top(), r.width());
199 KisHLineConstIteratorSP maskIt = selection->projection()->createHLineIteratorNG(r.x(), r.top(), r.width());
200
201 for (qint32 y = r.top(); y <= r.bottom(); ++y) {
202
203 do {
204 dev->colorSpace()->applyAlphaU8Mask(pixelIt->rawData(), maskIt->oldRawData(), 1);
205 } while (pixelIt->nextPixel() && maskIt->nextPixel());
206
207 pixelIt->nextRow();
208 maskIt->nextRow();
209 }
210 m_brush = KisBrushSP(new KisGbrBrush(dev, r.x(), r.y(), r.width(), r.height()));
211 }
212
213 }
214 else {
215 // For each layer in the current image, create a new image, and add it to the list
217 devices.push_back(QVector<KisPaintDevice*>());
218 int w = m_image->width();
219 int h = m_image->height();
220
221 KisImageReadOnlyBarrierLock lock(m_image);
222
223 // We only loop over the rootLayer. Since we actually should have a layer selection
224 // list, no need to elaborate on that here and now
225 KoProperties properties;
226 properties.setProperty("visible", true);
227 QList<KisNodeSP> layers = m_image->root()->childNodes(QStringList("KisLayer"), properties);
228 KisNodeSP node;
229 Q_FOREACH (KisNodeSP node, layers) {
230 devices[0].push_back(node->projection().data());
231 }
232
234
235 switch (comboBox2->currentIndex()) {
236 case 0: modes.push_back(KisParasite::Constant); break;
237 case 1: modes.push_back(KisParasite::Random); break;
238 case 2: modes.push_back(KisParasite::Incremental); break;
239 case 3: modes.push_back(KisParasite::Pressure); break;
240 case 4: modes.push_back(KisParasite::Angular); break;
241 default: modes.push_back(KisParasite::Incremental);
242 }
243
244 m_brush = KisBrushSP(new KisImagePipeBrush(m_image->objectName(), w, h, devices, modes));
245 }
246 if (colorAsMask->isChecked()) {
247 static_cast<KisGbrBrush*>(m_brush.data())->makeMaskImage(preserveAlpha->isChecked());
248 static_cast<KisGbrBrush*>(m_brush.data())->setBrushApplication(preserveAlpha->isChecked() ? LIGHTNESSMAP : ALPHAMASK);
249 } else {
250 static_cast<KisGbrBrush*>(m_brush.data())->setBrushApplication(IMAGESTAMP);
251 }
252 m_brush->setSpacing(spacingWidget->spacing());
253 m_brush->setAutoSpacing(spacingWidget->autoSpacingActive(), spacingWidget->autoSpacingCoeff());
254 m_brush->setFilename(TEMPORARY_FILENAME);
256 m_brush->setValid(true);
257}
QList< QString > QStringList
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
virtual const quint8 * oldRawData() const =0
virtual bool nextPixel()=0
static KisBrushServerProvider * instance()
KoResourceServer< KisBrush > * brushServer()
void sigNewPredefinedBrush(KoResourceSP)
void showEvent(QShowEvent *) override
KisCustomBrushWidget(QWidget *parent, const QString &caption, KisImageWSP image)
KoResourceServer< KisBrush > * m_rServer
void slotUpdateUseColorAsMask(bool useColorAsMask)
void setImage(KisImageWSP image)
virtual void nextRow()=0
void unlock()
Definition kis_image.cc:805
void barrierLock(bool readOnly=false)
Wait until all the queued background jobs are completed and lock the image.
Definition kis_image.cc:756
KisPaintDeviceSP projection() const
qint32 width() const
qint32 height() const
KisSelectionSP globalSelection() const
Definition kis_image.cc:695
KisHLineIteratorSP createHLineIteratorNG(qint32 x, qint32 y, qint32 w)
const KoColorSpace * colorSpace() const
static bool addResourceWithUserInput(QWidget *widgetParent, KoResourceSP resource, QString storageLocation="")
virtual void applyAlphaU8Mask(quint8 *pixels, const quint8 *alpha, qint32 nPixels) const =0
void setProperty(const QString &name, const QVariant &value)
static QString saveLocation(const QString &type, const QString &suffix=QString(), bool create=true)
QString saveLocation()
Returns path where to save user defined and imported resources to.
QSharedPointer< KisBrush > KisBrushSP
Definition kis_brush.h:49
@ ALPHAMASK
Definition kis_brush.h:39
@ IMAGESTAMP
Definition kis_brush.h:40
@ LIGHTNESSMAP
Definition kis_brush.h:41
const QString TEMPORARY_FILENAME
const QString TEMPORARY_BRUSH_NAME
const QString Brushes
virtual KisPaintDeviceSP projection() const =0
QList< KisNodeSP > childNodes(const QStringList &nodeTypes, const KoProperties &properties) const
Definition kis_node.cpp:439
KisPixelSelectionSP projection() const
QRect selectedExactRect() const
Slow, but exact way of determining the rectangle that encloses the selection.