TGA格式图像是游戏中十分常见的一种图像格式,所以有必要了解其内部格式以及编程实现。

TGA图像一般有非压缩和压缩两种格式,下面分别进行介绍。

一、非压缩TGA图像

注:前面的标记绿色的部分(共12字节)表示对于所有的非压缩TGA格式图像值都是相同的!所以通常用来在读取数据时鉴别是否为TGA图像。
 

名称

偏移

长度

说明

图像信息字段长度

0

1

本字段是 1 字节无符号整型,指出图像信息字段( 见本子表的后面 )长度,其取值范围是 0 到 255 ,当它 0 时表示没有图像的信息字段

颜色表类型

1

1

0 表示没有颜色表,1 表示颜色表存在。由于本格式是无颜色表的,因此此项通常被忽略。

图像类型码

2

1

该字段总为 2 , 这也是此类型为格式 2 的原因。

颜色表规格字段

颜色表首址

3

2

颜色表首的入口索引,整型(低位-高位)

如果颜色表字段为0,则忽略该字段

颜色表的长度

5

2

颜色表的表项总数,整型(低位-高位)

颜色表项位数

7

1

位数(bit),16 代表 16 位 TGA ,24 代表 24 位 TGA ,32 代表 32 位 TGA

图像规格字段

图像 X 坐标起始位置

8

2

图像左下角 X坐标的整型(低位-高位)值

图像 Y 坐标起始位置

10

2

图像左下角 Y坐标的整型(低位-高位)值

图像宽度

12

2

以像素为单位,图像宽度的整型(低位-高位)

图像高度

14

2

以像素为单位,图像宽度的整型(低位-高位)

图像每像素存储占用位数

16

1

它的值为16,24 或 32 等等。决定了该图像是 TGA 16,TGA24,TGA 32 等等。

图像描述符字节

17

1

bits 3-0 - 每像素对应的属性位的位数;

对于TGA 16, 该值为 0 或 1,对于 TGA                     24,该值为 0,对于 TGA 32,该值为 8。

 

bit 4    - 保留,必须为 0

 

bit 5    - 屏幕起始位置标志

0 = 原点在左下角

1 = 原点在左上角

对于 truevision 图像必须为 0

 

bits 7-6 - 交叉数据存储标志

00 = 无交叉

01 = 两路奇/偶交叉

10 = 四路交叉

11 = 保留(一般这个字节设为0x00即可)

图像信息字段

18

可变

包含一个自由格式的,长度是图像由“图像信息字段”指定。它常常被忽略(即偏移 0 处值为 0 ),注意其最大可以含有 255 个字符。如果需要存储更多信息,可以放在图像数据之后。

颜色表数据

可变

可变

如果颜色表类型为 0,则该域不存在,否则越过该域直接读取图像颜色表规格中描述了每项的字节数,为 2,3,4 之一。

图像数据

可变

可变

RGB颜色数据,存放顺序为:BGR(A)

下面的程序实现了绘制一个立方体,并进行纹理贴图。

需要注意的是:TGA图像中数据存放的顺序是BGR(A),而在OpenGL中顺序是RGB(A),所以在进行纹理生成的时候必须先进行格式的转化。

在OpenGL中只能加载24位或者32位的TGA图像生成纹理。

TGATexture.h定义了一些结构体以及函数声明:

#ifndef TGATEXTURE_H
#define TGATEXTURE_H#include <GL/glut.h>
#include <iostream>using namespace std;//纹理结构体定义
typedef struct
{GLubyte *imageData;//图像数据GLuint bpp;//像素深度GLuint width;//图像宽度GLuint height;//图像高度GLuint texID;//对应的纹理ID
}TextureImage;//加载TGA图像,生成纹理
bool LoadTGA(TextureImage *texture,char *fileName);#endif

TGATexture.cpp则包含加载TGA图像生成纹理的函数具体实现:

