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     QRect rc = m_selection->selectedRect();
00196 
00197     bltSelection(rc.x(), rc.y(), m_compositeOp, filled, m_selection, m_opacity,
00198                  rc.x(), rc.y(), rc.width(), rc.height());
00199 
00200     emit notifyProgressDone();
00201 
00202     m_width = m_height = -1;
00203 }
00204 
00205 struct FillSegment {
00206     FillSegment(int x, int y/*, FillSegment* parent*/) : x(x), y(y)/*, parent(parent)*/ {}
00207     int x;
00208     int y;
00209 //    FillSegment* parent;
00210 };
00211 
00212 typedef enum { None = 0, Added = 1, Checked = 2 } Status;
00213 
00214 KisSelectionSP KisFillPainter::createFloodSelection(int startX, int startY) {
00215     if (m_width < 0 || m_height < 0) {
00216         if (m_device->hasSelection() && m_careForSelection) {
00217 
00218             QRect rc = m_device->selection()->selectedRect();
00219             m_width = rc.width() - (startX - rc.x());
00220             m_height = rc.height() - (startY - rc.y());
00221 
00222         } else if (m_device->image()) {
00223 
00224             m_width = m_device->image()->width();
00225             m_height = m_device->image()->height();
00226 
00227         } else {
00228             m_width = m_height = 500;
00229         }
00230     }
00231 
00232     // Don't try to fill if we start outside the borders, just return an empty 'fill'
00233     if (startX < 0 || startY < 0 || startX >= m_width || startY >= m_height)
00234         return new KisSelection(m_device);
00235 
00236     KisPaintDeviceSP sourceDevice = 0;
00237 
00238     // sample merged?
00239     if (m_sampleMerged) {
00240         if (!m_device->image()) {
00241             return new KisSelection(m_device);
00242         }
00243         sourceDevice = m_device->image()->mergedImage();
00244     } else {
00245         sourceDevice = m_device;
00246     }
00247 
00248     m_size = m_width * m_height;
00249 
00250     KisSelectionSP selection = new KisSelection(m_device);
00251     KisColorSpace * colorSpace = selection->colorSpace();
00252     KisColorSpace * devColorSpace = sourceDevice->colorSpace();
00253 
00254     Q_UINT8* source = new Q_UINT8[sourceDevice->pixelSize()];
00255     KisHLineIteratorPixel pixelIt = sourceDevice->createHLineIterator(startX, startY, startX+1, false);
00256 
00257     memcpy(source, pixelIt.rawData(), sourceDevice->pixelSize());
00258 
00259     std::stack<FillSegment*> stack;
00260 
00261     stack.push(new FillSegment(startX, startY/*, 0*/));
00262 
00263     Status* map = new Status[m_size];
00264 
00265     memset(map, None, m_size * sizeof(Status));
00266 
00267     int progressPercent = 0; int pixelsDone = 0; int currentPercent = 0;
00268     emit notifyProgressStage(i18n("Making fill outline..."), 0);
00269 
00270     bool hasSelection = m_careForSelection && sourceDevice->hasSelection();
00271     KisSelectionSP srcSel = 0;
00272     if (hasSelection)
00273         srcSel = sourceDevice->selection();
00274 
00275     while(!stack.empty()) {
00276         FillSegment* segment = stack.top();
00277         stack.pop();
00278         if (map[m_width * segment->y + segment->x] == Checked) {
00279             delete segment;
00280             continue;
00281         }
00282         map[m_width * segment->y + segment->x] = Checked;
00283 
00284         int x = segment->x;
00285         int y = segment->y;
00286 
00287         /* We need an iterator that is valid in the range (0,y) - (width,y). Therefore,
00288         it is needed to start the iterator at the first position, and then skip to (x,y). */
00289         pixelIt = sourceDevice->createHLineIterator(0, y, m_width, false);
00290         pixelIt += x;
00291         Q_UINT8 diff = devColorSpace->difference(source, pixelIt.rawData());
00292 
00293         if (diff >= m_threshold
00294             || (hasSelection && srcSel->selected(pixelIt.x(), pixelIt.y()) == MIN_SELECTED)) {
00295             delete segment;
00296             continue;
00297         }
00298 
00299         // Here as well: start the iterator at (0,y)
00300         KisHLineIteratorPixel selIt = selection->createHLineIterator(0, y, m_width, true);
00301         selIt += x;
00302         if (m_fuzzy)
00303             colorSpace->fromQColor(Qt::white, MAX_SELECTED - diff, selIt.rawData());
00304         else
00305             colorSpace->fromQColor(Qt::white, MAX_SELECTED, selIt.rawData());
00306 
00307         if (y > 0 && (map[m_width * (y - 1) + x] == None)) {
00308             map[m_width * (y - 1) + x] = Added;
00309             stack.push(new FillSegment(x, y-1));
00310         }
00311         if (y < (m_height - 1) && (map[m_width * (y + 1) + x] == None)) {
00312             map[m_width * (y + 1) + x] = Added;
00313             stack.push(new FillSegment(x, y+1));
00314         }
00315 
00316         ++pixelsDone;
00317 
00318         bool stop = false;
00319 
00320         --pixelIt;
00321         --selIt;
00322         --x;
00323 
00324         // go to the left
00325         while(!stop && x >= 0 && (map[m_width * y + x] != Checked) ) { // FIXME optimizeable?
00326             map[m_width * y + x] = Checked;
00327             diff = devColorSpace->difference(source, pixelIt.rawData());
00328             if (diff >= m_threshold
00329                 || (hasSelection && srcSel->selected(pixelIt.x(), pixelIt.y()) == MIN_SELECTED)) {
00330                 stop = true;
00331                 continue;
00332             }
00333 
00334             if (m_fuzzy)
00335                 colorSpace->fromQColor(Qt::white, MAX_SELECTED - diff, selIt.rawData());
00336             else
00337                 colorSpace->fromQColor(Qt::white, MAX_SELECTED, selIt.rawData());
00338 
00339             if (y > 0 && (map[m_width * (y - 1) + x] == None)) {
00340                 map[m_width * (y - 1) + x] = Added;
00341                 stack.push(new FillSegment(x, y-1));
00342             }
00343             if (y < (m_height - 1) && (map[m_width * (y + 1) + x] == None)) {
00344                 map[m_width * (y + 1) + x] = Added;
00345                 stack.push(new FillSegment(x, y+1));
00346             }
00347             ++pixelsDone;
00348             --pixelIt;
00349             --selIt;
00350             --x;
00351         }
00352 
00353         x = segment->x + 1;
00354         delete segment;
00355 
00356         if (map[m_width * y + x] == Checked)
00357             continue;
00358 
00359         // and go to the right
00360         pixelIt = sourceDevice->createHLineIterator(x, y, m_width, false);
00361         selIt = selection->createHLineIterator(x, y, m_width, true);
00362 
00363         stop = false;
00364         while(!stop && x < m_width && (map[m_width * y + x] != Checked) ) {
00365             diff = devColorSpace->difference(source, pixelIt.rawData());
00366             map[m_width * y + x] = Checked;
00367 
00368             if (diff >= m_threshold
00369                 || (hasSelection && srcSel->selected(pixelIt.x(), pixelIt.y()) == MIN_SELECTED) ) {
00370                 stop = true;
00371                 continue;
00372             }
00373 
00374             if (m_fuzzy)
00375                 colorSpace->fromQColor(Qt::white, MAX_SELECTED - diff, selIt.rawData());
00376             else
00377                 colorSpace->fromQColor(Qt::white, MAX_SELECTED, selIt.rawData());
00378 
00379             if (y > 0 && (map[m_width * (y - 1) + x] == None)) {
00380                 map[m_width * (y - 1) + x] = Added;
00381                 stack.push(new FillSegment(x, y-1));
00382             }
00383             if (y < (m_height - 1) && (map[m_width * (y + 1) + x] == None)) {
00384                 map[m_width * (y + 1) + x] = Added;
00385                 stack.push(new FillSegment(x, y+1));
00386             }
00387             ++pixelsDone;
00388             ++pixelIt;
00389             ++selIt;
00390             ++x;
00391         }
00392 
00393         if (m_size > 0) {
00394             progressPercent = (pixelsDone * 100) / m_size;
00395             if (progressPercent > currentPercent) {
00396                 emit notifyProgress(progressPercent);
00397                 currentPercent = progressPercent;
00398             }
00399         }
00400     }
00401 
00402 
00403     delete[] map;
00404     delete[] source;
00405 
00406     return selection;
00407 }
KDE Home | KDE Accessibility Home | Description of Access Keys