JPEG case: draw black when the tile is missing

This commit is contained in:
bmatthieu3
2025-03-05 11:00:19 +01:00
committed by Matthieu Baumann
parent 3f248a82ea
commit 5f9c9d9154
6 changed files with 62 additions and 39 deletions

View File

@@ -20,6 +20,7 @@ use crate::{
tile_fetcher::TileFetcherQueue, tile_fetcher::TileFetcherQueue,
time::DeltaTime, time::DeltaTime,
}; };
use al_core::image::format::ChannelType;
use wcs::WCS; use wcs::WCS;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
@@ -702,7 +703,7 @@ impl App {
tile_copied = true; tile_copied = true;
match hips { match hips {
HiPS::D2(hips) => { HiPS::D2(hips) => {
hips.add_tile(&tile.cell, img, tile.time_req)? hips.add_tile(&tile.cell, Some(img), tile.time_req)?
} }
HiPS::D3(hips) => hips.add_tile( HiPS::D3(hips) => hips.add_tile(
&tile.cell, &tile.cell,
@@ -711,10 +712,19 @@ impl App {
tile.channel.unwrap() as u16, tile.channel.unwrap() as u16,
)?, )?,
} }
self.time_start_blending = Time::now(); self.time_start_blending = Time::now();
} },
None => (), // In case of JPEG tile missing, add it to the HiPS because it must be drawn as black
None if cfg.get_format().get_channel() == ChannelType::RGB8U => {
self.request_redraw = true;
match hips {
HiPS::D2(hips) => {
hips.add_tile::<ImageType>(&tile.cell, None, tile.time_req)?
}
HiPS::D3(_) => (),
}
},
_ => ()
}; };
} }
} }

View File