#include "TGATexture.h"//加载TGA图像(无压缩格式),生成纹理
bool LoadTGA(TextureImage *texture, char *filename)         // Loads A TGA File Into Memory
{    GLubyte        TGAheader[12]={0,0,2,0,0,0,0,0,0,0,0,0};   // Uncompressed TGA HeaderGLubyte       TGAcompare[12];                             // Used To Compare TGA HeaderGLubyte        header[6];                                  // First 6 Useful Bytes From The HeaderGLuint       bytesPerPixel;                              // Holds Number Of Bytes Per Pixel Used In The TGA FileGLuint       imageSize;                                  // Used To Store The Image Size When Setting Aside RamGLuint        temp;                                       // Temporary VariableGLuint     type=GL_RGBA;                              // Set The Default GL Mode To RBGA (32 BPP)FILE *file = fopen(filename, "rb");                       // Open The TGA Fileif( file==NULL ||                                     // Does File Even Exist?fread(TGAcompare,1,sizeof(TGAcompare),file)!=sizeof(TGAcompare) || // Are There 12 Bytes To Read?memcmp(TGAheader,TGAcompare,sizeof(TGAheader))!=0                ||  // Does The Header Match What We Want?fread(header,1,sizeof(header),file)!=sizeof(header))             // If So Read Next 6 Header Bytes{if (file == NULL)                                   // Did The File Even Exist? *Added Jim Strong*return false;                                 // Return Falseelse{fclose(file);                                   // If Anything Failed, Close The Filereturn false;                                  // Return False}}texture->width  = header[1] * 256 + header[0];            // Determine The TGA Width  (highbyte*256+lowbyte)texture->height = header[3] * 256 + header[2];          // Determine The TGA Height (highbyte*256+lowbyte)//OpenGL中纹理只能使用24位或者32位的TGA图像if( texture->width   <=0 ||                              // Is The Width Less Than Or Equal To Zerotexture->height    <=0 ||                              // Is The Height Less Than Or Equal To Zero(header[4]!=24 && header[4]!=32))                  // Is The TGA 24 or 32 Bit?{fclose(file);                                       // If Anything Failed, Close The Filereturn false;                                      // Return False}texture->bpp = header[4];                           // Grab The TGA's Bits Per Pixel (24 or 32)bytesPerPixel   = texture->bpp/8;                       // Divide By 8 To Get The Bytes Per PixelimageSize      = texture->width*texture->height*bytesPerPixel;  // Calculate The Memory Required For The TGA Datatexture->imageData=(GLubyte *)malloc(imageSize);       // Reserve Memory To Hold The TGA Dataif(   texture->imageData==NULL ||                            // Does The Storage Memory Exist?fread(texture->imageData, 1, imageSize, file)!=imageSize)  // Does The Image Size Match The Memory Reserved?{if(texture->imageData!=NULL)                      // Was Image Data Loadedfree(texture->imageData);                        // If So, Release The Image Datafclose(file);                                       // Close The Filereturn false;                                      // Return False}//RGB数据格式转换,便于在OpenGL中使用for(GLuint i=0; i<int(imageSize); i+=bytesPerPixel)        // Loop Through The Image Data{                                                     // Swaps The 1st And 3rd Bytes ('R'ed and 'B'lue)temp=texture->imageData[i];                            // Temporarily Store The Value At Image Data 'i'texture->imageData[i] = texture->imageData[i + 2];    // Set The 1st Byte To The Value Of The 3rd Bytetexture->imageData[i + 2] = temp;                  // Set The 3rd Byte To The Value In 'temp' (1st Byte Value)}fclose (file);                                            // Close The File// Build A Texture From The DataglGenTextures(1, &texture[0].texID);                   // Generate OpenGL texture IDsglBindTexture(GL_TEXTURE_2D, texture[0].texID);           // Bind Our TextureglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);    // Linear FilteredglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Linear Filteredif (texture[0].bpp==24)                                 // Was The TGA 24 Bits{type=GL_RGB;                                        // If So Set The 'type' To GL_RGB}glTexImage2D(GL_TEXTURE_2D, 0, type, texture[0].width, texture[0].height, 0, type, GL_UNSIGNED_BYTE, texture[0].imageData);return true;                                         // Texture Building Went Ok, Return True
}

main.cpp主程序:

