Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
# Change Log

## 1.8.0
## 1.9.0

- Add more compression support
- Add a Metadata panel

## 1.8.0 (2026-06-20)

- Support more tiff compression formats
- Improve colormap support
- Speed up loading and decoding by in average 30% by using WebGL2/GPU
- Use Rust/WASM for HDR, 16bit PNG, EXR and TIFF

## 1.7.0 (2026-06-14)

Expand Down
3 changes: 3 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,9 @@ The extension handles diverse image formats with minimal processor code. Each fo
- Detects actual bit depth and converts to Float32Array
- Uses LUT optimization for gamma/brightness in 8-bit mode

- **Raw**
- Ignore the implemented raw images support for now. It's too slow to be useful. Therefore we ignore it for now.

### Library Integration
- **geotiff.min.js**: Browser build automatically copied from node_modules during build
- **parse-exr.js**: Bundled with extension for OpenEXR support
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ The viewer supports 8-bit and 16-bit integer images as well as 16-bit and 32-bit
- **Session-Wide Settings**: A single VS Code window keeps visualization settings across opened images.
- **Export and Copy**: Export the current visualization as PNG, copy the image, or copy image zoom level to the clipboard to paste onto other image.
- **VS Code Native Controls**: Most options are available from the right-click menu, command palette, or clickable status bar entries.
- **Metadata panel** shows file info, image statistics (min/max/mean/std) and Exif/GPS sub-IFD tags.

## How to Use

