mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Expose libvips pyramid/tile options for TIFF output (#1483)
This commit is contained in:
parent
fd1ca1dbb2
commit
c695c40abc
@ -177,7 +177,11 @@ const Sharp = function (input, options) {
|
|||||||
tiffQuality: 80,
|
tiffQuality: 80,
|
||||||
tiffCompression: 'jpeg',
|
tiffCompression: 'jpeg',
|
||||||
tiffPredictor: 'horizontal',
|
tiffPredictor: 'horizontal',
|
||||||
|
tiffPyramid: false,
|
||||||
tiffSquash: false,
|
tiffSquash: false,
|
||||||
|
tiffTile: false,
|
||||||
|
tiffTileHeight: 256,
|
||||||
|
tiffTileWidth: 256,
|
||||||
tiffXres: 1.0,
|
tiffXres: 1.0,
|
||||||
tiffYres: 1.0,
|
tiffYres: 1.0,
|
||||||
tileSize: 256,
|
tileSize: 256,
|
||||||
|
116
lib/output.js
116
lib/output.js
@ -304,6 +304,10 @@ function webp (options) {
|
|||||||
* @param {Boolean} [options.force=true] - force TIFF output, otherwise attempt to use input format
|
* @param {Boolean} [options.force=true] - force TIFF output, otherwise attempt to use input format
|
||||||
* @param {Boolean} [options.compression='jpeg'] - compression options: lzw, deflate, jpeg, ccittfax4
|
* @param {Boolean} [options.compression='jpeg'] - compression options: lzw, deflate, jpeg, ccittfax4
|
||||||
* @param {Boolean} [options.predictor='horizontal'] - compression predictor options: none, horizontal, float
|
* @param {Boolean} [options.predictor='horizontal'] - compression predictor options: none, horizontal, float
|
||||||
|
* @param {Boolean} [options.pyramid=false] - write an image pyramid
|
||||||
|
* @param {Boolean} [options.tile=false] - write a tiled tiff
|
||||||
|
* @param {Boolean} [options.tileWidth=256] - horizontal tile size
|
||||||
|
* @param {Boolean} [options.tileHeight=256] - vertical tile size
|
||||||
* @param {Number} [options.xres=1.0] - horizontal resolution in pixels/mm
|
* @param {Number} [options.xres=1.0] - horizontal resolution in pixels/mm
|
||||||
* @param {Number} [options.yres=1.0] - vertical resolution in pixels/mm
|
* @param {Number} [options.yres=1.0] - vertical resolution in pixels/mm
|
||||||
* @param {Boolean} [options.squash=false] - squash 8-bit images down to 1 bit
|
* @param {Boolean} [options.squash=false] - squash 8-bit images down to 1 bit
|
||||||
@ -311,51 +315,83 @@ function webp (options) {
|
|||||||
* @throws {Error} Invalid options
|
* @throws {Error} Invalid options
|
||||||
*/
|
*/
|
||||||
function tiff (options) {
|
function tiff (options) {
|
||||||
if (is.object(options) && is.defined(options.quality)) {
|
if (is.object(options)) {
|
||||||
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
|
if (is.defined(options.quality)) {
|
||||||
this.options.tiffQuality = options.quality;
|
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
|
||||||
} else {
|
this.options.tiffQuality = options.quality;
|
||||||
throw new Error('Invalid quality (integer, 1-100) ' + options.quality);
|
} else {
|
||||||
|
throw new Error('Invalid quality (integer, 1-100) ' + options.quality);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (is.defined(options.squash)) {
|
||||||
if (is.object(options) && is.defined(options.squash)) {
|
if (is.bool(options.squash)) {
|
||||||
if (is.bool(options.squash)) {
|
this.options.tiffSquash = options.squash;
|
||||||
this.options.tiffSquash = options.squash;
|
} else {
|
||||||
} else {
|
throw new Error('Invalid Value for squash ' + options.squash + ' Only Boolean Values allowed for options.squash.');
|
||||||
throw new Error('Invalid Value for squash ' + options.squash + ' Only Boolean Values allowed for options.squash.');
|
}
|
||||||
}
|
}
|
||||||
}
|
// tiling
|
||||||
// resolution
|
if (is.defined(options.tile)) {
|
||||||
if (is.object(options) && is.defined(options.xres)) {
|
if (is.bool(options.tile)) {
|
||||||
if (is.number(options.xres)) {
|
this.options.tiffTile = options.tile;
|
||||||
this.options.tiffXres = options.xres;
|
} else {
|
||||||
} else {
|
throw new Error('Invalid Value for tile ' + options.tile + ' Only Boolean values allowed for options.tile');
|
||||||
throw new Error('Invalid Value for xres ' + options.xres + ' Only numeric values allowed for options.xres');
|
}
|
||||||
}
|
}
|
||||||
}
|
if (is.defined(options.tileWidth)) {
|
||||||
if (is.object(options) && is.defined(options.yres)) {
|
if (is.number(options.tileWidth) && options.tileWidth > 0) {
|
||||||
if (is.number(options.yres)) {
|
this.options.tiffTileWidth = options.tileWidth;
|
||||||
this.options.tiffYres = options.yres;
|
} else {
|
||||||
} else {
|
throw new Error('Invalid Value for tileWidth ' + options.tileWidth + ' Only positive numeric values allowed for options.tileWidth');
|
||||||
throw new Error('Invalid Value for yres ' + options.yres + ' Only numeric values allowed for options.yres');
|
}
|
||||||
}
|
}
|
||||||
}
|
if (is.defined(options.tileHeight)) {
|
||||||
// compression
|
if (is.number(options.tileHeight) && options.tileHeight > 0) {
|
||||||
if (is.defined(options) && is.defined(options.compression)) {
|
this.options.tiffTileHeight = options.tileHeight;
|
||||||
if (is.string(options.compression) && is.inArray(options.compression, ['lzw', 'deflate', 'jpeg', 'ccittfax4', 'none'])) {
|
} else {
|
||||||
this.options.tiffCompression = options.compression;
|
throw new Error('Invalid Value for tileHeight ' + options.tileHeight + ' Only positive numeric values allowed for options.tileHeight');
|
||||||
} else {
|
}
|
||||||
const message = `Invalid compression option "${options.compression}". Should be one of: lzw, deflate, jpeg, ccittfax4, none`;
|
|
||||||
throw new Error(message);
|
|
||||||
}
|
}
|
||||||
}
|
// pyramid
|
||||||
// predictor
|
if (is.defined(options.pyramid)) {
|
||||||
if (is.defined(options) && is.defined(options.predictor)) {
|
if (is.bool(options.pyramid)) {
|
||||||
if (is.string(options.predictor) && is.inArray(options.predictor, ['none', 'horizontal', 'float'])) {
|
this.options.tiffPyramid = options.pyramid;
|
||||||
this.options.tiffPredictor = options.predictor;
|
} else {
|
||||||
} else {
|
throw new Error('Invalid Value for pyramid ' + options.pyramid + ' Only Boolean values allowed for options.pyramid');
|
||||||
const message = `Invalid predictor option "${options.predictor}". Should be one of: none, horizontal, float`;
|
}
|
||||||
throw new Error(message);
|
}
|
||||||
|
// resolution
|
||||||
|
if (is.defined(options.xres)) {
|
||||||
|
if (is.number(options.xres)) {
|
||||||
|
this.options.tiffXres = options.xres;
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid Value for xres ' + options.xres + ' Only numeric values allowed for options.xres');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is.defined(options.yres)) {
|
||||||
|
if (is.number(options.yres)) {
|
||||||
|
this.options.tiffYres = options.yres;
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid Value for yres ' + options.yres + ' Only numeric values allowed for options.yres');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// compression
|
||||||
|
if (is.defined(options.compression)) {
|
||||||
|
if (is.string(options.compression) && is.inArray(options.compression, ['lzw', 'deflate', 'jpeg', 'ccittfax4', 'none'])) {
|
||||||
|
this.options.tiffCompression = options.compression;
|
||||||
|
} else {
|
||||||
|
const message = `Invalid compression option "${options.compression}". Should be one of: lzw, deflate, jpeg, ccittfax4, none`;
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// predictor
|
||||||
|
if (is.defined(options.predictor)) {
|
||||||
|
if (is.string(options.predictor) && is.inArray(options.predictor, ['none', 'horizontal', 'float'])) {
|
||||||
|
this.options.tiffPredictor = options.predictor;
|
||||||
|
} else {
|
||||||
|
const message = `Invalid predictor option "${options.predictor}". Should be one of: none, horizontal, float`;
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this._updateFormatOut('tiff', options);
|
return this._updateFormatOut('tiff', options);
|
||||||
|
@ -58,7 +58,8 @@
|
|||||||
"Freezy <freezy@xbmc.org>",
|
"Freezy <freezy@xbmc.org>",
|
||||||
"Daiz <taneli.vatanen@gmail.com>",
|
"Daiz <taneli.vatanen@gmail.com>",
|
||||||
"Julian Aubourg <j@ubourg.net>",
|
"Julian Aubourg <j@ubourg.net>",
|
||||||
"Keith Belovay <keith@picthrive.com>"
|
"Keith Belovay <keith@picthrive.com>",
|
||||||
|
"Michael B. Klein <mbklein@gmail.com>"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
|
"install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node-gyp rebuild && node install/dll-copy)",
|
||||||
|
@ -763,6 +763,10 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
->set("squash", baton->tiffSquash)
|
->set("squash", baton->tiffSquash)
|
||||||
->set("compression", baton->tiffCompression)
|
->set("compression", baton->tiffCompression)
|
||||||
->set("predictor", baton->tiffPredictor)
|
->set("predictor", baton->tiffPredictor)
|
||||||
|
->set("pyramid", baton->tiffPyramid)
|
||||||
|
->set("tile", baton->tiffTile)
|
||||||
|
->set("tile_height", baton->tiffTileHeight)
|
||||||
|
->set("tile_width", baton->tiffTileWidth)
|
||||||
->set("xres", baton->tiffXres)
|
->set("xres", baton->tiffXres)
|
||||||
->set("yres", baton->tiffYres)));
|
->set("yres", baton->tiffYres)));
|
||||||
baton->bufferOut = static_cast<char*>(area->data);
|
baton->bufferOut = static_cast<char*>(area->data);
|
||||||
@ -862,6 +866,10 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
->set("squash", baton->tiffSquash)
|
->set("squash", baton->tiffSquash)
|
||||||
->set("compression", baton->tiffCompression)
|
->set("compression", baton->tiffCompression)
|
||||||
->set("predictor", baton->tiffPredictor)
|
->set("predictor", baton->tiffPredictor)
|
||||||
|
->set("pyramid", baton->tiffPyramid)
|
||||||
|
->set("tile", baton->tiffTile)
|
||||||
|
->set("tile_height", baton->tiffTileHeight)
|
||||||
|
->set("tile_width", baton->tiffTileWidth)
|
||||||
->set("xres", baton->tiffXres)
|
->set("xres", baton->tiffXres)
|
||||||
->set("yres", baton->tiffYres));
|
->set("yres", baton->tiffYres));
|
||||||
baton->formatOut = "tiff";
|
baton->formatOut = "tiff";
|
||||||
@ -1272,7 +1280,11 @@ NAN_METHOD(pipeline) {
|
|||||||
baton->webpLossless = AttrTo<bool>(options, "webpLossless");
|
baton->webpLossless = AttrTo<bool>(options, "webpLossless");
|
||||||
baton->webpNearLossless = AttrTo<bool>(options, "webpNearLossless");
|
baton->webpNearLossless = AttrTo<bool>(options, "webpNearLossless");
|
||||||
baton->tiffQuality = AttrTo<uint32_t>(options, "tiffQuality");
|
baton->tiffQuality = AttrTo<uint32_t>(options, "tiffQuality");
|
||||||
|
baton->tiffPyramid = AttrTo<bool>(options, "tiffPyramid");
|
||||||
baton->tiffSquash = AttrTo<bool>(options, "tiffSquash");
|
baton->tiffSquash = AttrTo<bool>(options, "tiffSquash");
|
||||||
|
baton->tiffTile = AttrTo<bool>(options, "tiffTile");
|
||||||
|
baton->tiffTileWidth = AttrTo<uint32_t>(options, "tiffTileWidth");
|
||||||
|
baton->tiffTileHeight = AttrTo<uint32_t>(options, "tiffTileHeight");
|
||||||
baton->tiffXres = AttrTo<double>(options, "tiffXres");
|
baton->tiffXres = AttrTo<double>(options, "tiffXres");
|
||||||
baton->tiffYres = AttrTo<double>(options, "tiffYres");
|
baton->tiffYres = AttrTo<double>(options, "tiffYres");
|
||||||
// tiff compression options
|
// tiff compression options
|
||||||
|
@ -122,7 +122,11 @@ struct PipelineBaton {
|
|||||||
int tiffQuality;
|
int tiffQuality;
|
||||||
VipsForeignTiffCompression tiffCompression;
|
VipsForeignTiffCompression tiffCompression;
|
||||||
VipsForeignTiffPredictor tiffPredictor;
|
VipsForeignTiffPredictor tiffPredictor;
|
||||||
|
bool tiffPyramid;
|
||||||
bool tiffSquash;
|
bool tiffSquash;
|
||||||
|
bool tiffTile;
|
||||||
|
int tiffTileHeight;
|
||||||
|
int tiffTileWidth;
|
||||||
double tiffXres;
|
double tiffXres;
|
||||||
double tiffYres;
|
double tiffYres;
|
||||||
std::string err;
|
std::string err;
|
||||||
@ -215,7 +219,11 @@ struct PipelineBaton {
|
|||||||
tiffQuality(80),
|
tiffQuality(80),
|
||||||
tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
|
tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
|
||||||
tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL),
|
tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_HORIZONTAL),
|
||||||
|
tiffPyramid(false),
|
||||||
tiffSquash(false),
|
tiffSquash(false),
|
||||||
|
tiffTile(false),
|
||||||
|
tiffTileHeight(256),
|
||||||
|
tiffTileWidth(256),
|
||||||
tiffXres(1.0),
|
tiffXres(1.0),
|
||||||
tiffYres(1.0),
|
tiffYres(1.0),
|
||||||
withMetadata(false),
|
withMetadata(false),
|
||||||
|
@ -1285,6 +1285,84 @@ describe('Input/output', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('TIFF tiled pyramid image without compression enlarges test file', function (done) {
|
||||||
|
const startSize = fs.statSync(fixtures.inputTiffUncompressed).size;
|
||||||
|
sharp(fixtures.inputTiffUncompressed)
|
||||||
|
.tiff({
|
||||||
|
compression: 'none',
|
||||||
|
pyramid: true,
|
||||||
|
tile: true,
|
||||||
|
tileHeight: 256,
|
||||||
|
tileWidth: 256
|
||||||
|
})
|
||||||
|
.toFile(fixtures.outputTiff, (err, info) => {
|
||||||
|
if (err) throw err;
|
||||||
|
assert.strictEqual('tiff', info.format);
|
||||||
|
assert(info.size > startSize);
|
||||||
|
rimraf(fixtures.outputTiff, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF pyramid true value does not throw error', function () {
|
||||||
|
assert.doesNotThrow(function () {
|
||||||
|
sharp().tiff({ pyramid: true });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid TIFF pyramid value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().tiff({ pyramid: 'true' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid TIFF tile value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().tiff({ tile: 'true' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TIFF tile true value does not throw error', function () {
|
||||||
|
assert.doesNotThrow(function () {
|
||||||
|
sharp().tiff({ tile: true });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Valid TIFF tileHeight value does not throw error', function () {
|
||||||
|
assert.doesNotThrow(function () {
|
||||||
|
sharp().tiff({ tileHeight: 512 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Valid TIFF tileWidth value does not throw error', function () {
|
||||||
|
assert.doesNotThrow(function () {
|
||||||
|
sharp().tiff({ tileWidth: 512 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid TIFF tileHeight value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().tiff({ tileHeight: '256' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid TIFF tileWidth value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().tiff({ tileWidth: '256' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid TIFF tileHeight value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().tiff({ tileHeight: 0 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Invalid TIFF tileWidth value throws error', function () {
|
||||||
|
assert.throws(function () {
|
||||||
|
sharp().tiff({ tileWidth: 0 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Input and output formats match when not forcing', function (done) {
|
it('Input and output formats match when not forcing', function (done) {
|
||||||
sharp(fixtures.inputJpg)
|
sharp(fixtures.inputJpg)
|
||||||
.resize(320, 240)
|
.resize(320, 240)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user