/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkFixedPointVolumeRayCastMapper.h

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/**
 * @class   vtkFixedPointVolumeRayCastMapper
 * @brief   A fixed point mapper for volumes
 *
 * This is a software ray caster for rendering volumes in vtkImageData.
 * It works with all input data types and up to four components. It performs
 * composite or MIP rendering, and can be intermixed with geometric data.
 * Space leaping is used to speed up the rendering process. In addition,
 * calculation are performed in 15 bit fixed point precision. This mapper
 * is threaded, and will interleave scan lines across processors.
 *
 * WARNING: This ray caster may not produce consistent results when
 * the number of threads exceeds 1. The class warns if the number of
 * threads > 1. The differences may be subtle. Applications should decide
 * if the trade-off in performance is worth the lack of consistency.
 *
 * Other limitations of this ray caster include that:
 *   - it does not do isosurface ray casting
 *   - it does only interpolate before classify compositing
 *   - it does only maximum scalar value MIP
 *
 * This mapper handles all data type from unsigned char through double.
 * However, some of the internal calcultions are performed in float and
 * therefore even the full float range may cause problems for this mapper
 * (both in scalar data values and in spacing between samples).
 *
 * Space leaping is performed by creating a sub-sampled volume. 4x4x4
 * cells in the original volume are represented by a min, max, and
 * combined gradient and flag value. The min max volume has three
 * unsigned shorts per 4x4x4 group of cells from the original volume -
 * one reprenting the minimum scalar index (the scalar value adjusted
 * to fit in the 15 bit range), the maximum scalar index, and a
 * third unsigned short which is both the maximum gradient opacity in
 * the neighborhood (an unsigned char) and the flag that is filled
 * in for the current lookup tables to indicate whether this region
 * can be skipped.
 *
 * @sa
 * vtkVolumeMapper
*/

#ifndef vtkFixedPointVolumeRayCastMapper_h
#define vtkFixedPointVolumeRayCastMapper_h

#include "vtkRenderingVolumeModule.h" // For export macro
#include "vtkVolumeMapper.h"

#define VTKKW_FP_SHIFT       15
#define VTKKW_FPMM_SHIFT     17
#define VTKKW_FP_MASK        0x7fff
#define VTKKW_FP_SCALE       32767.0

class vtkMatrix4x4;
class vtkMultiThreader;
class vtkPlaneCollection;
class vtkRenderer;
class vtkTimerLog;
class vtkVolume;
class vtkTransform;
class vtkRenderWindow;
class vtkColorTransferFunction;
class vtkPiecewiseFunction;
class vtkFixedPointVolumeRayCastMIPHelper;
class vtkFixedPointVolumeRayCastCompositeHelper;
class vtkFixedPointVolumeRayCastCompositeGOHelper;
class vtkFixedPointVolumeRayCastCompositeGOShadeHelper;
class vtkFixedPointVolumeRayCastCompositeShadeHelper;
class vtkVolumeRayCastSpaceLeapingImageFilter;
class vtkDirectionEncoder;
class vtkEncodedGradientShader;
class vtkFiniteDifferenceGradientEstimator;
class vtkRayCastImageDisplayHelper;
class vtkFixedPointRayCastImage;
class vtkDataArray;

// Forward declaration needed for use by friend declaration below.
VTK_THREAD_RETURN_TYPE FixedPointVolumeRayCastMapper_CastRays( void *arg );
VTK_THREAD_RETURN_TYPE vtkFPVRCMSwitchOnDataType( void *arg );

class VTKRENDERINGVOLUME_EXPORT vtkFixedPointVolumeRayCastMapper : public vtkVolumeMapper
{
public:
  static vtkFixedPointVolumeRayCastMapper *New();
  vtkTypeMacro(vtkFixedPointVolumeRayCastMapper,vtkVolumeMapper);
  void PrintSelf( ostream& os, vtkIndent indent ) override;

  //@{
  /**
   * Set/Get the distance between samples used for rendering
   * when AutoAdjustSampleDistances is off, or when this mapper
   * has more than 1 second allocated to it for rendering.
   */
  vtkSetMacro( SampleDistance, float );
  vtkGetMacro( SampleDistance, float );
  //@}

