自己编写一个读取TGA文件的类

时间:2024-03-31 12:55:15
<style type="text/css"> <!-- @page {margin:2cm} pre.cjk {font-family:"DejaVu Sans Condensed",monospace} p {margin-bottom:0.21cm} --> </style>

自己编写一个读取TGA文件的类

TGA文件,也就是Targa文件,是一种图片的格式,在游戏和绘图领域中用得比较广泛。TGA文件是位图文件,存储着各个像素的颜色信息。本来想直接使用《OpenGL超级宝典》里面现成的TGA文件载入函数,然后修改成一个类,但遗憾的是,书上的代码并不适合于所有的TGA文件。有些由GIMPPhotoshop创建TGA文件无法载入。这真是让我感到不爽。好在自己有了几年的C/C++编程经验,通过上网查询TGA文件的格式规范,我终于了解到了TGA文件的奥秘。带着一份自信和一点探索精神,我用了大概一天的时间完成了TGA文件的读取。

首先我们看看GIMP中有关TGA文件格式的处理。

自己编写一个读取TGA文件的类

从上图我们可以知道,TGA分为RLE压缩和非RLE压缩,而且TGA文件的起始位置可以选择左下或者左上。RLE全称游程长度编码(RunLengthEncoded),它通过一种简单的算法来对颜色进行压缩。比如说『1,1,1,1,1,1』就可以表示为『6,1』,其中6表示循环的次数,1表示循环的值。这是一种非常简单的压缩算法。在我的资源中详细地讲述了如何解压缩RLE。如果你在Windows下安装了Photoshop,你会发现TGA文件可以保存为16bit24bit32bit三种类型。这些就是我们可以TGA文件的全部了。

可是TGA文件又远远不止这些,有些TGA文件具有索引的功能,也就是说图像数据保存的并不是颜色,而是索引。可以根据索引来确定每一个像素的颜色。一篇介绍TGA格式文件的说明中写道,TGA文件保存索引可以有效地节约空间,而这些颜色保存在一个颜色映射域(colormapfield)中。所以这无疑是给我们增添了读取的难度。说明只讨论了4种常用的TGA格式文件的读取方法。我就是以这个说明为基础来编写TGA格式文件的。下面是这个页面的截图:


自己编写一个读取TGA文件的类

我的源代码可以在这里找到,我用了《OpenGL超级宝典》的Pyramid例子来测试TGA文件的读取。我的开发环境是Ubuntu+QtCreator2.41。下面是例子的截图:

自己编写一个读取TGA文件的类

程序可以通过[w][s][a][d]键上下左右移动金字塔。当然这不是主要的,可以看到,这个图形可以顺利地载入TGA格式的文件。在我的资源中,我随意地创建了一个16bit16bitRLE24bit24bitRLE32bit32bitRLETGA图像,当你修改文件GLWidget.cpp文件第27行的文件名,可以试试其它格式的文件载入看看。

自己编写一个读取TGA文件的类

出于版面考虑,我只列出部分载入TGA文件的代码,供大家参考。完整代码在我的资源中。