#include "TGATexture.h"TextureImage texture[1];GLfloat xRot,yRot,zRot;//control cube's rotationint init()
{if(!LoadTGA(&texture[0],"GSK1.tga"))return GL_FALSE;glEnable(GL_TEXTURE_2D);glShadeModel(GL_SMOOTH);glClearColor(0.0f,0.0f,0.0f,0.5f);glClearDepth(1.0f);glEnable(GL_DEPTH_TEST);glDepthFunc(GL_LEQUAL);glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);return GL_TRUE;
}void display()
{glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glLoadIdentity();glTranslatef(0.0f,0.0f,-5.0f);glRotatef(xRot,1.0f,0.0f,0.0f);glRotatef(yRot,0.0f,1.0f,0.0f);glRotatef(zRot,0.0f,0.0f,1.0f);glBindTexture(GL_TEXTURE_2D,texture[0].texID);glBegin(GL_QUADS); // Front Face // Bottom Left Of The Texture and Quad glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f); // Bottom Right Of The Texture and Quad glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f); // Top Right Of The Texture and Quad glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f); // Top Left Of The Texture and Quad glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f); glEnd(); glBindTexture(GL_TEXTURE_2D,texture[0].texID);glBegin(GL_QUADS); // Back Face // Bottom Right Of The Texture and Quad glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // Top Right Of The Texture and Quad glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f); // Top Left Of The Texture and Quad glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f); // Bottom Left Of The Texture and Quad glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); glEnd(); glBindTexture(GL_TEXTURE_2D,texture[0].texID);glBegin(GL_QUADS); // Top Face // Top Left Of The Texture and Quad glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f); // Bottom Left Of The Texture and Quad glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,  1.0f,  1.0f); // Bottom Right Of The Texture and Quad glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,  1.0f,  1.0f); // Top Right Of The Texture and Quad glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f); glEnd(); glBindTexture(GL_TEXTURE_2D,texture[0].texID);glBegin(GL_QUADS); // Bottom Face // Top Right Of The Texture and Quad glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // Top Left Of The Texture and Quad glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // Bottom Left Of The Texture and Quad glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f); // Bottom Right Of The Texture and Quad glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f); glEnd(); glBindTexture(GL_TEXTURE_2D,texture[0].texID);glBegin(GL_QUADS); // Right face // Bottom Right Of The Texture and Quad glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // Top Right Of The Texture and Quad glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f); // Top Left Of The Texture and Quad glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f); // Bottom Left Of The Texture and Quad glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f); glEnd(); glBindTexture(GL_TEXTURE_2D,texture[0].texID);glBegin(GL_QUADS); // Left Face // Bottom Left Of The Texture and Quad glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // Bottom Right Of The Texture and Quad glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f); // Top Right Of The Texture and Quad glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f); // Top Left Of The Texture and Quad glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f); glEnd(); glutSwapBuffers();
}void reshape(int w,int h)
{if (0 == h)h = 1;glViewport(0,0,(GLsizei)w,(GLsizei)h);glMatrixMode(GL_PROJECTION);glLoadIdentity();gluPerspective(60.0f,(GLfloat)w / (GLfloat)h,1,100);glMatrixMode(GL_MODELVIEW);glLoadIdentity();
}void keyboard(unsigned char key,int x,int y)
{switch(key){case 'x':xRot += 1.0f;glutPostRedisplay();break;case 'y':yRot += 1.0f;glutPostRedisplay();break;case 'z':zRot += 1.0f;glutPostRedisplay();break;default:break;}
}int main(int argc,char** argv)
{glutInit(&argc,argv);glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);glutInitWindowSize(400,400);glutInitWindowPosition(100,100);glutCreateWindow("Texture Map");init();glutDisplayFunc(display);glutReshapeFunc(reshape);glutKeyboardFunc(keyboard);glutMainLoop();return 0;
}

运行结果:

二、压缩TGA图像

其格式如下表:

名称

偏移

长度

说明

图像信息字段长度

0

1

本字段是 1 字节无符号整型,指出图像信息字段( 见本子表的后面 )长度,其取值范围是 0 到 255 ,当它为 0 时表示没有图像的信息字段。

颜色表类型

1

1

0 表示没有颜色表,1 表示颜色表存在。由于本格式是无颜色表的,因此此项通常被忽略。

图像类型码

2

1

该字段总为 10 , 这也是此类型为格式 10 的原因。

颜色表规格字段

颜色表首址

3

2

颜色表首的入口索引,整型(低位-高位)

如果颜色表字段为0,则忽略该字段

颜色表的长度

5

2

颜色表的表项总数,整型(低位-高位)

颜色表项位数

7

1

位数(bit),16 代表 16 位 TGA ,24 代表 24 位 TGA ,32 代表 32 位 TGA

图像规格字段

图像 X 坐标起始位置

8

2

图像左下角 X坐标的整型(低位-高位)值

图像 Y 坐标起始位置

10

2

图像左下角 Y坐标的整型(低位-高位)值

图像宽度

12

2

以像素为单位,图像宽度的整型(低位-高位)

图像高度

14

2

以像素为单位,图像宽度的整型(低位-高位)

图像每像素存储占用位数

16

1