  //@{
  /**
   * Set/Get the distance between samples when interactive rendering is happening.
   * In this case, interactive is defined as this volume mapper having less than 1
   * second allocated for rendering. When AutoAdjustSampleDistance is On, and the
   * allocated render time is less than 1 second, then this InteractiveSampleDistance
   * will be used instead of the SampleDistance above.
   */
  vtkSetMacro( InteractiveSampleDistance, float );
  vtkGetMacro( InteractiveSampleDistance, float );
  //@}

  //@{
  /**
   * Sampling distance in the XY image dimensions. Default value of 1 meaning
   * 1 ray cast per pixel. If set to 0.5, 4 rays will be cast per pixel. If
   * set to 2.0, 1 ray will be cast for every 4 (2 by 2) pixels. This value
   * will be adjusted to meet a desired frame rate when AutoAdjustSampleDistances
   * is on.
   */
  vtkSetClampMacro( ImageSampleDistance, float, 0.1f, 100.0f );
  vtkGetMacro( ImageSampleDistance, float );
  //@}

  //@{
  /**
   * This is the minimum image sample distance allow when the image
   * sample distance is being automatically adjusted.
   */
  vtkSetClampMacro( MinimumImageSampleDistance, float, 0.1f, 100.0f );
  vtkGetMacro( MinimumImageSampleDistance, float );
  //@}

  //@{
  /**
   * This is the maximum image sample distance allow when the image
   * sample distance is being automatically adjusted.
   */
  vtkSetClampMacro( MaximumImageSampleDistance, float, 0.1f, 100.0f );
  vtkGetMacro( MaximumImageSampleDistance, float );
  //@}

  //@{
  /**
   * If AutoAdjustSampleDistances is on, the ImageSampleDistance
   * and the SampleDistance will be varied to achieve the allocated
   * render time of this prop (controlled by the desired update rate
   * and any culling in use). If this is an interactive render (more
   * than 1 frame per second) the SampleDistance will be increased,
   * otherwise it will not be altered (a binary decision, as opposed
   * to the ImageSampleDistance which will vary continuously).
   */
  vtkSetClampMacro( AutoAdjustSampleDistances, vtkTypeBool, 0, 1 );
  vtkGetMacro( AutoAdjustSampleDistances, vtkTypeBool );
  vtkBooleanMacro( AutoAdjustSampleDistances, vtkTypeBool );
  //@}

  //@{
  /**
   * Automatically compute the sample distance from the data spacing.  When
   * the number of voxels is 8, the sample distance will be roughly 1/200
   * the average voxel size. The distance will grow proportionally to
   * numVoxels^(1/3) until it reaches 1/2 average voxel size when number of
   * voxels is 1E6. Note that ScalarOpacityUnitDistance is still taken into
   * account and if different than 1, will effect the sample distance.
   */
  vtkSetClampMacro( LockSampleDistanceToInputSpacing, vtkTypeBool, 0, 1 );
  vtkGetMacro( LockSampleDistanceToInputSpacing, vtkTypeBool );
  vtkBooleanMacro( LockSampleDistanceToInputSpacing, vtkTypeBool );
  //@}

  //@{
  /**
   * Set/Get the number of threads to use. This by default is equal to
   * the number of available processors detected.
   * WARNING: If number of threads > 1, results may not be consistent.
   */
  void SetNumberOfThreads( int num );
  int GetNumberOfThreads();
  //@}

  //@{
  /**
   * If IntermixIntersectingGeometry is turned on, the zbuffer will be
   * captured and used to limit the traversal of the rays.
   */
  vtkSetClampMacro( IntermixIntersectingGeometry, vtkTypeBool, 0, 1 );
  vtkGetMacro( IntermixIntersectingGeometry, vtkTypeBool );
  vtkBooleanMacro( IntermixIntersectingGeometry, vtkTypeBool );
  //@}

