Ensure tests pass with latest libvips master branch

Expose forthcoming HEIF features where available
This commit is contained in:
Lovell Fuller 2021-01-13 16:47:49 +00:00
parent 138e60adb3
commit 8d49b7dde1
12 changed files with 50 additions and 9 deletions

View File

@ -327,6 +327,7 @@ most web browsers do not display these properly.
- `options.quality` **[number][9]** quality, integer 1-100 (optional, default `50`)
- `options.lossless` **[boolean][7]** use lossless compression (optional, default `false`)
- `options.speed` **[boolean][7]** CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest) (optional, default `5`)
- `options.chromaSubsampling` **[string][2]** set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling, requires libvips v8.11.0 (optional, default `'4:2:0'`)
- Throws **[Error][4]** Invalid options
@ -351,6 +352,7 @@ globally-installed libvips compiled with support for libheif, libde265 and x265.
- `options.compression` **[boolean][7]** compression format: av1, hevc (optional, default `'av1'`)
- `options.lossless` **[boolean][7]** use lossless compression (optional, default `false`)
- `options.speed` **[boolean][7]** CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest) (optional, default `5`)
- `options.chromaSubsampling` **[string][2]** set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling, requires libvips v8.11.0 (optional, default `'4:2:0'`)
- Throws **[Error][4]** Invalid options

View File

