Cloned library of VTK-5.0.0 with extra build files for internal package management.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

675 lines
18 KiB

2 years ago
/*=========================================================================
Program: Visualization Toolkit
Module: $RCSfile: vtkOpenGLImageActor.cxx,v $
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.
=========================================================================*/
#include "vtkOpenGLImageActor.h"
#include "vtkMapper.h"
#include "vtkObjectFactory.h"
#include "vtkOpenGLRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkImageData.h"
#include "vtkMatrix4x4.h"
#include "vtkOpenGLRenderWindow.h"
#include <math.h>
#include "vtkOpenGL.h"
#ifndef GL_MAX_TEXTURE_SIZE
#define GL_MAX_TEXTURE_SIZE 1024
#endif
#ifndef VTK_IMPLEMENT_MESA_CXX
vtkCxxRevisionMacro(vtkOpenGLImageActor, "$Revision: 1.31.6.1 $");
vtkStandardNewMacro(vtkOpenGLImageActor);
#endif
// Initializes an instance, generates a unique index.
vtkOpenGLImageActor::vtkOpenGLImageActor()
{
this->Index = 0;
this->RenderWindow = 0;
this->TextureSize[0] = 0;
this->TextureSize[1] = 0;
}
vtkOpenGLImageActor::~vtkOpenGLImageActor()
{
this->RenderWindow = NULL;
}
// Release the graphics resources used by this texture.
void vtkOpenGLImageActor::ReleaseGraphicsResources(vtkWindow *renWin)
{
if (this->Index && renWin)
{
((vtkRenderWindow *) renWin)->MakeCurrent();
#ifdef GL_VERSION_1_1
// free any textures
if (glIsTexture(this->Index))
{
GLuint tempIndex;
tempIndex = this->Index;
// NOTE: Sun's OpenGL seems to require disabling of texture before delete
glDisable(GL_TEXTURE_2D);
glDeleteTextures(1, &tempIndex);
}
#else
if (glIsList(this->Index))
{
glDeleteLists(this->Index,1);
}
#endif
this->TextureSize[0] = 0;
this->TextureSize[1] = 0;
}
this->Index = 0;
this->RenderWindow = NULL;
this->Modified();
}
unsigned char *vtkOpenGLImageActor::MakeDataSuitable(int &xsize, int &ysize,
int &release,
int &reuseTexture)
{
int contiguous = 0;
unsigned short xs,ys;
int powOfTwo = 0;
int numComp = this->Input->GetNumberOfScalarComponents();
int xdim, ydim;
reuseTexture = 0;
// it must be a power of two and contiguous
// find the two used dimensions
// this assumes a 2D image, no lines here folk
if (this->DisplayExtent[0] != this->DisplayExtent[1])
{
xdim = 0;
if (this->DisplayExtent[2] != this->DisplayExtent[3])
{
ydim = 1;
}
else
{
ydim = 2;
}
}
else
{
xdim = 1;
ydim = 2;
}
double *spacing = this->Input->GetSpacing();
double *origin = this->Input->GetOrigin();
// compute the world coordinates
this->Coords[0] = this->DisplayExtent[0]*spacing[0] + origin[0];
this->Coords[1] = this->DisplayExtent[2]*spacing[1] + origin[1];
this->Coords[2] = this->DisplayExtent[4]*spacing[2] + origin[2];
this->Coords[3] = this->DisplayExtent[1]*spacing[0] + origin[0];
this->Coords[4] =
this->DisplayExtent[2 + (xdim == 1)]*spacing[1] + origin[1];
this->Coords[5] = this->DisplayExtent[4]*spacing[2] + origin[2];
this->Coords[6] = this->DisplayExtent[1]*spacing[0] + origin[0];
this->Coords[7] = this->DisplayExtent[3]*spacing[1] + origin[1];
this->Coords[8] = this->DisplayExtent[5]*spacing[2] + origin[2];
this->Coords[9] = this->DisplayExtent[0]*spacing[0] + origin[0];
this->Coords[10] =
this->DisplayExtent[2 + (ydim == 1)]*spacing[1] + origin[1];
this->Coords[11] = this->DisplayExtent[5]*spacing[2] + origin[2];
// now contiguous would require that xdim = 0 and ydim = 1
// OR xextent = 1 pixel and xdim = 1 and ydim = 2
// OR xdim = 0 and ydim = 2 and yextent = i pixel. In addition
// the corresponding x display extents must match the
// extent of the data
int *ext = this->Input->GetExtent();
if ( ( xdim == 0 && ydim == 1 &&
this->DisplayExtent[0] == ext[0] &&
this->DisplayExtent[1] == ext[1] )||
( ext[0] == ext[1] && xdim == 1 &&
this->DisplayExtent[2] == ext[2] &&
this->DisplayExtent[3] == ext[3] ) ||
( ext[2] == ext[3] && xdim == 0 && ydim == 2 &&
this->DisplayExtent[0] == ext[0] &&
this->DisplayExtent[1] == ext[1] ) )
{
contiguous = 1;
}
// if contiguous is it a pow of 2
if (contiguous)
{
xsize = ext[xdim*2+1] - ext[xdim*2] + 1;
// xsize and ysize must be a power of 2 in OpenGL
xs = (unsigned short)xsize;
while (!(xs & 0x01))
{
xs = xs >> 1;
}
if (xs == 1)
{
powOfTwo = 1;
}
}
if (contiguous && powOfTwo)
{
// can we make y a power of two also ?
ysize = this->DisplayExtent[ydim*2+1] - this->DisplayExtent[ydim*2] + 1;
ys = (unsigned short)ysize;
while (!(ys & 0x01))
{
ys = ys >> 1;
}
// yes it is a power of two already
if (ys == 1)
{
release = 0;
this->TCoords[0] = (this->DisplayExtent[xdim*2] - ext[xdim*2] + 0.5)/xsize;
this->TCoords[1] = 0.5/ysize;
this->TCoords[2] = (this->DisplayExtent[xdim*2+1] - ext[xdim*2] + 0.5)/xsize;
this->TCoords[3] = this->TCoords[1];
this->TCoords[4] = this->TCoords[2];
this->TCoords[5] = 1.0 - 0.5/ysize;
this->TCoords[6] = this->TCoords[0];
this->TCoords[7] = this->TCoords[5];
#ifdef GL_VERSION_1_1
// if texture size hasn't changed, reuse old texture
if (xsize == this->TextureSize[0] && ysize == this->TextureSize[1])
{
reuseTexture = 1;
}
#endif
return (unsigned char *)
this->Input->GetScalarPointerForExtent(this->DisplayExtent);
}
}
// if we made it here then we must copy the data and possibly pad
// it as well
// find the target size
xsize = 1;
while (xsize <
this->DisplayExtent[xdim*2+1] - this->DisplayExtent[xdim*2] + 1)
{
xsize *= 2;
}
ysize = 1;
while (ysize <
this->DisplayExtent[ydim*2+1] - this->DisplayExtent[ydim*2] + 1)
{
ysize *= 2;
}
// compute the tcoords
this->TCoords[0] = 0.5/xsize;
this->TCoords[1] = 0.5/ysize;
this->TCoords[2] = (this->DisplayExtent[xdim*2+1] - this->DisplayExtent[xdim*2] + 0.5)/xsize;
this->TCoords[3] = this->TCoords[1];
this->TCoords[4] = this->TCoords[2];
this->TCoords[5] = (this->DisplayExtent[ydim*2+1] - this->DisplayExtent[ydim*2] + 0.5)/ysize;
this->TCoords[6] = this->TCoords[0];
this->TCoords[7] = this->TCoords[5];
#ifdef GL_VERSION_1_1
// reuse texture if texture size has not changed
if (xsize == this->TextureSize[0] && ysize == this->TextureSize[1])
{
reuseTexture = 1;
xsize = this->DisplayExtent[xdim*2+1] - this->DisplayExtent[xdim*2] + 1;
ysize = this->DisplayExtent[ydim*2+1] - this->DisplayExtent[ydim*2] + 1;
}
#endif
// if contiguous and texture size hasn't changed, don't copy or pad
if (reuseTexture && contiguous)
{
release = 0;
return (unsigned char *)
this->Input->GetScalarPointerForExtent(this->DisplayExtent);
}
// allocate the memory
unsigned char *res = new unsigned char [ysize*xsize*numComp];
release = 1;
// copy the input data to the memory
vtkIdType inIncX, inIncY, inIncZ;
int idxZ, idxY, idxR;
unsigned char *inPtr = (unsigned char *)
this->Input->GetScalarPointerForExtent(this->DisplayExtent);
this->Input->GetContinuousIncrements(this->DisplayExtent,
inIncX, inIncY, inIncZ);
int rowLength = numComp*(this->DisplayExtent[1] -this->DisplayExtent[0] +1);
unsigned char *outPtr = res;
vtkIdType outIncY, outIncZ;
if (ydim == 2)
{
if (xdim == 0)
{
outIncZ = numComp *
(xsize - (this->DisplayExtent[1] - this->DisplayExtent[0] + 1));
}
else
{
outIncZ = numComp *
(xsize - (this->DisplayExtent[3] - this->DisplayExtent[2] + 1));
}
outIncY = 0;
}
else
{
outIncY = numComp *
(xsize - (this->DisplayExtent[1] - this->DisplayExtent[0] + 1));
outIncZ = 0;
}
for (idxZ = this->DisplayExtent[4]; idxZ <= this->DisplayExtent[5]; idxZ++)
{
for (idxY = this->DisplayExtent[2]; idxY <= this->DisplayExtent[3]; idxY++)
{
for (idxR = 0; idxR < rowLength; idxR++)
{
// Pixel operation
*outPtr = *inPtr;
outPtr++;
inPtr++;
}
outPtr += outIncY;
inPtr += inIncY;
}
outPtr += outIncZ;
inPtr += inIncZ;
}
return res;
}
// Implement base class method.
void vtkOpenGLImageActor::Load(vtkRenderer *ren)
{
GLenum format = GL_LUMINANCE;
// need to reload the texture
if (this->GetMTime() > this->LoadTime.GetMTime() ||
this->Input->GetMTime() > this->LoadTime.GetMTime() ||
ren->GetRenderWindow() != this->RenderWindow)
{
int xsize, ysize;
int release, reuseTexture;
unsigned char *data = this->MakeDataSuitable(xsize,ysize,
release, reuseTexture);
int bytesPerPixel = this->Input->GetNumberOfScalarComponents();
GLuint tempIndex=0;
if (reuseTexture)
{
#ifdef GL_VERSION_1_1
glBindTexture(GL_TEXTURE_2D, this->Index);
#endif
}
else
{
// free any old display lists
this->ReleaseGraphicsResources(ren->GetRenderWindow());
this->RenderWindow = ren->GetRenderWindow();
// define a display list for this texture
// get a unique display list id
#ifdef GL_VERSION_1_1
glGenTextures(1, &tempIndex);
this->Index = (long) tempIndex;
glBindTexture(GL_TEXTURE_2D, this->Index);
#else
this->Index = glGenLists(1);
glDeleteLists ((GLuint) this->Index, (GLsizei) 0);
glNewList ((GLuint) this->Index, GL_COMPILE);
#endif
((vtkOpenGLRenderWindow *)(ren->GetRenderWindow()))->RegisterTextureResource( this->Index );
}
if (this->Interpolate)
{
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR);
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_LINEAR );
}
else
{
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
}
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
int internalFormat = bytesPerPixel;
switch (bytesPerPixel)
{
case 1: format = GL_LUMINANCE; break;
case 2: format = GL_LUMINANCE_ALPHA; break;
case 3: format = GL_RGB; break;
case 4: format = GL_RGBA; break;
}
// if we are using OpenGL 1.1, you can force 32 or16 bit textures
#ifdef GL_VERSION_1_1
switch (bytesPerPixel)
{
case 1: internalFormat = GL_LUMINANCE8; break;
case 2: internalFormat = GL_LUMINANCE8_ALPHA8; break;
case 3: internalFormat = GL_RGB8; break;
case 4: internalFormat = GL_RGBA8; break;
}
#endif
if (reuseTexture)
{
#ifdef GL_VERSION_1_1
glPixelStorei( GL_UNPACK_ALIGNMENT, 1);
glPixelStorei( GL_UNPACK_ROW_LENGTH, 0);
glTexSubImage2D( GL_TEXTURE_2D, 0,
0, 0, xsize, ysize, format,
GL_UNSIGNED_BYTE, (const GLvoid *)data );
#endif
}
else
{
glTexImage2D( GL_TEXTURE_2D, 0, internalFormat,
xsize, ysize, 0, format,
GL_UNSIGNED_BYTE, (const GLvoid *)data );
this->TextureSize[0] = xsize;
this->TextureSize[1] = ysize;
}
#ifndef GL_VERSION_1_1
glEndList ();
#endif
// modify the load time to the current time
this->LoadTime.Modified();
if (release)
{
delete [] data;
}
}
// execute the display list that uses creates the texture
#ifdef GL_VERSION_1_1
glBindTexture(GL_TEXTURE_2D, this->Index);
#else
glCallList ((GLuint) this->Index);
#endif
// don't accept fragments if they have zero opacity. this will stop the
// zbuffer from be blocked by totally transparent texture fragments.
glAlphaFunc (GL_GREATER, (GLclampf) 0);
glEnable (GL_ALPHA_TEST);
// now bind it
glEnable(GL_TEXTURE_2D);
// draw the quad
if ( vtkMapper::GetResolveCoincidentTopology() )
{
if ( vtkMapper::GetResolveCoincidentTopology() ==
VTK_RESOLVE_SHIFT_ZBUFFER )
{
}
else
{
#ifdef GL_VERSION_1_1
double f, u;
glEnable(GL_POLYGON_OFFSET_FILL);
vtkMapper::GetResolveCoincidentTopologyPolygonOffsetParameters(f,u);
glPolygonOffset(f,u);
#endif
}
}
glDisable(GL_COLOR_MATERIAL);
glDisable (GL_CULL_FACE);
glDisable( GL_LIGHTING );
glColor4f( 1.0, 1.0, 1.0, this->Opacity );
glBegin( GL_QUADS );
for (int i = 0; i < 4; i++ )
{
glTexCoord2dv( this->TCoords + i*2 );
glVertex3dv(this->Coords + i*3);
}
glEnd();
// Turn lighting back on
glEnable( GL_LIGHTING );
}
// Determine if a given texture size is supported by the
// video card
int vtkOpenGLImageActor::TextureSizeOK( int size[2] )
{
// In version 1.1 or later, use proxy texture to figure out if
// the texture is too big
#ifdef GL_VERSION_1_1
// Do a quick test to see if we are too large
if ( size[0] > GL_MAX_TEXTURE_SIZE ||
size[1] > GL_MAX_TEXTURE_SIZE )
{
return 0;
}
// Test the texture to see if it fits in memory
glTexImage2D( GL_PROXY_TEXTURE_2D, 0, GL_RGBA8,
size[0], size[1],
0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)NULL );
GLint params = 0;
glGetTexLevelParameteriv ( GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH,
&params );
// if it does, we will render it later. define the texture here
if ( params == 0 )
{
// Can't use that texture
return 0;
}
else
{
return 1;
}
#else
// Otherwise we are version 1.0 and we'll just assume the card can do 1024x1024
if ( size[0] > 1024 || size[1] > 1024 )
{
return 0;
}
else
{
return 1;
}
#endif
}
// Actual actor render method.
// Recursive to handle larger textures than can be rendered by
// a given video card. Assumes all video cards can render a texture
// of 256x256 so will fail if card reports that it cannot render
// a texture of this size rather than recursing further
void vtkOpenGLImageActor::Render(vtkRenderer *ren)
{
// Save the current display extent since we might change it
int savedDisplayExtent[6];
this->GetDisplayExtent( savedDisplayExtent );
// What is the power of two texture big enough to fit the display extent?
// This should be 1 in some direction.
int i;
int pow2[3] = {1,1,1};
int baseSize[3];
for ( i = 0; i < 3; i++ )
{
baseSize[i] = this->DisplayExtent[i*2+1] - this->DisplayExtent[i*2] + 1;
while( pow2[i] < baseSize[i] )
{
pow2[i] *= 2;
}
}
// Find the 2d texture in the 3d pow2 structure
int size[2];
if ( pow2[0] == 1 )
{
size[0] = pow2[1];
size[1] = pow2[2];
}
else if ( pow2[1] == 1 )
{
size[0] = pow2[0];
size[1] = pow2[2];
}
else
{
size[0] = pow2[0];
size[1] = pow2[1];
}
// Check if we can fit this texture in memory
if ( this->TextureSizeOK(size) )
{
// We can fit it - render
this->InternalRender( ren );
}
else
{
// If we can't handle a 256x256 or smaller texture,
// just give up and don't render anything. Something
// must be horribly wrong...
if ( size[0] <= 256 && size[1] <= 256 )
{
return;
}
// We can't fit it - subdivide
int newDisplayExtent[6];
int idx;
// Find the biggest side
if ( baseSize[0] >= baseSize[1] && baseSize[0] >= baseSize[2] )
{
idx = 0;
}
else if ( baseSize[1] >= baseSize[0] && baseSize[1] >= baseSize[2] )
{
idx = 1;
}
else
{
idx = 2;
}
// For the other two sides, just copy in the display extent
for ( i = 0; i < 3; i++ )
{
if ( i != idx )
{
newDisplayExtent[i*2] = this->DisplayExtent[i*2];
newDisplayExtent[i*2+1] = this->DisplayExtent[i*2+1];
}
}
// For the biggest side - divide the power of two size in 1/2
// This is the first half
newDisplayExtent[idx*2] = savedDisplayExtent[idx*2];
newDisplayExtent[idx*2+1] =
this->DisplayExtent[idx*2] + baseSize[idx]/2 - 1;
// Set it as the display extent and render
this->SetDisplayExtent( newDisplayExtent );
this->Render(ren);
// This is the remaining side (since the display extent is not
// necessarily a power of 2, this is likely to be less than half
newDisplayExtent[idx*2] =
this->DisplayExtent[idx*2] + baseSize[idx]/2 - 1;
newDisplayExtent[idx*2+1] = savedDisplayExtent[idx*2+1];
// Set it as the display extent and render
this->SetDisplayExtent( newDisplayExtent );
this->Render(ren);
}
// Restore the old display extent
this->SetDisplayExtent( savedDisplayExtent );
}
// This is the non-recursive render that will not check the
// size of the image (it has already been determined to
// be fine)
void vtkOpenGLImageActor::InternalRender(vtkRenderer *ren)
{
// for picking
glDepthMask (GL_TRUE);
// build transformation
if (!this->IsIdentity)
{
double *mat = this->GetMatrix()->Element[0];
double mat2[16];
mat2[0] = mat[0];
mat2[1] = mat[4];
mat2[2] = mat[8];
mat2[3] = mat[12];
mat2[4] = mat[1];
mat2[5] = mat[5];
mat2[6] = mat[9];
mat2[7] = mat[13];
mat2[8] = mat[2];
mat2[9] = mat[6];
mat2[10] = mat[10];
mat2[11] = mat[14];
mat2[12] = mat[3];
mat2[13] = mat[7];
mat2[14] = mat[11];
mat2[15] = mat[15];
// insert model transformation
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glMultMatrixd(mat2);
}
// Render the texture
this->Load(ren);
// pop transformation matrix
if (!this->IsIdentity)
{
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
}
void vtkOpenGLImageActor::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os,indent);
}