  //@{
  /**
   * What is the image sample distance required to achieve the desired time?
   * A version of this method is provided that does not require the volume
   * argument since if you are using an LODProp3D you may not know this information.
   * If you use this version you must be certain that the ray cast mapper is
   * only used for one volume (and not shared among multiple volumes)
   */
  float ComputeRequiredImageSampleDistance( float desiredTime,
                                            vtkRenderer *ren );
  float ComputeRequiredImageSampleDistance( float desiredTime,
                                            vtkRenderer *ren,
                                            vtkVolume *vol );
  //@}

  /**
   * WARNING: INTERNAL METHOD - NOT INTENDED FOR GENERAL USE
   * Initialize rendering for this volume.
   */
  void Render( vtkRenderer *, vtkVolume * ) override;

  unsigned int ToFixedPointPosition( float val );
  void ToFixedPointPosition( float in[3], unsigned int out[3] );
  unsigned int ToFixedPointDirection( float dir );
  void ToFixedPointDirection( float in[3], unsigned int out[3] );
  void FixedPointIncrement( unsigned int position[3], unsigned int increment[3] );
  void GetFloatTripleFromPointer( float v[3], float *ptr );
  void GetUIntTripleFromPointer( unsigned int v[3], unsigned int *ptr );
  void ShiftVectorDown( unsigned int in[3], unsigned int out[3] );
  int CheckMinMaxVolumeFlag( unsigned int pos[3], int c );
  int CheckMIPMinMaxVolumeFlag( unsigned int pos[3], int c, unsigned short maxIdx, int flip );

  void LookupColorUC( unsigned short *colorTable,
                      unsigned short *scalarOpacityTable,
                      unsigned short index,
                      unsigned char  color[4] );
  void LookupDependentColorUC( unsigned short *colorTable,
                               unsigned short *scalarOpacityTable,
                               unsigned short index[4],
                               int            components,
                               unsigned char  color[4] );
  void LookupAndCombineIndependentColorsUC(
    unsigned short *colorTable[4],
    unsigned short *scalarOpacityTable[4],
    unsigned short index[4],
    float          weights[4],
    int            components,
    unsigned char  color[4] );
  int CheckIfCropped( unsigned int pos[3] );

  vtkGetObjectMacro( RenderWindow, vtkRenderWindow );
  vtkGetObjectMacro( MIPHelper, vtkFixedPointVolumeRayCastMIPHelper );
  vtkGetObjectMacro( CompositeHelper, vtkFixedPointVolumeRayCastCompositeHelper );
  vtkGetObjectMacro( CompositeGOHelper, vtkFixedPointVolumeRayCastCompositeGOHelper );
  vtkGetObjectMacro( CompositeGOShadeHelper, vtkFixedPointVolumeRayCastCompositeGOShadeHelper );
  vtkGetObjectMacro( CompositeShadeHelper, vtkFixedPointVolumeRayCastCompositeShadeHelper );
  vtkGetVectorMacro( TableShift, float, 4 );
  vtkGetVectorMacro( TableScale, float, 4 );
  vtkGetMacro( ShadingRequired, int );
  vtkGetMacro( GradientOpacityRequired, int );

  vtkGetObjectMacro( CurrentScalars, vtkDataArray );
  vtkGetObjectMacro( PreviousScalars, vtkDataArray );


  int             *GetRowBounds()                 {return this->RowBounds;}
  unsigned short  *GetColorTable(int c)           {return this->ColorTable[c];}
  unsigned short  *GetScalarOpacityTable(int c)   {return this->ScalarOpacityTable[c];}
  unsigned short  *GetGradientOpacityTable(int c) {return this->GradientOpacityTable[c];}
  vtkVolume       *GetVolume()                    {return this->Volume;}
  unsigned short **GetGradientNormal()            {return this->GradientNormal;}
  unsigned char  **GetGradientMagnitude()         {return this->GradientMagnitude;}
  unsigned short  *GetDiffuseShadingTable(int c)  {return this->DiffuseShadingTable[c];}
  unsigned short  *GetSpecularShadingTable(int c) {return this->SpecularShadingTable[c];}

  void ComputeRayInfo( int x, int y,
                       unsigned int pos[3],
                       unsigned int dir[3],
                       unsigned int *numSteps );

