UNPKG

node-lwip

Version:

Comprehensive, fast, and simple image processing and manipulation

621 lines (579 loc) 20.1 kB
(function(undefined) { var Image = require('./Image'), path = require('path'), fs = require('fs'), decree = require('decree'), defs = require('./defs'), util = require('./util'), encoder = require('bindings')('lwip_encoder'), lwip_image = require('bindings')('lwip_image'), normalizeColor = util.normalizeColor; var judges = { scale: decree(defs.args.scale), resize: decree(defs.args.resize), contain: decree(defs.args.contain), cover: decree(defs.args.cover), rotate: decree(defs.args.rotate), blur: decree(defs.args.blur), hslaAdjust: decree(defs.args.hslaAdjust), saturate: decree(defs.args.saturate), lighten: decree(defs.args.lighten), darken: decree(defs.args.darken), fade: decree(defs.args.fade), opacify: decree(defs.args.opacify), hue: decree(defs.args.hue), crop: decree(defs.args.crop), mirror: decree(defs.args.mirror), pad: decree(defs.args.pad), border: decree(defs.args.border), sharpen: decree(defs.args.sharpen), paste: decree(defs.args.paste), clone: decree(defs.args.clone), extract: decree(defs.args.extract), toBuffer: decree(defs.args.toBuffer), writeFile: decree(defs.args.writeFile), setPixel: decree(defs.args.setPixel), getPixel: decree(defs.args.getPixel) }; Image.prototype.__lock = function() { if (!this.__locked) this.__locked = true; else throw Error("Another image operation already in progress"); }; Image.prototype.__release = function() { this.__locked = false; }; Image.prototype.getMetadata = function() { return this.__metadata; }; Image.prototype.setMetadata = function(data) { if (typeof data != "string" && data != null) { throw Error("Metadata must be a string or null"); } this.__metadata = data; return data; }; Image.prototype.width = function() { return this.__lwip.width(); }; Image.prototype.height = function() { return this.__lwip.height(); }; Image.prototype.size = function() { return { width: this.__lwip.width(), height: this.__lwip.height() }; }; Image.prototype.getPixel = function() { var args = judges.getPixel(arguments), left = args[0], top = args[1]; if (left >= this.width() || top >= this.height()) throw Error("Coordinates exceed dimensions of image"); var rgba = this.__lwip.getPixel(left, top); return { r: rgba[0], g: rgba[1], b: rgba[2], a: rgba[3] }; }; Image.prototype.scale = function() { this.__lock(); var that = this; judges.scale( arguments, function(wRatio, hRatio, inter, callback) { hRatio = hRatio || wRatio; var width = +wRatio * that.width(), height = +hRatio * that.height(); that.__lwip.resize(width, height, defs.interpolations[inter], function(err) { that.__release(); callback(err, that); }); }, function(err) { that.__release(); throw err; } ); }; Image.prototype.resize = function() { this.__lock(); var that = this; judges.resize( arguments, function(width, height, inter, callback) { height = height || width; that.__lwip.resize(+width, +height, defs.interpolations[inter], function(err) { that.__release(); callback(err, that); }); }, function(err) { that.__release(); throw err; } ); }; Image.prototype.contain = function() { var that = this; judges.contain( arguments, function(width, height, color, inter, callback) { var s = Math.min(width / that.width(), height / that.height()); that.scale(s, s, inter, function(err){ if (err) return callback(err); var padX = (width - that.width()) / 2, padY = (height - that.height()) / 2; that.pad( Math.ceil(padX), Math.ceil(padY), Math.floor(padX), Math.floor(padY), color, callback ); }); } ); }; Image.prototype.cover = function() { var that = this; judges.cover( arguments, function(width, height, inter, callback) { var s = Math.max(width / that.width(), height / that.height()); that.scale(s, s, inter, function(err){ if (err) return callback(err); that.crop(width, height, callback); }); } ); }; Image.prototype.rotate = function() { this.__lock(); var that = this; judges.rotate( arguments, function(degs, color, callback) { color = normalizeColor(color); if (color.a < 100) that.__trans = true; that.__lwip.rotate(+degs, +color.r, +color.g, +color.b, +color.a, function(err) { that.__release(); callback(err, that); }); }, function(err) { that.__release(); throw err; } ); }; Image.prototype.blur = function() { this.__lock(); var that = this; judges.blur( arguments, function(sigma, callback) { that.__lwip.blur(+sigma, function(err) { that.__release(); callback(err, that); }); }, function(err) { that.__release(); throw err; } ); }; Image.prototype.hslaAdjust = function() { this.__lock(); var that = this; judges.hslaAdjust( arguments, function(hs, sd, ld, ad, callback) { that.__lwip.hslaAdj(+hs, +sd, +ld, +ad, function(err) { that.__release(); callback(err, that); }); }, function(err) { that.__release(); throw err; } ); }; Image.prototype.saturate = function() { this.__lock(); var that = this; judges.saturate( arguments, function(delta, callback) { that.__lwip.hslaAdj(0, +delta, 0, 0, function(err) { that.__release(); callback(err, that); }); }, function(err) { that.__release(); throw err; } ); }; Image.prototype.lighten = function() { this.__lock(); var that = this; judges.lighten( arguments, function(delta, callback) { that.__lwip.hslaAdj(0, 0, +delta, 0, function(err) { that.__release(); callback(err, that); }); }, function(err) { that.__release(); throw err; } ); }; Image.prototype.darken = function() { this.__lock(); var that = this; judges.darken( arguments, function(delta, callback) { that.__lwip.hslaAdj(0, 0, -delta, 0, function(err) { that.__release(); callback(err, that); }); }, function(err) { that.__release(); throw err; } ); }; Image.prototype.fade = function() { this.__lock(); var that = this; judges.fade( arguments, function(delta, callback) { that.__lwip.hslaAdj(0, 0, 0, -delta, function(err) { if (+delta > 0) that.__trans = true; that.__release(); callback(err, that); }); }, function(err) { that.__release(); throw err; } ); }; Image.prototype.opacify = function() { this.__lock(); var that = this; judges.opacify( arguments, function(callback) { that.__lwip.opacify(function(err) { that.__trans = false; that.__release(); callback(err, that); }); }, function(err) { that.__release(); throw err; } ); }; Image.prototype.hue = function() { this.__lock(); var that = this; judges.hue( arguments, function(shift, callback) { that.__lwip.hslaAdj(+shift, 0, 0, 0, function(err) { that.__release(); callback(err, that); }); }, function(err) { that.__release(); throw err; } ); }; Image.prototype.crop = function() { this.__lock(); var that = this; judges.crop( arguments, function(left, top, right, bottom, callback) { if (!right && !bottom) { var size = that.size(), width = left, height = top; left = 0 | (size.width - width) / 2; top = 0 | (size.height - height) / 2; right = left + width - 1; bottom = top + height - 1; } that.__lwip.crop(left, top, right, bottom, function(err) { that.__release(); callback(err, that); }); }, function(err) { that.__release(); throw err; } ); }; Image.prototype.mirror = function() { this.__lock(); var that = this; judges.mirror( arguments, function(axes, callback) { var xaxis = false, yaxis = false; if ('x' === axes) xaxis = true; if ('y' === axes) yaxis = true; if ('xy' === axes || 'yx' === axes) { xaxis = true; yaxis = true; } that.__lwip.mirror(xaxis, yaxis, function(err) { that.__release(); callback(err, that); }); }, function(err) { that.__release(); throw err; } ); }; // mirror alias: Image.prototype.flip = Image.prototype.mirror; Image.prototype.pad = function() { this.__lock(); var that = this; judges.pad( arguments, function(left, top, right, bottom, color, callback) { color = normalizeColor(color); if (color.a < 100) that.__trans = true; that.__lwip.pad(+left, +top, +right, +bottom, +color.r, +color.g, +color.b, +color.a, function(err) { that.__release(); callback(err, that); }); }, function(err) { that.__release(); throw err; } ); }; Image.prototype.border = function() { this.__lock(); var that = this; judges.border( arguments, function(width, color, callback) { color = normalizeColor(color); if (color.a < 100) that.__trans = true; // we can just use image.pad... that.__lwip.pad(+width, +width, +width, +width, +color.r, +color.g, +color.b, +color.a, function(err) { that.__release(); callback(err, that); }); }, function(err) { that.__release(); throw err; } ); }; Image.prototype.sharpen = function() { this.__lock(); var that = this; judges.sharpen( arguments, function(amplitude, callback) { that.__lwip.sharpen(+amplitude, function(err) { that.__release(); callback(err, that); }); }, function(err) { that.__release(); throw err; } ); }; Image.prototype.paste = function() { this.__lock(); var that = this; try{ judges.paste( arguments, function(left, top, img, callback) { // first we retrieve what we need (buffer, dimensions, ...) // synchronously so that the pasted image doesn't have a chance // to be changed var pixbuff = img.__lwip.buffer(), width = img.__lwip.width(), height = img.__lwip.height(); if (left + width > that.__lwip.width() || top + height > that.__lwip.height()) throw Error("Pasted image exceeds dimensions of base image"); that.__lwip.paste(+left, +top, pixbuff, +width, +height, function(err) { that.__release(); callback(err, that); }); } ); } catch(err){ that.__release(); throw err; } }; Image.prototype.setPixel = function() { this.__lock(); var that = this; try{ judges.setPixel( arguments, function(left, top, color, callback) { if (left >= that.width() || top >= that.height()) throw Error("Coordinates exceed dimensions of image"); color = normalizeColor(color); if (color.a < 100) that.__trans = true; that.__lwip.setPixel(+left, +top, +color.r, +color.g, +color.b, +color.a, function(err) { that.__release(); callback(err, that); }); } ); } catch(err){ that.__release(); throw err; } }; Image.prototype.clone = function() { // no need to lock the image. we don't modify the memory buffer. // just copy it. var that = this; judges.clone( arguments, function(callback) { // first we retrieve what we need (buffer, dimensions, ...) // synchronously so that the original image doesn't have a chance // to be changed (remember, we don't lock it); and only then call // the callback asynchronously. var pixbuff = that.__lwip.buffer(), width = that.__lwip.width(), height = that.__lwip.height(), trans = that.__trans; setImmediate(function() { callback(null, new Image(pixbuff, width, height, trans)); }); } ); }; Image.prototype.extract = function() { // no need to lock the image. we don't modify the memory buffer. // just copy it and then crop it. var that = this; judges.extract( arguments, function(left, top, right, bottom, callback) { // first we retrieve what we need (buffer, dimensions, ...) // synchronously so that the original image doesn't have a chance // to be changed (remember, we don't lock it); then we crop it and // only call the callback asynchronously. var pixbuff = that.__lwip.buffer(), width = that.__lwip.width(), height = that.__lwip.height(), trans = that.__trans, eximg = new Image(pixbuff, width, height, trans); eximg.__lwip.crop(left, top, right, bottom, function(err) { callback(err, eximg); }); } ); }; Image.prototype.toBuffer = function() { this.__lock(); var that = this; try{ judges.toBuffer( arguments, function(type, params, callback) { if (type === 'jpg' || type === 'jpeg') { util.normalizeJpegParams(params); return encoder.jpeg( that.__lwip.buffer(), that.__lwip.width(), that.__lwip.height(), params.quality, encoderCb ); } else if (type === 'png') { util.normalizePngParams(params); return encoder.png( that.__lwip.buffer(), that.__lwip.width(), that.__lwip.height(), params.compression, params.interlaced, params.transparency === 'auto' ? that.__trans : params.transparency, that.__metadata, encoderCb ); } else if (type === 'gif') { util.normalizeGifParams(params); return encoder.gif( that.__lwip.buffer(), that.__lwip.width(), that.__lwip.height(), util.getClosest2Exp(params.colors), params.colors, params.interlaced, params.transparency === 'auto' ? that.__trans : params.transparency, params.threshold, encoderCb ); } else throw Error('Unknown type \'' + type + '\''); function encoderCb(err, buffer) { that.__release(); callback(err, buffer); } } ); } catch (err){ that.__release(); throw err; } }; Image.prototype.writeFile = function() { var that = this; judges.writeFile( arguments, function(outpath, type, params, callback) { type = type || path.extname(outpath).slice(1).toLowerCase(); that.toBuffer(type, params, function(err, buffer) { if (err) return callback(err); fs.writeFile(outpath, buffer, { encoding: 'binary' }, callback); }); } ); }; })(void 0);