$frameCount, 'looped' => $isLooped, 'duration' => $duration, 'xmp' => $xmp, 'comment' => $comment, ]; } /** * @param resource $fh * @param int $bpp * @return void */ static function readGCT( $fh, $bpp ) { if ( $bpp > 0 ) { $max = pow( 2, $bpp ); for ( $i = 1; $i <= $max; ++$i ) { fread( $fh, 3 ); } } } /** * @param string $data * @throws Exception * @return int */ static function decodeBPP( $data ) { if ( strlen( $data ) < 1 ) { throw new Exception( "Ran out of input" ); } $buf = unpack( 'C', $data )[1]; $bpp = ( $buf & 7 ) + 1; $buf >>= 7; $have_map = $buf & 1; return $have_map ? $bpp : 0; } /** * @param resource $fh * @throws Exception */ static function skipBlock( $fh ) { while ( !feof( $fh ) ) { $buf = fread( $fh, 1 ); if ( strlen( $buf ) < 1 ) { throw new Exception( "Ran out of input" ); } $block_len = unpack( 'C', $buf )[1]; if ( $block_len == 0 ) { return; } fread( $fh, $block_len ); } } /** * Read a block. In the GIF format, a block is made up of * several sub-blocks. Each sub block starts with one byte * saying how long the sub-block is, followed by the sub-block. * The entire block is terminated by a sub-block of length * 0. * @param resource $fh File handle * @param bool $includeLengths Include the length bytes of the * sub-blocks in the returned value. Normally this is false, * except XMP is weird and does a hack where you need to keep * these length bytes. * @throws Exception * @return string The data. */ static function readBlock( $fh, $includeLengths = false ) { $data = ''; $subLength = fread( $fh, 1 ); $blocks = 0; while ( $subLength !== "\0" ) { $blocks++; if ( $blocks > self::MAX_SUBBLOCKS ) { throw new Exception( "MAX_SUBBLOCKS exceeded (over $blocks sub-blocks)" ); } if ( feof( $fh ) ) { throw new Exception( "Read error: Unexpected EOF." ); } if ( $includeLengths ) { $data .= $subLength; } $data .= fread( $fh, ord( $subLength ) ); $subLength = fread( $fh, 1 ); } return $data; } }