计算机数值和非数值数据编码基础

数值的存储都是小端模式:little endian,网络的传输是大端模式:big endian。

1.整数用补码表示


正数和0的补码是自身;负数的补码是符号位不变,其余各位取反,最后加1(加1是因为二进制最大只能表示到模减1大小)。
                         正数                                                              负数
原码                  就是其自身                                                     符号位置1,数值部分不变
反码                  就是其自身                                                     符号位置1,数值部分取反
补码                  就是其自身                                                     符号位置1,数值部分取反加1
移码                  对应补码的符号位直接变反即可

//计算机是以补码的形式存放数据的
补码(two's complement) 在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值位统一处理(符号位参与,都为正数处理);同时,加法和减法也可以统一处理。
补码加法
[X+Y]补 = [X]补 + [Y]补
补码减法
[X-Y]补 = [X]补 - [Y]补 = [X]补 + [-Y]补【1】

【X*Y】补==【X】补×Y
实例:
[+1] = [00000001]原 = [00000001]反 = [00000001]补
[-1] = [10000001]原 = [11111110]反 = [11111111]补

于是补码的出现, 解决了0的符号以及两个编码的问题:
1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原
这样0用[0000 0000]表示, 而以前出现问题的-0则不存在了.而且可以用[1000 0000]表示-128:
(-1) + (-127) = [1000 0001]原 + [1111 1111]原 = [1111 1111]补 + [1000 0001]补 = [1000 0000]补

整数的溢出及异常:
整数是不会出现溢出异常的,整数的溢出被认为是正常的舍弃(其实只要很合理)。整数只有被0除才会异常,而浮点数,即使被0除也不会抛出异常。

计算机离散数特殊现象之绝对值:
绝对值等于自己的数有两个,0 和最小的负数。
计算机中绝对值表示:
abs(x) :=  (x >= 0) ? x : -x

负数在计算机中以补码形式存储,计算补码的方式和取反操作类似。
符号位不变,其它位取反,最后加一。

现在我们回到最小的负数问题,最小的负数在计算机中表示为 1000,000,下面我们对这个数操作
补码:     1000,0000
各位取反:  0111,1111
加一:     1000,0000

整数内存中十六进制表示(二进制)和十进制转换:
    int TestNum = -1;
    char *pNum = (char*)&TestNum;
    char szNumBuffer[256];
    // szNumBuffer的结果只是存储表示,并不是数字的正序十六进制表示
    sprintf_s(szNumBuffer, "%02x%02x%02x%02x",(unsigned char)*pNum, unsigned char(*(pNum + 1)), unsigned char(*(pNum + 2)), unsigned char(*(pNum + 3)));
    union uTagNum
    {
        int a;
        char b[4];
    }uTagData;
    // 十六进制字符拷贝到联合体中,因为浮点数不能左移运算
    string strNumBuffer = szNumBuffer;
    
    for(int i = 0; i < 4; i++)
    {
        int hex = 0;
        string hexStr = strNumBuffer.substr(2 *i, 2);
        sscanf(hexStr.c_str(), "%02x", &hex);
        uTagData.b[i] = hex; // 3-i需要小端模式存放,也就是低字节在低位置
    }
    printf("整数本来的值:%d\n", uTagData.a);


2.浮点数用IEEE 754标准浮点数格式表示

公式:将浮点数化为:Sx(2^E)xF 后转换为:(-1)Sx(2^(E + 127))x(F - 1),得到S+(E+127)+(F-1)的二进制数,也就是十六进制数。
浮点数表示格式:float:符号位(1) 指数位(8) 尾数位(23); double:也是类似的方式,只是位数不同。
对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字F。

S表示正负,0正,1负;E用移码表示(+127);F用原码表示(无论是IEEE 754标准还是IBM 370格式)。

浮点数的精度问题,溢出异常:
    1)比较精度问题:浮点数的比较,精度定位到七位或者六位,或者自定义精度,这样可以得到自己想要的结果。
    2)运算精度问题:浮点数的运算也存在有效位,差异大的数相加减会舍掉小的数的精度,高精度尽量用double表示。
    
    3)转换精度问题:直接float,double和 int long long之间转换,因为内存中的表示差异会得到不一样的值,转换会发生错误。
    4)溢出问题:除0操作,0与无穷大运算,无穷大与无穷大运算

    若E位为255并且F位为0时表示无穷大的数,此时浮点数受S位影响,例如0x7F800000表示正无穷大,0xFF800000表示负无穷大。当我们使用1个数除以0时,结果将被记作0x7F800000。无穷大的调试表示为:1.#INF,当除以0的时候会发生。INF是infinity 无穷大的意思。
    若E位为255并且F位不为0时表示非数值,也就是说是非法数,例如0x7F800001。非法数的调试表示为:1.#IND, 对负数开平方,对负数取对数,0.0/0.0,0.0*∞, ∞/∞ 等会产生。或者表示为NAN。IND是indeterminate不确定/模糊的意思,NAN是not a number的意思。

    int nTest = -1;// 0x1000 0001,补码为0xFFFF FFFF
    char *pNPointer = (char*)&nTest;
    char szBuffer[256];
    sprintf_s(szBuffer, "内存字节:%0x:%0x:%0x:%0x", *pNPointer, *(pNPointer + 1), *(pNPointer + 2), *(pNPointer + 3));
    // 浮点数在内存中的表示,没有使用补码,而是IEEE 754的浮点数表示形式,S是符号位,E是指数,F是原码