  void InitializeRayInfo( vtkVolume *vol );

  int ShouldUseNearestNeighborInterpolation( vtkVolume *vol );

  //@{
  /**
   * Set / Get the underlying image object. One will be automatically
   * created - only need to set it when using from an AMR mapper which
   * renders multiple times into the same image.
   */
  void SetRayCastImage( vtkFixedPointRayCastImage * );
  vtkGetObjectMacro( RayCastImage, vtkFixedPointRayCastImage  );
  //@}

  int PerImageInitialization( vtkRenderer *, vtkVolume *, int,
                              double *, double *, int * );
  void PerVolumeInitialization( vtkRenderer *, vtkVolume * );
  void PerSubVolumeInitialization( vtkRenderer *, vtkVolume *, int );
  void RenderSubVolume();
  void DisplayRenderedImage( vtkRenderer *, vtkVolume * );
  void AbortRender();

  void CreateCanonicalView( vtkVolume *volume,
                            vtkImageData *image,
                            int blend_mode,
                            double viewDirection[3],
                            double viewUp[3] );

  /**
   * Get an estimate of the rendering time for a given volume / renderer.
   * Only valid if this mapper has been used to render that volume for
   * that renderer previously. Estimate is good when the viewing parameters
   * have not changed much since that last render.
   */
  float GetEstimatedRenderTime( vtkRenderer *ren,
                                vtkVolume   *vol )
    { return this->RetrieveRenderTime( ren, vol ); }
  float GetEstimatedRenderTime( vtkRenderer *ren )
    { return this->RetrieveRenderTime( ren ); }


  //@{
  /**
   * Set/Get the window / level applied to the final color.
   * This allows brightness / contrast adjustments on the
   * final image.
   * window is the width of the window.
   * level is the center of the window.
   * Initial window value is 1.0
   * Initial level value is 0.5
   * window cannot be null but can be negative, this way
   * values will be reversed.
   * |window| can be larger than 1.0
   * level can be any real value.
   */
  vtkSetMacro( FinalColorWindow, float );
  vtkGetMacro( FinalColorWindow, float );
  vtkSetMacro( FinalColorLevel,  float );
  vtkGetMacro( FinalColorLevel,  float );
  //@}


  // Here to be used by the mapper to tell the helper
  // to flip the MIP comparison in order to support
  // minimum intensity blending
  vtkGetMacro( FlipMIPComparison, int );

  /**
   * WARNING: INTERNAL METHOD - NOT INTENDED FOR GENERAL USE
   * Release any graphics resources that are being consumed by this mapper.
   * The parameter window could be used to determine which graphic
   * resources to release.
   */
  void ReleaseGraphicsResources(vtkWindow *) override;

protected:
  vtkFixedPointVolumeRayCastMapper();
  ~vtkFixedPointVolumeRayCastMapper() override;

  // The helper class that displays the image
  vtkRayCastImageDisplayHelper *ImageDisplayHelper;

  // The distance between sample points along the ray
  float                        SampleDistance;
  float                        InteractiveSampleDistance;

  // The distance between rays in the image
  float                        ImageSampleDistance;
  float                        MinimumImageSampleDistance;
  float                        MaximumImageSampleDistance;
  vtkTypeBool                          AutoAdjustSampleDistances;
  vtkTypeBool                          LockSampleDistanceToInputSpacing;

  // Saved values used to restore
  float                        OldSampleDistance;
  float                        OldImageSampleDistance;

  // Internal method for computing matrices needed during
  // ray casting
  void ComputeMatrices( double volumeOrigin[3],
                        double volumeSpacing[3],
                        int volumeExtent[6],
                        vtkRenderer  *ren,
                        vtkVolume    *vol );

  int ComputeRowBounds( vtkRenderer *ren,
                        int imageFlag, int rowBoundsFlag,
                        int volumeExtent[6]);

  void CaptureZBuffer( vtkRenderer *ren );

  friend VTK_THREAD_RETURN_TYPE FixedPointVolumeRayCastMapper_CastRays( void *arg );
  friend VTK_THREAD_RETURN_TYPE vtkFPVRCMSwitchOnDataType( void *arg );

