tgaimage
Version:
TGA Image Loader for Web browsers
634 lines (589 loc) • 18.9 kB
JavaScript
module.exports =
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
/*global Buffer*/
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var _ImageType = {
noImage: 0,
colorMapped: 1,
RGB: 2,
blackAndWhite: 3,
runlengthColorMapped: 9,
runlengthRGB: 10,
compressedBlackAndWhite: 11,
compressedColorMapped: 32,
compressed4PassQTColorMapped: 33
};
var _headerLength = 18;
var TGAImage = function () {
/**
* constructor
* @param {Buffer|ArrayBuffer} data -
* @constructor
*/
function TGAImage(data) {
var _this = this;
_classCallCheck(this, TGAImage);
if (data instanceof Buffer) {
this._buffer = data;
} else if (typeof data === 'string') {
this._buffer = Buffer.from(data, 'binary');
} else if (data) {
this._buffer = Buffer.from(data);
} else {
this._buffer = null;
}
// Header
this._idLength = 0;
this._colorMapType = 0;
this._imageType = 0;
this._colorMapOrigin = 0;
this._colorMapLength = 0;
this._colorMapDepth = 0;
this._imageXOrigin = 0;
this._imageYOrigin = 0;
this._imageWidth = 0;
this._imageHeight = 0;
this._imageDepth = 0;
this._alphaDepth = 0;
this._leftToRight = true;
this._topToBottom = false;
this._interleave = false;
this._hasAlpha = false;
// Image Identification Field
this._imageID = null;
// Image Data
this._canvas = null;
this._context = null;
this._imageData = null;
this._image = null;
// for HTML Image tag compatibility
this._src = null;
this.onload = null;
this.onerror = null;
this._resolveFunc = null;
this._rejectFunc = null;
this._didLoad = new Promise(function (resolve, reject) {
_this._resolveFunc = resolve;
_this._rejectFunc = reject;
});
if (data) {
this._parseData();
}
}
_createClass(TGAImage, [{
key: '_loadURL',
value: function _loadURL(url) {
var _this2 = this;
this._src = url;
this._requestBinaryFile(url).then(function (data) {
_this2._buffer = Buffer.from(data);
_this2._parseData();
}).catch(function (error) {
_this2._reject(error);
});
}
}, {
key: '_requestBinaryFile',
value: function _requestBinaryFile(url) {
return new Promise(function (resolve, reject) {
var request = new XMLHttpRequest();
request.open('GET', url);
request.responseType = 'arraybuffer';
request.onload = function (ev) {
if (request.response) {
resolve(request.response);
} else {
reject(request);
}
};
request.onerror = function (ev) {
reject(ev);
};
request.send(null);
});
}
}, {
key: '_parseData',
value: function _parseData() {
this._readHeader();
this._readImageID();
this._initImage();
var data = this._getImageData();
switch (this._imageType) {
case _ImageType.noImage:
{
// nothing to do
break;
}
case _ImageType.colorMapped:
{
this._parseColorMapData(data);
break;
}
case _ImageType.RGB:
{
this._parseRGBData(data);
break;
}
case _ImageType.blackAndWhite:
{
this._parseBlackAndWhiteData(data);
break;
}
case _ImageType.runlengthColorMapped:
{
this._parseColorMapData(data);
break;
}
case _ImageType.runlengthRGB:
{
this._parseRGBData(data);
break;
}
case _ImageType.compressedBlackAndWhite:
{
this._parseBlackAndWhiteData(data);
break;
}
case _ImageType.compressedColorMapped:
{
console.error('parser for compressed TGA is not implemeneted');
break;
}
case _ImageType.compressed4PassQTColorMapped:
{
console.error('parser for compressed TGA is not implemeneted');
break;
}
default:
{
throw new Error('unknown imageType: ' + this._imageType);
}
}
this._setImage();
this._deleteBuffer();
}
}, {
key: '_readHeader',
value: function _readHeader() {
this._idLength = this._buffer.readUIntLE(0, 1);
this._colorMapType = this._buffer.readUIntLE(1, 1);
this._imageType = this._buffer.readUIntLE(2, 1);
this._colorMapOrigin = this._buffer.readUIntLE(3, 2);
this._colorMapLength = this._buffer.readUIntLE(5, 2);
this._colorMapDepth = this._buffer.readUIntLE(7, 1);
this._imageXOrigin = this._buffer.readUIntLE(8, 2);
this._imageYOrigin = this._buffer.readUIntLE(10, 2);
this._imageWidth = this._buffer.readUIntLE(12, 2);
this._imageHeight = this._buffer.readUIntLE(14, 2);
this._imageDepth = this._buffer.readUIntLE(16, 1);
var descriptor = this._buffer.readUIntLE(17, 1);
this._alphaDepth = descriptor & 0x0F;
this._leftToRight = (descriptor & 0x10) === 0;
this._topToBottom = (descriptor & 0x20) > 0;
this._interleave = descriptor & 0xC0;
}
}, {
key: '_readImageID',
value: function _readImageID() {
if (this._idLength > 0) {
this._imageID = this._buffer.subarray(_headerLength, this._idLength);
}
}
}, {
key: '_initImage',
value: function _initImage() {
if (this._imageType === _ImageType.noImage) {
return;
}
if (this._imageWidth <= 0 || this._imageHeight <= 0) {
return;
}
this._canvas = document.createElement('canvas');
this._canvas.width = this._imageWidth;
this._canvas.height = this._imageHeight;
this._context = this._canvas.getContext('2d');
this._imageData = this._context.createImageData(this._imageWidth, this._imageHeight);
}
}, {
key: '_setImage',
value: function _setImage() {
var _this3 = this;
this._context.putImageData(this._imageData, 0, 0);
this._image = new Image();
this._image.width = this._imageWidth;
this._image.height = this._imageHeight;
this._image.onload = function () {
_this3._resolve();
};
this._image.src = this._canvas.toDataURL();
}
}, {
key: '_deleteBuffer',
value: function _deleteBuffer() {
if (this._buffer) {
delete this._buffer;
this._buffer = null;
}
if (this._imageData) {
delete this._imageData;
this._imageData = null;
}
}
}, {
key: '_parseColorMapData',
value: function _parseColorMapData(buf) {
if (this._colorMapDepth === 24 || this._colorMapDepth === 16 || this._colorMapDepth === 15) {
this._hasAlpha = false;
} else if (this._colorMapDepth === 32) {
this._hasAlpha = true;
} else {
throw new Error('unknown colorMapDepth: ' + this._colorMapDepth);
}
var colorMapDataPos = _headerLength + this._idLength;
var colorMapDataSize = Math.ceil(this._colorMapDepth / 8);
var colorMapDataLen = colorMapDataSize * this._colorMapLength;
var imageDataSize = 1;
var colorMap = [];
var pos = colorMapDataPos;
for (var i = 0; i < this._colorMapLength; i++) {
var rgba = this._getRGBA(this._buffer, pos, this._colorMapDepth);
colorMap.push(rgba);
pos += colorMapDataSize;
}
var data = this._imageData.data;
var initX = 0;
var initY = 0;
var xStep = 1;
var yStep = 1;
if (!this._leftToRight) {
initX = this._imageWidth - 1;
xStep = -1;
}
if (!this._topToBottom) {
initY = this._imageHeight - 1;
yStep = -1;
}
pos = 0;
var y = initY;
var defaultColor = [0xFF, 0xFF, 0xFF, 0xFF];
for (var iy = 0; iy < this._imageHeight; iy++) {
var x = initX;
for (var ix = 0; ix < this._imageWidth; ix++) {
var index = (y * this._imageWidth + x) * 4;
var color = defaultColor;
var mapNo = buf[pos] - this._colorMapOrigin;
if (mapNo >= 0) {
color = colorMap[mapNo];
}
data[index] = color[0];
data[index + 1] = color[1];
data[index + 2] = color[2];
data[index + 3] = color[3];
x += xStep;
pos += imageDataSize;
}
y += yStep;
}
}
}, {
key: '_parseRGBData',
value: function _parseRGBData(buf) {
if (this._imageDepth === 24 || this._imageDepth === 16 || this._imageDepth === 15) {
this._hasAlpha = false;
} else if (this._imageDepth === 32) {
this._hasAlpha = true;
} else {
throw new Error('unknown imageDepth: ' + this._imageDepth);
}
var imageDataSize = Math.ceil(this._imageDepth / 8);
var data = this._imageData.data;
var initX = 0;
var initY = 0;
var xStep = 1;
var yStep = 1;
if (!this._leftToRight) {
initX = this._imageWidth - 1;
xStep = -1;
}
if (!this._topToBottom) {
initY = this._imageHeight - 1;
yStep = -1;
}
var pos = 0;
var y = initY;
for (var iy = 0; iy < this._imageHeight; iy++) {
var x = initX;
for (var ix = 0; ix < this._imageWidth; ix++) {
var index = (y * this._imageWidth + x) * 4;
var rgba = this._getRGBA(buf, pos, this._imageDepth);
data[index] = rgba[0];
data[index + 1] = rgba[1];
data[index + 2] = rgba[2];
data[index + 3] = rgba[3];
x += xStep;
pos += imageDataSize;
}
y += yStep;
}
}
}, {
key: '_getRGBA',
value: function _getRGBA(buf, offset, depth) {
if (depth === 15) {
var r = (buf[offset + 1] & 0x7c) << 1;
var g = (buf[offset + 1] & 0x03) << 6 | (buf[offset] & 0xe0) >> 2;
var b = (buf[offset] & 0x1f) << 3;
//const a = (buf[offset+1] & 0x80) > 0 ? 255 : 0
var a = 255;
return [r, g, b, a];
} else if (depth === 16) {
var _r = (buf[offset + 1] & 0x7c) << 1;
var _g = (buf[offset + 1] & 0x03) << 6 | (buf[offset] & 0xe0) >> 2;
var _b = (buf[offset] & 0x1f) << 3;
var _a = 255;
return [_r, _g, _b, _a];
} else if (depth === 24) {
return [buf[offset + 2], buf[offset + 1], buf[offset], 255];
} else if (depth === 32) {
return [buf[offset + 2], buf[offset + 1], buf[offset], buf[offset + 3]];
}
throw new Error('unsupported imageDepth: ' + depth);
}
}, {
key: '_parseBlackAndWhiteData',
value: function _parseBlackAndWhiteData(buf) {
if (this._imageDepth == 8) {
this._hasAlpha = false;
} else if (this._imageDepth == 16) {
this._hasAlpha = true;
} else {
throw new Error('unknown imageDepth: ' + this._imageDepth);
}
var imageDataSize = this._imageDepth / 8;
var data = this._imageData.data;
var initX = 0;
var initY = 0;
var xStep = 1;
var yStep = 1;
if (!this._leftToRight) {
initX = this._imageWidth - 1;
xStep = -1;
}
if (!this._topToBottom) {
initY = this._imageHeight - 1;
yStep = -1;
}
var pos = 0;
if (this._hasAlpha) {
var y = initY;
for (var iy = 0; iy < this._imageHeight; iy++) {
var x = initX;
for (var ix = 0; ix < this._imageWidth; ix++) {
var index = (y * this._imageWidth + x) * 4;
var c = buf[pos];
var a = buf[pos + 1];
data[index] = c;
data[index + 1] = c;
data[index + 2] = c;
data[index + 3] = a;
x += xStep;
pos += imageDataSize;
}
y += yStep;
}
} else {
var _y = initY;
for (var _iy = 0; _iy < this._imageHeight; _iy++) {
var _x = initX;
for (var _ix = 0; _ix < this._imageWidth; _ix++) {
var _index = (_y * this._imageWidth + _x) * 4;
var _c = buf[pos];
var _a2 = 255;
data[_index] = _c;
data[_index + 1] = _c;
data[_index + 2] = _c;
data[_index + 3] = _a2;
_x += xStep;
pos += imageDataSize;
}
_y += yStep;
}
}
}
}, {
key: '_getImageData',
value: function _getImageData() {
var data = null;
if (this._imageType !== _ImageType.none) {
var colorMapDataLen = Math.ceil(this._colorMapDepth / 8) * this._colorMapLength;
var start = _headerLength + this._idLength + colorMapDataLen;
data = this._buffer.subarray(start);
}
if (this._imageType === _ImageType.runlengthColorMapped || this._imageType === _ImageType.runlengthRGB) {
data = this._decompressRunlengthData(data);
} else if (this._imageType === _ImageType.compressedBlackAndWhite) {
data = this._decompressRunlengthData(data);
} else if (this._imageType === _ImageType.compressedColorMapped) {
// TODO: implement
console.error('Compressed Color Mapped TGA Image data is not supported');
} else if (this._imageType === _ImageType.compressed4PassQTColorMapped) {
// TODO: implement
console.error('Compressed Color Mapped TGA Image data is not supported');
}
return data;
}
}, {
key: '_decompressRunlengthData',
value: function _decompressRunlengthData(data) {
var d = [];
var elementCount = Math.ceil(this._imageDepth / 8);
var dataLength = elementCount * this._imageWidth * this._imageHeight;
var pos = 0;
while (d.length < dataLength) {
var packet = data[pos];
pos += 1;
if ((packet & 0x80) !== 0) {
// RLE
var elements = data.slice(pos, pos + elementCount);
pos += elementCount;
var count = (packet & 0x7F) + 1;
for (var i = 0; i < count; i++) {
d.push.apply(d, _toConsumableArray(elements));
}
} else {
// RAW
var len = (packet + 1) * elementCount;
d.push.apply(d, _toConsumableArray(data.slice(pos, pos + len)));
pos += len;
}
}
return d;
}
}, {
key: '_resolve',
value: function _resolve(e) {
if (this.onload) {
this.onload(e);
}
this._resolveFunc(e);
}
}, {
key: '_reject',
value: function _reject(e) {
if (this.onerror) {
this.onerror(e);
}
this._rejectFunc(e);
}
}, {
key: 'image',
get: function get() {
return this._image;
}
}, {
key: 'canvas',
get: function get() {
return this._canvas;
}
}, {
key: 'didLoad',
get: function get() {
return this._didLoad;
}
}, {
key: 'src',
get: function get() {
return this._src;
},
set: function set(newValue) {
this._loadURL(newValue);
}
}], [{
key: 'imageWithData',
value: function imageWithData(data) {
return new TGAImage(data);
}
}, {
key: 'imageWithURL',
value: function imageWithURL(url) {
var image = new TGAImage();
image._loadURL(url);
return image;
}
}]);
return TGAImage;
}();
module.exports = TGAImage;
/***/ })
/******/ ]);