1
2//----------------------------------------------------------------------------
3// Anti-Grain Geometry - Version 2.3
4// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
5//
6// Permission to copy, use, modify, sell and distribute this software
7// is granted provided this copyright notice appears in all copies.
8// This software is provided "as is" without express or implied
9// warranty, and with no claim as to its suitability for any purpose.
10//
11//----------------------------------------------------------------------------
12//
13// The author gratefully acknowleges the support of David Turner,
14// Robert Wilhelm, and Werner Lemberg - the authors of the FreeType
15// libray - in producing this work. See http://www.freetype.org for details.
16//
17//----------------------------------------------------------------------------
18// Contact: mcseem@antigrain.com
19//          mcseemagg@yahoo.com
20//          http://www.antigrain.com
21//----------------------------------------------------------------------------
22//
23// Adaptation for 32-bit screen coordinates has been sponsored by
24// Liberty Technology Systems, Inc., visit http://lib-sys.com
25//
26// Liberty Technology Systems, Inc. is the provider of
27// PostScript and PDF technology for software developers.
28//
29//----------------------------------------------------------------------------
30#ifndef AGG_RASTERIZER_SCANLINE_AA_INCLUDED
31#define AGG_RASTERIZER_SCANLINE_AA_INCLUDED
32
33#include "agg_array.h"
34#include "agg_basics.h"
35#include "agg_clip_liang_barsky.h"
36#include "agg_math.h"
37#include "agg_render_scanlines.h"
38#include "core/fxcrt/fx_coordinates.h"
39#include "core/fxcrt/fx_memory.h"
40
41namespace agg
42{
43enum poly_base_scale_e {
44    poly_base_shift = 8,
45    poly_base_size  = 1 << poly_base_shift,
46    poly_base_mask  = poly_base_size - 1
47};
48inline int poly_coord(float c)
49{
50    return int(c * poly_base_size);
51}
52struct cell_aa  {
53    int x;
54    int y;
55    int cover;
56    int area;
57    void set(int x, int y, int c, int a);
58    void set_coord(int x, int y);
59    void set_cover(int c, int a);
60    void add_cover(int c, int a);
61};
62class outline_aa
63{
64    enum cell_block_scale_e {
65        cell_block_shift = 12,
66        cell_block_size  = 1 << cell_block_shift,
67        cell_block_mask  = cell_block_size - 1,
68        cell_block_pool  = 256,
69        cell_block_limit = 1024
70    };
71    struct sorted_y  {
72        unsigned start;
73        unsigned num;
74    };
75public:
76    ~outline_aa();
77    outline_aa();
78    void reset();
79    void move_to(int x, int y);
80    void line_to(int x, int y);
81    int min_x() const
82    {
83        return m_min_x;
84    }
85    int min_y() const
86    {
87        return m_min_y;
88    }
89    int max_x() const
90    {
91        return m_max_x;
92    }
93    int max_y() const
94    {
95        return m_max_y;
96    }
97    void sort_cells();
98    unsigned total_cells() const
99    {
100        return m_num_cells;
101    }
102    unsigned scanline_num_cells(unsigned y) const
103    {
104        return m_sorted_y[y - m_min_y].num;
105    }
106    const cell_aa* const* scanline_cells(unsigned y) const
107    {
108        return m_sorted_cells.data() + m_sorted_y[y - m_min_y].start;
109    }
110    bool sorted() const
111    {
112        return m_sorted;
113    }
114private:
115    outline_aa(const outline_aa&);
116    const outline_aa& operator = (const outline_aa&);
117    void set_cur_cell(int x, int y);
118    void add_cur_cell();
119    void render_hline(int ey, int x1, int y1, int x2, int y2);
120    void render_line(int x1, int y1, int x2, int y2);
121    void allocate_block();
122private:
123    unsigned  m_num_blocks;
124    unsigned  m_max_blocks;
125    unsigned  m_cur_block;
126    unsigned  m_num_cells;
127    cell_aa** m_cells;
128    cell_aa*  m_cur_cell_ptr;
129    pod_array<cell_aa*> m_sorted_cells;
130    pod_array<sorted_y> m_sorted_y;
131    cell_aa   m_cur_cell;
132    int       m_cur_x;
133    int       m_cur_y;
134    int       m_min_x;
135    int       m_min_y;
136    int       m_max_x;
137    int       m_max_y;
138    bool      m_sorted;
139};
140class scanline_hit_test
141{
142public:
143    scanline_hit_test(int x) : m_x(x), m_hit(false) {}
144    void reset_spans() {}
145    void finalize(int) {}
146    void add_cell(int x, int)
147    {
148        if(m_x == x) {
149            m_hit = true;
150        }
151    }
152    void add_span(int x, int len, int)
153    {
154        if(m_x >= x && m_x < x + len) {
155            m_hit = true;
156        }
157    }
158    unsigned num_spans() const
159    {
160        return 1;
161    }
162    bool hit() const
163    {
164        return m_hit;
165    }
166private:
167    int  m_x;
168    bool m_hit;
169};
170enum filling_rule_e {
171    fill_non_zero,
172    fill_even_odd
173};
174class rasterizer_scanline_aa
175{
176    enum status {
177        status_initial,
178        status_line_to,
179        status_closed
180    };
181public:
182    enum aa_scale_e {
183        aa_num   = 1 << 8,
184        aa_mask  = aa_num - 1,
185        aa_2num  = aa_num * 2,
186        aa_2mask = aa_2num - 1
187    };
188    rasterizer_scanline_aa() :
189        m_filling_rule(fill_non_zero),
190        m_clipped_start_x(0),
191        m_clipped_start_y(0),
192        m_status(status_initial),
193        m_clipping(false)
194    {
195    }
196    ~rasterizer_scanline_aa() {}
197    void filling_rule(filling_rule_e filling_rule)
198    {
199        m_filling_rule = filling_rule;
200    }
201    int min_x() const
202    {
203        return m_outline.min_x();
204    }
205    int min_y() const
206    {
207        return m_outline.min_y();
208    }
209    int max_x() const
210    {
211        return m_outline.max_x();
212    }
213    int max_y() const
214    {
215        return m_outline.max_y();
216    }
217    void reset()
218    {
219        m_outline.reset();
220        m_status = status_initial;
221    }
222    void clip_box(float x1, float y1, float x2, float y2)
223    {
224        m_clip_box = rect(poly_coord(x1), poly_coord(y1),
225                          poly_coord(x2), poly_coord(y2));
226        m_clip_box.normalize();
227        m_clipping = true;
228    }
229    void add_vertex(float x, float y, unsigned cmd)
230    {
231        if(is_close(cmd)) {
232            close_polygon();
233        } else {
234            if(is_move_to(cmd)) {
235                move_to(poly_coord(x), poly_coord(y));
236            } else {
237                if(is_vertex(cmd)) {
238                    line_to(poly_coord(x), poly_coord(y));
239                }
240            }
241        }
242    }
243    void move_to(int x, int y)
244    {
245        if(m_clipping) {
246            if(m_outline.sorted()) {
247                reset();
248            }
249            if(m_status == status_line_to) {
250                close_polygon();
251            }
252            m_prev_x = m_start_x = x;
253            m_prev_y = m_start_y = y;
254            m_status = status_initial;
255            m_prev_flags = clipping_flags(x, y, m_clip_box);
256            if(m_prev_flags == 0) {
257                move_to_no_clip(x, y);
258            }
259        } else {
260            move_to_no_clip(x, y);
261        }
262    }
263    void line_to(int x, int y)
264    {
265        if(m_clipping) {
266            clip_segment(x, y);
267        } else {
268            line_to_no_clip(x, y);
269        }
270    }
271    void close_polygon()
272    {
273        if (m_status != status_line_to) {
274            return;
275        }
276        if(m_clipping) {
277            clip_segment(m_start_x, m_start_y);
278        }
279        close_polygon_no_clip();
280    }
281    AGG_INLINE unsigned calculate_alpha(int area, bool no_smooth) const
282    {
283        int cover = area >> (poly_base_shift * 2 + 1 - 8);
284        if(cover < 0) {
285            cover = -cover;
286        }
287        if(m_filling_rule == fill_even_odd) {
288            cover &= aa_2mask;
289            if(cover > aa_num) {
290                cover = aa_2num - cover;
291            }
292        }
293        if (no_smooth) {
294            cover = cover > aa_mask / 2 ? aa_mask : 0;
295        }
296        if(cover > aa_mask) {
297            cover = aa_mask;
298        }
299        return cover;
300    }
301    AGG_INLINE void sort()
302    {
303        m_outline.sort_cells();
304    }
305    AGG_INLINE bool rewind_scanlines()
306    {
307        close_polygon();
308        m_outline.sort_cells();
309        if(m_outline.total_cells() == 0) {
310            return false;
311        }
312        m_cur_y = m_outline.min_y();
313        return true;
314    }
315    AGG_INLINE bool navigate_scanline(int y)
316    {
317        close_polygon();
318        m_outline.sort_cells();
319        if(m_outline.total_cells() == 0 ||
320                y < m_outline.min_y() ||
321                y > m_outline.max_y()) {
322            return false;
323        }
324        m_cur_y = y;
325        return true;
326    }
327    template<class Scanline> bool sweep_scanline(Scanline& sl, bool no_smooth)
328    {
329        for(;;) {
330            if(m_cur_y > m_outline.max_y()) {
331                return false;
332            }
333            sl.reset_spans();
334            unsigned num_cells = m_outline.scanline_num_cells(m_cur_y);
335            const cell_aa* const* cells = m_outline.scanline_cells(m_cur_y);
336            int cover = 0;
337            while(num_cells) {
338                const cell_aa* cur_cell = *cells;
339                int x    = cur_cell->x;
340                int area = cur_cell->area;
341                unsigned alpha;
342                cover += cur_cell->cover;
343                while(--num_cells) {
344                    cur_cell = *++cells;
345                    if(cur_cell->x != x) {
346                        break;
347                    }
348                    area  += cur_cell->area;
349                    cover += cur_cell->cover;
350                }
351                if(area) {
352                    alpha = calculate_alpha((cover << (poly_base_shift + 1)) - area, no_smooth);
353                    if(alpha) {
354                        sl.add_cell(x, alpha);
355                    }
356                    x++;
357                }
358                if(num_cells && cur_cell->x > x) {
359                    alpha = calculate_alpha(cover << (poly_base_shift + 1), no_smooth);
360                    if(alpha) {
361                        sl.add_span(x, cur_cell->x - x, alpha);
362                    }
363                }
364            }
365            if(sl.num_spans()) {
366                break;
367            }
368            ++m_cur_y;
369        }
370        sl.finalize(m_cur_y);
371        ++m_cur_y;
372        return true;
373    }
374    template<class VertexSource>
375    void add_path(VertexSource& vs, unsigned path_id = 0)
376    {
377        float x;
378        float y;
379        unsigned cmd;
380        vs.rewind(path_id);
381        while(!is_stop(cmd = vs.vertex(&x, &y))) {
382            add_vertex(x, y, cmd);
383        }
384    }
385    template<class VertexSource>
386    void add_path_transformed(VertexSource& vs, const CFX_Matrix* pMatrix, unsigned path_id = 0)
387    {
388        float x;
389        float y;
390        unsigned cmd;
391        vs.rewind(path_id);
392        while(!is_stop(cmd = vs.vertex(&x, &y))) {
393          if (pMatrix) {
394            CFX_PointF ret = pMatrix->Transform(CFX_PointF(x, y));
395            x = ret.x;
396            y = ret.y;
397          }
398          add_vertex(x, y, cmd);
399        }
400    }
401private:
402    rasterizer_scanline_aa(const rasterizer_scanline_aa&);
403    const rasterizer_scanline_aa&
404    operator = (const rasterizer_scanline_aa&);
405    void move_to_no_clip(int x, int y)
406    {
407        if(m_status == status_line_to) {
408            close_polygon_no_clip();
409        }
410        m_outline.move_to(x * 1, y);
411        m_clipped_start_x = x;
412        m_clipped_start_y = y;
413        m_status = status_line_to;
414    }
415    void line_to_no_clip(int x, int y)
416    {
417        if(m_status != status_initial) {
418            m_outline.line_to(x * 1, y);
419            m_status = status_line_to;
420        }
421    }
422    void close_polygon_no_clip()
423    {
424        if(m_status == status_line_to) {
425            m_outline.line_to(m_clipped_start_x * 1, m_clipped_start_y);
426            m_status = status_closed;
427        }
428    }
429    void clip_segment(int x, int y)
430    {
431        unsigned flags = clipping_flags(x, y, m_clip_box);
432        if(m_prev_flags == flags) {
433            if(flags == 0) {
434                if(m_status == status_initial) {
435                    move_to_no_clip(x, y);
436                } else {
437                    line_to_no_clip(x, y);
438                }
439            }
440        } else {
441            int cx[4];
442            int cy[4];
443            unsigned n = clip_liang_barsky(m_prev_x, m_prev_y,
444                                           x, y,
445                                           m_clip_box,
446                                           cx, cy);
447            const int* px = cx;
448            const int* py = cy;
449            while(n--) {
450                if(m_status == status_initial) {
451                    move_to_no_clip(*px++, *py++);
452                } else {
453                    line_to_no_clip(*px++, *py++);
454                }
455            }
456        }
457        m_prev_flags = flags;
458        m_prev_x = x;
459        m_prev_y = y;
460    }
461private:
462    outline_aa     m_outline;
463    filling_rule_e m_filling_rule;
464    int            m_clipped_start_x;
465    int            m_clipped_start_y;
466    int            m_start_x;
467    int            m_start_y;
468    int            m_prev_x;
469    int            m_prev_y;
470    unsigned       m_prev_flags;
471    unsigned       m_status;
472    rect           m_clip_box;
473    bool           m_clipping;
474    int            m_cur_y;
475};
476}
477#endif
478