import { IOBuffer } from 'iobuffer' ; const CLEAR_CODE = 256 ; const EOI_CODE = 257 ; const TABLE_START = 258 ; const MIN_BIT_LENGTH = 9 ; let stringTable = [ ] ; function initializeStringTable ( ) { if ( stringTable . length === 0 ) { for ( let i = 0 ; i < 256 ; i ++ ) { stringTable . push ( [ i ] ) ; } const dummyString = [ ] ; for ( let i = 256 ; i < 4096 ; i ++ ) { stringTable . push ( dummyString ) ; } } } const andTable = [ 511 , 1023 , 2047 , 4095 ] ; const bitJumps = [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 511 , 1023 , 2047 , 4095 ] ; class LzwDecoder { private stripArray : Uint8Array ; private nextData = 0 ; private nextBits = 0 ; private bytePointer = 0 ; private tableLength = TABLE_START ; private currentBitLength = MIN_BIT_LENGTH ; private outData : IOBuffer ; public constructor ( data ) { this . stripArray = new Uint8Array ( data . buffer , data . byteOffset , data . byteLength ) ; this . outData = new IOBuffer ( data . byteLength ) ; this . initializeTable ( ) ; } public decode ( ) { let code = 0 ; let oldCode = 0 ; while ( ( code = this . getNextCode ( ) ) !== EOI_CODE ) { if ( code === CLEAR_CODE ) { this . initializeTable ( ) ; code = this . getNextCode ( ) ; if ( code === EOI_CODE ) { break ; } this . writeString ( this . stringFromCode ( code ) ) ; oldCode = code ; } else if ( this . isInTable ( code ) ) { this . writeString ( this . stringFromCode ( code ) ) ; this . addStringToTable ( this . stringFromCode ( oldCode ) . concat ( this . stringFromCode ( code ) [ 0 ] ) ) ; oldCode = code ; } else { const outString = this . stringFromCode ( oldCode ) . concat ( this . stringFromCode ( oldCode ) [ 0 ] ) ; this . writeString ( outString ) ; this . addStringToTable ( outString ) ; oldCode = code ; } } const outArray = this . outData . toArray ( ) ; return new DataView ( outArray . buffer , outArray . byteOffset , outArray . byteLength ) ; } private initializeTable ( ) { initializeStringTable ( ) ; this . tableLength = TABLE_START ; this . currentBitLength = MIN_BIT_LENGTH ; } private writeString ( string ) { this . outData . writeBytes ( string ) ; } private stringFromCode ( code ) { return stringTable [ code ] ; } private isInTable ( code ) { return code < this . tableLength ; } private addStringToTable ( string ) { stringTable [ this . tableLength ++ ] = string ; if ( stringTable . length > 4096 ) { stringTable = [ ] ; throw new Error ( 'LZW decoding error. Please open an issue at https://github.com/image-js/tiff/issues/new/choose (include a test image).' ) ; } if ( this . tableLength === bitJumps [ this . currentBitLength ] ) { this . currentBitLength ++ ; } } private getNextCode ( ) { this . nextData = ( this . nextData << 8 ) | ( this . stripArray [ this . bytePointer ++ ] & 0xff ) ; this . nextBits += 8 ; if ( this . nextBits < this . currentBitLength ) { this . nextData = ( this . nextData << 8 ) | ( this . stripArray [ this . bytePointer ++ ] & 0xff ) ; this . nextBits += 8 ; } const code = ( this . nextData >> ( this . nextBits - this . currentBitLength ) ) & andTable [ this . currentBitLength - 9 ] ; this . nextBits -= this . currentBitLength ; if ( this . bytePointer > this . stripArray . length ) { return 257 ; } return code ; } } export function decompressLzw ( stripData ) { return new LzwDecoder ( stripData ) . decode ( ) ; }