Skip to content

Commit

Permalink
fix: support WhiteIsZero
Browse files Browse the repository at this point in the history
Closes #14
  • Loading branch information
targos committed Sep 12, 2018
1 parent bd97443 commit bf7a4ca
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 28 deletions.
45 changes: 24 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
# tiff

[![NPM version][npm-image]][npm-url]
[![build status][travis-image]][travis-url]
[![npm download][download-image]][download-url]
[![NPM version][npm-image]][npm-url]
[![build status][travis-image]][travis-url]
[![npm download][download-image]][download-url]

TIFF image decoder written entirely in JavaScript.

## Installation

```
$ npm install tiff
```console
npm install tiff
```

## Compatibility

### Platform

This package is written using ES2015 features. It is natively compatible with recent versions of Google Chrome
and Node.js. You can transpile it with a tool like [babel](https://babeljs.io/) if you need to support more
JavaScript engines.
This package is written using ES2015 features. It is natively compatible with
recent web browsers and Node.js. You can transpile it with a tool like
[babel](https://babeljs.io/) if you need to support more JavaScript engines.

### [TIFF standard](./TIFF6.pdf)

The library can currently decode greyscale and RGB images (8, 16 or 32 bits). It does not support any compression algorithm yet.
The library can currently decode greyscale and RGB images (8, 16 or 32 bits).
It does not support any compression algorithm yet.

## API

Expand All @@ -36,31 +37,33 @@ Each decoded image is stored in an `IFD`.

##### IFD#data

The `data` property is a Typed Array containing the pixel data. It is a `Uint8Array` for 8bit images, a `Uint16Array` for 16bit images and a `Float32Array` for 32bit images.
The `data` property is a Typed Array containing the pixel data. It is a
`Uint8Array` for 8bit images, a `Uint16Array` for 16bit images and a
`Float32Array` for 32bit images.

##### Other properties of IFD

* `size` - number of pixels
* `width` - number of columns
* `height` - number of rows
* `bitsPerSample` - bit depth
* `xResolution`
* `yResolution`
* `resolutionUnit`
- `size` - number of pixels
- `width` - number of columns
- `height` - number of rows
- `bitsPerSample` - bit depth
- `xResolution`
- `yResolution`
- `resolutionUnit`

### tiff.pageCount(data)

Returns the number of IFDs (pages) in the file.

### tiff.isMultiPage(data)

Returns true if the file has 2 or more IFDs (pages) and false if it has 1. This is slightly more
efficient than calling `pageCount()` if all you need to know is whether the file has multiple
pages or not.
Returns true if the file has 2 or more IFDs (pages) and false if it has 1.
This is slightly more efficient than calling `pageCount()` if all you need to
know is whether the file has multiple pages or not.

## License

[MIT](./LICENSE)
[MIT](./LICENSE)

[npm-image]: https://img.shields.io/npm/v/tiff.svg?style=flat-square
[npm-url]: https://www.npmjs.com/package/tiff
Expand Down
17 changes: 14 additions & 3 deletions __tests__/decode.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,17 @@ import { join } from 'path';

import { decode } from '../src';

const files = ['grey8.tif', 'grey16.tif', 'color8.tif', 'color16.tif'];
const files = [
'grey8.tif',
'grey16.tif',
'color8.tif',
'color16.tif',
'whiteIsZero.tif'
];
// const files = ['color8c.tif'];//'grey8.tif', 'grey16.tif', 'color8.tif', 'color16.tif'];
const contents = files.map((file) => readFileSync(join(__dirname, 'img', file)));
const contents = files.map((file) =>
readFileSync(join(__dirname, 'img', file))
);

describe('TIFF decoder', () => {
it('should decode', () => {
Expand All @@ -24,7 +32,10 @@ describe('TIFF decoder', () => {
expect(result[0].data).toBeNull();
});
it('should read exif data', () => {
const result = decode(contents[0], { onlyFirst: true, ignoreImageData: true });
const result = decode(contents[0], {
onlyFirst: true,
ignoreImageData: true
});
expect(result.exif.map).toEqual({
ColorSpace: 65535,
PixelXDimension: 30,
Expand Down
Binary file added __tests__/img/whiteIsZero.tif
Binary file not shown.
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"eslint-config-cheminfo": "^1.18.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-jest": "^21.22.0",
"esm": "^3.0.82",
"jest": "^23.6.0",
"npm-run-all": "^4.1.3",
"rollup": "^0.65.2"
Expand Down
31 changes: 27 additions & 4 deletions src/tiffDecoder.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export default class TIFFDecoder extends IOBuffer {
const value = this.readUint16();
if (value === 0x4949) {
this.setLittleEndian();
} else if (value === 0x4D4D) {
} else if (value === 0x4d4d) {
this.setBigEndian();
} else {
throw new Error(`invalid byte order: 0x${value.toString(16)}`);
Expand Down Expand Up @@ -145,13 +145,22 @@ export default class TIFFDecoder extends IOBuffer {
throw unsupported('orientation', orientation);
}
switch (ifd.type) {
case 0: // WhiteIsZero
case 1: // BlackIsZero
case 2: // RGB
this.readStripData(ifd);
break;
default:
throw unsupported('image type', ifd.type);
}
if (ifd.type === 0) {
// WhiteIsZero: we invert the values
const bitDepth = validateBitDepth(ifd.bitsPerSample);
const maxValue = Math.pow(2, bitDepth) - 1;
for (var i = 0; i < ifd.data.length; i++) {
ifd.data[i] = maxValue - ifd.data[i];
}
}
}

readStripData(ifd) {
Expand All @@ -172,15 +181,26 @@ export default class TIFFDecoder extends IOBuffer {
var remainingPixels = size;
var pixel = 0;
for (var i = 0; i < stripOffsets.length; i++) {
var stripData = new DataView(this.buffer, stripOffsets[i], stripByteCounts[i]);
var stripData = new DataView(
this.buffer,
stripOffsets[i],
stripByteCounts[i]
);

// Last strip can be smaller
var length = remainingPixels > maxPixels ? maxPixels : remainingPixels;
remainingPixels -= length;

switch (compression) {
case 1: // No compression
pixel = this.fillUncompressed(bitDepth, sampleFormat, data, stripData, pixel, length);
pixel = this.fillUncompressed(
bitDepth,
sampleFormat,
data,
stripData,
pixel,
length
);
break;
case 5: // LZW
throw unsupported('lzw');
Expand Down Expand Up @@ -216,7 +236,10 @@ function getDataArray(size, channels, bitDepth, sampleFormat) {
} else if (bitDepth === 32 && sampleFormat === 3) {
return new Float32Array(size * channels);
} else {
throw unsupported('bit depth / sample format', `${bitDepth} / ${sampleFormat}`);
throw unsupported(
'bit depth / sample format',
`${bitDepth} / ${sampleFormat}`
);
}
}

Expand Down

0 comments on commit bf7a4ca

Please sign in to comment.