Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_imagepipe_brush.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2004 Boudewijn Rempt <boud@valdyas.org>
3 * SPDX-FileCopyrightText: 2005 Bart Coppens <kde@bartcoppens.be>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
9#include "kis_brushes_pipe.h"
11
12class KisImageBrushesPipe : public KisBrushesPipe<KisGbrBrush>
13{
14public:
20
21
22 /*
23 pre and post are split because:
24
25 21:12:20 < dmitryK> boud: i guess it was somehow related to the fact that the maskWidth/maskHeight should
26 correspond to the size of the mask returned by paintDevice()
27 21:13:33 < dmitryK> boud: the random stuff is called once per brush->paintDevice() call, after the device is
28 returned to the paint op, that is "preparing the randomness for the next call"
29 21:14:16 < dmitryK> boud: and brushesPipe->currentBrush() always returning the same brush for any particular
30 paintInfo.
31 */
32protected:
34 int index, int rank,
35 const KisPaintInformation& info) {
36
37 qreal angle;
38 qreal velocity;
39 qreal capSpeed = 3;
40
41 switch (mode) {
45 break;
47 index = static_cast<int>(info.pressure() * (rank - 1) + 0.5);
48 break;
50 // + M_PI_2 + M_PI_4 to be compatible with the gimp
51 angle = info.drawingAngle() + M_PI_2 + M_PI_4;
52 angle = normalizeAngle(angle);
53
54 index = static_cast<int>(angle / (2.0 * M_PI) * rank);
55 break;
57 index = qRound(info.xTilt() / 2.0 * rank) + rank / 2;
58 break;
60 index = qRound(info.yTilt() / 2.0 * rank) + rank / 2;
61 break;
63 // log is slow, but allows for nicer dab transition
64 velocity = log(info.drawingSpeed() + 1);
65 if (velocity > capSpeed) {
66 velocity = capSpeed;
67 }
68 velocity /= capSpeed;
69 velocity *= (rank - 1) + 0.5;
70 index = qRound(velocity);
71 break;
72 default:
73 warnImage << "Parasite" << mode << "is not implemented";
74 index = 0;
75 }
76
77 return index;
78 }
79
81 int index, int rank,
82 KisRandomSourceSP randomSource,
83 int seqNo) {
84
85 switch (mode) {
86 case KisParasite::Constant: break;
88 index = (seqNo >= 0 ? seqNo : (index + 1)) % rank;
89 break;
91 index = randomSource->generate(0, rank-1);
92 break;
95 break;
99 break;
100 default:
101 warnImage << "Parasite" << mode << "is not implemented";
102 index = 0;
103 }
104
105 return index;
106 }
107
108 int chooseNextBrush(const KisPaintInformation& info) override {
109 quint32 brushIndex = 0;
110
113 }
114
115 for (int i = 0; i < m_parasite.dim; i++) {
116 int index = selectPre(m_parasite.selection[i],
117 m_parasite.index[i],
118 m_parasite.rank[i], info);
119
120 brushIndex += m_parasite.brushesCount[i] * index;
121 }
122 brushIndex %= (quint32)m_brushes.size();
123 m_currentBrushIndex = brushIndex;
124 return brushIndex;
125 }
126
127 void updateBrushIndexes(KisRandomSourceSP randomSource, int seqNo) override {
128 m_isInitialized = true;
129
130 for (int i = 0; i < m_parasite.dim; i++) {
132 m_parasite.index[i],
133 m_parasite.rank[i],
134 randomSource,
135 seqNo);
136 }
137 }
138
139public:
142
143 int currentBrushIndex() override {
144 return m_currentBrushIndex;
145 }
146
150
152 return m_parasite;
153 }
154
156 Q_FOREACH (KisGbrBrushSP brush, m_brushes) {
157 brush->setAdjustmentMidPoint(value);
158 }
159 }
160
162 Q_FOREACH (KisGbrBrushSP brush, m_brushes) {
163 brush->setBrightnessAdjustment(value);
164 }
165 }
166
168 Q_FOREACH (KisGbrBrushSP brush, m_brushes) {
169 brush->setContrastAdjustment(value);
170 }
171 }
172
174 Q_FOREACH (KisGbrBrushSP brush, m_brushes) {
175 brush->setAutoAdjustMidPoint(value);
176 }
177 }
178
179 void makeMaskImage(bool preserveAlpha) {
180 Q_FOREACH (KisGbrBrushSP brush, m_brushes) {
181 brush->makeMaskImage(preserveAlpha);
182 }
183 }
184
185 bool saveToDevice(QIODevice* dev) const {
186 Q_FOREACH (KisGbrBrushSP brush, m_brushes) {
187 if (!brush->saveToDevice(dev)) {
188 return false;
189 }
190 }
191 return true;
192 }
193
195 Q_FOREACH (KisGbrBrushSP brush, m_brushes) {
196 brush->coldInitBrush();
197 }
198 }
199
200 void notifyStrokeStarted() override {
201 m_isInitialized = false;
202 }
203
204private:
208};
209
210
215
217 : KisGbrBrush(filename)
218 , d(new Private())
219{
220}
221
222KisImagePipeBrush::KisImagePipeBrush(const QString& name, int w, int h,
225 : KisGbrBrush(QString())
226 , d(new Private())
227{
228 Q_ASSERT(devices.count() == modes.count());
229 Q_ASSERT(devices.count() > 0);
230 Q_ASSERT(devices.count() < 2); // XXX Multidimensionals not supported yet, change to MaxDim!
231
232 setName(name);
233
235
236 parasite.dim = devices.count();
237 // XXX Change for multidim! :
238 parasite.ncells = devices.at(0).count();
239 parasite.rank[0] = parasite.ncells; // ### This can masquerade some bugs, be careful here in the future
240 parasite.selection[0] = modes.at(0);
241
242
243 // XXX needsmovement!
244
246
248 setDevices(devices, w, h);
249 setBrushTipImage(d->brushesPipe.firstBrush()->brushTipImage());
250}
251
253 : KisGbrBrush(rhs),
254 d(new Private(*rhs.d))
255{
256}
257
259{
260 return KoResourceSP(new KisImagePipeBrush(*this));
261}
262
267
268bool KisImagePipeBrush::loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface)
269{
270 Q_UNUSED(resourcesInterface);
271
272 QByteArray data = dev->readAll();
273 return initFromData(data);
274}
275
276bool KisImagePipeBrush::initFromData(const QByteArray &data)
277{
278 if (data.size() == 0) return false;
279 // XXX: this doesn't correctly load the image pipe brushes yet.
280
281 // XXX: This stuff is in utf-8, too.
282 // The first line contains the name -- this means we look until we arrive at the first newline
283 QByteArray line1;
284
285 qint32 i = 0;
286
287 while (i < data.size() && data[i] != '\n') {
288 line1.append(data[i]);
289 i++;
290 }
291 setName(QString::fromUtf8(line1, line1.size()));
292
293 i++; // Skip past the first newline
294
295 // The second line contains the number of brushes, separated by a space from the parasite
296
297 // XXX: This stuff is in utf-8, too.
298 QByteArray line2;
299 while (i < data.size() && data[i] != '\n') {
300 line2.append(data[i]);
301 i++;
302 }
303
304 QString paramline = QString::fromUtf8(line2, line2.size());
305 qint32 numOfBrushes = paramline.left(paramline.indexOf(' ')).toUInt();
306 QString parasiteString = paramline.mid(paramline.indexOf(' ') + 1);
307
310
311 parasiteSelectionString = parasite.selectionMode; // selection mode to return to UI
312
314 i++; // Skip past the second newline
315
316 for (int brushIndex = d->brushesPipe.sizeBrush();
317 brushIndex < numOfBrushes && i < data.size(); brushIndex++) {
318
319 KisGbrBrushSP brush = KisGbrBrushSP(new KisGbrBrush(name() + '_' + QString().setNum(brushIndex),
320 data,
321 i));
322
323 d->brushesPipe.addBrush(brush);
324 }
325
326 if (numOfBrushes > 0) {
327 setValid(true);
328 setSpacing(d->brushesPipe.lastBrush()->spacing());
329 setWidth(d->brushesPipe.firstBrush()->width());
330 setHeight(d->brushesPipe.firstBrush()->height());
331 setBrushTipImage(d->brushesPipe.firstBrush()->brushTipImage());
332 setBrushApplication(d->brushesPipe.firstBrush()->brushApplication());
335 }
336
337 return true;
338}
339
340bool KisImagePipeBrush::saveToDevice(QIODevice* dev) const
341{
342 QByteArray utf8Name = name().toUtf8(); // Names in v2 brushes are in UTF-8
343 char const* name = utf8Name.data();
344 int len = qstrlen(name);
345
347 warnImage << "Save to file for pipe brushes with dim != not yet supported!";
348 return false;
349 }
350
351 // Save this pipe brush: first the header, and then all individual brushes consecutively
352 // XXX: this needs some care for when we have > 1 dimension)
353
354 // Gimp Pipe Brush header format: Name\n<number of brushes> <parasite>\n
355
356 // The name\n
357 if (dev->write(name, len) == -1)
358 return false;
359
360 if (!dev->putChar('\n'))
361 return false;
362
363 // Write the parasite (also writes number of brushes)
364 if (!d->brushesPipe.parasite().saveToDevice(dev))
365 return false;
366
367 if (!dev->putChar('\n'))
368 return false;
369
370 // <gbr brushes>
371 return d->brushesPipe.saveToDevice(dev);
372}
373
378
380{
381 d->brushesPipe.prepareForSeqNo(info, seqNo);
382}
383
385 KisDabShape const& shape,
386 const KisPaintInformation& info,
387 double subPixelX , double subPixelY,
388 qreal softnessFactor, qreal lightnessStrength) const
389{
390 d->brushesPipe.generateMaskAndApplyMaskOrCreateDab(dst, coloringInformation, shape, info, subPixelX, subPixelY, softnessFactor, lightnessStrength);
391}
392
397
402
404 const KoColorSpace * colorSpace,
405 KisDabShape const& shape,
406 const KisPaintInformation& info,
407 double subPixelX, double subPixelY) const
408{
409 return d->brushesPipe.paintDevice(colorSpace, shape, info, subPixelX, subPixelY);
410}
411
416
417void KisImagePipeBrush::makeMaskImage(bool preserveAlpha)
418{
419 KisGbrBrush::makeMaskImage(preserveAlpha);
420 d->brushesPipe.makeMaskImage(preserveAlpha);
422}
423
429
435
441
447
449{
450 Q_UNUSED(forcePreciseOutline);
451
453 Q_ASSERT(brush);
454
455 return brush->outline();
456}
457
459{
460 return (!d->brushesPipe.parasite().needsMovement || info.drawingDistance() >= 0.5);
461}
462
464{
465 return QString(".gih");
466}
467
469{
471}
472
473qint32 KisImagePipeBrush::maskWidth(KisDabShape const& shape, double subPixelX, double subPixelY, const KisPaintInformation& info) const
474{
475 return d->brushesPipe.maskWidth(shape, subPixelX, subPixelY, info);
476}
477
478qint32 KisImagePipeBrush::maskHeight(KisDabShape const& shape, double subPixelX, double subPixelY, const KisPaintInformation& info) const
479{
480 return d->brushesPipe.maskHeight(shape, subPixelX, subPixelY, info);
481}
482
484{
485 KisGbrBrush::setAngle(_angle);
486 d->brushesPipe.setAngle(_angle);
487}
488
490{
491 KisGbrBrush::setScale(_scale);
492 d->brushesPipe.setScale(_scale);
493}
494
495void KisImagePipeBrush::setSpacing(double _spacing)
496{
497 KisGbrBrush::setSpacing(_spacing);
498 d->brushesPipe.setSpacing(_spacing);
499}
500
502{
503 //Set all underlying brushes to use the same brush Application
506}
507
509 //Set all underlying brushes to use the same gradient
510 KisGbrBrush::setGradient(gradient);
511 d->brushesPipe.setGradient(gradient);
512}
513
518
519
524
528
533
535{
536
537 for (int i = 0; i < devices.at(0).count(); i++) {
538 d->brushesPipe.addBrush(KisGbrBrushSP(new KisGbrBrush(devices.at(0).at(i), 0, 0, w, h)));
539 }
540}
541
float value(const T *src, size_t ch)
virtual void setGradient(KoAbstractGradientSP gradient)
virtual enumBrushApplication brushApplication() const
virtual void setSpacing(double spacing)
virtual void setAngle(qreal _angle)
virtual void setBrushType(enumBrushType type)
virtual void setScale(qreal _scale)
void setWidth(qint32 width)
void setHeight(qint32 height)
virtual void setBrushApplication(enumBrushApplication brushApplication)
QSharedPointer< BrushType > firstBrush() const
QVector< QSharedPointer< KisGbrBrush > > m_brushes
void setSpacing(double spacing)
QSharedPointer< BrushType > currentBrush(const KisPaintInformation &info)
KisFixedPaintDeviceSP paintDevice(const KoColorSpace *colorSpace, KisDabShape const &shape, const KisPaintInformation &info, double subPixelX, double subPixelY)
QSharedPointer< BrushType > lastBrush() const
qint32 maskWidth(KisDabShape const &shape, double subPixelX, double subPixelY, const KisPaintInformation &info)
void notifyBrushIsGoingToBeClonedForStroke()
bool hasColorAndTransparency() const
void setGradient(KoAbstractGradientSP gradient) const
void addBrush(QSharedPointer< KisGbrBrush > brush)
void generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, KisBrush::ColoringInformation *coloringInformation, KisDabShape const &shape, const KisPaintInformation &info, double subPixelX, double subPixelY, qreal softnessFactor, qreal lightnessStrength=DEFAULT_LIGHTNESS_STRENGTH)
QVector< QSharedPointer< BrushType > > brushes()
void prepareForSeqNo(const KisPaintInformation &info, int seqNo)
bool isImageType() const
qint32 maskHeight(KisDabShape const &shape, double subPixelX, double subPixelY, const KisPaintInformation &info)
void setBrushApplication(enumBrushApplication brushApplication) const
void setAngle(qreal angle)
void setScale(qreal scale)
void testingSelectNextBrush(const KisPaintInformation &info)
virtual void setAdjustmentMidPoint(quint8 value)
virtual void setContrastAdjustment(qreal value)
virtual void setBrightnessAdjustment(qreal value)
virtual void setAutoAdjustMidPoint(bool autoAdjustMidPoint)
void setHasColorAndTransparency(bool value)
void setBrushTipImage(const QImage &image) override
virtual void makeMaskImage(bool preserveAlpha)
KisGbrBrush(const QString &filename)
Construct brush to load filename later as brush.
void setAdjustmentMidPoint(quint8 value)
void updateBrushIndexes(KisRandomSourceSP randomSource, int seqNo) override
bool saveToDevice(QIODevice *dev) const
void setParasite(const KisPipeBrushParasite &parasite)
void makeMaskImage(bool preserveAlpha)
const KisPipeBrushParasite & parasite() const
void setAutoAdjustMidPoint(bool value)
void setContrastAdjustment(qreal value)
KisPipeBrushParasite m_parasite
int chooseNextBrush(const KisPaintInformation &info) override
static int selectPost(KisParasite::SelectionMode mode, int index, int rank, KisRandomSourceSP randomSource, int seqNo)
static int selectPre(KisParasite::SelectionMode mode, int index, int rank, const KisPaintInformation &info)
void notifyStrokeStarted() override
void setBrightnessAdjustment(qreal value)
void coldInitBrush() override
QString defaultFileExtension() const override
void setDevices(QVector< QVector< KisPaintDevice * > > devices, int w, int h)
void setAngle(qreal _angle) override
void setBrightnessAdjustment(qreal value) override
void setScale(qreal _scale) override
qint32 maskWidth(KisDabShape const &, double subPixelX, double subPixelY, const KisPaintInformation &info) const override
bool saveToDevice(QIODevice *dev) const override
QVector< KisGbrBrushSP > brushes() const
KisGbrBrushSP testingGetCurrentBrush(const KisPaintInformation &info) const
KisOptimizedBrushOutline outline(bool forcePreciseOutline=false) const override
void prepareForSeqNo(const KisPaintInformation &info, int seqNo) override
void notifyStrokeStarted() override
quint32 brushIndex() const override
void makeMaskImage(bool preserveAlpha) override
const KisPipeBrushParasite & parasite() const
KisImagePipeBrush(const QString &filename)
KoResourceSP clone() const override
virtual void setBrushApplication(enumBrushApplication brushApplication) override
void setAdjustmentMidPoint(quint8 value) override
void setContrastAdjustment(qreal value) override
void setSpacing(double _spacing) override
bool initFromData(const QByteArray &data)
void notifyBrushIsGoingToBeClonedForStroke() override
virtual void setGradient(KoAbstractGradientSP gradient) override
bool loadFromDevice(QIODevice *dev, KisResourcesInterfaceSP resourcesInterface) override
bool canPaintFor(const KisPaintInformation &info) override
qint32 maskHeight(KisDabShape const &, double subPixelX, double subPixelY, const KisPaintInformation &info) const override
void setParasite(const KisPipeBrushParasite &parasite)
KisFixedPaintDeviceSP paintDevice(const KoColorSpace *colorSpace, KisDabShape const &, const KisPaintInformation &info, double subPixelX=0, double subPixelY=0) const override
void setAutoAdjustMidPoint(bool value) override
void generateMaskAndApplyMaskOrCreateDab(KisFixedPaintDeviceSP dst, KisBrush::ColoringInformation *coloringInformation, KisDabShape const &, const KisPaintInformation &info, double subPixelX=0, double subPixelY=0, qreal softnessFactor=DEFAULT_SOFTNESS_FACTOR, qreal lightnessStrength=DEFAULT_LIGHTNESS_STRENGTH) const override
void testingSelectNextBrush(const KisPaintInformation &info) const
KisRandomSourceSP randomSource() const
qreal xTilt() const
The tilt of the pen on the horizontal axis (from 0.0 to 1.0)
qreal yTilt() const
The tilt of the pen on the vertical axis (from 0.0 to 1.0)
qreal pressure() const
The pressure of the value (from 0.0 to 1.0)
qreal drawingAngle(bool considerLockedAngle=false) const
KisParasite::SelectionMode selection[MaxDim]
bool saveToDevice(QIODevice *dev) const
void setBrushesCount()
Initializes the brushesCount helper.
bool needsMovement
If true, the brush won't be painted when there is no motion.
qint32 brushesCount[MaxDim]
The total count of brushes in each dimension (helper)
qint32 index[MaxDim]
The current index in each dimension, so that the selection modes know where to start.
qint64 generate() const
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
@ PIPE_IMAGE
Definition kis_brush.h:35
@ PIPE_MASK
Definition kis_brush.h:34
enumBrushApplication
Definition kis_brush.h:38
#define warnImage
Definition kis_debug.h:88
QSharedPointer< KisGbrBrush > KisGbrBrushSP
std::enable_if< std::is_floating_point< T >::value, T >::type normalizeAngle(T a)
Definition kis_global.h:121
#define M_PI
Definition kis_global.h:111
QSharedPointer< KoResource > KoResourceSP
void setValid(bool valid)
void setName(const QString &name)
QString name