public function decodeToBlocks($bytes)
{
$bytes->setPosition(0);
$blocks = array();
// Header block
$blocks['header'] = $bytes->bite(6);
// Logical screen descriptor block
$part = $bytes->bite(2);
// canvass w
$hex = $part;
$part = $bytes->bite(2);
// canvass h
$hex .= $part;
$part = $bytes->bite(1);
// packed field
$hex .= $part;
$bin = $this->_fixSize($this->_hexToBin($part), 8);
$globalColorTableFlag = bindec(substr($bin, 0, 1));
$sizeOfGlobalColorTable = bindec(substr($bin, 5, 3));
$part = $bytes->bite(1);
// backgroundColorIndex
$hex .= $part;
$part = $bytes->bite(1);
// pixelAspectRatio
$hex .= $part;
$blocks['logicalScreenDescriptor'] = $hex;
// Global color table is optional so check its existence
if ($globalColorTableFlag > 0) {
// Formula: 3 * (2^(N+1))
$colorTableLength = 3 * pow(2, $sizeOfGlobalColorTable + 1);
$part = $bytes->bite($colorTableLength);
$blocks['globalColorTable'] = $part;
}
$commentC = $plainTextC = $appCount = $gce = $dc = 0;
// index count
while (!$bytes->isEnd()) {
$part = $bytes->bite(1);
if ('21' === $part) {
// block tests
$hex = $part;
$part = $bytes->bite(1);
if ('ff' === $part) {
// App extension block
$hex .= $part;
$part = $bytes->bite(1);
// app name length should be 0x0b or int 11 but we check anyways
$size = hexdec($part);
// turn it to int
$hex .= $part;
$part = $bytes->bite($size);
// app name
$hex .= $part;
while (!$bytes->isEnd()) {
// loop thru all app sub blocks
$nextSize = $bytes->bite(1);
if ($nextSize !== '00') {
$hex .= $nextSize;
$size = hexdec($nextSize);
$part = $bytes->bite($size);
$hex .= $part;
} else {
$hex .= $nextSize;
$blocks['applicationExtension-' . $appCount] = $hex;
break;
}
}
$appCount++;
} else {
if ('f9' === $part) {
// graphic
$hex .= $part;
$part = $bytes->bite(1);
// size
$hex .= $part;
$part = $bytes->bite(1);
// packed field
$hex .= $part;
$part = $bytes->bite(2);
// delay time
$hex .= $part;
$part = $bytes->bite(1);
// trans color index
$hex .= $part;
$part = $bytes->bite(1);
// terminator
$hex .= $part;
$blocks['graphicControlExtension-' . $gce] = $hex;
$gce++;
} else {
if ('01' === $part) {
// plain text ext
$hex .= $part;
while (!$bytes->isEnd()) {
// loop thru all app sub blocks
$nextSize = $bytes->bite(1);
if ($nextSize !== '00') {
$hex .= $nextSize;
$size = hexdec($nextSize);
$part = $bytes->bite($size);
$hex .= $part;
} else {
$hex .= $nextSize;
$blocks['plainTextExtension-' . $plainTextC] = $hex;
break;
}
}
$plainTextC++;
} else {
if ('fe' === $part) {
// comment ext
$hex .= $part;
while (!$bytes->isEnd()) {
// loop thru all app sub blocks
$nextSize = $bytes->bite(1);
if ($nextSize !== '00') {
$hex .= $nextSize;
$size = hexdec($nextSize);
$part = $bytes->bite($size);
$hex .= $part;
} else {
$hex .= $nextSize;
$blocks['commentExtension-' . $commentC] = $hex;
break;
}
}
$commentC++;
}
}
}
}
} else {
if ('2c' === $part) {
// image descriptors
$hex = $part;
$part = $bytes->bite(2);
// imageLeft
$hex .= $part;
$part = $bytes->bite(2);
// imageTop
$hex .= $part;
$part = $bytes->bite(2);
// imageWidth
$hex .= $part;
$part = $bytes->bite(2);
// imageHeight
$hex .= $part;
$part = $bytes->bite(1);
// packed field
$hex .= $part;
$blocks['imageDescriptor-' . $dc] = $hex;
$bin = $this->_fixSize($this->_hexToBin($part), 8);
$localColorTableFlag = bindec(substr($bin, 0, 1));
$sizeOfLocalColorTable = bindec(substr($bin, 5, 3));
//LC
if ($localColorTableFlag) {
// Formula: 3 * (2^(N+1))
$localColorTableLen = 3 * pow(2, $sizeOfLocalColorTable + 1);
$part = $bytes->bite($localColorTableLen);
$blocks['localColorTable-' . $dc] = $part;
}
// Image data
$part = $bytes->bite(1);
// LZW code
$hex = $part;
while ($bytes->isEnd() === false) {
$nextSize = $bytes->bite(1);
$hex .= $nextSize;
if ($nextSize !== '00') {
$subBlockLen = hexdec($nextSize);
$subBlock = $bytes->bite($subBlockLen);
$hex .= $subBlock;
} else {
$blocks['imageData-' . $dc] = $hex;
break;
}
}
$dc++;
} else {
$blocks['trailer'] = $part;
break;
}
}
}
if ($blocks['trailer'] !== '3b') {
throw new \Exception('Error decoding GIF. Stopped at ' . $bytes->getPosition() . '. Length is ' . $bytes->length() . '.');
}
return $blocks;
}