//
// AggPas 2.4 RM3 Demo application
// Note: Press F1 key on run to see more info about this demo
//
// Paths: ..\;..\ctrl;..\svg;..\util;..\platform\win;expat-wrap
//
program
 alpha_gradient ;

uses
 agg_basics ,
 agg_platform_support ,

 agg_color ,
 agg_pixfmt ,
 agg_pixfmt_rgb ,

 agg_ctrl ,
 agg_spline_ctrl ,

 agg_renderer_base ,
 agg_renderer_scanline ,
 agg_rasterizer_scanline_aa ,
 agg_scanline ,
 agg_scanline_u ,
 agg_render_scanlines ,

 agg_array ,
 agg_span_gradient ,
 agg_span_gradient_alpha ,
 agg_span_interpolator_linear ,
 agg_span_converter ,
 agg_span_allocator ,
 agg_ellipse ,
 agg_vcgen_stroke ,
 agg_trans_affine ,
 agg_math ;

{$I agg_mode.inc }

const
 flip_y = true;

type
 the_application = object(platform_support )
   m_x   ,
   m_y   : array[0..2 ] of double;
   m_dx  ,
   m_dy  : double;
   m_idx : int;

   m_alpha : spline_ctrl;

   constructor Construct(format_ : pix_format_e; flip_y_ : boolean );
   destructor  Destruct;

   procedure fill_color_array(array_ : pod_auto_array_ptr; begin_ ,middle_ ,end_ : aggclr_ptr );

   procedure on_draw; virtual;

   procedure on_mouse_move       (x ,y : int; flags : unsigned ); virtual;
   procedure on_mouse_button_down(x ,y : int; flags : unsigned ); virtual;
   procedure on_mouse_button_up  (x ,y : int; flags : unsigned ); virtual;

   procedure on_key(x ,y : int; key ,flags : unsigned ); virtual;

  end;

{ CONSTRUCT }
constructor the_application.Construct;
begin
 inherited Construct(format_ ,flip_y_ );

 m_alpha.Construct(2 ,2 ,200 ,30 ,6 ,not flip_y_ );

 m_idx:=-1;

 m_x[0 ]:=257;
 m_y[0 ]:=60;
 m_x[1 ]:=369;
 m_y[1 ]:=170;
 m_x[2 ]:=143;
 m_y[2 ]:=310;

 m_alpha.point_(0 ,0.0       ,0.0);
 m_alpha.point_(1 ,1.0 / 5.0 ,1.0 - 4.0 / 5.0 );
 m_alpha.point_(2 ,2.0 / 5.0 ,1.0 - 3.0 / 5.0 );
 m_alpha.point_(3 ,3.0 / 5.0 ,1.0 - 2.0 / 5.0 );
 m_alpha.point_(4 ,4.0 / 5.0 ,1.0 - 1.0 / 5.0 );
 m_alpha.point_(5 ,1.0       ,1.0 );

 m_alpha.update_spline;

 add_ctrl(@m_alpha );

end;

{ DESTRUCT }
destructor the_application.Destruct;
begin
 inherited Destruct;

 m_alpha.Destruct;

end;

{ FILL_COLOR_ARRAY }
procedure the_application.fill_color_array;
var
 i : unsigned;

begin
 i:=0;

 while i < 128 do
  begin
   aggclr_ptr(array_.array_operator(i ) )^:=
    begin_.gradient(middle_ ,i / 128.0 );

   inc(i );

  end;

 while i < 256 do
  begin
   aggclr_ptr(array_.array_operator(i ) )^:=
    middle_.gradient(end_ ,(i - 128 ) / 128.0 );

   inc(i );

  end;

end;

{ ON_DRAW }
procedure the_application.on_draw;
type
// Gradient shape function (linear, radial, custom, etc)
 gradient_func_type = gradient_circle;

// Alpha gradient shape function (linear, radial, custom, etc)
 gradient_alpha_func_type = gradient_xy;

// Span interpolator. This object is used in all span generators 
// that operate with transformations during iterating of the spans,
// for example, image transformers use the interpolator too.
 interpolator_type = span_interpolator_linear;

// Span allocator is an object that allocates memory for
// the array of colors that will be used to render the
// color spans. One object can be shared between different
// span generators.
 span_allocator_type = span_allocator;

// Gradient colors array adaptor
 gradient_colors_type = pod_auto_array;

// Finally, the gradient span generator working with the color_type
// color type.
 span_gradient_type = span_gradient;

// Gradient alpha array adaptor
 gradient_alpha_type = pod_auto_array;

// The alpha gradient span converter working with the color_type
// color type.
 span_gradient_alpha_type = span_gradient_alpha;

// Span converter type
 span_conv_type = span_converter;

// The gradient (plus span converter) renderer type
 renderer_gradient_type = renderer_scanline_aa;

