All of the things and none of the sleep
[ipdf/code.git] / src / objectrenderer.cpp
1 /**
2  * @file objectrenderer.cpp
3  * @brief Implements ObjectRenderer and derived classes
4  */
5
6 #include "objectrenderer.h"
7 #include "view.h"
8 #include <vector>
9 #include <queue>
10 #include <stack>
11
12 using namespace std;
13
14 namespace IPDF
15 {
16
17 /**
18  * ObjectRenderer constructor
19  * Note we cannot compile the shaders in the ShaderProgram constructor
20  *  because the Screen class needs to initialise GL first and it has a
21  *      ShaderProgram member
22  */
23 ObjectRenderer::ObjectRenderer(const ObjectType & type, 
24                 const char * vert_glsl_file, const char * frag_glsl_file, const char * geom_glsl_file)
25                 : m_type(type), m_shader_program(), m_indexes(), m_buffer_builder(NULL)
26 {
27         m_shader_program.InitialiseShaders(vert_glsl_file, frag_glsl_file, geom_glsl_file);
28         m_shader_program.Use();
29         glUniform4f(m_shader_program.GetUniformLocation("colour"), 0,0,0,1); //TODO: Allow different colours
30 }
31
32 /**
33  * Render using GPU
34  */
35 void ObjectRenderer::RenderUsingGPU(unsigned first_obj_id, unsigned last_obj_id)
36 {
37         // If we don't have anything to render, return.
38         if (first_obj_id == last_obj_id) return;
39         // If there are no objects of this type, return.
40         if (m_indexes.empty()) return;
41         unsigned first_index = 0;
42         while (m_indexes.size() > first_index && m_indexes[first_index] < first_obj_id) first_index ++;
43         unsigned last_index = first_index;
44         while (m_indexes.size() > last_index && m_indexes[last_index] < last_obj_id) last_index ++;
45
46         m_shader_program.Use();
47         m_ibo.Bind();
48         glDrawElements(GL_LINES, (last_index-first_index)*2, GL_UNSIGNED_INT, (GLvoid*)(2*first_index*sizeof(uint32_t)));
49 }
50
51
52 /**
53  * Default implementation for rendering using CPU
54  */
55 void ObjectRenderer::RenderUsingCPU(Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id)
56 {
57         Error("Cannot render objects of type %d on CPU", m_type);
58         //TODO: Render a rect or something instead?
59 }
60
61 /**
62  * Prepare index buffers for both CPU and GPU rendering to receive indexes (but don't add any yet!)
63  */
64 void ObjectRenderer::PrepareBuffers(unsigned max_objects)
65 {
66         if (m_buffer_builder != NULL) // We already have a BufferBuilder
67         {
68                 Fatal("Has been called before, without FinaliseBuffers being called since!");
69         }
70         // Empty and reserve the indexes vector (for CPU rendering)
71         m_indexes.clear();
72         m_indexes.reserve(max_objects); //TODO: Can probably make this smaller? Or leave it out? Do we care?
73
74         // Initialise and resize the ibo (for GPU rendering)
75         m_ibo.Invalidate();
76         m_ibo.SetUsage(GraphicsBuffer::BufferUsageStaticDraw);
77         m_ibo.SetType(GraphicsBuffer::BufferTypeIndex);
78         m_ibo.Resize(max_objects * 2 * sizeof(uint32_t));
79         // BufferBuilder is used to construct the ibo
80         m_buffer_builder = new BufferBuilder<uint32_t>(m_ibo.Map(false, true, true), m_ibo.GetSize()); // new matches delete in ObjectRenderer::FinaliseBuffers
81
82 }
83
84 /**
85  * Add object index to the buffers for CPU and GPU rendering
86  */
87 void ObjectRenderer::AddObjectToBuffers(unsigned index)
88 {
89         if (m_buffer_builder == NULL) // No BufferBuilder!
90         {
91                 Fatal("Called without calling PrepareBuffers");
92         }
93         m_buffer_builder->Add(2*index); // ibo for GPU rendering
94         m_buffer_builder->Add(2*index+1);
95         m_indexes.push_back(index); // std::vector of indices for CPU rendering
96 }
97
98 /**
99  * Finalise the index buffers for CPU and GPU rendering
100  */
101 void ObjectRenderer::FinaliseBuffers()
102 {
103         if (m_buffer_builder == NULL) // No BufferBuilder!
104         {
105                 Fatal("Called without calling PrepareBuffers");
106         }
107         // For GPU rendering, UnMap the ibo
108         m_ibo.UnMap();
109         // ... and delete the BufferBuilder used to create it
110         delete m_buffer_builder; // delete matches new in ObjectRenderer::PrepareBuffers
111         m_buffer_builder = NULL;
112         
113         // Nothing is necessary for CPU rendering
114 }
115
116
117 /**
118  * Rectangle (filled)
119  */
120 void RectFilledRenderer::RenderUsingCPU(Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id)
121 {
122         for (unsigned i = 0; i < m_indexes.size(); ++i)
123         {
124                 if (m_indexes[i] < first_obj_id) continue;
125                 if (m_indexes[i] >= last_obj_id) continue;
126                 PixelBounds bounds(CPURenderBounds(objects.bounds[m_indexes[i]], view, target));
127                 FloodFillOnCPU(bounds.x+1, bounds.y+1, bounds, target, Colour(0,0,0,1));
128                 /*
129                 for (int64_t x = max((int64_t)0, bounds.x); x <= min(bounds.x+bounds.w, target.w-1); ++x)
130                 {
131                         for (int64_t y = max((int64_t)0, bounds.y); y <= min(bounds.y+bounds.h, target.h-1); ++y)
132                         {
133                                 int index = (x+target.w*y)*4;
134                                 target.pixels[index+0] = 0;
135                                 target.pixels[index+1] = 0;
136                                 target.pixels[index+2] = 0;
137                                 target.pixels[index+3] = 255;
138                         }
139                 }
140                 */
141         }
142 }
143
144 /**
145  * Rectangle (outine)
146  */
147 void RectOutlineRenderer::RenderUsingCPU(Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id)
148 {
149         //Debug("Render %u outlined rectangles on CPU", m_indexes.size());
150         for (unsigned i = 0; i < m_indexes.size(); ++i)
151         {
152                 if (m_indexes[i] < first_obj_id) continue;
153                 if (m_indexes[i] >= last_obj_id) continue;
154                 PixelBounds bounds(CPURenderBounds(objects.bounds[m_indexes[i]], view, target));
155                 
156                 // Using bresenham's lines now mainly because I want to see if they work
157                 // top
158                 ObjectRenderer::RenderLineOnCPU(bounds.x, bounds.y, bounds.x+bounds.w, bounds.y, target);
159                 // bottom
160                 ObjectRenderer::RenderLineOnCPU(bounds.x, bounds.y+bounds.h, bounds.x+bounds.w, bounds.y+bounds.h, target);
161                 // left
162                 ObjectRenderer::RenderLineOnCPU(bounds.x, bounds.y, bounds.x, bounds.y+bounds.h, target);
163                 // right
164                 ObjectRenderer::RenderLineOnCPU(bounds.x+bounds.w, bounds.y, bounds.x+bounds.w, bounds.y+bounds.h, target);
165
166                 // Diagonal for testing (from bottom left to top right)
167                 //ObjectRenderer::RenderLineOnCPU(bounds.x,bounds.y+bounds.h, bounds.x+bounds.w, bounds.y,target, C_BLUE);
168                 //ObjectRenderer::RenderLineOnCPU(bounds.x+bounds.w, bounds.y+bounds.h, bounds.x, bounds.y, target,C_GREEN);
169         }
170 }
171
172 /**
173  * Circle (filled)
174  */
175 void CircleFilledRenderer::RenderUsingCPU(Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id)
176 {
177         for (unsigned i = 0; i < m_indexes.size(); ++i)
178         {
179                 if (m_indexes[i] < first_obj_id) continue;
180                 if (m_indexes[i] >= last_obj_id) continue;
181                 PixelBounds bounds(CPURenderBounds(objects.bounds[m_indexes[i]], view, target));
182                 int64_t centre_x = bounds.x + bounds.w / 2;
183                 int64_t centre_y = bounds.y + bounds.h / 2;
184                 
185                 //Debug("Centre is %d, %d", centre_x, centre_y);
186                 //Debug("Bounds are %d,%d,%d,%d", bounds.x, bounds.y, bounds.w, bounds.h);
187                 //Debug("Windos is %d,%d", target.w, target.h);
188                 for (int64_t x = max((int64_t)0, bounds.x); x <= min(bounds.x+bounds.w, target.w-1); ++x)
189                 {
190                         for (int64_t y = max((int64_t)0, bounds.y); y <= min(bounds.y + bounds.h, target.h-1); ++y)
191                         {
192                                 Real dx(2); dx *= Real(x - centre_x)/Real(bounds.w);
193                                 Real dy(2); dy *= Real(y - centre_y)/Real(bounds.h);
194                                 int64_t index = (x+target.w*y)*4;
195                                 
196                                 if (dx*dx + dy*dy <= Real(1))
197                                 {
198                                         target.pixels[index+0] = 0;
199                                         target.pixels[index+1] = 0;
200                                         target.pixels[index+2] = 0;
201                                         target.pixels[index+3] = 255;
202                                 }
203                         }
204                 }
205         }
206 }
207
208 Rect ObjectRenderer::CPURenderBounds(const Rect & bounds, const View & view, const CPURenderTarget & target)
209 {
210         Rect result = view.TransformToViewCoords(bounds);
211         result.x *= Real(target.w);
212         result.y *= Real(target.h);
213         result.w *= Real(target.w);
214         result.h *= Real(target.h);
215         return result;
216 }
217
218 ObjectRenderer::PixelPoint ObjectRenderer::CPUPointLocation(const Vec2 & point, const View & view, const CPURenderTarget & target)
219 {
220         // hack...
221         Rect result = view.TransformToViewCoords(Rect(point.x, point.y,1,1));
222         int64_t x = Int64(result.x)*target.w;
223         int64_t y = Int64(result.y)*target.h;
224         return PixelPoint(x,y);
225 }
226         
227         
228 void BezierRenderer::RenderBezierOnCPU(unsigned i, Objects & objects, const View & view, const CPURenderTarget & target, const Colour & c)
229 {
230         const Rect & bounds = objects.bounds[i];
231         PixelBounds pix_bounds(CPURenderBounds(bounds,view,target));
232         Bezier control(objects.beziers[objects.data_indices[i]].ToAbsolute(bounds),CPURenderBounds(Rect(0,0,1,1), view, target));
233
234         if (view.ShowingBezierBounds())
235         {
236                 ObjectRenderer::RenderLineOnCPU(pix_bounds.x, pix_bounds.y, pix_bounds.x+pix_bounds.w, pix_bounds.y, target, Colour(255,0,0,0));
237                 ObjectRenderer::RenderLineOnCPU(pix_bounds.x, pix_bounds.y+pix_bounds.h, pix_bounds.x+pix_bounds.w, pix_bounds.y+pix_bounds.h, target, Colour(0,255,0,0));
238                 ObjectRenderer::RenderLineOnCPU(pix_bounds.x, pix_bounds.y, pix_bounds.x, pix_bounds.y+pix_bounds.h, target, Colour(255,0,0,0));
239                 ObjectRenderer::RenderLineOnCPU(pix_bounds.x+pix_bounds.w, pix_bounds.y, pix_bounds.x+pix_bounds.w, pix_bounds.y+pix_bounds.h, target, Colour(0,255,0,0));
240         }
241         
242         unsigned blen = target.w;//min(max(2U, (unsigned)Int64(Real(target.w)/view.GetBounds().w)), 
243                         //min((unsigned)(pix_bounds.w+pix_bounds.h)/4 + 1, 100U));
244                 
245                 // DeCasteljau Divide the Bezier
246         queue<Bezier> divisions;
247         divisions.push(control);
248         while(divisions.size() < blen)
249         {
250                 Bezier & current = divisions.front();
251                 if (current.GetType() == Bezier::LINE)
252                 {
253                         --blen;
254                         continue;
255                 }
256                 divisions.push(current.DeCasteljauSubdivideRight(Real(1)/Real(2)));     
257                 divisions.push(current.DeCasteljauSubdivideLeft(Real(1)/Real(2)));
258                 divisions.pop();
259         }
260         while (divisions.size() > 0)
261         {
262                 Bezier & current = divisions.front();
263                 RenderLineOnCPU(Int64(current.x0), Int64(current.y0), Int64(current.x3), Int64(current.y3), target, c);
264                 divisions.pop();
265         }               
266 }
267
268 /**
269  * Bezier curve
270  * Not sure how to apply De'Casteljau, will just use a bunch of Bresnham lines for now.
271  */
272 void BezierRenderer::RenderUsingCPU(Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id)
273 {
274         if (view.PerformingShading())
275                 return;
276                 
277         //Warn("Rendering Beziers on CPU. Things may explode.");
278         for (unsigned i = 0; i < m_indexes.size(); ++i)
279         {
280                 if (m_indexes[i] < first_obj_id) continue;
281                 if (m_indexes[i] >= last_obj_id) continue;
282                 Colour c(0,0,0,255);
283                 if (view.ShowingBezierType())
284                 {
285                         switch (objects.beziers[objects.data_indices[m_indexes[i]]].GetType())
286                         {
287                                 case Bezier::LINE:
288                                         break;
289                                 case Bezier::QUADRATIC:
290                                         c.b = 255;
291                                         break;
292                                 case Bezier::SERPENTINE:
293                                         c.r = 255;
294                                         break;
295                                 case Bezier::CUSP:
296                                         c.g = 255;
297                                         break;
298                                 case Bezier::LOOP:
299                                         c.r = 128;
300                                         c.b = 128;
301                                         break;
302                                 default:
303                                         c.r = 128;
304                                         c.g = 128;
305                                         break;
306                         }
307                 }
308                 RenderBezierOnCPU(m_indexes[i], objects, view, target, c);
309         }
310 }
311
312 void BezierRenderer::PrepareBezierGPUBuffer(Objects & objects)
313 {
314         m_bezier_coeffs.SetType(GraphicsBuffer::BufferTypeTexture);
315         m_bezier_coeffs.SetUsage(GraphicsBuffer::BufferUsageDynamicDraw);
316         m_bezier_coeffs.Resize(objects.beziers.size()*sizeof(GPUBezierCoeffs));
317         BufferBuilder<GPUBezierCoeffs> builder(m_bezier_coeffs.Map(false, true, true), m_bezier_coeffs.GetSize());
318
319
320         for (unsigned i = 0; i < objects.beziers.size(); ++i)
321         {
322                 const Bezier & bez = objects.beziers[i];
323                 
324                 GPUBezierCoeffs coeffs = {
325                         Float(bez.x0), Float(bez.y0),
326                         Float(bez.x1), Float(bez.y1),
327                         Float(bez.x2), Float(bez.y2),
328                         Float(bez.x3), Float(bez.y3)
329                         };
330                 builder.Add(coeffs);
331         }
332         m_bezier_coeffs.UnMap();
333         glGenTextures(1, &m_bezier_buffer_texture);
334         glBindTexture(GL_TEXTURE_BUFFER, m_bezier_buffer_texture);
335         glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, m_bezier_coeffs.GetHandle());
336
337         m_bezier_ids.SetType(GraphicsBuffer::BufferTypeTexture);
338         m_bezier_ids.SetUsage(GraphicsBuffer::BufferUsageDynamicDraw);
339         m_bezier_ids.Upload(objects.data_indices.size() * sizeof(uint32_t), &objects.data_indices[0]);
340         
341         glGenTextures(1, &m_bezier_id_buffer_texture);
342         glActiveTexture(GL_TEXTURE1);
343         glBindTexture(GL_TEXTURE_BUFFER, m_bezier_id_buffer_texture);
344         glTexBuffer(GL_TEXTURE_BUFFER, GL_R32I, m_bezier_ids.GetHandle());
345         glActiveTexture(GL_TEXTURE0);
346 }
347
348 void BezierRenderer::RenderUsingGPU(unsigned first_obj_id, unsigned last_obj_id)
349 {
350         if (!m_shader_program.Valid())
351                 Warn("Shader is invalid (objects are of type %d)", m_type);
352
353         // If we don't have anything to render, return.
354         if (first_obj_id == last_obj_id) return;
355         // If there are no objects of this type, return.
356         if (m_indexes.empty()) return;
357
358         unsigned first_index = 0;
359         while (m_indexes.size() > first_index && m_indexes[first_index] < first_obj_id) first_index ++;
360         unsigned last_index = first_index;
361         while (m_indexes.size() > last_index && m_indexes[last_index] < last_obj_id) last_index ++;
362
363         m_shader_program.Use();
364         glUniform1i(m_shader_program.GetUniformLocation("bezier_buffer_texture"), 0);
365         glUniform1i(m_shader_program.GetUniformLocation("bezier_id_buffer_texture"), 1);
366         m_ibo.Bind();
367         glDrawElements(GL_LINES, (last_index-first_index)*2, GL_UNSIGNED_INT, (GLvoid*)(2*first_index*sizeof(uint32_t)));
368 }
369
370
371
372 /**
373  * Render Path (shading)
374  */
375 void PathRenderer::RenderUsingCPU(Objects & objects, const View & view, const CPURenderTarget & target, unsigned first_obj_id, unsigned last_obj_id)
376 {
377
378                 
379         for (unsigned i = 0; i < m_indexes.size(); ++i)
380         {
381                 if (m_indexes[i] < first_obj_id) continue;
382                 if (m_indexes[i] >= last_obj_id) continue;
383                 
384                 
385                 Rect bounds(CPURenderBounds(objects.bounds[m_indexes[i]], view, target));
386                 PixelBounds pix_bounds(bounds);
387                 Path & path = objects.paths[objects.data_indices[m_indexes[i]]];
388                 
389                 if (view.ShowingFillPoints())
390                 {
391                         
392                         PixelPoint start(CPUPointLocation((path.m_top+path.m_left+path.m_right+path.m_bottom)/4, view, target));
393                         for (unsigned f = 0; f < path.m_fill_points.size(); ++f)
394                         {
395                                 PixelPoint end(CPUPointLocation(path.m_fill_points[f], view, target));
396                                 RenderLineOnCPU(start.first, start.second, end.first, end.second, target, Colour(0,0,255,0));
397                         }
398                 }
399                 
400                 if (!view.PerformingShading())
401                         continue;
402                 
403                 for (unsigned b = path.m_start; b <= path.m_end; ++b)
404                 {
405                         BezierRenderer::RenderBezierOnCPU(b,objects,view,target,path.m_stroke);
406                 }
407                 
408                 if (pix_bounds.w*pix_bounds.h > 100)
409                 {
410                         vector<Vec2> & fill_points = path.FillPoints(objects, view);
411                         Debug("High resolution; use fill points %u,%u", pix_bounds.w, pix_bounds.h);
412                         for (unsigned f = 0; f < fill_points.size(); ++f)
413                         {
414                                 PixelPoint fill_point(CPUPointLocation(fill_points[f], view, target));
415                                 
416                                 FloodFillOnCPU(fill_point.first, fill_point.second, pix_bounds, target, path.m_fill, path.m_stroke);
417                         }
418                 }
419                 else
420                 {
421                         Debug("Low resolution; use brute force %u,%u",pix_bounds.w, pix_bounds.h);
422                         int64_t y_min = max((int64_t)0, pix_bounds.y);
423                         int64_t y_max = min(pix_bounds.y+pix_bounds.h, target.h);
424                         int64_t x_min = max((int64_t)0, pix_bounds.x);
425                         int64_t x_max = min(pix_bounds.x+pix_bounds.w, target.w);
426                         for (int64_t y = y_min; y < y_max; ++y)
427                         {
428                                 for (int64_t x = x_min; x < x_max; ++x)
429                                 {
430                                         Rect pb(path.SolveBounds(objects));
431                                         Vec2 pt(pb.x + (Real(x-pix_bounds.x)/Real(pix_bounds.w))*pb.w, 
432                                                         pb.y + (Real(y-pix_bounds.y)/Real(pix_bounds.h))*pb.h);
433                                         if (path.PointInside(objects, pt))
434                                         {
435                                                 FloodFillOnCPU(x, y, pix_bounds, target, path.m_fill, path.m_stroke);
436                                         }
437                                 }
438                         }
439                 }
440         }       
441 }
442
443 /**
444  * For debug, save pixels to bitmap
445  */
446 void ObjectRenderer::SaveBMP(const CPURenderTarget & target, const char * filename)
447 {
448         SDL_Surface * surf = SDL_CreateRGBSurfaceFrom(target.pixels, target.w, target.h, 8*4, target.w*4,
449         #if SDL_BYTEORDER == SDL_LIL_ENDIAN
450                 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
451         #else
452                 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff
453         #endif //SDL_BYTEORDER  
454         );      
455         if (surf == NULL)
456                 Fatal("SDL_CreateRGBSurfaceFrom(pixels...) failed - %s", SDL_GetError());
457         if (SDL_SaveBMP(surf, filename) != 0)
458                 Fatal("SDL_SaveBMP failed - %s", SDL_GetError());
459
460         // Cleanup
461         SDL_FreeSurface(surf);
462 }
463
464 /**
465  * Bresenham's lines
466  */
467 void ObjectRenderer::RenderLineOnCPU(int64_t x0, int64_t y0, int64_t x1, int64_t y1, const CPURenderTarget & target, const Colour & colour, bool transpose)
468 {
469         int64_t dx = x1 - x0;
470         int64_t dy = y1 - y0;
471         bool neg_m = (dy*dx < 0);
472         dy = abs(dy);
473         dx = abs(dx);
474
475         // If positive slope > 1, just swap x and y
476         if (dy > dx)
477         {
478                 RenderLineOnCPU(y0,x0,y1,x1,target,colour,!transpose);
479                 return;
480         }
481
482         int64_t two_dy = 2*dy;
483         int64_t p = two_dy - dx;
484         int64_t two_dxdy = 2*(dy-dx);
485         int64_t x; int64_t y; int64_t x_end;
486         int64_t width = (transpose ? target.h : target.w);
487         int64_t height = (transpose ? target.w : target.h);
488
489         if (x0 > x1)
490         {
491                 x = x1;
492                 y = y1;
493                 x_end = x0;
494         }
495         else
496         {
497                 x = x0;
498                 y = y0;
499                 x_end = x1;
500         }
501
502         if (x < 0)
503         {
504                 if (x_end < 0) return;
505                 y = (neg_m ? y - (dy*-x)/dx : y + (dy*-x)/dx);
506                 x = 0;
507         }
508         
509         if (x_end > width)
510         {
511                 if (x > width) return;
512                 x_end = width-1;
513         }
514
515         // TODO: Avoid extra inner conditionals
516         do
517         {       
518                 if (x >= 0 && x < width && y >= 0 && y < height)
519                 {
520                         int64_t index = (transpose ? (y + x*target.w)*4 : (x + y*target.w)*4);
521                         target.pixels[index+0] = colour.r;
522                         target.pixels[index+1] = colour.g;
523                         target.pixels[index+2] = colour.b;
524                         target.pixels[index+3] = colour.a;
525                 }
526                 if (p < 0)
527                         p += two_dy;
528                 else
529                 {
530                         if (neg_m) --y; else ++y;
531                         p += two_dxdy;
532                 }
533         } while (++x <= x_end);
534 }
535
536
537 void ObjectRenderer::FloodFillOnCPU(int64_t x, int64_t y, const PixelBounds & bounds, const CPURenderTarget & target, const Colour & fill, const Colour & stroke)
538 {
539         // HACK to prevent overflooding (when the fill points for a path round to the pixel outside the boundary)
540         // (I totally just made that term up...)
541         Colour c = GetColour(target, x+1, y);
542         if (c == fill || c == stroke)
543                 return;
544         c = GetColour(target, x-1, y);
545         if (c == fill || c == stroke)
546                 return;
547         c = GetColour(target, x, y+1);
548         if (c == fill || c == stroke)
549                 return;
550         c = GetColour(target, x, y-1);
551         if (c == fill || c == stroke)
552                 return;
553                 
554         // The hack works but now we get underflooding, or, "droughts".
555         
556                 
557         queue<PixelPoint > traverse;
558         traverse.push(PixelPoint(x,y));
559         // now with 100% less stack overflows!
560         while (traverse.size() > 0)
561         {
562                 PixelPoint cur(traverse.front());
563                 traverse.pop();
564                 if (cur.first < 0 || cur.first < bounds.x || cur.first >= bounds.x+bounds.w || cur.first >= target.w ||
565                         cur.second < 0 || cur.second < bounds.y || cur.second >= bounds.y+bounds.h || cur.second >= target.h)
566                         continue;
567                 c = GetColour(target, cur.first, cur.second);
568                 if (c == fill || c == stroke)
569                         continue;
570
571                 SetColour(target, cur.first, cur.second, fill);
572                 
573                 //Debug("c is {%u,%u,%u,%u} fill is {%u,%u,%u,%u}, stroke is {%u,%u,%u,%u}",
574                 //      c.r,c.g,c.b,c.a, fill.r,fill.g,fill.b,fill.a, stroke.r,stroke.g,stroke.b,stroke.a);
575
576                 traverse.push(PixelPoint(cur.first+1, cur.second));
577                 traverse.push(PixelPoint(cur.first-1, cur.second));
578                 traverse.push(PixelPoint(cur.first, cur.second-1));
579                 traverse.push(PixelPoint(cur.first, cur.second+1)); 
580         }
581 }
582
583 }

UCC git Repository :: git.ucc.asn.au