use std::sync::Arc;
use wgpu::util::DeviceExt;
pub struct GpuImage {
texture: wgpu::Texture,
view: wgpu::TextureView,
width: u32,
height: u32,
}
impl GpuImage {
pub fn from_rgba(
device: &wgpu::Device,
queue: &wgpu::Queue,
pixels: &[u8],
width: u32,
height: u32,
label: Option<&str>,
) -> Self {
let texture = device.create_texture_with_data(
queue,
&wgpu::TextureDescriptor {
label,
size: wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8Unorm,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
view_formats: &[],
},
wgpu::util::TextureDataOrder::LayerMajor,
pixels,
);
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
Self {
texture,
view,
width,
height,
}
}
pub fn view(&self) -> &wgpu::TextureView {
&self.view
}
pub fn dimensions(&self) -> (u32, u32) {
(self.width, self.height)
}
pub fn width(&self) -> u32 {
self.width
}
pub fn height(&self) -> u32 {
self.height
}
pub fn texture(&self) -> &wgpu::Texture {
&self.texture
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
pub struct GpuImageInstance {
pub dst_rect: [f32; 4],
pub src_uv: [f32; 4],
pub tint: [f32; 4],
pub params: [f32; 4],
pub clip_bounds: [f32; 4],
pub clip_radius: [f32; 4],
pub filter_a: [f32; 4],
pub filter_b: [f32; 4],
pub transform: [f32; 4],
pub clip2_bounds: [f32; 4],
pub mask_params: [f32; 4],
pub mask_info: [f32; 4],
}
impl Default for GpuImageInstance {
fn default() -> Self {
Self {
dst_rect: [0.0, 0.0, 100.0, 100.0],
src_uv: [0.0, 0.0, 1.0, 1.0],
tint: [1.0, 1.0, 1.0, 1.0],
params: [0.0, 1.0, 0.0, 0.0], clip_bounds: [-10000.0, -10000.0, 100000.0, 100000.0],
clip_radius: [0.0; 4],
filter_a: [0.0, 0.0, 0.0, 0.0], filter_b: [1.0, 1.0, 1.0, 0.0], transform: [1.0, 0.0, 0.0, 1.0], clip2_bounds: [-10000.0, -10000.0, 100000.0, 100000.0],
mask_params: [0.0; 4],
mask_info: [0.0; 4],
}
}
}
impl GpuImageInstance {
pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
Self {
dst_rect: [x, y, width, height],
..Default::default()
}
}
pub fn with_src_uv(mut self, u_min: f32, v_min: f32, u_max: f32, v_max: f32) -> Self {
self.src_uv = [u_min, v_min, u_max, v_max];
self
}
pub fn with_tint(mut self, r: f32, g: f32, b: f32, a: f32) -> Self {
self.tint = [r, g, b, a];
self
}
pub fn with_border_radius(mut self, radius: f32) -> Self {
self.params[0] = radius;
self
}
pub fn with_opacity(mut self, opacity: f32) -> Self {
self.params[1] = opacity;
self
}
pub fn with_image_border(mut self, width: f32, r: f32, g: f32, b: f32, a: f32) -> Self {
self.params[2] = width;
let ru = (r.clamp(0.0, 1.0) * 255.0).round() as u32;
let gu = (g.clamp(0.0, 1.0) * 255.0).round() as u32;
let bu = (b.clamp(0.0, 1.0) * 255.0).round() as u32;
let au = (a.clamp(0.0, 1.0) * 255.0).round() as u32;
self.params[3] = f32::from_bits((ru << 24) | (gu << 16) | (bu << 8) | au);
self
}
pub fn with_transform(mut self, a: f32, b: f32, c: f32, d: f32) -> Self {
self.transform = [a, b, c, d];
self
}
pub fn with_clip_rect(mut self, x: f32, y: f32, width: f32, height: f32) -> Self {
self.clip_bounds = [x, y, width, height];
self.clip_radius = [0.0; 4];
self
}
pub fn with_clip_rounded_rect(
mut self,
x: f32,
y: f32,
width: f32,
height: f32,
radius: f32,
) -> Self {
self.clip_bounds = [x, y, width, height];
self.clip_radius = [radius; 4];
self
}
#[allow(clippy::too_many_arguments)]
pub fn with_clip_rounded_rect_corners(
mut self,
x: f32,
y: f32,
width: f32,
height: f32,
tl: f32,
tr: f32,
br: f32,
bl: f32,
) -> Self {
self.clip_bounds = [x, y, width, height];
self.clip_radius = [tl, tr, br, bl];
self
}
pub fn with_no_clip(mut self) -> Self {
self.clip_bounds = [-10000.0, -10000.0, 100000.0, 100000.0];
self.clip_radius = [0.0; 4];
self
}
pub fn with_clip2_rect(mut self, x: f32, y: f32, width: f32, height: f32) -> Self {
self.clip2_bounds = [x, y, width, height];
self
}
pub fn with_filter(mut self, filter_a: [f32; 4], filter_b: [f32; 4]) -> Self {
self.filter_a = filter_a;
self.filter_b = filter_b;
self
}
pub fn border_radius(&self) -> f32 {
self.params[0]
}
pub fn opacity(&self) -> f32 {
self.params[1]
}
}
pub struct ImageRenderingContext {
device: Arc<wgpu::Device>,
queue: Arc<wgpu::Queue>,
sampler_linear: wgpu::Sampler,
sampler_nearest: wgpu::Sampler,
}
impl ImageRenderingContext {
pub fn new(device: Arc<wgpu::Device>, queue: Arc<wgpu::Queue>) -> Self {
let sampler_linear = device.create_sampler(&wgpu::SamplerDescriptor {
label: Some("Image Sampler (Linear)"),
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::FilterMode::Linear,
..Default::default()
});
let sampler_nearest = device.create_sampler(&wgpu::SamplerDescriptor {
label: Some("Image Sampler (Nearest)"),
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Nearest,
min_filter: wgpu::FilterMode::Nearest,
mipmap_filter: wgpu::FilterMode::Nearest,
..Default::default()
});
Self {
device,
queue,
sampler_linear,
sampler_nearest,
}
}
pub fn create_image(&self, pixels: &[u8], width: u32, height: u32) -> GpuImage {
GpuImage::from_rgba(&self.device, &self.queue, pixels, width, height, None)
}
pub fn create_image_labeled(
&self,
pixels: &[u8],
width: u32,
height: u32,
label: &str,
) -> GpuImage {
GpuImage::from_rgba(
&self.device,
&self.queue,
pixels,
width,
height,
Some(label),
)
}
pub fn sampler_linear(&self) -> &wgpu::Sampler {
&self.sampler_linear
}
pub fn sampler_nearest(&self) -> &wgpu::Sampler {
&self.sampler_nearest
}
pub fn device(&self) -> &wgpu::Device {
&self.device
}
pub fn queue(&self) -> &wgpu::Queue {
&self.queue
}
}