Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
girliemac committed Feb 2, 2017
0 parents commit 1fc1558
Show file tree
Hide file tree
Showing 44 changed files with 2,374 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.env
.DS_Store
node_modules
npm-debug.log
browserify
9 changes: 9 additions & 0 deletions LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
The MIT License (MIT)

Copyright (c) 2017 Tomomi Imura

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
190 changes: 190 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@


![filterous-2](images/filterous-2.png)

# Filterous 2

Filterous 2 is an Instagram-like image manipulation library for Javascript and node.js.

This is a revamped version of Filterous, which was written for JavaScript for browser about 4 years ago.
This version works on both Node.js and browser, and comes with pre-defined Instagram-like filters (with the same filter names and very similar effects).

## Installation

**For Node.js:**

first, this module uses node-canvas, so you need **Cairo** and **Pango**. Please follow the [installation guide here](https://github.com/Automattic/node-canvas/wiki/_pages) before started.

```bash
$ npm install filterous
```

**For Browser:**

```html
<script src="filterous2.min.js"></script>
```

The minified JavaScript code is available on Release page.


## Usage

The usages are slightly different for Node.js and the browser.

### Basic Usage for Node.js

Import an image buffer to `filterous` then `save` to the disk.

```javascript
const filterous = require('filterous');

filterous.importImage(buffer, options)
.applyFilter(filter, value)
.save(filename);
```

also:

```javascript
filterous.importImage(buffer)
.applyInstaFilter(filterName, options)
.save(filename);
```

The `applyFilter()` can be used with other filters and the results are accumulative, while
the predefined `applyInstaFilter()` overwrite the previous filter result.
However you can use `applyFilter()` to adjust the colors after `applyInstaFilter()` is applied.

Options are:

```javascript
{
scale: <value>,
format: <imageFormat>
}
```
The value must be less than 1. You can only scale down an image.
and the imageFormat is either 'png', 'gif', or 'jpeg' (default).

### Example for Node.js

Using color adjustment filters:

```javascript
fs.readFile('input/leia.jpg', (err, buffer) => {
if (err) throw err;
let f = filterous.importImage(buffer)
.applyFilter('brightness', 0.2)
.applyFilter('colorFilter', [255, 255, 0, 0.05])
.save('output/leia.jpg');
});
```

Example with predefined Instagram-like effects:

```javascript
fs.readFile('input/leia.jpg', (err, buffer) => {
let f = filterous.importImage(buffer, {scale: 0.5, format: 'png'})
.applyInstaFilter('amaro')
.save('output/leia.jpg');
});

```

### Basic Usage for JavaScript on Browser

Import an image object to `filterous` and render as HTML with `renderHtml`.

```javascript
filterous.importImage(imgObj, options)
.applyFilter(filter, value)
.renderHtml(imageDOM);
```
also:

```javascript
filterous.importImage(imgObj, options)
.applyInstaFilter(filterName)
.renderHtml(imageDOM);
```


```javascript
var imageDOM = document.querySelector('img.photo');
var imgObj = new Image();
imgObj.src = 'input/leia.jpg';

filterous.importImage(imgObj, options)
.applyFilter('brightness', 0.2)
.applyFilter('contrast', -0.3)
.renderHtml(imageDOM);
```
Example with predefined Instagram-like effects:

```javascript
filterous.importImage(imgObj, options)
.applyInstaFilter(filterButton.id)
.renderHtml(imageDOM);
```

## Available Filter Effects and the Values

Most effects take a value (the amount of the effects) between -1 and 1.
for example, the value for the `brightness()` 0 means unchanged, -1 darkens the image, and 1 means full-brightness. The image will turn almost completely white.


| Effect | Adjestment(s) |
| ------------- | ------------------------------- |
| `grayscale` | N/A |
| `sepia` | 0 to 1 |
| `invert` | N/A |
| `brightness` | -1 to 1 |
| `saturation` | -1 to 1 |
| `contrast` | -1 to 1 |
| `rgbAdjust` | [r, g, b] |
| `colorFilter` | [r, g, b, adj] // adj is 0 to 1 |
| `convolute` | 3x3 matrix |


## Available InstaFilter Names

| Names | | | | | |
| -------- | --------- | --------- | ------- | -------- | --------- |
| normal | clarendon | gingham | moon | lark | reyes |
| juno | slumber | crema | ludwig | aden | perpetua |
| amaro | mayfair | rise | hudson | valencia | xpro2 |
| sierra | willow | lofi | inkwell | hefe | nashville |
| stinson | vesper | earlybird | brannan | sutro | toaster |
| walden | 1977 | kelvin | maven | ginza | skyline |
| dogpatch | brooklyn | helena | ashby | charmes | |

Note: `normal` gives no filter effect. It normalize the image to the original.

## Demo
[Try the demo on browser!](https://girliemac.github.io/filterous-2/demo-browser)


## Behind the Scene

Filterous takes an image into a `canvas` to manipulate the pixels of the image. Unlike the CSS filters that alters how the image appearance only on browsers, the JavaScript library actually alters the pixel color values. So you can actually download the modified image.

The `CanvasRenderingContext.getImageData()` method of the Canvas API returns an `ImageData` object representing the underlying pixel data of the canvas, and the `data` property of `pixelData` stores the color info of an each pixel in the canvas. (The diagram below shows a canvas size of only 9x9 pixel to make it simple).

Each pixel in the data array consists of 4 bytes values- red, green, blue, and alpha channel, and each of the R (red), G (green), B (blue) and A (alpha transparency) values can take values between 0 and 255.

![canvas image manipulation](images/canvas-pixels.png)

This library alters R, G, or B values of each pixel (yes, each pixel in the entire image! so the operation can be quite slow with JavaScript!) to get filtered look.



## Browser Supports

Filterous 2 for browsers should support all the modern browsers that [supports Promises](http://caniuse.com/#feat=promises).



## Contribute

I am pretty sure this library is buggy. Please feel free to send me pull requests.
158 changes: 158 additions & 0 deletions demo-browser/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
(function() {
/* DOM */
var imageDOM = document.getElementById('photo');
var caption = document.getElementById('caption');
var input = document.querySelector('input[type=text]');
var upload = document.querySelector('input[type=file]');
var errorText = document.querySelector('.error');
var loader = document.getElementById('loader');

var willScale = false;
var scaleFactor = 1;

/* Page and image setup */
var currentImage = '';

var photos = {
bubble: {
caption: 'Soap Bubble',
url: 'images/bubble.jpg'
},
sf: {
caption: 'SF Bay Bridge',
url: 'images/sf.jpg'
},
bride: {
caption: 'विवाह',
url: 'images/bride.jpg'
},
latte: {
caption: 'Caffè latte',
url: 'images/latte.jpg'
},
cats: {
caption: 'Kitties',
url: 'images/cats.jpg'
}
};

if(location.hash === '') { // default
setImage('cats');
} else {
var imageName = location.hash.substr(1);
setImage(imageName);
}

window.addEventListener('hashchange', function(e) {
var imageName = location.hash.substr(1);
setImage(imageName);
}, false);

input.addEventListener('keyup', function(e) {
if (e.keyCode === 13) {
if(input.value === '') return;
errorText.textContent = '';
caption.textContent = 'an image from web';
loadImageFromWeb(input.value);
}
}, false);

upload.addEventListener('change', function(e) {
errorText.textContent = '';
caption.textContent = 'an image from HD';
loadImageFromDisk(e);
}, false);

function setImage(imageName) {
willScale = false;
currentImage = new Image();
currentImage.src = photos[imageName].url;
imageDOM.src = photos[imageName].url;
caption.textContent = photos[imageName].caption;
}

function imageError(error) {
console.log(error)
errorText.innerHTML = 'An invalid URL.<br> Possibly blocked by CORS policy.';
imageDOM.src = 'images/fail.png'
}

function loadImageFromWeb(imageUrl) {
currentImage = new Image();
currentImage.crossOrigin = 'Anonymous';
currentImage.onerror = imageError;
currentImage.onload = function() {
imageDOM.src = imageUrl;
checkImageDimension();
}
currentImage.src = imageUrl;
}

function loadImageFromDisk(event) {
console.log(event)
var reader = new FileReader();
reader.onload = function(e) {
currentImage = new Image();
imageDOM.src = e.target.result;
currentImage.onload = function() {
checkImageDimension();
}
currentImage.src = e.target.result;
};
reader.readAsDataURL(event.target.files[0]);
}

function checkImageDimension() {
if(currentImage.width > 1000 || currentImage.height > 1000) {
willScale = true;
scaleFactor = 1000 / Math.max(currentImage.width, currentImage.height);
} else {
willScale = false;
}
}

function show(loader) {
loader.removeAttribute('hidden');
}

function hide(loader) {
loader.setAttribute('hidden', 'hidden');
}

/* Insta-fy the selected image */

document.getElementById('filterButtons').addEventListener('click', prepFilterEffect, false);

function prepFilterEffect(e) {
show(loader);

var filterButton = getFilterButton(e.target);
if(!filterButton) return;

var options = (willScale) ? {scale: scaleFactor} : null;

var promise = new Promise(function(resolve) {
setTimeout(function() {
var f = filterous.importImage(currentImage, options)
.applyInstaFilter(filterButton.id)
.renderHtml(imageDOM);
resolve(f);
}, 1);
});

promise.then(function() {
hide(loader);
});

}
function getFilterButton(target) {
var button;
if(target.classList.contains('filter')) {
button = target;
} else if (target.parentNode.classList.contains('filter')) {
button = target.parentNode;
}
return button;
}

})();
Loading

0 comments on commit 1fc1558

Please sign in to comment.