#ifndef TGAFILE_H
#define TGAFILE_H
#pragma pack( push, 1 )
class TGAFile
{
public:
    TGAFile( void );
        bool Load( const char* fileName );
    static const char* GetLastError( void );
        inline signed short Width( void ) { return m_Width; }
        inline signed short Height( void ) { return m_Height; }
        inline unsigned short GLInternatFormat( void ) { return 0x1908; }// GL_RGBA8
        inline unsigned short GLFormat( void ) { return 0x80E1; }// GL_BGRA
        inline unsigned char* Data( void ) { return m_pImageData; }
private:
        bool ReadIDField( void* );
        bool ReadColorMapData( void* );
        void DecodeColorMap( void );
        bool DecodeImageData( void* );
        bool DecodeRLEDataWithColorMap( void* );
        bool DecodeRLE( void* );
        bool ReadImageData( void* );
        unsigned char GetIndexSize( unsigned long );
        void GetRGBA( unsigned char*, unsigned char&, unsigned char&,
                                  unsigned char&, unsigned char& );
    unsigned char                       m_IDFieldLength;
    unsigned char                       m_ColorMapType;
        unsigned char                   m_ImageType;
    // 颜色映射(Color map specfication
        unsigned short                  m_ColorMapOrigin;
        unsigned short                  m_ColorMapLength;
    unsigned char                       m_ColorMapEntrySize;
    // 图像Image Specification
    signed short                        m_OriginX;
    signed short                        m_OriginY;
    signed short                        m_Width;
    signed short                        m_Height;
        unsigned char                   m_PixelSize;
    unsigned char                       m_ImageDescriptor;
        // 注意,此文件解码后像素大小始终是32位的
    unsigned char*                      m_pIDField;             // 图像身份域,长度为m_nIDField
    unsigned char*                      m_pColorMapData;// 颜色映射域,长度为m_ColorMapLength
        unsigned char*                  m_pImageData;   // 图像数据,长度为m_Width * m_Height * m_PixelSize;
    static const char*      s_pErrorMessage;
};
#pragma pack( pop )
#endif // TGAFILE_H


#include <stdio.h>
#include <string.h>
#include <limits.h>
#include "TGAFile.h"
// 错误标志
#define _FILE_NOT_EXIST_   "Cannot open file, because the file does not exist."
#define _HEADER_BROKEN_    "The TGA file header is broken or unreadable, please check if it is a correct TGA file."
#define _ID_FIELD_BROKEN_  "The TGA identity field is broken in some minor cases, please use another file."
#define _COLOR_MAP_BROKEN_ "The TGA color map data is broken in some minor cases, please use another file."
#define _NO_SUCH_PIXEL_SIZE_ \
        "There is no such pixel size for TGA file reader to handle, "\
        "The TGA file reader support only 16, 24, and 32 bit pixel size."\
        "it happens when the header is corrupted."
#define _IMAGE_DATA_BROKEN_ "The image data is broken, please check its integrity or use another file."
#define _PACKAGE_HEADER_BROKEN_ \
        "The image package is broken, please check whether the image is correct."
#define _PACKAGE_DATA_BROKEN_ \
        "The image package data is broken, please check whether the image is correct."
//-----------------------------------------------------------------------------
// 静态成员
const char* TGAFile::s_pErrorMessage = 0;
const char* TGAFile::GetLastError( void )
{
        return s_pErrorMessage;
}
//-----------------------------------------------------------------------------
TGAFile::TGAFile( void )
{
        memset( &m_IDFieldLength, 0, sizeof( TGAFile ) );// 清零
}
//-----------------------------------------------------------------------------
bool TGAFile::Load( const char* fileName )
{
        FILE* pFile = fopen( fileName, "rb" );// 打开文件
        if ( pFile == 0 )
        {
                s_pErrorMessage = _FILE_NOT_EXIST_;
                return false;
        }
        // TGA文件头复制过来
        if ( fread( &m_IDFieldLength, 18, 1, pFile ) < 1 )
        {
                s_pErrorMessage = _HEADER_BROKEN_;
                return false;
        }
        if ( m_IDFieldLength > 0 )
        { if ( !ReadIDField( pFile ) ) return false; }// 读取图像的ID
        if ( m_ColorMapLength > 0 )
        { if ( !ReadColorMapData( pFile ) ) return false; }// 读取颜色映射域
        // 读取文件数据
        switch ( m_ImageType )
        {
        case 1:// 有颜色映射的图像
                DecodeColorMap( );
                if ( !DecodeImageData( pFile ) ) return false;
                break;
        case 2:// 无颜色映射,RGB图像
                if ( !ReadImageData( pFile ) ) return false;
                break;
        case 9:// 游程长度编码的解码(RLE),带颜色映射
                DecodeColorMap( );
                if ( !DecodeRLEDataWithColorMap( pFile ) ) return false;
        case 10:// 游程长度编码的解码(RLE
                if ( !DecodeRLE( pFile ) ) return false;
        }
        fclose( pFile );// 关闭文件
        return true;
}
……