2011-09-03 35 views
7

我想了解數據如何存儲到IDAT塊中。我正在寫一個PHP類,我可以檢索大部分塊信息,但是我得到的IDAT與我的圖像的像素不匹配:如何使用PNG的IDAT塊?

enter image description here它是2×2px具有alpha(bitdepth 8)的truecolour。

但當我解釋IDAT的數據是這樣的:

current(unpack('H*',gzuncompress($idat_data))); 

我得到

00000000ffffff00ffffff000000

我不明白它如何能匹配像素。或者是我的代碼破壞數據?

感謝您的幫助!

編輯:我得到

08d705c101010000008010ff4f1750a93029e405fb

爲十六進制的壓縮數據,這樣看來我解壓後虧幾個字節。

enter image description here

回答

7

使用gzinflate嘗試,但跳過第2個字節和最後4首。

$contents = file_get_contents($in_filename); 
$pos = 8; // skip header 

$color_types = array('Greyscale','unknown','Truecolour','Indexed-color','Greyscale with alpha','unknown','Truecolor with alpha'); 
$len = strlen($contents); 
$safety = 1000; 
do { 
    list($unused,$chunk_len) = unpack('N', substr($contents,$pos,4)); 

    $chunk_type = substr($contents,$pos+4,4); 

    $chunk_data = substr($contents,$pos+8,$chunk_len); 

    list($unused,$chunk_crc) = unpack('N', substr($contents,$pos+8+$chunk_len,4)); 
    echo "chunk length:$chunk_len(dec) 0x" . sprintf('%08x',$chunk_len) . "h<br>\n"; 
    echo "chunk crc :0x" . sprintf('%08x',$chunk_crc) . "h<br>\n"; 
    echo "chunk type :$chunk_type<br>\n"; 
    echo "chunk data $chunk_type bytes:<br>\n" . chunk_split(bin2hex($chunk_data)) . "<br>\n"; 
    switch($chunk_type) { 
     case 'IHDR': 
     list($unused,$width,$height) = unpack('N2', substr($chunk_data,0,8)); 
     list($unused,$depth,$Color_type,$Compression_method,$Filter_method,$Interlace_method) = unpack('C*', substr($chunk_data,8)); 
     echo "Width:$width,Height:$height,depth:$depth,Color_type:$Color_type(" . $color_types[$Color_type] . "),Compression_method:$Compression_method,Filter_method:$Filter_method,Interlace_method:$Interlace_method<br>\n"; 
     $bytes_per_pixel = $depth/8; 
     break; 

     case 'PLTE': 
     $palette = array(); 
     for($i=0;$i<$chunk_len;$i+=3) { 
      $tupl = bin2hex(substr($chunk_data,$i,3)); 
      $palette[] = $tupl; 
      if($i && ($i % 30 == 0)) { 
       echo "<br>\n"; 
      } 
      echo '<span style="color:' . $tupl . ';">[' . $tupl . ']</span>'; 
     } 
     echo print_r($palette,true) . "<br>"; 
     break; 

     case 'IDAT': 
     $compressed = substr($chunk_data,2,$chunk_len - 6); // 2 bytes on the front and 4 at the end 
     $decompressed = gzinflate($compressed); 
     echo "decompressed chunk data " . strlen($decompressed) . " bytes:<br>\n" . chunk_split(bin2hex($decompressed),2 + $width * $bytes_per_pixel * 2) . "<br>\n"; 
     for($row=0; $row<$height; $row++) { 
      for($col=1; $col<=$width; $col++) { 
       $index = (int)substr($decompressed,((int)$row*($width+1)+$col),1); 
       echo '<span style="color:' . $palette[$index] . ';">' . $index . '</span>'; 
      } 
      echo "<br>\n"; 
     } 
     // TODO use filters described here: 
     // http://www.w3.org/TR/PNG/#9Filters 
     // first byte of scan line is filter type 
     break; 

    } 
    $pos += $chunk_len + 12; 
    echo "<hr>"; 
} while(($pos < $len) && --$safety); 
+0

謝謝,現在膨脹工作,但我得到「00000000ffffff00ffffff000000 「(14個字節),它們如何用於獲取像素? – MatTheCat

+0

爲了獲得良好的壓縮效果,PNG格式在壓縮之前應用濾鏡。這些濾波器的作用如下:如果兩條掃描線彼此幾乎相同,則較低線上與上述像素相匹配的像素將變爲零。所以當你完成你有一個零碎的零和壓縮是非常好的。所以你需要反轉,並在解壓縮後撤銷過濾器。見http://www.w3.org/TR/PNG/#9過濾器 – Charlie

+0

正確,過濾器「將掃描行中的字節序列轉換爲以過濾器類型字節開頭的等長字節序列」。所以我不應該有18個字節的未壓縮數據(1「字節深度」* 4通道* 4像素+ 2個濾波器)? – MatTheCat

4
00000000 ffffff00 ffffff00 0000xxxx 
black white white black 

這就是我能告訴什麼(這是正確的)......但你缺少在最後2個字節。

+1

我認爲每個掃描線都有一個過濾器類型的字節?丟失的字節可能來自不良的解壓縮算法? – MatTheCat

+0

我並沒有真正閱讀過PNG,但你所呈現的數據似乎與你應該得到的數據相對應,除非它不是全部......所以我個人無法幫你解釋爲什麼這可能會發生:http://www.w3.org/TR/PNG/#11IDAT @leonbloy可能是正確的關於多個IDAT塊,但我覺得很奇怪,一個如此之小的塊將被拆分...是你確定你正在解壓所有的字節? – Andreas

+0

感謝但http://www.w3.org/TR/PNG/#4Concepts.EncodingFiltering它似乎過濾器類型應該在數據中存在,所以更多的字節將被丟失? (我知道在我的情況下只有一個IDAT塊)^^) – MatTheCat

3

爲了增加@Andreas(+1)的解析,有兩兩件事需要注意:

  1. PNG文件可以(而且經常有)很多IDAT塊,它們必須被連接起來以恢復壓縮的zlib流。 http://www.w3.org/TR/PNG/#10CompressionFSL

  2. Gzip/Compress/Deflate都是相關的,但並不完全相同。 PNG使用deflate/inflate。我會跟gzdeflate/gzinflate

+0

我試過了,但是在使用gzinflate = /時(我的圖像只有一個IDAT塊),我得到一個數據錯誤 – MatTheCat

+0

@MatTheCat我已經使用Java Deflater/Infalter類實現了PNG讀取/寫入,它的工作完美無瑕。也許你會嘗試剝離前兩個字節? 'gzinflate(substr($ idat_data,2)'?http://www.php.net/manual/en/function.gzinflate.php#70875 – leonbloy

+0

我剛注意到IDAT塊的長度部分小於它的數據長度,我認爲這是問題的來源,但我無法猜測爲什麼 – MatTheCat