//----------------------------------------------------------------------------
// 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.

