//
// 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
 image_fltr_graph ;

uses
 Math ,

 agg_basics ,
 agg_platform_support ,

 agg_color ,
 agg_pixfmt ,
 agg_pixfmt_rgb ,

 agg_ctrl ,
 agg_slider_ctrl ,
 agg_rbox_ctrl ,
 agg_cbox_ctrl ,

 agg_rendering_buffer ,
 agg_renderer_base ,
 agg_renderer_scanline ,
 agg_rasterizer_scanline_aa ,
 agg_scanline ,
 agg_scanline_u ,
 agg_scanline_p ,
 agg_render_scanlines ,

 agg_ellipse ,
 agg_trans_affine ,
 agg_conv_transform ,
 agg_conv_stroke ,
 agg_span_allocator ,
 agg_span_interpolator_linear ,
 agg_span_image_filter ,
 agg_span_image_filter_rgb ,
 agg_image_filters ,
 agg_path_storage ;

{$I agg_mode.inc }

const
 flip_y = true;

type
 filter_adaptor_ptr = ^filter_adaptor;
 filter_adaptor = object
   m_filter : image_filter_base_ptr;

   constructor Construct(filter : image_filter_base_ptr );
   destructor  Destruct;

   function  radius : double;
   function  calc_weight(x : double ) : double;

   procedure set_radius(r : double );

  end;

 the_application = object(platform_support )
   m_radius   : slider_ctrl;
   m_bilinear ,
   m_bicubic  ,
   m_spline16 ,
   m_spline36 ,
   m_hanning  ,
   m_hamming  ,
   m_hermite  ,
   m_kaiser   ,
   m_quadric  ,
   m_catrom   ,
   m_gaussian ,
   m_bessel   ,
   m_mitchell ,
   m_sinc     ,
   m_lanczos  ,
   m_blackman : cbox_ctrl;

   m_filters : array[0..31 ] of cbox_ctrl_ptr;

   m_filter_bilinear ,
   m_filter_bicubic  ,
   m_filter_spline16 ,
   m_filter_spline36 ,
   m_filter_hanning  ,
   m_filter_hamming  ,
   m_filter_hermite  ,
   m_filter_kaiser   ,
   m_filter_quadric  ,
   m_filter_catrom   ,
   m_filter_gaussian ,
   m_filter_bessel   ,
   m_filter_mitchell ,
   m_filter_sinc     ,
   m_filter_lanczos  ,
   m_filter_blackman : filter_adaptor;

   m_filter_func : array[0..31 ] of filter_adaptor_ptr;
   m_num_filters : unsigned;

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

   procedure on_draw; virtual;

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

  end;

{ CONSTRUCT }
constructor filter_adaptor.Construct;
begin
 m_filter:=filter;

end;

{ DESTRUCT }
destructor filter_adaptor.Destruct;
begin
 if m_filter <> NIL then
  dispose(m_filter );

end;

{ RADIUS }
function filter_adaptor.radius;
begin
 if m_filter <> NIL then
  result:=m_filter.radius
 else
  result:=0;

end;

{ CALC_WEIGHT }
function filter_adaptor.calc_weight;
begin
 if m_filter <> NIL then
  result:=m_filter.calc_weight(Abs(x ) )
 else
  result:=0;

end;

{ SET_RADIUS }
procedure filter_adaptor.set_radius;
begin
 if m_filter <> NIL then
  m_filter.set_radius(r );

end;

{ CONSTRUCT }
constructor the_application.Construct;
var
 i : unsigned;

