30648 lines
950 KiB
JavaScript
30648 lines
950 KiB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}(g.dicom || (g.dicom = {})).ts = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.render = exports.parseImage = exports.TransferSyntax = exports.Renderer = exports.Parser = void 0;
|
|
|
|
var _sha = _interopRequireDefault(require("sha1"));
|
|
|
|
var twgl = _interopRequireWildcard(require("twgl.js"));
|
|
|
|
var _dicomCharacterSet = require("@wearemothership/dicom-character-set");
|
|
|
|
var _pako = _interopRequireDefault(require("pako"));
|
|
|
|
var _jpegLosslessDecoderJs = require("jpeg-lossless-decoder-js");
|
|
|
|
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
|
|
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
function _extends() {
|
|
_extends = Object.assign ? Object.assign.bind() : function (target) {
|
|
for (var i = 1; i < arguments.length; i++) {
|
|
var source = arguments[i];
|
|
|
|
for (var key in source) {
|
|
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
target[key] = source[key];
|
|
}
|
|
}
|
|
}
|
|
|
|
return target;
|
|
};
|
|
return _extends.apply(this, arguments);
|
|
}
|
|
|
|
var TransferSyntax;
|
|
exports.TransferSyntax = TransferSyntax;
|
|
|
|
(function (TransferSyntax) {
|
|
TransferSyntax["ImplicitLittle"] = "1.2.840.10008.1.2";
|
|
TransferSyntax["ExplicitLittle"] = "1.2.840.10008.1.2.1";
|
|
TransferSyntax["ExplicitBig"] = "1.2.840.10008.1.2.2";
|
|
TransferSyntax["CompressionJpeg"] = "1.2.840.10008.1.2.4";
|
|
TransferSyntax["CompressionJpegLossless"] = "1.2.840.10008.1.2.4.57";
|
|
TransferSyntax["CompressionJpegLosslessSel1"] = "1.2.840.10008.1.2.4.70";
|
|
TransferSyntax["CompressionJpegBaseline8bit"] = "1.2.840.10008.1.2.4.50";
|
|
TransferSyntax["CompressionJpegBaseline12bit"] = "1.2.840.10008.1.2.4.51";
|
|
TransferSyntax["CompressionJpegLsLossless"] = "1.2.840.10008.1.2.4.80";
|
|
TransferSyntax["CompressionJpegLs"] = "1.2.840.10008.1.2.4.81";
|
|
TransferSyntax["CompressionJpeg2000Lossless"] = "1.2.840.10008.1.2.4.90";
|
|
TransferSyntax["CompressionJpeg2000"] = "1.2.840.10008.1.2.4.91";
|
|
TransferSyntax["CompressionRLE"] = "1.2.840.10008.1.2.5";
|
|
TransferSyntax["CompressionDeflate"] = "1.2.840.10008.1.2.1.99";
|
|
})(TransferSyntax || (exports.TransferSyntax = TransferSyntax = {}));
|
|
|
|
var PixelRepresentation;
|
|
|
|
(function (PixelRepresentation) {
|
|
PixelRepresentation[PixelRepresentation["UInt"] = 0] = "UInt";
|
|
PixelRepresentation[PixelRepresentation["Int"] = 1] = "Int";
|
|
})(PixelRepresentation || (PixelRepresentation = {}));
|
|
|
|
var SliceDirection;
|
|
|
|
(function (SliceDirection) {
|
|
SliceDirection[SliceDirection["Unknown"] = -1] = "Unknown";
|
|
SliceDirection[SliceDirection["Axial"] = 2] = "Axial";
|
|
SliceDirection[SliceDirection["Coronal"] = 1] = "Coronal";
|
|
SliceDirection[SliceDirection["Sagittal"] = 0] = "Sagittal";
|
|
SliceDirection[SliceDirection["Oblique"] = 3] = "Oblique";
|
|
})(SliceDirection || (SliceDirection = {}));
|
|
|
|
var ByteType;
|
|
|
|
(function (ByteType) {
|
|
ByteType[ByteType["Unkown"] = 0] = "Unkown";
|
|
ByteType[ByteType["Binary"] = 1] = "Binary";
|
|
ByteType[ByteType["Integer"] = 2] = "Integer";
|
|
ByteType[ByteType["IntegerUnsigned"] = 3] = "IntegerUnsigned";
|
|
ByteType[ByteType["Float"] = 4] = "Float";
|
|
ByteType[ByteType["Complex"] = 5] = "Complex";
|
|
ByteType[ByteType["Rgb"] = 6] = "Rgb";
|
|
})(ByteType || (ByteType = {}));
|
|
|
|
const DefaultCharset = "ISO 2022 IR 6";
|
|
/* eslint-disable no-bitwise */
|
|
|
|
let crcTable;
|
|
const MAX_VALUE = 9007199254740991;
|
|
const MIN_VALUE = -9007199254740991;
|
|
|
|
const dec2hex$1 = i => (i + 0x10000).toString(16).substr(-4).toUpperCase();
|
|
|
|
const getStringAt = (dataview, start, length, charset, vr) => {
|
|
const strBuff = new Uint8Array(dataview.buffer, dataview.byteOffset + start, length);
|
|
let str = (0, _dicomCharacterSet.convertBytes)(charset || DefaultCharset, strBuff, {
|
|
vr
|
|
});
|
|
|
|
while (str && str.charCodeAt(str.length - 1) === 0) {
|
|
str = str.slice(0, -1);
|
|
}
|
|
|
|
return str;
|
|
};
|
|
|
|
const stripLeadingZeros = str => str.replace(/^[0]+/g, "");
|
|
|
|
const safeParseInt = str => {
|
|
const intStr = stripLeadingZeros(str);
|
|
|
|
if (intStr.length > 0) {
|
|
return parseInt(intStr, 10);
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
|
|
const convertCamcelCaseToTitleCase = str => {
|
|
const result = str.replace(/([A-Z][a-z])/g, " $1");
|
|
return (result.charAt(0).toUpperCase() + result.slice(1)).trim();
|
|
};
|
|
|
|
const safeParseFloat = str => {
|
|
const floatStr = stripLeadingZeros(str);
|
|
|
|
if (floatStr.length > 0) {
|
|
return parseFloat(floatStr);
|
|
}
|
|
|
|
return 0;
|
|
}; // http://stackoverflow.com/questions/8361086/convert-byte-array-to-numbers-in-javascript
|
|
|
|
|
|
const bytesToDouble = data => {
|
|
const [b0, b1, b2, b3, b4, b5, b6, b7] = data;
|
|
const sign = (b0 & 1 << 7) >> 7;
|
|
const exponent = (b0 & 127) << 4 | (b1 & 15 << 4) >> 4;
|
|
|
|
if (exponent === 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (exponent === 0x7ff) {
|
|
return sign ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY;
|
|
}
|
|
|
|
const mul = 2 ** exponent - 1023 - 52;
|
|
const mantissa = b7 + b6 * 2 ** 8 + b5 * 2 ** (8 * 2) + b4 * 2 ** (8 * 3) + b3 * 2 ** (8 * 4) + b2 * 2 ** (8 * 5) + (b1 & 15) * 2 ** (8 * 6) + 2 ** 52;
|
|
return (-1) ** sign * mantissa * mul;
|
|
};
|
|
|
|
const concatArrayBuffers = (buffer1, buffer2) => {
|
|
const tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
|
|
tmp.set(new Uint8Array(buffer1), 0);
|
|
tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
|
|
return tmp.buffer;
|
|
};
|
|
|
|
const toArrayBuffer = buffer => {
|
|
const ab = new ArrayBuffer(buffer.length);
|
|
const view = new Uint8Array(ab);
|
|
|
|
for (let i = 0; i < buffer.length; i += 1) {
|
|
view[i] = buffer[i];
|
|
}
|
|
|
|
return ab;
|
|
}; // http://stackoverflow.com/questions/203739/why-does-instanceof-return-false-for-some-literals
|
|
|
|
|
|
const isString = s => typeof s === "string" || s instanceof String; // http://stackoverflow.com/questions/1353684/detecting-an-invalid-date-date-instance-in-javascript
|
|
|
|
|
|
const isValidDate = d => {
|
|
if (Object.prototype.toString.call(d) === "[object Date]") {
|
|
// eslint-disable-next-line no-restricted-globals
|
|
if (isNaN(d.getTime())) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
const swap32 = val => (val & 0xFF) << 24 | (val & 0xFF00) << 8 | val >> 8 & 0xFF00 | val >> 24 & 0xFF;
|
|
|
|
const swap16 = val => ((val & 0xFF) << 8 | val >> 8 & 0xFF) << 16 >> 16 // since JS uses 32-bit when bit shifting
|
|
; // http://stackoverflow.com/questions/18638900/javascript-crc32
|
|
|
|
|
|
const makeCRCTable = () => {
|
|
crcTable = crcTable || Array(256);
|
|
|
|
for (let n = 0; n < 256; n += 1) {
|
|
let c = n;
|
|
|
|
for (let k = 0; k < 8; k += 1) {
|
|
c = c & 1 ? 0xEDB88320 ^ c >>> 1 : c >>> 1;
|
|
}
|
|
|
|
crcTable[n] = c;
|
|
}
|
|
|
|
return crcTable;
|
|
};
|
|
|
|
const crc32 = dataView => {
|
|
crcTable = crcTable || makeCRCTable();
|
|
let crc = 0 ^ -1;
|
|
|
|
for (let i = 0; i < dataView.byteLength; i += 1) {
|
|
crc = crc >>> 8 ^ crcTable[(crc ^ dataView.getUint8(i)) & 0xFF];
|
|
}
|
|
|
|
return (crc ^ -1) >>> 0;
|
|
};
|
|
|
|
const createBitMask = (numBytes, bitsStored, unsigned) => {
|
|
let mask = 0xFFFFFFFF;
|
|
mask >>>= (4 - numBytes) * 8 + (numBytes * 8 - bitsStored);
|
|
|
|
if (unsigned) {
|
|
if (numBytes === 1) {
|
|
mask &= 0x000000FF;
|
|
} else if (numBytes === 2) {
|
|
mask &= 0x0000FFFF;
|
|
} else if (numBytes === 4) {
|
|
mask &= 0xFFFFFFFF;
|
|
} else if (numBytes === 8) {
|
|
mask = 0xFFFFFFFF;
|
|
}
|
|
} else {
|
|
mask = 0xFFFFFFFF;
|
|
}
|
|
|
|
return mask;
|
|
};
|
|
|
|
var Utils = {
|
|
__proto__: null,
|
|
MAX_VALUE: MAX_VALUE,
|
|
MIN_VALUE: MIN_VALUE,
|
|
dec2hex: dec2hex$1,
|
|
getStringAt: getStringAt,
|
|
stripLeadingZeros: stripLeadingZeros,
|
|
safeParseInt: safeParseInt,
|
|
convertCamcelCaseToTitleCase: convertCamcelCaseToTitleCase,
|
|
safeParseFloat: safeParseFloat,
|
|
bytesToDouble: bytesToDouble,
|
|
concatArrayBuffers: concatArrayBuffers,
|
|
toArrayBuffer: toArrayBuffer,
|
|
isString: isString,
|
|
isValidDate: isValidDate,
|
|
swap32: swap32,
|
|
swap16: swap16,
|
|
makeCRCTable: makeCRCTable,
|
|
crc32: crc32,
|
|
createBitMask: createBitMask
|
|
};
|
|
const {
|
|
dec2hex
|
|
} = Utils;
|
|
const dictPrivate = {
|
|
"0207": {
|
|
"101F": ["FE", "ElscintDataScale"] // uses special Elscint double type (see Tag class)
|
|
|
|
}
|
|
}; // strange bug in eslint, thinks the integer prop keys should not be in quotes!
|
|
|
|
/* eslint-disable quote-props */
|
|
|
|
const dict = {
|
|
"0002": {
|
|
"0001": ["OB", "FileMetaInformationVersion"],
|
|
"0002": ["UI", "MediaStoredSOPClassUID"],
|
|
"0003": ["UI", "MediaStoredSOPInstanceUID"],
|
|
"0010": ["UI", "TransferSyntaxUID"],
|
|
"0012": ["UI", "ImplementationClassUID"],
|
|
"0013": ["SH", "ImplementationVersionName"],
|
|
"0016": ["AE", "SourceApplicationEntityTitle"],
|
|
"0100": ["UI", "PrivateInformationCreatorUID"],
|
|
"0102": ["OB", "PrivateInformation"]
|
|
},
|
|
"0004": {
|
|
"1130": ["CS", "FilesetID"],
|
|
"1141": ["CS", "FilesetDescriptorFileFileID"],
|
|
"1142": ["CS", "FilesetDescriptorFileFormat"],
|
|
"1200": ["UL", "RootDirectoryEntitysFirstDirectoryRecordOffset"],
|
|
"1202": ["UL", "RootDirectoryEntitysLastDirectoryRecordOffset"],
|
|
"1212": ["US", "File-setConsistenceFlag"],
|
|
"1220": ["SQ", "DirectoryRecordSequence"],
|
|
"1400": ["UL", "NextDirectoryRecordOffset"],
|
|
"1410": ["US", "RecordInuseFlag"],
|
|
"1420": ["UL", "ReferencedLowerlevelDirectoryEntityOffset"],
|
|
"1430": ["CS", "DirectoryRecordType"],
|
|
"1432": ["UI", "PrivateRecordUID"],
|
|
"1500": ["CS", "ReferencedFileID"],
|
|
"1510": ["UI", "ReferencedSOPClassUIDInFile"],
|
|
"1511": ["UI", "ReferencedSOPInstanceUIDInFile"],
|
|
"1600": ["UL", "NumberOfReferences"]
|
|
},
|
|
"0008": {
|
|
"0001": ["UL", "LengthToEnd"],
|
|
"0005": ["CS", "SpecificCharacterSet"],
|
|
"0006": ["SQ", "LanguageCodeSequence"],
|
|
"0008": ["CS", "ImageType"],
|
|
"0010": ["SH", "RecognitionCode"],
|
|
"0012": ["DA", "InstanceCreationDate"],
|
|
"0013": ["TM", "InstanceCreationTime"],
|
|
"0014": ["UI", "InstanceCreatorUID"],
|
|
"0016": ["UI", "SOPClassUID"],
|
|
"0018": ["UI", "SOPInstanceUID"],
|
|
"001A": ["UI", "RelatedGeneralSOPClassUID"],
|
|
"001B": ["UI", "OriginalSpecializedSOPClassUID"],
|
|
"0020": ["DA", "StudyDate"],
|
|
"0021": ["DA", "SeriesDate"],
|
|
"0022": ["DA", "AcquisitionDate"],
|
|
"0023": ["DA", "ContentDate"],
|
|
"0024": ["DA", "OverlayDate"],
|
|
"0025": ["DA", "CurveDate"],
|
|
"002A": ["DT", "AcquisitionDateTime"],
|
|
"0030": ["TM", "StudyTime"],
|
|
"0031": ["TM", "SeriesTime"],
|
|
"0032": ["TM", "AcquisitionTime"],
|
|
"0033": ["TM", "ContentTime"],
|
|
"0034": ["TM", "OverlayTime"],
|
|
"0035": ["TM", "CurveTime"],
|
|
"0040": ["US", "DataSetType"],
|
|
"0041": ["LO", "DataSetSubtype"],
|
|
"0042": ["CS", "NuclearMedicineSeriesType"],
|
|
"0050": ["SH", "AccessionNumber"],
|
|
"0051": ["SQ", "IssuerOfAccessionNumberSequence"],
|
|
"0052": ["CS", "QueryRetrieveLevel"],
|
|
"0054": ["AE", "RetrieveAETitle"],
|
|
"0056": ["CS", "InstanceAvailability"],
|
|
"0058": ["UI", "FailedSOPInstanceUIDList"],
|
|
"0060": ["CS", "Modality"],
|
|
"0061": ["CS", "ModalitiesInStudy"],
|
|
"0062": ["UI", "SOPClassesInStudy"],
|
|
"0064": ["CS", "ConversionType"],
|
|
"0068": ["CS", "PresentationIntentType"],
|
|
"0070": ["LO", "Manufacturer"],
|
|
"0080": ["LO", "InstitutionName"],
|
|
"0081": ["ST", "InstitutionAddress"],
|
|
"0082": ["SQ", "InstitutionCodeSequence"],
|
|
"0090": ["PN", "ReferringPhysicianName"],
|
|
"0092": ["ST", "ReferringPhysicianAddress"],
|
|
"0094": ["SH", "ReferringPhysicianTelephoneNumbers"],
|
|
"0096": ["SQ", "ReferringPhysicianIdentificationSequence"],
|
|
"0100": ["SH", "CodeValue"],
|
|
"0102": ["SH", "CodingSchemeDesignator"],
|
|
"0103": ["SH", "CodingSchemeVersion"],
|
|
"0104": ["LO", "CodeMeaning"],
|
|
"0105": ["CS", "MappingResource"],
|
|
"0106": ["DT", "ContextGroupVersion"],
|
|
"0107": ["DT", "ContextGroupLocalVersion"],
|
|
"010B": ["CS", "ContextGroupExtensionFlag"],
|
|
"010C": ["UI", "CodingSchemeUID"],
|
|
"010D": ["UI", "ContextGroupExtensionCreatorUID"],
|
|
"010F": ["CS", "ContextIdentifier"],
|
|
"0110": ["SQ", "CodingSchemeIdentificationSequence"],
|
|
"0112": ["LO", "CodingSchemeRegistry"],
|
|
"0114": ["ST", "CodingSchemeExternalID"],
|
|
"0115": ["ST", "CodingSchemeName"],
|
|
"0116": ["ST", "CodingSchemeResponsibleOrganization"],
|
|
"0117": ["UI", "ContextUID"],
|
|
"0201": ["SH", "TimezoneOffsetFromUTC"],
|
|
"1000": ["AE", "NetworkID"],
|
|
"1010": ["SH", "StationName"],
|
|
"1030": ["LO", "StudyDescription"],
|
|
"1032": ["SQ", "ProcedureCodeSequence"],
|
|
"103E": ["LO", "SeriesDescription"],
|
|
"103F": ["SQ", "SeriesDescriptionCodeSequence"],
|
|
"1040": ["LO", "InstitutionalDepartmentName"],
|
|
"1048": ["PN", "PhysiciansOfRecord"],
|
|
"1049": ["SQ", "PhysiciansOfRecordIdentificationSequence"],
|
|
"1050": ["PN", "PerformingPhysicianName"],
|
|
"1052": ["SQ", "PerformingPhysicianIdentificationSequence"],
|
|
"1060": ["PN", "NameOfPhysiciansReadingStudy"],
|
|
"1062": ["SQ", "PhysiciansReadingStudyIdentificationSequence"],
|
|
"1070": ["PN", "OperatorsName"],
|
|
"1072": ["SQ", "OperatorIdentificationSequence"],
|
|
"1080": ["LO", "AdmittingDiagnosesDescription"],
|
|
"1084": ["SQ", "AdmittingDiagnosesCodeSequence"],
|
|
"1090": ["LO", "ManufacturerModelName"],
|
|
"1100": ["SQ", "ReferencedResultsSequence"],
|
|
"1110": ["SQ", "ReferencedStudySequence"],
|
|
"1111": ["SQ", "ReferencedPerformedProcedureStepSequence"],
|
|
"1115": ["SQ", "ReferencedSeriesSequence"],
|
|
"1120": ["SQ", "ReferencedPatientSequence"],
|
|
"1125": ["SQ", "ReferencedVisitSequence"],
|
|
"1130": ["SQ", "ReferencedOverlaySequence"],
|
|
"1134": ["SQ", "ReferencedStereometricInstanceSequence"],
|
|
"113A": ["SQ", "ReferencedWaveformSequence"],
|
|
"1140": ["SQ", "ReferencedImageSequence"],
|
|
"1145": ["SQ", "ReferencedCurveSequence"],
|
|
"114A": ["SQ", "ReferencedInstanceSequence"],
|
|
"114B": ["SQ", "ReferencedRealWorldValueMappingInstanceSequence"],
|
|
"1150": ["UI", "ReferencedSOPClassUID"],
|
|
"1155": ["UI", "ReferencedSOPInstanceUID"],
|
|
"115A": ["UI", "SOPClassesSupported"],
|
|
"1160": ["IS", "ReferencedFrameNumber"],
|
|
"1161": ["UL", "SimpleFrameList"],
|
|
"1162": ["UL", "CalculatedFrameList"],
|
|
"1163": ["FD", "TimeRange"],
|
|
"1164": ["SQ", "FrameExtractionSequence"],
|
|
"1167": ["UI", "MultiFrameSourceSOPInstanceUID"],
|
|
"1195": ["UI", "TransactionUID"],
|
|
"1197": ["US", "FailureReason"],
|
|
"1198": ["SQ", "FailedSOPSequence"],
|
|
"1199": ["SQ", "ReferencedSOPSequence"],
|
|
"1200": ["SQ", "StudiesContainingOtherReferencedInstancesSequence"],
|
|
"1250": ["SQ", "RelatedSeriesSequence"],
|
|
"2110": ["CS", "LossyImageCompressionRetired"],
|
|
"2111": ["ST", "DerivationDescription"],
|
|
"2112": ["SQ", "SourceImageSequence"],
|
|
"2120": ["SH", "StageName"],
|
|
"2122": ["IS", "StageNumber"],
|
|
"2124": ["IS", "NumberOfStages"],
|
|
"2127": ["SH", "ViewName"],
|
|
"2128": ["IS", "ViewNumber"],
|
|
"2129": ["IS", "NumberOfEventTimers"],
|
|
"212A": ["IS", "NumberOfViewsInStage"],
|
|
"2130": ["DS", "EventElapsedTimes"],
|
|
"2132": ["LO", "EventTimerNames"],
|
|
"2133": ["SQ", "EventTimerSequence"],
|
|
"2134": ["FD", "EventTimeOffset"],
|
|
"2135": ["SQ", "EventCodeSequence"],
|
|
"2142": ["IS", "StartTrim"],
|
|
"2143": ["IS", "StopTrim"],
|
|
"2144": ["IS", "RecommendedDisplayFrameRate"],
|
|
"2200": ["CS", "TransducerPosition"],
|
|
"2204": ["CS", "TransducerOrientation"],
|
|
"2208": ["CS", "AnatomicStructure"],
|
|
"2218": ["SQ", "AnatomicRegionSequence"],
|
|
"2220": ["SQ", "AnatomicRegionModifierSequence"],
|
|
"2228": ["SQ", "PrimaryAnatomicStructureSequence"],
|
|
"2229": ["SQ", "AnatomicStructureSpaceOrRegionSequence"],
|
|
"2230": ["SQ", "PrimaryAnatomicStructureModifierSequence"],
|
|
"2240": ["SQ", "TransducerPositionSequence"],
|
|
"2242": ["SQ", "TransducerPositionModifierSequence"],
|
|
"2244": ["SQ", "TransducerOrientationSequence"],
|
|
"2246": ["SQ", "TransducerOrientationModifierSequence"],
|
|
"2251": ["SQ", "AnatomicStructureSpaceOrRegionCodeSequenceTrial"],
|
|
"2253": ["SQ", "AnatomicPortalOfEntranceCodeSequenceTrial"],
|
|
"2255": ["SQ", "AnatomicApproachDirectionCodeSequenceTrial"],
|
|
"2256": ["ST", "AnatomicPerspectiveDescriptionTrial"],
|
|
"2257": ["SQ", "AnatomicPerspectiveCodeSequenceTrial"],
|
|
"2258": ["ST", "AnatomicLocationOfExaminingInstrumentDescriptionTrial"],
|
|
"2259": ["SQ", "AnatomicLocationOfExaminingInstrumentCodeSequenceTrial"],
|
|
"225A": ["SQ", "AnatomicStructureSpaceOrRegionModifierCodeSequenceTrial"],
|
|
"225C": ["SQ", "OnAxisBackgroundAnatomicStructureCodeSequenceTrial"],
|
|
"3001": ["SQ", "AlternateRepresentationSequence"],
|
|
"3010": ["UI", "IrradiationEventUID"],
|
|
"4000": ["LT", "IdentifyingComments"],
|
|
"9007": ["CS", "FrameType"],
|
|
"9092": ["SQ", "ReferencedImageEvidenceSequence"],
|
|
"9121": ["SQ", "ReferencedRawDataSequence"],
|
|
"9123": ["UI", "CreatorVersionUID"],
|
|
"9124": ["SQ", "DerivationImageSequence"],
|
|
"9154": ["SQ", "SourceImageEvidenceSequence"],
|
|
"9205": ["CS", "PixelPresentation"],
|
|
"9206": ["CS", "VolumetricProperties"],
|
|
"9207": ["CS", "VolumeBasedCalculationTechnique"],
|
|
"9208": ["CS", "ComplexImageComponent"],
|
|
"9209": ["CS", "AcquisitionContrast"],
|
|
"9215": ["SQ", "DerivationCodeSequence"],
|
|
"9237": ["SQ", "ReferencedPresentationStateSequence"],
|
|
"9410": ["SQ", "ReferencedOtherPlaneSequence"],
|
|
"9458": ["SQ", "FrameDisplaySequence"],
|
|
"9459": ["FL", "RecommendedDisplayFrameRateInFloat"],
|
|
"9460": ["CS", "SkipFrameRangeFlag"]
|
|
},
|
|
"0010": {
|
|
"0010": ["PN", "PatientName"],
|
|
"0020": ["LO", "PatientID"],
|
|
"0021": ["LO", "IssuerOfPatientID"],
|
|
"0022": ["CS", "TypeOfPatientID"],
|
|
"0024": ["SQ", "IssuerOfPatientIDQualifiersSequence"],
|
|
"0030": ["DA", "PatientBirthDate"],
|
|
"0032": ["TM", "PatientBirthTime"],
|
|
"0040": ["CS", "PatientSex"],
|
|
"0050": ["SQ", "PatientInsurancePlanCodeSequence"],
|
|
"0101": ["SQ", "PatientPrimaryLanguageCodeSequence"],
|
|
"0102": ["SQ", "PatientPrimaryLanguageModifierCodeSequence"],
|
|
"1000": ["LO", "OtherPatientIDs"],
|
|
"1001": ["PN", "OtherPatientNames"],
|
|
"1002": ["SQ", "OtherPatientIDsSequence"],
|
|
"1005": ["PN", "PatientBirthName"],
|
|
"1010": ["AS", "PatientAge"],
|
|
"1020": ["DS", "PatientSize"],
|
|
"1021": ["SQ", "PatientSizeCodeSequence"],
|
|
"1030": ["DS", "PatientWeight"],
|
|
"1040": ["LO", "PatientAddress"],
|
|
"1050": ["LO", "InsurancePlanIdentification"],
|
|
"1060": ["PN", "PatientMotherBirthName"],
|
|
"1080": ["LO", "MilitaryRank"],
|
|
"1081": ["LO", "BranchOfService"],
|
|
"1090": ["LO", "MedicalRecordLocator"],
|
|
"2000": ["LO", "MedicalAlerts"],
|
|
"2110": ["LO", "Allergies"],
|
|
"2150": ["LO", "CountryOfResidence"],
|
|
"2152": ["LO", "RegionOfResidence"],
|
|
"2154": ["SH", "PatientTelephoneNumbers"],
|
|
"2160": ["SH", "EthnicGroup"],
|
|
"2180": ["SH", "Occupation"],
|
|
"21A0": ["CS", "SmokingStatus"],
|
|
"21B0": ["LT", "AdditionalPatientHistory"],
|
|
"21C0": ["US", "PregnancyStatus"],
|
|
"21D0": ["DA", "LastMenstrualDate"],
|
|
"21F0": ["LO", "PatientReligiousPreference"],
|
|
"2201": ["LO", "PatientSpeciesDescription"],
|
|
"2202": ["SQ", "PatientSpeciesCodeSequence"],
|
|
"2203": ["CS", "PatientSexNeutered"],
|
|
"2210": ["CS", "AnatomicalOrientationType"],
|
|
"2292": ["LO", "PatientBreedDescription"],
|
|
"2293": ["SQ", "PatientBreedCodeSequence"],
|
|
"2294": ["SQ", "BreedRegistrationSequence"],
|
|
"2295": ["LO", "BreedRegistrationNumber"],
|
|
"2296": ["SQ", "BreedRegistryCodeSequence"],
|
|
"2297": ["PN", "ResponsiblePerson"],
|
|
"2298": ["CS", "ResponsiblePersonRole"],
|
|
"2299": ["LO", "ResponsibleOrganization"],
|
|
"4000": ["LT", "PatientComments"],
|
|
"9431": ["FL", "ExaminedBodyThickness"]
|
|
},
|
|
"0012": {
|
|
"0010": ["LO", "ClinicalTrialSponsorName"],
|
|
"0020": ["LO", "ClinicalTrialProtocolID"],
|
|
"0021": ["LO", "ClinicalTrialProtocolName"],
|
|
"0030": ["LO", "ClinicalTrialSiteID"],
|
|
"0031": ["LO", "ClinicalTrialSiteName"],
|
|
"0040": ["LO", "ClinicalTrialSubjectID"],
|
|
"0042": ["LO", "ClinicalTrialSubjectReadingID"],
|
|
"0050": ["LO", "ClinicalTrialTimePointID"],
|
|
"0051": ["ST", "ClinicalTrialTimePointDescription"],
|
|
"0060": ["LO", "ClinicalTrialCoordinatingCenterName"],
|
|
"0062": ["CS", "PatientIdentityRemoved"],
|
|
"0063": ["LO", "DeidentificationMethod"],
|
|
"0064": ["SQ", "DeidentificationMethodCodeSequence"],
|
|
"0071": ["LO", "ClinicalTrialSeriesID"],
|
|
"0072": ["LO", "ClinicalTrialSeriesDescription"],
|
|
"0081": ["LO", "ClinicalTrialProtocolEthicsCommitteeName"],
|
|
"0082": ["LO", "ClinicalTrialProtocolEthicsCommitteeApprovalNumber"],
|
|
"0083": ["SQ", "ConsentForClinicalTrialUseSequence"],
|
|
"0084": ["CS", "DistributionType"],
|
|
"0085": ["CS", "ConsentForDistributionFlag"]
|
|
},
|
|
"0014": {
|
|
"0023": ["ST", "CADFileFormat"],
|
|
"0024": ["ST", "ComponentReferenceSystem"],
|
|
"0025": ["ST", "ComponentManufacturingProcedure"],
|
|
"0028": ["ST", "ComponentManufacturer"],
|
|
"0030": ["DS", "MaterialThickness"],
|
|
"0032": ["DS", "MaterialPipeDiameter"],
|
|
"0034": ["DS", "MaterialIsolationDiameter"],
|
|
"0042": ["ST", "MaterialGrade"],
|
|
"0044": ["ST", "MaterialPropertiesFileID"],
|
|
"0045": ["ST", "MaterialPropertiesFileFormat"],
|
|
"0046": ["LT", "MaterialNotes"],
|
|
"0050": ["CS", "ComponentShape"],
|
|
"0052": ["CS", "CurvatureType"],
|
|
"0054": ["DS", "OuterDiameter"],
|
|
"0056": ["DS", "InnerDiameter"],
|
|
"1010": ["ST", "ActualEnvironmentalConditions"],
|
|
"1020": ["DA", "ExpiryDate"],
|
|
"1040": ["ST", "EnvironmentalConditions"],
|
|
"2002": ["SQ", "EvaluatorSequence"],
|
|
"2004": ["IS", "EvaluatorNumber"],
|
|
"2006": ["PN", "EvaluatorName"],
|
|
"2008": ["IS", "EvaluationAttempt"],
|
|
"2012": ["SQ", "IndicationSequence"],
|
|
"2014": ["IS", "IndicationNumber "],
|
|
"2016": ["SH", "IndicationLabel"],
|
|
"2018": ["ST", "IndicationDescription"],
|
|
"201A": ["CS", "IndicationType"],
|
|
"201C": ["CS", "IndicationDisposition"],
|
|
"201E": ["SQ", "IndicationROISequence"],
|
|
"2030": ["SQ", "IndicationPhysicalPropertySequence"],
|
|
"2032": ["SH", "PropertyLabel"],
|
|
"2202": ["IS", "CoordinateSystemNumberOfAxes "],
|
|
"2204": ["SQ", "CoordinateSystemAxesSequence"],
|
|
"2206": ["ST", "CoordinateSystemAxisDescription"],
|
|
"2208": ["CS", "CoordinateSystemDataSetMapping"],
|
|
"220A": ["IS", "CoordinateSystemAxisNumber"],
|
|
"220C": ["CS", "CoordinateSystemAxisType"],
|
|
"220E": ["CS", "CoordinateSystemAxisUnits"],
|
|
"2210": ["OB", "CoordinateSystemAxisValues"],
|
|
"2220": ["SQ", "CoordinateSystemTransformSequence"],
|
|
"2222": ["ST", "TransformDescription"],
|
|
"2224": ["IS", "TransformNumberOfAxes"],
|
|
"2226": ["IS", "TransformOrderOfAxes"],
|
|
"2228": ["CS", "TransformedAxisUnits"],
|
|
"222A": ["DS", "CoordinateSystemTransformRotationAndScaleMatrix"],
|
|
"222C": ["DS", "CoordinateSystemTransformTranslationMatrix"],
|
|
"3011": ["DS", "InternalDetectorFrameTime"],
|
|
"3012": ["DS", "NumberOfFramesIntegrated"],
|
|
"3020": ["SQ", "DetectorTemperatureSequence"],
|
|
"3022": ["DS", "SensorName"],
|
|
"3024": ["DS", "HorizontalOffsetOfSensor"],
|
|
"3026": ["DS", "VerticalOffsetOfSensor"],
|
|
"3028": ["DS", "SensorTemperature"],
|
|
"3040": ["SQ", "DarkCurrentSequence"],
|
|
"3050": ["OB", "DarkCurrentCounts"],
|
|
"3060": ["SQ", "GainCorrectionReferenceSequence"],
|
|
"3070": ["OB", "AirCounts"],
|
|
"3071": ["DS", "KVUsedInGainCalibration"],
|
|
"3072": ["DS", "MAUsedInGainCalibration"],
|
|
"3073": ["DS", "NumberOfFramesUsedForIntegration"],
|
|
"3074": ["LO", "FilterMaterialUsedInGainCalibration"],
|
|
"3075": ["DS", "FilterThicknessUsedInGainCalibration"],
|
|
"3076": ["DA", "DateOfGainCalibration"],
|
|
"3077": ["TM", "TimeOfGainCalibration"],
|
|
"3080": ["OB", "BadPixelImage"],
|
|
"3099": ["LT", "CalibrationNotes"],
|
|
"4002": ["SQ", "PulserEquipmentSequence"],
|
|
"4004": ["CS", "PulserType"],
|
|
"4006": ["LT", "PulserNotes"],
|
|
"4008": ["SQ", "ReceiverEquipmentSequence"],
|
|
"400A": ["CS", "AmplifierType"],
|
|
"400C": ["LT", "ReceiverNotes"],
|
|
"400E": ["SQ", "PreAmplifierEquipmentSequence"],
|
|
"400F": ["LT", "PreAmplifierNotes"],
|
|
"4010": ["SQ", "TransmitTransducerSequence"],
|
|
"4011": ["SQ", "ReceiveTransducerSequence"],
|
|
"4012": ["US", "NumberOfElements"],
|
|
"4013": ["CS", "ElementShape"],
|
|
"4014": ["DS", "ElementDimensionA"],
|
|
"4015": ["DS", "ElementDimensionB"],
|
|
"4016": ["DS", "ElementPitch"],
|
|
"4017": ["DS", "MeasuredBeamDimensionA"],
|
|
"4018": ["DS", "MeasuredBeamDimensionB"],
|
|
"4019": ["DS", "LocationOfMeasuredBeamDiameter"],
|
|
"401A": ["DS", "NominalFrequency"],
|
|
"401B": ["DS", "MeasuredCenterFrequency"],
|
|
"401C": ["DS", "MeasuredBandwidth"],
|
|
"4020": ["SQ", "PulserSettingsSequence"],
|
|
"4022": ["DS", "PulseWidth"],
|
|
"4024": ["DS", "ExcitationFrequency"],
|
|
"4026": ["CS", "ModulationType"],
|
|
"4028": ["DS", "Damping"],
|
|
"4030": ["SQ", "ReceiverSettingsSequence"],
|
|
"4031": ["DS", "AcquiredSoundpathLength"],
|
|
"4032": ["CS", "AcquisitionCompressionType"],
|
|
"4033": ["IS", "AcquisitionSampleSize"],
|
|
"4034": ["DS", "RectifierSmoothing"],
|
|
"4035": ["SQ", "DACSequence"],
|
|
"4036": ["CS", "DACType"],
|
|
"4038": ["DS", "DACGainPoints"],
|
|
"403A": ["DS", "DACTimePoints"],
|
|
"403C": ["DS", "DACAmplitude"],
|
|
"4040": ["SQ", "PreAmplifierSettingsSequence"],
|
|
"4050": ["SQ", "TransmitTransducerSettingsSequence"],
|
|
"4051": ["SQ", "ReceiveTransducerSettingsSequence"],
|
|
"4052": ["DS", "IncidentAngle"],
|
|
"4054": ["ST", "CouplingTechnique"],
|
|
"4056": ["ST", "CouplingMedium"],
|
|
"4057": ["DS", "CouplingVelocity"],
|
|
"4058": ["DS", "CrystalCenterLocationX"],
|
|
"4059": ["DS", "CrystalCenterLocationZ"],
|
|
"405A": ["DS", "SoundPathLength"],
|
|
"405C": ["ST", "DelayLawIdentifier"],
|
|
"4060": ["SQ", "GateSettingsSequence"],
|
|
"4062": ["DS", "GateThreshold"],
|
|
"4064": ["DS", "VelocityOfSound"],
|
|
"4070": ["SQ", "CalibrationSettingsSequence"],
|
|
"4072": ["ST", "CalibrationProcedure"],
|
|
"4074": ["SH", "ProcedureVersion"],
|
|
"4076": ["DA", "ProcedureCreationDate"],
|
|
"4078": ["DA", "ProcedureExpirationDate"],
|
|
"407A": ["DA", "ProcedureLastModifiedDate"],
|
|
"407C": ["TM", "CalibrationTime"],
|
|
"407E": ["DA", "CalibrationDate"],
|
|
"5002": ["IS", "LINACEnergy"],
|
|
"5004": ["IS", "LINACOutput"]
|
|
},
|
|
"0018": {
|
|
"0010": ["LO", "ContrastBolusAgent"],
|
|
"0012": ["SQ", "ContrastBolusAgentSequence"],
|
|
"0014": ["SQ", "ContrastBolusAdministrationRouteSequence"],
|
|
"0015": ["CS", "BodyPartExamined"],
|
|
"0020": ["CS", "ScanningSequence"],
|
|
"0021": ["CS", "SequenceVariant"],
|
|
"0022": ["CS", "ScanOptions"],
|
|
"0023": ["CS", "MRAcquisitionType"],
|
|
"0024": ["SH", "SequenceName"],
|
|
"0025": ["CS", "AngioFlag"],
|
|
"0026": ["SQ", "InterventionDrugInformationSequence"],
|
|
"0027": ["TM", "InterventionDrugStopTime"],
|
|
"0028": ["DS", "InterventionDrugDose"],
|
|
"0029": ["SQ", "InterventionDrugCodeSequence"],
|
|
"002A": ["SQ", "AdditionalDrugSequence"],
|
|
"0030": ["LO", "Radionuclide"],
|
|
"0031": ["LO", "Radiopharmaceutical"],
|
|
"0032": ["DS", "EnergyWindowCenterline"],
|
|
"0033": ["DS", "EnergyWindowTotalWidth"],
|
|
"0034": ["LO", "InterventionDrugName"],
|
|
"0035": ["TM", "InterventionDrugStartTime"],
|
|
"0036": ["SQ", "InterventionSequence"],
|
|
"0037": ["CS", "TherapyType"],
|
|
"0038": ["CS", "InterventionStatus"],
|
|
"0039": ["CS", "TherapyDescription"],
|
|
"003A": ["ST", "InterventionDescription"],
|
|
"0040": ["IS", "CineRate"],
|
|
"0042": ["CS", "InitialCineRunState"],
|
|
"0050": ["DS", "SliceThickness"],
|
|
"0060": ["DS", "KVP"],
|
|
"0070": ["IS", "CountsAccumulated"],
|
|
"0071": ["CS", "AcquisitionTerminationCondition"],
|
|
"0072": ["DS", "EffectiveDuration"],
|
|
"0073": ["CS", "AcquisitionStartCondition"],
|
|
"0074": ["IS", "AcquisitionStartConditionData"],
|
|
"0075": ["IS", "AcquisitionTerminationConditionData"],
|
|
"0080": ["DS", "RepetitionTime"],
|
|
"0081": ["DS", "EchoTime"],
|
|
"0082": ["DS", "InversionTime"],
|
|
"0083": ["DS", "NumberOfAverages"],
|
|
"0084": ["DS", "ImagingFrequency"],
|
|
"0085": ["SH", "ImagedNucleus"],
|
|
"0086": ["IS", "EchoNumbers"],
|
|
"0087": ["DS", "MagneticFieldStrength"],
|
|
"0088": ["DS", "SpacingBetweenSlices"],
|
|
"0089": ["IS", "NumberOfPhaseEncodingSteps"],
|
|
"0090": ["DS", "DataCollectionDiameter"],
|
|
"0091": ["IS", "EchoTrainLength"],
|
|
"0093": ["DS", "PercentSampling"],
|
|
"0094": ["DS", "PercentPhaseFieldOfView"],
|
|
"0095": ["DS", "PixelBandwidth"],
|
|
"1000": ["LO", "DeviceSerialNumber"],
|
|
"1002": ["UI", "DeviceUID"],
|
|
"1003": ["LO", "DeviceID"],
|
|
"1004": ["LO", "PlateID"],
|
|
"1005": ["LO", "GeneratorID"],
|
|
"1006": ["LO", "GridID"],
|
|
"1007": ["LO", "CassetteID"],
|
|
"1008": ["LO", "GantryID"],
|
|
"1010": ["LO", "SecondaryCaptureDeviceID"],
|
|
"1011": ["LO", "HardcopyCreationDeviceID"],
|
|
"1012": ["DA", "DateOfSecondaryCapture"],
|
|
"1014": ["TM", "TimeOfSecondaryCapture"],
|
|
"1016": ["LO", "SecondaryCaptureDeviceManufacturer"],
|
|
"1017": ["LO", "HardcopyDeviceManufacturer"],
|
|
"1018": ["LO", "SecondaryCaptureDeviceManufacturerModelName"],
|
|
"1019": ["LO", "SecondaryCaptureDeviceSoftwareVersions"],
|
|
"101A": ["LO", "HardcopyDeviceSoftwareVersion"],
|
|
"101B": ["LO", "HardcopyDeviceManufacturerModelName"],
|
|
"1020": ["LO", "SoftwareVersions"],
|
|
"1022": ["SH", "VideoImageFormatAcquired"],
|
|
"1023": ["LO", "DigitalImageFormatAcquired"],
|
|
"1030": ["LO", "ProtocolName"],
|
|
"1040": ["LO", "ContrastBolusRoute"],
|
|
"1041": ["DS", "ContrastBolusVolume"],
|
|
"1042": ["TM", "ContrastBolusStartTime"],
|
|
"1043": ["TM", "ContrastBolusStopTime"],
|
|
"1044": ["DS", "ContrastBolusTotalDose"],
|
|
"1045": ["IS", "SyringeCounts"],
|
|
"1046": ["DS", "ContrastFlowRate"],
|
|
"1047": ["DS", "ContrastFlowDuration"],
|
|
"1048": ["CS", "ContrastBolusIngredient"],
|
|
"1049": ["DS", "ContrastBolusIngredientConcentration"],
|
|
"1050": ["DS", "SpatialResolution"],
|
|
"1060": ["DS", "TriggerTime"],
|
|
"1061": ["LO", "TriggerSourceOrType"],
|
|
"1062": ["IS", "NominalInterval"],
|
|
"1063": ["DS", "FrameTime"],
|
|
"1064": ["LO", "CardiacFramingType"],
|
|
"1065": ["DS", "FrameTimeVector"],
|
|
"1066": ["DS", "FrameDelay"],
|
|
"1067": ["DS", "ImageTriggerDelay"],
|
|
"1068": ["DS", "MultiplexGroupTimeOffset"],
|
|
"1069": ["DS", "TriggerTimeOffset"],
|
|
"106A": ["CS", "SynchronizationTrigger"],
|
|
"106C": ["US", "SynchronizationChannel"],
|
|
"106E": ["UL", "TriggerSamplePosition"],
|
|
"1070": ["LO", "RadiopharmaceuticalRoute"],
|
|
"1071": ["DS", "RadiopharmaceuticalVolume"],
|
|
"1072": ["TM", "RadiopharmaceuticalStartTime"],
|
|
"1073": ["TM", "RadiopharmaceuticalStopTime"],
|
|
"1074": ["DS", "RadionuclideTotalDose"],
|
|
"1075": ["DS", "RadionuclideHalfLife"],
|
|
"1076": ["DS", "RadionuclidePositronFraction"],
|
|
"1077": ["DS", "RadiopharmaceuticalSpecificActivity"],
|
|
"1078": ["DT", "RadiopharmaceuticalStartDateTime"],
|
|
"1079": ["DT", "RadiopharmaceuticalStopDateTime"],
|
|
"1080": ["CS", "BeatRejectionFlag"],
|
|
"1081": ["IS", "LowRRValue"],
|
|
"1082": ["IS", "HighRRValue"],
|
|
"1083": ["IS", "IntervalsAcquired"],
|
|
"1084": ["IS", "IntervalsRejected"],
|
|
"1085": ["LO", "PVCRejection"],
|
|
"1086": ["IS", "SkipBeats"],
|
|
"1088": ["IS", "HeartRate"],
|
|
"1090": ["IS", "CardiacNumberOfImages"],
|
|
"1094": ["IS", "TriggerWindow"],
|
|
"1100": ["DS", "ReconstructionDiameter"],
|
|
"1110": ["DS", "DistanceSourceToDetector"],
|
|
"1111": ["DS", "DistanceSourceToPatient"],
|
|
"1114": ["DS", "EstimatedRadiographicMagnificationFactor"],
|
|
"1120": ["DS", "GantryDetectorTilt"],
|
|
"1121": ["DS", "GantryDetectorSlew"],
|
|
"1130": ["DS", "TableHeight"],
|
|
"1131": ["DS", "TableTraverse"],
|
|
"1134": ["CS", "TableMotion"],
|
|
"1135": ["DS", "TableVerticalIncrement"],
|
|
"1136": ["DS", "TableLateralIncrement"],
|
|
"1137": ["DS", "TableLongitudinalIncrement"],
|
|
"1138": ["DS", "TableAngle"],
|
|
"113A": ["CS", "TableType"],
|
|
"1140": ["CS", "RotationDirection"],
|
|
"1141": ["DS", "AngularPosition"],
|
|
"1142": ["DS", "RadialPosition"],
|
|
"1143": ["DS", "ScanArc"],
|
|
"1144": ["DS", "AngularStep"],
|
|
"1145": ["DS", "CenterOfRotationOffset"],
|
|
"1146": ["DS", "RotationOffset"],
|
|
"1147": ["CS", "FieldOfViewShape"],
|
|
"1149": ["IS", "FieldOfViewDimensions"],
|
|
"1150": ["IS", "ExposureTime"],
|
|
"1151": ["IS", "XRayTubeCurrent"],
|
|
"1152": ["IS", "Exposure"],
|
|
"1153": ["IS", "ExposureInuAs"],
|
|
"1154": ["DS", "AveragePulseWidth"],
|
|
"1155": ["CS", "RadiationSetting"],
|
|
"1156": ["CS", "RectificationType"],
|
|
"115A": ["CS", "RadiationMode"],
|
|
"115E": ["DS", "ImageAndFluoroscopyAreaDoseProduct"],
|
|
"1160": ["SH", "FilterType"],
|
|
"1161": ["LO", "TypeOfFilters"],
|
|
"1162": ["DS", "IntensifierSize"],
|
|
"1164": ["DS", "ImagerPixelSpacing"],
|
|
"1166": ["CS", "Grid"],
|
|
"1170": ["IS", "GeneratorPower"],
|
|
"1180": ["SH", "CollimatorGridName"],
|
|
"1181": ["CS", "CollimatorType"],
|
|
"1182": ["IS", "FocalDistance"],
|
|
"1183": ["DS", "XFocusCenter"],
|
|
"1184": ["DS", "YFocusCenter"],
|
|
"1190": ["DS", "FocalSpots"],
|
|
"1191": ["CS", "AnodeTargetMaterial"],
|
|
"11A0": ["DS", "BodyPartThickness"],
|
|
"11A2": ["DS", "CompressionForce"],
|
|
"1200": ["DA", "DateOfLastCalibration"],
|
|
"1201": ["TM", "TimeOfLastCalibration"],
|
|
"1210": ["SH", "ConvolutionKernel"],
|
|
"1240": ["IS", "UpperLowerPixelValues"],
|
|
"1242": ["IS", "ActualFrameDuration"],
|
|
"1243": ["IS", "CountRate"],
|
|
"1244": ["US", "PreferredPlaybackSequencing"],
|
|
"1250": ["SH", "ReceiveCoilName"],
|
|
"1251": ["SH", "TransmitCoilName"],
|
|
"1260": ["SH", "PlateType"],
|
|
"1261": ["LO", "PhosphorType"],
|
|
"1300": ["DS", "ScanVelocity"],
|
|
"1301": ["CS", "WholeBodyTechnique"],
|
|
"1302": ["IS", "ScanLength"],
|
|
"1310": ["US", "AcquisitionMatrix"],
|
|
"1312": ["CS", "InPlanePhaseEncodingDirection"],
|
|
"1314": ["DS", "FlipAngle"],
|
|
"1315": ["CS", "VariableFlipAngleFlag"],
|
|
"1316": ["DS", "SAR"],
|
|
"1318": ["DS", "dBdt"],
|
|
"1400": ["LO", "AcquisitionDeviceProcessingDescription"],
|
|
"1401": ["LO", "AcquisitionDeviceProcessingCode"],
|
|
"1402": ["CS", "CassetteOrientation"],
|
|
"1403": ["CS", "CassetteSize"],
|
|
"1404": ["US", "ExposuresOnPlate"],
|
|
"1405": ["IS", "RelativeXRayExposure"],
|
|
"1411": ["DS", "ExposureIndex"],
|
|
"1412": ["DS", "TargetExposureIndex"],
|
|
"1413": ["DS", "DeviationIndex"],
|
|
"1450": ["DS", "ColumnAngulation"],
|
|
"1460": ["DS", "TomoLayerHeight"],
|
|
"1470": ["DS", "TomoAngle"],
|
|
"1480": ["DS", "TomoTime"],
|
|
"1490": ["CS", "TomoType"],
|
|
"1491": ["CS", "TomoClass"],
|
|
"1495": ["IS", "NumberOfTomosynthesisSourceImages"],
|
|
"1500": ["CS", "PositionerMotion"],
|
|
"1508": ["CS", "PositionerType"],
|
|
"1510": ["DS", "PositionerPrimaryAngle"],
|
|
"1511": ["DS", "PositionerSecondaryAngle"],
|
|
"1520": ["DS", "PositionerPrimaryAngleIncrement"],
|
|
"1521": ["DS", "PositionerSecondaryAngleIncrement"],
|
|
"1530": ["DS", "DetectorPrimaryAngle"],
|
|
"1531": ["DS", "DetectorSecondaryAngle"],
|
|
"1600": ["CS", "ShutterShape"],
|
|
"1602": ["IS", "ShutterLeftVerticalEdge"],
|
|
"1604": ["IS", "ShutterRightVerticalEdge"],
|
|
"1606": ["IS", "ShutterUpperHorizontalEdge"],
|
|
"1608": ["IS", "ShutterLowerHorizontalEdge"],
|
|
"1610": ["IS", "CenterOfCircularShutter"],
|
|
"1612": ["IS", "RadiusOfCircularShutter"],
|
|
"1620": ["IS", "VerticesOfThePolygonalShutter"],
|
|
"1622": ["US", "ShutterPresentationValue"],
|
|
"1623": ["US", "ShutterOverlayGroup"],
|
|
"1624": ["US", "ShutterPresentationColorCIELabValue"],
|
|
"1700": ["CS", "CollimatorShape"],
|
|
"1702": ["IS", "CollimatorLeftVerticalEdge"],
|
|
"1704": ["IS", "CollimatorRightVerticalEdge"],
|
|
"1706": ["IS", "CollimatorUpperHorizontalEdge"],
|
|
"1708": ["IS", "CollimatorLowerHorizontalEdge"],
|
|
"1710": ["IS", "CenterOfCircularCollimator"],
|
|
"1712": ["IS", "RadiusOfCircularCollimator"],
|
|
"1720": ["IS", "VerticesOfThePolygonalCollimator"],
|
|
"1800": ["CS", "AcquisitionTimeSynchronized"],
|
|
"1801": ["SH", "TimeSource"],
|
|
"1802": ["CS", "TimeDistributionProtocol"],
|
|
"1803": ["LO", "NTPSourceAddress"],
|
|
"2001": ["IS", "PageNumberVector"],
|
|
"2002": ["SH", "FrameLabelVector"],
|
|
"2003": ["DS", "FramePrimaryAngleVector"],
|
|
"2004": ["DS", "FrameSecondaryAngleVector"],
|
|
"2005": ["DS", "SliceLocationVector"],
|
|
"2006": ["SH", "DisplayWindowLabelVector"],
|
|
"2010": ["DS", "NominalScannedPixelSpacing"],
|
|
"2020": ["CS", "DigitizingDeviceTransportDirection"],
|
|
"2030": ["DS", "RotationOfScannedFilm"],
|
|
"3100": ["CS", "IVUSAcquisition"],
|
|
"3101": ["DS", "IVUSPullbackRate"],
|
|
"3102": ["DS", "IVUSGatedRate"],
|
|
"3103": ["IS", "IVUSPullbackStartFrameNumber"],
|
|
"3104": ["IS", "IVUSPullbackStopFrameNumber"],
|
|
"3105": ["IS", "LesionNumber"],
|
|
"4000": ["LT", "AcquisitionComments"],
|
|
"5000": ["SH", "OutputPower"],
|
|
"5010": ["LO", "TransducerData"],
|
|
"5012": ["DS", "FocusDepth"],
|
|
"5020": ["LO", "ProcessingFunction"],
|
|
"5021": ["LO", "PostprocessingFunction"],
|
|
"5022": ["DS", "MechanicalIndex"],
|
|
"5024": ["DS", "BoneThermalIndex"],
|
|
"5026": ["DS", "CranialThermalIndex"],
|
|
"5027": ["DS", "SoftTissueThermalIndex"],
|
|
"5028": ["DS", "SoftTissueFocusThermalIndex"],
|
|
"5029": ["DS", "SoftTissueSurfaceThermalIndex"],
|
|
"5030": ["DS", "DynamicRange"],
|
|
"5040": ["DS", "TotalGain"],
|
|
"5050": ["IS", "DepthOfScanField"],
|
|
"5100": ["CS", "PatientPosition"],
|
|
"5101": ["CS", "ViewPosition"],
|
|
"5104": ["SQ", "ProjectionEponymousNameCodeSequence"],
|
|
"5210": ["DS", "ImageTransformationMatrix"],
|
|
"5212": ["DS", "ImageTranslationVector"],
|
|
"6000": ["DS", "Sensitivity"],
|
|
"6011": ["SQ", "SequenceOfUltrasoundRegions"],
|
|
"6012": ["US", "RegionSpatialFormat"],
|
|
"6014": ["US", "RegionDataType"],
|
|
"6016": ["UL", "RegionFlags"],
|
|
"6018": ["UL", "RegionLocationMinX0"],
|
|
"601A": ["UL", "RegionLocationMinY0"],
|
|
"601C": ["UL", "RegionLocationMaxX1"],
|
|
"601E": ["UL", "RegionLocationMaxY1"],
|
|
"6020": ["SL", "ReferencePixelX0"],
|
|
"6022": ["SL", "ReferencePixelY0"],
|
|
"6024": ["US", "PhysicalUnitsXDirection"],
|
|
"6026": ["US", "PhysicalUnitsYDirection"],
|
|
"6028": ["FD", "ReferencePixelPhysicalValueX"],
|
|
"602A": ["FD", "ReferencePixelPhysicalValueY"],
|
|
"602C": ["FD", "PhysicalDeltaX"],
|
|
"602E": ["FD", "PhysicalDeltaY"],
|
|
"6030": ["UL", "TransducerFrequency"],
|
|
"6031": ["CS", "TransducerType"],
|
|
"6032": ["UL", "PulseRepetitionFrequency"],
|
|
"6034": ["FD", "DopplerCorrectionAngle"],
|
|
"6036": ["FD", "SteeringAngle"],
|
|
"6038": ["UL", "DopplerSampleVolumeXPositionRetired"],
|
|
"6039": ["SL", "DopplerSampleVolumeXPosition"],
|
|
"603A": ["UL", "DopplerSampleVolumeYPositionRetired"],
|
|
"603B": ["SL", "DopplerSampleVolumeYPosition"],
|
|
"603C": ["UL", "TMLinePositionX0Retired"],
|
|
"603D": ["SL", "TMLinePositionX0"],
|
|
"603E": ["UL", "TMLinePositionY0Retired"],
|
|
"603F": ["SL", "TMLinePositionY0"],
|
|
"6040": ["UL", "TMLinePositionX1Retired"],
|
|
"6041": ["SL", "TMLinePositionX1"],
|
|
"6042": ["UL", "TMLinePositionY1Retired"],
|
|
"6043": ["SL", "TMLinePositionY1"],
|
|
"6044": ["US", "PixelComponentOrganization"],
|
|
"6046": ["UL", "PixelComponentMask"],
|
|
"6048": ["UL", "PixelComponentRangeStart"],
|
|
"604A": ["UL", "PixelComponentRangeStop"],
|
|
"604C": ["US", "PixelComponentPhysicalUnits"],
|
|
"604E": ["US", "PixelComponentDataType"],
|
|
"6050": ["UL", "NumberOfTableBreakPoints"],
|
|
"6052": ["UL", "TableOfXBreakPoints"],
|
|
"6054": ["FD", "TableOfYBreakPoints"],
|
|
"6056": ["UL", "NumberOfTableEntries"],
|
|
"6058": ["UL", "TableOfPixelValues"],
|
|
"605A": ["FL", "TableOfParameterValues"],
|
|
"6060": ["FL", "RWaveTimeVector"],
|
|
"7000": ["CS", "DetectorConditionsNominalFlag"],
|
|
"7001": ["DS", "DetectorTemperature"],
|
|
"7004": ["CS", "DetectorType"],
|
|
"7005": ["CS", "DetectorConfiguration"],
|
|
"7006": ["LT", "DetectorDescription"],
|
|
"7008": ["LT", "DetectorMode"],
|
|
"700A": ["SH", "DetectorID"],
|
|
"700C": ["DA", "DateOfLastDetectorCalibration"],
|
|
"700E": ["TM", "TimeOfLastDetectorCalibration"],
|
|
"7010": ["IS", "ExposuresOnDetectorSinceLastCalibration"],
|
|
"7011": ["IS", "ExposuresOnDetectorSinceManufactured"],
|
|
"7012": ["DS", "DetectorTimeSinceLastExposure"],
|
|
"7014": ["DS", "DetectorActiveTime"],
|
|
"7016": ["DS", "DetectorActivationOffsetFromExposure"],
|
|
"701A": ["DS", "DetectorBinning"],
|
|
"7020": ["DS", "DetectorElementPhysicalSize"],
|
|
"7022": ["DS", "DetectorElementSpacing"],
|
|
"7024": ["CS", "DetectorActiveShape"],
|
|
"7026": ["DS", "DetectorActiveDimensions"],
|
|
"7028": ["DS", "DetectorActiveOrigin"],
|
|
"702A": ["LO", "DetectorManufacturerName"],
|
|
"702B": ["LO", "DetectorManufacturerModelName"],
|
|
"7030": ["DS", "FieldOfViewOrigin"],
|
|
"7032": ["DS", "FieldOfViewRotation"],
|
|
"7034": ["CS", "FieldOfViewHorizontalFlip"],
|
|
"7036": ["FL", "PixelDataAreaOriginRelativeToFOV"],
|
|
"7038": ["FL", "PixelDataAreaRotationAngleRelativeToFOV"],
|
|
"7040": ["LT", "GridAbsorbingMaterial"],
|
|
"7041": ["LT", "GridSpacingMaterial"],
|
|
"7042": ["DS", "GridThickness"],
|
|
"7044": ["DS", "GridPitch"],
|
|
"7046": ["IS", "GridAspectRatio"],
|
|
"7048": ["DS", "GridPeriod"],
|
|
"704C": ["DS", "GridFocalDistance"],
|
|
"7050": ["CS", "FilterMaterial"],
|
|
"7052": ["DS", "FilterThicknessMinimum"],
|
|
"7054": ["DS", "FilterThicknessMaximum"],
|
|
"7056": ["FL", "FilterBeamPathLengthMinimum"],
|
|
"7058": ["FL", "FilterBeamPathLengthMaximum"],
|
|
"7060": ["CS", "ExposureControlMode"],
|
|
"7062": ["LT", "ExposureControlModeDescription"],
|
|
"7064": ["CS", "ExposureStatus"],
|
|
"7065": ["DS", "PhototimerSetting"],
|
|
"8150": ["DS", "ExposureTimeInuS"],
|
|
"8151": ["DS", "XRayTubeCurrentInuA"],
|
|
"9004": ["CS", "ContentQualification"],
|
|
"9005": ["SH", "PulseSequenceName"],
|
|
"9006": ["SQ", "MRImagingModifierSequence"],
|
|
"9008": ["CS", "EchoPulseSequence"],
|
|
"9009": ["CS", "InversionRecovery"],
|
|
"9010": ["CS", "FlowCompensation"],
|
|
"9011": ["CS", "MultipleSpinEcho"],
|
|
"9012": ["CS", "MultiPlanarExcitation"],
|
|
"9014": ["CS", "PhaseContrast"],
|
|
"9015": ["CS", "TimeOfFlightContrast"],
|
|
"9016": ["CS", "Spoiling"],
|
|
"9017": ["CS", "SteadyStatePulseSequence"],
|
|
"9018": ["CS", "EchoPlanarPulseSequence"],
|
|
"9019": ["FD", "TagAngleFirstAxis"],
|
|
"9020": ["CS", "MagnetizationTransfer"],
|
|
"9021": ["CS", "T2Preparation"],
|
|
"9022": ["CS", "BloodSignalNulling"],
|
|
"9024": ["CS", "SaturationRecovery"],
|
|
"9025": ["CS", "SpectrallySelectedSuppression"],
|
|
"9026": ["CS", "SpectrallySelectedExcitation"],
|
|
"9027": ["CS", "SpatialPresaturation"],
|
|
"9028": ["CS", "Tagging"],
|
|
"9029": ["CS", "OversamplingPhase"],
|
|
"9030": ["FD", "TagSpacingFirstDimension"],
|
|
"9032": ["CS", "GeometryOfKSpaceTraversal"],
|
|
"9033": ["CS", "SegmentedKSpaceTraversal"],
|
|
"9034": ["CS", "RectilinearPhaseEncodeReordering"],
|
|
"9035": ["FD", "TagThickness"],
|
|
"9036": ["CS", "PartialFourierDirection"],
|
|
"9037": ["CS", "CardiacSynchronizationTechnique"],
|
|
"9041": ["LO", "ReceiveCoilManufacturerName"],
|
|
"9042": ["SQ", "MRReceiveCoilSequence"],
|
|
"9043": ["CS", "ReceiveCoilType"],
|
|
"9044": ["CS", "QuadratureReceiveCoil"],
|
|
"9045": ["SQ", "MultiCoilDefinitionSequence"],
|
|
"9046": ["LO", "MultiCoilConfiguration"],
|
|
"9047": ["SH", "MultiCoilElementName"],
|
|
"9048": ["CS", "MultiCoilElementUsed"],
|
|
"9049": ["SQ", "MRTransmitCoilSequence"],
|
|
"9050": ["LO", "TransmitCoilManufacturerName"],
|
|
"9051": ["CS", "TransmitCoilType"],
|
|
"9052": ["FD", "SpectralWidth"],
|
|
"9053": ["FD", "ChemicalShiftReference"],
|
|
"9054": ["CS", "VolumeLocalizationTechnique"],
|
|
"9058": ["US", "MRAcquisitionFrequencyEncodingSteps"],
|
|
"9059": ["CS", "Decoupling"],
|
|
"9060": ["CS", "DecoupledNucleus"],
|
|
"9061": ["FD", "DecouplingFrequency"],
|
|
"9062": ["CS", "DecouplingMethod"],
|
|
"9063": ["FD", "DecouplingChemicalShiftReference"],
|
|
"9064": ["CS", "KSpaceFiltering"],
|
|
"9065": ["CS", "TimeDomainFiltering"],
|
|
"9066": ["US", "NumberOfZeroFills"],
|
|
"9067": ["CS", "BaselineCorrection"],
|
|
"9069": ["FD", "ParallelReductionFactorInPlane"],
|
|
"9070": ["FD", "CardiacRRIntervalSpecified"],
|
|
"9073": ["FD", "AcquisitionDuration"],
|
|
"9074": ["DT", "FrameAcquisitionDateTime"],
|
|
"9075": ["CS", "DiffusionDirectionality"],
|
|
"9076": ["SQ", "DiffusionGradientDirectionSequence"],
|
|
"9077": ["CS", "ParallelAcquisition"],
|
|
"9078": ["CS", "ParallelAcquisitionTechnique"],
|
|
"9079": ["FD", "InversionTimes"],
|
|
"9080": ["ST", "MetaboliteMapDescription"],
|
|
"9081": ["CS", "PartialFourier"],
|
|
"9082": ["FD", "EffectiveEchoTime"],
|
|
"9083": ["SQ", "MetaboliteMapCodeSequence"],
|
|
"9084": ["SQ", "ChemicalShiftSequence"],
|
|
"9085": ["CS", "CardiacSignalSource"],
|
|
"9087": ["FD", "DiffusionBValue"],
|
|
"9089": ["FD", "DiffusionGradientOrientation"],
|
|
"9090": ["FD", "VelocityEncodingDirection"],
|
|
"9091": ["FD", "VelocityEncodingMinimumValue"],
|
|
"9092": ["SQ", "VelocityEncodingAcquisitionSequence"],
|
|
"9093": ["US", "NumberOfKSpaceTrajectories"],
|
|
"9094": ["CS", "CoverageOfKSpace"],
|
|
"9095": ["UL", "SpectroscopyAcquisitionPhaseRows"],
|
|
"9096": ["FD", "ParallelReductionFactorInPlaneRetired"],
|
|
"9098": ["FD", "TransmitterFrequency"],
|
|
"9100": ["CS", "ResonantNucleus"],
|
|
"9101": ["CS", "FrequencyCorrection"],
|
|
"9103": ["SQ", "MRSpectroscopyFOVGeometrySequence"],
|
|
"9104": ["FD", "SlabThickness"],
|
|
"9105": ["FD", "SlabOrientation"],
|
|
"9106": ["FD", "MidSlabPosition"],
|
|
"9107": ["SQ", "MRSpatialSaturationSequence"],
|
|
"9112": ["SQ", "MRTimingAndRelatedParametersSequence"],
|
|
"9114": ["SQ", "MREchoSequence"],
|
|
"9115": ["SQ", "MRModifierSequence"],
|
|
"9117": ["SQ", "MRDiffusionSequence"],
|
|
"9118": ["SQ", "CardiacSynchronizationSequence"],
|
|
"9119": ["SQ", "MRAveragesSequence"],
|
|
"9125": ["SQ", "MRFOVGeometrySequence"],
|
|
"9126": ["SQ", "VolumeLocalizationSequence"],
|
|
"9127": ["UL", "SpectroscopyAcquisitionDataColumns"],
|
|
"9147": ["CS", "DiffusionAnisotropyType"],
|
|
"9151": ["DT", "FrameReferenceDateTime"],
|
|
"9152": ["SQ", "MRMetaboliteMapSequence"],
|
|
"9155": ["FD", "ParallelReductionFactorOutOfPlane"],
|
|
"9159": ["UL", "SpectroscopyAcquisitionOutOfPlanePhaseSteps"],
|
|
"9166": ["CS", "BulkMotionStatus"],
|
|
"9168": ["FD", "ParallelReductionFactorSecondInPlane"],
|
|
"9169": ["CS", "CardiacBeatRejectionTechnique"],
|
|
"9170": ["CS", "RespiratoryMotionCompensationTechnique"],
|
|
"9171": ["CS", "RespiratorySignalSource"],
|
|
"9172": ["CS", "BulkMotionCompensationTechnique"],
|
|
"9173": ["CS", "BulkMotionSignalSource"],
|
|
"9174": ["CS", "ApplicableSafetyStandardAgency"],
|
|
"9175": ["LO", "ApplicableSafetyStandardDescription"],
|
|
"9176": ["SQ", "OperatingModeSequence"],
|
|
"9177": ["CS", "OperatingModeType"],
|
|
"9178": ["CS", "OperatingMode"],
|
|
"9179": ["CS", "SpecificAbsorptionRateDefinition"],
|
|
"9180": ["CS", "GradientOutputType"],
|
|
"9181": ["FD", "SpecificAbsorptionRateValue"],
|
|
"9182": ["FD", "GradientOutput"],
|
|
"9183": ["CS", "FlowCompensationDirection"],
|
|
"9184": ["FD", "TaggingDelay"],
|
|
"9185": ["ST", "RespiratoryMotionCompensationTechniqueDescription"],
|
|
"9186": ["SH", "RespiratorySignalSourceID"],
|
|
"9195": ["FD", "ChemicalShiftMinimumIntegrationLimitInHz"],
|
|
"9196": ["FD", "ChemicalShiftMaximumIntegrationLimitInHz"],
|
|
"9197": ["SQ", "MRVelocityEncodingSequence"],
|
|
"9198": ["CS", "FirstOrderPhaseCorrection"],
|
|
"9199": ["CS", "WaterReferencedPhaseCorrection"],
|
|
"9200": ["CS", "MRSpectroscopyAcquisitionType"],
|
|
"9214": ["CS", "RespiratoryCyclePosition"],
|
|
"9217": ["FD", "VelocityEncodingMaximumValue"],
|
|
"9218": ["FD", "TagSpacingSecondDimension"],
|
|
"9219": ["SS", "TagAngleSecondAxis"],
|
|
"9220": ["FD", "FrameAcquisitionDuration"],
|
|
"9226": ["SQ", "MRImageFrameTypeSequence"],
|
|
"9227": ["SQ", "MRSpectroscopyFrameTypeSequence"],
|
|
"9231": ["US", "MRAcquisitionPhaseEncodingStepsInPlane"],
|
|
"9232": ["US", "MRAcquisitionPhaseEncodingStepsOutOfPlane"],
|
|
"9234": ["UL", "SpectroscopyAcquisitionPhaseColumns"],
|
|
"9236": ["CS", "CardiacCyclePosition"],
|
|
"9239": ["SQ", "SpecificAbsorptionRateSequence"],
|
|
"9240": ["US", "RFEchoTrainLength"],
|
|
"9241": ["US", "GradientEchoTrainLength"],
|
|
"9250": ["CS", "ArterialSpinLabelingContrast"],
|
|
"9251": ["SQ", "MRArterialSpinLabelingSequence"],
|
|
"9252": ["LO", "ASLTechniqueDescription"],
|
|
"9253": ["US", "ASLSlabNumber"],
|
|
"9254": ["FD ", "ASLSlabThickness"],
|
|
"9255": ["FD ", "ASLSlabOrientation"],
|
|
"9256": ["FD ", "ASLMidSlabPosition"],
|
|
"9257": ["CS", "ASLContext"],
|
|
"9258": ["UL", "ASLPulseTrainDuration"],
|
|
"9259": ["CS", "ASLCrusherFlag"],
|
|
"925A": ["FD", "ASLCrusherFlow"],
|
|
"925B": ["LO", "ASLCrusherDescription"],
|
|
"925C": ["CS", "ASLBolusCutoffFlag"],
|
|
"925D": ["SQ", "ASLBolusCutoffTimingSequence"],
|
|
"925E": ["LO", "ASLBolusCutoffTechnique"],
|
|
"925F": ["UL", "ASLBolusCutoffDelayTime"],
|
|
"9260": ["SQ", "ASLSlabSequence"],
|
|
"9295": ["FD", "ChemicalShiftMinimumIntegrationLimitInppm"],
|
|
"9296": ["FD", "ChemicalShiftMaximumIntegrationLimitInppm"],
|
|
"9301": ["SQ", "CTAcquisitionTypeSequence"],
|
|
"9302": ["CS", "AcquisitionType"],
|
|
"9303": ["FD", "TubeAngle"],
|
|
"9304": ["SQ", "CTAcquisitionDetailsSequence"],
|
|
"9305": ["FD", "RevolutionTime"],
|
|
"9306": ["FD", "SingleCollimationWidth"],
|
|
"9307": ["FD", "TotalCollimationWidth"],
|
|
"9308": ["SQ", "CTTableDynamicsSequence"],
|
|
"9309": ["FD", "TableSpeed"],
|
|
"9310": ["FD", "TableFeedPerRotation"],
|
|
"9311": ["FD", "SpiralPitchFactor"],
|
|
"9312": ["SQ", "CTGeometrySequence"],
|
|
"9313": ["FD", "DataCollectionCenterPatient"],
|
|
"9314": ["SQ", "CTReconstructionSequence"],
|
|
"9315": ["CS", "ReconstructionAlgorithm"],
|
|
"9316": ["CS", "ConvolutionKernelGroup"],
|
|
"9317": ["FD", "ReconstructionFieldOfView"],
|
|
"9318": ["FD", "ReconstructionTargetCenterPatient"],
|
|
"9319": ["FD", "ReconstructionAngle"],
|
|
"9320": ["SH", "ImageFilter"],
|
|
"9321": ["SQ", "CTExposureSequence"],
|
|
"9322": ["FD", "ReconstructionPixelSpacing"],
|
|
"9323": ["CS", "ExposureModulationType"],
|
|
"9324": ["FD", "EstimatedDoseSaving"],
|
|
"9325": ["SQ", "CTXRayDetailsSequence"],
|
|
"9326": ["SQ", "CTPositionSequence"],
|
|
"9327": ["FD", "TablePosition"],
|
|
"9328": ["FD", "ExposureTimeInms"],
|
|
"9329": ["SQ", "CTImageFrameTypeSequence"],
|
|
"9330": ["FD", "XRayTubeCurrentInmA"],
|
|
"9332": ["FD", "ExposureInmAs"],
|
|
"9333": ["CS", "ConstantVolumeFlag"],
|
|
"9334": ["CS", "FluoroscopyFlag"],
|
|
"9335": ["FD", "DistanceSourceToDataCollectionCenter"],
|
|
"9337": ["US", "ContrastBolusAgentNumber"],
|
|
"9338": ["SQ", "ContrastBolusIngredientCodeSequence"],
|
|
"9340": ["SQ", "ContrastAdministrationProfileSequence"],
|
|
"9341": ["SQ", "ContrastBolusUsageSequence"],
|
|
"9342": ["CS", "ContrastBolusAgentAdministered"],
|
|
"9343": ["CS", "ContrastBolusAgentDetected"],
|
|
"9344": ["CS", "ContrastBolusAgentPhase"],
|
|
"9345": ["FD", "CTDIvol"],
|
|
"9346": ["SQ", "CTDIPhantomTypeCodeSequence"],
|
|
"9351": ["FL", "CalciumScoringMassFactorPatient"],
|
|
"9352": ["FL", "CalciumScoringMassFactorDevice"],
|
|
"9353": ["FL", "EnergyWeightingFactor"],
|
|
"9360": ["SQ", "CTAdditionalXRaySourceSequence"],
|
|
"9401": ["SQ", "ProjectionPixelCalibrationSequence"],
|
|
"9402": ["FL", "DistanceSourceToIsocenter"],
|
|
"9403": ["FL", "DistanceObjectToTableTop"],
|
|
"9404": ["FL", "ObjectPixelSpacingInCenterOfBeam"],
|
|
"9405": ["SQ", "PositionerPositionSequence"],
|
|
"9406": ["SQ", "TablePositionSequence"],
|
|
"9407": ["SQ", "CollimatorShapeSequence"],
|
|
"9410": ["CS", "PlanesInAcquisition"],
|
|
"9412": ["SQ", "XAXRFFrameCharacteristicsSequence"],
|
|
"9417": ["SQ", "FrameAcquisitionSequence"],
|
|
"9420": ["CS", "XRayReceptorType"],
|
|
"9423": ["LO", "AcquisitionProtocolName"],
|
|
"9424": ["LT", "AcquisitionProtocolDescription"],
|
|
"9425": ["CS", "ContrastBolusIngredientOpaque"],
|
|
"9426": ["FL", "DistanceReceptorPlaneToDetectorHousing"],
|
|
"9427": ["CS", "IntensifierActiveShape"],
|
|
"9428": ["FL", "IntensifierActiveDimensions"],
|
|
"9429": ["FL", "PhysicalDetectorSize"],
|
|
"9430": ["FL", "PositionOfIsocenterProjection"],
|
|
"9432": ["SQ", "FieldOfViewSequence"],
|
|
"9433": ["LO", "FieldOfViewDescription"],
|
|
"9434": ["SQ", "ExposureControlSensingRegionsSequence"],
|
|
"9435": ["CS", "ExposureControlSensingRegionShape"],
|
|
"9436": ["SS", "ExposureControlSensingRegionLeftVerticalEdge"],
|
|
"9437": ["SS", "ExposureControlSensingRegionRightVerticalEdge"],
|
|
"9438": ["SS", "ExposureControlSensingRegionUpperHorizontalEdge"],
|
|
"9439": ["SS", "ExposureControlSensingRegionLowerHorizontalEdge"],
|
|
"9440": ["SS", "CenterOfCircularExposureControlSensingRegion"],
|
|
"9441": ["US", "RadiusOfCircularExposureControlSensingRegion"],
|
|
"9442": ["SS", "VerticesOfThePolygonalExposureControlSensingRegion"],
|
|
"9447": ["FL", "ColumnAngulationPatient"],
|
|
"9449": ["FL", "BeamAngle"],
|
|
"9451": ["SQ", "FrameDetectorParametersSequence"],
|
|
"9452": ["FL", "CalculatedAnatomyThickness"],
|
|
"9455": ["SQ", "CalibrationSequence"],
|
|
"9456": ["SQ", "ObjectThicknessSequence"],
|
|
"9457": ["CS", "PlaneIdentification"],
|
|
"9461": ["FL", "FieldOfViewDimensionsInFloat"],
|
|
"9462": ["SQ", "IsocenterReferenceSystemSequence"],
|
|
"9463": ["FL", "PositionerIsocenterPrimaryAngle"],
|
|
"9464": ["FL", "PositionerIsocenterSecondaryAngle"],
|
|
"9465": ["FL", "PositionerIsocenterDetectorRotationAngle"],
|
|
"9466": ["FL", "TableXPositionToIsocenter"],
|
|
"9467": ["FL", "TableYPositionToIsocenter"],
|
|
"9468": ["FL", "TableZPositionToIsocenter"],
|
|
"9469": ["FL", "TableHorizontalRotationAngle"],
|
|
"9470": ["FL", "TableHeadTiltAngle"],
|
|
"9471": ["FL", "TableCradleTiltAngle"],
|
|
"9472": ["SQ", "FrameDisplayShutterSequence"],
|
|
"9473": ["FL", "AcquiredImageAreaDoseProduct"],
|
|
"9474": ["CS", "CArmPositionerTabletopRelationship"],
|
|
"9476": ["SQ", "XRayGeometrySequence"],
|
|
"9477": ["SQ", "IrradiationEventIdentificationSequence"],
|
|
"9504": ["SQ", "XRay3DFrameTypeSequence"],
|
|
"9506": ["SQ", "ContributingSourcesSequence"],
|
|
"9507": ["SQ", "XRay3DAcquisitionSequence"],
|
|
"9508": ["FL", "PrimaryPositionerScanArc"],
|
|
"9509": ["FL", "SecondaryPositionerScanArc"],
|
|
"9510": ["FL", "PrimaryPositionerScanStartAngle"],
|
|
"9511": ["FL", "SecondaryPositionerScanStartAngle"],
|
|
"9514": ["FL", "PrimaryPositionerIncrement"],
|
|
"9515": ["FL", "SecondaryPositionerIncrement"],
|
|
"9516": ["DT", "StartAcquisitionDateTime"],
|
|
"9517": ["DT", "EndAcquisitionDateTime"],
|
|
"9524": ["LO", "ApplicationName"],
|
|
"9525": ["LO", "ApplicationVersion"],
|
|
"9526": ["LO", "ApplicationManufacturer"],
|
|
"9527": ["CS", "AlgorithmType"],
|
|
"9528": ["LO", "AlgorithmDescription"],
|
|
"9530": ["SQ", "XRay3DReconstructionSequence"],
|
|
"9531": ["LO", "ReconstructionDescription"],
|
|
"9538": ["SQ", "PerProjectionAcquisitionSequence"],
|
|
"9601": ["SQ", "DiffusionBMatrixSequence"],
|
|
"9602": ["FD", "DiffusionBValueXX"],
|
|
"9603": ["FD", "DiffusionBValueXY"],
|
|
"9604": ["FD", "DiffusionBValueXZ"],
|
|
"9605": ["FD", "DiffusionBValueYY"],
|
|
"9606": ["FD", "DiffusionBValueYZ"],
|
|
"9607": ["FD", "DiffusionBValueZZ"],
|
|
"9701": ["DT", "DecayCorrectionDateTime"],
|
|
"9715": ["FD", "StartDensityThreshold"],
|
|
"9716": ["FD", "StartRelativeDensityDifferenceThreshold"],
|
|
"9717": ["FD", "StartCardiacTriggerCountThreshold"],
|
|
"9718": ["FD", "StartRespiratoryTriggerCountThreshold"],
|
|
"9719": ["FD", "TerminationCountsThreshold"],
|
|
"9720": ["FD", "TerminationDensityThreshold"],
|
|
"9721": ["FD", "TerminationRelativeDensityThreshold"],
|
|
"9722": ["FD", "TerminationTimeThreshold"],
|
|
"9723": ["FD", "TerminationCardiacTriggerCountThreshold"],
|
|
"9724": ["FD", "TerminationRespiratoryTriggerCountThreshold"],
|
|
"9725": ["CS", "DetectorGeometry"],
|
|
"9726": ["FD", "TransverseDetectorSeparation"],
|
|
"9727": ["FD", "AxialDetectorDimension"],
|
|
"9729": ["US", "RadiopharmaceuticalAgentNumber"],
|
|
"9732": ["SQ", "PETFrameAcquisitionSequence"],
|
|
"9733": ["SQ", "PETDetectorMotionDetailsSequence"],
|
|
"9734": ["SQ", "PETTableDynamicsSequence"],
|
|
"9735": ["SQ", "PETPositionSequence"],
|
|
"9736": ["SQ", "PETFrameCorrectionFactorsSequence"],
|
|
"9737": ["SQ", "RadiopharmaceuticalUsageSequence"],
|
|
"9738": ["CS", "AttenuationCorrectionSource"],
|
|
"9739": ["US", "NumberOfIterations"],
|
|
"9740": ["US", "NumberOfSubsets"],
|
|
"9749": ["SQ", "PETReconstructionSequence"],
|
|
"9751": ["SQ", "PETFrameTypeSequence"],
|
|
"9755": ["CS", "TimeOfFlightInformationUsed"],
|
|
"9756": ["CS", "ReconstructionType"],
|
|
"9758": ["CS", "DecayCorrected"],
|
|
"9759": ["CS", "AttenuationCorrected"],
|
|
"9760": ["CS", "ScatterCorrected"],
|
|
"9761": ["CS", "DeadTimeCorrected"],
|
|
"9762": ["CS", "GantryMotionCorrected"],
|
|
"9763": ["CS", "PatientMotionCorrected"],
|
|
"9764": ["CS", "CountLossNormalizationCorrected"],
|
|
"9765": ["CS", "RandomsCorrected"],
|
|
"9766": ["CS", "NonUniformRadialSamplingCorrected"],
|
|
"9767": ["CS", "SensitivityCalibrated"],
|
|
"9768": ["CS", "DetectorNormalizationCorrection"],
|
|
"9769": ["CS", "IterativeReconstructionMethod"],
|
|
"9770": ["CS", "AttenuationCorrectionTemporalRelationship"],
|
|
"9771": ["SQ", "PatientPhysiologicalStateSequence"],
|
|
"9772": ["SQ", "PatientPhysiologicalStateCodeSequence"],
|
|
"9801": ["FD", "DepthsOfFocus"],
|
|
"9803": ["SQ", "ExcludedIntervalsSequence"],
|
|
"9804": ["DT", "ExclusionStartDatetime"],
|
|
"9805": ["FD", "ExclusionDuration"],
|
|
"9806": ["SQ", "USImageDescriptionSequence"],
|
|
"9807": ["SQ", "ImageDataTypeSequence"],
|
|
"9808": ["CS", "DataType"],
|
|
"9809": ["SQ", "TransducerScanPatternCodeSequence"],
|
|
"980B": ["CS", "AliasedDataType"],
|
|
"980C": ["CS", "PositionMeasuringDeviceUsed"],
|
|
"980D": ["SQ", "TransducerGeometryCodeSequence"],
|
|
"980E": ["SQ", "TransducerBeamSteeringCodeSequence"],
|
|
"980F": ["SQ", "TransducerApplicationCodeSequence"],
|
|
"A001": ["SQ", "ContributingEquipmentSequence"],
|
|
"A002": ["DT", "ContributionDateTime"],
|
|
"A003": ["ST", "ContributionDescription"]
|
|
},
|
|
"0020": {
|
|
"000D": ["UI", "StudyInstanceUID"],
|
|
"000E": ["UI", "SeriesInstanceUID"],
|
|
"0010": ["SH", "StudyID"],
|
|
"0011": ["IS", "SeriesNumber"],
|
|
"0012": ["IS", "AcquisitionNumber"],
|
|
"0013": ["IS", "InstanceNumber"],
|
|
"0014": ["IS", "IsotopeNumber"],
|
|
"0015": ["IS", "PhaseNumber"],
|
|
"0016": ["IS", "IntervalNumber"],
|
|
"0017": ["IS", "TimeSlotNumber"],
|
|
"0018": ["IS", "AngleNumber"],
|
|
"0019": ["IS", "ItemNumber"],
|
|
"0020": ["CS", "PatientOrientation"],
|
|
"0022": ["IS", "OverlayNumber"],
|
|
"0024": ["IS", "CurveNumber"],
|
|
"0026": ["IS", "LUTNumber"],
|
|
"0030": ["DS", "ImagePosition"],
|
|
"0032": ["DS", "ImagePositionPatient"],
|
|
"0035": ["DS", "ImageOrientation"],
|
|
"0037": ["DS", "ImageOrientationPatient"],
|
|
"0050": ["DS", "Location"],
|
|
"0052": ["UI", "FrameOfReferenceUID"],
|
|
"0060": ["CS", "Laterality"],
|
|
"0062": ["CS", "ImageLaterality"],
|
|
"0070": ["LO", "ImageGeometryType"],
|
|
"0080": ["CS", "MaskingImage"],
|
|
"00AA": ["IS", "ReportNumber"],
|
|
"0100": ["IS", "TemporalPositionIdentifier"],
|
|
"0105": ["IS", "NumberOfTemporalPositions"],
|
|
"0110": ["DS", "TemporalResolution"],
|
|
"0200": ["UI", "SynchronizationFrameOfReferenceUID"],
|
|
"0242": ["UI", "SOPInstanceUIDOfConcatenationSource"],
|
|
"1000": ["IS", "SeriesInStudy"],
|
|
"1001": ["IS", "AcquisitionsInSeries"],
|
|
"1002": ["IS", "ImagesInAcquisition"],
|
|
"1003": ["IS", "ImagesInSeries"],
|
|
"1004": ["IS", "AcquisitionsInStudy"],
|
|
"1005": ["IS", "ImagesInStudy"],
|
|
"1020": ["LO", "Reference"],
|
|
"1040": ["LO", "PositionReferenceIndicator"],
|
|
"1041": ["DS", "SliceLocation"],
|
|
"1070": ["IS", "OtherStudyNumbers"],
|
|
"1200": ["IS", "NumberOfPatientRelatedStudies"],
|
|
"1202": ["IS", "NumberOfPatientRelatedSeries"],
|
|
"1204": ["IS", "NumberOfPatientRelatedInstances"],
|
|
"1206": ["IS", "NumberOfStudyRelatedSeries"],
|
|
"1208": ["IS", "NumberOfStudyRelatedInstances"],
|
|
"1209": ["IS", "NumberOfSeriesRelatedInstances"],
|
|
"3401": ["CS", "ModifyingDeviceID"],
|
|
"3402": ["CS", "ModifiedImageID"],
|
|
"3403": ["DA", "ModifiedImageDate"],
|
|
"3404": ["LO", "ModifyingDeviceManufacturer"],
|
|
"3405": ["TM", "ModifiedImageTime"],
|
|
"3406": ["LO", "ModifiedImageDescription"],
|
|
"4000": ["LT", "ImageComments"],
|
|
"5000": ["AT", "OriginalImageIdentification"],
|
|
"5002": ["LO", "OriginalImageIdentificationNomenclature"],
|
|
"9056": ["SH", "StackID"],
|
|
"9057": ["UL", "InStackPositionNumber"],
|
|
"9071": ["SQ", "FrameAnatomySequence"],
|
|
"9072": ["CS", "FrameLaterality"],
|
|
"9111": ["SQ", "FrameContentSequence"],
|
|
"9113": ["SQ", "PlanePositionSequence"],
|
|
"9116": ["SQ", "PlaneOrientationSequence"],
|
|
"9128": ["UL", "TemporalPositionIndex"],
|
|
"9153": ["FD", "NominalCardiacTriggerDelayTime"],
|
|
"9154": ["FL", "NominalCardiacTriggerTimePriorToRPeak"],
|
|
"9155": ["FL", "ActualCardiacTriggerTimePriorToRPeak"],
|
|
"9156": ["US", "FrameAcquisitionNumber"],
|
|
"9157": ["UL", "DimensionIndexValues"],
|
|
"9158": ["LT", "FrameComments"],
|
|
"9161": ["UI", "ConcatenationUID"],
|
|
"9162": ["US", "InConcatenationNumber"],
|
|
"9163": ["US", "InConcatenationTotalNumber"],
|
|
"9164": ["UI", "DimensionOrganizationUID"],
|
|
"9165": ["AT", "DimensionIndexPointer"],
|
|
"9167": ["AT", "FunctionalGroupPointer"],
|
|
"9213": ["LO", "DimensionIndexPrivateCreator"],
|
|
"9221": ["SQ", "DimensionOrganizationSequence"],
|
|
"9222": ["SQ", "DimensionIndexSequence"],
|
|
"9228": ["UL", "ConcatenationFrameOffsetNumber"],
|
|
"9238": ["LO", "FunctionalGroupPrivateCreator"],
|
|
"9241": ["FL", "NominalPercentageOfCardiacPhase"],
|
|
"9245": ["FL", "NominalPercentageOfRespiratoryPhase"],
|
|
"9246": ["FL", "StartingRespiratoryAmplitude"],
|
|
"9247": ["CS", "StartingRespiratoryPhase"],
|
|
"9248": ["FL", "EndingRespiratoryAmplitude"],
|
|
"9249": ["CS", "EndingRespiratoryPhase"],
|
|
"9250": ["CS", "RespiratoryTriggerType"],
|
|
"9251": ["FD", "RRIntervalTimeNominal"],
|
|
"9252": ["FD", "ActualCardiacTriggerDelayTime"],
|
|
"9253": ["SQ", "RespiratorySynchronizationSequence"],
|
|
"9254": ["FD", "RespiratoryIntervalTime"],
|
|
"9255": ["FD", "NominalRespiratoryTriggerDelayTime"],
|
|
"9256": ["FD", "RespiratoryTriggerDelayThreshold"],
|
|
"9257": ["FD", "ActualRespiratoryTriggerDelayTime"],
|
|
"9301": ["FD", "ImagePositionVolume"],
|
|
"9302": ["FD", "ImageOrientationVolume"],
|
|
"9307": ["CS", "UltrasoundAcquisitionGeometry"],
|
|
"9308": ["FD", "ApexPosition"],
|
|
"9309": ["FD", "VolumeToTransducerMappingMatrix"],
|
|
"930A": ["FD", "VolumeToTableMappingMatrix"],
|
|
"930C": ["CS", "PatientFrameOfReferenceSource"],
|
|
"930D": ["FD", "TemporalPositionTimeOffset"],
|
|
"930E": ["SQ", "PlanePositionVolumeSequence"],
|
|
"930F": ["SQ", "PlaneOrientationVolumeSequence"],
|
|
"9310": ["SQ", "TemporalPositionSequence"],
|
|
"9311": ["CS", "DimensionOrganizationType"],
|
|
"9312": ["UI", "VolumeFrameOfReferenceUID"],
|
|
"9313": ["UI", "TableFrameOfReferenceUID"],
|
|
"9421": ["LO", "DimensionDescriptionLabel"],
|
|
"9450": ["SQ", "PatientOrientationInFrameSequence"],
|
|
"9453": ["LO", "FrameLabel"],
|
|
"9518": ["US", "AcquisitionIndex"],
|
|
"9529": ["SQ", "ContributingSOPInstancesReferenceSequence"],
|
|
"9536": ["US", "ReconstructionIndex"]
|
|
},
|
|
"0022": {
|
|
"0001": ["US", "LightPathFilterPassThroughWavelength"],
|
|
"0002": ["US", "LightPathFilterPassBand"],
|
|
"0003": ["US", "ImagePathFilterPassThroughWavelength"],
|
|
"0004": ["US", "ImagePathFilterPassBand"],
|
|
"0005": ["CS", "PatientEyeMovementCommanded"],
|
|
"0006": ["SQ", "PatientEyeMovementCommandCodeSequence"],
|
|
"0007": ["FL", "SphericalLensPower"],
|
|
"0008": ["FL", "CylinderLensPower"],
|
|
"0009": ["FL", "CylinderAxis"],
|
|
"000A": ["FL", "EmmetropicMagnification"],
|
|
"000B": ["FL", "IntraOcularPressure"],
|
|
"000C": ["FL", "HorizontalFieldOfView"],
|
|
"000D": ["CS", "PupilDilated"],
|
|
"000E": ["FL", "DegreeOfDilation"],
|
|
"0010": ["FL", "StereoBaselineAngle"],
|
|
"0011": ["FL", "StereoBaselineDisplacement"],
|
|
"0012": ["FL", "StereoHorizontalPixelOffset"],
|
|
"0013": ["FL", "StereoVerticalPixelOffset"],
|
|
"0014": ["FL", "StereoRotation"],
|
|
"0015": ["SQ", "AcquisitionDeviceTypeCodeSequence"],
|
|
"0016": ["SQ", "IlluminationTypeCodeSequence"],
|
|
"0017": ["SQ", "LightPathFilterTypeStackCodeSequence"],
|
|
"0018": ["SQ", "ImagePathFilterTypeStackCodeSequence"],
|
|
"0019": ["SQ", "LensesCodeSequence"],
|
|
"001A": ["SQ", "ChannelDescriptionCodeSequence"],
|
|
"001B": ["SQ", "RefractiveStateSequence"],
|
|
"001C": ["SQ", "MydriaticAgentCodeSequence"],
|
|
"001D": ["SQ", "RelativeImagePositionCodeSequence"],
|
|
"001E": ["FL", "CameraAngleOfView"],
|
|
"0020": ["SQ", "StereoPairsSequence"],
|
|
"0021": ["SQ", "LeftImageSequence"],
|
|
"0022": ["SQ", "RightImageSequence"],
|
|
"0030": ["FL", "AxialLengthOfTheEye"],
|
|
"0031": ["SQ", "OphthalmicFrameLocationSequence"],
|
|
"0032": ["FL", "ReferenceCoordinates"],
|
|
"0035": ["FL", "DepthSpatialResolution"],
|
|
"0036": ["FL", "MaximumDepthDistortion"],
|
|
"0037": ["FL", "AlongScanSpatialResolution"],
|
|
"0038": ["FL", "MaximumAlongScanDistortion"],
|
|
"0039": ["CS", "OphthalmicImageOrientation"],
|
|
"0041": ["FL", "DepthOfTransverseImage"],
|
|
"0042": ["SQ", "MydriaticAgentConcentrationUnitsSequence"],
|
|
"0048": ["FL", "AcrossScanSpatialResolution"],
|
|
"0049": ["FL", "MaximumAcrossScanDistortion"],
|
|
"004E": ["DS", "MydriaticAgentConcentration"],
|
|
"0055": ["FL", "IlluminationWaveLength"],
|
|
"0056": ["FL", "IlluminationPower"],
|
|
"0057": ["FL", "IlluminationBandwidth"],
|
|
"0058": ["SQ", "MydriaticAgentSequence"],
|
|
"1007": ["SQ", "OphthalmicAxialMeasurementsRightEyeSequence"],
|
|
"1008": ["SQ", "OphthalmicAxialMeasurementsLeftEyeSequence"],
|
|
"1010": ["CS", "OphthalmicAxialLengthMeasurementsType"],
|
|
"1019": ["FL", "OphthalmicAxialLength"],
|
|
"1024": ["SQ", "LensStatusCodeSequence"],
|
|
"1025": ["SQ", "VitreousStatusCodeSequence"],
|
|
"1028": ["SQ", "IOLFormulaCodeSequence"],
|
|
"1029": ["LO", "IOLFormulaDetail"],
|
|
"1033": ["FL", "KeratometerIndex"],
|
|
"1035": ["SQ", "SourceOfOphthalmicAxialLengthCodeSequence"],
|
|
"1037": ["FL", "TargetRefraction"],
|
|
"1039": ["CS", "RefractiveProcedureOccurred"],
|
|
"1040": ["SQ", "RefractiveSurgeryTypeCodeSequence"],
|
|
"1044": ["SQ", "OphthalmicUltrasoundAxialMeasurementsTypeCodeSequence"],
|
|
"1050": ["SQ", "OphthalmicAxialLengthMeasurementsSequence"],
|
|
"1053": ["FL", "IOLPower"],
|
|
"1054": ["FL", "PredictedRefractiveError"],
|
|
"1059": ["FL", "OphthalmicAxialLengthVelocity"],
|
|
"1065": ["LO", "LensStatusDescription"],
|
|
"1066": ["LO", "VitreousStatusDescription"],
|
|
"1090": ["SQ", "IOLPowerSequence"],
|
|
"1092": ["SQ", "LensConstantSequence"],
|
|
"1093": ["LO", "IOLManufacturer"],
|
|
"1094": ["LO", "LensConstantDescription"],
|
|
"1096": ["SQ", "KeratometryMeasurementTypeCodeSequence"],
|
|
"1100": ["SQ", "ReferencedOphthalmicAxialMeasurementsSequence"],
|
|
"1101": ["SQ", "OphthalmicAxialLengthMeasurementsSegmentNameCodeSequence"],
|
|
"1103": ["SQ", "RefractiveErrorBeforeRefractiveSurgeryCodeSequence"],
|
|
"1121": ["FL", "IOLPowerForExactEmmetropia"],
|
|
"1122": ["FL", "IOLPowerForExactTargetRefraction"],
|
|
"1125": ["SQ", "AnteriorChamberDepthDefinitionCodeSequence"],
|
|
"1130": ["FL", "LensThickness"],
|
|
"1131": ["FL", "AnteriorChamberDepth"],
|
|
"1132": ["SQ", "SourceOfLensThicknessDataCodeSequence"],
|
|
"1133": ["SQ", "SourceOfAnteriorChamberDepthDataCodeSequence"],
|
|
"1135": ["SQ", "SourceOfRefractiveErrorDataCodeSequence"],
|
|
"1140": ["CS", "OphthalmicAxialLengthMeasurementModified"],
|
|
"1150": ["SQ", "OphthalmicAxialLengthDataSourceCodeSequence"],
|
|
"1153": ["SQ", "OphthalmicAxialLengthAcquisitionMethodCodeSequence"],
|
|
"1155": ["FL", "SignalToNoiseRatio"],
|
|
"1159": ["LO", "OphthalmicAxialLengthDataSourceDescription"],
|
|
"1210": ["SQ", "OphthalmicAxialLengthMeasurementsTotalLengthSequence"],
|
|
"1211": ["SQ", "OphthalmicAxialLengthMeasurementsSegmentalLengthSequence"],
|
|
"1212": ["SQ", "OphthalmicAxialLengthMeasurementsLengthSummationSequence"],
|
|
"1220": ["SQ", "UltrasoundOphthalmicAxialLengthMeasurementsSequence"],
|
|
"1225": ["SQ", "OpticalOphthalmicAxialLengthMeasurementsSequence"],
|
|
"1230": ["SQ", "UltrasoundSelectedOphthalmicAxialLengthSequence"],
|
|
"1250": ["SQ", "OphthalmicAxialLengthSelectionMethodCodeSequence"],
|
|
"1255": ["SQ", "OpticalSelectedOphthalmicAxialLengthSequence"],
|
|
"1257": ["SQ", "SelectedSegmentalOphthalmicAxialLengthSequence"],
|
|
"1260": ["SQ", "SelectedTotalOphthalmicAxialLengthSequence"],
|
|
"1262": ["SQ", "OphthalmicAxialLengthQualityMetricSequence"],
|
|
"1273": ["LO", "OphthalmicAxialLengthQualityMetricTypeDescription"],
|
|
"1300": ["SQ", "IntraocularLensCalculationsRightEyeSequence"],
|
|
"1310": ["SQ", "IntraocularLensCalculationsLeftEyeSequence"],
|
|
"1330": ["SQ", "ReferencedOphthalmicAxialLengthMeasurementQCImageSequence"]
|
|
},
|
|
"0024": {
|
|
"0010": ["FL", "VisualFieldHorizontalExtent"],
|
|
"0011": ["FL", "VisualFieldVerticalExtent"],
|
|
"0012": ["CS", "VisualFieldShape"],
|
|
"0016": ["SQ", "ScreeningTestModeCodeSequence"],
|
|
"0018": ["FL", "MaximumStimulusLuminance"],
|
|
"0020": ["FL", "BackgroundLuminance"],
|
|
"0021": ["SQ", "StimulusColorCodeSequence"],
|
|
"0024": ["SQ", "BackgroundIlluminationColorCodeSequence"],
|
|
"0025": ["FL", "StimulusArea"],
|
|
"0028": ["FL", "StimulusPresentationTime"],
|
|
"0032": ["SQ", "FixationSequence"],
|
|
"0033": ["SQ", "FixationMonitoringCodeSequence"],
|
|
"0034": ["SQ", "VisualFieldCatchTrialSequence"],
|
|
"0035": ["US", "FixationCheckedQuantity"],
|
|
"0036": ["US", "PatientNotProperlyFixatedQuantity"],
|
|
"0037": ["CS", "PresentedVisualStimuliDataFlag"],
|
|
"0038": ["US", "NumberOfVisualStimuli"],
|
|
"0039": ["CS", "ExcessiveFixationLossesDataFlag"],
|
|
"0040": ["CS", "ExcessiveFixationLosses"],
|
|
"0042": ["US", "StimuliRetestingQuantity"],
|
|
"0044": ["LT", "CommentsOnPatientPerformanceOfVisualField"],
|
|
"0045": ["CS", "FalseNegativesEstimateFlag"],
|
|
"0046": ["FL", "FalseNegativesEstimate"],
|
|
"0048": ["US", "NegativeCatchTrialsQuantity"],
|
|
"0050": ["US", "FalseNegativesQuantity"],
|
|
"0051": ["CS", "ExcessiveFalseNegativesDataFlag"],
|
|
"0052": ["CS", "ExcessiveFalseNegatives"],
|
|
"0053": ["CS", "FalsePositivesEstimateFlag"],
|
|
"0054": ["FL", "FalsePositivesEstimate"],
|
|
"0055": ["CS", "CatchTrialsDataFlag"],
|
|
"0056": ["US", "PositiveCatchTrialsQuantity"],
|
|
"0057": ["CS", "TestPointNormalsDataFlag"],
|
|
"0058": ["SQ", "TestPointNormalsSequence"],
|
|
"0059": ["CS", "GlobalDeviationProbabilityNormalsFlag"],
|
|
"0060": ["US", "FalsePositivesQuantity"],
|
|
"0061": ["CS", "ExcessiveFalsePositivesDataFlag"],
|
|
"0062": ["CS", "ExcessiveFalsePositives"],
|
|
"0063": ["CS", "VisualFieldTestNormalsFlag"],
|
|
"0064": ["SQ", "ResultsNormalsSequence"],
|
|
"0065": ["SQ", "AgeCorrectedSensitivityDeviationAlgorithmSequence"],
|
|
"0066": ["FL", "GlobalDeviationFromNormal"],
|
|
"0067": ["SQ", "GeneralizedDefectSensitivityDeviationAlgorithmSequence"],
|
|
"0068": ["FL", "LocalizedDeviationfromNormal"],
|
|
"0069": ["LO", "PatientReliabilityIndicator"],
|
|
"0070": ["FL", "VisualFieldMeanSensitivity"],
|
|
"0071": ["FL", "GlobalDeviationProbability"],
|
|
"0072": ["CS", "LocalDeviationProbabilityNormalsFlag"],
|
|
"0073": ["FL", "LocalizedDeviationProbability"],
|
|
"0074": ["CS", "ShortTermFluctuationCalculated"],
|
|
"0075": ["FL", "ShortTermFluctuation"],
|
|
"0076": ["CS", "ShortTermFluctuationProbabilityCalculated"],
|
|
"0077": ["FL", "ShortTermFluctuationProbability"],
|
|
"0078": ["CS", "CorrectedLocalizedDeviationFromNormalCalculated"],
|
|
"0079": ["FL", "CorrectedLocalizedDeviationFromNormal"],
|
|
"0080": ["CS", "CorrectedLocalizedDeviationFromNormalProbabilityCalculated"],
|
|
"0081": ["FL", "CorrectedLocalizedDeviationFromNormalProbability"],
|
|
"0083": ["SQ", "GlobalDeviationProbabilitySequence"],
|
|
"0085": ["SQ", "LocalizedDeviationProbabilitySequence"],
|
|
"0086": ["CS", "FovealSensitivityMeasured"],
|
|
"0087": ["FL", "FovealSensitivity"],
|
|
"0088": ["FL", "VisualFieldTestDuration"],
|
|
"0089": ["SQ", "VisualFieldTestPointSequence"],
|
|
"0090": ["FL", "VisualFieldTestPointXCoordinate"],
|
|
"0091": ["FL", "VisualFieldTestPointYCoordinate"],
|
|
"0092": ["FL", "AgeCorrectedSensitivityDeviationValue"],
|
|
"0093": ["CS", "StimulusResults"],
|
|
"0094": ["FL", "SensitivityValue"],
|
|
"0095": ["CS", "RetestStimulusSeen"],
|
|
"0096": ["FL", "RetestSensitivityValue"],
|
|
"0097": ["SQ", "VisualFieldTestPointNormalsSequence"],
|
|
"0098": ["FL", "QuantifiedDefect"],
|
|
"0100": ["FL", "AgeCorrectedSensitivityDeviationProbabilityValue"],
|
|
"0102": ["CS", "GeneralizedDefectCorrectedSensitivityDeviationFlag "],
|
|
"0103": ["FL", "GeneralizedDefectCorrectedSensitivityDeviationValue "],
|
|
"0104": ["FL", "GeneralizedDefectCorrectedSensitivityDeviationProbabilityValue"],
|
|
"0105": ["FL ", "MinimumSensitivityValue"],
|
|
"0106": ["CS", "BlindSpotLocalized"],
|
|
"0107": ["FL", "BlindSpotXCoordinate"],
|
|
"0108": ["FL", "BlindSpotYCoordinate "],
|
|
"0110": ["SQ", "VisualAcuityMeasurementSequence"],
|
|
"0112": ["SQ", "RefractiveParametersUsedOnPatientSequence"],
|
|
"0113": ["CS", "MeasurementLaterality"],
|
|
"0114": ["SQ", "OphthalmicPatientClinicalInformationLeftEyeSequence"],
|
|
"0115": ["SQ", "OphthalmicPatientClinicalInformationRightEyeSequence"],
|
|
"0117": ["CS", "FovealPointNormativeDataFlag"],
|
|
"0118": ["FL", "FovealPointProbabilityValue"],
|
|
"0120": ["CS", "ScreeningBaselineMeasured"],
|
|
"0122": ["SQ", "ScreeningBaselineMeasuredSequence"],
|
|
"0124": ["CS", "ScreeningBaselineType"],
|
|
"0126": ["FL", "ScreeningBaselineValue"],
|
|
"0202": ["LO", "AlgorithmSource"],
|
|
"0306": ["LO", "DataSetName"],
|
|
"0307": ["LO", "DataSetVersion"],
|
|
"0308": ["LO", "DataSetSource"],
|
|
"0309": ["LO", "DataSetDescription"],
|
|
"0317": ["SQ", "VisualFieldTestReliabilityGlobalIndexSequence"],
|
|
"0320": ["SQ", "VisualFieldGlobalResultsIndexSequence"],
|
|
"0325": ["SQ", "DataObservationSequence"],
|
|
"0338": ["CS", "IndexNormalsFlag"],
|
|
"0341": ["FL", "IndexProbability"],
|
|
"0344": ["SQ", "IndexProbabilitySequence"]
|
|
},
|
|
"0028": {
|
|
"0002": ["US", "SamplesPerPixel"],
|
|
"0003": ["US", "SamplesPerPixelUsed"],
|
|
"0004": ["CS", "PhotometricInterpretation"],
|
|
"0005": ["US", "ImageDimensions"],
|
|
"0006": ["US", "PlanarConfiguration"],
|
|
"0008": ["IS", "NumberOfFrames"],
|
|
"0009": ["AT", "FrameIncrementPointer"],
|
|
"000A": ["AT", "FrameDimensionPointer"],
|
|
"0010": ["US", "Rows"],
|
|
"0011": ["US", "Columns"],
|
|
"0012": ["US", "Planes"],
|
|
"0014": ["US", "UltrasoundColorDataPresent"],
|
|
"0030": ["DS", "PixelSpacing"],
|
|
"0031": ["DS", "ZoomFactor"],
|
|
"0032": ["DS", "ZoomCenter"],
|
|
"0034": ["IS", "PixelAspectRatio"],
|
|
"0040": ["CS", "ImageFormat"],
|
|
"0050": ["LO", "ManipulatedImage"],
|
|
"0051": ["CS", "CorrectedImage"],
|
|
"005F": ["LO", "CompressionRecognitionCode"],
|
|
"0060": ["CS", "CompressionCode"],
|
|
"0061": ["SH", "CompressionOriginator"],
|
|
"0062": ["LO", "CompressionLabel"],
|
|
"0063": ["SH", "CompressionDescription"],
|
|
"0065": ["CS", "CompressionSequence"],
|
|
"0066": ["AT", "CompressionStepPointers"],
|
|
"0068": ["US", "RepeatInterval"],
|
|
"0069": ["US", "BitsGrouped"],
|
|
"0070": ["US", "PerimeterTable"],
|
|
"0071": ["SS", "PerimeterValue"],
|
|
"0080": ["US", "PredictorRows"],
|
|
"0081": ["US", "PredictorColumns"],
|
|
"0082": ["US", "PredictorConstants"],
|
|
"0090": ["CS", "BlockedPixels"],
|
|
"0091": ["US", "BlockRows"],
|
|
"0092": ["US", "BlockColumns"],
|
|
"0093": ["US", "RowOverlap"],
|
|
"0094": ["US", "ColumnOverlap"],
|
|
"0100": ["US", "BitsAllocated"],
|
|
"0101": ["US", "BitsStored"],
|
|
"0102": ["US", "HighBit"],
|
|
"0103": ["US", "PixelRepresentation"],
|
|
"0104": ["SS", "SmallestValidPixelValue"],
|
|
"0105": ["SS", "LargestValidPixelValue"],
|
|
"0106": ["SS", "SmallestImagePixelValue"],
|
|
"0107": ["SS", "LargestImagePixelValue"],
|
|
"0108": ["SS", "SmallestPixelValueInSeries"],
|
|
"0109": ["SS", "LargestPixelValueInSeries"],
|
|
"0110": ["SS", "SmallestImagePixelValueInPlane"],
|
|
"0111": ["SS", "LargestImagePixelValueInPlane"],
|
|
"0120": ["SS", "PixelPaddingValue"],
|
|
"0121": ["SS", "PixelPaddingRangeLimit"],
|
|
"0200": ["US", "ImageLocation"],
|
|
"0300": ["CS", "QualityControlImage"],
|
|
"0301": ["CS", "BurnedInAnnotation"],
|
|
"0302": ["CS", "RecognizableVisualFeatures"],
|
|
"0303": ["CS", "LongitudinalTemporalInformationModified"],
|
|
"0400": ["LO", "TransformLabel"],
|
|
"0401": ["LO", "TransformVersionNumber"],
|
|
"0402": ["US", "NumberOfTransformSteps"],
|
|
"0403": ["LO", "SequenceOfCompressedData"],
|
|
"0404": ["AT", "DetailsOfCoefficients"],
|
|
"0700": ["LO", "DCTLabel"],
|
|
"0701": ["CS", "DataBlockDescription"],
|
|
"0702": ["AT", "DataBlock"],
|
|
"0710": ["US", "NormalizationFactorFormat"],
|
|
"0720": ["US", "ZonalMapNumberFormat"],
|
|
"0721": ["AT", "ZonalMapLocation"],
|
|
"0722": ["US", "ZonalMapFormat"],
|
|
"0730": ["US", "AdaptiveMapFormat"],
|
|
"0740": ["US", "CodeNumberFormat"],
|
|
"0A02": ["CS", "PixelSpacingCalibrationType"],
|
|
"0A04": ["LO", "PixelSpacingCalibrationDescription"],
|
|
"1040": ["CS", "PixelIntensityRelationship"],
|
|
"1041": ["SS", "PixelIntensityRelationshipSign"],
|
|
"1050": ["DS", "WindowCenter"],
|
|
"1051": ["DS", "WindowWidth"],
|
|
"1052": ["DS", "RescaleIntercept"],
|
|
"1053": ["DS", "RescaleSlope"],
|
|
"1054": ["LO", "RescaleType"],
|
|
"1055": ["LO", "WindowCenterWidthExplanation"],
|
|
"1056": ["CS", "VOILUTFunction"],
|
|
"1080": ["CS", "GrayScale"],
|
|
"1090": ["CS", "RecommendedViewingMode"],
|
|
"1100": ["SS", "GrayLookupTableDescriptor"],
|
|
"1101": ["SS", "RedPaletteColorLookupTableDescriptor"],
|
|
"1102": ["SS", "GreenPaletteColorLookupTableDescriptor"],
|
|
"1103": ["SS", "BluePaletteColorLookupTableDescriptor"],
|
|
"1104": ["US", "AlphaPaletteColorLookupTableDescriptor"],
|
|
"1111": ["SS", "LargeRedPaletteColorLookupTableDescriptor"],
|
|
"1112": ["SS", "LargeGreenPaletteColorLookupTableDescriptor"],
|
|
"1113": ["SS", "LargeBluePaletteColorLookupTableDescriptor"],
|
|
"1199": ["UI", "PaletteColorLookupTableUID"],
|
|
"1200": ["OW", "GrayLookupTableData"],
|
|
"1201": ["OW", "RedPaletteColorLookupTableData"],
|
|
"1202": ["OW", "GreenPaletteColorLookupTableData"],
|
|
"1203": ["OW", "BluePaletteColorLookupTableData"],
|
|
"1204": ["OW", "AlphaPaletteColorLookupTableData"],
|
|
"1211": ["OW", "LargeRedPaletteColorLookupTableData"],
|
|
"1212": ["OW", "LargeGreenPaletteColorLookupTableData"],
|
|
"1213": ["OW", "LargeBluePaletteColorLookupTableData"],
|
|
"1214": ["UI", "LargePaletteColorLookupTableUID"],
|
|
"1221": ["OW", "SegmentedRedPaletteColorLookupTableData"],
|
|
"1222": ["OW", "SegmentedGreenPaletteColorLookupTableData"],
|
|
"1223": ["OW", "SegmentedBluePaletteColorLookupTableData"],
|
|
"1300": ["CS", "BreastImplantPresent"],
|
|
"1350": ["CS", "PartialView"],
|
|
"1351": ["ST", "PartialViewDescription"],
|
|
"1352": ["SQ", "PartialViewCodeSequence"],
|
|
"135A": ["CS", "SpatialLocationsPreserved"],
|
|
"1401": ["SQ", "DataFrameAssignmentSequence"],
|
|
"1402": ["CS", "DataPathAssignment"],
|
|
"1403": ["US", "BitsMappedToColorLookupTable"],
|
|
"1404": ["SQ", "BlendingLUT1Sequence"],
|
|
"1405": ["CS", "BlendingLUT1TransferFunction"],
|
|
"1406": ["FD", "BlendingWeightConstant"],
|
|
"1407": ["US", "BlendingLookupTableDescriptor"],
|
|
"1408": ["OW", "BlendingLookupTableData"],
|
|
"140B": ["SQ", "EnhancedPaletteColorLookupTableSequence"],
|
|
"140C": ["SQ", "BlendingLUT2Sequence"],
|
|
"140D": ["CS", "BlendingLUT2TransferFunction"],
|
|
"140E": ["CS", "DataPathID"],
|
|
"140F": ["CS", "RGBLUTTransferFunction"],
|
|
"1410": ["CS", "AlphaLUTTransferFunction"],
|
|
"2000": ["OB", "ICCProfile"],
|
|
"2110": ["CS", "LossyImageCompression"],
|
|
"2112": ["DS", "LossyImageCompressionRatio"],
|
|
"2114": ["CS", "LossyImageCompressionMethod"],
|
|
"3000": ["SQ", "ModalityLUTSequence"],
|
|
"3002": ["SS", "LUTDescriptor"],
|
|
"3003": ["LO", "LUTExplanation"],
|
|
"3004": ["LO", "ModalityLUTType"],
|
|
"3006": ["OW", "LUTData"],
|
|
"3010": ["SQ", "VOILUTSequence"],
|
|
"3110": ["SQ", "SoftcopyVOILUTSequence"],
|
|
"4000": ["LT", "ImagePresentationComments"],
|
|
"5000": ["SQ", "BiPlaneAcquisitionSequence"],
|
|
"6010": ["US", "RepresentativeFrameNumber"],
|
|
"6020": ["US", "FrameNumbersOfInterest"],
|
|
"6022": ["LO", "FrameOfInterestDescription"],
|
|
"6023": ["CS", "FrameOfInterestType"],
|
|
"6030": ["US", "MaskPointers"],
|
|
"6040": ["US", "RWavePointer"],
|
|
"6100": ["SQ", "MaskSubtractionSequence"],
|
|
"6101": ["CS", "MaskOperation"],
|
|
"6102": ["US", "ApplicableFrameRange"],
|
|
"6110": ["US", "MaskFrameNumbers"],
|
|
"6112": ["US", "ContrastFrameAveraging"],
|
|
"6114": ["FL", "MaskSubPixelShift"],
|
|
"6120": ["SS", "TIDOffset"],
|
|
"6190": ["ST", "MaskOperationExplanation"],
|
|
"7FE0": ["UT", "PixelDataProviderURL"],
|
|
"9001": ["UL", "DataPointRows"],
|
|
"9002": ["UL", "DataPointColumns"],
|
|
"9003": ["CS", "SignalDomainColumns"],
|
|
"9099": ["US", "LargestMonochromePixelValue"],
|
|
"9108": ["CS", "DataRepresentation"],
|
|
"9110": ["SQ", "PixelMeasuresSequence"],
|
|
"9132": ["SQ", "FrameVOILUTSequence"],
|
|
"9145": ["SQ", "PixelValueTransformationSequence"],
|
|
"9235": ["CS", "SignalDomainRows"],
|
|
"9411": ["FL", "DisplayFilterPercentage"],
|
|
"9415": ["SQ", "FramePixelShiftSequence"],
|
|
"9416": ["US", "SubtractionItemID"],
|
|
"9422": ["SQ", "PixelIntensityRelationshipLUTSequence"],
|
|
"9443": ["SQ", "FramePixelDataPropertiesSequence"],
|
|
"9444": ["CS", "GeometricalProperties"],
|
|
"9445": ["FL", "GeometricMaximumDistortion"],
|
|
"9446": ["CS", "ImageProcessingApplied"],
|
|
"9454": ["CS", "MaskSelectionMode"],
|
|
"9474": ["CS", "LUTFunction"],
|
|
"9478": ["FL", "MaskVisibilityPercentage"],
|
|
"9501": ["SQ", "PixelShiftSequence"],
|
|
"9502": ["SQ", "RegionPixelShiftSequence"],
|
|
"9503": ["SS", "VerticesOfTheRegion"],
|
|
"9505": ["SQ", "MultiFramePresentationSequence"],
|
|
"9506": ["US", "PixelShiftFrameRange"],
|
|
"9507": ["US", "LUTFrameRange"],
|
|
"9520": ["DS", "ImageToEquipmentMappingMatrix"],
|
|
"9537": ["CS", "EquipmentCoordinateSystemIdentification"]
|
|
},
|
|
"0032": {
|
|
"000A": ["CS", "StudyStatusID"],
|
|
"000C": ["CS", "StudyPriorityID"],
|
|
"0012": ["LO", "StudyIDIssuer"],
|
|
"0032": ["DA", "StudyVerifiedDate"],
|
|
"0033": ["TM", "StudyVerifiedTime"],
|
|
"0034": ["DA", "StudyReadDate"],
|
|
"0035": ["TM", "StudyReadTime"],
|
|
"1000": ["DA", "ScheduledStudyStartDate"],
|
|
"1001": ["TM", "ScheduledStudyStartTime"],
|
|
"1010": ["DA", "ScheduledStudyStopDate"],
|
|
"1011": ["TM", "ScheduledStudyStopTime"],
|
|
"1020": ["LO", "ScheduledStudyLocation"],
|
|
"1021": ["AE", "ScheduledStudyLocationAETitle"],
|
|
"1030": ["LO", "ReasonForStudy"],
|
|
"1031": ["SQ", "RequestingPhysicianIdentificationSequence"],
|
|
"1032": ["PN", "RequestingPhysician"],
|
|
"1033": ["LO", "RequestingService"],
|
|
"1034": ["SQ", "RequestingServiceCodeSequence"],
|
|
"1040": ["DA", "StudyArrivalDate"],
|
|
"1041": ["TM", "StudyArrivalTime"],
|
|
"1050": ["DA", "StudyCompletionDate"],
|
|
"1051": ["TM", "StudyCompletionTime"],
|
|
"1055": ["CS", "StudyComponentStatusID"],
|
|
"1060": ["LO", "RequestedProcedureDescription"],
|
|
"1064": ["SQ", "RequestedProcedureCodeSequence"],
|
|
"1070": ["LO", "RequestedContrastAgent"],
|
|
"4000": ["LT", "StudyComments"]
|
|
},
|
|
"0038": {
|
|
"0004": ["SQ", "ReferencedPatientAliasSequence"],
|
|
"0008": ["CS", "VisitStatusID"],
|
|
"0010": ["LO", "AdmissionID"],
|
|
"0011": ["LO", "IssuerOfAdmissionID"],
|
|
"0014": ["SQ", "IssuerOfAdmissionIDSequence"],
|
|
"0016": ["LO", "RouteOfAdmissions"],
|
|
"001A": ["DA", "ScheduledAdmissionDate"],
|
|
"001B": ["TM", "ScheduledAdmissionTime"],
|
|
"001C": ["DA", "ScheduledDischargeDate"],
|
|
"001D": ["TM", "ScheduledDischargeTime"],
|
|
"001E": ["LO", "ScheduledPatientInstitutionResidence"],
|
|
"0020": ["DA", "AdmittingDate"],
|
|
"0021": ["TM", "AdmittingTime"],
|
|
"0030": ["DA", "DischargeDate"],
|
|
"0032": ["TM", "DischargeTime"],
|
|
"0040": ["LO", "DischargeDiagnosisDescription"],
|
|
"0044": ["SQ", "DischargeDiagnosisCodeSequence"],
|
|
"0050": ["LO", "SpecialNeeds"],
|
|
"0060": ["LO", "ServiceEpisodeID"],
|
|
"0061": ["LO", "IssuerOfServiceEpisodeID"],
|
|
"0062": ["LO", "ServiceEpisodeDescription"],
|
|
"0064": ["SQ", "IssuerOfServiceEpisodeIDSequence"],
|
|
"0100": ["SQ", "PertinentDocumentsSequence"],
|
|
"0300": ["LO", "CurrentPatientLocation"],
|
|
"0400": ["LO", "PatientInstitutionResidence"],
|
|
"0500": ["LO", "PatientState"],
|
|
"0502": ["SQ", "PatientClinicalTrialParticipationSequence"],
|
|
"4000": ["LT", "VisitComments"]
|
|
},
|
|
"003A": {
|
|
"0004": ["CS", "WaveformOriginality"],
|
|
"0005": ["US", "NumberOfWaveformChannels"],
|
|
"0010": ["UL", "NumberOfWaveformSamples"],
|
|
"001A": ["DS", "SamplingFrequency"],
|
|
"0020": ["SH", "MultiplexGroupLabel"],
|
|
"0200": ["SQ", "ChannelDefinitionSequence"],
|
|
"0202": ["IS", "WaveformChannelNumber"],
|
|
"0203": ["SH", "ChannelLabel"],
|
|
"0205": ["CS", "ChannelStatus"],
|
|
"0208": ["SQ", "ChannelSourceSequence"],
|
|
"0209": ["SQ", "ChannelSourceModifiersSequence"],
|
|
"020A": ["SQ", "SourceWaveformSequence"],
|
|
"020C": ["LO", "ChannelDerivationDescription"],
|
|
"0210": ["DS", "ChannelSensitivity"],
|
|
"0211": ["SQ", "ChannelSensitivityUnitsSequence"],
|
|
"0212": ["DS", "ChannelSensitivityCorrectionFactor"],
|
|
"0213": ["DS", "ChannelBaseline"],
|
|
"0214": ["DS", "ChannelTimeSkew"],
|
|
"0215": ["DS", "ChannelSampleSkew"],
|
|
"0218": ["DS", "ChannelOffset"],
|
|
"021A": ["US", "WaveformBitsStored"],
|
|
"0220": ["DS", "FilterLowFrequency"],
|
|
"0221": ["DS", "FilterHighFrequency"],
|
|
"0222": ["DS", "NotchFilterFrequency"],
|
|
"0223": ["DS", "NotchFilterBandwidth"],
|
|
"0230": ["FL", "WaveformDataDisplayScale"],
|
|
"0231": ["US", "WaveformDisplayBackgroundCIELabValue"],
|
|
"0240": ["SQ", "WaveformPresentationGroupSequence"],
|
|
"0241": ["US", "PresentationGroupNumber"],
|
|
"0242": ["SQ", "ChannelDisplaySequence"],
|
|
"0244": ["US", "ChannelRecommendedDisplayCIELabValue"],
|
|
"0245": ["FL", "ChannelPosition"],
|
|
"0246": ["CS", "DisplayShadingFlag"],
|
|
"0247": ["FL", "FractionalChannelDisplayScale"],
|
|
"0248": ["FL", "AbsoluteChannelDisplayScale"],
|
|
"0300": ["SQ", "MultiplexedAudioChannelsDescriptionCodeSequence"],
|
|
"0301": ["IS", "ChannelIdentificationCode"],
|
|
"0302": ["CS", "ChannelMode"]
|
|
},
|
|
"0040": {
|
|
"0001": ["AE", "ScheduledStationAETitle"],
|
|
"0002": ["DA", "ScheduledProcedureStepStartDate"],
|
|
"0003": ["TM", "ScheduledProcedureStepStartTime"],
|
|
"0004": ["DA", "ScheduledProcedureStepEndDate"],
|
|
"0005": ["TM", "ScheduledProcedureStepEndTime"],
|
|
"0006": ["PN", "ScheduledPerformingPhysicianName"],
|
|
"0007": ["LO", "ScheduledProcedureStepDescription"],
|
|
"0008": ["SQ", "ScheduledProtocolCodeSequence"],
|
|
"0009": ["SH", "ScheduledProcedureStepID"],
|
|
"000A": ["SQ", "StageCodeSequence"],
|
|
"000B": ["SQ", "ScheduledPerformingPhysicianIdentificationSequence"],
|
|
"0010": ["SH", "ScheduledStationName"],
|
|
"0011": ["SH", "ScheduledProcedureStepLocation"],
|
|
"0012": ["LO", "PreMedication"],
|
|
"0020": ["CS", "ScheduledProcedureStepStatus"],
|
|
"0026": ["SQ", "OrderPlacerIdentifierSequence"],
|
|
"0027": ["SQ", "OrderFillerIdentifierSequence"],
|
|
"0031": ["UT", "LocalNamespaceEntityID"],
|
|
"0032": ["UT", "UniversalEntityID"],
|
|
"0033": ["CS", "UniversalEntityIDType"],
|
|
"0035": ["CS", "IdentifierTypeCode"],
|
|
"0036": ["SQ", "AssigningFacilitySequence"],
|
|
"0039": ["SQ", "AssigningJurisdictionCodeSequence"],
|
|
"003A": ["SQ", "AssigningAgencyOrDepartmentCodeSequence"],
|
|
"0100": ["SQ", "ScheduledProcedureStepSequence"],
|
|
"0220": ["SQ", "ReferencedNonImageCompositeSOPInstanceSequence"],
|
|
"0241": ["AE", "PerformedStationAETitle"],
|
|
"0242": ["SH", "PerformedStationName"],
|
|
"0243": ["SH", "PerformedLocation"],
|
|
"0244": ["DA", "PerformedProcedureStepStartDate"],
|
|
"0245": ["TM", "PerformedProcedureStepStartTime"],
|
|
"0250": ["DA", "PerformedProcedureStepEndDate"],
|
|
"0251": ["TM", "PerformedProcedureStepEndTime"],
|
|
"0252": ["CS", "PerformedProcedureStepStatus"],
|
|
"0253": ["SH", "PerformedProcedureStepID"],
|
|
"0254": ["LO", "PerformedProcedureStepDescription"],
|
|
"0255": ["LO", "PerformedProcedureTypeDescription"],
|
|
"0260": ["SQ", "PerformedProtocolCodeSequence"],
|
|
"0261": ["CS", "PerformedProtocolType"],
|
|
"0270": ["SQ", "ScheduledStepAttributesSequence"],
|
|
"0275": ["SQ", "RequestAttributesSequence"],
|
|
"0280": ["ST", "CommentsOnThePerformedProcedureStep"],
|
|
"0281": ["SQ", "PerformedProcedureStepDiscontinuationReasonCodeSequence"],
|
|
"0293": ["SQ", "QuantitySequence"],
|
|
"0294": ["DS", "Quantity"],
|
|
"0295": ["SQ", "MeasuringUnitsSequence"],
|
|
"0296": ["SQ", "BillingItemSequence"],
|
|
"0300": ["US", "TotalTimeOfFluoroscopy"],
|
|
"0301": ["US", "TotalNumberOfExposures"],
|
|
"0302": ["US", "EntranceDose"],
|
|
"0303": ["US", "ExposedArea"],
|
|
"0306": ["DS", "DistanceSourceToEntrance"],
|
|
"0307": ["DS", "DistanceSourceToSupport"],
|
|
"030E": ["SQ", "ExposureDoseSequence"],
|
|
"0310": ["ST", "CommentsOnRadiationDose"],
|
|
"0312": ["DS", "XRayOutput"],
|
|
"0314": ["DS", "HalfValueLayer"],
|
|
"0316": ["DS", "OrganDose"],
|
|
"0318": ["CS", "OrganExposed"],
|
|
"0320": ["SQ", "BillingProcedureStepSequence"],
|
|
"0321": ["SQ", "FilmConsumptionSequence"],
|
|
"0324": ["SQ", "BillingSuppliesAndDevicesSequence"],
|
|
"0330": ["SQ", "ReferencedProcedureStepSequence"],
|
|
"0340": ["SQ", "PerformedSeriesSequence"],
|
|
"0400": ["LT", "CommentsOnTheScheduledProcedureStep"],
|
|
"0440": ["SQ", "ProtocolContextSequence"],
|
|
"0441": ["SQ", "ContentItemModifierSequence"],
|
|
"0500": ["SQ", "ScheduledSpecimenSequence"],
|
|
"050A": ["LO", "SpecimenAccessionNumber"],
|
|
"0512": ["LO", "ContainerIdentifier"],
|
|
"0513": ["SQ", "IssuerOfTheContainerIdentifierSequence"],
|
|
"0515": ["SQ", "AlternateContainerIdentifierSequence"],
|
|
"0518": ["SQ", "ContainerTypeCodeSequence"],
|
|
"051A": ["LO", "ContainerDescription"],
|
|
"0520": ["SQ", "ContainerComponentSequence"],
|
|
"0550": ["SQ", "SpecimenSequence"],
|
|
"0551": ["LO", "SpecimenIdentifier"],
|
|
"0552": ["SQ", "SpecimenDescriptionSequenceTrial"],
|
|
"0553": ["ST", "SpecimenDescriptionTrial"],
|
|
"0554": ["UI", "SpecimenUID"],
|
|
"0555": ["SQ", "AcquisitionContextSequence"],
|
|
"0556": ["ST", "AcquisitionContextDescription"],
|
|
"059A": ["SQ", "SpecimenTypeCodeSequence"],
|
|
"0560": ["SQ", "SpecimenDescriptionSequence"],
|
|
"0562": ["SQ", "IssuerOfTheSpecimenIdentifierSequence"],
|
|
"0600": ["LO", "SpecimenShortDescription"],
|
|
"0602": ["UT", "SpecimenDetailedDescription"],
|
|
"0610": ["SQ", "SpecimenPreparationSequence"],
|
|
"0612": ["SQ", "SpecimenPreparationStepContentItemSequence"],
|
|
"0620": ["SQ", "SpecimenLocalizationContentItemSequence"],
|
|
"06FA": ["LO", "SlideIdentifier"],
|
|
"071A": ["SQ", "ImageCenterPointCoordinatesSequence"],
|
|
"072A": ["DS", "XOffsetInSlideCoordinateSystem"],
|
|
"073A": ["DS", "YOffsetInSlideCoordinateSystem"],
|
|
"074A": ["DS", "ZOffsetInSlideCoordinateSystem"],
|
|
"08D8": ["SQ", "PixelSpacingSequence"],
|
|
"08DA": ["SQ", "CoordinateSystemAxisCodeSequence"],
|
|
"08EA": ["SQ", "MeasurementUnitsCodeSequence"],
|
|
"09F8": ["SQ", "VitalStainCodeSequenceTrial"],
|
|
"1001": ["SH", "RequestedProcedureID"],
|
|
"1002": ["LO", "ReasonForTheRequestedProcedure"],
|
|
"1003": ["SH", "RequestedProcedurePriority"],
|
|
"1004": ["LO", "PatientTransportArrangements"],
|
|
"1005": ["LO", "RequestedProcedureLocation"],
|
|
"1006": ["SH", "PlacerOrderNumberProcedure"],
|
|
"1007": ["SH", "FillerOrderNumberProcedure"],
|
|
"1008": ["LO", "ConfidentialityCode"],
|
|
"1009": ["SH", "ReportingPriority"],
|
|
"100A": ["SQ", "ReasonForRequestedProcedureCodeSequence"],
|
|
"1010": ["PN", "NamesOfIntendedRecipientsOfResults"],
|
|
"1011": ["SQ", "IntendedRecipientsOfResultsIdentificationSequence"],
|
|
"1012": ["SQ", "ReasonForPerformedProcedureCodeSequence"],
|
|
"1060": ["LO", "RequestedProcedureDescriptionTrial"],
|
|
"1101": ["SQ", "PersonIdentificationCodeSequence"],
|
|
"1102": ["ST", "PersonAddress"],
|
|
"1103": ["LO", "PersonTelephoneNumbers"],
|
|
"1400": ["LT", "RequestedProcedureComments"],
|
|
"2001": ["LO", "ReasonForTheImagingServiceRequest"],
|
|
"2004": ["DA", "IssueDateOfImagingServiceRequest"],
|
|
"2005": ["TM", "IssueTimeOfImagingServiceRequest"],
|
|
"2006": ["SH", "PlacerOrderNumberImagingServiceRequestRetired"],
|
|
"2007": ["SH", "FillerOrderNumberImagingServiceRequestRetired"],
|
|
"2008": ["PN", "OrderEnteredBy"],
|
|
"2009": ["SH", "OrderEntererLocation"],
|
|
"2010": ["SH", "OrderCallbackPhoneNumber"],
|
|
"2016": ["LO", "PlacerOrderNumberImagingServiceRequest"],
|
|
"2017": ["LO", "FillerOrderNumberImagingServiceRequest"],
|
|
"2400": ["LT", "ImagingServiceRequestComments"],
|
|
"3001": ["LO", "ConfidentialityConstraintOnPatientDataDescription"],
|
|
"4001": ["CS", "GeneralPurposeScheduledProcedureStepStatus"],
|
|
"4002": ["CS", "GeneralPurposePerformedProcedureStepStatus"],
|
|
"4003": ["CS", "GeneralPurposeScheduledProcedureStepPriority"],
|
|
"4004": ["SQ", "ScheduledProcessingApplicationsCodeSequence"],
|
|
"4005": ["DT", "ScheduledProcedureStepStartDateTime"],
|
|
"4006": ["CS", "MultipleCopiesFlag"],
|
|
"4007": ["SQ", "PerformedProcessingApplicationsCodeSequence"],
|
|
"4009": ["SQ", "HumanPerformerCodeSequence"],
|
|
"4010": ["DT", "ScheduledProcedureStepModificationDateTime"],
|
|
"4011": ["DT", "ExpectedCompletionDateTime"],
|
|
"4015": ["SQ", "ResultingGeneralPurposePerformedProcedureStepsSequence"],
|
|
"4016": ["SQ", "ReferencedGeneralPurposeScheduledProcedureStepSequence"],
|
|
"4018": ["SQ", "ScheduledWorkitemCodeSequence"],
|
|
"4019": ["SQ", "PerformedWorkitemCodeSequence"],
|
|
"4020": ["CS", "InputAvailabilityFlag"],
|
|
"4021": ["SQ", "InputInformationSequence"],
|
|
"4022": ["SQ", "RelevantInformationSequence"],
|
|
"4023": ["UI", "ReferencedGeneralPurposeScheduledProcedureStepTransactionUID"],
|
|
"4025": ["SQ", "ScheduledStationNameCodeSequence"],
|
|
"4026": ["SQ", "ScheduledStationClassCodeSequence"],
|
|
"4027": ["SQ", "ScheduledStationGeographicLocationCodeSequence"],
|
|
"4028": ["SQ", "PerformedStationNameCodeSequence"],
|
|
"4029": ["SQ", "PerformedStationClassCodeSequence"],
|
|
"4030": ["SQ", "PerformedStationGeographicLocationCodeSequence"],
|
|
"4031": ["SQ", "RequestedSubsequentWorkitemCodeSequence"],
|
|
"4032": ["SQ", "NonDICOMOutputCodeSequence"],
|
|
"4033": ["SQ", "OutputInformationSequence"],
|
|
"4034": ["SQ", "ScheduledHumanPerformersSequence"],
|
|
"4035": ["SQ", "ActualHumanPerformersSequence"],
|
|
"4036": ["LO", "HumanPerformerOrganization"],
|
|
"4037": ["PN", "HumanPerformerName"],
|
|
"4040": ["CS", "RawDataHandling"],
|
|
"4041": ["CS", "InputReadinessState"],
|
|
"4050": ["DT", "PerformedProcedureStepStartDateTime"],
|
|
"4051": ["DT", "PerformedProcedureStepEndDateTime"],
|
|
"4052": ["DT", "ProcedureStepCancellationDateTime"],
|
|
"8302": ["DS", "EntranceDoseInmGy"],
|
|
"9094": ["SQ", "ReferencedImageRealWorldValueMappingSequence"],
|
|
"9096": ["SQ", "RealWorldValueMappingSequence"],
|
|
"9098": ["SQ", "PixelValueMappingCodeSequence"],
|
|
"9210": ["SH", "LUTLabel"],
|
|
"9211": ["SS", "RealWorldValueLastValueMapped"],
|
|
"9212": ["FD", "RealWorldValueLUTData"],
|
|
"9216": ["SS", "RealWorldValueFirstValueMapped"],
|
|
"9224": ["FD", "RealWorldValueIntercept"],
|
|
"9225": ["FD", "RealWorldValueSlope"],
|
|
"A007": ["CS", "FindingsFlagTrial"],
|
|
"A010": ["CS", "RelationshipType"],
|
|
"A020": ["SQ", "FindingsSequenceTrial"],
|
|
"A021": ["UI", "FindingsGroupUIDTrial"],
|
|
"A022": ["UI", "ReferencedFindingsGroupUIDTrial"],
|
|
"A023": ["DA", "FindingsGroupRecordingDateTrial"],
|
|
"A024": ["TM", "FindingsGroupRecordingTimeTrial"],
|
|
"A026": ["SQ", "FindingsSourceCategoryCodeSequenceTrial"],
|
|
"A027": ["LO", "VerifyingOrganization"],
|
|
"A028": ["SQ", "DocumentingOrganizationIdentifierCodeSequenceTrial"],
|
|
"A030": ["DT", "VerificationDateTime"],
|
|
"A032": ["DT", "ObservationDateTime"],
|
|
"A040": ["CS", "ValueType"],
|
|
"A043": ["SQ", "ConceptNameCodeSequence"],
|
|
"A047": ["LO", "MeasurementPrecisionDescriptionTrial"],
|
|
"A050": ["CS", "ContinuityOfContent"],
|
|
"A057": ["CS", "UrgencyOrPriorityAlertsTrial"],
|
|
"A060": ["LO", "SequencingIndicatorTrial"],
|
|
"A066": ["SQ", "DocumentIdentifierCodeSequenceTrial"],
|
|
"A067": ["PN", "DocumentAuthorTrial"],
|
|
"A068": ["SQ", "DocumentAuthorIdentifierCodeSequenceTrial"],
|
|
"A070": ["SQ", "IdentifierCodeSequenceTrial"],
|
|
"A073": ["SQ", "VerifyingObserverSequence"],
|
|
"A074": ["OB", "ObjectBinaryIdentifierTrial"],
|
|
"A075": ["PN", "VerifyingObserverName"],
|
|
"A076": ["SQ", "DocumentingObserverIdentifierCodeSequenceTrial"],
|
|
"A078": ["SQ", "AuthorObserverSequence"],
|
|
"A07A": ["SQ", "ParticipantSequence"],
|
|
"A07C": ["SQ", "CustodialOrganizationSequence"],
|
|
"A080": ["CS", "ParticipationType"],
|
|
"A082": ["DT", "ParticipationDateTime"],
|
|
"A084": ["CS", "ObserverType"],
|
|
"A085": ["SQ", "ProcedureIdentifierCodeSequenceTrial"],
|
|
"A088": ["SQ", "VerifyingObserverIdentificationCodeSequence"],
|
|
"A089": ["OB", "ObjectDirectoryBinaryIdentifierTrial"],
|
|
"A090": ["SQ", "EquivalentCDADocumentSequence"],
|
|
"A0B0": ["US", "ReferencedWaveformChannels"],
|
|
"A110": ["DA", "DateOfDocumentOrVerbalTransactionTrial"],
|
|
"A112": ["TM", "TimeOfDocumentCreationOrVerbalTransactionTrial"],
|
|
"A120": ["DT", "DateTime"],
|
|
"A121": ["DA", "Date"],
|
|
"A122": ["TM", "Time"],
|
|
"A123": ["PN", "PersonName"],
|
|
"A124": ["UI", "UID"],
|
|
"A125": ["CS", "ReportStatusIDTrial"],
|
|
"A130": ["CS", "TemporalRangeType"],
|
|
"A132": ["UL", "ReferencedSamplePositions"],
|
|
"A136": ["US", "ReferencedFrameNumbers"],
|
|
"A138": ["DS", "ReferencedTimeOffsets"],
|
|
"A13A": ["DT", "ReferencedDateTime"],
|
|
"A160": ["UT", "TextValue"],
|
|
"A167": ["SQ", "ObservationCategoryCodeSequenceTrial"],
|
|
"A168": ["SQ", "ConceptCodeSequence"],
|
|
"A16A": ["ST", "BibliographicCitationTrial"],
|
|
"A170": ["SQ", "PurposeOfReferenceCodeSequence"],
|
|
"A171": ["UI", "ObservationUIDTrial"],
|
|
"A172": ["UI", "ReferencedObservationUIDTrial"],
|
|
"A173": ["CS", "ReferencedObservationClassTrial"],
|
|
"A174": ["CS", "ReferencedObjectObservationClassTrial"],
|
|
"A180": ["US", "AnnotationGroupNumber"],
|
|
"A192": ["DA", "ObservationDateTrial"],
|
|
"A193": ["TM", "ObservationTimeTrial"],
|
|
"A194": ["CS", "MeasurementAutomationTrial"],
|
|
"A195": ["SQ", "ModifierCodeSequence"],
|
|
"A224": ["ST", "IdentificationDescriptionTrial"],
|
|
"A290": ["CS", "CoordinatesSetGeometricTypeTrial"],
|
|
"A296": ["SQ", "AlgorithmCodeSequenceTrial"],
|
|
"A297": ["ST", "AlgorithmDescriptionTrial"],
|
|
"A29A": ["SL", "PixelCoordinatesSetTrial"],
|
|
"A300": ["SQ", "MeasuredValueSequence"],
|
|
"A301": ["SQ", "NumericValueQualifierCodeSequence"],
|
|
"A307": ["PN", "CurrentObserverTrial"],
|
|
"A30A": ["DS", "NumericValue"],
|
|
"A313": ["SQ", "ReferencedAccessionSequenceTrial"],
|
|
"A33A": ["ST", "ReportStatusCommentTrial"],
|
|
"A340": ["SQ", "ProcedureContextSequenceTrial"],
|
|
"A352": ["PN", "VerbalSourceTrial"],
|
|
"A353": ["ST", "AddressTrial"],
|
|
"A354": ["LO", "TelephoneNumberTrial"],
|
|
"A358": ["SQ", "VerbalSourceIdentifierCodeSequenceTrial"],
|
|
"A360": ["SQ", "PredecessorDocumentsSequence"],
|
|
"A370": ["SQ", "ReferencedRequestSequence"],
|
|
"A372": ["SQ", "PerformedProcedureCodeSequence"],
|
|
"A375": ["SQ", "CurrentRequestedProcedureEvidenceSequence"],
|
|
"A380": ["SQ", "ReportDetailSequenceTrial"],
|
|
"A385": ["SQ", "PertinentOtherEvidenceSequence"],
|
|
"A390": ["SQ", "HL7StructuredDocumentReferenceSequence"],
|
|
"A402": ["UI", "ObservationSubjectUIDTrial"],
|
|
"A403": ["CS", "ObservationSubjectClassTrial"],
|
|
"A404": ["SQ", "ObservationSubjectTypeCodeSequenceTrial"],
|
|
"A491": ["CS", "CompletionFlag"],
|
|
"A492": ["LO", "CompletionFlagDescription"],
|
|
"A493": ["CS", "VerificationFlag"],
|
|
"A494": ["CS", "ArchiveRequested"],
|
|
"A496": ["CS", "PreliminaryFlag"],
|
|
"A504": ["SQ", "ContentTemplateSequence"],
|
|
"A525": ["SQ", "IdenticalDocumentsSequence"],
|
|
"A600": ["CS", "ObservationSubjectContextFlagTrial"],
|
|
"A601": ["CS", "ObserverContextFlagTrial"],
|
|
"A603": ["CS", "ProcedureContextFlagTrial"],
|
|
"A730": ["SQ", "ContentSequence"],
|
|
"A731": ["SQ", "RelationshipSequenceTrial"],
|
|
"A732": ["SQ", "RelationshipTypeCodeSequenceTrial"],
|
|
"A744": ["SQ", "LanguageCodeSequenceTrial"],
|
|
"A992": ["ST", "UniformResourceLocatorTrial"],
|
|
"B020": ["SQ", "WaveformAnnotationSequence"],
|
|
"DB00": ["CS", "TemplateIdentifier"],
|
|
"DB06": ["DT", "TemplateVersion"],
|
|
"DB07": ["DT", "TemplateLocalVersion"],
|
|
"DB0B": ["CS", "TemplateExtensionFlag"],
|
|
"DB0C": ["UI", "TemplateExtensionOrganizationUID"],
|
|
"DB0D": ["UI", "TemplateExtensionCreatorUID"],
|
|
"DB73": ["UL", "ReferencedContentItemIdentifier"],
|
|
"E001": ["ST", "HL7InstanceIdentifier"],
|
|
"E004": ["DT", "HL7DocumentEffectiveTime"],
|
|
"E006": ["SQ", "HL7DocumentTypeCodeSequence"],
|
|
"E008": ["SQ", "DocumentClassCodeSequence"],
|
|
"E010": ["UT", "RetrieveURI"],
|
|
"E011": ["UI", "RetrieveLocationUID"],
|
|
"E020": ["CS", "TypeOfInstances"],
|
|
"E021": ["SQ", "DICOMRetrievalSequence"],
|
|
"E022": ["SQ", "DICOMMediaRetrievalSequence"],
|
|
"E023": ["SQ", "WADORetrievalSequence"],
|
|
"E024": ["SQ", "XDSRetrievalSequence"],
|
|
"E030": ["UI", "RepositoryUniqueID"],
|
|
"E031": ["UI", "HomeCommunityID"]
|
|
},
|
|
"0042": {
|
|
"0010": ["ST", "DocumentTitle"],
|
|
"0011": ["OB", "EncapsulatedDocument"],
|
|
"0012": ["LO", "MIMETypeOfEncapsulatedDocument"],
|
|
"0013": ["SQ", "SourceInstanceSequence"],
|
|
"0014": ["LO", "ListOfMIMETypes"]
|
|
},
|
|
"0044": {
|
|
"0001": ["ST", "ProductPackageIdentifier"],
|
|
"0002": ["CS", "SubstanceAdministrationApproval"],
|
|
"0003": ["LT", "ApprovalStatusFurtherDescription"],
|
|
"0004": ["DT", "ApprovalStatusDateTime"],
|
|
"0007": ["SQ", "ProductTypeCodeSequence"],
|
|
"0008": ["LO", "ProductName"],
|
|
"0009": ["LT", "ProductDescription"],
|
|
"000A": ["LO", "ProductLotIdentifier"],
|
|
"000B": ["DT", "ProductExpirationDateTime"],
|
|
"0010": ["DT", "SubstanceAdministrationDateTime"],
|
|
"0011": ["LO", "SubstanceAdministrationNotes"],
|
|
"0012": ["LO", "SubstanceAdministrationDeviceID"],
|
|
"0013": ["SQ", "ProductParameterSequence"],
|
|
"0019": ["SQ", "SubstanceAdministrationParameterSequence"]
|
|
},
|
|
"0046": {
|
|
"0012": ["LO", "LensDescription"],
|
|
"0014": ["SQ", "RightLensSequence"],
|
|
"0015": ["SQ", "LeftLensSequence"],
|
|
"0016": ["SQ", "UnspecifiedLateralityLensSequence"],
|
|
"0018": ["SQ", "CylinderSequence"],
|
|
"0028": ["SQ", "PrismSequence"],
|
|
"0030": ["FD", "HorizontalPrismPower"],
|
|
"0032": ["CS", "HorizontalPrismBase"],
|
|
"0034": ["FD", "VerticalPrismPower"],
|
|
"0036": ["CS", "VerticalPrismBase"],
|
|
"0038": ["CS", "LensSegmentType"],
|
|
"0040": ["FD", "OpticalTransmittance"],
|
|
"0042": ["FD", "ChannelWidth"],
|
|
"0044": ["FD", "PupilSize"],
|
|
"0046": ["FD", "CornealSize"],
|
|
"0050": ["SQ", "AutorefractionRightEyeSequence"],
|
|
"0052": ["SQ", "AutorefractionLeftEyeSequence"],
|
|
"0060": ["FD", "DistancePupillaryDistance"],
|
|
"0062": ["FD", "NearPupillaryDistance"],
|
|
"0063": ["FD", "IntermediatePupillaryDistance"],
|
|
"0064": ["FD", "OtherPupillaryDistance"],
|
|
"0070": ["SQ", "KeratometryRightEyeSequence"],
|
|
"0071": ["SQ", "KeratometryLeftEyeSequence"],
|
|
"0074": ["SQ", "SteepKeratometricAxisSequence"],
|
|
"0075": ["FD", "RadiusOfCurvature"],
|
|
"0076": ["FD", "KeratometricPower"],
|
|
"0077": ["FD", "KeratometricAxis"],
|
|
"0080": ["SQ", "FlatKeratometricAxisSequence"],
|
|
"0092": ["CS", "BackgroundColor"],
|
|
"0094": ["CS", "Optotype"],
|
|
"0095": ["CS", "OptotypePresentation"],
|
|
"0097": ["SQ", "SubjectiveRefractionRightEyeSequence"],
|
|
"0098": ["SQ", "SubjectiveRefractionLeftEyeSequence"],
|
|
"0100": ["SQ", "AddNearSequence"],
|
|
"0101": ["SQ", "AddIntermediateSequence"],
|
|
"0102": ["SQ", "AddOtherSequence"],
|
|
"0104": ["FD", "AddPower"],
|
|
"0106": ["FD", "ViewingDistance"],
|
|
"0121": ["SQ", "VisualAcuityTypeCodeSequence"],
|
|
"0122": ["SQ", "VisualAcuityRightEyeSequence"],
|
|
"0123": ["SQ", "VisualAcuityLeftEyeSequence"],
|
|
"0124": ["SQ", "VisualAcuityBothEyesOpenSequence"],
|
|
"0125": ["CS", "ViewingDistanceType"],
|
|
"0135": ["SS", "VisualAcuityModifiers"],
|
|
"0137": ["FD", "DecimalVisualAcuity"],
|
|
"0139": ["LO", "OptotypeDetailedDefinition"],
|
|
"0145": ["SQ", "ReferencedRefractiveMeasurementsSequence"],
|
|
"0146": ["FD", "SpherePower"],
|
|
"0147": ["FD", "CylinderPower"]
|
|
},
|
|
"0048": {
|
|
"0001": ["FL", "ImagedVolumeWidth"],
|
|
"0002": ["FL", "ImagedVolumeHeight"],
|
|
"0003": ["FL", "ImagedVolumeDepth"],
|
|
"0006": ["UL", "TotalPixelMatrixColumns"],
|
|
"0007": ["UL", "TotalPixelMatrixRows"],
|
|
"0008": ["SQ", "TotalPixelMatrixOriginSequence"],
|
|
"0010": ["CS", "SpecimenLabelInImage"],
|
|
"0011": ["CS", "FocusMethod"],
|
|
"0012": ["CS", "ExtendedDepthOfField"],
|
|
"0013": ["US", "NumberOfFocalPlanes"],
|
|
"0014": ["FL", "DistanceBetweenFocalPlanes"],
|
|
"0015": ["US", "RecommendedAbsentPixelCIELabValue"],
|
|
"0100": ["SQ", "IlluminatorTypeCodeSequence"],
|
|
"0102": ["DS", "ImageOrientationSlide"],
|
|
"0105": ["SQ", "OpticalPathSequence"],
|
|
"0106": ["SH", "OpticalPathIdentifier"],
|
|
"0107": ["ST", "OpticalPathDescription"],
|
|
"0108": ["SQ", "IlluminationColorCodeSequence"],
|
|
"0110": ["SQ", "SpecimenReferenceSequence"],
|
|
"0111": ["DS", "CondenserLensPower"],
|
|
"0112": ["DS", "ObjectiveLensPower"],
|
|
"0113": ["DS", "ObjectiveLensNumericalAperture"],
|
|
"0120": ["SQ", "PaletteColorLookupTableSequence"],
|
|
"0200": ["SQ", "ReferencedImageNavigationSequence"],
|
|
"0201": ["US", "TopLeftHandCornerOfLocalizerArea"],
|
|
"0202": ["US", "BottomRightHandCornerOfLocalizerArea"],
|
|
"0207": ["SQ", "OpticalPathIdentificationSequence"],
|
|
"021A": ["SQ", "PlanePositionSlideSequence"],
|
|
"021E": ["SL", "RowPositionInTotalImagePixelMatrix"],
|
|
"021F": ["SL", "ColumnPositionInTotalImagePixelMatrix"],
|
|
"0301": ["CS", "PixelOriginInterpretation"]
|
|
},
|
|
"0050": {
|
|
"0004": ["CS", "CalibrationImage"],
|
|
"0010": ["SQ", "DeviceSequence"],
|
|
"0012": ["SQ", "ContainerComponentTypeCodeSequence"],
|
|
"0013": ["FD", "ContainerComponentThickness"],
|
|
"0014": ["DS", "DeviceLength"],
|
|
"0015": ["FD", "ContainerComponentWidth"],
|
|
"0016": ["DS", "DeviceDiameter"],
|
|
"0017": ["CS", "DeviceDiameterUnits"],
|
|
"0018": ["DS", "DeviceVolume"],
|
|
"0019": ["DS", "InterMarkerDistance"],
|
|
"001A": ["CS", "ContainerComponentMaterial"],
|
|
"001B": ["LO", "ContainerComponentID"],
|
|
"001C": ["FD", "ContainerComponentLength"],
|
|
"001D": ["FD", "ContainerComponentDiameter"],
|
|
"001E": ["LO", "ContainerComponentDescription"],
|
|
"0020": ["LO", "DeviceDescription"]
|
|
},
|
|
"0052": {
|
|
"0001": ["FL", "ContrastBolusIngredientPercentByVolume"],
|
|
"0002": ["FD", "OCTFocalDistance"],
|
|
"0003": ["FD", "BeamSpotSize"],
|
|
"0004": ["FD", "EffectiveRefractiveIndex"],
|
|
"0006": ["CS", "OCTAcquisitionDomain"],
|
|
"0007": ["FD", "OCTOpticalCenterWavelength"],
|
|
"0008": ["FD", "AxialResolution"],
|
|
"0009": ["FD", "RangingDepth"],
|
|
"0011": ["FD", "ALineRate"],
|
|
"0012": ["US", "ALinesPerFrame"],
|
|
"0013": ["FD", "CatheterRotationalRate"],
|
|
"0014": ["FD", "ALinePixelSpacing"],
|
|
"0016": ["SQ", "ModeOfPercutaneousAccessSequence"],
|
|
"0025": ["SQ", "IntravascularOCTFrameTypeSequence"],
|
|
"0026": ["CS", "OCTZOffsetApplied"],
|
|
"0027": ["SQ", "IntravascularFrameContentSequence"],
|
|
"0028": ["FD", "IntravascularLongitudinalDistance"],
|
|
"0029": ["SQ", "IntravascularOCTFrameContentSequence"],
|
|
"0030": ["SS", "OCTZOffsetCorrection"],
|
|
"0031": ["CS", "CatheterDirectionOfRotation"],
|
|
"0033": ["FD", "SeamLineLocation"],
|
|
"0034": ["FD", "FirstALineLocation"],
|
|
"0036": ["US", "SeamLineIndex"],
|
|
"0038": ["US", "NumberOfPaddedAlines"],
|
|
"0039": ["CS", "InterpolationType"],
|
|
"003A": ["CS", "RefractiveIndexApplied"]
|
|
},
|
|
"0054": {
|
|
"0010": ["US", "EnergyWindowVector"],
|
|
"0011": ["US", "NumberOfEnergyWindows"],
|
|
"0012": ["SQ", "EnergyWindowInformationSequence"],
|
|
"0013": ["SQ", "EnergyWindowRangeSequence"],
|
|
"0014": ["DS", "EnergyWindowLowerLimit"],
|
|
"0015": ["DS", "EnergyWindowUpperLimit"],
|
|
"0016": ["SQ", "RadiopharmaceuticalInformationSequence"],
|
|
"0017": ["IS", "ResidualSyringeCounts"],
|
|
"0018": ["SH", "EnergyWindowName"],
|
|
"0020": ["US", "DetectorVector"],
|
|
"0021": ["US", "NumberOfDetectors"],
|
|
"0022": ["SQ", "DetectorInformationSequence"],
|
|
"0030": ["US", "PhaseVector"],
|
|
"0031": ["US", "NumberOfPhases"],
|
|
"0032": ["SQ", "PhaseInformationSequence"],
|
|
"0033": ["US", "NumberOfFramesInPhase"],
|
|
"0036": ["IS", "PhaseDelay"],
|
|
"0038": ["IS", "PauseBetweenFrames"],
|
|
"0039": ["CS", "PhaseDescription"],
|
|
"0050": ["US", "RotationVector"],
|
|
"0051": ["US", "NumberOfRotations"],
|
|
"0052": ["SQ", "RotationInformationSequence"],
|
|
"0053": ["US", "NumberOfFramesInRotation"],
|
|
"0060": ["US", "RRIntervalVector"],
|
|
"0061": ["US", "NumberOfRRIntervals"],
|
|
"0062": ["SQ", "GatedInformationSequence"],
|
|
"0063": ["SQ", "DataInformationSequence"],
|
|
"0070": ["US", "TimeSlotVector"],
|
|
"0071": ["US", "NumberOfTimeSlots"],
|
|
"0072": ["SQ", "TimeSlotInformationSequence"],
|
|
"0073": ["DS", "TimeSlotTime"],
|
|
"0080": ["US", "SliceVector"],
|
|
"0081": ["US", "NumberOfSlices"],
|
|
"0090": ["US", "AngularViewVector"],
|
|
"0100": ["US", "TimeSliceVector"],
|
|
"0101": ["US", "NumberOfTimeSlices"],
|
|
"0200": ["DS", "StartAngle"],
|
|
"0202": ["CS", "TypeOfDetectorMotion"],
|
|
"0210": ["IS", "TriggerVector"],
|
|
"0211": ["US", "NumberOfTriggersInPhase"],
|
|
"0220": ["SQ", "ViewCodeSequence"],
|
|
"0222": ["SQ", "ViewModifierCodeSequence"],
|
|
"0300": ["SQ", "RadionuclideCodeSequence"],
|
|
"0302": ["SQ", "AdministrationRouteCodeSequence"],
|
|
"0304": ["SQ", "RadiopharmaceuticalCodeSequence"],
|
|
"0306": ["SQ", "CalibrationDataSequence"],
|
|
"0308": ["US", "EnergyWindowNumber"],
|
|
"0400": ["SH", "ImageID"],
|
|
"0410": ["SQ", "PatientOrientationCodeSequence"],
|
|
"0412": ["SQ", "PatientOrientationModifierCodeSequence"],
|
|
"0414": ["SQ", "PatientGantryRelationshipCodeSequence"],
|
|
"0500": ["CS", "SliceProgressionDirection"],
|
|
"1000": ["CS", "SeriesType"],
|
|
"1001": ["CS", "Units"],
|
|
"1002": ["CS", "CountsSource"],
|
|
"1004": ["CS", "ReprojectionMethod"],
|
|
"1006": ["CS", "SUVType"],
|
|
"1100": ["CS", "RandomsCorrectionMethod"],
|
|
"1101": ["LO", "AttenuationCorrectionMethod"],
|
|
"1102": ["CS", "DecayCorrection"],
|
|
"1103": ["LO", "ReconstructionMethod"],
|
|
"1104": ["LO", "DetectorLinesOfResponseUsed"],
|
|
"1105": ["LO", "ScatterCorrectionMethod"],
|
|
"1200": ["DS", "AxialAcceptance"],
|
|
"1201": ["IS", "AxialMash"],
|
|
"1202": ["IS", "TransverseMash"],
|
|
"1203": ["DS", "DetectorElementSize"],
|
|
"1210": ["DS", "CoincidenceWindowWidth"],
|
|
"1220": ["CS", "SecondaryCountsType"],
|
|
"1300": ["DS", "FrameReferenceTime"],
|
|
"1310": ["IS", "PrimaryPromptsCountsAccumulated"],
|
|
"1311": ["IS", "SecondaryCountsAccumulated"],
|
|
"1320": ["DS", "SliceSensitivityFactor"],
|
|
"1321": ["DS", "DecayFactor"],
|
|
"1322": ["DS", "DoseCalibrationFactor"],
|
|
"1323": ["DS", "ScatterFractionFactor"],
|
|
"1324": ["DS", "DeadTimeFactor"],
|
|
"1330": ["US", "ImageIndex"],
|
|
"1400": ["CS", "CountsIncluded"],
|
|
"1401": ["CS", "DeadTimeCorrectionFlag"]
|
|
},
|
|
"0060": {
|
|
"3000": ["SQ", "HistogramSequence"],
|
|
"3002": ["US", "HistogramNumberOfBins"],
|
|
"3004": ["SS", "HistogramFirstBinValue"],
|
|
"3006": ["SS", "HistogramLastBinValue"],
|
|
"3008": ["US", "HistogramBinWidth"],
|
|
"3010": ["LO", "HistogramExplanation"],
|
|
"3020": ["UL", "HistogramData"]
|
|
},
|
|
"0062": {
|
|
"0001": ["CS", "SegmentationType"],
|
|
"0002": ["SQ", "SegmentSequence"],
|
|
"0003": ["SQ", "SegmentedPropertyCategoryCodeSequence"],
|
|
"0004": ["US", "SegmentNumber"],
|
|
"0005": ["LO", "SegmentLabel"],
|
|
"0006": ["ST", "SegmentDescription"],
|
|
"0008": ["CS", "SegmentAlgorithmType"],
|
|
"0009": ["LO", "SegmentAlgorithmName"],
|
|
"000A": ["SQ", "SegmentIdentificationSequence"],
|
|
"000B": ["US", "ReferencedSegmentNumber"],
|
|
"000C": ["US", "RecommendedDisplayGrayscaleValue"],
|
|
"000D": ["US", "RecommendedDisplayCIELabValue"],
|
|
"000E": ["US", "MaximumFractionalValue"],
|
|
"000F": ["SQ", "SegmentedPropertyTypeCodeSequence"],
|
|
"0010": ["CS", "SegmentationFractionalType"]
|
|
},
|
|
"0064": {
|
|
"0002": ["SQ", "DeformableRegistrationSequence"],
|
|
"0003": ["UI", "SourceFrameOfReferenceUID"],
|
|
"0005": ["SQ", "DeformableRegistrationGridSequence"],
|
|
"0007": ["UL", "GridDimensions"],
|
|
"0008": ["FD", "GridResolution"],
|
|
"0009": ["OF", "VectorGridData"],
|
|
"000F": ["SQ", "PreDeformationMatrixRegistrationSequence"],
|
|
"0010": ["SQ", "PostDeformationMatrixRegistrationSequence"]
|
|
},
|
|
"0066": {
|
|
"0001": ["UL", "NumberOfSurfaces"],
|
|
"0002": ["SQ", "SurfaceSequence"],
|
|
"0003": ["UL", "SurfaceNumber"],
|
|
"0004": ["LT", "SurfaceComments"],
|
|
"0009": ["CS", "SurfaceProcessing"],
|
|
"000A": ["FL", "SurfaceProcessingRatio"],
|
|
"000B": ["LO", "SurfaceProcessingDescription"],
|
|
"000C": ["FL", "RecommendedPresentationOpacity"],
|
|
"000D": ["CS", "RecommendedPresentationType"],
|
|
"000E": ["CS", "FiniteVolume"],
|
|
"0010": ["CS", "Manifold"],
|
|
"0011": ["SQ", "SurfacePointsSequence"],
|
|
"0012": ["SQ", "SurfacePointsNormalsSequence"],
|
|
"0013": ["SQ", "SurfaceMeshPrimitivesSequence"],
|
|
"0015": ["UL", "NumberOfSurfacePoints"],
|
|
"0016": ["OF", "PointCoordinatesData"],
|
|
"0017": ["FL", "PointPositionAccuracy"],
|
|
"0018": ["FL", "MeanPointDistance"],
|
|
"0019": ["FL", "MaximumPointDistance"],
|
|
"001A": ["FL", "PointsBoundingBoxCoordinates"],
|
|
"001B": ["FL", "AxisOfRotation"],
|
|
"001C": ["FL", "CenterOfRotation"],
|
|
"001E": ["UL", "NumberOfVectors"],
|
|
"001F": ["US", "VectorDimensionality"],
|
|
"0020": ["FL", "VectorAccuracy"],
|
|
"0021": ["OF", "VectorCoordinateData"],
|
|
"0023": ["OW", "TrianglePointIndexList"],
|
|
"0024": ["OW", "EdgePointIndexList"],
|
|
"0025": ["OW", "VertexPointIndexList"],
|
|
"0026": ["SQ", "TriangleStripSequence"],
|
|
"0027": ["SQ", "TriangleFanSequence"],
|
|
"0028": ["SQ", "LineSequence"],
|
|
"0029": ["OW", "PrimitivePointIndexList"],
|
|
"002A": ["UL", "SurfaceCount"],
|
|
"002B": ["SQ", "ReferencedSurfaceSequence"],
|
|
"002C": ["UL", "ReferencedSurfaceNumber"],
|
|
"002D": ["SQ", "SegmentSurfaceGenerationAlgorithmIdentificationSequence"],
|
|
"002E": ["SQ", "SegmentSurfaceSourceInstanceSequence"],
|
|
"002F": ["SQ", "AlgorithmFamilyCodeSequence"],
|
|
"0030": ["SQ", "AlgorithmNameCodeSequence"],
|
|
"0031": ["LO", "AlgorithmVersion"],
|
|
"0032": ["LT", "AlgorithmParameters"],
|
|
"0034": ["SQ", "FacetSequence"],
|
|
"0035": ["SQ", "SurfaceProcessingAlgorithmIdentificationSequence"],
|
|
"0036": ["LO", "AlgorithmName"]
|
|
},
|
|
"0068": {
|
|
"6210": ["LO", "ImplantSize"],
|
|
"6221": ["LO", "ImplantTemplateVersion"],
|
|
"6222": ["SQ", "ReplacedImplantTemplateSequence"],
|
|
"6223": ["CS", "ImplantType"],
|
|
"6224": ["SQ", "DerivationImplantTemplateSequence"],
|
|
"6225": ["SQ", "OriginalImplantTemplateSequence"],
|
|
"6226": ["DT", "EffectiveDateTime"],
|
|
"6230": ["SQ", "ImplantTargetAnatomySequence"],
|
|
"6260": ["SQ", "InformationFromManufacturerSequence"],
|
|
"6265": ["SQ", "NotificationFromManufacturerSequence"],
|
|
"6270": ["DT", "InformationIssueDateTime"],
|
|
"6280": ["ST", "InformationSummary"],
|
|
"62A0": ["SQ", "ImplantRegulatoryDisapprovalCodeSequence"],
|
|
"62A5": ["FD", "OverallTemplateSpatialTolerance"],
|
|
"62C0": ["SQ", "HPGLDocumentSequence"],
|
|
"62D0": ["US", "HPGLDocumentID"],
|
|
"62D5": ["LO", "HPGLDocumentLabel"],
|
|
"62E0": ["SQ", "ViewOrientationCodeSequence"],
|
|
"62F0": ["FD", "ViewOrientationModifier"],
|
|
"62F2": ["FD", "HPGLDocumentScaling"],
|
|
"6300": ["OB", "HPGLDocument"],
|
|
"6310": ["US", "HPGLContourPenNumber"],
|
|
"6320": ["SQ", "HPGLPenSequence"],
|
|
"6330": ["US", "HPGLPenNumber"],
|
|
"6340": ["LO", "HPGLPenLabel"],
|
|
"6345": ["ST", "HPGLPenDescription"],
|
|
"6346": ["FD", "RecommendedRotationPoint"],
|
|
"6347": ["FD", "BoundingRectangle"],
|
|
"6350": ["US", "ImplantTemplate3DModelSurfaceNumber"],
|
|
"6360": ["SQ", "SurfaceModelDescriptionSequence"],
|
|
"6380": ["LO", "SurfaceModelLabel"],
|
|
"6390": ["FD", "SurfaceModelScalingFactor"],
|
|
"63A0": ["SQ", "MaterialsCodeSequence"],
|
|
"63A4": ["SQ", "CoatingMaterialsCodeSequence"],
|
|
"63A8": ["SQ", "ImplantTypeCodeSequence"],
|
|
"63AC": ["SQ", "FixationMethodCodeSequence"],
|
|
"63B0": ["SQ", "MatingFeatureSetsSequence"],
|
|
"63C0": ["US", "MatingFeatureSetID"],
|
|
"63D0": ["LO", "MatingFeatureSetLabel"],
|
|
"63E0": ["SQ", "MatingFeatureSequence"],
|
|
"63F0": ["US", "MatingFeatureID"],
|
|
"6400": ["SQ", "MatingFeatureDegreeOfFreedomSequence"],
|
|
"6410": ["US", "DegreeOfFreedomID"],
|
|
"6420": ["CS", "DegreeOfFreedomType"],
|
|
"6430": ["SQ", "TwoDMatingFeatureCoordinatesSequence"],
|
|
"6440": ["US", "ReferencedHPGLDocumentID"],
|
|
"6450": ["FD", "TwoDMatingPoint"],
|
|
"6460": ["FD", "TwoDMatingAxes"],
|
|
"6470": ["SQ", "TwoDDegreeOfFreedomSequence"],
|
|
"6490": ["FD", "ThreeDDegreeOfFreedomAxis"],
|
|
"64A0": ["FD", "RangeOfFreedom"],
|
|
"64C0": ["FD", "ThreeDMatingPoint"],
|
|
"64D0": ["FD", "ThreeDMatingAxes"],
|
|
"64F0": ["FD", "TwoDDegreeOfFreedomAxis"],
|
|
"6500": ["SQ", "PlanningLandmarkPointSequence"],
|
|
"6510": ["SQ", "PlanningLandmarkLineSequence"],
|
|
"6520": ["SQ", "PlanningLandmarkPlaneSequence"],
|
|
"6530": ["US", "PlanningLandmarkID"],
|
|
"6540": ["LO", "PlanningLandmarkDescription"],
|
|
"6545": ["SQ", "PlanningLandmarkIdentificationCodeSequence"],
|
|
"6550": ["SQ", "TwoDPointCoordinatesSequence"],
|
|
"6560": ["FD", "TwoDPointCoordinates"],
|
|
"6590": ["FD", "ThreeDPointCoordinates"],
|
|
"65A0": ["SQ", "TwoDLineCoordinatesSequence"],
|
|
"65B0": ["FD", "TwoDLineCoordinates"],
|
|
"65D0": ["FD", "ThreeDLineCoordinates"],
|
|
"65E0": ["SQ", "TwoDPlaneCoordinatesSequence"],
|
|
"65F0": ["FD", "TwoDPlaneIntersection"],
|
|
"6610": ["FD", "ThreeDPlaneOrigin"],
|
|
"6620": ["FD", "ThreeDPlaneNormal"]
|
|
},
|
|
"0070": {
|
|
"0001": ["SQ", "GraphicAnnotationSequence"],
|
|
"0002": ["CS", "GraphicLayer"],
|
|
"0003": ["CS", "BoundingBoxAnnotationUnits"],
|
|
"0004": ["CS", "AnchorPointAnnotationUnits"],
|
|
"0005": ["CS", "GraphicAnnotationUnits"],
|
|
"0006": ["ST", "UnformattedTextValue"],
|
|
"0008": ["SQ", "TextObjectSequence"],
|
|
"0009": ["SQ", "GraphicObjectSequence"],
|
|
"0010": ["FL", "BoundingBoxTopLeftHandCorner"],
|
|
"0011": ["FL", "BoundingBoxBottomRightHandCorner"],
|
|
"0012": ["CS", "BoundingBoxTextHorizontalJustification"],
|
|
"0014": ["FL", "AnchorPoint"],
|
|
"0015": ["CS", "AnchorPointVisibility"],
|
|
"0020": ["US", "GraphicDimensions"],
|
|
"0021": ["US", "NumberOfGraphicPoints"],
|
|
"0022": ["FL", "GraphicData"],
|
|
"0023": ["CS", "GraphicType"],
|
|
"0024": ["CS", "GraphicFilled"],
|
|
"0040": ["IS", "ImageRotationRetired"],
|
|
"0041": ["CS", "ImageHorizontalFlip"],
|
|
"0042": ["US", "ImageRotation"],
|
|
"0050": ["US", "DisplayedAreaTopLeftHandCornerTrial"],
|
|
"0051": ["US", "DisplayedAreaBottomRightHandCornerTrial"],
|
|
"0052": ["SL", "DisplayedAreaTopLeftHandCorner"],
|
|
"0053": ["SL", "DisplayedAreaBottomRightHandCorner"],
|
|
"005A": ["SQ", "DisplayedAreaSelectionSequence"],
|
|
"0060": ["SQ", "GraphicLayerSequence"],
|
|
"0062": ["IS", "GraphicLayerOrder"],
|
|
"0066": ["US", "GraphicLayerRecommendedDisplayGrayscaleValue"],
|
|
"0067": ["US", "GraphicLayerRecommendedDisplayRGBValue"],
|
|
"0068": ["LO", "GraphicLayerDescription"],
|
|
"0080": ["CS", "ContentLabel"],
|
|
"0081": ["LO", "ContentDescription"],
|
|
"0082": ["DA", "PresentationCreationDate"],
|
|
"0083": ["TM", "PresentationCreationTime"],
|
|
"0084": ["PN", "ContentCreatorName"],
|
|
"0086": ["SQ", "ContentCreatorIdentificationCodeSequence"],
|
|
"0087": ["SQ", "AlternateContentDescriptionSequence"],
|
|
"0100": ["CS", "PresentationSizeMode"],
|
|
"0101": ["DS", "PresentationPixelSpacing"],
|
|
"0102": ["IS", "PresentationPixelAspectRatio"],
|
|
"0103": ["FL", "PresentationPixelMagnificationRatio"],
|
|
"0207": ["LO", "GraphicGroupLabel"],
|
|
"0208": ["ST", "GraphicGroupDescription"],
|
|
"0209": ["SQ", "CompoundGraphicSequence"],
|
|
"0226": ["UL", "CompoundGraphicInstanceID"],
|
|
"0227": ["LO", "FontName"],
|
|
"0228": ["CS", "FontNameType"],
|
|
"0229": ["LO", "CSSFontName"],
|
|
"0230": ["FD", "RotationAngle"],
|
|
"0231": ["SQ", "TextStyleSequence"],
|
|
"0232": ["SQ", "LineStyleSequence"],
|
|
"0233": ["SQ", "FillStyleSequence"],
|
|
"0234": ["SQ", "GraphicGroupSequence"],
|
|
"0241": ["US", "TextColorCIELabValue"],
|
|
"0242": ["CS", "HorizontalAlignment"],
|
|
"0243": ["CS", "VerticalAlignment"],
|
|
"0244": ["CS", "ShadowStyle"],
|
|
"0245": ["FL", "ShadowOffsetX"],
|
|
"0246": ["FL", "ShadowOffsetY"],
|
|
"0247": ["US", "ShadowColorCIELabValue"],
|
|
"0248": ["CS", "Underlined"],
|
|
"0249": ["CS", "Bold"],
|
|
"0250": ["CS", "Italic"],
|
|
"0251": ["US", "PatternOnColorCIELabValue"],
|
|
"0252": ["US", "PatternOffColorCIELabValue"],
|
|
"0253": ["FL", "LineThickness"],
|
|
"0254": ["CS", "LineDashingStyle"],
|
|
"0255": ["UL", "LinePattern"],
|
|
"0256": ["OB", "FillPattern"],
|
|
"0257": ["CS", "FillMode"],
|
|
"0258": ["FL", "ShadowOpacity"],
|
|
"0261": ["FL", "GapLength"],
|
|
"0262": ["FL", "DiameterOfVisibility"],
|
|
"0273": ["FL", "RotationPoint"],
|
|
"0274": ["CS", "TickAlignment"],
|
|
"0278": ["CS", "ShowTickLabel"],
|
|
"0279": ["CS", "TickLabelAlignment"],
|
|
"0282": ["CS", "CompoundGraphicUnits"],
|
|
"0284": ["FL", "PatternOnOpacity"],
|
|
"0285": ["FL", "PatternOffOpacity"],
|
|
"0287": ["SQ", "MajorTicksSequence"],
|
|
"0288": ["FL", "TickPosition"],
|
|
"0289": ["SH", "TickLabel"],
|
|
"0294": ["CS", "CompoundGraphicType"],
|
|
"0295": ["UL", "GraphicGroupID"],
|
|
"0306": ["CS", "ShapeType"],
|
|
"0308": ["SQ", "RegistrationSequence"],
|
|
"0309": ["SQ", "MatrixRegistrationSequence"],
|
|
"030A": ["SQ", "MatrixSequence"],
|
|
"030C": ["CS", "FrameOfReferenceTransformationMatrixType"],
|
|
"030D": ["SQ", "RegistrationTypeCodeSequence"],
|
|
"030F": ["ST", "FiducialDescription"],
|
|
"0310": ["SH", "FiducialIdentifier"],
|
|
"0311": ["SQ", "FiducialIdentifierCodeSequence"],
|
|
"0312": ["FD", "ContourUncertaintyRadius"],
|
|
"0314": ["SQ", "UsedFiducialsSequence"],
|
|
"0318": ["SQ", "GraphicCoordinatesDataSequence"],
|
|
"031A": ["UI", "FiducialUID"],
|
|
"031C": ["SQ", "FiducialSetSequence"],
|
|
"031E": ["SQ", "FiducialSequence"],
|
|
"0401": ["US", "GraphicLayerRecommendedDisplayCIELabValue"],
|
|
"0402": ["SQ", "BlendingSequence"],
|
|
"0403": ["FL", "RelativeOpacity"],
|
|
"0404": ["SQ", "ReferencedSpatialRegistrationSequence"],
|
|
"0405": ["CS", "BlendingPosition"]
|
|
},
|
|
"0072": {
|
|
"0002": ["SH", "HangingProtocolName"],
|
|
"0004": ["LO", "HangingProtocolDescription"],
|
|
"0006": ["CS", "HangingProtocolLevel"],
|
|
"0008": ["LO", "HangingProtocolCreator"],
|
|
"000A": ["DT", "HangingProtocolCreationDateTime"],
|
|
"000C": ["SQ", "HangingProtocolDefinitionSequence"],
|
|
"000E": ["SQ", "HangingProtocolUserIdentificationCodeSequence"],
|
|
"0010": ["LO", "HangingProtocolUserGroupName"],
|
|
"0012": ["SQ", "SourceHangingProtocolSequence"],
|
|
"0014": ["US", "NumberOfPriorsReferenced"],
|
|
"0020": ["SQ", "ImageSetsSequence"],
|
|
"0022": ["SQ", "ImageSetSelectorSequence"],
|
|
"0024": ["CS", "ImageSetSelectorUsageFlag"],
|
|
"0026": ["AT", "SelectorAttribute"],
|
|
"0028": ["US", "SelectorValueNumber"],
|
|
"0030": ["SQ", "TimeBasedImageSetsSequence"],
|
|
"0032": ["US", "ImageSetNumber"],
|
|
"0034": ["CS", "ImageSetSelectorCategory"],
|
|
"0038": ["US", "RelativeTime"],
|
|
"003A": ["CS", "RelativeTimeUnits"],
|
|
"003C": ["SS", "AbstractPriorValue"],
|
|
"003E": ["SQ", "AbstractPriorCodeSequence"],
|
|
"0040": ["LO", "ImageSetLabel"],
|
|
"0050": ["CS", "SelectorAttributeVR"],
|
|
"0052": ["AT", "SelectorSequencePointer"],
|
|
"0054": ["LO", "SelectorSequencePointerPrivateCreator"],
|
|
"0056": ["LO", "SelectorAttributePrivateCreator"],
|
|
"0060": ["AT", "SelectorATValue"],
|
|
"0062": ["CS", "SelectorCSValue"],
|
|
"0064": ["IS", "SelectorISValue"],
|
|
"0066": ["LO", "SelectorLOValue"],
|
|
"0068": ["LT", "SelectorLTValue"],
|
|
"006A": ["PN", "SelectorPNValue"],
|
|
"006C": ["SH", "SelectorSHValue"],
|
|
"006E": ["ST", "SelectorSTValue"],
|
|
"0070": ["UT", "SelectorUTValue"],
|
|
"0072": ["DS", "SelectorDSValue"],
|
|
"0074": ["FD", "SelectorFDValue"],
|
|
"0076": ["FL", "SelectorFLValue"],
|
|
"0078": ["UL", "SelectorULValue"],
|
|
"007A": ["US", "SelectorUSValue"],
|
|
"007C": ["SL", "SelectorSLValue"],
|
|
"007E": ["SS", "SelectorSSValue"],
|
|
"0080": ["SQ", "SelectorCodeSequenceValue"],
|
|
"0100": ["US", "NumberOfScreens"],
|
|
"0102": ["SQ", "NominalScreenDefinitionSequence"],
|
|
"0104": ["US", "NumberOfVerticalPixels"],
|
|
"0106": ["US", "NumberOfHorizontalPixels"],
|
|
"0108": ["FD", "DisplayEnvironmentSpatialPosition"],
|
|
"010A": ["US", "ScreenMinimumGrayscaleBitDepth"],
|
|
"010C": ["US", "ScreenMinimumColorBitDepth"],
|
|
"010E": ["US", "ApplicationMaximumRepaintTime"],
|
|
"0200": ["SQ", "DisplaySetsSequence"],
|
|
"0202": ["US", "DisplaySetNumber"],
|
|
"0203": ["LO", "DisplaySetLabel"],
|
|
"0204": ["US", "DisplaySetPresentationGroup"],
|
|
"0206": ["LO", "DisplaySetPresentationGroupDescription"],
|
|
"0208": ["CS", "PartialDataDisplayHandling"],
|
|
"0210": ["SQ", "SynchronizedScrollingSequence"],
|
|
"0212": ["US", "DisplaySetScrollingGroup"],
|
|
"0214": ["SQ", "NavigationIndicatorSequence"],
|
|
"0216": ["US", "NavigationDisplaySet"],
|
|
"0218": ["US", "ReferenceDisplaySets"],
|
|
"0300": ["SQ", "ImageBoxesSequence"],
|
|
"0302": ["US", "ImageBoxNumber"],
|
|
"0304": ["CS", "ImageBoxLayoutType"],
|
|
"0306": ["US", "ImageBoxTileHorizontalDimension"],
|
|
"0308": ["US", "ImageBoxTileVerticalDimension"],
|
|
"0310": ["CS", "ImageBoxScrollDirection"],
|
|
"0312": ["CS", "ImageBoxSmallScrollType"],
|
|
"0314": ["US", "ImageBoxSmallScrollAmount"],
|
|
"0316": ["CS", "ImageBoxLargeScrollType"],
|
|
"0318": ["US", "ImageBoxLargeScrollAmount"],
|
|
"0320": ["US", "ImageBoxOverlapPriority"],
|
|
"0330": ["FD", "CineRelativeToRealTime"],
|
|
"0400": ["SQ", "FilterOperationsSequence"],
|
|
"0402": ["CS", "FilterByCategory"],
|
|
"0404": ["CS", "FilterByAttributePresence"],
|
|
"0406": ["CS", "FilterByOperator"],
|
|
"0420": ["US", "StructuredDisplayBackgroundCIELabValue"],
|
|
"0421": ["US", "EmptyImageBoxCIELabValue"],
|
|
"0422": ["SQ", "StructuredDisplayImageBoxSequence"],
|
|
"0424": ["SQ", "StructuredDisplayTextBoxSequence"],
|
|
"0427": ["SQ", "ReferencedFirstFrameSequence"],
|
|
"0430": ["SQ", "ImageBoxSynchronizationSequence"],
|
|
"0432": ["US", "SynchronizedImageBoxList"],
|
|
"0434": ["CS", "TypeOfSynchronization"],
|
|
"0500": ["CS", "BlendingOperationType"],
|
|
"0510": ["CS", "ReformattingOperationType"],
|
|
"0512": ["FD", "ReformattingThickness"],
|
|
"0514": ["FD", "ReformattingInterval"],
|
|
"0516": ["CS", "ReformattingOperationInitialViewDirection"],
|
|
"0520": ["CS", "ThreeDRenderingType"],
|
|
"0600": ["SQ", "SortingOperationsSequence"],
|
|
"0602": ["CS", "SortByCategory"],
|
|
"0604": ["CS", "SortingDirection"],
|
|
"0700": ["CS", "DisplaySetPatientOrientation"],
|
|
"0702": ["CS", "VOIType"],
|
|
"0704": ["CS", "PseudoColorType"],
|
|
"0705": ["SQ", "PseudoColorPaletteInstanceReferenceSequence"],
|
|
"0706": ["CS", "ShowGrayscaleInverted"],
|
|
"0710": ["CS", "ShowImageTrueSizeFlag"],
|
|
"0712": ["CS", "ShowGraphicAnnotationFlag"],
|
|
"0714": ["CS", "ShowPatientDemographicsFlag"],
|
|
"0716": ["CS", "ShowAcquisitionTechniquesFlag"],
|
|
"0717": ["CS", "DisplaySetHorizontalJustification"],
|
|
"0718": ["CS", "DisplaySetVerticalJustification"]
|
|
},
|
|
"0074": {
|
|
"0120": ["FD", "ContinuationStartMeterset"],
|
|
"0121": ["FD", "ContinuationEndMeterset"],
|
|
"1000": ["CS", "ProcedureStepState"],
|
|
"1002": ["SQ", "ProcedureStepProgressInformationSequence"],
|
|
"1004": ["DS", "ProcedureStepProgress"],
|
|
"1006": ["ST", "ProcedureStepProgressDescription"],
|
|
"1008": ["SQ", "ProcedureStepCommunicationsURISequence"],
|
|
"100A": ["ST", "ContactURI"],
|
|
"100C": ["LO", "ContactDisplayName"],
|
|
"100E": ["SQ", "ProcedureStepDiscontinuationReasonCodeSequence"],
|
|
"1020": ["SQ", "BeamTaskSequence"],
|
|
"1022": ["CS", "BeamTaskType"],
|
|
"1024": ["IS", "BeamOrderIndexTrial"],
|
|
"1026": ["FD", "TableTopVerticalAdjustedPosition"],
|
|
"1027": ["FD", "TableTopLongitudinalAdjustedPosition"],
|
|
"1028": ["FD", "TableTopLateralAdjustedPosition"],
|
|
"102A": ["FD", "PatientSupportAdjustedAngle"],
|
|
"102B": ["FD", "TableTopEccentricAdjustedAngle"],
|
|
"102C": ["FD", "TableTopPitchAdjustedAngle"],
|
|
"102D": ["FD", "TableTopRollAdjustedAngle"],
|
|
"1030": ["SQ", "DeliveryVerificationImageSequence"],
|
|
"1032": ["CS", "VerificationImageTiming"],
|
|
"1034": ["CS", "DoubleExposureFlag"],
|
|
"1036": ["CS", "DoubleExposureOrdering"],
|
|
"1038": ["DS", "DoubleExposureMetersetTrial"],
|
|
"103A": ["DS", "DoubleExposureFieldDeltaTrial"],
|
|
"1040": ["SQ", "RelatedReferenceRTImageSequence"],
|
|
"1042": ["SQ", "GeneralMachineVerificationSequence"],
|
|
"1044": ["SQ", "ConventionalMachineVerificationSequence"],
|
|
"1046": ["SQ", "IonMachineVerificationSequence"],
|
|
"1048": ["SQ", "FailedAttributesSequence"],
|
|
"104A": ["SQ", "OverriddenAttributesSequence"],
|
|
"104C": ["SQ", "ConventionalControlPointVerificationSequence"],
|
|
"104E": ["SQ", "IonControlPointVerificationSequence"],
|
|
"1050": ["SQ", "AttributeOccurrenceSequence"],
|
|
"1052": ["AT", "AttributeOccurrencePointer"],
|
|
"1054": ["UL", "AttributeItemSelector"],
|
|
"1056": ["LO", "AttributeOccurrencePrivateCreator"],
|
|
"1057": ["IS", "SelectorSequencePointerItems"],
|
|
"1200": ["CS", "ScheduledProcedureStepPriority"],
|
|
"1202": ["LO", "WorklistLabel"],
|
|
"1204": ["LO", "ProcedureStepLabel"],
|
|
"1210": ["SQ", "ScheduledProcessingParametersSequence"],
|
|
"1212": ["SQ", "PerformedProcessingParametersSequence"],
|
|
"1216": ["SQ", "UnifiedProcedureStepPerformedProcedureSequence"],
|
|
"1220": ["SQ", "RelatedProcedureStepSequence"],
|
|
"1222": ["LO", "ProcedureStepRelationshipType"],
|
|
"1224": ["SQ", "ReplacedProcedureStepSequence"],
|
|
"1230": ["LO", "DeletionLock"],
|
|
"1234": ["AE", "ReceivingAE"],
|
|
"1236": ["AE", "RequestingAE"],
|
|
"1238": ["LT", "ReasonForCancellation"],
|
|
"1242": ["CS", "SCPStatus"],
|
|
"1244": ["CS", "SubscriptionListStatus"],
|
|
"1246": ["CS", "UnifiedProcedureStepListStatus"],
|
|
"1324": ["UL", "BeamOrderIndex"],
|
|
"1338": ["FD", "DoubleExposureMeterset"],
|
|
"133A": ["FD", "DoubleExposureFieldDelta"]
|
|
},
|
|
"0076": {
|
|
"0001": ["LO", "ImplantAssemblyTemplateName"],
|
|
"0003": ["LO", "ImplantAssemblyTemplateIssuer"],
|
|
"0006": ["LO", "ImplantAssemblyTemplateVersion"],
|
|
"0008": ["SQ", "ReplacedImplantAssemblyTemplateSequence"],
|
|
"000A": ["CS", "ImplantAssemblyTemplateType"],
|
|
"000C": ["SQ", "OriginalImplantAssemblyTemplateSequence"],
|
|
"000E": ["SQ", "DerivationImplantAssemblyTemplateSequence"],
|
|
"0010": ["SQ", "ImplantAssemblyTemplateTargetAnatomySequence"],
|
|
"0020": ["SQ", "ProcedureTypeCodeSequence"],
|
|
"0030": ["LO", "SurgicalTechnique"],
|
|
"0032": ["SQ", "ComponentTypesSequence"],
|
|
"0034": ["CS", "ComponentTypeCodeSequence"],
|
|
"0036": ["CS", "ExclusiveComponentType"],
|
|
"0038": ["CS", "MandatoryComponentType"],
|
|
"0040": ["SQ", "ComponentSequence"],
|
|
"0055": ["US", "ComponentID"],
|
|
"0060": ["SQ", "ComponentAssemblySequence"],
|
|
"0070": ["US", "Component1ReferencedID"],
|
|
"0080": ["US", "Component1ReferencedMatingFeatureSetID"],
|
|
"0090": ["US", "Component1ReferencedMatingFeatureID"],
|
|
"00A0": ["US", "Component2ReferencedID"],
|
|
"00B0": ["US", "Component2ReferencedMatingFeatureSetID"],
|
|
"00C0": ["US", "Component2ReferencedMatingFeatureID"]
|
|
},
|
|
"0078": {
|
|
"0001": ["LO", "ImplantTemplateGroupName"],
|
|
"0010": ["ST", "ImplantTemplateGroupDescription"],
|
|
"0020": ["LO", "ImplantTemplateGroupIssuer"],
|
|
"0024": ["LO", "ImplantTemplateGroupVersion"],
|
|
"0026": ["SQ", "ReplacedImplantTemplateGroupSequence"],
|
|
"0028": ["SQ", "ImplantTemplateGroupTargetAnatomySequence"],
|
|
"002A": ["SQ", "ImplantTemplateGroupMembersSequence"],
|
|
"002E": ["US", "ImplantTemplateGroupMemberID"],
|
|
"0050": ["FD", "ThreeDImplantTemplateGroupMemberMatchingPoint"],
|
|
"0060": ["FD", "ThreeDImplantTemplateGroupMemberMatchingAxes"],
|
|
"0070": ["SQ", "ImplantTemplateGroupMemberMatching2DCoordinatesSequence"],
|
|
"0090": ["FD", "TwoDImplantTemplateGroupMemberMatchingPoint"],
|
|
"00A0": ["FD", "TwoDImplantTemplateGroupMemberMatchingAxes"],
|
|
"00B0": ["SQ", "ImplantTemplateGroupVariationDimensionSequence"],
|
|
"00B2": ["LO", "ImplantTemplateGroupVariationDimensionName"],
|
|
"00B4": ["SQ", "ImplantTemplateGroupVariationDimensionRankSequence"],
|
|
"00B6": ["US", "ReferencedImplantTemplateGroupMemberID"],
|
|
"00B8": ["US", "ImplantTemplateGroupVariationDimensionRank"]
|
|
},
|
|
"0088": {
|
|
"0130": ["SH", "StorageMediaFileSetID"],
|
|
"0140": ["UI", "StorageMediaFileSetUID"],
|
|
"0200": ["SQ", "IconImageSequence"],
|
|
"0904": ["LO", "TopicTitle"],
|
|
"0906": ["ST", "TopicSubject"],
|
|
"0910": ["LO", "TopicAuthor"],
|
|
"0912": ["LO", "TopicKeywords"]
|
|
},
|
|
"0100": {
|
|
"0410": ["CS", "SOPInstanceStatus"],
|
|
"0420": ["DT", "SOPAuthorizationDateTime"],
|
|
"0424": ["LT", "SOPAuthorizationComment"],
|
|
"0426": ["LO", "AuthorizationEquipmentCertificationNumber"]
|
|
},
|
|
"0400": {
|
|
"0005": ["US", "MACIDNumber"],
|
|
"0010": ["UI", "MACCalculationTransferSyntaxUID"],
|
|
"0015": ["CS", "MACAlgorithm"],
|
|
"0020": ["AT", "DataElementsSigned"],
|
|
"0100": ["UI", "DigitalSignatureUID"],
|
|
"0105": ["DT", "DigitalSignatureDateTime"],
|
|
"0110": ["CS", "CertificateType"],
|
|
"0115": ["OB", "CertificateOfSigner"],
|
|
"0120": ["OB", "Signature"],
|
|
"0305": ["CS", "CertifiedTimestampType"],
|
|
"0310": ["OB", "CertifiedTimestamp"],
|
|
"0401": ["SQ", "DigitalSignaturePurposeCodeSequence"],
|
|
"0402": ["SQ", "ReferencedDigitalSignatureSequence"],
|
|
"0403": ["SQ", "ReferencedSOPInstanceMACSequence"],
|
|
"0404": ["OB", "MAC"],
|
|
"0500": ["SQ", "EncryptedAttributesSequence"],
|
|
"0510": ["UI", "EncryptedContentTransferSyntaxUID"],
|
|
"0520": ["OB", "EncryptedContent"],
|
|
"0550": ["SQ", "ModifiedAttributesSequence"],
|
|
"0561": ["SQ", "OriginalAttributesSequence"],
|
|
"0562": ["DT", "AttributeModificationDateTime"],
|
|
"0563": ["LO", "ModifyingSystem"],
|
|
"0564": ["LO", "SourceOfPreviousValues"],
|
|
"0565": ["CS", "ReasonForTheAttributeModification"]
|
|
},
|
|
"2000": {
|
|
"0010": ["IS", "NumberOfCopies"],
|
|
"001E": ["SQ", "PrinterConfigurationSequence"],
|
|
"0020": ["CS", "PrintPriority"],
|
|
"0030": ["CS", "MediumType"],
|
|
"0040": ["CS", "FilmDestination"],
|
|
"0050": ["LO", "FilmSessionLabel"],
|
|
"0060": ["IS", "MemoryAllocation"],
|
|
"0061": ["IS", "MaximumMemoryAllocation"],
|
|
"0062": ["CS", "ColorImagePrintingFlag"],
|
|
"0063": ["CS", "CollationFlag"],
|
|
"0065": ["CS", "AnnotationFlag"],
|
|
"0067": ["CS", "ImageOverlayFlag"],
|
|
"0069": ["CS", "PresentationLUTFlag"],
|
|
"006A": ["CS", "ImageBoxPresentationLUTFlag"],
|
|
"00A0": ["US", "MemoryBitDepth"],
|
|
"00A1": ["US", "PrintingBitDepth"],
|
|
"00A2": ["SQ", "MediaInstalledSequence"],
|
|
"00A4": ["SQ", "OtherMediaAvailableSequence"],
|
|
"00A8": ["SQ", "SupportedImageDisplayFormatsSequence"],
|
|
"0500": ["SQ", "ReferencedFilmBoxSequence"],
|
|
"0510": ["SQ", "ReferencedStoredPrintSequence"]
|
|
},
|
|
"2010": {
|
|
"0010": ["ST", "ImageDisplayFormat"],
|
|
"0030": ["CS", "AnnotationDisplayFormatID"],
|
|
"0040": ["CS", "FilmOrientation"],
|
|
"0050": ["CS", "FilmSizeID"],
|
|
"0052": ["CS", "PrinterResolutionID"],
|
|
"0054": ["CS", "DefaultPrinterResolutionID"],
|
|
"0060": ["CS", "MagnificationType"],
|
|
"0080": ["CS", "SmoothingType"],
|
|
"00A6": ["CS", "DefaultMagnificationType"],
|
|
"00A7": ["CS", "OtherMagnificationTypesAvailable"],
|
|
"00A8": ["CS", "DefaultSmoothingType"],
|
|
"00A9": ["CS", "OtherSmoothingTypesAvailable"],
|
|
"0100": ["CS", "BorderDensity"],
|
|
"0110": ["CS", "EmptyImageDensity"],
|
|
"0120": ["US", "MinDensity"],
|
|
"0130": ["US", "MaxDensity"],
|
|
"0140": ["CS", "Trim"],
|
|
"0150": ["ST", "ConfigurationInformation"],
|
|
"0152": ["LT", "ConfigurationInformationDescription"],
|
|
"0154": ["IS", "MaximumCollatedFilms"],
|
|
"015E": ["US", "Illumination"],
|
|
"0160": ["US", "ReflectedAmbientLight"],
|
|
"0376": ["DS", "PrinterPixelSpacing"],
|
|
"0500": ["SQ", "ReferencedFilmSessionSequence"],
|
|
"0510": ["SQ", "ReferencedImageBoxSequence"],
|
|
"0520": ["SQ", "ReferencedBasicAnnotationBoxSequence"]
|
|
},
|
|
"2020": {
|
|
"0010": ["US", "ImageBoxPosition"],
|
|
"0020": ["CS", "Polarity"],
|
|
"0030": ["DS", "RequestedImageSize"],
|
|
"0040": ["CS", "RequestedDecimateCropBehavior"],
|
|
"0050": ["CS", "RequestedResolutionID"],
|
|
"00A0": ["CS", "RequestedImageSizeFlag"],
|
|
"00A2": ["CS", "DecimateCropResult"],
|
|
"0110": ["SQ", "BasicGrayscaleImageSequence"],
|
|
"0111": ["SQ", "BasicColorImageSequence"],
|
|
"0130": ["SQ", "ReferencedImageOverlayBoxSequence"],
|
|
"0140": ["SQ", "ReferencedVOILUTBoxSequence"]
|
|
},
|
|
"2030": {
|
|
"0010": ["US", "AnnotationPosition"],
|
|
"0020": ["LO", "TextString"]
|
|
},
|
|
"2040": {
|
|
"0010": ["SQ", "ReferencedOverlayPlaneSequence"],
|
|
"0011": ["US", "ReferencedOverlayPlaneGroups"],
|
|
"0020": ["SQ", "OverlayPixelDataSequence"],
|
|
"0060": ["CS", "OverlayMagnificationType"],
|
|
"0070": ["CS", "OverlaySmoothingType"],
|
|
"0072": ["CS", "OverlayOrImageMagnification"],
|
|
"0074": ["US", "MagnifyToNumberOfColumns"],
|
|
"0080": ["CS", "OverlayForegroundDensity"],
|
|
"0082": ["CS", "OverlayBackgroundDensity"],
|
|
"0090": ["CS", "OverlayMode"],
|
|
"0100": ["CS", "ThresholdDensity"],
|
|
"0500": ["SQ", "ReferencedImageBoxSequenceRetired"]
|
|
},
|
|
"2050": {
|
|
"0010": ["SQ", "PresentationLUTSequence"],
|
|
"0020": ["CS", "PresentationLUTShape"],
|
|
"0500": ["SQ", "ReferencedPresentationLUTSequence"]
|
|
},
|
|
"2100": {
|
|
"0010": ["SH", "PrintJobID"],
|
|
"0020": ["CS", "ExecutionStatus"],
|
|
"0030": ["CS", "ExecutionStatusInfo"],
|
|
"0040": ["DA", "CreationDate"],
|
|
"0050": ["TM", "CreationTime"],
|
|
"0070": ["AE", "Originator"],
|
|
"0140": ["AE", "DestinationAE"],
|
|
"0160": ["SH", "OwnerID"],
|
|
"0170": ["IS", "NumberOfFilms"],
|
|
"0500": ["SQ", "ReferencedPrintJobSequencePullStoredPrint"]
|
|
},
|
|
"2110": {
|
|
"0010": ["CS", "PrinterStatus"],
|
|
"0020": ["CS", "PrinterStatusInfo"],
|
|
"0030": ["LO", "PrinterName"],
|
|
"0099": ["SH", "PrintQueueID"]
|
|
},
|
|
"2120": {
|
|
"0010": ["CS", "QueueStatus"],
|
|
"0050": ["SQ", "PrintJobDescriptionSequence"],
|
|
"0070": ["SQ", "ReferencedPrintJobSequence"]
|
|
},
|
|
"2130": {
|
|
"0010": ["SQ", "PrintManagementCapabilitiesSequence"],
|
|
"0015": ["SQ", "PrinterCharacteristicsSequence"],
|
|
"0030": ["SQ", "FilmBoxContentSequence"],
|
|
"0040": ["SQ", "ImageBoxContentSequence"],
|
|
"0050": ["SQ", "AnnotationContentSequence"],
|
|
"0060": ["SQ", "ImageOverlayBoxContentSequence"],
|
|
"0080": ["SQ", "PresentationLUTContentSequence"],
|
|
"00A0": ["SQ", "ProposedStudySequence"],
|
|
"00C0": ["SQ", "OriginalImageSequence"]
|
|
},
|
|
"2200": {
|
|
"0001": ["CS", "LabelUsingInformationExtractedFromInstances"],
|
|
"0002": ["UT", "LabelText"],
|
|
"0003": ["CS", "LabelStyleSelection"],
|
|
"0004": ["LT", "MediaDisposition"],
|
|
"0005": ["LT", "BarcodeValue"],
|
|
"0006": ["CS", "BarcodeSymbology"],
|
|
"0007": ["CS", "AllowMediaSplitting"],
|
|
"0008": ["CS", "IncludeNonDICOMObjects"],
|
|
"0009": ["CS", "IncludeDisplayApplication"],
|
|
"000A": ["CS", "PreserveCompositeInstancesAfterMediaCreation"],
|
|
"000B": ["US", "TotalNumberOfPiecesOfMediaCreated"],
|
|
"000C": ["LO", "RequestedMediaApplicationProfile"],
|
|
"000D": ["SQ", "ReferencedStorageMediaSequence"],
|
|
"000E": ["AT", "FailureAttributes"],
|
|
"000F": ["CS", "AllowLossyCompression"],
|
|
"0020": ["CS", "RequestPriority"]
|
|
},
|
|
"3002": {
|
|
"0002": ["SH", "RTImageLabel"],
|
|
"0003": ["LO", "RTImageName"],
|
|
"0004": ["ST", "RTImageDescription"],
|
|
"000A": ["CS", "ReportedValuesOrigin"],
|
|
"000C": ["CS", "RTImagePlane"],
|
|
"000D": ["DS", "XRayImageReceptorTranslation"],
|
|
"000E": ["DS", "XRayImageReceptorAngle"],
|
|
"0010": ["DS", "RTImageOrientation"],
|
|
"0011": ["DS", "ImagePlanePixelSpacing"],
|
|
"0012": ["DS", "RTImagePosition"],
|
|
"0020": ["SH", "RadiationMachineName"],
|
|
"0022": ["DS", "RadiationMachineSAD"],
|
|
"0024": ["DS", "RadiationMachineSSD"],
|
|
"0026": ["DS", "RTImageSID"],
|
|
"0028": ["DS", "SourceToReferenceObjectDistance"],
|
|
"0029": ["IS", "FractionNumber"],
|
|
"0030": ["SQ", "ExposureSequence"],
|
|
"0032": ["DS", "MetersetExposure"],
|
|
"0034": ["DS", "DiaphragmPosition"],
|
|
"0040": ["SQ", "FluenceMapSequence"],
|
|
"0041": ["CS", "FluenceDataSource"],
|
|
"0042": ["DS", "FluenceDataScale"],
|
|
"0050": ["SQ", "PrimaryFluenceModeSequence"],
|
|
"0051": ["CS", "FluenceMode"],
|
|
"0052": ["SH", "FluenceModeID"]
|
|
},
|
|
"3004": {
|
|
"0001": ["CS", "DVHType"],
|
|
"0002": ["CS", "DoseUnits"],
|
|
"0004": ["CS", "DoseType"],
|
|
"0006": ["LO", "DoseComment"],
|
|
"0008": ["DS", "NormalizationPoint"],
|
|
"000A": ["CS", "DoseSummationType"],
|
|
"000C": ["DS", "GridFrameOffsetVector"],
|
|
"000E": ["DS", "DoseGridScaling"],
|
|
"0010": ["SQ", "RTDoseROISequence"],
|
|
"0012": ["DS", "DoseValue"],
|
|
"0014": ["CS", "TissueHeterogeneityCorrection"],
|
|
"0040": ["DS", "DVHNormalizationPoint"],
|
|
"0042": ["DS", "DVHNormalizationDoseValue"],
|
|
"0050": ["SQ", "DVHSequence"],
|
|
"0052": ["DS", "DVHDoseScaling"],
|
|
"0054": ["CS", "DVHVolumeUnits"],
|
|
"0056": ["IS", "DVHNumberOfBins"],
|
|
"0058": ["DS", "DVHData"],
|
|
"0060": ["SQ", "DVHReferencedROISequence"],
|
|
"0062": ["CS", "DVHROIContributionType"],
|
|
"0070": ["DS", "DVHMinimumDose"],
|
|
"0072": ["DS", "DVHMaximumDose"],
|
|
"0074": ["DS", "DVHMeanDose"]
|
|
},
|
|
"3006": {
|
|
"0002": ["SH", "StructureSetLabel"],
|
|
"0004": ["LO", "StructureSetName"],
|
|
"0006": ["ST", "StructureSetDescription"],
|
|
"0008": ["DA", "StructureSetDate"],
|
|
"0009": ["TM", "StructureSetTime"],
|
|
"0010": ["SQ", "ReferencedFrameOfReferenceSequence"],
|
|
"0012": ["SQ", "RTReferencedStudySequence"],
|
|
"0014": ["SQ", "RTReferencedSeriesSequence"],
|
|
"0016": ["SQ", "ContourImageSequence"],
|
|
"0020": ["SQ", "StructureSetROISequence"],
|
|
"0022": ["IS", "ROINumber"],
|
|
"0024": ["UI", "ReferencedFrameOfReferenceUID"],
|
|
"0026": ["LO", "ROIName"],
|
|
"0028": ["ST", "ROIDescription"],
|
|
"002A": ["IS", "ROIDisplayColor"],
|
|
"002C": ["DS", "ROIVolume"],
|
|
"0030": ["SQ", "RTRelatedROISequence"],
|
|
"0033": ["CS", "RTROIRelationship"],
|
|
"0036": ["CS", "ROIGenerationAlgorithm"],
|
|
"0038": ["LO", "ROIGenerationDescription"],
|
|
"0039": ["SQ", "ROIContourSequence"],
|
|
"0040": ["SQ", "ContourSequence"],
|
|
"0042": ["CS", "ContourGeometricType"],
|
|
"0044": ["DS", "ContourSlabThickness"],
|
|
"0045": ["DS", "ContourOffsetVector"],
|
|
"0046": ["IS", "NumberOfContourPoints"],
|
|
"0048": ["IS", "ContourNumber"],
|
|
"0049": ["IS", "AttachedContours"],
|
|
"0050": ["DS", "ContourData"],
|
|
"0080": ["SQ", "RTROIObservationsSequence"],
|
|
"0082": ["IS", "ObservationNumber"],
|
|
"0084": ["IS", "ReferencedROINumber"],
|
|
"0085": ["SH", "ROIObservationLabel"],
|
|
"0086": ["SQ", "RTROIIdentificationCodeSequence"],
|
|
"0088": ["ST", "ROIObservationDescription"],
|
|
"00A0": ["SQ", "RelatedRTROIObservationsSequence"],
|
|
"00A4": ["CS", "RTROIInterpretedType"],
|
|
"00A6": ["PN", "ROIInterpreter"],
|
|
"00B0": ["SQ", "ROIPhysicalPropertiesSequence"],
|
|
"00B2": ["CS", "ROIPhysicalProperty"],
|
|
"00B4": ["DS", "ROIPhysicalPropertyValue"],
|
|
"00B6": ["SQ", "ROIElementalCompositionSequence"],
|
|
"00B7": ["US", "ROIElementalCompositionAtomicNumber"],
|
|
"00B8": ["FL", "ROIElementalCompositionAtomicMassFraction"],
|
|
"00C0": ["SQ", "FrameOfReferenceRelationshipSequence"],
|
|
"00C2": ["UI", "RelatedFrameOfReferenceUID"],
|
|
"00C4": ["CS", "FrameOfReferenceTransformationType"],
|
|
"00C6": ["DS", "FrameOfReferenceTransformationMatrix"],
|
|
"00C8": ["LO", "FrameOfReferenceTransformationComment"]
|
|
},
|
|
"3008": {
|
|
"0010": ["SQ", "MeasuredDoseReferenceSequence"],
|
|
"0012": ["ST", "MeasuredDoseDescription"],
|
|
"0014": ["CS", "MeasuredDoseType"],
|
|
"0016": ["DS", "MeasuredDoseValue"],
|
|
"0020": ["SQ", "TreatmentSessionBeamSequence"],
|
|
"0021": ["SQ", "TreatmentSessionIonBeamSequence"],
|
|
"0022": ["IS", "CurrentFractionNumber"],
|
|
"0024": ["DA", "TreatmentControlPointDate"],
|
|
"0025": ["TM", "TreatmentControlPointTime"],
|
|
"002A": ["CS", "TreatmentTerminationStatus"],
|
|
"002B": ["SH", "TreatmentTerminationCode"],
|
|
"002C": ["CS", "TreatmentVerificationStatus"],
|
|
"0030": ["SQ", "ReferencedTreatmentRecordSequence"],
|
|
"0032": ["DS", "SpecifiedPrimaryMeterset"],
|
|
"0033": ["DS", "SpecifiedSecondaryMeterset"],
|
|
"0036": ["DS", "DeliveredPrimaryMeterset"],
|
|
"0037": ["DS", "DeliveredSecondaryMeterset"],
|
|
"003A": ["DS", "SpecifiedTreatmentTime"],
|
|
"003B": ["DS", "DeliveredTreatmentTime"],
|
|
"0040": ["SQ", "ControlPointDeliverySequence"],
|
|
"0041": ["SQ", "IonControlPointDeliverySequence"],
|
|
"0042": ["DS", "SpecifiedMeterset"],
|
|
"0044": ["DS", "DeliveredMeterset"],
|
|
"0045": ["FL", "MetersetRateSet"],
|
|
"0046": ["FL", "MetersetRateDelivered"],
|
|
"0047": ["FL", "ScanSpotMetersetsDelivered"],
|
|
"0048": ["DS", "DoseRateDelivered"],
|
|
"0050": ["SQ", "TreatmentSummaryCalculatedDoseReferenceSequence"],
|
|
"0052": ["DS", "CumulativeDoseToDoseReference"],
|
|
"0054": ["DA", "FirstTreatmentDate"],
|
|
"0056": ["DA", "MostRecentTreatmentDate"],
|
|
"005A": ["IS", "NumberOfFractionsDelivered"],
|
|
"0060": ["SQ", "OverrideSequence"],
|
|
"0061": ["AT", "ParameterSequencePointer"],
|
|
"0062": ["AT", "OverrideParameterPointer"],
|
|
"0063": ["IS", "ParameterItemIndex"],
|
|
"0064": ["IS", "MeasuredDoseReferenceNumber"],
|
|
"0065": ["AT", "ParameterPointer"],
|
|
"0066": ["ST", "OverrideReason"],
|
|
"0068": ["SQ", "CorrectedParameterSequence"],
|
|
"006A": ["FL", "CorrectionValue"],
|
|
"0070": ["SQ", "CalculatedDoseReferenceSequence"],
|
|
"0072": ["IS", "CalculatedDoseReferenceNumber"],
|
|
"0074": ["ST", "CalculatedDoseReferenceDescription"],
|
|
"0076": ["DS", "CalculatedDoseReferenceDoseValue"],
|
|
"0078": ["DS", "StartMeterset"],
|
|
"007A": ["DS", "EndMeterset"],
|
|
"0080": ["SQ", "ReferencedMeasuredDoseReferenceSequence"],
|
|
"0082": ["IS", "ReferencedMeasuredDoseReferenceNumber"],
|
|
"0090": ["SQ", "ReferencedCalculatedDoseReferenceSequence"],
|
|
"0092": ["IS", "ReferencedCalculatedDoseReferenceNumber"],
|
|
"00A0": ["SQ", "BeamLimitingDeviceLeafPairsSequence"],
|
|
"00B0": ["SQ", "RecordedWedgeSequence"],
|
|
"00C0": ["SQ", "RecordedCompensatorSequence"],
|
|
"00D0": ["SQ", "RecordedBlockSequence"],
|
|
"00E0": ["SQ", "TreatmentSummaryMeasuredDoseReferenceSequence"],
|
|
"00F0": ["SQ", "RecordedSnoutSequence"],
|
|
"00F2": ["SQ", "RecordedRangeShifterSequence"],
|
|
"00F4": ["SQ", "RecordedLateralSpreadingDeviceSequence"],
|
|
"00F6": ["SQ", "RecordedRangeModulatorSequence"],
|
|
"0100": ["SQ", "RecordedSourceSequence"],
|
|
"0105": ["LO", "SourceSerialNumber"],
|
|
"0110": ["SQ", "TreatmentSessionApplicationSetupSequence"],
|
|
"0116": ["CS", "ApplicationSetupCheck"],
|
|
"0120": ["SQ", "RecordedBrachyAccessoryDeviceSequence"],
|
|
"0122": ["IS", "ReferencedBrachyAccessoryDeviceNumber"],
|
|
"0130": ["SQ", "RecordedChannelSequence"],
|
|
"0132": ["DS", "SpecifiedChannelTotalTime"],
|
|
"0134": ["DS", "DeliveredChannelTotalTime"],
|
|
"0136": ["IS", "SpecifiedNumberOfPulses"],
|
|
"0138": ["IS", "DeliveredNumberOfPulses"],
|
|
"013A": ["DS", "SpecifiedPulseRepetitionInterval"],
|
|
"013C": ["DS", "DeliveredPulseRepetitionInterval"],
|
|
"0140": ["SQ", "RecordedSourceApplicatorSequence"],
|
|
"0142": ["IS", "ReferencedSourceApplicatorNumber"],
|
|
"0150": ["SQ", "RecordedChannelShieldSequence"],
|
|
"0152": ["IS", "ReferencedChannelShieldNumber"],
|
|
"0160": ["SQ", "BrachyControlPointDeliveredSequence"],
|
|
"0162": ["DA", "SafePositionExitDate"],
|
|
"0164": ["TM", "SafePositionExitTime"],
|
|
"0166": ["DA", "SafePositionReturnDate"],
|
|
"0168": ["TM", "SafePositionReturnTime"],
|
|
"0200": ["CS", "CurrentTreatmentStatus"],
|
|
"0202": ["ST", "TreatmentStatusComment"],
|
|
"0220": ["SQ", "FractionGroupSummarySequence"],
|
|
"0223": ["IS", "ReferencedFractionNumber"],
|
|
"0224": ["CS", "FractionGroupType"],
|
|
"0230": ["CS", "BeamStopperPosition"],
|
|
"0240": ["SQ", "FractionStatusSummarySequence"],
|
|
"0250": ["DA", "TreatmentDate"],
|
|
"0251": ["TM", "TreatmentTime"]
|
|
},
|
|
"300A": {
|
|
"0002": ["SH", "RTPlanLabel"],
|
|
"0003": ["LO", "RTPlanName"],
|
|
"0004": ["ST", "RTPlanDescription"],
|
|
"0006": ["DA", "RTPlanDate"],
|
|
"0007": ["TM", "RTPlanTime"],
|
|
"0009": ["LO", "TreatmentProtocols"],
|
|
"000A": ["CS", "PlanIntent"],
|
|
"000B": ["LO", "TreatmentSites"],
|
|
"000C": ["CS", "RTPlanGeometry"],
|
|
"000E": ["ST", "PrescriptionDescription"],
|
|
"0010": ["SQ", "DoseReferenceSequence"],
|
|
"0012": ["IS", "DoseReferenceNumber"],
|
|
"0013": ["UI", "DoseReferenceUID"],
|
|
"0014": ["CS", "DoseReferenceStructureType"],
|
|
"0015": ["CS", "NominalBeamEnergyUnit"],
|
|
"0016": ["LO", "DoseReferenceDescription"],
|
|
"0018": ["DS", "DoseReferencePointCoordinates"],
|
|
"001A": ["DS", "NominalPriorDose"],
|
|
"0020": ["CS", "DoseReferenceType"],
|
|
"0021": ["DS", "ConstraintWeight"],
|
|
"0022": ["DS", "DeliveryWarningDose"],
|
|
"0023": ["DS", "DeliveryMaximumDose"],
|
|
"0025": ["DS", "TargetMinimumDose"],
|
|
"0026": ["DS", "TargetPrescriptionDose"],
|
|
"0027": ["DS", "TargetMaximumDose"],
|
|
"0028": ["DS", "TargetUnderdoseVolumeFraction"],
|
|
"002A": ["DS", "OrganAtRiskFullVolumeDose"],
|
|
"002B": ["DS", "OrganAtRiskLimitDose"],
|
|
"002C": ["DS", "OrganAtRiskMaximumDose"],
|
|
"002D": ["DS", "OrganAtRiskOverdoseVolumeFraction"],
|
|
"0040": ["SQ", "ToleranceTableSequence"],
|
|
"0042": ["IS", "ToleranceTableNumber"],
|
|
"0043": ["SH", "ToleranceTableLabel"],
|
|
"0044": ["DS", "GantryAngleTolerance"],
|
|
"0046": ["DS", "BeamLimitingDeviceAngleTolerance"],
|
|
"0048": ["SQ", "BeamLimitingDeviceToleranceSequence"],
|
|
"004A": ["DS", "BeamLimitingDevicePositionTolerance"],
|
|
"004B": ["FL", "SnoutPositionTolerance"],
|
|
"004C": ["DS", "PatientSupportAngleTolerance"],
|
|
"004E": ["DS", "TableTopEccentricAngleTolerance"],
|
|
"004F": ["FL", "TableTopPitchAngleTolerance"],
|
|
"0050": ["FL", "TableTopRollAngleTolerance"],
|
|
"0051": ["DS", "TableTopVerticalPositionTolerance"],
|
|
"0052": ["DS", "TableTopLongitudinalPositionTolerance"],
|
|
"0053": ["DS", "TableTopLateralPositionTolerance"],
|
|
"0055": ["CS", "RTPlanRelationship"],
|
|
"0070": ["SQ", "FractionGroupSequence"],
|
|
"0071": ["IS", "FractionGroupNumber"],
|
|
"0072": ["LO", "FractionGroupDescription"],
|
|
"0078": ["IS", "NumberOfFractionsPlanned"],
|
|
"0079": ["IS", "NumberOfFractionPatternDigitsPerDay"],
|
|
"007A": ["IS", "RepeatFractionCycleLength"],
|
|
"007B": ["LT", "FractionPattern"],
|
|
"0080": ["IS", "NumberOfBeams"],
|
|
"0082": ["DS", "BeamDoseSpecificationPoint"],
|
|
"0084": ["DS", "BeamDose"],
|
|
"0086": ["DS", "BeamMeterset"],
|
|
"0088": ["FL", "BeamDosePointDepth"],
|
|
"0089": ["FL", "BeamDosePointEquivalentDepth"],
|
|
"008A": ["FL", "BeamDosePointSSD"],
|
|
"00A0": ["IS", "NumberOfBrachyApplicationSetups"],
|
|
"00A2": ["DS", "BrachyApplicationSetupDoseSpecificationPoint"],
|
|
"00A4": ["DS", "BrachyApplicationSetupDose"],
|
|
"00B0": ["SQ", "BeamSequence"],
|
|
"00B2": ["SH", "TreatmentMachineName"],
|
|
"00B3": ["CS", "PrimaryDosimeterUnit"],
|
|
"00B4": ["DS", "SourceAxisDistance"],
|
|
"00B6": ["SQ", "BeamLimitingDeviceSequence"],
|
|
"00B8": ["CS", "RTBeamLimitingDeviceType"],
|
|
"00BA": ["DS", "SourceToBeamLimitingDeviceDistance"],
|
|
"00BB": ["FL", "IsocenterToBeamLimitingDeviceDistance"],
|
|
"00BC": ["IS", "NumberOfLeafJawPairs"],
|
|
"00BE": ["DS", "LeafPositionBoundaries"],
|
|
"00C0": ["IS", "BeamNumber"],
|
|
"00C2": ["LO", "BeamName"],
|
|
"00C3": ["ST", "BeamDescription"],
|
|
"00C4": ["CS", "BeamType"],
|
|
"00C6": ["CS", "RadiationType"],
|
|
"00C7": ["CS", "HighDoseTechniqueType"],
|
|
"00C8": ["IS", "ReferenceImageNumber"],
|
|
"00CA": ["SQ", "PlannedVerificationImageSequence"],
|
|
"00CC": ["LO", "ImagingDeviceSpecificAcquisitionParameters"],
|
|
"00CE": ["CS", "TreatmentDeliveryType"],
|
|
"00D0": ["IS", "NumberOfWedges"],
|
|
"00D1": ["SQ", "WedgeSequence"],
|
|
"00D2": ["IS", "WedgeNumber"],
|
|
"00D3": ["CS", "WedgeType"],
|
|
"00D4": ["SH", "WedgeID"],
|
|
"00D5": ["IS", "WedgeAngle"],
|
|
"00D6": ["DS", "WedgeFactor"],
|
|
"00D7": ["FL", "TotalWedgeTrayWaterEquivalentThickness"],
|
|
"00D8": ["DS", "WedgeOrientation"],
|
|
"00D9": ["FL", "IsocenterToWedgeTrayDistance"],
|
|
"00DA": ["DS", "SourceToWedgeTrayDistance"],
|
|
"00DB": ["FL", "WedgeThinEdgePosition"],
|
|
"00DC": ["SH", "BolusID"],
|
|
"00DD": ["ST", "BolusDescription"],
|
|
"00E0": ["IS", "NumberOfCompensators"],
|
|
"00E1": ["SH", "MaterialID"],
|
|
"00E2": ["DS", "TotalCompensatorTrayFactor"],
|
|
"00E3": ["SQ", "CompensatorSequence"],
|
|
"00E4": ["IS", "CompensatorNumber"],
|
|
"00E5": ["SH", "CompensatorID"],
|
|
"00E6": ["DS", "SourceToCompensatorTrayDistance"],
|
|
"00E7": ["IS", "CompensatorRows"],
|
|
"00E8": ["IS", "CompensatorColumns"],
|
|
"00E9": ["DS", "CompensatorPixelSpacing"],
|
|
"00EA": ["DS", "CompensatorPosition"],
|
|
"00EB": ["DS", "CompensatorTransmissionData"],
|
|
"00EC": ["DS", "CompensatorThicknessData"],
|
|
"00ED": ["IS", "NumberOfBoli"],
|
|
"00EE": ["CS", "CompensatorType"],
|
|
"00F0": ["IS", "NumberOfBlocks"],
|
|
"00F2": ["DS", "TotalBlockTrayFactor"],
|
|
"00F3": ["FL", "TotalBlockTrayWaterEquivalentThickness"],
|
|
"00F4": ["SQ", "BlockSequence"],
|
|
"00F5": ["SH", "BlockTrayID"],
|
|
"00F6": ["DS", "SourceToBlockTrayDistance"],
|
|
"00F7": ["FL", "IsocenterToBlockTrayDistance"],
|
|
"00F8": ["CS", "BlockType"],
|
|
"00F9": ["LO", "AccessoryCode"],
|
|
"00FA": ["CS", "BlockDivergence"],
|
|
"00FB": ["CS", "BlockMountingPosition"],
|
|
"00FC": ["IS", "BlockNumber"],
|
|
"00FE": ["LO", "BlockName"],
|
|
"0100": ["DS", "BlockThickness"],
|
|
"0102": ["DS", "BlockTransmission"],
|
|
"0104": ["IS", "BlockNumberOfPoints"],
|
|
"0106": ["DS", "BlockData"],
|
|
"0107": ["SQ", "ApplicatorSequence"],
|
|
"0108": ["SH", "ApplicatorID"],
|
|
"0109": ["CS", "ApplicatorType"],
|
|
"010A": ["LO", "ApplicatorDescription"],
|
|
"010C": ["DS", "CumulativeDoseReferenceCoefficient"],
|
|
"010E": ["DS", "FinalCumulativeMetersetWeight"],
|
|
"0110": ["IS", "NumberOfControlPoints"],
|
|
"0111": ["SQ", "ControlPointSequence"],
|
|
"0112": ["IS", "ControlPointIndex"],
|
|
"0114": ["DS", "NominalBeamEnergy"],
|
|
"0115": ["DS", "DoseRateSet"],
|
|
"0116": ["SQ", "WedgePositionSequence"],
|
|
"0118": ["CS", "WedgePosition"],
|
|
"011A": ["SQ", "BeamLimitingDevicePositionSequence"],
|
|
"011C": ["DS", "LeafJawPositions"],
|
|
"011E": ["DS", "GantryAngle"],
|
|
"011F": ["CS", "GantryRotationDirection"],
|
|
"0120": ["DS", "BeamLimitingDeviceAngle"],
|
|
"0121": ["CS", "BeamLimitingDeviceRotationDirection"],
|
|
"0122": ["DS", "PatientSupportAngle"],
|
|
"0123": ["CS", "PatientSupportRotationDirection"],
|
|
"0124": ["DS", "TableTopEccentricAxisDistance"],
|
|
"0125": ["DS", "TableTopEccentricAngle"],
|
|
"0126": ["CS", "TableTopEccentricRotationDirection"],
|
|
"0128": ["DS", "TableTopVerticalPosition"],
|
|
"0129": ["DS", "TableTopLongitudinalPosition"],
|
|
"012A": ["DS", "TableTopLateralPosition"],
|
|
"012C": ["DS", "IsocenterPosition"],
|
|
"012E": ["DS", "SurfaceEntryPoint"],
|
|
"0130": ["DS", "SourceToSurfaceDistance"],
|
|
"0134": ["DS", "CumulativeMetersetWeight"],
|
|
"0140": ["FL", "TableTopPitchAngle"],
|
|
"0142": ["CS", "TableTopPitchRotationDirection"],
|
|
"0144": ["FL", "TableTopRollAngle"],
|
|
"0146": ["CS", "TableTopRollRotationDirection"],
|
|
"0148": ["FL", "HeadFixationAngle"],
|
|
"014A": ["FL", "GantryPitchAngle"],
|
|
"014C": ["CS", "GantryPitchRotationDirection"],
|
|
"014E": ["FL", "GantryPitchAngleTolerance"],
|
|
"0180": ["SQ", "PatientSetupSequence"],
|
|
"0182": ["IS", "PatientSetupNumber"],
|
|
"0183": ["LO", "PatientSetupLabel"],
|
|
"0184": ["LO", "PatientAdditionalPosition"],
|
|
"0190": ["SQ", "FixationDeviceSequence"],
|
|
"0192": ["CS", "FixationDeviceType"],
|
|
"0194": ["SH", "FixationDeviceLabel"],
|
|
"0196": ["ST", "FixationDeviceDescription"],
|
|
"0198": ["SH", "FixationDevicePosition"],
|
|
"0199": ["FL", "FixationDevicePitchAngle"],
|
|
"019A": ["FL", "FixationDeviceRollAngle"],
|
|
"01A0": ["SQ", "ShieldingDeviceSequence"],
|
|
"01A2": ["CS", "ShieldingDeviceType"],
|
|
"01A4": ["SH", "ShieldingDeviceLabel"],
|
|
"01A6": ["ST", "ShieldingDeviceDescription"],
|
|
"01A8": ["SH", "ShieldingDevicePosition"],
|
|
"01B0": ["CS", "SetupTechnique"],
|
|
"01B2": ["ST", "SetupTechniqueDescription"],
|
|
"01B4": ["SQ", "SetupDeviceSequence"],
|
|
"01B6": ["CS", "SetupDeviceType"],
|
|
"01B8": ["SH", "SetupDeviceLabel"],
|
|
"01BA": ["ST", "SetupDeviceDescription"],
|
|
"01BC": ["DS", "SetupDeviceParameter"],
|
|
"01D0": ["ST", "SetupReferenceDescription"],
|
|
"01D2": ["DS", "TableTopVerticalSetupDisplacement"],
|
|
"01D4": ["DS", "TableTopLongitudinalSetupDisplacement"],
|
|
"01D6": ["DS", "TableTopLateralSetupDisplacement"],
|
|
"0200": ["CS", "BrachyTreatmentTechnique"],
|
|
"0202": ["CS", "BrachyTreatmentType"],
|
|
"0206": ["SQ", "TreatmentMachineSequence"],
|
|
"0210": ["SQ", "SourceSequence"],
|
|
"0212": ["IS", "SourceNumber"],
|
|
"0214": ["CS", "SourceType"],
|
|
"0216": ["LO", "SourceManufacturer"],
|
|
"0218": ["DS", "ActiveSourceDiameter"],
|
|
"021A": ["DS", "ActiveSourceLength"],
|
|
"0222": ["DS", "SourceEncapsulationNominalThickness"],
|
|
"0224": ["DS", "SourceEncapsulationNominalTransmission"],
|
|
"0226": ["LO", "SourceIsotopeName"],
|
|
"0228": ["DS", "SourceIsotopeHalfLife"],
|
|
"0229": ["CS", "SourceStrengthUnits"],
|
|
"022A": ["DS", "ReferenceAirKermaRate"],
|
|
"022B": ["DS", "SourceStrength"],
|
|
"022C": ["DA", "SourceStrengthReferenceDate"],
|
|
"022E": ["TM", "SourceStrengthReferenceTime"],
|
|
"0230": ["SQ", "ApplicationSetupSequence"],
|
|
"0232": ["CS", "ApplicationSetupType"],
|
|
"0234": ["IS", "ApplicationSetupNumber"],
|
|
"0236": ["LO", "ApplicationSetupName"],
|
|
"0238": ["LO", "ApplicationSetupManufacturer"],
|
|
"0240": ["IS", "TemplateNumber"],
|
|
"0242": ["SH", "TemplateType"],
|
|
"0244": ["LO", "TemplateName"],
|
|
"0250": ["DS", "TotalReferenceAirKerma"],
|
|
"0260": ["SQ", "BrachyAccessoryDeviceSequence"],
|
|
"0262": ["IS", "BrachyAccessoryDeviceNumber"],
|
|
"0263": ["SH", "BrachyAccessoryDeviceID"],
|
|
"0264": ["CS", "BrachyAccessoryDeviceType"],
|
|
"0266": ["LO", "BrachyAccessoryDeviceName"],
|
|
"026A": ["DS", "BrachyAccessoryDeviceNominalThickness"],
|
|
"026C": ["DS", "BrachyAccessoryDeviceNominalTransmission"],
|
|
"0280": ["SQ", "ChannelSequence"],
|
|
"0282": ["IS", "ChannelNumber"],
|
|
"0284": ["DS", "ChannelLength"],
|
|
"0286": ["DS", "ChannelTotalTime"],
|
|
"0288": ["CS", "SourceMovementType"],
|
|
"028A": ["IS", "NumberOfPulses"],
|
|
"028C": ["DS", "PulseRepetitionInterval"],
|
|
"0290": ["IS", "SourceApplicatorNumber"],
|
|
"0291": ["SH", "SourceApplicatorID"],
|
|
"0292": ["CS", "SourceApplicatorType"],
|
|
"0294": ["LO", "SourceApplicatorName"],
|
|
"0296": ["DS", "SourceApplicatorLength"],
|
|
"0298": ["LO", "SourceApplicatorManufacturer"],
|
|
"029C": ["DS", "SourceApplicatorWallNominalThickness"],
|
|
"029E": ["DS", "SourceApplicatorWallNominalTransmission"],
|
|
"02A0": ["DS", "SourceApplicatorStepSize"],
|
|
"02A2": ["IS", "TransferTubeNumber"],
|
|
"02A4": ["DS", "TransferTubeLength"],
|
|
"02B0": ["SQ", "ChannelShieldSequence"],
|
|
"02B2": ["IS", "ChannelShieldNumber"],
|
|
"02B3": ["SH", "ChannelShieldID"],
|
|
"02B4": ["LO", "ChannelShieldName"],
|
|
"02B8": ["DS", "ChannelShieldNominalThickness"],
|
|
"02BA": ["DS", "ChannelShieldNominalTransmission"],
|
|
"02C8": ["DS", "FinalCumulativeTimeWeight"],
|
|
"02D0": ["SQ", "BrachyControlPointSequence"],
|
|
"02D2": ["DS", "ControlPointRelativePosition"],
|
|
"02D4": ["DS", "ControlPoint3DPosition"],
|
|
"02D6": ["DS", "CumulativeTimeWeight"],
|
|
"02E0": ["CS", "CompensatorDivergence"],
|
|
"02E1": ["CS", "CompensatorMountingPosition"],
|
|
"02E2": ["DS", "SourceToCompensatorDistance"],
|
|
"02E3": ["FL", "TotalCompensatorTrayWaterEquivalentThickness"],
|
|
"02E4": ["FL", "IsocenterToCompensatorTrayDistance"],
|
|
"02E5": ["FL", "CompensatorColumnOffset"],
|
|
"02E6": ["FL", "IsocenterToCompensatorDistances"],
|
|
"02E7": ["FL", "CompensatorRelativeStoppingPowerRatio"],
|
|
"02E8": ["FL", "CompensatorMillingToolDiameter"],
|
|
"02EA": ["SQ", "IonRangeCompensatorSequence"],
|
|
"02EB": ["LT", "CompensatorDescription"],
|
|
"0302": ["IS", "RadiationMassNumber"],
|
|
"0304": ["IS", "RadiationAtomicNumber"],
|
|
"0306": ["SS", "RadiationChargeState"],
|
|
"0308": ["CS", "ScanMode"],
|
|
"030A": ["FL", "VirtualSourceAxisDistances"],
|
|
"030C": ["SQ", "SnoutSequence"],
|
|
"030D": ["FL", "SnoutPosition"],
|
|
"030F": ["SH", "SnoutID"],
|
|
"0312": ["IS", "NumberOfRangeShifters"],
|
|
"0314": ["SQ", "RangeShifterSequence"],
|
|
"0316": ["IS", "RangeShifterNumber"],
|
|
"0318": ["SH", "RangeShifterID"],
|
|
"0320": ["CS", "RangeShifterType"],
|
|
"0322": ["LO", "RangeShifterDescription"],
|
|
"0330": ["IS", "NumberOfLateralSpreadingDevices"],
|
|
"0332": ["SQ", "LateralSpreadingDeviceSequence"],
|
|
"0334": ["IS", "LateralSpreadingDeviceNumber"],
|
|
"0336": ["SH", "LateralSpreadingDeviceID"],
|
|
"0338": ["CS", "LateralSpreadingDeviceType"],
|
|
"033A": ["LO", "LateralSpreadingDeviceDescription"],
|
|
"033C": ["FL", "LateralSpreadingDeviceWaterEquivalentThickness"],
|
|
"0340": ["IS", "NumberOfRangeModulators"],
|
|
"0342": ["SQ", "RangeModulatorSequence"],
|
|
"0344": ["IS", "RangeModulatorNumber"],
|
|
"0346": ["SH", "RangeModulatorID"],
|
|
"0348": ["CS", "RangeModulatorType"],
|
|
"034A": ["LO", "RangeModulatorDescription"],
|
|
"034C": ["SH", "BeamCurrentModulationID"],
|
|
"0350": ["CS", "PatientSupportType"],
|
|
"0352": ["SH", "PatientSupportID"],
|
|
"0354": ["LO", "PatientSupportAccessoryCode"],
|
|
"0356": ["FL", "FixationLightAzimuthalAngle"],
|
|
"0358": ["FL", "FixationLightPolarAngle"],
|
|
"035A": ["FL", "MetersetRate"],
|
|
"0360": ["SQ", "RangeShifterSettingsSequence"],
|
|
"0362": ["LO", "RangeShifterSetting"],
|
|
"0364": ["FL", "IsocenterToRangeShifterDistance"],
|
|
"0366": ["FL", "RangeShifterWaterEquivalentThickness"],
|
|
"0370": ["SQ", "LateralSpreadingDeviceSettingsSequence"],
|
|
"0372": ["LO", "LateralSpreadingDeviceSetting"],
|
|
"0374": ["FL", "IsocenterToLateralSpreadingDeviceDistance"],
|
|
"0380": ["SQ", "RangeModulatorSettingsSequence"],
|
|
"0382": ["FL", "RangeModulatorGatingStartValue"],
|
|
"0384": ["FL", "RangeModulatorGatingStopValue"],
|
|
"0386": ["FL", "RangeModulatorGatingStartWaterEquivalentThickness"],
|
|
"0388": ["FL", "RangeModulatorGatingStopWaterEquivalentThickness"],
|
|
"038A": ["FL", "IsocenterToRangeModulatorDistance"],
|
|
"0390": ["SH", "ScanSpotTuneID"],
|
|
"0392": ["IS", "NumberOfScanSpotPositions"],
|
|
"0394": ["FL", "ScanSpotPositionMap"],
|
|
"0396": ["FL", "ScanSpotMetersetWeights"],
|
|
"0398": ["FL", "ScanningSpotSize"],
|
|
"039A": ["IS", "NumberOfPaintings"],
|
|
"03A0": ["SQ", "IonToleranceTableSequence"],
|
|
"03A2": ["SQ", "IonBeamSequence"],
|
|
"03A4": ["SQ", "IonBeamLimitingDeviceSequence"],
|
|
"03A6": ["SQ", "IonBlockSequence"],
|
|
"03A8": ["SQ", "IonControlPointSequence"],
|
|
"03AA": ["SQ", "IonWedgeSequence"],
|
|
"03AC": ["SQ", "IonWedgePositionSequence"],
|
|
"0401": ["SQ", "ReferencedSetupImageSequence"],
|
|
"0402": ["ST", "SetupImageComment"],
|
|
"0410": ["SQ", "MotionSynchronizationSequence"],
|
|
"0412": ["FL", "ControlPointOrientation"],
|
|
"0420": ["SQ", "GeneralAccessorySequence"],
|
|
"0421": ["SH", "GeneralAccessoryID"],
|
|
"0422": ["ST", "GeneralAccessoryDescription"],
|
|
"0423": ["CS", "GeneralAccessoryType"],
|
|
"0424": ["IS", "GeneralAccessoryNumber"],
|
|
"0431": ["SQ", "ApplicatorGeometrySequence"],
|
|
"0432": ["CS", "ApplicatorApertureShape"],
|
|
"0433": ["FL", "ApplicatorOpening"],
|
|
"0434": ["FL", "ApplicatorOpeningX"],
|
|
"0435": ["FL", "ApplicatorOpeningY"],
|
|
"0436": ["FL", "SourceToApplicatorMountingPositionDistance"]
|
|
},
|
|
"300C": {
|
|
"0002": ["SQ", "ReferencedRTPlanSequence"],
|
|
"0004": ["SQ", "ReferencedBeamSequence"],
|
|
"0006": ["IS", "ReferencedBeamNumber"],
|
|
"0007": ["IS", "ReferencedReferenceImageNumber"],
|
|
"0008": ["DS", "StartCumulativeMetersetWeight"],
|
|
"0009": ["DS", "EndCumulativeMetersetWeight"],
|
|
"000A": ["SQ", "ReferencedBrachyApplicationSetupSequence"],
|
|
"000C": ["IS", "ReferencedBrachyApplicationSetupNumber"],
|
|
"000E": ["IS", "ReferencedSourceNumber"],
|
|
"0020": ["SQ", "ReferencedFractionGroupSequence"],
|
|
"0022": ["IS", "ReferencedFractionGroupNumber"],
|
|
"0040": ["SQ", "ReferencedVerificationImageSequence"],
|
|
"0042": ["SQ", "ReferencedReferenceImageSequence"],
|
|
"0050": ["SQ", "ReferencedDoseReferenceSequence"],
|
|
"0051": ["IS", "ReferencedDoseReferenceNumber"],
|
|
"0055": ["SQ", "BrachyReferencedDoseReferenceSequence"],
|
|
"0060": ["SQ", "ReferencedStructureSetSequence"],
|
|
"006A": ["IS", "ReferencedPatientSetupNumber"],
|
|
"0080": ["SQ", "ReferencedDoseSequence"],
|
|
"00A0": ["IS", "ReferencedToleranceTableNumber"],
|
|
"00B0": ["SQ", "ReferencedBolusSequence"],
|
|
"00C0": ["IS", "ReferencedWedgeNumber"],
|
|
"00D0": ["IS", "ReferencedCompensatorNumber"],
|
|
"00E0": ["IS", "ReferencedBlockNumber"],
|
|
"00F0": ["IS", "ReferencedControlPointIndex"],
|
|
"00F2": ["SQ", "ReferencedControlPointSequence"],
|
|
"00F4": ["IS", "ReferencedStartControlPointIndex"],
|
|
"00F6": ["IS", "ReferencedStopControlPointIndex"],
|
|
"0100": ["IS", "ReferencedRangeShifterNumber"],
|
|
"0102": ["IS", "ReferencedLateralSpreadingDeviceNumber"],
|
|
"0104": ["IS", "ReferencedRangeModulatorNumber"]
|
|
},
|
|
"300E": {
|
|
"0002": ["CS", "ApprovalStatus"],
|
|
"0004": ["DA", "ReviewDate"],
|
|
"0005": ["TM", "ReviewTime"],
|
|
"0008": ["PN", "ReviewerName"]
|
|
},
|
|
"4000": {
|
|
"0010": ["LT", "Arbitrary"],
|
|
"4000": ["LT", "TextComments"]
|
|
},
|
|
"4008": {
|
|
"0040": ["SH", "ResultsID"],
|
|
"0042": ["LO", "ResultsIDIssuer"],
|
|
"0050": ["SQ", "ReferencedInterpretationSequence"],
|
|
"00FF": ["CS", "ReportProductionStatusTrial"],
|
|
"0100": ["DA", "InterpretationRecordedDate"],
|
|
"0101": ["TM", "InterpretationRecordedTime"],
|
|
"0102": ["PN", "InterpretationRecorder"],
|
|
"0103": ["LO", "ReferenceToRecordedSound"],
|
|
"0108": ["DA", "InterpretationTranscriptionDate"],
|
|
"0109": ["TM", "InterpretationTranscriptionTime"],
|
|
"010A": ["PN", "InterpretationTranscriber"],
|
|
"010B": ["ST", "InterpretationText"],
|
|
"010C": ["PN", "InterpretationAuthor"],
|
|
"0111": ["SQ", "InterpretationApproverSequence"],
|
|
"0112": ["DA", "InterpretationApprovalDate"],
|
|
"0113": ["TM", "InterpretationApprovalTime"],
|
|
"0114": ["PN", "PhysicianApprovingInterpretation"],
|
|
"0115": ["LT", "InterpretationDiagnosisDescription"],
|
|
"0117": ["SQ", "InterpretationDiagnosisCodeSequence"],
|
|
"0118": ["SQ", "ResultsDistributionListSequence"],
|
|
"0119": ["PN", "DistributionName"],
|
|
"011A": ["LO", "DistributionAddress"],
|
|
"0200": ["SH", "InterpretationID"],
|
|
"0202": ["LO", "InterpretationIDIssuer"],
|
|
"0210": ["CS", "InterpretationTypeID"],
|
|
"0212": ["CS", "InterpretationStatusID"],
|
|
"0300": ["ST", "Impressions"],
|
|
"4000": ["ST", "ResultsComments"]
|
|
},
|
|
"4010": {
|
|
"0001": ["CS", "LowEnergyDetectors"],
|
|
"0002": ["CS", "HighEnergyDetectors"],
|
|
"0004": ["SQ", "DetectorGeometrySequence"],
|
|
"1001": ["SQ", "ThreatROIVoxelSequence"],
|
|
"1004": ["FL", "ThreatROIBase"],
|
|
"1005": ["FL", "ThreatROIExtents"],
|
|
"1006": ["OB", "ThreatROIBitmap"],
|
|
"1007": ["SH", "RouteSegmentID"],
|
|
"1008": ["CS", "GantryType"],
|
|
"1009": ["CS", "OOIOwnerType"],
|
|
"100A": ["SQ", "RouteSegmentSequence"],
|
|
"1010": ["US", "PotentialThreatObjectID"],
|
|
"1011": ["SQ", "ThreatSequence"],
|
|
"1012": ["CS", "ThreatCategory"],
|
|
"1013": ["LT", "ThreatCategoryDescription"],
|
|
"1014": ["CS", "ATDAbilityAssessment"],
|
|
"1015": ["CS", "ATDAssessmentFlag"],
|
|
"1016": ["FL", "ATDAssessmentProbability"],
|
|
"1017": ["FL", "Mass"],
|
|
"1018": ["FL", "Density"],
|
|
"1019": ["FL", "ZEffective"],
|
|
"101A": ["SH", "BoardingPassID"],
|
|
"101B": ["FL", "CenterOfMass"],
|
|
"101C": ["FL", "CenterOfPTO"],
|
|
"101D": ["FL", "BoundingPolygon"],
|
|
"101E": ["SH", "RouteSegmentStartLocationID"],
|
|
"101F": ["SH", "RouteSegmentEndLocationID"],
|
|
"1020": ["CS", "RouteSegmentLocationIDType"],
|
|
"1021": ["CS", "AbortReason"],
|
|
"1023": ["FL", "VolumeOfPTO"],
|
|
"1024": ["CS", "AbortFlag"],
|
|
"1025": ["DT", "RouteSegmentStartTime"],
|
|
"1026": ["DT", "RouteSegmentEndTime"],
|
|
"1027": ["CS", "TDRType"],
|
|
"1028": ["CS", "InternationalRouteSegment"],
|
|
"1029": ["LO", "ThreatDetectionAlgorithmandVersion"],
|
|
"102A": ["SH", "AssignedLocation"],
|
|
"102B": ["DT", "AlarmDecisionTime"],
|
|
"1031": ["CS", "AlarmDecision"],
|
|
"1033": ["US", "NumberOfTotalObjects"],
|
|
"1034": ["US", "NumberOfAlarmObjects"],
|
|
"1037": ["SQ", "PTORepresentationSequence"],
|
|
"1038": ["SQ", "ATDAssessmentSequence"],
|
|
"1039": ["CS", "TIPType"],
|
|
"103A": ["CS", "DICOSVersion"],
|
|
"1041": ["DT", "OOIOwnerCreationTime"],
|
|
"1042": ["CS", "OOIType"],
|
|
"1043": ["FL", "OOISize"],
|
|
"1044": ["CS", "AcquisitionStatus"],
|
|
"1045": ["SQ", "BasisMaterialsCodeSequence"],
|
|
"1046": ["CS", "PhantomType"],
|
|
"1047": ["SQ", "OOIOwnerSequence"],
|
|
"1048": ["CS", "ScanType"],
|
|
"1051": ["LO", "ItineraryID"],
|
|
"1052": ["SH", "ItineraryIDType"],
|
|
"1053": ["LO", "ItineraryIDAssigningAuthority"],
|
|
"1054": ["SH", "RouteID"],
|
|
"1055": ["SH", "RouteIDAssigningAuthority"],
|
|
"1056": ["CS", "InboundArrivalType"],
|
|
"1058": ["SH", "CarrierID"],
|
|
"1059": ["CS", "CarrierIDAssigningAuthority"],
|
|
"1060": ["FL", "SourceOrientation"],
|
|
"1061": ["FL", "SourcePosition"],
|
|
"1062": ["FL", "BeltHeight"],
|
|
"1064": ["SQ", "AlgorithmRoutingCodeSequence"],
|
|
"1067": ["CS", "TransportClassification"],
|
|
"1068": ["LT", "OOITypeDescriptor"],
|
|
"1069": ["FL", "TotalProcessingTime"],
|
|
"106C": ["OB", "DetectorCalibrationData"]
|
|
}
|
|
};
|
|
/**
|
|
* Returns the VR for the specified group and element.
|
|
* @param {number} group
|
|
* @param {number} element
|
|
* @returns {string}
|
|
*/
|
|
|
|
const getVR = (group, element) => {
|
|
let vr;
|
|
let elementData;
|
|
let groupData;
|
|
groupData = dict[dec2hex(group)];
|
|
|
|
if (groupData) {
|
|
elementData = groupData[dec2hex(element)];
|
|
|
|
if (elementData) {
|
|
[vr] = elementData;
|
|
} else if (element === 0) {
|
|
vr = "UL";
|
|
}
|
|
}
|
|
|
|
if (!vr) {
|
|
groupData = dictPrivate[dec2hex(group)];
|
|
|
|
if (groupData) {
|
|
elementData = groupData[dec2hex(element)];
|
|
|
|
if (elementData) {
|
|
[vr] = elementData;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!vr) {
|
|
vr = "OB";
|
|
}
|
|
|
|
return vr;
|
|
};
|
|
/**
|
|
* Returns the description for the specified group and element.
|
|
* @param {number} group
|
|
* @param {number} element
|
|
* @returns {string}
|
|
*/
|
|
|
|
|
|
const getDescription = (group, element) => {
|
|
let des;
|
|
let elementData;
|
|
let groupData;
|
|
groupData = dict[dec2hex(group)];
|
|
|
|
if (groupData) {
|
|
elementData = groupData[dec2hex(element)];
|
|
|
|
if (elementData) {
|
|
[, des] = elementData;
|
|
} else if (element === 0) {
|
|
des = `Group ${dec2hex(group)} Length`;
|
|
}
|
|
}
|
|
|
|
if (!des) {
|
|
groupData = dictPrivate[dec2hex(group)];
|
|
|
|
if (groupData) {
|
|
elementData = groupData[dec2hex(element)];
|
|
|
|
if (elementData) {
|
|
[, des] = elementData;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!des) {
|
|
des = "PrivateData";
|
|
}
|
|
|
|
return des;
|
|
};
|
|
|
|
var Dictionary = {
|
|
getVR,
|
|
getDescription
|
|
};
|
|
const CSA2_MAGIC_NUMBER = [83, 86, 49, 48];
|
|
const NAME_LENGTH = 64;
|
|
const ELEMENT_CSA1 = 0x1010;
|
|
const ELEMENT_CSA2 = 0x1020;
|
|
const GROUP_CSA = 0x029;
|
|
/**
|
|
* The Siemens constructor.
|
|
* @params {ArrayBuffer} buffer
|
|
* @type {Function}
|
|
*/
|
|
|
|
class Siemens {
|
|
constructor(buffer) {
|
|
this.output = void 0;
|
|
this.data = void 0;
|
|
|
|
this.canRead = (group, element) => group === GROUP_CSA && (element === ELEMENT_CSA1 || element === ELEMENT_CSA2);
|
|
|
|
this.output = "";
|
|
this.data = new DataView(buffer, 0);
|
|
}
|
|
/**
|
|
* Reads the Siemens header. (See http://nipy.org/nibabel/dicom/siemens_csa.html)
|
|
* @returns {string}
|
|
*/
|
|
|
|
|
|
readHeader() {
|
|
let match;
|
|
|
|
try {
|
|
if (this.data.byteLength > CSA2_MAGIC_NUMBER.length) {
|
|
match = true;
|
|
const {
|
|
data
|
|
} = this;
|
|
|
|
for (let ctr = 0; ctr < CSA2_MAGIC_NUMBER.length; ctr += 1) {
|
|
match = match && data.getUint8(ctr) === CSA2_MAGIC_NUMBER[ctr];
|
|
}
|
|
|
|
if (match) {
|
|
this.readHeaderAtOffset(CSA2_MAGIC_NUMBER.length + 4);
|
|
} else {
|
|
this.readHeaderAtOffset(0);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.log(error);
|
|
}
|
|
|
|
return this.output;
|
|
}
|
|
|
|
readHeaderAtOffset(offset) {
|
|
this.output += "\n";
|
|
const numTags = swap32(this.data.getUint32(offset));
|
|
|
|
if (numTags < 1 || numTags > 128) {
|
|
return this.output;
|
|
}
|
|
|
|
let newOffset = offset + 4;
|
|
newOffset += 4; // unused
|
|
|
|
for (let ctr = 0; ctr < numTags; ctr += 1) {
|
|
newOffset = this.readTag(newOffset);
|
|
|
|
if (offset === -1) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return this.output;
|
|
}
|
|
|
|
readTag(offset) {
|
|
const name = this.readString(offset, NAME_LENGTH);
|
|
let newOffset = offset + NAME_LENGTH;
|
|
newOffset += 4; // vm
|
|
|
|
newOffset += 4;
|
|
newOffset += 4; // syngodt
|
|
|
|
const numItems = swap32(this.data.getUint32(offset));
|
|
newOffset += 4;
|
|
newOffset += 4; // unused
|
|
|
|
this.output += ` ${name}=`;
|
|
|
|
for (let ctr = 0; ctr < numItems; ctr += 1) {
|
|
newOffset = this.readItem(newOffset);
|
|
|
|
if (newOffset === -1) {
|
|
break;
|
|
} else if (newOffset % 4 !== 0) {
|
|
newOffset += 4 - newOffset % 4;
|
|
}
|
|
}
|
|
|
|
this.output += "\n";
|
|
return offset;
|
|
}
|
|
|
|
readString(offset, length) {
|
|
let str = "";
|
|
|
|
for (let ctr = 0; ctr < length; ctr += 1) {
|
|
const char2 = this.data.getUint8(offset + ctr);
|
|
|
|
if (char2 === 0) {
|
|
break;
|
|
}
|
|
|
|
str += String.fromCharCode(char2);
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
readItem(offset) {
|
|
const itemLength = swap32(this.data.getUint32(offset));
|
|
|
|
if (offset + itemLength > this.data.byteLength) {
|
|
return -1;
|
|
}
|
|
|
|
const newOffset = offset + 16;
|
|
|
|
if (itemLength > 0) {
|
|
this.output += `${this.readString(newOffset, itemLength)} `;
|
|
}
|
|
|
|
return newOffset + itemLength;
|
|
}
|
|
/**
|
|
* Returns true if the specified group and element indicate this tag can be read.
|
|
* @param {number} group
|
|
* @param {number} element
|
|
* @returns {boolean}
|
|
*/
|
|
// eslint-disable-next-line class-methods-use-this
|
|
|
|
|
|
}
|
|
|
|
const PRIVATE_DATA_READERS = [Siemens];
|
|
const TagIds = {
|
|
// metadata
|
|
TransferSyntax: [0x0002, 0x0010],
|
|
MetaLength: [0x0002, 0x0000],
|
|
// sublists
|
|
SublistItem: [0xFFFE, 0xE000],
|
|
SublistItemDelim: [0xFFFE, 0xE00D],
|
|
SequenceDelim: [0xFFFE, 0xE0DD],
|
|
// image dims
|
|
Rows: [0x0028, 0x0010],
|
|
Cols: [0x0028, 0x0011],
|
|
AcquisitionMatrix: [0x0018, 0x1310],
|
|
NumberOfFrames: [0x0028, 0x0008],
|
|
NumberTemporalPositions: [0x0020, 0x0105],
|
|
// voxel dims
|
|
PixelSpacing: [0x0028, 0x0030],
|
|
SliceThickness: [0x0018, 0x0050],
|
|
SliceGap: [0x0018, 0x0088],
|
|
Tr: [0x0018, 0x0080],
|
|
FrameTime: [0x0018, 0x1063],
|
|
// datatype
|
|
BitsAllocated: [0x0028, 0x0100],
|
|
BitsStored: [0x0028, 0x0101],
|
|
PixelRepresentation: [0x0028, 0x0103],
|
|
PixelPaddingValue: [0x0028, 0x0120],
|
|
HighBit: [0x0028, 0x0102],
|
|
PhotometricInterpretation: [0x0028, 0x0004],
|
|
SamplesPerPixel: [0x0028, 0x0002],
|
|
PlanarConfig: [0x0028, 0x0006],
|
|
PaletteRedDescriptor: [0x0028, 0x1101],
|
|
PaletteRed: [0x0028, 0x1201],
|
|
PaletteGreen: [0x0028, 0x1202],
|
|
PaletteBlue: [0x0028, 0x1203],
|
|
// data scale
|
|
DataScaleSlope: [0x0028, 0x1053],
|
|
DataScaleIntercept: [0x0028, 0x1052],
|
|
DataScaleElscint: [0x0207, 0x101f],
|
|
PixelBandwidth: [0x0018, 0x0095],
|
|
// LUT
|
|
VoiLutSequence: [0x0028, 0x3010],
|
|
VoiLutDescriptor: [0x0028, 0x3002],
|
|
// VoiLutExplanation: [0x0028, 0x3003],
|
|
VoiLutData: [0x0028, 0x3006],
|
|
// range
|
|
ImageMin: [0x0028, 0x0106],
|
|
ImageMax: [0x0028, 0x0107],
|
|
WindowCenter: [0x0028, 0x1050],
|
|
WindowWidth: [0x0028, 0x1051],
|
|
// descriptors
|
|
Charset: [0x0008, 0x0005],
|
|
PatientName: [0x0010, 0x0010],
|
|
PatientId: [0x0010, 0x0020],
|
|
StudyDate: [0x0008, 0x0020],
|
|
StudyTime: [0x0008, 0x0030],
|
|
StudyDes: [0x0008, 0x1030],
|
|
ImageType: [0x0008, 0x0008],
|
|
ImageComments: [0x0020, 0x4000],
|
|
SequenceName: [0x0018, 0x0024],
|
|
Modality: [0x0008, 0x0060],
|
|
// session ID
|
|
FrameOfRefUid: [0x0020, 0x0052],
|
|
// study ID
|
|
StudyUid: [0x0020, 0x000d],
|
|
// volume ID
|
|
SeriesDescription: [0x0008, 0x103e],
|
|
SeriesInstanceUid: [0x0020, 0x000e],
|
|
SeriesNumber: [0x0020, 0x0011],
|
|
EchoNumber: [0x0018, 0x0086],
|
|
TemporalPosition: [0x0020, 0x0100],
|
|
// slice ID
|
|
ImageNum: [0x0020, 0x0013],
|
|
SliceLocation: [0x0020, 0x1041],
|
|
// orientation
|
|
ImageOrientation: [0x0020, 0x0037],
|
|
ImagePosition: [0x0020, 0x0032],
|
|
SliceLocationVector: [0x0018, 0x2005],
|
|
// LUT shape
|
|
LutShape: [0x2050, 0x0020],
|
|
// pixel data
|
|
PixelData: [0x7fe0, 0x0010]
|
|
};
|
|
/**
|
|
* Create an ID string based on the specified group and element
|
|
* @param {number} group
|
|
* @param {number} element
|
|
* @returns {string}
|
|
*/
|
|
|
|
const createTagId = (group, element) => {
|
|
const groupStr = dec2hex$1(group);
|
|
const elemStr = dec2hex$1(element);
|
|
return groupStr + elemStr;
|
|
};
|
|
|
|
const VRMaxLength = {
|
|
AE: 16,
|
|
AS: 4,
|
|
AT: 4,
|
|
CS: 16,
|
|
DA: 8,
|
|
DS: 16,
|
|
DT: 26,
|
|
FL: 4,
|
|
FD: 8,
|
|
IS: 12,
|
|
LO: 64,
|
|
LT: 10240,
|
|
OB: -1,
|
|
OD: -1,
|
|
OF: -1,
|
|
OW: -1,
|
|
PN: 64 * 5,
|
|
SH: 16,
|
|
SL: 4,
|
|
SS: 2,
|
|
ST: 1024,
|
|
TM: 16,
|
|
UI: 64,
|
|
UL: 4,
|
|
UN: -1,
|
|
US: 2,
|
|
UT: -1
|
|
};
|
|
/**
|
|
* Create an ID string based on the specified group and element
|
|
* @param {Array} tupple with
|
|
* @param {number} group
|
|
* @param {number} element
|
|
* @returns {string}
|
|
*/
|
|
|
|
const createTagIdWithTag = ([group, element]) => {
|
|
const groupStr = dec2hex$1(group);
|
|
const elemStr = dec2hex$1(element);
|
|
return groupStr + elemStr;
|
|
};
|
|
|
|
const getUnsignedInteger16 = (rawData, littleEndian) => {
|
|
const data = [];
|
|
const mul = rawData.byteLength / 2;
|
|
|
|
for (let ctr = 0; ctr < mul; ctr += 1) {
|
|
data[ctr] = rawData.getUint16(ctr * 2, littleEndian);
|
|
}
|
|
|
|
return data;
|
|
};
|
|
|
|
const getSignedInteger16 = (rawData, littleEndian) => {
|
|
const data = [];
|
|
const mul = rawData.byteLength / 2;
|
|
|
|
for (let ctr = 0; ctr < mul; ctr += 1) {
|
|
data[ctr] = rawData.getInt16(ctr * 2, littleEndian);
|
|
}
|
|
|
|
return data;
|
|
};
|
|
|
|
const getFloat32 = (rawData, littleEndian) => {
|
|
const data = [];
|
|
const mul = rawData.byteLength / 4;
|
|
|
|
for (let ctr = 0; ctr < mul; ctr += 1) {
|
|
data[ctr] = rawData.getFloat32(ctr * 4, littleEndian);
|
|
}
|
|
|
|
return data;
|
|
};
|
|
|
|
const getSignedInteger32 = (rawData, littleEndian) => {
|
|
const data = [];
|
|
const mul = rawData.byteLength / 4;
|
|
|
|
for (let ctr = 0; ctr < mul; ctr += 1) {
|
|
data[ctr] = rawData.getInt32(ctr * 4, littleEndian);
|
|
}
|
|
|
|
return data;
|
|
};
|
|
|
|
const getUnsignedInteger32 = (rawData, littleEndian) => {
|
|
const data = [];
|
|
const mul = rawData.byteLength / 4;
|
|
|
|
for (let ctr = 0; ctr < mul; ctr += 1) {
|
|
data[ctr] = rawData.getUint32(ctr * 4, littleEndian);
|
|
}
|
|
|
|
return data;
|
|
};
|
|
|
|
const getFloat64 = (rawData, littleEndian) => {
|
|
if (rawData.byteLength < 8) {
|
|
return [0];
|
|
}
|
|
|
|
const data = [];
|
|
const mul = rawData.byteLength / 8;
|
|
|
|
for (let ctr = 0; ctr < mul; ctr += 1) {
|
|
data[ctr] = rawData.getFloat64(ctr * 8, littleEndian);
|
|
}
|
|
|
|
return data;
|
|
};
|
|
|
|
const getDoubleElscint = (rawData, littleEndian) => {
|
|
const data = Array(8);
|
|
|
|
if (littleEndian) {
|
|
for (let ctr = 0; ctr < 8; ctr += 1) {
|
|
data[ctr] = rawData.getUint8(ctr);
|
|
}
|
|
} else {
|
|
for (let ctr = 0; ctr < 8; ctr += 1) {
|
|
data[ctr] = rawData.getUint8(7 - ctr);
|
|
}
|
|
}
|
|
|
|
const reordered = [data[3], data[2], data[1], data[0], data[7], data[6], data[5], data[4]];
|
|
return [bytesToDouble(reordered)];
|
|
};
|
|
|
|
const getFixedLengthStringValue = (rawData, maxLength, charset, vr) => {
|
|
const mul = Math.floor(rawData.byteLength / maxLength);
|
|
const data = Array(mul);
|
|
|
|
for (let ctr = 0; ctr < mul; ctr += 1) {
|
|
data[ctr] = getStringAt(rawData, ctr * maxLength, maxLength, charset, vr);
|
|
}
|
|
|
|
return data;
|
|
};
|
|
|
|
const getStringValue = (rawData, charset, vr) => {
|
|
const data = getStringAt(rawData, 0, rawData.byteLength, charset, vr).split("\\");
|
|
|
|
for (let ctr = 0; ctr < data.length; ctr += 1) {
|
|
data[ctr] = data[ctr].trim();
|
|
}
|
|
|
|
return data;
|
|
};
|
|
|
|
const getSingleStringValue = (rawData, maxLength = 0, charset = undefined, vr = undefined) => {
|
|
const len = Math.min(rawData.byteLength, maxLength);
|
|
return [getStringAt(rawData, 0, len, charset, vr).trim()];
|
|
};
|
|
|
|
const getDateStringValue = rawData => {
|
|
const dotFormat = getSingleStringValue(rawData)[0].indexOf(".") !== -1;
|
|
const stringData = getFixedLengthStringValue(rawData, dotFormat ? 10 : VRMaxLength.DA);
|
|
let parts = null;
|
|
const data = [];
|
|
|
|
for (let ctr = 0; ctr < stringData.length; ctr += 1) {
|
|
if (dotFormat) {
|
|
parts = stringData[ctr].split(".");
|
|
|
|
if (parts.length === 3) {
|
|
data[ctr] = new Date(safeParseInt(parts[0]), safeParseInt(parts[1]) - 1, safeParseInt(parts[2]));
|
|
} else {
|
|
data[ctr] = new Date();
|
|
}
|
|
} else if (stringData[ctr].length === 8) {
|
|
data[ctr] = new Date(safeParseInt(stringData[ctr].substring(0, 4)), safeParseInt(stringData[ctr].substring(4, 6)) - 1, safeParseInt(stringData[ctr].substring(6, 8)));
|
|
} else {
|
|
data[ctr] = Date.parse(stringData[ctr]);
|
|
}
|
|
|
|
if (!isValidDate(data[ctr])) {
|
|
data[ctr] = stringData[ctr];
|
|
}
|
|
}
|
|
|
|
return data;
|
|
};
|
|
|
|
const getDateTimeStringValue = rawData => {
|
|
const stringData = getStringValue(rawData);
|
|
const data = [];
|
|
let year = 0;
|
|
let month = 0;
|
|
let date = 0;
|
|
let hours = 0;
|
|
let minutes = 0;
|
|
let seconds = 0;
|
|
|
|
for (let ctr = 0; ctr < stringData.length; ctr += 1) {
|
|
const str = stringData[ctr];
|
|
const strLen = str.length;
|
|
|
|
if (strLen >= 4) {
|
|
year = parseInt(str.substring(0, 4), 10); // required
|
|
|
|
if (strLen >= 6) {
|
|
month = safeParseInt(str.substring(4, 6)) - 1;
|
|
}
|
|
|
|
if (strLen >= 8) {
|
|
date = safeParseInt(str.substring(6, 8));
|
|
}
|
|
|
|
if (strLen >= 10) {
|
|
hours = safeParseInt(str.substring(8, 10));
|
|
}
|
|
|
|
if (strLen >= 12) {
|
|
minutes = safeParseInt(str.substring(10, 12));
|
|
}
|
|
|
|
if (strLen >= 14) {
|
|
seconds = safeParseInt(str.substring(12, 14));
|
|
}
|
|
|
|
data[ctr] = new Date(year, month, date, hours, minutes, seconds);
|
|
} else {
|
|
data[ctr] = Date.parse(str);
|
|
}
|
|
|
|
if (!isValidDate(data[ctr])) {
|
|
data[ctr] = str;
|
|
}
|
|
}
|
|
|
|
return data;
|
|
};
|
|
|
|
const getTimeStringValue = (rawData, ms = false) => {
|
|
const stringData = getStringValue(rawData);
|
|
const data = [];
|
|
|
|
if (ms) {
|
|
let parts = null;
|
|
let hours = 0;
|
|
let minutes = 0;
|
|
let seconds = 0;
|
|
|
|
for (let ctr = 0; ctr < stringData.length; ctr += 1) {
|
|
if (stringData[ctr].indexOf(":") !== -1) {
|
|
parts = stringData[ctr].split(":");
|
|
hours = safeParseInt(parts[0]);
|
|
|
|
if (parts.length > 1) {
|
|
minutes = safeParseInt(parts[1]);
|
|
}
|
|
|
|
if (parts.length > 2) {
|
|
seconds = safeParseFloat(parts[2]);
|
|
}
|
|
} else {
|
|
if (stringData[ctr].length >= 2) {
|
|
hours = safeParseInt(stringData[ctr].substring(0, 2));
|
|
}
|
|
|
|
if (stringData[ctr].length >= 4) {
|
|
minutes = safeParseInt(stringData[ctr].substring(2, 4));
|
|
}
|
|
|
|
if (stringData[ctr].length >= 6) {
|
|
seconds = safeParseFloat(stringData[ctr].substring(4));
|
|
}
|
|
}
|
|
|
|
data[ctr] = Math.round(hours * 60 * 60 * 1000 + minutes * 60 * 1000 + seconds * 1000);
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
return stringData;
|
|
};
|
|
|
|
const getDoubleStringValue = rawData => {
|
|
const stringData = getStringValue(rawData);
|
|
const data = [];
|
|
|
|
for (let ctr = 0; ctr < stringData.length; ctr += 1) {
|
|
data[ctr] = parseFloat(stringData[ctr]);
|
|
}
|
|
|
|
return data;
|
|
};
|
|
|
|
const getIntegerStringValue = rawData => {
|
|
const stringData = getStringValue(rawData);
|
|
const data = [];
|
|
|
|
for (let ctr = 0; ctr < stringData.length; ctr += 1) {
|
|
data[ctr] = parseInt(stringData[ctr], 10);
|
|
}
|
|
|
|
return data;
|
|
};
|
|
|
|
const getPersonNameStringValue = (rawData, charset, vr) => {
|
|
const stringData = getStringValue(rawData, charset, vr);
|
|
const data = Array(stringData.length);
|
|
|
|
for (let ctr = 0; ctr < stringData.length; ctr += 1) {
|
|
data[ctr] = stringData[ctr].replace("^", " ");
|
|
}
|
|
|
|
return data;
|
|
};
|
|
|
|
const convertPrivateValue = (group, element, rawData) => {
|
|
let privReader;
|
|
|
|
for (let ctr = 0; ctr < PRIVATE_DATA_READERS.length; ctr += 1) {
|
|
privReader = new PRIVATE_DATA_READERS[ctr](rawData.buffer);
|
|
|
|
if (privReader.canRead(group, element)) {
|
|
return privReader.readHeader();
|
|
}
|
|
}
|
|
|
|
return rawData;
|
|
};
|
|
|
|
const convertValue = (vr, rawData, littleEndian, charset) => {
|
|
let data = null; // http://dicom.nema.org/dicom/2013/output/chtml/part05/sect_6.2.html
|
|
|
|
switch (vr) {
|
|
case "AE":
|
|
data = getSingleStringValue(rawData, VRMaxLength.AE);
|
|
break;
|
|
|
|
case "AS":
|
|
data = getFixedLengthStringValue(rawData, VRMaxLength.AS);
|
|
break;
|
|
|
|
case "AT":
|
|
data = getUnsignedInteger16(rawData, littleEndian);
|
|
break;
|
|
|
|
case "CS":
|
|
data = getStringValue(rawData);
|
|
break;
|
|
|
|
case "DA":
|
|
data = getDateStringValue(rawData);
|
|
break;
|
|
|
|
case "DS":
|
|
data = getDoubleStringValue(rawData);
|
|
break;
|
|
|
|
case "DT":
|
|
data = getDateTimeStringValue(rawData);
|
|
break;
|
|
|
|
case "FL":
|
|
data = getFloat32(rawData, littleEndian);
|
|
break;
|
|
|
|
case "FD":
|
|
data = getFloat64(rawData, littleEndian);
|
|
break;
|
|
|
|
case "FE":
|
|
data = getDoubleElscint(rawData, littleEndian);
|
|
break;
|
|
|
|
case "IS":
|
|
data = getIntegerStringValue(rawData);
|
|
break;
|
|
|
|
case "LO":
|
|
data = getStringValue(rawData, charset, vr);
|
|
break;
|
|
|
|
case "LT":
|
|
data = getSingleStringValue(rawData, VRMaxLength.AT);
|
|
break;
|
|
|
|
case "OB":
|
|
case "OD":
|
|
case "OF":
|
|
case "OW":
|
|
data = rawData;
|
|
break;
|
|
|
|
case "PN":
|
|
data = getPersonNameStringValue(rawData, charset, vr);
|
|
break;
|
|
|
|
case "SH":
|
|
data = getStringValue(rawData, charset, vr);
|
|
break;
|
|
|
|
case "SL":
|
|
data = getSignedInteger32(rawData, littleEndian);
|
|
break;
|
|
|
|
case "SQ":
|
|
data = null;
|
|
break;
|
|
|
|
case "SS":
|
|
data = getSignedInteger16(rawData, littleEndian);
|
|
break;
|
|
|
|
case "ST":
|
|
data = getSingleStringValue(rawData, VRMaxLength.ST);
|
|
break;
|
|
|
|
case "TM":
|
|
data = getTimeStringValue(rawData);
|
|
break;
|
|
|
|
case "UI":
|
|
data = getStringValue(rawData);
|
|
break;
|
|
|
|
case "UL":
|
|
data = getUnsignedInteger32(rawData, littleEndian);
|
|
break;
|
|
|
|
case "UN":
|
|
data = rawData;
|
|
break;
|
|
|
|
case "US":
|
|
data = getUnsignedInteger16(rawData, littleEndian);
|
|
break;
|
|
|
|
case "UT":
|
|
data = getSingleStringValue(rawData, Number.MAX_SAFE_INTEGER, charset, vr);
|
|
break;
|
|
}
|
|
|
|
return data;
|
|
};
|
|
|
|
class Tag {
|
|
static isEqual({
|
|
group,
|
|
element
|
|
}, tagId) {
|
|
return group === tagId[0] && element === tagId[1];
|
|
}
|
|
/**
|
|
* The Tag constuctor.
|
|
* @property {number} group
|
|
* @property {number} element
|
|
* @property {string} vr
|
|
* @property {number} offsetStart
|
|
* @property {number} offsetValue
|
|
* @property {number} offsetEnd
|
|
* @property {boolean} sublist - true if this tag is a sublist
|
|
* @property {number|number[]|string|string[]|object} value
|
|
* @type {Function}
|
|
*/
|
|
|
|
|
|
constructor({
|
|
group,
|
|
element,
|
|
vr = null,
|
|
value = null,
|
|
offsetStart = null,
|
|
offsetValue = null,
|
|
offsetEnd = null,
|
|
littleEndian = true,
|
|
charset = null
|
|
}) {
|
|
this.id = void 0;
|
|
this.group = void 0;
|
|
this.element = void 0;
|
|
this.vr = void 0;
|
|
this.rawValue = void 0;
|
|
this.convertedValue = void 0;
|
|
this.offsetStart = void 0;
|
|
this.offsetValue = void 0;
|
|
this.offsetEnd = void 0;
|
|
this.sublist = void 0;
|
|
this.preformatted = void 0;
|
|
this.littleEndian = void 0;
|
|
this.charset = void 0;
|
|
this.group = group;
|
|
this.element = element;
|
|
this.vr = vr;
|
|
this.offsetStart = offsetStart;
|
|
this.offsetValue = offsetValue;
|
|
this.offsetEnd = offsetEnd;
|
|
this.sublist = false;
|
|
this.preformatted = false;
|
|
this.id = createTagId(group, element);
|
|
this.littleEndian = littleEndian;
|
|
this.charset = charset;
|
|
this.rawValue = value;
|
|
|
|
if (value instanceof Array) {
|
|
this.sublist = true;
|
|
this.rawValue = value;
|
|
this.convertedValue = value;
|
|
}
|
|
}
|
|
|
|
hasValue() {
|
|
return this.rawValue !== null;
|
|
}
|
|
|
|
get value() {
|
|
// oonly convert value if needed
|
|
return this.convertedValue || this.getConvertedValue();
|
|
}
|
|
|
|
getConvertedValue() {
|
|
if (this.rawValue === null) {
|
|
return null;
|
|
}
|
|
|
|
const {
|
|
rawValue,
|
|
vr,
|
|
littleEndian,
|
|
charset,
|
|
group,
|
|
element
|
|
} = this;
|
|
const dv = rawValue;
|
|
let convertedValue = convertValue(vr, dv, littleEndian, charset);
|
|
|
|
if (convertedValue === dv && this.isPrivateData()) {
|
|
convertedValue = convertPrivateValue(group, element, dv);
|
|
this.preformatted = convertedValue !== dv;
|
|
}
|
|
|
|
this.convertedValue = convertedValue;
|
|
return convertedValue;
|
|
}
|
|
/**
|
|
* Returns true if this is the transform syntax tag.
|
|
* @returns {boolean}
|
|
*/
|
|
|
|
|
|
is([group, element]) {
|
|
return this.group === group && this.element === element;
|
|
}
|
|
/**
|
|
* Returns true if this tag contains private data.
|
|
* @returns {boolean}
|
|
*/
|
|
|
|
|
|
isPrivateData() {
|
|
/* eslint-disable no-bitwise */
|
|
return (this.group & 1) === 1;
|
|
}
|
|
/**
|
|
* Returns true if this tag contains private data that can be read.
|
|
* @returns {boolean}
|
|
*/
|
|
|
|
|
|
hasInterpretedPrivateData() {
|
|
return this.isPrivateData() && isString(this.value);
|
|
}
|
|
/**
|
|
* Returns a string representation of this tag.
|
|
* @param {number} [level] - the indentation level
|
|
* @param {boolean} [html]
|
|
* @returns {string}
|
|
*/
|
|
|
|
|
|
toString(level = 0, html = false) {
|
|
let valueStr = "";
|
|
const groupStr = dec2hex$1(this.group);
|
|
const elemStr = dec2hex$1(this.element);
|
|
let tagStr = `(${groupStr},${elemStr})`;
|
|
let des = "";
|
|
let padding;
|
|
padding = "";
|
|
|
|
for (let ctr = 0; ctr < level; ctr += 1) {
|
|
if (html) {
|
|
padding += " ";
|
|
} else {
|
|
padding += " ";
|
|
}
|
|
}
|
|
|
|
if (this.sublist) {
|
|
const value = this.value;
|
|
|
|
for (let ctr = 0; ctr < value.length; ctr += 1) {
|
|
const tag = value[ctr];
|
|
valueStr += `\n${tag.toString(level + 1, html)}`;
|
|
}
|
|
} else if (this.vr === "SQ" || this.is(TagIds.PixelData) || !this.value) {
|
|
valueStr = "";
|
|
} else if (html && this.preformatted) {
|
|
valueStr = `[<pre>${this.value}</pre>]`;
|
|
} else {
|
|
valueStr = `[${this.value}]`;
|
|
}
|
|
|
|
if (this.is(TagIds.SublistItem)) {
|
|
tagStr = "Sequence Item";
|
|
} else if (this.is(TagIds.SublistItemDelim)) {
|
|
tagStr = "Sequence Item Delimiter";
|
|
} else if (this.is(TagIds.SequenceDelim)) {
|
|
tagStr = "Sequence Delimiter";
|
|
} else if (this.is(TagIds.PixelData)) {
|
|
tagStr = "Pixel Data";
|
|
} else {
|
|
des = convertCamcelCaseToTitleCase(Dictionary.getDescription(this.group, this.element));
|
|
}
|
|
|
|
if (html) {
|
|
return `${padding}<span style='color:#B5CBD3'>${tagStr}</span> ${des} ${valueStr}`;
|
|
}
|
|
|
|
return `${padding} ${tagStr} ${des} ${valueStr}`;
|
|
}
|
|
/**
|
|
* Returns an HTML string representation of this tag.
|
|
* @param {number} level - the indentation level
|
|
* @returns {string}
|
|
*/
|
|
|
|
|
|
toHTMLString(level = 0) {
|
|
return this.toString(level, true);
|
|
} // for test, ignore any ptotocol changes etc
|
|
|
|
|
|
toObject() {
|
|
const {
|
|
id,
|
|
group,
|
|
element,
|
|
vr,
|
|
value,
|
|
offsetStart,
|
|
offsetValue,
|
|
offsetEnd,
|
|
sublist,
|
|
preformatted
|
|
} = this;
|
|
return {
|
|
id,
|
|
group,
|
|
element,
|
|
vr,
|
|
value,
|
|
offsetStart,
|
|
offsetValue,
|
|
offsetEnd,
|
|
sublist,
|
|
preformatted
|
|
};
|
|
}
|
|
|
|
}
|
|
|
|
const lutInfoFromImage = image => {
|
|
const lutDescriptor = image.getTagValue(TagIds.VoiLutDescriptor);
|
|
|
|
if ((lutDescriptor === null || lutDescriptor === void 0 ? void 0 : lutDescriptor.length) !== 3) {
|
|
return null;
|
|
}
|
|
|
|
const [nEntries, firstValue, bitsStored] = lutDescriptor;
|
|
|
|
if (nEntries === 0 || nEntries >= 2 ** bitsStored - 1) {
|
|
return null;
|
|
}
|
|
|
|
let ArrayType = Uint8Array;
|
|
|
|
if (bitsStored > 8) {
|
|
ArrayType = Uint16Array;
|
|
}
|
|
|
|
const lutDataTagValue = image.getTagValue(TagIds.VoiLutData);
|
|
|
|
if (!lutDataTagValue) {
|
|
return null;
|
|
}
|
|
|
|
const data = new ArrayType(lutDataTagValue, 0, Math.min(lutDescriptor[0] || 2 ** 16, lutDataTagValue.length));
|
|
return {
|
|
nEntries,
|
|
firstValue,
|
|
bitsStored,
|
|
data
|
|
};
|
|
};
|
|
|
|
const paletteInfoFromImage = info => {
|
|
const {
|
|
image
|
|
} = info;
|
|
const reds = image.getTagValue(TagIds.PaletteRed);
|
|
const greens = image.getTagValue(TagIds.PaletteGreen);
|
|
const blues = image.getTagValue(TagIds.PaletteBlue);
|
|
|
|
if ((reds === null || reds === void 0 ? void 0 : reds.byteLength) > 0 && (greens === null || greens === void 0 ? void 0 : greens.byteLength) > 0 && (blues === null || blues === void 0 ? void 0 : blues.byteLength) > 0) {
|
|
const paletteInfo = image.getTagValue(TagIds.PaletteRedDescriptor);
|
|
const [nEntries,, bitsAllocated] = paletteInfo;
|
|
return {
|
|
nEntries,
|
|
bitsAllocated,
|
|
r: reds,
|
|
g: greens,
|
|
b: blues
|
|
};
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
const displayInfoFromDecoderInfo = info => {
|
|
const {
|
|
image
|
|
} = info;
|
|
let invert = image.getTagValueIndexed(TagIds.LutShape) === "inverse";
|
|
invert = invert || image.photometricInterpretation === "MONOCHROME1";
|
|
|
|
const displayInfo = _extends({}, info, {
|
|
nFrames: image.numberOfFrames || 1,
|
|
pixelPaddingVal: image.pixelPaddingValue,
|
|
lut: lutInfoFromImage(info.image),
|
|
palette: paletteInfoFromImage(info),
|
|
minPixVal: image.imageMin,
|
|
maxPixVal: image.imageMax,
|
|
windowCenter: image.windowCenter,
|
|
windowWidth: image.windowWidth,
|
|
slope: image.dataScaleSlope || 1.0,
|
|
intercept: image.dataScaleIntercept || 0.0,
|
|
invert
|
|
});
|
|
|
|
return displayInfo;
|
|
};
|
|
|
|
class FrameInfo {
|
|
constructor(info) {
|
|
this.frameNo = void 0;
|
|
this.imageInfo = void 0;
|
|
this.texture = void 0;
|
|
this.gl = void 0;
|
|
this.imageInfo = info.imageInfo;
|
|
this.frameNo = info.frameNo;
|
|
this.texture = info.texture;
|
|
this.gl = info.gl;
|
|
}
|
|
|
|
destroy() {
|
|
this.gl.deleteTexture(this.texture);
|
|
}
|
|
|
|
}
|
|
|
|
class Decoder {
|
|
constructor(image) {
|
|
this.image = void 0;
|
|
this.outputSize = void 0;
|
|
this.image = displayInfoFromDecoderInfo(image);
|
|
this.outputSize = image.size;
|
|
}
|
|
|
|
async getFrame(gl, frameNo) {
|
|
const texture = await this.createTexture(gl, frameNo);
|
|
return new FrameInfo({
|
|
imageInfo: this.image,
|
|
frameNo,
|
|
gl,
|
|
texture
|
|
});
|
|
} // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
|
|
|
|
decode(frameNo) {
|
|
const {
|
|
data,
|
|
nFrames
|
|
} = this.image;
|
|
const bytesPerFrame = data.byteLength / nFrames;
|
|
const dv = new DataView(data.buffer, data.byteOffset + bytesPerFrame * frameNo, bytesPerFrame);
|
|
return Promise.resolve(dv);
|
|
}
|
|
|
|
async createTexture(gl, frameNo) {
|
|
const pixelData = await this.decode(frameNo);
|
|
const buffer = new Uint8Array(pixelData.buffer, pixelData.byteOffset, pixelData.byteLength);
|
|
let {
|
|
height
|
|
} = this.outputSize;
|
|
const {
|
|
width
|
|
} = this.outputSize;
|
|
const {
|
|
image
|
|
} = this;
|
|
let format = gl.LUMINANCE_ALPHA;
|
|
let internalFormat = gl.LUMINANCE_ALPHA;
|
|
|
|
if (image.rgb && !image.planar && !image.palette) {
|
|
format = gl.RGB;
|
|
internalFormat = gl.RGB;
|
|
} else if (image.bytesAllocated === 1) {
|
|
format = gl.LUMINANCE;
|
|
internalFormat = gl.LUMINANCE;
|
|
}
|
|
|
|
if (image.planar) {
|
|
height *= image.samples;
|
|
}
|
|
|
|
return Promise.resolve(twgl.createTexture(gl, {
|
|
src: buffer,
|
|
width,
|
|
height,
|
|
format,
|
|
internalFormat,
|
|
type: gl.UNSIGNED_BYTE,
|
|
min: gl.NEAREST,
|
|
mag: gl.NEAREST,
|
|
wrap: gl.CLAMP_TO_EDGE
|
|
}));
|
|
}
|
|
|
|
}
|
|
|
|
const getSingleValueSafely = (tag, index) => {
|
|
var _tag$value;
|
|
|
|
return (tag === null || tag === void 0 ? void 0 : (_tag$value = tag.value) === null || _tag$value === void 0 ? void 0 : _tag$value[index]) || null;
|
|
};
|
|
|
|
const getValueSafely = tag => {
|
|
var _tag$value2;
|
|
|
|
return (_tag$value2 = tag === null || tag === void 0 ? void 0 : tag.value) !== null && _tag$value2 !== void 0 ? _tag$value2 : null;
|
|
};
|
|
|
|
var Axis;
|
|
|
|
(function (Axis) {
|
|
Axis["R"] = "R";
|
|
Axis["L"] = "L";
|
|
Axis["A"] = "A";
|
|
Axis["P"] = "P";
|
|
Axis["F"] = "F";
|
|
Axis["H"] = "H";
|
|
})(Axis || (Axis = {})); // originally from: http://public.kitware.com/pipermail/insight-users/2005-March/012246.html
|
|
|
|
|
|
const ObliquityThresholdCosineValue = 0.8;
|
|
|
|
const getMajorAxisFromPatientRelativeDirectionCosine = (x, y, z) => {
|
|
const absX = Math.abs(x);
|
|
const absY = Math.abs(y);
|
|
const absZ = Math.abs(z); // The tests here really don't need to check the other dimensions,
|
|
// just the threshold, since the sum of the squares should be == 1.0
|
|
// but just in case ...
|
|
|
|
let axis = null;
|
|
|
|
if (absX > ObliquityThresholdCosineValue && absX > absY && absX > absZ) {
|
|
const orientationX = x < 0 ? Axis.R : Axis.L;
|
|
axis = orientationX;
|
|
} else if (absY > ObliquityThresholdCosineValue && absY > absX && absY > absZ) {
|
|
const orientationY = y < 0 ? Axis.A : Axis.P;
|
|
axis = orientationY;
|
|
} else if (absZ > ObliquityThresholdCosineValue && absZ > absX && absZ > absY) {
|
|
const orientationZ = z < 0 ? Axis.F : Axis.H;
|
|
axis = orientationZ;
|
|
}
|
|
|
|
return axis;
|
|
};
|
|
|
|
class DCMImage {
|
|
constructor() {
|
|
this.tags = {};
|
|
this.tagsFlat = {};
|
|
this.littleEndian = false;
|
|
this.index = -1;
|
|
this.decompressed = false;
|
|
this.privateDataAll = null;
|
|
this.bytesAllocated = null;
|
|
}
|
|
/**
|
|
* Returns a tag matching the specified group and element tuple
|
|
* @param {TagTupleID} tag - Tuple of group & elem like TagIds values
|
|
* @returns
|
|
*/
|
|
|
|
|
|
getTag(tag) {
|
|
var _this$tags$tagId;
|
|
|
|
const [group, element] = tag;
|
|
const tagId = createTagId(group, element);
|
|
return (_this$tags$tagId = this.tags[tagId]) !== null && _this$tags$tagId !== void 0 ? _this$tags$tagId : this.tagsFlat[tagId];
|
|
}
|
|
/**
|
|
* Returns a tag matching the specified group and element tuple ignoring tagsFlat
|
|
* @param {TagTupleID} tag - Tuple of group & elem like TagIds values
|
|
* @returns
|
|
*/
|
|
|
|
|
|
getTopLevelTag(tag) {
|
|
const [group, element] = tag;
|
|
const tagId = createTagId(group, element);
|
|
return this.tags[tagId];
|
|
}
|
|
/**
|
|
* get the value of the tag if exists
|
|
* @param {TagTupleID} tag tuple of group and element ids
|
|
* @returns the value of the tag or null if not exist
|
|
*/
|
|
|
|
|
|
getTagValue(tag) {
|
|
return getValueSafely(this.getTag(tag));
|
|
}
|
|
/**
|
|
* get the value of the tag if exists
|
|
* @param {TagTupleID} tag tuple of group and element ids
|
|
* @param {Number} index the position in the value
|
|
* @returns the value at index or null if not exist
|
|
*/
|
|
|
|
|
|
getTagValueIndexed(tag, index = 0) {
|
|
return getSingleValueSafely(this.getTag(tag), index);
|
|
}
|
|
/**
|
|
* Returns the pixel data tag.
|
|
* @returns {Tag}
|
|
*/
|
|
|
|
|
|
get pixelData() {
|
|
return this.tags[createTagIdWithTag(TagIds.PixelData)];
|
|
}
|
|
/**
|
|
* Returns the number of columns.
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
get columns() {
|
|
return this.getTagValueIndexed(TagIds.Cols);
|
|
}
|
|
/**
|
|
* Returns the number of rows.
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
get rows() {
|
|
return this.getTagValueIndexed(TagIds.Rows);
|
|
}
|
|
/**
|
|
* Returns the series description.
|
|
* @returns {string}
|
|
*/
|
|
|
|
|
|
get seriesDescription() {
|
|
return this.getTagValueIndexed(TagIds.SeriesDescription);
|
|
}
|
|
/**
|
|
* Returns the series instance UID.
|
|
* @returns {string}
|
|
*/
|
|
|
|
|
|
get seriesInstanceUID() {
|
|
return this.getTagValueIndexed(TagIds.SeriesInstanceUid);
|
|
}
|
|
/**
|
|
* Returns the series number.
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
get seriesNumber() {
|
|
return this.getTagValueIndexed(TagIds.SeriesNumber);
|
|
}
|
|
/**
|
|
* Returns the echo number.
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
get echoNumber() {
|
|
return this.getTagValueIndexed(TagIds.EchoNumber);
|
|
}
|
|
/**
|
|
* Returns the image position.
|
|
* @return {number[]}
|
|
*/
|
|
|
|
|
|
get imagePosition() {
|
|
return this.getTagValue(TagIds.ImagePosition);
|
|
}
|
|
/**
|
|
* Returns the image axis directions
|
|
* @return {number[]}
|
|
*/
|
|
|
|
|
|
get imageDirections() {
|
|
return this.getTagValue(TagIds.ImageOrientation);
|
|
}
|
|
/**
|
|
* Returns the image position value by index.
|
|
* @param {number} sliceDir - the index
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
getImagePositionSliceDir(sliceDir) {
|
|
const imagePos = this.imagePosition;
|
|
|
|
if (imagePos && sliceDir >= 0) {
|
|
return imagePos[sliceDir];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
/**
|
|
* Returns the modality
|
|
* @returns {string}
|
|
*/
|
|
|
|
|
|
get modality() {
|
|
return this.getTagValueIndexed(TagIds.Modality);
|
|
}
|
|
/**
|
|
* Returns the slice location.
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
get sliceLocation() {
|
|
return this.getTagValueIndexed(TagIds.SliceLocation);
|
|
}
|
|
/**
|
|
* Returns the slice location vector.
|
|
* @returns {number[]}
|
|
*/
|
|
|
|
|
|
get sliceLocationVector() {
|
|
return this.getTagValue(TagIds.SliceLocationVector);
|
|
}
|
|
/**
|
|
* Returns the image number.
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
get imageNumber() {
|
|
return this.getTagValueIndexed(TagIds.ImageNum);
|
|
}
|
|
/**
|
|
* Returns the temporal position.
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
get temporalPosition() {
|
|
return this.getTagValueIndexed(TagIds.TemporalPosition);
|
|
}
|
|
/**
|
|
* Returns the temporal number.
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
get temporalNumber() {
|
|
return this.getTagValueIndexed(TagIds.NumberTemporalPositions);
|
|
}
|
|
/**
|
|
* Returns the slice gap.
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
get sliceGap() {
|
|
return this.getTagValueIndexed(TagIds.SliceGap);
|
|
}
|
|
/**
|
|
* Returns the slice thickness.
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
get sliceThickness() {
|
|
return this.getTagValueIndexed(TagIds.SliceThickness);
|
|
}
|
|
/**
|
|
* Returns the image maximum.
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
get imageMax() {
|
|
return this.getTagValueIndexed(TagIds.ImageMax);
|
|
}
|
|
/**
|
|
* Returns the image minimum.
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
get imageMin() {
|
|
return this.getTagValueIndexed(TagIds.ImageMin);
|
|
}
|
|
/**
|
|
* Returns the rescale slope.
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
get dataScaleSlope() {
|
|
return this.getTagValueIndexed(TagIds.DataScaleSlope);
|
|
}
|
|
/**
|
|
* Returns the rescale intercept.
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
get dataScaleIntercept() {
|
|
return this.getTagValueIndexed(TagIds.DataScaleIntercept);
|
|
}
|
|
|
|
get dataScaleElscint() {
|
|
let scale = this.getTagValueIndexed(TagIds.DataScaleElscint) || 1;
|
|
const bandwidth = this.pixelBandwidth;
|
|
scale = Math.sqrt(bandwidth) / (10 * scale);
|
|
|
|
if (scale <= 0) {
|
|
scale = 1;
|
|
}
|
|
|
|
return scale;
|
|
}
|
|
/**
|
|
* Returns the window width (from top level as frame data can have these values)
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
get windowWidth() {
|
|
var _this$getTopLevelTag;
|
|
|
|
const tagVal = (_this$getTopLevelTag = this.getTopLevelTag(TagIds.WindowWidth)) === null || _this$getTopLevelTag === void 0 ? void 0 : _this$getTopLevelTag.value;
|
|
return tagVal === null || tagVal === void 0 ? void 0 : tagVal[0];
|
|
}
|
|
/**
|
|
* Returns the window center (from top level as frame data can have these values)
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
get windowCenter() {
|
|
var _this$getTopLevelTag2;
|
|
|
|
const tagVal = (_this$getTopLevelTag2 = this.getTopLevelTag(TagIds.WindowCenter)) === null || _this$getTopLevelTag2 === void 0 ? void 0 : _this$getTopLevelTag2.value;
|
|
return tagVal === null || tagVal === void 0 ? void 0 : tagVal[0];
|
|
}
|
|
|
|
get pixelBandwidth() {
|
|
return this.getTagValueIndexed(TagIds.PixelBandwidth);
|
|
}
|
|
|
|
get seriesId() {
|
|
const ids = [this.seriesDescription, this.seriesInstanceUID, this.seriesNumber, this.echoNumber, this.orientation].filter(id => id != null); // remove nulls
|
|
|
|
const {
|
|
columns,
|
|
rows
|
|
} = this;
|
|
return `${ids.join(",")} (${columns} x ${rows})`;
|
|
}
|
|
/**
|
|
* Returns the pixel spacing.
|
|
* @returns {number[]}
|
|
*/
|
|
|
|
|
|
get pixelSpacing() {
|
|
return this.getTagValue(TagIds.PixelSpacing);
|
|
}
|
|
/**
|
|
* Returns the image type.
|
|
* @returns {string[]}
|
|
*/
|
|
|
|
|
|
get imageType() {
|
|
return this.getTagValue(TagIds.ImageType);
|
|
}
|
|
/**
|
|
* Returns the number of bits stored.
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
get bitsStored() {
|
|
return this.getTagValueIndexed(TagIds.BitsStored);
|
|
}
|
|
/**
|
|
* Returns the number of bits allocated.
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
get bitsAllocated() {
|
|
return this.getTagValueIndexed(TagIds.BitsAllocated);
|
|
}
|
|
/**
|
|
* Returns the frame time.
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
getFrameTime() {
|
|
return this.getTagValueIndexed(TagIds.FrameTime);
|
|
}
|
|
/**
|
|
* Returns the acquisition matrix (e.g., "mosaic" data).
|
|
* @returns {number[]}
|
|
*/
|
|
|
|
|
|
getAcquisitionMatrix() {
|
|
var _this$privateDataAll;
|
|
|
|
const mat = [0, 0];
|
|
mat[0] = this.getTagValueIndexed(TagIds.AcquisitionMatrix);
|
|
|
|
if (this.privateDataAll === null) {
|
|
this.privateDataAll = this.allInterpretedPrivateData;
|
|
}
|
|
|
|
if (((_this$privateDataAll = this.privateDataAll) === null || _this$privateDataAll === void 0 ? void 0 : _this$privateDataAll.length) > 0) {
|
|
const start = this.privateDataAll.indexOf("AcquisitionMatrixText");
|
|
|
|
if (start !== -1) {
|
|
const end = this.privateDataAll.indexOf("\n", start);
|
|
|
|
if (end !== -1) {
|
|
var _matPrivate$length;
|
|
|
|
const str = this.privateDataAll.substring(start, end);
|
|
const matPrivate = str.match(/\d+/g);
|
|
|
|
if ((_matPrivate$length = matPrivate === null || matPrivate === void 0 ? void 0 : matPrivate.length) !== null && _matPrivate$length !== void 0 ? _matPrivate$length : 0 >= 1) {
|
|
mat[0] = parseFloat(matPrivate[0]);
|
|
|
|
if ((matPrivate === null || matPrivate === void 0 ? void 0 : matPrivate.length) === 2) {
|
|
mat[1] = parseFloat(matPrivate[1]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mat[1] === 0) {
|
|
[mat[1]] = mat;
|
|
}
|
|
|
|
return mat;
|
|
}
|
|
/**
|
|
* Returns the TR.
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
getTR() {
|
|
return this.getTagValueIndexed(TagIds.Tr);
|
|
}
|
|
|
|
putTag(tag) {
|
|
this.tags[tag.id] = tag;
|
|
this.putFlattenedTag(this.tagsFlat, tag);
|
|
}
|
|
|
|
putFlattenedTag(tags, tag) {
|
|
if (tag.sublist) {
|
|
const value = tag.value;
|
|
|
|
for (let ctr = 0; ctr < value.length; ctr += 1) {
|
|
this.putFlattenedTag(tags, value[ctr]);
|
|
}
|
|
} else if (!tags[tag.id]) {
|
|
// eslint-disable-next-line no-param-reassign
|
|
tags[tag.id] = tag;
|
|
}
|
|
}
|
|
/**
|
|
* Returns true if pixel data is found.
|
|
* @returns {boolean}
|
|
*/
|
|
|
|
|
|
hasPixelData() {
|
|
return this.tags[createTagIdWithTag(TagIds.PixelData)] !== undefined;
|
|
}
|
|
/**
|
|
* Returns an orientation string (e.g., XYZ+--).
|
|
* @returns {string}
|
|
*/
|
|
|
|
|
|
get orientation() {
|
|
let orientation = null;
|
|
const dirCos = this.getTagValue(TagIds.ImageOrientation);
|
|
let bigRow = 0;
|
|
let bigCol = 0;
|
|
|
|
if ((dirCos === null || dirCos === void 0 ? void 0 : dirCos.length) !== 6) {
|
|
return null;
|
|
}
|
|
|
|
const spacing = this.pixelSpacing;
|
|
|
|
if (!spacing) {
|
|
return null;
|
|
}
|
|
|
|
const [rowSpacing] = spacing;
|
|
let biggest = 0;
|
|
let ctr = 0;
|
|
|
|
for (; ctr < 3; ctr += 1) {
|
|
if (Math.abs(dirCos[ctr]) > biggest) {
|
|
biggest = Math.abs(dirCos[ctr]);
|
|
bigRow = ctr;
|
|
}
|
|
}
|
|
|
|
biggest = 0;
|
|
|
|
for (; ctr < 6; ctr += 1) {
|
|
if (Math.abs(dirCos[ctr]) > biggest) {
|
|
biggest = Math.abs(dirCos[ctr]);
|
|
bigCol = ctr;
|
|
}
|
|
}
|
|
|
|
let orient = "";
|
|
|
|
switch (bigRow) {
|
|
case 0:
|
|
orient += "X";
|
|
|
|
if (bigCol === 4) {
|
|
orient += "YZ";
|
|
} else {
|
|
orient += "ZY";
|
|
}
|
|
|
|
break;
|
|
|
|
case 1:
|
|
orient += "Y";
|
|
|
|
if (bigCol === 3) {
|
|
orient += "XZ";
|
|
} else {
|
|
orient += "ZX";
|
|
}
|
|
|
|
break;
|
|
|
|
case 2:
|
|
orient += "Z";
|
|
|
|
if (bigCol === 3) {
|
|
orient += "XY";
|
|
} else {
|
|
orient += "YX";
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
switch (bigRow) {
|
|
case 0:
|
|
if (dirCos[bigRow] > 0.0) {
|
|
orient += "-";
|
|
} else {
|
|
orient += "+";
|
|
}
|
|
|
|
if (bigCol === 4) {
|
|
if (dirCos[bigCol] > 0.0) {
|
|
orient += "-";
|
|
} else {
|
|
orient += "+";
|
|
}
|
|
} else if (dirCos[bigCol] > 0.0) {
|
|
orient += "+";
|
|
} else {
|
|
orient += "-";
|
|
}
|
|
|
|
break;
|
|
|
|
case 1:
|
|
if (dirCos[bigRow] > 0.0) {
|
|
orient += "-";
|
|
} else {
|
|
orient += "+";
|
|
}
|
|
|
|
if (bigCol === 3) {
|
|
if (dirCos[bigCol] > 0.0) {
|
|
orient += "-";
|
|
} else {
|
|
orient += "+";
|
|
}
|
|
} else if (dirCos[bigCol] > 0.0) {
|
|
orient += "+";
|
|
} else {
|
|
orient += "-";
|
|
}
|
|
|
|
break;
|
|
|
|
case 2:
|
|
if (dirCos[bigRow] > 0.0) {
|
|
orient += "+";
|
|
} else {
|
|
orient += "-";
|
|
} // Has to be X or Y so opposite senses
|
|
|
|
|
|
if (dirCos[bigCol] > 0.0) {
|
|
orient += "-";
|
|
} else {
|
|
orient += "+";
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (rowSpacing === 0.0) {
|
|
orient += "+";
|
|
orientation = orient;
|
|
} else {
|
|
{
|
|
switch (orient.charAt(2)) {
|
|
case "X":
|
|
if (rowSpacing > 0.0) {
|
|
orient += "-";
|
|
} else {
|
|
orient += "+";
|
|
}
|
|
|
|
break;
|
|
|
|
case "Y":
|
|
case "Z":
|
|
if (rowSpacing > 0.0) {
|
|
orient += "+";
|
|
} else {
|
|
orient += "-";
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
orientation = orient;
|
|
}
|
|
|
|
return orientation;
|
|
}
|
|
/**
|
|
* Returns true if this image is "mosaic".
|
|
* @returns {boolean}
|
|
*/
|
|
|
|
|
|
isMosaic() {
|
|
const {
|
|
imageType
|
|
} = this;
|
|
let labeledAsMosaic = false;
|
|
|
|
if (imageType !== null) {
|
|
for (let ctr = 0; ctr < imageType.length; ctr += 1) {
|
|
if (imageType[ctr].toUpperCase().indexOf("MOSAIC") !== -1) {
|
|
labeledAsMosaic = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!labeledAsMosaic) {
|
|
return false;
|
|
}
|
|
|
|
const [matHeight, matWidth] = this.getAcquisitionMatrix();
|
|
const canReadAsMosaic = matHeight > 0 && (matHeight < this.rows || matWidth < this.columns);
|
|
return canReadAsMosaic;
|
|
}
|
|
/**
|
|
* Returns true if this image uses palette colors.
|
|
* @returns {boolean}
|
|
*/
|
|
|
|
|
|
isPalette() {
|
|
const value = this.getTagValueIndexed(TagIds.PhotometricInterpretation);
|
|
return value != null && value.toLowerCase().indexOf("palette") !== -1;
|
|
}
|
|
|
|
get mosaicCols() {
|
|
return this.columns / this.getAcquisitionMatrix()[1];
|
|
}
|
|
|
|
get mosaicRows() {
|
|
return this.rows / this.getAcquisitionMatrix()[0];
|
|
}
|
|
|
|
isElscint() {
|
|
const tag = this.getTag(TagIds.DataScaleElscint);
|
|
return tag !== undefined;
|
|
}
|
|
/**
|
|
* Returns true if this image stores compressed data.
|
|
* @returns {boolean}
|
|
*/
|
|
|
|
|
|
isCompressed() {
|
|
const {
|
|
transferSyntax
|
|
} = this;
|
|
|
|
if (transferSyntax) {
|
|
if (transferSyntax.indexOf(TransferSyntax.CompressionJpeg) !== -1) {
|
|
return true;
|
|
}
|
|
|
|
if (transferSyntax.indexOf(TransferSyntax.CompressionRLE) !== -1) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
/**
|
|
* Returns true if this image stores JPEG data.
|
|
* @returns {boolean}
|
|
*/
|
|
|
|
|
|
isCompressedJPEG() {
|
|
const {
|
|
transferSyntax
|
|
} = this;
|
|
|
|
if (transferSyntax) {
|
|
if (transferSyntax.indexOf(TransferSyntax.CompressionJpeg) !== -1) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
/**
|
|
* Returns true of this image stores lossless JPEG data.
|
|
* @returns {boolean}
|
|
*/
|
|
|
|
|
|
isCompressedJPEGLossless() {
|
|
const {
|
|
transferSyntax
|
|
} = this;
|
|
|
|
if (transferSyntax) {
|
|
if (transferSyntax.indexOf(TransferSyntax.CompressionJpegLossless) !== -1 || transferSyntax.indexOf(TransferSyntax.CompressionJpegLosslessSel1) !== -1) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
/**
|
|
* Returns true if this image stores baseline JPEG data.
|
|
* @returns {boolean}
|
|
*/
|
|
|
|
|
|
isCompressedJPEGBaseline() {
|
|
const {
|
|
transferSyntax
|
|
} = this;
|
|
|
|
if (transferSyntax) {
|
|
if (transferSyntax.indexOf(TransferSyntax.CompressionJpegBaseline8bit) !== -1 || transferSyntax.indexOf(TransferSyntax.CompressionJpegBaseline12bit) !== -1) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
/**
|
|
* Returns true if this image stores JPEG2000 data.
|
|
* @returns {boolean}
|
|
*/
|
|
|
|
|
|
isCompressedJPEG2000() {
|
|
const {
|
|
transferSyntax
|
|
} = this;
|
|
|
|
if (transferSyntax) {
|
|
if (transferSyntax.indexOf(TransferSyntax.CompressionJpeg2000) !== -1 || transferSyntax.indexOf(TransferSyntax.CompressionJpeg2000Lossless) !== -1) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
/**
|
|
* Returns true if this image stores JPEG-LS data.
|
|
* @returns {boolean}
|
|
*/
|
|
|
|
|
|
isCompressedJPEGLS() {
|
|
const {
|
|
transferSyntax
|
|
} = this;
|
|
|
|
if (transferSyntax) {
|
|
if (transferSyntax.indexOf(TransferSyntax.CompressionJpegLs) !== -1 || transferSyntax.indexOf(TransferSyntax.CompressionJpegLsLossless) !== -1) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
/**
|
|
* Returns true if this image stores RLE data.
|
|
* @returns {boolean}
|
|
*/
|
|
|
|
|
|
isCompressedRLE() {
|
|
const {
|
|
transferSyntax
|
|
} = this;
|
|
|
|
if (transferSyntax) {
|
|
if (transferSyntax.indexOf(TransferSyntax.CompressionRLE) !== -1) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
/**
|
|
* Returns the number of frames.
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
get numberOfFrames() {
|
|
const value = this.getTagValueIndexed(TagIds.NumberOfFrames);
|
|
return value !== null && value !== void 0 ? value : 1;
|
|
}
|
|
/**
|
|
* Returns the number of samples per pixel.
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
get samplesPerPixel() {
|
|
const value = this.getTagValueIndexed(TagIds.SamplesPerPixel);
|
|
return value !== null && value !== void 0 ? value : 1;
|
|
}
|
|
|
|
getNumberOfImplicitFrames() {
|
|
if (this.isCompressed()) {
|
|
return 1;
|
|
}
|
|
|
|
const {
|
|
pixelData
|
|
} = this;
|
|
const length = pixelData.offsetEnd - pixelData.offsetValue;
|
|
const size = this.columns * this.rows * Math.round(this.bitsAllocated / 8);
|
|
return Math.floor(length / size);
|
|
}
|
|
/**
|
|
* Returns the pixel representation.
|
|
* @returns {PixelRepresentation}
|
|
*/
|
|
|
|
|
|
get pixelRepresentation() {
|
|
return this.getTagValueIndexed(TagIds.PixelRepresentation);
|
|
}
|
|
/**
|
|
* Returns the pixel padding value
|
|
* @returns {PixelPaddingValue}
|
|
*/
|
|
|
|
|
|
get pixelPaddingValue() {
|
|
return this.getTagValueIndexed(TagIds.PixelPaddingValue);
|
|
}
|
|
/**
|
|
* Returns the photometric interpretation.
|
|
* @returns {string}
|
|
*/
|
|
|
|
|
|
get photometricInterpretation() {
|
|
return this.getTagValueIndexed(TagIds.PhotometricInterpretation);
|
|
}
|
|
/**
|
|
* Returns the patient name.
|
|
* @returns {string}
|
|
*/
|
|
|
|
|
|
get patientName() {
|
|
return this.getTagValueIndexed(TagIds.PatientName);
|
|
}
|
|
/**
|
|
* Returns the patient ID.
|
|
* @returns {string}
|
|
*/
|
|
|
|
|
|
get patientID() {
|
|
return this.getTagValueIndexed(TagIds.PatientId);
|
|
}
|
|
/**
|
|
* Returns the study time.
|
|
* @returns {string}
|
|
*/
|
|
|
|
|
|
get studyTime() {
|
|
return this.getTagValueIndexed(TagIds.StudyTime);
|
|
}
|
|
/**
|
|
* Returns the transfer syntax.
|
|
* @returns {string}
|
|
*/
|
|
|
|
|
|
get transferSyntax() {
|
|
return this.getTagValueIndexed(TagIds.TransferSyntax);
|
|
}
|
|
/**
|
|
* Sets the tranfer syntax - so we can override it
|
|
*/
|
|
|
|
|
|
set transferSyntax(syntax) {
|
|
this.getTag(TagIds.TransferSyntax).convertedValue = syntax;
|
|
}
|
|
/**
|
|
* Returns the study date.
|
|
* @returns {string}
|
|
*/
|
|
|
|
|
|
get studyDate() {
|
|
return this.getTagValueIndexed(TagIds.StudyDate);
|
|
}
|
|
/**
|
|
* Returns the planar configuration.
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
get planarConfig() {
|
|
return this.getTagValueIndexed(TagIds.PlanarConfig);
|
|
}
|
|
/**
|
|
* Returns all descriptive info for this image.
|
|
* @returns {string}
|
|
*/
|
|
|
|
|
|
get imageDescription() {
|
|
const values = [this.getTagValueIndexed(TagIds.StudyDes), this.getTagValueIndexed(TagIds.SeriesDescription), this.getTagValueIndexed(TagIds.ImageComments)].filter(el => el !== null);
|
|
return values.join(" ").trim();
|
|
}
|
|
/**
|
|
* Returns the datatype (e.g., ByteType.integerUnsigned).
|
|
* @returns {number}
|
|
*/
|
|
|
|
|
|
get dataType() {
|
|
var _this$photometricInte;
|
|
|
|
const dataType = this.pixelRepresentation;
|
|
|
|
if (dataType === null) {
|
|
return ByteType.Unkown;
|
|
}
|
|
|
|
const interp = ((_this$photometricInte = this.photometricInterpretation) === null || _this$photometricInte === void 0 ? void 0 : _this$photometricInte.trim()) || null;
|
|
|
|
if (interp && (interp.indexOf("RGB") !== -1 || interp.indexOf("YBR") !== -1 || interp.toLowerCase().indexOf("palette") !== -1)) {
|
|
return ByteType.Rgb;
|
|
}
|
|
|
|
if (dataType === 0) {
|
|
return ByteType.IntegerUnsigned;
|
|
}
|
|
|
|
if (dataType === 1) {
|
|
return ByteType.Integer;
|
|
}
|
|
|
|
return ByteType.Unkown;
|
|
} // originally from: http://public.kitware.com/pipermail/insight-users/2005-March/012246.html
|
|
|
|
|
|
get acquiredSliceDirection() {
|
|
const dirCos = this.getTagValue(TagIds.ImageOrientation);
|
|
|
|
if ((dirCos === null || dirCos === void 0 ? void 0 : dirCos.length) !== 6) {
|
|
return SliceDirection.Unknown;
|
|
}
|
|
|
|
const rowAxis = getMajorAxisFromPatientRelativeDirectionCosine(dirCos[0], dirCos[1], dirCos[2]);
|
|
const colAxis = getMajorAxisFromPatientRelativeDirectionCosine(dirCos[3], dirCos[4], dirCos[5]);
|
|
|
|
if (rowAxis !== null && colAxis !== null) {
|
|
if ((rowAxis === "R" || rowAxis === "L") && (colAxis === "A" || colAxis === "P")) {
|
|
return SliceDirection.Axial;
|
|
}
|
|
|
|
if ((colAxis === "R" || colAxis === "L") && (rowAxis === "A" || rowAxis === "P")) {
|
|
return SliceDirection.Axial;
|
|
}
|
|
|
|
if ((rowAxis === "R" || rowAxis === "L") && (colAxis === "H" || colAxis === "F")) {
|
|
return SliceDirection.Coronal;
|
|
}
|
|
|
|
if ((colAxis === "R" || colAxis === "L") && (rowAxis === "H" || rowAxis === "F")) {
|
|
return SliceDirection.Coronal;
|
|
}
|
|
|
|
if ((rowAxis === "A" || rowAxis === "P") && (colAxis === "H" || colAxis === "F")) {
|
|
return SliceDirection.Sagittal;
|
|
}
|
|
|
|
if ((colAxis === "A" || colAxis === "P") && (rowAxis === "H" || rowAxis === "F")) {
|
|
return SliceDirection.Sagittal;
|
|
}
|
|
}
|
|
|
|
return SliceDirection.Oblique;
|
|
}
|
|
/**
|
|
* Returns a string of interpreted private data.
|
|
* @returns {string}
|
|
*/
|
|
|
|
|
|
get allInterpretedPrivateData() {
|
|
let str = "";
|
|
const sortedKeys = Object.keys(this.tags).sort();
|
|
|
|
for (let ctr = 0; ctr < sortedKeys.length; ctr += 1) {
|
|
const key = sortedKeys[ctr]; // eslint-disable-next-line no-prototype-builtins
|
|
|
|
if (this.tags.hasOwnProperty(key)) {
|
|
const tag = this.tags[key];
|
|
|
|
if (tag.hasInterpretedPrivateData()) {
|
|
str += tag.value;
|
|
}
|
|
}
|
|
}
|
|
|
|
return str;
|
|
}
|
|
/**
|
|
* Returns a string representation of this image.
|
|
* @returns {string}
|
|
*/
|
|
|
|
|
|
toString() {
|
|
let str = "";
|
|
const sortedKeys = Object.keys(this.tags).sort();
|
|
|
|
for (let ctr = 0; ctr < sortedKeys.length; ctr += 1) {
|
|
const key = sortedKeys[ctr];
|
|
const tag = this.tags[key];
|
|
|
|
if (tag) {
|
|
str += `${tag.toHTMLString()}<br />`;
|
|
}
|
|
}
|
|
|
|
str = str.replace(/\n\s*\n/g, "\n"); // replace mutli-newlines with single newline
|
|
|
|
str = str.replace(/(?:\r\n|\r|\n)/g, "<br />"); // replace newlines with <br>
|
|
|
|
return str;
|
|
}
|
|
|
|
}
|
|
|
|
DCMImage.skipPaletteConversion = false;
|
|
/* eslint no-use-before-define: ["error", { "classes": false }] */
|
|
|
|
const MAGIC_COOKIE_OFFSET = 128;
|
|
const MAGIC_COOKIE = [68, 73, 67, 77];
|
|
/**
|
|
* Returns true if the DICOM magic cookie is found.
|
|
* @param {DataView} data
|
|
* @returns {boolean}
|
|
*/
|
|
|
|
const isMagicCookieFound = data => {
|
|
const offset = MAGIC_COOKIE_OFFSET;
|
|
const magicCookieLength = MAGIC_COOKIE.length;
|
|
|
|
for (let ctr = 0; ctr < magicCookieLength; ctr += 1) {
|
|
if (data.getUint8(offset + ctr) !== MAGIC_COOKIE[ctr]) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
const findFirstTagOffset = data => {
|
|
const magicCookieLength = MAGIC_COOKIE.length;
|
|
|
|
if (isMagicCookieFound(data)) {
|
|
return MAGIC_COOKIE_OFFSET + magicCookieLength;
|
|
}
|
|
|
|
const searchOffsetMax = MAGIC_COOKIE_OFFSET * 5;
|
|
let found = false;
|
|
let offset = 0;
|
|
|
|
for (let ctr = 0; ctr < searchOffsetMax; ctr += 1) {
|
|
const ch = data.getUint8(ctr);
|
|
|
|
if (ch === MAGIC_COOKIE[0]) {
|
|
found = true;
|
|
|
|
for (let ctrIn = 1; ctrIn < magicCookieLength; ctrIn += 1) {
|
|
if (data.getUint8(ctr + ctrIn) !== MAGIC_COOKIE[ctrIn]) {
|
|
found = false;
|
|
}
|
|
}
|
|
|
|
if (found) {
|
|
offset = ctr + magicCookieLength;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return offset;
|
|
};
|
|
/**
|
|
* Parser class
|
|
*/
|
|
|
|
|
|
class Parser {
|
|
constructor() {
|
|
this.littleEndian = true;
|
|
this.explicit = true;
|
|
this.metaExplicit = true;
|
|
this.metaFound = false;
|
|
this.metaFinished = false;
|
|
this.metaFinishedOffset = -1;
|
|
this.needsDeflate = false;
|
|
this.inflated = null;
|
|
this.encapsulation = false;
|
|
this.level = 0;
|
|
this.error = null;
|
|
this.charset = null;
|
|
}
|
|
/**
|
|
* Parses this data and returns an image object.
|
|
* @param {DataView} data
|
|
* @returns {Image|null}
|
|
*/
|
|
|
|
|
|
parse(dataIn) {
|
|
let image = null;
|
|
let data = dataIn;
|
|
|
|
try {
|
|
image = new DCMImage();
|
|
const offset = findFirstTagOffset(data);
|
|
let tag = this.getNextTag(data, offset);
|
|
|
|
while (tag !== null) {
|
|
if (Parser.verbose) {
|
|
console.log(tag.toString());
|
|
}
|
|
|
|
image.putTag(tag);
|
|
|
|
if (tag.is(TagIds.PixelData)) {
|
|
break;
|
|
}
|
|
|
|
if (this.needsDeflate && tag.offsetEnd >= this.metaFinishedOffset) {
|
|
this.needsDeflate = false;
|
|
const copyMeta = data.buffer.slice(0, tag.offsetEnd);
|
|
const copyDeflated = data.buffer.slice(tag.offsetEnd);
|
|
this.inflated = concatArrayBuffers(copyMeta, _pako.default.inflateRaw(copyDeflated));
|
|
data = new DataView(this.inflated);
|
|
}
|
|
|
|
tag = this.getNextTag(data, tag.offsetEnd);
|
|
}
|
|
} catch (err) {
|
|
this.error = err;
|
|
}
|
|
|
|
if (image !== null) {
|
|
// set cached tags
|
|
image.littleEndian = this.littleEndian;
|
|
}
|
|
|
|
return image;
|
|
}
|
|
|
|
parseEncapsulated(data) {
|
|
this.encapsulation = true;
|
|
const tags = [];
|
|
|
|
try {
|
|
let tag = this.getNextTag(data, 0);
|
|
|
|
while (tag !== null) {
|
|
if (tag.is(TagIds.SublistItem)) {
|
|
tags.push(tag);
|
|
}
|
|
|
|
if (Parser.verbose) {
|
|
console.log(tag.toString());
|
|
}
|
|
|
|
tag = this.getNextTag(data, tag.offsetEnd);
|
|
}
|
|
} catch (err) {
|
|
this.error = err;
|
|
}
|
|
|
|
return tags;
|
|
}
|
|
|
|
testForValidTag(data) {
|
|
let tag = null;
|
|
|
|
try {
|
|
const offset = findFirstTagOffset(data);
|
|
tag = this.getNextTag(data, offset, false);
|
|
} catch (err) {
|
|
this.error = err;
|
|
}
|
|
|
|
return tag;
|
|
}
|
|
|
|
getNextTag(data, offsetStart, testForTag = false) {
|
|
let group = 0;
|
|
let value = null;
|
|
let offset = offsetStart;
|
|
let length = 0;
|
|
let little = true;
|
|
let vr = null;
|
|
|
|
if (offset >= data.byteLength) {
|
|
return null;
|
|
}
|
|
|
|
if (this.metaFinished) {
|
|
little = this.littleEndian;
|
|
group = data.getUint16(offset, little);
|
|
} else {
|
|
group = data.getUint16(offset, true);
|
|
|
|
if (this.metaFinishedOffset !== -1 && offset >= this.metaFinishedOffset || group !== 0x0002) {
|
|
this.metaFinished = true;
|
|
little = this.littleEndian;
|
|
group = data.getUint16(offset, little);
|
|
}
|
|
}
|
|
|
|
if (!this.metaFound && group === 0x0002) {
|
|
this.metaFound = true;
|
|
}
|
|
|
|
offset += 2;
|
|
const element = data.getUint16(offset, little);
|
|
offset += 2;
|
|
let useImplicitVR = true;
|
|
|
|
if (this.explicit && this.metaFinished || this.metaExplicit && !this.metaFinished) {
|
|
vr = String.fromCharCode(data.getUint8(offset)) + String.fromCharCode(data.getUint8(offset + 1));
|
|
const foundVR = Parser.VRS.indexOf(vr) !== -1;
|
|
|
|
if (!foundVR && (!this.metaFound && this.metaFinished || this.metaFound && !this.metaFinished)) {
|
|
if (this.metaFinished) {
|
|
this.explicit = false;
|
|
} else {
|
|
this.metaExplicit = false;
|
|
}
|
|
} else {
|
|
offset += 2;
|
|
|
|
if (Parser.DATA_VRS.indexOf(vr) !== -1) {
|
|
offset += 2; // skip two empty bytes
|
|
|
|
length = data.getUint32(offset, little);
|
|
offset += 4;
|
|
} else {
|
|
length = data.getUint16(offset, little);
|
|
offset += 2;
|
|
}
|
|
|
|
useImplicitVR = false;
|
|
}
|
|
}
|
|
|
|
if (useImplicitVR) {
|
|
vr = Dictionary.getVR(group, element);
|
|
length = data.getUint32(offset, little);
|
|
|
|
if (length === Parser.UNDEFINED_LENGTH) {
|
|
vr = "SQ";
|
|
}
|
|
|
|
offset += 4;
|
|
}
|
|
|
|
const offsetValue = offset;
|
|
const isPixelData = Tag.isEqual({
|
|
group,
|
|
element
|
|
}, TagIds.PixelData);
|
|
|
|
if (vr === "SQ" || this.level > 0 && Parser.DATA_VRS.indexOf(vr) !== -1) {
|
|
value = this.parseSublist(data, offset, length, vr !== "SQ");
|
|
|
|
if (length === Parser.UNDEFINED_LENGTH) {
|
|
const _tag = value[value.length - 1];
|
|
length = _tag.offsetEnd - offset;
|
|
}
|
|
} else if (length > 0 && !testForTag) {
|
|
if (length === Parser.UNDEFINED_LENGTH) {
|
|
if (isPixelData) {
|
|
length = data.byteLength - offset;
|
|
}
|
|
}
|
|
|
|
if (length > 0) {
|
|
value = new DataView(data.buffer, data.byteOffset + offset, length);
|
|
}
|
|
}
|
|
|
|
offset += length;
|
|
const tag = new Tag({
|
|
group,
|
|
element,
|
|
vr,
|
|
value,
|
|
offsetStart,
|
|
offsetValue,
|
|
offsetEnd: offset,
|
|
littleEndian: little,
|
|
charset: this.charset
|
|
});
|
|
|
|
if (tag.hasValue()) {
|
|
if (tag.is(TagIds.TransferSyntax)) {
|
|
const [val] = tag.value;
|
|
this.explicit = true;
|
|
this.littleEndian = true;
|
|
|
|
if (val === TransferSyntax.ImplicitLittle) {
|
|
this.explicit = false;
|
|
} else if (val === TransferSyntax.ExplicitBig) {
|
|
this.littleEndian = false;
|
|
} else if (val === TransferSyntax.CompressionDeflate) {
|
|
this.needsDeflate = true;
|
|
}
|
|
} else if (tag.is(TagIds.MetaLength)) {
|
|
const [val] = tag.value;
|
|
this.metaFinishedOffset = val + offset;
|
|
} else if (tag.is(TagIds.Charset)) {
|
|
const charsetValue = tag.value;
|
|
let charset = DefaultCharset;
|
|
|
|
if (charsetValue.length === 2) {
|
|
charset = `${charsetValue[0] || DefaultCharset}\\${charsetValue[1]}`;
|
|
} else if (charsetValue.length === 1) {
|
|
[charset] = charsetValue;
|
|
}
|
|
|
|
this.charset = charset;
|
|
}
|
|
}
|
|
|
|
return tag;
|
|
}
|
|
|
|
parseSublist(data, offsetStart, length, raw) {
|
|
const tags = [];
|
|
let offset = offsetStart;
|
|
const offsetEnd = offsetStart + length;
|
|
this.level += 1;
|
|
|
|
if (length === Parser.UNDEFINED_LENGTH) {
|
|
let sublistItem = this.parseSublistItem(data, offset, raw);
|
|
|
|
while (!sublistItem.is(TagIds.SequenceDelim)) {
|
|
tags.push(sublistItem);
|
|
offset = sublistItem.offsetEnd || 0;
|
|
sublistItem = this.parseSublistItem(data, offset, raw);
|
|
}
|
|
|
|
tags.push(sublistItem);
|
|
} else {
|
|
while (offset < offsetEnd) {
|
|
const sublistItem = this.parseSublistItem(data, offset, raw);
|
|
tags.push(sublistItem);
|
|
offset = sublistItem.offsetEnd || 0;
|
|
}
|
|
}
|
|
|
|
this.level -= 1;
|
|
return tags;
|
|
}
|
|
|
|
parseSublistItem(data, offsetStart, raw) {
|
|
var _value;
|
|
|
|
let offset = offsetStart;
|
|
let value = null;
|
|
const tags = [];
|
|
const group = data.getUint16(offset, this.littleEndian);
|
|
offset += 2;
|
|
const element = data.getUint16(offset, this.littleEndian);
|
|
offset += 2;
|
|
let length = data.getUint32(offset, this.littleEndian);
|
|
offset += 4;
|
|
const offsetValue = offset;
|
|
|
|
if (length === Parser.UNDEFINED_LENGTH) {
|
|
let tag = this.getNextTag(data, offset);
|
|
|
|
while (!tag.is(TagIds.SublistItemDelim)) {
|
|
tags.push(tag);
|
|
offset = tag.offsetEnd;
|
|
tag = this.getNextTag(data, offset);
|
|
}
|
|
|
|
tags.push(tag);
|
|
offset = tag.offsetEnd;
|
|
} else if (raw) {
|
|
length = Math.min(data.byteLength - offset - data.byteOffset, length);
|
|
value = new DataView(data.buffer, offset + data.byteOffset, length);
|
|
offset += length;
|
|
} else {
|
|
const offsetEnd = offset + length;
|
|
let tag;
|
|
|
|
while (offset < offsetEnd) {
|
|
tag = this.getNextTag(data, offset);
|
|
tags.push(tag);
|
|
offset = tag.offsetEnd;
|
|
}
|
|
}
|
|
|
|
const sublistItemTag = new Tag({
|
|
group,
|
|
element,
|
|
value: (_value = value) !== null && _value !== void 0 ? _value : tags,
|
|
offsetStart,
|
|
offsetValue,
|
|
offsetEnd: offset,
|
|
littleEndian: this.littleEndian
|
|
});
|
|
return sublistItemTag;
|
|
}
|
|
|
|
hasError() {
|
|
return this.error !== null;
|
|
}
|
|
|
|
}
|
|
|
|
exports.Parser = Parser;
|
|
Parser.verbose = false;
|
|
Parser.VRS = ["AE", "AS", "AT", "CS", "DA", "DS", "DT", "FL", "FD", "IS", "LO", "LT", "OB", "OD", "OF", "OW", "PN", "SH", "SL", "SS", "ST", "TM", "UI", "UL", "UN", "US", "UT"];
|
|
Parser.DATA_VRS = ["OB", "OW", "OF", "SQ", "UT", "UN"];
|
|
Parser.RAW_DATA_VRS = ["OB", "OD", "OF", "OW", "UN"];
|
|
Parser.UNDEFINED_LENGTH = 0xFFFFFFFF;
|
|
|
|
class OrderedMapIterator {
|
|
constructor(orderedMap) {
|
|
this.orderedMap = void 0;
|
|
this.index = void 0;
|
|
this.orderedMap = orderedMap;
|
|
this.index = 0;
|
|
}
|
|
|
|
hasNext() {
|
|
return this.index < this.orderedMap.orderedKeys.length;
|
|
}
|
|
|
|
next() {
|
|
const item = this.orderedMap.get(this.orderedMap.orderedKeys[this.index]);
|
|
this.index += 1;
|
|
return item;
|
|
}
|
|
|
|
length() {
|
|
return this.orderedMap.orderedKeys.length;
|
|
}
|
|
|
|
} // Based on: http://stackoverflow.com/questions/3549894/javascript-data-structure-for-fast-lookup-and-ordered-looping
|
|
|
|
|
|
class OrderedMap {
|
|
constructor() {
|
|
this.orderedKeys = [];
|
|
this.map = new Map();
|
|
this.index = 0;
|
|
} // bit of a hack - necessary for Series
|
|
|
|
|
|
put(key, value) {
|
|
if (!this.map.get(key)) {
|
|
// insert new key and value
|
|
this.orderedKeys.push(key);
|
|
this.orderedKeys.sort((a, b) => parseFloat(a) - parseFloat(b));
|
|
}
|
|
|
|
this.map.set(key, value);
|
|
}
|
|
|
|
remove(key) {
|
|
const index = this.orderedKeys.indexOf(key);
|
|
|
|
if (index === -1) {
|
|
throw new Error("key does not exist");
|
|
}
|
|
|
|
this.orderedKeys.splice(index, 1);
|
|
this.map.delete(key);
|
|
}
|
|
|
|
get(key) {
|
|
return this.map.get(key);
|
|
}
|
|
|
|
iterator() {
|
|
return new OrderedMapIterator(this);
|
|
}
|
|
|
|
getOrderedValues() {
|
|
const it = this.iterator();
|
|
const orderedValues = Array(it.length());
|
|
|
|
while (it.hasNext()) {
|
|
orderedValues.push(it.next());
|
|
}
|
|
|
|
return orderedValues;
|
|
}
|
|
|
|
}
|
|
|
|
const getMosaicOffset = (mosaicCols, mosaicColWidth, mosaicRowHeight, mosaicWidth, xLocVal, yLocVal, zLocVal) => {
|
|
let xLoc = xLocVal;
|
|
let yLoc = yLocVal;
|
|
const zLoc = zLocVal;
|
|
xLoc = zLoc % mosaicCols * mosaicColWidth + xLoc;
|
|
yLoc = (Math.floor(zLoc / mosaicCols) * mosaicRowHeight + yLoc) * mosaicWidth;
|
|
return xLoc + yLoc;
|
|
};
|
|
|
|
const orderByImagePosition = (images, sliceDir) => {
|
|
const dicomMap = new OrderedMap();
|
|
|
|
for (let ctr = 0; ctr < images.length; ctr += 1) {
|
|
dicomMap.put(images[ctr].getImagePositionSliceDir(sliceDir), images[ctr]);
|
|
}
|
|
|
|
return dicomMap.getOrderedValues();
|
|
};
|
|
|
|
const orderBySliceLocation = images => {
|
|
const dicomMap = new OrderedMap();
|
|
|
|
for (let ctr = 0; ctr < images.length; ctr += 1) {
|
|
dicomMap.put(images[ctr].sliceLocation, images[ctr]);
|
|
}
|
|
|
|
return dicomMap.getOrderedValues();
|
|
};
|
|
|
|
const orderByImageNumber = images => {
|
|
const dicomMap = new OrderedMap();
|
|
|
|
for (let ctr = 0; ctr < images.length; ctr += 1) {
|
|
dicomMap.put(images[ctr].imageNumber, images[ctr]);
|
|
}
|
|
|
|
return dicomMap.getOrderedValues();
|
|
};
|
|
|
|
const hasMatchingSlice = (dg, image, sliceDir, doImagePos, doSliceLoc) => {
|
|
let matchingNum = 0;
|
|
|
|
if (doImagePos) {
|
|
matchingNum = image.getImagePositionSliceDir(sliceDir);
|
|
} else if (doSliceLoc) {
|
|
matchingNum = image.sliceLocation;
|
|
} else {
|
|
matchingNum = image.imageNumber;
|
|
}
|
|
|
|
for (let ctr = 0; ctr < dg.length; ctr += 1) {
|
|
const current = dg[ctr];
|
|
|
|
if (doImagePos) {
|
|
const imagePos = current.getImagePositionSliceDir(sliceDir);
|
|
|
|
if (imagePos === matchingNum) {
|
|
return true;
|
|
}
|
|
} else if (doSliceLoc) {
|
|
const sliceLoc = current.sliceLocation;
|
|
|
|
if (sliceLoc === matchingNum) {
|
|
return true;
|
|
}
|
|
} else {
|
|
const imageNum = current.imageNumber;
|
|
|
|
if (imageNum === matchingNum) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
const orderByTime = (images, numFrames, sliceDir, hasImagePosition, hasSliceLocation) => {
|
|
const dicomMap = new OrderedMap();
|
|
const hasTemporalPosition = numFrames > 1 && images[0].temporalPosition !== null;
|
|
const hasTemporalNumber = numFrames > 1 && images[0].temporalNumber !== null && images[0].temporalNumber === numFrames;
|
|
|
|
if (hasTemporalPosition && hasTemporalNumber) {
|
|
// explicit series
|
|
for (let ctr = 0; ctr < images.length; ctr += 1) {
|
|
const image = images[ctr];
|
|
const tempPos = image.temporalPosition;
|
|
let dg = dicomMap.get(tempPos);
|
|
|
|
if (!dg) {
|
|
dg = [];
|
|
dicomMap.put(tempPos, dg);
|
|
}
|
|
|
|
dg.push(image);
|
|
}
|
|
} else {
|
|
// implicit series
|
|
// order data by slice then time
|
|
const timeBySliceMap = new OrderedMap();
|
|
|
|
for (let ctr = 0; ctr < images.length; ctr += 1) {
|
|
if (images[ctr] !== null) {
|
|
let sliceMarker = ctr;
|
|
|
|
if (hasImagePosition) {
|
|
sliceMarker = images[ctr].getImagePositionSliceDir(sliceDir);
|
|
} else if (hasSliceLocation) {
|
|
sliceMarker = images[ctr].sliceLocation;
|
|
}
|
|
|
|
let slice = timeBySliceMap.get(sliceMarker);
|
|
|
|
if (slice === null) {
|
|
slice = new OrderedMap();
|
|
timeBySliceMap.put(sliceMarker, slice);
|
|
}
|
|
|
|
slice.put(ctr, images[ctr]);
|
|
}
|
|
} // copy into DICOM array (ordered by slice by time)
|
|
|
|
|
|
const dicomsCopy = [];
|
|
let dicomsCopyIndex = 0;
|
|
const sliceIt = timeBySliceMap.iterator();
|
|
|
|
while (sliceIt.hasNext()) {
|
|
const slice = sliceIt.next();
|
|
const timeIt = slice.iterator();
|
|
|
|
while (timeIt.hasNext()) {
|
|
dicomsCopy[dicomsCopyIndex] = timeIt.next();
|
|
dicomsCopyIndex += 1;
|
|
}
|
|
} // groups dicoms by timepoint
|
|
|
|
|
|
for (let ctr = 0; ctr < dicomsCopy.length; ctr += 1) {
|
|
if (dicomsCopy[ctr] !== null) {
|
|
let dgFound;
|
|
const it = dicomMap.iterator();
|
|
|
|
while (it.hasNext()) {
|
|
const dg = it.next();
|
|
|
|
if (!hasMatchingSlice(dg, dicomsCopy[ctr], sliceDir, hasImagePosition, hasSliceLocation)) {
|
|
dgFound = dg;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (dgFound === null) {
|
|
dgFound = [];
|
|
dicomMap.put(dicomMap.orderedKeys.length, dgFound);
|
|
}
|
|
|
|
dgFound.push(dicomsCopy[ctr]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return dicomMap;
|
|
};
|
|
|
|
const orderDicoms = (images, numFrames, sliceDir) => {
|
|
const hasImagePosition = images[0].imagePosition !== null;
|
|
const hasSliceLocation = images[0].sliceLocation !== null;
|
|
const hasImageNumber = images[0].imageNumber !== null;
|
|
const timeMap = orderByTime(images, numFrames, sliceDir, hasImagePosition, hasSliceLocation);
|
|
const timeIt = timeMap.orderedKeys;
|
|
const imagesOrderedByTimeAndSpace = [];
|
|
|
|
for (let ctr = 0; ctr < timeIt.length; ctr += 1) {
|
|
const dg = timeMap.get(timeIt[ctr]);
|
|
let ordered;
|
|
|
|
if (hasImagePosition) {
|
|
ordered = orderByImagePosition(dg, sliceDir);
|
|
} else if (hasSliceLocation) {
|
|
ordered = orderBySliceLocation(dg);
|
|
} else if (hasImageNumber) {
|
|
ordered = orderByImageNumber(dg);
|
|
} else {
|
|
ordered = dg;
|
|
}
|
|
|
|
for (let ctrIn = 0; ctrIn < ordered.length; ctrIn += 1) {
|
|
imagesOrderedByTimeAndSpace.push(ordered[ctrIn]);
|
|
}
|
|
}
|
|
|
|
for (let ctrIn = 0; ctrIn < imagesOrderedByTimeAndSpace.length; ctrIn += 1) {
|
|
imagesOrderedByTimeAndSpace[ctrIn].index = ctrIn;
|
|
}
|
|
|
|
return imagesOrderedByTimeAndSpace;
|
|
};
|
|
/**
|
|
* The Series constructor.
|
|
* @property {DCMImage[]} images
|
|
* @type {Function}
|
|
*/
|
|
|
|
|
|
class Series {
|
|
constructor() {
|
|
this.images = [];
|
|
this.imagesOriginalOrder = null;
|
|
this.isMosaic = false;
|
|
this.isElscint = false;
|
|
this.isCompressed = false;
|
|
this.numberOfFrames = 0;
|
|
this.numberOfFramesInFile = 0;
|
|
this.isMultiFrame = false;
|
|
this.isMultiFrameVolume = false;
|
|
this.isMultiFrameTimeseries = false;
|
|
this.isImplicitTimeseries = false;
|
|
this.sliceSense = false;
|
|
this.sliceDir = SliceDirection.Unknown;
|
|
this.error = null;
|
|
}
|
|
|
|
getOrder() {
|
|
const order = [];
|
|
|
|
for (let ctr = 0; ctr < this.imagesOriginalOrder.length; ctr += 1) {
|
|
order[ctr] = this.imagesOriginalOrder[ctr].index;
|
|
}
|
|
|
|
return order;
|
|
}
|
|
/**
|
|
* Returns the series ID.
|
|
* @returns {string}
|
|
*/
|
|
|
|
|
|
toString() {
|
|
return this.images[0].seriesId;
|
|
}
|
|
/**
|
|
* Returns a nice name for the series.
|
|
* @returns {string|null}
|
|
*/
|
|
|
|
|
|
getName() {
|
|
const des = this.images[0].seriesDescription;
|
|
const uid = this.images[0].seriesInstanceUID;
|
|
|
|
if (des !== null) {
|
|
return des;
|
|
}
|
|
|
|
if (uid !== null) {
|
|
return uid;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
/**
|
|
* Adds an image to the series.
|
|
* @param {DCMImage} image
|
|
*/
|
|
|
|
|
|
addImage(image) {
|
|
this.images.push(image);
|
|
}
|
|
/**
|
|
* Returns true if the specified image is part of the series
|
|
* (or if no images are yet part of the series).
|
|
* @param {DCMImage} image
|
|
* @returns {boolean}
|
|
*/
|
|
|
|
|
|
matchesSeries(image) {
|
|
if (this.images.length === 0) {
|
|
return true;
|
|
}
|
|
|
|
return this.images[0].seriesId === image.seriesId;
|
|
}
|
|
/**
|
|
* Orders and organizes the images in this series.
|
|
*/
|
|
|
|
|
|
buildSeries() {
|
|
const [image0] = this.images;
|
|
this.isMosaic = image0.isMosaic();
|
|
this.isElscint = image0.isElscint();
|
|
this.isCompressed = image0.isCompressed(); // check for multi-frame
|
|
|
|
this.numberOfFrames = image0.numberOfFrames;
|
|
this.numberOfFramesInFile = image0.getNumberOfImplicitFrames();
|
|
this.isMultiFrame = this.numberOfFrames > 1 || this.isMosaic && image0.mosaicCols * image0.mosaicRows > 1;
|
|
this.isMultiFrameVolume = false;
|
|
this.isMultiFrameTimeseries = false;
|
|
this.isImplicitTimeseries = false;
|
|
|
|
if (this.isMultiFrame) {
|
|
const hasFrameTime = image0.getFrameTime() > 0;
|
|
|
|
if (this.isMosaic) {
|
|
this.isMultiFrameTimeseries = true;
|
|
} else if (hasFrameTime) {
|
|
this.isMultiFrameTimeseries = true;
|
|
} else if (this.numberOfFramesInFile > 1) {
|
|
this.isMultiFrameTimeseries = true;
|
|
this.numberOfFrames = this.images.length;
|
|
} else {
|
|
this.isMultiFrameVolume = true;
|
|
}
|
|
}
|
|
|
|
if (!this.isMosaic && this.numberOfFrames <= 1) {
|
|
// check for implicit frame count
|
|
let imagePos = image0.imagePosition || [];
|
|
const sliceLoc = imagePos.toString();
|
|
this.numberOfFrames = 0;
|
|
|
|
for (let ctr = 0; ctr < this.images.length; ctr += 1) {
|
|
imagePos = this.images[ctr].imagePosition || [];
|
|
|
|
if (imagePos.toString() === sliceLoc) {
|
|
this.numberOfFrames += 1;
|
|
}
|
|
}
|
|
|
|
if (this.numberOfFrames > 1) {
|
|
this.isImplicitTimeseries = true;
|
|
}
|
|
}
|
|
|
|
this.sliceDir = image0.acquiredSliceDirection;
|
|
let orderedImages;
|
|
|
|
if (Series.useExplicitOrdering) {
|
|
orderedImages = this.images.slice();
|
|
} else {
|
|
orderedImages = orderDicoms(this.images, this.numberOfFrames, this.sliceDir);
|
|
}
|
|
|
|
const sliceLocationFirst = orderedImages[0].getImagePositionSliceDir(this.sliceDir);
|
|
const sliceLocationLast = orderedImages[orderedImages.length - 1].getImagePositionSliceDir(this.sliceDir);
|
|
const sliceLocDiff = sliceLocationLast - sliceLocationFirst;
|
|
|
|
if (Series.useExplicitOrdering) {
|
|
this.sliceSense = false;
|
|
} else if (this.isMosaic) {
|
|
this.sliceSense = true;
|
|
} else if (this.isMultiFrame) {
|
|
const sliceLocations = orderedImages[0].sliceLocationVector;
|
|
|
|
if (sliceLocations !== null) {
|
|
const {
|
|
orientation
|
|
} = orderedImages[0];
|
|
|
|
if ((orientation === null || orientation === void 0 ? void 0 : orientation.charAt(2)) === "Z") {
|
|
this.sliceSense = sliceLocations[0] - sliceLocations[sliceLocations.length - 1] < 0;
|
|
} else {
|
|
this.sliceSense = sliceLocations[0] - sliceLocations[sliceLocations.length - 1] > 0;
|
|
}
|
|
} else {
|
|
this.sliceSense = sliceLocationFirst >= 0;
|
|
}
|
|
}
|
|
/*
|
|
* "The direction of the axes is defined fully by the patient's orientation.
|
|
* The x-axis is increasing to the left hand side of the patient. The
|
|
* y-axis is increasing to the posterior side of the patient.
|
|
* The z-axis is increasing toward the head of the patient."
|
|
*/
|
|
else if (this.sliceDir === SliceDirection.Sagittal || this.sliceDir === SliceDirection.Coronal) {
|
|
if (sliceLocDiff > 0) {
|
|
this.sliceSense = false;
|
|
} else {
|
|
this.sliceSense = true;
|
|
}
|
|
} else if (sliceLocDiff > 0) {
|
|
this.sliceSense = true;
|
|
} else {
|
|
this.sliceSense = false;
|
|
}
|
|
|
|
this.imagesOriginalOrder = this.images;
|
|
this.images = orderedImages;
|
|
}
|
|
|
|
getMosaicData(image, data) {
|
|
const [image0] = this.images;
|
|
const mosaicWidth = image0.columns;
|
|
const mosaicHeight = image0.rows;
|
|
const {
|
|
mosaicRows,
|
|
mosaicCols
|
|
} = image0;
|
|
const numBytes = image0.bytesAllocated || 1;
|
|
const numSlices = mosaicWidth * mosaicHeight;
|
|
const numRows = Math.floor(mosaicHeight / mosaicRows);
|
|
const numCols = Math.floor(mosaicWidth / mosaicCols);
|
|
const mosaicRowHeight = Math.floor(mosaicHeight / mosaicRows);
|
|
const mosaicColWidth = Math.floor(mosaicWidth / mosaicCols);
|
|
const buffer = new Uint8Array(new ArrayBuffer(numSlices * numRows * numCols * numBytes));
|
|
const dataTyped = new Uint8Array(data);
|
|
let index = 0;
|
|
|
|
for (let ctrS = 0; ctrS < numSlices; ctrS += 1) {
|
|
for (let ctrR = 0; ctrR < numRows; ctrR += 1) {
|
|
for (let ctrC = 0; ctrC < numCols; ctrC += 1) {
|
|
const offset = getMosaicOffset(mosaicCols, mosaicColWidth, mosaicRowHeight, mosaicWidth, ctrC, ctrR, ctrS);
|
|
|
|
for (let ctr = 0; ctr < numBytes; ctr += 1) {
|
|
buffer[index] = dataTyped[offset * numBytes + ctr];
|
|
index += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return buffer.buffer;
|
|
}
|
|
|
|
}
|
|
/**
|
|
* Parses the DICOM header and return an image object.
|
|
* @param {DataView|ArrayBuffer} data
|
|
* @returns {DCMImage|null}
|
|
*/
|
|
|
|
|
|
Series.parserError = null;
|
|
Series.useExplicitOrdering = false;
|
|
Series.useExplicitSpacing = 0;
|
|
|
|
const parseImage = data => {
|
|
const parser = new Parser();
|
|
let dataView;
|
|
|
|
if (data instanceof DataView) {
|
|
dataView = data;
|
|
} else {
|
|
dataView = new DataView(data);
|
|
}
|
|
|
|
const image = parser.parse(dataView);
|
|
|
|
if (parser.hasError()) {
|
|
Series.parserError = parser.error;
|
|
return null;
|
|
}
|
|
|
|
if (parser.inflated) {
|
|
image.inflated = parser.inflated;
|
|
}
|
|
|
|
return image;
|
|
};
|
|
|
|
exports.parseImage = parseImage;
|
|
|
|
const getEncapsulatedData = data => {
|
|
const parser = new Parser();
|
|
return parser.parseEncapsulated(data);
|
|
};
|
|
|
|
const concatDataViews = dataViews => {
|
|
let length = 0;
|
|
let offset = 0;
|
|
|
|
for (let ctr = 0; ctr < dataViews.length; ctr += 1) {
|
|
length += dataViews[ctr].byteLength;
|
|
}
|
|
|
|
const tmp = new Uint8Array(length);
|
|
let dataView;
|
|
|
|
for (let ctr = 0; ctr < dataViews.length; ctr += 1) {
|
|
dataView = dataViews[ctr];
|
|
tmp.set(new Uint8Array(dataView.buffer, dataView.byteOffset, dataView.byteLength), offset);
|
|
offset += dataViews[ctr].byteLength;
|
|
}
|
|
|
|
return new DataView(tmp.buffer);
|
|
};
|
|
|
|
const JPEG_MAGIC_NUMBER = [0xFF, 0xD8];
|
|
const JPEG2000_MAGIC_NUMBER = [0xFF, 0x4F, 0xFF, 0x51];
|
|
|
|
const isHeaderJPEG = data => {
|
|
if (!data) {
|
|
return false;
|
|
}
|
|
|
|
if (data.getUint8(0) !== JPEG_MAGIC_NUMBER[0]) {
|
|
return false;
|
|
}
|
|
|
|
if (data.getUint8(1) !== JPEG_MAGIC_NUMBER[1]) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
const isHeaderJPEG2000 = data => {
|
|
if (!data) {
|
|
return false;
|
|
}
|
|
|
|
for (let ctr = 0; ctr < JPEG2000_MAGIC_NUMBER.length; ctr += 1) {
|
|
if (data.getUint8(ctr) !== JPEG2000_MAGIC_NUMBER[ctr]) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
};
|
|
|
|
const getJpegData = inData => {
|
|
const encapTags = getEncapsulatedData(inData);
|
|
const data = [];
|
|
const dataConcat = [];
|
|
let currentJpeg; // organize data as an array of an array of JPEG parts
|
|
|
|
if (encapTags) {
|
|
const numTags = encapTags.length;
|
|
|
|
for (let ctr = 0; ctr < numTags; ctr += 1) {
|
|
const dataView = encapTags[ctr].value;
|
|
|
|
if (isHeaderJPEG(dataView) || isHeaderJPEG2000(dataView)) {
|
|
currentJpeg = [];
|
|
currentJpeg.push(dataView);
|
|
data.push(currentJpeg);
|
|
} else if (currentJpeg && dataView) {
|
|
currentJpeg.push(dataView);
|
|
}
|
|
}
|
|
} // concat into an array of full JPEGs
|
|
|
|
|
|
for (let ctr = 0; ctr < data.length; ctr += 1) {
|
|
const buffers = data[ctr];
|
|
|
|
if (buffers.length > 1) {
|
|
// TODO: this will be slow...is it necessary?
|
|
dataConcat[ctr] = concatDataViews(buffers);
|
|
} else {
|
|
[dataConcat[ctr]] = data[ctr];
|
|
}
|
|
|
|
delete data[ctr];
|
|
}
|
|
|
|
return dataConcat;
|
|
};
|
|
|
|
class RLEDecoder extends Decoder {
|
|
constructor(...args) {
|
|
super(...args);
|
|
this.rleData = null;
|
|
}
|
|
|
|
decode(frameNo) {
|
|
return import('./rle-2b767c29.js').then(RLE => {
|
|
const {
|
|
image
|
|
} = this;
|
|
|
|
if (!this.rleData) {
|
|
const encapTags = getEncapsulatedData(image.data);
|
|
const numTags = (encapTags === null || encapTags === void 0 ? void 0 : encapTags.length) || 0;
|
|
const data = new Array(numTags); // the first sublist item contains offsets - ignore
|
|
|
|
for (let ctr = 1; ctr < numTags; ctr += 1) {
|
|
const dataView = encapTags[ctr].rawValue;
|
|
data[ctr - 1] = dataView;
|
|
}
|
|
|
|
this.rleData = data; // don't decode to planar...just makes render more complex
|
|
// RLE input is the same either way
|
|
|
|
if (image.planar) {
|
|
image.planar = false;
|
|
}
|
|
}
|
|
|
|
const decompressed = RLE.default(image, this.rleData[frameNo]);
|
|
return decompressed;
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
class NativeDecoder extends Decoder {
|
|
constructor(image) {
|
|
super(image);
|
|
this.jpegData = void 0;
|
|
this.image.rgb = true; // native img decoder outputs RGB
|
|
|
|
this.jpegData = getJpegData(image.data);
|
|
}
|
|
|
|
createTexture(gl, frameNo) {
|
|
var _this$jpegData;
|
|
|
|
const {
|
|
width,
|
|
height
|
|
} = this.outputSize;
|
|
const jpegFrameData = (_this$jpegData = this.jpegData) === null || _this$jpegData === void 0 ? void 0 : _this$jpegData[frameNo];
|
|
|
|
if (!jpegFrameData) {
|
|
throw Error("No Native JPEG image data");
|
|
}
|
|
|
|
const blob = new Blob([jpegFrameData]);
|
|
const src = URL.createObjectURL(blob);
|
|
return new Promise((resolve, reject) => {
|
|
twgl.createTexture(gl, {
|
|
src,
|
|
width,
|
|
height,
|
|
type: gl.UNSIGNED_BYTE,
|
|
min: gl.NEAREST,
|
|
mag: gl.NEAREST,
|
|
wrap: gl.CLAMP_TO_EDGE
|
|
}, (error, texture) => {
|
|
URL.revokeObjectURL(src);
|
|
|
|
if (error) {
|
|
reject(error);
|
|
}
|
|
|
|
resolve(texture);
|
|
});
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
class JPEGLosslessDecoder$2 extends Decoder {
|
|
constructor(...args) {
|
|
super(...args);
|
|
this.jpegs = null;
|
|
}
|
|
|
|
decode(frameNo) {
|
|
var _this$jpegs;
|
|
|
|
const {
|
|
image
|
|
} = this;
|
|
|
|
if (!this.jpegs) {
|
|
this.jpegs = getJpegData(image.data);
|
|
}
|
|
|
|
if (!((_this$jpegs = this.jpegs) !== null && _this$jpegs !== void 0 && _this$jpegs.length)) {
|
|
return Promise.reject(new Error("No JPEG lossless image data"));
|
|
}
|
|
|
|
const decoder = new _jpegLosslessDecoderJs.lossless.Decoder();
|
|
const jpeg = this.jpegs[frameNo];
|
|
const buffer = new Uint8Array(jpeg.buffer, jpeg.byteOffset, jpeg.byteLength);
|
|
const temp = decoder.decode(buffer); // const numComponents = decoder.numComp;
|
|
|
|
return Promise.resolve(temp);
|
|
}
|
|
|
|
}
|
|
|
|
class JPEGLosslessDecoder$1 extends Decoder {
|
|
constructor(...args) {
|
|
super(...args);
|
|
this.jpegs = null;
|
|
}
|
|
|
|
decode(frameNo) {
|
|
var _this$jpegs;
|
|
|
|
const {
|
|
image
|
|
} = this;
|
|
|
|
if (!this.jpegs) {
|
|
this.jpegs = getJpegData(image.data);
|
|
}
|
|
|
|
if (!((_this$jpegs = this.jpegs) !== null && _this$jpegs !== void 0 && _this$jpegs.length)) {
|
|
return Promise.reject(new Error("No JPEG-LS image data"));
|
|
}
|
|
|
|
return new Promise(resolve => {
|
|
let charLS;
|
|
|
|
const init = () => {
|
|
const decoder = new charLS.JpegLSDecoder();
|
|
const jpeg = this.jpegs[frameNo];
|
|
const buffer = new Uint8Array(jpeg.buffer, jpeg.byteOffset, jpeg.byteLength);
|
|
const encodedBuffer = decoder.getEncodedBuffer(buffer.length);
|
|
encodedBuffer.set(buffer);
|
|
decoder.decode();
|
|
const decoded = decoder.getDecodedBuffer();
|
|
return resolve(decoded);
|
|
};
|
|
|
|
import('./charlsjs-d0f5a366.js').then(CharLS => {
|
|
charLS = CharLS.default({
|
|
onRuntimeInitialized: init
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
class JPEG2000Decoder extends Decoder {
|
|
constructor(...args) {
|
|
super(...args);
|
|
this.jpegs = null;
|
|
}
|
|
|
|
decode(frameNo) {
|
|
var _this$jpegs;
|
|
|
|
const {
|
|
image
|
|
} = this;
|
|
|
|
if (!this.jpegs) {
|
|
this.jpegs = getJpegData(image.data);
|
|
}
|
|
|
|
if (!((_this$jpegs = this.jpegs) !== null && _this$jpegs !== void 0 && _this$jpegs.length)) {
|
|
return Promise.reject(new Error("No JPEG2000 image data"));
|
|
}
|
|
|
|
return new Promise(resolve => {
|
|
let OJ;
|
|
|
|
const init = () => {
|
|
const decoder = new OJ.J2KDecoder();
|
|
const jpeg = this.jpegs[frameNo];
|
|
const buffer = new Uint8Array(jpeg.buffer, jpeg.byteOffset, jpeg.byteLength);
|
|
const encodedBuffer = decoder.getEncodedBuffer(buffer.length);
|
|
encodedBuffer.set(buffer);
|
|
const decodeLevel = 0;
|
|
const decodeLayer = 0;
|
|
|
|
for (let i = 0; i < 1; i += 1) {
|
|
decoder.decodeSubResolution(decodeLevel, decodeLayer);
|
|
}
|
|
|
|
decoder.getFrameInfo();
|
|
const decodedBuffer = decoder.getDecodedBuffer();
|
|
return resolve(decodedBuffer);
|
|
};
|
|
|
|
import('./openjpeg-2693517c.js').then(OpenJpegJS => {
|
|
OJ = OpenJpegJS.default({
|
|
onRuntimeInitialized: init
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
class JPEGLosslessDecoder extends Decoder {
|
|
constructor(...args) {
|
|
super(...args);
|
|
this.jpegs = null;
|
|
}
|
|
|
|
decode(frameNo) {
|
|
return import('./jpeg-baseline-81e0d4be.js').then(jpegBaseline => {
|
|
var _this$jpegs;
|
|
|
|
const {
|
|
image
|
|
} = this;
|
|
|
|
if (!this.jpegs) {
|
|
this.jpegs = getJpegData(image.data);
|
|
}
|
|
|
|
if (!((_this$jpegs = this.jpegs) !== null && _this$jpegs !== void 0 && _this$jpegs.length)) {
|
|
return Promise.reject(new Error("No JPEG image data"));
|
|
}
|
|
|
|
const decoder = new jpegBaseline.JpegImage();
|
|
const jpeg = this.jpegs[frameNo];
|
|
const buffer = new Uint8Array(jpeg.buffer, jpeg.byteOffset, jpeg.byteLength);
|
|
decoder.parse(buffer);
|
|
const {
|
|
width,
|
|
height
|
|
} = decoder;
|
|
let decoded = null;
|
|
|
|
if (image.bitsAllocated === 8) {
|
|
decoded = decoder.getData(width, height);
|
|
} else {
|
|
decoded = decoder.getData16(width, height);
|
|
}
|
|
|
|
return Promise.resolve(decoded);
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
class ImageSize {
|
|
constructor({
|
|
width,
|
|
height,
|
|
rows,
|
|
columns
|
|
}) {
|
|
var _ref, _ref2;
|
|
|
|
this.width = void 0;
|
|
this.height = void 0;
|
|
this.width = (_ref = width !== null && width !== void 0 ? width : columns) !== null && _ref !== void 0 ? _ref : 0;
|
|
this.height = (_ref2 = height !== null && height !== void 0 ? height : rows) !== null && _ref2 !== void 0 ? _ref2 : 0;
|
|
}
|
|
|
|
get rows() {
|
|
return this.height;
|
|
}
|
|
|
|
get columns() {
|
|
return this.width;
|
|
}
|
|
|
|
get numberOfPixels() {
|
|
const {
|
|
width,
|
|
height
|
|
} = this;
|
|
return width * height;
|
|
}
|
|
|
|
scale(scale) {
|
|
let {
|
|
width,
|
|
height
|
|
} = this;
|
|
width *= scale;
|
|
height *= scale;
|
|
return new ImageSize({
|
|
width,
|
|
height
|
|
});
|
|
}
|
|
|
|
}
|
|
/* eslint-disable no-bitwise */
|
|
|
|
|
|
var Codec;
|
|
|
|
(function (Codec) {
|
|
Codec[Codec["Uncompressed"] = 0] = "Uncompressed";
|
|
Codec[Codec["JPEG"] = 1] = "JPEG";
|
|
Codec[Codec["JPEGExt"] = 2] = "JPEGExt";
|
|
Codec[Codec["JPEG2000"] = 4] = "JPEG2000";
|
|
Codec[Codec["JPEGLS"] = 8] = "JPEGLS";
|
|
Codec[Codec["JPEGLossless"] = 16] = "JPEGLossless";
|
|
Codec[Codec["RLE"] = 32] = "RLE";
|
|
})(Codec || (Codec = {}));
|
|
|
|
const Signed = 0x8;
|
|
var PixelDataType;
|
|
|
|
(function (PixelDataType) {
|
|
PixelDataType[PixelDataType["Uint8"] = 1] = "Uint8";
|
|
PixelDataType[PixelDataType["Int8"] = 0x1 & Signed] = "Int8";
|
|
PixelDataType[PixelDataType["Uint16"] = 2] = "Uint16";
|
|
PixelDataType[PixelDataType["Int16"] = 0x2 & Signed] = "Int16";
|
|
})(PixelDataType || (PixelDataType = {}));
|
|
|
|
class DecoderInfo {
|
|
constructor(image) {
|
|
this.image = void 0;
|
|
this.size = void 0;
|
|
this.codec = void 0;
|
|
this.rgb = void 0;
|
|
this.planar = void 0;
|
|
this.samples = void 0;
|
|
this.bitsAllocated = void 0;
|
|
this.bytesAllocated = void 0;
|
|
this.bitsStored = void 0;
|
|
this.signed = void 0;
|
|
this.littleEndian = void 0;
|
|
this.data = void 0;
|
|
this.image = image;
|
|
|
|
if (!image.pixelData) {
|
|
throw Error("Image has no data");
|
|
}
|
|
|
|
this.size = new ImageSize(image);
|
|
|
|
switch (image.transferSyntax) {
|
|
case TransferSyntax.CompressionJpeg:
|
|
case TransferSyntax.CompressionJpegBaseline8bit:
|
|
this.codec = Codec.JPEG;
|
|
break;
|
|
|
|
case TransferSyntax.CompressionJpegBaseline12bit:
|
|
this.codec = Codec.JPEGExt;
|
|
break;
|
|
|
|
case TransferSyntax.CompressionJpegLossless:
|
|
case TransferSyntax.CompressionJpegLosslessSel1:
|
|
this.codec = Codec.JPEGLossless;
|
|
break;
|
|
|
|
case TransferSyntax.CompressionJpegLs:
|
|
case TransferSyntax.CompressionJpegLsLossless:
|
|
this.codec = Codec.JPEGLS;
|
|
break;
|
|
|
|
case TransferSyntax.CompressionJpeg2000Lossless:
|
|
case TransferSyntax.CompressionJpeg2000:
|
|
this.codec = Codec.JPEG2000;
|
|
break;
|
|
|
|
case TransferSyntax.CompressionRLE:
|
|
this.codec = Codec.RLE;
|
|
break;
|
|
|
|
case TransferSyntax.CompressionDeflate:
|
|
case TransferSyntax.ImplicitLittle:
|
|
case TransferSyntax.ExplicitLittle:
|
|
case TransferSyntax.ExplicitBig:
|
|
this.codec = Codec.Uncompressed;
|
|
break;
|
|
|
|
default:
|
|
this.codec = Codec.Uncompressed;
|
|
}
|
|
|
|
this.rgb = !(image.photometricInterpretation || "").startsWith("MONO");
|
|
this.planar = !!image.planarConfig;
|
|
this.samples = image.samplesPerPixel;
|
|
this.bitsAllocated = image.bitsAllocated;
|
|
this.bytesAllocated = Math.round(image.bitsAllocated / 8);
|
|
this.bitsStored = image.bitsStored;
|
|
this.signed = image.pixelRepresentation === 1;
|
|
this.data = image.pixelData.value;
|
|
this.littleEndian = image.littleEndian;
|
|
}
|
|
|
|
}
|
|
|
|
const decodeInfoForImage = image => new DecoderInfo(image);
|
|
|
|
const hasCreateObjectURL = !!URL.createObjectURL;
|
|
|
|
const decoderForImage = image => {
|
|
const info = decodeInfoForImage(image);
|
|
|
|
switch (info.codec) {
|
|
case Codec.JPEG:
|
|
if (hasCreateObjectURL) {
|
|
/* istanbul ignore next */
|
|
return new NativeDecoder(info);
|
|
}
|
|
|
|
return new JPEGLosslessDecoder(info);
|
|
|
|
case Codec.JPEGExt:
|
|
return new JPEGLosslessDecoder(info);
|
|
|
|
case Codec.JPEGLS:
|
|
return new JPEGLosslessDecoder$1(info);
|
|
|
|
case Codec.JPEGLossless:
|
|
return new JPEGLosslessDecoder$2(info);
|
|
|
|
case Codec.JPEG2000:
|
|
// safari support native JPEG2000 decode
|
|
if (hasCreateObjectURL && /^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
|
|
/* istanbul ignore next */
|
|
return new NativeDecoder(info);
|
|
}
|
|
|
|
return new JPEG2000Decoder(info);
|
|
|
|
case Codec.RLE:
|
|
return new RLEDecoder(info);
|
|
|
|
case Codec.Uncompressed:
|
|
return new Decoder(info);
|
|
}
|
|
/* istanbul ignore next */
|
|
|
|
|
|
return null;
|
|
};
|
|
/**
|
|
* Unpack pseudo integer or a float from a color value
|
|
* insert into GLSL to change behaviour depending on data
|
|
* @param {Image} image the parsed DICOM image
|
|
* @param {Boolean} integerVal should return greyscale psuedo int (0 - 65535)
|
|
* else return a 0.0-1.0 float color ratio
|
|
*/
|
|
|
|
|
|
const glslUnpackWordString = (image, integerVal = true) => {
|
|
let val;
|
|
let divisor = "";
|
|
const {
|
|
bitsAllocated,
|
|
signed,
|
|
pixelPaddingVal
|
|
} = image;
|
|
|
|
if (!integerVal) {
|
|
divisor = ` / ${2 ** image.bitsStored}.0`;
|
|
}
|
|
|
|
if (bitsAllocated <= 8) {
|
|
// one byte
|
|
val = "float p = (color.r * 255.0);\n";
|
|
|
|
if (pixelPaddingVal !== null) {
|
|
let pv = pixelPaddingVal;
|
|
|
|
if (signed) {
|
|
// eslint-disable-next-line prefer-destructuring
|
|
pv = new Uint8Array(new Int8Array([pixelPaddingVal]))[0];
|
|
}
|
|
|
|
val = `${val}if (floor(p + 0.5) == ${pv}.0) return -1.0;\n`;
|
|
}
|
|
|
|
if (signed) {
|
|
val = `${val}(p > 127.0 ? p - 127.0 : p + 127.0);\n`;
|
|
}
|
|
} else {
|
|
const {
|
|
rgb
|
|
} = image; // 2nd byte for greyscale images is packed in alpha chan,
|
|
// or green channel for RGB based 16bit greyscale
|
|
|
|
const byte2Chan = rgb ? "g" : "a";
|
|
|
|
if (image.littleEndian) {
|
|
val = `float p = (color.${byte2Chan} * 65280.0 + color.r * 255.0);\n`;
|
|
} else {
|
|
val = `float p = (color.r * 65280.0 + color.${byte2Chan} * 255.0);\n`;
|
|
}
|
|
|
|
if (pixelPaddingVal !== null) {
|
|
let pv = pixelPaddingVal;
|
|
|
|
if (signed) {
|
|
// eslint-disable-next-line prefer-destructuring
|
|
pv = new Uint16Array(new Int16Array([pixelPaddingVal]))[0];
|
|
}
|
|
|
|
val = `${val}if (floor(p + 0.5) == ${pv}.0) return -1.0;\n`;
|
|
}
|
|
|
|
if (signed) {
|
|
val = `${val}p = (p > 32767.0 ? p - 32767.0 : p + 32767.0);\n`;
|
|
}
|
|
}
|
|
|
|
return `${val}return p${divisor};`;
|
|
};
|
|
/**
|
|
* replace placeholders in the glsl strings with proper code
|
|
* @param info the image info we are to display
|
|
* @param shaderString input shader program string
|
|
* @param integerVal should getWord return a sized integer, or a 0-1 float ratio
|
|
* @returns
|
|
*/
|
|
|
|
|
|
const preCompileGreyscaleShader = (info, shaderString, integerVal = true) => {
|
|
let outShaderString = shaderString.replace("$(word)", glslUnpackWordString(info, integerVal));
|
|
const {
|
|
invert,
|
|
pixelPaddingVal
|
|
} = info;
|
|
|
|
if (invert) {
|
|
outShaderString = outShaderString.replace("// $(shouldInvert)", "grey = 1.0 - grey;");
|
|
}
|
|
|
|
if (pixelPaddingVal !== null) {
|
|
outShaderString = outShaderString.replace("// $(pixelPadding)", "if (grey < 0.0) {\ngl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);\nreturn;\n}\n");
|
|
}
|
|
|
|
return outShaderString;
|
|
};
|
|
|
|
const vertexShader$4 = "attribute vec4 position;\n\nvoid main() {\n gl_Position = position;\n}\n";
|
|
const minMaxShader = "#extension GL_EXT_draw_buffers : require\n\nprecision mediump float;\n\n#define CELL_SIZE $(cellSize)\n\nuniform sampler2D u_minTexture;\nuniform sampler2D u_maxTexture;\nuniform vec2 u_srcResolution;\n\nfloat greyscale(vec4 color) {\n $(word)\n}\n\nvoid main() {\n // compute the first pixel the source cell\n vec2 srcPixel = floor(gl_FragCoord.xy) * float(CELL_SIZE);\n\n // one pixel in source\n vec2 onePixel = vec2(1) / u_srcResolution;\n\n // uv for first pixel in cell. +0.5 for center of pixel\n vec2 uv = (srcPixel + 0.5) / u_srcResolution;\n\n float minVal = 65535.0;\n float maxVal = 0.0;\n vec4 minColor = vec4(1.0, 1.0, 1.0, 1.0);\n vec4 maxColor = vec4(0.0, 0.0, 0.0, 0.0);\n for (int y = 0; y < CELL_SIZE; ++y) {\n for (int x = 0; x < CELL_SIZE; ++x) {\n vec2 off = uv + vec2(x, y) * onePixel;\n vec4 colorMin = texture2D(u_minTexture, off);\n float grey = greyscale(colorMin);\n if (minVal > grey) {\n minColor = colorMin;\n minVal = grey;\n }\n vec4 colorMax = texture2D(u_maxTexture, off);\n grey = greyscale(colorMax);\n if (maxVal < grey) {\n maxColor = colorMax;\n maxVal = grey;\n }\n }\n }\n\n gl_FragData[0] = minColor;\n gl_FragData[1] = maxColor;\n}\n";
|
|
const contrastifyShader = "precision highp float;\nuniform sampler2D u_minColor;\nuniform sampler2D u_maxColor;\nuniform sampler2D u_texture;\nuniform vec2 u_resolution;\nuniform float u_slope;\nuniform float u_intercept;\n\nfloat greyscale(vec4 color) {\n\t$(word)\n}\n\nfloat minMaxWord(vec4 color) {\n\t$(minMaxWord)\n}\n\nvoid main() {\n\tvec2 uv = gl_FragCoord.xy / u_resolution;\n\tuv.y = 1.0 - uv.y;\n\tfloat grey = greyscale(texture2D(u_texture, uv));\n\t// $(pixelPadding)\n\tfloat minColor = minMaxWord(texture2D(u_minColor, vec2(0)));\n\tfloat maxColor = minMaxWord(texture2D(u_maxColor, vec2(0)));\n\n\tgrey = grey * u_slope + u_intercept;\n\tminColor = minColor * u_slope + u_intercept;\n\tmaxColor = maxColor * u_slope + u_intercept;\n\n\tfloat ww = maxColor - minColor;\n\tfloat wc = (minColor + maxColor) / 2.0 - 0.5;\n\n\tgrey = ((grey - wc) / ww) + 0.5;\n\tgrey = clamp(grey, 0.0, 1.0);\n\n\t// $(shouldInvert)\n\tgl_FragColor = vec4(grey, grey, grey, 1);\n}\n";
|
|
const cellSize = 16;
|
|
|
|
class ContrastifyProgram {
|
|
// eslint-disable-next-line camelcase
|
|
static programStringForInfo(info) {
|
|
// don't ignore pixelPaddingVal in minMax calcs
|
|
const getMinMaxWordString = glslUnpackWordString(_extends({}, info, {
|
|
pixelPaddingVal: null
|
|
}), true);
|
|
const minMaxFragString = minMaxShader.replace("$(cellSize)", cellSize.toString()).replace("$(word)", getMinMaxWordString);
|
|
const contrastifyShaderString = preCompileGreyscaleShader(info, contrastifyShader.replace("$(minMaxWord)", getMinMaxWordString), true);
|
|
return [minMaxFragString, contrastifyShaderString];
|
|
}
|
|
|
|
constructor(gl, info) {
|
|
this.ext = void 0;
|
|
this.minMaxProgramInfo = void 0;
|
|
this.contrastProgramInfo = void 0;
|
|
this.unitQuadBufferInfo = null;
|
|
this.gl = void 0;
|
|
const ext = gl.getExtension("WEBGL_draw_buffers");
|
|
|
|
if (!ext) {
|
|
throw new Error("Image requires WEBGL_draw_buffers");
|
|
}
|
|
|
|
this.ext = ext; // TODO: don;t double up on program string generation
|
|
|
|
const [minMaxFrag, contrastifyFrag] = ContrastifyProgram.programStringForInfo(info);
|
|
this.minMaxProgramInfo = twgl.createProgramInfo(gl, [vertexShader$4, minMaxFrag]);
|
|
this.contrastProgramInfo = twgl.createProgramInfo(gl, [vertexShader$4, contrastifyFrag]);
|
|
this.unitQuadBufferInfo = twgl.primitives.createXYQuadBufferInfo(gl);
|
|
this.gl = gl;
|
|
} // eslint-disable-next-line class-methods-use-this
|
|
|
|
|
|
use() {// can't really do anything here...
|
|
}
|
|
|
|
run(frame, outputSize) {
|
|
const framebuffers = [];
|
|
const {
|
|
gl,
|
|
ext,
|
|
minMaxProgramInfo,
|
|
contrastProgramInfo,
|
|
unitQuadBufferInfo
|
|
} = this;
|
|
const {
|
|
imageInfo
|
|
} = frame;
|
|
const {
|
|
size,
|
|
slope,
|
|
intercept
|
|
} = imageInfo;
|
|
const {
|
|
width,
|
|
height
|
|
} = size;
|
|
let w = width;
|
|
let h = height;
|
|
const srcTex = frame.texture;
|
|
|
|
while (w > 1 || h > 1) {
|
|
// | 0 like floor but Infinity/NaN are zero'd
|
|
// eslint-disable-next-line no-bitwise
|
|
w = Math.max(1, (w + cellSize - 1) / cellSize | 0); // eslint-disable-next-line no-bitwise
|
|
|
|
h = Math.max(1, (h + cellSize - 1) / cellSize | 0); // creates a framebuffer and creates and attaches 2 RGBA/UNSIGNED textures
|
|
|
|
const fbi = twgl.createFramebufferInfo(gl, [{
|
|
format: gl.RGBA,
|
|
min: gl.NEAREST,
|
|
mag: gl.NEAREST,
|
|
wrap: gl.CLAMP_TO_EDGE
|
|
}, {
|
|
format: gl.RGBA,
|
|
min: gl.NEAREST,
|
|
mag: gl.NEAREST,
|
|
wrap: gl.CLAMP_TO_EDGE
|
|
}], w, h); // WebGl2
|
|
// gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]);
|
|
|
|
ext.drawBuffersWEBGL([ext.COLOR_ATTACHMENT0_WEBGL, ext.COLOR_ATTACHMENT1_WEBGL]);
|
|
framebuffers.push(fbi);
|
|
} // need separate FBs to read the output
|
|
|
|
|
|
const lastFBI = framebuffers[framebuffers.length - 1];
|
|
const uniforms = {
|
|
u_srcResolution: [width, height],
|
|
u_minTexture: srcTex,
|
|
u_maxTexture: srcTex
|
|
};
|
|
gl.useProgram(minMaxProgramInfo.program);
|
|
twgl.setBuffersAndAttributes(gl, minMaxProgramInfo, unitQuadBufferInfo);
|
|
w = width;
|
|
h = height;
|
|
framebuffers.forEach(fbi => {
|
|
// | 0 like floor but Infinity/NaN are zero'd
|
|
// eslint-disable-next-line no-bitwise
|
|
w = Math.max(1, (w + cellSize - 1) / cellSize | 0); // eslint-disable-next-line no-bitwise
|
|
|
|
h = Math.max(1, (h + cellSize - 1) / cellSize | 0); // uniforms.u_dstResolution = [w, h];
|
|
|
|
twgl.bindFramebufferInfo(gl, fbi);
|
|
twgl.setUniforms(minMaxProgramInfo, uniforms);
|
|
twgl.drawBufferInfo(gl, unitQuadBufferInfo);
|
|
[uniforms.u_minTexture, uniforms.u_maxTexture] = fbi.attachments;
|
|
uniforms.u_srcResolution = [w, h];
|
|
}); // // Read min/max pixel onto CPU - slow but might be useful
|
|
// const minFBI = twgl.createFramebufferInfo(gl, [
|
|
// { attachment: lastFBI.attachments[0] }
|
|
// ], 1, 1);
|
|
// const maxFBI = twgl.createFramebufferInfo(gl, [
|
|
// { attachment: lastFBI.attachments[1] }
|
|
// ], 1, 1);
|
|
// const minVals = new Uint8Array(4);
|
|
// const maxVals = new Uint8Array(4);
|
|
// twgl.bindFramebufferInfo(gl, minFBI);
|
|
// gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, minVals);
|
|
// console.log("min: ", minVals[0], minVals[1], minVals[2], minVals[3]);
|
|
// twgl.bindFramebufferInfo(gl, maxFBI);
|
|
// gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, maxVals);
|
|
// console.log("max: ", maxVals[0], maxVals[1], maxVals[2], maxVals[3]);
|
|
|
|
twgl.bindFramebufferInfo(gl, null);
|
|
gl.useProgram(contrastProgramInfo.program);
|
|
twgl.setUniforms(contrastProgramInfo, {
|
|
u_resolution: [outputSize.width, outputSize.height],
|
|
u_texture: srcTex,
|
|
u_minColor: lastFBI.attachments[0],
|
|
u_maxColor: lastFBI.attachments[1],
|
|
u_slope: slope,
|
|
u_intercept: intercept
|
|
});
|
|
twgl.drawBufferInfo(gl, unitQuadBufferInfo); // cleanup on next runloop
|
|
|
|
setTimeout(() => {
|
|
framebuffers.forEach(fbi => {
|
|
const {
|
|
attachments,
|
|
framebuffer
|
|
} = fbi;
|
|
gl.deleteFramebuffer(framebuffer);
|
|
|
|
if (attachments[0] instanceof WebGLRenderbuffer) {
|
|
gl.deleteRenderbuffer(attachments[0]);
|
|
} else {
|
|
gl.deleteTexture(attachments[0]);
|
|
}
|
|
});
|
|
}, 0);
|
|
}
|
|
|
|
destroy() {
|
|
this.gl.deleteProgram(this.contrastProgramInfo.program);
|
|
this.gl.deleteProgram(this.minMaxProgramInfo.program);
|
|
}
|
|
|
|
} // import { IDisplayInfo } from "../image/DisplayInfo";
|
|
|
|
|
|
const vertexShader$3 = "attribute vec4 position;\n\nvoid main() {\n gl_Position = position;\n}\n";
|
|
const colorShader = "precision highp float;\nuniform bool u_invert;\nuniform sampler2D u_texture;\nuniform vec2 u_resolution;\n\nuniform float u_slope;\nuniform float u_intercept;\n\nvec4 getPlanar(vec2 coord) {\n\tfloat third = 1.0 / 3.0;\n\tint col = int(mod(coord.y, 3.0));\n\tfloat yPos = coord.y / 3.0;\n\n\tfloat xPos = coord.x;\n\n\tvec4 red = texture2D(u_texture, vec2(xPos, yPos));\n\n\tyPos = yPos + third;\n\tvec4 green = texture2D(u_texture, vec2(xPos, yPos));\n\n\tyPos = yPos + third;\n\tvec4 blue = texture2D(u_texture, vec2(xPos, yPos));\n\n\tif (col == 0) {\n\t\treturn vec4(red.r, green.r, blue.r, 1.0);\n\t}\n\tif (col == 1) {\n\t\treturn vec4(red.g, green.g, blue.g, 1.0);\n\t}\n\tif (col == 2) {\n\t\treturn vec4(red.b, green.b, blue.b, 1.0);\n\t}\n\treturn vec4(0.0, 0.0, 0.0, 0.0);\n}\n\nvoid main() {\n\tvec4 color;\n\tvec2 uv = gl_FragCoord.xy / u_resolution;\n\tuv.y = 1.0 - uv.y;\n\n\tcolor = // $(getColor);\n\n \tcolor = (color * u_slope) + u_intercept;\n\n\t// $(u_invert)\n\n\tgl_FragColor = color;\n}\n";
|
|
|
|
class ColorProgram {
|
|
static programStringForInfo(imageInfo) {
|
|
const {
|
|
planar,
|
|
invert
|
|
} = imageInfo;
|
|
let shaderString;
|
|
|
|
if (planar) {
|
|
shaderString = colorShader.replace("// $(getColor);", "getPlanar(uv);\n");
|
|
} else {
|
|
shaderString = colorShader.replace("// $(getColor);", "texture2D(u_texture, uv);\n");
|
|
}
|
|
|
|
if (invert) {
|
|
shaderString = shaderString.replace("// $(u_invert)", "color = vec4(1.0 - color.r, 1.0 - color.g, 1.0 - color.b, color.a);");
|
|
}
|
|
|
|
return shaderString;
|
|
} // don't need info! all non palette color images use same program
|
|
|
|
|
|
constructor(gl, info) {
|
|
this.programInfo = void 0;
|
|
this.unitQuadBufferInfo = void 0;
|
|
this.gl = void 0;
|
|
const shaderString = ColorProgram.programStringForInfo(info);
|
|
const programInfo = twgl.createProgramInfo(gl, [vertexShader$3, shaderString]);
|
|
this.unitQuadBufferInfo = twgl.primitives.createXYQuadBufferInfo(gl);
|
|
this.programInfo = programInfo;
|
|
this.gl = gl;
|
|
}
|
|
|
|
use() {
|
|
const {
|
|
gl,
|
|
programInfo,
|
|
unitQuadBufferInfo
|
|
} = this;
|
|
twgl.bindFramebufferInfo(gl, null);
|
|
gl.useProgram(programInfo.program);
|
|
twgl.setBuffersAndAttributes(gl, programInfo, unitQuadBufferInfo);
|
|
}
|
|
|
|
run(frame, outputSize) {
|
|
const {
|
|
gl,
|
|
programInfo,
|
|
unitQuadBufferInfo
|
|
} = this;
|
|
const {
|
|
invert,
|
|
slope,
|
|
intercept
|
|
} = frame.imageInfo;
|
|
const {
|
|
texture
|
|
} = frame;
|
|
twgl.setUniforms(programInfo, {
|
|
u_resolution: [outputSize.width, outputSize.height],
|
|
u_texture: texture,
|
|
u_invert: invert,
|
|
u_slope: slope,
|
|
u_intercept: intercept
|
|
});
|
|
twgl.drawBufferInfo(gl, unitQuadBufferInfo);
|
|
}
|
|
|
|
destroy() {
|
|
this.gl.deleteProgram(this.programInfo.program);
|
|
}
|
|
|
|
}
|
|
|
|
const vertexShader$2 = "attribute vec4 position;\n\nvoid main() {\n gl_Position = position;\n}\n";
|
|
const greyscaleShader = "precision highp float;\nuniform sampler2D u_texture;\nuniform vec2 u_resolution;\nuniform float u_slope;\nuniform float u_intercept;\n\nuniform float u_winCenter;\nuniform float u_winWidth;\n\nfloat greyscale(vec4 color) {\n\t$(word)\n}\n\nvoid main() {\n\tvec2 uv = gl_FragCoord.xy / u_resolution;\n\tuv.y = 1.0 - uv.y;\n\n\tfloat grey = greyscale(texture2D(u_texture, uv));\n\t// $(pixelPadding)\n\tgrey = (grey * u_slope) + u_intercept;\n\n\tfloat center = u_winCenter - 0.5;\n\tfloat width = max(u_winWidth, 1.0);\n\tgrey = (grey - center) / width + 0.5;\n\tgrey = clamp(grey, 0.0, 1.0);\n\n\t// $(shouldInvert)\n\tgl_FragColor = vec4(grey, grey, grey, 1);\n}\n";
|
|
|
|
class GreyscaleProgram {
|
|
static programStringForInfo(info) {
|
|
return preCompileGreyscaleShader(info, greyscaleShader);
|
|
}
|
|
|
|
constructor(gl, info) {
|
|
this.programInfo = void 0;
|
|
this.unitQuadBufferInfo = null;
|
|
this.gl = void 0;
|
|
const greyscaleShaderString = GreyscaleProgram.programStringForInfo(info);
|
|
const programInfo = twgl.createProgramInfo(gl, [vertexShader$2, greyscaleShaderString]);
|
|
this.unitQuadBufferInfo = twgl.primitives.createXYQuadBufferInfo(gl);
|
|
this.programInfo = programInfo;
|
|
this.gl = gl;
|
|
}
|
|
|
|
use() {
|
|
const {
|
|
gl,
|
|
programInfo,
|
|
unitQuadBufferInfo
|
|
} = this;
|
|
twgl.bindFramebufferInfo(gl, null);
|
|
gl.useProgram(programInfo.program);
|
|
twgl.setBuffersAndAttributes(gl, programInfo, unitQuadBufferInfo);
|
|
}
|
|
|
|
run(frame, outputSize) {
|
|
const {
|
|
gl,
|
|
unitQuadBufferInfo,
|
|
programInfo
|
|
} = this;
|
|
const {
|
|
texture,
|
|
imageInfo
|
|
} = frame;
|
|
let {
|
|
windowWidth,
|
|
windowCenter
|
|
} = imageInfo;
|
|
const {
|
|
maxPixVal,
|
|
minPixVal,
|
|
slope,
|
|
intercept,
|
|
signed,
|
|
bitsAllocated
|
|
} = imageInfo;
|
|
|
|
if (!windowWidth && (maxPixVal !== null || minPixVal !== null)) {
|
|
windowWidth = Math.abs((maxPixVal !== null && maxPixVal !== void 0 ? maxPixVal : 0) - (minPixVal !== null && minPixVal !== void 0 ? minPixVal : 0));
|
|
windowCenter = ((maxPixVal || 0) + (minPixVal || 0)) / 2;
|
|
}
|
|
|
|
if (signed) {
|
|
windowCenter = (windowCenter || 0) + 2 ** (bitsAllocated - 1);
|
|
}
|
|
|
|
twgl.setUniforms(programInfo, {
|
|
u_resolution: [outputSize.width, outputSize.height],
|
|
u_texture: texture,
|
|
u_winWidth: windowWidth,
|
|
u_winCenter: windowCenter,
|
|
u_slope: slope,
|
|
u_intercept: intercept
|
|
});
|
|
twgl.drawBufferInfo(gl, unitQuadBufferInfo);
|
|
}
|
|
|
|
destroy() {
|
|
this.gl.deleteProgram(this.programInfo.program);
|
|
}
|
|
|
|
}
|
|
|
|
const vertexShader$1 = "attribute vec4 position;\n\nvoid main() {\n gl_Position = position;\n}\n";
|
|
const greyscaleLUTShader = "precision highp float;\nuniform sampler2D u_texture;\nuniform vec2 u_resolution;\nuniform sampler2D u_lutTexture;\nuniform float u_lutWidth;\nuniform float u_firstInputValue;\nuniform float u_maxValue;\n\nfloat greyscale(vec4 color) {\n\t$(word)\n}\n\nvoid main() {\n\tvec2 uv = gl_FragCoord.xy / u_resolution;\n\tuv.y = 1.0 - uv.y;\n\n\tfloat grey = greyscale(texture2D(u_texture, uv));\n\t// $(pixelPadding)\n\tfloat lutPos = (max(u_firstInputValue, grey) - u_firstInputValue);\n\tgrey = greyscale(texture2D(u_lutTexture, vec2(lutPos / u_lutWidth, 0.5))) / u_maxValue;\n\t// $(shouldInvert)\n\tgl_FragColor = vec4(grey, grey, grey, 1);\n}\n";
|
|
|
|
class GreyscaleLUTProgram {
|
|
static programStringForInfo(info) {
|
|
return preCompileGreyscaleShader(info, greyscaleLUTShader);
|
|
}
|
|
|
|
constructor(gl, info) {
|
|
this.programInfo = void 0;
|
|
this.unitQuadBufferInfo = null;
|
|
this.info = void 0;
|
|
this.gl = void 0;
|
|
const fragShaderString = GreyscaleLUTProgram.programStringForInfo(info);
|
|
const programInfo = twgl.createProgramInfo(gl, [vertexShader$1, fragShaderString]);
|
|
this.programInfo = programInfo;
|
|
this.gl = gl;
|
|
this.info = info;
|
|
}
|
|
|
|
use() {
|
|
const {
|
|
gl,
|
|
programInfo
|
|
} = this; // can this be reused?
|
|
|
|
const unitQuadBufferInfo = twgl.primitives.createXYQuadBufferInfo(gl);
|
|
twgl.bindFramebufferInfo(gl, null);
|
|
gl.useProgram(programInfo.program);
|
|
twgl.setBuffersAndAttributes(gl, programInfo, unitQuadBufferInfo);
|
|
this.unitQuadBufferInfo = unitQuadBufferInfo;
|
|
}
|
|
|
|
run(frame, outputSize) {
|
|
const {
|
|
gl,
|
|
unitQuadBufferInfo,
|
|
programInfo,
|
|
info
|
|
} = this;
|
|
const {
|
|
texture,
|
|
imageInfo
|
|
} = frame;
|
|
const {
|
|
lut
|
|
} = imageInfo;
|
|
let format = gl.LUMINANCE_ALPHA;
|
|
let internalFormat = gl.LUMINANCE_ALPHA;
|
|
|
|
if (info.bitsAllocated <= 8) {
|
|
format = gl.LUMINANCE;
|
|
internalFormat = gl.LUMINANCE;
|
|
} // 1D tex
|
|
|
|
|
|
const lutTexture = twgl.createTexture(gl, {
|
|
src: new Uint8Array(lut.data.buffer),
|
|
width: lut.data.length,
|
|
height: 1,
|
|
format,
|
|
internalFormat,
|
|
type: gl.UNSIGNED_BYTE,
|
|
min: gl.NEAREST,
|
|
mag: gl.NEAREST,
|
|
wrap: gl.CLAMP_TO_EDGE
|
|
});
|
|
twgl.setUniforms(programInfo, {
|
|
u_resolution: [outputSize.width, outputSize.height],
|
|
u_texture: texture,
|
|
u_lutTexture: lutTexture,
|
|
u_lutWidth: lut.data.length,
|
|
u_firstInputValue: lut.firstValue,
|
|
u_maxValue: 2 ** info.bitsStored
|
|
});
|
|
twgl.drawBufferInfo(gl, unitQuadBufferInfo);
|
|
setTimeout(() => {
|
|
gl.deleteTexture(lutTexture);
|
|
}, 0);
|
|
}
|
|
|
|
destroy() {
|
|
const {
|
|
gl
|
|
} = this;
|
|
gl.deleteProgram(this.programInfo.program);
|
|
}
|
|
|
|
}
|
|
|
|
const vertexShader = "attribute vec4 position;\n\nvoid main() {\n gl_Position = position;\n}\n";
|
|
const colorPaletteShader = "precision highp float;\nuniform bool u_invert;\nuniform sampler2D u_texture;\nuniform vec2 u_resolution;\nuniform sampler2D u_redTexture;\nuniform sampler2D u_greenTexture;\nuniform sampler2D u_blueTexture;\nuniform float u_paletteWidthRatio;\n\nfloat getWord(vec4 color) {\n\t$(word)\n}\n\nfloat getPaletteWord(vec4 color) {\n\t$(paletteWord)\n}\n\nvoid main() {\n\tvec2 uv = gl_FragCoord.xy / u_resolution;\n\tuv.y = 1.0 - uv.y;\n\n\tfloat palettePos = getWord(texture2D(u_texture, uv)) * u_paletteWidthRatio;\n\n\tfloat red = getPaletteWord(texture2D(u_redTexture, vec2( palettePos, 0.5)));\n\tfloat green = getPaletteWord(texture2D(u_greenTexture, vec2(palettePos, 0.5)));\n\tfloat blue = getPaletteWord(texture2D(u_blueTexture, vec2(palettePos, 0.5)));\n\n\tvec4 color = vec4(red, green, blue, 1.0);\n\tif (u_invert) {\n\t\tcolor = vec4(1.0 - color.r, 1.0 - color.g, 1.0 - color.b, 1.0);\n\t}\n\n\tgl_FragColor = color;\n}\n";
|
|
|
|
class ColorPaletteProgram {
|
|
static programStringForInfo(info) {
|
|
const {
|
|
palette
|
|
} = info;
|
|
const getWordString = glslUnpackWordString(info, false);
|
|
const getPaletteWordString = glslUnpackWordString(_extends({}, info, {
|
|
rgb: false,
|
|
bitsAllocated: palette.bitsAllocated,
|
|
bitsStored: palette.bitsAllocated
|
|
}), false);
|
|
return colorPaletteShader.replace("$(word)", getWordString).replace("$(paletteWord)", getPaletteWordString);
|
|
}
|
|
|
|
constructor(gl, info) {
|
|
this.programInfo = void 0;
|
|
this.unitQuadBufferInfo = void 0;
|
|
this.gl = void 0;
|
|
const programString = ColorPaletteProgram.programStringForInfo(info);
|
|
const programInfo = twgl.createProgramInfo(gl, [vertexShader, programString]);
|
|
this.programInfo = programInfo;
|
|
this.gl = gl; // can this be reused?
|
|
|
|
this.unitQuadBufferInfo = twgl.primitives.createXYQuadBufferInfo(gl);
|
|
}
|
|
|
|
use() {
|
|
const {
|
|
gl,
|
|
programInfo,
|
|
unitQuadBufferInfo
|
|
} = this;
|
|
twgl.bindFramebufferInfo(gl, null);
|
|
gl.useProgram(programInfo.program);
|
|
twgl.setBuffersAndAttributes(gl, programInfo, unitQuadBufferInfo);
|
|
}
|
|
|
|
run(frame, outputSize) {
|
|
const {
|
|
gl,
|
|
unitQuadBufferInfo,
|
|
programInfo
|
|
} = this;
|
|
const {
|
|
texture,
|
|
imageInfo
|
|
} = frame;
|
|
const {
|
|
palette,
|
|
invert,
|
|
bitsAllocated
|
|
} = imageInfo;
|
|
let format = gl.LUMINANCE_ALPHA;
|
|
let internalFormat = gl.LUMINANCE_ALPHA;
|
|
|
|
if (palette.bitsAllocated === 8) {
|
|
format = gl.LUMINANCE;
|
|
internalFormat = gl.LUMINANCE;
|
|
}
|
|
|
|
const {
|
|
r,
|
|
g,
|
|
b,
|
|
nEntries
|
|
} = palette; // 1D tex
|
|
|
|
const red = twgl.createTexture(gl, {
|
|
src: new Uint8Array(r.buffer, r.byteOffset, r.byteLength),
|
|
width: nEntries,
|
|
height: 1,
|
|
format,
|
|
internalFormat,
|
|
type: gl.UNSIGNED_BYTE,
|
|
min: gl.NEAREST,
|
|
mag: gl.NEAREST,
|
|
wrap: gl.CLAMP_TO_EDGE
|
|
});
|
|
const green = twgl.createTexture(gl, {
|
|
src: new Uint8Array(g.buffer, g.byteOffset, g.byteLength),
|
|
width: nEntries,
|
|
height: 1,
|
|
format,
|
|
internalFormat,
|
|
type: gl.UNSIGNED_BYTE,
|
|
min: gl.NEAREST,
|
|
mag: gl.NEAREST,
|
|
wrap: gl.CLAMP_TO_EDGE
|
|
});
|
|
const blue = twgl.createTexture(gl, {
|
|
src: new Uint8Array(b.buffer, b.byteOffset, b.byteLength),
|
|
width: nEntries,
|
|
height: 1,
|
|
format,
|
|
internalFormat,
|
|
type: gl.UNSIGNED_BYTE,
|
|
min: gl.NEAREST,
|
|
mag: gl.NEAREST,
|
|
wrap: gl.CLAMP_TO_EDGE
|
|
});
|
|
twgl.setUniforms(programInfo, {
|
|
u_resolution: [outputSize.width, outputSize.height],
|
|
u_texture: texture,
|
|
u_redTexture: red,
|
|
u_greenTexture: green,
|
|
u_blueTexture: blue,
|
|
u_paletteWidthRatio: 2 ** bitsAllocated / palette.nEntries,
|
|
u_invert: invert
|
|
});
|
|
twgl.drawBufferInfo(gl, unitQuadBufferInfo);
|
|
setTimeout(() => {
|
|
gl.deleteTexture(red);
|
|
gl.deleteTexture(green);
|
|
gl.deleteTexture(blue);
|
|
});
|
|
}
|
|
|
|
destroy() {
|
|
const {
|
|
gl,
|
|
programInfo
|
|
} = this;
|
|
gl.deleteProgram(programInfo.program);
|
|
}
|
|
|
|
}
|
|
|
|
class Renderer {
|
|
constructor(inCanvas) {
|
|
var _document;
|
|
|
|
this.canvas = void 0;
|
|
this.image = null;
|
|
this.gl = void 0;
|
|
this.decoder = null;
|
|
this.program = null;
|
|
this.outSize = null;
|
|
this.programCacheMap = void 0;
|
|
const canvas = inCanvas || ((_document = document) === null || _document === void 0 ? void 0 : _document.createElement("canvas")) || new HTMLCanvasElement();
|
|
const gl = canvas.getContext("webgl");
|
|
|
|
if (!gl) {
|
|
throw Error("could not create webgl from canvas");
|
|
}
|
|
|
|
this.canvas = canvas;
|
|
this.gl = gl;
|
|
this.programCacheMap = new Map();
|
|
}
|
|
/**
|
|
* prime the renderer with specific programs
|
|
* this can improve 1st image render time quite dramatically
|
|
* @param programType
|
|
*/
|
|
|
|
|
|
primeGreyscale(programType) {
|
|
var _ref;
|
|
|
|
const {
|
|
hasLut,
|
|
invert,
|
|
signed,
|
|
bitsAllocated,
|
|
bitsStored,
|
|
littleEndian,
|
|
hasPixelPaddingValue,
|
|
knownWindow
|
|
} = programType;
|
|
const imageType = {
|
|
image: new DCMImage(),
|
|
nFrames: 1,
|
|
rgb: false,
|
|
planar: false,
|
|
signed: signed !== null && signed !== void 0 ? signed : false,
|
|
size: new ImageSize({
|
|
width: 0,
|
|
height: 0
|
|
}),
|
|
codec: Codec.Uncompressed,
|
|
samples: 0,
|
|
bitsAllocated: bitsAllocated !== null && bitsAllocated !== void 0 ? bitsAllocated : 16,
|
|
bytesAllocated: (bitsAllocated !== null && bitsAllocated !== void 0 ? bitsAllocated : 16) / 8,
|
|
bitsStored: (_ref = bitsStored !== null && bitsStored !== void 0 ? bitsStored : bitsAllocated) !== null && _ref !== void 0 ? _ref : 16,
|
|
littleEndian: littleEndian !== null && littleEndian !== void 0 ? littleEndian : true,
|
|
data: new DataView(new ArrayBuffer(0)),
|
|
lut: hasLut ? {
|
|
nEntries: 0,
|
|
firstValue: 0,
|
|
bitsStored: 0,
|
|
data: new Uint8Array(0)
|
|
} : null,
|
|
palette: null,
|
|
invert: invert !== null && invert !== void 0 ? invert : false,
|
|
pixelPaddingVal: hasPixelPaddingValue ? 1 : null,
|
|
minPixVal: null,
|
|
maxPixVal: null,
|
|
windowCenter: knownWindow ? 0.5 : null,
|
|
windowWidth: knownWindow ? 1 : null,
|
|
slope: 1,
|
|
intercept: 0
|
|
};
|
|
this.getProgram(imageType);
|
|
}
|
|
/**
|
|
* prime the renderer with specific programs
|
|
* this can improve 1st image render time quite dramatically
|
|
* @param programType
|
|
*/
|
|
|
|
|
|
primeColor(programType) {
|
|
const {
|
|
planar,
|
|
bitsAllocated,
|
|
signed,
|
|
littleEndian,
|
|
hasPaletteWithWordBits,
|
|
invert
|
|
} = programType;
|
|
const imageType = {
|
|
image: new DCMImage(),
|
|
nFrames: 1,
|
|
rgb: true,
|
|
planar: planar !== null && planar !== void 0 ? planar : false,
|
|
signed: signed !== null && signed !== void 0 ? signed : false,
|
|
size: new ImageSize({
|
|
width: 0,
|
|
height: 0
|
|
}),
|
|
codec: Codec.Uncompressed,
|
|
samples: 3,
|
|
bitsAllocated,
|
|
bytesAllocated: bitsAllocated / 8,
|
|
bitsStored: bitsAllocated,
|
|
littleEndian: littleEndian !== null && littleEndian !== void 0 ? littleEndian : true,
|
|
data: new DataView(new ArrayBuffer(0)),
|
|
lut: null,
|
|
palette: hasPaletteWithWordBits ? {
|
|
nEntries: 0,
|
|
firstValue: 0,
|
|
bitsAllocated: hasPaletteWithWordBits,
|
|
r: new DataView(new ArrayBuffer(0)),
|
|
g: new DataView(new ArrayBuffer(0)),
|
|
b: new DataView(new ArrayBuffer(0))
|
|
} : null,
|
|
invert: invert !== null && invert !== void 0 ? invert : false,
|
|
pixelPaddingVal: null,
|
|
minPixVal: null,
|
|
maxPixVal: null,
|
|
windowCenter: null,
|
|
windowWidth: null,
|
|
slope: 1,
|
|
intercept: 0
|
|
};
|
|
this.getProgram(imageType);
|
|
}
|
|
/**
|
|
* render the image frame to the canvas
|
|
* @param image parsed DCMImage
|
|
* @param frameNo the frame index
|
|
*/
|
|
|
|
|
|
async render(image, frameNo = 0) {
|
|
const {
|
|
gl,
|
|
canvas
|
|
} = this;
|
|
|
|
if (!this.outSize) {
|
|
this.outSize = new ImageSize(image);
|
|
}
|
|
|
|
const size = this.outSize;
|
|
|
|
if (size.width !== canvas.width || size.height !== canvas.height) {
|
|
canvas.width = 1; // near zero the canvas, makes resize much faste!
|
|
|
|
canvas.height = 1;
|
|
canvas.width = size.width;
|
|
canvas.height = size.height;
|
|
}
|
|
|
|
if (this.image !== image) {
|
|
this.image = image;
|
|
const decoder = decoderForImage(image);
|
|
decoder.outputSize = new ImageSize(image);
|
|
const imageInfo = decoder.image;
|
|
const program = this.getProgram(imageInfo);
|
|
program.use();
|
|
this.program = program;
|
|
this.decoder = decoder;
|
|
}
|
|
|
|
const frame = await this.decoder.getFrame(gl, frameNo);
|
|
this.program.run(frame, size);
|
|
setTimeout(() => {
|
|
frame.destroy();
|
|
}, 0);
|
|
}
|
|
|
|
set outputSize(size) {
|
|
this.outSize = new ImageSize(size);
|
|
}
|
|
|
|
get outputSize() {
|
|
if (this.outSize) {
|
|
return this.outSize;
|
|
}
|
|
|
|
return new ImageSize({
|
|
width: 0,
|
|
height: 0
|
|
});
|
|
}
|
|
|
|
clear() {
|
|
const {
|
|
gl,
|
|
canvas
|
|
} = this;
|
|
canvas.width = 0; // zero the canvas, makes resize much faster!
|
|
|
|
canvas.height = 0; // eslint-disable-next-line no-bitwise
|
|
|
|
gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT);
|
|
}
|
|
|
|
destroy(aggressive = false) {
|
|
this.programCacheMap.forEach(program => {
|
|
program.destroy();
|
|
});
|
|
this.programCacheMap = new Map();
|
|
this.program = null;
|
|
this.image = null;
|
|
|
|
if (aggressive) {
|
|
var _this$gl$getExtension; // https://stackoverflow.com/questions/23598471/how-do-i-clean-up-and-unload-a-webgl-canvas-context-from-gpu-after-use
|
|
|
|
|
|
(_this$gl$getExtension = this.gl.getExtension("WEBGL_lose_context")) === null || _this$gl$getExtension === void 0 ? void 0 : _this$gl$getExtension.loseContext();
|
|
this.canvas.width = 1;
|
|
this.canvas.height = 1;
|
|
}
|
|
}
|
|
|
|
getProgram(imageInfo) {
|
|
const {
|
|
gl
|
|
} = this;
|
|
let signature = null;
|
|
|
|
if (imageInfo.palette) {
|
|
signature = {
|
|
hash: (0, _sha.default)(ColorPaletteProgram.programStringForInfo(imageInfo)).toString(),
|
|
Type: ColorPaletteProgram
|
|
};
|
|
} else if (imageInfo.rgb) {
|
|
signature = {
|
|
hash: (0, _sha.default)(ColorProgram.programStringForInfo(imageInfo)).toString(),
|
|
Type: ColorProgram
|
|
};
|
|
} else if (imageInfo.windowCenter || imageInfo.minPixVal || imageInfo.maxPixVal) {
|
|
signature = {
|
|
hash: (0, _sha.default)(GreyscaleProgram.programStringForInfo(imageInfo)).toString(),
|
|
Type: GreyscaleProgram
|
|
};
|
|
} else if (imageInfo.lut) {
|
|
signature = {
|
|
hash: (0, _sha.default)(GreyscaleLUTProgram.programStringForInfo(imageInfo)).toString(),
|
|
Type: GreyscaleLUTProgram
|
|
};
|
|
} else {
|
|
const [s0, s1] = ContrastifyProgram.programStringForInfo(imageInfo);
|
|
signature = {
|
|
hash: (0, _sha.default)(s0 + s1).toString(),
|
|
Type: ContrastifyProgram
|
|
};
|
|
}
|
|
|
|
let program = this.programCacheMap.get(signature.hash);
|
|
|
|
if (!program) {
|
|
program = new signature.Type(gl, imageInfo);
|
|
this.programCacheMap.set(signature.hash, program);
|
|
}
|
|
|
|
return program;
|
|
}
|
|
|
|
}
|
|
|
|
exports.Renderer = Renderer;
|
|
|
|
const render = async (image, canvas, scale = 1.0) => {
|
|
if (!image) {
|
|
return Promise.reject(Series.parserError);
|
|
}
|
|
|
|
const renderer = new Renderer(canvas);
|
|
const outSize = new ImageSize(image).scale(scale);
|
|
renderer.outputSize = outSize;
|
|
await renderer.render(image, 0);
|
|
renderer.destroy();
|
|
return Promise.resolve();
|
|
};
|
|
|
|
exports.render = render;
|
|
|
|
},{"@wearemothership/dicom-character-set":2,"jpeg-lossless-decoder-js":13,"pako":18,"sha1":19,"twgl.js":20}],2:[function(require,module,exports){
|
|
/*! @wearemothership/dicom-character-set - 1.0.4-opt.1 - 2021-03-22 | (c) 2018 Radialogica, LLC | https://github.com/radialogica/dicom-character-set */
|
|
!function(e,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports["dicom-character-set"]=n():e["dicom-character-set"]=n()}(this,function(){return(()=>{"use strict";var o={d:(e,n)=>{for(var t in n)o.o(n,t)&&!o.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:n[t]})},o:(e,n)=>Object.prototype.hasOwnProperty.call(e,n),r:e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})}},e={};o.r(e),o.d(e,{characterSets:()=>i,convertBytes:()=>function(e,n,t){return g(y,r,e,n,t)},convertBytesPromise:()=>function(e,n,t){return g(P,m,e,n,t)}});var n={codeElement:"G0",escapeSequence:[27,40,66],encoding:"windows-1252",isASCII:!0,bytesPerCodePoint:1},i={"ISO_IR 6":{encoding:"utf-8"},"ISO_IR 100":{encoding:"windows-1252"},"ISO_IR 101":{encoding:"iso-8859-2"},"ISO_IR 109":{encoding:"iso-8859-3"},"ISO_IR 110":{encoding:"iso-8859-4"},"ISO_IR 144":{encoding:"iso-8859-5"},"ISO_IR 127":{encoding:"iso-8859-6"},"ISO_IR 126":{encoding:"iso-8859-7"},"ISO_IR 138":{encoding:"iso-8859-8"},"ISO_IR 148":{encoding:"windows-1254"},"ISO_IR 13":{encoding:"shift-jis"},"ISO_IR 166":{encoding:"tis-620"},"ISO 2022 IR 6":{extension:!0,elements:[n]},"ISO 2022 IR 100":{extension:!0,elements:[n,{codeElement:"G1",escapeSequence:[27,45,65],encoding:"windows-1252",bytesPerCodePoint:1}]},"ISO 2022 IR 101":{extension:!0,elements:[n,{codeElement:"G1",escapeSequence:[27,45,66],encoding:"iso-8859-2",bytesPerCodePoint:1}]},"ISO 2022 IR 109":{extension:!0,elements:[n,{codeElement:"G1",escapeSequence:[27,45,67],encoding:"iso-8859-3",bytesPerCodePoint:1}]},"ISO 2022 IR 110":{extension:!0,elements:[n,{codeElement:"G1",escapeSequence:[27,45,68],encoding:"iso-8859-4",bytesPerCodePoint:1}]},"ISO 2022 IR 144":{extension:!0,elements:[n,{codeElement:"G1",escapeSequence:[27,45,76],encoding:"iso-8859-5",bytesPerCodePoint:1}]},"ISO 2022 IR 127":{extension:!0,elements:[n,{codeElement:"G1",escapeSequence:[27,45,71],encoding:"iso-8859-6",bytesPerCodePoint:1}]},"ISO 2022 IR 126":{extension:!0,elements:[n,{codeElement:"G1",escapeSequence:[27,45,70],encoding:"iso-8859-7",bytesPerCodePoint:1}]},"ISO 2022 IR 138":{extension:!0,elements:[n,{codeElement:"G1",escapeSequence:[27,45,72],encoding:"iso-8859-8",bytesPerCodePoint:1}]},"ISO 2022 IR 148":{extension:!0,elements:[n,{codeElement:"G1",escapeSequence:[27,45,77],encoding:"windows-1254",bytesPerCodePoint:1}]},"ISO 2022 IR 13":{extension:!0,elements:[{codeElement:"G0",escapeSequence:[27,40,74],encoding:"shift-jis",bytesPerCodePoint:1},{codeElement:"G1",escapeSequence:[27,41,73],encoding:"shift-jis",bytesPerCodePoint:1}]},"ISO 2022 IR 166":{extension:!0,elements:[n,{codeElement:"G1",escapeSequence:[27,45,84],encoding:"tis-620",bytesPerCodePoint:1}]},"ISO 2022 IR 87":{extension:!0,multiByte:!0,elements:[{codeElement:"G0",escapeSequence:[27,36,66],encoding:"euc-jp",setHighBit:!0,bytesPerCodePoint:2}]},"ISO 2022 IR 159":{extension:!0,multiByte:!0,elements:[{codeElement:"G0",escapeSequence:[27,36,40,68],encoding:"euc-jp",isJISX0212:!0,bytesPerCodePoint:2}]},"ISO 2022 IR 149":{extension:!0,multiByte:!0,elements:[{codeElement:"G1",escapeSequence:[27,36,41,67],encoding:"euc-kr",bytesPerCodePoint:2}]},"ISO 2022 IR 58":{extension:!0,multiByte:!0,elements:[{codeElement:"G1",escapeSequence:[27,36,41,65],encoding:"gb18030",bytesPerCodePoint:2}]},"ISO_IR 192":{encoding:"utf-8",multiByte:!0},GB18030:{encoding:"gb18030",multiByte:!0},GBK:{encoding:"gbk",multiByte:!0}},l=27,c=10,s=12,u=13,d=9,a=92,f=61,I=94;function S(e){return e.replace(/~/g,"‾").replace(/\\/g,"¥")}function r(e,n,t,o,r){r=b(n,t,o,r);return e+y(n.encoding,r)}function m(e,t,n,o,r){var i=b(t,n,o,r);return(""===e?Promise.resolve(""):e).then(function(n){return P(t.encoding,i).then(function(e){return n+e})})}function g(e,n,t,o,r){!function(e,n){if(n&&!(n instanceof Uint8Array))throw new Error("bytes must be a Uint8Array");if(e&&"string"!=typeof e)throw new Error("specificCharacterSet must be a string")}(t,o);t=function(e){e=e?e.split("\\").map(function(e){return e.trim().toUpperCase()}):[""];""===e[0]&&(e[0]=1<e.length?"ISO 2022 IR 6":"ISO_IR 6");if(e.some(function(e){return void 0===i[e]}))throw new Error("Invalid specific character set specified.");return function(e){for(var n=1<e.length,t=[],o=0;o<e.length;o++){var r=e[o];t.includes(r)||t.push(n?r.replace("ISO_IR","ISO 2022 IR"):r)}return t}(e=function(e){var n=i[e[0]];return!n.multiByte||n.extension?e.filter(function(e){return!i[e].multiByte||i[e].extension}):[e[0]]}(e))}(t);if(1===t.length&&!t[0].startsWith("ISO 2022"))return e(i[t[0]].encoding,o);r=r||{};return function(e,n,t,o){var r="";if(!n||0===n.length)return r;var i={G0:e[0].elements.find(function(e){return"G0"===e.codeElement}),G1:e[0].elements.find(function(e){return"G1"===e.codeElement})},c=Object.assign({},i),s=0,u=void 0,d=0;for(;d<n.length;){u=u||h(n[s],c);var a=function(e,n,t,o,r,i){for(var c=n;c<e.length;c+=t.bytesPerCodePoint){if(e[c]===l)return{escapeSequence:!0,index:c};1===t.bytesPerCodePoint&&i.includes(e[c])&&Object.assign(o,r);var s=h(e[c],o);if(t&&s!==t)return{characterSet:s,index:c}}return{index:e.length}}(n,s,u,c,i,t);d=a.index,s<d&&(r=o(r,u,n,s,d)),s=d,u=a.characterSet,a.escapeSequence&&(a=function(e,n,t){for(var o=0;o<t.length;o++)for(var r=t[o],i=0;i<r.elements.length;i++){var c=r.elements[i];if(function(e,n,t){for(var o=0;o<e.length;o++){if(t+o>=n.length)return!1;if(n[t+o]!==e[o])return!1}return!0}(c.escapeSequence,e,n))return c}throw new Error("Unknown escape sequence encountered at byte "+n)}(n,d,e),c[a.codeElement]=a,s+=a.escapeSequence.length)}return r}(t.map(function(e){return i[e]}),o,function(e){var n=(e||"").trim().toUpperCase(),e=[c,s,u,d];["UT","ST","LT"].includes(n)||e.push(a);"PN"===n&&(e.push(f),e.push(I));return e}(r.vr),n)}var p={};function y(e,n){var t=p[e];return t||(t=new TextDecoder(e),p[e]=t="shift-jis"===e?{textDecoder:t,decode:function(e){return S(t.decode(e))}}:t),t.decode(n)}function P(o,r){return new Promise(function(e){var n=new FileReader;n.onload="shift-jis"===o?function(){return e(S(n.result))}:function(){return e(n.result)};var t=new Blob([r]);n.readAsText(t,o)})}function h(e,n){return 127<e&&n.G1?n.G1:n.G0||(n.G1&&1===n.G1.bytesPerCodePoint?n.G1:i["ISO 2022 IR 6"].elements[0])}function b(e,n,t,o){var r=void 0;return e.isJISX0212?r=function(e,n,t){var o=t-n;if(o%2!=0)throw new Error("JIS X string with a character not having exactly two bytes!");for(var r=new Uint8Array(o+o/2),i=0,c=n;c<t;c+=2)r[i++]=143,r[i++]=128|e[c],r[i++]=128|e[c+1];return r}(n,t,o):((r=new Uint8Array(o-t)).set(new Uint8Array(n.buffer,n.byteOffset+t,o-t)),e.setHighBit&&function(e){for(var n=0;n<e.length;n++)e[n]|=128}(r)),r}return e})()});
|
|
|
|
},{}],3:[function(require,module,exports){
|
|
'use strict'
|
|
|
|
exports.byteLength = byteLength
|
|
exports.toByteArray = toByteArray
|
|
exports.fromByteArray = fromByteArray
|
|
|
|
var lookup = []
|
|
var revLookup = []
|
|
var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array
|
|
|
|
var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
|
|
for (var i = 0, len = code.length; i < len; ++i) {
|
|
lookup[i] = code[i]
|
|
revLookup[code.charCodeAt(i)] = i
|
|
}
|
|
|
|
// Support decoding URL-safe base64 strings, as Node.js does.
|
|
// See: https://en.wikipedia.org/wiki/Base64#URL_applications
|
|
revLookup['-'.charCodeAt(0)] = 62
|
|
revLookup['_'.charCodeAt(0)] = 63
|
|
|
|
function getLens (b64) {
|
|
var len = b64.length
|
|
|
|
if (len % 4 > 0) {
|
|
throw new Error('Invalid string. Length must be a multiple of 4')
|
|
}
|
|
|
|
// Trim off extra bytes after placeholder bytes are found
|
|
// See: https://github.com/beatgammit/base64-js/issues/42
|
|
var validLen = b64.indexOf('=')
|
|
if (validLen === -1) validLen = len
|
|
|
|
var placeHoldersLen = validLen === len
|
|
? 0
|
|
: 4 - (validLen % 4)
|
|
|
|
return [validLen, placeHoldersLen]
|
|
}
|
|
|
|
// base64 is 4/3 + up to two characters of the original data
|
|
function byteLength (b64) {
|
|
var lens = getLens(b64)
|
|
var validLen = lens[0]
|
|
var placeHoldersLen = lens[1]
|
|
return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
|
|
}
|
|
|
|
function _byteLength (b64, validLen, placeHoldersLen) {
|
|
return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
|
|
}
|
|
|
|
function toByteArray (b64) {
|
|
var tmp
|
|
var lens = getLens(b64)
|
|
var validLen = lens[0]
|
|
var placeHoldersLen = lens[1]
|
|
|
|
var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))
|
|
|
|
var curByte = 0
|
|
|
|
// if there are placeholders, only get up to the last complete 4 chars
|
|
var len = placeHoldersLen > 0
|
|
? validLen - 4
|
|
: validLen
|
|
|
|
var i
|
|
for (i = 0; i < len; i += 4) {
|
|
tmp =
|
|
(revLookup[b64.charCodeAt(i)] << 18) |
|
|
(revLookup[b64.charCodeAt(i + 1)] << 12) |
|
|
(revLookup[b64.charCodeAt(i + 2)] << 6) |
|
|
revLookup[b64.charCodeAt(i + 3)]
|
|
arr[curByte++] = (tmp >> 16) & 0xFF
|
|
arr[curByte++] = (tmp >> 8) & 0xFF
|
|
arr[curByte++] = tmp & 0xFF
|
|
}
|
|
|
|
if (placeHoldersLen === 2) {
|
|
tmp =
|
|
(revLookup[b64.charCodeAt(i)] << 2) |
|
|
(revLookup[b64.charCodeAt(i + 1)] >> 4)
|
|
arr[curByte++] = tmp & 0xFF
|
|
}
|
|
|
|
if (placeHoldersLen === 1) {
|
|
tmp =
|
|
(revLookup[b64.charCodeAt(i)] << 10) |
|
|
(revLookup[b64.charCodeAt(i + 1)] << 4) |
|
|
(revLookup[b64.charCodeAt(i + 2)] >> 2)
|
|
arr[curByte++] = (tmp >> 8) & 0xFF
|
|
arr[curByte++] = tmp & 0xFF
|
|
}
|
|
|
|
return arr
|
|
}
|
|
|
|
function tripletToBase64 (num) {
|
|
return lookup[num >> 18 & 0x3F] +
|
|
lookup[num >> 12 & 0x3F] +
|
|
lookup[num >> 6 & 0x3F] +
|
|
lookup[num & 0x3F]
|
|
}
|
|
|
|
function encodeChunk (uint8, start, end) {
|
|
var tmp
|
|
var output = []
|
|
for (var i = start; i < end; i += 3) {
|
|
tmp =
|
|
((uint8[i] << 16) & 0xFF0000) +
|
|
((uint8[i + 1] << 8) & 0xFF00) +
|
|
(uint8[i + 2] & 0xFF)
|
|
output.push(tripletToBase64(tmp))
|
|
}
|
|
return output.join('')
|
|
}
|
|
|
|
function fromByteArray (uint8) {
|
|
var tmp
|
|
var len = uint8.length
|
|
var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
|
|
var parts = []
|
|
var maxChunkLength = 16383 // must be multiple of 3
|
|
|
|
// go through the array every three bytes, we'll deal with trailing stuff later
|
|
for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
|
|
parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)))
|
|
}
|
|
|
|
// pad the end with zeros, but make sure to not forget the extra bytes
|
|
if (extraBytes === 1) {
|
|
tmp = uint8[len - 1]
|
|
parts.push(
|
|
lookup[tmp >> 2] +
|
|
lookup[(tmp << 4) & 0x3F] +
|
|
'=='
|
|
)
|
|
} else if (extraBytes === 2) {
|
|
tmp = (uint8[len - 2] << 8) + uint8[len - 1]
|
|
parts.push(
|
|
lookup[tmp >> 10] +
|
|
lookup[(tmp >> 4) & 0x3F] +
|
|
lookup[(tmp << 2) & 0x3F] +
|
|
'='
|
|
)
|
|
}
|
|
|
|
return parts.join('')
|
|
}
|
|
|
|
},{}],4:[function(require,module,exports){
|
|
(function (Buffer){(function (){
|
|
/*!
|
|
* The buffer module from node.js, for the browser.
|
|
*
|
|
* @author Feross Aboukhadijeh <https://feross.org>
|
|
* @license MIT
|
|
*/
|
|
/* eslint-disable no-proto */
|
|
|
|
'use strict'
|
|
|
|
var base64 = require('base64-js')
|
|
var ieee754 = require('ieee754')
|
|
|
|
exports.Buffer = Buffer
|
|
exports.SlowBuffer = SlowBuffer
|
|
exports.INSPECT_MAX_BYTES = 50
|
|
|
|
var K_MAX_LENGTH = 0x7fffffff
|
|
exports.kMaxLength = K_MAX_LENGTH
|
|
|
|
/**
|
|
* If `Buffer.TYPED_ARRAY_SUPPORT`:
|
|
* === true Use Uint8Array implementation (fastest)
|
|
* === false Print warning and recommend using `buffer` v4.x which has an Object
|
|
* implementation (most compatible, even IE6)
|
|
*
|
|
* Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
|
|
* Opera 11.6+, iOS 4.2+.
|
|
*
|
|
* We report that the browser does not support typed arrays if the are not subclassable
|
|
* using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array`
|
|
* (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support
|
|
* for __proto__ and has a buggy typed array implementation.
|
|
*/
|
|
Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport()
|
|
|
|
if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' &&
|
|
typeof console.error === 'function') {
|
|
console.error(
|
|
'This browser lacks typed array (Uint8Array) support which is required by ' +
|
|
'`buffer` v5.x. Use `buffer` v4.x if you require old browser support.'
|
|
)
|
|
}
|
|
|
|
function typedArraySupport () {
|
|
// Can typed array instances can be augmented?
|
|
try {
|
|
var arr = new Uint8Array(1)
|
|
arr.__proto__ = { __proto__: Uint8Array.prototype, foo: function () { return 42 } }
|
|
return arr.foo() === 42
|
|
} catch (e) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
Object.defineProperty(Buffer.prototype, 'parent', {
|
|
enumerable: true,
|
|
get: function () {
|
|
if (!Buffer.isBuffer(this)) return undefined
|
|
return this.buffer
|
|
}
|
|
})
|
|
|
|
Object.defineProperty(Buffer.prototype, 'offset', {
|
|
enumerable: true,
|
|
get: function () {
|
|
if (!Buffer.isBuffer(this)) return undefined
|
|
return this.byteOffset
|
|
}
|
|
})
|
|
|
|
function createBuffer (length) {
|
|
if (length > K_MAX_LENGTH) {
|
|
throw new RangeError('The value "' + length + '" is invalid for option "size"')
|
|
}
|
|
// Return an augmented `Uint8Array` instance
|
|
var buf = new Uint8Array(length)
|
|
buf.__proto__ = Buffer.prototype
|
|
return buf
|
|
}
|
|
|
|
/**
|
|
* The Buffer constructor returns instances of `Uint8Array` that have their
|
|
* prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of
|
|
* `Uint8Array`, so the returned instances will have all the node `Buffer` methods
|
|
* and the `Uint8Array` methods. Square bracket notation works as expected -- it
|
|
* returns a single octet.
|
|
*
|
|
* The `Uint8Array` prototype remains unmodified.
|
|
*/
|
|
|
|
function Buffer (arg, encodingOrOffset, length) {
|
|
// Common case.
|
|
if (typeof arg === 'number') {
|
|
if (typeof encodingOrOffset === 'string') {
|
|
throw new TypeError(
|
|
'The "string" argument must be of type string. Received type number'
|
|
)
|
|
}
|
|
return allocUnsafe(arg)
|
|
}
|
|
return from(arg, encodingOrOffset, length)
|
|
}
|
|
|
|
// Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97
|
|
if (typeof Symbol !== 'undefined' && Symbol.species != null &&
|
|
Buffer[Symbol.species] === Buffer) {
|
|
Object.defineProperty(Buffer, Symbol.species, {
|
|
value: null,
|
|
configurable: true,
|
|
enumerable: false,
|
|
writable: false
|
|
})
|
|
}
|
|
|
|
Buffer.poolSize = 8192 // not used by this implementation
|
|
|
|
function from (value, encodingOrOffset, length) {
|
|
if (typeof value === 'string') {
|
|
return fromString(value, encodingOrOffset)
|
|
}
|
|
|
|
if (ArrayBuffer.isView(value)) {
|
|
return fromArrayLike(value)
|
|
}
|
|
|
|
if (value == null) {
|
|
throw TypeError(
|
|
'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' +
|
|
'or Array-like Object. Received type ' + (typeof value)
|
|
)
|
|
}
|
|
|
|
if (isInstance(value, ArrayBuffer) ||
|
|
(value && isInstance(value.buffer, ArrayBuffer))) {
|
|
return fromArrayBuffer(value, encodingOrOffset, length)
|
|
}
|
|
|
|
if (typeof value === 'number') {
|
|
throw new TypeError(
|
|
'The "value" argument must not be of type number. Received type number'
|
|
)
|
|
}
|
|
|
|
var valueOf = value.valueOf && value.valueOf()
|
|
if (valueOf != null && valueOf !== value) {
|
|
return Buffer.from(valueOf, encodingOrOffset, length)
|
|
}
|
|
|
|
var b = fromObject(value)
|
|
if (b) return b
|
|
|
|
if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null &&
|
|
typeof value[Symbol.toPrimitive] === 'function') {
|
|
return Buffer.from(
|
|
value[Symbol.toPrimitive]('string'), encodingOrOffset, length
|
|
)
|
|
}
|
|
|
|
throw new TypeError(
|
|
'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' +
|
|
'or Array-like Object. Received type ' + (typeof value)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Functionally equivalent to Buffer(arg, encoding) but throws a TypeError
|
|
* if value is a number.
|
|
* Buffer.from(str[, encoding])
|
|
* Buffer.from(array)
|
|
* Buffer.from(buffer)
|
|
* Buffer.from(arrayBuffer[, byteOffset[, length]])
|
|
**/
|
|
Buffer.from = function (value, encodingOrOffset, length) {
|
|
return from(value, encodingOrOffset, length)
|
|
}
|
|
|
|
// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug:
|
|
// https://github.com/feross/buffer/pull/148
|
|
Buffer.prototype.__proto__ = Uint8Array.prototype
|
|
Buffer.__proto__ = Uint8Array
|
|
|
|
function assertSize (size) {
|
|
if (typeof size !== 'number') {
|
|
throw new TypeError('"size" argument must be of type number')
|
|
} else if (size < 0) {
|
|
throw new RangeError('The value "' + size + '" is invalid for option "size"')
|
|
}
|
|
}
|
|
|
|
function alloc (size, fill, encoding) {
|
|
assertSize(size)
|
|
if (size <= 0) {
|
|
return createBuffer(size)
|
|
}
|
|
if (fill !== undefined) {
|
|
// Only pay attention to encoding if it's a string. This
|
|
// prevents accidentally sending in a number that would
|
|
// be interpretted as a start offset.
|
|
return typeof encoding === 'string'
|
|
? createBuffer(size).fill(fill, encoding)
|
|
: createBuffer(size).fill(fill)
|
|
}
|
|
return createBuffer(size)
|
|
}
|
|
|
|
/**
|
|
* Creates a new filled Buffer instance.
|
|
* alloc(size[, fill[, encoding]])
|
|
**/
|
|
Buffer.alloc = function (size, fill, encoding) {
|
|
return alloc(size, fill, encoding)
|
|
}
|
|
|
|
function allocUnsafe (size) {
|
|
assertSize(size)
|
|
return createBuffer(size < 0 ? 0 : checked(size) | 0)
|
|
}
|
|
|
|
/**
|
|
* Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.
|
|
* */
|
|
Buffer.allocUnsafe = function (size) {
|
|
return allocUnsafe(size)
|
|
}
|
|
/**
|
|
* Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.
|
|
*/
|
|
Buffer.allocUnsafeSlow = function (size) {
|
|
return allocUnsafe(size)
|
|
}
|
|
|
|
function fromString (string, encoding) {
|
|
if (typeof encoding !== 'string' || encoding === '') {
|
|
encoding = 'utf8'
|
|
}
|
|
|
|
if (!Buffer.isEncoding(encoding)) {
|
|
throw new TypeError('Unknown encoding: ' + encoding)
|
|
}
|
|
|
|
var length = byteLength(string, encoding) | 0
|
|
var buf = createBuffer(length)
|
|
|
|
var actual = buf.write(string, encoding)
|
|
|
|
if (actual !== length) {
|
|
// Writing a hex string, for example, that contains invalid characters will
|
|
// cause everything after the first invalid character to be ignored. (e.g.
|
|
// 'abxxcd' will be treated as 'ab')
|
|
buf = buf.slice(0, actual)
|
|
}
|
|
|
|
return buf
|
|
}
|
|
|
|
function fromArrayLike (array) {
|
|
var length = array.length < 0 ? 0 : checked(array.length) | 0
|
|
var buf = createBuffer(length)
|
|
for (var i = 0; i < length; i += 1) {
|
|
buf[i] = array[i] & 255
|
|
}
|
|
return buf
|
|
}
|
|
|
|
function fromArrayBuffer (array, byteOffset, length) {
|
|
if (byteOffset < 0 || array.byteLength < byteOffset) {
|
|
throw new RangeError('"offset" is outside of buffer bounds')
|
|
}
|
|
|
|
if (array.byteLength < byteOffset + (length || 0)) {
|
|
throw new RangeError('"length" is outside of buffer bounds')
|
|
}
|
|
|
|
var buf
|
|
if (byteOffset === undefined && length === undefined) {
|
|
buf = new Uint8Array(array)
|
|
} else if (length === undefined) {
|
|
buf = new Uint8Array(array, byteOffset)
|
|
} else {
|
|
buf = new Uint8Array(array, byteOffset, length)
|
|
}
|
|
|
|
// Return an augmented `Uint8Array` instance
|
|
buf.__proto__ = Buffer.prototype
|
|
return buf
|
|
}
|
|
|
|
function fromObject (obj) {
|
|
if (Buffer.isBuffer(obj)) {
|
|
var len = checked(obj.length) | 0
|
|
var buf = createBuffer(len)
|
|
|
|
if (buf.length === 0) {
|
|
return buf
|
|
}
|
|
|
|
obj.copy(buf, 0, 0, len)
|
|
return buf
|
|
}
|
|
|
|
if (obj.length !== undefined) {
|
|
if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) {
|
|
return createBuffer(0)
|
|
}
|
|
return fromArrayLike(obj)
|
|
}
|
|
|
|
if (obj.type === 'Buffer' && Array.isArray(obj.data)) {
|
|
return fromArrayLike(obj.data)
|
|
}
|
|
}
|
|
|
|
function checked (length) {
|
|
// Note: cannot use `length < K_MAX_LENGTH` here because that fails when
|
|
// length is NaN (which is otherwise coerced to zero.)
|
|
if (length >= K_MAX_LENGTH) {
|
|
throw new RangeError('Attempt to allocate Buffer larger than maximum ' +
|
|
'size: 0x' + K_MAX_LENGTH.toString(16) + ' bytes')
|
|
}
|
|
return length | 0
|
|
}
|
|
|
|
function SlowBuffer (length) {
|
|
if (+length != length) { // eslint-disable-line eqeqeq
|
|
length = 0
|
|
}
|
|
return Buffer.alloc(+length)
|
|
}
|
|
|
|
Buffer.isBuffer = function isBuffer (b) {
|
|
return b != null && b._isBuffer === true &&
|
|
b !== Buffer.prototype // so Buffer.isBuffer(Buffer.prototype) will be false
|
|
}
|
|
|
|
Buffer.compare = function compare (a, b) {
|
|
if (isInstance(a, Uint8Array)) a = Buffer.from(a, a.offset, a.byteLength)
|
|
if (isInstance(b, Uint8Array)) b = Buffer.from(b, b.offset, b.byteLength)
|
|
if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {
|
|
throw new TypeError(
|
|
'The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array'
|
|
)
|
|
}
|
|
|
|
if (a === b) return 0
|
|
|
|
var x = a.length
|
|
var y = b.length
|
|
|
|
for (var i = 0, len = Math.min(x, y); i < len; ++i) {
|
|
if (a[i] !== b[i]) {
|
|
x = a[i]
|
|
y = b[i]
|
|
break
|
|
}
|
|
}
|
|
|
|
if (x < y) return -1
|
|
if (y < x) return 1
|
|
return 0
|
|
}
|
|
|
|
Buffer.isEncoding = function isEncoding (encoding) {
|
|
switch (String(encoding).toLowerCase()) {
|
|
case 'hex':
|
|
case 'utf8':
|
|
case 'utf-8':
|
|
case 'ascii':
|
|
case 'latin1':
|
|
case 'binary':
|
|
case 'base64':
|
|
case 'ucs2':
|
|
case 'ucs-2':
|
|
case 'utf16le':
|
|
case 'utf-16le':
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
Buffer.concat = function concat (list, length) {
|
|
if (!Array.isArray(list)) {
|
|
throw new TypeError('"list" argument must be an Array of Buffers')
|
|
}
|
|
|
|
if (list.length === 0) {
|
|
return Buffer.alloc(0)
|
|
}
|
|
|
|
var i
|
|
if (length === undefined) {
|
|
length = 0
|
|
for (i = 0; i < list.length; ++i) {
|
|
length += list[i].length
|
|
}
|
|
}
|
|
|
|
var buffer = Buffer.allocUnsafe(length)
|
|
var pos = 0
|
|
for (i = 0; i < list.length; ++i) {
|
|
var buf = list[i]
|
|
if (isInstance(buf, Uint8Array)) {
|
|
buf = Buffer.from(buf)
|
|
}
|
|
if (!Buffer.isBuffer(buf)) {
|
|
throw new TypeError('"list" argument must be an Array of Buffers')
|
|
}
|
|
buf.copy(buffer, pos)
|
|
pos += buf.length
|
|
}
|
|
return buffer
|
|
}
|
|
|
|
function byteLength (string, encoding) {
|
|
if (Buffer.isBuffer(string)) {
|
|
return string.length
|
|
}
|
|
if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) {
|
|
return string.byteLength
|
|
}
|
|
if (typeof string !== 'string') {
|
|
throw new TypeError(
|
|
'The "string" argument must be one of type string, Buffer, or ArrayBuffer. ' +
|
|
'Received type ' + typeof string
|
|
)
|
|
}
|
|
|
|
var len = string.length
|
|
var mustMatch = (arguments.length > 2 && arguments[2] === true)
|
|
if (!mustMatch && len === 0) return 0
|
|
|
|
// Use a for loop to avoid recursion
|
|
var loweredCase = false
|
|
for (;;) {
|
|
switch (encoding) {
|
|
case 'ascii':
|
|
case 'latin1':
|
|
case 'binary':
|
|
return len
|
|
case 'utf8':
|
|
case 'utf-8':
|
|
return utf8ToBytes(string).length
|
|
case 'ucs2':
|
|
case 'ucs-2':
|
|
case 'utf16le':
|
|
case 'utf-16le':
|
|
return len * 2
|
|
case 'hex':
|
|
return len >>> 1
|
|
case 'base64':
|
|
return base64ToBytes(string).length
|
|
default:
|
|
if (loweredCase) {
|
|
return mustMatch ? -1 : utf8ToBytes(string).length // assume utf8
|
|
}
|
|
encoding = ('' + encoding).toLowerCase()
|
|
loweredCase = true
|
|
}
|
|
}
|
|
}
|
|
Buffer.byteLength = byteLength
|
|
|
|
function slowToString (encoding, start, end) {
|
|
var loweredCase = false
|
|
|
|
// No need to verify that "this.length <= MAX_UINT32" since it's a read-only
|
|
// property of a typed array.
|
|
|
|
// This behaves neither like String nor Uint8Array in that we set start/end
|
|
// to their upper/lower bounds if the value passed is out of range.
|
|
// undefined is handled specially as per ECMA-262 6th Edition,
|
|
// Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.
|
|
if (start === undefined || start < 0) {
|
|
start = 0
|
|
}
|
|
// Return early if start > this.length. Done here to prevent potential uint32
|
|
// coercion fail below.
|
|
if (start > this.length) {
|
|
return ''
|
|
}
|
|
|
|
if (end === undefined || end > this.length) {
|
|
end = this.length
|
|
}
|
|
|
|
if (end <= 0) {
|
|
return ''
|
|
}
|
|
|
|
// Force coersion to uint32. This will also coerce falsey/NaN values to 0.
|
|
end >>>= 0
|
|
start >>>= 0
|
|
|
|
if (end <= start) {
|
|
return ''
|
|
}
|
|
|
|
if (!encoding) encoding = 'utf8'
|
|
|
|
while (true) {
|
|
switch (encoding) {
|
|
case 'hex':
|
|
return hexSlice(this, start, end)
|
|
|
|
case 'utf8':
|
|
case 'utf-8':
|
|
return utf8Slice(this, start, end)
|
|
|
|
case 'ascii':
|
|
return asciiSlice(this, start, end)
|
|
|
|
case 'latin1':
|
|
case 'binary':
|
|
return latin1Slice(this, start, end)
|
|
|
|
case 'base64':
|
|
return base64Slice(this, start, end)
|
|
|
|
case 'ucs2':
|
|
case 'ucs-2':
|
|
case 'utf16le':
|
|
case 'utf-16le':
|
|
return utf16leSlice(this, start, end)
|
|
|
|
default:
|
|
if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
|
|
encoding = (encoding + '').toLowerCase()
|
|
loweredCase = true
|
|
}
|
|
}
|
|
}
|
|
|
|
// This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package)
|
|
// to detect a Buffer instance. It's not possible to use `instanceof Buffer`
|
|
// reliably in a browserify context because there could be multiple different
|
|
// copies of the 'buffer' package in use. This method works even for Buffer
|
|
// instances that were created from another copy of the `buffer` package.
|
|
// See: https://github.com/feross/buffer/issues/154
|
|
Buffer.prototype._isBuffer = true
|
|
|
|
function swap (b, n, m) {
|
|
var i = b[n]
|
|
b[n] = b[m]
|
|
b[m] = i
|
|
}
|
|
|
|
Buffer.prototype.swap16 = function swap16 () {
|
|
var len = this.length
|
|
if (len % 2 !== 0) {
|
|
throw new RangeError('Buffer size must be a multiple of 16-bits')
|
|
}
|
|
for (var i = 0; i < len; i += 2) {
|
|
swap(this, i, i + 1)
|
|
}
|
|
return this
|
|
}
|
|
|
|
Buffer.prototype.swap32 = function swap32 () {
|
|
var len = this.length
|
|
if (len % 4 !== 0) {
|
|
throw new RangeError('Buffer size must be a multiple of 32-bits')
|
|
}
|
|
for (var i = 0; i < len; i += 4) {
|
|
swap(this, i, i + 3)
|
|
swap(this, i + 1, i + 2)
|
|
}
|
|
return this
|
|
}
|
|
|
|
Buffer.prototype.swap64 = function swap64 () {
|
|
var len = this.length
|
|
if (len % 8 !== 0) {
|
|
throw new RangeError('Buffer size must be a multiple of 64-bits')
|
|
}
|
|
for (var i = 0; i < len; i += 8) {
|
|
swap(this, i, i + 7)
|
|
swap(this, i + 1, i + 6)
|
|
swap(this, i + 2, i + 5)
|
|
swap(this, i + 3, i + 4)
|
|
}
|
|
return this
|
|
}
|
|
|
|
Buffer.prototype.toString = function toString () {
|
|
var length = this.length
|
|
if (length === 0) return ''
|
|
if (arguments.length === 0) return utf8Slice(this, 0, length)
|
|
return slowToString.apply(this, arguments)
|
|
}
|
|
|
|
Buffer.prototype.toLocaleString = Buffer.prototype.toString
|
|
|
|
Buffer.prototype.equals = function equals (b) {
|
|
if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
|
|
if (this === b) return true
|
|
return Buffer.compare(this, b) === 0
|
|
}
|
|
|
|
Buffer.prototype.inspect = function inspect () {
|
|
var str = ''
|
|
var max = exports.INSPECT_MAX_BYTES
|
|
str = this.toString('hex', 0, max).replace(/(.{2})/g, '$1 ').trim()
|
|
if (this.length > max) str += ' ... '
|
|
return '<Buffer ' + str + '>'
|
|
}
|
|
|
|
Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {
|
|
if (isInstance(target, Uint8Array)) {
|
|
target = Buffer.from(target, target.offset, target.byteLength)
|
|
}
|
|
if (!Buffer.isBuffer(target)) {
|
|
throw new TypeError(
|
|
'The "target" argument must be one of type Buffer or Uint8Array. ' +
|
|
'Received type ' + (typeof target)
|
|
)
|
|
}
|
|
|
|
if (start === undefined) {
|
|
start = 0
|
|
}
|
|
if (end === undefined) {
|
|
end = target ? target.length : 0
|
|
}
|
|
if (thisStart === undefined) {
|
|
thisStart = 0
|
|
}
|
|
if (thisEnd === undefined) {
|
|
thisEnd = this.length
|
|
}
|
|
|
|
if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {
|
|
throw new RangeError('out of range index')
|
|
}
|
|
|
|
if (thisStart >= thisEnd && start >= end) {
|
|
return 0
|
|
}
|
|
if (thisStart >= thisEnd) {
|
|
return -1
|
|
}
|
|
if (start >= end) {
|
|
return 1
|
|
}
|
|
|
|
start >>>= 0
|
|
end >>>= 0
|
|
thisStart >>>= 0
|
|
thisEnd >>>= 0
|
|
|
|
if (this === target) return 0
|
|
|
|
var x = thisEnd - thisStart
|
|
var y = end - start
|
|
var len = Math.min(x, y)
|
|
|
|
var thisCopy = this.slice(thisStart, thisEnd)
|
|
var targetCopy = target.slice(start, end)
|
|
|
|
for (var i = 0; i < len; ++i) {
|
|
if (thisCopy[i] !== targetCopy[i]) {
|
|
x = thisCopy[i]
|
|
y = targetCopy[i]
|
|
break
|
|
}
|
|
}
|
|
|
|
if (x < y) return -1
|
|
if (y < x) return 1
|
|
return 0
|
|
}
|
|
|
|
// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,
|
|
// OR the last index of `val` in `buffer` at offset <= `byteOffset`.
|
|
//
|
|
// Arguments:
|
|
// - buffer - a Buffer to search
|
|
// - val - a string, Buffer, or number
|
|
// - byteOffset - an index into `buffer`; will be clamped to an int32
|
|
// - encoding - an optional encoding, relevant is val is a string
|
|
// - dir - true for indexOf, false for lastIndexOf
|
|
function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {
|
|
// Empty buffer means no match
|
|
if (buffer.length === 0) return -1
|
|
|
|
// Normalize byteOffset
|
|
if (typeof byteOffset === 'string') {
|
|
encoding = byteOffset
|
|
byteOffset = 0
|
|
} else if (byteOffset > 0x7fffffff) {
|
|
byteOffset = 0x7fffffff
|
|
} else if (byteOffset < -0x80000000) {
|
|
byteOffset = -0x80000000
|
|
}
|
|
byteOffset = +byteOffset // Coerce to Number.
|
|
if (numberIsNaN(byteOffset)) {
|
|
// byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer
|
|
byteOffset = dir ? 0 : (buffer.length - 1)
|
|
}
|
|
|
|
// Normalize byteOffset: negative offsets start from the end of the buffer
|
|
if (byteOffset < 0) byteOffset = buffer.length + byteOffset
|
|
if (byteOffset >= buffer.length) {
|
|
if (dir) return -1
|
|
else byteOffset = buffer.length - 1
|
|
} else if (byteOffset < 0) {
|
|
if (dir) byteOffset = 0
|
|
else return -1
|
|
}
|
|
|
|
// Normalize val
|
|
if (typeof val === 'string') {
|
|
val = Buffer.from(val, encoding)
|
|
}
|
|
|
|
// Finally, search either indexOf (if dir is true) or lastIndexOf
|
|
if (Buffer.isBuffer(val)) {
|
|
// Special case: looking for empty string/buffer always fails
|
|
if (val.length === 0) {
|
|
return -1
|
|
}
|
|
return arrayIndexOf(buffer, val, byteOffset, encoding, dir)
|
|
} else if (typeof val === 'number') {
|
|
val = val & 0xFF // Search for a byte value [0-255]
|
|
if (typeof Uint8Array.prototype.indexOf === 'function') {
|
|
if (dir) {
|
|
return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)
|
|
} else {
|
|
return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset)
|
|
}
|
|
}
|
|
return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir)
|
|
}
|
|
|
|
throw new TypeError('val must be string, number or Buffer')
|
|
}
|
|
|
|
function arrayIndexOf (arr, val, byteOffset, encoding, dir) {
|
|
var indexSize = 1
|
|
var arrLength = arr.length
|
|
var valLength = val.length
|
|
|
|
if (encoding !== undefined) {
|
|
encoding = String(encoding).toLowerCase()
|
|
if (encoding === 'ucs2' || encoding === 'ucs-2' ||
|
|
encoding === 'utf16le' || encoding === 'utf-16le') {
|
|
if (arr.length < 2 || val.length < 2) {
|
|
return -1
|
|
}
|
|
indexSize = 2
|
|
arrLength /= 2
|
|
valLength /= 2
|
|
byteOffset /= 2
|
|
}
|
|
}
|
|
|
|
function read (buf, i) {
|
|
if (indexSize === 1) {
|
|
return buf[i]
|
|
} else {
|
|
return buf.readUInt16BE(i * indexSize)
|
|
}
|
|
}
|
|
|
|
var i
|
|
if (dir) {
|
|
var foundIndex = -1
|
|
for (i = byteOffset; i < arrLength; i++) {
|
|
if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {
|
|
if (foundIndex === -1) foundIndex = i
|
|
if (i - foundIndex + 1 === valLength) return foundIndex * indexSize
|
|
} else {
|
|
if (foundIndex !== -1) i -= i - foundIndex
|
|
foundIndex = -1
|
|
}
|
|
}
|
|
} else {
|
|
if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength
|
|
for (i = byteOffset; i >= 0; i--) {
|
|
var found = true
|
|
for (var j = 0; j < valLength; j++) {
|
|
if (read(arr, i + j) !== read(val, j)) {
|
|
found = false
|
|
break
|
|
}
|
|
}
|
|
if (found) return i
|
|
}
|
|
}
|
|
|
|
return -1
|
|
}
|
|
|
|
Buffer.prototype.includes = function includes (val, byteOffset, encoding) {
|
|
return this.indexOf(val, byteOffset, encoding) !== -1
|
|
}
|
|
|
|
Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {
|
|
return bidirectionalIndexOf(this, val, byteOffset, encoding, true)
|
|
}
|
|
|
|
Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) {
|
|
return bidirectionalIndexOf(this, val, byteOffset, encoding, false)
|
|
}
|
|
|
|
function hexWrite (buf, string, offset, length) {
|
|
offset = Number(offset) || 0
|
|
var remaining = buf.length - offset
|
|
if (!length) {
|
|
length = remaining
|
|
} else {
|
|
length = Number(length)
|
|
if (length > remaining) {
|
|
length = remaining
|
|
}
|
|
}
|
|
|
|
var strLen = string.length
|
|
|
|
if (length > strLen / 2) {
|
|
length = strLen / 2
|
|
}
|
|
for (var i = 0; i < length; ++i) {
|
|
var parsed = parseInt(string.substr(i * 2, 2), 16)
|
|
if (numberIsNaN(parsed)) return i
|
|
buf[offset + i] = parsed
|
|
}
|
|
return i
|
|
}
|
|
|
|
function utf8Write (buf, string, offset, length) {
|
|
return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)
|
|
}
|
|
|
|
function asciiWrite (buf, string, offset, length) {
|
|
return blitBuffer(asciiToBytes(string), buf, offset, length)
|
|
}
|
|
|
|
function latin1Write (buf, string, offset, length) {
|
|
return asciiWrite(buf, string, offset, length)
|
|
}
|
|
|
|
function base64Write (buf, string, offset, length) {
|
|
return blitBuffer(base64ToBytes(string), buf, offset, length)
|
|
}
|
|
|
|
function ucs2Write (buf, string, offset, length) {
|
|
return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)
|
|
}
|
|
|
|
Buffer.prototype.write = function write (string, offset, length, encoding) {
|
|
// Buffer#write(string)
|
|
if (offset === undefined) {
|
|
encoding = 'utf8'
|
|
length = this.length
|
|
offset = 0
|
|
// Buffer#write(string, encoding)
|
|
} else if (length === undefined && typeof offset === 'string') {
|
|
encoding = offset
|
|
length = this.length
|
|
offset = 0
|
|
// Buffer#write(string, offset[, length][, encoding])
|
|
} else if (isFinite(offset)) {
|
|
offset = offset >>> 0
|
|
if (isFinite(length)) {
|
|
length = length >>> 0
|
|
if (encoding === undefined) encoding = 'utf8'
|
|
} else {
|
|
encoding = length
|
|
length = undefined
|
|
}
|
|
} else {
|
|
throw new Error(
|
|
'Buffer.write(string, encoding, offset[, length]) is no longer supported'
|
|
)
|
|
}
|
|
|
|
var remaining = this.length - offset
|
|
if (length === undefined || length > remaining) length = remaining
|
|
|
|
if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {
|
|
throw new RangeError('Attempt to write outside buffer bounds')
|
|
}
|
|
|
|
if (!encoding) encoding = 'utf8'
|
|
|
|
var loweredCase = false
|
|
for (;;) {
|
|
switch (encoding) {
|
|
case 'hex':
|
|
return hexWrite(this, string, offset, length)
|
|
|
|
case 'utf8':
|
|
case 'utf-8':
|
|
return utf8Write(this, string, offset, length)
|
|
|
|
case 'ascii':
|
|
return asciiWrite(this, string, offset, length)
|
|
|
|
case 'latin1':
|
|
case 'binary':
|
|
return latin1Write(this, string, offset, length)
|
|
|
|
case 'base64':
|
|
// Warning: maxLength not taken into account in base64Write
|
|
return base64Write(this, string, offset, length)
|
|
|
|
case 'ucs2':
|
|
case 'ucs-2':
|
|
case 'utf16le':
|
|
case 'utf-16le':
|
|
return ucs2Write(this, string, offset, length)
|
|
|
|
default:
|
|
if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
|
|
encoding = ('' + encoding).toLowerCase()
|
|
loweredCase = true
|
|
}
|
|
}
|
|
}
|
|
|
|
Buffer.prototype.toJSON = function toJSON () {
|
|
return {
|
|
type: 'Buffer',
|
|
data: Array.prototype.slice.call(this._arr || this, 0)
|
|
}
|
|
}
|
|
|
|
function base64Slice (buf, start, end) {
|
|
if (start === 0 && end === buf.length) {
|
|
return base64.fromByteArray(buf)
|
|
} else {
|
|
return base64.fromByteArray(buf.slice(start, end))
|
|
}
|
|
}
|
|
|
|
function utf8Slice (buf, start, end) {
|
|
end = Math.min(buf.length, end)
|
|
var res = []
|
|
|
|
var i = start
|
|
while (i < end) {
|
|
var firstByte = buf[i]
|
|
var codePoint = null
|
|
var bytesPerSequence = (firstByte > 0xEF) ? 4
|
|
: (firstByte > 0xDF) ? 3
|
|
: (firstByte > 0xBF) ? 2
|
|
: 1
|
|
|
|
if (i + bytesPerSequence <= end) {
|
|
var secondByte, thirdByte, fourthByte, tempCodePoint
|
|
|
|
switch (bytesPerSequence) {
|
|
case 1:
|
|
if (firstByte < 0x80) {
|
|
codePoint = firstByte
|
|
}
|
|
break
|
|
case 2:
|
|
secondByte = buf[i + 1]
|
|
if ((secondByte & 0xC0) === 0x80) {
|
|
tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)
|
|
if (tempCodePoint > 0x7F) {
|
|
codePoint = tempCodePoint
|
|
}
|
|
}
|
|
break
|
|
case 3:
|
|
secondByte = buf[i + 1]
|
|
thirdByte = buf[i + 2]
|
|
if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {
|
|
tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)
|
|
if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {
|
|
codePoint = tempCodePoint
|
|
}
|
|
}
|
|
break
|
|
case 4:
|
|
secondByte = buf[i + 1]
|
|
thirdByte = buf[i + 2]
|
|
fourthByte = buf[i + 3]
|
|
if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {
|
|
tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)
|
|
if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {
|
|
codePoint = tempCodePoint
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (codePoint === null) {
|
|
// we did not generate a valid codePoint so insert a
|
|
// replacement char (U+FFFD) and advance only 1 byte
|
|
codePoint = 0xFFFD
|
|
bytesPerSequence = 1
|
|
} else if (codePoint > 0xFFFF) {
|
|
// encode to utf16 (surrogate pair dance)
|
|
codePoint -= 0x10000
|
|
res.push(codePoint >>> 10 & 0x3FF | 0xD800)
|
|
codePoint = 0xDC00 | codePoint & 0x3FF
|
|
}
|
|
|
|
res.push(codePoint)
|
|
i += bytesPerSequence
|
|
}
|
|
|
|
return decodeCodePointsArray(res)
|
|
}
|
|
|
|
// Based on http://stackoverflow.com/a/22747272/680742, the browser with
|
|
// the lowest limit is Chrome, with 0x10000 args.
|
|
// We go 1 magnitude less, for safety
|
|
var MAX_ARGUMENTS_LENGTH = 0x1000
|
|
|
|
function decodeCodePointsArray (codePoints) {
|
|
var len = codePoints.length
|
|
if (len <= MAX_ARGUMENTS_LENGTH) {
|
|
return String.fromCharCode.apply(String, codePoints) // avoid extra slice()
|
|
}
|
|
|
|
// Decode in chunks to avoid "call stack size exceeded".
|
|
var res = ''
|
|
var i = 0
|
|
while (i < len) {
|
|
res += String.fromCharCode.apply(
|
|
String,
|
|
codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)
|
|
)
|
|
}
|
|
return res
|
|
}
|
|
|
|
function asciiSlice (buf, start, end) {
|
|
var ret = ''
|
|
end = Math.min(buf.length, end)
|
|
|
|
for (var i = start; i < end; ++i) {
|
|
ret += String.fromCharCode(buf[i] & 0x7F)
|
|
}
|
|
return ret
|
|
}
|
|
|
|
function latin1Slice (buf, start, end) {
|
|
var ret = ''
|
|
end = Math.min(buf.length, end)
|
|
|
|
for (var i = start; i < end; ++i) {
|
|
ret += String.fromCharCode(buf[i])
|
|
}
|
|
return ret
|
|
}
|
|
|
|
function hexSlice (buf, start, end) {
|
|
var len = buf.length
|
|
|
|
if (!start || start < 0) start = 0
|
|
if (!end || end < 0 || end > len) end = len
|
|
|
|
var out = ''
|
|
for (var i = start; i < end; ++i) {
|
|
out += toHex(buf[i])
|
|
}
|
|
return out
|
|
}
|
|
|
|
function utf16leSlice (buf, start, end) {
|
|
var bytes = buf.slice(start, end)
|
|
var res = ''
|
|
for (var i = 0; i < bytes.length; i += 2) {
|
|
res += String.fromCharCode(bytes[i] + (bytes[i + 1] * 256))
|
|
}
|
|
return res
|
|
}
|
|
|
|
Buffer.prototype.slice = function slice (start, end) {
|
|
var len = this.length
|
|
start = ~~start
|
|
end = end === undefined ? len : ~~end
|
|
|
|
if (start < 0) {
|
|
start += len
|
|
if (start < 0) start = 0
|
|
} else if (start > len) {
|
|
start = len
|
|
}
|
|
|
|
if (end < 0) {
|
|
end += len
|
|
if (end < 0) end = 0
|
|
} else if (end > len) {
|
|
end = len
|
|
}
|
|
|
|
if (end < start) end = start
|
|
|
|
var newBuf = this.subarray(start, end)
|
|
// Return an augmented `Uint8Array` instance
|
|
newBuf.__proto__ = Buffer.prototype
|
|
return newBuf
|
|
}
|
|
|
|
/*
|
|
* Need to make sure that buffer isn't trying to write out of bounds.
|
|
*/
|
|
function checkOffset (offset, ext, length) {
|
|
if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')
|
|
if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')
|
|
}
|
|
|
|
Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {
|
|
offset = offset >>> 0
|
|
byteLength = byteLength >>> 0
|
|
if (!noAssert) checkOffset(offset, byteLength, this.length)
|
|
|
|
var val = this[offset]
|
|
var mul = 1
|
|
var i = 0
|
|
while (++i < byteLength && (mul *= 0x100)) {
|
|
val += this[offset + i] * mul
|
|
}
|
|
|
|
return val
|
|
}
|
|
|
|
Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {
|
|
offset = offset >>> 0
|
|
byteLength = byteLength >>> 0
|
|
if (!noAssert) {
|
|
checkOffset(offset, byteLength, this.length)
|
|
}
|
|
|
|
var val = this[offset + --byteLength]
|
|
var mul = 1
|
|
while (byteLength > 0 && (mul *= 0x100)) {
|
|
val += this[offset + --byteLength] * mul
|
|
}
|
|
|
|
return val
|
|
}
|
|
|
|
Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkOffset(offset, 1, this.length)
|
|
return this[offset]
|
|
}
|
|
|
|
Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkOffset(offset, 2, this.length)
|
|
return this[offset] | (this[offset + 1] << 8)
|
|
}
|
|
|
|
Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkOffset(offset, 2, this.length)
|
|
return (this[offset] << 8) | this[offset + 1]
|
|
}
|
|
|
|
Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkOffset(offset, 4, this.length)
|
|
|
|
return ((this[offset]) |
|
|
(this[offset + 1] << 8) |
|
|
(this[offset + 2] << 16)) +
|
|
(this[offset + 3] * 0x1000000)
|
|
}
|
|
|
|
Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkOffset(offset, 4, this.length)
|
|
|
|
return (this[offset] * 0x1000000) +
|
|
((this[offset + 1] << 16) |
|
|
(this[offset + 2] << 8) |
|
|
this[offset + 3])
|
|
}
|
|
|
|
Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {
|
|
offset = offset >>> 0
|
|
byteLength = byteLength >>> 0
|
|
if (!noAssert) checkOffset(offset, byteLength, this.length)
|
|
|
|
var val = this[offset]
|
|
var mul = 1
|
|
var i = 0
|
|
while (++i < byteLength && (mul *= 0x100)) {
|
|
val += this[offset + i] * mul
|
|
}
|
|
mul *= 0x80
|
|
|
|
if (val >= mul) val -= Math.pow(2, 8 * byteLength)
|
|
|
|
return val
|
|
}
|
|
|
|
Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {
|
|
offset = offset >>> 0
|
|
byteLength = byteLength >>> 0
|
|
if (!noAssert) checkOffset(offset, byteLength, this.length)
|
|
|
|
var i = byteLength
|
|
var mul = 1
|
|
var val = this[offset + --i]
|
|
while (i > 0 && (mul *= 0x100)) {
|
|
val += this[offset + --i] * mul
|
|
}
|
|
mul *= 0x80
|
|
|
|
if (val >= mul) val -= Math.pow(2, 8 * byteLength)
|
|
|
|
return val
|
|
}
|
|
|
|
Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) {
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkOffset(offset, 1, this.length)
|
|
if (!(this[offset] & 0x80)) return (this[offset])
|
|
return ((0xff - this[offset] + 1) * -1)
|
|
}
|
|
|
|
Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkOffset(offset, 2, this.length)
|
|
var val = this[offset] | (this[offset + 1] << 8)
|
|
return (val & 0x8000) ? val | 0xFFFF0000 : val
|
|
}
|
|
|
|
Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkOffset(offset, 2, this.length)
|
|
var val = this[offset + 1] | (this[offset] << 8)
|
|
return (val & 0x8000) ? val | 0xFFFF0000 : val
|
|
}
|
|
|
|
Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkOffset(offset, 4, this.length)
|
|
|
|
return (this[offset]) |
|
|
(this[offset + 1] << 8) |
|
|
(this[offset + 2] << 16) |
|
|
(this[offset + 3] << 24)
|
|
}
|
|
|
|
Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkOffset(offset, 4, this.length)
|
|
|
|
return (this[offset] << 24) |
|
|
(this[offset + 1] << 16) |
|
|
(this[offset + 2] << 8) |
|
|
(this[offset + 3])
|
|
}
|
|
|
|
Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkOffset(offset, 4, this.length)
|
|
return ieee754.read(this, offset, true, 23, 4)
|
|
}
|
|
|
|
Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkOffset(offset, 4, this.length)
|
|
return ieee754.read(this, offset, false, 23, 4)
|
|
}
|
|
|
|
Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkOffset(offset, 8, this.length)
|
|
return ieee754.read(this, offset, true, 52, 8)
|
|
}
|
|
|
|
Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkOffset(offset, 8, this.length)
|
|
return ieee754.read(this, offset, false, 52, 8)
|
|
}
|
|
|
|
function checkInt (buf, value, offset, ext, max, min) {
|
|
if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance')
|
|
if (value > max || value < min) throw new RangeError('"value" argument is out of bounds')
|
|
if (offset + ext > buf.length) throw new RangeError('Index out of range')
|
|
}
|
|
|
|
Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {
|
|
value = +value
|
|
offset = offset >>> 0
|
|
byteLength = byteLength >>> 0
|
|
if (!noAssert) {
|
|
var maxBytes = Math.pow(2, 8 * byteLength) - 1
|
|
checkInt(this, value, offset, byteLength, maxBytes, 0)
|
|
}
|
|
|
|
var mul = 1
|
|
var i = 0
|
|
this[offset] = value & 0xFF
|
|
while (++i < byteLength && (mul *= 0x100)) {
|
|
this[offset + i] = (value / mul) & 0xFF
|
|
}
|
|
|
|
return offset + byteLength
|
|
}
|
|
|
|
Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {
|
|
value = +value
|
|
offset = offset >>> 0
|
|
byteLength = byteLength >>> 0
|
|
if (!noAssert) {
|
|
var maxBytes = Math.pow(2, 8 * byteLength) - 1
|
|
checkInt(this, value, offset, byteLength, maxBytes, 0)
|
|
}
|
|
|
|
var i = byteLength - 1
|
|
var mul = 1
|
|
this[offset + i] = value & 0xFF
|
|
while (--i >= 0 && (mul *= 0x100)) {
|
|
this[offset + i] = (value / mul) & 0xFF
|
|
}
|
|
|
|
return offset + byteLength
|
|
}
|
|
|
|
Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {
|
|
value = +value
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)
|
|
this[offset] = (value & 0xff)
|
|
return offset + 1
|
|
}
|
|
|
|
Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {
|
|
value = +value
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
|
|
this[offset] = (value & 0xff)
|
|
this[offset + 1] = (value >>> 8)
|
|
return offset + 2
|
|
}
|
|
|
|
Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {
|
|
value = +value
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
|
|
this[offset] = (value >>> 8)
|
|
this[offset + 1] = (value & 0xff)
|
|
return offset + 2
|
|
}
|
|
|
|
Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {
|
|
value = +value
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
|
|
this[offset + 3] = (value >>> 24)
|
|
this[offset + 2] = (value >>> 16)
|
|
this[offset + 1] = (value >>> 8)
|
|
this[offset] = (value & 0xff)
|
|
return offset + 4
|
|
}
|
|
|
|
Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {
|
|
value = +value
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
|
|
this[offset] = (value >>> 24)
|
|
this[offset + 1] = (value >>> 16)
|
|
this[offset + 2] = (value >>> 8)
|
|
this[offset + 3] = (value & 0xff)
|
|
return offset + 4
|
|
}
|
|
|
|
Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {
|
|
value = +value
|
|
offset = offset >>> 0
|
|
if (!noAssert) {
|
|
var limit = Math.pow(2, (8 * byteLength) - 1)
|
|
|
|
checkInt(this, value, offset, byteLength, limit - 1, -limit)
|
|
}
|
|
|
|
var i = 0
|
|
var mul = 1
|
|
var sub = 0
|
|
this[offset] = value & 0xFF
|
|
while (++i < byteLength && (mul *= 0x100)) {
|
|
if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) {
|
|
sub = 1
|
|
}
|
|
this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
|
|
}
|
|
|
|
return offset + byteLength
|
|
}
|
|
|
|
Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {
|
|
value = +value
|
|
offset = offset >>> 0
|
|
if (!noAssert) {
|
|
var limit = Math.pow(2, (8 * byteLength) - 1)
|
|
|
|
checkInt(this, value, offset, byteLength, limit - 1, -limit)
|
|
}
|
|
|
|
var i = byteLength - 1
|
|
var mul = 1
|
|
var sub = 0
|
|
this[offset + i] = value & 0xFF
|
|
while (--i >= 0 && (mul *= 0x100)) {
|
|
if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) {
|
|
sub = 1
|
|
}
|
|
this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
|
|
}
|
|
|
|
return offset + byteLength
|
|
}
|
|
|
|
Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {
|
|
value = +value
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)
|
|
if (value < 0) value = 0xff + value + 1
|
|
this[offset] = (value & 0xff)
|
|
return offset + 1
|
|
}
|
|
|
|
Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {
|
|
value = +value
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
|
|
this[offset] = (value & 0xff)
|
|
this[offset + 1] = (value >>> 8)
|
|
return offset + 2
|
|
}
|
|
|
|
Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {
|
|
value = +value
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
|
|
this[offset] = (value >>> 8)
|
|
this[offset + 1] = (value & 0xff)
|
|
return offset + 2
|
|
}
|
|
|
|
Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {
|
|
value = +value
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
|
|
this[offset] = (value & 0xff)
|
|
this[offset + 1] = (value >>> 8)
|
|
this[offset + 2] = (value >>> 16)
|
|
this[offset + 3] = (value >>> 24)
|
|
return offset + 4
|
|
}
|
|
|
|
Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {
|
|
value = +value
|
|
offset = offset >>> 0
|
|
if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
|
|
if (value < 0) value = 0xffffffff + value + 1
|
|
this[offset] = (value >>> 24)
|
|
this[offset + 1] = (value >>> 16)
|
|
this[offset + 2] = (value >>> 8)
|
|
this[offset + 3] = (value & 0xff)
|
|
return offset + 4
|
|
}
|
|
|
|
function checkIEEE754 (buf, value, offset, ext, max, min) {
|
|
if (offset + ext > buf.length) throw new RangeError('Index out of range')
|
|
if (offset < 0) throw new RangeError('Index out of range')
|
|
}
|
|
|
|
function writeFloat (buf, value, offset, littleEndian, noAssert) {
|
|
value = +value
|
|
offset = offset >>> 0
|
|
if (!noAssert) {
|
|
checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)
|
|
}
|
|
ieee754.write(buf, value, offset, littleEndian, 23, 4)
|
|
return offset + 4
|
|
}
|
|
|
|
Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {
|
|
return writeFloat(this, value, offset, true, noAssert)
|
|
}
|
|
|
|
Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {
|
|
return writeFloat(this, value, offset, false, noAssert)
|
|
}
|
|
|
|
function writeDouble (buf, value, offset, littleEndian, noAssert) {
|
|
value = +value
|
|
offset = offset >>> 0
|
|
if (!noAssert) {
|
|
checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)
|
|
}
|
|
ieee754.write(buf, value, offset, littleEndian, 52, 8)
|
|
return offset + 8
|
|
}
|
|
|
|
Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {
|
|
return writeDouble(this, value, offset, true, noAssert)
|
|
}
|
|
|
|
Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {
|
|
return writeDouble(this, value, offset, false, noAssert)
|
|
}
|
|
|
|
// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
|
|
Buffer.prototype.copy = function copy (target, targetStart, start, end) {
|
|
if (!Buffer.isBuffer(target)) throw new TypeError('argument should be a Buffer')
|
|
if (!start) start = 0
|
|
if (!end && end !== 0) end = this.length
|
|
if (targetStart >= target.length) targetStart = target.length
|
|
if (!targetStart) targetStart = 0
|
|
if (end > 0 && end < start) end = start
|
|
|
|
// Copy 0 bytes; we're done
|
|
if (end === start) return 0
|
|
if (target.length === 0 || this.length === 0) return 0
|
|
|
|
// Fatal error conditions
|
|
if (targetStart < 0) {
|
|
throw new RangeError('targetStart out of bounds')
|
|
}
|
|
if (start < 0 || start >= this.length) throw new RangeError('Index out of range')
|
|
if (end < 0) throw new RangeError('sourceEnd out of bounds')
|
|
|
|
// Are we oob?
|
|
if (end > this.length) end = this.length
|
|
if (target.length - targetStart < end - start) {
|
|
end = target.length - targetStart + start
|
|
}
|
|
|
|
var len = end - start
|
|
|
|
if (this === target && typeof Uint8Array.prototype.copyWithin === 'function') {
|
|
// Use built-in when available, missing from IE11
|
|
this.copyWithin(targetStart, start, end)
|
|
} else if (this === target && start < targetStart && targetStart < end) {
|
|
// descending copy from end
|
|
for (var i = len - 1; i >= 0; --i) {
|
|
target[i + targetStart] = this[i + start]
|
|
}
|
|
} else {
|
|
Uint8Array.prototype.set.call(
|
|
target,
|
|
this.subarray(start, end),
|
|
targetStart
|
|
)
|
|
}
|
|
|
|
return len
|
|
}
|
|
|
|
// Usage:
|
|
// buffer.fill(number[, offset[, end]])
|
|
// buffer.fill(buffer[, offset[, end]])
|
|
// buffer.fill(string[, offset[, end]][, encoding])
|
|
Buffer.prototype.fill = function fill (val, start, end, encoding) {
|
|
// Handle string cases:
|
|
if (typeof val === 'string') {
|
|
if (typeof start === 'string') {
|
|
encoding = start
|
|
start = 0
|
|
end = this.length
|
|
} else if (typeof end === 'string') {
|
|
encoding = end
|
|
end = this.length
|
|
}
|
|
if (encoding !== undefined && typeof encoding !== 'string') {
|
|
throw new TypeError('encoding must be a string')
|
|
}
|
|
if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {
|
|
throw new TypeError('Unknown encoding: ' + encoding)
|
|
}
|
|
if (val.length === 1) {
|
|
var code = val.charCodeAt(0)
|
|
if ((encoding === 'utf8' && code < 128) ||
|
|
encoding === 'latin1') {
|
|
// Fast path: If `val` fits into a single byte, use that numeric value.
|
|
val = code
|
|
}
|
|
}
|
|
} else if (typeof val === 'number') {
|
|
val = val & 255
|
|
}
|
|
|
|
// Invalid ranges are not set to a default, so can range check early.
|
|
if (start < 0 || this.length < start || this.length < end) {
|
|
throw new RangeError('Out of range index')
|
|
}
|
|
|
|
if (end <= start) {
|
|
return this
|
|
}
|
|
|
|
start = start >>> 0
|
|
end = end === undefined ? this.length : end >>> 0
|
|
|
|
if (!val) val = 0
|
|
|
|
var i
|
|
if (typeof val === 'number') {
|
|
for (i = start; i < end; ++i) {
|
|
this[i] = val
|
|
}
|
|
} else {
|
|
var bytes = Buffer.isBuffer(val)
|
|
? val
|
|
: Buffer.from(val, encoding)
|
|
var len = bytes.length
|
|
if (len === 0) {
|
|
throw new TypeError('The value "' + val +
|
|
'" is invalid for argument "value"')
|
|
}
|
|
for (i = 0; i < end - start; ++i) {
|
|
this[i + start] = bytes[i % len]
|
|
}
|
|
}
|
|
|
|
return this
|
|
}
|
|
|
|
// HELPER FUNCTIONS
|
|
// ================
|
|
|
|
var INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g
|
|
|
|
function base64clean (str) {
|
|
// Node takes equal signs as end of the Base64 encoding
|
|
str = str.split('=')[0]
|
|
// Node strips out invalid characters like \n and \t from the string, base64-js does not
|
|
str = str.trim().replace(INVALID_BASE64_RE, '')
|
|
// Node converts strings with length < 2 to ''
|
|
if (str.length < 2) return ''
|
|
// Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
|
|
while (str.length % 4 !== 0) {
|
|
str = str + '='
|
|
}
|
|
return str
|
|
}
|
|
|
|
function toHex (n) {
|
|
if (n < 16) return '0' + n.toString(16)
|
|
return n.toString(16)
|
|
}
|
|
|
|
function utf8ToBytes (string, units) {
|
|
units = units || Infinity
|
|
var codePoint
|
|
var length = string.length
|
|
var leadSurrogate = null
|
|
var bytes = []
|
|
|
|
for (var i = 0; i < length; ++i) {
|
|
codePoint = string.charCodeAt(i)
|
|
|
|
// is surrogate component
|
|
if (codePoint > 0xD7FF && codePoint < 0xE000) {
|
|
// last char was a lead
|
|
if (!leadSurrogate) {
|
|
// no lead yet
|
|
if (codePoint > 0xDBFF) {
|
|
// unexpected trail
|
|
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
|
|
continue
|
|
} else if (i + 1 === length) {
|
|
// unpaired lead
|
|
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
|
|
continue
|
|
}
|
|
|
|
// valid lead
|
|
leadSurrogate = codePoint
|
|
|
|
continue
|
|
}
|
|
|
|
// 2 leads in a row
|
|
if (codePoint < 0xDC00) {
|
|
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
|
|
leadSurrogate = codePoint
|
|
continue
|
|
}
|
|
|
|
// valid surrogate pair
|
|
codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000
|
|
} else if (leadSurrogate) {
|
|
// valid bmp char, but last char was a lead
|
|
if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
|
|
}
|
|
|
|
leadSurrogate = null
|
|
|
|
// encode utf8
|
|
if (codePoint < 0x80) {
|
|
if ((units -= 1) < 0) break
|
|
bytes.push(codePoint)
|
|
} else if (codePoint < 0x800) {
|
|
if ((units -= 2) < 0) break
|
|
bytes.push(
|
|
codePoint >> 0x6 | 0xC0,
|
|
codePoint & 0x3F | 0x80
|
|
)
|
|
} else if (codePoint < 0x10000) {
|
|
if ((units -= 3) < 0) break
|
|
bytes.push(
|
|
codePoint >> 0xC | 0xE0,
|
|
codePoint >> 0x6 & 0x3F | 0x80,
|
|
codePoint & 0x3F | 0x80
|
|
)
|
|
} else if (codePoint < 0x110000) {
|
|
if ((units -= 4) < 0) break
|
|
bytes.push(
|
|
codePoint >> 0x12 | 0xF0,
|
|
codePoint >> 0xC & 0x3F | 0x80,
|
|
codePoint >> 0x6 & 0x3F | 0x80,
|
|
codePoint & 0x3F | 0x80
|
|
)
|
|
} else {
|
|
throw new Error('Invalid code point')
|
|
}
|
|
}
|
|
|
|
return bytes
|
|
}
|
|
|
|
function asciiToBytes (str) {
|
|
var byteArray = []
|
|
for (var i = 0; i < str.length; ++i) {
|
|
// Node's code seems to be doing this and not & 0x7F..
|
|
byteArray.push(str.charCodeAt(i) & 0xFF)
|
|
}
|
|
return byteArray
|
|
}
|
|
|
|
function utf16leToBytes (str, units) {
|
|
var c, hi, lo
|
|
var byteArray = []
|
|
for (var i = 0; i < str.length; ++i) {
|
|
if ((units -= 2) < 0) break
|
|
|
|
c = str.charCodeAt(i)
|
|
hi = c >> 8
|
|
lo = c % 256
|
|
byteArray.push(lo)
|
|
byteArray.push(hi)
|
|
}
|
|
|
|
return byteArray
|
|
}
|
|
|
|
function base64ToBytes (str) {
|
|
return base64.toByteArray(base64clean(str))
|
|
}
|
|
|
|
function blitBuffer (src, dst, offset, length) {
|
|
for (var i = 0; i < length; ++i) {
|
|
if ((i + offset >= dst.length) || (i >= src.length)) break
|
|
dst[i + offset] = src[i]
|
|
}
|
|
return i
|
|
}
|
|
|
|
// ArrayBuffer or Uint8Array objects from other contexts (i.e. iframes) do not pass
|
|
// the `instanceof` check but they should be treated as of that type.
|
|
// See: https://github.com/feross/buffer/issues/166
|
|
function isInstance (obj, type) {
|
|
return obj instanceof type ||
|
|
(obj != null && obj.constructor != null && obj.constructor.name != null &&
|
|
obj.constructor.name === type.name)
|
|
}
|
|
function numberIsNaN (obj) {
|
|
// For IE11 support
|
|
return obj !== obj // eslint-disable-line no-self-compare
|
|
}
|
|
|
|
}).call(this)}).call(this,require("buffer").Buffer)
|
|
},{"base64-js":3,"buffer":4,"ieee754":7}],5:[function(require,module,exports){
|
|
var charenc = {
|
|
// UTF-8 encoding
|
|
utf8: {
|
|
// Convert a string to a byte array
|
|
stringToBytes: function(str) {
|
|
return charenc.bin.stringToBytes(unescape(encodeURIComponent(str)));
|
|
},
|
|
|
|
// Convert a byte array to a string
|
|
bytesToString: function(bytes) {
|
|
return decodeURIComponent(escape(charenc.bin.bytesToString(bytes)));
|
|
}
|
|
},
|
|
|
|
// Binary encoding
|
|
bin: {
|
|
// Convert a string to a byte array
|
|
stringToBytes: function(str) {
|
|
for (var bytes = [], i = 0; i < str.length; i++)
|
|
bytes.push(str.charCodeAt(i) & 0xFF);
|
|
return bytes;
|
|
},
|
|
|
|
// Convert a byte array to a string
|
|
bytesToString: function(bytes) {
|
|
for (var str = [], i = 0; i < bytes.length; i++)
|
|
str.push(String.fromCharCode(bytes[i]));
|
|
return str.join('');
|
|
}
|
|
}
|
|
};
|
|
|
|
module.exports = charenc;
|
|
|
|
},{}],6:[function(require,module,exports){
|
|
(function() {
|
|
var base64map
|
|
= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
|
|
|
|
crypt = {
|
|
// Bit-wise rotation left
|
|
rotl: function(n, b) {
|
|
return (n << b) | (n >>> (32 - b));
|
|
},
|
|
|
|
// Bit-wise rotation right
|
|
rotr: function(n, b) {
|
|
return (n << (32 - b)) | (n >>> b);
|
|
},
|
|
|
|
// Swap big-endian to little-endian and vice versa
|
|
endian: function(n) {
|
|
// If number given, swap endian
|
|
if (n.constructor == Number) {
|
|
return crypt.rotl(n, 8) & 0x00FF00FF | crypt.rotl(n, 24) & 0xFF00FF00;
|
|
}
|
|
|
|
// Else, assume array and swap all items
|
|
for (var i = 0; i < n.length; i++)
|
|
n[i] = crypt.endian(n[i]);
|
|
return n;
|
|
},
|
|
|
|
// Generate an array of any length of random bytes
|
|
randomBytes: function(n) {
|
|
for (var bytes = []; n > 0; n--)
|
|
bytes.push(Math.floor(Math.random() * 256));
|
|
return bytes;
|
|
},
|
|
|
|
// Convert a byte array to big-endian 32-bit words
|
|
bytesToWords: function(bytes) {
|
|
for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8)
|
|
words[b >>> 5] |= bytes[i] << (24 - b % 32);
|
|
return words;
|
|
},
|
|
|
|
// Convert big-endian 32-bit words to a byte array
|
|
wordsToBytes: function(words) {
|
|
for (var bytes = [], b = 0; b < words.length * 32; b += 8)
|
|
bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
|
|
return bytes;
|
|
},
|
|
|
|
// Convert a byte array to a hex string
|
|
bytesToHex: function(bytes) {
|
|
for (var hex = [], i = 0; i < bytes.length; i++) {
|
|
hex.push((bytes[i] >>> 4).toString(16));
|
|
hex.push((bytes[i] & 0xF).toString(16));
|
|
}
|
|
return hex.join('');
|
|
},
|
|
|
|
// Convert a hex string to a byte array
|
|
hexToBytes: function(hex) {
|
|
for (var bytes = [], c = 0; c < hex.length; c += 2)
|
|
bytes.push(parseInt(hex.substr(c, 2), 16));
|
|
return bytes;
|
|
},
|
|
|
|
// Convert a byte array to a base-64 string
|
|
bytesToBase64: function(bytes) {
|
|
for (var base64 = [], i = 0; i < bytes.length; i += 3) {
|
|
var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
|
|
for (var j = 0; j < 4; j++)
|
|
if (i * 8 + j * 6 <= bytes.length * 8)
|
|
base64.push(base64map.charAt((triplet >>> 6 * (3 - j)) & 0x3F));
|
|
else
|
|
base64.push('=');
|
|
}
|
|
return base64.join('');
|
|
},
|
|
|
|
// Convert a base-64 string to a byte array
|
|
base64ToBytes: function(base64) {
|
|
// Remove non-base-64 characters
|
|
base64 = base64.replace(/[^A-Z0-9+\/]/ig, '');
|
|
|
|
for (var bytes = [], i = 0, imod4 = 0; i < base64.length;
|
|
imod4 = ++i % 4) {
|
|
if (imod4 == 0) continue;
|
|
bytes.push(((base64map.indexOf(base64.charAt(i - 1))
|
|
& (Math.pow(2, -2 * imod4 + 8) - 1)) << (imod4 * 2))
|
|
| (base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2)));
|
|
}
|
|
return bytes;
|
|
}
|
|
};
|
|
|
|
module.exports = crypt;
|
|
})();
|
|
|
|
},{}],7:[function(require,module,exports){
|
|
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
|
exports.read = function (buffer, offset, isLE, mLen, nBytes) {
|
|
var e, m
|
|
var eLen = (nBytes * 8) - mLen - 1
|
|
var eMax = (1 << eLen) - 1
|
|
var eBias = eMax >> 1
|
|
var nBits = -7
|
|
var i = isLE ? (nBytes - 1) : 0
|
|
var d = isLE ? -1 : 1
|
|
var s = buffer[offset + i]
|
|
|
|
i += d
|
|
|
|
e = s & ((1 << (-nBits)) - 1)
|
|
s >>= (-nBits)
|
|
nBits += eLen
|
|
for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}
|
|
|
|
m = e & ((1 << (-nBits)) - 1)
|
|
e >>= (-nBits)
|
|
nBits += mLen
|
|
for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}
|
|
|
|
if (e === 0) {
|
|
e = 1 - eBias
|
|
} else if (e === eMax) {
|
|
return m ? NaN : ((s ? -1 : 1) * Infinity)
|
|
} else {
|
|
m = m + Math.pow(2, mLen)
|
|
e = e - eBias
|
|
}
|
|
return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
|
|
}
|
|
|
|
exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
|
|
var e, m, c
|
|
var eLen = (nBytes * 8) - mLen - 1
|
|
var eMax = (1 << eLen) - 1
|
|
var eBias = eMax >> 1
|
|
var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)
|
|
var i = isLE ? 0 : (nBytes - 1)
|
|
var d = isLE ? 1 : -1
|
|
var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0
|
|
|
|
value = Math.abs(value)
|
|
|
|
if (isNaN(value) || value === Infinity) {
|
|
m = isNaN(value) ? 1 : 0
|
|
e = eMax
|
|
} else {
|
|
e = Math.floor(Math.log(value) / Math.LN2)
|
|
if (value * (c = Math.pow(2, -e)) < 1) {
|
|
e--
|
|
c *= 2
|
|
}
|
|
if (e + eBias >= 1) {
|
|
value += rt / c
|
|
} else {
|
|
value += rt * Math.pow(2, 1 - eBias)
|
|
}
|
|
if (value * c >= 2) {
|
|
e++
|
|
c /= 2
|
|
}
|
|
|
|
if (e + eBias >= eMax) {
|
|
m = 0
|
|
e = eMax
|
|
} else if (e + eBias >= 1) {
|
|
m = ((value * c) - 1) * Math.pow(2, mLen)
|
|
e = e + eBias
|
|
} else {
|
|
m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)
|
|
e = 0
|
|
}
|
|
}
|
|
|
|
for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
|
|
|
|
e = (e << mLen) | m
|
|
eLen += mLen
|
|
for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
|
|
|
|
buffer[offset + i - d] |= s * 128
|
|
}
|
|
|
|
},{}],8:[function(require,module,exports){
|
|
/*
|
|
* Copyright (C) 2015 Michael Martinez
|
|
* Changes: Added support for selection values 2-7, fixed minor bugs &
|
|
* warnings, split into multiple class files, and general clean up.
|
|
*
|
|
* 08-25-2015: Helmut Dersch agreed to a license change from LGPL to MIT.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) Helmut Dersch
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
/*jslint browser: true, node: true */
|
|
/*global require, module */
|
|
|
|
"use strict";
|
|
|
|
/*** Imports ***/
|
|
var jpeg = jpeg || {};
|
|
jpeg.lossless = jpeg.lossless || {};
|
|
|
|
|
|
/*** Constructor ***/
|
|
jpeg.lossless.ComponentSpec = jpeg.lossless.ComponentSpec || function () {
|
|
this.hSamp = 0; // Horizontal sampling factor
|
|
this.quantTableSel = 0; // Quantization table destination selector
|
|
this.vSamp = 0; // Vertical
|
|
};
|
|
|
|
|
|
/*** Exports ***/
|
|
|
|
var moduleType = typeof module;
|
|
if ((moduleType !== 'undefined') && module.exports) {
|
|
module.exports = jpeg.lossless.ComponentSpec;
|
|
}
|
|
|
|
},{}],9:[function(require,module,exports){
|
|
/*
|
|
* Copyright (C) 2015 Michael Martinez
|
|
* Changes: Added support for selection values 2-7, fixed minor bugs &
|
|
* warnings, split into multiple class files, and general clean up.
|
|
*
|
|
* 08-25-2015: Helmut Dersch agreed to a license change from LGPL to MIT.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) Helmut Dersch
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
/*jslint browser: true, node: true */
|
|
/*global require, module */
|
|
|
|
"use strict";
|
|
|
|
/*** Imports ***/
|
|
var jpeg = jpeg || {};
|
|
jpeg.lossless = jpeg.lossless || {};
|
|
|
|
|
|
/*** Constructor ***/
|
|
jpeg.lossless.DataStream = jpeg.lossless.DataStream || function (data, offset, length) {
|
|
if (offset === undefined && length === undefined) { // Old api
|
|
this.buffer = new Uint8Array(data);
|
|
} else {
|
|
this.buffer = new Uint8Array(data, offset, length);
|
|
}
|
|
this.index = 0;
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.DataStream.prototype.get16 = function () {
|
|
// var value = this.buffer.getUint16(this.index, false);
|
|
var value = (this.buffer[this.index] << 8) + this.buffer[this.index + 1]; // DataView is big-endian by default
|
|
this.index += 2;
|
|
return value;
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.DataStream.prototype.get8 = function () {
|
|
// var value = this.buffer.getUint8(this.index);
|
|
var value = this.buffer[this.index];
|
|
this.index += 1;
|
|
return value;
|
|
};
|
|
|
|
|
|
/*** Exports ***/
|
|
|
|
var moduleType = typeof module;
|
|
if ((moduleType !== 'undefined') && module.exports) {
|
|
module.exports = jpeg.lossless.DataStream;
|
|
}
|
|
|
|
},{}],10:[function(require,module,exports){
|
|
/*
|
|
* Copyright (C) 2015 Michael Martinez
|
|
* Changes: Added support for selection values 2-7, fixed minor bugs &
|
|
* warnings, split into multiple class files, and general clean up.
|
|
*
|
|
* 08-25-2015: Helmut Dersch agreed to a license change from LGPL to MIT.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) Helmut Dersch
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
/*jslint browser: true, node: true */
|
|
/*global require, module */
|
|
|
|
"use strict";
|
|
|
|
/*** Imports ***/
|
|
var jpeg = jpeg || {};
|
|
jpeg.lossless = jpeg.lossless || {};
|
|
jpeg.lossless.DataStream = jpeg.lossless.DataStream || ((typeof require !== 'undefined') ? require('./data-stream.js') : null);
|
|
jpeg.lossless.HuffmanTable = jpeg.lossless.HuffmanTable || ((typeof require !== 'undefined') ? require('./huffman-table.js') : null);
|
|
jpeg.lossless.QuantizationTable = jpeg.lossless.QuantizationTable || ((typeof require !== 'undefined') ? require('./quantization-table.js') : null);
|
|
jpeg.lossless.ScanHeader = jpeg.lossless.ScanHeader || ((typeof require !== 'undefined') ? require('./scan-header.js') : null);
|
|
jpeg.lossless.FrameHeader = jpeg.lossless.FrameHeader || ((typeof require !== 'undefined') ? require('./frame-header.js') : null);
|
|
jpeg.lossless.Utils = jpeg.lossless.Utils || ((typeof require !== 'undefined') ? require('./utils.js') : null);
|
|
|
|
|
|
/*** Constructor ***/
|
|
|
|
/**
|
|
* The Decoder constructor.
|
|
* @property {number} xDim - size of x dimension
|
|
* @property {number} yDim - size of y dimension
|
|
* @property {number} numComp - number of components
|
|
* @property {number} numBytes - number of bytes per component
|
|
* @type {Function}
|
|
*/
|
|
jpeg.lossless.Decoder = jpeg.lossless.Decoder || function (buffer, numBytes) {
|
|
this.buffer = buffer;
|
|
this.frame = new jpeg.lossless.FrameHeader();
|
|
this.huffTable = new jpeg.lossless.HuffmanTable();
|
|
this.quantTable = new jpeg.lossless.QuantizationTable();
|
|
this.scan = new jpeg.lossless.ScanHeader();
|
|
this.DU = jpeg.lossless.Utils.createArray(10, 4, 64); // at most 10 data units in a MCU, at most 4 data units in one component
|
|
this.HuffTab = jpeg.lossless.Utils.createArray(4, 2, 50 * 256);
|
|
this.IDCT_Source = [];
|
|
this.nBlock = []; // number of blocks in the i-th Comp in a scan
|
|
this.acTab = jpeg.lossless.Utils.createArray(10, 1); // ac HuffTab for the i-th Comp in a scan
|
|
this.dcTab = jpeg.lossless.Utils.createArray(10, 1); // dc HuffTab for the i-th Comp in a scan
|
|
this.qTab = jpeg.lossless.Utils.createArray(10, 1); // quantization table for the i-th Comp in a scan
|
|
this.marker = 0;
|
|
this.markerIndex = 0;
|
|
this.numComp = 0;
|
|
this.restartInterval = 0;
|
|
this.selection = 0;
|
|
this.xDim = 0;
|
|
this.yDim = 0;
|
|
this.xLoc = 0;
|
|
this.yLoc = 0;
|
|
this.numBytes = 0;
|
|
this.outputData = null;
|
|
this.restarting = false;
|
|
this.mask = 0;
|
|
|
|
if (typeof numBytes !== "undefined") {
|
|
this.numBytes = numBytes;
|
|
}
|
|
};
|
|
|
|
|
|
/*** Static Pseudo-constants ***/
|
|
|
|
jpeg.lossless.Decoder.IDCT_P = [0, 5, 40, 16, 45, 2, 7, 42, 21, 56, 8, 61, 18, 47, 1, 4, 41, 23, 58, 13, 32, 24, 37, 10, 63, 17, 44, 3, 6, 43, 20,
|
|
57, 15, 34, 29, 48, 53, 26, 39, 9, 60, 19, 46, 22, 59, 12, 33, 31, 50, 55, 25, 36, 11, 62, 14, 35, 28, 49, 52, 27, 38, 30, 51, 54];
|
|
jpeg.lossless.Decoder.TABLE = [0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44, 53,
|
|
10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63];
|
|
jpeg.lossless.Decoder.MAX_HUFFMAN_SUBTREE = 50;
|
|
jpeg.lossless.Decoder.MSB = 0x80000000;
|
|
jpeg.lossless.Decoder.RESTART_MARKER_BEGIN = 0xFFD0;
|
|
jpeg.lossless.Decoder.RESTART_MARKER_END = 0xFFD7;
|
|
|
|
/*** Prototype Methods ***/
|
|
|
|
/**
|
|
* Returns decompressed data.
|
|
* @param {ArrayBuffer} buffer
|
|
* @param {number} [offset]
|
|
* @param {number} [length]
|
|
* @returns {ArrayBufer}
|
|
*/
|
|
jpeg.lossless.Decoder.prototype.decompress = function (buffer, offset, length) {
|
|
return this.decode(buffer, offset, length).buffer;
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.Decoder.prototype.decode = function (buffer, offset, length, numBytes) {
|
|
/*jslint bitwise: true */
|
|
|
|
var current, scanNum = 0, pred = [], i, compN, temp = [], index = [], mcuNum;
|
|
|
|
if (typeof buffer !== "undefined") {
|
|
this.buffer = buffer;
|
|
}
|
|
|
|
if (typeof numBytes !== "undefined") {
|
|
this.numBytes = numBytes;
|
|
}
|
|
|
|
this.stream = new jpeg.lossless.DataStream(this.buffer, offset, length);
|
|
this.buffer = null;
|
|
|
|
this.xLoc = 0;
|
|
this.yLoc = 0;
|
|
current = this.stream.get16();
|
|
|
|
if (current !== 0xFFD8) { // SOI
|
|
throw new Error("Not a JPEG file");
|
|
}
|
|
|
|
current = this.stream.get16();
|
|
|
|
while ((((current >> 4) !== 0x0FFC) || (current === 0xFFC4))) { // SOF 0~15
|
|
switch (current) {
|
|
case 0xFFC4: // DHT
|
|
this.huffTable.read(this.stream, this.HuffTab);
|
|
break;
|
|
case 0xFFCC: // DAC
|
|
throw new Error("Program doesn't support arithmetic coding. (format throw new IOException)");
|
|
case 0xFFDB:
|
|
this.quantTable.read(this.stream, jpeg.lossless.Decoder.TABLE);
|
|
break;
|
|
case 0xFFDD:
|
|
this.restartInterval = this.readNumber();
|
|
break;
|
|
case 0xFFE0:
|
|
case 0xFFE1:
|
|
case 0xFFE2:
|
|
case 0xFFE3:
|
|
case 0xFFE4:
|
|
case 0xFFE5:
|
|
case 0xFFE6:
|
|
case 0xFFE7:
|
|
case 0xFFE8:
|
|
case 0xFFE9:
|
|
case 0xFFEA:
|
|
case 0xFFEB:
|
|
case 0xFFEC:
|
|
case 0xFFED:
|
|
case 0xFFEE:
|
|
case 0xFFEF:
|
|
this.readApp();
|
|
break;
|
|
case 0xFFFE:
|
|
this.readComment();
|
|
break;
|
|
default:
|
|
if ((current >> 8) !== 0xFF) {
|
|
throw new Error("ERROR: format throw new IOException! (decode)");
|
|
}
|
|
}
|
|
|
|
current = this.stream.get16();
|
|
}
|
|
|
|
if ((current < 0xFFC0) || (current > 0xFFC7)) {
|
|
throw new Error("ERROR: could not handle arithmetic code!");
|
|
}
|
|
|
|
this.frame.read(this.stream);
|
|
current = this.stream.get16();
|
|
|
|
do {
|
|
while (current !== 0x0FFDA) { // SOS
|
|
switch (current) {
|
|
case 0xFFC4: // DHT
|
|
this.huffTable.read(this.stream, this.HuffTab);
|
|
break;
|
|
case 0xFFCC: // DAC
|
|
throw new Error("Program doesn't support arithmetic coding. (format throw new IOException)");
|
|
case 0xFFDB:
|
|
this.quantTable.read(this.stream, jpeg.lossless.Decoder.TABLE);
|
|
break;
|
|
case 0xFFDD:
|
|
this.restartInterval = this.readNumber();
|
|
break;
|
|
case 0xFFE0:
|
|
case 0xFFE1:
|
|
case 0xFFE2:
|
|
case 0xFFE3:
|
|
case 0xFFE4:
|
|
case 0xFFE5:
|
|
case 0xFFE6:
|
|
case 0xFFE7:
|
|
case 0xFFE8:
|
|
case 0xFFE9:
|
|
case 0xFFEA:
|
|
case 0xFFEB:
|
|
case 0xFFEC:
|
|
case 0xFFED:
|
|
case 0xFFEE:
|
|
case 0xFFEF:
|
|
this.readApp();
|
|
break;
|
|
case 0xFFFE:
|
|
this.readComment();
|
|
break;
|
|
default:
|
|
if ((current >> 8) !== 0xFF) {
|
|
throw new Error("ERROR: format throw new IOException! (Parser.decode)");
|
|
}
|
|
}
|
|
|
|
current = this.stream.get16();
|
|
}
|
|
|
|
this.precision = this.frame.precision;
|
|
this.components = this.frame.components;
|
|
|
|
if (!this.numBytes) {
|
|
this.numBytes = parseInt(Math.ceil(this.precision / 8));
|
|
}
|
|
|
|
if (this.numBytes == 1) {
|
|
this.mask = 0xFF;
|
|
} else {
|
|
this.mask = 0xFFFF;
|
|
}
|
|
|
|
this.scan.read(this.stream);
|
|
this.numComp = this.scan.numComp;
|
|
this.selection = this.scan.selection;
|
|
|
|
if (this.numBytes === 1) {
|
|
if (this.numComp === 3) {
|
|
this.getter = this.getValueRGB;
|
|
this.setter = this.setValueRGB;
|
|
this.output = this.outputRGB;
|
|
} else {
|
|
this.getter = this.getValue8;
|
|
this.setter = this.setValue8;
|
|
this.output = this.outputSingle;
|
|
}
|
|
} else {
|
|
this.getter = this.getValue16;
|
|
this.setter = this.setValue16;
|
|
this.output = this.outputSingle;
|
|
}
|
|
|
|
switch (this.selection) {
|
|
case 2:
|
|
this.selector = this.select2;
|
|
break;
|
|
case 3:
|
|
this.selector = this.select3;
|
|
break;
|
|
case 4:
|
|
this.selector = this.select4;
|
|
break;
|
|
case 5:
|
|
this.selector = this.select5;
|
|
break;
|
|
case 6:
|
|
this.selector = this.select6;
|
|
break;
|
|
case 7:
|
|
this.selector = this.select7;
|
|
break;
|
|
default:
|
|
this.selector = this.select1;
|
|
break;
|
|
}
|
|
|
|
this.scanComps = this.scan.components;
|
|
this.quantTables = this.quantTable.quantTables;
|
|
|
|
for (i = 0; i < this.numComp; i+=1) {
|
|
compN = this.scanComps[i].scanCompSel;
|
|
this.qTab[i] = this.quantTables[this.components[compN].quantTableSel];
|
|
this.nBlock[i] = this.components[compN].vSamp * this.components[compN].hSamp;
|
|
this.dcTab[i] = this.HuffTab[this.scanComps[i].dcTabSel][0];
|
|
this.acTab[i] = this.HuffTab[this.scanComps[i].acTabSel][1];
|
|
}
|
|
|
|
this.xDim = this.frame.dimX;
|
|
this.yDim = this.frame.dimY;
|
|
if (this.numBytes == 1) {
|
|
this.outputData = new Uint8Array(new ArrayBuffer(this.xDim * this.yDim * this.numBytes * this.numComp));
|
|
} else {
|
|
this.outputData = new Uint16Array(new ArrayBuffer(this.xDim * this.yDim * this.numBytes * this.numComp));
|
|
}
|
|
|
|
scanNum+=1;
|
|
|
|
while (true) { // Decode one scan
|
|
temp[0] = 0;
|
|
index[0] = 0;
|
|
|
|
for (i = 0; i < 10; i+=1) {
|
|
pred[i] = (1 << (this.precision - 1));
|
|
}
|
|
|
|
if (this.restartInterval === 0) {
|
|
current = this.decodeUnit(pred, temp, index);
|
|
|
|
while ((current === 0) && ((this.xLoc < this.xDim) && (this.yLoc < this.yDim))) {
|
|
this.output(pred);
|
|
current = this.decodeUnit(pred, temp, index);
|
|
}
|
|
|
|
break; //current=MARKER
|
|
}
|
|
|
|
for (mcuNum = 0; mcuNum < this.restartInterval; mcuNum+=1) {
|
|
this.restarting = (mcuNum == 0);
|
|
current = this.decodeUnit(pred, temp, index);
|
|
this.output(pred);
|
|
|
|
if (current !== 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (current === 0) {
|
|
if (this.markerIndex !== 0) {
|
|
current = (0xFF00 | this.marker);
|
|
this.markerIndex = 0;
|
|
} else {
|
|
current = this.stream.get16();
|
|
}
|
|
}
|
|
|
|
if (!((current >= jpeg.lossless.Decoder.RESTART_MARKER_BEGIN) &&
|
|
(current <= jpeg.lossless.Decoder.RESTART_MARKER_END))) {
|
|
break; //current=MARKER
|
|
}
|
|
}
|
|
|
|
if ((current === 0xFFDC) && (scanNum === 1)) { //DNL
|
|
this.readNumber();
|
|
current = this.stream.get16();
|
|
}
|
|
} while ((current !== 0xFFD9) && ((this.xLoc < this.xDim) && (this.yLoc < this.yDim)) && (scanNum === 0));
|
|
|
|
return this.outputData;
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.Decoder.prototype.decodeUnit = function (prev, temp, index) {
|
|
if (this.numComp == 1) {
|
|
return this.decodeSingle(prev, temp, index);
|
|
} else if (this.numComp == 3) {
|
|
return this.decodeRGB(prev, temp, index);
|
|
} else {
|
|
return -1;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.Decoder.prototype.select1 = function (compOffset) {
|
|
return this.getPreviousX(compOffset);
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.Decoder.prototype.select2 = function (compOffset) {
|
|
return this.getPreviousY(compOffset);
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.Decoder.prototype.select3 = function (compOffset) {
|
|
return this.getPreviousXY(compOffset);
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.Decoder.prototype.select4 = function (compOffset) {
|
|
return (this.getPreviousX(compOffset) + this.getPreviousY(compOffset)) - this.getPreviousXY(compOffset);
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.Decoder.prototype.select5 = function (compOffset) {
|
|
return this.getPreviousX(compOffset) + ((this.getPreviousY(compOffset) - this.getPreviousXY(compOffset)) >> 1);
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.Decoder.prototype.select6 = function (compOffset) {
|
|
return this.getPreviousY(compOffset) + ((this.getPreviousX(compOffset) - this.getPreviousXY(compOffset)) >> 1);
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.Decoder.prototype.select7 = function (compOffset) {
|
|
return ((this.getPreviousX(compOffset) + this.getPreviousY(compOffset)) / 2);
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.Decoder.prototype.decodeRGB = function (prev, temp, index) {
|
|
/*jslint bitwise: true */
|
|
|
|
var value, actab, dctab, qtab, ctrC, i, k, j;
|
|
|
|
prev[0] = this.selector(0);
|
|
prev[1] = this.selector(1);
|
|
prev[2] = this.selector(2);
|
|
|
|
for (ctrC = 0; ctrC < this.numComp; ctrC+=1) {
|
|
qtab = this.qTab[ctrC];
|
|
actab = this.acTab[ctrC];
|
|
dctab = this.dcTab[ctrC];
|
|
for (i = 0; i < this.nBlock[ctrC]; i+=1) {
|
|
for (k = 0; k < this.IDCT_Source.length; k+=1) {
|
|
this.IDCT_Source[k] = 0;
|
|
}
|
|
|
|
value = this.getHuffmanValue(dctab, temp, index);
|
|
|
|
if (value >= 0xFF00) {
|
|
return value;
|
|
}
|
|
|
|
prev[ctrC] = this.IDCT_Source[0] = prev[ctrC] + this.getn(index, value, temp, index);
|
|
this.IDCT_Source[0] *= qtab[0];
|
|
|
|
for (j = 1; j < 64; j+=1) {
|
|
value = this.getHuffmanValue(actab, temp, index);
|
|
|
|
if (value >= 0xFF00) {
|
|
return value;
|
|
}
|
|
|
|
j += (value >> 4);
|
|
|
|
if ((value & 0x0F) === 0) {
|
|
if ((value >> 4) === 0) {
|
|
break;
|
|
}
|
|
} else {
|
|
this.IDCT_Source[jpeg.lossless.Decoder.IDCT_P[j]] = this.getn(index, value & 0x0F, temp, index) * qtab[j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.Decoder.prototype.decodeSingle = function (prev, temp, index) {
|
|
/*jslint bitwise: true */
|
|
|
|
var value, i, n, nRestart;
|
|
|
|
if (this.restarting) {
|
|
this.restarting = false;
|
|
prev[0] = (1 << (this.frame.precision - 1));
|
|
} else {
|
|
prev[0] = this.selector();
|
|
}
|
|
|
|
for (i = 0; i < this.nBlock[0]; i+=1) {
|
|
value = this.getHuffmanValue(this.dcTab[0], temp, index);
|
|
if (value >= 0xFF00) {
|
|
return value;
|
|
}
|
|
|
|
n = this.getn(prev, value, temp, index);
|
|
nRestart = (n >> 8);
|
|
|
|
if ((nRestart >= jpeg.lossless.Decoder.RESTART_MARKER_BEGIN) && (nRestart <= jpeg.lossless.Decoder.RESTART_MARKER_END)) {
|
|
return nRestart;
|
|
}
|
|
|
|
prev[0] += n;
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
|
|
|
|
|
|
// Huffman table for fast search: (HuffTab) 8-bit Look up table 2-layer search architecture, 1st-layer represent 256 node (8 bits) if codeword-length > 8
|
|
// bits, then the entry of 1st-layer = (# of 2nd-layer table) | MSB and it is stored in the 2nd-layer Size of tables in each layer are 256.
|
|
// HuffTab[*][*][0-256] is always the only 1st-layer table.
|
|
//
|
|
// An entry can be: (1) (# of 2nd-layer table) | MSB , for code length > 8 in 1st-layer (2) (Code length) << 8 | HuffVal
|
|
//
|
|
// HuffmanValue(table HuffTab[x][y] (ex) HuffmanValue(HuffTab[1][0],...)
|
|
// ):
|
|
// return: Huffman Value of table
|
|
// 0xFF?? if it receives a MARKER
|
|
// Parameter: table HuffTab[x][y] (ex) HuffmanValue(HuffTab[1][0],...)
|
|
// temp temp storage for remainded bits
|
|
// index index to bit of temp
|
|
// in FILE pointer
|
|
// Effect:
|
|
// temp store new remainded bits
|
|
// index change to new index
|
|
// in change to new position
|
|
// NOTE:
|
|
// Initial by temp=0; index=0;
|
|
// NOTE: (explain temp and index)
|
|
// temp: is always in the form at calling time or returning time
|
|
// | byte 4 | byte 3 | byte 2 | byte 1 |
|
|
// | 0 | 0 | 00000000 | 00000??? | if not a MARKER
|
|
// ^index=3 (from 0 to 15)
|
|
// 321
|
|
// NOTE (marker and marker_index):
|
|
// If get a MARKER from 'in', marker=the low-byte of the MARKER
|
|
// and marker_index=9
|
|
// If marker_index=9 then index is always > 8, or HuffmanValue()
|
|
// will not be called
|
|
jpeg.lossless.Decoder.prototype.getHuffmanValue = function (table, temp, index) {
|
|
/*jslint bitwise: true */
|
|
|
|
var code, input, mask;
|
|
mask = 0xFFFF;
|
|
|
|
if (index[0] < 8) {
|
|
temp[0] <<= 8;
|
|
input = this.stream.get8();
|
|
if (input === 0xFF) {
|
|
this.marker = this.stream.get8();
|
|
if (this.marker !== 0) {
|
|
this.markerIndex = 9;
|
|
}
|
|
}
|
|
temp[0] |= input;
|
|
} else {
|
|
index[0] -= 8;
|
|
}
|
|
|
|
code = table[temp[0] >> index[0]];
|
|
|
|
if ((code & jpeg.lossless.Decoder.MSB) !== 0) {
|
|
if (this.markerIndex !== 0) {
|
|
this.markerIndex = 0;
|
|
return 0xFF00 | this.marker;
|
|
}
|
|
|
|
temp[0] &= (mask >> (16 - index[0]));
|
|
temp[0] <<= 8;
|
|
input = this.stream.get8();
|
|
|
|
if (input === 0xFF) {
|
|
this.marker = this.stream.get8();
|
|
if (this.marker !== 0) {
|
|
this.markerIndex = 9;
|
|
}
|
|
}
|
|
|
|
temp[0] |= input;
|
|
code = table[((code & 0xFF) * 256) + (temp[0] >> index[0])];
|
|
index[0] += 8;
|
|
}
|
|
|
|
index[0] += 8 - (code >> 8);
|
|
|
|
if (index[0] < 0) {
|
|
throw new Error("index=" + index[0] + " temp=" + temp[0] + " code=" + code + " in HuffmanValue()");
|
|
}
|
|
|
|
if (index[0] < this.markerIndex) {
|
|
this.markerIndex = 0;
|
|
return 0xFF00 | this.marker;
|
|
}
|
|
|
|
temp[0] &= (mask >> (16 - index[0]));
|
|
return code & 0xFF;
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.Decoder.prototype.getn = function (PRED, n, temp, index) {
|
|
/*jslint bitwise: true */
|
|
|
|
var result, one, n_one, mask, input;
|
|
one = 1;
|
|
n_one = -1;
|
|
mask = 0xFFFF;
|
|
|
|
if (n === 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (n === 16) {
|
|
if (PRED[0] >= 0) {
|
|
return -32768;
|
|
} else {
|
|
return 32768;
|
|
}
|
|
}
|
|
|
|
index[0] -= n;
|
|
|
|
if (index[0] >= 0) {
|
|
if ((index[0] < this.markerIndex) && !this.isLastPixel()) { // this was corrupting the last pixel in some cases
|
|
this.markerIndex = 0;
|
|
return (0xFF00 | this.marker) << 8;
|
|
}
|
|
|
|
result = temp[0] >> index[0];
|
|
temp[0] &= (mask >> (16 - index[0]));
|
|
} else {
|
|
temp[0] <<= 8;
|
|
input = this.stream.get8();
|
|
|
|
if (input === 0xFF) {
|
|
this.marker = this.stream.get8();
|
|
if (this.marker !== 0) {
|
|
this.markerIndex = 9;
|
|
}
|
|
}
|
|
|
|
temp[0] |= input;
|
|
index[0] += 8;
|
|
|
|
if (index[0] < 0) {
|
|
if (this.markerIndex !== 0) {
|
|
this.markerIndex = 0;
|
|
return (0xFF00 | this.marker) << 8;
|
|
}
|
|
|
|
temp[0] <<= 8;
|
|
input = this.stream.get8();
|
|
|
|
if (input === 0xFF) {
|
|
this.marker = this.stream.get8();
|
|
if (this.marker !== 0) {
|
|
this.markerIndex = 9;
|
|
}
|
|
}
|
|
|
|
temp[0] |= input;
|
|
index[0] += 8;
|
|
}
|
|
|
|
if (index[0] < 0) {
|
|
throw new Error("index=" + index[0] + " in getn()");
|
|
}
|
|
|
|
if (index[0] < this.markerIndex) {
|
|
this.markerIndex = 0;
|
|
return (0xFF00 | this.marker) << 8;
|
|
}
|
|
|
|
result = temp[0] >> index[0];
|
|
temp[0] &= (mask >> (16 - index[0]));
|
|
}
|
|
|
|
if (result < (one << (n - 1))) {
|
|
result += (n_one << n) + 1;
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.Decoder.prototype.getPreviousX = function (compOffset) {
|
|
/*jslint bitwise: true */
|
|
|
|
if (this.xLoc > 0) {
|
|
return this.getter((((this.yLoc * this.xDim) + this.xLoc) - 1), compOffset);
|
|
} else if (this.yLoc > 0) {
|
|
return this.getPreviousY(compOffset);
|
|
} else {
|
|
return (1 << (this.frame.precision - 1));
|
|
}
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.Decoder.prototype.getPreviousXY = function (compOffset) {
|
|
/*jslint bitwise: true */
|
|
|
|
if ((this.xLoc > 0) && (this.yLoc > 0)) {
|
|
return this.getter(((((this.yLoc - 1) * this.xDim) + this.xLoc) - 1), compOffset);
|
|
} else {
|
|
return this.getPreviousY(compOffset);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.Decoder.prototype.getPreviousY = function (compOffset) {
|
|
/*jslint bitwise: true */
|
|
|
|
if (this.yLoc > 0) {
|
|
return this.getter((((this.yLoc - 1) * this.xDim) + this.xLoc), compOffset);
|
|
} else {
|
|
return this.getPreviousX(compOffset);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.Decoder.prototype.isLastPixel = function () {
|
|
return (this.xLoc === (this.xDim - 1)) && (this.yLoc === (this.yDim - 1));
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.Decoder.prototype.outputSingle = function (PRED) {
|
|
if ((this.xLoc < this.xDim) && (this.yLoc < this.yDim)) {
|
|
this.setter((((this.yLoc * this.xDim) + this.xLoc)), this.mask & PRED[0]);
|
|
|
|
this.xLoc+=1;
|
|
|
|
if (this.xLoc >= this.xDim) {
|
|
this.yLoc+=1;
|
|
this.xLoc = 0;
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.Decoder.prototype.outputRGB = function (PRED) {
|
|
var offset = ((this.yLoc * this.xDim) + this.xLoc);
|
|
|
|
if ((this.xLoc < this.xDim) && (this.yLoc < this.yDim)) {
|
|
this.setter(offset, PRED[0], 0);
|
|
this.setter(offset, PRED[1], 1);
|
|
this.setter(offset, PRED[2], 2);
|
|
|
|
this.xLoc+=1;
|
|
|
|
if (this.xLoc >= this.xDim) {
|
|
this.yLoc+=1;
|
|
this.xLoc = 0;
|
|
}
|
|
}
|
|
};
|
|
|
|
jpeg.lossless.Decoder.prototype.setValue8 = function (index, val) {
|
|
this.outputData[index] = val;
|
|
};
|
|
|
|
jpeg.lossless.Decoder.prototype.getValue8 = function (index) {
|
|
return this.outputData[index]; // mask should not be necessary because outputData is either Int8Array or Int16Array
|
|
};
|
|
|
|
var littleEndian = (function() {
|
|
var buffer = new ArrayBuffer(2);
|
|
new DataView(buffer).setInt16(0, 256, true /* littleEndian */);
|
|
// Int16Array uses the platform's endianness.
|
|
return new Int16Array(buffer)[0] === 256;
|
|
})();
|
|
|
|
if (littleEndian) {
|
|
// just reading from an array is fine then. Int16Array will use platform endianness.
|
|
jpeg.lossless.Decoder.prototype.setValue16 = jpeg.lossless.Decoder.prototype.setValue8;
|
|
jpeg.lossless.Decoder.prototype.getValue16 = jpeg.lossless.Decoder.prototype.getValue8;
|
|
}
|
|
else {
|
|
// If platform is big-endian, we will need to convert to little-endian
|
|
jpeg.lossless.Decoder.prototype.setValue16 = function (index, val) {
|
|
this.outputData[index] = ((val & 0xFF) << 8) | ((val >> 8) & 0xFF);
|
|
};
|
|
|
|
jpeg.lossless.Decoder.prototype.getValue16 = function (index) {
|
|
var val = this.outputData[index];
|
|
return ((val & 0xFF) << 8) | ((val >> 8) & 0xFF);
|
|
};
|
|
}
|
|
|
|
jpeg.lossless.Decoder.prototype.setValueRGB = function (index, val, compOffset) {
|
|
// this.outputData.setUint8(index * 3 + compOffset, val);
|
|
this.outputData[index * 3 + compOffset] = val;
|
|
};
|
|
|
|
jpeg.lossless.Decoder.prototype.getValueRGB = function (index, compOffset) {
|
|
// return this.outputData.getUint8(index * 3 + compOffset);
|
|
return this.outputData[index * 3 + compOffset];
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.Decoder.prototype.readApp = function() {
|
|
var count = 0, length = this.stream.get16();
|
|
count += 2;
|
|
|
|
while (count < length) {
|
|
this.stream.get8();
|
|
count+=1;
|
|
}
|
|
|
|
return length;
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.Decoder.prototype.readComment = function () {
|
|
var sb = "", count = 0, length;
|
|
|
|
length = this.stream.get16();
|
|
count += 2;
|
|
|
|
while (count < length) {
|
|
sb += this.stream.get8();
|
|
count+=1;
|
|
}
|
|
|
|
return sb;
|
|
};
|
|
|
|
|
|
|
|
jpeg.lossless.Decoder.prototype.readNumber = function() {
|
|
var Ld = this.stream.get16();
|
|
|
|
if (Ld !== 4) {
|
|
throw new Error("ERROR: Define number format throw new IOException [Ld!=4]");
|
|
}
|
|
|
|
return this.stream.get16();
|
|
};
|
|
|
|
|
|
|
|
/*** Exports ***/
|
|
|
|
var moduleType = typeof module;
|
|
if ((moduleType !== 'undefined') && module.exports) {
|
|
module.exports = jpeg.lossless.Decoder;
|
|
}
|
|
|
|
},{"./data-stream.js":9,"./frame-header.js":11,"./huffman-table.js":12,"./quantization-table.js":14,"./scan-header.js":16,"./utils.js":17}],11:[function(require,module,exports){
|
|
/*
|
|
* Copyright (C) 2015 Michael Martinez
|
|
* Changes: Added support for selection values 2-7, fixed minor bugs &
|
|
* warnings, split into multiple class files, and general clean up.
|
|
*
|
|
* 08-25-2015: Helmut Dersch agreed to a license change from LGPL to MIT.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) Helmut Dersch
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
/*jslint browser: true, node: true */
|
|
/*global require, module */
|
|
|
|
"use strict";
|
|
|
|
/*** Imports ***/
|
|
var jpeg = jpeg || {};
|
|
jpeg.lossless = jpeg.lossless || {};
|
|
jpeg.lossless.ComponentSpec = jpeg.lossless.ComponentSpec || ((typeof require !== 'undefined') ? require('./component-spec.js') : null);
|
|
jpeg.lossless.DataStream = jpeg.lossless.DataStream || ((typeof require !== 'undefined') ? require('./data-stream.js') : null);
|
|
|
|
|
|
/*** Constructor ***/
|
|
jpeg.lossless.FrameHeader = jpeg.lossless.FrameHeader || function () {
|
|
this.components = []; // Components
|
|
this.dimX = 0; // Number of samples per line
|
|
this.dimY = 0; // Number of lines
|
|
this.numComp = 0; // Number of component in the frame
|
|
this.precision = 0; // Sample Precision (from the original image)
|
|
};
|
|
|
|
|
|
|
|
/*** Prototype Methods ***/
|
|
|
|
jpeg.lossless.FrameHeader.prototype.read = function (data) {
|
|
/*jslint bitwise: true */
|
|
|
|
var count = 0, length, i, c, temp;
|
|
|
|
length = data.get16();
|
|
count += 2;
|
|
|
|
this.precision = data.get8();
|
|
count+=1;
|
|
|
|
this.dimY = data.get16();
|
|
count += 2;
|
|
|
|
this.dimX = data.get16();
|
|
count += 2;
|
|
|
|
this.numComp = data.get8();
|
|
count+=1;
|
|
for (i = 1; i <= this.numComp; i+=1) {
|
|
if (count > length) {
|
|
throw new Error("ERROR: frame format error");
|
|
}
|
|
|
|
c = data.get8();
|
|
count+=1;
|
|
|
|
if (count >= length) {
|
|
throw new Error("ERROR: frame format error [c>=Lf]");
|
|
}
|
|
|
|
temp = data.get8();
|
|
count+=1;
|
|
|
|
if (!this.components[c]) {
|
|
this.components[c] = new jpeg.lossless.ComponentSpec();
|
|
}
|
|
|
|
this.components[c].hSamp = temp >> 4;
|
|
this.components[c].vSamp = temp & 0x0F;
|
|
this.components[c].quantTableSel = data.get8();
|
|
count+=1;
|
|
}
|
|
|
|
if (count !== length) {
|
|
throw new Error("ERROR: frame format error [Lf!=count]");
|
|
}
|
|
|
|
return 1;
|
|
};
|
|
|
|
|
|
/*** Exports ***/
|
|
|
|
var moduleType = typeof module;
|
|
if ((moduleType !== 'undefined') && module.exports) {
|
|
module.exports = jpeg.lossless.FrameHeader;
|
|
}
|
|
|
|
},{"./component-spec.js":8,"./data-stream.js":9}],12:[function(require,module,exports){
|
|
/*
|
|
* Copyright (C) 2015 Michael Martinez
|
|
* Changes: Added support for selection values 2-7, fixed minor bugs &
|
|
* warnings, split into multiple class files, and general clean up.
|
|
*
|
|
* 08-25-2015: Helmut Dersch agreed to a license change from LGPL to MIT.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) Helmut Dersch
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
/*jslint browser: true, node: true */
|
|
/*global require, module */
|
|
|
|
"use strict";
|
|
|
|
/*** Imports ***/
|
|
var jpeg = jpeg || {};
|
|
jpeg.lossless = jpeg.lossless || {};
|
|
jpeg.lossless.DataStream = jpeg.lossless.DataStream || ((typeof require !== 'undefined') ? require('./data-stream.js') : null);
|
|
jpeg.lossless.Utils = jpeg.lossless.Utils || ((typeof require !== 'undefined') ? require('./utils.js') : null);
|
|
|
|
|
|
/*** Constructor ***/
|
|
jpeg.lossless.HuffmanTable = jpeg.lossless.HuffmanTable || function () {
|
|
this.l = jpeg.lossless.Utils.createArray(4, 2, 16);
|
|
this.th = [];
|
|
this.v = jpeg.lossless.Utils.createArray(4, 2, 16, 200);
|
|
this.tc = jpeg.lossless.Utils.createArray(4, 2);
|
|
|
|
this.tc[0][0] = 0;
|
|
this.tc[1][0] = 0;
|
|
this.tc[2][0] = 0;
|
|
this.tc[3][0] = 0;
|
|
this.tc[0][1] = 0;
|
|
this.tc[1][1] = 0;
|
|
this.tc[2][1] = 0;
|
|
this.tc[3][1] = 0;
|
|
this.th[0] = 0;
|
|
this.th[1] = 0;
|
|
this.th[2] = 0;
|
|
this.th[3] = 0;
|
|
};
|
|
|
|
|
|
|
|
/*** Static Pseudo-constants ***/
|
|
|
|
jpeg.lossless.HuffmanTable.MSB = 0x80000000;
|
|
|
|
|
|
/*** Prototype Methods ***/
|
|
|
|
jpeg.lossless.HuffmanTable.prototype.read = function(data, HuffTab) {
|
|
/*jslint bitwise: true */
|
|
|
|
var count = 0, length, temp, t, c, i, j;
|
|
|
|
length = data.get16();
|
|
count += 2;
|
|
|
|
while (count < length) {
|
|
temp = data.get8();
|
|
count+=1;
|
|
t = temp & 0x0F;
|
|
if (t > 3) {
|
|
throw new Error("ERROR: Huffman table ID > 3");
|
|
}
|
|
|
|
c = temp >> 4;
|
|
if (c > 2) {
|
|
throw new Error("ERROR: Huffman table [Table class > 2 ]");
|
|
}
|
|
|
|
this.th[t] = 1;
|
|
this.tc[t][c] = 1;
|
|
|
|
for (i = 0; i < 16; i+=1) {
|
|
this.l[t][c][i] = data.get8();
|
|
count+=1;
|
|
}
|
|
|
|
for (i = 0; i < 16; i+=1) {
|
|
for (j = 0; j < this.l[t][c][i]; j+=1) {
|
|
if (count > length) {
|
|
throw new Error("ERROR: Huffman table format error [count>Lh]");
|
|
}
|
|
|
|
this.v[t][c][i][j] = data.get8();
|
|
count+=1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (count !== length) {
|
|
throw new Error("ERROR: Huffman table format error [count!=Lf]");
|
|
}
|
|
|
|
for (i = 0; i < 4; i+=1) {
|
|
for (j = 0; j < 2; j+=1) {
|
|
if (this.tc[i][j] !== 0) {
|
|
this.buildHuffTable(HuffTab[i][j], this.l[i][j], this.v[i][j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
};
|
|
|
|
|
|
|
|
// Build_HuffTab()
|
|
// Parameter: t table ID
|
|
// c table class ( 0 for DC, 1 for AC )
|
|
// L[i] # of codewords which length is i
|
|
// V[i][j] Huffman Value (length=i)
|
|
// Effect:
|
|
// build up HuffTab[t][c] using L and V.
|
|
jpeg.lossless.HuffmanTable.prototype.buildHuffTable = function(tab, L, V) {
|
|
/*jslint bitwise: true */
|
|
|
|
var currentTable, temp, k, i, j, n;
|
|
temp = 256;
|
|
k = 0;
|
|
|
|
for (i = 0; i < 8; i+=1) { // i+1 is Code length
|
|
for (j = 0; j < L[i]; j+=1) {
|
|
for (n = 0; n < (temp >> (i + 1)); n+=1) {
|
|
tab[k] = V[i][j] | ((i + 1) << 8);
|
|
k+=1;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 1; k < 256; i+=1, k+=1) {
|
|
tab[k] = i | jpeg.lossless.HuffmanTable.MSB;
|
|
}
|
|
|
|
currentTable = 1;
|
|
k = 0;
|
|
|
|
for (i = 8; i < 16; i+=1) { // i+1 is Code length
|
|
for (j = 0; j < L[i]; j+=1) {
|
|
for (n = 0; n < (temp >> (i - 7)); n+=1) {
|
|
tab[(currentTable * 256) + k] = V[i][j] | ((i + 1) << 8);
|
|
k+=1;
|
|
}
|
|
|
|
if (k >= 256) {
|
|
if (k > 256) {
|
|
throw new Error("ERROR: Huffman table error(1)!");
|
|
}
|
|
|
|
k = 0;
|
|
currentTable+=1;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
/*** Exports ***/
|
|
|
|
var moduleType = typeof module;
|
|
if ((moduleType !== 'undefined') && module.exports) {
|
|
module.exports = jpeg.lossless.HuffmanTable;
|
|
}
|
|
|
|
},{"./data-stream.js":9,"./utils.js":17}],13:[function(require,module,exports){
|
|
/*jslint browser: true, node: true */
|
|
/*global require, module */
|
|
|
|
"use strict";
|
|
|
|
/*** Imports ****/
|
|
|
|
/**
|
|
* jpeg
|
|
* @type {*|{}}
|
|
*/
|
|
var jpeg = jpeg || {};
|
|
|
|
/**
|
|
* jpeg.lossless
|
|
* @type {*|{}}
|
|
*/
|
|
jpeg.lossless = jpeg.lossless || {};
|
|
|
|
|
|
jpeg.lossless.ComponentSpec = jpeg.lossless.ComponentSpec || ((typeof require !== 'undefined') ? require('./component-spec.js') : null);
|
|
jpeg.lossless.DataStream = jpeg.lossless.DataStream || ((typeof require !== 'undefined') ? require('./data-stream.js') : null);
|
|
jpeg.lossless.Decoder = jpeg.lossless.Decoder || ((typeof require !== 'undefined') ? require('./decoder.js') : null);
|
|
jpeg.lossless.FrameHeader = jpeg.lossless.FrameHeader || ((typeof require !== 'undefined') ? require('./frame-header.js') : null);
|
|
jpeg.lossless.HuffmanTable = jpeg.lossless.HuffmanTable || ((typeof require !== 'undefined') ? require('./huffman-table.js') : null);
|
|
jpeg.lossless.QuantizationTable = jpeg.lossless.QuantizationTable || ((typeof require !== 'undefined') ? require('./quantization-table.js') : null);
|
|
jpeg.lossless.ScanComponent = jpeg.lossless.ScanComponent || ((typeof require !== 'undefined') ? require('./scan-component.js') : null);
|
|
jpeg.lossless.ScanHeader = jpeg.lossless.ScanHeader || ((typeof require !== 'undefined') ? require('./scan-header.js') : null);
|
|
jpeg.lossless.Utils = jpeg.lossless.Utils || ((typeof require !== 'undefined') ? require('./utils.js') : null);
|
|
|
|
|
|
/*** Exports ***/
|
|
var moduleType = typeof module;
|
|
if ((moduleType !== 'undefined') && module.exports) {
|
|
module.exports = jpeg;
|
|
}
|
|
|
|
},{"./component-spec.js":8,"./data-stream.js":9,"./decoder.js":10,"./frame-header.js":11,"./huffman-table.js":12,"./quantization-table.js":14,"./scan-component.js":15,"./scan-header.js":16,"./utils.js":17}],14:[function(require,module,exports){
|
|
/*
|
|
* Copyright (C) 2015 Michael Martinez
|
|
* Changes: Added support for selection values 2-7, fixed minor bugs &
|
|
* warnings, split into multiple class files, and general clean up.
|
|
*
|
|
* 08-25-2015: Helmut Dersch agreed to a license change from LGPL to MIT.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) Helmut Dersch
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
/*jslint browser: true, node: true */
|
|
/*global require, module */
|
|
|
|
"use strict";
|
|
|
|
/*** Imports ***/
|
|
var jpeg = jpeg || {};
|
|
jpeg.lossless = jpeg.lossless || {};
|
|
jpeg.lossless.DataStream = jpeg.lossless.DataStream || ((typeof require !== 'undefined') ? require('./data-stream.js') : null);
|
|
jpeg.lossless.Utils = jpeg.lossless.Utils || ((typeof require !== 'undefined') ? require('./utils.js') : null);
|
|
|
|
|
|
/*** Constructor ***/
|
|
jpeg.lossless.QuantizationTable = jpeg.lossless.QuantizationTable || function () {
|
|
this.precision = []; // Quantization precision 8 or 16
|
|
this.tq = []; // 1: this table is presented
|
|
this.quantTables = jpeg.lossless.Utils.createArray(4, 64); // Tables
|
|
|
|
this.tq[0] = 0;
|
|
this.tq[1] = 0;
|
|
this.tq[2] = 0;
|
|
this.tq[3] = 0;
|
|
};
|
|
|
|
|
|
|
|
/*** Static Methods ***/
|
|
|
|
jpeg.lossless.QuantizationTable.enhanceQuantizationTable = function(qtab, table) {
|
|
/*jslint bitwise: true */
|
|
|
|
var i;
|
|
|
|
for (i = 0; i < 8; i+=1) {
|
|
qtab[table[(0 * 8) + i]] *= 90;
|
|
qtab[table[(4 * 8) + i]] *= 90;
|
|
qtab[table[(2 * 8) + i]] *= 118;
|
|
qtab[table[(6 * 8) + i]] *= 49;
|
|
qtab[table[(5 * 8) + i]] *= 71;
|
|
qtab[table[(1 * 8) + i]] *= 126;
|
|
qtab[table[(7 * 8) + i]] *= 25;
|
|
qtab[table[(3 * 8) + i]] *= 106;
|
|
}
|
|
|
|
for (i = 0; i < 8; i+=1) {
|
|
qtab[table[0 + (8 * i)]] *= 90;
|
|
qtab[table[4 + (8 * i)]] *= 90;
|
|
qtab[table[2 + (8 * i)]] *= 118;
|
|
qtab[table[6 + (8 * i)]] *= 49;
|
|
qtab[table[5 + (8 * i)]] *= 71;
|
|
qtab[table[1 + (8 * i)]] *= 126;
|
|
qtab[table[7 + (8 * i)]] *= 25;
|
|
qtab[table[3 + (8 * i)]] *= 106;
|
|
}
|
|
|
|
for (i = 0; i < 64; i+=1) {
|
|
qtab[i] >>= 6;
|
|
}
|
|
};
|
|
|
|
|
|
/*** Prototype Methods ***/
|
|
|
|
jpeg.lossless.QuantizationTable.prototype.read = function (data, table) {
|
|
/*jslint bitwise: true */
|
|
|
|
var count = 0, length, temp, t, i;
|
|
|
|
length = data.get16();
|
|
count += 2;
|
|
|
|
while (count < length) {
|
|
temp = data.get8();
|
|
count+=1;
|
|
t = temp & 0x0F;
|
|
|
|
if (t > 3) {
|
|
throw new Error("ERROR: Quantization table ID > 3");
|
|
}
|
|
|
|
this.precision[t] = temp >> 4;
|
|
|
|
if (this.precision[t] === 0) {
|
|
this.precision[t] = 8;
|
|
} else if (this.precision[t] === 1) {
|
|
this.precision[t] = 16;
|
|
} else {
|
|
throw new Error("ERROR: Quantization table precision error");
|
|
}
|
|
|
|
this.tq[t] = 1;
|
|
|
|
if (this.precision[t] === 8) {
|
|
for (i = 0; i < 64; i+=1) {
|
|
if (count > length) {
|
|
throw new Error("ERROR: Quantization table format error");
|
|
}
|
|
|
|
this.quantTables[t][i] = data.get8();
|
|
count+=1;
|
|
}
|
|
|
|
jpeg.lossless.QuantizationTable.enhanceQuantizationTable(this.quantTables[t], table);
|
|
} else {
|
|
for (i = 0; i < 64; i+=1) {
|
|
if (count > length) {
|
|
throw new Error("ERROR: Quantization table format error");
|
|
}
|
|
|
|
this.quantTables[t][i] = data.get16();
|
|
count += 2;
|
|
}
|
|
|
|
jpeg.lossless.QuantizationTable.enhanceQuantizationTable(this.quantTables[t], table);
|
|
}
|
|
}
|
|
|
|
if (count !== length) {
|
|
throw new Error("ERROR: Quantization table error [count!=Lq]");
|
|
}
|
|
|
|
return 1;
|
|
};
|
|
|
|
|
|
|
|
/*** Exports ***/
|
|
|
|
var moduleType = typeof module;
|
|
if ((moduleType !== 'undefined') && module.exports) {
|
|
module.exports = jpeg.lossless.QuantizationTable;
|
|
}
|
|
|
|
},{"./data-stream.js":9,"./utils.js":17}],15:[function(require,module,exports){
|
|
/*
|
|
* Copyright (C) 2015 Michael Martinez
|
|
* Changes: Added support for selection values 2-7, fixed minor bugs &
|
|
* warnings, split into multiple class files, and general clean up.
|
|
*
|
|
* 08-25-2015: Helmut Dersch agreed to a license change from LGPL to MIT.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) Helmut Dersch
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
/*jslint browser: true, node: true */
|
|
/*global require, module */
|
|
|
|
"use strict";
|
|
|
|
/*** Imports ***/
|
|
var jpeg = jpeg || {};
|
|
jpeg.lossless = jpeg.lossless || {};
|
|
|
|
|
|
/*** Constructor ***/
|
|
jpeg.lossless.ScanComponent = jpeg.lossless.ScanComponent || function () {
|
|
this.acTabSel = 0; // AC table selector
|
|
this.dcTabSel = 0; // DC table selector
|
|
this.scanCompSel = 0; // Scan component selector
|
|
};
|
|
|
|
|
|
|
|
/*** Exports ***/
|
|
|
|
var moduleType = typeof module;
|
|
if ((moduleType !== 'undefined') && module.exports) {
|
|
module.exports = jpeg.lossless.ScanComponent;
|
|
}
|
|
|
|
},{}],16:[function(require,module,exports){
|
|
/*
|
|
* Copyright (C) 2015 Michael Martinez
|
|
* Changes: Added support for selection values 2-7, fixed minor bugs &
|
|
* warnings, split into multiple class files, and general clean up.
|
|
*
|
|
* 08-25-2015: Helmut Dersch agreed to a license change from LGPL to MIT.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) Helmut Dersch
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
/*jslint browser: true, node: true */
|
|
/*global require, module */
|
|
|
|
"use strict";
|
|
|
|
/*** Imports ***/
|
|
var jpeg = jpeg || {};
|
|
jpeg.lossless = jpeg.lossless || {};
|
|
jpeg.lossless.DataStream = jpeg.lossless.DataStream || ((typeof require !== 'undefined') ? require('./data-stream.js') : null);
|
|
jpeg.lossless.ScanComponent = jpeg.lossless.ScanComponent || ((typeof require !== 'undefined') ? require('./scan-component.js') : null);
|
|
|
|
|
|
/*** Constructor ***/
|
|
jpeg.lossless.ScanHeader = jpeg.lossless.ScanHeader || function () {
|
|
this.ah = 0;
|
|
this.al = 0;
|
|
this.numComp = 0; // Number of components in the scan
|
|
this.selection = 0; // Start of spectral or predictor selection
|
|
this.spectralEnd = 0; // End of spectral selection
|
|
this.components = [];
|
|
};
|
|
|
|
|
|
/*** Prototype Methods ***/
|
|
|
|
jpeg.lossless.ScanHeader.prototype.read = function(data) {
|
|
/*jslint bitwise: true */
|
|
|
|
var count = 0, length, i, temp;
|
|
|
|
length = data.get16();
|
|
count += 2;
|
|
|
|
this.numComp = data.get8();
|
|
count+=1;
|
|
|
|
for (i = 0; i < this.numComp; i+=1) {
|
|
this.components[i] = new jpeg.lossless.ScanComponent();
|
|
|
|
if (count > length) {
|
|
throw new Error("ERROR: scan header format error");
|
|
}
|
|
|
|
this.components[i].scanCompSel = data.get8();
|
|
count+=1;
|
|
|
|
temp = data.get8();
|
|
count+=1;
|
|
|
|
this.components[i].dcTabSel = (temp >> 4);
|
|
this.components[i].acTabSel = (temp & 0x0F);
|
|
}
|
|
|
|
this.selection = data.get8();
|
|
count+=1;
|
|
|
|
this.spectralEnd = data.get8();
|
|
count+=1;
|
|
|
|
temp = data.get8();
|
|
this.ah = (temp >> 4);
|
|
this.al = (temp & 0x0F);
|
|
count+=1;
|
|
|
|
if (count !== length) {
|
|
throw new Error("ERROR: scan header format error [count!=Ns]");
|
|
}
|
|
|
|
return 1;
|
|
};
|
|
|
|
|
|
|
|
/*** Exports ***/
|
|
|
|
var moduleType = typeof module;
|
|
if ((moduleType !== 'undefined') && module.exports) {
|
|
module.exports = jpeg.lossless.ScanHeader;
|
|
}
|
|
|
|
},{"./data-stream.js":9,"./scan-component.js":15}],17:[function(require,module,exports){
|
|
/*
|
|
* Copyright (C) 2015 Michael Martinez
|
|
* Changes: Added support for selection values 2-7, fixed minor bugs &
|
|
* warnings, split into multiple class files, and general clean up.
|
|
*
|
|
* 08-25-2015: Helmut Dersch agreed to a license change from LGPL to MIT.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) Helmut Dersch
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
/*jslint browser: true, node: true */
|
|
/*global require, module */
|
|
|
|
"use strict";
|
|
|
|
/*** Imports ***/
|
|
var jpeg = jpeg || {};
|
|
jpeg.lossless = jpeg.lossless || {};
|
|
|
|
|
|
/*** Constructor ***/
|
|
jpeg.lossless.Utils = jpeg.lossless.Utils || {};
|
|
|
|
|
|
/*** Static methods ***/
|
|
|
|
// http://stackoverflow.com/questions/966225/how-can-i-create-a-two-dimensional-array-in-javascript
|
|
jpeg.lossless.Utils.createArray = function (length) {
|
|
var arr = new Array(length || 0),
|
|
i = length;
|
|
|
|
if (arguments.length > 1) {
|
|
var args = Array.prototype.slice.call(arguments, 1);
|
|
while(i--) arr[length-1 - i] = jpeg.lossless.Utils.createArray.apply(this, args);
|
|
}
|
|
|
|
return arr;
|
|
};
|
|
|
|
|
|
// http://stackoverflow.com/questions/18638900/javascript-crc32
|
|
jpeg.lossless.Utils.makeCRCTable = function(){
|
|
var c;
|
|
var crcTable = [];
|
|
for(var n =0; n < 256; n++){
|
|
c = n;
|
|
for(var k =0; k < 8; k++){
|
|
c = ((c&1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
|
|
}
|
|
crcTable[n] = c;
|
|
}
|
|
return crcTable;
|
|
};
|
|
|
|
jpeg.lossless.Utils.crc32 = function(dataView) {
|
|
var uint8view = new Uint8Array(dataView.buffer);
|
|
var crcTable = jpeg.lossless.Utils.crcTable || (jpeg.lossless.Utils.crcTable = jpeg.lossless.Utils.makeCRCTable());
|
|
var crc = 0 ^ (-1);
|
|
|
|
for (var i = 0; i < uint8view.length; i++ ) {
|
|
crc = (crc >>> 8) ^ crcTable[(crc ^ uint8view[i]) & 0xFF];
|
|
}
|
|
|
|
return (crc ^ (-1)) >>> 0;
|
|
};
|
|
|
|
|
|
/*** Exports ***/
|
|
|
|
var moduleType = typeof module;
|
|
if ((moduleType !== 'undefined') && module.exports) {
|
|
module.exports = jpeg.lossless.Utils;
|
|
}
|
|
|
|
},{}],18:[function(require,module,exports){
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.ungzip = exports.inflateRaw = exports.inflate = exports.gzip = exports.deflateRaw = exports.deflate = exports.default = exports.constants = exports.Inflate = exports.Deflate = void 0;
|
|
|
|
/*! pako 2.0.4 https://github.com/nodeca/pako @license (MIT AND Zlib) */
|
|
// (C) 1995-2013 Jean-loup Gailly and Mark Adler
|
|
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
|
|
//
|
|
// This software is provided 'as-is', without any express or implied
|
|
// warranty. In no event will the authors be held liable for any damages
|
|
// arising from the use of this software.
|
|
//
|
|
// Permission is granted to anyone to use this software for any purpose,
|
|
// including commercial applications, and to alter it and redistribute it
|
|
// freely, subject to the following restrictions:
|
|
//
|
|
// 1. The origin of this software must not be misrepresented; you must not
|
|
// claim that you wrote the original software. If you use this software
|
|
// in a product, an acknowledgment in the product documentation would be
|
|
// appreciated but is not required.
|
|
// 2. Altered source versions must be plainly marked as such, and must not be
|
|
// misrepresented as being the original software.
|
|
// 3. This notice may not be removed or altered from any source distribution.
|
|
|
|
/* eslint-disable space-unary-ops */
|
|
|
|
/* Public constants ==========================================================*/
|
|
|
|
/* ===========================================================================*/
|
|
//const Z_FILTERED = 1;
|
|
//const Z_HUFFMAN_ONLY = 2;
|
|
//const Z_RLE = 3;
|
|
const Z_FIXED$1 = 4; //const Z_DEFAULT_STRATEGY = 0;
|
|
|
|
/* Possible values of the data_type field (though see inflate()) */
|
|
|
|
const Z_BINARY = 0;
|
|
const Z_TEXT = 1; //const Z_ASCII = 1; // = Z_TEXT
|
|
|
|
const Z_UNKNOWN$1 = 2;
|
|
/*============================================================================*/
|
|
|
|
function zero$1(buf) {
|
|
let len = buf.length;
|
|
|
|
while (--len >= 0) {
|
|
buf[len] = 0;
|
|
}
|
|
} // From zutil.h
|
|
|
|
|
|
const STORED_BLOCK = 0;
|
|
const STATIC_TREES = 1;
|
|
const DYN_TREES = 2;
|
|
/* The three kinds of block type */
|
|
|
|
const MIN_MATCH$1 = 3;
|
|
const MAX_MATCH$1 = 258;
|
|
/* The minimum and maximum match lengths */
|
|
// From deflate.h
|
|
|
|
/* ===========================================================================
|
|
* Internal compression state.
|
|
*/
|
|
|
|
const LENGTH_CODES$1 = 29;
|
|
/* number of length codes, not counting the special END_BLOCK code */
|
|
|
|
const LITERALS$1 = 256;
|
|
/* number of literal bytes 0..255 */
|
|
|
|
const L_CODES$1 = LITERALS$1 + 1 + LENGTH_CODES$1;
|
|
/* number of Literal or Length codes, including the END_BLOCK code */
|
|
|
|
const D_CODES$1 = 30;
|
|
/* number of distance codes */
|
|
|
|
const BL_CODES$1 = 19;
|
|
/* number of codes used to transfer the bit lengths */
|
|
|
|
const HEAP_SIZE$1 = 2 * L_CODES$1 + 1;
|
|
/* maximum heap size */
|
|
|
|
const MAX_BITS$1 = 15;
|
|
/* All codes must not exceed MAX_BITS bits */
|
|
|
|
const Buf_size = 16;
|
|
/* size of bit buffer in bi_buf */
|
|
|
|
/* ===========================================================================
|
|
* Constants
|
|
*/
|
|
|
|
const MAX_BL_BITS = 7;
|
|
/* Bit length codes must not exceed MAX_BL_BITS bits */
|
|
|
|
const END_BLOCK = 256;
|
|
/* end of block literal code */
|
|
|
|
const REP_3_6 = 16;
|
|
/* repeat previous bit length 3-6 times (2 bits of repeat count) */
|
|
|
|
const REPZ_3_10 = 17;
|
|
/* repeat a zero length 3-10 times (3 bits of repeat count) */
|
|
|
|
const REPZ_11_138 = 18;
|
|
/* repeat a zero length 11-138 times (7 bits of repeat count) */
|
|
|
|
/* eslint-disable comma-spacing,array-bracket-spacing */
|
|
|
|
const extra_lbits =
|
|
/* extra bits for each length code */
|
|
new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0]);
|
|
const extra_dbits =
|
|
/* extra bits for each distance code */
|
|
new Uint8Array([0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13]);
|
|
const extra_blbits =
|
|
/* extra bits for each bit length code */
|
|
new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7]);
|
|
const bl_order = new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
|
|
/* eslint-enable comma-spacing,array-bracket-spacing */
|
|
|
|
/* The lengths of the bit length codes are sent in order of decreasing
|
|
* probability, to avoid transmitting the lengths for unused bit length codes.
|
|
*/
|
|
|
|
/* ===========================================================================
|
|
* Local data. These are initialized only once.
|
|
*/
|
|
// We pre-fill arrays with 0 to avoid uninitialized gaps
|
|
|
|
const DIST_CODE_LEN = 512;
|
|
/* see definition of array dist_code below */
|
|
// !!!! Use flat array instead of structure, Freq = i*2, Len = i*2+1
|
|
|
|
const static_ltree = new Array((L_CODES$1 + 2) * 2);
|
|
zero$1(static_ltree);
|
|
/* The static literal tree. Since the bit lengths are imposed, there is no
|
|
* need for the L_CODES extra codes used during heap construction. However
|
|
* The codes 286 and 287 are needed to build a canonical tree (see _tr_init
|
|
* below).
|
|
*/
|
|
|
|
const static_dtree = new Array(D_CODES$1 * 2);
|
|
zero$1(static_dtree);
|
|
/* The static distance tree. (Actually a trivial tree since all codes use
|
|
* 5 bits.)
|
|
*/
|
|
|
|
const _dist_code = new Array(DIST_CODE_LEN);
|
|
|
|
zero$1(_dist_code);
|
|
/* Distance codes. The first 256 values correspond to the distances
|
|
* 3 .. 258, the last 256 values correspond to the top 8 bits of
|
|
* the 15 bit distances.
|
|
*/
|
|
|
|
const _length_code = new Array(MAX_MATCH$1 - MIN_MATCH$1 + 1);
|
|
|
|
zero$1(_length_code);
|
|
/* length code for each normalized match length (0 == MIN_MATCH) */
|
|
|
|
const base_length = new Array(LENGTH_CODES$1);
|
|
zero$1(base_length);
|
|
/* First normalized length for each code (0 = MIN_MATCH) */
|
|
|
|
const base_dist = new Array(D_CODES$1);
|
|
zero$1(base_dist);
|
|
/* First normalized distance for each code (0 = distance of 1) */
|
|
|
|
function StaticTreeDesc(static_tree, extra_bits, extra_base, elems, max_length) {
|
|
this.static_tree = static_tree;
|
|
/* static tree or NULL */
|
|
|
|
this.extra_bits = extra_bits;
|
|
/* extra bits for each code or NULL */
|
|
|
|
this.extra_base = extra_base;
|
|
/* base index for extra_bits */
|
|
|
|
this.elems = elems;
|
|
/* max number of elements in the tree */
|
|
|
|
this.max_length = max_length;
|
|
/* max bit length for the codes */
|
|
// show if `static_tree` has data or dummy - needed for monomorphic objects
|
|
|
|
this.has_stree = static_tree && static_tree.length;
|
|
}
|
|
|
|
let static_l_desc;
|
|
let static_d_desc;
|
|
let static_bl_desc;
|
|
|
|
function TreeDesc(dyn_tree, stat_desc) {
|
|
this.dyn_tree = dyn_tree;
|
|
/* the dynamic tree */
|
|
|
|
this.max_code = 0;
|
|
/* largest code with non zero frequency */
|
|
|
|
this.stat_desc = stat_desc;
|
|
/* the corresponding static tree */
|
|
}
|
|
|
|
const d_code = dist => {
|
|
return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)];
|
|
};
|
|
/* ===========================================================================
|
|
* Output a short LSB first on the stream.
|
|
* IN assertion: there is enough room in pendingBuf.
|
|
*/
|
|
|
|
|
|
const put_short = (s, w) => {
|
|
// put_byte(s, (uch)((w) & 0xff));
|
|
// put_byte(s, (uch)((ush)(w) >> 8));
|
|
s.pending_buf[s.pending++] = w & 0xff;
|
|
s.pending_buf[s.pending++] = w >>> 8 & 0xff;
|
|
};
|
|
/* ===========================================================================
|
|
* Send a value on a given number of bits.
|
|
* IN assertion: length <= 16 and value fits in length bits.
|
|
*/
|
|
|
|
|
|
const send_bits = (s, value, length) => {
|
|
if (s.bi_valid > Buf_size - length) {
|
|
s.bi_buf |= value << s.bi_valid & 0xffff;
|
|
put_short(s, s.bi_buf);
|
|
s.bi_buf = value >> Buf_size - s.bi_valid;
|
|
s.bi_valid += length - Buf_size;
|
|
} else {
|
|
s.bi_buf |= value << s.bi_valid & 0xffff;
|
|
s.bi_valid += length;
|
|
}
|
|
};
|
|
|
|
const send_code = (s, c, tree) => {
|
|
send_bits(s, tree[c * 2]
|
|
/*.Code*/
|
|
, tree[c * 2 + 1]
|
|
/*.Len*/
|
|
);
|
|
};
|
|
/* ===========================================================================
|
|
* Reverse the first len bits of a code, using straightforward code (a faster
|
|
* method would use a table)
|
|
* IN assertion: 1 <= len <= 15
|
|
*/
|
|
|
|
|
|
const bi_reverse = (code, len) => {
|
|
let res = 0;
|
|
|
|
do {
|
|
res |= code & 1;
|
|
code >>>= 1;
|
|
res <<= 1;
|
|
} while (--len > 0);
|
|
|
|
return res >>> 1;
|
|
};
|
|
/* ===========================================================================
|
|
* Flush the bit buffer, keeping at most 7 bits in it.
|
|
*/
|
|
|
|
|
|
const bi_flush = s => {
|
|
if (s.bi_valid === 16) {
|
|
put_short(s, s.bi_buf);
|
|
s.bi_buf = 0;
|
|
s.bi_valid = 0;
|
|
} else if (s.bi_valid >= 8) {
|
|
s.pending_buf[s.pending++] = s.bi_buf & 0xff;
|
|
s.bi_buf >>= 8;
|
|
s.bi_valid -= 8;
|
|
}
|
|
};
|
|
/* ===========================================================================
|
|
* Compute the optimal bit lengths for a tree and update the total bit length
|
|
* for the current block.
|
|
* IN assertion: the fields freq and dad are set, heap[heap_max] and
|
|
* above are the tree nodes sorted by increasing frequency.
|
|
* OUT assertions: the field len is set to the optimal bit length, the
|
|
* array bl_count contains the frequencies for each bit length.
|
|
* The length opt_len is updated; static_len is also updated if stree is
|
|
* not null.
|
|
*/
|
|
|
|
|
|
const gen_bitlen = (s, desc) => // deflate_state *s;
|
|
// tree_desc *desc; /* the tree descriptor */
|
|
{
|
|
const tree = desc.dyn_tree;
|
|
const max_code = desc.max_code;
|
|
const stree = desc.stat_desc.static_tree;
|
|
const has_stree = desc.stat_desc.has_stree;
|
|
const extra = desc.stat_desc.extra_bits;
|
|
const base = desc.stat_desc.extra_base;
|
|
const max_length = desc.stat_desc.max_length;
|
|
let h;
|
|
/* heap index */
|
|
|
|
let n, m;
|
|
/* iterate over the tree elements */
|
|
|
|
let bits;
|
|
/* bit length */
|
|
|
|
let xbits;
|
|
/* extra bits */
|
|
|
|
let f;
|
|
/* frequency */
|
|
|
|
let overflow = 0;
|
|
/* number of elements with bit length too large */
|
|
|
|
for (bits = 0; bits <= MAX_BITS$1; bits++) {
|
|
s.bl_count[bits] = 0;
|
|
}
|
|
/* In a first pass, compute the optimal bit lengths (which may
|
|
* overflow in the case of the bit length tree).
|
|
*/
|
|
|
|
|
|
tree[s.heap[s.heap_max] * 2 + 1]
|
|
/*.Len*/
|
|
= 0;
|
|
/* root of the heap */
|
|
|
|
for (h = s.heap_max + 1; h < HEAP_SIZE$1; h++) {
|
|
n = s.heap[h];
|
|
bits = tree[tree[n * 2 + 1]
|
|
/*.Dad*/
|
|
* 2 + 1]
|
|
/*.Len*/
|
|
+ 1;
|
|
|
|
if (bits > max_length) {
|
|
bits = max_length;
|
|
overflow++;
|
|
}
|
|
|
|
tree[n * 2 + 1]
|
|
/*.Len*/
|
|
= bits;
|
|
/* We overwrite tree[n].Dad which is no longer needed */
|
|
|
|
if (n > max_code) {
|
|
continue;
|
|
}
|
|
/* not a leaf node */
|
|
|
|
|
|
s.bl_count[bits]++;
|
|
xbits = 0;
|
|
|
|
if (n >= base) {
|
|
xbits = extra[n - base];
|
|
}
|
|
|
|
f = tree[n * 2]
|
|
/*.Freq*/
|
|
;
|
|
s.opt_len += f * (bits + xbits);
|
|
|
|
if (has_stree) {
|
|
s.static_len += f * (stree[n * 2 + 1]
|
|
/*.Len*/
|
|
+ xbits);
|
|
}
|
|
}
|
|
|
|
if (overflow === 0) {
|
|
return;
|
|
} // Trace((stderr,"\nbit length overflow\n"));
|
|
|
|
/* This happens for example on obj2 and pic of the Calgary corpus */
|
|
|
|
/* Find the first bit length which could increase: */
|
|
|
|
|
|
do {
|
|
bits = max_length - 1;
|
|
|
|
while (s.bl_count[bits] === 0) {
|
|
bits--;
|
|
}
|
|
|
|
s.bl_count[bits]--;
|
|
/* move one leaf down the tree */
|
|
|
|
s.bl_count[bits + 1] += 2;
|
|
/* move one overflow item as its brother */
|
|
|
|
s.bl_count[max_length]--;
|
|
/* The brother of the overflow item also moves one step up,
|
|
* but this does not affect bl_count[max_length]
|
|
*/
|
|
|
|
overflow -= 2;
|
|
} while (overflow > 0);
|
|
/* Now recompute all bit lengths, scanning in increasing frequency.
|
|
* h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
|
|
* lengths instead of fixing only the wrong ones. This idea is taken
|
|
* from 'ar' written by Haruhiko Okumura.)
|
|
*/
|
|
|
|
|
|
for (bits = max_length; bits !== 0; bits--) {
|
|
n = s.bl_count[bits];
|
|
|
|
while (n !== 0) {
|
|
m = s.heap[--h];
|
|
|
|
if (m > max_code) {
|
|
continue;
|
|
}
|
|
|
|
if (tree[m * 2 + 1]
|
|
/*.Len*/
|
|
!== bits) {
|
|
// Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
|
|
s.opt_len += (bits - tree[m * 2 + 1]
|
|
/*.Len*/
|
|
) * tree[m * 2]
|
|
/*.Freq*/
|
|
;
|
|
tree[m * 2 + 1]
|
|
/*.Len*/
|
|
= bits;
|
|
}
|
|
|
|
n--;
|
|
}
|
|
}
|
|
};
|
|
/* ===========================================================================
|
|
* Generate the codes for a given tree and bit counts (which need not be
|
|
* optimal).
|
|
* IN assertion: the array bl_count contains the bit length statistics for
|
|
* the given tree and the field len is set for all tree elements.
|
|
* OUT assertion: the field code is set for all tree elements of non
|
|
* zero code length.
|
|
*/
|
|
|
|
|
|
const gen_codes = (tree, max_code, bl_count) => // ct_data *tree; /* the tree to decorate */
|
|
// int max_code; /* largest code with non zero frequency */
|
|
// ushf *bl_count; /* number of codes at each bit length */
|
|
{
|
|
const next_code = new Array(MAX_BITS$1 + 1);
|
|
/* next code value for each bit length */
|
|
|
|
let code = 0;
|
|
/* running code value */
|
|
|
|
let bits;
|
|
/* bit index */
|
|
|
|
let n;
|
|
/* code index */
|
|
|
|
/* The distribution counts are first used to generate the code values
|
|
* without bit reversal.
|
|
*/
|
|
|
|
for (bits = 1; bits <= MAX_BITS$1; bits++) {
|
|
next_code[bits] = code = code + bl_count[bits - 1] << 1;
|
|
}
|
|
/* Check that the bit counts in bl_count are consistent. The last code
|
|
* must be all ones.
|
|
*/
|
|
//Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
|
|
// "inconsistent bit counts");
|
|
//Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
|
|
|
|
|
|
for (n = 0; n <= max_code; n++) {
|
|
let len = tree[n * 2 + 1]
|
|
/*.Len*/
|
|
;
|
|
|
|
if (len === 0) {
|
|
continue;
|
|
}
|
|
/* Now reverse the bits */
|
|
|
|
|
|
tree[n * 2]
|
|
/*.Code*/
|
|
= bi_reverse(next_code[len]++, len); //Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
|
|
// n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
|
|
}
|
|
};
|
|
/* ===========================================================================
|
|
* Initialize the various 'constant' tables.
|
|
*/
|
|
|
|
|
|
const tr_static_init = () => {
|
|
let n;
|
|
/* iterates over tree elements */
|
|
|
|
let bits;
|
|
/* bit counter */
|
|
|
|
let length;
|
|
/* length value */
|
|
|
|
let code;
|
|
/* code value */
|
|
|
|
let dist;
|
|
/* distance index */
|
|
|
|
const bl_count = new Array(MAX_BITS$1 + 1);
|
|
/* number of codes at each bit length for an optimal tree */
|
|
// do check in _tr_init()
|
|
//if (static_init_done) return;
|
|
|
|
/* For some embedded targets, global variables are not initialized: */
|
|
|
|
/*#ifdef NO_INIT_GLOBAL_POINTERS
|
|
static_l_desc.static_tree = static_ltree;
|
|
static_l_desc.extra_bits = extra_lbits;
|
|
static_d_desc.static_tree = static_dtree;
|
|
static_d_desc.extra_bits = extra_dbits;
|
|
static_bl_desc.extra_bits = extra_blbits;
|
|
#endif*/
|
|
|
|
/* Initialize the mapping length (0..255) -> length code (0..28) */
|
|
|
|
length = 0;
|
|
|
|
for (code = 0; code < LENGTH_CODES$1 - 1; code++) {
|
|
base_length[code] = length;
|
|
|
|
for (n = 0; n < 1 << extra_lbits[code]; n++) {
|
|
_length_code[length++] = code;
|
|
}
|
|
} //Assert (length == 256, "tr_static_init: length != 256");
|
|
|
|
/* Note that the length 255 (match length 258) can be represented
|
|
* in two different ways: code 284 + 5 bits or code 285, so we
|
|
* overwrite length_code[255] to use the best encoding:
|
|
*/
|
|
|
|
|
|
_length_code[length - 1] = code;
|
|
/* Initialize the mapping dist (0..32K) -> dist code (0..29) */
|
|
|
|
dist = 0;
|
|
|
|
for (code = 0; code < 16; code++) {
|
|
base_dist[code] = dist;
|
|
|
|
for (n = 0; n < 1 << extra_dbits[code]; n++) {
|
|
_dist_code[dist++] = code;
|
|
}
|
|
} //Assert (dist == 256, "tr_static_init: dist != 256");
|
|
|
|
|
|
dist >>= 7;
|
|
/* from now on, all distances are divided by 128 */
|
|
|
|
for (; code < D_CODES$1; code++) {
|
|
base_dist[code] = dist << 7;
|
|
|
|
for (n = 0; n < 1 << extra_dbits[code] - 7; n++) {
|
|
_dist_code[256 + dist++] = code;
|
|
}
|
|
} //Assert (dist == 256, "tr_static_init: 256+dist != 512");
|
|
|
|
/* Construct the codes of the static literal tree */
|
|
|
|
|
|
for (bits = 0; bits <= MAX_BITS$1; bits++) {
|
|
bl_count[bits] = 0;
|
|
}
|
|
|
|
n = 0;
|
|
|
|
while (n <= 143) {
|
|
static_ltree[n * 2 + 1]
|
|
/*.Len*/
|
|
= 8;
|
|
n++;
|
|
bl_count[8]++;
|
|
}
|
|
|
|
while (n <= 255) {
|
|
static_ltree[n * 2 + 1]
|
|
/*.Len*/
|
|
= 9;
|
|
n++;
|
|
bl_count[9]++;
|
|
}
|
|
|
|
while (n <= 279) {
|
|
static_ltree[n * 2 + 1]
|
|
/*.Len*/
|
|
= 7;
|
|
n++;
|
|
bl_count[7]++;
|
|
}
|
|
|
|
while (n <= 287) {
|
|
static_ltree[n * 2 + 1]
|
|
/*.Len*/
|
|
= 8;
|
|
n++;
|
|
bl_count[8]++;
|
|
}
|
|
/* Codes 286 and 287 do not exist, but we must include them in the
|
|
* tree construction to get a canonical Huffman tree (longest code
|
|
* all ones)
|
|
*/
|
|
|
|
|
|
gen_codes(static_ltree, L_CODES$1 + 1, bl_count);
|
|
/* The static distance tree is trivial: */
|
|
|
|
for (n = 0; n < D_CODES$1; n++) {
|
|
static_dtree[n * 2 + 1]
|
|
/*.Len*/
|
|
= 5;
|
|
static_dtree[n * 2]
|
|
/*.Code*/
|
|
= bi_reverse(n, 5);
|
|
} // Now data ready and we can init static trees
|
|
|
|
|
|
static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS$1 + 1, L_CODES$1, MAX_BITS$1);
|
|
static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0, D_CODES$1, MAX_BITS$1);
|
|
static_bl_desc = new StaticTreeDesc(new Array(0), extra_blbits, 0, BL_CODES$1, MAX_BL_BITS); //static_init_done = true;
|
|
};
|
|
/* ===========================================================================
|
|
* Initialize a new block.
|
|
*/
|
|
|
|
|
|
const init_block = s => {
|
|
let n;
|
|
/* iterates over tree elements */
|
|
|
|
/* Initialize the trees. */
|
|
|
|
for (n = 0; n < L_CODES$1; n++) {
|
|
s.dyn_ltree[n * 2]
|
|
/*.Freq*/
|
|
= 0;
|
|
}
|
|
|
|
for (n = 0; n < D_CODES$1; n++) {
|
|
s.dyn_dtree[n * 2]
|
|
/*.Freq*/
|
|
= 0;
|
|
}
|
|
|
|
for (n = 0; n < BL_CODES$1; n++) {
|
|
s.bl_tree[n * 2]
|
|
/*.Freq*/
|
|
= 0;
|
|
}
|
|
|
|
s.dyn_ltree[END_BLOCK * 2]
|
|
/*.Freq*/
|
|
= 1;
|
|
s.opt_len = s.static_len = 0;
|
|
s.last_lit = s.matches = 0;
|
|
};
|
|
/* ===========================================================================
|
|
* Flush the bit buffer and align the output on a byte boundary
|
|
*/
|
|
|
|
|
|
const bi_windup = s => {
|
|
if (s.bi_valid > 8) {
|
|
put_short(s, s.bi_buf);
|
|
} else if (s.bi_valid > 0) {
|
|
//put_byte(s, (Byte)s->bi_buf);
|
|
s.pending_buf[s.pending++] = s.bi_buf;
|
|
}
|
|
|
|
s.bi_buf = 0;
|
|
s.bi_valid = 0;
|
|
};
|
|
/* ===========================================================================
|
|
* Copy a stored block, storing first the length and its
|
|
* one's complement if requested.
|
|
*/
|
|
|
|
|
|
const copy_block = (s, buf, len, header) => //DeflateState *s;
|
|
//charf *buf; /* the input data */
|
|
//unsigned len; /* its length */
|
|
//int header; /* true if block header must be written */
|
|
{
|
|
bi_windup(s);
|
|
/* align on byte boundary */
|
|
|
|
if (header) {
|
|
put_short(s, len);
|
|
put_short(s, ~len);
|
|
} // while (len--) {
|
|
// put_byte(s, *buf++);
|
|
// }
|
|
|
|
|
|
s.pending_buf.set(s.window.subarray(buf, buf + len), s.pending);
|
|
s.pending += len;
|
|
};
|
|
/* ===========================================================================
|
|
* Compares to subtrees, using the tree depth as tie breaker when
|
|
* the subtrees have equal frequency. This minimizes the worst case length.
|
|
*/
|
|
|
|
|
|
const smaller = (tree, n, m, depth) => {
|
|
const _n2 = n * 2;
|
|
|
|
const _m2 = m * 2;
|
|
|
|
return tree[_n2]
|
|
/*.Freq*/
|
|
< tree[_m2]
|
|
/*.Freq*/
|
|
|| tree[_n2]
|
|
/*.Freq*/
|
|
=== tree[_m2]
|
|
/*.Freq*/
|
|
&& depth[n] <= depth[m];
|
|
};
|
|
/* ===========================================================================
|
|
* Restore the heap property by moving down the tree starting at node k,
|
|
* exchanging a node with the smallest of its two sons if necessary, stopping
|
|
* when the heap property is re-established (each father smaller than its
|
|
* two sons).
|
|
*/
|
|
|
|
|
|
const pqdownheap = (s, tree, k) => // deflate_state *s;
|
|
// ct_data *tree; /* the tree to restore */
|
|
// int k; /* node to move down */
|
|
{
|
|
const v = s.heap[k];
|
|
let j = k << 1;
|
|
/* left son of k */
|
|
|
|
while (j <= s.heap_len) {
|
|
/* Set j to the smallest of the two sons: */
|
|
if (j < s.heap_len && smaller(tree, s.heap[j + 1], s.heap[j], s.depth)) {
|
|
j++;
|
|
}
|
|
/* Exit if v is smaller than both sons */
|
|
|
|
|
|
if (smaller(tree, v, s.heap[j], s.depth)) {
|
|
break;
|
|
}
|
|
/* Exchange v with the smallest son */
|
|
|
|
|
|
s.heap[k] = s.heap[j];
|
|
k = j;
|
|
/* And continue down the tree, setting j to the left son of k */
|
|
|
|
j <<= 1;
|
|
}
|
|
|
|
s.heap[k] = v;
|
|
}; // inlined manually
|
|
// const SMALLEST = 1;
|
|
|
|
/* ===========================================================================
|
|
* Send the block data compressed using the given Huffman trees
|
|
*/
|
|
|
|
|
|
const compress_block = (s, ltree, dtree) => // deflate_state *s;
|
|
// const ct_data *ltree; /* literal tree */
|
|
// const ct_data *dtree; /* distance tree */
|
|
{
|
|
let dist;
|
|
/* distance of matched string */
|
|
|
|
let lc;
|
|
/* match length or unmatched char (if dist == 0) */
|
|
|
|
let lx = 0;
|
|
/* running index in l_buf */
|
|
|
|
let code;
|
|
/* the code to send */
|
|
|
|
let extra;
|
|
/* number of extra bits to send */
|
|
|
|
if (s.last_lit !== 0) {
|
|
do {
|
|
dist = s.pending_buf[s.d_buf + lx * 2] << 8 | s.pending_buf[s.d_buf + lx * 2 + 1];
|
|
lc = s.pending_buf[s.l_buf + lx];
|
|
lx++;
|
|
|
|
if (dist === 0) {
|
|
send_code(s, lc, ltree);
|
|
/* send a literal byte */
|
|
//Tracecv(isgraph(lc), (stderr," '%c' ", lc));
|
|
} else {
|
|
/* Here, lc is the match length - MIN_MATCH */
|
|
code = _length_code[lc];
|
|
send_code(s, code + LITERALS$1 + 1, ltree);
|
|
/* send the length code */
|
|
|
|
extra = extra_lbits[code];
|
|
|
|
if (extra !== 0) {
|
|
lc -= base_length[code];
|
|
send_bits(s, lc, extra);
|
|
/* send the extra length bits */
|
|
}
|
|
|
|
dist--;
|
|
/* dist is now the match distance - 1 */
|
|
|
|
code = d_code(dist); //Assert (code < D_CODES, "bad d_code");
|
|
|
|
send_code(s, code, dtree);
|
|
/* send the distance code */
|
|
|
|
extra = extra_dbits[code];
|
|
|
|
if (extra !== 0) {
|
|
dist -= base_dist[code];
|
|
send_bits(s, dist, extra);
|
|
/* send the extra distance bits */
|
|
}
|
|
}
|
|
/* literal or match pair ? */
|
|
|
|
/* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
|
|
//Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx,
|
|
// "pendingBuf overflow");
|
|
|
|
} while (lx < s.last_lit);
|
|
}
|
|
|
|
send_code(s, END_BLOCK, ltree);
|
|
};
|
|
/* ===========================================================================
|
|
* Construct one Huffman tree and assigns the code bit strings and lengths.
|
|
* Update the total bit length for the current block.
|
|
* IN assertion: the field freq is set for all tree elements.
|
|
* OUT assertions: the fields len and code are set to the optimal bit length
|
|
* and corresponding code. The length opt_len is updated; static_len is
|
|
* also updated if stree is not null. The field max_code is set.
|
|
*/
|
|
|
|
|
|
const build_tree = (s, desc) => // deflate_state *s;
|
|
// tree_desc *desc; /* the tree descriptor */
|
|
{
|
|
const tree = desc.dyn_tree;
|
|
const stree = desc.stat_desc.static_tree;
|
|
const has_stree = desc.stat_desc.has_stree;
|
|
const elems = desc.stat_desc.elems;
|
|
let n, m;
|
|
/* iterate over heap elements */
|
|
|
|
let max_code = -1;
|
|
/* largest code with non zero frequency */
|
|
|
|
let node;
|
|
/* new node being created */
|
|
|
|
/* Construct the initial heap, with least frequent element in
|
|
* heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
|
|
* heap[0] is not used.
|
|
*/
|
|
|
|
s.heap_len = 0;
|
|
s.heap_max = HEAP_SIZE$1;
|
|
|
|
for (n = 0; n < elems; n++) {
|
|
if (tree[n * 2]
|
|
/*.Freq*/
|
|
!== 0) {
|
|
s.heap[++s.heap_len] = max_code = n;
|
|
s.depth[n] = 0;
|
|
} else {
|
|
tree[n * 2 + 1]
|
|
/*.Len*/
|
|
= 0;
|
|
}
|
|
}
|
|
/* The pkzip format requires that at least one distance code exists,
|
|
* and that at least one bit should be sent even if there is only one
|
|
* possible code. So to avoid special checks later on we force at least
|
|
* two codes of non zero frequency.
|
|
*/
|
|
|
|
|
|
while (s.heap_len < 2) {
|
|
node = s.heap[++s.heap_len] = max_code < 2 ? ++max_code : 0;
|
|
tree[node * 2]
|
|
/*.Freq*/
|
|
= 1;
|
|
s.depth[node] = 0;
|
|
s.opt_len--;
|
|
|
|
if (has_stree) {
|
|
s.static_len -= stree[node * 2 + 1]
|
|
/*.Len*/
|
|
;
|
|
}
|
|
/* node is 0 or 1 so it does not have extra bits */
|
|
|
|
}
|
|
|
|
desc.max_code = max_code;
|
|
/* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
|
|
* establish sub-heaps of increasing lengths:
|
|
*/
|
|
|
|
for (n = s.heap_len >> 1
|
|
/*int /2*/
|
|
; n >= 1; n--) {
|
|
pqdownheap(s, tree, n);
|
|
}
|
|
/* Construct the Huffman tree by repeatedly combining the least two
|
|
* frequent nodes.
|
|
*/
|
|
|
|
|
|
node = elems;
|
|
/* next internal node of the tree */
|
|
|
|
do {
|
|
//pqremove(s, tree, n); /* n = node of least frequency */
|
|
|
|
/*** pqremove ***/
|
|
n = s.heap[1
|
|
/*SMALLEST*/
|
|
];
|
|
s.heap[1
|
|
/*SMALLEST*/
|
|
] = s.heap[s.heap_len--];
|
|
pqdownheap(s, tree, 1
|
|
/*SMALLEST*/
|
|
);
|
|
/***/
|
|
|
|
m = s.heap[1
|
|
/*SMALLEST*/
|
|
];
|
|
/* m = node of next least frequency */
|
|
|
|
s.heap[--s.heap_max] = n;
|
|
/* keep the nodes sorted by frequency */
|
|
|
|
s.heap[--s.heap_max] = m;
|
|
/* Create a new node father of n and m */
|
|
|
|
tree[node * 2]
|
|
/*.Freq*/
|
|
= tree[n * 2]
|
|
/*.Freq*/
|
|
+ tree[m * 2]
|
|
/*.Freq*/
|
|
;
|
|
s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1;
|
|
tree[n * 2 + 1]
|
|
/*.Dad*/
|
|
= tree[m * 2 + 1]
|
|
/*.Dad*/
|
|
= node;
|
|
/* and insert the new node in the heap */
|
|
|
|
s.heap[1
|
|
/*SMALLEST*/
|
|
] = node++;
|
|
pqdownheap(s, tree, 1
|
|
/*SMALLEST*/
|
|
);
|
|
} while (s.heap_len >= 2);
|
|
|
|
s.heap[--s.heap_max] = s.heap[1
|
|
/*SMALLEST*/
|
|
];
|
|
/* At this point, the fields freq and dad are set. We can now
|
|
* generate the bit lengths.
|
|
*/
|
|
|
|
gen_bitlen(s, desc);
|
|
/* The field len is now set, we can generate the bit codes */
|
|
|
|
gen_codes(tree, max_code, s.bl_count);
|
|
};
|
|
/* ===========================================================================
|
|
* Scan a literal or distance tree to determine the frequencies of the codes
|
|
* in the bit length tree.
|
|
*/
|
|
|
|
|
|
const scan_tree = (s, tree, max_code) => // deflate_state *s;
|
|
// ct_data *tree; /* the tree to be scanned */
|
|
// int max_code; /* and its largest code of non zero frequency */
|
|
{
|
|
let n;
|
|
/* iterates over all tree elements */
|
|
|
|
let prevlen = -1;
|
|
/* last emitted length */
|
|
|
|
let curlen;
|
|
/* length of current code */
|
|
|
|
let nextlen = tree[0 * 2 + 1]
|
|
/*.Len*/
|
|
;
|
|
/* length of next code */
|
|
|
|
let count = 0;
|
|
/* repeat count of the current code */
|
|
|
|
let max_count = 7;
|
|
/* max repeat count */
|
|
|
|
let min_count = 4;
|
|
/* min repeat count */
|
|
|
|
if (nextlen === 0) {
|
|
max_count = 138;
|
|
min_count = 3;
|
|
}
|
|
|
|
tree[(max_code + 1) * 2 + 1]
|
|
/*.Len*/
|
|
= 0xffff;
|
|
/* guard */
|
|
|
|
for (n = 0; n <= max_code; n++) {
|
|
curlen = nextlen;
|
|
nextlen = tree[(n + 1) * 2 + 1]
|
|
/*.Len*/
|
|
;
|
|
|
|
if (++count < max_count && curlen === nextlen) {
|
|
continue;
|
|
} else if (count < min_count) {
|
|
s.bl_tree[curlen * 2]
|
|
/*.Freq*/
|
|
+= count;
|
|
} else if (curlen !== 0) {
|
|
if (curlen !== prevlen) {
|
|
s.bl_tree[curlen * 2] /*.Freq*/++;
|
|
}
|
|
|
|
s.bl_tree[REP_3_6 * 2] /*.Freq*/++;
|
|
} else if (count <= 10) {
|
|
s.bl_tree[REPZ_3_10 * 2] /*.Freq*/++;
|
|
} else {
|
|
s.bl_tree[REPZ_11_138 * 2] /*.Freq*/++;
|
|
}
|
|
|
|
count = 0;
|
|
prevlen = curlen;
|
|
|
|
if (nextlen === 0) {
|
|
max_count = 138;
|
|
min_count = 3;
|
|
} else if (curlen === nextlen) {
|
|
max_count = 6;
|
|
min_count = 3;
|
|
} else {
|
|
max_count = 7;
|
|
min_count = 4;
|
|
}
|
|
}
|
|
};
|
|
/* ===========================================================================
|
|
* Send a literal or distance tree in compressed form, using the codes in
|
|
* bl_tree.
|
|
*/
|
|
|
|
|
|
const send_tree = (s, tree, max_code) => // deflate_state *s;
|
|
// ct_data *tree; /* the tree to be scanned */
|
|
// int max_code; /* and its largest code of non zero frequency */
|
|
{
|
|
let n;
|
|
/* iterates over all tree elements */
|
|
|
|
let prevlen = -1;
|
|
/* last emitted length */
|
|
|
|
let curlen;
|
|
/* length of current code */
|
|
|
|
let nextlen = tree[0 * 2 + 1]
|
|
/*.Len*/
|
|
;
|
|
/* length of next code */
|
|
|
|
let count = 0;
|
|
/* repeat count of the current code */
|
|
|
|
let max_count = 7;
|
|
/* max repeat count */
|
|
|
|
let min_count = 4;
|
|
/* min repeat count */
|
|
|
|
/* tree[max_code+1].Len = -1; */
|
|
|
|
/* guard already set */
|
|
|
|
if (nextlen === 0) {
|
|
max_count = 138;
|
|
min_count = 3;
|
|
}
|
|
|
|
for (n = 0; n <= max_code; n++) {
|
|
curlen = nextlen;
|
|
nextlen = tree[(n + 1) * 2 + 1]
|
|
/*.Len*/
|
|
;
|
|
|
|
if (++count < max_count && curlen === nextlen) {
|
|
continue;
|
|
} else if (count < min_count) {
|
|
do {
|
|
send_code(s, curlen, s.bl_tree);
|
|
} while (--count !== 0);
|
|
} else if (curlen !== 0) {
|
|
if (curlen !== prevlen) {
|
|
send_code(s, curlen, s.bl_tree);
|
|
count--;
|
|
} //Assert(count >= 3 && count <= 6, " 3_6?");
|
|
|
|
|
|
send_code(s, REP_3_6, s.bl_tree);
|
|
send_bits(s, count - 3, 2);
|
|
} else if (count <= 10) {
|
|
send_code(s, REPZ_3_10, s.bl_tree);
|
|
send_bits(s, count - 3, 3);
|
|
} else {
|
|
send_code(s, REPZ_11_138, s.bl_tree);
|
|
send_bits(s, count - 11, 7);
|
|
}
|
|
|
|
count = 0;
|
|
prevlen = curlen;
|
|
|
|
if (nextlen === 0) {
|
|
max_count = 138;
|
|
min_count = 3;
|
|
} else if (curlen === nextlen) {
|
|
max_count = 6;
|
|
min_count = 3;
|
|
} else {
|
|
max_count = 7;
|
|
min_count = 4;
|
|
}
|
|
}
|
|
};
|
|
/* ===========================================================================
|
|
* Construct the Huffman tree for the bit lengths and return the index in
|
|
* bl_order of the last bit length code to send.
|
|
*/
|
|
|
|
|
|
const build_bl_tree = s => {
|
|
let max_blindex;
|
|
/* index of last bit length code of non zero freq */
|
|
|
|
/* Determine the bit length frequencies for literal and distance trees */
|
|
|
|
scan_tree(s, s.dyn_ltree, s.l_desc.max_code);
|
|
scan_tree(s, s.dyn_dtree, s.d_desc.max_code);
|
|
/* Build the bit length tree: */
|
|
|
|
build_tree(s, s.bl_desc);
|
|
/* opt_len now includes the length of the tree representations, except
|
|
* the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
|
|
*/
|
|
|
|
/* Determine the number of bit length codes to send. The pkzip format
|
|
* requires that at least 4 bit length codes be sent. (appnote.txt says
|
|
* 3 but the actual value used is 4.)
|
|
*/
|
|
|
|
for (max_blindex = BL_CODES$1 - 1; max_blindex >= 3; max_blindex--) {
|
|
if (s.bl_tree[bl_order[max_blindex] * 2 + 1]
|
|
/*.Len*/
|
|
!== 0) {
|
|
break;
|
|
}
|
|
}
|
|
/* Update opt_len to include the bit length tree and counts */
|
|
|
|
|
|
s.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; //Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
|
|
// s->opt_len, s->static_len));
|
|
|
|
return max_blindex;
|
|
};
|
|
/* ===========================================================================
|
|
* Send the header for a block using dynamic Huffman trees: the counts, the
|
|
* lengths of the bit length codes, the literal tree and the distance tree.
|
|
* IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
|
|
*/
|
|
|
|
|
|
const send_all_trees = (s, lcodes, dcodes, blcodes) => // deflate_state *s;
|
|
// int lcodes, dcodes, blcodes; /* number of codes for each tree */
|
|
{
|
|
let rank;
|
|
/* index in bl_order */
|
|
//Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
|
|
//Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
|
|
// "too many codes");
|
|
//Tracev((stderr, "\nbl counts: "));
|
|
|
|
send_bits(s, lcodes - 257, 5);
|
|
/* not +255 as stated in appnote.txt */
|
|
|
|
send_bits(s, dcodes - 1, 5);
|
|
send_bits(s, blcodes - 4, 4);
|
|
/* not -3 as stated in appnote.txt */
|
|
|
|
for (rank = 0; rank < blcodes; rank++) {
|
|
//Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
|
|
send_bits(s, s.bl_tree[bl_order[rank] * 2 + 1]
|
|
/*.Len*/
|
|
, 3);
|
|
} //Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
|
|
|
|
|
|
send_tree(s, s.dyn_ltree, lcodes - 1);
|
|
/* literal tree */
|
|
//Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
|
|
|
|
send_tree(s, s.dyn_dtree, dcodes - 1);
|
|
/* distance tree */
|
|
//Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
|
|
};
|
|
/* ===========================================================================
|
|
* Check if the data type is TEXT or BINARY, using the following algorithm:
|
|
* - TEXT if the two conditions below are satisfied:
|
|
* a) There are no non-portable control characters belonging to the
|
|
* "black list" (0..6, 14..25, 28..31).
|
|
* b) There is at least one printable character belonging to the
|
|
* "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).
|
|
* - BINARY otherwise.
|
|
* - The following partially-portable control characters form a
|
|
* "gray list" that is ignored in this detection algorithm:
|
|
* (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).
|
|
* IN assertion: the fields Freq of dyn_ltree are set.
|
|
*/
|
|
|
|
|
|
const detect_data_type = s => {
|
|
/* black_mask is the bit mask of black-listed bytes
|
|
* set bits 0..6, 14..25, and 28..31
|
|
* 0xf3ffc07f = binary 11110011111111111100000001111111
|
|
*/
|
|
let black_mask = 0xf3ffc07f;
|
|
let n;
|
|
/* Check for non-textual ("black-listed") bytes. */
|
|
|
|
for (n = 0; n <= 31; n++, black_mask >>>= 1) {
|
|
if (black_mask & 1 && s.dyn_ltree[n * 2]
|
|
/*.Freq*/
|
|
!== 0) {
|
|
return Z_BINARY;
|
|
}
|
|
}
|
|
/* Check for textual ("white-listed") bytes. */
|
|
|
|
|
|
if (s.dyn_ltree[9 * 2]
|
|
/*.Freq*/
|
|
!== 0 || s.dyn_ltree[10 * 2]
|
|
/*.Freq*/
|
|
!== 0 || s.dyn_ltree[13 * 2]
|
|
/*.Freq*/
|
|
!== 0) {
|
|
return Z_TEXT;
|
|
}
|
|
|
|
for (n = 32; n < LITERALS$1; n++) {
|
|
if (s.dyn_ltree[n * 2]
|
|
/*.Freq*/
|
|
!== 0) {
|
|
return Z_TEXT;
|
|
}
|
|
}
|
|
/* There are no "black-listed" or "white-listed" bytes:
|
|
* this stream either is empty or has tolerated ("gray-listed") bytes only.
|
|
*/
|
|
|
|
|
|
return Z_BINARY;
|
|
};
|
|
|
|
let static_init_done = false;
|
|
/* ===========================================================================
|
|
* Initialize the tree data structures for a new zlib stream.
|
|
*/
|
|
|
|
const _tr_init$1 = s => {
|
|
if (!static_init_done) {
|
|
tr_static_init();
|
|
static_init_done = true;
|
|
}
|
|
|
|
s.l_desc = new TreeDesc(s.dyn_ltree, static_l_desc);
|
|
s.d_desc = new TreeDesc(s.dyn_dtree, static_d_desc);
|
|
s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc);
|
|
s.bi_buf = 0;
|
|
s.bi_valid = 0;
|
|
/* Initialize the first block of the first file: */
|
|
|
|
init_block(s);
|
|
};
|
|
/* ===========================================================================
|
|
* Send a stored block
|
|
*/
|
|
|
|
|
|
const _tr_stored_block$1 = (s, buf, stored_len, last) => //DeflateState *s;
|
|
//charf *buf; /* input block */
|
|
//ulg stored_len; /* length of input block */
|
|
//int last; /* one if this is the last block for a file */
|
|
{
|
|
send_bits(s, (STORED_BLOCK << 1) + (last ? 1 : 0), 3);
|
|
/* send block type */
|
|
|
|
copy_block(s, buf, stored_len, true);
|
|
/* with header */
|
|
};
|
|
/* ===========================================================================
|
|
* Send one empty static block to give enough lookahead for inflate.
|
|
* This takes 10 bits, of which 7 may remain in the bit buffer.
|
|
*/
|
|
|
|
|
|
const _tr_align$1 = s => {
|
|
send_bits(s, STATIC_TREES << 1, 3);
|
|
send_code(s, END_BLOCK, static_ltree);
|
|
bi_flush(s);
|
|
};
|
|
/* ===========================================================================
|
|
* Determine the best encoding for the current block: dynamic trees, static
|
|
* trees or store, and output the encoded block to the zip file.
|
|
*/
|
|
|
|
|
|
const _tr_flush_block$1 = (s, buf, stored_len, last) => //DeflateState *s;
|
|
//charf *buf; /* input block, or NULL if too old */
|
|
//ulg stored_len; /* length of input block */
|
|
//int last; /* one if this is the last block for a file */
|
|
{
|
|
let opt_lenb, static_lenb;
|
|
/* opt_len and static_len in bytes */
|
|
|
|
let max_blindex = 0;
|
|
/* index of last bit length code of non zero freq */
|
|
|
|
/* Build the Huffman trees unless a stored block is forced */
|
|
|
|
if (s.level > 0) {
|
|
/* Check if the file is binary or text */
|
|
if (s.strm.data_type === Z_UNKNOWN$1) {
|
|
s.strm.data_type = detect_data_type(s);
|
|
}
|
|
/* Construct the literal and distance trees */
|
|
|
|
|
|
build_tree(s, s.l_desc); // Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
|
|
// s->static_len));
|
|
|
|
build_tree(s, s.d_desc); // Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
|
|
// s->static_len));
|
|
|
|
/* At this point, opt_len and static_len are the total bit lengths of
|
|
* the compressed block data, excluding the tree representations.
|
|
*/
|
|
|
|
/* Build the bit length tree for the above two trees, and get the index
|
|
* in bl_order of the last bit length code to send.
|
|
*/
|
|
|
|
max_blindex = build_bl_tree(s);
|
|
/* Determine the best encoding. Compute the block lengths in bytes. */
|
|
|
|
opt_lenb = s.opt_len + 3 + 7 >>> 3;
|
|
static_lenb = s.static_len + 3 + 7 >>> 3; // Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
|
|
// opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
|
|
// s->last_lit));
|
|
|
|
if (static_lenb <= opt_lenb) {
|
|
opt_lenb = static_lenb;
|
|
}
|
|
} else {
|
|
// Assert(buf != (char*)0, "lost buf");
|
|
opt_lenb = static_lenb = stored_len + 5;
|
|
/* force a stored block */
|
|
}
|
|
|
|
if (stored_len + 4 <= opt_lenb && buf !== -1) {
|
|
/* 4: two words for the lengths */
|
|
|
|
/* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
|
|
* Otherwise we can't have processed more than WSIZE input bytes since
|
|
* the last block flush, because compression would have been
|
|
* successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
|
|
* transform a block into a stored block.
|
|
*/
|
|
_tr_stored_block$1(s, buf, stored_len, last);
|
|
} else if (s.strategy === Z_FIXED$1 || static_lenb === opt_lenb) {
|
|
send_bits(s, (STATIC_TREES << 1) + (last ? 1 : 0), 3);
|
|
compress_block(s, static_ltree, static_dtree);
|
|
} else {
|
|
send_bits(s, (DYN_TREES << 1) + (last ? 1 : 0), 3);
|
|
send_all_trees(s, s.l_desc.max_code + 1, s.d_desc.max_code + 1, max_blindex + 1);
|
|
compress_block(s, s.dyn_ltree, s.dyn_dtree);
|
|
} // Assert (s->compressed_len == s->bits_sent, "bad compressed size");
|
|
|
|
/* The above check is made mod 2^32, for files larger than 512 MB
|
|
* and uLong implemented on 32 bits.
|
|
*/
|
|
|
|
|
|
init_block(s);
|
|
|
|
if (last) {
|
|
bi_windup(s);
|
|
} // Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
|
|
// s->compressed_len-7*last));
|
|
|
|
};
|
|
/* ===========================================================================
|
|
* Save the match info and tally the frequency counts. Return true if
|
|
* the current block must be flushed.
|
|
*/
|
|
|
|
|
|
const _tr_tally$1 = (s, dist, lc) => // deflate_state *s;
|
|
// unsigned dist; /* distance of matched string */
|
|
// unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */
|
|
{
|
|
//let out_length, in_length, dcode;
|
|
s.pending_buf[s.d_buf + s.last_lit * 2] = dist >>> 8 & 0xff;
|
|
s.pending_buf[s.d_buf + s.last_lit * 2 + 1] = dist & 0xff;
|
|
s.pending_buf[s.l_buf + s.last_lit] = lc & 0xff;
|
|
s.last_lit++;
|
|
|
|
if (dist === 0) {
|
|
/* lc is the unmatched char */
|
|
s.dyn_ltree[lc * 2] /*.Freq*/++;
|
|
} else {
|
|
s.matches++;
|
|
/* Here, lc is the match length - MIN_MATCH */
|
|
|
|
dist--;
|
|
/* dist = match distance - 1 */
|
|
//Assert((ush)dist < (ush)MAX_DIST(s) &&
|
|
// (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
|
|
// (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match");
|
|
|
|
s.dyn_ltree[(_length_code[lc] + LITERALS$1 + 1) * 2] /*.Freq*/++;
|
|
s.dyn_dtree[d_code(dist) * 2] /*.Freq*/++;
|
|
} // (!) This block is disabled in zlib defaults,
|
|
// don't enable it for binary compatibility
|
|
//#ifdef TRUNCATE_BLOCK
|
|
// /* Try to guess if it is profitable to stop the current block here */
|
|
// if ((s.last_lit & 0x1fff) === 0 && s.level > 2) {
|
|
// /* Compute an upper bound for the compressed length */
|
|
// out_length = s.last_lit*8;
|
|
// in_length = s.strstart - s.block_start;
|
|
//
|
|
// for (dcode = 0; dcode < D_CODES; dcode++) {
|
|
// out_length += s.dyn_dtree[dcode*2]/*.Freq*/ * (5 + extra_dbits[dcode]);
|
|
// }
|
|
// out_length >>>= 3;
|
|
// //Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
|
|
// // s->last_lit, in_length, out_length,
|
|
// // 100L - out_length*100L/in_length));
|
|
// if (s.matches < (s.last_lit>>1)/*int /2*/ && out_length < (in_length>>1)/*int /2*/) {
|
|
// return true;
|
|
// }
|
|
// }
|
|
//#endif
|
|
|
|
|
|
return s.last_lit === s.lit_bufsize - 1;
|
|
/* We avoid equality with lit_bufsize because of wraparound at 64K
|
|
* on 16 bit machines and because stored blocks are restricted to
|
|
* 64K-1 bytes.
|
|
*/
|
|
};
|
|
|
|
var _tr_init_1 = _tr_init$1;
|
|
var _tr_stored_block_1 = _tr_stored_block$1;
|
|
var _tr_flush_block_1 = _tr_flush_block$1;
|
|
var _tr_tally_1 = _tr_tally$1;
|
|
var _tr_align_1 = _tr_align$1;
|
|
var trees = {
|
|
_tr_init: _tr_init_1,
|
|
_tr_stored_block: _tr_stored_block_1,
|
|
_tr_flush_block: _tr_flush_block_1,
|
|
_tr_tally: _tr_tally_1,
|
|
_tr_align: _tr_align_1
|
|
}; // Note: adler32 takes 12% for level 0 and 2% for level 6.
|
|
// It isn't worth it to make additional optimizations as in original.
|
|
// Small size is preferable.
|
|
// (C) 1995-2013 Jean-loup Gailly and Mark Adler
|
|
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
|
|
//
|
|
// This software is provided 'as-is', without any express or implied
|
|
// warranty. In no event will the authors be held liable for any damages
|
|
// arising from the use of this software.
|
|
//
|
|
// Permission is granted to anyone to use this software for any purpose,
|
|
// including commercial applications, and to alter it and redistribute it
|
|
// freely, subject to the following restrictions:
|
|
//
|
|
// 1. The origin of this software must not be misrepresented; you must not
|
|
// claim that you wrote the original software. If you use this software
|
|
// in a product, an acknowledgment in the product documentation would be
|
|
// appreciated but is not required.
|
|
// 2. Altered source versions must be plainly marked as such, and must not be
|
|
// misrepresented as being the original software.
|
|
// 3. This notice may not be removed or altered from any source distribution.
|
|
|
|
const adler32 = (adler, buf, len, pos) => {
|
|
let s1 = adler & 0xffff | 0,
|
|
s2 = adler >>> 16 & 0xffff | 0,
|
|
n = 0;
|
|
|
|
while (len !== 0) {
|
|
// Set limit ~ twice less than 5552, to keep
|
|
// s2 in 31-bits, because we force signed ints.
|
|
// in other case %= will fail.
|
|
n = len > 2000 ? 2000 : len;
|
|
len -= n;
|
|
|
|
do {
|
|
s1 = s1 + buf[pos++] | 0;
|
|
s2 = s2 + s1 | 0;
|
|
} while (--n);
|
|
|
|
s1 %= 65521;
|
|
s2 %= 65521;
|
|
}
|
|
|
|
return s1 | s2 << 16 | 0;
|
|
};
|
|
|
|
var adler32_1 = adler32; // Note: we can't get significant speed boost here.
|
|
// So write code to minimize size - no pregenerated tables
|
|
// and array tools dependencies.
|
|
// (C) 1995-2013 Jean-loup Gailly and Mark Adler
|
|
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
|
|
//
|
|
// This software is provided 'as-is', without any express or implied
|
|
// warranty. In no event will the authors be held liable for any damages
|
|
// arising from the use of this software.
|
|
//
|
|
// Permission is granted to anyone to use this software for any purpose,
|
|
// including commercial applications, and to alter it and redistribute it
|
|
// freely, subject to the following restrictions:
|
|
//
|
|
// 1. The origin of this software must not be misrepresented; you must not
|
|
// claim that you wrote the original software. If you use this software
|
|
// in a product, an acknowledgment in the product documentation would be
|
|
// appreciated but is not required.
|
|
// 2. Altered source versions must be plainly marked as such, and must not be
|
|
// misrepresented as being the original software.
|
|
// 3. This notice may not be removed or altered from any source distribution.
|
|
// Use ordinary array, since untyped makes no boost here
|
|
|
|
const makeTable = () => {
|
|
let c,
|
|
table = [];
|
|
|
|
for (var n = 0; n < 256; n++) {
|
|
c = n;
|
|
|
|
for (var k = 0; k < 8; k++) {
|
|
c = c & 1 ? 0xEDB88320 ^ c >>> 1 : c >>> 1;
|
|
}
|
|
|
|
table[n] = c;
|
|
}
|
|
|
|
return table;
|
|
}; // Create table on load. Just 255 signed longs. Not a problem.
|
|
|
|
|
|
const crcTable = new Uint32Array(makeTable());
|
|
|
|
const crc32 = (crc, buf, len, pos) => {
|
|
const t = crcTable;
|
|
const end = pos + len;
|
|
crc ^= -1;
|
|
|
|
for (let i = pos; i < end; i++) {
|
|
crc = crc >>> 8 ^ t[(crc ^ buf[i]) & 0xFF];
|
|
}
|
|
|
|
return crc ^ -1; // >>> 0;
|
|
};
|
|
|
|
var crc32_1 = crc32; // (C) 1995-2013 Jean-loup Gailly and Mark Adler
|
|
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
|
|
//
|
|
// This software is provided 'as-is', without any express or implied
|
|
// warranty. In no event will the authors be held liable for any damages
|
|
// arising from the use of this software.
|
|
//
|
|
// Permission is granted to anyone to use this software for any purpose,
|
|
// including commercial applications, and to alter it and redistribute it
|
|
// freely, subject to the following restrictions:
|
|
//
|
|
// 1. The origin of this software must not be misrepresented; you must not
|
|
// claim that you wrote the original software. If you use this software
|
|
// in a product, an acknowledgment in the product documentation would be
|
|
// appreciated but is not required.
|
|
// 2. Altered source versions must be plainly marked as such, and must not be
|
|
// misrepresented as being the original software.
|
|
// 3. This notice may not be removed or altered from any source distribution.
|
|
|
|
var messages = {
|
|
2: 'need dictionary',
|
|
|
|
/* Z_NEED_DICT 2 */
|
|
1: 'stream end',
|
|
|
|
/* Z_STREAM_END 1 */
|
|
0: '',
|
|
|
|
/* Z_OK 0 */
|
|
'-1': 'file error',
|
|
|
|
/* Z_ERRNO (-1) */
|
|
'-2': 'stream error',
|
|
|
|
/* Z_STREAM_ERROR (-2) */
|
|
'-3': 'data error',
|
|
|
|
/* Z_DATA_ERROR (-3) */
|
|
'-4': 'insufficient memory',
|
|
|
|
/* Z_MEM_ERROR (-4) */
|
|
'-5': 'buffer error',
|
|
|
|
/* Z_BUF_ERROR (-5) */
|
|
'-6': 'incompatible version'
|
|
/* Z_VERSION_ERROR (-6) */
|
|
|
|
}; // (C) 1995-2013 Jean-loup Gailly and Mark Adler
|
|
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
|
|
//
|
|
// This software is provided 'as-is', without any express or implied
|
|
// warranty. In no event will the authors be held liable for any damages
|
|
// arising from the use of this software.
|
|
//
|
|
// Permission is granted to anyone to use this software for any purpose,
|
|
// including commercial applications, and to alter it and redistribute it
|
|
// freely, subject to the following restrictions:
|
|
//
|
|
// 1. The origin of this software must not be misrepresented; you must not
|
|
// claim that you wrote the original software. If you use this software
|
|
// in a product, an acknowledgment in the product documentation would be
|
|
// appreciated but is not required.
|
|
// 2. Altered source versions must be plainly marked as such, and must not be
|
|
// misrepresented as being the original software.
|
|
// 3. This notice may not be removed or altered from any source distribution.
|
|
|
|
var constants$2 = {
|
|
/* Allowed flush values; see deflate() and inflate() below for details */
|
|
Z_NO_FLUSH: 0,
|
|
Z_PARTIAL_FLUSH: 1,
|
|
Z_SYNC_FLUSH: 2,
|
|
Z_FULL_FLUSH: 3,
|
|
Z_FINISH: 4,
|
|
Z_BLOCK: 5,
|
|
Z_TREES: 6,
|
|
|
|
/* Return codes for the compression/decompression functions. Negative values
|
|
* are errors, positive values are used for special but normal events.
|
|
*/
|
|
Z_OK: 0,
|
|
Z_STREAM_END: 1,
|
|
Z_NEED_DICT: 2,
|
|
Z_ERRNO: -1,
|
|
Z_STREAM_ERROR: -2,
|
|
Z_DATA_ERROR: -3,
|
|
Z_MEM_ERROR: -4,
|
|
Z_BUF_ERROR: -5,
|
|
//Z_VERSION_ERROR: -6,
|
|
|
|
/* compression levels */
|
|
Z_NO_COMPRESSION: 0,
|
|
Z_BEST_SPEED: 1,
|
|
Z_BEST_COMPRESSION: 9,
|
|
Z_DEFAULT_COMPRESSION: -1,
|
|
Z_FILTERED: 1,
|
|
Z_HUFFMAN_ONLY: 2,
|
|
Z_RLE: 3,
|
|
Z_FIXED: 4,
|
|
Z_DEFAULT_STRATEGY: 0,
|
|
|
|
/* Possible values of the data_type field (though see inflate()) */
|
|
Z_BINARY: 0,
|
|
Z_TEXT: 1,
|
|
//Z_ASCII: 1, // = Z_TEXT (deprecated)
|
|
Z_UNKNOWN: 2,
|
|
|
|
/* The deflate compression method */
|
|
Z_DEFLATED: 8 //Z_NULL: null // Use -1 or null inline, depending on var type
|
|
|
|
}; // (C) 1995-2013 Jean-loup Gailly and Mark Adler
|
|
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
|
|
//
|
|
// This software is provided 'as-is', without any express or implied
|
|
// warranty. In no event will the authors be held liable for any damages
|
|
// arising from the use of this software.
|
|
//
|
|
// Permission is granted to anyone to use this software for any purpose,
|
|
// including commercial applications, and to alter it and redistribute it
|
|
// freely, subject to the following restrictions:
|
|
//
|
|
// 1. The origin of this software must not be misrepresented; you must not
|
|
// claim that you wrote the original software. If you use this software
|
|
// in a product, an acknowledgment in the product documentation would be
|
|
// appreciated but is not required.
|
|
// 2. Altered source versions must be plainly marked as such, and must not be
|
|
// misrepresented as being the original software.
|
|
// 3. This notice may not be removed or altered from any source distribution.
|
|
|
|
const {
|
|
_tr_init,
|
|
_tr_stored_block,
|
|
_tr_flush_block,
|
|
_tr_tally,
|
|
_tr_align
|
|
} = trees;
|
|
/* Public constants ==========================================================*/
|
|
|
|
/* ===========================================================================*/
|
|
|
|
const {
|
|
Z_NO_FLUSH: Z_NO_FLUSH$2,
|
|
Z_PARTIAL_FLUSH,
|
|
Z_FULL_FLUSH: Z_FULL_FLUSH$1,
|
|
Z_FINISH: Z_FINISH$3,
|
|
Z_BLOCK: Z_BLOCK$1,
|
|
Z_OK: Z_OK$3,
|
|
Z_STREAM_END: Z_STREAM_END$3,
|
|
Z_STREAM_ERROR: Z_STREAM_ERROR$2,
|
|
Z_DATA_ERROR: Z_DATA_ERROR$2,
|
|
Z_BUF_ERROR: Z_BUF_ERROR$1,
|
|
Z_DEFAULT_COMPRESSION: Z_DEFAULT_COMPRESSION$1,
|
|
Z_FILTERED,
|
|
Z_HUFFMAN_ONLY,
|
|
Z_RLE,
|
|
Z_FIXED,
|
|
Z_DEFAULT_STRATEGY: Z_DEFAULT_STRATEGY$1,
|
|
Z_UNKNOWN,
|
|
Z_DEFLATED: Z_DEFLATED$2
|
|
} = constants$2;
|
|
/*============================================================================*/
|
|
|
|
const MAX_MEM_LEVEL = 9;
|
|
/* Maximum value for memLevel in deflateInit2 */
|
|
|
|
const MAX_WBITS$1 = 15;
|
|
/* 32K LZ77 window */
|
|
|
|
const DEF_MEM_LEVEL = 8;
|
|
const LENGTH_CODES = 29;
|
|
/* number of length codes, not counting the special END_BLOCK code */
|
|
|
|
const LITERALS = 256;
|
|
/* number of literal bytes 0..255 */
|
|
|
|
const L_CODES = LITERALS + 1 + LENGTH_CODES;
|
|
/* number of Literal or Length codes, including the END_BLOCK code */
|
|
|
|
const D_CODES = 30;
|
|
/* number of distance codes */
|
|
|
|
const BL_CODES = 19;
|
|
/* number of codes used to transfer the bit lengths */
|
|
|
|
const HEAP_SIZE = 2 * L_CODES + 1;
|
|
/* maximum heap size */
|
|
|
|
const MAX_BITS = 15;
|
|
/* All codes must not exceed MAX_BITS bits */
|
|
|
|
const MIN_MATCH = 3;
|
|
const MAX_MATCH = 258;
|
|
const MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1;
|
|
const PRESET_DICT = 0x20;
|
|
const INIT_STATE = 42;
|
|
const EXTRA_STATE = 69;
|
|
const NAME_STATE = 73;
|
|
const COMMENT_STATE = 91;
|
|
const HCRC_STATE = 103;
|
|
const BUSY_STATE = 113;
|
|
const FINISH_STATE = 666;
|
|
const BS_NEED_MORE = 1;
|
|
/* block not completed, need more input or more output */
|
|
|
|
const BS_BLOCK_DONE = 2;
|
|
/* block flush performed */
|
|
|
|
const BS_FINISH_STARTED = 3;
|
|
/* finish started, need only more output at next deflate */
|
|
|
|
const BS_FINISH_DONE = 4;
|
|
/* finish done, accept no more input or output */
|
|
|
|
const OS_CODE = 0x03; // Unix :) . Don't detect, use this default.
|
|
|
|
const err = (strm, errorCode) => {
|
|
strm.msg = messages[errorCode];
|
|
return errorCode;
|
|
};
|
|
|
|
const rank = f => {
|
|
return (f << 1) - (f > 4 ? 9 : 0);
|
|
};
|
|
|
|
const zero = buf => {
|
|
let len = buf.length;
|
|
|
|
while (--len >= 0) {
|
|
buf[len] = 0;
|
|
}
|
|
};
|
|
/* eslint-disable new-cap */
|
|
|
|
|
|
let HASH_ZLIB = (s, prev, data) => (prev << s.hash_shift ^ data) & s.hash_mask; // This hash causes less collisions, https://github.com/nodeca/pako/issues/135
|
|
// But breaks binary compatibility
|
|
//let HASH_FAST = (s, prev, data) => ((prev << 8) + (prev >> 8) + (data << 4)) & s.hash_mask;
|
|
|
|
|
|
let HASH = HASH_ZLIB;
|
|
/* =========================================================================
|
|
* Flush as much pending output as possible. All deflate() output goes
|
|
* through this function so some applications may wish to modify it
|
|
* to avoid allocating a large strm->output buffer and copying into it.
|
|
* (See also read_buf()).
|
|
*/
|
|
|
|
const flush_pending = strm => {
|
|
const s = strm.state; //_tr_flush_bits(s);
|
|
|
|
let len = s.pending;
|
|
|
|
if (len > strm.avail_out) {
|
|
len = strm.avail_out;
|
|
}
|
|
|
|
if (len === 0) {
|
|
return;
|
|
}
|
|
|
|
strm.output.set(s.pending_buf.subarray(s.pending_out, s.pending_out + len), strm.next_out);
|
|
strm.next_out += len;
|
|
s.pending_out += len;
|
|
strm.total_out += len;
|
|
strm.avail_out -= len;
|
|
s.pending -= len;
|
|
|
|
if (s.pending === 0) {
|
|
s.pending_out = 0;
|
|
}
|
|
};
|
|
|
|
const flush_block_only = (s, last) => {
|
|
_tr_flush_block(s, s.block_start >= 0 ? s.block_start : -1, s.strstart - s.block_start, last);
|
|
|
|
s.block_start = s.strstart;
|
|
flush_pending(s.strm);
|
|
};
|
|
|
|
const put_byte = (s, b) => {
|
|
s.pending_buf[s.pending++] = b;
|
|
};
|
|
/* =========================================================================
|
|
* Put a short in the pending buffer. The 16-bit value is put in MSB order.
|
|
* IN assertion: the stream state is correct and there is enough room in
|
|
* pending_buf.
|
|
*/
|
|
|
|
|
|
const putShortMSB = (s, b) => {
|
|
// put_byte(s, (Byte)(b >> 8));
|
|
// put_byte(s, (Byte)(b & 0xff));
|
|
s.pending_buf[s.pending++] = b >>> 8 & 0xff;
|
|
s.pending_buf[s.pending++] = b & 0xff;
|
|
};
|
|
/* ===========================================================================
|
|
* Read a new buffer from the current input stream, update the adler32
|
|
* and total number of bytes read. All deflate() input goes through
|
|
* this function so some applications may wish to modify it to avoid
|
|
* allocating a large strm->input buffer and copying from it.
|
|
* (See also flush_pending()).
|
|
*/
|
|
|
|
|
|
const read_buf = (strm, buf, start, size) => {
|
|
let len = strm.avail_in;
|
|
|
|
if (len > size) {
|
|
len = size;
|
|
}
|
|
|
|
if (len === 0) {
|
|
return 0;
|
|
}
|
|
|
|
strm.avail_in -= len; // zmemcpy(buf, strm->next_in, len);
|
|
|
|
buf.set(strm.input.subarray(strm.next_in, strm.next_in + len), start);
|
|
|
|
if (strm.state.wrap === 1) {
|
|
strm.adler = adler32_1(strm.adler, buf, len, start);
|
|
} else if (strm.state.wrap === 2) {
|
|
strm.adler = crc32_1(strm.adler, buf, len, start);
|
|
}
|
|
|
|
strm.next_in += len;
|
|
strm.total_in += len;
|
|
return len;
|
|
};
|
|
/* ===========================================================================
|
|
* Set match_start to the longest match starting at the given string and
|
|
* return its length. Matches shorter or equal to prev_length are discarded,
|
|
* in which case the result is equal to prev_length and match_start is
|
|
* garbage.
|
|
* IN assertions: cur_match is the head of the hash chain for the current
|
|
* string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
|
|
* OUT assertion: the match length is not greater than s->lookahead.
|
|
*/
|
|
|
|
|
|
const longest_match = (s, cur_match) => {
|
|
let chain_length = s.max_chain_length;
|
|
/* max hash chain length */
|
|
|
|
let scan = s.strstart;
|
|
/* current string */
|
|
|
|
let match;
|
|
/* matched string */
|
|
|
|
let len;
|
|
/* length of current match */
|
|
|
|
let best_len = s.prev_length;
|
|
/* best match length so far */
|
|
|
|
let nice_match = s.nice_match;
|
|
/* stop if match long enough */
|
|
|
|
const limit = s.strstart > s.w_size - MIN_LOOKAHEAD ? s.strstart - (s.w_size - MIN_LOOKAHEAD) : 0
|
|
/*NIL*/
|
|
;
|
|
const _win = s.window; // shortcut
|
|
|
|
const wmask = s.w_mask;
|
|
const prev = s.prev;
|
|
/* Stop when cur_match becomes <= limit. To simplify the code,
|
|
* we prevent matches with the string of window index 0.
|
|
*/
|
|
|
|
const strend = s.strstart + MAX_MATCH;
|
|
let scan_end1 = _win[scan + best_len - 1];
|
|
let scan_end = _win[scan + best_len];
|
|
/* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
|
|
* It is easy to get rid of this optimization if necessary.
|
|
*/
|
|
// Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
|
|
|
|
/* Do not waste too much time if we already have a good match: */
|
|
|
|
if (s.prev_length >= s.good_match) {
|
|
chain_length >>= 2;
|
|
}
|
|
/* Do not look for matches beyond the end of the input. This is necessary
|
|
* to make deflate deterministic.
|
|
*/
|
|
|
|
|
|
if (nice_match > s.lookahead) {
|
|
nice_match = s.lookahead;
|
|
} // Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
|
|
|
|
|
|
do {
|
|
// Assert(cur_match < s->strstart, "no future");
|
|
match = cur_match;
|
|
/* Skip to next match if the match length cannot increase
|
|
* or if the match length is less than 2. Note that the checks below
|
|
* for insufficient lookahead only occur occasionally for performance
|
|
* reasons. Therefore uninitialized memory will be accessed, and
|
|
* conditional jumps will be made that depend on those values.
|
|
* However the length of the match is limited to the lookahead, so
|
|
* the output of deflate is not affected by the uninitialized values.
|
|
*/
|
|
|
|
if (_win[match + best_len] !== scan_end || _win[match + best_len - 1] !== scan_end1 || _win[match] !== _win[scan] || _win[++match] !== _win[scan + 1]) {
|
|
continue;
|
|
}
|
|
/* The check at best_len-1 can be removed because it will be made
|
|
* again later. (This heuristic is not always a win.)
|
|
* It is not necessary to compare scan[2] and match[2] since they
|
|
* are always equal when the other bytes match, given that
|
|
* the hash keys are equal and that HASH_BITS >= 8.
|
|
*/
|
|
|
|
|
|
scan += 2;
|
|
match++; // Assert(*scan == *match, "match[2]?");
|
|
|
|
/* We check for insufficient lookahead only every 8th comparison;
|
|
* the 256th check will be made at strstart+258.
|
|
*/
|
|
|
|
do {
|
|
/*jshint noempty:false*/
|
|
} while (_win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && scan < strend); // Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
|
|
|
|
|
|
len = MAX_MATCH - (strend - scan);
|
|
scan = strend - MAX_MATCH;
|
|
|
|
if (len > best_len) {
|
|
s.match_start = cur_match;
|
|
best_len = len;
|
|
|
|
if (len >= nice_match) {
|
|
break;
|
|
}
|
|
|
|
scan_end1 = _win[scan + best_len - 1];
|
|
scan_end = _win[scan + best_len];
|
|
}
|
|
} while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0);
|
|
|
|
if (best_len <= s.lookahead) {
|
|
return best_len;
|
|
}
|
|
|
|
return s.lookahead;
|
|
};
|
|
/* ===========================================================================
|
|
* Fill the window when the lookahead becomes insufficient.
|
|
* Updates strstart and lookahead.
|
|
*
|
|
* IN assertion: lookahead < MIN_LOOKAHEAD
|
|
* OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
|
|
* At least one byte has been read, or avail_in == 0; reads are
|
|
* performed for at least two bytes (required for the zip translate_eol
|
|
* option -- not supported here).
|
|
*/
|
|
|
|
|
|
const fill_window = s => {
|
|
const _w_size = s.w_size;
|
|
let p, n, m, more, str; //Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead");
|
|
|
|
do {
|
|
more = s.window_size - s.lookahead - s.strstart; // JS ints have 32 bit, block below not needed
|
|
|
|
/* Deal with !@#$% 64K limit: */
|
|
//if (sizeof(int) <= 2) {
|
|
// if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
|
|
// more = wsize;
|
|
//
|
|
// } else if (more == (unsigned)(-1)) {
|
|
// /* Very unlikely, but possible on 16 bit machine if
|
|
// * strstart == 0 && lookahead == 1 (input done a byte at time)
|
|
// */
|
|
// more--;
|
|
// }
|
|
//}
|
|
|
|
/* If the window is almost full and there is insufficient lookahead,
|
|
* move the upper half to the lower one to make room in the upper half.
|
|
*/
|
|
|
|
if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) {
|
|
s.window.set(s.window.subarray(_w_size, _w_size + _w_size), 0);
|
|
s.match_start -= _w_size;
|
|
s.strstart -= _w_size;
|
|
/* we now have strstart >= MAX_DIST */
|
|
|
|
s.block_start -= _w_size;
|
|
/* Slide the hash table (could be avoided with 32 bit values
|
|
at the expense of memory usage). We slide even when level == 0
|
|
to keep the hash table consistent if we switch back to level > 0
|
|
later. (Using level 0 permanently is not an optimal usage of
|
|
zlib, so we don't care about this pathological case.)
|
|
*/
|
|
|
|
n = s.hash_size;
|
|
p = n;
|
|
|
|
do {
|
|
m = s.head[--p];
|
|
s.head[p] = m >= _w_size ? m - _w_size : 0;
|
|
} while (--n);
|
|
|
|
n = _w_size;
|
|
p = n;
|
|
|
|
do {
|
|
m = s.prev[--p];
|
|
s.prev[p] = m >= _w_size ? m - _w_size : 0;
|
|
/* If n is not on any hash chain, prev[n] is garbage but
|
|
* its value will never be used.
|
|
*/
|
|
} while (--n);
|
|
|
|
more += _w_size;
|
|
}
|
|
|
|
if (s.strm.avail_in === 0) {
|
|
break;
|
|
}
|
|
/* If there was no sliding:
|
|
* strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
|
|
* more == window_size - lookahead - strstart
|
|
* => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
|
|
* => more >= window_size - 2*WSIZE + 2
|
|
* In the BIG_MEM or MMAP case (not yet supported),
|
|
* window_size == input_size + MIN_LOOKAHEAD &&
|
|
* strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
|
|
* Otherwise, window_size == 2*WSIZE so more >= 2.
|
|
* If there was sliding, more >= WSIZE. So in all cases, more >= 2.
|
|
*/
|
|
//Assert(more >= 2, "more < 2");
|
|
|
|
|
|
n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more);
|
|
s.lookahead += n;
|
|
/* Initialize the hash value now that we have some input: */
|
|
|
|
if (s.lookahead + s.insert >= MIN_MATCH) {
|
|
str = s.strstart - s.insert;
|
|
s.ins_h = s.window[str];
|
|
/* UPDATE_HASH(s, s->ins_h, s->window[str + 1]); */
|
|
|
|
s.ins_h = HASH(s, s.ins_h, s.window[str + 1]); //#if MIN_MATCH != 3
|
|
// Call update_hash() MIN_MATCH-3 more times
|
|
//#endif
|
|
|
|
while (s.insert) {
|
|
/* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */
|
|
s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH - 1]);
|
|
s.prev[str & s.w_mask] = s.head[s.ins_h];
|
|
s.head[s.ins_h] = str;
|
|
str++;
|
|
s.insert--;
|
|
|
|
if (s.lookahead + s.insert < MIN_MATCH) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
|
|
* but this is not important since only literal bytes will be emitted.
|
|
*/
|
|
|
|
} while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in !== 0);
|
|
/* If the WIN_INIT bytes after the end of the current data have never been
|
|
* written, then zero those bytes in order to avoid memory check reports of
|
|
* the use of uninitialized (or uninitialised as Julian writes) bytes by
|
|
* the longest match routines. Update the high water mark for the next
|
|
* time through here. WIN_INIT is set to MAX_MATCH since the longest match
|
|
* routines allow scanning to strstart + MAX_MATCH, ignoring lookahead.
|
|
*/
|
|
// if (s.high_water < s.window_size) {
|
|
// const curr = s.strstart + s.lookahead;
|
|
// let init = 0;
|
|
//
|
|
// if (s.high_water < curr) {
|
|
// /* Previous high water mark below current data -- zero WIN_INIT
|
|
// * bytes or up to end of window, whichever is less.
|
|
// */
|
|
// init = s.window_size - curr;
|
|
// if (init > WIN_INIT)
|
|
// init = WIN_INIT;
|
|
// zmemzero(s->window + curr, (unsigned)init);
|
|
// s->high_water = curr + init;
|
|
// }
|
|
// else if (s->high_water < (ulg)curr + WIN_INIT) {
|
|
// /* High water mark at or above current data, but below current data
|
|
// * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up
|
|
// * to end of window, whichever is less.
|
|
// */
|
|
// init = (ulg)curr + WIN_INIT - s->high_water;
|
|
// if (init > s->window_size - s->high_water)
|
|
// init = s->window_size - s->high_water;
|
|
// zmemzero(s->window + s->high_water, (unsigned)init);
|
|
// s->high_water += init;
|
|
// }
|
|
// }
|
|
//
|
|
// Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,
|
|
// "not enough room for search");
|
|
|
|
};
|
|
/* ===========================================================================
|
|
* Copy without compression as much as possible from the input stream, return
|
|
* the current block state.
|
|
* This function does not insert new strings in the dictionary since
|
|
* uncompressible data is probably not useful. This function is used
|
|
* only for the level=0 compression option.
|
|
* NOTE: this function should be optimized to avoid extra copying from
|
|
* window to pending_buf.
|
|
*/
|
|
|
|
|
|
const deflate_stored = (s, flush) => {
|
|
/* Stored blocks are limited to 0xffff bytes, pending_buf is limited
|
|
* to pending_buf_size, and each stored block has a 5 byte header:
|
|
*/
|
|
let max_block_size = 0xffff;
|
|
|
|
if (max_block_size > s.pending_buf_size - 5) {
|
|
max_block_size = s.pending_buf_size - 5;
|
|
}
|
|
/* Copy as much as possible from input to output: */
|
|
|
|
|
|
for (;;) {
|
|
/* Fill the window as much as possible: */
|
|
if (s.lookahead <= 1) {
|
|
//Assert(s->strstart < s->w_size+MAX_DIST(s) ||
|
|
// s->block_start >= (long)s->w_size, "slide too late");
|
|
// if (!(s.strstart < s.w_size + (s.w_size - MIN_LOOKAHEAD) ||
|
|
// s.block_start >= s.w_size)) {
|
|
// throw new Error("slide too late");
|
|
// }
|
|
fill_window(s);
|
|
|
|
if (s.lookahead === 0 && flush === Z_NO_FLUSH$2) {
|
|
return BS_NEED_MORE;
|
|
}
|
|
|
|
if (s.lookahead === 0) {
|
|
break;
|
|
}
|
|
/* flush the current block */
|
|
|
|
} //Assert(s->block_start >= 0L, "block gone");
|
|
// if (s.block_start < 0) throw new Error("block gone");
|
|
|
|
|
|
s.strstart += s.lookahead;
|
|
s.lookahead = 0;
|
|
/* Emit a stored block if pending_buf will be full: */
|
|
|
|
const max_start = s.block_start + max_block_size;
|
|
|
|
if (s.strstart === 0 || s.strstart >= max_start) {
|
|
/* strstart == 0 is possible when wraparound on 16-bit machine */
|
|
s.lookahead = s.strstart - max_start;
|
|
s.strstart = max_start;
|
|
/*** FLUSH_BLOCK(s, 0); ***/
|
|
|
|
flush_block_only(s, false);
|
|
|
|
if (s.strm.avail_out === 0) {
|
|
return BS_NEED_MORE;
|
|
}
|
|
/***/
|
|
|
|
}
|
|
/* Flush if we may have to slide, otherwise block_start may become
|
|
* negative and the data will be gone:
|
|
*/
|
|
|
|
|
|
if (s.strstart - s.block_start >= s.w_size - MIN_LOOKAHEAD) {
|
|
/*** FLUSH_BLOCK(s, 0); ***/
|
|
flush_block_only(s, false);
|
|
|
|
if (s.strm.avail_out === 0) {
|
|
return BS_NEED_MORE;
|
|
}
|
|
/***/
|
|
|
|
}
|
|
}
|
|
|
|
s.insert = 0;
|
|
|
|
if (flush === Z_FINISH$3) {
|
|
/*** FLUSH_BLOCK(s, 1); ***/
|
|
flush_block_only(s, true);
|
|
|
|
if (s.strm.avail_out === 0) {
|
|
return BS_FINISH_STARTED;
|
|
}
|
|
/***/
|
|
|
|
|
|
return BS_FINISH_DONE;
|
|
}
|
|
|
|
if (s.strstart > s.block_start) {
|
|
/*** FLUSH_BLOCK(s, 0); ***/
|
|
flush_block_only(s, false);
|
|
|
|
if (s.strm.avail_out === 0) {
|
|
return BS_NEED_MORE;
|
|
}
|
|
/***/
|
|
|
|
}
|
|
|
|
return BS_NEED_MORE;
|
|
};
|
|
/* ===========================================================================
|
|
* Compress as much as possible from the input stream, return the current
|
|
* block state.
|
|
* This function does not perform lazy evaluation of matches and inserts
|
|
* new strings in the dictionary only for unmatched strings or for short
|
|
* matches. It is used only for the fast compression options.
|
|
*/
|
|
|
|
|
|
const deflate_fast = (s, flush) => {
|
|
let hash_head;
|
|
/* head of the hash chain */
|
|
|
|
let bflush;
|
|
/* set if current block must be flushed */
|
|
|
|
for (;;) {
|
|
/* Make sure that we always have enough lookahead, except
|
|
* at the end of the input file. We need MAX_MATCH bytes
|
|
* for the next match, plus MIN_MATCH bytes to insert the
|
|
* string following the next match.
|
|
*/
|
|
if (s.lookahead < MIN_LOOKAHEAD) {
|
|
fill_window(s);
|
|
|
|
if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH$2) {
|
|
return BS_NEED_MORE;
|
|
}
|
|
|
|
if (s.lookahead === 0) {
|
|
break;
|
|
/* flush the current block */
|
|
}
|
|
}
|
|
/* Insert the string window[strstart .. strstart+2] in the
|
|
* dictionary, and set hash_head to the head of the hash chain:
|
|
*/
|
|
|
|
|
|
hash_head = 0
|
|
/*NIL*/
|
|
;
|
|
|
|
if (s.lookahead >= MIN_MATCH) {
|
|
/*** INSERT_STRING(s, s.strstart, hash_head); ***/
|
|
s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]);
|
|
hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
|
|
s.head[s.ins_h] = s.strstart;
|
|
/***/
|
|
}
|
|
/* Find the longest match, discarding those <= prev_length.
|
|
* At this point we have always match_length < MIN_MATCH
|
|
*/
|
|
|
|
|
|
if (hash_head !== 0
|
|
/*NIL*/
|
|
&& s.strstart - hash_head <= s.w_size - MIN_LOOKAHEAD) {
|
|
/* To simplify the code, we prevent matches with the string
|
|
* of window index 0 (in particular we have to avoid a match
|
|
* of the string with itself at the start of the input file).
|
|
*/
|
|
s.match_length = longest_match(s, hash_head);
|
|
/* longest_match() sets match_start */
|
|
}
|
|
|
|
if (s.match_length >= MIN_MATCH) {
|
|
// check_match(s, s.strstart, s.match_start, s.match_length); // for debug only
|
|
|
|
/*** _tr_tally_dist(s, s.strstart - s.match_start,
|
|
s.match_length - MIN_MATCH, bflush); ***/
|
|
bflush = _tr_tally(s, s.strstart - s.match_start, s.match_length - MIN_MATCH);
|
|
s.lookahead -= s.match_length;
|
|
/* Insert new strings in the hash table only if the match length
|
|
* is not too large. This saves time but degrades compression.
|
|
*/
|
|
|
|
if (s.match_length <= s.max_lazy_match
|
|
/*max_insert_length*/
|
|
&& s.lookahead >= MIN_MATCH) {
|
|
s.match_length--;
|
|
/* string at strstart already in table */
|
|
|
|
do {
|
|
s.strstart++;
|
|
/*** INSERT_STRING(s, s.strstart, hash_head); ***/
|
|
|
|
s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]);
|
|
hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
|
|
s.head[s.ins_h] = s.strstart;
|
|
/***/
|
|
|
|
/* strstart never exceeds WSIZE-MAX_MATCH, so there are
|
|
* always MIN_MATCH bytes ahead.
|
|
*/
|
|
} while (--s.match_length !== 0);
|
|
|
|
s.strstart++;
|
|
} else {
|
|
s.strstart += s.match_length;
|
|
s.match_length = 0;
|
|
s.ins_h = s.window[s.strstart];
|
|
/* UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]); */
|
|
|
|
s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + 1]); //#if MIN_MATCH != 3
|
|
// Call UPDATE_HASH() MIN_MATCH-3 more times
|
|
//#endif
|
|
|
|
/* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
|
|
* matter since it will be recomputed at next deflate call.
|
|
*/
|
|
}
|
|
} else {
|
|
/* No match, output a literal byte */
|
|
//Tracevv((stderr,"%c", s.window[s.strstart]));
|
|
|
|
/*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
|
|
bflush = _tr_tally(s, 0, s.window[s.strstart]);
|
|
s.lookahead--;
|
|
s.strstart++;
|
|
}
|
|
|
|
if (bflush) {
|
|
/*** FLUSH_BLOCK(s, 0); ***/
|
|
flush_block_only(s, false);
|
|
|
|
if (s.strm.avail_out === 0) {
|
|
return BS_NEED_MORE;
|
|
}
|
|
/***/
|
|
|
|
}
|
|
}
|
|
|
|
s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1;
|
|
|
|
if (flush === Z_FINISH$3) {
|
|
/*** FLUSH_BLOCK(s, 1); ***/
|
|
flush_block_only(s, true);
|
|
|
|
if (s.strm.avail_out === 0) {
|
|
return BS_FINISH_STARTED;
|
|
}
|
|
/***/
|
|
|
|
|
|
return BS_FINISH_DONE;
|
|
}
|
|
|
|
if (s.last_lit) {
|
|
/*** FLUSH_BLOCK(s, 0); ***/
|
|
flush_block_only(s, false);
|
|
|
|
if (s.strm.avail_out === 0) {
|
|
return BS_NEED_MORE;
|
|
}
|
|
/***/
|
|
|
|
}
|
|
|
|
return BS_BLOCK_DONE;
|
|
};
|
|
/* ===========================================================================
|
|
* Same as above, but achieves better compression. We use a lazy
|
|
* evaluation for matches: a match is finally adopted only if there is
|
|
* no better match at the next window position.
|
|
*/
|
|
|
|
|
|
const deflate_slow = (s, flush) => {
|
|
let hash_head;
|
|
/* head of hash chain */
|
|
|
|
let bflush;
|
|
/* set if current block must be flushed */
|
|
|
|
let max_insert;
|
|
/* Process the input block. */
|
|
|
|
for (;;) {
|
|
/* Make sure that we always have enough lookahead, except
|
|
* at the end of the input file. We need MAX_MATCH bytes
|
|
* for the next match, plus MIN_MATCH bytes to insert the
|
|
* string following the next match.
|
|
*/
|
|
if (s.lookahead < MIN_LOOKAHEAD) {
|
|
fill_window(s);
|
|
|
|
if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH$2) {
|
|
return BS_NEED_MORE;
|
|
}
|
|
|
|
if (s.lookahead === 0) {
|
|
break;
|
|
}
|
|
/* flush the current block */
|
|
|
|
}
|
|
/* Insert the string window[strstart .. strstart+2] in the
|
|
* dictionary, and set hash_head to the head of the hash chain:
|
|
*/
|
|
|
|
|
|
hash_head = 0
|
|
/*NIL*/
|
|
;
|
|
|
|
if (s.lookahead >= MIN_MATCH) {
|
|
/*** INSERT_STRING(s, s.strstart, hash_head); ***/
|
|
s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]);
|
|
hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
|
|
s.head[s.ins_h] = s.strstart;
|
|
/***/
|
|
}
|
|
/* Find the longest match, discarding those <= prev_length.
|
|
*/
|
|
|
|
|
|
s.prev_length = s.match_length;
|
|
s.prev_match = s.match_start;
|
|
s.match_length = MIN_MATCH - 1;
|
|
|
|
if (hash_head !== 0
|
|
/*NIL*/
|
|
&& s.prev_length < s.max_lazy_match && s.strstart - hash_head <= s.w_size - MIN_LOOKAHEAD
|
|
/*MAX_DIST(s)*/
|
|
) {
|
|
/* To simplify the code, we prevent matches with the string
|
|
* of window index 0 (in particular we have to avoid a match
|
|
* of the string with itself at the start of the input file).
|
|
*/
|
|
s.match_length = longest_match(s, hash_head);
|
|
/* longest_match() sets match_start */
|
|
|
|
if (s.match_length <= 5 && (s.strategy === Z_FILTERED || s.match_length === MIN_MATCH && s.strstart - s.match_start > 4096
|
|
/*TOO_FAR*/
|
|
)) {
|
|
/* If prev_match is also MIN_MATCH, match_start is garbage
|
|
* but we will ignore the current match anyway.
|
|
*/
|
|
s.match_length = MIN_MATCH - 1;
|
|
}
|
|
}
|
|
/* If there was a match at the previous step and the current
|
|
* match is not better, output the previous match:
|
|
*/
|
|
|
|
|
|
if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) {
|
|
max_insert = s.strstart + s.lookahead - MIN_MATCH;
|
|
/* Do not insert strings in hash table beyond this. */
|
|
//check_match(s, s.strstart-1, s.prev_match, s.prev_length);
|
|
|
|
/***_tr_tally_dist(s, s.strstart - 1 - s.prev_match,
|
|
s.prev_length - MIN_MATCH, bflush);***/
|
|
|
|
bflush = _tr_tally(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH);
|
|
/* Insert in hash table all strings up to the end of the match.
|
|
* strstart-1 and strstart are already inserted. If there is not
|
|
* enough lookahead, the last two strings are not inserted in
|
|
* the hash table.
|
|
*/
|
|
|
|
s.lookahead -= s.prev_length - 1;
|
|
s.prev_length -= 2;
|
|
|
|
do {
|
|
if (++s.strstart <= max_insert) {
|
|
/*** INSERT_STRING(s, s.strstart, hash_head); ***/
|
|
s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]);
|
|
hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
|
|
s.head[s.ins_h] = s.strstart;
|
|
/***/
|
|
}
|
|
} while (--s.prev_length !== 0);
|
|
|
|
s.match_available = 0;
|
|
s.match_length = MIN_MATCH - 1;
|
|
s.strstart++;
|
|
|
|
if (bflush) {
|
|
/*** FLUSH_BLOCK(s, 0); ***/
|
|
flush_block_only(s, false);
|
|
|
|
if (s.strm.avail_out === 0) {
|
|
return BS_NEED_MORE;
|
|
}
|
|
/***/
|
|
|
|
}
|
|
} else if (s.match_available) {
|
|
/* If there was no match at the previous position, output a
|
|
* single literal. If there was a match but the current match
|
|
* is longer, truncate the previous match to a single literal.
|
|
*/
|
|
//Tracevv((stderr,"%c", s->window[s->strstart-1]));
|
|
|
|
/*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/
|
|
bflush = _tr_tally(s, 0, s.window[s.strstart - 1]);
|
|
|
|
if (bflush) {
|
|
/*** FLUSH_BLOCK_ONLY(s, 0) ***/
|
|
flush_block_only(s, false);
|
|
/***/
|
|
}
|
|
|
|
s.strstart++;
|
|
s.lookahead--;
|
|
|
|
if (s.strm.avail_out === 0) {
|
|
return BS_NEED_MORE;
|
|
}
|
|
} else {
|
|
/* There is no previous match to compare with, wait for
|
|
* the next step to decide.
|
|
*/
|
|
s.match_available = 1;
|
|
s.strstart++;
|
|
s.lookahead--;
|
|
}
|
|
} //Assert (flush != Z_NO_FLUSH, "no flush?");
|
|
|
|
|
|
if (s.match_available) {
|
|
//Tracevv((stderr,"%c", s->window[s->strstart-1]));
|
|
|
|
/*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/
|
|
bflush = _tr_tally(s, 0, s.window[s.strstart - 1]);
|
|
s.match_available = 0;
|
|
}
|
|
|
|
s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1;
|
|
|
|
if (flush === Z_FINISH$3) {
|
|
/*** FLUSH_BLOCK(s, 1); ***/
|
|
flush_block_only(s, true);
|
|
|
|
if (s.strm.avail_out === 0) {
|
|
return BS_FINISH_STARTED;
|
|
}
|
|
/***/
|
|
|
|
|
|
return BS_FINISH_DONE;
|
|
}
|
|
|
|
if (s.last_lit) {
|
|
/*** FLUSH_BLOCK(s, 0); ***/
|
|
flush_block_only(s, false);
|
|
|
|
if (s.strm.avail_out === 0) {
|
|
return BS_NEED_MORE;
|
|
}
|
|
/***/
|
|
|
|
}
|
|
|
|
return BS_BLOCK_DONE;
|
|
};
|
|
/* ===========================================================================
|
|
* For Z_RLE, simply look for runs of bytes, generate matches only of distance
|
|
* one. Do not maintain a hash table. (It will be regenerated if this run of
|
|
* deflate switches away from Z_RLE.)
|
|
*/
|
|
|
|
|
|
const deflate_rle = (s, flush) => {
|
|
let bflush;
|
|
/* set if current block must be flushed */
|
|
|
|
let prev;
|
|
/* byte at distance one to match */
|
|
|
|
let scan, strend;
|
|
/* scan goes up to strend for length of run */
|
|
|
|
const _win = s.window;
|
|
|
|
for (;;) {
|
|
/* Make sure that we always have enough lookahead, except
|
|
* at the end of the input file. We need MAX_MATCH bytes
|
|
* for the longest run, plus one for the unrolled loop.
|
|
*/
|
|
if (s.lookahead <= MAX_MATCH) {
|
|
fill_window(s);
|
|
|
|
if (s.lookahead <= MAX_MATCH && flush === Z_NO_FLUSH$2) {
|
|
return BS_NEED_MORE;
|
|
}
|
|
|
|
if (s.lookahead === 0) {
|
|
break;
|
|
}
|
|
/* flush the current block */
|
|
|
|
}
|
|
/* See how many times the previous byte repeats */
|
|
|
|
|
|
s.match_length = 0;
|
|
|
|
if (s.lookahead >= MIN_MATCH && s.strstart > 0) {
|
|
scan = s.strstart - 1;
|
|
prev = _win[scan];
|
|
|
|
if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) {
|
|
strend = s.strstart + MAX_MATCH;
|
|
|
|
do {
|
|
/*jshint noempty:false*/
|
|
} while (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && scan < strend);
|
|
|
|
s.match_length = MAX_MATCH - (strend - scan);
|
|
|
|
if (s.match_length > s.lookahead) {
|
|
s.match_length = s.lookahead;
|
|
}
|
|
} //Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan");
|
|
|
|
}
|
|
/* Emit match if have run of MIN_MATCH or longer, else emit literal */
|
|
|
|
|
|
if (s.match_length >= MIN_MATCH) {
|
|
//check_match(s, s.strstart, s.strstart - 1, s.match_length);
|
|
|
|
/*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/
|
|
bflush = _tr_tally(s, 1, s.match_length - MIN_MATCH);
|
|
s.lookahead -= s.match_length;
|
|
s.strstart += s.match_length;
|
|
s.match_length = 0;
|
|
} else {
|
|
/* No match, output a literal byte */
|
|
//Tracevv((stderr,"%c", s->window[s->strstart]));
|
|
|
|
/*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
|
|
bflush = _tr_tally(s, 0, s.window[s.strstart]);
|
|
s.lookahead--;
|
|
s.strstart++;
|
|
}
|
|
|
|
if (bflush) {
|
|
/*** FLUSH_BLOCK(s, 0); ***/
|
|
flush_block_only(s, false);
|
|
|
|
if (s.strm.avail_out === 0) {
|
|
return BS_NEED_MORE;
|
|
}
|
|
/***/
|
|
|
|
}
|
|
}
|
|
|
|
s.insert = 0;
|
|
|
|
if (flush === Z_FINISH$3) {
|
|
/*** FLUSH_BLOCK(s, 1); ***/
|
|
flush_block_only(s, true);
|
|
|
|
if (s.strm.avail_out === 0) {
|
|
return BS_FINISH_STARTED;
|
|
}
|
|
/***/
|
|
|
|
|
|
return BS_FINISH_DONE;
|
|
}
|
|
|
|
if (s.last_lit) {
|
|
/*** FLUSH_BLOCK(s, 0); ***/
|
|
flush_block_only(s, false);
|
|
|
|
if (s.strm.avail_out === 0) {
|
|
return BS_NEED_MORE;
|
|
}
|
|
/***/
|
|
|
|
}
|
|
|
|
return BS_BLOCK_DONE;
|
|
};
|
|
/* ===========================================================================
|
|
* For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table.
|
|
* (It will be regenerated if this run of deflate switches away from Huffman.)
|
|
*/
|
|
|
|
|
|
const deflate_huff = (s, flush) => {
|
|
let bflush;
|
|
/* set if current block must be flushed */
|
|
|
|
for (;;) {
|
|
/* Make sure that we have a literal to write. */
|
|
if (s.lookahead === 0) {
|
|
fill_window(s);
|
|
|
|
if (s.lookahead === 0) {
|
|
if (flush === Z_NO_FLUSH$2) {
|
|
return BS_NEED_MORE;
|
|
}
|
|
|
|
break;
|
|
/* flush the current block */
|
|
}
|
|
}
|
|
/* Output a literal byte */
|
|
|
|
|
|
s.match_length = 0; //Tracevv((stderr,"%c", s->window[s->strstart]));
|
|
|
|
/*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
|
|
|
|
bflush = _tr_tally(s, 0, s.window[s.strstart]);
|
|
s.lookahead--;
|
|
s.strstart++;
|
|
|
|
if (bflush) {
|
|
/*** FLUSH_BLOCK(s, 0); ***/
|
|
flush_block_only(s, false);
|
|
|
|
if (s.strm.avail_out === 0) {
|
|
return BS_NEED_MORE;
|
|
}
|
|
/***/
|
|
|
|
}
|
|
}
|
|
|
|
s.insert = 0;
|
|
|
|
if (flush === Z_FINISH$3) {
|
|
/*** FLUSH_BLOCK(s, 1); ***/
|
|
flush_block_only(s, true);
|
|
|
|
if (s.strm.avail_out === 0) {
|
|
return BS_FINISH_STARTED;
|
|
}
|
|
/***/
|
|
|
|
|
|
return BS_FINISH_DONE;
|
|
}
|
|
|
|
if (s.last_lit) {
|
|
/*** FLUSH_BLOCK(s, 0); ***/
|
|
flush_block_only(s, false);
|
|
|
|
if (s.strm.avail_out === 0) {
|
|
return BS_NEED_MORE;
|
|
}
|
|
/***/
|
|
|
|
}
|
|
|
|
return BS_BLOCK_DONE;
|
|
};
|
|
/* Values for max_lazy_match, good_match and max_chain_length, depending on
|
|
* the desired pack level (0..9). The values given below have been tuned to
|
|
* exclude worst case performance for pathological files. Better values may be
|
|
* found for specific files.
|
|
*/
|
|
|
|
|
|
function Config(good_length, max_lazy, nice_length, max_chain, func) {
|
|
this.good_length = good_length;
|
|
this.max_lazy = max_lazy;
|
|
this.nice_length = nice_length;
|
|
this.max_chain = max_chain;
|
|
this.func = func;
|
|
}
|
|
|
|
const configuration_table = [
|
|
/* good lazy nice chain */
|
|
new Config(0, 0, 0, 0, deflate_stored),
|
|
/* 0 store only */
|
|
new Config(4, 4, 8, 4, deflate_fast),
|
|
/* 1 max speed, no lazy matches */
|
|
new Config(4, 5, 16, 8, deflate_fast),
|
|
/* 2 */
|
|
new Config(4, 6, 32, 32, deflate_fast),
|
|
/* 3 */
|
|
new Config(4, 4, 16, 16, deflate_slow),
|
|
/* 4 lazy matches */
|
|
new Config(8, 16, 32, 32, deflate_slow),
|
|
/* 5 */
|
|
new Config(8, 16, 128, 128, deflate_slow),
|
|
/* 6 */
|
|
new Config(8, 32, 128, 256, deflate_slow),
|
|
/* 7 */
|
|
new Config(32, 128, 258, 1024, deflate_slow),
|
|
/* 8 */
|
|
new Config(32, 258, 258, 4096, deflate_slow)
|
|
/* 9 max compression */
|
|
];
|
|
/* ===========================================================================
|
|
* Initialize the "longest match" routines for a new zlib stream
|
|
*/
|
|
|
|
const lm_init = s => {
|
|
s.window_size = 2 * s.w_size;
|
|
/*** CLEAR_HASH(s); ***/
|
|
|
|
zero(s.head); // Fill with NIL (= 0);
|
|
|
|
/* Set the default configuration parameters:
|
|
*/
|
|
|
|
s.max_lazy_match = configuration_table[s.level].max_lazy;
|
|
s.good_match = configuration_table[s.level].good_length;
|
|
s.nice_match = configuration_table[s.level].nice_length;
|
|
s.max_chain_length = configuration_table[s.level].max_chain;
|
|
s.strstart = 0;
|
|
s.block_start = 0;
|
|
s.lookahead = 0;
|
|
s.insert = 0;
|
|
s.match_length = s.prev_length = MIN_MATCH - 1;
|
|
s.match_available = 0;
|
|
s.ins_h = 0;
|
|
};
|
|
|
|
function DeflateState() {
|
|
this.strm = null;
|
|
/* pointer back to this zlib stream */
|
|
|
|
this.status = 0;
|
|
/* as the name implies */
|
|
|
|
this.pending_buf = null;
|
|
/* output still pending */
|
|
|
|
this.pending_buf_size = 0;
|
|
/* size of pending_buf */
|
|
|
|
this.pending_out = 0;
|
|
/* next pending byte to output to the stream */
|
|
|
|
this.pending = 0;
|
|
/* nb of bytes in the pending buffer */
|
|
|
|
this.wrap = 0;
|
|
/* bit 0 true for zlib, bit 1 true for gzip */
|
|
|
|
this.gzhead = null;
|
|
/* gzip header information to write */
|
|
|
|
this.gzindex = 0;
|
|
/* where in extra, name, or comment */
|
|
|
|
this.method = Z_DEFLATED$2;
|
|
/* can only be DEFLATED */
|
|
|
|
this.last_flush = -1;
|
|
/* value of flush param for previous deflate call */
|
|
|
|
this.w_size = 0;
|
|
/* LZ77 window size (32K by default) */
|
|
|
|
this.w_bits = 0;
|
|
/* log2(w_size) (8..16) */
|
|
|
|
this.w_mask = 0;
|
|
/* w_size - 1 */
|
|
|
|
this.window = null;
|
|
/* Sliding window. Input bytes are read into the second half of the window,
|
|
* and move to the first half later to keep a dictionary of at least wSize
|
|
* bytes. With this organization, matches are limited to a distance of
|
|
* wSize-MAX_MATCH bytes, but this ensures that IO is always
|
|
* performed with a length multiple of the block size.
|
|
*/
|
|
|
|
this.window_size = 0;
|
|
/* Actual size of window: 2*wSize, except when the user input buffer
|
|
* is directly used as sliding window.
|
|
*/
|
|
|
|
this.prev = null;
|
|
/* Link to older string with same hash index. To limit the size of this
|
|
* array to 64K, this link is maintained only for the last 32K strings.
|
|
* An index in this array is thus a window index modulo 32K.
|
|
*/
|
|
|
|
this.head = null;
|
|
/* Heads of the hash chains or NIL. */
|
|
|
|
this.ins_h = 0;
|
|
/* hash index of string to be inserted */
|
|
|
|
this.hash_size = 0;
|
|
/* number of elements in hash table */
|
|
|
|
this.hash_bits = 0;
|
|
/* log2(hash_size) */
|
|
|
|
this.hash_mask = 0;
|
|
/* hash_size-1 */
|
|
|
|
this.hash_shift = 0;
|
|
/* Number of bits by which ins_h must be shifted at each input
|
|
* step. It must be such that after MIN_MATCH steps, the oldest
|
|
* byte no longer takes part in the hash key, that is:
|
|
* hash_shift * MIN_MATCH >= hash_bits
|
|
*/
|
|
|
|
this.block_start = 0;
|
|
/* Window position at the beginning of the current output block. Gets
|
|
* negative when the window is moved backwards.
|
|
*/
|
|
|
|
this.match_length = 0;
|
|
/* length of best match */
|
|
|
|
this.prev_match = 0;
|
|
/* previous match */
|
|
|
|
this.match_available = 0;
|
|
/* set if previous match exists */
|
|
|
|
this.strstart = 0;
|
|
/* start of string to insert */
|
|
|
|
this.match_start = 0;
|
|
/* start of matching string */
|
|
|
|
this.lookahead = 0;
|
|
/* number of valid bytes ahead in window */
|
|
|
|
this.prev_length = 0;
|
|
/* Length of the best match at previous step. Matches not greater than this
|
|
* are discarded. This is used in the lazy match evaluation.
|
|
*/
|
|
|
|
this.max_chain_length = 0;
|
|
/* To speed up deflation, hash chains are never searched beyond this
|
|
* length. A higher limit improves compression ratio but degrades the
|
|
* speed.
|
|
*/
|
|
|
|
this.max_lazy_match = 0;
|
|
/* Attempt to find a better match only when the current match is strictly
|
|
* smaller than this value. This mechanism is used only for compression
|
|
* levels >= 4.
|
|
*/
|
|
// That's alias to max_lazy_match, don't use directly
|
|
//this.max_insert_length = 0;
|
|
|
|
/* Insert new strings in the hash table only if the match length is not
|
|
* greater than this length. This saves time but degrades compression.
|
|
* max_insert_length is used only for compression levels <= 3.
|
|
*/
|
|
|
|
this.level = 0;
|
|
/* compression level (1..9) */
|
|
|
|
this.strategy = 0;
|
|
/* favor or force Huffman coding*/
|
|
|
|
this.good_match = 0;
|
|
/* Use a faster search when the previous match is longer than this */
|
|
|
|
this.nice_match = 0;
|
|
/* Stop searching when current match exceeds this */
|
|
|
|
/* used by trees.c: */
|
|
|
|
/* Didn't use ct_data typedef below to suppress compiler warning */
|
|
// struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */
|
|
// struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
|
|
// struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */
|
|
// Use flat array of DOUBLE size, with interleaved fata,
|
|
// because JS does not support effective
|
|
|
|
this.dyn_ltree = new Uint16Array(HEAP_SIZE * 2);
|
|
this.dyn_dtree = new Uint16Array((2 * D_CODES + 1) * 2);
|
|
this.bl_tree = new Uint16Array((2 * BL_CODES + 1) * 2);
|
|
zero(this.dyn_ltree);
|
|
zero(this.dyn_dtree);
|
|
zero(this.bl_tree);
|
|
this.l_desc = null;
|
|
/* desc. for literal tree */
|
|
|
|
this.d_desc = null;
|
|
/* desc. for distance tree */
|
|
|
|
this.bl_desc = null;
|
|
/* desc. for bit length tree */
|
|
//ush bl_count[MAX_BITS+1];
|
|
|
|
this.bl_count = new Uint16Array(MAX_BITS + 1);
|
|
/* number of codes at each bit length for an optimal tree */
|
|
//int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */
|
|
|
|
this.heap = new Uint16Array(2 * L_CODES + 1);
|
|
/* heap used to build the Huffman trees */
|
|
|
|
zero(this.heap);
|
|
this.heap_len = 0;
|
|
/* number of elements in the heap */
|
|
|
|
this.heap_max = 0;
|
|
/* element of largest frequency */
|
|
|
|
/* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
|
|
* The same heap array is used to build all trees.
|
|
*/
|
|
|
|
this.depth = new Uint16Array(2 * L_CODES + 1); //uch depth[2*L_CODES+1];
|
|
|
|
zero(this.depth);
|
|
/* Depth of each subtree used as tie breaker for trees of equal frequency
|
|
*/
|
|
|
|
this.l_buf = 0;
|
|
/* buffer index for literals or lengths */
|
|
|
|
this.lit_bufsize = 0;
|
|
/* Size of match buffer for literals/lengths. There are 4 reasons for
|
|
* limiting lit_bufsize to 64K:
|
|
* - frequencies can be kept in 16 bit counters
|
|
* - if compression is not successful for the first block, all input
|
|
* data is still in the window so we can still emit a stored block even
|
|
* when input comes from standard input. (This can also be done for
|
|
* all blocks if lit_bufsize is not greater than 32K.)
|
|
* - if compression is not successful for a file smaller than 64K, we can
|
|
* even emit a stored file instead of a stored block (saving 5 bytes).
|
|
* This is applicable only for zip (not gzip or zlib).
|
|
* - creating new Huffman trees less frequently may not provide fast
|
|
* adaptation to changes in the input data statistics. (Take for
|
|
* example a binary file with poorly compressible code followed by
|
|
* a highly compressible string table.) Smaller buffer sizes give
|
|
* fast adaptation but have of course the overhead of transmitting
|
|
* trees more frequently.
|
|
* - I can't count above 4
|
|
*/
|
|
|
|
this.last_lit = 0;
|
|
/* running index in l_buf */
|
|
|
|
this.d_buf = 0;
|
|
/* Buffer index for distances. To simplify the code, d_buf and l_buf have
|
|
* the same number of elements. To use different lengths, an extra flag
|
|
* array would be necessary.
|
|
*/
|
|
|
|
this.opt_len = 0;
|
|
/* bit length of current block with optimal trees */
|
|
|
|
this.static_len = 0;
|
|
/* bit length of current block with static trees */
|
|
|
|
this.matches = 0;
|
|
/* number of string matches in current block */
|
|
|
|
this.insert = 0;
|
|
/* bytes at end of window left to insert */
|
|
|
|
this.bi_buf = 0;
|
|
/* Output buffer. bits are inserted starting at the bottom (least
|
|
* significant bits).
|
|
*/
|
|
|
|
this.bi_valid = 0;
|
|
/* Number of valid bits in bi_buf. All bits above the last valid bit
|
|
* are always zero.
|
|
*/
|
|
// Used for window memory init. We safely ignore it for JS. That makes
|
|
// sense only for pointers and memory check tools.
|
|
//this.high_water = 0;
|
|
|
|
/* High water mark offset in window for initialized bytes -- bytes above
|
|
* this are set to zero in order to avoid memory check warnings when
|
|
* longest match routines access bytes past the input. This is then
|
|
* updated to the new high water mark.
|
|
*/
|
|
}
|
|
|
|
const deflateResetKeep = strm => {
|
|
if (!strm || !strm.state) {
|
|
return err(strm, Z_STREAM_ERROR$2);
|
|
}
|
|
|
|
strm.total_in = strm.total_out = 0;
|
|
strm.data_type = Z_UNKNOWN;
|
|
const s = strm.state;
|
|
s.pending = 0;
|
|
s.pending_out = 0;
|
|
|
|
if (s.wrap < 0) {
|
|
s.wrap = -s.wrap;
|
|
/* was made negative by deflate(..., Z_FINISH); */
|
|
}
|
|
|
|
s.status = s.wrap ? INIT_STATE : BUSY_STATE;
|
|
strm.adler = s.wrap === 2 ? 0 // crc32(0, Z_NULL, 0)
|
|
: 1; // adler32(0, Z_NULL, 0)
|
|
|
|
s.last_flush = Z_NO_FLUSH$2;
|
|
|
|
_tr_init(s);
|
|
|
|
return Z_OK$3;
|
|
};
|
|
|
|
const deflateReset = strm => {
|
|
const ret = deflateResetKeep(strm);
|
|
|
|
if (ret === Z_OK$3) {
|
|
lm_init(strm.state);
|
|
}
|
|
|
|
return ret;
|
|
};
|
|
|
|
const deflateSetHeader = (strm, head) => {
|
|
if (!strm || !strm.state) {
|
|
return Z_STREAM_ERROR$2;
|
|
}
|
|
|
|
if (strm.state.wrap !== 2) {
|
|
return Z_STREAM_ERROR$2;
|
|
}
|
|
|
|
strm.state.gzhead = head;
|
|
return Z_OK$3;
|
|
};
|
|
|
|
const deflateInit2 = (strm, level, method, windowBits, memLevel, strategy) => {
|
|
if (!strm) {
|
|
// === Z_NULL
|
|
return Z_STREAM_ERROR$2;
|
|
}
|
|
|
|
let wrap = 1;
|
|
|
|
if (level === Z_DEFAULT_COMPRESSION$1) {
|
|
level = 6;
|
|
}
|
|
|
|
if (windowBits < 0) {
|
|
/* suppress zlib wrapper */
|
|
wrap = 0;
|
|
windowBits = -windowBits;
|
|
} else if (windowBits > 15) {
|
|
wrap = 2;
|
|
/* write gzip wrapper instead */
|
|
|
|
windowBits -= 16;
|
|
}
|
|
|
|
if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED$2 || windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) {
|
|
return err(strm, Z_STREAM_ERROR$2);
|
|
}
|
|
|
|
if (windowBits === 8) {
|
|
windowBits = 9;
|
|
}
|
|
/* until 256-byte window bug fixed */
|
|
|
|
|
|
const s = new DeflateState();
|
|
strm.state = s;
|
|
s.strm = strm;
|
|
s.wrap = wrap;
|
|
s.gzhead = null;
|
|
s.w_bits = windowBits;
|
|
s.w_size = 1 << s.w_bits;
|
|
s.w_mask = s.w_size - 1;
|
|
s.hash_bits = memLevel + 7;
|
|
s.hash_size = 1 << s.hash_bits;
|
|
s.hash_mask = s.hash_size - 1;
|
|
s.hash_shift = ~~((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH);
|
|
s.window = new Uint8Array(s.w_size * 2);
|
|
s.head = new Uint16Array(s.hash_size);
|
|
s.prev = new Uint16Array(s.w_size); // Don't need mem init magic for JS.
|
|
//s.high_water = 0; /* nothing written to s->window yet */
|
|
|
|
s.lit_bufsize = 1 << memLevel + 6;
|
|
/* 16K elements by default */
|
|
|
|
s.pending_buf_size = s.lit_bufsize * 4; //overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
|
|
//s->pending_buf = (uchf *) overlay;
|
|
|
|
s.pending_buf = new Uint8Array(s.pending_buf_size); // It is offset from `s.pending_buf` (size is `s.lit_bufsize * 2`)
|
|
//s->d_buf = overlay + s->lit_bufsize/sizeof(ush);
|
|
|
|
s.d_buf = 1 * s.lit_bufsize; //s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
|
|
|
|
s.l_buf = (1 + 2) * s.lit_bufsize;
|
|
s.level = level;
|
|
s.strategy = strategy;
|
|
s.method = method;
|
|
return deflateReset(strm);
|
|
};
|
|
|
|
const deflateInit = (strm, level) => {
|
|
return deflateInit2(strm, level, Z_DEFLATED$2, MAX_WBITS$1, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY$1);
|
|
};
|
|
|
|
const deflate$2 = (strm, flush) => {
|
|
let beg, val; // for gzip header write only
|
|
|
|
if (!strm || !strm.state || flush > Z_BLOCK$1 || flush < 0) {
|
|
return strm ? err(strm, Z_STREAM_ERROR$2) : Z_STREAM_ERROR$2;
|
|
}
|
|
|
|
const s = strm.state;
|
|
|
|
if (!strm.output || !strm.input && strm.avail_in !== 0 || s.status === FINISH_STATE && flush !== Z_FINISH$3) {
|
|
return err(strm, strm.avail_out === 0 ? Z_BUF_ERROR$1 : Z_STREAM_ERROR$2);
|
|
}
|
|
|
|
s.strm = strm;
|
|
/* just in case */
|
|
|
|
const old_flush = s.last_flush;
|
|
s.last_flush = flush;
|
|
/* Write the header */
|
|
|
|
if (s.status === INIT_STATE) {
|
|
if (s.wrap === 2) {
|
|
// GZIP header
|
|
strm.adler = 0; //crc32(0L, Z_NULL, 0);
|
|
|
|
put_byte(s, 31);
|
|
put_byte(s, 139);
|
|
put_byte(s, 8);
|
|
|
|
if (!s.gzhead) {
|
|
// s->gzhead == Z_NULL
|
|
put_byte(s, 0);
|
|
put_byte(s, 0);
|
|
put_byte(s, 0);
|
|
put_byte(s, 0);
|
|
put_byte(s, 0);
|
|
put_byte(s, s.level === 9 ? 2 : s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? 4 : 0);
|
|
put_byte(s, OS_CODE);
|
|
s.status = BUSY_STATE;
|
|
} else {
|
|
put_byte(s, (s.gzhead.text ? 1 : 0) + (s.gzhead.hcrc ? 2 : 0) + (!s.gzhead.extra ? 0 : 4) + (!s.gzhead.name ? 0 : 8) + (!s.gzhead.comment ? 0 : 16));
|
|
put_byte(s, s.gzhead.time & 0xff);
|
|
put_byte(s, s.gzhead.time >> 8 & 0xff);
|
|
put_byte(s, s.gzhead.time >> 16 & 0xff);
|
|
put_byte(s, s.gzhead.time >> 24 & 0xff);
|
|
put_byte(s, s.level === 9 ? 2 : s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? 4 : 0);
|
|
put_byte(s, s.gzhead.os & 0xff);
|
|
|
|
if (s.gzhead.extra && s.gzhead.extra.length) {
|
|
put_byte(s, s.gzhead.extra.length & 0xff);
|
|
put_byte(s, s.gzhead.extra.length >> 8 & 0xff);
|
|
}
|
|
|
|
if (s.gzhead.hcrc) {
|
|
strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending, 0);
|
|
}
|
|
|
|
s.gzindex = 0;
|
|
s.status = EXTRA_STATE;
|
|
}
|
|
} else // DEFLATE header
|
|
{
|
|
let header = Z_DEFLATED$2 + (s.w_bits - 8 << 4) << 8;
|
|
let level_flags = -1;
|
|
|
|
if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) {
|
|
level_flags = 0;
|
|
} else if (s.level < 6) {
|
|
level_flags = 1;
|
|
} else if (s.level === 6) {
|
|
level_flags = 2;
|
|
} else {
|
|
level_flags = 3;
|
|
}
|
|
|
|
header |= level_flags << 6;
|
|
|
|
if (s.strstart !== 0) {
|
|
header |= PRESET_DICT;
|
|
}
|
|
|
|
header += 31 - header % 31;
|
|
s.status = BUSY_STATE;
|
|
putShortMSB(s, header);
|
|
/* Save the adler32 of the preset dictionary: */
|
|
|
|
if (s.strstart !== 0) {
|
|
putShortMSB(s, strm.adler >>> 16);
|
|
putShortMSB(s, strm.adler & 0xffff);
|
|
}
|
|
|
|
strm.adler = 1; // adler32(0L, Z_NULL, 0);
|
|
}
|
|
} //#ifdef GZIP
|
|
|
|
|
|
if (s.status === EXTRA_STATE) {
|
|
if (s.gzhead.extra
|
|
/* != Z_NULL*/
|
|
) {
|
|
beg = s.pending;
|
|
/* start of bytes to update crc */
|
|
|
|
while (s.gzindex < (s.gzhead.extra.length & 0xffff)) {
|
|
if (s.pending === s.pending_buf_size) {
|
|
if (s.gzhead.hcrc && s.pending > beg) {
|
|
strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);
|
|
}
|
|
|
|
flush_pending(strm);
|
|
beg = s.pending;
|
|
|
|
if (s.pending === s.pending_buf_size) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
put_byte(s, s.gzhead.extra[s.gzindex] & 0xff);
|
|
s.gzindex++;
|
|
}
|
|
|
|
if (s.gzhead.hcrc && s.pending > beg) {
|
|
strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);
|
|
}
|
|
|
|
if (s.gzindex === s.gzhead.extra.length) {
|
|
s.gzindex = 0;
|
|
s.status = NAME_STATE;
|
|
}
|
|
} else {
|
|
s.status = NAME_STATE;
|
|
}
|
|
}
|
|
|
|
if (s.status === NAME_STATE) {
|
|
if (s.gzhead.name
|
|
/* != Z_NULL*/
|
|
) {
|
|
beg = s.pending;
|
|
/* start of bytes to update crc */
|
|
//int val;
|
|
|
|
do {
|
|
if (s.pending === s.pending_buf_size) {
|
|
if (s.gzhead.hcrc && s.pending > beg) {
|
|
strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);
|
|
}
|
|
|
|
flush_pending(strm);
|
|
beg = s.pending;
|
|
|
|
if (s.pending === s.pending_buf_size) {
|
|
val = 1;
|
|
break;
|
|
}
|
|
} // JS specific: little magic to add zero terminator to end of string
|
|
|
|
|
|
if (s.gzindex < s.gzhead.name.length) {
|
|
val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff;
|
|
} else {
|
|
val = 0;
|
|
}
|
|
|
|
put_byte(s, val);
|
|
} while (val !== 0);
|
|
|
|
if (s.gzhead.hcrc && s.pending > beg) {
|
|
strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);
|
|
}
|
|
|
|
if (val === 0) {
|
|
s.gzindex = 0;
|
|
s.status = COMMENT_STATE;
|
|
}
|
|
} else {
|
|
s.status = COMMENT_STATE;
|
|
}
|
|
}
|
|
|
|
if (s.status === COMMENT_STATE) {
|
|
if (s.gzhead.comment
|
|
/* != Z_NULL*/
|
|
) {
|
|
beg = s.pending;
|
|
/* start of bytes to update crc */
|
|
//int val;
|
|
|
|
do {
|
|
if (s.pending === s.pending_buf_size) {
|
|
if (s.gzhead.hcrc && s.pending > beg) {
|
|
strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);
|
|
}
|
|
|
|
flush_pending(strm);
|
|
beg = s.pending;
|
|
|
|
if (s.pending === s.pending_buf_size) {
|
|
val = 1;
|
|
break;
|
|
}
|
|
} // JS specific: little magic to add zero terminator to end of string
|
|
|
|
|
|
if (s.gzindex < s.gzhead.comment.length) {
|
|
val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff;
|
|
} else {
|
|
val = 0;
|
|
}
|
|
|
|
put_byte(s, val);
|
|
} while (val !== 0);
|
|
|
|
if (s.gzhead.hcrc && s.pending > beg) {
|
|
strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);
|
|
}
|
|
|
|
if (val === 0) {
|
|
s.status = HCRC_STATE;
|
|
}
|
|
} else {
|
|
s.status = HCRC_STATE;
|
|
}
|
|
}
|
|
|
|
if (s.status === HCRC_STATE) {
|
|
if (s.gzhead.hcrc) {
|
|
if (s.pending + 2 > s.pending_buf_size) {
|
|
flush_pending(strm);
|
|
}
|
|
|
|
if (s.pending + 2 <= s.pending_buf_size) {
|
|
put_byte(s, strm.adler & 0xff);
|
|
put_byte(s, strm.adler >> 8 & 0xff);
|
|
strm.adler = 0; //crc32(0L, Z_NULL, 0);
|
|
|
|
s.status = BUSY_STATE;
|
|
}
|
|
} else {
|
|
s.status = BUSY_STATE;
|
|
}
|
|
} //#endif
|
|
|
|
/* Flush as much pending output as possible */
|
|
|
|
|
|
if (s.pending !== 0) {
|
|
flush_pending(strm);
|
|
|
|
if (strm.avail_out === 0) {
|
|
/* Since avail_out is 0, deflate will be called again with
|
|
* more output space, but possibly with both pending and
|
|
* avail_in equal to zero. There won't be anything to do,
|
|
* but this is not an error situation so make sure we
|
|
* return OK instead of BUF_ERROR at next call of deflate:
|
|
*/
|
|
s.last_flush = -1;
|
|
return Z_OK$3;
|
|
}
|
|
/* Make sure there is something to do and avoid duplicate consecutive
|
|
* flushes. For repeated and useless calls with Z_FINISH, we keep
|
|
* returning Z_STREAM_END instead of Z_BUF_ERROR.
|
|
*/
|
|
|
|
} else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) && flush !== Z_FINISH$3) {
|
|
return err(strm, Z_BUF_ERROR$1);
|
|
}
|
|
/* User must not provide more input after the first FINISH: */
|
|
|
|
|
|
if (s.status === FINISH_STATE && strm.avail_in !== 0) {
|
|
return err(strm, Z_BUF_ERROR$1);
|
|
}
|
|
/* Start a new block or continue the current one.
|
|
*/
|
|
|
|
|
|
if (strm.avail_in !== 0 || s.lookahead !== 0 || flush !== Z_NO_FLUSH$2 && s.status !== FINISH_STATE) {
|
|
let bstate = s.strategy === Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : s.strategy === Z_RLE ? deflate_rle(s, flush) : configuration_table[s.level].func(s, flush);
|
|
|
|
if (bstate === BS_FINISH_STARTED || bstate === BS_FINISH_DONE) {
|
|
s.status = FINISH_STATE;
|
|
}
|
|
|
|
if (bstate === BS_NEED_MORE || bstate === BS_FINISH_STARTED) {
|
|
if (strm.avail_out === 0) {
|
|
s.last_flush = -1;
|
|
/* avoid BUF_ERROR next call, see above */
|
|
}
|
|
|
|
return Z_OK$3;
|
|
/* If flush != Z_NO_FLUSH && avail_out == 0, the next call
|
|
* of deflate should use the same flush parameter to make sure
|
|
* that the flush is complete. So we don't have to output an
|
|
* empty block here, this will be done at next call. This also
|
|
* ensures that for a very small output buffer, we emit at most
|
|
* one empty block.
|
|
*/
|
|
}
|
|
|
|
if (bstate === BS_BLOCK_DONE) {
|
|
if (flush === Z_PARTIAL_FLUSH) {
|
|
_tr_align(s);
|
|
} else if (flush !== Z_BLOCK$1) {
|
|
/* FULL_FLUSH or SYNC_FLUSH */
|
|
_tr_stored_block(s, 0, 0, false);
|
|
/* For a full flush, this empty block will be recognized
|
|
* as a special marker by inflate_sync().
|
|
*/
|
|
|
|
|
|
if (flush === Z_FULL_FLUSH$1) {
|
|
/*** CLEAR_HASH(s); ***/
|
|
|
|
/* forget history */
|
|
zero(s.head); // Fill with NIL (= 0);
|
|
|
|
if (s.lookahead === 0) {
|
|
s.strstart = 0;
|
|
s.block_start = 0;
|
|
s.insert = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
flush_pending(strm);
|
|
|
|
if (strm.avail_out === 0) {
|
|
s.last_flush = -1;
|
|
/* avoid BUF_ERROR at next call, see above */
|
|
|
|
return Z_OK$3;
|
|
}
|
|
}
|
|
} //Assert(strm->avail_out > 0, "bug2");
|
|
//if (strm.avail_out <= 0) { throw new Error("bug2");}
|
|
|
|
|
|
if (flush !== Z_FINISH$3) {
|
|
return Z_OK$3;
|
|
}
|
|
|
|
if (s.wrap <= 0) {
|
|
return Z_STREAM_END$3;
|
|
}
|
|
/* Write the trailer */
|
|
|
|
|
|
if (s.wrap === 2) {
|
|
put_byte(s, strm.adler & 0xff);
|
|
put_byte(s, strm.adler >> 8 & 0xff);
|
|
put_byte(s, strm.adler >> 16 & 0xff);
|
|
put_byte(s, strm.adler >> 24 & 0xff);
|
|
put_byte(s, strm.total_in & 0xff);
|
|
put_byte(s, strm.total_in >> 8 & 0xff);
|
|
put_byte(s, strm.total_in >> 16 & 0xff);
|
|
put_byte(s, strm.total_in >> 24 & 0xff);
|
|
} else {
|
|
putShortMSB(s, strm.adler >>> 16);
|
|
putShortMSB(s, strm.adler & 0xffff);
|
|
}
|
|
|
|
flush_pending(strm);
|
|
/* If avail_out is zero, the application will call deflate again
|
|
* to flush the rest.
|
|
*/
|
|
|
|
if (s.wrap > 0) {
|
|
s.wrap = -s.wrap;
|
|
}
|
|
/* write the trailer only once! */
|
|
|
|
|
|
return s.pending !== 0 ? Z_OK$3 : Z_STREAM_END$3;
|
|
};
|
|
|
|
const deflateEnd = strm => {
|
|
if (!strm
|
|
/*== Z_NULL*/
|
|
|| !strm.state
|
|
/*== Z_NULL*/
|
|
) {
|
|
return Z_STREAM_ERROR$2;
|
|
}
|
|
|
|
const status = strm.state.status;
|
|
|
|
if (status !== INIT_STATE && status !== EXTRA_STATE && status !== NAME_STATE && status !== COMMENT_STATE && status !== HCRC_STATE && status !== BUSY_STATE && status !== FINISH_STATE) {
|
|
return err(strm, Z_STREAM_ERROR$2);
|
|
}
|
|
|
|
strm.state = null;
|
|
return status === BUSY_STATE ? err(strm, Z_DATA_ERROR$2) : Z_OK$3;
|
|
};
|
|
/* =========================================================================
|
|
* Initializes the compression dictionary from the given byte
|
|
* sequence without producing any compressed output.
|
|
*/
|
|
|
|
|
|
const deflateSetDictionary = (strm, dictionary) => {
|
|
let dictLength = dictionary.length;
|
|
|
|
if (!strm
|
|
/*== Z_NULL*/
|
|
|| !strm.state
|
|
/*== Z_NULL*/
|
|
) {
|
|
return Z_STREAM_ERROR$2;
|
|
}
|
|
|
|
const s = strm.state;
|
|
const wrap = s.wrap;
|
|
|
|
if (wrap === 2 || wrap === 1 && s.status !== INIT_STATE || s.lookahead) {
|
|
return Z_STREAM_ERROR$2;
|
|
}
|
|
/* when using zlib wrappers, compute Adler-32 for provided dictionary */
|
|
|
|
|
|
if (wrap === 1) {
|
|
/* adler32(strm->adler, dictionary, dictLength); */
|
|
strm.adler = adler32_1(strm.adler, dictionary, dictLength, 0);
|
|
}
|
|
|
|
s.wrap = 0;
|
|
/* avoid computing Adler-32 in read_buf */
|
|
|
|
/* if dictionary would fill window, just replace the history */
|
|
|
|
if (dictLength >= s.w_size) {
|
|
if (wrap === 0) {
|
|
/* already empty otherwise */
|
|
|
|
/*** CLEAR_HASH(s); ***/
|
|
zero(s.head); // Fill with NIL (= 0);
|
|
|
|
s.strstart = 0;
|
|
s.block_start = 0;
|
|
s.insert = 0;
|
|
}
|
|
/* use the tail */
|
|
// dictionary = dictionary.slice(dictLength - s.w_size);
|
|
|
|
|
|
let tmpDict = new Uint8Array(s.w_size);
|
|
tmpDict.set(dictionary.subarray(dictLength - s.w_size, dictLength), 0);
|
|
dictionary = tmpDict;
|
|
dictLength = s.w_size;
|
|
}
|
|
/* insert dictionary into window and hash */
|
|
|
|
|
|
const avail = strm.avail_in;
|
|
const next = strm.next_in;
|
|
const input = strm.input;
|
|
strm.avail_in = dictLength;
|
|
strm.next_in = 0;
|
|
strm.input = dictionary;
|
|
fill_window(s);
|
|
|
|
while (s.lookahead >= MIN_MATCH) {
|
|
let str = s.strstart;
|
|
let n = s.lookahead - (MIN_MATCH - 1);
|
|
|
|
do {
|
|
/* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */
|
|
s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH - 1]);
|
|
s.prev[str & s.w_mask] = s.head[s.ins_h];
|
|
s.head[s.ins_h] = str;
|
|
str++;
|
|
} while (--n);
|
|
|
|
s.strstart = str;
|
|
s.lookahead = MIN_MATCH - 1;
|
|
fill_window(s);
|
|
}
|
|
|
|
s.strstart += s.lookahead;
|
|
s.block_start = s.strstart;
|
|
s.insert = s.lookahead;
|
|
s.lookahead = 0;
|
|
s.match_length = s.prev_length = MIN_MATCH - 1;
|
|
s.match_available = 0;
|
|
strm.next_in = next;
|
|
strm.input = input;
|
|
strm.avail_in = avail;
|
|
s.wrap = wrap;
|
|
return Z_OK$3;
|
|
};
|
|
|
|
var deflateInit_1 = deflateInit;
|
|
var deflateInit2_1 = deflateInit2;
|
|
var deflateReset_1 = deflateReset;
|
|
var deflateResetKeep_1 = deflateResetKeep;
|
|
var deflateSetHeader_1 = deflateSetHeader;
|
|
var deflate_2$1 = deflate$2;
|
|
var deflateEnd_1 = deflateEnd;
|
|
var deflateSetDictionary_1 = deflateSetDictionary;
|
|
var deflateInfo = 'pako deflate (from Nodeca project)';
|
|
/* Not implemented
|
|
module.exports.deflateBound = deflateBound;
|
|
module.exports.deflateCopy = deflateCopy;
|
|
module.exports.deflateParams = deflateParams;
|
|
module.exports.deflatePending = deflatePending;
|
|
module.exports.deflatePrime = deflatePrime;
|
|
module.exports.deflateTune = deflateTune;
|
|
*/
|
|
|
|
var deflate_1$2 = {
|
|
deflateInit: deflateInit_1,
|
|
deflateInit2: deflateInit2_1,
|
|
deflateReset: deflateReset_1,
|
|
deflateResetKeep: deflateResetKeep_1,
|
|
deflateSetHeader: deflateSetHeader_1,
|
|
deflate: deflate_2$1,
|
|
deflateEnd: deflateEnd_1,
|
|
deflateSetDictionary: deflateSetDictionary_1,
|
|
deflateInfo: deflateInfo
|
|
};
|
|
|
|
const _has = (obj, key) => {
|
|
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
};
|
|
|
|
var assign = function (obj
|
|
/*from1, from2, from3, ...*/
|
|
) {
|
|
const sources = Array.prototype.slice.call(arguments, 1);
|
|
|
|
while (sources.length) {
|
|
const source = sources.shift();
|
|
|
|
if (!source) {
|
|
continue;
|
|
}
|
|
|
|
if (typeof source !== 'object') {
|
|
throw new TypeError(source + 'must be non-object');
|
|
}
|
|
|
|
for (const p in source) {
|
|
if (_has(source, p)) {
|
|
obj[p] = source[p];
|
|
}
|
|
}
|
|
}
|
|
|
|
return obj;
|
|
}; // Join array of chunks to single array.
|
|
|
|
|
|
var flattenChunks = chunks => {
|
|
// calculate data length
|
|
let len = 0;
|
|
|
|
for (let i = 0, l = chunks.length; i < l; i++) {
|
|
len += chunks[i].length;
|
|
} // join chunks
|
|
|
|
|
|
const result = new Uint8Array(len);
|
|
|
|
for (let i = 0, pos = 0, l = chunks.length; i < l; i++) {
|
|
let chunk = chunks[i];
|
|
result.set(chunk, pos);
|
|
pos += chunk.length;
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
var common = {
|
|
assign: assign,
|
|
flattenChunks: flattenChunks
|
|
}; // String encode/decode helpers
|
|
// Quick check if we can use fast array to bin string conversion
|
|
//
|
|
// - apply(Array) can fail on Android 2.2
|
|
// - apply(Uint8Array) can fail on iOS 5.1 Safari
|
|
//
|
|
|
|
let STR_APPLY_UIA_OK = true;
|
|
|
|
try {
|
|
String.fromCharCode.apply(null, new Uint8Array(1));
|
|
} catch (__) {
|
|
STR_APPLY_UIA_OK = false;
|
|
} // Table with utf8 lengths (calculated by first byte of sequence)
|
|
// Note, that 5 & 6-byte values and some 4-byte values can not be represented in JS,
|
|
// because max possible codepoint is 0x10ffff
|
|
|
|
|
|
const _utf8len = new Uint8Array(256);
|
|
|
|
for (let q = 0; q < 256; q++) {
|
|
_utf8len[q] = q >= 252 ? 6 : q >= 248 ? 5 : q >= 240 ? 4 : q >= 224 ? 3 : q >= 192 ? 2 : 1;
|
|
}
|
|
|
|
_utf8len[254] = _utf8len[254] = 1; // Invalid sequence start
|
|
// convert string to array (typed, when possible)
|
|
|
|
var string2buf = str => {
|
|
if (typeof TextEncoder === 'function' && TextEncoder.prototype.encode) {
|
|
return new TextEncoder().encode(str);
|
|
}
|
|
|
|
let buf,
|
|
c,
|
|
c2,
|
|
m_pos,
|
|
i,
|
|
str_len = str.length,
|
|
buf_len = 0; // count binary size
|
|
|
|
for (m_pos = 0; m_pos < str_len; m_pos++) {
|
|
c = str.charCodeAt(m_pos);
|
|
|
|
if ((c & 0xfc00) === 0xd800 && m_pos + 1 < str_len) {
|
|
c2 = str.charCodeAt(m_pos + 1);
|
|
|
|
if ((c2 & 0xfc00) === 0xdc00) {
|
|
c = 0x10000 + (c - 0xd800 << 10) + (c2 - 0xdc00);
|
|
m_pos++;
|
|
}
|
|
}
|
|
|
|
buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4;
|
|
} // allocate buffer
|
|
|
|
|
|
buf = new Uint8Array(buf_len); // convert
|
|
|
|
for (i = 0, m_pos = 0; i < buf_len; m_pos++) {
|
|
c = str.charCodeAt(m_pos);
|
|
|
|
if ((c & 0xfc00) === 0xd800 && m_pos + 1 < str_len) {
|
|
c2 = str.charCodeAt(m_pos + 1);
|
|
|
|
if ((c2 & 0xfc00) === 0xdc00) {
|
|
c = 0x10000 + (c - 0xd800 << 10) + (c2 - 0xdc00);
|
|
m_pos++;
|
|
}
|
|
}
|
|
|
|
if (c < 0x80) {
|
|
/* one byte */
|
|
buf[i++] = c;
|
|
} else if (c < 0x800) {
|
|
/* two bytes */
|
|
buf[i++] = 0xC0 | c >>> 6;
|
|
buf[i++] = 0x80 | c & 0x3f;
|
|
} else if (c < 0x10000) {
|
|
/* three bytes */
|
|
buf[i++] = 0xE0 | c >>> 12;
|
|
buf[i++] = 0x80 | c >>> 6 & 0x3f;
|
|
buf[i++] = 0x80 | c & 0x3f;
|
|
} else {
|
|
/* four bytes */
|
|
buf[i++] = 0xf0 | c >>> 18;
|
|
buf[i++] = 0x80 | c >>> 12 & 0x3f;
|
|
buf[i++] = 0x80 | c >>> 6 & 0x3f;
|
|
buf[i++] = 0x80 | c & 0x3f;
|
|
}
|
|
}
|
|
|
|
return buf;
|
|
}; // Helper
|
|
|
|
|
|
const buf2binstring = (buf, len) => {
|
|
// On Chrome, the arguments in a function call that are allowed is `65534`.
|
|
// If the length of the buffer is smaller than that, we can use this optimization,
|
|
// otherwise we will take a slower path.
|
|
if (len < 65534) {
|
|
if (buf.subarray && STR_APPLY_UIA_OK) {
|
|
return String.fromCharCode.apply(null, buf.length === len ? buf : buf.subarray(0, len));
|
|
}
|
|
}
|
|
|
|
let result = '';
|
|
|
|
for (let i = 0; i < len; i++) {
|
|
result += String.fromCharCode(buf[i]);
|
|
}
|
|
|
|
return result;
|
|
}; // convert array to string
|
|
|
|
|
|
var buf2string = (buf, max) => {
|
|
const len = max || buf.length;
|
|
|
|
if (typeof TextDecoder === 'function' && TextDecoder.prototype.decode) {
|
|
return new TextDecoder().decode(buf.subarray(0, max));
|
|
}
|
|
|
|
let i, out; // Reserve max possible length (2 words per char)
|
|
// NB: by unknown reasons, Array is significantly faster for
|
|
// String.fromCharCode.apply than Uint16Array.
|
|
|
|
const utf16buf = new Array(len * 2);
|
|
|
|
for (out = 0, i = 0; i < len;) {
|
|
let c = buf[i++]; // quick process ascii
|
|
|
|
if (c < 0x80) {
|
|
utf16buf[out++] = c;
|
|
continue;
|
|
}
|
|
|
|
let c_len = _utf8len[c]; // skip 5 & 6 byte codes
|
|
|
|
if (c_len > 4) {
|
|
utf16buf[out++] = 0xfffd;
|
|
i += c_len - 1;
|
|
continue;
|
|
} // apply mask on first byte
|
|
|
|
|
|
c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07; // join the rest
|
|
|
|
while (c_len > 1 && i < len) {
|
|
c = c << 6 | buf[i++] & 0x3f;
|
|
c_len--;
|
|
} // terminated by end of string?
|
|
|
|
|
|
if (c_len > 1) {
|
|
utf16buf[out++] = 0xfffd;
|
|
continue;
|
|
}
|
|
|
|
if (c < 0x10000) {
|
|
utf16buf[out++] = c;
|
|
} else {
|
|
c -= 0x10000;
|
|
utf16buf[out++] = 0xd800 | c >> 10 & 0x3ff;
|
|
utf16buf[out++] = 0xdc00 | c & 0x3ff;
|
|
}
|
|
}
|
|
|
|
return buf2binstring(utf16buf, out);
|
|
}; // Calculate max possible position in utf8 buffer,
|
|
// that will not break sequence. If that's not possible
|
|
// - (very small limits) return max size as is.
|
|
//
|
|
// buf[] - utf8 bytes array
|
|
// max - length limit (mandatory);
|
|
|
|
|
|
var utf8border = (buf, max) => {
|
|
max = max || buf.length;
|
|
|
|
if (max > buf.length) {
|
|
max = buf.length;
|
|
} // go back from last position, until start of sequence found
|
|
|
|
|
|
let pos = max - 1;
|
|
|
|
while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) {
|
|
pos--;
|
|
} // Very small and broken sequence,
|
|
// return max, because we should return something anyway.
|
|
|
|
|
|
if (pos < 0) {
|
|
return max;
|
|
} // If we came to start of buffer - that means buffer is too small,
|
|
// return max too.
|
|
|
|
|
|
if (pos === 0) {
|
|
return max;
|
|
}
|
|
|
|
return pos + _utf8len[buf[pos]] > max ? pos : max;
|
|
};
|
|
|
|
var strings = {
|
|
string2buf: string2buf,
|
|
buf2string: buf2string,
|
|
utf8border: utf8border
|
|
}; // (C) 1995-2013 Jean-loup Gailly and Mark Adler
|
|
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
|
|
//
|
|
// This software is provided 'as-is', without any express or implied
|
|
// warranty. In no event will the authors be held liable for any damages
|
|
// arising from the use of this software.
|
|
//
|
|
// Permission is granted to anyone to use this software for any purpose,
|
|
// including commercial applications, and to alter it and redistribute it
|
|
// freely, subject to the following restrictions:
|
|
//
|
|
// 1. The origin of this software must not be misrepresented; you must not
|
|
// claim that you wrote the original software. If you use this software
|
|
// in a product, an acknowledgment in the product documentation would be
|
|
// appreciated but is not required.
|
|
// 2. Altered source versions must be plainly marked as such, and must not be
|
|
// misrepresented as being the original software.
|
|
// 3. This notice may not be removed or altered from any source distribution.
|
|
|
|
function ZStream() {
|
|
/* next input byte */
|
|
this.input = null; // JS specific, because we have no pointers
|
|
|
|
this.next_in = 0;
|
|
/* number of bytes available at input */
|
|
|
|
this.avail_in = 0;
|
|
/* total number of input bytes read so far */
|
|
|
|
this.total_in = 0;
|
|
/* next output byte should be put there */
|
|
|
|
this.output = null; // JS specific, because we have no pointers
|
|
|
|
this.next_out = 0;
|
|
/* remaining free space at output */
|
|
|
|
this.avail_out = 0;
|
|
/* total number of bytes output so far */
|
|
|
|
this.total_out = 0;
|
|
/* last error message, NULL if no error */
|
|
|
|
this.msg = ''
|
|
/*Z_NULL*/
|
|
;
|
|
/* not visible by applications */
|
|
|
|
this.state = null;
|
|
/* best guess about the data type: binary or text */
|
|
|
|
this.data_type = 2
|
|
/*Z_UNKNOWN*/
|
|
;
|
|
/* adler32 value of the uncompressed data */
|
|
|
|
this.adler = 0;
|
|
}
|
|
|
|
var zstream = ZStream;
|
|
const toString$1 = Object.prototype.toString;
|
|
/* Public constants ==========================================================*/
|
|
|
|
/* ===========================================================================*/
|
|
|
|
const {
|
|
Z_NO_FLUSH: Z_NO_FLUSH$1,
|
|
Z_SYNC_FLUSH,
|
|
Z_FULL_FLUSH,
|
|
Z_FINISH: Z_FINISH$2,
|
|
Z_OK: Z_OK$2,
|
|
Z_STREAM_END: Z_STREAM_END$2,
|
|
Z_DEFAULT_COMPRESSION,
|
|
Z_DEFAULT_STRATEGY,
|
|
Z_DEFLATED: Z_DEFLATED$1
|
|
} = constants$2;
|
|
/* ===========================================================================*/
|
|
|
|
/**
|
|
* class Deflate
|
|
*
|
|
* Generic JS-style wrapper for zlib calls. If you don't need
|
|
* streaming behaviour - use more simple functions: [[deflate]],
|
|
* [[deflateRaw]] and [[gzip]].
|
|
**/
|
|
|
|
/* internal
|
|
* Deflate.chunks -> Array
|
|
*
|
|
* Chunks of output data, if [[Deflate#onData]] not overridden.
|
|
**/
|
|
|
|
/**
|
|
* Deflate.result -> Uint8Array
|
|
*
|
|
* Compressed result, generated by default [[Deflate#onData]]
|
|
* and [[Deflate#onEnd]] handlers. Filled after you push last chunk
|
|
* (call [[Deflate#push]] with `Z_FINISH` / `true` param).
|
|
**/
|
|
|
|
/**
|
|
* Deflate.err -> Number
|
|
*
|
|
* Error code after deflate finished. 0 (Z_OK) on success.
|
|
* You will not need it in real life, because deflate errors
|
|
* are possible only on wrong options or bad `onData` / `onEnd`
|
|
* custom handlers.
|
|
**/
|
|
|
|
/**
|
|
* Deflate.msg -> String
|
|
*
|
|
* Error message, if [[Deflate.err]] != 0
|
|
**/
|
|
|
|
/**
|
|
* new Deflate(options)
|
|
* - options (Object): zlib deflate options.
|
|
*
|
|
* Creates new deflator instance with specified params. Throws exception
|
|
* on bad params. Supported options:
|
|
*
|
|
* - `level`
|
|
* - `windowBits`
|
|
* - `memLevel`
|
|
* - `strategy`
|
|
* - `dictionary`
|
|
*
|
|
* [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
|
|
* for more information on these.
|
|
*
|
|
* Additional options, for internal needs:
|
|
*
|
|
* - `chunkSize` - size of generated data chunks (16K by default)
|
|
* - `raw` (Boolean) - do raw deflate
|
|
* - `gzip` (Boolean) - create gzip wrapper
|
|
* - `header` (Object) - custom header for gzip
|
|
* - `text` (Boolean) - true if compressed data believed to be text
|
|
* - `time` (Number) - modification time, unix timestamp
|
|
* - `os` (Number) - operation system code
|
|
* - `extra` (Array) - array of bytes with extra data (max 65536)
|
|
* - `name` (String) - file name (binary string)
|
|
* - `comment` (String) - comment (binary string)
|
|
* - `hcrc` (Boolean) - true if header crc should be added
|
|
*
|
|
* ##### Example:
|
|
*
|
|
* ```javascript
|
|
* const pako = require('pako')
|
|
* , chunk1 = new Uint8Array([1,2,3,4,5,6,7,8,9])
|
|
* , chunk2 = new Uint8Array([10,11,12,13,14,15,16,17,18,19]);
|
|
*
|
|
* const deflate = new pako.Deflate({ level: 3});
|
|
*
|
|
* deflate.push(chunk1, false);
|
|
* deflate.push(chunk2, true); // true -> last chunk
|
|
*
|
|
* if (deflate.err) { throw new Error(deflate.err); }
|
|
*
|
|
* console.log(deflate.result);
|
|
* ```
|
|
**/
|
|
|
|
function Deflate$1(options) {
|
|
this.options = common.assign({
|
|
level: Z_DEFAULT_COMPRESSION,
|
|
method: Z_DEFLATED$1,
|
|
chunkSize: 16384,
|
|
windowBits: 15,
|
|
memLevel: 8,
|
|
strategy: Z_DEFAULT_STRATEGY
|
|
}, options || {});
|
|
let opt = this.options;
|
|
|
|
if (opt.raw && opt.windowBits > 0) {
|
|
opt.windowBits = -opt.windowBits;
|
|
} else if (opt.gzip && opt.windowBits > 0 && opt.windowBits < 16) {
|
|
opt.windowBits += 16;
|
|
}
|
|
|
|
this.err = 0; // error code, if happens (0 = Z_OK)
|
|
|
|
this.msg = ''; // error message
|
|
|
|
this.ended = false; // used to avoid multiple onEnd() calls
|
|
|
|
this.chunks = []; // chunks of compressed data
|
|
|
|
this.strm = new zstream();
|
|
this.strm.avail_out = 0;
|
|
let status = deflate_1$2.deflateInit2(this.strm, opt.level, opt.method, opt.windowBits, opt.memLevel, opt.strategy);
|
|
|
|
if (status !== Z_OK$2) {
|
|
throw new Error(messages[status]);
|
|
}
|
|
|
|
if (opt.header) {
|
|
deflate_1$2.deflateSetHeader(this.strm, opt.header);
|
|
}
|
|
|
|
if (opt.dictionary) {
|
|
let dict; // Convert data if needed
|
|
|
|
if (typeof opt.dictionary === 'string') {
|
|
// If we need to compress text, change encoding to utf8.
|
|
dict = strings.string2buf(opt.dictionary);
|
|
} else if (toString$1.call(opt.dictionary) === '[object ArrayBuffer]') {
|
|
dict = new Uint8Array(opt.dictionary);
|
|
} else {
|
|
dict = opt.dictionary;
|
|
}
|
|
|
|
status = deflate_1$2.deflateSetDictionary(this.strm, dict);
|
|
|
|
if (status !== Z_OK$2) {
|
|
throw new Error(messages[status]);
|
|
}
|
|
|
|
this._dict_set = true;
|
|
}
|
|
}
|
|
/**
|
|
* Deflate#push(data[, flush_mode]) -> Boolean
|
|
* - data (Uint8Array|ArrayBuffer|String): input data. Strings will be
|
|
* converted to utf8 byte sequence.
|
|
* - flush_mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes.
|
|
* See constants. Skipped or `false` means Z_NO_FLUSH, `true` means Z_FINISH.
|
|
*
|
|
* Sends input data to deflate pipe, generating [[Deflate#onData]] calls with
|
|
* new compressed chunks. Returns `true` on success. The last data block must
|
|
* have `flush_mode` Z_FINISH (or `true`). That will flush internal pending
|
|
* buffers and call [[Deflate#onEnd]].
|
|
*
|
|
* On fail call [[Deflate#onEnd]] with error code and return false.
|
|
*
|
|
* ##### Example
|
|
*
|
|
* ```javascript
|
|
* push(chunk, false); // push one of data chunks
|
|
* ...
|
|
* push(chunk, true); // push last chunk
|
|
* ```
|
|
**/
|
|
|
|
|
|
Deflate$1.prototype.push = function (data, flush_mode) {
|
|
const strm = this.strm;
|
|
const chunkSize = this.options.chunkSize;
|
|
|
|
let status, _flush_mode;
|
|
|
|
if (this.ended) {
|
|
return false;
|
|
}
|
|
|
|
if (flush_mode === ~~flush_mode) _flush_mode = flush_mode;else _flush_mode = flush_mode === true ? Z_FINISH$2 : Z_NO_FLUSH$1; // Convert data if needed
|
|
|
|
if (typeof data === 'string') {
|
|
// If we need to compress text, change encoding to utf8.
|
|
strm.input = strings.string2buf(data);
|
|
} else if (toString$1.call(data) === '[object ArrayBuffer]') {
|
|
strm.input = new Uint8Array(data);
|
|
} else {
|
|
strm.input = data;
|
|
}
|
|
|
|
strm.next_in = 0;
|
|
strm.avail_in = strm.input.length;
|
|
|
|
for (;;) {
|
|
if (strm.avail_out === 0) {
|
|
strm.output = new Uint8Array(chunkSize);
|
|
strm.next_out = 0;
|
|
strm.avail_out = chunkSize;
|
|
} // Make sure avail_out > 6 to avoid repeating markers
|
|
|
|
|
|
if ((_flush_mode === Z_SYNC_FLUSH || _flush_mode === Z_FULL_FLUSH) && strm.avail_out <= 6) {
|
|
this.onData(strm.output.subarray(0, strm.next_out));
|
|
strm.avail_out = 0;
|
|
continue;
|
|
}
|
|
|
|
status = deflate_1$2.deflate(strm, _flush_mode); // Ended => flush and finish
|
|
|
|
if (status === Z_STREAM_END$2) {
|
|
if (strm.next_out > 0) {
|
|
this.onData(strm.output.subarray(0, strm.next_out));
|
|
}
|
|
|
|
status = deflate_1$2.deflateEnd(this.strm);
|
|
this.onEnd(status);
|
|
this.ended = true;
|
|
return status === Z_OK$2;
|
|
} // Flush if out buffer full
|
|
|
|
|
|
if (strm.avail_out === 0) {
|
|
this.onData(strm.output);
|
|
continue;
|
|
} // Flush if requested and has data
|
|
|
|
|
|
if (_flush_mode > 0 && strm.next_out > 0) {
|
|
this.onData(strm.output.subarray(0, strm.next_out));
|
|
strm.avail_out = 0;
|
|
continue;
|
|
}
|
|
|
|
if (strm.avail_in === 0) break;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
/**
|
|
* Deflate#onData(chunk) -> Void
|
|
* - chunk (Uint8Array): output data.
|
|
*
|
|
* By default, stores data blocks in `chunks[]` property and glue
|
|
* those in `onEnd`. Override this handler, if you need another behaviour.
|
|
**/
|
|
|
|
|
|
Deflate$1.prototype.onData = function (chunk) {
|
|
this.chunks.push(chunk);
|
|
};
|
|
/**
|
|
* Deflate#onEnd(status) -> Void
|
|
* - status (Number): deflate status. 0 (Z_OK) on success,
|
|
* other if not.
|
|
*
|
|
* Called once after you tell deflate that the input stream is
|
|
* complete (Z_FINISH). By default - join collected chunks,
|
|
* free memory and fill `results` / `err` properties.
|
|
**/
|
|
|
|
|
|
Deflate$1.prototype.onEnd = function (status) {
|
|
// On success - join
|
|
if (status === Z_OK$2) {
|
|
this.result = common.flattenChunks(this.chunks);
|
|
}
|
|
|
|
this.chunks = [];
|
|
this.err = status;
|
|
this.msg = this.strm.msg;
|
|
};
|
|
/**
|
|
* deflate(data[, options]) -> Uint8Array
|
|
* - data (Uint8Array|String): input data to compress.
|
|
* - options (Object): zlib deflate options.
|
|
*
|
|
* Compress `data` with deflate algorithm and `options`.
|
|
*
|
|
* Supported options are:
|
|
*
|
|
* - level
|
|
* - windowBits
|
|
* - memLevel
|
|
* - strategy
|
|
* - dictionary
|
|
*
|
|
* [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
|
|
* for more information on these.
|
|
*
|
|
* Sugar (options):
|
|
*
|
|
* - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify
|
|
* negative windowBits implicitly.
|
|
*
|
|
* ##### Example:
|
|
*
|
|
* ```javascript
|
|
* const pako = require('pako')
|
|
* const data = new Uint8Array([1,2,3,4,5,6,7,8,9]);
|
|
*
|
|
* console.log(pako.deflate(data));
|
|
* ```
|
|
**/
|
|
|
|
|
|
function deflate$1(input, options) {
|
|
const deflator = new Deflate$1(options);
|
|
deflator.push(input, true); // That will never happens, if you don't cheat with options :)
|
|
|
|
if (deflator.err) {
|
|
throw deflator.msg || messages[deflator.err];
|
|
}
|
|
|
|
return deflator.result;
|
|
}
|
|
/**
|
|
* deflateRaw(data[, options]) -> Uint8Array
|
|
* - data (Uint8Array|String): input data to compress.
|
|
* - options (Object): zlib deflate options.
|
|
*
|
|
* The same as [[deflate]], but creates raw data, without wrapper
|
|
* (header and adler32 crc).
|
|
**/
|
|
|
|
|
|
function deflateRaw$1(input, options) {
|
|
options = options || {};
|
|
options.raw = true;
|
|
return deflate$1(input, options);
|
|
}
|
|
/**
|
|
* gzip(data[, options]) -> Uint8Array
|
|
* - data (Uint8Array|String): input data to compress.
|
|
* - options (Object): zlib deflate options.
|
|
*
|
|
* The same as [[deflate]], but create gzip wrapper instead of
|
|
* deflate one.
|
|
**/
|
|
|
|
|
|
function gzip$1(input, options) {
|
|
options = options || {};
|
|
options.gzip = true;
|
|
return deflate$1(input, options);
|
|
}
|
|
|
|
var Deflate_1$1 = Deflate$1;
|
|
var deflate_2 = deflate$1;
|
|
var deflateRaw_1$1 = deflateRaw$1;
|
|
var gzip_1$1 = gzip$1;
|
|
var constants$1 = constants$2;
|
|
var deflate_1$1 = {
|
|
Deflate: Deflate_1$1,
|
|
deflate: deflate_2,
|
|
deflateRaw: deflateRaw_1$1,
|
|
gzip: gzip_1$1,
|
|
constants: constants$1
|
|
}; // (C) 1995-2013 Jean-loup Gailly and Mark Adler
|
|
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
|
|
//
|
|
// This software is provided 'as-is', without any express or implied
|
|
// warranty. In no event will the authors be held liable for any damages
|
|
// arising from the use of this software.
|
|
//
|
|
// Permission is granted to anyone to use this software for any purpose,
|
|
// including commercial applications, and to alter it and redistribute it
|
|
// freely, subject to the following restrictions:
|
|
//
|
|
// 1. The origin of this software must not be misrepresented; you must not
|
|
// claim that you wrote the original software. If you use this software
|
|
// in a product, an acknowledgment in the product documentation would be
|
|
// appreciated but is not required.
|
|
// 2. Altered source versions must be plainly marked as such, and must not be
|
|
// misrepresented as being the original software.
|
|
// 3. This notice may not be removed or altered from any source distribution.
|
|
// See state defs from inflate.js
|
|
|
|
const BAD$1 = 30;
|
|
/* got a data error -- remain here until reset */
|
|
|
|
const TYPE$1 = 12;
|
|
/* i: waiting for type bits, including last-flag bit */
|
|
|
|
/*
|
|
Decode literal, length, and distance codes and write out the resulting
|
|
literal and match bytes until either not enough input or output is
|
|
available, an end-of-block is encountered, or a data error is encountered.
|
|
When large enough input and output buffers are supplied to inflate(), for
|
|
example, a 16K input buffer and a 64K output buffer, more than 95% of the
|
|
inflate execution time is spent in this routine.
|
|
|
|
Entry assumptions:
|
|
|
|
state.mode === LEN
|
|
strm.avail_in >= 6
|
|
strm.avail_out >= 258
|
|
start >= strm.avail_out
|
|
state.bits < 8
|
|
|
|
On return, state.mode is one of:
|
|
|
|
LEN -- ran out of enough output space or enough available input
|
|
TYPE -- reached end of block code, inflate() to interpret next block
|
|
BAD -- error in block data
|
|
|
|
Notes:
|
|
|
|
- The maximum input bits used by a length/distance pair is 15 bits for the
|
|
length code, 5 bits for the length extra, 15 bits for the distance code,
|
|
and 13 bits for the distance extra. This totals 48 bits, or six bytes.
|
|
Therefore if strm.avail_in >= 6, then there is enough input to avoid
|
|
checking for available input while decoding.
|
|
|
|
- The maximum bytes that a single length/distance pair can output is 258
|
|
bytes, which is the maximum length that can be coded. inflate_fast()
|
|
requires strm.avail_out >= 258 for each loop to avoid checking for
|
|
output space.
|
|
*/
|
|
|
|
var inffast = function inflate_fast(strm, start) {
|
|
let _in;
|
|
/* local strm.input */
|
|
|
|
|
|
let last;
|
|
/* have enough input while in < last */
|
|
|
|
let _out;
|
|
/* local strm.output */
|
|
|
|
|
|
let beg;
|
|
/* inflate()'s initial strm.output */
|
|
|
|
let end;
|
|
/* while out < end, enough space available */
|
|
//#ifdef INFLATE_STRICT
|
|
|
|
let dmax;
|
|
/* maximum distance from zlib header */
|
|
//#endif
|
|
|
|
let wsize;
|
|
/* window size or zero if not using window */
|
|
|
|
let whave;
|
|
/* valid bytes in the window */
|
|
|
|
let wnext;
|
|
/* window write index */
|
|
// Use `s_window` instead `window`, avoid conflict with instrumentation tools
|
|
|
|
let s_window;
|
|
/* allocated sliding window, if wsize != 0 */
|
|
|
|
let hold;
|
|
/* local strm.hold */
|
|
|
|
let bits;
|
|
/* local strm.bits */
|
|
|
|
let lcode;
|
|
/* local strm.lencode */
|
|
|
|
let dcode;
|
|
/* local strm.distcode */
|
|
|
|
let lmask;
|
|
/* mask for first level of length codes */
|
|
|
|
let dmask;
|
|
/* mask for first level of distance codes */
|
|
|
|
let here;
|
|
/* retrieved table entry */
|
|
|
|
let op;
|
|
/* code bits, operation, extra bits, or */
|
|
|
|
/* window position, window bytes to copy */
|
|
|
|
let len;
|
|
/* match length, unused bytes */
|
|
|
|
let dist;
|
|
/* match distance */
|
|
|
|
let from;
|
|
/* where to copy match from */
|
|
|
|
let from_source;
|
|
let input, output; // JS specific, because we have no pointers
|
|
|
|
/* copy state to local variables */
|
|
|
|
const state = strm.state; //here = state.here;
|
|
|
|
_in = strm.next_in;
|
|
input = strm.input;
|
|
last = _in + (strm.avail_in - 5);
|
|
_out = strm.next_out;
|
|
output = strm.output;
|
|
beg = _out - (start - strm.avail_out);
|
|
end = _out + (strm.avail_out - 257); //#ifdef INFLATE_STRICT
|
|
|
|
dmax = state.dmax; //#endif
|
|
|
|
wsize = state.wsize;
|
|
whave = state.whave;
|
|
wnext = state.wnext;
|
|
s_window = state.window;
|
|
hold = state.hold;
|
|
bits = state.bits;
|
|
lcode = state.lencode;
|
|
dcode = state.distcode;
|
|
lmask = (1 << state.lenbits) - 1;
|
|
dmask = (1 << state.distbits) - 1;
|
|
/* decode literals and length/distances until end-of-block or not enough
|
|
input data or output space */
|
|
|
|
top: do {
|
|
if (bits < 15) {
|
|
hold += input[_in++] << bits;
|
|
bits += 8;
|
|
hold += input[_in++] << bits;
|
|
bits += 8;
|
|
}
|
|
|
|
here = lcode[hold & lmask];
|
|
|
|
dolen: for (;;) {
|
|
// Goto emulation
|
|
op = here >>> 24
|
|
/*here.bits*/
|
|
;
|
|
hold >>>= op;
|
|
bits -= op;
|
|
op = here >>> 16 & 0xff
|
|
/*here.op*/
|
|
;
|
|
|
|
if (op === 0) {
|
|
/* literal */
|
|
//Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
|
|
// "inflate: literal '%c'\n" :
|
|
// "inflate: literal 0x%02x\n", here.val));
|
|
output[_out++] = here & 0xffff
|
|
/*here.val*/
|
|
;
|
|
} else if (op & 16) {
|
|
/* length base */
|
|
len = here & 0xffff
|
|
/*here.val*/
|
|
;
|
|
op &= 15;
|
|
/* number of extra bits */
|
|
|
|
if (op) {
|
|
if (bits < op) {
|
|
hold += input[_in++] << bits;
|
|
bits += 8;
|
|
}
|
|
|
|
len += hold & (1 << op) - 1;
|
|
hold >>>= op;
|
|
bits -= op;
|
|
} //Tracevv((stderr, "inflate: length %u\n", len));
|
|
|
|
|
|
if (bits < 15) {
|
|
hold += input[_in++] << bits;
|
|
bits += 8;
|
|
hold += input[_in++] << bits;
|
|
bits += 8;
|
|
}
|
|
|
|
here = dcode[hold & dmask];
|
|
|
|
dodist: for (;;) {
|
|
// goto emulation
|
|
op = here >>> 24
|
|
/*here.bits*/
|
|
;
|
|
hold >>>= op;
|
|
bits -= op;
|
|
op = here >>> 16 & 0xff
|
|
/*here.op*/
|
|
;
|
|
|
|
if (op & 16) {
|
|
/* distance base */
|
|
dist = here & 0xffff
|
|
/*here.val*/
|
|
;
|
|
op &= 15;
|
|
/* number of extra bits */
|
|
|
|
if (bits < op) {
|
|
hold += input[_in++] << bits;
|
|
bits += 8;
|
|
|
|
if (bits < op) {
|
|
hold += input[_in++] << bits;
|
|
bits += 8;
|
|
}
|
|
}
|
|
|
|
dist += hold & (1 << op) - 1; //#ifdef INFLATE_STRICT
|
|
|
|
if (dist > dmax) {
|
|
strm.msg = 'invalid distance too far back';
|
|
state.mode = BAD$1;
|
|
break top;
|
|
} //#endif
|
|
|
|
|
|
hold >>>= op;
|
|
bits -= op; //Tracevv((stderr, "inflate: distance %u\n", dist));
|
|
|
|
op = _out - beg;
|
|
/* max distance in output */
|
|
|
|
if (dist > op) {
|
|
/* see if copy from window */
|
|
op = dist - op;
|
|
/* distance back in window */
|
|
|
|
if (op > whave) {
|
|
if (state.sane) {
|
|
strm.msg = 'invalid distance too far back';
|
|
state.mode = BAD$1;
|
|
break top;
|
|
} // (!) This block is disabled in zlib defaults,
|
|
// don't enable it for binary compatibility
|
|
//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
|
|
// if (len <= op - whave) {
|
|
// do {
|
|
// output[_out++] = 0;
|
|
// } while (--len);
|
|
// continue top;
|
|
// }
|
|
// len -= op - whave;
|
|
// do {
|
|
// output[_out++] = 0;
|
|
// } while (--op > whave);
|
|
// if (op === 0) {
|
|
// from = _out - dist;
|
|
// do {
|
|
// output[_out++] = output[from++];
|
|
// } while (--len);
|
|
// continue top;
|
|
// }
|
|
//#endif
|
|
|
|
}
|
|
|
|
from = 0; // window index
|
|
|
|
from_source = s_window;
|
|
|
|
if (wnext === 0) {
|
|
/* very common case */
|
|
from += wsize - op;
|
|
|
|
if (op < len) {
|
|
/* some from window */
|
|
len -= op;
|
|
|
|
do {
|
|
output[_out++] = s_window[from++];
|
|
} while (--op);
|
|
|
|
from = _out - dist;
|
|
/* rest from output */
|
|
|
|
from_source = output;
|
|
}
|
|
} else if (wnext < op) {
|
|
/* wrap around window */
|
|
from += wsize + wnext - op;
|
|
op -= wnext;
|
|
|
|
if (op < len) {
|
|
/* some from end of window */
|
|
len -= op;
|
|
|
|
do {
|
|
output[_out++] = s_window[from++];
|
|
} while (--op);
|
|
|
|
from = 0;
|
|
|
|
if (wnext < len) {
|
|
/* some from start of window */
|
|
op = wnext;
|
|
len -= op;
|
|
|
|
do {
|
|
output[_out++] = s_window[from++];
|
|
} while (--op);
|
|
|
|
from = _out - dist;
|
|
/* rest from output */
|
|
|
|
from_source = output;
|
|
}
|
|
}
|
|
} else {
|
|
/* contiguous in window */
|
|
from += wnext - op;
|
|
|
|
if (op < len) {
|
|
/* some from window */
|
|
len -= op;
|
|
|
|
do {
|
|
output[_out++] = s_window[from++];
|
|
} while (--op);
|
|
|
|
from = _out - dist;
|
|
/* rest from output */
|
|
|
|
from_source = output;
|
|
}
|
|
}
|
|
|
|
while (len > 2) {
|
|
output[_out++] = from_source[from++];
|
|
output[_out++] = from_source[from++];
|
|
output[_out++] = from_source[from++];
|
|
len -= 3;
|
|
}
|
|
|
|
if (len) {
|
|
output[_out++] = from_source[from++];
|
|
|
|
if (len > 1) {
|
|
output[_out++] = from_source[from++];
|
|
}
|
|
}
|
|
} else {
|
|
from = _out - dist;
|
|
/* copy direct from output */
|
|
|
|
do {
|
|
/* minimum length is three */
|
|
output[_out++] = output[from++];
|
|
output[_out++] = output[from++];
|
|
output[_out++] = output[from++];
|
|
len -= 3;
|
|
} while (len > 2);
|
|
|
|
if (len) {
|
|
output[_out++] = output[from++];
|
|
|
|
if (len > 1) {
|
|
output[_out++] = output[from++];
|
|
}
|
|
}
|
|
}
|
|
} else if ((op & 64) === 0) {
|
|
/* 2nd level distance code */
|
|
here = dcode[(here & 0xffff
|
|
/*here.val*/
|
|
) + (hold & (1 << op) - 1)];
|
|
continue dodist;
|
|
} else {
|
|
strm.msg = 'invalid distance code';
|
|
state.mode = BAD$1;
|
|
break top;
|
|
}
|
|
|
|
break; // need to emulate goto via "continue"
|
|
}
|
|
} else if ((op & 64) === 0) {
|
|
/* 2nd level length code */
|
|
here = lcode[(here & 0xffff
|
|
/*here.val*/
|
|
) + (hold & (1 << op) - 1)];
|
|
continue dolen;
|
|
} else if (op & 32) {
|
|
/* end-of-block */
|
|
//Tracevv((stderr, "inflate: end of block\n"));
|
|
state.mode = TYPE$1;
|
|
break top;
|
|
} else {
|
|
strm.msg = 'invalid literal/length code';
|
|
state.mode = BAD$1;
|
|
break top;
|
|
}
|
|
|
|
break; // need to emulate goto via "continue"
|
|
}
|
|
} while (_in < last && _out < end);
|
|
/* return unused bytes (on entry, bits < 8, so in won't go too far back) */
|
|
|
|
|
|
len = bits >> 3;
|
|
_in -= len;
|
|
bits -= len << 3;
|
|
hold &= (1 << bits) - 1;
|
|
/* update state and return */
|
|
|
|
strm.next_in = _in;
|
|
strm.next_out = _out;
|
|
strm.avail_in = _in < last ? 5 + (last - _in) : 5 - (_in - last);
|
|
strm.avail_out = _out < end ? 257 + (end - _out) : 257 - (_out - end);
|
|
state.hold = hold;
|
|
state.bits = bits;
|
|
return;
|
|
}; // (C) 1995-2013 Jean-loup Gailly and Mark Adler
|
|
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
|
|
//
|
|
// This software is provided 'as-is', without any express or implied
|
|
// warranty. In no event will the authors be held liable for any damages
|
|
// arising from the use of this software.
|
|
//
|
|
// Permission is granted to anyone to use this software for any purpose,
|
|
// including commercial applications, and to alter it and redistribute it
|
|
// freely, subject to the following restrictions:
|
|
//
|
|
// 1. The origin of this software must not be misrepresented; you must not
|
|
// claim that you wrote the original software. If you use this software
|
|
// in a product, an acknowledgment in the product documentation would be
|
|
// appreciated but is not required.
|
|
// 2. Altered source versions must be plainly marked as such, and must not be
|
|
// misrepresented as being the original software.
|
|
// 3. This notice may not be removed or altered from any source distribution.
|
|
|
|
|
|
const MAXBITS = 15;
|
|
const ENOUGH_LENS$1 = 852;
|
|
const ENOUGH_DISTS$1 = 592; //const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);
|
|
|
|
const CODES$1 = 0;
|
|
const LENS$1 = 1;
|
|
const DISTS$1 = 2;
|
|
const lbase = new Uint16Array([
|
|
/* Length codes 257..285 base */
|
|
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0]);
|
|
const lext = new Uint8Array([
|
|
/* Length codes 257..285 extra */
|
|
16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78]);
|
|
const dbase = new Uint16Array([
|
|
/* Distance codes 0..29 base */
|
|
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0]);
|
|
const dext = new Uint8Array([
|
|
/* Distance codes 0..29 extra */
|
|
16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 64, 64]);
|
|
|
|
const inflate_table = (type, lens, lens_index, codes, table, table_index, work, opts) => {
|
|
const bits = opts.bits; //here = opts.here; /* table entry for duplication */
|
|
|
|
let len = 0;
|
|
/* a code's length in bits */
|
|
|
|
let sym = 0;
|
|
/* index of code symbols */
|
|
|
|
let min = 0,
|
|
max = 0;
|
|
/* minimum and maximum code lengths */
|
|
|
|
let root = 0;
|
|
/* number of index bits for root table */
|
|
|
|
let curr = 0;
|
|
/* number of index bits for current table */
|
|
|
|
let drop = 0;
|
|
/* code bits to drop for sub-table */
|
|
|
|
let left = 0;
|
|
/* number of prefix codes available */
|
|
|
|
let used = 0;
|
|
/* code entries in table used */
|
|
|
|
let huff = 0;
|
|
/* Huffman code */
|
|
|
|
let incr;
|
|
/* for incrementing code, index */
|
|
|
|
let fill;
|
|
/* index for replicating entries */
|
|
|
|
let low;
|
|
/* low bits for current root entry */
|
|
|
|
let mask;
|
|
/* mask for low root bits */
|
|
|
|
let next;
|
|
/* next available space in table */
|
|
|
|
let base = null;
|
|
/* base value table to use */
|
|
|
|
let base_index = 0; // let shoextra; /* extra bits table to use */
|
|
|
|
let end;
|
|
/* use base and extra for symbol > end */
|
|
|
|
const count = new Uint16Array(MAXBITS + 1); //[MAXBITS+1]; /* number of codes of each length */
|
|
|
|
const offs = new Uint16Array(MAXBITS + 1); //[MAXBITS+1]; /* offsets in table for each length */
|
|
|
|
let extra = null;
|
|
let extra_index = 0;
|
|
let here_bits, here_op, here_val;
|
|
/*
|
|
Process a set of code lengths to create a canonical Huffman code. The
|
|
code lengths are lens[0..codes-1]. Each length corresponds to the
|
|
symbols 0..codes-1. The Huffman code is generated by first sorting the
|
|
symbols by length from short to long, and retaining the symbol order
|
|
for codes with equal lengths. Then the code starts with all zero bits
|
|
for the first code of the shortest length, and the codes are integer
|
|
increments for the same length, and zeros are appended as the length
|
|
increases. For the deflate format, these bits are stored backwards
|
|
from their more natural integer increment ordering, and so when the
|
|
decoding tables are built in the large loop below, the integer codes
|
|
are incremented backwards.
|
|
This routine assumes, but does not check, that all of the entries in
|
|
lens[] are in the range 0..MAXBITS. The caller must assure this.
|
|
1..MAXBITS is interpreted as that code length. zero means that that
|
|
symbol does not occur in this code.
|
|
The codes are sorted by computing a count of codes for each length,
|
|
creating from that a table of starting indices for each length in the
|
|
sorted table, and then entering the symbols in order in the sorted
|
|
table. The sorted table is work[], with that space being provided by
|
|
the caller.
|
|
The length counts are used for other purposes as well, i.e. finding
|
|
the minimum and maximum length codes, determining if there are any
|
|
codes at all, checking for a valid set of lengths, and looking ahead
|
|
at length counts to determine sub-table sizes when building the
|
|
decoding tables.
|
|
*/
|
|
|
|
/* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
|
|
|
|
for (len = 0; len <= MAXBITS; len++) {
|
|
count[len] = 0;
|
|
}
|
|
|
|
for (sym = 0; sym < codes; sym++) {
|
|
count[lens[lens_index + sym]]++;
|
|
}
|
|
/* bound code lengths, force root to be within code lengths */
|
|
|
|
|
|
root = bits;
|
|
|
|
for (max = MAXBITS; max >= 1; max--) {
|
|
if (count[max] !== 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (root > max) {
|
|
root = max;
|
|
}
|
|
|
|
if (max === 0) {
|
|
/* no symbols to code at all */
|
|
//table.op[opts.table_index] = 64; //here.op = (var char)64; /* invalid code marker */
|
|
//table.bits[opts.table_index] = 1; //here.bits = (var char)1;
|
|
//table.val[opts.table_index++] = 0; //here.val = (var short)0;
|
|
table[table_index++] = 1 << 24 | 64 << 16 | 0; //table.op[opts.table_index] = 64;
|
|
//table.bits[opts.table_index] = 1;
|
|
//table.val[opts.table_index++] = 0;
|
|
|
|
table[table_index++] = 1 << 24 | 64 << 16 | 0;
|
|
opts.bits = 1;
|
|
return 0;
|
|
/* no symbols, but wait for decoding to report error */
|
|
}
|
|
|
|
for (min = 1; min < max; min++) {
|
|
if (count[min] !== 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (root < min) {
|
|
root = min;
|
|
}
|
|
/* check for an over-subscribed or incomplete set of lengths */
|
|
|
|
|
|
left = 1;
|
|
|
|
for (len = 1; len <= MAXBITS; len++) {
|
|
left <<= 1;
|
|
left -= count[len];
|
|
|
|
if (left < 0) {
|
|
return -1;
|
|
}
|
|
/* over-subscribed */
|
|
|
|
}
|
|
|
|
if (left > 0 && (type === CODES$1 || max !== 1)) {
|
|
return -1;
|
|
/* incomplete set */
|
|
}
|
|
/* generate offsets into symbol table for each length for sorting */
|
|
|
|
|
|
offs[1] = 0;
|
|
|
|
for (len = 1; len < MAXBITS; len++) {
|
|
offs[len + 1] = offs[len] + count[len];
|
|
}
|
|
/* sort symbols by length, by symbol order within each length */
|
|
|
|
|
|
for (sym = 0; sym < codes; sym++) {
|
|
if (lens[lens_index + sym] !== 0) {
|
|
work[offs[lens[lens_index + sym]]++] = sym;
|
|
}
|
|
}
|
|
/*
|
|
Create and fill in decoding tables. In this loop, the table being
|
|
filled is at next and has curr index bits. The code being used is huff
|
|
with length len. That code is converted to an index by dropping drop
|
|
bits off of the bottom. For codes where len is less than drop + curr,
|
|
those top drop + curr - len bits are incremented through all values to
|
|
fill the table with replicated entries.
|
|
root is the number of index bits for the root table. When len exceeds
|
|
root, sub-tables are created pointed to by the root entry with an index
|
|
of the low root bits of huff. This is saved in low to check for when a
|
|
new sub-table should be started. drop is zero when the root table is
|
|
being filled, and drop is root when sub-tables are being filled.
|
|
When a new sub-table is needed, it is necessary to look ahead in the
|
|
code lengths to determine what size sub-table is needed. The length
|
|
counts are used for this, and so count[] is decremented as codes are
|
|
entered in the tables.
|
|
used keeps track of how many table entries have been allocated from the
|
|
provided *table space. It is checked for LENS and DIST tables against
|
|
the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in
|
|
the initial root table size constants. See the comments in inftrees.h
|
|
for more information.
|
|
sym increments through all symbols, and the loop terminates when
|
|
all codes of length max, i.e. all codes, have been processed. This
|
|
routine permits incomplete codes, so another loop after this one fills
|
|
in the rest of the decoding tables with invalid code markers.
|
|
*/
|
|
|
|
/* set up for code type */
|
|
// poor man optimization - use if-else instead of switch,
|
|
// to avoid deopts in old v8
|
|
|
|
|
|
if (type === CODES$1) {
|
|
base = extra = work;
|
|
/* dummy value--not used */
|
|
|
|
end = 19;
|
|
} else if (type === LENS$1) {
|
|
base = lbase;
|
|
base_index -= 257;
|
|
extra = lext;
|
|
extra_index -= 257;
|
|
end = 256;
|
|
} else {
|
|
/* DISTS */
|
|
base = dbase;
|
|
extra = dext;
|
|
end = -1;
|
|
}
|
|
/* initialize opts for loop */
|
|
|
|
|
|
huff = 0;
|
|
/* starting code */
|
|
|
|
sym = 0;
|
|
/* starting code symbol */
|
|
|
|
len = min;
|
|
/* starting code length */
|
|
|
|
next = table_index;
|
|
/* current table to fill in */
|
|
|
|
curr = root;
|
|
/* current table index bits */
|
|
|
|
drop = 0;
|
|
/* current bits to drop from code for index */
|
|
|
|
low = -1;
|
|
/* trigger new sub-table when len > root */
|
|
|
|
used = 1 << root;
|
|
/* use root table entries */
|
|
|
|
mask = used - 1;
|
|
/* mask for comparing low */
|
|
|
|
/* check available table space */
|
|
|
|
if (type === LENS$1 && used > ENOUGH_LENS$1 || type === DISTS$1 && used > ENOUGH_DISTS$1) {
|
|
return 1;
|
|
}
|
|
/* process all codes and make table entries */
|
|
|
|
|
|
for (;;) {
|
|
/* create table entry */
|
|
here_bits = len - drop;
|
|
|
|
if (work[sym] < end) {
|
|
here_op = 0;
|
|
here_val = work[sym];
|
|
} else if (work[sym] > end) {
|
|
here_op = extra[extra_index + work[sym]];
|
|
here_val = base[base_index + work[sym]];
|
|
} else {
|
|
here_op = 32 + 64;
|
|
/* end of block */
|
|
|
|
here_val = 0;
|
|
}
|
|
/* replicate for those indices with low len bits equal to huff */
|
|
|
|
|
|
incr = 1 << len - drop;
|
|
fill = 1 << curr;
|
|
min = fill;
|
|
/* save offset to next table */
|
|
|
|
do {
|
|
fill -= incr;
|
|
table[next + (huff >> drop) + fill] = here_bits << 24 | here_op << 16 | here_val | 0;
|
|
} while (fill !== 0);
|
|
/* backwards increment the len-bit code huff */
|
|
|
|
|
|
incr = 1 << len - 1;
|
|
|
|
while (huff & incr) {
|
|
incr >>= 1;
|
|
}
|
|
|
|
if (incr !== 0) {
|
|
huff &= incr - 1;
|
|
huff += incr;
|
|
} else {
|
|
huff = 0;
|
|
}
|
|
/* go to next symbol, update count, len */
|
|
|
|
|
|
sym++;
|
|
|
|
if (--count[len] === 0) {
|
|
if (len === max) {
|
|
break;
|
|
}
|
|
|
|
len = lens[lens_index + work[sym]];
|
|
}
|
|
/* create new sub-table if needed */
|
|
|
|
|
|
if (len > root && (huff & mask) !== low) {
|
|
/* if first time, transition to sub-tables */
|
|
if (drop === 0) {
|
|
drop = root;
|
|
}
|
|
/* increment past last table */
|
|
|
|
|
|
next += min;
|
|
/* here min is 1 << curr */
|
|
|
|
/* determine length of next table */
|
|
|
|
curr = len - drop;
|
|
left = 1 << curr;
|
|
|
|
while (curr + drop < max) {
|
|
left -= count[curr + drop];
|
|
|
|
if (left <= 0) {
|
|
break;
|
|
}
|
|
|
|
curr++;
|
|
left <<= 1;
|
|
}
|
|
/* check for enough space */
|
|
|
|
|
|
used += 1 << curr;
|
|
|
|
if (type === LENS$1 && used > ENOUGH_LENS$1 || type === DISTS$1 && used > ENOUGH_DISTS$1) {
|
|
return 1;
|
|
}
|
|
/* point entry in root table to sub-table */
|
|
|
|
|
|
low = huff & mask;
|
|
/*table.op[low] = curr;
|
|
table.bits[low] = root;
|
|
table.val[low] = next - opts.table_index;*/
|
|
|
|
table[low] = root << 24 | curr << 16 | next - table_index | 0;
|
|
}
|
|
}
|
|
/* fill in remaining table entry if code is incomplete (guaranteed to have
|
|
at most one remaining entry, since if the code is incomplete, the
|
|
maximum code length that was allowed to get this far is one bit) */
|
|
|
|
|
|
if (huff !== 0) {
|
|
//table.op[next + huff] = 64; /* invalid code marker */
|
|
//table.bits[next + huff] = len - drop;
|
|
//table.val[next + huff] = 0;
|
|
table[next + huff] = len - drop << 24 | 64 << 16 | 0;
|
|
}
|
|
/* set return parameters */
|
|
//opts.table_index += used;
|
|
|
|
|
|
opts.bits = root;
|
|
return 0;
|
|
};
|
|
|
|
var inftrees = inflate_table; // (C) 1995-2013 Jean-loup Gailly and Mark Adler
|
|
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
|
|
//
|
|
// This software is provided 'as-is', without any express or implied
|
|
// warranty. In no event will the authors be held liable for any damages
|
|
// arising from the use of this software.
|
|
//
|
|
// Permission is granted to anyone to use this software for any purpose,
|
|
// including commercial applications, and to alter it and redistribute it
|
|
// freely, subject to the following restrictions:
|
|
//
|
|
// 1. The origin of this software must not be misrepresented; you must not
|
|
// claim that you wrote the original software. If you use this software
|
|
// in a product, an acknowledgment in the product documentation would be
|
|
// appreciated but is not required.
|
|
// 2. Altered source versions must be plainly marked as such, and must not be
|
|
// misrepresented as being the original software.
|
|
// 3. This notice may not be removed or altered from any source distribution.
|
|
|
|
const CODES = 0;
|
|
const LENS = 1;
|
|
const DISTS = 2;
|
|
/* Public constants ==========================================================*/
|
|
|
|
/* ===========================================================================*/
|
|
|
|
const {
|
|
Z_FINISH: Z_FINISH$1,
|
|
Z_BLOCK,
|
|
Z_TREES,
|
|
Z_OK: Z_OK$1,
|
|
Z_STREAM_END: Z_STREAM_END$1,
|
|
Z_NEED_DICT: Z_NEED_DICT$1,
|
|
Z_STREAM_ERROR: Z_STREAM_ERROR$1,
|
|
Z_DATA_ERROR: Z_DATA_ERROR$1,
|
|
Z_MEM_ERROR: Z_MEM_ERROR$1,
|
|
Z_BUF_ERROR,
|
|
Z_DEFLATED
|
|
} = constants$2;
|
|
/* STATES ====================================================================*/
|
|
|
|
/* ===========================================================================*/
|
|
|
|
const HEAD = 1;
|
|
/* i: waiting for magic header */
|
|
|
|
const FLAGS = 2;
|
|
/* i: waiting for method and flags (gzip) */
|
|
|
|
const TIME = 3;
|
|
/* i: waiting for modification time (gzip) */
|
|
|
|
const OS = 4;
|
|
/* i: waiting for extra flags and operating system (gzip) */
|
|
|
|
const EXLEN = 5;
|
|
/* i: waiting for extra length (gzip) */
|
|
|
|
const EXTRA = 6;
|
|
/* i: waiting for extra bytes (gzip) */
|
|
|
|
const NAME = 7;
|
|
/* i: waiting for end of file name (gzip) */
|
|
|
|
const COMMENT = 8;
|
|
/* i: waiting for end of comment (gzip) */
|
|
|
|
const HCRC = 9;
|
|
/* i: waiting for header crc (gzip) */
|
|
|
|
const DICTID = 10;
|
|
/* i: waiting for dictionary check value */
|
|
|
|
const DICT = 11;
|
|
/* waiting for inflateSetDictionary() call */
|
|
|
|
const TYPE = 12;
|
|
/* i: waiting for type bits, including last-flag bit */
|
|
|
|
const TYPEDO = 13;
|
|
/* i: same, but skip check to exit inflate on new block */
|
|
|
|
const STORED = 14;
|
|
/* i: waiting for stored size (length and complement) */
|
|
|
|
const COPY_ = 15;
|
|
/* i/o: same as COPY below, but only first time in */
|
|
|
|
const COPY = 16;
|
|
/* i/o: waiting for input or output to copy stored block */
|
|
|
|
const TABLE = 17;
|
|
/* i: waiting for dynamic block table lengths */
|
|
|
|
const LENLENS = 18;
|
|
/* i: waiting for code length code lengths */
|
|
|
|
const CODELENS = 19;
|
|
/* i: waiting for length/lit and distance code lengths */
|
|
|
|
const LEN_ = 20;
|
|
/* i: same as LEN below, but only first time in */
|
|
|
|
const LEN = 21;
|
|
/* i: waiting for length/lit/eob code */
|
|
|
|
const LENEXT = 22;
|
|
/* i: waiting for length extra bits */
|
|
|
|
const DIST = 23;
|
|
/* i: waiting for distance code */
|
|
|
|
const DISTEXT = 24;
|
|
/* i: waiting for distance extra bits */
|
|
|
|
const MATCH = 25;
|
|
/* o: waiting for output space to copy string */
|
|
|
|
const LIT = 26;
|
|
/* o: waiting for output space to write literal */
|
|
|
|
const CHECK = 27;
|
|
/* i: waiting for 32-bit check value */
|
|
|
|
const LENGTH = 28;
|
|
/* i: waiting for 32-bit length (gzip) */
|
|
|
|
const DONE = 29;
|
|
/* finished check, done -- remain here until reset */
|
|
|
|
const BAD = 30;
|
|
/* got a data error -- remain here until reset */
|
|
|
|
const MEM = 31;
|
|
/* got an inflate() memory error -- remain here until reset */
|
|
|
|
const SYNC = 32;
|
|
/* looking for synchronization bytes to restart inflate() */
|
|
|
|
/* ===========================================================================*/
|
|
|
|
const ENOUGH_LENS = 852;
|
|
const ENOUGH_DISTS = 592; //const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);
|
|
|
|
const MAX_WBITS = 15;
|
|
/* 32K LZ77 window */
|
|
|
|
const DEF_WBITS = MAX_WBITS;
|
|
|
|
const zswap32 = q => {
|
|
return (q >>> 24 & 0xff) + (q >>> 8 & 0xff00) + ((q & 0xff00) << 8) + ((q & 0xff) << 24);
|
|
};
|
|
|
|
function InflateState() {
|
|
this.mode = 0;
|
|
/* current inflate mode */
|
|
|
|
this.last = false;
|
|
/* true if processing last block */
|
|
|
|
this.wrap = 0;
|
|
/* bit 0 true for zlib, bit 1 true for gzip */
|
|
|
|
this.havedict = false;
|
|
/* true if dictionary provided */
|
|
|
|
this.flags = 0;
|
|
/* gzip header method and flags (0 if zlib) */
|
|
|
|
this.dmax = 0;
|
|
/* zlib header max distance (INFLATE_STRICT) */
|
|
|
|
this.check = 0;
|
|
/* protected copy of check value */
|
|
|
|
this.total = 0;
|
|
/* protected copy of output count */
|
|
// TODO: may be {}
|
|
|
|
this.head = null;
|
|
/* where to save gzip header information */
|
|
|
|
/* sliding window */
|
|
|
|
this.wbits = 0;
|
|
/* log base 2 of requested window size */
|
|
|
|
this.wsize = 0;
|
|
/* window size or zero if not using window */
|
|
|
|
this.whave = 0;
|
|
/* valid bytes in the window */
|
|
|
|
this.wnext = 0;
|
|
/* window write index */
|
|
|
|
this.window = null;
|
|
/* allocated sliding window, if needed */
|
|
|
|
/* bit accumulator */
|
|
|
|
this.hold = 0;
|
|
/* input bit accumulator */
|
|
|
|
this.bits = 0;
|
|
/* number of bits in "in" */
|
|
|
|
/* for string and stored block copying */
|
|
|
|
this.length = 0;
|
|
/* literal or length of data to copy */
|
|
|
|
this.offset = 0;
|
|
/* distance back to copy string from */
|
|
|
|
/* for table and code decoding */
|
|
|
|
this.extra = 0;
|
|
/* extra bits needed */
|
|
|
|
/* fixed and dynamic code tables */
|
|
|
|
this.lencode = null;
|
|
/* starting table for length/literal codes */
|
|
|
|
this.distcode = null;
|
|
/* starting table for distance codes */
|
|
|
|
this.lenbits = 0;
|
|
/* index bits for lencode */
|
|
|
|
this.distbits = 0;
|
|
/* index bits for distcode */
|
|
|
|
/* dynamic table building */
|
|
|
|
this.ncode = 0;
|
|
/* number of code length code lengths */
|
|
|
|
this.nlen = 0;
|
|
/* number of length code lengths */
|
|
|
|
this.ndist = 0;
|
|
/* number of distance code lengths */
|
|
|
|
this.have = 0;
|
|
/* number of code lengths in lens[] */
|
|
|
|
this.next = null;
|
|
/* next available space in codes[] */
|
|
|
|
this.lens = new Uint16Array(320);
|
|
/* temporary storage for code lengths */
|
|
|
|
this.work = new Uint16Array(288);
|
|
/* work area for code table building */
|
|
|
|
/*
|
|
because we don't have pointers in js, we use lencode and distcode directly
|
|
as buffers so we don't need codes
|
|
*/
|
|
//this.codes = new Int32Array(ENOUGH); /* space for code tables */
|
|
|
|
this.lendyn = null;
|
|
/* dynamic table for length/literal codes (JS specific) */
|
|
|
|
this.distdyn = null;
|
|
/* dynamic table for distance codes (JS specific) */
|
|
|
|
this.sane = 0;
|
|
/* if false, allow invalid distance too far */
|
|
|
|
this.back = 0;
|
|
/* bits back of last unprocessed length/lit */
|
|
|
|
this.was = 0;
|
|
/* initial length of match */
|
|
}
|
|
|
|
const inflateResetKeep = strm => {
|
|
if (!strm || !strm.state) {
|
|
return Z_STREAM_ERROR$1;
|
|
}
|
|
|
|
const state = strm.state;
|
|
strm.total_in = strm.total_out = state.total = 0;
|
|
strm.msg = '';
|
|
/*Z_NULL*/
|
|
|
|
if (state.wrap) {
|
|
/* to support ill-conceived Java test suite */
|
|
strm.adler = state.wrap & 1;
|
|
}
|
|
|
|
state.mode = HEAD;
|
|
state.last = 0;
|
|
state.havedict = 0;
|
|
state.dmax = 32768;
|
|
state.head = null
|
|
/*Z_NULL*/
|
|
;
|
|
state.hold = 0;
|
|
state.bits = 0; //state.lencode = state.distcode = state.next = state.codes;
|
|
|
|
state.lencode = state.lendyn = new Int32Array(ENOUGH_LENS);
|
|
state.distcode = state.distdyn = new Int32Array(ENOUGH_DISTS);
|
|
state.sane = 1;
|
|
state.back = -1; //Tracev((stderr, "inflate: reset\n"));
|
|
|
|
return Z_OK$1;
|
|
};
|
|
|
|
const inflateReset = strm => {
|
|
if (!strm || !strm.state) {
|
|
return Z_STREAM_ERROR$1;
|
|
}
|
|
|
|
const state = strm.state;
|
|
state.wsize = 0;
|
|
state.whave = 0;
|
|
state.wnext = 0;
|
|
return inflateResetKeep(strm);
|
|
};
|
|
|
|
const inflateReset2 = (strm, windowBits) => {
|
|
let wrap;
|
|
/* get the state */
|
|
|
|
if (!strm || !strm.state) {
|
|
return Z_STREAM_ERROR$1;
|
|
}
|
|
|
|
const state = strm.state;
|
|
/* extract wrap request from windowBits parameter */
|
|
|
|
if (windowBits < 0) {
|
|
wrap = 0;
|
|
windowBits = -windowBits;
|
|
} else {
|
|
wrap = (windowBits >> 4) + 1;
|
|
|
|
if (windowBits < 48) {
|
|
windowBits &= 15;
|
|
}
|
|
}
|
|
/* set number of window bits, free window if different */
|
|
|
|
|
|
if (windowBits && (windowBits < 8 || windowBits > 15)) {
|
|
return Z_STREAM_ERROR$1;
|
|
}
|
|
|
|
if (state.window !== null && state.wbits !== windowBits) {
|
|
state.window = null;
|
|
}
|
|
/* update state and reset the rest of it */
|
|
|
|
|
|
state.wrap = wrap;
|
|
state.wbits = windowBits;
|
|
return inflateReset(strm);
|
|
};
|
|
|
|
const inflateInit2 = (strm, windowBits) => {
|
|
if (!strm) {
|
|
return Z_STREAM_ERROR$1;
|
|
} //strm.msg = Z_NULL; /* in case we return an error */
|
|
|
|
|
|
const state = new InflateState(); //if (state === Z_NULL) return Z_MEM_ERROR;
|
|
//Tracev((stderr, "inflate: allocated\n"));
|
|
|
|
strm.state = state;
|
|
state.window = null
|
|
/*Z_NULL*/
|
|
;
|
|
const ret = inflateReset2(strm, windowBits);
|
|
|
|
if (ret !== Z_OK$1) {
|
|
strm.state = null
|
|
/*Z_NULL*/
|
|
;
|
|
}
|
|
|
|
return ret;
|
|
};
|
|
|
|
const inflateInit = strm => {
|
|
return inflateInit2(strm, DEF_WBITS);
|
|
};
|
|
/*
|
|
Return state with length and distance decoding tables and index sizes set to
|
|
fixed code decoding. Normally this returns fixed tables from inffixed.h.
|
|
If BUILDFIXED is defined, then instead this routine builds the tables the
|
|
first time it's called, and returns those tables the first time and
|
|
thereafter. This reduces the size of the code by about 2K bytes, in
|
|
exchange for a little execution time. However, BUILDFIXED should not be
|
|
used for threaded applications, since the rewriting of the tables and virgin
|
|
may not be thread-safe.
|
|
*/
|
|
|
|
|
|
let virgin = true;
|
|
let lenfix, distfix; // We have no pointers in JS, so keep tables separate
|
|
|
|
const fixedtables = state => {
|
|
/* build fixed huffman tables if first call (may not be thread safe) */
|
|
if (virgin) {
|
|
lenfix = new Int32Array(512);
|
|
distfix = new Int32Array(32);
|
|
/* literal/length table */
|
|
|
|
let sym = 0;
|
|
|
|
while (sym < 144) {
|
|
state.lens[sym++] = 8;
|
|
}
|
|
|
|
while (sym < 256) {
|
|
state.lens[sym++] = 9;
|
|
}
|
|
|
|
while (sym < 280) {
|
|
state.lens[sym++] = 7;
|
|
}
|
|
|
|
while (sym < 288) {
|
|
state.lens[sym++] = 8;
|
|
}
|
|
|
|
inftrees(LENS, state.lens, 0, 288, lenfix, 0, state.work, {
|
|
bits: 9
|
|
});
|
|
/* distance table */
|
|
|
|
sym = 0;
|
|
|
|
while (sym < 32) {
|
|
state.lens[sym++] = 5;
|
|
}
|
|
|
|
inftrees(DISTS, state.lens, 0, 32, distfix, 0, state.work, {
|
|
bits: 5
|
|
});
|
|
/* do this just once */
|
|
|
|
virgin = false;
|
|
}
|
|
|
|
state.lencode = lenfix;
|
|
state.lenbits = 9;
|
|
state.distcode = distfix;
|
|
state.distbits = 5;
|
|
};
|
|
/*
|
|
Update the window with the last wsize (normally 32K) bytes written before
|
|
returning. If window does not exist yet, create it. This is only called
|
|
when a window is already in use, or when output has been written during this
|
|
inflate call, but the end of the deflate stream has not been reached yet.
|
|
It is also called to create a window for dictionary data when a dictionary
|
|
is loaded.
|
|
|
|
Providing output buffers larger than 32K to inflate() should provide a speed
|
|
advantage, since only the last 32K of output is copied to the sliding window
|
|
upon return from inflate(), and since all distances after the first 32K of
|
|
output will fall in the output data, making match copies simpler and faster.
|
|
The advantage may be dependent on the size of the processor's data caches.
|
|
*/
|
|
|
|
|
|
const updatewindow = (strm, src, end, copy) => {
|
|
let dist;
|
|
const state = strm.state;
|
|
/* if it hasn't been done already, allocate space for the window */
|
|
|
|
if (state.window === null) {
|
|
state.wsize = 1 << state.wbits;
|
|
state.wnext = 0;
|
|
state.whave = 0;
|
|
state.window = new Uint8Array(state.wsize);
|
|
}
|
|
/* copy state->wsize or less output bytes into the circular window */
|
|
|
|
|
|
if (copy >= state.wsize) {
|
|
state.window.set(src.subarray(end - state.wsize, end), 0);
|
|
state.wnext = 0;
|
|
state.whave = state.wsize;
|
|
} else {
|
|
dist = state.wsize - state.wnext;
|
|
|
|
if (dist > copy) {
|
|
dist = copy;
|
|
} //zmemcpy(state->window + state->wnext, end - copy, dist);
|
|
|
|
|
|
state.window.set(src.subarray(end - copy, end - copy + dist), state.wnext);
|
|
copy -= dist;
|
|
|
|
if (copy) {
|
|
//zmemcpy(state->window, end - copy, copy);
|
|
state.window.set(src.subarray(end - copy, end), 0);
|
|
state.wnext = copy;
|
|
state.whave = state.wsize;
|
|
} else {
|
|
state.wnext += dist;
|
|
|
|
if (state.wnext === state.wsize) {
|
|
state.wnext = 0;
|
|
}
|
|
|
|
if (state.whave < state.wsize) {
|
|
state.whave += dist;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
};
|
|
|
|
const inflate$2 = (strm, flush) => {
|
|
let state;
|
|
let input, output; // input/output buffers
|
|
|
|
let next;
|
|
/* next input INDEX */
|
|
|
|
let put;
|
|
/* next output INDEX */
|
|
|
|
let have, left;
|
|
/* available input and output */
|
|
|
|
let hold;
|
|
/* bit buffer */
|
|
|
|
let bits;
|
|
/* bits in bit buffer */
|
|
|
|
let _in, _out;
|
|
/* save starting available input and output */
|
|
|
|
|
|
let copy;
|
|
/* number of stored or match bytes to copy */
|
|
|
|
let from;
|
|
/* where to copy match bytes from */
|
|
|
|
let from_source;
|
|
let here = 0;
|
|
/* current decoding table entry */
|
|
|
|
let here_bits, here_op, here_val; // paked "here" denormalized (JS specific)
|
|
//let last; /* parent table entry */
|
|
|
|
let last_bits, last_op, last_val; // paked "last" denormalized (JS specific)
|
|
|
|
let len;
|
|
/* length to copy for repeats, bits to drop */
|
|
|
|
let ret;
|
|
/* return code */
|
|
|
|
const hbuf = new Uint8Array(4);
|
|
/* buffer for gzip header crc calculation */
|
|
|
|
let opts;
|
|
let n; // temporary variable for NEED_BITS
|
|
|
|
const order =
|
|
/* permutation of code lengths */
|
|
new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
|
|
|
|
if (!strm || !strm.state || !strm.output || !strm.input && strm.avail_in !== 0) {
|
|
return Z_STREAM_ERROR$1;
|
|
}
|
|
|
|
state = strm.state;
|
|
|
|
if (state.mode === TYPE) {
|
|
state.mode = TYPEDO;
|
|
}
|
|
/* skip check */
|
|
//--- LOAD() ---
|
|
|
|
|
|
put = strm.next_out;
|
|
output = strm.output;
|
|
left = strm.avail_out;
|
|
next = strm.next_in;
|
|
input = strm.input;
|
|
have = strm.avail_in;
|
|
hold = state.hold;
|
|
bits = state.bits; //---
|
|
|
|
_in = have;
|
|
_out = left;
|
|
ret = Z_OK$1;
|
|
|
|
inf_leave: // goto emulation
|
|
for (;;) {
|
|
switch (state.mode) {
|
|
case HEAD:
|
|
if (state.wrap === 0) {
|
|
state.mode = TYPEDO;
|
|
break;
|
|
} //=== NEEDBITS(16);
|
|
|
|
|
|
while (bits < 16) {
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--;
|
|
hold += input[next++] << bits;
|
|
bits += 8;
|
|
} //===//
|
|
|
|
|
|
if (state.wrap & 2 && hold === 0x8b1f) {
|
|
/* gzip header */
|
|
state.check = 0
|
|
/*crc32(0L, Z_NULL, 0)*/
|
|
; //=== CRC2(state.check, hold);
|
|
|
|
hbuf[0] = hold & 0xff;
|
|
hbuf[1] = hold >>> 8 & 0xff;
|
|
state.check = crc32_1(state.check, hbuf, 2, 0); //===//
|
|
//=== INITBITS();
|
|
|
|
hold = 0;
|
|
bits = 0; //===//
|
|
|
|
state.mode = FLAGS;
|
|
break;
|
|
}
|
|
|
|
state.flags = 0;
|
|
/* expect zlib header */
|
|
|
|
if (state.head) {
|
|
state.head.done = false;
|
|
}
|
|
|
|
if (!(state.wrap & 1) ||
|
|
/* check if zlib header allowed */
|
|
(((hold & 0xff
|
|
/*BITS(8)*/
|
|
) << 8) + (hold >> 8)) % 31) {
|
|
strm.msg = 'incorrect header check';
|
|
state.mode = BAD;
|
|
break;
|
|
}
|
|
|
|
if ((hold & 0x0f
|
|
/*BITS(4)*/
|
|
) !== Z_DEFLATED) {
|
|
strm.msg = 'unknown compression method';
|
|
state.mode = BAD;
|
|
break;
|
|
} //--- DROPBITS(4) ---//
|
|
|
|
|
|
hold >>>= 4;
|
|
bits -= 4; //---//
|
|
|
|
len = (hold & 0x0f
|
|
/*BITS(4)*/
|
|
) + 8;
|
|
|
|
if (state.wbits === 0) {
|
|
state.wbits = len;
|
|
} else if (len > state.wbits) {
|
|
strm.msg = 'invalid window size';
|
|
state.mode = BAD;
|
|
break;
|
|
} // !!! pako patch. Force use `options.windowBits` if passed.
|
|
// Required to always use max window size by default.
|
|
|
|
|
|
state.dmax = 1 << state.wbits; //state.dmax = 1 << len;
|
|
//Tracev((stderr, "inflate: zlib header ok\n"));
|
|
|
|
strm.adler = state.check = 1
|
|
/*adler32(0L, Z_NULL, 0)*/
|
|
;
|
|
state.mode = hold & 0x200 ? DICTID : TYPE; //=== INITBITS();
|
|
|
|
hold = 0;
|
|
bits = 0; //===//
|
|
|
|
break;
|
|
|
|
case FLAGS:
|
|
//=== NEEDBITS(16); */
|
|
while (bits < 16) {
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--;
|
|
hold += input[next++] << bits;
|
|
bits += 8;
|
|
} //===//
|
|
|
|
|
|
state.flags = hold;
|
|
|
|
if ((state.flags & 0xff) !== Z_DEFLATED) {
|
|
strm.msg = 'unknown compression method';
|
|
state.mode = BAD;
|
|
break;
|
|
}
|
|
|
|
if (state.flags & 0xe000) {
|
|
strm.msg = 'unknown header flags set';
|
|
state.mode = BAD;
|
|
break;
|
|
}
|
|
|
|
if (state.head) {
|
|
state.head.text = hold >> 8 & 1;
|
|
}
|
|
|
|
if (state.flags & 0x0200) {
|
|
//=== CRC2(state.check, hold);
|
|
hbuf[0] = hold & 0xff;
|
|
hbuf[1] = hold >>> 8 & 0xff;
|
|
state.check = crc32_1(state.check, hbuf, 2, 0); //===//
|
|
} //=== INITBITS();
|
|
|
|
|
|
hold = 0;
|
|
bits = 0; //===//
|
|
|
|
state.mode = TIME;
|
|
|
|
/* falls through */
|
|
|
|
case TIME:
|
|
//=== NEEDBITS(32); */
|
|
while (bits < 32) {
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--;
|
|
hold += input[next++] << bits;
|
|
bits += 8;
|
|
} //===//
|
|
|
|
|
|
if (state.head) {
|
|
state.head.time = hold;
|
|
}
|
|
|
|
if (state.flags & 0x0200) {
|
|
//=== CRC4(state.check, hold)
|
|
hbuf[0] = hold & 0xff;
|
|
hbuf[1] = hold >>> 8 & 0xff;
|
|
hbuf[2] = hold >>> 16 & 0xff;
|
|
hbuf[3] = hold >>> 24 & 0xff;
|
|
state.check = crc32_1(state.check, hbuf, 4, 0); //===
|
|
} //=== INITBITS();
|
|
|
|
|
|
hold = 0;
|
|
bits = 0; //===//
|
|
|
|
state.mode = OS;
|
|
|
|
/* falls through */
|
|
|
|
case OS:
|
|
//=== NEEDBITS(16); */
|
|
while (bits < 16) {
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--;
|
|
hold += input[next++] << bits;
|
|
bits += 8;
|
|
} //===//
|
|
|
|
|
|
if (state.head) {
|
|
state.head.xflags = hold & 0xff;
|
|
state.head.os = hold >> 8;
|
|
}
|
|
|
|
if (state.flags & 0x0200) {
|
|
//=== CRC2(state.check, hold);
|
|
hbuf[0] = hold & 0xff;
|
|
hbuf[1] = hold >>> 8 & 0xff;
|
|
state.check = crc32_1(state.check, hbuf, 2, 0); //===//
|
|
} //=== INITBITS();
|
|
|
|
|
|
hold = 0;
|
|
bits = 0; //===//
|
|
|
|
state.mode = EXLEN;
|
|
|
|
/* falls through */
|
|
|
|
case EXLEN:
|
|
if (state.flags & 0x0400) {
|
|
//=== NEEDBITS(16); */
|
|
while (bits < 16) {
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--;
|
|
hold += input[next++] << bits;
|
|
bits += 8;
|
|
} //===//
|
|
|
|
|
|
state.length = hold;
|
|
|
|
if (state.head) {
|
|
state.head.extra_len = hold;
|
|
}
|
|
|
|
if (state.flags & 0x0200) {
|
|
//=== CRC2(state.check, hold);
|
|
hbuf[0] = hold & 0xff;
|
|
hbuf[1] = hold >>> 8 & 0xff;
|
|
state.check = crc32_1(state.check, hbuf, 2, 0); //===//
|
|
} //=== INITBITS();
|
|
|
|
|
|
hold = 0;
|
|
bits = 0; //===//
|
|
} else if (state.head) {
|
|
state.head.extra = null
|
|
/*Z_NULL*/
|
|
;
|
|
}
|
|
|
|
state.mode = EXTRA;
|
|
|
|
/* falls through */
|
|
|
|
case EXTRA:
|
|
if (state.flags & 0x0400) {
|
|
copy = state.length;
|
|
|
|
if (copy > have) {
|
|
copy = have;
|
|
}
|
|
|
|
if (copy) {
|
|
if (state.head) {
|
|
len = state.head.extra_len - state.length;
|
|
|
|
if (!state.head.extra) {
|
|
// Use untyped array for more convenient processing later
|
|
state.head.extra = new Uint8Array(state.head.extra_len);
|
|
}
|
|
|
|
state.head.extra.set(input.subarray(next, // extra field is limited to 65536 bytes
|
|
// - no need for additional size check
|
|
next + copy),
|
|
/*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/
|
|
len); //zmemcpy(state.head.extra + len, next,
|
|
// len + copy > state.head.extra_max ?
|
|
// state.head.extra_max - len : copy);
|
|
}
|
|
|
|
if (state.flags & 0x0200) {
|
|
state.check = crc32_1(state.check, input, copy, next);
|
|
}
|
|
|
|
have -= copy;
|
|
next += copy;
|
|
state.length -= copy;
|
|
}
|
|
|
|
if (state.length) {
|
|
break inf_leave;
|
|
}
|
|
}
|
|
|
|
state.length = 0;
|
|
state.mode = NAME;
|
|
|
|
/* falls through */
|
|
|
|
case NAME:
|
|
if (state.flags & 0x0800) {
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
copy = 0;
|
|
|
|
do {
|
|
// TODO: 2 or 1 bytes?
|
|
len = input[next + copy++];
|
|
/* use constant limit because in js we should not preallocate memory */
|
|
|
|
if (state.head && len && state.length < 65536
|
|
/*state.head.name_max*/
|
|
) {
|
|
state.head.name += String.fromCharCode(len);
|
|
}
|
|
} while (len && copy < have);
|
|
|
|
if (state.flags & 0x0200) {
|
|
state.check = crc32_1(state.check, input, copy, next);
|
|
}
|
|
|
|
have -= copy;
|
|
next += copy;
|
|
|
|
if (len) {
|
|
break inf_leave;
|
|
}
|
|
} else if (state.head) {
|
|
state.head.name = null;
|
|
}
|
|
|
|
state.length = 0;
|
|
state.mode = COMMENT;
|
|
|
|
/* falls through */
|
|
|
|
case COMMENT:
|
|
if (state.flags & 0x1000) {
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
copy = 0;
|
|
|
|
do {
|
|
len = input[next + copy++];
|
|
/* use constant limit because in js we should not preallocate memory */
|
|
|
|
if (state.head && len && state.length < 65536
|
|
/*state.head.comm_max*/
|
|
) {
|
|
state.head.comment += String.fromCharCode(len);
|
|
}
|
|
} while (len && copy < have);
|
|
|
|
if (state.flags & 0x0200) {
|
|
state.check = crc32_1(state.check, input, copy, next);
|
|
}
|
|
|
|
have -= copy;
|
|
next += copy;
|
|
|
|
if (len) {
|
|
break inf_leave;
|
|
}
|
|
} else if (state.head) {
|
|
state.head.comment = null;
|
|
}
|
|
|
|
state.mode = HCRC;
|
|
|
|
/* falls through */
|
|
|
|
case HCRC:
|
|
if (state.flags & 0x0200) {
|
|
//=== NEEDBITS(16); */
|
|
while (bits < 16) {
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--;
|
|
hold += input[next++] << bits;
|
|
bits += 8;
|
|
} //===//
|
|
|
|
|
|
if (hold !== (state.check & 0xffff)) {
|
|
strm.msg = 'header crc mismatch';
|
|
state.mode = BAD;
|
|
break;
|
|
} //=== INITBITS();
|
|
|
|
|
|
hold = 0;
|
|
bits = 0; //===//
|
|
}
|
|
|
|
if (state.head) {
|
|
state.head.hcrc = state.flags >> 9 & 1;
|
|
state.head.done = true;
|
|
}
|
|
|
|
strm.adler = state.check = 0;
|
|
state.mode = TYPE;
|
|
break;
|
|
|
|
case DICTID:
|
|
//=== NEEDBITS(32); */
|
|
while (bits < 32) {
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--;
|
|
hold += input[next++] << bits;
|
|
bits += 8;
|
|
} //===//
|
|
|
|
|
|
strm.adler = state.check = zswap32(hold); //=== INITBITS();
|
|
|
|
hold = 0;
|
|
bits = 0; //===//
|
|
|
|
state.mode = DICT;
|
|
|
|
/* falls through */
|
|
|
|
case DICT:
|
|
if (state.havedict === 0) {
|
|
//--- RESTORE() ---
|
|
strm.next_out = put;
|
|
strm.avail_out = left;
|
|
strm.next_in = next;
|
|
strm.avail_in = have;
|
|
state.hold = hold;
|
|
state.bits = bits; //---
|
|
|
|
return Z_NEED_DICT$1;
|
|
}
|
|
|
|
strm.adler = state.check = 1
|
|
/*adler32(0L, Z_NULL, 0)*/
|
|
;
|
|
state.mode = TYPE;
|
|
|
|
/* falls through */
|
|
|
|
case TYPE:
|
|
if (flush === Z_BLOCK || flush === Z_TREES) {
|
|
break inf_leave;
|
|
}
|
|
|
|
/* falls through */
|
|
|
|
case TYPEDO:
|
|
if (state.last) {
|
|
//--- BYTEBITS() ---//
|
|
hold >>>= bits & 7;
|
|
bits -= bits & 7; //---//
|
|
|
|
state.mode = CHECK;
|
|
break;
|
|
} //=== NEEDBITS(3); */
|
|
|
|
|
|
while (bits < 3) {
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--;
|
|
hold += input[next++] << bits;
|
|
bits += 8;
|
|
} //===//
|
|
|
|
|
|
state.last = hold & 0x01
|
|
/*BITS(1)*/
|
|
; //--- DROPBITS(1) ---//
|
|
|
|
hold >>>= 1;
|
|
bits -= 1; //---//
|
|
|
|
switch (hold & 0x03
|
|
/*BITS(2)*/
|
|
) {
|
|
case 0:
|
|
/* stored block */
|
|
//Tracev((stderr, "inflate: stored block%s\n",
|
|
// state.last ? " (last)" : ""));
|
|
state.mode = STORED;
|
|
break;
|
|
|
|
case 1:
|
|
/* fixed block */
|
|
fixedtables(state); //Tracev((stderr, "inflate: fixed codes block%s\n",
|
|
// state.last ? " (last)" : ""));
|
|
|
|
state.mode = LEN_;
|
|
/* decode codes */
|
|
|
|
if (flush === Z_TREES) {
|
|
//--- DROPBITS(2) ---//
|
|
hold >>>= 2;
|
|
bits -= 2; //---//
|
|
|
|
break inf_leave;
|
|
}
|
|
|
|
break;
|
|
|
|
case 2:
|
|
/* dynamic block */
|
|
//Tracev((stderr, "inflate: dynamic codes block%s\n",
|
|
// state.last ? " (last)" : ""));
|
|
state.mode = TABLE;
|
|
break;
|
|
|
|
case 3:
|
|
strm.msg = 'invalid block type';
|
|
state.mode = BAD;
|
|
} //--- DROPBITS(2) ---//
|
|
|
|
|
|
hold >>>= 2;
|
|
bits -= 2; //---//
|
|
|
|
break;
|
|
|
|
case STORED:
|
|
//--- BYTEBITS() ---// /* go to byte boundary */
|
|
hold >>>= bits & 7;
|
|
bits -= bits & 7; //---//
|
|
//=== NEEDBITS(32); */
|
|
|
|
while (bits < 32) {
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--;
|
|
hold += input[next++] << bits;
|
|
bits += 8;
|
|
} //===//
|
|
|
|
|
|
if ((hold & 0xffff) !== (hold >>> 16 ^ 0xffff)) {
|
|
strm.msg = 'invalid stored block lengths';
|
|
state.mode = BAD;
|
|
break;
|
|
}
|
|
|
|
state.length = hold & 0xffff; //Tracev((stderr, "inflate: stored length %u\n",
|
|
// state.length));
|
|
//=== INITBITS();
|
|
|
|
hold = 0;
|
|
bits = 0; //===//
|
|
|
|
state.mode = COPY_;
|
|
|
|
if (flush === Z_TREES) {
|
|
break inf_leave;
|
|
}
|
|
|
|
/* falls through */
|
|
|
|
case COPY_:
|
|
state.mode = COPY;
|
|
|
|
/* falls through */
|
|
|
|
case COPY:
|
|
copy = state.length;
|
|
|
|
if (copy) {
|
|
if (copy > have) {
|
|
copy = have;
|
|
}
|
|
|
|
if (copy > left) {
|
|
copy = left;
|
|
}
|
|
|
|
if (copy === 0) {
|
|
break inf_leave;
|
|
} //--- zmemcpy(put, next, copy); ---
|
|
|
|
|
|
output.set(input.subarray(next, next + copy), put); //---//
|
|
|
|
have -= copy;
|
|
next += copy;
|
|
left -= copy;
|
|
put += copy;
|
|
state.length -= copy;
|
|
break;
|
|
} //Tracev((stderr, "inflate: stored end\n"));
|
|
|
|
|
|
state.mode = TYPE;
|
|
break;
|
|
|
|
case TABLE:
|
|
//=== NEEDBITS(14); */
|
|
while (bits < 14) {
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--;
|
|
hold += input[next++] << bits;
|
|
bits += 8;
|
|
} //===//
|
|
|
|
|
|
state.nlen = (hold & 0x1f
|
|
/*BITS(5)*/
|
|
) + 257; //--- DROPBITS(5) ---//
|
|
|
|
hold >>>= 5;
|
|
bits -= 5; //---//
|
|
|
|
state.ndist = (hold & 0x1f
|
|
/*BITS(5)*/
|
|
) + 1; //--- DROPBITS(5) ---//
|
|
|
|
hold >>>= 5;
|
|
bits -= 5; //---//
|
|
|
|
state.ncode = (hold & 0x0f
|
|
/*BITS(4)*/
|
|
) + 4; //--- DROPBITS(4) ---//
|
|
|
|
hold >>>= 4;
|
|
bits -= 4; //---//
|
|
//#ifndef PKZIP_BUG_WORKAROUND
|
|
|
|
if (state.nlen > 286 || state.ndist > 30) {
|
|
strm.msg = 'too many length or distance symbols';
|
|
state.mode = BAD;
|
|
break;
|
|
} //#endif
|
|
//Tracev((stderr, "inflate: table sizes ok\n"));
|
|
|
|
|
|
state.have = 0;
|
|
state.mode = LENLENS;
|
|
|
|
/* falls through */
|
|
|
|
case LENLENS:
|
|
while (state.have < state.ncode) {
|
|
//=== NEEDBITS(3);
|
|
while (bits < 3) {
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--;
|
|
hold += input[next++] << bits;
|
|
bits += 8;
|
|
} //===//
|
|
|
|
|
|
state.lens[order[state.have++]] = hold & 0x07; //BITS(3);
|
|
//--- DROPBITS(3) ---//
|
|
|
|
hold >>>= 3;
|
|
bits -= 3; //---//
|
|
}
|
|
|
|
while (state.have < 19) {
|
|
state.lens[order[state.have++]] = 0;
|
|
} // We have separate tables & no pointers. 2 commented lines below not needed.
|
|
//state.next = state.codes;
|
|
//state.lencode = state.next;
|
|
// Switch to use dynamic table
|
|
|
|
|
|
state.lencode = state.lendyn;
|
|
state.lenbits = 7;
|
|
opts = {
|
|
bits: state.lenbits
|
|
};
|
|
ret = inftrees(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts);
|
|
state.lenbits = opts.bits;
|
|
|
|
if (ret) {
|
|
strm.msg = 'invalid code lengths set';
|
|
state.mode = BAD;
|
|
break;
|
|
} //Tracev((stderr, "inflate: code lengths ok\n"));
|
|
|
|
|
|
state.have = 0;
|
|
state.mode = CODELENS;
|
|
|
|
/* falls through */
|
|
|
|
case CODELENS:
|
|
while (state.have < state.nlen + state.ndist) {
|
|
for (;;) {
|
|
here = state.lencode[hold & (1 << state.lenbits) - 1];
|
|
/*BITS(state.lenbits)*/
|
|
|
|
here_bits = here >>> 24;
|
|
here_op = here >>> 16 & 0xff;
|
|
here_val = here & 0xffff;
|
|
|
|
if (here_bits <= bits) {
|
|
break;
|
|
} //--- PULLBYTE() ---//
|
|
|
|
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--;
|
|
hold += input[next++] << bits;
|
|
bits += 8; //---//
|
|
}
|
|
|
|
if (here_val < 16) {
|
|
//--- DROPBITS(here.bits) ---//
|
|
hold >>>= here_bits;
|
|
bits -= here_bits; //---//
|
|
|
|
state.lens[state.have++] = here_val;
|
|
} else {
|
|
if (here_val === 16) {
|
|
//=== NEEDBITS(here.bits + 2);
|
|
n = here_bits + 2;
|
|
|
|
while (bits < n) {
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--;
|
|
hold += input[next++] << bits;
|
|
bits += 8;
|
|
} //===//
|
|
//--- DROPBITS(here.bits) ---//
|
|
|
|
|
|
hold >>>= here_bits;
|
|
bits -= here_bits; //---//
|
|
|
|
if (state.have === 0) {
|
|
strm.msg = 'invalid bit length repeat';
|
|
state.mode = BAD;
|
|
break;
|
|
}
|
|
|
|
len = state.lens[state.have - 1];
|
|
copy = 3 + (hold & 0x03); //BITS(2);
|
|
//--- DROPBITS(2) ---//
|
|
|
|
hold >>>= 2;
|
|
bits -= 2; //---//
|
|
} else if (here_val === 17) {
|
|
//=== NEEDBITS(here.bits + 3);
|
|
n = here_bits + 3;
|
|
|
|
while (bits < n) {
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--;
|
|
hold += input[next++] << bits;
|
|
bits += 8;
|
|
} //===//
|
|
//--- DROPBITS(here.bits) ---//
|
|
|
|
|
|
hold >>>= here_bits;
|
|
bits -= here_bits; //---//
|
|
|
|
len = 0;
|
|
copy = 3 + (hold & 0x07); //BITS(3);
|
|
//--- DROPBITS(3) ---//
|
|
|
|
hold >>>= 3;
|
|
bits -= 3; //---//
|
|
} else {
|
|
//=== NEEDBITS(here.bits + 7);
|
|
n = here_bits + 7;
|
|
|
|
while (bits < n) {
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--;
|
|
hold += input[next++] << bits;
|
|
bits += 8;
|
|
} //===//
|
|
//--- DROPBITS(here.bits) ---//
|
|
|
|
|
|
hold >>>= here_bits;
|
|
bits -= here_bits; //---//
|
|
|
|
len = 0;
|
|
copy = 11 + (hold & 0x7f); //BITS(7);
|
|
//--- DROPBITS(7) ---//
|
|
|
|
hold >>>= 7;
|
|
bits -= 7; //---//
|
|
}
|
|
|
|
if (state.have + copy > state.nlen + state.ndist) {
|
|
strm.msg = 'invalid bit length repeat';
|
|
state.mode = BAD;
|
|
break;
|
|
}
|
|
|
|
while (copy--) {
|
|
state.lens[state.have++] = len;
|
|
}
|
|
}
|
|
}
|
|
/* handle error breaks in while */
|
|
|
|
|
|
if (state.mode === BAD) {
|
|
break;
|
|
}
|
|
/* check for end-of-block code (better have one) */
|
|
|
|
|
|
if (state.lens[256] === 0) {
|
|
strm.msg = 'invalid code -- missing end-of-block';
|
|
state.mode = BAD;
|
|
break;
|
|
}
|
|
/* build code tables -- note: do not change the lenbits or distbits
|
|
values here (9 and 6) without reading the comments in inftrees.h
|
|
concerning the ENOUGH constants, which depend on those values */
|
|
|
|
|
|
state.lenbits = 9;
|
|
opts = {
|
|
bits: state.lenbits
|
|
};
|
|
ret = inftrees(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts); // We have separate tables & no pointers. 2 commented lines below not needed.
|
|
// state.next_index = opts.table_index;
|
|
|
|
state.lenbits = opts.bits; // state.lencode = state.next;
|
|
|
|
if (ret) {
|
|
strm.msg = 'invalid literal/lengths set';
|
|
state.mode = BAD;
|
|
break;
|
|
}
|
|
|
|
state.distbits = 6; //state.distcode.copy(state.codes);
|
|
// Switch to use dynamic table
|
|
|
|
state.distcode = state.distdyn;
|
|
opts = {
|
|
bits: state.distbits
|
|
};
|
|
ret = inftrees(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts); // We have separate tables & no pointers. 2 commented lines below not needed.
|
|
// state.next_index = opts.table_index;
|
|
|
|
state.distbits = opts.bits; // state.distcode = state.next;
|
|
|
|
if (ret) {
|
|
strm.msg = 'invalid distances set';
|
|
state.mode = BAD;
|
|
break;
|
|
} //Tracev((stderr, 'inflate: codes ok\n'));
|
|
|
|
|
|
state.mode = LEN_;
|
|
|
|
if (flush === Z_TREES) {
|
|
break inf_leave;
|
|
}
|
|
|
|
/* falls through */
|
|
|
|
case LEN_:
|
|
state.mode = LEN;
|
|
|
|
/* falls through */
|
|
|
|
case LEN:
|
|
if (have >= 6 && left >= 258) {
|
|
//--- RESTORE() ---
|
|
strm.next_out = put;
|
|
strm.avail_out = left;
|
|
strm.next_in = next;
|
|
strm.avail_in = have;
|
|
state.hold = hold;
|
|
state.bits = bits; //---
|
|
|
|
inffast(strm, _out); //--- LOAD() ---
|
|
|
|
put = strm.next_out;
|
|
output = strm.output;
|
|
left = strm.avail_out;
|
|
next = strm.next_in;
|
|
input = strm.input;
|
|
have = strm.avail_in;
|
|
hold = state.hold;
|
|
bits = state.bits; //---
|
|
|
|
if (state.mode === TYPE) {
|
|
state.back = -1;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
state.back = 0;
|
|
|
|
for (;;) {
|
|
here = state.lencode[hold & (1 << state.lenbits) - 1];
|
|
/*BITS(state.lenbits)*/
|
|
|
|
here_bits = here >>> 24;
|
|
here_op = here >>> 16 & 0xff;
|
|
here_val = here & 0xffff;
|
|
|
|
if (here_bits <= bits) {
|
|
break;
|
|
} //--- PULLBYTE() ---//
|
|
|
|
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--;
|
|
hold += input[next++] << bits;
|
|
bits += 8; //---//
|
|
}
|
|
|
|
if (here_op && (here_op & 0xf0) === 0) {
|
|
last_bits = here_bits;
|
|
last_op = here_op;
|
|
last_val = here_val;
|
|
|
|
for (;;) {
|
|
here = state.lencode[last_val + ((hold & (1 << last_bits + last_op) - 1
|
|
/*BITS(last.bits + last.op)*/
|
|
) >> last_bits)];
|
|
here_bits = here >>> 24;
|
|
here_op = here >>> 16 & 0xff;
|
|
here_val = here & 0xffff;
|
|
|
|
if (last_bits + here_bits <= bits) {
|
|
break;
|
|
} //--- PULLBYTE() ---//
|
|
|
|
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--;
|
|
hold += input[next++] << bits;
|
|
bits += 8; //---//
|
|
} //--- DROPBITS(last.bits) ---//
|
|
|
|
|
|
hold >>>= last_bits;
|
|
bits -= last_bits; //---//
|
|
|
|
state.back += last_bits;
|
|
} //--- DROPBITS(here.bits) ---//
|
|
|
|
|
|
hold >>>= here_bits;
|
|
bits -= here_bits; //---//
|
|
|
|
state.back += here_bits;
|
|
state.length = here_val;
|
|
|
|
if (here_op === 0) {
|
|
//Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
|
|
// "inflate: literal '%c'\n" :
|
|
// "inflate: literal 0x%02x\n", here.val));
|
|
state.mode = LIT;
|
|
break;
|
|
}
|
|
|
|
if (here_op & 32) {
|
|
//Tracevv((stderr, "inflate: end of block\n"));
|
|
state.back = -1;
|
|
state.mode = TYPE;
|
|
break;
|
|
}
|
|
|
|
if (here_op & 64) {
|
|
strm.msg = 'invalid literal/length code';
|
|
state.mode = BAD;
|
|
break;
|
|
}
|
|
|
|
state.extra = here_op & 15;
|
|
state.mode = LENEXT;
|
|
|
|
/* falls through */
|
|
|
|
case LENEXT:
|
|
if (state.extra) {
|
|
//=== NEEDBITS(state.extra);
|
|
n = state.extra;
|
|
|
|
while (bits < n) {
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--;
|
|
hold += input[next++] << bits;
|
|
bits += 8;
|
|
} //===//
|
|
|
|
|
|
state.length += hold & (1 << state.extra) - 1
|
|
/*BITS(state.extra)*/
|
|
; //--- DROPBITS(state.extra) ---//
|
|
|
|
hold >>>= state.extra;
|
|
bits -= state.extra; //---//
|
|
|
|
state.back += state.extra;
|
|
} //Tracevv((stderr, "inflate: length %u\n", state.length));
|
|
|
|
|
|
state.was = state.length;
|
|
state.mode = DIST;
|
|
|
|
/* falls through */
|
|
|
|
case DIST:
|
|
for (;;) {
|
|
here = state.distcode[hold & (1 << state.distbits) - 1];
|
|
/*BITS(state.distbits)*/
|
|
|
|
here_bits = here >>> 24;
|
|
here_op = here >>> 16 & 0xff;
|
|
here_val = here & 0xffff;
|
|
|
|
if (here_bits <= bits) {
|
|
break;
|
|
} //--- PULLBYTE() ---//
|
|
|
|
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--;
|
|
hold += input[next++] << bits;
|
|
bits += 8; //---//
|
|
}
|
|
|
|
if ((here_op & 0xf0) === 0) {
|
|
last_bits = here_bits;
|
|
last_op = here_op;
|
|
last_val = here_val;
|
|
|
|
for (;;) {
|
|
here = state.distcode[last_val + ((hold & (1 << last_bits + last_op) - 1
|
|
/*BITS(last.bits + last.op)*/
|
|
) >> last_bits)];
|
|
here_bits = here >>> 24;
|
|
here_op = here >>> 16 & 0xff;
|
|
here_val = here & 0xffff;
|
|
|
|
if (last_bits + here_bits <= bits) {
|
|
break;
|
|
} //--- PULLBYTE() ---//
|
|
|
|
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--;
|
|
hold += input[next++] << bits;
|
|
bits += 8; //---//
|
|
} //--- DROPBITS(last.bits) ---//
|
|
|
|
|
|
hold >>>= last_bits;
|
|
bits -= last_bits; //---//
|
|
|
|
state.back += last_bits;
|
|
} //--- DROPBITS(here.bits) ---//
|
|
|
|
|
|
hold >>>= here_bits;
|
|
bits -= here_bits; //---//
|
|
|
|
state.back += here_bits;
|
|
|
|
if (here_op & 64) {
|
|
strm.msg = 'invalid distance code';
|
|
state.mode = BAD;
|
|
break;
|
|
}
|
|
|
|
state.offset = here_val;
|
|
state.extra = here_op & 15;
|
|
state.mode = DISTEXT;
|
|
|
|
/* falls through */
|
|
|
|
case DISTEXT:
|
|
if (state.extra) {
|
|
//=== NEEDBITS(state.extra);
|
|
n = state.extra;
|
|
|
|
while (bits < n) {
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--;
|
|
hold += input[next++] << bits;
|
|
bits += 8;
|
|
} //===//
|
|
|
|
|
|
state.offset += hold & (1 << state.extra) - 1
|
|
/*BITS(state.extra)*/
|
|
; //--- DROPBITS(state.extra) ---//
|
|
|
|
hold >>>= state.extra;
|
|
bits -= state.extra; //---//
|
|
|
|
state.back += state.extra;
|
|
} //#ifdef INFLATE_STRICT
|
|
|
|
|
|
if (state.offset > state.dmax) {
|
|
strm.msg = 'invalid distance too far back';
|
|
state.mode = BAD;
|
|
break;
|
|
} //#endif
|
|
//Tracevv((stderr, "inflate: distance %u\n", state.offset));
|
|
|
|
|
|
state.mode = MATCH;
|
|
|
|
/* falls through */
|
|
|
|
case MATCH:
|
|
if (left === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
copy = _out - left;
|
|
|
|
if (state.offset > copy) {
|
|
/* copy from window */
|
|
copy = state.offset - copy;
|
|
|
|
if (copy > state.whave) {
|
|
if (state.sane) {
|
|
strm.msg = 'invalid distance too far back';
|
|
state.mode = BAD;
|
|
break;
|
|
} // (!) This block is disabled in zlib defaults,
|
|
// don't enable it for binary compatibility
|
|
//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
|
|
// Trace((stderr, "inflate.c too far\n"));
|
|
// copy -= state.whave;
|
|
// if (copy > state.length) { copy = state.length; }
|
|
// if (copy > left) { copy = left; }
|
|
// left -= copy;
|
|
// state.length -= copy;
|
|
// do {
|
|
// output[put++] = 0;
|
|
// } while (--copy);
|
|
// if (state.length === 0) { state.mode = LEN; }
|
|
// break;
|
|
//#endif
|
|
|
|
}
|
|
|
|
if (copy > state.wnext) {
|
|
copy -= state.wnext;
|
|
from = state.wsize - copy;
|
|
} else {
|
|
from = state.wnext - copy;
|
|
}
|
|
|
|
if (copy > state.length) {
|
|
copy = state.length;
|
|
}
|
|
|
|
from_source = state.window;
|
|
} else {
|
|
/* copy from output */
|
|
from_source = output;
|
|
from = put - state.offset;
|
|
copy = state.length;
|
|
}
|
|
|
|
if (copy > left) {
|
|
copy = left;
|
|
}
|
|
|
|
left -= copy;
|
|
state.length -= copy;
|
|
|
|
do {
|
|
output[put++] = from_source[from++];
|
|
} while (--copy);
|
|
|
|
if (state.length === 0) {
|
|
state.mode = LEN;
|
|
}
|
|
|
|
break;
|
|
|
|
case LIT:
|
|
if (left === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
output[put++] = state.length;
|
|
left--;
|
|
state.mode = LEN;
|
|
break;
|
|
|
|
case CHECK:
|
|
if (state.wrap) {
|
|
//=== NEEDBITS(32);
|
|
while (bits < 32) {
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--; // Use '|' instead of '+' to make sure that result is signed
|
|
|
|
hold |= input[next++] << bits;
|
|
bits += 8;
|
|
} //===//
|
|
|
|
|
|
_out -= left;
|
|
strm.total_out += _out;
|
|
state.total += _out;
|
|
|
|
if (_out) {
|
|
strm.adler = state.check =
|
|
/*UPDATE(state.check, put - _out, _out);*/
|
|
state.flags ? crc32_1(state.check, output, _out, put - _out) : adler32_1(state.check, output, _out, put - _out);
|
|
}
|
|
|
|
_out = left; // NB: crc32 stored as signed 32-bit int, zswap32 returns signed too
|
|
|
|
if ((state.flags ? hold : zswap32(hold)) !== state.check) {
|
|
strm.msg = 'incorrect data check';
|
|
state.mode = BAD;
|
|
break;
|
|
} //=== INITBITS();
|
|
|
|
|
|
hold = 0;
|
|
bits = 0; //===//
|
|
//Tracev((stderr, "inflate: check matches trailer\n"));
|
|
}
|
|
|
|
state.mode = LENGTH;
|
|
|
|
/* falls through */
|
|
|
|
case LENGTH:
|
|
if (state.wrap && state.flags) {
|
|
//=== NEEDBITS(32);
|
|
while (bits < 32) {
|
|
if (have === 0) {
|
|
break inf_leave;
|
|
}
|
|
|
|
have--;
|
|
hold += input[next++] << bits;
|
|
bits += 8;
|
|
} //===//
|
|
|
|
|
|
if (hold !== (state.total & 0xffffffff)) {
|
|
strm.msg = 'incorrect length check';
|
|
state.mode = BAD;
|
|
break;
|
|
} //=== INITBITS();
|
|
|
|
|
|
hold = 0;
|
|
bits = 0; //===//
|
|
//Tracev((stderr, "inflate: length matches trailer\n"));
|
|
}
|
|
|
|
state.mode = DONE;
|
|
|
|
/* falls through */
|
|
|
|
case DONE:
|
|
ret = Z_STREAM_END$1;
|
|
break inf_leave;
|
|
|
|
case BAD:
|
|
ret = Z_DATA_ERROR$1;
|
|
break inf_leave;
|
|
|
|
case MEM:
|
|
return Z_MEM_ERROR$1;
|
|
|
|
case SYNC:
|
|
/* falls through */
|
|
|
|
default:
|
|
return Z_STREAM_ERROR$1;
|
|
}
|
|
} // inf_leave <- here is real place for "goto inf_leave", emulated via "break inf_leave"
|
|
|
|
/*
|
|
Return from inflate(), updating the total counts and the check value.
|
|
If there was no progress during the inflate() call, return a buffer
|
|
error. Call updatewindow() to create and/or update the window state.
|
|
Note: a memory error from inflate() is non-recoverable.
|
|
*/
|
|
//--- RESTORE() ---
|
|
|
|
|
|
strm.next_out = put;
|
|
strm.avail_out = left;
|
|
strm.next_in = next;
|
|
strm.avail_in = have;
|
|
state.hold = hold;
|
|
state.bits = bits; //---
|
|
|
|
if (state.wsize || _out !== strm.avail_out && state.mode < BAD && (state.mode < CHECK || flush !== Z_FINISH$1)) {
|
|
if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) ;
|
|
}
|
|
|
|
_in -= strm.avail_in;
|
|
_out -= strm.avail_out;
|
|
strm.total_in += _in;
|
|
strm.total_out += _out;
|
|
state.total += _out;
|
|
|
|
if (state.wrap && _out) {
|
|
strm.adler = state.check =
|
|
/*UPDATE(state.check, strm.next_out - _out, _out);*/
|
|
state.flags ? crc32_1(state.check, output, _out, strm.next_out - _out) : adler32_1(state.check, output, _out, strm.next_out - _out);
|
|
}
|
|
|
|
strm.data_type = state.bits + (state.last ? 64 : 0) + (state.mode === TYPE ? 128 : 0) + (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0);
|
|
|
|
if ((_in === 0 && _out === 0 || flush === Z_FINISH$1) && ret === Z_OK$1) {
|
|
ret = Z_BUF_ERROR;
|
|
}
|
|
|
|
return ret;
|
|
};
|
|
|
|
const inflateEnd = strm => {
|
|
if (!strm || !strm.state
|
|
/*|| strm->zfree == (free_func)0*/
|
|
) {
|
|
return Z_STREAM_ERROR$1;
|
|
}
|
|
|
|
let state = strm.state;
|
|
|
|
if (state.window) {
|
|
state.window = null;
|
|
}
|
|
|
|
strm.state = null;
|
|
return Z_OK$1;
|
|
};
|
|
|
|
const inflateGetHeader = (strm, head) => {
|
|
/* check state */
|
|
if (!strm || !strm.state) {
|
|
return Z_STREAM_ERROR$1;
|
|
}
|
|
|
|
const state = strm.state;
|
|
|
|
if ((state.wrap & 2) === 0) {
|
|
return Z_STREAM_ERROR$1;
|
|
}
|
|
/* save header structure */
|
|
|
|
|
|
state.head = head;
|
|
head.done = false;
|
|
return Z_OK$1;
|
|
};
|
|
|
|
const inflateSetDictionary = (strm, dictionary) => {
|
|
const dictLength = dictionary.length;
|
|
let state;
|
|
let dictid;
|
|
let ret;
|
|
/* check state */
|
|
|
|
if (!strm
|
|
/* == Z_NULL */
|
|
|| !strm.state
|
|
/* == Z_NULL */
|
|
) {
|
|
return Z_STREAM_ERROR$1;
|
|
}
|
|
|
|
state = strm.state;
|
|
|
|
if (state.wrap !== 0 && state.mode !== DICT) {
|
|
return Z_STREAM_ERROR$1;
|
|
}
|
|
/* check for correct dictionary identifier */
|
|
|
|
|
|
if (state.mode === DICT) {
|
|
dictid = 1;
|
|
/* adler32(0, null, 0)*/
|
|
|
|
/* dictid = adler32(dictid, dictionary, dictLength); */
|
|
|
|
dictid = adler32_1(dictid, dictionary, dictLength, 0);
|
|
|
|
if (dictid !== state.check) {
|
|
return Z_DATA_ERROR$1;
|
|
}
|
|
}
|
|
/* copy dictionary to window using updatewindow(), which will amend the
|
|
existing dictionary if appropriate */
|
|
|
|
|
|
ret = updatewindow(strm, dictionary, dictLength, dictLength);
|
|
|
|
if (ret) {
|
|
state.mode = MEM;
|
|
return Z_MEM_ERROR$1;
|
|
}
|
|
|
|
state.havedict = 1; // Tracev((stderr, "inflate: dictionary set\n"));
|
|
|
|
return Z_OK$1;
|
|
};
|
|
|
|
var inflateReset_1 = inflateReset;
|
|
var inflateReset2_1 = inflateReset2;
|
|
var inflateResetKeep_1 = inflateResetKeep;
|
|
var inflateInit_1 = inflateInit;
|
|
var inflateInit2_1 = inflateInit2;
|
|
var inflate_2$1 = inflate$2;
|
|
var inflateEnd_1 = inflateEnd;
|
|
var inflateGetHeader_1 = inflateGetHeader;
|
|
var inflateSetDictionary_1 = inflateSetDictionary;
|
|
var inflateInfo = 'pako inflate (from Nodeca project)';
|
|
/* Not implemented
|
|
module.exports.inflateCopy = inflateCopy;
|
|
module.exports.inflateGetDictionary = inflateGetDictionary;
|
|
module.exports.inflateMark = inflateMark;
|
|
module.exports.inflatePrime = inflatePrime;
|
|
module.exports.inflateSync = inflateSync;
|
|
module.exports.inflateSyncPoint = inflateSyncPoint;
|
|
module.exports.inflateUndermine = inflateUndermine;
|
|
*/
|
|
|
|
var inflate_1$2 = {
|
|
inflateReset: inflateReset_1,
|
|
inflateReset2: inflateReset2_1,
|
|
inflateResetKeep: inflateResetKeep_1,
|
|
inflateInit: inflateInit_1,
|
|
inflateInit2: inflateInit2_1,
|
|
inflate: inflate_2$1,
|
|
inflateEnd: inflateEnd_1,
|
|
inflateGetHeader: inflateGetHeader_1,
|
|
inflateSetDictionary: inflateSetDictionary_1,
|
|
inflateInfo: inflateInfo
|
|
}; // (C) 1995-2013 Jean-loup Gailly and Mark Adler
|
|
// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
|
|
//
|
|
// This software is provided 'as-is', without any express or implied
|
|
// warranty. In no event will the authors be held liable for any damages
|
|
// arising from the use of this software.
|
|
//
|
|
// Permission is granted to anyone to use this software for any purpose,
|
|
// including commercial applications, and to alter it and redistribute it
|
|
// freely, subject to the following restrictions:
|
|
//
|
|
// 1. The origin of this software must not be misrepresented; you must not
|
|
// claim that you wrote the original software. If you use this software
|
|
// in a product, an acknowledgment in the product documentation would be
|
|
// appreciated but is not required.
|
|
// 2. Altered source versions must be plainly marked as such, and must not be
|
|
// misrepresented as being the original software.
|
|
// 3. This notice may not be removed or altered from any source distribution.
|
|
|
|
function GZheader() {
|
|
/* true if compressed data believed to be text */
|
|
this.text = 0;
|
|
/* modification time */
|
|
|
|
this.time = 0;
|
|
/* extra flags (not used when writing a gzip file) */
|
|
|
|
this.xflags = 0;
|
|
/* operating system */
|
|
|
|
this.os = 0;
|
|
/* pointer to extra field or Z_NULL if none */
|
|
|
|
this.extra = null;
|
|
/* extra field length (valid if extra != Z_NULL) */
|
|
|
|
this.extra_len = 0; // Actually, we don't need it in JS,
|
|
// but leave for few code modifications
|
|
//
|
|
// Setup limits is not necessary because in js we should not preallocate memory
|
|
// for inflate use constant limit in 65536 bytes
|
|
//
|
|
|
|
/* space at extra (only when reading header) */
|
|
// this.extra_max = 0;
|
|
|
|
/* pointer to zero-terminated file name or Z_NULL */
|
|
|
|
this.name = '';
|
|
/* space at name (only when reading header) */
|
|
// this.name_max = 0;
|
|
|
|
/* pointer to zero-terminated comment or Z_NULL */
|
|
|
|
this.comment = '';
|
|
/* space at comment (only when reading header) */
|
|
// this.comm_max = 0;
|
|
|
|
/* true if there was or will be a header crc */
|
|
|
|
this.hcrc = 0;
|
|
/* true when done reading gzip header (not used when writing a gzip file) */
|
|
|
|
this.done = false;
|
|
}
|
|
|
|
var gzheader = GZheader;
|
|
const toString = Object.prototype.toString;
|
|
/* Public constants ==========================================================*/
|
|
|
|
/* ===========================================================================*/
|
|
|
|
const {
|
|
Z_NO_FLUSH,
|
|
Z_FINISH,
|
|
Z_OK,
|
|
Z_STREAM_END,
|
|
Z_NEED_DICT,
|
|
Z_STREAM_ERROR,
|
|
Z_DATA_ERROR,
|
|
Z_MEM_ERROR
|
|
} = constants$2;
|
|
/* ===========================================================================*/
|
|
|
|
/**
|
|
* class Inflate
|
|
*
|
|
* Generic JS-style wrapper for zlib calls. If you don't need
|
|
* streaming behaviour - use more simple functions: [[inflate]]
|
|
* and [[inflateRaw]].
|
|
**/
|
|
|
|
/* internal
|
|
* inflate.chunks -> Array
|
|
*
|
|
* Chunks of output data, if [[Inflate#onData]] not overridden.
|
|
**/
|
|
|
|
/**
|
|
* Inflate.result -> Uint8Array|String
|
|
*
|
|
* Uncompressed result, generated by default [[Inflate#onData]]
|
|
* and [[Inflate#onEnd]] handlers. Filled after you push last chunk
|
|
* (call [[Inflate#push]] with `Z_FINISH` / `true` param).
|
|
**/
|
|
|
|
/**
|
|
* Inflate.err -> Number
|
|
*
|
|
* Error code after inflate finished. 0 (Z_OK) on success.
|
|
* Should be checked if broken data possible.
|
|
**/
|
|
|
|
/**
|
|
* Inflate.msg -> String
|
|
*
|
|
* Error message, if [[Inflate.err]] != 0
|
|
**/
|
|
|
|
/**
|
|
* new Inflate(options)
|
|
* - options (Object): zlib inflate options.
|
|
*
|
|
* Creates new inflator instance with specified params. Throws exception
|
|
* on bad params. Supported options:
|
|
*
|
|
* - `windowBits`
|
|
* - `dictionary`
|
|
*
|
|
* [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
|
|
* for more information on these.
|
|
*
|
|
* Additional options, for internal needs:
|
|
*
|
|
* - `chunkSize` - size of generated data chunks (16K by default)
|
|
* - `raw` (Boolean) - do raw inflate
|
|
* - `to` (String) - if equal to 'string', then result will be converted
|
|
* from utf8 to utf16 (javascript) string. When string output requested,
|
|
* chunk length can differ from `chunkSize`, depending on content.
|
|
*
|
|
* By default, when no options set, autodetect deflate/gzip data format via
|
|
* wrapper header.
|
|
*
|
|
* ##### Example:
|
|
*
|
|
* ```javascript
|
|
* const pako = require('pako')
|
|
* const chunk1 = new Uint8Array([1,2,3,4,5,6,7,8,9])
|
|
* const chunk2 = new Uint8Array([10,11,12,13,14,15,16,17,18,19]);
|
|
*
|
|
* const inflate = new pako.Inflate({ level: 3});
|
|
*
|
|
* inflate.push(chunk1, false);
|
|
* inflate.push(chunk2, true); // true -> last chunk
|
|
*
|
|
* if (inflate.err) { throw new Error(inflate.err); }
|
|
*
|
|
* console.log(inflate.result);
|
|
* ```
|
|
**/
|
|
|
|
function Inflate$1(options) {
|
|
this.options = common.assign({
|
|
chunkSize: 1024 * 64,
|
|
windowBits: 15,
|
|
to: ''
|
|
}, options || {});
|
|
const opt = this.options; // Force window size for `raw` data, if not set directly,
|
|
// because we have no header for autodetect.
|
|
|
|
if (opt.raw && opt.windowBits >= 0 && opt.windowBits < 16) {
|
|
opt.windowBits = -opt.windowBits;
|
|
|
|
if (opt.windowBits === 0) {
|
|
opt.windowBits = -15;
|
|
}
|
|
} // If `windowBits` not defined (and mode not raw) - set autodetect flag for gzip/deflate
|
|
|
|
|
|
if (opt.windowBits >= 0 && opt.windowBits < 16 && !(options && options.windowBits)) {
|
|
opt.windowBits += 32;
|
|
} // Gzip header has no info about windows size, we can do autodetect only
|
|
// for deflate. So, if window size not set, force it to max when gzip possible
|
|
|
|
|
|
if (opt.windowBits > 15 && opt.windowBits < 48) {
|
|
// bit 3 (16) -> gzipped data
|
|
// bit 4 (32) -> autodetect gzip/deflate
|
|
if ((opt.windowBits & 15) === 0) {
|
|
opt.windowBits |= 15;
|
|
}
|
|
}
|
|
|
|
this.err = 0; // error code, if happens (0 = Z_OK)
|
|
|
|
this.msg = ''; // error message
|
|
|
|
this.ended = false; // used to avoid multiple onEnd() calls
|
|
|
|
this.chunks = []; // chunks of compressed data
|
|
|
|
this.strm = new zstream();
|
|
this.strm.avail_out = 0;
|
|
let status = inflate_1$2.inflateInit2(this.strm, opt.windowBits);
|
|
|
|
if (status !== Z_OK) {
|
|
throw new Error(messages[status]);
|
|
}
|
|
|
|
this.header = new gzheader();
|
|
inflate_1$2.inflateGetHeader(this.strm, this.header); // Setup dictionary
|
|
|
|
if (opt.dictionary) {
|
|
// Convert data if needed
|
|
if (typeof opt.dictionary === 'string') {
|
|
opt.dictionary = strings.string2buf(opt.dictionary);
|
|
} else if (toString.call(opt.dictionary) === '[object ArrayBuffer]') {
|
|
opt.dictionary = new Uint8Array(opt.dictionary);
|
|
}
|
|
|
|
if (opt.raw) {
|
|
//In raw mode we need to set the dictionary early
|
|
status = inflate_1$2.inflateSetDictionary(this.strm, opt.dictionary);
|
|
|
|
if (status !== Z_OK) {
|
|
throw new Error(messages[status]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Inflate#push(data[, flush_mode]) -> Boolean
|
|
* - data (Uint8Array|ArrayBuffer): input data
|
|
* - flush_mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE
|
|
* flush modes. See constants. Skipped or `false` means Z_NO_FLUSH,
|
|
* `true` means Z_FINISH.
|
|
*
|
|
* Sends input data to inflate pipe, generating [[Inflate#onData]] calls with
|
|
* new output chunks. Returns `true` on success. If end of stream detected,
|
|
* [[Inflate#onEnd]] will be called.
|
|
*
|
|
* `flush_mode` is not needed for normal operation, because end of stream
|
|
* detected automatically. You may try to use it for advanced things, but
|
|
* this functionality was not tested.
|
|
*
|
|
* On fail call [[Inflate#onEnd]] with error code and return false.
|
|
*
|
|
* ##### Example
|
|
*
|
|
* ```javascript
|
|
* push(chunk, false); // push one of data chunks
|
|
* ...
|
|
* push(chunk, true); // push last chunk
|
|
* ```
|
|
**/
|
|
|
|
|
|
Inflate$1.prototype.push = function (data, flush_mode) {
|
|
const strm = this.strm;
|
|
const chunkSize = this.options.chunkSize;
|
|
const dictionary = this.options.dictionary;
|
|
|
|
let status, _flush_mode, last_avail_out;
|
|
|
|
if (this.ended) return false;
|
|
if (flush_mode === ~~flush_mode) _flush_mode = flush_mode;else _flush_mode = flush_mode === true ? Z_FINISH : Z_NO_FLUSH; // Convert data if needed
|
|
|
|
if (toString.call(data) === '[object ArrayBuffer]') {
|
|
strm.input = new Uint8Array(data);
|
|
} else {
|
|
strm.input = data;
|
|
}
|
|
|
|
strm.next_in = 0;
|
|
strm.avail_in = strm.input.length;
|
|
|
|
for (;;) {
|
|
if (strm.avail_out === 0) {
|
|
strm.output = new Uint8Array(chunkSize);
|
|
strm.next_out = 0;
|
|
strm.avail_out = chunkSize;
|
|
}
|
|
|
|
status = inflate_1$2.inflate(strm, _flush_mode);
|
|
|
|
if (status === Z_NEED_DICT && dictionary) {
|
|
status = inflate_1$2.inflateSetDictionary(strm, dictionary);
|
|
|
|
if (status === Z_OK) {
|
|
status = inflate_1$2.inflate(strm, _flush_mode);
|
|
} else if (status === Z_DATA_ERROR) {
|
|
// Replace code with more verbose
|
|
status = Z_NEED_DICT;
|
|
}
|
|
} // Skip snyc markers if more data follows and not raw mode
|
|
|
|
|
|
while (strm.avail_in > 0 && status === Z_STREAM_END && strm.state.wrap > 0 && data[strm.next_in] !== 0) {
|
|
inflate_1$2.inflateReset(strm);
|
|
status = inflate_1$2.inflate(strm, _flush_mode);
|
|
}
|
|
|
|
switch (status) {
|
|
case Z_STREAM_ERROR:
|
|
case Z_DATA_ERROR:
|
|
case Z_NEED_DICT:
|
|
case Z_MEM_ERROR:
|
|
this.onEnd(status);
|
|
this.ended = true;
|
|
return false;
|
|
} // Remember real `avail_out` value, because we may patch out buffer content
|
|
// to align utf8 strings boundaries.
|
|
|
|
|
|
last_avail_out = strm.avail_out;
|
|
|
|
if (strm.next_out) {
|
|
if (strm.avail_out === 0 || status === Z_STREAM_END) {
|
|
if (this.options.to === 'string') {
|
|
let next_out_utf8 = strings.utf8border(strm.output, strm.next_out);
|
|
let tail = strm.next_out - next_out_utf8;
|
|
let utf8str = strings.buf2string(strm.output, next_out_utf8); // move tail & realign counters
|
|
|
|
strm.next_out = tail;
|
|
strm.avail_out = chunkSize - tail;
|
|
if (tail) strm.output.set(strm.output.subarray(next_out_utf8, next_out_utf8 + tail), 0);
|
|
this.onData(utf8str);
|
|
} else {
|
|
this.onData(strm.output.length === strm.next_out ? strm.output : strm.output.subarray(0, strm.next_out));
|
|
}
|
|
}
|
|
} // Must repeat iteration if out buffer is full
|
|
|
|
|
|
if (status === Z_OK && last_avail_out === 0) continue; // Finalize if end of stream reached.
|
|
|
|
if (status === Z_STREAM_END) {
|
|
status = inflate_1$2.inflateEnd(this.strm);
|
|
this.onEnd(status);
|
|
this.ended = true;
|
|
return true;
|
|
}
|
|
|
|
if (strm.avail_in === 0) break;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
/**
|
|
* Inflate#onData(chunk) -> Void
|
|
* - chunk (Uint8Array|String): output data. When string output requested,
|
|
* each chunk will be string.
|
|
*
|
|
* By default, stores data blocks in `chunks[]` property and glue
|
|
* those in `onEnd`. Override this handler, if you need another behaviour.
|
|
**/
|
|
|
|
|
|
Inflate$1.prototype.onData = function (chunk) {
|
|
this.chunks.push(chunk);
|
|
};
|
|
/**
|
|
* Inflate#onEnd(status) -> Void
|
|
* - status (Number): inflate status. 0 (Z_OK) on success,
|
|
* other if not.
|
|
*
|
|
* Called either after you tell inflate that the input stream is
|
|
* complete (Z_FINISH). By default - join collected chunks,
|
|
* free memory and fill `results` / `err` properties.
|
|
**/
|
|
|
|
|
|
Inflate$1.prototype.onEnd = function (status) {
|
|
// On success - join
|
|
if (status === Z_OK) {
|
|
if (this.options.to === 'string') {
|
|
this.result = this.chunks.join('');
|
|
} else {
|
|
this.result = common.flattenChunks(this.chunks);
|
|
}
|
|
}
|
|
|
|
this.chunks = [];
|
|
this.err = status;
|
|
this.msg = this.strm.msg;
|
|
};
|
|
/**
|
|
* inflate(data[, options]) -> Uint8Array|String
|
|
* - data (Uint8Array): input data to decompress.
|
|
* - options (Object): zlib inflate options.
|
|
*
|
|
* Decompress `data` with inflate/ungzip and `options`. Autodetect
|
|
* format via wrapper header by default. That's why we don't provide
|
|
* separate `ungzip` method.
|
|
*
|
|
* Supported options are:
|
|
*
|
|
* - windowBits
|
|
*
|
|
* [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
|
|
* for more information.
|
|
*
|
|
* Sugar (options):
|
|
*
|
|
* - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify
|
|
* negative windowBits implicitly.
|
|
* - `to` (String) - if equal to 'string', then result will be converted
|
|
* from utf8 to utf16 (javascript) string. When string output requested,
|
|
* chunk length can differ from `chunkSize`, depending on content.
|
|
*
|
|
*
|
|
* ##### Example:
|
|
*
|
|
* ```javascript
|
|
* const pako = require('pako');
|
|
* const input = pako.deflate(new Uint8Array([1,2,3,4,5,6,7,8,9]));
|
|
* let output;
|
|
*
|
|
* try {
|
|
* output = pako.inflate(input);
|
|
* } catch (err) {
|
|
* console.log(err);
|
|
* }
|
|
* ```
|
|
**/
|
|
|
|
|
|
function inflate$1(input, options) {
|
|
const inflator = new Inflate$1(options);
|
|
inflator.push(input); // That will never happens, if you don't cheat with options :)
|
|
|
|
if (inflator.err) throw inflator.msg || messages[inflator.err];
|
|
return inflator.result;
|
|
}
|
|
/**
|
|
* inflateRaw(data[, options]) -> Uint8Array|String
|
|
* - data (Uint8Array): input data to decompress.
|
|
* - options (Object): zlib inflate options.
|
|
*
|
|
* The same as [[inflate]], but creates raw data, without wrapper
|
|
* (header and adler32 crc).
|
|
**/
|
|
|
|
|
|
function inflateRaw$1(input, options) {
|
|
options = options || {};
|
|
options.raw = true;
|
|
return inflate$1(input, options);
|
|
}
|
|
/**
|
|
* ungzip(data[, options]) -> Uint8Array|String
|
|
* - data (Uint8Array): input data to decompress.
|
|
* - options (Object): zlib inflate options.
|
|
*
|
|
* Just shortcut to [[inflate]], because it autodetects format
|
|
* by header.content. Done for convenience.
|
|
**/
|
|
|
|
|
|
var Inflate_1$1 = Inflate$1;
|
|
var inflate_2 = inflate$1;
|
|
var inflateRaw_1$1 = inflateRaw$1;
|
|
var ungzip$1 = inflate$1;
|
|
var constants = constants$2;
|
|
var inflate_1$1 = {
|
|
Inflate: Inflate_1$1,
|
|
inflate: inflate_2,
|
|
inflateRaw: inflateRaw_1$1,
|
|
ungzip: ungzip$1,
|
|
constants: constants
|
|
};
|
|
const {
|
|
Deflate,
|
|
deflate,
|
|
deflateRaw,
|
|
gzip
|
|
} = deflate_1$1;
|
|
const {
|
|
Inflate,
|
|
inflate,
|
|
inflateRaw,
|
|
ungzip
|
|
} = inflate_1$1;
|
|
var Deflate_1 = Deflate;
|
|
exports.Deflate = Deflate_1;
|
|
var deflate_1 = deflate;
|
|
exports.deflate = deflate_1;
|
|
var deflateRaw_1 = deflateRaw;
|
|
exports.deflateRaw = deflateRaw_1;
|
|
var gzip_1 = gzip;
|
|
exports.gzip = gzip_1;
|
|
var Inflate_1 = Inflate;
|
|
exports.Inflate = Inflate_1;
|
|
var inflate_1 = inflate;
|
|
exports.inflate = inflate_1;
|
|
var inflateRaw_1 = inflateRaw;
|
|
exports.inflateRaw = inflateRaw_1;
|
|
var ungzip_1 = ungzip;
|
|
exports.ungzip = ungzip_1;
|
|
var constants_1 = constants$2;
|
|
exports.constants = constants_1;
|
|
var pako = {
|
|
Deflate: Deflate_1,
|
|
deflate: deflate_1,
|
|
deflateRaw: deflateRaw_1,
|
|
gzip: gzip_1,
|
|
Inflate: Inflate_1,
|
|
inflate: inflate_1,
|
|
inflateRaw: inflateRaw_1,
|
|
ungzip: ungzip_1,
|
|
constants: constants_1
|
|
};
|
|
exports.default = pako;
|
|
|
|
},{}],19:[function(require,module,exports){
|
|
(function (Buffer){(function (){
|
|
(function() {
|
|
var crypt = require('crypt'),
|
|
utf8 = require('charenc').utf8,
|
|
bin = require('charenc').bin,
|
|
|
|
// The core
|
|
sha1 = function (message) {
|
|
// Convert to byte array
|
|
if (message.constructor == String)
|
|
message = utf8.stringToBytes(message);
|
|
else if (typeof Buffer !== 'undefined' && typeof Buffer.isBuffer == 'function' && Buffer.isBuffer(message))
|
|
message = Array.prototype.slice.call(message, 0);
|
|
else if (!Array.isArray(message))
|
|
message = message.toString();
|
|
|
|
// otherwise assume byte array
|
|
|
|
var m = crypt.bytesToWords(message),
|
|
l = message.length * 8,
|
|
w = [],
|
|
H0 = 1732584193,
|
|
H1 = -271733879,
|
|
H2 = -1732584194,
|
|
H3 = 271733878,
|
|
H4 = -1009589776;
|
|
|
|
// Padding
|
|
m[l >> 5] |= 0x80 << (24 - l % 32);
|
|
m[((l + 64 >>> 9) << 4) + 15] = l;
|
|
|
|
for (var i = 0; i < m.length; i += 16) {
|
|
var a = H0,
|
|
b = H1,
|
|
c = H2,
|
|
d = H3,
|
|
e = H4;
|
|
|
|
for (var j = 0; j < 80; j++) {
|
|
|
|
if (j < 16)
|
|
w[j] = m[i + j];
|
|
else {
|
|
var n = w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16];
|
|
w[j] = (n << 1) | (n >>> 31);
|
|
}
|
|
|
|
var t = ((H0 << 5) | (H0 >>> 27)) + H4 + (w[j] >>> 0) + (
|
|
j < 20 ? (H1 & H2 | ~H1 & H3) + 1518500249 :
|
|
j < 40 ? (H1 ^ H2 ^ H3) + 1859775393 :
|
|
j < 60 ? (H1 & H2 | H1 & H3 | H2 & H3) - 1894007588 :
|
|
(H1 ^ H2 ^ H3) - 899497514);
|
|
|
|
H4 = H3;
|
|
H3 = H2;
|
|
H2 = (H1 << 30) | (H1 >>> 2);
|
|
H1 = H0;
|
|
H0 = t;
|
|
}
|
|
|
|
H0 += a;
|
|
H1 += b;
|
|
H2 += c;
|
|
H3 += d;
|
|
H4 += e;
|
|
}
|
|
|
|
return [H0, H1, H2, H3, H4];
|
|
},
|
|
|
|
// Public API
|
|
api = function (message, options) {
|
|
var digestbytes = crypt.wordsToBytes(sha1(message));
|
|
return options && options.asBytes ? digestbytes :
|
|
options && options.asString ? bin.bytesToString(digestbytes) :
|
|
crypt.bytesToHex(digestbytes);
|
|
};
|
|
|
|
api._blocksize = 16;
|
|
api._digestsize = 20;
|
|
|
|
module.exports = api;
|
|
})();
|
|
|
|
}).call(this)}).call(this,require("buffer").Buffer)
|
|
},{"buffer":4,"charenc":5,"crypt":6}],20:[function(require,module,exports){
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.addExtensionsToContext = addExtensionsToContext;
|
|
exports.attributes = void 0;
|
|
exports.bindFramebufferInfo = bindFramebufferInfo;
|
|
exports.bindTransformFeedbackInfo = bindTransformFeedbackInfo;
|
|
exports.bindUniformBlock = bindUniformBlock;
|
|
exports.canFilter = canFilter;
|
|
exports.canGenerateMipmap = canGenerateMipmap;
|
|
exports.createAttribsFromArrays = createAttribsFromArrays;
|
|
exports.createAttributeSetters = createAttributeSetters;
|
|
exports.createBufferFromArray = createBufferFromArray;
|
|
exports.createBufferFromTypedArray = createBufferFromTypedArray;
|
|
exports.createBufferInfoFromArrays = createBufferInfoFromArrays;
|
|
exports.createBuffersFromArrays = createBuffersFromArrays;
|
|
exports.createFramebufferInfo = createFramebufferInfo;
|
|
exports.createProgram = createProgram;
|
|
exports.createProgramFromScripts = createProgramFromScripts;
|
|
exports.createProgramFromSources = createProgramFromSources;
|
|
exports.createProgramInfo = createProgramInfo;
|
|
exports.createProgramInfoFromProgram = createProgramInfoFromProgram;
|
|
exports.createSampler = createSampler;
|
|
exports.createSamplers = createSamplers;
|
|
exports.createTexture = createTexture;
|
|
exports.createTextures = createTextures;
|
|
exports.createTransformFeedback = createTransformFeedback;
|
|
exports.createTransformFeedbackInfo = createTransformFeedbackInfo;
|
|
exports.createUniformBlockInfo = createUniformBlockInfo;
|
|
exports.createUniformBlockInfoFromProgram = createUniformBlockInfoFromProgram;
|
|
exports.createUniformBlockSpecFromProgram = createUniformBlockSpecFromProgram;
|
|
exports.createUniformSetters = createUniformSetters;
|
|
exports.createVAOAndSetAttributes = createVAOAndSetAttributes;
|
|
exports.createVAOFromBufferInfo = createVAOFromBufferInfo;
|
|
exports.createVertexArrayInfo = createVertexArrayInfo;
|
|
exports.draw = void 0;
|
|
exports.drawBufferInfo = drawBufferInfo;
|
|
exports.drawObjectList = drawObjectList;
|
|
exports.framebuffers = void 0;
|
|
exports.getArray_ = getArray;
|
|
exports.getBytesPerElementForInternalFormat = getBytesPerElementForInternalFormat;
|
|
exports.getContext = getContext;
|
|
exports.getFormatAndTypeForInternalFormat = getFormatAndTypeForInternalFormat;
|
|
exports.getGLTypeForTypedArray = getGLTypeForTypedArray;
|
|
exports.getGLTypeForTypedArrayType = getGLTypeForTypedArrayType;
|
|
exports.getNumComponentsForFormat = getNumComponentsForFormat;
|
|
exports.getNumComponents_ = getNumComponents;
|
|
exports.getTypedArrayTypeForGLType = getTypedArrayTypeForGLType;
|
|
exports.getWebGLContext = getWebGLContext;
|
|
exports.isArrayBuffer = exports.glEnumToString = void 0;
|
|
exports.isWebGL1 = isWebGL1;
|
|
exports.isWebGL2 = isWebGL2;
|
|
exports.loadTextureFromUrl = loadTextureFromUrl;
|
|
exports.programs = exports.primitives = exports.m4 = void 0;
|
|
exports.resizeCanvasToDisplaySize = resizeCanvasToDisplaySize;
|
|
exports.resizeFramebufferInfo = resizeFramebufferInfo;
|
|
exports.resizeTexture = resizeTexture;
|
|
exports.setAttribInfoBufferFromArray = setAttribInfoBufferFromArray;
|
|
exports.setAttributeDefaults_ = setDefaults;
|
|
exports.setAttributePrefix = setAttributePrefix;
|
|
exports.setAttributes = setAttributes;
|
|
exports.setBlockUniforms = setBlockUniforms;
|
|
exports.setBuffersAndAttributes = setBuffersAndAttributes;
|
|
exports.setDefaultTextureColor = setDefaultTextureColor;
|
|
exports.setDefaults = setDefaults$2;
|
|
exports.setEmptyTexture = setEmptyTexture;
|
|
exports.setSamplerParameters = setSamplerParameters;
|
|
exports.setTextureDefaults_ = setDefaults$1;
|
|
exports.setTextureFilteringForSize = setTextureFilteringForSize;
|
|
exports.setTextureFromArray = setTextureFromArray;
|
|
exports.setTextureFromElement = setTextureFromElement;
|
|
exports.setTextureParameters = setTextureParameters;
|
|
exports.setUniformBlock = setUniformBlock;
|
|
exports.setUniforms = setUniforms;
|
|
exports.vertexArrays = exports.v3 = exports.utils = exports.typedarrays = exports.textures = exports.setUniformsAndBindTextures = void 0;
|
|
|
|
/* @license twgl.js 4.19.1 Copyright (c) 2015, Gregg Tavares All Rights Reserved.
|
|
Available via the MIT license.
|
|
see: http://github.com/greggman/twgl.js for details */
|
|
|
|
/*
|
|
* Copyright 2019 Gregg Tavares
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
/**
|
|
*
|
|
* Vec3 math math functions.
|
|
*
|
|
* Almost all functions take an optional `dst` argument. If it is not passed in the
|
|
* functions will create a new Vec3. In other words you can do this
|
|
*
|
|
* var v = v3.cross(v1, v2); // Creates a new Vec3 with the cross product of v1 x v2.
|
|
*
|
|
* or
|
|
*
|
|
* var v = v3.create();
|
|
* v3.cross(v1, v2, v); // Puts the cross product of v1 x v2 in v
|
|
*
|
|
* The first style is often easier but depending on where it's used it generates garbage where
|
|
* as there is almost never allocation with the second style.
|
|
*
|
|
* It is always save to pass any vector as the destination. So for example
|
|
*
|
|
* v3.cross(v1, v2, v1); // Puts the cross product of v1 x v2 in v1
|
|
*
|
|
* @module twgl/v3
|
|
*/
|
|
let VecType = Float32Array;
|
|
/**
|
|
* A JavaScript array with 3 values or a Float32Array with 3 values.
|
|
* When created by the library will create the default type which is `Float32Array`
|
|
* but can be set by calling {@link module:twgl/v3.setDefaultType}.
|
|
* @typedef {(number[]|Float32Array)} Vec3
|
|
* @memberOf module:twgl/v3
|
|
*/
|
|
|
|
/**
|
|
* Sets the type this library creates for a Vec3
|
|
* @param {constructor} ctor the constructor for the type. Either `Float32Array` or `Array`
|
|
* @return {constructor} previous constructor for Vec3
|
|
* @memberOf module:twgl/v3
|
|
*/
|
|
|
|
function setDefaultType(ctor) {
|
|
const oldType = VecType;
|
|
VecType = ctor;
|
|
return oldType;
|
|
}
|
|
/**
|
|
* Creates a vec3; may be called with x, y, z to set initial values.
|
|
* @param {number} [x] Initial x value.
|
|
* @param {number} [y] Initial y value.
|
|
* @param {number} [z] Initial z value.
|
|
* @return {module:twgl/v3.Vec3} the created vector
|
|
* @memberOf module:twgl/v3
|
|
*/
|
|
|
|
|
|
function create(x, y, z) {
|
|
const dst = new VecType(3);
|
|
|
|
if (x) {
|
|
dst[0] = x;
|
|
}
|
|
|
|
if (y) {
|
|
dst[1] = y;
|
|
}
|
|
|
|
if (z) {
|
|
dst[2] = z;
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
/**
|
|
* Adds two vectors; assumes a and b have the same dimension.
|
|
* @param {module:twgl/v3.Vec3} a Operand vector.
|
|
* @param {module:twgl/v3.Vec3} b Operand vector.
|
|
* @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created.
|
|
* @return {module:twgl/v3.Vec3} A vector tha tis the sum of a and b.
|
|
* @memberOf module:twgl/v3
|
|
*/
|
|
|
|
|
|
function add(a, b, dst) {
|
|
dst = dst || new VecType(3);
|
|
dst[0] = a[0] + b[0];
|
|
dst[1] = a[1] + b[1];
|
|
dst[2] = a[2] + b[2];
|
|
return dst;
|
|
}
|
|
/**
|
|
* Subtracts two vectors.
|
|
* @param {module:twgl/v3.Vec3} a Operand vector.
|
|
* @param {module:twgl/v3.Vec3} b Operand vector.
|
|
* @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created.
|
|
* @return {module:twgl/v3.Vec3} A vector that is the difference of a and b.
|
|
* @memberOf module:twgl/v3
|
|
*/
|
|
|
|
|
|
function subtract(a, b, dst) {
|
|
dst = dst || new VecType(3);
|
|
dst[0] = a[0] - b[0];
|
|
dst[1] = a[1] - b[1];
|
|
dst[2] = a[2] - b[2];
|
|
return dst;
|
|
}
|
|
/**
|
|
* Performs linear interpolation on two vectors.
|
|
* Given vectors a and b and interpolation coefficient t, returns
|
|
* a + t * (b - a).
|
|
* @param {module:twgl/v3.Vec3} a Operand vector.
|
|
* @param {module:twgl/v3.Vec3} b Operand vector.
|
|
* @param {number} t Interpolation coefficient.
|
|
* @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created.
|
|
* @return {module:twgl/v3.Vec3} The linear interpolated result.
|
|
* @memberOf module:twgl/v3
|
|
*/
|
|
|
|
|
|
function lerp(a, b, t, dst) {
|
|
dst = dst || new VecType(3);
|
|
dst[0] = a[0] + t * (b[0] - a[0]);
|
|
dst[1] = a[1] + t * (b[1] - a[1]);
|
|
dst[2] = a[2] + t * (b[2] - a[2]);
|
|
return dst;
|
|
}
|
|
/**
|
|
* Performs linear interpolation on two vectors.
|
|
* Given vectors a and b and interpolation coefficient vector t, returns
|
|
* a + t * (b - a).
|
|
* @param {module:twgl/v3.Vec3} a Operand vector.
|
|
* @param {module:twgl/v3.Vec3} b Operand vector.
|
|
* @param {module:twgl/v3.Vec3} t Interpolation coefficients vector.
|
|
* @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created.
|
|
* @return {module:twgl/v3.Vec3} the linear interpolated result.
|
|
* @memberOf module:twgl/v3
|
|
*/
|
|
|
|
|
|
function lerpV(a, b, t, dst) {
|
|
dst = dst || new VecType(3);
|
|
dst[0] = a[0] + t[0] * (b[0] - a[0]);
|
|
dst[1] = a[1] + t[1] * (b[1] - a[1]);
|
|
dst[2] = a[2] + t[2] * (b[2] - a[2]);
|
|
return dst;
|
|
}
|
|
/**
|
|
* Return max values of two vectors.
|
|
* Given vectors a and b returns
|
|
* [max(a[0], b[0]), max(a[1], b[1]), max(a[2], b[2])].
|
|
* @param {module:twgl/v3.Vec3} a Operand vector.
|
|
* @param {module:twgl/v3.Vec3} b Operand vector.
|
|
* @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created.
|
|
* @return {module:twgl/v3.Vec3} The max components vector.
|
|
* @memberOf module:twgl/v3
|
|
*/
|
|
|
|
|
|
function max(a, b, dst) {
|
|
dst = dst || new VecType(3);
|
|
dst[0] = Math.max(a[0], b[0]);
|
|
dst[1] = Math.max(a[1], b[1]);
|
|
dst[2] = Math.max(a[2], b[2]);
|
|
return dst;
|
|
}
|
|
/**
|
|
* Return min values of two vectors.
|
|
* Given vectors a and b returns
|
|
* [min(a[0], b[0]), min(a[1], b[1]), min(a[2], b[2])].
|
|
* @param {module:twgl/v3.Vec3} a Operand vector.
|
|
* @param {module:twgl/v3.Vec3} b Operand vector.
|
|
* @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created.
|
|
* @return {module:twgl/v3.Vec3} The min components vector.
|
|
* @memberOf module:twgl/v3
|
|
*/
|
|
|
|
|
|
function min(a, b, dst) {
|
|
dst = dst || new VecType(3);
|
|
dst[0] = Math.min(a[0], b[0]);
|
|
dst[1] = Math.min(a[1], b[1]);
|
|
dst[2] = Math.min(a[2], b[2]);
|
|
return dst;
|
|
}
|
|
/**
|
|
* Multiplies a vector by a scalar.
|
|
* @param {module:twgl/v3.Vec3} v The vector.
|
|
* @param {number} k The scalar.
|
|
* @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created.
|
|
* @return {module:twgl/v3.Vec3} The scaled vector.
|
|
* @memberOf module:twgl/v3
|
|
*/
|
|
|
|
|
|
function mulScalar(v, k, dst) {
|
|
dst = dst || new VecType(3);
|
|
dst[0] = v[0] * k;
|
|
dst[1] = v[1] * k;
|
|
dst[2] = v[2] * k;
|
|
return dst;
|
|
}
|
|
/**
|
|
* Divides a vector by a scalar.
|
|
* @param {module:twgl/v3.Vec3} v The vector.
|
|
* @param {number} k The scalar.
|
|
* @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created.
|
|
* @return {module:twgl/v3.Vec3} The scaled vector.
|
|
* @memberOf module:twgl/v3
|
|
*/
|
|
|
|
|
|
function divScalar(v, k, dst) {
|
|
dst = dst || new VecType(3);
|
|
dst[0] = v[0] / k;
|
|
dst[1] = v[1] / k;
|
|
dst[2] = v[2] / k;
|
|
return dst;
|
|
}
|
|
/**
|
|
* Computes the cross product of two vectors; assumes both vectors have
|
|
* three entries.
|
|
* @param {module:twgl/v3.Vec3} a Operand vector.
|
|
* @param {module:twgl/v3.Vec3} b Operand vector.
|
|
* @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created.
|
|
* @return {module:twgl/v3.Vec3} The vector of a cross b.
|
|
* @memberOf module:twgl/v3
|
|
*/
|
|
|
|
|
|
function cross(a, b, dst) {
|
|
dst = dst || new VecType(3);
|
|
const t1 = a[2] * b[0] - a[0] * b[2];
|
|
const t2 = a[0] * b[1] - a[1] * b[0];
|
|
dst[0] = a[1] * b[2] - a[2] * b[1];
|
|
dst[1] = t1;
|
|
dst[2] = t2;
|
|
return dst;
|
|
}
|
|
/**
|
|
* Computes the dot product of two vectors; assumes both vectors have
|
|
* three entries.
|
|
* @param {module:twgl/v3.Vec3} a Operand vector.
|
|
* @param {module:twgl/v3.Vec3} b Operand vector.
|
|
* @return {number} dot product
|
|
* @memberOf module:twgl/v3
|
|
*/
|
|
|
|
|
|
function dot(a, b) {
|
|
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
}
|
|
/**
|
|
* Computes the length of vector
|
|
* @param {module:twgl/v3.Vec3} v vector.
|
|
* @return {number} length of vector.
|
|
* @memberOf module:twgl/v3
|
|
*/
|
|
|
|
|
|
function length$1(v) {
|
|
return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
|
|
}
|
|
/**
|
|
* Computes the square of the length of vector
|
|
* @param {module:twgl/v3.Vec3} v vector.
|
|
* @return {number} square of the length of vector.
|
|
* @memberOf module:twgl/v3
|
|
*/
|
|
|
|
|
|
function lengthSq(v) {
|
|
return v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
|
|
}
|
|
/**
|
|
* Computes the distance between 2 points
|
|
* @param {module:twgl/v3.Vec3} a vector.
|
|
* @param {module:twgl/v3.Vec3} b vector.
|
|
* @return {number} distance between a and b
|
|
* @memberOf module:twgl/v3
|
|
*/
|
|
|
|
|
|
function distance(a, b) {
|
|
const dx = a[0] - b[0];
|
|
const dy = a[1] - b[1];
|
|
const dz = a[2] - b[2];
|
|
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
|
}
|
|
/**
|
|
* Computes the square of the distance between 2 points
|
|
* @param {module:twgl/v3.Vec3} a vector.
|
|
* @param {module:twgl/v3.Vec3} b vector.
|
|
* @return {number} square of the distance between a and b
|
|
* @memberOf module:twgl/v3
|
|
*/
|
|
|
|
|
|
function distanceSq(a, b) {
|
|
const dx = a[0] - b[0];
|
|
const dy = a[1] - b[1];
|
|
const dz = a[2] - b[2];
|
|
return dx * dx + dy * dy + dz * dz;
|
|
}
|
|
/**
|
|
* Divides a vector by its Euclidean length and returns the quotient.
|
|
* @param {module:twgl/v3.Vec3} a The vector.
|
|
* @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created.
|
|
* @return {module:twgl/v3.Vec3} The normalized vector.
|
|
* @memberOf module:twgl/v3
|
|
*/
|
|
|
|
|
|
function normalize(a, dst) {
|
|
dst = dst || new VecType(3);
|
|
const lenSq = a[0] * a[0] + a[1] * a[1] + a[2] * a[2];
|
|
const len = Math.sqrt(lenSq);
|
|
|
|
if (len > 0.00001) {
|
|
dst[0] = a[0] / len;
|
|
dst[1] = a[1] / len;
|
|
dst[2] = a[2] / len;
|
|
} else {
|
|
dst[0] = 0;
|
|
dst[1] = 0;
|
|
dst[2] = 0;
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
/**
|
|
* Negates a vector.
|
|
* @param {module:twgl/v3.Vec3} v The vector.
|
|
* @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created.
|
|
* @return {module:twgl/v3.Vec3} -v.
|
|
* @memberOf module:twgl/v3
|
|
*/
|
|
|
|
|
|
function negate(v, dst) {
|
|
dst = dst || new VecType(3);
|
|
dst[0] = -v[0];
|
|
dst[1] = -v[1];
|
|
dst[2] = -v[2];
|
|
return dst;
|
|
}
|
|
/**
|
|
* Copies a vector.
|
|
* @param {module:twgl/v3.Vec3} v The vector.
|
|
* @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created.
|
|
* @return {module:twgl/v3.Vec3} A copy of v.
|
|
* @memberOf module:twgl/v3
|
|
*/
|
|
|
|
|
|
function copy(v, dst) {
|
|
dst = dst || new VecType(3);
|
|
dst[0] = v[0];
|
|
dst[1] = v[1];
|
|
dst[2] = v[2];
|
|
return dst;
|
|
}
|
|
/**
|
|
* Multiplies a vector by another vector (component-wise); assumes a and
|
|
* b have the same length.
|
|
* @param {module:twgl/v3.Vec3} a Operand vector.
|
|
* @param {module:twgl/v3.Vec3} b Operand vector.
|
|
* @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created.
|
|
* @return {module:twgl/v3.Vec3} The vector of products of entries of a and
|
|
* b.
|
|
* @memberOf module:twgl/v3
|
|
*/
|
|
|
|
|
|
function multiply(a, b, dst) {
|
|
dst = dst || new VecType(3);
|
|
dst[0] = a[0] * b[0];
|
|
dst[1] = a[1] * b[1];
|
|
dst[2] = a[2] * b[2];
|
|
return dst;
|
|
}
|
|
/**
|
|
* Divides a vector by another vector (component-wise); assumes a and
|
|
* b have the same length.
|
|
* @param {module:twgl/v3.Vec3} a Operand vector.
|
|
* @param {module:twgl/v3.Vec3} b Operand vector.
|
|
* @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created.
|
|
* @return {module:twgl/v3.Vec3} The vector of quotients of entries of a and
|
|
* b.
|
|
* @memberOf module:twgl/v3
|
|
*/
|
|
|
|
|
|
function divide(a, b, dst) {
|
|
dst = dst || new VecType(3);
|
|
dst[0] = a[0] / b[0];
|
|
dst[1] = a[1] / b[1];
|
|
dst[2] = a[2] / b[2];
|
|
return dst;
|
|
}
|
|
|
|
var v3 = /*#__PURE__*/Object.freeze({
|
|
__proto__: null,
|
|
add: add,
|
|
copy: copy,
|
|
create: create,
|
|
cross: cross,
|
|
distance: distance,
|
|
distanceSq: distanceSq,
|
|
divide: divide,
|
|
divScalar: divScalar,
|
|
dot: dot,
|
|
lerp: lerp,
|
|
lerpV: lerpV,
|
|
length: length$1,
|
|
lengthSq: lengthSq,
|
|
max: max,
|
|
min: min,
|
|
mulScalar: mulScalar,
|
|
multiply: multiply,
|
|
negate: negate,
|
|
normalize: normalize,
|
|
setDefaultType: setDefaultType,
|
|
subtract: subtract
|
|
});
|
|
/*
|
|
* Copyright 2019 Gregg Tavares
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
/**
|
|
* 4x4 Matrix math math functions.
|
|
*
|
|
* Almost all functions take an optional `dst` argument. If it is not passed in the
|
|
* functions will create a new matrix. In other words you can do this
|
|
*
|
|
* const mat = m4.translation([1, 2, 3]); // Creates a new translation matrix
|
|
*
|
|
* or
|
|
*
|
|
* const mat = m4.create();
|
|
* m4.translation([1, 2, 3], mat); // Puts translation matrix in mat.
|
|
*
|
|
* The first style is often easier but depending on where it's used it generates garbage where
|
|
* as there is almost never allocation with the second style.
|
|
*
|
|
* It is always save to pass any matrix as the destination. So for example
|
|
*
|
|
* const mat = m4.identity();
|
|
* const trans = m4.translation([1, 2, 3]);
|
|
* m4.multiply(mat, trans, mat); // Multiplies mat * trans and puts result in mat.
|
|
*
|
|
* @module twgl/m4
|
|
*/
|
|
|
|
exports.v3 = v3;
|
|
let MatType = Float32Array;
|
|
/**
|
|
* A JavaScript array with 16 values or a Float32Array with 16 values.
|
|
* When created by the library will create the default type which is `Float32Array`
|
|
* but can be set by calling {@link module:twgl/m4.setDefaultType}.
|
|
* @typedef {(number[]|Float32Array)} Mat4
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
/**
|
|
* Sets the type this library creates for a Mat4
|
|
* @param {constructor} ctor the constructor for the type. Either `Float32Array` or `Array`
|
|
* @return {constructor} previous constructor for Mat4
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
function setDefaultType$1(ctor) {
|
|
const oldType = MatType;
|
|
MatType = ctor;
|
|
return oldType;
|
|
}
|
|
/**
|
|
* Negates a matrix.
|
|
* @param {module:twgl/m4.Mat4} m The matrix.
|
|
* @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} -m.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function negate$1(m, dst) {
|
|
dst = dst || new MatType(16);
|
|
dst[0] = -m[0];
|
|
dst[1] = -m[1];
|
|
dst[2] = -m[2];
|
|
dst[3] = -m[3];
|
|
dst[4] = -m[4];
|
|
dst[5] = -m[5];
|
|
dst[6] = -m[6];
|
|
dst[7] = -m[7];
|
|
dst[8] = -m[8];
|
|
dst[9] = -m[9];
|
|
dst[10] = -m[10];
|
|
dst[11] = -m[11];
|
|
dst[12] = -m[12];
|
|
dst[13] = -m[13];
|
|
dst[14] = -m[14];
|
|
dst[15] = -m[15];
|
|
return dst;
|
|
}
|
|
/**
|
|
* Copies a matrix.
|
|
* @param {module:twgl/m4.Mat4} m The matrix.
|
|
* @param {module:twgl/m4.Mat4} [dst] The matrix. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} A copy of m.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function copy$1(m, dst) {
|
|
dst = dst || new MatType(16);
|
|
dst[0] = m[0];
|
|
dst[1] = m[1];
|
|
dst[2] = m[2];
|
|
dst[3] = m[3];
|
|
dst[4] = m[4];
|
|
dst[5] = m[5];
|
|
dst[6] = m[6];
|
|
dst[7] = m[7];
|
|
dst[8] = m[8];
|
|
dst[9] = m[9];
|
|
dst[10] = m[10];
|
|
dst[11] = m[11];
|
|
dst[12] = m[12];
|
|
dst[13] = m[13];
|
|
dst[14] = m[14];
|
|
dst[15] = m[15];
|
|
return dst;
|
|
}
|
|
/**
|
|
* Creates an n-by-n identity matrix.
|
|
*
|
|
* @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} An n-by-n identity matrix.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function identity(dst) {
|
|
dst = dst || new MatType(16);
|
|
dst[0] = 1;
|
|
dst[1] = 0;
|
|
dst[2] = 0;
|
|
dst[3] = 0;
|
|
dst[4] = 0;
|
|
dst[5] = 1;
|
|
dst[6] = 0;
|
|
dst[7] = 0;
|
|
dst[8] = 0;
|
|
dst[9] = 0;
|
|
dst[10] = 1;
|
|
dst[11] = 0;
|
|
dst[12] = 0;
|
|
dst[13] = 0;
|
|
dst[14] = 0;
|
|
dst[15] = 1;
|
|
return dst;
|
|
}
|
|
/**
|
|
* Takes the transpose of a matrix.
|
|
* @param {module:twgl/m4.Mat4} m The matrix.
|
|
* @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} The transpose of m.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function transpose(m, dst) {
|
|
dst = dst || new MatType(16);
|
|
|
|
if (dst === m) {
|
|
let t;
|
|
t = m[1];
|
|
m[1] = m[4];
|
|
m[4] = t;
|
|
t = m[2];
|
|
m[2] = m[8];
|
|
m[8] = t;
|
|
t = m[3];
|
|
m[3] = m[12];
|
|
m[12] = t;
|
|
t = m[6];
|
|
m[6] = m[9];
|
|
m[9] = t;
|
|
t = m[7];
|
|
m[7] = m[13];
|
|
m[13] = t;
|
|
t = m[11];
|
|
m[11] = m[14];
|
|
m[14] = t;
|
|
return dst;
|
|
}
|
|
|
|
const m00 = m[0 * 4 + 0];
|
|
const m01 = m[0 * 4 + 1];
|
|
const m02 = m[0 * 4 + 2];
|
|
const m03 = m[0 * 4 + 3];
|
|
const m10 = m[1 * 4 + 0];
|
|
const m11 = m[1 * 4 + 1];
|
|
const m12 = m[1 * 4 + 2];
|
|
const m13 = m[1 * 4 + 3];
|
|
const m20 = m[2 * 4 + 0];
|
|
const m21 = m[2 * 4 + 1];
|
|
const m22 = m[2 * 4 + 2];
|
|
const m23 = m[2 * 4 + 3];
|
|
const m30 = m[3 * 4 + 0];
|
|
const m31 = m[3 * 4 + 1];
|
|
const m32 = m[3 * 4 + 2];
|
|
const m33 = m[3 * 4 + 3];
|
|
dst[0] = m00;
|
|
dst[1] = m10;
|
|
dst[2] = m20;
|
|
dst[3] = m30;
|
|
dst[4] = m01;
|
|
dst[5] = m11;
|
|
dst[6] = m21;
|
|
dst[7] = m31;
|
|
dst[8] = m02;
|
|
dst[9] = m12;
|
|
dst[10] = m22;
|
|
dst[11] = m32;
|
|
dst[12] = m03;
|
|
dst[13] = m13;
|
|
dst[14] = m23;
|
|
dst[15] = m33;
|
|
return dst;
|
|
}
|
|
/**
|
|
* Computes the inverse of a 4-by-4 matrix.
|
|
* @param {module:twgl/m4.Mat4} m The matrix.
|
|
* @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} The inverse of m.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function inverse(m, dst) {
|
|
dst = dst || new MatType(16);
|
|
const m00 = m[0 * 4 + 0];
|
|
const m01 = m[0 * 4 + 1];
|
|
const m02 = m[0 * 4 + 2];
|
|
const m03 = m[0 * 4 + 3];
|
|
const m10 = m[1 * 4 + 0];
|
|
const m11 = m[1 * 4 + 1];
|
|
const m12 = m[1 * 4 + 2];
|
|
const m13 = m[1 * 4 + 3];
|
|
const m20 = m[2 * 4 + 0];
|
|
const m21 = m[2 * 4 + 1];
|
|
const m22 = m[2 * 4 + 2];
|
|
const m23 = m[2 * 4 + 3];
|
|
const m30 = m[3 * 4 + 0];
|
|
const m31 = m[3 * 4 + 1];
|
|
const m32 = m[3 * 4 + 2];
|
|
const m33 = m[3 * 4 + 3];
|
|
const tmp_0 = m22 * m33;
|
|
const tmp_1 = m32 * m23;
|
|
const tmp_2 = m12 * m33;
|
|
const tmp_3 = m32 * m13;
|
|
const tmp_4 = m12 * m23;
|
|
const tmp_5 = m22 * m13;
|
|
const tmp_6 = m02 * m33;
|
|
const tmp_7 = m32 * m03;
|
|
const tmp_8 = m02 * m23;
|
|
const tmp_9 = m22 * m03;
|
|
const tmp_10 = m02 * m13;
|
|
const tmp_11 = m12 * m03;
|
|
const tmp_12 = m20 * m31;
|
|
const tmp_13 = m30 * m21;
|
|
const tmp_14 = m10 * m31;
|
|
const tmp_15 = m30 * m11;
|
|
const tmp_16 = m10 * m21;
|
|
const tmp_17 = m20 * m11;
|
|
const tmp_18 = m00 * m31;
|
|
const tmp_19 = m30 * m01;
|
|
const tmp_20 = m00 * m21;
|
|
const tmp_21 = m20 * m01;
|
|
const tmp_22 = m00 * m11;
|
|
const tmp_23 = m10 * m01;
|
|
const t0 = tmp_0 * m11 + tmp_3 * m21 + tmp_4 * m31 - (tmp_1 * m11 + tmp_2 * m21 + tmp_5 * m31);
|
|
const t1 = tmp_1 * m01 + tmp_6 * m21 + tmp_9 * m31 - (tmp_0 * m01 + tmp_7 * m21 + tmp_8 * m31);
|
|
const t2 = tmp_2 * m01 + tmp_7 * m11 + tmp_10 * m31 - (tmp_3 * m01 + tmp_6 * m11 + tmp_11 * m31);
|
|
const t3 = tmp_5 * m01 + tmp_8 * m11 + tmp_11 * m21 - (tmp_4 * m01 + tmp_9 * m11 + tmp_10 * m21);
|
|
const d = 1.0 / (m00 * t0 + m10 * t1 + m20 * t2 + m30 * t3);
|
|
dst[0] = d * t0;
|
|
dst[1] = d * t1;
|
|
dst[2] = d * t2;
|
|
dst[3] = d * t3;
|
|
dst[4] = d * (tmp_1 * m10 + tmp_2 * m20 + tmp_5 * m30 - (tmp_0 * m10 + tmp_3 * m20 + tmp_4 * m30));
|
|
dst[5] = d * (tmp_0 * m00 + tmp_7 * m20 + tmp_8 * m30 - (tmp_1 * m00 + tmp_6 * m20 + tmp_9 * m30));
|
|
dst[6] = d * (tmp_3 * m00 + tmp_6 * m10 + tmp_11 * m30 - (tmp_2 * m00 + tmp_7 * m10 + tmp_10 * m30));
|
|
dst[7] = d * (tmp_4 * m00 + tmp_9 * m10 + tmp_10 * m20 - (tmp_5 * m00 + tmp_8 * m10 + tmp_11 * m20));
|
|
dst[8] = d * (tmp_12 * m13 + tmp_15 * m23 + tmp_16 * m33 - (tmp_13 * m13 + tmp_14 * m23 + tmp_17 * m33));
|
|
dst[9] = d * (tmp_13 * m03 + tmp_18 * m23 + tmp_21 * m33 - (tmp_12 * m03 + tmp_19 * m23 + tmp_20 * m33));
|
|
dst[10] = d * (tmp_14 * m03 + tmp_19 * m13 + tmp_22 * m33 - (tmp_15 * m03 + tmp_18 * m13 + tmp_23 * m33));
|
|
dst[11] = d * (tmp_17 * m03 + tmp_20 * m13 + tmp_23 * m23 - (tmp_16 * m03 + tmp_21 * m13 + tmp_22 * m23));
|
|
dst[12] = d * (tmp_14 * m22 + tmp_17 * m32 + tmp_13 * m12 - (tmp_16 * m32 + tmp_12 * m12 + tmp_15 * m22));
|
|
dst[13] = d * (tmp_20 * m32 + tmp_12 * m02 + tmp_19 * m22 - (tmp_18 * m22 + tmp_21 * m32 + tmp_13 * m02));
|
|
dst[14] = d * (tmp_18 * m12 + tmp_23 * m32 + tmp_15 * m02 - (tmp_22 * m32 + tmp_14 * m02 + tmp_19 * m12));
|
|
dst[15] = d * (tmp_22 * m22 + tmp_16 * m02 + tmp_21 * m12 - (tmp_20 * m12 + tmp_23 * m22 + tmp_17 * m02));
|
|
return dst;
|
|
}
|
|
/**
|
|
* Multiplies two 4-by-4 matrices with a on the left and b on the right
|
|
* @param {module:twgl/m4.Mat4} a The matrix on the left.
|
|
* @param {module:twgl/m4.Mat4} b The matrix on the right.
|
|
* @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} The matrix product of a and b.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function multiply$1(a, b, dst) {
|
|
dst = dst || new MatType(16);
|
|
const a00 = a[0];
|
|
const a01 = a[1];
|
|
const a02 = a[2];
|
|
const a03 = a[3];
|
|
const a10 = a[4 + 0];
|
|
const a11 = a[4 + 1];
|
|
const a12 = a[4 + 2];
|
|
const a13 = a[4 + 3];
|
|
const a20 = a[8 + 0];
|
|
const a21 = a[8 + 1];
|
|
const a22 = a[8 + 2];
|
|
const a23 = a[8 + 3];
|
|
const a30 = a[12 + 0];
|
|
const a31 = a[12 + 1];
|
|
const a32 = a[12 + 2];
|
|
const a33 = a[12 + 3];
|
|
const b00 = b[0];
|
|
const b01 = b[1];
|
|
const b02 = b[2];
|
|
const b03 = b[3];
|
|
const b10 = b[4 + 0];
|
|
const b11 = b[4 + 1];
|
|
const b12 = b[4 + 2];
|
|
const b13 = b[4 + 3];
|
|
const b20 = b[8 + 0];
|
|
const b21 = b[8 + 1];
|
|
const b22 = b[8 + 2];
|
|
const b23 = b[8 + 3];
|
|
const b30 = b[12 + 0];
|
|
const b31 = b[12 + 1];
|
|
const b32 = b[12 + 2];
|
|
const b33 = b[12 + 3];
|
|
dst[0] = a00 * b00 + a10 * b01 + a20 * b02 + a30 * b03;
|
|
dst[1] = a01 * b00 + a11 * b01 + a21 * b02 + a31 * b03;
|
|
dst[2] = a02 * b00 + a12 * b01 + a22 * b02 + a32 * b03;
|
|
dst[3] = a03 * b00 + a13 * b01 + a23 * b02 + a33 * b03;
|
|
dst[4] = a00 * b10 + a10 * b11 + a20 * b12 + a30 * b13;
|
|
dst[5] = a01 * b10 + a11 * b11 + a21 * b12 + a31 * b13;
|
|
dst[6] = a02 * b10 + a12 * b11 + a22 * b12 + a32 * b13;
|
|
dst[7] = a03 * b10 + a13 * b11 + a23 * b12 + a33 * b13;
|
|
dst[8] = a00 * b20 + a10 * b21 + a20 * b22 + a30 * b23;
|
|
dst[9] = a01 * b20 + a11 * b21 + a21 * b22 + a31 * b23;
|
|
dst[10] = a02 * b20 + a12 * b21 + a22 * b22 + a32 * b23;
|
|
dst[11] = a03 * b20 + a13 * b21 + a23 * b22 + a33 * b23;
|
|
dst[12] = a00 * b30 + a10 * b31 + a20 * b32 + a30 * b33;
|
|
dst[13] = a01 * b30 + a11 * b31 + a21 * b32 + a31 * b33;
|
|
dst[14] = a02 * b30 + a12 * b31 + a22 * b32 + a32 * b33;
|
|
dst[15] = a03 * b30 + a13 * b31 + a23 * b32 + a33 * b33;
|
|
return dst;
|
|
}
|
|
/**
|
|
* Sets the translation component of a 4-by-4 matrix to the given
|
|
* vector.
|
|
* @param {module:twgl/m4.Mat4} a The matrix.
|
|
* @param {module:twgl/v3.Vec3} v The vector.
|
|
* @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} The matrix with translation set.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function setTranslation(a, v, dst) {
|
|
dst = dst || identity();
|
|
|
|
if (a !== dst) {
|
|
dst[0] = a[0];
|
|
dst[1] = a[1];
|
|
dst[2] = a[2];
|
|
dst[3] = a[3];
|
|
dst[4] = a[4];
|
|
dst[5] = a[5];
|
|
dst[6] = a[6];
|
|
dst[7] = a[7];
|
|
dst[8] = a[8];
|
|
dst[9] = a[9];
|
|
dst[10] = a[10];
|
|
dst[11] = a[11];
|
|
}
|
|
|
|
dst[12] = v[0];
|
|
dst[13] = v[1];
|
|
dst[14] = v[2];
|
|
dst[15] = 1;
|
|
return dst;
|
|
}
|
|
/**
|
|
* Returns the translation component of a 4-by-4 matrix as a vector with 3
|
|
* entries.
|
|
* @param {module:twgl/m4.Mat4} m The matrix.
|
|
* @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not passed a new one is created.
|
|
* @return {module:twgl/v3.Vec3} The translation component of m.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function getTranslation(m, dst) {
|
|
dst = dst || create();
|
|
dst[0] = m[12];
|
|
dst[1] = m[13];
|
|
dst[2] = m[14];
|
|
return dst;
|
|
}
|
|
/**
|
|
* Returns an axis of a 4x4 matrix as a vector with 3 entries
|
|
* @param {module:twgl/m4.Mat4} m The matrix.
|
|
* @param {number} axis The axis 0 = x, 1 = y, 2 = z;
|
|
* @return {module:twgl/v3.Vec3} [dst] vector.
|
|
* @return {module:twgl/v3.Vec3} The axis component of m.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function getAxis(m, axis, dst) {
|
|
dst = dst || create();
|
|
const off = axis * 4;
|
|
dst[0] = m[off + 0];
|
|
dst[1] = m[off + 1];
|
|
dst[2] = m[off + 2];
|
|
return dst;
|
|
}
|
|
/**
|
|
* Sets an axis of a 4x4 matrix as a vector with 3 entries
|
|
* @param {module:twgl/m4.Mat4} m The matrix.
|
|
* @param {module:twgl/v3.Vec3} v the axis vector
|
|
* @param {number} axis The axis 0 = x, 1 = y, 2 = z;
|
|
* @param {module:twgl/m4.Mat4} [dst] The matrix to set. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} The matrix with axis set.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function setAxis(a, v, axis, dst) {
|
|
if (dst !== a) {
|
|
dst = copy$1(a, dst);
|
|
}
|
|
|
|
const off = axis * 4;
|
|
dst[off + 0] = v[0];
|
|
dst[off + 1] = v[1];
|
|
dst[off + 2] = v[2];
|
|
return dst;
|
|
}
|
|
/**
|
|
* Computes a 4-by-4 perspective transformation matrix given the angular height
|
|
* of the frustum, the aspect ratio, and the near and far clipping planes. The
|
|
* arguments define a frustum extending in the negative z direction. The given
|
|
* angle is the vertical angle of the frustum, and the horizontal angle is
|
|
* determined to produce the given aspect ratio. The arguments near and far are
|
|
* the distances to the near and far clipping planes. Note that near and far
|
|
* are not z coordinates, but rather they are distances along the negative
|
|
* z-axis. The matrix generated sends the viewing frustum to the unit box.
|
|
* We assume a unit box extending from -1 to 1 in the x and y dimensions and
|
|
* from 0 to 1 in the z dimension.
|
|
* @param {number} fieldOfViewYInRadians The camera angle from top to bottom (in radians).
|
|
* @param {number} aspect The aspect ratio width / height.
|
|
* @param {number} zNear The depth (negative z coordinate)
|
|
* of the near clipping plane.
|
|
* @param {number} zFar The depth (negative z coordinate)
|
|
* of the far clipping plane.
|
|
* @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} The perspective matrix.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function perspective(fieldOfViewYInRadians, aspect, zNear, zFar, dst) {
|
|
dst = dst || new MatType(16);
|
|
const f = Math.tan(Math.PI * 0.5 - 0.5 * fieldOfViewYInRadians);
|
|
const rangeInv = 1.0 / (zNear - zFar);
|
|
dst[0] = f / aspect;
|
|
dst[1] = 0;
|
|
dst[2] = 0;
|
|
dst[3] = 0;
|
|
dst[4] = 0;
|
|
dst[5] = f;
|
|
dst[6] = 0;
|
|
dst[7] = 0;
|
|
dst[8] = 0;
|
|
dst[9] = 0;
|
|
dst[10] = (zNear + zFar) * rangeInv;
|
|
dst[11] = -1;
|
|
dst[12] = 0;
|
|
dst[13] = 0;
|
|
dst[14] = zNear * zFar * rangeInv * 2;
|
|
dst[15] = 0;
|
|
return dst;
|
|
}
|
|
/**
|
|
* Computes a 4-by-4 orthogonal transformation matrix given the left, right,
|
|
* bottom, and top dimensions of the near clipping plane as well as the
|
|
* near and far clipping plane distances.
|
|
* @param {number} left Left side of the near clipping plane viewport.
|
|
* @param {number} right Right side of the near clipping plane viewport.
|
|
* @param {number} bottom Bottom of the near clipping plane viewport.
|
|
* @param {number} top Top of the near clipping plane viewport.
|
|
* @param {number} near The depth (negative z coordinate)
|
|
* of the near clipping plane.
|
|
* @param {number} far The depth (negative z coordinate)
|
|
* of the far clipping plane.
|
|
* @param {module:twgl/m4.Mat4} [dst] Output matrix. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} The perspective matrix.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function ortho(left, right, bottom, top, near, far, dst) {
|
|
dst = dst || new MatType(16);
|
|
dst[0] = 2 / (right - left);
|
|
dst[1] = 0;
|
|
dst[2] = 0;
|
|
dst[3] = 0;
|
|
dst[4] = 0;
|
|
dst[5] = 2 / (top - bottom);
|
|
dst[6] = 0;
|
|
dst[7] = 0;
|
|
dst[8] = 0;
|
|
dst[9] = 0;
|
|
dst[10] = 2 / (near - far);
|
|
dst[11] = 0;
|
|
dst[12] = (right + left) / (left - right);
|
|
dst[13] = (top + bottom) / (bottom - top);
|
|
dst[14] = (far + near) / (near - far);
|
|
dst[15] = 1;
|
|
return dst;
|
|
}
|
|
/**
|
|
* Computes a 4-by-4 perspective transformation matrix given the left, right,
|
|
* top, bottom, near and far clipping planes. The arguments define a frustum
|
|
* extending in the negative z direction. The arguments near and far are the
|
|
* distances to the near and far clipping planes. Note that near and far are not
|
|
* z coordinates, but rather they are distances along the negative z-axis. The
|
|
* matrix generated sends the viewing frustum to the unit box. We assume a unit
|
|
* box extending from -1 to 1 in the x and y dimensions and from 0 to 1 in the z
|
|
* dimension.
|
|
* @param {number} left The x coordinate of the left plane of the box.
|
|
* @param {number} right The x coordinate of the right plane of the box.
|
|
* @param {number} bottom The y coordinate of the bottom plane of the box.
|
|
* @param {number} top The y coordinate of the right plane of the box.
|
|
* @param {number} near The negative z coordinate of the near plane of the box.
|
|
* @param {number} far The negative z coordinate of the far plane of the box.
|
|
* @param {module:twgl/m4.Mat4} [dst] Output matrix. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} The perspective projection matrix.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function frustum(left, right, bottom, top, near, far, dst) {
|
|
dst = dst || new MatType(16);
|
|
const dx = right - left;
|
|
const dy = top - bottom;
|
|
const dz = near - far;
|
|
dst[0] = 2 * near / dx;
|
|
dst[1] = 0;
|
|
dst[2] = 0;
|
|
dst[3] = 0;
|
|
dst[4] = 0;
|
|
dst[5] = 2 * near / dy;
|
|
dst[6] = 0;
|
|
dst[7] = 0;
|
|
dst[8] = (left + right) / dx;
|
|
dst[9] = (top + bottom) / dy;
|
|
dst[10] = far / dz;
|
|
dst[11] = -1;
|
|
dst[12] = 0;
|
|
dst[13] = 0;
|
|
dst[14] = near * far / dz;
|
|
dst[15] = 0;
|
|
return dst;
|
|
}
|
|
|
|
let xAxis;
|
|
let yAxis;
|
|
let zAxis;
|
|
/**
|
|
* Computes a 4-by-4 look-at transformation.
|
|
*
|
|
* This is a matrix which positions the camera itself. If you want
|
|
* a view matrix (a matrix which moves things in front of the camera)
|
|
* take the inverse of this.
|
|
*
|
|
* @param {module:twgl/v3.Vec3} eye The position of the eye.
|
|
* @param {module:twgl/v3.Vec3} target The position meant to be viewed.
|
|
* @param {module:twgl/v3.Vec3} up A vector pointing up.
|
|
* @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} The look-at matrix.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
function lookAt(eye, target, up, dst) {
|
|
dst = dst || new MatType(16);
|
|
xAxis = xAxis || create();
|
|
yAxis = yAxis || create();
|
|
zAxis = zAxis || create();
|
|
normalize(subtract(eye, target, zAxis), zAxis);
|
|
normalize(cross(up, zAxis, xAxis), xAxis);
|
|
normalize(cross(zAxis, xAxis, yAxis), yAxis);
|
|
dst[0] = xAxis[0];
|
|
dst[1] = xAxis[1];
|
|
dst[2] = xAxis[2];
|
|
dst[3] = 0;
|
|
dst[4] = yAxis[0];
|
|
dst[5] = yAxis[1];
|
|
dst[6] = yAxis[2];
|
|
dst[7] = 0;
|
|
dst[8] = zAxis[0];
|
|
dst[9] = zAxis[1];
|
|
dst[10] = zAxis[2];
|
|
dst[11] = 0;
|
|
dst[12] = eye[0];
|
|
dst[13] = eye[1];
|
|
dst[14] = eye[2];
|
|
dst[15] = 1;
|
|
return dst;
|
|
}
|
|
/**
|
|
* Creates a 4-by-4 matrix which translates by the given vector v.
|
|
* @param {module:twgl/v3.Vec3} v The vector by
|
|
* which to translate.
|
|
* @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} The translation matrix.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function translation(v, dst) {
|
|
dst = dst || new MatType(16);
|
|
dst[0] = 1;
|
|
dst[1] = 0;
|
|
dst[2] = 0;
|
|
dst[3] = 0;
|
|
dst[4] = 0;
|
|
dst[5] = 1;
|
|
dst[6] = 0;
|
|
dst[7] = 0;
|
|
dst[8] = 0;
|
|
dst[9] = 0;
|
|
dst[10] = 1;
|
|
dst[11] = 0;
|
|
dst[12] = v[0];
|
|
dst[13] = v[1];
|
|
dst[14] = v[2];
|
|
dst[15] = 1;
|
|
return dst;
|
|
}
|
|
/**
|
|
* Translates the given 4-by-4 matrix by the given vector v.
|
|
* @param {module:twgl/m4.Mat4} m The matrix.
|
|
* @param {module:twgl/v3.Vec3} v The vector by
|
|
* which to translate.
|
|
* @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} The translated matrix.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function translate(m, v, dst) {
|
|
dst = dst || new MatType(16);
|
|
const v0 = v[0];
|
|
const v1 = v[1];
|
|
const v2 = v[2];
|
|
const m00 = m[0];
|
|
const m01 = m[1];
|
|
const m02 = m[2];
|
|
const m03 = m[3];
|
|
const m10 = m[1 * 4 + 0];
|
|
const m11 = m[1 * 4 + 1];
|
|
const m12 = m[1 * 4 + 2];
|
|
const m13 = m[1 * 4 + 3];
|
|
const m20 = m[2 * 4 + 0];
|
|
const m21 = m[2 * 4 + 1];
|
|
const m22 = m[2 * 4 + 2];
|
|
const m23 = m[2 * 4 + 3];
|
|
const m30 = m[3 * 4 + 0];
|
|
const m31 = m[3 * 4 + 1];
|
|
const m32 = m[3 * 4 + 2];
|
|
const m33 = m[3 * 4 + 3];
|
|
|
|
if (m !== dst) {
|
|
dst[0] = m00;
|
|
dst[1] = m01;
|
|
dst[2] = m02;
|
|
dst[3] = m03;
|
|
dst[4] = m10;
|
|
dst[5] = m11;
|
|
dst[6] = m12;
|
|
dst[7] = m13;
|
|
dst[8] = m20;
|
|
dst[9] = m21;
|
|
dst[10] = m22;
|
|
dst[11] = m23;
|
|
}
|
|
|
|
dst[12] = m00 * v0 + m10 * v1 + m20 * v2 + m30;
|
|
dst[13] = m01 * v0 + m11 * v1 + m21 * v2 + m31;
|
|
dst[14] = m02 * v0 + m12 * v1 + m22 * v2 + m32;
|
|
dst[15] = m03 * v0 + m13 * v1 + m23 * v2 + m33;
|
|
return dst;
|
|
}
|
|
/**
|
|
* Creates a 4-by-4 matrix which rotates around the x-axis by the given angle.
|
|
* @param {number} angleInRadians The angle by which to rotate (in radians).
|
|
* @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} The rotation matrix.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function rotationX(angleInRadians, dst) {
|
|
dst = dst || new MatType(16);
|
|
const c = Math.cos(angleInRadians);
|
|
const s = Math.sin(angleInRadians);
|
|
dst[0] = 1;
|
|
dst[1] = 0;
|
|
dst[2] = 0;
|
|
dst[3] = 0;
|
|
dst[4] = 0;
|
|
dst[5] = c;
|
|
dst[6] = s;
|
|
dst[7] = 0;
|
|
dst[8] = 0;
|
|
dst[9] = -s;
|
|
dst[10] = c;
|
|
dst[11] = 0;
|
|
dst[12] = 0;
|
|
dst[13] = 0;
|
|
dst[14] = 0;
|
|
dst[15] = 1;
|
|
return dst;
|
|
}
|
|
/**
|
|
* Rotates the given 4-by-4 matrix around the x-axis by the given
|
|
* angle.
|
|
* @param {module:twgl/m4.Mat4} m The matrix.
|
|
* @param {number} angleInRadians The angle by which to rotate (in radians).
|
|
* @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} The rotated matrix.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function rotateX(m, angleInRadians, dst) {
|
|
dst = dst || new MatType(16);
|
|
const m10 = m[4];
|
|
const m11 = m[5];
|
|
const m12 = m[6];
|
|
const m13 = m[7];
|
|
const m20 = m[8];
|
|
const m21 = m[9];
|
|
const m22 = m[10];
|
|
const m23 = m[11];
|
|
const c = Math.cos(angleInRadians);
|
|
const s = Math.sin(angleInRadians);
|
|
dst[4] = c * m10 + s * m20;
|
|
dst[5] = c * m11 + s * m21;
|
|
dst[6] = c * m12 + s * m22;
|
|
dst[7] = c * m13 + s * m23;
|
|
dst[8] = c * m20 - s * m10;
|
|
dst[9] = c * m21 - s * m11;
|
|
dst[10] = c * m22 - s * m12;
|
|
dst[11] = c * m23 - s * m13;
|
|
|
|
if (m !== dst) {
|
|
dst[0] = m[0];
|
|
dst[1] = m[1];
|
|
dst[2] = m[2];
|
|
dst[3] = m[3];
|
|
dst[12] = m[12];
|
|
dst[13] = m[13];
|
|
dst[14] = m[14];
|
|
dst[15] = m[15];
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
/**
|
|
* Creates a 4-by-4 matrix which rotates around the y-axis by the given angle.
|
|
* @param {number} angleInRadians The angle by which to rotate (in radians).
|
|
* @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} The rotation matrix.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function rotationY(angleInRadians, dst) {
|
|
dst = dst || new MatType(16);
|
|
const c = Math.cos(angleInRadians);
|
|
const s = Math.sin(angleInRadians);
|
|
dst[0] = c;
|
|
dst[1] = 0;
|
|
dst[2] = -s;
|
|
dst[3] = 0;
|
|
dst[4] = 0;
|
|
dst[5] = 1;
|
|
dst[6] = 0;
|
|
dst[7] = 0;
|
|
dst[8] = s;
|
|
dst[9] = 0;
|
|
dst[10] = c;
|
|
dst[11] = 0;
|
|
dst[12] = 0;
|
|
dst[13] = 0;
|
|
dst[14] = 0;
|
|
dst[15] = 1;
|
|
return dst;
|
|
}
|
|
/**
|
|
* Rotates the given 4-by-4 matrix around the y-axis by the given
|
|
* angle.
|
|
* @param {module:twgl/m4.Mat4} m The matrix.
|
|
* @param {number} angleInRadians The angle by which to rotate (in radians).
|
|
* @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} The rotated matrix.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function rotateY(m, angleInRadians, dst) {
|
|
dst = dst || new MatType(16);
|
|
const m00 = m[0 * 4 + 0];
|
|
const m01 = m[0 * 4 + 1];
|
|
const m02 = m[0 * 4 + 2];
|
|
const m03 = m[0 * 4 + 3];
|
|
const m20 = m[2 * 4 + 0];
|
|
const m21 = m[2 * 4 + 1];
|
|
const m22 = m[2 * 4 + 2];
|
|
const m23 = m[2 * 4 + 3];
|
|
const c = Math.cos(angleInRadians);
|
|
const s = Math.sin(angleInRadians);
|
|
dst[0] = c * m00 - s * m20;
|
|
dst[1] = c * m01 - s * m21;
|
|
dst[2] = c * m02 - s * m22;
|
|
dst[3] = c * m03 - s * m23;
|
|
dst[8] = c * m20 + s * m00;
|
|
dst[9] = c * m21 + s * m01;
|
|
dst[10] = c * m22 + s * m02;
|
|
dst[11] = c * m23 + s * m03;
|
|
|
|
if (m !== dst) {
|
|
dst[4] = m[4];
|
|
dst[5] = m[5];
|
|
dst[6] = m[6];
|
|
dst[7] = m[7];
|
|
dst[12] = m[12];
|
|
dst[13] = m[13];
|
|
dst[14] = m[14];
|
|
dst[15] = m[15];
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
/**
|
|
* Creates a 4-by-4 matrix which rotates around the z-axis by the given angle.
|
|
* @param {number} angleInRadians The angle by which to rotate (in radians).
|
|
* @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} The rotation matrix.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function rotationZ(angleInRadians, dst) {
|
|
dst = dst || new MatType(16);
|
|
const c = Math.cos(angleInRadians);
|
|
const s = Math.sin(angleInRadians);
|
|
dst[0] = c;
|
|
dst[1] = s;
|
|
dst[2] = 0;
|
|
dst[3] = 0;
|
|
dst[4] = -s;
|
|
dst[5] = c;
|
|
dst[6] = 0;
|
|
dst[7] = 0;
|
|
dst[8] = 0;
|
|
dst[9] = 0;
|
|
dst[10] = 1;
|
|
dst[11] = 0;
|
|
dst[12] = 0;
|
|
dst[13] = 0;
|
|
dst[14] = 0;
|
|
dst[15] = 1;
|
|
return dst;
|
|
}
|
|
/**
|
|
* Rotates the given 4-by-4 matrix around the z-axis by the given
|
|
* angle.
|
|
* @param {module:twgl/m4.Mat4} m The matrix.
|
|
* @param {number} angleInRadians The angle by which to rotate (in radians).
|
|
* @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} The rotated matrix.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function rotateZ(m, angleInRadians, dst) {
|
|
dst = dst || new MatType(16);
|
|
const m00 = m[0 * 4 + 0];
|
|
const m01 = m[0 * 4 + 1];
|
|
const m02 = m[0 * 4 + 2];
|
|
const m03 = m[0 * 4 + 3];
|
|
const m10 = m[1 * 4 + 0];
|
|
const m11 = m[1 * 4 + 1];
|
|
const m12 = m[1 * 4 + 2];
|
|
const m13 = m[1 * 4 + 3];
|
|
const c = Math.cos(angleInRadians);
|
|
const s = Math.sin(angleInRadians);
|
|
dst[0] = c * m00 + s * m10;
|
|
dst[1] = c * m01 + s * m11;
|
|
dst[2] = c * m02 + s * m12;
|
|
dst[3] = c * m03 + s * m13;
|
|
dst[4] = c * m10 - s * m00;
|
|
dst[5] = c * m11 - s * m01;
|
|
dst[6] = c * m12 - s * m02;
|
|
dst[7] = c * m13 - s * m03;
|
|
|
|
if (m !== dst) {
|
|
dst[8] = m[8];
|
|
dst[9] = m[9];
|
|
dst[10] = m[10];
|
|
dst[11] = m[11];
|
|
dst[12] = m[12];
|
|
dst[13] = m[13];
|
|
dst[14] = m[14];
|
|
dst[15] = m[15];
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
/**
|
|
* Creates a 4-by-4 matrix which rotates around the given axis by the given
|
|
* angle.
|
|
* @param {module:twgl/v3.Vec3} axis The axis
|
|
* about which to rotate.
|
|
* @param {number} angleInRadians The angle by which to rotate (in radians).
|
|
* @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} A matrix which rotates angle radians
|
|
* around the axis.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function axisRotation(axis, angleInRadians, dst) {
|
|
dst = dst || new MatType(16);
|
|
let x = axis[0];
|
|
let y = axis[1];
|
|
let z = axis[2];
|
|
const n = Math.sqrt(x * x + y * y + z * z);
|
|
x /= n;
|
|
y /= n;
|
|
z /= n;
|
|
const xx = x * x;
|
|
const yy = y * y;
|
|
const zz = z * z;
|
|
const c = Math.cos(angleInRadians);
|
|
const s = Math.sin(angleInRadians);
|
|
const oneMinusCosine = 1 - c;
|
|
dst[0] = xx + (1 - xx) * c;
|
|
dst[1] = x * y * oneMinusCosine + z * s;
|
|
dst[2] = x * z * oneMinusCosine - y * s;
|
|
dst[3] = 0;
|
|
dst[4] = x * y * oneMinusCosine - z * s;
|
|
dst[5] = yy + (1 - yy) * c;
|
|
dst[6] = y * z * oneMinusCosine + x * s;
|
|
dst[7] = 0;
|
|
dst[8] = x * z * oneMinusCosine + y * s;
|
|
dst[9] = y * z * oneMinusCosine - x * s;
|
|
dst[10] = zz + (1 - zz) * c;
|
|
dst[11] = 0;
|
|
dst[12] = 0;
|
|
dst[13] = 0;
|
|
dst[14] = 0;
|
|
dst[15] = 1;
|
|
return dst;
|
|
}
|
|
/**
|
|
* Rotates the given 4-by-4 matrix around the given axis by the
|
|
* given angle.
|
|
* @param {module:twgl/m4.Mat4} m The matrix.
|
|
* @param {module:twgl/v3.Vec3} axis The axis
|
|
* about which to rotate.
|
|
* @param {number} angleInRadians The angle by which to rotate (in radians).
|
|
* @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} The rotated matrix.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function axisRotate(m, axis, angleInRadians, dst) {
|
|
dst = dst || new MatType(16);
|
|
let x = axis[0];
|
|
let y = axis[1];
|
|
let z = axis[2];
|
|
const n = Math.sqrt(x * x + y * y + z * z);
|
|
x /= n;
|
|
y /= n;
|
|
z /= n;
|
|
const xx = x * x;
|
|
const yy = y * y;
|
|
const zz = z * z;
|
|
const c = Math.cos(angleInRadians);
|
|
const s = Math.sin(angleInRadians);
|
|
const oneMinusCosine = 1 - c;
|
|
const r00 = xx + (1 - xx) * c;
|
|
const r01 = x * y * oneMinusCosine + z * s;
|
|
const r02 = x * z * oneMinusCosine - y * s;
|
|
const r10 = x * y * oneMinusCosine - z * s;
|
|
const r11 = yy + (1 - yy) * c;
|
|
const r12 = y * z * oneMinusCosine + x * s;
|
|
const r20 = x * z * oneMinusCosine + y * s;
|
|
const r21 = y * z * oneMinusCosine - x * s;
|
|
const r22 = zz + (1 - zz) * c;
|
|
const m00 = m[0];
|
|
const m01 = m[1];
|
|
const m02 = m[2];
|
|
const m03 = m[3];
|
|
const m10 = m[4];
|
|
const m11 = m[5];
|
|
const m12 = m[6];
|
|
const m13 = m[7];
|
|
const m20 = m[8];
|
|
const m21 = m[9];
|
|
const m22 = m[10];
|
|
const m23 = m[11];
|
|
dst[0] = r00 * m00 + r01 * m10 + r02 * m20;
|
|
dst[1] = r00 * m01 + r01 * m11 + r02 * m21;
|
|
dst[2] = r00 * m02 + r01 * m12 + r02 * m22;
|
|
dst[3] = r00 * m03 + r01 * m13 + r02 * m23;
|
|
dst[4] = r10 * m00 + r11 * m10 + r12 * m20;
|
|
dst[5] = r10 * m01 + r11 * m11 + r12 * m21;
|
|
dst[6] = r10 * m02 + r11 * m12 + r12 * m22;
|
|
dst[7] = r10 * m03 + r11 * m13 + r12 * m23;
|
|
dst[8] = r20 * m00 + r21 * m10 + r22 * m20;
|
|
dst[9] = r20 * m01 + r21 * m11 + r22 * m21;
|
|
dst[10] = r20 * m02 + r21 * m12 + r22 * m22;
|
|
dst[11] = r20 * m03 + r21 * m13 + r22 * m23;
|
|
|
|
if (m !== dst) {
|
|
dst[12] = m[12];
|
|
dst[13] = m[13];
|
|
dst[14] = m[14];
|
|
dst[15] = m[15];
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
/**
|
|
* Creates a 4-by-4 matrix which scales in each dimension by an amount given by
|
|
* the corresponding entry in the given vector; assumes the vector has three
|
|
* entries.
|
|
* @param {module:twgl/v3.Vec3} v A vector of
|
|
* three entries specifying the factor by which to scale in each dimension.
|
|
* @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} The scaling matrix.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function scaling(v, dst) {
|
|
dst = dst || new MatType(16);
|
|
dst[0] = v[0];
|
|
dst[1] = 0;
|
|
dst[2] = 0;
|
|
dst[3] = 0;
|
|
dst[4] = 0;
|
|
dst[5] = v[1];
|
|
dst[6] = 0;
|
|
dst[7] = 0;
|
|
dst[8] = 0;
|
|
dst[9] = 0;
|
|
dst[10] = v[2];
|
|
dst[11] = 0;
|
|
dst[12] = 0;
|
|
dst[13] = 0;
|
|
dst[14] = 0;
|
|
dst[15] = 1;
|
|
return dst;
|
|
}
|
|
/**
|
|
* Scales the given 4-by-4 matrix in each dimension by an amount
|
|
* given by the corresponding entry in the given vector; assumes the vector has
|
|
* three entries.
|
|
* @param {module:twgl/m4.Mat4} m The matrix to be modified.
|
|
* @param {module:twgl/v3.Vec3} v A vector of three entries specifying the
|
|
* factor by which to scale in each dimension.
|
|
* @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If not passed a new one is created.
|
|
* @return {module:twgl/m4.Mat4} The scaled matrix.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function scale(m, v, dst) {
|
|
dst = dst || new MatType(16);
|
|
const v0 = v[0];
|
|
const v1 = v[1];
|
|
const v2 = v[2];
|
|
dst[0] = v0 * m[0 * 4 + 0];
|
|
dst[1] = v0 * m[0 * 4 + 1];
|
|
dst[2] = v0 * m[0 * 4 + 2];
|
|
dst[3] = v0 * m[0 * 4 + 3];
|
|
dst[4] = v1 * m[1 * 4 + 0];
|
|
dst[5] = v1 * m[1 * 4 + 1];
|
|
dst[6] = v1 * m[1 * 4 + 2];
|
|
dst[7] = v1 * m[1 * 4 + 3];
|
|
dst[8] = v2 * m[2 * 4 + 0];
|
|
dst[9] = v2 * m[2 * 4 + 1];
|
|
dst[10] = v2 * m[2 * 4 + 2];
|
|
dst[11] = v2 * m[2 * 4 + 3];
|
|
|
|
if (m !== dst) {
|
|
dst[12] = m[12];
|
|
dst[13] = m[13];
|
|
dst[14] = m[14];
|
|
dst[15] = m[15];
|
|
}
|
|
|
|
return dst;
|
|
}
|
|
/**
|
|
* Takes a 4-by-4 matrix and a vector with 3 entries,
|
|
* interprets the vector as a point, transforms that point by the matrix, and
|
|
* returns the result as a vector with 3 entries.
|
|
* @param {module:twgl/m4.Mat4} m The matrix.
|
|
* @param {module:twgl/v3.Vec3} v The point.
|
|
* @param {module:twgl/v3.Vec3} [dst] optional vec3 to store result. If not passed a new one is created.
|
|
* @return {module:twgl/v3.Vec3} The transformed point.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function transformPoint(m, v, dst) {
|
|
dst = dst || create();
|
|
const v0 = v[0];
|
|
const v1 = v[1];
|
|
const v2 = v[2];
|
|
const d = v0 * m[0 * 4 + 3] + v1 * m[1 * 4 + 3] + v2 * m[2 * 4 + 3] + m[3 * 4 + 3];
|
|
dst[0] = (v0 * m[0 * 4 + 0] + v1 * m[1 * 4 + 0] + v2 * m[2 * 4 + 0] + m[3 * 4 + 0]) / d;
|
|
dst[1] = (v0 * m[0 * 4 + 1] + v1 * m[1 * 4 + 1] + v2 * m[2 * 4 + 1] + m[3 * 4 + 1]) / d;
|
|
dst[2] = (v0 * m[0 * 4 + 2] + v1 * m[1 * 4 + 2] + v2 * m[2 * 4 + 2] + m[3 * 4 + 2]) / d;
|
|
return dst;
|
|
}
|
|
/**
|
|
* Takes a 4-by-4 matrix and a vector with 3 entries, interprets the vector as a
|
|
* direction, transforms that direction by the matrix, and returns the result;
|
|
* assumes the transformation of 3-dimensional space represented by the matrix
|
|
* is parallel-preserving, i.e. any combination of rotation, scaling and
|
|
* translation, but not a perspective distortion. Returns a vector with 3
|
|
* entries.
|
|
* @param {module:twgl/m4.Mat4} m The matrix.
|
|
* @param {module:twgl/v3.Vec3} v The direction.
|
|
* @param {module:twgl/v3.Vec3} [dst] optional Vec3 to store result. If not passed a new one is created.
|
|
* @return {module:twgl/v3.Vec3} The transformed direction.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function transformDirection(m, v, dst) {
|
|
dst = dst || create();
|
|
const v0 = v[0];
|
|
const v1 = v[1];
|
|
const v2 = v[2];
|
|
dst[0] = v0 * m[0 * 4 + 0] + v1 * m[1 * 4 + 0] + v2 * m[2 * 4 + 0];
|
|
dst[1] = v0 * m[0 * 4 + 1] + v1 * m[1 * 4 + 1] + v2 * m[2 * 4 + 1];
|
|
dst[2] = v0 * m[0 * 4 + 2] + v1 * m[1 * 4 + 2] + v2 * m[2 * 4 + 2];
|
|
return dst;
|
|
}
|
|
/**
|
|
* Takes a 4-by-4 matrix m and a vector v with 3 entries, interprets the vector
|
|
* as a normal to a surface, and computes a vector which is normal upon
|
|
* transforming that surface by the matrix. The effect of this function is the
|
|
* same as transforming v (as a direction) by the inverse-transpose of m. This
|
|
* function assumes the transformation of 3-dimensional space represented by the
|
|
* matrix is parallel-preserving, i.e. any combination of rotation, scaling and
|
|
* translation, but not a perspective distortion. Returns a vector with 3
|
|
* entries.
|
|
* @param {module:twgl/m4.Mat4} m The matrix.
|
|
* @param {module:twgl/v3.Vec3} v The normal.
|
|
* @param {module:twgl/v3.Vec3} [dst] The direction. If not passed a new one is created.
|
|
* @return {module:twgl/v3.Vec3} The transformed normal.
|
|
* @memberOf module:twgl/m4
|
|
*/
|
|
|
|
|
|
function transformNormal(m, v, dst) {
|
|
dst = dst || create();
|
|
const mi = inverse(m);
|
|
const v0 = v[0];
|
|
const v1 = v[1];
|
|
const v2 = v[2];
|
|
dst[0] = v0 * mi[0 * 4 + 0] + v1 * mi[0 * 4 + 1] + v2 * mi[0 * 4 + 2];
|
|
dst[1] = v0 * mi[1 * 4 + 0] + v1 * mi[1 * 4 + 1] + v2 * mi[1 * 4 + 2];
|
|
dst[2] = v0 * mi[2 * 4 + 0] + v1 * mi[2 * 4 + 1] + v2 * mi[2 * 4 + 2];
|
|
return dst;
|
|
}
|
|
|
|
var m4 = /*#__PURE__*/Object.freeze({
|
|
__proto__: null,
|
|
axisRotate: axisRotate,
|
|
axisRotation: axisRotation,
|
|
copy: copy$1,
|
|
frustum: frustum,
|
|
getAxis: getAxis,
|
|
getTranslation: getTranslation,
|
|
identity: identity,
|
|
inverse: inverse,
|
|
lookAt: lookAt,
|
|
multiply: multiply$1,
|
|
negate: negate$1,
|
|
ortho: ortho,
|
|
perspective: perspective,
|
|
rotateX: rotateX,
|
|
rotateY: rotateY,
|
|
rotateZ: rotateZ,
|
|
rotationX: rotationX,
|
|
rotationY: rotationY,
|
|
rotationZ: rotationZ,
|
|
scale: scale,
|
|
scaling: scaling,
|
|
setAxis: setAxis,
|
|
setDefaultType: setDefaultType$1,
|
|
setTranslation: setTranslation,
|
|
transformDirection: transformDirection,
|
|
transformNormal: transformNormal,
|
|
transformPoint: transformPoint,
|
|
translate: translate,
|
|
translation: translation,
|
|
transpose: transpose
|
|
});
|
|
/*
|
|
* Copyright 2019 Gregg Tavares
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
/* DataType */
|
|
|
|
exports.m4 = m4;
|
|
const BYTE = 0x1400;
|
|
const UNSIGNED_BYTE = 0x1401;
|
|
const SHORT = 0x1402;
|
|
const UNSIGNED_SHORT = 0x1403;
|
|
const INT = 0x1404;
|
|
const UNSIGNED_INT = 0x1405;
|
|
const FLOAT = 0x1406;
|
|
const UNSIGNED_SHORT_4_4_4_4 = 0x8033;
|
|
const UNSIGNED_SHORT_5_5_5_1 = 0x8034;
|
|
const UNSIGNED_SHORT_5_6_5 = 0x8363;
|
|
const HALF_FLOAT = 0x140B;
|
|
const UNSIGNED_INT_2_10_10_10_REV = 0x8368;
|
|
const UNSIGNED_INT_10F_11F_11F_REV = 0x8C3B;
|
|
const UNSIGNED_INT_5_9_9_9_REV = 0x8C3E;
|
|
const FLOAT_32_UNSIGNED_INT_24_8_REV = 0x8DAD;
|
|
const UNSIGNED_INT_24_8 = 0x84FA;
|
|
const glTypeToTypedArray = {};
|
|
{
|
|
const tt = glTypeToTypedArray;
|
|
tt[BYTE] = Int8Array;
|
|
tt[UNSIGNED_BYTE] = Uint8Array;
|
|
tt[SHORT] = Int16Array;
|
|
tt[UNSIGNED_SHORT] = Uint16Array;
|
|
tt[INT] = Int32Array;
|
|
tt[UNSIGNED_INT] = Uint32Array;
|
|
tt[FLOAT] = Float32Array;
|
|
tt[UNSIGNED_SHORT_4_4_4_4] = Uint16Array;
|
|
tt[UNSIGNED_SHORT_5_5_5_1] = Uint16Array;
|
|
tt[UNSIGNED_SHORT_5_6_5] = Uint16Array;
|
|
tt[HALF_FLOAT] = Uint16Array;
|
|
tt[UNSIGNED_INT_2_10_10_10_REV] = Uint32Array;
|
|
tt[UNSIGNED_INT_10F_11F_11F_REV] = Uint32Array;
|
|
tt[UNSIGNED_INT_5_9_9_9_REV] = Uint32Array;
|
|
tt[FLOAT_32_UNSIGNED_INT_24_8_REV] = Uint32Array;
|
|
tt[UNSIGNED_INT_24_8] = Uint32Array;
|
|
}
|
|
/**
|
|
* Get the GL type for a typedArray
|
|
* @param {ArrayBufferView} typedArray a typedArray
|
|
* @return {number} the GL type for array. For example pass in an `Int8Array` and `gl.BYTE` will
|
|
* be returned. Pass in a `Uint32Array` and `gl.UNSIGNED_INT` will be returned
|
|
* @memberOf module:twgl/typedArray
|
|
*/
|
|
|
|
function getGLTypeForTypedArray(typedArray) {
|
|
if (typedArray instanceof Int8Array) {
|
|
return BYTE;
|
|
} // eslint-disable-line
|
|
|
|
|
|
if (typedArray instanceof Uint8Array) {
|
|
return UNSIGNED_BYTE;
|
|
} // eslint-disable-line
|
|
|
|
|
|
if (typedArray instanceof Uint8ClampedArray) {
|
|
return UNSIGNED_BYTE;
|
|
} // eslint-disable-line
|
|
|
|
|
|
if (typedArray instanceof Int16Array) {
|
|
return SHORT;
|
|
} // eslint-disable-line
|
|
|
|
|
|
if (typedArray instanceof Uint16Array) {
|
|
return UNSIGNED_SHORT;
|
|
} // eslint-disable-line
|
|
|
|
|
|
if (typedArray instanceof Int32Array) {
|
|
return INT;
|
|
} // eslint-disable-line
|
|
|
|
|
|
if (typedArray instanceof Uint32Array) {
|
|
return UNSIGNED_INT;
|
|
} // eslint-disable-line
|
|
|
|
|
|
if (typedArray instanceof Float32Array) {
|
|
return FLOAT;
|
|
} // eslint-disable-line
|
|
|
|
|
|
throw new Error('unsupported typed array type');
|
|
}
|
|
/**
|
|
* Get the GL type for a typedArray type
|
|
* @param {ArrayBufferView} typedArrayType a typedArray constructor
|
|
* @return {number} the GL type for type. For example pass in `Int8Array` and `gl.BYTE` will
|
|
* be returned. Pass in `Uint32Array` and `gl.UNSIGNED_INT` will be returned
|
|
* @memberOf module:twgl/typedArray
|
|
*/
|
|
|
|
|
|
function getGLTypeForTypedArrayType(typedArrayType) {
|
|
if (typedArrayType === Int8Array) {
|
|
return BYTE;
|
|
} // eslint-disable-line
|
|
|
|
|
|
if (typedArrayType === Uint8Array) {
|
|
return UNSIGNED_BYTE;
|
|
} // eslint-disable-line
|
|
|
|
|
|
if (typedArrayType === Uint8ClampedArray) {
|
|
return UNSIGNED_BYTE;
|
|
} // eslint-disable-line
|
|
|
|
|
|
if (typedArrayType === Int16Array) {
|
|
return SHORT;
|
|
} // eslint-disable-line
|
|
|
|
|
|
if (typedArrayType === Uint16Array) {
|
|
return UNSIGNED_SHORT;
|
|
} // eslint-disable-line
|
|
|
|
|
|
if (typedArrayType === Int32Array) {
|
|
return INT;
|
|
} // eslint-disable-line
|
|
|
|
|
|
if (typedArrayType === Uint32Array) {
|
|
return UNSIGNED_INT;
|
|
} // eslint-disable-line
|
|
|
|
|
|
if (typedArrayType === Float32Array) {
|
|
return FLOAT;
|
|
} // eslint-disable-line
|
|
|
|
|
|
throw new Error('unsupported typed array type');
|
|
}
|
|
/**
|
|
* Get the typed array constructor for a given GL type
|
|
* @param {number} type the GL type. (eg: `gl.UNSIGNED_INT`)
|
|
* @return {function} the constructor for a the corresponding typed array. (eg. `Uint32Array`).
|
|
* @memberOf module:twgl/typedArray
|
|
*/
|
|
|
|
|
|
function getTypedArrayTypeForGLType(type) {
|
|
const CTOR = glTypeToTypedArray[type];
|
|
|
|
if (!CTOR) {
|
|
throw new Error('unknown gl type');
|
|
}
|
|
|
|
return CTOR;
|
|
}
|
|
|
|
const isArrayBuffer = typeof SharedArrayBuffer !== 'undefined' ? function isArrayBufferOrSharedArrayBuffer(a) {
|
|
return a && a.buffer && a.buffer.toString && (a.buffer.toString() === "[object ArrayBuffer]" || a.buffer.toString() === "[object SharedArrayBuffer]");
|
|
} : function isArrayBuffer(a) {
|
|
return a && a.buffer && a.buffer.toString && a.buffer.toString() === "[object ArrayBuffer]";
|
|
};
|
|
exports.isArrayBuffer = isArrayBuffer;
|
|
var typedarrays = /*#__PURE__*/Object.freeze({
|
|
__proto__: null,
|
|
getGLTypeForTypedArray: getGLTypeForTypedArray,
|
|
getGLTypeForTypedArrayType: getGLTypeForTypedArrayType,
|
|
getTypedArrayTypeForGLType: getTypedArrayTypeForGLType,
|
|
isArrayBuffer: isArrayBuffer
|
|
});
|
|
/*
|
|
* Copyright 2019 Gregg Tavares
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
/* eslint no-console: "off" */
|
|
|
|
/**
|
|
* Copy named properties
|
|
*
|
|
* @param {string[]} names names of properties to copy
|
|
* @param {object} src object to copy properties from
|
|
* @param {object} dst object to copy properties to
|
|
* @private
|
|
*/
|
|
|
|
exports.typedarrays = typedarrays;
|
|
|
|
function copyNamedProperties(names, src, dst) {
|
|
names.forEach(function (name) {
|
|
const value = src[name];
|
|
|
|
if (value !== undefined) {
|
|
dst[name] = value;
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* Copies properties from source to dest only if a matching key is in dest
|
|
*
|
|
* @param {Object.<string, ?>} src the source
|
|
* @param {Object.<string, ?>} dst the dest
|
|
* @private
|
|
*/
|
|
|
|
|
|
function copyExistingProperties(src, dst) {
|
|
Object.keys(dst).forEach(function (key) {
|
|
if (dst.hasOwnProperty(key) && src.hasOwnProperty(key)) {
|
|
/* eslint no-prototype-builtins: 0 */
|
|
dst[key] = src[key];
|
|
}
|
|
});
|
|
}
|
|
|
|
function error(...args) {
|
|
console.error(...args);
|
|
}
|
|
|
|
function warn(...args) {
|
|
console.warn(...args);
|
|
}
|
|
|
|
function isBuffer(gl, t) {
|
|
return typeof WebGLBuffer !== 'undefined' && t instanceof WebGLBuffer;
|
|
}
|
|
|
|
function isRenderbuffer(gl, t) {
|
|
return typeof WebGLRenderbuffer !== 'undefined' && t instanceof WebGLRenderbuffer;
|
|
}
|
|
|
|
function isShader(gl, t) {
|
|
return typeof WebGLShader !== 'undefined' && t instanceof WebGLShader;
|
|
}
|
|
|
|
function isTexture(gl, t) {
|
|
return typeof WebGLTexture !== 'undefined' && t instanceof WebGLTexture;
|
|
}
|
|
|
|
function isSampler(gl, t) {
|
|
return typeof WebGLSampler !== 'undefined' && t instanceof WebGLSampler;
|
|
}
|
|
/*
|
|
* Copyright 2019 Gregg Tavares
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
|
|
const STATIC_DRAW = 0x88e4;
|
|
const ARRAY_BUFFER = 0x8892;
|
|
const ELEMENT_ARRAY_BUFFER = 0x8893;
|
|
const BUFFER_SIZE = 0x8764;
|
|
const BYTE$1 = 0x1400;
|
|
const UNSIGNED_BYTE$1 = 0x1401;
|
|
const SHORT$1 = 0x1402;
|
|
const UNSIGNED_SHORT$1 = 0x1403;
|
|
const INT$1 = 0x1404;
|
|
const UNSIGNED_INT$1 = 0x1405;
|
|
const FLOAT$1 = 0x1406;
|
|
const defaults = {
|
|
attribPrefix: ""
|
|
};
|
|
/**
|
|
* Sets the default attrib prefix
|
|
*
|
|
* When writing shaders I prefer to name attributes with `a_`, uniforms with `u_` and varyings with `v_`
|
|
* as it makes it clear where they came from. But, when building geometry I prefer using un-prefixed names.
|
|
*
|
|
* In other words I'll create arrays of geometry like this
|
|
*
|
|
* var arrays = {
|
|
* position: ...
|
|
* normal: ...
|
|
* texcoord: ...
|
|
* };
|
|
*
|
|
* But need those mapped to attributes and my attributes start with `a_`.
|
|
*
|
|
* @deprecated see {@link module:twgl.setDefaults}
|
|
* @param {string} prefix prefix for attribs
|
|
* @memberOf module:twgl/attributes
|
|
*/
|
|
|
|
function setAttributePrefix(prefix) {
|
|
defaults.attribPrefix = prefix;
|
|
}
|
|
|
|
function setDefaults(newDefaults) {
|
|
copyExistingProperties(newDefaults, defaults);
|
|
}
|
|
|
|
function setBufferFromTypedArray(gl, type, buffer, array, drawType) {
|
|
gl.bindBuffer(type, buffer);
|
|
gl.bufferData(type, array, drawType || STATIC_DRAW);
|
|
}
|
|
/**
|
|
* Given typed array creates a WebGLBuffer and copies the typed array
|
|
* into it.
|
|
*
|
|
* @param {WebGLRenderingContext} gl A WebGLRenderingContext
|
|
* @param {ArrayBuffer|SharedArrayBuffer|ArrayBufferView|WebGLBuffer} typedArray the typed array. Note: If a WebGLBuffer is passed in it will just be returned. No action will be taken
|
|
* @param {number} [type] the GL bind type for the buffer. Default = `gl.ARRAY_BUFFER`.
|
|
* @param {number} [drawType] the GL draw type for the buffer. Default = 'gl.STATIC_DRAW`.
|
|
* @return {WebGLBuffer} the created WebGLBuffer
|
|
* @memberOf module:twgl/attributes
|
|
*/
|
|
|
|
|
|
function createBufferFromTypedArray(gl, typedArray, type, drawType) {
|
|
if (isBuffer(gl, typedArray)) {
|
|
return typedArray;
|
|
}
|
|
|
|
type = type || ARRAY_BUFFER;
|
|
const buffer = gl.createBuffer();
|
|
setBufferFromTypedArray(gl, type, buffer, typedArray, drawType);
|
|
return buffer;
|
|
}
|
|
|
|
function isIndices(name) {
|
|
return name === "indices";
|
|
} // This is really just a guess. Though I can't really imagine using
|
|
// anything else? Maybe for some compression?
|
|
|
|
|
|
function getNormalizationForTypedArray(typedArray) {
|
|
if (typedArray instanceof Int8Array) {
|
|
return true;
|
|
} // eslint-disable-line
|
|
|
|
|
|
if (typedArray instanceof Uint8Array) {
|
|
return true;
|
|
} // eslint-disable-line
|
|
|
|
|
|
return false;
|
|
} // This is really just a guess. Though I can't really imagine using
|
|
// anything else? Maybe for some compression?
|
|
|
|
|
|
function getNormalizationForTypedArrayType(typedArrayType) {
|
|
if (typedArrayType === Int8Array) {
|
|
return true;
|
|
} // eslint-disable-line
|
|
|
|
|
|
if (typedArrayType === Uint8Array) {
|
|
return true;
|
|
} // eslint-disable-line
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
function getArray(array) {
|
|
return array.length ? array : array.data;
|
|
}
|
|
|
|
const texcoordRE = /coord|texture/i;
|
|
const colorRE = /color|colour/i;
|
|
|
|
function guessNumComponentsFromName(name, length) {
|
|
let numComponents;
|
|
|
|
if (texcoordRE.test(name)) {
|
|
numComponents = 2;
|
|
} else if (colorRE.test(name)) {
|
|
numComponents = 4;
|
|
} else {
|
|
numComponents = 3; // position, normals, indices ...
|
|
}
|
|
|
|
if (length % numComponents > 0) {
|
|
throw new Error(`Can not guess numComponents for attribute '${name}'. Tried ${numComponents} but ${length} values is not evenly divisible by ${numComponents}. You should specify it.`);
|
|
}
|
|
|
|
return numComponents;
|
|
}
|
|
|
|
function getNumComponents(array, arrayName) {
|
|
return array.numComponents || array.size || guessNumComponentsFromName(arrayName, getArray(array).length);
|
|
}
|
|
|
|
function makeTypedArray(array, name) {
|
|
if (isArrayBuffer(array)) {
|
|
return array;
|
|
}
|
|
|
|
if (isArrayBuffer(array.data)) {
|
|
return array.data;
|
|
}
|
|
|
|
if (Array.isArray(array)) {
|
|
array = {
|
|
data: array
|
|
};
|
|
}
|
|
|
|
let Type = array.type;
|
|
|
|
if (!Type) {
|
|
if (isIndices(name)) {
|
|
Type = Uint16Array;
|
|
} else {
|
|
Type = Float32Array;
|
|
}
|
|
}
|
|
|
|
return new Type(array.data);
|
|
}
|
|
/**
|
|
* The info for an attribute. This is effectively just the arguments to `gl.vertexAttribPointer` plus the WebGLBuffer
|
|
* for the attribute.
|
|
*
|
|
* @typedef {Object} AttribInfo
|
|
* @property {number[]|ArrayBufferView} [value] a constant value for the attribute. Note: if this is set the attribute will be
|
|
* disabled and set to this constant value and all other values will be ignored.
|
|
* @property {number} [numComponents] the number of components for this attribute.
|
|
* @property {number} [size] synonym for `numComponents`.
|
|
* @property {number} [type] the type of the attribute (eg. `gl.FLOAT`, `gl.UNSIGNED_BYTE`, etc...) Default = `gl.FLOAT`
|
|
* @property {boolean} [normalize] whether or not to normalize the data. Default = false
|
|
* @property {number} [offset] offset into buffer in bytes. Default = 0
|
|
* @property {number} [stride] the stride in bytes per element. Default = 0
|
|
* @property {number} [divisor] the divisor in instances. Default = undefined. Note: undefined = don't call gl.vertexAttribDivisor
|
|
* where as anything else = do call it with this value
|
|
* @property {WebGLBuffer} buffer the buffer that contains the data for this attribute
|
|
* @property {number} [drawType] the draw type passed to gl.bufferData. Default = gl.STATIC_DRAW
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* Use this type of array spec when TWGL can't guess the type or number of components of an array
|
|
* @typedef {Object} FullArraySpec
|
|
* @property {number[]|ArrayBufferView} [value] a constant value for the attribute. Note: if this is set the attribute will be
|
|
* disabled and set to this constant value and all other values will be ignored.
|
|
* @property {(number|number[]|ArrayBufferView)} data The data of the array. A number alone becomes the number of elements of type.
|
|
* @property {number} [numComponents] number of components for `vertexAttribPointer`. Default is based on the name of the array.
|
|
* If `coord` is in the name assumes `numComponents = 2`.
|
|
* If `color` is in the name assumes `numComponents = 4`.
|
|
* otherwise assumes `numComponents = 3`
|
|
* @property {constructor} [type] type. This is only used if `data` is a JavaScript array. It is the constructor for the typedarray. (eg. `Uint8Array`).
|
|
* For example if you want colors in a `Uint8Array` you might have a `FullArraySpec` like `{ type: Uint8Array, data: [255,0,255,255, ...], }`.
|
|
* @property {number} [size] synonym for `numComponents`.
|
|
* @property {boolean} [normalize] normalize for `vertexAttribPointer`. Default is true if type is `Int8Array` or `Uint8Array` otherwise false.
|
|
* @property {number} [stride] stride for `vertexAttribPointer`. Default = 0
|
|
* @property {number} [offset] offset for `vertexAttribPointer`. Default = 0
|
|
* @property {number} [divisor] divisor for `vertexAttribDivisor`. Default = undefined. Note: undefined = don't call gl.vertexAttribDivisor
|
|
* where as anything else = do call it with this value
|
|
* @property {string} [attrib] name of attribute this array maps to. Defaults to same name as array prefixed by the default attribPrefix.
|
|
* @property {string} [name] synonym for `attrib`.
|
|
* @property {string} [attribName] synonym for `attrib`.
|
|
* @property {WebGLBuffer} [buffer] Buffer to use for this attribute. This lets you use your own buffer
|
|
* but you will need to supply `numComponents` and `type`. You can effectively pass an `AttribInfo`
|
|
* to provide this. Example:
|
|
*
|
|
* const bufferInfo1 = twgl.createBufferInfoFromArrays(gl, {
|
|
* position: [1, 2, 3, ... ],
|
|
* });
|
|
* const bufferInfo2 = twgl.createBufferInfoFromArrays(gl, {
|
|
* position: bufferInfo1.attribs.position, // use the same buffer from bufferInfo1
|
|
* });
|
|
*
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* An individual array in {@link module:twgl.Arrays}
|
|
*
|
|
* When passed to {@link module:twgl.createBufferInfoFromArrays} if an ArraySpec is `number[]` or `ArrayBufferView`
|
|
* the types will be guessed based on the name. `indices` will be `Uint16Array`, everything else will
|
|
* be `Float32Array`. If an ArraySpec is a number it's the number of floats for an empty (zeroed) buffer.
|
|
*
|
|
* @typedef {(number|number[]|ArrayBufferView|module:twgl.FullArraySpec)} ArraySpec
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* This is a JavaScript object of arrays by name. The names should match your shader's attributes. If your
|
|
* attributes have a common prefix you can specify it by calling {@link module:twgl.setAttributePrefix}.
|
|
*
|
|
* Bare JavaScript Arrays
|
|
*
|
|
* var arrays = {
|
|
* position: [-1, 1, 0],
|
|
* normal: [0, 1, 0],
|
|
* ...
|
|
* }
|
|
*
|
|
* Bare TypedArrays
|
|
*
|
|
* var arrays = {
|
|
* position: new Float32Array([-1, 1, 0]),
|
|
* color: new Uint8Array([255, 128, 64, 255]),
|
|
* ...
|
|
* }
|
|
*
|
|
* * Will guess at `numComponents` if not specified based on name.
|
|
*
|
|
* If `coord` is in the name assumes `numComponents = 2`
|
|
*
|
|
* If `color` is in the name assumes `numComponents = 4`
|
|
*
|
|
* otherwise assumes `numComponents = 3`
|
|
*
|
|
* Objects with various fields. See {@link module:twgl.FullArraySpec}.
|
|
*
|
|
* var arrays = {
|
|
* position: { numComponents: 3, data: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0], },
|
|
* texcoord: { numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1], },
|
|
* normal: { numComponents: 3, data: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1], },
|
|
* indices: { numComponents: 3, data: [0, 1, 2, 1, 2, 3], },
|
|
* };
|
|
*
|
|
* @typedef {Object.<string, module:twgl.ArraySpec>} Arrays
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* Creates a set of attribute data and WebGLBuffers from set of arrays
|
|
*
|
|
* Given
|
|
*
|
|
* var arrays = {
|
|
* position: { numComponents: 3, data: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0], },
|
|
* texcoord: { numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1], },
|
|
* normal: { numComponents: 3, data: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1], },
|
|
* color: { numComponents: 4, data: [255, 255, 255, 255, 255, 0, 0, 255, 0, 0, 255, 255], type: Uint8Array, },
|
|
* indices: { numComponents: 3, data: [0, 1, 2, 1, 2, 3], },
|
|
* };
|
|
*
|
|
* returns something like
|
|
*
|
|
* var attribs = {
|
|
* position: { numComponents: 3, type: gl.FLOAT, normalize: false, buffer: WebGLBuffer, },
|
|
* texcoord: { numComponents: 2, type: gl.FLOAT, normalize: false, buffer: WebGLBuffer, },
|
|
* normal: { numComponents: 3, type: gl.FLOAT, normalize: false, buffer: WebGLBuffer, },
|
|
* color: { numComponents: 4, type: gl.UNSIGNED_BYTE, normalize: true, buffer: WebGLBuffer, },
|
|
* };
|
|
*
|
|
* notes:
|
|
*
|
|
* * Arrays can take various forms
|
|
*
|
|
* Bare JavaScript Arrays
|
|
*
|
|
* var arrays = {
|
|
* position: [-1, 1, 0],
|
|
* normal: [0, 1, 0],
|
|
* ...
|
|
* }
|
|
*
|
|
* Bare TypedArrays
|
|
*
|
|
* var arrays = {
|
|
* position: new Float32Array([-1, 1, 0]),
|
|
* color: new Uint8Array([255, 128, 64, 255]),
|
|
* ...
|
|
* }
|
|
*
|
|
* * Will guess at `numComponents` if not specified based on name.
|
|
*
|
|
* If `coord` is in the name assumes `numComponents = 2`
|
|
*
|
|
* If `color` is in the name assumes `numComponents = 4`
|
|
*
|
|
* otherwise assumes `numComponents = 3`
|
|
*
|
|
* @param {WebGLRenderingContext} gl The webgl rendering context.
|
|
* @param {module:twgl.Arrays} arrays The arrays
|
|
* @param {module:twgl.BufferInfo} [srcBufferInfo] a BufferInfo to copy from
|
|
* This lets you share buffers. Any arrays you supply will override
|
|
* the buffers from srcBufferInfo.
|
|
* @return {Object.<string, module:twgl.AttribInfo>} the attribs
|
|
* @memberOf module:twgl/attributes
|
|
*/
|
|
|
|
|
|
function createAttribsFromArrays(gl, arrays) {
|
|
const attribs = {};
|
|
Object.keys(arrays).forEach(function (arrayName) {
|
|
if (!isIndices(arrayName)) {
|
|
const array = arrays[arrayName];
|
|
const attribName = array.attrib || array.name || array.attribName || defaults.attribPrefix + arrayName;
|
|
|
|
if (array.value) {
|
|
if (!Array.isArray(array.value) && !isArrayBuffer(array.value)) {
|
|
throw new Error('array.value is not array or typedarray');
|
|
}
|
|
|
|
attribs[attribName] = {
|
|
value: array.value
|
|
};
|
|
} else {
|
|
let buffer;
|
|
let type;
|
|
let normalization;
|
|
let numComponents;
|
|
|
|
if (array.buffer && array.buffer instanceof WebGLBuffer) {
|
|
buffer = array.buffer;
|
|
numComponents = array.numComponents || array.size;
|
|
type = array.type;
|
|
normalization = array.normalize;
|
|
} else if (typeof array === "number" || typeof array.data === "number") {
|
|
const numValues = array.data || array;
|
|
const arrayType = array.type || Float32Array;
|
|
const numBytes = numValues * arrayType.BYTES_PER_ELEMENT;
|
|
type = getGLTypeForTypedArrayType(arrayType);
|
|
normalization = array.normalize !== undefined ? array.normalize : getNormalizationForTypedArrayType(arrayType);
|
|
numComponents = array.numComponents || array.size || guessNumComponentsFromName(arrayName, numValues);
|
|
buffer = gl.createBuffer();
|
|
gl.bindBuffer(ARRAY_BUFFER, buffer);
|
|
gl.bufferData(ARRAY_BUFFER, numBytes, array.drawType || STATIC_DRAW);
|
|
} else {
|
|
const typedArray = makeTypedArray(array, arrayName);
|
|
buffer = createBufferFromTypedArray(gl, typedArray, undefined, array.drawType);
|
|
type = getGLTypeForTypedArray(typedArray);
|
|
normalization = array.normalize !== undefined ? array.normalize : getNormalizationForTypedArray(typedArray);
|
|
numComponents = getNumComponents(array, arrayName);
|
|
}
|
|
|
|
attribs[attribName] = {
|
|
buffer: buffer,
|
|
numComponents: numComponents,
|
|
type: type,
|
|
normalize: normalization,
|
|
stride: array.stride || 0,
|
|
offset: array.offset || 0,
|
|
divisor: array.divisor === undefined ? undefined : array.divisor,
|
|
drawType: array.drawType
|
|
};
|
|
}
|
|
}
|
|
});
|
|
gl.bindBuffer(ARRAY_BUFFER, null);
|
|
return attribs;
|
|
}
|
|
/**
|
|
* Sets the contents of a buffer attached to an attribInfo
|
|
*
|
|
* This is helper function to dynamically update a buffer.
|
|
*
|
|
* Let's say you make a bufferInfo
|
|
*
|
|
* var arrays = {
|
|
* position: new Float32Array([0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0]),
|
|
* texcoord: new Float32Array([0, 0, 0, 1, 1, 0, 1, 1]),
|
|
* normal: new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]),
|
|
* indices: new Uint16Array([0, 1, 2, 1, 2, 3]),
|
|
* };
|
|
* var bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
|
|
*
|
|
* And you want to dynamically update the positions. You could do this
|
|
*
|
|
* // assuming arrays.position has already been updated with new data.
|
|
* twgl.setAttribInfoBufferFromArray(gl, bufferInfo.attribs.position, arrays.position);
|
|
*
|
|
* @param {WebGLRenderingContext} gl
|
|
* @param {AttribInfo} attribInfo The attribInfo who's buffer contents to set. NOTE: If you have an attribute prefix
|
|
* the name of the attribute will include the prefix.
|
|
* @param {ArraySpec} array Note: it is arguably inefficient to pass in anything but a typed array because anything
|
|
* else will have to be converted to a typed array before it can be used by WebGL. During init time that
|
|
* inefficiency is usually not important but if you're updating data dynamically best to be efficient.
|
|
* @param {number} [offset] an optional offset into the buffer. This is only an offset into the WebGL buffer
|
|
* not the array. To pass in an offset into the array itself use a typed array and create an `ArrayBufferView`
|
|
* for the portion of the array you want to use.
|
|
*
|
|
* var someArray = new Float32Array(1000); // an array with 1000 floats
|
|
* var someSubArray = new Float32Array(someArray.buffer, offsetInBytes, sizeInUnits); // a view into someArray
|
|
*
|
|
* Now you can pass `someSubArray` into setAttribInfoBufferFromArray`
|
|
* @memberOf module:twgl/attributes
|
|
*/
|
|
|
|
|
|
function setAttribInfoBufferFromArray(gl, attribInfo, array, offset) {
|
|
array = makeTypedArray(array);
|
|
|
|
if (offset !== undefined) {
|
|
gl.bindBuffer(ARRAY_BUFFER, attribInfo.buffer);
|
|
gl.bufferSubData(ARRAY_BUFFER, offset, array);
|
|
} else {
|
|
setBufferFromTypedArray(gl, ARRAY_BUFFER, attribInfo.buffer, array, attribInfo.drawType);
|
|
}
|
|
}
|
|
|
|
function getBytesPerValueForGLType(gl, type) {
|
|
if (type === BYTE$1) return 1; // eslint-disable-line
|
|
|
|
if (type === UNSIGNED_BYTE$1) return 1; // eslint-disable-line
|
|
|
|
if (type === SHORT$1) return 2; // eslint-disable-line
|
|
|
|
if (type === UNSIGNED_SHORT$1) return 2; // eslint-disable-line
|
|
|
|
if (type === INT$1) return 4; // eslint-disable-line
|
|
|
|
if (type === UNSIGNED_INT$1) return 4; // eslint-disable-line
|
|
|
|
if (type === FLOAT$1) return 4; // eslint-disable-line
|
|
|
|
return 0;
|
|
} // Tries to get the number of elements from a set of arrays.
|
|
|
|
|
|
const positionKeys = ['position', 'positions', 'a_position'];
|
|
|
|
function getNumElementsFromNonIndexedArrays(arrays) {
|
|
let key;
|
|
let ii;
|
|
|
|
for (ii = 0; ii < positionKeys.length; ++ii) {
|
|
key = positionKeys[ii];
|
|
|
|
if (key in arrays) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ii === positionKeys.length) {
|
|
key = Object.keys(arrays)[0];
|
|
}
|
|
|
|
const array = arrays[key];
|
|
const length = getArray(array).length;
|
|
const numComponents = getNumComponents(array, key);
|
|
const numElements = length / numComponents;
|
|
|
|
if (length % numComponents > 0) {
|
|
throw new Error(`numComponents ${numComponents} not correct for length ${length}`);
|
|
}
|
|
|
|
return numElements;
|
|
}
|
|
|
|
function getNumElementsFromAttributes(gl, attribs) {
|
|
let key;
|
|
let ii;
|
|
|
|
for (ii = 0; ii < positionKeys.length; ++ii) {
|
|
key = positionKeys[ii];
|
|
|
|
if (key in attribs) {
|
|
break;
|
|
}
|
|
|
|
key = defaults.attribPrefix + key;
|
|
|
|
if (key in attribs) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ii === positionKeys.length) {
|
|
key = Object.keys(attribs)[0];
|
|
}
|
|
|
|
const attrib = attribs[key];
|
|
gl.bindBuffer(ARRAY_BUFFER, attrib.buffer);
|
|
const numBytes = gl.getBufferParameter(ARRAY_BUFFER, BUFFER_SIZE);
|
|
gl.bindBuffer(ARRAY_BUFFER, null);
|
|
const bytesPerValue = getBytesPerValueForGLType(gl, attrib.type);
|
|
const totalElements = numBytes / bytesPerValue;
|
|
const numComponents = attrib.numComponents || attrib.size; // TODO: check stride
|
|
|
|
const numElements = totalElements / numComponents;
|
|
|
|
if (numElements % 1 !== 0) {
|
|
throw new Error(`numComponents ${numComponents} not correct for length ${length}`);
|
|
}
|
|
|
|
return numElements;
|
|
}
|
|
/**
|
|
* @typedef {Object} BufferInfo
|
|
* @property {number} numElements The number of elements to pass to `gl.drawArrays` or `gl.drawElements`.
|
|
* @property {number} [elementType] The type of indices `UNSIGNED_BYTE`, `UNSIGNED_SHORT` etc..
|
|
* @property {WebGLBuffer} [indices] The indices `ELEMENT_ARRAY_BUFFER` if any indices exist.
|
|
* @property {Object.<string, module:twgl.AttribInfo>} [attribs] The attribs appropriate to call `setAttributes`
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* Creates a BufferInfo from an object of arrays.
|
|
*
|
|
* This can be passed to {@link module:twgl.setBuffersAndAttributes} and to
|
|
* {@link module:twgl:drawBufferInfo}.
|
|
*
|
|
* Given an object like
|
|
*
|
|
* var arrays = {
|
|
* position: { numComponents: 3, data: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0], },
|
|
* texcoord: { numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1], },
|
|
* normal: { numComponents: 3, data: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1], },
|
|
* indices: { numComponents: 3, data: [0, 1, 2, 1, 2, 3], },
|
|
* };
|
|
*
|
|
* Creates an BufferInfo like this
|
|
*
|
|
* bufferInfo = {
|
|
* numElements: 4, // or whatever the number of elements is
|
|
* indices: WebGLBuffer, // this property will not exist if there are no indices
|
|
* attribs: {
|
|
* position: { buffer: WebGLBuffer, numComponents: 3, },
|
|
* normal: { buffer: WebGLBuffer, numComponents: 3, },
|
|
* texcoord: { buffer: WebGLBuffer, numComponents: 2, },
|
|
* },
|
|
* };
|
|
*
|
|
* The properties of arrays can be JavaScript arrays in which case the number of components
|
|
* will be guessed.
|
|
*
|
|
* var arrays = {
|
|
* position: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0],
|
|
* texcoord: [0, 0, 0, 1, 1, 0, 1, 1],
|
|
* normal: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1],
|
|
* indices: [0, 1, 2, 1, 2, 3],
|
|
* };
|
|
*
|
|
* They can also be TypedArrays
|
|
*
|
|
* var arrays = {
|
|
* position: new Float32Array([0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0]),
|
|
* texcoord: new Float32Array([0, 0, 0, 1, 1, 0, 1, 1]),
|
|
* normal: new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]),
|
|
* indices: new Uint16Array([0, 1, 2, 1, 2, 3]),
|
|
* };
|
|
*
|
|
* Or AugmentedTypedArrays
|
|
*
|
|
* var positions = createAugmentedTypedArray(3, 4);
|
|
* var texcoords = createAugmentedTypedArray(2, 4);
|
|
* var normals = createAugmentedTypedArray(3, 4);
|
|
* var indices = createAugmentedTypedArray(3, 2, Uint16Array);
|
|
*
|
|
* positions.push([0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0]);
|
|
* texcoords.push([0, 0, 0, 1, 1, 0, 1, 1]);
|
|
* normals.push([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]);
|
|
* indices.push([0, 1, 2, 1, 2, 3]);
|
|
*
|
|
* var arrays = {
|
|
* position: positions,
|
|
* texcoord: texcoords,
|
|
* normal: normals,
|
|
* indices: indices,
|
|
* };
|
|
*
|
|
* For the last example it is equivalent to
|
|
*
|
|
* var bufferInfo = {
|
|
* attribs: {
|
|
* position: { numComponents: 3, buffer: gl.createBuffer(), },
|
|
* texcoord: { numComponents: 2, buffer: gl.createBuffer(), },
|
|
* normal: { numComponents: 3, buffer: gl.createBuffer(), },
|
|
* },
|
|
* indices: gl.createBuffer(),
|
|
* numElements: 6,
|
|
* };
|
|
*
|
|
* gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.position.buffer);
|
|
* gl.bufferData(gl.ARRAY_BUFFER, arrays.position, gl.STATIC_DRAW);
|
|
* gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.texcoord.buffer);
|
|
* gl.bufferData(gl.ARRAY_BUFFER, arrays.texcoord, gl.STATIC_DRAW);
|
|
* gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.normal.buffer);
|
|
* gl.bufferData(gl.ARRAY_BUFFER, arrays.normal, gl.STATIC_DRAW);
|
|
* gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufferInfo.indices);
|
|
* gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, arrays.indices, gl.STATIC_DRAW);
|
|
*
|
|
* @param {WebGLRenderingContext} gl A WebGLRenderingContext
|
|
* @param {module:twgl.Arrays} arrays Your data
|
|
* @param {module:twgl.BufferInfo} [srcBufferInfo] An existing
|
|
* buffer info to start from. WebGLBuffers etc specified
|
|
* in the srcBufferInfo will be used in a new BufferInfo
|
|
* with any arrays specified overriding the ones in
|
|
* srcBufferInfo.
|
|
* @return {module:twgl.BufferInfo} A BufferInfo
|
|
* @memberOf module:twgl/attributes
|
|
*/
|
|
|
|
|
|
function createBufferInfoFromArrays(gl, arrays, srcBufferInfo) {
|
|
const newAttribs = createAttribsFromArrays(gl, arrays);
|
|
const bufferInfo = Object.assign({}, srcBufferInfo ? srcBufferInfo : {});
|
|
bufferInfo.attribs = Object.assign({}, srcBufferInfo ? srcBufferInfo.attribs : {}, newAttribs);
|
|
const indices = arrays.indices;
|
|
|
|
if (indices) {
|
|
const newIndices = makeTypedArray(indices, "indices");
|
|
bufferInfo.indices = createBufferFromTypedArray(gl, newIndices, ELEMENT_ARRAY_BUFFER);
|
|
bufferInfo.numElements = newIndices.length;
|
|
bufferInfo.elementType = getGLTypeForTypedArray(newIndices);
|
|
} else if (!bufferInfo.numElements) {
|
|
bufferInfo.numElements = getNumElementsFromAttributes(gl, bufferInfo.attribs);
|
|
}
|
|
|
|
return bufferInfo;
|
|
}
|
|
/**
|
|
* Creates a buffer from an array, typed array, or array spec
|
|
*
|
|
* Given something like this
|
|
*
|
|
* [1, 2, 3],
|
|
*
|
|
* or
|
|
*
|
|
* new Uint16Array([1,2,3]);
|
|
*
|
|
* or
|
|
*
|
|
* {
|
|
* data: [1, 2, 3],
|
|
* type: Uint8Array,
|
|
* }
|
|
*
|
|
* returns a WebGLBuffer that contains the given data.
|
|
*
|
|
* @param {WebGLRenderingContext} gl A WebGLRenderingContext.
|
|
* @param {module:twgl.ArraySpec} array an array, typed array, or array spec.
|
|
* @param {string} arrayName name of array. Used to guess the type if type can not be derived otherwise.
|
|
* @return {WebGLBuffer} a WebGLBuffer containing the data in array.
|
|
* @memberOf module:twgl/attributes
|
|
*/
|
|
|
|
|
|
function createBufferFromArray(gl, array, arrayName) {
|
|
const type = arrayName === "indices" ? ELEMENT_ARRAY_BUFFER : ARRAY_BUFFER;
|
|
const typedArray = makeTypedArray(array, arrayName);
|
|
return createBufferFromTypedArray(gl, typedArray, type);
|
|
}
|
|
/**
|
|
* Creates buffers from arrays or typed arrays
|
|
*
|
|
* Given something like this
|
|
*
|
|
* var arrays = {
|
|
* positions: [1, 2, 3],
|
|
* normals: [0, 0, 1],
|
|
* }
|
|
*
|
|
* returns something like
|
|
*
|
|
* buffers = {
|
|
* positions: WebGLBuffer,
|
|
* normals: WebGLBuffer,
|
|
* }
|
|
*
|
|
* If the buffer is named 'indices' it will be made an ELEMENT_ARRAY_BUFFER.
|
|
*
|
|
* @param {WebGLRenderingContext} gl A WebGLRenderingContext.
|
|
* @param {module:twgl.Arrays} arrays
|
|
* @return {Object<string, WebGLBuffer>} returns an object with one WebGLBuffer per array
|
|
* @memberOf module:twgl/attributes
|
|
*/
|
|
|
|
|
|
function createBuffersFromArrays(gl, arrays) {
|
|
const buffers = {};
|
|
Object.keys(arrays).forEach(function (key) {
|
|
buffers[key] = createBufferFromArray(gl, arrays[key], key);
|
|
}); // Ugh!
|
|
|
|
if (arrays.indices) {
|
|
buffers.numElements = arrays.indices.length;
|
|
buffers.elementType = getGLTypeForTypedArray(makeTypedArray(arrays.indices));
|
|
} else {
|
|
buffers.numElements = getNumElementsFromNonIndexedArrays(arrays);
|
|
}
|
|
|
|
return buffers;
|
|
}
|
|
|
|
var attributes = /*#__PURE__*/Object.freeze({
|
|
__proto__: null,
|
|
createAttribsFromArrays: createAttribsFromArrays,
|
|
createBuffersFromArrays: createBuffersFromArrays,
|
|
createBufferFromArray: createBufferFromArray,
|
|
createBufferFromTypedArray: createBufferFromTypedArray,
|
|
createBufferInfoFromArrays: createBufferInfoFromArrays,
|
|
setAttribInfoBufferFromArray: setAttribInfoBufferFromArray,
|
|
setAttributePrefix: setAttributePrefix,
|
|
setAttributeDefaults_: setDefaults,
|
|
getNumComponents_: getNumComponents,
|
|
getArray_: getArray
|
|
});
|
|
/*
|
|
* Copyright 2019 Gregg Tavares
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
exports.attributes = attributes;
|
|
const getArray$1 = getArray; // eslint-disable-line
|
|
|
|
const getNumComponents$1 = getNumComponents; // eslint-disable-line
|
|
|
|
/**
|
|
* @typedef {(Int8Array|Uint8Array|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array)} TypedArray
|
|
*/
|
|
|
|
/**
|
|
* Add `push` to a typed array. It just keeps a 'cursor'
|
|
* and allows use to `push` values into the array so we
|
|
* don't have to manually compute offsets
|
|
* @param {TypedArray} typedArray TypedArray to augment
|
|
* @param {number} numComponents number of components.
|
|
* @private
|
|
*/
|
|
|
|
function augmentTypedArray(typedArray, numComponents) {
|
|
let cursor = 0;
|
|
|
|
typedArray.push = function () {
|
|
for (let ii = 0; ii < arguments.length; ++ii) {
|
|
const value = arguments[ii];
|
|
|
|
if (value instanceof Array || isArrayBuffer(value)) {
|
|
for (let jj = 0; jj < value.length; ++jj) {
|
|
typedArray[cursor++] = value[jj];
|
|
}
|
|
} else {
|
|
typedArray[cursor++] = value;
|
|
}
|
|
}
|
|
};
|
|
|
|
typedArray.reset = function (opt_index) {
|
|
cursor = opt_index || 0;
|
|
};
|
|
|
|
typedArray.numComponents = numComponents;
|
|
Object.defineProperty(typedArray, 'numElements', {
|
|
get: function () {
|
|
return this.length / this.numComponents | 0;
|
|
}
|
|
});
|
|
return typedArray;
|
|
}
|
|
/**
|
|
* creates a typed array with a `push` function attached
|
|
* so that you can easily *push* values.
|
|
*
|
|
* `push` can take multiple arguments. If an argument is an array each element
|
|
* of the array will be added to the typed array.
|
|
*
|
|
* Example:
|
|
*
|
|
* const array = createAugmentedTypedArray(3, 2); // creates a Float32Array with 6 values
|
|
* array.push(1, 2, 3);
|
|
* array.push([4, 5, 6]);
|
|
* // array now contains [1, 2, 3, 4, 5, 6]
|
|
*
|
|
* Also has `numComponents` and `numElements` properties.
|
|
*
|
|
* @param {number} numComponents number of components
|
|
* @param {number} numElements number of elements. The total size of the array will be `numComponents * numElements`.
|
|
* @param {constructor} opt_type A constructor for the type. Default = `Float32Array`.
|
|
* @return {ArrayBufferView} A typed array.
|
|
* @memberOf module:twgl/primitives
|
|
*/
|
|
|
|
|
|
function createAugmentedTypedArray(numComponents, numElements, opt_type) {
|
|
const Type = opt_type || Float32Array;
|
|
return augmentTypedArray(new Type(numComponents * numElements), numComponents);
|
|
}
|
|
|
|
function allButIndices(name) {
|
|
return name !== "indices";
|
|
}
|
|
/**
|
|
* Given indexed vertices creates a new set of vertices un-indexed by expanding the indexed vertices.
|
|
* @param {Object.<string, TypedArray>} vertices The indexed vertices to deindex
|
|
* @return {Object.<string, TypedArray>} The deindexed vertices
|
|
* @memberOf module:twgl/primitives
|
|
*/
|
|
|
|
|
|
function deindexVertices(vertices) {
|
|
const indices = vertices.indices;
|
|
const newVertices = {};
|
|
const numElements = indices.length;
|
|
|
|
function expandToUnindexed(channel) {
|
|
const srcBuffer = vertices[channel];
|
|
const numComponents = srcBuffer.numComponents;
|
|
const dstBuffer = createAugmentedTypedArray(numComponents, numElements, srcBuffer.constructor);
|
|
|
|
for (let ii = 0; ii < numElements; ++ii) {
|
|
const ndx = indices[ii];
|
|
const offset = ndx * numComponents;
|
|
|
|
for (let jj = 0; jj < numComponents; ++jj) {
|
|
dstBuffer.push(srcBuffer[offset + jj]);
|
|
}
|
|
}
|
|
|
|
newVertices[channel] = dstBuffer;
|
|
}
|
|
|
|
Object.keys(vertices).filter(allButIndices).forEach(expandToUnindexed);
|
|
return newVertices;
|
|
}
|
|
/**
|
|
* flattens the normals of deindexed vertices in place.
|
|
* @param {Object.<string, TypedArray>} vertices The deindexed vertices who's normals to flatten
|
|
* @return {Object.<string, TypedArray>} The flattened vertices (same as was passed in)
|
|
* @memberOf module:twgl/primitives
|
|
*/
|
|
|
|
|
|
function flattenNormals(vertices) {
|
|
if (vertices.indices) {
|
|
throw new Error('can not flatten normals of indexed vertices. deindex them first');
|
|
}
|
|
|
|
const normals = vertices.normal;
|
|
const numNormals = normals.length;
|
|
|
|
for (let ii = 0; ii < numNormals; ii += 9) {
|
|
// pull out the 3 normals for this triangle
|
|
const nax = normals[ii + 0];
|
|
const nay = normals[ii + 1];
|
|
const naz = normals[ii + 2];
|
|
const nbx = normals[ii + 3];
|
|
const nby = normals[ii + 4];
|
|
const nbz = normals[ii + 5];
|
|
const ncx = normals[ii + 6];
|
|
const ncy = normals[ii + 7];
|
|
const ncz = normals[ii + 8]; // add them
|
|
|
|
let nx = nax + nbx + ncx;
|
|
let ny = nay + nby + ncy;
|
|
let nz = naz + nbz + ncz; // normalize them
|
|
|
|
const length = Math.sqrt(nx * nx + ny * ny + nz * nz);
|
|
nx /= length;
|
|
ny /= length;
|
|
nz /= length; // copy them back in
|
|
|
|
normals[ii + 0] = nx;
|
|
normals[ii + 1] = ny;
|
|
normals[ii + 2] = nz;
|
|
normals[ii + 3] = nx;
|
|
normals[ii + 4] = ny;
|
|
normals[ii + 5] = nz;
|
|
normals[ii + 6] = nx;
|
|
normals[ii + 7] = ny;
|
|
normals[ii + 8] = nz;
|
|
}
|
|
|
|
return vertices;
|
|
}
|
|
|
|
function applyFuncToV3Array(array, matrix, fn) {
|
|
const len = array.length;
|
|
const tmp = new Float32Array(3);
|
|
|
|
for (let ii = 0; ii < len; ii += 3) {
|
|
fn(matrix, [array[ii], array[ii + 1], array[ii + 2]], tmp);
|
|
array[ii] = tmp[0];
|
|
array[ii + 1] = tmp[1];
|
|
array[ii + 2] = tmp[2];
|
|
}
|
|
}
|
|
|
|
function transformNormal$1(mi, v, dst) {
|
|
dst = dst || create();
|
|
const v0 = v[0];
|
|
const v1 = v[1];
|
|
const v2 = v[2];
|
|
dst[0] = v0 * mi[0 * 4 + 0] + v1 * mi[0 * 4 + 1] + v2 * mi[0 * 4 + 2];
|
|
dst[1] = v0 * mi[1 * 4 + 0] + v1 * mi[1 * 4 + 1] + v2 * mi[1 * 4 + 2];
|
|
dst[2] = v0 * mi[2 * 4 + 0] + v1 * mi[2 * 4 + 1] + v2 * mi[2 * 4 + 2];
|
|
return dst;
|
|
}
|
|
/**
|
|
* Reorients directions by the given matrix..
|
|
* @param {(number[]|TypedArray)} array The array. Assumes value floats per element.
|
|
* @param {module:twgl/m4.Mat4} matrix A matrix to multiply by.
|
|
* @return {(number[]|TypedArray)} the same array that was passed in
|
|
* @memberOf module:twgl/primitives
|
|
*/
|
|
|
|
|
|
function reorientDirections(array, matrix) {
|
|
applyFuncToV3Array(array, matrix, transformDirection);
|
|
return array;
|
|
}
|
|
/**
|
|
* Reorients normals by the inverse-transpose of the given
|
|
* matrix..
|
|
* @param {(number[]|TypedArray)} array The array. Assumes value floats per element.
|
|
* @param {module:twgl/m4.Mat4} matrix A matrix to multiply by.
|
|
* @return {(number[]|TypedArray)} the same array that was passed in
|
|
* @memberOf module:twgl/primitives
|
|
*/
|
|
|
|
|
|
function reorientNormals(array, matrix) {
|
|
applyFuncToV3Array(array, inverse(matrix), transformNormal$1);
|
|
return array;
|
|
}
|
|
/**
|
|
* Reorients positions by the given matrix. In other words, it
|
|
* multiplies each vertex by the given matrix.
|
|
* @param {(number[]|TypedArray)} array The array. Assumes value floats per element.
|
|
* @param {module:twgl/m4.Mat4} matrix A matrix to multiply by.
|
|
* @return {(number[]|TypedArray)} the same array that was passed in
|
|
* @memberOf module:twgl/primitives
|
|
*/
|
|
|
|
|
|
function reorientPositions(array, matrix) {
|
|
applyFuncToV3Array(array, matrix, transformPoint);
|
|
return array;
|
|
}
|
|
/**
|
|
* @typedef {(number[]|TypedArray)} NativeArrayOrTypedArray
|
|
*/
|
|
|
|
/**
|
|
* Reorients arrays by the given matrix. Assumes arrays have
|
|
* names that contains 'pos' could be reoriented as positions,
|
|
* 'binorm' or 'tan' as directions, and 'norm' as normals.
|
|
*
|
|
* @param {Object.<string, NativeArrayOrTypedArray>} arrays The vertices to reorient
|
|
* @param {module:twgl/m4.Mat4} matrix matrix to reorient by.
|
|
* @return {Object.<string, NativeArrayOrTypedArray>} same arrays that were passed in.
|
|
* @memberOf module:twgl/primitives
|
|
*/
|
|
|
|
|
|
function reorientVertices(arrays, matrix) {
|
|
Object.keys(arrays).forEach(function (name) {
|
|
const array = arrays[name];
|
|
|
|
if (name.indexOf("pos") >= 0) {
|
|
reorientPositions(array, matrix);
|
|
} else if (name.indexOf("tan") >= 0 || name.indexOf("binorm") >= 0) {
|
|
reorientDirections(array, matrix);
|
|
} else if (name.indexOf("norm") >= 0) {
|
|
reorientNormals(array, matrix);
|
|
}
|
|
});
|
|
return arrays;
|
|
}
|
|
/**
|
|
* Creates XY quad BufferInfo
|
|
*
|
|
* The default with no parameters will return a 2x2 quad with values from -1 to +1.
|
|
* If you want a unit quad with that goes from 0 to 1 you'd call it with
|
|
*
|
|
* twgl.primitives.createXYQuadBufferInfo(gl, 1, 0.5, 0.5);
|
|
*
|
|
* If you want a unit quad centered above 0,0 you'd call it with
|
|
*
|
|
* twgl.primitives.createXYQuadBufferInfo(gl, 1, 0, 0.5);
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext.
|
|
* @param {number} [size] the size across the quad. Defaults to 2 which means vertices will go from -1 to +1
|
|
* @param {number} [xOffset] the amount to offset the quad in X
|
|
* @param {number} [yOffset] the amount to offset the quad in Y
|
|
* @return {Object.<string, WebGLBuffer>} the created XY Quad BufferInfo
|
|
* @memberOf module:twgl/primitives
|
|
* @function createXYQuadBuffers
|
|
*/
|
|
|
|
/**
|
|
* Creates XY quad Buffers
|
|
*
|
|
* The default with no parameters will return a 2x2 quad with values from -1 to +1.
|
|
* If you want a unit quad with that goes from 0 to 1 you'd call it with
|
|
*
|
|
* twgl.primitives.createXYQuadBufferInfo(gl, 1, 0.5, 0.5);
|
|
*
|
|
* If you want a unit quad centered above 0,0 you'd call it with
|
|
*
|
|
* twgl.primitives.createXYQuadBufferInfo(gl, 1, 0, 0.5);
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext.
|
|
* @param {number} [size] the size across the quad. Defaults to 2 which means vertices will go from -1 to +1
|
|
* @param {number} [xOffset] the amount to offset the quad in X
|
|
* @param {number} [yOffset] the amount to offset the quad in Y
|
|
* @return {module:twgl.BufferInfo} the created XY Quad buffers
|
|
* @memberOf module:twgl/primitives
|
|
* @function createXYQuadBufferInfo
|
|
*/
|
|
|
|
/**
|
|
* Creates XY quad vertices
|
|
*
|
|
* The default with no parameters will return a 2x2 quad with values from -1 to +1.
|
|
* If you want a unit quad with that goes from 0 to 1 you'd call it with
|
|
*
|
|
* twgl.primitives.createXYQuadVertices(1, 0.5, 0.5);
|
|
*
|
|
* If you want a unit quad centered above 0,0 you'd call it with
|
|
*
|
|
* twgl.primitives.createXYQuadVertices(1, 0, 0.5);
|
|
*
|
|
* @param {number} [size] the size across the quad. Defaults to 2 which means vertices will go from -1 to +1
|
|
* @param {number} [xOffset] the amount to offset the quad in X
|
|
* @param {number} [yOffset] the amount to offset the quad in Y
|
|
* @return {Object.<string, TypedArray>} the created XY Quad vertices
|
|
* @memberOf module:twgl/primitives
|
|
*/
|
|
|
|
|
|
function createXYQuadVertices(size, xOffset, yOffset) {
|
|
size = size || 2;
|
|
xOffset = xOffset || 0;
|
|
yOffset = yOffset || 0;
|
|
size *= 0.5;
|
|
return {
|
|
position: {
|
|
numComponents: 2,
|
|
data: [xOffset + -1 * size, yOffset + -1 * size, xOffset + 1 * size, yOffset + -1 * size, xOffset + -1 * size, yOffset + 1 * size, xOffset + 1 * size, yOffset + 1 * size]
|
|
},
|
|
normal: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1],
|
|
texcoord: [0, 0, 1, 0, 0, 1, 1, 1],
|
|
indices: [0, 1, 2, 2, 1, 3]
|
|
};
|
|
}
|
|
/**
|
|
* Creates XZ plane BufferInfo.
|
|
*
|
|
* The created plane has position, normal, and texcoord data
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext.
|
|
* @param {number} [width] Width of the plane. Default = 1
|
|
* @param {number} [depth] Depth of the plane. Default = 1
|
|
* @param {number} [subdivisionsWidth] Number of steps across the plane. Default = 1
|
|
* @param {number} [subdivisionsDepth] Number of steps down the plane. Default = 1
|
|
* @param {module:twgl/m4.Mat4} [matrix] A matrix by which to multiply all the vertices.
|
|
* @return {module:twgl.BufferInfo} The created plane BufferInfo.
|
|
* @memberOf module:twgl/primitives
|
|
* @function createPlaneBufferInfo
|
|
*/
|
|
|
|
/**
|
|
* Creates XZ plane buffers.
|
|
*
|
|
* The created plane has position, normal, and texcoord data
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext.
|
|
* @param {number} [width] Width of the plane. Default = 1
|
|
* @param {number} [depth] Depth of the plane. Default = 1
|
|
* @param {number} [subdivisionsWidth] Number of steps across the plane. Default = 1
|
|
* @param {number} [subdivisionsDepth] Number of steps down the plane. Default = 1
|
|
* @param {module:twgl/m4.Mat4} [matrix] A matrix by which to multiply all the vertices.
|
|
* @return {Object.<string, WebGLBuffer>} The created plane buffers.
|
|
* @memberOf module:twgl/primitives
|
|
* @function createPlaneBuffers
|
|
*/
|
|
|
|
/**
|
|
* Creates XZ plane vertices.
|
|
*
|
|
* The created plane has position, normal, and texcoord data
|
|
*
|
|
* @param {number} [width] Width of the plane. Default = 1
|
|
* @param {number} [depth] Depth of the plane. Default = 1
|
|
* @param {number} [subdivisionsWidth] Number of steps across the plane. Default = 1
|
|
* @param {number} [subdivisionsDepth] Number of steps down the plane. Default = 1
|
|
* @param {module:twgl/m4.Mat4} [matrix] A matrix by which to multiply all the vertices.
|
|
* @return {Object.<string, TypedArray>} The created plane vertices.
|
|
* @memberOf module:twgl/primitives
|
|
*/
|
|
|
|
|
|
function createPlaneVertices(width, depth, subdivisionsWidth, subdivisionsDepth, matrix) {
|
|
width = width || 1;
|
|
depth = depth || 1;
|
|
subdivisionsWidth = subdivisionsWidth || 1;
|
|
subdivisionsDepth = subdivisionsDepth || 1;
|
|
matrix = matrix || identity();
|
|
const numVertices = (subdivisionsWidth + 1) * (subdivisionsDepth + 1);
|
|
const positions = createAugmentedTypedArray(3, numVertices);
|
|
const normals = createAugmentedTypedArray(3, numVertices);
|
|
const texcoords = createAugmentedTypedArray(2, numVertices);
|
|
|
|
for (let z = 0; z <= subdivisionsDepth; z++) {
|
|
for (let x = 0; x <= subdivisionsWidth; x++) {
|
|
const u = x / subdivisionsWidth;
|
|
const v = z / subdivisionsDepth;
|
|
positions.push(width * u - width * 0.5, 0, depth * v - depth * 0.5);
|
|
normals.push(0, 1, 0);
|
|
texcoords.push(u, v);
|
|
}
|
|
}
|
|
|
|
const numVertsAcross = subdivisionsWidth + 1;
|
|
const indices = createAugmentedTypedArray(3, subdivisionsWidth * subdivisionsDepth * 2, Uint16Array);
|
|
|
|
for (let z = 0; z < subdivisionsDepth; z++) {
|
|
// eslint-disable-line
|
|
for (let x = 0; x < subdivisionsWidth; x++) {
|
|
// eslint-disable-line
|
|
// Make triangle 1 of quad.
|
|
indices.push((z + 0) * numVertsAcross + x, (z + 1) * numVertsAcross + x, (z + 0) * numVertsAcross + x + 1); // Make triangle 2 of quad.
|
|
|
|
indices.push((z + 1) * numVertsAcross + x, (z + 1) * numVertsAcross + x + 1, (z + 0) * numVertsAcross + x + 1);
|
|
}
|
|
}
|
|
|
|
const arrays = reorientVertices({
|
|
position: positions,
|
|
normal: normals,
|
|
texcoord: texcoords,
|
|
indices: indices
|
|
}, matrix);
|
|
return arrays;
|
|
}
|
|
/**
|
|
* Creates sphere BufferInfo.
|
|
*
|
|
* The created sphere has position, normal, and texcoord data
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext.
|
|
* @param {number} radius radius of the sphere.
|
|
* @param {number} subdivisionsAxis number of steps around the sphere.
|
|
* @param {number} subdivisionsHeight number of vertically on the sphere.
|
|
* @param {number} [opt_startLatitudeInRadians] where to start the
|
|
* top of the sphere. Default = 0.
|
|
* @param {number} [opt_endLatitudeInRadians] Where to end the
|
|
* bottom of the sphere. Default = Math.PI.
|
|
* @param {number} [opt_startLongitudeInRadians] where to start
|
|
* wrapping the sphere. Default = 0.
|
|
* @param {number} [opt_endLongitudeInRadians] where to end
|
|
* wrapping the sphere. Default = 2 * Math.PI.
|
|
* @return {module:twgl.BufferInfo} The created sphere BufferInfo.
|
|
* @memberOf module:twgl/primitives
|
|
* @function createSphereBufferInfo
|
|
*/
|
|
|
|
/**
|
|
* Creates sphere buffers.
|
|
*
|
|
* The created sphere has position, normal, and texcoord data
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext.
|
|
* @param {number} radius radius of the sphere.
|
|
* @param {number} subdivisionsAxis number of steps around the sphere.
|
|
* @param {number} subdivisionsHeight number of vertically on the sphere.
|
|
* @param {number} [opt_startLatitudeInRadians] where to start the
|
|
* top of the sphere. Default = 0.
|
|
* @param {number} [opt_endLatitudeInRadians] Where to end the
|
|
* bottom of the sphere. Default = Math.PI.
|
|
* @param {number} [opt_startLongitudeInRadians] where to start
|
|
* wrapping the sphere. Default = 0.
|
|
* @param {number} [opt_endLongitudeInRadians] where to end
|
|
* wrapping the sphere. Default = 2 * Math.PI.
|
|
* @return {Object.<string, WebGLBuffer>} The created sphere buffers.
|
|
* @memberOf module:twgl/primitives
|
|
* @function createSphereBuffers
|
|
*/
|
|
|
|
/**
|
|
* Creates sphere vertices.
|
|
*
|
|
* The created sphere has position, normal, and texcoord data
|
|
*
|
|
* @param {number} radius radius of the sphere.
|
|
* @param {number} subdivisionsAxis number of steps around the sphere.
|
|
* @param {number} subdivisionsHeight number of vertically on the sphere.
|
|
* @param {number} [opt_startLatitudeInRadians] where to start the
|
|
* top of the sphere. Default = 0.
|
|
* @param {number} [opt_endLatitudeInRadians] Where to end the
|
|
* bottom of the sphere. Default = Math.PI.
|
|
* @param {number} [opt_startLongitudeInRadians] where to start
|
|
* wrapping the sphere. Default = 0.
|
|
* @param {number} [opt_endLongitudeInRadians] where to end
|
|
* wrapping the sphere. Default = 2 * Math.PI.
|
|
* @return {Object.<string, TypedArray>} The created sphere vertices.
|
|
* @memberOf module:twgl/primitives
|
|
*/
|
|
|
|
|
|
function createSphereVertices(radius, subdivisionsAxis, subdivisionsHeight, opt_startLatitudeInRadians, opt_endLatitudeInRadians, opt_startLongitudeInRadians, opt_endLongitudeInRadians) {
|
|
if (subdivisionsAxis <= 0 || subdivisionsHeight <= 0) {
|
|
throw new Error('subdivisionAxis and subdivisionHeight must be > 0');
|
|
}
|
|
|
|
opt_startLatitudeInRadians = opt_startLatitudeInRadians || 0;
|
|
opt_endLatitudeInRadians = opt_endLatitudeInRadians || Math.PI;
|
|
opt_startLongitudeInRadians = opt_startLongitudeInRadians || 0;
|
|
opt_endLongitudeInRadians = opt_endLongitudeInRadians || Math.PI * 2;
|
|
const latRange = opt_endLatitudeInRadians - opt_startLatitudeInRadians;
|
|
const longRange = opt_endLongitudeInRadians - opt_startLongitudeInRadians; // We are going to generate our sphere by iterating through its
|
|
// spherical coordinates and generating 2 triangles for each quad on a
|
|
// ring of the sphere.
|
|
|
|
const numVertices = (subdivisionsAxis + 1) * (subdivisionsHeight + 1);
|
|
const positions = createAugmentedTypedArray(3, numVertices);
|
|
const normals = createAugmentedTypedArray(3, numVertices);
|
|
const texcoords = createAugmentedTypedArray(2, numVertices); // Generate the individual vertices in our vertex buffer.
|
|
|
|
for (let y = 0; y <= subdivisionsHeight; y++) {
|
|
for (let x = 0; x <= subdivisionsAxis; x++) {
|
|
// Generate a vertex based on its spherical coordinates
|
|
const u = x / subdivisionsAxis;
|
|
const v = y / subdivisionsHeight;
|
|
const theta = longRange * u + opt_startLongitudeInRadians;
|
|
const phi = latRange * v + opt_startLatitudeInRadians;
|
|
const sinTheta = Math.sin(theta);
|
|
const cosTheta = Math.cos(theta);
|
|
const sinPhi = Math.sin(phi);
|
|
const cosPhi = Math.cos(phi);
|
|
const ux = cosTheta * sinPhi;
|
|
const uy = cosPhi;
|
|
const uz = sinTheta * sinPhi;
|
|
positions.push(radius * ux, radius * uy, radius * uz);
|
|
normals.push(ux, uy, uz);
|
|
texcoords.push(1 - u, v);
|
|
}
|
|
}
|
|
|
|
const numVertsAround = subdivisionsAxis + 1;
|
|
const indices = createAugmentedTypedArray(3, subdivisionsAxis * subdivisionsHeight * 2, Uint16Array);
|
|
|
|
for (let x = 0; x < subdivisionsAxis; x++) {
|
|
// eslint-disable-line
|
|
for (let y = 0; y < subdivisionsHeight; y++) {
|
|
// eslint-disable-line
|
|
// Make triangle 1 of quad.
|
|
indices.push((y + 0) * numVertsAround + x, (y + 0) * numVertsAround + x + 1, (y + 1) * numVertsAround + x); // Make triangle 2 of quad.
|
|
|
|
indices.push((y + 1) * numVertsAround + x, (y + 0) * numVertsAround + x + 1, (y + 1) * numVertsAround + x + 1);
|
|
}
|
|
}
|
|
|
|
return {
|
|
position: positions,
|
|
normal: normals,
|
|
texcoord: texcoords,
|
|
indices: indices
|
|
};
|
|
}
|
|
/**
|
|
* Array of the indices of corners of each face of a cube.
|
|
* @type {Array.<number[]>}
|
|
* @private
|
|
*/
|
|
|
|
|
|
const CUBE_FACE_INDICES = [[3, 7, 5, 1], // right
|
|
[6, 2, 0, 4], // left
|
|
[6, 7, 3, 2], // ??
|
|
[0, 1, 5, 4], // ??
|
|
[7, 6, 4, 5], // front
|
|
[2, 3, 1, 0] // back
|
|
];
|
|
/**
|
|
* Creates a BufferInfo for a cube.
|
|
*
|
|
* The cube is created around the origin. (-size / 2, size / 2).
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext.
|
|
* @param {number} [size] width, height and depth of the cube.
|
|
* @return {module:twgl.BufferInfo} The created BufferInfo.
|
|
* @memberOf module:twgl/primitives
|
|
* @function createCubeBufferInfo
|
|
*/
|
|
|
|
/**
|
|
* Creates the buffers and indices for a cube.
|
|
*
|
|
* The cube is created around the origin. (-size / 2, size / 2).
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext.
|
|
* @param {number} [size] width, height and depth of the cube.
|
|
* @return {Object.<string, WebGLBuffer>} The created buffers.
|
|
* @memberOf module:twgl/primitives
|
|
* @function createCubeBuffers
|
|
*/
|
|
|
|
/**
|
|
* Creates the vertices and indices for a cube.
|
|
*
|
|
* The cube is created around the origin. (-size / 2, size / 2).
|
|
*
|
|
* @param {number} [size] width, height and depth of the cube.
|
|
* @return {Object.<string, TypedArray>} The created vertices.
|
|
* @memberOf module:twgl/primitives
|
|
*/
|
|
|
|
function createCubeVertices(size) {
|
|
size = size || 1;
|
|
const k = size / 2;
|
|
const cornerVertices = [[-k, -k, -k], [+k, -k, -k], [-k, +k, -k], [+k, +k, -k], [-k, -k, +k], [+k, -k, +k], [-k, +k, +k], [+k, +k, +k]];
|
|
const faceNormals = [[+1, +0, +0], [-1, +0, +0], [+0, +1, +0], [+0, -1, +0], [+0, +0, +1], [+0, +0, -1]];
|
|
const uvCoords = [[1, 0], [0, 0], [0, 1], [1, 1]];
|
|
const numVertices = 6 * 4;
|
|
const positions = createAugmentedTypedArray(3, numVertices);
|
|
const normals = createAugmentedTypedArray(3, numVertices);
|
|
const texcoords = createAugmentedTypedArray(2, numVertices);
|
|
const indices = createAugmentedTypedArray(3, 6 * 2, Uint16Array);
|
|
|
|
for (let f = 0; f < 6; ++f) {
|
|
const faceIndices = CUBE_FACE_INDICES[f];
|
|
|
|
for (let v = 0; v < 4; ++v) {
|
|
const position = cornerVertices[faceIndices[v]];
|
|
const normal = faceNormals[f];
|
|
const uv = uvCoords[v]; // Each face needs all four vertices because the normals and texture
|
|
// coordinates are not all the same.
|
|
|
|
positions.push(position);
|
|
normals.push(normal);
|
|
texcoords.push(uv);
|
|
} // Two triangles make a square face.
|
|
|
|
|
|
const offset = 4 * f;
|
|
indices.push(offset + 0, offset + 1, offset + 2);
|
|
indices.push(offset + 0, offset + 2, offset + 3);
|
|
}
|
|
|
|
return {
|
|
position: positions,
|
|
normal: normals,
|
|
texcoord: texcoords,
|
|
indices: indices
|
|
};
|
|
}
|
|
/**
|
|
* Creates a BufferInfo for a truncated cone, which is like a cylinder
|
|
* except that it has different top and bottom radii. A truncated cone
|
|
* can also be used to create cylinders and regular cones. The
|
|
* truncated cone will be created centered about the origin, with the
|
|
* y axis as its vertical axis.
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext.
|
|
* @param {number} bottomRadius Bottom radius of truncated cone.
|
|
* @param {number} topRadius Top radius of truncated cone.
|
|
* @param {number} height Height of truncated cone.
|
|
* @param {number} radialSubdivisions The number of subdivisions around the
|
|
* truncated cone.
|
|
* @param {number} verticalSubdivisions The number of subdivisions down the
|
|
* truncated cone.
|
|
* @param {boolean} [opt_topCap] Create top cap. Default = true.
|
|
* @param {boolean} [opt_bottomCap] Create bottom cap. Default = true.
|
|
* @return {module:twgl.BufferInfo} The created cone BufferInfo.
|
|
* @memberOf module:twgl/primitives
|
|
* @function createTruncatedConeBufferInfo
|
|
*/
|
|
|
|
/**
|
|
* Creates buffers for a truncated cone, which is like a cylinder
|
|
* except that it has different top and bottom radii. A truncated cone
|
|
* can also be used to create cylinders and regular cones. The
|
|
* truncated cone will be created centered about the origin, with the
|
|
* y axis as its vertical axis.
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext.
|
|
* @param {number} bottomRadius Bottom radius of truncated cone.
|
|
* @param {number} topRadius Top radius of truncated cone.
|
|
* @param {number} height Height of truncated cone.
|
|
* @param {number} radialSubdivisions The number of subdivisions around the
|
|
* truncated cone.
|
|
* @param {number} verticalSubdivisions The number of subdivisions down the
|
|
* truncated cone.
|
|
* @param {boolean} [opt_topCap] Create top cap. Default = true.
|
|
* @param {boolean} [opt_bottomCap] Create bottom cap. Default = true.
|
|
* @return {Object.<string, WebGLBuffer>} The created cone buffers.
|
|
* @memberOf module:twgl/primitives
|
|
* @function createTruncatedConeBuffers
|
|
*/
|
|
|
|
/**
|
|
* Creates vertices for a truncated cone, which is like a cylinder
|
|
* except that it has different top and bottom radii. A truncated cone
|
|
* can also be used to create cylinders and regular cones. The
|
|
* truncated cone will be created centered about the origin, with the
|
|
* y axis as its vertical axis. .
|
|
*
|
|
* @param {number} bottomRadius Bottom radius of truncated cone.
|
|
* @param {number} topRadius Top radius of truncated cone.
|
|
* @param {number} height Height of truncated cone.
|
|
* @param {number} radialSubdivisions The number of subdivisions around the
|
|
* truncated cone.
|
|
* @param {number} verticalSubdivisions The number of subdivisions down the
|
|
* truncated cone.
|
|
* @param {boolean} [opt_topCap] Create top cap. Default = true.
|
|
* @param {boolean} [opt_bottomCap] Create bottom cap. Default = true.
|
|
* @return {Object.<string, TypedArray>} The created cone vertices.
|
|
* @memberOf module:twgl/primitives
|
|
*/
|
|
|
|
|
|
function createTruncatedConeVertices(bottomRadius, topRadius, height, radialSubdivisions, verticalSubdivisions, opt_topCap, opt_bottomCap) {
|
|
if (radialSubdivisions < 3) {
|
|
throw new Error('radialSubdivisions must be 3 or greater');
|
|
}
|
|
|
|
if (verticalSubdivisions < 1) {
|
|
throw new Error('verticalSubdivisions must be 1 or greater');
|
|
}
|
|
|
|
const topCap = opt_topCap === undefined ? true : opt_topCap;
|
|
const bottomCap = opt_bottomCap === undefined ? true : opt_bottomCap;
|
|
const extra = (topCap ? 2 : 0) + (bottomCap ? 2 : 0);
|
|
const numVertices = (radialSubdivisions + 1) * (verticalSubdivisions + 1 + extra);
|
|
const positions = createAugmentedTypedArray(3, numVertices);
|
|
const normals = createAugmentedTypedArray(3, numVertices);
|
|
const texcoords = createAugmentedTypedArray(2, numVertices);
|
|
const indices = createAugmentedTypedArray(3, radialSubdivisions * (verticalSubdivisions + extra / 2) * 2, Uint16Array);
|
|
const vertsAroundEdge = radialSubdivisions + 1; // The slant of the cone is constant across its surface
|
|
|
|
const slant = Math.atan2(bottomRadius - topRadius, height);
|
|
const cosSlant = Math.cos(slant);
|
|
const sinSlant = Math.sin(slant);
|
|
const start = topCap ? -2 : 0;
|
|
const end = verticalSubdivisions + (bottomCap ? 2 : 0);
|
|
|
|
for (let yy = start; yy <= end; ++yy) {
|
|
let v = yy / verticalSubdivisions;
|
|
let y = height * v;
|
|
let ringRadius;
|
|
|
|
if (yy < 0) {
|
|
y = 0;
|
|
v = 1;
|
|
ringRadius = bottomRadius;
|
|
} else if (yy > verticalSubdivisions) {
|
|
y = height;
|
|
v = 1;
|
|
ringRadius = topRadius;
|
|
} else {
|
|
ringRadius = bottomRadius + (topRadius - bottomRadius) * (yy / verticalSubdivisions);
|
|
}
|
|
|
|
if (yy === -2 || yy === verticalSubdivisions + 2) {
|
|
ringRadius = 0;
|
|
v = 0;
|
|
}
|
|
|
|
y -= height / 2;
|
|
|
|
for (let ii = 0; ii < vertsAroundEdge; ++ii) {
|
|
const sin = Math.sin(ii * Math.PI * 2 / radialSubdivisions);
|
|
const cos = Math.cos(ii * Math.PI * 2 / radialSubdivisions);
|
|
positions.push(sin * ringRadius, y, cos * ringRadius);
|
|
|
|
if (yy < 0) {
|
|
normals.push(0, -1, 0);
|
|
} else if (yy > verticalSubdivisions) {
|
|
normals.push(0, 1, 0);
|
|
} else if (ringRadius === 0.0) {
|
|
normals.push(0, 0, 0);
|
|
} else {
|
|
normals.push(sin * cosSlant, sinSlant, cos * cosSlant);
|
|
}
|
|
|
|
texcoords.push(ii / radialSubdivisions, 1 - v);
|
|
}
|
|
}
|
|
|
|
for (let yy = 0; yy < verticalSubdivisions + extra; ++yy) {
|
|
// eslint-disable-line
|
|
if (yy === 1 && topCap || yy === verticalSubdivisions + extra - 2 && bottomCap) {
|
|
continue;
|
|
}
|
|
|
|
for (let ii = 0; ii < radialSubdivisions; ++ii) {
|
|
// eslint-disable-line
|
|
indices.push(vertsAroundEdge * (yy + 0) + 0 + ii, vertsAroundEdge * (yy + 0) + 1 + ii, vertsAroundEdge * (yy + 1) + 1 + ii);
|
|
indices.push(vertsAroundEdge * (yy + 0) + 0 + ii, vertsAroundEdge * (yy + 1) + 1 + ii, vertsAroundEdge * (yy + 1) + 0 + ii);
|
|
}
|
|
}
|
|
|
|
return {
|
|
position: positions,
|
|
normal: normals,
|
|
texcoord: texcoords,
|
|
indices: indices
|
|
};
|
|
}
|
|
/**
|
|
* Expands RLE data
|
|
* @param {number[]} rleData data in format of run-length, x, y, z, run-length, x, y, z
|
|
* @param {number[]} [padding] value to add each entry with.
|
|
* @return {number[]} the expanded rleData
|
|
* @private
|
|
*/
|
|
|
|
|
|
function expandRLEData(rleData, padding) {
|
|
padding = padding || [];
|
|
const data = [];
|
|
|
|
for (let ii = 0; ii < rleData.length; ii += 4) {
|
|
const runLength = rleData[ii];
|
|
const element = rleData.slice(ii + 1, ii + 4);
|
|
element.push.apply(element, padding);
|
|
|
|
for (let jj = 0; jj < runLength; ++jj) {
|
|
data.push.apply(data, element);
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
/**
|
|
* Creates 3D 'F' BufferInfo.
|
|
* An 'F' is useful because you can easily tell which way it is oriented.
|
|
* The created 'F' has position, normal, texcoord, and color buffers.
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext.
|
|
* @return {module:twgl.BufferInfo} The created BufferInfo.
|
|
* @memberOf module:twgl/primitives
|
|
* @function create3DFBufferInfo
|
|
*/
|
|
|
|
/**
|
|
* Creates 3D 'F' buffers.
|
|
* An 'F' is useful because you can easily tell which way it is oriented.
|
|
* The created 'F' has position, normal, texcoord, and color buffers.
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext.
|
|
* @return {Object.<string, WebGLBuffer>} The created buffers.
|
|
* @memberOf module:twgl/primitives
|
|
* @function create3DFBuffers
|
|
*/
|
|
|
|
/**
|
|
* Creates 3D 'F' vertices.
|
|
* An 'F' is useful because you can easily tell which way it is oriented.
|
|
* The created 'F' has position, normal, texcoord, and color arrays.
|
|
*
|
|
* @return {Object.<string, TypedArray>} The created vertices.
|
|
* @memberOf module:twgl/primitives
|
|
*/
|
|
|
|
|
|
function create3DFVertices() {
|
|
const positions = [// left column front
|
|
0, 0, 0, 0, 150, 0, 30, 0, 0, 0, 150, 0, 30, 150, 0, 30, 0, 0, // top rung front
|
|
30, 0, 0, 30, 30, 0, 100, 0, 0, 30, 30, 0, 100, 30, 0, 100, 0, 0, // middle rung front
|
|
30, 60, 0, 30, 90, 0, 67, 60, 0, 30, 90, 0, 67, 90, 0, 67, 60, 0, // left column back
|
|
0, 0, 30, 30, 0, 30, 0, 150, 30, 0, 150, 30, 30, 0, 30, 30, 150, 30, // top rung back
|
|
30, 0, 30, 100, 0, 30, 30, 30, 30, 30, 30, 30, 100, 0, 30, 100, 30, 30, // middle rung back
|
|
30, 60, 30, 67, 60, 30, 30, 90, 30, 30, 90, 30, 67, 60, 30, 67, 90, 30, // top
|
|
0, 0, 0, 100, 0, 0, 100, 0, 30, 0, 0, 0, 100, 0, 30, 0, 0, 30, // top rung front
|
|
100, 0, 0, 100, 30, 0, 100, 30, 30, 100, 0, 0, 100, 30, 30, 100, 0, 30, // under top rung
|
|
30, 30, 0, 30, 30, 30, 100, 30, 30, 30, 30, 0, 100, 30, 30, 100, 30, 0, // between top rung and middle
|
|
30, 30, 0, 30, 60, 30, 30, 30, 30, 30, 30, 0, 30, 60, 0, 30, 60, 30, // top of middle rung
|
|
30, 60, 0, 67, 60, 30, 30, 60, 30, 30, 60, 0, 67, 60, 0, 67, 60, 30, // front of middle rung
|
|
67, 60, 0, 67, 90, 30, 67, 60, 30, 67, 60, 0, 67, 90, 0, 67, 90, 30, // bottom of middle rung.
|
|
30, 90, 0, 30, 90, 30, 67, 90, 30, 30, 90, 0, 67, 90, 30, 67, 90, 0, // front of bottom
|
|
30, 90, 0, 30, 150, 30, 30, 90, 30, 30, 90, 0, 30, 150, 0, 30, 150, 30, // bottom
|
|
0, 150, 0, 0, 150, 30, 30, 150, 30, 0, 150, 0, 30, 150, 30, 30, 150, 0, // left side
|
|
0, 0, 0, 0, 0, 30, 0, 150, 30, 0, 0, 0, 0, 150, 30, 0, 150, 0];
|
|
const texcoords = [// left column front
|
|
0.22, 0.19, 0.22, 0.79, 0.34, 0.19, 0.22, 0.79, 0.34, 0.79, 0.34, 0.19, // top rung front
|
|
0.34, 0.19, 0.34, 0.31, 0.62, 0.19, 0.34, 0.31, 0.62, 0.31, 0.62, 0.19, // middle rung front
|
|
0.34, 0.43, 0.34, 0.55, 0.49, 0.43, 0.34, 0.55, 0.49, 0.55, 0.49, 0.43, // left column back
|
|
0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, // top rung back
|
|
0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, // middle rung back
|
|
0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, // top
|
|
0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, // top rung front
|
|
0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, // under top rung
|
|
0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, // between top rung and middle
|
|
0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, // top of middle rung
|
|
0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, // front of middle rung
|
|
0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, // bottom of middle rung.
|
|
0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, // front of bottom
|
|
0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, // bottom
|
|
0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, // left side
|
|
0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0];
|
|
const normals = expandRLEData([// left column front
|
|
// top rung front
|
|
// middle rung front
|
|
18, 0, 0, 1, // left column back
|
|
// top rung back
|
|
// middle rung back
|
|
18, 0, 0, -1, // top
|
|
6, 0, 1, 0, // top rung front
|
|
6, 1, 0, 0, // under top rung
|
|
6, 0, -1, 0, // between top rung and middle
|
|
6, 1, 0, 0, // top of middle rung
|
|
6, 0, 1, 0, // front of middle rung
|
|
6, 1, 0, 0, // bottom of middle rung.
|
|
6, 0, -1, 0, // front of bottom
|
|
6, 1, 0, 0, // bottom
|
|
6, 0, -1, 0, // left side
|
|
6, -1, 0, 0]);
|
|
const colors = expandRLEData([// left column front
|
|
// top rung front
|
|
// middle rung front
|
|
18, 200, 70, 120, // left column back
|
|
// top rung back
|
|
// middle rung back
|
|
18, 80, 70, 200, // top
|
|
6, 70, 200, 210, // top rung front
|
|
6, 200, 200, 70, // under top rung
|
|
6, 210, 100, 70, // between top rung and middle
|
|
6, 210, 160, 70, // top of middle rung
|
|
6, 70, 180, 210, // front of middle rung
|
|
6, 100, 70, 210, // bottom of middle rung.
|
|
6, 76, 210, 100, // front of bottom
|
|
6, 140, 210, 80, // bottom
|
|
6, 90, 130, 110, // left side
|
|
6, 160, 160, 220], [255]);
|
|
const numVerts = positions.length / 3;
|
|
const arrays = {
|
|
position: createAugmentedTypedArray(3, numVerts),
|
|
texcoord: createAugmentedTypedArray(2, numVerts),
|
|
normal: createAugmentedTypedArray(3, numVerts),
|
|
color: createAugmentedTypedArray(4, numVerts, Uint8Array),
|
|
indices: createAugmentedTypedArray(3, numVerts / 3, Uint16Array)
|
|
};
|
|
arrays.position.push(positions);
|
|
arrays.texcoord.push(texcoords);
|
|
arrays.normal.push(normals);
|
|
arrays.color.push(colors);
|
|
|
|
for (let ii = 0; ii < numVerts; ++ii) {
|
|
arrays.indices.push(ii);
|
|
}
|
|
|
|
return arrays;
|
|
}
|
|
/**
|
|
* Creates crescent BufferInfo.
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext.
|
|
* @param {number} verticalRadius The vertical radius of the crescent.
|
|
* @param {number} outerRadius The outer radius of the crescent.
|
|
* @param {number} innerRadius The inner radius of the crescent.
|
|
* @param {number} thickness The thickness of the crescent.
|
|
* @param {number} subdivisionsDown number of steps around the crescent.
|
|
* @param {number} [startOffset] Where to start arc. Default 0.
|
|
* @param {number} [endOffset] Where to end arg. Default 1.
|
|
* @return {module:twgl.BufferInfo} The created BufferInfo.
|
|
* @memberOf module:twgl/primitives
|
|
* @function createCresentBufferInfo
|
|
*/
|
|
|
|
/**
|
|
* Creates crescent buffers.
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext.
|
|
* @param {number} verticalRadius The vertical radius of the crescent.
|
|
* @param {number} outerRadius The outer radius of the crescent.
|
|
* @param {number} innerRadius The inner radius of the crescent.
|
|
* @param {number} thickness The thickness of the crescent.
|
|
* @param {number} subdivisionsDown number of steps around the crescent.
|
|
* @param {number} [startOffset] Where to start arc. Default 0.
|
|
* @param {number} [endOffset] Where to end arg. Default 1.
|
|
* @return {Object.<string, WebGLBuffer>} The created buffers.
|
|
* @memberOf module:twgl/primitives
|
|
* @function createCresentBuffers
|
|
*/
|
|
|
|
/**
|
|
* Creates crescent vertices.
|
|
*
|
|
* @param {number} verticalRadius The vertical radius of the crescent.
|
|
* @param {number} outerRadius The outer radius of the crescent.
|
|
* @param {number} innerRadius The inner radius of the crescent.
|
|
* @param {number} thickness The thickness of the crescent.
|
|
* @param {number} subdivisionsDown number of steps around the crescent.
|
|
* @param {number} [startOffset] Where to start arc. Default 0.
|
|
* @param {number} [endOffset] Where to end arg. Default 1.
|
|
* @return {Object.<string, TypedArray>} The created vertices.
|
|
* @memberOf module:twgl/primitives
|
|
* @function createCresentBuffers
|
|
*/
|
|
|
|
/**
|
|
* Creates crescent BufferInfo.
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext.
|
|
* @param {number} verticalRadius The vertical radius of the crescent.
|
|
* @param {number} outerRadius The outer radius of the crescent.
|
|
* @param {number} innerRadius The inner radius of the crescent.
|
|
* @param {number} thickness The thickness of the crescent.
|
|
* @param {number} subdivisionsDown number of steps around the crescent.
|
|
* @param {number} [startOffset] Where to start arc. Default 0.
|
|
* @param {number} [endOffset] Where to end arg. Default 1.
|
|
* @return {module:twgl.BufferInfo} The created BufferInfo.
|
|
* @memberOf module:twgl/primitives
|
|
* @function createCrescentBufferInfo
|
|
*/
|
|
|
|
/**
|
|
* Creates crescent buffers.
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext.
|
|
* @param {number} verticalRadius The vertical radius of the crescent.
|
|
* @param {number} outerRadius The outer radius of the crescent.
|
|
* @param {number} innerRadius The inner radius of the crescent.
|
|
* @param {number} thickness The thickness of the crescent.
|
|
* @param {number} subdivisionsDown number of steps around the crescent.
|
|
* @param {number} [startOffset] Where to start arc. Default 0.
|
|
* @param {number} [endOffset] Where to end arg. Default 1.
|
|
* @return {Object.<string, WebGLBuffer>} The created buffers.
|
|
* @memberOf module:twgl/primitives
|
|
* @function createCrescentBuffers
|
|
*/
|
|
|
|
/**
|
|
* Creates crescent vertices.
|
|
*
|
|
* @param {number} verticalRadius The vertical radius of the crescent.
|
|
* @param {number} outerRadius The outer radius of the crescent.
|
|
* @param {number} innerRadius The inner radius of the crescent.
|
|
* @param {number} thickness The thickness of the crescent.
|
|
* @param {number} subdivisionsDown number of steps around the crescent.
|
|
* @param {number} [startOffset] Where to start arc. Default 0.
|
|
* @param {number} [endOffset] Where to end arg. Default 1.
|
|
* @return {Object.<string, TypedArray>} The created vertices.
|
|
* @memberOf module:twgl/primitives
|
|
*/
|
|
|
|
|
|
function createCrescentVertices(verticalRadius, outerRadius, innerRadius, thickness, subdivisionsDown, startOffset, endOffset) {
|
|
if (subdivisionsDown <= 0) {
|
|
throw new Error('subdivisionDown must be > 0');
|
|
}
|
|
|
|
startOffset = startOffset || 0;
|
|
endOffset = endOffset || 1;
|
|
const subdivisionsThick = 2;
|
|
const offsetRange = endOffset - startOffset;
|
|
const numVertices = (subdivisionsDown + 1) * 2 * (2 + subdivisionsThick);
|
|
const positions = createAugmentedTypedArray(3, numVertices);
|
|
const normals = createAugmentedTypedArray(3, numVertices);
|
|
const texcoords = createAugmentedTypedArray(2, numVertices);
|
|
|
|
function lerp(a, b, s) {
|
|
return a + (b - a) * s;
|
|
}
|
|
|
|
function createArc(arcRadius, x, normalMult, normalAdd, uMult, uAdd) {
|
|
for (let z = 0; z <= subdivisionsDown; z++) {
|
|
const uBack = x / (subdivisionsThick - 1);
|
|
const v = z / subdivisionsDown;
|
|
const xBack = (uBack - 0.5) * 2;
|
|
const angle = (startOffset + v * offsetRange) * Math.PI;
|
|
const s = Math.sin(angle);
|
|
const c = Math.cos(angle);
|
|
const radius = lerp(verticalRadius, arcRadius, s);
|
|
const px = xBack * thickness;
|
|
const py = c * verticalRadius;
|
|
const pz = s * radius;
|
|
positions.push(px, py, pz);
|
|
const n = add(multiply([0, s, c], normalMult), normalAdd);
|
|
normals.push(n);
|
|
texcoords.push(uBack * uMult + uAdd, v);
|
|
}
|
|
} // Generate the individual vertices in our vertex buffer.
|
|
|
|
|
|
for (let x = 0; x < subdivisionsThick; x++) {
|
|
const uBack = (x / (subdivisionsThick - 1) - 0.5) * 2;
|
|
createArc(outerRadius, x, [1, 1, 1], [0, 0, 0], 1, 0);
|
|
createArc(outerRadius, x, [0, 0, 0], [uBack, 0, 0], 0, 0);
|
|
createArc(innerRadius, x, [1, 1, 1], [0, 0, 0], 1, 0);
|
|
createArc(innerRadius, x, [0, 0, 0], [uBack, 0, 0], 0, 1);
|
|
} // Do outer surface.
|
|
|
|
|
|
const indices = createAugmentedTypedArray(3, subdivisionsDown * 2 * (2 + subdivisionsThick), Uint16Array);
|
|
|
|
function createSurface(leftArcOffset, rightArcOffset) {
|
|
for (let z = 0; z < subdivisionsDown; ++z) {
|
|
// Make triangle 1 of quad.
|
|
indices.push(leftArcOffset + z + 0, leftArcOffset + z + 1, rightArcOffset + z + 0); // Make triangle 2 of quad.
|
|
|
|
indices.push(leftArcOffset + z + 1, rightArcOffset + z + 1, rightArcOffset + z + 0);
|
|
}
|
|
}
|
|
|
|
const numVerticesDown = subdivisionsDown + 1; // front
|
|
|
|
createSurface(numVerticesDown * 0, numVerticesDown * 4); // right
|
|
|
|
createSurface(numVerticesDown * 5, numVerticesDown * 7); // back
|
|
|
|
createSurface(numVerticesDown * 6, numVerticesDown * 2); // left
|
|
|
|
createSurface(numVerticesDown * 3, numVerticesDown * 1);
|
|
return {
|
|
position: positions,
|
|
normal: normals,
|
|
texcoord: texcoords,
|
|
indices: indices
|
|
};
|
|
}
|
|
/**
|
|
* Creates cylinder BufferInfo. The cylinder will be created around the origin
|
|
* along the y-axis.
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext.
|
|
* @param {number} radius Radius of cylinder.
|
|
* @param {number} height Height of cylinder.
|
|
* @param {number} radialSubdivisions The number of subdivisions around the cylinder.
|
|
* @param {number} verticalSubdivisions The number of subdivisions down the cylinder.
|
|
* @param {boolean} [topCap] Create top cap. Default = true.
|
|
* @param {boolean} [bottomCap] Create bottom cap. Default = true.
|
|
* @return {module:twgl.BufferInfo} The created BufferInfo.
|
|
* @memberOf module:twgl/primitives
|
|
* @function createCylinderBufferInfo
|
|
*/
|
|
|
|
/**
|
|
* Creates cylinder buffers. The cylinder will be created around the origin
|
|
* along the y-axis.
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext.
|
|
* @param {number} radius Radius of cylinder.
|
|
* @param {number} height Height of cylinder.
|
|
* @param {number} radialSubdivisions The number of subdivisions around the cylinder.
|
|
* @param {number} verticalSubdivisions The number of subdivisions down the cylinder.
|
|
* @param {boolean} [topCap] Create top cap. Default = true.
|
|
* @param {boolean} [bottomCap] Create bottom cap. Default = true.
|
|
* @return {Object.<string, WebGLBuffer>} The created buffers.
|
|
* @memberOf module:twgl/primitives
|
|
* @function createCylinderBuffers
|
|
*/
|
|
|
|
/**
|
|
* Creates cylinder vertices. The cylinder will be created around the origin
|
|
* along the y-axis.
|
|
*
|
|
* @param {number} radius Radius of cylinder.
|
|
* @param {number} height Height of cylinder.
|
|
* @param {number} radialSubdivisions The number of subdivisions around the cylinder.
|
|
* @param {number} verticalSubdivisions The number of subdivisions down the cylinder.
|
|
* @param {boolean} [topCap] Create top cap. Default = true.
|
|
* @param {boolean} [bottomCap] Create bottom cap. Default = true.
|
|
* @return {Object.<string, TypedArray>} The created vertices.
|
|
* @memberOf module:twgl/primitives
|
|
*/
|
|
|
|
|
|
function createCylinderVertices(radius, height, radialSubdivisions, verticalSubdivisions, topCap, bottomCap) {
|
|
return createTruncatedConeVertices(radius, radius, height, radialSubdivisions, verticalSubdivisions, topCap, bottomCap);
|
|
}
|
|
/**
|
|
* Creates BufferInfo for a torus
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext.
|
|
* @param {number} radius radius of center of torus circle.
|
|
* @param {number} thickness radius of torus ring.
|
|
* @param {number} radialSubdivisions The number of subdivisions around the torus.
|
|
* @param {number} bodySubdivisions The number of subdivisions around the body torus.
|
|
* @param {boolean} [startAngle] start angle in radians. Default = 0.
|
|
* @param {boolean} [endAngle] end angle in radians. Default = Math.PI * 2.
|
|
* @return {module:twgl.BufferInfo} The created BufferInfo.
|
|
* @memberOf module:twgl/primitives
|
|
* @function createTorusBufferInfo
|
|
*/
|
|
|
|
/**
|
|
* Creates buffers for a torus
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext.
|
|
* @param {number} radius radius of center of torus circle.
|
|
* @param {number} thickness radius of torus ring.
|
|
* @param {number} radialSubdivisions The number of subdivisions around the torus.
|
|
* @param {number} bodySubdivisions The number of subdivisions around the body torus.
|
|
* @param {boolean} [startAngle] start angle in radians. Default = 0.
|
|
* @param {boolean} [endAngle] end angle in radians. Default = Math.PI * 2.
|
|
* @return {Object.<string, WebGLBuffer>} The created buffers.
|
|
* @memberOf module:twgl/primitives
|
|
* @function createTorusBuffers
|
|
*/
|
|
|
|
/**
|
|
* Creates vertices for a torus
|
|
*
|
|
* @param {number} radius radius of center of torus circle.
|
|
* @param {number} thickness radius of torus ring.
|
|
* @param {number} radialSubdivisions The number of subdivisions around the torus.
|
|
* @param {number} bodySubdivisions The number of subdivisions around the body torus.
|
|
* @param {boolean} [startAngle] start angle in radians. Default = 0.
|
|
* @param {boolean} [endAngle] end angle in radians. Default = Math.PI * 2.
|
|
* @return {Object.<string, TypedArray>} The created vertices.
|
|
* @memberOf module:twgl/primitives
|
|
*/
|
|
|
|
|
|
function createTorusVertices(radius, thickness, radialSubdivisions, bodySubdivisions, startAngle, endAngle) {
|
|
if (radialSubdivisions < 3) {
|
|
throw new Error('radialSubdivisions must be 3 or greater');
|
|
}
|
|
|
|
if (bodySubdivisions < 3) {
|
|
throw new Error('verticalSubdivisions must be 3 or greater');
|
|
}
|
|
|
|
startAngle = startAngle || 0;
|
|
endAngle = endAngle || Math.PI * 2;
|
|
const range = endAngle - startAngle;
|
|
const radialParts = radialSubdivisions + 1;
|
|
const bodyParts = bodySubdivisions + 1;
|
|
const numVertices = radialParts * bodyParts;
|
|
const positions = createAugmentedTypedArray(3, numVertices);
|
|
const normals = createAugmentedTypedArray(3, numVertices);
|
|
const texcoords = createAugmentedTypedArray(2, numVertices);
|
|
const indices = createAugmentedTypedArray(3, radialSubdivisions * bodySubdivisions * 2, Uint16Array);
|
|
|
|
for (let slice = 0; slice < bodyParts; ++slice) {
|
|
const v = slice / bodySubdivisions;
|
|
const sliceAngle = v * Math.PI * 2;
|
|
const sliceSin = Math.sin(sliceAngle);
|
|
const ringRadius = radius + sliceSin * thickness;
|
|
const ny = Math.cos(sliceAngle);
|
|
const y = ny * thickness;
|
|
|
|
for (let ring = 0; ring < radialParts; ++ring) {
|
|
const u = ring / radialSubdivisions;
|
|
const ringAngle = startAngle + u * range;
|
|
const xSin = Math.sin(ringAngle);
|
|
const zCos = Math.cos(ringAngle);
|
|
const x = xSin * ringRadius;
|
|
const z = zCos * ringRadius;
|
|
const nx = xSin * sliceSin;
|
|
const nz = zCos * sliceSin;
|
|
positions.push(x, y, z);
|
|
normals.push(nx, ny, nz);
|
|
texcoords.push(u, 1 - v);
|
|
}
|
|
}
|
|
|
|
for (let slice = 0; slice < bodySubdivisions; ++slice) {
|
|
// eslint-disable-line
|
|
for (let ring = 0; ring < radialSubdivisions; ++ring) {
|
|
// eslint-disable-line
|
|
const nextRingIndex = 1 + ring;
|
|
const nextSliceIndex = 1 + slice;
|
|
indices.push(radialParts * slice + ring, radialParts * nextSliceIndex + ring, radialParts * slice + nextRingIndex);
|
|
indices.push(radialParts * nextSliceIndex + ring, radialParts * nextSliceIndex + nextRingIndex, radialParts * slice + nextRingIndex);
|
|
}
|
|
}
|
|
|
|
return {
|
|
position: positions,
|
|
normal: normals,
|
|
texcoord: texcoords,
|
|
indices: indices
|
|
};
|
|
}
|
|
/**
|
|
* Creates a disc BufferInfo. The disc will be in the xz plane, centered at
|
|
* the origin. When creating, at least 3 divisions, or pie
|
|
* pieces, need to be specified, otherwise the triangles making
|
|
* up the disc will be degenerate. You can also specify the
|
|
* number of radial pieces `stacks`. A value of 1 for
|
|
* stacks will give you a simple disc of pie pieces. If you
|
|
* want to create an annulus you can set `innerRadius` to a
|
|
* value > 0. Finally, `stackPower` allows you to have the widths
|
|
* increase or decrease as you move away from the center. This
|
|
* is particularly useful when using the disc as a ground plane
|
|
* with a fixed camera such that you don't need the resolution
|
|
* of small triangles near the perimeter. For example, a value
|
|
* of 2 will produce stacks whose outside radius increases with
|
|
* the square of the stack index. A value of 1 will give uniform
|
|
* stacks.
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext.
|
|
* @param {number} radius Radius of the ground plane.
|
|
* @param {number} divisions Number of triangles in the ground plane (at least 3).
|
|
* @param {number} [stacks] Number of radial divisions (default=1).
|
|
* @param {number} [innerRadius] Default 0.
|
|
* @param {number} [stackPower] Power to raise stack size to for decreasing width.
|
|
* @return {module:twgl.BufferInfo} The created BufferInfo.
|
|
* @memberOf module:twgl/primitives
|
|
* @function createDiscBufferInfo
|
|
*/
|
|
|
|
/**
|
|
* Creates disc buffers. The disc will be in the xz plane, centered at
|
|
* the origin. When creating, at least 3 divisions, or pie
|
|
* pieces, need to be specified, otherwise the triangles making
|
|
* up the disc will be degenerate. You can also specify the
|
|
* number of radial pieces `stacks`. A value of 1 for
|
|
* stacks will give you a simple disc of pie pieces. If you
|
|
* want to create an annulus you can set `innerRadius` to a
|
|
* value > 0. Finally, `stackPower` allows you to have the widths
|
|
* increase or decrease as you move away from the center. This
|
|
* is particularly useful when using the disc as a ground plane
|
|
* with a fixed camera such that you don't need the resolution
|
|
* of small triangles near the perimeter. For example, a value
|
|
* of 2 will produce stacks whose outside radius increases with
|
|
* the square of the stack index. A value of 1 will give uniform
|
|
* stacks.
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext.
|
|
* @param {number} radius Radius of the ground plane.
|
|
* @param {number} divisions Number of triangles in the ground plane (at least 3).
|
|
* @param {number} [stacks] Number of radial divisions (default=1).
|
|
* @param {number} [innerRadius] Default 0.
|
|
* @param {number} [stackPower] Power to raise stack size to for decreasing width.
|
|
* @return {Object.<string, WebGLBuffer>} The created buffers.
|
|
* @memberOf module:twgl/primitives
|
|
* @function createDiscBuffers
|
|
*/
|
|
|
|
/**
|
|
* Creates disc vertices. The disc will be in the xz plane, centered at
|
|
* the origin. When creating, at least 3 divisions, or pie
|
|
* pieces, need to be specified, otherwise the triangles making
|
|
* up the disc will be degenerate. You can also specify the
|
|
* number of radial pieces `stacks`. A value of 1 for
|
|
* stacks will give you a simple disc of pie pieces. If you
|
|
* want to create an annulus you can set `innerRadius` to a
|
|
* value > 0. Finally, `stackPower` allows you to have the widths
|
|
* increase or decrease as you move away from the center. This
|
|
* is particularly useful when using the disc as a ground plane
|
|
* with a fixed camera such that you don't need the resolution
|
|
* of small triangles near the perimeter. For example, a value
|
|
* of 2 will produce stacks whose outside radius increases with
|
|
* the square of the stack index. A value of 1 will give uniform
|
|
* stacks.
|
|
*
|
|
* @param {number} radius Radius of the ground plane.
|
|
* @param {number} divisions Number of triangles in the ground plane (at least 3).
|
|
* @param {number} [stacks] Number of radial divisions (default=1).
|
|
* @param {number} [innerRadius] Default 0.
|
|
* @param {number} [stackPower] Power to raise stack size to for decreasing width.
|
|
* @return {Object.<string, TypedArray>} The created vertices.
|
|
* @memberOf module:twgl/primitives
|
|
*/
|
|
|
|
|
|
function createDiscVertices(radius, divisions, stacks, innerRadius, stackPower) {
|
|
if (divisions < 3) {
|
|
throw new Error('divisions must be at least 3');
|
|
}
|
|
|
|
stacks = stacks ? stacks : 1;
|
|
stackPower = stackPower ? stackPower : 1;
|
|
innerRadius = innerRadius ? innerRadius : 0; // Note: We don't share the center vertex because that would
|
|
// mess up texture coordinates.
|
|
|
|
const numVertices = (divisions + 1) * (stacks + 1);
|
|
const positions = createAugmentedTypedArray(3, numVertices);
|
|
const normals = createAugmentedTypedArray(3, numVertices);
|
|
const texcoords = createAugmentedTypedArray(2, numVertices);
|
|
const indices = createAugmentedTypedArray(3, stacks * divisions * 2, Uint16Array);
|
|
let firstIndex = 0;
|
|
const radiusSpan = radius - innerRadius;
|
|
const pointsPerStack = divisions + 1; // Build the disk one stack at a time.
|
|
|
|
for (let stack = 0; stack <= stacks; ++stack) {
|
|
const stackRadius = innerRadius + radiusSpan * Math.pow(stack / stacks, stackPower);
|
|
|
|
for (let i = 0; i <= divisions; ++i) {
|
|
const theta = 2.0 * Math.PI * i / divisions;
|
|
const x = stackRadius * Math.cos(theta);
|
|
const z = stackRadius * Math.sin(theta);
|
|
positions.push(x, 0, z);
|
|
normals.push(0, 1, 0);
|
|
texcoords.push(1 - i / divisions, stack / stacks);
|
|
|
|
if (stack > 0 && i !== divisions) {
|
|
// a, b, c and d are the indices of the vertices of a quad. unless
|
|
// the current stack is the one closest to the center, in which case
|
|
// the vertices a and b connect to the center vertex.
|
|
const a = firstIndex + (i + 1);
|
|
const b = firstIndex + i;
|
|
const c = firstIndex + i - pointsPerStack;
|
|
const d = firstIndex + (i + 1) - pointsPerStack; // Make a quad of the vertices a, b, c, d.
|
|
|
|
indices.push(a, b, c);
|
|
indices.push(a, c, d);
|
|
}
|
|
}
|
|
|
|
firstIndex += divisions + 1;
|
|
}
|
|
|
|
return {
|
|
position: positions,
|
|
normal: normals,
|
|
texcoord: texcoords,
|
|
indices: indices
|
|
};
|
|
}
|
|
/**
|
|
* creates a random integer between 0 and range - 1 inclusive.
|
|
* @param {number} range
|
|
* @return {number} random value between 0 and range - 1 inclusive.
|
|
* @private
|
|
*/
|
|
|
|
|
|
function randInt(range) {
|
|
return Math.random() * range | 0;
|
|
}
|
|
/**
|
|
* Used to supply random colors
|
|
* @callback RandomColorFunc
|
|
* @param {number} ndx index of triangle/quad if unindexed or index of vertex if indexed
|
|
* @param {number} channel 0 = red, 1 = green, 2 = blue, 3 = alpha
|
|
* @return {number} a number from 0 to 255
|
|
* @memberOf module:twgl/primitives
|
|
*/
|
|
|
|
/**
|
|
* @typedef {Object} RandomVerticesOptions
|
|
* @property {number} [vertsPerColor] Defaults to 3 for non-indexed vertices
|
|
* @property {module:twgl/primitives.RandomColorFunc} [rand] A function to generate random numbers
|
|
* @memberOf module:twgl/primitives
|
|
*/
|
|
|
|
/**
|
|
* Creates an augmentedTypedArray of random vertex colors.
|
|
* If the vertices are indexed (have an indices array) then will
|
|
* just make random colors. Otherwise assumes they are triangles
|
|
* and makes one random color for every 3 vertices.
|
|
* @param {Object.<string, AugmentedTypedArray>} vertices Vertices as returned from one of the createXXXVertices functions.
|
|
* @param {module:twgl/primitives.RandomVerticesOptions} [options] options.
|
|
* @return {Object.<string, AugmentedTypedArray>} same vertices as passed in with `color` added.
|
|
* @memberOf module:twgl/primitives
|
|
*/
|
|
|
|
|
|
function makeRandomVertexColors(vertices, options) {
|
|
options = options || {};
|
|
const numElements = vertices.position.numElements;
|
|
const vColors = createAugmentedTypedArray(4, numElements, Uint8Array);
|
|
|
|
const rand = options.rand || function (ndx, channel) {
|
|
return channel < 3 ? randInt(256) : 255;
|
|
};
|
|
|
|
vertices.color = vColors;
|
|
|
|
if (vertices.indices) {
|
|
// just make random colors if index
|
|
for (let ii = 0; ii < numElements; ++ii) {
|
|
vColors.push(rand(ii, 0), rand(ii, 1), rand(ii, 2), rand(ii, 3));
|
|
}
|
|
} else {
|
|
// make random colors per triangle
|
|
const numVertsPerColor = options.vertsPerColor || 3;
|
|
const numSets = numElements / numVertsPerColor;
|
|
|
|
for (let ii = 0; ii < numSets; ++ii) {
|
|
// eslint-disable-line
|
|
const color = [rand(ii, 0), rand(ii, 1), rand(ii, 2), rand(ii, 3)];
|
|
|
|
for (let jj = 0; jj < numVertsPerColor; ++jj) {
|
|
vColors.push(color);
|
|
}
|
|
}
|
|
}
|
|
|
|
return vertices;
|
|
}
|
|
/**
|
|
* creates a function that calls fn to create vertices and then
|
|
* creates a buffers for them
|
|
* @private
|
|
*/
|
|
|
|
|
|
function createBufferFunc(fn) {
|
|
return function (gl) {
|
|
const arrays = fn.apply(this, Array.prototype.slice.call(arguments, 1));
|
|
return createBuffersFromArrays(gl, arrays);
|
|
};
|
|
}
|
|
/**
|
|
* creates a function that calls fn to create vertices and then
|
|
* creates a bufferInfo object for them
|
|
* @private
|
|
*/
|
|
|
|
|
|
function createBufferInfoFunc(fn) {
|
|
return function (gl) {
|
|
const arrays = fn.apply(null, Array.prototype.slice.call(arguments, 1));
|
|
return createBufferInfoFromArrays(gl, arrays);
|
|
};
|
|
}
|
|
|
|
const arraySpecPropertyNames = ["numComponents", "size", "type", "normalize", "stride", "offset", "attrib", "name", "attribName"];
|
|
/**
|
|
* Copy elements from one array to another
|
|
*
|
|
* @param {Array|TypedArray} src source array
|
|
* @param {Array|TypedArray} dst dest array
|
|
* @param {number} dstNdx index in dest to copy src
|
|
* @param {number} [offset] offset to add to copied values
|
|
* @private
|
|
*/
|
|
|
|
function copyElements(src, dst, dstNdx, offset) {
|
|
offset = offset || 0;
|
|
const length = src.length;
|
|
|
|
for (let ii = 0; ii < length; ++ii) {
|
|
dst[dstNdx + ii] = src[ii] + offset;
|
|
}
|
|
}
|
|
/**
|
|
* Creates an array of the same time
|
|
*
|
|
* @param {(number[]|ArrayBufferView|module:twgl.FullArraySpec)} srcArray array who's type to copy
|
|
* @param {number} length size of new array
|
|
* @return {(number[]|ArrayBufferView|module:twgl.FullArraySpec)} array with same type as srcArray
|
|
* @private
|
|
*/
|
|
|
|
|
|
function createArrayOfSameType(srcArray, length) {
|
|
const arraySrc = getArray$1(srcArray);
|
|
const newArray = new arraySrc.constructor(length);
|
|
let newArraySpec = newArray; // If it appears to have been augmented make new one augmented
|
|
|
|
if (arraySrc.numComponents && arraySrc.numElements) {
|
|
augmentTypedArray(newArray, arraySrc.numComponents);
|
|
} // If it was a full spec make new one a full spec
|
|
|
|
|
|
if (srcArray.data) {
|
|
newArraySpec = {
|
|
data: newArray
|
|
};
|
|
copyNamedProperties(arraySpecPropertyNames, srcArray, newArraySpec);
|
|
}
|
|
|
|
return newArraySpec;
|
|
}
|
|
/**
|
|
* Concatenates sets of vertices
|
|
*
|
|
* Assumes the vertices match in composition. For example
|
|
* if one set of vertices has positions, normals, and indices
|
|
* all sets of vertices must have positions, normals, and indices
|
|
* and of the same type.
|
|
*
|
|
* Example:
|
|
*
|
|
* const cubeVertices = twgl.primitives.createCubeVertices(2);
|
|
* const sphereVertices = twgl.primitives.createSphereVertices(1, 10, 10);
|
|
* // move the sphere 2 units up
|
|
* twgl.primitives.reorientVertices(
|
|
* sphereVertices, twgl.m4.translation([0, 2, 0]));
|
|
* // merge the sphere with the cube
|
|
* const cubeSphereVertices = twgl.primitives.concatVertices(
|
|
* [cubeVertices, sphereVertices]);
|
|
* // turn them into WebGL buffers and attrib data
|
|
* const bufferInfo = twgl.createBufferInfoFromArrays(gl, cubeSphereVertices);
|
|
*
|
|
* @param {module:twgl.Arrays[]} arrays Array of arrays of vertices
|
|
* @return {module:twgl.Arrays} The concatenated vertices.
|
|
* @memberOf module:twgl/primitives
|
|
*/
|
|
|
|
|
|
function concatVertices(arrayOfArrays) {
|
|
const names = {};
|
|
let baseName; // get names of all arrays.
|
|
// and numElements for each set of vertices
|
|
|
|
for (let ii = 0; ii < arrayOfArrays.length; ++ii) {
|
|
const arrays = arrayOfArrays[ii];
|
|
Object.keys(arrays).forEach(function (name) {
|
|
// eslint-disable-line
|
|
if (!names[name]) {
|
|
names[name] = [];
|
|
}
|
|
|
|
if (!baseName && name !== 'indices') {
|
|
baseName = name;
|
|
}
|
|
|
|
const arrayInfo = arrays[name];
|
|
const numComponents = getNumComponents$1(arrayInfo, name);
|
|
const array = getArray$1(arrayInfo);
|
|
const numElements = array.length / numComponents;
|
|
names[name].push(numElements);
|
|
});
|
|
} // compute length of combined array
|
|
// and return one for reference
|
|
|
|
|
|
function getLengthOfCombinedArrays(name) {
|
|
let length = 0;
|
|
let arraySpec;
|
|
|
|
for (let ii = 0; ii < arrayOfArrays.length; ++ii) {
|
|
const arrays = arrayOfArrays[ii];
|
|
const arrayInfo = arrays[name];
|
|
const array = getArray$1(arrayInfo);
|
|
length += array.length;
|
|
|
|
if (!arraySpec || arrayInfo.data) {
|
|
arraySpec = arrayInfo;
|
|
}
|
|
}
|
|
|
|
return {
|
|
length: length,
|
|
spec: arraySpec
|
|
};
|
|
}
|
|
|
|
function copyArraysToNewArray(name, base, newArray) {
|
|
let baseIndex = 0;
|
|
let offset = 0;
|
|
|
|
for (let ii = 0; ii < arrayOfArrays.length; ++ii) {
|
|
const arrays = arrayOfArrays[ii];
|
|
const arrayInfo = arrays[name];
|
|
const array = getArray$1(arrayInfo);
|
|
|
|
if (name === 'indices') {
|
|
copyElements(array, newArray, offset, baseIndex);
|
|
baseIndex += base[ii];
|
|
} else {
|
|
copyElements(array, newArray, offset);
|
|
}
|
|
|
|
offset += array.length;
|
|
}
|
|
}
|
|
|
|
const base = names[baseName];
|
|
const newArrays = {};
|
|
Object.keys(names).forEach(function (name) {
|
|
const info = getLengthOfCombinedArrays(name);
|
|
const newArraySpec = createArrayOfSameType(info.spec, info.length);
|
|
copyArraysToNewArray(name, base, getArray$1(newArraySpec));
|
|
newArrays[name] = newArraySpec;
|
|
});
|
|
return newArrays;
|
|
}
|
|
/**
|
|
* Creates a duplicate set of vertices
|
|
*
|
|
* This is useful for calling reorientVertices when you
|
|
* also want to keep the original available
|
|
*
|
|
* @param {module:twgl.Arrays} arrays of vertices
|
|
* @return {module:twgl.Arrays} The duplicated vertices.
|
|
* @memberOf module:twgl/primitives
|
|
*/
|
|
|
|
|
|
function duplicateVertices(arrays) {
|
|
const newArrays = {};
|
|
Object.keys(arrays).forEach(function (name) {
|
|
const arraySpec = arrays[name];
|
|
const srcArray = getArray$1(arraySpec);
|
|
const newArraySpec = createArrayOfSameType(arraySpec, srcArray.length);
|
|
copyElements(srcArray, getArray$1(newArraySpec), 0);
|
|
newArrays[name] = newArraySpec;
|
|
});
|
|
return newArrays;
|
|
}
|
|
|
|
const create3DFBufferInfo = createBufferInfoFunc(create3DFVertices);
|
|
const create3DFBuffers = createBufferFunc(create3DFVertices);
|
|
const createCubeBufferInfo = createBufferInfoFunc(createCubeVertices);
|
|
const createCubeBuffers = createBufferFunc(createCubeVertices);
|
|
const createPlaneBufferInfo = createBufferInfoFunc(createPlaneVertices);
|
|
const createPlaneBuffers = createBufferFunc(createPlaneVertices);
|
|
const createSphereBufferInfo = createBufferInfoFunc(createSphereVertices);
|
|
const createSphereBuffers = createBufferFunc(createSphereVertices);
|
|
const createTruncatedConeBufferInfo = createBufferInfoFunc(createTruncatedConeVertices);
|
|
const createTruncatedConeBuffers = createBufferFunc(createTruncatedConeVertices);
|
|
const createXYQuadBufferInfo = createBufferInfoFunc(createXYQuadVertices);
|
|
const createXYQuadBuffers = createBufferFunc(createXYQuadVertices);
|
|
const createCrescentBufferInfo = createBufferInfoFunc(createCrescentVertices);
|
|
const createCrescentBuffers = createBufferFunc(createCrescentVertices);
|
|
const createCylinderBufferInfo = createBufferInfoFunc(createCylinderVertices);
|
|
const createCylinderBuffers = createBufferFunc(createCylinderVertices);
|
|
const createTorusBufferInfo = createBufferInfoFunc(createTorusVertices);
|
|
const createTorusBuffers = createBufferFunc(createTorusVertices);
|
|
const createDiscBufferInfo = createBufferInfoFunc(createDiscVertices);
|
|
const createDiscBuffers = createBufferFunc(createDiscVertices); // these were mis-spelled until 4.12
|
|
|
|
const createCresentBufferInfo = createCrescentBufferInfo;
|
|
const createCresentBuffers = createCrescentBuffers;
|
|
const createCresentVertices = createCrescentVertices;
|
|
var primitives = /*#__PURE__*/Object.freeze({
|
|
__proto__: null,
|
|
create3DFBufferInfo: create3DFBufferInfo,
|
|
create3DFBuffers: create3DFBuffers,
|
|
create3DFVertices: create3DFVertices,
|
|
createAugmentedTypedArray: createAugmentedTypedArray,
|
|
createCubeBufferInfo: createCubeBufferInfo,
|
|
createCubeBuffers: createCubeBuffers,
|
|
createCubeVertices: createCubeVertices,
|
|
createPlaneBufferInfo: createPlaneBufferInfo,
|
|
createPlaneBuffers: createPlaneBuffers,
|
|
createPlaneVertices: createPlaneVertices,
|
|
createSphereBufferInfo: createSphereBufferInfo,
|
|
createSphereBuffers: createSphereBuffers,
|
|
createSphereVertices: createSphereVertices,
|
|
createTruncatedConeBufferInfo: createTruncatedConeBufferInfo,
|
|
createTruncatedConeBuffers: createTruncatedConeBuffers,
|
|
createTruncatedConeVertices: createTruncatedConeVertices,
|
|
createXYQuadBufferInfo: createXYQuadBufferInfo,
|
|
createXYQuadBuffers: createXYQuadBuffers,
|
|
createXYQuadVertices: createXYQuadVertices,
|
|
createCresentBufferInfo: createCresentBufferInfo,
|
|
createCresentBuffers: createCresentBuffers,
|
|
createCresentVertices: createCresentVertices,
|
|
createCrescentBufferInfo: createCrescentBufferInfo,
|
|
createCrescentBuffers: createCrescentBuffers,
|
|
createCrescentVertices: createCrescentVertices,
|
|
createCylinderBufferInfo: createCylinderBufferInfo,
|
|
createCylinderBuffers: createCylinderBuffers,
|
|
createCylinderVertices: createCylinderVertices,
|
|
createTorusBufferInfo: createTorusBufferInfo,
|
|
createTorusBuffers: createTorusBuffers,
|
|
createTorusVertices: createTorusVertices,
|
|
createDiscBufferInfo: createDiscBufferInfo,
|
|
createDiscBuffers: createDiscBuffers,
|
|
createDiscVertices: createDiscVertices,
|
|
deindexVertices: deindexVertices,
|
|
flattenNormals: flattenNormals,
|
|
makeRandomVertexColors: makeRandomVertexColors,
|
|
reorientDirections: reorientDirections,
|
|
reorientNormals: reorientNormals,
|
|
reorientPositions: reorientPositions,
|
|
reorientVertices: reorientVertices,
|
|
concatVertices: concatVertices,
|
|
duplicateVertices: duplicateVertices
|
|
});
|
|
/*
|
|
* Copyright 2019 Gregg Tavares
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
/**
|
|
* Gets the gl version as a number
|
|
* @param {WebGLRenderingContext} gl A WebGLRenderingContext
|
|
* @return {number} version of gl
|
|
* @private
|
|
*/
|
|
//function getVersionAsNumber(gl) {
|
|
// return parseFloat(gl.getParameter(gl.VERSION).substr(6));
|
|
//}
|
|
|
|
/**
|
|
* Check if context is WebGL 2.0
|
|
* @param {WebGLRenderingContext} gl A WebGLRenderingContext
|
|
* @return {bool} true if it's WebGL 2.0
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
exports.primitives = primitives;
|
|
|
|
function isWebGL2(gl) {
|
|
// This is the correct check but it's slow
|
|
// return gl.getParameter(gl.VERSION).indexOf("WebGL 2.0") === 0;
|
|
// This might also be the correct check but I'm assuming it's slow-ish
|
|
// return gl instanceof WebGL2RenderingContext;
|
|
return !!gl.texStorage2D;
|
|
}
|
|
/**
|
|
* Check if context is WebGL 1.0
|
|
* @param {WebGLRenderingContext} gl A WebGLRenderingContext
|
|
* @return {bool} true if it's WebGL 1.0
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
|
|
function isWebGL1(gl) {
|
|
// This is the correct check but it's slow
|
|
// const version = getVersionAsNumber(gl);
|
|
// return version <= 1.0 && version > 0.0; // because as of 2016/5 Edge returns 0.96
|
|
// This might also be the correct check but I'm assuming it's slow-ish
|
|
// return gl instanceof WebGLRenderingContext;
|
|
return !gl.texStorage2D;
|
|
}
|
|
/**
|
|
* Gets a string for WebGL enum
|
|
*
|
|
* Note: Several enums are the same. Without more
|
|
* context (which function) it's impossible to always
|
|
* give the correct enum. As it is, for matching values
|
|
* it gives all enums. Checking the WebGL2RenderingContext
|
|
* that means
|
|
*
|
|
* 0 = ZERO | POINT | NONE | NO_ERROR
|
|
* 1 = ONE | LINES | SYNC_FLUSH_COMMANDS_BIT
|
|
* 32777 = BLEND_EQUATION_RGB | BLEND_EQUATION_RGB
|
|
* 36662 = COPY_READ_BUFFER | COPY_READ_BUFFER_BINDING
|
|
* 36663 = COPY_WRITE_BUFFER | COPY_WRITE_BUFFER_BINDING
|
|
* 36006 = FRAMEBUFFER_BINDING | DRAW_FRAMEBUFFER_BINDING
|
|
*
|
|
* It's also not useful for bits really unless you pass in individual bits.
|
|
* In other words
|
|
*
|
|
* const bits = gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT;
|
|
* twgl.glEnumToString(gl, bits); // not going to work
|
|
*
|
|
* Note that some enums only exist on extensions. If you
|
|
* want them to show up you need to pass the extension at least
|
|
* once. For example
|
|
*
|
|
* const ext = gl.getExtension('WEBGL_compressed_texture_s3tc');
|
|
* if (ext) {
|
|
* twgl.glEnumToString(ext, 0); // just prime the function
|
|
*
|
|
* ..later..
|
|
*
|
|
* const internalFormat = ext.COMPRESSED_RGB_S3TC_DXT1_EXT;
|
|
* console.log(twgl.glEnumToString(gl, internalFormat));
|
|
*
|
|
* Notice I didn't have to pass the extension the second time. This means
|
|
* you can have place that generically gets an enum for texture formats for example.
|
|
* and as long as you primed the function with the extensions
|
|
*
|
|
* If you're using `twgl.addExtensionsToContext` to enable your extensions
|
|
* then twgl will automatically get the extension's enums.
|
|
*
|
|
* @param {WebGLRenderingContext} gl A WebGLRenderingContext or any extension object
|
|
* @param {number} value the value of the enum you want to look up.
|
|
* @return {string} enum string or hex value
|
|
* @memberOf module:twgl
|
|
* @function glEnumToString
|
|
*/
|
|
|
|
|
|
const glEnumToString = function () {
|
|
const haveEnumsForType = {};
|
|
const enums = {};
|
|
|
|
function addEnums(gl) {
|
|
const type = gl.constructor.name;
|
|
|
|
if (!haveEnumsForType[type]) {
|
|
for (const key in gl) {
|
|
if (typeof gl[key] === 'number') {
|
|
const existing = enums[gl[key]];
|
|
enums[gl[key]] = existing ? `${existing} | ${key}` : key;
|
|
}
|
|
}
|
|
|
|
haveEnumsForType[type] = true;
|
|
}
|
|
}
|
|
|
|
return function glEnumToString(gl, value) {
|
|
addEnums(gl);
|
|
return enums[value] || (typeof value === 'number' ? `0x${value.toString(16)}` : value);
|
|
};
|
|
}();
|
|
|
|
exports.glEnumToString = glEnumToString;
|
|
var utils = /*#__PURE__*/Object.freeze({
|
|
__proto__: null,
|
|
glEnumToString: glEnumToString,
|
|
isWebGL1: isWebGL1,
|
|
isWebGL2: isWebGL2
|
|
});
|
|
/*
|
|
* Copyright 2019 Gregg Tavares
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
exports.utils = utils;
|
|
const defaults$1 = {
|
|
textureColor: new Uint8Array([128, 192, 255, 255]),
|
|
textureOptions: {},
|
|
crossOrigin: undefined
|
|
};
|
|
const isArrayBuffer$1 = isArrayBuffer; // Should we make this on demand?
|
|
|
|
const getShared2DContext = function () {
|
|
let s_ctx;
|
|
return function getShared2DContext() {
|
|
s_ctx = s_ctx || (typeof document !== 'undefined' && document.createElement ? document.createElement("canvas").getContext("2d") : null);
|
|
return s_ctx;
|
|
};
|
|
}(); // NOTE: Chrome supports 2D canvas in a Worker (behind flag as of v64 but
|
|
// not only does Firefox NOT support it but Firefox freezes immediately
|
|
// if you try to create one instead of just returning null and continuing.
|
|
// : (global.OffscreenCanvas && (new global.OffscreenCanvas(1, 1)).getContext("2d")); // OffscreenCanvas may not support 2d
|
|
// NOTE: We can maybe remove some of the need for the 2d canvas. In WebGL2
|
|
// we can use the various unpack settings. Otherwise we could try using
|
|
// the ability of an ImageBitmap to be cut. Unfortunately cutting an ImageBitmap
|
|
// is async and the current TWGL code expects a non-Async result though that
|
|
// might not be a problem. ImageBitmap though is not available in Edge or Safari
|
|
// as of 2018-01-02
|
|
|
|
/* PixelFormat */
|
|
|
|
|
|
const ALPHA = 0x1906;
|
|
const RGB = 0x1907;
|
|
const RGBA = 0x1908;
|
|
const LUMINANCE = 0x1909;
|
|
const LUMINANCE_ALPHA = 0x190A;
|
|
const DEPTH_COMPONENT = 0x1902;
|
|
const DEPTH_STENCIL = 0x84F9;
|
|
/* TextureWrapMode */
|
|
// const REPEAT = 0x2901;
|
|
// const MIRRORED_REPEAT = 0x8370;
|
|
|
|
const CLAMP_TO_EDGE = 0x812f;
|
|
/* TextureMagFilter */
|
|
|
|
const NEAREST = 0x2600;
|
|
const LINEAR = 0x2601;
|
|
/* TextureMinFilter */
|
|
// const NEAREST_MIPMAP_NEAREST = 0x2700;
|
|
// const LINEAR_MIPMAP_NEAREST = 0x2701;
|
|
// const NEAREST_MIPMAP_LINEAR = 0x2702;
|
|
// const LINEAR_MIPMAP_LINEAR = 0x2703;
|
|
|
|
/* Texture Target */
|
|
|
|
const TEXTURE_2D = 0x0de1;
|
|
const TEXTURE_CUBE_MAP = 0x8513;
|
|
const TEXTURE_3D = 0x806f;
|
|
const TEXTURE_2D_ARRAY = 0x8c1a;
|
|
/* Cubemap Targets */
|
|
|
|
const TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515;
|
|
const TEXTURE_CUBE_MAP_NEGATIVE_X = 0x8516;
|
|
const TEXTURE_CUBE_MAP_POSITIVE_Y = 0x8517;
|
|
const TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x8518;
|
|
const TEXTURE_CUBE_MAP_POSITIVE_Z = 0x8519;
|
|
const TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x851a;
|
|
/* Texture Parameters */
|
|
|
|
const TEXTURE_MIN_FILTER = 0x2801;
|
|
const TEXTURE_MAG_FILTER = 0x2800;
|
|
const TEXTURE_WRAP_S = 0x2802;
|
|
const TEXTURE_WRAP_T = 0x2803;
|
|
const TEXTURE_WRAP_R = 0x8072;
|
|
const TEXTURE_MIN_LOD = 0x813a;
|
|
const TEXTURE_MAX_LOD = 0x813b;
|
|
const TEXTURE_BASE_LEVEL = 0x813c;
|
|
const TEXTURE_MAX_LEVEL = 0x813d;
|
|
/* Pixel store */
|
|
|
|
const UNPACK_ALIGNMENT = 0x0cf5;
|
|
const UNPACK_ROW_LENGTH = 0x0cf2;
|
|
const UNPACK_IMAGE_HEIGHT = 0x806e;
|
|
const UNPACK_SKIP_PIXELS = 0x0cf4;
|
|
const UNPACK_SKIP_ROWS = 0x0cf3;
|
|
const UNPACK_SKIP_IMAGES = 0x806d;
|
|
const UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243;
|
|
const UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241;
|
|
const UNPACK_FLIP_Y_WEBGL = 0x9240;
|
|
const R8 = 0x8229;
|
|
const R8_SNORM = 0x8F94;
|
|
const R16F = 0x822D;
|
|
const R32F = 0x822E;
|
|
const R8UI = 0x8232;
|
|
const R8I = 0x8231;
|
|
const RG16UI = 0x823A;
|
|
const RG16I = 0x8239;
|
|
const RG32UI = 0x823C;
|
|
const RG32I = 0x823B;
|
|
const RG8 = 0x822B;
|
|
const RG8_SNORM = 0x8F95;
|
|
const RG16F = 0x822F;
|
|
const RG32F = 0x8230;
|
|
const RG8UI = 0x8238;
|
|
const RG8I = 0x8237;
|
|
const R16UI = 0x8234;
|
|
const R16I = 0x8233;
|
|
const R32UI = 0x8236;
|
|
const R32I = 0x8235;
|
|
const RGB8 = 0x8051;
|
|
const SRGB8 = 0x8C41;
|
|
const RGB565 = 0x8D62;
|
|
const RGB8_SNORM = 0x8F96;
|
|
const R11F_G11F_B10F = 0x8C3A;
|
|
const RGB9_E5 = 0x8C3D;
|
|
const RGB16F = 0x881B;
|
|
const RGB32F = 0x8815;
|
|
const RGB8UI = 0x8D7D;
|
|
const RGB8I = 0x8D8F;
|
|
const RGB16UI = 0x8D77;
|
|
const RGB16I = 0x8D89;
|
|
const RGB32UI = 0x8D71;
|
|
const RGB32I = 0x8D83;
|
|
const RGBA8 = 0x8058;
|
|
const SRGB8_ALPHA8 = 0x8C43;
|
|
const RGBA8_SNORM = 0x8F97;
|
|
const RGB5_A1 = 0x8057;
|
|
const RGBA4 = 0x8056;
|
|
const RGB10_A2 = 0x8059;
|
|
const RGBA16F = 0x881A;
|
|
const RGBA32F = 0x8814;
|
|
const RGBA8UI = 0x8D7C;
|
|
const RGBA8I = 0x8D8E;
|
|
const RGB10_A2UI = 0x906F;
|
|
const RGBA16UI = 0x8D76;
|
|
const RGBA16I = 0x8D88;
|
|
const RGBA32I = 0x8D82;
|
|
const RGBA32UI = 0x8D70;
|
|
const DEPTH_COMPONENT16 = 0x81A5;
|
|
const DEPTH_COMPONENT24 = 0x81A6;
|
|
const DEPTH_COMPONENT32F = 0x8CAC;
|
|
const DEPTH32F_STENCIL8 = 0x8CAD;
|
|
const DEPTH24_STENCIL8 = 0x88F0;
|
|
/* DataType */
|
|
|
|
const BYTE$2 = 0x1400;
|
|
const UNSIGNED_BYTE$2 = 0x1401;
|
|
const SHORT$2 = 0x1402;
|
|
const UNSIGNED_SHORT$2 = 0x1403;
|
|
const INT$2 = 0x1404;
|
|
const UNSIGNED_INT$2 = 0x1405;
|
|
const FLOAT$2 = 0x1406;
|
|
const UNSIGNED_SHORT_4_4_4_4$1 = 0x8033;
|
|
const UNSIGNED_SHORT_5_5_5_1$1 = 0x8034;
|
|
const UNSIGNED_SHORT_5_6_5$1 = 0x8363;
|
|
const HALF_FLOAT$1 = 0x140B;
|
|
const HALF_FLOAT_OES = 0x8D61; // Thanks Khronos for making this different >:(
|
|
|
|
const UNSIGNED_INT_2_10_10_10_REV$1 = 0x8368;
|
|
const UNSIGNED_INT_10F_11F_11F_REV$1 = 0x8C3B;
|
|
const UNSIGNED_INT_5_9_9_9_REV$1 = 0x8C3E;
|
|
const FLOAT_32_UNSIGNED_INT_24_8_REV$1 = 0x8DAD;
|
|
const UNSIGNED_INT_24_8$1 = 0x84FA;
|
|
const RG = 0x8227;
|
|
const RG_INTEGER = 0x8228;
|
|
const RED = 0x1903;
|
|
const RED_INTEGER = 0x8D94;
|
|
const RGB_INTEGER = 0x8D98;
|
|
const RGBA_INTEGER = 0x8D99;
|
|
const formatInfo = {};
|
|
{
|
|
// NOTE: this is named `numColorComponents` vs `numComponents` so we can let Uglify mangle
|
|
// the name.
|
|
const f = formatInfo;
|
|
f[ALPHA] = {
|
|
numColorComponents: 1
|
|
};
|
|
f[LUMINANCE] = {
|
|
numColorComponents: 1
|
|
};
|
|
f[LUMINANCE_ALPHA] = {
|
|
numColorComponents: 2
|
|
};
|
|
f[RGB] = {
|
|
numColorComponents: 3
|
|
};
|
|
f[RGBA] = {
|
|
numColorComponents: 4
|
|
};
|
|
f[RED] = {
|
|
numColorComponents: 1
|
|
};
|
|
f[RED_INTEGER] = {
|
|
numColorComponents: 1
|
|
};
|
|
f[RG] = {
|
|
numColorComponents: 2
|
|
};
|
|
f[RG_INTEGER] = {
|
|
numColorComponents: 2
|
|
};
|
|
f[RGB] = {
|
|
numColorComponents: 3
|
|
};
|
|
f[RGB_INTEGER] = {
|
|
numColorComponents: 3
|
|
};
|
|
f[RGBA] = {
|
|
numColorComponents: 4
|
|
};
|
|
f[RGBA_INTEGER] = {
|
|
numColorComponents: 4
|
|
};
|
|
f[DEPTH_COMPONENT] = {
|
|
numColorComponents: 1
|
|
};
|
|
f[DEPTH_STENCIL] = {
|
|
numColorComponents: 2
|
|
};
|
|
}
|
|
/**
|
|
* @typedef {Object} TextureFormatDetails
|
|
* @property {number} textureFormat format to pass texImage2D and similar functions.
|
|
* @property {boolean} colorRenderable true if you can render to this format of texture.
|
|
* @property {boolean} textureFilterable true if you can filter the texture, false if you can ony use `NEAREST`.
|
|
* @property {number[]} type Array of possible types you can pass to texImage2D and similar function
|
|
* @property {Object.<number,number>} bytesPerElementMap A map of types to bytes per element
|
|
* @private
|
|
*/
|
|
|
|
let s_textureInternalFormatInfo;
|
|
|
|
function getTextureInternalFormatInfo(internalFormat) {
|
|
if (!s_textureInternalFormatInfo) {
|
|
// NOTE: these properties need unique names so we can let Uglify mangle the name.
|
|
const t = {}; // unsized formats
|
|
|
|
t[ALPHA] = {
|
|
textureFormat: ALPHA,
|
|
colorRenderable: true,
|
|
textureFilterable: true,
|
|
bytesPerElement: [1, 2, 2, 4],
|
|
type: [UNSIGNED_BYTE$2, HALF_FLOAT$1, HALF_FLOAT_OES, FLOAT$2]
|
|
};
|
|
t[LUMINANCE] = {
|
|
textureFormat: LUMINANCE,
|
|
colorRenderable: true,
|
|
textureFilterable: true,
|
|
bytesPerElement: [1, 2, 2, 4],
|
|
type: [UNSIGNED_BYTE$2, HALF_FLOAT$1, HALF_FLOAT_OES, FLOAT$2]
|
|
};
|
|
t[LUMINANCE_ALPHA] = {
|
|
textureFormat: LUMINANCE_ALPHA,
|
|
colorRenderable: true,
|
|
textureFilterable: true,
|
|
bytesPerElement: [2, 4, 4, 8],
|
|
type: [UNSIGNED_BYTE$2, HALF_FLOAT$1, HALF_FLOAT_OES, FLOAT$2]
|
|
};
|
|
t[RGB] = {
|
|
textureFormat: RGB,
|
|
colorRenderable: true,
|
|
textureFilterable: true,
|
|
bytesPerElement: [3, 6, 6, 12, 2],
|
|
type: [UNSIGNED_BYTE$2, HALF_FLOAT$1, HALF_FLOAT_OES, FLOAT$2, UNSIGNED_SHORT_5_6_5$1]
|
|
};
|
|
t[RGBA] = {
|
|
textureFormat: RGBA,
|
|
colorRenderable: true,
|
|
textureFilterable: true,
|
|
bytesPerElement: [4, 8, 8, 16, 2, 2],
|
|
type: [UNSIGNED_BYTE$2, HALF_FLOAT$1, HALF_FLOAT_OES, FLOAT$2, UNSIGNED_SHORT_4_4_4_4$1, UNSIGNED_SHORT_5_5_5_1$1]
|
|
};
|
|
t[DEPTH_COMPONENT] = {
|
|
textureFormat: DEPTH_COMPONENT,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [2, 4],
|
|
type: [UNSIGNED_INT$2, UNSIGNED_SHORT$2]
|
|
}; // sized formats
|
|
|
|
t[R8] = {
|
|
textureFormat: RED,
|
|
colorRenderable: true,
|
|
textureFilterable: true,
|
|
bytesPerElement: [1],
|
|
type: [UNSIGNED_BYTE$2]
|
|
};
|
|
t[R8_SNORM] = {
|
|
textureFormat: RED,
|
|
colorRenderable: false,
|
|
textureFilterable: true,
|
|
bytesPerElement: [1],
|
|
type: [BYTE$2]
|
|
};
|
|
t[R16F] = {
|
|
textureFormat: RED,
|
|
colorRenderable: false,
|
|
textureFilterable: true,
|
|
bytesPerElement: [4, 2],
|
|
type: [FLOAT$2, HALF_FLOAT$1]
|
|
};
|
|
t[R32F] = {
|
|
textureFormat: RED,
|
|
colorRenderable: false,
|
|
textureFilterable: false,
|
|
bytesPerElement: [4],
|
|
type: [FLOAT$2]
|
|
};
|
|
t[R8UI] = {
|
|
textureFormat: RED_INTEGER,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [1],
|
|
type: [UNSIGNED_BYTE$2]
|
|
};
|
|
t[R8I] = {
|
|
textureFormat: RED_INTEGER,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [1],
|
|
type: [BYTE$2]
|
|
};
|
|
t[R16UI] = {
|
|
textureFormat: RED_INTEGER,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [2],
|
|
type: [UNSIGNED_SHORT$2]
|
|
};
|
|
t[R16I] = {
|
|
textureFormat: RED_INTEGER,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [2],
|
|
type: [SHORT$2]
|
|
};
|
|
t[R32UI] = {
|
|
textureFormat: RED_INTEGER,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [4],
|
|
type: [UNSIGNED_INT$2]
|
|
};
|
|
t[R32I] = {
|
|
textureFormat: RED_INTEGER,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [4],
|
|
type: [INT$2]
|
|
};
|
|
t[RG8] = {
|
|
textureFormat: RG,
|
|
colorRenderable: true,
|
|
textureFilterable: true,
|
|
bytesPerElement: [2],
|
|
type: [UNSIGNED_BYTE$2]
|
|
};
|
|
t[RG8_SNORM] = {
|
|
textureFormat: RG,
|
|
colorRenderable: false,
|
|
textureFilterable: true,
|
|
bytesPerElement: [2],
|
|
type: [BYTE$2]
|
|
};
|
|
t[RG16F] = {
|
|
textureFormat: RG,
|
|
colorRenderable: false,
|
|
textureFilterable: true,
|
|
bytesPerElement: [8, 4],
|
|
type: [FLOAT$2, HALF_FLOAT$1]
|
|
};
|
|
t[RG32F] = {
|
|
textureFormat: RG,
|
|
colorRenderable: false,
|
|
textureFilterable: false,
|
|
bytesPerElement: [8],
|
|
type: [FLOAT$2]
|
|
};
|
|
t[RG8UI] = {
|
|
textureFormat: RG_INTEGER,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [2],
|
|
type: [UNSIGNED_BYTE$2]
|
|
};
|
|
t[RG8I] = {
|
|
textureFormat: RG_INTEGER,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [2],
|
|
type: [BYTE$2]
|
|
};
|
|
t[RG16UI] = {
|
|
textureFormat: RG_INTEGER,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [4],
|
|
type: [UNSIGNED_SHORT$2]
|
|
};
|
|
t[RG16I] = {
|
|
textureFormat: RG_INTEGER,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [4],
|
|
type: [SHORT$2]
|
|
};
|
|
t[RG32UI] = {
|
|
textureFormat: RG_INTEGER,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [8],
|
|
type: [UNSIGNED_INT$2]
|
|
};
|
|
t[RG32I] = {
|
|
textureFormat: RG_INTEGER,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [8],
|
|
type: [INT$2]
|
|
};
|
|
t[RGB8] = {
|
|
textureFormat: RGB,
|
|
colorRenderable: true,
|
|
textureFilterable: true,
|
|
bytesPerElement: [3],
|
|
type: [UNSIGNED_BYTE$2]
|
|
};
|
|
t[SRGB8] = {
|
|
textureFormat: RGB,
|
|
colorRenderable: false,
|
|
textureFilterable: true,
|
|
bytesPerElement: [3],
|
|
type: [UNSIGNED_BYTE$2]
|
|
};
|
|
t[RGB565] = {
|
|
textureFormat: RGB,
|
|
colorRenderable: true,
|
|
textureFilterable: true,
|
|
bytesPerElement: [3, 2],
|
|
type: [UNSIGNED_BYTE$2, UNSIGNED_SHORT_5_6_5$1]
|
|
};
|
|
t[RGB8_SNORM] = {
|
|
textureFormat: RGB,
|
|
colorRenderable: false,
|
|
textureFilterable: true,
|
|
bytesPerElement: [3],
|
|
type: [BYTE$2]
|
|
};
|
|
t[R11F_G11F_B10F] = {
|
|
textureFormat: RGB,
|
|
colorRenderable: false,
|
|
textureFilterable: true,
|
|
bytesPerElement: [12, 6, 4],
|
|
type: [FLOAT$2, HALF_FLOAT$1, UNSIGNED_INT_10F_11F_11F_REV$1]
|
|
};
|
|
t[RGB9_E5] = {
|
|
textureFormat: RGB,
|
|
colorRenderable: false,
|
|
textureFilterable: true,
|
|
bytesPerElement: [12, 6, 4],
|
|
type: [FLOAT$2, HALF_FLOAT$1, UNSIGNED_INT_5_9_9_9_REV$1]
|
|
};
|
|
t[RGB16F] = {
|
|
textureFormat: RGB,
|
|
colorRenderable: false,
|
|
textureFilterable: true,
|
|
bytesPerElement: [12, 6],
|
|
type: [FLOAT$2, HALF_FLOAT$1]
|
|
};
|
|
t[RGB32F] = {
|
|
textureFormat: RGB,
|
|
colorRenderable: false,
|
|
textureFilterable: false,
|
|
bytesPerElement: [12],
|
|
type: [FLOAT$2]
|
|
};
|
|
t[RGB8UI] = {
|
|
textureFormat: RGB_INTEGER,
|
|
colorRenderable: false,
|
|
textureFilterable: false,
|
|
bytesPerElement: [3],
|
|
type: [UNSIGNED_BYTE$2]
|
|
};
|
|
t[RGB8I] = {
|
|
textureFormat: RGB_INTEGER,
|
|
colorRenderable: false,
|
|
textureFilterable: false,
|
|
bytesPerElement: [3],
|
|
type: [BYTE$2]
|
|
};
|
|
t[RGB16UI] = {
|
|
textureFormat: RGB_INTEGER,
|
|
colorRenderable: false,
|
|
textureFilterable: false,
|
|
bytesPerElement: [6],
|
|
type: [UNSIGNED_SHORT$2]
|
|
};
|
|
t[RGB16I] = {
|
|
textureFormat: RGB_INTEGER,
|
|
colorRenderable: false,
|
|
textureFilterable: false,
|
|
bytesPerElement: [6],
|
|
type: [SHORT$2]
|
|
};
|
|
t[RGB32UI] = {
|
|
textureFormat: RGB_INTEGER,
|
|
colorRenderable: false,
|
|
textureFilterable: false,
|
|
bytesPerElement: [12],
|
|
type: [UNSIGNED_INT$2]
|
|
};
|
|
t[RGB32I] = {
|
|
textureFormat: RGB_INTEGER,
|
|
colorRenderable: false,
|
|
textureFilterable: false,
|
|
bytesPerElement: [12],
|
|
type: [INT$2]
|
|
};
|
|
t[RGBA8] = {
|
|
textureFormat: RGBA,
|
|
colorRenderable: true,
|
|
textureFilterable: true,
|
|
bytesPerElement: [4],
|
|
type: [UNSIGNED_BYTE$2]
|
|
};
|
|
t[SRGB8_ALPHA8] = {
|
|
textureFormat: RGBA,
|
|
colorRenderable: true,
|
|
textureFilterable: true,
|
|
bytesPerElement: [4],
|
|
type: [UNSIGNED_BYTE$2]
|
|
};
|
|
t[RGBA8_SNORM] = {
|
|
textureFormat: RGBA,
|
|
colorRenderable: false,
|
|
textureFilterable: true,
|
|
bytesPerElement: [4],
|
|
type: [BYTE$2]
|
|
};
|
|
t[RGB5_A1] = {
|
|
textureFormat: RGBA,
|
|
colorRenderable: true,
|
|
textureFilterable: true,
|
|
bytesPerElement: [4, 2, 4],
|
|
type: [UNSIGNED_BYTE$2, UNSIGNED_SHORT_5_5_5_1$1, UNSIGNED_INT_2_10_10_10_REV$1]
|
|
};
|
|
t[RGBA4] = {
|
|
textureFormat: RGBA,
|
|
colorRenderable: true,
|
|
textureFilterable: true,
|
|
bytesPerElement: [4, 2],
|
|
type: [UNSIGNED_BYTE$2, UNSIGNED_SHORT_4_4_4_4$1]
|
|
};
|
|
t[RGB10_A2] = {
|
|
textureFormat: RGBA,
|
|
colorRenderable: true,
|
|
textureFilterable: true,
|
|
bytesPerElement: [4],
|
|
type: [UNSIGNED_INT_2_10_10_10_REV$1]
|
|
};
|
|
t[RGBA16F] = {
|
|
textureFormat: RGBA,
|
|
colorRenderable: false,
|
|
textureFilterable: true,
|
|
bytesPerElement: [16, 8],
|
|
type: [FLOAT$2, HALF_FLOAT$1]
|
|
};
|
|
t[RGBA32F] = {
|
|
textureFormat: RGBA,
|
|
colorRenderable: false,
|
|
textureFilterable: false,
|
|
bytesPerElement: [16],
|
|
type: [FLOAT$2]
|
|
};
|
|
t[RGBA8UI] = {
|
|
textureFormat: RGBA_INTEGER,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [4],
|
|
type: [UNSIGNED_BYTE$2]
|
|
};
|
|
t[RGBA8I] = {
|
|
textureFormat: RGBA_INTEGER,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [4],
|
|
type: [BYTE$2]
|
|
};
|
|
t[RGB10_A2UI] = {
|
|
textureFormat: RGBA_INTEGER,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [4],
|
|
type: [UNSIGNED_INT_2_10_10_10_REV$1]
|
|
};
|
|
t[RGBA16UI] = {
|
|
textureFormat: RGBA_INTEGER,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [8],
|
|
type: [UNSIGNED_SHORT$2]
|
|
};
|
|
t[RGBA16I] = {
|
|
textureFormat: RGBA_INTEGER,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [8],
|
|
type: [SHORT$2]
|
|
};
|
|
t[RGBA32I] = {
|
|
textureFormat: RGBA_INTEGER,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [16],
|
|
type: [INT$2]
|
|
};
|
|
t[RGBA32UI] = {
|
|
textureFormat: RGBA_INTEGER,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [16],
|
|
type: [UNSIGNED_INT$2]
|
|
}; // Sized Internal
|
|
|
|
t[DEPTH_COMPONENT16] = {
|
|
textureFormat: DEPTH_COMPONENT,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [2, 4],
|
|
type: [UNSIGNED_SHORT$2, UNSIGNED_INT$2]
|
|
};
|
|
t[DEPTH_COMPONENT24] = {
|
|
textureFormat: DEPTH_COMPONENT,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [4],
|
|
type: [UNSIGNED_INT$2]
|
|
};
|
|
t[DEPTH_COMPONENT32F] = {
|
|
textureFormat: DEPTH_COMPONENT,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [4],
|
|
type: [FLOAT$2]
|
|
};
|
|
t[DEPTH24_STENCIL8] = {
|
|
textureFormat: DEPTH_STENCIL,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [4],
|
|
type: [UNSIGNED_INT_24_8$1]
|
|
};
|
|
t[DEPTH32F_STENCIL8] = {
|
|
textureFormat: DEPTH_STENCIL,
|
|
colorRenderable: true,
|
|
textureFilterable: false,
|
|
bytesPerElement: [4],
|
|
type: [FLOAT_32_UNSIGNED_INT_24_8_REV$1]
|
|
};
|
|
Object.keys(t).forEach(function (internalFormat) {
|
|
const info = t[internalFormat];
|
|
info.bytesPerElementMap = {};
|
|
info.bytesPerElement.forEach(function (bytesPerElement, ndx) {
|
|
const type = info.type[ndx];
|
|
info.bytesPerElementMap[type] = bytesPerElement;
|
|
});
|
|
});
|
|
s_textureInternalFormatInfo = t;
|
|
}
|
|
|
|
return s_textureInternalFormatInfo[internalFormat];
|
|
}
|
|
/**
|
|
* Gets the number of bytes per element for a given internalFormat / type
|
|
* @param {number} internalFormat The internalFormat parameter from texImage2D etc..
|
|
* @param {number} type The type parameter for texImage2D etc..
|
|
* @return {number} the number of bytes per element for the given internalFormat, type combo
|
|
* @memberOf module:twgl/textures
|
|
*/
|
|
|
|
|
|
function getBytesPerElementForInternalFormat(internalFormat, type) {
|
|
const info = getTextureInternalFormatInfo(internalFormat);
|
|
|
|
if (!info) {
|
|
throw "unknown internal format";
|
|
}
|
|
|
|
const bytesPerElement = info.bytesPerElementMap[type];
|
|
|
|
if (bytesPerElement === undefined) {
|
|
throw "unknown internal format";
|
|
}
|
|
|
|
return bytesPerElement;
|
|
}
|
|
/**
|
|
* Info related to a specific texture internalFormat as returned
|
|
* from {@link module:twgl/textures.getFormatAndTypeForInternalFormat}.
|
|
*
|
|
* @typedef {Object} TextureFormatInfo
|
|
* @property {number} format Format to pass to texImage2D and related functions
|
|
* @property {number} type Type to pass to texImage2D and related functions
|
|
* @memberOf module:twgl/textures
|
|
*/
|
|
|
|
/**
|
|
* Gets the format and type for a given internalFormat
|
|
*
|
|
* @param {number} internalFormat The internal format
|
|
* @return {module:twgl/textures.TextureFormatInfo} the corresponding format and type,
|
|
* @memberOf module:twgl/textures
|
|
*/
|
|
|
|
|
|
function getFormatAndTypeForInternalFormat(internalFormat) {
|
|
const info = getTextureInternalFormatInfo(internalFormat);
|
|
|
|
if (!info) {
|
|
throw "unknown internal format";
|
|
}
|
|
|
|
return {
|
|
format: info.textureFormat,
|
|
type: info.type[0]
|
|
};
|
|
}
|
|
/**
|
|
* Returns true if value is power of 2
|
|
* @param {number} value number to check.
|
|
* @return true if value is power of 2
|
|
* @private
|
|
*/
|
|
|
|
|
|
function isPowerOf2(value) {
|
|
return (value & value - 1) === 0;
|
|
}
|
|
/**
|
|
* Gets whether or not we can generate mips for the given
|
|
* internal format.
|
|
*
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @param {number} width The width parameter from texImage2D etc..
|
|
* @param {number} height The height parameter from texImage2D etc..
|
|
* @param {number} internalFormat The internalFormat parameter from texImage2D etc..
|
|
* @return {boolean} true if we can generate mips
|
|
* @memberOf module:twgl/textures
|
|
*/
|
|
|
|
|
|
function canGenerateMipmap(gl, width, height, internalFormat) {
|
|
if (!isWebGL2(gl)) {
|
|
return isPowerOf2(width) && isPowerOf2(height);
|
|
}
|
|
|
|
const info = getTextureInternalFormatInfo(internalFormat);
|
|
|
|
if (!info) {
|
|
throw "unknown internal format";
|
|
}
|
|
|
|
return info.colorRenderable && info.textureFilterable;
|
|
}
|
|
/**
|
|
* Gets whether or not we can generate mips for the given format
|
|
* @param {number} internalFormat The internalFormat parameter from texImage2D etc..
|
|
* @return {boolean} true if we can generate mips
|
|
* @memberOf module:twgl/textures
|
|
*/
|
|
|
|
|
|
function canFilter(internalFormat) {
|
|
const info = getTextureInternalFormatInfo(internalFormat);
|
|
|
|
if (!info) {
|
|
throw "unknown internal format";
|
|
}
|
|
|
|
return info.textureFilterable;
|
|
}
|
|
/**
|
|
* Gets the number of components for a given image format.
|
|
* @param {number} format the format.
|
|
* @return {number} the number of components for the format.
|
|
* @memberOf module:twgl/textures
|
|
*/
|
|
|
|
|
|
function getNumComponentsForFormat(format) {
|
|
const info = formatInfo[format];
|
|
|
|
if (!info) {
|
|
throw "unknown format: " + format;
|
|
}
|
|
|
|
return info.numColorComponents;
|
|
}
|
|
/**
|
|
* Gets the texture type for a given array type.
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @return {number} the gl texture type
|
|
* @private
|
|
*/
|
|
|
|
|
|
function getTextureTypeForArrayType(gl, src, defaultType) {
|
|
if (isArrayBuffer$1(src)) {
|
|
return getGLTypeForTypedArray(src);
|
|
}
|
|
|
|
return defaultType || UNSIGNED_BYTE$2;
|
|
}
|
|
|
|
function guessDimensions(gl, target, width, height, numElements) {
|
|
if (numElements % 1 !== 0) {
|
|
throw "can't guess dimensions";
|
|
}
|
|
|
|
if (!width && !height) {
|
|
const size = Math.sqrt(numElements / (target === TEXTURE_CUBE_MAP ? 6 : 1));
|
|
|
|
if (size % 1 === 0) {
|
|
width = size;
|
|
height = size;
|
|
} else {
|
|
width = numElements;
|
|
height = 1;
|
|
}
|
|
} else if (!height) {
|
|
height = numElements / width;
|
|
|
|
if (height % 1) {
|
|
throw "can't guess dimensions";
|
|
}
|
|
} else if (!width) {
|
|
width = numElements / height;
|
|
|
|
if (width % 1) {
|
|
throw "can't guess dimensions";
|
|
}
|
|
}
|
|
|
|
return {
|
|
width: width,
|
|
height: height
|
|
};
|
|
}
|
|
/**
|
|
* Sets the default texture color.
|
|
*
|
|
* The default texture color is used when loading textures from
|
|
* urls. Because the URL will be loaded async we'd like to be
|
|
* able to use the texture immediately. By putting a 1x1 pixel
|
|
* color in the texture we can start using the texture before
|
|
* the URL has loaded.
|
|
*
|
|
* @param {number[]} color Array of 4 values in the range 0 to 1
|
|
* @deprecated see {@link module:twgl.setDefaults}
|
|
* @memberOf module:twgl/textures
|
|
*/
|
|
|
|
|
|
function setDefaultTextureColor(color) {
|
|
defaults$1.textureColor = new Uint8Array([color[0] * 255, color[1] * 255, color[2] * 255, color[3] * 255]);
|
|
}
|
|
|
|
function setDefaults$1(newDefaults) {
|
|
copyExistingProperties(newDefaults, defaults$1);
|
|
|
|
if (newDefaults.textureColor) {
|
|
setDefaultTextureColor(newDefaults.textureColor);
|
|
}
|
|
}
|
|
/**
|
|
* A function to generate the source for a texture.
|
|
* @callback TextureFunc
|
|
* @param {WebGLRenderingContext} gl A WebGLRenderingContext
|
|
* @param {module:twgl.TextureOptions} options the texture options
|
|
* @return {*} Returns any of the things documented for `src` for {@link module:twgl.TextureOptions}.
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* Texture options passed to most texture functions. Each function will use whatever options
|
|
* are appropriate for its needs. This lets you pass the same options to all functions.
|
|
*
|
|
* Note: A `TexImageSource` is defined in the WebGL spec as a `HTMLImageElement`, `HTMLVideoElement`,
|
|
* `HTMLCanvasElement`, `ImageBitmap`, or `ImageData`.
|
|
*
|
|
* @typedef {Object} TextureOptions
|
|
* @property {number} [target] the type of texture `gl.TEXTURE_2D` or `gl.TEXTURE_CUBE_MAP`. Defaults to `gl.TEXTURE_2D`.
|
|
* @property {number} [level] the mip level to affect. Defaults to 0. Note, if set auto will be considered false unless explicitly set to true.
|
|
* @property {number} [width] the width of the texture. Only used if src is an array or typed array or null.
|
|
* @property {number} [height] the height of a texture. Only used if src is an array or typed array or null.
|
|
* @property {number} [depth] the depth of a texture. Only used if src is an array or type array or null and target is `TEXTURE_3D` .
|
|
* @property {number} [min] the min filter setting (eg. `gl.LINEAR`). Defaults to `gl.NEAREST_MIPMAP_LINEAR`
|
|
* or if texture is not a power of 2 on both dimensions then defaults to `gl.LINEAR`.
|
|
* @property {number} [mag] the mag filter setting (eg. `gl.LINEAR`). Defaults to `gl.LINEAR`
|
|
* @property {number} [minMag] both the min and mag filter settings.
|
|
* @property {number} [internalFormat] internal format for texture. Defaults to `gl.RGBA`
|
|
* @property {number} [format] format for texture. Defaults to `gl.RGBA`.
|
|
* @property {number} [type] type for texture. Defaults to `gl.UNSIGNED_BYTE` unless `src` is ArrayBufferView. If `src`
|
|
* is ArrayBufferView defaults to type that matches ArrayBufferView type.
|
|
* @property {number} [wrap] Texture wrapping for both S and T (and R if TEXTURE_3D or WebGLSampler). Defaults to `gl.REPEAT` for 2D unless src is WebGL1 and src not npot and `gl.CLAMP_TO_EDGE` for cube
|
|
* @property {number} [wrapS] Texture wrapping for S. Defaults to `gl.REPEAT` and `gl.CLAMP_TO_EDGE` for cube. If set takes precedence over `wrap`.
|
|
* @property {number} [wrapT] Texture wrapping for T. Defaults to `gl.REPEAT` and `gl.CLAMP_TO_EDGE` for cube. If set takes precedence over `wrap`.
|
|
* @property {number} [wrapR] Texture wrapping for R. Defaults to `gl.REPEAT` and `gl.CLAMP_TO_EDGE` for cube. If set takes precedence over `wrap`.
|
|
* @property {number} [minLod] TEXTURE_MIN_LOD setting
|
|
* @property {number} [maxLod] TEXTURE_MAX_LOD setting
|
|
* @property {number} [baseLevel] TEXTURE_BASE_LEVEL setting
|
|
* @property {number} [maxLevel] TEXTURE_MAX_LEVEL setting
|
|
* @property {number} [unpackAlignment] The `gl.UNPACK_ALIGNMENT` used when uploading an array. Defaults to 1.
|
|
* @property {number[]|ArrayBufferView} [color] Color to initialize this texture with if loading an image asynchronously.
|
|
* The default use a blue 1x1 pixel texture. You can set another default by calling `twgl.setDefaults`
|
|
* or you can set an individual texture's initial color by setting this property. Example: `[1, .5, .5, 1]` = pink
|
|
* @property {number} [premultiplyAlpha] Whether or not to premultiply alpha. Defaults to whatever the current setting is.
|
|
* This lets you set it once before calling `twgl.createTexture` or `twgl.createTextures` and only override
|
|
* the current setting for specific textures.
|
|
* @property {number} [flipY] Whether or not to flip the texture vertically on upload. Defaults to whatever the current setting is.
|
|
* This lets you set it once before calling `twgl.createTexture` or `twgl.createTextures` and only override
|
|
* the current setting for specific textures.
|
|
* @property {number} [colorspaceConversion] Whether or not to let the browser do colorspace conversion of the texture on upload. Defaults to whatever the current setting is.
|
|
* This lets you set it once before calling `twgl.createTexture` or `twgl.createTextures` and only override
|
|
* the current setting for specific textures.
|
|
* @property {boolean} [auto] If `undefined` or `true`, in WebGL1, texture filtering is set automatically for non-power of 2 images and
|
|
* mips are generated for power of 2 images. In WebGL2 mips are generated if they can be. Note: if `level` is set above
|
|
* then then `auto` is assumed to be `false` unless explicity set to `true`.
|
|
* @property {number[]} [cubeFaceOrder] The order that cube faces are pulled out of an img or set of images. The default is
|
|
*
|
|
* [gl.TEXTURE_CUBE_MAP_POSITIVE_X,
|
|
* gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
|
|
* gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
|
|
* gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
|
|
* gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
|
|
* gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]
|
|
*
|
|
* @property {(number[]|ArrayBufferView|TexImageSource|TexImageSource[]|string|string[]|module:twgl.TextureFunc)} [src] source for texture
|
|
*
|
|
* If `string` then it's assumed to be a URL to an image. The image will be downloaded async. A usable
|
|
* 1x1 pixel texture will be returned immediately. The texture will be updated once the image has downloaded.
|
|
* If `target` is `gl.TEXTURE_CUBE_MAP` will attempt to divide image into 6 square pieces. 1x6, 6x1, 3x2, 2x3.
|
|
* The pieces will be uploaded in `cubeFaceOrder`
|
|
*
|
|
* If `string[]` or `TexImageSource[]` and target is `gl.TEXTURE_CUBE_MAP` then it must have 6 entries, one for each face of a cube map.
|
|
*
|
|
* If `string[]` or `TexImageSource[]` and target is `gl.TEXTURE_2D_ARRAY` then each entry is a slice of the a 2d array texture
|
|
* and will be scaled to the specified width and height OR to the size of the first image that loads.
|
|
*
|
|
* If `TexImageSource` then it wil be used immediately to create the contents of the texture. Examples `HTMLImageElement`,
|
|
* `HTMLCanvasElement`, `HTMLVideoElement`.
|
|
*
|
|
* If `number[]` or `ArrayBufferView` it's assumed to be data for a texture. If `width` or `height` is
|
|
* not specified it is guessed as follows. First the number of elements is computed by `src.length / numComponents`
|
|
* where `numComponents` is derived from `format`. If `target` is `gl.TEXTURE_CUBE_MAP` then `numElements` is divided
|
|
* by 6. Then
|
|
*
|
|
* * If neither `width` nor `height` are specified and `sqrt(numElements)` is an integer then width and height
|
|
* are set to `sqrt(numElements)`. Otherwise `width = numElements` and `height = 1`.
|
|
*
|
|
* * If only one of `width` or `height` is specified then the other equals `numElements / specifiedDimension`.
|
|
*
|
|
* If `number[]` will be converted to `type`.
|
|
*
|
|
* If `src` is a function it will be called with a `WebGLRenderingContext` and these options.
|
|
* Whatever it returns is subject to these rules. So it can return a string url, an `HTMLElement`
|
|
* an array etc...
|
|
*
|
|
* If `src` is undefined then an empty texture will be created of size `width` by `height`.
|
|
*
|
|
* @property {string} [crossOrigin] What to set the crossOrigin property of images when they are downloaded.
|
|
* default: undefined. Also see {@link module:twgl.setDefaults}.
|
|
*
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* Sets any packing state that will be set based on the options.
|
|
* @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set.
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @private
|
|
*/
|
|
|
|
|
|
function setPackState(gl, options) {
|
|
if (options.colorspaceConversion !== undefined) {
|
|
gl.pixelStorei(UNPACK_COLORSPACE_CONVERSION_WEBGL, options.colorspaceConversion);
|
|
}
|
|
|
|
if (options.premultiplyAlpha !== undefined) {
|
|
gl.pixelStorei(UNPACK_PREMULTIPLY_ALPHA_WEBGL, options.premultiplyAlpha);
|
|
}
|
|
|
|
if (options.flipY !== undefined) {
|
|
gl.pixelStorei(UNPACK_FLIP_Y_WEBGL, options.flipY);
|
|
}
|
|
}
|
|
/**
|
|
* Set skip state to defaults
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @private
|
|
*/
|
|
|
|
|
|
function setSkipStateToDefault(gl) {
|
|
gl.pixelStorei(UNPACK_ALIGNMENT, 4);
|
|
|
|
if (isWebGL2(gl)) {
|
|
gl.pixelStorei(UNPACK_ROW_LENGTH, 0);
|
|
gl.pixelStorei(UNPACK_IMAGE_HEIGHT, 0);
|
|
gl.pixelStorei(UNPACK_SKIP_PIXELS, 0);
|
|
gl.pixelStorei(UNPACK_SKIP_ROWS, 0);
|
|
gl.pixelStorei(UNPACK_SKIP_IMAGES, 0);
|
|
}
|
|
}
|
|
/**
|
|
* Sets the parameters of a texture or sampler
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @param {number|WebGLSampler} target texture target or sampler
|
|
* @param {function()} parameteriFn texParameteri or samplerParameteri fn
|
|
* @param {WebGLTexture} tex the WebGLTexture to set parameters for
|
|
* @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set.
|
|
* This is often the same options you passed in when you created the texture.
|
|
* @private
|
|
*/
|
|
|
|
|
|
function setTextureSamplerParameters(gl, target, parameteriFn, options) {
|
|
if (options.minMag) {
|
|
parameteriFn.call(gl, target, TEXTURE_MIN_FILTER, options.minMag);
|
|
parameteriFn.call(gl, target, TEXTURE_MAG_FILTER, options.minMag);
|
|
}
|
|
|
|
if (options.min) {
|
|
parameteriFn.call(gl, target, TEXTURE_MIN_FILTER, options.min);
|
|
}
|
|
|
|
if (options.mag) {
|
|
parameteriFn.call(gl, target, TEXTURE_MAG_FILTER, options.mag);
|
|
}
|
|
|
|
if (options.wrap) {
|
|
parameteriFn.call(gl, target, TEXTURE_WRAP_S, options.wrap);
|
|
parameteriFn.call(gl, target, TEXTURE_WRAP_T, options.wrap);
|
|
|
|
if (target === TEXTURE_3D || isSampler(gl, target)) {
|
|
parameteriFn.call(gl, target, TEXTURE_WRAP_R, options.wrap);
|
|
}
|
|
}
|
|
|
|
if (options.wrapR) {
|
|
parameteriFn.call(gl, target, TEXTURE_WRAP_R, options.wrapR);
|
|
}
|
|
|
|
if (options.wrapS) {
|
|
parameteriFn.call(gl, target, TEXTURE_WRAP_S, options.wrapS);
|
|
}
|
|
|
|
if (options.wrapT) {
|
|
parameteriFn.call(gl, target, TEXTURE_WRAP_T, options.wrapT);
|
|
}
|
|
|
|
if (options.minLod) {
|
|
parameteriFn.call(gl, target, TEXTURE_MIN_LOD, options.minLod);
|
|
}
|
|
|
|
if (options.maxLod) {
|
|
parameteriFn.call(gl, target, TEXTURE_MAX_LOD, options.maxLod);
|
|
}
|
|
|
|
if (options.baseLevel) {
|
|
parameteriFn.call(gl, target, TEXTURE_BASE_LEVEL, options.baseLevel);
|
|
}
|
|
|
|
if (options.maxLevel) {
|
|
parameteriFn.call(gl, target, TEXTURE_MAX_LEVEL, options.maxLevel);
|
|
}
|
|
}
|
|
/**
|
|
* Sets the texture parameters of a texture.
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @param {WebGLTexture} tex the WebGLTexture to set parameters for
|
|
* @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set.
|
|
* This is often the same options you passed in when you created the texture.
|
|
* @memberOf module:twgl/textures
|
|
*/
|
|
|
|
|
|
function setTextureParameters(gl, tex, options) {
|
|
const target = options.target || TEXTURE_2D;
|
|
gl.bindTexture(target, tex);
|
|
setTextureSamplerParameters(gl, target, gl.texParameteri, options);
|
|
}
|
|
/**
|
|
* Sets the sampler parameters of a sampler.
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @param {WebGLSampler} sampler the WebGLSampler to set parameters for
|
|
* @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set.
|
|
* @memberOf module:twgl/textures
|
|
*/
|
|
|
|
|
|
function setSamplerParameters(gl, sampler, options) {
|
|
setTextureSamplerParameters(gl, sampler, gl.samplerParameteri, options);
|
|
}
|
|
/**
|
|
* Creates a new sampler object and sets parameters.
|
|
*
|
|
* Example:
|
|
*
|
|
* const sampler = twgl.createSampler(gl, {
|
|
* minMag: gl.NEAREST, // sets both TEXTURE_MIN_FILTER and TEXTURE_MAG_FILTER
|
|
* wrap: gl.CLAMP_TO_NEAREST, // sets both TEXTURE_WRAP_S and TEXTURE_WRAP_T and TEXTURE_WRAP_R
|
|
* });
|
|
*
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @param {Object.<string,module:twgl.TextureOptions>} options A object of TextureOptions one per sampler.
|
|
* @return {Object.<string,WebGLSampler>} the created samplers by name
|
|
* @private
|
|
*/
|
|
|
|
|
|
function createSampler(gl, options) {
|
|
const sampler = gl.createSampler();
|
|
setSamplerParameters(gl, sampler, options);
|
|
return sampler;
|
|
}
|
|
/**
|
|
* Creates a multiple sampler objects and sets parameters on each.
|
|
*
|
|
* Example:
|
|
*
|
|
* const samplers = twgl.createSamplers(gl, {
|
|
* nearest: {
|
|
* minMag: gl.NEAREST,
|
|
* },
|
|
* nearestClampS: {
|
|
* minMag: gl.NEAREST,
|
|
* wrapS: gl.CLAMP_TO_NEAREST,
|
|
* },
|
|
* linear: {
|
|
* minMag: gl.LINEAR,
|
|
* },
|
|
* nearestClamp: {
|
|
* minMag: gl.NEAREST,
|
|
* wrap: gl.CLAMP_TO_EDGE,
|
|
* },
|
|
* linearClamp: {
|
|
* minMag: gl.LINEAR,
|
|
* wrap: gl.CLAMP_TO_EDGE,
|
|
* },
|
|
* linearClampT: {
|
|
* minMag: gl.LINEAR,
|
|
* wrapT: gl.CLAMP_TO_EDGE,
|
|
* },
|
|
* });
|
|
*
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @param {module:twgl.TextureOptions} [options] A TextureOptions object with whatever parameters you want set on the sampler
|
|
* @private
|
|
*/
|
|
|
|
|
|
function createSamplers(gl, samplerOptions) {
|
|
const samplers = {};
|
|
Object.keys(samplerOptions).forEach(function (name) {
|
|
samplers[name] = createSampler(gl, samplerOptions[name]);
|
|
});
|
|
return samplers;
|
|
}
|
|
/**
|
|
* Makes a 1x1 pixel
|
|
* If no color is passed in uses the default color which can be set by calling `setDefaultTextureColor`.
|
|
* @param {(number[]|ArrayBufferView)} [color] The color using 0-1 values
|
|
* @return {Uint8Array} Unit8Array with color.
|
|
* @private
|
|
*/
|
|
|
|
|
|
function make1Pixel(color) {
|
|
color = color || defaults$1.textureColor;
|
|
|
|
if (isArrayBuffer$1(color)) {
|
|
return color;
|
|
}
|
|
|
|
return new Uint8Array([color[0] * 255, color[1] * 255, color[2] * 255, color[3] * 255]);
|
|
}
|
|
/**
|
|
* Sets filtering or generates mips for texture based on width or height
|
|
* If width or height is not passed in uses `options.width` and//or `options.height`
|
|
*
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @param {WebGLTexture} tex the WebGLTexture to set parameters for
|
|
* @param {module:twgl.TextureOptions} [options] A TextureOptions object with whatever parameters you want set.
|
|
* This is often the same options you passed in when you created the texture.
|
|
* @param {number} [width] width of texture
|
|
* @param {number} [height] height of texture
|
|
* @param {number} [internalFormat] The internalFormat parameter from texImage2D etc..
|
|
* @memberOf module:twgl/textures
|
|
*/
|
|
|
|
|
|
function setTextureFilteringForSize(gl, tex, options, width, height, internalFormat) {
|
|
options = options || defaults$1.textureOptions;
|
|
internalFormat = internalFormat || RGBA;
|
|
const target = options.target || TEXTURE_2D;
|
|
width = width || options.width;
|
|
height = height || options.height;
|
|
gl.bindTexture(target, tex);
|
|
|
|
if (canGenerateMipmap(gl, width, height, internalFormat)) {
|
|
gl.generateMipmap(target);
|
|
} else {
|
|
const filtering = canFilter(internalFormat) ? LINEAR : NEAREST;
|
|
gl.texParameteri(target, TEXTURE_MIN_FILTER, filtering);
|
|
gl.texParameteri(target, TEXTURE_MAG_FILTER, filtering);
|
|
gl.texParameteri(target, TEXTURE_WRAP_S, CLAMP_TO_EDGE);
|
|
gl.texParameteri(target, TEXTURE_WRAP_T, CLAMP_TO_EDGE);
|
|
}
|
|
}
|
|
|
|
function shouldAutomaticallySetTextureFilteringForSize(options) {
|
|
return options.auto === true || options.auto === undefined && options.level === undefined;
|
|
}
|
|
/**
|
|
* Gets an array of cubemap face enums
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set.
|
|
* This is often the same options you passed in when you created the texture.
|
|
* @return {number[]} cubemap face enums
|
|
* @private
|
|
*/
|
|
|
|
|
|
function getCubeFaceOrder(gl, options) {
|
|
options = options || {};
|
|
return options.cubeFaceOrder || [TEXTURE_CUBE_MAP_POSITIVE_X, TEXTURE_CUBE_MAP_NEGATIVE_X, TEXTURE_CUBE_MAP_POSITIVE_Y, TEXTURE_CUBE_MAP_NEGATIVE_Y, TEXTURE_CUBE_MAP_POSITIVE_Z, TEXTURE_CUBE_MAP_NEGATIVE_Z];
|
|
}
|
|
/**
|
|
* @typedef {Object} FaceInfo
|
|
* @property {number} face gl enum for texImage2D
|
|
* @property {number} ndx face index (0 - 5) into source data
|
|
* @ignore
|
|
*/
|
|
|
|
/**
|
|
* Gets an array of FaceInfos
|
|
* There's a bug in some NVidia drivers that will crash the driver if
|
|
* `gl.TEXTURE_CUBE_MAP_POSITIVE_X` is not uploaded first. So, we take
|
|
* the user's desired order from his faces to WebGL and make sure we
|
|
* do the faces in WebGL order
|
|
*
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set.
|
|
* @return {FaceInfo[]} cubemap face infos. Arguably the `face` property of each element is redundant but
|
|
* it's needed internally to sort the array of `ndx` properties by `face`.
|
|
* @private
|
|
*/
|
|
|
|
|
|
function getCubeFacesWithNdx(gl, options) {
|
|
const faces = getCubeFaceOrder(gl, options); // work around bug in NVidia drivers. We have to upload the first face first else the driver crashes :(
|
|
|
|
const facesWithNdx = faces.map(function (face, ndx) {
|
|
return {
|
|
face: face,
|
|
ndx: ndx
|
|
};
|
|
});
|
|
facesWithNdx.sort(function (a, b) {
|
|
return a.face - b.face;
|
|
});
|
|
return facesWithNdx;
|
|
}
|
|
/**
|
|
* Set a texture from the contents of an element. Will also set
|
|
* texture filtering or generate mips based on the dimensions of the element
|
|
* unless `options.auto === false`. If `target === gl.TEXTURE_CUBE_MAP` will
|
|
* attempt to slice image into 1x6, 2x3, 3x2, or 6x1 images, one for each face.
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @param {WebGLTexture} tex the WebGLTexture to set parameters for
|
|
* @param {HTMLElement} element a canvas, img, or video element.
|
|
* @param {module:twgl.TextureOptions} [options] A TextureOptions object with whatever parameters you want set.
|
|
* This is often the same options you passed in when you created the texture.
|
|
* @memberOf module:twgl/textures
|
|
* @kind function
|
|
*/
|
|
|
|
|
|
function setTextureFromElement(gl, tex, element, options) {
|
|
options = options || defaults$1.textureOptions;
|
|
const target = options.target || TEXTURE_2D;
|
|
const level = options.level || 0;
|
|
let width = element.width;
|
|
let height = element.height;
|
|
const internalFormat = options.internalFormat || options.format || RGBA;
|
|
const formatType = getFormatAndTypeForInternalFormat(internalFormat);
|
|
const format = options.format || formatType.format;
|
|
const type = options.type || formatType.type;
|
|
setPackState(gl, options);
|
|
gl.bindTexture(target, tex);
|
|
|
|
if (target === TEXTURE_CUBE_MAP) {
|
|
// guess the parts
|
|
const imgWidth = element.width;
|
|
const imgHeight = element.height;
|
|
let size;
|
|
let slices;
|
|
|
|
if (imgWidth / 6 === imgHeight) {
|
|
// It's 6x1
|
|
size = imgHeight;
|
|
slices = [0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0];
|
|
} else if (imgHeight / 6 === imgWidth) {
|
|
// It's 1x6
|
|
size = imgWidth;
|
|
slices = [0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5];
|
|
} else if (imgWidth / 3 === imgHeight / 2) {
|
|
// It's 3x2
|
|
size = imgWidth / 3;
|
|
slices = [0, 0, 1, 0, 2, 0, 0, 1, 1, 1, 2, 1];
|
|
} else if (imgWidth / 2 === imgHeight / 3) {
|
|
// It's 2x3
|
|
size = imgWidth / 2;
|
|
slices = [0, 0, 1, 0, 0, 1, 1, 1, 0, 2, 1, 2];
|
|
} else {
|
|
throw "can't figure out cube map from element: " + (element.src ? element.src : element.nodeName);
|
|
}
|
|
|
|
const ctx = getShared2DContext();
|
|
|
|
if (ctx) {
|
|
ctx.canvas.width = size;
|
|
ctx.canvas.height = size;
|
|
width = size;
|
|
height = size;
|
|
getCubeFacesWithNdx(gl, options).forEach(function (f) {
|
|
const xOffset = slices[f.ndx * 2 + 0] * size;
|
|
const yOffset = slices[f.ndx * 2 + 1] * size;
|
|
ctx.drawImage(element, xOffset, yOffset, size, size, 0, 0, size, size);
|
|
gl.texImage2D(f.face, level, internalFormat, format, type, ctx.canvas);
|
|
}); // Free up the canvas memory
|
|
|
|
ctx.canvas.width = 1;
|
|
ctx.canvas.height = 1;
|
|
} else if (typeof createImageBitmap !== 'undefined') {
|
|
// NOTE: It seems like we should prefer ImageBitmap because unlike canvas it's
|
|
// note lossy? (alpha is not premultiplied? although I'm not sure what
|
|
width = size;
|
|
height = size;
|
|
getCubeFacesWithNdx(gl, options).forEach(function (f) {
|
|
const xOffset = slices[f.ndx * 2 + 0] * size;
|
|
const yOffset = slices[f.ndx * 2 + 1] * size; // We can't easily use a default texture color here as it would have to match
|
|
// the type across all faces where as with a 2D one there's only one face
|
|
// so we're replacing everything all at once. It also has to be the correct size.
|
|
// On the other hand we need all faces to be the same size so as one face loads
|
|
// the rest match else the texture will be un-renderable.
|
|
|
|
gl.texImage2D(f.face, level, internalFormat, size, size, 0, format, type, null);
|
|
createImageBitmap(element, xOffset, yOffset, size, size, {
|
|
premultiplyAlpha: 'none',
|
|
colorSpaceConversion: 'none'
|
|
}).then(function (imageBitmap) {
|
|
setPackState(gl, options);
|
|
gl.bindTexture(target, tex);
|
|
gl.texImage2D(f.face, level, internalFormat, format, type, imageBitmap);
|
|
|
|
if (shouldAutomaticallySetTextureFilteringForSize(options)) {
|
|
setTextureFilteringForSize(gl, tex, options, width, height, internalFormat);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
} else if (target === TEXTURE_3D || target === TEXTURE_2D_ARRAY) {
|
|
const smallest = Math.min(element.width, element.height);
|
|
const largest = Math.max(element.width, element.height);
|
|
const depth = largest / smallest;
|
|
|
|
if (depth % 1 !== 0) {
|
|
throw "can not compute 3D dimensions of element";
|
|
}
|
|
|
|
const xMult = element.width === largest ? 1 : 0;
|
|
const yMult = element.height === largest ? 1 : 0;
|
|
gl.pixelStorei(UNPACK_ALIGNMENT, 1);
|
|
gl.pixelStorei(UNPACK_ROW_LENGTH, element.width);
|
|
gl.pixelStorei(UNPACK_IMAGE_HEIGHT, 0);
|
|
gl.pixelStorei(UNPACK_SKIP_IMAGES, 0);
|
|
gl.texImage3D(target, level, internalFormat, smallest, smallest, smallest, 0, format, type, null);
|
|
|
|
for (let d = 0; d < depth; ++d) {
|
|
const srcX = d * smallest * xMult;
|
|
const srcY = d * smallest * yMult;
|
|
gl.pixelStorei(UNPACK_SKIP_PIXELS, srcX);
|
|
gl.pixelStorei(UNPACK_SKIP_ROWS, srcY);
|
|
gl.texSubImage3D(target, level, 0, 0, d, smallest, smallest, 1, format, type, element);
|
|
}
|
|
|
|
setSkipStateToDefault(gl);
|
|
} else {
|
|
gl.texImage2D(target, level, internalFormat, format, type, element);
|
|
}
|
|
|
|
if (shouldAutomaticallySetTextureFilteringForSize(options)) {
|
|
setTextureFilteringForSize(gl, tex, options, width, height, internalFormat);
|
|
}
|
|
|
|
setTextureParameters(gl, tex, options);
|
|
}
|
|
|
|
function noop() {}
|
|
/**
|
|
* Checks whether the url's origin is the same so that we can set the `crossOrigin`
|
|
* @param {string} url url to image
|
|
* @returns {boolean} true if the window's origin is the same as image's url
|
|
* @private
|
|
*/
|
|
|
|
|
|
function urlIsSameOrigin(url) {
|
|
if (typeof document !== 'undefined') {
|
|
// for IE really
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
return a.hostname === location.hostname && a.port === location.port && a.protocol === location.protocol;
|
|
} else {
|
|
const localOrigin = new URL(location.href).origin;
|
|
const urlOrigin = new URL(url, location.href).origin;
|
|
return urlOrigin === localOrigin;
|
|
}
|
|
}
|
|
|
|
function setToAnonymousIfUndefinedAndURLIsNotSameOrigin(url, crossOrigin) {
|
|
return crossOrigin === undefined && !urlIsSameOrigin(url) ? 'anonymous' : crossOrigin;
|
|
}
|
|
/**
|
|
* Loads an image
|
|
* @param {string} url url to image
|
|
* @param {string} crossOrigin
|
|
* @param {function(err, img)} [callback] a callback that's passed an error and the image. The error will be non-null
|
|
* if there was an error
|
|
* @return {HTMLImageElement} the image being loaded.
|
|
* @private
|
|
*/
|
|
|
|
|
|
function loadImage(url, crossOrigin, callback) {
|
|
callback = callback || noop;
|
|
let img;
|
|
crossOrigin = crossOrigin !== undefined ? crossOrigin : defaults$1.crossOrigin;
|
|
crossOrigin = setToAnonymousIfUndefinedAndURLIsNotSameOrigin(url, crossOrigin);
|
|
|
|
if (typeof Image !== 'undefined') {
|
|
img = new Image();
|
|
|
|
if (crossOrigin !== undefined) {
|
|
img.crossOrigin = crossOrigin;
|
|
}
|
|
|
|
const clearEventHandlers = function clearEventHandlers() {
|
|
img.removeEventListener('error', onError); // eslint-disable-line
|
|
|
|
img.removeEventListener('load', onLoad); // eslint-disable-line
|
|
|
|
img = null;
|
|
};
|
|
|
|
const onError = function onError() {
|
|
const msg = "couldn't load image: " + url;
|
|
error(msg);
|
|
callback(msg, img);
|
|
clearEventHandlers();
|
|
};
|
|
|
|
const onLoad = function onLoad() {
|
|
callback(null, img);
|
|
clearEventHandlers();
|
|
};
|
|
|
|
img.addEventListener('error', onError);
|
|
img.addEventListener('load', onLoad);
|
|
img.src = url;
|
|
return img;
|
|
} else if (typeof ImageBitmap !== 'undefined') {
|
|
let err;
|
|
let bm;
|
|
|
|
const cb = function cb() {
|
|
callback(err, bm);
|
|
};
|
|
|
|
const options = {};
|
|
|
|
if (crossOrigin) {
|
|
options.mode = 'cors'; // TODO: not sure how to translate image.crossOrigin
|
|
}
|
|
|
|
fetch(url, options).then(function (response) {
|
|
if (!response.ok) {
|
|
throw response;
|
|
}
|
|
|
|
return response.blob();
|
|
}).then(function (blob) {
|
|
return createImageBitmap(blob, {
|
|
premultiplyAlpha: 'none',
|
|
colorSpaceConversion: 'none'
|
|
});
|
|
}).then(function (bitmap) {
|
|
// not sure if this works. We don't want
|
|
// to catch the user's error. So, call
|
|
// the callback in a timeout so we're
|
|
// not in this scope inside the promise.
|
|
bm = bitmap;
|
|
setTimeout(cb);
|
|
}).catch(function (e) {
|
|
err = e;
|
|
setTimeout(cb);
|
|
});
|
|
img = null;
|
|
}
|
|
|
|
return img;
|
|
}
|
|
/**
|
|
* check if object is a TexImageSource
|
|
*
|
|
* @param {Object} obj Object to test
|
|
* @return {boolean} true if object is a TexImageSource
|
|
* @private
|
|
*/
|
|
|
|
|
|
function isTexImageSource(obj) {
|
|
return typeof ImageBitmap !== 'undefined' && obj instanceof ImageBitmap || typeof ImageData !== 'undefined' && obj instanceof ImageData || typeof HTMLElement !== 'undefined' && obj instanceof HTMLElement;
|
|
}
|
|
/**
|
|
* if obj is an TexImageSource then just
|
|
* uses it otherwise if obj is a string
|
|
* then load it first.
|
|
*
|
|
* @param {string|TexImageSource} obj
|
|
* @param {string} crossOrigin
|
|
* @param {function(err, img)} [callback] a callback that's passed an error and the image. The error will be non-null
|
|
* if there was an error
|
|
* @private
|
|
*/
|
|
|
|
|
|
function loadAndUseImage(obj, crossOrigin, callback) {
|
|
if (isTexImageSource(obj)) {
|
|
setTimeout(function () {
|
|
callback(null, obj);
|
|
});
|
|
return obj;
|
|
}
|
|
|
|
return loadImage(obj, crossOrigin, callback);
|
|
}
|
|
/**
|
|
* Sets a texture to a 1x1 pixel color. If `options.color === false` is nothing happens. If it's not set
|
|
* the default texture color is used which can be set by calling `setDefaultTextureColor`.
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @param {WebGLTexture} tex the WebGLTexture to set parameters for
|
|
* @param {module:twgl.TextureOptions} [options] A TextureOptions object with whatever parameters you want set.
|
|
* This is often the same options you passed in when you created the texture.
|
|
* @memberOf module:twgl/textures
|
|
*/
|
|
|
|
|
|
function setTextureTo1PixelColor(gl, tex, options) {
|
|
options = options || defaults$1.textureOptions;
|
|
const target = options.target || TEXTURE_2D;
|
|
gl.bindTexture(target, tex);
|
|
|
|
if (options.color === false) {
|
|
return;
|
|
} // Assume it's a URL
|
|
// Put 1x1 pixels in texture. That makes it renderable immediately regardless of filtering.
|
|
|
|
|
|
const color = make1Pixel(options.color);
|
|
|
|
if (target === TEXTURE_CUBE_MAP) {
|
|
for (let ii = 0; ii < 6; ++ii) {
|
|
gl.texImage2D(TEXTURE_CUBE_MAP_POSITIVE_X + ii, 0, RGBA, 1, 1, 0, RGBA, UNSIGNED_BYTE$2, color);
|
|
}
|
|
} else if (target === TEXTURE_3D || target === TEXTURE_2D_ARRAY) {
|
|
gl.texImage3D(target, 0, RGBA, 1, 1, 1, 0, RGBA, UNSIGNED_BYTE$2, color);
|
|
} else {
|
|
gl.texImage2D(target, 0, RGBA, 1, 1, 0, RGBA, UNSIGNED_BYTE$2, color);
|
|
}
|
|
}
|
|
/**
|
|
* The src image(s) used to create a texture.
|
|
*
|
|
* When you call {@link module:twgl.createTexture} or {@link module:twgl.createTextures}
|
|
* you can pass in urls for images to load into the textures. If it's a single url
|
|
* then this will be a single HTMLImageElement. If it's an array of urls used for a cubemap
|
|
* this will be a corresponding array of images for the cubemap.
|
|
*
|
|
* @typedef {HTMLImageElement|HTMLImageElement[]} TextureSrc
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* A callback for when an image finished downloading and been uploaded into a texture
|
|
* @callback TextureReadyCallback
|
|
* @param {*} err If truthy there was an error.
|
|
* @param {WebGLTexture} texture the texture.
|
|
* @param {module:twgl.TextureSrc} source image(s) used to as the src for the texture
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* A callback for when all images have finished downloading and been uploaded into their respective textures
|
|
* @callback TexturesReadyCallback
|
|
* @param {*} err If truthy there was an error.
|
|
* @param {Object.<string, WebGLTexture>} textures the created textures by name. Same as returned by {@link module:twgl.createTextures}.
|
|
* @param {Object.<string, module:twgl.TextureSrc>} sources the image(s) used for the texture by name.
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* A callback for when an image finished downloading and been uploaded into a texture
|
|
* @callback CubemapReadyCallback
|
|
* @param {*} err If truthy there was an error.
|
|
* @param {WebGLTexture} tex the texture.
|
|
* @param {HTMLImageElement[]} imgs the images for each face.
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* A callback for when an image finished downloading and been uploaded into a texture
|
|
* @callback ThreeDReadyCallback
|
|
* @param {*} err If truthy there was an error.
|
|
* @param {WebGLTexture} tex the texture.
|
|
* @param {HTMLImageElement[]} imgs the images for each slice.
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* Loads a texture from an image from a Url as specified in `options.src`
|
|
* If `options.color !== false` will set the texture to a 1x1 pixel color so that the texture is
|
|
* immediately useable. It will be updated with the contents of the image once the image has finished
|
|
* downloading. Filtering options will be set as appropriate for image unless `options.auto === false`.
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @param {WebGLTexture} tex the WebGLTexture to set parameters for
|
|
* @param {module:twgl.TextureOptions} [options] A TextureOptions object with whatever parameters you want set.
|
|
* @param {module:twgl.TextureReadyCallback} [callback] A function to be called when the image has finished loading. err will
|
|
* be non null if there was an error.
|
|
* @return {HTMLImageElement} the image being downloaded.
|
|
* @memberOf module:twgl/textures
|
|
*/
|
|
|
|
|
|
function loadTextureFromUrl(gl, tex, options, callback) {
|
|
callback = callback || noop;
|
|
options = options || defaults$1.textureOptions;
|
|
setTextureTo1PixelColor(gl, tex, options); // Because it's async we need to copy the options.
|
|
|
|
options = Object.assign({}, options);
|
|
const img = loadAndUseImage(options.src, options.crossOrigin, function (err, img) {
|
|
if (err) {
|
|
callback(err, tex, img);
|
|
} else {
|
|
setTextureFromElement(gl, tex, img, options);
|
|
callback(null, tex, img);
|
|
}
|
|
});
|
|
return img;
|
|
}
|
|
/**
|
|
* Loads a cubemap from 6 urls or TexImageSources as specified in `options.src`. Will set the cubemap to a 1x1 pixel color
|
|
* so that it is usable immediately unless `option.color === false`.
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @param {WebGLTexture} tex the WebGLTexture to set parameters for
|
|
* @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set.
|
|
* @param {module:twgl.CubemapReadyCallback} [callback] A function to be called when all the images have finished loading. err will
|
|
* be non null if there was an error.
|
|
* @memberOf module:twgl/textures
|
|
*/
|
|
|
|
|
|
function loadCubemapFromUrls(gl, tex, options, callback) {
|
|
callback = callback || noop;
|
|
const urls = options.src;
|
|
|
|
if (urls.length !== 6) {
|
|
throw "there must be 6 urls for a cubemap";
|
|
}
|
|
|
|
const level = options.level || 0;
|
|
const internalFormat = options.internalFormat || options.format || RGBA;
|
|
const formatType = getFormatAndTypeForInternalFormat(internalFormat);
|
|
const format = options.format || formatType.format;
|
|
const type = options.type || UNSIGNED_BYTE$2;
|
|
const target = options.target || TEXTURE_2D;
|
|
|
|
if (target !== TEXTURE_CUBE_MAP) {
|
|
throw "target must be TEXTURE_CUBE_MAP";
|
|
}
|
|
|
|
setTextureTo1PixelColor(gl, tex, options); // Because it's async we need to copy the options.
|
|
|
|
options = Object.assign({}, options);
|
|
let numToLoad = 6;
|
|
const errors = [];
|
|
const faces = getCubeFaceOrder(gl, options);
|
|
let imgs; // eslint-disable-line
|
|
|
|
function uploadImg(faceTarget) {
|
|
return function (err, img) {
|
|
--numToLoad;
|
|
|
|
if (err) {
|
|
errors.push(err);
|
|
} else {
|
|
if (img.width !== img.height) {
|
|
errors.push("cubemap face img is not a square: " + img.src);
|
|
} else {
|
|
setPackState(gl, options);
|
|
gl.bindTexture(target, tex); // So assuming this is the first image we now have one face that's img sized
|
|
// and 5 faces that are 1x1 pixel so size the other faces
|
|
|
|
if (numToLoad === 5) {
|
|
// use the default order
|
|
getCubeFaceOrder().forEach(function (otherTarget) {
|
|
// Should we re-use the same face or a color?
|
|
gl.texImage2D(otherTarget, level, internalFormat, format, type, img);
|
|
});
|
|
} else {
|
|
gl.texImage2D(faceTarget, level, internalFormat, format, type, img);
|
|
}
|
|
|
|
if (shouldAutomaticallySetTextureFilteringForSize(options)) {
|
|
gl.generateMipmap(target);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (numToLoad === 0) {
|
|
callback(errors.length ? errors : undefined, tex, imgs);
|
|
}
|
|
};
|
|
}
|
|
|
|
imgs = urls.map(function (url, ndx) {
|
|
return loadAndUseImage(url, options.crossOrigin, uploadImg(faces[ndx]));
|
|
});
|
|
}
|
|
/**
|
|
* Loads a 2d array or 3d texture from urls OR TexImageSources as specified in `options.src`.
|
|
* Will set the texture to a 1x1 pixel color
|
|
* so that it is usable immediately unless `option.color === false`.
|
|
*
|
|
* If the width and height is not specified the width and height of the first
|
|
* image loaded will be used. Note that since images are loaded async
|
|
* which image downloads first is unknown.
|
|
*
|
|
* If an image is not the same size as the width and height it will be scaled
|
|
* to that width and height.
|
|
*
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @param {WebGLTexture} tex the WebGLTexture to set parameters for
|
|
* @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set.
|
|
* @param {module:twgl.ThreeDReadyCallback} [callback] A function to be called when all the images have finished loading. err will
|
|
* be non null if there was an error.
|
|
* @memberOf module:twgl/textures
|
|
*/
|
|
|
|
|
|
function loadSlicesFromUrls(gl, tex, options, callback) {
|
|
callback = callback || noop;
|
|
const urls = options.src;
|
|
const internalFormat = options.internalFormat || options.format || RGBA;
|
|
const formatType = getFormatAndTypeForInternalFormat(internalFormat);
|
|
const format = options.format || formatType.format;
|
|
const type = options.type || UNSIGNED_BYTE$2;
|
|
const target = options.target || TEXTURE_2D_ARRAY;
|
|
|
|
if (target !== TEXTURE_3D && target !== TEXTURE_2D_ARRAY) {
|
|
throw "target must be TEXTURE_3D or TEXTURE_2D_ARRAY";
|
|
}
|
|
|
|
setTextureTo1PixelColor(gl, tex, options); // Because it's async we need to copy the options.
|
|
|
|
options = Object.assign({}, options);
|
|
let numToLoad = urls.length;
|
|
const errors = [];
|
|
let imgs; // eslint-disable-line
|
|
|
|
const level = options.level || 0;
|
|
let width = options.width;
|
|
let height = options.height;
|
|
const depth = urls.length;
|
|
let firstImage = true;
|
|
|
|
function uploadImg(slice) {
|
|
return function (err, img) {
|
|
--numToLoad;
|
|
|
|
if (err) {
|
|
errors.push(err);
|
|
} else {
|
|
setPackState(gl, options);
|
|
gl.bindTexture(target, tex);
|
|
|
|
if (firstImage) {
|
|
firstImage = false;
|
|
width = options.width || img.width;
|
|
height = options.height || img.height;
|
|
gl.texImage3D(target, level, internalFormat, width, height, depth, 0, format, type, null); // put it in every slice otherwise some slices will be 0,0,0,0
|
|
|
|
for (let s = 0; s < depth; ++s) {
|
|
gl.texSubImage3D(target, level, 0, 0, s, width, height, 1, format, type, img);
|
|
}
|
|
} else {
|
|
let src = img;
|
|
let ctx;
|
|
|
|
if (img.width !== width || img.height !== height) {
|
|
// Size the image to fix
|
|
ctx = getShared2DContext();
|
|
src = ctx.canvas;
|
|
ctx.canvas.width = width;
|
|
ctx.canvas.height = height;
|
|
ctx.drawImage(img, 0, 0, width, height);
|
|
}
|
|
|
|
gl.texSubImage3D(target, level, 0, 0, slice, width, height, 1, format, type, src); // free the canvas memory
|
|
|
|
if (ctx && src === ctx.canvas) {
|
|
ctx.canvas.width = 0;
|
|
ctx.canvas.height = 0;
|
|
}
|
|
}
|
|
|
|
if (shouldAutomaticallySetTextureFilteringForSize(options)) {
|
|
gl.generateMipmap(target);
|
|
}
|
|
}
|
|
|
|
if (numToLoad === 0) {
|
|
callback(errors.length ? errors : undefined, tex, imgs);
|
|
}
|
|
};
|
|
}
|
|
|
|
imgs = urls.map(function (url, ndx) {
|
|
return loadAndUseImage(url, options.crossOrigin, uploadImg(ndx));
|
|
});
|
|
}
|
|
/**
|
|
* Sets a texture from an array or typed array. If the width or height is not provided will attempt to
|
|
* guess the size. See {@link module:twgl.TextureOptions}.
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @param {WebGLTexture} tex the WebGLTexture to set parameters for
|
|
* @param {(number[]|ArrayBufferView)} src An array or typed arry with texture data.
|
|
* @param {module:twgl.TextureOptions} [options] A TextureOptions object with whatever parameters you want set.
|
|
* This is often the same options you passed in when you created the texture.
|
|
* @memberOf module:twgl/textures
|
|
*/
|
|
|
|
|
|
function setTextureFromArray(gl, tex, src, options) {
|
|
options = options || defaults$1.textureOptions;
|
|
const target = options.target || TEXTURE_2D;
|
|
gl.bindTexture(target, tex);
|
|
let width = options.width;
|
|
let height = options.height;
|
|
let depth = options.depth;
|
|
const level = options.level || 0;
|
|
const internalFormat = options.internalFormat || options.format || RGBA;
|
|
const formatType = getFormatAndTypeForInternalFormat(internalFormat);
|
|
const format = options.format || formatType.format;
|
|
const type = options.type || getTextureTypeForArrayType(gl, src, formatType.type);
|
|
|
|
if (!isArrayBuffer$1(src)) {
|
|
const Type = getTypedArrayTypeForGLType(type);
|
|
src = new Type(src);
|
|
} else if (src instanceof Uint8ClampedArray) {
|
|
src = new Uint8Array(src.buffer);
|
|
}
|
|
|
|
const bytesPerElement = getBytesPerElementForInternalFormat(internalFormat, type);
|
|
const numElements = src.byteLength / bytesPerElement; // TODO: check UNPACK_ALIGNMENT?
|
|
|
|
if (numElements % 1) {
|
|
throw "length wrong size for format: " + glEnumToString(gl, format);
|
|
}
|
|
|
|
let dimensions;
|
|
|
|
if (target === TEXTURE_3D || target === TEXTURE_2D_ARRAY) {
|
|
if (!width && !height && !depth) {
|
|
const size = Math.cbrt(numElements);
|
|
|
|
if (size % 1 !== 0) {
|
|
throw "can't guess cube size of array of numElements: " + numElements;
|
|
}
|
|
|
|
width = size;
|
|
height = size;
|
|
depth = size;
|
|
} else if (width && (!height || !depth)) {
|
|
dimensions = guessDimensions(gl, target, height, depth, numElements / width);
|
|
height = dimensions.width;
|
|
depth = dimensions.height;
|
|
} else if (height && (!width || !depth)) {
|
|
dimensions = guessDimensions(gl, target, width, depth, numElements / height);
|
|
width = dimensions.width;
|
|
depth = dimensions.height;
|
|
} else {
|
|
dimensions = guessDimensions(gl, target, width, height, numElements / depth);
|
|
width = dimensions.width;
|
|
height = dimensions.height;
|
|
}
|
|
} else {
|
|
dimensions = guessDimensions(gl, target, width, height, numElements);
|
|
width = dimensions.width;
|
|
height = dimensions.height;
|
|
}
|
|
|
|
setSkipStateToDefault(gl);
|
|
gl.pixelStorei(UNPACK_ALIGNMENT, options.unpackAlignment || 1);
|
|
setPackState(gl, options);
|
|
|
|
if (target === TEXTURE_CUBE_MAP) {
|
|
const elementsPerElement = bytesPerElement / src.BYTES_PER_ELEMENT;
|
|
const faceSize = numElements / 6 * elementsPerElement;
|
|
getCubeFacesWithNdx(gl, options).forEach(f => {
|
|
const offset = faceSize * f.ndx;
|
|
const data = src.subarray(offset, offset + faceSize);
|
|
gl.texImage2D(f.face, level, internalFormat, width, height, 0, format, type, data);
|
|
});
|
|
} else if (target === TEXTURE_3D || target === TEXTURE_2D_ARRAY) {
|
|
gl.texImage3D(target, level, internalFormat, width, height, depth, 0, format, type, src);
|
|
} else {
|
|
gl.texImage2D(target, level, internalFormat, width, height, 0, format, type, src);
|
|
}
|
|
|
|
return {
|
|
width: width,
|
|
height: height,
|
|
depth: depth,
|
|
type: type
|
|
};
|
|
}
|
|
/**
|
|
* Sets a texture with no contents of a certain size. In other words calls `gl.texImage2D` with `null`.
|
|
* You must set `options.width` and `options.height`.
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @param {WebGLTexture} tex the WebGLTexture to set parameters for
|
|
* @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set.
|
|
* @memberOf module:twgl/textures
|
|
*/
|
|
|
|
|
|
function setEmptyTexture(gl, tex, options) {
|
|
const target = options.target || TEXTURE_2D;
|
|
gl.bindTexture(target, tex);
|
|
const level = options.level || 0;
|
|
const internalFormat = options.internalFormat || options.format || RGBA;
|
|
const formatType = getFormatAndTypeForInternalFormat(internalFormat);
|
|
const format = options.format || formatType.format;
|
|
const type = options.type || formatType.type;
|
|
setPackState(gl, options);
|
|
|
|
if (target === TEXTURE_CUBE_MAP) {
|
|
for (let ii = 0; ii < 6; ++ii) {
|
|
gl.texImage2D(TEXTURE_CUBE_MAP_POSITIVE_X + ii, level, internalFormat, options.width, options.height, 0, format, type, null);
|
|
}
|
|
} else if (target === TEXTURE_3D || target === TEXTURE_2D_ARRAY) {
|
|
gl.texImage3D(target, level, internalFormat, options.width, options.height, options.depth, 0, format, type, null);
|
|
} else {
|
|
gl.texImage2D(target, level, internalFormat, options.width, options.height, 0, format, type, null);
|
|
}
|
|
}
|
|
/**
|
|
* Creates a texture based on the options passed in.
|
|
*
|
|
* Note: may reset UNPACK_ALIGNMENT, UNPACK_ROW_LENGTH, UNPACK_IMAGE_HEIGHT, UNPACK_SKIP_IMAGES
|
|
* UNPACK_SKIP_PIXELS, and UNPACK_SKIP_ROWS
|
|
*
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @param {module:twgl.TextureOptions} [options] A TextureOptions object with whatever parameters you want set.
|
|
* @param {module:twgl.TextureReadyCallback} [callback] A callback called when an image has been downloaded and uploaded to the texture.
|
|
* @return {WebGLTexture} the created texture.
|
|
* @memberOf module:twgl/textures
|
|
*/
|
|
|
|
|
|
function createTexture(gl, options, callback) {
|
|
callback = callback || noop;
|
|
options = options || defaults$1.textureOptions;
|
|
const tex = gl.createTexture();
|
|
const target = options.target || TEXTURE_2D;
|
|
let width = options.width || 1;
|
|
let height = options.height || 1;
|
|
const internalFormat = options.internalFormat || RGBA;
|
|
gl.bindTexture(target, tex);
|
|
|
|
if (target === TEXTURE_CUBE_MAP) {
|
|
// this should have been the default for cubemaps :(
|
|
gl.texParameteri(target, TEXTURE_WRAP_S, CLAMP_TO_EDGE);
|
|
gl.texParameteri(target, TEXTURE_WRAP_T, CLAMP_TO_EDGE);
|
|
}
|
|
|
|
let src = options.src;
|
|
|
|
if (src) {
|
|
if (typeof src === "function") {
|
|
src = src(gl, options);
|
|
}
|
|
|
|
if (typeof src === "string") {
|
|
loadTextureFromUrl(gl, tex, options, callback);
|
|
} else if (isArrayBuffer$1(src) || Array.isArray(src) && (typeof src[0] === 'number' || Array.isArray(src[0]) || isArrayBuffer$1(src[0]))) {
|
|
const dimensions = setTextureFromArray(gl, tex, src, options);
|
|
width = dimensions.width;
|
|
height = dimensions.height;
|
|
} else if (Array.isArray(src) && (typeof src[0] === 'string' || isTexImageSource(src[0]))) {
|
|
if (target === TEXTURE_CUBE_MAP) {
|
|
loadCubemapFromUrls(gl, tex, options, callback);
|
|
} else {
|
|
loadSlicesFromUrls(gl, tex, options, callback);
|
|
}
|
|
} else {
|
|
// if (isTexImageSource(src))
|
|
setTextureFromElement(gl, tex, src, options);
|
|
width = src.width;
|
|
height = src.height;
|
|
}
|
|
} else {
|
|
setEmptyTexture(gl, tex, options);
|
|
}
|
|
|
|
if (shouldAutomaticallySetTextureFilteringForSize(options)) {
|
|
setTextureFilteringForSize(gl, tex, options, width, height, internalFormat);
|
|
}
|
|
|
|
setTextureParameters(gl, tex, options);
|
|
return tex;
|
|
}
|
|
/**
|
|
* Resizes a texture based on the options passed in.
|
|
*
|
|
* Note: This is not a generic resize anything function.
|
|
* It's mostly used by {@link module:twgl.resizeFramebufferInfo}
|
|
* It will use `options.src` if it exists to try to determine a `type`
|
|
* otherwise it will assume `gl.UNSIGNED_BYTE`. No data is provided
|
|
* for the texture. Texture parameters will be set accordingly
|
|
*
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @param {WebGLTexture} tex the texture to resize
|
|
* @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set.
|
|
* @param {number} [width] the new width. If not passed in will use `options.width`
|
|
* @param {number} [height] the new height. If not passed in will use `options.height`
|
|
* @param {number} [depth] the new depth. If not passed in will use `options.depth`
|
|
* @memberOf module:twgl/textures
|
|
*/
|
|
|
|
|
|
function resizeTexture(gl, tex, options, width, height, depth) {
|
|
width = width || options.width;
|
|
height = height || options.height;
|
|
depth = depth || options.depth;
|
|
const target = options.target || TEXTURE_2D;
|
|
gl.bindTexture(target, tex);
|
|
const level = options.level || 0;
|
|
const internalFormat = options.internalFormat || options.format || RGBA;
|
|
const formatType = getFormatAndTypeForInternalFormat(internalFormat);
|
|
const format = options.format || formatType.format;
|
|
let type;
|
|
const src = options.src;
|
|
|
|
if (!src) {
|
|
type = options.type || formatType.type;
|
|
} else if (isArrayBuffer$1(src) || Array.isArray(src) && typeof src[0] === 'number') {
|
|
type = options.type || getTextureTypeForArrayType(gl, src, formatType.type);
|
|
} else {
|
|
type = options.type || formatType.type;
|
|
}
|
|
|
|
if (target === TEXTURE_CUBE_MAP) {
|
|
for (let ii = 0; ii < 6; ++ii) {
|
|
gl.texImage2D(TEXTURE_CUBE_MAP_POSITIVE_X + ii, level, internalFormat, width, height, 0, format, type, null);
|
|
}
|
|
} else if (target === TEXTURE_3D || target === TEXTURE_2D_ARRAY) {
|
|
gl.texImage3D(target, level, internalFormat, width, height, depth, 0, format, type, null);
|
|
} else {
|
|
gl.texImage2D(target, level, internalFormat, width, height, 0, format, type, null);
|
|
}
|
|
}
|
|
/**
|
|
* Check if a src is an async request.
|
|
* if src is a string we're going to download an image
|
|
* if src is an array of strings we're going to download cubemap images
|
|
* @param {*} src The src from a TextureOptions
|
|
* @returns {bool} true if src is async.
|
|
* @private
|
|
*/
|
|
|
|
|
|
function isAsyncSrc(src) {
|
|
return typeof src === 'string' || Array.isArray(src) && typeof src[0] === 'string';
|
|
}
|
|
/**
|
|
* Creates a bunch of textures based on the passed in options.
|
|
*
|
|
* Example:
|
|
*
|
|
* const textures = twgl.createTextures(gl, {
|
|
* // a power of 2 image
|
|
* hftIcon: { src: "images/hft-icon-16.png", mag: gl.NEAREST },
|
|
* // a non-power of 2 image
|
|
* clover: { src: "images/clover.jpg" },
|
|
* // From a canvas
|
|
* fromCanvas: { src: ctx.canvas },
|
|
* // A cubemap from 6 images
|
|
* yokohama: {
|
|
* target: gl.TEXTURE_CUBE_MAP,
|
|
* src: [
|
|
* 'images/yokohama/posx.jpg',
|
|
* 'images/yokohama/negx.jpg',
|
|
* 'images/yokohama/posy.jpg',
|
|
* 'images/yokohama/negy.jpg',
|
|
* 'images/yokohama/posz.jpg',
|
|
* 'images/yokohama/negz.jpg',
|
|
* ],
|
|
* },
|
|
* // A cubemap from 1 image (can be 1x6, 2x3, 3x2, 6x1)
|
|
* goldengate: {
|
|
* target: gl.TEXTURE_CUBE_MAP,
|
|
* src: 'images/goldengate.jpg',
|
|
* },
|
|
* // A 2x2 pixel texture from a JavaScript array
|
|
* checker: {
|
|
* mag: gl.NEAREST,
|
|
* min: gl.LINEAR,
|
|
* src: [
|
|
* 255,255,255,255,
|
|
* 192,192,192,255,
|
|
* 192,192,192,255,
|
|
* 255,255,255,255,
|
|
* ],
|
|
* },
|
|
* // a 1x2 pixel texture from a typed array.
|
|
* stripe: {
|
|
* mag: gl.NEAREST,
|
|
* min: gl.LINEAR,
|
|
* format: gl.LUMINANCE,
|
|
* src: new Uint8Array([
|
|
* 255,
|
|
* 128,
|
|
* 255,
|
|
* 128,
|
|
* 255,
|
|
* 128,
|
|
* 255,
|
|
* 128,
|
|
* ]),
|
|
* width: 1,
|
|
* },
|
|
* });
|
|
*
|
|
* Now
|
|
*
|
|
* * `textures.hftIcon` will be a 2d texture
|
|
* * `textures.clover` will be a 2d texture
|
|
* * `textures.fromCanvas` will be a 2d texture
|
|
* * `textures.yohohama` will be a cubemap texture
|
|
* * `textures.goldengate` will be a cubemap texture
|
|
* * `textures.checker` will be a 2d texture
|
|
* * `textures.stripe` will be a 2d texture
|
|
*
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @param {Object.<string,module:twgl.TextureOptions>} options A object of TextureOptions one per texture.
|
|
* @param {module:twgl.TexturesReadyCallback} [callback] A callback called when all textures have been downloaded.
|
|
* @return {Object.<string,WebGLTexture>} the created textures by name
|
|
* @memberOf module:twgl/textures
|
|
*/
|
|
|
|
|
|
function createTextures(gl, textureOptions, callback) {
|
|
callback = callback || noop;
|
|
let numDownloading = 0;
|
|
const errors = [];
|
|
const textures = {};
|
|
const images = {};
|
|
|
|
function callCallbackIfReady() {
|
|
if (numDownloading === 0) {
|
|
setTimeout(function () {
|
|
callback(errors.length ? errors : undefined, textures, images);
|
|
}, 0);
|
|
}
|
|
}
|
|
|
|
Object.keys(textureOptions).forEach(function (name) {
|
|
const options = textureOptions[name];
|
|
let onLoadFn;
|
|
|
|
if (isAsyncSrc(options.src)) {
|
|
onLoadFn = function (err, tex, img) {
|
|
images[name] = img;
|
|
--numDownloading;
|
|
|
|
if (err) {
|
|
errors.push(err);
|
|
}
|
|
|
|
callCallbackIfReady();
|
|
};
|
|
|
|
++numDownloading;
|
|
}
|
|
|
|
textures[name] = createTexture(gl, options, onLoadFn);
|
|
}); // queue the callback if there are no images to download.
|
|
// We do this because if your code is structured to wait for
|
|
// images to download but then you comment out all the async
|
|
// images your code would break.
|
|
|
|
callCallbackIfReady();
|
|
return textures;
|
|
}
|
|
|
|
var textures = /*#__PURE__*/Object.freeze({
|
|
__proto__: null,
|
|
setTextureDefaults_: setDefaults$1,
|
|
createSampler: createSampler,
|
|
createSamplers: createSamplers,
|
|
setSamplerParameters: setSamplerParameters,
|
|
createTexture: createTexture,
|
|
setEmptyTexture: setEmptyTexture,
|
|
setTextureFromArray: setTextureFromArray,
|
|
loadTextureFromUrl: loadTextureFromUrl,
|
|
setTextureFromElement: setTextureFromElement,
|
|
setTextureFilteringForSize: setTextureFilteringForSize,
|
|
setTextureParameters: setTextureParameters,
|
|
setDefaultTextureColor: setDefaultTextureColor,
|
|
createTextures: createTextures,
|
|
resizeTexture: resizeTexture,
|
|
canGenerateMipmap: canGenerateMipmap,
|
|
canFilter: canFilter,
|
|
getNumComponentsForFormat: getNumComponentsForFormat,
|
|
getBytesPerElementForInternalFormat: getBytesPerElementForInternalFormat,
|
|
getFormatAndTypeForInternalFormat: getFormatAndTypeForInternalFormat
|
|
});
|
|
/*
|
|
* Copyright 2019 Gregg Tavares
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
/**
|
|
* Low level shader program related functions
|
|
*
|
|
* You should generally not need to use these functions. They are provided
|
|
* for those cases where you're doing something out of the ordinary
|
|
* and you need lower level access.
|
|
*
|
|
* For backward compatibility they are available at both `twgl.programs` and `twgl`
|
|
* itself
|
|
*
|
|
* See {@link module:twgl} for core functions
|
|
*
|
|
* @module twgl/programs
|
|
*/
|
|
|
|
exports.textures = textures;
|
|
const error$1 = error;
|
|
const warn$1 = warn;
|
|
|
|
function getElementById(id) {
|
|
return typeof document !== 'undefined' && document.getElementById ? document.getElementById(id) : null;
|
|
}
|
|
|
|
const TEXTURE0 = 0x84c0;
|
|
const DYNAMIC_DRAW = 0x88e8;
|
|
const ARRAY_BUFFER$1 = 0x8892;
|
|
const ELEMENT_ARRAY_BUFFER$1 = 0x8893;
|
|
const UNIFORM_BUFFER = 0x8a11;
|
|
const TRANSFORM_FEEDBACK_BUFFER = 0x8c8e;
|
|
const TRANSFORM_FEEDBACK = 0x8e22;
|
|
const COMPILE_STATUS = 0x8b81;
|
|
const LINK_STATUS = 0x8b82;
|
|
const FRAGMENT_SHADER = 0x8b30;
|
|
const VERTEX_SHADER = 0x8b31;
|
|
const SEPARATE_ATTRIBS = 0x8c8d;
|
|
const ACTIVE_UNIFORMS = 0x8b86;
|
|
const ACTIVE_ATTRIBUTES = 0x8b89;
|
|
const TRANSFORM_FEEDBACK_VARYINGS = 0x8c83;
|
|
const ACTIVE_UNIFORM_BLOCKS = 0x8a36;
|
|
const UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER = 0x8a44;
|
|
const UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER = 0x8a46;
|
|
const UNIFORM_BLOCK_DATA_SIZE = 0x8a40;
|
|
const UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES = 0x8a43;
|
|
const FLOAT$3 = 0x1406;
|
|
const FLOAT_VEC2 = 0x8B50;
|
|
const FLOAT_VEC3 = 0x8B51;
|
|
const FLOAT_VEC4 = 0x8B52;
|
|
const INT$3 = 0x1404;
|
|
const INT_VEC2 = 0x8B53;
|
|
const INT_VEC3 = 0x8B54;
|
|
const INT_VEC4 = 0x8B55;
|
|
const BOOL = 0x8B56;
|
|
const BOOL_VEC2 = 0x8B57;
|
|
const BOOL_VEC3 = 0x8B58;
|
|
const BOOL_VEC4 = 0x8B59;
|
|
const FLOAT_MAT2 = 0x8B5A;
|
|
const FLOAT_MAT3 = 0x8B5B;
|
|
const FLOAT_MAT4 = 0x8B5C;
|
|
const SAMPLER_2D = 0x8B5E;
|
|
const SAMPLER_CUBE = 0x8B60;
|
|
const SAMPLER_3D = 0x8B5F;
|
|
const SAMPLER_2D_SHADOW = 0x8B62;
|
|
const FLOAT_MAT2x3 = 0x8B65;
|
|
const FLOAT_MAT2x4 = 0x8B66;
|
|
const FLOAT_MAT3x2 = 0x8B67;
|
|
const FLOAT_MAT3x4 = 0x8B68;
|
|
const FLOAT_MAT4x2 = 0x8B69;
|
|
const FLOAT_MAT4x3 = 0x8B6A;
|
|
const SAMPLER_2D_ARRAY = 0x8DC1;
|
|
const SAMPLER_2D_ARRAY_SHADOW = 0x8DC4;
|
|
const SAMPLER_CUBE_SHADOW = 0x8DC5;
|
|
const UNSIGNED_INT$3 = 0x1405;
|
|
const UNSIGNED_INT_VEC2 = 0x8DC6;
|
|
const UNSIGNED_INT_VEC3 = 0x8DC7;
|
|
const UNSIGNED_INT_VEC4 = 0x8DC8;
|
|
const INT_SAMPLER_2D = 0x8DCA;
|
|
const INT_SAMPLER_3D = 0x8DCB;
|
|
const INT_SAMPLER_CUBE = 0x8DCC;
|
|
const INT_SAMPLER_2D_ARRAY = 0x8DCF;
|
|
const UNSIGNED_INT_SAMPLER_2D = 0x8DD2;
|
|
const UNSIGNED_INT_SAMPLER_3D = 0x8DD3;
|
|
const UNSIGNED_INT_SAMPLER_CUBE = 0x8DD4;
|
|
const UNSIGNED_INT_SAMPLER_2D_ARRAY = 0x8DD7;
|
|
const TEXTURE_2D$1 = 0x0DE1;
|
|
const TEXTURE_CUBE_MAP$1 = 0x8513;
|
|
const TEXTURE_3D$1 = 0x806F;
|
|
const TEXTURE_2D_ARRAY$1 = 0x8C1A;
|
|
const typeMap = {};
|
|
/**
|
|
* Returns the corresponding bind point for a given sampler type
|
|
*/
|
|
|
|
function getBindPointForSamplerType(gl, type) {
|
|
return typeMap[type].bindPoint;
|
|
} // This kind of sucks! If you could compose functions as in `var fn = gl[name];`
|
|
// this code could be a lot smaller but that is sadly really slow (T_T)
|
|
|
|
|
|
function floatSetter(gl, location) {
|
|
return function (v) {
|
|
gl.uniform1f(location, v);
|
|
};
|
|
}
|
|
|
|
function floatArraySetter(gl, location) {
|
|
return function (v) {
|
|
gl.uniform1fv(location, v);
|
|
};
|
|
}
|
|
|
|
function floatVec2Setter(gl, location) {
|
|
return function (v) {
|
|
gl.uniform2fv(location, v);
|
|
};
|
|
}
|
|
|
|
function floatVec3Setter(gl, location) {
|
|
return function (v) {
|
|
gl.uniform3fv(location, v);
|
|
};
|
|
}
|
|
|
|
function floatVec4Setter(gl, location) {
|
|
return function (v) {
|
|
gl.uniform4fv(location, v);
|
|
};
|
|
}
|
|
|
|
function intSetter(gl, location) {
|
|
return function (v) {
|
|
gl.uniform1i(location, v);
|
|
};
|
|
}
|
|
|
|
function intArraySetter(gl, location) {
|
|
return function (v) {
|
|
gl.uniform1iv(location, v);
|
|
};
|
|
}
|
|
|
|
function intVec2Setter(gl, location) {
|
|
return function (v) {
|
|
gl.uniform2iv(location, v);
|
|
};
|
|
}
|
|
|
|
function intVec3Setter(gl, location) {
|
|
return function (v) {
|
|
gl.uniform3iv(location, v);
|
|
};
|
|
}
|
|
|
|
function intVec4Setter(gl, location) {
|
|
return function (v) {
|
|
gl.uniform4iv(location, v);
|
|
};
|
|
}
|
|
|
|
function uintSetter(gl, location) {
|
|
return function (v) {
|
|
gl.uniform1ui(location, v);
|
|
};
|
|
}
|
|
|
|
function uintArraySetter(gl, location) {
|
|
return function (v) {
|
|
gl.uniform1uiv(location, v);
|
|
};
|
|
}
|
|
|
|
function uintVec2Setter(gl, location) {
|
|
return function (v) {
|
|
gl.uniform2uiv(location, v);
|
|
};
|
|
}
|
|
|
|
function uintVec3Setter(gl, location) {
|
|
return function (v) {
|
|
gl.uniform3uiv(location, v);
|
|
};
|
|
}
|
|
|
|
function uintVec4Setter(gl, location) {
|
|
return function (v) {
|
|
gl.uniform4uiv(location, v);
|
|
};
|
|
}
|
|
|
|
function floatMat2Setter(gl, location) {
|
|
return function (v) {
|
|
gl.uniformMatrix2fv(location, false, v);
|
|
};
|
|
}
|
|
|
|
function floatMat3Setter(gl, location) {
|
|
return function (v) {
|
|
gl.uniformMatrix3fv(location, false, v);
|
|
};
|
|
}
|
|
|
|
function floatMat4Setter(gl, location) {
|
|
return function (v) {
|
|
gl.uniformMatrix4fv(location, false, v);
|
|
};
|
|
}
|
|
|
|
function floatMat23Setter(gl, location) {
|
|
return function (v) {
|
|
gl.uniformMatrix2x3fv(location, false, v);
|
|
};
|
|
}
|
|
|
|
function floatMat32Setter(gl, location) {
|
|
return function (v) {
|
|
gl.uniformMatrix3x2fv(location, false, v);
|
|
};
|
|
}
|
|
|
|
function floatMat24Setter(gl, location) {
|
|
return function (v) {
|
|
gl.uniformMatrix2x4fv(location, false, v);
|
|
};
|
|
}
|
|
|
|
function floatMat42Setter(gl, location) {
|
|
return function (v) {
|
|
gl.uniformMatrix4x2fv(location, false, v);
|
|
};
|
|
}
|
|
|
|
function floatMat34Setter(gl, location) {
|
|
return function (v) {
|
|
gl.uniformMatrix3x4fv(location, false, v);
|
|
};
|
|
}
|
|
|
|
function floatMat43Setter(gl, location) {
|
|
return function (v) {
|
|
gl.uniformMatrix4x3fv(location, false, v);
|
|
};
|
|
}
|
|
|
|
function samplerSetter(gl, type, unit, location) {
|
|
const bindPoint = getBindPointForSamplerType(gl, type);
|
|
return isWebGL2(gl) ? function (textureOrPair) {
|
|
let texture;
|
|
let sampler;
|
|
|
|
if (isTexture(gl, textureOrPair)) {
|
|
texture = textureOrPair;
|
|
sampler = null;
|
|
} else {
|
|
texture = textureOrPair.texture;
|
|
sampler = textureOrPair.sampler;
|
|
}
|
|
|
|
gl.uniform1i(location, unit);
|
|
gl.activeTexture(TEXTURE0 + unit);
|
|
gl.bindTexture(bindPoint, texture);
|
|
gl.bindSampler(unit, sampler);
|
|
} : function (texture) {
|
|
gl.uniform1i(location, unit);
|
|
gl.activeTexture(TEXTURE0 + unit);
|
|
gl.bindTexture(bindPoint, texture);
|
|
};
|
|
}
|
|
|
|
function samplerArraySetter(gl, type, unit, location, size) {
|
|
const bindPoint = getBindPointForSamplerType(gl, type);
|
|
const units = new Int32Array(size);
|
|
|
|
for (let ii = 0; ii < size; ++ii) {
|
|
units[ii] = unit + ii;
|
|
}
|
|
|
|
return isWebGL2(gl) ? function (textures) {
|
|
gl.uniform1iv(location, units);
|
|
textures.forEach(function (textureOrPair, index) {
|
|
gl.activeTexture(TEXTURE0 + units[index]);
|
|
let texture;
|
|
let sampler;
|
|
|
|
if (isTexture(gl, textureOrPair)) {
|
|
texture = textureOrPair;
|
|
sampler = null;
|
|
} else {
|
|
texture = textureOrPair.texture;
|
|
sampler = textureOrPair.sampler;
|
|
}
|
|
|
|
gl.bindSampler(unit, sampler);
|
|
gl.bindTexture(bindPoint, texture);
|
|
});
|
|
} : function (textures) {
|
|
gl.uniform1iv(location, units);
|
|
textures.forEach(function (texture, index) {
|
|
gl.activeTexture(TEXTURE0 + units[index]);
|
|
gl.bindTexture(bindPoint, texture);
|
|
});
|
|
};
|
|
}
|
|
|
|
typeMap[FLOAT$3] = {
|
|
Type: Float32Array,
|
|
size: 4,
|
|
setter: floatSetter,
|
|
arraySetter: floatArraySetter
|
|
};
|
|
typeMap[FLOAT_VEC2] = {
|
|
Type: Float32Array,
|
|
size: 8,
|
|
setter: floatVec2Setter
|
|
};
|
|
typeMap[FLOAT_VEC3] = {
|
|
Type: Float32Array,
|
|
size: 12,
|
|
setter: floatVec3Setter
|
|
};
|
|
typeMap[FLOAT_VEC4] = {
|
|
Type: Float32Array,
|
|
size: 16,
|
|
setter: floatVec4Setter
|
|
};
|
|
typeMap[INT$3] = {
|
|
Type: Int32Array,
|
|
size: 4,
|
|
setter: intSetter,
|
|
arraySetter: intArraySetter
|
|
};
|
|
typeMap[INT_VEC2] = {
|
|
Type: Int32Array,
|
|
size: 8,
|
|
setter: intVec2Setter
|
|
};
|
|
typeMap[INT_VEC3] = {
|
|
Type: Int32Array,
|
|
size: 12,
|
|
setter: intVec3Setter
|
|
};
|
|
typeMap[INT_VEC4] = {
|
|
Type: Int32Array,
|
|
size: 16,
|
|
setter: intVec4Setter
|
|
};
|
|
typeMap[UNSIGNED_INT$3] = {
|
|
Type: Uint32Array,
|
|
size: 4,
|
|
setter: uintSetter,
|
|
arraySetter: uintArraySetter
|
|
};
|
|
typeMap[UNSIGNED_INT_VEC2] = {
|
|
Type: Uint32Array,
|
|
size: 8,
|
|
setter: uintVec2Setter
|
|
};
|
|
typeMap[UNSIGNED_INT_VEC3] = {
|
|
Type: Uint32Array,
|
|
size: 12,
|
|
setter: uintVec3Setter
|
|
};
|
|
typeMap[UNSIGNED_INT_VEC4] = {
|
|
Type: Uint32Array,
|
|
size: 16,
|
|
setter: uintVec4Setter
|
|
};
|
|
typeMap[BOOL] = {
|
|
Type: Uint32Array,
|
|
size: 4,
|
|
setter: intSetter,
|
|
arraySetter: intArraySetter
|
|
};
|
|
typeMap[BOOL_VEC2] = {
|
|
Type: Uint32Array,
|
|
size: 8,
|
|
setter: intVec2Setter
|
|
};
|
|
typeMap[BOOL_VEC3] = {
|
|
Type: Uint32Array,
|
|
size: 12,
|
|
setter: intVec3Setter
|
|
};
|
|
typeMap[BOOL_VEC4] = {
|
|
Type: Uint32Array,
|
|
size: 16,
|
|
setter: intVec4Setter
|
|
};
|
|
typeMap[FLOAT_MAT2] = {
|
|
Type: Float32Array,
|
|
size: 16,
|
|
setter: floatMat2Setter
|
|
};
|
|
typeMap[FLOAT_MAT3] = {
|
|
Type: Float32Array,
|
|
size: 36,
|
|
setter: floatMat3Setter
|
|
};
|
|
typeMap[FLOAT_MAT4] = {
|
|
Type: Float32Array,
|
|
size: 64,
|
|
setter: floatMat4Setter
|
|
};
|
|
typeMap[FLOAT_MAT2x3] = {
|
|
Type: Float32Array,
|
|
size: 24,
|
|
setter: floatMat23Setter
|
|
};
|
|
typeMap[FLOAT_MAT2x4] = {
|
|
Type: Float32Array,
|
|
size: 32,
|
|
setter: floatMat24Setter
|
|
};
|
|
typeMap[FLOAT_MAT3x2] = {
|
|
Type: Float32Array,
|
|
size: 24,
|
|
setter: floatMat32Setter
|
|
};
|
|
typeMap[FLOAT_MAT3x4] = {
|
|
Type: Float32Array,
|
|
size: 48,
|
|
setter: floatMat34Setter
|
|
};
|
|
typeMap[FLOAT_MAT4x2] = {
|
|
Type: Float32Array,
|
|
size: 32,
|
|
setter: floatMat42Setter
|
|
};
|
|
typeMap[FLOAT_MAT4x3] = {
|
|
Type: Float32Array,
|
|
size: 48,
|
|
setter: floatMat43Setter
|
|
};
|
|
typeMap[SAMPLER_2D] = {
|
|
Type: null,
|
|
size: 0,
|
|
setter: samplerSetter,
|
|
arraySetter: samplerArraySetter,
|
|
bindPoint: TEXTURE_2D$1
|
|
};
|
|
typeMap[SAMPLER_CUBE] = {
|
|
Type: null,
|
|
size: 0,
|
|
setter: samplerSetter,
|
|
arraySetter: samplerArraySetter,
|
|
bindPoint: TEXTURE_CUBE_MAP$1
|
|
};
|
|
typeMap[SAMPLER_3D] = {
|
|
Type: null,
|
|
size: 0,
|
|
setter: samplerSetter,
|
|
arraySetter: samplerArraySetter,
|
|
bindPoint: TEXTURE_3D$1
|
|
};
|
|
typeMap[SAMPLER_2D_SHADOW] = {
|
|
Type: null,
|
|
size: 0,
|
|
setter: samplerSetter,
|
|
arraySetter: samplerArraySetter,
|
|
bindPoint: TEXTURE_2D$1
|
|
};
|
|
typeMap[SAMPLER_2D_ARRAY] = {
|
|
Type: null,
|
|
size: 0,
|
|
setter: samplerSetter,
|
|
arraySetter: samplerArraySetter,
|
|
bindPoint: TEXTURE_2D_ARRAY$1
|
|
};
|
|
typeMap[SAMPLER_2D_ARRAY_SHADOW] = {
|
|
Type: null,
|
|
size: 0,
|
|
setter: samplerSetter,
|
|
arraySetter: samplerArraySetter,
|
|
bindPoint: TEXTURE_2D_ARRAY$1
|
|
};
|
|
typeMap[SAMPLER_CUBE_SHADOW] = {
|
|
Type: null,
|
|
size: 0,
|
|
setter: samplerSetter,
|
|
arraySetter: samplerArraySetter,
|
|
bindPoint: TEXTURE_CUBE_MAP$1
|
|
};
|
|
typeMap[INT_SAMPLER_2D] = {
|
|
Type: null,
|
|
size: 0,
|
|
setter: samplerSetter,
|
|
arraySetter: samplerArraySetter,
|
|
bindPoint: TEXTURE_2D$1
|
|
};
|
|
typeMap[INT_SAMPLER_3D] = {
|
|
Type: null,
|
|
size: 0,
|
|
setter: samplerSetter,
|
|
arraySetter: samplerArraySetter,
|
|
bindPoint: TEXTURE_3D$1
|
|
};
|
|
typeMap[INT_SAMPLER_CUBE] = {
|
|
Type: null,
|
|
size: 0,
|
|
setter: samplerSetter,
|
|
arraySetter: samplerArraySetter,
|
|
bindPoint: TEXTURE_CUBE_MAP$1
|
|
};
|
|
typeMap[INT_SAMPLER_2D_ARRAY] = {
|
|
Type: null,
|
|
size: 0,
|
|
setter: samplerSetter,
|
|
arraySetter: samplerArraySetter,
|
|
bindPoint: TEXTURE_2D_ARRAY$1
|
|
};
|
|
typeMap[UNSIGNED_INT_SAMPLER_2D] = {
|
|
Type: null,
|
|
size: 0,
|
|
setter: samplerSetter,
|
|
arraySetter: samplerArraySetter,
|
|
bindPoint: TEXTURE_2D$1
|
|
};
|
|
typeMap[UNSIGNED_INT_SAMPLER_3D] = {
|
|
Type: null,
|
|
size: 0,
|
|
setter: samplerSetter,
|
|
arraySetter: samplerArraySetter,
|
|
bindPoint: TEXTURE_3D$1
|
|
};
|
|
typeMap[UNSIGNED_INT_SAMPLER_CUBE] = {
|
|
Type: null,
|
|
size: 0,
|
|
setter: samplerSetter,
|
|
arraySetter: samplerArraySetter,
|
|
bindPoint: TEXTURE_CUBE_MAP$1
|
|
};
|
|
typeMap[UNSIGNED_INT_SAMPLER_2D_ARRAY] = {
|
|
Type: null,
|
|
size: 0,
|
|
setter: samplerSetter,
|
|
arraySetter: samplerArraySetter,
|
|
bindPoint: TEXTURE_2D_ARRAY$1
|
|
};
|
|
|
|
function floatAttribSetter(gl, index) {
|
|
return function (b) {
|
|
if (b.value) {
|
|
gl.disableVertexAttribArray(index);
|
|
|
|
switch (b.value.length) {
|
|
case 4:
|
|
gl.vertexAttrib4fv(index, b.value);
|
|
break;
|
|
|
|
case 3:
|
|
gl.vertexAttrib3fv(index, b.value);
|
|
break;
|
|
|
|
case 2:
|
|
gl.vertexAttrib2fv(index, b.value);
|
|
break;
|
|
|
|
case 1:
|
|
gl.vertexAttrib1fv(index, b.value);
|
|
break;
|
|
|
|
default:
|
|
throw new Error('the length of a float constant value must be between 1 and 4!');
|
|
}
|
|
} else {
|
|
gl.bindBuffer(ARRAY_BUFFER$1, b.buffer);
|
|
gl.enableVertexAttribArray(index);
|
|
gl.vertexAttribPointer(index, b.numComponents || b.size, b.type || FLOAT$3, b.normalize || false, b.stride || 0, b.offset || 0);
|
|
|
|
if (b.divisor !== undefined) {
|
|
gl.vertexAttribDivisor(index, b.divisor);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function intAttribSetter(gl, index) {
|
|
return function (b) {
|
|
if (b.value) {
|
|
gl.disableVertexAttribArray(index);
|
|
|
|
if (b.value.length === 4) {
|
|
gl.vertexAttrib4iv(index, b.value);
|
|
} else {
|
|
throw new Error('The length of an integer constant value must be 4!');
|
|
}
|
|
} else {
|
|
gl.bindBuffer(ARRAY_BUFFER$1, b.buffer);
|
|
gl.enableVertexAttribArray(index);
|
|
gl.vertexAttribIPointer(index, b.numComponents || b.size, b.type || INT$3, b.stride || 0, b.offset || 0);
|
|
|
|
if (b.divisor !== undefined) {
|
|
gl.vertexAttribDivisor(index, b.divisor);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function uintAttribSetter(gl, index) {
|
|
return function (b) {
|
|
if (b.value) {
|
|
gl.disableVertexAttribArray(index);
|
|
|
|
if (b.value.length === 4) {
|
|
gl.vertexAttrib4uiv(index, b.value);
|
|
} else {
|
|
throw new Error('The length of an unsigned integer constant value must be 4!');
|
|
}
|
|
} else {
|
|
gl.bindBuffer(ARRAY_BUFFER$1, b.buffer);
|
|
gl.enableVertexAttribArray(index);
|
|
gl.vertexAttribIPointer(index, b.numComponents || b.size, b.type || UNSIGNED_INT$3, b.stride || 0, b.offset || 0);
|
|
|
|
if (b.divisor !== undefined) {
|
|
gl.vertexAttribDivisor(index, b.divisor);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
function matAttribSetter(gl, index, typeInfo) {
|
|
const defaultSize = typeInfo.size;
|
|
const count = typeInfo.count;
|
|
return function (b) {
|
|
gl.bindBuffer(ARRAY_BUFFER$1, b.buffer);
|
|
const numComponents = b.size || b.numComponents || defaultSize;
|
|
const size = numComponents / count;
|
|
const type = b.type || FLOAT$3;
|
|
const typeInfo = typeMap[type];
|
|
const stride = typeInfo.size * numComponents;
|
|
const normalize = b.normalize || false;
|
|
const offset = b.offset || 0;
|
|
const rowOffset = stride / count;
|
|
|
|
for (let i = 0; i < count; ++i) {
|
|
gl.enableVertexAttribArray(index + i);
|
|
gl.vertexAttribPointer(index + i, size, type, normalize, stride, offset + rowOffset * i);
|
|
|
|
if (b.divisor !== undefined) {
|
|
gl.vertexAttribDivisor(index + i, b.divisor);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
const attrTypeMap = {};
|
|
attrTypeMap[FLOAT$3] = {
|
|
size: 4,
|
|
setter: floatAttribSetter
|
|
};
|
|
attrTypeMap[FLOAT_VEC2] = {
|
|
size: 8,
|
|
setter: floatAttribSetter
|
|
};
|
|
attrTypeMap[FLOAT_VEC3] = {
|
|
size: 12,
|
|
setter: floatAttribSetter
|
|
};
|
|
attrTypeMap[FLOAT_VEC4] = {
|
|
size: 16,
|
|
setter: floatAttribSetter
|
|
};
|
|
attrTypeMap[INT$3] = {
|
|
size: 4,
|
|
setter: intAttribSetter
|
|
};
|
|
attrTypeMap[INT_VEC2] = {
|
|
size: 8,
|
|
setter: intAttribSetter
|
|
};
|
|
attrTypeMap[INT_VEC3] = {
|
|
size: 12,
|
|
setter: intAttribSetter
|
|
};
|
|
attrTypeMap[INT_VEC4] = {
|
|
size: 16,
|
|
setter: intAttribSetter
|
|
};
|
|
attrTypeMap[UNSIGNED_INT$3] = {
|
|
size: 4,
|
|
setter: uintAttribSetter
|
|
};
|
|
attrTypeMap[UNSIGNED_INT_VEC2] = {
|
|
size: 8,
|
|
setter: uintAttribSetter
|
|
};
|
|
attrTypeMap[UNSIGNED_INT_VEC3] = {
|
|
size: 12,
|
|
setter: uintAttribSetter
|
|
};
|
|
attrTypeMap[UNSIGNED_INT_VEC4] = {
|
|
size: 16,
|
|
setter: uintAttribSetter
|
|
};
|
|
attrTypeMap[BOOL] = {
|
|
size: 4,
|
|
setter: intAttribSetter
|
|
};
|
|
attrTypeMap[BOOL_VEC2] = {
|
|
size: 8,
|
|
setter: intAttribSetter
|
|
};
|
|
attrTypeMap[BOOL_VEC3] = {
|
|
size: 12,
|
|
setter: intAttribSetter
|
|
};
|
|
attrTypeMap[BOOL_VEC4] = {
|
|
size: 16,
|
|
setter: intAttribSetter
|
|
};
|
|
attrTypeMap[FLOAT_MAT2] = {
|
|
size: 4,
|
|
setter: matAttribSetter,
|
|
count: 2
|
|
};
|
|
attrTypeMap[FLOAT_MAT3] = {
|
|
size: 9,
|
|
setter: matAttribSetter,
|
|
count: 3
|
|
};
|
|
attrTypeMap[FLOAT_MAT4] = {
|
|
size: 16,
|
|
setter: matAttribSetter,
|
|
count: 4
|
|
};
|
|
const errorRE = /ERROR:\s*\d+:(\d+)/gi;
|
|
|
|
function addLineNumbersWithError(src, log = '', lineOffset = 0) {
|
|
// Note: Error message formats are not defined by any spec so this may or may not work.
|
|
const matches = [...log.matchAll(errorRE)];
|
|
const lineNoToErrorMap = new Map(matches.map((m, ndx) => {
|
|
const lineNo = parseInt(m[1]);
|
|
const next = matches[ndx + 1];
|
|
const end = next ? next.index : log.length;
|
|
const msg = log.substring(m.index, end);
|
|
return [lineNo - 1, msg];
|
|
}));
|
|
return src.split('\n').map((line, lineNo) => {
|
|
const err = lineNoToErrorMap.get(lineNo);
|
|
return `${lineNo + 1 + lineOffset}: ${line}${err ? `\n\n^^^ ${err}` : ''}`;
|
|
}).join('\n');
|
|
}
|
|
/**
|
|
* Error Callback
|
|
* @callback ErrorCallback
|
|
* @param {string} msg error message.
|
|
* @param {number} [lineOffset] amount to add to line number
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
|
|
const spaceRE = /^[ \t]*\n/;
|
|
/**
|
|
* Loads a shader.
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
|
|
* @param {string} shaderSource The shader source.
|
|
* @param {number} shaderType The type of shader.
|
|
* @param {module:twgl.ErrorCallback} opt_errorCallback callback for errors.
|
|
* @return {WebGLShader} The created shader.
|
|
* @private
|
|
*/
|
|
|
|
function loadShader(gl, shaderSource, shaderType, opt_errorCallback) {
|
|
const errFn = opt_errorCallback || error$1; // Create the shader object
|
|
|
|
const shader = gl.createShader(shaderType); // Remove the first end of line because WebGL 2.0 requires
|
|
// #version 300 es
|
|
// as the first line. No whitespace allowed before that line
|
|
// so
|
|
//
|
|
// <script>
|
|
// #version 300 es
|
|
// </script>
|
|
//
|
|
// Has one line before it which is invalid according to GLSL ES 3.00
|
|
//
|
|
|
|
let lineOffset = 0;
|
|
|
|
if (spaceRE.test(shaderSource)) {
|
|
lineOffset = 1;
|
|
shaderSource = shaderSource.replace(spaceRE, '');
|
|
} // Load the shader source
|
|
|
|
|
|
gl.shaderSource(shader, shaderSource); // Compile the shader
|
|
|
|
gl.compileShader(shader); // Check the compile status
|
|
|
|
const compiled = gl.getShaderParameter(shader, COMPILE_STATUS);
|
|
|
|
if (!compiled) {
|
|
// Something went wrong during compilation; get the error
|
|
const lastError = gl.getShaderInfoLog(shader);
|
|
errFn(`${addLineNumbersWithError(shaderSource, lastError, lineOffset)}\nError compiling ${glEnumToString(gl, shaderType)}: ${lastError}`);
|
|
gl.deleteShader(shader);
|
|
return null;
|
|
}
|
|
|
|
return shader;
|
|
}
|
|
/**
|
|
* @typedef {Object} ProgramOptions
|
|
* @property {function(string)} [errorCallback] callback for errors
|
|
* @property {Object.<string,number>} [attribLocations] a attribute name to location map
|
|
* @property {(module:twgl.BufferInfo|Object.<string,module:twgl.AttribInfo>|string[])} [transformFeedbackVaryings] If passed
|
|
* a BufferInfo will use the attribs names inside. If passed an object of AttribInfos will use the names from that object. Otherwise
|
|
* you can pass an array of names.
|
|
* @property {number} [transformFeedbackMode] the mode to pass `gl.transformFeedbackVaryings`. Defaults to `SEPARATE_ATTRIBS`.
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* Gets the program options based on all these optional arguments
|
|
* @param {module:twgl.ProgramOptions|string[]} [opt_attribs] Options for the program or an array of attribs names. Locations will be assigned by index if not passed in
|
|
* @param {number[]} [opt_locations] The locations for the. A parallel array to opt_attribs letting you assign locations.
|
|
* @param {module:twgl.ErrorCallback} [opt_errorCallback] callback for errors. By default it just prints an error to the console
|
|
* on error. If you want something else pass an callback. It's passed an error message.
|
|
* @return {module:twgl.ProgramOptions} an instance of ProgramOptions based on the arguments passed in
|
|
* @private
|
|
*/
|
|
|
|
|
|
function getProgramOptions(opt_attribs, opt_locations, opt_errorCallback) {
|
|
let transformFeedbackVaryings;
|
|
let transformFeedbackMode;
|
|
|
|
if (typeof opt_locations === 'function') {
|
|
opt_errorCallback = opt_locations;
|
|
opt_locations = undefined;
|
|
}
|
|
|
|
if (typeof opt_attribs === 'function') {
|
|
opt_errorCallback = opt_attribs;
|
|
opt_attribs = undefined;
|
|
} else if (opt_attribs && !Array.isArray(opt_attribs)) {
|
|
// If we have an errorCallback we can just return this object
|
|
// Otherwise we need to construct one with default errorCallback
|
|
if (opt_attribs.errorCallback) {
|
|
return opt_attribs;
|
|
}
|
|
|
|
const opt = opt_attribs;
|
|
opt_errorCallback = opt.errorCallback;
|
|
opt_attribs = opt.attribLocations;
|
|
transformFeedbackVaryings = opt.transformFeedbackVaryings;
|
|
transformFeedbackMode = opt.transformFeedbackMode;
|
|
}
|
|
|
|
const options = {
|
|
errorCallback: opt_errorCallback || error$1,
|
|
transformFeedbackVaryings: transformFeedbackVaryings,
|
|
transformFeedbackMode: transformFeedbackMode
|
|
};
|
|
|
|
if (opt_attribs) {
|
|
let attribLocations = {};
|
|
|
|
if (Array.isArray(opt_attribs)) {
|
|
opt_attribs.forEach(function (attrib, ndx) {
|
|
attribLocations[attrib] = opt_locations ? opt_locations[ndx] : ndx;
|
|
});
|
|
} else {
|
|
attribLocations = opt_attribs;
|
|
}
|
|
|
|
options.attribLocations = attribLocations;
|
|
}
|
|
|
|
return options;
|
|
}
|
|
|
|
const defaultShaderType = ["VERTEX_SHADER", "FRAGMENT_SHADER"];
|
|
|
|
function getShaderTypeFromScriptType(gl, scriptType) {
|
|
if (scriptType.indexOf("frag") >= 0) {
|
|
return FRAGMENT_SHADER;
|
|
} else if (scriptType.indexOf("vert") >= 0) {
|
|
return VERTEX_SHADER;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
function deleteShaders(gl, shaders) {
|
|
shaders.forEach(function (shader) {
|
|
gl.deleteShader(shader);
|
|
});
|
|
}
|
|
/**
|
|
* Creates a program, attaches (and/or compiles) shaders, binds attrib locations, links the
|
|
* program and calls useProgram.
|
|
*
|
|
* NOTE: There are 4 signatures for this function
|
|
*
|
|
* twgl.createProgram(gl, [vs, fs], options);
|
|
* twgl.createProgram(gl, [vs, fs], opt_errFunc);
|
|
* twgl.createProgram(gl, [vs, fs], opt_attribs, opt_errFunc);
|
|
* twgl.createProgram(gl, [vs, fs], opt_attribs, opt_locations, opt_errFunc);
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
|
|
* @param {WebGLShader[]|string[]} shaders The shaders to attach, or element ids for their source, or strings that contain their source
|
|
* @param {module:twgl.ProgramOptions|string[]|module:twgl.ErrorCallback} [opt_attribs] Options for the program or an array of attribs names or an error callback. Locations will be assigned by index if not passed in
|
|
* @param {number[]} [opt_locations|module:twgl.ErrorCallback] The locations for the. A parallel array to opt_attribs letting you assign locations or an error callback.
|
|
* @param {module:twgl.ErrorCallback} [opt_errorCallback] callback for errors. By default it just prints an error to the console
|
|
* on error. If you want something else pass an callback. It's passed an error message.
|
|
* @return {WebGLProgram?} the created program or null if error.
|
|
* @memberOf module:twgl/programs
|
|
*/
|
|
|
|
|
|
function createProgram(gl, shaders, opt_attribs, opt_locations, opt_errorCallback) {
|
|
const progOptions = getProgramOptions(opt_attribs, opt_locations, opt_errorCallback);
|
|
const realShaders = [];
|
|
const newShaders = [];
|
|
|
|
for (let ndx = 0; ndx < shaders.length; ++ndx) {
|
|
let shader = shaders[ndx];
|
|
|
|
if (typeof shader === 'string') {
|
|
const elem = getElementById(shader);
|
|
const src = elem ? elem.text : shader;
|
|
let type = gl[defaultShaderType[ndx]];
|
|
|
|
if (elem && elem.type) {
|
|
type = getShaderTypeFromScriptType(gl, elem.type) || type;
|
|
}
|
|
|
|
shader = loadShader(gl, src, type, progOptions.errorCallback);
|
|
newShaders.push(shader);
|
|
}
|
|
|
|
if (isShader(gl, shader)) {
|
|
realShaders.push(shader);
|
|
}
|
|
}
|
|
|
|
if (realShaders.length !== shaders.length) {
|
|
progOptions.errorCallback("not enough shaders for program");
|
|
deleteShaders(gl, newShaders);
|
|
return null;
|
|
}
|
|
|
|
const program = gl.createProgram();
|
|
realShaders.forEach(function (shader) {
|
|
gl.attachShader(program, shader);
|
|
});
|
|
|
|
if (progOptions.attribLocations) {
|
|
Object.keys(progOptions.attribLocations).forEach(function (attrib) {
|
|
gl.bindAttribLocation(program, progOptions.attribLocations[attrib], attrib);
|
|
});
|
|
}
|
|
|
|
let varyings = progOptions.transformFeedbackVaryings;
|
|
|
|
if (varyings) {
|
|
if (varyings.attribs) {
|
|
varyings = varyings.attribs;
|
|
}
|
|
|
|
if (!Array.isArray(varyings)) {
|
|
varyings = Object.keys(varyings);
|
|
}
|
|
|
|
gl.transformFeedbackVaryings(program, varyings, progOptions.transformFeedbackMode || SEPARATE_ATTRIBS);
|
|
}
|
|
|
|
gl.linkProgram(program); // Check the link status
|
|
|
|
const linked = gl.getProgramParameter(program, LINK_STATUS);
|
|
|
|
if (!linked) {
|
|
// something went wrong with the link
|
|
const lastError = gl.getProgramInfoLog(program);
|
|
progOptions.errorCallback(`${realShaders.map(shader => {
|
|
const src = addLineNumbersWithError(gl.getShaderSource(shader), '', 0);
|
|
const type = gl.getShaderParameter(shader, gl.SHADER_TYPE);
|
|
return `${glEnumToString(gl, type)}\n${src}}`;
|
|
}).join('\n')}\nError in program linking: ${lastError}`);
|
|
gl.deleteProgram(program);
|
|
deleteShaders(gl, newShaders);
|
|
return null;
|
|
}
|
|
|
|
return program;
|
|
}
|
|
/**
|
|
* Loads a shader from a script tag.
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
|
|
* @param {string} scriptId The id of the script tag.
|
|
* @param {number} [opt_shaderType] The type of shader. If not passed in it will
|
|
* be derived from the type of the script tag.
|
|
* @param {module:twgl.ErrorCallback} [opt_errorCallback] callback for errors.
|
|
* @return {WebGLShader?} The created shader or null if error.
|
|
* @private
|
|
*/
|
|
|
|
|
|
function createShaderFromScript(gl, scriptId, opt_shaderType, opt_errorCallback) {
|
|
let shaderSource = "";
|
|
const shaderScript = getElementById(scriptId);
|
|
|
|
if (!shaderScript) {
|
|
throw new Error(`unknown script element: ${scriptId}`);
|
|
}
|
|
|
|
shaderSource = shaderScript.text;
|
|
const shaderType = opt_shaderType || getShaderTypeFromScriptType(gl, shaderScript.type);
|
|
|
|
if (!shaderType) {
|
|
throw new Error('unknown shader type');
|
|
}
|
|
|
|
return loadShader(gl, shaderSource, shaderType, opt_errorCallback);
|
|
}
|
|
/**
|
|
* Creates a program from 2 script tags.
|
|
*
|
|
* NOTE: There are 4 signatures for this function
|
|
*
|
|
* twgl.createProgramFromScripts(gl, [vs, fs], opt_options);
|
|
* twgl.createProgramFromScripts(gl, [vs, fs], opt_errFunc);
|
|
* twgl.createProgramFromScripts(gl, [vs, fs], opt_attribs, opt_errFunc);
|
|
* twgl.createProgramFromScripts(gl, [vs, fs], opt_attribs, opt_locations, opt_errFunc);
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext
|
|
* to use.
|
|
* @param {string[]} shaderScriptIds Array of ids of the script
|
|
* tags for the shaders. The first is assumed to be the
|
|
* vertex shader, the second the fragment shader.
|
|
* @param {module:twgl.ProgramOptions|string[]|module:twgl.ErrorCallback} [opt_attribs] Options for the program or an array of attribs names or an error callback. Locations will be assigned by index if not passed in
|
|
* @param {number[]} [opt_locations|module:twgl.ErrorCallback] The locations for the. A parallel array to opt_attribs letting you assign locations or an error callback.
|
|
* @param {module:twgl.ErrorCallback} [opt_errorCallback] callback for errors. By default it just prints an error to the console
|
|
* on error. If you want something else pass an callback. It's passed an error message.
|
|
* @return {WebGLProgram?} the created program or null if error.
|
|
* @memberOf module:twgl/programs
|
|
*/
|
|
|
|
|
|
function createProgramFromScripts(gl, shaderScriptIds, opt_attribs, opt_locations, opt_errorCallback) {
|
|
const progOptions = getProgramOptions(opt_attribs, opt_locations, opt_errorCallback);
|
|
const shaders = [];
|
|
|
|
for (let ii = 0; ii < shaderScriptIds.length; ++ii) {
|
|
const shader = createShaderFromScript(gl, shaderScriptIds[ii], gl[defaultShaderType[ii]], progOptions.errorCallback);
|
|
|
|
if (!shader) {
|
|
return null;
|
|
}
|
|
|
|
shaders.push(shader);
|
|
}
|
|
|
|
return createProgram(gl, shaders, progOptions);
|
|
}
|
|
/**
|
|
* Creates a program from 2 sources.
|
|
*
|
|
* NOTE: There are 4 signatures for this function
|
|
*
|
|
* twgl.createProgramFromSource(gl, [vs, fs], opt_options);
|
|
* twgl.createProgramFromSource(gl, [vs, fs], opt_errFunc);
|
|
* twgl.createProgramFromSource(gl, [vs, fs], opt_attribs, opt_errFunc);
|
|
* twgl.createProgramFromSource(gl, [vs, fs], opt_attribs, opt_locations, opt_errFunc);
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext
|
|
* to use.
|
|
* @param {string[]} shaderSources Array of sources for the
|
|
* shaders. The first is assumed to be the vertex shader,
|
|
* the second the fragment shader.
|
|
* @param {module:twgl.ProgramOptions|string[]|module:twgl.ErrorCallback} [opt_attribs] Options for the program or an array of attribs names or an error callback. Locations will be assigned by index if not passed in
|
|
* @param {number[]} [opt_locations|module:twgl.ErrorCallback] The locations for the. A parallel array to opt_attribs letting you assign locations or an error callback.
|
|
* @param {module:twgl.ErrorCallback} [opt_errorCallback] callback for errors. By default it just prints an error to the console
|
|
* on error. If you want something else pass an callback. It's passed an error message.
|
|
* @return {WebGLProgram?} the created program or null if error.
|
|
* @memberOf module:twgl/programs
|
|
*/
|
|
|
|
|
|
function createProgramFromSources(gl, shaderSources, opt_attribs, opt_locations, opt_errorCallback) {
|
|
const progOptions = getProgramOptions(opt_attribs, opt_locations, opt_errorCallback);
|
|
const shaders = [];
|
|
|
|
for (let ii = 0; ii < shaderSources.length; ++ii) {
|
|
const shader = loadShader(gl, shaderSources[ii], gl[defaultShaderType[ii]], progOptions.errorCallback);
|
|
|
|
if (!shader) {
|
|
return null;
|
|
}
|
|
|
|
shaders.push(shader);
|
|
}
|
|
|
|
return createProgram(gl, shaders, progOptions);
|
|
}
|
|
/**
|
|
* Returns true if attribute/uniform is a reserved/built in
|
|
*
|
|
* It makes no sense to me why GL returns these because it's
|
|
* illegal to call `gl.getUniformLocation` and `gl.getAttribLocation`
|
|
* with names that start with `gl_` (and `webgl_` in WebGL)
|
|
*
|
|
* I can only assume they are there because they might count
|
|
* when computing the number of uniforms/attributes used when you want to
|
|
* know if you are near the limit. That doesn't really make sense
|
|
* to me but the fact that these get returned are in the spec.
|
|
*
|
|
* @param {WebGLActiveInfo} info As returned from `gl.getActiveUniform` or
|
|
* `gl.getActiveAttrib`.
|
|
* @return {bool} true if it's reserved
|
|
* @private
|
|
*/
|
|
|
|
|
|
function isBuiltIn(info) {
|
|
const name = info.name;
|
|
return name.startsWith("gl_") || name.startsWith("webgl_");
|
|
}
|
|
/**
|
|
* Creates setter functions for all uniforms of a shader
|
|
* program.
|
|
*
|
|
* @see {@link module:twgl.setUniforms}
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
|
|
* @param {WebGLProgram} program the program to create setters for.
|
|
* @returns {Object.<string, function>} an object with a setter by name for each uniform
|
|
* @memberOf module:twgl/programs
|
|
*/
|
|
|
|
|
|
function createUniformSetters(gl, program) {
|
|
let textureUnit = 0;
|
|
/**
|
|
* Creates a setter for a uniform of the given program with it's
|
|
* location embedded in the setter.
|
|
* @param {WebGLProgram} program
|
|
* @param {WebGLUniformInfo} uniformInfo
|
|
* @returns {function} the created setter.
|
|
*/
|
|
|
|
function createUniformSetter(program, uniformInfo, location) {
|
|
const isArray = uniformInfo.name.endsWith("[0]");
|
|
const type = uniformInfo.type;
|
|
const typeInfo = typeMap[type];
|
|
|
|
if (!typeInfo) {
|
|
throw new Error(`unknown type: 0x${type.toString(16)}`); // we should never get here.
|
|
}
|
|
|
|
let setter;
|
|
|
|
if (typeInfo.bindPoint) {
|
|
// it's a sampler
|
|
const unit = textureUnit;
|
|
textureUnit += uniformInfo.size;
|
|
|
|
if (isArray) {
|
|
setter = typeInfo.arraySetter(gl, type, unit, location, uniformInfo.size);
|
|
} else {
|
|
setter = typeInfo.setter(gl, type, unit, location, uniformInfo.size);
|
|
}
|
|
} else {
|
|
if (typeInfo.arraySetter && isArray) {
|
|
setter = typeInfo.arraySetter(gl, location);
|
|
} else {
|
|
setter = typeInfo.setter(gl, location);
|
|
}
|
|
}
|
|
|
|
setter.location = location;
|
|
return setter;
|
|
}
|
|
|
|
const uniformSetters = {};
|
|
const numUniforms = gl.getProgramParameter(program, ACTIVE_UNIFORMS);
|
|
|
|
for (let ii = 0; ii < numUniforms; ++ii) {
|
|
const uniformInfo = gl.getActiveUniform(program, ii);
|
|
|
|
if (isBuiltIn(uniformInfo)) {
|
|
continue;
|
|
}
|
|
|
|
let name = uniformInfo.name; // remove the array suffix.
|
|
|
|
if (name.endsWith("[0]")) {
|
|
name = name.substr(0, name.length - 3);
|
|
}
|
|
|
|
const location = gl.getUniformLocation(program, uniformInfo.name); // the uniform will have no location if it's in a uniform block
|
|
|
|
if (location) {
|
|
uniformSetters[name] = createUniformSetter(program, uniformInfo, location);
|
|
}
|
|
}
|
|
|
|
return uniformSetters;
|
|
}
|
|
/**
|
|
* @typedef {Object} TransformFeedbackInfo
|
|
* @property {number} index index of transform feedback
|
|
* @property {number} type GL type
|
|
* @property {number} size 1 - 4
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* Create TransformFeedbackInfo for passing to bindTransformFeedbackInfo.
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
|
|
* @param {WebGLProgram} program an existing WebGLProgram.
|
|
* @return {Object<string, module:twgl.TransformFeedbackInfo>}
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
|
|
function createTransformFeedbackInfo(gl, program) {
|
|
const info = {};
|
|
const numVaryings = gl.getProgramParameter(program, TRANSFORM_FEEDBACK_VARYINGS);
|
|
|
|
for (let ii = 0; ii < numVaryings; ++ii) {
|
|
const varying = gl.getTransformFeedbackVarying(program, ii);
|
|
info[varying.name] = {
|
|
index: ii,
|
|
type: varying.type,
|
|
size: varying.size
|
|
};
|
|
}
|
|
|
|
return info;
|
|
}
|
|
/**
|
|
* Binds buffers for transform feedback.
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
|
|
* @param {(module:twgl.ProgramInfo|Object<string, module:twgl.TransformFeedbackInfo>)} transformFeedbackInfo A ProgramInfo or TransformFeedbackInfo.
|
|
* @param {(module:twgl.BufferInfo|Object<string, module:twgl.AttribInfo>)} [bufferInfo] A BufferInfo or set of AttribInfos.
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
|
|
function bindTransformFeedbackInfo(gl, transformFeedbackInfo, bufferInfo) {
|
|
if (transformFeedbackInfo.transformFeedbackInfo) {
|
|
transformFeedbackInfo = transformFeedbackInfo.transformFeedbackInfo;
|
|
}
|
|
|
|
if (bufferInfo.attribs) {
|
|
bufferInfo = bufferInfo.attribs;
|
|
}
|
|
|
|
for (const name in bufferInfo) {
|
|
const varying = transformFeedbackInfo[name];
|
|
|
|
if (varying) {
|
|
const buf = bufferInfo[name];
|
|
|
|
if (buf.offset) {
|
|
gl.bindBufferRange(TRANSFORM_FEEDBACK_BUFFER, varying.index, buf.buffer, buf.offset, buf.size);
|
|
} else {
|
|
gl.bindBufferBase(TRANSFORM_FEEDBACK_BUFFER, varying.index, buf.buffer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Creates a transform feedback and sets the buffers
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
|
|
* @param {module:twgl.ProgramInfo} programInfo A ProgramInfo as returned from {@link module:twgl.createProgramInfo}
|
|
* @param {(module:twgl.BufferInfo|Object<string, module:twgl.AttribInfo>)} [bufferInfo] A BufferInfo or set of AttribInfos.
|
|
* @return {WebGLTransformFeedback} the created transform feedback
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
|
|
function createTransformFeedback(gl, programInfo, bufferInfo) {
|
|
const tf = gl.createTransformFeedback();
|
|
gl.bindTransformFeedback(TRANSFORM_FEEDBACK, tf);
|
|
gl.useProgram(programInfo.program);
|
|
bindTransformFeedbackInfo(gl, programInfo, bufferInfo);
|
|
gl.bindTransformFeedback(TRANSFORM_FEEDBACK, null);
|
|
return tf;
|
|
}
|
|
/**
|
|
* @typedef {Object} UniformData
|
|
* @property {number} type The WebGL type enum for this uniform
|
|
* @property {number} size The number of elements for this uniform
|
|
* @property {number} blockNdx The block index this uniform appears in
|
|
* @property {number} offset The byte offset in the block for this uniform's value
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* The specification for one UniformBlockObject
|
|
*
|
|
* @typedef {Object} BlockSpec
|
|
* @property {number} index The index of the block.
|
|
* @property {number} size The size in bytes needed for the block
|
|
* @property {number[]} uniformIndices The indices of the uniforms used by the block. These indices
|
|
* correspond to entries in a UniformData array in the {@link module:twgl.UniformBlockSpec}.
|
|
* @property {bool} usedByVertexShader Self explanatory
|
|
* @property {bool} usedByFragmentShader Self explanatory
|
|
* @property {bool} used Self explanatory
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* A `UniformBlockSpec` represents the data needed to create and bind
|
|
* UniformBlockObjects for a given program
|
|
*
|
|
* @typedef {Object} UniformBlockSpec
|
|
* @property {Object.<string, module:twgl.BlockSpec> blockSpecs The BlockSpec for each block by block name
|
|
* @property {UniformData[]} uniformData An array of data for each uniform by uniform index.
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* Creates a UniformBlockSpec for the given program.
|
|
*
|
|
* A UniformBlockSpec represents the data needed to create and bind
|
|
* UniformBlockObjects
|
|
*
|
|
* @param {WebGL2RenderingContext} gl A WebGL2 Rendering Context
|
|
* @param {WebGLProgram} program A WebGLProgram for a successfully linked program
|
|
* @return {module:twgl.UniformBlockSpec} The created UniformBlockSpec
|
|
* @memberOf module:twgl/programs
|
|
*/
|
|
|
|
|
|
function createUniformBlockSpecFromProgram(gl, program) {
|
|
const numUniforms = gl.getProgramParameter(program, ACTIVE_UNIFORMS);
|
|
const uniformData = [];
|
|
const uniformIndices = [];
|
|
|
|
for (let ii = 0; ii < numUniforms; ++ii) {
|
|
uniformIndices.push(ii);
|
|
uniformData.push({});
|
|
const uniformInfo = gl.getActiveUniform(program, ii);
|
|
|
|
if (isBuiltIn(uniformInfo)) {
|
|
break;
|
|
}
|
|
|
|
uniformData[ii].name = uniformInfo.name;
|
|
}
|
|
|
|
[["UNIFORM_TYPE", "type"], ["UNIFORM_SIZE", "size"], // num elements
|
|
["UNIFORM_BLOCK_INDEX", "blockNdx"], ["UNIFORM_OFFSET", "offset"]].forEach(function (pair) {
|
|
const pname = pair[0];
|
|
const key = pair[1];
|
|
gl.getActiveUniforms(program, uniformIndices, gl[pname]).forEach(function (value, ndx) {
|
|
uniformData[ndx][key] = value;
|
|
});
|
|
});
|
|
const blockSpecs = {};
|
|
const numUniformBlocks = gl.getProgramParameter(program, ACTIVE_UNIFORM_BLOCKS);
|
|
|
|
for (let ii = 0; ii < numUniformBlocks; ++ii) {
|
|
const name = gl.getActiveUniformBlockName(program, ii);
|
|
const blockSpec = {
|
|
index: gl.getUniformBlockIndex(program, name),
|
|
usedByVertexShader: gl.getActiveUniformBlockParameter(program, ii, UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER),
|
|
usedByFragmentShader: gl.getActiveUniformBlockParameter(program, ii, UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER),
|
|
size: gl.getActiveUniformBlockParameter(program, ii, UNIFORM_BLOCK_DATA_SIZE),
|
|
uniformIndices: gl.getActiveUniformBlockParameter(program, ii, UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES)
|
|
};
|
|
blockSpec.used = blockSpec.usedByVertexShader || blockSpec.usedByFragmentShader;
|
|
blockSpecs[name] = blockSpec;
|
|
}
|
|
|
|
return {
|
|
blockSpecs: blockSpecs,
|
|
uniformData: uniformData
|
|
};
|
|
}
|
|
|
|
const arraySuffixRE = /\[\d+\]\.$/; // better way to check?
|
|
|
|
const pad = (v, padding) => ((v + (padding - 1)) / padding | 0) * padding;
|
|
|
|
function createUniformBlockUniformSetter(view, Type, typeSize, paddedSize, isArray) {
|
|
if (isArray) {
|
|
const numElements = typeSize / Type.BYTES_PER_ELEMENT;
|
|
const numPaddedElements = paddedSize / Type.BYTES_PER_ELEMENT;
|
|
return function (value) {
|
|
let dst = 0;
|
|
|
|
for (let src = 0; src < value.length; src += numElements) {
|
|
for (let i = 0; i < numElements; ++i) {
|
|
view[dst + i] = value[src + i];
|
|
}
|
|
|
|
dst += numPaddedElements;
|
|
}
|
|
};
|
|
} else {
|
|
return function (value) {
|
|
if (value.length) {
|
|
view.set(value);
|
|
} else {
|
|
view[0] = value;
|
|
}
|
|
};
|
|
}
|
|
}
|
|
/**
|
|
* Represents a UniformBlockObject including an ArrayBuffer with all the uniform values
|
|
* and a corresponding WebGLBuffer to hold those values on the GPU
|
|
*
|
|
* @typedef {Object} UniformBlockInfo
|
|
* @property {string} name The name of the block
|
|
* @property {ArrayBuffer} array The array buffer that contains the uniform values
|
|
* @property {Float32Array} asFloat A float view on the array buffer. This is useful
|
|
* inspecting the contents of the buffer in the debugger.
|
|
* @property {WebGLBuffer} buffer A WebGL buffer that will hold a copy of the uniform values for rendering.
|
|
* @property {number} [offset] offset into buffer
|
|
* @property {Object<string, ArrayBufferView>} uniforms A uniform name to ArrayBufferView map.
|
|
* each Uniform has a correctly typed `ArrayBufferView` into array at the correct offset
|
|
* and length of that uniform. So for example a float uniform would have a 1 float `Float32Array`
|
|
* view. A single mat4 would have a 16 element `Float32Array` view. An ivec2 would have an
|
|
* `Int32Array` view, etc.
|
|
* @property {Object<string, function>} setters A setter for this uniform.
|
|
* The reason to use setters is elements of arrays are padded to vec4 sizes which
|
|
* means if you want to set an array of 4 floats you'd need to set 16 values
|
|
* (or set elements 0, 4, 8, 12). In other words
|
|
* `someBlockInfo.uniforms.some4FloatArrayUniform.set([0, , , , 1, , , , 2, , , , 3])`
|
|
* where as the setter handles just passing in [0, 1, 2, 3] either directly as in
|
|
* `someBlockInfo.setter.some4FloatArrayUniform.set([0, 1, 2, 3])` (not recommended)
|
|
* or via {@link module:twgl.setBlockUniforms}
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* Creates a `UniformBlockInfo` for the specified block
|
|
*
|
|
* Note: **If the blockName matches no existing blocks a warning is printed to the console and a dummy
|
|
* `UniformBlockInfo` is returned**. This is because when debugging GLSL
|
|
* it is common to comment out large portions of a shader or for example set
|
|
* the final output to a constant. When that happens blocks get optimized out.
|
|
* If this function did not create dummy blocks your code would crash when debugging.
|
|
*
|
|
* @param {WebGL2RenderingContext} gl A WebGL2RenderingContext
|
|
* @param {WebGLProgram} program A WebGLProgram
|
|
* @param {module:twgl.UniformBlockSpec} uniformBlockSpec. A UniformBlockSpec as returned
|
|
* from {@link module:twgl.createUniformBlockSpecFromProgram}.
|
|
* @param {string} blockName The name of the block.
|
|
* @return {module:twgl.UniformBlockInfo} The created UniformBlockInfo
|
|
* @memberOf module:twgl/programs
|
|
*/
|
|
|
|
|
|
function createUniformBlockInfoFromProgram(gl, program, uniformBlockSpec, blockName) {
|
|
const blockSpecs = uniformBlockSpec.blockSpecs;
|
|
const uniformData = uniformBlockSpec.uniformData;
|
|
const blockSpec = blockSpecs[blockName];
|
|
|
|
if (!blockSpec) {
|
|
warn$1("no uniform block object named:", blockName);
|
|
return {
|
|
name: blockName,
|
|
uniforms: {}
|
|
};
|
|
}
|
|
|
|
const array = new ArrayBuffer(blockSpec.size);
|
|
const buffer = gl.createBuffer();
|
|
const uniformBufferIndex = blockSpec.index;
|
|
gl.bindBuffer(UNIFORM_BUFFER, buffer);
|
|
gl.uniformBlockBinding(program, blockSpec.index, uniformBufferIndex);
|
|
let prefix = blockName + ".";
|
|
|
|
if (arraySuffixRE.test(prefix)) {
|
|
prefix = prefix.replace(arraySuffixRE, ".");
|
|
}
|
|
|
|
const uniforms = {};
|
|
const setters = {};
|
|
blockSpec.uniformIndices.forEach(function (uniformNdx) {
|
|
const data = uniformData[uniformNdx];
|
|
const typeInfo = typeMap[data.type];
|
|
const Type = typeInfo.Type;
|
|
const paddedSize = pad(typeInfo.size, 16);
|
|
const length = typeInfo.size + (data.size - 1) * paddedSize;
|
|
let name = data.name;
|
|
|
|
if (name.startsWith(prefix)) {
|
|
name = name.substr(prefix.length);
|
|
}
|
|
|
|
const isArray = name.endsWith('[0]');
|
|
|
|
if (isArray) {
|
|
name = name.substr(0, name.length - 3);
|
|
}
|
|
|
|
const uniformView = new Type(array, data.offset, length / Type.BYTES_PER_ELEMENT);
|
|
uniforms[name] = uniformView;
|
|
setters[name] = createUniformBlockUniformSetter(uniformView, Type, typeInfo.size, paddedSize, isArray);
|
|
});
|
|
return {
|
|
name: blockName,
|
|
array,
|
|
asFloat: new Float32Array(array),
|
|
// for debugging
|
|
buffer,
|
|
uniforms,
|
|
setters
|
|
};
|
|
}
|
|
/**
|
|
* Creates a `UniformBlockInfo` for the specified block
|
|
*
|
|
* Note: **If the blockName matches no existing blocks a warning is printed to the console and a dummy
|
|
* `UniformBlockInfo` is returned**. This is because when debugging GLSL
|
|
* it is common to comment out large portions of a shader or for example set
|
|
* the final output to a constant. When that happens blocks get optimized out.
|
|
* If this function did not create dummy blocks your code would crash when debugging.
|
|
*
|
|
* @param {WebGL2RenderingContext} gl A WebGL2RenderingContext
|
|
* @param {module:twgl.ProgramInfo} programInfo a `ProgramInfo`
|
|
* as returned from {@link module:twgl.createProgramInfo}
|
|
* @param {string} blockName The name of the block.
|
|
* @return {module:twgl.UniformBlockInfo} The created UniformBlockInfo
|
|
* @memberOf module:twgl/programs
|
|
*/
|
|
|
|
|
|
function createUniformBlockInfo(gl, programInfo, blockName) {
|
|
return createUniformBlockInfoFromProgram(gl, programInfo.program, programInfo.uniformBlockSpec, blockName);
|
|
}
|
|
/**
|
|
* Binds a uniform block to the matching uniform block point.
|
|
* Matches by blocks by name so blocks must have the same name not just the same
|
|
* structure.
|
|
*
|
|
* If you have changed any values and you upload the values into the corresponding WebGLBuffer
|
|
* call {@link module:twgl.setUniformBlock} instead.
|
|
*
|
|
* @param {WebGL2RenderingContext} gl A WebGL 2 rendering context.
|
|
* @param {(module:twgl.ProgramInfo|module:twgl.UniformBlockSpec)} programInfo a `ProgramInfo`
|
|
* as returned from {@link module:twgl.createProgramInfo} or or `UniformBlockSpec` as
|
|
* returned from {@link module:twgl.createUniformBlockSpecFromProgram}.
|
|
* @param {module:twgl.UniformBlockInfo} uniformBlockInfo a `UniformBlockInfo` as returned from
|
|
* {@link module:twgl.createUniformBlockInfo}.
|
|
* @return {bool} true if buffer was bound. If the programInfo has no block with the same block name
|
|
* no buffer is bound.
|
|
* @memberOf module:twgl/programs
|
|
*/
|
|
|
|
|
|
function bindUniformBlock(gl, programInfo, uniformBlockInfo) {
|
|
const uniformBlockSpec = programInfo.uniformBlockSpec || programInfo;
|
|
const blockSpec = uniformBlockSpec.blockSpecs[uniformBlockInfo.name];
|
|
|
|
if (blockSpec) {
|
|
const bufferBindIndex = blockSpec.index;
|
|
gl.bindBufferRange(UNIFORM_BUFFER, bufferBindIndex, uniformBlockInfo.buffer, uniformBlockInfo.offset || 0, uniformBlockInfo.array.byteLength);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
/**
|
|
* Uploads the current uniform values to the corresponding WebGLBuffer
|
|
* and binds that buffer to the program's corresponding bind point for the uniform block object.
|
|
*
|
|
* If you haven't changed any values and you only need to bind the uniform block object
|
|
* call {@link module:twgl.bindUniformBlock} instead.
|
|
*
|
|
* @param {WebGL2RenderingContext} gl A WebGL 2 rendering context.
|
|
* @param {(module:twgl.ProgramInfo|module:twgl.UniformBlockSpec)} programInfo a `ProgramInfo`
|
|
* as returned from {@link module:twgl.createProgramInfo} or or `UniformBlockSpec` as
|
|
* returned from {@link module:twgl.createUniformBlockSpecFromProgram}.
|
|
* @param {module:twgl.UniformBlockInfo} uniformBlockInfo a `UniformBlockInfo` as returned from
|
|
* {@link module:twgl.createUniformBlockInfo}.
|
|
* @memberOf module:twgl/programs
|
|
*/
|
|
|
|
|
|
function setUniformBlock(gl, programInfo, uniformBlockInfo) {
|
|
if (bindUniformBlock(gl, programInfo, uniformBlockInfo)) {
|
|
gl.bufferData(UNIFORM_BUFFER, uniformBlockInfo.array, DYNAMIC_DRAW);
|
|
}
|
|
}
|
|
/**
|
|
* Sets values of a uniform block object
|
|
*
|
|
* @param {module:twgl.UniformBlockInfo} uniformBlockInfo A UniformBlockInfo as returned by {@link module:twgl.createUniformBlockInfo}.
|
|
* @param {Object.<string, ?>} values A uniform name to value map where the value is correct for the given
|
|
* type of uniform. So for example given a block like
|
|
*
|
|
* uniform SomeBlock {
|
|
* float someFloat;
|
|
* vec2 someVec2;
|
|
* vec3 someVec3Array[2];
|
|
* int someInt;
|
|
* }
|
|
*
|
|
* You can set the values of the uniform block with
|
|
*
|
|
* twgl.setBlockUniforms(someBlockInfo, {
|
|
* someFloat: 12.3,
|
|
* someVec2: [1, 2],
|
|
* someVec3Array: [1, 2, 3, 4, 5, 6],
|
|
* someInt: 5,
|
|
* }
|
|
*
|
|
* Arrays can be JavaScript arrays or typed arrays
|
|
*
|
|
* Any name that doesn't match will be ignored
|
|
* @memberOf module:twgl/programs
|
|
*/
|
|
|
|
|
|
function setBlockUniforms(uniformBlockInfo, values) {
|
|
const setters = uniformBlockInfo.setters;
|
|
|
|
for (const name in values) {
|
|
const setter = setters[name];
|
|
|
|
if (setter) {
|
|
const value = values[name];
|
|
setter(value);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Set uniforms and binds related textures.
|
|
*
|
|
* example:
|
|
*
|
|
* const programInfo = createProgramInfo(
|
|
* gl, ["some-vs", "some-fs"]);
|
|
*
|
|
* const tex1 = gl.createTexture();
|
|
* const tex2 = gl.createTexture();
|
|
*
|
|
* ... assume we setup the textures with data ...
|
|
*
|
|
* const uniforms = {
|
|
* u_someSampler: tex1,
|
|
* u_someOtherSampler: tex2,
|
|
* u_someColor: [1,0,0,1],
|
|
* u_somePosition: [0,1,1],
|
|
* u_someMatrix: [
|
|
* 1,0,0,0,
|
|
* 0,1,0,0,
|
|
* 0,0,1,0,
|
|
* 0,0,0,0,
|
|
* ],
|
|
* };
|
|
*
|
|
* gl.useProgram(program);
|
|
*
|
|
* This will automatically bind the textures AND set the
|
|
* uniforms.
|
|
*
|
|
* twgl.setUniforms(programInfo, uniforms);
|
|
*
|
|
* For the example above it is equivalent to
|
|
*
|
|
* var texUnit = 0;
|
|
* gl.activeTexture(gl.TEXTURE0 + texUnit);
|
|
* gl.bindTexture(gl.TEXTURE_2D, tex1);
|
|
* gl.uniform1i(u_someSamplerLocation, texUnit++);
|
|
* gl.activeTexture(gl.TEXTURE0 + texUnit);
|
|
* gl.bindTexture(gl.TEXTURE_2D, tex2);
|
|
* gl.uniform1i(u_someSamplerLocation, texUnit++);
|
|
* gl.uniform4fv(u_someColorLocation, [1, 0, 0, 1]);
|
|
* gl.uniform3fv(u_somePositionLocation, [0, 1, 1]);
|
|
* gl.uniformMatrix4fv(u_someMatrix, false, [
|
|
* 1,0,0,0,
|
|
* 0,1,0,0,
|
|
* 0,0,1,0,
|
|
* 0,0,0,0,
|
|
* ]);
|
|
*
|
|
* Note it is perfectly reasonable to call `setUniforms` multiple times. For example
|
|
*
|
|
* const uniforms = {
|
|
* u_someSampler: tex1,
|
|
* u_someOtherSampler: tex2,
|
|
* };
|
|
*
|
|
* const moreUniforms {
|
|
* u_someColor: [1,0,0,1],
|
|
* u_somePosition: [0,1,1],
|
|
* u_someMatrix: [
|
|
* 1,0,0,0,
|
|
* 0,1,0,0,
|
|
* 0,0,1,0,
|
|
* 0,0,0,0,
|
|
* ],
|
|
* };
|
|
*
|
|
* twgl.setUniforms(programInfo, uniforms);
|
|
* twgl.setUniforms(programInfo, moreUniforms);
|
|
*
|
|
* You can also add WebGLSamplers to uniform samplers as in
|
|
*
|
|
* const uniforms = {
|
|
* u_someSampler: {
|
|
* texture: someWebGLTexture,
|
|
* sampler: someWebGLSampler,
|
|
* },
|
|
* };
|
|
*
|
|
* In which case both the sampler and texture will be bound to the
|
|
* same unit.
|
|
*
|
|
* @param {(module:twgl.ProgramInfo|Object.<string, function>)} setters a `ProgramInfo` as returned from `createProgramInfo` or the setters returned from
|
|
* `createUniformSetters`.
|
|
* @param {Object.<string, ?>} values an object with values for the
|
|
* uniforms.
|
|
* You can pass multiple objects by putting them in an array or by calling with more arguments.For example
|
|
*
|
|
* const sharedUniforms = {
|
|
* u_fogNear: 10,
|
|
* u_projection: ...
|
|
* ...
|
|
* };
|
|
*
|
|
* const localUniforms = {
|
|
* u_world: ...
|
|
* u_diffuseColor: ...
|
|
* };
|
|
*
|
|
* twgl.setUniforms(programInfo, sharedUniforms, localUniforms);
|
|
*
|
|
* // is the same as
|
|
*
|
|
* twgl.setUniforms(programInfo, [sharedUniforms, localUniforms]);
|
|
*
|
|
* // is the same as
|
|
*
|
|
* twgl.setUniforms(programInfo, sharedUniforms);
|
|
* twgl.setUniforms(programInfo, localUniforms};
|
|
*
|
|
* @memberOf module:twgl/programs
|
|
*/
|
|
|
|
|
|
function setUniforms(setters, values) {
|
|
// eslint-disable-line
|
|
const actualSetters = setters.uniformSetters || setters;
|
|
const numArgs = arguments.length;
|
|
|
|
for (let aNdx = 1; aNdx < numArgs; ++aNdx) {
|
|
const values = arguments[aNdx];
|
|
|
|
if (Array.isArray(values)) {
|
|
const numValues = values.length;
|
|
|
|
for (let ii = 0; ii < numValues; ++ii) {
|
|
setUniforms(actualSetters, values[ii]);
|
|
}
|
|
} else {
|
|
for (const name in values) {
|
|
const setter = actualSetters[name];
|
|
|
|
if (setter) {
|
|
setter(values[name]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Alias for `setUniforms`
|
|
* @function
|
|
* @param {(module:twgl.ProgramInfo|Object.<string, function>)} setters a `ProgramInfo` as returned from `createProgramInfo` or the setters returned from
|
|
* `createUniformSetters`.
|
|
* @param {Object.<string, ?>} values an object with values for the
|
|
* @memberOf module:twgl/programs
|
|
*/
|
|
|
|
|
|
const setUniformsAndBindTextures = setUniforms;
|
|
/**
|
|
* Creates setter functions for all attributes of a shader
|
|
* program. You can pass this to {@link module:twgl.setBuffersAndAttributes} to set all your buffers and attributes.
|
|
*
|
|
* @see {@link module:twgl.setAttributes} for example
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
|
|
* @param {WebGLProgram} program the program to create setters for.
|
|
* @return {Object.<string, function>} an object with a setter for each attribute by name.
|
|
* @memberOf module:twgl/programs
|
|
*/
|
|
|
|
exports.setUniformsAndBindTextures = setUniformsAndBindTextures;
|
|
|
|
function createAttributeSetters(gl, program) {
|
|
const attribSetters = {};
|
|
const numAttribs = gl.getProgramParameter(program, ACTIVE_ATTRIBUTES);
|
|
|
|
for (let ii = 0; ii < numAttribs; ++ii) {
|
|
const attribInfo = gl.getActiveAttrib(program, ii);
|
|
|
|
if (isBuiltIn(attribInfo)) {
|
|
continue;
|
|
}
|
|
|
|
const index = gl.getAttribLocation(program, attribInfo.name);
|
|
const typeInfo = attrTypeMap[attribInfo.type];
|
|
const setter = typeInfo.setter(gl, index, typeInfo);
|
|
setter.location = index;
|
|
attribSetters[attribInfo.name] = setter;
|
|
}
|
|
|
|
return attribSetters;
|
|
}
|
|
/**
|
|
* Sets attributes and binds buffers (deprecated... use {@link module:twgl.setBuffersAndAttributes})
|
|
*
|
|
* Example:
|
|
*
|
|
* const program = createProgramFromScripts(
|
|
* gl, ["some-vs", "some-fs");
|
|
*
|
|
* const attribSetters = createAttributeSetters(program);
|
|
*
|
|
* const positionBuffer = gl.createBuffer();
|
|
* const texcoordBuffer = gl.createBuffer();
|
|
*
|
|
* const attribs = {
|
|
* a_position: {buffer: positionBuffer, numComponents: 3},
|
|
* a_texcoord: {buffer: texcoordBuffer, numComponents: 2},
|
|
* };
|
|
*
|
|
* gl.useProgram(program);
|
|
*
|
|
* This will automatically bind the buffers AND set the
|
|
* attributes.
|
|
*
|
|
* setAttributes(attribSetters, attribs);
|
|
*
|
|
* Properties of attribs. For each attrib you can add
|
|
* properties:
|
|
*
|
|
* * type: the type of data in the buffer. Default = gl.FLOAT
|
|
* * normalize: whether or not to normalize the data. Default = false
|
|
* * stride: the stride. Default = 0
|
|
* * offset: offset into the buffer. Default = 0
|
|
* * divisor: the divisor for instances. Default = undefined
|
|
*
|
|
* For example if you had 3 value float positions, 2 value
|
|
* float texcoord and 4 value uint8 colors you'd setup your
|
|
* attribs like this
|
|
*
|
|
* const attribs = {
|
|
* a_position: {buffer: positionBuffer, numComponents: 3},
|
|
* a_texcoord: {buffer: texcoordBuffer, numComponents: 2},
|
|
* a_color: {
|
|
* buffer: colorBuffer,
|
|
* numComponents: 4,
|
|
* type: gl.UNSIGNED_BYTE,
|
|
* normalize: true,
|
|
* },
|
|
* };
|
|
*
|
|
* @param {Object.<string, function>} setters Attribute setters as returned from createAttributeSetters
|
|
* @param {Object.<string, module:twgl.AttribInfo>} buffers AttribInfos mapped by attribute name.
|
|
* @memberOf module:twgl/programs
|
|
* @deprecated use {@link module:twgl.setBuffersAndAttributes}
|
|
*/
|
|
|
|
|
|
function setAttributes(setters, buffers) {
|
|
for (const name in buffers) {
|
|
const setter = setters[name];
|
|
|
|
if (setter) {
|
|
setter(buffers[name]);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Sets attributes and buffers including the `ELEMENT_ARRAY_BUFFER` if appropriate
|
|
*
|
|
* Example:
|
|
*
|
|
* const programInfo = createProgramInfo(
|
|
* gl, ["some-vs", "some-fs");
|
|
*
|
|
* const arrays = {
|
|
* position: { numComponents: 3, data: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0], },
|
|
* texcoord: { numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1], },
|
|
* };
|
|
*
|
|
* const bufferInfo = createBufferInfoFromArrays(gl, arrays);
|
|
*
|
|
* gl.useProgram(programInfo.program);
|
|
*
|
|
* This will automatically bind the buffers AND set the
|
|
* attributes.
|
|
*
|
|
* setBuffersAndAttributes(gl, programInfo, bufferInfo);
|
|
*
|
|
* For the example above it is equivalent to
|
|
*
|
|
* gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
|
|
* gl.enableVertexAttribArray(a_positionLocation);
|
|
* gl.vertexAttribPointer(a_positionLocation, 3, gl.FLOAT, false, 0, 0);
|
|
* gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
|
|
* gl.enableVertexAttribArray(a_texcoordLocation);
|
|
* gl.vertexAttribPointer(a_texcoordLocation, 4, gl.FLOAT, false, 0, 0);
|
|
*
|
|
* @param {WebGLRenderingContext} gl A WebGLRenderingContext.
|
|
* @param {(module:twgl.ProgramInfo|Object.<string, function>)} setters A `ProgramInfo` as returned from {@link module:twgl.createProgramInfo} or Attribute setters as returned from {@link module:twgl.createAttributeSetters}
|
|
* @param {(module:twgl.BufferInfo|module:twgl.VertexArrayInfo)} buffers a `BufferInfo` as returned from {@link module:twgl.createBufferInfoFromArrays}.
|
|
* or a `VertexArrayInfo` as returned from {@link module:twgl.createVertexArrayInfo}
|
|
* @memberOf module:twgl/programs
|
|
*/
|
|
|
|
|
|
function setBuffersAndAttributes(gl, programInfo, buffers) {
|
|
if (buffers.vertexArrayObject) {
|
|
gl.bindVertexArray(buffers.vertexArrayObject);
|
|
} else {
|
|
setAttributes(programInfo.attribSetters || programInfo, buffers.attribs);
|
|
|
|
if (buffers.indices) {
|
|
gl.bindBuffer(ELEMENT_ARRAY_BUFFER$1, buffers.indices);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* @typedef {Object} ProgramInfo
|
|
* @property {WebGLProgram} program A shader program
|
|
* @property {Object<string, function>} uniformSetters object of setters as returned from createUniformSetters,
|
|
* @property {Object<string, function>} attribSetters object of setters as returned from createAttribSetters,
|
|
* @property {module:twgl.UniformBlockSpec} [uniformBlockSpace] a uniform block spec for making UniformBlockInfos with createUniformBlockInfo etc..
|
|
* @property {Object<string, module:twgl.TransformFeedbackInfo>} [transformFeedbackInfo] info for transform feedbacks
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* Creates a ProgramInfo from an existing program.
|
|
*
|
|
* A ProgramInfo contains
|
|
*
|
|
* programInfo = {
|
|
* program: WebGLProgram,
|
|
* uniformSetters: object of setters as returned from createUniformSetters,
|
|
* attribSetters: object of setters as returned from createAttribSetters,
|
|
* }
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext
|
|
* to use.
|
|
* @param {WebGLProgram} program an existing WebGLProgram.
|
|
* @return {module:twgl.ProgramInfo} The created ProgramInfo.
|
|
* @memberOf module:twgl/programs
|
|
*/
|
|
|
|
|
|
function createProgramInfoFromProgram(gl, program) {
|
|
const uniformSetters = createUniformSetters(gl, program);
|
|
const attribSetters = createAttributeSetters(gl, program);
|
|
const programInfo = {
|
|
program: program,
|
|
uniformSetters: uniformSetters,
|
|
attribSetters: attribSetters
|
|
};
|
|
|
|
if (isWebGL2(gl)) {
|
|
programInfo.uniformBlockSpec = createUniformBlockSpecFromProgram(gl, program);
|
|
programInfo.transformFeedbackInfo = createTransformFeedbackInfo(gl, program);
|
|
}
|
|
|
|
return programInfo;
|
|
}
|
|
/**
|
|
* Creates a ProgramInfo from 2 sources.
|
|
*
|
|
* A ProgramInfo contains
|
|
*
|
|
* programInfo = {
|
|
* program: WebGLProgram,
|
|
* uniformSetters: object of setters as returned from createUniformSetters,
|
|
* attribSetters: object of setters as returned from createAttribSetters,
|
|
* }
|
|
*
|
|
* NOTE: There are 4 signatures for this function
|
|
*
|
|
* twgl.createProgramInfo(gl, [vs, fs], options);
|
|
* twgl.createProgramInfo(gl, [vs, fs], opt_errFunc);
|
|
* twgl.createProgramInfo(gl, [vs, fs], opt_attribs, opt_errFunc);
|
|
* twgl.createProgramInfo(gl, [vs, fs], opt_attribs, opt_locations, opt_errFunc);
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext
|
|
* to use.
|
|
* @param {string[]} shaderSources Array of sources for the
|
|
* shaders or ids. The first is assumed to be the vertex shader,
|
|
* the second the fragment shader.
|
|
* @param {module:twgl.ProgramOptions|string[]|module:twgl.ErrorCallback} [opt_attribs] Options for the program or an array of attribs names or an error callback. Locations will be assigned by index if not passed in
|
|
* @param {number[]} [opt_locations|module:twgl.ErrorCallback] The locations for the. A parallel array to opt_attribs letting you assign locations or an error callback.
|
|
* @param {module:twgl.ErrorCallback} [opt_errorCallback] callback for errors. By default it just prints an error to the console
|
|
* on error. If you want something else pass an callback. It's passed an error message.
|
|
* @return {module:twgl.ProgramInfo?} The created ProgramInfo or null if it failed to link or compile
|
|
* @memberOf module:twgl/programs
|
|
*/
|
|
|
|
|
|
function createProgramInfo(gl, shaderSources, opt_attribs, opt_locations, opt_errorCallback) {
|
|
const progOptions = getProgramOptions(opt_attribs, opt_locations, opt_errorCallback);
|
|
let good = true;
|
|
shaderSources = shaderSources.map(function (source) {
|
|
// Lets assume if there is no \n it's an id
|
|
if (source.indexOf("\n") < 0) {
|
|
const script = getElementById(source);
|
|
|
|
if (!script) {
|
|
progOptions.errorCallback("no element with id: " + source);
|
|
good = false;
|
|
} else {
|
|
source = script.text;
|
|
}
|
|
}
|
|
|
|
return source;
|
|
});
|
|
|
|
if (!good) {
|
|
return null;
|
|
}
|
|
|
|
const program = createProgramFromSources(gl, shaderSources, progOptions);
|
|
|
|
if (!program) {
|
|
return null;
|
|
}
|
|
|
|
return createProgramInfoFromProgram(gl, program);
|
|
}
|
|
|
|
var programs = /*#__PURE__*/Object.freeze({
|
|
__proto__: null,
|
|
createAttributeSetters: createAttributeSetters,
|
|
createProgram: createProgram,
|
|
createProgramFromScripts: createProgramFromScripts,
|
|
createProgramFromSources: createProgramFromSources,
|
|
createProgramInfo: createProgramInfo,
|
|
createProgramInfoFromProgram: createProgramInfoFromProgram,
|
|
createUniformSetters: createUniformSetters,
|
|
createUniformBlockSpecFromProgram: createUniformBlockSpecFromProgram,
|
|
createUniformBlockInfoFromProgram: createUniformBlockInfoFromProgram,
|
|
createUniformBlockInfo: createUniformBlockInfo,
|
|
createTransformFeedback: createTransformFeedback,
|
|
createTransformFeedbackInfo: createTransformFeedbackInfo,
|
|
bindTransformFeedbackInfo: bindTransformFeedbackInfo,
|
|
setAttributes: setAttributes,
|
|
setBuffersAndAttributes: setBuffersAndAttributes,
|
|
setUniforms: setUniforms,
|
|
setUniformsAndBindTextures: setUniformsAndBindTextures,
|
|
setUniformBlock: setUniformBlock,
|
|
setBlockUniforms: setBlockUniforms,
|
|
bindUniformBlock: bindUniformBlock
|
|
});
|
|
/*
|
|
* Copyright 2019 Gregg Tavares
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
exports.programs = programs;
|
|
const TRIANGLES = 0x0004;
|
|
const UNSIGNED_SHORT$3 = 0x1403;
|
|
/**
|
|
* Drawing related functions
|
|
*
|
|
* For backward compatibility they are available at both `twgl.draw` and `twgl`
|
|
* itself
|
|
*
|
|
* See {@link module:twgl} for core functions
|
|
*
|
|
* @module twgl/draw
|
|
*/
|
|
|
|
/**
|
|
* Calls `gl.drawElements` or `gl.drawArrays`, whichever is appropriate
|
|
*
|
|
* normally you'd call `gl.drawElements` or `gl.drawArrays` yourself
|
|
* but calling this means if you switch from indexed data to non-indexed
|
|
* data you don't have to remember to update your draw call.
|
|
*
|
|
* @param {WebGLRenderingContext} gl A WebGLRenderingContext
|
|
* @param {(module:twgl.BufferInfo|module:twgl.VertexArrayInfo)} bufferInfo A BufferInfo as returned from {@link module:twgl.createBufferInfoFromArrays} or
|
|
* a VertexArrayInfo as returned from {@link module:twgl.createVertexArrayInfo}
|
|
* @param {number} [type] eg (gl.TRIANGLES, gl.LINES, gl.POINTS, gl.TRIANGLE_STRIP, ...). Defaults to `gl.TRIANGLES`
|
|
* @param {number} [count] An optional count. Defaults to bufferInfo.numElements
|
|
* @param {number} [offset] An optional offset. Defaults to 0.
|
|
* @param {number} [instanceCount] An optional instanceCount. if set then `drawArraysInstanced` or `drawElementsInstanced` will be called
|
|
* @memberOf module:twgl/draw
|
|
*/
|
|
|
|
function drawBufferInfo(gl, bufferInfo, type, count, offset, instanceCount) {
|
|
type = type === undefined ? TRIANGLES : type;
|
|
const indices = bufferInfo.indices;
|
|
const elementType = bufferInfo.elementType;
|
|
const numElements = count === undefined ? bufferInfo.numElements : count;
|
|
offset = offset === undefined ? 0 : offset;
|
|
|
|
if (elementType || indices) {
|
|
if (instanceCount !== undefined) {
|
|
gl.drawElementsInstanced(type, numElements, elementType === undefined ? UNSIGNED_SHORT$3 : bufferInfo.elementType, offset, instanceCount);
|
|
} else {
|
|
gl.drawElements(type, numElements, elementType === undefined ? UNSIGNED_SHORT$3 : bufferInfo.elementType, offset);
|
|
}
|
|
} else {
|
|
if (instanceCount !== undefined) {
|
|
gl.drawArraysInstanced(type, offset, numElements, instanceCount);
|
|
} else {
|
|
gl.drawArrays(type, offset, numElements);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* A DrawObject is useful for putting objects in to an array and passing them to {@link module:twgl.drawObjectList}.
|
|
*
|
|
* You need either a `BufferInfo` or a `VertexArrayInfo`.
|
|
*
|
|
* @typedef {Object} DrawObject
|
|
* @property {boolean} [active] whether or not to draw. Default = `true` (must be `false` to be not true). In other words `undefined` = `true`
|
|
* @property {number} [type] type to draw eg. `gl.TRIANGLES`, `gl.LINES`, etc...
|
|
* @property {module:twgl.ProgramInfo} programInfo A ProgramInfo as returned from {@link module:twgl.createProgramInfo}
|
|
* @property {module:twgl.BufferInfo} [bufferInfo] A BufferInfo as returned from {@link module:twgl.createBufferInfoFromArrays}
|
|
* @property {module:twgl.VertexArrayInfo} [vertexArrayInfo] A VertexArrayInfo as returned from {@link module:twgl.createVertexArrayInfo}
|
|
* @property {Object<string, ?>} uniforms The values for the uniforms.
|
|
* You can pass multiple objects by putting them in an array. For example
|
|
*
|
|
* var sharedUniforms = {
|
|
* u_fogNear: 10,
|
|
* u_projection: ...
|
|
* ...
|
|
* };
|
|
*
|
|
* var localUniforms = {
|
|
* u_world: ...
|
|
* u_diffuseColor: ...
|
|
* };
|
|
*
|
|
* var drawObj = {
|
|
* ...
|
|
* uniforms: [sharedUniforms, localUniforms],
|
|
* };
|
|
*
|
|
* @property {number} [offset] the offset to pass to `gl.drawArrays` or `gl.drawElements`. Defaults to 0.
|
|
* @property {number} [count] the count to pass to `gl.drawArrays` or `gl.drawElements`. Defaults to bufferInfo.numElements.
|
|
* @property {number} [instanceCount] the number of instances. Defaults to undefined.
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* Draws a list of objects
|
|
* @param {WebGLRenderingContext} gl A WebGLRenderingContext
|
|
* @param {DrawObject[]} objectsToDraw an array of objects to draw.
|
|
* @memberOf module:twgl/draw
|
|
*/
|
|
|
|
|
|
function drawObjectList(gl, objectsToDraw) {
|
|
let lastUsedProgramInfo = null;
|
|
let lastUsedBufferInfo = null;
|
|
objectsToDraw.forEach(function (object) {
|
|
if (object.active === false) {
|
|
return;
|
|
}
|
|
|
|
const programInfo = object.programInfo;
|
|
const bufferInfo = object.vertexArrayInfo || object.bufferInfo;
|
|
let bindBuffers = false;
|
|
const type = object.type === undefined ? TRIANGLES : object.type;
|
|
|
|
if (programInfo !== lastUsedProgramInfo) {
|
|
lastUsedProgramInfo = programInfo;
|
|
gl.useProgram(programInfo.program); // We have to rebind buffers when changing programs because we
|
|
// only bind buffers the program uses. So if 2 programs use the same
|
|
// bufferInfo but the 1st one uses only positions the when the
|
|
// we switch to the 2nd one some of the attributes will not be on.
|
|
|
|
bindBuffers = true;
|
|
} // Setup all the needed attributes.
|
|
|
|
|
|
if (bindBuffers || bufferInfo !== lastUsedBufferInfo) {
|
|
if (lastUsedBufferInfo && lastUsedBufferInfo.vertexArrayObject && !bufferInfo.vertexArrayObject) {
|
|
gl.bindVertexArray(null);
|
|
}
|
|
|
|
lastUsedBufferInfo = bufferInfo;
|
|
setBuffersAndAttributes(gl, programInfo, bufferInfo);
|
|
} // Set the uniforms.
|
|
|
|
|
|
setUniforms(programInfo, object.uniforms); // Draw
|
|
|
|
drawBufferInfo(gl, bufferInfo, type, object.count, object.offset, object.instanceCount);
|
|
});
|
|
|
|
if (lastUsedBufferInfo && lastUsedBufferInfo.vertexArrayObject) {
|
|
gl.bindVertexArray(null);
|
|
}
|
|
}
|
|
|
|
var draw = /*#__PURE__*/Object.freeze({
|
|
__proto__: null,
|
|
drawBufferInfo: drawBufferInfo,
|
|
drawObjectList: drawObjectList
|
|
});
|
|
/*
|
|
* Copyright 2019 Gregg Tavares
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
exports.draw = draw;
|
|
const FRAMEBUFFER = 0x8d40;
|
|
const RENDERBUFFER = 0x8d41;
|
|
const TEXTURE_2D$2 = 0x0de1;
|
|
const UNSIGNED_BYTE$3 = 0x1401;
|
|
/* PixelFormat */
|
|
|
|
const DEPTH_COMPONENT$1 = 0x1902;
|
|
const RGBA$1 = 0x1908;
|
|
const DEPTH_COMPONENT24$1 = 0x81a6;
|
|
const DEPTH_COMPONENT32F$1 = 0x8cac;
|
|
const DEPTH24_STENCIL8$1 = 0x88f0;
|
|
const DEPTH32F_STENCIL8$1 = 0x8cad;
|
|
/* Framebuffer Object. */
|
|
|
|
const RGBA4$1 = 0x8056;
|
|
const RGB5_A1$1 = 0x8057;
|
|
const RGB565$1 = 0x8D62;
|
|
const DEPTH_COMPONENT16$1 = 0x81A5;
|
|
const STENCIL_INDEX = 0x1901;
|
|
const STENCIL_INDEX8 = 0x8D48;
|
|
const DEPTH_STENCIL$1 = 0x84F9;
|
|
const COLOR_ATTACHMENT0 = 0x8CE0;
|
|
const DEPTH_ATTACHMENT = 0x8D00;
|
|
const STENCIL_ATTACHMENT = 0x8D20;
|
|
const DEPTH_STENCIL_ATTACHMENT = 0x821A;
|
|
/* TextureWrapMode */
|
|
|
|
const CLAMP_TO_EDGE$1 = 0x812F;
|
|
/* TextureMagFilter */
|
|
|
|
const LINEAR$1 = 0x2601;
|
|
/**
|
|
* The options for a framebuffer attachment.
|
|
*
|
|
* Note: For a `format` that is a texture include all the texture
|
|
* options from {@link module:twgl.TextureOptions} for example
|
|
* `min`, `mag`, `clamp`, etc... Note that unlike {@link module:twgl.TextureOptions}
|
|
* `auto` defaults to `false` for attachment textures but `min` and `mag` default
|
|
* to `gl.LINEAR` and `wrap` defaults to `CLAMP_TO_EDGE`
|
|
*
|
|
* @typedef {Object} AttachmentOptions
|
|
* @property {number} [attachmentPoint] The attachment point. Defaults
|
|
* to `gl.COLOR_ATTACHMENT0 + ndx` unless type is a depth or stencil type
|
|
* then it's gl.DEPTH_ATTACHMENT or `gl.DEPTH_STENCIL_ATTACHMENT` depending
|
|
* on the format or attachment type.
|
|
* @property {number} [format] The format. If one of `gl.RGBA4`,
|
|
* `gl.RGB565`, `gl.RGB5_A1`, `gl.DEPTH_COMPONENT16`,
|
|
* `gl.STENCIL_INDEX8` or `gl.DEPTH_STENCIL` then will create a
|
|
* renderbuffer. Otherwise will create a texture. Default = `gl.RGBA`
|
|
* @property {number} [type] The type. Used for texture. Default = `gl.UNSIGNED_BYTE`.
|
|
* @property {number} [target] The texture target for `gl.framebufferTexture2D`.
|
|
* Defaults to `gl.TEXTURE_2D`. Set to appropriate face for cube maps.
|
|
* @property {number} [level] level for `gl.framebufferTexture2D`. Defaults to 0.
|
|
* @property {number} [layer] layer for `gl.framebufferTextureLayer`. Defaults to undefined.
|
|
* If set then `gl.framebufferTextureLayer` is called, if not then `gl.framebufferTexture2D`
|
|
* @property {WebGLObject} [attachment] An existing renderbuffer or texture.
|
|
* If provided will attach this Object. This allows you to share
|
|
* attachments across framebuffers.
|
|
* @memberOf module:twgl
|
|
* @mixes module:twgl.TextureOptions
|
|
*/
|
|
|
|
const defaultAttachments = [{
|
|
format: RGBA$1,
|
|
type: UNSIGNED_BYTE$3,
|
|
min: LINEAR$1,
|
|
wrap: CLAMP_TO_EDGE$1
|
|
}, {
|
|
format: DEPTH_STENCIL$1
|
|
}];
|
|
const attachmentsByFormat = {};
|
|
attachmentsByFormat[DEPTH_STENCIL$1] = DEPTH_STENCIL_ATTACHMENT;
|
|
attachmentsByFormat[STENCIL_INDEX] = STENCIL_ATTACHMENT;
|
|
attachmentsByFormat[STENCIL_INDEX8] = STENCIL_ATTACHMENT;
|
|
attachmentsByFormat[DEPTH_COMPONENT$1] = DEPTH_ATTACHMENT;
|
|
attachmentsByFormat[DEPTH_COMPONENT16$1] = DEPTH_ATTACHMENT;
|
|
attachmentsByFormat[DEPTH_COMPONENT24$1] = DEPTH_ATTACHMENT;
|
|
attachmentsByFormat[DEPTH_COMPONENT32F$1] = DEPTH_ATTACHMENT;
|
|
attachmentsByFormat[DEPTH24_STENCIL8$1] = DEPTH_STENCIL_ATTACHMENT;
|
|
attachmentsByFormat[DEPTH32F_STENCIL8$1] = DEPTH_STENCIL_ATTACHMENT;
|
|
|
|
function getAttachmentPointForFormat(format, internalFormat) {
|
|
return attachmentsByFormat[format] || attachmentsByFormat[internalFormat];
|
|
}
|
|
|
|
const renderbufferFormats = {};
|
|
renderbufferFormats[RGBA4$1] = true;
|
|
renderbufferFormats[RGB5_A1$1] = true;
|
|
renderbufferFormats[RGB565$1] = true;
|
|
renderbufferFormats[DEPTH_STENCIL$1] = true;
|
|
renderbufferFormats[DEPTH_COMPONENT16$1] = true;
|
|
renderbufferFormats[STENCIL_INDEX] = true;
|
|
renderbufferFormats[STENCIL_INDEX8] = true;
|
|
|
|
function isRenderbufferFormat(format) {
|
|
return renderbufferFormats[format];
|
|
}
|
|
/**
|
|
* @typedef {Object} FramebufferInfo
|
|
* @property {WebGLFramebuffer} framebuffer The WebGLFramebuffer for this framebufferInfo
|
|
* @property {WebGLObject[]} attachments The created attachments in the same order as passed in to {@link module:twgl.createFramebufferInfo}.
|
|
* @property {number} width The width of the framebuffer and its attachments
|
|
* @property {number} height The width of the framebuffer and its attachments
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* Creates a framebuffer and attachments.
|
|
*
|
|
* This returns a {@link module:twgl.FramebufferInfo} because it needs to return the attachments as well as the framebuffer.
|
|
*
|
|
* The simplest usage
|
|
*
|
|
* // create an RGBA/UNSIGNED_BYTE texture and DEPTH_STENCIL renderbuffer
|
|
* const fbi = twgl.createFramebufferInfo(gl);
|
|
*
|
|
* More complex usage
|
|
*
|
|
* // create an RGB565 renderbuffer and a STENCIL_INDEX8 renderbuffer
|
|
* const attachments = [
|
|
* { format: RGB565, mag: NEAREST },
|
|
* { format: STENCIL_INDEX8 },
|
|
* ]
|
|
* const fbi = twgl.createFramebufferInfo(gl, attachments);
|
|
*
|
|
* Passing in a specific size
|
|
*
|
|
* const width = 256;
|
|
* const height = 256;
|
|
* const fbi = twgl.createFramebufferInfo(gl, attachments, width, height);
|
|
*
|
|
* **Note!!** It is up to you to check if the framebuffer is renderable by calling `gl.checkFramebufferStatus`.
|
|
* [WebGL1 only guarantees 3 combinations of attachments work](https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.6).
|
|
*
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @param {module:twgl.AttachmentOptions[]} [attachments] which attachments to create. If not provided the default is a framebuffer with an
|
|
* `RGBA`, `UNSIGNED_BYTE` texture `COLOR_ATTACHMENT0` and a `DEPTH_STENCIL` renderbuffer `DEPTH_STENCIL_ATTACHMENT`.
|
|
* @param {number} [width] the width for the attachments. Default = size of drawingBuffer
|
|
* @param {number} [height] the height for the attachments. Default = size of drawingBuffer
|
|
* @return {module:twgl.FramebufferInfo} the framebuffer and attachments.
|
|
* @memberOf module:twgl/framebuffers
|
|
*/
|
|
|
|
|
|
function createFramebufferInfo(gl, attachments, width, height) {
|
|
const target = FRAMEBUFFER;
|
|
const fb = gl.createFramebuffer();
|
|
gl.bindFramebuffer(target, fb);
|
|
width = width || gl.drawingBufferWidth;
|
|
height = height || gl.drawingBufferHeight;
|
|
attachments = attachments || defaultAttachments;
|
|
let colorAttachmentCount = 0;
|
|
const framebufferInfo = {
|
|
framebuffer: fb,
|
|
attachments: [],
|
|
width: width,
|
|
height: height
|
|
};
|
|
attachments.forEach(function (attachmentOptions) {
|
|
let attachment = attachmentOptions.attachment;
|
|
const format = attachmentOptions.format;
|
|
let attachmentPoint = attachmentOptions.attachmentPoint || getAttachmentPointForFormat(format, attachmentOptions.internalFormat);
|
|
|
|
if (!attachmentPoint) {
|
|
attachmentPoint = COLOR_ATTACHMENT0 + colorAttachmentCount++;
|
|
}
|
|
|
|
if (!attachment) {
|
|
if (isRenderbufferFormat(format)) {
|
|
attachment = gl.createRenderbuffer();
|
|
gl.bindRenderbuffer(RENDERBUFFER, attachment);
|
|
gl.renderbufferStorage(RENDERBUFFER, format, width, height);
|
|
} else {
|
|
const textureOptions = Object.assign({}, attachmentOptions);
|
|
textureOptions.width = width;
|
|
textureOptions.height = height;
|
|
|
|
if (textureOptions.auto === undefined) {
|
|
textureOptions.auto = false;
|
|
textureOptions.min = textureOptions.min || textureOptions.minMag || LINEAR$1;
|
|
textureOptions.mag = textureOptions.mag || textureOptions.minMag || LINEAR$1;
|
|
textureOptions.wrapS = textureOptions.wrapS || textureOptions.wrap || CLAMP_TO_EDGE$1;
|
|
textureOptions.wrapT = textureOptions.wrapT || textureOptions.wrap || CLAMP_TO_EDGE$1;
|
|
}
|
|
|
|
attachment = createTexture(gl, textureOptions);
|
|
}
|
|
}
|
|
|
|
if (isRenderbuffer(gl, attachment)) {
|
|
gl.framebufferRenderbuffer(target, attachmentPoint, RENDERBUFFER, attachment);
|
|
} else if (isTexture(gl, attachment)) {
|
|
if (attachmentOptions.layer !== undefined) {
|
|
gl.framebufferTextureLayer(target, attachmentPoint, attachment, attachmentOptions.level || 0, attachmentOptions.layer);
|
|
} else {
|
|
gl.framebufferTexture2D(target, attachmentPoint, attachmentOptions.target || TEXTURE_2D$2, attachment, attachmentOptions.level || 0);
|
|
}
|
|
} else {
|
|
throw new Error('unknown attachment type');
|
|
}
|
|
|
|
framebufferInfo.attachments.push(attachment);
|
|
});
|
|
return framebufferInfo;
|
|
}
|
|
/**
|
|
* Resizes the attachments of a framebuffer.
|
|
*
|
|
* You need to pass in the same `attachments` as you passed in {@link module:twgl.createFramebufferInfo}
|
|
* because TWGL has no idea the format/type of each attachment.
|
|
*
|
|
* The simplest usage
|
|
*
|
|
* // create an RGBA/UNSIGNED_BYTE texture and DEPTH_STENCIL renderbuffer
|
|
* const fbi = twgl.createFramebufferInfo(gl);
|
|
*
|
|
* ...
|
|
*
|
|
* function render() {
|
|
* if (twgl.resizeCanvasToDisplaySize(gl.canvas)) {
|
|
* // resize the attachments
|
|
* twgl.resizeFramebufferInfo(gl, fbi);
|
|
* }
|
|
*
|
|
* More complex usage
|
|
*
|
|
* // create an RGB565 renderbuffer and a STENCIL_INDEX8 renderbuffer
|
|
* const attachments = [
|
|
* { format: RGB565, mag: NEAREST },
|
|
* { format: STENCIL_INDEX8 },
|
|
* ]
|
|
* const fbi = twgl.createFramebufferInfo(gl, attachments);
|
|
*
|
|
* ...
|
|
*
|
|
* function render() {
|
|
* if (twgl.resizeCanvasToDisplaySize(gl.canvas)) {
|
|
* // resize the attachments to match
|
|
* twgl.resizeFramebufferInfo(gl, fbi, attachments);
|
|
* }
|
|
*
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @param {module:twgl.FramebufferInfo} framebufferInfo a framebufferInfo as returned from {@link module:twgl.createFramebufferInfo}.
|
|
* @param {module:twgl.AttachmentOptions[]} [attachments] the same attachments options as passed to {@link module:twgl.createFramebufferInfo}.
|
|
* @param {number} [width] the width for the attachments. Default = size of drawingBuffer
|
|
* @param {number} [height] the height for the attachments. Default = size of drawingBuffer
|
|
* @memberOf module:twgl/framebuffers
|
|
*/
|
|
|
|
|
|
function resizeFramebufferInfo(gl, framebufferInfo, attachments, width, height) {
|
|
width = width || gl.drawingBufferWidth;
|
|
height = height || gl.drawingBufferHeight;
|
|
framebufferInfo.width = width;
|
|
framebufferInfo.height = height;
|
|
attachments = attachments || defaultAttachments;
|
|
attachments.forEach(function (attachmentOptions, ndx) {
|
|
const attachment = framebufferInfo.attachments[ndx];
|
|
const format = attachmentOptions.format;
|
|
|
|
if (isRenderbuffer(gl, attachment)) {
|
|
gl.bindRenderbuffer(RENDERBUFFER, attachment);
|
|
gl.renderbufferStorage(RENDERBUFFER, format, width, height);
|
|
} else if (isTexture(gl, attachment)) {
|
|
resizeTexture(gl, attachment, attachmentOptions, width, height);
|
|
} else {
|
|
throw new Error('unknown attachment type');
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* Binds a framebuffer
|
|
*
|
|
* This function pretty much solely exists because I spent hours
|
|
* trying to figure out why something I wrote wasn't working only
|
|
* to realize I forget to set the viewport dimensions.
|
|
* My hope is this function will fix that.
|
|
*
|
|
* It is effectively the same as
|
|
*
|
|
* gl.bindFramebuffer(gl.FRAMEBUFFER, someFramebufferInfo.framebuffer);
|
|
* gl.viewport(0, 0, someFramebufferInfo.width, someFramebufferInfo.height);
|
|
*
|
|
* @param {WebGLRenderingContext} gl the WebGLRenderingContext
|
|
* @param {module:twgl.FramebufferInfo|null} [framebufferInfo] a framebufferInfo as returned from {@link module:twgl.createFramebufferInfo}.
|
|
* If falsy will bind the canvas.
|
|
* @param {number} [target] The target. If not passed `gl.FRAMEBUFFER` will be used.
|
|
* @memberOf module:twgl/framebuffers
|
|
*/
|
|
|
|
|
|
function bindFramebufferInfo(gl, framebufferInfo, target) {
|
|
target = target || FRAMEBUFFER;
|
|
|
|
if (framebufferInfo) {
|
|
gl.bindFramebuffer(target, framebufferInfo.framebuffer);
|
|
gl.viewport(0, 0, framebufferInfo.width, framebufferInfo.height);
|
|
} else {
|
|
gl.bindFramebuffer(target, null);
|
|
gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
|
|
}
|
|
}
|
|
|
|
var framebuffers = /*#__PURE__*/Object.freeze({
|
|
__proto__: null,
|
|
bindFramebufferInfo: bindFramebufferInfo,
|
|
createFramebufferInfo: createFramebufferInfo,
|
|
resizeFramebufferInfo: resizeFramebufferInfo
|
|
});
|
|
/*
|
|
* Copyright 2019 Gregg Tavares
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
/**
|
|
* vertex array object related functions
|
|
*
|
|
* You should generally not need to use these functions. They are provided
|
|
* for those cases where you're doing something out of the ordinary
|
|
* and you need lower level access.
|
|
*
|
|
* For backward compatibility they are available at both `twgl.attributes` and `twgl`
|
|
* itself
|
|
*
|
|
* See {@link module:twgl} for core functions
|
|
*
|
|
* @module twgl/vertexArrays
|
|
*/
|
|
|
|
exports.framebuffers = framebuffers;
|
|
const ELEMENT_ARRAY_BUFFER$2 = 0x8893;
|
|
/**
|
|
* @typedef {Object} VertexArrayInfo
|
|
* @property {number} numElements The number of elements to pass to `gl.drawArrays` or `gl.drawElements`.
|
|
* @property {number} [elementType] The type of indices `UNSIGNED_BYTE`, `UNSIGNED_SHORT` etc..
|
|
* @property {WebGLVertexArrayObject} [vertexArrayObject] a vertex array object
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* Creates a VertexArrayInfo from a BufferInfo and one or more ProgramInfos
|
|
*
|
|
* This can be passed to {@link module:twgl.setBuffersAndAttributes} and to
|
|
* {@link module:twgl:drawBufferInfo}.
|
|
*
|
|
* > **IMPORTANT:** Vertex Array Objects are **not** a direct analog for a BufferInfo. Vertex Array Objects
|
|
* assign buffers to specific attributes at creation time. That means they can only be used with programs
|
|
* who's attributes use the same attribute locations for the same purposes.
|
|
*
|
|
* > Bind your attribute locations by passing an array of attribute names to {@link module:twgl.createProgramInfo}
|
|
* or use WebGL 2's GLSL ES 3's `layout(location = <num>)` to make sure locations match.
|
|
*
|
|
* also
|
|
*
|
|
* > **IMPORTANT:** After calling twgl.setBuffersAndAttribute with a BufferInfo that uses a Vertex Array Object
|
|
* that Vertex Array Object will be bound. That means **ANY MANIPULATION OF ELEMENT_ARRAY_BUFFER or ATTRIBUTES**
|
|
* will affect the Vertex Array Object state.
|
|
*
|
|
* > Call `gl.bindVertexArray(null)` to get back manipulating the global attributes and ELEMENT_ARRAY_BUFFER.
|
|
*
|
|
* @param {WebGLRenderingContext} gl A WebGLRenderingContext
|
|
* @param {module:twgl.ProgramInfo|module:twgl.ProgramInfo[]} programInfo a programInfo or array of programInfos
|
|
* @param {module:twgl.BufferInfo} bufferInfo BufferInfo as returned from createBufferInfoFromArrays etc...
|
|
*
|
|
* You need to make sure every attribute that will be used is bound. So for example assume shader 1
|
|
* uses attributes A, B, C and shader 2 uses attributes A, B, D. If you only pass in the programInfo
|
|
* for shader 1 then only attributes A, B, and C will have their attributes set because TWGL doesn't
|
|
* now attribute D's location.
|
|
*
|
|
* So, you can pass in both shader 1 and shader 2's programInfo
|
|
*
|
|
* @return {module:twgl.VertexArrayInfo} The created VertexArrayInfo
|
|
*
|
|
* @memberOf module:twgl/vertexArrays
|
|
*/
|
|
|
|
function createVertexArrayInfo(gl, programInfos, bufferInfo) {
|
|
const vao = gl.createVertexArray();
|
|
gl.bindVertexArray(vao);
|
|
|
|
if (!programInfos.length) {
|
|
programInfos = [programInfos];
|
|
}
|
|
|
|
programInfos.forEach(function (programInfo) {
|
|
setBuffersAndAttributes(gl, programInfo, bufferInfo);
|
|
});
|
|
gl.bindVertexArray(null);
|
|
return {
|
|
numElements: bufferInfo.numElements,
|
|
elementType: bufferInfo.elementType,
|
|
vertexArrayObject: vao
|
|
};
|
|
}
|
|
/**
|
|
* Creates a vertex array object and then sets the attributes on it
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
|
|
* @param {Object.<string, function>} setters Attribute setters as returned from createAttributeSetters
|
|
* @param {Object.<string, module:twgl.AttribInfo>} attribs AttribInfos mapped by attribute name.
|
|
* @param {WebGLBuffer} [indices] an optional ELEMENT_ARRAY_BUFFER of indices
|
|
* @memberOf module:twgl/vertexArrays
|
|
*/
|
|
|
|
|
|
function createVAOAndSetAttributes(gl, setters, attribs, indices) {
|
|
const vao = gl.createVertexArray();
|
|
gl.bindVertexArray(vao);
|
|
setAttributes(setters, attribs);
|
|
|
|
if (indices) {
|
|
gl.bindBuffer(ELEMENT_ARRAY_BUFFER$2, indices);
|
|
} // We unbind this because otherwise any change to ELEMENT_ARRAY_BUFFER
|
|
// like when creating buffers for other stuff will mess up this VAO's binding
|
|
|
|
|
|
gl.bindVertexArray(null);
|
|
return vao;
|
|
}
|
|
/**
|
|
* Creates a vertex array object and then sets the attributes
|
|
* on it
|
|
*
|
|
* @param {WebGLRenderingContext} gl The WebGLRenderingContext
|
|
* to use.
|
|
* @param {Object.<string, function>| module:twgl.ProgramInfo} programInfo as returned from createProgramInfo or Attribute setters as returned from createAttributeSetters
|
|
* @param {module:twgl.BufferInfo} bufferInfo BufferInfo as returned from createBufferInfoFromArrays etc...
|
|
* @param {WebGLBuffer} [indices] an optional ELEMENT_ARRAY_BUFFER of indices
|
|
* @memberOf module:twgl/vertexArrays
|
|
*/
|
|
|
|
|
|
function createVAOFromBufferInfo(gl, programInfo, bufferInfo) {
|
|
return createVAOAndSetAttributes(gl, programInfo.attribSetters || programInfo, bufferInfo.attribs, bufferInfo.indices);
|
|
}
|
|
|
|
var vertexArrays = /*#__PURE__*/Object.freeze({
|
|
__proto__: null,
|
|
createVertexArrayInfo: createVertexArrayInfo,
|
|
createVAOAndSetAttributes: createVAOAndSetAttributes,
|
|
createVAOFromBufferInfo: createVAOFromBufferInfo
|
|
});
|
|
/*
|
|
* Copyright 2019 Gregg Tavares
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
exports.vertexArrays = vertexArrays;
|
|
const defaults$2 = {
|
|
addExtensionsToContext: true
|
|
};
|
|
/**
|
|
* Various default settings for twgl.
|
|
*
|
|
* Note: You can call this any number of times. Example:
|
|
*
|
|
* twgl.setDefaults({ textureColor: [1, 0, 0, 1] });
|
|
* twgl.setDefaults({ attribPrefix: 'a_' });
|
|
*
|
|
* is equivalent to
|
|
*
|
|
* twgl.setDefaults({
|
|
* textureColor: [1, 0, 0, 1],
|
|
* attribPrefix: 'a_',
|
|
* });
|
|
*
|
|
* @typedef {Object} Defaults
|
|
* @property {string} [attribPrefix] The prefix to stick on attributes
|
|
*
|
|
* When writing shaders I prefer to name attributes with `a_`, uniforms with `u_` and varyings with `v_`
|
|
* as it makes it clear where they came from. But, when building geometry I prefer using un-prefixed names.
|
|
*
|
|
* In other words I'll create arrays of geometry like this
|
|
*
|
|
* const arrays = {
|
|
* position: ...
|
|
* normal: ...
|
|
* texcoord: ...
|
|
* };
|
|
*
|
|
* But need those mapped to attributes and my attributes start with `a_`.
|
|
*
|
|
* Default: `""`
|
|
*
|
|
* @property {number[]} [textureColor] Array of 4 values in the range 0 to 1
|
|
*
|
|
* The default texture color is used when loading textures from
|
|
* urls. Because the URL will be loaded async we'd like to be
|
|
* able to use the texture immediately. By putting a 1x1 pixel
|
|
* color in the texture we can start using the texture before
|
|
* the URL has loaded.
|
|
*
|
|
* Default: `[0.5, 0.75, 1, 1]`
|
|
*
|
|
* @property {string} [crossOrigin]
|
|
*
|
|
* If not undefined sets the crossOrigin attribute on images
|
|
* that twgl creates when downloading images for textures.
|
|
*
|
|
* Also see {@link module:twgl.TextureOptions}.
|
|
*
|
|
* @property {bool} [addExtensionsToContext]
|
|
*
|
|
* If true, then, when twgl will try to add any supported WebGL extensions
|
|
* directly to the context under their normal GL names. For example
|
|
* if ANGLE_instances_arrays exists then twgl would enable it,
|
|
* add the functions `vertexAttribDivisor`, `drawArraysInstanced`,
|
|
* `drawElementsInstanced`, and the constant `VERTEX_ATTRIB_ARRAY_DIVISOR`
|
|
* to the `WebGLRenderingContext`.
|
|
*
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
/**
|
|
* Sets various defaults for twgl.
|
|
*
|
|
* In the interest of terseness which is kind of the point
|
|
* of twgl I've integrated a few of the older functions here
|
|
*
|
|
* @param {module:twgl.Defaults} newDefaults The default settings.
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
function setDefaults$2(newDefaults) {
|
|
copyExistingProperties(newDefaults, defaults$2);
|
|
setDefaults(newDefaults); // eslint-disable-line
|
|
|
|
setDefaults$1(newDefaults); // eslint-disable-line
|
|
}
|
|
|
|
const prefixRE = /^(.*?)_/;
|
|
|
|
function addExtensionToContext(gl, extensionName) {
|
|
glEnumToString(gl, 0);
|
|
const ext = gl.getExtension(extensionName);
|
|
|
|
if (ext) {
|
|
const enums = {};
|
|
const fnSuffix = prefixRE.exec(extensionName)[1];
|
|
const enumSuffix = '_' + fnSuffix;
|
|
|
|
for (const key in ext) {
|
|
const value = ext[key];
|
|
const isFunc = typeof value === 'function';
|
|
const suffix = isFunc ? fnSuffix : enumSuffix;
|
|
let name = key; // examples of where this is not true are WEBGL_compressed_texture_s3tc
|
|
// and WEBGL_compressed_texture_pvrtc
|
|
|
|
if (key.endsWith(suffix)) {
|
|
name = key.substring(0, key.length - suffix.length);
|
|
}
|
|
|
|
if (gl[name] !== undefined) {
|
|
if (!isFunc && gl[name] !== value) {
|
|
warn(name, gl[name], value, key);
|
|
}
|
|
} else {
|
|
if (isFunc) {
|
|
gl[name] = function (origFn) {
|
|
return function () {
|
|
return origFn.apply(ext, arguments);
|
|
};
|
|
}(value);
|
|
} else {
|
|
gl[name] = value;
|
|
enums[name] = value;
|
|
}
|
|
}
|
|
} // pass the modified enums to glEnumToString
|
|
|
|
|
|
enums.constructor = {
|
|
name: ext.constructor.name
|
|
};
|
|
glEnumToString(enums, 0);
|
|
}
|
|
|
|
return ext;
|
|
}
|
|
/*
|
|
* If you're wondering why the code doesn't just iterate
|
|
* over all extensions using `gl.getExtensions` is that it's possible
|
|
* some future extension is incompatible with this code. Rather than
|
|
* have thing suddenly break it seems better to manually add to this
|
|
* list.
|
|
*
|
|
*/
|
|
|
|
|
|
const supportedExtensions = ['ANGLE_instanced_arrays', 'EXT_blend_minmax', 'EXT_color_buffer_float', 'EXT_color_buffer_half_float', 'EXT_disjoint_timer_query', 'EXT_disjoint_timer_query_webgl2', 'EXT_frag_depth', 'EXT_sRGB', 'EXT_shader_texture_lod', 'EXT_texture_filter_anisotropic', 'OES_element_index_uint', 'OES_standard_derivatives', 'OES_texture_float', 'OES_texture_float_linear', 'OES_texture_half_float', 'OES_texture_half_float_linear', 'OES_vertex_array_object', 'WEBGL_color_buffer_float', 'WEBGL_compressed_texture_atc', 'WEBGL_compressed_texture_etc1', 'WEBGL_compressed_texture_pvrtc', 'WEBGL_compressed_texture_s3tc', 'WEBGL_compressed_texture_s3tc_srgb', 'WEBGL_depth_texture', 'WEBGL_draw_buffers'];
|
|
/**
|
|
* Attempts to enable all of the following extensions
|
|
* and add their functions and constants to the
|
|
* `WebGLRenderingContext` using their normal non-extension like names.
|
|
*
|
|
* ANGLE_instanced_arrays
|
|
* EXT_blend_minmax
|
|
* EXT_color_buffer_float
|
|
* EXT_color_buffer_half_float
|
|
* EXT_disjoint_timer_query
|
|
* EXT_disjoint_timer_query_webgl2
|
|
* EXT_frag_depth
|
|
* EXT_sRGB
|
|
* EXT_shader_texture_lod
|
|
* EXT_texture_filter_anisotropic
|
|
* OES_element_index_uint
|
|
* OES_standard_derivatives
|
|
* OES_texture_float
|
|
* OES_texture_float_linear
|
|
* OES_texture_half_float
|
|
* OES_texture_half_float_linear
|
|
* OES_vertex_array_object
|
|
* WEBGL_color_buffer_float
|
|
* WEBGL_compressed_texture_atc
|
|
* WEBGL_compressed_texture_etc1
|
|
* WEBGL_compressed_texture_pvrtc
|
|
* WEBGL_compressed_texture_s3tc
|
|
* WEBGL_compressed_texture_s3tc_srgb
|
|
* WEBGL_depth_texture
|
|
* WEBGL_draw_buffers
|
|
*
|
|
* For example if `ANGLE_instanced_arrays` exists then the functions
|
|
* `drawArraysInstanced`, `drawElementsInstanced`, `vertexAttribDivisor`
|
|
* and the constant `VERTEX_ATTRIB_ARRAY_DIVISOR` are added to the
|
|
* `WebGLRenderingContext`.
|
|
*
|
|
* Note that if you want to know if the extension exists you should
|
|
* probably call `gl.getExtension` for each extension. Alternatively
|
|
* you can check for the existence of the functions or constants that
|
|
* are expected to be added. For example
|
|
*
|
|
* if (gl.drawBuffers) {
|
|
* // Either WEBGL_draw_buffers was enabled OR you're running in WebGL2
|
|
* ....
|
|
*
|
|
* @param {WebGLRenderingContext} gl A WebGLRenderingContext
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
function addExtensionsToContext(gl) {
|
|
for (let ii = 0; ii < supportedExtensions.length; ++ii) {
|
|
addExtensionToContext(gl, supportedExtensions[ii]);
|
|
}
|
|
}
|
|
/**
|
|
* Creates a webgl context.
|
|
* @param {HTMLCanvasElement} canvas The canvas tag to get
|
|
* context from. If one is not passed in one will be
|
|
* created.
|
|
* @return {WebGLRenderingContext} The created context.
|
|
* @private
|
|
*/
|
|
|
|
|
|
function create3DContext(canvas, opt_attribs) {
|
|
const names = ["webgl", "experimental-webgl"];
|
|
let context = null;
|
|
|
|
for (let ii = 0; ii < names.length; ++ii) {
|
|
context = canvas.getContext(names[ii], opt_attribs);
|
|
|
|
if (context) {
|
|
if (defaults$2.addExtensionsToContext) {
|
|
addExtensionsToContext(context);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return context;
|
|
}
|
|
/**
|
|
* Gets a WebGL1 context.
|
|
*
|
|
* Note: Will attempt to enable Vertex Array Objects
|
|
* and add WebGL2 entry points. (unless you first set defaults with
|
|
* `twgl.setDefaults({enableVertexArrayObjects: false})`;
|
|
*
|
|
* @param {HTMLCanvasElement} canvas a canvas element.
|
|
* @param {WebGLContextAttributes} [opt_attribs] optional webgl context creation attributes
|
|
* @return {WebGLRenderingContext} The created context.
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
|
|
function getWebGLContext(canvas, opt_attribs) {
|
|
const gl = create3DContext(canvas, opt_attribs);
|
|
return gl;
|
|
}
|
|
/**
|
|
* Creates a webgl context.
|
|
*
|
|
* Will return a WebGL2 context if possible.
|
|
*
|
|
* You can check if it's WebGL2 with
|
|
*
|
|
* twgl.isWebGL2(gl);
|
|
*
|
|
* @param {HTMLCanvasElement} canvas The canvas tag to get
|
|
* context from. If one is not passed in one will be
|
|
* created.
|
|
* @return {WebGLRenderingContext} The created context.
|
|
*/
|
|
|
|
|
|
function createContext(canvas, opt_attribs) {
|
|
const names = ["webgl2", "webgl", "experimental-webgl"];
|
|
let context = null;
|
|
|
|
for (let ii = 0; ii < names.length; ++ii) {
|
|
context = canvas.getContext(names[ii], opt_attribs);
|
|
|
|
if (context) {
|
|
if (defaults$2.addExtensionsToContext) {
|
|
addExtensionsToContext(context);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return context;
|
|
}
|
|
/**
|
|
* Gets a WebGL context. Will create a WebGL2 context if possible.
|
|
*
|
|
* You can check if it's WebGL2 with
|
|
*
|
|
* function isWebGL2(gl) {
|
|
* return gl.getParameter(gl.VERSION).indexOf("WebGL 2.0 ") == 0;
|
|
* }
|
|
*
|
|
* Note: For a WebGL1 context will attempt to enable Vertex Array Objects
|
|
* and add WebGL2 entry points. (unless you first set defaults with
|
|
* `twgl.setDefaults({enableVertexArrayObjects: false})`;
|
|
*
|
|
* @param {HTMLCanvasElement} canvas a canvas element.
|
|
* @param {WebGLContextAttributes} [opt_attribs] optional webgl context creation attributes
|
|
* @return {WebGLRenderingContext} The created context.
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
|
|
function getContext(canvas, opt_attribs) {
|
|
const gl = createContext(canvas, opt_attribs);
|
|
return gl;
|
|
}
|
|
/**
|
|
* Resize a canvas to match the size it's displayed.
|
|
* @param {HTMLCanvasElement} canvas The canvas to resize.
|
|
* @param {number} [multiplier] So you can pass in `window.devicePixelRatio` or other scale value if you want to.
|
|
* @return {boolean} true if the canvas was resized.
|
|
* @memberOf module:twgl
|
|
*/
|
|
|
|
|
|
function resizeCanvasToDisplaySize(canvas, multiplier) {
|
|
multiplier = multiplier || 1;
|
|
multiplier = Math.max(0, multiplier);
|
|
const width = canvas.clientWidth * multiplier | 0;
|
|
const height = canvas.clientHeight * multiplier | 0;
|
|
|
|
if (canvas.width !== width || canvas.height !== height) {
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
},{}]},{},[1])(1)
|
|
});
|