How to use PNG's IDAT chunk?

前端 未结 3 432
鱼传尺愫
鱼传尺愫 2021-02-04 12:12

I\'m trying to understand how data are stored into IDAT chunk. I\'m writing a little PHP class and I can retrieve most of chunks information but what I get for IDAT doesn\'t mat

相关标签:
3条回答
  • 2021-02-04 12:40

    Use gzinflate but skip the first 2 bytes and the last 4 first.

    $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 讨论(0)
  • 2021-02-04 12:42
    00000000 ffffff00 ffffff00 0000xxxx
    black    white    white    black
    

    That's what I can tell (which is correct) ... but you are missing 2 bytes at the end.

    0 讨论(0)
  • 2021-02-04 12:49

    To add to @Andreas (+1) parsing, two things to note:

    1. A PNG file can have (and often has) many IDAT chunks, they must be concatenated to recover the compressed zlib stream. http://www.w3.org/TR/PNG/#10CompressionFSL

    2. Gzip/Compress/Deflate are all related but are not exactly the same. PNG uses deflate/inflate. I'd try with gzdeflate/gzinflate

    0 讨论(0)
提交回复
热议问题