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