Expand Down
5 changes: 5 additions & 0 deletions media/decode-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import initTiffWasm, { decode_exr_fast, decode_hdr_fast, decode_png16_fast, deco
import { NpyProcessor } from './modules/npy-processor.js';
import { PfmProcessor } from './modules/pfm-processor.js';
import { PpmProcessor } from './modules/ppm-processor.js';
import { buildTagsFromGeotiffImage } from './modules/tiff-tag-utils.js';

// Parser-only instances: the constructors just assign fields, and the
// _parse* methods used here touch no DOM or vscode APIs.
Expand Down Expand Up @@ -170,6 +171,7 @@ function decodeTiffWasm(buffer) {
rasters,
min: result.min_value,
max: result.max_value,
allTagsJson: result.all_tags_json,
decodedWith: 'wasm (worker)',
decodeTimings: timings,
};
Expand Down Expand Up @@ -242,6 +244,7 @@ async function decodeTiffGeotiff(buffer, wasmError) {
planarConfiguration: fileDirectory.PlanarConfiguration || 1,
data,
rasters,
allTagsJson: JSON.stringify(buildTagsFromGeotiffImage(image)),
decodedWith: 'geotiff.js (worker)',
wasmFallbackReason: wasmError,
decodeTimings: timings,
Expand Down Expand Up @@ -303,6 +306,7 @@ function decodeExrWasm(buffer) {
displayedChannels,
shape: [result.width, result.height],
flipY: false,
allTagsJson: result.all_tags_json,
decodedWith: 'rust-exr-wasm (worker)',
decodeTimings: timings,
};
Expand Down Expand Up @@ -377,6 +381,7 @@ function decodeHdrWasm(buffer) {
exposure,
gamma,
data,
allTagsJson: result.all_tags_json,
decodedWith: 'rust-hdr-wasm (worker)',
decodeTimings: timings,
};
Expand Down
181 changes: 181 additions & 0 deletions media/imagePreview.css
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,183 @@ body img {
min-width: 0;
}

/* Metadata Panel */
.metadata-panel {
position: fixed;
top: 20px;
right: 20px;
background: var(--vscode-editor-background, #1e1e1e);
border: 1px solid var(--vscode-panel-border, #3c3c3c);
border-radius: 6px;
z-index: 1000;
font-family: var(--vscode-editor-font-family, 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif);
font-size: var(--vscode-editor-font-size, 12px);
color: var(--vscode-editor-foreground, #cccccc);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
display: flex;
flex-direction: column;
width: 340px;
max-width: 340px;
max-height: 70vh;
box-sizing: border-box;
}

.metadata-panel-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 12px;
background: var(--vscode-sideBarSectionHeader-background, #252526);
border-bottom: 1px solid var(--vscode-panel-border, #3c3c3c);
border-radius: 6px 6px 0 0;
gap: 8px;
flex: 0 0 auto;
}

.metadata-panel-title {
font-weight: 600;
font-size: 13px;
flex: 1;
}

.metadata-panel-button {
background: var(--vscode-button-secondaryBackground, #3a3a3a);
color: var(--vscode-button-secondaryForeground, #cccccc);
border: 1px solid var(--vscode-panel-border, #3c3c3c);
border-radius: 3px;
padding: 2px 8px;
font-size: 11px;
cursor: pointer;
white-space: nowrap;
}

.metadata-panel-button:hover {
background: var(--vscode-button-secondaryHoverBackground, #454545);
}

.metadata-panel-close {
background: transparent;
border: none;
color: var(--vscode-icon-foreground, #cccccc);
font-size: 20px;
line-height: 1;
cursor: pointer;
padding: 0;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 3px;
flex: 0 0 auto;
}

.metadata-panel-close:hover {
background: var(--vscode-toolbar-hoverBackground, #3a3a3a);
}

.metadata-panel-body {
overflow-y: auto;
padding: 4px 0;
}

.metadata-panel-empty {
padding: 12px;
font-size: 11px;
color: var(--vscode-descriptionForeground, #999999);
}

.metadata-panel-section {
border-bottom: 1px solid var(--vscode-panel-border, #3c3c3c);
}

.metadata-panel-section:last-child {
border-bottom: none;
}

.metadata-panel-section > summary {
padding: 6px 12px;
font-weight: 600;
font-size: 11px;
cursor: pointer;
user-select: none;
list-style: none;
}

.metadata-panel-section > summary::-webkit-details-marker {
display: none;
}

.metadata-panel-section > summary::before {
content: '▶';
display: inline-block;
margin-right: 6px;
font-size: 9px;
transition: transform 0.1s ease;
}

.metadata-panel-section[open] > summary::before {
transform: rotate(90deg);
}

.metadata-panel-section-content {
padding: 0 12px 8px;
font-family: var(--vscode-editor-font-family, 'Consolas', 'Monaco', monospace);
font-size: 11px;
}

.metadata-panel-row {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 10px;
padding: 2px 0;
}

.metadata-panel-row-name {
color: var(--vscode-descriptionForeground, #999999);
white-space: nowrap;
flex: 0 0 auto;
}

.metadata-panel-row-value {
text-align: right;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-width: 0;
}

.vscode-light .metadata-panel {
background: var(--vscode-editor-background, #ffffff);
border: 1px solid var(--vscode-panel-border, #e5e5e5);
color: var(--vscode-editor-foreground, #333333);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
}

.vscode-light .metadata-panel-header {
background: var(--vscode-sideBarSectionHeader-background, #f3f3f3);
border-color: var(--vscode-panel-border, #e5e5e5);
}

.vscode-light .metadata-panel-button {
background: var(--vscode-button-secondaryBackground, #e8e8e8);
color: var(--vscode-button-secondaryForeground, #333333);
border-color: var(--vscode-panel-border, #d0d0d0);
}

.vscode-light .metadata-panel-button:hover {
background: var(--vscode-button-secondaryHoverBackground, #d8d8d8);
}

.vscode-light .metadata-panel-close:hover {
background: var(--vscode-toolbar-hoverBackground, #e8e8e8);
}

.vscode-light .metadata-panel-section {
border-color: var(--vscode-panel-border, #e5e5e5);
}

/* Light theme adjustments for histogram */
.vscode-light .histogram-overlay {
background: var(--vscode-editor-background, #ffffff);
Expand Down Expand Up @@ -612,6 +789,10 @@ body img {
z-index: 3000;
min-width: 120px;
user-select: none;
/* Never let the menu exceed the viewport: scroll if it is too tall. */
max-height: calc(100vh - 16px);
overflow-y: auto;
overflow-x: hidden;
}

.context-menu-item {
Expand Down
Loading
Loading