var
 pf : pixel_formats;

 rgba ,
 rgbb ,
 rgbc : aggclr;

 ren_base  : renderer_base;
 ren_solid : renderer_scanline_aa_solid;

 sl  : scanline_u8;
 ras : rasterizer_scanline_aa;

 ell : ellipse;

 i ,w ,h : unsigned;

 parallelogram : array[0..5 ] of double;

 tas : trans_affine_scaling;
 tar : trans_affine_rotation;
 tat : trans_affine_translation;

 stroke : vcgen_stroke;

// The gradient objects declarations
 gradient_func           : gradient_func_type;       // The gradient function
 alpha_func              : gradient_alpha_func_type; // The gradient function
 gradient_mtx            ,                           // Gradient affine transformer
 alpha_mtx               : trans_affine;             // Alpha affine transformer
 span_interpolator       ,                           // Span gradient interpolator
 span_interpolator_alpha : interpolator_type;        // Span alpha interpolator
 span_allocator          : span_allocator_type;      // Span Allocator
 color_array             : gradient_colors_type;     // The gradient colors

 span_gradient       : span_gradient_type;
 alpha_array         : gradient_alpha_type;
 span_gradient_alpha : span_gradient_alpha_type;
 span_conv           : span_conv_type;
 ren_gradient        : renderer_gradient_type;

begin
// Initialize structures
 pixfmt_bgr24(pf ,rbuf_window );

 ren_base.Construct (@pf );
 ren_solid.Construct(@ren_base );

 rgba.ConstrDbl(1 ,1 ,1 );
 ren_base.clear(@rgba );

 sl.Construct;
 ras.Construct;

// Draw some background
 ell.Construct;

 RandSeed:=1234;

 w:=trunc(_width );
 h:=trunc(_height );

 for i:=0 to 99 do
  begin
   ell.init(
    Random($7fff ) mod w ,Random($7fff ) mod h ,
    Random($7fff ) mod 60 + 5 ,Random($7fff ) mod 60 + 5 ,50 );

   rgba.ConstrDbl(
    Random($7fff ) / $7fff ,
    Random($7fff ) / $7fff ,
    Random($7fff ) / $7fff ,
    Random($7fff ) / $7fff / 2.0 );

   ren_solid.color_(@rgba );
   ras.add_path    (@ell);
   render_scanlines(@ras ,@sl ,@ren_solid );

  end;

 parallelogram[0 ]:=m_x[0 ];
 parallelogram[1 ]:=m_y[0 ];
 parallelogram[2 ]:=m_x[1 ];
 parallelogram[3 ]:=m_y[1 ];
 parallelogram[4 ]:=m_x[2 ];
 parallelogram[5 ]:=m_y[2 ];

// The gradient objects initializations
 gradient_func.Construct;
 alpha_func.Construct;
 gradient_mtx.Construct;
 alpha_mtx.Construct;
 span_interpolator.Construct      (@gradient_mtx );
 span_interpolator_alpha.Construct(@alpha_mtx );
 span_allocator.Construct;
 color_array.Construct(256 ,sizeof(aggclr ) );

// Initialize the gradient span itself.
// The last two arguments are so called "d1" and "d2"
// defining two distances in pixels, where the gradient starts
// and where it ends. The actual meaning of "d1" and "d2" depands
// on the gradient function.
 span_gradient.Construct(
  @span_allocator ,
  @span_interpolator ,
  @gradient_func ,
  @color_array ,0 ,150 );

 alpha_array.Construct(256 ,sizeof(int8u ) );

 span_gradient_alpha.Construct(
  @span_interpolator_alpha ,
  @alpha_func ,
  @alpha_array ,0 ,100 );

// Span converter initialization
 span_conv.Construct(@span_gradient ,@span_gradient_alpha );

// The gradient renderer
 ren_gradient.Construct(@ren_base ,@span_conv );

// Finally we can draw a circle
 tas.Construct(0.75 ,1.2 );
 tar.Construct(pi / 3.0 );
 tat.Construct(_width / 2 ,_height / 2 );

 gradient_mtx.multiply(@tas );
 gradient_mtx.multiply(@tar );
 gradient_mtx.multiply(@tat );
 gradient_mtx.invert;

 alpha_mtx.parl_to_rect(@parallelogram ,-100 ,-100 ,100 ,100 );

 rgba.ConstrDbl(0    ,0.19 ,0.19 );
 rgbb.ConstrDbl(0.7  ,0.7  ,0.19 );
 rgbc.ConstrDbl(0.31 ,0    ,0 );

 fill_color_array(@color_array ,@rgba ,@rgbb ,@rgbc );

// Fill Alpha array
 for i:=0 to 255 do
  int8u_ptr(alpha_array.array_operator(i ) )^:=
   int8u(trunc(m_alpha._value(i / 255.0 ) * base_mask ) );

 ell.init(_width / 2 ,_height / 2 ,150 ,150 ,100 );

 ras.add_path    (@ell);
 render_scanlines(@ras ,@sl ,@ren_gradient );