  vtkMultiThreader  *Threader;

  vtkMatrix4x4   *PerspectiveMatrix;
  vtkMatrix4x4   *ViewToWorldMatrix;
  vtkMatrix4x4   *ViewToVoxelsMatrix;
  vtkMatrix4x4   *VoxelsToViewMatrix;
  vtkMatrix4x4   *WorldToVoxelsMatrix;
  vtkMatrix4x4   *VoxelsToWorldMatrix;

  vtkMatrix4x4   *VolumeMatrix;

  vtkTransform   *PerspectiveTransform;
  vtkTransform   *VoxelsTransform;
  vtkTransform   *VoxelsToViewTransform;

  // This object encapsulated the image and all related information
  vtkFixedPointRayCastImage *RayCastImage;

  int             *RowBounds;
  int             *OldRowBounds;

  float           *RenderTimeTable;
  vtkVolume      **RenderVolumeTable;
  vtkRenderer    **RenderRendererTable;
  int              RenderTableSize;
  int              RenderTableEntries;

  void             StoreRenderTime( vtkRenderer *ren, vtkVolume *vol, float t );
  float            RetrieveRenderTime( vtkRenderer *ren, vtkVolume *vol );
  float            RetrieveRenderTime( vtkRenderer *ren );

  vtkTypeBool              IntermixIntersectingGeometry;

  float            MinimumViewDistance;

  vtkColorTransferFunction *SavedRGBFunction[4];
  vtkPiecewiseFunction     *SavedGrayFunction[4];
  vtkPiecewiseFunction     *SavedScalarOpacityFunction[4];
  vtkPiecewiseFunction     *SavedGradientOpacityFunction[4];
  int                       SavedColorChannels[4];
  float                     SavedScalarOpacityDistance[4];
  int                       SavedBlendMode;
  vtkImageData             *SavedParametersInput;
  vtkTimeStamp              SavedParametersMTime;

  vtkImageData             *SavedGradientsInput;
  vtkTimeStamp              SavedGradientsMTime;

  float                     SavedSampleDistance;


  unsigned short            ColorTable[4][32768*3];
  unsigned short            ScalarOpacityTable[4][32768];
  unsigned short            GradientOpacityTable[4][256];
  int                       TableSize[4];
  float                     TableScale[4];
  float                     TableShift[4];

  float                     GradientMagnitudeScale[4];
  float                     GradientMagnitudeShift[4];

  unsigned short           **GradientNormal;
  unsigned char            **GradientMagnitude;
  unsigned short            *ContiguousGradientNormal;
  unsigned char             *ContiguousGradientMagnitude;

  int                        NumberOfGradientSlices;

  vtkDirectionEncoder       *DirectionEncoder;

  vtkEncodedGradientShader  *GradientShader;

  vtkFiniteDifferenceGradientEstimator *GradientEstimator;

  unsigned short             DiffuseShadingTable [4][65536*3];
  unsigned short             SpecularShadingTable[4][65536*3];

  int                        ShadingRequired;
  int                        GradientOpacityRequired;

  vtkDataArray              *CurrentScalars;
  vtkDataArray              *PreviousScalars;

  vtkRenderWindow           *RenderWindow;
  vtkVolume                 *Volume;

  int           ClipRayAgainstVolume( float rayStart[3],
                                      float rayEnd[3],
                                      float rayDirection[3],
                                      double bounds[6] );

  int           UpdateColorTable( vtkVolume *vol );
  int           UpdateGradients( vtkVolume *vol );
  int           UpdateShadingTable( vtkRenderer *ren,
                                    vtkVolume *vol );
  void          UpdateCroppingRegions();

  void          ComputeGradients( vtkVolume *vol );

  int           ClipRayAgainstClippingPlanes( float  rayStart[3],
                                              float  rayEnd[3],
                                              int    numClippingPlanes,
                                              float *clippingPlanes );

  unsigned int  FixedPointCroppingRegionPlanes[6];
  unsigned int  CroppingRegionMask[27];

