Skip to content

fix(viewer): warn upfront when a NIfTI volume is too large to display (#228)#256

Merged
korbinian90 merged 1 commit into
mainfrom
claude/unruffled-ptolemy-8e6e67
Jun 20, 2026
Merged

fix(viewer): warn upfront when a NIfTI volume is too large to display (#228)#256
korbinian90 merged 1 commit into
mainfrom
claude/unruffled-ptolemy-8e6e67

Conversation

@korbinian90

Copy link
Copy Markdown
Collaborator

Problem

Opening high-resolution anatomical data (>2 GB uncompressed) failed badly (#228):

  • VS Code: Array buffer allocation failed.
  • Online viewer: blank/garbage values when moving around, with niivue-error blur shader / sobel shader console errors.

Root cause: a single browser ArrayBuffer / WebGL texture is capped at ~2 GB, so a volume whose uncompressed voxel data exceeds that cannot be allocated or uploaded. The viewer tried anyway, slowly, then failed or rendered garbage.

Fix

Detect the oversized case from the NIfTI header and show an actionable warning before any allocation, instead of loading slowly into a blank canvas or a cryptic error.

.nii.gz is taken into account: the check inflates only the first ~64 KB of the gzip stream to read the header, so it uses the true uncompressed size, not the compressed file size on disk.

Where the guard runs (all in @niivue/react, the shared webview layer every host uses)

  • nifti.ts (new): parses NIfTI-1/2 headers (endian-aware, int64 dims) into product(dim) * bitpix / 8, inflates the gzip header prefix, and returns a warning when voxels exceed 2 GiB. Fail-open: any uncertainty returns null, so normal loads proceed and genuine errors still surface.
  • buildImageMessageBodies (browser file picker / drag-drop): peeks the header via file.slice(), so an oversized local file is never read into memory at all.
  • loadVolume (universal net): guards in-memory buffers (Jupyter, Streamlit, VS Code binary) and URL streams alike. VS Code local files stream by URL with no data buffer, so that path fetches only the header and aborts the rest of the download.
  • Volume.tsx: the oversized case renders an amber "Image too large to display" overlay with the size and a downsample/crop hint.

Testing

  • Unit: new nifti.test.ts (16 cases: NIfTI-1/2 parsing, endianness, gzip header inflation including a truncated prefix, the size boundary) and new oversized-guard cases in utility.test.ts (including proof the whole file is never read). Full @niivue/react suite: 161 passing.
  • e2e (Chromium): new oversized-NIfTI test in ErrorHandling.spec.ts; Images / Meshes / Overlays / 4D-navigation still pass, confirming normal and 4D volumes load and the added header-fetch does not regress the URL path.
  • turbo type-check 8/8 and turbo lint 10/10.

Notes for reviewers

  • The 2 GB threshold matches the empirical boundary in the issue (1.9 GB loads, >2 GB fails) and the texture-buffer framing. It checks raw voxel bytes; if NiiVue later expands narrow datatypes to float32 for the texture, the effective limit is lower, easy to tighten by multiplying out to the component size.
  • The guard intentionally lives in the webview layer rather than the VS Code extension: vscode.workspace.fs has no partial read and the extension also targets web (no Node fs), so a header-only .nii.gz peek is not possible extension-side. The webview header-fetch covers VS Code local + remote and .nii / .nii.gz uniformly.

Closes #228

🤖 Generated with Claude Code

…#228)

High-resolution volumes whose uncompressed voxel data exceeds the ~2 GB
browser ArrayBuffer/WebGL-texture limit previously failed with "Array
buffer allocation failed" (VS Code) or rendered as garbage with blur/sobel
shader errors (online viewer) after a slow load. Detect this from the
NIfTI header and show an actionable "Image too large to display" warning
before any allocation.

- New @niivue/react nifti.ts computes the uncompressed voxel byte length
  from the NIfTI-1/2 header. For .nii.gz it inflates only the gzip header
  prefix, so the true uncompressed size is used, not the compressed file
  size on disk.
- buildImageMessageBodies peeks the header via file.slice(), so an
  oversized local file is never read into memory at all.
- loadVolume guards every other host: in-memory buffers (Jupyter,
  Streamlit, VS Code binary) and URL streams (VS Code local files, fetched
  header-only and then aborted).
- The on-canvas overlay shows an amber "Image too large to display"
  warning with the size and a downsample/crop hint.

Verified with new unit tests (nifti, oversized-guard) and a new e2e test,
plus turbo type-check (8/8) and lint (10/10).

Closes #228

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🚀 PWA Preview Deployment

Your PWA preview has been deployed!

Preview URL: https://niivue.github.io/niivue-vscode/pr-256/


This preview will be updated automatically when you push new commits to this PR.

github-actions Bot added a commit that referenced this pull request Jun 19, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report

coverage

Overall line coverage: 45.2% (+2.4) vs main

Package Statements Branches Functions Lines
Shared core (packages/niivue-react) 49.6% (+2.7) 49.8% (+1.7) 46.5% (+0.8) 50.2% (+2.8)
apps/pwa 28.3% 32.4% 52.9% 29.5%
apps/jupyter 14.4% 15.9% 14.9% 14.5%
apps/streamlit 17.8% 5.3% 18.5% 17.7%
apps/vscode 38.7% 39.6% 18.9% 38.1%
apps/desktop-tauri 81.8% 59.1% 78.9% 84.3%

📊 View full report →

@korbinian90 korbinian90 merged commit 1db38a8 into main Jun 20, 2026
13 checks passed
github-actions Bot added a commit that referenced this pull request Jun 20, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🧹 PWA Preview Cleanup

The preview deployment for this PR has been removed.

github-actions Bot added a commit that referenced this pull request Jun 20, 2026
korbinian90 added a commit that referenced this pull request Jun 20, 2026
Reconcile #256 (warn upfront when a NIfTI volume is too large to display,
issue #228) with the @niivue/niivue v1.0 core migration.

Resolution:
- utility.ts import conflict: the migration switched to the v1 default export
  (import NiiVue, plus `import type { NVImage }`); #256 added the nifti-guard
  import (isNiftiName, NIFTI_PEEK_BYTES, niftiTooLargeWarning from './nifti').
  Kept the migration's default-export form and folded in #256's nifti import.
- #256's header-peek guard (file.slice -> niftiTooLargeWarning, push an
  ImageMessageBody with loadError) lives in the shared file-reading loop and
  is unaffected by the migration's loader rewrite, which acts downstream in
  NiiVueCanvas/events. NiiVueCanvas.tsx, Volume.tsx and utility.test.ts
  auto-merged.

Verified (forced, uncached): turbo type-check 8/8, turbo build 8/8,
@niivue/react 179 unit tests pass (16 files, incl. #256's nifti suite). The
streamlit python test fails only on a missing local pytest (env, pre-existing).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Opening high resolution anatomical data (>2GB) fails to load and/or causes no visible values on the online viewer

1 participant