1 //----------------------------------------------------------------------------
2 // Anti-Grain Geometry - Version 2.4
3 // Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
4 //
5 // Permission to copy, use, modify, sell and distribute this software
6 // is granted provided this copyright notice appears in all copies.
7 // This software is provided "as is" without express or implied
8 // warranty, and with no claim as to its suitability for any purpose.
9 //
10 //----------------------------------------------------------------------------
11 //
12 // The author gratefully acknowleges the support of David Turner,
13 // Robert Wilhelm, and Werner Lemberg - the authors of the FreeType
14 // libray - in producing this work. See http://www.freetype.org for details.
15 //
16 //----------------------------------------------------------------------------
17 // Contact: mcseem@antigrain.com
18 //          mcseemagg@yahoo.com
19 //          http://www.antigrain.com
20 //----------------------------------------------------------------------------
21 //
22 // Adaptation for 32-bit screen coordinates has been sponsored by
23 // Liberty Technology Systems, Inc., visit http://lib-sys.com
24 //
25 // Liberty Technology Systems, Inc. is the provider of
26 // PostScript and PDF technology for software developers.
27 //
28 //----------------------------------------------------------------------------
29 #ifndef AGG_RASTERIZER_SCANLINE_AA_INCLUDED
30 #define AGG_RASTERIZER_SCANLINE_AA_INCLUDED
31 
32 #include "agg_rasterizer_cells_aa.h"
33 #include "agg_rasterizer_sl_clip.h"
34 #include "agg_rasterizer_scanline_aa_nogamma.h"
35 #include "agg_gamma_functions.h"
36 
37 
38 namespace agg
39 {
40     //==================================================rasterizer_scanline_aa
41     // Polygon rasterizer that is used to render filled polygons with
42     // high-quality Anti-Aliasing. Internally, by default, the class uses
43     // integer coordinates in format 24.8, i.e. 24 bits for integer part
44     // and 8 bits for fractional - see poly_subpixel_shift. This class can be
45     // used in the following  way:
46     //
47     // 1. filling_rule(filling_rule_e ft) - optional.
48     //
49     // 2. gamma() - optional.
50     //
51     // 3. reset()
52     //
53     // 4. move_to(x, y) / line_to(x, y) - make the polygon. One can create
54     //    more than one contour, but each contour must consist of at least 3
55     //    vertices, i.e. move_to(x1, y1); line_to(x2, y2); line_to(x3, y3);
56     //    is the absolute minimum of vertices that define a triangle.
57     //    The algorithm does not check either the number of vertices nor
58     //    coincidence of their coordinates, but in the worst case it just
59     //    won't draw anything.
60     //    The orger of the vertices (clockwise or counterclockwise)
61     //    is important when using the non-zero filling rule (fill_non_zero).
62     //    In this case the vertex order of all the contours must be the same
63     //    if you want your intersecting polygons to be without "holes".
64     //    You actually can use different vertices order. If the contours do not
65     //    intersect each other the order is not important anyway. If they do,
66     //    contours with the same vertex order will be rendered without "holes"
67     //    while the intersecting contours with different orders will have "holes".
68     //
69     // filling_rule() and gamma() can be called anytime before "sweeping".
70     //------------------------------------------------------------------------
71     template<class Clip=rasterizer_sl_clip_int> class rasterizer_scanline_aa
72     {
73         enum status
74         {
75             status_initial,
76             status_move_to,
77             status_line_to,
78             status_closed
79         };
80 
81     public:
82         typedef Clip                      clip_type;
83         typedef typename Clip::conv_type  conv_type;
84         typedef typename Clip::coord_type coord_type;
85 
86         enum aa_scale_e
87         {
88             aa_shift  = 8,
89             aa_scale  = 1 << aa_shift,
90             aa_mask   = aa_scale - 1,
91             aa_scale2 = aa_scale * 2,
92             aa_mask2  = aa_scale2 - 1
93         };
94 
95         //--------------------------------------------------------------------
rasterizer_scanline_aa()96         rasterizer_scanline_aa() :
97             m_outline(),
98             m_clipper(),
99             m_filling_rule(fill_non_zero),
100             m_auto_close(true),
101             m_start_x(0),
102             m_start_y(0),
103             m_status(status_initial)
104         {
105             int i;
106             for(i = 0; i < aa_scale; i++) m_gamma[i] = i;
107         }
108 
109         //--------------------------------------------------------------------
110         template<class GammaF>
rasterizer_scanline_aa(const GammaF & gamma_function)111         rasterizer_scanline_aa(const GammaF& gamma_function) :
112             m_outline(),
113             m_clipper(m_outline),
114             m_filling_rule(fill_non_zero),
115             m_auto_close(true),
116             m_start_x(0),
117             m_start_y(0),
118             m_status(status_initial)
119         {
120             gamma(gamma_function);
121         }
122 
123         //--------------------------------------------------------------------
124         void reset();
125         void reset_clipping();
126         void clip_box(double x1, double y1, double x2, double y2);
127         void filling_rule(filling_rule_e filling_rule);
auto_close(bool flag)128         void auto_close(bool flag) { m_auto_close = flag; }
129 
130         //--------------------------------------------------------------------
gamma(const GammaF & gamma_function)131         template<class GammaF> void gamma(const GammaF& gamma_function)
132         {
133             int i;
134             for(i = 0; i < aa_scale; i++)
135             {
136                 m_gamma[i] = uround(gamma_function(double(i) / aa_mask) * aa_mask);
137             }
138         }
139 
140         //--------------------------------------------------------------------
apply_gamma(unsigned cover)141         unsigned apply_gamma(unsigned cover) const
142         {
143             return m_gamma[cover];
144         }
145 
146         //--------------------------------------------------------------------
147         void move_to(int x, int y);
148         void line_to(int x, int y);
149         void move_to_d(double x, double y);
150         void line_to_d(double x, double y);
151         void close_polygon();
152         void add_vertex(double x, double y, unsigned cmd);
153 
154         void edge(int x1, int y1, int x2, int y2);
155         void edge_d(double x1, double y1, double x2, double y2);
156 
157         //-------------------------------------------------------------------
158         template<class VertexSource>
159         void add_path(VertexSource &&vs, unsigned path_id=0)
160         {
161             double x;
162             double y;
163 
164             unsigned cmd;
165             vs.rewind(path_id);
166             if(m_outline.sorted()) reset();
167             while(!is_stop(cmd = vs.vertex(&x, &y)))
168             {
169                 add_vertex(x, y, cmd);
170             }
171         }
172 
173         //--------------------------------------------------------------------
min_x()174         int min_x() const { return m_outline.min_x(); }
min_y()175         int min_y() const { return m_outline.min_y(); }
max_x()176         int max_x() const { return m_outline.max_x(); }
max_y()177         int max_y() const { return m_outline.max_y(); }
178 
179         //--------------------------------------------------------------------
180         void sort();
181         bool rewind_scanlines();
182         bool navigate_scanline(int y);
183 
184         //--------------------------------------------------------------------
calculate_alpha(int area)185         AGG_INLINE unsigned calculate_alpha(int area) const
186         {
187             int cover = area >> (poly_subpixel_shift*2 + 1 - aa_shift);
188 
189             if(cover < 0) cover = -cover;
190             if(m_filling_rule == fill_even_odd)
191             {
192                 cover &= aa_mask2;
193                 if(cover > aa_scale)
194                 {
195                     cover = aa_scale2 - cover;
196                 }
197             }
198             if(cover > aa_mask) cover = aa_mask;
199             return m_gamma[cover];
200         }
201 
202         //--------------------------------------------------------------------
sweep_scanline(Scanline & sl)203         template<class Scanline> bool sweep_scanline(Scanline& sl)
204         {
205             for(;;)
206             {
207                 if(m_scan_y > m_outline.max_y()) return false;
208                 sl.reset_spans();
209                 unsigned num_cells = m_outline.scanline_num_cells(m_scan_y);
210                 const cell_aa* const* cells = m_outline.scanline_cells(m_scan_y);
211                 int cover = 0;
212 
213                 while(num_cells)
214                 {
215                     const cell_aa* cur_cell = *cells;
216                     int x    = cur_cell->x;
217                     int area = cur_cell->area;
218                     unsigned alpha;
219 
220                     cover += cur_cell->cover;
221 
222                     //accumulate all cells with the same X
223                     while(--num_cells)
224                     {
225                         cur_cell = *++cells;
226                         if(cur_cell->x != x) break;
227                         area  += cur_cell->area;
228                         cover += cur_cell->cover;
229                     }
230 
231                     if(area)
232                     {
233                         alpha = calculate_alpha((cover << (poly_subpixel_shift + 1)) - area);
234                         if(alpha)
235                         {
236                             sl.add_cell(x, alpha);
237                         }
238                         x++;
239                     }
240 
241                     if(num_cells && cur_cell->x > x)
242                     {
243                         alpha = calculate_alpha(cover << (poly_subpixel_shift + 1));
244                         if(alpha)
245                         {
246                             sl.add_span(x, cur_cell->x - x, alpha);
247                         }
248                     }
249                 }
250 
251                 if(sl.num_spans()) break;
252                 ++m_scan_y;
253             }
254 
255             sl.finalize(m_scan_y);
256             ++m_scan_y;
257             return true;
258         }
259 
260         //--------------------------------------------------------------------
261         bool hit_test(int tx, int ty);
262 
263 
264     private:
265         //--------------------------------------------------------------------
266         // Disable copying
267         rasterizer_scanline_aa(const rasterizer_scanline_aa<Clip>&);
268         const rasterizer_scanline_aa<Clip>&
269         operator = (const rasterizer_scanline_aa<Clip>&);
270 
271     private:
272         rasterizer_cells_aa<cell_aa> m_outline;
273         clip_type      m_clipper;
274         int            m_gamma[aa_scale];
275         filling_rule_e m_filling_rule;
276         bool           m_auto_close;
277         coord_type     m_start_x;
278         coord_type     m_start_y;
279         unsigned       m_status;
280         int            m_scan_y;
281     };
282 
283 
284 
285 
286 
287 
288 
289 
290 
291 
292 
293 
294     //------------------------------------------------------------------------
295     template<class Clip>
reset()296     void rasterizer_scanline_aa<Clip>::reset()
297     {
298         m_outline.reset();
299         m_status = status_initial;
300     }
301 
302     //------------------------------------------------------------------------
303     template<class Clip>
filling_rule(filling_rule_e filling_rule)304     void rasterizer_scanline_aa<Clip>::filling_rule(filling_rule_e filling_rule)
305     {
306         m_filling_rule = filling_rule;
307     }
308 
309     //------------------------------------------------------------------------
310     template<class Clip>
clip_box(double x1,double y1,double x2,double y2)311     void rasterizer_scanline_aa<Clip>::clip_box(double x1, double y1,
312                                                 double x2, double y2)
313     {
314         reset();
315         m_clipper.clip_box(conv_type::upscale(x1), conv_type::upscale(y1),
316                            conv_type::upscale(x2), conv_type::upscale(y2));
317     }
318 
319     //------------------------------------------------------------------------
320     template<class Clip>
reset_clipping()321     void rasterizer_scanline_aa<Clip>::reset_clipping()
322     {
323         reset();
324         m_clipper.reset_clipping();
325     }
326 
327     //------------------------------------------------------------------------
328     template<class Clip>
close_polygon()329     void rasterizer_scanline_aa<Clip>::close_polygon()
330     {
331         if(m_status == status_line_to)
332         {
333             m_clipper.line_to(m_outline, m_start_x, m_start_y);
334             m_status = status_closed;
335         }
336     }
337 
338     //------------------------------------------------------------------------
339     template<class Clip>
move_to(int x,int y)340     void rasterizer_scanline_aa<Clip>::move_to(int x, int y)
341     {
342         if(m_outline.sorted()) reset();
343         if(m_auto_close) close_polygon();
344         m_clipper.move_to(m_start_x = conv_type::downscale(x),
345                           m_start_y = conv_type::downscale(y));
346         m_status = status_move_to;
347     }
348 
349     //------------------------------------------------------------------------
350     template<class Clip>
line_to(int x,int y)351     void rasterizer_scanline_aa<Clip>::line_to(int x, int y)
352     {
353         m_clipper.line_to(m_outline,
354                           conv_type::downscale(x),
355                           conv_type::downscale(y));
356         m_status = status_line_to;
357     }
358 
359     //------------------------------------------------------------------------
360     template<class Clip>
move_to_d(double x,double y)361     void rasterizer_scanline_aa<Clip>::move_to_d(double x, double y)
362     {
363         if(m_outline.sorted()) reset();
364         if(m_auto_close) close_polygon();
365         m_clipper.move_to(m_start_x = conv_type::upscale(x),
366                           m_start_y = conv_type::upscale(y));
367         m_status = status_move_to;
368     }
369 
370     //------------------------------------------------------------------------
371     template<class Clip>
line_to_d(double x,double y)372     void rasterizer_scanline_aa<Clip>::line_to_d(double x, double y)
373     {
374         m_clipper.line_to(m_outline,
375                           conv_type::upscale(x),
376                           conv_type::upscale(y));
377         m_status = status_line_to;
378     }
379 
380     //------------------------------------------------------------------------
381     template<class Clip>
add_vertex(double x,double y,unsigned cmd)382     void rasterizer_scanline_aa<Clip>::add_vertex(double x, double y, unsigned cmd)
383     {
384         if(is_move_to(cmd))
385         {
386             move_to_d(x, y);
387         }
388         else
389         if(is_vertex(cmd))
390         {
391             line_to_d(x, y);
392         }
393         else
394         if(is_close(cmd))
395         {
396             close_polygon();
397         }
398     }
399 
400     //------------------------------------------------------------------------
401     template<class Clip>
edge(int x1,int y1,int x2,int y2)402     void rasterizer_scanline_aa<Clip>::edge(int x1, int y1, int x2, int y2)
403     {
404         if(m_outline.sorted()) reset();
405         m_clipper.move_to(conv_type::downscale(x1), conv_type::downscale(y1));
406         m_clipper.line_to(m_outline,
407                           conv_type::downscale(x2),
408                           conv_type::downscale(y2));
409         m_status = status_move_to;
410     }
411 
412     //------------------------------------------------------------------------
413     template<class Clip>
edge_d(double x1,double y1,double x2,double y2)414     void rasterizer_scanline_aa<Clip>::edge_d(double x1, double y1,
415                                               double x2, double y2)
416     {
417         if(m_outline.sorted()) reset();
418         m_clipper.move_to(conv_type::upscale(x1), conv_type::upscale(y1));
419         m_clipper.line_to(m_outline,
420                           conv_type::upscale(x2),
421                           conv_type::upscale(y2));
422         m_status = status_move_to;
423     }
424 
425     //------------------------------------------------------------------------
426     template<class Clip>
sort()427     void rasterizer_scanline_aa<Clip>::sort()
428     {
429         if(m_auto_close) close_polygon();
430         m_outline.sort_cells();
431     }
432 
433     //------------------------------------------------------------------------
434     template<class Clip>
rewind_scanlines()435     AGG_INLINE bool rasterizer_scanline_aa<Clip>::rewind_scanlines()
436     {
437         if(m_auto_close) close_polygon();
438         m_outline.sort_cells();
439         if(m_outline.total_cells() == 0)
440         {
441             return false;
442         }
443         m_scan_y = m_outline.min_y();
444         return true;
445     }
446 
447 
448     //------------------------------------------------------------------------
449     template<class Clip>
navigate_scanline(int y)450     AGG_INLINE bool rasterizer_scanline_aa<Clip>::navigate_scanline(int y)
451     {
452         if(m_auto_close) close_polygon();
453         m_outline.sort_cells();
454         if(m_outline.total_cells() == 0 ||
455            y < m_outline.min_y() ||
456            y > m_outline.max_y())
457         {
458             return false;
459         }
460         m_scan_y = y;
461         return true;
462     }
463 
464     //------------------------------------------------------------------------
465     template<class Clip>
hit_test(int tx,int ty)466     bool rasterizer_scanline_aa<Clip>::hit_test(int tx, int ty)
467     {
468         if(!navigate_scanline(ty)) return false;
469         scanline_hit_test sl(tx);
470         sweep_scanline(sl);
471         return sl.hit();
472     }
473 
474 
475 
476 }
477 
478 
479 
480 #endif
481 
482