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