Skip to content

Commit a38051b

Browse files
author
Alexandru Badiu
committed
feat(build): Initial code and build changes.
Initial import of the code and updates to the build system.
1 parent 2fc5e54 commit a38051b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+5646
-7
lines changed

.babelrc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"presets": [
3+
["env", {
4+
"targets": {
5+
"node": true
6+
}
7+
}]
8+
]
9+
}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,4 @@ typings/
5757
# dotenv environment variables file
5858
.env
5959

60+
.DS_Store

README.md

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
[![Stories in Ready](https://badge.waffle.io/voidberg/imagecache.png?label=ready&title=Ready)](https://waffle.io/voidberg/imagecache)
2+
[![Latest release on NPM](https://img.shields.io/npm/v/imagecache.svg)](https://www.npmjs.com/package/imagecache)
3+
[![Codeship Status for voidberg/imagecache](https://img.shields.io/codeship/0de31e00-4b12-0133-f672-7236a2d50232.svg)](https://codeship.com/projects/106113)
4+
[![codecov.io](http://codecov.io/github/voidberg/imagecache/coverage.svg?branch=master)](http://codecov.io/github/voidberg/imagecache?branch=master)
5+
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
6+
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
7+
[![David-dm.org](https://david-dm.org/voidberg/imagecache.svg)](https://david-dm.org/voidberg/imagecache#info=dependencies&view=table)
8+
[![David-dm.org](https://david-dm.org/voidberg/imagecache/dev-status.svg)](https://david-dm.org/voidberg/imagecache#info=devDependencies&view=table)
9+
[![MIT License](https://img.shields.io/npm/l/imagecache.svg)](https://opensource.org/licenses/MIT)
10+
11+
#Imagecache
12+
---
13+
14+
## What is it?
15+
16+
Node image generation module based on CamanJS and inspired by Drupal's image styles.
17+
18+
## Installation
19+
20+
### Linux prerequisites
21+
* `aptitude install libcairo-dev libgif-dev libjpeg-dev`
22+
23+
### Mac OS X prerequisites
24+
25+
It's quite painful, be warned.
26+
27+
* Install X Server (http://xquartz.macosforge.org/trac/wiki/X112.7.6).
28+
* Install X Code (https://itunes.apple.com/gb/app/xcode/id497799835?mt=12) and command line tools.
29+
* Install Homebrew (https://github.com/Homebrew/homebrew/wiki/Installation).
30+
* `export PKG_CONFIG_PATH=/opt/X11/lib/pkgconfig`.
31+
* Install Cairo from source: `brew install --build-from-source cairo`.
32+
33+
## Usage
34+
35+
```
36+
var presets = require('./presets.js');
37+
var ImageCache = require('imagecache');
38+
39+
var imagecache = new ImageCache(presets);
40+
41+
imagecache.render('./in.png', 'preset_one', function (err, image) {
42+
// Save the image
43+
image.save('out_s_crop_teaser.png');
44+
45+
// or
46+
47+
// Get a buffer, stream it etc
48+
image.canvas.toBuffer();
49+
})
50+
```
51+
52+
## Presets structure
53+
54+
```
55+
{
56+
preset_one: {
57+
presetname: 'preset_one',
58+
actions: [
59+
{
60+
action: 'scale_and_crop',
61+
config: {
62+
width: 100,
63+
height: 300
64+
}
65+
},
66+
{
67+
action: 'define_canvas',
68+
config: {
69+
color: '#333333',
70+
exact: {
71+
width: 400,
72+
height: 400,
73+
xpos: 'center',
74+
ypos: 'center'
75+
}
76+
}
77+
}
78+
]
79+
},
80+
preset_two: {
81+
presetname: 'preset_two',
82+
actions: [
83+
{
84+
action: 'scale_and_crop',
85+
config: {
86+
width: 70,
87+
height: 70,
88+
}
89+
}
90+
]
91+
}
92+
}
93+
```
94+
95+
### Creating plugins
96+
Breeze is used for plugin management. There are two types of plugins available:
97+
98+
* Utility plugins that add functions to be used by other plugins. For example see `plugins/_converters.js` that adds utility functions for parsing parameters that are used by all other plugins.
99+
* Action plugins that expose one or more functions that implement actions.
100+
101+
For example, a plugin that implements the sharpen action will attach a sharpen function to the plugin registry. The function signature is `image, config, callback` where `image` is the `CamanJS` object, `config` is the action configuration from the preset and `callback` is the function that triggers the completion of the action.
102+
103+
```
104+
var self = module.exports = {
105+
attach: function (options) {
106+
this.sharpen = function (image, config, callback) {
107+
var value = this.convertInt(config.value);
108+
109+
if (value > 100) {
110+
value = 100;
111+
}
112+
if (value < 0) {
113+
value = 0;
114+
}
115+
116+
image.sharpen(value);
117+
callback();
118+
};
119+
}
120+
};
121+
```
122+
123+
##Imagecache actions:
124+
125+
126+
* Brightness []
127+
* Crop []
128+
* Define canvas []
129+
* Desaturate []
130+
* Negative image []
131+
* Resize []
132+
* Scale []
133+
* Scale and crop []
134+
* Sharpen []
135+
* Contrast []
136+
* Clip []
137+
* Colorize []
138+
* Exposure []
139+
* Gamma []
140+
* Hue []
141+
* Noise []
142+
* Saturation []
143+
* Sepia []
144+
* Vibrance []
145+
* Curves []
146+
* Overlay (watermark) []
147+
* CamanJS filters (vintage, lomo, clarity, sinCity, sunrise, crossProcess, orangePeel, love, grungy, jarques, pinhole, oldBoot, glowingSun, hazyDays, herMajesty, nostalgia, hemingway, concentrate) []
148+
149+
Legend:
150+
151+
* [] - Implemented
152+
* [] - Will not be implemented
153+
* [] - Not implemented yet

dist/index.js

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { readdirSync, accessSync, constants } from 'fs';
2+
import { resolve } from 'path';
3+
import sharp from 'sharp';
4+
import async from 'async';
5+
6+
module.exports = class ImageCache {
7+
/**
8+
* constructor() returns a new ImageCache
9+
* instance with a list of presets.
10+
*
11+
* @param {Object} presets
12+
* @return {ImageCache} instance
13+
*/
14+
constructor(presets) {
15+
this.presetsConfiguration = presets;
16+
this.actions = {};
17+
this.pluginNames = [];
18+
19+
readdirSync(resolve(__dirname, './plugins/')).forEach(file => {
20+
let name;
21+
let plugin;
22+
23+
if (file.match(/.+\.js/g) !== null && file !== 'index.js') {
24+
name = file.replace('.js', '');
25+
plugin = require(`./plugins/${file}`); // eslint-disable-line
26+
27+
plugin.attach(this);
28+
this.pluginNames.push(name);
29+
}
30+
});
31+
}
32+
33+
/**
34+
* presets() returns a list
35+
* of all available presets.
36+
*
37+
* @return {Array} list of presets
38+
*/
39+
presets() {
40+
return Object.getOwnPropertyNames(this.presetsConfiguration);
41+
}
42+
43+
/**
44+
* plugins() returns a list
45+
* of all available plugins.
46+
*
47+
* @return {Array} list of plugins
48+
*/
49+
plugins() {
50+
return this.pluginNames;
51+
}
52+
53+
/**
54+
* render() renders an image
55+
* using the supplied preset.
56+
*
57+
* @param {String} image
58+
* @param {String} presetName
59+
* @param {String} callback
60+
*/
61+
render(image, presetName, callback) {
62+
// try {
63+
// accessSync(image, constants.R_OK);
64+
// } catch (imageErr) {
65+
// return callback(new Error(`File ${image} does not exist or is inaccesible.`));
66+
// }
67+
68+
if (!this.presetsConfiguration[presetName]) {
69+
return callback(new Error(`Preset ${presetName} could not be found.`));
70+
}
71+
72+
const preset = this.presetsConfiguration[presetName];
73+
let sharpInstance;
74+
75+
try {
76+
sharpInstance = sharp(image);
77+
} catch (sharpErr) {
78+
callback(sharpErr);
79+
}
80+
81+
let metadata = {};
82+
83+
return sharpInstance.metadata().then(info => {
84+
metadata = info;
85+
86+
async.each(preset.actions, (action, asyncCallback) => {
87+
if (!this.actions[action.action]) {
88+
return asyncCallback(new Error(`Action ${action.action} for preset ${presetName} not found in loaded plugins.`));
89+
}
90+
91+
return this.actions[action.action](sharpInstance, metadata, action.config || {}, (err, processed) => {
92+
sharpInstance = processed;
93+
asyncCallback(err);
94+
});
95+
}, err => callback(err, sharpInstance));
96+
}).catch(err => {
97+
return callback(err);
98+
});
99+
}
100+
};

dist/plugins/_converters.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/* eslint no-bitwise: ["error", { "allow": ["~"] }] */
2+
const S = require('string');
3+
4+
module.exports = {
5+
attach: function attach(app) {
6+
app.actions.convertBoolean = value => S(value).toBool();
7+
8+
app.actions.convertFloat = value => S(value).toFloat();
9+
10+
app.actions.convertInt = value => S(value).toInt();
11+
12+
app.actions.convertDimension = (value, maxDimension) => {
13+
let converted;
14+
15+
if (S(value).endsWith('%')) {
16+
converted = ~~(maxDimension * S(value).replace('%', '').toInt() / 100);
17+
} else {
18+
converted = S(value).toInt();
19+
}
20+
21+
return converted;
22+
};
23+
24+
app.actions.convertPosition = (value, maxPosition, imageSize = 0) => {
25+
let converted;
26+
27+
if (value === 'left' || value === 'top') {
28+
converted = 0;
29+
} else if (value === 'center') {
30+
converted = ~~(maxPosition / 2) - ~~(imageSize / 2);
31+
} else if (value === 'right' || value === 'bottom') {
32+
converted = maxPosition;
33+
} else {
34+
converted = app.actions.convertDimension(value, maxPosition);
35+
}
36+
37+
return converted;
38+
};
39+
}
40+
};

dist/plugins/blur.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module.exports = {
2+
attach: function attach(app) {
3+
app.actions.blur = (image, metadata, config, callback) => {
4+
const blur = config.blur || 1;
5+
6+
return callback(undefined, image.sharpen(blur));
7+
};
8+
}
9+
};

dist/plugins/define_canvas.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import sharp from 'sharp';
2+
3+
module.exports = {
4+
attach: function attach(app) {
5+
app.actions.define_canvas = (image, metadata, config, callback) => {
6+
const canvas = sharp(null, {
7+
create: {
8+
width: config.width,
9+
height: config.height,
10+
channels: config.channels || 4,
11+
background: config.color || '#ffffff00'
12+
}
13+
});
14+
15+
const options = {
16+
gravity: config.gravity,
17+
tile: config.tile || false,
18+
cutout: config.cutout || false
19+
};
20+
21+
return image.toBuffer().then(buffer => callback(undefined, canvas.overlayWith(buffer, options)));
22+
};
23+
}
24+
};

dist/plugins/file.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module.exports = {
2+
attach: function attach(app) {
3+
app.actions.file = (image, metadata, config, callback) => {
4+
const filepath = config.path;
5+
6+
const options = {
7+
gravity: config.gravity,
8+
tile: config.tile || false,
9+
cutout: config.cutout || false
10+
};
11+
12+
return callback(undefined, image.overlayWith(filepath, options));
13+
};
14+
}
15+
};

dist/plugins/flip.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module.exports = {
2+
attach: function attach(app) {
3+
app.actions.flip = (image, metadata, config, callback) => {
4+
const axis = config.axis || 'y';
5+
6+
if (axis === 'y') {
7+
return callback(undefined, image.flip());
8+
}
9+
10+
return callback(undefined, image.flop());
11+
};
12+
}
13+
};

dist/plugins/gamma.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module.exports = {
2+
attach: function attach(app) {
3+
app.actions.gamma = (image, metadata, config, callback) => {
4+
const gamma = config.gamma || 2.2;
5+
6+
return callback(undefined, image.gamma(gamma));
7+
};
8+
}
9+
};

0 commit comments

Comments
 (0)