Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d5ecc537af | ||
|
|
c7a49054fd | ||
|
|
a2314c4aa0 | ||
|
|
1717173f17 | ||
|
|
e44c12f029 | ||
|
|
1a98c390fc | ||
|
|
91902740e4 | ||
|
|
6aa6a93b44 | ||
|
|
b4135ac9b3 | ||
|
|
78906e6551 |
2
.github/ISSUE_TEMPLATE/installation.md
vendored
@@ -7,7 +7,7 @@ assignees: ''
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Did you see the [documentation relating to installation](https://sharp.pixelplumbing.com/en/stable/install/)?
|
Did you see the [documentation relating to installation](https://sharp.pixelplumbing.com/install)?
|
||||||
|
|
||||||
Have you ensured the platform and version of Node.js used for `npm install` is the same as the platform and version of Node.js used at runtime?
|
Have you ensured the platform and version of Node.js used for `npm install` is the same as the platform and version of Node.js used at runtime?
|
||||||
|
|
||||||
|
|||||||
@@ -90,10 +90,10 @@ readableStream
|
|||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
Visit [sharp.pixelplumbing.com](https://sharp.pixelplumbing.com/) for complete
|
Visit [sharp.pixelplumbing.com](https://sharp.pixelplumbing.com/) for complete
|
||||||
[installation instructions](https://sharp.pixelplumbing.com/page/install),
|
[installation instructions](https://sharp.pixelplumbing.com/install),
|
||||||
[API documentation](https://sharp.pixelplumbing.com/page/api),
|
[API documentation](https://sharp.pixelplumbing.com/api-constructor),
|
||||||
[benchmark tests](https://sharp.pixelplumbing.com/page/performance) and
|
[benchmark tests](https://sharp.pixelplumbing.com/performance) and
|
||||||
[changelog](https://sharp.pixelplumbing.com/page/changelog).
|
[changelog](https://sharp.pixelplumbing.com/changelog).
|
||||||
|
|
||||||
### Contributing
|
### Contributing
|
||||||
|
|
||||||
|
|||||||
@@ -88,9 +88,9 @@ image
|
|||||||
|
|
||||||
Returns **[Promise][5]<[Object][6]>**
|
Returns **[Promise][5]<[Object][6]>**
|
||||||
|
|
||||||
[1]: https://github.com/libvips/libvips/blob/master/libvips/iofuncs/enumtypes.c#L636
|
[1]: https://libvips.github.io/libvips/API/current/VipsImage.html#VipsInterpretation
|
||||||
|
|
||||||
[2]: https://github.com/libvips/libvips/blob/master/libvips/iofuncs/enumtypes.c#L672
|
[2]: https://libvips.github.io/libvips/API/current/VipsImage.html#VipsBandFormat
|
||||||
|
|
||||||
[3]: https://www.npmjs.com/package/icc
|
[3]: https://www.npmjs.com/package/icc
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,14 @@
|
|||||||
|
|
||||||
Requires libvips v8.9.0.
|
Requires libvips v8.9.0.
|
||||||
|
|
||||||
|
### v0.24.1 - 15<sup>th</sup> February 2020
|
||||||
|
|
||||||
|
* Prevent use of sequentialRead for EXIF-based rotate operation.
|
||||||
|
[#2042](https://github.com/lovell/sharp/issues/2042)
|
||||||
|
|
||||||
|
* Ensure RGBA LZW TIFF returns correct channel count.
|
||||||
|
[#2064](https://github.com/lovell/sharp/issues/2064)
|
||||||
|
|
||||||
### v0.24.0 - 16<sup>th</sup> January 2020
|
### v0.24.0 - 16<sup>th</sup> January 2020
|
||||||
|
|
||||||
* Drop support for Node.js 8.
|
* Drop support for Node.js 8.
|
||||||
|
|||||||
@@ -25,6 +25,11 @@
|
|||||||
"destination": "/install",
|
"destination": "/install",
|
||||||
"type": 301
|
"type": 301
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"source": "/page/install",
|
||||||
|
"destination": "/install",
|
||||||
|
"type": 301
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"source": "**/api-constructor/**",
|
"source": "**/api-constructor/**",
|
||||||
"destination": "/api-constructor",
|
"destination": "/api-constructor",
|
||||||
@@ -70,16 +75,31 @@
|
|||||||
"destination": "/api-utility",
|
"destination": "/api-utility",
|
||||||
"type": 301
|
"type": 301
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"source": "/page/api",
|
||||||
|
"destination": "/api-constructor",
|
||||||
|
"type": 301
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"source": "**/performance/**",
|
"source": "**/performance/**",
|
||||||
"destination": "/performance",
|
"destination": "/performance",
|
||||||
"type": 301
|
"type": 301
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"source": "/page/performance",
|
||||||
|
"destination": "/performance",
|
||||||
|
"type": 301
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"source": "**/changelog/**",
|
"source": "**/changelog/**",
|
||||||
"destination": "/changelog",
|
"destination": "/changelog",
|
||||||
"type": 301
|
"type": 301
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"source": "/page/changelog",
|
||||||
|
"destination": "/changelog",
|
||||||
|
"type": 301
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"source": "/en/**",
|
"source": "/en/**",
|
||||||
"destination": "/",
|
"destination": "/",
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
docuteGoogleAnalytics('UA-13034748-12'),
|
docuteGoogleAnalytics('UA-13034748-12'),
|
||||||
docuteApiTitlePlugin
|
docuteApiTitlePlugin
|
||||||
],
|
],
|
||||||
sourcePath: 'https://cdn.jsdelivr.net/gh/lovell/sharp@v0.24.0/docs',
|
sourcePath: 'https://cdn.jsdelivr.net/gh/lovell/sharp@v0.24.1/docs',
|
||||||
nav: [
|
nav: [
|
||||||
{
|
{
|
||||||
title: 'Funding',
|
title: 'Funding',
|
||||||
|
|||||||
@@ -54,6 +54,8 @@ must be the same as the platform and major version of Node.js used at runtime.
|
|||||||
|
|
||||||
The `npm install --unsafe-perm` flag must be used when installing as `root` or a `sudo` user.
|
The `npm install --unsafe-perm` flag must be used when installing as `root` or a `sudo` user.
|
||||||
|
|
||||||
|
The `npm install --ignore-scripts=false` flag must be used when `npm` has been configured to ignore installation scripts.
|
||||||
|
|
||||||
Check the output of running `npm install --verbose sharp` for useful error messages.
|
Check the output of running `npm install --verbose sharp` for useful error messages.
|
||||||
|
|
||||||
## Custom libvips
|
## Custom libvips
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ try {
|
|||||||
help.push('- Ensure the version of Node.js used at install time matches that used at runtime');
|
help.push('- Ensure the version of Node.js used at install time matches that used at runtime');
|
||||||
} else if (/invalid ELF header/.test(err.message)) {
|
} else if (/invalid ELF header/.test(err.message)) {
|
||||||
help.push(`- Ensure "${process.platform}" is used at install time as well as runtime`);
|
help.push(`- Ensure "${process.platform}" is used at install time as well as runtime`);
|
||||||
|
} else if (/dylib/.test(err.message) && /Incompatible library version/.test(err.message)) {
|
||||||
|
help.push('- Run "brew update && brew upgrade vips"');
|
||||||
} else if (/Cannot find module/.test(err.message)) {
|
} else if (/Cannot find module/.test(err.message)) {
|
||||||
help.push('- Run "npm rebuild --verbose sharp" and look for errors');
|
help.push('- Run "npm rebuild --verbose sharp" and look for errors');
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -187,9 +187,9 @@ function _isStreamInput () {
|
|||||||
* - `size`: Total size of image in bytes, for Stream and Buffer input only
|
* - `size`: Total size of image in bytes, for Stream and Buffer input only
|
||||||
* - `width`: Number of pixels wide (EXIF orientation is not taken into consideration)
|
* - `width`: Number of pixels wide (EXIF orientation is not taken into consideration)
|
||||||
* - `height`: Number of pixels high (EXIF orientation is not taken into consideration)
|
* - `height`: Number of pixels high (EXIF orientation is not taken into consideration)
|
||||||
* - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://github.com/libvips/libvips/blob/master/libvips/iofuncs/enumtypes.c#L636)
|
* - `space`: Name of colour space interpretation e.g. `srgb`, `rgb`, `cmyk`, `lab`, `b-w` [...](https://libvips.github.io/libvips/API/current/VipsImage.html#VipsInterpretation)
|
||||||
* - `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
|
* - `channels`: Number of bands e.g. `3` for sRGB, `4` for CMYK
|
||||||
* - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://github.com/libvips/libvips/blob/master/libvips/iofuncs/enumtypes.c#L672)
|
* - `depth`: Name of pixel depth format e.g. `uchar`, `char`, `ushort`, `float` [...](https://libvips.github.io/libvips/API/current/VipsImage.html#VipsBandFormat)
|
||||||
* - `density`: Number of pixels per inch (DPI), if present
|
* - `density`: Number of pixels per inch (DPI), if present
|
||||||
* - `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
|
* - `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
|
||||||
* - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
* - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
||||||
|
|||||||
12
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "sharp",
|
"name": "sharp",
|
||||||
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
|
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
|
||||||
"version": "0.24.0",
|
"version": "0.24.1",
|
||||||
"author": "Lovell Fuller <npm@lovell.info>",
|
"author": "Lovell Fuller <npm@lovell.info>",
|
||||||
"homepage": "https://github.com/lovell/sharp",
|
"homepage": "https://github.com/lovell/sharp",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
@@ -112,25 +112,25 @@
|
|||||||
"nan": "^2.14.0",
|
"nan": "^2.14.0",
|
||||||
"npmlog": "^4.1.2",
|
"npmlog": "^4.1.2",
|
||||||
"prebuild-install": "^5.3.3",
|
"prebuild-install": "^5.3.3",
|
||||||
"semver": "^7.1.1",
|
"semver": "^7.1.3",
|
||||||
"simple-get": "^3.1.0",
|
"simple-get": "^3.1.0",
|
||||||
"tar": "^5.0.5",
|
"tar": "^6.0.1",
|
||||||
"tunnel-agent": "^0.6.0"
|
"tunnel-agent": "^0.6.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"async": "^3.1.0",
|
"async": "^3.1.1",
|
||||||
"cc": "^2.0.1",
|
"cc": "^2.0.1",
|
||||||
"decompress-zip": "^0.3.2",
|
"decompress-zip": "^0.3.2",
|
||||||
"documentation": "^12.1.4",
|
"documentation": "^12.1.4",
|
||||||
"exif-reader": "^1.0.3",
|
"exif-reader": "^1.0.3",
|
||||||
"icc": "^1.0.0",
|
"icc": "^1.0.0",
|
||||||
"license-checker": "^25.0.1",
|
"license-checker": "^25.0.1",
|
||||||
"mocha": "^7.0.0",
|
"mocha": "^7.0.1",
|
||||||
"mock-fs": "^4.10.4",
|
"mock-fs": "^4.10.4",
|
||||||
"nyc": "^15.0.0",
|
"nyc": "^15.0.0",
|
||||||
"prebuild": "^10.0.0",
|
"prebuild": "^10.0.0",
|
||||||
"prebuild-ci": "^3.1.0",
|
"prebuild-ci": "^3.1.0",
|
||||||
"rimraf": "^3.0.0",
|
"rimraf": "^3.0.2",
|
||||||
"semistandard": "^14.2.0"
|
"semistandard": "^14.2.0"
|
||||||
},
|
},
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
|||||||
@@ -764,6 +764,7 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
// Write TIFF to buffer
|
// Write TIFF to buffer
|
||||||
if (baton->tiffCompression == VIPS_FOREIGN_TIFF_COMPRESSION_JPEG) {
|
if (baton->tiffCompression == VIPS_FOREIGN_TIFF_COMPRESSION_JPEG) {
|
||||||
sharp::AssertImageTypeDimensions(image, ImageType::JPEG);
|
sharp::AssertImageTypeDimensions(image, ImageType::JPEG);
|
||||||
|
baton->channels = std::min(baton->channels, 3);
|
||||||
}
|
}
|
||||||
// Cast pixel values to float, if required
|
// Cast pixel values to float, if required
|
||||||
if (baton->tiffPredictor == VIPS_FOREIGN_TIFF_PREDICTOR_FLOAT) {
|
if (baton->tiffPredictor == VIPS_FOREIGN_TIFF_PREDICTOR_FLOAT) {
|
||||||
@@ -786,7 +787,6 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
area->free_fn = nullptr;
|
area->free_fn = nullptr;
|
||||||
vips_area_unref(area);
|
vips_area_unref(area);
|
||||||
baton->formatOut = "tiff";
|
baton->formatOut = "tiff";
|
||||||
baton->channels = std::min(baton->channels, 3);
|
|
||||||
} else if (baton->formatOut == "heif" || (baton->formatOut == "input" && inputImageType == ImageType::HEIF)) {
|
} else if (baton->formatOut == "heif" || (baton->formatOut == "input" && inputImageType == ImageType::HEIF)) {
|
||||||
// Write HEIF to buffer
|
// Write HEIF to buffer
|
||||||
VipsArea *area = VIPS_AREA(image.heifsave_buffer(VImage::option()
|
VipsArea *area = VIPS_AREA(image.heifsave_buffer(VImage::option()
|
||||||
@@ -887,6 +887,7 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
// Write TIFF to file
|
// Write TIFF to file
|
||||||
if (baton->tiffCompression == VIPS_FOREIGN_TIFF_COMPRESSION_JPEG) {
|
if (baton->tiffCompression == VIPS_FOREIGN_TIFF_COMPRESSION_JPEG) {
|
||||||
sharp::AssertImageTypeDimensions(image, ImageType::JPEG);
|
sharp::AssertImageTypeDimensions(image, ImageType::JPEG);
|
||||||
|
baton->channels = std::min(baton->channels, 3);
|
||||||
}
|
}
|
||||||
image.tiffsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
image.tiffsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
||||||
->set("strip", !baton->withMetadata)
|
->set("strip", !baton->withMetadata)
|
||||||
@@ -901,7 +902,6 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
->set("xres", baton->tiffXres)
|
->set("xres", baton->tiffXres)
|
||||||
->set("yres", baton->tiffYres));
|
->set("yres", baton->tiffYres));
|
||||||
baton->formatOut = "tiff";
|
baton->formatOut = "tiff";
|
||||||
baton->channels = std::min(baton->channels, 3);
|
|
||||||
} else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) ||
|
} else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) ||
|
||||||
(willMatchInput && inputImageType == ImageType::HEIF)) {
|
(willMatchInput && inputImageType == ImageType::HEIF)) {
|
||||||
// Write HEIF to file
|
// Write HEIF to file
|
||||||
@@ -1404,7 +1404,9 @@ NAN_METHOD(pipeline) {
|
|||||||
baton->trimThreshold > 0.0 ||
|
baton->trimThreshold > 0.0 ||
|
||||||
baton->normalise ||
|
baton->normalise ||
|
||||||
baton->position == 16 || baton->position == 17 ||
|
baton->position == 16 || baton->position == 17 ||
|
||||||
baton->angle != 0 || baton->rotationAngle != 0.0
|
baton->angle % 360 != 0 ||
|
||||||
|
fmod(baton->rotationAngle, 360.0) != 0.0 ||
|
||||||
|
baton->useExifOrientation
|
||||||
) {
|
) {
|
||||||
baton->input->access = VIPS_ACCESS_RANDOM;
|
baton->input->access = VIPS_ACCESS_RANDOM;
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
test/fixtures/expected/overlay-offset-0.jpg
vendored
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 2.0 KiB |
BIN
test/fixtures/expected/overlay-offset-with-tile.jpg
vendored
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 2.9 KiB |
@@ -141,7 +141,7 @@ describe('composite', () => {
|
|||||||
|
|
||||||
it('zero offset', done => {
|
it('zero offset', done => {
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
.resize(400)
|
.resize(80)
|
||||||
.composite([{
|
.composite([{
|
||||||
input: fixtures.inputPngWithTransparency16bit,
|
input: fixtures.inputPngWithTransparency16bit,
|
||||||
top: 0,
|
top: 0,
|
||||||
@@ -157,7 +157,7 @@ describe('composite', () => {
|
|||||||
|
|
||||||
it('offset and gravity', done => {
|
it('offset and gravity', done => {
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
.resize(400)
|
.resize(80)
|
||||||
.composite([{
|
.composite([{
|
||||||
input: fixtures.inputPngWithTransparency16bit,
|
input: fixtures.inputPngWithTransparency16bit,
|
||||||
left: 10,
|
left: 10,
|
||||||
@@ -174,7 +174,7 @@ describe('composite', () => {
|
|||||||
|
|
||||||
it('offset, gravity and tile', done => {
|
it('offset, gravity and tile', done => {
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
.resize(400)
|
.resize(80)
|
||||||
.composite([{
|
.composite([{
|
||||||
input: fixtures.inputPngWithTransparency16bit,
|
input: fixtures.inputPngWithTransparency16bit,
|
||||||
left: 10,
|
left: 10,
|
||||||
@@ -192,7 +192,7 @@ describe('composite', () => {
|
|||||||
|
|
||||||
it('offset and tile', done => {
|
it('offset and tile', done => {
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
.resize(400)
|
.resize(80)
|
||||||
.composite([{
|
.composite([{
|
||||||
input: fixtures.inputPngWithTransparency16bit,
|
input: fixtures.inputPngWithTransparency16bit,
|
||||||
left: 10,
|
left: 10,
|
||||||
|
|||||||
@@ -208,11 +208,48 @@ describe('TIFF', function () {
|
|||||||
.toFile(fixtures.outputTiff, (err, info) => {
|
.toFile(fixtures.outputTiff, (err, info) => {
|
||||||
if (err) throw err;
|
if (err) throw err;
|
||||||
assert.strictEqual('tiff', info.format);
|
assert.strictEqual('tiff', info.format);
|
||||||
|
assert.strictEqual(3, info.channels);
|
||||||
assert(info.size < startSize);
|
assert(info.size < startSize);
|
||||||
rimraf(fixtures.outputTiff, done);
|
rimraf(fixtures.outputTiff, done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('TIFF LZW RGBA toFile', () =>
|
||||||
|
sharp({
|
||||||
|
create: {
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
channels: 4,
|
||||||
|
background: 'red'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.tiff({
|
||||||
|
compression: 'lzw'
|
||||||
|
})
|
||||||
|
.toFile(fixtures.outputTiff)
|
||||||
|
.then(info => {
|
||||||
|
assert.strictEqual(4, info.channels);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
it('TIFF LZW RGBA toBuffer', () =>
|
||||||
|
sharp({
|
||||||
|
create: {
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
channels: 4,
|
||||||
|
background: 'red'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.tiff({
|
||||||
|
compression: 'lzw'
|
||||||
|
})
|
||||||
|
.toBuffer({ resolveWithObject: true })
|
||||||
|
.then(({ info }) => {
|
||||||
|
assert.strictEqual(4, info.channels);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
it('TIFF ccittfax4 compression shrinks b-w test file', function (done) {
|
it('TIFF ccittfax4 compression shrinks b-w test file', function (done) {
|
||||||
const startSize = fs.statSync(fixtures.inputTiff).size;
|
const startSize = fs.statSync(fixtures.inputTiff).size;
|
||||||
sharp(fixtures.inputTiff)
|
sharp(fixtures.inputTiff)
|
||||||
|
|||||||