Skip to content

Commit d02e56f

Browse files
authored
fix(tiler-sharp): when resampling uint round numbers rather than truncate (#3392)
### Motivation When resampling `uint` it can end up resampling slightly high or low eg resampling `1` can result in `1.00..01` or `0.99999..` When setting a number on a Uint8Array floating point numbers are truncated, so `0.9999` will be rounded down to `0` rather than up to `1`. ### Modifications When resampling `uint` round numbers before assigning them. ### Verification Added unit test to validate rounding
1 parent f316042 commit d02e56f

File tree

2 files changed

+36
-2
lines changed

2 files changed

+36
-2
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import assert from 'node:assert';
2+
import { describe, it } from 'node:test';
3+
4+
import { CompositionTiff } from '@basemaps/tiler';
5+
6+
import { resizeBilinear } from '../pipeline.resize.js';
7+
8+
describe('resize-bilinear', () => {
9+
it('should round numbers when working with uint arrays', () => {
10+
const ret = resizeBilinear(
11+
{
12+
pixels: new Uint8Array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]),
13+
depth: 'uint8',
14+
width: 4,
15+
height: 4,
16+
channels: 1,
17+
},
18+
{ source: { width: 4, height: 4 } } as unknown as CompositionTiff,
19+
{ x: 0, y: 0, width: 1, height: 1 },
20+
{ width: 256, height: 256, scale: 123.123123123 },
21+
);
22+
23+
// All values should be rounded to 1 and not truncated down to 0
24+
assert.ok(ret.pixels.every((f) => f === 1));
25+
});
26+
});

packages/tiler-sharp/src/pipeline/pipeline.resize.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ function getOutputBuffer(source: DecompressedInterleaved, target: Size): Decompr
138138
}
139139
}
140140

141-
function resizeBilinear(
141+
export function resizeBilinear(
142142
data: DecompressedInterleaved,
143143
comp: CompositionTiff,
144144
source: BoundingBox,
@@ -151,6 +151,14 @@ function resizeBilinear(
151151
const maxHeight = Math.min(comp.source.height, data.height) - 2;
152152
const ret = getOutputBuffer(data, target);
153153
const outputBuffer = ret.pixels;
154+
155+
// should numbers be rounded when resampling, with some numbers like uint8 or uint32 numbers
156+
// will be truncated when being set in their typed buffers,
157+
//
158+
// for example: `0.9999` will end up as `0` in a Uint8Array
159+
// Only floats should be left as floating numbers
160+
const needsRounding = !data.depth.startsWith('float');
161+
154162
for (let y = 0; y < target.height; y++) {
155163
const sourceY = Math.min((y + 0.5) * invScale + source.y, maxHeight);
156164
const minY = Math.floor(sourceY);
@@ -193,7 +201,7 @@ function resizeBilinear(
193201

194202
const pixel = minXMinY * weightA + maxXMinY * weightB + minXMaxY * weightC + maxXMaxY * weightD;
195203

196-
outputBuffer[outPx] = pixel;
204+
outputBuffer[outPx] = needsRounding ? Math.round(pixel) : pixel;
197205
}
198206
}
199207

0 commit comments

Comments
 (0)