sharp 0.32.0 → 0.32.2
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/binding.gyp +3 -1
- package/install/libvips.js +18 -11
- package/lib/agent.js +1 -1
- package/lib/colour.js +2 -1
- package/lib/composite.js +1 -1
- package/lib/constructor.js +4 -3
- package/lib/index.d.ts +10 -20
- package/lib/libvips.js +1 -2
- package/lib/operation.js +37 -4
- package/lib/output.js +16 -9
- package/package.json +7 -7
- package/src/common.cc +4 -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 +29 -7
- 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/binding.gyp
CHANGED
|
@@ -70,7 +70,9 @@
|
|
|
70
70
|
}, {
|
|
71
71
|
'target_name': 'sharp-<(platform_and_arch)',
|
|
72
72
|
'defines': [
|
|
73
|
-
'NAPI_VERSION=7'
|
|
73
|
+
'NAPI_VERSION=7',
|
|
74
|
+
'NODE_ADDON_API_DISABLE_DEPRECATED',
|
|
75
|
+
'NODE_API_SWALLOW_UNTHROWABLE_EXCEPTIONS'
|
|
74
76
|
],
|
|
75
77
|
'dependencies': [
|
|
76
78
|
'<!(node -p "require(\'node-addon-api\').gyp")',
|
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/composite.js
CHANGED
|
@@ -46,7 +46,7 @@ const blend = {
|
|
|
46
46
|
* The images to composite must be the same size or smaller than the processed image.
|
|
47
47
|
* If both `top` and `left` options are provided, they take precedence over `gravity`.
|
|
48
48
|
*
|
|
49
|
-
* Any resize or
|
|
49
|
+
* Any resize, rotate or extract operations in the same processing pipeline
|
|
50
50
|
* will always be applied to the input image before composition.
|
|
51
51
|
*
|
|
52
52
|
* The `blend` option can be one of `clear`, `source`, `over`, `in`, `out`, `atop`,
|
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.
|
|
@@ -1343,9 +1350,9 @@ declare namespace sharp {
|
|
|
1343
1350
|
grayscale?: boolean | undefined;
|
|
1344
1351
|
}
|
|
1345
1352
|
|
|
1346
|
-
interface OverlayOptions {
|
|
1353
|
+
interface OverlayOptions extends SharpOptions {
|
|
1347
1354
|
/** Buffer containing image data, String containing the path to an image file, or Create object */
|
|
1348
|
-
input?: string | Buffer | { create: Create } | { text: CreateText } | undefined;
|
|
1355
|
+
input?: string | Buffer | { create: Create } | { text: CreateText } | { raw: CreateRaw } | undefined;
|
|
1349
1356
|
/** how to blend this image with the image below. (optional, default `'over'`) */
|
|
1350
1357
|
blend?: Blend | undefined;
|
|
1351
1358
|
/** gravity at which to place the overlay. (optional, default 'centre') */
|
|
@@ -1356,25 +1363,8 @@ declare namespace sharp {
|
|
|
1356
1363
|
left?: number | undefined;
|
|
1357
1364
|
/** set to true to repeat the overlay image across the entire image with the given gravity. (optional, default false) */
|
|
1358
1365
|
tile?: boolean | undefined;
|
|
1359
|
-
/** number representing the DPI for vector overlay image. (optional, default 72) */
|
|
1360
|
-
density?: number | undefined;
|
|
1361
|
-
/** describes overlay when using raw pixel data. */
|
|
1362
|
-
raw?: Raw | undefined;
|
|
1363
1366
|
/** Set to true to avoid premultipling the image below. Equivalent to the --premultiplied vips option. */
|
|
1364
1367
|
premultiplied?: boolean | undefined;
|
|
1365
|
-
/** Set to true to read all frames/pages of an animated image. (optional, default false). */
|
|
1366
|
-
animated?: boolean | undefined;
|
|
1367
|
-
/**
|
|
1368
|
-
* When to abort processing of invalid pixel data, one of (in order of sensitivity):
|
|
1369
|
-
* 'none' (least), 'truncated', 'error' or 'warning' (most), highers level imply lower levels, invalid metadata will always abort. (optional, default 'warning')
|
|
1370
|
-
*/
|
|
1371
|
-
failOn?: FailOnOptions | undefined;
|
|
1372
|
-
/**
|
|
1373
|
-
* Do not process input images where the number of pixels (width x height) exceeds this limit.
|
|
1374
|
-
* Assumes image dimensions contained in the input metadata can be trusted.
|
|
1375
|
-
* An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF). (optional, default 268402689)
|
|
1376
|
-
*/
|
|
1377
|
-
limitInputPixels?: number | boolean | undefined;
|
|
1378
1368
|
}
|
|
1379
1369
|
|
|
1380
1370
|
interface TileOptions {
|
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
|
@@ -80,9 +80,13 @@ function rotate (angle, options) {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
/**
|
|
83
|
-
*
|
|
83
|
+
* Mirror the image vertically (up-down) about the x-axis.
|
|
84
|
+
* This always occurs before rotation, if any.
|
|
85
|
+
*
|
|
84
86
|
* The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
|
|
85
87
|
*
|
|
88
|
+
* This operation does not work correctly with multi-page images.
|
|
89
|
+
*
|
|
86
90
|
* @example
|
|
87
91
|
* const output = await sharp(input).flip().toBuffer();
|
|
88
92
|
*
|
|
@@ -95,7 +99,9 @@ function flip (flip) {
|
|
|
95
99
|
}
|
|
96
100
|
|
|
97
101
|
/**
|
|
98
|
-
*
|
|
102
|
+
* Mirror the image horizontally (left-right) about the y-axis.
|
|
103
|
+
* This always occurs before rotation, if any.
|
|
104
|
+
*
|
|
99
105
|
* The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
|
|
100
106
|
*
|
|
101
107
|
* @example
|
|
@@ -114,7 +120,7 @@ function flop (flop) {
|
|
|
114
120
|
*
|
|
115
121
|
* You must provide an array of length 4 or a 2x2 affine transformation matrix.
|
|
116
122
|
* 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.
|
|
123
|
+
* 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
124
|
*
|
|
119
125
|
* In the case of a 2x2 matrix, the transform is:
|
|
120
126
|
* - X = `matrix[0, 0]` \* (x + `idx`) + `matrix[0, 1]` \* (y + `idy`) + `odx`
|
|
@@ -131,7 +137,7 @@ function flop (flop) {
|
|
|
131
137
|
* const pipeline = sharp()
|
|
132
138
|
* .affine([[1, 0.3], [0.1, 0.7]], {
|
|
133
139
|
* background: 'white',
|
|
134
|
-
*
|
|
140
|
+
* interpolator: sharp.interpolators.nohalo
|
|
135
141
|
* })
|
|
136
142
|
* .toBuffer((err, outputBuffer, info) => {
|
|
137
143
|
* // outputBuffer contains the transformed image
|
|
@@ -405,6 +411,32 @@ function flatten (options) {
|
|
|
405
411
|
return this;
|
|
406
412
|
}
|
|
407
413
|
|
|
414
|
+
/**
|
|
415
|
+
* Ensure the image has an alpha channel
|
|
416
|
+
* with all white pixel values made fully transparent.
|
|
417
|
+
*
|
|
418
|
+
* Existing alpha channel values for non-white pixels remain unchanged.
|
|
419
|
+
*
|
|
420
|
+
* This feature is experimental and the API may change.
|
|
421
|
+
*
|
|
422
|
+
* @since 0.32.1
|
|
423
|
+
*
|
|
424
|
+
* @example
|
|
425
|
+
* await sharp(rgbInput)
|
|
426
|
+
* .unflatten()
|
|
427
|
+
* .toBuffer();
|
|
428
|
+
*
|
|
429
|
+
* @example
|
|
430
|
+
* await sharp(rgbInput)
|
|
431
|
+
* .threshold(128, { grayscale: false }) // converter bright pixels to white
|
|
432
|
+
* .unflatten()
|
|
433
|
+
* .toBuffer();
|
|
434
|
+
*/
|
|
435
|
+
function unflatten () {
|
|
436
|
+
this.options.unflatten = true;
|
|
437
|
+
return this;
|
|
438
|
+
}
|
|
439
|
+
|
|
408
440
|
/**
|
|
409
441
|
* Apply a gamma correction by reducing the encoding (darken) pre-resize at a factor of `1/gamma`
|
|
410
442
|
* then increasing the encoding (brighten) post-resize at a factor of `gamma`.
|
|
@@ -875,6 +907,7 @@ module.exports = function (Sharp) {
|
|
|
875
907
|
median,
|
|
876
908
|
blur,
|
|
877
909
|
flatten,
|
|
910
|
+
unflatten,
|
|
878
911
|
gamma,
|
|
879
912
|
negate,
|
|
880
913
|
normalise,
|
package/lib/output.js
CHANGED
|
@@ -29,7 +29,7 @@ const formats = new Map([
|
|
|
29
29
|
['jxl', 'jxl']
|
|
30
30
|
]);
|
|
31
31
|
|
|
32
|
-
const jp2Regex = /\.jp[2x]|j2[kc]$/i;
|
|
32
|
+
const jp2Regex = /\.(jp[2x]|j2[kc])$/i;
|
|
33
33
|
|
|
34
34
|
const errJp2Save = () => new Error('JP2 output requires libvips with support for OpenJPEG');
|
|
35
35
|
|
|
@@ -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
|
*
|
|
@@ -75,7 +75,7 @@ function toFile (fileOut, callback) {
|
|
|
75
75
|
err = new Error('Missing output file path');
|
|
76
76
|
} else if (is.string(this.options.input.file) && path.resolve(this.options.input.file) === path.resolve(fileOut)) {
|
|
77
77
|
err = new Error('Cannot use same file for input and output');
|
|
78
|
-
} else if (jp2Regex.test(fileOut) && !this.constructor.format.jp2k.output.file) {
|
|
78
|
+
} else if (jp2Regex.test(path.extname(fileOut)) && !this.constructor.format.jp2k.output.file) {
|
|
79
79
|
err = errJp2Save();
|
|
80
80
|
}
|
|
81
81
|
if (err) {
|
|
@@ -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.2",
|
|
5
5
|
"author": "Lovell Fuller <npm@lovell.info>",
|
|
6
6
|
"homepage": "https://github.com/lovell/sharp",
|
|
7
7
|
"contributors": [
|
|
@@ -134,11 +134,11 @@
|
|
|
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.4",
|
|
140
140
|
"simple-get": "^4.0.1",
|
|
141
|
-
"tar-fs": "^
|
|
141
|
+
"tar-fs": "^3.0.4",
|
|
142
142
|
"tunnel-agent": "^0.6.0"
|
|
143
143
|
},
|
|
144
144
|
"devDependencies": {
|
|
@@ -147,15 +147,15 @@
|
|
|
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",
|
|
154
154
|
"mock-fs": "^5.2.0",
|
|
155
155
|
"nyc": "^15.1.0",
|
|
156
|
-
"prebuild": "
|
|
156
|
+
"prebuild": "lovell/prebuild#add-nodejs-20-drop-nodejs-10-and-12",
|
|
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) {
|
|
@@ -679,6 +669,10 @@ namespace sharp {
|
|
|
679
669
|
if (image.width() > 65535 || height > 65535) {
|
|
680
670
|
throw vips::VError("Processed image is too large for the GIF format");
|
|
681
671
|
}
|
|
672
|
+
} else if (imageType == ImageType::HEIF) {
|
|
673
|
+
if (image.width() > 16384 || height > 16384) {
|
|
674
|
+
throw vips::VError("Processed image is too large for the HEIF format");
|
|
675
|
+
}
|
|
682
676
|
}
|
|
683
677
|
}
|
|
684
678
|
|
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
|
@@ -79,6 +79,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
79
79
|
// Rotate and flip image according to Exif orientation
|
|
80
80
|
std::tie(autoRotation, autoFlip, autoFlop) = CalculateExifRotationAndFlip(sharp::ExifOrientation(image));
|
|
81
81
|
image = sharp::RemoveExifOrientation(image);
|
|
82
|
+
if (baton->input->access == VIPS_ACCESS_SEQUENTIAL && (autoRotation != VIPS_ANGLE_D0 || autoFlip)) {
|
|
83
|
+
image = image.copy_memory();
|
|
84
|
+
}
|
|
82
85
|
} else {
|
|
83
86
|
rotation = CalculateAngleRotation(baton->angle);
|
|
84
87
|
}
|
|
@@ -116,7 +119,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
116
119
|
MultiPageUnsupported(nPages, "Rotate");
|
|
117
120
|
std::vector<double> background;
|
|
118
121
|
std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, FALSE);
|
|
119
|
-
image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background));
|
|
122
|
+
image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background)).copy_memory();
|
|
120
123
|
}
|
|
121
124
|
}
|
|
122
125
|
|
|
@@ -322,7 +325,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
322
325
|
} catch(...) {
|
|
323
326
|
sharp::VipsWarningCallback(nullptr, G_LOG_LEVEL_WARNING, "Invalid embedded profile", nullptr);
|
|
324
327
|
}
|
|
325
|
-
} else if (
|
|
328
|
+
} else if (
|
|
329
|
+
image.interpretation() == VIPS_INTERPRETATION_CMYK &&
|
|
330
|
+
baton->colourspaceInput != VIPS_INTERPRETATION_CMYK
|
|
331
|
+
) {
|
|
326
332
|
image = image.icc_transform(processingProfile, VImage::option()
|
|
327
333
|
->set("input_profile", "cmyk")
|
|
328
334
|
->set("intent", VIPS_INTENT_PERCEPTUAL));
|
|
@@ -377,11 +383,11 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
377
383
|
if (autoRotation != VIPS_ANGLE_D0) {
|
|
378
384
|
image = image.rot(autoRotation);
|
|
379
385
|
}
|
|
380
|
-
//
|
|
386
|
+
// Mirror vertically (up-down) about the x-axis
|
|
381
387
|
if (baton->flip || autoFlip) {
|
|
382
388
|
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
|
383
389
|
}
|
|
384
|
-
//
|
|
390
|
+
// Mirror horizontally (left-right) about the y-axis
|
|
385
391
|
if (baton->flop || autoFlop) {
|
|
386
392
|
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
|
387
393
|
}
|
|
@@ -396,6 +402,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
396
402
|
sharp::ImageType joinImageType = sharp::ImageType::UNKNOWN;
|
|
397
403
|
|
|
398
404
|
for (unsigned int i = 0; i < baton->joinChannelIn.size(); i++) {
|
|
405
|
+
baton->joinChannelIn[i]->access = baton->input->access;
|
|
399
406
|
std::tie(joinImage, joinImageType) = sharp::OpenInput(baton->joinChannelIn[i]);
|
|
400
407
|
joinImage = sharp::EnsureColourspace(joinImage, baton->colourspaceInput);
|
|
401
408
|
image = image.bandjoin(joinImage);
|
|
@@ -470,6 +477,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
470
477
|
|
|
471
478
|
image = image.smartcrop(baton->width, baton->height, VImage::option()
|
|
472
479
|
->set("interesting", baton->position == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION)
|
|
480
|
+
#if (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 15)
|
|
481
|
+
->set("premultiplied", shouldPremultiplyAlpha)
|
|
482
|
+
#endif
|
|
473
483
|
->set("attention_x", &attention_x)
|
|
474
484
|
->set("attention_y", &attention_y));
|
|
475
485
|
baton->hasCropOffset = true;
|
|
@@ -550,7 +560,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
550
560
|
if (baton->medianSize > 0) {
|
|
551
561
|
image = image.median(baton->medianSize);
|
|
552
562
|
}
|
|
563
|
+
|
|
553
564
|
// Threshold - must happen before blurring, due to the utility of blurring after thresholding
|
|
565
|
+
// Threshold - must happen before unflatten to enable non-white unflattening
|
|
554
566
|
if (baton->threshold != 0) {
|
|
555
567
|
image = sharp::Threshold(image, baton->threshold, baton->thresholdGrayscale);
|
|
556
568
|
}
|
|
@@ -560,6 +572,11 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
560
572
|
image = sharp::Blur(image, baton->blurSigma);
|
|
561
573
|
}
|
|
562
574
|
|
|
575
|
+
// Unflatten the image
|
|
576
|
+
if (baton->unflatten) {
|
|
577
|
+
image = sharp::Unflatten(image);
|
|
578
|
+
}
|
|
579
|
+
|
|
563
580
|
// Convolve
|
|
564
581
|
if (shouldConv) {
|
|
565
582
|
image = sharp::Convolve(image,
|
|
@@ -597,6 +614,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
597
614
|
for (Composite *composite : baton->composite) {
|
|
598
615
|
VImage compositeImage;
|
|
599
616
|
sharp::ImageType compositeImageType = sharp::ImageType::UNKNOWN;
|
|
617
|
+
composite->input->access = baton->input->access;
|
|
600
618
|
std::tie(compositeImage, compositeImageType) = sharp::OpenInput(composite->input);
|
|
601
619
|
compositeImage = sharp::EnsureColourspace(compositeImage, baton->colourspaceInput);
|
|
602
620
|
// Verify within current dimensions
|
|
@@ -695,6 +713,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
695
713
|
if (baton->boolean != nullptr) {
|
|
696
714
|
VImage booleanImage;
|
|
697
715
|
sharp::ImageType booleanImageType = sharp::ImageType::UNKNOWN;
|
|
716
|
+
baton->boolean->access = baton->input->access;
|
|
698
717
|
std::tie(booleanImage, booleanImageType) = sharp::OpenInput(baton->boolean);
|
|
699
718
|
booleanImage = sharp::EnsureColourspace(booleanImage, baton->colourspaceInput);
|
|
700
719
|
image = sharp::Boolean(image, booleanImage, baton->booleanOp);
|
|
@@ -922,6 +941,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
922
941
|
} else if (baton->formatOut == "heif" ||
|
|
923
942
|
(baton->formatOut == "input" && inputImageType == sharp::ImageType::HEIF)) {
|
|
924
943
|
// Write HEIF to buffer
|
|
944
|
+
sharp::AssertImageTypeDimensions(image, sharp::ImageType::HEIF);
|
|
925
945
|
image = sharp::RemoveAnimationProperties(image).cast(VIPS_FORMAT_UCHAR);
|
|
926
946
|
VipsArea *area = reinterpret_cast<VipsArea*>(image.heifsave_buffer(VImage::option()
|
|
927
947
|
->set("strip", !baton->withMetadata)
|
|
@@ -1111,6 +1131,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1111
1131
|
} else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) ||
|
|
1112
1132
|
(willMatchInput && inputImageType == sharp::ImageType::HEIF)) {
|
|
1113
1133
|
// Write HEIF to file
|
|
1134
|
+
sharp::AssertImageTypeDimensions(image, sharp::ImageType::HEIF);
|
|
1114
1135
|
image = sharp::RemoveAnimationProperties(image).cast(VIPS_FORMAT_UCHAR);
|
|
1115
1136
|
image.heifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
1116
1137
|
->set("strip", !baton->withMetadata)
|
|
@@ -1222,8 +1243,8 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
1222
1243
|
// Add buffer size to info
|
|
1223
1244
|
info.Set("size", static_cast<uint32_t>(baton->bufferOutLength));
|
|
1224
1245
|
// Pass ownership of output data to Buffer instance
|
|
1225
|
-
Napi::Buffer<char> data =
|
|
1226
|
-
baton->bufferOutLength);
|
|
1246
|
+
Napi::Buffer<char> data = Napi::Buffer<char>::NewOrCopy(env, static_cast<char*>(baton->bufferOut),
|
|
1247
|
+
baton->bufferOutLength, sharp::FreeCallback);
|
|
1227
1248
|
Callback().MakeCallback(Receiver().Value(), { env.Null(), data, info });
|
|
1228
1249
|
} else {
|
|
1229
1250
|
// Add file size to info
|
|
@@ -1460,6 +1481,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1460
1481
|
// Operators
|
|
1461
1482
|
baton->flatten = sharp::AttrAsBool(options, "flatten");
|
|
1462
1483
|
baton->flattenBackground = sharp::AttrAsVectorOfDouble(options, "flattenBackground");
|
|
1484
|
+
baton->unflatten = sharp::AttrAsBool(options, "unflatten");
|
|
1463
1485
|
baton->negate = sharp::AttrAsBool(options, "negate");
|
|
1464
1486
|
baton->negateAlpha = sharp::AttrAsBool(options, "negateAlpha");
|
|
1465
1487
|
baton->blurSigma = sharp::AttrAsDouble(options, "blurSigma");
|
|
@@ -1661,7 +1683,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1661
1683
|
baton->angle != 0 ||
|
|
1662
1684
|
baton->rotationAngle != 0.0 ||
|
|
1663
1685
|
baton->tileAngle != 0 ||
|
|
1664
|
-
baton->
|
|
1686
|
+
baton->flip ||
|
|
1665
1687
|
baton->claheWidth != 0 ||
|
|
1666
1688
|
!baton->affineMatrix.empty()
|
|
1667
1689
|
) {
|
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),
|