From e8ec11b15ca3400044699f43881ccd48058177e2 Mon Sep 17 00:00:00 2001 From: Victor Lamoine Date: Tue, 15 Oct 2024 09:10:47 +0200 Subject: [PATCH] Add AVIF file test --- lib/image/avif.js | 56 +++++++++++++++++++ test-data/expected/images/countryside.jpg | Bin 0 -> 27730 bytes test-data/input/images/countryside.avif | Bin 0 -> 18701 bytes test/integration/image-avif.test.js | 13 +++++ test/unit/avif.test.js | 65 ++++++++++++++++++++++ 5 files changed, 134 insertions(+) create mode 100644 lib/image/avif.js create mode 100644 test-data/expected/images/countryside.jpg create mode 100644 test-data/input/images/countryside.avif create mode 100644 test/integration/image-avif.test.js create mode 100644 test/unit/avif.test.js diff --git a/lib/image/avif.js b/lib/image/avif.js new file mode 100644 index 0000000..411db67 --- /dev/null +++ b/lib/image/avif.js @@ -0,0 +1,56 @@ +const childProcess = require('node:child_process') +const path = require('node:path') +const async = require('async') +const tmp = require('tmp') +const trace = require('debug')('thumbsup:trace') + +// IMPORTANT NOTE +// +// We rely on GraphicsMagick for all image processing, and ImageMagick 7 to convert AVIF to JPEG + +const SRGB_ICM_PATH = path.join(__dirname, 'sRGB.icm') +const processed = {} + +// This function is typically called several times, for the thumbnail, small version, large version... +// To avoid converting the image 3 times we re-use previously converted images +exports.convert = function (source, callback) { + if (!processed[source]) { + const tmpfile = tmp.fileSync({ postfix: '.jpg' }) + processed[source] = processFile(source, tmpfile.name) + } + processed[source].then((target) => callback(null, target)).catch(err => callback(err)) +} + +// Return a promise so multiple callers can subscribe to when it's finished +function processFile (source, target) { + return async.series([ + done => convertToJpeg(source, target, done), + done => copyColorProfile(source, target, done), + done => convertToSRGB(target, done) + ]).then(() => target) +} + +function convertToJpeg (source, target, done) { + // only process the first image, in case of burst shots + const args = ['convert', `${source}[0]`, target] + exec('magick', args, done) +} + +function copyColorProfile (source, target, done) { + const args = ['-overwrite_original', '-TagsFromFile', source, '-icc_profile', target] + exec('exiftool', args, done) +} + +function convertToSRGB (target, done) { + const args = ['mogrify', '-profile', SRGB_ICM_PATH, target] + exec('magick', args, done) +} + +function exec (command, args, done) { + trace(command + ' ' + args.map(a => `"${a}"`).join(' ')) + childProcess.execFile(command, args, done) +} + +// Optionally, we could remove the original profile +// It shouldn't matter since we're resizing the image afterwards +// exiftool -overwrite_original "-icc_profile:all=" photo.jpg diff --git a/test-data/expected/images/countryside.jpg b/test-data/expected/images/countryside.jpg new file mode 100644 index 0000000000000000000000000000000000000000..51b36ec8c6dd144d6ff9a54dfc3b763aa1c34528 GIT binary patch literal 27730 zcmbTdbyOV9*6`c7YaqehEqHLZ!QBb&?h*(Zbg;n*?t=x_gg|f!I!KT}kYE9V2mdDL zJnwhTUF*Gn+*`A%_phsV?Y-NoyQ^!SmYy~Md}RfY0)T*k04TwKz|#*T8TkN5I{*NI z*Z_0@0B`^R0U1Dm({DUD{=*ko0Dy;p_%DrWcpW~RapAHFo(U0<{-yu74H*K;zjzbQ z9#1HUt^YV-EaEtKp)9?t0@Ck|V3IE;Pf5rAc zk`l;`_;VF zKROd|ofHV-zhV;>76elNiy{BT2u1(4^YjFdq3AzH8R;MXdz2B&{;l)ZKGJ_Na@oJ* zANU*FKjkmrI;j7pLsRy5%m0)9x1;dCm9ca4ig0rS!2ie=xCtA0{(DA!f$#ADkCCTTzC_(8!nu?I--y_`5OiWNGc~p(8K* zFa96-ua*D1xLWwP?i}a;UjKg;|2M$2vGulwSKPz1jkSlD4}33Lz&Y68$Kx-r!kNh0 z!4kfk5Prj%!wViDJZ_4=<@Wz(c=P~~)xTNpFMH`~!vjV%gfor(f3eknv8#*kU%!9T z-z~+qb<@*{(vJ~I>5;r(1zE@ zz)2VIf@`?JOY8t!I0OGLJ}my_Ll$rcJb*yBo+DrnxAfO43t$bf!X+=j4bQxA()pK9 zZ@5=CxE2i{54gZ>Tf?=z;O*VuZLQ({XyEa>16%+HoK)d;HUQV(;|TWyPyabS-gbQd z>K*_DY;pM2vHJ9M&JMq7Qvl#0<>~3J^y%rL6n@Sv13)kMKkXyo$3Xnk)6-M;f6A0U z0YIZ5{2cB4PZqe_J9Uqr)5GV_{&yOaDKnr(S>n9T-8tLP8(_5D5^F2oRnI07`h=h)92LKmU`E zQIJp(5YYg140yFPK7a`KjEI7WjDm;?Aj7W~L?mPs0)UVfl?Uw^owOE_B{8pOI6A$I zb}C=-a|Wx%zTX%xydq>FX(eA~e!xwT!!P;&hXeR3{Emi#iUzNI1rG%geoI3}LPtP` z2LeAw5di`uWI|dVlxNafbe5h(yy2$)km)k9Nm>6uj_9MBI{KpSRwsa_|o-&mwhn2DH^ zjd65iZ#l`NKwb-LOe8&$6n1Jz$g}J?tO|M%+pAb&i-4cWY(#1GNjXUya^qD%lKqRW z6-QRKp9Q3^0?SgVhd%XWvD;|Y?Tb~EzaVA)&H#Rqyae)%lrc+Fb;?Lr1h060PRfC*FgKWoo!PCyog?UyUElR?PPtl9b zGnL?%ul)dfBh8%h*YS!IGfB-chgiACGM7Z>!S_@_F9=e}NfIhLLF~xcU0WMlWHVPD zBC+R&Se;Ci7p&`|Zb+J;iVwps26_Q3gr@yBlz z38}<#-bU8tc79-}8KYOWtu1qPb#Y_%j&?X{ti>?tgFv@>fxowe!Z)ma*5$4|y%8dX zqu#XweloB%X)zODcaCMI2siJFXYK0nbOQTEc`g$=Y9ffRnzQZ3r&Say{C02EjgU1< z#E>SJ=8KfNgl{Bc>~anS!heZWe#~34eDgzT>~2368iC7qvGm*8P6aKtMh>{zZ|=!~cA}it|$g+KQR}*Y;vzXr#Xf)M0-iYrI5Qa0TqzE< z{Y_5amy`m&)%;P)q!jJe1-`oiOMdN?8q#H7Ke>#H*>yP#XLh2QHX*Ou;b#UKqUH&j z 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 z%0D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 { + it('can process a single-image AVIF file', done => { + diff.image({ + input: 'images/countryside.avif', + expect: 'images/countryside.jpg', + options: { + height: 200 + } + }, done) + }) +}) diff --git a/test/unit/avif.test.js b/test/unit/avif.test.js new file mode 100644 index 0000000..2a7e796 --- /dev/null +++ b/test/unit/avif.test.js @@ -0,0 +1,65 @@ +const childProcess = require('node:child_process') +const should = require('should/as-function') +const async = require('async') +const sinon = require('sinon') +const avif = require('../../lib/image/avif') + +afterEach(() => { + sinon.restore() +}) + +describe('avif', () => { + it('calls gmagick and exiftool', done => { + sinon.stub(childProcess, 'execFile').callsFake(fakeExecFile) + avif.convert('input1.avif', err => { + should(err).eql(null) + should(childProcess.execFile.callCount).eql(3) + should(childProcess.execFile.getCall(0).args[0]).eql('magick') + should(childProcess.execFile.getCall(1).args[0]).eql('exiftool') + should(childProcess.execFile.getCall(2).args[0]).eql('magick') + done() + }) + }) + + it('stops at the first failing call', done => { + sinon.stub(childProcess, 'execFile').callsFake(fakeExecFileFail) + avif.convert('input2.avif', err => { + should(err.message).eql('FAIL') + should(childProcess.execFile.callCount).eql(1) + should(childProcess.execFile.getCall(0).args[0]).eql('magick') + done() + }) + }) + + it('only processes each file once', done => { + sinon.stub(childProcess, 'execFile').callsFake(fakeExecFile) + async.parallel([ + done => avif.convert('input3.avif', done), + done => avif.convert('input3.avif', done) + ]).then(res => { + should(childProcess.execFile.callCount).eql(3) + done() + }) + }) + + it('keeps track of files already processed', done => { + sinon.stub(childProcess, 'execFile').callsFake(fakeExecFile) + async.parallel([ + done => avif.convert('input4.avif', done), + done => avif.convert('input5.avif', done), + done => avif.convert('input6.avif', done), + done => avif.convert('input4.avif', done) + ]).then(res => { + should(childProcess.execFile.callCount).eql(3 * 3) + done() + }) + }) +}) + +function fakeExecFile (cmd, args, done) { + setTimeout(done, 50) +} + +function fakeExecFileFail (cmd, args, done) { + setTimeout(() => done(new Error('FAIL')), 50) +}