UNPKG

image-in-browser

Version:

Package for encoding / decoding images, transforming images, applying filters, drawing primitives on images on the client side (no need for server Node.js)

285 lines 10.5 kB
import { InputBuffer } from '../common/input-buffer.js'; import { MemoryImage } from '../image/image.js'; import { ImageFormat } from './image-format.js'; import { TgaImageType } from './tga/tga-image-type.js'; import { TgaInfo } from './tga/tga-info.js'; export class TgaDecoder { constructor() { this._input = undefined; this._info = undefined; } get format() { return ImageFormat.tga; } get numFrames() { return this._info !== undefined ? 1 : 0; } decodeColorMap(colorMap, palette) { if (this._info === undefined || this._input === undefined) { return; } const cm = new InputBuffer({ buffer: colorMap, }); if (this._info.colorMapDepth === 16) { const color = this._input.readUint16(); const r = (color & 0x7c00) >>> 7; const g = (color & 0x3e0) >>> 2; const b = (color & 0x1f) << 3; const a = (color & 0x8000) !== 0 ? 0 : 255; for (let i = 0; i < this._info.colorMapLength; ++i) { palette.setRed(i, r); palette.setGreen(i, g); palette.setBlue(i, b); palette.setAlpha(i, a); } } else { const hasAlpha = this._info.colorMapDepth === 32; for (let i = 0; i < this._info.colorMapLength; ++i) { const b = cm.read(); const g = cm.read(); const r = cm.read(); const a = hasAlpha ? cm.read() : 255; palette.setRed(i, r); palette.setGreen(i, g); palette.setBlue(i, b); palette.setAlpha(i, a); } } } decodeRle() { if (this._info === undefined || this._input === undefined) { return undefined; } const bpp = this._info.pixelDepth; const hasAlpha = bpp === 16 || bpp === 32; const image = new MemoryImage({ width: this._info.width, height: this._info.height, numChannels: hasAlpha ? 4 : 3, withPalette: this._info.hasColorMap, }); const rleBit = 0x80; const rleMask = 0x7f; if (image.palette !== undefined && this._info.colorMap !== undefined) { this.decodeColorMap(this._info.colorMap, image.palette); } const w = image.width; const h = image.height; let y = h - 1; let x = 0; while (!this._input.isEOS && y >= 0) { const c = this._input.read(); const count = (c & rleMask) + 1; if ((c & rleBit) !== 0) { if (bpp === 8) { const r = this._input.read(); for (let i = 0; i < count; ++i) { image.setPixelR(x++, y, r); if (x >= w) { x = 0; y--; if (y < 0) { break; } } } } else if (bpp === 16) { const color = this._input.readUint16(); const r = (color & 0x7c00) >>> 7; const g = (color & 0x3e0) >>> 2; const b = (color & 0x1f) << 3; const a = (color & 0x8000) !== 0 ? 0 : 255; for (let i = 0; i < count; ++i) { image.setPixelRgba(x++, y, r, g, b, a); if (x >= w) { x = 0; y--; if (y < 0) { break; } } } } else { const b = this._input.read(); const g = this._input.read(); const r = this._input.read(); const a = hasAlpha ? this._input.read() : 255; for (let i = 0; i < count; ++i) { image.setPixelRgba(x++, y, r, g, b, a); if (x >= w) { x = 0; y--; if (y < 0) { break; } } } } } else { if (bpp === 8) { for (let i = 0; i < count; ++i) { const r = this._input.read(); image.setPixelR(x++, y, r); if (x >= w) { x = 0; y--; if (y < 0) { break; } } } } else if (bpp === 16) { for (let i = 0; i < count; ++i) { const color = this._input.readUint16(); const r = (color & 0x7c00) >>> 7; const g = (color & 0x3e0) >>> 2; const b = (color & 0x1f) << 3; const a = (color & 0x8000) !== 0 ? 0 : 255; image.setPixelRgba(x++, y, r, g, b, a); if (this._input.isEOS) { break; } if (x >= w) { x = 0; y--; if (y < 0) { break; } } } } else { for (let i = 0; i < count; ++i) { const b = this._input.read(); const g = this._input.read(); const r = this._input.read(); const a = hasAlpha ? this._input.read() : 255; image.setPixelRgba(x++, y, r, g, b, a); if (x >= w) { x = 0; y--; if (y < 0) { break; } } } } } if (x >= w) { x = 0; y--; if (y < 0) { break; } } } return image; } decodeRgb() { if (this._info === undefined || this._input === undefined) { return undefined; } this._input.offset = this._info.imageOffset; const bpp = this._info.pixelDepth; const hasAlpha = bpp === 16 || bpp === 32 || (this._info.hasColorMap && (this._info.colorMapDepth === 16 || this._info.colorMapDepth === 32)); const image = new MemoryImage({ width: this._info.width, height: this._info.height, numChannels: hasAlpha ? 4 : 3, withPalette: this._info.hasColorMap, }); if (this._info.hasColorMap) { this.decodeColorMap(this._info.colorMap, image.palette); } if (bpp === 8) { for (let y = image.height - 1; y >= 0; --y) { for (let x = 0; x < image.width; ++x) { const index = this._input.read(); image.setPixelR(x, y, index); } } } else if (bpp === 16) { for (let y = image.height - 1; y >= 0; --y) { for (let x = 0; x < image.width; ++x) { const color = this._input.readUint16(); const r = (color & 0x7c00) >>> 7; const g = (color & 0x3e0) >>> 2; const b = (color & 0x1f) << 3; const a = (color & 0x8000) !== 0 ? 0 : 255; image.setPixelRgba(x, y, r, g, b, a); } } } else { for (let y = image.height - 1; y >= 0; --y) { for (let x = 0; x < image.width; ++x) { const b = this._input.read(); const g = this._input.read(); const r = this._input.read(); const a = hasAlpha ? this._input.read() : 255; image.setPixelRgba(x, y, r, g, b, a); } } } return image; } isValidFile(bytes) { const input = new InputBuffer({ buffer: bytes, }); this._info = new TgaInfo(); this._info.read(input); return this._info.isValid(); } startDecode(bytes) { this._info = new TgaInfo(); this._input = new InputBuffer({ buffer: bytes }); const header = this._input.readRange(18); this._info.read(header); if (!this._info.isValid()) { return undefined; } this._input.skip(this._info.idLength); if (this._info.hasColorMap) { const size = this._info.colorMapLength * (this._info.colorMapDepth >>> 3); this._info.colorMap = this._input.readRange(size).toUint8Array(); } this._info.imageOffset = this._input.offset; return this._info; } decode(opt) { var _a; const bytes = opt.bytes; if (this.startDecode(bytes) === undefined) { return undefined; } return this.decodeFrame((_a = opt.frameIndex) !== null && _a !== void 0 ? _a : 0); } decodeFrame(_frameIndex) { if (this._info === undefined || this._input === undefined) { return undefined; } if (this._info.imageType === TgaImageType.rgb) { return this.decodeRgb(); } else if (this._info.imageType === TgaImageType.rgbRle || this._info.imageType === TgaImageType.paletteRle) { return this.decodeRle(); } else if (this._info.imageType === TgaImageType.palette) { return this.decodeRgb(); } return undefined; } } //# sourceMappingURL=tga-decoder.js.map