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