浮点数内存中十六进制表示(二进制)和十进制相互转换:
string DigitalData::TestFloat(float fData)
{
    unsigned char *pData = (unsigned char*)&fData;
    char szBuffer[32];
    //再转化为16进制为:47 F1 20 00,最后把它翻过来,就成了:00 20 F1 47。
    unsigned char Tail = *(pData);
    unsigned char Tail2 = *(pData + 1);
    unsigned char Exp = *(pData + 2);
    unsigned char Head = *(pData + 3);
    sprintf_s(szBuffer, "%02x%02x%02x%02x", Head, Exp,Tail2, Tail);
    //printf(szBuffer);
    return string(szBuffer);
}

float DigitalData::GetFloatFromString( string &strFloatValue)
{
    union
    {
        float fValue;
        char szBuffer[4];
    }tempUnion;

    // 十六进制字符拷贝到联合体中,因为浮点数不能左移运算
    for(int i = 0; i < 4; i++)
    {
        int hex = 0;
        string hexStr = strFloatValue.substr(2 *i, 2);
        sscanf(hexStr.c_str(), "%02x", &hex);
        tempUnion.szBuffer[ 3 - i] = hex; // 3-i需要小端模式存放,也就是低字节在低位置
    }

    return tempUnion.fValue;
}


3.非数值类型char/singed char/unsigned char的区别-数值直接转换需要看原类型 但unsigned char的范围更广和十六进制正确/ 指针间接解析字节都正确

总结:

1. 直接数值转换:

在表示数值类型时候,char和unsigned char表示范围不同,转换时候需要注意原来的类型,否则数值直接转换都会出现问题。但当转换为大数据类型时候,char会根据首位来对十六进制前面拓展编码为0或者1,改变了原来编码;unsigned char转换为大数据类型时候,都是拓展编码了0,没有改变原来十六进制编码的含义。

2.非数值表示含义:

但在表示非数值类型时候,unsigned char可以支持Ascii以外的拓展编码,可以表示更多的非数值含义。

3.指针遍历间接转换:

