UNPKG

image-raub

Version:
253 lines (176 loc) 4.79 kB
'use strict'; const { resolveObjectURL } = require('node:buffer'); const { inspect, inherits } = require('node:util'); const Emitter = require('node:events'); const { download } = require('addon-tools-raub'); const { Image } = require('../core'); inherits(Image, Emitter); class JsImage extends Image { constructor(src) { super(); this._src = ''; this._isDataUri = false; this._data = null; this.on('error', (err) => { this._data = null; this._error = err; console.error('Image Error:', err); }); if (typeof src === 'string') { this.src = src; } } addEventListener(type, cb) { this.on(type, cb); } removeEventListener(type, cb) { this.removeListener(type, cb); } dispatchEvent(e) { this.emit(e.type || 'unknown', e); } on(name, cb) { super.on(name, cb); if (name === 'load' && this._data) { cb.call(this); } } once(name, cb) { if (name === 'load' && this._data) { cb.call(this); } else { super.once(name, cb); } } load(data) { this._load(data); } get complete() { return this._data !== null; } get data() { return this._data; } get naturalWidth() { return this.width; } get naturalHeight() { return this.height; } get wh() { return [this.width, this.height]; } get size() { const [width, height] = this.wh; return { width, height }; } get src() { return this._src; } set src(v) { // The same - do nothing if (v === this._src) { return; } this._error = null; this._data = null; this._src = v; this._isDataUri = false; // Empty - do nothing if (!this._src) { this._unload(); return; } // Object URL if (/^blob:nodedata:/.test(this._src)) { const blob = resolveObjectURL(this._src); (async () => { const arrayBuffer = await blob.arrayBuffer(); const buffer = Buffer.from(arrayBuffer); this._load(buffer); })(); return; } // Data URI if (/^data:/.test(this._src)) { this._isDataUri = true; const [head, body] = this._src.split(','); const isBase64 = head.indexOf('base64') > -1; const data = isBase64 ? Buffer.from(body, 'base64') : Buffer.from(unescape(body)); this._load(data); return; } // Remote image if (/^https?:\/\//i.test(this._src)) { download(this._src).then( (data) => this._load(data), (err) => this.emit('error', err) ); return; } // Filesystem image require('node:fs').readFile(this._src, (err, data) => { if (err) { return this.emit('error', err); } this._load(data); }); } get onerror() { return this.listeners('error'); } set onerror(cb) { cb ? this.on('error', cb) : this.removeAllListeners('error'); } get onload() { return this.listeners('load'); } set onload(cb) { cb ? this.on('load', cb) : this.removeAllListeners('load'); } [inspect.custom]() { return this.toString(); } toString() { if (!this.src) { return 'Image { EMPTY }'; } const src = this._isDataUri ? `${this.src.slice(0, 32)}...` : this.src; if (this._error) { return `Image { ERROR, ${src} }`; } if (!this._data) { return `Image { LOADING, ${src} }`; } return `Image { ${this.width}x${this.height} ${src} }`; } static async loadAsync(src) { return new Promise((res, rej) => { const img = new JsImage(); img.on('load', () => res(img)); img.on('error', () => rej); img.src = src; }); } static fromPixels(width, height, bpp, pixels) { const memSize = width * height * Math.floor(bpp / 8); // ====== MIMIC BMP // see https://en.wikipedia.org/wiki/BMP_file_format const dibSize = 40; const headerSize = 14 + dibSize; const bmpSize = headerSize + memSize; const fakeBmp = Buffer.allocUnsafeSlow(bmpSize); let pos = 0; // ---------- BMP header fakeBmp.write('BM', pos, 2, 'ascii'); pos += 2; fakeBmp.writeUInt32LE(bmpSize, pos); pos += 4; pos += 4; // skip unused fakeBmp.writeUInt32LE(headerSize, pos); pos += 4; // ---------- DIB header fakeBmp.writeUInt32LE(dibSize, pos); pos += 4; fakeBmp.writeInt32LE(width, pos); pos += 4; fakeBmp.writeInt32LE(height, pos); pos += 4; fakeBmp.writeUInt16LE(1, pos); pos += 2; fakeBmp.writeUInt16LE(bpp, pos); pos += 2; fakeBmp.writeUInt32LE(0, pos); pos += 4; fakeBmp.writeUInt32LE(memSize, pos); pos += 4; fakeBmp.writeUInt32LE(0x0ec4, pos); pos += 4; fakeBmp.writeUInt32LE(0x0ec4, pos); pos += 4; fakeBmp.writeUInt32LE(0, pos); pos += 4; fakeBmp.writeUInt32LE(0, pos); pos += 4; // ---------- PIXELS pixels.copy(fakeBmp, pos); // ====== STORE JPEG const img = new JsImage(); img._load(fakeBmp, true); return img; } } module.exports = JsImage;