我正在实现C ++程序以从TGA文件读取图像。我得到了标题的结构。
struct TGA_HEADER
{
// length of id string
char id_length;
// image storage info
char colour_map_type;
char image_type;
// colour Map
short first_entry;
short num_entries;
char bits_per_entry;
// image description
short x_origin;
short y_origin;
short width;
short height;
char bits_per_pixel;
char descriptor;
};
图像类如下:
class image
{
private:
public:
TGA_HEADER header;
std::unique_ptr<std::vector<char>> pixel_data;
image(const std::string& image_path);
~image();
static void save_image(const std::string& file_name, image& image);
static void load_image(const std::string& path, const std::unique_ptr<std::vector<char>>& pixel_data, TGA_HEADER& header);
};
我的图像类的构造函数:
image::image(const std::string& image_path)
:
pixel_data(new std::vector<char>)
{
load_image(image_path, this->pixel_data, this->header);
}
我的加载类:
void image::load_image(const std::string& path, const std::unique_ptr<std::vector<char>>& pixel_data, TGA_HEADER& header)
{
std::ifstream file_stream(path, std::ios::in | std::ios::binary | std::ios::ate);
if (file_stream.is_open())
{
file_stream.seekg(0, std::ios::beg);
int tgaDesc = 0;
/* read header */
file_stream.read(&header.id_length, sizeof(header.id_length));
file_stream.read(&header.colour_map_type, sizeof(header.colour_map_type));
file_stream.read(&header.image_type, sizeof(header.image_type));
file_stream.read((char*)(&header.first_entry), sizeof(header.first_entry));
file_stream.read((char*)(&header.num_entries), sizeof(header.num_entries));
file_stream.read(&header.bits_per_entry, sizeof(header.bits_per_entry));
file_stream.read((char*)(&header.x_origin), sizeof(header.x_origin));
file_stream.read((char*)(&header.y_origin), sizeof(header.y_origin));
file_stream.read((char*)(&header.width), sizeof(header.width));
file_stream.read((char*)(&header.height), sizeof(header.height));
file_stream.read(&header.bits_per_pixel, sizeof(header.bits_per_pixel));
file_stream.read(&header.descriptor, sizeof(header.descriptor));
// Skip the ID String
char* skip = new char[256];
file_stream.read(skip, header.id_length);
// Skip the colour map if it doesn't exist
if (!(tgaDesc & 4))
{
int colourMapSize = header.colour_map_type * header.num_entries;
file_stream.read(skip, colourMapSize);
}
delete skip;
int imageDataSize = header.width * header.height * (header.bits_per_pixel / 8);
pixel_data->resize(imageDataSize);
int originalPosition = (int)file_stream.tellg();
/* read image data */
file_stream.read(pixel_data->data(), imageDataSize);
}
我的第一个问题是,如果我以正确的方式(尤其是在构造函数中)将unique_ptr
用于我的pixel_data,第二个问题是我是否分别正确地初始化了TGA_HEADER
对象如果我必须手动删除它或使用智能指针。
我对C ++还是很陌生,所以请随时评论可能遇到的其他问题。
最诚挚的问候
答案 0 :(得分:2)
您在这里使用std::unique_ptr
在技术上没有任何问题,但是在这种情况下完全没有必要。 std::vector
本质上已经是指向基础数组的唯一指针。与其让您的班级有一个std::unique_ptr<std::vector<char>>
成员,不如让它只有一个std::vector<char>
成员。
我也将质疑为什么load_image
和save_image
是静态的,如果它们需要对类的成员(或对象本身,如果是save_image
的引用/指针)。看起来它们成为非静态成员函数似乎更有意义。这样,他们将隐式有权访问被调用对象的成员。
您的load_image
函数中还存在潜在的内存泄漏。您动态分配skip
指向的数组。如果从那里到delete skip
之间的任何读取操作都将引发异常,则将泄漏该数组。 256个字节可能也不足以读取整个颜色图。您可能应该使用std::vector<char>
。更好的是,与其阅读不需要的值,还可以超越它们:
// Skip the ID String
file_stream.seekg(header.id_length, std::ios::cur);
// Skip the colour map if it doesn't exist
if (!(tgaDesc & 4)) {
int colourMapSize = header.colour_map_type * header.num_entries;
file_stream.seekg(colourMapSize, std::ios::cur);
}
编写该示例使我注意到tgaDesc
始终为0
,因此if
块将始终运行。您是要在这里检查header.colour_map_type
吗?当然,如果没有颜色映射,则header.num_entries
应该为0,所以我不确定甚至不需要if
。
虽然我们位于load_image
,但是在打开std::ios::ate
时传递了file_stream
标志,但随后立即seekg
回到了文件的开头。如果删除std::ios::ate
标志,则流最初将定位在文件的开头,多余的seekg
可被删除。
读取文件头的方式通常很好。 Byte ordering (AKA endianness)可能是一个问题,但是TGA和大多数现代CPU都使用低端字节序,因此除非您想支持某些深奥的平台,否则您可能还可以。
将所有内容放在一起可以得到一个image
类,看起来像这样:
class image {
public:
TGA_HEADER header;
std::vector<char> pixel_data;
image(const std::string& image_path);
~image();
void save_image(const std::string& file_name);
void load_image(const std::string& path);
};
image::image(const std::string& image_path)
{
load_image(image_path);
}
void image::load_image(const std::string& path)
{
std::ifstream file_stream(path, std::ios::in | std::ios::binary);
if (file_stream.is_open()) {
int tgaDesc = 0;
/* read header */
file_stream.read(&header.id_length, sizeof(header.id_length));
file_stream.read(&header.colour_map_type, sizeof(header.colour_map_type));
file_stream.read(&header.image_type, sizeof(header.image_type));
file_stream.read((char*)(&header.first_entry), sizeof(header.first_entry));
file_stream.read((char*)(&header.num_entries), sizeof(header.num_entries));
file_stream.read(&header.bits_per_entry, sizeof(header.bits_per_entry));
file_stream.read((char*)(&header.x_origin), sizeof(header.x_origin));
file_stream.read((char*)(&header.y_origin), sizeof(header.y_origin));
file_stream.read((char*)(&header.width), sizeof(header.width));
file_stream.read((char*)(&header.height), sizeof(header.height));
file_stream.read(&header.bits_per_pixel, sizeof(header.bits_per_pixel));
file_stream.read(&header.descriptor, sizeof(header.descriptor));
// Skip the ID String
file_stream.seekg(header.id_length, std::ios::cur);
// Skip the colour map if it doesn't exist
if (!(tgaDesc & 4)) {
int colourMapSize = header.colour_map_type * header.num_entries;
file_stream.seekg(colourMapSize, std::ios::cur);
}
int imageDataSize = header.width * header.height * (header.bits_per_pixel / 8);
pixel_data.resize(imageDataSize);
/* read image data */
file_stream.read(pixel_data.data(), imageDataSize);
}
}
答案 1 :(得分:0)
转发
几年前,我一直在关注一组3D图形视频教程。该系列的创建者是MASc的Marek A. Krzeminski,您可以在这里找到他的网站:www.marekknows.com在他的系列的下载标签下,该代码涉及2个系列,它们属于Shader Engine和Zing系列。由于多种原因,我无法显示要复制,粘贴,编译和运行的代码的所有部分。首先是关于版权的原因。 Marek以前已允许我使用他的代码来指导他人寻找自己作品的解决方案,但前提是我对他的代码表示赞赏。因此,我将要展示的代码不是我的代码,但是,我从他那里学到的概念和技术应该用于帮助解决或解决有关此问题的问题。我无法显示所有代码的另一个原因是,这是一个非常大的项目,其中集成了许多部分。我能做的就是向您展示用于加载TGA
文件的功能,并说明他如何设计该文件,如何将其与项目的其他部分集成以及如何使用它。然后,我将解释它如何适用于您的情况和您的问题。
这是TextureFileReader::loadTga()
函数
void TextureFileReader::loadTga( Texture* pTexture ) {
if ( nullptr == pTexture ) {
throw ExceptionHandler( __FUNCTION__ + std::string( " invalid pTexture passed in" ) );
}
struct TgaHeader {
unsigned char idLength;
unsigned char colorMapType;
unsigned char imageType;
unsigned char colorMapSpecifications[5];
short xOrigin;
short yOrigin;
short imageWidth;
short imageHeight;
unsigned char pixelDepth;
unsigned char imageDescriptor;
} tgaHeader;
enum TgaFileType {
TGA_RGB = 2,
TGA_RLE_RGB = 10,
}; // TgaFileType
// Error Message Handling
std::ostringstream strStream;
strStream << __FUNCTION__ << " ";
// Open File For Reading
m_fileStream.open( m_strFilenameWithPath, std::ios_base::in | std::ios_base::binary );
if ( !m_fileStream.is_open() ) {
strStream << "can not open file for reading";
throwError( strStream );
}
// Get TGA File Header
if ( !m_fileStream.read( reinterpret_cast<char*>( &tgaHeader ), sizeof( tgaHeader ) ) ) {
strStream << "error reading header";
throwError( strStream );
}
// This TGA File Loader Can Only Load Uncompressed Or Compressed True-Color Images
if ( (tgaHeader.imageType != TGA_RGB ) && (tgaHeader.imageType != TGA_RLE_RGB ) ) {
strStream << "TGA loader only supports loading RGB{" << TGA_RGB << "} and RLE_RGB{" << TGA_RLE_RGB
<< "} encoded files. This file contains pixels encoded in an unsupported type{" << tgaHeader.imageType << "}";
throwError( strStream );
}
// Convert Bits Per Pixel To Bytes Per Pixel
unsigned uBytesPerPixel = tgaHeader.pixelDepth / 8;
if ( (uBytesPerPixel != 3) && (uBytesPerPixel != 4) ) {
strStream << "TGA loader only supports 24bpp or 32bpp images. This image uses " << tgaHeader.pixelDepth << " bits per pixel";
throwError( strStream );
}
// Make Room For All Pixel Data
if ( 0 == tgaHeader.imageWidth || 0 == tgaHeader.imageHeight ) {
strStream << "invalid image size (" << tgaHeader.imageWidth << "," << tgaHeader.imageHeight << ")";
throwError( strStream );
}
unsigned uTotalNumBytes = tgaHeader.imageWidth * tgaHeader.imageHeight * uBytesPerPixel;
pTexture->vPixelData.resize( uTotalNumBytes );
// Move Read Pointer To Beginning Of Image Data
if ( tgaHeader.idLength > 0 ) {
m_fileStream.ignore( tgaHeader.idLength );
}
// Used To Get And Flip Pixels Data
std::vector<unsigned char> vTempPixel( uBytesPerPixel, 0 );
if ( tgaHeader.imageType == TGA_RLE_RGB ) {
// TGA Data Is Compressed
// All Error Messages The Same If Error Occurs Below
strStream << "file is corrupted, missing pixel data";
unsigned char ucRepetitionCounter = 0;
unsigned uTotalNumberPixels = tgaHeader.imageWidth * tgaHeader.imageHeight;
unsigned uCurrentPixel = 0;
while( uCurrentPixel < uTotalNumberPixels ) {
// Get Repetition Count Value
if ( !m_fileStream.read( reinterpret_cast<char*>( &ucRepetitionCounter ), sizeof( unsigned char ) ) ) {
throwError( strStream );
}
if ( ucRepetitionCounter < 128 ) {
// Raw Packet. Counter Indicates How Many Different Pixels Need To Be Read
++ucRepetitionCounter;
// Get Pixel Values
if ( !m_fileStream.read( reinterpret_cast<char*>( &pTexture->vPixelData[uCurrentPixel * uBytesPerPixel] ), uBytesPerPixel * ucRepetitionCounter ) ) {
throwError( strStream );
}
} else {
// Run-Length Packet. Counter Indicates How Many Times The Text Pixel Needs To Repeat
ucRepetitionCounter -= 127;
// Get Pixel Value
if ( !m_fileStream.read( reinterpret_cast<char*>( &vTempPixel[0] ), uBytesPerPixel ) ) {
throwError( strStream );
}
// Save Pixel Multiple Times
for ( unsigned int u = uCurrentPixel; u < ( uCurrentPixel + ucRepetitionCounter ); ++u ) {
memcpy( &pTexture->vPixelData[u * uBytesPerPixel], &vTempPixel[0], uBytesPerPixel );
}
}
// Increment Counter
uCurrentPixel += ucRepetitionCounter;
}
} else {
// TGA Data Is Uncompressed
// Get Pixel Data
if ( !m_fileStream.read( reinterpret_cast<char*>( &pTexture->vPixelData[0] ), pTexture->vPixelData.size() ) ) {
strStream << "file is corrupted, missing pixel data";
throwError( strStream );
}
}
m_fileStream.close();
// Convert All Pixel Data from BGR To RGB
unsigned char ucTemp;
for ( unsigned int u = 0; u < uTotalNumBytes; u += uBytesPerPixel ) {
ucTemp = pTexture->vPixelData[u]; // Save Blue Color
pTexture->vPixelData[u] = pTexture->vPixelData[u + 2]; // Set Red Color
pTexture->vPixelData[u + 2] = ucTemp; // Set Blue Color
}
// Flip Image Horizontally
if ( tgaHeader.imageDescriptor & 0x10 ) {
short sHalfWidth = tgaHeader.imageWidth >> 1;
for ( short h = 0; h < tgaHeader.imageHeight; ++h ) {
for ( short w = 0; w < sHalfWidth; ++w ) {
unsigned uPixelLeft = uBytesPerPixel * ( h * tgaHeader.imageWidth + w );
unsigned uPixelRight = uBytesPerPixel * ( h * tgaHeader.imageWidth + tgaHeader.imageWidth - 1 - w );
memcpy( &vTempPixel[0], &pTexture->vPixelData[uPixelLeft], uBytesPerPixel ); // Store Left Pixel
memcpy( &pTexture->vPixelData[uPixelLeft], &pTexture->vPixelData[uPixelRight], uBytesPerPixel ); // Save Right Pixel @ Left
memcpy( &pTexture->vPixelData[uPixelRight], &vTempPixel[0], uBytesPerPixel ); // Save Left Pixel @ Right
}
}
}
// Flip Vertically
if ( tgaHeader.imageDescriptor & 0x20 ) {
short sHalfHeight = tgaHeader.imageHeight >> 1;
for ( short w = 0; w < tgaHeader.imageWidth; ++w ) {
for ( short h = 0; h < sHalfHeight; ++h ) {
unsigned uPixelTop = uBytesPerPixel * ( w + tgaHeader.imageWidth * h );
unsigned uPixelBottom = uBytesPerPixel * ( w + tgaHeader.imageWidth * ( tgaHeader.imageHeight - 1 - h ) );
memcpy( &vTempPixel[0], &pTexture->vPixelData[uPixelTop], uBytesPerPixel ); // Store Top Pixel
memcpy( &pTexture->vPixelData[uPixelTop], &pTexture->vPixelData[uPixelBottom], uBytesPerPixel ); // Save Bottom Pixel @ Top
memcpy( &pTexture->vPixelData[uPixelBottom], &vTempPixel[0], uBytesPerPixel ); // Save Top Pixel @ Bottom
}
}
}
// Store Other Values In Texture
pTexture->uWidth = tgaHeader.imageWidth;
pTexture->uHeight = tgaHeader.imageHeight;
pTexture->hasAlphaChannel = ( tgaHeader.pixelDepth == 32 );
}
该函数由TextureFileReader::getOrCreateTextureInfo()
TextureInfo TextureFileReader::getOrCreateTextureInfo( TextureInfo::FilterQuality filterQuality, bool generateMipMap, bool wrapRepeat ) {
TextureInfo textureInfo = m_pAssetStorage->getTextureInfo( m_strFilenameWithPath );
if ( INVALID_UNSIGNED == textureInfo.uTextureId ) {
// Need To Create The Texture
Texture texture( filterQuality, generateMipMap, wrapRepeat );
if ( !loadPng( &texture ) ) {
loadTga( &texture );
}
textureInfo = m_pAssetStorage->add( texture, m_strFilenameWithPath );
}
return textureInfo;
}
TextureFileReader::loadTga()
使用指向Texture
对象的指针。该纹理对象不过是AssetsStorage
类的头文件中声明的结构。我不会在这里显示AssetsStorage
类,但是会显示Texture
声明:
struct Texture {
bool hasAlphaChannel;
bool generateMipMap;
bool wrapRepeat;
unsigned uWidth;
unsigned uHeight;
TextureInfo::FilterQuality filterQuality;
std::vector<unsigned char> vPixelData;
Texture( TextureInfo::FilterQuality filterQualityIn, bool generateMipMapIn, bool wrapRepeatIn ) :
hasAlphaChannel( false ),
generateMipMap( generateMipMapIn ),
wrapRepeat( wrapRepeatIn ),
uWidth( 0 ),
uHeight( 0 ),
filterQuality( filterQualityIn )
{}
}; // Texture
在调用TextureFileReader::loadTGA()
或TextureFileReader::loadPNG()
之前,此函数将生成一个TextureInfo
对象。这是在“ CommonStructs.h”中声明的另一个结构,可以在这里看到:
struct TextureInfo {
enum FilterQuality {
FILTER_NONE = 1,
FILTER_GOOD,
FILTER_BETTER,
FILTER_BEST
}; // FilterQuality
unsigned uTextureId;
bool hasTransparency;
glm::uvec2 size;
TextureInfo() :
uTextureId( INVALID_UNSIGNED ),
hasTransparency( false ),
size( glm::uvec2( 0, 0 ) )
{}
};
如果通过查找其ID作为其ID的文件名已经存在,它将尝试从TextureInfo
中检索AssetStorage
对象,如果存在则返回它。如果在textureInfo
中找不到AssetStorage
,则需要创建一个Texture
和一个TextureInfo
对象。
如果不存在texture
,并且需要创建一个TextureFileReader::getOrCreateTextureInfo()
方法,则会创建TextureInfo
和Texture
对象的本地堆栈对象。 TextureInfo
和Texture
对象的构造都没有动态内存。这些对象的内存由AssetStorage
类处理。如果Texture
对象的构建成功,则此函数将尝试调用loadPng()
或loadTga()
,因为此引擎支持两种图像文件格式。这是我们将地址传递到临时Texture
对象的地方。
如果在没有引发任何异常的情况下成功打开,读取和解析文件,并且所有图像数据均有效,则控制流将离开load
方法的范围并返回到{ {1}}成员函数。然后,这将尝试将getOrCreateTextureInfo
对象从其texture
成员函数存储到AssetStorage
类的成员容器中(如果尚不存在)。无论哪种方式,此成员函数都将返回add(/*resource*/)
类的textureInfo
引用的AssetStorage
对象。
这就是add(/*resource*/)
和TextureFileReader
用于创建AssetStorage
...
Texture
对象的方式。
在Engine
的构造函数中的game.cpp
文件或application.cpp
文件中,这里是正在创建的纹理的实例:
Game
TextureFileReader titleTextureFileReader( "Assets/images/titleScreen.png" );
m_titleTextureInfo = titleTextureFileReader.getOrCreateTextureInfo( TextureInfo::FILTER_NONE, false, false );
类正在自动使用AssetStorage
。有一个指向TextureFileReader
对象的指针,其中AssetStorage
类是AssetStorage
对象。无法直接在singleton
的标头中看到成员变量,因为这是从TextureFileReader
基类派生的类。此引擎中有大量的继承和多态行为!
现在就您的问题进行全面介绍,如果您通读FileHandler
函数并查看如何使用loadTga()
,则该结构仅存在于此TgaHeader
函数。读取文件并存储数据后,我们将不再需要它。数据存储后,我们将对其进行解析并将其处理为我们要支持的格式。至于实际的像素数据,您可以看到它们清楚地存储在声明为loadTga()
容器的pTexture
的{{1}}成员中。这里没有地方使用vPixelData
或std::vector<unsigned char>
...
现在,对于std::shared_ptr<T>
和std::unique_ptr<T>
对象的内存管理,这些由Texture
类处理...
TextureInfo
类具有AssetStorage
的{{1}}支持...
AssetStorage
从这个typedef
开始,它具有以下成员变量:
Texture
此类还提供了几种处理纹理的方法:
typedef std::unordered_map<std::string, TextureInfo> MapTextureInfos;
在这里,我将显示与typedef
相关的MapTextureInfos m_textureInfos;
方法,此函数的版本可以添加TextureInfo getTextureInfo( const std::string& strFilename ) const;
TextureInfo add( const Texture& texture, const std::string& strFilename );
bool removeTextureInfo( const std::string& strFilename );
bool removeTextureInfo( unsigned uTextureId );
void showLoadedTextureInfo() const;
,AssetStorage::add(/*resource*/)
,Textures
,{ {1}}等...
Audio
对于属于Fonts
类(属于Sprites
的基类)的2D & 3D Rednerables
类的实例,它声明为:
TextureInfo AssetStorage::add( const Texture& texture, const std::string& strFilename ) {
if ( INVALID_UNSIGNED != getTextureInfo( strFilename ).uTextureId ) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " can not store " << strFilename << " multiple times";
throw ExceptionHandler( strStream );
}
TextureInfo textureInfo;
textureInfo.hasTransparency = texture.hasAlphaChannel;
textureInfo.size = glm::uvec2( texture.uWidth, texture.uHeight );
glGetError(); // Clear Errors
glGenTextures( 1, &textureInfo.uTextureId );
GLenum err = glGetError();
if ( err != GL_NO_ERROR ) {
std::ostringstream strStream;
strStream << __FUNCTION__ << " failed glGenTextures with error code 0x" << std::hex << err;
throw ExceptionHandler( strStream );
}
glBindTexture( GL_TEXTURE_2D, textureInfo.uTextureId );
// Wrap Textures
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, ( texture.wrapRepeat ? GL_REPEAT : GL_CLAMP_TO_EDGE ) );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, ( texture.wrapRepeat ? GL_REPEAT : GL_CLAMP_TO_EDGE ) );
const glm::uvec2& openglVersion = s_pSettings->getOpenglVersion();
if ( texture.generateMipMap ) {
switch ( texture.filterQuality ) {
case TextureInfo::FILTER_NONE : {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST );
break;
}
case TextureInfo::FILTER_GOOD: {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR );
break;
}
case TextureInfo::FILTER_BEST: {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
break;
}
default: {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );
}
} // switch
if ( openglVersion.x < 3 ) {
// In OpenGL v3 GL_GENERATE_MIPMAP Is Deprecated, And In 3.1+ It Was Removed
// So For Those Versions We Use glGenerateMipmap Below
static const unsigned int GL_GENERATE_MIPMAP = 0x8191;
glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE );
}
} else {
// No MipMaps
switch ( texture.filterQuality ) {
case TextureInfo::FILTER_NONE:
case TextureInfo::FILTER_GOOD: {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
break;
}
default: {
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
}
} // switch
}
// Load Texture Into Video Memory
glPixelStorei( GL_UNPACK_ALIGNMENT, texture.hasAlphaChannel ? 4 : 1 );
glTexImage2D( GL_TEXTURE_2D,
0,
( texture.hasAlphaChannel ? GL_RGBA8 : GL_RGB8 ),
texture.uWidth,
texture.uHeight,
0,
( texture.hasAlphaChannel ? GL_RGBA : GL_RGB ),
GL_UNSIGNED_BYTE,
&texture.vPixelData[0] );
if ( texture.generateMipMap && openglVersion.x >= 3 ) {
glGenerateMipmaps( GL_TEXTURE_2D );
}
// Store textureId
BlockThread blockThread( s_criticalSection );
m_textureInfos.insert( MapTextureInfos::value_type( strFilename, textureInfo ) );
if ( s_pSettings->isDebugLoggingEnabled( Settings::DEBUG_MEMORY ) ) {
Logger::log( std::string( "Created " ) + strFilename );
}
return textureInfo;
} // add
,并由AssetStorage
的构造函数初始化为:
FileHandler
如您所见,此TextureFileReader
具有许多集成的部分,并且没有使用“手动”动态内存,也没有在此上下文中直接用于图像文件的任何智能指针。所有这些都通过使用的容器的动态内存来处理。现在,此引擎的其他部分中,有些对象是static AssetStorage* m_pAssetStorage;
,而另一些是FileHandler
对象,这取决于对象的生存期和对象的所有权。在这种情况下看不到使用if ( bSaveExceptionInLog && nullptr == m_pAssetStorage ) {
m_pAssetStorage = AssetStorage::get();
}
或Engine
的原因是由于整体引擎的设计方式。在这里,shared_ptr
中的unique_ptr
类使用的shared_ptr
对象是unique_ptr
类的成员变量的引用对象:
AssetStorage
Game
类驻留在库中,而TextureFileReader
类驻留在主应用程序项目中。 Engine
类是从std::unique_ptr<AssetStorage> m_pAssetStorage;
类继承的。
这里Engine
负责整个生命周期,并拥有指向所有单例对象(例如Game
,Game
,Engine
,{ {1}}和Engine
。所有这些对象都是在ShaderManager
的构造函数中创建的,该构造函数在调用AudioManager
类或从其继承的BatchManager
类时被调用。它们的生命周期都与FontManager
类的生命周期相同,或者直到它们被明确释放为止。现在,对于shared_ptr对象,它们将在AnimationManager
,Engine
,Game
,Application
等中找到,因为它们是可以由多个对象使用的共享资源。
这不仅可以直接回答有关Engine
的使用或编写纹理或图像加载器的实现的问题,而且还可以说明这种最低限度的复杂性和健壮但灵活,动态且通用的集成系统。
如果您想了解更多有关此代码的信息,那么在此软件的工程设计中将使用它的设计结构以及规划和设计方法,我强烈建议您访问Marek的网站。尽管他的内容不是免费的,但也不是真正昂贵的。我想说,多年来我从购买和查看他的内容中学到的知识非常值得。我并不是说他的方法,技术或实现设计是最好的,但他对自己的工作方式和原因的解释和说明是您收藏中的宝贵资源和工具。我会说这是一项很好的投资。