@ -251,6 +251,7 @@ const Sharp = function (input, options) {
heifLossless: false,
heifCompression: 'av1',
heifSpeed: 5,
heifChromaSubsampling: '4:2:0',
tileSize: 256,
tileOverlap: 0,
tileContainer: 'fs',

View File

@ -39,8 +39,9 @@ const cachePath = function () {
const globalLibvipsVersion = function () {
if (process.platform !== 'win32') {
const globalLibvipsVersion = spawnSync(`PKG_CONFIG_PATH="${pkgConfigPath()}" pkg-config --modversion vips-cpp`, spawnSyncOptions).stdout || '';
return globalLibvipsVersion.trim();
const globalLibvipsVersion = spawnSync(`PKG_CONFIG_PATH="${pkgConfigPath()}" pkg-config --modversion vips-cpp`, spawnSyncOptions).stdout;
/* istanbul ignore next */
return (globalLibvipsVersion || '').trim();
} else {
return '';
}

View File

@ -583,6 +583,7 @@ function tiff (options) {
* @param {number} [options.quality=50] - quality, integer 1-100
* @param {boolean} [options.lossless=false] - use lossless compression
* @param {boolean} [options.speed=5] - CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest)
* @param {string} [options.chromaSubsampling='4:2:0'] - set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling, requires libvips v8.11.0
* @returns {Sharp}
* @throws {Error} Invalid options
*/
@ -603,6 +604,7 @@ function avif (options) {
* @param {boolean} [options.compression='av1'] - compression format: av1, hevc
* @param {boolean} [options.lossless=false] - use lossless compression
* @param {boolean} [options.speed=5] - CPU effort vs file size, 0 (slowest/smallest) to 8 (fastest/largest)
* @param {string} [options.chromaSubsampling='4:2:0'] - set to '4:4:4' to prevent chroma subsampling otherwise defaults to '4:2:0' chroma subsampling, requires libvips v8.11.0
* @returns {Sharp}
* @throws {Error} Invalid options
*/
@ -636,6 +638,13 @@ function heif (options) {
throw is.invalidParameterError('speed', 'integer between 0 and 8', options.speed);
}
}
if (is.defined(options.chromaSubsampling)) {
if (is.string(options.chromaSubsampling) && is.inArray(options.chromaSubsampling, ['4:2:0', '4:4:4'])) {
this.options.heifChromaSubsampling = options.chromaSubsampling;
} else {
throw is.invalidParameterError('chromaSubsampling', 'one of: 4:2:0, 4:4:4', options.chromaSubsampling);
}
}
}
return this._updateFormatOut('heif', options);
}

View File

@ -74,6 +74,9 @@ class MetadataWorker : public Napi::AsyncWorker {
if (image.get_typeof("heif-primary") == G_TYPE_INT) {
baton->pagePrimary = image.get_int("heif-primary");
}
if (image.get_typeof("heif-compression") == VIPS_TYPE_REF_STRING) {
baton->compression = image.get_string("heif-compression");
}
if (image.get_typeof("openslide.level-count") == VIPS_TYPE_REF_STRING) {
int const levels = std::stoi(image.get_string("openslide.level-count"));
for (int l = 0; l < levels; l++) {
@ -186,6 +189,9 @@ class MetadataWorker : public Napi::AsyncWorker {
if (baton->pagePrimary > -1) {
info.Set("pagePrimary", baton->pagePrimary);
}
if (!baton->compression.empty()) {
info.Set("compression", baton->compression);
}
if (!baton->levels.empty()) {
int i = 0;
Napi::Array levels = Napi::Array::New(env, static_cast<size_t>(baton->levels.size()));

View File

@ -39,6 +39,7 @@ struct MetadataBaton {
int loop;
std::vector<int> delay;
int pagePrimary;
std::string compression;
std::vector<std::pair<int, int>> levels;
bool hasProfile;
bool hasAlpha;

View File

@ -838,6 +838,10 @@ class PipelineWorker : public Napi::AsyncWorker {
->set("compression", baton->heifCompression)
->set("Q", baton->heifQuality)
->set("speed", baton->heifSpeed)
#ifdef VIPS_TYPE_FOREIGN_SUBSAMPLE
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
#endif
->set("lossless", baton->heifLossless)));
baton->bufferOut = static_cast<char*>(area->data);
baton->bufferOutLength = area->length;
@ -972,6 +976,10 @@ class PipelineWorker : public Napi::AsyncWorker {
->set("Q", baton->heifQuality)
->set("compression", baton->heifCompression)
->set("speed", baton->heifSpeed)
#ifdef VIPS_TYPE_FOREIGN_SUBSAMPLE
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
#endif
->set("lossless", baton->heifLossless));
baton->formatOut = "heif";
} else if (baton->formatOut == "dz" || isDz || isDzZip) {
@ -1396,6 +1404,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
sharp::AttrAsStr(options, "heifCompression").data()));
baton->heifSpeed = sharp::AttrAsUint32(options, "heifSpeed");
baton->heifChromaSubsampling = sharp::AttrAsStr(options, "heifChromaSubsampling");
// Animated output
if (sharp::HasAttr(options, "pageHeight")) {

View File

@ -162,6 +162,7 @@ struct PipelineBaton {
int heifQuality;
VipsForeignHeifCompression heifCompression;
int heifSpeed;
std::string heifChromaSubsampling;
bool heifLossless;
std::string err;
bool withMetadata;
@ -282,6 +283,7 @@ struct PipelineBaton {
heifQuality(50),
heifCompression(VIPS_FOREIGN_HEIF_COMPRESSION_AV1),
heifSpeed(5),
heifChromaSubsampling("4:2:0"),
heifLossless(false),
withMetadata(false),
withMetadataOrientation(-1),

View File

@ -18,7 +18,7 @@ describe('AVIF', () => {
.toBuffer();
const metadata = await sharp(data)
.metadata();
const { size, ...metadataWithoutSize } = metadata;
const { compression, size, ...metadataWithoutSize } = metadata;
assert.deepStrictEqual(metadataWithoutSize, {
channels: 3,
depth: 'uchar',
@ -42,7 +42,7 @@ describe('AVIF', () => {
.toBuffer();
const metadata = await sharp(data)
.metadata();
const { size, ...metadataWithoutSize } = metadata;
const { compression, size, ...metadataWithoutSize } = metadata;
assert.deepStrictEqual(metadataWithoutSize, {
channels: 3,
chromaSubsampling: '4:2:0',
@ -65,7 +65,7 @@ describe('AVIF', () => {
.toBuffer();
const metadata = await sharp(data)
.metadata();
const { size, ...metadataWithoutSize } = metadata;
const { compression, size, ...metadataWithoutSize } = metadata;
assert.deepStrictEqual(metadataWithoutSize, {
channels: 3,
depth: 'uchar',

View File

@ -53,7 +53,7 @@ describe('failOnError', function () {
it('returns errors to callback for truncated JPEG', function (done) {
sharp(fixtures.inputJpgTruncated).toBuffer(function (err, data, info) {
assert.ok(err.message.includes('VipsJpeg: Premature end of JPEG file'), err);
assert.ok(err.message.includes('VipsJpeg: Premature end of'), err);
assert.strictEqual(data, undefined);
assert.strictEqual(info, undefined);
done();
@ -76,7 +76,7 @@ describe('failOnError', function () {
throw new Error('Expected rejection');
})
.catch(err => {
done(err.message.includes('VipsJpeg: Premature end of JPEG file') ? undefined : err);
done(err.message.includes('VipsJpeg: Premature end of') ? undefined : err);
});
});

View File

@ -65,4 +65,14 @@ describe('HEIF', () => {
sharp().heif({ compression: 'fail' });
});
});
it('invalid chromaSubsampling should throw an error', () => {
assert.throws(() => {
sharp().heif({ chromaSubsampling: 'fail' });
});
});
it('valid chromaSubsampling does not throw an error', () => {
assert.doesNotThrow(() => {
sharp().heif({ chromaSubsampling: '4:4:4' });
});
});
});

View File

@ -667,7 +667,7 @@ describe('Image metadata', function () {
sharp(fixtures.inputJpgWithCorruptHeader)
.metadata(function (err) {
assert.strictEqual(true, !!err);
assert.strictEqual(true, /Input file has corrupt header: VipsJpeg: Premature end of JPEG file/.test(err.message));
assert.ok(err.message.includes('Input file has corrupt header: VipsJpeg: Premature end of'), err);
done();
});
});
@ -676,7 +676,7 @@ describe('Image metadata', function () {
sharp(fs.readFileSync(fixtures.inputJpgWithCorruptHeader))
.metadata(function (err) {
assert.strictEqual(true, !!err);
assert.strictEqual(true, /Input buffer has corrupt header: VipsJpeg: Premature end of JPEG file/.test(err.message));
assert.ok(err.message.includes('Input buffer has corrupt header: VipsJpeg: Premature end of'), err);
done();
});
});