  // Get the ZBuffer value corresponding to location (x,y) where (x,y)
  // are indexing into the ImageInUse image. This must be converted to
  // the zbuffer image coordinates. Nearest neighbor value is returned.
  float         GetZBufferValue( int x, int y );

  vtkFixedPointVolumeRayCastMIPHelper              *MIPHelper;
  vtkFixedPointVolumeRayCastCompositeHelper        *CompositeHelper;
  vtkFixedPointVolumeRayCastCompositeGOHelper      *CompositeGOHelper;
  vtkFixedPointVolumeRayCastCompositeShadeHelper   *CompositeShadeHelper;
  vtkFixedPointVolumeRayCastCompositeGOShadeHelper *CompositeGOShadeHelper;

  // Some variables used for ray computation
  float ViewToVoxelsArray[16];
  float WorldToVoxelsArray[16];
  float VoxelsToWorldArray[16];

  double CroppingBounds[6];

  int NumTransformedClippingPlanes;
  float *TransformedClippingPlanes;

  double SavedSpacing[3];


  // Min Max structure used to do space leaping
  unsigned short *MinMaxVolume;
  int             MinMaxVolumeSize[4];
  vtkImageData   *SavedMinMaxInput;
  vtkImageData   *MinMaxVolumeCache;
  vtkVolumeRayCastSpaceLeapingImageFilter * SpaceLeapFilter;

  void            UpdateMinMaxVolume( vtkVolume *vol );
  void            FillInMaxGradientMagnitudes( int fullDim[3],
                                               int smallDim[3] );

  float FinalColorWindow;
  float FinalColorLevel;

  int FlipMIPComparison;

  void ApplyFinalColorWindowLevel();

private:
  vtkFixedPointVolumeRayCastMapper(const vtkFixedPointVolumeRayCastMapper&) = delete;
  void operator=(const vtkFixedPointVolumeRayCastMapper&) = delete;

  bool ThreadWarning;
};


inline unsigned int vtkFixedPointVolumeRayCastMapper::ToFixedPointPosition( float val )
{
  return static_cast<unsigned int>(val * VTKKW_FP_SCALE + 0.5);
}

inline void vtkFixedPointVolumeRayCastMapper::ToFixedPointPosition( float in[3], unsigned int out[3] )
{
  out[0] = static_cast<unsigned int>(in[0] * VTKKW_FP_SCALE + 0.5);
  out[1] = static_cast<unsigned int>(in[1] * VTKKW_FP_SCALE + 0.5);
  out[2] = static_cast<unsigned int>(in[2] * VTKKW_FP_SCALE + 0.5);
}

inline unsigned int vtkFixedPointVolumeRayCastMapper::ToFixedPointDirection( float dir )
{
  return ((dir<0.0)?
          (static_cast<unsigned int>(-dir * VTKKW_FP_SCALE + 0.5)):
          (0x80000000+static_cast<unsigned int>(dir*VTKKW_FP_SCALE + 0.5)));
}

inline void vtkFixedPointVolumeRayCastMapper::ToFixedPointDirection( float in[3], unsigned int out[3] )
{
  out[0] = ((in[0]<0.0)?
            (static_cast<unsigned int>(-in[0] * VTKKW_FP_SCALE + 0.5)):
            (0x80000000+
             static_cast<unsigned int>(in[0]*VTKKW_FP_SCALE + 0.5)));
  out[1] = ((in[1]<0.0)?
            (static_cast<unsigned int>(-in[1] * VTKKW_FP_SCALE + 0.5)):
            (0x80000000+
             static_cast<unsigned int>(in[1]*VTKKW_FP_SCALE + 0.5)));
  out[2] = ((in[2]<0.0)?
            (static_cast<unsigned int>(-in[2] * VTKKW_FP_SCALE + 0.5)):
            (0x80000000+
             static_cast<unsigned int>(in[2]*VTKKW_FP_SCALE + 0.5)));
}