指针遍历时候,没有对字节上面的值进行截取转换,所以char *和unsigned char*间接转换为大数据类型是没有问题的;但是在char和unsigned char之间转换,会出现上面的1,2情况。
void TestFunc( unsigned char v)
{
    char c = v; // 记录的是有符号的数,若是负数那么转换正确,十六进制字面表达填充错误
    unsigned char uc = v;  // 记录的是无符号的数,十六进制字面表达正确

    unsigned int a = c, b = uc;
    int i = c, j = uc;
    printf("----------------\n");
    printf("%%c: %c, %c\n", c, uc);// 都是Ascii拓展码表的拉丁字符
    printf("%%X: %X, %X\n", c, uc); // FFFFFF80, 80 ;unsigned char正确高位拓展为0
    printf("%%u: %u, %u\n", a, b); // 4294967168, 128
    char szBuffer1[256];
    unsigned char *pJ = (unsigned char*)&b;
    sprintf_s(szBuffer1, "%02x%02x%02x%02x",unsigned char(*(pJ + 3)),unsigned char(*(pJ + 2)),
        unsigned char(*(pJ + 1)), unsigned char(*(pJ + 0)));
    printf("unsigned int的十六进制值:%s\n", szBuffer1);// 输出0x00000080

    printf("%%d: %d, %d\n", i, j); // -128, 128
//1.数值转换:因为一般编译器char是signed char表示范围[-128,127]最高位是符号位,
// 高位为1时转换为大数前面都填充1,导致十六进制不准确;但是本身为负数,转换为大数负数是正确的。
// unsigned char表示范围是[0,255],最高位不是符号位,转换为大数时,不管最高位是1还是0都是填充0
// 十六进制是正确的;但是本身为负数转换到的却不是负数。
// 总之数值转换要看原来的值来确定用char还是unsigned char,否则char/unsigned char转换整数值都会有问题;
 // 非数值类型例如颜色等却要用unsigned char额外保证转换为大数值类型时正确, 真正的操作用指针解析将会更加准确。
             

    int nTest = -255/*0x80*/;
    printf("Origin num: %d\n", nTest);
// 2.指针解析:对数值十六进制(二进制)解析时候(文件和网络内存中),unsigned char*,char*都是OK的,因为指针只是遍历并没有转换截取
/*unsigned*/ char *pNum = (/*unsigned*/ char*)&nTest;

    int nDecodeNum = 0;
    nDecodeNum |= int(*(pNum+3));// 需要转换为int,所以用int
    nDecodeNum<<=8; // 需要<<=
    nDecodeNum |= int(*(pNum+2));
    nDecodeNum<<=8;
    nDecodeNum |= int(*(pNum+1));
    nDecodeNum<<=8;
    nDecodeNum |= int(*(pNum + 0));
    printf("rellay negativeData:%d\n", nDecodeNum);
}


int _tmain(int argc, _TCHAR* argv[])
{
    TestFunc(0x80);// 前面是1,转换为char后,char转换为十六进制,转换为int/unsigned int会首位拓展导致数值拓展不正确;转换为unsigned char却不会出现这样的问题
    TestFunc(0x7E);// 前面符号位是0,无论是转换为char还是unsigned char都是没有问题的

   char nTestNum = 0xFF;
    unsigned char nResult = (unsigned char)nTestNum;
    printf("测试char和unsignec char之间的转换1:%d\n", (char)nResult);

    unsigned char nTestNum2 = 0xFF;
    char nResult2 = (char)nTestNum2;
    printf("测试char和unsignec char之间的转换2:%u\n", (unsigned char)nResult2);


//输出结果:
    /*----------------
    ----------------
    %c: €, €
    %X: FFFFFF80, 80
    %u: 4294967168, 128
    %d: -128, 128
    Origin num: -255
    rellay negativeData:-255
    ----------------
    %c: ~, ~
    %X: 7E, 7E
    %u: 126, 126
    %d: 126, 126
    Origin num: -255
    rellay negativeData:-255*/
    while(1);
    return 0;
}


4.汉字字符编码

汉字是字模库中的点阵信息(图形学上也就是图像贴图,获取后需要转换为纹理渲染到显示器上)
汉字的显示原理:
1).从键盘输入的汉字经过键盘管理模块,变换成机内码。
机内码
也就是内码。汉字区位码的区码和位码的范围在1~94内,如果直接作为机内码必将与基本的ASCII码冲突。为避免与基本ASCII码中的控制码与字符码的冲突,分别在区码、位码上增加A0H(即10100000)。所以,内码同样占两个字节,分别称为高位内码与低位内码,按如下规则确定:
高位内码=区码+A0H
低位内码=位码+A0H
例如,汉字“啊”的内码是“B0A1H”(即1011000010100001)。
注:高位内码与低位内码的取值范围均是A1H~FEH。