它的值为16,24 或 32 等等。决定了该图像是 TGA 16,TGA24,TGA 32 等等。

图像描述符字节

17

1

bits 3-0 - 每像素对应的属性位的位数;

对于TGA 16, 该值为 0 或 1,对于 TGA                     24,该值为 0,对于 TGA 32,该值为 8。

 

bit 4    - 保留,必须为 0

 

bit 5    - 屏幕起始位置标志

0 = 原点在左下角

1 = 原点在左上角

对于 truevision 图像必须为 0

 

bits 7-6 - 交叉数据存储标志

00 = 无交叉

01 = 两路奇/偶交叉

10 = 四路交叉

11 = 保留

图像信息字段

18

可变

包含一个自由格式的,长度是图像由“图像信息字段”指定。它常常被忽略(即偏移 0 处值为 0 ),注意其最大可以含有 255 个字符。如果需要存储更多信息,可以放在图像数据之后。

颜色表数据

可变

可变

如果颜色表类型为 0,则该域不存在,否则越过该域直接读取图像颜色表规格中描述了每项的字节数,为 2,3,4 之一。

图像数据

可变

可变

采用RLE压缩后的RGB颜色数据。

Tga的压缩算法采用了RLE算法,RLE算法的基本思想是将数据分为两大类:

A:连续的不重复字节

B:连续的重复字节

 

RLE算法应用于RGB格式的图片压缩中,则把数据分为:

A:连续的不重复像素颜色值

B:连续的重复像素颜色值

 

然后将数据按这两类数据分成若干长度不相等数据块,每个数据块的开始都是一个1个字节长度的header(RLE在纯数据压缩中header位2个字节16位),后面紧跟着data数据块,如下。

 

Header(1个字节)

Data(变长)

 

每个header的第一位作为标记:0表示A类颜色数据,1表示B类颜色数据。剩下的7位意义如下:

对于A类数据:表示data有多少个像素的RGB颜色值。取值0-127,0表示1个像素,所以最多为128个像素,data块则为这些不重复的像素RGB颜色值。

对于B类数据:表示有多少个像素具有相同的RGB颜色值。取值0-127,0表示1个像素,所以最多为128个像素,data仅包含一个像素的颜色值,即为重复的那个颜色值。

读取其像素数据部分需要注意一下,需要分情况讨论看是A类数据还是B类数据:

