// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
/**
 * @class   vtkImageSincInterpolator
 * @brief   perform sinc interpolation on images
 *
 * vtkImageSincInterpolator provides various windowed sinc interpolation
 * methods for image data.  The default is a five-lobed Lanczos interpolant,
 * with a kernel size of 6.  The interpolator can also bandlimit the image,
 * which can be used for antialiasing.  The interpolation kernels are
 * evaluated via a lookup table for efficiency.
 * @par Thanks:
 * Thanks to David Gobbi at the Seaman Family MR Centre and Dept. of Clinical
 * Neurosciences, Foothills Medical Centre, Calgary, for providing this class.
 * @sa
 * vtkImageReslice
 */

#ifndef vtkImageSincInterpolator_h
#define vtkImageSincInterpolator_h

#include "vtkAbstractImageInterpolator.h"
#include "vtkImagingCoreModule.h" // For export macro

#define VTK_LANCZOS_WINDOW 0
#define VTK_KAISER_WINDOW 1
#define VTK_COSINE_WINDOW 2
#define VTK_HANN_WINDOW 3
#define VTK_HAMMING_WINDOW 4
#define VTK_BLACKMAN_WINDOW 5
#define VTK_BLACKMAN_HARRIS3 6
#define VTK_BLACKMAN_HARRIS4 7
#define VTK_NUTTALL_WINDOW 8
#define VTK_BLACKMAN_NUTTALL3 9
#define VTK_BLACKMAN_NUTTALL4 10
#define VTK_SINC_KERNEL_SIZE_MAX 32

VTK_ABI_NAMESPACE_BEGIN
class vtkImageData;
struct vtkInterpolationInfo;

class VTKIMAGINGCORE_EXPORT vtkImageSincInterpolator : public vtkAbstractImageInterpolator
{
public:
  static vtkImageSincInterpolator* New();
  vtkTypeMacro(vtkImageSincInterpolator, vtkAbstractImageInterpolator);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  ///@{
  /**
   * The window function to use.  The default is Lanczos, which is very
   * popular and performs well with a kernel width of 6.  The Cosine
   * window is included for historical reasons.  All other windows are
   * described in AH Nuttall, "Some windows with very good sidelobe
   * behavior," IEEE Transactions on Acoustics, Speech, and Signal
   * Processing 29:84-91, 1981.
   */
  virtual void SetWindowFunction(int mode);
  void SetWindowFunctionToLanczos() { this->SetWindowFunction(VTK_LANCZOS_WINDOW); }
  void SetWindowFunctionToKaiser() { this->SetWindowFunction(VTK_KAISER_WINDOW); }
  void SetWindowFunctionToCosine() { this->SetWindowFunction(VTK_COSINE_WINDOW); }
  void SetWindowFunctionToHann() { this->SetWindowFunction(VTK_HANN_WINDOW); }
  void SetWindowFunctionToHamming() { this->SetWindowFunction(VTK_HAMMING_WINDOW); }
  void SetWindowFunctionToBlackman() { this->SetWindowFunction(VTK_BLACKMAN_WINDOW); }
  void SetWindowFunctionToBlackmanHarris3() { this->SetWindowFunction(VTK_BLACKMAN_HARRIS3); }
  void SetWindowFunctionToBlackmanHarris4() { this->SetWindowFunction(VTK_BLACKMAN_HARRIS4); }
  void SetWindowFunctionToNuttall() { this->SetWindowFunction(VTK_NUTTALL_WINDOW); }
  void SetWindowFunctionToBlackmanNuttall3() { this->SetWindowFunction(VTK_BLACKMAN_NUTTALL3); }
  void SetWindowFunctionToBlackmanNuttall4() { this->SetWindowFunction(VTK_BLACKMAN_NUTTALL4); }
  int GetWindowFunction() { return this->WindowFunction; }
  virtual const char* GetWindowFunctionAsString();
  ///@}

  /**
   * Set the window half-width, this must be an integer between 1 and 16,
   * with a default value of 3.  The kernel size will be twice this value
   * if no blur factors are applied. The total number of sinc lobes will
   * be one less than twice the half-width, so if the half-width is 3 then
   * the kernel size will be 6 and there will be 5 sinc lobes.
   */
  void SetWindowHalfWidth(int n);
  int GetWindowHalfWidth() { return this->WindowHalfWidth; }

  /**
   * Turn this on in order to use SetWindowParameter.  If it is off,
   * then the default parameter will be used for the window.
   */
  void SetUseWindowParameter(int val);
  void UseWindowParameterOn() { this->SetUseWindowParameter(1); }
  void UseWindowParameterOff() { this->SetUseWindowParameter(0); }
  int GetUseWindowParameter() { return this->UseWindowParameter; }

  /**
   * Set the alpha parameter for the Kaiser window function.
   * This parameter will be ignored unless UseWindowParameter is On.
   * If UseWindowParameter is Off, then alpha is set to be the same as n
   * where n is the window half-width.  Using an alpha less than n
   * increases the sharpness and ringing, while using an alpha greater
   * than n increases the blurring.
   */
  void SetWindowParameter(double parm);
  double GetWindowParameter() { return this->WindowParameter; }

