]> git.saurik.com Git - wxWidgets.git/blame - src/common/imagfill.cpp
fixing overrelease and out-of-bounds write, fixes #13725
[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,
b2596741 61 const wxColour& testColour, int style)
7011cf13
JS
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
068993c7
JS
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 */
7011cf13 70
3a0a5ada
VS
71 int width = image->GetWidth();
72 int height = image->GetHeight();
7011cf13
JS
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
3a0a5ada
VS
85 if ( image->GetRed(x,y) != r
86 || image->GetGreen(x,y) != g
87 || image->GetBlue (x,y) != b )
7011cf13
JS
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
3a0a5ada 110 image->SetRGB(xt,yt,r,g,b);
7011cf13
JS
111
112 //Main queue loop
113 while(qr!=qs)
114 {
115 //Add new members to queue
116 //Above current pixel
3a0a5ada 117 if(MatchPixel(image,xt,yt-1,width,height,testColour))
7011cf13
JS
118 {
119 *qs=xt;
120 qs++;
121 *qs=yt-1;
122 qs++;
3a0a5ada 123 image->SetRGB(xt,yt-1,r,g,b);
7011cf13
JS
124
125 //Loop back to beginning of queue
126 if(qs>=(qst+qSz)) qs=qst;
127 }
128
129 //Below current pixel
3a0a5ada 130 if(MatchPixel(image,xt,yt+1,width,height,testColour))
7011cf13
JS
131 {
132 *qs=xt;
133 qs++;
134 *qs=yt+1;
135 qs++;
3a0a5ada 136 image->SetRGB(xt,yt+1,r,g,b);
7011cf13
JS
137 if(qs>=(qst+qSz)) qs=qst;
138 }
139
140 //Left of current pixel
3a0a5ada 141 if(MatchPixel(image,xt-1,yt,width,height,testColour))
7011cf13
JS
142 {
143 *qs=xt-1;
144 qs++;
145 *qs=yt;
146 qs++;
3a0a5ada 147 image->SetRGB(xt-1,yt,r,g,b);
7011cf13
JS
148 if(qs>=(qst+qSz)) qs=qst;
149 }
150
151 //Right of current pixel
3a0a5ada 152 if(MatchPixel(image,xt+1,yt,width,height,testColour))
7011cf13
JS
153 {
154 *qs=xt+1;
155 qs++;
156 *qs=yt;
157 qs++;
3a0a5ada 158 image->SetRGB(xt+1,yt,r,g,b);
7011cf13
JS
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
3a0a5ada 173 delete[] qst;
7011cf13
JS
174 }
175 }
176 else
177 {
178 //style is wxFLOOD_BORDER
179 // fill up to testColor border - if already testColour don't do anything
3a0a5ada
VS
180 if ( image->GetRed(x,y) != testColour.Red()
181 || image->GetGreen(x,y) != testColour.Green()
182 || image->GetBlue(x,y) != testColour.Blue() )
183 {
7011cf13
JS
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
3a0a5ada 205 image->SetRGB(xt,yt,r,g,b);
7011cf13
JS
206
207 //Main queue loop
3a0a5ada 208 while (qr!=qs)
7011cf13
JS
209 {
210 //Add new members to queue
211 //Above current pixel
3a0a5ada 212 if(!MatchBoundaryPixel(image,xt,yt-1,width,height,fillColour,testColour))
7011cf13
JS
213 {
214 *qs=xt;
215 qs++;
216 *qs=yt-1;
217 qs++;
3a0a5ada 218 image->SetRGB(xt,yt-1,r,g,b);
7011cf13
JS
219
220 //Loop back to beginning of queue
221 if(qs>=(qst+qSz)) qs=qst;
222 }
223
224 //Below current pixel
3a0a5ada 225 if(!MatchBoundaryPixel(image,xt,yt+1,width,height,fillColour,testColour))
7011cf13
JS
226 {
227 *qs=xt;
228 qs++;
229 *qs=yt+1;
230 qs++;
3a0a5ada 231 image->SetRGB(xt,yt+1,r,g,b);
7011cf13
JS
232 if(qs>=(qst+qSz)) qs=qst;
233 }
234
235 //Left of current pixel
3a0a5ada 236 if(!MatchBoundaryPixel(image,xt-1,yt,width,height,fillColour,testColour))
7011cf13
JS
237 {
238 *qs=xt-1;
239 qs++;
240 *qs=yt;
241 qs++;
3a0a5ada 242 image->SetRGB(xt-1,yt,r,g,b);
7011cf13
JS
243 if(qs>=(qst+qSz)) qs=qst;
244 }
245
246 //Right of current pixel
3a0a5ada 247 if(!MatchBoundaryPixel(image,xt+1,yt,width,height,fillColour,testColour))
7011cf13
JS
248 {
249 *qs=xt+1;
250 qs++;
251 *qs=yt;
252 qs++;
3a0a5ada 253 image->SetRGB(xt+1,yt,r,g,b);
7011cf13
JS
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
3a0a5ada 268 delete[] qst;
7011cf13
JS
269 }
270 }
271 //all done,
272}
273
3a0a5ada 274
f842e673 275bool wxDoFloodFill(wxDC *dc, wxCoord x, wxCoord y,
89efaf2b 276 const wxColour& col, wxFloodFillStyle style)
f842e673 277{
e6777e65 278 if (dc->GetBrush().IsTransparent())
f842e673 279 return true;
7011cf13 280
f842e673
KH
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
173d67aa
PC
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
f842e673 296 wxBitmap bitmap(width, height);
173d67aa
PC
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);
f842e673
KH
312 memdc.SelectObject(wxNullBitmap);
313
314 wxImage image = bitmap.ConvertToImage();
b2596741 315 wxImageFloodFill(&image, x_dev, y_dev, dc->GetBrush(), col, style);
f842e673
KH
316 bitmap = wxBitmap(image);
317 memdc.SelectObject(bitmap);
173d67aa 318 dc->Blit(x0_log, y0_log, w_log, h_log, &memdc, 0, 0);
f842e673
KH
319
320 return true;
321}
322
323#endif // wxUSE_IMAGE