Add ability to read and write native vips .v files (#500)

This commit is contained in:
Matt Hirsch 2016-07-09 11:21:16 -04:00 committed by Lovell Fuller
parent b69627891d
commit f672f86b53
10 changed files with 55 additions and 4 deletions

View File

@ -507,7 +507,7 @@ In the above example if `input.png` is a 3 channel RGB image, `output.png` will
`path` is a String containing the path to write the image data to. `path` is a String containing the path to write the image data to.
If an explicit output format is not selected, it will be inferred from the extension, with JPEG, PNG, WebP, TIFF and DZI supported. Note that RAW format is only supported for buffer output. If an explicit output format is not selected, it will be inferred from the extension, with JPEG, PNG, WebP, TIFF, DZI, and VIPS V format supported. Note that RAW format is only supported for buffer output.
`callback`, if present, is called with two arguments `(err, info)` where: `callback`, if present, is called with two arguments `(err, info)` where:

View File

@ -55,6 +55,9 @@ namespace sharp {
bool IsDzZip(std::string const &str) { bool IsDzZip(std::string const &str) {
return EndsWith(str, ".zip") || EndsWith(str, ".ZIP") || EndsWith(str, ".szi") || EndsWith(str, ".SZI"); return EndsWith(str, ".zip") || EndsWith(str, ".ZIP") || EndsWith(str, ".szi") || EndsWith(str, ".SZI");
} }
bool IsV(std::string const &str) {
return EndsWith(str, ".v") || EndsWith(str, ".V") || EndsWith(str, ".vips") || EndsWith(str, ".VIPS");
}
/* /*
Provide a string identifier for the given image type. Provide a string identifier for the given image type.
@ -73,6 +76,7 @@ namespace sharp {
case ImageType::OPENSLIDE: id = "openslide"; break; case ImageType::OPENSLIDE: id = "openslide"; break;
case ImageType::PPM: id = "ppm"; break; case ImageType::PPM: id = "ppm"; break;
case ImageType::FITS: id = "fits"; break; case ImageType::FITS: id = "fits"; break;
case ImageType::VIPS: id = "v"; break;
case ImageType::RAW: id = "raw"; break; case ImageType::RAW: id = "raw"; break;
case ImageType::UNKNOWN: id = "unknown"; break; case ImageType::UNKNOWN: id = "unknown"; break;
} }
@ -136,6 +140,8 @@ namespace sharp {
imageType = ImageType::PPM; imageType = ImageType::PPM;
} else if (EndsWith(loader, "Fits")) { } else if (EndsWith(loader, "Fits")) {
imageType = ImageType::FITS; imageType = ImageType::FITS;
} else if (EndsWith(loader, "Vips")) {
imageType = ImageType::VIPS;
} else if (EndsWith(loader, "Magick") || EndsWith(loader, "MagickFile")) { } else if (EndsWith(loader, "Magick") || EndsWith(loader, "MagickFile")) {
imageType = ImageType::MAGICK; imageType = ImageType::MAGICK;
} }

View File

@ -22,6 +22,7 @@ namespace sharp {
OPENSLIDE, OPENSLIDE,
PPM, PPM,
FITS, FITS,
VIPS,
RAW, RAW,
UNKNOWN UNKNOWN
}; };
@ -39,6 +40,7 @@ namespace sharp {
bool IsTiff(std::string const &str); bool IsTiff(std::string const &str);
bool IsDz(std::string const &str); bool IsDz(std::string const &str);
bool IsDzZip(std::string const &str); bool IsDzZip(std::string const &str);
bool IsV(std::string const &str);
/* /*
Provide a string identifier for the given image type. Provide a string identifier for the given image type.

View File

@ -73,6 +73,7 @@ using sharp::IsWebp;
using sharp::IsTiff; using sharp::IsTiff;
using sharp::IsDz; using sharp::IsDz;
using sharp::IsDzZip; using sharp::IsDzZip;
using sharp::IsV;
using sharp::FreeCallback; using sharp::FreeCallback;
using sharp::CalculateCrop; using sharp::CalculateCrop;
using sharp::counterProcess; using sharp::counterProcess;
@ -878,7 +879,9 @@ class PipelineWorker : public AsyncWorker {
bool isTiff = IsTiff(baton->fileOut); bool isTiff = IsTiff(baton->fileOut);
bool isDz = IsDz(baton->fileOut); bool isDz = IsDz(baton->fileOut);
bool isDzZip = IsDzZip(baton->fileOut); bool isDzZip = IsDzZip(baton->fileOut);
bool matchInput = baton->formatOut == "input" && !(isJpeg || isPng || isWebp || isTiff || isDz || isDzZip); bool isV = IsV(baton->fileOut);
bool matchInput = baton->formatOut == "input" &&
!(isJpeg || isPng || isWebp || isTiff || isDz || isDzZip || isV);
if (baton->formatOut == "jpeg" || isJpeg || (matchInput && inputImageType == ImageType::JPEG)) { if (baton->formatOut == "jpeg" || isJpeg || (matchInput && inputImageType == ImageType::JPEG)) {
// Write JPEG to file // Write JPEG to file
image.jpegsave(const_cast<char*>(baton->fileOut.data()), VImage::option() image.jpegsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
@ -932,6 +935,12 @@ class PipelineWorker : public AsyncWorker {
->set("layout", baton->tileLayout) ->set("layout", baton->tileLayout)
); );
baton->formatOut = "dz"; baton->formatOut = "dz";
} else if (baton->formatOut == "v" || isV || (matchInput && inputImageType == ImageType::VIPS)) {
// Write V to file
image.vipssave(const_cast<char*>(baton->fileOut.data()), VImage::option()
->set("strip", !baton->withMetadata)
);
baton->formatOut = "v";
} else { } else {
// Unsupported output format // Unsupported output format
(baton->err).append("Unsupported output format " + baton->fileOut); (baton->err).append("Unsupported output format " + baton->fileOut);

View File

@ -139,7 +139,7 @@ NAN_METHOD(format) {
// Which load/save operations are available for each compressed format? // Which load/save operations are available for each compressed format?
Local<Object> format = New<Object>(); Local<Object> format = New<Object>();
for (std::string f : { for (std::string f : {
"jpeg", "png", "webp", "tiff", "magick", "openslide", "dz", "ppm", "fits", "gif", "svg", "pdf" "jpeg", "png", "webp", "tiff", "magick", "openslide", "dz", "ppm", "fits", "gif", "svg", "pdf", "v"
}) { }) {
// Input // Input
Local<Boolean> hasInputFile = Local<Boolean> hasInputFile =

BIN
test/fixtures/expected/vfile.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -95,9 +95,12 @@ module.exports = {
inputPngStripesV: getPath('stripesV.png'), inputPngStripesV: getPath('stripesV.png'),
inputPngStripesH: getPath('stripesH.png'), inputPngStripesH: getPath('stripesH.png'),
inputV: getPath('vfile.v'),
outputJpg: getPath('output.jpg'), outputJpg: getPath('output.jpg'),
outputPng: getPath('output.png'), outputPng: getPath('output.png'),
outputWebP: getPath('output.webp'), outputWebP: getPath('output.webp'),
outputV: getPath('output.v'),
outputZoinks: getPath('output.zoinks'), // an 'unknown' file extension outputZoinks: getPath('output.zoinks'), // an 'unknown' file extension
// Path for tests requiring human inspection // Path for tests requiring human inspection

BIN
test/fixtures/vfile.v vendored Normal file

Binary file not shown.

View File

@ -781,6 +781,37 @@ describe('Input/output', function() {
}); });
} }
if (sharp.format.v.input.file) {
it("Load Vips V file", function(done) {
sharp(fixtures.inputV)
.jpeg()
.toBuffer(function(err, data, info) {
if (err) throw err;
assert.strictEqual(true, data.length > 0);
assert.strictEqual('jpeg', info.format);
assert.strictEqual(70, info.width);
assert.strictEqual(60, info.height);
fixtures.assertSimilar(fixtures.expected('vfile.jpg'), data, done);
});
});
}
if (sharp.format.v.output.file) {
it("Save Vips V file", function(done) {
sharp(fixtures.inputJpg)
.extract({left: 910, top: 1105, width: 70, height: 60})
.toFile(fixtures.outputV, function(err, info) {
if(err) throw err;
assert.strictEqual(true, info.size > 0);
assert.strictEqual('v', info.format);
assert.strictEqual(70, info.width);
assert.strictEqual(60, info.height);
fs.unlinkSync(fixtures.outputV);
done();
});
});
}
if (sharp.format.raw.output.buffer) { if (sharp.format.raw.output.buffer) {
describe('Ouput raw, uncompressed image data', function() { describe('Ouput raw, uncompressed image data', function() {
it('1 channel greyscale image', function(done) { it('1 channel greyscale image', function(done) {