2).然后经字模检索程序,查到机内码对应的点阵信息在字模库的地址。
以16×16的点阵汉字库文件为例。一个汉字用了256个点共32个字节表示。汉字共分94区,每个区有94位汉字。机内码用两个字节表示,第一个字节存储区号(qh),为了和ASCII码相区别,范围从十六进制的A1H开始(小于80H地为ASCII码字符),对应区码的第一区;第二个字节是位号(wh),范围也从A1H开始,对应某区中的第一个位码。这样,将汉字机内码减去A0A0H就得到该汉字的区位码。从而可以得到汉字在字库中的具体位置:
位置=(94*(qh-1) + wh-1) * 一个汉字字模占用的字节数
对于16×16的点阵汉字库,汉字在字库中的具体位置的计算公式就是:(94*(qh-1)+wh-1)*32。例如,“房”的机内码为十六进制的B7BF,则其区位码是B7BFH-A0A0H=171FH,转化为十进制就是2331,在汉字库中的位置就是32*[94*(23-1)+(31-1)]=67136字节以后的32个字节为“房”的显示点阵。

3).从字库中检索出该汉字点阵信息。
4).利用显示驱动程序将这些信息送到显示卡的显示缓冲存储器中。
5).显示器的控制器把点阵信息整屏顺次读出,并使每一个二进制位与屏幕的一个点位相对应,就可以将汉字字形在屏幕上显示出来。

大陆用得最多最广泛的是GB2312-80 汉字编码。
汉字用两个字节(共16位二进制数编码)表示, 两字节首位都是“1” ,这种汉
字编码最多可以表示 27×27=128×128=16384个汉字。
见:http://www.wendangdaquan.com/Wdshow.asp?id=2744a7ee6294dd88d0d26b99
汉字的存储也是小端顺序的,但是这个时候汉字的最小单位不是一个字节而是两个字节。

汉字内存中十六进制表示(二进制)和汉字字符之间的相互转换:
  char *pCh = "啊爱";
 char szCh[256];
 char szCh0[256];
 sprintf_s(szCh0, "%d", char(*(pCh)));// szCh0的值为-80,因为汉字是双字节的,汉字的最高位都是1,将单个字节转成整数当然就是负数了

 sprintf_s(szCh, "%02x%02x%02x%02x", unsigned char(*(pCh)), unsigned char(*(pCh+1)),unsigned char(*(pCh+2)), unsigned char(*(pCh+3)));
 
 // 汉字的GB2312编码,或者UTF-16,UTF-8编码,是用2字节是作为一个存储顺序单元,所以pCh+0是高位8字节,pCh+1是低位8字节。
 int nHighByte;
 int nLowByte;
 sscanf(&szCh[0], "%02x%02x", &nHighByte, &nLowByte);
 char szDecodeBuffer[256];
 szDecodeBuffer[0] = nHighByte;
 szDecodeBuffer[1] = nLowByte;
  sscanf(&szCh[4], "%2x%2x", &nHighByte, &nLowByte);
  szDecodeBuffer[2] = nHighByte;
  szDecodeBuffer[3] = nLowByte;

 szDecodeBuffer[4] = 0;

 printf("%s\n", szDecodeBuffer);


asc转十六进制 ,十进制数值上的转换而不是字符编码的转换,属于加密解密范畴
int ascToHex(const char *Asc, char *Hex, int AscLen)  
{  
    char *HexPtr = Hex;  
    short i;  

    for(i = 0; i < AscLen; i++)  
    {  
        *HexPtr = Asc[i] << 4;  
        if (!(Asc[i]>='0' && Asc[i]<='9'))  
            *HexPtr += 0x90;  
        i++;  
        *HexPtr |= (Asc[i] & 0x0F);  
        if (!(Asc[i]>='0' && Asc[i]<='9'))  
            *HexPtr += 0x09;  
        HexPtr++;  
    }  

    return 0;  
}


