2011-09-03 14 views
7

Sto cercando di capire come i dati vengono archiviati nel blocco IDAT. Sto scrivendo una piccola classe PHP e posso recuperare la maggior parte delle informazioni sui blocchi ma quello che ottengo per IDAT non corrisponde ai pixel dell'immagine:Come usare il pezzo IDAT di PNG?

enter image description here È 2 × 2px truecolour con alfa (bitdepth 8).

Ma quando interpreto dati IDAT come questo:

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

ottengo

00000000ffffff00ffffff000000

Non capisco come possa corrispondere pixel. O è il mio codice che corrompe i dati?

Grazie per il vostro aiuto!

EDIT: ottengo

08d705c101010000008010ff4f1750a93029e405fb

come dati esadecimali compressa, così sembra che la perdita diversi byte dopo la decompressione.

enter image description here

risposta

7

Usa gzinflate ma saltare i primi 2 byte e l'ultimo 4 prima.

$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

grazie, gonfia funziona ora ma ottengo "00000000ffffff00ffffff000000" "(14 byte), come sono usati per ottenere i pixel? – MatTheCat

+0

Per ottenere una buona compressione, il formato PNG applica i filtri prima della compressione. I filtri fanno cose come: se due linee di scansione sovrapposte sono quasi le stesse, i pixel sulla riga inferiore che corrispondono al pixel sopra, vengono cambiati in zeri. Quindi, quando hai finito, hai uno schifo di zeri e la compressione è davvero buona. Quindi è necessario invertirlo e * annullare * i filtri dopo la decompressione. vedi http://www.w3.org/TR/PNG/#9Filters – Charlie

+0

A destra, e il filtro "trasforma la sequenza di byte in una scanline in una sequenza di lunghezza uguale di byte preceduta da un byte del tipo di filtro". Quindi non dovrei avere 18 byte di dati non compressi (1 "bytedepth" * 4 canali * 4 pixel + 2 filtri)? – MatTheCat

4
00000000 ffffff00 ffffff00 0000xxxx 
black white white black 

Questo è quello che posso dire (che è corretto) ... ma non trovi il 2 byte alla fine.

+1

Ho pensato che c'era un tipo di filtro byte in ogni scanline? I byte mancanti potrebbero provenire da un algoritmo di decompressione errato? – MatTheCat

+0

Non sono affatto letto su PNG, ma i dati che presenti sembrano corrispondere a quello che dovresti ottenere, tranne che non è tutto ... quindi personalmente non posso aiutarti con il perché che potrebbe accadere: http://www.w3.org/TR/PNG/#11IDAT @leonbloy potrebbe avere ragione su più blocchi IDAT, ma trovo strano che un blocco così piccolo venga scisso ... sicuro di non decomprimere tutti i byte? – Andreas

+0

Grazie ma http://www.w3.org/TR/PNG/#4Concepts.EncodingFiltering sembra che i tipi di filtro siano presenti nei dati, quindi mancano più byte ?? (So ​​che c'è un solo pezzo IDAT nel mio caso ^^) – MatTheCat

3

Per aggiungere a @Andreas (+1) analisi, due cose da notare:

  1. un file PNG può avere (e spesso ha) molti pezzi IDAT, devono essere concatenati per recuperare lo zlib compresso ruscello. http://www.w3.org/TR/PNG/#10CompressionFSL

  2. Gzip/Compress/Deflate sono tutti correlati ma non sono esattamente uguali. PNG utilizza deflate/inflate. Mi piacerebbe provare con gzdeflate/gzinflate

+0

Ho provato ma ottengo un errore di dati quando utilizzo gzinflate =/(la mia immagine ha solo un pezzo IDAT) – MatTheCat

+0

@MatTheCat Ho implementato PNG in lettura/scrittura usando le classi Java Deflater/Infalter e ha funzionato perfettamente. Forse proverai a rimuovere i primi due byte? 'gzinflate (substr ($ idat_data, 2)'? http://www.php.net/manual/en/function.gzinflate.php#70875 – leonbloy

+0

Ho appena notato che la lunghezza del pezzo IDAT è inferiore alla lunghezza dei suoi dati , Penso che sia da dove viene il problema, ma non riesco a indovinare perché la parte di IDAT della lunghezza di – MatTheCat

Problemi correlati