]> git.saurik.com Git - wxWidgets.git/blame - src/common/imagfill.cpp
Make brush hatches in wxGTK consistent with wxMSW.
[wxWidgets.git] / src / common / imagfill.cpp
CommitLineData
7011cf13 1/////////////////////////////////////////////////////////////////////////////
8898456d 2// Name: src/common/imagfill.cpp
7011cf13 3// Purpose: FloodFill for wxImage
99d80019 4// Author: Julian Smart
7011cf13 5// RCS-ID: $Id$
99d80019 6// Copyright: (c) Julian Smart
65571936 7// Licence: wxWindows licence
7011cf13
JS
8/////////////////////////////////////////////////////////////////////////////
9
7011cf13
JS
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
3a0a5ada
VS
18#if wxUSE_IMAGE && !defined(__WXMSW__)
19// we have no use for this code in wxMSW...
7011cf13 20
0fc5dbf5
VZ
21#ifndef WX_PRECOMP
22 #include "wx/brush.h"
3a0a5ada
VS
23 #include "wx/dc.h"
24 #include "wx/dcmemory.h"
155ecd4c 25 #include "wx/image.h"
0fc5dbf5 26#endif
7011cf13
JS
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
f5f05abf 33static bool LINKAGEMODE MatchPixel(wxImage *img, int x, int y, int w, int h, const wxColour& c)
7011cf13 34{
7beb59f3 35 if ((x<0)||(x>=w)||(y<0)||(y>=h)) return false;
7011cf13 36
3a0a5ada
VS
37 unsigned char r = img->GetRed(x,y);
38 unsigned char g = img->GetGreen(x,y);
39 unsigned char b = img->GetBlue(x,y);
7011cf13
JS
40 return c.Red() == r && c.Green() == g && c.Blue() == b ;
41}
42
f5f05abf 43static bool LINKAGEMODE MatchBoundaryPixel(wxImage *img, int x, int y, int w, int h, const wxColour & fill, const wxColour& bound)
7011cf13 44{
7beb59f3 45 if ((x<0)||(x>=w)||(y<0)||(y>=h)) return true;
7011cf13 46
3a0a5ada
VS
47 unsigned char r = img->GetRed(x,y);
48 unsigned char g = img->GetGreen(x,y);
49 unsigned char b = img->GetBlue(x,y);
f5f05abf 50 if ( fill.Red() == r && fill.Green() == g && fill.Blue() == b )
7beb59f3 51 return true;
f5f05abf 52 if ( bound.Red() == r && bound.Green() == g && bound.Blue() == b )
7beb59f3
WS
53 return true;
54 return false;
7011cf13
JS
55}
56
57
f5f05abf
DW
58static void LINKAGEMODE
59wxImageFloodFill(wxImage *image,
60 wxCoord x, wxCoord y, const wxBrush & fillBrush,
61 const wxColour& testColour, int style,
7a893a31 62 int WXUNUSED(LogicalFunction))
7011cf13
JS
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
068993c7
JS
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 */
7011cf13 71
3a0a5ada
VS
72 int width = image->GetWidth();
73 int height = image->GetHeight();
7011cf13
JS
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
3a0a5ada
VS
86 if ( image->GetRed(x,y) != r
87 || image->GetGreen(x,y) != g
88 || image->GetBlue (x,y) != b )
7011cf13
JS
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
3a0a5ada 111 image->SetRGB(xt,yt,r,g,b);
7011cf13
JS
112
113 //Main queue loop
114 while(qr!=qs)
115 {
116 //Add new members to queue
117 //Above current pixel
3a0a5ada 118 if(MatchPixel(image,xt,yt-1,width,height,testColour))
7011cf13
JS
119 {
120 *qs=xt;
121 qs++;
122 *qs=yt-1;
123 qs++;
3a0a5ada 124 image->SetRGB(xt,yt-1,r,g,b);
7011cf13
JS
125
126 //Loop back to beginning of queue
127 if(qs>=(qst+qSz)) qs=qst;
128 }
129
130 //Below current pixel
3a0a5ada 131 if(MatchPixel(image,xt,yt+1,width,height,testColour))
7011cf13
JS
132 {
133 *qs=xt;
134 qs++;
135 *qs=yt+1;
136 qs++;
3a0a5ada 137 image->SetRGB(xt,yt+1,r,g,b);
7011cf13
JS
138 if(qs>=(qst+qSz)) qs=qst;
139 }
140
141 //Left of current pixel
3a0a5ada 142 if(MatchPixel(image,xt-1,yt,width,height,testColour))
7011cf13
JS
143 {
144 *qs=xt-1;
145 qs++;
146 *qs=yt;
147 qs++;
3a0a5ada 148 image->SetRGB(xt-1,yt,r,g,b);
7011cf13
JS
149 if(qs>=(qst+qSz)) qs=qst;
150 }
151
152 //Right of current pixel
3a0a5ada 153 if(MatchPixel(image,xt+1,yt,width,height,testColour))
7011cf13
JS
154 {
155 *qs=xt+1;
156 qs++;
157 *qs=yt;
158 qs++;
3a0a5ada 159 image->SetRGB(xt+1,yt,r,g,b);
7011cf13
JS
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
3a0a5ada 174 delete[] qst;
7011cf13
JS
175 }
176 }
177 else
178 {
179 //style is wxFLOOD_BORDER
180 // fill up to testColor border - if already testColour don't do anything
3a0a5ada
VS
181 if ( image->GetRed(x,y) != testColour.Red()
182 || image->GetGreen(x,y) != testColour.Green()
183 || image->GetBlue(x,y) != testColour.Blue() )
184 {
7011cf13
JS
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
3a0a5ada 206 image->SetRGB(xt,yt,r,g,b);
7011cf13
JS
207
208 //Main queue loop
3a0a5ada 209 while (qr!=qs)
7011cf13
JS
210 {
211 //Add new members to queue
212 //Above current pixel
3a0a5ada 213 if(!MatchBoundaryPixel(image,xt,yt-1,width,height,fillColour,testColour))
7011cf13
JS
214 {
215 *qs=xt;
216 qs++;
217 *qs=yt-1;
218 qs++;
3a0a5ada 219 image->SetRGB(xt,yt-1,r,g,b);
7011cf13
JS
220
221 //Loop back to beginning of queue
222 if(qs>=(qst+qSz)) qs=qst;
223 }
224
225 //Below current pixel
3a0a5ada 226 if(!MatchBoundaryPixel(image,xt,yt+1,width,height,fillColour,testColour))
7011cf13
JS
227 {
228 *qs=xt;
229 qs++;
230 *qs=yt+1;
231 qs++;
3a0a5ada 232 image->SetRGB(xt,yt+1,r,g,b);
7011cf13
JS
233 if(qs>=(qst+qSz)) qs=qst;
234 }
235
236 //Left of current pixel
3a0a5ada 237 if(!MatchBoundaryPixel(image,xt-1,yt,width,height,fillColour,testColour))
7011cf13
JS
238 {
239 *qs=xt-1;
240 qs++;
241 *qs=yt;
242 qs++;
3a0a5ada 243 image->SetRGB(xt-1,yt,r,g,b);
7011cf13
JS
244 if(qs>=(qst+qSz)) qs=qst;
245 }
246
247 //Right of current pixel
3a0a5ada 248 if(!MatchBoundaryPixel(image,xt+1,yt,width,height,fillColour,testColour))
7011cf13
JS
249 {
250 *qs=xt+1;
251 qs++;
252 *qs=yt;
253 qs++;
3a0a5ada 254 image->SetRGB(xt+1,yt,r,g,b);
7011cf13
JS
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
3a0a5ada 269 delete[] qst;
7011cf13
JS
270 }
271 }
272 //all done,
273}
274
3a0a5ada 275
f842e673 276bool wxDoFloodFill(wxDC *dc, wxCoord x, wxCoord y,
89efaf2b 277 const wxColour& col, wxFloodFillStyle style)
f842e673 278{
e6777e65 279 if (dc->GetBrush().IsTransparent())
f842e673 280 return true;
7011cf13 281
f842e673
KH
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
173d67aa
PC
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
f842e673 297 wxBitmap bitmap(width, height);
173d67aa
PC
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);
f842e673
KH
313 memdc.SelectObject(wxNullBitmap);
314
315 wxImage image = bitmap.ConvertToImage();
173d67aa 316 wxImageFloodFill(&image, x_dev, y_dev, dc->GetBrush(), col, style,
f842e673
KH
317 dc->GetLogicalFunction());
318 bitmap = wxBitmap(image);
319 memdc.SelectObject(bitmap);
173d67aa 320 dc->Blit(x0_log, y0_log, w_log, h_log, &memdc, 0, 0);
f842e673
KH
321
322 return true;
323}
324
325#endif // wxUSE_IMAGE