UNPKG

tiff

Version:

TIFF image decoder written entirely in JavaScript

143 lines (124 loc) 4.07 kB
import { IOBuffer } from 'iobuffer'; const CLEAR_CODE = 256; const EOI_CODE = 257; // 0-255 from the table + 256 for clear code + 257 for end of information code. const TABLE_START = 258; const MIN_BIT_LENGTH = 9; let stringTable: number[][] = []; function initializeStringTable() { if (stringTable.length === 0) { for (let i = 0; i < 256; i++) { stringTable.push([i]); } // Fill the table with dummy data. // Elements at indices > 257 will be replaced during decompression. const dummyString: number[] = []; 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: DataView) { this.stripArray = new Uint8Array( data.buffer, data.byteOffset, data.byteLength, ); this.outData = new IOBuffer(data.byteLength); this.initializeTable(); } public decode(): DataView { 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(): void { initializeStringTable(); this.tableLength = TABLE_START; this.currentBitLength = MIN_BIT_LENGTH; } private writeString(string: number[]): void { this.outData.writeBytes(string); } private stringFromCode(code: number): number[] { // At this point, `code` must be defined in the table. return stringTable[code]; } private isInTable(code: number): boolean { return code < this.tableLength; } private addStringToTable(string: number[]): void { 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(): number { 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; // This should not really happen but is present in other codes as well. // See: https://github.com/sugark/Tiffus/blob/15a60123813d1612f4ae9e4fab964f9f7d71cf63/src/org/eclipse/swt/internal/image/TIFFLZWDecoder.java if (this.bytePointer > this.stripArray.length) { return 257; } return code; } } export function decompressLzw(stripData: DataView): DataView { return new LzwDecoder(stripData).decode(); }