3 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #define DEBUG_KP_FLOOD_FILL 0
32 #include <kpFloodFill.h>
34 #include <QApplication>
36 #include <QLinkedList>
45 #include <kpPixmapFX.h>
52 kpFillLine (int y
= -1, int x1
= -1, int x2
= -1)
53 : m_y (y
), m_x1 (x1
), m_x2 (x2
)
57 static kpCommandSize::SizeType
size ()
59 return sizeof (kpFillLine
);
65 static kpCommandSize::SizeType
FillLinesListSize (const QLinkedList
<kpFillLine
> &fillLines
)
67 return (fillLines
.size () * kpFillLine::size ());
71 struct kpFloodFillPrivate
74 // Copy of whatever was passed to the constructor.
80 int processedColorSimilarity
;
87 kpColor colorToChange
;
96 QLinkedList
<kpFillLine
> fillLines
;
97 QList
< QLinkedList
<kpFillLine
> > fillLinesCache
;
104 kpFloodFill::kpFloodFill (kpImage
*image
, int x
, int y
,
105 const kpColor
&color
, int processedColorSimilarity
)
106 : d (new kpFloodFillPrivate ())
110 d
->color
= color
, d
->processedColorSimilarity
= processedColorSimilarity
;
115 kpFloodFill::~kpFloodFill ()
122 kpColor
kpFloodFill::color () const
128 int kpFloodFill::processedColorSimilarity () const
130 return d
->processedColorSimilarity
;
135 kpCommandSize::SizeType
kpFloodFill::size () const
137 kpCommandSize::SizeType fillLinesCacheSize
= 0;
138 foreach (const QLinkedList
<kpFillLine
> &linesList
, d
->fillLinesCache
)
140 fillLinesCacheSize
+= ::FillLinesListSize (linesList
);
143 return ::FillLinesListSize (d
->fillLines
) +
144 kpCommandSize::QImageSize (d
->readableImage
) +
150 void kpFloodFill::prepareColorToChange ()
152 if (d
->colorToChange
.isValid ())
155 #if DEBUG_KP_FLOOD_FILL && 1
156 kDebug () << "kpFloodFill::prepareColorToChange()";
159 d
->colorToChange
= kpPixmapFX::getColorAtPixel (*d
->imagePtr
, QPoint (d
->x
, d
->y
));
161 if (d
->colorToChange
.isOpaque ())
163 #if DEBUG_KP_FLOOD_FILL && 1
164 kDebug () << "\tcolorToChange: " << (int *) d
->colorToChange
.toQRgb ()
170 #if DEBUG_KP_FLOOD_FILL && 1
171 kDebug () << "\tcolorToChange: transparent";
177 kpColor
kpFloodFill::colorToChange ()
179 prepareColorToChange ();
181 return d
->colorToChange
;
185 // Derived from the zSprite2 Graphics Engine
189 kpColor
kpFloodFill::pixelColor (int x
, int y
, bool *beenHere
) const
194 Q_ASSERT (y
>= 0 && y
< (int) d
->fillLinesCache
.count ());
196 foreach (const kpFillLine
&line
, d
->fillLinesCache
[y
])
198 if (x
>= line
.m_x1
&& x
<= line
.m_x2
)
206 return kpPixmapFX::getColorAtPixel (d
->readableImage
, QPoint (x
, y
));
210 bool kpFloodFill::shouldGoTo (int x
, int y
) const
213 const kpColor col
= pixelColor (x
, y
, &beenThere
);
215 return (!beenThere
&& col
.isSimilarTo (d
->colorToChange
, d
->processedColorSimilarity
));
220 int kpFloodFill::findMinX (int y
, int x
) const
227 if (shouldGoTo (x
, y
))
235 int kpFloodFill::findMaxX (int y
, int x
) const
239 if (x
> d
->imagePtr
->width () - 1)
240 return d
->imagePtr
->width () - 1;
242 if (shouldGoTo (x
, y
))
251 void kpFloodFill::addLine (int y
, int x1
, int x2
)
253 #if DEBUG_KP_FLOOD_FILL && 0
254 kDebug () << "kpFillCommand::fillAddLine ("
255 << y
<< "," << x1
<< "," << x2
<< ")" << endl
;
258 d
->fillLines
.append (kpFillLine (y
, x1
, x2
));
259 d
->fillLinesCache
[y
].append (
260 kpFillLine (y
/*OPT: can determine from array index*/, x1
, x2
));
261 d
->boundingRect
= d
->boundingRect
.unite (QRect (QPoint (x1
, y
), QPoint (x2
, y
)));
265 void kpFloodFill::findAndAddLines (const kpFillLine
&fillLine
, int dy
)
268 if (fillLine
.m_y
+ dy
< 0 || fillLine
.m_y
+ dy
>= d
->imagePtr
->height ())
271 for (int xnow
= fillLine
.m_x1
; xnow
<= fillLine
.m_x2
; xnow
++)
273 // At current position, right colour?
274 if (shouldGoTo (xnow
, fillLine
.m_y
+ dy
))
276 // Find minimum and maximum x values
277 int minxnow
= findMinX (fillLine
.m_y
+ dy
, xnow
);
278 int maxxnow
= findMaxX (fillLine
.m_y
+ dy
, xnow
);
281 addLine (fillLine
.m_y
+ dy
, minxnow
, maxxnow
);
291 void kpFloodFill::prepare ()
296 #if DEBUG_KP_FLOOD_FILL && 1
297 kDebug () << "kpFloodFill::prepare()";
300 prepareColorToChange ();
302 d
->boundingRect
= QRect ();
305 #if DEBUG_KP_FLOOD_FILL && 1
306 kDebug () << "\tperforming NOP check";
309 // get the color we need to replace
310 if (d
->processedColorSimilarity
== 0 && d
->color
== d
->colorToChange
)
312 // need to do absolutely nothing (this is a significant optimization
313 // for people who randomly click a lot over already-filled areas)
314 d
->prepared
= true; // sync with all "return true"'s
318 #if DEBUG_KP_FLOOD_FILL && 1
319 kDebug () << "\tconverting to image";
322 // The only way to read pixels. Sigh.
323 d
->readableImage
= kpPixmapFX::convertToQImage (*d
->imagePtr
);
325 #if DEBUG_KP_FLOOD_FILL && 1
326 kDebug () << "\tcreating fillLinesCache";
330 for (int i
= 0; i
< d
->imagePtr
->height (); i
++)
331 d
->fillLinesCache
.append (QLinkedList
<kpFillLine
> ());
333 #if DEBUG_KP_FLOOD_FILL && 1
334 kDebug () << "\tcreating fill lines";
338 addLine (d
->y
, findMinX (d
->y
, d
->x
), findMaxX (d
->y
, d
->x
));
340 for (QLinkedList
<kpFillLine
>::ConstIterator it
= d
->fillLines
.begin ();
341 it
!= d
->fillLines
.end ();
344 #if DEBUG_KP_FLOOD_FILL && 0
345 kDebug () << "Expanding from y=" << (*it
).m_y
346 << " x1=" << (*it
).m_x1
347 << " x2=" << (*it
).m_x2
352 // Make more lines above and below current line.
354 // WARNING: Adds to end of "fillLines" (the linked list we are iterating
355 // through). Therefore, "fillLines" must remain a linked list
356 // - you cannot change it into a vector. Also, do not use
357 // "foreach" for this loop as that makes a copy of the linked
358 // list at the start and won't see new lines.
360 findAndAddLines (*it
, -1);
361 findAndAddLines (*it
, +1);
364 #if DEBUG_KP_FLOOD_FILL && 1
365 kDebug () << "\tfinalising memory usage";
368 // finalize memory usage
369 d
->readableImage
= QImage ();
370 d
->fillLinesCache
.clear ();
372 d
->prepared
= true; // sync with all "return true"'s
376 QRect
kpFloodFill::boundingRect ()
380 return d
->boundingRect
;
384 struct DrawLinesPackage
386 const QLinkedList
<kpFillLine
> *lines
;
390 static void DrawLinesHelper (QPainter
*p
,
391 bool drawingOnRGBLayer
,
394 const DrawLinesPackage
*pack
= static_cast <DrawLinesPackage
*> (data
);
396 #if DEBUG_KP_FLOOD_FILL
397 kDebug () << "DrawLinesHelper() lines"
398 << " color=" << (int *) pack
->color
.toQRgb ()
402 p
->setPen (kpPixmapFX::draw_ToQColor (pack
->color
, drawingOnRGBLayer
));
404 foreach (const kpFillLine
&l
, *pack
->lines
)
406 const QPoint
p1 (l
.m_x1
, l
.m_y
);
407 const QPoint
p2 (l
.m_x2
, l
.m_y
);
409 p
->drawLine (p1
, p2
);
413 static void DrawLines (kpImage
*image
,
414 const QLinkedList
<kpFillLine
> &lines
,
415 const kpColor
&color
)
417 DrawLinesPackage pack
;
421 kpPixmapFX::draw (image
, &::DrawLinesHelper
,
422 color
.isOpaque (), color
.isTransparent (),
428 void kpFloodFill::fill ()
433 QApplication::setOverrideCursor (Qt::WaitCursor
);
435 ::DrawLines (d
->imagePtr
, d
->fillLines
, d
->color
);
437 QApplication::restoreOverrideCursor ();