mirror of
https://github.com/cds-astro/aladin-lite.git
synced 2025-12-12 07:40:26 -08:00
Fix NaN detection by the GPU on float textures for window platform
float textures coming from BITPIX<0 fits images are sent to GPU as RGBA8UI textures. Float decoding from a vec4 rgba is done in the shader. Then this obtained decoded float can be tested against nan/inf in the shader
This commit is contained in:
committed by
Matthieu Baumann
parent
68d9e67774
commit
ee2eb6e704
@@ -163,7 +163,7 @@ impl Image for Fits<'_> {
|
||||
);
|
||||
}
|
||||
Data::F32(data) => {
|
||||
let view = unsafe { R32F::view(&data) };
|
||||
let view = unsafe { R8UI::view(&std::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 4)) };
|
||||
textures.tex_sub_image_3d_with_opt_array_buffer_view(
|
||||
offset.x,
|
||||
offset.y,
|
||||
|
||||
@@ -152,21 +152,13 @@ impl ImageFormat for RGB32F {
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct R32F;
|
||||
impl ImageFormat for R32F {
|
||||
type P = [f32; 1];
|
||||
type P = [u8; 4];
|
||||
|
||||
const NUM_CHANNELS: usize = 1;
|
||||
const NUM_CHANNELS: usize = 4;
|
||||
|
||||
#[cfg(feature = "webgl2")]
|
||||
const FORMAT: u32 = WebGlRenderingCtx::RED as u32;
|
||||
#[cfg(feature = "webgl1")]
|
||||
const FORMAT: u32 = WebGlRenderingCtx::LUMINANCE as u32;
|
||||
|
||||
#[cfg(feature = "webgl2")]
|
||||
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::R32F as i32;
|
||||
#[cfg(feature = "webgl1")]
|
||||
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::LUMINANCE as i32;
|
||||
|
||||
const TYPE: u32 = WebGlRenderingCtx::FLOAT;
|
||||
const FORMAT: u32 = WebGlRenderingCtx::RGBA as u32;
|
||||
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::RGBA8 as i32;
|
||||
const TYPE: u32 = WebGlRenderingCtx::UNSIGNED_BYTE;
|
||||
|
||||
const CHANNEL_TYPE: ChannelType = ChannelType::R32F;
|
||||
|
||||
@@ -174,41 +166,31 @@ impl ImageFormat for R32F {
|
||||
Ok(Bytes::Borrowed(raw_bytes))
|
||||
}
|
||||
|
||||
type ArrayBufferView = js_sys::Float32Array;
|
||||
type ArrayBufferView = js_sys::Uint8Array;
|
||||
|
||||
unsafe fn view(s: &[<Self::P as Pixel>::Item]) -> Self::ArrayBufferView {
|
||||
Self::ArrayBufferView::view(s)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "webgl2")]
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct R64F;
|
||||
#[cfg(feature = "webgl2")]
|
||||
impl ImageFormat for R64F {
|
||||
type P = [f32; 1];
|
||||
type P = [u8; 4];
|
||||
|
||||
const NUM_CHANNELS: usize = 1;
|
||||
const NUM_CHANNELS: usize = 4;
|
||||
|
||||
#[cfg(feature = "webgl2")]
|
||||
const FORMAT: u32 = WebGlRenderingCtx::RED as u32;
|
||||
#[cfg(feature = "webgl1")]
|
||||
const FORMAT: u32 = WebGlRenderingCtx::LUMINANCE as u32;
|
||||
const FORMAT: u32 = WebGlRenderingCtx::RGBA as u32;
|
||||
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::RGBA8 as i32;
|
||||
const TYPE: u32 = WebGlRenderingCtx::UNSIGNED_BYTE;
|
||||
|
||||
#[cfg(feature = "webgl2")]
|
||||
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::R32F as i32;
|
||||
#[cfg(feature = "webgl1")]
|
||||
const INTERNAL_FORMAT: i32 = WebGlRenderingCtx::LUMINANCE as i32;
|
||||
|
||||
const TYPE: u32 = WebGlRenderingCtx::FLOAT;
|
||||
|
||||
const CHANNEL_TYPE: ChannelType = ChannelType::R64F;
|
||||
const CHANNEL_TYPE: ChannelType = ChannelType::R32F;
|
||||
|
||||
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
|
||||
Ok(Bytes::Borrowed(raw_bytes))
|
||||
}
|
||||
|
||||
type ArrayBufferView = js_sys::Float32Array;
|
||||
type ArrayBufferView = js_sys::Uint8Array;
|
||||
|
||||
unsafe fn view(s: &[<Self::P as Pixel>::Item]) -> Self::ArrayBufferView {
|
||||
Self::ArrayBufferView::view(s)
|
||||
@@ -309,6 +291,18 @@ pub enum ChannelType {
|
||||
R32I,
|
||||
}
|
||||
|
||||
impl ChannelType {
|
||||
pub fn is_colored(&self) -> bool {
|
||||
match self {
|
||||
ChannelType::RGBA32F
|
||||
| ChannelType::RGB32F
|
||||
| ChannelType::RGBA8U
|
||||
| ChannelType::RGB8U => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const NUM_CHANNELS: usize = 9;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||
@@ -327,12 +321,6 @@ impl ImageFormatType {
|
||||
}
|
||||
|
||||
pub fn is_colored(&self) -> bool {
|
||||
match self.channel {
|
||||
ChannelType::RGBA32F
|
||||
| ChannelType::RGB32F
|
||||
| ChannelType::RGBA8U
|
||||
| ChannelType::RGB8U => true,
|
||||
_ => false,
|
||||
}
|
||||
self.channel.is_colored()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ pub trait Pixel:
|
||||
+ Copy
|
||||
+ std::fmt::Debug
|
||||
+ cgmath::Zero
|
||||
+ cgmath::One
|
||||
+ std::cmp::PartialEq
|
||||
+ crate::convert::Cast<f32>;
|
||||
type Container: ArrayBuffer<Item = Self::Item>;
|
||||
|
||||
@@ -160,47 +160,42 @@ impl From<query::Allsky> for AllskyRequest {
|
||||
let Fits { hdu } = Fits::from_reader(&mut reader)
|
||||
.map_err(|_| JsValue::from_str("Parsing fits error of allsky"))?;
|
||||
|
||||
//let width_allsky_px = 27 * std::cmp::min(tile_size, 64) as i32;
|
||||
//let height_allsky_px = 29 * std::cmp::min(tile_size, 64) as i32;
|
||||
let data = hdu.get_data();
|
||||
|
||||
match data {
|
||||
InMemData::U8(data) => {
|
||||
Ok(handle_allsky_fits(&data, tile_size, texture_size)?
|
||||
.into_iter()
|
||||
.map(|image| ImageType::RawR8ui { image })
|
||||
.collect())
|
||||
}
|
||||
InMemData::I16(data) => {
|
||||
Ok(handle_allsky_fits(&data, tile_size, texture_size)?
|
||||
.into_iter()
|
||||
.map(|image| ImageType::RawR16i { image })
|
||||
.collect())
|
||||
}
|
||||
InMemData::I32(data) => {
|
||||
Ok(handle_allsky_fits(&data, tile_size, texture_size)?
|
||||
.into_iter()
|
||||
.map(|image| ImageType::RawR32i { image })
|
||||
.collect())
|
||||
}
|
||||
InMemData::F32(data) => {
|
||||
Ok(handle_allsky_fits(&data, tile_size, texture_size)?
|
||||
.into_iter()
|
||||
.map(|image| ImageType::RawR32f { image })
|
||||
.collect())
|
||||
}
|
||||
InMemData::I64(data) => {
|
||||
let data = data.iter().map(|v| *v as i32).collect::<Vec<_>>();
|
||||
Ok(handle_allsky_fits(&data, tile_size, texture_size)?
|
||||
.into_iter()
|
||||
.map(|image| ImageType::RawR32i { image })
|
||||
.collect())
|
||||
}
|
||||
InMemData::F32(data) => {
|
||||
let data = unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 4) };
|
||||
Ok(handle_allsky_fits(&data, tile_size, texture_size)?
|
||||
.map(|image| ImageType::RawRgba8u { image })
|
||||
.collect())
|
||||
}
|
||||
InMemData::F64(data) => {
|
||||
let data = data.iter().map(|v| *v as f32).collect::<Vec<_>>();
|
||||
let data = unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * 4) };
|
||||
|
||||
Ok(handle_allsky_fits(&data, tile_size, texture_size)?
|
||||
.into_iter()
|
||||
.map(|image| ImageType::RawR32f { image })
|
||||
.map(|image| ImageType::RawRgba8u { image })
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
@@ -226,44 +221,44 @@ fn handle_allsky_file<F: ImageFormat>(
|
||||
allsky_tile_size: i32,
|
||||
texture_size: i32,
|
||||
tile_size: i32,
|
||||
) -> Result<Vec<ImageBuffer<F>>, JsValue> {
|
||||
) -> Result<impl Iterator<Item = ImageBuffer<F>>, JsValue> {
|
||||
let num_tiles_per_texture = (texture_size / tile_size) * (texture_size / tile_size);
|
||||
let num_tiles = num_tiles_per_texture * 12;
|
||||
let mut tiles = Vec::with_capacity(num_tiles as usize);
|
||||
|
||||
let num_allsky_tiles_per_tile = (tile_size / allsky_tile_size) * (tile_size / allsky_tile_size);
|
||||
|
||||
let mut src_idx = 0;
|
||||
for _ in 0..num_tiles {
|
||||
let mut base_tile =
|
||||
let tiles = (0..num_tiles)
|
||||
.map(move |_| {
|
||||
let mut base_tile =
|
||||
ImageBuffer::<F>::allocate(&<F as ImageFormat>::P::BLACK, tile_size, tile_size);
|
||||
for idx_tile in 0..num_allsky_tiles_per_tile {
|
||||
let (x, y) = crate::utils::unmortonize(idx_tile as u64);
|
||||
let dx = x * (allsky_tile_size as u32);
|
||||
let dy = y * (allsky_tile_size as u32);
|
||||
for idx_tile in 0..num_allsky_tiles_per_tile {
|
||||
let (x, y) = crate::utils::unmortonize(idx_tile as u64);
|
||||
let dx = x * (allsky_tile_size as u32);
|
||||
let dy = y * (allsky_tile_size as u32);
|
||||
|
||||
let sx = (src_idx % 27) * allsky_tile_size;
|
||||
let sy = (src_idx / 27) * allsky_tile_size;
|
||||
let s = ImageBufferView {
|
||||
x: sx as i32,
|
||||
y: sy as i32,
|
||||
w: allsky_tile_size as i32,
|
||||
h: allsky_tile_size as i32,
|
||||
};
|
||||
let d = ImageBufferView {
|
||||
x: dx as i32,
|
||||
y: dy as i32,
|
||||
w: allsky_tile_size as i32,
|
||||
h: allsky_tile_size as i32,
|
||||
};
|
||||
let sx = (src_idx % 27) * allsky_tile_size;
|
||||
let sy = (src_idx / 27) * allsky_tile_size;
|
||||
let s = ImageBufferView {
|
||||
x: sx as i32,
|
||||
y: sy as i32,
|
||||
w: allsky_tile_size as i32,
|
||||
h: allsky_tile_size as i32,
|
||||
};
|
||||
let d = ImageBufferView {
|
||||
x: dx as i32,
|
||||
y: dy as i32,
|
||||
w: allsky_tile_size as i32,
|
||||
h: allsky_tile_size as i32,
|
||||
};
|
||||
|
||||
base_tile.tex_sub(&allsky, &s, &d);
|
||||
base_tile.tex_sub(&allsky, &s, &d);
|
||||
|
||||
src_idx += 1;
|
||||
}
|
||||
src_idx += 1;
|
||||
}
|
||||
|
||||
tiles.push(base_tile);
|
||||
}
|
||||
base_tile
|
||||
});
|
||||
|
||||
Ok(tiles)
|
||||
}
|
||||
@@ -272,7 +267,7 @@ fn handle_allsky_fits<F: ImageFormat>(
|
||||
allsky_data: &[<<F as ImageFormat>::P as Pixel>::Item],
|
||||
tile_size: i32,
|
||||
texture_size: i32,
|
||||
) -> Result<Vec<ImageBuffer<F>>, JsValue> {
|
||||
) -> Result<impl Iterator<Item=ImageBuffer<F>>, JsValue> {
|
||||
let allsky_tile_size = std::cmp::min(tile_size, 64);
|
||||
let width_allsky_px = 27 * allsky_tile_size;
|
||||
let height_allsky_px = 29 * allsky_tile_size;
|
||||
@@ -286,9 +281,8 @@ fn handle_allsky_fits<F: ImageFormat>(
|
||||
|
||||
let allsky = ImageBuffer::<F>::new(reversed_rows_data, width_allsky_px, height_allsky_px);
|
||||
|
||||
let allsky_tiles = handle_allsky_file::<F>(allsky, allsky_tile_size, texture_size, tile_size)?
|
||||
.into_iter()
|
||||
.map(|image| {
|
||||
let allsky_tiles_iter = handle_allsky_file::<F>(allsky, allsky_tile_size, texture_size, tile_size)?
|
||||
.map(move |image| {
|
||||
// The GPU does a specific transformation on the UV
|
||||
// for FITS tiles
|
||||
// We must revert this to be compatible with this GPU transformation
|
||||
@@ -298,10 +292,9 @@ fn handle_allsky_fits<F: ImageFormat>(
|
||||
}
|
||||
|
||||
ImageBuffer::<F>::new(new_image_data, tile_size, tile_size)
|
||||
})
|
||||
.collect();
|
||||
});
|
||||
|
||||
Ok(allsky_tiles)
|
||||
Ok(allsky_tiles_iter)
|
||||
}
|
||||
|
||||
use al_core::image::format::RGBA8U;
|
||||
|
||||
@@ -75,13 +75,22 @@ pub fn get_raster_shader<'a>(
|
||||
shaders: &'a mut ShaderManager,
|
||||
config: &HiPSConfig,
|
||||
) -> Result<&'a Shader, JsValue> {
|
||||
if config.get_format().is_colored() && cmap.label() == "native" {
|
||||
crate::shader::get_shader(
|
||||
gl,
|
||||
shaders,
|
||||
"hips_rasterizer_raster.vert",
|
||||
"hips_rasterizer_color.frag",
|
||||
)
|
||||
if config.get_format().is_colored() {
|
||||
if cmap.label() == "native" {
|
||||
crate::shader::get_shader(
|
||||
gl,
|
||||
shaders,
|
||||
"hips_rasterizer_raster.vert",
|
||||
"hips_rasterizer_color.frag",
|
||||
)
|
||||
} else {
|
||||
crate::shader::get_shader(
|
||||
gl,
|
||||
shaders,
|
||||
"hips_rasterizer_raster.vert",
|
||||
"hips_rasterizer_color_to_colormap.frag",
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if config.tex_storing_unsigned_int {
|
||||
crate::shader::get_shader(
|
||||
@@ -115,13 +124,22 @@ pub fn get_raytracer_shader<'a>(
|
||||
config: &HiPSConfig,
|
||||
) -> Result<&'a Shader, JsValue> {
|
||||
//let colored_hips = config.is_colored();
|
||||
if config.get_format().is_colored() && cmap.label() == "native" {
|
||||
crate::shader::get_shader(
|
||||
gl,
|
||||
shaders,
|
||||
"hips_raytracer_raytracer.vert",
|
||||
"hips_raytracer_color.frag",
|
||||
)
|
||||
if config.get_format().is_colored() {
|
||||
if cmap.label() == "native" {
|
||||
crate::shader::get_shader(
|
||||
gl,
|
||||
shaders,
|
||||
"hips_raytracer_raytracer.vert",
|
||||
"hips_raytracer_color.frag",
|
||||
)
|
||||
} else {
|
||||
crate::shader::get_shader(
|
||||
gl,
|
||||
shaders,
|
||||
"hips_raytracer_raytracer.vert",
|
||||
"hips_raytracer_color_to_colormap.frag",
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if config.tex_storing_unsigned_int {
|
||||
crate::shader::get_shader(
|
||||
|
||||
@@ -52,13 +52,22 @@ pub fn get_raster_shader<'a>(
|
||||
shaders: &'a mut ShaderManager,
|
||||
config: &HiPSConfig,
|
||||
) -> Result<&'a Shader, JsValue> {
|
||||
if config.get_format().is_colored() && cmap.label() == "native" {
|
||||
crate::shader::get_shader(
|
||||
gl,
|
||||
shaders,
|
||||
"hips3d_rasterizer_raster.vert",
|
||||
"hips3d_rasterizer_color.frag",
|
||||
)
|
||||
if config.get_format().is_colored() {
|
||||
if cmap.label() == "native" {
|
||||
crate::shader::get_shader(
|
||||
gl,
|
||||
shaders,
|
||||
"hips3d_rasterizer_raster.vert",
|
||||
"hips3d_rasterizer_color.frag",
|
||||
)
|
||||
} else {
|
||||
crate::shader::get_shader(
|
||||
gl,
|
||||
shaders,
|
||||
"hips3d_rasterizer_raster.vert",
|
||||
"hips3d_rasterizer_color_to_colormap.frag",
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if config.tex_storing_unsigned_int {
|
||||
crate::shader::get_shader(
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::ops::Range;
|
||||
pub fn first_and_last_percent<T>(slice: &mut [T], first_percent: i32, last_percent: i32) -> Range<T>
|
||||
pub fn first_and_last_percent<T>(slice: &mut [T], mut first_percent: i32, mut last_percent: i32) -> Range<T>
|
||||
where
|
||||
T: PartialOrd + Copy,
|
||||
{
|
||||
if first_percent > last_percent {
|
||||
std::mem::swap(&mut first_percent, &mut last_percent);
|
||||
}
|
||||
|
||||
let n = slice.len();
|
||||
let first_pct_idx = ((first_percent as f32) * 0.01 * (n as f32)) as usize;
|
||||
let last_pct_idx = ((last_percent as f32) * 0.01 * (n as f32)) as usize;
|
||||
let i1 = ((first_percent as f32) * 0.01 * (n as f32)) as usize;
|
||||
let i2 = ((last_percent as f32) * 0.01 * (n as f32)) as usize;
|
||||
|
||||
let min_val = {
|
||||
let (_, min_val, _) = slice.select_nth_unstable_by(first_pct_idx, |a, b| {
|
||||
a.partial_cmp(b).unwrap_or(Ordering::Greater)
|
||||
});
|
||||
let (_, min_val, _) = slice.select_nth_unstable_by(i1, |a, b| a.partial_cmp(b).unwrap_or(Ordering::Greater));
|
||||
*min_val
|
||||
};
|
||||
let max_val = {
|
||||
let (_, max_val, _) = slice.select_nth_unstable_by(last_pct_idx, |a, b| {
|
||||
a.partial_cmp(b).unwrap_or(Ordering::Greater)
|
||||
});
|
||||
let (_, max_val, _) = slice.select_nth_unstable_by(i2, |a, b| a.partial_cmp(b).unwrap_or(Ordering::Greater));
|
||||
*max_val
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ pub mod grid;
|
||||
pub mod subdivide_texture;
|
||||
|
||||
use al_core::webgl_ctx::WebGlRenderingCtx;
|
||||
use al_core::convert::Cast;
|
||||
use std::fmt::Debug;
|
||||
use std::marker::Unpin;
|
||||
use std::vec;
|
||||
@@ -35,6 +36,7 @@ use crate::ProjectionType;
|
||||
use crate::ShaderManager;
|
||||
|
||||
use std::ops::Range;
|
||||
type PixelItem<F> = <<F as ImageFormat>::P as Pixel>::Item;
|
||||
|
||||
pub struct Image {
|
||||
/// A reference to the GL context
|
||||
@@ -49,7 +51,7 @@ pub struct Image {
|
||||
|
||||
/// Parameters extracted from the fits
|
||||
wcs: WCS,
|
||||
blank: f32,
|
||||
blank: Option<f32>,
|
||||
scale: f32,
|
||||
offset: f32,
|
||||
cuts: Range<f32>,
|
||||
@@ -75,6 +77,7 @@ use fitsrs::hdu::header::extension;
|
||||
use fitsrs::hdu::AsyncHDU;
|
||||
use futures::io::BufReader;
|
||||
use futures::AsyncReadExt;
|
||||
|
||||
impl Image {
|
||||
pub async fn from_reader_and_wcs<R, F>(
|
||||
gl: &WebGlContext,
|
||||
@@ -103,9 +106,8 @@ impl Image {
|
||||
// apply bscale to the cuts
|
||||
let offset = offset.unwrap_or(0.0);
|
||||
let scale = scale.unwrap_or(1.0);
|
||||
let blank = blank.unwrap_or(std::f32::NAN);
|
||||
|
||||
let (textures, mut cuts) = if width <= max_tex_size as u64 && height <= max_tex_size as u64
|
||||
let (textures, cuts) = if width <= max_tex_size as u64 && height <= max_tex_size as u64
|
||||
{
|
||||
max_tex_size_x = width as usize;
|
||||
max_tex_size_y = height as usize;
|
||||
@@ -113,7 +115,7 @@ impl Image {
|
||||
|
||||
let num_pixels_to_read = (width as usize) * (height as usize);
|
||||
let num_bytes_to_read =
|
||||
num_pixels_to_read * std::mem::size_of::<<F::P as Pixel>::Item>() * F::NUM_CHANNELS;
|
||||
num_pixels_to_read * std::mem::size_of::<F::P>();
|
||||
let mut buf = vec![0; num_bytes_to_read];
|
||||
|
||||
let _ = reader
|
||||
@@ -123,30 +125,11 @@ impl Image {
|
||||
|
||||
// bytes aligned
|
||||
unsafe {
|
||||
let slice = std::slice::from_raw_parts(
|
||||
buf[..].as_ptr() as *const <F::P as Pixel>::Item,
|
||||
let data = std::slice::from_raw_parts_mut(
|
||||
buf[..].as_mut_ptr() as *mut PixelItem<F>,
|
||||
(num_pixels_to_read as usize) * F::NUM_CHANNELS,
|
||||
);
|
||||
|
||||
let cuts = if F::NUM_CHANNELS == 1 {
|
||||
let mut samples = slice
|
||||
.iter()
|
||||
.filter_map(|item| {
|
||||
let t: f32 =
|
||||
<<F::P as Pixel>::Item as al_core::convert::Cast<f32>>::cast(*item);
|
||||
if t.is_nan() || t == blank {
|
||||
None
|
||||
} else {
|
||||
Some(t)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
cuts::first_and_last_percent(&mut samples, 1, 99)
|
||||
} else {
|
||||
0.0..1.0
|
||||
};
|
||||
|
||||
let texture = Texture2D::create_from_raw_pixels::<F>(
|
||||
gl,
|
||||
width as i32,
|
||||
@@ -171,9 +154,52 @@ impl Image {
|
||||
WebGlRenderingCtx::CLAMP_TO_EDGE,
|
||||
),
|
||||
],
|
||||
Some(slice),
|
||||
Some(data),
|
||||
)?;
|
||||
|
||||
let cuts = match F::CHANNEL_TYPE {
|
||||
ChannelType::R32F | ChannelType::R64F => {
|
||||
let pixels = std::slice::from_raw_parts(data.as_ptr() as *const f32, data.len() / 4);
|
||||
|
||||
let mut sub_pixels = pixels.iter()
|
||||
.step_by(100)
|
||||
.filter(|pixel| (*pixel).is_finite())
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
cuts::first_and_last_percent(&mut sub_pixels, 1, 99)
|
||||
}
|
||||
ChannelType::R8UI | ChannelType::R16I | ChannelType::R32I => {
|
||||
// BLANK is only valid for those channels/BITPIX (> 0)
|
||||
if let Some(blank) = blank {
|
||||
let mut sub_pixels = data.iter()
|
||||
.step_by(100)
|
||||
.filter_map(|pixel| {
|
||||
let pixel = <PixelItem::<F> as Cast<f32>>::cast(*pixel);
|
||||
|
||||
if pixel != blank {
|
||||
Some(pixel)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
cuts::first_and_last_percent(&mut sub_pixels, 1, 99)
|
||||
} else {
|
||||
// No blank value => we consider all the values
|
||||
let mut sub_pixels = data.iter()
|
||||
.step_by(100)
|
||||
.map(|pixel| <PixelItem::<F> as Cast<f32>>::cast(*pixel))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
cuts::first_and_last_percent(&mut sub_pixels, 1, 99)
|
||||
}
|
||||
}
|
||||
// RGB(A) images
|
||||
_ => 0.0..1.0
|
||||
};
|
||||
|
||||
(vec![texture], cuts)
|
||||
}
|
||||
} else {
|
||||
@@ -195,7 +221,7 @@ impl Image {
|
||||
let start = cuts.start * scale + offset;
|
||||
let end = cuts.end * scale + offset;
|
||||
|
||||
cuts = start..end;
|
||||
let cuts = start..end;
|
||||
|
||||
let num_indices = vec![];
|
||||
let indices = vec![];
|
||||
@@ -319,26 +345,22 @@ impl Image {
|
||||
gl: &WebGlContext,
|
||||
hdu: &mut AsyncHDU<'a, BufReader<R>, extension::image::Image>,
|
||||
coo_sys: CooSystem,
|
||||
//reader: &'a mut BufReader<R>,
|
||||
) -> Result<Self, JsValue>
|
||||
where
|
||||
R: AsyncRead + Unpin + Debug + 'a,
|
||||
{
|
||||
// Load the fits file
|
||||
// Load the FITS file
|
||||
let header = hdu.get_header();
|
||||
|
||||
let scale = header
|
||||
.get_parsed::<f64>(b"BSCALE ")
|
||||
.unwrap_or(Ok(1.0))
|
||||
.unwrap() as f32;
|
||||
.map(|v| v.unwrap());
|
||||
let offset = header
|
||||
.get_parsed::<f64>(b"BZERO ")
|
||||
.unwrap_or(Ok(0.0))
|
||||
.unwrap() as f32;
|
||||
.map(|v| v.unwrap());
|
||||
let blank = header
|
||||
.get_parsed::<f64>(b"BLANK ")
|
||||
.unwrap_or(Ok(std::f64::NAN))
|
||||
.unwrap() as f32;
|
||||
.map(|v| v.unwrap());
|
||||
|
||||
// Create a WCS from a specific header unit
|
||||
let wcs = WCS::from_fits_header(header)
|
||||
@@ -354,9 +376,9 @@ impl Image {
|
||||
gl,
|
||||
reader,
|
||||
wcs,
|
||||
Some(scale),
|
||||
Some(offset),
|
||||
Some(blank),
|
||||
scale.map(|v| v as f32),
|
||||
offset.map(|v| v as f32),
|
||||
blank.map(|v| v as f32),
|
||||
coo_sys,
|
||||
)
|
||||
.await
|
||||
@@ -368,9 +390,9 @@ impl Image {
|
||||
gl,
|
||||
reader,
|
||||
wcs,
|
||||
Some(scale),
|
||||
Some(offset),
|
||||
Some(blank),
|
||||
scale.map(|v| v as f32),
|
||||
offset.map(|v| v as f32),
|
||||
blank.map(|v| v as f32),
|
||||
coo_sys,
|
||||
)
|
||||
.await
|
||||
@@ -382,9 +404,9 @@ impl Image {
|
||||
gl,
|
||||
reader,
|
||||
wcs,
|
||||
Some(scale),
|
||||
Some(offset),
|
||||
Some(blank),
|
||||
scale.map(|v| v as f32),
|
||||
offset.map(|v| v as f32),
|
||||
blank.map(|v| v as f32),
|
||||
coo_sys,
|
||||
)
|
||||
.await
|
||||
@@ -401,9 +423,9 @@ impl Image {
|
||||
gl,
|
||||
reader,
|
||||
wcs,
|
||||
Some(scale),
|
||||
Some(offset),
|
||||
Some(blank),
|
||||
scale.map(|v| v as f32),
|
||||
offset.map(|v| v as f32),
|
||||
blank.map(|v| v as f32),
|
||||
coo_sys,
|
||||
)
|
||||
.await
|
||||
@@ -415,9 +437,9 @@ impl Image {
|
||||
gl,
|
||||
reader,
|
||||
wcs,
|
||||
Some(scale),
|
||||
Some(offset),
|
||||
Some(blank),
|
||||
scale.map(|v| v as f32),
|
||||
offset.map(|v| v as f32),
|
||||
blank.map(|v| v as f32),
|
||||
coo_sys,
|
||||
)
|
||||
.await
|
||||
@@ -434,9 +456,9 @@ impl Image {
|
||||
gl,
|
||||
reader,
|
||||
wcs,
|
||||
Some(scale),
|
||||
Some(offset),
|
||||
Some(blank),
|
||||
scale.map(|v| v as f32),
|
||||
offset.map(|v| v as f32),
|
||||
blank.map(|v| v as f32),
|
||||
coo_sys,
|
||||
)
|
||||
.await
|
||||
@@ -724,15 +746,22 @@ impl Image {
|
||||
let texture = &self.textures[idx_tex];
|
||||
let num_indices = self.num_indices[idx] as i32;
|
||||
|
||||
shader
|
||||
.bind(&self.gl)
|
||||
let shader_bound = shader
|
||||
.bind(&self.gl);
|
||||
|
||||
shader_bound
|
||||
.attach_uniforms_from(colormaps)
|
||||
.attach_uniforms_with_params_from(color, colormaps)
|
||||
.attach_uniform("opacity", opacity)
|
||||
.attach_uniform("tex", texture)
|
||||
.attach_uniform("scale", &self.scale)
|
||||
.attach_uniform("offset", &self.offset)
|
||||
.attach_uniform("blank", &self.blank)
|
||||
.attach_uniform("offset", &self.offset);
|
||||
|
||||
if let Some(blank) = self.blank {
|
||||
shader_bound.attach_uniform("blank", &blank);
|
||||
}
|
||||
|
||||
shader_bound
|
||||
.bind_vertex_array_object_ref(&self.vao)
|
||||
.draw_elements_with_i32(
|
||||
WebGl2RenderingContext::TRIANGLES,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use al_core::image::format::ChannelType;
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
use futures::AsyncReadExt;
|
||||
@@ -10,13 +11,16 @@ use al_core::Texture2D;
|
||||
use al_core::WebGlContext;
|
||||
use std::ops::Range;
|
||||
|
||||
use al_core::convert::Cast;
|
||||
type PixelItem<F> = <<F as ImageFormat>::P as Pixel>::Item;
|
||||
|
||||
pub async fn crop_image<'a, F, R>(
|
||||
gl: &WebGlContext,
|
||||
width: u64,
|
||||
height: u64,
|
||||
mut reader: R,
|
||||
max_tex_size: u64,
|
||||
blank: f32,
|
||||
blank: Option<f32>,
|
||||
) -> Result<(Vec<Texture2D>, Range<f32>), JsValue>
|
||||
where
|
||||
F: ImageFormat,
|
||||
@@ -67,10 +71,12 @@ where
|
||||
let mut pixels_written = 0;
|
||||
let num_pixels = width * height;
|
||||
|
||||
let step_x_cut = (width / 50) as usize;
|
||||
let step_y_cut = (height / 50) as usize;
|
||||
const PIXEL_STEP: u64 = 256;
|
||||
|
||||
let mut samples = vec![];
|
||||
let step_x_cut = (width / PIXEL_STEP) as usize;
|
||||
let step_y_cut = (height / PIXEL_STEP) as usize;
|
||||
|
||||
let mut sub_pixels = vec![];
|
||||
|
||||
let step_cut = step_x_cut.max(step_y_cut) + 1;
|
||||
|
||||
@@ -101,36 +107,60 @@ where
|
||||
|
||||
let dy = (pixels_written / width) - off_y_px;
|
||||
let view = unsafe {
|
||||
let slice = std::slice::from_raw_parts(
|
||||
let data = std::slice::from_raw_parts(
|
||||
buf[..num_bytes_to_read].as_ptr() as *const <F::P as Pixel>::Item,
|
||||
(num_pixels_to_read as usize) * F::NUM_CHANNELS,
|
||||
);
|
||||
|
||||
// compute the cuts if the pixel is grayscale
|
||||
if F::NUM_CHANNELS == 1 {
|
||||
// fill the samples buffer
|
||||
if (pixels_written / width) % (step_cut as u64) == 0 {
|
||||
// We are in a good line
|
||||
let xmin = pixels_written % width;
|
||||
if (pixels_written / width) % (step_cut as u64) == 0 {
|
||||
// We are in a good line
|
||||
let xmin = pixels_written % width;
|
||||
|
||||
for i in (0..width).step_by(step_cut) {
|
||||
if (xmin..(xmin + num_pixels_to_read)).contains(&i) {
|
||||
let j = (i - xmin) as usize;
|
||||
match F::CHANNEL_TYPE {
|
||||
ChannelType::R32F | ChannelType::R64F => {
|
||||
let pixels = std::slice::from_raw_parts(data.as_ptr() as *const f32, data.len() / 4);
|
||||
|
||||
let sj: f32 = <<F::P as Pixel>::Item as al_core::convert::Cast<
|
||||
f32,
|
||||
>>::cast(slice[j]);
|
||||
if !sj.is_nan() {
|
||||
if blank != sj {
|
||||
samples.push(sj);
|
||||
for i in (0..width).step_by(step_cut) {
|
||||
if (xmin..(xmin + num_pixels_to_read)).contains(&i) {
|
||||
let j = (i - xmin) as usize;
|
||||
|
||||
if pixels[j].is_finite() {
|
||||
sub_pixels.push(pixels[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
ChannelType::R8UI | ChannelType::R16I | ChannelType::R32I => {
|
||||
if let Some(blank) = blank {
|
||||
for i in (0..width).step_by(step_cut) {
|
||||
if (xmin..(xmin + num_pixels_to_read)).contains(&i) {
|
||||
let j = (i - xmin) as usize;
|
||||
|
||||
let pixel = <PixelItem::<F> as Cast<f32>>::cast(data[j]);
|
||||
|
||||
if pixel != blank {
|
||||
sub_pixels.push(pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i in (0..width).step_by(step_cut) {
|
||||
if (xmin..(xmin + num_pixels_to_read)).contains(&i) {
|
||||
let j = (i - xmin) as usize;
|
||||
|
||||
let pixel = <PixelItem::<F> as Cast<f32>>::cast(data[j]);
|
||||
sub_pixels.push(pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
// colored pixels
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
F::view(slice)
|
||||
F::view(data)
|
||||
};
|
||||
|
||||
(&mut tex_chunks[id_t as usize])
|
||||
@@ -151,8 +181,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
let cuts = if F::NUM_CHANNELS == 1 {
|
||||
cuts::first_and_last_percent(&mut samples, 1, 99)
|
||||
let cuts = if F::CHANNEL_TYPE.is_colored() {
|
||||
cuts::first_and_last_percent(&mut sub_pixels, 1, 99)
|
||||
} else {
|
||||
0.0..1.0
|
||||
};
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
use crate::{healpix::coverage::HEALPixCoverage, CameraViewPort, ShaderManager};
|
||||
use web_sys::WebGl2RenderingContext;
|
||||
|
||||
use al_core::WebGlContext;
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
|
||||
@@ -225,8 +225,6 @@ impl Layers {
|
||||
let cdid = self.ids.get(layer).unwrap_abort();
|
||||
|
||||
if let Some(hips) = self.hipses.get(cdid) {
|
||||
let hips_cfg = hips.get_config();
|
||||
|
||||
let allsky = hips.is_allsky();
|
||||
let opaque = meta.opacity == 1.0;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#version 300 es
|
||||
precision lowp float;
|
||||
precision mediump int;
|
||||
precision highp float;
|
||||
precision highp int;
|
||||
|
||||
layout (location = 0) in vec2 ndc_pos;
|
||||
layout (location = 1) in vec2 uv;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#version 300 es
|
||||
precision lowp float;
|
||||
precision lowp sampler2D;
|
||||
precision highp float;
|
||||
precision highp sampler2D;
|
||||
precision lowp isampler2D;
|
||||
precision lowp usampler2D;
|
||||
precision mediump int;
|
||||
precision highp int;
|
||||
|
||||
out vec4 out_frag_color;
|
||||
in vec2 frag_uv;
|
||||
@@ -25,19 +25,31 @@ uniform float reversed;
|
||||
#include ./../hips/transfer_funcs.glsl;
|
||||
#include ./../hips/tonal_corrections.glsl;
|
||||
|
||||
vec4 apply_colormap_to_grayscale(float x, float a) {
|
||||
vec4 apply_colormap_to_grayscale(float x) {
|
||||
float alpha = x * scale + offset;
|
||||
alpha = transfer_func(H, alpha, min_value, max_value);
|
||||
|
||||
// apply reversed
|
||||
alpha = mix(alpha, 1.0 - alpha, reversed);
|
||||
|
||||
vec4 new_color = mix(colormap_f(alpha) * a, vec4(0.0), float(x == blank || isnan(x)));
|
||||
vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x)));
|
||||
return apply_tonal(new_color);
|
||||
}
|
||||
|
||||
highp float decode32(highp vec4 rgba) {
|
||||
highp float Sign = 1.0 - step(128.0,rgba[0])*2.0;
|
||||
highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0;
|
||||
if (abs(Exponent + 127.0) < 1e-3) {
|
||||
return 0.0;
|
||||
}
|
||||
highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000);
|
||||
highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 ));
|
||||
return Result;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 color = texture(tex, frag_uv);
|
||||
out_frag_color = apply_colormap_to_grayscale(color.r, color.a);
|
||||
highp float value = decode32(texture(tex, frag_uv).abgr*255.0);
|
||||
// reconstruct the float value
|
||||
out_frag_color = apply_colormap_to_grayscale(value);
|
||||
out_frag_color.a = out_frag_color.a * opacity;
|
||||
}
|
||||
@@ -1,15 +1,11 @@
|
||||
uniform float scale;
|
||||
uniform float offset;
|
||||
uniform float blank;
|
||||
|
||||
uniform float min_value;
|
||||
uniform float max_value;
|
||||
uniform int H;
|
||||
|
||||
uniform float reversed;
|
||||
|
||||
uniform float size_tile_uv;
|
||||
|
||||
uniform int tex_storing_fits;
|
||||
|
||||
#include ../colormaps/colormap.glsl;
|
||||
@@ -26,7 +22,6 @@ vec3 reverse_uv(vec3 uv) {
|
||||
return uv;
|
||||
}
|
||||
|
||||
|
||||
vec4 get_color_from_texture(vec3 UV) {
|
||||
vec4 color = get_pixels(UV);
|
||||
|
||||
@@ -40,21 +35,34 @@ vec4 get_color_from_texture(vec3 UV) {
|
||||
return apply_tonal(color);
|
||||
}
|
||||
|
||||
vec4 apply_colormap_to_grayscale(float x, float a) {
|
||||
vec4 apply_colormap_to_grayscale(float x) {
|
||||
float alpha = x * scale + offset;
|
||||
alpha = transfer_func(H, alpha, min_value, max_value);
|
||||
|
||||
// apply reversed
|
||||
alpha = mix(alpha, 1.0 - alpha, reversed);
|
||||
|
||||
vec4 new_color = mix(colormap_f(alpha) * a, vec4(0.0), float(x == blank || isnan(x)));
|
||||
vec4 new_color = mix(colormap_f(alpha), vec4(0.0), float(isinf(x) || isnan(x)));
|
||||
return apply_tonal(new_color);
|
||||
}
|
||||
|
||||
highp float decode32(highp vec4 rgba) {
|
||||
highp float Sign = 1.0 - step(128.0,rgba[0])*2.0;
|
||||
highp float Exponent = 2.0 * mod(rgba[0],128.0) + step(128.0,rgba[1]) - 127.0;
|
||||
highp float Mantissa = mod(rgba[1],128.0)*65536.0 + rgba[2]*256.0 +rgba[3] + float(0x800000);
|
||||
highp float Result = Sign * exp2(Exponent) * (Mantissa * exp2(-23.0 ));
|
||||
return Result;
|
||||
}
|
||||
|
||||
vec4 get_colormap_from_grayscale_texture(vec3 UV) {
|
||||
// FITS data pixels are reversed along the y axis
|
||||
vec3 uv = mix(UV, reverse_uv(UV), float(tex_storing_fits == 1));
|
||||
|
||||
vec4 color = get_pixels(uv);
|
||||
return apply_colormap_to_grayscale(color.r, color.a);
|
||||
float value = decode32(get_pixels(uv).abgr*255.0);
|
||||
return apply_colormap_to_grayscale(value);
|
||||
}
|
||||
|
||||
vec4 get_colormap_from_color_texture(vec3 uv) {
|
||||
float value = get_pixels(uv).r;
|
||||
return apply_colormap_to_grayscale(value);
|
||||
}
|
||||
24
src/glsl/webgl2/hips/rasterizer/color_to_colormap.frag
Normal file
24
src/glsl/webgl2/hips/rasterizer/color_to_colormap.frag
Normal file
@@ -0,0 +1,24 @@
|
||||
#version 300 es
|
||||
precision lowp float;
|
||||
precision lowp sampler2DArray;
|
||||
precision lowp isampler2DArray;
|
||||
precision lowp usampler2DArray;
|
||||
|
||||
uniform sampler2DArray tex;
|
||||
|
||||
in vec3 frag_uv_start;
|
||||
in vec3 frag_uv_end;
|
||||
in float frag_blending_factor;
|
||||
|
||||
out vec4 out_frag_color;
|
||||
uniform float opacity;
|
||||
|
||||
#include ../color.glsl;
|
||||
|
||||
void main() {
|
||||
vec4 color_start = get_colormap_from_color_texture(frag_uv_start);
|
||||
vec4 color_end = get_colormap_from_color_texture(frag_uv_end);
|
||||
|
||||
out_frag_color = mix(color_start, color_end, frag_blending_factor);
|
||||
out_frag_color.a = opacity * out_frag_color.a;
|
||||
}
|
||||
52
src/glsl/webgl2/hips/raytracer/color_to_colormap.frag
Normal file
52
src/glsl/webgl2/hips/raytracer/color_to_colormap.frag
Normal file
@@ -0,0 +1,52 @@
|
||||
#version 300 es
|
||||
precision lowp float;
|
||||
precision lowp sampler2DArray;
|
||||
precision lowp sampler2DArray;
|
||||
precision lowp isampler2DArray;
|
||||
precision mediump int;
|
||||
|
||||
in vec3 frag_pos;
|
||||
in vec2 out_clip_pos;
|
||||
out vec4 out_frag_color;
|
||||
|
||||
struct Tile {
|
||||
int uniq; // Healpix cell
|
||||
int texture_idx; // Index in the texture buffer
|
||||
float start_time; // Absolute time that the load has been done in ms
|
||||
float empty;
|
||||
};
|
||||
|
||||
uniform Tile textures_tiles[12];
|
||||
|
||||
uniform float opacity;
|
||||
uniform sampler2DArray tex;
|
||||
|
||||
struct TileColor {
|
||||
Tile tile;
|
||||
vec4 color;
|
||||
bool found;
|
||||
};
|
||||
|
||||
#include ../color.glsl;
|
||||
#include ../../projection/hpx_proj.glsl;
|
||||
|
||||
vec4 get_tile_color(vec3 pos) {
|
||||
HashDxDy result = hash_with_dxdy(0, pos.zxy);
|
||||
|
||||
int idx = result.idx;
|
||||
vec2 uv = vec2(result.dy, result.dx);
|
||||
Tile tile = textures_tiles[idx];
|
||||
|
||||
vec2 offset = uv;
|
||||
vec3 UV = vec3(offset, float(tile.texture_idx));
|
||||
|
||||
vec4 color = get_colormap_from_color_texture(UV);
|
||||
color.a *= (1.0 - tile.empty);
|
||||
return color;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 c = get_tile_color(normalize(frag_pos));
|
||||
out_frag_color = c;
|
||||
out_frag_color.a = out_frag_color.a * opacity;
|
||||
}
|
||||
21
src/glsl/webgl2/hips3d/rasterizer/color_to_colormap.frag
Normal file
21
src/glsl/webgl2/hips3d/rasterizer/color_to_colormap.frag
Normal file
@@ -0,0 +1,21 @@
|
||||
#version 300 es
|
||||
precision lowp float;
|
||||
precision lowp sampler3D;
|
||||
precision lowp isampler3D;
|
||||
precision lowp usampler3D;
|
||||
|
||||
uniform sampler3D tex;
|
||||
|
||||
in vec3 frag_uv;
|
||||
|
||||
out vec4 out_frag_color;
|
||||
uniform float opacity;
|
||||
|
||||
#include ../../hips/color.glsl;
|
||||
|
||||
void main() {
|
||||
vec4 color = get_colormap_from_color_texture(vec3(frag_uv.xy, mod(frag_uv.z, 32.0) / 32.0));
|
||||
|
||||
out_frag_color = color;
|
||||
out_frag_color.a = opacity * out_frag_color.a;
|
||||
}
|
||||
Reference in New Issue
Block a user