lazarus/components/aggpas/src/agg_rasterizer_scanline_aa.pas
mattias 80fb2b2367 aggpas: 64bit fixes from fpgui aggpas
git-svn-id: trunk@47745 -
2015-02-13 17:45:09 +00:00

1825 lines
35 KiB
ObjectPascal

//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.4 (Public License)
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Anti-Grain Geometry - Version 2.4 Release Milano 3 (AggPas 2.4 RM3)
// Pascal Port By: Milan Marusinec alias Milano
// milan@marusinec.sk
// http://www.aggpas.org
// Copyright (c) 2005-2006
//
// 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.
//
// [Pascal Port History] -----------------------------------------------------
//
// 23.06.2006-Milano: ptrcomp adjustments
// 28.02.2006-Milano: scanline_hit_test
// 23.11.2005-Milano: outline_aa.sort_cells, rasterizer.rewind,sweep, ...
// 21.11.2005-Milano: outline_aa ,rasterizer_scanline_aa.line_to.move_to
// 17.11.2005-Milano: Unit port establishment
//
{ agg_rasterizer_scanline_aa.pas }
unit
agg_rasterizer_scanline_aa ;
INTERFACE
{$I agg_mode.inc }
{$Q- }
{$R- }
uses
agg_basics ,
agg_array ,
agg_scanline ,
agg_vertex_source ,
agg_gamma_functions ,
agg_clip_liang_barsky ;
{ TYPES DEFINITION }
const
aa_shift = 8;
aa_num = 1 shl aa_shift;
aa_mask = aa_num - 1;
aa_2num = aa_num * 2;
aa_2mask = aa_2num - 1;
cell_block_shift = 12;
cell_block_size = 1 shl cell_block_shift;
cell_block_mask = cell_block_size - 1;
cell_block_pool = 256;
cell_block_limit = 1024;
// These constants determine the subpixel accuracy, to be more precise,
// the number of bits of the fractional part of the coordinates.
// The possible coordinate capacity in bits can be calculated by formula:
// sizeof(int) * 8 - poly_base_shift * 2, i.e, for 32-bit integers and
// 8-bits fractional part the capacity is 16 bits or [-32768...32767].
poly_base_shift = 8; //----poly_base_shift
poly_base_size = 1 shl poly_base_shift; //----poly_base_size
poly_base_mask = poly_base_size - 1; //----poly_base_mask
type
//-----------------------------------------------------------------cell_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.
cell_aa_ptr_ptr = ^cell_aa_ptr;
cell_aa_ptr = ^cell_aa;
cell_aa = record
x ,y ,
cover ,
area : int;
end;
//--------------------------------------------------------------outline_aa
// An internal class that implements the main rasterization algorithm.
// Used in the rasterizer. Should not be used direcly.
sorted_y_ptr = ^sorted_y;
sorted_y = record
start ,
num : unsigned;
end;
outline_aa = object
m_num_blocks ,
m_max_blocks ,
m_cur_block ,
m_num_cells : unsigned;
m_cur_x ,
m_cur_y ,
m_min_x ,
m_min_y ,
m_max_x ,
m_max_y : int;
m_sorted : boolean;
m_cells : cell_aa_ptr_ptr;
m_cur_cell_ptr : cell_aa_ptr;
m_cur_cell : cell_aa;
m_sorted_cells : pod_array;
m_sorted_y : pod_array;
constructor Construct;
destructor Destruct;
procedure move_to(x ,y : int );
procedure line_to(x ,y : int );
procedure reset;
procedure add_cur_cell;
procedure set_cur_cell(x ,y : int );
procedure sort_cells;
function total_cells : unsigned;
function scanline_num_cells(y : unsigned ) : unsigned;
function scanline_cells (y : unsigned ) : cell_aa_ptr_ptr;
function sorted : boolean;
procedure render_line (x1 ,y1 ,x2 ,y2 : int );
procedure render_hline(ey ,x1 ,y1 ,x2 ,y2 : int );
procedure allocate_block;
function _min_x : int;
function _min_y : int;
function _max_x : int;
function _max_y : int;
end;
//------------------------------------------------------scanline_hit_test
scanline_hit_test = object(scanline )
m_x : int;
m_hit : boolean;
constructor Construct(x : int );
procedure reset_spans; virtual;
procedure finalize(y_ : int ); virtual;
procedure add_cell(x : int; cover : unsigned ); virtual;
procedure add_span(x : int; len ,cover : unsigned ); virtual;
function num_spans : unsigned; virtual;
function hit : boolean;
end;
//==================================================rasterizer_scanline_aa
// Polygon rasterizer that is used to render filled polygons with
// high-quality Anti-Aliasing. Internally, by default, the class uses
// integer coordinates in format 24.8, i.e. 24 bits for integer part
// and 8 bits for fractional - see poly_base_shift. This class can be
// used in the following way:
//
// 1. filling_rule(filling_rule_e ft) - optional.
//
// 2. gamma() - optional.
//
// 3. reset()
//
// 4. move_to(x, y) / line_to(x, y) - make the polygon. One can create
// more than one contour, but each contour must consist of at least 3
// vertices, i.e. move_to(x1, y1); line_to(x2, y2); line_to(x3, y3);
// is the absolute minimum of vertices that define a triangle.
// The algorithm does not check either the number of vertices nor
// coincidence of their coordinates, but in the worst case it just
// won't draw anything.
// The orger of the vertices (clockwise or counterclockwise)
// is important when using the non-zero filling rule (fill_non_zero).
// In this case the vertex order of all the contours must be the same
// if you want your intersecting polygons to be without "holes".
// You actually can use different vertices order. If the contours do not
// intersect each other the order is not important anyway. If they do,
// contours with the same vertex order will be rendered without "holes"
// while the intersecting contours with different orders will have "holes".
//
// filling_rule() and gamma() can be called anytime before "sweeping".
//------------------------------------------------------------------------
// filling_rule_e = (fill_non_zero ,fill_even_odd );
status = (status_initial ,status_line_to ,status_closed );
rasterizer_scanline_ptr = ^rasterizer_scanline;
rasterizer_scanline = object
procedure reset; virtual; abstract;
procedure filling_rule(filling_rule_ : filling_rule_e ); virtual; abstract;
procedure clip_box (x1 ,y1 ,x2 ,y2 : double ); virtual; abstract;
procedure gamma(gamma_function : vertex_source_ptr ); virtual; abstract;
procedure add_path (vs : vertex_source_ptr; path_id : unsigned = 0 ); virtual; abstract;
procedure add_vertex(x ,y : double; cmd : unsigned ); virtual; abstract;
procedure sort; virtual; abstract;
function rewind_scanlines : boolean; virtual; abstract;
function sweep_scanline (sl : scanline_ptr ) : boolean; virtual; abstract;
function sweep_scanline_em(sl : scanline_ptr ) : boolean; virtual; abstract;
function hit_test(tx ,ty : int ) : boolean; virtual; abstract;
function _min_x : int; virtual; abstract;
function _min_y : int; virtual; abstract;
function _max_x : int; virtual; abstract;
function _max_y : int; virtual; abstract;
end;
rasterizer_scanline_aa_ptr = ^rasterizer_scanline_aa;
rasterizer_scanline_aa = object(rasterizer_scanline )
m_outline : outline_aa;
m_gamma : array [0..aa_num - 1 ] of int;
m_filling_rule : filling_rule_e;
m_clipped_start_x ,
m_clipped_start_y ,
m_start_x ,
m_start_y ,
m_prev_x ,
m_prev_y : int;
m_prev_flags : unsigned;
m_status : status;
m_clip_box : rect;
m_clipping : boolean;
m_cur_y ,
XScale : int;
m_auto_close : boolean;
constructor Construct;
destructor Destruct;
procedure reset; virtual;
procedure filling_rule(filling_rule_ : filling_rule_e ); virtual;
procedure auto_close (flag : boolean );
procedure clip_box (x1 ,y1 ,x2 ,y2 : double ); virtual;
procedure gamma (gamma_function : vertex_source_ptr ); virtual;
function apply_gamma(cover : unsigned ) : unsigned;
procedure move_to_no_clip(x ,y : int );
procedure line_to_no_clip(x ,y : int );
procedure close_polygon;
procedure close_polygon_no_clip;
procedure clip_segment(x ,y : int );
procedure move_to_d(x ,y : double );
procedure line_to_d(x ,y : double );
procedure move_to(x ,y : int );
procedure line_to(x ,y : int );
procedure sort; virtual;
function rewind_scanlines : boolean; virtual;
function sweep_scanline(sl : scanline_ptr ) : boolean; virtual;
function navigate_scanline(y : int ) : boolean;
function hit_test(tx ,ty : int ) : boolean; virtual;
function _min_x : int; virtual;
function _min_y : int; virtual;
function _max_x : int; virtual;
function _max_y : int; virtual;
function calculate_alpha(area : int ) : unsigned;
procedure add_path (vs : vertex_source_ptr; path_id : unsigned = 0 ); virtual;
procedure add_vertex(x ,y : double; cmd : unsigned ); virtual;
end;
{ GLOBAL PROCEDURES }
function poly_coord(c : double ) : int;
IMPLEMENTATION
{ LOCAL VARIABLES & CONSTANTS }
{ UNIT IMPLEMENTATION }
{ CONSTRUCT }
constructor outline_aa.Construct;
begin
m_sorted_cells.Construct(sizeof(cell_aa_ptr ) );
m_sorted_y.Construct (sizeof(sorted_y ) );
m_num_blocks:=0;
m_max_blocks:=0;
m_cur_block :=0;
m_num_cells :=0;
m_cur_x:=0;
m_cur_y:=0;
m_min_x:=$7FFFFFFF;
m_min_y:=$7FFFFFFF;
m_max_x:=-$7FFFFFFF;
m_max_y:=-$7FFFFFFF;
m_sorted:=false;
m_cells :=NIL;
m_cur_cell_ptr:=NIL;
with m_cur_cell do
begin
x:=$7FFF;
y:=$7FFF;
cover:=0;
area :=0;
end;
end;
{ DESTRUCT }
destructor outline_aa.Destruct;
begin
m_sorted_cells.Destruct;
m_sorted_y.Destruct;
if m_num_blocks > 0 then
begin
repeat
dec(m_num_blocks );
agg_freemem(
pointer(pointer(ptrcomp(m_cells ) + m_num_blocks * sizeof(cell_aa_ptr ) )^ ) ,
cell_block_size * sizeof(cell_aa ) );
until m_num_blocks = 0;
agg_freemem(pointer(m_cells ) ,sizeof(cell_aa_ptr ) * m_max_blocks );
end;
end;
{ MOVE_TO }
procedure outline_aa.move_to;
begin
if m_sorted then
reset;
// set_cur_cell(x shr poly_base_shift ,y shr poly_base_shift );
set_cur_cell(shr_int32(x ,poly_base_shift ) ,shr_int32(y ,poly_base_shift ) );
m_cur_x:=x;
m_cur_y:=y;
end;
{ LINE_TO }
procedure outline_aa.line_to;
begin
render_line(m_cur_x ,m_cur_y ,x ,y );
m_cur_x:=x;
m_cur_y:=y;
m_sorted:=false;
end;
{ RESET }
procedure outline_aa.reset;
begin
m_num_cells:=0;
m_cur_block:=0;
m_cur_cell.x :=$7FFF;
m_cur_cell.y :=$7FFF;
m_cur_cell.cover:=0;
m_cur_cell.area :=0;
m_sorted:=false;
m_min_x:=$7FFFFFFF;
m_min_y:=$7FFFFFFF;
m_max_x:=-$7FFFFFFF;
m_max_y:=-$7FFFFFFF;
end;
{ ADD_CUR_CELL }
procedure outline_aa.add_cur_cell;
begin
if (m_cur_cell.area or m_cur_cell.cover ) <> 0 then
begin
if (m_num_cells and cell_block_mask ) = 0 then
begin
if m_num_blocks >= cell_block_limit then
exit;
allocate_block;
end;
m_cur_cell_ptr^:=m_cur_cell;
inc(ptrcomp(m_cur_cell_ptr ) ,sizeof(cell_aa ) );
inc(m_num_cells );
if m_cur_cell.x < m_min_x then
m_min_x:=m_cur_cell.x;
if m_cur_cell.x > m_max_x then
m_max_x:=m_cur_cell.x;
if m_cur_cell.y < m_min_y then
m_min_y:=m_cur_cell.y;
if m_cur_cell.y > m_max_y then
m_max_y:=m_cur_cell.y;
end;
end;
{ SET_CUR_CELL }
procedure outline_aa.set_cur_cell;
begin
if (m_cur_cell.x <> x ) or
(m_cur_cell.y <> y ) then
begin
add_cur_cell;
m_cur_cell.x:=x;
m_cur_cell.y:=y;
m_cur_cell.cover:=0;
m_cur_cell.area :=0;
end;
end;
{ qsort_cells }
procedure qsort_cells(start : cell_aa_ptr_ptr; num : unsigned );
var
len ,x : int;
temp : pointer;
stack : array[0..79 ] of cell_aa_ptr_ptr;
limit ,
base ,
i ,j ,
pivot : cell_aa_ptr_ptr;
top : ^cell_aa_ptr_ptr;
const
qsort_threshold = 9;
begin
limit:=cell_aa_ptr_ptr(ptrcomp(start ) + num * SizeOf(Pointer) );
base :=start;
top :=@stack[0 ];
repeat
len:=(ptrcomp(limit ) - ptrcomp(base ) ) div SizeOf(Pointer);
if len > qsort_threshold then
begin
// we use base + len/2 as the pivot
pivot:=cell_aa_ptr_ptr(ptrcomp(base ) + (len div 2 ) * SizeOf(Pointer) );
temp:=p32_ptr(base ).ptr;
p32_ptr(base ).ptr :=p32_ptr(pivot ).ptr;
p32_ptr(pivot ).ptr:=temp;
i:=cell_aa_ptr_ptr(ptrcomp(base ) + SizeOf(Pointer) );
j:=cell_aa_ptr_ptr(ptrcomp(limit ) - SizeOf(Pointer) );
// now ensure that *i <= *base <= *j
if j^.x < i^.x then
begin
temp:=p32_ptr(i ).ptr;
p32_ptr(i ).ptr:=p32_ptr(j ).ptr;
p32_ptr(j ).ptr:=temp;
end;
if base^.x < i^.x then
begin
temp:=p32_ptr(base ).ptr;
p32_ptr(base ).ptr:=p32_ptr(i ).ptr;
p32_ptr(i ).ptr :=temp;
end;
if j^.x < base^.x then
begin
temp:=p32_ptr(base ).ptr;
p32_ptr(base ).ptr:=p32_ptr(j ).ptr;
p32_ptr(j ).ptr :=temp;
end;
repeat
x:=base^.x;
inc(ptrcomp(i ) ,sizeof(cell_aa_ptr ) );
while i^.x < x do
inc(ptrcomp(i ) ,sizeof(cell_aa_ptr ) );
dec(ptrcomp(j ) ,sizeof(cell_aa_ptr ) );
while x < j^.x do
dec(ptrcomp(j ) ,sizeof(cell_aa_ptr ) );
if ptrcomp(i ) > ptrcomp(j ) then
break;
temp:=p32_ptr(i ).ptr;
p32_ptr(i ).ptr:=p32_ptr(j ).ptr;
p32_ptr(j ).ptr:=temp;
until false;
temp:=p32_ptr(base ).ptr;
p32_ptr(base ).ptr:=p32_ptr(j ).ptr;
p32_ptr(j ).ptr :=temp;
// now, push the largest sub-array
if (ptrcomp(j ) - ptrcomp(base ) ) div SizeOf(Pointer) > (ptrcomp(limit ) - ptrcomp(i ) ) div SizeOf(Pointer) then
begin
top^:=base;
inc(ptrcomp(top ) ,sizeof(cell_aa_ptr_ptr ) );
top^:=j;
base:=i;
end
else
begin
top^:=i;
inc(ptrcomp(top ) ,sizeof(cell_aa_ptr_ptr ) );
top^ :=limit;
limit:=j;
end;
inc(ptrcomp(top ) ,sizeof(cell_aa_ptr_ptr ) );
end
else
begin
// the sub-array is small, perform insertion sort
j:=base;
i:=cell_aa_ptr_ptr(ptrcomp(j ) + 1 * SizeOf(Pointer) );
while ptrcomp(i ) < ptrcomp(limit ) do
begin
try
while cell_aa_ptr_ptr(ptrcomp(j ) + 1 * SizeOf(Pointer) )^^.x < j^.x do
begin
//swap_ptrs(cell_aa_ptr_ptr(ptrcomp(j ) + 1 * SizeOf(Pointer) ) ,j );
temp:=p32_ptr(cell_aa_ptr_ptr(ptrcomp(j ) + 1 * SizeOf(Pointer) ) ).ptr;
p32_ptr(cell_aa_ptr_ptr(ptrcomp(j ) + 1 * SizeOf(Pointer) ) ).ptr:=p32_ptr(j ).ptr;
p32_ptr(j ).ptr:=temp;
if j = base then
break;
dec(ptrcomp(j ) ,sizeof(cell_aa_ptr ) );
end;
except
end;
j:=i;
inc(ptrcomp(i ) ,sizeof(cell_aa_ptr ) );
end;
if ptrcomp(top ) > ptrcomp(@stack[0 ] ) then
begin
dec(ptrcomp(top ) ,sizeof(cell_aa_ptr_ptr ) );
limit:=top^;
dec(ptrcomp(top ) ,sizeof(cell_aa_ptr_ptr ) );
base:=top^;
end
else
break;
end;
until false;
end;
{ SORT_CELLS }
procedure outline_aa.sort_cells;
var
nb ,i ,
v ,
start : unsigned;
cur_y : sorted_y_ptr;
block_ptr : cell_aa_ptr_ptr;
cell_ptr : cell_aa_ptr;
// fout : text;
begin
//Perform sort only the first time
if m_sorted then
exit;
add_cur_cell;
if m_num_cells = 0 then
exit;
// Allocate the array of cell pointers
m_sorted_cells.allocate(m_num_cells ,16 );
// Allocate and zero the Y array
m_sorted_y.allocate(m_max_y - m_min_y + 1 ,16 );
m_sorted_y.zero;
// Create the Y-histogram (count the numbers of cells for each Y)
block_ptr:=m_cells;
nb:=m_num_cells shr cell_block_shift;
while nb > 0 do
begin
dec(nb );
cell_ptr:=block_ptr^;
inc(ptrcomp(block_ptr ) ,sizeof(cell_aa_ptr ) );
i:=cell_block_size;
while i > 0 do
begin
dec(i );
inc(sorted_y_ptr(ptrcomp(m_sorted_y.m_array ) + unsigned(cell_ptr^.y - m_min_y ) * m_sorted_y.m_entry_sz ).start );
inc(ptrcomp(cell_ptr ) ,sizeof(cell_aa ) );
end;
end;
cell_ptr:=block_ptr^;
inc(ptrcomp(block_ptr ) ,sizeof(cell_aa_ptr ) );
i:=m_num_cells and cell_block_mask;
while i > 0 do
begin
dec(i );
inc(sorted_y_ptr(ptrcomp(m_sorted_y.m_array ) + unsigned(cell_ptr^.y - m_min_y ) * m_sorted_y.m_entry_sz ).start );
inc(ptrcomp(cell_ptr ) ,sizeof(cell_aa ) );
end;
// Convert the Y-histogram into the array of starting indexes
start:=0;
for i:=0 to m_sorted_y.size - 1 do
begin
v:=sorted_y_ptr(ptrcomp(m_sorted_y.m_array ) + i * m_sorted_y.m_entry_sz ).start;
sorted_y_ptr(ptrcomp(m_sorted_y.m_array ) + i * m_sorted_y.m_entry_sz ).start:=start;
inc(start ,v );
end;
// Fill the cell pointer array sorted by Y
block_ptr:=m_cells;
nb:=m_num_cells shr cell_block_shift;
while nb > 0 do
begin
dec(nb );
cell_ptr:=block_ptr^;
inc(ptrcomp(block_ptr ) ,sizeof(cell_aa_ptr ) );
i:=cell_block_size;
while i > 0 do
begin
dec(i );
cur_y:=sorted_y_ptr(ptrcomp(m_sorted_y.m_array ) + unsigned(cell_ptr.y - m_min_y ) * m_sorted_y.m_entry_sz );
p32_ptr(ptrcomp(m_sorted_cells.m_array ) + unsigned(cur_y.start + cur_y.num ) * m_sorted_cells.m_entry_sz ).ptr:=cell_ptr;
inc(cur_y.num );
inc(ptrcomp(cell_ptr ) ,sizeof(cell_aa ) );
end;
end;
cell_ptr:=block_ptr^;
inc(ptrcomp(block_ptr ) ,sizeof(cell_aa_ptr ) );
i:=m_num_cells and cell_block_mask;
while i > 0 do
begin
dec(i );
cur_y:=sorted_y_ptr(ptrcomp(m_sorted_y.m_array ) + unsigned(cell_ptr.y - m_min_y ) * m_sorted_y.m_entry_sz );
p32_ptr(ptrcomp(m_sorted_cells.m_array ) + unsigned(cur_y.start + cur_y.num ) * m_sorted_cells.m_entry_sz ).ptr:=cell_ptr;
inc(cur_y.num );
inc(ptrcomp(cell_ptr ) ,sizeof(cell_aa ) );
end;
// Finally arrange the X-arrays
for i:=0 to m_sorted_y.size - 1 do
begin
cur_y:=sorted_y_ptr(ptrcomp(m_sorted_y.m_array ) + i * m_sorted_y.m_entry_sz );
if cur_y.num > 0 then
qsort_cells(cell_aa_ptr_ptr(ptrcomp(m_sorted_cells.m_array ) + cur_y.start * m_sorted_cells.m_entry_sz ) ,cur_y.num );
end;
m_sorted:=true;
// M_SORTED_CELLS
(* assignfile(fout ,'sorted.yes' );
rewrite(fout );
for i:=0 to m_sorted_cells.size - 1 do
begin
cell_ptr:=p32_ptr(ptrcomp(m_sorted_cells.m_array ) + i * m_sorted_cells.m_entry_sz ).ptr;
write(fout ,cell_ptr.x ,' ,' ,cell_ptr.y ,' ,' );
write(fout ,cell_ptr.cover ,' ,' ,cell_ptr.area );
writeln(fout );
end;
close(fout );*)
// M_SORTED_Y
(* assignfile(fout ,'y.yes' );
rewrite(fout );
for i:=0 to m_sorted_y.size - 1 do
begin
cur_y:=sorted_y_ptr(ptrcomp(m_sorted_y.m_array ) + i * m_sorted_y.m_entry_sz );
writeln(fout ,cur_y.start ,' ,' ,cur_y.num );
end;
close(fout );*)
end;
{ TOTAL_CELLS }
function outline_aa.total_cells;
begin
result:=m_num_cells;
end;
{ SCANLINE_NUM_CELLS }
function outline_aa.scanline_num_cells;
begin
result:=sorted_y_ptr(ptrcomp(m_sorted_y.m_array ) + unsigned(y - m_min_y ) * m_sorted_y.m_entry_sz ).num;
end;
{ SCANLINE_CELLS }
function outline_aa.scanline_cells;
begin
result:=
cell_aa_ptr_ptr(
ptrcomp(m_sorted_cells.m_array ) +
sorted_y_ptr(ptrcomp(m_sorted_y.m_array ) + unsigned(y - m_min_y ) * m_sorted_y.m_entry_sz ).start
* m_sorted_cells.m_entry_sz );
end;
{ SORTED }
function outline_aa.sorted;
begin
result:=m_sorted;
end;
{ RENDER_LINE }
procedure outline_aa.render_line;
var
p ,
cx ,
cy ,
dx ,
dy ,
ex ,
ey1 ,
ey2 ,
fy1 ,
fy2 ,
rem ,
m_d ,
x_from ,
x_to ,
lift ,
delta ,
first ,
incr ,
two_fx ,
area : int;
const
dx_limit = 16384 shl poly_base_shift;
begin
dx:=x2 - x1;
if (dx >= dx_limit ) or
(dx <= -dx_limit ) then
begin
cx:=(x1 + x2) shr 1;
cy:=(y1 + y2) shr 1;
render_line(x1 ,y1 ,cx ,cy );
render_line(cx ,cy ,x2 ,y2 );
end;
dy:=y2 - y1;
// ey1:=y1 shr poly_base_shift;
// ey2:=y2 shr poly_base_shift;
ey1:=shr_int32(y1 ,poly_base_shift );
ey2:=shr_int32(y2 ,poly_base_shift );
fy1:=y1 and poly_base_mask;
fy2:=y2 and poly_base_mask;
//everything is on a single hline
if ey1 = ey2 then
begin
render_hline(ey1 ,x1 ,fy1 ,x2 ,fy2 );
exit;
end;
//Vertical line - we have to calculate start and end cells,
//and then - the common values of the area and coverage for
//all cells of the line. We know exactly there's only one
//cell, so, we don't have to call render_hline().
incr:=1;
if dx = 0 then
begin
// ex:=x1 shr poly_base_shift;
ex:=shr_int32(x1 ,poly_base_shift );
two_fx:=(x1 - (ex shl poly_base_shift ) ) shl 1;
first :=poly_base_size;
if dy < 0 then
begin
first:=0;
incr :=-1;
end;
x_from:=x1;
//render_hline(ey1 ,x_from ,fy1 ,x_from ,first );
delta:=first - fy1;
inc(m_cur_cell.cover ,delta );
inc(m_cur_cell.area ,two_fx * delta );
inc(ey1 ,incr );
set_cur_cell(ex, ey1);
delta:=first + first - poly_base_size;
area :=two_fx * delta;
while ey1 <> ey2 do
begin
//render_hline(ey1 ,x_from ,poly_base_size - first ,x_from ,first );
m_cur_cell.cover:=delta;
m_cur_cell.area :=area;
inc(ey1 ,incr );
set_cur_cell(ex ,ey1 );
end;
//render_hline(ey1, x_from, poly_base_size - first, x_from, fy2);
delta:=fy2 - poly_base_size + first;
inc(m_cur_cell.cover ,delta );
inc(m_cur_cell.area ,two_fx * delta );
exit;
end;
//ok, we have to render several hlines
p :=(poly_base_size - fy1 ) * dx;
first:=poly_base_size;
if dy < 0 then
begin
p :=fy1 * dx;
first:=0;
incr :=-1;
dy :=-dy;
end;
delta:=p div dy;
m_d :=p mod dy;
if m_d < 0 then
begin
dec(delta );
inc(m_d ,dy );
end;
x_from:=x1 + delta;
render_hline(ey1 ,x1 ,fy1 ,x_from ,first );
inc(ey1 ,incr );
// set_cur_cell(x_from shr poly_base_shift ,ey1 );
set_cur_cell(shr_int32(x_from ,poly_base_shift ) ,ey1 );
if ey1 <> ey2 then
begin
p :=poly_base_size * dx;
lift:=p div dy;
rem :=p mod dy;
if rem < 0 then
begin
dec(lift );
inc(rem ,dy );
end;
dec(m_d ,dy );
while ey1 <> ey2 do
begin
delta:=lift;
inc(m_d ,rem );
if m_d >= 0 then
begin
dec(m_d ,dy );
inc(delta );
end;
x_to:=x_from + delta;
render_hline(ey1 ,x_from ,poly_base_size - first ,x_to ,first );
x_from:=x_to;
inc(ey1 ,incr );
// set_cur_cell(x_from shr poly_base_shift ,ey1 );
set_cur_cell(shr_int32(x_from ,poly_base_shift ) ,ey1 );
end;
end;
render_hline(ey1 ,x_from ,poly_base_size - first ,x2 ,fy2 );
end;
{ RENDER_HLINE }
procedure outline_aa.render_hline;
var
p ,
dx ,
ex1 ,
ex2 ,
fx1 ,
fx2 ,
delta ,
first ,
incr ,
lift ,
m_d ,
rem : int;
begin
// ex1:=x1 shr poly_base_shift;
// ex2:=x2 shr poly_base_shift;
ex1:=shr_int32(x1 ,poly_base_shift );
ex2:=shr_int32(x2 ,poly_base_shift );
fx1:=x1 and poly_base_mask;
fx2:=x2 and poly_base_mask;
//trivial case. Happens often
if y1 = y2 then
begin
set_cur_cell(ex2 ,ey );
exit;
end;
//everything is located in a single cell. That is easy!
if ex1 = ex2 then
begin
delta:=y2 - y1;
inc(m_cur_cell.cover ,delta );
inc(m_cur_cell.area ,(fx1 + fx2 ) * delta );
exit;
end;
//ok, we'll have to render a run of adjacent cells on the same
//hline...
p :=(poly_base_size - fx1 ) * (y2 - y1 );
first:=poly_base_size;
incr :=1;
dx :=x2 - x1;
if dx < 0 then
begin
p :=fx1 * (y2 - y1 );
first:=0;
incr :=-1;
dx :=-dx;
end;
delta:=p div dx;
m_d :=p mod dx;
if m_d < 0 then
begin
dec(delta );
inc(m_d ,dx );
end;
inc(m_cur_cell.cover ,delta );
inc(m_cur_cell.area ,(fx1 + first ) * delta );
inc(ex1 ,incr );
set_cur_cell(ex1 ,ey );
inc(y1 ,delta );
if ex1 <> ex2 then
begin
p :=poly_base_size * (y2 - y1 + delta );
lift:=p div dx;
rem :=p mod dx;
if rem < 0 then
begin
dec(lift );
inc(rem ,dx );
end;
dec(m_d ,dx );
while ex1 <> ex2 do
begin
delta:=lift;
inc(m_d ,rem );
if m_d >= 0 then
begin
dec(m_d ,dx );
inc(delta );
end;
inc(m_cur_cell.cover ,delta );
inc(m_cur_cell.area ,(poly_base_size ) * delta );
inc(y1 ,delta );
inc(ex1 ,incr );
set_cur_cell(ex1 ,ey );
end;
end;
delta:=y2 - y1;
inc(m_cur_cell.cover ,delta );
inc(m_cur_cell.area ,(fx2 + poly_base_size - first ) * delta );
end;
{ ALLOCATE_BLOCK }
procedure outline_aa.allocate_block;
var
new_cells : cell_aa_ptr_ptr;
begin
if m_cur_block >= m_num_blocks then
begin
if m_num_blocks >= m_max_blocks then
begin
agg_getmem(pointer(new_cells ) ,sizeof(cell_aa_ptr ) * (m_max_blocks + cell_block_pool ) );
if m_cells <> NIL then
begin
move(m_cells^ ,new_cells^ ,sizeof(cell_aa_ptr ) * m_max_blocks );
agg_freemem(pointer(m_cells ) ,sizeof(cell_aa_ptr ) * m_max_blocks );
end;
m_cells:=new_cells;
inc(m_max_blocks ,cell_block_pool );
end;
agg_getmem(
pointer(pointer(ptrcomp(m_cells ) + m_num_blocks * sizeof(cell_aa_ptr ) )^ ) ,
cell_block_size * sizeof(cell_aa ) );
inc(m_num_blocks );
end;
m_cur_cell_ptr:=cell_aa_ptr_ptr(ptrcomp(m_cells ) + m_cur_block * sizeof(cell_aa_ptr ) )^;
inc(m_cur_block );
end;
{ _MIN_X }
function outline_aa._min_x;
begin
result:=m_min_x;
end;
{ _MIN_Y }
function outline_aa._min_y;
begin
result:=m_min_y;
end;
{ _MAX_X }
function outline_aa._max_x;
begin
result:=m_max_x;
end;
{ _MAX_Y }
function outline_aa._max_y;
begin
result:=m_max_y;
end;
{ CONSTRUCT }
constructor scanline_hit_test.Construct;
begin
m_x :=x;
m_hit:=false;
end;
{ RESET_SPANS }
procedure scanline_hit_test.reset_spans;
begin
end;
{ FINALIZE }
procedure scanline_hit_test.finalize;
begin
end;
{ ADD_CELL }
procedure scanline_hit_test.add_cell;
begin
if m_x = x then
m_hit:=true;
end;
{ ADD_SPAN }
procedure scanline_hit_test.add_span;
begin
if (m_x >= x ) and
(m_x < x + len ) then
m_hit:=true;
end;
{ NUM_SPANS }
function scanline_hit_test.num_spans;
begin
result:=1;
end;
{ HIT }
function scanline_hit_test.hit;
begin
result:=m_hit;
end;
{ CONSTRUCT }
constructor rasterizer_scanline_aa.Construct;
var
i : integer;
begin
m_outline.Construct;
m_clip_box.Construct;
m_filling_rule:=fill_non_zero;
m_auto_close :=true;
m_clipped_start_x:=0;
m_clipped_start_y:=0;
m_start_x:=0;
m_start_y:=0;
m_prev_x :=0;
m_prev_y :=0;
m_prev_flags:=0;
m_status :=status_initial;
m_clipping :=false;
for i:=0 to aa_num - 1 do
m_gamma[i ]:=i;
XScale:=1;
end;
{ DESTRUCT }
destructor rasterizer_scanline_aa.Destruct;
begin
m_outline.Destruct;
end;
{ RESET }
procedure rasterizer_scanline_aa.reset;
begin
m_outline.reset;
m_status:=status_initial;
end;
{ FILLING_RULE }
procedure rasterizer_scanline_aa.filling_rule(filling_rule_: filling_rule_e);
begin
m_filling_rule:=filling_rule_;
end;
{ AUTO_CLOSE }
procedure rasterizer_scanline_aa.auto_close(flag: boolean);
begin
m_auto_close:=flag;
end;
{ CLIP_BOX }
procedure rasterizer_scanline_aa.clip_box(x1, y1, x2, y2: double);
begin
reset;
m_clip_box.x1:=poly_coord(x1 );
m_clip_box.y1:=poly_coord(y1 );
m_clip_box.x2:=poly_coord(x2 );
m_clip_box.y2:=poly_coord(y2 );
m_clip_box.normalize;
m_clipping:=true;
end;
{ GAMMA }
procedure rasterizer_scanline_aa.gamma(gamma_function: vertex_source_ptr);
var
i : int;
begin
for i:=0 to aa_num - 1 do
m_gamma[i ]:=
trunc(
gamma_function.func_operator_gamma(i / aa_mask ) * aa_mask + 0.5 );
end;
{ APPLY_GAMMA }
function rasterizer_scanline_aa.apply_gamma(cover: unsigned): unsigned;
begin
result:=m_gamma[cover ];
end;
{ MOVE_TO_NO_CLIP }
procedure rasterizer_scanline_aa.move_to_no_clip(x, y: int);
begin
if (m_status = status_line_to ) and
m_auto_close then
close_polygon_no_clip;
m_outline.move_to(x * XScale ,y );
m_clipped_start_x:=x;
m_clipped_start_y:=y;
m_status:=status_line_to;
end;
{ LINE_TO_NO_CLIP }
procedure rasterizer_scanline_aa.line_to_no_clip(x, y: int);
begin
if m_status <> status_initial then
begin
m_outline.line_to(x * XScale ,y );
m_status:=status_line_to;
end;
end;
{ CLOSE_POLYGON }
procedure rasterizer_scanline_aa.close_polygon;
begin
if m_clipping then
clip_segment(m_start_x ,m_start_y );
if m_auto_close then
close_polygon_no_clip;
end;
{ CLOSE_POLYGON_NO_CLIP }
procedure rasterizer_scanline_aa.close_polygon_no_clip;
begin
if m_status = status_line_to then
begin
m_outline.line_to(m_clipped_start_x * XScale ,m_clipped_start_y );
m_status:=status_closed;
end;
end;
{ CLIP_SEGMENT }
procedure rasterizer_scanline_aa.clip_segment(x, y: int);
var
flags ,n : unsigned;
cx ,cy : array[0..3 ] of int;
px ,py : int_ptr;
begin
flags:=clipping_flags_int(x ,y ,@m_clip_box );
if m_prev_flags = flags then
if flags = 0 then
if m_status = status_initial then
move_to_no_clip(x ,y )
else
line_to_no_clip(x ,y )
else
else
begin
n:=clip_liang_barsky_int(m_prev_x ,m_prev_y ,x ,y ,@m_clip_box ,@cx[0 ] ,@cy[0 ] );
px:=@cx[0 ];
py:=@cy[0 ];
while n > 0 do
begin
if m_status = status_initial then
move_to_no_clip(px^ ,py^ )
else
line_to_no_clip(px^ ,py^ );
inc(ptrcomp(px ) ,sizeof(int ) );
inc(ptrcomp(py ) ,sizeof(int ) );
dec(n );
end;
end;
m_prev_flags:=flags;
m_prev_x:=x;
m_prev_y:=y;
end;
{ MOVE_TO_D }
procedure rasterizer_scanline_aa.move_to_d(x, y: double);
begin
move_to(poly_coord(x ) ,poly_coord(y ) );
end;
{ LINE_TO_D }
procedure rasterizer_scanline_aa.line_to_d(x, y: double);
begin
line_to(poly_coord(x ) ,poly_coord(y ) );
end;
{ MOVE_TO }
procedure rasterizer_scanline_aa.move_to(x, y: int);
begin
if m_clipping then
begin
if m_outline.sorted then
reset;
if (m_status = status_line_to ) and
m_auto_close then
close_polygon;
m_prev_x :=x;
m_start_x:=x;
m_prev_y :=y;
m_start_y:=y;
m_status :=status_initial;
m_prev_flags:=clipping_flags_int(x ,y ,@m_clip_box );
if m_prev_flags = 0 then
move_to_no_clip(x ,y );
end
else
move_to_no_clip(x ,y );
end;
{ LINE_TO }
procedure rasterizer_scanline_aa.line_to(x, y: int);
begin
if m_clipping then
clip_segment(x ,y )
else
line_to_no_clip(x ,y );
end;
{ SORT }
procedure rasterizer_scanline_aa.sort;
begin
m_outline.sort_cells;
end;
{ REWIND_SCANLINES }
function rasterizer_scanline_aa.rewind_scanlines: boolean;
begin
if m_auto_close then
close_polygon;
m_outline.sort_cells;
if m_outline.total_cells = 0 then
begin
result:=false;
exit;
end;
m_cur_y:=m_outline._min_y;
result :=true;
end;
{ SWEEP_SCANLINE }
function rasterizer_scanline_aa.sweep_scanline(sl: scanline_ptr): boolean;
var
x ,
area ,
cover : int;
alpha : unsigned;
cells : cell_aa_ptr_ptr;
cur_cell : cell_aa_ptr;
num_cells : unsigned;
begin
repeat
if m_cur_y > m_outline._max_y then
begin
result:=false;
exit;
end;
sl.reset_spans;
num_cells:=m_outline.scanline_num_cells(m_cur_y );
cells :=m_outline.scanline_cells (m_cur_y );
cover:=0;
while num_cells > 0 do
begin
cur_cell:=cells^;
x :=cur_cell.x;
area:=cur_cell.area;
inc(cover ,cur_cell.cover );
//accumulate all cells with the same X
dec(num_cells );
while num_cells > 0 do
begin
inc(ptrcomp(cells ) ,sizeof(cell_aa_ptr ) );
cur_cell:=cells^;
if cur_cell.x <> x then
break;
inc(area ,cur_cell.area );
inc(cover ,cur_cell.cover );
dec(num_cells );
end;
if area <> 0 then
begin
alpha:=calculate_alpha((cover shl (poly_base_shift + 1 ) ) - area );
if alpha <> 0 then
sl.add_cell(x ,alpha );
inc(x );
end;
if (num_cells <> 0 ) and
(cur_cell.x > x ) then
begin
alpha:=calculate_alpha(cover shl (poly_base_shift + 1 ) );
if alpha <> 0 then
sl.add_span(x ,cur_cell.x - x ,alpha );
end;
end;
if boolean(sl.num_spans ) then
break;
inc(m_cur_y );
until false;
sl.finalize(m_cur_y);
inc(m_cur_y );
result:=true;
end;
{ NAVIGATE_SCANLINE }
function rasterizer_scanline_aa.navigate_scanline(y: int): boolean;
begin
if m_auto_close then
close_polygon;
m_outline.sort_cells;
if (m_outline.total_cells = 0 ) or
(y < m_outline._min_y ) or
(y > m_outline._max_y ) then
begin
result:=false;
exit;
end;
m_cur_y:=y;
result :=true;
end;
{ HIT_TEST }
function rasterizer_scanline_aa.hit_test(tx, ty: int): boolean;
var
sl : scanline_hit_test;
begin
if not navigate_scanline(ty ) then
begin
result:=false;
exit;
end;
sl.Construct (tx );
sweep_scanline(@sl );
result:=sl.hit;
end;
{ _MIN_X }
function rasterizer_scanline_aa._min_x: int;
begin
result:=m_outline._min_x;
end;
{ _MIN_Y }
function rasterizer_scanline_aa._min_y: int;
begin
result:=m_outline._min_y;
end;
{ _MAX_X }
function rasterizer_scanline_aa._max_x: int;
begin
result:=m_outline._max_x;
end;
{ _MAX_Y }
function rasterizer_scanline_aa._max_y: int;
begin
result:=m_outline._max_y;
end;
{ CALCULATE_ALPHA }
function rasterizer_scanline_aa.calculate_alpha(area: int): unsigned;
var
cover : system.integer;
begin
// 1: cover:=area shr (poly_base_shift * 2 + 1 - aa_shift );
// 2: cover:=round(area / (1 shl (poly_base_shift * 2 + 1 - aa_shift ) ) );
cover:=shr_int32(area ,poly_base_shift * 2 + 1 - aa_shift );
if cover < 0 then
cover:=-cover;
if m_filling_rule = fill_even_odd then
begin
cover:=cover and aa_2mask;
if cover > aa_num then
cover:=aa_2num - cover;
end;
if cover > aa_mask then
cover:=aa_mask;
result:=m_gamma[cover ];
end;
{ ADD_PATH }
procedure rasterizer_scanline_aa.add_path(vs: vertex_source_ptr; path_id: unsigned);
var
cmd : unsigned;
x ,y : double;
begin
vs.rewind(path_id );
cmd:=vs.vertex(@x ,@y );
while not is_stop(cmd ) do
begin
add_vertex(x ,y ,cmd );
cmd:=vs.vertex(@x ,@y );
end;
end;
{ ADD_VERTEX }
procedure rasterizer_scanline_aa.add_vertex(x, y: double; cmd: unsigned);
begin
if is_close(cmd ) then
close_polygon
else
if is_move_to(cmd ) then
move_to(poly_coord(x ) ,poly_coord(y ) )
else
if is_vertex(cmd ) then
line_to(poly_coord(x ) ,poly_coord(y ) );
end;
{ POLY_COORD }
function poly_coord;
begin
result:=trunc(c * poly_base_size );
end;
END.