From f672f86b539d578026f2505151452ee08196f963 Mon Sep 17 00:00:00 2001 From: Matt Hirsch Date: Sat, 9 Jul 2016 11:21:16 -0400 Subject: [PATCH] Add ability to read and write native vips .v files (#500) --- docs/api.md | 2 +- index.js | 2 +- src/common.cc | 6 ++++++ src/common.h | 2 ++ src/pipeline.cc | 11 ++++++++++- src/utilities.cc | 2 +- test/fixtures/expected/vfile.jpg | Bin 0 -> 1758 bytes test/fixtures/index.js | 3 +++ test/fixtures/vfile.v | Bin 0 -> 12963 bytes test/unit/io.js | 31 +++++++++++++++++++++++++++++++ 10 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 test/fixtures/expected/vfile.jpg create mode 100644 test/fixtures/vfile.v diff --git a/docs/api.md b/docs/api.md index 564df25a..f2f9573e 100644 --- a/docs/api.md +++ b/docs/api.md @@ -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. -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: diff --git a/index.js b/index.js index 40dd8133..414fe206 100644 --- a/index.js +++ b/index.js @@ -975,7 +975,7 @@ Sharp.prototype.toFormat = function(formatOut) { } if ( isDefined(formatOut) && - ['jpeg', 'png', 'webp', 'raw', 'tiff', 'dz', 'input'].indexOf(formatOut) !== -1 + ['jpeg', 'png', 'webp', 'raw', 'tiff', 'dz', 'input'].indexOf(formatOut) !== -1 ) { this.options.formatOut = formatOut; } else { diff --git a/src/common.cc b/src/common.cc index 71f163d0..1045c39e 100644 --- a/src/common.cc +++ b/src/common.cc @@ -55,6 +55,9 @@ namespace sharp { bool IsDzZip(std::string const &str) { 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. @@ -73,6 +76,7 @@ namespace sharp { case ImageType::OPENSLIDE: id = "openslide"; break; case ImageType::PPM: id = "ppm"; break; case ImageType::FITS: id = "fits"; break; + case ImageType::VIPS: id = "v"; break; case ImageType::RAW: id = "raw"; break; case ImageType::UNKNOWN: id = "unknown"; break; } @@ -136,6 +140,8 @@ namespace sharp { imageType = ImageType::PPM; } else if (EndsWith(loader, "Fits")) { imageType = ImageType::FITS; + } else if (EndsWith(loader, "Vips")) { + imageType = ImageType::VIPS; } else if (EndsWith(loader, "Magick") || EndsWith(loader, "MagickFile")) { imageType = ImageType::MAGICK; } diff --git a/src/common.h b/src/common.h index b8d6afc1..511fc804 100644 --- a/src/common.h +++ b/src/common.h @@ -22,6 +22,7 @@ namespace sharp { OPENSLIDE, PPM, FITS, + VIPS, RAW, UNKNOWN }; @@ -39,6 +40,7 @@ namespace sharp { bool IsTiff(std::string const &str); bool IsDz(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. diff --git a/src/pipeline.cc b/src/pipeline.cc index a2721449..c3ac736d 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -73,6 +73,7 @@ using sharp::IsWebp; using sharp::IsTiff; using sharp::IsDz; using sharp::IsDzZip; +using sharp::IsV; using sharp::FreeCallback; using sharp::CalculateCrop; using sharp::counterProcess; @@ -878,7 +879,9 @@ class PipelineWorker : public AsyncWorker { bool isTiff = IsTiff(baton->fileOut); bool isDz = IsDz(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)) { // Write JPEG to file image.jpegsave(const_cast(baton->fileOut.data()), VImage::option() @@ -932,6 +935,12 @@ class PipelineWorker : public AsyncWorker { ->set("layout", baton->tileLayout) ); baton->formatOut = "dz"; + } else if (baton->formatOut == "v" || isV || (matchInput && inputImageType == ImageType::VIPS)) { + // Write V to file + image.vipssave(const_cast(baton->fileOut.data()), VImage::option() + ->set("strip", !baton->withMetadata) + ); + baton->formatOut = "v"; } else { // Unsupported output format (baton->err).append("Unsupported output format " + baton->fileOut); diff --git a/src/utilities.cc b/src/utilities.cc index 3cc686d2..8dae0bd9 100644 --- a/src/utilities.cc +++ b/src/utilities.cc @@ -139,7 +139,7 @@ NAN_METHOD(format) { // Which load/save operations are available for each compressed format? Local format = New(); 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 Local hasInputFile = diff --git a/test/fixtures/expected/vfile.jpg b/test/fixtures/expected/vfile.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9d1e4a571017836d3b282aaeb9d2d15bbc464956 GIT binary patch literal 1758 zcmb7EeK=El96#r5hp|oP%)Bly8>(B#h{qLXLV3AZuU9R#w0k3?)YS`lxiyI>H!XUT zqO8(MM2Ss-B)6 zX_|qdKGl#y)uXCGKpgfB9@c_kEh?EvrvC3z-33qr;0pNQKoo#b5Ql=Qdw?+jKsapK zse&~i93D{jj6eWu{n!IQ9E68e?Enb}0uX{j000aba*ID;4&?aa!qYbDhs%2h{CN#8 z&g?$al44p~{)m@R6ShMbdtl|k1vY1wdJJ^3Trx8*=^eF{rz{akjLE~>mz|C5?(4o{ z-?}9@;N;fA$I=iQuN7nz4>~6hqRu)gV-Cj)?=9xpH%?1&*kbUjqg&(&eq&s?aPNMb zs>O2hmB{w)AZ%Mp1Y-h#cueR?G83;97}cdS*i=hIkBKc#!1#bL6#S@q|7(x0I#%K% zL?$<)Bj-r=!*wxz&-@~2eBt7|x0>8e*+T84Vcngsd5^~~r zjo5(pOMJq+=X#IZ!kf)T$HwYGX`Otj0(=u1)5Y4lt1xiL!j&jHJFHLYSoDXN#@MO6x-BWGH0~itkuyoE`b!3xF~DBU+>PQc8|HC3 zerD1tSFURn&^a7I9)@Iv367-_#7|*?C`4!C5Q;fRmtoI#;BB5%T!RJWmAmBjSMRKk zcrJPt>R-{`lN?;)xoZ9EmDx6ai6)*_q^>bVkJn?r?9CU}-%Nk-DdYV3wBGZ@4`K#w zuPX8imNw1Q@ayfBmHBoh*VLjM%UY_SvE3!W=(iA209Tm2#EFHiWQKf;ET+=rQXqiR znQRVkb8=Sccl%lN;1S%hqBr7%(xZj^lDwi7oke?>eV%Q-=k(xv?KXy1W?iINiMu$h z`I6xVmxb0Uz!~E1$>bJ=#|xunBQtLMiNl=!ye?0@EvZgl{eCVaAimn7ASKTtj2A{b zriY~)i=J2<;YSrN8hEokKGWNra4T0J6~q=@=a$tIs_h)CsN>&IVVxBSuqy! zM%q>UlYcEnYT`0Z5JtQ>R-L{d&$)Z*G)a-08`qjC!@qty)_J=C?Z)Jxp%WMSKN&*l z2-AEjLP)3%aoeHo<-#CYH(4ui)&lw&%R1K1F|MEgY}U@vlw6Z*1tk%zeW#}z$Ilz5 zm?FV}!qfu4c<_DouJFBJUyAMqyVn znYq53Byn>Lv0)Z!=wuO9z$znvc|e@ErD=qv=vk6jhn{Uu&0faJ#c4 z?gB8T489S^X>C0G&~D60_~otkyq11vW|*OI1qj@(mh|vX+x{X8oCWZwXVBwG8FUsVoswMH&T)F!w+7qv|ekH zc3pD&%+)wylD37RHQoEONs|2qMQCi}(^!L-$NIG{xfzg57s@F$)LBm3dPMkjJxkew!H+w+~Pb(&5MY_K`u;i{Wr7fDM?+yGE# hEK!0Nt{qTPoYzplu=&Z-3=u!GGl$X>rASmg{Rd(mfHMF9 literal 0 HcmV?d00001 diff --git a/test/fixtures/index.js b/test/fixtures/index.js index 3555a93c..65df6673 100644 --- a/test/fixtures/index.js +++ b/test/fixtures/index.js @@ -95,9 +95,12 @@ module.exports = { inputPngStripesV: getPath('stripesV.png'), inputPngStripesH: getPath('stripesH.png'), + inputV: getPath('vfile.v'), + outputJpg: getPath('output.jpg'), outputPng: getPath('output.png'), outputWebP: getPath('output.webp'), + outputV: getPath('output.v'), outputZoinks: getPath('output.zoinks'), // an 'unknown' file extension // Path for tests requiring human inspection diff --git a/test/fixtures/vfile.v b/test/fixtures/vfile.v new file mode 100644 index 0000000000000000000000000000000000000000..d0fb600065344e06a18a4ee9d2fb14406ca7cc53 GIT binary patch literal 12963 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 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) { describe('Ouput raw, uncompressed image data', function() { it('1 channel greyscale image', function(done) {