inline void vtkFixedPointVolumeRayCastMapper::FixedPointIncrement( unsigned int position[3], unsigned int increment[3] )
{
  if ( increment[0]&0x80000000 )
  {
    position[0] += (increment[0]&0x7fffffff);
  }
  else
  {
    position[0] -= increment[0];
  }
  if ( increment[1]&0x80000000 )
  {
    position[1] += (increment[1]&0x7fffffff);
  }
  else
  {
    position[1] -= increment[1];
  }
  if ( increment[2]&0x80000000 )
  {
    position[2] += (increment[2]&0x7fffffff);
  }
  else
  {
    position[2] -= increment[2];
  }
}


inline void vtkFixedPointVolumeRayCastMapper::GetFloatTripleFromPointer( float v[3], float *ptr )
{
  v[0] = *(ptr);
  v[1] = *(ptr+1);
  v[2] = *(ptr+2);
}

inline void vtkFixedPointVolumeRayCastMapper::GetUIntTripleFromPointer( unsigned int v[3], unsigned int *ptr )
{
  v[0] = *(ptr);
  v[1] = *(ptr+1);
  v[2] = *(ptr+2);
}

inline void vtkFixedPointVolumeRayCastMapper::ShiftVectorDown( unsigned int in[3],
                                                       unsigned int out[3] )
{
  out[0] = in[0] >> VTKKW_FP_SHIFT;
  out[1] = in[1] >> VTKKW_FP_SHIFT;
  out[2] = in[2] >> VTKKW_FP_SHIFT;
}

inline int vtkFixedPointVolumeRayCastMapper::CheckMinMaxVolumeFlag( unsigned int mmpos[3], int c )
{
  vtkIdType offset =
    static_cast<vtkIdType>(this->MinMaxVolumeSize[3]) *
    ( mmpos[2]*static_cast<vtkIdType>(
        this->MinMaxVolumeSize[0]*this->MinMaxVolumeSize[1]) +
      mmpos[1]*static_cast<vtkIdType>(this->MinMaxVolumeSize[0]) +
      mmpos[0] ) + static_cast<vtkIdType>(c);

  return ((*(this->MinMaxVolume + 3*offset + 2))&0x00ff);
}

inline int vtkFixedPointVolumeRayCastMapper::CheckMIPMinMaxVolumeFlag( unsigned int mmpos[3], int c,
                                                                       unsigned short maxIdx, int flip )
{
  vtkIdType offset =
    static_cast<vtkIdType>(this->MinMaxVolumeSize[3]) *
    ( mmpos[2]*static_cast<vtkIdType>(
        this->MinMaxVolumeSize[0]*this->MinMaxVolumeSize[1]) +
      mmpos[1]*static_cast<vtkIdType>(this->MinMaxVolumeSize[0]) +
      mmpos[0] ) + static_cast<vtkIdType>(c);

  if ( (*(this->MinMaxVolume + 3*offset + 2)&0x00ff) )
  {
    if (flip)
    {
      return ( *(this->MinMaxVolume + 3*offset) < maxIdx );
    }
    else
    {
      return ( *(this->MinMaxVolume + 3*offset + 1) > maxIdx );
    }
  }
  else
  {
    return 0;
  }
}

inline void vtkFixedPointVolumeRayCastMapper::LookupColorUC( unsigned short *colorTable,
                                                     unsigned short *scalarOpacityTable,
                                                     unsigned short index,
                                                     unsigned char  color[4] )
{
  unsigned short alpha = scalarOpacityTable[index];
  color[0] = static_cast<unsigned char>
    ((colorTable[3*index  ]*alpha + 0x7fff)>>(2*VTKKW_FP_SHIFT - 8));
  color[1] = static_cast<unsigned char>
    ((colorTable[3*index+1]*alpha + 0x7fff)>>(2*VTKKW_FP_SHIFT - 8));
  color[2] = static_cast<unsigned char>
    ((colorTable[3*index+2]*alpha + 0x7fff)>>(2*VTKKW_FP_SHIFT - 8));
  color[3] = static_cast<unsigned char>(alpha>>(VTKKW_FP_SHIFT - 8));
}

