use wgpu::*;
use std::path::Path;
use std::fs;
pub struct Texture {
pub texture: wgpu::Texture,
pub view: TextureView,
pub sampler: Sampler,
pub width: u32,
pub height: u32,
}
impl Texture {
pub fn from_jxl(
device: &Device,
queue: &Queue,
path: &Path,
) -> Result<Self, Box<dyn std::error::Error>> {
let data = fs::read(path)?;
use jxl_oxide::JxlImage;
let mut image = JxlImage::builder()
.read(&data[..])
.map_err(|e| format!("Failed to read JXL header: {}", e))?;
let header = image.image_header();
let width = header.size.width as u32;
let height = header.size.height as u32;
let render = image.render_frame(0)
.map_err(|e| format!("Failed to render frame: {}", e))?;
let frame_buffer = render.image_all_channels();
let f32_pixels = frame_buffer.buf();
let pixels_per_channel = (width * height) as usize;
let num_channels = f32_pixels.len() / pixels_per_channel;
let mut rgba8_data = Vec::with_capacity(pixels_per_channel * 4);
if num_channels == 3 {
for i in 0..pixels_per_channel {
rgba8_data.push((f32_pixels[i * 3].clamp(0.0, 1.0) * 255.0) as u8); rgba8_data.push((f32_pixels[i * 3 + 1].clamp(0.0, 1.0) * 255.0) as u8); rgba8_data.push((f32_pixels[i * 3 + 2].clamp(0.0, 1.0) * 255.0) as u8); rgba8_data.push(255); }
} else if num_channels >= 4 {
for i in 0..pixels_per_channel {
rgba8_data.push((f32_pixels[i * num_channels].clamp(0.0, 1.0) * 255.0) as u8); rgba8_data.push((f32_pixels[i * num_channels + 1].clamp(0.0, 1.0) * 255.0) as u8); rgba8_data.push((f32_pixels[i * num_channels + 2].clamp(0.0, 1.0) * 255.0) as u8); rgba8_data.push((f32_pixels[i * num_channels + 3].clamp(0.0, 1.0) * 255.0) as u8); }
} else {
return Err(format!("Unsupported channel count: {} (expected 3 or 4)", num_channels).into());
}
let pixels = rgba8_data.as_slice();
let texture_size = Extent3d {
width,
height,
depth_or_array_layers: 1,
};
let texture = device.create_texture(&TextureDescriptor {
label: Some("JXL Texture"),
size: texture_size,
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Rgba8UnormSrgb,
usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
view_formats: &[],
});
queue.write_texture(
ImageCopyTexture {
texture: &texture,
mip_level: 0,
origin: Origin3d::ZERO,
aspect: TextureAspect::All,
},
pixels,
ImageDataLayout {
offset: 0,
bytes_per_row: Some(4 * width),
rows_per_image: Some(height),
},
texture_size,
);
let view = texture.create_view(&TextureViewDescriptor::default());
let sampler = device.create_sampler(&SamplerDescriptor {
label: Some("Texture Sampler"),
address_mode_u: AddressMode::ClampToEdge,
address_mode_v: AddressMode::ClampToEdge,
address_mode_w: AddressMode::ClampToEdge,
mag_filter: FilterMode::Linear,
min_filter: FilterMode::Linear,
mipmap_filter: FilterMode::Nearest,
..Default::default()
});
Ok(Self {
texture,
view,
sampler,
width,
height,
})
}
}