sharp 0.32.0 → 0.32.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -2
- package/install/libvips.js +18 -11
- package/lib/agent.js +1 -1
- package/lib/colour.js +2 -1
- package/lib/constructor.js +4 -3
- package/lib/index.d.ts +8 -1
- package/lib/libvips.js +1 -2
- package/lib/operation.js +29 -2
- package/lib/output.js +14 -7
- package/package.json +5 -5
- package/src/common.cc +0 -10
- package/src/common.h +0 -1
- package/src/metadata.cc +6 -5
- package/src/operations.cc +28 -23
- package/src/operations.h +5 -0
- package/src/pipeline.cc +16 -3
- package/src/pipeline.h +2 -0
package/README.md
CHANGED
|
@@ -98,8 +98,6 @@ readableStream
|
|
|
98
98
|
A [guide for contributors](https://github.com/lovell/sharp/blob/main/.github/CONTRIBUTING.md)
|
|
99
99
|
covers reporting bugs, requesting features and submitting code changes.
|
|
100
100
|
|
|
101
|
-
[](https://nodejs.org/dist/latest/docs/api/n-api.html#n_api_n_api_version_matrix)
|
|
102
|
-
|
|
103
101
|
## Licensing
|
|
104
102
|
|
|
105
103
|
Copyright 2013 Lovell Fuller and others.
|
package/install/libvips.js
CHANGED
|
@@ -11,6 +11,7 @@ const zlib = require('zlib');
|
|
|
11
11
|
const { createHash } = require('crypto');
|
|
12
12
|
|
|
13
13
|
const detectLibc = require('detect-libc');
|
|
14
|
+
const semverCoerce = require('semver/functions/coerce');
|
|
14
15
|
const semverLessThan = require('semver/functions/lt');
|
|
15
16
|
const semverSatisfies = require('semver/functions/satisfies');
|
|
16
17
|
const simpleGet = require('simple-get');
|
|
@@ -77,7 +78,11 @@ const verifyIntegrity = function (platformAndArch) {
|
|
|
77
78
|
flush: function (done) {
|
|
78
79
|
const digest = `sha512-${hash.digest('base64')}`;
|
|
79
80
|
if (expected !== digest) {
|
|
80
|
-
|
|
81
|
+
try {
|
|
82
|
+
libvips.removeVendoredLibvips();
|
|
83
|
+
} catch (err) {
|
|
84
|
+
libvips.log(err.message);
|
|
85
|
+
}
|
|
81
86
|
libvips.log(`Integrity expected: ${expected}`);
|
|
82
87
|
libvips.log(`Integrity received: ${digest}`);
|
|
83
88
|
done(new Error(`Integrity check failed for ${platformAndArch}`));
|
|
@@ -135,17 +140,19 @@ try {
|
|
|
135
140
|
throw new Error(`BSD/SunOS systems require manual installation of libvips >= ${minimumLibvipsVersion}`);
|
|
136
141
|
}
|
|
137
142
|
// Linux libc version check
|
|
138
|
-
const
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
if (
|
|
143
|
-
|
|
143
|
+
const libcVersionRaw = detectLibc.versionSync();
|
|
144
|
+
if (libcVersionRaw) {
|
|
145
|
+
const libcFamily = detectLibc.familySync();
|
|
146
|
+
const libcVersion = semverCoerce(libcVersionRaw).version;
|
|
147
|
+
if (libcFamily === detectLibc.GLIBC && minimumGlibcVersionByArch[arch]) {
|
|
148
|
+
if (semverLessThan(libcVersion, semverCoerce(minimumGlibcVersionByArch[arch]).version)) {
|
|
149
|
+
handleError(new Error(`Use with glibc ${libcVersionRaw} requires manual installation of libvips >= ${minimumLibvipsVersion}`));
|
|
150
|
+
}
|
|
144
151
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
152
|
+
if (libcFamily === detectLibc.MUSL) {
|
|
153
|
+
if (semverLessThan(libcVersion, '1.1.24')) {
|
|
154
|
+
handleError(new Error(`Use with musl ${libcVersionRaw} requires manual installation of libvips >= ${minimumLibvipsVersion}`));
|
|
155
|
+
}
|
|
149
156
|
}
|
|
150
157
|
}
|
|
151
158
|
// Node.js minimum version check
|
package/lib/agent.js
CHANGED
|
@@ -30,7 +30,7 @@ module.exports = function (log) {
|
|
|
30
30
|
const proxyAuth = proxy.username && proxy.password
|
|
31
31
|
? `${decodeURIComponent(proxy.username)}:${decodeURIComponent(proxy.password)}`
|
|
32
32
|
: null;
|
|
33
|
-
log(`Via proxy ${proxy.protocol}
|
|
33
|
+
log(`Via proxy ${proxy.protocol}//${proxy.hostname}:${proxy.port} ${proxyAuth ? 'with' : 'no'} credentials`);
|
|
34
34
|
return tunnel({
|
|
35
35
|
proxy: {
|
|
36
36
|
port: Number(proxy.port),
|
package/lib/colour.js
CHANGED
|
@@ -70,7 +70,8 @@ function grayscale (grayscale) {
|
|
|
70
70
|
* Set the pipeline colourspace.
|
|
71
71
|
*
|
|
72
72
|
* The input image will be converted to the provided colourspace at the start of the pipeline.
|
|
73
|
-
* All operations will use this colourspace before converting to the output colourspace,
|
|
73
|
+
* All operations will use this colourspace before converting to the output colourspace,
|
|
74
|
+
* as defined by {@link #tocolourspace|toColourspace}.
|
|
74
75
|
*
|
|
75
76
|
* This feature is experimental and has not yet been fully-tested with all operations.
|
|
76
77
|
*
|
package/lib/constructor.js
CHANGED
|
@@ -154,9 +154,9 @@ const debuglog = util.debuglog('sharp');
|
|
|
154
154
|
* @param {string} [options.text.text] - text to render as a UTF-8 string. It can contain Pango markup, for example `<i>Le</i>Monde`.
|
|
155
155
|
* @param {string} [options.text.font] - font name to render with.
|
|
156
156
|
* @param {string} [options.text.fontfile] - absolute filesystem path to a font file that can be used by `font`.
|
|
157
|
-
* @param {number} [options.text.width=0] -
|
|
158
|
-
* @param {number} [options.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.
|
|
159
|
-
* @param {string} [options.text.align='left'] - text
|
|
157
|
+
* @param {number} [options.text.width=0] - Integral number of pixels to word-wrap at. Lines of text wider than this will be broken at word boundaries.
|
|
158
|
+
* @param {number} [options.text.height=0] - Maximum 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.
|
|
159
|
+
* @param {string} [options.text.align='left'] - Alignment style for multi-line text (`'left'`, `'centre'`, `'center'`, `'right'`).
|
|
160
160
|
* @param {boolean} [options.text.justify=false] - set this to true to apply justification to the text.
|
|
161
161
|
* @param {number} [options.text.dpi=72] - the resolution (size) at which to render the text. Does not take effect if `height` is specified.
|
|
162
162
|
* @param {boolean} [options.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>`.
|
|
@@ -217,6 +217,7 @@ const Sharp = function (input, options) {
|
|
|
217
217
|
tintB: 128,
|
|
218
218
|
flatten: false,
|
|
219
219
|
flattenBackground: [0, 0, 0],
|
|
220
|
+
unflatten: false,
|
|
220
221
|
negate: false,
|
|
221
222
|
negateAlpha: true,
|
|
222
223
|
medianSize: 0,
|
package/lib/index.d.ts
CHANGED
|
@@ -356,7 +356,7 @@ declare namespace sharp {
|
|
|
356
356
|
* Perform an affine transform on an image. This operation will always occur after resizing, extraction and rotation, if any.
|
|
357
357
|
* You must provide an array of length 4 or a 2x2 affine transformation matrix.
|
|
358
358
|
* By default, new pixels are filled with a black background. You can provide a background color with the `background` option.
|
|
359
|
-
* A particular interpolator may also be specified. Set the `interpolator` option to an attribute of the `sharp.
|
|
359
|
+
* A particular interpolator may also be specified. Set the `interpolator` option to an attribute of the `sharp.interpolators` Object e.g. `sharp.interpolators.nohalo`.
|
|
360
360
|
*
|
|
361
361
|
* In the case of a 2x2 matrix, the transform is:
|
|
362
362
|
* X = matrix[0, 0] * (x + idx) + matrix[0, 1] * (y + idy) + odx
|
|
@@ -427,6 +427,13 @@ declare namespace sharp {
|
|
|
427
427
|
*/
|
|
428
428
|
flatten(flatten?: boolean | FlattenOptions): Sharp;
|
|
429
429
|
|
|
430
|
+
/**
|
|
431
|
+
* Ensure the image has an alpha channel with all white pixel values made fully transparent.
|
|
432
|
+
* Existing alpha channel values for non-white pixels remain unchanged.
|
|
433
|
+
* @returns A sharp instance that can be used to chain operations
|
|
434
|
+
*/
|
|
435
|
+
unflatten(): Sharp;
|
|
436
|
+
|
|
430
437
|
/**
|
|
431
438
|
* Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of 1/gamma then increasing the encoding (brighten) post-resize at a factor of gamma.
|
|
432
439
|
* This can improve the perceived brightness of a resized image in non-linear colour spaces.
|
package/lib/libvips.js
CHANGED
|
@@ -88,8 +88,7 @@ const hasVendoredLibvips = function () {
|
|
|
88
88
|
|
|
89
89
|
/* istanbul ignore next */
|
|
90
90
|
const removeVendoredLibvips = function () {
|
|
91
|
-
|
|
92
|
-
rm(vendorPath, { recursive: true, maxRetries: 3, force: true });
|
|
91
|
+
fs.rmSync(vendorPath, { recursive: true, maxRetries: 3, force: true });
|
|
93
92
|
};
|
|
94
93
|
|
|
95
94
|
/* istanbul ignore next */
|
package/lib/operation.js
CHANGED
|
@@ -114,7 +114,7 @@ function flop (flop) {
|
|
|
114
114
|
*
|
|
115
115
|
* You must provide an array of length 4 or a 2x2 affine transformation matrix.
|
|
116
116
|
* By default, new pixels are filled with a black background. You can provide a background color with the `background` option.
|
|
117
|
-
* A particular interpolator may also be specified. Set the `interpolator` option to an attribute of the `sharp.
|
|
117
|
+
* A particular interpolator may also be specified. Set the `interpolator` option to an attribute of the `sharp.interpolators` Object e.g. `sharp.interpolators.nohalo`.
|
|
118
118
|
*
|
|
119
119
|
* In the case of a 2x2 matrix, the transform is:
|
|
120
120
|
* - X = `matrix[0, 0]` \* (x + `idx`) + `matrix[0, 1]` \* (y + `idy`) + `odx`
|
|
@@ -131,7 +131,7 @@ function flop (flop) {
|
|
|
131
131
|
* const pipeline = sharp()
|
|
132
132
|
* .affine([[1, 0.3], [0.1, 0.7]], {
|
|
133
133
|
* background: 'white',
|
|
134
|
-
*
|
|
134
|
+
* interpolator: sharp.interpolators.nohalo
|
|
135
135
|
* })
|
|
136
136
|
* .toBuffer((err, outputBuffer, info) => {
|
|
137
137
|
* // outputBuffer contains the transformed image
|
|
@@ -405,6 +405,32 @@ function flatten (options) {
|
|
|
405
405
|
return this;
|
|
406
406
|
}
|
|
407
407
|
|
|
408
|
+
/**
|
|
409
|
+
* Ensure the image has an alpha channel
|
|
410
|
+
* with all white pixel values made fully transparent.
|
|
411
|
+
*
|
|
412
|
+
* Existing alpha channel values for non-white pixels remain unchanged.
|
|
413
|
+
*
|
|
414
|
+
* This feature is experimental and the API may change.
|
|
415
|
+
*
|
|
416
|
+
* @since 0.32.1
|
|
417
|
+
*
|
|
418
|
+
* @example
|
|
419
|
+
* await sharp(rgbInput)
|
|
420
|
+
* .unflatten()
|
|
421
|
+
* .toBuffer();
|
|
422
|
+
*
|
|
423
|
+
* @example
|
|
424
|
+
* await sharp(rgbInput)
|
|
425
|
+
* .threshold(128, { grayscale: false }) // converter bright pixels to white
|
|
426
|
+
* .unflatten()
|
|
427
|
+
* .toBuffer();
|
|
428
|
+
*/
|
|
429
|
+
function unflatten () {
|
|
430
|
+
this.options.unflatten = true;
|
|
431
|
+
return this;
|
|
432
|
+
}
|
|
433
|
+
|
|
408
434
|
/**
|
|
409
435
|
* Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of `1/gamma`
|
|
410
436
|
* then increasing the encoding (brighten) post-resize at a factor of `gamma`.
|
|
@@ -875,6 +901,7 @@ module.exports = function (Sharp) {
|
|
|
875
901
|
median,
|
|
876
902
|
blur,
|
|
877
903
|
flatten,
|
|
904
|
+
unflatten,
|
|
878
905
|
gamma,
|
|
879
906
|
negate,
|
|
880
907
|
normalise,
|
package/lib/output.js
CHANGED
|
@@ -43,7 +43,7 @@ const bitdepthFromColourCount = (colours) => 1 << 31 - Math.clz32(Math.ceil(Math
|
|
|
43
43
|
* Note that raw pixel data is only supported for buffer output.
|
|
44
44
|
*
|
|
45
45
|
* By default all metadata will be removed, which includes EXIF-based orientation.
|
|
46
|
-
* See {@link withMetadata} for control over this.
|
|
46
|
+
* See {@link #withmetadata|withMetadata} for control over this.
|
|
47
47
|
*
|
|
48
48
|
* The caller is responsible for ensuring directory structures and permissions exist.
|
|
49
49
|
*
|
|
@@ -95,12 +95,12 @@ function toFile (fileOut, callback) {
|
|
|
95
95
|
* Write output to a Buffer.
|
|
96
96
|
* JPEG, PNG, WebP, AVIF, TIFF, GIF and raw pixel data output are supported.
|
|
97
97
|
*
|
|
98
|
-
* Use {@link toFormat} or one of the format-specific functions such as {@link jpeg}, {@link png} etc. to set the output format.
|
|
98
|
+
* Use {@link #toformat|toFormat} or one of the format-specific functions such as {@link jpeg}, {@link png} etc. to set the output format.
|
|
99
99
|
*
|
|
100
100
|
* If no explicit format is set, the output format will match the input image, except SVG input which becomes PNG output.
|
|
101
101
|
*
|
|
102
102
|
* By default all metadata will be removed, which includes EXIF-based orientation.
|
|
103
|
-
* See {@link withMetadata} for control over this.
|
|
103
|
+
* See {@link #withmetadata|withMetadata} for control over this.
|
|
104
104
|
*
|
|
105
105
|
* `callback`, if present, gets three arguments `(err, data, info)` where:
|
|
106
106
|
* - `err` is an error, if any.
|
|
@@ -177,12 +177,18 @@ function toBuffer (options, callback) {
|
|
|
177
177
|
* .then(info => { ... });
|
|
178
178
|
*
|
|
179
179
|
* @example
|
|
180
|
-
* // Set
|
|
180
|
+
* // Set output EXIF metadata
|
|
181
181
|
* const data = await sharp(input)
|
|
182
182
|
* .withMetadata({
|
|
183
183
|
* exif: {
|
|
184
184
|
* IFD0: {
|
|
185
|
-
* Copyright: '
|
|
185
|
+
* Copyright: 'The National Gallery'
|
|
186
|
+
* },
|
|
187
|
+
* IFD3: {
|
|
188
|
+
* GPSLatitudeRef: 'N',
|
|
189
|
+
* GPSLatitude: '51/1 30/1 3230/100',
|
|
190
|
+
* GPSLongitudeRef: 'W',
|
|
191
|
+
* GPSLongitude: '0/1 7/1 4366/100'
|
|
186
192
|
* }
|
|
187
193
|
* }
|
|
188
194
|
* })
|
|
@@ -626,6 +632,7 @@ function gif (options) {
|
|
|
626
632
|
return this._updateFormatOut('gif', options);
|
|
627
633
|
}
|
|
628
634
|
|
|
635
|
+
/* istanbul ignore next */
|
|
629
636
|
/**
|
|
630
637
|
* Use these JP2 options for output image.
|
|
631
638
|
*
|
|
@@ -659,7 +666,6 @@ function gif (options) {
|
|
|
659
666
|
* @returns {Sharp}
|
|
660
667
|
* @throws {Error} Invalid options
|
|
661
668
|
*/
|
|
662
|
-
/* istanbul ignore next */
|
|
663
669
|
function jp2 (options) {
|
|
664
670
|
if (!this.constructor.format.jp2k.output.buffer) {
|
|
665
671
|
throw errJp2Save();
|
|
@@ -740,7 +746,8 @@ function trySetAnimationOptions (source, target) {
|
|
|
740
746
|
/**
|
|
741
747
|
* Use these TIFF options for output image.
|
|
742
748
|
*
|
|
743
|
-
* The `density` can be set in pixels/inch via {@link withMetadata}
|
|
749
|
+
* The `density` can be set in pixels/inch via {@link #withmetadata|withMetadata}
|
|
750
|
+
* instead of providing `xres` and `yres` in pixels/mm.
|
|
744
751
|
*
|
|
745
752
|
* @example
|
|
746
753
|
* // Convert SVG input to LZW-compressed, 1 bit per pixel TIFF output
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sharp",
|
|
3
3
|
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
|
|
4
|
-
"version": "0.32.
|
|
4
|
+
"version": "0.32.1",
|
|
5
5
|
"author": "Lovell Fuller <npm@lovell.info>",
|
|
6
6
|
"homepage": "https://github.com/lovell/sharp",
|
|
7
7
|
"contributors": [
|
|
@@ -134,9 +134,9 @@
|
|
|
134
134
|
"dependencies": {
|
|
135
135
|
"color": "^4.2.3",
|
|
136
136
|
"detect-libc": "^2.0.1",
|
|
137
|
-
"node-addon-api": "^6.
|
|
137
|
+
"node-addon-api": "^6.1.0",
|
|
138
138
|
"prebuild-install": "^7.1.1",
|
|
139
|
-
"semver": "^7.
|
|
139
|
+
"semver": "^7.5.0",
|
|
140
140
|
"simple-get": "^4.0.1",
|
|
141
141
|
"tar-fs": "^2.1.1",
|
|
142
142
|
"tunnel-agent": "^0.6.0"
|
|
@@ -147,7 +147,7 @@
|
|
|
147
147
|
"cc": "^3.0.1",
|
|
148
148
|
"exif-reader": "^1.2.0",
|
|
149
149
|
"extract-zip": "^2.0.1",
|
|
150
|
-
"icc": "^
|
|
150
|
+
"icc": "^3.0.0",
|
|
151
151
|
"jsdoc-to-markdown": "^8.0.0",
|
|
152
152
|
"license-checker": "^25.0.1",
|
|
153
153
|
"mocha": "^10.2.0",
|
|
@@ -155,7 +155,7 @@
|
|
|
155
155
|
"nyc": "^15.1.0",
|
|
156
156
|
"prebuild": "^11.0.4",
|
|
157
157
|
"semistandard": "^16.0.1",
|
|
158
|
-
"tsd": "^0.28.
|
|
158
|
+
"tsd": "^0.28.1"
|
|
159
159
|
},
|
|
160
160
|
"license": "Apache-2.0",
|
|
161
161
|
"config": {
|
package/src/common.cc
CHANGED
|
@@ -65,16 +65,6 @@ namespace sharp {
|
|
|
65
65
|
}
|
|
66
66
|
return vector;
|
|
67
67
|
}
|
|
68
|
-
Napi::Buffer<char> NewOrCopyBuffer(Napi::Env env, char* data, size_t len) {
|
|
69
|
-
try {
|
|
70
|
-
return Napi::Buffer<char>::New(env, data, len, FreeCallback);
|
|
71
|
-
} catch (Napi::Error const &err) {
|
|
72
|
-
static_cast<void>(err);
|
|
73
|
-
}
|
|
74
|
-
Napi::Buffer<char> buf = Napi::Buffer<char>::Copy(env, data, len);
|
|
75
|
-
FreeCallback(nullptr, data);
|
|
76
|
-
return buf;
|
|
77
|
-
}
|
|
78
68
|
|
|
79
69
|
// Create an InputDescriptor instance from a Napi::Object describing an input image
|
|
80
70
|
InputDescriptor* CreateInputDescriptor(Napi::Object input) {
|
package/src/common.h
CHANGED
|
@@ -126,7 +126,6 @@ namespace sharp {
|
|
|
126
126
|
return static_cast<T>(
|
|
127
127
|
vips_enum_from_nick(nullptr, type, AttrAsStr(obj, attr).data()));
|
|
128
128
|
}
|
|
129
|
-
Napi::Buffer<char> NewOrCopyBuffer(Napi::Env env, char* data, size_t len);
|
|
130
129
|
|
|
131
130
|
// Create an InputDescriptor instance from a Napi::Object describing an input image
|
|
132
131
|
InputDescriptor* CreateInputDescriptor(Napi::Object input);
|
package/src/metadata.cc
CHANGED
|
@@ -230,20 +230,21 @@ class MetadataWorker : public Napi::AsyncWorker {
|
|
|
230
230
|
info.Set("orientation", baton->orientation);
|
|
231
231
|
}
|
|
232
232
|
if (baton->exifLength > 0) {
|
|
233
|
-
info.Set("exif",
|
|
233
|
+
info.Set("exif", Napi::Buffer<char>::NewOrCopy(env, baton->exif, baton->exifLength, sharp::FreeCallback));
|
|
234
234
|
}
|
|
235
235
|
if (baton->iccLength > 0) {
|
|
236
|
-
info.Set("icc",
|
|
236
|
+
info.Set("icc", Napi::Buffer<char>::NewOrCopy(env, baton->icc, baton->iccLength, sharp::FreeCallback));
|
|
237
237
|
}
|
|
238
238
|
if (baton->iptcLength > 0) {
|
|
239
|
-
info.Set("iptc",
|
|
239
|
+
info.Set("iptc", Napi::Buffer<char>::NewOrCopy(env, baton->iptc, baton->iptcLength, sharp::FreeCallback));
|
|
240
240
|
}
|
|
241
241
|
if (baton->xmpLength > 0) {
|
|
242
|
-
info.Set("xmp",
|
|
242
|
+
info.Set("xmp", Napi::Buffer<char>::NewOrCopy(env, baton->xmp, baton->xmpLength, sharp::FreeCallback));
|
|
243
243
|
}
|
|
244
244
|
if (baton->tifftagPhotoshopLength > 0) {
|
|
245
245
|
info.Set("tifftagPhotoshop",
|
|
246
|
-
|
|
246
|
+
Napi::Buffer<char>::NewOrCopy(env, baton->tifftagPhotoshop,
|
|
247
|
+
baton->tifftagPhotoshopLength, sharp::FreeCallback));
|
|
247
248
|
}
|
|
248
249
|
Callback().MakeCallback(Receiver().Value(), { env.Null(), info });
|
|
249
250
|
} else {
|
package/src/operations.cc
CHANGED
|
@@ -186,6 +186,7 @@ namespace sharp {
|
|
|
186
186
|
|
|
187
187
|
VImage Modulate(VImage image, double const brightness, double const saturation,
|
|
188
188
|
int const hue, double const lightness) {
|
|
189
|
+
VipsInterpretation colourspaceBeforeModulate = image.interpretation();
|
|
189
190
|
if (HasAlpha(image)) {
|
|
190
191
|
// Separate alpha channel
|
|
191
192
|
VImage alpha = image[image.bands() - 1];
|
|
@@ -195,7 +196,7 @@ namespace sharp {
|
|
|
195
196
|
{ brightness, saturation, 1},
|
|
196
197
|
{ lightness, 0.0, static_cast<double>(hue) }
|
|
197
198
|
)
|
|
198
|
-
.colourspace(
|
|
199
|
+
.colourspace(colourspaceBeforeModulate)
|
|
199
200
|
.bandjoin(alpha);
|
|
200
201
|
} else {
|
|
201
202
|
return image
|
|
@@ -204,7 +205,7 @@ namespace sharp {
|
|
|
204
205
|
{ brightness, saturation, 1 },
|
|
205
206
|
{ lightness, 0.0, static_cast<double>(hue) }
|
|
206
207
|
)
|
|
207
|
-
.colourspace(
|
|
208
|
+
.colourspace(colourspaceBeforeModulate);
|
|
208
209
|
}
|
|
209
210
|
}
|
|
210
211
|
|
|
@@ -268,30 +269,20 @@ namespace sharp {
|
|
|
268
269
|
if (image.width() < 3 && image.height() < 3) {
|
|
269
270
|
throw VError("Image to trim must be at least 3x3 pixels");
|
|
270
271
|
}
|
|
271
|
-
|
|
272
|
-
// Scale up 8-bit values to match 16-bit input image
|
|
273
|
-
double multiplier = sharp::Is16Bit(image.interpretation()) ? 256.0 : 1.0;
|
|
274
|
-
threshold *= multiplier;
|
|
275
|
-
|
|
276
|
-
std::vector<double> backgroundAlpha(1);
|
|
277
272
|
if (background.size() == 0) {
|
|
278
273
|
// Top-left pixel provides the default background colour if none is given
|
|
279
274
|
background = image.extract_area(0, 0, 1, 1)(0, 0);
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
backgroundAlpha[0] = background[3] * multiplier;
|
|
275
|
+
} else if (sharp::Is16Bit(image.interpretation())) {
|
|
276
|
+
for (size_t i = 0; i < background.size(); i++) {
|
|
277
|
+
background[i] *= 256.0;
|
|
278
|
+
}
|
|
279
|
+
threshold *= 256.0;
|
|
286
280
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
background[1] * multiplier,
|
|
291
|
-
background[2] * multiplier
|
|
292
|
-
};
|
|
281
|
+
std::vector<double> backgroundAlpha({ background.back() });
|
|
282
|
+
if (HasAlpha(image)) {
|
|
283
|
+
background.pop_back();
|
|
293
284
|
} else {
|
|
294
|
-
background
|
|
285
|
+
background.resize(image.bands());
|
|
295
286
|
}
|
|
296
287
|
int left, top, width, height;
|
|
297
288
|
left = image.find_trim(&top, &width, &height, VImage::option()
|
|
@@ -332,12 +323,26 @@ namespace sharp {
|
|
|
332
323
|
if (a.size() > bands) {
|
|
333
324
|
throw VError("Band expansion using linear is unsupported");
|
|
334
325
|
}
|
|
326
|
+
bool const uchar = !Is16Bit(image.interpretation());
|
|
335
327
|
if (HasAlpha(image) && a.size() != bands && (a.size() == 1 || a.size() == bands - 1 || bands - 1 == 1)) {
|
|
336
328
|
// Separate alpha channel
|
|
337
329
|
VImage alpha = image[bands - 1];
|
|
338
|
-
return RemoveAlpha(image).linear(a, b, VImage::option()->set("uchar",
|
|
330
|
+
return RemoveAlpha(image).linear(a, b, VImage::option()->set("uchar", uchar)).bandjoin(alpha);
|
|
331
|
+
} else {
|
|
332
|
+
return image.linear(a, b, VImage::option()->set("uchar", uchar));
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/*
|
|
337
|
+
* Unflatten
|
|
338
|
+
*/
|
|
339
|
+
VImage Unflatten(VImage image) {
|
|
340
|
+
if (HasAlpha(image)) {
|
|
341
|
+
VImage alpha = image[image.bands() - 1];
|
|
342
|
+
VImage noAlpha = RemoveAlpha(image);
|
|
343
|
+
return noAlpha.bandjoin(alpha & (noAlpha.colourspace(VIPS_INTERPRETATION_B_W) < 255));
|
|
339
344
|
} else {
|
|
340
|
-
return image.
|
|
345
|
+
return image.bandjoin(image.colourspace(VIPS_INTERPRETATION_B_W) < 255);
|
|
341
346
|
}
|
|
342
347
|
}
|
|
343
348
|
|
package/src/operations.h
CHANGED
|
@@ -86,6 +86,11 @@ namespace sharp {
|
|
|
86
86
|
*/
|
|
87
87
|
VImage Linear(VImage image, std::vector<double> const a, std::vector<double> const b);
|
|
88
88
|
|
|
89
|
+
/*
|
|
90
|
+
* Unflatten
|
|
91
|
+
*/
|
|
92
|
+
VImage Unflatten(VImage image);
|
|
93
|
+
|
|
89
94
|
/*
|
|
90
95
|
* Recomb with a Matrix of the given bands/channel size.
|
|
91
96
|
* Eg. RGB will be a 3x3 matrix.
|
package/src/pipeline.cc
CHANGED
|
@@ -322,7 +322,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
322
322
|
} catch(...) {
|
|
323
323
|
sharp::VipsWarningCallback(nullptr, G_LOG_LEVEL_WARNING, "Invalid embedded profile", nullptr);
|
|
324
324
|
}
|
|
325
|
-
} else if (
|
|
325
|
+
} else if (
|
|
326
|
+
image.interpretation() == VIPS_INTERPRETATION_CMYK &&
|
|
327
|
+
baton->colourspaceInput != VIPS_INTERPRETATION_CMYK
|
|
328
|
+
) {
|
|
326
329
|
image = image.icc_transform(processingProfile, VImage::option()
|
|
327
330
|
->set("input_profile", "cmyk")
|
|
328
331
|
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
|
@@ -470,6 +473,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
470
473
|
|
|
471
474
|
image = image.smartcrop(baton->width, baton->height, VImage::option()
|
|
472
475
|
->set("interesting", baton->position == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION)
|
|
476
|
+
->set("premultiplied", shouldPremultiplyAlpha)
|
|
473
477
|
->set("attention_x", &attention_x)
|
|
474
478
|
->set("attention_y", &attention_y));
|
|
475
479
|
baton->hasCropOffset = true;
|
|
@@ -550,7 +554,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
550
554
|
if (baton->medianSize > 0) {
|
|
551
555
|
image = image.median(baton->medianSize);
|
|
552
556
|
}
|
|
557
|
+
|
|
553
558
|
// Threshold - must happen before blurring, due to the utility of blurring after thresholding
|
|
559
|
+
// Threshold - must happen before unflatten to enable non-white unflattening
|
|
554
560
|
if (baton->threshold != 0) {
|
|
555
561
|
image = sharp::Threshold(image, baton->threshold, baton->thresholdGrayscale);
|
|
556
562
|
}
|
|
@@ -560,6 +566,11 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
560
566
|
image = sharp::Blur(image, baton->blurSigma);
|
|
561
567
|
}
|
|
562
568
|
|
|
569
|
+
// Unflatten the image
|
|
570
|
+
if (baton->unflatten) {
|
|
571
|
+
image = sharp::Unflatten(image);
|
|
572
|
+
}
|
|
573
|
+
|
|
563
574
|
// Convolve
|
|
564
575
|
if (shouldConv) {
|
|
565
576
|
image = sharp::Convolve(image,
|
|
@@ -1222,8 +1233,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1222
1233
|
// Add buffer size to info
|
|
1223
1234
|
info.Set("size", static_cast<uint32_t>(baton->bufferOutLength));
|
|
1224
1235
|
// Pass ownership of output data to Buffer instance
|
|
1225
|
-
Napi::Buffer<char> data =
|
|
1226
|
-
baton->bufferOutLength);
|
|
1236
|
+
Napi::Buffer<char> data = Napi::Buffer<char>::NewOrCopy(env, static_cast<char*>(baton->bufferOut),
|
|
1237
|
+
baton->bufferOutLength, sharp::FreeCallback);
|
|
1227
1238
|
Callback().MakeCallback(Receiver().Value(), { env.Null(), data, info });
|
|
1228
1239
|
} else {
|
|
1229
1240
|
// Add file size to info
|
|
@@ -1460,6 +1471,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1460
1471
|
// Operators
|
|
1461
1472
|
baton->flatten = sharp::AttrAsBool(options, "flatten");
|
|
1462
1473
|
baton->flattenBackground = sharp::AttrAsVectorOfDouble(options, "flattenBackground");
|
|
1474
|
+
baton->unflatten = sharp::AttrAsBool(options, "unflatten");
|
|
1463
1475
|
baton->negate = sharp::AttrAsBool(options, "negate");
|
|
1464
1476
|
baton->negateAlpha = sharp::AttrAsBool(options, "negateAlpha");
|
|
1465
1477
|
baton->blurSigma = sharp::AttrAsDouble(options, "blurSigma");
|
|
@@ -1662,6 +1674,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1662
1674
|
baton->rotationAngle != 0.0 ||
|
|
1663
1675
|
baton->tileAngle != 0 ||
|
|
1664
1676
|
baton->useExifOrientation ||
|
|
1677
|
+
baton->flip ||
|
|
1665
1678
|
baton->claheWidth != 0 ||
|
|
1666
1679
|
!baton->affineMatrix.empty()
|
|
1667
1680
|
) {
|
package/src/pipeline.h
CHANGED
|
@@ -73,6 +73,7 @@ struct PipelineBaton {
|
|
|
73
73
|
double tintB;
|
|
74
74
|
bool flatten;
|
|
75
75
|
std::vector<double> flattenBackground;
|
|
76
|
+
bool unflatten;
|
|
76
77
|
bool negate;
|
|
77
78
|
bool negateAlpha;
|
|
78
79
|
double blurSigma;
|
|
@@ -239,6 +240,7 @@ struct PipelineBaton {
|
|
|
239
240
|
tintB(128.0),
|
|
240
241
|
flatten(false),
|
|
241
242
|
flattenBackground{ 0.0, 0.0, 0.0 },
|
|
243
|
+
unflatten(false),
|
|
242
244
|
negate(false),
|
|
243
245
|
negateAlpha(true),
|
|
244
246
|
blurSigma(0.0),
|