Compare commits

..

11 Commits

Author SHA1 Message Date
Lovell Fuller
b87a20b881 Release v0.19.1 2018-02-24 10:02:12 +00:00
Lovell Fuller
0e6f2d16ab Bump dependencies 2018-02-23 19:27:34 +00:00
Lovell Fuller
498b061819 Upgrade nan dependency, enables async hooks 2018-02-23 19:18:31 +00:00
Lovell Fuller
5ab6f599fb Prevent crop when at or below target dimensions #1134 2018-02-23 10:31:11 +00:00
Lovell Fuller
8b80626035 Changelog, credit and doc refresh for #1121 2018-02-18 19:14:37 +00:00
Andrea Bianco
f86ae79fdb Expose angle option in tile feature (#1121) 2018-02-18 20:00:08 +01:00
Lovell Fuller
1a4e68096f Changelog and credit for #1024, bump dependencies 2018-02-04 13:30:47 +00:00
Marcel
d599d1f29e Expose linear transform feature of libvips (#1024) 2018-02-04 10:36:04 +00:00
Lovell Fuller
73edfb3d2c Update list of files that npm can ignore 2018-01-13 10:28:31 +00:00
Lovell Fuller
ebae68d579 Doc refresh for #1077 2018-01-13 10:27:43 +00:00
Tony Mobily
573836e2b8 Add mention of max() to preserve ratio in withoutEnlargement (#1077) 2018-01-13 09:59:59 +00:00
27 changed files with 329 additions and 39 deletions

View File

@@ -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

View File

@@ -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>`.

View File

@@ -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**

View File

@@ -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'`)

View File

@@ -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.

View File

@@ -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.

View File

@@ -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!

View File

@@ -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

View File

@@ -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;
});

View File

@@ -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');
}

View File

@@ -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.
*

View File

@@ -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",

View File

@@ -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:

View File

@@ -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

View File

@@ -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_

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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:

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -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
View 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' });
});
});
});

View File

@@ -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);
});