Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c4f6abe

Browse files
committedMar 4, 2025·
Fallback to default LUT if colors exceed max uniform size
1 parent 77c2e32 commit c4f6abe

File tree

1 file changed

+36
-17
lines changed

1 file changed

+36
-17
lines changed
 

‎src/layers/label-layer.ts

+36-17
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export class LabelLayer extends TileLayer<LabelPixelData> {
7171
pixelData: data,
7272
opacity: props.opacity,
7373
modelMatrix: props.modelMatrix,
74-
lut: createColorLookupTable({ source: props.lut }),
74+
lut: createColorLookupTable({ source: props.lut, fallback: DEFAULT_PALETTE }),
7575
bounds: [clamp(left, 0, width), clamp(top, 0, height), clamp(right, 0, width), clamp(bottom, 0, height)],
7676
// For underlying class
7777
image: new ImageData(data.width, data.height),
@@ -192,27 +192,44 @@ function getTileSizeForResolutions(resolutions: Array<ZarrPixelSource>): number
192192
return tileSize;
193193
}
194194

195+
// Approx. Limit for WebGL2
196+
const MAX_UNIFORM_VEC3 = 1365;
197+
const SEEN_LUTS = new WeakSet<LabelLayerLut>();
198+
199+
/**
200+
* Creates a color lookup table (LUT) as a Float32Array.
201+
* Falls back to a default LUT if the source is missing or exceeds the uniform size limit.
202+
*
203+
* @param options.source - The source lookup table.
204+
* @param options.fallback - The fallback LUT to use if the source is invalid.
205+
* @returns {Float32Array | undefined} The generated LUT or the fallback if the source is too large.
206+
*/
195207
function createColorLookupTable(options: {
196208
source?: LabelLayerLut;
197-
palette?: ReadonlyArray<readonly [number, number, number]>;
209+
fallback: Float32Array;
198210
}): Float32Array {
199-
const { source, palette = COLOR_PALETTES.pathology } = options;
200-
if (source) {
201-
const values = Object.keys(source).map((value) => +value);
202-
// This could blow up in size, should we worry about it?
203-
// What about alpha?
204-
const lut = new Float32Array(Math.max(...values) * 3);
205-
for (let [value, color] of Object.entries(source)) {
206-
const i = +value;
207-
lut[i * 3 + 0] = color[0];
208-
lut[i * 3 + 1] = color[1];
209-
lut[i * 3 + 2] = color[2];
211+
const { source, fallback } = options;
212+
if (!source) {
213+
return fallback;
214+
}
215+
const values = Object.keys(source).map((value) => +value);
216+
const size = Math.max(...values);
217+
if (size >= MAX_UNIFORM_VEC3) {
218+
if (!SEEN_LUTS.has(source)) {
219+
console.warn("[vizarr] Skipping color palette from OME-NGFF `image-label` source: exceeds uniform size limit.");
220+
SEEN_LUTS.add(source);
210221
}
211-
return lut;
222+
return fallback;
212223
}
213-
214-
// generate a random categorical palette
215-
return Float32Array.from(palette.flat(), (d) => d / 255.0);
224+
// Either empty source or larger than what we can handle with WebGL uniforms
225+
const lut = new Float32Array(size * 3);
226+
for (let [value, color] of Object.entries(source)) {
227+
const i = +value;
228+
lut[i * 3 + 0] = color[0];
229+
lut[i * 3 + 1] = color[1];
230+
lut[i * 3 + 2] = color[2];
231+
}
232+
return lut;
216233
}
217234

218235
const COLOR_PALETTES = {
@@ -267,6 +284,8 @@ const COLOR_PALETTES = {
267284
],
268285
} as const;
269286

287+
const DEFAULT_PALETTE = Float32Array.from(COLOR_PALETTES.pathology.flat(), (d) => d / 255);
288+
270289
function typedArrayConstructorName(arr: zarr.TypedArray<LabelDataType>) {
271290
const ArrayType = arr.constructor as zarr.TypedArrayConstructor<LabelDataType>;
272291
const name = ArrayType.name as `${Capitalize<LabelDataType>}Array`;

0 commit comments

Comments
 (0)
Please sign in to comment.