Skip to content

Commit

Permalink
Version 2.1.6
Browse files Browse the repository at this point in the history
- Added `exposure()` filter.
- Added `lighting()` filter, with shadows and highlights.
  • Loading branch information
jhuckaby committed Mar 14, 2023
1 parent 0122683 commit b82e813
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 2 deletions.
47 changes: 47 additions & 0 deletions README.md
Expand Up @@ -897,6 +897,32 @@ This would *increase* the canvas size by 100px horizontally and vertically, esse

Note that expanding regenerates the underlying canvas object. It effectively creates a new canvas at the expanded size, then copies the source into the destination, then discards the source.

### exposure

**Live Demo:** [Exposure](https://jhuckaby.github.io/canvas-plus-playground/?t=load/eyJpbWFnZSI6ImtpdHRlbi5qcGcifQ%3D%3D&t=exposure/eyJhbW91bnQiOjI1fQ%3D%3D&f=png)

The `exposure()` method simulates increasing or decreasing the camera film exposure. Interally, this is accomplished by a special [curve](#curves). To use, specify an `amount` between -100 (darken) and 100 (lighten). The method accepts an object containing the following properties:

| Property Name | Type | Description |
|---------------|------|-------------|
| `amount` | Integer | Exposure adjustment, from -100 (darken) to 100 (lighten). |
| `channels` | String | Which channels to apply the filter to, defaults to `rgb`. See [Channels](#channels). |
| `clip` | Object | Optional clipping rectangle (see [Clipping](#clipping) below). |

Example use:

```js
canvas.exposure({
"amount": 25
});
```

Note that if `amount` is the sole argument, you can simply pass the number itself:

```js
canvas.exposure( 25 );
```

### findEdges

**Live Demo:** [Find Edges](https://jhuckaby.github.io/canvas-plus-playground/?t=load/eyJpbWFnZSI6InN1bnNldC5qcGcifQ%3D%3D&t=resize/eyJ3aWR0aCI6NjQwLCJoZWlnaHQiOjQ4MCwibW9kZSI6ImZpdCJ9&t=findEdges/e30%3D&f=png)
Expand Down Expand Up @@ -1019,6 +1045,27 @@ canvas.curves({
});
```

### lighting

**Live Demo:** [Shadow Detail](https://jhuckaby.github.io/canvas-plus-playground/?t=load/eyJpbWFnZSI6InN1bnNldC5qcGcifQ%3D%3D&t=lighting/eyJzaGFkb3dzIjo1MH0%3D&f=png)

The `lighting()` method can adjust both shadows and highlights, to bring out hidden details. Interally, this is accomplished by a special multi-point [curve](#curves). The method accepts an object containing the following properties:

| Property Name | Type | Description |
|---------------|------|-------------|
| `shadows` | Integer | Shadow detail adjustment, from -100 (darken) to 100 (lighten). |
| `highlights` | Integer | Highlight detail adjustment, from -100 (darken) to 100 (lighten). |
| `channels` | String | Which channels to apply the filter to, defaults to `rgb`. See [Channels](#channels). |
| `clip` | Object | Optional clipping rectangle (see [Clipping](#clipping) below). |

Example use:

```js
canvas.lighting({
"shadows": 25
});
```

### mask

**Live Demo:** [Apply Mask](https://jhuckaby.github.io/canvas-plus-playground/?t=load/eyJpbWFnZSI6IndhdGVyZmFsbC5qcGcifQ%3D%3D&t=resize/eyJ3aWR0aCI6NjQwLCJoZWlnaHQiOjQ4MCwibW9kZSI6ImZpdCJ9&t=mask/e30%3D&f=png)
Expand Down
79 changes: 78 additions & 1 deletion canvas-plus.js
Expand Up @@ -1554,6 +1554,83 @@ module.exports = Class.create({
return this.curves(opts);
},

lighting: function(opts) {
// apply lighting functions, e.g. shadows, highlights
// opts: { shadows, highlights, channels, clip }
opts = this.copyHash( opts || {} );
var curve = [ [0,0], [63,63], [191,191], [255,255] ];

// shadows
if (opts.shadows) {
curve[1][1] += opts.shadows;
}

// highlights
if (opts.highlights) {
curve[2][1] += opts.highlights;
}

for (var idx = 0, len = curve.length; idx < len; idx++) {
curve[idx][1] = Math.max(0, Math.min(255, curve[idx][1]) );
}

var channels = opts.channels || 'rgb';
delete opts.channels;

if (channels.match(/rgb/i)) opts.rgb = curve;
else {
if (channels.match(/r/i)) opts.red = curve;
if (channels.match(/g/i)) opts.green = curve;
if (channels.match(/b/i)) opts.blue = curve;
}
if (channels.match(/a/i)) opts.alpha = curve;

return this.curves(opts);
},

exposure: function(opts) {
// apply exposure adjustment
// opts: { amount, channels, clip }
if (typeof(opts) == 'number') {
opts = { amount: opts };
}
opts = this.copyHash( opts || {} );
var curve = [ 0, 63, 127, 191, 255 ];

// exposure
if (opts.amount) {
if (opts.amount > 0) {
curve[1] += (opts.amount * 2);
curve[2] += (opts.amount * 3);
curve[3] += (opts.amount * 4);
}
else {
opts.amount /= 2;
curve[4] += (opts.amount * 5);
curve[3] += (opts.amount * 4);
curve[2] += (opts.amount * 3);
curve[1] += (opts.amount * 2);
}
}

for (var idx = 0, len = curve.length; idx < len; idx++) {
curve[idx] = Math.max(0, Math.min(255, curve[idx]) );
}

var channels = opts.channels || 'rgb';
delete opts.channels;

if (channels.match(/rgb/i)) opts.rgb = curve;
else {
if (channels.match(/r/i)) opts.red = curve;
if (channels.match(/g/i)) opts.green = curve;
if (channels.match(/b/i)) opts.blue = curve;
}
if (channels.match(/a/i)) opts.alpha = curve;

return this.curves(opts);
},

generateCurve: function(points) {
// Generate curve from points using monotone cubic interpolation.
// This is somewhat like Adobe Photoshop's 'Curves' filter.
Expand Down Expand Up @@ -3765,7 +3842,7 @@ module.exports = Class.create({
this.logDebug(6, "Compressing into WebP format", opts );

this.webp.encode( imgData, opts, function(err, data) {
if (err) return doError('webp', "WebP Encode Error: " + err, callback);
if (err) return self.doError('webp', "WebP Encode Error: " + err, callback);
self.logDebug(6, "WebP compression complete");
callback(null, data);
} );
Expand Down
77 changes: 77 additions & 0 deletions lib/filter/curves.js
Expand Up @@ -302,6 +302,83 @@ module.exports = Class.create({
return this.curves(opts);
},

lighting: function(opts) {
// apply lighting functions, e.g. shadows, highlights
// opts: { shadows, highlights, channels, clip }
opts = this.copyHash( opts || {} );
var curve = [ [0,0], [63,63], [191,191], [255,255] ];

// shadows
if (opts.shadows) {
curve[1][1] += opts.shadows;
}

// highlights
if (opts.highlights) {
curve[2][1] += opts.highlights;
}

for (var idx = 0, len = curve.length; idx < len; idx++) {
curve[idx][1] = Math.max(0, Math.min(255, curve[idx][1]) );
}

var channels = opts.channels || 'rgb';
delete opts.channels;

if (channels.match(/rgb/i)) opts.rgb = curve;
else {
if (channels.match(/r/i)) opts.red = curve;
if (channels.match(/g/i)) opts.green = curve;
if (channels.match(/b/i)) opts.blue = curve;
}
if (channels.match(/a/i)) opts.alpha = curve;

return this.curves(opts);
},

exposure: function(opts) {
// apply exposure adjustment
// opts: { amount, channels, clip }
if (typeof(opts) == 'number') {
opts = { amount: opts };
}
opts = this.copyHash( opts || {} );
var curve = [ 0, 63, 127, 191, 255 ];

// exposure
if (opts.amount) {
if (opts.amount > 0) {
curve[1] += (opts.amount * 2);
curve[2] += (opts.amount * 3);
curve[3] += (opts.amount * 4);
}
else {
opts.amount /= 2;
curve[4] += (opts.amount * 5);
curve[3] += (opts.amount * 4);
curve[2] += (opts.amount * 3);
curve[1] += (opts.amount * 2);
}
}

for (var idx = 0, len = curve.length; idx < len; idx++) {
curve[idx] = Math.max(0, Math.min(255, curve[idx]) );
}

var channels = opts.channels || 'rgb';
delete opts.channels;

if (channels.match(/rgb/i)) opts.rgb = curve;
else {
if (channels.match(/r/i)) opts.red = curve;
if (channels.match(/g/i)) opts.green = curve;
if (channels.match(/b/i)) opts.blue = curve;
}
if (channels.match(/a/i)) opts.alpha = curve;

return this.curves(opts);
},

generateCurve: function(points) {
// Generate curve from points using monotone cubic interpolation.
// This is somewhat like Adobe Photoshop's 'Curves' filter.
Expand Down
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "pixl-canvas-plus",
"version": "2.1.5",
"version": "2.1.6",
"description": "A universal library for manipulating images, built on canvas.",
"author": "Joseph Huckaby <jhuckaby@gmail.com>",
"homepage": "https://github.com/jhuckaby/canvas-plus",
Expand Down

0 comments on commit b82e813

Please sign in to comment.