From f8cf25ca565ba96ea4e84b711d8c1460c1ec69d7 Mon Sep 17 00:00:00 2001 From: Lovell Fuller Date: Thu, 5 Oct 2023 14:05:36 +0100 Subject: [PATCH] Ensure correct interp of 16-bit raw input #3808 --- docs/changelog.md | 3 +++ src/common.cc | 5 +++-- test/unit/raw.js | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 6606c16a..6bbd42c5 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -13,6 +13,9 @@ Requires libvips v8.14.5 * Make `compression` option of `heif` mandatory to help reduce HEIF vs HEIC confusion. [#3740](https://github.com/lovell/sharp/issues/3740) +* Ensure correct interpretation of 16-bit raw input. + [#3808](https://github.com/lovell/sharp/issues/3808) + ## v0.32 - *flow* Requires libvips v8.14.5 diff --git a/src/common.cc b/src/common.cc index 9e2eacde..3b6b2827 100644 --- a/src/common.cc +++ b/src/common.cc @@ -363,12 +363,13 @@ namespace sharp { if (descriptor->isBuffer) { if (descriptor->rawChannels > 0) { // Raw, uncompressed pixel data + bool const is8bit = vips_band_format_is8bit(descriptor->rawDepth); image = VImage::new_from_memory(descriptor->buffer, descriptor->bufferLength, descriptor->rawWidth, descriptor->rawHeight, descriptor->rawChannels, descriptor->rawDepth); if (descriptor->rawChannels < 3) { - image.get_image()->Type = VIPS_INTERPRETATION_B_W; + image.get_image()->Type = is8bit ? VIPS_INTERPRETATION_B_W : VIPS_INTERPRETATION_GREY16; } else { - image.get_image()->Type = VIPS_INTERPRETATION_sRGB; + image.get_image()->Type = is8bit ? VIPS_INTERPRETATION_sRGB : VIPS_INTERPRETATION_RGB16; } if (descriptor->rawPremultiplied) { image = image.unpremultiply(); diff --git a/test/unit/raw.js b/test/unit/raw.js index 7976ccd4..7ad24012 100644 --- a/test/unit/raw.js +++ b/test/unit/raw.js @@ -284,4 +284,42 @@ describe('Raw pixel data', function () { ); } }); + + describe('16-bit roundtrip', () => { + it('grey', async () => { + const grey = 42000; + const png = await sharp( + Uint16Array.from([grey]), + { raw: { width: 1, height: 1, channels: 1 } } + ) + .toColourspace('grey16') + .png({ compressionLevel: 0 }) + .toBuffer(); + const raw = await sharp(png) + .toColourspace('grey16') + .raw({ depth: 'ushort' }) + .toBuffer(); + + assert.strictEqual(raw.readUint16LE(0), grey); + }); + + it('RGB', async () => { + const rgb = [10946, 28657, 46368]; + const png = await sharp( + Uint16Array.from(rgb), + { raw: { width: 1, height: 1, channels: 3 } } + ) + .toColourspace('rgb16') + .png({ compressionLevel: 0 }) + .toBuffer(); + const raw = await sharp(png) + .toColourspace('rgb16') + .raw({ depth: 'ushort' }) + .toBuffer(); + + assert.strictEqual(raw.readUint16LE(0), rgb[0]); + assert.strictEqual(raw.readUint16LE(2), rgb[1]); + assert.strictEqual(raw.readUint16LE(4), rgb[2]); + }); + }); });