Skip to content

Commit

Permalink
Add bitdepth option to heif output (#4036)
Browse files Browse the repository at this point in the history
Prebuilt binaries support only AVIF with a bitdepth of 8
  • Loading branch information
mertalev committed Mar 21, 2024
1 parent dc07fd4 commit 3c26080
Show file tree
Hide file tree
Showing 7 changed files with 35 additions and 2 deletions.
1 change: 1 addition & 0 deletions lib/constructor.js
Expand Up @@ -325,6 +325,7 @@ const Sharp = function (input, options) {
heifCompression: 'av1',
heifEffort: 4,
heifChromaSubsampling: '4:4:4',
heifBitdepth: 8,
jxlDistance: 1,
jxlDecodingTier: 0,
jxlEffort: 7,
Expand Down
4 changes: 4 additions & 0 deletions lib/index.d.ts
Expand Up @@ -1244,6 +1244,8 @@ declare namespace sharp {
effort?: number | undefined;
/** set to '4:2:0' to use chroma subsampling, requires libvips v8.11.0 (optional, default '4:4:4') */
chromaSubsampling?: string | undefined;
/** Set bitdepth to 8, 10 or 12 bit (optional, default 8) */
bitdepth?: 8 | 10 | 12 | undefined;
}

interface HeifOptions extends OutputOptions {
Expand All @@ -1257,6 +1259,8 @@ declare namespace sharp {
effort?: number | undefined;
/** set to '4:2:0' to use chroma subsampling (optional, default '4:4:4') */
chromaSubsampling?: string | undefined;
/** Set bitdepth to 8, 10 or 12 bit (optional, default 8) */
bitdepth?: 8 | 10 | 12 | undefined;
}

interface GifOptions extends OutputOptions, AnimationOptions {
Expand Down
9 changes: 9 additions & 0 deletions lib/output.js
Expand Up @@ -1029,6 +1029,7 @@ function tiff (options) {
* @param {boolean} [options.lossless=false] - use lossless compression
* @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 9 (slowest)
* @param {string} [options.chromaSubsampling='4:4:4'] - set to '4:2:0' to use chroma subsampling
* @param {number} [options.bitdepth=8] - set bitdepth to 8, 10 or 12 bit
* @returns {Sharp}
* @throws {Error} Invalid options
*/
Expand All @@ -1055,6 +1056,7 @@ function avif (options) {
* @param {boolean} [options.lossless=false] - use lossless compression
* @param {number} [options.effort=4] - CPU effort, between 0 (fastest) and 9 (slowest)
* @param {string} [options.chromaSubsampling='4:4:4'] - set to '4:2:0' to use chroma subsampling
* @param {number} [options.bitdepth=8] - set bitdepth to 8, 10 or 12 bit
* @returns {Sharp}
* @throws {Error} Invalid options
*/
Expand Down Expand Up @@ -1093,6 +1095,13 @@ function heif (options) {
throw is.invalidParameterError('chromaSubsampling', 'one of: 4:2:0, 4:4:4', options.chromaSubsampling);
}
}
if (is.defined(options.bitdepth)) {
if (is.integer(options.bitdepth) && is.inArray(options.bitdepth, [8, 10, 12])) {
this.options.heifBitdepth = options.bitdepth;
} else {
throw is.invalidParameterError('bitdepth', '8, 10 or 12', options.bitdepth);
}
}
} else {
throw is.invalidParameterError('options', 'Object', options);
}
Expand Down
5 changes: 3 additions & 2 deletions src/pipeline.cc
Expand Up @@ -989,7 +989,7 @@ class PipelineWorker : public Napi::AsyncWorker {
->set("Q", baton->heifQuality)
->set("compression", baton->heifCompression)
->set("effort", baton->heifEffort)
->set("bitdepth", 8)
->set("bitdepth", baton->heifBitdepth)
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
->set("lossless", baton->heifLossless)));
Expand Down Expand Up @@ -1182,7 +1182,7 @@ class PipelineWorker : public Napi::AsyncWorker {
->set("Q", baton->heifQuality)
->set("compression", baton->heifCompression)
->set("effort", baton->heifEffort)
->set("bitdepth", 8)
->set("bitdepth", baton->heifBitdepth)
->set("subsample_mode", baton->heifChromaSubsampling == "4:4:4"
? VIPS_FOREIGN_SUBSAMPLE_OFF : VIPS_FOREIGN_SUBSAMPLE_ON)
->set("lossless", baton->heifLossless));
Expand Down Expand Up @@ -1696,6 +1696,7 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) {
options, "heifCompression", VIPS_TYPE_FOREIGN_HEIF_COMPRESSION);
baton->heifEffort = sharp::AttrAsUint32(options, "heifEffort");
baton->heifChromaSubsampling = sharp::AttrAsStr(options, "heifChromaSubsampling");
baton->heifBitdepth = sharp::AttrAsUint32(options, "heifBitdepth");
baton->jxlDistance = sharp::AttrAsDouble(options, "jxlDistance");
baton->jxlDecodingTier = sharp::AttrAsUint32(options, "jxlDecodingTier");
baton->jxlEffort = sharp::AttrAsUint32(options, "jxlEffort");
Expand Down
2 changes: 2 additions & 0 deletions src/pipeline.h
Expand Up @@ -181,6 +181,7 @@ struct PipelineBaton {
int heifEffort;
std::string heifChromaSubsampling;
bool heifLossless;
int heifBitdepth;
double jxlDistance;
int jxlDecodingTier;
int jxlEffort;
Expand Down Expand Up @@ -349,6 +350,7 @@ struct PipelineBaton {
heifEffort(4),
heifChromaSubsampling("4:4:4"),
heifLossless(false),
heifBitdepth(8),
jxlDistance(1.0),
jxlDecodingTier(0),
jxlEffort(7),
Expand Down
6 changes: 6 additions & 0 deletions test/unit/avif.js
Expand Up @@ -144,4 +144,10 @@ describe('AVIF', () => {
/Processed image is too large for the HEIF format/
)
);

it('Invalid bitdepth value throws error', async () => {
assert.rejects(
() => sharp().avif({ bitdepth: 11 }),
/Error: Expected 8, 10 or 12 for bitdepth but received 11 of type number/);
});
});
10 changes: 10 additions & 0 deletions test/unit/heif.js
Expand Up @@ -78,4 +78,14 @@ describe('HEIF', () => {
sharp().heif({ compression: 'av1', chromaSubsampling: '4:4:4' });
});
});
it('valid bitdepth value does not throw an error', () => {
assert.doesNotThrow(() => {
sharp().heif({ compression: 'av1', bitdepth: 12 });
});
});
it('invalid bitdepth value should throw an error', () => {
assert.throws(() => {
sharp().heif({ compression: 'av1', bitdepth: 11 });
}, /Error: Expected 8, 10 or 12 for bitdepth but received 11 of type number/);
});
});

0 comments on commit 3c26080

Please sign in to comment.