lazarus/components/aggpas/src/agg_path_storage.pas
mattias 36a2b1ea07 added aggpas
git-svn-id: trunk@21942 -
2009-10-01 12:24:32 +00:00

1263 lines
25 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.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
// mcseemagg@yahoo.com
// http://www.antigrain.com
//
//----------------------------------------------------------------------------
//
// Class path_storage
//
// [Pascal Port History] -----------------------------------------------------
//
// 09.10.2007-Milano: Path Affine Transformations
// 23.06.2006-Milano: ptrcomp adjustments
// 19.12.2005-Milano: Unit port establishment
//
{ agg_path_storage.pas }
unit
agg_path_storage ;
INTERFACE
{$I agg_mode.inc }
uses
agg_basics ,
agg_math ,
agg_bezier_arc ,
agg_vertex_source ,
agg_trans_affine ;
{ TYPES DEFINITION }
// Allocation parameters
const
block_shift = 8;
block_size = 1 shl block_shift;
block_mask = block_size - 1;
block_pool = 256;
type
path_storage_ptr = ^path_storage;
ps_vertex_source_ptr = ^ps_vertex_source;
ps_vertex_source = object(vertex_source )
m_path : path_storage_ptr;
m_vertex_idx : unsigned;
constructor Construct; overload;
constructor Construct(p : path_storage_ptr ); overload;
procedure rewind(path_id : unsigned ); virtual;
function vertex(x ,y : double_ptr ) : unsigned; virtual;
end;
//------------------------------------------------------------path_storage
// A container to store vertices with their flags.
// A path consists of a number of contours separated with "move_to"
// commands. The path storage can keep and maintain more than one
// path.
// To navigate to the beginning of a particular path, use rewind(path_id);
// Where path_id is what start_new_path() returns. So, when you call
// start_new_path() you need to store its return value somewhere else
// to navigate to the path afterwards.
path_storage = object(vertex_source )
m_total_vertices ,
m_total_blocks ,
m_max_blocks : unsigned;
m_coord_blocks : double_ptr_ptr;
m_cmd_blocks : int8u_ptr_ptr;
m_iterator : unsigned;
constructor Construct; overload;
constructor Construct(ps : path_storage_ptr ); overload;
destructor Destruct; virtual;
procedure remove_all; virtual;
function last_vertex(x ,y : double_ptr ) : unsigned;
function prev_vertex(x ,y : double_ptr ) : unsigned;
function last_x : double;
function last_y : double;
procedure rel_to_abs(x ,y : double_ptr );
procedure move_to (x ,y : double );
procedure move_rel(dx ,dy : double );
procedure line_to (x ,y : double );
procedure line_rel(dx ,dy : double );
procedure hline_to (x : double );
procedure hline_rel(dx : double );
procedure vline_to (y : double );
procedure vline_rel(dy : double );
procedure arc_to (rx ,ry ,angle : double; large_arc_flag ,sweep_flag : boolean; x ,y : double );
procedure arc_rel(rx ,ry ,angle : double; large_arc_flag ,sweep_flag : boolean; dx ,dy : double );
procedure curve3 (x_ctrl ,y_ctrl ,x_to ,y_to : double ); overload;
procedure curve3_rel(dx_ctrl ,dy_ctrl ,dx_to ,dy_to : double ); overload;
procedure curve3 (x_to ,y_to : double ); overload;
procedure curve3_rel(dx_to ,dy_to : double ); overload;
procedure curve4 (x_ctrl1 ,y_ctrl1 ,x_ctrl2 ,y_ctrl2 ,x_to ,y_to : double ); overload;
procedure curve4_rel(dx_ctrl1 ,dy_ctrl1 ,dx_ctrl2 ,dy_ctrl2 ,dx_to ,dy_to : double ); overload;
procedure curve4 (x_ctrl2 ,y_ctrl2 ,x_to ,y_to : double ); overload;
procedure curve4_rel(dx_ctrl2 ,dy_ctrl2 ,dx_to ,dy_to : double ); overload;
procedure end_poly (flags : unsigned = path_flags_close );
procedure close_polygon(flags : unsigned = path_flags_none );
procedure add_poly(vertices : double_2_ptr; num : unsigned; solid_path : boolean = false; end_flags : unsigned = path_flags_none );
procedure add_path(vs : vertex_source_ptr; path_id : unsigned = 0; solid_path : boolean = true );
function start_new_path : unsigned;
procedure copy_from(ps : path_storage_ptr );
function total_vertices : unsigned;
function vertex_(idx : unsigned; x ,y : double_ptr ) : unsigned;
function command(idx : unsigned ) : unsigned;
procedure rewind (path_id : unsigned ); virtual;
function vertex (x ,y : double_ptr ) : unsigned; virtual;
// Arrange the orientation of a polygon, all polygons in a path,
// or in all paths. After calling arrange_orientations() or
// arrange_orientations_all_paths(), all the polygons will have
// the same orientation, i.e. path_flags_cw or path_flags_ccw
function arrange_polygon_orientation (start ,orientation : unsigned ) : unsigned;
function arrange_orientations (start ,orientation : unsigned ) : unsigned;
procedure arrange_orientations_all_paths(orientation : unsigned );
// Flip all the vertices horizontally or vertically
procedure flip_x(x1 ,x2 : double );
procedure flip_y(y1 ,y2 : double );
// This function adds a vertex with its flags directly. Since there's no
// checking for errors, keeping proper path integrity is the responsibility
// of the caller. It can be said the function is "not very public".
procedure add_vertex(x ,y : double; cmd : unsigned ); virtual;
// Allows you to modify vertex coordinates. The caller must know
// the index of the vertex.
procedure modify_vertex(idx : unsigned; x ,y : double );
// Allows you to modify vertex command. The caller must know
// the index of the vertex.
procedure modify_command(idx ,cmd : unsigned );
// Path Affine Transformations
procedure transform (trans : trans_affine_ptr; path_id : unsigned = 0 );
procedure transform_all_paths(trans : trans_affine_ptr );
// Private
procedure allocate_block(nb : unsigned );
function storage_ptrs (xy_ptr : double_ptr_ptr ) : int8u_ptr;
function perceive_polygon_orientation(start ,end_ : unsigned ) : unsigned;
procedure invert_polygon(start ,end_ : unsigned ); overload;
procedure invert_polygon(start : unsigned ); overload;
// from 2.4
procedure concat_path(vs : vertex_source_ptr; path_id : unsigned = 0 );
end;
{ GLOBAL PROCEDURES }
IMPLEMENTATION
{ LOCAL VARIABLES & CONSTANTS }
{ UNIT IMPLEMENTATION }
{ CONSTRUCT }
constructor ps_vertex_source.Construct;
begin
m_path:=NIL;
m_vertex_idx:=0;
end;
{ CONSTRUCT }
constructor ps_vertex_source.Construct(p : path_storage_ptr );
begin
m_path:=p;
m_vertex_idx:=0;
end;
{ REWIND }
procedure ps_vertex_source.rewind;
begin
m_vertex_idx:=path_id;
end;
{ VERTEX }
function ps_vertex_source.vertex;
begin
if m_vertex_idx < m_path.total_vertices then
begin
result:=m_path.vertex_(m_vertex_idx ,x ,y );
inc(m_vertex_idx );
end
else
result:=path_cmd_stop;
end;
{ CONSTRUCT }
constructor path_storage.Construct;
begin
m_total_vertices:=0;
m_total_blocks :=0;
m_max_blocks :=0;
m_coord_blocks:=NIL;
m_cmd_blocks :=NIL;
m_iterator:=0;
end;
{ CONSTRUCT }
constructor path_storage.Construct(ps : path_storage_ptr );
begin
m_total_vertices:=0;
m_total_blocks :=0;
m_max_blocks :=0;
m_coord_blocks:=NIL;
m_cmd_blocks :=NIL;
m_iterator:=0;
copy_from(ps );
end;
{ DESTRUCT }
destructor path_storage.Destruct;
var
coord_blk : double_ptr_ptr;
begin
if m_total_blocks <> 0 then
begin
coord_blk:=double_ptr_ptr(ptrcomp(m_coord_blocks ) + (m_total_blocks - 1 ) * sizeof(double_ptr ) );
while m_total_blocks > 0 do
begin
agg_freemem(
p32(coord_blk^ ).ptr ,
(block_size * 2 + block_size div (sizeof(double ) div sizeof(int8u ) ) ) * sizeof(double ) );
dec(ptrcomp(coord_blk ) ,sizeof(double_ptr ) );
dec(m_total_blocks );
end;
agg_freemem(pointer(m_coord_blocks ) ,m_max_blocks * 2 * sizeof(double_ptr ) );
end;
end;
{ REMOVE_ALL }
procedure path_storage.remove_all;
begin
m_total_vertices:=0;
m_iterator :=0;
end;
{ LAST_VERTEX }
function path_storage.last_vertex;
begin
if m_total_vertices <> 0 then
result:=vertex_(m_total_vertices - 1 ,x ,y )
else
result:=path_cmd_stop;
end;
{ PREV_VERTEX }
function path_storage.prev_vertex;
begin
if m_total_vertices > 1 then
result:=vertex_(m_total_vertices - 2 ,x ,y )
else
result:=path_cmd_stop;
end;
{ LAST_X }
function path_storage.last_x;
var
idx : unsigned;
begin
if m_total_vertices <> 0 then
begin
idx:=m_total_vertices - 1;
result:=
double_ptr(
ptrcomp(
p32_ptr(ptrcomp(m_coord_blocks ) + (idx shr block_shift ) * sizeof(double_ptr ) ).ptr ) +
((idx and block_mask) shl 1 ) * sizeof(double ) )^;
end
else
result:=0.0;
end;
{ LAST_Y }
function path_storage.last_y;
var
idx : unsigned;
begin
if m_total_vertices <> 0 then
begin
idx:=m_total_vertices - 1;
result:=
double_ptr(
ptrcomp(
p32_ptr(ptrcomp(m_coord_blocks ) + (idx shr block_shift ) * sizeof(double_ptr ) ).ptr ) +
(((idx and block_mask) shl 1 ) + 1 ) * sizeof(double ) )^;
end
else
result:=0.0;
end;
{ REL_TO_ABS }
procedure path_storage.rel_to_abs;
var
x2 ,y2 : double;
begin
if m_total_vertices <> 0 then
if is_vertex(vertex_(m_total_vertices - 1 ,@x2 ,@y2 ) ) then
begin
x^:=x^ + x2;
y^:=y^ + y2;
end;
end;
{ MOVE_TO }
procedure path_storage.move_to;
begin
add_vertex(x ,y ,path_cmd_move_to );
end;
{ MOVE_REL }
procedure path_storage.move_rel;
begin
rel_to_abs(@dx ,@dy );
add_vertex(dx ,dy ,path_cmd_move_to );
end;
{ LINE_TO }
procedure path_storage.line_to;
begin
add_vertex(x ,y ,path_cmd_line_to );
end;
{ LINE_REL }
procedure path_storage.line_rel;
begin
rel_to_abs(@dx ,@dy);
add_vertex(dx ,dy ,path_cmd_line_to );
end;
{ HLINE_TO }
procedure path_storage.hline_to;
begin
add_vertex(x ,last_y ,path_cmd_line_to );
end;
{ HLINE_REL }
procedure path_storage.hline_rel;
var
dy : double;
begin
dy:=0;
rel_to_abs(@dx ,@dy);
add_vertex(dx ,dy ,path_cmd_line_to );
end;
{ VLINE_TO }
procedure path_storage.vline_to;
begin
add_vertex(last_x ,y ,path_cmd_line_to );
end;
{ VLINE_REL }
procedure path_storage.vline_rel;
var
dx : double;
begin
dx:=0;
rel_to_abs(@dx, @dy);
add_vertex(dx ,dy ,path_cmd_line_to );
end;
{ ARC_TO }
procedure path_storage.arc_to;
var
a : bezier_arc_svg_ptr;
x0 ,y0 ,epsilon : double;
begin
a:=NIL;
if (m_total_vertices <> 0 ) and
is_vertex(command(m_total_vertices - 1 ) ) then
begin
epsilon:=1e-30;
x0:=0.0;
y0:=0.0;
last_vertex(@x0 ,@y0 );
rx:=Abs(rx );
ry:=Abs(ry );
// Ensure radii are valid
if (rx < epsilon ) or
(ry < epsilon ) then
begin
line_to(x ,y );
exit;
end;
// If the endpoints (x, y) and (x0, y0) are identical, then this
// is equivalent to omitting the elliptical arc segment entirely.
if calc_distance(x0 ,y0 ,x ,y ) < epsilon then
exit;
new(a ,Construct(x0 ,y0 ,rx ,ry ,angle ,large_arc_flag ,sweep_flag ,x ,y ) );
if a.radii_ok then
add_path(a ,0 ,true )
else
line_to(x ,y );
end
else
move_to(x ,y );
if a <> NIL then
dispose(a ,Destruct );
end;
{ ARC_REL }
procedure path_storage.arc_rel;
begin
rel_to_abs(@dx ,@dy );
arc_to (rx ,ry ,angle ,large_arc_flag ,sweep_flag ,dx ,dy );
end;
{ CURVE3 }
procedure path_storage.curve3(x_ctrl ,y_ctrl ,x_to ,y_to : double );
begin
add_vertex(x_ctrl ,y_ctrl ,path_cmd_curve3 );
add_vertex(x_to ,y_to ,path_cmd_curve3 );
end;
{ CURVE3_REL }
procedure path_storage.curve3_rel(dx_ctrl ,dy_ctrl ,dx_to ,dy_to : double );
begin
rel_to_abs(@dx_ctrl ,@dy_ctrl );
rel_to_abs(@dx_to ,@dy_to );
add_vertex(dx_ctrl ,dy_ctrl ,path_cmd_curve3 );
add_vertex(dx_to ,dy_to ,path_cmd_curve3 );
end;
{ CURVE3 }
procedure path_storage.curve3(x_to ,y_to : double );
var
cmd : unsigned;
x0 ,y0 ,x_ctrl ,y_ctrl : double;
begin
if is_vertex(last_vertex(@x0 ,@y0 ) ) then
begin
cmd:=prev_vertex(@x_ctrl ,@y_ctrl );
if is_curve(cmd ) then
begin
x_ctrl:=x0 + x0 - x_ctrl;
y_ctrl:=y0 + y0 - y_ctrl;
end
else
begin
x_ctrl:=x0;
y_ctrl:=y0;
end;
curve3(x_ctrl ,y_ctrl ,x_to ,y_to );
end;
end;
{ CURVE3_REL }
procedure path_storage.curve3_rel(dx_to ,dy_to : double );
begin
rel_to_abs(@dx_to ,@dy_to );
curve3 (dx_to ,dy_to );
end;
{ CURVE4 }
procedure path_storage.curve4(x_ctrl1 ,y_ctrl1 ,x_ctrl2 ,y_ctrl2 ,x_to ,y_to : double );
begin
add_vertex(x_ctrl1 ,y_ctrl1 ,path_cmd_curve4 );
add_vertex(x_ctrl2 ,y_ctrl2 ,path_cmd_curve4 );
add_vertex(x_to ,y_to ,path_cmd_curve4 );
end;
{ CURVE4_REL }
procedure path_storage.curve4_rel(dx_ctrl1 ,dy_ctrl1 ,dx_ctrl2 ,dy_ctrl2 ,dx_to ,dy_to : double );
begin
rel_to_abs(@dx_ctrl1 ,@dy_ctrl1 );
rel_to_abs(@dx_ctrl2 ,@dy_ctrl2 );
rel_to_abs(@dx_to ,@dy_to );
add_vertex(dx_ctrl1 ,dy_ctrl1 ,path_cmd_curve4 );
add_vertex(dx_ctrl2 ,dy_ctrl2 ,path_cmd_curve4 );
add_vertex(dx_to ,dy_to ,path_cmd_curve4 );
end;
{ CURVE4 }
procedure path_storage.curve4(x_ctrl2 ,y_ctrl2 ,x_to ,y_to : double );
var
cmd : unsigned;
x0 ,y0 ,x_ctrl1 ,y_ctrl1 : double;
begin
if is_vertex(last_vertex(@x0 ,@y0 ) ) then
begin
cmd:=prev_vertex(@x_ctrl1 ,@y_ctrl1 );
if is_curve(cmd ) then
begin
x_ctrl1:=x0 + x0 - x_ctrl1;
y_ctrl1:=y0 + y0 - y_ctrl1;
end
else
begin
x_ctrl1:=x0;
y_ctrl1:=y0;
end;
curve4(x_ctrl1 ,y_ctrl1 ,x_ctrl2 ,y_ctrl2 ,x_to ,y_to );
end;
end;
{ CURVE4_REL }
procedure path_storage.curve4_rel(dx_ctrl2 ,dy_ctrl2 ,dx_to ,dy_to : double );
begin
rel_to_abs(@dx_ctrl2 ,@dy_ctrl2 );
rel_to_abs(@dx_to ,@dy_to );
curve4(dx_ctrl2 ,dy_ctrl2 ,dx_to ,dy_to );
end;
{ END_POLY }
procedure path_storage.end_poly;
begin
if m_total_vertices <> 0 then
if is_vertex(command(m_total_vertices - 1 ) ) then
add_vertex(0.0 ,0.0 ,path_cmd_end_poly or flags );
end;
{ CLOSE_POLYGON }
procedure path_storage.close_polygon;
begin
end_poly(path_flags_close or flags );
end;
{ ADD_POLY }
procedure path_storage.add_poly;
begin
if num <> 0 then
begin
if not solid_path then
begin
move_to(vertices[0 ] ,vertices[1 ] );
inc(ptrcomp(vertices ) ,2 * sizeof(double ) );
dec(num );
end;
while num > 0 do
begin
line_to(vertices[0 ] ,vertices[1 ] );
inc(ptrcomp(vertices ) ,2 * sizeof(double ) );
dec(num );
end;
if end_flags <> 0 then
end_poly(end_flags );
end;
end;
{ ADD_PATH }
procedure path_storage.add_path;
var
cmd : unsigned;
x ,y : double;
begin
vs.rewind(path_id );
cmd:=vs.vertex(@x ,@y );
while not is_stop(cmd ) do
begin
if is_move_to(cmd ) and
solid_path and
(m_total_vertices <> 0 ) then
cmd:=path_cmd_line_to;
add_vertex(x ,y ,cmd );
cmd:=vs.vertex(@x ,@y );
end;
end;
{ START_NEW_PATH }
function path_storage.start_new_path;
begin
if m_total_vertices <> 0 then
if not is_stop(command(m_total_vertices - 1 ) ) then
add_vertex(0.0 ,0.0 ,path_cmd_stop );
result:=m_total_vertices;
end;
{ COPY_FROM }
procedure path_storage.copy_from;
var
i ,cmd : unsigned;
x ,y : double;
begin
remove_all;
for i:=0 to ps.total_vertices - 1 do
begin
cmd:=ps.vertex_(i ,@x ,@y );
add_vertex(x ,y ,cmd );
end;
end;
{ TOTAL_VERTICES }
function path_storage.total_vertices;
begin
result:=m_total_vertices
end;
{ VERTEX_ }
function path_storage.vertex_;
var
nb : unsigned;
pv : double_ptr;
begin
nb:=idx shr block_shift;
pv:=
double_ptr(
ptrcomp(
p32_ptr(ptrcomp(m_coord_blocks ) + nb * sizeof(double_ptr ) ).ptr ) +
((idx and block_mask ) shl 1 ) * sizeof(double ) );
x^:=pv^; inc(ptrcomp(pv ) ,sizeof(double ) );
y^:=pv^;
result:=
int8u_ptr(
ptrcomp(
p32_ptr(ptrcomp(m_cmd_blocks ) + nb * sizeof(int8u_ptr ) ).ptr ) +
(idx and block_mask ) * sizeof(int8u ) )^;
end;
{ COMMAND }
function path_storage.command;
begin
result:=
int8u_ptr(
ptrcomp(
p32_ptr(ptrcomp(m_cmd_blocks ) + (idx shr block_shift ) * sizeof(int8u_ptr ) ).ptr ) +
(idx and block_mask ) * sizeof(int8u ) )^;
end;
{ REWIND }
procedure path_storage.rewind;
begin
m_iterator:=path_id;
end;
{ VERTEX }
function path_storage.vertex;
begin
if m_iterator >= m_total_vertices then
result:=path_cmd_stop
else
begin
result:=vertex_(m_iterator ,x ,y );
inc(m_iterator );
end;
end;
{ ARRANGE_POLYGON_ORIENTATION }
function path_storage.arrange_polygon_orientation;
var
cmd ,
end_ : unsigned;
begin
if orientation = path_flags_none then
begin
result:=start;
exit;
end;
// Skip all non-vertices at the beginning
while (start < m_total_vertices ) and
not is_vertex(command(start ) ) do
inc(start );
// Skip all insignificant move_to
while (start + 1 < m_total_vertices ) and
is_move_to(command(start ) ) and
is_move_to(command(start + 1 ) ) do
inc(start );
// Find the last vertex
end_:=start + 1;
while (end_ < m_total_vertices ) and
not is_next_poly(command(end_ ) ) do
inc(end_ );
if end_ - start > 2 then
if perceive_polygon_orientation(start ,end_ ) <> orientation then
begin
// Invert polygon, set orientation flag, and skip all end_poly
invert_polygon(start ,end_ );
cmd:=command(end_ );
while (end_ < m_total_vertices ) and
is_end_poly(cmd ) do
begin
modify_command(end_ ,set_orientation(cmd ,orientation ) );
inc(end_ );
cmd:=command(end_ );
end;
end;
result:=end_;
end;
{ ARRANGE_ORIENTATIONS }
function path_storage.arrange_orientations;
begin
if orientation <> path_flags_none then
while start < m_total_vertices do
begin
start:=arrange_polygon_orientation(start ,orientation );
if is_stop(command(start ) ) then
begin
inc(start );
break;
end;
end;
result:=start;
end;
{ ARRANGE_ORIENTATIONS_ALL_PATHS }
procedure path_storage.arrange_orientations_all_paths;
var
start : unsigned;
begin
if orientation <> path_flags_none then
begin
start:=0;
while start < m_total_vertices do
start:=arrange_orientations(start ,orientation );
end;
end;
{ FLIP_X }
procedure path_storage.flip_x;
var
i ,cmd : unsigned;
x ,y : double;
begin
if m_total_vertices > 0 then
for i:=0 to m_total_vertices - 1 do
begin
cmd:=vertex_(i ,@x ,@y );
if is_vertex(cmd ) then
modify_vertex(i ,x2 - x + x1 ,y );
end;
end;
{ FLIP_Y }
procedure path_storage.flip_y;
var
i ,cmd : unsigned;
x ,y : double;
begin
if m_total_vertices > 0 then
for i:=0 to m_total_vertices - 1 do
begin
cmd:=vertex_(i ,@x ,@y );
if is_vertex(cmd ) then
modify_vertex(i ,x ,y2 - y + y1 );
end;
end;
{ ADD_VERTEX }
procedure path_storage.add_vertex;
var
coord_ptr : double_ptr;
cmd_ptr : int8u_ptr;
begin
coord_ptr:=NIL;
cmd_ptr:=storage_ptrs(@coord_ptr );
cmd_ptr^:=int8u(cmd );
coord_ptr^:=x; inc(ptrcomp(coord_ptr ) ,sizeof(double ) );
coord_ptr^:=y;
inc(m_total_vertices );
end;
{ MODIFY_VERTEX }
procedure path_storage.modify_vertex;
var
pv : double_ptr;
begin
pv:=
double_ptr(
ptrcomp(
p32_ptr(ptrcomp(m_coord_blocks ) + (idx shr block_shift ) * sizeof(double_ptr ) ).ptr ) +
((idx and block_mask ) shl 1 ) * sizeof(double ) );
pv^:=x; inc(ptrcomp(pv ) ,sizeof(double ) );
pv^:=y;
end;
{ MODIFY_COMMAND }
procedure path_storage.modify_command;
begin
int8u_ptr(
ptrcomp(
p32_ptr(ptrcomp(m_cmd_blocks ) + (idx shr block_shift ) * sizeof(int8u_ptr ) ).ptr ) +
(idx and block_mask ) * sizeof(int8u ) )^:=int8u(cmd );
end;
{ TRANSFORM }
procedure path_storage.transform(trans : trans_affine_ptr; path_id : unsigned = 0 );
var
x ,y : double;
cmd : unsigned;
begin
while path_id < m_total_vertices do
begin
cmd:=vertex_(path_id ,@x ,@y );
if is_stop(cmd ) then
break;
if is_vertex(cmd ) then
begin
trans.transform(trans ,@x ,@y );
modify_vertex (path_id ,x ,y );
end;
inc(path_id );
end;
end;
{ TRANSFORM_ALL_PATHS }
procedure path_storage.transform_all_paths(trans : trans_affine_ptr );
var
x ,y : double;
idx : unsigned;
begin
idx:=0;
while idx < m_total_vertices do
begin
if is_vertex(vertex_(idx ,@x ,@y ) ) then
begin
trans.transform(trans ,@x ,@y );
modify_vertex (idx ,x ,y );
end;
inc(idx );
end;
end;
{ ALLOCATE_BLOCK }
procedure path_storage.allocate_block;
var
new_coords : double_ptr_ptr;
new_cmds : int8u_ptr_ptr;
begin
if nb >= m_max_blocks then
begin
agg_getmem(pointer(new_coords ) ,((m_max_blocks + block_pool ) * 2 ) * sizeof(double_ptr ) );
new_cmds:=int8u_ptr_ptr(ptrcomp(new_coords ) + (m_max_blocks + block_pool ) * sizeof(double_ptr ) );
if m_coord_blocks <> NIL then
begin
move(m_coord_blocks^ ,new_coords^ ,m_max_blocks * sizeof(double_ptr ) );
move(m_cmd_blocks^ ,new_cmds^ ,m_max_blocks * sizeof(int8u_ptr ) );
agg_freemem(pointer(m_coord_blocks ) ,m_max_blocks * 2 * sizeof(double_ptr ) );
end;
m_coord_blocks:=new_coords;
m_cmd_blocks :=new_cmds;
inc(m_max_blocks ,block_pool );
end;
agg_getmem(
p32_ptr(ptrcomp(m_coord_blocks ) + nb * sizeof(double_ptr ) ).ptr ,
(block_size * 2 + block_size div (sizeof(double ) div sizeof(int8u ) ) ) * sizeof(double ) );
p32_ptr(ptrcomp(m_cmd_blocks ) + nb * sizeof(int8u_ptr ) ).ptr:=
pointer(
ptrcomp(p32_ptr(ptrcomp(m_coord_blocks ) + nb * sizeof(double_ptr ) ).ptr ) +
block_size * 2 * sizeof(double ) );
inc(m_total_blocks );
end;
{ STORAGE_PTRS }
function path_storage.storage_ptrs;
var
nb : unsigned;
begin
nb:=m_total_vertices shr block_shift;
if nb >= m_total_blocks then
allocate_block(nb );
xy_ptr^:=
double_ptr(
ptrcomp(
p32_ptr(ptrcomp(m_coord_blocks ) + nb * sizeof(double_ptr ) ).ptr ) +
((m_total_vertices and block_mask ) shl 1 ) * sizeof(double ) );
result:=
int8u_ptr(
ptrcomp(
p32_ptr(ptrcomp(m_cmd_blocks ) + nb * sizeof(int8u_ptr ) ).ptr ) +
(m_total_vertices and block_mask ) * sizeof(int8u ) );
end;
{ PERCEIVE_POLYGON_ORIENTATION }
function path_storage.perceive_polygon_orientation;
var
np ,i : unsigned;
area ,x1 ,y1 ,x2 ,y2 : double;
begin
// Calculate signed area (double area to be exact)
np :=end_ - start;
area:=0.0;
if np > 0 then
for i:=0 to np - 1 do
begin
vertex_(start + i ,@x1 ,@y1 );
vertex_(start + (i + 1 ) mod np ,@x2 ,@y2 );
area:=area + (x1 * y2 - y1 * x2 );
end;
if area < 0.0 then
result:=path_flags_cw
else
result:=path_flags_ccw;
end;
{ INVERT_POLYGON }
procedure path_storage.invert_polygon(start ,end_ : unsigned );
var
i ,tmp_cmd ,start_nb ,end_nb : unsigned;
start_ptr ,end_ptr : double_ptr;
tmp_xy : double;
begin
tmp_cmd:=command(start );
dec(end_ ); // Make "end" inclusive
// Shift all commands to one position
i:=start;
while i < end_ do
begin
modify_command(i ,command(i + 1 ) );
inc(i );
end;
// Assign starting command to the ending command
modify_command(end_ ,tmp_cmd );
// Reverse the polygon
while end_ > start do
begin
start_nb:=start shr block_shift;
end_nb :=end_ shr block_shift;
start_ptr:=
double_ptr(
ptrcomp(
p32_ptr(ptrcomp(m_coord_blocks ) + start_nb * sizeof(double_ptr ) ).ptr ) +
((start and block_mask ) shl 1 ) * sizeof(double ) );
end_ptr:=
double_ptr(
ptrcomp(
p32_ptr(ptrcomp(m_coord_blocks ) + end_nb * sizeof(double_ptr ) ).ptr ) +
((end_ and block_mask ) shl 1 ) * sizeof(double ) );
tmp_xy :=start_ptr^;
start_ptr^:=end_ptr^; inc(ptrcomp(start_ptr ) ,sizeof(double ) );
end_ptr^ :=tmp_xy; inc(ptrcomp(end_ptr ) ,sizeof(double ) );
tmp_xy :=start_ptr^;
start_ptr^:=end_ptr^;
end_ptr^ :=tmp_xy;
tmp_cmd:=
int8u_ptr(
ptrcomp(
p32_ptr(ptrcomp(m_cmd_blocks ) + start_nb * sizeof(int8u_ptr ) ).ptr ) +
(start and block_mask ) * sizeof(int8u ) )^;
int8u_ptr(
ptrcomp(
p32_ptr(ptrcomp(m_cmd_blocks ) + start_nb * sizeof(int8u_ptr ) ).ptr ) +
(start and block_mask ) * sizeof(int8u ) )^:=
int8u_ptr(
ptrcomp(
p32_ptr(ptrcomp(m_cmd_blocks ) + end_nb * sizeof(int8u_ptr ) ).ptr ) +
(end_ and block_mask ) * sizeof(int8u ) )^;
int8u_ptr(
ptrcomp(
p32_ptr(ptrcomp(m_cmd_blocks ) + end_nb * sizeof(int8u_ptr ) ).ptr ) +
(end_ and block_mask ) * sizeof(int8u ) )^:=int8u(tmp_cmd );
inc(start );
dec(end_ );
end;
end;
{ INVERT_POLYGON }
procedure path_storage.invert_polygon(start : unsigned );
var
end_ : unsigned;
begin
// Skip all non-vertices at the beginning
while (start < m_total_vertices ) and
not is_vertex(command(start ) ) do
inc(start );
// Skip all insignificant move_to
while (start + 1 < m_total_vertices ) and
is_move_to(command(start ) ) and
is_move_to(command(start + 1 ) ) do
inc(start );
// Find the last vertex
end_:=start + 1;
while (end_ < m_total_vertices ) and
not is_next_poly(command(end_ ) ) do
inc(end_ );
invert_polygon(start ,end_ );
end;
{ CONCAT_PATH }
procedure path_storage.concat_path(vs : vertex_source_ptr; path_id : unsigned = 0 );
var
x ,y : double;
cmd : unsigned;
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;
END.