Commit 5d8e8d52 authored by Alvaro Sanchez's avatar Alvaro Sanchez
Browse files

Added texture width checks in opacity and gradientOpacity tables.

ColorTransferFunction and PiecewiseFunction estimate an appropriate number of
samples based on a given range and minimum distance between nodes. This estimated
value is used by the volume lookup tables to sample the functions into the texture.
parent ddb5ebc0
Pipeline #23051 passed with stage
......@@ -682,6 +682,37 @@ int vtkPiecewiseFunction::AdjustRange(double range[2])
return 1;
}
//--------------------------------------------------------------------------
int vtkPiecewiseFunction::EstimateMinNumberOfSamples(double const & x1,
double const & x2)
{
double const d = this->FindMinimumXDistance();
int idealWidth = static_cast<int>(ceil((x2 - x1) / d));
return idealWidth;
}
//----------------------------------------------------------------------------
double vtkPiecewiseFunction::FindMinimumXDistance()
{
std::vector<vtkPiecewiseFunctionNode*> const & nodes = this->Internal->Nodes;
size_t const size = nodes.size();
if (size < 2)
return -1.0;
double distance = std::numeric_limits<double>::max();
for (size_t i = 0; i < size - 1; i++)
{
double const currentDist = nodes[i + 1]->X - nodes[i]->X;
if (currentDist < distance)
{
distance = currentDist;
}
}
return distance;
}
// Returns a table of function values evaluated at regular intervals
void vtkPiecewiseFunction::GetTable( double xStart, double xEnd,
int size, double* table,
......
......@@ -165,10 +165,26 @@ public:
vtkGetMacro(AllowDuplicateScalars, int);
vtkBooleanMacro(AllowDuplicateScalars, int);
// Description:
// Estimates the minimum size of a table such that it would correctly sample this function.
// The returned value should be passed as parameter 'n' when calling GetTable().
int EstimateMinNumberOfSamples(double const & x1, double const & x2);
protected:
vtkPiecewiseFunction();
~vtkPiecewiseFunction();
// Internal method to sort the vector and update the
// Range whenever a node is added, edited or removed.
// It always calls Modified().
void SortAndUpdateRange();
// Returns true if the range has been updated and Modified() has been called
bool UpdateRange();
// Description:
// Traverses the nodes to find the minimum distance. Assumes nodes are sorted.
double FindMinimumXDistance();
// The internal STL structures
vtkPiecewiseFunctionInternals *Internal;
......@@ -184,13 +200,6 @@ protected:
// Min and max range of function point locations
double Range[2];
// Internal method to sort the vector and update the
// Range whenever a node is added, edited or removed.
// It always calls Modified().
void SortAndUpdateRange();
// Returns true if the range has been updated and Modified() has been called
bool UpdateRange();
int AllowDuplicateScalars;
private:
......
......@@ -1835,6 +1835,16 @@ int vtkColorTransferFunction::AdjustRange(double range[2])
return 1;
}
//--------------------------------------------------------------------------
int vtkColorTransferFunction::EstimateMinNumberOfSamples(double const & x1,
double const & x2)
{
double const d = this->FindMinimumXDistance();
int idealWidth = static_cast<int>(ceil((x2 - x1) / d));
return idealWidth;
}
//----------------------------------------------------------------------------
double vtkColorTransferFunction::FindMinimumXDistance()
{
......@@ -1844,11 +1854,9 @@ double vtkColorTransferFunction::FindMinimumXDistance()
return -1.0;
double distance = std::numeric_limits<double>::max();
for (size_t i = 0; i < size - 1; i++)
{
double const currentDist = nodes.at(i + 1)->X - nodes.at(i)->X;
double const currentDist = nodes[i + 1]->X - nodes[i]->X;
if (currentDist < distance)
{
distance = currentDist;
......
......@@ -247,8 +247,9 @@ public:
virtual void GetIndexedColor(vtkIdType idx, double rgba[4]);
// Description:
// Traverses the nodes to find the minimum distance. Assumes nodes are sorted.
double FindMinimumXDistance();
// Estimates the minimum size of a table such that it would correctly sample this function.
// The returned value should be passed as parameter 'n' when calling GetTable().
int EstimateMinNumberOfSamples(double const & x1, double const & x2);
protected:
vtkColorTransferFunction();
......@@ -341,6 +342,10 @@ protected:
// any point existed at newX, it will also be removed.
void MovePoint(double oldX, double newX);
// Description:
// Traverses the nodes to find the minimum distance. Assumes nodes are sorted.
double FindMinimumXDistance();
private:
vtkColorTransferFunction(const vtkColorTransferFunction&) VTK_DELETE_FUNCTION;
void operator=(const vtkColorTransferFunction&) VTK_DELETE_FUNCTION;
......
......@@ -18,6 +18,7 @@
#include "vtkCamera.h"
#include "vtkColorTransferFunction.h"
#include "vtkGPUVolumeRayCastMapper.h"
#include "vtkFixedPointVolumeRayCastMapper.h"
#include "vtkImageData.h"
#include "vtkLookupTable.h"
#include "vtkPiecewiseFunction.h"
......@@ -33,6 +34,9 @@
#include "vtkVolumeProperty.h"
#include "vtkXMLImageDataReader.h"
#define GPU_MAPPER
//----------------------------------------------------------------------------
int TestGPURayCastLargeColorTransferFunction(int argc, char* argv[])
{
......@@ -43,8 +47,16 @@ int TestGPURayCastLargeColorTransferFunction(int argc, char* argv[])
// http://www.spl.harvard.edu/publications/item/view/2037
vtkSmartPointer<vtkLookupTable> lut =
vtkSmartPointer<vtkLookupTable>::New();
lut->SetNumberOfTableValues(5023);
lut->SetTableRange(0, 5022);
// Required for vtkLookupTable initialization
int const NumValues = 5023;
lut->SetNumberOfTableValues(NumValues);
lut->SetTableRange(0, NumValues-1);
for (int i = 0; i < NumValues; i++)
{
lut->SetTableValue(i, 0.0, 0.0, 0.0, 0.0);
}
lut->SetTableValue(0, 0 / 255.0, 0 / 255.0, 0 / 255.0, 0 / 255.0);
lut->SetTableValue(2, 250 / 255.0, 250 / 255.0, 225 / 255.0, 255 / 255.0);
lut->SetTableValue(3, 225 / 255.0, 190 / 255.0, 150 / 255.0, 255 / 255.0);
......@@ -80,7 +92,8 @@ int TestGPURayCastLargeColorTransferFunction(int argc, char* argv[])
lut->SetTableValue(54, 98 / 255.0, 153 / 255.0, 112 / 255.0, 255 / 255.0);
lut->SetTableValue(58, 165 / 255.0, 0 / 255.0, 255 / 255.0, 255 / 255.0);
lut->SetTableValue(60, 165 / 255.0, 40 / 255.0, 40 / 255.0, 255 / 255.0);
lut->SetTableValue(61, 135 / 255.0, 205 / 255.0, 235 / 255.0, 255 / 255.0);
// lut->SetTableValue(61, 165 / 255.0, 40 / 255.0, 40 / 255.0, 255 / 255.0);
lut->SetTableValue(61, 135 / 255.0, 205 / 255.0, 235 / 255.0, 255 / 255.0); //medulla oblongata
lut->SetTableValue(63, 90 / 255.0, 105 / 255.0, 215 / 255.0, 255 / 255.0);
lut->SetTableValue(66, 0 / 255.0, 108 / 255.0, 112 / 255.0, 255 / 255.0);
lut->SetTableValue(71, 0 / 255.0, 108 / 255.0, 112 / 255.0, 255 / 255.0);
......@@ -393,6 +406,7 @@ int TestGPURayCastLargeColorTransferFunction(int argc, char* argv[])
const double midPoint = 0.5;
const double sharpness = 1.0;
for (int i = 0; i < numColors; i++, value += step)
// for (int i = 0; i < numColors; i++, value += (50 * step) )
{
lut->GetTableValue(i, color);
......@@ -412,8 +426,14 @@ int TestGPURayCastLargeColorTransferFunction(int argc, char* argv[])
delete [] filename;
filename = NULL;
#ifdef GPU_MAPPER
vtkSmartPointer<vtkGPUVolumeRayCastMapper> volumeMapper =
vtkSmartPointer<vtkGPUVolumeRayCastMapper>::New();
#else
vtkSmartPointer<vtkFixedPointVolumeRayCastMapper> volumeMapper =
vtkSmartPointer<vtkFixedPointVolumeRayCastMapper>::New();
#endif
volumeMapper->SetInputData(reader->GetOutput());
vtkSmartPointer<vtkVolumeProperty> volumeProperty =
......@@ -421,7 +441,7 @@ int TestGPURayCastLargeColorTransferFunction(int argc, char* argv[])
volumeProperty->SetColor(colorTransferFunction);
volumeProperty->SetScalarOpacity(opacity);
volumeProperty->SetInterpolationTypeToNearest();
volumeProperty->ShadeOn();
// volumeProperty->ShadeOn();
volumeProperty->SetAmbient(0.3);
volumeProperty->SetDiffuse(0.6);
volumeProperty->SetSpecular(0.5);
......@@ -437,6 +457,7 @@ int TestGPURayCastLargeColorTransferFunction(int argc, char* argv[])
xf->RotateZ(180.0);
xf->RotateY(25.0);
xf->RotateX(-65.0);
//xf->RotateY(-90.0);
volume->SetUserTransform(xf);
vtkSmartPointer<vtkRenderWindow> renderWindow =
......@@ -460,7 +481,11 @@ int TestGPURayCastLargeColorTransferFunction(int argc, char* argv[])
iren->SetRenderWindow(renderWindow);
renderWindow->Render();// make sure we have an OpenGL context.
#ifdef GPU_MAPPER
int valid = volumeMapper->IsRenderSupported(renderWindow, volumeProperty);
#else
int valid = 1;
#endif
int retVal;
if (valid)
......
......@@ -248,28 +248,9 @@ vtkStandardNewMacro(vtkOpenGLGPUVolumeRayCastMapper);
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
class vtkTextureTable
class vtkTextureTable : public vtkObject
{
public:
vtkTextureTable()
{
this->TextureId=0;
this->Table=0;
this->Loaded=false;
this->LastLinearInterpolation=false;
this->LastRange[0] = this->LastRange[1] = 0.0;
}
virtual ~vtkTextureTable()
{
if(this->TextureId!=0)
{
glDeleteTextures(1,&this->TextureId);
vtkOpenGLStaticCheckErrorMacro("failed at glDeleteTextures");
this->TextureId=0;
}
delete[] this->Table;
this->Table = 0;
}
bool IsLoaded()const
{
return this->Loaded;
......@@ -282,11 +263,17 @@ public:
}
int ComputeTableSize(vtkPiecewiseFunction* function)
{
return this->ComputeTableSize(function->GetSize());
int const idealW = function->EstimateMinNumberOfSamples(this->LastRange[0],
this->LastRange[1]);
return this->ComputeTableSize(idealW);
}
int ComputeTableSize(vtkColorTransferFunction* function)
{
return this->ComputeTableSize(function->GetSize());
int const idealW = function->EstimateMinNumberOfSamples(this->LastRange[0],
this->LastRange[1]);
return this->ComputeTableSize(idealW);
}
int ComputeTableSize(vtkLookupTable* function)
{
......@@ -294,6 +281,26 @@ public:
}
protected:
vtkTextureTable()
{
this->TextureId=0;
this->Table=0;
this->Loaded=false;
this->LastLinearInterpolation=false;
this->LastRange[0] = this->LastRange[1] = 0.0;
}
virtual ~vtkTextureTable()
{
if(this->TextureId!=0)
{
glDeleteTextures(1,&this->TextureId);
vtkOpenGLStaticCheckErrorMacro("failed at glDeleteTextures");
this->TextureId=0;
}
delete[] this->Table;
this->Table = 0;
}
GLuint TextureId;
vtkTimeStamp BuildTime;
float *Table;
......@@ -302,14 +309,38 @@ protected:
double LastRange[2];
private:
vtkTextureTable(const vtkTextureTable&);
vtkTextureTable& operator=(const vtkTextureTable&);
vtkTextureTable(const vtkTextureTable&); VTK_DELETE_FUNCTION
vtkTextureTable& operator=(const vtkTextureTable&); VTK_DELETE_FUNCTION
// Description:
// Queries the GL_MAX_TEXTURE_SIZE and returns either the requested idealWidth
// or the maximum supported.
// Warning: This method assumes there is an active GL context.
int ComputeTableSize(int idealWidth)
{
idealWidth = vtkMath::NearestPowerOfTwo(idealWidth);
GLint maxWidth = -1;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxWidth);
if (maxWidth < 0)
{
vtkErrorMacro("Failed to query max texture size! using default "
<< vtkOpenGLGPUVolumeRayCastMapperOpacityTableSize);
return vtkOpenGLGPUVolumeRayCastMapperOpacityTableSize;
}
int ComputeTableSize(int functionSize)
{
functionSize = std::max(functionSize,
vtkOpenGLGPUVolumeRayCastMapperOpacityTableSize);
return vtkMath::NearestPowerOfTwo(functionSize);
if (maxWidth >= idealWidth)
{
idealWidth = vtkMath::Max(vtkOpenGLGPUVolumeRayCastMapperOpacityTableSize,
idealWidth);
return idealWidth;
}
vtkWarningMacro("This OpenGL implementation does not support the required "
"texture size of " << idealWidth << ", falling back to maximum allowed, "
<< maxWidth << "." << "This may cause an incorrect color table mapping.");
return maxWidth;
}
};
......@@ -319,15 +350,8 @@ private:
class vtkOpacityTable: public vtkTextureTable
{
public:
vtkOpacityTable()
{
this->LastBlendMode=vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND;
this->LastSampleDistance=1.0;
}
~vtkOpacityTable()
{
}
static vtkOpacityTable* New();
vtkTypeMacro(vtkOpacityTable, vtkTextureTable);
// \pre the active texture is set to TEXTURE2
void Update(vtkPiecewiseFunction *scalarOpacity,
......@@ -439,13 +463,22 @@ public:
vtkOpenGLStaticCheckErrorMacro("failed after Update");
}
protected:
vtkOpacityTable()
{
this->LastBlendMode=vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND;
this->LastSampleDistance=1.0;
}
~vtkOpacityTable() {};
int LastBlendMode;
double LastSampleDistance;
private:
vtkOpacityTable(const vtkOpacityTable&);
vtkOpacityTable& operator=(const vtkOpacityTable&);
vtkOpacityTable(const vtkOpacityTable&); VTK_DELETE_FUNCTION
vtkOpacityTable& operator=(const vtkOpacityTable&); VTK_DELETE_FUNCTION
};
vtkStandardNewMacro(vtkOpacityTable);
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
......@@ -454,43 +487,41 @@ class vtkOpacityTables
public:
vtkOpacityTables(unsigned int numberOfTables)
{
this->Tables = new vtkOpacityTable[numberOfTables];
this->NumberOfTables = numberOfTables;
this->Tables.reserve(static_cast<size_t>(numberOfTables));
for (unsigned int i = 0; i < numberOfTables; i++)
{
this->Tables.push_back(vtkOpacityTable::New());
}
}
~vtkOpacityTables()
{
delete [] this->Tables;
size_t const size = this->Tables.size();
for (size_t i = 0; i < size; i++)
{
this->Tables[i]->Delete();
}
}
vtkOpacityTable* GetTable(unsigned int i)
vtkOpacityTable* GetTable(size_t const i)
{
return &this->Tables[i];
return this->Tables[i];
}
unsigned int GetNumberOfTables()
size_t GetNumberOfTables()
{
return this->NumberOfTables;
return this->Tables.size();
}
private:
unsigned int NumberOfTables;
vtkOpacityTable *Tables;
// undefined default constructor.
vtkOpacityTables();
// undefined copy constructor.
vtkOpacityTables(const vtkOpacityTables &other);
// undefined assignment operator.
vtkOpacityTables &operator=(const vtkOpacityTables &other);
vtkOpacityTables(); VTK_DELETE_FUNCTION
vtkOpacityTables(const vtkOpacityTables &other); VTK_DELETE_FUNCTION
vtkOpacityTables &operator=(const vtkOpacityTables &other); VTK_DELETE_FUNCTION
std::vector<vtkOpacityTable*> Tables;
};
//-----------------------------------------------------------------------------
class vtkRGBTable: public vtkTextureTable
{
public:
vtkRGBTable()
{
}
~vtkRGBTable()
{
}
static vtkRGBTable* New();
// \pre the active texture is set properly. (default color,
// mask1, mask2,..)
......@@ -510,6 +541,8 @@ public:
if (range[0] != this->LastRange[0] || range[1] != this->LastRange[1])
{
needUpdate=true;
this->LastRange[0] = range[0];
this->LastRange[1] = range[1];
}
glBindTexture(GL_TEXTURE_1D,this->TextureId);
if(needUpdate)
......@@ -535,8 +568,6 @@ public:
vtkOpenGLStaticCheckErrorMacro("1d RGB texture is too large");
this->Loaded=true;
this->BuildTime.Modified();
this->LastRange[0] = range[0];
this->LastRange[1] = range[1];
}
needUpdate=needUpdate ||
......@@ -559,8 +590,16 @@ public:
vtkOpenGLStaticCheckErrorMacro("failed after Update");
}
protected:
vtkRGBTable() {};
~vtkRGBTable() {};
private:
vtkRGBTable(const vtkRGBTable &other); VTK_DELETE_FUNCTION
vtkRGBTable &operator=(const vtkRGBTable &other); VTK_DELETE_FUNCTION
};
vtkStandardNewMacro(vtkRGBTable);
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
......@@ -2535,14 +2574,23 @@ void vtkOpenGLGPUVolumeRayCastMapper::ReleaseGraphicsResources(
}
}
delete this->RGBTable;
this->RGBTable=0;
if (this->RGBTable)
{
this->RGBTable->Delete();
this->RGBTable = NULL;
}
delete this->Mask1RGBTable;
this->Mask1RGBTable=0;
if (this->Mask1RGBTable)
{
this->Mask1RGBTable->Delete();
this->Mask1RGBTable = NULL;
}
delete this->Mask2RGBTable;
this->Mask2RGBTable=0;
if (this->Mask2RGBTable)
{
this->Mask2RGBTable->Delete();
this->Mask2RGBTable = NULL;
}
delete this->OpacityTables;
this->OpacityTables=0;
......@@ -4067,20 +4115,20 @@ void vtkOpenGLGPUVolumeRayCastMapper::PreRender(vtkRenderer *ren,
this->TableRange[1]=scalarRange[1];
if(this->RGBTable==0)
if(this->RGBTable == NULL)
{
this->RGBTable=new vtkRGBTable;
this->RGBTable = vtkRGBTable::New();
}
if (this->MaskInput != 0 && this->MaskType == LabelMapMaskType)
if (this->MaskInput != NULL && this->MaskType == LabelMapMaskType)
{
if(this->Mask1RGBTable==0)
if(this->Mask1RGBTable == NULL)
{
this->Mask1RGBTable=new vtkRGBTable;
this->Mask1RGBTable = vtkRGBTable::New();
}
if(this->Mask2RGBTable==0)
if(this->Mask2RGBTable == NULL)
{
this->Mask2RGBTable=new vtkRGBTable;
this->Mask2RGBTable = vtkRGBTable::New();
}
}
......
......@@ -560,13 +560,13 @@ void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::Initialize(
if (this->Parent->MaskInput != 0 &&
this->Parent->MaskType == LabelMapMaskType)
{
if(this->Mask1RGBTable == 0)
if(this->Mask1RGBTable == NULL)
{
this->Mask1RGBTable = new vtkOpenGLVolumeRGBTable();
this->Mask1RGBTable = vtkOpenGLVolumeRGBTable::New();
}
if(this->Mask2RGBTable == 0)
if(this->Mask2RGBTable == NULL)
{
this->Mask2RGBTable = new vtkOpenGLVolumeRGBTable();
this->Mask2RGBTable = vtkOpenGLVolumeRGBTable::New();
}
}
......@@ -985,21 +985,27 @@ bool vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::LoadMask(vtkRenderer* ren,
void vtkOpenGLGPUVolumeRayCastMapper::vtkInternal::DeleteTransferFunctions()
{
delete this->RGBTables;
this->RGBTables = 0;
this->RGBTables = NULL;
delete this->Mask1RGBTable;
this->Mask1RGBTable=0;
if (this->Mask1RGBTable)
{
this->Mask1RGBTable->Delete();
this->Mask1RGBTable = NULL;
}
delete this->Mask2RGBTable;
this->Mask2RGBTable=0;
if (this->Mask2RGBTable)
{
this->Mask2RGBTable->Delete();
this->Mask2RGBTable = NULL;
}
delete this->OpacityTables;
this->OpacityTables = 0;
this->OpacityTables = NULL;