Skip to content

Commit

Permalink
Use lzw-tiff-decoder
Browse files Browse the repository at this point in the history
  • Loading branch information
manzt committed Sep 8, 2020
1 parent 7d4a7ab commit cb860da
Show file tree
Hide file tree
Showing 5 changed files with 26 additions and 130 deletions.
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"browsers": "defaults"
},
"dependencies": {
"lzw-tiff-decoder": "0.0.2",
"pako": "^1.0.11",
"threads": "^1.3.1",
"txml": "^3.1.2"
Expand Down Expand Up @@ -61,7 +62,7 @@
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"prepare": "npm run build",
"test": "mocha --require @babel/register test/geotiff.spec.js"
"test": "mocha --unhandled-rejections=strict --require @babel/register test/geotiff.spec.js"
},
"author": "Fabian Schindler",
"contributors": [
Expand Down
4 changes: 2 additions & 2 deletions src/compression/basedecoder.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { applyPredictor } from '../predictor';

export default class BaseDecoder {
decode(fileDirectory, buffer) {
const decoded = this.decodeBlock(buffer);
async decode(fileDirectory, buffer) {
const decoded = await this.decodeBlock(buffer);
const predictor = fileDirectory.Predictor || 1;
if (predictor !== 1) {
const isTiled = !fileDirectory.StripOffsets;
Expand Down
2 changes: 1 addition & 1 deletion src/compression/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export function getDecoder(fileDirectory) {
case 1: // no compression
return new RawDecoder();
case 5: // LZW
return new LZWDecoder();
return new LZWDecoder(fileDirectory);
case 6: // JPEG
throw new Error('old style JPEG compression is not supported.');
case 7: // JPEG
Expand Down
142 changes: 16 additions & 126 deletions src/compression/lzw.js
Original file line number Diff line number Diff line change
@@ -1,132 +1,22 @@
import WasmLZWDecoder from 'lzw-tiff-decoder';
import BaseDecoder from './basedecoder';


const MIN_BITS = 9;
const CLEAR_CODE = 256; // clear code
const EOI_CODE = 257; // end of information
const MAX_BYTELENGTH = 12;

function getByte(array, position, length) {
const d = position % 8;
const a = Math.floor(position / 8);
const de = 8 - d;
const ef = (position + length) - ((a + 1) * 8);
let fg = (8 * (a + 2)) - (position + length);
const dg = ((a + 2) * 8) - position;
fg = Math.max(0, fg);
if (a >= array.length) {
console.warn('ran off the end of the buffer before finding EOI_CODE (end on input code)');
return EOI_CODE;
}
let chunk1 = array[a] & ((2 ** (8 - d)) - 1);
chunk1 <<= (length - de);
let chunks = chunk1;
if (a + 1 < array.length) {
let chunk2 = array[a + 1] >>> fg;
chunk2 <<= Math.max(0, (length - dg));
chunks += chunk2;
}
if (ef > 8 && a + 2 < array.length) {
const hi = ((a + 3) * 8) - (position + length);
const chunk3 = array[a + 2] >>> hi;
chunks += chunk3;
}
return chunks;
}

function appendReversed(dest, source) {
for (let i = source.length - 1; i >= 0; i--) {
dest.push(source[i]);
}
return dest;
}

function decompress(input) {
const dictionaryIndex = new Uint16Array(4093);
const dictionaryChar = new Uint8Array(4093);
for (let i = 0; i <= 257; i++) {
dictionaryIndex[i] = 4096;
dictionaryChar[i] = i;
}
let dictionaryLength = 258;
let byteLength = MIN_BITS;
let position = 0;

function initDictionary() {
dictionaryLength = 258;
byteLength = MIN_BITS;
}
function getNext(array) {
const byte = getByte(array, position, byteLength);
position += byteLength;
return byte;
}
function addToDictionary(i, c) {
dictionaryChar[dictionaryLength] = c;
dictionaryIndex[dictionaryLength] = i;
dictionaryLength++;
return dictionaryLength - 1;
}
function getDictionaryReversed(n) {
const rev = [];
for (let i = n; i !== 4096; i = dictionaryIndex[i]) {
rev.push(dictionaryChar[i]);
}
return rev;
}

const result = [];
initDictionary();
const array = new Uint8Array(input);
let code = getNext(array);
let oldCode;
while (code !== EOI_CODE) {
if (code === CLEAR_CODE) {
initDictionary();
code = getNext(array);
while (code === CLEAR_CODE) {
code = getNext(array);
}

if (code === EOI_CODE) {
break;
} else if (code > CLEAR_CODE) {
throw new Error(`corrupted code at scanline ${code}`);
} else {
const val = getDictionaryReversed(code);
appendReversed(result, val);
oldCode = code;
}
} else if (code < dictionaryLength) {
const val = getDictionaryReversed(code);
appendReversed(result, val);
addToDictionary(oldCode, val[val.length - 1]);
oldCode = code;
} else {
const oldVal = getDictionaryReversed(oldCode);
if (!oldVal) {
throw new Error(`Bogus entry. Not in dictionary, ${oldCode} / ${dictionaryLength}, position: ${position}`);
}
appendReversed(result, oldVal);
result.push(oldVal[oldVal.length - 1]);
addToDictionary(oldCode, oldVal[oldVal.length - 1]);
oldCode = code;
}

if (dictionaryLength + 1 >= (2 ** byteLength)) {
if (byteLength === MAX_BYTELENGTH) {
oldCode = undefined;
} else {
byteLength++;
}
}
code = getNext(array);
}
return new Uint8Array(result);
}
const decoder = new WasmLZWDecoder();

export default class LZWDecoder extends BaseDecoder {
decodeBlock(buffer) {
return decompress(buffer, false).buffer;
constructor(fileDirectory) {
super();
// Need to calculate max size of uncompressed array
const width = fileDirectory.TileWidth || fileDirectory.ImageWidth;
const height = fileDirectory.TileLength || fileDirectory.ImageLength;
const nbytes = fileDirectory.BitsPerSample[0] / 8;
const size = width * height * nbytes;
this.max_uncompressed_size = size + 100000;
}

async decodeBlock(buffer) {
const bytes = new Uint8Array(buffer);
const result = await decoder.decompress(bytes, this.max_uncompressed_size);
return result.buffer;
}
}

0 comments on commit cb860da

Please sign in to comment.