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 
MatchPixel(wxImage * img,int x,int y,int w,int h,const wxColour & c)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 
MatchBoundaryPixel(wxImage * img,int x,int y,int w,int h,const wxColour & fill,const wxColour & bound)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
wxImageFloodFill(wxImage * image,wxCoord x,wxCoord y,const wxBrush & fillBrush,const wxColour & testColour,int style)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 
wxDoFloodFill(wxDC * dc,wxCoord x,wxCoord y,const wxColour & col,wxFloodFillStyle style)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
323