程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> GDI+中GIF圖片的顯示

GDI+中GIF圖片的顯示

編輯:關於VC++

某位網友曾經問過我GDI+中Gif圖像顯示的問題,一直沒時間給你寫,在此致歉。我把這篇文章送給他。

一、GIF格式介紹

1.概述

GIF(Graphics Interchange Format,圖形交換格式)文件是由 CompuServe公司開發的圖形文件格式,版權所有,任何商業目的使用均須 CompuServe公司授權。

GIF圖象是基於顏色列表的(存儲的數據是該點的顏色對應於顏色列表的索引值),最多只支持8位(256色)。GIF文件內部分成許多存儲塊, 用來存儲多幅圖象或者是決定圖象表現行為的控制塊, 用以實現動畫和交互式應用。GIF文件還通過LZW壓縮算法壓縮圖象數據來減少圖象尺寸。

2.GIF文件存儲結構

GIF文件內部是按塊劃分的,包括控制塊( Control Block )和數據塊(Data Sub-blocks)兩種。控制塊是控制數據塊行為的,根據不同的控制塊包含一些不同的控制參數; 數據塊只包含一些8-bit的字符流,由它前面的控制塊來決定它的功能,每個數據塊大小從0到255個字節, 數據塊的第一個字節指出這個數據塊大小(字節數), 計算數據塊的大小時不包括這個字節,所以一個空的數據塊有一個字節,那就是數據塊的大小0x00。 下表是一個數據塊的結構:

BYTE 7 6 5 4 3 2 1 0 BIT 0

塊大小

Block Size - 塊大小,不包括這個這個字節(不計算塊大小自身) 1   Data Values - 塊數據,8-bit的字符串 2   ...   254   255  

一個GIF文件的結構可分為文件頭(File Header)、GIF數據流(GIF Data Stream)和文件終結器(Trailer)三個部分。文件頭包含GIF文件署名(Signature)和版本號(Version);GIF數據流由控制標識符、圖象塊(Image Block)和其他的一些擴展塊組成;文件終結器只有一個值為0x3B的字符('';'')表示文件結束。下表顯示了一個GIF文件的組成結構:

  GIF署名 文件頭     版本號   邏輯屏幕標識符 GIF數據流     全局顏色列表     ...     圖象標識符 圖象塊                         圖象局部顏色列表圖                     基於顏色列表的圖象數據       ...     GIF結尾 文件結尾  

下面就具體介紹各個部分:

文件頭部分(Header)

GIF署名(Signature)和版本號(Version)

GIF署名用來確認一個文件是否是GIF格式的文件,這一部分由三個字符組成:"GIF";文件版本號也是由三個字節組成,可以為"87a"或"89a".具體描述見下表:

BYTE 7 6 5 4 3 2 1 0 BIT 1 ''G'' GIF文件標識 2 ''I'' 3 ''F'' 4 ''8'' GIF文件版本號:87a - 1987年5月

89a - 1989年7月

5 ''7''或''9'' 6 ''a''

GIF數據流部分(GIF Data Stream)

邏輯屏幕標識符(Logical Screen Descriptor)

這一部分由7個字節組成,定義了GIF圖象的大小(Logical Screen Width & Height)、顏色深度(Color Bits)、背景色(Blackground Color Index)以及有無全局顏色列表(Global Color Table)和顏色列表的索引數(Index Count),具體描述見下表:

BYTE 7 6 5 4 3 2 1 0 BIT   1 邏輯屏幕寬度 像素數,定義GIF圖象的寬度 2 3 邏輯屏幕高度 像素數,定義GIF圖象的高度 4 5 m cr s pixel 具體描述見下... 6 背景色 背景顏色(在全局顏色列表中的索引,如果沒有全局顏色列表,該值沒有意義) 7 像素寬高比 像素寬高比(Pixel Aspect Radio)

m - 全局顏色列表標志(Global Color Table Flag),當置位時表示有全局顏色列表,pixel值有意義.

cr - 顏色深度(Color ResoluTion),cr+1確定圖象的顏色深度.

s - 分類標志(Sort Flag),如果置位表示全局顏色列表分類排列.

pixel - 全局顏色列表大小,pixel+1確定顏色列表的索引數(2的pixel+1次方).

全局顏色列表(Global Color Table)

全局顏色列表必須緊跟在邏輯屏幕標識符後面,每個顏色列表索引條目由三個字節組成,按R、G、B的順序排列。

BYTE 7 6 5 4 3 2 1 0 BIT 1 索引1的紅色值   2 索引1的綠色值   3 索引1的藍色值   4 索引2的紅色值   5 索引2的綠色值   6 索引2的藍色值   7 ...                     

圖象標識符(Image Descriptor)

~~~~~~~~~~~~~~~~~~~~~~~~~

一個GIF文件內可以包含多幅圖象,一幅圖象結束之後緊接著下是一幅圖象的標識符,圖象標識符以0x2C('','')字符開始, 定義緊接著它的圖象的性質,包括圖象相對於邏輯屏幕邊界的偏移量、圖象大小以及有無局部顏色列表和顏色列表大小, 由10個字節組成:

BYTE 7 6 5 4 3 2 1 0 BIT   1 0 0 1 0 1 1 0 0 圖象標識符開始,固定值為'','' 2 X方向偏移量 必須限定在邏輯屏幕尺寸范圍內 3 4 Y方向偏移量 5 6 圖象寬度 7 8 圖象高度 9 10 m i s r pixel m - 局部顏色列表標志(Local Color Table Flag)               置位時標識緊接在圖象標識符之後有一個局部顏色列表,供緊跟在它之後的一幅圖象使用;值否時使用全局顏色列表, 忽略pixel值。

i - 交織標志(Interlace Flag),置位時圖象數據使用交織方式排列 (詳細描述...),否則使用順序排列。

s - 分類標志(Sort Flag),如果置位表示緊跟著的局部顏色列表分類排列.

r - 保留,必須初始化為0.

pixel - 局部顏色列表大小(Size of Local Color Table),pixel+1就為顏色列表的位數

局部顏色列表(Local Color Table)

如果上面的局部顏色列表標志置位的話,則需要在這裡(緊跟在圖象標識符之後)定義一個局部顏色列表以供緊接著它的圖象使用,注 意使用前應線保存原來的顏色列表,使用結束之後回復原來保存的全局顏色列表。如果一個GIF文件即沒有提供全局顏色列表,也沒有提供局部顏色列表, 可以自己創建一個顏色列表,或使用系統的顏色列表。局部顏色列表的排列方式和全局顏色列表一樣:RGBRGB......

基於顏色列表的圖象數據(Table-Based Image Data)

由兩部分組成:LZW編碼長度(LZW Minimum Code Size)和圖象數據(Image Data)。

BYTE 7 6 5 4 3 2 1 0 BIT 1 LZW編碼長度 LZW編碼初始碼表大小的位數,詳細描述見LZW編碼...  

...

圖象數據,由一個或幾個數據塊(Data Sub-blocks)組成

數據塊

...

GIF圖象數據使用了LZW壓縮算法(詳細介紹請看後面的『LZW算法和GIF數據壓縮』),大大減小了圖象數據的大小。圖象數據在壓縮前有兩種排列格式:連續的和交織的(由圖象標識符的交織標志控制)。連續方式按從左到右、從上到下的順序排列圖象的光柵數據;交織圖象按下面的方法處理光柵數據:

創建四個通道(pass)保存數據,每個通道提取不同行的數據:

第一通道(Pass 1)提取從第0行開始每隔8行的數據;

第二通道(Pass 2)提取從第4行開始每隔8行的數據;

第三通道(Pass 3)提取從第2行開始每隔4行的數據;

第四通道(Pass 4)提取從第1行開始每隔2行的數據;

下面的例子演示了提取交織圖象數據的順序:

行  通道1   通道2   通道3   通道4    0 -------------------------------------------------------- 1         1 --------------------------------------------------------       4   2 --------------------------------------------------------     3     3 --------------------------------------------------------       4   4 --------------------------------------------------------   2       5 --------------------------------------------------------       4   6 --------------------------------------------------------     3     7 --------------------------------------------------------       4   8 -------------------------------------------------------- 1         9 --------------------------------------------------------       4   10 --------------------------------------------------------     3     11 --------------------------------------------------------       4   12 --------------------------------------------------------   2       13 --------------------------------------------------------       4   14 --------------------------------------------------------     3     15 --------------------------------------------------------       4   16 -------------------------------------------------------- 1         17 --------------------------------------------------------       4   18 --------------------------------------------------------     3     19 --------------------------------------------------------       4   20 --------------------------------------------------------   2      

圖形控制擴展(Graphic Control Extension)

這一部分是可選的(需要89a版本),可以放在一個圖象塊(圖象標識符)或文本擴展塊的前面, 用來控制跟在它後面的第一個圖象(或文本)的渲染(Render)形式,組成結構如下:

BYTE 7 6 5 4 3 2 1 0 BIT 1 擴展塊標識 Extension Introducer - 標識這是一個擴展塊,固定值0x21 2 圖形控制擴展標簽 Graphic Control Label - 標識這是一個圖形控制擴展塊,固定值0xF9 3 塊大小 Block Size - 不包括塊終結器,固定值4 4 保留 處置方法

