/*========================================================================= 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 #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, ¶ms ); // 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); }