You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/3rd/VTK7.1/source/VTK-7.1.1/Rendering/OpenGL/vtkOpenGLTexture.cxx

642 lines
18 KiB
C++

/*=========================================================================
Program: Visualization Toolkit
Module: vtkOpenGLTexture.cxx
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 "vtkOpenGLTexture.h"
#include "vtkHomogeneousTransform.h"
#include "vtkImageData.h"
#include "vtkLookupTable.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkOpenGLRenderer.h"
#include "vtkPointData.h"
#include "vtkRenderWindow.h"
#include "vtkOpenGLExtensionManager.h"
#include "vtkOpenGLRenderWindow.h"
#include "vtkTransform.h"
#include "vtkPixelBufferObject.h"
#include "vtkOpenGL.h"
#include "vtkOpenGLError.h"
#include "vtkgl.h" // vtkgl namespace
#include <cmath>
// ----------------------------------------------------------------------------
vtkStandardNewMacro(vtkOpenGLTexture);
// ----------------------------------------------------------------------------
vtkOpenGLTexture::vtkOpenGLTexture()
{
this->Index = 0;
this->RenderWindow = 0;
this->CheckedHardwareSupport = false;
this->SupportsNonPowerOfTwoTextures = false;
this->SupportsPBO = false;
this->PBO = 0;
}
// ----------------------------------------------------------------------------
vtkOpenGLTexture::~vtkOpenGLTexture()
{
if (this->RenderWindow)
{
this->ReleaseGraphicsResources(this->RenderWindow);
this->RenderWindow = 0;
}
if (this->PBO)
{
vtkErrorMacro(<< "PBO should have been deleted in ReleaseGraphicsResources()");
}
}
// ----------------------------------------------------------------------------
void vtkOpenGLTexture::Initialize(vtkRenderer* vtkNotUsed(ren))
{
}
// ----------------------------------------------------------------------------
// Release the graphics resources used by this texture.
void vtkOpenGLTexture::ReleaseGraphicsResources(vtkWindow *win)
{
if (this->Index && win)
{
vtkRenderWindow *renWin = dynamic_cast<vtkRenderWindow *>(win);
renWin->MakeCurrent();
vtkOpenGLClearErrorMacro();
#ifdef GL_VERSION_1_1
// free any textures
if (glIsTexture(static_cast<GLuint>(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
vtkOpenGLCheckErrorMacro("failed after ReleaseGraphicsResources");
}
this->Index = 0;
this->RenderWindow = NULL;
this->CheckedHardwareSupport = false;
this->SupportsNonPowerOfTwoTextures = false;
this->SupportsPBO = false;
if(this->PBO)
{
this->PBO->Delete();
this->PBO = 0;
}
this->Modified();
}
// ----------------------------------------------------------------------------
// Implement base class method.
void vtkOpenGLTexture::Load(vtkRenderer *ren)
{
GLenum format = GL_LUMINANCE;
vtkImageData *input = this->GetInput();
this->Initialize(ren);
// Need to reload the texture.
// There used to be a check on the render window's mtime, but
// this is too broad of a check (e.g. it would cause all textures
// to load when only the desired update rate changed).
// If a better check is required, check something more specific,
// like the graphics context.
vtkOpenGLRenderWindow* renWin =
static_cast<vtkOpenGLRenderWindow*>(ren->GetRenderWindow());
vtkOpenGLClearErrorMacro();
if (this->BlendingMode != VTK_TEXTURE_BLENDING_MODE_NONE
&& vtkgl::ActiveTexture)
{
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, vtkgl::COMBINE);
switch (this->BlendingMode)
{
case VTK_TEXTURE_BLENDING_MODE_REPLACE:
{
glTexEnvf(GL_TEXTURE_ENV, vtkgl::COMBINE_RGB, GL_REPLACE);
glTexEnvf(GL_TEXTURE_ENV, vtkgl::COMBINE_ALPHA, GL_REPLACE);
break;
}
case VTK_TEXTURE_BLENDING_MODE_MODULATE:
{
glTexEnvf(GL_TEXTURE_ENV, vtkgl::COMBINE_RGB, GL_MODULATE);
glTexEnvf(GL_TEXTURE_ENV, vtkgl::COMBINE_ALPHA, GL_MODULATE);
break;
}
case VTK_TEXTURE_BLENDING_MODE_ADD:
{
glTexEnvf(GL_TEXTURE_ENV, vtkgl::COMBINE_RGB, GL_ADD);
glTexEnvf(GL_TEXTURE_ENV, vtkgl::COMBINE_ALPHA, GL_ADD);
break;
}
case VTK_TEXTURE_BLENDING_MODE_ADD_SIGNED:
{
glTexEnvf(GL_TEXTURE_ENV, vtkgl::COMBINE_RGB, vtkgl::ADD_SIGNED);
glTexEnvf(GL_TEXTURE_ENV, vtkgl::COMBINE_ALPHA, vtkgl::ADD_SIGNED);
break;
}
case VTK_TEXTURE_BLENDING_MODE_INTERPOLATE:
{
glTexEnvf(GL_TEXTURE_ENV, vtkgl::COMBINE_RGB, vtkgl::INTERPOLATE);
glTexEnvf(GL_TEXTURE_ENV, vtkgl::COMBINE_ALPHA, vtkgl::INTERPOLATE);
break;
}
case VTK_TEXTURE_BLENDING_MODE_SUBTRACT:
{
glTexEnvf(GL_TEXTURE_ENV, vtkgl::COMBINE_RGB, vtkgl::SUBTRACT);
glTexEnvf(GL_TEXTURE_ENV, vtkgl::COMBINE_ALPHA, vtkgl::SUBTRACT);
break;
}
default:
{
glTexEnvf(GL_TEXTURE_ENV, vtkgl::COMBINE_RGB, GL_ADD);
glTexEnvf(GL_TEXTURE_ENV, vtkgl::COMBINE_ALPHA, GL_ADD);
}
}
vtkOpenGLCheckErrorMacro("failed at glTexEnvf");
}
if (this->GetMTime() > this->LoadTime.GetMTime() ||
input->GetMTime() > this->LoadTime.GetMTime() ||
(this->GetLookupTable() && this->GetLookupTable()->GetMTime () >
this->LoadTime.GetMTime()) ||
renWin != this->RenderWindow.GetPointer() ||
renWin->GetContextCreationTime() > this->LoadTime)
{
int size[3];
unsigned char *dataPtr;
unsigned char *resultData = 0;
int xsize, ysize;
GLuint tempIndex = 0;
// Get the scalars the user choose to color with.
vtkDataArray* scalars = this->GetInputArrayToProcess(0, input);
// make sure scalars are non null
if (!scalars)
{
vtkErrorMacro(<< "No scalar values found for texture input!");
return;
}
// free any old display lists (from the old context)
// make the new context current before we mess with opengl
if (this->RenderWindow)
{
this->ReleaseGraphicsResources(this->RenderWindow);
}
this->RenderWindow = renWin;
this->RenderWindow->MakeCurrent();
// get some info
input->GetDimensions(size);
if (input->GetNumberOfCells() == scalars->GetNumberOfTuples())
{
// we are using cell scalars. Adjust image size for cells.
for (int kk = 0; kk < 3; kk++)
{
if (size[kk]>1)
{
size[kk]--;
}
}
}
int bytesPerPixel = scalars->GetNumberOfComponents();
// make sure using unsigned char data of color scalars type
if (this->MapColorScalarsThroughLookupTable ||
scalars->GetDataType() != VTK_UNSIGNED_CHAR )
{
dataPtr = this->MapScalarsToColors (scalars);
bytesPerPixel = 4;
}
else
{
dataPtr = static_cast<vtkUnsignedCharArray *>(scalars)->GetPointer(0);
}
// we only support 2d texture maps right now
// so one of the three sizes must be 1, but it
// could be any of them, so lets find it
if (size[0] == 1)
{
xsize = size[1]; ysize = size[2];
}
else
{
xsize = size[0];
if (size[1] == 1)
{
ysize = size[2];
}
else
{
ysize = size[1];
if (size[2] != 1)
{
vtkErrorMacro(<< "3D texture maps currently are not supported!");
return;
}
}
}
if (!this->CheckedHardwareSupport)
{
vtkOpenGLExtensionManager *m = renWin->GetExtensionManager();
this->CheckedHardwareSupport = true;
this->SupportsNonPowerOfTwoTextures =
m->ExtensionSupported("GL_VERSION_2_0")
|| m->ExtensionSupported("GL_ARB_texture_non_power_of_two");
this->SupportsPBO=vtkPixelBufferObject::IsSupported(renWin);
}
// -- decide whether the texture needs to be resampled --
GLint maxDimGL;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxDimGL);
vtkOpenGLCheckErrorMacro("failed at glGetIntegerv");
// if larger than permitted by the graphics library then must resample
bool resampleNeeded=xsize > maxDimGL || ysize > maxDimGL;
if(resampleNeeded)
{
vtkDebugMacro( "Texture too big for gl, maximum is " << maxDimGL);
}
if(!resampleNeeded && !this->SupportsNonPowerOfTwoTextures)
{
// if not a power of two then resampling is required
resampleNeeded = !(vtkMath::IsPowerOfTwo(xsize) &&
vtkMath::IsPowerOfTwo(ysize));
}
if (resampleNeeded)
{
vtkDebugMacro(<< "Resampling texture to power of two for OpenGL");
resultData = this->ResampleToPowerOfTwo(xsize, ysize, dataPtr,
bytesPerPixel);
}
if (!resultData)
{
resultData = dataPtr;
}
// define a display list for this texture
// get a unique display list id
#ifdef GL_VERSION_1_1
glGenTextures(1, &tempIndex);
vtkOpenGLCheckErrorMacro("failed at glGenTextures");
this->Index = static_cast<long>(tempIndex);
glBindTexture(GL_TEXTURE_2D, this->Index);
vtkOpenGLCheckErrorMacro("failed at glBindTexture");
#else
this->Index = glGenLists(1);
glDeleteLists(static_cast<GLuint>(this->Index), static_cast<GLsizei>(0));
vtkOpenGLCheckErrorMacro("failed at glDeleteLists");
glNewList(static_cast<GLuint>(this->Index), GL_COMPILE);
vtkOpenGLCheckErrorMacro("failed at glNewList");
#endif
//seg fault protection for those wackos that don't use an
//opengl render window
if (this->RenderWindow->IsA("vtkOpenGLRenderWindow"))
{
static_cast<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);
}
if (this->Repeat)
{
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
}
else
{
vtkOpenGLExtensionManager* manager = renWin->GetExtensionManager();
if (this->EdgeClamp &&
(manager->ExtensionSupported("GL_VERSION_1_2") ||
manager->ExtensionSupported("GL_EXT_texture_edge_clamp")))
{
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, vtkgl::CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, vtkgl::CLAMP_TO_EDGE);
}
else
{
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
}
}
vtkOpenGLCheckErrorMacro("failed at glTexParameterf");
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
if (this->Quality == VTK_TEXTURE_QUALITY_32BIT)
{
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;
}
}
else if (this->Quality == VTK_TEXTURE_QUALITY_16BIT)
{
switch (bytesPerPixel)
{
case 1: internalFormat = GL_LUMINANCE4; break;
case 2: internalFormat = GL_LUMINANCE4_ALPHA4; break;
case 3: internalFormat = GL_RGB4; break;
case 4: internalFormat = GL_RGBA4; break;
}
}
#endif
if (this->SupportsPBO)
{
if (!this->PBO)
{
this->PBO = vtkPixelBufferObject::New();
this->PBO->SetContext(renWin);
}
unsigned int dims[2];
dims[0] = static_cast<unsigned int>(xsize);
dims[1] = static_cast<unsigned int>(ysize);
vtkIdType increments[2];
increments[0] = 0;
increments[1] = 0;
this->PBO->Upload2D(VTK_UNSIGNED_CHAR, resultData, dims, bytesPerPixel, increments);
// non-blocking call
this->PBO->Bind(vtkPixelBufferObject::UNPACKED_BUFFER);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat,
xsize, ysize, 0, format,
GL_UNSIGNED_BYTE, 0);
vtkOpenGLCheckErrorMacro("failed at glTexImage2D");
this->PBO->UnBind();
}
else
{
// blocking call
glTexImage2D(GL_TEXTURE_2D, 0 , internalFormat,
xsize, ysize, 0, format, GL_UNSIGNED_BYTE,
static_cast<const GLvoid *>(resultData));
vtkOpenGLCheckErrorMacro("failed at glTexImage2D");
}
#ifndef GL_VERSION_1_1
glEndList ();
vtkOpenGLCheckErrorMacro("failed at glEndList");
#endif
// modify the load time to the current time
this->LoadTime.Modified();
// free memory
if (resultData != dataPtr)
{
delete [] resultData;
resultData = 0;
}
}
// execute the display list that uses creates the texture
#ifdef GL_VERSION_1_1
glBindTexture(GL_TEXTURE_2D, this->Index);
vtkOpenGLCheckErrorMacro("failed at glBindTexture");
#else
glCallList(this->Index);
vtkOpenGLCheckErrorMacro("failed at glCallList");
#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, static_cast<GLclampf>(0.f));
glEnable(GL_ALPHA_TEST);
if (this->PremultipliedAlpha)
{
// save the blend function.
glPushAttrib(GL_COLOR_BUFFER_BIT);
// make the blend function correct for textures premultiplied by alpha.
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
// now bind it
glEnable(GL_TEXTURE_2D);
// clear any texture transform
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
// build transformation
if (this->Transform)
{
double mat[16];
vtkMatrix4x4::Transpose(*this->Transform->GetMatrix()->Element, mat);
// insert texture transformation
glMultMatrixd(mat);
}
glMatrixMode(GL_MODELVIEW);
GLint uUseTexture = -1;
GLint uTexture = -1;
vtkOpenGLRenderer *oRenderer=static_cast<vtkOpenGLRenderer *>(ren);
if(oRenderer->GetDepthPeelingHigherLayer())
{
uUseTexture = oRenderer->GetUseTextureUniformVariable();
uTexture = oRenderer->GetTextureUniformVariable();
vtkgl::Uniform1i(uUseTexture, 1);
vtkgl::Uniform1i(uTexture, 0); // active texture 0
}
vtkOpenGLCheckErrorMacro("failed after Load");
}
// ----------------------------------------------------------------------------
void vtkOpenGLTexture::PostRender(vtkRenderer *ren)
{
(void)ren;
if (this->GetInput() && this->PremultipliedAlpha)
{
// restore the blend function
glPopAttrib();
vtkOpenGLCheckErrorMacro("failed after PostRender");
}
}
// ----------------------------------------------------------------------------
static int FindPowerOfTwo(int i)
{
int size = vtkMath::NearestPowerOfTwo(i);
// [these lines added by Tim Hutton (implementing Joris Vanden Wyngaerd's
// suggestions)]
// limit the size of the texture to the maximum allowed by OpenGL
// (slightly more graceful than texture failing but not ideal)
GLint maxDimGL;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxDimGL);
if (size < 0 || size > maxDimGL)
{
size = maxDimGL ;
}
// end of Tim's additions
return size;
}
// ----------------------------------------------------------------------------
// Creates resampled unsigned char texture map that is a power of two in both
// x and y.
unsigned char *vtkOpenGLTexture::ResampleToPowerOfTwo(int &xs,
int &ys,
unsigned char *dptr,
int bpp)
{
unsigned char *tptr, *p, *p1, *p2, *p3, *p4;
int jOffset, iIdx, jIdx;
double pcoords[3], rm, sm, w0, w1, w2, w3;
int yInIncr = xs;
int xInIncr = 1;
int xsize = FindPowerOfTwo(xs);
int ysize = FindPowerOfTwo(ys);
if (this->RestrictPowerOf2ImageSmaller)
{
if (xsize > xs)
{
xsize /= 2;
}
if (ysize > ys)
{
ysize /= 2;
}
}
double hx = xsize > 1 ? (xs - 1.0) / (xsize - 1.0) : 0;
double hy = ysize > 1 ? (ys - 1.0) / (ysize - 1.0) : 0;
tptr = p = new unsigned char[xsize*ysize*bpp];
// Resample from the previous image. Compute parametric coordinates and
// interpolate
for (int j = 0; j < ysize; j++)
{
pcoords[1] = j*hy;
jIdx = static_cast<int>(pcoords[1]);
if (jIdx >= (ys-1)) //make sure to interpolate correctly at edge
{
if (ys == 1)
{
jIdx = 0;
yInIncr = 0;
}
else
{
jIdx = ys - 2;
}
pcoords[1] = 1.0;
}
else
{
pcoords[1] = pcoords[1] - jIdx;
}
jOffset = jIdx*xs;
sm = 1.0 - pcoords[1];
for (int i = 0; i < xsize; i++)
{
pcoords[0] = i*hx;
iIdx = static_cast<int>(pcoords[0]);
if (iIdx >= (xs-1))
{
if (xs == 1)
{
iIdx = 0;
xInIncr = 0;
}
else
{
iIdx = xs - 2;
}
pcoords[0] = 1.0;
}
else
{
pcoords[0] = pcoords[0] - iIdx;
}
rm = 1.0 - pcoords[0];
// Get pointers to 4 surrounding pixels
p1 = dptr + bpp*(iIdx + jOffset);
p2 = p1 + bpp*xInIncr;
p3 = p1 + bpp*yInIncr;
p4 = p3 + bpp*xInIncr;
// Compute interpolation weights interpolate components
w0 = rm*sm;
w1 = pcoords[0]*sm;
w2 = rm*pcoords[1];
w3 = pcoords[0]*pcoords[1];
for (int k = 0; k < bpp; k++)
{
*p++ = static_cast<unsigned char>(p1[k]*w0 + p2[k]*w1 + p3[k]*w2
+ p4[k]*w3);
}
}
}
xs = xsize;
ys = ysize;
return tptr;
}
// ----------------------------------------------------------------------------
void vtkOpenGLTexture::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os,indent);
os << indent << "Index: " << this->Index << endl;
}