inline void vtkFixedPointVolumeRayCastMapper::LookupDependentColorUC( unsigned short *colorTable,
                                                              unsigned short *scalarOpacityTable,
                                                              unsigned short index[4],
                                                              int            components,
                                                              unsigned char  color[4] )
{
  unsigned short alpha;
  switch ( components )
  {
    case 2:
      alpha = scalarOpacityTable[index[1]];
      color[0] = static_cast<unsigned char>
        ((colorTable[3*index[0]  ]*alpha + 0x7fff)>>(2*VTKKW_FP_SHIFT - 8));
      color[1] = static_cast<unsigned char>
        ((colorTable[3*index[0]+1]*alpha + 0x7fff)>>(2*VTKKW_FP_SHIFT - 8));
      color[2] = static_cast<unsigned char>
        ((colorTable[3*index[0]+2]*alpha + 0x7fff)>>(2*VTKKW_FP_SHIFT - 8));
      color[3] = static_cast<unsigned char>(alpha>>(VTKKW_FP_SHIFT - 8));
      break;
    case 4:
      alpha = scalarOpacityTable[index[3]];
      color[0] = static_cast<unsigned char>((index[0]*alpha + 0x7fff)>>VTKKW_FP_SHIFT );
      color[1] = static_cast<unsigned char>((index[1]*alpha + 0x7fff)>>VTKKW_FP_SHIFT );
      color[2] = static_cast<unsigned char>((index[2]*alpha + 0x7fff)>>VTKKW_FP_SHIFT );
      color[3] = static_cast<unsigned char>(alpha>>(VTKKW_FP_SHIFT - 8));
      break;
  }
}


inline void vtkFixedPointVolumeRayCastMapper::LookupAndCombineIndependentColorsUC( unsigned short *colorTable[4],
                                                                           unsigned short *scalarOpacityTable[4],
                                                                           unsigned short  index[4],
                                                                           float           weights[4],
                                                                           int             components,
                                                                           unsigned char   color[4] )
{
  unsigned int tmp[4] = {0,0,0,0};

  for ( int i = 0; i < components; i++ )
  {
    unsigned short alpha = static_cast<unsigned short>(static_cast<float>(scalarOpacityTable[i][index[i]])*weights[i]);
    tmp[0] += static_cast<unsigned char>(((colorTable[i][3*index[i]  ])*alpha + 0x7fff)>>(2*VTKKW_FP_SHIFT - 8));
    tmp[1] += static_cast<unsigned char>(((colorTable[i][3*index[i]+1])*alpha + 0x7fff)>>(2*VTKKW_FP_SHIFT - 8));
    tmp[2] += static_cast<unsigned char>(((colorTable[i][3*index[i]+2])*alpha + 0x7fff)>>(2*VTKKW_FP_SHIFT - 8));
    tmp[3] += static_cast<unsigned char>(alpha>>(VTKKW_FP_SHIFT - 8));
  }

  color[0] = static_cast<unsigned char>((tmp[0]>255)?(255):(tmp[0]));
  color[1] = static_cast<unsigned char>((tmp[1]>255)?(255):(tmp[1]));
  color[2] = static_cast<unsigned char>((tmp[2]>255)?(255):(tmp[2]));
  color[3] = static_cast<unsigned char>((tmp[3]>255)?(255):(tmp[3]));

}

inline int vtkFixedPointVolumeRayCastMapper::CheckIfCropped( unsigned int pos[3] )
{
  int idx;

  if ( pos[2] < this->FixedPointCroppingRegionPlanes[4] )
  {
    idx = 0;
  }
  else if ( pos[2] > this->FixedPointCroppingRegionPlanes[5] )
  {
    idx = 18;
  }
  else
  {
    idx = 9;
  }

  if ( pos[1] >= this->FixedPointCroppingRegionPlanes[2] )
  {
    if ( pos[1] > this->FixedPointCroppingRegionPlanes[3] )
    {
      idx += 6;
    }
    else
    {
      idx += 3;
    }
  }

  if ( pos[0] >= this->FixedPointCroppingRegionPlanes[0] )
  {
    if ( pos[0] > this->FixedPointCroppingRegionPlanes[1] )
    {
      idx += 2;
    }
    else
    {
      idx += 1;
    }
  }

  return !(static_cast<unsigned int>(this->CroppingRegionFlags)
           &this->CroppingRegionMask[idx]);
}

#endif
