Skip to content

Commit dbac4b9

Browse files
Rik Heywoodlovell
authored andcommitted
Add tint operation to set image chroma
1 parent bdac5b5 commit dbac4b9

15 files changed

Lines changed: 172 additions & 23 deletions

docs/api-colour.md

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
### Table of Contents
44

55
- [background][1]
6-
- [greyscale][2]
7-
- [grayscale][3]
8-
- [toColourspace][4]
9-
- [toColorspace][5]
6+
- [tint][2]
7+
- [greyscale][3]
8+
- [grayscale][4]
9+
- [toColourspace][5]
10+
- [toColorspace][6]
1011

1112
## background
1213

@@ -19,10 +20,24 @@ The alpha value is a float between `0` (transparent) and `1` (opaque).
1920

2021
**Parameters**
2122

22-
- `rgba` **([String][6] \| [Object][7])** parsed by the [color][8] module to extract values for red, green, blue and alpha.
23+
- `rgba` **([String][7] \| [Object][8])** parsed by the [color][9] module to extract values for red, green, blue and alpha.
2324

2425

25-
- Throws **[Error][9]** Invalid parameter
26+
- Throws **[Error][10]** Invalid parameter
27+
28+
Returns **Sharp**
29+
30+
## tint
31+
32+
Tint the image using the provided chroma while preserving the image luminance.
33+
An alpha channel may be present and will be unchanged by the operation.
34+
35+
**Parameters**
36+
37+
- `rgb` **([String][7] \| [Object][8])** parsed by the [color][9] module to extract chroma values.
38+
39+
40+
- Throws **[Error][10]** Invalid parameter
2641

2742
Returns **Sharp**
2843

@@ -37,7 +52,7 @@ An alpha channel may be present, and will be unchanged by the operation.
3752

3853
**Parameters**
3954

40-
- `greyscale` **[Boolean][10]** (optional, default `true`)
55+
- `greyscale` **[Boolean][11]** (optional, default `true`)
4156

4257
Returns **Sharp**
4358

@@ -47,7 +62,7 @@ Alternative spelling of `greyscale`.
4762

4863
**Parameters**
4964

50-
- `grayscale` **[Boolean][10]** (optional, default `true`)
65+
- `grayscale` **[Boolean][11]** (optional, default `true`)
5166

5267
Returns **Sharp**
5368

@@ -58,10 +73,10 @@ By default output image will be web-friendly sRGB, with additional channels inte
5873

5974
**Parameters**
6075

61-
- `colourspace` **[String][6]?** output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...][11]
76+
- `colourspace` **[String][7]?** output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...][12]
6277

6378

64-
- Throws **[Error][9]** Invalid parameters
79+
- Throws **[Error][10]** Invalid parameters
6580

6681
Returns **Sharp**
6782

@@ -71,31 +86,33 @@ Alternative spelling of `toColourspace`.
7186

7287
**Parameters**
7388

74-
- `colorspace` **[String][6]?** output colorspace.
89+
- `colorspace` **[String][7]?** output colorspace.
7590

7691

77-
- Throws **[Error][9]** Invalid parameters
92+
- Throws **[Error][10]** Invalid parameters
7893

7994
Returns **Sharp**
8095

8196
[1]: #background
8297

83-
[2]: #greyscale
98+
[2]: #tint
99+
100+
[3]: #greyscale
84101

85-
[3]: #grayscale
102+
[4]: #grayscale
86103

87-
[4]: #tocolourspace
104+
[5]: #tocolourspace
88105

89-
[5]: #tocolorspace
106+
[6]: #tocolorspace
90107

