//---------------------------------------------------------------------------- // 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. // // [Pascal Port History] ----------------------------------------------------- // // 16.10.2007-Milano: Unit port establishment & Finished OK // { agg_rasterizer_cells_aa.pas } unit agg_rasterizer_cells_aa ; INTERFACE {$I agg_mode.inc } uses agg_basics , agg_math , agg_array , agg_scanline ; { GLOBAL VARIABLES & CONSTANTS } const 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; { TYPES DEFINITION } type // 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_style_aa_ptr_ptr_ptr = ^cell_style_aa_ptr_ptr; cell_style_aa_ptr_ptr = ^cell_style_aa_ptr; cell_style_aa_ptr = ^cell_style_aa; cell_style_aa = object x ,y ,cover ,area : int; left ,right : int16; procedure initial; procedure style (c : cell_style_aa_ptr ); function not_equal(ex ,ey : int; c : cell_style_aa_ptr ) : int; end; cell_block_scale_e = int; sorted_y_ptr = ^sorted_y; sorted_y = record start , num : unsigned; end; { cell_type_ptr_ptr_ptr = ^cell_type_ptr_ptr; cell_type_ptr_ptr = ^cell_type_ptr; cell_type_ptr = ^cell_type; cell_type = cell_style_aa; } // An internal class that implements the main rasterization algorithm. // Used in the rasterizer. Should not be used direcly. rasterizer_cells_aa_ptr = ^rasterizer_cells_aa; rasterizer_cells_aa = object private m_num_blocks , m_max_blocks , m_curr_block , m_num_cells : unsigned; m_cells : cell_style_aa_ptr_ptr; m_curr_cell_ptr : cell_style_aa_ptr; m_sorted_cells , m_sorted_y : pod_vector; m_curr_cell , m_style_cell : cell_style_aa; m_min_x , m_min_y , m_max_x , m_max_y : int; m_sorted : boolean; public constructor Construct; destructor Destruct; procedure reset; procedure style(style_cell : cell_style_aa_ptr ); procedure line (x1 ,y1 ,x2 ,y2 : int ); function min_x : int; function min_y : int; function max_x : int; function max_y : int; procedure sort_cells; function total_cells : unsigned; function scanline_num_cells(y : unsigned ) : unsigned; function scanline_cells (y : unsigned ) : cell_style_aa_ptr_ptr; function sorted : boolean; private procedure set_curr_cell(x ,y : int ); procedure add_curr_cell; procedure render_hline(ey ,x1 ,y1 ,x2 ,y2 : int ); procedure allocate_block; end; //------------------------------------------------------scanline_hit_test scanline_hit_test = object(scanline ) private m_x : int; m_hit : boolean; public 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; { GLOBAL PROCEDURES } IMPLEMENTATION { LOCAL VARIABLES & CONSTANTS } { UNIT IMPLEMENTATION } { INITIAL } procedure cell_style_aa.initial; begin x :=$7FFFFFFF; y :=$7FFFFFFF; cover:=0; area :=0; left :=-1; right:=-1; end; { STYLE } procedure cell_style_aa.style(c : cell_style_aa_ptr ); begin left :=c.left; right:=c.right; end; { NOT_EQUAL } function cell_style_aa.not_equal(ex ,ey : int; c : cell_style_aa_ptr ) : int; begin result:=(ex - x ) or (ey - y ) or (left - c.left ) or (right - c.right ); end; { CONSTRUCT } constructor rasterizer_cells_aa.Construct; begin m_num_blocks:=0; m_max_blocks:=0; m_curr_block:=0; m_num_cells :=0; m_cells :=NIL; m_curr_cell_ptr:=NIL; m_sorted_cells.Construct(sizeof(cell_style_aa_ptr ) ); m_sorted_y.Construct(sizeof(sorted_y ) ); m_min_x :=$7FFFFFFF; m_min_y :=$7FFFFFFF; m_max_x :=-$7FFFFFFF; m_max_y :=-$7FFFFFFF; m_sorted:=false; m_style_cell.initial; m_curr_cell.initial; end; { DESTRUCT } destructor rasterizer_cells_aa.Destruct; var ptr : cell_style_aa_ptr_ptr; begin m_sorted_cells.Destruct; m_sorted_y.Destruct; if m_num_blocks <> 0 then begin ptr:=cell_style_aa_ptr_ptr(ptrcomp(m_cells ) + (m_num_blocks - 1 ) * sizeof(cell_style_aa_ptr ) ); while m_num_blocks <> 0 do begin dec(m_num_blocks ); agg_freemem(pointer(ptr^ ) ,cell_block_size * sizeof(cell_style_aa ) ); dec(ptrcomp(ptr ) ,sizeof(cell_style_aa_ptr ) ); end; agg_freemem(pointer(m_cells ) ,m_max_blocks * sizeof(cell_style_aa_ptr ) ); end; end; { RESET } procedure rasterizer_cells_aa.reset; begin m_num_cells :=0; m_curr_block:=0; m_curr_cell.initial; m_style_cell.initial; m_sorted:=false; m_min_x :=$7FFFFFFF; m_min_y :=$7FFFFFFF; m_max_x :=-$7FFFFFFF; m_max_y :=-$7FFFFFFF; end; { STYLE } procedure rasterizer_cells_aa.style(style_cell : cell_style_aa_ptr ); begin m_style_cell.style(style_cell ); end; { LINE } procedure rasterizer_cells_aa.line(x1 ,y1 ,x2 ,y2 : int ); const dx_limit = 16384 shl poly_subpixel_shift; var dx ,cx ,cy ,dy ,ex1 ,ex2 ,ey1 ,ey2 ,fy1 ,fy2 ,ex ,two_fx ,area , x_from ,x_to ,p ,rem ,mod_ ,lift ,delta ,first ,incr : int; begin dx:=x2 - x1; if (dx >= dx_limit ) or (dx <= -dx_limit ) then begin cx:=shr_int32(x1 + x2 ,1 ); cy:=shr_int32(y1 + y2 ,1 ); line(x1 ,y1 ,cx ,cy ); line(cx ,cy ,x2 ,y2 ); end; dy := y2 - y1; ex1:=shr_int32(x1 ,poly_subpixel_shift ); ex2:=shr_int32(x2 ,poly_subpixel_shift ); ey1:=shr_int32(y1 ,poly_subpixel_shift ); ey2:=shr_int32(y2 ,poly_subpixel_shift ); fy1:=y1 and poly_subpixel_mask; fy2:=y2 and poly_subpixel_mask; if ex1 < m_min_x then m_min_x:=ex1; if ex1 > m_max_x then m_max_x:=ex1; if ey1 < m_min_y then m_min_y:=ey1; if ey1 > m_max_y then m_max_y:=ey1; if ex2 < m_min_x then m_min_x:=ex2; if ex2 > m_max_x then m_max_x:=ex2; if ey2 < m_min_y then m_min_y:=ey2; if ey2 > m_max_y then m_max_y:=ey2; set_curr_cell(ex1 ,ey1 ); // 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 :=shr_int32(x1 ,poly_subpixel_shift ); two_fx:=(x1 - (ex shl poly_subpixel_shift ) ) shl 1; first :=poly_subpixel_scale; 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_curr_cell.cover ,delta ); inc(m_curr_cell.area ,two_fx * delta ); inc(ey1 ,incr ); set_curr_cell(ex ,ey1 ); delta:=first + first - poly_subpixel_scale; area :=two_fx * delta; while ey1 <> ey2 do begin // render_hline(ey1 ,x_from ,poly_subpixel_scale - first ,x_from ,first ); m_curr_cell.cover:=delta; m_curr_cell.area :=area; inc(ey1 ,incr ); set_curr_cell(ex ,ey1 ); end; // render_hline(ey1 ,x_from ,poly_subpixel_scale - first ,x_from ,fy2 ); delta:=fy2 - poly_subpixel_scale + first; inc(m_curr_cell.cover ,delta ); inc(m_curr_cell.area ,two_fx * delta ); exit; end; // ok, we have to render several hlines p :=(poly_subpixel_scale - fy1 ) * dx; first:=poly_subpixel_scale; if dy < 0 then begin p :=fy1 * dx; first:=0; incr :=-1; dy :=-dy; end; delta:=p div dy; mod_ :=p mod dy; if mod_ < 0 then begin dec(delta ); inc(mod_,dy ); end; x_from:=x1 + delta; render_hline(ey1 ,x1 ,fy1 ,x_from ,first ); inc(ey1 ,incr ); set_curr_cell(shr_int32(x_from ,poly_subpixel_shift ) ,ey1 ); if ey1 <> ey2 then begin p :=poly_subpixel_scale * dx; lift:=p div dy; rem :=p mod dy; if rem < 0 then begin dec(lift ); inc(rem ,dy ); end; dec(mod_ ,dy ); while ey1 <> ey2 do begin delta:=lift; inc(mod_ ,rem ); if mod_ >= 0 then begin dec(mod_ ,dy ); inc(delta ); end; x_to:=x_from + delta; render_hline(ey1 ,x_from ,poly_subpixel_scale - first ,x_to ,first ); x_from:=x_to; inc(ey1 ,incr ); set_curr_cell(shr_int32(x_from ,poly_subpixel_shift ) ,ey1 ); end; end; render_hline(ey1 ,x_from ,poly_subpixel_scale - first ,x2 ,fy2 ); end; { MIN_X } function rasterizer_cells_aa.min_x : int; begin result:=m_min_x; end; { MIN_Y } function rasterizer_cells_aa.min_y : int; begin result:=m_min_y; end; { MAX_X } function rasterizer_cells_aa.max_x : int; begin result:=m_max_x; end; { MAX_Y } function rasterizer_cells_aa.max_y : int; begin result:=m_max_y; end; { swap_cells } procedure swap_cells(a ,b : pointer ); var temp : pointer; begin temp :=pointer(a^ ); pointer(a^ ):=pointer(b^ ); pointer(b^ ):=temp; end; const qsort_threshold = 9; { qsort_cells } procedure qsort_cells(start : cell_style_aa_ptr_ptr; num : unsigned ); var stack : array[0..79 ] of cell_style_aa_ptr_ptr; top : cell_style_aa_ptr_ptr_ptr; limit , base : cell_style_aa_ptr_ptr; len ,x : int; i ,j ,pivot : cell_style_aa_ptr_ptr; begin limit:=cell_style_aa_ptr_ptr(ptrcomp(start ) + num * sizeof(cell_style_aa_ptr ) ); base :=start; top :=@stack[0 ]; repeat len:=(ptrcomp(limit ) - ptrcomp(base ) ) div sizeof(cell_style_aa_ptr ); if len > qsort_threshold then begin // we use base + len/2 as the pivot pivot:=cell_style_aa_ptr_ptr(ptrcomp(base ) + (len div 2 ) * sizeof(cell_style_aa_ptr ) ); swap_cells(base ,pivot ); i:=cell_style_aa_ptr_ptr(ptrcomp(base ) + sizeof(cell_style_aa_ptr ) ); j:=cell_style_aa_ptr_ptr(ptrcomp(limit ) - sizeof(cell_style_aa_ptr ) ); // now ensure that *i <= *base <= *j if j^^.x < i^^.x then swap_cells(i ,j ); if base^^.x < i^^.x then swap_cells(base ,i ); if j^^.x < base^^.x then swap_cells(base ,j ); repeat x:=base^^.x; repeat inc(ptrcomp(i ) ,sizeof(cell_style_aa_ptr ) ); until i^^.x >= x; repeat dec(ptrcomp(j ) ,sizeof(cell_style_aa_ptr ) ); until x >= j^^.x; if ptrcomp(i ) > ptrcomp(j ) then break; swap_cells(i ,j ); until false; swap_cells(base ,j ); // now, push the largest sub-array if ptrcomp(j ) - ptrcomp(base ) > ptrcomp(limit ) - ptrcomp(i ) then begin top^:=base; cell_style_aa_ptr_ptr_ptr(ptrcomp(top ) + sizeof(cell_style_aa_ptr_ptr ) )^:=j; base:=i; end else begin top^:=i; cell_style_aa_ptr_ptr_ptr(ptrcomp(top ) + sizeof(cell_style_aa_ptr_ptr ) )^:=limit; limit:=j; end; inc(ptrcomp(top ) ,2 * sizeof(cell_style_aa_ptr_ptr ) ); end else begin // the sub-array is small, perform insertion sort j:=base; i:=cell_style_aa_ptr_ptr(ptrcomp(j ) + sizeof(cell_style_aa_ptr ) ); while ptrcomp(i ) < ptrcomp(limit ) do begin while cell_style_aa_ptr_ptr(ptrcomp(j ) + sizeof(cell_style_aa_ptr ) )^^.x < j^^.x do begin swap_cells(cell_style_aa_ptr_ptr(ptrcomp(j ) + sizeof(cell_style_aa_ptr ) ) ,j ); if ptrcomp(j ) = ptrcomp(base ) then break; dec(ptrcomp(j ) ,sizeof(cell_style_aa_ptr ) ); end; j:=i; inc(ptrcomp(i ) ,sizeof(cell_style_aa_ptr ) ); end; if ptrcomp(top ) > ptrcomp(@stack[0 ] ) then begin dec(ptrcomp(top ) ,2 * sizeof(cell_style_aa_ptr_ptr ) ); base :=top^; limit:=cell_style_aa_ptr_ptr_ptr(ptrcomp(top ) + sizeof(cell_style_aa_ptr_ptr ) )^; end else break; end; until false; end; { SORT_CELLS } procedure rasterizer_cells_aa.sort_cells; var block_ptr : cell_style_aa_ptr_ptr; cell_ptr : cell_style_aa_ptr; nb ,i ,start ,v : unsigned; curr_y : sorted_y_ptr; begin //Perform sort only the first time. if m_sorted then exit; add_curr_cell; m_curr_cell.x :=$7FFFFFFF; m_curr_cell.y :=$7FFFFFFF; m_curr_cell.cover:=0; m_curr_cell.area :=0; 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_style_aa_ptr ) ); i:=cell_block_size; while i <> 0 do begin dec(i ); inc(sorted_y_ptr(m_sorted_y.array_operator(cell_ptr.y - m_min_y ) ).start ); inc(ptrcomp(cell_ptr ) ,sizeof(cell_style_aa ) ); end; end; cell_ptr:=block_ptr^; inc(ptrcomp(block_ptr ) ,sizeof(cell_style_aa_ptr ) ); i:=m_num_cells and cell_block_mask; while i <> 0 do begin dec(i ); inc(sorted_y_ptr(m_sorted_y.array_operator(cell_ptr.y - m_min_y ) ).start ); inc(ptrcomp(cell_ptr ) ,sizeof(cell_style_aa ) ); end; // Convert the Y-histogram into the array of starting indexes start:=0; i :=0; while i < m_sorted_y.size do begin v:=sorted_y_ptr(m_sorted_y.array_operator(i ) ).start; sorted_y_ptr(m_sorted_y.array_operator(i ) ).start:=start; inc(start ,v ); inc(i ); 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_style_aa_ptr ) ); i:=cell_block_size; while i <> 0 do begin dec(i ); curr_y:=sorted_y_ptr(m_sorted_y.array_operator(cell_ptr.y - m_min_y ) ); cell_style_aa_ptr_ptr(m_sorted_cells.array_operator(curr_y.start + curr_y.num ) )^:=cell_ptr; inc(curr_y.num ); inc(ptrcomp(cell_ptr ) ,sizeof(cell_style_aa ) ); end; end; cell_ptr:=block_ptr^; inc(ptrcomp(block_ptr ) ,sizeof(cell_style_aa_ptr ) ); i:=m_num_cells and cell_block_mask; while i <> 0 do begin dec(i ); curr_y:=sorted_y_ptr(m_sorted_y.array_operator(cell_ptr.y - m_min_y ) ); cell_style_aa_ptr_ptr(m_sorted_cells.array_operator(curr_y.start + curr_y.num ) )^:=cell_ptr; inc(curr_y.num ); inc(ptrcomp(cell_ptr ) ,sizeof(cell_style_aa ) ); end; // Finally arrange the X-arrays i:=0; while i < m_sorted_y.size do begin curr_y:=sorted_y_ptr(m_sorted_y.array_operator(i ) ); if curr_y.num <> 0 then qsort_cells( cell_style_aa_ptr_ptr(ptrcomp(m_sorted_cells.data ) + curr_y.start * sizeof(cell_style_aa_ptr ) ) , curr_y.num ); inc(i ); end; m_sorted:=true; end; { TOTAL_CELLS } function rasterizer_cells_aa.total_cells : unsigned; begin result:=m_num_cells; end; { SCANLINE_NUM_CELLS } function rasterizer_cells_aa.scanline_num_cells(y : unsigned ) : unsigned; begin result:=sorted_y_ptr(m_sorted_y.array_operator(y - m_min_y ) ).num; end; { SCANLINE_CELLS } function rasterizer_cells_aa.scanline_cells(y : unsigned ) : cell_style_aa_ptr_ptr; begin result:=cell_style_aa_ptr_ptr(ptrcomp(m_sorted_cells.data ) + sorted_y_ptr(m_sorted_y.array_operator(y - m_min_y ) ).start * sizeof(cell_style_aa_ptr ) ); end; { SORTED } function rasterizer_cells_aa.sorted : boolean; begin result:=m_sorted; end; { SET_CURR_CELL } procedure rasterizer_cells_aa.set_curr_cell(x ,y : int ); begin if m_curr_cell.not_equal(x ,y ,@m_style_cell ) <> 0 then begin add_curr_cell; m_curr_cell.style(@m_style_cell ); m_curr_cell.x :=x; m_curr_cell.y :=y; m_curr_cell.cover:=0; m_curr_cell.area :=0; end; end; { ADD_CURR_CELL } procedure rasterizer_cells_aa.add_curr_cell; begin if m_curr_cell.area or m_curr_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_curr_cell_ptr^:=m_curr_cell; inc(ptrcomp(m_curr_cell_ptr ) ,sizeof(cell_style_aa ) ); inc(m_num_cells ); end; end; { RENDER_HLINE } procedure rasterizer_cells_aa.render_hline(ey ,x1 ,y1 ,x2 ,y2 : int ); var ex1 ,ex2 ,fx1 ,fx2 ,delta ,p ,first ,dx ,incr ,lift ,mod_ ,rem : int; begin ex1:=shr_int32(x1 ,poly_subpixel_shift ); ex2:=shr_int32(x2 ,poly_subpixel_shift ); fx1:=x1 and poly_subpixel_mask; fx2:=x2 and poly_subpixel_mask; // trivial case. Happens often if y1 = y2 then begin set_curr_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_curr_cell.cover ,delta ); inc(m_curr_cell.area ,(fx1 + fx2 ) * delta ); exit; end; // ok, we'll have to render a run of adjacent cells on the same // hline... p :=(poly_subpixel_scale - fx1 ) * (y2 - y1 ); first:=poly_subpixel_scale; 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; mod_ :=p mod dx; if mod_ < 0 then begin dec(delta ); inc(mod_ ,dx ); end; inc(m_curr_cell.cover ,delta ); inc(m_curr_cell.area ,(fx1 + first ) * delta ); inc(ex1 ,incr ); set_curr_cell(ex1 ,ey ); inc(y1 ,delta ); if ex1 <> ex2 then begin p :=poly_subpixel_scale * (y2 - y1 + delta ); lift:=p div dx; rem :=p mod dx; if rem < 0 then begin dec(lift ); inc(rem ,dx ); end; dec(mod_ ,dx ); while ex1 <> ex2 do begin delta:=lift; inc(mod_ ,rem ); if mod_ >= 0 then begin dec(mod_ ,dx ); inc(delta ); end; inc(m_curr_cell.cover ,delta ); inc(m_curr_cell.area ,poly_subpixel_scale * delta ); inc(y1 ,delta ); inc(ex1 ,incr ); set_curr_cell(ex1 ,ey ); end; end; delta:=y2 - y1; inc(m_curr_cell.cover ,delta ); inc(m_curr_cell.area ,(fx2 + poly_subpixel_scale - first ) * delta ); end; { ALLOCATE_BLOCK } procedure rasterizer_cells_aa.allocate_block; var new_cells : cell_style_aa_ptr_ptr; begin if m_curr_block >= m_num_blocks then begin if m_num_blocks >= m_max_blocks then begin agg_getmem(pointer(new_cells ) ,(m_max_blocks + cell_block_pool ) * sizeof(cell_style_aa_ptr ) ); if m_cells <> NIL then begin move(m_cells^ ,new_cells^ ,m_max_blocks * sizeof(cell_style_aa_ptr ) ); agg_freemem(pointer(m_cells ) ,m_max_blocks * sizeof(cell_style_aa_ptr ) ); end; m_cells:=new_cells; inc(m_max_blocks ,cell_block_pool ); end; agg_getmem( pointer(cell_style_aa_ptr_ptr(ptrcomp(m_cells ) + m_num_blocks * sizeof(cell_style_aa_ptr ) )^ ) , cell_block_size * sizeof(cell_style_aa ) ); inc(m_num_blocks ); end; m_curr_cell_ptr:=cell_style_aa_ptr_ptr(ptrcomp(m_cells ) + m_curr_block * sizeof(cell_style_aa_ptr ) )^; inc(m_curr_block ); 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; END.