Jimp
JavaScript Image Manipulation Program
The "JavaScript Image Manipulation Program" :-)
An image processing library for Node written entirely in JavaScript, with zero native dependencies.
The default jimp configuration.
Supported types:
@jimp/jpeg
@jimp/png
@jimp/bmp
@jimp/tiff
@jimp/gif
Installation
npm install --save jimp
Example usage (Promise will never resolve if callback is passed):
var Jimp = require('jimp');
Jimp.read('lenna.png', (err, lenna) => {
if (err) throw err;
lenna
.resize(256, 256)
.quality(60)
.greyscale()
.write('lena-small-bw.jpg');
});
Using promises:
Jimp.read('lenna.png')
.then(lenna => {
return lenna
.resize(256, 256)
.quality(60)
.greyscale()
.write('lena-small-bw.jpg');
})
.catch(err => {
console.error(err);
});
TypeScript Usage
If you're using this library with TypeScript the method of importing slightly differs from JavaScript. Instead of using require, you must import it with ES6 default import scheme
import Jimp from 'jimp';
This requires setting the allowSyntheticDefaultImports
compiler option to true
in your tsconfig
Note 1: esModuleInterop
implicitly sets allowSyntheticDefaultImports
to true
Note 2: allowSyntheticDefaultImports
nor esModuleInterop
change the runtime behavior of your code at all. They are just flags that tells TypeScript you need the compatibility they offer.
Module Build
If you're using a web bundles (webpack, rollup, parcel) you can benefit from using the module
build of jimp. Using the module build will allow your bundler to understand your code better and exclude things you aren't using.
import Jimp from 'jimp/es';
WebPack
If you're using webpack you can set process.browser
to true and your build of jimp will exclude certain parts, making it load faster.
{
plugins: [
new webpack.DefinePlugin({
'process.browser': 'true'
}),
...
],
}
Basic usage
The static Jimp.read
method takes the path to a file, URL, dimensions, a Jimp instance or a buffer and returns a Promise:
Jimp.read('./path/to/image.jpg')
.then(image => {
})
.catch(err => {
});
Jimp.read('http://www.example.com/path/to/lenna.jpg')
.then(image => {
})
.catch(err => {
});
Jimp.read(jimpInstance)
.then(image => {
})
.catch(err => {
});
Jimp.read(buffer)
.then(image => {
})
.catch(err => {
});
In some cases, you need to pass additional parameters with an image's URL. You can pass an object to the Jimp.read
method:
Jimp.read({
url: 'http://www.example.com/path/to/lenna.jpg',
headers: {},
...
})
.then(image => {
})
.catch(err => {
});
Jimp uses phin as it's HTTP client. Phin uses http.request(options[, callback])
or https.request(options[, callback])
methods for making HTTP requests. Phin parses a url
with the url.parse(...)
method and passes it with all the other parameters as an options
to the http.request(options[, callback])
or https.request(options[, callback])
methods.
Briefly speaking, you can pass any options from http.request(options[, callback])
, https.request(options[, callback])
or even tls.connect(options[, callback])
.
Phin parses a url
and combines it with any options you want. This behavior can be very useful when you need to pass some additional headers
. Also, you can pass rejectUnauthorized: false
if you don't require an SSL certificate to be valid (it helps to prevent unable to verify the first certificate
error).
The convenience method Jimp.create
also exists. It is just a wrapper around Jimp.read
.
Custom Constructor
You might want to initialize jimp in so custom way. To do this Jimp exposes the static function appendConstructorOption
. The appended constructor options run after all the defaults.
To define a custom constructor provide a name for it, a function to call to determine if the arguments provided to jimp match your constructor, and a function called where you can construct the image however you want.
Jimp.appendConstructorOption(
'Name of Option',
args => arg.hasSomeCustomThing,
function(resolve, reject, args) {
this.bitmap = customParser(args);
resolve();
}
);
If you don't want to handle parsing the bitmap. For example if you want to do some sort of authentication for URL request. Jimp exposes parseBitmap
so you can fall back to jimp to do the heavy lifting.
Parse bitmap takes the raw image data in a Buffer, a path (optional), and a node style callback.
Jimp.appendConstructorOption('Custom Url', options => options.url, function(
resolve,
reject,
options
) {
phin(options, (err, res) => {
if (err) {
return reject(err);
}
this.parseBitmap(res.body, options.url, err => {
if (err) {
return reject(err);
}
resolve();
});
});
});
Methods
Once the promise is fulfilled, the following methods can be called on the image:
image.contain( w, h[, alignBits || mode, mode] );
image.cover( w, h[, alignBits || mode, mode] );
image.resize( w, h[, mode] );
image.scale( f[, mode] );
image.scaleToFit( w, h[, mode] );
image.autocrop([tolerance, frames]);
image.autocrop(options);
image.crop( x, y, w, h );
image.blit( src, x, y, [srcx, srcy, srcw, srch] );
image.composite( src, x, y, [{ mode, opacitySource, opacityDest }] );
image.mask( src, x, y );
image.convolute( kernel );
image.flip( horz, vert );
image.mirror( horz, vert );
image.rotate( deg[, mode] );
image.brightness( val );
image.contrast( val );
image.dither565();
image.greyscale();
image.invert();
image.normalize();
image.hasAlpha();
image.fade( f );
image.opacity( f );
image.opaque();
image.background( hex );
image.gaussian( r );
image.blur( r );
image.posterize( n );
image.sepia();
image.pixelate( size[, x, y, w, h ]);
image.displace( map, offset );
Some of these methods are irreversible, so it can be useful to perform them on a clone of the original image:
image.clone();
(Contributions of more methods are welcome!)
Resize modes
The default resizing algorithm uses a bilinear method as follows:
image.resize(250, 250);
image.resize(Jimp.AUTO, 250);
image.resize(250, Jimp.AUTO);
Optionally, the following constants can be passed to choose a particular resizing algorithm:
Jimp.RESIZE_NEAREST_NEIGHBOR;
Jimp.RESIZE_BILINEAR;
Jimp.RESIZE_BICUBIC;
Jimp.RESIZE_HERMITE;
Jimp.RESIZE_BEZIER;
For example:
image.resize(250, 250, Jimp.RESIZE_BEZIER);
Align modes
The following constants can be passed to the image.cover
, image.contain
and image.print
methods:
Jimp.HORIZONTAL_ALIGN_LEFT;
Jimp.HORIZONTAL_ALIGN_CENTER;
Jimp.HORIZONTAL_ALIGN_RIGHT;
Jimp.VERTICAL_ALIGN_TOP;
Jimp.VERTICAL_ALIGN_MIDDLE;
Jimp.VERTICAL_ALIGN_BOTTOM;
Where the align mode changes the position of the associated axis as described in the table below.
Align Mode | Axis Point |
---|
Jimp.HORIZONTAL_ALIGN_LEFT | Positions the x-axis at the left of the image |
Jimp.HORIZONTAL_ALIGN_CENTER | Positions the x-axis at the center of the image |
Jimp.HORIZONTAL_ALIGN_RIGHT | Positions the x-axis at the right of the image |
Jimp.VERTICAL_ALIGN_TOP | Positions the y-axis at the top of the image |
Jimp.VERTICAL_ALIGN_MIDDLE | Positions the y-axis at the center of the image |
Jimp.VERTICAL_ALIGN_BOTTOM | Positions the y-axis at the bottom of the image |
For example:
image.contain(250, 250, Jimp.HORIZONTAL_ALIGN_LEFT | Jimp.VERTICAL_ALIGN_TOP);
Default align modes for image.cover
and image.contain
are:
Jimp.HORIZONTAL_ALIGN_CENTER | Jimp.VERTICAL_ALIGN_MIDDLE;
Default align modes for image.print
are:
{
alignmentX: Jimp.HORIZONTAL_ALIGN_LEFT,
alignmentY: Jimp.VERTICAL_ALIGN_TOP
}
Compositing and blend modes
The following modes can be used for compositing two images together. mode defaults to Jimp.BLEND_SOURCE_OVER.
Jimp.BLEND_SOURCE_OVER;
Jimp.BLEND_DESTINATION_OVER;
Jimp.BLEND_MULTIPLY;
Jimp.BLEND_SCREEN;
Jimp.BLEND_OVERLAY;
Jimp.BLEND_DARKEN;
Jimp.BLEND_LIGHTEN;
Jimp.BLEND_HARDLIGHT;
Jimp.BLEND_DIFFERENCE;
Jimp.BLEND_EXCLUSION;
image.composite(srcImage, 100, 0, {
mode: Jimp.BLEND_MULTIPLY,
opacitySource: 0.5,
opacityDest: 0.9
});
Writing text
Jimp supports basic typography using BMFont format (.fnt) even ones in different languages! Just find a bitmap font that is suitable bitmap fonts:
Jimp.loadFont(pathOrURL).then(font => {
image.print(font, x, y, message);
image.print(font, x, y, message, maxWidth);
});
Alignment modes are supported by replacing the str
argument with an object containing text
, alignmentX
and alignmentY
. alignmentX
defaults to Jimp.HORIZONTAL_ALIGN_LEFT
and alignmentY
defaults to Jimp.VERTICAL_ALIGN_TOP
.
Jimp.loadFont(pathOrURL).then(font => {
image.print(
font,
x,
y,
{
text: 'Hello world!',
alignmentX: Jimp.HORIZONTAL_ALIGN_CENTER,
alignmentY: Jimp.VERTICAL_ALIGN_MIDDLE
},
maxWidth,
maxHeight
);
});
Jimp.loadFont(path, cb);
BMFont fonts are raster based and fixed in size and colour. Jimp comes with a set of fonts that can be used on images:
Jimp.FONT_SANS_8_BLACK;
Jimp.FONT_SANS_10_BLACK;
Jimp.FONT_SANS_12_BLACK;
Jimp.FONT_SANS_14_BLACK;
Jimp.FONT_SANS_16_BLACK;
Jimp.FONT_SANS_32_BLACK;
Jimp.FONT_SANS_64_BLACK;
Jimp.FONT_SANS_128_BLACK;
Jimp.FONT_SANS_8_WHITE;
Jimp.FONT_SANS_16_WHITE;
Jimp.FONT_SANS_32_WHITE;
Jimp.FONT_SANS_64_WHITE;
Jimp.FONT_SANS_128_WHITE;
These can be used as follows:
Jimp.loadFont(Jimp.FONT_SANS_32_BLACK).then(font => {
image.print(font, 10, 10, 'Hello world!');
});
Measuring text
If you need to do calculations on where to place your text jimp provides two methods that measure how wide and how tall a piece of text will be. You can use these methods to lay out multiple pieces of text in relation to each other
Jimp.measureText(Jimp.FONT_SANS_32_BLACK, 'Some string');
Jimp.measureTextHeight(Jimp.FONT_SANS_32_BLACK, 'Some string', 100);
Staggering Text
If you need to stagger text position along the x or y-axis the print method also returns the final coordinates as an argument to the callback.
Jimp.loadFont(Jimp.FONT_SANS_32_BLACK).then(font => {
image.print(
font,
10,
10,
'Hello world that wraps!',
50,
(err, image, { x, y }) => {
image.print(font, x, y + 20, 'More text on another line', 50);
}
);
});
Online tools are also available to convert TTF fonts to BMFont format. They can be used to create color font or sprite packs.
:star: Littera
:star: Hiero
Writing to files and buffers
Writing to files
The image can be written to disk in PNG, JPEG or BMP format (based on the save path extension or if no extension is provided the original image's MIME type which, if not available, defaults to PNG) using:
image.write(path, cb);
image.writeAsync(path);
The original extension for an image (or "png") can accessed as using image.getExtension()
. The following will save an image using its original format:
var file = 'new_name.' + image.getExtension();
var file = 'new_name';
image.write(file);
Writing to Buffers
A PNG, JPEG or BMP binary Buffer of an image (e.g. for storage in a database) can be generated using:
image.getBuffer(mime, cb);
image.getBufferAsync(mime);
For convenience, supported MIME types are available as static properties:
Jimp.MIME_PNG;
Jimp.MIME_JPEG;
Jimp.MIME_BMP;
If Jimp.AUTO
is passed as the MIME type then the original MIME type for the image (or "image/png") will be used. Alternatively, image.getMIME()
will return the original MIME type of the image (or "image/png").
Data URI
A Base64 data URI can be generated in the same way as a Buffer, using:
image.getBase64(mime, cb);
image.getBase64Async(mime);
PNG and JPEG quality
The quality of JPEGs can be set with:
image.quality(n);
The format of PNGs can be set with:
image.rgba(bool);
image.filterType(number);
image.deflateLevel(number);
Jimp.deflateStrategy(number);
For convenience, supported filter types are available as static properties:
Jimp.PNG_FILTER_AUTO;
Jimp.PNG_FILTER_NONE;
Jimp.PNG_FILTER_SUB;
Jimp.PNG_FILTER_UP;
Jimp.PNG_FILTER_AVERAGE;
Jimp.PNG_FILTER_PATH;
Advanced usage
Colour manipulation
Jimp supports advanced colour manipulation using a single method as follows:
image.color([
{ apply: 'hue', params: [-90] },
{ apply: 'lighten', params: [50] },
{ apply: 'xor', params: ['#06D'] }
]);
The method supports the following modifiers:
Modifier | Description |
---|
lighten {amount} | Lighten the color a given amount, from 0 to 100. Providing 100 will always return white (works through TinyColor) |
brighten {amount} | Brighten the color a given amount, from 0 to 100 (works through TinyColor) |
darken {amount} | Darken the color a given amount, from 0 to 100. Providing 100 will always return black (works through TinyColor) |
desaturate {amount} | Desaturate the color a given amount, from 0 to 100. Providing 100 will is the same as calling greyscale (works through TinyColor) |
saturate {amount} | Saturate the color a given amount, from 0 to 100 (works through TinyColor) |
greyscale {amount} | Completely desaturates a color into greyscale (works through TinyColor) |
spin {degree} | Spin the hue a given amount, from -360 to 360. Calling with 0, 360, or -360 will do nothing - since it sets the hue back to what it was before. (works through TinyColor) |
hue {degree} | Alias for spin |
mix {color, amount} | Mixes colors by their RGB component values. Amount is opacity of overlaying color |
tint {amount} | Same as applying mix with white color |
shade {amount} | Same as applying mix with black color |
xor {color} | Treats the two colors as bitfields and applies an XOR operation to the red, green, and blue components |
red {amount} | Modify Red component by a given amount |
green {amount} | Modify Green component by a given amount |
blue {amount} | Modify Blue component by a given amount |
Convolution matrix
Sum neighbor pixels weighted by the kernel matrix. You can find a nice explanation with examples at GIMP's Convolution Matrix plugin
Implement emboss effect:
image.convolute([[-2, -1, 0], [-1, 1, 1], [0, 1, 2]]);
Low-level manipulation
Jimp enables low-level manipulation of images in memory through the bitmap property of each Jimp object:
image.bitmap.data;
image.bitmap.width;
image.bitmap.height;
This data can be manipulated directly, but remember: garbage in, garbage out.
A helper method is available to scan a region of the bitmap:
image.scan(x, y, w, h, f);
Example usage:
image.scan(0, 0, image.bitmap.width, image.bitmap.height, function(x, y, idx) {
var red = this.bitmap.data[idx + 0];
var green = this.bitmap.data[idx + 1];
var blue = this.bitmap.data[idx + 2];
var alpha = this.bitmap.data[idx + 3];
});
If you need to do something with the image at the end of the scan:
image.scan(0, 0, image.bitmap.width, image.bitmap.height, function(x, y, idx) {
if (x == image.bitmap.width - 1 && y == image.bitmap.height - 1) {
}
});
It's possible to make an iterator scan with a for ... of
, if you want to break
the scan before it reaches the end, but note, that this iterator has a huge performance implication:
for (const { x, y, idx, image } of image.scanIterator(
0,
0,
image.bitmap.width,
image.bitmap.height
)) {
}
A helper to locate a particular pixel within the raw bitmap buffer:
image.getPixelIndex(x, y);
One of the following may be optionally passed as a third parameter to indicate a strategy for x, y positions that are outside of boundaries of the image:
Jimp.EDGE_EXTEND = 1;
Jimp.EDGE_WRAP = 2;
Jimp.EDGE_CROP = 3;
Alternatively, you can manipulate individual pixels using the following these functions:
image.getPixelColor(x, y);
image.setPixelColor(hex, x, y);
Two static helper functions exist to convert RGBA values into single integer (hex) values:
Jimp.rgbaToInt(r, g, b, a);
Jimp.intToRGBA(hex);
You can convert a css color (Hex, RGB, RGBA, HSL, HSLA, HSV, HSVA, Named) to its hexadecimal equivalent:
Jimp.cssColorToHex(cssColor);
Creating new images
If you want to begin with an empty Jimp image, you can call the Jimp constructor passing the width and height of the image to create and a Node-style callback:
new Jimp(256, 256, (err, image) => {
});
You can optionally set the pixel colour as follows:
new Jimp(256, 256, 0xff0000ff, (err, image) => {
});
Or you can use a css color format:
new Jimp(256, 256, '#FF00FF', (err, image) => {
});
You can also initialize a new Jimp image with a raw image buffer:
new Jimp({ data: buffer, width: 1280, height: 768 }, (err, image) => {
});
This can be useful for interoperating with other image processing libraries. buffer
is expected to be four-channel (rgba) image data.
Comparing images
To generate a perceptual hash of a Jimp image, based on the pHash algorithm, use:
image.hash();
By default the hash is returned as base 64. The hash can be returned at another base by passing a number from 2 to 64 to the method:
image.hash(2);
There are 18,446,744,073,709,551,615 unique hashes. The hamming distance between the binary representation of these hashes can be used to find similar-looking images.
To calculate the hamming distance between two Jimp images based on their perceptual hash use:
Jimp.distance(image1, image2);
Jimp also allows the diffing of two Jimp images using PixelMatch as follows:
var diff = Jimp.diff(image1, image2, threshold);
diff.image;
diff.percent;
Using a mix of hamming distance and pixel diffing to compare images, the following code has a 99% success rate of detecting the same image from a random sample (with 1% false positives). The test this figure is drawn from attempts to match each image from a sample of 120 PNGs against 120 corresponding JPEGs saved at a quality setting of 60.
var distance = Jimp.distance(png, jpeg);
var diff = Jimp.diff(png, jpeg);
if (distance < 0.15 || diff.percent < 0.15) {
} else {
}
You can also calculate the raw pHash (no padding or custom base). You can then use this in distanceFromHash
to calculate the hash distance from a loaded image.
const hash1 = image1.pHash();
const hash2 = image2.pHash();
image2.distanceFromHash(hash1);
Jimp.compareHashes(hash1, hash2);
Chaining or callbacks
Most instance methods can be chained together, for example as follows:
Jimp.read('lenna.png').then(image => {
image
.greyscale()
.scale(0.5)
.write('lena-half-bw.png');
});
Alternatively, methods can be passed Node-style callbacks:
Jimp.read('lenna.png').then(image => {
image.greyscale((err, image) => {
image.scale(0.5, (err, image) => {
image.write('lena-half-bw.png');
});
});
});
The Node-style callback pattern allows Jimp to be used with frameworks that expect or build on the Node-style callback pattern.