krita

kis_fill_painter.cc

00001 /*
00002  *  Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
00003  *  Copyright (c) 2004 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 #include <stdlib.h>
00021 #include <string.h>
00022 #include <cfloat>
00023 #include <stack>
00024 
00025 #include "qbrush.h"
00026 #include "qfontinfo.h"
00027 #include "qfontmetrics.h"
00028 #include "qpen.h"
00029 #include "qregion.h"
00030 #include "qwmatrix.h"
00031 #include <qimage.h>
00032 #include <qmap.h>
00033 #include <qpainter.h>
00034 #include <qpixmap.h>
00035 #include <qpointarray.h>
00036 #include <qrect.h>
00037 #include <qstring.h>
00038 
00039 #include <kdebug.h>
00040 #include <kcommand.h>
00041 #include <klocale.h>
00042 
00043 #include "kis_brush.h"
00044 #include "kis_debug_areas.h"
00045 #include "kis_image.h"
00046 #include "kis_layer.h"
00047 #include "kis_paint_device.h"
00048 #include "kis_painter.h"
00049 #include "kis_pattern.h"
00050 #include "kis_rect.h"
00051 #include "kis_colorspace.h"
00052 #include "kis_transaction.h"
00053 #include "kis_types.h"
00054 #include "kis_vec.h"
00055 #include "kis_selection.h"
00056 #include "kis_fill_painter.h"
00057 #include "kis_iterators_pixel.h"
00058 #include "kis_iterator.h"
00059 #include "kis_color.h"
00060 #include "kis_selection.h"
00061 
00062 namespace {
00063 }
00064 
00065 KisFillPainter::KisFillPainter()
00066     : super()
00067 {
00068     m_width = m_height = -1;
00069     m_sampleMerged = false;
00070     m_careForSelection = false;
00071     m_fuzzy = false;
00072 }
00073 
00074 KisFillPainter::KisFillPainter(KisPaintDeviceSP device) : super(device)
00075 {
00076     m_width = m_height = -1;
00077     m_sampleMerged = false;
00078     m_careForSelection = false;
00079     m_fuzzy = false;
00080 }
00081 
00082 // 'regular' filling
00083 // XXX: This also needs renaming, since filling ought to keep the opacity and the composite op in mind,
00084 //      this is more eraseToColor.
00085 void KisFillPainter::fillRect(Q_INT32 x1, Q_INT32 y1, Q_INT32 w, Q_INT32 h, const KisColor& kc, Q_UINT8 opacity)
00086 {
00087     if (w > 0 && h > 0) {
00088         // Make sure we're in the right colorspace
00089 
00090         KisColor kc2(kc); // get rid of const
00091         kc2.convertTo(m_device->colorSpace());
00092         Q_UINT8 * data = kc2.data();
00093         m_device->colorSpace()->setAlpha(data, opacity, 1);
00094 
00095         m_device->fill(x1, y1, w, h, data);
00096 
00097         addDirtyRect(QRect(x1, y1, w, h));
00098     }
00099 }
00100 
00101 void KisFillPainter::fillRect(Q_INT32 x1, Q_INT32 y1, Q_INT32 w, Q_INT32 h, KisPattern * pattern) {
00102     if (!pattern) return;
00103     if (!pattern->valid()) return;
00104     if (!m_device) return;
00105 
00106 
00107     KisPaintDeviceSP patternLayer = pattern->image(m_device->colorSpace());
00108 
00109     int sx, sy, sw, sh;
00110 
00111     int y = y1;
00112 
00113     if (y >= 0) {
00114         sy = y % pattern->height();
00115     } else {
00116         sy = pattern->height() - (((-y - 1) % pattern->height()) + 1);
00117     }
00118 
00119     while (y < y1 + h) {
00120         sh = QMIN((y1 + h) - y, pattern->height() - sy);
00121 
00122         int x = x1;
00123 
00124         if (x >= 0) {
00125             sx = x % pattern->width();
00126         } else {
00127             sx = pattern->width() - (((-x - 1) % pattern->width()) + 1);
00128         }
00129 
00130         while (x < x1 + w) {
00131             sw = QMIN((x1 + w) - x, pattern->width() - sx);
00132 
00133             bitBlt(x, y, m_compositeOp, patternLayer.data(), m_opacity, sx, sy, sw, sh);
00134             x += sw; sx = 0;
00135         }
00136 
00137         y+=sh; sy = 0;
00138     }
00139 
00140     addDirtyRect(QRect(x1, y1, w, h));
00141 }
00142 
00143 // flood filling
00144 
00145 void KisFillPainter::fillColor(int startX, int startY) {
00146     genericFillStart(startX, startY);
00147 
00148     // Now create a layer and fill it
00149     KisPaintDeviceSP filled = new KisPaintDevice(m_device->colorSpace(), "filled");
00150     Q_CHECK_PTR(filled);
00151     KisFillPainter painter(filled.data());
00152     painter.fillRect(0, 0, m_width, m_height, m_paintColor);
00153     painter.end();
00154 
00155     genericFillEnd(filled);
00156 }
00157 
00158 void KisFillPainter::fillPattern(int startX, int startY) {
00159     genericFillStart(startX, startY);
00160 
00161     // Now create a layer and fill it
00162     KisPaintDeviceSP filled = new KisPaintDevice(m_device->colorSpace(), "filled");
00163     Q_CHECK_PTR(filled);
00164     KisFillPainter painter(filled.data());
00165     painter.fillRect(0, 0, m_width, m_height, m_pattern);
00166     painter.end();
00167 
00168     genericFillEnd(filled);
00169 }
00170 
00171 void KisFillPainter::genericFillStart(int startX, int startY) {
00172     m_cancelRequested = false;
00173 
00174     if (m_width < 0 || m_height < 0) {
00175         if (m_device->image()) {
00176             m_width = m_device->image()->width();
00177             m_height = m_device->image()->height();
00178         } else {
00179             m_width = m_height = 500;
00180         }
00181     }
00182 
00183     m_size = m_width * m_height;
00184 
00185     // Create a selection from the surrounding area
00186     m_selection = createFloodSelection(startX, startY);
00187 }
00188 
00189 void KisFillPainter::genericFillEnd(KisPaintDeviceSP filled) {
00190     if (m_cancelRequested) {
00191         m_width = m_height = -1;
00192         return;
00193     }
00194 
00195     bltSelection(0, 0, m_compositeOp, filled, m_selection, m_opacity,
00196                  0, 0, m_width, m_height);
00197 
00198     emit notifyProgressDone();
00199 
00200     m_width = m_height = -1;
00201 }
00202 
00203 struct FillSegment {
00204     FillSegment(int x, int y/*, FillSegment* parent*/) : x(x), y(y)/*, parent(parent)*/ {}
00205     int x;
00206     int y;
00207 //    FillSegment* parent;
00208 };
00209 
00210 typedef enum { None = 0, Added = 1, Checked = 2 } Status;
00211 
00212 KisSelectionSP KisFillPainter::createFloodSelection(int startX, int startY) {
00213     if (m_width < 0 || m_height < 0) {
00214         if (m_device->hasSelection() && m_careForSelection) {
00215             Q_INT32 x,y,w,h;
00216             m_device->selection()->extent(x,y,w,h);
00217             m_width = w - (startX - x);
00218             m_height = h - (startY - y);
00219         } else if (m_device->image()) {
00220             m_width = m_device->image()->width();
00221             m_height = m_device->image()->height();
00222         } else {
00223             m_width = m_height = 500;
00224         }
00225     }
00226 
00227     // Don't try to fill if we start outside the borders, just return an empty 'fill'
00228     if (startX < 0 || startY < 0 || startX >= m_width || startY >= m_height)
00229         return new KisSelection(m_device);
00230 
00231     KisPaintDeviceSP sourceDevice = 0;
00232 
00233     // sample merged?
00234     if (m_sampleMerged) {
00235         if (!m_device->image()) {
00236             return new KisSelection(m_device);
00237         }
00238         sourceDevice = m_device->image()->mergedImage();
00239     } else {
00240         sourceDevice = m_device;
00241     }
00242 
00243     m_size = m_width * m_height;
00244 
00245     KisSelectionSP selection = new KisSelection(m_device);
00246     KisColorSpace * colorSpace = selection->colorSpace();
00247     KisColorSpace * devColorSpace = sourceDevice->colorSpace();
00248 
00249     Q_UINT8* source = new Q_UINT8[sourceDevice->pixelSize()];
00250     KisHLineIteratorPixel pixelIt = sourceDevice->createHLineIterator(startX, startY, startX+1, false);
00251 
00252     memcpy(source, pixelIt.rawData(), sourceDevice->pixelSize());
00253 
00254     std::stack<FillSegment*> stack;
00255 
00256     stack.push(new FillSegment(startX, startY/*, 0*/));
00257 
00258     Status* map = new Status[m_size];
00259 
00260     memset(map, None, m_size * sizeof(Status));
00261 
00262     int progressPercent = 0; int pixelsDone = 0; int currentPercent = 0;
00263     emit notifyProgressStage(i18n("Making fill outline..."), 0);
00264 
00265     bool hasSelection = m_careForSelection && sourceDevice->hasSelection();
00266     KisSelectionSP srcSel = 0;
00267     if (hasSelection)
00268         srcSel = sourceDevice->selection();
00269 
00270     while(!stack.empty()) {
00271         FillSegment* segment = stack.top();
00272         stack.pop();
00273         if (map[m_width * segment->y + segment->x] == Checked) {
00274             delete segment;
00275             continue;
00276         }
00277         map[m_width * segment->y + segment->x] = Checked;
00278 
00279         int x = segment->x;
00280         int y = segment->y;
00281 
00282         /* We need an iterator that is valid in the range (0,y) - (width,y). Therefore,
00283         it is needed to start the iterator at the first position, and then skip to (x,y). */
00284         pixelIt = sourceDevice->createHLineIterator(0, y, m_width, false);
00285         pixelIt += x;
00286         Q_UINT8 diff = devColorSpace->difference(source, pixelIt.rawData());
00287 
00288         if (diff >= m_threshold
00289             || (hasSelection && srcSel->selected(pixelIt.x(), pixelIt.y()) == MIN_SELECTED)) {
00290             delete segment;
00291             continue;
00292         }
00293 
00294         // Here as well: start the iterator at (0,y)
00295         KisHLineIteratorPixel selIt = selection->createHLineIterator(0, y, m_width, true);
00296         selIt += x;
00297         if (m_fuzzy)
00298             colorSpace->fromQColor(Qt::white, MAX_SELECTED - diff, selIt.rawData());
00299         else
00300             colorSpace->fromQColor(Qt::white, MAX_SELECTED, selIt.rawData());
00301 
00302         if (y > 0 && (map[m_width * (y - 1) + x] == None)) {
00303             map[m_width * (y - 1) + x] = Added;
00304             stack.push(new FillSegment(x, y-1));
00305         }
00306         if (y < (m_height - 1) && (map[m_width * (y + 1) + x] == None)) {
00307             map[m_width * (y + 1) + x] = Added;
00308             stack.push(new FillSegment(x, y+1));
00309         }
00310 
00311         ++pixelsDone;
00312 
00313         bool stop = false;
00314 
00315         --pixelIt;
00316         --selIt;
00317         --x;
00318 
00319         // go to the left
00320         while(!stop && x >= 0 && (map[m_width * y + x] != Checked) ) { // FIXME optimizeable?
00321             map[m_width * y + x] = Checked;
00322             diff = devColorSpace->difference(source, pixelIt.rawData());
00323             if (diff >= m_threshold
00324                 || (hasSelection && srcSel->selected(pixelIt.x(), pixelIt.y()) == MIN_SELECTED)) {
00325                 stop = true;
00326                 continue;
00327             }
00328 
00329             if (m_fuzzy)
00330                 colorSpace->fromQColor(Qt::white, MAX_SELECTED - diff, selIt.rawData());
00331             else
00332                 colorSpace->fromQColor(Qt::white, MAX_SELECTED, selIt.rawData());
00333 
00334             if (y > 0 && (map[m_width * (y - 1) + x] == None)) {
00335                 map[m_width * (y - 1) + x] = Added;
00336                 stack.push(new FillSegment(x, y-1));
00337             }
00338             if (y < (m_height - 1) && (map[m_width * (y + 1) + x] == None)) {
00339                 map[m_width * (y + 1) + x] = Added;
00340                 stack.push(new FillSegment(x, y+1));
00341             }
00342             ++pixelsDone;
00343             --pixelIt;
00344             --selIt;
00345             --x;
00346         }
00347 
00348         x = segment->x + 1;
00349         delete segment;
00350 
00351         if (map[m_width * y + x] == Checked)
00352             continue;
00353 
00354         // and go to the right
00355         pixelIt = sourceDevice->createHLineIterator(x, y, m_width, false);
00356         selIt = selection->createHLineIterator(x, y, m_width, true);
00357 
00358         stop = false;
00359         while(!stop && x < m_width && (map[m_width * y + x] != Checked) ) {
00360             diff = devColorSpace->difference(source, pixelIt.rawData());
00361             map[m_width * y + x] = Checked;
00362 
00363             if (diff >= m_threshold
00364                 || (hasSelection && srcSel->selected(pixelIt.x(), pixelIt.y()) == MIN_SELECTED) ) {
00365                 stop = true;
00366                 continue;
00367             }
00368 
00369             if (m_fuzzy)
00370                 colorSpace->fromQColor(Qt::white, MAX_SELECTED - diff, selIt.rawData());
00371             else
00372                 colorSpace->fromQColor(Qt::white, MAX_SELECTED, selIt.rawData());
00373 
00374             if (y > 0 && (map[m_width * (y - 1) + x] == None)) {
00375                 map[m_width * (y - 1) + x] = Added;
00376                 stack.push(new FillSegment(x, y-1));
00377             }
00378             if (y < (m_height - 1) && (map[m_width * (y + 1) + x] == None)) {
00379                 map[m_width * (y + 1) + x] = Added;
00380                 stack.push(new FillSegment(x, y+1));
00381             }
00382             ++pixelsDone;
00383             ++pixelIt;
00384             ++selIt;
00385             ++x;
00386         }
00387 
00388         if (m_size > 0) {
00389             progressPercent = (pixelsDone * 100) / m_size;
00390             if (progressPercent > currentPercent) {
00391                 emit notifyProgress(progressPercent);
00392                 currentPercent = progressPercent;
00393             }
00394         }
00395     }
00396 
00397 
00398     delete[] map;
00399     delete[] source;
00400 
00401     return selection;
00402 }
KDE Home | KDE Accessibility Home | Description of Access Keys