mirror of
https://github.com/lovell/sharp.git
synced 2026-02-04 13:46:19 +01:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16ea04fe80 | ||
|
|
9c547dc321 | ||
|
|
5522060e9e | ||
|
|
d2f0fa855b | ||
|
|
2bb3ea8170 | ||
|
|
3434eef5b9 | ||
|
|
2f67823c3d | ||
|
|
38c760cdd7 | ||
|
|
14c3346800 | ||
|
|
0da55bab7e | ||
|
|
cfb659f576 | ||
|
|
cc5ac5385f | ||
|
|
93fafb0c18 | ||
|
|
41e3c8ca09 | ||
|
|
da61ea0199 | ||
|
|
7e6a70af44 | ||
|
|
f5845c7e61 | ||
|
|
eb1e53db83 | ||
|
|
3340120aea | ||
|
|
de0fc07092 | ||
|
|
dc4b39f73f | ||
|
|
e873978e53 | ||
|
|
5255964c79 |
1
.github/workflows/ci.yml
vendored
1
.github/workflows/ci.yml
vendored
@@ -66,6 +66,7 @@ jobs:
|
||||
if: contains(matrix.container, 'centos')
|
||||
run: |
|
||||
curl -sL https://rpm.nodesource.com/setup_${{ matrix.nodejs_version }}.x | bash -
|
||||
yum install -y https://packages.endpointdev.com/rhel/7/os/x86_64/endpoint-repo.x86_64.rpm
|
||||
yum install -y centos-release-scl
|
||||
yum install -y devtoolset-11-gcc-c++ make git python3 nodejs fontconfig google-noto-sans-fonts
|
||||
echo "/opt/rh/devtoolset-11/root/usr/bin" >> $GITHUB_PATH
|
||||
|
||||
@@ -70,7 +70,9 @@
|
||||
}, {
|
||||
'target_name': 'sharp-<(platform_and_arch)',
|
||||
'defines': [
|
||||
'NAPI_VERSION=7'
|
||||
'NAPI_VERSION=7',
|
||||
'NODE_ADDON_API_DISABLE_DEPRECATED',
|
||||
'NODE_API_SWALLOW_UNTHROWABLE_EXCEPTIONS'
|
||||
],
|
||||
'dependencies': [
|
||||
'<!(node -p "require(\'node-addon-api\').gyp")',
|
||||
|
||||
@@ -4,7 +4,7 @@ Composite image(s) over the processed (resized, extracted etc.) image.
|
||||
The images to composite must be the same size or smaller than the processed image.
|
||||
If both `top` and `left` options are provided, they take precedence over `gravity`.
|
||||
|
||||
Any resize or rotate operations in the same processing pipeline
|
||||
Any resize, rotate or extract operations in the same processing pipeline
|
||||
will always be applied to the input image before composition.
|
||||
|
||||
The `blend` option can be one of `clear`, `source`, `over`, `in`, `out`, `atop`,
|
||||
|
||||
@@ -57,9 +57,13 @@ const resizeThenRotate = await sharp(input)
|
||||
|
||||
|
||||
## flip
|
||||
Flip the image about the vertical Y axis. This always occurs before rotation, if any.
|
||||
Mirror the image vertically (up-down) about the x-axis.
|
||||
This always occurs before rotation, if any.
|
||||
|
||||
The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
|
||||
|
||||
This operation does not work correctly with multi-page images.
|
||||
|
||||
|
||||
|
||||
| Param | Type | Default |
|
||||
@@ -73,7 +77,9 @@ const output = await sharp(input).flip().toBuffer();
|
||||
|
||||
|
||||
## flop
|
||||
Flop the image about the horizontal X axis. This always occurs before rotation, if any.
|
||||
Mirror the image horizontally (left-right) about the y-axis.
|
||||
This always occurs before rotation, if any.
|
||||
|
||||
The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
|
||||
|
||||
|
||||
|
||||
@@ -374,9 +374,9 @@ await sharp('in.gif', { animated: true })
|
||||
.gif({ interFrameMaxError: 8 })
|
||||
.toFile('optim.gif');
|
||||
```
|
||||
<a name="jp2"></a>
|
||||
|
||||
## jp
|
||||
|
||||
## jp2
|
||||
Use these JP2 options for output image.
|
||||
|
||||
Requires libvips compiled with support for OpenJPEG.
|
||||
|
||||
@@ -29,8 +29,8 @@ const jsdoc2md = require('jsdoc-to-markdown');
|
||||
});
|
||||
|
||||
const cleanMarkdown = markdown
|
||||
.replace(/(## [A-Za-z]+)[^\n]*/g, '$1') // simplify headings to match those of documentationjs, ensures existing URLs work
|
||||
.replace(/<a name="[A-Za-z+]+"><\/a>/g, '') // remove anchors, let docute add these (at markdown to HTML render time)
|
||||
.replace(/(## [A-Za-z0-9]+)[^\n]*/g, '$1') // simplify headings to match those of documentationjs, ensures existing URLs work
|
||||
.replace(/<a name="[A-Za-z0-9+]+"><\/a>/g, '') // remove anchors, let docute add these (at markdown to HTML render time)
|
||||
.replace(/\*\*Kind\*\*: global[^\n]+/g, '') // remove all "global" Kind labels (requires JSDoc refactoring)
|
||||
.trim();
|
||||
|
||||
|
||||
@@ -4,6 +4,29 @@
|
||||
|
||||
Requires libvips v8.14.2
|
||||
|
||||
### v0.32.2 - 11th July 2023
|
||||
|
||||
* Limit HEIF output dimensions to 16384x16384, matches libvips.
|
||||
|
||||
* Ensure exceptions are not thrown when terminating.
|
||||
[#3569](https://github.com/lovell/sharp/issues/3569)
|
||||
|
||||
* Ensure the same access method is used for all inputs (regression in 0.32.0).
|
||||
[#3669](https://github.com/lovell/sharp/issues/3669)
|
||||
|
||||
* Improve detection of jp2 filename extensions.
|
||||
[#3674](https://github.com/lovell/sharp/pull/3674)
|
||||
[@bianjunjie1981](https://github.com/bianjunjie1981)
|
||||
|
||||
* Guard use of smartcrop premultiplied option to prevent warning (regression in 0.32.1).
|
||||
[#3710](https://github.com/lovell/sharp/issues/3710)
|
||||
|
||||
* Prevent over-compute in affine-based rotate before resize.
|
||||
[#3722](https://github.com/lovell/sharp/issues/3722)
|
||||
|
||||
* Allow sequential read for EXIF-based auto-orientation.
|
||||
[#3725](https://github.com/lovell/sharp/issues/3725)
|
||||
|
||||
### v0.32.1 - 27th April 2023
|
||||
|
||||
* Add experimental `unflatten` operation.
|
||||
|
||||
@@ -272,3 +272,6 @@ GitHub: https://github.com/janaz
|
||||
|
||||
Name: Lachlan Newman
|
||||
GitHub: https://github.com/LachlanNewman
|
||||
|
||||
Name: BJJ
|
||||
GitHub: https://github.com/bianjunjie1981
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -46,7 +46,7 @@ const blend = {
|
||||
* The images to composite must be the same size or smaller than the processed image.
|
||||
* If both `top` and `left` options are provided, they take precedence over `gravity`.
|
||||
*
|
||||
* Any resize or rotate operations in the same processing pipeline
|
||||
* Any resize, rotate or extract operations in the same processing pipeline
|
||||
* will always be applied to the input image before composition.
|
||||
*
|
||||
* The `blend` option can be one of `clear`, `source`, `over`, `in`, `out`, `atop`,
|
||||
|
||||
21
lib/index.d.ts
vendored
21
lib/index.d.ts
vendored
@@ -1350,9 +1350,9 @@ declare namespace sharp {
|
||||
grayscale?: boolean | undefined;
|
||||
}
|
||||
|
||||
interface OverlayOptions {
|
||||
interface OverlayOptions extends SharpOptions {
|
||||
/** Buffer containing image data, String containing the path to an image file, or Create object */
|
||||
input?: string | Buffer | { create: Create } | { text: CreateText } | undefined;
|
||||
input?: string | Buffer | { create: Create } | { text: CreateText } | { raw: CreateRaw } | undefined;
|
||||
/** how to blend this image with the image below. (optional, default `'over'`) */
|
||||
blend?: Blend | undefined;
|
||||
/** gravity at which to place the overlay. (optional, default 'centre') */
|
||||
@@ -1363,25 +1363,8 @@ declare namespace sharp {
|
||||
left?: number | undefined;
|
||||
/** set to true to repeat the overlay image across the entire image with the given gravity. (optional, default false) */
|
||||
tile?: boolean | undefined;
|
||||
/** number representing the DPI for vector overlay image. (optional, default 72) */
|
||||
density?: number | undefined;
|
||||
/** describes overlay when using raw pixel data. */
|
||||
raw?: Raw | undefined;
|
||||
/** Set to true to avoid premultipling the image below. Equivalent to the --premultiplied vips option. */
|
||||
premultiplied?: boolean | undefined;
|
||||
/** Set to true to read all frames/pages of an animated image. (optional, default false). */
|
||||
animated?: boolean | undefined;
|
||||
/**
|
||||
* When to abort processing of invalid pixel data, one of (in order of sensitivity):
|
||||
* 'none' (least), 'truncated', 'error' or 'warning' (most), highers level imply lower levels, invalid metadata will always abort. (optional, default 'warning')
|
||||
*/
|
||||
failOn?: FailOnOptions | undefined;
|
||||
/**
|
||||
* Do not process input images where the number of pixels (width x height) exceeds this limit.
|
||||
* Assumes image dimensions contained in the input metadata can be trusted.
|
||||
* An integral Number of pixels, zero or false to remove limit, true to use default limit of 268402689 (0x3FFF x 0x3FFF). (optional, default 268402689)
|
||||
*/
|
||||
limitInputPixels?: number | boolean | undefined;
|
||||
}
|
||||
|
||||
interface TileOptions {
|
||||
|
||||
@@ -80,9 +80,13 @@ function rotate (angle, options) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Flip the image about the vertical Y axis. This always occurs before rotation, if any.
|
||||
* Mirror the image vertically (up-down) about the x-axis.
|
||||
* This always occurs before rotation, if any.
|
||||
*
|
||||
* The use of `flip` implies the removal of the EXIF `Orientation` tag, if any.
|
||||
*
|
||||
* This operation does not work correctly with multi-page images.
|
||||
*
|
||||
* @example
|
||||
* const output = await sharp(input).flip().toBuffer();
|
||||
*
|
||||
@@ -95,7 +99,9 @@ function flip (flip) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Flop the image about the horizontal X axis. This always occurs before rotation, if any.
|
||||
* Mirror the image horizontally (left-right) about the y-axis.
|
||||
* This always occurs before rotation, if any.
|
||||
*
|
||||
* The use of `flop` implies the removal of the EXIF `Orientation` tag, if any.
|
||||
*
|
||||
* @example
|
||||
|
||||
@@ -29,7 +29,7 @@ const formats = new Map([
|
||||
['jxl', 'jxl']
|
||||
]);
|
||||
|
||||
const jp2Regex = /\.jp[2x]|j2[kc]$/i;
|
||||
const jp2Regex = /\.(jp[2x]|j2[kc])$/i;
|
||||
|
||||
const errJp2Save = () => new Error('JP2 output requires libvips with support for OpenJPEG');
|
||||
|
||||
@@ -75,7 +75,7 @@ function toFile (fileOut, callback) {
|
||||
err = new Error('Missing output file path');
|
||||
} else if (is.string(this.options.input.file) && path.resolve(this.options.input.file) === path.resolve(fileOut)) {
|
||||
err = new Error('Cannot use same file for input and output');
|
||||
} else if (jp2Regex.test(fileOut) && !this.constructor.format.jp2k.output.file) {
|
||||
} else if (jp2Regex.test(path.extname(fileOut)) && !this.constructor.format.jp2k.output.file) {
|
||||
err = errJp2Save();
|
||||
}
|
||||
if (err) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "sharp",
|
||||
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, GIF, AVIF and TIFF images",
|
||||
"version": "0.32.1",
|
||||
"version": "0.32.2",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://github.com/lovell/sharp",
|
||||
"contributors": [
|
||||
@@ -136,9 +136,9 @@
|
||||
"detect-libc": "^2.0.1",
|
||||
"node-addon-api": "^6.1.0",
|
||||
"prebuild-install": "^7.1.1",
|
||||
"semver": "^7.5.0",
|
||||
"semver": "^7.5.4",
|
||||
"simple-get": "^4.0.1",
|
||||
"tar-fs": "^2.1.1",
|
||||
"tar-fs": "^3.0.4",
|
||||
"tunnel-agent": "^0.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -153,7 +153,7 @@
|
||||
"mocha": "^10.2.0",
|
||||
"mock-fs": "^5.2.0",
|
||||
"nyc": "^15.1.0",
|
||||
"prebuild": "^11.0.4",
|
||||
"prebuild": "lovell/prebuild#add-nodejs-20-drop-nodejs-10-and-12",
|
||||
"semistandard": "^16.0.1",
|
||||
"tsd": "^0.28.1"
|
||||
},
|
||||
|
||||
@@ -669,6 +669,10 @@ namespace sharp {
|
||||
if (image.width() > 65535 || height > 65535) {
|
||||
throw vips::VError("Processed image is too large for the GIF format");
|
||||
}
|
||||
} else if (imageType == ImageType::HEIF) {
|
||||
if (image.width() > 16384 || height > 16384) {
|
||||
throw vips::VError("Processed image is too large for the HEIF format");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
// Rotate and flip image according to Exif orientation
|
||||
std::tie(autoRotation, autoFlip, autoFlop) = CalculateExifRotationAndFlip(sharp::ExifOrientation(image));
|
||||
image = sharp::RemoveExifOrientation(image);
|
||||
if (baton->input->access == VIPS_ACCESS_SEQUENTIAL && (autoRotation != VIPS_ANGLE_D0 || autoFlip)) {
|
||||
image = image.copy_memory();
|
||||
}
|
||||
} else {
|
||||
rotation = CalculateAngleRotation(baton->angle);
|
||||
}
|
||||
@@ -116,7 +119,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
MultiPageUnsupported(nPages, "Rotate");
|
||||
std::vector<double> background;
|
||||
std::tie(image, background) = sharp::ApplyAlpha(image, baton->rotationBackground, FALSE);
|
||||
image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background));
|
||||
image = image.rotate(baton->rotationAngle, VImage::option()->set("background", background)).copy_memory();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,11 +383,11 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
if (autoRotation != VIPS_ANGLE_D0) {
|
||||
image = image.rot(autoRotation);
|
||||
}
|
||||
// Flip (mirror about Y axis)
|
||||
// Mirror vertically (up-down) about the x-axis
|
||||
if (baton->flip || autoFlip) {
|
||||
image = image.flip(VIPS_DIRECTION_VERTICAL);
|
||||
}
|
||||
// Flop (mirror about X axis)
|
||||
// Mirror horizontally (left-right) about the y-axis
|
||||
if (baton->flop || autoFlop) {
|
||||
image = image.flip(VIPS_DIRECTION_HORIZONTAL);
|
||||
}
|
||||
@@ -399,6 +402,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
sharp::ImageType joinImageType = sharp::ImageType::UNKNOWN;
|
||||
|
||||
for (unsigned int i = 0; i < baton->joinChannelIn.size(); i++) {
|
||||
baton->joinChannelIn[i]->access = baton->input->access;
|
||||
std::tie(joinImage, joinImageType) = sharp::OpenInput(baton->joinChannelIn[i]);
|
||||
joinImage = sharp::EnsureColourspace(joinImage, baton->colourspaceInput);
|
||||
image = image.bandjoin(joinImage);
|
||||
@@ -473,7 +477,9 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
|
||||
image = image.smartcrop(baton->width, baton->height, VImage::option()
|
||||
->set("interesting", baton->position == 16 ? VIPS_INTERESTING_ENTROPY : VIPS_INTERESTING_ATTENTION)
|
||||
#if (VIPS_MAJOR_VERSION >= 8 && VIPS_MINOR_VERSION >= 15)
|
||||
->set("premultiplied", shouldPremultiplyAlpha)
|
||||
#endif
|
||||
->set("attention_x", &attention_x)
|
||||
->set("attention_y", &attention_y));
|
||||
baton->hasCropOffset = true;
|
||||
@@ -608,6 +614,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
for (Composite *composite : baton->composite) {
|
||||
VImage compositeImage;
|
||||
sharp::ImageType compositeImageType = sharp::ImageType::UNKNOWN;
|
||||
composite->input->access = baton->input->access;
|
||||
std::tie(compositeImage, compositeImageType) = sharp::OpenInput(composite->input);
|
||||
compositeImage = sharp::EnsureColourspace(compositeImage, baton->colourspaceInput);
|
||||
// Verify within current dimensions
|
||||
@@ -706,6 +713,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
if (baton->boolean != nullptr) {
|
||||
VImage booleanImage;
|
||||
sharp::ImageType booleanImageType = sharp::ImageType::UNKNOWN;
|
||||
baton->boolean->access = baton->input->access;
|
||||
std::tie(booleanImage, booleanImageType) = sharp::OpenInput(baton->boolean);
|
||||
booleanImage = sharp::EnsureColourspace(booleanImage, baton->colourspaceInput);
|
||||
image = sharp::Boolean(image, booleanImage, baton->booleanOp);
|
||||
@@ -933,6 +941,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
} else if (baton->formatOut == "heif" ||
|
||||
(baton->formatOut == "input" && inputImageType == sharp::ImageType::HEIF)) {
|
||||
// Write HEIF to buffer
|
||||
sharp::AssertImageTypeDimensions(image, sharp::ImageType::HEIF);
|
||||
image = sharp::RemoveAnimationProperties(image).cast(VIPS_FORMAT_UCHAR);
|
||||
VipsArea *area = reinterpret_cast<VipsArea*>(image.heifsave_buffer(VImage::option()
|
||||
->set("strip", !baton->withMetadata)
|
||||
@@ -1122,6 +1131,7 @@ class PipelineWorker : public Napi::AsyncWorker {
|
||||
} else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) ||
|
||||
(willMatchInput && inputImageType == sharp::ImageType::HEIF)) {
|
||||
// Write HEIF to file
|
||||
sharp::AssertImageTypeDimensions(image, sharp::ImageType::HEIF);
|
||||
image = sharp::RemoveAnimationProperties(image).cast(VIPS_FORMAT_UCHAR);
|
||||
image.heifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
||||
->set("strip", !baton->withMetadata)
|
||||
@@ -1673,7 +1683,6 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
|
||||
baton->angle != 0 ||
|
||||
baton->rotationAngle != 0.0 ||
|
||||
baton->tileAngle != 0 ||
|
||||
baton->useExifOrientation ||
|
||||
baton->flip ||
|
||||
baton->claheWidth != 0 ||
|
||||
!baton->affineMatrix.empty()
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
"benchmark": "2.1.4",
|
||||
"gm": "1.25.0",
|
||||
"imagemagick": "0.1.3",
|
||||
"jimp": "0.22.7",
|
||||
"semver": "7.3.8"
|
||||
"jimp": "0.22.7"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tensorflow/tfjs-node": "4.2.0",
|
||||
|
||||
@@ -637,3 +637,17 @@ sharp('input.png').composite([
|
||||
sharp('input.png').tile({
|
||||
basename: 'output.dz.tiles',
|
||||
});
|
||||
|
||||
// https://github.com/lovell/sharp/issues/3669
|
||||
sharp(input).composite([
|
||||
{
|
||||
raw: {
|
||||
width: 1,
|
||||
height: 1,
|
||||
channels: 1,
|
||||
premultiplied: false,
|
||||
},
|
||||
sequentialRead: false,
|
||||
unlimited: true,
|
||||
}
|
||||
]);
|
||||
|
||||
@@ -130,4 +130,18 @@ describe('AVIF', () => {
|
||||
width: 32
|
||||
});
|
||||
});
|
||||
|
||||
it('Invalid width - too large', async () =>
|
||||
assert.rejects(
|
||||
() => sharp({ create: { width: 16385, height: 16, channels: 3, background: 'red' } }).avif().toBuffer(),
|
||||
/Processed image is too large for the HEIF format/
|
||||
)
|
||||
);
|
||||
|
||||
it('Invalid height - too large', async () =>
|
||||
assert.rejects(
|
||||
() => sharp({ create: { width: 16, height: 16385, channels: 3, background: 'red' } }).avif().toBuffer(),
|
||||
/Processed image is too large for the HEIF format/
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
@@ -25,6 +25,13 @@ describe('JP2 output', () => {
|
||||
/JP2 output requires libvips with support for OpenJPEG/
|
||||
)
|
||||
);
|
||||
|
||||
it('File with JP2-like suffix should not fail due to missing OpenJPEG', () => {
|
||||
const output = fixtures.path('output.failj2c');
|
||||
return assert.doesNotReject(
|
||||
async () => sharp(fixtures.inputPngWithOneColor).toFile(output)
|
||||
);
|
||||
});
|
||||
} else {
|
||||
it('JP2 Buffer to PNG Buffer', () => {
|
||||
sharp(fs.readFileSync(fixtures.inputJp2))
|
||||
|
||||
@@ -473,4 +473,20 @@ describe('Rotation', function () {
|
||||
assert.strictEqual(g, 64);
|
||||
assert.strictEqual(b, 30);
|
||||
});
|
||||
|
||||
it('Resize after affine-based rotation does not overcompute', async () =>
|
||||
sharp({
|
||||
create: {
|
||||
width: 4640,
|
||||
height: 2610,
|
||||
channels: 3,
|
||||
background: 'black'
|
||||
}
|
||||
})
|
||||
.rotate(28)
|
||||
.resize({ width: 640, height: 360 })
|
||||
.raw()
|
||||
.timeout({ seconds: 5 })
|
||||
.toBuffer()
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user