plexora 0.0.1__py3-none-any.whl
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.
- plexora/__init__.py +129 -0
- plexora/__main__.py +15 -0
- plexora/_url.py +105 -0
- plexora/api/__init__.py +55 -0
- plexora/api/dataset.py +398 -0
- plexora/api/http.py +19 -0
- plexora/api/plugin.py +541 -0
- plexora/api/store.py +221 -0
- plexora/cli.py +777 -0
- plexora/client/dist/354_bundle.js +2 -0
- plexora/client/dist/354_bundle.js.LICENSE.txt +20 -0
- plexora/client/dist/418_bundle.js +2 -0
- plexora/client/dist/418_bundle.js.LICENSE.txt +1 -0
- plexora/client/dist/693b935cb1f907814be9c6199a68c217.svg +19 -0
- plexora/client/dist/713c134ff47cd328a5b711526f151082.svg +18 -0
- plexora/client/dist/770_bundle.js +1 -0
- plexora/client/dist/ff743f408972e0e96ca9153ed5ae4c83.svg +23 -0
- plexora/client/dist/vendor_bundle.js +2 -0
- plexora/client/dist/vendor_bundle.js.LICENSE.txt +308 -0
- plexora/client/external/openseadragon-bin-2.4.0/LICENSE.txt +28 -0
- plexora/client/external/openseadragon-bin-2.4.0/canvas-overlay-hd.js +140 -0
- plexora/client/external/openseadragon-bin-2.4.0/changelog.txt +501 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/.gitattributes +17 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/LICENSE.txt +116 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/Toolbar.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/fullpage.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/home.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/next.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/previous.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateleft.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/rotateright.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/Photoshop/zoom.psd +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/README.md +9 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/fullpage_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/home_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/next_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/previous_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateleft_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/rotateright_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomin_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_grouphover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_hover.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_pressed.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-flat-toolbar-icons-master/images/zoomout_rest.png +0 -0
- plexora/client/external/openseadragon-bin-2.4.0/openseadragon-scalebar.js +562 -0
- plexora/client/src/css/import.css +474 -0
- plexora/client/src/css/main.css +903 -0
- plexora/client/src/css/openProject.css +363 -0
- plexora/client/src/css/quickView.css +166 -0
- plexora/client/src/css/tokens.css +51 -0
- plexora/client/src/css/viewer.css +1655 -0
- plexora/client/src/img/apple-touch-icon.png +0 -0
- plexora/client/src/img/favicon.ico +0 -0
- plexora/client/src/img/logo.ai +8892 -33
- plexora/client/src/img/logo.svg +1 -0
- plexora/client/src/img/logo_with_text.ai +9011 -34
- plexora/client/src/img/logo_with_text.svg +1 -0
- plexora/client/src/js/main.js +704 -0
- plexora/client/src/js/pluginRegistry.js +108 -0
- plexora/client/src/js/services/appStatus.js +286 -0
- plexora/client/src/js/services/browsePicker.js +73 -0
- plexora/client/src/js/services/dataLayer.js +400 -0
- plexora/client/src/js/services/datasetContext.js +143 -0
- plexora/client/src/js/services/glRenderer.js +143 -0
- plexora/client/src/js/services/importFormValidation.js +257 -0
- plexora/client/src/js/services/numericData.js +100 -0
- plexora/client/src/js/services/passVariablesToFrontend.js +20 -0
- plexora/client/src/js/services/simpleEventHandler.js +25 -0
- plexora/client/src/js/vendor.js +29 -0
- plexora/client/src/js/views/channelList.js +663 -0
- plexora/client/src/js/views/colorSwatchPicker.js +202 -0
- plexora/client/src/js/views/columnClassifier.js +146 -0
- plexora/client/src/js/views/coordinateField.js +133 -0
- plexora/client/src/js/views/dataSourceField.js +276 -0
- plexora/client/src/js/views/imageViewer.js +3306 -0
- plexora/client/src/js/views/miniMap.js +787 -0
- plexora/client/src/js/views/navbarControls.js +178 -0
- plexora/client/src/js/views/openProjectPage.js +239 -0
- plexora/client/src/js/views/projectEdit.js +335 -0
- plexora/client/src/js/views/quickViewLanding.js +136 -0
- plexora/client/src/js/views/rainbow.js +326 -0
- plexora/client/src/js/views/requirementsModal.js +572 -0
- plexora/client/src/js/views/rgbImageViewer.js +115 -0
- plexora/client/src/js/views/searchableSelect.js +411 -0
- plexora/client/src/js/views/segmentationProgress.js +122 -0
- plexora/client/src/js/views/toolLoader.js +747 -0
- plexora/client/src/js/views/viewerControls.js +689 -0
- plexora/client/src/js/views/viewerManager.js +434 -0
- plexora/client/src/js/views/viewerSidebar.js +1117 -0
- plexora/client/src/js/workers/tileDecoder.js +130 -0
- plexora/client/src/shaders/frag.glsl +528 -0
- plexora/client/src/shaders/vert.glsl +12 -0
- plexora/client/templates/base.html +212 -0
- plexora/client/templates/index.html +238 -0
- plexora/client/templates/open_project.html +96 -0
- plexora/client/templates/project_columns.html +86 -0
- plexora/client/templates/project_edit.html +196 -0
- plexora/client/templates/upload.html +118 -0
- plexora/connect.py +528 -0
- plexora/datasource.py +782 -0
- plexora/jupyter.py +406 -0
- plexora/notebook_env.py +232 -0
- plexora/paths.py +486 -0
- plexora/plugins/__init__.py +12 -0
- plexora/plugins/cell_explorer/__init__.py +100 -0
- plexora/plugins/cell_explorer/server/__init__.py +11 -0
- plexora/plugins/cell_explorer/server/routes.py +167 -0
- plexora/plugins/cell_explorer/server/state.py +281 -0
- plexora/plugins/cell_explorer/server/values.py +145 -0
- plexora/plugins/cell_explorer/server/variables.py +367 -0
- plexora/plugins/cell_explorer/static/cellExplorerApi.js +158 -0
- plexora/plugins/cell_explorer/static/cellExplorerColors.js +316 -0
- plexora/plugins/cell_explorer/static/cellExplorerContinuous.js +403 -0
- plexora/plugins/cell_explorer/static/cellExplorerFigureBridge.js +133 -0
- plexora/plugins/cell_explorer/static/cellExplorerLegend.js +188 -0
- plexora/plugins/cell_explorer/static/cellExplorerRoiBridge.js +822 -0
- plexora/plugins/cell_explorer/static/cellExplorerSidebarController.js +826 -0
- plexora/plugins/cell_explorer/static/cellExplorerState.js +372 -0
- plexora/plugins/cell_explorer/static/cell_explorer.css +752 -0
- plexora/plugins/cell_explorer/templates/cell_explorer/panel.html +137 -0
- plexora/plugins/figure_builder/__init__.py +125 -0
- plexora/plugins/figure_builder/server/__init__.py +7 -0
- plexora/plugins/figure_builder/server/compose.py +365 -0
- plexora/plugins/figure_builder/server/export.py +784 -0
- plexora/plugins/figure_builder/server/export_jobs.py +148 -0
- plexora/plugins/figure_builder/server/operations.py +556 -0
- plexora/plugins/figure_builder/server/pixels.py +142 -0
- plexora/plugins/figure_builder/server/provenance.py +174 -0
- plexora/plugins/figure_builder/server/render.py +278 -0
- plexora/plugins/figure_builder/server/repository.py +701 -0
- plexora/plugins/figure_builder/server/routes.py +594 -0
- plexora/plugins/figure_builder/server/schema.py +1023 -0
- plexora/plugins/figure_builder/server/sources.py +120 -0
- plexora/plugins/figure_builder/server/textmetrics.py +155 -0
- plexora/plugins/figure_builder/static/figureActions.js +363 -0
- plexora/plugins/figure_builder/static/figureBuilderApi.js +255 -0
- plexora/plugins/figure_builder/static/figureCanvas.js +2362 -0
- plexora/plugins/figure_builder/static/figureCaptureBoxes.js +369 -0
- plexora/plugins/figure_builder/static/figureCaptureDock.js +466 -0
- plexora/plugins/figure_builder/static/figureCaptureTool.js +1034 -0
- plexora/plugins/figure_builder/static/figureConfirm.js +136 -0
- plexora/plugins/figure_builder/static/figureContextBar.js +1193 -0
- plexora/plugins/figure_builder/static/figureContextMenu.js +253 -0
- plexora/plugins/figure_builder/static/figureDocumentState.js +275 -0
- plexora/plugins/figure_builder/static/figureExportUi.js +174 -0
- plexora/plugins/figure_builder/static/figureLibrary.js +247 -0
- plexora/plugins/figure_builder/static/figureQuickEdit.js +628 -0
- plexora/plugins/figure_builder/static/figureRichText.js +634 -0
- plexora/plugins/figure_builder/static/figureSceneSnapshot.js +361 -0
- plexora/plugins/figure_builder/static/figureSchema.js +218 -0
- plexora/plugins/figure_builder/static/figureSidebarController.js +1159 -0
- plexora/plugins/figure_builder/static/figureTextEditor.js +503 -0
- plexora/plugins/figure_builder/static/figureTextPanel.js +491 -0
- plexora/plugins/figure_builder/static/figureViewOptions.js +273 -0
- plexora/plugins/figure_builder/static/figureWorkspace.js +2030 -0
- plexora/plugins/figure_builder/static/figure_builder.css +3292 -0
- plexora/plugins/figure_builder/templates/figure_builder/library.html +74 -0
- plexora/plugins/figure_builder/templates/figure_builder/workspace.html +27 -0
- plexora/plugins/figure_builder/templates/figure_builder/workspace_body.html +458 -0
- plexora/plugins/gating/__init__.py +76 -0
- plexora/plugins/gating/server/__init__.py +0 -0
- plexora/plugins/gating/server/anndata_gates.py +367 -0
- plexora/plugins/gating/server/database.py +16 -0
- plexora/plugins/gating/server/model.py +320 -0
- plexora/plugins/gating/server/routes.py +235 -0
- plexora/plugins/gating/static/csvGatingList.js +947 -0
- plexora/plugins/gating/static/gating.css +289 -0
- plexora/plugins/gating/static/gatingApi.js +221 -0
- plexora/plugins/gating/static/gatingSidebarController.js +513 -0
- plexora/plugins/gating/templates/gating/legacy.html +26 -0
- plexora/plugins/gating/templates/gating/panel.html +69 -0
- plexora/plugins/roi/__init__.py +95 -0
- plexora/plugins/roi/server/__init__.py +8 -0
- plexora/plugins/roi/server/adapters.py +655 -0
- plexora/plugins/roi/server/geojson.py +267 -0
- plexora/plugins/roi/server/geometry.py +181 -0
- plexora/plugins/roi/server/mapping.py +169 -0
- plexora/plugins/roi/server/operations.py +293 -0
- plexora/plugins/roi/server/repository.py +215 -0
- plexora/plugins/roi/server/routes.py +409 -0
- plexora/plugins/roi/server/schema.py +323 -0
- plexora/plugins/roi/static/roi.css +583 -0
- plexora/plugins/roi/static/roiApi.js +177 -0
- plexora/plugins/roi/static/roiFigureBridge.js +107 -0
- plexora/plugins/roi/static/roiGeometry.js +350 -0
- plexora/plugins/roi/static/roiRenderer.js +278 -0
- plexora/plugins/roi/static/roiSidebarController.js +1038 -0
- plexora/plugins/roi/static/roiState.js +518 -0
- plexora/plugins/roi/static/roiTools.js +993 -0
- plexora/plugins/roi/templates/roi/panel.html +240 -0
- plexora/proxy.py +57 -0
- plexora/server/models/adapters/__init__.py +94 -0
- plexora/server/models/adapters/anndata_adapter.py +388 -0
- plexora/server/models/adapters/base.py +93 -0
- plexora/server/models/adapters/classify.py +192 -0
- plexora/server/models/adapters/csv_adapter.py +105 -0
- plexora/server/models/adapters/inspection.py +265 -0
- plexora/server/models/adapters/spatialdata_adapter.py +248 -0
- plexora/server/models/centroid_tiles.py +305 -0
- plexora/server/models/data_model.py +1659 -0
- plexora/server/models/database_model.py +169 -0
- plexora/server/models/project.py +1270 -0
- plexora/server/plugins.py +199 -0
- plexora/server/routes/browse_routes.py +41 -0
- plexora/server/routes/data_routes.py +294 -0
- plexora/server/routes/import_routes.py +603 -0
- plexora/server/routes/page_routes.py +139 -0
- plexora/server/routes/project_routes.py +438 -0
- plexora/server/routes/quick_view_routes.py +62 -0
- plexora/server/routes/system_routes.py +39 -0
- plexora/server/routes/tool_routes.py +418 -0
- plexora/server/utils/addHEColumns.py +11 -0
- plexora/server/utils/fast_png.py +97 -0
- plexora/server/utils/fullConversion.py +50 -0
- plexora/server/utils/native_dialog.py +168 -0
- plexora/server/utils/pre_normalization.py +47 -0
- plexora/server/utils/segmentation_pyramid.py +598 -0
- plexora/server/utils/smallestenclosingcircle.py +126 -0
- plexora/server/utils/tiffsurgeon.py +372 -0
- plexora/server_cli.py +48 -0
- plexora/test.py +0 -0
- plexora-0.0.1.dist-info/METADATA +341 -0
- plexora-0.0.1.dist-info/RECORD +248 -0
- plexora-0.0.1.dist-info/WHEEL +5 -0
- plexora-0.0.1.dist-info/entry_points.txt +12 -0
- plexora-0.0.1.dist-info/licenses/LICENSE +207 -0
- plexora-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tile decode worker.
|
|
3
|
+
*
|
|
4
|
+
* Turns one compressed WebP channel tile into the single-channel Uint8Array the
|
|
5
|
+
* WebGL colorize pass uploads as an R8UI texture.
|
|
6
|
+
*
|
|
7
|
+
* This runs off the main thread because at high channel counts the decode is
|
|
8
|
+
* the dominant cost of panning. A CPU profile of a 15-channel pan attributed
|
|
9
|
+
* ~60% of all time to this work when it ran inline on the main thread:
|
|
10
|
+
* handleTileLoaded 24.6%, getImageData 23.5%, plus Blob/createImageBitmap/
|
|
11
|
+
* bitmap.close and the garbage collection of the ~3 MB of intermediates each
|
|
12
|
+
* tile allocates. At 7 channels the same work was only ~2%, which is why it
|
|
13
|
+
* initially looked not worth moving -- it scales with the number of tiles
|
|
14
|
+
* streaming in, i.e. with the channel count.
|
|
15
|
+
*
|
|
16
|
+
* createImageBitmap alone was already off-thread; what was not is everything
|
|
17
|
+
* after it -- the canvas draw, the getImageData readback, and the per-pixel
|
|
18
|
+
* loop over 1,048,576 pixels for a 1024x1024 tile.
|
|
19
|
+
*
|
|
20
|
+
* Only the default 8-bit WebP path lives here. The HD 16-bit and segmentation
|
|
21
|
+
* paths decode via UPNG.js, which is loaded as a plain global script and is not
|
|
22
|
+
* reachable from a worker; they also run far less often (HD is a parked-view
|
|
23
|
+
* mode, segmentation is a single layer rather than one per channel).
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Decode a 16-bit grayscale PNG (the HD channel path) into its raw big-endian
|
|
28
|
+
* sample bytes, using the browser's native inflate instead of UPNG.js/pako.
|
|
29
|
+
*
|
|
30
|
+
* A CPU profile of a 7-channel HD pan put 81% of ALL time in pako's JavaScript
|
|
31
|
+
* inflate -- inflate_fast alone was 71% -- with UPNG's unfiltering another 7%.
|
|
32
|
+
* DecompressionStream does the same job in native code, and doing it here keeps
|
|
33
|
+
* it off the main thread entirely.
|
|
34
|
+
*
|
|
35
|
+
* Only handles what fast_png.py actually emits: non-interlaced, filter type 0
|
|
36
|
+
* on every scanline. Anything else returns null so the caller can fall back to
|
|
37
|
+
* UPNG on the main thread.
|
|
38
|
+
*/
|
|
39
|
+
async function decodeGray16Png(buffer) {
|
|
40
|
+
const bytes = new Uint8Array(buffer);
|
|
41
|
+
const view = new DataView(buffer);
|
|
42
|
+
// PNG signature
|
|
43
|
+
if (bytes.length < 8 || bytes[0] !== 0x89 || bytes[1] !== 0x50) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
let pos = 8;
|
|
47
|
+
let width = 0, height = 0, bitDepth = 0, colorType = 0, interlace = 0;
|
|
48
|
+
const idat = [];
|
|
49
|
+
while (pos + 8 <= bytes.length) {
|
|
50
|
+
const length = view.getUint32(pos);
|
|
51
|
+
const type = String.fromCharCode(bytes[pos + 4], bytes[pos + 5], bytes[pos + 6], bytes[pos + 7]);
|
|
52
|
+
if (type === "IHDR") {
|
|
53
|
+
width = view.getUint32(pos + 8);
|
|
54
|
+
height = view.getUint32(pos + 12);
|
|
55
|
+
bitDepth = bytes[pos + 16];
|
|
56
|
+
colorType = bytes[pos + 17];
|
|
57
|
+
interlace = bytes[pos + 20];
|
|
58
|
+
} else if (type === "IDAT") {
|
|
59
|
+
idat.push(bytes.subarray(pos + 8, pos + 8 + length));
|
|
60
|
+
} else if (type === "IEND") {
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
pos += 12 + length;
|
|
64
|
+
}
|
|
65
|
+
if (bitDepth !== 16 || colorType !== 0 || interlace !== 0 || !idat.length) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// IDAT carries a zlib-wrapped stream, so "deflate" (not "deflate-raw").
|
|
70
|
+
const stream = new Blob(idat).stream().pipeThrough(new DecompressionStream("deflate"));
|
|
71
|
+
const inflated = new Uint8Array(await new Response(stream).arrayBuffer());
|
|
72
|
+
|
|
73
|
+
const stride = width * 2; // 16-bit grayscale = 2 bytes per pixel
|
|
74
|
+
if (inflated.length < height * (stride + 1)) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
const array = new Uint8Array(height * stride);
|
|
78
|
+
for (let row = 0; row < height; row++) {
|
|
79
|
+
const src = row * (stride + 1);
|
|
80
|
+
if (inflated[src] !== 0) {
|
|
81
|
+
return null; // a real filter we are not equipped to undo
|
|
82
|
+
}
|
|
83
|
+
array.set(inflated.subarray(src + 1, src + 1 + stride), row * stride);
|
|
84
|
+
}
|
|
85
|
+
return { array, width, height };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function decodeWebp(buffer) {
|
|
89
|
+
const blob = new Blob([buffer], { type: "image/webp" });
|
|
90
|
+
// premultiplyAlpha/colorSpaceConversion "none" keep the stored byte
|
|
91
|
+
// values exact -- these tiles are quantized measurements, not photos,
|
|
92
|
+
// so any colour management would corrupt them.
|
|
93
|
+
const bitmap = await createImageBitmap(blob, {
|
|
94
|
+
premultiplyAlpha: "none",
|
|
95
|
+
colorSpaceConversion: "none",
|
|
96
|
+
});
|
|
97
|
+
const { width, height } = bitmap; // capture before close() zeroes them
|
|
98
|
+
const canvas = new OffscreenCanvas(width, height);
|
|
99
|
+
const ctx = canvas.getContext("2d", { willReadFrequently: true });
|
|
100
|
+
ctx.drawImage(bitmap, 0, 0);
|
|
101
|
+
const rgba = ctx.getImageData(0, 0, width, height).data;
|
|
102
|
+
bitmap.close();
|
|
103
|
+
|
|
104
|
+
// The server writes a grayscale value into every colour channel, so
|
|
105
|
+
// taking R reconstructs the original single-channel tile.
|
|
106
|
+
const array = new Uint8Array(width * height);
|
|
107
|
+
for (let i = 0; i < array.length; i++) {
|
|
108
|
+
array[i] = rgba[i * 4];
|
|
109
|
+
}
|
|
110
|
+
return { array, width, height };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
self.onmessage = async (event) => {
|
|
114
|
+
const { id, buffer, kind } = event.data;
|
|
115
|
+
try {
|
|
116
|
+
const decoded = kind === "gray16png"
|
|
117
|
+
? await decodeGray16Png(buffer)
|
|
118
|
+
: await decodeWebp(buffer);
|
|
119
|
+
if (!decoded) {
|
|
120
|
+
// Unsupported PNG shape -- tell the caller to decode it itself
|
|
121
|
+
// rather than guessing.
|
|
122
|
+
self.postMessage({ id, ok: false, unsupported: true, error: "unsupported PNG variant" });
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
// Transfer the output (1-2 MB for a 1024x1024 tile) rather than copying.
|
|
126
|
+
self.postMessage({ id, ok: true, ...decoded }, [decoded.array.buffer]);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
self.postMessage({ id, ok: false, error: String(error) });
|
|
129
|
+
}
|
|
130
|
+
};
|
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
#version 300 es
|
|
2
|
+
precision highp int;
|
|
3
|
+
precision highp float;
|
|
4
|
+
precision highp usampler2D;
|
|
5
|
+
|
|
6
|
+
uniform usampler2D u_ids;
|
|
7
|
+
uniform usampler2D u_tile;
|
|
8
|
+
uniform sampler2D u_cell_ranges;
|
|
9
|
+
uniform sampler2D u_centers;
|
|
10
|
+
uniform usampler2D u_pickings;
|
|
11
|
+
uniform sampler2D u_mag_0;
|
|
12
|
+
uniform sampler2D u_mag_1;
|
|
13
|
+
uniform sampler2D u_mag_2;
|
|
14
|
+
uniform sampler2D u_mag_3;
|
|
15
|
+
uniform ivec2 u_ids_shape;
|
|
16
|
+
uniform vec2 u_tile_shape;
|
|
17
|
+
uniform ivec2 u_cell_range_shape;
|
|
18
|
+
uniform ivec3 u_center_shape;
|
|
19
|
+
uniform ivec2 u_picking_shape;
|
|
20
|
+
uniform ivec2 u_magnitude_shape;
|
|
21
|
+
uniform ivec4 u_marker_sample;
|
|
22
|
+
|
|
23
|
+
uniform float u_tile_fraction;
|
|
24
|
+
uniform float u_tile_scale;
|
|
25
|
+
uniform float u_pie_radius;
|
|
26
|
+
uniform vec2 u_tile_origin;
|
|
27
|
+
uniform vec2 u_tile_range;
|
|
28
|
+
uniform vec3 u_tile_color;
|
|
29
|
+
uniform bvec2 u_draw_mode;
|
|
30
|
+
uniform vec2 u_x_bounds;
|
|
31
|
+
uniform vec2 u_y_bounds;
|
|
32
|
+
uniform int u_tile_fmt;
|
|
33
|
+
uniform int u_picked_end;
|
|
34
|
+
uniform int u_id_end;
|
|
35
|
+
|
|
36
|
+
in vec2 uv;
|
|
37
|
+
out vec4 color;
|
|
38
|
+
|
|
39
|
+
// Fixed maximum number of ids
|
|
40
|
+
const uint MAX = uint(16384) * uint(16384);
|
|
41
|
+
const uint bMAX = uint(ceil(log2(float(MAX))));
|
|
42
|
+
// Fixed range of radians
|
|
43
|
+
const vec2 TAU = vec2(0., 6.2831853);
|
|
44
|
+
const float PI = 3.14159265;
|
|
45
|
+
// Fixed maximum number of channels
|
|
46
|
+
const int kMAX = 4;
|
|
47
|
+
|
|
48
|
+
// square given float
|
|
49
|
+
float pow2(float v) {
|
|
50
|
+
return v * v;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// difference
|
|
54
|
+
float dx(vec2 v) {
|
|
55
|
+
return v[1] - v[0];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// rgba to one integer
|
|
59
|
+
uint unpack(uvec4 id) {
|
|
60
|
+
return id.x + uint(256)*id.y + uint(65536)*id.z + uint(16777216)*id.w;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Check if value between min and max
|
|
64
|
+
bool check_range(float value, vec2 range) {
|
|
65
|
+
return value >= range.x && value <= range.y;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Interpolate between domain and range
|
|
69
|
+
float linear(vec2 ran, float dom, float x) {
|
|
70
|
+
float b = ran[0];
|
|
71
|
+
float m = dx(ran) / dom;
|
|
72
|
+
return m * float(clamp(x, 0., dom)) + b;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// From screen to local tile coordinates
|
|
76
|
+
vec2 screen_to_tile(vec2 screen) {
|
|
77
|
+
float x = linear(u_x_bounds, 1., screen.x);
|
|
78
|
+
float y = linear(u_y_bounds, 1., screen.y);
|
|
79
|
+
return vec2(x, y) * u_tile_shape;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// From global to local tile coordinates
|
|
83
|
+
vec2 global_to_tile(vec2 v) {
|
|
84
|
+
vec2 c = v / u_tile_scale - u_tile_origin;
|
|
85
|
+
return vec2(c.x, u_tile_shape.y - c.y);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Check if values in array match
|
|
89
|
+
bool check_same(uvec4 arr, ivec2 ii) {
|
|
90
|
+
if (arr[ii.x] == arr[ii.y]) return true;
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
bool check_same(uvec4 arr, ivec3 ii) {
|
|
94
|
+
if (arr[ii.x] == arr[ii.y]) return true;
|
|
95
|
+
if (arr[ii.y] == arr[ii.z]) return true;
|
|
96
|
+
if (arr[ii.x] == arr[ii.z]) return true;
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Float ratio of two integers
|
|
101
|
+
float division(int ai, int bi) {
|
|
102
|
+
return float(ai) / float(bi);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Float modulo of two integers
|
|
106
|
+
float modulo(uint ai, uint bi) {
|
|
107
|
+
return mod(float(ai), float(bi));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Turn 2D ratios to coordinates for 2D texture
|
|
111
|
+
vec2 to_texture_xy(vec2 s, float x, float y) {
|
|
112
|
+
return vec2((x + 0.5) / s.x, 1.0 - (y + 0.5) / s.y);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Turn 2D integers to coordinates for wrapped texture
|
|
116
|
+
// Note: needed for 2D textures larger than normal limits
|
|
117
|
+
vec2 to_flat_texture_xy(ivec3 size, int cell_index, int d) {
|
|
118
|
+
uint idx = uint(cell_index * size.z + d);
|
|
119
|
+
float idx_x = modulo(idx, uint(size.x));
|
|
120
|
+
float idx_y = floor(division(int(idx), size.x));
|
|
121
|
+
return to_texture_xy(vec2(size.xy), idx_x, idx_y);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Access cell center at cell index
|
|
125
|
+
vec2 sample_center(int cell_index) {
|
|
126
|
+
ivec3 size = u_center_shape;
|
|
127
|
+
vec2 center_x = to_flat_texture_xy(size, cell_index, 0);
|
|
128
|
+
vec2 center_y = to_flat_texture_xy(size, cell_index, 1);
|
|
129
|
+
float cx = texture(u_centers, center_x).r;
|
|
130
|
+
float cy = texture(u_centers, center_y).r;
|
|
131
|
+
return vec2(cx, cy);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Access marker key magnitude at cell index
|
|
135
|
+
float sample_magnitude(int cell_index, int key) {
|
|
136
|
+
ivec3 size = ivec3(u_magnitude_shape, 1);
|
|
137
|
+
vec2 idx_2d = to_flat_texture_xy(size, cell_index, 0);
|
|
138
|
+
// Use any of available samplers
|
|
139
|
+
if (u_marker_sample[0] == key) {
|
|
140
|
+
return texture(u_mag_0, idx_2d).r;
|
|
141
|
+
}
|
|
142
|
+
else if (u_marker_sample[1] == key) {
|
|
143
|
+
return texture(u_mag_1, idx_2d).r;
|
|
144
|
+
}
|
|
145
|
+
else if (u_marker_sample[2] == key) {
|
|
146
|
+
return texture(u_mag_2, idx_2d).r;
|
|
147
|
+
}
|
|
148
|
+
else if (u_marker_sample[3] == key) {
|
|
149
|
+
return texture(u_mag_3, idx_2d).r;
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
return -1.;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Access marker key gating parameter
|
|
157
|
+
float sample_cell_range(float param, float key) {
|
|
158
|
+
vec2 size = vec2(u_cell_range_shape);
|
|
159
|
+
vec2 idx_2d = to_texture_xy(size, param, key);
|
|
160
|
+
return texture(u_cell_ranges, idx_2d).r;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Access marker key gated min/max
|
|
164
|
+
vec2 sample_cell_range_bounds(float key) {
|
|
165
|
+
float min_range = sample_cell_range(0., key);
|
|
166
|
+
float max_range = sample_cell_range(1., key);
|
|
167
|
+
return vec2(min_range, max_range);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Access marker key gated color
|
|
171
|
+
vec3 sample_cell_range_color(float key) {
|
|
172
|
+
float r = sample_cell_range(2., key);
|
|
173
|
+
float g = sample_cell_range(3., key);
|
|
174
|
+
float b = sample_cell_range(4., key);
|
|
175
|
+
return vec3(r, g, b);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Check if angle is within one evenly divided slice
|
|
179
|
+
bool match_angle(int count, int total, float angle) {
|
|
180
|
+
float rad_min = linear(TAU, float(total), float(count) - 1.);
|
|
181
|
+
float rad_max = linear(TAU, float(total), float(count));
|
|
182
|
+
vec2 rad_range = vec2(rad_min, rad_max);
|
|
183
|
+
return check_range(angle, rad_range);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Access cell id at cell index
|
|
187
|
+
uint sample_id(usampler2D sam, ivec2 size, uint idx) {
|
|
188
|
+
float idx_x = modulo(idx, uint(size.x));
|
|
189
|
+
float idx_y = floor(division(int(idx), int(size.x)));
|
|
190
|
+
vec2 ids_idx = to_texture_xy(vec2(size), idx_x, idx_y);
|
|
191
|
+
uvec4 m_value = texture(sam, ids_idx);
|
|
192
|
+
return unpack(m_value);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Sample texture at given texel offset
|
|
196
|
+
uvec4 offset(usampler2D sam, vec2 size, vec2 pos, vec2 off) {
|
|
197
|
+
float x_pos = pos.x + off.x / size.x;
|
|
198
|
+
float y_pos = pos.y + off.y / size.y;
|
|
199
|
+
float x = linear(u_x_bounds, 1., x_pos);
|
|
200
|
+
float y = linear(u_y_bounds, 1., y_pos);
|
|
201
|
+
return texture(sam, vec2(x, y));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Generic binary search of ids
|
|
205
|
+
int binary_search(usampler2D sam, ivec2 size, uint last, uint ikey) {
|
|
206
|
+
uint first = uint(0);
|
|
207
|
+
// Search within log(n) runtime
|
|
208
|
+
for (uint i = uint(0); i <= bMAX; i++) {
|
|
209
|
+
// Evaluate the midpoint
|
|
210
|
+
uint mid = (first + last) / uint(2);
|
|
211
|
+
uint here = sample_id(sam, size, mid);
|
|
212
|
+
|
|
213
|
+
// Break if list gone
|
|
214
|
+
if (first == last && ikey != here) {
|
|
215
|
+
break;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Search below midpoint
|
|
219
|
+
if (here > ikey) last = mid;
|
|
220
|
+
|
|
221
|
+
// Search above midpoint
|
|
222
|
+
else if (ikey > here) first = mid;
|
|
223
|
+
|
|
224
|
+
// Found at midpoint
|
|
225
|
+
else return int(mid);
|
|
226
|
+
}
|
|
227
|
+
// Return -1 if id not in list
|
|
228
|
+
return -1;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Sample index of cell at given offset
|
|
232
|
+
// Note: will be -1 if cell not in cell list
|
|
233
|
+
int sample_cell_index(vec2 off) {
|
|
234
|
+
// Find cell id at given offset
|
|
235
|
+
uint ikey = unpack(offset(u_tile, u_tile_shape, uv, off));
|
|
236
|
+
|
|
237
|
+
// Array size
|
|
238
|
+
// Return -1 if background
|
|
239
|
+
if (ikey == uint(0)) {
|
|
240
|
+
return -1;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
uint n_i = uint(u_id_end);
|
|
244
|
+
uint n_p = uint(max(u_picked_end, 0));
|
|
245
|
+
|
|
246
|
+
// Use any possible id
|
|
247
|
+
if (u_picked_end < 0) {
|
|
248
|
+
return binary_search(u_ids, u_ids_shape, n_i, ikey);
|
|
249
|
+
}
|
|
250
|
+
// Use externally picked id
|
|
251
|
+
int is_picked = binary_search(u_pickings, u_picking_shape, n_p, ikey);
|
|
252
|
+
if (is_picked > -1) {
|
|
253
|
+
return binary_search(u_ids, u_ids_shape, n_i, ikey);
|
|
254
|
+
}
|
|
255
|
+
return -1;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Limit to available marker keys
|
|
259
|
+
bool catch_key(int key) {
|
|
260
|
+
if (key >= u_cell_range_shape.y) {
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Count total OR-mode pie chart slices
|
|
267
|
+
int count_gated_keys(int cell_index) {
|
|
268
|
+
int gated_total = 0;
|
|
269
|
+
for (int key = 0; key <= kMAX; key++) {
|
|
270
|
+
if (catch_key(key)) {
|
|
271
|
+
return gated_total;
|
|
272
|
+
}
|
|
273
|
+
float scale = sample_magnitude(cell_index, key);
|
|
274
|
+
vec2 range = sample_cell_range_bounds(float(key));
|
|
275
|
+
if (check_range(scale, range)) {
|
|
276
|
+
gated_total = gated_total + 1;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return gated_total;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
float dist (vec2 v) {
|
|
283
|
+
return sqrt(pow2(v.x) + pow2(v.y));
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Match channel to cell index
|
|
287
|
+
int to_and_gate(int cell_index) {
|
|
288
|
+
// Check each possible key for color
|
|
289
|
+
for (int key = 0; key <= kMAX; key++) {
|
|
290
|
+
if (catch_key(key)) {
|
|
291
|
+
return 0;
|
|
292
|
+
}
|
|
293
|
+
float scale = sample_magnitude(cell_index, key);
|
|
294
|
+
vec2 range = sample_cell_range_bounds(float(key));
|
|
295
|
+
if (!check_range(scale, range)) {
|
|
296
|
+
return -1;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return 0;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Return angle within pie chart
|
|
303
|
+
float to_chart_angle(int cell_index, float radius) {
|
|
304
|
+
if (cell_index < 0) {
|
|
305
|
+
return -1.;
|
|
306
|
+
}
|
|
307
|
+
float pie_radius = radius * u_tile_fraction;
|
|
308
|
+
vec2 global_center = sample_center(cell_index);
|
|
309
|
+
vec2 center = global_to_tile(global_center);
|
|
310
|
+
vec2 pos = screen_to_tile(uv);
|
|
311
|
+
vec2 delta = pos - center;
|
|
312
|
+
|
|
313
|
+
if (dist(delta) > pie_radius) {
|
|
314
|
+
return -1.;
|
|
315
|
+
}
|
|
316
|
+
float rad = atan(delta.y, delta.x);
|
|
317
|
+
float angle = mod(rad + PI, TAU.y);
|
|
318
|
+
return angle;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Match channel to cell index
|
|
322
|
+
int to_or_gate(int cell_index, float radius) {
|
|
323
|
+
float angle = 0.0;
|
|
324
|
+
int gated_count = 0;
|
|
325
|
+
int gated_total = count_gated_keys(cell_index);
|
|
326
|
+
// Outside of circle
|
|
327
|
+
if (radius > 0.0) {
|
|
328
|
+
angle = to_chart_angle(cell_index, radius);
|
|
329
|
+
if (angle < 0.0) {
|
|
330
|
+
return -1;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
// Check each possible key for color
|
|
334
|
+
for (int key = 0; key <= kMAX; key++) {
|
|
335
|
+
if (catch_key(key)) {
|
|
336
|
+
return -1;
|
|
337
|
+
}
|
|
338
|
+
float scale = sample_magnitude(cell_index, key);
|
|
339
|
+
vec2 range = sample_cell_range_bounds(float(key));
|
|
340
|
+
if (check_range(scale, range)) {
|
|
341
|
+
gated_count = gated_count + 1;
|
|
342
|
+
if (radius <= 0.0) {
|
|
343
|
+
return key;
|
|
344
|
+
}
|
|
345
|
+
if (match_angle(gated_count, gated_total, angle)) {
|
|
346
|
+
return key;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return -1;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Match channel to cell index
|
|
354
|
+
int to_gate(int cell_index, bvec2 mode, float radius) {
|
|
355
|
+
int n_gates = u_cell_range_shape.y;
|
|
356
|
+
if (n_gates < 1 || cell_index < 0) {
|
|
357
|
+
return -1;
|
|
358
|
+
}
|
|
359
|
+
bool or_mode = mode.y;
|
|
360
|
+
if (or_mode) {
|
|
361
|
+
return to_or_gate(cell_index, radius);
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
return to_and_gate(cell_index);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Sample gating state of cell at given offset
|
|
369
|
+
int sample_cell_gate(vec2 off, bvec2 mode, float radius) {
|
|
370
|
+
int cell_index = sample_cell_index(off);
|
|
371
|
+
return to_gate(cell_index, mode, radius);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Sample gated cell index at given offset
|
|
375
|
+
int gate_cell_index(vec2 off, bvec2 mode, float radius) {
|
|
376
|
+
if (u_cell_range_shape.y < 1 && mode.x) {
|
|
377
|
+
return sample_cell_index(off);
|
|
378
|
+
}
|
|
379
|
+
int cell_gate = sample_cell_gate(off, mode, radius);
|
|
380
|
+
if (cell_gate == -1) {
|
|
381
|
+
return -1;
|
|
382
|
+
}
|
|
383
|
+
return sample_cell_index(off);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
bool in_diff(usampler2D sam, vec2 pos, float one, bvec2 mode) {
|
|
387
|
+
int cell_idx = gate_cell_index(vec2(0., 0.), mode, -1.0);
|
|
388
|
+
|
|
389
|
+
int right = gate_cell_index(vec2(1. * one, 0.), mode, -1.0);
|
|
390
|
+
int left = gate_cell_index(vec2(-1. * one, 0.), mode, -1.0);
|
|
391
|
+
int down = gate_cell_index(vec2(0., -1. * one), mode, -1.0);
|
|
392
|
+
int top = gate_cell_index(vec2(0., 1. * one), mode, -1.0);
|
|
393
|
+
|
|
394
|
+
bool right_d = right != cell_idx;
|
|
395
|
+
bool left_d = left != cell_idx;
|
|
396
|
+
bool down_d = down != cell_idx;
|
|
397
|
+
bool top_d = top != cell_idx;
|
|
398
|
+
|
|
399
|
+
// Avoid double counting when zoomed in
|
|
400
|
+
if (one > 1.0 && cell_idx != -1) {
|
|
401
|
+
if (right == -1 || left == -1 || down == -1 || top == -1) {
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
// Count any border of gated cell
|
|
406
|
+
return (right_d || left_d || down_d || top_d);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
bool equals4(uvec4 id1, uvec4 id2) {
|
|
410
|
+
return all(equal(id1, id2));
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Check if pixel is on a border
|
|
414
|
+
bool near_cell_edge(float one, bvec2 mode) {
|
|
415
|
+
uvec4 empty_val = uvec4(0., 0., 0., 0.);
|
|
416
|
+
int cell_index = sample_cell_index(vec2(0, 0));
|
|
417
|
+
uvec4 pixel = offset(u_tile, u_tile_shape, uv, vec2(0., 0.));
|
|
418
|
+
bool background = equals4(empty_val, pixel);
|
|
419
|
+
|
|
420
|
+
// Background not counted
|
|
421
|
+
if (one == 1.0 && background) {
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
// pixels are at different cells
|
|
425
|
+
return in_diff(u_tile, uv, one, mode);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Colorize discrete u32 signal
|
|
429
|
+
vec4 u32_rgba_map(bvec2 mode) {
|
|
430
|
+
vec4 empty_pixel = vec4(0., 0., 0., 0.);
|
|
431
|
+
vec4 white_pixel = vec4(1., 1., 1., 1.);
|
|
432
|
+
bool edge_mode = mode.x;
|
|
433
|
+
bool or_mode = mode.y;
|
|
434
|
+
if (!edge_mode) {
|
|
435
|
+
return empty_pixel;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
int cell_index = sample_cell_index(vec2(0., 0.));
|
|
439
|
+
if (cell_index < 0) {
|
|
440
|
+
return empty_pixel;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (!or_mode) {
|
|
444
|
+
return white_pixel;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if (u_cell_range_shape.y < 1) {
|
|
448
|
+
return white_pixel;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
int key = to_gate(cell_index, mode, -1.0);
|
|
452
|
+
if (key > -1) {
|
|
453
|
+
return vec4(sample_cell_range_color(float(key)), 1.0);
|
|
454
|
+
}
|
|
455
|
+
return empty_pixel;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Clamp signal between given min/max
|
|
459
|
+
float range_clamp(float value) {
|
|
460
|
+
float min_ = u_tile_range[0];
|
|
461
|
+
float max_ = u_tile_range[1];
|
|
462
|
+
return clamp((value - min_) / (max_ - min_), 0.0, 1.0);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Colorize continuous u16 signal
|
|
466
|
+
vec4 u16_rg_range(float alpha) {
|
|
467
|
+
uvec2 pixel = offset(u_tile, u_tile_shape, uv, vec2(0, 0)).rg;
|
|
468
|
+
float value = float(pixel.r * uint(255) + pixel.g) / 65535.;
|
|
469
|
+
|
|
470
|
+
// Threshhold pixel within range
|
|
471
|
+
float pixel_val = range_clamp(value);
|
|
472
|
+
|
|
473
|
+
// Color pixel value
|
|
474
|
+
vec3 pixel_color = u_tile_color * pixel_val;
|
|
475
|
+
return vec4(pixel_color, alpha);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Colorize a quantized 8-bit signal (the fast/default WebP tile path).
|
|
479
|
+
// The server already quantized the original 16-bit value into [0, 255]
|
|
480
|
+
// (linear against the channel's true max); u_tile_range is expressed in
|
|
481
|
+
// that SAME [0, 255] domain in this mode (see viewerSidebar.js), so the
|
|
482
|
+
// quantized byte can feed range_clamp directly -- no reconstruction back
|
|
483
|
+
// into 16-bit units, and therefore no precision mismatch between the
|
|
484
|
+
// slider's domain and the encoded data's domain.
|
|
485
|
+
vec4 u8_r_range(float alpha) {
|
|
486
|
+
uint pixel = offset(u_tile, u_tile_shape, uv, vec2(0, 0)).r;
|
|
487
|
+
float quant = float(pixel) / 255.;
|
|
488
|
+
|
|
489
|
+
// Threshhold pixel within range
|
|
490
|
+
float pixel_val = range_clamp(quant);
|
|
491
|
+
|
|
492
|
+
// Color pixel value
|
|
493
|
+
vec3 pixel_color = u_tile_color * pixel_val;
|
|
494
|
+
return vec4(pixel_color, alpha);
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// True while `screen` (quad-space uv) maps inside the tile's actual valid
|
|
498
|
+
// data region. u_x_bounds/u_y_bounds can legitimately extend outside
|
|
499
|
+
// [0, 1] -- e.g. an edge tile stretched past its real coverage while
|
|
500
|
+
// zoomed beyond native resolution (see toMagnifiedBounds in
|
|
501
|
+
// viewerManager.js) -- and outside that range GL's CLAMP_TO_EDGE wrap mode
|
|
502
|
+
// just keeps returning the boundary texel forever. Without this check
|
|
503
|
+
// those out-of-bounds fragments painted a solid smear of that edge
|
|
504
|
+
// texel/color across the rest of the quad instead of showing nothing.
|
|
505
|
+
bool in_tile_bounds(vec2 screen) {
|
|
506
|
+
float x = linear(u_x_bounds, 1., screen.x);
|
|
507
|
+
float y = linear(u_y_bounds, 1., screen.y);
|
|
508
|
+
return x >= 0.0 && x <= 1.0 && y >= 0.0 && y <= 1.0;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
//
|
|
512
|
+
// Entrypoint
|
|
513
|
+
//
|
|
514
|
+
|
|
515
|
+
void main() {
|
|
516
|
+
if (u_tile_fmt == 32) {
|
|
517
|
+
color = u32_rgba_map(u_draw_mode);
|
|
518
|
+
}
|
|
519
|
+
else if (!in_tile_bounds(uv)) {
|
|
520
|
+
color = vec4(0.);
|
|
521
|
+
}
|
|
522
|
+
else if (u_tile_fmt == 8) {
|
|
523
|
+
color = u8_r_range(0.9);
|
|
524
|
+
}
|
|
525
|
+
else {
|
|
526
|
+
color = u16_rg_range(0.9);
|
|
527
|
+
}
|
|
528
|
+
}
|