i

t

i - 用戶輸入標志;t - 透明色標志。詳細描述見下... 5 延遲時間 Delay Time - 單位1/100秒,如果值不為1,表示暫停規定的時間後再繼續往下處理數據流 6 7 透明色索引 Transparent Color Index - 透明色索引值 8 塊終結器 Block Terminator - 標識塊終結,固定值0

處置方法(Disposal Method):指出處置圖形的方法,當值為:

0 - 不使用處置方法

1 - 不處置圖形,把圖形從當前位置移去

2 - 回復到背景色

3 - 回復到先前狀態

4-7 - 自定義

用戶輸入標志(Use Input Flag):指出是否期待用戶有輸入之後才繼續進行下去,置位表示期待,值否表示不期待。用戶輸入可以是按回車鍵、鼠標點擊等, 可以和延遲時間一起使用,在設置的延遲時間內用戶有輸入則馬上繼續進行,或者沒有輸入直到延遲時間到達而繼續

透明顏色標志(Transparent Color Flag):置位表示使用透明顏色

注釋擴展(Comment Extension)

這一部分是可選的(需要89a版本),可以用來記錄圖形、版權、描述等任何的非圖形和控制的純文本數據(7-bit ASCII字符),注釋擴展並不影響對圖象數據流的處理,解碼器完全可以忽略它。 存放位置可以是數據流的任何地方,最好不要妨礙控制和數據塊,推薦放在數據流的開始或結尾。具體組成:

BYTE 7 6 5 4 3 2 1 0 BIT 1 擴展塊標識 Extension Introducer - 標識這是一個擴展塊,固定值0x21 2 注釋塊標簽 Comment Label - 標識這是一個注釋塊,固定值0xFE  

...

Comment Data - 一個或多個數據塊(Data Sub-Blocks)組成

注釋塊

...

  塊終結器 Block Terminator - 標識注釋塊結束,固定值0

圖形文本擴展(Plain Text Extension)

這一部分是可選的(需要89a版本),用來繪制一個簡單的文本圖象,這一部分由用來繪制的純文本數據(7-bit ASCII字符)和控制繪制的參數等組成。繪制文本借助於一個文本框(Text Grid)來定義邊界,在文本框中劃分多個單元格,每個字符占用一個單元,繪制時按從左到右、從上到下的順序依次進行, 直到最後一個字符或者占滿整個文本框(之後的字符將被忽略,因此定義文本框的大小時應該注意到是否可以容納整個文本), 繪制文本的顏色索引使用全局顏色列表,沒有則可以使用一個已經保存的前一個顏色列表。另外,圖形文本擴展塊也屬於圖形塊(Graphic Rendering Block),可以在它前面定義圖形控制擴展對它的表現形式進一步修改。圖形文本擴展的組成:

BYTE 7 6 5 4 3 2 1 0 BIT 1 擴展塊標識 Extension Introducer - 標識這是一個擴展塊,固定值0x21 2 圖形控制擴展標簽 Plain Text Label - 標識這是一個圖形文本擴展塊,固定值0x01 3 塊大小 Block Size - 塊大小,固定值12 4 文本框左邊界位置 Text Glid Left Posotion - 像素值,文本框離邏輯屏幕的左邊界距離 5 6 文本框上邊界位置 Text Glid Top Posotion - 像素值,文本框離邏輯屏幕的上邊界距離 7 8 文本框高度 Text Glid Width -像素值 9 10 文本框高度 Text Glid Height - 像素值 11 12 字符單元格寬度 Character Cell Width - 像素值,單個單元格寬度 13 字符單元格高度 Character Cell Height- 像素值,單個單元格高度 14 文本前景色索引 Text Foreground Color Index - 前景色在全局顏色列表中的索引 15 文本背景色索引 Text Blackground Color Index - 背景色在全局顏色列表中的索引 N

...

Plain Text Data - 一個或多個數據塊(Data Sub-Blocks)組成,保存要在顯示的字符串。

文本數據塊

...

N+1 塊終結 Block Terminator - 標識注釋塊結束,固定值0

推薦:1.由於文本的字體(Font)和尺寸(Size)沒有定義,解碼器應該根據情況選擇最合適的;

2.如果一個字符的值小於0x20或大於0xF7,則這個字符被推薦顯示為一個空格(0x20);

3.為了兼容性,最好定義字符單元格的大小為8x8或8x16(寬度x高度)。

應用程序擴展(Application Extension)

這是提供給應用程序自己使用的(需要89a版本),應用程序可以在這裡定義自己的標識、信息等,組成:

