krita

kis_imagepipe_brush.cc

00001 /*
00002  *  Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
00003  *  Copyright (c) 2005 Bart Coppens <kde@bartcoppens.be>
00004  *
00005  *  This program is free software; you can redistribute it and/or modify
00006  *  it under the terms of the GNU General Public License as published by
00007  *  the Free Software Foundation; either version 2 of the License, or
00008  *  (at your option) any later version.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00018  */
00019 
00020 #ifdef HAVE_CONFIG_H
00021 #include <config.h>
00022 #endif
00023 
00024 #ifdef HAVE_SYS_TYPES_H
00025 #include <sys/types.h>
00026 #endif
00027 
00028 #include <math.h>
00029 
00030 #include <netinet/in.h>
00031 #include <limits.h>
00032 #include <stdlib.h>
00033 
00034 #include <qimage.h>
00035 #include <qpoint.h>
00036 #include <qvaluevector.h>
00037 #include <qfile.h>
00038 #include <qregexp.h>
00039 #include <qstringlist.h>
00040 #include <qtextstream.h>
00041 
00042 #include <kdebug.h>
00043 #include <klocale.h>
00044 #include <kapplication.h>
00045 
00046 #include "kis_global.h"
00047 #include "kis_paint_device.h"
00048 #include "kis_imagepipe_brush.h"
00049 #include "kis_brush.h"
00050 #include "kis_alpha_mask.h"
00051 #include "kis_layer.h"
00052 #include "kis_meta_registry.h"
00053 #include "kis_colorspace_factory_registry.h"
00054 
00055 
00056 KisPipeBrushParasite::KisPipeBrushParasite(const QString& source)
00057 {
00058     needsMovement = false;
00059     QRegExp basicSplitter(" ", true);
00060     QRegExp parasiteSplitter(":", true);
00061     QStringList parasites = QStringList::split(basicSplitter, source);
00062     for (uint i = 0; i < parasites.count(); i++) {
00063         QStringList splitted = QStringList::split(parasiteSplitter, *parasites.at(i));
00064         if (splitted.count() != 2) {
00065             kdWarning(41001) << "Wrong count for this parasite key/value:" << *parasites.at(i) << endl;
00066             continue;
00067         }
00068         QString index = *splitted.at(0);
00069         if (index == "dim") {
00070             dim = (*splitted.at(1)).toInt();
00071             if (dim < 1 || dim > MaxDim) {
00072                 dim = 1;
00073             }
00074         } else if (index.startsWith("sel")) {
00075             int selIndex = index.mid(strlen("sel")).toInt();
00076             if (selIndex >= 0 && selIndex < dim) {
00077                 QString selectionMode = *splitted.at(1);
00078                 if (selectionMode == "incremental")
00079                     selection[selIndex] = Incremental;
00080                 else if (selectionMode == "angular") {
00081                     selection[selIndex] = Angular;
00082                     needsMovement = true;
00083                 } else if (selectionMode == "random")
00084                     selection[selIndex] = Random;
00085                 else if (selectionMode == "pressure")
00086                     selection[selIndex] = Pressure;
00087                 else if (selectionMode == "xtilt")
00088                     selection[selIndex] = TiltX;
00089                 else if (selectionMode == "ytilt")
00090                     selection[selIndex] = TiltY;
00091                 else
00092                     selection[selIndex] = Constant;
00093             } else {
00094                 kdWarning(41001)<< "Sel: wrong index: " << selIndex << "(dim = " << dim << ")" << endl;
00095             }
00096         } else if (index.startsWith("rank")) {
00097             int rankIndex = index.mid(strlen("rank")).toInt();
00098             if (rankIndex < 0 || rankIndex > dim) {
00099                 kdWarning(41001) << "Rankindex out of range: " << rankIndex << endl;
00100                 continue;
00101             }
00102             rank[rankIndex] = (*splitted.at(1)).toInt();
00103         } else if (index == "ncells") {
00104             ncells = (*splitted.at(1)).toInt();
00105             if (ncells < 1 ) {
00106                 kdWarning(41001) << "ncells out of range: " << ncells << endl;
00107                 ncells = 1;
00108             }
00109         }
00110     }
00111 
00112     for (int i = 0; i < dim; i++) {
00113         index[i] = 0;
00114     }
00115 
00116     setBrushesCount();
00117 }
00118 
00119 void KisPipeBrushParasite::setBrushesCount() {
00120     // I assume ncells is correct. If it isn't, complain to the parasite header.
00121     brushesCount[0] = ncells / rank[0];
00122     for (int i = 1; i < dim; i++) {
00123         brushesCount[i] = brushesCount[i-1] / rank[i];
00124     }
00125 }
00126 
00127 bool KisPipeBrushParasite::saveToDevice(QIODevice* dev) const {
00128     // write out something like
00129     // <count> ncells:<count> dim:<dim> rank0:<rank0> sel0:<sel0> <...>
00130 
00131     QTextStream stream(dev);
00133     stream << ncells << " ncells:" << ncells << " dim:" << dim;
00134 
00135     for (int i = 0; i < dim; i++) {
00136         stream << " rank" << i << ":" << rank[i] << " sel" << i << ":";
00137         switch (selection[i]) {
00138             case Constant: stream << "constant"; break;
00139             case Incremental: stream << "incremental"; break;
00140             case Angular: stream << "angular"; break;
00141             case Velocity: stream << "velocity"; break;
00142             case Random: stream << "random"; break;
00143             case Pressure: stream << "pressure"; break;
00144             case TiltX: stream << "xtilt"; break;
00145             case TiltY: stream << "ytilt"; break;
00146         }
00147     }
00148 
00149     return true;
00150 }
00151 
00152 KisImagePipeBrush::KisImagePipeBrush(const QString& filename) : super(filename)
00153 {
00154     m_brushType = INVALID;
00155     m_numOfBrushes = 0;
00156     m_currentBrush = 0;
00157 }
00158 
00159 KisImagePipeBrush::KisImagePipeBrush(const QString& name, int w, int h,
00160                                      QValueVector< QValueVector<KisPaintDevice*> > devices,
00161                                      QValueVector<KisPipeBrushParasite::SelectionMode> modes)
00162     : super("")
00163 {
00164     Q_ASSERT(devices.count() == modes.count());
00165     Q_ASSERT(devices.count() > 0);
00166     Q_ASSERT(devices.count() < 2); // XXX Multidimensionals not supported yet, change to MaxDim!
00167 
00168     setName(name);
00169 
00170     m_parasite.dim = devices.count();
00171     // XXX Change for multidim! :
00172     m_parasite.ncells = devices.at(0).count();
00173     m_parasite.rank[0] = m_parasite.ncells;
00174     m_parasite.selection[0] = modes.at(0);
00175     // XXX needsmovement!
00176 
00177     m_parasite.setBrushesCount();
00178 
00179     for (uint i = 0; i < devices.at(0).count(); i++) {
00180         m_brushes.append(new KisBrush(devices.at(0).at(i), 0, 0, w, h));
00181     }
00182 
00183     setImage(m_brushes.at(0)->img());
00184 
00185     m_brushType = PIPE_IMAGE;
00186 }
00187 
00188 KisImagePipeBrush::~KisImagePipeBrush()
00189 {
00190     m_brushes.setAutoDelete(true);
00191     m_brushes.clear();
00192 }
00193 
00194 bool KisImagePipeBrush::load()
00195 {
00196     QFile file(filename());
00197     file.open(IO_ReadOnly);
00198     m_data = file.readAll();
00199     file.close();
00200     return init();
00201 }
00202 
00203 bool KisImagePipeBrush::init()
00204 {
00205     // XXX: this doesn't correctly load the image pipe brushes yet.
00206 
00207     // XXX: This stuff is in utf-8, too.
00208     // The first line contains the name -- this means we look until we arrive at the first newline
00209     QValueVector<char> line1;
00210 
00211     Q_UINT32 i = 0;
00212 
00213     while (m_data[i] != '\n' && i < m_data.size()) {
00214         line1.append(m_data[i]);
00215         i++;
00216     }
00217     setName(i18n(QString::fromUtf8(&line1[0], i).ascii()));
00218 
00219     i++; // Skip past the first newline
00220 
00221     // The second line contains the number of brushes, separated by a space from the parasite
00222 
00223     // XXX: This stuff is in utf-8, too.
00224      QValueVector<char> line2;
00225      while (m_data[i] != '\n' && i < m_data.size()) {
00226         line2.append(m_data[i]);
00227          i++;
00228      }
00229 
00230     QString paramline = QString::fromUtf8((&line2[0]), line2.size());
00231     Q_UINT32 m_numOfBrushes = paramline.left(paramline.find(' ')).toUInt();
00232     m_parasite = paramline.mid(paramline.find(' ') + 1);
00233     i++; // Skip past the second newline
00234 
00235      Q_UINT32 numOfBrushes = 0;
00236       while (numOfBrushes < m_numOfBrushes && i < m_data.size()){
00237         KisBrush * brush = new KisBrush(name() + "_" + numOfBrushes,
00238                         m_data,
00239                         i);
00240         Q_CHECK_PTR(brush);
00241 
00242         m_brushes.append(brush);
00243 
00244          numOfBrushes++;
00245      }
00246 
00247     if (!m_brushes.isEmpty()) {
00248         setValid(true);
00249         if (m_brushes.at( 0 )->brushType() == MASK) {
00250             m_brushType = PIPE_MASK;
00251         }
00252         else {
00253             m_brushType = PIPE_IMAGE;
00254         }
00255         setSpacing(m_brushes.at(m_brushes.count() - 1)->spacing());
00256         setWidth(m_brushes.at(0)->width());
00257         setHeight(m_brushes.at(0)->height());
00258     }
00259 
00260     m_data.resize(0);
00261     return true;
00262 }
00263 
00264 bool KisImagePipeBrush::save()
00265 {
00266     QFile file(filename());
00267     file.open(IO_WriteOnly | IO_Truncate);
00268     bool ok = saveToDevice(&file);
00269     file.close();
00270     return ok;
00271 }
00272 
00273 bool KisImagePipeBrush::saveToDevice(QIODevice* dev) const
00274 {
00275     QCString utf8Name = name().utf8(); // Names in v2 brushes are in UTF-8
00276     char const* name = utf8Name.data();
00277     int len = qstrlen(name);
00278 
00279     if (parasite().dim != 1) {
00280         kdWarning(41001) << "Save to file for pipe brushes with dim != not yet supported!" << endl;
00281         return false;
00282     }
00283 
00284     // Save this pipe brush: first the header, and then all individual brushes consecutively
00285     // (this needs some care for when we have > 1 dimension), FIXME
00286 
00287     // Gimp Pipe Brush header format: Name\n<number of brushes> <parasite>\n
00288 
00289     // The name\n
00290     if (dev->writeBlock(name, len) == -1)
00291         return false;
00292 
00293     if (dev->putch('\n') == -1)
00294         return false;
00295 
00296     // Write the parasite (also writes number of brushes)
00297     if (!m_parasite.saveToDevice(dev))
00298         return false;
00299 
00300     if (dev->putch('\n') == -1)
00301         return false;
00302 
00303     // <gbr brushes>
00304     for (uint i = 0; i < m_brushes.count(); i++)
00305         if (!m_brushes.at(i)->saveToDevice(dev))
00306             return false;
00307 
00308     return true;
00309 }
00310 
00311 QImage KisImagePipeBrush::img()
00312 {
00313     if (m_brushes.isEmpty()) {
00314         return 0;
00315     }
00316     else {
00317         return m_brushes.at(0)->img();
00318     }
00319 }
00320 
00321 KisAlphaMaskSP KisImagePipeBrush::mask(const KisPaintInformation& info, double subPixelX, double subPixelY) const
00322 {
00323     if (m_brushes.isEmpty()) return 0;
00324     selectNextBrush(info);
00325     return m_brushes.at(m_currentBrush)->mask(info, subPixelX, subPixelY);
00326 }
00327 
00328 KisPaintDeviceSP KisImagePipeBrush::image(KisColorSpace * colorSpace, const KisPaintInformation& info, double subPixelX, double subPixelY) const
00329 {
00330     if (m_brushes.isEmpty()) return 0;
00331     selectNextBrush(info);
00332     return m_brushes.at(m_currentBrush)->image(colorSpace, info, subPixelX, subPixelY);
00333 }
00334 
00335 void KisImagePipeBrush::setParasiteString(const QString& parasite)
00336 {
00337     m_parasiteString = parasite;
00338     m_parasite = KisPipeBrushParasite(parasite);
00339 }
00340 
00341 
00342 enumBrushType KisImagePipeBrush::brushType() const
00343 {
00344     if (m_brushType == PIPE_IMAGE && useColorAsMask()) {
00345         return PIPE_MASK;
00346     }
00347     else {
00348         return m_brushType;
00349     }
00350 }
00351 
00352 bool KisImagePipeBrush::useColorAsMask() const
00353 {
00354     if (m_brushes.count() > 0) {
00355         return m_brushes.at(0)->useColorAsMask();
00356     }
00357     else {
00358         return false;
00359     }
00360 }
00361 
00362 void KisImagePipeBrush::setUseColorAsMask(bool useColorAsMask)
00363 {
00364     for (uint i = 0; i < m_brushes.count(); i++) {
00365         m_brushes.at(i)->setUseColorAsMask(useColorAsMask);
00366     }
00367 }
00368 
00369 bool KisImagePipeBrush::hasColor() const
00370 {
00371     if (m_brushes.count() > 0) {
00372         return m_brushes.at(0)->hasColor();
00373     }
00374     else {
00375         return false;
00376     }
00377 }
00378 
00379 KisBoundary KisImagePipeBrush::boundary() {
00380     Q_ASSERT(!m_brushes.isEmpty());
00381     return m_brushes.at(0)->boundary();
00382 }
00383 
00384 void KisImagePipeBrush::selectNextBrush(const KisPaintInformation& info) const {
00385     m_currentBrush = 0;
00386     double angle;
00387     for (int i = 0; i < m_parasite.dim; i++) {
00388         int index = m_parasite.index[i];
00389         switch (m_parasite.selection[i]) {
00390             case KisPipeBrushParasite::Constant: break;
00391             case KisPipeBrushParasite::Incremental:
00392                 index = (index + 1) % m_parasite.rank[i]; break;
00393             case KisPipeBrushParasite::Random:
00394                 index = int(float(m_parasite.rank[i])*KApplication::random() / RAND_MAX); break;
00395             case KisPipeBrushParasite::Pressure:
00396                 index = static_cast<int>(info.pressure * (m_parasite.rank[i] - 1) + 0.5); break;
00397             case KisPipeBrushParasite::Angular:
00398                 // + M_PI_2 to be compatible with the gimp
00399                 angle = atan2(info.movement.y(), info.movement.x()) + M_PI_2;
00400                 // We need to be in the [0..2*Pi[ interval so that we can more nicely select it
00401                 if (angle < 0)
00402                     angle += 2.0 * M_PI;
00403                 else if (angle > 2.0 * M_PI)
00404                     angle -= 2.0 * M_PI;
00405                 index = static_cast<int>(angle / (2.0 * M_PI) * m_parasite.rank[i]);
00406                 break;
00407             default:
00408                 kdWarning(41001) << "This parasite selectionMode has not been implemented. Reselecting"
00409                         << " to Incremental" << endl;
00410                 m_parasite.selection[i] = KisPipeBrushParasite::Incremental;
00411                 index = 0;
00412         }
00413         m_parasite.index[i] = index;
00414         m_currentBrush += m_parasite.brushesCount[i] * index;
00415     }
00416 }
00417 
00418 bool KisImagePipeBrush::canPaintFor(const KisPaintInformation& info) {
00419     if (info.movement.isNull() && m_parasite.needsMovement)
00420         return false;
00421     return true;
00422 }
00423 
00424 void KisImagePipeBrush::makeMaskImage() {
00425     for (uint i = 0; i < m_brushes.count(); i++)
00426         m_brushes.at(i)->makeMaskImage();
00427 
00428     setBrushType(PIPE_MASK);
00429     setUseColorAsMask(false);
00430 }
00431 
00432 KisImagePipeBrush* KisImagePipeBrush::clone() const {
00433     // The obvious way of cloning each brush in this one doesn't work for some reason...
00434 
00435     // XXX Multidimensionals not supported yet, change together with the constructor...
00436     QValueVector< QValueVector<KisPaintDevice*> > devices;
00437     QValueVector<KisPipeBrushParasite::SelectionMode> modes;
00438 
00439     devices.push_back(QValueVector<KisPaintDevice*>());
00440     modes.push_back(m_parasite.selection[0]);
00441 
00442     for (uint i = 0; i < m_brushes.count(); i++) {
00443         KisPaintDevice* pd = new KisPaintDevice(
00444                 KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("RGBA",""),""), "clone pd" );
00445         pd->convertFromQImage(m_brushes.at(i)->img(), "");
00446         devices.at(0).append(pd);
00447     }
00448 
00449     KisImagePipeBrush* c = new KisImagePipeBrush(name(), width(), height(), devices, modes);
00450     // XXX clean up devices
00451 
00452     return c;
00453 }
00454 
00455 #include "kis_imagepipe_brush.moc"
00456 
KDE Home | KDE Accessibility Home | Description of Access Keys