{
    fpGUI  -  Free Pascal GUI Toolkit

    Copyright (C) 2006 - 2010 See the file AUTHORS.txt, included in this
    distribution, for details of the copyright.

    See the file COPYING.modifiedLGPL, included in this distribution,
    for details about redistributing fpGUI.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

    Description:
      Some more interpolation filters for TfpgCanvas.StretchDraw:
      Bessel, Gaussian and Sinc are infinite impulse response (IIR),
      the others are finite impulse response (FIR). The implementation
      of Bessel and Sinc are windowed with Blackman filter.

      This unit was ported from fcl-image which is part of FPC. A few
      more filters have also been added.
}

unit fpg_extinterpolation;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, fpg_base;

type

  TBlackmanInterpolation = class(TfpgBaseInterpolation)
  protected
    function  Filter(x: double): double; override;
    function  MaxSupport: double; override;
  end;


  TBlackmanSincInterpolation = class(TfpgBaseInterpolation)
  protected
    function  Filter(x: double): double; override;
    function  MaxSupport: double; override;
  end;


  TBlackmanBesselInterpolation = class(TfpgBaseInterpolation)
  protected
    function  Filter(x: double): double; override;
    function  MaxSupport: double; override;
  end;


  TGaussianInterpolation = class(TfpgBaseInterpolation)
  protected
    function  Filter(x: double): double; override;
    function  MaxSupport: double; override;
  end;


  // a.k.a. "Nearest Neighbour" filter
  TBoxInterpolation = class(TfpgBaseInterpolation)
  protected
    function  Filter(x: double): double; override;
    function  MaxSupport: double; override;
  end;


  THermiteInterpolation = class(TfpgBaseInterpolation)
  protected
    function  Filter(x: double): double; override;
    function  MaxSupport: double; override;
  end;


  TLanczosInterpolation = class(TfpgBaseInterpolation)
  protected
    function  Filter(x: double): double; override;
    function  MaxSupport: double; override;
  end;


  TQuadraticInterpolation = class(TfpgBaseInterpolation)
  protected
    function  Filter(x: double): double; override;
    function  MaxSupport: double; override;
  end;


  TCubicInterpolation = class(TfpgBaseInterpolation)
  protected
    function  Filter(x: double): double; override;
    function  MaxSupport: double; override;
  end;


  TCatromInterpolation = class(TfpgBaseInterpolation)
  protected
    function  Filter(x: double): double; override;
    function  MaxSupport: double; override;
  end;


  TBilinearInterpolation = class(TfpgBaseInterpolation)
  protected
    function  Filter(x: double): double; override;
    function  MaxSupport: double; override;
  end;


  THanningInterpolation = class(TfpgBaseInterpolation)
  protected
    function  Filter(x: double): double; override;
    function  MaxSupport: double; override;
  end;


  THammingInterpolation = class(TfpgBaseInterpolation)
  protected
    function  Filter(x: double): double; override;
    function  MaxSupport: double; override;
  end;
  
  
  TBSplineInterpolation = class(TfpgBaseInterpolation)
  protected
    function  Filter(x: double): double; override;
    function  MaxSupport: double; override;
  end;
  
  
  TBellInterpolation = class(TfpgBaseInterpolation)
  protected
    function  Filter(x: double): double; override;
    function  MaxSupport: double; override;
  end;

  

implementation


// BesselOrderOne: computes Bessel function of x in the first kind of order 0

function J1(x: double): double;
const
  Pone: array[0..8] of double =
    (  0.581199354001606143928050809e+21,
      -0.6672106568924916298020941484e+20,
       0.2316433580634002297931815435e+19,
      -0.3588817569910106050743641413e+17,
       0.2908795263834775409737601689e+15,
      -0.1322983480332126453125473247e+13,
       0.3413234182301700539091292655e+10,
      -0.4695753530642995859767162166e+7,
       0.270112271089232341485679099e+4
    );
  Qone: array [0..8] of double =
    ( 0.11623987080032122878585294e+22,
      0.1185770712190320999837113348e+20,
      0.6092061398917521746105196863e+17,
      0.2081661221307607351240184229e+15,
      0.5243710262167649715406728642e+12,
      0.1013863514358673989967045588e+10,
      0.1501793594998585505921097578e+7,
      0.1606931573481487801970916749e+4,
      0.1e+1
    );
var
  p, q: double;
  r: byte;
begin
  p := Pone[8];
  q := Qone[8];
  for r := 7 downto 0 do
    begin
    p := p*x*x+pOne[r];
    q := q*X*X+Qone[r];
    end;
  result := p / q;
end;

