Skip to content

Commit

Permalink
Merge pull request #39 from mike42/bugfix/38-lzw
Browse files Browse the repository at this point in the history
Fix LZW deferred clear code
  • Loading branch information
mike42 authored Feb 26, 2019
2 parents 0ea8f50 + 4e88acc commit 2ee1b09
Show file tree
Hide file tree
Showing 6 changed files with 9 additions and 39 deletions.
17 changes: 8 additions & 9 deletions src/Mike42/GfxPhp/Util/LzwCompression.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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
Expand All @@ -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;
}

Expand All @@ -116,19 +114,20 @@ 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;
if ($dict -> getSize() >= (2 ** $bits)) {
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";
}
}
}
Expand Down
7 changes: 0 additions & 7 deletions src/Mike42/GfxPhp/Util/LzwDecodeBuffer.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
7 changes: 0 additions & 7 deletions src/Mike42/GfxPhp/Util/LzwDecodeDictionary.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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++;
}
Expand Down
13 changes: 1 addition & 12 deletions src/Mike42/GfxPhp/Util/LzwEncodeBuffer.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
*/
class LzwEncodeBuffer
{


public function __construct()
{
$this -> textBuffer = "";
Expand All @@ -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) {
Expand Down
3 changes: 0 additions & 3 deletions src/Mike42/GfxPhp/Util/LzwEncodeDictionary.php
Original file line number Diff line number Diff line change
Expand Up @@ -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++;
}
Expand Down
1 change: 0 additions & 1 deletion test/integration/GifsuiteTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down

0 comments on commit 2ee1b09

Please sign in to comment.