diff --git a/src/pipeline.cc b/src/pipeline.cc index b640bddb7..bcc577081 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -298,10 +298,13 @@ class PipelineWorker : public Napi::AsyncWorker { if (baton->input->autoOrient) { image = sharp::RemoveExifOrientation(image); } + VImage gainMapSource; VImage gainMap; int gainMapScaleFactor = 1; if (sharp::HasGainMap(image)) { if (baton->keepGainMap) { + // Keep source-owned gain-map bytes alive for lazy decoding. + gainMapSource = image; gainMap = image.gainmap(); if (image.get_typeof("gainmap-scale-factor") == G_TYPE_INT) { gainMapScaleFactor = image.get_int("gainmap-scale-factor"); diff --git a/test/unit/gain-map.js b/test/unit/gain-map.js index 17751522b..9a393616d 100644 --- a/test/unit/gain-map.js +++ b/test/unit/gain-map.js @@ -10,6 +10,31 @@ const sharp = require('../../'); const fixtures = require('../fixtures'); suite('Gain maps', () => { + test('Can rotate while keeping gain map with cache disabled', async (t) => { + const previousCache = sharp.cache(); + sharp.cache(false); + try { + const data = await sharp(fixtures.inputJpgWithGainMap) + .keepGainMap() + .rotate(90) + .toBuffer(); + const metadata = await sharp(data).metadata(); + t.assert.deepStrictEqual([metadata.width, metadata.height], [2160, 3840]); + t.assert.ok(metadata.gainMap); + const { data: gainMapPixels, info: gainMapInfo } = await sharp(metadata.gainMap.image) + .raw() + .toBuffer({ resolveWithObject: true }); + t.assert.ok(gainMapPixels.length > 0); + t.assert.deepStrictEqual([gainMapInfo.width, gainMapInfo.height], [540, 960]); + } finally { + sharp.cache({ + memory: previousCache.memory.max, + files: previousCache.files.max, + items: previousCache.items.max + }); + } + }); + test('Metadata contains gainMap', async (t) => { t.plan(4);