@revizly/sharp 0.33.2-revizly13

Sign up to get free protection for your applications and to get access to all the features.
package/lib/colour.js ADDED
@@ -0,0 +1,182 @@
1
+ // Copyright 2013 Lovell Fuller and others.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ 'use strict';
5
+
6
+ const color = require('color');
7
+ const is = require('./is');
8
+
9
+ /**
10
+ * Colourspaces.
11
+ * @private
12
+ */
13
+ const colourspace = {
14
+ multiband: 'multiband',
15
+ 'b-w': 'b-w',
16
+ bw: 'b-w',
17
+ cmyk: 'cmyk',
18
+ srgb: 'srgb'
19
+ };
20
+
21
+ /**
22
+ * Tint the image using the provided colour.
23
+ * An alpha channel may be present and will be unchanged by the operation.
24
+ *
25
+ * @example
26
+ * const output = await sharp(input)
27
+ * .tint({ r: 255, g: 240, b: 16 })
28
+ * .toBuffer();
29
+ *
30
+ * @param {string|Object} tint - Parsed by the [color](https://www.npmjs.org/package/color) module.
31
+ * @returns {Sharp}
32
+ * @throws {Error} Invalid parameter
33
+ */
34
+ function tint (tint) {
35
+ this._setBackgroundColourOption('tint', tint);
36
+ return this;
37
+ }
38
+
39
+ /**
40
+ * Convert to 8-bit greyscale; 256 shades of grey.
41
+ * 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.
42
+ * By default the output image will be web-friendly sRGB and contain three (identical) color channels.
43
+ * This may be overridden by other sharp operations such as `toColourspace('b-w')`,
44
+ * which will produce an output image containing one color channel.
45
+ * An alpha channel may be present, and will be unchanged by the operation.
46
+ *
47
+ * @example
48
+ * const output = await sharp(input).greyscale().toBuffer();
49
+ *
50
+ * @param {Boolean} [greyscale=true]
51
+ * @returns {Sharp}
52
+ */
53
+ function greyscale (greyscale) {
54
+ this.options.greyscale = is.bool(greyscale) ? greyscale : true;
55
+ return this;
56
+ }
57
+
58
+ /**
59
+ * Alternative spelling of `greyscale`.
60
+ * @param {Boolean} [grayscale=true]
61
+ * @returns {Sharp}
62
+ */
63
+ function grayscale (grayscale) {
64
+ return this.greyscale(grayscale);
65
+ }
66
+
67
+ /**
68
+ * Set the pipeline colourspace.
69
+ *
70
+ * The input image will be converted to the provided colourspace at the start of the pipeline.
71
+ * All operations will use this colourspace before converting to the output colourspace,
72
+ * as defined by {@link #tocolourspace|toColourspace}.
73
+ *
74
+ * This feature is experimental and has not yet been fully-tested with all operations.
75
+ *
76
+ * @since 0.29.0
77
+ *
78
+ * @example
79
+ * // Run pipeline in 16 bits per channel RGB while converting final result to 8 bits per channel sRGB.
80
+ * await sharp(input)
81
+ * .pipelineColourspace('rgb16')
82
+ * .toColourspace('srgb')
83
+ * .toFile('16bpc-pipeline-to-8bpc-output.png')
84
+ *
85
+ * @param {string} [colourspace] - pipeline colourspace e.g. `rgb16`, `scrgb`, `lab`, `grey16` [...](https://github.com/libvips/libvips/blob/41cff4e9d0838498487a00623462204eb10ee5b8/libvips/iofuncs/enumtypes.c#L774)
86
+ * @returns {Sharp}
87
+ * @throws {Error} Invalid parameters
88
+ */
89
+ function pipelineColourspace (colourspace) {
90
+ if (!is.string(colourspace)) {
91
+ throw is.invalidParameterError('colourspace', 'string', colourspace);
92
+ }
93
+ this.options.colourspacePipeline = colourspace;
94
+ return this;
95
+ }
96
+
97
+ /**
98
+ * Alternative spelling of `pipelineColourspace`.
99
+ * @param {string} [colorspace] - pipeline colorspace.
100
+ * @returns {Sharp}
101
+ * @throws {Error} Invalid parameters
102
+ */
103
+ function pipelineColorspace (colorspace) {
104
+ return this.pipelineColourspace(colorspace);
105
+ }
106
+
107
+ /**
108
+ * Set the output colourspace.
109
+ * By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
110
+ *
111
+ * @example
112
+ * // Output 16 bits per pixel RGB
113
+ * await sharp(input)
114
+ * .toColourspace('rgb16')
115
+ * .toFile('16-bpp.png')
116
+ *
117
+ * @param {string} [colourspace] - output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/libvips/libvips/blob/3c0bfdf74ce1dc37a6429bed47fa76f16e2cd70a/libvips/iofuncs/enumtypes.c#L777-L794)
118
+ * @returns {Sharp}
119
+ * @throws {Error} Invalid parameters
120
+ */
121
+ function toColourspace (colourspace) {
122
+ if (!is.string(colourspace)) {
123
+ throw is.invalidParameterError('colourspace', 'string', colourspace);
124
+ }
125
+ this.options.colourspace = colourspace;
126
+ return this;
127
+ }
128
+
129
+ /**
130
+ * Alternative spelling of `toColourspace`.
131
+ * @param {string} [colorspace] - output colorspace.
132
+ * @returns {Sharp}
133
+ * @throws {Error} Invalid parameters
134
+ */
135
+ function toColorspace (colorspace) {
136
+ return this.toColourspace(colorspace);
137
+ }
138
+
139
+ /**
140
+ * Update a colour attribute of the this.options Object.
141
+ * @private
142
+ * @param {string} key
143
+ * @param {string|Object} value
144
+ * @throws {Error} Invalid value
145
+ */
146
+ function _setBackgroundColourOption (key, value) {
147
+ if (is.defined(value)) {
148
+ if (is.object(value) || is.string(value)) {
149
+ const colour = color(value);
150
+ this.options[key] = [
151
+ colour.red(),
152
+ colour.green(),
153
+ colour.blue(),
154
+ Math.round(colour.alpha() * 255)
155
+ ];
156
+ } else {
157
+ throw is.invalidParameterError('background', 'object or string', value);
158
+ }
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Decorate the Sharp prototype with colour-related functions.
164
+ * @private
165
+ */
166
+ module.exports = function (Sharp) {
167
+ Object.assign(Sharp.prototype, {
168
+ // Public
169
+ tint,
170
+ greyscale,
171
+ grayscale,
172
+ pipelineColourspace,
173
+ pipelineColorspace,
174
+ toColourspace,
175
+ toColorspace,
176
+ // Private
177
+ _setBackgroundColourOption
178
+ });
179
+ // Class attributes
180
+ Sharp.colourspace = colourspace;
181
+ Sharp.colorspace = colourspace;
182
+ };
@@ -0,0 +1,210 @@
1
+ // Copyright 2013 Lovell Fuller and others.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ 'use strict';
5
+
6
+ const is = require('./is');
7
+
8
+ /**
9
+ * Blend modes.
10
+ * @member
11
+ * @private
12
+ */
13
+ const blend = {
14
+ clear: 'clear',
15
+ source: 'source',
16
+ over: 'over',
17
+ in: 'in',
18
+ out: 'out',
19
+ atop: 'atop',
20
+ dest: 'dest',
21
+ 'dest-over': 'dest-over',
22
+ 'dest-in': 'dest-in',
23
+ 'dest-out': 'dest-out',
24
+ 'dest-atop': 'dest-atop',
25
+ xor: 'xor',
26
+ add: 'add',
27
+ saturate: 'saturate',
28
+ multiply: 'multiply',
29
+ screen: 'screen',
30
+ overlay: 'overlay',
31
+ darken: 'darken',
32
+ lighten: 'lighten',
33
+ 'colour-dodge': 'colour-dodge',
34
+ 'color-dodge': 'colour-dodge',
35
+ 'colour-burn': 'colour-burn',
36
+ 'color-burn': 'colour-burn',
37
+ 'hard-light': 'hard-light',
38
+ 'soft-light': 'soft-light',
39
+ difference: 'difference',
40
+ exclusion: 'exclusion'
41
+ };
42
+
43
+ /**
44
+ * Composite image(s) over the processed (resized, extracted etc.) image.
45
+ *
46
+ * The images to composite must be the same size or smaller than the processed image.
47
+ * If both `top` and `left` options are provided, they take precedence over `gravity`.
48
+ *
49
+ * Any resize, rotate or extract operations in the same processing pipeline
50
+ * will always be applied to the input image before composition.
51
+ *
52
+ * The `blend` option can be one of `clear`, `source`, `over`, `in`, `out`, `atop`,
53
+ * `dest`, `dest-over`, `dest-in`, `dest-out`, `dest-atop`,
54
+ * `xor`, `add`, `saturate`, `multiply`, `screen`, `overlay`, `darken`, `lighten`,
55
+ * `colour-dodge`, `color-dodge`, `colour-burn`,`color-burn`,
56
+ * `hard-light`, `soft-light`, `difference`, `exclusion`.
57
+ *
58
+ * More information about blend modes can be found at
59
+ * https://www.libvips.org/API/current/libvips-conversion.html#VipsBlendMode
60
+ * and https://www.cairographics.org/operators/
61
+ *
62
+ * @since 0.22.0
63
+ *
64
+ * @example
65
+ * await sharp(background)
66
+ * .composite([
67
+ * { input: layer1, gravity: 'northwest' },
68
+ * { input: layer2, gravity: 'southeast' },
69
+ * ])
70
+ * .toFile('combined.png');
71
+ *
72
+ * @example
73
+ * const output = await sharp('input.gif', { animated: true })
74
+ * .composite([
75
+ * { input: 'overlay.png', tile: true, blend: 'saturate' }
76
+ * ])
77
+ * .toBuffer();
78
+ *
79
+ * @example
80
+ * sharp('input.png')
81
+ * .rotate(180)
82
+ * .resize(300)
83
+ * .flatten( { background: '#ff6600' } )
84
+ * .composite([{ input: 'overlay.png', gravity: 'southeast' }])
85
+ * .sharpen()
86
+ * .withMetadata()
87
+ * .webp( { quality: 90 } )
88
+ * .toBuffer()
89
+ * .then(function(outputBuffer) {
90
+ * // outputBuffer contains upside down, 300px wide, alpha channel flattened
91
+ * // onto orange background, composited with overlay.png with SE gravity,
92
+ * // sharpened, with metadata, 90% quality WebP image data. Phew!
93
+ * });
94
+ *
95
+ * @param {Object[]} images - Ordered list of images to composite
96
+ * @param {Buffer|String} [images[].input] - Buffer containing image data, String containing the path to an image file, or Create object (see below)
97
+ * @param {Object} [images[].input.create] - describes a blank overlay to be created.
98
+ * @param {Number} [images[].input.create.width]
99
+ * @param {Number} [images[].input.create.height]
100
+ * @param {Number} [images[].input.create.channels] - 3-4
101
+ * @param {String|Object} [images[].input.create.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
102
+ * @param {Object} [images[].input.text] - describes a new text image to be created.
103
+ * @param {string} [images[].input.text.text] - text to render as a UTF-8 string. It can contain Pango markup, for example `<i>Le</i>Monde`.
104
+ * @param {string} [images[].input.text.font] - font name to render with.
105
+ * @param {string} [images[].input.text.fontfile] - absolute filesystem path to a font file that can be used by `font`.
106
+ * @param {number} [images[].input.text.width=0] - integral number of pixels to word-wrap at. Lines of text wider than this will be broken at word boundaries.
107
+ * @param {number} [images[].input.text.height=0] - integral number of pixels high. When defined, `dpi` will be ignored and the text will automatically fit the pixel resolution defined by `width` and `height`. Will be ignored if `width` is not specified or set to 0.
108
+ * @param {string} [images[].input.text.align='left'] - text alignment (`'left'`, `'centre'`, `'center'`, `'right'`).
109
+ * @param {boolean} [images[].input.text.justify=false] - set this to true to apply justification to the text.
110
+ * @param {number} [images[].input.text.dpi=72] - the resolution (size) at which to render the text. Does not take effect if `height` is specified.
111
+ * @param {boolean} [images[].input.text.rgba=false] - set this to true to enable RGBA output. This is useful for colour emoji rendering, or support for Pango markup features like `<span foreground="red">Red!</span>`.
112
+ * @param {number} [images[].input.text.spacing=0] - text line height in points. Will use the font line height if none is specified.
113
+ * @param {String} [images[].blend='over'] - how to blend this image with the image below.
114
+ * @param {String} [images[].gravity='centre'] - gravity at which to place the overlay.
115
+ * @param {Number} [images[].top] - the pixel offset from the top edge.
116
+ * @param {Number} [images[].left] - the pixel offset from the left edge.
117
+ * @param {Boolean} [images[].tile=false] - set to true to repeat the overlay image across the entire image with the given `gravity`.
118
+ * @param {Boolean} [images[].premultiplied=false] - set to true to avoid premultiplying the image below. Equivalent to the `--premultiplied` vips option.
119
+ * @param {Number} [images[].density=72] - number representing the DPI for vector overlay image.
120
+ * @param {Object} [images[].raw] - describes overlay when using raw pixel data.
121
+ * @param {Number} [images[].raw.width]
122
+ * @param {Number} [images[].raw.height]
123
+ * @param {Number} [images[].raw.channels]
124
+ * @param {boolean} [images[].animated=false] - Set to `true` to read all frames/pages of an animated image.
125
+ * @param {string} [images[].failOn='warning'] - @see {@link /api-constructor#parameters|constructor parameters}
126
+ * @param {number|boolean} [images[].limitInputPixels=268402689] - @see {@link /api-constructor#parameters|constructor parameters}
127
+ * @returns {Sharp}
128
+ * @throws {Error} Invalid parameters
129
+ */
130
+ function composite (images) {
131
+ if (!Array.isArray(images)) {
132
+ throw is.invalidParameterError('images to composite', 'array', images);
133
+ }
134
+ this.options.composite = images.map(image => {
135
+ if (!is.object(image)) {
136
+ throw is.invalidParameterError('image to composite', 'object', image);
137
+ }
138
+ const inputOptions = this._inputOptionsFromObject(image);
139
+ const composite = {
140
+ input: this._createInputDescriptor(image.input, inputOptions, { allowStream: false }),
141
+ blend: 'over',
142
+ tile: false,
143
+ left: 0,
144
+ top: 0,
145
+ hasOffset: false,
146
+ gravity: 0,
147
+ premultiplied: false
148
+ };
149
+ if (is.defined(image.blend)) {
150
+ if (is.string(blend[image.blend])) {
151
+ composite.blend = blend[image.blend];
152
+ } else {
153
+ throw is.invalidParameterError('blend', 'valid blend name', image.blend);
154
+ }
155
+ }
156
+ if (is.defined(image.tile)) {
157
+ if (is.bool(image.tile)) {
158
+ composite.tile = image.tile;
159
+ } else {
160
+ throw is.invalidParameterError('tile', 'boolean', image.tile);
161
+ }
162
+ }
163
+ if (is.defined(image.left)) {
164
+ if (is.integer(image.left)) {
165
+ composite.left = image.left;
166
+ } else {
167
+ throw is.invalidParameterError('left', 'integer', image.left);
168
+ }
169
+ }
170
+ if (is.defined(image.top)) {
171
+ if (is.integer(image.top)) {
172
+ composite.top = image.top;
173
+ } else {
174
+ throw is.invalidParameterError('top', 'integer', image.top);
175
+ }
176
+ }
177
+ if (is.defined(image.top) !== is.defined(image.left)) {
178
+ throw new Error('Expected both left and top to be set');
179
+ } else {
180
+ composite.hasOffset = is.integer(image.top) && is.integer(image.left);
181
+ }
182
+ if (is.defined(image.gravity)) {
183
+ if (is.integer(image.gravity) && is.inRange(image.gravity, 0, 8)) {
184
+ composite.gravity = image.gravity;
185
+ } else if (is.string(image.gravity) && is.integer(this.constructor.gravity[image.gravity])) {
186
+ composite.gravity = this.constructor.gravity[image.gravity];
187
+ } else {
188
+ throw is.invalidParameterError('gravity', 'valid gravity', image.gravity);
189
+ }
190
+ }
191
+ if (is.defined(image.premultiplied)) {
192
+ if (is.bool(image.premultiplied)) {
193
+ composite.premultiplied = image.premultiplied;
194
+ } else {
195
+ throw is.invalidParameterError('premultiplied', 'boolean', image.premultiplied);
196
+ }
197
+ }
198
+ return composite;
199
+ });
200
+ return this;
201
+ }
202
+
203
+ /**
204
+ * Decorate the Sharp prototype with composite-related functions.
205
+ * @private
206
+ */
207
+ module.exports = function (Sharp) {
208
+ Sharp.prototype.composite = composite;
209
+ Sharp.blend = blend;
210
+ };