@awayjs/graphics
Version:
AwayJS graphics classes
705 lines (704 loc) • 26.4 kB
JavaScript
/*
* Copyright 2014 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { __extends } from "tslib";
import { Settings } from '../Settings';
import { NativeDeflate } from './NativeDeflate';
import { memCopy } from './utilities';
var InflateState;
(function (InflateState) {
InflateState[InflateState["INIT"] = 0] = "INIT";
InflateState[InflateState["BLOCK_0"] = 1] = "BLOCK_0";
InflateState[InflateState["BLOCK_1"] = 2] = "BLOCK_1";
InflateState[InflateState["BLOCK_2_PRE"] = 3] = "BLOCK_2_PRE";
InflateState[InflateState["BLOCK_2"] = 4] = "BLOCK_2";
InflateState[InflateState["DONE"] = 5] = "DONE";
InflateState[InflateState["ERROR"] = 6] = "ERROR";
InflateState[InflateState["VERIFY_HEADER"] = 7] = "VERIFY_HEADER";
})(InflateState || (InflateState = {}));
var WINDOW_SIZE = 32768;
var WINDOW_SHIFT_POSITION = 65536;
var MAX_WINDOW_SIZE = WINDOW_SHIFT_POSITION + 258; /* plus max copy len */
var Inflate = /** @class */ (function () {
function Inflate(verifyHeader) {
//
}
Inflate.prototype.push = function (data) {
//Debug.abstractMethod('Inflate.push');
};
Inflate.prototype.close = function () {
//
};
Inflate.create = function (verifyHeader, size, tryNative) {
if (size === void 0) { size = 0; }
if (tryNative === void 0) { tryNative = true; }
if (tryNative && !NativeDeflate.isSupported) {
console.warn('[NativeDeflate] Is not supported!');
}
if (tryNative && NativeDeflate.isSupported && Settings.USE_NATIVE_DEFLATE) {
if (size) {
//console.debug('[NativeDeflate] Decoding API is supported and enabled, use native');
return new NativeDeflate(verifyHeader, size);
}
else {
console.debug('[NativeDeflate] size not presented, can`t use a native implementation');
}
}
return new BasicInflate(verifyHeader);
};
Inflate.prototype._processZLibHeader = function (buffer, start, end) {
/* returns -1 - bad header, 0 - not enough data, 1+ - number of bytes processed */
var ZLIB_HEADER_SIZE = 2;
if (start + ZLIB_HEADER_SIZE > end) {
return 0;
}
var header = (buffer[start] << 8) | buffer[start + 1];
var error = null;
if ((header & 0x0f00) !== 0x0800) {
error = 'inflate: unknown compression method';
}
else if ((header % 31) !== 0) {
error = 'inflate: bad FCHECK';
}
else if ((header & 0x20) !== 0) {
error = 'inflate: FDICT bit set';
}
if (error) {
if (this.onError) {
this.onError(error);
}
return -1;
}
else {
return ZLIB_HEADER_SIZE;
}
};
Inflate.inflate = function (data, expectedLength, zlibHeader) {
var position = 0;
var output = new Uint8Array(expectedLength);
var inflate = Inflate.create(zlibHeader, 0, false);
inflate.onData = function (data) {
// Make sure we don't cause an exception here when trying to set out-of-bound data by clamping the number of
// bytes to write to the remaining space in our output buffer. The Flash Player ignores data that goes over the
// expected length, so should we.
var length = Math.min(data.length, output.length - position);
if (length) {
memCopy(output, data, position, 0, length);
}
position += length;
};
inflate.onError = function (error) {
throw new Error(error);
};
inflate.push(data);
inflate.close();
return output;
};
return Inflate;
}());
export { Inflate };
var BasicInflate = /** @class */ (function (_super) {
__extends(BasicInflate, _super);
function BasicInflate(verifyHeader) {
var _this = _super.call(this, verifyHeader) || this;
_this._buffer = null;
_this._bufferSize = 0;
_this._bufferPosition = 0;
_this._bitBuffer = 0;
_this._bitLength = 0;
_this._window = new Uint8Array(MAX_WINDOW_SIZE);
_this._windowPosition = 0;
_this._state = verifyHeader ? InflateState.VERIFY_HEADER : InflateState.INIT;
_this._isFinalBlock = false;
_this._literalTable = null;
_this._distanceTable = null;
_this._block0Read = 0;
_this._block2State = null;
_this._copyState = {
state: 0,
len: 0,
lenBits: 0,
dist: 0,
distBits: 0
};
if (!areTablesInitialized) {
initializeTables();
areTablesInitialized = true;
}
return _this;
}
BasicInflate.prototype.push = function (data) {
if (!this._buffer || this._buffer.length < this._bufferSize + data.length) {
var newBuffer = new Uint8Array(this._bufferSize + data.length);
if (this._buffer) {
newBuffer.set(this._buffer);
}
this._buffer = newBuffer;
}
this._buffer.set(data, this._bufferSize);
this._bufferSize += data.length;
this._bufferPosition = 0;
var incomplete = false;
do {
var lastPosition = this._windowPosition;
if (this._state === InflateState.INIT) {
incomplete = this._decodeInitState();
if (incomplete) {
break;
}
}
switch (this._state) {
case InflateState.BLOCK_0:
incomplete = this._decodeBlock0();
break;
case InflateState.BLOCK_2_PRE:
incomplete = this._decodeBlock2Pre();
if (incomplete) {
break;
}
/* fall through */
case InflateState.BLOCK_1:
case InflateState.BLOCK_2:
incomplete = this._decodeBlock();
break;
case InflateState.ERROR:
case InflateState.DONE:
// skipping all data
this._bufferPosition = this._bufferSize;
break;
case InflateState.VERIFY_HEADER:
var processed = this._processZLibHeader(this._buffer, this._bufferPosition, this._bufferSize);
if (processed > 0) {
this._bufferPosition += processed;
this._state = InflateState.INIT;
}
else if (processed === 0) {
incomplete = true;
}
else {
this._state = InflateState.ERROR;
}
break;
}
var decoded = this._windowPosition - lastPosition;
if (decoded > 0) {
this.onData(this._window.subarray(lastPosition, this._windowPosition));
}
if (this._windowPosition >= WINDOW_SHIFT_POSITION) {
// shift window
if ('copyWithin' in this._buffer) {
this._window['copyWithin'](0, this._windowPosition - WINDOW_SIZE, this._windowPosition);
}
else {
this._window.set(this._window.subarray(this._windowPosition - WINDOW_SIZE, this._windowPosition));
}
this._windowPosition = WINDOW_SIZE;
}
} while (!incomplete && this._bufferPosition < this._bufferSize);
if (this._bufferPosition < this._bufferSize) {
// shift buffer
if ('copyWithin' in this._buffer) {
this._buffer['copyWithin'](0, this._bufferPosition, this._bufferSize);
}
else {
this._buffer.set(this._buffer.subarray(this._bufferPosition, this._bufferSize));
}
this._bufferSize -= this._bufferPosition;
}
else {
this._bufferSize = 0;
}
};
BasicInflate.prototype._decodeInitState = function () {
if (this._isFinalBlock) {
this._state = InflateState.DONE;
return false;
}
var buffer = this._buffer, bufferSize = this._bufferSize;
var bitBuffer = this._bitBuffer, bitLength = this._bitLength;
var state;
var position = this._bufferPosition;
if (((bufferSize - position) << 3) + bitLength < 3) {
return true;
}
if (bitLength < 3) {
bitBuffer |= buffer[position++] << bitLength;
bitLength += 8;
}
var type = bitBuffer & 7;
bitBuffer >>= 3;
bitLength -= 3;
switch (type >> 1) {
case 0:
bitBuffer = 0;
bitLength = 0;
if (bufferSize - position < 4) {
return true;
}
var length = buffer[position] | (buffer[position + 1] << 8);
var length2 = buffer[position + 2] | (buffer[position + 3] << 8);
position += 4;
if ((length ^ length2) !== 0xFFFF) {
this._error('inflate: invalid block 0 length');
state = InflateState.ERROR;
break;
}
if (length === 0) {
state = InflateState.INIT;
}
else {
this._block0Read = length;
state = InflateState.BLOCK_0;
}
break;
case 1:
state = InflateState.BLOCK_1;
this._literalTable = fixedLiteralTable;
this._distanceTable = fixedDistanceTable;
break;
case 2:
if (((bufferSize - position) << 3) + bitLength < 14 + 3 * 4) {
return true;
}
while (bitLength < 14) {
bitBuffer |= buffer[position++] << bitLength;
bitLength += 8;
}
var numLengthCodes = ((bitBuffer >> 10) & 15) + 4;
if (((bufferSize - position) << 3) + bitLength < 14 + 3 * numLengthCodes) {
return true;
}
var block2State = {
numLiteralCodes: (bitBuffer & 31) + 257,
numDistanceCodes: ((bitBuffer >> 5) & 31) + 1,
codeLengthTable: undefined,
bitLengths: undefined,
codesRead: 0,
dupBits: 0
};
bitBuffer >>= 14;
bitLength -= 14;
var codeLengths = new Uint8Array(19);
for (var i = 0; i < numLengthCodes; ++i) {
if (bitLength < 3) {
bitBuffer |= buffer[position++] << bitLength;
bitLength += 8;
}
codeLengths[codeLengthOrder[i]] = bitBuffer & 7;
bitBuffer >>= 3;
bitLength -= 3;
}
for (; i < 19; i++) {
codeLengths[codeLengthOrder[i]] = 0;
}
block2State.bitLengths =
new Uint8Array(block2State.numLiteralCodes + block2State.numDistanceCodes);
block2State.codeLengthTable = makeHuffmanTable(codeLengths);
this._block2State = block2State;
state = InflateState.BLOCK_2_PRE;
break;
default:
this._error('inflate: unsupported block type');
state = InflateState.ERROR;
return false;
}
this._isFinalBlock = !!(type & 1);
this._state = state;
this._bufferPosition = position;
this._bitBuffer = bitBuffer;
this._bitLength = bitLength;
return false;
};
BasicInflate.prototype._error = function (e) {
if (this.onError) {
this.onError(e);
}
};
BasicInflate.prototype._decodeBlock0 = function () {
var position = this._bufferPosition;
var windowPosition = this._windowPosition;
var toRead = this._block0Read;
var leftInWindow = MAX_WINDOW_SIZE - windowPosition;
var leftInBuffer = this._bufferSize - position;
var incomplete = leftInBuffer < toRead;
var canFit = Math.min(leftInWindow, leftInBuffer, toRead);
this._window.set(this._buffer.subarray(position, position + canFit), windowPosition);
this._windowPosition = windowPosition + canFit;
this._bufferPosition = position + canFit;
this._block0Read = toRead - canFit;
if (toRead === canFit) {
this._state = InflateState.INIT;
return false;
}
return incomplete && leftInWindow < leftInBuffer;
};
BasicInflate.prototype._readBits = function (size) {
var bitBuffer = this._bitBuffer;
var bitLength = this._bitLength;
if (size > bitLength) {
var pos = this._bufferPosition;
var end = this._bufferSize;
do {
if (pos >= end) {
this._bufferPosition = pos;
this._bitBuffer = bitBuffer;
this._bitLength = bitLength;
return -1;
}
bitBuffer |= this._buffer[pos++] << bitLength;
bitLength += 8;
} while (size > bitLength);
this._bufferPosition = pos;
}
this._bitBuffer = bitBuffer >> size;
this._bitLength = bitLength - size;
return bitBuffer & ((1 << size) - 1);
};
BasicInflate.prototype._readCode = function (codeTable) {
var bitBuffer = this._bitBuffer;
var bitLength = this._bitLength;
var maxBits = codeTable.maxBits;
if (maxBits > bitLength) {
var pos = this._bufferPosition;
var end = this._bufferSize;
do {
if (pos >= end) {
this._bufferPosition = pos;
this._bitBuffer = bitBuffer;
this._bitLength = bitLength;
return -1;
}
bitBuffer |= this._buffer[pos++] << bitLength;
bitLength += 8;
} while (maxBits > bitLength);
this._bufferPosition = pos;
}
var code = codeTable.codes[bitBuffer & ((1 << maxBits) - 1)];
var len = code >> 16;
if ((code & 0x8000)) {
this._error('inflate: invalid encoding');
this._state = InflateState.ERROR;
return -1;
}
this._bitBuffer = bitBuffer >> len;
this._bitLength = bitLength - len;
return code & 0xffff;
};
BasicInflate.prototype._decodeBlock2Pre = function () {
var block2State = this._block2State;
var numCodes = block2State.numLiteralCodes + block2State.numDistanceCodes;
var bitLengths = block2State.bitLengths;
var i = block2State.codesRead;
var prev = i > 0 ? bitLengths[i - 1] : 0;
var codeLengthTable = block2State.codeLengthTable;
var j;
if (block2State.dupBits > 0) {
j = this._readBits(block2State.dupBits);
if (j < 0) {
return true;
}
while (j--) {
bitLengths[i++] = prev;
}
block2State.dupBits = 0;
}
while (i < numCodes) {
var sym = this._readCode(codeLengthTable);
if (sym < 0) {
block2State.codesRead = i;
return true;
}
else if (sym < 16) {
bitLengths[i++] = (prev = sym);
continue;
}
var j, dupBits;
switch (sym) {
case 16:
dupBits = 2;
j = 3;
sym = prev;
break;
case 17:
dupBits = 3;
j = 3;
sym = 0;
break;
case 18:
dupBits = 7;
j = 11;
sym = 0;
break;
}
while (j--) {
bitLengths[i++] = sym;
}
j = this._readBits(dupBits);
if (j < 0) {
block2State.codesRead = i;
block2State.dupBits = dupBits;
return true;
}
while (j--) {
bitLengths[i++] = sym;
}
prev = sym;
}
this._literalTable = makeHuffmanTable(bitLengths.subarray(0, block2State.numLiteralCodes));
this._distanceTable = makeHuffmanTable(bitLengths.subarray(block2State.numLiteralCodes));
this._state = InflateState.BLOCK_2;
this._block2State = null;
return false;
};
BasicInflate.prototype._decodeBlock = function () {
var literalTable = this._literalTable, distanceTable = this._distanceTable;
var output = this._window, pos = this._windowPosition;
var copyState = this._copyState;
var i, j, sym;
var len, lenBits, dist, distBits;
if (copyState.state !== 0) {
// continuing len/distance operation
switch (copyState.state) {
case 1:
j = 0;
if ((j = this._readBits(copyState.lenBits)) < 0) {
return true;
}
copyState.len += j;
copyState.state = 2;
/* fall through */
case 2:
if ((sym = this._readCode(distanceTable)) < 0) {
return true;
}
copyState.distBits = distanceExtraBits[sym];
copyState.dist = distanceCodes[sym];
copyState.state = 3;
/* fall through */
case 3:
j = 0;
if (copyState.distBits > 0 && (j = this._readBits(copyState.distBits)) < 0) {
return true;
}
dist = copyState.dist + j;
len = copyState.len;
i = pos - dist;
while (len--) {
output[pos++] = output[i++];
}
copyState.state = 0;
if (pos >= WINDOW_SHIFT_POSITION) {
this._windowPosition = pos;
return false;
}
break;
}
}
do {
sym = this._readCode(literalTable);
if (sym < 0) {
this._windowPosition = pos;
return true;
}
else if (sym < 256) {
output[pos++] = sym;
}
else if (sym > 256) {
this._windowPosition = pos;
sym -= 257;
lenBits = lengthExtraBits[sym];
len = lengthCodes[sym];
j = lenBits === 0 ? 0 : this._readBits(lenBits);
if (j < 0) {
copyState.state = 1;
copyState.len = len;
copyState.lenBits = lenBits;
return true;
}
len += j;
sym = this._readCode(distanceTable);
if (sym < 0) {
copyState.state = 2;
copyState.len = len;
return true;
}
distBits = distanceExtraBits[sym];
dist = distanceCodes[sym];
j = distBits === 0 ? 0 : this._readBits(distBits);
if (j < 0) {
copyState.state = 3;
copyState.len = len;
copyState.dist = dist;
copyState.distBits = distBits;
return true;
}
dist += j;
i = pos - dist;
while (len--) {
output[pos++] = output[i++];
}
}
else {
this._state = InflateState.INIT;
break; // end of block
}
} while (pos < WINDOW_SHIFT_POSITION);
this._windowPosition = pos;
return false;
};
return BasicInflate;
}(Inflate));
var codeLengthOrder;
var distanceCodes;
var distanceExtraBits;
var fixedDistanceTable;
var lengthCodes;
var lengthExtraBits;
var fixedLiteralTable;
var areTablesInitialized = false;
function initializeTables() {
codeLengthOrder = new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
distanceCodes = new Uint16Array(30);
distanceExtraBits = new Uint8Array(30);
for (var i = 0, j = 0, code = 1; i < 30; ++i) {
distanceCodes[i] = code;
code += 1 << (distanceExtraBits[i] = ~~((j += (i > 2 ? 1 : 0)) / 2));
}
var bitLengths = new Uint8Array(288);
for (var i = 0; i < 32; ++i) {
bitLengths[i] = 5;
}
fixedDistanceTable = makeHuffmanTable(bitLengths.subarray(0, 32));
lengthCodes = new Uint16Array(29);
lengthExtraBits = new Uint8Array(29);
for (var i = 0, j = 0, code = 3; i < 29; ++i) {
lengthCodes[i] = code - (i == 28 ? 1 : 0);
code += 1 << (lengthExtraBits[i] = ~~(((j += (i > 4 ? 1 : 0)) / 4) % 6));
}
for (var i = 0; i < 288; ++i) {
bitLengths[i] = i < 144 || i > 279 ? 8 : (i < 256 ? 9 : 7);
}
fixedLiteralTable = makeHuffmanTable(bitLengths);
}
function makeHuffmanTable(bitLengths) {
var maxBits = Math.max.apply(null, bitLengths);
var numLengths = bitLengths.length;
var size = 1 << maxBits;
var codes = new Uint32Array(size);
// avoiding len == 0: using max number of bits
var dummyCode = (maxBits << 16) | 0xFFFF;
for (var j = 0; j < size; j++) {
codes[j] = dummyCode;
}
for (var code = 0, len = 1, skip = 2; len <= maxBits; code <<= 1, ++len, skip <<= 1) {
for (var val = 0; val < numLengths; ++val) {
if (bitLengths[val] === len) {
var lsb = 0;
for (var i = 0; i < len; ++i)
lsb = (lsb * 2) + ((code >> i) & 1);
for (var i = lsb; i < size; i += skip)
codes[i] = (len << 16) | val;
++code;
}
}
}
return { codes: codes, maxBits: maxBits };
}
var DeflateState;
(function (DeflateState) {
DeflateState[DeflateState["WRITE"] = 0] = "WRITE";
DeflateState[DeflateState["DONE"] = 1] = "DONE";
DeflateState[DeflateState["ZLIB_HEADER"] = 2] = "ZLIB_HEADER";
})(DeflateState || (DeflateState = {}));
var Adler32 = /** @class */ (function () {
function Adler32() {
this.a = 1;
this.b = 0;
}
Adler32.prototype.update = function (data, start, end) {
var a = this.a;
var b = this.b;
for (var i = start; i < end; ++i) {
a = (a + (data[i] & 0xff)) % 65521;
b = (b + a) % 65521;
}
this.a = a;
this.b = b;
};
Adler32.prototype.getChecksum = function () {
return (this.b << 16) | this.a;
};
return Adler32;
}());
export { Adler32 };
var Deflate = /** @class */ (function () {
function Deflate(writeZlibHeader) {
this._writeZlibHeader = writeZlibHeader;
this._state = writeZlibHeader ? DeflateState.ZLIB_HEADER : DeflateState.WRITE;
this._adler32 = writeZlibHeader ? new Adler32() : null;
}
Deflate.prototype.push = function (data) {
if (this._state === DeflateState.ZLIB_HEADER) {
this.onData(new Uint8Array([0x78, 0x9C]));
this._state = DeflateState.WRITE;
}
// simple non-compressing algorithm for now
var len = data.length;
var outputSize = len + Math.ceil(len / 0xFFFF) * 5;
var output = new Uint8Array(outputSize);
var outputPos = 0;
var pos = 0;
while (len > 0xFFFF) {
output.set(new Uint8Array([
0x00,
0xFF, 0xFF,
0x00, 0x00
]), outputPos);
outputPos += 5;
output.set(data.subarray(pos, pos + 0xFFFF), outputPos);
pos += 0xFFFF;
outputPos += 0xFFFF;
len -= 0xFFFF;
}
output.set(new Uint8Array([
0x00,
(len & 0xff), ((len >> 8) & 0xff),
((~len) & 0xff), (((~len) >> 8) & 0xff)
]), outputPos);
outputPos += 5;
output.set(data.subarray(pos, len), outputPos);
this.onData(output);
if (this._adler32) {
this._adler32.update(data, 0, len);
}
};
Deflate.prototype.close = function () {
this._state = DeflateState.DONE;
this.onData(new Uint8Array([
0x01,
0x00, 0x00,
0xFF, 0xFF
]));
if (this._adler32) {
var checksum = this._adler32.getChecksum();
this.onData(new Uint8Array([
checksum & 0xff, (checksum >> 8) & 0xff,
(checksum >> 16) & 0xff, (checksum >>> 24) & 0xff
]));
}
};
return Deflate;
}());
export { Deflate };