518 lines
14 KiB
C++
518 lines
14 KiB
C++
|
|
//----------------------------------------------------------------------------
|
|
// 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;
|
|
}
|
|
// static
|
|
int rasterizer_scanline_aa::calculate_area(int cover, int shift)
|
|
{
|
|
unsigned int result = cover;
|
|
result <<= shift;
|
|
return result;
|
|
}
|
|
// static
|
|
bool rasterizer_scanline_aa::safe_add(int* op1, int op2)
|
|
{
|
|
pdfium::base::CheckedNumeric<int> safeOp1 = *op1;
|
|
safeOp1 += op2;
|
|
if(!safeOp1.IsValid()) {
|
|
return false;
|
|
}
|
|
|
|
*op1 = safeOp1.ValueOrDie();
|
|
return true;
|
|
}
|
|
}
|