24#ifdef HAVE_SYS_TYPES_H
30#include <netinet/in.h>
36#include <tqvaluevector.h>
39#include <tqstringlist.h>
40#include <tqtextstream.h>
44#include <tdeapplication.h>
46#include "kis_global.h"
47#include "kis_paint_device.h"
48#include "kis_imagepipe_brush.h"
50#include "kis_alpha_mask.h"
52#include "kis_meta_registry.h"
53#include "kis_colorspace_factory_registry.h"
59 TQRegExp basicSplitter(
" ",
true);
60 TQRegExp parasiteSplitter(
":",
true);
61 TQStringList parasites = TQStringList::split(basicSplitter, source);
62 for (uint i = 0; i < parasites.count(); i++) {
63 TQStringList splitted = TQStringList::split(parasiteSplitter, *parasites.at(i));
64 if (splitted.count() != 2) {
65 kdWarning(41001) <<
"Wrong count for this parasite key/value:" << *parasites.at(i) << endl;
68 TQString
index = *splitted.at(0);
70 dim = (*splitted.at(1)).toInt();
71 if (dim < 1 || dim > MaxDim) {
74 }
else if (
index.startsWith(
"sel")) {
75 int selIndex =
index.mid(strlen(
"sel")).toInt();
76 if (selIndex >= 0 && selIndex < dim) {
77 TQString selectionMode = *splitted.at(1);
78 if (selectionMode ==
"incremental")
79 selection[selIndex] = Incremental;
80 else if (selectionMode ==
"angular") {
81 selection[selIndex] = Angular;
83 }
else if (selectionMode ==
"random")
84 selection[selIndex] = Random;
85 else if (selectionMode ==
"pressure")
86 selection[selIndex] = Pressure;
87 else if (selectionMode ==
"xtilt")
88 selection[selIndex] = TiltX;
89 else if (selectionMode ==
"ytilt")
90 selection[selIndex] = TiltY;
92 selection[selIndex] = Constant;
94 kdWarning(41001)<<
"Sel: wrong index: " << selIndex <<
"(dim = " << dim <<
")" << endl;
96 }
else if (
index.startsWith(
"rank")) {
97 int rankIndex =
index.mid(strlen(
"rank")).toInt();
98 if (rankIndex < 0 || rankIndex > dim) {
99 kdWarning(41001) <<
"Rankindex out of range: " << rankIndex << endl;
102 rank[rankIndex] = (*splitted.at(1)).toInt();
103 }
else if (
index ==
"ncells") {
104 ncells = (*splitted.at(1)).toInt();
106 kdWarning(41001) <<
"ncells out of range: " << ncells << endl;
112 for (
int i = 0; i < dim; i++) {
122 for (
int i = 1; i < dim; i++) {
131 TQTextStream stream(dev);
133 stream << ncells <<
" ncells:" << ncells <<
" dim:" << dim;
135 for (
int i = 0; i < dim; i++) {
136 stream <<
" rank" << i <<
":" << rank[i] <<
" sel" << i <<
":";
137 switch (selection[i]) {
138 case Constant: stream <<
"constant";
break;
139 case Incremental: stream <<
"incremental";
break;
140 case Angular: stream <<
"angular";
break;
141 case Velocity: stream <<
"velocity";
break;
142 case Random: stream <<
"random";
break;
143 case Pressure: stream <<
"pressure";
break;
144 case TiltX: stream <<
"xtilt";
break;
145 case TiltY: stream <<
"ytilt";
break;
152KisImagePipeBrush::KisImagePipeBrush(
const TQString& filename) : super(filename)
154 m_brushType = INVALID;
159KisImagePipeBrush::KisImagePipeBrush(
const TQString& name,
int w,
int h,
160 TQValueVector< TQValueVector<KisPaintDevice*> > devices,
161 TQValueVector<KisPipeBrushParasite::SelectionMode> modes)
164 Q_ASSERT(devices.count() == modes.count());
165 Q_ASSERT(devices.count() > 0);
166 Q_ASSERT(devices.count() < 2);
170 m_parasite.dim = devices.count();
172 m_parasite.ncells = devices.at(0).count();
173 m_parasite.rank[0] = m_parasite.ncells;
174 m_parasite.selection[0] = modes.at(0);
177 m_parasite.setBrushesCount();
179 for (uint i = 0; i < devices.at(0).count(); i++) {
180 m_brushes.append(
new KisBrush(devices.at(0).at(i), 0, 0, w, h));
183 setImage(m_brushes.at(0)->img());
185 m_brushType = PIPE_IMAGE;
188KisImagePipeBrush::~KisImagePipeBrush()
190 m_brushes.setAutoDelete(
true);
194bool KisImagePipeBrush::load()
196 TQFile file(filename());
197 file.open(IO_ReadOnly);
198 m_data = file.readAll();
203bool KisImagePipeBrush::init()
209 TQValueVector<char> line1;
213 while (m_data[i] !=
'\n' && i < m_data.size()) {
214 line1.append(m_data[i]);
217 setName(i18n(TQString::fromUtf8(&line1[0], i).ascii()));
224 TQValueVector<char> line2;
225 while (m_data[i] !=
'\n' && i < m_data.size()) {
226 line2.append(m_data[i]);
230 TQString paramline = TQString::fromUtf8((&line2[0]), line2.size());
231 TQ_UINT32 m_numOfBrushes = paramline.left(paramline.find(
' ')).toUInt();
232 m_parasite = paramline.mid(paramline.find(
' ') + 1);
235 TQ_UINT32 numOfBrushes = 0;
236 while (numOfBrushes < m_numOfBrushes && i < m_data.size()){
237 KisBrush * brush =
new KisBrush(name() +
"_" + numOfBrushes,
242 m_brushes.append(brush);
247 if (!m_brushes.isEmpty()) {
249 if (m_brushes.at( 0 )->brushType() == MASK) {
250 m_brushType = PIPE_MASK;
253 m_brushType = PIPE_IMAGE;
255 setSpacing(m_brushes.at(m_brushes.count() - 1)->spacing());
256 setWidth(m_brushes.at(0)->width());
257 setHeight(m_brushes.at(0)->height());
264bool KisImagePipeBrush::save()
266 TQFile file(filename());
267 file.open(IO_WriteOnly | IO_Truncate);
268 bool ok = saveToDevice(&file);
273bool KisImagePipeBrush::saveToDevice(TQIODevice* dev)
const
275 TQCString utf8Name = name().utf8();
276 char const* name = utf8Name.data();
277 int len = tqstrlen(name);
279 if (parasite().dim != 1) {
280 kdWarning(41001) <<
"Save to file for pipe brushes with dim != not yet supported!" << endl;
290 if (dev->writeBlock(name, len) == -1)
293 if (dev->putch(
'\n') == -1)
297 if (!m_parasite.saveToDevice(dev))
300 if (dev->putch(
'\n') == -1)
304 for (uint i = 0; i < m_brushes.count(); i++)
305 if (!m_brushes.at(i)->saveToDevice(dev))
311TQImage KisImagePipeBrush::img()
313 if (m_brushes.isEmpty()) {
317 return m_brushes.at(0)->img();
321KisAlphaMaskSP KisImagePipeBrush::mask(
const KisPaintInformation& info,
double subPixelX,
double subPixelY)
const
323 if (m_brushes.isEmpty())
return 0;
324 selectNextBrush(info);
325 return m_brushes.at(m_currentBrush)->mask(info, subPixelX, subPixelY);
330 if (m_brushes.isEmpty())
return 0;
331 selectNextBrush(info);
332 return m_brushes.at(m_currentBrush)->image(colorSpace, info, subPixelX, subPixelY);
335void KisImagePipeBrush::setParasiteString(
const TQString& parasite)
337 m_parasiteString = parasite;
342enumBrushType KisImagePipeBrush::brushType()
const
344 if (m_brushType == PIPE_IMAGE && useColorAsMask()) {
352bool KisImagePipeBrush::useColorAsMask()
const
354 if (m_brushes.count() > 0) {
355 return m_brushes.at(0)->useColorAsMask();
362void KisImagePipeBrush::setUseColorAsMask(
bool useColorAsMask)
364 for (uint i = 0; i < m_brushes.count(); i++) {
365 m_brushes.at(i)->setUseColorAsMask(useColorAsMask);
369bool KisImagePipeBrush::hasColor()
const
371 if (m_brushes.count() > 0) {
372 return m_brushes.at(0)->hasColor();
380 Q_ASSERT(!m_brushes.isEmpty());
381 return m_brushes.at(0)->boundary();
387 for (
int i = 0; i < m_parasite.dim; i++) {
388 int index = m_parasite.index[i];
389 switch (m_parasite.selection[i]) {
390 case KisPipeBrushParasite::Constant:
break;
391 case KisPipeBrushParasite::Incremental:
392 index = (index + 1) % m_parasite.rank[i];
break;
393 case KisPipeBrushParasite::Random:
394 index = int(
float(m_parasite.rank[i])*TDEApplication::random() / RAND_MAX);
break;
395 case KisPipeBrushParasite::Pressure:
396 index =
static_cast<int>(info.pressure * (m_parasite.rank[i] - 1) + 0.5);
break;
397 case KisPipeBrushParasite::Angular:
399 angle = atan2(info.movement.y(), info.movement.x()) + M_PI_2;
403 else if (angle > 2.0 * M_PI)
405 index =
static_cast<int>(angle / (2.0 * M_PI) * m_parasite.rank[i]);
408 kdWarning(41001) <<
"This parasite selectionMode has not been implemented. Reselecting"
409 <<
" to Incremental" << endl;
410 m_parasite.selection[i] = KisPipeBrushParasite::Incremental;
413 m_parasite.index[i] = index;
414 m_currentBrush += m_parasite.brushesCount[i] * index;
419 if (info.movement.isNull() && m_parasite.needsMovement)
424void KisImagePipeBrush::makeMaskImage() {
425 for (uint i = 0; i < m_brushes.count(); i++)
426 m_brushes.at(i)->makeMaskImage();
428 setBrushType(PIPE_MASK);
429 setUseColorAsMask(
false);
432KisImagePipeBrush* KisImagePipeBrush::clone()
const {
436 TQValueVector< TQValueVector<KisPaintDevice*> > devices;
437 TQValueVector<KisPipeBrushParasite::SelectionMode> modes;
439 devices.push_back(TQValueVector<KisPaintDevice*>());
440 modes.push_back(m_parasite.selection[0]);
442 for (uint i = 0; i < m_brushes.count(); i++) {
444 KisMetaRegistry::instance()->csRegistry()->getColorSpace(
KisID(
"RGBA",
""),
""),
"clone pd" );
446 devices.at(0).append(pd);
449 KisImagePipeBrush* c =
new KisImagePipeBrush(name(), width(), height(), devices, modes);
455#include "kis_imagepipe_brush.moc"
Generates an 'outline' for a paint device.
A colorspace is the definition of a certain color model in Chalk.
Chalk has a large number of extensible resources.
A paint device contains the actual pixel data and offers methods to read and write pixels.
virtual void convertFromTQImage(const TQImage &img, const TQString &srcProfileName, TQ_INT32 offsetX=0, TQ_INT32 offsetY=0)
Fill this paint device with the data from img; starting at (offsetX, offsetY)
The parasite info that gets loaded from the terribly documented gimp pipe brush parasite.
KisPipeBrushParasite()
Set some default values.
TQ_INT32 brushesCount[MaxDim]
The total count of brushes in each dimension (helper)
void setBrushesCount()
Initializes the brushesCount helper.
bool saveToDevice(TQIODevice *dev) const
Saves a GIMP-compatible representation of this parasite to the device.
bool needsMovement
If true, the brush won't be painted when there is no motion.
TQ_INT32 index[MaxDim]
The current index in each dimension, so that the selection modes know where to start.