krita

kis_brush.cc

00001 /*
00002  *  Copyright (c) 1999 Matthias Elter  <me@kde.org>
00003  *  Copyright (c) 2003 Patrick Julien  <freak@codepimps.org>
00004  *  Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
00005  *  Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
00006  *  Copyright (c) 2005 Bart Coppens <kde@bartcoppens.be>
00007  *
00008  *  This program is free software; you can redistribute it and/or modify
00009  *  it under the terms of the GNU General Public License as published by
00010  *  the Free Software Foundation; either version 2 of the License, or
00011  *  (at your option) any later version.
00012  *
00013  *  This program is distributed in the hope that it will be useful,
00014  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  *  GNU General Public License for more details.
00017  *
00018  *  You should have received a copy of the GNU General Public License
00019  *  along with this program; if not, write to the Free Software
00020  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00021  */
00022 
00023 #ifdef HAVE_CONFIG_H
00024 #include <config.h>
00025 #endif
00026 
00027 #ifdef HAVE_SYS_TYPES_H
00028 #include <sys/types.h>
00029 #endif
00030 
00031 #include <netinet/in.h>
00032 #include <limits.h>
00033 #include <stdlib.h>
00034 #include <cfloat>
00035 
00036 #include <qfile.h>
00037 #include <qimage.h>
00038 #include <qpoint.h>
00039 #include <qvaluevector.h>
00040 
00041 #include <kdebug.h>
00042 #include <klocale.h>
00043 
00044 #include <kis_meta_registry.h>
00045 #include "kis_paint_device.h"
00046 #include "kis_global.h"
00047 #include "kis_brush.h"
00048 #include "kis_alpha_mask.h"
00049 #include "kis_colorspace_factory_registry.h"
00050 #include "kis_iterators_pixel.h"
00051 #include "kis_image.h"
00052 
00053 
00054 namespace {
00055     struct GimpBrushV1Header {
00056         Q_UINT32 header_size;  /*  header_size = sizeof (BrushHeader) + brush name  */
00057         Q_UINT32 version;      /*  brush file version #  */
00058         Q_UINT32 width;        /*  width of brush  */
00059         Q_UINT32 height;       /*  height of brush  */
00060         Q_UINT32 bytes;        /*  depth of brush in bytes */
00061     };
00062 
00064     struct GimpBrushHeader {
00065         Q_UINT32 header_size;  /*  header_size = sizeof (BrushHeader) + brush name  */
00066         Q_UINT32 version;      /*  brush file version #  */
00067         Q_UINT32 width;        /*  width of brush  */
00068         Q_UINT32 height;       /*  height of brush  */
00069         Q_UINT32 bytes;        /*  depth of brush in bytes */
00070 
00071                        /*  The following are only defined in version 2 */
00072         Q_UINT32 magic_number; /*  GIMP brush magic number  */
00073         Q_UINT32 spacing;      /*  brush spacing as % of width & height, 0 - 1000 */
00074     };
00075 
00076     // Needed, or the GIMP won't open it!
00077     Q_UINT32 const GimpV2BrushMagic = ('G' << 24) + ('I' << 16) + ('M' << 8) + ('P' << 0);
00078 }
00079 
00080 #define DEFAULT_SPACING 0.25
00081 #define MAXIMUM_SCALE 2
00082 
00083 KisBrush::KisBrush(const QString& filename) : super(filename)
00084 {
00085     m_brushType = INVALID;
00086     m_ownData = true;
00087     m_useColorAsMask = false;
00088     m_hasColor = false;
00089     m_spacing = DEFAULT_SPACING;
00090     m_boundary = 0;
00091 }
00092 
00093 KisBrush::KisBrush(const QString& filename,
00094            const QByteArray& data,
00095            Q_UINT32 & dataPos) : super(filename)
00096 {
00097     m_brushType = INVALID;
00098     m_ownData = false;
00099     m_useColorAsMask = false;
00100     m_hasColor = false;
00101     m_spacing = DEFAULT_SPACING;
00102     m_boundary = 0;
00103 
00104     m_data.setRawData(data.data() + dataPos, data.size() - dataPos);
00105     init();
00106     m_data.resetRawData(data.data() + dataPos, data.size() - dataPos);
00107     dataPos += m_header_size + (width() * height() * m_bytes);
00108 }
00109 
00110 KisBrush::KisBrush(KisPaintDevice* image, int x, int y, int w, int h)
00111     : super(QString(""))
00112 {
00113     m_brushType = INVALID;
00114     m_ownData = true;
00115     m_useColorAsMask = false;
00116     m_hasColor = true;
00117     m_spacing = DEFAULT_SPACING;
00118     m_boundary = 0;
00119 
00120     initFromPaintDev(image, x, y, w, h);
00121 }
00122 
00123 KisBrush::KisBrush(const QImage& image, const QString& name)
00124     : super(QString(""))
00125 {
00126     m_ownData = false;
00127     m_useColorAsMask = false;
00128     m_hasColor = true;
00129     m_spacing = DEFAULT_SPACING;
00130     m_boundary = 0;
00131 
00132     setImage(image);
00133     setName(name);
00134     setBrushType(IMAGE);
00135 }
00136 
00137 
00138 KisBrush::~KisBrush()
00139 {
00140     m_scaledBrushes.clear();
00141     delete m_boundary;
00142 }
00143 
00144 bool KisBrush::load()
00145 {
00146     if (m_ownData) {
00147         QFile file(filename());
00148         file.open(IO_ReadOnly);
00149         m_data = file.readAll();
00150         file.close();
00151     }
00152     return init();
00153 }
00154 
00155 bool KisBrush::init()
00156 {
00157     GimpBrushHeader bh;
00158 
00159     if (sizeof(GimpBrushHeader) > m_data.size()) {
00160         return false;
00161     }
00162 
00163     memcpy(&bh, &m_data[0], sizeof(GimpBrushHeader));
00164     bh.header_size = ntohl(bh.header_size);
00165     m_header_size = bh.header_size;
00166 
00167     bh.version = ntohl(bh.version);
00168     m_version = bh.version;
00169 
00170     bh.width = ntohl(bh.width);
00171     bh.height = ntohl(bh.height);
00172 
00173     bh.bytes = ntohl(bh.bytes);
00174     m_bytes = bh.bytes;
00175 
00176     bh.magic_number = ntohl(bh.magic_number);
00177     m_magic_number = bh.magic_number;
00178 
00179     if (bh.version == 1) {
00180         // No spacing in version 1 files so use Gimp default
00181         bh.spacing = static_cast<int>(DEFAULT_SPACING * 100);
00182     }
00183     else {
00184         bh.spacing = ntohl(bh.spacing);
00185 
00186         if (bh.spacing > 1000) {
00187             return false;
00188         }
00189     }
00190 
00191     setSpacing(bh.spacing / 100.0);
00192 
00193     if (bh.header_size > m_data.size() || bh.header_size == 0) {
00194         return false;
00195     }
00196 
00197     QString name;
00198 
00199     if (bh.version == 1) {
00200         // Version 1 has no magic number or spacing, so the name
00201         // is at a different offset. Character encoding is undefined.
00202         const char *text = &m_data[sizeof(GimpBrushV1Header)];
00203         name = QString::fromAscii(text, bh.header_size - sizeof(GimpBrushV1Header));
00204     } else {
00205         // ### Version = 3->cinepaint; may be float16 data!
00206         // Version >=2: UTF-8 encoding is used
00207         name = QString::fromUtf8(&m_data[sizeof(GimpBrushHeader)],
00208                                   bh.header_size - sizeof(GimpBrushHeader));
00209     }
00210 
00211     setName(i18n(name.ascii())); // Ascii? And what with real UTF-8 chars?
00212 
00213     if (bh.width == 0 || bh.height == 0 || !m_img.create(bh.width, bh.height, 32)) {
00214         return false;
00215     }
00216 
00217     Q_INT32 k = bh.header_size;
00218 
00219     if (bh.bytes == 1) {
00220         // Grayscale
00221 
00222         if (static_cast<Q_UINT32>(k + bh.width * bh.height) > m_data.size()) {
00223             return false;
00224         }
00225 
00226         m_brushType = MASK;
00227         m_hasColor = false;
00228 
00229         for (Q_UINT32 y = 0; y < bh.height; y++) {
00230             for (Q_UINT32 x = 0; x < bh.width; x++, k++) {
00231                 Q_INT32 val = 255 - static_cast<uchar>(m_data[k]);
00232                 m_img.setPixel(x, y, qRgb(val, val, val));
00233             }
00234         }
00235     } else if (bh.bytes == 4) {
00236         // RGBA
00237 
00238         if (static_cast<Q_UINT32>(k + (bh.width * bh.height * 4)) > m_data.size()) {
00239             return false;
00240         }
00241 
00242         m_brushType = IMAGE;
00243         m_img.setAlphaBuffer(true);
00244         m_hasColor = true;
00245 
00246         for (Q_UINT32 y = 0; y < bh.height; y++) {
00247             for (Q_UINT32 x = 0; x < bh.width; x++, k += 4) {
00248                 m_img.setPixel(x, y, qRgba(m_data[k],
00249                                m_data[k+1],
00250                                m_data[k+2],
00251                                m_data[k+3]));
00252             }
00253         }
00254     } else {
00255         return false;
00256     }
00257 
00258     setWidth(m_img.width());
00259     setHeight(m_img.height());
00260     //createScaledBrushes();
00261     if (m_ownData) {
00262         m_data.resize(0); // Save some memory, we're using enough of it as it is.
00263     }
00264 
00265 
00266     if (m_img.width() == 0 || m_img.height() == 0)
00267         setValid(false);
00268     else
00269         setValid(true);
00270 
00271     return true;
00272 }
00273 
00274 bool KisBrush::initFromPaintDev(KisPaintDevice* image, int x, int y, int w, int h) {
00275     // Forcefully convert to RGBA8
00276     // XXX profile and exposure?
00277     setImage(image->convertToQImage(0, x, y, w, h));
00278     setName(image->name());
00279 
00280     m_brushType = IMAGE;
00281     m_hasColor = true;
00282 
00283     return true;
00284 }
00285 
00286 bool KisBrush::save()
00287 {
00288     QFile file(filename());
00289     file.open(IO_WriteOnly | IO_Truncate);
00290     bool ok = saveToDevice(&file);
00291     file.close();
00292     return ok;
00293 }
00294 
00295 bool KisBrush::saveToDevice(QIODevice* dev) const
00296 {
00297     GimpBrushHeader bh;
00298     QCString utf8Name = name().utf8(); // Names in v2 brushes are in UTF-8
00299     char const* name = utf8Name.data();
00300     int nameLength = qstrlen(name);
00301     int wrote;
00302 
00303     bh.header_size = htonl(sizeof(GimpBrushHeader) + nameLength);
00304     bh.version = htonl(2); // Only RGBA8 data needed atm, no cinepaint stuff
00305     bh.width = htonl(width());
00306     bh.height = htonl(height());
00307     // Hardcoded, 4 bytes RGBA or 1 byte GREY
00308     if (!hasColor())
00309         bh.bytes = htonl(1);
00310     else
00311         bh.bytes = htonl(4);
00312     bh.magic_number = htonl(GimpV2BrushMagic);
00313     bh.spacing = htonl(static_cast<Q_UINT32>(spacing() * 100.0));
00314 
00315     // Write header: first bh, then the name
00316     QByteArray bytes;
00317     bytes.setRawData(reinterpret_cast<char*>(&bh), sizeof(GimpBrushHeader));
00318     wrote = dev->writeBlock(bytes);
00319     bytes.resetRawData(reinterpret_cast<char*>(&bh), sizeof(GimpBrushHeader));
00320 
00321     if (wrote == -1)
00322         return false;
00323 
00324     wrote = dev->writeBlock(name, nameLength); // No +1 for the trailing NULL it seems...
00325     if (wrote == -1)
00326         return false;
00327 
00328     int k = 0;
00329 
00330     if (!hasColor()) {
00331         bytes.resize(width() * height());
00332         for (Q_INT32 y = 0; y < height(); y++) {
00333             for (Q_INT32 x = 0; x < width(); x++) {
00334                 QRgb c = m_img.pixel(x, y);
00335                 bytes[k++] = static_cast<char>(255 - qRed(c)); // red == blue == green
00336             }
00337         }
00338     } else {
00339         bytes.resize(width() * height() * 4);
00340         for (Q_INT32 y = 0; y < height(); y++) {
00341             for (Q_INT32 x = 0; x < width(); x++) {
00342                 // order for gimp brushes, v2 is: RGBA
00343                 QRgb pixel = m_img.pixel(x,y);
00344                 bytes[k++] = static_cast<char>(qRed(pixel));
00345                 bytes[k++] = static_cast<char>(qGreen(pixel));
00346                 bytes[k++] = static_cast<char>(qBlue(pixel));
00347                 bytes[k++] = static_cast<char>(qAlpha(pixel));
00348             }
00349         }
00350     }
00351 
00352     wrote = dev->writeBlock(bytes);
00353     if (wrote == -1)
00354         return false;
00355 
00356     return true;
00357 }
00358 
00359 QImage KisBrush::img()
00360 {
00361     QImage image = m_img;
00362 
00363     if (hasColor() && useColorAsMask()) {
00364         image.detach();
00365 
00366         for (int x = 0; x < image.width(); x++) {
00367             for (int y = 0; y < image.height(); y++) {
00368                 QRgb c = image.pixel(x, y);
00369                 int a = (qGray(c) * qAlpha(c)) / 255;
00370                 image.setPixel(x, y, qRgba(a, 0, a, a));
00371             }
00372         }
00373     }
00374 
00375     return image;
00376 }
00377 
00378 KisAlphaMaskSP KisBrush::mask(const KisPaintInformation& info, double subPixelX, double subPixelY) const
00379 {
00380     if (m_scaledBrushes.isEmpty()) {
00381         createScaledBrushes();
00382     }
00383 
00384     double scale = scaleForPressure(info.pressure);
00385 
00386     const ScaledBrush *aboveBrush = 0;
00387     const ScaledBrush *belowBrush = 0;
00388 
00389     findScaledBrushes(scale, &aboveBrush,  &belowBrush);
00390     Q_ASSERT(aboveBrush != 0);
00391 
00392     KisAlphaMaskSP outputMask = 0;
00393 
00394     if (belowBrush != 0) {
00395         // We're in between two masks. Interpolate between them.
00396 
00397         KisAlphaMaskSP scaledAboveMask = scaleMask(aboveBrush, scale, subPixelX, subPixelY);
00398         KisAlphaMaskSP scaledBelowMask = scaleMask(belowBrush, scale, subPixelX, subPixelY);
00399 
00400         double t = (scale - belowBrush->scale()) / (aboveBrush->scale() - belowBrush->scale());
00401 
00402         outputMask = KisAlphaMask::interpolate(scaledBelowMask, scaledAboveMask, t);
00403     } else {
00404         if (fabs(scale - aboveBrush->scale()) < DBL_EPSILON) {
00405             // Exact match.
00406             outputMask = scaleMask(aboveBrush, scale, subPixelX, subPixelY);
00407         } else {
00408             // We are smaller than the smallest mask, which is always 1x1.
00409             double s = scale / aboveBrush->scale();
00410             outputMask = scaleSinglePixelMask(s, aboveBrush->mask()->alphaAt(0, 0), subPixelX, subPixelY);
00411         }
00412     }
00413 
00414     return outputMask;
00415 }
00416 
00417 KisPaintDeviceSP KisBrush::image(KisColorSpace * /*colorSpace*/, const KisPaintInformation& info, double subPixelX, double subPixelY) const
00418 {
00419     if (m_scaledBrushes.isEmpty()) {
00420         createScaledBrushes();
00421     }
00422 
00423     double scale = scaleForPressure(info.pressure);
00424 
00425     const ScaledBrush *aboveBrush = 0;
00426     const ScaledBrush *belowBrush = 0;
00427 
00428     findScaledBrushes(scale, &aboveBrush,  &belowBrush);
00429     Q_ASSERT(aboveBrush != 0);
00430 
00431     QImage outputImage;
00432 
00433     if (belowBrush != 0) {
00434         // We're in between two brushes. Interpolate between them.
00435 
00436         QImage scaledAboveImage = scaleImage(aboveBrush, scale, subPixelX, subPixelY);
00437         QImage scaledBelowImage = scaleImage(belowBrush, scale, subPixelX, subPixelY);
00438 
00439         double t = (scale - belowBrush->scale()) / (aboveBrush->scale() - belowBrush->scale());
00440 
00441         outputImage = interpolate(scaledBelowImage, scaledAboveImage, t);
00442     } else {
00443         if (fabs(scale - aboveBrush->scale()) < DBL_EPSILON) {
00444             // Exact match.
00445             outputImage = scaleImage(aboveBrush, scale, subPixelX, subPixelY);
00446         } else {
00447             // We are smaller than the smallest brush, which is always 1x1.
00448             double s = scale / aboveBrush->scale();
00449             outputImage = scaleSinglePixelImage(s, aboveBrush->image().pixel(0, 0), subPixelX, subPixelY);
00450         }
00451     }
00452 
00453     int outputWidth = outputImage.width();
00454     int outputHeight = outputImage.height();
00455 
00456     KisPaintDevice *layer = new KisPaintDevice(KisMetaRegistry::instance()->csRegistry()->getRGB8(), "brush");
00457 
00458     Q_CHECK_PTR(layer);
00459 
00460     for (int y = 0; y < outputHeight; y++) {
00461         KisHLineIterator iter = layer->createHLineIterator( 0, y, outputWidth, true);
00462         for (int x = 0; x < outputWidth; x++) {
00463         Q_UINT8 * p = iter.rawData();
00464 
00465             QRgb pixel = outputImage.pixel(x, y);
00466             int red = qRed(pixel);
00467             int green = qGreen(pixel);
00468             int blue = qBlue(pixel);
00469             int alpha = qAlpha(pixel);
00470 
00471             // Scaled images are in pre-multiplied alpha form so
00472             // divide by alpha.
00473         // channel order is BGRA
00474             if (alpha != 0) {
00475                 p[2] = (red * 255) / alpha;
00476                 p[1] = (green * 255) / alpha;
00477         p[0] = (blue * 255) / alpha;
00478         p[3] = alpha;
00479             }
00480 
00481             ++iter;
00482         }
00483     }
00484 
00485     return layer;
00486 }
00487 
00488 void KisBrush::setHotSpot(KisPoint pt)
00489 {
00490     double x = pt.x();
00491     double y = pt.y();
00492 
00493     if (x < 0)
00494         x = 0;
00495     else if (x >= width())
00496         x = width() - 1;
00497 
00498     if (y < 0)
00499         y = 0;
00500     else if (y >= height())
00501         y = height() - 1;
00502 
00503     m_hotSpot = KisPoint(x, y);
00504 }
00505 
00506 KisPoint KisBrush::hotSpot(const KisPaintInformation& info) const
00507 {
00508     double scale = scaleForPressure(info.pressure);
00509     double w = width() * scale;
00510     double h = height() * scale;
00511 
00512     // The smallest brush we can produce is a single pixel.
00513     if (w < 1) {
00514         w = 1;
00515     }
00516 
00517     if (h < 1) {
00518         h = 1;
00519     }
00520 
00521     // XXX: This should take m_hotSpot into account, though it
00522     // isn't specified by gimp brushes so it would default to the centre
00523     // anyway.
00524     KisPoint p(w / 2, h / 2);
00525     return p;
00526 }
00527 
00528 enumBrushType KisBrush::brushType() const
00529 {
00530     if (m_brushType == IMAGE && useColorAsMask()) {
00531         return MASK;
00532     }
00533     else {
00534         return m_brushType;
00535     }
00536 }
00537 
00538 bool KisBrush::hasColor() const
00539 {
00540     return m_hasColor;
00541 }
00542 
00543 void KisBrush::createScaledBrushes() const
00544 {
00545     if (!m_scaledBrushes.isEmpty())
00546         m_scaledBrushes.clear();
00547 
00548     // Construct a series of brushes where each one's dimensions are
00549     // half the size of the previous one.
00550     int width = m_img.width() * MAXIMUM_SCALE;
00551     int height = m_img.height() * MAXIMUM_SCALE;
00552 
00553     QImage scaledImage;
00554 
00555     while (true) {
00556 
00557         if (width >= m_img.width() && height >= m_img.height()) {
00558             scaledImage = scaleImage(m_img, width, height);
00559         }
00560         else {
00561             // Scale down the previous image once we're below 1:1.
00562             scaledImage = scaleImage(scaledImage, width, height);
00563         }
00564 
00565         KisAlphaMaskSP scaledMask = new KisAlphaMask(scaledImage, hasColor());
00566         Q_CHECK_PTR(scaledMask);
00567 
00568         double xScale = static_cast<double>(width) / m_img.width();
00569         double yScale = static_cast<double>(height) / m_img.height();
00570         double scale = xScale;
00571 
00572         m_scaledBrushes.append(ScaledBrush(scaledMask, hasColor() ? scaledImage : QImage(), scale, xScale, yScale));
00573 
00574         if (width == 1 && height == 1) {
00575             break;
00576         }
00577 
00578         // Round up so that we never have to scale an image by less than 1/2.
00579         width = (width + 1) / 2;
00580         height = (height + 1) / 2;
00581 
00582     }
00583 
00584 }
00585 
00586 double KisBrush::xSpacing(double pressure) const
00587 {
00588     return width() * scaleForPressure(pressure) * m_spacing;
00589 }
00590 
00591 double KisBrush::ySpacing(double pressure) const
00592 {
00593     return height() * scaleForPressure(pressure) * m_spacing;
00594 }
00595 
00596 double KisBrush::scaleForPressure(double pressure)
00597 {
00598     double scale = pressure / PRESSURE_DEFAULT;
00599 
00600     if (scale < 0) {
00601         scale = 0;
00602     }
00603 
00604     if (scale > MAXIMUM_SCALE) {
00605         scale = MAXIMUM_SCALE;
00606     }
00607 
00608     return scale;
00609 }
00610 
00611 Q_INT32 KisBrush::maskWidth(const KisPaintInformation& info) const
00612 {
00613     // Add one for sub-pixel shift
00614     return static_cast<Q_INT32>(ceil(width() * scaleForPressure(info.pressure)) + 1);
00615 }
00616 
00617 Q_INT32 KisBrush::maskHeight(const KisPaintInformation& info) const
00618 {
00619     // Add one for sub-pixel shift
00620     return static_cast<Q_INT32>(ceil(height() * scaleForPressure(info.pressure)) + 1);
00621 }
00622 
00623 KisAlphaMaskSP KisBrush::scaleMask(const ScaledBrush *srcBrush, double scale, double subPixelX, double subPixelY) const
00624 {
00625     // Add one pixel for sub-pixel shifting
00626     int dstWidth = static_cast<int>(ceil(scale * width())) + 1;
00627     int dstHeight = static_cast<int>(ceil(scale * height())) + 1;
00628 
00629     KisAlphaMaskSP dstMask = new KisAlphaMask(dstWidth, dstHeight);
00630     Q_CHECK_PTR(dstMask);
00631 
00632     KisAlphaMaskSP srcMask = srcBrush->mask();
00633 
00634     // Compute scales to map the scaled brush onto the required scale.
00635     double xScale = srcBrush->xScale() / scale;
00636     double yScale = srcBrush->yScale() / scale;
00637 
00638     int srcWidth = srcMask->width();
00639     int srcHeight = srcMask->height();
00640 
00641     for (int dstY = 0; dstY < dstHeight; dstY++) {
00642         for (int dstX = 0; dstX < dstWidth; dstX++) {
00643 
00644             double srcX = (dstX - subPixelX + 0.5) * xScale;
00645             double srcY = (dstY - subPixelY + 0.5) * yScale;
00646 
00647             srcX -= 0.5;
00648             srcY -= 0.5;
00649 
00650             int leftX = static_cast<int>(srcX);
00651 
00652             if (srcX < 0) {
00653                 leftX--;
00654             }
00655 
00656             double xInterp = srcX - leftX;
00657 
00658             int topY = static_cast<int>(srcY);
00659 
00660             if (srcY < 0) {
00661                 topY--;
00662             }
00663 
00664             double yInterp = srcY - topY;
00665 
00666             Q_UINT8 topLeft = (leftX >= 0 && leftX < srcWidth && topY >= 0 && topY < srcHeight) ? srcMask->alphaAt(leftX, topY) : OPACITY_TRANSPARENT;
00667             Q_UINT8 bottomLeft = (leftX >= 0 && leftX < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcMask->alphaAt(leftX, topY + 1) : OPACITY_TRANSPARENT;
00668             Q_UINT8 topRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY >= 0 && topY < srcHeight) ? srcMask->alphaAt(leftX + 1, topY) : OPACITY_TRANSPARENT;
00669             Q_UINT8 bottomRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcMask->alphaAt(leftX + 1, topY + 1) : OPACITY_TRANSPARENT;
00670 
00671             double a = 1 - xInterp;
00672             double b = 1 - yInterp;
00673 
00674             // Bi-linear interpolation
00675             int d = static_cast<int>(a * b * topLeft
00676                 + a * (1 - b) * bottomLeft
00677                 + (1 - a) * b * topRight
00678                 + (1 - a) * (1 - b) * bottomRight + 0.5);
00679 
00680             if (d < OPACITY_TRANSPARENT) {
00681                 d = OPACITY_TRANSPARENT;
00682             }
00683             else
00684             if (d > OPACITY_OPAQUE) {
00685                 d = OPACITY_OPAQUE;
00686             }
00687 
00688             dstMask->setAlphaAt(dstX, dstY, static_cast<Q_UINT8>(d));
00689         }
00690     }
00691 
00692     return dstMask;
00693 }
00694 
00695 QImage KisBrush::scaleImage(const ScaledBrush *srcBrush, double scale, double subPixelX, double subPixelY) const
00696 {
00697     // Add one pixel for sub-pixel shifting
00698     int dstWidth = static_cast<int>(ceil(scale * width())) + 1;
00699     int dstHeight = static_cast<int>(ceil(scale * height())) + 1;
00700 
00701     QImage dstImage(dstWidth, dstHeight, 32);
00702     dstImage.setAlphaBuffer(true);
00703 
00704     const QImage srcImage = srcBrush->image();
00705 
00706     // Compute scales to map the scaled brush onto the required scale.
00707     double xScale = srcBrush->xScale() / scale;
00708     double yScale = srcBrush->yScale() / scale;
00709 
00710     int srcWidth = srcImage.width();
00711     int srcHeight = srcImage.height();
00712 
00713     for (int dstY = 0; dstY < dstHeight; dstY++) {
00714         for (int dstX = 0; dstX < dstWidth; dstX++) {
00715 
00716             double srcX = (dstX - subPixelX + 0.5) * xScale;
00717             double srcY = (dstY - subPixelY + 0.5) * yScale;
00718 
00719             srcX -= 0.5;
00720             srcY -= 0.5;
00721 
00722             int leftX = static_cast<int>(srcX);
00723 
00724             if (srcX < 0) {
00725                 leftX--;
00726             }
00727 
00728             double xInterp = srcX - leftX;
00729 
00730             int topY = static_cast<int>(srcY);
00731 
00732             if (srcY < 0) {
00733                 topY--;
00734             }
00735 
00736             double yInterp = srcY - topY;
00737 
00738             QRgb topLeft = (leftX >= 0 && leftX < srcWidth && topY >= 0 && topY < srcHeight) ? srcImage.pixel(leftX, topY) : qRgba(0, 0, 0, 0);
00739             QRgb bottomLeft = (leftX >= 0 && leftX < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcImage.pixel(leftX, topY + 1) : qRgba(0, 0, 0, 0);
00740             QRgb topRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY >= 0 && topY < srcHeight) ? srcImage.pixel(leftX + 1, topY) : qRgba(0, 0, 0, 0);
00741             QRgb bottomRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcImage.pixel(leftX + 1, topY + 1) : qRgba(0, 0, 0, 0);
00742 
00743             double a = 1 - xInterp;
00744             double b = 1 - yInterp;
00745 
00746             // Bi-linear interpolation. Image is pre-multiplied by alpha.
00747             int red = static_cast<int>(a * b * qRed(topLeft)
00748                 + a * (1 - b) * qRed(bottomLeft)
00749                 + (1 - a) * b * qRed(topRight)
00750                 + (1 - a) * (1 - b) * qRed(bottomRight) + 0.5);
00751             int green = static_cast<int>(a * b * qGreen(topLeft)
00752                 + a * (1 - b) * qGreen(bottomLeft)
00753                 + (1 - a) * b * qGreen(topRight)
00754                 + (1 - a) * (1 - b) * qGreen(bottomRight) + 0.5);
00755             int blue = static_cast<int>(a * b * qBlue(topLeft)
00756                 + a * (1 - b) * qBlue(bottomLeft)
00757                 + (1 - a) * b * qBlue(topRight)
00758                 + (1 - a) * (1 - b) * qBlue(bottomRight) + 0.5);
00759             int alpha = static_cast<int>(a * b * qAlpha(topLeft)
00760                 + a * (1 - b) * qAlpha(bottomLeft)
00761                 + (1 - a) * b * qAlpha(topRight)
00762                 + (1 - a) * (1 - b) * qAlpha(bottomRight) + 0.5);
00763 
00764             if (red < 0) {
00765                 red = 0;
00766             }
00767             else
00768             if (red > 255) {
00769                 red = 255;
00770             }
00771 
00772             if (green < 0) {
00773                 green = 0;
00774             }
00775             else
00776             if (green > 255) {
00777                 green = 255;
00778             }
00779 
00780             if (blue < 0) {
00781                 blue = 0;
00782             }
00783             else
00784             if (blue > 255) {
00785                 blue = 255;
00786             }
00787 
00788             if (alpha < 0) {
00789                 alpha = 0;
00790             }
00791             else
00792             if (alpha > 255) {
00793                 alpha = 255;
00794             }
00795 
00796             dstImage.setPixel(dstX, dstY, qRgba(red, green, blue, alpha));
00797         }
00798     }
00799 
00800     return dstImage;
00801 }
00802 
00803 QImage KisBrush::scaleImage(const QImage& srcImage, int width, int height)
00804 {
00805     QImage scaledImage;
00806     //QString filename;
00807 
00808     int srcWidth = srcImage.width();
00809     int srcHeight = srcImage.height();
00810 
00811     double xScale = static_cast<double>(srcWidth) / width;
00812     double yScale = static_cast<double>(srcHeight) / height;
00813 
00814     if (xScale > 2 + DBL_EPSILON || yScale > 2 + DBL_EPSILON || xScale < 1 - DBL_EPSILON || yScale < 1 - DBL_EPSILON) {
00815         // smoothScale gives better results when scaling an image up
00816         // or scaling it to less than half size.
00817         scaledImage = srcImage.smoothScale(width, height);
00818 
00819         //filename = QString("smoothScale_%1x%2.png").arg(width).arg(height);
00820     }
00821     else {
00822         scaledImage.create(width, height, 32);
00823         scaledImage.setAlphaBuffer(srcImage.hasAlphaBuffer());
00824 
00825         for (int dstY = 0; dstY < height; dstY++) {
00826             for (int dstX = 0; dstX < width; dstX++) {
00827 
00828                 double srcX = (dstX + 0.5) * xScale;
00829                 double srcY = (dstY + 0.5) * yScale;
00830 
00831                 srcX -= 0.5;
00832                 srcY -= 0.5;
00833 
00834                 int leftX = static_cast<int>(srcX);
00835 
00836                 if (srcX < 0) {
00837                     leftX--;
00838                 }
00839 
00840                 double xInterp = srcX - leftX;
00841 
00842                 int topY = static_cast<int>(srcY);
00843 
00844                 if (srcY < 0) {
00845                     topY--;
00846                 }
00847 
00848                 double yInterp = srcY - topY;
00849 
00850                 QRgb topLeft = (leftX >= 0 && leftX < srcWidth && topY >= 0 && topY < srcHeight) ? srcImage.pixel(leftX, topY) : qRgba(0, 0, 0, 0);
00851                 QRgb bottomLeft = (leftX >= 0 && leftX < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcImage.pixel(leftX, topY + 1) : qRgba(0, 0, 0, 0);
00852                 QRgb topRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY >= 0 && topY < srcHeight) ? srcImage.pixel(leftX + 1, topY) : qRgba(0, 0, 0, 0);
00853                 QRgb bottomRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcImage.pixel(leftX + 1, topY + 1) : qRgba(0, 0, 0, 0);
00854 
00855                 double a = 1 - xInterp;
00856                 double b = 1 - yInterp;
00857 
00858                 int red;
00859                 int green;
00860                 int blue;
00861                 int alpha;
00862 
00863                 if (srcImage.hasAlphaBuffer()) {
00864                     red = static_cast<int>(a * b * qRed(topLeft)         * qAlpha(topLeft)
00865                         + a * (1 - b) * qRed(bottomLeft)             * qAlpha(bottomLeft)
00866                         + (1 - a) * b * qRed(topRight)               * qAlpha(topRight)
00867                         + (1 - a) * (1 - b) * qRed(bottomRight)      * qAlpha(bottomRight) + 0.5);
00868                     green = static_cast<int>(a * b * qGreen(topLeft)     * qAlpha(topLeft)
00869                         + a * (1 - b) * qGreen(bottomLeft)           * qAlpha(bottomLeft)
00870                         + (1 - a) * b * qGreen(topRight)             * qAlpha(topRight)
00871                         + (1 - a) * (1 - b) * qGreen(bottomRight)    * qAlpha(bottomRight) + 0.5);
00872                     blue = static_cast<int>(a * b * qBlue(topLeft)       * qAlpha(topLeft)
00873                         + a * (1 - b) * qBlue(bottomLeft)            * qAlpha(bottomLeft)
00874                         + (1 - a) * b * qBlue(topRight)              * qAlpha(topRight)
00875                         + (1 - a) * (1 - b) * qBlue(bottomRight)     * qAlpha(bottomRight) + 0.5);
00876                     alpha = static_cast<int>(a * b * qAlpha(topLeft)
00877                         + a * (1 - b) * qAlpha(bottomLeft)
00878                         + (1 - a) * b * qAlpha(topRight)
00879                         + (1 - a) * (1 - b) * qAlpha(bottomRight) + 0.5);
00880 
00881                     if (alpha != 0) {
00882                         red /= alpha;
00883                         green /= alpha;
00884                         blue /= alpha;
00885                     }
00886                 }
00887                 else {
00888                     red = static_cast<int>(a * b * qRed(topLeft)
00889                         + a * (1 - b) * qRed(bottomLeft)
00890                         + (1 - a) * b * qRed(topRight)
00891                         + (1 - a) * (1 - b) * qRed(bottomRight) + 0.5);
00892                     green = static_cast<int>(a * b * qGreen(topLeft)
00893                         + a * (1 - b) * qGreen(bottomLeft)
00894                         + (1 - a) * b * qGreen(topRight)
00895                         + (1 - a) * (1 - b) * qGreen(bottomRight) + 0.5);
00896                     blue = static_cast<int>(a * b * qBlue(topLeft)
00897                         + a * (1 - b) * qBlue(bottomLeft)
00898                         + (1 - a) * b * qBlue(topRight)
00899                         + (1 - a) * (1 - b) * qBlue(bottomRight) + 0.5);
00900                     alpha = 255;
00901                 }
00902 
00903                 if (red < 0) {
00904                     red = 0;
00905                 }
00906                 else
00907                 if (red > 255) {
00908                     red = 255;
00909                 }
00910 
00911                 if (green < 0) {
00912                     green = 0;
00913                 }
00914                 else
00915                 if (green > 255) {
00916                     green = 255;
00917                 }
00918 
00919                 if (blue < 0) {
00920                     blue = 0;
00921                 }
00922                 else
00923                 if (blue > 255) {
00924                     blue = 255;
00925                 }
00926 
00927                 if (alpha < 0) {
00928                     alpha = 0;
00929                 }
00930                 else
00931                 if (alpha > 255) {
00932                     alpha = 255;
00933                 }
00934 
00935                 scaledImage.setPixel(dstX, dstY, qRgba(red, green, blue, alpha));
00936             }
00937         }
00938 
00939         //filename = QString("bilinear_%1x%2.png").arg(width).arg(height);
00940     }
00941 
00942     //scaledImage.save(filename, "PNG");
00943 
00944     return scaledImage;
00945 }
00946 
00947 void KisBrush::findScaledBrushes(double scale, const ScaledBrush **aboveBrush, const ScaledBrush **belowBrush) const
00948 {
00949     uint current = 0;
00950 
00951     while (true) {
00952         *aboveBrush = &(m_scaledBrushes[current]);
00953 
00954         if (fabs((*aboveBrush)->scale() - scale) < DBL_EPSILON) {
00955             // Scale matches exactly
00956             break;
00957         }
00958 
00959         if (current == m_scaledBrushes.count() - 1) {
00960             // This is the last one
00961             break;
00962         }
00963 
00964         if (scale > m_scaledBrushes[current + 1].scale() + DBL_EPSILON) {
00965             // We fit in between the two.
00966             *belowBrush = &(m_scaledBrushes[current + 1]);
00967             break;
00968         }
00969 
00970         current++;
00971     }
00972 }
00973 
00974 KisAlphaMaskSP KisBrush::scaleSinglePixelMask(double scale, Q_UINT8 maskValue, double subPixelX, double subPixelY)
00975 {
00976     int srcWidth = 1;
00977     int srcHeight = 1;
00978     int dstWidth = 2;
00979     int dstHeight = 2;
00980     KisAlphaMaskSP outputMask = new KisAlphaMask(dstWidth, dstHeight);
00981     Q_CHECK_PTR(outputMask);
00982 
00983     double a = subPixelX;
00984     double b = subPixelY;
00985 
00986     for (int y = 0; y < dstHeight; y++) {
00987         for (int x = 0; x < dstWidth; x++) {
00988 
00989             Q_UINT8 topLeft = (x > 0 && y > 0) ? maskValue : OPACITY_TRANSPARENT;
00990             Q_UINT8 bottomLeft = (x > 0 && y < srcHeight) ? maskValue : OPACITY_TRANSPARENT;
00991             Q_UINT8 topRight = (x < srcWidth && y > 0) ? maskValue : OPACITY_TRANSPARENT;
00992             Q_UINT8 bottomRight = (x < srcWidth && y < srcHeight) ? maskValue : OPACITY_TRANSPARENT;
00993 
00994             // Bi-linear interpolation
00995             int d = static_cast<int>(a * b * topLeft
00996                 + a * (1 - b) * bottomLeft
00997                 + (1 - a) * b * topRight
00998                 + (1 - a) * (1 - b) * bottomRight + 0.5);
00999 
01000             // Multiply by the square of the scale because a 0.5x0.5 pixel
01001             // has 0.25 the value of the 1x1.
01002             d = static_cast<int>(d * scale * scale + 0.5);
01003 
01004             if (d < OPACITY_TRANSPARENT) {
01005                 d = OPACITY_TRANSPARENT;
01006             }
01007             else
01008             if (d > OPACITY_OPAQUE) {
01009                 d = OPACITY_OPAQUE;
01010             }
01011 
01012             outputMask->setAlphaAt(x, y, static_cast<Q_UINT8>(d));
01013         }
01014     }
01015 
01016     return outputMask;
01017 }
01018 
01019 QImage KisBrush::scaleSinglePixelImage(double scale, QRgb pixel, double subPixelX, double subPixelY)
01020 {
01021     int srcWidth = 1;
01022     int srcHeight = 1;
01023     int dstWidth = 2;
01024     int dstHeight = 2;
01025 
01026     QImage outputImage(dstWidth, dstHeight, 32);
01027     outputImage.setAlphaBuffer(true);
01028 
01029     double a = subPixelX;
01030     double b = subPixelY;
01031 
01032     for (int y = 0; y < dstHeight; y++) {
01033         for (int x = 0; x < dstWidth; x++) {
01034 
01035             QRgb topLeft = (x > 0 && y > 0) ? pixel : qRgba(0, 0, 0, 0);
01036             QRgb bottomLeft = (x > 0 && y < srcHeight) ? pixel : qRgba(0, 0, 0, 0);
01037             QRgb topRight = (x < srcWidth && y > 0) ? pixel : qRgba(0, 0, 0, 0);
01038             QRgb bottomRight = (x < srcWidth && y < srcHeight) ? pixel : qRgba(0, 0, 0, 0);
01039 
01040             // Bi-linear interpolation. Images are in pre-multiplied form.
01041             int red = static_cast<int>(a * b * qRed(topLeft)
01042                 + a * (1 - b) * qRed(bottomLeft)
01043                 + (1 - a) * b * qRed(topRight)
01044                 + (1 - a) * (1 - b) * qRed(bottomRight) + 0.5);
01045             int green = static_cast<int>(a * b * qGreen(topLeft)
01046                 + a * (1 - b) * qGreen(bottomLeft)
01047                 + (1 - a) * b * qGreen(topRight)
01048                 + (1 - a) * (1 - b) * qGreen(bottomRight) + 0.5);
01049             int blue = static_cast<int>(a * b * qBlue(topLeft)
01050                 + a * (1 - b) * qBlue(bottomLeft)
01051                 + (1 - a) * b * qBlue(topRight)
01052                 + (1 - a) * (1 - b) * qBlue(bottomRight) + 0.5);
01053             int alpha = static_cast<int>(a * b * qAlpha(topLeft)
01054                 + a * (1 - b) * qAlpha(bottomLeft)
01055                 + (1 - a) * b * qAlpha(topRight)
01056                 + (1 - a) * (1 - b) * qAlpha(bottomRight) + 0.5);
01057 
01058             // Multiply by the square of the scale because a 0.5x0.5 pixel
01059             // has 0.25 the value of the 1x1.
01060             alpha = static_cast<int>(alpha * scale * scale + 0.5);
01061 
01062             // Apply to the colour channels too since we are
01063             // storing pre-multiplied by alpha.
01064             red = static_cast<int>(red * scale * scale + 0.5);
01065             green = static_cast<int>(green * scale * scale + 0.5);
01066             blue = static_cast<int>(blue * scale * scale + 0.5);
01067 
01068             if (red < 0) {
01069                 red = 0;
01070             }
01071             else
01072             if (red > 255) {
01073                 red = 255;
01074             }
01075 
01076             if (green < 0) {
01077                 green = 0;
01078             }
01079             else
01080             if (green > 255) {
01081                 green = 255;
01082             }
01083 
01084             if (blue < 0) {
01085                 blue = 0;
01086             }
01087             else
01088             if (blue > 255) {
01089                 blue = 255;
01090             }
01091 
01092             if (alpha < 0) {
01093                 alpha = 0;
01094             }
01095             else
01096             if (alpha > 255) {
01097                 alpha = 255;
01098             }
01099 
01100             outputImage.setPixel(x, y, qRgba(red, green, blue, alpha));
01101         }
01102     }
01103 
01104     return outputImage;
01105 }
01106 
01107 QImage KisBrush::interpolate(const QImage& image1, const QImage& image2, double t)
01108 {
01109     Q_ASSERT((image1.width() == image2.width()) && (image1.height() == image2.height()));
01110     Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON);
01111 
01112     int width = image1.width();
01113     int height = image1.height();
01114 
01115     QImage outputImage(width, height, 32);
01116     outputImage.setAlphaBuffer(true);
01117 
01118     for (int x = 0; x < width; x++) {
01119         for (int y = 0; y < height; y++) {
01120             QRgb image1pixel = image1.pixel(x, y);
01121             QRgb image2pixel = image2.pixel(x, y);
01122 
01123             // Images are in pre-multiplied alpha format.
01124             int red = static_cast<int>((1 - t) * qRed(image1pixel) + t * qRed(image2pixel) + 0.5);
01125             int green = static_cast<int>((1 - t) * qGreen(image1pixel) + t * qGreen(image2pixel) + 0.5);
01126             int blue = static_cast<int>((1 - t) * qBlue(image1pixel) + t * qBlue(image2pixel) + 0.5);
01127             int alpha = static_cast<int>((1 - t) * qAlpha(image1pixel) + t * qAlpha(image2pixel) + 0.5);
01128 
01129             if (red < 0) {
01130                 red = 0;
01131             }
01132             else
01133             if (red > 255) {
01134                 red = 255;
01135             }
01136 
01137             if (green < 0) {
01138                 green = 0;
01139             }
01140             else
01141             if (green > 255) {
01142                 green = 255;
01143             }
01144 
01145             if (blue < 0) {
01146                 blue = 0;
01147             }
01148             else
01149             if (blue > 255) {
01150                 blue = 255;
01151             }
01152 
01153             if (alpha < 0) {
01154                 alpha = 0;
01155             }
01156             else
01157             if (alpha > 255) {
01158                 alpha = 255;
01159             }
01160 
01161             outputImage.setPixel(x, y, qRgba(red, green, blue, alpha));
01162         }
01163     }
01164 
01165     return outputImage;
01166 }
01167 
01168 KisBrush::ScaledBrush::ScaledBrush()
01169 {
01170     m_mask = 0;
01171     m_image = QImage();
01172     m_scale = 1;
01173     m_xScale = 1;
01174     m_yScale = 1;
01175 }
01176 
01177 KisBrush::ScaledBrush::ScaledBrush(KisAlphaMaskSP scaledMask, const QImage& scaledImage, double scale, double xScale, double yScale)
01178 {
01179     m_mask = scaledMask;
01180     m_image = scaledImage;
01181     m_scale = scale;
01182     m_xScale = xScale;
01183     m_yScale = yScale;
01184 
01185     if (!m_image.isNull()) {
01186         // Convert image to pre-multiplied by alpha.
01187 
01188         m_image.detach();
01189 
01190         for (int y = 0; y < m_image.height(); y++) {
01191             for (int x = 0; x < m_image.width(); x++) {
01192 
01193                 QRgb pixel = m_image.pixel(x, y);
01194 
01195                 int red = qRed(pixel);
01196                 int green = qGreen(pixel);
01197                 int blue = qBlue(pixel);
01198                 int alpha = qAlpha(pixel);
01199 
01200                 red = (red * alpha) / 255;
01201                 green = (green * alpha) / 255;
01202                 blue = (blue * alpha) / 255;
01203 
01204                 m_image.setPixel(x, y, qRgba(red, green, blue, alpha));
01205             }
01206         }
01207     }
01208 }
01209 
01210 void KisBrush::setImage(const QImage& img)
01211 {
01212     m_img = img;
01213     m_img.detach();
01214 
01215     setWidth(img.width());
01216     setHeight(img.height());
01217 
01218     m_scaledBrushes.clear();
01219 
01220     setValid(true);
01221 }
01222 
01223 Q_INT32 KisBrush::width() const
01224 {
01225     return m_width;
01226 }
01227 
01228 void KisBrush::setWidth(Q_INT32 w)
01229 {
01230     m_width = w;
01231 }
01232 
01233 Q_INT32 KisBrush::height() const
01234 {
01235     return m_height;
01236 }
01237 
01238 void KisBrush::setHeight(Q_INT32 h)
01239 {
01240     m_height = h;
01241 }
01242 
01243 /*QImage KisBrush::outline(double pressure) {
01244     KisLayerSP layer = image(KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("RGBA",""),""),
01245                              KisPaintInformation(pressure));
01246     KisBoundary bounds(layer.data());
01247     int w = maskWidth(pressure);
01248     int h = maskHeight(pressure);
01249 
01250     bounds.generateBoundary(w, h);
01251     QPixmap pix(bounds.pixmap(w, h));
01252     QImage result;
01253     result = pix;
01254     return result;
01255 }*/
01256 
01257 void KisBrush::generateBoundary() {
01258     KisPaintDeviceSP dev;
01259     int w = maskWidth(KisPaintInformation());
01260     int h = maskHeight(KisPaintInformation());
01261 
01262     if (brushType() == IMAGE || brushType() == PIPE_IMAGE) {
01263         dev = image(KisMetaRegistry::instance()->csRegistry() ->getColorSpace(KisID("RGBA",""),""), KisPaintInformation());
01264     } else {
01265         KisAlphaMaskSP amask = mask(KisPaintInformation());
01266         KisColorSpace* cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("RGBA",""),"");
01267         dev = new KisPaintDevice(cs, "tmp for generateBoundary");
01268         for (int y = 0; y < h; y++) {
01269             KisHLineIteratorPixel it = dev->createHLineIterator(0, y, w, true);
01270             int x = 0;
01271 
01272             while(!it.isDone()) {
01273                 cs->setAlpha(it.rawData(), amask->alphaAt(x++, y), 1);
01274                 ++it;
01275             }
01276         }
01277     }
01278 
01279     m_boundary = new KisBoundary(dev);
01280     m_boundary->generateBoundary(w, h);
01281 }
01282 
01283 KisBoundary KisBrush::boundary() {
01284     if (!m_boundary)
01285         generateBoundary();
01286     return *m_boundary;
01287 }
01288 
01289 void KisBrush::makeMaskImage() {
01290     if (!hasColor())
01291         return;
01292 
01293     QImage img;
01294     img.create(width(), height(), 32);
01295 
01296     if (m_img.width() == img.width() && m_img.height() == img.height()) {
01297         for (int x = 0; x < width(); x++) {
01298             for (int y = 0; y < height(); y++) {
01299                 QRgb c = m_img.pixel(x, y);
01300                 int a = (qGray(c) * qAlpha(c)) / 255; // qGray(black) = 0
01301                 img.setPixel(x, y, qRgba(a, a, a, 255));
01302             }
01303         }
01304 
01305         m_img = img;
01306     }
01307 
01308     m_brushType = MASK;
01309     m_hasColor = false;
01310     m_useColorAsMask = false;
01311     delete m_boundary;
01312     m_boundary = 0;
01313     m_scaledBrushes.clear();
01314 }
01315 
01316 KisBrush* KisBrush::clone() const {
01317     KisBrush* c = new KisBrush("");
01318     c->m_spacing = m_spacing;
01319     c->m_useColorAsMask = m_useColorAsMask;
01320     c->m_hasColor = m_useColorAsMask;
01321     c->m_img = m_img;
01322     c->m_width = m_width;
01323     c->m_height = m_height;
01324     c->m_ownData = false;
01325     c->m_hotSpot = m_hotSpot;
01326     c->m_brushType = m_brushType;
01327     c->setValid(true);
01328 
01329     return c;
01330 }
01331 
01332 #include "kis_brush.moc"
01333 
KDE Home | KDE Accessibility Home | Description of Access Keys