function P1(x: double): double;
const
  Pone: array[0..5] of double =
    ( 0.352246649133679798341724373e+5,
      0.62758845247161281269005675e+5,
      0.313539631109159574238669888e+5,
      0.49854832060594338434500455e+4,
      0.2111529182853962382105718e+3,
      0.12571716929145341558495e+1
    );
  Qone: array [0..5] of double =
    ( 0.352246649133679798068390431e+5,
      0.626943469593560511888833731e+5,
      0.312404063819041039923015703e+5,
      0.4930396490181088979386097e+4,
      0.2030775189134759322293574e+3,
      0.1e+1
    );
var
  x8, p, q: double;
  r: byte;
begin
  p := Pone[5];
  q := Qone[5];
  x8 := 8.0 / x;
  for r := 4 downto 0 do
  begin
    p := p*x8*x8+pOne[r];
    q := q*x8*x8+Qone[r];
  end;
  result := p / q;
end;

function Q1(x: double): double;
const
  Pone: array[0..5] of double =
    ( 0.3511751914303552822533318e+3,
      0.7210391804904475039280863e+3,
      0.4259873011654442389886993e+3,
      0.831898957673850827325226e+2,
      0.45681716295512267064405e+1,
      0.3532840052740123642735e-1
    );
  Qone : array [0..5] of double =
    ( 0.74917374171809127714519505e+4,
      0.154141773392650970499848051e+5,
      0.91522317015169922705904727e+4,
      0.18111867005523513506724158e+4,
      0.1038187585462133728776636e+3,
      0.1e+1
    );
var
  x8, p, q: double;
  r: byte;
begin
  p := Pone[5];
  q := Qone[5];
  x8 := 8.0 / x;
  for r := 4 downto 0 do
  begin
    p := p*x8*x8+pOne[r];
    q := q*x8*x8+Qone[r];
  end;
  result := p / q;
end;

function BesselOrderOne(x: double): double;
var
  p, OneOverSqrt2, sinx, cosx: double;
begin
  if x = 0.0 then
    result := 0.0
  else
    begin
    p := x;
    if x < 0.0 then
      x := -x;
    if x < 8.0 then
      result := p * J1(x)
    else
      begin
      OneOverSqrt2 := 1.0 / sqrt(2.0);
      sinx := sin(x);
      cosx := cos(x);
      result := sqrt(2.0/(PI*x)) *
           ( P1(x)*(OneOverSqrt2*(sinx-cosx))
             - 8.0/x*Q1(x)*(-OneOverSqrt2*(sinx+cosx))
           );
      if p < 0.0 then
        result := -result;
      end
    end;
end;

// Functions to aid calculations

function Bessel(x: double): double;
begin
  if x = 0.0 then
    result := PI / 4.0
  else
    result := BesselOrderOne(PI * x) / (2.0 * x);
end;

function Sinc(x: double): double;
var
  xx: double;
begin
  if x = 0.0 then
    result := 1.0
  else
    begin
    xx := PI*x;
    result := sin(xx) / (xx);
    end;
end;

function Blackman(x: double): double;
var
  xpi: double;
begin
  xpi := PI * x;
  result := 0.42 + 0.5 * cos(xpi) + 0.08 * cos(2*xpi);
end;

{ THermiteInterpolation }

function THermiteInterpolation.Filter(x: double): double;
begin
  if x < -1.0 then
    result := 0.0
  else if x < 0.0 then
    result := (2.0*(-x)-3.0)*(-x)*(-x)+1.0
  else if x < 1.0 then
    result := (2.0*x-3.0)*x*x+1.0
  else
    result := 0;
end;

function THermiteInterpolation.MaxSupport: double;
begin
  result := 1.0;
end;

{ TLanczosInterpolation }

function TLanczosInterpolation.Filter(x: double): double;
begin
  if x < -3.0 then
    result := 0.0
  else if x < 0.0 then
    result := sinc(-x)*sinc(-x/3.0)
  else if x < 3.0 then
    result := sinc(x)*sinc(x/3.0)
  else
    result := 0.0;
end;

function TLanczosInterpolation.MaxSupport: double;
begin
  result := 3.0;
end;

{ TQuadraticInterpolation }

function TQuadraticInterpolation.Filter(x: double): double;
begin
  if x < -1.5 then
    result := 0.0
  else if x < -0.5 then
    begin
    x := x + 1.5;
    result := 0.5*x*x;
    end
  else if x < 0.5 then
    result := 0.75 - x*x
  else if x < 1.5 then
    begin
    x := x - 1.5;
    result := 0.5*x*x;
    end
  else
    result := 0.0;
end;

function TQuadraticInterpolation.MaxSupport: double;
begin
  result := 1.5;
end;

{ TCubicInterpolation }

function TCubicInterpolation.Filter(x: double): double;
begin
  if x < -2.0 then
    result := 0.0
  else if x < -1.0 then
    begin
    x := x +2.0;
    result := x*x*x / 6.0;
    end
  else if x < 0.0 then
    result := (4.0+x*x*(-6.0-3.0*x)) / 6.0
  else if x < 1.0 then
    result := (4.0+x*x*(-6.0+3.0*x)) / 6.0
  else if x < 2.0 then
    begin
    x := 2.0 - x;
    result := x*x*x / 6.0;
    end
  else
    result := 0.0;