// Draw the control points and the parallelogram
 rgba.ConstrDbl  (0 ,0.4 ,0.4 ,0.31 );
 ren_solid.color_(@rgba );

 ell.init        (m_x[0 ] ,m_y[0 ] ,5 ,5 ,20 );
 ras.add_path    (@ell);
 render_scanlines(@ras ,@sl ,@ren_solid );

 ell.init        (m_x[1 ] ,m_y[1 ] ,5 ,5 ,20 );
 ras.add_path    (@ell );
 render_scanlines(@ras ,@sl ,@ren_solid );

 ell.init        (m_x[2 ] ,m_y[2 ] ,5 ,5 ,20 );
 ras.add_path    (@ell );
 render_scanlines(@ras ,@sl ,@ren_solid );

 stroke.Construct;
 stroke.add_vertex(m_x[0 ] ,m_y[0 ] ,path_cmd_move_to );
 stroke.add_vertex(m_x[1 ] ,m_y[1 ] ,path_cmd_line_to );
 stroke.add_vertex(m_x[2 ] ,m_y[2 ] ,path_cmd_line_to );
 stroke.add_vertex(m_x[0 ] + m_x[2 ] - m_x[1 ] ,m_y[0 ] + m_y[2 ] - m_y[1 ] ,path_cmd_line_to );
 stroke.add_vertex(0 ,0 ,path_cmd_end_poly or path_flags_close );

 rgba.ConstrDbl  (0 ,0 ,0 );
 ren_solid.color_(@rgba );
 ras.add_path    (@stroke);
 render_scanlines(@ras ,@sl ,@ren_solid );

// Render the controls
 render_ctrl(@ras ,@sl ,@ren_solid ,@m_alpha );

// Free AGG resources
 sl.Destruct;
 ras.Destruct;

 span_allocator.Destruct;
 color_array.Destruct;
 alpha_array.Destruct;

 stroke.Destruct;

end;

{ ON_MOUSE_MOVE }
procedure the_application.on_mouse_move;
var
 dx ,dy : double;

begin
 if flags and mouse_left <> 0 then
  begin
   if m_idx = 3 then
    begin
     dx:=x - m_dx;
     dy:=y - m_dy;

     m_x[1 ]:=m_x[1 ] - (m_x[0 ] - dx );
     m_y[1 ]:=m_y[1 ] - (m_y[0 ] - dy );
     m_x[2 ]:=m_x[2 ] - (m_x[0 ] - dx );
     m_y[2 ]:=m_y[2 ] - (m_y[0 ] - dy );
     m_x[0 ]:=dx;
     m_y[0 ]:=dy;

     force_redraw;

     exit;

    end;

   if m_idx >= 0 then
    begin
     m_x[m_idx ]:=x - m_dx;
     m_y[m_idx ]:=y - m_dy;

     force_redraw;

    end;

  end
 else
  on_mouse_button_up(x ,y ,flags );

end;

{ ON_MOUSE_BUTTON_DOWN }
procedure the_application.on_mouse_button_down;
var
 i : unsigned;

begin
 if flags and mouse_left <> 0 then
  begin
   i:=0;

   while i < 3 do
    begin
     if Sqrt((x - m_x[i ] ) * (x - m_x[i ] ) + (y - m_y[i ] ) * (y - m_y[i ] ) ) < 10.0 then
      begin
       m_dx :=x - m_x[i ];
       m_dy :=y - m_y[i ];
       m_idx:=i;

       break;

      end;

     inc(i );

    end;

   if i = 3 then
    if point_in_triangle(
        m_x[0 ] ,m_y[0 ] ,
        m_x[1 ] ,m_y[1 ] ,
        m_x[2 ] ,m_y[2 ] ,x ,y ) then
     begin
      m_dx :=x - m_x[0 ];
      m_dy :=y - m_y[0 ];
      m_idx:=3;

     end;

  end;

end;

{ ON_MOUSE_BUTTON_UP }
procedure the_application.on_mouse_button_up;
begin
 m_idx:=-1;

end;

{ ON_KEY }
procedure the_application.on_key;
var
 dx ,dy : double;

begin
 dx:=0;
 dy:=0;

 case key of
  key_left  : dx:=-0.1;
  key_right : dx:= 0.1;
  key_up    : dy:= 0.1;
  key_down  : dy:=-0.1;

 end;

 m_x[0 ]:=m_x[0 ] + dx;
 m_y[0 ]:=m_y[0 ] + dy;
 m_x[1 ]:=m_x[1 ] + dx;
 m_y[1 ]:=m_y[1 ] + dy;

 force_redraw;

 if key = key_f1 then
  message_(
   'The demo shows how to combine any span generator with alpha-channel gradient.'#13#13 +
   'How to play with:'#13#13 +
   'Use the mouse to move the parallelogram around.'#13 +
   'Use the arrow keys to move the parallelogram very precisely.' +
   #13#13'Note: F2 key saves current "screenshot" file in this demo''s directory.  ' );

end;

VAR
 app : the_application;

BEGIN
 app.Construct(pix_format_bgr24 ,flip_y );
 app.caption_ ('AGG Example. Alpha channel gradient (F1-Help)' );

 if app.init(400 ,320 ,window_resize ) then
  app.run;

 app.Destruct;

END.