mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-13 08:09:22 +02:00
1825 lines
35 KiB
ObjectPascal
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.
|
|
|