BYTE 7 6 5 4 3 2 1 0 BIT 1 擴展塊標識 Extension Introducer - 標識這是一個擴展塊,固定值0x21 2 圖形控制擴展標簽 Application Extension Label - 標識這是一個應用程序擴展塊,固定值0xFF 3 塊大小 Block Size - 塊大小,固定值11 4 應用程序標識符 Application Identifier - 用來鑒別應用程序自身的標識(8個連續ASCII字符) 5 6 7 8 9 10 11 12 應用程序鑒別碼 Application Authentication Code - 應用程序定義的特殊標識碼(3個連續ASCII字符) 13 14 N

...

應用程序自定義數據塊 - 一個或多個數據塊(Data Sub-Blocks)組成,保存應用程序自己定義的數據

應用程序數據

...

N+1 塊終結器 lock Terminator - 標識注釋塊結束,固定值0

文件結尾部分

文件終結器(Trailer)

這一部分只有一個值為0的字節,標識一個GIF文件結束.

BYTE 7 6 5 4 3 2 1 0   1

文件終結

GIF Trailer - 標識GIF文件結束,固定值0x3B

二、在GDI+中繪制GIF

GDI+中繪制一個圖片的代碼如下:

void CMyWnd::OnPaint()
   {
     CPaintDC dc(this);
     Graphics graphics(&dc); // Create a GDI+ graphics object

     Image image(L"Test.Gif"); // Construct an image
     graphics.DrawImage(&image, 0, 0, image.GetWidth(), image.GetHeight());
   }

Gif分為兩種,一種是靜態的,對於這種格式的Gif,在GDI+中無需采用任何方法就能夠直接顯示(上面的代碼就屬於這種情況)。另一種是動態的, 這種文件能夠顯示簡單的動畫。動態的實際上由多幅靜態的組成,在顯示Gif時,每幅圖片按照一定的時間間隔依次進行顯示,從而實現了動畫效果。

我把GIF封裝成了一個類ImageEx,這個類繼承了GDI+中的Image類。我們首先要做的工作是判斷GIF是動態的還是靜態的。

bool ImageEx::TestForAnimatedGIF()
{
      UINT count = 0;
      count = GetFrameDimensionsCount();
      GUID* pDimensionIDs = new GUID[count];

      // 得到子幀的對象列表
      GetFrameDimensionsList(pDimensionIDs, count);

    //獲取總幀數
    m_nFrameCount = GetFrameCount(&pDimensionIDs[0]);

    // 假設圖像具有屬性條目 PropertyItemEquipMake.
    // 獲取此條目的大小.
    int nSize = GetPropertyItemSize(PropertyTagFrameDelay);

    // 為屬性條目分配空間.
    m_pPropertyItem = (PropertyItem*) malloc(nSize);
    GetPropertyItem(PropertyTagFrameDelay, nSize, m_pPropertyItem);
    delete pDimensionIDs;
    return m_nFrameCount > 1;

}

m_pPropertyItem->value 是一個長整形數組, 每個長整形代表每幀的延時。由於獲取的屬性不同,GetPropertyItem會獲得不同大小的對象, 因此要由用戶來獲得的對象大小,開辟與刪除 GetPropertyItem相關的內存。對象的大小是通過GetPropertyItemSize 獲取的,其參數是你所感興趣的屬性條目。 一旦獲取了幀的數量與延時,我們就可生成一個線程來調用 DrawFrameGIF()來顯示。

bool ImageEx::DrawFrameGIF()
    {
    ::WaitForSingleObject(m_hPause, INFINITE);
    GUID pageGuid = FrameDimensionTime;
    long hmWidth = GetWidth();
    long hmHeight = GetHeight();
      HDC hDC = GetDC(m_hWnd);
      if (hDC)
      {
      Graphics graphics(hDC);
      graphics.DrawImage(this, m_rc.left, m_rc.top, hmWidth, hmHeight);
      ReleaseDC(m_hWnd, hDC);
      }
      SelectActiveFrame(&pageGuid, m_nFramePosition++);
      if (m_nFramePosition == m_nFrameCount)
      m_nFramePosition = 0;

      long lPause = ((long*) m_pPropertyItem->value)[m_nFramePosition] * 10;
      DWORD dwErr = WaitForSingleObject(m_hExitEvent, lPause);
      return dwErr == WAIT_OBJECT_0;
      }

三、效果圖

圖一 效果

四、結束語

本人無償提供繪圖軟件與數據庫軟件技術咨詢。有償提供算法,控件,組件,繪圖軟件與數據庫軟件的開發。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
欄目導航
Copyright © 程式師世界 All Rights Reserved