File/dir dialog styles and other changes (patch 1488371):
[wxWidgets.git] / src / common / imagfill.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/imagfill.cpp
3 // Purpose: FloodFill for wxImage
4 // Author: Julian Smart
5 // RCS-ID: $Id$
6 // Copyright: (c) Julian Smart
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 #if wxUSE_IMAGE && !defined(__WXMSW__)
19 // we have no use for this code in wxMSW...
20
21 #ifndef WX_PRECOMP
22 #include "wx/brush.h"
23 #include "wx/dc.h"
24 #include "wx/dcmemory.h"
25 #endif
26
27 #include "wx/image.h"
28
29 // DoFloodFill
30 // Fills with the colour extracted from fillBrush, starting at x,y until either
31 // a color different from the start pixel is reached (wxFLOOD_SURFACE)
32 // or fill color is reached (wxFLOOD_BORDER)
33
34 static bool LINKAGEMODE MatchPixel(wxImage *img, int x, int y, int w, int h, const wxColour& c)
35 {
36 if ((x<0)||(x>=w)||(y<0)||(y>=h)) return false;
37
38 unsigned char r = img->GetRed(x,y);
39 unsigned char g = img->GetGreen(x,y);
40 unsigned char b = img->GetBlue(x,y);
41 return c.Red() == r && c.Green() == g && c.Blue() == b ;
42 }
43
44 static bool LINKAGEMODE MatchBoundaryPixel(wxImage *img, int x, int y, int w, int h, const wxColour & fill, const wxColour& bound)
45 {
46 if ((x<0)||(x>=w)||(y<0)||(y>=h)) return true;
47
48 unsigned char r = img->GetRed(x,y);
49 unsigned char g = img->GetGreen(x,y);
50 unsigned char b = img->GetBlue(x,y);
51 if ( fill.Red() == r && fill.Green() == g && fill.Blue() == b )
52 return true;
53 if ( bound.Red() == r && bound.Green() == g && bound.Blue() == b )
54 return true;
55 return false;
56 }
57
58
59 static void LINKAGEMODE
60 wxImageFloodFill(wxImage *image,
61 wxCoord x, wxCoord y, const wxBrush & fillBrush,
62 const wxColour& testColour, int style,
63 int WXUNUSED(LogicalFunction))
64 {
65 /* A diamond flood-fill using a circular queue system.
66 Each pixel surrounding the current pixel is added to
67 the queue if it meets the criteria, then is retrieved in
68 its turn. Code originally based on http://www.drawit.co.nz/Developers.htm,
69 with explicit permission to use this for wxWidgets granted by Andrew Empson
70 (no copyright claimed)
71 */
72
73 int width = image->GetWidth();
74 int height = image->GetHeight();
75
76 //Draw using a pen made from the current brush colour
77 //Potentially allows us to use patterned flood fills in future code
78 wxColour fillColour = fillBrush.GetColour();
79 unsigned char r = fillColour.Red();
80 unsigned char g = fillColour.Green();
81 unsigned char b = fillColour.Blue();
82
83 //initial test :
84 if (style == wxFLOOD_SURFACE)
85 {
86 //if wxFLOOD_SURFACE, if fill colour is same as required, we don't do anything
87 if ( image->GetRed(x,y) != r
88 || image->GetGreen(x,y) != g
89 || image->GetBlue (x,y) != b )
90 {
91 //prepare memory for queue
92 //queue save, start, read
93 size_t *qs, *qst, *qr;
94
95 //queue size (physical)
96 long qSz= height * width * 2;
97 qst = new size_t [qSz];
98
99 //temporary x and y locations
100 int xt, yt;
101
102 for (int i=0; i < qSz; i++)
103 qst[i] = 0;
104
105 // start queue
106 qs=qr=qst;
107 *qs=xt=x;
108 qs++;
109 *qs=yt=y;
110 qs++;
111
112 image->SetRGB(xt,yt,r,g,b);
113
114 //Main queue loop
115 while(qr!=qs)
116 {
117 //Add new members to queue
118 //Above current pixel
119 if(MatchPixel(image,xt,yt-1,width,height,testColour))
120 {
121 *qs=xt;
122 qs++;
123 *qs=yt-1;
124 qs++;
125 image->SetRGB(xt,yt-1,r,g,b);
126
127 //Loop back to beginning of queue
128 if(qs>=(qst+qSz)) qs=qst;
129 }
130
131 //Below current pixel
132 if(MatchPixel(image,xt,yt+1,width,height,testColour))
133 {
134 *qs=xt;
135 qs++;
136 *qs=yt+1;
137 qs++;
138 image->SetRGB(xt,yt+1,r,g,b);
139 if(qs>=(qst+qSz)) qs=qst;
140 }
141
142 //Left of current pixel
143 if(MatchPixel(image,xt-1,yt,width,height,testColour))
144 {
145 *qs=xt-1;
146 qs++;
147 *qs=yt;
148 qs++;
149 image->SetRGB(xt-1,yt,r,g,b);
150 if(qs>=(qst+qSz)) qs=qst;
151 }
152
153 //Right of current pixel
154 if(MatchPixel(image,xt+1,yt,width,height,testColour))
155 {
156 *qs=xt+1;
157 qs++;
158 *qs=yt;
159 qs++;
160 image->SetRGB(xt+1,yt,r,g,b);
161 if(qs>=(qst+qSz)) qs=qst;
162 }
163
164 //Retrieve current queue member
165 qr+=2;
166
167 //Loop back to the beginning
168 if(qr>=(qst+qSz)) qr=qst;
169 xt=*qr;
170 yt=*(qr+1);
171
172 //Go Back to beginning of loop
173 }
174
175 delete[] qst;
176 }
177 }
178 else
179 {
180 //style is wxFLOOD_BORDER
181 // fill up to testColor border - if already testColour don't do anything
182 if ( image->GetRed(x,y) != testColour.Red()
183 || image->GetGreen(x,y) != testColour.Green()
184 || image->GetBlue(x,y) != testColour.Blue() )
185 {
186 //prepare memory for queue
187 //queue save, start, read
188 size_t *qs, *qst, *qr;
189
190 //queue size (physical)
191 long qSz= height * width * 2;
192 qst = new size_t [qSz];
193
194 //temporary x and y locations
195 int xt, yt;
196
197 for (int i=0; i < qSz; i++)
198 qst[i] = 0;
199
200 // start queue
201 qs=qr=qst;
202 *qs=xt=x;
203 qs++;
204 *qs=yt=y;
205 qs++;
206
207 image->SetRGB(xt,yt,r,g,b);
208
209 //Main queue loop
210 while (qr!=qs)
211 {
212 //Add new members to queue
213 //Above current pixel
214 if(!MatchBoundaryPixel(image,xt,yt-1,width,height,fillColour,testColour))
215 {
216 *qs=xt;
217 qs++;
218 *qs=yt-1;
219 qs++;
220 image->SetRGB(xt,yt-1,r,g,b);
221
222 //Loop back to beginning of queue
223 if(qs>=(qst+qSz)) qs=qst;
224 }
225
226 //Below current pixel
227 if(!MatchBoundaryPixel(image,xt,yt+1,width,height,fillColour,testColour))
228 {
229 *qs=xt;
230 qs++;
231 *qs=yt+1;
232 qs++;
233 image->SetRGB(xt,yt+1,r,g,b);
234 if(qs>=(qst+qSz)) qs=qst;
235 }
236
237 //Left of current pixel
238 if(!MatchBoundaryPixel(image,xt-1,yt,width,height,fillColour,testColour))
239 {
240 *qs=xt-1;
241 qs++;
242 *qs=yt;
243 qs++;
244 image->SetRGB(xt-1,yt,r,g,b);
245 if(qs>=(qst+qSz)) qs=qst;
246 }
247
248 //Right of current pixel
249 if(!MatchBoundaryPixel(image,xt+1,yt,width,height,fillColour,testColour))
250 {
251 *qs=xt+1;
252 qs++;
253 *qs=yt;
254 qs++;
255 image->SetRGB(xt+1,yt,r,g,b);
256 if(qs>=(qst+qSz)) qs=qst;
257 }
258
259 //Retrieve current queue member
260 qr+=2;
261
262 //Loop back to the beginning
263 if(qr>=(qst+qSz)) qr=qst;
264 xt=*qr;
265 yt=*(qr+1);
266
267 //Go Back to beginning of loop
268 }
269
270 delete[] qst;
271 }
272 }
273 //all done,
274 }
275
276
277 bool wxDoFloodFill(wxDC *dc, wxCoord x, wxCoord y,
278 const wxColour& col, int style)
279 {
280 if (dc->GetBrush().GetStyle() == wxTRANSPARENT)
281 return true;
282
283 int height = 0;
284 int width = 0;
285 dc->GetSize(&width, &height);
286
287 //it would be nice to fail if we don't get a sensible size...
288 wxCHECK_MSG(width >= 1 && height >= 1, false,
289 wxT("In FloodFill, dc.GetSize routine failed, method not supported by this DC"));
290
291 //this is much faster than doing the individual pixels
292 wxMemoryDC memdc;
293 wxBitmap bitmap(width, height);
294 memdc.SelectObject(bitmap);
295 memdc.Blit(0, 0, width, height, dc, 0, 0);
296 memdc.SelectObject(wxNullBitmap);
297
298 wxImage image = bitmap.ConvertToImage();
299 wxImageFloodFill(&image, x,y, dc->GetBrush(), col, style,
300 dc->GetLogicalFunction());
301 bitmap = wxBitmap(image);
302 memdc.SelectObject(bitmap);
303 dc->Blit(0, 0, width, height, &memdc, 0, 0);
304 memdc.SelectObject(wxNullBitmap);
305
306 return true;
307 }
308
309 #endif // wxUSE_IMAGE