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