end;

function TCubicInterpolation.MaxSupport: double;
begin
  result := 2.0;
end;

{ TCatromInterpolation }

function TCatromInterpolation.Filter(x: double): double;
begin
  if x < -2.0 then
    result := 0.0
  else if x < -1.0 then
    result := 0.5*(4.0+x*(8.0+x*(5.0+x)))
  else if x < 0.0 then
    result := 0.5*(2.0+x*x*(-5.0-3.0*x))
  else if x < 1.0 then
    result := 0.5*(2.0+x*x*(-5.0+3.0*x))
  else if x < 2.0 then
    result := 0.5*(4.0+x*(-8.0+x*(5.0-x)))
  else
    result := 0.0;
end;

function TCatromInterpolation.MaxSupport: double;
begin
  result := 2.0;
end;

{ THanningInterpolation }

function THanningInterpolation.Filter(x: double): double;
begin
  if x < -1.0 then
    result := 0.0
  else if x <= 1.0 then
    result := 0.5+0.5*cos(PI*x)
  else
    result := 0.0;
end;

function THanningInterpolation.MaxSupport: double;
begin
  result := 1.0;
end;

{ THammingInterpolation }

function THammingInterpolation.Filter(x: double): double;
begin
  if x < -1.0 then
    result := 0.0
  else if x <= 1.0 then
    result := 0.54+0.46*cos(PI*x)
  else
    result := 0.0;
end;

function THammingInterpolation.MaxSupport: double;
begin
  result := 1.0;
end;

{ TBilinearInterpolation }

function TBilinearInterpolation.Filter(x: double): double;
begin
  if x < -1.0 then
    result := 0.0
  else if x < 0.0 then
    result := 1 + x
  else if x < 1.0 then
    result := 1 - x
  else
    result := 0.0;
end;

function TBilinearInterpolation.MaxSupport: double;
begin
  result := 1.0;
end;

{ TBoxInterpolation }

function TBoxInterpolation.Filter(x: double): double;
begin
  if x < -0.5 then
    result := 0.0
  else if x < 0.5 then
    result := 1.0
  else
    result := 0.0;
end;

function TBoxInterpolation.MaxSupport: double;
begin
  result := 0.5;
end;

{ TGaussianInterpolation }

function TGaussianInterpolation.Filter(x: double): double;
begin
  result := exp(-2.0*x*x) * sqrt(2.0/PI);
end;

function TGaussianInterpolation.MaxSupport: double;
begin
  result := 1.25;
end;

{ TBlackmanBesselInterpolation }

function TBlackmanBesselInterpolation.Filter(x: double): double;
begin
  result := Blackman(x/MaxSupport) * Bessel (x);
end;

function TBlackmanBesselInterpolation.MaxSupport: double;
begin
  Result := 3.2383;
end;

{ TBlackmanSincInterpolation }

function TBlackmanSincInterpolation.Filter(x: double): double;
begin
  Result := Blackman(x/MaxSupport) * Sinc(x);
end;

function TBlackmanSincInterpolation.MaxSupport: double;
begin
  Result := 4.0;
end;

{ TBlackmanInterpolation }

function TBlackmanInterpolation.Filter(x: double): double;
begin
  Result := Blackman (x);
end;

function TBlackmanInterpolation.MaxSupport: double;
begin
  Result := 1.0;
end;

{ TBSplineInterpolation }

function TBSplineInterpolation.Filter(x: double): double;
var
  tt: double;
  Value: double;
begin
  if (x < 0.0) then
    Value := -x
  else
    Value := x;
    
  if (Value < 1.0) then
  begin
    tt := Sqr(Value);
    Result := 0.5*tt*Value - tt + 2.0 / 3.0;
  end
  else if (Value < 2.0) then
  begin
    Value := 2.0 - Value;
    Result := 1.0/6.0 * Sqr(Value) * Value;
  end else
    Result := 0.0;
end;

function TBSplineInterpolation.MaxSupport: double;
begin
  Result := 2.0;
end;

{ TBellInterpolation }

function TBellInterpolation.Filter(x: double): double;
var
  Value: double;
begin
  if (x < 0.0) then
    Value := -x
  else
    Value := x;
    
  if (Value < 0.5) then
    Result := 0.75 - Sqr(Value)
  else if (Value < 1.5) then
  begin
    Value := Value - 1.5;
    Result := 0.5 * Sqr(Value);
  end
  else
    Result := 0.0;
end;

function TBellInterpolation.MaxSupport: double;
begin
  Result := 1.5;
end;

end.


