Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

imagejs

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

imagejs - npm Package Compare versions

Comparing version 0.0.1 to 0.0.2

175

lib/bitmap.js

@@ -31,2 +31,3 @@ /**

//var png = require("png-js");
var PNG = require("node-png").PNG;

@@ -37,2 +38,7 @@ var Enums = require("./enums");

// default pad colour
var transparentBlack = {
r: 0, g: 0, b: 0, a: 0
};
var Bitmap = module.exports = function(options) {

@@ -79,3 +85,16 @@ if (options) {

read: function(stream, type) {
_deduceFileType: function(filename) {
switch (filename.substr(-4).toLowerCase()) {
case ".jpg":
return Enums.ImageType.JPG;
case ".png":
return Enums.ImageType.PNG;
}
if (filename.substr(-5).toLowerCase() == ".jpeg") {
return Enums.ImageType.JPG;
}
throw new Error("Can't recognise image type: " + filename);
},
_readStream: function(stream) {
var self = this;

@@ -90,28 +109,48 @@ var deferred = Promise.defer();

var data = Buffer.concat(chunks);
if (data) {
try {
switch(type) {
case Enums.ImageType.JPG:
self._data = jpeg.decode(data);
break;
//case Enums.ImageType.PNG:
// self._data = png.decode(data);
// break;
default:
throw new Error("Not supported: ImageType " + type);
}
//console.log(JSON.stringify({width: self._data.width, height: self._data.height, data: (self._data.data != undefined)}));
deferred.resolve();
}
catch(ex) {
deferred.reject(ex);
}
} else {
deferred.reject(new Error("No data found on stream"))
}
deferred.resolve(data);
});
stream.on('error', function(error) {
deferred.reject(error);
});
return deferred.promise;
},
_readPNG: function(stream) {
var deferred = Promise.defer();
var png = new PNG({filterType: 4});
png.on('parsed', function() {
deferred.resolve(png);
});
png.on('error', function(error) {
deferred.rejecyt(error);
});
stream.pipe(png);
return deferred.promise;
},
read: function(stream, type) {
var self = this;
switch(type) {
case Enums.ImageType.JPG:
return this._readStream(stream)
.then(function(data) {
self._data = jpeg.decode(data);
});
case Enums.ImageType.PNG:
return this._readPNG(stream)
.then(function(png) {
self._data = {
data: png.data,
width: png.width,
height: png.height
};
});
default:
return Promise.reject(new Error("Not supported: ImageType " + type));
}
},
readFile: function(filename, type) {
type = type || this._deduceFileType(filename);
var stream = fs.createReadStream(filename);

@@ -124,22 +163,31 @@ return this.read(stream, type);

try {
var buffer;
stream.on('finish', function() {
deferred.resolve();
});
stream.on('error', function(error) {
deferred.reject(error);
});
switch(type) {
case Enums.ImageType.JPG:
buffer = jpeg.encode(this._data, 50).data;
var buffer = jpeg.encode(this._data, 50).data;
stream.write(buffer);
stream.end();
break;
//case Enums.ImageType.PNG:
// buffer = png.encode(this._data);
// break;
case Enums.ImageType.PNG:
var png = new PNG();
png.width = this.width;
png.height = this.height;
png.data = this._data.data;
png.on('end', function() {
deferred.resolve();
});
png.on('error', function(error) {
deferred.reject(error);
});
png.pack().pipe(stream);
break;
default:
throw new Error("Not supported: ImageType " + type);
}
stream.on('finish', function() {
deferred.resolve();
});
stream.on('error', function(error) {
deferred.reject(error);
});
stream.write(buffer);
stream.end();
}

@@ -152,2 +200,3 @@ catch(ex) {

writeFile: function(filename, type) {
type = type || this._deduceFileType(filename);
var stream = fs.createWriteStream(filename);

@@ -184,5 +233,57 @@ return this.write(stream, type);

var that = new Bitmap(options);
Resize[options.algorithm](this, that);
// crop determines from where in src to fetch image data
options.crop = {
left: 0, top: 0,
width: this.width, height: this.height
};
// bounds determines where in dst to draw the scaled image
options.bounds = {
left: 0, top: 0,
width: options.width, height: options.height
};
switch (options.fit) {
case "pad": // fit all of src in dst with optional pad colour
options.padColor = options.padColor || transparentBlack;
var srcAr = this.width / this.height;
var dstAr = that.width / that.height;
var w2 = Math.round(srcAr * that.height);
var h2 = Math.round(that.width / srcAr);
if (w2 < that.width) {
// pad sides
var dw = that.width - w2;
options.bounds.left = Math.round(dw / 2);
options.bounds.width = w2;
} else if (h2 < that.height) {
// pad top & bottom
var dh = that.height - h2;
options.bounds.top = Math.round(dh / 2);
options.bounds.height = h2;
}
break;
case "crop": // crop original to fit in dst with no pad
var gravity = options.gravity || {x: 0.5, y: 0.5};
var dstAr = that.width / that.height;
var w2 = Math.round(dstAr * this.height);
var h2 = Math.round(this.width / dstAr);
if (w2 < this.width) {
// crop src width
var dw = this.width - w2;
options.crop.left = Math.round(gravity.x * dw);
options.crop.width = w2;
} else if (h2 < this.height) {
// crop src height
var dh = this.height - h2;
options.crop.top = Math.round(gravity.y * dh);
options.crop.height = h2;
}
break;
case "stretch":
default:
// default crop and bounds are set above
break;
}
Resize[options.algorithm](this, that, options);
return that;
}
}

@@ -30,23 +30,179 @@ /**

module.exports = {
nearestNeighbor: function(src, dst) {
var wSrc = src.width;
var hSrc = src.height;
var wDst = dst.width;
var hDst = dst.height;
_pad: function(dst, options) {
var top = options.bounds.top;
var left = options.bounds.left;
var bottom = top + options.bounds.height;
var right = left + options.bounds.width;
// optimization - quit now if top and left are zero
if (!top && !left) {
return;
}
var width = dst.width;
var height = dst.height;
var data = dst._data.data;
var r = options.padColor.r;
var g = options.padColor.g;
var b = options.padColor.b;
var a = options.padColor.a;
var i, j, pos;
// top margin
for (i = 0; i < top; i++) {
for (j = 0; j < width; j++) {
pos = (i * width + j) * 4;
data[pos++] = r;
data[pos++] = g;
data[pos++] = b;
data[pos++] = a;
}
}
// bottom margin
for (i = bottom; i < height; i++) {
for (j = 0; j < width; j++) {
pos = (i * width + j) * 4;
data[pos++] = r;
data[pos++] = g;
data[pos++] = b;
data[pos++] = a;
}
}
// left margin
for (i = 0; i < height; i++) {
for (j = 0; j < left; j++) {
pos = (i * width + j) * 4;
data[pos++] = r;
data[pos++] = g;
data[pos++] = b;
data[pos++] = a;
}
}
// right margin
for (i = 0; i < height; i++) {
for (j = right; j < width; j++) {
pos = (i * width + j) * 4;
data[pos++] = r;
data[pos++] = g;
data[pos++] = b;
data[pos++] = a;
}
}
},
nearestNeighbor: function(src, dst, options) {
this._pad(dst, options);
var srcWidth = src.width;
var srcHeight = src.height;
var dstWidth = dst.width;
var dstHeight = dst.height;
var tSrc = options.crop.top;
var lSrc = options.crop.left;
var wSrc = options.crop.width;
var hSrc = options.crop.height;
var bSrc = tSrc + hSrc;
var rSrc = lSrc + wSrc;
//console.log("tSrc="+tSrc + ", lSrc="+lSrc + ", wSrc="+wSrc + ", hSrc="+hSrc + ", bSrc="+bSrc + ", rSrc="+rSrc);
var tDst = options.bounds.top;
var lDst = options.bounds.left;
var wDst = options.bounds.width;
var hDst = options.bounds.height;
var bDst = tDst + hDst;
var rDst = lDst + wDst;
//console.log("tDst="+tDst + ", lDst="+lDst + ", wDst="+wDst + ", hDst="+hDst + ", bDst="+bDst + ", rDst="+rDst);
var bufSrc = src._data.data;
var bufDst = dst._data.data;
for (var i = 0; i < hDst; i++) {
for (var j = 0; j < wDst; j++) {
var pos = (i * wDst + j) * 4;
var nni = Math.round(i * hSrc / hDst);
var nnj = Math.round(j * wSrc / wDst);
var nnPos = (nni * wSrc + nnj) * 4;
for (var i = tDst; i < bDst; i++) {
for (var j = lDst; j < rDst; j++) {
var posDst = (i * dstWidth + j) * 4;
var iSrc = tSrc + Math.round((i-tDst) * hSrc / hDst);
var jSrc = lSrc + Math.round((j-lDst) * wSrc / wDst);
var posSrc = (iSrc * srcWidth + jSrc) * 4;
bufDst[pos++] = bufSrc[nnPos++];
bufDst[pos++] = bufSrc[nnPos++];
bufDst[pos++] = bufSrc[nnPos++];
bufDst[pos++] = bufSrc[nnPos++];
bufDst[posDst++] = bufSrc[posSrc++];
bufDst[posDst++] = bufSrc[posSrc++];
bufDst[posDst++] = bufSrc[posSrc++];
bufDst[posDst++] = bufSrc[posSrc++];
}
}
},
bilinearInterpolation: function(src, dst, options) {
this._pad(dst, options);
var srcWidth = src.width;
var srcHeight = src.height;
var dstWidth = dst.width;
var dstHeight = dst.height;
var tSrc = options.crop.top;
var lSrc = options.crop.left;
var wSrc = options.crop.width;
var hSrc = options.crop.height;
var bSrc = tSrc + hSrc;
var rSrc = lSrc + wSrc;
//console.log("tSrc="+tSrc + ", lSrc="+lSrc + ", wSrc="+wSrc + ", hSrc="+hSrc + ", bSrc="+bSrc + ", rSrc="+rSrc);
var tDst = options.bounds.top;
var lDst = options.bounds.left;
var wDst = options.bounds.width;
var hDst = options.bounds.height;
var bDst = tDst + hDst;
var rDst = lDst + wDst;
//console.log("tDst="+tDst + ", lDst="+lDst + ", wDst="+wDst + ", hDst="+hDst + ", bDst="+bDst + ", rDst="+rDst);
var bufSrc = src._data.data;
var bufDst = dst._data.data;
var interpolate = function(k, kMin, vMin, kMax, vMax) {
// special case - k is integer
if (kMin === kMax) {
return vMin;
}
return Math.round((k - kMin) * vMax + (kMax - k) * vMin);
};
var assign = function(pos, offset, x, xMin, xMax, y, yMin, yMax) {
var posMin = (yMin * srcWidth + xMin) * 4 + offset;
var posMax = (yMin * srcWidth + xMax) * 4 + offset;
var vMin = interpolate(x, xMin, bufSrc[posMin], xMax, bufSrc[posMax]);
// special case, y is integer
if (yMax === yMin) {
bufDst[pos+offset] = vMin;
} else {
posMin = (yMax * srcWidth + xMin) * 4 + offset;
posMax = (yMax * srcWidth + xMax) * 4 + offset;
var vMax = interpolate(x, xMin, bufSrc[posMin], xMax, bufSrc[posMax]);
bufDst[pos+offset] = interpolate(y, yMin, vMin, yMax, vMax);
}
}
for (var i = tDst; i < bDst; i++) {
for (var j = lDst; j < rDst; j++) {
var posDst = (i * dstWidth + j) * 4;
// x & y in src coordinates
var x = lSrc + (j-lDst) * wSrc / wDst;
var xMin = Math.floor(x);
var xMax = Math.min(Math.ceil(x), rSrc-1);
var y = tSrc + (i-tDst) * hSrc / hDst;
var yMin = Math.floor(y);
var yMax = Math.min(Math.ceil(y), bSrc-1);
assign(posDst, 0, x, xMin, xMax, y, yMin, yMax);
assign(posDst, 1, x, xMin, xMax, y, yMin, yMax);
assign(posDst, 2, x, xMin, xMax, y, yMin, yMax);
assign(posDst, 3, x, xMin, xMax, y, yMin, yMax);
}
}
}
}
{
"name": "imagejs",
"version": "0.0.1",
"version": "0.0.2",
"description": "Image Processor",

@@ -22,2 +22,3 @@ "private": false,

"jpeg-js": "0.1.1",
"node-png": "0.4.3",
"underscore": "1.4.4"

@@ -24,0 +25,0 @@ },

@@ -13,2 +13,23 @@ # ImageJS

# New Features!
<ul>
<li>
<a href="#image-resize">Enhanced Resize</a>
<ul>
<li>New Resize Algorithm: Bilinear Interpolation</li>
<li>Stretch, Crop or Pad to Fit</li>
</ul>
</li>
<li><a href="#reading-images">PNG Image files supported</a></li>
</ul>
# Coming Soon
<ul>
<li>Bicubic Interpolation</li>
<li>Crop</li>
<li>Pad</li>
</ul>
# Contents

@@ -21,3 +42,8 @@

<li><a href="#creating-bitmaps">Creating Bitmaps</a></li>
<li><a href="#manipulating-bitmaps">Manipulating Bitmaps</a></li>
<li>
<a href="#manipulating-bitmaps">Manipulating Bitmaps</a>
<ul>
<li><a href="set-pixel">Set Pixel</a></li>
</ul>
</li>
<li><a href="#reading-images">Reading Images</a></li>

@@ -60,2 +86,3 @@ <li><a href="#writing-images">Writing Images</a></li>

```javascript
// Set a pixel

@@ -69,5 +96,31 @@ // where: 0 <= x < width, 0 <= y < height, 0 <= a,r,g,b < 256

// Create a new bitmap resized from an original
// Currently only nearest neighbor is implemented. More to follow.
var thumbnail = bitmap.resize({width: 64, height: 64, algorithm: "nearestNeighbor"})
// resize to 64x64 icon sized bitmap using nearest neighbor algorithm & stretch to fit
var thumbnail = bitmap.resize({
width: 64, height: 64,
algorithm: "nearestNeighbor"
});
// resize to 100x150 bitmap using bilinear interpolation and cropping to fit, gravity center
var thumbnail = bitmap.resize({
width: 100, height: 150,
algorithm: "bilinearInterpolation",
fit: "crop",
gravity: {x:0.5, y:0.5} // center - note: this is the default
});
// resize to 300x200 bitmap using bilinear interpolation and padding to fit, pad color solid red
var thumbnail = bitmap.resize({
width: 100, height: 150,
algorithm: "bilinearInterpolation",
fit: "pad",
padColor: {r:255, g:0, b:0, a:255}
});
```
**Supported Resize Algorithms**
* nearestNeighbor
* bilinearInterpolation
## Reading Images

@@ -78,3 +131,3 @@

var bitmap = new Bitmap();
bitmap.readFile(filename, ImageJS.ImageType.JPG)
bitmap.readFile(filename)
.then(function() {

@@ -84,3 +137,3 @@ // bitmap is ready

// read from stream
// read JPG data from stream
var stream = createReadStream();

@@ -99,3 +152,3 @@ var bitmap = new Bitmap();

// write to a file
return bitmap.writeFile(filename, ImageJS.ImageType.JPG)
return bitmap.writeFile(filename)
.then(function() {

@@ -105,5 +158,5 @@ // bitmap has been saved

// write to a stream
// write PNG Image to a stream
var stream = createWriteStream();
return bitmap.write(stream, ImageJS.ImageType.JPG)
return bitmap.write(stream, ImageJS.ImageType.PNG)
.then(function() {

@@ -121,2 +174,3 @@ // bitmap has been written and stream ended

| 0.0.1 | Initial Version |
| 0.0.2 | <ul><li><a href="#image-resize">Enhanced Resize</a><ul><li>New Resize Algorithm: Bilinear Interpolation</li><li>Stretch, Crop or Pad to Fit</li></ul></li><li><a href="#reading-images">PNG Image files supported</a></li></ul> |
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc