mirror of
https://github.com/lovell/sharp.git
synced 2026-02-07 15:16:17 +01:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b87a20b881 | ||
|
|
0e6f2d16ab | ||
|
|
498b061819 | ||
|
|
5ab6f599fb | ||
|
|
8b80626035 | ||
|
|
f86ae79fdb | ||
|
|
1a4e68096f | ||
|
|
d599d1f29e | ||
|
|
73edfb3d2c | ||
|
|
ebae68d579 | ||
|
|
573836e2b8 |
@@ -2,13 +2,12 @@ build
|
||||
node_modules
|
||||
coverage
|
||||
.editorconfig
|
||||
.gitattributes
|
||||
.gitignore
|
||||
test
|
||||
.travis.yml
|
||||
appveyor.yml
|
||||
circle.yml
|
||||
mkdocs.yml
|
||||
vendor
|
||||
packaging
|
||||
preinstall.sh
|
||||
.nyc_output
|
||||
CONTRIBUTING.md
|
||||
|
||||
@@ -41,8 +41,9 @@ Any change that modifies the existing public API should be added to the relevant
|
||||
|
||||
| Release | WIP branch |
|
||||
| ------: | :--------- |
|
||||
| v0.19.0 | suit |
|
||||
| v0.20.0 | teeth |
|
||||
| v0.20.0 | prebuild |
|
||||
| v0.21.0 | teeth |
|
||||
| v0.22.0 | uptake |
|
||||
|
||||
Please squash your changes into a single commit using a command like `git rebase -i upstream/<wip-branch>`.
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
- [convolve](#convolve)
|
||||
- [threshold](#threshold)
|
||||
- [boolean](#boolean)
|
||||
- [linear](#linear)
|
||||
|
||||
## rotate
|
||||
|
||||
@@ -318,6 +319,20 @@ the selected bitwise boolean `operation` between the corresponding pixels of the
|
||||
- `options.raw.channels` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?**
|
||||
|
||||
|
||||
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
|
||||
|
||||
Returns **Sharp**
|
||||
|
||||
## linear
|
||||
|
||||
Apply the linear formula a \* input + b to the image (levels adjustment)
|
||||
|
||||
**Parameters**
|
||||
|
||||
- `a` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** multiplier (optional, default `1.0`)
|
||||
- `b` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** offset (optional, default `0.0`)
|
||||
|
||||
|
||||
- Throws **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** Invalid parameters
|
||||
|
||||
Returns **Sharp**
|
||||
|
||||
@@ -183,6 +183,7 @@ Use a `.zip` or `.szi` file extension with `toFile` to write to a compressed arc
|
||||
- `tile` **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)?**
|
||||
- `tile.size` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** tile size in pixels, a value between 1 and 8192. (optional, default `256`)
|
||||
- `tile.overlap` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** tile overlap in pixels, a value between 0 and 8192. (optional, default `0`)
|
||||
- `tile.angle` **[Number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)** tile angle of rotation, must be a multiple of 90. (optional, default `0`)
|
||||
- `tile.container` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** tile container, with value `fs` (filesystem) or `zip` (compressed file). (optional, default `'fs'`)
|
||||
- `tile.layout` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** filesystem layout, possible values are `dz`, `zoomify` or `google`. (optional, default `'dz'`)
|
||||
|
||||
|
||||
@@ -163,6 +163,7 @@ Returns **Sharp**
|
||||
Do not enlarge the output image if the input image width _or_ height are already less than the required dimensions.
|
||||
This is equivalent to GraphicsMagick's `>` geometry option:
|
||||
"_change the dimensions of the image only if its width or height exceeds the geometry specification_".
|
||||
Use with `max()` to preserve the image's aspect ratio.
|
||||
|
||||
The default behaviour _before_ function call is `false`, meaning the image will be enlarged.
|
||||
|
||||
|
||||
@@ -4,6 +4,20 @@
|
||||
|
||||
Requires libvips v8.6.1.
|
||||
|
||||
#### v0.19.1 - 24<sup>th</sup> February 2018
|
||||
|
||||
* Expose libvips' linear transform feature.
|
||||
[#1024](https://github.com/lovell/sharp/pull/1024)
|
||||
[@3epnm](https://github.com/3epnm)
|
||||
|
||||
* Expose angle option for tile-based output.
|
||||
[#1121](https://github.com/lovell/sharp/pull/1121)
|
||||
[@BiancoA](https://github.com/BiancoA)
|
||||
|
||||
* Prevent crop operation when image already at or below target dimensions.
|
||||
[#1134](https://github.com/lovell/sharp/issues/1134)
|
||||
[@pieh](https://github.com/pieh)
|
||||
|
||||
#### v0.19.0 - 11<sup>th</sup> January 2018
|
||||
|
||||
* Expose offset coordinates of strategy-based crop.
|
||||
|
||||
@@ -105,6 +105,9 @@ the help and code contributions of the following people:
|
||||
* [Matthew McEachen](https://github.com/mceachen)
|
||||
* [Jarda Kotěšovec](https://github.com/jardakotesovec)
|
||||
* [Kenric D'Souza](https://github.com/AzureByte)
|
||||
* [Oleh Aleinyk](https://github.com/oaleynik)
|
||||
* [Marcel Bretschneider](https://github.com/3epnm)
|
||||
* [Andrea Bianco](https://github.com/BiancoA)
|
||||
|
||||
Thank you!
|
||||
|
||||
|
||||
@@ -203,6 +203,8 @@ const Sharp = function (input, options) {
|
||||
tiffYres: 1.0,
|
||||
tileSize: 256,
|
||||
tileOverlap: 0,
|
||||
linearA: 1,
|
||||
linearB: 0,
|
||||
// Function to notify of libvips warnings
|
||||
debuglog: debuglog,
|
||||
// Function to notify of queue length changes
|
||||
|
||||
@@ -406,6 +406,33 @@ function boolean (operand, operator, options) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the linear formula a * input + b to the image (levels adjustment)
|
||||
* @param {Number} [a=1.0] multiplier
|
||||
* @param {Number} [b=0.0] offset
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid parameters
|
||||
*/
|
||||
function linear (a, b) {
|
||||
if (!is.defined(a)) {
|
||||
this.options.linearA = 1.0;
|
||||
} else if (is.number(a)) {
|
||||
this.options.linearA = a;
|
||||
} else {
|
||||
throw new Error('Invalid linear transform multiplier ' + a);
|
||||
}
|
||||
|
||||
if (!is.defined(b)) {
|
||||
this.options.linearB = 0.0;
|
||||
} else if (is.number(b)) {
|
||||
this.options.linearB = b;
|
||||
} else {
|
||||
throw new Error('Invalid linear transform offset ' + b);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorate the Sharp prototype with operation-related functions.
|
||||
* @private
|
||||
@@ -427,7 +454,8 @@ module.exports = function (Sharp) {
|
||||
normalize,
|
||||
convolve,
|
||||
threshold,
|
||||
boolean
|
||||
boolean,
|
||||
linear
|
||||
].forEach(function (f) {
|
||||
Sharp.prototype[f.name] = f;
|
||||
});
|
||||
|
||||
@@ -319,6 +319,7 @@ function toFormat (format, options) {
|
||||
* @param {Object} [tile]
|
||||
* @param {Number} [tile.size=256] tile size in pixels, a value between 1 and 8192.
|
||||
* @param {Number} [tile.overlap=0] tile overlap in pixels, a value between 0 and 8192.
|
||||
* @param {Number} [tile.angle=0] tile angle of rotation, must be a multiple of 90.
|
||||
* @param {String} [tile.container='fs'] tile container, with value `fs` (filesystem) or `zip` (compressed file).
|
||||
* @param {String} [tile.layout='dz'] filesystem layout, possible values are `dz`, `zoomify` or `google`.
|
||||
* @returns {Sharp}
|
||||
@@ -361,6 +362,15 @@ function tile (tile) {
|
||||
throw new Error('Invalid tile layout ' + tile.layout);
|
||||
}
|
||||
}
|
||||
|
||||
// Angle of rotation,
|
||||
if (is.defined(tile.angle)) {
|
||||
if (is.integer(tile.angle) && !(tile.angle % 90)) {
|
||||
this.options.tileAngle = tile.angle;
|
||||
} else {
|
||||
throw new Error('Unsupported angle: angle must be a positive/negative multiple of 90 ' + tile.angle);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Format
|
||||
if (is.inArray(this.options.formatOut, ['jpeg', 'png', 'webp'])) {
|
||||
@@ -368,6 +378,7 @@ function tile (tile) {
|
||||
} else if (this.options.formatOut !== 'input') {
|
||||
throw new Error('Invalid tile format ' + this.options.formatOut);
|
||||
}
|
||||
|
||||
return this._updateFormatOut('dz');
|
||||
}
|
||||
|
||||
|
||||
@@ -253,6 +253,7 @@ function ignoreAspectRatio () {
|
||||
* Do not enlarge the output image if the input image width *or* height are already less than the required dimensions.
|
||||
* This is equivalent to GraphicsMagick's `>` geometry option:
|
||||
* "*change the dimensions of the image only if its width or height exceeds the geometry specification*".
|
||||
* Use with `max()` to preserve the image's aspect ratio.
|
||||
*
|
||||
* The default behaviour *before* function call is `false`, meaning the image will be enlarged.
|
||||
*
|
||||
|
||||
23
package.json
23
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "sharp",
|
||||
"description": "High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images",
|
||||
"version": "0.19.0",
|
||||
"version": "0.19.1",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://github.com/lovell/sharp",
|
||||
"contributors": [
|
||||
@@ -39,10 +39,13 @@
|
||||
"Guy Maliar <guy@tailorbrands.com>",
|
||||
"Nicolas Coden <nicolas@ncoden.fr>",
|
||||
"Matt Parrish <matt.r.parrish@gmail.com>",
|
||||
"Marcel Bretschneider <marcel.bretschneider@gmail.com>",
|
||||
"Matthew McEachen <matthew+github@mceachen.org>",
|
||||
"Jarda Kotěšovec <jarda.kotesovec@gmail.com>",
|
||||
"Kenric D'Souza <kenric.dsouza@gmail.com>",
|
||||
"Oleh Aleinyk <oleg.aleynik@gmail.com>"
|
||||
"Oleh Aleinyk <oleg.aleynik@gmail.com>",
|
||||
"Marcel Bretschneider <marcel.bretschneider@gmail.com>",
|
||||
"Andrea Bianco <andrea.bianco@unibas.ch>"
|
||||
],
|
||||
"scripts": {
|
||||
"clean": "rm -rf node_modules/ build/ vendor/ coverage/ test/fixtures/output.*",
|
||||
@@ -73,24 +76,24 @@
|
||||
"vips"
|
||||
],
|
||||
"dependencies": {
|
||||
"color": "^2.0.1",
|
||||
"color": "^3.0.0",
|
||||
"detect-libc": "^1.0.3",
|
||||
"nan": "^2.8.0",
|
||||
"semver": "^5.4.1",
|
||||
"nan": "^2.9.2",
|
||||
"semver": "^5.5.0",
|
||||
"simple-get": "^2.7.0",
|
||||
"tar": "^4.2.0",
|
||||
"tar": "^4.4.0",
|
||||
"tunnel-agent": "^0.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"async": "^2.6.0",
|
||||
"cc": "^1.0.1",
|
||||
"documentation": "^5.3.5",
|
||||
"documentation": "^5.4.0",
|
||||
"exif-reader": "^1.0.2",
|
||||
"icc": "^1.0.0",
|
||||
"mocha": "^4.1.0",
|
||||
"nyc": "^11.4.1",
|
||||
"mocha": "^5.0.1",
|
||||
"nyc": "^11.5.0",
|
||||
"rimraf": "^2.6.2",
|
||||
"semistandard": "^12.0.0",
|
||||
"semistandard": "^12.0.1",
|
||||
"unzip": "^0.1.11"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -27,7 +27,8 @@ class MetadataWorker : public Nan::AsyncWorker {
|
||||
MetadataWorker(
|
||||
Nan::Callback *callback, MetadataBaton *baton, Nan::Callback *debuglog,
|
||||
std::vector<v8::Local<v8::Object>> const buffersToPersist) :
|
||||
Nan::AsyncWorker(callback), baton(baton), debuglog(debuglog),
|
||||
Nan::AsyncWorker(callback, "sharp:MetadataWorker"),
|
||||
baton(baton), debuglog(debuglog),
|
||||
buffersToPersist(buffersToPersist) {
|
||||
// Protect Buffer objects from GC, keyed on index
|
||||
std::accumulate(buffersToPersist.begin(), buffersToPersist.end(), 0,
|
||||
@@ -165,12 +166,12 @@ class MetadataWorker : public Nan::AsyncWorker {
|
||||
std::string warning = sharp::VipsWarningPop();
|
||||
while (!warning.empty()) {
|
||||
v8::Local<v8::Value> message[1] = { New(warning).ToLocalChecked() };
|
||||
debuglog->Call(1, message);
|
||||
debuglog->Call(1, message, async_resource);
|
||||
warning = sharp::VipsWarningPop();
|
||||
}
|
||||
|
||||
// Return to JavaScript
|
||||
callback->Call(2, argv);
|
||||
callback->Call(2, argv, async_resource);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -341,4 +341,18 @@ namespace sharp {
|
||||
return image.extract_area(left, top, width, height);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate (a * in + b)
|
||||
*/
|
||||
VImage Linear(VImage image, double const a, double const b) {
|
||||
if (HasAlpha(image)) {
|
||||
// Separate alpha channel
|
||||
VImage imageWithoutAlpha = image.extract_band(0,
|
||||
VImage::option()->set("n", image.bands() - 1));
|
||||
VImage alpha = image[image.bands() - 1];
|
||||
return imageWithoutAlpha.linear(a, b).bandjoin(alpha);
|
||||
} else {
|
||||
return image.linear(a, b);
|
||||
}
|
||||
}
|
||||
} // namespace sharp
|
||||
|
||||
@@ -92,6 +92,11 @@ namespace sharp {
|
||||
*/
|
||||
VImage Trim(VImage image, int const tolerance);
|
||||
|
||||
/*
|
||||
* Linear adjustment (a * in + b)
|
||||
*/
|
||||
VImage Linear(VImage image, double const a, double const b);
|
||||
|
||||
} // namespace sharp
|
||||
|
||||
#endif // SRC_OPERATIONS_H_
|
||||
|
||||
@@ -35,7 +35,8 @@ class PipelineWorker : public Nan::AsyncWorker {
|
||||
PipelineWorker(
|
||||
Nan::Callback *callback, PipelineBaton *baton, Nan::Callback *debuglog, Nan::Callback *queueListener,
|
||||
std::vector<v8::Local<v8::Object>> const buffersToPersist) :
|
||||
Nan::AsyncWorker(callback), baton(baton), debuglog(debuglog), queueListener(queueListener),
|
||||
Nan::AsyncWorker(callback, "sharp:PipelineWorker"),
|
||||
baton(baton), debuglog(debuglog), queueListener(queueListener),
|
||||
buffersToPersist(buffersToPersist) {
|
||||
// Protect Buffer objects from GC, keyed on index
|
||||
std::accumulate(buffersToPersist.begin(), buffersToPersist.end(), 0,
|
||||
@@ -465,7 +466,10 @@ class PipelineWorker : public Nan::AsyncWorker {
|
||||
->set("extend", VIPS_EXTEND_BACKGROUND)
|
||||
->set("background", background));
|
||||
|
||||
} else if (baton->canvas != Canvas::IGNORE_ASPECT) {
|
||||
} else if (
|
||||
baton->canvas != Canvas::IGNORE_ASPECT &&
|
||||
(image.width() > baton->width || image.height() > baton->height)
|
||||
) {
|
||||
// Crop/max/min
|
||||
if (baton->crop < 9) {
|
||||
// Gravity-based crop
|
||||
@@ -644,6 +648,11 @@ class PipelineWorker : public Nan::AsyncWorker {
|
||||
image = sharp::Gamma(image, baton->gamma);
|
||||
}
|
||||
|
||||
// Linear adjustment (a * in + b)
|
||||
if (baton->linearA != 1.0 || baton->linearB != 0.0) {
|
||||
image = sharp::Linear(image, baton->linearA, baton->linearB);
|
||||
}
|
||||
|
||||
// Apply normalisation - stretch luminance to cover full dynamic range
|
||||
if (baton->normalise) {
|
||||
image = sharp::Normalise(image);
|
||||
@@ -912,7 +921,8 @@ class PipelineWorker : public Nan::AsyncWorker {
|
||||
->set("overlap", baton->tileOverlap)
|
||||
->set("container", baton->tileContainer)
|
||||
->set("layout", baton->tileLayout)
|
||||
->set("suffix", const_cast<char*>(suffix.data())));
|
||||
->set("suffix", const_cast<char*>(suffix.data()))
|
||||
->set("angle", CalculateAngleRotation(baton->tileAngle)));
|
||||
baton->formatOut = "dz";
|
||||
} else if (baton->formatOut == "v" || (mightMatchInput && isV) ||
|
||||
(willMatchInput && inputImageType == ImageType::VIPS)) {
|
||||
@@ -1005,18 +1015,18 @@ class PipelineWorker : public Nan::AsyncWorker {
|
||||
std::string warning = sharp::VipsWarningPop();
|
||||
while (!warning.empty()) {
|
||||
v8::Local<v8::Value> message[1] = { New(warning).ToLocalChecked() };
|
||||
debuglog->Call(1, message);
|
||||
debuglog->Call(1, message, async_resource);
|
||||
warning = sharp::VipsWarningPop();
|
||||
}
|
||||
|
||||
// Decrement processing task counter
|
||||
g_atomic_int_dec_and_test(&sharp::counterProcess);
|
||||
v8::Local<v8::Value> queueLength[1] = { New<v8::Uint32>(sharp::counterQueue) };
|
||||
queueListener->Call(1, queueLength);
|
||||
queueListener->Call(1, queueLength, async_resource);
|
||||
delete queueListener;
|
||||
|
||||
// Return to JavaScript
|
||||
callback->Call(3, argv);
|
||||
callback->Call(3, argv, async_resource);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -1185,6 +1195,8 @@ NAN_METHOD(pipeline) {
|
||||
baton->thresholdGrayscale = AttrTo<bool>(options, "thresholdGrayscale");
|
||||
baton->trimTolerance = AttrTo<int32_t>(options, "trimTolerance");
|
||||
baton->gamma = AttrTo<double>(options, "gamma");
|
||||
baton->linearA = AttrTo<double>(options, "linearA");
|
||||
baton->linearB = AttrTo<double>(options, "linearB");
|
||||
baton->greyscale = AttrTo<bool>(options, "greyscale");
|
||||
baton->normalise = AttrTo<bool>(options, "normalise");
|
||||
baton->useExifOrientation = AttrTo<bool>(options, "useExifOrientation");
|
||||
@@ -1256,6 +1268,7 @@ NAN_METHOD(pipeline) {
|
||||
baton->tileSize = AttrTo<uint32_t>(options, "tileSize");
|
||||
baton->tileOverlap = AttrTo<uint32_t>(options, "tileOverlap");
|
||||
std::string tileContainer = AttrAsStr(options, "tileContainer");
|
||||
baton->tileAngle = AttrTo<int32_t>(options, "tileAngle");
|
||||
if (tileContainer == "zip") {
|
||||
baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
|
||||
} else {
|
||||
@@ -1290,5 +1303,6 @@ NAN_METHOD(pipeline) {
|
||||
// Increment queued task counter
|
||||
g_atomic_int_inc(&sharp::counterQueue);
|
||||
v8::Local<v8::Value> queueLength[1] = { Nan::New<v8::Uint32>(sharp::counterQueue) };
|
||||
queueListener->Call(1, queueLength);
|
||||
v8::Local<v8::Object> recv = Nan::New<v8::Object>();
|
||||
Nan::Call(*queueListener, recv, 1, queueLength);
|
||||
}
|
||||
|
||||
@@ -79,6 +79,8 @@ struct PipelineBaton {
|
||||
int threshold;
|
||||
bool thresholdGrayscale;
|
||||
int trimTolerance;
|
||||
double linearA;
|
||||
double linearB;
|
||||
double gamma;
|
||||
bool greyscale;
|
||||
bool normalise;
|
||||
@@ -130,6 +132,7 @@ struct PipelineBaton {
|
||||
VipsForeignDzContainer tileContainer;
|
||||
VipsForeignDzLayout tileLayout;
|
||||
std::string tileFormat;
|
||||
int tileAngle;
|
||||
|
||||
PipelineBaton():
|
||||
input(nullptr),
|
||||
@@ -160,6 +163,8 @@ struct PipelineBaton {
|
||||
threshold(0),
|
||||
thresholdGrayscale(true),
|
||||
trimTolerance(0),
|
||||
linearA(1.0),
|
||||
linearB(0.0),
|
||||
gamma(0.0),
|
||||
greyscale(false),
|
||||
normalise(false),
|
||||
@@ -202,7 +207,8 @@ struct PipelineBaton {
|
||||
tileSize(256),
|
||||
tileOverlap(0),
|
||||
tileContainer(VIPS_FOREIGN_DZ_CONTAINER_FS),
|
||||
tileLayout(VIPS_FOREIGN_DZ_LAYOUT_DZ) {
|
||||
tileLayout(VIPS_FOREIGN_DZ_LAYOUT_DZ),
|
||||
tileAngle(0){
|
||||
background[0] = 0.0;
|
||||
background[1] = 0.0;
|
||||
background[2] = 0.0;
|
||||
|
||||
@@ -28,7 +28,8 @@ class StatsWorker : public Nan::AsyncWorker {
|
||||
StatsWorker(
|
||||
Nan::Callback *callback, StatsBaton *baton, Nan::Callback *debuglog,
|
||||
std::vector<v8::Local<v8::Object>> const buffersToPersist) :
|
||||
Nan::AsyncWorker(callback), baton(baton), debuglog(debuglog),
|
||||
Nan::AsyncWorker(callback, "sharp:StatsWorker"),
|
||||
baton(baton), debuglog(debuglog),
|
||||
buffersToPersist(buffersToPersist) {
|
||||
// Protect Buffer objects from GC, keyed on index
|
||||
std::accumulate(buffersToPersist.begin(), buffersToPersist.end(), 0,
|
||||
@@ -145,12 +146,12 @@ class StatsWorker : public Nan::AsyncWorker {
|
||||
std::string warning = sharp::VipsWarningPop();
|
||||
while (!warning.empty()) {
|
||||
v8::Local<v8::Value> message[1] = { New(warning).ToLocalChecked() };
|
||||
debuglog->Call(1, message);
|
||||
debuglog->Call(1, message, async_resource);
|
||||
warning = sharp::VipsWarningPop();
|
||||
}
|
||||
|
||||
// Return to JavaScript
|
||||
callback->Call(2, argv);
|
||||
callback->Call(2, argv, async_resource);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
BIN
test/fixtures/expected/alpha-layer-1-fill-linear.png
vendored
Normal file
BIN
test/fixtures/expected/alpha-layer-1-fill-linear.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 154 KiB |
BIN
test/fixtures/expected/alpha-layer-1-fill-offset.png
vendored
Normal file
BIN
test/fixtures/expected/alpha-layer-1-fill-offset.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
BIN
test/fixtures/expected/alpha-layer-1-fill-slope.png
vendored
Normal file
BIN
test/fixtures/expected/alpha-layer-1-fill-slope.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 179 KiB |
BIN
test/fixtures/expected/low-contrast-linear.jpg
vendored
Normal file
BIN
test/fixtures/expected/low-contrast-linear.jpg
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
BIN
test/fixtures/expected/low-contrast-offset.jpg
vendored
Normal file
BIN
test/fixtures/expected/low-contrast-offset.jpg
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
BIN
test/fixtures/expected/low-contrast-slope.jpg
vendored
Normal file
BIN
test/fixtures/expected/low-contrast-slope.jpg
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
@@ -159,6 +159,24 @@ describe('Crop', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Skip crop when post-resize dimensions are at or below target dimensions', function () {
|
||||
return sharp(fixtures.inputJpg)
|
||||
.resize(1600, 1200)
|
||||
.toBuffer()
|
||||
.then(function (input) {
|
||||
return sharp(input)
|
||||
.resize(1110)
|
||||
.crop(sharp.strategy.attention)
|
||||
.toBuffer({ resolveWithObject: true })
|
||||
.then(function (result) {
|
||||
assert.strictEqual(1110, result.info.width);
|
||||
assert.strictEqual(832, result.info.height);
|
||||
assert.strictEqual(undefined, result.info.cropOffsetLeft);
|
||||
assert.strictEqual(undefined, result.info.cropOffsetTop);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Entropy-based strategy', function () {
|
||||
it('JPEG', function (done) {
|
||||
sharp(fixtures.inputJpg)
|
||||
|
||||
79
test/unit/linear.js
Normal file
79
test/unit/linear.js
Normal file
@@ -0,0 +1,79 @@
|
||||
'use strict';
|
||||
|
||||
const sharp = require('../../');
|
||||
const fixtures = require('../fixtures');
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
describe('Linear adjustment', function () {
|
||||
const blackPoint = 70;
|
||||
const whitePoint = 203;
|
||||
const a = 255 / (whitePoint - blackPoint);
|
||||
const b = -blackPoint * a;
|
||||
|
||||
it('applies linear levels adjustment w/o alpha ch', function (done) {
|
||||
sharp(fixtures.inputJpgWithLowContrast)
|
||||
.linear(a, b)
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
fixtures.assertSimilar(fixtures.expected('low-contrast-linear.jpg'), data, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('applies slope level adjustment w/o alpha ch', function (done) {
|
||||
sharp(fixtures.inputJpgWithLowContrast)
|
||||
.linear(a)
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
fixtures.assertSimilar(fixtures.expected('low-contrast-slope.jpg'), data, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('applies offset level adjustment w/o alpha ch', function (done) {
|
||||
sharp(fixtures.inputJpgWithLowContrast)
|
||||
.linear(null, b)
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
fixtures.assertSimilar(fixtures.expected('low-contrast-offset.jpg'), data, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('applies linear levels adjustment w alpha ch', function (done) {
|
||||
sharp(fixtures.inputPngOverlayLayer1)
|
||||
.linear(a, b)
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
fixtures.assertSimilar(fixtures.expected('alpha-layer-1-fill-linear.png'), data, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('applies slope level adjustment w alpha ch', function (done) {
|
||||
sharp(fixtures.inputPngOverlayLayer1)
|
||||
.linear(a)
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
fixtures.assertSimilar(fixtures.expected('alpha-layer-1-fill-slope.png'), data, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('applies offset level adjustment w alpha ch', function (done) {
|
||||
sharp(fixtures.inputPngOverlayLayer1)
|
||||
.linear(null, b)
|
||||
.toBuffer(function (err, data, info) {
|
||||
if (err) throw err;
|
||||
fixtures.assertSimilar(fixtures.expected('alpha-layer-1-fill-offset.png'), data, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('Invalid linear arguments', function () {
|
||||
assert.throws(function () {
|
||||
sharp(fixtures.inputPngOverlayLayer1)
|
||||
.linear('foo');
|
||||
});
|
||||
|
||||
assert.throws(function () {
|
||||
sharp(fixtures.inputPngOverlayLayer1)
|
||||
.linear(undefined, { 'bar': 'baz' });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -146,13 +146,38 @@ describe('Tile', function () {
|
||||
|
||||
it('Prevent larger overlap than default size', function () {
|
||||
assert.throws(function () {
|
||||
sharp().tile({overlap: 257});
|
||||
sharp().tile({
|
||||
overlap: 257
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Prevent larger overlap than provided size', function () {
|
||||
assert.throws(function () {
|
||||
sharp().tile({size: 512, overlap: 513});
|
||||
sharp().tile({
|
||||
size: 512,
|
||||
overlap: 513
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Valid rotation angle values pass', function () {
|
||||
[90, 270, -90].forEach(function (angle) {
|
||||
assert.doesNotThrow(function () {
|
||||
sharp().tile({
|
||||
angle: angle
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Invalid rotation angle values fail', function () {
|
||||
['zoinks', 1.1, -1, 27].forEach(function (angle) {
|
||||
assert.throws(function () {
|
||||
sharp().tile({
|
||||
angle: angle
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -192,6 +217,40 @@ describe('Tile', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Deep Zoom layout with custom size+angle', function (done) {
|
||||
const directory = fixtures.path('output.512_90.dzi_files');
|
||||
rimraf(directory, function () {
|
||||
sharp(fixtures.inputJpg)
|
||||
.tile({
|
||||
size: 512,
|
||||
angle: 90
|
||||
})
|
||||
.toFile(fixtures.path('output.512_90.dzi'), function (err, info) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('dz', info.format);
|
||||
assert.strictEqual(2725, info.width);
|
||||
assert.strictEqual(2225, info.height);
|
||||
assert.strictEqual(3, info.channels);
|
||||
assert.strictEqual('undefined', typeof info.size);
|
||||
assertDeepZoomTiles(directory, 512, 13, done);
|
||||
// Verifies tiles in 10th level are rotated
|
||||
let tile = path.join(directory, '10', '0_1.jpeg');
|
||||
// verify that the width and height correspond to the rotated image
|
||||
// expected are w=512 and h=170 for the 0_1.jpeg.
|
||||
// if a 0 angle is supplied to the .tile function
|
||||
// the expected values are w=170 and h=512 for the 1_0.jpeg
|
||||
sharp(tile).metadata(function (err, metadata) {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
assert.strictEqual(true, metadata.width === 512);
|
||||
assert.strictEqual(true, metadata.height === 170);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Zoomify layout', function (done) {
|
||||
const directory = fixtures.path('output.zoomify.dzi');
|
||||
rimraf(directory, function () {
|
||||
@@ -244,7 +303,9 @@ describe('Tile', function () {
|
||||
const directory = fixtures.path('output.jpg.google.dzi');
|
||||
rimraf(directory, function () {
|
||||
sharp(fixtures.inputJpg)
|
||||
.jpeg({ quality: 1 })
|
||||
.jpeg({
|
||||
quality: 1
|
||||
})
|
||||
.tile({
|
||||
layout: 'google'
|
||||
})
|
||||
@@ -279,7 +340,9 @@ describe('Tile', function () {
|
||||
const directory = fixtures.path('output.png.google.dzi');
|
||||
rimraf(directory, function () {
|
||||
sharp(fixtures.inputJpg)
|
||||
.png({ compressionLevel: 1 })
|
||||
.png({
|
||||
compressionLevel: 1
|
||||
})
|
||||
.tile({
|
||||
layout: 'google'
|
||||
})
|
||||
@@ -314,7 +377,9 @@ describe('Tile', function () {
|
||||
const directory = fixtures.path('output.webp.google.dzi');
|
||||
rimraf(directory, function () {
|
||||
sharp(fixtures.inputJpg)
|
||||
.webp({ quality: 1 })
|
||||
.webp({
|
||||
quality: 1
|
||||
})
|
||||
.tile({
|
||||
layout: 'google'
|
||||
})
|
||||
@@ -363,8 +428,12 @@ describe('Tile', function () {
|
||||
assert.strictEqual(true, stat.isFile());
|
||||
assert.strictEqual(true, stat.size > 0);
|
||||
fs.createReadStream(container)
|
||||
.pipe(unzip.Extract({path: path.dirname(extractTo)}))
|
||||
.on('error', function (err) { throw err; })
|
||||
.pipe(unzip.Extract({
|
||||
path: path.dirname(extractTo)
|
||||
}))
|
||||
.on('error', function (err) {
|
||||
throw err;
|
||||
})
|
||||
.on('close', function () {
|
||||
assertDeepZoomTiles(directory, 256, 13, done);
|
||||
});
|
||||
@@ -395,8 +464,12 @@ describe('Tile', function () {
|
||||
assert.strictEqual(true, stat.isFile());
|
||||
assert.strictEqual(true, stat.size > 0);
|
||||
fs.createReadStream(container)
|
||||
.pipe(unzip.Extract({path: path.dirname(extractTo)}))
|
||||
.on('error', function (err) { throw err; })
|
||||
.pipe(unzip.Extract({
|
||||
path: path.dirname(extractTo)
|
||||
}))
|
||||
.on('error', function (err) {
|
||||
throw err;
|
||||
})
|
||||
.on('close', function () {
|
||||
assertDeepZoomTiles(directory, 256, 13, done);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user