load( $uri ); } } public function load( $uri ) { if ( $this->handle ) { $this->close(); } $this->handle = @fopen( $uri, 'r' ); } public function close() { if ( $this->handle ) { fclose( $this->handle ); $this->handle = null; $this->type = null; $this->str = null; } } public function getSize() { $this->strpos = 0; if ( $this->getType() ) { return array_values( $this->parseSize() ); } return false; } public function getType() { $this->strpos = 0; if ( ! $this->type ) { switch ( $this->getChars( 2 ) ) { case 'BM': return $this->type = 'bmp'; case 'GI': return $this->type = 'gif'; case chr( 0xFF ) . chr( 0xd8 ): return $this->type = 'jpeg'; case chr( 0x89 ) . 'P': return $this->type = 'png'; default: return false; } } return $this->type; } private function parseSize() { $this->strpos = 0; switch ( $this->type ) { case 'png': return $this->parseSizeForPNG(); case 'gif': return $this->parseSizeForGIF(); case 'bmp': return $this->parseSizeForBMP(); case 'jpeg': return $this->parseSizeForJPEG(); } return null; } private function parseSizeForPNG() { $chars = $this->getChars( 25 ); return unpack( 'N*', substr( $chars, 16, 8 ) ); } private function parseSizeForGIF() { $chars = $this->getChars( 11 ); return unpack( 'S*', substr( $chars, 6, 4 ) ); } private function parseSizeForBMP() { $chars = $this->getChars( 29 ); $chars = substr( $chars, 14, 14 ); $type = unpack( 'C', $chars ); return ( reset( $type ) == 40 ) ? unpack( 'L*', substr( $chars, 4 ) ) : unpack( 'L*', substr( $chars, 4, 8 ) ); } private function parseSizeForJPEG() { $state = null; $i = 0; while ( true ) { switch ( $state ) { default: $this->getChars( 2 ); $state = 'started'; break; case 'started': $b = $this->getByte(); if ( $b === false ) { return false; } $state = $b == 0xFF ? 'sof' : 'started'; break; case 'sof': $b = $this->getByte(); if ( in_array( $b, range( 0xe0, 0xef ) ) ) { $state = 'skipframe'; } elseif ( in_array( $b, array_merge( range( 0xC0, 0xC3 ), range( 0xC5, 0xC7 ), range( 0xC9, 0xCB ), range( 0xCD, 0xCF ) ) ) ) { $state = 'readsize'; } elseif ( $b == 0xFF ) { $state = 'sof'; } else { $state = 'skipframe'; } break; case 'skipframe': $skip = $this->readInt( $this->getChars( 2 ) ) - 2; $state = 'doskip'; break; case 'doskip': $this->getChars( $skip ); $state = 'started'; break; case 'readsize': $c = $this->getChars( 7 ); return array( $this->readInt( substr( $c, 5, 2 ) ), $this->readInt( substr( $c, 3, 2 ) ) ); } } } private function getChars( $n ) { $response = null; if ( ! $this->handle ) { return false; } // do we need more data? if ( $this->strpos + $n - 1 >= strlen( $this->str ) ) { $end = ( $this->strpos + $n ); while ( strlen( $this->str ) < $end && $response !== false ) { // read more from the file handle $need = $end - ftell( $this->handle ); if ( $response = fread( $this->handle, $need ) ) { $this->str .= $response; } else { return false; } } } $result = substr( $this->str, $this->strpos, $n ); $this->strpos += $n; return $result; } private function getByte() { $c = $this->getChars( 1 ); $b = unpack( 'C', $c ); return reset( $b ); } private function readInt( $str ) { $size = unpack( 'C*', $str ); return ( $size[1] << 8 ) + $size[2]; } public function __destruct() { $this->close(); } }