mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 02:30:12 +02:00
Add support for squashing TIFF output to 1-bit (#783)
This commit is contained in:
parent
567e3dd258
commit
4cd3b66761
@ -171,6 +171,7 @@ const Sharp = function (input, options) {
|
||||
tiffQuality: 80,
|
||||
tiffCompression: 'jpeg',
|
||||
tiffPredictor: 'none',
|
||||
tiffSquash: false,
|
||||
tileSize: 256,
|
||||
tileOverlap: 0,
|
||||
// Function to notify of queue length changes
|
||||
|
@ -213,6 +213,7 @@ function webp (options) {
|
||||
* @param {Boolean} [options.force=true] - force TIFF output, otherwise attempt to use input format
|
||||
* @param {Boolean} [options.compression='jpeg'] - compression options: lzw, deflate, jpeg
|
||||
* @param {Boolean} [options.predictor='none'] - compression predictor options: none, horizontal, float
|
||||
* @param {Boolean} [options.squash=false] - squash 8-bit images down to 1 bit
|
||||
* @returns {Sharp}
|
||||
* @throws {Error} Invalid options
|
||||
*/
|
||||
@ -224,6 +225,13 @@ function tiff (options) {
|
||||
throw new Error('Invalid quality (integer, 1-100) ' + options.quality);
|
||||
}
|
||||
}
|
||||
if (is.object(options) && is.defined(options.squash)) {
|
||||
if (is.bool(options.squash)) {
|
||||
this.options.tiffSquash = options.squash;
|
||||
} else {
|
||||
throw new Error('Invalid Value for squash ' + options.squash + ' Only Boolean Values allowed for options.squash.');
|
||||
}
|
||||
}
|
||||
// compression
|
||||
if (is.defined(options) && is.defined(options.compression)) {
|
||||
if (is.string(options.compression) && is.inArray(options.compression, ['lzw', 'deflate', 'jpeg', 'none'])) {
|
||||
|
@ -846,8 +846,9 @@ class PipelineWorker : public Nan::AsyncWorker {
|
||||
image.tiffsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
||||
->set("strip", !baton->withMetadata)
|
||||
->set("Q", baton->tiffQuality)
|
||||
->set("squash", baton->tiffSquash)
|
||||
->set("compression", baton->tiffCompression)
|
||||
->set("predictor", baton->tiffPredictor) );
|
||||
->set("predictor", baton->tiffPredictor));
|
||||
baton->formatOut = "tiff";
|
||||
baton->channels = std::min(baton->channels, 3);
|
||||
} else if (baton->formatOut == "dz" || isDz || isDzZip) {
|
||||
@ -1204,6 +1205,7 @@ NAN_METHOD(pipeline) {
|
||||
baton->webpLossless = AttrTo<bool>(options, "webpLossless");
|
||||
baton->webpNearLossless = AttrTo<bool>(options, "webpNearLossless");
|
||||
baton->tiffQuality = AttrTo<uint32_t>(options, "tiffQuality");
|
||||
baton->tiffSquash = AttrTo<bool>(options, "tiffSquash");
|
||||
// tiff compression options
|
||||
baton->tiffCompression = static_cast<VipsForeignTiffCompression>(
|
||||
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_TIFF_COMPRESSION,
|
||||
|
@ -106,6 +106,7 @@ struct PipelineBaton {
|
||||
int tiffQuality;
|
||||
VipsForeignTiffCompression tiffCompression;
|
||||
VipsForeignTiffPredictor tiffPredictor;
|
||||
bool tiffSquash;
|
||||
std::string err;
|
||||
bool withMetadata;
|
||||
int withMetadataOrientation;
|
||||
@ -176,6 +177,7 @@ struct PipelineBaton {
|
||||
tiffQuality(80),
|
||||
tiffCompression(VIPS_FOREIGN_TIFF_COMPRESSION_JPEG),
|
||||
tiffPredictor(VIPS_FOREIGN_TIFF_PREDICTOR_NONE),
|
||||
tiffSquash(false),
|
||||
withMetadata(false),
|
||||
withMetadataOrientation(-1),
|
||||
convKernelWidth(0),
|
||||
|
BIN
test/fixtures/8bit_depth.tiff
vendored
Normal file
BIN
test/fixtures/8bit_depth.tiff
vendored
Normal file
Binary file not shown.
1
test/fixtures/index.js
vendored
1
test/fixtures/index.js
vendored
@ -85,6 +85,7 @@ module.exports = {
|
||||
inputTiff: getPath('G31D.TIF'), // http://www.fileformat.info/format/tiff/sample/e6c9a6e5253348f4aef6d17b534360ab/index.htm
|
||||
inputTiffCielab: getPath('cielab-dagams.tiff'), // https://github.com/lovell/sharp/issues/646
|
||||
inputTiffUncompressed: getPath('uncompressed_tiff.tiff'), // https://code.google.com/archive/p/imagetestsuite/wikis/TIFFTestSuite.wiki file: 0c84d07e1b22b76f24cccc70d8788e4a.tif
|
||||
inputTiff8BitDepth: getPath('8bit_depth.tiff'),
|
||||
inputGif: getPath('Crash_test.gif'), // http://upload.wikimedia.org/wikipedia/commons/e/e3/Crash_test.gif
|
||||
inputGifGreyPlusAlpha: getPath('grey-plus-alpha.gif'), // http://i.imgur.com/gZ5jlmE.gif
|
||||
inputSvg: getPath('check.svg'), // http://dev.w3.org/SVG/tools/svgweb/samples/svg-files/check.svg
|
||||
|
@ -861,6 +861,44 @@ describe('Input/output', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Not squashing TIFF to a bit depth of 1 should not change the file size', function (done) {
|
||||
const startSize = fs.statSync(fixtures.inputTiff8BitDepth).size;
|
||||
sharp(fixtures.inputTiff8BitDepth)
|
||||
.toColourspace('b-w') // can only squash 1 band uchar images
|
||||
.tiff({
|
||||
squash: false,
|
||||
compression: 'none'
|
||||
})
|
||||
.toFile(fixtures.outputTiff, (err, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('tiff', info.format);
|
||||
assert(info.size === startSize);
|
||||
fs.unlink(fixtures.outputTiff, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('Squashing TIFF to a bit depth of 1 should significantly reduce file size', function (done) {
|
||||
const startSize = fs.statSync(fixtures.inputTiff8BitDepth).size;
|
||||
sharp(fixtures.inputTiff8BitDepth)
|
||||
.toColourspace('b-w') // can only squash 1 band uchar images
|
||||
.tiff({
|
||||
squash: true,
|
||||
compression: 'none'
|
||||
})
|
||||
.toFile(fixtures.outputTiff, (err, info) => {
|
||||
if (err) throw err;
|
||||
assert.strictEqual('tiff', info.format);
|
||||
assert(info.size < (startSize / 2));
|
||||
fs.unlink(fixtures.outputTiff, done);
|
||||
});
|
||||
});
|
||||
|
||||
it('Invalid TIFF squash value throws error', function () {
|
||||
assert.throws(function () {
|
||||
sharp().tiff({ squash: 'true' });
|
||||
});
|
||||
});
|
||||
|
||||
it('TIFF lzw compression with horizontal predictor shrinks test file', function (done) {
|
||||
const startSize = fs.statSync(fixtures.inputTiffUncompressed).size;
|
||||
sharp(fixtures.inputTiffUncompressed)
|
||||
|
Loading…
x
Reference in New Issue
Block a user