5.图形图像颜色的表示

1)颜色数据的表示和操作

颜色一般用unsigned int来表示,ARGB每个通道一个字节的格式,为了避免符号位所以需要unsigned char来存取操作。
显卡的监视器会解析这些unsigned char的颜色值,将其渲染出来。

颜色操作:
uint32_t Color::to_ARGB32() const {

 uint32_t c=(uint8_t)(a*255); // 例如0x0000007F
 c<<=8;      //0x00007F00
 c|=(uint8_t)(r*255);   //0x00007F10
 c<<=8;  //0x007F1000
 c|=(uint8_t)(g*255);  //0x007F1011
 c<<=8; //0x7F101100
 c|=(uint8_t)(b*255);  //0x7F101112

unsigned char red = (unsigned char)(color >> 16);
int nRed = (int)red; // 输出16

 return c; //0x7F101112
}
// 总结:   
// 1. C/C++语言的位移运算不需要考虑内存存放的大小端,因为这个是对逻辑上十六进制的位移, 直接16进制左右移动即可。
// 2.小转大,大左移<<填充:类型的转换,将小类型转换为大类型,只需要声明一块大数据类型的空间,然后左移填充各个小的数据类型。
// 3.大转小,大右移>>截取:大的转换为小的先右移,然后直接强制类型转换截取,或者格式截取。

左右移动只是数值上的操作,赋值应该在寄存器上不用考虑小端模式(逻辑上左移动即可); 读取在小端存储模式上,读取后也用到了寄存器。
左右移动只是改变大数据的值,地址并不改变。输出是<<,输入是>。

2)数值图形数据的表示和操作

图形数据存放形式:
结论1:unsigned char编码转换安全和范围更大:
unsigned char可以安全的表示十六进制数据,浮点数,结构体和非数值数据;unsigned char可以表示更多的编码范围。
结论2:图形图像数据的存取和内存表示:
1)传入二维指针其实里面是用了一维,传入指针时候用unsigned char就可以了。
2)unsigned char可以安全的表示十六进制,浮点数和结构体,非数值数据,也就是图形图像数据表示。

例子1:
void TestFunc( unsigned char v)
{
     char c = v;
     unsigned char uc = v;
     unsigned int a = c, b = uc;
     int i = c, j = uc;
     printf("----------------\n");
     printf("%%c: %c, %c\n", c, uc);
     printf("%%X: %X, %X\n", c, uc);
     printf("%%u: %u, %u\n", a, b);
     printf("%%d: %d, %d\n", i, j);
}
void TestFunc( /*unsigned*/ char v)
{
     char c = v;
     unsigned char uc = v;
     unsigned int a = c, b = uc;
     int i = c, j = uc;
     printf("----------------\n");
     printf("%%c: %c, %c\n", c, uc);
     printf("%%X: %X, %X\n", c, uc);
     printf("%%u: %u, %u\n", a, b);
     printf("%%d: %d, %d\n", i, j);
}

void main()
 {
    TestFunc(0x80);//都是这样的测试代码,结果表明unsigned char作为数值转化为高位时候填充是安全的,非数值时候可以表示更多。
}

例子2:
struct MyVertex
{
    float x;
    float y;
    float z;
    MyVertex(float a,float b,float c)
    {
        x = a; y = b; z = c;
    }
};

void printVertex(const MyVertex &b)
{
    printf("value:(%1.2f,%1.2f,%1.2f)\n",b.x,b.y,b.z);
};