91-
[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
108+
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
92109

93-
[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
110+
[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
94111

95-
[8]: https://www.npmjs.org/package/color
112+
[9]: https://www.npmjs.org/package/color
96113

97-
[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
114+
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error
98115

99-
[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
116+
[11]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
100117

101-
[11]: https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L568
118+
[12]: https://github.com/jcupitt/libvips/blob/master/libvips/iofuncs/enumtypes.c#L568

docs/changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44

55
Requires libvips v8.6.1.
66

7+
#### v0.20.2 - TBD
8+
9+
* Add tint operation to set image chroma.
10+
[#825](https://github.com/lovell/sharp/pull/825)
11+
[@rikh42](https://github.com/rikh42)
12+
713
#### v0.20.1 - 17<sup>th</sup> March 2018
814

915
* Improve installation experience when a globally-installed libvips below the minimum required version is found.

lib/colour.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,21 @@ function background (rgba) {
3838
return this;
3939
}
4040

41+
/**
42+
* Tint the image using the provided chroma while preserving the image luminance.
43+
* An alpha channel may be present and will be unchanged by the operation.
44+
*
45+
* @param {String|Object} rgb - parsed by the [color](https://www.npmjs.org/package/color) module to extract chroma values.
46+
* @returns {Sharp}
47+
* @throws {Error} Invalid parameter
48+
*/
49+
function tint (rgb) {
50+
const colour = color(rgb);
51+
this.options.tintA = colour.a();
52+
this.options.tintB = colour.b();
53+
return this;
54+
}
55+
4156
/**
4257
* Convert to 8-bit greyscale; 256 shades of grey.
4358
* This is a linear operation. If the input image is in a non-linear colour space such as sRGB, use `gamma()` with `greyscale()` for the best results.
@@ -95,6 +110,7 @@ module.exports = function (Sharp) {
95110
// Public instance functions
96111
[
97112
background,
113+
tint,
98114
greyscale,
99115
grayscale,
100116
toColourspace,

lib/constructor.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ const Sharp = function (input, options) {
151151
fastShrinkOnLoad: true,
152152
// operations
153153
background: [0, 0, 0, 255],
154+
tintA: 0,
155+
tintB: 0,
154156
flatten: false,
155157
negate: false,
156158
medianSize: 0,

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@
4545
"Kenric D'Souza <kenric.dsouza@gmail.com>",
4646
"Oleh Aleinyk <oleg.aleynik@gmail.com>",
4747
"Marcel Bretschneider <marcel.bretschneider@gmail.com>",
48-
"Andrea Bianco <andrea.bianco@unibas.ch>"
48+
"Andrea Bianco <andrea.bianco@unibas.ch>",
49+
"Rik Heywood <rik@rik.org>"
4950
],
5051
"scripts": {
5152
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",

src/operations.cc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,32 @@ namespace sharp {
152152
return dst.bandjoin(mask.cast(dst.format()));
153153
}
154154

155+
/*
156+
* Tint an image using the specified chroma, preserving the original image luminance
157+
*/
158+
VImage Tint(VImage image, double const a, double const b) {
159+
// Get original colourspace
160+
VipsInterpretation typeBeforeTint = image.interpretation();
161+
if (typeBeforeTint == VIPS_INTERPRETATION_RGB) {
162+
typeBeforeTint = VIPS_INTERPRETATION_sRGB;
163+
}
164+
// Create 2 band image with every pixel set to the tint chroma
165+
std::vector<double> chromaPixel {a, b};
166+
VImage chroma = image.new_from_image(chromaPixel);
167+
// Extract luminance
168+
VImage luminance = image.colourspace(VIPS_INTERPRETATION_LAB)[0];
169+
// Create the tinted version by combining the L from the original and the chroma from the tint
170+
VImage tinted = luminance.bandjoin(chroma).colourspace(typeBeforeTint);
171+
// Attach original alpha channel, if any
172+
if (HasAlpha(image)) {
173+
// Extract original alpha channel
174+
VImage alpha = image[image.bands() - 1];
175+
// Join alpha channel to normalised image
176+
tinted = tinted.bandjoin(alpha);
177+
}
178+
return tinted;
179+
}
180+
155181
/*
156182
* Stretch luminance to cover full dynamic range.
157183
*/

src/operations.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ namespace sharp {
4646
*/
4747
VImage Cutout(VImage src, VImage dst, const int gravity);
4848

49+
/*
50+
* Tint an image using the specified chroma, preserving the original image luminance
51+
*/
52+
VImage Tint(VImage image, double const a, double const b);
53+
4954
/*
5055
* Stretch luminance to cover full dynamic range.
5156
*/

src/pipeline.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,11 @@ class PipelineWorker : public Nan::AsyncWorker {
682682
image = sharp::Bandbool(image, baton->bandBoolOp);
683683
}
684684

685+
// Tint the image
686+
if (baton->tintA > 0 || baton->tintB > 0) {
687+
image = sharp::Tint(image, baton->tintA, baton->tintB);
688+
}
689+
685690
// Extract an image channel (aka vips band)
686691
if (baton->extractChannel > -1) {
687692
if (baton->extractChannel >= image.bands()) {
@@ -1167,6 +1172,9 @@ NAN_METHOD(pipeline) {
11671172
for (unsigned int i = 0; i < 4; i++) {
11681173
baton->background[i] = AttrTo<double>(background, i);
11691174
}
1175+
// Tint chroma
1176+
baton->tintA = AttrTo<double>(options, "tintA");
1177+
baton->tintB = AttrTo<double>(options, "tintB");
11701178
// Overlay options
11711179
if (HasAttr(options, "overlay")) {
11721180
baton->overlay = CreateInputDescriptor(AttrAs<v8::Object>(options, "overlay"), buffersToPersist);

src/pipeline.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ struct PipelineBaton {
7070
std::string kernel;
7171
bool fastShrinkOnLoad;
7272
double background[4];
73+
double tintA;
74+
double tintB;
7375
bool flatten;
7476
bool negate;
7577
double blurSigma;
@@ -155,6 +157,8 @@ struct PipelineBaton {
155157
cropOffsetLeft(0),
156158
cropOffsetTop(0),
157159
premultiplied(false),
160+
tintA(0.0),
161+
tintB(0.0),
158162
flatten(false),
159163
negate(false),
160164
blurSigma(0.0),
6.83 MB
Loading

0 commit comments

Comments
 (0)