begin
 inherited Construct(format_ ,flip_y_ );

 m_radius.Construct  (5.0 ,5.0 ,780 - 5 ,10.0 ,not flip_y_ );
 m_bilinear.Construct(8.0 ,30.0 + 15 * 0  ,'bilinear' ,not flip_y_ );
 m_bicubic.Construct (8.0 ,30.0 + 15 * 1  ,'bicubic ' ,not flip_y_ );
 m_spline16.Construct(8.0 ,30.0 + 15 * 2  ,'spline16' ,not flip_y_ );
 m_spline36.Construct(8.0 ,30.0 + 15 * 3  ,'spline36' ,not flip_y_ );
 m_hanning.Construct (8.0 ,30.0 + 15 * 4  ,'hanning ' ,not flip_y_ );
 m_hamming.Construct (8.0 ,30.0 + 15 * 5  ,'hamming ' ,not flip_y_ );
 m_hermite.Construct (8.0 ,30.0 + 15 * 6  ,'hermite ' ,not flip_y_ );
 m_kaiser.Construct  (8.0 ,30.0 + 15 * 7  ,'kaiser  ' ,not flip_y_ );
 m_quadric.Construct (8.0 ,30.0 + 15 * 8  ,'quadric ' ,not flip_y_ );
 m_catrom.Construct  (8.0 ,30.0 + 15 * 9  ,'catrom  ' ,not flip_y_ );
 m_gaussian.Construct(8.0 ,30.0 + 15 * 10 ,'gaussian' ,not flip_y_ );
 m_bessel.Construct  (8.0 ,30.0 + 15 * 11 ,'bessel  ' ,not flip_y_ );
 m_mitchell.Construct(8.0 ,30.0 + 15 * 12 ,'mitchell' ,not flip_y_ );
 m_sinc.Construct    (8.0 ,30.0 + 15 * 13 ,'sinc    ' ,not flip_y_ );
 m_lanczos.Construct (8.0 ,30.0 + 15 * 14 ,'lanczos ' ,not flip_y_ );
 m_blackman.Construct(8.0 ,30.0 + 15 * 15 ,'blackman' ,not flip_y_ );

 m_filter_bilinear.Construct(new(image_filter_bilinear_ptr ,Construct ) );
 m_filter_bicubic.Construct (new(image_filter_bicubic_ptr ,Construct ) );
 m_filter_spline16.Construct(new(image_filter_spline16_ptr ,Construct ) );
 m_filter_spline36.Construct(new(image_filter_spline36_ptr ,Construct ) );
 m_filter_hanning.Construct (new(image_filter_hanning_ptr ,Construct ) );
 m_filter_hamming.Construct (new(image_filter_hamming_ptr ,Construct ) );
 m_filter_hermite.Construct (new(image_filter_hermite_ptr ,Construct ) );
 m_filter_kaiser.Construct  (new(image_filter_kaiser_ptr ,Construct ) );
 m_filter_quadric.Construct (new(image_filter_quadric_ptr ,Construct ) );
 m_filter_catrom.Construct  (new(image_filter_catrom_ptr ,Construct ) );
 m_filter_gaussian.Construct(new(image_filter_gaussian_ptr ,Construct ) );
 m_filter_bessel.Construct  (new(image_filter_bessel_ptr ,Construct ) );
 m_filter_mitchell.Construct(new(image_filter_mitchell_ptr ,Construct ) );
 m_filter_sinc.Construct    (new(image_filter_sinc_ptr ,Construct(2.0 ) ) );
 m_filter_lanczos.Construct (new(image_filter_lanczos_ptr ,Construct(2.0 ) ) );
 m_filter_blackman.Construct(new(image_filter_blackman_ptr ,Construct(2.0 ) ) );

 m_num_filters:=0;

 m_filters[m_num_filters ]:=@m_bilinear; inc(m_num_filters );
 m_filters[m_num_filters ]:=@m_bicubic;  inc(m_num_filters );
 m_filters[m_num_filters ]:=@m_spline16; inc(m_num_filters );
 m_filters[m_num_filters ]:=@m_spline36; inc(m_num_filters );
 m_filters[m_num_filters ]:=@m_hanning;  inc(m_num_filters );
 m_filters[m_num_filters ]:=@m_hamming;  inc(m_num_filters );
 m_filters[m_num_filters ]:=@m_hermite;  inc(m_num_filters );
 m_filters[m_num_filters ]:=@m_kaiser;   inc(m_num_filters );
 m_filters[m_num_filters ]:=@m_quadric;  inc(m_num_filters );
 m_filters[m_num_filters ]:=@m_catrom;   inc(m_num_filters );
 m_filters[m_num_filters ]:=@m_gaussian; inc(m_num_filters );
 m_filters[m_num_filters ]:=@m_bessel;   inc(m_num_filters );
 m_filters[m_num_filters ]:=@m_mitchell; inc(m_num_filters );
 m_filters[m_num_filters ]:=@m_sinc;     inc(m_num_filters );
 m_filters[m_num_filters ]:=@m_lanczos;  inc(m_num_filters );
 m_filters[m_num_filters ]:=@m_blackman; inc(m_num_filters );

 i:=0;

 m_filter_func[i ]:=@m_filter_bilinear; inc(i );
 m_filter_func[i ]:=@m_filter_bicubic;  inc(i );
 m_filter_func[i ]:=@m_filter_spline16; inc(i );
 m_filter_func[i ]:=@m_filter_spline36; inc(i );
 m_filter_func[i ]:=@m_filter_hanning;  inc(i );
 m_filter_func[i ]:=@m_filter_hamming;  inc(i );
 m_filter_func[i ]:=@m_filter_hermite;  inc(i );
 m_filter_func[i ]:=@m_filter_kaiser;   inc(i );
 m_filter_func[i ]:=@m_filter_quadric;  inc(i );
 m_filter_func[i ]:=@m_filter_catrom;   inc(i );
 m_filter_func[i ]:=@m_filter_gaussian; inc(i );
 m_filter_func[i ]:=@m_filter_bessel;   inc(i );
 m_filter_func[i ]:=@m_filter_mitchell; inc(i );
 m_filter_func[i ]:=@m_filter_sinc;     inc(i );
 m_filter_func[i ]:=@m_filter_lanczos;  inc(i );
 m_filter_func[i ]:=@m_filter_blackman; inc(i );

 for i:=0 to m_num_filters - 1 do
  add_ctrl(m_filters[i ] );

 m_radius.range_(2.0 ,8.0 );
 m_radius.value_(4.0 );
 m_radius.label_('Radius=%.3f' );

 add_ctrl(@m_radius );

