embroidery-viewer/src/file-renderer/jdataview.js

848 lines
22 KiB
JavaScript

// @ts-nocheck
//
// jDataView by Vjeux <vjeuxx@gmail.com> - Jan 2010
// Continued by RReverser <me@rreverser.com> - Feb 2013
//
// A unique way to work with a binary file in the browser
// http://github.com/jDataView/jDataView
// http://jDataView.github.io/
var compatibility = {
// NodeJS Buffer in v0.5.5 and newer
NodeBuffer: "Buffer" in globalThis && "readInt16LE" in Buffer.prototype,
DataView:
"DataView" in globalThis &&
("getFloat64" in DataView.prototype || // Chrome
"getFloat64" in new DataView(new ArrayBuffer(1))), // Node
ArrayBuffer: "ArrayBuffer" in globalThis,
PixelData:
"CanvasPixelArray" in globalThis &&
"ImageData" in globalThis &&
"document" in globalThis,
};
var createPixelData = function (byteLength, buffer) {
var data = createPixelData.context2d.createImageData(
(byteLength + 3) / 4,
1
).data;
data.byteLength = byteLength;
if (buffer !== undefined) {
for (var i = 0; i < byteLength; i++) {
data[i] = buffer[i];
}
}
return data;
};
createPixelData.context2d = document.createElement("canvas").getContext("2d");
var dataTypes = {
Int8: 1,
Int16: 2,
Int32: 4,
Uint8: 1,
Uint16: 2,
Uint32: 4,
Float32: 4,
Float64: 8,
};
var nodeNaming = {
Int8: "Int8",
Int16: "Int16",
Int32: "Int32",
Uint8: "UInt8",
Uint16: "UInt16",
Uint32: "UInt32",
Float32: "Float",
Float64: "Double",
};
function arrayFrom(arrayLike, forceCopy) {
return !forceCopy && arrayLike instanceof Array
? arrayLike
: Array.prototype.slice.call(arrayLike);
}
function defined(value, defaultValue) {
return value !== undefined ? value : defaultValue;
}
export function jDataView(buffer, byteOffset, byteLength, littleEndian) {
/* jshint validthis:true */
if (buffer instanceof jDataView) {
var result = buffer.slice(byteOffset, byteOffset + byteLength);
result._littleEndian = defined(littleEndian, result._littleEndian);
return result;
}
if (!(this instanceof jDataView)) {
return new jDataView(buffer, byteOffset, byteLength, littleEndian);
}
this.buffer = buffer = jDataView.wrapBuffer(buffer);
// Check parameters and existing functionnalities
this._isArrayBuffer =
compatibility.ArrayBuffer && buffer instanceof ArrayBuffer;
this._isPixelData =
compatibility.PixelData && buffer instanceof CanvasPixelArray;
this._isDataView = compatibility.DataView && this._isArrayBuffer;
this._isNodeBuffer = compatibility.NodeBuffer && buffer instanceof Buffer;
// Handle Type Errors
if (
!this._isNodeBuffer &&
!this._isArrayBuffer &&
!this._isPixelData &&
!(buffer instanceof Array)
) {
throw new TypeError("jDataView buffer has an incompatible type");
}
// Default Values
this._littleEndian = !!littleEndian;
var bufferLength = "byteLength" in buffer ? buffer.byteLength : buffer.length;
this.byteOffset = byteOffset = defined(byteOffset, 0);
this.byteLength = byteLength = defined(byteLength, bufferLength - byteOffset);
if (!this._isDataView) {
this._checkBounds(byteOffset, byteLength, bufferLength);
} else {
this._view = new DataView(buffer, byteOffset, byteLength);
}
// Create uniform methods (action wrappers) for the following data types
this._engineAction = this._isDataView
? this._dataViewAction
: this._isNodeBuffer
? this._nodeBufferAction
: this._isArrayBuffer
? this._arrayBufferAction
: this._arrayAction;
}
function getCharCodes(string) {
if (compatibility.NodeBuffer) {
return new Buffer(string, "binary");
}
var Type = compatibility.ArrayBuffer ? Uint8Array : Array,
codes = new Type(string.length);
for (var i = 0, length = string.length; i < length; i++) {
codes[i] = string.charCodeAt(i) & 0xff;
}
return codes;
}
// mostly internal function for wrapping any supported input (String or Array-like) to best suitable buffer format
jDataView.wrapBuffer = function (buffer) {
switch (typeof buffer) {
case "number":
if (compatibility.NodeBuffer) {
buffer = new Buffer(buffer);
buffer.fill(0);
} else if (compatibility.ArrayBuffer) {
buffer = new Uint8Array(buffer).buffer;
} else if (compatibility.PixelData) {
buffer = createPixelData(buffer);
} else {
buffer = new Array(buffer);
for (var i = 0; i < buffer.length; i++) {
buffer[i] = 0;
}
}
return buffer;
case "string":
buffer = getCharCodes(buffer);
/* falls through */
default:
if (
"length" in buffer &&
!(
(compatibility.NodeBuffer && buffer instanceof Buffer) ||
(compatibility.ArrayBuffer && buffer instanceof ArrayBuffer) ||
(compatibility.PixelData && buffer instanceof CanvasPixelArray)
)
) {
if (compatibility.NodeBuffer) {
buffer = new Buffer(buffer);
} else if (compatibility.ArrayBuffer) {
if (!(buffer instanceof ArrayBuffer)) {
buffer = new Uint8Array(buffer).buffer;
// bug in Node.js <= 0.8:
if (!(buffer instanceof ArrayBuffer)) {
buffer = new Uint8Array(arrayFrom(buffer, true)).buffer;
}
}
} else if (compatibility.PixelData) {
buffer = createPixelData(buffer.length, buffer);
} else {
buffer = arrayFrom(buffer);
}
}
return buffer;
}
};
function pow2(n) {
return n >= 0 && n < 31 ? 1 << n : pow2[n] || (pow2[n] = Math.pow(2, n));
}
// left for backward compatibility
jDataView.createBuffer = function () {
return jDataView.wrapBuffer(arguments);
};
function Uint64(lo, hi) {
this.lo = lo;
this.hi = hi;
}
jDataView.Uint64 = Uint64;
Uint64.prototype = {
valueOf: function () {
return this.lo + pow2(32) * this.hi;
},
toString: function () {
return Number.prototype.toString.apply(this.valueOf(), arguments);
},
};
Uint64.fromNumber = function (number) {
var hi = Math.floor(number / pow2(32)),
lo = number - hi * pow2(32);
return new Uint64(lo, hi);
};
function Int64(lo, hi) {
Uint64.apply(this, arguments);
}
jDataView.Int64 = Int64;
Int64.prototype =
"create" in Object ? Object.create(Uint64.prototype) : new Uint64();
Int64.prototype.valueOf = function () {
if (this.hi < pow2(31)) {
return Uint64.prototype.valueOf.apply(this, arguments);
}
return -(pow2(32) - this.lo + pow2(32) * (pow2(32) - 1 - this.hi));
};
Int64.fromNumber = function (number) {
var lo, hi;
if (number >= 0) {
var unsigned = Uint64.fromNumber(number);
lo = unsigned.lo;
hi = unsigned.hi;
} else {
hi = Math.floor(number / pow2(32));
lo = number - hi * pow2(32);
hi += pow2(32);
}
return new Int64(lo, hi);
};
jDataView.prototype = {
_offset: 0,
_bitOffset: 0,
compatibility: compatibility,
_checkBounds: function (byteOffset, byteLength, maxLength) {
// Do additional checks to simulate DataView
if (typeof byteOffset !== "number") {
throw new TypeError("Offset is not a number.");
}
if (typeof byteLength !== "number") {
throw new TypeError("Size is not a number.");
}
if (byteLength < 0) {
throw new RangeError("Length is negative.");
}
if (
byteOffset < 0 ||
byteOffset + byteLength > defined(maxLength, this.byteLength)
) {
throw new RangeError("Offsets are out of bounds.");
}
},
_action: function (type, isReadAction, byteOffset, littleEndian, value) {
return this._engineAction(
type,
isReadAction,
defined(byteOffset, this._offset),
defined(littleEndian, this._littleEndian),
value
);
},
_dataViewAction: function (
type,
isReadAction,
byteOffset,
littleEndian,
value
) {
// Move the internal offset forward
this._offset = byteOffset + dataTypes[type];
return isReadAction
? this._view["get" + type](byteOffset, littleEndian)
: this._view["set" + type](byteOffset, value, littleEndian);
},
_nodeBufferAction: function (
type,
isReadAction,
byteOffset,
littleEndian,
value
) {
// Move the internal offset forward
this._offset = byteOffset + dataTypes[type];
var nodeName =
nodeNaming[type] +
(type === "Int8" || type === "Uint8" ? "" : littleEndian ? "LE" : "BE");
byteOffset += this.byteOffset;
return isReadAction
? this.buffer["read" + nodeName](byteOffset)
: this.buffer["write" + nodeName](value, byteOffset);
},
_arrayBufferAction: function (
type,
isReadAction,
byteOffset,
littleEndian,
value
) {
var size = dataTypes[type],
TypedArray = globalThis[type + "Array"],
typedArray;
littleEndian = defined(littleEndian, this._littleEndian);
// ArrayBuffer: we use a typed array of size 1 from original buffer if alignment is good and from slice when it's not
if (
size === 1 ||
((this.byteOffset + byteOffset) % size === 0 && littleEndian)
) {
typedArray = new TypedArray(this.buffer, this.byteOffset + byteOffset, 1);
this._offset = byteOffset + size;
return isReadAction ? typedArray[0] : (typedArray[0] = value);
} else {
var bytes = new Uint8Array(
isReadAction
? this.getBytes(size, byteOffset, littleEndian, true)
: size
);
typedArray = new TypedArray(bytes.buffer, 0, 1);
if (isReadAction) {
return typedArray[0];
} else {
typedArray[0] = value;
this._setBytes(byteOffset, bytes, littleEndian);
}
}
},
_arrayAction: function (type, isReadAction, byteOffset, littleEndian, value) {
return isReadAction
? this["_get" + type](byteOffset, littleEndian)
: this["_set" + type](byteOffset, value, littleEndian);
},
// Helpers
_getBytes: function (length, byteOffset, littleEndian) {
littleEndian = defined(littleEndian, this._littleEndian);
byteOffset = defined(byteOffset, this._offset);
length = defined(length, this.byteLength - byteOffset);
this._checkBounds(byteOffset, length);
byteOffset += this.byteOffset;
this._offset = byteOffset - this.byteOffset + length;
var result = this._isArrayBuffer
? new Uint8Array(this.buffer, byteOffset, length)
: (this.buffer.slice || Array.prototype.slice).call(
this.buffer,
byteOffset,
byteOffset + length
);
return littleEndian || length <= 1 ? result : arrayFrom(result).reverse();
},
// wrapper for external calls (do not return inner buffer directly to prevent it's modifying)
getBytes: function (length, byteOffset, littleEndian, toArray) {
var result = this._getBytes(
length,
byteOffset,
defined(littleEndian, true)
);
return toArray ? arrayFrom(result) : result;
},
_setBytes: function (byteOffset, bytes, littleEndian) {
var length = bytes.length;
// needed for Opera
if (length === 0) {
return;
}
littleEndian = defined(littleEndian, this._littleEndian);
byteOffset = defined(byteOffset, this._offset);
this._checkBounds(byteOffset, length);
if (!littleEndian && length > 1) {
bytes = arrayFrom(bytes, true).reverse();
}
byteOffset += this.byteOffset;
if (this._isArrayBuffer) {
new Uint8Array(this.buffer, byteOffset, length).set(bytes);
} else {
if (this._isNodeBuffer) {
new Buffer(bytes).copy(this.buffer, byteOffset);
} else {
for (var i = 0; i < length; i++) {
this.buffer[byteOffset + i] = bytes[i];
}
}
}
this._offset = byteOffset - this.byteOffset + length;
},
setBytes: function (byteOffset, bytes, littleEndian) {
this._setBytes(byteOffset, bytes, defined(littleEndian, true));
},
getString: function (byteLength, byteOffset, encoding) {
if (this._isNodeBuffer) {
byteOffset = defined(byteOffset, this._offset);
byteLength = defined(byteLength, this.byteLength - byteOffset);
this._checkBounds(byteOffset, byteLength);
this._offset = byteOffset + byteLength;
return this.buffer.toString(
encoding || "binary",
this.byteOffset + byteOffset,
this.byteOffset + this._offset
);
}
var bytes = this._getBytes(byteLength, byteOffset, true),
string = "";
byteLength = bytes.length;
for (var i = 0; i < byteLength; i++) {
string += String.fromCharCode(bytes[i]);
}
if (encoding === "utf8") {
string = decodeURIComponent(escape(string));
}
return string;
},
setString: function (byteOffset, subString, encoding) {
if (this._isNodeBuffer) {
byteOffset = defined(byteOffset, this._offset);
this._checkBounds(byteOffset, subString.length);
this._offset =
byteOffset +
this.buffer.write(
subString,
this.byteOffset + byteOffset,
encoding || "binary"
);
return;
}
if (encoding === "utf8") {
subString = unescape(encodeURIComponent(subString));
}
this._setBytes(byteOffset, getCharCodes(subString), true);
},
getChar: function (byteOffset) {
return this.getString(1, byteOffset);
},
setChar: function (byteOffset, character) {
this.setString(byteOffset, character);
},
tell: function () {
return this._offset;
},
seek: function (byteOffset) {
this._checkBounds(byteOffset, 0);
/* jshint boss: true */
return (this._offset = byteOffset);
},
skip: function (byteLength) {
return this.seek(this._offset + byteLength);
},
slice: function (start, end, forceCopy) {
function normalizeOffset(offset, byteLength) {
return offset < 0 ? offset + byteLength : offset;
}
start = normalizeOffset(start, this.byteLength);
end = normalizeOffset(defined(end, this.byteLength), this.byteLength);
return forceCopy
? new jDataView(
this.getBytes(end - start, start, true, true),
undefined,
undefined,
this._littleEndian
)
: new jDataView(
this.buffer,
this.byteOffset + start,
end - start,
this._littleEndian
);
},
alignBy: function (byteCount) {
this._bitOffset = 0;
if (defined(byteCount, 1) !== 1) {
return this.skip(byteCount - (this._offset % byteCount || byteCount));
} else {
return this._offset;
}
},
// Compatibility functions
_getFloat64: function (byteOffset, littleEndian) {
var b = this._getBytes(8, byteOffset, littleEndian),
sign = 1 - 2 * (b[7] >> 7),
exponent = ((((b[7] << 1) & 0xff) << 3) | (b[6] >> 4)) - ((1 << 10) - 1),
// Binary operators such as | and << operate on 32 bit values, using + and Math.pow(2) instead
mantissa =
(b[6] & 0x0f) * pow2(48) +
b[5] * pow2(40) +
b[4] * pow2(32) +
b[3] * pow2(24) +
b[2] * pow2(16) +
b[1] * pow2(8) +
b[0];
if (exponent === 1024) {
if (mantissa !== 0) {
return NaN;
} else {
return sign * Infinity;
}
}
if (exponent === -1023) {
// Denormalized
return sign * mantissa * pow2(-1022 - 52);
}
return sign * (1 + mantissa * pow2(-52)) * pow2(exponent);
},
_getFloat32: function (byteOffset, littleEndian) {
var b = this._getBytes(4, byteOffset, littleEndian),
sign = 1 - 2 * (b[3] >> 7),
exponent = (((b[3] << 1) & 0xff) | (b[2] >> 7)) - 127,
mantissa = ((b[2] & 0x7f) << 16) | (b[1] << 8) | b[0];
if (exponent === 128) {
if (mantissa !== 0) {
return NaN;
} else {
return sign * Infinity;
}
}
if (exponent === -127) {
// Denormalized
return sign * mantissa * pow2(-126 - 23);
}
return sign * (1 + mantissa * pow2(-23)) * pow2(exponent);
},
_get64: function (Type, byteOffset, littleEndian) {
littleEndian = defined(littleEndian, this._littleEndian);
byteOffset = defined(byteOffset, this._offset);
var parts = littleEndian ? [0, 4] : [4, 0];
for (var i = 0; i < 2; i++) {
parts[i] = this.getUint32(byteOffset + parts[i], littleEndian);
}
this._offset = byteOffset + 8;
return new Type(parts[0], parts[1]);
},
getInt64: function (byteOffset, littleEndian) {
return this._get64(Int64, byteOffset, littleEndian);
},
getUint64: function (byteOffset, littleEndian) {
return this._get64(Uint64, byteOffset, littleEndian);
},
_getInt32: function (byteOffset, littleEndian) {
var b = this._getBytes(4, byteOffset, littleEndian);
return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0];
},
_getUint32: function (byteOffset, littleEndian) {
return this._getInt32(byteOffset, littleEndian) >>> 0;
},
_getInt16: function (byteOffset, littleEndian) {
return (this._getUint16(byteOffset, littleEndian) << 16) >> 16;
},
_getUint16: function (byteOffset, littleEndian) {
var b = this._getBytes(2, byteOffset, littleEndian);
return (b[1] << 8) | b[0];
},
_getInt8: function (byteOffset) {
return (this._getUint8(byteOffset) << 24) >> 24;
},
_getUint8: function (byteOffset) {
return this._getBytes(1, byteOffset)[0];
},
_getBitRangeData: function (bitLength, byteOffset) {
var startBit = (defined(byteOffset, this._offset) << 3) + this._bitOffset,
endBit = startBit + bitLength,
start = startBit >>> 3,
end = (endBit + 7) >>> 3,
b = this._getBytes(end - start, start, true),
wideValue = 0;
/* jshint boss: true */
if ((this._bitOffset = endBit & 7)) {
this._bitOffset -= 8;
}
for (var i = 0, length = b.length; i < length; i++) {
wideValue = (wideValue << 8) | b[i];
}
return {
start: start,
bytes: b,
wideValue: wideValue,
};
},
getSigned: function (bitLength, byteOffset) {
var shift = 32 - bitLength;
return (this.getUnsigned(bitLength, byteOffset) << shift) >> shift;
},
getUnsigned: function (bitLength, byteOffset) {
var value =
this._getBitRangeData(bitLength, byteOffset).wideValue >>>
-this._bitOffset;
return bitLength < 32 ? value & ~(-1 << bitLength) : value;
},
_setBinaryFloat: function (
byteOffset,
value,
mantSize,
expSize,
littleEndian
) {
var signBit = value < 0 ? 1 : 0,
exponent,
mantissa,
eMax = ~(-1 << (expSize - 1)),
eMin = 1 - eMax;
if (value < 0) {
value = -value;
}
if (value === 0) {
exponent = 0;
mantissa = 0;
} else if (isNaN(value)) {
exponent = 2 * eMax + 1;
mantissa = 1;
} else if (value === Infinity) {
exponent = 2 * eMax + 1;
mantissa = 0;
} else {
exponent = Math.floor(Math.log(value) / Math.LN2);
if (exponent >= eMin && exponent <= eMax) {
mantissa = Math.floor((value * pow2(-exponent) - 1) * pow2(mantSize));
exponent += eMax;
} else {
mantissa = Math.floor(value / pow2(eMin - mantSize));
exponent = 0;
}
}
var b = [];
while (mantSize >= 8) {
b.push(mantissa % 256);
mantissa = Math.floor(mantissa / 256);
mantSize -= 8;
}
exponent = (exponent << mantSize) | mantissa;
expSize += mantSize;
while (expSize >= 8) {
b.push(exponent & 0xff);
exponent >>>= 8;
expSize -= 8;
}
b.push((signBit << expSize) | exponent);
this._setBytes(byteOffset, b, littleEndian);
},
_setFloat32: function (byteOffset, value, littleEndian) {
this._setBinaryFloat(byteOffset, value, 23, 8, littleEndian);
},
_setFloat64: function (byteOffset, value, littleEndian) {
this._setBinaryFloat(byteOffset, value, 52, 11, littleEndian);
},
_set64: function (Type, byteOffset, value, littleEndian) {
if (!(value instanceof Type)) {
value = Type.fromNumber(value);
}
littleEndian = defined(littleEndian, this._littleEndian);
byteOffset = defined(byteOffset, this._offset);
var parts = littleEndian ? { lo: 0, hi: 4 } : { lo: 4, hi: 0 };
for (var partName in parts) {
this.setUint32(
byteOffset + parts[partName],
value[partName],
littleEndian
);
}
this._offset = byteOffset + 8;
},
setInt64: function (byteOffset, value, littleEndian) {
this._set64(Int64, byteOffset, value, littleEndian);
},
setUint64: function (byteOffset, value, littleEndian) {
this._set64(Uint64, byteOffset, value, littleEndian);
},
_setUint32: function (byteOffset, value, littleEndian) {
this._setBytes(
byteOffset,
[value & 0xff, (value >>> 8) & 0xff, (value >>> 16) & 0xff, value >>> 24],
littleEndian
);
},
_setUint16: function (byteOffset, value, littleEndian) {
this._setBytes(
byteOffset,
[value & 0xff, (value >>> 8) & 0xff],
littleEndian
);
},
_setUint8: function (byteOffset, value) {
this._setBytes(byteOffset, [value & 0xff]);
},
setUnsigned: function (byteOffset, value, bitLength) {
var data = this._getBitRangeData(bitLength, byteOffset),
wideValue = data.wideValue,
b = data.bytes;
wideValue &= ~(~(-1 << bitLength) << -this._bitOffset); // clearing bit range before binary "or"
wideValue |=
(bitLength < 32 ? value & ~(-1 << bitLength) : value) << -this._bitOffset; // setting bits
for (var i = b.length - 1; i >= 0; i--) {
b[i] = wideValue & 0xff;
wideValue >>>= 8;
}
this._setBytes(data.start, b, true);
},
};
var proto = jDataView.prototype;
for (var type in dataTypes) {
(function (type) {
proto["get" + type] = function (byteOffset, littleEndian) {
return this._action(type, true, byteOffset, littleEndian);
};
proto["set" + type] = function (byteOffset, value, littleEndian) {
this._action(type, false, byteOffset, littleEndian, value);
};
})(type);
}
proto._setInt32 = proto._setUint32;
proto._setInt16 = proto._setUint16;
proto._setInt8 = proto._setUint8;
proto.setSigned = proto.setUnsigned;
for (var method in proto) {
if (method.slice(0, 3) === "set") {
(function (type) {
proto["write" + type] = function () {
Array.prototype.unshift.call(arguments, undefined);
this["set" + type].apply(this, arguments);
};
})(method.slice(3));
}
}
if (typeof module !== "undefined" && typeof module.exports === "object") {
module.exports = jDataView;
} else if (typeof define === "function" && define.amd) {
define([], function () {
return jDataView;
});
} else {
var oldGlobalThis = globalThis.jDataView;
(globalThis.jDataView = jDataView).noConflict = function () {
globalThis.jDataView = oldGlobalThis;
return this;
};
}