Files
EgtGraphics/TextureMgr.cpp
Dario Sassi fb44cd7e95 EgtGraphics 1.8l2 :
- corretto caricamento texture da jpeg con ridimensionamento immediato.
2018-01-08 10:33:31 +00:00

449 lines
15 KiB
C++

//----------------------------------------------------------------------------
// EgalTech 2015-2015
//----------------------------------------------------------------------------
// File : TextureMgr.cpp Data : 27.09.15 Versione : 1.6i8
// Contenuto : Implementazione della classe gestione textures.
//
//
//
// Modifiche : 27.09.15 DS Creazione modulo.
//
//
//----------------------------------------------------------------------------
//--------------------------- Include ----------------------------------------
#include "stdafx.h"
#include "Scene.h"
#include "TextureMgr.h"
#include "/EgtDev/Include/EGnStringUtils.h"
#include "/EgtDev/Include/EgtStringConverter.h"
#include "/EgtDev/Extern/FreeImage/Include/FreeImage.h"
using namespace std ;
//----------------------------------------------------------------------------
TextureMgr::TextureMgr( void)
{
m_pScene = nullptr ;
m_nMaxLinPix = 16384 ;
}
//----------------------------------------------------------------------------
TextureMgr::~TextureMgr( void)
{
Clear() ;
}
//----------------------------------------------------------------------------
bool
TextureMgr::Clear( void)
{
// verifico il contesto OpenGL
if ( m_pScene == nullptr || ! m_pScene->MakeCurrent())
return false ;
// scarico le texture da OpenGL
for ( auto iIter = m_umTextData.begin() ; iIter != m_umTextData.end() ; ++ iIter) {
GLuint nTextId = iIter->second.nTexId ;
glDeleteTextures( 1, &nTextId) ;
}
// pulisco i dati
m_umTextData.clear() ;
return true ;
}
//----------------------------------------------------------------------------
bool
TextureMgr::SetTextureMaxLinPixels( int nMaxLinPix)
{
m_nMaxLinPix = max( nMaxLinPix, 128) ;
return true ;
}
//----------------------------------------------------------------------------
bool
TextureMgr::LoadTexture( const string& sName, const string& sFile,
double dMMxPix, double dDimX, double dDimY, int nRepeat)
{
// verifico il contesto OpenGL
if ( m_pScene == nullptr || ! m_pScene->MakeCurrent())
return false ;
// verifico se immagine già caricata
auto iIter = m_umTextData.find( sName) ;
if ( iIter != m_umTextData.end())
return ( EqualNoCase( iIter->second.sFile, sFile) && iIter->second.nRepeat == nRepeat) ;
// se texture calcolata
if ( sFile.empty())
return LoadCalcTexture( sName, dMMxPix, dDimX, dDimY, nRepeat) ;
// leggo l'immagine dal file insieme con le dimensioni in pixel originali dell'immagine
FIBITMAP* pDib = nullptr ;
int nOriWidth, nOriHeight ;
if ( ! ReadImage( sFile, pDib, nOriWidth, nOriHeight))
return false ;
// se necessario, converto l'immagine a 24 bit per pixel (BGR)
int bitsPerPixel = FreeImage_GetBPP( pDib) ;
if ( bitsPerPixel != 24) {
FIBITMAP* pTmpDib = pDib ;
pDib = FreeImage_ConvertTo24Bits( pTmpDib) ;
FreeImage_Unload( pTmpDib) ;
}
// se necessario e possibile, la riduco alle dimensioni caricabili da OpenGL corrente
if ( ! TestImageWithOpenGL( pDib))
return false ;
// recupero le dimensioni in pixel dell'immagine
int nWidth = FreeImage_GetWidth( pDib) ;
int nHeight = FreeImage_GetHeight( pDib) ;
// determino il flag di ripetizione
GLint nFlag ;
switch ( nRepeat) {
default : // TXR_CLAMP
nFlag = (( m_pScene->GetOpenGLver() < 13) ? GL_CLAMP : GL_CLAMP_TO_BORDER) ;
break ;
case TXR_REPEAT :
nFlag = GL_REPEAT ;
break ;
case TXR_MIRROR :
nFlag = (( m_pScene->GetOpenGLver() < 14) ? GL_REPEAT : GL_MIRRORED_REPEAT) ;
break ;
}
// carico l'immagine in OpenGL come texture
GLuint nTexName ;
glPixelStorei( GL_UNPACK_ALIGNMENT, 4) ;
glGenTextures( 1, &nTexName) ;
glBindTexture( GL_TEXTURE_2D, nTexName) ;
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, nFlag) ;
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, nFlag) ;
float color[] = { 0.9f, 0.9f, 0.9f, 1.0f} ;
glTexParameterfv( GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color) ;
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) ;
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) ;
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, nWidth, nHeight,
0, GL_BGR, GL_UNSIGNED_BYTE, FreeImage_GetBits( pDib)) ;
// libero l'immagine originaria
FreeImage_Unload( pDib) ;
// verifico che la texture sia stata veramente caricata
if ( ! glIsTexture( nTexName))
return false ;
// rendo non corrente questa texture
glBindTexture( GL_TEXTURE_2D, 0) ;
// se dati mm/pixel determino le dimensioni fisiche della texture
if ( dMMxPix > EPS_SMALL && ( dDimX < EPS_SMALL || dDimY < EPS_SMALL)) {
dDimX = dMMxPix * nOriWidth ;
dDimY = dMMxPix * nOriHeight ;
}
// salvo i dati
TextureData textData( sFile, dDimX, dDimY, nWidth, nHeight, nOriWidth, nOriHeight, nTexName, nRepeat) ;
m_umTextData.emplace( sName, textData) ;
return true ;
}
//----------------------------------------------------------------------------
bool
TextureMgr::LoadCalcTexture( const string& sName,
double dMMxPix, double dDimX, double dDimY, int nRepeat)
{
// verifico il contesto OpenGL
if ( m_pScene == nullptr || ! m_pScene->MakeCurrent())
return false ;
// verifico se immagine già caricata
auto iIter = m_umTextData.find( sName) ;
if ( iIter != m_umTextData.end())
return ( iIter->second.sFile.empty() && iIter->second.nRepeat == nRepeat) ;
// le texture calcolate sono : Chessboard e Lines
if ( ! EqualNoCase( sName, "Chessboard") && ! EqualNoCase( sName, "Lines"))
return false ;
bool bChess = ( EqualNoCase( sName, "Chessboard")) ;
// creo l'immagine
const int IMG_WIDTH = 64 ;
const int IMG_HEIGHT = 64 ;
GLubyte Image[IMG_HEIGHT][IMG_WIDTH][4] ;
for ( int i = 0 ; i < IMG_HEIGHT ; ++ i) {
for ( int j = 0 ; j < IMG_WIDTH ; ++ j) {
int c ;
if ( bChess)
c = (((i & 0x8) == 0) ^ ((j & 0x8) == 0)) * 255 ;
else
c = ((i & 0x8) == 0) * 255 ;
Image[i][j][0] = (GLubyte) c ;
Image[i][j][1] = (GLubyte) c ;
Image[i][j][2] = (GLubyte) c ;
Image[i][j][3] = (GLubyte) 255 ;
}
}
// determino il flag di ripetizione
GLint nFlag ;
switch ( nRepeat) {
default : // TXR_CLAMP
nFlag = (( m_pScene->GetOpenGLver() < 13) ? GL_CLAMP : GL_CLAMP_TO_BORDER) ;
break ;
case TXR_REPEAT :
nFlag = GL_REPEAT ;
break ;
case TXR_MIRROR :
nFlag = (( m_pScene->GetOpenGLver() < 14) ? GL_REPEAT : GL_MIRRORED_REPEAT) ;
break ;
}
// carico l'immagine in OpenGL come texture
GLuint nTexName ;
glPixelStorei( GL_UNPACK_ALIGNMENT, 4) ;
glGenTextures( 1, &nTexName) ;
glBindTexture( GL_TEXTURE_2D, nTexName) ;
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, nFlag) ;
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, nFlag) ;
float color[] = { 0.9f, 0.9f, 0.9f, 1.0f} ;
glTexParameterfv( GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color) ;
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) ;
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) ;
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, IMG_WIDTH, IMG_HEIGHT,
0, GL_BGRA, GL_UNSIGNED_BYTE, &Image) ;
// verifico che la texture sia stata veramente caricata
if ( ! glIsTexture( nTexName))
return false ;
// rendo non corrente questa texture
glBindTexture( GL_TEXTURE_2D, 0) ;
// se dati mm/pixel determino le dimensioni fisiche della texture
if ( dMMxPix > EPS_SMALL && ( dDimX < EPS_SMALL || dDimY < EPS_SMALL)) {
dDimX = dMMxPix * IMG_WIDTH ;
dDimY = dMMxPix * IMG_HEIGHT ;
}
// salvo i dati
TextureData textData( "", dDimX, dDimY, IMG_WIDTH, IMG_HEIGHT, 0, 0, nTexName, nRepeat) ;
m_umTextData.emplace( sName, textData) ;
return true ;
}
//----------------------------------------------------------------------------
bool
TextureMgr::UnloadTexture( const string& sName)
{
// verifico il contesto OpenGL
if ( m_pScene == nullptr || ! m_pScene->MakeCurrent())
return false ;
// cerco la texture, se non la trovo già ok
auto iIter = m_umTextData.find( sName) ;
if ( iIter == m_umTextData.end())
return true ;
// scarico la texture da OpenGL
GLuint nTextId = iIter->second.nTexId ;
glDeleteTextures( 1, &nTextId) ;
// tolgo la texture dall'elenco
m_umTextData.erase( iIter) ;
return true ;
}
//----------------------------------------------------------------------------
bool
TextureMgr::Exists( const string& sName) const
{
// ricerca della texture di nome dato
auto iIter = m_umTextData.find( sName) ;
return ( iIter != m_umTextData.end()) ;
}
//----------------------------------------------------------------------------
bool
TextureMgr::GetPixels( const string& sName, int& nWidth, int& nHeight) const
{
// ricerca della texture di nome dato
auto iIter = m_umTextData.find( sName) ;
if ( iIter == m_umTextData.end())
return false ;
// restituisco le dimensioni in pixel
nWidth = iIter->second.nWidth ;
nHeight = iIter->second.nHeight ;
return true ;
}
//----------------------------------------------------------------------------
bool
TextureMgr::GetImagePixels( const string& sName, int& nWidth, int& nHeight) const
{
// ricerca della texture di nome dato
auto iIter = m_umTextData.find( sName) ;
if ( iIter == m_umTextData.end())
return false ;
// restituisco le dimensioni in pixel dell'immagine da cui è stata derivata la texture
nWidth = iIter->second.nImgWidth ;
nHeight = iIter->second.nImgHeight ;
return true ;
}
//----------------------------------------------------------------------------
bool
TextureMgr::GetDimensions( const string& sName, double& dDimX, double& dDimY) const
{
// ricerca della texture di nome dato
auto iIter = m_umTextData.find( sName) ;
if ( iIter == m_umTextData.end())
return false ;
// restituisco le dimensioni fisiche
dDimX = iIter->second.dDimX ;
dDimY = iIter->second.dDimY ;
return true ;
}
//----------------------------------------------------------------------------
bool
TextureMgr::ChangeDimensions( const string& sName, double dDimX, double dDimY)
{
// ricerca della texture di nome dato
auto iIter = m_umTextData.find( sName) ;
if ( iIter == m_umTextData.end())
return false ;
// assegno le nuove dimensioni
iIter->second.dDimX = dDimX ;
iIter->second.dDimY = dDimY ;
return true ;
}
//----------------------------------------------------------------------------
bool
TextureMgr::GetTextureData( const string& sName, unsigned int& nId, double& dDimX, double& dDimY) const
{
// ricerca della texture di nome dato
auto iIter = m_umTextData.find( sName) ;
if ( iIter == m_umTextData.end())
return false ;
// restituzione dei parametri
nId = iIter->second.nTexId ;
dDimX = iIter->second.dDimX ;
dDimY = iIter->second.dDimY ;
return true ;
}
//----------------------------------------------------------------------------
bool
TextureMgr::ReadImage( const string& sFile, FIBITMAP*& pDib, int& nFilePixX, int& nFilePixY)
{
// deduco il formato dell'immagine dalla sua segnatura o dalla estensione del file
FREE_IMAGE_FORMAT nFif = FreeImage_GetFileTypeU( stringtoW( sFile), 0) ;
if ( nFif == FIF_UNKNOWN)
nFif = FreeImage_GetFIFFromFilenameU( stringtoW( sFile)) ;
// se non determinata, esco con errore
if ( nFif == FIF_UNKNOWN)
return false ;
// se formato Jpeg, impostazione flag per eventuale scalatura immagine già in lettura
int nFlag = 0 ;
if ( nFif == FIF_JPEG)
nFlag = ( m_nMaxLinPix / 2) << 16 ;
// carico l'immagine
pDib = nullptr ;
if ( FreeImage_FIFSupportsReading( nFif))
pDib = FreeImage_LoadU( nFif, stringtoW( sFile), nFlag) ;
if ( pDib == nullptr)
return false ;
// recupero alcuni dati dell'immagine e li verifico
if ( FreeImage_GetBits( pDib) == nullptr ||
FreeImage_GetWidth( pDib) == 0 || FreeImage_GetHeight( pDib) == 0) {
FreeImage_Unload( pDib) ;
pDib = nullptr ;
return false ;
}
// recupero dimensioni dell'immagine nel file
if ( nFif == FIF_JPEG) {
FIBITMAP* pTmp = FreeImage_LoadU( nFif, stringtoW( sFile), FIF_LOAD_NOPIXELS) ;
if ( pTmp == nullptr)
return false ;
nFilePixX = FreeImage_GetWidth( pTmp) ;
nFilePixY = FreeImage_GetHeight( pTmp) ;
FreeImage_Unload( pTmp) ;
}
else {
nFilePixX = FreeImage_GetWidth( pDib) ;
nFilePixY = FreeImage_GetHeight( pDib) ;
}
return true ;
}
//----------------------------------------------------------------------------
bool
TextureMgr::TestImageWithOpenGL( FIBITMAP*& pDib)
{
// recupero le dimensioni in pixel dell'immagine
int nWidth = FreeImage_GetWidth( pDib) ;
int nHeight = FreeImage_GetHeight( pDib) ;
// verifico di non superare la massima dimensione ammessa
if ( nWidth > m_nMaxLinPix || nHeight > m_nMaxLinPix) {
double dCoeff = max( nWidth / double( m_nMaxLinPix), nHeight / double( m_nMaxLinPix)) ;
int nNewW = int( nWidth / dCoeff) ;
int nNewH = int( nHeight / dCoeff) ;
FIBITMAP* pTmpDib = pDib ;
pDib = FreeImage_Rescale( pTmpDib, nNewW, nNewH, FILTER_BOX) ;
FreeImage_Unload( pTmpDib) ;
nWidth = nNewW ;
nHeight = nNewH ;
// verifico che la nuova immagine sia valida
if ( pDib == nullptr)
return false ;
if ( FreeImage_GetBits( pDib) == nullptr ||
FreeImage_GetWidth( pDib) != nWidth || FreeImage_GetHeight( pDib) != nHeight) {
FreeImage_Unload( pDib) ;
pDib = nullptr ;
return false ;
}
}
// verifico sia caricabile da OpenGL corrente
glTexImage2D( GL_PROXY_TEXTURE_2D, 0, GL_RGBA, nWidth, nHeight,
0, GL_BGR, GL_UNSIGNED_BYTE, FreeImage_GetBits( pDib)) ;
int nW ;
glGetTexLevelParameteriv( GL_PROXY_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &nW) ;
if ( nW == 0) {
// recupero la massima dimensione di una texture per OpenGL corrente
int nMaxTextDim ;
glGetIntegerv( GL_MAX_TEXTURE_SIZE, &nMaxTextDim) ;
// cerco le dimensioni POT (power of two) appena minori delle due dimensioni
int nNewW = nMaxTextDim ;
while ( nNewW > nWidth)
nNewW /= 2 ;
int nNewH = nMaxTextDim ;
while ( nNewH > nHeight)
nNewH /= 2 ;
// deformo la texture per avere queste dimensioni
FIBITMAP* pTmpDib = pDib ;
pDib = FreeImage_Rescale( pTmpDib, nNewW, nNewH, FILTER_BILINEAR) ;
FreeImage_Unload( pTmpDib) ;
nWidth = nNewW ;
nHeight = nNewH ;
// verifico che la nuova immagine sia valida
if ( pDib == nullptr)
return false ;
if ( FreeImage_GetBits( pDib) == nullptr ||
FreeImage_GetWidth( pDib) != nWidth || FreeImage_GetHeight( pDib) != nHeight) {
FreeImage_Unload( pDib) ;
pDib = nullptr ;
return false ;
}
}
return true ;
}