mirror of
https://github.com/lovell/sharp.git
synced 2025-07-09 18:40:16 +02:00
Add experimental support for HEIF images #1105
Requires a custom, globally-installed libvips compiled with libheif
This commit is contained in:
parent
3ff3353550
commit
b737d4601e
@ -34,8 +34,9 @@ A `Promise` is returned when `callback` is not provided.
|
|||||||
- `density`: Number of pixels per inch (DPI), if present
|
- `density`: Number of pixels per inch (DPI), if present
|
||||||
- `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
|
- `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
|
||||||
- `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
- `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
||||||
- `pages`: Number of pages/frames contained within the image, with support for TIFF, PDF, animated GIF and animated WebP
|
- `pages`: Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP
|
||||||
- `pageHeight`: Number of pixels high each page in this PDF image will be.
|
- `pageHeight`: Number of pixels high each page in this PDF image will be.
|
||||||
|
- `pagePrimary`: Number of the primary page in a HEIF image
|
||||||
- `hasProfile`: Boolean indicating the presence of an embedded ICC profile
|
- `hasProfile`: Boolean indicating the presence of an embedded ICC profile
|
||||||
- `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
- `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
||||||
- `orientation`: Number value of the EXIF Orientation header, if present
|
- `orientation`: Number value of the EXIF Orientation header, if present
|
||||||
|
@ -232,6 +232,29 @@ sharp('input.svg')
|
|||||||
.then(info => { ... });
|
.then(info => { ... });
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- Throws **[Error][3]** Invalid options
|
||||||
|
|
||||||
|
Returns **Sharp**
|
||||||
|
|
||||||
|
## heif
|
||||||
|
|
||||||
|
Use these HEIF options for output image.
|
||||||
|
|
||||||
|
Support for HEIF (HEIC/AVIF) is experimental.
|
||||||
|
Do not use this in production systems.
|
||||||
|
|
||||||
|
Requires a custom, globally-installed libvips compiled with support for libheif.
|
||||||
|
|
||||||
|
Most versions of libheif support only the patent-encumbered HEVC compression format.
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
- `options` **[Object][5]?** output options
|
||||||
|
- `options.quality` **[Number][8]** quality, integer 1-100 (optional, default `80`)
|
||||||
|
- `options.compression` **[Boolean][6]** compression format: hevc, avc, jpeg, av1 (optional, default `'hevc'`)
|
||||||
|
- `options.lossless` **[Boolean][6]** use lossless compression (optional, default `false`)
|
||||||
|
|
||||||
|
|
||||||
- Throws **[Error][3]** Invalid options
|
- Throws **[Error][3]** Invalid options
|
||||||
|
|
||||||
Returns **Sharp**
|
Returns **Sharp**
|
||||||
|
@ -8,6 +8,9 @@ Requires libvips v8.8.0.
|
|||||||
|
|
||||||
* Remove `overlayWith` previously deprecated in v0.22.0.
|
* Remove `overlayWith` previously deprecated in v0.22.0.
|
||||||
|
|
||||||
|
* Add experimental support for HEIF images. Requires libvips compiled with libheif.
|
||||||
|
[#1105](https://github.com/lovell/sharp/issues/1105)
|
||||||
|
|
||||||
* Add experimental support for Worker Threads.
|
* Add experimental support for Worker Threads.
|
||||||
[#1558](https://github.com/lovell/sharp/issues/1558)
|
[#1558](https://github.com/lovell/sharp/issues/1558)
|
||||||
|
|
||||||
|
@ -207,6 +207,9 @@ const Sharp = function (input, options) {
|
|||||||
tiffTileWidth: 256,
|
tiffTileWidth: 256,
|
||||||
tiffXres: 1.0,
|
tiffXres: 1.0,
|
||||||
tiffYres: 1.0,
|
tiffYres: 1.0,
|
||||||
|
heifQuality: 80,
|
||||||
|
heifLossless: false,
|
||||||
|
heifCompression: 'hevc',
|
||||||
tileSize: 256,
|
tileSize: 256,
|
||||||
tileOverlap: 0,
|
tileOverlap: 0,
|
||||||
linearA: 1,
|
linearA: 1,
|
||||||
|
@ -195,8 +195,9 @@ function clone () {
|
|||||||
* - `density`: Number of pixels per inch (DPI), if present
|
* - `density`: Number of pixels per inch (DPI), if present
|
||||||
* - `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
|
* - `chromaSubsampling`: String containing JPEG chroma subsampling, `4:2:0` or `4:4:4` for RGB, `4:2:0:4` or `4:4:4:4` for CMYK
|
||||||
* - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
* - `isProgressive`: Boolean indicating whether the image is interlaced using a progressive scan
|
||||||
* - `pages`: Number of pages/frames contained within the image, with support for TIFF, PDF, animated GIF and animated WebP
|
* - `pages`: Number of pages/frames contained within the image, with support for TIFF, HEIF, PDF, animated GIF and animated WebP
|
||||||
* - `pageHeight`: Number of pixels high each page in this PDF image will be.
|
* - `pageHeight`: Number of pixels high each page in this PDF image will be.
|
||||||
|
* - `pagePrimary`: Number of the primary page in a HEIF image
|
||||||
* - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
|
* - `hasProfile`: Boolean indicating the presence of an embedded ICC profile
|
||||||
* - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
* - `hasAlpha`: Boolean indicating the presence of an alpha transparency channel
|
||||||
* - `orientation`: Number value of the EXIF Orientation header, if present
|
* - `orientation`: Number value of the EXIF Orientation header, if present
|
||||||
|
@ -429,6 +429,53 @@ function tiff (options) {
|
|||||||
return this._updateFormatOut('tiff', options);
|
return this._updateFormatOut('tiff', options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use these HEIF options for output image.
|
||||||
|
*
|
||||||
|
* Support for HEIF (HEIC/AVIF) is experimental.
|
||||||
|
* Do not use this in production systems.
|
||||||
|
*
|
||||||
|
* Requires a custom, globally-installed libvips compiled with support for libheif.
|
||||||
|
*
|
||||||
|
* Most versions of libheif support only the patent-encumbered HEVC compression format.
|
||||||
|
*
|
||||||
|
* @param {Object} [options] - output options
|
||||||
|
* @param {Number} [options.quality=80] - quality, integer 1-100
|
||||||
|
* @param {Boolean} [options.compression='hevc'] - compression format: hevc, avc, jpeg, av1
|
||||||
|
* @param {Boolean} [options.lossless=false] - use lossless compression
|
||||||
|
* @returns {Sharp}
|
||||||
|
* @throws {Error} Invalid options
|
||||||
|
*/
|
||||||
|
function heif (options) {
|
||||||
|
if (!this.constructor.format.heif.output.buffer) {
|
||||||
|
throw new Error('The heif operation requires libvips to have been installed with support for libheif');
|
||||||
|
}
|
||||||
|
if (is.object(options)) {
|
||||||
|
if (is.defined(options.quality)) {
|
||||||
|
if (is.integer(options.quality) && is.inRange(options.quality, 1, 100)) {
|
||||||
|
this.options.heifQuality = options.quality;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('quality', 'integer between 1 and 100', options.quality);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is.defined(options.lossless)) {
|
||||||
|
if (is.bool(options.lossless)) {
|
||||||
|
this.options.heifLossless = options.lossless;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('lossless', 'boolean', options.lossless);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is.defined(options.compression)) {
|
||||||
|
if (is.string(options.compression) && is.inArray(options.compression, ['hevc', 'avc', 'jpeg', 'av1'])) {
|
||||||
|
this.options.heifCompression = options.compression;
|
||||||
|
} else {
|
||||||
|
throw is.invalidParameterError('compression', 'one of: hevc, avc, jpeg, av1', options.compression);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this._updateFormatOut('heif', options);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Force output to be raw, uncompressed uint8 pixel data.
|
* Force output to be raw, uncompressed uint8 pixel data.
|
||||||
*
|
*
|
||||||
@ -720,6 +767,7 @@ module.exports = function (Sharp) {
|
|||||||
png,
|
png,
|
||||||
webp,
|
webp,
|
||||||
tiff,
|
tiff,
|
||||||
|
heif,
|
||||||
raw,
|
raw,
|
||||||
toFormat,
|
toFormat,
|
||||||
tile,
|
tile,
|
||||||
|
@ -110,6 +110,15 @@ namespace sharp {
|
|||||||
bool IsTiff(std::string const &str) {
|
bool IsTiff(std::string const &str) {
|
||||||
return EndsWith(str, ".tif") || EndsWith(str, ".tiff") || EndsWith(str, ".TIF") || EndsWith(str, ".TIFF");
|
return EndsWith(str, ".tif") || EndsWith(str, ".tiff") || EndsWith(str, ".TIF") || EndsWith(str, ".TIFF");
|
||||||
}
|
}
|
||||||
|
bool IsHeic(std::string const &str) {
|
||||||
|
return EndsWith(str, ".heic") || EndsWith(str, ".HEIC");
|
||||||
|
}
|
||||||
|
bool IsHeif(std::string const &str) {
|
||||||
|
return EndsWith(str, ".heif") || EndsWith(str, ".HEIF") || IsHeic(str) || IsAvif(str);
|
||||||
|
}
|
||||||
|
bool IsAvif(std::string const &str) {
|
||||||
|
return EndsWith(str, ".avif") || EndsWith(str, ".AVIF");
|
||||||
|
}
|
||||||
bool IsDz(std::string const &str) {
|
bool IsDz(std::string const &str) {
|
||||||
return EndsWith(str, ".dzi") || EndsWith(str, ".DZI");
|
return EndsWith(str, ".dzi") || EndsWith(str, ".DZI");
|
||||||
}
|
}
|
||||||
@ -132,6 +141,7 @@ namespace sharp {
|
|||||||
case ImageType::TIFF: id = "tiff"; break;
|
case ImageType::TIFF: id = "tiff"; break;
|
||||||
case ImageType::GIF: id = "gif"; break;
|
case ImageType::GIF: id = "gif"; break;
|
||||||
case ImageType::SVG: id = "svg"; break;
|
case ImageType::SVG: id = "svg"; break;
|
||||||
|
case ImageType::HEIF: id = "heif"; break;
|
||||||
case ImageType::PDF: id = "pdf"; break;
|
case ImageType::PDF: id = "pdf"; break;
|
||||||
case ImageType::MAGICK: id = "magick"; break;
|
case ImageType::MAGICK: id = "magick"; break;
|
||||||
case ImageType::OPENSLIDE: id = "openslide"; break;
|
case ImageType::OPENSLIDE: id = "openslide"; break;
|
||||||
@ -165,6 +175,8 @@ namespace sharp {
|
|||||||
imageType = ImageType::GIF;
|
imageType = ImageType::GIF;
|
||||||
} else if (EndsWith(loader, "SvgBuffer")) {
|
} else if (EndsWith(loader, "SvgBuffer")) {
|
||||||
imageType = ImageType::SVG;
|
imageType = ImageType::SVG;
|
||||||
|
} else if (EndsWith(loader, "HeifBuffer")) {
|
||||||
|
imageType = ImageType::HEIF;
|
||||||
} else if (EndsWith(loader, "PdfBuffer")) {
|
} else if (EndsWith(loader, "PdfBuffer")) {
|
||||||
imageType = ImageType::PDF;
|
imageType = ImageType::PDF;
|
||||||
} else if (EndsWith(loader, "MagickBuffer")) {
|
} else if (EndsWith(loader, "MagickBuffer")) {
|
||||||
@ -196,6 +208,8 @@ namespace sharp {
|
|||||||
imageType = ImageType::GIF;
|
imageType = ImageType::GIF;
|
||||||
} else if (EndsWith(loader, "SvgFile")) {
|
} else if (EndsWith(loader, "SvgFile")) {
|
||||||
imageType = ImageType::SVG;
|
imageType = ImageType::SVG;
|
||||||
|
} else if (EndsWith(loader, "HeifFile")) {
|
||||||
|
imageType = ImageType::HEIF;
|
||||||
} else if (EndsWith(loader, "PdfFile")) {
|
} else if (EndsWith(loader, "PdfFile")) {
|
||||||
imageType = ImageType::PDF;
|
imageType = ImageType::PDF;
|
||||||
} else if (EndsWith(loader, "Ppm")) {
|
} else if (EndsWith(loader, "Ppm")) {
|
||||||
@ -222,6 +236,7 @@ namespace sharp {
|
|||||||
return
|
return
|
||||||
imageType == ImageType::GIF ||
|
imageType == ImageType::GIF ||
|
||||||
imageType == ImageType::TIFF ||
|
imageType == ImageType::TIFF ||
|
||||||
|
imageType == ImageType::HEIF ||
|
||||||
imageType == ImageType::PDF;
|
imageType == ImageType::PDF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,6 +101,7 @@ namespace sharp {
|
|||||||
TIFF,
|
TIFF,
|
||||||
GIF,
|
GIF,
|
||||||
SVG,
|
SVG,
|
||||||
|
HEIF,
|
||||||
PDF,
|
PDF,
|
||||||
MAGICK,
|
MAGICK,
|
||||||
OPENSLIDE,
|
OPENSLIDE,
|
||||||
@ -123,6 +124,9 @@ namespace sharp {
|
|||||||
bool IsPng(std::string const &str);
|
bool IsPng(std::string const &str);
|
||||||
bool IsWebp(std::string const &str);
|
bool IsWebp(std::string const &str);
|
||||||
bool IsTiff(std::string const &str);
|
bool IsTiff(std::string const &str);
|
||||||
|
bool IsHeic(std::string const &str);
|
||||||
|
bool IsHeif(std::string const &str);
|
||||||
|
bool IsAvif(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);
|
bool IsV(std::string const &str);
|
||||||
|
@ -77,6 +77,9 @@ class MetadataWorker : public Nan::AsyncWorker {
|
|||||||
if (image.get_typeof(VIPS_META_PAGE_HEIGHT) == G_TYPE_INT) {
|
if (image.get_typeof(VIPS_META_PAGE_HEIGHT) == G_TYPE_INT) {
|
||||||
baton->pageHeight = image.get_int(VIPS_META_PAGE_HEIGHT);
|
baton->pageHeight = image.get_int(VIPS_META_PAGE_HEIGHT);
|
||||||
}
|
}
|
||||||
|
if (image.get_typeof("heif-primary") == G_TYPE_INT) {
|
||||||
|
baton->pagePrimary = image.get_int("heif-primary");
|
||||||
|
}
|
||||||
baton->hasProfile = sharp::HasProfile(image);
|
baton->hasProfile = sharp::HasProfile(image);
|
||||||
// Derived attributes
|
// Derived attributes
|
||||||
baton->hasAlpha = sharp::HasAlpha(image);
|
baton->hasAlpha = sharp::HasAlpha(image);
|
||||||
@ -158,6 +161,9 @@ class MetadataWorker : public Nan::AsyncWorker {
|
|||||||
if (baton->pageHeight > 0) {
|
if (baton->pageHeight > 0) {
|
||||||
Set(info, New("pageHeight").ToLocalChecked(), New<v8::Uint32>(baton->pageHeight));
|
Set(info, New("pageHeight").ToLocalChecked(), New<v8::Uint32>(baton->pageHeight));
|
||||||
}
|
}
|
||||||
|
if (baton->pagePrimary > -1) {
|
||||||
|
Set(info, New("pagePrimary").ToLocalChecked(), New<v8::Uint32>(baton->pagePrimary));
|
||||||
|
}
|
||||||
Set(info, New("hasProfile").ToLocalChecked(), New<v8::Boolean>(baton->hasProfile));
|
Set(info, New("hasProfile").ToLocalChecked(), New<v8::Boolean>(baton->hasProfile));
|
||||||
Set(info, New("hasAlpha").ToLocalChecked(), New<v8::Boolean>(baton->hasAlpha));
|
Set(info, New("hasAlpha").ToLocalChecked(), New<v8::Boolean>(baton->hasAlpha));
|
||||||
if (baton->orientation > 0) {
|
if (baton->orientation > 0) {
|
||||||
|
@ -36,6 +36,7 @@ struct MetadataBaton {
|
|||||||
int paletteBitDepth;
|
int paletteBitDepth;
|
||||||
int pages;
|
int pages;
|
||||||
int pageHeight;
|
int pageHeight;
|
||||||
|
int pagePrimary;
|
||||||
bool hasProfile;
|
bool hasProfile;
|
||||||
bool hasAlpha;
|
bool hasAlpha;
|
||||||
int orientation;
|
int orientation;
|
||||||
@ -59,6 +60,7 @@ struct MetadataBaton {
|
|||||||
paletteBitDepth(0),
|
paletteBitDepth(0),
|
||||||
pages(0),
|
pages(0),
|
||||||
pageHeight(0),
|
pageHeight(0),
|
||||||
|
pagePrimary(-1),
|
||||||
hasProfile(false),
|
hasProfile(false),
|
||||||
hasAlpha(false),
|
hasAlpha(false),
|
||||||
orientation(0),
|
orientation(0),
|
||||||
|
@ -793,6 +793,18 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
vips_area_unref(area);
|
vips_area_unref(area);
|
||||||
baton->formatOut = "tiff";
|
baton->formatOut = "tiff";
|
||||||
baton->channels = std::min(baton->channels, 3);
|
baton->channels = std::min(baton->channels, 3);
|
||||||
|
} else if (baton->formatOut == "heif" || (baton->formatOut == "input" && inputImageType == ImageType::HEIF)) {
|
||||||
|
// Write HEIF to buffer
|
||||||
|
VipsArea *area = VIPS_AREA(image.heifsave_buffer(VImage::option()
|
||||||
|
->set("strip", !baton->withMetadata)
|
||||||
|
->set("compression", baton->heifCompression)
|
||||||
|
->set("Q", baton->heifQuality)
|
||||||
|
->set("lossless", baton->heifLossless)));
|
||||||
|
baton->bufferOut = static_cast<char*>(area->data);
|
||||||
|
baton->bufferOutLength = area->length;
|
||||||
|
area->free_fn = nullptr;
|
||||||
|
vips_area_unref(area);
|
||||||
|
baton->formatOut = "heif";
|
||||||
} else if (baton->formatOut == "raw" || (baton->formatOut == "input" && inputImageType == ImageType::RAW)) {
|
} else if (baton->formatOut == "raw" || (baton->formatOut == "input" && inputImageType == ImageType::RAW)) {
|
||||||
// Write raw, uncompressed image data to buffer
|
// Write raw, uncompressed image data to buffer
|
||||||
if (baton->greyscale || image.interpretation() == VIPS_INTERPRETATION_B_W) {
|
if (baton->greyscale || image.interpretation() == VIPS_INTERPRETATION_B_W) {
|
||||||
@ -827,6 +839,7 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
bool const isPng = sharp::IsPng(baton->fileOut);
|
bool const isPng = sharp::IsPng(baton->fileOut);
|
||||||
bool const isWebp = sharp::IsWebp(baton->fileOut);
|
bool const isWebp = sharp::IsWebp(baton->fileOut);
|
||||||
bool const isTiff = sharp::IsTiff(baton->fileOut);
|
bool const isTiff = sharp::IsTiff(baton->fileOut);
|
||||||
|
bool const isHeif = sharp::IsHeif(baton->fileOut);
|
||||||
bool const isDz = sharp::IsDz(baton->fileOut);
|
bool const isDz = sharp::IsDz(baton->fileOut);
|
||||||
bool const isDzZip = sharp::IsDzZip(baton->fileOut);
|
bool const isDzZip = sharp::IsDzZip(baton->fileOut);
|
||||||
bool const isV = sharp::IsV(baton->fileOut);
|
bool const isV = sharp::IsV(baton->fileOut);
|
||||||
@ -893,6 +906,20 @@ class PipelineWorker : public Nan::AsyncWorker {
|
|||||||
->set("yres", baton->tiffYres));
|
->set("yres", baton->tiffYres));
|
||||||
baton->formatOut = "tiff";
|
baton->formatOut = "tiff";
|
||||||
baton->channels = std::min(baton->channels, 3);
|
baton->channels = std::min(baton->channels, 3);
|
||||||
|
} else if (baton->formatOut == "heif" || (mightMatchInput && isHeif) ||
|
||||||
|
(willMatchInput && inputImageType == ImageType::HEIF)) {
|
||||||
|
// Write HEIF to file
|
||||||
|
#ifdef VIPS_TYPE_FOREIGN_HEIF_COMPRESSION
|
||||||
|
if (sharp::IsAvif(baton->fileOut)) {
|
||||||
|
baton->heifCompression = VIPS_FOREIGN_HEIF_COMPRESSION_AV1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
image.heifsave(const_cast<char*>(baton->fileOut.data()), VImage::option()
|
||||||
|
->set("strip", !baton->withMetadata)
|
||||||
|
->set("Q", baton->heifQuality)
|
||||||
|
->set("compression", baton->heifCompression)
|
||||||
|
->set("lossless", baton->heifLossless));
|
||||||
|
baton->formatOut = "heif";
|
||||||
} else if (baton->formatOut == "dz" || isDz || isDzZip) {
|
} else if (baton->formatOut == "dz" || isDz || isDzZip) {
|
||||||
if (isDzZip) {
|
if (isDzZip) {
|
||||||
baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
|
baton->tileContainer = VIPS_FOREIGN_DZ_CONTAINER_ZIP;
|
||||||
@ -1332,7 +1359,13 @@ NAN_METHOD(pipeline) {
|
|||||||
baton->tiffPredictor = static_cast<VipsForeignTiffPredictor>(
|
baton->tiffPredictor = static_cast<VipsForeignTiffPredictor>(
|
||||||
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_TIFF_PREDICTOR,
|
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_TIFF_PREDICTOR,
|
||||||
AttrAsStr(options, "tiffPredictor").data()));
|
AttrAsStr(options, "tiffPredictor").data()));
|
||||||
|
baton->heifQuality = AttrTo<uint32_t>(options, "heifQuality");
|
||||||
|
baton->heifLossless = AttrTo<bool>(options, "heifLossless");
|
||||||
|
#ifdef VIPS_TYPE_FOREIGN_HEIF_COMPRESSION
|
||||||
|
baton->heifCompression = static_cast<VipsForeignHeifCompression>(
|
||||||
|
vips_enum_from_nick(nullptr, VIPS_TYPE_FOREIGN_HEIF_COMPRESSION,
|
||||||
|
AttrAsStr(options, "heifCompression").data()));
|
||||||
|
#endif
|
||||||
// Tile output
|
// Tile output
|
||||||
baton->tileSize = AttrTo<uint32_t>(options, "tileSize");
|
baton->tileSize = AttrTo<uint32_t>(options, "tileSize");
|
||||||
baton->tileOverlap = AttrTo<uint32_t>(options, "tileOverlap");
|
baton->tileOverlap = AttrTo<uint32_t>(options, "tileOverlap");
|
||||||
|
@ -148,6 +148,9 @@ struct PipelineBaton {
|
|||||||
int tiffTileWidth;
|
int tiffTileWidth;
|
||||||
double tiffXres;
|
double tiffXres;
|
||||||
double tiffYres;
|
double tiffYres;
|
||||||
|
int heifQuality;
|
||||||
|
int heifCompression; // TODO(libvips 8.9.0): VipsForeignHeifCompression
|
||||||
|
bool heifLossless;
|
||||||
std::string err;
|
std::string err;
|
||||||
bool withMetadata;
|
bool withMetadata;
|
||||||
int withMetadataOrientation;
|
int withMetadataOrientation;
|
||||||
@ -247,6 +250,9 @@ struct PipelineBaton {
|
|||||||
tiffTileWidth(256),
|
tiffTileWidth(256),
|
||||||
tiffXres(1.0),
|
tiffXres(1.0),
|
||||||
tiffYres(1.0),
|
tiffYres(1.0),
|
||||||
|
heifQuality(80),
|
||||||
|
heifCompression(1), // TODO(libvips 8.9.0): VIPS_FOREIGN_HEIF_COMPRESSION_HEVC
|
||||||
|
heifLossless(false),
|
||||||
withMetadata(false),
|
withMetadata(false),
|
||||||
withMetadataOrientation(-1),
|
withMetadataOrientation(-1),
|
||||||
convKernelWidth(0),
|
convKernelWidth(0),
|
||||||
|
@ -151,7 +151,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", "v"
|
"jpeg", "png", "webp", "tiff", "magick", "openslide", "dz", "ppm", "fits", "gif", "svg", "heif", "pdf", "v"
|
||||||
}) {
|
}) {
|
||||||
// Input
|
// Input
|
||||||
Local<Boolean> hasInputFile =
|
Local<Boolean> hasInputFile =
|
||||||
|
79
test/unit/heif.js
Normal file
79
test/unit/heif.js
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
const sharp = require('../../');
|
||||||
|
|
||||||
|
const formatHeifOutputBuffer = sharp.format.heif.output.buffer;
|
||||||
|
|
||||||
|
describe('HEIF (experimental)', () => {
|
||||||
|
describe('Stubbed without support for HEIF', () => {
|
||||||
|
before(() => {
|
||||||
|
sharp.format.heif.output.buffer = false;
|
||||||
|
});
|
||||||
|
after(() => {
|
||||||
|
sharp.format.heif.output.buffer = formatHeifOutputBuffer;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an error', () => {
|
||||||
|
assert.throws(() => {
|
||||||
|
sharp().heif();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Stubbed with support for HEIF', () => {
|
||||||
|
before(() => {
|
||||||
|
sharp.format.heif.output.buffer = true;
|
||||||
|
});
|
||||||
|
after(() => {
|
||||||
|
sharp.format.heif.output.buffer = formatHeifOutputBuffer;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('called without options does not throw an error', () => {
|
||||||
|
assert.doesNotThrow(() => {
|
||||||
|
sharp().heif();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('valid quality does not throw an error', () => {
|
||||||
|
assert.doesNotThrow(() => {
|
||||||
|
sharp().heif({ quality: 50 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('invalid quality should throw an error', () => {
|
||||||
|
assert.throws(() => {
|
||||||
|
sharp().heif({ quality: 101 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('non-numeric quality should throw an error', () => {
|
||||||
|
assert.throws(() => {
|
||||||
|
sharp().heif({ quality: 'fail' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('valid lossless does not throw an error', () => {
|
||||||
|
assert.doesNotThrow(() => {
|
||||||
|
sharp().heif({ lossless: true });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('non-boolean lossless should throw an error', () => {
|
||||||
|
assert.throws(() => {
|
||||||
|
sharp().heif({ lossless: 'fail' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('valid compression does not throw an error', () => {
|
||||||
|
assert.doesNotThrow(() => {
|
||||||
|
sharp().heif({ compression: 'avc' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('unknown compression should throw an error', () => {
|
||||||
|
assert.throws(() => {
|
||||||
|
sharp().heif({ compression: 'fail' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('invalid compression should throw an error', () => {
|
||||||
|
assert.throws(() => {
|
||||||
|
sharp().heif({ compression: 1 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user