Skip to content

Commit

Permalink
fix: support images that do not define samplesPerPixel
Browse files Browse the repository at this point in the history
Closes: #21
  • Loading branch information
targos committed Aug 7, 2020
1 parent c5a0fce commit 2c2587b
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 33 deletions.
Binary file added img/cells.tif
Binary file not shown.
91 changes: 76 additions & 15 deletions src/__tests__/decode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,95 @@ function readImage(file: string): Buffer {
return readFileSync(join(__dirname, '../../img', file));
}

const files = [
interface TiffFile {
name: string;
width: number;
height: number;
bitsPerSample: number;
components: number;
}

const files: TiffFile[] = [
// TODO: Unsupported LZW compression?
// 'color-1px.tif',
'color8.tif',
'color8-lzw.tif',
'color16.tif',
'color16-lzw.tif',
'grey8.tif',
'grey8-lzw.tif',
'grey16.tif',
'whiteIsZero.tif',
{
name: 'color8.tif',
width: 160,
height: 120,
bitsPerSample: 8,
components: 3,
},
{
name: 'color8-lzw.tif',
width: 160,
height: 120,
bitsPerSample: 8,
components: 3,
},
{
name: 'color16.tif',
width: 160,
height: 120,
bitsPerSample: 16,
components: 3,
},
{
name: 'color16-lzw.tif',
width: 160,
height: 120,
bitsPerSample: 16,
components: 3,
},
{ name: 'grey8.tif', width: 30, height: 90, bitsPerSample: 8, components: 1 },
{
name: 'grey8-lzw.tif',
width: 30,
height: 90,
bitsPerSample: 8,
components: 1,
},
{
name: 'grey16.tif',
width: 30,
height: 90,
bitsPerSample: 16,
components: 1,
},
{
name: 'whiteIsZero.tif',
width: 1248,
height: 1248,
bitsPerSample: 16,
components: 1,
},
{
name: 'cells.tif',
width: 2048,
height: 2048,
bitsPerSample: 16,
components: 1,
},
];
const cases = files.map((name) => [name, readImage(name)] as const);
const cases = files.map((file) => [file, readImage(file.name)] as const);

const stack = readImage('stack.tif');

test.each(cases)('should decode %s', (_, image) => {
test.each(cases)('should decode %s', (file, image) => {
const result = decode(image);
expect(result).toHaveLength(1);
const { data, samplesPerPixel, width, height } = result[0];
expect(data).toHaveLength(width * height * samplesPerPixel);
const { data, bitsPerSample, width, height, components } = result[0];
expect(width).toBe(file.width);
expect(height).toBe(file.height);
expect(components).toBe(file.components);
expect(bitsPerSample).toBe(file.bitsPerSample);
expect(data).toHaveLength(file.width * file.height * file.components);
});

test('should decode RGB', () => {
test('should decode RGB 8bit', () => {
const [result] = decode(readImage('color-5x5.tif'));
expect(result.width).toBe(5);
expect(result.height).toBe(5);
expect(result.bitsPerSample).toStrictEqual(Uint16Array.from([8, 8, 8]));
expect(result.bitsPerSample).toBe(8);
expect(result.components).toBe(3);
expect(result.data).toStrictEqual(
// prettier-ignore
Expand Down
19 changes: 3 additions & 16 deletions src/tiffDecoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ export default class TIFFDecoder extends IOBuffer {
this.applyPredictor(ifd);
if (ifd.type === 0) {
// WhiteIsZero: we invert the values
const bitDepth = validateBitDepth(ifd.bitsPerSample);
const bitDepth = ifd.bitsPerSample;
const maxValue = Math.pow(2, bitDepth) - 1;
for (let i = 0; i < ifd.data.length; i++) {
ifd.data[i] = maxValue - ifd.data[i];
Expand All @@ -192,7 +192,7 @@ export default class TIFFDecoder extends IOBuffer {
const width = ifd.width;
const height = ifd.height;

const bitDepth = validateBitDepth(ifd.bitsPerSample);
const bitDepth = ifd.bitsPerSample;
const sampleFormat = ifd.sampleFormat;
const size = width * height * ifd.samplesPerPixel;
const data = getDataArray(size, bitDepth, sampleFormat);
Expand Down Expand Up @@ -268,7 +268,7 @@ export default class TIFFDecoder extends IOBuffer {
}

private applyPredictor(ifd: TiffIfd): void {
const bitDepth = validateBitDepth(ifd.bitsPerSample);
const bitDepth = ifd.bitsPerSample;
switch (ifd.predictor) {
case 1: {
// No prediction scheme, nothing to do
Expand Down Expand Up @@ -358,16 +358,3 @@ function fillFloat32(
function unsupported(type: string, value: any): Error {
return new Error(`Unsupported ${type}: ${value}`);
}

function validateBitDepth(bitDepth: number[] | number): number {
if (typeof bitDepth !== 'number') {
const bitDepthArray = bitDepth;
bitDepth = bitDepthArray[0];
for (let i = 0; i < bitDepthArray.length; i++) {
if (bitDepthArray[i] !== bitDepth) {
throw unsupported('bit depth', bitDepthArray);
}
}
}
return bitDepth;
}
8 changes: 6 additions & 2 deletions src/tiffIfd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ export default class TiffIfd extends Ifd {
return this.get(257);
}
public get bitsPerSample(): number {
return this.get(258);
const data = this.get(258);
if (data && typeof data !== 'number') {
return data[0];
}
return data;
}
public get compression(): number {
return this.get(259) || 1;
Expand All @@ -71,7 +75,7 @@ export default class TiffIfd extends Ifd {
return this.get(274);
}
public get samplesPerPixel(): number {
return this.get(277);
return this.get(277) || 1;
}
public get rowsPerStrip(): number {
return this.get(278);
Expand Down

0 comments on commit 2c2587b

Please sign in to comment.