于比較常用的圖片格式Png,、Jpg、Gif,、Bmp,我們需要針對不同的圖片格式使用不同的控件來顯示,,這里就有一個來解析圖片格式的問題,。我們不能單純的用文件后綴名.png、.jpg,、.jpeg,、.gif、.bmp來區(qū)分圖片格式,,因為實際上我們可以直接修改圖片后綴名,,修改后綴名并不能修改圖片的格式,圖片還是保持它原來的格式,。 圖片文件的格式結果中,,在頭部信息(一般都會在圖片文件最開始的幾個字節(jié))中都會包含圖片的格式信息。下面就列車常用的這幾種格式圖片的頭部信息標識(十六進制),。 1.Png圖片文件包括8字節(jié):89 50 4E 47 0D 0A 1A 0A,。即為 .PNG....。 2.Jpg圖片文件包括2字節(jié):FF D8,。 3.Gif圖片文件包括6字節(jié):47 49 46 38 39|37 61 ,。即為 GIF89(7)a。 4.Bmp圖片文件包括2字節(jié):42 4D,。即為 BM,。 根據(jù)圖片問題頭標識信息我們可以能很方便的判斷出文件的格式,首先我們需要獲取圖片文件的字節(jié)流信息,,代碼如下,。 Stream stream = ; StreamResourceInfo info = Application.GetResourceStream( Uri(path, UriKind.Relative)); (info != ) { stream = info.Stream; } (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication()) { stream = myIsolatedStorage.OpenFile(path, FileMode.Open, FileAccess.Read); } 從圖片文件流stream中讀取8個字節(jié),然后再根據(jù)不同的圖片格式做文件頭匹配比較具能判斷出文件的格式,,代碼如下,。 ImageType { Null, Png, Jpg, Gif, Bmp } ImageType getImageType(Stream stream) { ImageType type = ImageType.Null; [] header = []; stream.Read(header, , ); (header[] == && header[] == && header[] == && header[] == && header[] == && header[] == && header[] == && header[] == ) { type = ImageType.Png; } (header[] == && header[] == ) { type = ImageType.Jpg; } (header[] == && header[] == && header[] == && header[] == && (header[] == || header[] == ) && header[] == ) { type = ImageType.Gif; } (header[] == && header[] == ) { type = ImageType.Bmp; } stream.Close(); type; } 解析到圖片格式后,我們就可以根據(jù)圖片格式選擇對應的控件來顯示圖片了,。 PNG: 要解析Png圖片的寬度和高度信息,,首先需要了解Png圖片的數(shù)據(jù)塊結構,Png圖片的尺寸信息存放在文件頭數(shù)據(jù)塊中,所以我們需要了解文件頭的數(shù)據(jù)塊結構,。 文件頭數(shù)據(jù)塊IHDR(header chunk):它包含有PNG文件中存儲的圖像數(shù)據(jù)的基本信息,,并要作為第一個數(shù)據(jù)塊出現(xiàn)在PNG數(shù)據(jù)流中,而且一個PNG數(shù)據(jù)流中只能有一個文件頭數(shù)據(jù)塊,。 文件頭數(shù)據(jù)塊由13字節(jié)組成,,其中前8個字節(jié)即為圖片的寬度和高度信息,各占4個字節(jié),。 文件頭數(shù)據(jù)塊是第一個數(shù)據(jù)塊,,但是在數(shù)據(jù)塊前,也就是PNG圖片文件的最開始位置首先存儲的是PNG文件署名域,,占8個字節(jié),,即:89 50 4e 47 0d 0a 1a 0a ( .PNG....),通過這個可以判斷圖片是否為PNG格式,,接下來是文件頭數(shù)據(jù)塊的長度和標識,,各占4個字節(jié),文件頭數(shù)據(jù)塊的長度為13,,所以文件頭數(shù)據(jù)塊的長度固定為 00 00 00 0D,,而文件頭數(shù)據(jù)塊的標識為49 48 44 52,即“IHDR”,。 通過上面的解析,,我們可以分一下幾步來解析PNG圖片的寬度和高度。 1.首先讀取起始位的8個字節(jié),,即PNG文件署名域,,判斷圖片是否為PNG格式,如果不是,,則退出,。 [] header = []; stream.Read(header, , ); (!(header[] == && header[] == && header[] == && header[] == && header[] == && header[] == && header[] == && header[] == )) { ; } 2.然后跳過8個字節(jié),即文件頭數(shù)據(jù)塊的長度值 00 00 00 0D,,以及文件頭數(shù)據(jù)塊標識 49 48 44 52(IHDR)。 stream.Seek(, SeekOrigin.Current); 3.接下來要讀取就是圖片的寬度和高度值,,讀取8個字節(jié),,由于是按照高低位調換存儲,所以需要做高低位轉換,,轉換后通過BitConverter類直接將字節(jié)類型轉換為整數(shù)型即為圖片的尺寸,。 [] buffer = []; stream.Read(buffer, , buffer.Length); Array.Reverse(buffer, , ); Array.Reverse(buffer, , ); width_ = BitConverter.ToInt32(buffer, ); height_ = BitConverter.ToInt32(buffer, ); GIF: GIF圖片的文件格式相對比較簡單,其中寬度和高度信息存放在邏輯視屏描述塊的前4個字節(jié),,而邏輯視屏描述塊是GIF圖片的第二塊區(qū)域,,第一個區(qū)域為6個字節(jié)的頭部,頭部包括標識符和版本。下表列出到高度信息為止的各個字節(jié)的描述,。 名稱 字節(jié) 說明 頭部 標識符 3 GIF 47 49 46 版本 3 87a(89a) 38 39|37 61 邏輯視屏描述塊 寬度 2 高度 2 根據(jù)上面的格式很容易獲取圖片的高度和寬度信息,,具體代碼如下。 [] header = []; stream.Read(header, , ); (!(header[] == && header[] == && header[] == && header[] == && (header[] == || header[] == ) && header[] == )) { ; } [] buffer = []; stream.Read(buffer, , buffer.Length); width_ = BitConverter.ToInt16(buffer, ); height_ = BitConverter.ToInt16(buffer, ); BMP: 典型的位圖文件格式通常包含下面幾個數(shù)據(jù)塊: 位圖頭:保存位圖文件的總體信息,。 位圖信息:保存位圖圖像的詳細信息,。 調色板:保存所用顏色的定義。 位圖數(shù)據(jù):保存一個又一個像素的實際圖像,。 通過解析位圖頭我們可以判斷圖片是否為bmp格式,,而我們所需要的圖片尺寸信息存儲在位圖信息數(shù)據(jù)塊里。所以我們需要詳細了解位圖頭和位圖信息兩個數(shù)據(jù)塊的存儲結構,。 位圖頭,,這部分是識別信息,典型的應用程序會首先普通讀取這部分數(shù)據(jù)以確保的確是位圖文件并且沒有損壞,。 字節(jié) #0-1 保存位圖文件的標識符,,這兩個字節(jié)的典型數(shù)據(jù)是BM。 字節(jié) #2-5 使用一個dword保存位圖文件大小,。 字節(jié) #6-9 是保留部分,,留做以后的擴展使用,對實際的解碼格式?jīng)]有影響。 字節(jié) #10-13 保存位圖數(shù)據(jù)位置的地址偏移,,也就是起始地址,。 位圖信息,這部分告訴應用程序圖像的詳細信息,,在屏幕上顯示圖像將會使用這些信息,,它從文件的第15個字節(jié)開始。 字節(jié) #14-17 定義以下用來描述影像的區(qū)塊(BitmapInfoHeader)的大小,。它的值是:40 - Windows 3.2,、95、NT,、12 - OS/2 1.x,、240 - OS/2 2.x 字節(jié) #18-21 保存位圖寬度(以像素個數(shù)表示)。 字節(jié) #22-25 保存位圖高度(以像素個數(shù)表示),。 ………… 以上關于bmp文件結構的內容參考維基百科http://zh./wiki/BMP,。 通過上面對BMP圖片格式的了解,可以用下面的代碼來解析bmp圖片的尺寸大小,。 [] header = []; stream.Read(header, , ); ( !(header[] == && header[] == )) { ; } stream.Seek(, SeekOrigin.Current); [] buffer = []; stream.Read(buffer, , buffer.Length); width_ = BitConverter.ToInt32(buffer, ); height_ = BitConverter.ToInt32(buffer, ); JPG: 由于jpg圖片的格式相對于png要復雜很多,,所以首先我們要先清楚的了解jpg圖片的數(shù)據(jù)格式,jpg圖片包括SOI和數(shù)據(jù)兩個部分,。 SOI,,Start of Image,圖像開始,標記代碼 2字節(jié) 固定值0xFFD8,。 數(shù)據(jù)部分分成很多數(shù)據(jù)段,,數(shù)據(jù)段的一般結構如下。 段數(shù)據(jù)結構 名稱 字節(jié) 說明 段標識 >= 1 多于一個的0xFF 段類型 1 類型編碼(稱作“標記碼”) 段長度 2 包括段內容和段長度本身,不包括段標識和段類型 短內容 <= 65533 段類型有30種,,但只有10種是必須被所有程序識別的,,其它的類型都可以忽略。在這么多的段中,,其中JPG圖片的尺寸相關信息存儲在SOF0(圖像基本信息)段中,。所以需要詳細了解一下SOFO段的數(shù)據(jù)結構。 SOFO段結構 名稱 字節(jié) 說明 段標識 1 0XFF 段類型 1 0XCO JFIF格式的為0XC2 段長度 2 其值=8+組件數(shù)量×3 樣本精度 1 8 每個樣本位數(shù)(大多數(shù)軟件不支持12和16) 圖片高的 2 采用Motorola格式,,即:高位在前,,低位在后 圖片寬度 2 采用Motorola格式,即:高位在前,,低位在后 由于我們是為了解析JPG圖片的寬度和高度信息,,所以上表SOFO段結構只列出了到寬度為止結構信息,接下來還有其他一些圖片的相關信息,,這里就不再列出,。 根據(jù)上面對JPG圖片格式的解析,我們可以分一下幾步來解析JPG圖片的尺寸信息,。 1.讀取2個字節(jié)的SOI,,即0xFFD8,根據(jù)這兩個字節(jié)判斷圖片是否為JPG圖片,,如果不是,,則退出解析過程。 [] header = []; stream.Read(header, , ); (!(header[] == && header[] == )) { ; } 2.接下來就需要解析圖片的數(shù)據(jù)部分,,由于數(shù)據(jù)部分是有很多不同的數(shù)據(jù)段構成,,數(shù)據(jù)段擁有一些共同的特性,所以這里我們需要做一個循環(huán)來逐個遍歷查找到SOFO數(shù)據(jù)段,。 type = -; ff = -; ps = ; { { ff = stream.ReadByte(); (ff < ) { ; } } (ff != ); { type = stream.ReadByte(); } (type == ); ps = stream.Position; (type) { : : : : : : : : : : ; : : { getJpgSize(stream); ; } : ps = stream.ReadByte() * ; ps = stream.Position + ps + stream.ReadByte() - ; ; } (ps + >= stream.Length) { ; } stream.Position = ps; } (type != ); 3.找到SOFO數(shù)據(jù)段后,,就可以解析圖片的寬度和高度信息。 getJpgSize(Stream stream) { stream.Seek(, SeekOrigin.Current); height_ = stream.ReadByte() * ; height_ += stream.ReadByte(); width_ = stream.ReadByte() * ; width_ += stream.ReadByte(); } |
|
來自: 勤奮不止 > 《數(shù)據(jù)結構和算法》