// there is no elegance here. only sleep deprivation and regret.
[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 = result.x*target.w;
223         int64_t y = 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 = min(max(2U, (unsigned)(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(current.x0, current.y0, current.x3, 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                 const 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                         Debug("High resolution; use fill points %u,%u", pix_bounds.w, pix_bounds.h);
411                         for (unsigned f = 0; f < path.m_fill_points.size(); ++f)
412                         {
413                                 PixelPoint fill_point(CPUPointLocation(path.m_fill_points[f], view, target));
414                                 
415                                 FloodFillOnCPU(fill_point.first, fill_point.second, pix_bounds, target, path.m_fill, path.m_stroke);
416                         }
417                 }
418                 else
419                 {
420                         Debug("Low resolution; use brute force %u,%u",pix_bounds.w, pix_bounds.h);
421                         int64_t y_min = max((int64_t)0, pix_bounds.y);
422                         int64_t y_max = min(pix_bounds.y+pix_bounds.h, target.h);
423                         int64_t x_min = max((int64_t)0, pix_bounds.x);
424                         int64_t x_max = min(pix_bounds.x+pix_bounds.w, target.w);
425                         for (int64_t y = y_min; y < y_max; ++y)
426                         {
427                                 for (int64_t x = x_min; x < x_max; ++x)
428                                 {
429                                         Rect pb(path.SolveBounds(objects));
430                                         Vec2 pt(pb.x + (Real(x-pix_bounds.x)/Real(pix_bounds.w))*pb.w, 
431                                                         pb.y + (Real(y-pix_bounds.y)/Real(pix_bounds.h))*pb.h);
432                                         if (path.PointInside(objects, pt))
433                                         {
434                                                 FloodFillOnCPU(x, y, pix_bounds, target, path.m_fill, path.m_stroke);
435                                         }
436                                 }
437                         }
438                 }
439         }       
440 }
441
442 /**
443  * For debug, save pixels to bitmap
444  */
445 void ObjectRenderer::SaveBMP(const CPURenderTarget & target, const char * filename)
446 {
447         SDL_Surface * surf = SDL_CreateRGBSurfaceFrom(target.pixels, target.w, target.h, 8*4, target.w*4,
448         #if SDL_BYTEORDER == SDL_LIL_ENDIAN
449                 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
450         #else
451                 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff
452         #endif //SDL_BYTEORDER  
453         );      
454         if (surf == NULL)
455                 Fatal("SDL_CreateRGBSurfaceFrom(pixels...) failed - %s", SDL_GetError());
456         if (SDL_SaveBMP(surf, filename) != 0)
457                 Fatal("SDL_SaveBMP failed - %s", SDL_GetError());
458
459         // Cleanup
460         SDL_FreeSurface(surf);
461 }
462
463 /**
464  * Bresenham's lines
465  */
466 void ObjectRenderer::RenderLineOnCPU(int64_t x0, int64_t y0, int64_t x1, int64_t y1, const CPURenderTarget & target, const Colour & colour, bool transpose)
467 {
468         int64_t dx = x1 - x0;
469         int64_t dy = y1 - y0;
470         bool neg_m = (dy*dx < 0);
471         dy = abs(dy);
472         dx = abs(dx);
473
474         // If positive slope > 1, just swap x and y
475         if (dy > dx)
476         {
477                 RenderLineOnCPU(y0,x0,y1,x1,target,colour,!transpose);
478                 return;
479         }
480
481         int64_t two_dy = 2*dy;
482         int64_t p = two_dy - dx;
483         int64_t two_dxdy = 2*(dy-dx);
484         int64_t x; int64_t y; int64_t x_end;
485         int64_t width = (transpose ? target.h : target.w);
486         int64_t height = (transpose ? target.w : target.h);
487
488         if (x0 > x1)
489         {
490                 x = x1;
491                 y = y1;
492                 x_end = x0;
493         }
494         else
495         {
496                 x = x0;
497                 y = y0;
498                 x_end = x1;
499         }
500
501         if (x < 0)
502         {
503                 if (x_end < 0) return;
504                 y = (neg_m ? y - (dy*-x)/dx : y + (dy*-x)/dx);
505                 x = 0;
506         }
507         
508         if (x_end > width)
509         {
510                 if (x > width) return;
511                 x_end = width-1;
512         }
513
514         // TODO: Avoid extra inner conditionals
515         do
516         {       
517                 if (x >= 0 && x < width && y >= 0 && y < height)
518                 {
519                         int64_t index = (transpose ? (y + x*target.w)*4 : (x + y*target.w)*4);
520                         target.pixels[index+0] = colour.r;
521                         target.pixels[index+1] = colour.g;
522                         target.pixels[index+2] = colour.b;
523                         target.pixels[index+3] = colour.a;
524                 }
525                 if (p < 0)
526                         p += two_dy;
527                 else
528                 {
529                         if (neg_m) --y; else ++y;
530                         p += two_dxdy;
531                 }
532         } while (++x <= x_end);
533 }
534
535
536 void ObjectRenderer::FloodFillOnCPU(int64_t x, int64_t y, const PixelBounds & bounds, const CPURenderTarget & target, const Colour & fill, const Colour & stroke)
537 {
538         // HACK to prevent overflooding (when the fill points for a path round to the pixel outside the boundary)
539         // (I totally just made that term up...)
540         Colour c = GetColour(target, x+1, y);
541         if (c == fill || c == stroke)
542                 return;
543         c = GetColour(target, x-1, y);
544         if (c == fill || c == stroke)
545                 return;
546         c = GetColour(target, x, y+1);
547         if (c == fill || c == stroke)
548                 return;
549         c = GetColour(target, x, y-1);
550         if (c == fill || c == stroke)
551                 return;
552                 
553         // The hack works but now we get underflooding, or, "droughts".
554         
555                 
556         queue<PixelPoint > traverse;
557         traverse.push(PixelPoint(x,y));
558         // now with 100% less stack overflows!
559         while (traverse.size() > 0)
560         {
561                 PixelPoint cur(traverse.front());
562                 traverse.pop();
563                 if (cur.first < 0 || cur.first < bounds.x || cur.first >= bounds.x+bounds.w || cur.first >= target.w ||
564                         cur.second < 0 || cur.second < bounds.y || cur.second >= bounds.y+bounds.h || cur.second >= target.h)
565                         continue;
566                 c = GetColour(target, cur.first, cur.second);
567                 if (c == fill || c == stroke)
568                         continue;
569
570                 SetColour(target, cur.first, cur.second, fill);
571                 
572                 //Debug("c is {%u,%u,%u,%u} fill is {%u,%u,%u,%u}, stroke is {%u,%u,%u,%u}",
573                 //      c.r,c.g,c.b,c.a, fill.r,fill.g,fill.b,fill.a, stroke.r,stroke.g,stroke.b,stroke.a);
574
575                 traverse.push(PixelPoint(cur.first+1, cur.second));
576                 traverse.push(PixelPoint(cur.first-1, cur.second));
577                 traverse.push(PixelPoint(cur.first, cur.second-1));
578                 traverse.push(PixelPoint(cur.first, cur.second+1)); 
579         }
580 }
581
582 }

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