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