Add withGainMap to process HDR JPEGs with embedded gain map #4314

This commit is contained in:
Lovell Fuller
2025-12-19 15:04:19 +00:00
parent f6cdd36559
commit aaeded2b67
16 changed files with 197 additions and 2 deletions

BIN
test/fixtures/gain-map.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -70,6 +70,7 @@ module.exports = {
inputJpgRandom: getPath('random.jpg'), // convert -size 200x200 xc: +noise Random random.jpg
inputJpgThRandom: getPath('thRandom.jpg'), // convert random.jpg -channel G -threshold 5% -separate +channel -negate thRandom.jpg
inputJpgLossless: getPath('testimgl.jpg'), // Lossless JPEG from ftp://ftp.fu-berlin.de/unix/X11/graphics/ImageMagick/delegates/ljpeg-6b.tar.gz
inputJpgWithGainMap: getPath('gain-map.jpg'), // https://github.com/libvips/libvips/issues/3799
inputPng: getPath('50020484-00001.png'), // http://c.searspartsdirect.com/lis_png/PLDM/50020484-00001.png
inputPngGradients: getPath('gradients-rgb8.png'),

69
test/unit/gain-map.js Normal file
View File

@@ -0,0 +1,69 @@
/*!
Copyright 2013 Lovell Fuller and others.
SPDX-License-Identifier: Apache-2.0
*/
const { describe, it } = require('node:test');
const sharp = require('../../');
const fixtures = require('../fixtures');
describe('Gain maps', () => {
it('Metadata contains gainMap', async (t) => {
t.plan(4);
const { format, gainMap } = await sharp(
fixtures.inputJpgWithGainMap,
).metadata();
t.assert.strictEqual(format, 'jpeg');
t.assert.strictEqual(typeof gainMap, 'object');
t.assert.ok(Buffer.isBuffer(gainMap.image));
t.assert.strictEqual(gainMap.image.length, 31738);
});
it('Can be regenerated', async (t) => {
t.plan(4);
const data = await sharp(fixtures.inputJpgWithGainMap)
.withGainMap()
.toBuffer();
const metadata = await sharp(data).metadata();
t.assert.strictEqual(metadata.format, 'jpeg');
t.assert.strictEqual(typeof metadata.gainMap, 'object');
t.assert.ok(Buffer.isBuffer(metadata.gainMap.image));
const {
format,
width,
height,
channels,
depth,
space,
hasProfile,
chromaSubsampling,
} = await sharp(metadata.gainMap.image).metadata();
t.assert.deepEqual(
{
format,
width,
height,
channels,
depth,
space,
hasProfile,
chromaSubsampling,
},
{
format: 'jpeg',
width: 1920,
height: 1080,
channels: 1,
depth: 'uchar',
space: 'b-w',
hasProfile: true,
chromaSubsampling: '4:4:4',
},
);
});
});