| //---------------------------------------------------------------------------- | |
| // XYQ: 2006-01-22 Copied from AGG project. | |
| // This file uses only integer data, so it's suitable for all platforms. | |
| //---------------------------------------------------------------------------- | |
| //---------------------------------------------------------------------------- | |
| // Anti-Grain Geometry - Version 2.3 | |
| // Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) | |
| // | |
| // Permission to copy, use, modify, sell and distribute this software | |
| // is granted provided this copyright notice appears in all copies. | |
| // This software is provided "as is" without express or implied | |
| // warranty, and with no claim as to its suitability for any purpose. | |
| // | |
| //---------------------------------------------------------------------------- | |
| // | |
| // The author gratefully acknowleges the support of David Turner, | |
| // Robert Wilhelm, and Werner Lemberg - the authors of the FreeType | |
| // libray - in producing this work. See http://www.freetype.org for details. | |
| // | |
| //---------------------------------------------------------------------------- | |
| // Contact: mcseem@antigrain.com | |
| // mcseemagg@yahoo.com | |
| // http://www.antigrain.com | |
| //---------------------------------------------------------------------------- | |
| // | |
| // Adaptation for 32-bit screen coordinates has been sponsored by | |
| // Liberty Technology Systems, Inc., visit http://lib-sys.com | |
| // | |
| // Liberty Technology Systems, Inc. is the provider of | |
| // PostScript and PDF technology for software developers. | |
| // | |
| //---------------------------------------------------------------------------- | |
| // | |
| // Class outline_aa - implementation. | |
| // | |
| // Initially the rendering algorithm was designed by David Turner and the | |
| // other authors of the FreeType library - see the above notice. I nearly | |
| // created a similar renderer, but still I was far from David's work. | |
| // I completely redesigned the original code and adapted it for Anti-Grain | |
| // ideas. Two functions - render_line and render_hline are the core of | |
| // the algorithm - they calculate the exact coverage of each pixel cell | |
| // of the polygon. I left these functions almost as is, because there's | |
| // no way to improve the perfection - hats off to David and his group! | |
| // | |
| // All other code is very different from the original. | |
| // | |
| //---------------------------------------------------------------------------- | |
| #include <limits.h> | |
| #include "agg_rasterizer_scanline_aa.h" | |
| #include "third_party/base/numerics/safe_math.h" | |
| namespace agg | |
| { | |
| AGG_INLINE void cell_aa::set_cover(int c, int a) | |
| { | |
| cover = c; | |
| area = a; | |
| } | |
| AGG_INLINE void cell_aa::add_cover(int c, int a) | |
| { | |
| cover += c; | |
| area += a; | |
| } | |
| AGG_INLINE void cell_aa::set_coord(int cx, int cy) | |
| { | |
| x = cx; | |
| y = cy; | |
| } | |
| AGG_INLINE void cell_aa::set(int cx, int cy, int c, int a) | |
| { | |
| x = cx; | |
| y = cy; | |
| cover = c; | |
| area = a; | |
| } | |
| outline_aa::~outline_aa() | |
| { | |
| if(m_num_blocks) { | |
| cell_aa** ptr = m_cells + m_num_blocks - 1; | |
| while(m_num_blocks--) { | |
| FX_Free(*ptr); | |
| ptr--; | |
| } | |
| FX_Free(m_cells); | |
| } | |
| } | |
| outline_aa::outline_aa() : | |
| m_num_blocks(0), | |
| m_max_blocks(0), | |
| m_cur_block(0), | |
| m_num_cells(0), | |
| m_cells(0), | |
| m_cur_cell_ptr(0), | |
| m_cur_x(0), | |
| m_cur_y(0), | |
| m_min_x(0x7FFFFFFF), | |
| m_min_y(0x7FFFFFFF), | |
| m_max_x(-0x7FFFFFFF), | |
| m_max_y(-0x7FFFFFFF), | |
| m_sorted(false) | |
| { | |
| m_cur_cell.set(0x7FFF, 0x7FFF, 0, 0); | |
| } | |
| void outline_aa::reset() | |
| { | |
| m_num_cells = 0; | |
| m_cur_block = 0; | |
| m_cur_cell.set(0x7FFF, 0x7FFF, 0, 0); | |
| m_sorted = false; | |
| m_min_x = 0x7FFFFFFF; | |
| m_min_y = 0x7FFFFFFF; | |
| m_max_x = -0x7FFFFFFF; | |
| m_max_y = -0x7FFFFFFF; | |
| } | |
| void outline_aa::allocate_block() | |
| { | |
| if(m_cur_block >= m_num_blocks) { | |
| if(m_num_blocks >= m_max_blocks) { | |
| cell_aa** new_cells = FX_Alloc( cell_aa*, m_max_blocks + cell_block_pool); | |
| if(m_cells) { | |
| memcpy(new_cells, m_cells, m_max_blocks * sizeof(cell_aa*)); | |
| FX_Free(m_cells); | |
| } | |
| m_cells = new_cells; | |
| m_max_blocks += cell_block_pool; | |
| } | |
| m_cells[m_num_blocks++] = FX_Alloc(cell_aa, cell_block_size); | |
| } | |
| m_cur_cell_ptr = m_cells[m_cur_block++]; | |
| } | |
| AGG_INLINE void outline_aa::add_cur_cell() | |
| { | |
| if(m_cur_cell.area | m_cur_cell.cover) { | |
| if((m_num_cells & cell_block_mask) == 0) { | |
| if(m_num_blocks >= cell_block_limit) { | |
| return; | |
| } | |
| allocate_block(); | |
| } | |
| *m_cur_cell_ptr++ = m_cur_cell; | |
| ++m_num_cells; | |
| } | |
| } | |
| AGG_INLINE void outline_aa::set_cur_cell(int x, int y) | |
| { | |
| if(m_cur_cell.x != x || m_cur_cell.y != y) { | |
| add_cur_cell(); | |
| m_cur_cell.set(x, y, 0, 0); | |
| if(x < m_min_x) { | |
| m_min_x = x; | |
| } | |
| if(x > m_max_x) { | |
| m_max_x = x; | |
| } | |
| if(y < m_min_y) { | |
| m_min_y = y; | |
| } | |
| if(y > m_max_y) { | |
| m_max_y = y; | |
| } | |
| } | |
| } | |
| AGG_INLINE void outline_aa::render_hline(int ey, int x1, int y1, int x2, int y2) | |
| { | |
| int ex1 = x1 >> poly_base_shift; | |
| int ex2 = x2 >> poly_base_shift; | |
| int fx1 = x1 & poly_base_mask; | |
| int fx2 = x2 & poly_base_mask; | |
| int delta, p, first, dx; | |
| int incr, lift, mod, rem; | |
| if(y1 == y2) { | |
| set_cur_cell(ex2, ey); | |
| return; | |
| } | |
| if(ex1 == ex2) { | |
| delta = y2 - y1; | |
| m_cur_cell.add_cover(delta, (fx1 + fx2) * delta); | |
| return; | |
| } | |
| p = (poly_base_size - fx1) * (y2 - y1); | |
| first = poly_base_size; | |
| incr = 1; | |
| dx = x2 - x1; | |
| if(dx < 0) { | |
| p = fx1 * (y2 - y1); | |
| first = 0; | |
| incr = -1; | |
| dx = -dx; | |
| } | |
| delta = p / dx; | |
| mod = p % dx; | |
| if(mod < 0) { | |
| delta--; | |
| mod += dx; | |
| } | |
| m_cur_cell.add_cover(delta, (fx1 + first) * delta); | |
| ex1 += incr; | |
| set_cur_cell(ex1, ey); | |
| y1 += delta; | |
| if(ex1 != ex2) { | |
| p = poly_base_size * (y2 - y1 + delta); | |
| lift = p / dx; | |
| rem = p % dx; | |
| if (rem < 0) { | |
| lift--; | |
| rem += dx; | |
| } | |
| mod -= dx; | |
| while (ex1 != ex2) { | |
| delta = lift; | |
| mod += rem; | |
| if(mod >= 0) { | |
| mod -= dx; | |
| delta++; | |
| } | |
| m_cur_cell.add_cover(delta, (poly_base_size) * delta); | |
| y1 += delta; | |
| ex1 += incr; | |
| set_cur_cell(ex1, ey); | |
| } | |
| } | |
| delta = y2 - y1; | |
| m_cur_cell.add_cover(delta, (fx2 + poly_base_size - first) * delta); | |
| } | |
| void outline_aa::render_line(int x1, int y1, int x2, int y2) | |
| { | |
| enum dx_limit_e { dx_limit = 16384 << poly_base_shift }; | |
| int dx = x2 - x1; | |
| if(dx >= dx_limit || dx <= -dx_limit) { | |
| int cx = (x1 + x2) >> 1; | |
| int cy = (y1 + y2) >> 1; | |
| render_line(x1, y1, cx, cy); | |
| render_line(cx, cy, x2, y2); | |
| } | |
| int dy = y2 - y1; | |
| int ey1 = y1 >> poly_base_shift; | |
| int ey2 = y2 >> poly_base_shift; | |
| int fy1 = y1 & poly_base_mask; | |
| int fy2 = y2 & poly_base_mask; | |
| int x_from, x_to; | |
| int rem, mod, lift, delta, first, incr; | |
| if(ey1 == ey2) { | |
| render_hline(ey1, x1, fy1, x2, fy2); | |
| return; | |
| } | |
| incr = 1; | |
| if(dx == 0) { | |
| int ex = x1 >> poly_base_shift; | |
| int two_fx = (x1 - (ex << poly_base_shift)) << 1; | |
| int area; | |
| first = poly_base_size; | |
| if(dy < 0) { | |
| first = 0; | |
| incr = -1; | |
| } | |
| x_from = x1; | |
| delta = first - fy1; | |
| m_cur_cell.add_cover(delta, two_fx * delta); | |
| ey1 += incr; | |
| set_cur_cell(ex, ey1); | |
| delta = first + first - poly_base_size; | |
| area = two_fx * delta; | |
| while(ey1 != ey2) { | |
| m_cur_cell.set_cover(delta, area); | |
| ey1 += incr; | |
| set_cur_cell(ex, ey1); | |
| } | |
| delta = fy2 - poly_base_size + first; | |
| m_cur_cell.add_cover(delta, two_fx * delta); | |
| return; | |
| } | |
| pdfium::base::CheckedNumeric<int> safeP = poly_base_size - fy1; | |
| safeP *= dx; | |
| if (!safeP.IsValid()) | |
| return; | |
| first = poly_base_size; | |
| if(dy < 0) { | |
| safeP = fy1; | |
| safeP *= dx; | |
| if (!safeP.IsValid()) | |
| return; | |
| first = 0; | |
| incr = -1; | |
| dy = -dy; | |
| } | |
| delta = (safeP / dy).ValueOrDie(); | |
| mod = (safeP % dy).ValueOrDie(); | |
| if(mod < 0) { | |
| delta--; | |
| mod += dy; | |
| } | |
| x_from = x1 + delta; | |
| render_hline(ey1, x1, fy1, x_from, first); | |
| ey1 += incr; | |
| set_cur_cell(x_from >> poly_base_shift, ey1); | |
| if(ey1 != ey2) { | |
| safeP = static_cast<int>(poly_base_size); | |
| safeP *= dx; | |
| if (!safeP.IsValid()) | |
| return; | |
| lift = (safeP / dy).ValueOrDie(); | |
| rem = (safeP % dy).ValueOrDie(); | |
| if (rem < 0) { | |
| lift--; | |
| rem += dy; | |
| } | |
| mod -= dy; | |
| while(ey1 != ey2) { | |
| delta = lift; | |
| mod += rem; | |
| if (mod >= 0) { | |
| mod -= dy; | |
| delta++; | |
| } | |
| x_to = x_from + delta; | |
| render_hline(ey1, x_from, poly_base_size - first, x_to, first); | |
| x_from = x_to; | |
| ey1 += incr; | |
| set_cur_cell(x_from >> poly_base_shift, ey1); | |
| } | |
| } | |
| render_hline(ey1, x_from, poly_base_size - first, x2, fy2); | |
| } | |
| void outline_aa::move_to(int x, int y) | |
| { | |
| if(m_sorted) { | |
| reset(); | |
| } | |
| set_cur_cell(x >> poly_base_shift, y >> poly_base_shift); | |
| m_cur_x = x; | |
| m_cur_y = y; | |
| } | |
| void outline_aa::line_to(int x, int y) | |
| { | |
| render_line(m_cur_x, m_cur_y, x, y); | |
| m_cur_x = x; | |
| m_cur_y = y; | |
| m_sorted = false; | |
| } | |
| template <class T> static AGG_INLINE void swap_cells(T* a, T* b) | |
| { | |
| T temp = *a; | |
| *a = *b; | |
| *b = temp; | |
| } | |
| enum { | |
| qsort_threshold = 9 | |
| }; | |
| static void qsort_cells(cell_aa** start, unsigned num) | |
| { | |
| cell_aa** stack[80]; | |
| cell_aa*** top; | |
| cell_aa** limit; | |
| cell_aa** base; | |
| limit = start + num; | |
| base = start; | |
| top = stack; | |
| for (;;) { | |
| int len = int(limit - base); | |
| cell_aa** i; | |
| cell_aa** j; | |
| cell_aa** pivot; | |
| if(len > qsort_threshold) { | |
| pivot = base + len / 2; | |
| swap_cells(base, pivot); | |
| i = base + 1; | |
| j = limit - 1; | |
| if((*j)->x < (*i)->x) { | |
| swap_cells(i, j); | |
| } | |
| if((*base)->x < (*i)->x) { | |
| swap_cells(base, i); | |
| } | |
| if((*j)->x < (*base)->x) { | |
| swap_cells(base, j); | |
| } | |
| for(;;) { | |
| int x = (*base)->x; | |
| do { | |
| i++; | |
| } while( (*i)->x < x ); | |
| do { | |
| j--; | |
| } while( x < (*j)->x ); | |
| if(i > j) { | |
| break; | |
| } | |
| swap_cells(i, j); | |
| } | |
| swap_cells(base, j); | |
| if(j - base > limit - i) { | |
| top[0] = base; | |
| top[1] = j; | |
| base = i; | |
| } else { | |
| top[0] = i; | |
| top[1] = limit; | |
| limit = j; | |
| } | |
| top += 2; | |
| } else { | |
| j = base; | |
| i = j + 1; | |
| for(; i < limit; j = i, i++) { | |
| for(; j[1]->x < (*j)->x; j--) { | |
| swap_cells(j + 1, j); | |
| if (j == base) { | |
| break; | |
| } | |
| } | |
| } | |
| if(top > stack) { | |
| top -= 2; | |
| base = top[0]; | |
| limit = top[1]; | |
| } else { | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| void outline_aa::sort_cells() | |
| { | |
| if(m_sorted) { | |
| return; | |
| } | |
| add_cur_cell(); | |
| if(m_num_cells == 0) { | |
| return; | |
| } | |
| m_sorted_cells.allocate(m_num_cells, 16); | |
| if (m_max_y > 0 && m_min_y < 0 && -m_min_y > INT_MAX - m_max_y) { | |
| return; | |
| } | |
| unsigned size = m_max_y - m_min_y; | |
| if (size + 1 < size) { | |
| return; | |
| } | |
| size++; | |
| m_sorted_y.allocate(size, 16); | |
| m_sorted_y.zero(); | |
| cell_aa** block_ptr = m_cells; | |
| cell_aa* cell_ptr = NULL; | |
| unsigned nb = m_num_cells >> cell_block_shift; | |
| unsigned i; | |
| while(nb--) { | |
| cell_ptr = *block_ptr++; | |
| i = cell_block_size; | |
| while(i--) { | |
| m_sorted_y[cell_ptr->y - m_min_y].start++; | |
| ++cell_ptr; | |
| } | |
| } | |
| i = m_num_cells & cell_block_mask; | |
| if (i) { | |
| cell_ptr = *block_ptr++; | |
| } | |
| while(i--) { | |
| m_sorted_y[cell_ptr->y - m_min_y].start++; | |
| ++cell_ptr; | |
| } | |
| unsigned start = 0; | |
| for(i = 0; i < m_sorted_y.size(); i++) { | |
| unsigned v = m_sorted_y[i].start; | |
| m_sorted_y[i].start = start; | |
| start += v; | |
| } | |
| block_ptr = m_cells; | |
| nb = m_num_cells >> cell_block_shift; | |
| while(nb--) { | |
| cell_ptr = *block_ptr++; | |
| i = cell_block_size; | |
| while(i--) { | |
| sorted_y& cur_y = m_sorted_y[cell_ptr->y - m_min_y]; | |
| m_sorted_cells[cur_y.start + cur_y.num] = cell_ptr; | |
| ++cur_y.num; | |
| ++cell_ptr; | |
| } | |
| } | |
| i = m_num_cells & cell_block_mask; | |
| if (i) { | |
| cell_ptr = *block_ptr++; | |
| } | |
| while(i--) { | |
| sorted_y& cur_y = m_sorted_y[cell_ptr->y - m_min_y]; | |
| m_sorted_cells[cur_y.start + cur_y.num] = cell_ptr; | |
| ++cur_y.num; | |
| ++cell_ptr; | |
| } | |
| for(i = 0; i < m_sorted_y.size(); i++) { | |
| const sorted_y& cur_y = m_sorted_y[i]; | |
| if(cur_y.num) { | |
| qsort_cells(m_sorted_cells.data() + cur_y.start, cur_y.num); | |
| } | |
| } | |
| m_sorted = true; | |
| } | |
| } |