1use std::sync::Arc;
2
3use pluot_core::{maybe_timeout, FutureExt, Duration};
4
5use serde::{Deserialize, Serialize};
6
7use pluot_core::log;
8use pluot_core::wgpu;
9use pluot_core::cache::use_memo_numeric_data;
10use pluot_core::zarr::is_timed_out_zarrs_error;
11use zarrs::storage::AsyncReadableStorageTraits;
12use pluot_core::render_traits::{
13 BrushableLayer, ExtentableLayer, DrawToRasterGpu, DrawToRasterCpu, DrawToSvg, MarginParams, PickableLayer, PreparedLayer, UnitsMode, ViewParams, resolve_store_name,
14};
15use pluot_core::two::svg::SvgContext;
16use pluot_core::layers::bitmap_layer::{
17 BitmapLayer, BitmapLayerParams, ChannelSettings, DimensionOrder,
18};
19use pluot_core::numeric_data::NumericData;
20use pluot_core::render_types::{CpuContext, CpuRenderPass, PrepareResult};
21use pluot_core::render_types::GpuContext;
22use pluot_core::LayerPickingResult;
23use pluot_core::viewport::{DataCoord, ScreenCoord};
24use crate::layers::ome_zarr_utils::{OmeDim, OmeDimensionOrder, OmeZarrChannelSetting};
25use pluot_core::multiscale_utils::to_y_slice;
26
27#[derive(Serialize, Deserialize, Debug, Clone)]
29#[serde(default)]
30pub struct OmeZarrBitmapLayerParams {
31 pub store_name: Option<String>,
34
35 pub array_path: String,
37 pub array_metadata: Option<zarrs::array::ArrayMetadata>,
41
42 pub array_shape: Vec<u64>,
47 pub array_chunk_shape: Vec<u64>,
49 pub array_dimension_order: OmeDimensionOrder,
51
52 pub target_z: Option<u64>,
54 pub target_t: Option<u64>,
56
57 pub model_matrix: [f32; 16],
61
62 pub slice_x: Option<(u64, u64)>,
64 pub slice_y: Option<(u64, u64)>,
65
66 pub channel_settings: Vec<OmeZarrChannelSetting>,
69
70 pub bounds: Option<MarginParams>,
71 pub opacity: f32,
72 pub layer_id: String,
73}
74
75impl Default for OmeZarrBitmapLayerParams {
76 fn default() -> Self {
77 Self {
78 store_name: None,
79 array_path: "".to_string(),
80 array_metadata: None,
81 array_shape: vec![],
82 array_chunk_shape: vec![],
83 array_dimension_order: OmeDimensionOrder::new(vec![OmeDim::Y, OmeDim::X]),
84 target_z: None,
85 target_t: None,
86 model_matrix: [
88 1.0, 0.0, 0.0, 0.0,
89 0.0, 1.0, 0.0, 0.0,
90 0.0, 0.0, 1.0, 0.0,
91 0.0, 0.0, 0.0, 1.0,
92 ],
93 slice_x: None,
94 slice_y: None,
95 channel_settings: vec![],
96 bounds: None,
97 opacity: 1.0,
98 layer_id: "".to_string(),
99 }
100 }
101}
102
103pub struct OmeZarrBitmapLayer {
108 view_params: ViewParams,
109 layer_params: OmeZarrBitmapLayerParams,
110 store: Arc<dyn AsyncReadableStorageTraits>,
111 store_name: String,
112
113 inner: Option<BitmapLayer>,
115}
116
117impl OmeZarrBitmapLayer {
118 pub fn new(
119 view_params: ViewParams,
120 layer_params: OmeZarrBitmapLayerParams,
121 ) -> Self {
122 let store_name = resolve_store_name(&layer_params.store_name, &view_params);
123
124 let store = view_params.get_store(&store_name);
125
126 Self {
127 view_params,
128 layer_params,
129 store,
130 store_name,
131 inner: None,
132 }
133 }
134
135 fn dim_index(&self, dim: OmeDim) -> Option<usize> {
136 self.layer_params.array_dimension_order.index_of(dim)
137 }
138
139 async fn load_tile_data(&self) -> Result<NumericData, zarrs::array::ArrayError> {
149 let store = self.store.clone();
150 let array_path = self.layer_params.array_path.clone();
151 let array_metadata = self.layer_params.array_metadata.clone();
152 let slice_x = self.layer_params.slice_x;
153 let slice_y = self.layer_params.slice_y;
154 let channel_settings = self.layer_params.channel_settings.clone();
155 let c_dim_i = self.dim_index(OmeDim::C);
156
157 let y_dim_i = self.dim_index(OmeDim::Y).expect("array_dimension_order must contain Y");
158 let x_dim_i = self.dim_index(OmeDim::X).expect("array_dimension_order must contain X");
159
160 let array_shape = self.layer_params.array_shape.clone();
161 let (y_start, y_end) = slice_y.unwrap_or((0, array_shape[y_dim_i]));
162 let (x_start, x_end) = slice_x.unwrap_or((0, array_shape[x_dim_i]));
163
164 let z_dim_i = self.dim_index(OmeDim::Z);
165 let t_dim_i = self.dim_index(OmeDim::T);
166 let target_z = self.layer_params.target_z;
167 let target_t = self.layer_params.target_t;
168
169 let tile_h = y_end - y_start;
171 let tile_w = x_end - x_start;
172
173 let cache_enabled = self.view_params.cache_enabled;
174
175 let base_keys: Vec<String> = vec![
177 self.store_name.clone(),
178 array_path.clone(),
179 format!("slice_x_{:?}", slice_x),
180 format!("slice_y_{:?}", slice_y),
181 format!("z_{:?}", target_z),
182 format!("t_{:?}", target_t),
183 ];
184
185 let num_channels = channel_settings.len();
186 let tile_num_elements = num_channels * tile_h as usize * tile_w as usize;
187
188 let array = if let Some(metadata) = array_metadata {
192 zarrs::array::Array::new_with_metadata(store.clone(), &array_path, metadata)
193 .unwrap_or_else(|e| {
194 panic!("Failed to create array at {}: {:?}", array_path, e)
195 })
196 } else {
197 zarrs::array::Array::async_open(store.clone(), &array_path)
198 .await
199 .unwrap_or_else(|e| {
200 panic!("Failed to open array at {}: {:?}", array_path, e)
201 })
202 };
203
204 use zarrs::plugin::{ExtensionName, ZarrVersion};
206 let dtype_name = array
207 .data_type()
208 .name(ZarrVersion::V3)
209 .expect("Array data type must have a V3 name")
210 .to_string();
211
212 let channel_requests: Vec<(Vec<String>, zarrs::array::ArraySubset)> = channel_settings
215 .iter()
216 .map(|cs| {
217 let mut channel_keys = base_keys.clone();
220 channel_keys.push(format!("c_{}", cs.c_index));
221
222 let mut start = array_shape.iter().map(|_| 0u64).collect::<Vec<_>>();
224 let mut shape = array_shape.clone();
225
226 start[y_dim_i] = y_start;
227 shape[y_dim_i] = tile_h;
228 start[x_dim_i] = x_start;
229 shape[x_dim_i] = tile_w;
230
231 if let Some(z_dim_i) = z_dim_i {
232 start[z_dim_i] = target_z.unwrap_or(0);
233 shape[z_dim_i] = 1;
234 }
235 if let Some(t_dim_i) = t_dim_i {
236 start[t_dim_i] = target_t.unwrap_or(0);
237 shape[t_dim_i] = 1;
238 }
239 if let Some(c_dim_i) = c_dim_i {
240 start[c_dim_i] = cs.c_index as u64;
241 shape[c_dim_i] = 1;
242 }
243
244 let subset = zarrs::array::ArraySubset::new_with_start_shape(start, shape)
245 .expect("Valid array subset");
246
247 (channel_keys, subset)
248 })
249 .collect();
250
251 let array = &array;
253 let dtype_name = &dtype_name;
254 let channel_futures = channel_requests.iter().map(|(channel_keys, subset)| {
255 use_memo_numeric_data(async || {
256 macro_rules! load_channel_data {
257 ($rust_ty:ty, $variant:ident) => {{
258 let chunk = array
259 .async_retrieve_array_subset::<Vec<$rust_ty>>(subset)
260 .await?;
261 NumericData::$variant(Arc::new(chunk))
262 }};
263 }
264
265 Ok::<NumericData, zarrs::array::ArrayError>(match dtype_name.as_str() {
266 "uint8" => load_channel_data!(u8, Uint8),
267 "uint16" => load_channel_data!(u16, Uint16),
268 "uint32" => load_channel_data!(u32, Uint32),
269 "uint64" => load_channel_data!(u64, Uint64),
270 "int8" => load_channel_data!(i8, Int8),
271 "int16" => load_channel_data!(i16, Int16),
272 "int32" => load_channel_data!(i32, Int32),
273 "int64" => load_channel_data!(i64, Int64),
274 "float32" => load_channel_data!(f32, Float32),
275 "float64" => load_channel_data!(f64, Float64),
276 _ => panic!("Unsupported zarr data type: {}", dtype_name),
277 })
278 }, channel_keys, cache_enabled)
279 });
280
281 let channel_parts: Vec<Arc<NumericData>> = futures::future::join_all(channel_futures)
282 .await
283 .into_iter()
284 .collect::<Result<_, _>>()?;
285
286 macro_rules! concat_channels {
291 ($rust_ty:ty, $variant:ident) => {{
292 let mut combined: Vec<$rust_ty> = Vec::with_capacity(tile_num_elements);
293 for part in &channel_parts {
294 match &**part {
295 NumericData::$variant(v) => combined.extend_from_slice(v),
296 _ => unreachable!("All channels share the array's data type"),
297 }
298 }
299 NumericData::$variant(Arc::new(combined))
300 }};
301 }
302
303 Ok(match dtype_name.as_str() {
304 "uint8" => concat_channels!(u8, Uint8),
305 "uint16" => concat_channels!(u16, Uint16),
306 "uint32" => concat_channels!(u32, Uint32),
307 "uint64" => concat_channels!(u64, Uint64),
308 "int8" => concat_channels!(i8, Int8),
309 "int16" => concat_channels!(i16, Int16),
310 "int32" => concat_channels!(i32, Int32),
311 "int64" => concat_channels!(i64, Int64),
312 "float32" => concat_channels!(f32, Float32),
313 "float64" => concat_channels!(f64, Float64),
314 _ => panic!("Unsupported zarr data type: {}", dtype_name),
315 })
316 }
317}
318
319#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
320#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
321impl PreparedLayer for OmeZarrBitmapLayer {
322 async fn prepare(&mut self, gpu_context: Option<&GpuContext<'_>>) -> PrepareResult {
323 let data_future = self.load_tile_data();
325
326 let future_result = maybe_timeout!(data_future, self.view_params.timeout)
327 .await;
328
329 let data = match future_result {
330 Ok(Ok(data_result)) => data_result,
331 Ok(Err(e)) => {
332 if is_timed_out_zarrs_error(&e) {
334 return PrepareResult { bailed_early: true };
335 } else {
336 panic!("Zarrs error during OmeZarrBitmapLayer prepare: {:?}", e);
337 }
338 }
339 Err(_) => {
340 return PrepareResult { bailed_early: true };
342 }
343 };
344
345 let y_dim_i = self.dim_index(OmeDim::Y).expect("array_dimension_order must contain Y");
346 let x_dim_i = self.dim_index(OmeDim::X).expect("array_dimension_order must contain X");
347
348 let (y_start, y_end) = self.layer_params.slice_y.unwrap_or((0, self.layer_params.array_shape[y_dim_i]));
349 let (x_start, x_end) = self.layer_params.slice_x.unwrap_or((0, self.layer_params.array_shape[x_dim_i]));
350
351 let num_channels = self.layer_params.channel_settings.len();
352 let tile_h = (y_end - y_start) as u32;
353 let tile_w = (x_end - x_start) as u32;
354
355 let pixel_offset_x = x_start as u32;
356 let (pixel_offset_y_phys, _) = to_y_slice(
358 y_start,
359 y_end,
360 self.layer_params.array_shape[y_dim_i],
361 );
362 let pixel_offset_y = pixel_offset_y_phys as u32;
363
364 let channel_settings: Vec<ChannelSettings> = self
365 .layer_params
366 .channel_settings
367 .iter()
368 .map(|cs| ChannelSettings {
369 window: (cs.window.0, cs.window.1),
370 color: (cs.color.0, cs.color.1, cs.color.2),
371 })
372 .collect();
373
374 let bitmap_params = BitmapLayerParams {
375 layer_id: self.layer_params.layer_id.clone(),
376 bounds: self.layer_params.bounds.clone(),
377 data_unit_mode_x: UnitsMode::Data,
378 data_unit_mode_y: UnitsMode::Data,
379 pixel_offset: Some((pixel_offset_x, pixel_offset_y)),
380 model_matrix: Some(self.layer_params.model_matrix),
381 dimension_order: if y_dim_i < x_dim_i {
382 DimensionOrder::CYX
383 } else {
384 DimensionOrder::CXY
385 },
386 shape: if y_dim_i < x_dim_i {
387 vec![num_channels as u32, tile_h, tile_w]
388 } else {
389 vec![num_channels as u32, tile_w, tile_h]
390 },
391 channel_settings,
392 opacity: self.layer_params.opacity,
393 data,
394 };
395
396 let mut inner = BitmapLayer::new(self.view_params.clone(), bitmap_params);
397 let result = inner.prepare(gpu_context).await;
398 self.inner = Some(inner);
399
400 result
401 }
402}
403
404#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
405#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
406impl DrawToRasterGpu for OmeZarrBitmapLayer {
407 async fn draw(&self, gpu_context: &GpuContext<'_>, pass: &mut wgpu::RenderPass) {
408 if let Some(inner) = &self.inner {
409 DrawToRasterGpu::draw(inner, gpu_context, pass).await;
410 }
411 }
412}
413
414#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
415#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
416impl DrawToRasterCpu for OmeZarrBitmapLayer {
417 async fn draw(&self, _cpu_context: &CpuContext<'_>, _pass: &mut CpuRenderPass) {}
418}
419
420#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
421#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
422impl DrawToSvg for OmeZarrBitmapLayer {
423 async fn draw(&self, ctx: &mut SvgContext) {
424 if let Some(inner) = &self.inner {
425 DrawToSvg::draw(inner, ctx).await
426 }
427 }
428}
429
430impl BrushableLayer for OmeZarrBitmapLayer {}
431
432impl ExtentableLayer for OmeZarrBitmapLayer {}
433
434impl PickableLayer for OmeZarrBitmapLayer {
435 fn pick(&self, screen_coord: ScreenCoord, data_coord: Option<DataCoord>) -> Option<LayerPickingResult> {
436 let inner = self.inner.as_ref()?;
441 let mut result = PickableLayer::pick(inner, screen_coord, data_coord)?;
442
443 let (x_start, _) = self.layer_params.slice_x.unwrap_or((0, 0));
444 let (y_start, _) = self.layer_params.slice_y.unwrap_or((0, 0));
445 if let Some(x) = result.info.get("x").and_then(|v| v.parse::<u64>().ok()) {
446 result.info.insert("array_x".to_string(), (x_start + x).to_string());
447 }
448 if let Some(y) = result.info.get("y").and_then(|v| v.parse::<u64>().ok()) {
449 result.info.insert("array_y".to_string(), (y_start + y).to_string());
450 }
451
452 Some(result)
453 }
454}