agg_rasterizer_scanline_aa.cpp revision 33357cad1fd1321a2b38d2963e2585f27ce980a2
1
2//----------------------------------------------------------------------------
3// XYQ: 2006-01-22 Copied from AGG project.
4// This file uses only integer data, so it's suitable for all platforms.
5//----------------------------------------------------------------------------
6//----------------------------------------------------------------------------
7// Anti-Grain Geometry - Version 2.3
8// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
9//
10// Permission to copy, use, modify, sell and distribute this software
11// is granted provided this copyright notice appears in all copies.
12// This software is provided "as is" without express or implied
13// warranty, and with no claim as to its suitability for any purpose.
14//
15//----------------------------------------------------------------------------
16//
17// The author gratefully acknowleges the support of David Turner,
18// Robert Wilhelm, and Werner Lemberg - the authors of the FreeType
19// libray - in producing this work. See http://www.freetype.org for details.
20//
21//----------------------------------------------------------------------------
22// Contact: mcseem@antigrain.com
23//          mcseemagg@yahoo.com
24//          http://www.antigrain.com
25//----------------------------------------------------------------------------
26//
27// Adaptation for 32-bit screen coordinates has been sponsored by
28// Liberty Technology Systems, Inc., visit http://lib-sys.com
29//
30// Liberty Technology Systems, Inc. is the provider of
31// PostScript and PDF technology for software developers.
32//
33//----------------------------------------------------------------------------
34//
35// Class outline_aa - implementation.
36//
37// Initially the rendering algorithm was designed by David Turner and the
38// other authors of the FreeType library - see the above notice. I nearly
39// created a similar renderer, but still I was far from David's work.
40// I completely redesigned the original code and adapted it for Anti-Grain
41// ideas. Two functions - render_line and render_hline are the core of
42// the algorithm - they calculate the exact coverage of each pixel cell
43// of the polygon. I left these functions almost as is, because there's
44// no way to improve the perfection - hats off to David and his group!
45//
46// All other code is very different from the original.
47//
48//----------------------------------------------------------------------------
49#include <limits.h>
50#include "agg_rasterizer_scanline_aa.h"
51#include "third_party/base/numerics/safe_math.h"
52namespace agg
53{
54AGG_INLINE void cell_aa::set_cover(int c, int a)
55{
56    cover = c;
57    area = a;
58}
59AGG_INLINE void cell_aa::add_cover(int c, int a)
60{
61    cover += c;
62    area += a;
63}
64AGG_INLINE void cell_aa::set_coord(int cx, int cy)
65{
66    x = cx;
67    y = cy;
68}
69AGG_INLINE void cell_aa::set(int cx, int cy, int c, int a)
70{
71    x = cx;
72    y = cy;
73    cover = c;
74    area = a;
75}
76outline_aa::~outline_aa()
77{
78    if(m_num_blocks) {
79        cell_aa** ptr = m_cells + m_num_blocks - 1;
80        while(m_num_blocks--) {
81            FX_Free(*ptr);
82            ptr--;
83        }
84        FX_Free(m_cells);
85    }
86}
87outline_aa::outline_aa() :
88    m_num_blocks(0),
89    m_max_blocks(0),
90    m_cur_block(0),
91    m_num_cells(0),
92    m_cells(0),
93    m_cur_cell_ptr(0),
94    m_cur_x(0),
95    m_cur_y(0),
96    m_min_x(0x7FFFFFFF),
97    m_min_y(0x7FFFFFFF),
98    m_max_x(-0x7FFFFFFF),
99    m_max_y(-0x7FFFFFFF),
100    m_sorted(false)
101{
102    m_cur_cell.set(0x7FFF, 0x7FFF, 0, 0);
103}
104void outline_aa::reset()
105{
106    m_num_cells = 0;
107    m_cur_block = 0;
108    m_cur_cell.set(0x7FFF, 0x7FFF, 0, 0);
109    m_sorted = false;
110    m_min_x =  0x7FFFFFFF;
111    m_min_y =  0x7FFFFFFF;
112    m_max_x = -0x7FFFFFFF;
113    m_max_y = -0x7FFFFFFF;
114}
115void outline_aa::allocate_block()
116{
117    if(m_cur_block >= m_num_blocks) {
118        if(m_num_blocks >= m_max_blocks) {
119            cell_aa** new_cells = FX_Alloc( cell_aa*, m_max_blocks + cell_block_pool);
120            if(m_cells) {
121                FXSYS_memcpy(new_cells, m_cells, m_max_blocks * sizeof(cell_aa*));
122                FX_Free(m_cells);
123            }
124            m_cells = new_cells;
125            m_max_blocks += cell_block_pool;
126        }
127        m_cells[m_num_blocks++] = FX_Alloc(cell_aa, cell_block_size);
128    }
129    m_cur_cell_ptr = m_cells[m_cur_block++];
130}
131AGG_INLINE void outline_aa::add_cur_cell()
132{
133    if(m_cur_cell.area | m_cur_cell.cover) {
134        if((m_num_cells & cell_block_mask) == 0) {
135            if(m_num_blocks >= cell_block_limit) {
136                return;
137            }
138            allocate_block();
139        }
140        *m_cur_cell_ptr++ = m_cur_cell;
141        ++m_num_cells;
142    }
143}
144AGG_INLINE void outline_aa::set_cur_cell(int x, int y)
145{
146    if(m_cur_cell.x != x || m_cur_cell.y != y) {
147        add_cur_cell();
148        m_cur_cell.set(x, y, 0, 0);
149        if(x < m_min_x) {
150            m_min_x = x;
151        }
152        if(x > m_max_x) {
153            m_max_x = x;
154        }
155        if(y < m_min_y) {
156            m_min_y = y;
157        }
158        if(y > m_max_y) {
159            m_max_y = y;
160        }
161    }
162}
163AGG_INLINE void outline_aa::render_hline(int ey, int x1, int y1, int x2, int y2)
164{
165    int ex1 = x1 >> poly_base_shift;
166    int ex2 = x2 >> poly_base_shift;
167    int fx1 = x1 & poly_base_mask;
168    int fx2 = x2 & poly_base_mask;
169    int delta, p, first, dx;
170    int incr, lift, mod, rem;
171    if(y1 == y2) {
172        set_cur_cell(ex2, ey);
173        return;
174    }
175    if(ex1 == ex2) {
176        delta = y2 - y1;
177        m_cur_cell.add_cover(delta, (fx1 + fx2) * delta);
178        return;
179    }
180    p     = (poly_base_size - fx1) * (y2 - y1);
181    first = poly_base_size;
182    incr  = 1;
183    dx = x2 - x1;
184    if(dx < 0) {
185        p     = fx1 * (y2 - y1);
186        first = 0;
187        incr  = -1;
188        dx    = -dx;
189    }
190    delta = p / dx;
191    mod   = p % dx;
192    if(mod < 0) {
193        delta--;
194        mod += dx;
195    }
196    m_cur_cell.add_cover(delta, (fx1 + first) * delta);
197    ex1 += incr;
198    set_cur_cell(ex1, ey);
199    y1  += delta;
200    if(ex1 != ex2) {
201        p     = poly_base_size * (y2 - y1 + delta);
202        lift  = p / dx;
203        rem   = p % dx;
204        if (rem < 0) {
205            lift--;
206            rem += dx;
207        }
208        mod -= dx;
209        while (ex1 != ex2) {
210            delta = lift;
211            mod  += rem;
212            if(mod >= 0) {
213                mod -= dx;
214                delta++;
215            }
216            m_cur_cell.add_cover(delta, (poly_base_size) * delta);
217            y1  += delta;
218            ex1 += incr;
219            set_cur_cell(ex1, ey);
220        }
221    }
222    delta = y2 - y1;
223    m_cur_cell.add_cover(delta, (fx2 + poly_base_size - first) * delta);
224}
225void outline_aa::render_line(int x1, int y1, int x2, int y2)
226{
227    enum dx_limit_e { dx_limit = 16384 << poly_base_shift };
228    int dx = x2 - x1;
229    if(dx >= dx_limit || dx <= -dx_limit) {
230        int cx = (x1 + x2) >> 1;
231        int cy = (y1 + y2) >> 1;
232        render_line(x1, y1, cx, cy);
233        render_line(cx, cy, x2, y2);
234    }
235    int dy = y2 - y1;
236    int ey1 = y1 >> poly_base_shift;
237    int ey2 = y2 >> poly_base_shift;
238    int fy1 = y1 & poly_base_mask;
239    int fy2 = y2 & poly_base_mask;
240    int x_from, x_to;
241    int rem, mod, lift, delta, first, incr;
242    if(ey1 == ey2) {
243        render_hline(ey1, x1, fy1, x2, fy2);
244        return;
245    }
246    incr  = 1;
247    if(dx == 0) {
248        int ex = x1 >> poly_base_shift;
249        int two_fx = (x1 - (ex << poly_base_shift)) << 1;
250        int area;
251        first = poly_base_size;
252        if(dy < 0) {
253            first = 0;
254            incr  = -1;
255        }
256        x_from = x1;
257        delta = first - fy1;
258        m_cur_cell.add_cover(delta, two_fx * delta);
259        ey1 += incr;
260        set_cur_cell(ex, ey1);
261        delta = first + first - poly_base_size;
262        area = two_fx * delta;
263        while(ey1 != ey2) {
264            m_cur_cell.set_cover(delta, area);
265            ey1 += incr;
266            set_cur_cell(ex, ey1);
267        }
268        delta = fy2 - poly_base_size + first;
269        m_cur_cell.add_cover(delta, two_fx * delta);
270        return;
271    }
272    pdfium::base::CheckedNumeric<int> safeP = poly_base_size - fy1;
273    safeP *= dx;
274    if (!safeP.IsValid())
275      return;
276    first = poly_base_size;
277    if(dy < 0) {
278      safeP = fy1;
279      safeP *= dx;
280      if (!safeP.IsValid())
281        return;
282      first = 0;
283      incr = -1;
284      dy = -dy;
285    }
286    delta = (safeP / dy).ValueOrDie();
287    mod = (safeP % dy).ValueOrDie();
288    if(mod < 0) {
289        delta--;
290        mod += dy;
291    }
292    x_from = x1 + delta;
293    render_hline(ey1, x1, fy1, x_from, first);
294    ey1 += incr;
295    set_cur_cell(x_from >> poly_base_shift, ey1);
296    if(ey1 != ey2) {
297      safeP = static_cast<int>(poly_base_size);
298      safeP *= dx;
299      if (!safeP.IsValid())
300        return;
301      lift = (safeP / dy).ValueOrDie();
302      rem = (safeP % dy).ValueOrDie();
303      if (rem < 0) {
304        lift--;
305        rem += dy;
306        }
307        mod -= dy;
308        while(ey1 != ey2) {
309            delta = lift;
310            mod  += rem;
311            if (mod >= 0) {
312                mod -= dy;
313                delta++;
314            }
315            x_to = x_from + delta;
316            render_hline(ey1, x_from, poly_base_size - first, x_to, first);
317            x_from = x_to;
318            ey1 += incr;
319            set_cur_cell(x_from >> poly_base_shift, ey1);
320        }
321    }
322    render_hline(ey1, x_from, poly_base_size - first, x2, fy2);
323}
324void outline_aa::move_to(int x, int y)
325{
326    if(m_sorted) {
327        reset();
328    }
329    set_cur_cell(x >> poly_base_shift, y >> poly_base_shift);
330    m_cur_x = x;
331    m_cur_y = y;
332}
333void outline_aa::line_to(int x, int y)
334{
335    render_line(m_cur_x, m_cur_y, x, y);
336    m_cur_x = x;
337    m_cur_y = y;
338    m_sorted = false;
339}
340template <class T> static AGG_INLINE void swap_cells(T* a, T* b)
341{
342    T temp = *a;
343    *a = *b;
344    *b = temp;
345}
346enum {
347    qsort_threshold = 9
348};
349static void qsort_cells(cell_aa** start, unsigned num)
350{
351    cell_aa**  stack[80];
352    cell_aa*** top;
353    cell_aa**  limit;
354    cell_aa**  base;
355    limit = start + num;
356    base  = start;
357    top   = stack;
358    for (;;) {
359        int len = int(limit - base);
360        cell_aa** i;
361        cell_aa** j;
362        cell_aa** pivot;
363        if(len > qsort_threshold) {
364            pivot = base + len / 2;
365            swap_cells(base, pivot);
366            i = base + 1;
367            j = limit - 1;
368            if((*j)->x < (*i)->x) {
369                swap_cells(i, j);
370            }
371            if((*base)->x < (*i)->x) {
372                swap_cells(base, i);
373            }
374            if((*j)->x < (*base)->x) {
375                swap_cells(base, j);
376            }
377            for(;;) {
378                int x = (*base)->x;
379                do {
380                    i++;
381                } while( (*i)->x < x );
382                do {
383                    j--;
384                } while( x < (*j)->x );
385                if(i > j) {
386                    break;
387                }
388                swap_cells(i, j);
389            }
390            swap_cells(base, j);
391            if(j - base > limit - i) {
392                top[0] = base;
393                top[1] = j;
394                base   = i;
395            } else {
396                top[0] = i;
397                top[1] = limit;
398                limit  = j;
399            }
400            top += 2;
401        } else {
402            j = base;
403            i = j + 1;
404            for(; i < limit; j = i, i++) {
405                for(; j[1]->x < (*j)->x; j--) {
406                    swap_cells(j + 1, j);
407                    if (j == base) {
408                        break;
409                    }
410                }
411            }
412            if(top > stack) {
413                top  -= 2;
414                base  = top[0];
415                limit = top[1];
416            } else {
417                break;
418            }
419        }
420    }
421}
422void outline_aa::sort_cells()
423{
424    if(m_sorted) {
425        return;
426    }
427    add_cur_cell();
428    if(m_num_cells == 0) {
429        return;
430    }
431    m_sorted_cells.allocate(m_num_cells, 16);
432    if (m_max_y > 0 && m_min_y < 0 && -m_min_y > INT_MAX - m_max_y) {
433        return;
434    }
435    unsigned size = m_max_y - m_min_y;
436    if (size + 1 < size) {
437        return;
438    }
439    size++;
440    m_sorted_y.allocate(size, 16);
441    m_sorted_y.zero();
442    cell_aa** block_ptr = m_cells;
443    cell_aa*  cell_ptr = NULL;
444    unsigned nb = m_num_cells >> cell_block_shift;
445    unsigned i;
446    while(nb--) {
447        cell_ptr = *block_ptr++;
448        i = cell_block_size;
449        while(i--) {
450            m_sorted_y[cell_ptr->y - m_min_y].start++;
451            ++cell_ptr;
452        }
453    }
454    i = m_num_cells & cell_block_mask;
455    if (i) {
456        cell_ptr = *block_ptr++;
457    }
458    while(i--) {
459        m_sorted_y[cell_ptr->y - m_min_y].start++;
460        ++cell_ptr;
461    }
462    unsigned start = 0;
463    for(i = 0; i < m_sorted_y.size(); i++) {
464        unsigned v = m_sorted_y[i].start;
465        m_sorted_y[i].start = start;
466        start += v;
467    }
468    block_ptr = m_cells;
469    nb = m_num_cells >> cell_block_shift;
470    while(nb--) {
471        cell_ptr = *block_ptr++;
472        i = cell_block_size;
473        while(i--) {
474            sorted_y& cur_y = m_sorted_y[cell_ptr->y - m_min_y];
475            m_sorted_cells[cur_y.start + cur_y.num] = cell_ptr;
476            ++cur_y.num;
477            ++cell_ptr;
478        }
479    }
480    i = m_num_cells & cell_block_mask;
481    if (i) {
482        cell_ptr = *block_ptr++;
483    }
484    while(i--) {
485        sorted_y& cur_y = m_sorted_y[cell_ptr->y - m_min_y];
486        m_sorted_cells[cur_y.start + cur_y.num] = cell_ptr;
487        ++cur_y.num;
488        ++cell_ptr;
489    }
490    for(i = 0; i < m_sorted_y.size(); i++) {
491        const sorted_y& cur_y = m_sorted_y[i];
492        if(cur_y.num) {
493            qsort_cells(m_sorted_cells.data() + cur_y.start, cur_y.num);
494        }
495    }
496    m_sorted = true;
497}
498}
499