use image::{ExtendedColorType, ImageEncoder as _};
use re_renderer::{external::wgpu::TextureFormat, texture_readback::TextureReadbackId};
use re_viewer_context::CommandSender;
#[derive(Default)]
pub struct TextureReadbacks {
active_readbacks: Vec<TextureReadbackId>,
}
impl TextureReadbacks {
pub fn push(&mut self, id: TextureReadbackId) {
self.active_readbacks.push(id);
}
pub fn poll_and_save_texture_readbacks(
&mut self,
render_ctx: &re_renderer::RenderContext,
ui: &egui::Ui,
command_sender: &CommandSender,
) {
self.active_readbacks.retain(|id| {
if let Some(readback) = re_renderer::poll_read_texture(render_ctx, *id) {
let Some(color_type) = texture_format_to_color_type(readback.format) else {
re_log::warn!("Can't download texture with format {:?}", readback.format);
return false;
};
let mut png_bytes = Vec::new();
if let Err(err) = image::codecs::png::PngEncoder::new(&mut png_bytes).write_image(
&readback.data,
readback.extent.width,
readback.extent.height,
color_type,
) {
re_log::error!("Failed to encode preview image as PNG: {err}");
} else {
command_sender.save_file_dialog(
re_capabilities::MainThreadToken::from_egui_ui(ui),
"preview.png",
"Preview Image".to_owned(),
png_bytes,
);
}
false
} else {
true
}
});
if !self.active_readbacks.is_empty() {
ui.request_repaint();
}
}
}
fn texture_format_to_color_type(
format: re_renderer::external::wgpu::TextureFormat,
) -> Option<image::ExtendedColorType> {
match format {
TextureFormat::R8Unorm
| TextureFormat::R8Snorm
| TextureFormat::R8Uint
| TextureFormat::R8Sint => Some(ExtendedColorType::L8),
TextureFormat::Rg8Unorm
| TextureFormat::Rg8Snorm
| TextureFormat::Rg8Uint
| TextureFormat::Rg8Sint => Some(ExtendedColorType::La8),
TextureFormat::Rgba8Unorm
| TextureFormat::Rgba8UnormSrgb
| TextureFormat::Rgba8Snorm
| TextureFormat::Rgba8Uint
| TextureFormat::Rgba8Sint => Some(ExtendedColorType::Rgba8),
TextureFormat::Bgra8Unorm | TextureFormat::Bgra8UnormSrgb => Some(ExtendedColorType::Bgra8),
TextureFormat::R16Uint
| TextureFormat::R16Sint
| TextureFormat::R16Unorm
| TextureFormat::R16Snorm
| TextureFormat::R16Float => Some(ExtendedColorType::L16),
TextureFormat::Rg16Uint
| TextureFormat::Rg16Sint
| TextureFormat::Rg16Unorm
| TextureFormat::Rg16Snorm
| TextureFormat::Rg16Float => Some(ExtendedColorType::La16),
TextureFormat::Rgba16Uint
| TextureFormat::Rgba16Sint
| TextureFormat::Rgba16Unorm
| TextureFormat::Rgba16Snorm
| TextureFormat::Rgba16Float => Some(ExtendedColorType::Rgba16),
TextureFormat::Rgba32Float => Some(ExtendedColorType::Rgba32F),
_ => None,
}
}