Commit 26843687 authored by Bart Coppens's avatar Bart Coppens

Implement basic gimp pipe brush parasite parsing and selection modes....

Implement basic gimp pipe brush parasite parsing and selection modes. Currently, only the selection modes Constant, Incremental, Random and Pressure are implemented, and should work in brushes with dimension = 1. Other cases like dimension > 1 and selection modes like Tilt and Angular don't really work yet, but that wasn't requested in the wishlist item.

BUG: 106731

svn path=/trunk/koffice/; revision=461178
parent 4b1b6859
/*
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
* (c) 2005 Bart Coppens <kde@bartcoppens.be>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -32,21 +33,85 @@
#include <qpoint.h>
#include <qvaluevector.h>
#include <qfile.h>
#include <qregexp.h>
#include <qstringlist.h>
#include <kdebug.h>
#include <klocale.h>
#include <kapplication.h>
#include "kis_global.h"
#include "kis_imagepipe_brush.h"
#include "kis_brush.h"
#include "kis_alpha_mask.h"
KisPipeBrushParasite::KisPipeBrushParasite(const QString& source)
{
QRegExp basicSplitter(" ", true);
QRegExp parasiteSplitter(":", true);
QStringList parasites = QStringList::split(basicSplitter, source);
for (uint i = 0; i < parasites.count(); i++) {
QStringList splitted = QStringList::split(parasiteSplitter, *parasites.at(i));
if (splitted.count() != 2) {
kdDebug() << "Wrong count for this parasite key/value:" << *parasites.at(i) << endl;
continue;
}
QString index = *splitted.at(0);
if (index == "dim") {
dim = (*splitted.at(1)).toInt();
if (dim < 1 || dim > MaxDim) {
kdDebug() << "dim out of range: " << dim << endl;
dim = 1;
}
} else if (index.startsWith("sel")) {
int selIndex = index.mid(strlen("sel")).toInt();
if (selIndex >= 0 && selIndex < dim) {
QString selectionMode = *splitted.at(1);
if (selectionMode == "incremental")
selection[selIndex] = Incremental;
else if (selectionMode == "angular")
selection[selIndex] = Angular;
else if (selectionMode == "random")
selection[selIndex] = Random;
else if (selectionMode == "pressure")
selection[selIndex] = Pressure;
else if (selectionMode == "xtilt")
selection[selIndex] = TiltX;
else if (selectionMode == "ytilt")
selection[selIndex] = TiltY;
else
selection[selIndex] = Constant;
} else {
kdDebug()<< "Sel: wrong index: " << selIndex << "(dim = " << dim << ")" << endl;
}
} else if (index.startsWith("rank")) {
int rankIndex = index.mid(strlen("rank")).toInt();
if (rankIndex < 0 && rankIndex > dim) {
kdDebug() << "Rankindex out of range: " << rankIndex << endl;
continue;
}
rank[rankIndex] = (*splitted.at(1)).toInt();
} else if (index == "ncells") {
ncells = (*splitted.at(1)).toInt();
if (ncells < 1 ) {
kdDebug() << "ncells out of range: " << ncells << endl;
ncells = 1;
}
}
}
// I assume ncells is correct. If it isn't, complain to the parasite header.
brushesCount[0] = ncells / rank[0];
for (int i = 1; i < dim; i++) {
brushesCount[i] = brushesCount[i-1] / rank[i];
}
}
KisImagePipeBrush::KisImagePipeBrush(const QString& filename) : super(filename)
{
m_brushType = INVALID;
m_numOfBrushes = 0;
m_currentBrush = 0;
}
KisImagePipeBrush::~KisImagePipeBrush()
......@@ -94,7 +159,6 @@ bool KisImagePipeBrush::init()
QString paramline = QString::fromUtf8((&line2[0]), line2.size());
Q_UINT32 m_numOfBrushes = paramline.left(paramline.find(' ')).toUInt();
m_parasite = paramline.mid(paramline.find(' ') + 1);
i++; // Skip past the second newline
Q_UINT32 numOfBrushes = 0;
......@@ -144,28 +208,21 @@ QImage KisImagePipeBrush::img()
KisAlphaMaskSP KisImagePipeBrush::mask(double pressure, double subPixelX, double subPixelY) const
{
if (m_brushes.isEmpty()) return 0;
// XXX: This does not follow the instructions in the 'parasite'
if (m_currentBrush == m_brushes.count()) {
m_currentBrush = 0;
}
m_currentBrush++;
return m_brushes.at(m_currentBrush - 1) -> mask(pressure, subPixelX, subPixelY);
selectNextBrush(pressure);
return m_brushes.at(m_currentBrush) -> mask(pressure, subPixelX, subPixelY);
}
KisLayerSP KisImagePipeBrush::image(KisColorSpace * colorSpace, double pressure, double subPixelX, double subPixelY) const
{
if (m_brushes.isEmpty()) return 0;
// XXX: This does not follow the instructions in the 'parasite'
if (m_currentBrush == m_brushes.count()) {
m_currentBrush = 0;
}
m_currentBrush++;
return m_brushes.at(m_currentBrush - 1) -> image(colorSpace, pressure, subPixelX, subPixelY);
selectNextBrush(pressure);
return m_brushes.at(m_currentBrush) -> image(colorSpace, pressure, subPixelX, subPixelY);
}
void KisImagePipeBrush::setParasite(const QString& parasite)
void KisImagePipeBrush::setParasiteString(const QString& parasite)
{
m_parasite = parasite;
m_parasiteString = parasite;
m_parasite = KisPipeBrushParasite(parasite);
}
......@@ -210,6 +267,29 @@ KisBoundary KisImagePipeBrush::boundary() {
Q_ASSERT(!m_brushes.isEmpty());
return m_brushes.at(0) -> boundary();
}
void KisImagePipeBrush::selectNextBrush(double pressure) const {
m_currentBrush = 0;
for (int i = 0; i < m_parasite.dim; i++) {
int index = m_parasite.index[i];
switch (m_parasite.selection[i]) {
case KisPipeBrushParasite::Constant: break;
case KisPipeBrushParasite::Incremental:
index = (index + 1) % m_parasite.rank[i]; break;
case KisPipeBrushParasite::Random:
index = int(float(m_parasite.rank[i])*KApplication::random() / RAND_MAX); break;
case KisPipeBrushParasite::Pressure:
index = static_cast<int>(pressure * (m_parasite.rank[i] - 1) + 0.5); break;
default:
kdDebug() << "This parasite selectionMode has not been implemented. Reselecting"
<< " to Incremental" << endl;
m_parasite.selection[i] = KisPipeBrushParasite::Incremental;
index = 0;
}
m_parasite.index[i] = index;
m_currentBrush += m_parasite.brushesCount[i] * index;
}
}
#include "kis_imagepipe_brush.moc"
......@@ -20,6 +20,8 @@
#include <qptrlist.h>
#include <qvaluelist.h>
#include <qmap.h>
#include <qstring.h>
#include <kio/job.h>
......@@ -32,6 +34,52 @@ class QImage;
class QPoint;
class QSize;
/**
* The parasite info that gets loaded from the terribly documented gimp pipe brush parasite.
* We only store data we actually use.
* BC: How it seems the dimension stuff interacts with rank, selectionMode and the actual
* selection of a brush to be drawn. So apparantly you can have at most 4 'dimensions'.
* Each dimension has a number of brushes, the rank. Each dimension has an associated selection
* mode and placement mode (which we don't use). The selection mode says us in which way
* which of the brushes or brush sets will be selected. In the case of a 1-dimensional pipe
* brush it is easy.
* However, when there are more dimensions it is a bit harder. You can according to the gimp
* source maximally use 4 dimensions. When you want to select a brush, you first go to the
* first dimension. Say it has a rank of 2. The code chooses one of the 2 according to the
* selection mode. Say we choose 2. Then the currentBrush will skip over all the brushes
* from the first element in dimension 1. Then in dimension we pick again from the choices
* we have in dimension 2. We again add the appropriate amount to currentBrush. And so on,
* until we have reached dimension dim. Or at least, that is how it looks like, we'll know
* for sure when we can test it better with >1 dim brushes and Angular selectionMode.
**/
class KisPipeBrushParasite {
public:
KisPipeBrushParasite() {}
KisPipeBrushParasite(const QString& source);
/** Velocity won't be supported, atm Angular and Tilt aren't either, but have chances of implementation */
enum SelectionMode {
Constant, Incremental, Angular, Velocity, Random, Pressure, TiltX, TiltY
};
enum Placement { DefaultPlacement, ConstantPlacement, RandomPlacement };
static int const MaxDim = 4;
//Q_INT32 step;
Q_INT32 ncells;
Q_INT32 dim;
// Apparantly only used for editing a pipe brush, which we won't at the moment
// Q_INT32 cols, rows;
// Q_INT32 cellwidth, cellheight;
// Aparantly the gimp doesn't use this anymore? Anyway it is a bit weird to
// paint at someplace else than where your cursor displays it will...
//Placement placement;
Q_INT32 rank[MaxDim];
SelectionMode selection[MaxDim];
/// The total count of brushes in each dimension (helper)
Q_INT32 brushesCount[MaxDim];
/// The current index in each dimension, so that the selection modes know where to start
Q_INT32 index[MaxDim];
};
class KisImagePipeBrush : public KisBrush {
typedef KisBrush super;
Q_OBJECT
......@@ -61,14 +109,17 @@ public:
virtual enumBrushType brushType() const;
virtual KisBoundary boundary();
KisPipeBrushParasite parasite() { return m_parasite; }
private:
bool init();
void setParasite(const QString& parasite);
void setParasiteString(const QString& parasite);
void selectNextBrush(double pressure) const;
QString m_name;
QString m_parasite; // This contains some kind of instructions on how to use the brush
// That I haven't decoded yet.
QString m_parasiteString; // Contains instructions on how to use the brush
mutable KisPipeBrushParasite m_parasite;
Q_UINT32 m_numOfBrushes;
mutable Q_UINT32 m_currentBrush;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment