mirror of
https://github.com/lovell/sharp.git
synced 2025-07-11 03:20:13 +02:00
Add skipBlanks support for tile layout (#1687)
This commit is contained in:
parent
b737d4601e
commit
6c02949fc1
@ -311,6 +311,7 @@ Warning: multiple sharp instances concurrently producing tile output can expose
|
||||
- `tile.overlap` **[Number][8]** tile overlap in pixels, a value between 0 and 8192. (optional, default `0`)
|
||||
- `tile.angle` **[Number][8]** tile angle of rotation, must be a multiple of 90. (optional, default `0`)
|
||||
- `tile.depth` **[String][1]?** how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
|
||||
- `tile.skipBlanks` **[Number][8]** threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images (optional, default `-1`)
|
||||
- `tile.container` **[String][1]** tile container, with value `fs` (filesystem) or `zip` (compressed file). (optional, default `'fs'`)
|
||||
- `tile.layout` **[String][1]** filesystem layout, possible values are `dz`, `zoomify` or `google`. (optional, default `'dz'`)
|
||||
|
||||
|
@ -539,6 +539,7 @@ function toFormat (format, options) {
|
||||
* @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.depth] how deep to make the pyramid, possible values are `onepixel`, `onetile` or `one`, default based on layout.
|
||||
* @param {Number} [tile.skipBlanks=-1] threshold to skip tile generation, a value 0 - 255 for 8-bit images or 0 - 65535 for 16-bit images
|
||||
* @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}
|
||||
@ -599,6 +600,21 @@ function tile (tile) {
|
||||
throw new Error("Invalid tile depth '" + tile.depth + "', should be one of 'onepixel', 'onetile' or 'one'");
|
||||
}
|
||||
}
|
||||
|
||||
// Threshold of skipping blanks,
|
||||
if (is.defined(tile.skipBlanks)) {
|
||||
if (is.integer(tile.skipBlanks) && is.inRange(tile.skipBlanks, -1, 65535)) {
|
||||
this.options.skipBlanks = tile.skipBlanks;
|
||||
} else {
|
||||
throw new Error('Invalid skipBlank threshold (-1 to 255/65535) ' + tile.skipBlanks);
|
||||
}
|
||||
} else {
|
||||
if (is.defined(tile.layout) && tile.layout === 'google') {
|
||||
this.options.skipBlanks = 5;
|
||||
} else {
|
||||
this.options.skipBlanks = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Format
|
||||
if (is.inArray(this.options.formatOut, ['jpeg', 'png', 'webp'])) {
|
||||
|
@ -59,7 +59,8 @@
|
||||
"Daiz <taneli.vatanen@gmail.com>",
|
||||
"Julian Aubourg <j@ubourg.net>",
|
||||
"Keith Belovay <keith@picthrive.com>",
|
||||
"Michael B. Klein <mbklein@gmail.com>"
|
||||
"Michael B. Klein <mbklein@gmail.com>",
|
||||
"Jordan Prudhomme <jordan@raboland.fr>"
|
||||
],
|
||||
"scripts": {
|
||||
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
|
||||
|
@ -965,7 +965,8 @@ class PipelineWorker : public Nan::AsyncWorker {
|
||||
->set("container", baton->tileContainer)
|
||||
->set("layout", baton->tileLayout)
|
||||
->set("suffix", const_cast<char*>(suffix.data()))
|
||||
->set("angle", CalculateAngleRotation(baton->tileAngle));
|
||||
->set("angle", CalculateAngleRotation(baton->tileAngle))
|
||||
->set("skip-blanks", baton->skipBlanks);
|
||||
|
||||
// libvips chooses a default depth based on layout. Instead of replicating that logic here by
|
||||
// not passing anything - libvips will handle choice
|
||||
@ -1371,6 +1372,7 @@ NAN_METHOD(pipeline) {
|
||||
baton->tileOverlap = AttrTo<uint32_t>(options, "tileOverlap");
|
||||
std::string tileContainer = AttrAsStr(options, "tileContainer");
|
||||
baton->tileAngle = AttrTo<int32_t>(options, "tileAngle");
|
||||
baton->skipBlanks = AttrTo<int32_t>(options, "skipBlanks");
|
||||
if (tileContainer == "zip") {
|
||||
baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
|
||||
} else {
|
||||
|
@ -172,6 +172,7 @@ struct PipelineBaton {
|
||||
VipsForeignDzLayout tileLayout;
|
||||
std::string tileFormat;
|
||||
int tileAngle;
|
||||
int skipBlanks;
|
||||
VipsForeignDzDepth tileDepth;
|
||||
std::unique_ptr<double[]> recombMatrix;
|
||||
|
||||
@ -271,6 +272,7 @@ struct PipelineBaton {
|
||||
tileContainer(VIPS_FOREIGN_DZ_CONTAINER_FS),
|
||||
tileLayout(VIPS_FOREIGN_DZ_LAYOUT_DZ),
|
||||
tileAngle(0),
|
||||
skipBlanks(-1),
|
||||
tileDepth(VIPS_FOREIGN_DZ_DEPTH_LAST) {}
|
||||
};
|
||||
|
||||
|
@ -246,6 +246,26 @@ describe('Tile', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Valid skipBlanks threshold values pass', function () {
|
||||
[-1, 0, 255, 65535].forEach(function (skipBlanksThreshold) {
|
||||
assert.doesNotThrow(function () {
|
||||
sharp().tile({
|
||||
skipBlanks: skipBlanksThreshold
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('InvalidskipBlanks threshold values fail', function () {
|
||||
['zoinks', -2, 65536].forEach(function (skipBlanksThreshold) {
|
||||
assert.throws(function () {
|
||||
sharp().tile({
|
||||
skipBlanks: skipBlanksThreshold
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Deep Zoom layout', function (done) {
|
||||
const directory = fixtures.path('output.dzi_files');
|
||||
rimraf(directory, function () {
|
||||
@ -364,6 +384,25 @@ describe('Tile', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Deep Zoom layout with skipBlanks', function (done) {
|
||||
const directory = fixtures.path('output.256_skip_blanks.dzi_files');
|
||||
rimraf(directory, function () {
|
||||
sharp(fixtures.inputJpgOverlayLayer2)
|
||||
.tile({
|
||||
size: 256,
|
||||
skipBlanks: 0
|
||||
})
|
||||
.toFile(fixtures.path('output.256_skip_blanks.dzi'), function (err, info) {
|
||||
if (err) throw err;
|
||||
// assert them 0_0.jpeg doesn't exist because it's a white tile
|
||||
const whiteTilePath = path.join(directory, '11', '0_0.jpeg');
|
||||
assert.strictEqual(fs.existsSync(whiteTilePath), false, `Tile shouldn't exist`);
|
||||
// Verify only one depth generated
|
||||
assertDeepZoomTiles(directory, 256, 12, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Zoomify layout', function (done) {
|
||||
const directory = fixtures.path('output.zoomify.dzi');
|
||||
rimraf(directory, function () {
|
||||
@ -451,6 +490,30 @@ describe('Tile', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Zoomify layout with skip blanks', function (done) {
|
||||
const directory = fixtures.path('output.zoomify.skipBlanks.dzi');
|
||||
rimraf(directory, function () {
|
||||
sharp(fixtures.inputJpgOverlayLayer2)
|
||||
.tile({
|
||||
size: 256,
|
||||
layout: 'zoomify',
|
||||
skipBlanks: 0
|
||||
})
|
||||
.toFile(directory, function (err, info) {
|
||||
if (err) throw err;
|
||||
// assert them 0_0.jpeg doesn't exist because it's a white tile
|
||||
const whiteTilePath = path.join(directory, 'TileGroup0', '2-0-0.jpg');
|
||||
assert.strictEqual(fs.existsSync(whiteTilePath), false, `Tile shouldn't exist`);
|
||||
assert.strictEqual('dz', info.format);
|
||||
assert.strictEqual(2048, info.width);
|
||||
assert.strictEqual(1536, info.height);
|
||||
assert.strictEqual(3, info.channels);
|
||||
assert.strictEqual('number', typeof info.size);
|
||||
assertZoomifyTiles(directory, 256, 4, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Google layout', function (done) {
|
||||
const directory = fixtures.path('output.google.dzi');
|
||||
rimraf(directory, function () {
|
||||
@ -652,6 +715,31 @@ describe('Tile', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Google layout with default skip Blanks', function (done) {
|
||||
const directory = fixtures.path('output.google_depth_skipBlanks.dzi');
|
||||
rimraf(directory, function () {
|
||||
sharp(fixtures.inputPng)
|
||||
.tile({
|
||||
layout: 'google',
|
||||
size: 256
|
||||
})
|
||||
.toFile(directory, function (err, info) {
|
||||
if (err) throw err;
|
||||
|
||||
const whiteTilePath = path.join(directory, '4', '8', '0.jpg');
|
||||
assert.strictEqual(fs.existsSync(whiteTilePath), false, `Tile shouldn't exist`);
|
||||
|
||||
assert.strictEqual('dz', info.format);
|
||||
assert.strictEqual(2809, info.width);
|
||||
assert.strictEqual(2074, info.height);
|
||||
assert.strictEqual(3, info.channels);
|
||||
assert.strictEqual('number', typeof info.size);
|
||||
|
||||
assertGoogleTiles(directory, 256, 5, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Write to ZIP container using file extension', function (done) {
|
||||
const container = fixtures.path('output.dz.container.zip');
|
||||
const extractTo = fixtures.path('output.dz.container');
|
||||
|
Loading…
x
Reference in New Issue
Block a user