From a48f8fbb61b1c6a5aa11f8ed4bb85b2df3f8f0c2 Mon Sep 17 00:00:00 2001 From: Daiz Date: Sun, 11 Nov 2018 11:15:38 +0200 Subject: [PATCH] Allow separate parameters for gamma encoding and decoding (#1439) --- docs/api-operation.md | 1 + docs/changelog.md | 4 ++++ lib/constructor.js | 1 + lib/operation.js | 13 +++++++++-- src/pipeline.cc | 5 +++-- src/pipeline.h | 1 + .../expected/gamma-in-2.2-out-3.0.jpg | Bin 0 -> 1877 bytes test/unit/gamma.js | 21 +++++++++++++++++- 8 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 test/fixtures/expected/gamma-in-2.2-out-3.0.jpg diff --git a/docs/api-operation.md b/docs/api-operation.md index 5422a194..0fc3a8c6 100644 --- a/docs/api-operation.md +++ b/docs/api-operation.md @@ -134,6 +134,7 @@ when applying a gamma correction. ### Parameters - `gamma` **[Number][1]** value between 1.0 and 3.0. (optional, default `2.2`) +- `gammaOut` ** [Number][1]** value between 1.0 and 3.0. (optional, defaults to same as `gamma`) - Throws **[Error][5]** Invalid parameters diff --git a/docs/changelog.md b/docs/changelog.md index 5a54e0af..3f9b4aa4 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -14,6 +14,10 @@ Requires libvips v8.7.0. [#1438](https://github.com/lovell/sharp/pull/1438) [@Daiz](https://github.com/Daiz) +* Add support for different output gamma from what is used for scaling. + [#1439](https://github.com/lovell/sharp/pull/1439) + [@Daiz](https://github.com/Daiz) + #### v0.21.0 - 4th October 2018 * Deprecate the following resize-related functions: diff --git a/lib/constructor.js b/lib/constructor.js index 24a2eb69..130f3bab 100644 --- a/lib/constructor.js +++ b/lib/constructor.js @@ -136,6 +136,7 @@ const Sharp = function (input, options) { thresholdGrayscale: true, trimThreshold: 0, gamma: 0, + gammaOut: 0, greyscale: false, normalise: 0, booleanBufferIn: null, diff --git a/lib/operation.js b/lib/operation.js index 4eb86f5f..1f03b64d 100644 --- a/lib/operation.js +++ b/lib/operation.js @@ -189,12 +189,13 @@ function flatten (options) { * then increasing the encoding (brighten) post-resize at a factor of `gamma`. * This can improve the perceived brightness of a resized image in non-linear colour spaces. * JPEG and WebP input images will not take advantage of the shrink-on-load performance optimisation - * when applying a gamma correction. + * when applying a gamma correction. Supply a second argument to use a different output gamma value, otherwise the first value is used in both cases. * @param {Number} [gamma=2.2] value between 1.0 and 3.0. + * @param {Number} [gammaOut] value between 1.0 and 3.0. Defaults to same as gamma. * @returns {Sharp} * @throws {Error} Invalid parameters */ -function gamma (gamma) { +function gamma (gamma, gammaOut) { if (!is.defined(gamma)) { // Default gamma correction of 2.2 (sRGB) this.options.gamma = 2.2; @@ -203,6 +204,14 @@ function gamma (gamma) { } else { throw new Error('Invalid gamma correction (1.0 to 3.0) ' + gamma); } + if (!is.defined(gammaOut)) { + // Default gamma correction for output is same as input + this.options.gammaOut = this.options.gamma; + } else if (is.number(gammaOut) && is.inRange(gammaOut, 1, 3)) { + this.options.gammaOut = gammaOut; + } else { + throw new Error('Invalid post gamma correction (1.0 to 3.0) ' + gammaOut); + } return this; } diff --git a/src/pipeline.cc b/src/pipeline.cc index 97597b48..d230b1e4 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -612,8 +612,8 @@ class PipelineWorker : public Nan::AsyncWorker { baton->premultiplied = shouldPremultiplyAlpha; // Gamma decoding (brighten) - if (baton->gamma >= 1 && baton->gamma <= 3) { - image = sharp::Gamma(image, baton->gamma); + if (baton->gammaOut >= 1 && baton->gammaOut <= 3) { + image = sharp::Gamma(image, baton->gammaOut); } // Linear adjustment (a * in + b) @@ -1193,6 +1193,7 @@ NAN_METHOD(pipeline) { baton->thresholdGrayscale = AttrTo(options, "thresholdGrayscale"); baton->trimThreshold = AttrTo(options, "trimThreshold"); baton->gamma = AttrTo(options, "gamma"); + baton->gammaOut = AttrTo(options, "gammaOut"); baton->linearA = AttrTo(options, "linearA"); baton->linearB = AttrTo(options, "linearB"); baton->greyscale = AttrTo(options, "greyscale"); diff --git a/src/pipeline.h b/src/pipeline.h index cd763a86..fb18ab2b 100644 --- a/src/pipeline.h +++ b/src/pipeline.h @@ -87,6 +87,7 @@ struct PipelineBaton { double linearA; double linearB; double gamma; + double gammaOut; bool greyscale; bool normalise; bool useExifOrientation; diff --git a/test/fixtures/expected/gamma-in-2.2-out-3.0.jpg b/test/fixtures/expected/gamma-in-2.2-out-3.0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..50ddb9d16336cc6037f37a63fae8e60aed0f1d1e GIT binary patch literal 1877 zcmb7@dpy$%AIE>cU6~nUW|qPXvsP>thSW;5q{nh{*9$`uF>*bJT$&?98%H2$)p80w|0@PnlYI?)tdFJO)Rs*Z4g>}uIYLeJRuSY|c#iiD+`7ZlUJscK$`%Hk zMix^wn_r$HEn8grrS4D?M=SbVUPXi|Ijl{K_55MA=eAiBHQPzF3=`Z*c;o6I12WxG zmXwvWBX-VDi zgdC$|(HwFiq44f>`L&ZVga)$k03>)=oOO1z^iC9N3N4DQCJg7`%-T;nP~#sqHr^wI z2QG5=L>WJ)OnB;8paVwh&5BSfYF~_tQN`s*OQzIS$^WNH8gdl{yAbt%^>c z;?{*$_KZIJ-WC45+SI$w&VE0Cn2P-BwSMd)%{g`dcBLu(dph*QW2Ik3zPyG>^khY< zs74HR-nm&}v8c|jr1Z1BqO}a?qC^K#{^(nzGxJTkx5$siEwoTJ~MFf%&X-X9yCY81q8 z@nY_Jld-XEMam9)`coKu=*-Vw($=Uj6J#Md-M&utegKPKK zcCOGp>pREH%|m5Zi|JI|%)2pxQ)&Es0xhEL=m$rMAX+iN)a#<%L@?>0C*wo`?AP*0)qJ0k$$e7QSC2=6 zwT}QcY{^B&B^Lhsj!MfLpGnsTnH3f$*?ezV%Py*bh9J#{V1Ey4=ol!~0;WK`N`>xK`g>(W?+* z&+FDkQu?U<(i=lNsvemIiuH>u`I>A}a;^w!NIlil_C zzDltydiZ!>zb8MzlMgvP{0lYxl`e13Y{10`&Uf(}iki{tq1~N`)l;WQu_gyR# z2fP~X8oss>QVM~d(HF)&7!n}%A8)o(bo+WX+;zHdF;{p-W!LwCfpR{!F};a$|mz6j&Rdvk@262eG)Xu8$y*oCn}%{xEY z98XbN#t<}%;viFEeamQtRov1v=ipKL^{PV5XfWpvc-rda~v%tcNl>Ktlf$#ma((<{_Hmf}p zH}5d1WY7HAg!N#6pNbFBc0};J6NdIY5v{>eZCTBKWpj`@)*GAxx@Dcub>b6n@flUC aX%9#j+AUWZ9Ty6|%#l;nx_#blj{FB1g%$Gv literal 0 HcmV?d00001 diff --git a/test/unit/gamma.js b/test/unit/gamma.js index b5c56e1b..da8feea9 100644 --- a/test/unit/gamma.js +++ b/test/unit/gamma.js @@ -44,6 +44,19 @@ describe('Gamma correction', function () { }); }); + it('input value of 2.2, output value of 3.0', function (done) { + sharp(fixtures.inputJpgWithGammaHoliness) + .resize(129, 111) + .gamma(2.2, 3.0) + .toBuffer(function (err, data, info) { + if (err) throw err; + assert.strictEqual('jpeg', info.format); + assert.strictEqual(129, info.width); + assert.strictEqual(111, info.height); + fixtures.assertSimilar(fixtures.expected('gamma-in-2.2-out-3.0.jpg'), data, { threshold: 6 }, done); + }); + }); + it('alpha transparency', function (done) { sharp(fixtures.inputPngOverlayLayer1) .resize(320) @@ -57,9 +70,15 @@ describe('Gamma correction', function () { }); }); - it('invalid value', function () { + it('invalid first parameter value', function () { assert.throws(function () { sharp(fixtures.inputJpgWithGammaHoliness).gamma(4); }); }); + + it('invalid second parameter value', function () { + assert.throws(function () { + sharp(fixtures.inputJpgWithGammaHoliness).gamma(2.2, 4); + }); + }); });