zoukankan      html  css  js  c++  java
  • Tga图片格式分析以及程序实现

    Tga图片格式分析以及程序实现

    本文转自本人Blog: Singlerblog.mblogger.cn

    继位图之后,我们来看看Tga图片的格式,以及程序实现。

     

    一、  文件格式

    Tga常见的格式有非压缩RGB和压缩RGB两种格式,其他格式的我们在这里不做讲述。文件的第三个Byte位作为标记:2为非压缩RGB格式,10为压缩RGB格式。它们的具体格式如下:

     

    1、非压缩格式

    图片类型:2-非压缩RGB格式

     

    名称

    偏移

    长度

    说明

    图像信息字段长度

    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

    它的值为1624 32 等等。决定了该图像是 TGA 16TGA24,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,则该域不存在,否则越过该域直接读取图像颜色表规格中描述了每项的字节数,为 234 之一。

    图像数据

    可变

    可变

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

     

     

    2、压缩格式

    图片类型:10-压缩RGB格式

     

    名称

    偏移

    长度

    说明

    图像信息字段长度

    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

    2

    它的值为1624 32 等等。决定了该图像是 TGA 16TGA24,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,则该域不存在,否则越过该域直接读取图像颜色表规格中描述了每项的字节数,为 234 之一。

    图像数据

    可变

    可变

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

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

    A:连续的不重复字节

    B:连续的重复字节

     

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

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

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

     

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

     

    Header1个字节)

    Data(变长)

     

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

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

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

     

    关于RLE代码的实现,可以参考本文后面的“程序实现”部分的代码。

     

    二、  程序实现

    好了,弄清楚了文件格式后,编写程序就不是个难事清了。为了读取方便,我定义了结构体来表示Tga文件头:

     

    //------------------------------------------------

    // Name:TgaHeader

    // Introduce:The tga header information in file

    //------------------------------------------------

    typedef struct tagTgaHeader

    {

       BYTE  ubImageInfoByteCnt;    //The image information length, in byte

       BYTE  ubColorTableExist;       //0-have not color table,1-have

       BYTE  ubImageType;              //Image type,2-uncompare RGB image,10-compare RGB image

       BYTE  ubColorTableInfo[5];    //Color table information

       BYTE  ubImageInfo[10];          //Image information

    }TgaHeader, *LPTGAHEADER;

    //------------------------------------------------

     

    1、文件头信息

     

       //Load the file header

       TgaHeader header;

       memset(&header, 0, sizeof(header));

       is.read((char*)&header, sizeof(header));

       if(header.ubImageType != 2 && header.ubImageType != 10)

                   return false;

     

    2、获取图像规格信息

     

       //Get image size

       m_iImageWidth = (header.ubImageInfo[5]<<8) + header.ubImageInfo[4];

       m_iImageHeight = (header.ubImageInfo[7]<<8) + header.ubImageInfo[6];

       m_iBitsPerPixel = header.ubImageInfo[8];

       if(         m_iImageWidth <=0 || m_iImageHeight <= 0 ||

                   (m_iBitsPerPixel != 24 && m_iBitsPerPixel != 32))

                   return false;

       m_iImageDataSize = m_iImageWidth * m_iImageHeight * m_iBitsPerPixel/8;

     

    3、读取图片信息字段

     

    //Load the image information

    char info[255] = {'/0'};

    is.read(info, header.ubImageInfoByteCnt);

     

    4、读取图片数据

    对于非压缩格式(header.ubImageType = 2),我们可以直接读取:

     

    //Load image data

    if(m_pImageData) delete []m_pImageData;

    m_pImageData = new unsigned char[m_iImageDataSize];

    is.read((char*)m_pImageData, m_iImageDataSize);

     

    对于压缩格式(header.ubImageType = 10),我们要按照RLE算法解码:

     

    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 data

                if(chunkheader & 0x80)

                {

                            int iCnt = (chunkheader & 0x7f) + 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 data

                else

                {

                            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);

     

    5、注意事项

    行对齐:

    同位图一样,如果想把tga用于GDI,需要做行对齐处理,而却Tga文件里的数据是没有行对齐的,所以加载必须程序进行行对齐。行对齐的方法如下:

     

    A:计算图片行对齐前每行的字节数:

        iRowDataLenOld = m_iImageWidth * m_iImageHeight * m_iBitsPerPixel/8;

    B:计算行对齐后的字节数:

        iRowDataLenNew= (((m_iImageWidth*m_iBitsPerPixel) + 31) >> 5) << 2;

    C:如果iRowDataLenOldiRowDataLenNew则不需要对齐

    D:否则在每行的后面添加iRowDataLenNewiRowDataLenOld个字节,并以0填充。

     

    BGR->RGB转换:

    Tga文件存储颜色数据的格式为BGR(A),如果需要得到RGB(A)格式的数据(如OpenGL中)需要做BGR->RGB转换。

  • 相关阅读:
    Comet OJ
    Comet OJ
    Comet OJ
    Comet OJ
    Codeforces Round #562 (Div. 2)
    P1202 USACO1.1 黑色星期五
    P1201 USACO1.1 贪婪的送礼者
    【线段树】HDU1166:敌兵布阵
    标准C++中的string类的用法总结(转)
    【递归】分形
  • 原文地址:https://www.cnblogs.com/xiayong123/p/3716993.html
Copyright © 2011-2022 走看看