chalk

kis_fill_painter.cpp
1/*
2 * Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
3 * Copyright (c) 2004 Bart Coppens <kde@bartcoppens.be>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20#include <stdlib.h>
21#include <string.h>
22#include <cfloat>
23#include <stack>
24
25#include "tqbrush.h"
26#include "tqfontinfo.h"
27#include "tqfontmetrics.h"
28#include "tqpen.h"
29#include "tqregion.h"
30#include "tqwmatrix.h"
31#include <tqimage.h>
32#include <tqmap.h>
33#include <tqpainter.h>
34#include <tqpixmap.h>
35#include <tqpointarray.h>
36#include <tqrect.h>
37#include <tqstring.h>
38
39#include <kdebug.h>
40#include <kcommand.h>
41#include <tdelocale.h>
42
43#include "kis_brush.h"
44#include "kis_debug_areas.h"
45#include "kis_image.h"
46#include "kis_layer.h"
47#include "kis_paint_device.h"
48#include "kis_painter.h"
49#include "kis_pattern.h"
50#include "kis_rect.h"
51#include "kis_colorspace.h"
52#include "kis_transaction.h"
53#include "kis_types.h"
54#include "kis_vec.h"
55#include "kis_selection.h"
56#include "kis_fill_painter.h"
57#include "kis_iterators_pixel.h"
58#include "kis_iterator.h"
59#include "kis_color.h"
60#include "kis_selection.h"
61
62namespace {
63}
64
66 : super()
67{
68 m_width = m_height = -1;
69 m_sampleMerged = false;
70 m_careForSelection = false;
71 m_fuzzy = false;
72}
73
75{
76 m_width = m_height = -1;
77 m_sampleMerged = false;
78 m_careForSelection = false;
79 m_fuzzy = false;
80}
81
82// 'regular' filling
83// XXX: This also needs renaming, since filling ought to keep the opacity and the composite op in mind,
84// this is more eraseToColor.
85void KisFillPainter::fillRect(TQ_INT32 x1, TQ_INT32 y1, TQ_INT32 w, TQ_INT32 h, const KisColor& kc, TQ_UINT8 opacity)
86{
87 if (w > 0 && h > 0) {
88 // Make sure we're in the right colorspace
89
90 KisColor kc2(kc); // get rid of const
91 kc2.convertTo(m_device->colorSpace());
92 TQ_UINT8 * data = kc2.data();
93 m_device->colorSpace()->setAlpha(data, opacity, 1);
94
95 m_device->fill(x1, y1, w, h, data);
96
97 addDirtyRect(TQRect(x1, y1, w, h));
98 }
99}
100
101void KisFillPainter::fillRect(TQ_INT32 x1, TQ_INT32 y1, TQ_INT32 w, TQ_INT32 h, KisPattern * pattern) {
102 if (!pattern) return;
103 if (!pattern->valid()) return;
104 if (!m_device) return;
105
106
107 KisPaintDeviceSP patternLayer = pattern->image(m_device->colorSpace());
108
109 int sx, sy, sw, sh;
110
111 int y = y1;
112
113 if (y >= 0) {
114 sy = y % pattern->height();
115 } else {
116 sy = pattern->height() - (((-y - 1) % pattern->height()) + 1);
117 }
118
119 while (y < y1 + h) {
120 sh = TQMIN((y1 + h) - y, pattern->height() - sy);
121
122 int x = x1;
123
124 if (x >= 0) {
125 sx = x % pattern->width();
126 } else {
127 sx = pattern->width() - (((-x - 1) % pattern->width()) + 1);
128 }
129
130 while (x < x1 + w) {
131 sw = TQMIN((x1 + w) - x, pattern->width() - sx);
132
133 bitBlt(x, y, m_compositeOp, patternLayer.data(), m_opacity, sx, sy, sw, sh);
134 x += sw; sx = 0;
135 }
136
137 y+=sh; sy = 0;
138 }
139
140 addDirtyRect(TQRect(x1, y1, w, h));
141}
142
143// flood filling
144
145void KisFillPainter::fillColor(int startX, int startY) {
146 genericFillStart(startX, startY);
147
148 // Now create a layer and fill it
149 KisPaintDeviceSP filled = new KisPaintDevice(m_device->colorSpace(), "filled");
150 TQ_CHECK_PTR(filled);
151 KisFillPainter painter(filled.data());
152 painter.fillRect(0, 0, m_width, m_height, m_paintColor);
153 painter.end();
154
155 genericFillEnd(filled);
156}
157
158void KisFillPainter::fillPattern(int startX, int startY) {
159 genericFillStart(startX, startY);
160
161 // Now create a layer and fill it
162 KisPaintDeviceSP filled = new KisPaintDevice(m_device->colorSpace(), "filled");
163 TQ_CHECK_PTR(filled);
164 KisFillPainter painter(filled.data());
165 painter.fillRect(0, 0, m_width, m_height, m_pattern);
166 painter.end();
167
168 genericFillEnd(filled);
169}
170
171void KisFillPainter::genericFillStart(int startX, int startY) {
172 m_cancelRequested = false;
173
174 if (m_width < 0 || m_height < 0) {
175 if (m_device->image()) {
176 m_width = m_device->image()->width();
177 m_height = m_device->image()->height();
178 } else {
179 m_width = m_height = 500;
180 }
181 }
182
183 m_size = m_width * m_height;
184
185 // Create a selection from the surrounding area
186 m_selection = createFloodSelection(startX, startY);
187}
188
189void KisFillPainter::genericFillEnd(KisPaintDeviceSP filled) {
190 if (m_cancelRequested) {
191 m_width = m_height = -1;
192 return;
193 }
194
195 TQRect rc = m_selection->selectedRect();
196
197 bltSelection(rc.x(), rc.y(), m_compositeOp, filled, m_selection, m_opacity,
198 rc.x(), rc.y(), rc.width(), rc.height());
199
200 emit notifyProgressDone();
201
202 m_width = m_height = -1;
203}
204
205struct FillSegment {
206 FillSegment(int x, int y/*, FillSegment* parent*/) : x(x), y(y)/*, parent(parent)*/ {}
207 int x;
208 int y;
209// FillSegment* parent;
210};
211
212typedef enum { None = 0, Added = 1, Checked = 2 } Status;
213
214KisSelectionSP KisFillPainter::createFloodSelection(int startX, int startY) {
215 if (m_width < 0 || m_height < 0) {
216 if (m_device->hasSelection() && m_careForSelection) {
217
218 TQRect rc = m_device->selection()->selectedRect();
219 m_width = rc.width() - (startX - rc.x());
220 m_height = rc.height() - (startY - rc.y());
221
222 } else if (m_device->image()) {
223
224 m_width = m_device->image()->width();
225 m_height = m_device->image()->height();
226
227 } else {
228 m_width = m_height = 500;
229 }
230 }
231
232 // Don't try to fill if we start outside the borders, just return an empty 'fill'
233 if (startX < 0 || startY < 0 || startX >= m_width || startY >= m_height)
234 return new KisSelection(m_device);
235
236 KisPaintDeviceSP sourceDevice = 0;
237
238 // sample merged?
239 if (m_sampleMerged) {
240 if (!m_device->image()) {
241 return new KisSelection(m_device);
242 }
243 sourceDevice = m_device->image()->mergedImage();
244 } else {
245 sourceDevice = m_device;
246 }
247
248 m_size = m_width * m_height;
249
250 KisSelectionSP selection = new KisSelection(m_device);
251 KisColorSpace * colorSpace = selection->colorSpace();
252 KisColorSpace * devColorSpace = sourceDevice->colorSpace();
253
254 TQ_UINT8* source = new TQ_UINT8[sourceDevice->pixelSize()];
255 KisHLineIteratorPixel pixelIt = sourceDevice->createHLineIterator(startX, startY, startX+1, false);
256
257 memcpy(source, pixelIt.rawData(), sourceDevice->pixelSize());
258
259 std::stack<FillSegment*> stack;
260
261 stack.push(new FillSegment(startX, startY/*, 0*/));
262
263 Status* map = new Status[m_size];
264
265 memset(map, None, m_size * sizeof(Status));
266
267 int progressPercent = 0; int pixelsDone = 0; int currentPercent = 0;
268 emit notifyProgressStage(i18n("Making fill outline..."), 0);
269
270 bool hasSelection = m_careForSelection && sourceDevice->hasSelection();
271 KisSelectionSP srcSel = 0;
272 if (hasSelection)
273 srcSel = sourceDevice->selection();
274
275 while(!stack.empty()) {
276 FillSegment* segment = stack.top();
277 stack.pop();
278 if (map[m_width * segment->y + segment->x] == Checked) {
279 delete segment;
280 continue;
281 }
282 map[m_width * segment->y + segment->x] = Checked;
283
284 int x = segment->x;
285 int y = segment->y;
286
287 /* We need an iterator that is valid in the range (0,y) - (width,y). Therefore,
288 it is needed to start the iterator at the first position, and then skip to (x,y). */
289 pixelIt = sourceDevice->createHLineIterator(0, y, m_width, false);
290 pixelIt += x;
291 TQ_UINT8 diff = devColorSpace->difference(source, pixelIt.rawData());
292
293 if (diff >= m_threshold
294 || (hasSelection && srcSel->selected(pixelIt.x(), pixelIt.y()) == MIN_SELECTED)) {
295 delete segment;
296 continue;
297 }
298
299 // Here as well: start the iterator at (0,y)
300 KisHLineIteratorPixel selIt = selection->createHLineIterator(0, y, m_width, true);
301 selIt += x;
302 if (m_fuzzy)
303 colorSpace->fromTQColor(TQt::white, MAX_SELECTED - diff, selIt.rawData());
304 else
305 colorSpace->fromTQColor(TQt::white, MAX_SELECTED, selIt.rawData());
306
307 if (y > 0 && (map[m_width * (y - 1) + x] == None)) {
308 map[m_width * (y - 1) + x] = Added;
309 stack.push(new FillSegment(x, y-1));
310 }
311 if (y < (m_height - 1) && (map[m_width * (y + 1) + x] == None)) {
312 map[m_width * (y + 1) + x] = Added;
313 stack.push(new FillSegment(x, y+1));
314 }
315
316 ++pixelsDone;
317
318 bool stop = false;
319
320 --pixelIt;
321 --selIt;
322 --x;
323
324 // go to the left
325 while(!stop && x >= 0 && (map[m_width * y + x] != Checked) ) { // FIXME optimizeable?
326 map[m_width * y + x] = Checked;
327 diff = devColorSpace->difference(source, pixelIt.rawData());
328 if (diff >= m_threshold
329 || (hasSelection && srcSel->selected(pixelIt.x(), pixelIt.y()) == MIN_SELECTED)) {
330 stop = true;
331 continue;
332 }
333
334 if (m_fuzzy)
335 colorSpace->fromTQColor(TQt::white, MAX_SELECTED - diff, selIt.rawData());
336 else
337 colorSpace->fromTQColor(TQt::white, MAX_SELECTED, selIt.rawData());
338
339 if (y > 0 && (map[m_width * (y - 1) + x] == None)) {
340 map[m_width * (y - 1) + x] = Added;
341 stack.push(new FillSegment(x, y-1));
342 }
343 if (y < (m_height - 1) && (map[m_width * (y + 1) + x] == None)) {
344 map[m_width * (y + 1) + x] = Added;
345 stack.push(new FillSegment(x, y+1));
346 }
347 ++pixelsDone;
348 --pixelIt;
349 --selIt;
350 --x;
351 }
352
353 x = segment->x + 1;
354 delete segment;
355
356 if (map[m_width * y + x] == Checked)
357 continue;
358
359 // and go to the right
360 pixelIt = sourceDevice->createHLineIterator(x, y, m_width, false);
361 selIt = selection->createHLineIterator(x, y, m_width, true);
362
363 stop = false;
364 while(!stop && x < m_width && (map[m_width * y + x] != Checked) ) {
365 diff = devColorSpace->difference(source, pixelIt.rawData());
366 map[m_width * y + x] = Checked;
367
368 if (diff >= m_threshold
369 || (hasSelection && srcSel->selected(pixelIt.x(), pixelIt.y()) == MIN_SELECTED) ) {
370 stop = true;
371 continue;
372 }
373
374 if (m_fuzzy)
375 colorSpace->fromTQColor(TQt::white, MAX_SELECTED - diff, selIt.rawData());
376 else
377 colorSpace->fromTQColor(TQt::white, MAX_SELECTED, selIt.rawData());
378
379 if (y > 0 && (map[m_width * (y - 1) + x] == None)) {
380 map[m_width * (y - 1) + x] = Added;
381 stack.push(new FillSegment(x, y-1));
382 }
383 if (y < (m_height - 1) && (map[m_width * (y + 1) + x] == None)) {
384 map[m_width * (y + 1) + x] = Added;
385 stack.push(new FillSegment(x, y+1));
386 }
387 ++pixelsDone;
388 ++pixelIt;
389 ++selIt;
390 ++x;
391 }
392
393 if (m_size > 0) {
394 progressPercent = (pixelsDone * 100) / m_size;
395 if (progressPercent > currentPercent) {
396 emit notifyProgress(progressPercent);
397 currentPercent = progressPercent;
398 }
399 }
400 }
401
402
403 delete[] map;
404 delete[] source;
405
406 return selection;
407}
A colorspace is the definition of a certain color model in Chalk.
virtual void fromTQColor(const TQColor &c, TQ_UINT8 *dst, KisProfile *profile=0)=0
The fromTQColor methods take a given color defined as an RGB TQColor and fills a byte array with the ...
virtual TQ_UINT8 difference(const TQ_UINT8 *src1, const TQ_UINT8 *src2)=0
Get the difference between 2 colors, normalized in the range (0,255).
A KisColor describes a color in a certain colorspace.
Definition kis_color.h:33
void convertTo(KisColorSpace *cs)
Convert this KisColor to the specified colorspace.
TQ_UINT8 * data() const
For easy memcpy'ing etc.
Definition kis_color.h:62
void fillPattern(int startX, int startY)
Fills the enclosed area around the point with the set pattern.
KisFillPainter()
Construct an empty painter.
KisSelectionSP createFloodSelection(int startX, int startY)
Returns a selection mask for the floodfill starting at the specified position.
void fillRect(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, const KisColor &c)
Fill a rectangle with a certain color.
The pixel iterators are high level iterarators.
A paint device contains the actual pixel data and offers methods to read and write pixels.
TQRect addDirtyRect(TQRect r)
Add the r to the current dirty rect, and return the dirtyRect after adding r to it.
KisPattern * pattern() const
Returns the currently set pattern.
KCommand * end()
Finish painting on the current device.
KisPaintDeviceSP device() const
Returns the current paint device.
Definition kis_painter.h:95
TQ_UINT8 opacity() const
Returns the opacity that is used in painting.
void bitBlt(TQ_INT32 dx, TQ_INT32 dy, const KisCompositeOp &op, KisPaintDeviceSP src, TQ_INT32 sx, TQ_INT32 sy, TQ_INT32 sw, TQ_INT32 sh)
Blast the specified region from src onto the current paint device.
KisColor fillColor() const
Returns the current fill color.
void bltSelection(TQ_INT32 dx, TQ_INT32 dy, const KisCompositeOp &op, KisPaintDeviceSP src, KisSelectionSP selMask, TQ_UINT8 opacity, TQ_INT32 sx, TQ_INT32 sy, TQ_INT32 sw, TQ_INT32 sh)
A version of bitBlt that renders using an external selection mask, ignoring the src device's own sele...
KisSelection contains a byte-map representation of a layer, where the value of a byte signifies wheth...