void func(unsigned char **c)
{
    *c= (unsigned char *)malloc(sizeof(unsigned char) * 100);
}
    MyVertex *vertex = NULL;;
    func((unsigned char**)&vertex); // Direct3D很多函数都是,传入二维指针,函数内部对一维进行分配内存(连续的内存空间).
    if(vertex == NULL)
        return;
    vertex[0] = MyVertex( 2.0f,2.0f,3.0f); // 对传出的一维连续内存空间进行赋值
    vertex[1] = MyVertex( 2.0f,2.0f,3.0f);
    vertex[2] = MyVertex( 2.0f,2.0f,3.0f);
    vertex[3] = MyVertex( 2.0f,2.0f,3.0f);
    vertex[4] = MyVertex( 4.0f,4.0f,9.0f);
    printVertex(vertex[0]);//1.传入二维指针其实里面是用了一维,传入指针时候用unsigned char就可以了。2.unsigned char可以安全的表示十六进制,浮点数和结构体,非数值数据。
    printVertex(vertex[3]);
    while(1);
}

3)图形图像文件的表示和操作
图形数据格式:
GA文件
TGA(Tagged Graphics)文件  TGA是由美国Truevision公司为其显示卡开发的一种图像文件格式,已被国际上的图形、图像工业所接受。现在已成为数字化图像,以及运用光线跟踪算法所产生的高质量图像的常用格式。TGA文件的扩展名为.tga。 TGA的结构比较简单,属于一种图形、图像数据的通用格式,目前大部分文件为24位或32位真彩色,在多媒体领域有着很大影响。由于Truevision 公司推出TGA的目的是为了采集、输出电视图像,所以TGA文件总是按行存储、按行进行压缩的,这使得它同时也成为计算机生成图像向电视转换的一种首选格 式。使用photoshop软件可以打开此类文件。
结构
  TGA的结构比较简单,属于一种图形、图像数据的通用格式,在多媒体领域有很大影响,是计算机生成图像向电视转换的一种首选格式。
特点
  TGA图像格式最大的特点是可以做出不规则形状的图形、图像文件,一般图形、图像文件都为四方形,若需要有圆形、菱形甚至是缕空的图像文件时,TGA可就派上用场了!
优点
  TGA格式支持压缩,使用不失真的压缩算法。
  在工业设计领域,使用三维软件制作出来的图像可以利用TGA格式的优势,在图像内部生成一个Alpha(通道),这个功能方便了在平面软件中的工作。
Tga常见的格式有非压缩RGB和压缩RGB两种格式,文件的第三个Byte位作为标记:2为非压缩RGB格式,10为压缩RGB格式。这里的类只实现读取非压缩格式的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

2

它的值为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 之一。

图像数据

可变

可变

