geotiff 3.0.4-beta.1 → 3.0.4-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "geotiff",
3
- "version": "3.0.4-beta.1",
3
+ "version": "3.0.4-beta.2",
4
4
  "description": "GeoTIFF image decoding in JavaScript",
5
5
  "repository": {
6
6
  "type": "git",
@@ -26,6 +26,7 @@
26
26
  },
27
27
  "types": "dist-module/geotiff.d.ts",
28
28
  "files": [
29
+ "src",
29
30
  "dist-module",
30
31
  "dist-node",
31
32
  "dist-browser"
@@ -0,0 +1,50 @@
1
+ import { applyPredictor } from '../predictor.js';
2
+
3
+ /**
4
+ * @typedef {Object} BaseDecoderParameters
5
+ * @property {number} tileWidth
6
+ * @property {number} tileHeight
7
+ * @property {number} predictor
8
+ * @property {number|number[]|import('../geotiff.js').TypedArray} bitsPerSample
9
+ * @property {number} planarConfiguration
10
+ * @property {number} [samplesPerPixel]
11
+ */
12
+
13
+ export default class BaseDecoder {
14
+ /**
15
+ * @param {BaseDecoderParameters} parameters
16
+ */
17
+ constructor(parameters) {
18
+ this.parameters = parameters;
19
+ }
20
+
21
+ /**
22
+ * @abstract
23
+ * @param {ArrayBufferLike} _buffer
24
+ * @returns {Promise<ArrayBufferLike>|ArrayBufferLike}
25
+ */
26
+ decodeBlock(_buffer) {
27
+ throw new Error('decodeBlock not implemented');
28
+ }
29
+
30
+ /**
31
+ * @param {ArrayBufferLike} buffer
32
+ * @returns {Promise<ArrayBufferLike>}
33
+ */
34
+ async decode(buffer) {
35
+ const decoded = await this.decodeBlock(buffer);
36
+
37
+ const {
38
+ tileWidth, tileHeight, predictor, bitsPerSample, planarConfiguration,
39
+ } = this.parameters;
40
+ if (predictor !== 1) {
41
+ const isBitsPerSampleArray = Array.isArray(bitsPerSample) || ArrayBuffer.isView(bitsPerSample);
42
+ const adaptedBitsPerSample = isBitsPerSampleArray ? Array.from(bitsPerSample) : [bitsPerSample];
43
+ return applyPredictor(
44
+ decoded, predictor, tileWidth, tileHeight, adaptedBitsPerSample,
45
+ planarConfiguration,
46
+ );
47
+ }
48
+ return decoded;
49
+ }
50
+ }
@@ -0,0 +1,9 @@
1
+ import { inflate } from 'pako';
2
+ import BaseDecoder from './basedecoder.js';
3
+
4
+ export default class DeflateDecoder extends BaseDecoder {
5
+ /** @param {ArrayBuffer} buffer */
6
+ decodeBlock(buffer) {
7
+ return inflate(new Uint8Array(buffer)).buffer;
8
+ }
9
+ }
@@ -0,0 +1,185 @@
1
+ /** @import BaseDecoder, {BaseDecoderParameters} from "./basedecoder.js" */
2
+
3
+ /**
4
+ * @typedef {Object} RegistryEntry
5
+ * @property {function():Promise<typeof BaseDecoder>} importFn
6
+ * @property {function(import("../imagefiledirectory").ImageFileDirectory):Promise<BaseDecoderParameters>} decoderParameterFn
7
+ * @property {boolean} preferWorker
8
+ */
9
+
10
+ /** @type {Map<number | undefined, RegistryEntry>} */
11
+ const registry = new Map();
12
+
13
+ /**
14
+ * Default decoder parameter retrieval function
15
+ * @param {import("../imagefiledirectory").ImageFileDirectory} fileDirectory
16
+ * @returns {Promise<BaseDecoderParameters>}
17
+ */
18
+ async function defaultDecoderParameterFn(fileDirectory) {
19
+ const isTiled = !fileDirectory.hasTag('StripOffsets');
20
+ return /** @type {BaseDecoderParameters} */ ({
21
+ tileWidth: isTiled
22
+ ? await fileDirectory.loadValue('TileWidth')
23
+ : await fileDirectory.loadValue('ImageWidth'),
24
+ tileHeight: isTiled
25
+ ? await fileDirectory.loadValue('TileLength')
26
+ : (
27
+ await fileDirectory.loadValue('RowsPerStrip')
28
+ || await fileDirectory.loadValue('ImageLength')
29
+ ),
30
+ planarConfiguration: await fileDirectory.loadValue('PlanarConfiguration'),
31
+ bitsPerSample: await fileDirectory.loadValue('BitsPerSample'),
32
+ predictor: await fileDirectory.loadValue('Predictor') || 1,
33
+ });
34
+ }
35
+
36
+ /**
37
+ * Register a decoder for a specific compression method or a range of compressions
38
+ * @param {(number|undefined|(number|undefined)[])} cases ids of the compression methods to register for
39
+ * @param {function():Promise<typeof BaseDecoder>} importFn the function to import the decoder
40
+ * @param {function(import("../imagefiledirectory").ImageFileDirectory):Promise<BaseDecoderParameters>} decoderParameterFn
41
+ * @param {boolean} preferWorker_ Whether to prefer running the decoder in a worker
42
+ */
43
+ export function addDecoder(cases, importFn, decoderParameterFn = defaultDecoderParameterFn, preferWorker_ = true) {
44
+ if (!Array.isArray(cases)) {
45
+ cases = [cases]; // eslint-disable-line no-param-reassign
46
+ }
47
+ cases.forEach((c) => {
48
+ registry.set(c, { importFn, decoderParameterFn, preferWorker: preferWorker_ });
49
+ });
50
+ }
51
+
52
+ /**
53
+ * Get the required decoder parameters for a specific compression method
54
+ * @param {number|undefined} compression
55
+ * @param {import('../imagefiledirectory.js').ImageFileDirectory} fileDirectory
56
+ */
57
+ export async function getDecoderParameters(compression, fileDirectory) {
58
+ if (!registry.has(compression)) {
59
+ throw new Error(`Unknown compression method identifier: ${compression}`);
60
+ }
61
+ const { decoderParameterFn } = /** @type {RegistryEntry} */ (registry.get(compression));
62
+ return decoderParameterFn(fileDirectory);
63
+ }
64
+
65
+ /**
66
+ * Get a decoder for a specific compression and parameters
67
+ * @param {number} compression the compression method identifier
68
+ * @param {BaseDecoderParameters} decoderParameters the parameters for the decoder
69
+ * @returns {Promise<import('./basedecoder.js').default>}
70
+ */
71
+ export async function getDecoder(compression, decoderParameters) {
72
+ if (!registry.has(compression)) {
73
+ throw new Error(`Unknown compression method identifier: ${compression}`);
74
+ }
75
+ const { importFn } = /** @type {RegistryEntry} */ (registry.get(compression));
76
+ const Decoder = await importFn();
77
+ return new Decoder(decoderParameters);
78
+ }
79
+
80
+ /**
81
+ * Whether to prefer running the decoder in a worker
82
+ * @param {number|undefined} compression the compression method identifier
83
+ * @returns {boolean}
84
+ */
85
+ export function preferWorker(compression) {
86
+ if (!registry.has(compression)) {
87
+ throw new Error(`Unknown compression method identifier: ${compression}`);
88
+ }
89
+ return /** @type {RegistryEntry} */ (registry.get(compression)).preferWorker;
90
+ }
91
+
92
+ const defaultDecoderDefinitions = [
93
+ // No compression
94
+ {
95
+ cases: [undefined, 1],
96
+ importFn: () => import('./raw.js').then((m) => m.default),
97
+ preferWorker: false,
98
+ },
99
+ // LZW
100
+ {
101
+ cases: 5,
102
+ importFn: () => import('./lzw.js').then((m) => m.default),
103
+ },
104
+ // Old-style JPEG
105
+ {
106
+ cases: 6,
107
+ importFn: () => {
108
+ throw new Error('old style JPEG compression is not supported.');
109
+ },
110
+ },
111
+ // JPEG
112
+ {
113
+ cases: 7,
114
+ importFn: () => import('./jpeg.js').then((m) => m.default),
115
+ /**
116
+ * @param {import("../imagefiledirectory").ImageFileDirectory} fileDirectory
117
+ */
118
+ decoderParameterFn: async (fileDirectory) => {
119
+ return {
120
+ ...await defaultDecoderParameterFn(fileDirectory),
121
+ JPEGTables: await fileDirectory.loadValue('JPEGTables'),
122
+ };
123
+ },
124
+ },
125
+ // Deflate / Adobe Deflate
126
+ {
127
+ cases: [8, 32946],
128
+ importFn: () => import('./deflate.js').then((m) => m.default),
129
+ },
130
+ // PackBits
131
+ {
132
+ cases: 32773,
133
+ importFn: () => import('./packbits.js').then((m) => m.default),
134
+ },
135
+ // LERC
136
+ {
137
+ cases: 34887,
138
+ importFn: () => import('./lerc.js')
139
+ .then(async (m) => {
140
+ await m.zstd.init();
141
+ return m;
142
+ })
143
+ .then((m) => m.default),
144
+ /**
145
+ * @param {import("../imagefiledirectory").ImageFileDirectory} fileDirectory
146
+ */
147
+ decoderParameterFn: async (fileDirectory) => {
148
+ return {
149
+ ...await defaultDecoderParameterFn(fileDirectory),
150
+ LercParameters: await fileDirectory.loadValue('LercParameters'),
151
+ };
152
+ },
153
+ },
154
+ // zstd
155
+ {
156
+ cases: 50000,
157
+ importFn: () => import('./zstd.js')
158
+ .then(async (m) => {
159
+ await m.zstd.init();
160
+ return m;
161
+ })
162
+ .then((m) => m.default),
163
+ },
164
+ // WebP Images
165
+ {
166
+ cases: 50001,
167
+ importFn: () => import('./webimage.js').then((m) => m.default),
168
+ /**
169
+ * @param {import("../imagefiledirectory").ImageFileDirectory} fileDirectory
170
+ */
171
+ decoderParameterFn: async (fileDirectory) => {
172
+ return {
173
+ ...await defaultDecoderParameterFn(fileDirectory),
174
+ samplesPerPixel: Number(await fileDirectory.loadValue('SamplesPerPixel')) || 4,
175
+ };
176
+ },
177
+ preferWorker: false,
178
+ },
179
+ ];
180
+
181
+ // Add default decoders to registry (end-user may override with other implementations)
182
+ for (const decoderDefinition of defaultDecoderDefinitions) {
183
+ const { cases, importFn, decoderParameterFn, preferWorker: preferWorker_ } = decoderDefinition;
184
+ addDecoder(cases, importFn, decoderParameterFn, preferWorker_);
185
+ }