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 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) : x(x), y(y) {}
00205 int x;
00206 int y;
00207
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
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
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));
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
00283
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
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
00320 while(!stop && x >= 0 && (map[m_width * y + x] != Checked) ) {
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
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 }