RGB颜色数据,存放顺序为:BBB GGG RRR (AAA)


 
代码如下:
1.    //========================================================
2.    /**
3.    *  @file      TGALoader.h
4.    *
5.    *  项目描述: TGA文件载入类
6.    *  文件描述:  纹理映射  
7.    *  适用平台: Windows98/2000/NT/XP
8.    *  
9.    *
10.    */     
11.    //========================================================
12.    #ifndef __TGALOADER_H__
13.    #define __TGALOADER_H__
14.    #include "stdafx.h"
15.    /** TGA文件载入类 */
16.    class CTGALoader
17.    {
18.       public:
19.          
20.          CTGALoader();                             /**< 构造函数 */
21.          ~CTGALoader();
22.          bool LoadTGA(const char *file);          /**< 载入TGA文件 */
23.          void FreeImage();                        /**< 释放内存 */
24.          bool Load(const char* fileName); /**< 载入TGA文件为纹理 */
25.          unsigned int ID;                        /**< 生成纹理的ID号 */
26.          int imageWidth;                         /**< 图像宽度 */
27.          int imageHeight;                        /**< 图像高度 */
28.          unsigned char *image;                   /**< 指向图像数据的指针 */
29.          unsigned int type;                      /**< 图象类型GL_RGB 或GL_RGBA */
30.    };
31.    #endif
1.    //========================================================
2.    /**
3.    *  @file      TGALoader.cpp
4.    *
5.    *  项目描述: 纹理映射
6.    *  文件描述:  TGA文件载入  
7.    *  适用平台: Windows98/2000/NT/XP
8.    *  
9.    *
10.    */     
11.    //========================================================
12.    #include "TGALoader.h"
13.    /** 构造函数 */
14.    CTGALoader::CTGALoader()
15.    {
16.      /** 设置为默认值 */
17.      image = 0;
18.      type = 0;
19.      ID = -1;
20.      imageWidth = 0;
21.      imageHeight = 0;
22.    }
23.    /** 析构函数 */
24.    CTGALoader::~CTGALoader()
25.    {
26.       FreeImage();           /**< 释放内存 */
27.    }
28.    /** 载入TGA文件 */
29.    bool CTGALoader::LoadTGA(const char* file)
30.    {
31.       FILE *pfile;
32.       unsigned char tempColor;              /**< 用于交换颜色分量 */
33.       unsigned char bitCount;               /**< 每象素的bit位数 */
34.       int colorMode;                        /**< 颜色模式 */
35.       long tgaSize;                         /**< TGA文件大小 */
36.       unsigned char unCompressHeader[12] = {0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /**< 未压缩TGA文件头 */
37.       unsigned char tgaHeader[12];          /**< 文件头 */
38.       unsigned char header[6];              /**< 文件头前6个字节 */
39.       /** 检查文件名是否为空 */
40.       if(!file)
41.           return false;
42.       
43.       /** 打开文件 */
44.       pfile = fopen(file, "rb");
45.       if(!pfile)
46.           return false;
47.       /** 读取文件头前12个字节 */
48.       fread(tgaHeader, 1, sizeof(tgaHeader), pfile);
49.       /** 比较文件是否为未压缩文件 */
50.       if(memcmp(unCompressHeader, tgaHeader, sizeof(unCompressHeader)) != 0)
51.           {
52.               MessageBox(NULL,"文件类型错误!","错误",MB_OK);
53.               fclose(pfile);
54.               return false;
55.           }
56.       /** 读取6个字节(从上面读取的12个字节后再取6个字节) */
57.       fread(header, 1, sizeof(header), pfile);
58.       /** 计算图像的宽度和高度 */
59.       imageWidth = header[1] * 256 + header[0];    
60.       imageHeight = header[3] * 256 + header[2];   
61.       /** 获取每象素的bit位数 */
62.       bitCount = header[4];
63.       /** 计算颜色模式和图像大小 */
64.       colorMode = bitCount / 8;
65.       tgaSize = imageWidth * imageHeight * colorMode;
66.       /** 分配内存 */
67.       image = new unsigned char[sizeof(unsigned char) * tgaSize];
68.       /** 读取数据 */
69.       fread(image, sizeof(unsigned char), tgaSize, pfile);
70.       /** 将BGA格式转化为RGA格式 */
71.       for(long index = 0; index < tgaSize; index += colorMode)
72.           {
73.               tempColor = image[index];
74.               image[index] = image[index + 2];
75.               image[index + 2] = tempColor;
76.           }
77.       /** 关闭文件 */
78.       fclose(pfile);
79.       /** 设置图象类型 */
80.       if(colorMode == 3)
81.           type = GL_RGB;
82.       else
83.           type = GL_RGBA;
84.       return true;
85.    }
86.    /** 载入TGA文件并创建纹理 */
87.    bool CTGALoader::Load(const char* fileName)
88.    {
89.        if(!LoadTGA(fileName))
90.        {
91.            MessageBox(NULL,"载入TGA文件失败!","错误",MB_OK);
92.            exit(0);
93.        }
94.        /** 生成纹理对象名称 */
95.        glGenTextures(1, &ID);
96.       
97.        /** 创建纹理对象 */
98.        glBindTexture(GL_TEXTURE_2D, ID);
99.        
100.        /** 控制滤波 */
101.        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
102.        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
103.        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
104.        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
105.       
106.        /** 创建纹理 */
107.        gluBuild2DMipmaps(GL_TEXTURE_2D, type, imageWidth,
108.                          imageHeight, type, GL_UNSIGNED_BYTE,
109.                          image);
110.       return true;
111.    }
112.    void CTGALoader::FreeImage()
113.    {
114.       /** 释放内存 */
115.       if(image)
116.          {
117.             delete[] image;
118.             image = 0;
119.          }
120.    }





  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值