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