UNPKG

geotiff

Version:

GeoTIFF image decoding in JavaScript

175 lines 6.71 kB
/** @import BaseDecoder, {BaseDecoderParameters} from "./basedecoder.js" */ /** * @typedef {Object} RegistryEntry * @property {function():Promise<typeof BaseDecoder>} importFn * @property {function(import("../imagefiledirectory.js").ImageFileDirectory):Promise<BaseDecoderParameters>} decoderParameterFn * @property {boolean} preferWorker */ /** @type {Map<number | undefined, RegistryEntry>} */ const registry = new Map(); /** * Default decoder parameter retrieval function * @param {import("../imagefiledirectory.js").ImageFileDirectory} fileDirectory * @returns {Promise<BaseDecoderParameters>} */ async function defaultDecoderParameterFn(fileDirectory) { const isTiled = !fileDirectory.hasTag('StripOffsets'); return /** @type {BaseDecoderParameters} */ ({ tileWidth: isTiled ? await fileDirectory.loadValue('TileWidth') : await fileDirectory.loadValue('ImageWidth'), tileHeight: isTiled ? await fileDirectory.loadValue('TileLength') : (await fileDirectory.loadValue('RowsPerStrip') || await fileDirectory.loadValue('ImageLength')), planarConfiguration: await fileDirectory.loadValue('PlanarConfiguration'), bitsPerSample: await fileDirectory.loadValue('BitsPerSample'), predictor: await fileDirectory.loadValue('Predictor') || 1, }); } /** * Register a decoder for a specific compression method or a range of compressions * @param {(number|undefined|(number|undefined)[])} cases ids of the compression methods to register for * @param {function():Promise<typeof BaseDecoder>} importFn the function to import the decoder * @param {function(import("../imagefiledirectory.js").ImageFileDirectory):Promise<BaseDecoderParameters>} decoderParameterFn * @param {boolean} preferWorker_ Whether to prefer running the decoder in a worker */ export function addDecoder(cases, importFn, decoderParameterFn = defaultDecoderParameterFn, preferWorker_ = true) { if (!Array.isArray(cases)) { cases = [cases]; // eslint-disable-line no-param-reassign } cases.forEach((c) => { registry.set(c, { importFn, decoderParameterFn, preferWorker: preferWorker_ }); }); } /** * Get the required decoder parameters for a specific compression method * @param {number|undefined} compression * @param {import('../imagefiledirectory.js').ImageFileDirectory} fileDirectory */ export async function getDecoderParameters(compression, fileDirectory) { if (!registry.has(compression)) { throw new Error(`Unknown compression method identifier: ${compression}`); } const { decoderParameterFn } = /** @type {RegistryEntry} */ (registry.get(compression)); return decoderParameterFn(fileDirectory); } /** * Get a decoder for a specific compression and parameters * @param {number} compression the compression method identifier * @param {BaseDecoderParameters} decoderParameters the parameters for the decoder * @returns {Promise<import('./basedecoder.js').default>} */ export async function getDecoder(compression, decoderParameters) { if (!registry.has(compression)) { throw new Error(`Unknown compression method identifier: ${compression}`); } const { importFn } = /** @type {RegistryEntry} */ (registry.get(compression)); const Decoder = await importFn(); return new Decoder(decoderParameters); } /** * Whether to prefer running the decoder in a worker * @param {number|undefined} compression the compression method identifier * @returns {boolean} */ export function preferWorker(compression) { if (!registry.has(compression)) { throw new Error(`Unknown compression method identifier: ${compression}`); } return /** @type {RegistryEntry} */ (registry.get(compression)).preferWorker; } const defaultDecoderDefinitions = [ // No compression { cases: [undefined, 1], importFn: () => import('./raw.js').then((m) => m.default), preferWorker: false, }, // LZW { cases: 5, importFn: () => import('./lzw.js').then((m) => m.default), }, // Old-style JPEG { cases: 6, importFn: () => { throw new Error('old style JPEG compression is not supported.'); }, }, // JPEG { cases: 7, importFn: () => import('./jpeg.js').then((m) => m.default), /** * @param {import("../imagefiledirectory.js").ImageFileDirectory} fileDirectory */ decoderParameterFn: async (fileDirectory) => { return { ...await defaultDecoderParameterFn(fileDirectory), JPEGTables: await fileDirectory.loadValue('JPEGTables'), }; }, }, // Deflate / Adobe Deflate { cases: [8, 32946], importFn: () => import('./deflate.js').then((m) => m.default), }, // PackBits { cases: 32773, importFn: () => import('./packbits.js').then((m) => m.default), }, // LERC { cases: 34887, importFn: () => import('./lerc.js') .then(async (m) => { await m.zstd.init(); return m; }) .then((m) => m.default), /** * @param {import("../imagefiledirectory.js").ImageFileDirectory} fileDirectory */ decoderParameterFn: async (fileDirectory) => { return { ...await defaultDecoderParameterFn(fileDirectory), LercParameters: await fileDirectory.loadValue('LercParameters'), }; }, }, // zstd { cases: 50000, importFn: () => import('./zstd.js') .then(async (m) => { await m.zstd.init(); return m; }) .then((m) => m.default), }, // WebP Images { cases: 50001, importFn: () => import('./webimage.js').then((m) => m.default), /** * @param {import("../imagefiledirectory.js").ImageFileDirectory} fileDirectory */ decoderParameterFn: async (fileDirectory) => { return { ...await defaultDecoderParameterFn(fileDirectory), samplesPerPixel: Number(await fileDirectory.loadValue('SamplesPerPixel')) || 4, }; }, preferWorker: false, }, ]; // Add default decoders to registry (end-user may override with other implementations) for (const decoderDefinition of defaultDecoderDefinitions) { const { cases, importFn, decoderParameterFn, preferWorker: preferWorker_ } = decoderDefinition; addDecoder(cases, importFn, decoderParameterFn, preferWorker_); } //# sourceMappingURL=index.js.map