omezarr-tilesource 0.5.0 → 0.6.0
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/README.md +169 -56
- package/dist/omezarr-tilesource.d.ts +506 -24
- package/dist/omezarr-tilesource.js +214 -105
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,33 +38,42 @@ import { OMEZarrTileSource } from "omezarr-tilesource";
|
|
|
38
38
|
|
|
39
39
|
const url = ...;
|
|
40
40
|
|
|
41
|
-
//
|
|
41
|
+
// register the tile source and its data type converters with OpenSeadragon
|
|
42
|
+
// (required for inline configurations and URLs, see "Data pipeline" below)
|
|
43
|
+
OMEZarrTileSource.enable(OpenSeadragon);
|
|
44
|
+
|
|
45
|
+
// configuration with URL (only works with zipped OME-Zarr URLs, path ending in .ozx)
|
|
42
46
|
const tileSource1 = url;
|
|
43
47
|
|
|
44
|
-
// inline configuration with options object
|
|
45
|
-
OMEZarrTileSource.enable(OpenSeadragon);
|
|
48
|
+
// inline configuration with options object
|
|
46
49
|
const tileSource2 = {
|
|
47
50
|
type: "ome-zarr",
|
|
48
51
|
url: url,
|
|
49
|
-
// zip: undefined, // undefined = OME-Zarr ZIP auto-detection based on .ozx suffix
|
|
50
|
-
// c: undefined, // undefined = composite of all active channels (requires dataType "context2d")
|
|
51
|
-
// z: undefined, // undefined = omero rdefs default (middle z-slice if missing)
|
|
52
|
+
// zip: undefined, // undefined = OME-Zarr ZIP auto-detection based on .ozx path suffix
|
|
52
53
|
// t: undefined, // undefined = omero rdefs default (middle timepoint if missing)
|
|
53
|
-
//
|
|
54
|
+
// z: undefined, // undefined = omero rdefs default (middle z-slice if missing)
|
|
55
|
+
// c: undefined, // channel index or array of indices; undefined = all active channels
|
|
56
|
+
// range: undefined, // contrast limits [min, max] or array thereof; undefined = omero windows
|
|
57
|
+
// color: undefined, // RGB color or array of colors; undefined = omero channel colors
|
|
58
|
+
// lutOrColorMap: undefined, // LUT/color map or array thereof; undefined = omero LUTs
|
|
59
|
+
// inverted: undefined, // boolean or array of booleans; undefined = omero inversion
|
|
54
60
|
// autoBoost: undefined // boost brightness of dark tiles (default false)
|
|
55
61
|
};
|
|
56
62
|
|
|
57
63
|
// direct instantiation with URL (works with any OME-Zarr storage backend)
|
|
58
64
|
const tileSource3 = new OMEZarrTileSource(url);
|
|
59
65
|
|
|
60
|
-
// direct instantiation with options object
|
|
66
|
+
// direct instantiation with options object
|
|
61
67
|
const tileSource4 = new OMEZarrTileSource({
|
|
62
68
|
url: url,
|
|
63
|
-
// zip: undefined, // undefined = OME-Zarr ZIP auto-detection based on .ozx suffix
|
|
64
|
-
// c: undefined, // undefined = composite of all active channels (requires dataType "context2d")
|
|
65
|
-
// z: undefined, // undefined = omero rdefs default (middle z-slice if missing)
|
|
69
|
+
// zip: undefined, // undefined = OME-Zarr ZIP auto-detection based on .ozx path suffix
|
|
66
70
|
// t: undefined, // undefined = omero rdefs default (middle timepoint if missing)
|
|
67
|
-
//
|
|
71
|
+
// z: undefined, // undefined = omero rdefs default (middle z-slice if missing)
|
|
72
|
+
// c: undefined, // channel index or array of indices; undefined = all active channels
|
|
73
|
+
// range: undefined, // contrast limits [min, max] or array thereof; undefined = omero windows
|
|
74
|
+
// color: undefined, // RGB color or array of colors; undefined = omero channel colors
|
|
75
|
+
// lutOrColorMap: undefined, // LUT/color map or array thereof; undefined = omero LUTs
|
|
76
|
+
// inverted: undefined, // boolean or array of booleans; undefined = omero inversion
|
|
68
77
|
// autoBoost: undefined // boost brightness of dark tiles (default false)
|
|
69
78
|
});
|
|
70
79
|
|
|
@@ -79,81 +88,185 @@ const viewer = OpenSeadragon({
|
|
|
79
88
|
});
|
|
80
89
|
```
|
|
81
90
|
|
|
91
|
+
### Options
|
|
92
|
+
|
|
93
|
+
| Option | Type | Default | Description |
|
|
94
|
+
| --------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
95
|
+
| `url` | `string \| URL` | (required) | URL of the OME-Zarr image (group) or of a zipped OME-Zarr file, relative to the document base URL |
|
|
96
|
+
| `zip` | `boolean` | `true` if the `url` path ends with `.ozx` | Whether `url` points to a zipped OME-Zarr file |
|
|
97
|
+
| `t` | `number` | omero `rdefs.defaultT`, else middle plane | Timepoint index (0-based) |
|
|
98
|
+
| `z` | `number` | omero `rdefs.defaultZ`, else middle plane | Z-slice index (0-based) |
|
|
99
|
+
| `c` | `number \| number[]` | all channels marked active in omero | Channel index or indices (0-based) to render, composited in the given order. Arrays must be non-empty |
|
|
100
|
+
| `range` | `[number, number] \| ([number, number] \| undefined)[]` | omero channel windows (`window.start`, `window.end`), else the data type range | Contrast limits (`[min, max]`) to render the channels with. Ignored for channels rendered with a color map |
|
|
101
|
+
| `color` | `Color \| (Color \| undefined)[]` | colors of the rendered omero channels, else white | RGB color or colors (`[r, g, b]`, 0-255) to render the channels with. Ignored for channels rendered with a LUT or color map |
|
|
102
|
+
| `lutOrColorMap` | `LUTOrColorMap \| (LUTOrColorMap \| undefined)[]` | omero channel LUTs and color maps, else none | Color LUT (a color per scaled value) or color map (a color per raw value, `Map`) to render the channels with; colors may carry alpha. A color map also overrides `range` and `inverted` |
|
|
103
|
+
| `inverted` | `boolean \| (boolean \| undefined)[]` | omero channel inversion, else `false` | Whether to invert the channels. Ignored for channels rendered with a color map |
|
|
104
|
+
| `autoBoost` | `boolean` | `false` | Boost the brightness of dark tiles (ome-zarr.js `renderChunks` option) |
|
|
105
|
+
|
|
106
|
+
`range`, `color`, `lutOrColorMap` and `inverted` take a single value, which
|
|
107
|
+
applies to all rendered channels, or one entry per rendered channel. An
|
|
108
|
+
`undefined` entry falls back to the omero metadata of that channel, as does the
|
|
109
|
+
option as a whole. The `ranges`, `colors`, `lutsOrColorMaps` and `inverteds`
|
|
110
|
+
getters return the resolved per-channel arrays.
|
|
111
|
+
|
|
112
|
+
`t`, `z` and `c` are validated against the image shape when the OME-Zarr
|
|
113
|
+
metadata is loaded; invalid indices fail with `open-failed`. Images without
|
|
114
|
+
`c` fail if the omero metadata marks no channel as active. An invalid `c`,
|
|
115
|
+
`url` or rendering setting is rejected by the constructor, which throws
|
|
116
|
+
synchronously before any request is made; the length of a rendering setting
|
|
117
|
+
array is only checked against `c` if that is configured too.
|
|
118
|
+
|
|
119
|
+
`url` is resolved against the document base URL when the tile source is
|
|
120
|
+
created; the resolved absolute URL is available as `tileSource.url` (always a
|
|
121
|
+
string) and is used for loading, for the tile cache keys and for comparing tile
|
|
122
|
+
sources.
|
|
123
|
+
|
|
82
124
|
### Accessing OME-Zarr metadata
|
|
83
125
|
|
|
84
126
|
Directly instantiated tile sources start loading the OME-Zarr metadata
|
|
85
127
|
immediately. Await `whenReady()` (or use the `OMEZarrTileSource.open` shortcut)
|
|
86
|
-
to access the `
|
|
87
|
-
(one per resolution level
|
|
128
|
+
to access the loaded image (`loaded.image`, an ome-zarr.js `NgffImage`) and the
|
|
129
|
+
opened zarrita arrays (`loaded.arrays`, one per resolution level, highest
|
|
130
|
+
resolution first) before adding the tile source to a viewer:
|
|
88
131
|
|
|
89
132
|
```javascript
|
|
90
133
|
const tileSource = await OMEZarrTileSource.open({ url: url, c: 0 });
|
|
91
134
|
// equivalent: await new OMEZarrTileSource({ url: url, c: 0 }).whenReady();
|
|
92
135
|
|
|
93
|
-
console.log(tileSource.image.getAxesNames()); // e.g. ["t", "
|
|
94
|
-
console.log(tileSource.image.omero?.channels); // omero channel metadata
|
|
95
|
-
console.log(tileSource.arrays[0].shape); // full-resolution array shape
|
|
136
|
+
console.log(tileSource.loaded.image.getAxesNames()); // e.g. ["t", "c", "z", "y", "x"]
|
|
137
|
+
console.log(tileSource.loaded.image.omero?.channels); // omero channel metadata
|
|
138
|
+
console.log(tileSource.loaded.arrays[0].shape); // full-resolution array shape
|
|
139
|
+
|
|
140
|
+
console.log(tileSource.t, tileSource.z, tileSource.cs); // resolved indices
|
|
141
|
+
console.log(tileSource.channels); // omero channels of the rendered channel indices
|
|
142
|
+
console.log(tileSource.ranges, tileSource.colors); // resolved rendering settings
|
|
143
|
+
console.log(tileSource.lutsOrColorMaps, tileSource.inverteds);
|
|
144
|
+
console.log(tileSource.getWidth(), tileSource.getHeight()); // full resolution
|
|
145
|
+
console.log(tileSource.getWidth(0), tileSource.getHeight(0)); // lowest resolution
|
|
96
146
|
|
|
97
147
|
viewer.addTiledImage({ tileSource: tileSource }); // no second metadata request
|
|
98
148
|
```
|
|
99
149
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
150
|
+
`OMEZarrTileSource.open` accepts an `AbortSignal` as `signal` in its third
|
|
151
|
+
argument to cancel loading. The metadata is loaded once per tile source
|
|
152
|
+
instance: OpenSeadragon reuses a tile source instance passed to it as-is
|
|
153
|
+
(waiting for it to become ready if necessary), whereas a URL or an inline
|
|
154
|
+
configuration object makes OpenSeadragon create (and load) a new instance.
|
|
155
|
+
`whenReady()` rejects (and `loaded` throws) if loading or validation fails.
|
|
156
|
+
|
|
157
|
+
The `t`, `z` and `cs` getters return the resolved values (the omero defaults or
|
|
158
|
+
active channels if the option was not given); `cs` is always an array (the
|
|
159
|
+
`c` option may be a single index), and `channels` returns the omero channel
|
|
160
|
+
objects for `cs`. Before the tile source is ready, they return the configured
|
|
161
|
+
options. The `ranges`, `colors`, `lutsOrColorMaps` and `inverteds` getters
|
|
162
|
+
return the resolved rendering settings of the rendered channels, merging the
|
|
163
|
+
configured values with the omero metadata per channel: `undefined` before the
|
|
164
|
+
tile source is ready or if the image has no omero metadata, like `channels`.
|
|
165
|
+
Entries that are still `undefined` fall back at render time to the data type
|
|
166
|
+
range, white, no LUT and not inverted.
|
|
105
167
|
|
|
106
168
|
### Sharing OME-Zarr metadata between tile sources
|
|
107
169
|
|
|
108
|
-
|
|
109
|
-
for the same URL (e.g. one tile source per channel)
|
|
110
|
-
constructor argument (also supported by
|
|
111
|
-
loading the OME-Zarr metadata (the `zip`
|
|
112
|
-
opened zarrita arrays
|
|
113
|
-
|
|
170
|
+
The loaded image and arrays (`OMEZarr`) can be reused by other directly
|
|
171
|
+
instantiated tile sources for the same URL (e.g. one tile source per channel)
|
|
172
|
+
by passing them as the second constructor argument (also supported by
|
|
173
|
+
`OMEZarrTileSource.open`). This skips loading the OME-Zarr metadata (the `zip`
|
|
174
|
+
option is ignored) and reuses the opened zarrita arrays. The tile source never
|
|
175
|
+
modifies the loaded image, so sharing is safe:
|
|
114
176
|
|
|
115
177
|
```javascript
|
|
116
|
-
|
|
178
|
+
// load once with the tile source's loader ...
|
|
179
|
+
const loaded = await OMEZarrTileSource.loadOMEZarr(url);
|
|
180
|
+
const tileSources = loaded.image.omero.channels.map(
|
|
181
|
+
(_, c) => new OMEZarrTileSource({ url: url, c: c }, loaded),
|
|
182
|
+
);
|
|
117
183
|
|
|
118
|
-
//
|
|
184
|
+
// ... or reuse what a first tile source has loaded
|
|
119
185
|
const tileSource1 = await OMEZarrTileSource.open({ url: url, c: 0 });
|
|
120
186
|
const tileSource2 = new OMEZarrTileSource(
|
|
121
187
|
{ url: url, c: 1 },
|
|
122
|
-
tileSource1.
|
|
188
|
+
tileSource1.loaded,
|
|
123
189
|
);
|
|
190
|
+
```
|
|
124
191
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
192
|
+
`loadOMEZarr(url, zip?, { signal })` loads the metadata with ome-zarr.js and
|
|
193
|
+
opens the arrays of all resolution levels. An `OMEZarr` can also be assembled
|
|
194
|
+
from an `NgffImage` loaded by the app, as long as `arrays` lists the
|
|
195
|
+
opened arrays of `image.paths` in order. It is not checked against the URL of
|
|
196
|
+
the tile source it is passed to.
|
|
197
|
+
|
|
198
|
+
### Loading chunks directly
|
|
199
|
+
|
|
200
|
+
`loadChunks(level, tile, { signal })` returns the (y, x) zarrita chunks of the
|
|
201
|
+
rendered channels at the rendered timepoint and z-slice, either for one tile
|
|
202
|
+
(`{ x, y }` tile coordinates, as passed to OpenSeadragon) or for the whole
|
|
203
|
+
level plane (`tile` `undefined`):
|
|
204
|
+
|
|
205
|
+
```javascript
|
|
206
|
+
const chunks = await tileSource.loadChunks(tileSource.maxLevel, { x: 0, y: 0 });
|
|
207
|
+
const plane = await tileSource.loadChunks(0, undefined); // lowest resolution
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`OMEZarrTileSource.render(tileData)` composites such chunks into a 2D canvas
|
|
211
|
+
context, the same way the `ome-zarr` to `context2d` converter does:
|
|
212
|
+
|
|
213
|
+
```javascript
|
|
214
|
+
import { getDataTypeRange } from "omezarr-tilesource";
|
|
215
|
+
|
|
216
|
+
const ctx = OMEZarrTileSource.render({
|
|
217
|
+
chunks: plane,
|
|
218
|
+
// the tile data holds the renderChunks arguments, one per chunk, with the
|
|
219
|
+
// defaults of the tile source getters applied
|
|
220
|
+
ranges: plane.map(
|
|
221
|
+
(chunk, c) => tileSource.ranges?.[c] ?? getDataTypeRange(chunk),
|
|
222
|
+
),
|
|
223
|
+
colors: plane.map((_, c) => tileSource.colors?.[c] ?? [255, 255, 255]),
|
|
224
|
+
lutsOrColorMaps: plane.map((_, c) => tileSource.lutsOrColorMaps?.[c]),
|
|
225
|
+
inverteds: plane.map((_, c) => tileSource.inverteds?.[c] ?? false),
|
|
226
|
+
autoBoost: tileSource.autoBoost,
|
|
227
|
+
});
|
|
128
228
|
```
|
|
129
229
|
|
|
130
230
|
## Data pipeline
|
|
131
231
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
`OpenSeadragon.converter` when the module is
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
232
|
+
Tiles are downloaded as raw zarrita chunks, one per rendered channel, together
|
|
233
|
+
with the rendering settings resolved for that tile, and passed to OpenSeadragon
|
|
234
|
+
with the data type `ome-zarr` (see the `OMEZarrTileData` type: `chunks`,
|
|
235
|
+
`ranges`, `colors`, `lutsOrColorMaps`, `inverteds` and `autoBoost` — the
|
|
236
|
+
arguments of ome-zarr.js `renderChunks`, one entry per chunk). A converter from
|
|
237
|
+
`ome-zarr` to `context2d` calls `OMEZarrTileSource.render` on demand, which
|
|
238
|
+
composites them into a 2D canvas context. Because the raw chunks stay in the
|
|
239
|
+
tile cache, tiles can be re-rendered without re-downloading them.
|
|
240
|
+
|
|
241
|
+
Settings that are neither configured nor in the omero metadata are resolved
|
|
242
|
+
when the tile is downloaded: to the data type range for integer types (e.g.
|
|
243
|
+
`[0, 65535]` for `uint16`) and `[0, 1]` for floating point types, to white, to
|
|
244
|
+
no LUT and to not inverted — so an image without omero metadata is rendered in
|
|
245
|
+
white over its full data type range.
|
|
246
|
+
|
|
247
|
+
The converters are registered on `OpenSeadragon.converter` when the module is
|
|
248
|
+
imported (for the OpenSeadragon instance it imports, and for a global
|
|
249
|
+
`OpenSeadragon` if present) and by `OMEZarrTileSource.enable`. Tiles cannot be
|
|
250
|
+
rendered without them, so call `enable` if the app uses a different
|
|
251
|
+
OpenSeadragon instance than the one resolved by this module (e.g. a separately
|
|
252
|
+
bundled copy).
|
|
253
|
+
|
|
254
|
+
All rendering settings in `OMEZarrTileData` are plain values, resolved from the
|
|
255
|
+
tile source configuration and the omero metadata when the tile is downloaded.
|
|
256
|
+
Editing the omero channels of a loaded image therefore does not affect tiles
|
|
257
|
+
that are already cached, with or without `viewer.requestInvalidate()`; render
|
|
258
|
+
them differently by creating a tile source with the corresponding options
|
|
259
|
+
instead. Tile sources that configure rendering settings do not share cached
|
|
260
|
+
tiles with tile sources that configure different ones.
|
|
153
261
|
|
|
154
262
|
## Example
|
|
155
263
|
|
|
156
|
-
[
|
|
264
|
+
[https://tissuumaps.github.io/OMEZarrTileSource?url=https://livingobjects.ebi.ac.uk/idr/zarr/v0.5/idr0062A/6001240_labels.zarr](https://tissuumaps.github.io/OMEZarrTileSource?url=https%3A%2F%2Flivingobjects.ebi.ac.uk%2Fidr%2Fzarr%2Fv0.5%2Fidr0062A%2F6001240_labels.zarr)
|
|
265
|
+
|
|
266
|
+
The example page takes the URL of the OME-Zarr image from the mandatory `url`
|
|
267
|
+
query parameter, and the timepoint index, the z-slice index and the channel
|
|
268
|
+
indices from the optional `t`, `z` and `c` query parameters (repeat `c` for
|
|
269
|
+
multiple channels).
|
|
157
270
|
|
|
158
271
|
[Source code](index.html)
|
|
159
272
|
|
|
@@ -3,58 +3,540 @@ import { default as default_2 } from 'openseadragon';
|
|
|
3
3
|
import { NgffImage } from 'ome-zarr.js';
|
|
4
4
|
import * as zarr from 'zarrita';
|
|
5
5
|
|
|
6
|
+
/** An RGB color, each component in the range 0-255. */
|
|
7
|
+
export declare type Color = [number, number, number];
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 32-bit FNV-1a hash of a string, over its UTF-16 code units.
|
|
11
|
+
*
|
|
12
|
+
* @param text - Text to hash
|
|
13
|
+
* @returns The hash, as an unsigned 32-bit integer
|
|
14
|
+
*/
|
|
15
|
+
export declare function fnv1a(text: string): number;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Value range of a chunk's data type, used when a channel has no window.
|
|
19
|
+
*
|
|
20
|
+
* @param chunk - Chunk to get the data type range of
|
|
21
|
+
* @returns The minimum and maximum value of the data type (`[0, 1]` for
|
|
22
|
+
* floating point and boolean data)
|
|
23
|
+
*/
|
|
24
|
+
export declare function getDataTypeRange(chunk: zarr.Chunk<zarr.NumberDataType | zarr.BigintDataType>): [number, number];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Whether a URL points to a zipped OME-Zarr file (`.ozx` path suffix, ignoring
|
|
28
|
+
* any query and fragment).
|
|
29
|
+
*
|
|
30
|
+
* @param url - URL to check, as a string or a `URL`
|
|
31
|
+
* @returns Whether the URL path ends in `.ozx`
|
|
32
|
+
*/
|
|
33
|
+
export declare function isOZX(url: string | URL): boolean;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A color LUT (a color per scaled value, e.g. 256 entries) or a color map (a
|
|
37
|
+
* color per raw pixel value), as used by the omero channels.
|
|
38
|
+
*
|
|
39
|
+
* Unlike {@link Color}, the colors may carry an alpha component, e.g. to render
|
|
40
|
+
* unmapped label values transparently.
|
|
41
|
+
*/
|
|
42
|
+
export declare type LUTOrColorMap = (Color | [number, number, number, number])[] | Map<number, Color | [number, number, number, number]>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* A loaded OME-Zarr image: the ome-zarr.js {@link NgffImage} holding the
|
|
46
|
+
* metadata and the opened zarrita arrays of its multiscale pyramid.
|
|
47
|
+
*
|
|
48
|
+
* Created by {@link OMEZarrTileSource.loadOMEZarr} and exposed by
|
|
49
|
+
* {@link OMEZarrTileSource.loaded}. Can be passed to the constructor or to
|
|
50
|
+
* {@link OMEZarrTileSource.open} to share one metadata load across several
|
|
51
|
+
* tile sources for the same URL. The tile source never modifies it.
|
|
52
|
+
*/
|
|
53
|
+
export declare type OMEZarr = {
|
|
54
|
+
/** OME-Zarr metadata (multiscales, axes, omero) */
|
|
55
|
+
image: NgffImage;
|
|
56
|
+
/** One array per resolution level, highest resolution first */
|
|
57
|
+
arrays: zarr.Array<zarr.NumberDataType | zarr.BigintDataType>[];
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Tile data of the OpenSeadragon data type `ome-zarr`.
|
|
62
|
+
*
|
|
63
|
+
* Every tile is finished with this raw data type; the converter registered by
|
|
64
|
+
* {@link OMEZarrTileSource.enable} (and on import) renders it to `context2d`
|
|
65
|
+
* on demand, so cached tiles can be re-rendered without re-downloading them.
|
|
66
|
+
*
|
|
67
|
+
* The rendering settings are the arguments of ome-zarr.js `renderChunks`, one
|
|
68
|
+
* entry per chunk, fully resolved from the tile source configuration and the
|
|
69
|
+
* omero metadata when the tile was downloaded — so changing either requires
|
|
70
|
+
* re-downloading the tiles.
|
|
71
|
+
*/
|
|
6
72
|
export declare interface OMEZarrTileData {
|
|
7
|
-
chunk
|
|
8
|
-
|
|
9
|
-
|
|
73
|
+
/** One (y, x) chunk per rendered channel, in {@link OMEZarrTileSource.cs} order */
|
|
74
|
+
chunks: zarr.Chunk<zarr.NumberDataType | zarr.BigintDataType>[];
|
|
75
|
+
/**
|
|
76
|
+
* The contrast limits (`[min, max]`) of each chunk
|
|
77
|
+
* ({@link OMEZarrTileSource.ranges}), falling back to the data type range of
|
|
78
|
+
* the chunk. `min` is always less than `max`.
|
|
79
|
+
*/
|
|
80
|
+
ranges: [number, number][];
|
|
81
|
+
/**
|
|
82
|
+
* The RGB color of each chunk ({@link OMEZarrTileSource.colors}), falling
|
|
83
|
+
* back to white. Unused for chunks with a LUT or a color map.
|
|
84
|
+
*/
|
|
85
|
+
colors: Color[];
|
|
86
|
+
/**
|
|
87
|
+
* The color LUT or color map of each chunk
|
|
88
|
+
* ({@link OMEZarrTileSource.lutsOrColorMaps}). `undefined` entries are
|
|
89
|
+
* rendered with their {@link colors} entry instead.
|
|
90
|
+
*/
|
|
91
|
+
lutsOrColorMaps: (LUTOrColorMap | undefined)[];
|
|
92
|
+
/**
|
|
93
|
+
* Whether to invert each chunk ({@link OMEZarrTileSource.inverteds}), falling
|
|
94
|
+
* back to `false`.
|
|
95
|
+
*/
|
|
96
|
+
inverteds: boolean[];
|
|
97
|
+
/** Boost the brightness of dark tiles (ome-zarr.js `renderChunks` option) */
|
|
98
|
+
autoBoost: boolean;
|
|
10
99
|
}
|
|
11
100
|
|
|
101
|
+
/**
|
|
102
|
+
* OpenSeadragon tile source for the OME-Zarr bioimage file format.
|
|
103
|
+
*
|
|
104
|
+
* Tiles correspond to the chunks of the multiscale pyramid stored in the
|
|
105
|
+
* OME-Zarr image; OpenSeadragon level 0 is the lowest resolution and
|
|
106
|
+
* `maxLevel` the highest. Tiles are delivered as raw `ome-zarr` data (see
|
|
107
|
+
* {@link OMEZarrTileData}) and rendered by a registered converter.
|
|
108
|
+
*
|
|
109
|
+
* Constructing a tile source starts loading the OME-Zarr metadata
|
|
110
|
+
* asynchronously (unless an {@link OMEZarr} is passed). Await
|
|
111
|
+
* {@link OMEZarrTileSource.whenReady} or use {@link OMEZarrTileSource.open}
|
|
112
|
+
* before accessing {@link OMEZarrTileSource.loaded} and the resolved
|
|
113
|
+
* {@link OMEZarrTileSource.t}, {@link OMEZarrTileSource.z},
|
|
114
|
+
* {@link OMEZarrTileSource.cs}, {@link OMEZarrTileSource.channels},
|
|
115
|
+
* {@link OMEZarrTileSource.ranges}, {@link OMEZarrTileSource.colors},
|
|
116
|
+
* {@link OMEZarrTileSource.lutsOrColorMaps} and
|
|
117
|
+
* {@link OMEZarrTileSource.inverteds}.
|
|
118
|
+
*
|
|
119
|
+
* Importing this module registers the `ome-zarr` converters on the imported
|
|
120
|
+
* OpenSeadragon instance (and on a global `OpenSeadragon`, if present). Call
|
|
121
|
+
* {@link OMEZarrTileSource.enable} for any other OpenSeadragon instance, and to
|
|
122
|
+
* register the tile source for inline configurations.
|
|
123
|
+
*/
|
|
12
124
|
export declare class OMEZarrTileSource extends default_2.TileSource {
|
|
13
125
|
readonly url: string;
|
|
14
|
-
readonly zip
|
|
15
|
-
readonly
|
|
16
|
-
readonly
|
|
17
|
-
readonly
|
|
18
|
-
readonly
|
|
19
|
-
readonly
|
|
126
|
+
readonly zip: boolean;
|
|
127
|
+
private readonly _t?;
|
|
128
|
+
private readonly _z?;
|
|
129
|
+
private readonly _c?;
|
|
130
|
+
private readonly _renderSettings;
|
|
131
|
+
private readonly _renderSettingsHash;
|
|
20
132
|
width: number;
|
|
21
133
|
height: number;
|
|
22
|
-
private
|
|
23
|
-
private _arrays?;
|
|
134
|
+
private _loaded?;
|
|
24
135
|
private readonly _readyPromise;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
136
|
+
/**
|
|
137
|
+
* Registers the tile source and its `ome-zarr` data type converters with an
|
|
138
|
+
* OpenSeadragon instance.
|
|
139
|
+
*
|
|
140
|
+
* Required for inline configurations (`{ type: "ome-zarr", ... }`) and for
|
|
141
|
+
* rendering tiles with an OpenSeadragon instance other than the one imported
|
|
142
|
+
* by this module.
|
|
143
|
+
*
|
|
144
|
+
* @param os - The OpenSeadragon module to register with
|
|
145
|
+
*/
|
|
146
|
+
static enable(os?: typeof default_2): void;
|
|
147
|
+
/**
|
|
148
|
+
* Loads the OME-Zarr metadata and opens the arrays of all resolution levels.
|
|
149
|
+
*
|
|
150
|
+
* The result can be passed to the constructor or to {@link open} to share one
|
|
151
|
+
* metadata load across several tile sources for the same URL.
|
|
152
|
+
*
|
|
153
|
+
* @param url - URL of the OME-Zarr image or zipped OME-Zarr file, as a string
|
|
154
|
+
* or a `URL`; relative URLs are resolved against the document base URL
|
|
155
|
+
* @param zip - Whether the URL points to a zipped OME-Zarr file; defaults to
|
|
156
|
+
* `true` for URLs whose path ends in `.ozx`
|
|
157
|
+
* @param options - `signal` aborts the load
|
|
158
|
+
* @returns The loaded image and its arrays, highest resolution first
|
|
159
|
+
* @throws If the URL is relative and there is no document base URL
|
|
160
|
+
*/
|
|
161
|
+
static loadOMEZarr(url: string | URL, zip?: boolean, options?: {
|
|
162
|
+
signal?: AbortSignal;
|
|
163
|
+
}): Promise<OMEZarr>;
|
|
164
|
+
/**
|
|
165
|
+
* Constructs a tile source and waits until its OME-Zarr metadata is loaded.
|
|
166
|
+
*
|
|
167
|
+
* Equivalent to `new OMEZarrTileSource(config, loaded).whenReady()`, except
|
|
168
|
+
* that loading the metadata can be aborted with `signal`.
|
|
169
|
+
*
|
|
170
|
+
* @param config - URL or {@link OMEZarrTileSourceOptions}
|
|
171
|
+
* @param loaded - Previously loaded image of the same URL to reuse instead of
|
|
172
|
+
* loading it
|
|
173
|
+
* @param options - `signal` aborts the load
|
|
174
|
+
* @returns The ready tile source; rejects if loading or validation fails
|
|
175
|
+
*/
|
|
176
|
+
static open(config: string | URL | OMEZarrTileSourceOptions, loaded?: OMEZarr, options?: {
|
|
177
|
+
signal?: AbortSignal;
|
|
178
|
+
}): Promise<OMEZarrTileSource>;
|
|
179
|
+
/**
|
|
180
|
+
* Renders `ome-zarr` tile data into a composite 2D canvas context.
|
|
181
|
+
*
|
|
182
|
+
* Called by the `ome-zarr` to `context2d` converter registered by
|
|
183
|
+
* {@link enable} (and on import), and usable directly for rendering chunks
|
|
184
|
+
* loaded with {@link loadChunks} outside of OpenSeadragon.
|
|
185
|
+
*
|
|
186
|
+
* The tile data holds the rendering settings exactly as passed to ome-zarr.js
|
|
187
|
+
* `renderChunks`: each chunk is scaled to its {@link OMEZarrTileData.ranges}
|
|
188
|
+
* entry, colorized with its {@link OMEZarrTileData.colors} entry or with its
|
|
189
|
+
* {@link OMEZarrTileData.lutsOrColorMaps} entry, inverted if its
|
|
190
|
+
* {@link OMEZarrTileData.inverteds} entry is set, and the results are blended
|
|
191
|
+
* additively. Color maps ignore the range and the inversion.
|
|
192
|
+
*
|
|
193
|
+
* @param tileData - Chunks and rendering settings of one tile
|
|
194
|
+
* @returns A 2D canvas context of the chunk size holding the composite
|
|
195
|
+
* @throws If the chunks are empty or no 2D canvas context is available
|
|
196
|
+
*/
|
|
197
|
+
static render(tileData: OMEZarrTileData): CanvasRenderingContext2D;
|
|
198
|
+
/**
|
|
199
|
+
* Creates a tile source and starts loading the OME-Zarr metadata (unless
|
|
200
|
+
* `loaded` is given), raising `ready` or `open-failed` asynchronously.
|
|
201
|
+
*
|
|
202
|
+
* @param url - URL of the OME-Zarr image, as a string or a `URL`, resolved
|
|
203
|
+
* against the document base URL; zipped files are detected by the `.ozx`
|
|
204
|
+
* path suffix
|
|
205
|
+
* @param loaded - Previously loaded image of the same URL to reuse instead of
|
|
206
|
+
* loading it
|
|
207
|
+
* @throws If the URL cannot be resolved
|
|
208
|
+
*/
|
|
209
|
+
constructor(url: string | URL, loaded?: OMEZarr);
|
|
210
|
+
/**
|
|
211
|
+
* Creates a tile source and starts loading the OME-Zarr metadata (unless
|
|
212
|
+
* `loaded` is given), raising `ready` or `open-failed` asynchronously.
|
|
213
|
+
*
|
|
214
|
+
* @param options - Tile source configuration
|
|
215
|
+
* @param loaded - Previously loaded image of the same URL to reuse instead of
|
|
216
|
+
* loading it
|
|
217
|
+
* @throws If the configuration is invalid or the URL cannot be resolved */
|
|
218
|
+
constructor(options: OMEZarrTileSourceOptions, loaded?: OMEZarr);
|
|
219
|
+
/**
|
|
220
|
+
* The loaded OME-Zarr image and arrays.
|
|
221
|
+
*
|
|
222
|
+
* @throws If the tile source is not ready yet (or failed to load)
|
|
223
|
+
*/
|
|
224
|
+
get loaded(): OMEZarr;
|
|
225
|
+
/**
|
|
226
|
+
* The rendered timepoint index: the configured `t`, otherwise the omero
|
|
227
|
+
* `rdefs.defaultT` once loaded, otherwise `undefined` (middle timepoint).
|
|
228
|
+
*/
|
|
229
|
+
get t(): number | undefined;
|
|
230
|
+
/**
|
|
231
|
+
* The rendered z-slice index: the configured `z`, otherwise the omero
|
|
232
|
+
* `rdefs.defaultZ` once loaded, otherwise `undefined` (middle z-slice).
|
|
233
|
+
*/
|
|
234
|
+
get z(): number | undefined;
|
|
235
|
+
/**
|
|
236
|
+
* The rendered channel indices: the configured `c` option (as an array, even if a
|
|
237
|
+
* single index was configured), otherwise the indices of all channels marked
|
|
238
|
+
* active in the omero metadata once loaded, otherwise `undefined` (all
|
|
239
|
+
* channels).
|
|
240
|
+
*/
|
|
241
|
+
get cs(): number[] | undefined;
|
|
242
|
+
/**
|
|
243
|
+
* The omero channels of the rendered channels ({@link cs}): one entry per
|
|
244
|
+
* rendered channel, `undefined` for channels that the omero metadata does
|
|
245
|
+
* not cover.
|
|
246
|
+
*
|
|
247
|
+
* `undefined` instead of the whole array if `c` was not configured and the
|
|
248
|
+
* tile source is not ready or the image has no omero metadata.
|
|
249
|
+
*/
|
|
250
|
+
get channels(): (Channel | undefined)[] | undefined;
|
|
251
|
+
/**
|
|
252
|
+
* The contrast limits (`[min, max]`) of the rendered channels ({@link cs}):
|
|
253
|
+
* the configured `ranges` (as an array, even if a single range was
|
|
254
|
+
* configured), otherwise the windows of the corresponding omero
|
|
255
|
+
* {@link channels}, and `undefined` wherever those are.
|
|
256
|
+
*
|
|
257
|
+
* Channels without contrast limits (`undefined` entries, or `undefined`
|
|
258
|
+
* instead of the whole array) are rendered with their data type range.
|
|
259
|
+
*/
|
|
260
|
+
get ranges(): ([number, number] | undefined)[] | undefined;
|
|
261
|
+
/**
|
|
262
|
+
* The RGB colors of the rendered channels ({@link cs}): the configured
|
|
263
|
+
* `colors` (as an array, even if a single color was configured), otherwise
|
|
264
|
+
* the colors of the corresponding omero {@link channels}, and `undefined`
|
|
265
|
+
* wherever those are.
|
|
266
|
+
*
|
|
267
|
+
* Channels without a color (`undefined` entries, or `undefined` instead of
|
|
268
|
+
* the whole array) are rendered in white.
|
|
269
|
+
*/
|
|
270
|
+
get colors(): (Color | undefined)[] | undefined;
|
|
271
|
+
/**
|
|
272
|
+
* The color LUTs and color maps of the rendered channels ({@link cs}): the
|
|
273
|
+
* configured `lutsOrColorMaps` (as an array, even if a single LUT or color
|
|
274
|
+
* map was configured), otherwise those of the corresponding omero
|
|
275
|
+
* {@link channels}, and `undefined` wherever those are.
|
|
276
|
+
*
|
|
277
|
+
* Channels without a LUT or color map (`undefined` entries, or `undefined`
|
|
278
|
+
* instead of the whole array) are rendered with their {@link colors} entry.
|
|
279
|
+
*/
|
|
280
|
+
get lutsOrColorMaps(): (LUTOrColorMap | undefined)[] | undefined;
|
|
281
|
+
/**
|
|
282
|
+
* Whether the rendered channels ({@link cs}) are inverted: the configured
|
|
283
|
+
* `inverteds` (as an array, even if a single value was configured),
|
|
284
|
+
* otherwise the inversion of the corresponding omero {@link channels}, and
|
|
285
|
+
* `undefined` wherever those are.
|
|
286
|
+
*
|
|
287
|
+
* Channels without an inversion (`undefined` entries, or `undefined` instead
|
|
288
|
+
* of the whole array) are not inverted.
|
|
289
|
+
*/
|
|
290
|
+
get inverteds(): (boolean | undefined)[] | undefined;
|
|
291
|
+
/** Whether the brightness of dark tiles is boosted (the configured `autoBoost`) */
|
|
292
|
+
get autoBoost(): boolean;
|
|
293
|
+
/**
|
|
294
|
+
* Waits until the OME-Zarr metadata is loaded and validated.
|
|
295
|
+
*
|
|
296
|
+
* @returns The tile source once `ready` has been raised; rejects with the
|
|
297
|
+
* `open-failed` message otherwise
|
|
298
|
+
*/
|
|
30
299
|
whenReady(): Promise<this>;
|
|
300
|
+
/**
|
|
301
|
+
* Width in pixels of a resolution level.
|
|
302
|
+
*
|
|
303
|
+
* @param level - OpenSeadragon level (0 = lowest resolution); defaults to
|
|
304
|
+
* `maxLevel` (full resolution)
|
|
305
|
+
* @throws If the tile source is not ready or the level is out of bounds
|
|
306
|
+
*/
|
|
307
|
+
getWidth(level?: number): number;
|
|
308
|
+
/**
|
|
309
|
+
* Height in pixels of a resolution level.
|
|
310
|
+
*
|
|
311
|
+
* @param level - OpenSeadragon level (0 = lowest resolution); defaults to
|
|
312
|
+
* `maxLevel` (full resolution)
|
|
313
|
+
* @throws If the tile source is not ready or the level is out of bounds
|
|
314
|
+
*/
|
|
315
|
+
getHeight(level?: number): number;
|
|
316
|
+
/**
|
|
317
|
+
* Loads the (y, x) chunks of the rendered channels ({@link cs}) at the rendered
|
|
318
|
+
* timepoint and z-slice ({@link t}, {@link z}).
|
|
319
|
+
*
|
|
320
|
+
* @param level - OpenSeadragon level (0 = lowest resolution)
|
|
321
|
+
* @param tile - Tile coordinates within the level, or `undefined` to load the
|
|
322
|
+
* whole level plane
|
|
323
|
+
* @param options - `signal` aborts the chunk requests
|
|
324
|
+
* @returns One chunk per rendered channel, in {@link cs} order
|
|
325
|
+
* @throws If the tile source is not ready or the level or tile is out of
|
|
326
|
+
* bounds
|
|
327
|
+
*/
|
|
328
|
+
loadChunks(level: number, tile: {
|
|
329
|
+
x: number;
|
|
330
|
+
y: number;
|
|
331
|
+
} | undefined, options?: {
|
|
332
|
+
signal?: AbortSignal;
|
|
333
|
+
}): Promise<zarr.Chunk<zarr.NumberDataType | zarr.BigintDataType>[]>;
|
|
334
|
+
/**
|
|
335
|
+
* Whether OpenSeadragon should use this tile source for a configuration:
|
|
336
|
+
* URLs whose path ends in `.ozx` and objects with `type: "ome-zarr"`.
|
|
337
|
+
*/
|
|
31
338
|
supports(data: string | object | object[] | Document): boolean;
|
|
339
|
+
/**
|
|
340
|
+
* Normalizes an OpenSeadragon configuration into
|
|
341
|
+
* {@link OMEZarrTileSourceOptions}.
|
|
342
|
+
*
|
|
343
|
+
* @throws For array, XML document and POST data configurations
|
|
344
|
+
*/
|
|
32
345
|
configure(data: string | object | object[] | Document, _url: string, postData?: string | null): OMEZarrTileSourceOptions;
|
|
346
|
+
/**
|
|
347
|
+
* Whether another tile source renders the same data: the URL, `zip`, the
|
|
348
|
+
* resolved {@link t}, {@link z} and {@link cs}, and the hash of the
|
|
349
|
+
* configured rendering settings.
|
|
350
|
+
*
|
|
351
|
+
* These are the components of {@link getTileHashKey}, so tile sources compare
|
|
352
|
+
* equal exactly when they share cached tiles. The resolved indices depend on
|
|
353
|
+
* the loaded metadata, so tile sources that are not ready yet compare by
|
|
354
|
+
* their configured indices.
|
|
355
|
+
*/
|
|
33
356
|
equals(other: default_2.TileSource): boolean;
|
|
357
|
+
/**
|
|
358
|
+
* Loads (or reuses) the OME-Zarr metadata, validates it against the
|
|
359
|
+
* configuration and raises `ready`, or raises `open-failed` on error.
|
|
360
|
+
*
|
|
361
|
+
* Called asynchronously by the OpenSeadragon `TileSource` constructor.
|
|
362
|
+
*/
|
|
34
363
|
getImageInfo(url: string): void;
|
|
364
|
+
/** Chunk width of a resolution level in pixels. */
|
|
35
365
|
getTileWidth(level: number): number;
|
|
366
|
+
/** Chunk height of a resolution level in pixels. */
|
|
36
367
|
getTileHeight(level: number): number;
|
|
368
|
+
/** Width of a resolution level relative to the full resolution. */
|
|
37
369
|
getLevelScale(level: number): number;
|
|
370
|
+
/** Number of tiles (chunks) per axis of a resolution level. */
|
|
371
|
+
getNumTiles(level: number): default_2.Point;
|
|
372
|
+
/**
|
|
373
|
+
* Encodes the tile coordinates as `level=…&x=…&y=…` for
|
|
374
|
+
* {@link downloadTileStart}; no network URL is involved.
|
|
375
|
+
*/
|
|
38
376
|
getTileUrl(level: number, x: number, y: number): string;
|
|
377
|
+
/**
|
|
378
|
+
* Cache key of a tile: the image URL plus the resolved data parameters
|
|
379
|
+
* (`zip`, {@link t}, {@link z}, {@link cs}) and tile coordinates, and an
|
|
380
|
+
* FNV-1a hash of the rendering settings (the configured `ranges`, `colors`,
|
|
381
|
+
* `lutsOrColorMaps` and `inverteds`, and `autoBoost`), so that tile sources
|
|
382
|
+
* rendering the same data share cached tiles.
|
|
383
|
+
*
|
|
384
|
+
* Rendering settings that are not configured are omitted from the hash, as
|
|
385
|
+
* they resolve to the same omero values for the same image.
|
|
386
|
+
*/
|
|
39
387
|
getTileHashKey(level: number, x: number, y: number): string;
|
|
388
|
+
/**
|
|
389
|
+
* Loads the chunks of a tile and finishes the job with `ome-zarr` data
|
|
390
|
+
* ({@link OMEZarrTileData}); fails the job if loading fails.
|
|
391
|
+
*/
|
|
40
392
|
downloadTileStart(context: default_2.ImageJob): void;
|
|
393
|
+
/** Aborts the chunk requests of a pending tile download. */
|
|
41
394
|
downloadTileAbort(context: default_2.ImageJob): void;
|
|
42
|
-
|
|
395
|
+
/**
|
|
396
|
+
* Normalizes a configured rendering setting into one entry per rendered
|
|
397
|
+
* channel.
|
|
398
|
+
*
|
|
399
|
+
* @param options - The tile source configuration
|
|
400
|
+
* @param name - Name of the rendering setting to normalize
|
|
401
|
+
* @param isValue - Whether a value is a single value rather than an array of
|
|
402
|
+
* values
|
|
403
|
+
* @param c - The normalized `c` option, to validate the length against and to
|
|
404
|
+
* repeat a single value for
|
|
405
|
+
* @returns One entry per rendered channel (`undefined` entries fall back to
|
|
406
|
+
* the omero metadata), or `undefined` if the setting is not configured; a
|
|
407
|
+
* single value is repeated for every channel in `c`, or kept as the only
|
|
408
|
+
* entry if `c` is not configured, in which case the getters repeat it
|
|
409
|
+
* @throws If the setting is neither a value nor a non-empty array of values,
|
|
410
|
+
* or if its length does not match `c`
|
|
411
|
+
*/
|
|
412
|
+
private static _normalize;
|
|
413
|
+
/**
|
|
414
|
+
* Spreads a normalized rendering setting over the rendered channels.
|
|
415
|
+
*
|
|
416
|
+
* @param values - The normalized rendering setting, if configured
|
|
417
|
+
* @param channels - The omero channels of the rendered channels, if known
|
|
418
|
+
* @returns One entry per rendered channel: the configured entries, a single
|
|
419
|
+
* configured entry repeated for every rendered channel, or one `undefined`
|
|
420
|
+
* entry per rendered channel if the setting is not configured
|
|
421
|
+
*/
|
|
422
|
+
private static _repeat;
|
|
423
|
+
/** Registers the `ome-zarr` → `context2d` and copy converters (once). */
|
|
43
424
|
private static _learnConverters;
|
|
44
|
-
|
|
45
|
-
private static
|
|
46
|
-
private static _getDataTypeRange;
|
|
425
|
+
/** Size of a named axis at a resolution level (`1` if the axis is absent). */
|
|
426
|
+
private static _getAxisSize;
|
|
47
427
|
}
|
|
48
428
|
|
|
429
|
+
/**
|
|
430
|
+
* Configuration of an {@link OMEZarrTileSource}.
|
|
431
|
+
*
|
|
432
|
+
* Also accepted by OpenSeadragon as an inline tile source configuration
|
|
433
|
+
* (`tileSources: { type: "ome-zarr", url, ... }`) once the tile source has been
|
|
434
|
+
* registered with {@link OMEZarrTileSource.enable}.
|
|
435
|
+
*/
|
|
49
436
|
export declare interface OMEZarrTileSourceOptions {
|
|
437
|
+
/** Tile source type for inline OpenSeadragon configurations */
|
|
50
438
|
type?: "ome-zarr";
|
|
51
|
-
|
|
439
|
+
/**
|
|
440
|
+
* URL of the OME-Zarr image (group) or of a zipped OME-Zarr file, as a
|
|
441
|
+
* string or a `URL`.
|
|
442
|
+
*
|
|
443
|
+
* Relative URLs are resolved against the document base URL.
|
|
444
|
+
*/
|
|
445
|
+
url: string | URL;
|
|
446
|
+
/**
|
|
447
|
+
* Whether the URL points to a zipped OME-Zarr file.
|
|
448
|
+
*
|
|
449
|
+
* Defaults to `true` for URLs whose path ends in `.ozx` (ignoring any query
|
|
450
|
+
* and fragment) and `false` otherwise.
|
|
451
|
+
*/
|
|
52
452
|
zip?: boolean;
|
|
53
|
-
|
|
54
|
-
|
|
453
|
+
/**
|
|
454
|
+
* Timepoint index (0-based) to render.
|
|
455
|
+
*
|
|
456
|
+
* Defaults to the omero `rdefs.defaultT`, or to the middle timepoint if the
|
|
457
|
+
* metadata has none. Validated against the image shape when the image is
|
|
458
|
+
* loaded.
|
|
459
|
+
*/
|
|
55
460
|
t?: number;
|
|
56
|
-
|
|
461
|
+
/**
|
|
462
|
+
* Z-slice index (0-based) to render.
|
|
463
|
+
*
|
|
464
|
+
* Defaults to the omero `rdefs.defaultZ`, or to the middle z-slice if the
|
|
465
|
+
* metadata has none. Validated against the image shape when the image is
|
|
466
|
+
* loaded.
|
|
467
|
+
*/
|
|
468
|
+
z?: number;
|
|
469
|
+
/**
|
|
470
|
+
* Channel index or indices (0-based) to render, composited in the given
|
|
471
|
+
* order.
|
|
472
|
+
*
|
|
473
|
+
* A single index is equivalent to an array with one element; arrays must be
|
|
474
|
+
* non-empty. Defaults to all channels marked active in the omero metadata
|
|
475
|
+
* (or to all channels if the metadata has no omero section). Validated
|
|
476
|
+
* against the image shape when the image is loaded.
|
|
477
|
+
*/
|
|
478
|
+
c?: number | number[];
|
|
479
|
+
/**
|
|
480
|
+
* Contrast limits (`[min, max]`) to render the channels ({@link c}) with,
|
|
481
|
+
* overriding the omero channel windows.
|
|
482
|
+
*
|
|
483
|
+
* A single range applies to all rendered channels; arrays must have one
|
|
484
|
+
* entry per rendered channel, which is only checked against {@link c} if
|
|
485
|
+
* that is configured too. `undefined` entries fall back to the omero channel
|
|
486
|
+
* window (`window.start`, `window.end`), as does the default, and to the
|
|
487
|
+
* data type range if the metadata has none. Ignored for channels rendered
|
|
488
|
+
* with a color map.
|
|
489
|
+
*/
|
|
490
|
+
range?: [number, number] | ([number, number] | undefined)[];
|
|
491
|
+
/**
|
|
492
|
+
* RGB color to render the channels ({@link c}) with, overriding the omero
|
|
493
|
+
* channel colors.
|
|
494
|
+
*
|
|
495
|
+
* A single color applies to all rendered channels; arrays must have one
|
|
496
|
+
* color per rendered channel, which is only checked against {@link c} if
|
|
497
|
+
* that is configured too. `undefined` entries fall back to the omero channel
|
|
498
|
+
* color, as does the default, and to white if the metadata has none. Ignored
|
|
499
|
+
* for channels rendered with a LUT or color map.
|
|
500
|
+
*/
|
|
501
|
+
color?: Color | (Color | undefined)[];
|
|
502
|
+
/**
|
|
503
|
+
* Color LUT or color map to render the channels ({@link c}) with, overriding
|
|
504
|
+
* the omero channel LUTs and color maps.
|
|
505
|
+
*
|
|
506
|
+
* A single LUT or color map applies to all rendered channels; arrays must
|
|
507
|
+
* have one entry per rendered channel, which is only checked against
|
|
508
|
+
* {@link c} if that is configured too. `undefined` entries fall back to the
|
|
509
|
+
* omero channel LUT or color map, as does the default, and to rendering with
|
|
510
|
+
* {@link color} if the metadata has none. A color map also overrides
|
|
511
|
+
* {@link range} and {@link inverted}.
|
|
512
|
+
*/
|
|
513
|
+
lutOrColorMap?: LUTOrColorMap | (LUTOrColorMap | undefined)[];
|
|
514
|
+
/**
|
|
515
|
+
* Whether to invert the channels ({@link c}), overriding the omero channel
|
|
516
|
+
* inversion.
|
|
517
|
+
*
|
|
518
|
+
* A single value applies to all rendered channels; arrays must have one
|
|
519
|
+
* entry per rendered channel, which is only checked against {@link c} if
|
|
520
|
+
* that is configured too. `undefined` entries fall back to the omero channel
|
|
521
|
+
* inversion, as does the default, and to `false` if the metadata has none.
|
|
522
|
+
* Ignored for channels rendered with a color map.
|
|
523
|
+
*/
|
|
524
|
+
inverted?: boolean | (boolean | undefined)[];
|
|
525
|
+
/**
|
|
526
|
+
* Boost the brightness of dark tiles (ome-zarr.js `renderChunks` option).
|
|
527
|
+
*
|
|
528
|
+
* Defaults to `false`.
|
|
529
|
+
*/
|
|
57
530
|
autoBoost?: boolean;
|
|
58
531
|
}
|
|
59
532
|
|
|
533
|
+
/**
|
|
534
|
+
* Resolves a URL against the document base URL.
|
|
535
|
+
*
|
|
536
|
+
* @param url - URL of the OME-Zarr image, as a string or a `URL`
|
|
537
|
+
* @returns The absolute URL
|
|
538
|
+
* @throws If the URL is relative and there is no document base URL
|
|
539
|
+
*/
|
|
540
|
+
export declare function resolveUrl(url: string | URL): URL;
|
|
541
|
+
|
|
60
542
|
export { }
|
|
@@ -2,43 +2,158 @@ import e from "@zarrita/storage/zip";
|
|
|
2
2
|
import { NgffImage as t, getSlices as n, renderChunks as r } from "ome-zarr.js";
|
|
3
3
|
import i from "openseadragon";
|
|
4
4
|
import * as a from "zarrita";
|
|
5
|
+
//#region src/utils.ts
|
|
6
|
+
function o(e) {
|
|
7
|
+
let t = 2166136261;
|
|
8
|
+
for (let n = 0; n < e.length; n++) t = Math.imul(t ^ e.charCodeAt(n), 16777619);
|
|
9
|
+
return t >>> 0;
|
|
10
|
+
}
|
|
11
|
+
function s(e) {
|
|
12
|
+
return new URL(e, globalThis.document?.baseURI);
|
|
13
|
+
}
|
|
14
|
+
function c(e) {
|
|
15
|
+
try {
|
|
16
|
+
return s(e).pathname.endsWith(".ozx");
|
|
17
|
+
} catch {
|
|
18
|
+
return e.toString().endsWith(".ozx");
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function l(e) {
|
|
22
|
+
let { data: t } = e;
|
|
23
|
+
return t instanceof Int8Array ? [-128, 127] : t instanceof Uint8Array ? [0, 255] : t instanceof Int16Array ? [-32768, 32767] : t instanceof Uint16Array ? [0, 65535] : t instanceof Int32Array ? [-2147483648, 2147483647] : t instanceof Uint32Array ? [0, 4294967295] : t instanceof BigInt64Array ? [-(2 ** 63), 2 ** 63 - 1] : t instanceof BigUint64Array ? [0, 2 ** 64 - 1] : [0, 1];
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
5
26
|
//#region src/OMEZarrTileSource.ts
|
|
6
|
-
var
|
|
7
|
-
zip;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
27
|
+
var u = class u extends i.TileSource {
|
|
28
|
+
zip = !1;
|
|
29
|
+
_t;
|
|
30
|
+
_z;
|
|
31
|
+
_c;
|
|
32
|
+
_renderSettings;
|
|
33
|
+
_renderSettingsHash;
|
|
13
34
|
width = 10;
|
|
14
35
|
height = 10;
|
|
15
|
-
|
|
16
|
-
_arrays;
|
|
36
|
+
_loaded;
|
|
17
37
|
_readyPromise = new Promise((e, t) => {
|
|
18
38
|
this.addOnceHandler("ready", () => e(this)), this.addOnceHandler("open-failed", (e) => t(Error(e.message)));
|
|
19
39
|
});
|
|
20
40
|
static {
|
|
21
|
-
|
|
41
|
+
u._learnConverters(i);
|
|
42
|
+
}
|
|
43
|
+
static enable(e = i) {
|
|
44
|
+
Object.assign(e, { OMEZarrTileSource: u }), u._learnConverters(e);
|
|
45
|
+
}
|
|
46
|
+
static async loadOMEZarr(n, r, i) {
|
|
47
|
+
let { signal: a } = i ?? {};
|
|
48
|
+
a?.throwIfAborted(), n = s(n), r ??= c(n);
|
|
49
|
+
let o = await t.load(r ? e.fromUrl(n) : n.toString(), { signal: a });
|
|
50
|
+
return {
|
|
51
|
+
image: o,
|
|
52
|
+
arrays: await Promise.all(o.paths.map((e) => o.openArray(e, { signal: a })))
|
|
53
|
+
};
|
|
22
54
|
}
|
|
23
|
-
static open(e, t) {
|
|
24
|
-
|
|
55
|
+
static async open(e, t, n) {
|
|
56
|
+
let { signal: r } = n ?? {};
|
|
57
|
+
return r?.throwIfAborted(), t ??= typeof e == "string" || e instanceof URL ? await u.loadOMEZarr(e, void 0, { signal: r }) : await u.loadOMEZarr(e.url, e.zip, { signal: r }), new u(e, t).whenReady();
|
|
58
|
+
}
|
|
59
|
+
static render(e) {
|
|
60
|
+
let { chunks: t, ranges: n, colors: i, lutsOrColorMaps: a, inverteds: o, autoBoost: s } = e, c = t[0].shape[1], l = t[0].shape[0], u = document.createElement("canvas");
|
|
61
|
+
u.width = c, u.height = l;
|
|
62
|
+
let d = u.getContext("2d");
|
|
63
|
+
if (d === null) throw Error("failed to get 2D canvas context");
|
|
64
|
+
let f = r(t, n, i, a.map((e) => Array.isArray(e) ? [...e] : e), o, s), p = new ImageData(f, c, l);
|
|
65
|
+
return d.putImageData(p, 0, 0), d;
|
|
25
66
|
}
|
|
26
67
|
constructor(e, t) {
|
|
27
|
-
typeof e == "string"
|
|
68
|
+
let n = typeof e == "string" || e instanceof URL ? { url: e } : e, r = s(n.url), i;
|
|
69
|
+
if (n.c !== void 0) if (Array.isArray(n.c)) {
|
|
70
|
+
if (n.c.length === 0) throw Error("c array must be non-empty");
|
|
71
|
+
i = n.c;
|
|
72
|
+
} else i = [n.c];
|
|
73
|
+
let a = u._normalize(n, "range", (e) => Array.isArray(e) && e.length === 2 && e.every((e) => typeof e == "number"), i), l = u._normalize(n, "color", (e) => Array.isArray(e) && e.length === 3 && e.every((e) => typeof e == "number"), i), d = u._normalize(n, "lutOrColorMap", (e) => e instanceof Map || Array.isArray(e) && e.length > 0 && e.every((e) => Array.isArray(e) && (e.length === 3 || e.length === 4) && e.every((e) => typeof e == "number")), i), f = u._normalize(n, "inverted", (e) => typeof e == "boolean", i);
|
|
74
|
+
super(r.toString()), this.url = r.toString(), this.zip = n.zip ?? c(r), this._t = n.t, this._z = n.z, this._c = i, this._renderSettings = {
|
|
75
|
+
ranges: a,
|
|
76
|
+
colors: l,
|
|
77
|
+
lutsOrColorMaps: d,
|
|
78
|
+
inverteds: f,
|
|
79
|
+
autoBoost: n.autoBoost ?? !1
|
|
80
|
+
}, this._renderSettingsHash = o(JSON.stringify({
|
|
81
|
+
...this._renderSettings,
|
|
82
|
+
lutsOrColorMaps: d?.map((e) => e instanceof Map ? [...e].sort(([e], [t]) => e - t) : e)
|
|
83
|
+
})), this._loaded = t, this._readyPromise.catch(() => {});
|
|
84
|
+
}
|
|
85
|
+
get loaded() {
|
|
86
|
+
if (this._loaded === void 0) throw Error("tile source not ready");
|
|
87
|
+
return this._loaded;
|
|
88
|
+
}
|
|
89
|
+
get t() {
|
|
90
|
+
return this._t === void 0 ? this._loaded?.image.omero?.rdefs?.defaultT : this._t;
|
|
91
|
+
}
|
|
92
|
+
get z() {
|
|
93
|
+
return this._z === void 0 ? this._loaded?.image.omero?.rdefs?.defaultZ : this._z;
|
|
94
|
+
}
|
|
95
|
+
get cs() {
|
|
96
|
+
return this._c === void 0 ? this._loaded?.image.omero?.channels?.flatMap((e, t) => e.active === !1 ? [] : [t]) : this._c;
|
|
97
|
+
}
|
|
98
|
+
get channels() {
|
|
99
|
+
return this._c === void 0 ? this._loaded?.image.omero?.channels?.filter((e) => e.active !== !1) : this._c.map((e) => this._loaded?.image.omero?.channels?.[e]);
|
|
100
|
+
}
|
|
101
|
+
get ranges() {
|
|
102
|
+
let e = this.channels, t = (e) => {
|
|
103
|
+
if (e?.window.start !== void 0 && e.window.end !== void 0) return [e.window.start, e.window.end];
|
|
104
|
+
};
|
|
105
|
+
return u._repeat(this._renderSettings.ranges, e)?.map((n, r) => n ?? t(e?.[r]));
|
|
106
|
+
}
|
|
107
|
+
get colors() {
|
|
108
|
+
let e = this.channels, t = (e) => {
|
|
109
|
+
if (e === void 0) return;
|
|
110
|
+
let t = e.color.replace(/^#/, "");
|
|
111
|
+
return [
|
|
112
|
+
parseInt(t.slice(0, 2), 16),
|
|
113
|
+
parseInt(t.slice(2, 4), 16),
|
|
114
|
+
parseInt(t.slice(4, 6), 16)
|
|
115
|
+
];
|
|
116
|
+
};
|
|
117
|
+
return u._repeat(this._renderSettings.colors, e)?.map((n, r) => n ?? t(e?.[r]));
|
|
118
|
+
}
|
|
119
|
+
get lutsOrColorMaps() {
|
|
120
|
+
let e = this.channels, t = (e) => e?.lut ?? e?.colorMap;
|
|
121
|
+
return u._repeat(this._renderSettings.lutsOrColorMaps, e)?.map((n, r) => n ?? t(e?.[r]));
|
|
28
122
|
}
|
|
29
|
-
get
|
|
30
|
-
|
|
31
|
-
return this.
|
|
123
|
+
get inverteds() {
|
|
124
|
+
let e = this.channels;
|
|
125
|
+
return u._repeat(this._renderSettings.inverteds, e)?.map((t, n) => t ?? e?.[n]?.inverted);
|
|
32
126
|
}
|
|
33
|
-
get
|
|
34
|
-
|
|
35
|
-
return this._arrays;
|
|
127
|
+
get autoBoost() {
|
|
128
|
+
return this._renderSettings.autoBoost;
|
|
36
129
|
}
|
|
37
130
|
whenReady() {
|
|
38
131
|
return this._readyPromise;
|
|
39
132
|
}
|
|
133
|
+
getWidth(e = this.maxLevel) {
|
|
134
|
+
if (e < 0 || e > this.maxLevel) throw Error("level out of bounds");
|
|
135
|
+
return u._getAxisSize(this.loaded, "x", this.maxLevel - e);
|
|
136
|
+
}
|
|
137
|
+
getHeight(e = this.maxLevel) {
|
|
138
|
+
if (e < 0 || e > this.maxLevel) throw Error("level out of bounds");
|
|
139
|
+
return u._getAxisSize(this.loaded, "y", this.maxLevel - e);
|
|
140
|
+
}
|
|
141
|
+
loadChunks(e, t, r) {
|
|
142
|
+
let i = {
|
|
143
|
+
t: this.t,
|
|
144
|
+
z: this.z
|
|
145
|
+
};
|
|
146
|
+
if (t !== void 0) {
|
|
147
|
+
let { x: n, y: r } = this.getNumTiles(e);
|
|
148
|
+
if (t.x < 0 || t.x >= n || t.y < 0 || t.y >= r) throw Error("tile out of bounds");
|
|
149
|
+
let a = this.getTileWidth(e), o = this.getTileHeight(e);
|
|
150
|
+
i.x = [t.x * a, (t.x + 1) * a], i.y = [t.y * o, (t.y + 1) * o];
|
|
151
|
+
}
|
|
152
|
+
let o = u._getAxisSize(this.loaded, "c"), s = n(this.cs ?? Array.from({ length: o }, (e, t) => t), this.loaded.arrays[this.maxLevel - e].shape, this.loaded.image.getAxesNames(), i);
|
|
153
|
+
return Promise.all(s.map((t) => a.get(this.loaded.arrays[this.maxLevel - e], t, r)));
|
|
154
|
+
}
|
|
40
155
|
supports(e) {
|
|
41
|
-
return Array.isArray(e) || e instanceof Document ? !1 : typeof e == "string" ? e
|
|
156
|
+
return Array.isArray(e) || e instanceof Document ? !1 : typeof e == "string" ? c(e) : "type" in e && e.type === "ome-zarr";
|
|
42
157
|
}
|
|
43
158
|
configure(e, t, n = null) {
|
|
44
159
|
if (Array.isArray(e)) throw Error("configuration from array is not supported");
|
|
@@ -53,11 +168,11 @@ var o = class o extends i.TileSource {
|
|
|
53
168
|
};
|
|
54
169
|
}
|
|
55
170
|
equals(e) {
|
|
56
|
-
return e instanceof
|
|
171
|
+
return e instanceof u && this.url === e.url && this.zip === e.zip && this.t === e.t && this.z === e.z && this.cs?.join(",") === e.cs?.join(",") && this._renderSettingsHash === e._renderSettingsHash;
|
|
57
172
|
}
|
|
58
|
-
getImageInfo(
|
|
59
|
-
Promise.resolve(this.
|
|
60
|
-
let t = e.getAxesNames();
|
|
173
|
+
getImageInfo(e) {
|
|
174
|
+
Promise.resolve(this._loaded ?? u.loadOMEZarr(e, this.zip)).then((e) => {
|
|
175
|
+
let t = e.image.getAxesNames();
|
|
61
176
|
for (let e of t) if (![
|
|
62
177
|
"t",
|
|
63
178
|
"c",
|
|
@@ -67,38 +182,39 @@ var o = class o extends i.TileSource {
|
|
|
67
182
|
].includes(e)) throw Error(`unsupported axis: ${e}`);
|
|
68
183
|
if (!t.includes("x") || !t.includes("y")) throw Error("missing X or Y axis");
|
|
69
184
|
if (t.indexOf("y") > t.indexOf("x")) throw Error("X axis must come after Y axis");
|
|
70
|
-
let n =
|
|
71
|
-
if (this.
|
|
72
|
-
let
|
|
73
|
-
if (this.
|
|
74
|
-
let
|
|
75
|
-
if (this.
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
this.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
message: `failed to get image info for ${n}: ${e}`,
|
|
84
|
-
source: n
|
|
185
|
+
let n = u._getAxisSize(e, "t");
|
|
186
|
+
if (this._t !== void 0 && (this._t < 0 || this._t >= n)) throw Error(`Invalid t index ${this._t} for image with ${n} timepoints`);
|
|
187
|
+
let r = u._getAxisSize(e, "z");
|
|
188
|
+
if (this._z !== void 0 && (this._z < 0 || this._z >= r)) throw Error(`Invalid z index ${this._z} for image with ${r} z-slices`);
|
|
189
|
+
let i = u._getAxisSize(e, "c");
|
|
190
|
+
if (this._c !== void 0 && this._c.some((e) => e < 0 || e >= i)) throw Error(`Invalid c indices [${this._c?.join(", ")}] for image with ${i} channels`);
|
|
191
|
+
if (e.image.omero !== void 0 && e.image.omero.channels !== void 0 && e.image.omero.channels.length !== i) throw Error(`OME-Zarr metadata lists ${e.image.omero.channels.length} channels, but the image has ${i}`);
|
|
192
|
+
if (this._c === void 0 && e.image.omero !== void 0 && e.image.omero.channels !== void 0 && !e.image.omero.channels.some((e) => e.active !== !1)) throw Error("No active channels; specify c or activate channels in the OME-Zarr metadata");
|
|
193
|
+
this._loaded = e, this.width = e.arrays[0].shape[t.indexOf("x")], this.height = e.arrays[0].shape[t.indexOf("y")], this.maxLevel = e.arrays.length - 1, this.raiseEvent("ready", { tileSource: this });
|
|
194
|
+
}).catch((t) => {
|
|
195
|
+
this._loaded = void 0, this.width = 10, this.height = 10, this.maxLevel = 0, this.raiseEvent("open-failed", {
|
|
196
|
+
message: `failed to get image info for ${e}: ${t}`,
|
|
197
|
+
source: e
|
|
85
198
|
});
|
|
86
199
|
});
|
|
87
200
|
}
|
|
88
201
|
getTileWidth(e) {
|
|
89
|
-
|
|
202
|
+
let { image: t, arrays: n } = this.loaded;
|
|
90
203
|
if (e < 0 || e > this.maxLevel) throw Error("level out of bounds");
|
|
91
|
-
return
|
|
204
|
+
return n[this.maxLevel - e].chunks[t.getAxesNames().indexOf("x")];
|
|
92
205
|
}
|
|
93
206
|
getTileHeight(e) {
|
|
94
|
-
|
|
207
|
+
let { image: t, arrays: n } = this.loaded;
|
|
95
208
|
if (e < 0 || e > this.maxLevel) throw Error("level out of bounds");
|
|
96
|
-
return
|
|
209
|
+
return n[this.maxLevel - e].chunks[t.getAxesNames().indexOf("y")];
|
|
97
210
|
}
|
|
98
211
|
getLevelScale(e) {
|
|
99
|
-
if (this._image === void 0 || this._arrays === void 0) throw Error("tile source not ready");
|
|
100
212
|
if (e < 0 || e > this.maxLevel) throw Error("level out of bounds");
|
|
101
|
-
return this.
|
|
213
|
+
return this.getWidth(e) / this.width;
|
|
214
|
+
}
|
|
215
|
+
getNumTiles(e) {
|
|
216
|
+
if (e < 0 || e > this.maxLevel) throw Error("level out of bounds");
|
|
217
|
+
return new i.Point(Math.ceil(this.getWidth(e) / this.getTileWidth(e)), Math.ceil(this.getHeight(e) / this.getTileHeight(e)));
|
|
102
218
|
}
|
|
103
219
|
getTileUrl(e, t, n) {
|
|
104
220
|
let r = new URLSearchParams();
|
|
@@ -106,84 +222,77 @@ var o = class o extends i.TileSource {
|
|
|
106
222
|
}
|
|
107
223
|
getTileHashKey(e, t, n) {
|
|
108
224
|
let r = new URL(this.url);
|
|
109
|
-
return r.searchParams.append("
|
|
225
|
+
return r.searchParams.append("zip", this.zip.toString()), this.t !== void 0 && r.searchParams.append("t", this.t.toString()), this.z !== void 0 && r.searchParams.append("z", this.z.toString()), this.cs !== void 0 && r.searchParams.append("c", this.cs.join(",")), r.searchParams.append("y", n.toString()), r.searchParams.append("x", t.toString()), r.searchParams.append("level", e.toString()), r.searchParams.append("render", this._renderSettingsHash.toString(16)), r.toString();
|
|
110
226
|
}
|
|
111
227
|
downloadTileStart(e) {
|
|
112
|
-
let t = e.userData,
|
|
113
|
-
t.abortController =
|
|
114
|
-
let
|
|
228
|
+
let t = e.userData, n = new AbortController();
|
|
229
|
+
t.abortController = n;
|
|
230
|
+
let r = new URLSearchParams(e.src), i = +r.get("level"), a = +r.get("x"), o = +r.get("y");
|
|
115
231
|
try {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
r
|
|
232
|
+
this.loadChunks(i, {
|
|
233
|
+
x: a,
|
|
234
|
+
y: o
|
|
235
|
+
}, { signal: n.signal }).then((n) => {
|
|
236
|
+
let r = this.ranges, i = this.colors, a = this.inverteds, o = this.lutsOrColorMaps, s = this.autoBoost, c = {
|
|
237
|
+
chunks: n,
|
|
238
|
+
ranges: n.map((e, t) => {
|
|
239
|
+
let [n, i] = r?.[t] ?? l(e);
|
|
240
|
+
return n < i ? [n, i] : [n, n + 1];
|
|
241
|
+
}),
|
|
242
|
+
colors: n.map((e, t) => i?.[t] ?? [
|
|
243
|
+
255,
|
|
244
|
+
255,
|
|
245
|
+
255
|
|
246
|
+
]),
|
|
247
|
+
lutsOrColorMaps: n.map((e, t) => o?.[t]),
|
|
248
|
+
inverteds: n.map((e, t) => a?.[t] ?? !1),
|
|
249
|
+
autoBoost: s
|
|
250
|
+
};
|
|
251
|
+
t.abortController = void 0, e.finish(c, null, "ome-zarr");
|
|
252
|
+
}, (t) => {
|
|
253
|
+
let r = n.signal.aborted;
|
|
254
|
+
n.abort(), r || e.fail(`failed to load chunks for level=${i}, x=${a}, y=${o}: ${String(t)}`, null);
|
|
138
255
|
});
|
|
139
256
|
} catch (t) {
|
|
140
|
-
e.fail(`failed to download tile for level=${
|
|
257
|
+
e.fail(`failed to download tile for level=${i}, x=${a}, y=${o}: ${String(t)}`, null);
|
|
141
258
|
}
|
|
142
259
|
}
|
|
143
260
|
downloadTileAbort(e) {
|
|
144
261
|
let t = e.userData;
|
|
145
262
|
t.abortController !== void 0 && (t.abortController.abort(), t.abortController = void 0);
|
|
146
263
|
}
|
|
147
|
-
static
|
|
148
|
-
|
|
264
|
+
static _normalize(e, t, n, r) {
|
|
265
|
+
let i = e[t];
|
|
266
|
+
if (i !== void 0) {
|
|
267
|
+
if (n(i)) {
|
|
268
|
+
let e = i;
|
|
269
|
+
return r === void 0 ? [e] : r.map(() => e);
|
|
270
|
+
}
|
|
271
|
+
if (!Array.isArray(i) || !i.every((e) => e === void 0 || n(e))) throw Error(`${t} must be a value or an array of values`);
|
|
272
|
+
if (i.length === 0) throw Error(`${t} array must be non-empty`);
|
|
273
|
+
if (r !== void 0 && i.length !== r.length) throw Error(`${t} array length ${i.length} does not match number of channels ${r.length} in c`);
|
|
274
|
+
return i;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
static _repeat(e, t) {
|
|
278
|
+
return e === void 0 ? t?.map(() => void 0) : e.length === 1 && t !== void 0 ? t.map(() => e[0]) : e;
|
|
149
279
|
}
|
|
150
280
|
static _learnConverters(e) {
|
|
151
|
-
e.converter.existsType("ome-zarr") || (e.converter.learn("ome-zarr", "context2d", (e,
|
|
281
|
+
e.converter.existsType("ome-zarr") || (e.converter.learn("ome-zarr", "context2d", (e, t) => u.render(t), 1, 1), e.converter.learn("ome-zarr", "ome-zarr", (e, t) => ({
|
|
152
282
|
...t,
|
|
153
|
-
|
|
154
|
-
...
|
|
155
|
-
data:
|
|
156
|
-
}
|
|
283
|
+
chunks: t.chunks.map((e) => ({
|
|
284
|
+
...e,
|
|
285
|
+
data: e.data.slice()
|
|
286
|
+
}))
|
|
157
287
|
})));
|
|
158
288
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
static _render(e, t, n) {
|
|
163
|
-
let i = t.map((e) => {
|
|
164
|
-
let t = e.color.replace(/^#/, "");
|
|
165
|
-
return [
|
|
166
|
-
parseInt(t.slice(0, 2), 16),
|
|
167
|
-
parseInt(t.slice(2, 4), 16),
|
|
168
|
-
parseInt(t.slice(4, 6), 16)
|
|
169
|
-
];
|
|
170
|
-
}), a = r(e, t.map((t, n) => {
|
|
171
|
-
let [r, i] = t.window.start !== void 0 && t.window.end !== void 0 ? [t.window.start, t.window.end] : o._getDataTypeRange(e[n]);
|
|
172
|
-
return r < i ? [r, i] : [r, r + 1];
|
|
173
|
-
}), i, t.map((e) => e.lut ?? e.colorMap), t.map((e) => e.inverted === !0), n), s = e[0].shape[1], c = e[0].shape[0], l = document.createElement("canvas");
|
|
174
|
-
l.width = s, l.height = c;
|
|
175
|
-
let u = l.getContext("2d");
|
|
176
|
-
if (u === null) throw Error("failed to get 2D canvas context");
|
|
177
|
-
let d = new ImageData(a, s, c);
|
|
178
|
-
return u.putImageData(d, 0, 0), u;
|
|
179
|
-
}
|
|
180
|
-
static _getDataTypeRange(e) {
|
|
181
|
-
let { data: t } = e;
|
|
182
|
-
return t instanceof Int8Array ? [-128, 127] : t instanceof Uint8Array ? [0, 255] : t instanceof Int16Array ? [-32768, 32767] : t instanceof Uint16Array ? [0, 65535] : t instanceof Int32Array ? [-2147483648, 2147483647] : t instanceof Uint32Array ? [0, 4294967295] : t instanceof BigInt64Array ? [-(2 ** 63), 2 ** 63 - 1] : t instanceof BigUint64Array ? [0, 2 ** 64 - 1] : [0, 1];
|
|
289
|
+
static _getAxisSize(e, t, n = 0) {
|
|
290
|
+
let r = e.image.getAxesNames().indexOf(t);
|
|
291
|
+
return r >= 0 ? e.arrays[n].shape[r] : 1;
|
|
183
292
|
}
|
|
184
293
|
};
|
|
185
294
|
//#endregion
|
|
186
295
|
//#region src/main.ts
|
|
187
|
-
globalThis.OpenSeadragon &&
|
|
296
|
+
globalThis.OpenSeadragon && u.enable(globalThis.OpenSeadragon);
|
|
188
297
|
//#endregion
|
|
189
|
-
export {
|
|
298
|
+
export { u as OMEZarrTileSource, o as fnv1a, l as getDataTypeRange, c as isOZX, s as resolveUrl };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omezarr-tilesource",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "An OpenSeadragon tile source for the OME-Zarr bioimage file format",
|
|
5
5
|
"homepage": "https://github.com/TissUUmaps/OMEZarrTileSource#readme",
|
|
6
6
|
"bugs": "https://github.com/TissUUmaps/OMEZarrTileSource/issues",
|