sharp 0.27.0 → 0.27.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 +1 -1
- package/lib/colour.js +7 -0
- package/lib/constructor.js +42 -8
- package/lib/input.js +41 -10
- package/lib/is.js +10 -0
- package/lib/libvips.js +3 -2
- package/lib/output.js +30 -7
- package/package.json +8 -6
- package/src/common.cc +68 -35
- package/src/common.h +6 -1
- package/src/metadata.cc +6 -0
- package/src/metadata.h +1 -0
- package/src/pipeline.cc +19 -6
- package/src/pipeline.h +2 -0
- package/src/sharp.cc +0 -1
package/README.md
CHANGED
|
@@ -102,7 +102,7 @@ covers reporting bugs, requesting features and submitting code changes.
|
|
|
102
102
|
|
|
103
103
|
### Licensing
|
|
104
104
|
|
|
105
|
-
Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Lovell Fuller and contributors.
|
|
105
|
+
Copyright 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 Lovell Fuller and contributors.
|
|
106
106
|
|
|
107
107
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
108
108
|
you may not use this file except in compliance with the License.
|
package/lib/colour.js
CHANGED
|
@@ -57,6 +57,13 @@ function grayscale (grayscale) {
|
|
|
57
57
|
/**
|
|
58
58
|
* Set the output colourspace.
|
|
59
59
|
* By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* // Output 16 bits per pixel RGB
|
|
63
|
+
* await sharp(input)
|
|
64
|
+
* .toColourspace('rgb16')
|
|
65
|
+
* .toFile('16-bpp.png')
|
|
66
|
+
*
|
|
60
67
|
* @param {string} [colourspace] - output colourspace e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/libvips/libvips/blob/master/libvips/iofuncs/enumtypes.c#L568)
|
|
61
68
|
* @returns {Sharp}
|
|
62
69
|
* @throws {Error} Invalid parameters
|
package/lib/constructor.js
CHANGED
|
@@ -90,8 +90,37 @@ const debuglog = util.debuglog('sharp');
|
|
|
90
90
|
* // Convert an animated GIF to an animated WebP
|
|
91
91
|
* await sharp('in.gif', { animated: true }).toFile('out.webp');
|
|
92
92
|
*
|
|
93
|
-
* @
|
|
94
|
-
*
|
|
93
|
+
* @example
|
|
94
|
+
* // Read a raw array of pixels and save it to a png
|
|
95
|
+
* const input = Uint8Array.from([255, 255, 255, 0, 0, 0]); // or Uint8ClampedArray
|
|
96
|
+
* const image = sharp(input, {
|
|
97
|
+
* // because the input does not contain its dimensions or how many channels it has
|
|
98
|
+
* // we need to specify it in the constructor options
|
|
99
|
+
* raw: {
|
|
100
|
+
* width: 2,
|
|
101
|
+
* height: 1,
|
|
102
|
+
* channels: 3
|
|
103
|
+
* }
|
|
104
|
+
* });
|
|
105
|
+
* await image.toFile('my-two-pixels.png');
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* // Generate RGB Gaussian noise
|
|
109
|
+
* await sharp({
|
|
110
|
+
* create: {
|
|
111
|
+
* width: 300,
|
|
112
|
+
* height: 200,
|
|
113
|
+
* channels: 3,
|
|
114
|
+
* noise: {
|
|
115
|
+
* type: 'gaussian',
|
|
116
|
+
* mean: 128,
|
|
117
|
+
* sigma: 30
|
|
118
|
+
* }
|
|
119
|
+
* }
|
|
120
|
+
* }.toFile('noise.png');
|
|
121
|
+
*
|
|
122
|
+
* @param {(Buffer|Uint8Array|Uint8ClampedArray|string)} [input] - if present, can be
|
|
123
|
+
* a Buffer / Uint8Array / Uint8ClampedArray containing JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data, or
|
|
95
124
|
* a String containing the filesystem path to an JPEG, PNG, WebP, AVIF, GIF, SVG or TIFF image file.
|
|
96
125
|
* JPEG, PNG, WebP, AVIF, GIF, SVG, TIFF or raw pixel image data can be streamed into the object when not present.
|
|
97
126
|
* @param {Object} [options] - if present, is an Object with optional attributes.
|
|
@@ -108,14 +137,18 @@ const debuglog = util.debuglog('sharp');
|
|
|
108
137
|
* @param {number} [options.level=0] - level to extract from a multi-level input (OpenSlide), zero based.
|
|
109
138
|
* @param {boolean} [options.animated=false] - Set to `true` to read all frames/pages of an animated image (equivalent of setting `pages` to `-1`).
|
|
110
139
|
* @param {Object} [options.raw] - describes raw pixel input image data. See `raw()` for pixel ordering.
|
|
111
|
-
* @param {number} [options.raw.width]
|
|
112
|
-
* @param {number} [options.raw.height]
|
|
113
|
-
* @param {number} [options.raw.channels] - 1
|
|
140
|
+
* @param {number} [options.raw.width] - integral number of pixels wide.
|
|
141
|
+
* @param {number} [options.raw.height] - integral number of pixels high.
|
|
142
|
+
* @param {number} [options.raw.channels] - integral number of channels, between 1 and 4.
|
|
114
143
|
* @param {Object} [options.create] - describes a new image to be created.
|
|
115
|
-
* @param {number} [options.create.width]
|
|
116
|
-
* @param {number} [options.create.height]
|
|
117
|
-
* @param {number} [options.create.channels] - 3
|
|
144
|
+
* @param {number} [options.create.width] - integral number of pixels wide.
|
|
145
|
+
* @param {number} [options.create.height] - integral number of pixels high.
|
|
146
|
+
* @param {number} [options.create.channels] - integral number of channels, either 3 (RGB) or 4 (RGBA).
|
|
118
147
|
* @param {string|Object} [options.create.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
|
|
148
|
+
* @param {Object} [options.create.noise] - describes a noise to be created.
|
|
149
|
+
* @param {string} [options.create.noise.type] - type of generated noise, currently only `gaussian` is supported.
|
|
150
|
+
* @param {number} [options.create.noise.mean] - mean of pixels in generated noise.
|
|
151
|
+
* @param {number} [options.create.noise.sigma] - standard deviation of pixels in generated noise.
|
|
119
152
|
* @returns {Sharp}
|
|
120
153
|
* @throws {Error} Invalid parameters
|
|
121
154
|
*/
|
|
@@ -237,6 +270,7 @@ const Sharp = function (input, options) {
|
|
|
237
270
|
heifLossless: false,
|
|
238
271
|
heifCompression: 'av1',
|
|
239
272
|
heifSpeed: 5,
|
|
273
|
+
heifChromaSubsampling: '4:2:0',
|
|
240
274
|
tileSize: 256,
|
|
241
275
|
tileOverlap: 0,
|
|
242
276
|
tileContainer: 'fs',
|
package/lib/input.js
CHANGED
|
@@ -31,6 +31,9 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|
|
31
31
|
} else if (is.buffer(input)) {
|
|
32
32
|
// Buffer
|
|
33
33
|
inputDescriptor.buffer = input;
|
|
34
|
+
} else if (is.uint8Array(input)) {
|
|
35
|
+
// Uint8Array or Uint8ClampedArray
|
|
36
|
+
inputDescriptor.buffer = Buffer.from(input.buffer);
|
|
34
37
|
} else if (is.plainObject(input) && !is.defined(inputOptions)) {
|
|
35
38
|
// Plain Object descriptor, e.g. create
|
|
36
39
|
inputOptions = input;
|
|
@@ -134,22 +137,50 @@ function _createInputDescriptor (input, inputOptions, containerOptions) {
|
|
|
134
137
|
is.object(inputOptions.create) &&
|
|
135
138
|
is.integer(inputOptions.create.width) && inputOptions.create.width > 0 &&
|
|
136
139
|
is.integer(inputOptions.create.height) && inputOptions.create.height > 0 &&
|
|
137
|
-
is.integer(inputOptions.create.channels)
|
|
138
|
-
is.defined(inputOptions.create.background)
|
|
140
|
+
is.integer(inputOptions.create.channels)
|
|
139
141
|
) {
|
|
140
142
|
inputDescriptor.createWidth = inputOptions.create.width;
|
|
141
143
|
inputDescriptor.createHeight = inputOptions.create.height;
|
|
142
144
|
inputDescriptor.createChannels = inputOptions.create.channels;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
145
|
+
// Noise
|
|
146
|
+
if (is.defined(inputOptions.create.noise)) {
|
|
147
|
+
if (!is.object(inputOptions.create.noise)) {
|
|
148
|
+
throw new Error('Expected noise to be an object');
|
|
149
|
+
}
|
|
150
|
+
if (!is.inArray(inputOptions.create.noise.type, ['gaussian'])) {
|
|
151
|
+
throw new Error('Only gaussian noise is supported at the moment');
|
|
152
|
+
}
|
|
153
|
+
if (!is.inRange(inputOptions.create.channels, 1, 4)) {
|
|
154
|
+
throw is.invalidParameterError('create.channels', 'number between 1 and 4', inputOptions.create.channels);
|
|
155
|
+
}
|
|
156
|
+
inputDescriptor.createNoiseType = inputOptions.create.noise.type;
|
|
157
|
+
if (is.number(inputOptions.create.noise.mean) && is.inRange(inputOptions.create.noise.mean, 0, 10000)) {
|
|
158
|
+
inputDescriptor.createNoiseMean = inputOptions.create.noise.mean;
|
|
159
|
+
} else {
|
|
160
|
+
throw is.invalidParameterError('create.noise.mean', 'number between 0 and 10000', inputOptions.create.noise.mean);
|
|
161
|
+
}
|
|
162
|
+
if (is.number(inputOptions.create.noise.sigma) && is.inRange(inputOptions.create.noise.sigma, 0, 10000)) {
|
|
163
|
+
inputDescriptor.createNoiseSigma = inputOptions.create.noise.sigma;
|
|
164
|
+
} else {
|
|
165
|
+
throw is.invalidParameterError('create.noise.sigma', 'number between 0 and 10000', inputOptions.create.noise.sigma);
|
|
166
|
+
}
|
|
167
|
+
} else if (is.defined(inputOptions.create.background)) {
|
|
168
|
+
if (!is.inRange(inputOptions.create.channels, 3, 4)) {
|
|
169
|
+
throw is.invalidParameterError('create.channels', 'number between 3 and 4', inputOptions.create.channels);
|
|
170
|
+
}
|
|
171
|
+
const background = color(inputOptions.create.background);
|
|
172
|
+
inputDescriptor.createBackground = [
|
|
173
|
+
background.red(),
|
|
174
|
+
background.green(),
|
|
175
|
+
background.blue(),
|
|
176
|
+
Math.round(background.alpha() * 255)
|
|
177
|
+
];
|
|
178
|
+
} else {
|
|
179
|
+
throw new Error('Expected valid noise or background to create a new input image');
|
|
180
|
+
}
|
|
150
181
|
delete inputDescriptor.buffer;
|
|
151
182
|
} else {
|
|
152
|
-
throw new Error('Expected width, height
|
|
183
|
+
throw new Error('Expected valid width, height and channels to create a new input image');
|
|
153
184
|
}
|
|
154
185
|
}
|
|
155
186
|
} else if (is.defined(inputOptions)) {
|
package/lib/is.js
CHANGED
|
@@ -48,6 +48,15 @@ const buffer = function (val) {
|
|
|
48
48
|
return val instanceof Buffer;
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Is this value a Uint8Array or Uint8ClampedArray object?
|
|
53
|
+
* @private
|
|
54
|
+
*/
|
|
55
|
+
const uint8Array = function (val) {
|
|
56
|
+
// allow both since Uint8ClampedArray simply clamps the values between 0-255
|
|
57
|
+
return val instanceof Uint8Array || val instanceof Uint8ClampedArray;
|
|
58
|
+
};
|
|
59
|
+
|
|
51
60
|
/**
|
|
52
61
|
* Is this value a non-empty string?
|
|
53
62
|
* @private
|
|
@@ -110,6 +119,7 @@ module.exports = {
|
|
|
110
119
|
fn: fn,
|
|
111
120
|
bool: bool,
|
|
112
121
|
buffer: buffer,
|
|
122
|
+
uint8Array: uint8Array,
|
|
113
123
|
string: string,
|
|
114
124
|
number: number,
|
|
115
125
|
integer: integer,
|
package/lib/libvips.js
CHANGED
|
@@ -39,8 +39,9 @@ const cachePath = function () {
|
|
|
39
39
|
|
|
40
40
|
const globalLibvipsVersion = function () {
|
|
41
41
|
if (process.platform !== 'win32') {
|
|
42
|
-
const globalLibvipsVersion = spawnSync(`PKG_CONFIG_PATH="${pkgConfigPath()}" pkg-config --modversion vips-cpp`, spawnSyncOptions).stdout
|
|
43
|
-
|
|
42
|
+
const globalLibvipsVersion = spawnSync(`PKG_CONFIG_PATH="${pkgConfigPath()}" pkg-config --modversion vips-cpp`, spawnSyncOptions).stdout;
|
|
43
|
+
/* istanbul ignore next */
|
|
44
|
+
return (globalLibvipsVersion || '').trim();
|
|
44
45
|
} else {
|
|
45
46
|
return '';
|
|
46
47
|
}
|
package/lib/output.js
CHANGED
|
@@ -104,6 +104,20 @@ function toFile (fileOut, callback) {
|
|
|
104
104
|
* .then(({ data, info }) => { ... })
|
|
105
105
|
* .catch(err => { ... });
|
|
106
106
|
*
|
|
107
|
+
* @example
|
|
108
|
+
* const data = await sharp('my-image.jpg')
|
|
109
|
+
* // output the raw pixels
|
|
110
|
+
* .raw()
|
|
111
|
+
* .toBuffer();
|
|
112
|
+
*
|
|
113
|
+
* // create a more type safe way to work with the raw pixel data
|
|
114
|
+
* // this will not copy the data, instead it will change `data`s underlying ArrayBuffer
|
|
115
|
+
* // so `data` and `pixelArray` point to the same memory location
|
|
116
|
+
* const pixelArray = new Uint8ClampedArray(data.buffer);
|
|
117
|
+
*
|
|
118
|
+
* // When you are done changing the pixelArray, sharp takes the `pixelArray` as an input
|
|
119
|
+
* await sharp(pixelArray).toFile('my-changed-image.jpg');
|
|
120
|
+
*
|
|
107
121
|
* @param {Object} [options]
|
|
108
122
|
* @param {boolean} [options.resolveWithObject] Resolve the Promise with an Object containing `data` and `info` properties instead of resolving only with `data`.
|
|
109
123
|
* @param {Function} [callback]
|
|
@@ -472,15 +486,15 @@ function trySetAnimationOptions (source, target) {
|
|
|
472
486
|
* @param {Object} [options] - output options
|
|
473
487
|
* @param {number} [options.quality=80] - quality, integer 1-100
|
|
474
488
|
* @param {boolean} [options.force=true] - force TIFF output, otherwise attempt to use input format
|
|
475
|
-
* @param {
|
|
476
|
-
* @param {
|
|
489
|
+
* @param {string} [options.compression='jpeg'] - compression options: lzw, deflate, jpeg, ccittfax4
|
|
490
|
+
* @param {string} [options.predictor='horizontal'] - compression predictor options: none, horizontal, float
|
|
477
491
|
* @param {boolean} [options.pyramid=false] - write an image pyramid
|
|
478
492
|
* @param {boolean} [options.tile=false] - write a tiled tiff
|
|
479
|
-
* @param {
|
|
480
|
-
* @param {
|
|
493
|
+
* @param {number} [options.tileWidth=256] - horizontal tile size
|
|
494
|
+
* @param {number} [options.tileHeight=256] - vertical tile size
|
|
481
495
|
* @param {number} [options.xres=1.0] - horizontal resolution in pixels/mm
|
|
482
496
|
* @param {number} [options.yres=1.0] - vertical resolution in pixels/mm
|
|
483
|
-
* @param {
|
|
497
|
+
* @param {number} [options.bitdepth=8] - reduce bitdepth to 1, 2 or 4 bit
|
|
484
498
|
* @returns {Sharp}
|
|
485
499
|
* @throws {Error} Invalid options
|
|
486
500
|
*/
|
|
@@ -569,6 +583,7 @@ function tiff (options) {
|
|
|
569
583
|
* @param {number} [options.quality=50] - quality, integer 1-100
|
|
570
584
|
* @param {boolean} [options.lossless=false] - use lossless compression
|
|
571
585
|
* @param {boolean} [options.speed=5] - CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest)
|
|
586
|
+
* @param {string} [options.chromaSubsampling='4:2:0'] - set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling, requires libvips v8.11.0
|
|
572
587
|
* @returns {Sharp}
|
|
573
588
|
* @throws {Error} Invalid options
|
|
574
589
|
*/
|
|
@@ -586,9 +601,10 @@ function avif (options) {
|
|
|
586
601
|
*
|
|
587
602
|
* @param {Object} [options] - output options
|
|
588
603
|
* @param {number} [options.quality=50] - quality, integer 1-100
|
|
589
|
-
* @param {
|
|
604
|
+
* @param {string} [options.compression='av1'] - compression format: av1, hevc
|
|
590
605
|
* @param {boolean} [options.lossless=false] - use lossless compression
|
|
591
|
-
* @param {
|
|
606
|
+
* @param {number} [options.speed=5] - CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest)
|
|
607
|
+
* @param {string} [options.chromaSubsampling='4:2:0'] - set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling, requires libvips v8.11.0
|
|
592
608
|
* @returns {Sharp}
|
|
593
609
|
* @throws {Error} Invalid options
|
|
594
610
|
*/
|
|
@@ -622,6 +638,13 @@ function heif (options) {
|
|
|
622
638
|
throw is.invalidParameterError('speed', 'integer between 0 and 8', options.speed);
|
|
623
639
|
}
|
|
624
640
|
}
|
|
641
|
+
if (is.defined(options.chromaSubsampling)) {
|
|
642
|
+
if (is.string(options.chromaSubsampling) && is.inArray(options.chromaSubsampling, ['4:2:0', '4:4:4'])) {
|
|
643
|
+
this.options.heifChromaSubsampling = options.chromaSubsampling;
|
|
644
|
+
} else {
|
|
645
|
+
throw is.invalidParameterError('chromaSubsampling', 'one of: 4:2:0, 4:4:4', options.chromaSubsampling);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
625
648
|
}
|
|
626
649
|
return this._updateFormatOut('heif', options);
|
|
627
650
|
}
|
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, AVIF and TIFF images",
|
|
4
|
-
"version": "0.27.
|
|
4
|
+
"version": "0.27.1",
|
|
5
5
|
"author": "Lovell Fuller <npm@lovell.info>",
|
|
6
6
|
"homepage": "https://github.com/lovell/sharp",
|
|
7
7
|
"contributors": [
|
|
@@ -72,17 +72,19 @@
|
|
|
72
72
|
"Robert O'Rourke <robert@o-rourke.org>",
|
|
73
73
|
"Guillermo Alfonso Varela Chouciño <guillevch@gmail.com>",
|
|
74
74
|
"Christian Flintrup <chr@gigahost.dk>",
|
|
75
|
-
"Manan Jadhav <manan@motionden.com>"
|
|
75
|
+
"Manan Jadhav <manan@motionden.com>",
|
|
76
|
+
"Leon Radley <leon@radley.se>",
|
|
77
|
+
"alza54 <alza54@thiocod.in>"
|
|
76
78
|
],
|
|
77
79
|
"scripts": {
|
|
78
80
|
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
|
|
79
81
|
"clean": "rm -rf node_modules/ build/ vendor/ .nyc_output/ coverage/ test/fixtures/output.*",
|
|
80
82
|
"test": "semistandard && cpplint && npm run test-unit && npm run test-licensing",
|
|
81
|
-
"test-unit": "nyc --reporter=lcov --branches=99 mocha --slow=5000 --timeout=60000 ./test/unit/*.js",
|
|
83
|
+
"test-unit": "nyc --reporter=lcov --branches=99 mocha --parallel --slow=5000 --timeout=60000 ./test/unit/*.js",
|
|
82
84
|
"test-licensing": "license-checker --production --summary --onlyAllow=\"Apache-2.0;BSD;ISC;MIT\"",
|
|
83
85
|
"test-coverage": "./test/coverage/report.sh",
|
|
84
86
|
"test-leak": "./test/leak/leak.sh",
|
|
85
|
-
"docs-build": "documentation lint lib &&
|
|
87
|
+
"docs-build": "documentation lint lib && node docs/build && node docs/search-index/build",
|
|
86
88
|
"docs-serve": "cd docs && npx serve",
|
|
87
89
|
"docs-publish": "cd docs && npx firebase-tools deploy --project pixelplumbing --only hosting:pixelplumbing-sharp"
|
|
88
90
|
},
|
|
@@ -129,8 +131,8 @@
|
|
|
129
131
|
"devDependencies": {
|
|
130
132
|
"async": "^3.2.0",
|
|
131
133
|
"cc": "^3.0.1",
|
|
132
|
-
"decompress-zip": "^0.3.
|
|
133
|
-
"documentation": "^13.1.
|
|
134
|
+
"decompress-zip": "^0.3.3",
|
|
135
|
+
"documentation": "^13.1.1",
|
|
134
136
|
"exif-reader": "^1.0.3",
|
|
135
137
|
"icc": "^2.0.0",
|
|
136
138
|
"license-checker": "^25.0.1",
|
package/src/common.cc
CHANGED
|
@@ -109,7 +109,13 @@ namespace sharp {
|
|
|
109
109
|
descriptor->createChannels = AttrAsUint32(input, "createChannels");
|
|
110
110
|
descriptor->createWidth = AttrAsUint32(input, "createWidth");
|
|
111
111
|
descriptor->createHeight = AttrAsUint32(input, "createHeight");
|
|
112
|
-
|
|
112
|
+
if (HasAttr(input, "createNoiseType")) {
|
|
113
|
+
descriptor->createNoiseType = AttrAsStr(input, "createNoiseType");
|
|
114
|
+
descriptor->createNoiseMean = AttrAsDouble(input, "createNoiseMean");
|
|
115
|
+
descriptor->createNoiseSigma = AttrAsDouble(input, "createNoiseSigma");
|
|
116
|
+
} else {
|
|
117
|
+
descriptor->createBackground = AttrAsVectorOfDouble(input, "createBackground");
|
|
118
|
+
}
|
|
113
119
|
}
|
|
114
120
|
// Limit input images to a given number of pixels, where pixels = width * height
|
|
115
121
|
descriptor->limitInputPixels = AttrAsUint32(input, "limitInputPixels");
|
|
@@ -189,31 +195,38 @@ namespace sharp {
|
|
|
189
195
|
return id;
|
|
190
196
|
}
|
|
191
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Regenerate this table with something like:
|
|
200
|
+
*
|
|
201
|
+
* $ vips -l foreign | grep -i load | awk '{ print $2, $1; }'
|
|
202
|
+
*
|
|
203
|
+
* Plus a bit of editing.
|
|
204
|
+
*/
|
|
192
205
|
std::map<std::string, ImageType> loaderToType = {
|
|
193
|
-
{ "
|
|
194
|
-
{ "
|
|
195
|
-
{ "
|
|
196
|
-
{ "
|
|
197
|
-
{ "
|
|
198
|
-
{ "
|
|
199
|
-
{ "
|
|
200
|
-
{ "
|
|
201
|
-
{ "
|
|
202
|
-
{ "
|
|
203
|
-
{ "
|
|
204
|
-
{ "
|
|
205
|
-
{ "
|
|
206
|
-
{ "
|
|
207
|
-
{ "
|
|
208
|
-
{ "
|
|
209
|
-
{ "
|
|
210
|
-
{ "
|
|
211
|
-
{ "
|
|
212
|
-
{ "
|
|
213
|
-
{ "
|
|
214
|
-
{ "
|
|
215
|
-
{ "
|
|
216
|
-
{ "
|
|
206
|
+
{ "VipsForeignLoadJpegFile", ImageType::JPEG },
|
|
207
|
+
{ "VipsForeignLoadJpegBuffer", ImageType::JPEG },
|
|
208
|
+
{ "VipsForeignLoadPngFile", ImageType::PNG },
|
|
209
|
+
{ "VipsForeignLoadPngBuffer", ImageType::PNG },
|
|
210
|
+
{ "VipsForeignLoadWebpFile", ImageType::WEBP },
|
|
211
|
+
{ "VipsForeignLoadWebpBuffer", ImageType::WEBP },
|
|
212
|
+
{ "VipsForeignLoadTiffFile", ImageType::TIFF },
|
|
213
|
+
{ "VipsForeignLoadTiffBuffer", ImageType::TIFF },
|
|
214
|
+
{ "VipsForeignLoadGifFile", ImageType::GIF },
|
|
215
|
+
{ "VipsForeignLoadGifBuffer", ImageType::GIF },
|
|
216
|
+
{ "VipsForeignLoadSvgFile", ImageType::SVG },
|
|
217
|
+
{ "VipsForeignLoadSvgBuffer", ImageType::SVG },
|
|
218
|
+
{ "VipsForeignLoadHeifFile", ImageType::HEIF },
|
|
219
|
+
{ "VipsForeignLoadHeifBuffer", ImageType::HEIF },
|
|
220
|
+
{ "VipsForeignLoadPdfFile", ImageType::PDF },
|
|
221
|
+
{ "VipsForeignLoadPdfBuffer", ImageType::PDF },
|
|
222
|
+
{ "VipsForeignLoadMagickFile", ImageType::MAGICK },
|
|
223
|
+
{ "VipsForeignLoadMagickBuffer", ImageType::MAGICK },
|
|
224
|
+
{ "VipsForeignLoadOpenslide", ImageType::OPENSLIDE },
|
|
225
|
+
{ "VipsForeignLoadPpmFile", ImageType::PPM },
|
|
226
|
+
{ "VipsForeignLoadFits", ImageType::FITS },
|
|
227
|
+
{ "VipsForeignLoadOpenexr", ImageType::EXR },
|
|
228
|
+
{ "VipsForeignLoadVips", ImageType::VIPS },
|
|
229
|
+
{ "VipsForeignLoadRaw", ImageType::RAW }
|
|
217
230
|
};
|
|
218
231
|
|
|
219
232
|
/*
|
|
@@ -223,7 +236,7 @@ namespace sharp {
|
|
|
223
236
|
ImageType imageType = ImageType::UNKNOWN;
|
|
224
237
|
char const *load = vips_foreign_find_load_buffer(buffer, length);
|
|
225
238
|
if (load != nullptr) {
|
|
226
|
-
auto it = loaderToType.find(
|
|
239
|
+
auto it = loaderToType.find(load);
|
|
227
240
|
if (it != loaderToType.end()) {
|
|
228
241
|
imageType = it->second;
|
|
229
242
|
}
|
|
@@ -238,7 +251,7 @@ namespace sharp {
|
|
|
238
251
|
ImageType imageType = ImageType::UNKNOWN;
|
|
239
252
|
char const *load = vips_foreign_find_load(file);
|
|
240
253
|
if (load != nullptr) {
|
|
241
|
-
auto it = loaderToType.find(
|
|
254
|
+
auto it = loaderToType.find(load);
|
|
242
255
|
if (it != loaderToType.end()) {
|
|
243
256
|
imageType = it->second;
|
|
244
257
|
}
|
|
@@ -318,15 +331,35 @@ namespace sharp {
|
|
|
318
331
|
} else {
|
|
319
332
|
if (descriptor->createChannels > 0) {
|
|
320
333
|
// Create new image
|
|
321
|
-
|
|
322
|
-
descriptor->
|
|
323
|
-
descriptor->
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
334
|
+
if (descriptor->createNoiseType == "gaussian") {
|
|
335
|
+
int const channels = descriptor->createChannels;
|
|
336
|
+
image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight);
|
|
337
|
+
std::vector<VImage> bands = {};
|
|
338
|
+
bands.reserve(channels);
|
|
339
|
+
for (int _band = 0; _band < channels; _band++) {
|
|
340
|
+
bands.push_back(image.gaussnoise(
|
|
341
|
+
descriptor->createWidth,
|
|
342
|
+
descriptor->createHeight,
|
|
343
|
+
VImage::option()->set("mean", descriptor->createNoiseMean)->set("sigma", descriptor->createNoiseSigma)));
|
|
344
|
+
}
|
|
345
|
+
image = image.bandjoin(bands);
|
|
346
|
+
image = image.cast(VipsBandFormat::VIPS_FORMAT_UCHAR);
|
|
347
|
+
if (channels < 3) {
|
|
348
|
+
image = image.colourspace(VIPS_INTERPRETATION_B_W);
|
|
349
|
+
} else {
|
|
350
|
+
image = image.colourspace(VIPS_INTERPRETATION_sRGB);
|
|
351
|
+
}
|
|
352
|
+
} else {
|
|
353
|
+
std::vector<double> background = {
|
|
354
|
+
descriptor->createBackground[0],
|
|
355
|
+
descriptor->createBackground[1],
|
|
356
|
+
descriptor->createBackground[2]
|
|
357
|
+
};
|
|
358
|
+
if (descriptor->createChannels == 4) {
|
|
359
|
+
background.push_back(descriptor->createBackground[3]);
|
|
360
|
+
}
|
|
361
|
+
image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight).new_from_image(background);
|
|
328
362
|
}
|
|
329
|
-
image = VImage::new_matrix(descriptor->createWidth, descriptor->createHeight).new_from_image(background);
|
|
330
363
|
image.get_image()->Type = VIPS_INTERPRETATION_sRGB;
|
|
331
364
|
imageType = ImageType::RAW;
|
|
332
365
|
} else {
|
package/src/common.h
CHANGED
|
@@ -64,6 +64,9 @@ namespace sharp {
|
|
|
64
64
|
int createWidth;
|
|
65
65
|
int createHeight;
|
|
66
66
|
std::vector<double> createBackground;
|
|
67
|
+
std::string createNoiseType;
|
|
68
|
+
double createNoiseMean;
|
|
69
|
+
double createNoiseSigma;
|
|
67
70
|
|
|
68
71
|
InputDescriptor():
|
|
69
72
|
buffer(nullptr),
|
|
@@ -82,7 +85,9 @@ namespace sharp {
|
|
|
82
85
|
createChannels(0),
|
|
83
86
|
createWidth(0),
|
|
84
87
|
createHeight(0),
|
|
85
|
-
createBackground{ 0.0, 0.0, 0.0, 255.0 }
|
|
88
|
+
createBackground{ 0.0, 0.0, 0.0, 255.0 },
|
|
89
|
+
createNoiseMean(0.0),
|
|
90
|
+
createNoiseSigma(0.0) {}
|
|
86
91
|
};
|
|
87
92
|
|
|
88
93
|
// Convenience methods to access the attributes of a Napi::Object
|
package/src/metadata.cc
CHANGED
|
@@ -74,6 +74,9 @@ class MetadataWorker : public Napi::AsyncWorker {
|
|
|
74
74
|
if (image.get_typeof("heif-primary") == G_TYPE_INT) {
|
|
75
75
|
baton->pagePrimary = image.get_int("heif-primary");
|
|
76
76
|
}
|
|
77
|
+
if (image.get_typeof("heif-compression") == VIPS_TYPE_REF_STRING) {
|
|
78
|
+
baton->compression = image.get_string("heif-compression");
|
|
79
|
+
}
|
|
77
80
|
if (image.get_typeof("openslide.level-count") == VIPS_TYPE_REF_STRING) {
|
|
78
81
|
int const levels = std::stoi(image.get_string("openslide.level-count"));
|
|
79
82
|
for (int l = 0; l < levels; l++) {
|
|
@@ -186,6 +189,9 @@ class MetadataWorker : public Napi::AsyncWorker {
|
|
|
186
189
|
if (baton->pagePrimary > -1) {
|
|
187
190
|
info.Set("pagePrimary", baton->pagePrimary);
|
|
188
191
|
}
|
|
192
|
+
if (!baton->compression.empty()) {
|
|
193
|
+
info.Set("compression", baton->compression);
|
|
194
|
+
}
|
|
189
195
|
if (!baton->levels.empty()) {
|
|
190
196
|
int i = 0;
|
|
191
197
|
Napi::Array levels = Napi::Array::New(env, static_cast<size_t>(baton->levels.size()));
|
package/src/metadata.h
CHANGED
package/src/pipeline.cc
CHANGED
|
@@ -730,7 +730,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
730
730
|
if (baton->formatOut == "jpeg" || (baton->formatOut == "input" && inputImageType == sharp::ImageType::JPEG)) {
|
|
731
731
|
// Write JPEG to buffer
|
|
732
732
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::JPEG);
|
|
733
|
-
VipsArea *area =
|
|
733
|
+
VipsArea *area = reinterpret_cast<VipsArea*>(image.jpegsave_buffer(VImage::option()
|
|
734
734
|
->set("strip", !baton->withMetadata)
|
|
735
735
|
->set("Q", baton->jpegQuality)
|
|
736
736
|
->set("interlace", baton->jpegProgressive)
|
|
@@ -757,7 +757,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
757
757
|
inputImageType == sharp::ImageType::SVG))) {
|
|
758
758
|
// Write PNG to buffer
|
|
759
759
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::PNG);
|
|
760
|
-
VipsArea *area =
|
|
760
|
+
VipsArea *area = reinterpret_cast<VipsArea*>(image.pngsave_buffer(VImage::option()
|
|
761
761
|
->set("strip", !baton->withMetadata)
|
|
762
762
|
->set("interlace", baton->pngProgressive)
|
|
763
763
|
->set("compression", baton->pngCompressionLevel)
|
|
@@ -775,7 +775,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
775
775
|
(baton->formatOut == "input" && inputImageType == sharp::ImageType::WEBP)) {
|
|
776
776
|
// Write WEBP to buffer
|
|
777
777
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::WEBP);
|
|
778
|
-
VipsArea *area =
|
|
778
|
+
VipsArea *area = reinterpret_cast<VipsArea*>(image.webpsave_buffer(VImage::option()
|
|
779
779
|
->set("strip", !baton->withMetadata)
|
|
780
780
|
->set("Q", baton->webpQuality)
|
|
781
781
|
->set("lossless", baton->webpLossless)
|
|
@@ -792,7 +792,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
792
792
|
(baton->formatOut == "input" && inputImageType == sharp::ImageType::GIF && supportsGifOutput)) {
|
|
793
793
|
// Write GIF to buffer
|
|
794
794
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::GIF);
|
|
795
|
-
VipsArea *area =
|
|
795
|
+
VipsArea *area = reinterpret_cast<VipsArea*>(image.magicksave_buffer(VImage::option()
|
|
796
796
|
->set("strip", !baton->withMetadata)
|
|
797
797
|
->set("optimize_gif_frames", TRUE)
|
|
798
798
|
->set("optimize_gif_transparency", TRUE)
|
|
@@ -813,7 +813,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
813
813
|
if (baton->tiffPredictor == VIPS_FOREIGN_TIFF_PREDICTOR_FLOAT) {
|
|
814
814
|
image = image.cast(VIPS_FORMAT_FLOAT);
|
|
815
815
|
}
|
|
816
|
-
VipsArea *area =
|
|
816
|
+
VipsArea *area = reinterpret_cast<VipsArea*>(image.tiffsave_buffer(VImage::option()
|
|
817
817
|
->set("strip", !baton->withMetadata)
|
|
818
818
|
->set("Q", baton->tiffQuality)
|
|
819
819
|
->set("bitdepth", baton->tiffBitdepth)
|
|
@@ -833,11 +833,15 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
833
833
|
} else if (baton->formatOut == "heif" ||
|
|
834
834
|
(baton->formatOut == "input" && inputImageType == sharp::ImageType::HEIF)) {
|
|
835
835
|
// Write HEIF to buffer
|
|
836
|
-
VipsArea *area =
|
|
836
|
+
VipsArea *area = reinterpret_cast<VipsArea*>(image.heifsave_buffer(VImage::option()
|
|
837
837
|
->set("strip", !baton->withMetadata)
|
|
838
838
|
->set("compression", baton->heifCompression)
|
|
839
839
|
->set("Q", baton->heifQuality)
|
|
840
840
|
->set("speed", baton->heifSpeed)
|
|
841
|
+
#if defined(VIPS_TYPE_FOREIGN_SUBSAMPLE)
|
|
842
|
+
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
|
|
843
|
+
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
|
844
|
+
#endif
|
|
841
845
|
->set("lossless", baton->heifLossless)));
|
|
842
846
|
baton->bufferOut = static_cast<char*>(area->data);
|
|
843
847
|
baton->bufferOutLength = area->length;
|
|
@@ -951,6 +955,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
951
955
|
sharp::AssertImageTypeDimensions(image, sharp::ImageType::JPEG);
|
|
952
956
|
baton->channels = std::min(baton->channels, 3);
|
|
953
957
|
}
|
|
958
|
+
// Cast pixel values to float, if required
|
|
959
|
+
if (baton->tiffPredictor == VIPS_FOREIGN_TIFF_PREDICTOR_FLOAT) {
|
|
960
|
+
image = image.cast(VIPS_FORMAT_FLOAT);
|
|
961
|
+
}
|
|
954
962
|
image.tiffsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
|
955
963
|
->set("strip", !baton->withMetadata)
|
|
956
964
|
->set("Q", baton->tiffQuality)
|
|
@@ -972,6 +980,10 @@ class PipelineWorker : public Napi::AsyncWorker {
|
|
|
972
980
|
->set("Q", baton->heifQuality)
|
|
973
981
|
->set("compression", baton->heifCompression)
|
|
974
982
|
->set("speed", baton->heifSpeed)
|
|
983
|
+
#if defined(VIPS_TYPE_FOREIGN_SUBSAMPLE)
|
|
984
|
+
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
|
|
985
|
+
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
|
|
986
|
+
#endif
|
|
975
987
|
->set("lossless", baton->heifLossless));
|
|
976
988
|
baton->formatOut = "heif";
|
|
977
989
|
} else if (baton->formatOut == "dz" || isDz || isDzZip) {
|
|
@@ -1396,6 +1408,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
|
|
1396
1408
|
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
|
|
1397
1409
|
sharp::AttrAsStr(options, "heifCompression").data()));
|
|
1398
1410
|
baton->heifSpeed = sharp::AttrAsUint32(options, "heifSpeed");
|
|
1411
|
+
baton->heifChromaSubsampling = sharp::AttrAsStr(options, "heifChromaSubsampling");
|
|
1399
1412
|
|
|
1400
1413
|
// Animated output
|
|
1401
1414
|
if (sharp::HasAttr(options, "pageHeight")) {
|
package/src/pipeline.h
CHANGED
|
@@ -162,6 +162,7 @@ struct PipelineBaton {
|
|
|
162
162
|
int heifQuality;
|
|
163
163
|
VipsForeignHeifCompression heifCompression;
|
|
164
164
|
int heifSpeed;
|
|
165
|
+
std::string heifChromaSubsampling;
|
|
165
166
|
bool heifLossless;
|
|
166
167
|
std::string err;
|
|
167
168
|
bool withMetadata;
|
|
@@ -282,6 +283,7 @@ struct PipelineBaton {
|
|
|
282
283
|
heifQuality(50),
|
|
283
284
|
heifCompression(VIPS_FOREIGN_HEIF_COMPRESSION_AV1),
|
|
284
285
|
heifSpeed(5),
|
|
286
|
+
heifChromaSubsampling("4:2:0"),
|
|
285
287
|
heifLossless(false),
|
|
286
288
|
withMetadata(false),
|
|
287
289
|
withMetadataOrientation(-1),
|