@@ -196,7 +196,7 @@ impl HiPS2DBuffer {
let mutex_locked = image.borrow(); let mutex_locked = image.borrow();
let images = mutex_locked.as_ref().unwrap_abort(); let images = mutex_locked.as_ref().unwrap_abort();
for (idx, image) in images.iter().enumerate() { for (idx, image) in images.iter().enumerate() {
self.push(&HEALPixCell(depth_tile, idx as u64), image, time_req)?; self.push(&HEALPixCell(depth_tile, idx as u64), Some(image), time_req)?;
} }
} }
@@ -272,7 +272,7 @@ impl HiPS2DBuffer {
pub fn push<I: Image>( pub fn push<I: Image>(
&mut self, &mut self,
cell: &HEALPixCell, cell: &HEALPixCell,
image: I, image: Option<I>,
time_request: Time, time_request: Time,
) -> Result<(), JsValue> { ) -> Result<(), JsValue> {
if !self.contains_tile(cell) { if !self.contains_tile(cell) {
@@ -331,14 +331,16 @@ impl HiPS2DBuffer {
&mut self.base_textures[idx as usize] &mut self.base_textures[idx as usize]
}; };
send_to_gpu( if let Some(image) = image {
cell, send_to_gpu(
texture, cell,
image, texture,
&self.texture_2d_array, image,
&mut self.config, &self.texture_2d_array,
)?; &mut self.config,
)?;
}
texture.append( texture.append(
cell, // The tile cell cell, // The tile cell
&self.config, &self.config,

View File

@@ -417,18 +417,14 @@ impl HiPS2D {
for cell in &self.hpx_cells_in_view { for cell in &self.hpx_cells_in_view {
// filter textures that are not in the moc // filter textures that are not in the moc
let cell = if let Some(moc) = self.footprint_moc.as_ref() { let cell = if let Some(moc) = self.footprint_moc.as_ref() {
if moc.intersects_cell(&cell) { if moc.intersects_cell(&cell) || channel == ChannelType::RGB8U {
// Rasterizer does not render tiles that are not in the MOC
// This is not a problem for transparency rendered HiPses (FITS or PNG)
// but JPEG tiles do have black when no pixels data is found
// We therefore must draw in black for the tiles outside the HiPS MOC
Some(&cell) Some(&cell)
} else { } else {
if channel == ChannelType::RGB8U { None
// Rasterizer does not render tiles that are not in the MOC
// This is not a problem for transparency rendered HiPses (FITS or PNG)
// but JPEG tiles do have black when no pixels data is found
// We therefore must draw in black for the tiles outside the HiPS MOC
Some(&cell)
} else {
None
}
} }
} else { } else {
Some(&cell) Some(&cell)
@@ -627,7 +623,7 @@ impl HiPS2D {
pub fn add_tile<I: Image>( pub fn add_tile<I: Image>(
&mut self, &mut self,
cell: &HEALPixCell, cell: &HEALPixCell,
image: I, image: Option<I>,
time_request: Time, time_request: Time,
) -> Result<(), JsValue> { ) -> Result<(), JsValue> {
self.buffer.push(&cell, image, time_request) self.buffer.push(&cell, image, time_request)

View File

@@ -182,6 +182,7 @@ impl<'a> HpxTexture2DUniforms<'a> {
use al_core::shader::{SendUniforms, ShaderBound}; use al_core::shader::{SendUniforms, ShaderBound};
impl<'a> SendUniforms for HpxTexture2DUniforms<'a> { impl<'a> SendUniforms for HpxTexture2DUniforms<'a> {
// Info: These uniforms are used for raytracing drawing mode only
fn attach_uniforms<'b>(&self, shader: &'b ShaderBound<'b>) -> &'b ShaderBound<'b> { fn attach_uniforms<'b>(&self, shader: &'b ShaderBound<'b>) -> &'b ShaderBound<'b> {
shader shader
.attach_uniform(&format!("{}{}", self.name, "uniq"), &self.texture.uniq) .attach_uniform(&format!("{}{}", self.name, "uniq"), &self.texture.uniq)
@@ -191,8 +192,12 @@ impl<'a> SendUniforms for HpxTexture2DUniforms<'a> {
) )
.attach_uniform( .attach_uniform(
&format!("{}{}", self.name, "empty"), &format!("{}{}", self.name, "empty"),
//&((self.texture.full as u8) as f32), // This is useful for FITS tiles only because:
&0.0, // - for JPEG, missing tiles are inserted in the buffer and black is drawn
// - for PNG, tiles are not inserted but default color chosen is fully transparent (might be vec4(0.0, 0.0, 0.0, 0.0))
//
// Therefore for FITS files we must indicate GPU which base tiles are missing so that we draw fully transparent pixels
&((!self.texture.full as u8) as f32),
) )
.attach_uniform( .attach_uniform(
&format!("{}{}", self.name, "start_time"), &format!("{}{}", self.name, "start_time"),

View File

@@ -219,23 +219,33 @@ impl Layers {
// Check whether a hips to plot is allsky // Check whether a hips to plot is allsky
// if neither are, we draw a font // if neither are, we draw a font
// if there are, we do not draw nothing // if there are, we do not draw nothing
let render_background_color = !self.layers.iter().any(|layer| { let mut background_color = None;
for layer in self.layers.iter() {
let meta = self.meta.get(layer).unwrap_abort(); let meta = self.meta.get(layer).unwrap_abort();
let cdid = self.ids.get(layer).unwrap_abort(); let cdid = self.ids.get(layer).unwrap_abort();
if let Some(hips) = self.hipses.get(cdid) { if let Some(hips) = self.hipses.get(cdid) {
let hips_cfg = hips.get_config(); let hips_cfg = hips.get_config();
(hips.is_allsky() || hips_cfg.get_format().get_channel() == ChannelType::RGB8U)
&& meta.opacity == 1.0 let allsky = hips.is_allsky();
let opaque = meta.opacity == 1.0;
background_color = match (allsky, opaque) {
(true, true) => None,
_ => Some(self.background_color),
};
} else { } else {
// image fits case // image fits case. We render a background
false background_color = Some(self.background_color);
} }
});
if background_color.is_some() {
break;
}
}
// Need to render transparency font // Need to render transparency font
if render_background_color { if let Some(background_color) = &background_color {
let background_color = &self.background_color;
let vao = if raytracing { let vao = if raytracing {
raytracer.get_vao() raytracer.get_vao()
} else { } else {
@@ -275,8 +285,8 @@ impl Layers {
} }
} }
let rendered_layers = &self.layers[idx_start_layer..]; let layers_to_render = &self.layers[idx_start_layer..];
for layer in rendered_layers { for layer in layers_to_render {
let draw_opt = self.meta.get(layer).expect("Meta should be found"); let draw_opt = self.meta.get(layer).expect("Meta should be found");
if draw_opt.visible() { if draw_opt.visible() {
// 1. Update the hips if necessary // 1. Update the hips if necessary

View File

@@ -26,7 +26,7 @@ uniform float opacity;
#include ../../projection/hpx_proj.glsl; #include ../../projection/hpx_proj.glsl;
vec4 get_tile_color(vec3 pos) { vec4 get_tile_color(vec3 pos) {
HashDxDy result = hash_with_dxdy(0, pos.zxy); HashDxDy result = hash_with_dxdy(0, pos.zxy);
int idx = result.idx; int idx = result.idx;
vec2 uv = vec2(result.dy, result.dx); vec2 uv = vec2(result.dy, result.dx);