00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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
00083
00084
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
00089
00090 KisColor kc2(kc);
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
00144
00145 void KisFillPainter::fillColor(int startX, int startY) {
00146 genericFillStart(startX, startY);
00147
00148
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
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
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) : x(x), y(y) {}
00207 int x;
00208 int y;
00209
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
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
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));
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
00288
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
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
00325 while(!stop && x >= 0 && (map[m_width * y + x] != Checked) ) {
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
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 }