end;

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

 m_radius.Destruct;
 m_bilinear.Destruct;
 m_bicubic.Destruct;
 m_spline16.Destruct;
 m_spline36.Destruct;
 m_hanning.Destruct;
 m_hamming.Destruct;
 m_hermite.Destruct;
 m_kaiser.Destruct;
 m_quadric.Destruct;
 m_catrom.Destruct;
 m_gaussian.Destruct;
 m_bessel.Destruct;
 m_mitchell.Destruct;
 m_sinc.Destruct;
 m_lanczos.Destruct;
 m_blackman.Destruct;

 m_filter_bilinear.Destruct;
 m_filter_bicubic.Destruct;
 m_filter_spline16.Destruct;
 m_filter_spline36.Destruct;
 m_filter_hanning.Destruct;
 m_filter_hamming.Destruct;
 m_filter_hermite.Destruct;
 m_filter_kaiser.Destruct;
 m_filter_quadric.Destruct;
 m_filter_catrom.Destruct;
 m_filter_gaussian.Destruct;
 m_filter_bessel.Destruct;
 m_filter_mitchell.Destruct;
 m_filter_sinc.Destruct;
 m_filter_lanczos.Destruct;
 m_filter_blackman.Destruct;

end;

{ ON_DRAW }
procedure the_application.on_draw;
var
 pixf : pixel_formats;
 rgba : aggclr;

 rb : renderer_base;
 rs : renderer_scanline_aa_solid;
 sl : scanline_p8;

 ras : rasterizer_scanline_aa;

 normalized : image_filter_lut;
 weights    : int16_ptr;

 x_start ,x_end ,y_start ,y_end ,x_center ,x ,y ,ys ,radius ,dy ,xs ,dx ,sum ,xf : double;

 xfract ,ir : int;

 i ,j ,n ,xint ,nn : unsigned;

 p  : path_storage;
 pl : conv_stroke;
 tr : conv_transform;

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

 rb.Construct(@pixf );
 rs.Construct(@rb );

 rgba.ConstrDbl(1.0 ,1.0 ,1.0 );
 rb.clear      (@rgba );

 ras.Construct;
 sl.Construct;

