From 6b5f138498384f107f08378eaafd3d9b30678291 Mon Sep 17 00:00:00 2001 From: huang <13165998033@163.com> Date: Thu, 29 Oct 2026 17:37:57 +0800 Subject: [PATCH] Use an unsigned shift when reading 32-bit function samples The bit reservoir can have the high bit set for 32 bits-per-sample data, in which case the signed right shift sign-extends and yields a negative sample value. --- src/core/function.js | 4 +++- test/unit/function_spec.js | 24 ++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/core/function.js b/src/core/function.js index acca046bf41fc..d32a156556e5e 100644 --- a/src/core/function.js +++ b/src/core/function.js @@ -141,7 +141,9 @@ class PDFFunction { codeSize += 8; } codeSize -= bps; - array[i] = (codeBuf >> codeSize) * sampleMul; + // Use an unsigned shift, since `codeBuf` can have the high bit set for + // 32 bits-per-sample data. + array[i] = (codeBuf >>> codeSize) * sampleMul; codeBuf &= (1 << codeSize) - 1; } return array; diff --git a/test/unit/function_spec.js b/test/unit/function_spec.js index 7481f1259e3db..925d9c90e82aa 100644 --- a/test/unit/function_spec.js +++ b/test/unit/function_spec.js @@ -30,13 +30,19 @@ describe("function", function () { factory = null; }); - function createSampledStream({ domain, range, size, samples }) { + function createSampledStream({ + domain, + range, + size, + samples, + bitsPerSample = 8, + }) { const dict = new Dict(); dict.set("FunctionType", 0); dict.set("Domain", domain); dict.set("Range", range); dict.set("Size", size); - dict.set("BitsPerSample", 8); + dict.set("BitsPerSample", bitsPerSample); const bytes = new Uint8Array(samples); return new Stream(bytes, 0, bytes.length, dict); } @@ -129,6 +135,20 @@ describe("function", function () { expect(evaluate(fn, ...new Array(numInputs).fill(0.5))).toEqual([0.2]); }); + + it("must interpret 32-bit samples as unsigned", function () { + const fn = createSampledFunction({ + domain: [0, 1], + range: [0, 1], + size: [2], + samples: [0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff], + bitsPerSample: 32, + }); + + const sampleMul = 1 / (2 ** 32 - 1); + expect(evaluate(fn, 0)).toEqual([0x80000000 * sampleMul]); + expect(evaluate(fn, 1)).toEqual([1]); + }); }); describe("Array of functions", function () {