UNPKG

@andreekeberg/imagedata

Version:

Unified method of retrieving an ImageData instance from an image, in both the browser and Node.js, asynchronously or synchronously

135 lines (113 loc) 4 kB
const { ReadStream, existsSync, readFileSync } = require('fs') const jimp = require('jimp') const { streamToBuffer } = require('@jorgeferrero/stream-to-buffer') const { sync: mimeSync } = require('mime-kind') /** * {@link https://developer.mozilla.org/en-US/docs/Web/API/ImageData|ImageData} polyfill for Node.js */ class ImageData { /** * Create a new ImageData instance from an object literal * * @param {Object} object * @throws {Error} If the object argument is not an object literal * @throws {Error} If the object argument is missing required data parameter * @throws {Error} If the object argument is missing required width parameter * @throws {Error} If the object argument is missing required height parameter */ constructor(object) { if (!(object instanceof Object) || object.constructor !== Object) { throw new Error('Argument object must be an object literal') } const required = ['data', 'width', 'height'] required.forEach(param => { if (typeof object[param] === 'undefined') { throw new Error(`Argument object is missing required "${param}" parameter`) } }) let data if (object.data instanceof Uint8ClampedArray) { data = object.data } else { data = new Uint8ClampedArray(object.data) } Object.defineProperty(this, 'data', { value: data, writable: false, enumerable: true }) Object.defineProperty(this, 'width', { value: object.width, writable: false, enumerable: true }) Object.defineProperty(this, 'height', { value: object.height, writable: false, enumerable: true }) } } /** * Asynchronously get an ImageData instance based on provided data * * @param {(string|Buffer|ReadStream)} data * @param {function} callback * @throws {Error} If the data argument is not a string, Buffer, or ReadStream * @throws {Error} If the callback argument is not a function */ const get = (data, callback) => { if ( typeof data !== 'string' && !(data instanceof Buffer) && !(data instanceof ReadStream) ) { throw new Error('Argument data must be a string, Buffer, or ReadStream') } if (typeof callback !== 'function') { throw new Error('Argument callback must be a function') } const read = data => { jimp.read(data).then(image => { callback(null, new ImageData(image.bitmap)) }, callback) } if (data instanceof ReadStream) { streamToBuffer(data).then(read) } else { read(data) } } /** * Synchronously get an ImageData instance based on provided data * * @param {(string|Buffer)} data * @throws {Error} If the data argument is not a string or Buffer * @throws {Error} If the data argument is a string and the file could not be found * @throws {Error} If the mime type could not be retrieved * @throws {Error} If no image decoder could be found for the mime type * @returns {ImageData} */ const getSync = data => { if (typeof data !== 'string' && !(data instanceof Buffer)) { throw new Error('Argument data must be a string or Buffer') } if (typeof data === 'string' && !existsSync(data)) { throw new Error(`Could not find ${data}`) } const mimeType = mimeSync(data) if (mimeType === null) { throw new Error('Could not retrieve mime type of file') } if (typeof data === 'string') { data = readFileSync(data) } const type = mimeType.mime if (typeof jimp.decoders[type] === 'undefined') { throw new Error(`No image decoder available for file of type ${type}`) } return new ImageData(jimp.decoders[type](data)) } exports.get = get exports.getSync = getSync exports.ImageData = ImageData