// Render
 x_start :=125.0;
 x_end   :=_initial_width - 15.0;
 y_start :=10.0;
 y_end   :=_initial_height - 10.0;
 x_center:=(x_start + x_end ) / 2;

 p.Construct;
 pl.Construct(@p );
 tr.Construct(@pl ,_trans_affine_resizing );

 for i:=0 to 15 do
  begin
   x:=x_start + (x_end - x_start ) * i / 16.0;

   p.remove_all;
   p.move_to(x + 0.5 ,y_start );
   p.line_to(x + 0.5 ,y_end );

   ras.add_path(@tr );

   if i = 8 then
    rgba.ConstrInt(0 ,0 ,0 ,255 )
   else
    rgba.ConstrInt(0 ,0 ,0 ,100 );

   rs.color_       (@rgba );
   render_scanlines(@ras ,@sl ,@rs );

  end;

 ys:=y_start + (y_end - y_start ) / 6.0;

 p.remove_all;
 p.move_to       (x_start ,ys );
 p.line_to       (x_end ,ys );
 ras.add_path    (@tr );
 rgba.ConstrInt  (0 ,0 ,0 );
 rs.color_       (@rgba );
 render_scanlines(@ras ,@sl ,@rs );

 pl.width_(1.0 );

 for i:=0 to m_num_filters - 1 do
  if m_filters[i ]._status then
   begin
    m_filter_func[i ].set_radius(m_radius._value );

    radius:=m_filter_func[i ].radius;

    n :=trunc(radius * 256 * 2 );
    dy:=y_end - ys;

    xs:=(x_end + x_start ) / 2.0 - (radius * (x_end - x_start ) / 16.0 );
    dx:=(x_end - x_start ) * radius / 8.0;

    p.remove_all;
    p.move_to(xs + 0.5 ,ys + dy * m_filter_func[i ].calc_weight(-radius ) );

    j:=1;

    while j < n do
     begin
      p.line_to(
       xs + dx * j / n + 0.5 ,
       ys + dy * m_filter_func[i ].calc_weight(j / 256.0 - radius ) );

      inc(j );

     end;

    ras.add_path    (@tr );
    rgba.ConstrInt  (100 ,0 ,0 );
    rs.color_       (@rgba );
    render_scanlines(@ras ,@sl ,@rs );

    p.remove_all;

    ir:=trunc(Ceil(radius ) + 0.1 );

    for xint:=0 to 255 do
     begin
      sum:=0;

      xfract:=-ir;

      while xfract < ir do
       begin
        xf:=xint / 256.0 + xfract;

        if (xf >= -radius ) or
           (xf <= radius ) then
         sum:=sum + m_filter_func[i ].calc_weight(xf );

        inc(xfract );

       end;

      x:=x_center + ((-128.0 + xint ) / 128.0 ) * radius * (x_end - x_start ) / 16.0;
      y:=ys + sum * 256 - 256;

      if xint = 0 then
       p.move_to(x ,y )
      else
       p.line_to(x ,y );

     end;

    ras.add_path    (@tr );
    rgba.ConstrInt  (0 ,100 ,0 );
    rs.color_       (@rgba );
    render_scanlines(@ras ,@sl ,@rs );

    normalized.Construct(m_filter_func[i ].m_filter );

    weights:=normalized.weight_array;

    xs:=(x_end + x_start ) / 2.0 - (normalized.diameter * (x_end - x_start ) / 32.0 );
    nn:=normalized.diameter * 256;

    p.remove_all;
    p.move_to(xs + 0.5 ,ys + dy * int16_ptr(weights )^ / image_filter_size );

    j:=1;

    while j < nn do
     begin
      p.line_to(
       xs + dx * j / n + 0.5 ,
       ys + dy * int16_ptr(ptrcomp(weights ) + j * sizeof(int16 ) )^ / image_filter_size );

      inc(j );

     end;

    ras.add_path    (@tr );
    rgba.ConstrInt  (0 ,0 ,100 ,255 );
    rs.color_       (@rgba );
    render_scanlines(@ras ,@sl ,@rs );

   // Free
    normalized.Destruct;

   end;

// Render the controls
 for i:=0 to m_num_filters - 1 do
  render_ctrl(@ras ,@sl ,@rs ,m_filters[i ] );

 if m_sinc._status or
    m_lanczos._status or
    m_blackman._status then
  render_ctrl(@ras ,@sl ,@rs ,@m_radius );

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

 p.Destruct;
 pl.Destruct;

end;

{ ON_KEY }
procedure the_application.on_key;
begin
 if key = key_f1 then
  message_(
   'Demonstration of the shapes of different interpolation filters. '#13 +
   'Just in case if you are curious.' +
   #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_ ('Image filters'' shape comparison (F1-Help)' );

 if app.init(780 ,300 ,window_resize ) then
  app.run;

 app.Destruct;

END.