agg/include/agg_rasterizer_compound_aa.h

666 lines
22 KiB
C
Raw Normal View History

2021-12-27 20:14:31 +01:00
//----------------------------------------------------------------------------
// 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.
//
//----------------------------------------------------------------------------
#ifndef AGG_RASTERIZER_COMPOUND_AA_INCLUDED
#define AGG_RASTERIZER_COMPOUND_AA_INCLUDED
#include <limits>
#include "agg_rasterizer_cells_aa.h"
#include "agg_rasterizer_sl_clip.h"
namespace agg
{
//-----------------------------------------------------------cell_style_aa
// A pixel cell. There're no constructors defined and it was done
// intentionally in order to avoid extra overhead when allocating an
// array of cells.
struct cell_style_aa
{
int x;
int y;
int cover;
int area;
int16 left, right;
void initial()
{
x = std::numeric_limits<int>::max();
y = std::numeric_limits<int>::max();
cover = 0;
area = 0;
left = -1;
right = -1;
}
void style(const cell_style_aa& c)
{
left = c.left;
right = c.right;
}
int not_equal(int ex, int ey, const cell_style_aa& c) const
{
return ((unsigned)ex - (unsigned)x) | ((unsigned)ey - (unsigned)y) |
((unsigned)left - (unsigned)c.left) | ((unsigned)right - (unsigned)c.right);
}
};
//===========================================================layer_order_e
enum layer_order_e
{
layer_unsorted, //------layer_unsorted
layer_direct, //------layer_direct
layer_inverse //------layer_inverse
};
//==================================================rasterizer_compound_aa
template<class Clip=rasterizer_sl_clip_int> class rasterizer_compound_aa
{
struct style_info
{
unsigned start_cell;
unsigned num_cells;
int last_x;
};
struct cell_info
{
int x, area, cover;
};
public:
typedef Clip clip_type;
typedef typename Clip::conv_type conv_type;
typedef typename Clip::coord_type coord_type;
enum aa_scale_e
{
aa_shift = 8,
aa_scale = 1 << aa_shift,
aa_mask = aa_scale - 1,
aa_scale2 = aa_scale * 2,
aa_mask2 = aa_scale2 - 1
};
//--------------------------------------------------------------------
rasterizer_compound_aa() :
m_outline(),
m_clipper(),
m_filling_rule(fill_non_zero),
m_layer_order(layer_direct),
m_styles(), // Active Styles
m_ast(), // Active Style Table (unique values)
m_asm(), // Active Style Mask
m_cells(),
m_cover_buf(),
m_min_style(std::numeric_limits<int>::max()),
m_max_style(std::numeric_limits<int>::min()),
m_start_x(0),
m_start_y(0),
m_scan_y(std::numeric_limits<int>::max()),
m_sl_start(0),
m_sl_len(0)
{}
//--------------------------------------------------------------------
void reset();
void reset_clipping();
void clip_box(double x1, double y1, double x2, double y2);
void filling_rule(filling_rule_e filling_rule);
void layer_order(layer_order_e order);
//--------------------------------------------------------------------
void styles(int left, int right);
void move_to(int x, int y);
void line_to(int x, int y);
void move_to_d(double x, double y);
void line_to_d(double x, double y);
void add_vertex(double x, double y, unsigned cmd);
void edge(int x1, int y1, int x2, int y2);
void edge_d(double x1, double y1, double x2, double y2);
//-------------------------------------------------------------------
template<class VertexSource>
void add_path(VertexSource& vs, unsigned path_id=0)
{
double x;
double y;
unsigned cmd;
vs.rewind(path_id);
if(m_outline.sorted()) reset();
while(!is_stop(cmd = vs.vertex(&x, &y)))
{
add_vertex(x, y, cmd);
}
}
//--------------------------------------------------------------------
int min_x() const { return m_outline.min_x(); }
int min_y() const { return m_outline.min_y(); }
int max_x() const { return m_outline.max_x(); }
int max_y() const { return m_outline.max_y(); }
int min_style() const { return m_min_style; }
int max_style() const { return m_max_style; }
//--------------------------------------------------------------------
void sort();
bool rewind_scanlines();
unsigned sweep_styles();
int scanline_start() const { return m_sl_start; }
unsigned scanline_length() const { return m_sl_len; }
unsigned style(unsigned style_idx) const;
cover_type* allocate_cover_buffer(unsigned len);
//--------------------------------------------------------------------
bool navigate_scanline(int y);
bool hit_test(int tx, int ty);
//--------------------------------------------------------------------
AGG_INLINE unsigned calculate_alpha(int area) const
{
int cover = area >> (poly_subpixel_shift*2 + 1 - aa_shift);
if(cover < 0) cover = -cover;
if(m_filling_rule == fill_even_odd)
{
cover &= aa_mask2;
if(cover > aa_scale)
{
cover = aa_scale2 - cover;
}
}
if(cover > aa_mask) cover = aa_mask;
return cover;
}
//--------------------------------------------------------------------
// Sweeps one scanline with one style index. The style ID can be
// determined by calling style().
template<class Scanline> bool sweep_scanline(Scanline& sl, int style_idx)
{
int scan_y = m_scan_y - 1;
if(scan_y > m_outline.max_y()) return false;
sl.reset_spans();
if(style_idx < 0)
{
style_idx = 0;
}
else
{
style_idx++;
}
const style_info& st = m_styles[m_ast[style_idx]];
unsigned num_cells = st.num_cells;
cell_info* cell = &m_cells[st.start_cell];
int cover = 0;
while(num_cells--)
{
unsigned alpha;
int x = cell->x;
int area = cell->area;
cover += cell->cover;
++cell;
if(area)
{
alpha = calculate_alpha((cover << (poly_subpixel_shift + 1)) - area);
sl.add_cell(x, alpha);
x++;
}
if(num_cells && cell->x > x)
{
alpha = calculate_alpha(cover << (poly_subpixel_shift + 1));
if(alpha)
{
sl.add_span(x, cell->x - x, alpha);
}
}
}
if(sl.num_spans() == 0) return false;
sl.finalize(scan_y);
return true;
}
private:
void add_style(int style_id);
//--------------------------------------------------------------------
// Disable copying
rasterizer_compound_aa(const rasterizer_compound_aa<Clip>&);
const rasterizer_compound_aa<Clip>&
operator = (const rasterizer_compound_aa<Clip>&);
private:
rasterizer_cells_aa<cell_style_aa> m_outline;
clip_type m_clipper;
filling_rule_e m_filling_rule;
layer_order_e m_layer_order;
pod_vector<style_info> m_styles; // Active Styles
pod_vector<unsigned> m_ast; // Active Style Table (unique values)
pod_vector<int8u> m_asm; // Active Style Mask
pod_vector<cell_info> m_cells;
pod_vector<cover_type> m_cover_buf;
int m_min_style;
int m_max_style;
coord_type m_start_x;
coord_type m_start_y;
int m_scan_y;
int m_sl_start;
unsigned m_sl_len;
};
//------------------------------------------------------------------------
template<class Clip>
void rasterizer_compound_aa<Clip>::reset()
{
m_outline.reset();
m_min_style = std::numeric_limits<int>::max();
m_max_style = std::numeric_limits<int>::min();
m_scan_y = std::numeric_limits<int>::max();
m_sl_start = 0;
m_sl_len = 0;
}
//------------------------------------------------------------------------
template<class Clip>
void rasterizer_compound_aa<Clip>::filling_rule(filling_rule_e filling_rule)
{
m_filling_rule = filling_rule;
}
//------------------------------------------------------------------------
template<class Clip>
void rasterizer_compound_aa<Clip>::layer_order(layer_order_e order)
{
m_layer_order = order;
}
//------------------------------------------------------------------------
template<class Clip>
void rasterizer_compound_aa<Clip>::clip_box(double x1, double y1,
double x2, double y2)
{
reset();
m_clipper.clip_box(conv_type::upscale(x1), conv_type::upscale(y1),
conv_type::upscale(x2), conv_type::upscale(y2));
}
//------------------------------------------------------------------------
template<class Clip>
void rasterizer_compound_aa<Clip>::reset_clipping()
{
reset();
m_clipper.reset_clipping();
}
//------------------------------------------------------------------------
template<class Clip>
void rasterizer_compound_aa<Clip>::styles(int left, int right)
{
cell_style_aa cell;
cell.initial();
cell.left = (int16)left;
cell.right = (int16)right;
m_outline.style(cell);
if(left >= 0 && left < m_min_style) m_min_style = left;
if(left >= 0 && left > m_max_style) m_max_style = left;
if(right >= 0 && right < m_min_style) m_min_style = right;
if(right >= 0 && right > m_max_style) m_max_style = right;
}
//------------------------------------------------------------------------
template<class Clip>
void rasterizer_compound_aa<Clip>::move_to(int x, int y)
{
if(m_outline.sorted()) reset();
m_clipper.move_to(m_start_x = conv_type::downscale(x),
m_start_y = conv_type::downscale(y));
}
//------------------------------------------------------------------------
template<class Clip>
void rasterizer_compound_aa<Clip>::line_to(int x, int y)
{
m_clipper.line_to(m_outline,
conv_type::downscale(x),
conv_type::downscale(y));
}
//------------------------------------------------------------------------
template<class Clip>
void rasterizer_compound_aa<Clip>::move_to_d(double x, double y)
{
if(m_outline.sorted()) reset();
m_clipper.move_to(m_start_x = conv_type::upscale(x),
m_start_y = conv_type::upscale(y));
}
//------------------------------------------------------------------------
template<class Clip>
void rasterizer_compound_aa<Clip>::line_to_d(double x, double y)
{
m_clipper.line_to(m_outline,
conv_type::upscale(x),
conv_type::upscale(y));
}
//------------------------------------------------------------------------
template<class Clip>
void rasterizer_compound_aa<Clip>::add_vertex(double x, double y, unsigned cmd)
{
if(is_move_to(cmd))
{
move_to_d(x, y);
}
else
if(is_vertex(cmd))
{
line_to_d(x, y);
}
else
if(is_close(cmd))
{
m_clipper.line_to(m_outline, m_start_x, m_start_y);
}
}
//------------------------------------------------------------------------
template<class Clip>
void rasterizer_compound_aa<Clip>::edge(int x1, int y1, int x2, int y2)
{
if(m_outline.sorted()) reset();
m_clipper.move_to(conv_type::downscale(x1), conv_type::downscale(y1));
m_clipper.line_to(m_outline,
conv_type::downscale(x2),
conv_type::downscale(y2));
}
//------------------------------------------------------------------------
template<class Clip>
void rasterizer_compound_aa<Clip>::edge_d(double x1, double y1,
double x2, double y2)
{
if(m_outline.sorted()) reset();
m_clipper.move_to(conv_type::upscale(x1), conv_type::upscale(y1));
m_clipper.line_to(m_outline,
conv_type::upscale(x2),
conv_type::upscale(y2));
}
//------------------------------------------------------------------------
template<class Clip>
AGG_INLINE void rasterizer_compound_aa<Clip>::sort()
{
m_outline.sort_cells();
}
//------------------------------------------------------------------------
template<class Clip>
AGG_INLINE bool rasterizer_compound_aa<Clip>::rewind_scanlines()
{
m_outline.sort_cells();
if(m_outline.total_cells() == 0)
{
return false;
}
if(m_max_style < m_min_style)
{
return false;
}
m_scan_y = m_outline.min_y();
m_styles.allocate(m_max_style - m_min_style + 2, 128);
return true;
}
//------------------------------------------------------------------------
template<class Clip>
AGG_INLINE void rasterizer_compound_aa<Clip>::add_style(int style_id)
{
if(style_id < 0) style_id = 0;
else style_id -= m_min_style - 1;
unsigned nbyte = style_id >> 3;
unsigned mask = 1 << (style_id & 7);
style_info* style = &m_styles[style_id];
if((m_asm[nbyte] & mask) == 0)
{
m_ast.add(style_id);
m_asm[nbyte] |= mask;
style->start_cell = 0;
style->num_cells = 0;
style->last_x = std::numeric_limits<int>::min();
}
++style->start_cell;
}
//------------------------------------------------------------------------
// Returns the number of styles
template<class Clip>
unsigned rasterizer_compound_aa<Clip>::sweep_styles()
{
for(;;)
{
if(m_scan_y > m_outline.max_y()) return 0;
unsigned num_cells = m_outline.scanline_num_cells(m_scan_y);
const cell_style_aa* const* cells = m_outline.scanline_cells(m_scan_y);
unsigned num_styles = m_max_style - m_min_style + 2;
const cell_style_aa* curr_cell;
unsigned style_id;
style_info* style;
cell_info* cell;
m_cells.allocate(num_cells * 2, 256); // Each cell can have two styles
m_ast.capacity(num_styles, 64);
m_asm.allocate((num_styles + 7) >> 3, 8);
m_asm.zero();
if(num_cells)
{
// Pre-add zero (for no-fill style, that is, -1).
// We need that to ensure that the "-1 style" would go first.
m_asm[0] |= 1;
m_ast.add(0);
style = &m_styles[0];
style->start_cell = 0;
style->num_cells = 0;
style->last_x = std::numeric_limits<int>::min();
m_sl_start = cells[0]->x;
m_sl_len = cells[num_cells-1]->x - m_sl_start + 1;
while(num_cells--)
{
curr_cell = *cells++;
add_style(curr_cell->left);
add_style(curr_cell->right);
}
// Convert the Y-histogram into the array of starting indexes
unsigned i;
unsigned start_cell = 0;
for(i = 0; i < m_ast.size(); i++)
{
style_info& st = m_styles[m_ast[i]];
unsigned v = st.start_cell;
st.start_cell = start_cell;
start_cell += v;
}
cells = m_outline.scanline_cells(m_scan_y);
num_cells = m_outline.scanline_num_cells(m_scan_y);
while(num_cells--)
{
curr_cell = *cells++;
style_id = (curr_cell->left < 0) ? 0 :
curr_cell->left - m_min_style + 1;
style = &m_styles[style_id];
if(curr_cell->x == style->last_x)
{
cell = &m_cells[style->start_cell + style->num_cells - 1];
cell->area += curr_cell->area;
cell->cover += curr_cell->cover;
}
else
{
cell = &m_cells[style->start_cell + style->num_cells];
cell->x = curr_cell->x;
cell->area = curr_cell->area;
cell->cover = curr_cell->cover;
style->last_x = curr_cell->x;
style->num_cells++;
}
style_id = (curr_cell->right < 0) ? 0 :
curr_cell->right - m_min_style + 1;
style = &m_styles[style_id];
if(curr_cell->x == style->last_x)
{
cell = &m_cells[style->start_cell + style->num_cells - 1];
cell->area -= curr_cell->area;
cell->cover -= curr_cell->cover;
}
else
{
cell = &m_cells[style->start_cell + style->num_cells];
cell->x = curr_cell->x;
cell->area = -curr_cell->area;
cell->cover = -curr_cell->cover;
style->last_x = curr_cell->x;
style->num_cells++;
}
}
}
if(m_ast.size() > 1) break;
++m_scan_y;
}
++m_scan_y;
if(m_layer_order != layer_unsorted)
{
range_adaptor<pod_vector<unsigned> > ra(m_ast, 1, m_ast.size() - 1);
if(m_layer_order == layer_direct) quick_sort(ra, unsigned_greater);
else quick_sort(ra, unsigned_less);
}
return m_ast.size() - 1;
}
//------------------------------------------------------------------------
// Returns style ID depending of the existing style index
template<class Clip>
AGG_INLINE
unsigned rasterizer_compound_aa<Clip>::style(unsigned style_idx) const
{
return m_ast[style_idx + 1] + m_min_style - 1;
}
//------------------------------------------------------------------------
template<class Clip>
AGG_INLINE bool rasterizer_compound_aa<Clip>::navigate_scanline(int y)
{
m_outline.sort_cells();
if(m_outline.total_cells() == 0)
{
return false;
}
if(m_max_style < m_min_style)
{
return false;
}
if(y < m_outline.min_y() || y > m_outline.max_y())
{
return false;
}
m_scan_y = y;
m_styles.allocate(m_max_style - m_min_style + 2, 128);
return true;
}
//------------------------------------------------------------------------
template<class Clip>
bool rasterizer_compound_aa<Clip>::hit_test(int tx, int ty)
{
if(!navigate_scanline(ty))
{
return false;
}
unsigned num_styles = sweep_styles();
if(num_styles <= 0)
{
return false;
}
scanline_hit_test sl(tx);
sweep_scanline(sl, -1);
return sl.hit();
}
//------------------------------------------------------------------------
template<class Clip>
cover_type* rasterizer_compound_aa<Clip>::allocate_cover_buffer(unsigned len)
{
m_cover_buf.allocate(len, 256);
return &m_cover_buf[0];
}
}
#endif