if(m_pImageData) delete []m_pImageData;
m_pImageData = new unsigned char[m_iImageDataSize];int iBytePerPixel = m_iBitsPerPixel/8;
int iPixelCnt = m_iImageWidth * m_iImageHeight;
int iCurPixel = 0;
unsigned char ucColorBuffer[4] = {'/0'};do{BYTE chunkheader = 0;is.read((char*)&chunkheader, sizeof(BYTE));//run length dataif(chunkheader & 0x80)//B类数据{int iCnt = (chunkheader & 0x7f) + 1;//计算连续的颜色值重复的像素数。注意:最高位的1是标记位,所以要特殊处理is.read((char*)ucColorBuffer, iBytePerPixel);for(int i = 0; i < iCnt; i++){m_pImageData[iCurPixel*iBytePerPixel + 0] = ucColorBuffer[0];m_pImageData[iCurPixel*iBytePerPixel + 1] = ucColorBuffer[1];m_pImageData[iCurPixel*iBytePerPixel + 2] = ucColorBuffer[2];if(m_iBitsPerPixel == 32){m_pImageData[iCurPixel*iBytePerPixel + 3] = ucColorBuffer[3];}iCurPixel++;if(iCurPixel > iPixelCnt)return false;}}//no processed dataelse//A类数据{int iCnt = chunkheader + 1;//颜色值不重复的像素数量,注意要加一for(int i = 0; i < iCnt; i++){                                             is.read((char*)ucColorBuffer, iBytePerPixel);m_pImageData[iCurPixel*iBytePerPixel + 0] = ucColorBuffer[0];m_pImageData[iCurPixel*iBytePerPixel + 1] = ucColorBuffer[1];m_pImageData[iCurPixel*iBytePerPixel + 2] = ucColorBuffer[2];if(m_iBitsPerPixel == 32){m_pImageData[iCurPixel*iBytePerPixel + 3] = ucColorBuffer[3];}iCurPixel++;if(iCurPixel > iPixelCnt)return false;}}}while(iCurPixel < iPixelCnt);

【OpenGL】游戏编程常用TGA图像格式详解以及加载纹理编程实现相关推荐

  1. java配置文件实现方式_java相关:详解Spring加载Properties配置文件的四种方式

    java相关:详解Spring加载Properties配置文件的四种方式 发布于 2020-4-29| 复制链接 摘记: 一.通过 context:property-placeholder 标签实现配 ...

  2. 浏览器渲染阻塞与优化-详解推迟加载、异步加载。

    我认为一个前端工程师是否优秀,很大程度上取决于对前端性能上优化的功力.所以性能优化对前端真的很重要!!! 本文介绍了什么是阻塞.为什么会阻塞?阻塞优化常用的5种方式以及他们的注意事项. 浏览器渲染阻塞 ...

  3. linux insmod命令参数及用法详解--linux加载模块命令

    insmod(install module) 功能说明:载入模块 install loadable kernel module 语法:insmod [-fkmpsvxX][-o <模块名称> ...

  4. JVM从入门到精通(二):详解Class加载过程,双亲委派机制,编译执行与解释执行

    复习:Class文件结构 Class Loading Linking Initializing:编译 加载 初始化 这节课,我们讲 class 是怎么从硬盘中加载到内存中,并且准备执行的. packa ...

  5. linux modprobe命令参数及用法详解--linux加载模块命令

    From:  http://www.linuxso.com/command/modprobe.html modprobe(module probe) 功能说明:自动处理可载入模块. 语 法:modpr ...

  6. 类的加载过程详解:加载、验证、准备、解析、初始化

    想要弄明白的知识点: 类加载的过程,加载.验证.准备.解析.初始化.每个部分详细描述. 加载阶段读入.class文件,class文件时二进制吗,为什么需要使用二进制的方式? 验证过程是防止什么问题?验 ...

  7. linux 查看ko符号表,linux modprobe命令参数及用法详解--linux加载模块命令

    modprobe(module probe) 功能说明:自动处理可载入模块. 语 法:modprobe [-acdlrtvV][--help][模块文件][符号名称 = 符号值] 补充说明:modpr ...

  8. linux下modprobe命令参数及用法详解--linux加载模块命令

    modprobe(module probe) 功能说明:自动处理可载入模块. 语 法:modprobe [-acdlrtvV][--help][模块文件][符号名称 = 符号值] 补充说明:modpr ...

  9. Python Selenium.WebDriver 最强详解页面加载策略

    Python Selenium.WebDriver 网页加载策略『详细』 文章目录 Python Selenium.WebDriver 网页加载策略『详细』 一.网页加载策略

最新文章

  1. [Prism]Composite Application Guidance for WPF(8)——事件
  2. /usr/bin/time -v 显示内容含义
  3. python条件控制语句要注意什么_关于Python条件控制怎么用?要注意什么?
  4. Android-实现View滑动的6种方式
  5. em算法 实例 正态分布_【机器学习】EM算法详细推导和讲解
  6. python 数据结构-字典
  7. Elasticsearch实现原理分析
  8. 成为LINUX系统管理员几点规则
  9. python selenium系列(六)实战技巧之弹框处理
  10. python自加_浅谈:为什么python没有自增运算符?
  11. 图像处理基本算法之空间域图像增强
  12. 推荐几本比较好的投资书籍
  13. VR+教育 虚拟现实(VR)智慧教室整体解决方案
  14. 多元线性回归模型检验-续上篇
  15. linux小米随身wifi,小米随身wifi for mac版详细使用图文步骤
  16. 字子序列中英翻译模型(五笔特征)
  17. html中代表颜色的字母16个,html中16进制的代码对应颜色表
  18. 如何实现广告图片总是显示在页面上方,并且随滚动条同步移动?
  19. Poj1704:staircase nim【博弈】
  20. Waves.Exchange福利大放送: 投资赢Lambo Huracan或240,000 USDN

热门文章

  1. Mybatis学习IDEA(1)-环境搭建以及入门案例
  2. Docker网络代理配置
  3. 华为交换机配置时区_华为交换机怎么设置时间?
  4. Ripro子主题-ziyuan-zhankr资源主题 蓝色简约版
  5. 校园PHP表白墙程序源码
  6. 狮子鱼社区团购独立版V15.3.0源码
  7. Java快速入门学习笔记1 | Eclipse使用
  8. idea 父文件_在ideal创建新的模块(子项目,同时依赖父模块)
  9. oracle jinitiator版本太旧,Oracle Jinitiator 版本太旧或过低怎么办
  10. java的基本结构_【Java基础】基本程序设计结构