From 4e88acc463516e79df19f40ea9c3aef4f1385622 Mon Sep 17 00:00:00 2001 From: Michael Billington Date: Tue, 26 Feb 2019 20:42:29 +1100 Subject: [PATCH] fix lzw deferred clear code, remove some commented-out debug code --- src/Mike42/GfxPhp/Util/LzwCompression.php | 17 ++++++++--------- src/Mike42/GfxPhp/Util/LzwDecodeBuffer.php | 7 ------- src/Mike42/GfxPhp/Util/LzwDecodeDictionary.php | 7 ------- src/Mike42/GfxPhp/Util/LzwEncodeBuffer.php | 13 +------------ src/Mike42/GfxPhp/Util/LzwEncodeDictionary.php | 3 --- test/integration/GifsuiteTest.php | 1 - 6 files changed, 9 insertions(+), 39 deletions(-) diff --git a/src/Mike42/GfxPhp/Util/LzwCompression.php b/src/Mike42/GfxPhp/Util/LzwCompression.php index 520ad29..76d572b 100644 --- a/src/Mike42/GfxPhp/Util/LzwCompression.php +++ b/src/Mike42/GfxPhp/Util/LzwCompression.php @@ -32,14 +32,13 @@ public static function compress(string $inp, int $minCodeSize) $outp -> add($dict -> get($word), $bits); $dict -> add($ch); if ($dict -> getSize() > (2 ** $bits)) { + // expand bit size $bits++; - //echo "compress Expanded to $bits\n"; } $word = $inp[$i]; } // Separately to parsing the input stream, periodically reset to keep under 12 bits if ($dict -> getSize() >= AbstractLzwDictionary::MAX_SIZE) { - //echo "truncating dict\n"; if ($word !== "") { $outp -> add($dict -> get($word), $bits); } @@ -75,20 +74,19 @@ public static function decompress(string $inp, int $minCodeSize) $prevcodeStr = $dict -> get($prevcode); $outp .= $prevcodeStr; while (($code = $buffer -> read($bits)) !== false) { - //echo "DECODING " . $code . "\n"; + // Decode if ($code > $dict -> getSize()) { throw new \Exception("Bad LZW, code too large"); } if ($code === $dict -> getClearCode()) { - //echo "got RESET\n"; + // got a clear code $bits = $minCodeSize + 1; $prevcodeStr = ""; $dict -> clear(); // Manually do an iteration with no dictionary modification. // This involves reading ahead 1 character, same as what we do above the // 'while' loop. There is probably some way to combine these - // way to combine them. $prevcode = $buffer -> read($bits); while ($prevcode == $dict -> getClearCode()) { // In case of consecutive clear codes @@ -105,7 +103,7 @@ public static function decompress(string $inp, int $minCodeSize) $outp .= $prevcodeStr; continue; } else if ($code === $dict -> getEodCode()) { - //$outp .= $prevcodeStr; + // End the stream break; } @@ -116,7 +114,9 @@ public static function decompress(string $inp, int $minCodeSize) } $outp .= $codeStr; $ch = $codeStr[0]; - $dict -> add($prevcodeStr . $ch); + if ($dict -> getSize() < 4096) { // Don't modify dictionary after a certain point + $dict -> add($prevcodeStr . $ch); + } // assign current code to prev code $prevcode = $code; $prevcodeStr = $codeStr; @@ -124,11 +124,10 @@ public static function decompress(string $inp, int $minCodeSize) if (($bits + 1) > 12) { // Don't exceed 12 bits. If table gets max'd out at // 4096, the following RESET or END code remains 12-bit encoded. - //echo "not expanding"; continue; } else { + // Expand $bits++; - //echo "decompress: expanded to $bits\n"; } } } diff --git a/src/Mike42/GfxPhp/Util/LzwDecodeBuffer.php b/src/Mike42/GfxPhp/Util/LzwDecodeBuffer.php index 06ceac0..2a4cb0b 100644 --- a/src/Mike42/GfxPhp/Util/LzwDecodeBuffer.php +++ b/src/Mike42/GfxPhp/Util/LzwDecodeBuffer.php @@ -24,33 +24,26 @@ public function __construct(string $contents) */ public function read(int $readBits) { - //echo "\n read($readBits)\n"; - //echo " pointer is " . $this -> ptr . "\n"; $num = 0; $firstBit = $this -> ptr - $readBits + 1; $lastBit = $this -> ptr; if ($firstBit < 0) { return false; } - //echo " firstBit = $firstBit, lastBit = $lastBit\n"; $val = 0; for ($i = $firstBit; $i <= $lastBit; $i++) { $bit = $this -> readBit($i); $val = ($val << 1) | $bit; } $this -> ptr -= $readBits; - //echo "\n returning $val\n"; return $val; } public function readBit(int $i) { - //echo " readBit($i)\n"; $byte = intdiv($i, 8); - //echo " byte is $byte\n"; $bit = $i % 8; $rightIgnore = 7 - $bit; - //echo " bit is is $bit ($rightIgnore bits to the right to remove)\n"; return (($this -> contents[$byte]) >> $rightIgnore) & 1; } } diff --git a/src/Mike42/GfxPhp/Util/LzwDecodeDictionary.php b/src/Mike42/GfxPhp/Util/LzwDecodeDictionary.php index 38c1252..9659f19 100644 --- a/src/Mike42/GfxPhp/Util/LzwDecodeDictionary.php +++ b/src/Mike42/GfxPhp/Util/LzwDecodeDictionary.php @@ -10,15 +10,11 @@ public function clear() { $count = 2 << ($this -> minCodeSize - 1); $this -> decodeDict = range(chr(0), chr($count - 1)); - //$this -> encodeDict = array_flip($this -> decodeDict); $this -> clearCode = $count; $count++; $this -> eodCode = $count; $count++; $this -> size = $count; - - //print_r($this -> decodeDict); - //print_r($this -> encodeDict); } public function get(int $code) @@ -39,9 +35,6 @@ public function add(string $entry) if ($this -> size == AbstractLzwDictionary::MAX_SIZE) { throw new \Exception("LZW code table overflow"); } - //$hexVal = bin2hex($entry); - //$l = strlen($entry); - //echo "LzwDecodeDictionary ADDING 0x$hexVal (len=$l) to dict @ position " . $this -> size ."\n"; $this -> decodeDict[$this -> size] = $entry; $this -> size++; } diff --git a/src/Mike42/GfxPhp/Util/LzwEncodeBuffer.php b/src/Mike42/GfxPhp/Util/LzwEncodeBuffer.php index 01ad324..b0f8a0b 100644 --- a/src/Mike42/GfxPhp/Util/LzwEncodeBuffer.php +++ b/src/Mike42/GfxPhp/Util/LzwEncodeBuffer.php @@ -6,8 +6,6 @@ */ class LzwEncodeBuffer { - - public function __construct() { $this -> textBuffer = ""; @@ -17,42 +15,33 @@ public function __construct() public function add(int $code, int $bits) { - //echo "ADD $code to output ($bits bits)\n"; $mask = []; $byte = 0; for ($i = 0; $i <= 8; $i++) { $mask[] = $byte; $byte = ($byte << 1) | 1; } - //print_r($mask); while ($bits > 0) { - //echo " $code, $bits bits remaining\n"; // Determine number of bits to write to this byte $capacity = (8 - $this -> bitPos); $writeBits = min($capacity, $bits); // Extract bits to use, shift and OR them on $maskedByte = ($code & $mask[$writeBits]); - //echo " masked byte for $writeBits bits is $maskedByte\n"; $shiftDir = ($this -> bitPos); - //echo " need to shift by $shiftDir to place\n"; $theBits = $maskedByte << $shiftDir; $this -> bitBuffer = $this -> bitBuffer | $theBits; // Truncate lower bits of code and repeat $code = $code >> $writeBits; $bits = $bits - $writeBits; $this -> bitPos += $writeBits; - //echo " curstor is at position " . $this -> bitPos . " \n"; if ($this -> bitPos >= 8) { $this -> bitPos = 0; $this -> textBuffer .= chr($this -> bitBuffer); $this -> bitBuffer = 0; } - //echo " string is: " . bin2hex($this -> textBuffer) . "\n"; - //echo " buffer is: " . $this -> bitBuffer . ", position " . $this -> bitPos . "\n"; } } - - + public function asString() { if ($this -> bitPos !== 0) { diff --git a/src/Mike42/GfxPhp/Util/LzwEncodeDictionary.php b/src/Mike42/GfxPhp/Util/LzwEncodeDictionary.php index 4712373..12b5314 100644 --- a/src/Mike42/GfxPhp/Util/LzwEncodeDictionary.php +++ b/src/Mike42/GfxPhp/Util/LzwEncodeDictionary.php @@ -34,9 +34,6 @@ public function add(string $entry) if ($this -> size == self::MAX_SIZE) { throw new \Exception("LZW code table overflow"); } - //$hexVal = bin2hex($entry); - //$l = strlen($entry); - //echo "LzwEncodeDictionary ADDING 0x$hexVal (len=$l) to dict @ position " . $this -> size ."\n"; $this -> encodeDict[$entry] = $this -> size; $this -> size++; } diff --git a/test/integration/GifsuiteTest.php b/test/integration/GifsuiteTest.php index 99a0469..1db128b 100644 --- a/test/integration/GifsuiteTest.php +++ b/test/integration/GifsuiteTest.php @@ -28,7 +28,6 @@ function test_4095_codes_clear() { } function test_4095_codes() { - $this -> markTestSkipped("Known bug: LZW overflow"); $img = $this -> loadImage("4095-codes.gif"); $this -> assertEquals(100, $img -> getWidth()); $this -> assertEquals(100, $img -> getHeight());