  /**
   * Get the support size for use in computing update extents.  If the data
   * will be sampled on a regular grid, then pass a matrix describing the
   * structured coordinate transformation between the output and the input.
   * Otherwise, pass nullptr as the matrix to retrieve the full kernel size.
   */
  void ComputeSupportSize(const double matrix[16], int support[3]) override;

  ///@{
  /**
   * Blur the image by widening the windowed sinc kernel by the specified
   * factors for the x, y, and z directions.  This reduces the bandwidth
   * by these same factors.  If you turn Antialiasing on, then the blur
   * factors will be computed automatically from the output sampling rate.
   * Blurring increases the computation time because the kernel size
   * increases by the blur factor.
   */
  void SetBlurFactors(double x, double y, double z);
  void SetBlurFactors(const double f[3]) { this->SetBlurFactors(f[0], f[1], f[2]); }
  void GetBlurFactors(double f[3])
  {
    f[0] = this->BlurFactors[0];
    f[1] = this->BlurFactors[1];
    f[2] = this->BlurFactors[2];
  }
  double* GetBlurFactors() VTK_SIZEHINT(3) { return this->BlurFactors; }
  ///@}

  /**
   * Turn on antialiasing.  If antialiasing is on, then the BlurFactors
   * will be computed automatically from the output sampling rate such that
   * that the image will be bandlimited to the Nyquist frequency.  This
   * is only applicable when the interpolator is being used by a resampling
   * filter like vtkImageReslice.  Such a filter will indicate the output
   * sampling by calling the interpolator's ComputeSupportSize() method,
   * which will compute the blur factors at the same time that it computes
   * the support size.
   */
  void SetAntialiasing(int antialiasing);
  void AntialiasingOn() { this->SetAntialiasing(1); }
  void AntialiasingOff() { this->SetAntialiasing(0); }
  int GetAntialiasing() { return this->Antialiasing; }

  /**
   * Turn off renormalization.  Most of the sinc windows provide kernels
   * for which the weights do not sum to one, and for which the sum depends
   * on the offset.  This results in small ripple artifacts in the output.
   * By default, the vtkImageSincInterpolator will renormalize these kernels.
   * This method allows the renormalization to be turned off.
   */
  void SetRenormalization(int renormalization);
  void RenormalizationOn() { this->SetRenormalization(1); }
  void RenormalizationOff() { this->SetRenormalization(0); }
  int GetRenormalization() { return this->Renormalization; }

  /**
   * Returns true if the interpolator supports weight precomputation.
   * This will always return true for this interpolator.
   */
  bool IsSeparable() override;

  ///@{
  /**
   * If the data is going to be sampled on a regular grid, then the
   * interpolation weights can be precomputed.  A matrix must be
   * supplied that provides a transformation between the provided
   * extent and the structured coordinates of the input.  This
   * matrix must perform only permutations, scales, and translation,
   * i.e. each of the three columns must have only one non-zero value.
   * A new extent is provided for out-of-bounds checks.
   * THIS METHOD IS THREAD SAFE.
   */
  void PrecomputeWeightsForExtent(const double matrix[16], const int extent[6], int newExtent[6],
    vtkInterpolationWeights*& weights) override;
  void PrecomputeWeightsForExtent(const float matrix[16], const int extent[6], int newExtent[6],
    vtkInterpolationWeights*& weights) override;
  ///@}

  /**
   * Free the precomputed weights.  THIS METHOD IS THREAD SAFE.
   */
  void FreePrecomputedWeights(vtkInterpolationWeights*& weights) override;

protected:
  vtkImageSincInterpolator();
  ~vtkImageSincInterpolator() override;

  /**
   * Update the interpolator.
   */
  void InternalUpdate() override;

  /**
   * Copy the interpolator.
   */
  void InternalDeepCopy(vtkAbstractImageInterpolator* obj) override;

  ///@{
  /**
   * Get the interpolation functions.
   */
  void GetInterpolationFunc(
    void (**doublefunc)(vtkInterpolationInfo*, const double[3], double*)) override;
  void GetInterpolationFunc(
    void (**floatfunc)(vtkInterpolationInfo*, const float[3], float*)) override;
  ///@}

  ///@{
  /**
   * Get the row interpolation functions.
   */
  void GetRowInterpolationFunc(
    void (**doublefunc)(vtkInterpolationWeights*, int, int, int, double*, int)) override;
  void GetRowInterpolationFunc(
    void (**floatfunc)(vtkInterpolationWeights*, int, int, int, float*, int)) override;
  ///@}

  /**
   * Build the lookup tables used for the interpolation.
   */
  virtual void BuildKernelLookupTable();

  /**
   * Free the kernel lookup tables.
   */
  virtual void FreeKernelLookupTable();

  int WindowFunction;
  int WindowHalfWidth;
  float* KernelLookupTable[3];
  int KernelSize[3];
  int Antialiasing;
  int Renormalization;
  double BlurFactors[3];
  double LastBlurFactors[3];
  double WindowParameter;
  int UseWindowParameter;

private:
  vtkImageSincInterpolator(const vtkImageSincInterpolator&) = delete;
  void operator=(const vtkImageSincInterpolator&) = delete;
};

VTK_ABI_NAMESPACE_END
#endif
