mirror of
https://github.com/lovell/sharp.git
synced 2025-07-12 03:50:13 +02:00
Expose angle option in tile feature (#1121)
This commit is contained in:
parent
1a4e68096f
commit
f86ae79fdb
@ -319,6 +319,7 @@ function toFormat (format, options) {
|
|||||||
* @param {Object} [tile]
|
* @param {Object} [tile]
|
||||||
* @param {Number} [tile.size=256] tile size in pixels, a value between 1 and 8192.
|
* @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.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.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`.
|
* @param {String} [tile.layout='dz'] filesystem layout, possible values are `dz`, `zoomify` or `google`.
|
||||||
* @returns {Sharp}
|
* @returns {Sharp}
|
||||||
@ -361,6 +362,15 @@ function tile (tile) {
|
|||||||
throw new Error('Invalid tile layout ' + tile.layout);
|
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
|
// Format
|
||||||
if (is.inArray(this.options.formatOut, ['jpeg', 'png', 'webp'])) {
|
if (is.inArray(this.options.formatOut, ['jpeg', 'png', 'webp'])) {
|
||||||
@ -368,6 +378,7 @@ function tile (tile) {
|
|||||||
} else if (this.options.formatOut !== 'input') {
|
} else if (this.options.formatOut !== 'input') {
|
||||||
throw new Error('Invalid tile format ' + this.options.formatOut);
|
throw new Error('Invalid tile format ' + this.options.formatOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._updateFormatOut('dz');
|
return this._updateFormatOut('dz');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -917,7 +917,8 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
->set("overlap", baton->tileOverlap)
|
->set("overlap", baton->tileOverlap)
|
||||||
->set("container", baton->tileContainer)
|
->set("container", baton->tileContainer)
|
||||||
->set("layout", baton->tileLayout)
|
->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";
|
baton->formatOut = "dz";
|
||||||
} else if (baton->formatOut == "v" || (mightMatchInput && isV) ||
|
} else if (baton->formatOut == "v" || (mightMatchInput && isV) ||
|
||||||
(willMatchInput && inputImageType == ImageType::VIPS)) {
|
(willMatchInput && inputImageType == ImageType::VIPS)) {
|
||||||
@ -1263,6 +1264,7 @@ NAN_METHOD(pipeline) {
|
|||||||
baton->tileSize = AttrTo<uint32_t>(options, "tileSize");
|
baton->tileSize = AttrTo<uint32_t>(options, "tileSize");
|
||||||
baton->tileOverlap = AttrTo<uint32_t>(options, "tileOverlap");
|
baton->tileOverlap = AttrTo<uint32_t>(options, "tileOverlap");
|
||||||
std::string tileContainer = AttrAsStr(options, "tileContainer");
|
std::string tileContainer = AttrAsStr(options, "tileContainer");
|
||||||
|
baton->tileAngle = AttrTo<int32_t>(options, "tileAngle");
|
||||||
if (tileContainer == "zip") {
|
if (tileContainer == "zip") {
|
||||||
baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
|
baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
|
||||||
} else {
|
} else {
|
||||||
|
@ -132,6 +132,7 @@ struct PipelineBaton {
|
|||||||
VipsForeignDzContainer tileContainer;
|
VipsForeignDzContainer tileContainer;
|
||||||
VipsForeignDzLayout tileLayout;
|
VipsForeignDzLayout tileLayout;
|
||||||
std::string tileFormat;
|
std::string tileFormat;
|
||||||
|
int tileAngle;
|
||||||
|
|
||||||
PipelineBaton():
|
PipelineBaton():
|
||||||
input(nullptr),
|
input(nullptr),
|
||||||
@ -206,7 +207,8 @@ struct PipelineBaton {
|
|||||||
tileSize(256),
|
tileSize(256),
|
||||||
tileOverlap(0),
|
tileOverlap(0),
|
||||||
tileContainer(VIPS_FOREIGN_DZ_CONTAINER_FS),
|
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[0] = 0.0;
|
||||||
background[1] = 0.0;
|
background[1] = 0.0;
|
||||||
background[2] = 0.0;
|
background[2] = 0.0;
|
||||||
|
@ -146,13 +146,38 @@ describe('Tile', function () {
|
|||||||
|
|
||||||
it('Prevent larger overlap than default size', function () {
|
it('Prevent larger overlap than default size', function () {
|
||||||
assert.throws(function () {
|
assert.throws(function () {
|
||||||
sharp().tile({overlap: 257});
|
sharp().tile({
|
||||||
|
overlap: 257
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Prevent larger overlap than provided size', function () {
|
it('Prevent larger overlap than provided size', function () {
|
||||||
assert.throws(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) {
|
it('Zoomify layout', function (done) {
|
||||||
const directory = fixtures.path('output.zoomify.dzi');
|
const directory = fixtures.path('output.zoomify.dzi');
|
||||||
rimraf(directory, function () {
|
rimraf(directory, function () {
|
||||||
@ -244,7 +303,9 @@ describe('Tile', function () {
|
|||||||
const directory = fixtures.path('output.jpg.google.dzi');
|
const directory = fixtures.path('output.jpg.google.dzi');
|
||||||
rimraf(directory, function () {
|
rimraf(directory, function () {
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
.jpeg({ quality: 1 })
|
.jpeg({
|
||||||
|
quality: 1
|
||||||
|
})
|
||||||
.tile({
|
.tile({
|
||||||
layout: 'google'
|
layout: 'google'
|
||||||
})
|
})
|
||||||
@ -279,7 +340,9 @@ describe('Tile', function () {
|
|||||||
const directory = fixtures.path('output.png.google.dzi');
|
const directory = fixtures.path('output.png.google.dzi');
|
||||||
rimraf(directory, function () {
|
rimraf(directory, function () {
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
.png({ compressionLevel: 1 })
|
.png({
|
||||||
|
compressionLevel: 1
|
||||||
|
})
|
||||||
.tile({
|
.tile({
|
||||||
layout: 'google'
|
layout: 'google'
|
||||||
})
|
})
|
||||||
@ -314,7 +377,9 @@ describe('Tile', function () {
|
|||||||
const directory = fixtures.path('output.webp.google.dzi');
|
const directory = fixtures.path('output.webp.google.dzi');
|
||||||
rimraf(directory, function () {
|
rimraf(directory, function () {
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
.webp({ quality: 1 })
|
.webp({
|
||||||
|
quality: 1
|
||||||
|
})
|
||||||
.tile({
|
.tile({
|
||||||
layout: 'google'
|
layout: 'google'
|
||||||
})
|
})
|
||||||
@ -363,8 +428,12 @@ describe('Tile', function () {
|
|||||||
assert.strictEqual(true, stat.isFile());
|
assert.strictEqual(true, stat.isFile());
|
||||||
assert.strictEqual(true, stat.size > 0);
|
assert.strictEqual(true, stat.size > 0);
|
||||||
fs.createReadStream(container)
|
fs.createReadStream(container)
|
||||||
.pipe(unzip.Extract({path: path.dirname(extractTo)}))
|
.pipe(unzip.Extract({
|
||||||
.on('error', function (err) { throw err; })
|
path: path.dirname(extractTo)
|
||||||
|
}))
|
||||||
|
.on('error', function (err) {
|
||||||
|
throw err;
|
||||||
|
})
|
||||||
.on('close', function () {
|
.on('close', function () {
|
||||||
assertDeepZoomTiles(directory, 256, 13, done);
|
assertDeepZoomTiles(directory, 256, 13, done);
|
||||||
});
|
});
|
||||||
@ -395,8 +464,12 @@ describe('Tile', function () {
|
|||||||
assert.strictEqual(true, stat.isFile());
|
assert.strictEqual(true, stat.isFile());
|
||||||
assert.strictEqual(true, stat.size > 0);
|
assert.strictEqual(true, stat.size > 0);
|
||||||
fs.createReadStream(container)
|
fs.createReadStream(container)
|
||||||
.pipe(unzip.Extract({path: path.dirname(extractTo)}))
|
.pipe(unzip.Extract({
|
||||||
.on('error', function (err) { throw err; })
|
path: path.dirname(extractTo)
|
||||||
|
}))
|
||||||
|
.on('error', function (err) {
|
||||||
|
throw err;
|
||||||
|
})
|
||||||
.on('close', function () {
|
.on('close', function () {
|
||||||
assertDeepZoomTiles(directory, 256, 13, done);
|
assertDeepZoomTiles(directory, 256, 13, done);
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user