[原]BMP位图 转换 透明 TGA图像 - 增加 alpha通道 -TGA文件格式初解

    技术2022-05-12  11

    最近做一个将文字转换为图片做视频叠加字幕的软件,大家都知道VB的图像控件Picture Box只能输出BMP格式位图,自然我的软件是用Picture Box做图像输出的也不例外,但是客户要求要输出带alpha通道的TGA格式,以方便视频制作软件去除背景色实现透明,找了一些资料,都没见到与VB相关的说明更不用说代码了,也想过借助第3方软件用命令行方式来转换,但是找到转换软件一看,都不支持透明色处理,没办法,最后只好参照支离破碎图像格式资料,又结合Photoshop输出的透明与不透明类型TGA图片进行对比,基本参悟出了最常见的“图像类型”为 02 的TGA格式图像文件结构。TGA格式的图像类型在用的有10几种,难得一一破解了,我偷懒,挑了最常见的未压缩真彩格式来解读。TGA文件格式说明大家可以随处搜索到,我也不做详解,大家一看代码便知,主要是针对位图添加alpha通道实现背景透明色这个问题做点简单的探讨。以下是最常见的TGA格式文件结构VB定义,后面的程序员目录之类的信息多余,略定义。

    Public  Type TgaFileHeaderInfo    taID_Length  As   Byte            ' 载入文件头的TGA图像信息偏移量     taPalType   As   Byte          ' 调色板类型     taImageType     As   Byte        ' 图像类型 02 未压缩的,rgb 图像      ' -----------------------------------------------     taPalFirstNdx     As   Integer        ' 调色板索引     taPalLength   As   Integer          ' 调色板长度     taPalBits    As   Byte         ' 调色板颜色数      ' --------------------------------------------------------     taLeft  As   Integer     taBottom  As   Integer     taWidth    As   Integer         ' 宽度     taHeight  As   Integer           ' 高度     taBits   As   Byte            ' 图象颜色数     TgaDescriptor  As   Byte End  Type Public  Type TGAfiles    tgaH  As  TgaFileHeaderInfo    TgaStream()  As   Byte End  Type

    和其他位图文件一样。TGA有标准的文件头,后跟图像数据区,最后面可以留文件脚注信息,Photoshop就会在文件尾部留“TRUEVISION-XFILE”这样一串字符,脚注也是有格式要求的,我嫌多余,不要脚注也不影响图像显示,就省略了。当然,大家有兴趣的可以参照Photoshop留的信息整一个个性化的文件脚注出来。至于图像数据区,TGA在未采用压缩时图像数据区和BMP格式的图像数据区是一样的格式。所以,24位BMP格式文件不加透明通道转换为TGA格式是最简单的:只要将BMP文件的文件头中图像宽高信息和颜色数3个值拿来TGA文件头既可,数据区都不用进行任何操作,直接保存既可。大多数BMP格式转TGA格式的“图像格式转换专家”之类的软件就是这么做的,这种软件要之何用?如果不能添加alpha通道的话,我何苦要转换为TGA格式?无奈之余决心自己写一个能将指定色做alpha通道透明处理的BMP to TGA图像转换模块出来。首先看下所谓的alpha通道是怎么回事,在24位的BMP图像中。一个象素的VB定义如下:

    Public  Type RGBdate    rgbBlue  As   Byte    '  指定蓝色强度     rgbGreen  As   Byte   '  指定绿色强度     rgbRed  As   Byte   '  指定红色强度 End  Type

    如果TGA格式图像不添加alpha通道的时候也是上述形式的象素定义,而增加了alpha通道后的TGA图像中,一个象素的VB定义就变为:

    Public  Type RGBA    rgbBlue  As   Byte    '  指定蓝色强度     rgbGreen  As   Byte   '  指定绿色强度     rgbRed  As   Byte   '  指定红色强度     alpha  As   Byte   ' 透明通道 End  Type

    这样的话,一个象素就变为了32位的,文件尺寸也就相应增大了。这样的对比不难看出格式转换程序的思路了。即以象素为单位,先将对应RGB数据传递过来,之后再判断该象素的颜色是否是指定的透明色,或者先判断是否透明色也可以,根据判断结果,来置alpha的值,透明色的话,alpha值置为0,其他颜色的话置为255(&HFF&)(为了便于理解,暂不考虑置半透明值&H80之类的值),这样转换完成后得到新的图像数据区就是具备了alpha通道支持透明处理的TGA图像了。

    下面是整个转换函数的代码(其中含多余的变量定义),BMP文件相关内容参见我的其他文章:

    Private   Sub  TrenBMtoTGA(BMfilePath  As   String , TGAfilePath  As   String , BColor  As   Long )     Dim  freefn  As   Integer      Dim  colorN  As   Long      Dim  i  As   Long , j  As   Long , k  As   Long , l  As   Long , m  As   Long , tmp( 0   To   2 As   Byte      Dim  BmPix()  As   Byte , PixStr  As   String , BmPix555()  As   Long      Dim  bitC1  As   Integer      ' 每个字节包含的象素数量0 to -1      Dim  bitC2  As   Integer      ' 每个象素所占位数      Dim  x  As   Long , hPal  As   Long      Dim  BMLineBytes  As   Long , TGALineBytes  As   Long      Dim  Bmfd  As  bmpfile Dim  TGAfd  As  TGAfiles ' -----------------------------     freefn  =  FreeFile    Open BMfilePath  For  Binary  As  #freefn   ' 以二进制方式度文件          Get  #freefn, , Bmfd.bmHead         Get  #freefn, , Bmfd.bmInfo.bmiHeader        colorN  =  pColor(Bmfd.bmHead.bfOffBits, Bmfd.bmInfo.bmiHeader.biSize)         If  colorN  <   0   Then              ' ---------文件损坏,拒绝读入             Close #freefn             MsgBox   " 文件损坏,拒绝读入 " , vbCritical,  " Error! "              Exit   Sub          ElseIf  colorN  >   0   Then             ' --------有色彩表,并进行定义              ReDim  Bmfd.bmInfo.bmiColors(colorN)             Get  #freefn, , Bmfd.bmInfo.bmiColors         Else               ' --------无色彩表          End   If          ' --------------------------计算每行字节数         BMLineBytes  =  ((Bmfd.bmInfo.bmiHeader.biWidth  *  Bmfd.bmInfo.bmiHeader.biBitCount  +   31 And   & HFFFFFFE0)    8          ReDim  Bmfd.bmDate(BMLineBytes  -   1 , Bmfd.bmInfo.bmiHeader.biHeight  -   1 ' (x,y)(列,行)          Get  #freefn, , Bmfd.bmDate    Close #freefn ' ----------------------------转换TGA格式      With  TGAfd.tgaH    .taID_Length  =   0     .taPalType  =   0     .taImageType  =   2         .taPalFirstNdx  =   0     .taPalLength  =   0     .taPalBits  =   0         .taLeft  =   0     .taBottom  =   0     .taWidth  =  Bmfd.bmInfo.bmiHeader.biWidth    .taHeight  =  Bmfd.bmInfo.bmiHeader.biHeight    .taBits  =   32   ' 32位色     .TgaDescriptor  =   8    ' 图像象素存储顺序(上下左右),00001000,同BMP图像数据区默认存储方式。      End   With      ' -------------添加图像Alpha通道     TGALineBytes  =  ((Bmfd.bmInfo.bmiHeader.biWidth  *  TGAfd.tgaH.taBits  +   31 And   & HFFFFFFE0)    8      ReDim  TGAfd.TgaStream(TGALineBytes  -   1 , TGAfd.tgaH.taHeight  -   1 As   Byte      For  i  =   0   To  TGAfd.tgaH.taHeight  -   1          For  j  =   0   To  BMLineBytes  /   3   -   1              For  k  =   0   To   2                 m  =  j  *   3   +  k                l  =  m  +  j                TGAfd.TgaStream(l, i)  =  Bmfd.bmDate(m, i)                tmp(k)  =  Bmfd.bmDate(m, i)             Next  k             If   RGB (tmp( 2 ), tmp( 1 ), tmp( 0 ))  <>  BColor  Then   ' 处理透明色置Alpha值                 TGAfd.TgaStream(l  +   1 , i)  =   & HFF &              End   If                      Next  j        DoEvents     Next  i    ' ------------------------------------     freefn  =  FreeFile    Open TGAfilePath  For  Binary  As  #freefn        Put #freefn, , TGAfd.tgaH        Put #freefn, , TGAfd.TgaStream    Close #freefn  ' ------------------------------------ End Sub

    代码中最后保存数据时为什么不直接“ Put #freefn, , TGAfd ”而要分开单独保存头和数据区呢?大家可以用“ Put #freefn, , TGAfd ”看看保存的结果再问。上面的代码中只是针对最常见的24位位图数据的转换处理,并且不涉及半透明效果,只要是让大家明白转换的原理,而TGA格式的图像类型还有很多种,这只是其中微不足道的一种,所以请高手不要见笑。上面转换时的代码看起来比较费时间,经测试在我2.4GHz CPU配置的机器上720*576尺寸的BMP图像文件转换时间约为0.125秒,还算可以接受。

    转载请注明出处!多谢!

    '-------------------------------------------------------------------------------------------------------------

    补充BMP文件格式定义:

    Option   Explicit ' -BMP 文件格式读写模块 ' -BMP文件格式定义 ' --------------一些常量,固定定义图像信息等。 ' -----说明文件的类型.(该值必需是0x4D42,也就是字符'BM'。我们不需要判断OS/2的位图标识,这么做现在来看似乎已经没有什么意义了,而且如果要支持OS/2的位图,程序将变得很繁琐。所以,在此只建议你检察'BM'标识) ' 文件标识 ' bfType As String * 2 ' Integer   'UINT,文件标识 Public   Const  bfTypeBM  As   String   *   2   =   " BM "   '  : Windows 3.1x, 95, NT, ..." Public   Const  bfTypeBA  As   String   *   2   =   " BA "   '  :OS/2 Bitmap Array Public   Const  bfTypeCI  As   String   *   2   =   " CI "   '  :OS/2 Color Icon Public   Const  bfTypeCP  As   String   *   2   =   " CP "   '  :OS/2 Color Pointer Public   Const  bfTypeIC  As   String   *   2   =   " IC "   '  : OS/2 Icon Public   Const  bfTypePT  As   String   *   2   =   " PT "   '  :OS/2 Pointer ' 位图信息头(Bitmap Info Header)的长度,用来描述位图的颜色、压缩方法等。下面的长度表示: ' biSize As Long 'DWORD ;Bitmap Header Size Public   Const  biSizeWin  As   Long   =   & H28  ' - Windows 3.1x, 95, NT, ... Public   Const  biSizeOS21x  As   Long   =   & HC  '  - OS/2 1.x Public   Const  biSizeOS22x  As   Long   =   & HF0  '  - OS/2 2.x ' Bits Per Pixel 1 word 每个象素的位数 ' biBitCount  As Integer ';WORD;Bits Per Pixel,每个象素的位数 Public   Const  biBitCount1c  As   Integer   =   1   ' 1 - 单色位图(实际上可有两种颜色,缺省情况下是黑色和白色。你可以自己定义这两种颜色) Public   Const  biBitCount16c  As   Integer   =   4   '  4 - 16 色位图 Public   Const  biBitCount256c  As   Integer   =   8   ' 8 - 256 色位图 Public   Const  biBitCount16bit  As   Integer   =   16   ' 16 - 16bit 高彩色位图 Public   Const  biBitCount24bit  As   Integer   =   24   ' 24 - 24bit 真彩色位图 Public   Const  biBitCount32bit  As   Integer   =   32   ' 32 - 32bit 增强型真彩色位图   ' 001Eh Compression 1 dword 压缩说明: ' biCompression  As Long ';DWORD,Compression,压缩说明: Public   Const  biCBI_RGB   As   Long   =   0   ' 0 - 不压缩 (使用BI_RGB表示) Public   Const  biCBI_RLE8   As   Long   =   1   ' 1 - RLE 8-使用8位RLE压缩方式(用BI_RLE8表示) Public   Const  biCBI_RLE4   As   Long   =   2   ' 2 - RLE 4-使用4位RLE压缩方式(用BI_RLE4表示) Public   Const  biCBI_BITFIELDS  As   Long   =   3   ' 3 - Bitfields-位域存放方式(用BI_BITFIELDS表示) ' -------位图文件头,BITMAPFILEHEADER ' 位图文件头包含有关于文件类型、文件大小、存放位置等信息 Public  Type bmfh    bfType  As   String   *   2   '  Integer   'UINT,文件标识     bfSize  As   Long     ' DWORD File Size     bfReserved1  As   Integer   ' UINT Reserved     bfReserved2  As   Integer    ' UINT Reserved     bfOffBits  As   Long   ' DWORD Bitmap Data Offset End  Type ' --------------------位图信息头,BITMAPINFOHEADER ' 说明BITMAPINFOHEADER结构,其中包含了有关位图的尺寸及位格式等信息 Public  Type bmih     ' BITMAPINFOHEADER ,bmiHeader;     biSize  As   Long   ' DWORD ;Bitmap Header Size     biWidth  As   Long   ' LONG ;Width 1 dword 位图的宽度,以象素为单位     biHeight  As   Long   ' LONG ;Height 1 dword 位图的高度,以象素为单位     biPlanes  As   Integer   ' WORD ;Planes 1 word 位图的位面数(注:该值将总是1)     biBitCount   As   Integer   ' ;WORD;Bits Per Pixel,每个象素的位数     biCompression   As   Long   ' ;DWORD,Compression,压缩说明:     biSizeImage   As   Long   ' ;DWORD;Bitmap Data Size 1 dword 用字节数表示的位图数据的大小。该数必须是4的倍数     biXPelsPerMeter   As   Long   ' ;LONG;HResolution 1 dword 用象素/米表示的水平分辨率     biYPelsPerMeter   As   Long   ' ;LONG;VResolution 1 dword 用象素/米表示的垂直分辨率     biClrUsed   As   Long   ' ;DWORD;Colors 1 dword 位图使用的颜色数。如8-比特/象素表示为100h或者 256.     biClrImportant  As   Long   ' ;DWORD;Important Colors 1 dword 指定重要的颜色数。当该域的值等于颜色数时(或者等于0时),表示所有颜色都一样重要 End  Type ' -----------------色彩表 ' 说明彩色表RGBQUAD结构的阵列,其中包含索引图像的真实RGB值。 Public  Type rgbq     ' RGBQUAD, bmiColors[1];     rgbBlue  As   Byte    '  指定蓝色强度     rgbGreen  As   Byte   '  指定绿色强度     rgbRed  As   Byte   '  指定红色强度     rgbReserved  As   Byte   '  保留,设置为0 End  Type ' -----------------位图信息,BITMAPINFO Public  Type bmi    bmiHeader   As  bmih  ' ;BITMAPINFOHEADER     bmiColors()  As  rgbq   ' RGBQUAD ; End  Type  ' -------------------------文件构成 Public  Type bmpfile    bmHead  As  bmfh   ' 位图文件头     bmInfo  As  bmi    ' 位图信息     bmDate()  As   Byte      ' 图象数据,位图颜色数据 End  Type ' ------------------------转换16Bit颜色数据 Public  Type RGBdate    rgbBlue  As   Byte    '  指定蓝色强度     rgbGreen  As   Byte   '  指定绿色强度     rgbRed  As   Byte   '  指定红色强度 End  Type ' Public LineBytes As Long Declare  Function  SetDIBitsToDevice Lib  " gdi32 "  (ByVal HDC  As   Long , ByVal X  As   Long , ByVal Y  As   Long , ByVal dx  As   Long , ByVal dy  As   Long , ByVal SrcX  As   Long , ByVal SrcY  As   Long , ByVal Scan  As   Long , ByVal NumScans  As   Long , Bits  As  Any, BitsInfo  As  bmi, ByVal wUsage  As   Long As   Long Declare  Function  GlobalLock Lib  " kernel32 "  (ByVal hMem  As   Long As   Long Declare  Function  GlobalUnlock Lib  " kernel32 "  (ByVal hMem  As   Long As   Long Declare  Function  GlobalFree Lib  " kernel32 "  (ByVal hMem  As   Long As   Long ' -----------计算色彩表位置和数量 Public   Function  pColor(bfOffBits  As   Long , biSize  As   Long As   Long Dim  PclC  As   Long     PclC  =  bfOffBits  -  biSize  -   & HE     If  PclC  >   0   Then          ' --------有色板          If   Int (PclC  /   4 *   4   <>  PclC  Then             pColor  =   - 1          Else              If  PclC  <   8   Then                 pColor  =   - 1              Else                 pColor  =  (PclC  /   4 -   1              End   If          End   If      ElseIf  PclC  <   0   Then          ' -------坏文件         pColor  =   - 1      Else          ' --------无色板         pColor  =  PclC     End   If End Function

    '====================================================================================

    先前发的转换函数有严重BUG,即:将TGA行字节数量也按照BMP格式的做了DWORD对齐为4的整数倍,可是在TGA格式中,行字节数量是不用做对齐调整的。这个BUG在BMP行实际象素字节数不是4的整数倍时就会出现。今天转换一张291象素宽度的BMP图片时发现的这个BUG,现已修复,并将修复代码贴在后面跟上(之所以保留以前有错误的代码是为了让大家能理解到BUG出现在哪里):

    Private   Sub  TrenBMtoTGA(BMfilePath  As   String , TGAfilePath  As   String , TBackColor  As   Long )     Dim  freefn  As   Integer      Dim  colorN  As   Long      Dim  i  As   Long , j  As   Long , k  As   Long , l  As   Long , m  As   Long , tmp( 0   To   2 As   Byte      Dim  BmPix()  As   Byte , PixStr  As   String , BmPix555()  As   Long      Dim  bitC1  As   Integer      ' 每个字节包含的象素数量0 to -1      Dim  bitC2  As   Integer      ' 每个象素所占位数      Dim  x  As   Long , hPal  As   Long      Dim  BMLineBytes  As   Long , TGALineBytes  As   Long , BMTrueLineBytes  As   Long , BMpicByte  As   Integer      Dim  Bmfd  As  bmpfile Dim  TGAfd  As  TGAfiles ' -----------------------------     freefn  =  FreeFile    Open BMfilePath  For  Binary  As  #freefn   ' 以二进制方式读文件          Get  #freefn, , Bmfd.bmHead         Get  #freefn, , Bmfd.bmInfo.bmiHeader        colorN  =  pColor(Bmfd.bmHead.bfOffBits, Bmfd.bmInfo.bmiHeader.biSize)         If  colorN  <   0   Then              ' ---------文件损坏,拒绝读入             Close #freefn             MsgBox   " 文件损坏,拒绝读入 " , vbCritical,  " Error! "              Exit   Sub          ElseIf  colorN  >   0   Then             ' --------有色彩表,并进行定义              ReDim  Bmfd.bmInfo.bmiColors(colorN)             Get  #freefn, , Bmfd.bmInfo.bmiColors         Else               ' --------无色彩表          End   If          ' --------------------------计算每行字节数         BMLineBytes  =  ((Bmfd.bmInfo.bmiHeader.biWidth  *  Bmfd.bmInfo.bmiHeader.biBitCount  +   31 And   & HFFFFFFE0)    8   ' 每一扫描行的字节数必需是4的整倍数,也就是DWORD对齐的         BMpicByte  =  Bmfd.bmInfo.bmiHeader.biBitCount  /   8         BMTrueLineBytes  =  Bmfd.bmInfo.bmiHeader.biWidth  *  BMpicByte         ReDim  Bmfd.bmDate(BMLineBytes  -   1 , Bmfd.bmInfo.bmiHeader.biHeight  -   1 ' (x,y)(列,行)          ' --------------直接加载              Get  #freefn, , Bmfd.bmDate    Close #freefn ' ----------------------------转换TGA格式      With  TGAfd.tgaH    .taID_Length  =   0     .taPalType  =   0     .taImageType  =   2         .taPalFirstNdx  =   0     .taPalLength  =   0     .taPalBits  =   0         .taLeft  =   0     .taBottom  =   0     .taWidth  =  Bmfd.bmInfo.bmiHeader.biWidth    .taHeight  =  Bmfd.bmInfo.bmiHeader.biHeight    .taBits  =   32     .TgaDescriptor  =   8      End   With      ' -------------添加图像Alphi通道     TGALineBytes  =  Bmfd.bmInfo.bmiHeader.biWidth  *  (TGAfd.tgaH.taBits  /   8 ' TGA格式不用行DWORD对齐      ReDim  TGAfd.TgaStream(TGALineBytes  -   1 , TGAfd.tgaH.taHeight  -   1 As   Byte '     Dim t1 As Single, t2 As Single '     t1 = Timer      For  i  =   0   To  TGAfd.tgaH.taHeight  -   1          For  j  =   0   To  BMTrueLineBytes  /  BMpicByte  -   1   ' 根据实际象素来传递数据              For  k  =   0   To   2                 m  =  j  *   3   +  k                l  =  m  +  j                TGAfd.TgaStream(l, i)  =  Bmfd.bmDate(m, i)                tmp(k)  =  Bmfd.bmDate(m, i)             Next  k             If   RGB (tmp( 2 ), tmp( 1 ), tmp( 0 ))  <>  TBackColor  Then                 TGAfd.TgaStream(l  +   1 , i)  =   & HFF &              End   If          Next  j        DoEvents     Next  i '     t2 = Timer '     MsgBox Trim(Str(t2 - t1)) + "秒"     ' ------------------------------------     freefn  =  FreeFile    Open TGAfilePath  For  Output  As  #freefn  ' 清空文件     Close #freefn    freefn  =  FreeFile    Open TGAfilePath  For  Binary  As  #freefn        Put #freefn, , TGAfd.tgaH        Put #freefn, , TGAfd.TgaStream    Close #freefn  ' ------------------------------------ End Sub

    最新回复(0)