1fn load_image_or_svg(bytes: &[u8], max_dim: Option<u32>) -> Result<(Vec<u8>, u32, u32), image::ImageError> {
2 match image::load_from_memory(bytes) {
3 Ok(img) => {
4 let resized = if let Some(max) = max_dim {
5 img.thumbnail(max, max)
6 } else {
7 img
8 };
9 let rgba = resized.to_rgba8();
10 let dim = rgba.dimensions();
11 Ok((rgba.into_raw(), dim.0, dim.1))
12 }
13 Err(e) => {
14 let opt = resvg::usvg::Options::default();
16 match resvg::usvg::Tree::from_data(bytes, &opt) {
17 Ok(rtree) => {
18 let size = rtree.size();
19 let mut w = size.width();
20 let mut h = size.height();
21
22 let target_max = max_dim.unwrap_or(256) as f32;
23 let current_max = w.max(h);
24 if current_max > 0.0 {
25 let scale = target_max / current_max;
26 w *= scale;
27 h *= scale;
28 }
29
30 let w_u32 = w.round() as u32;
31 let h_u32 = h.round() as u32;
32
33 if w_u32 > 0 && h_u32 > 0 {
34 if let Some(mut pixmap) = resvg::tiny_skia::Pixmap::new(w_u32, h_u32) {
35 let transform = resvg::tiny_skia::Transform::from_scale(
36 w / size.width(),
37 h / size.height(),
38 );
39 resvg::render(&rtree, transform, &mut pixmap.as_mut());
40 return Ok((pixmap.data().to_vec(), w_u32, h_u32));
41 }
42 }
43 Err(e)
44 }
45 Err(_) => Err(e),
46 }
47 }
48 }
49}
50
51pub fn create_texture_bind_group(
52 device: &wgpu::Device,
53 queue: &wgpu::Queue,
54 bgl: &wgpu::BindGroupLayout,
55 bytes: &[u8],
56) -> Result<(wgpu::BindGroup, u32, u32), image::ImageError> {
57 let (rgba, w, h) = load_image_or_svg(bytes, None)?;
58 let size = wgpu::Extent3d {
59 width: w,
60 height: h,
61 depth_or_array_layers: 1,
62 };
63
64 let texture = device.create_texture(&wgpu::TextureDescriptor {
65 label: Some("Image Texture"),
66 size,
67 mip_level_count: 1,
68 sample_count: 1,
69 dimension: wgpu::TextureDimension::D2,
70 format: wgpu::TextureFormat::Rgba8UnormSrgb,
71 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
72 view_formats: &[],
73 });
74
75 queue.write_texture(
76 wgpu::TexelCopyTextureInfo {
77 texture: &texture,
78 mip_level: 0,
79 origin: wgpu::Origin3d::ZERO,
80 aspect: wgpu::TextureAspect::All,
81 },
82 &rgba,
83 wgpu::TexelCopyBufferLayout {
84 offset: 0,
85 bytes_per_row: Some(4 * w),
86 rows_per_image: Some(h),
87 },
88 size,
89 );
90
91 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
92 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
93 address_mode_u: wgpu::AddressMode::ClampToEdge,
94 address_mode_v: wgpu::AddressMode::ClampToEdge,
95 address_mode_w: wgpu::AddressMode::ClampToEdge,
96 mag_filter: wgpu::FilterMode::Linear,
97 min_filter: wgpu::FilterMode::Linear,
98 mipmap_filter: wgpu::MipmapFilterMode::Linear,
99 ..Default::default()
100 });
101
102 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
103 layout: bgl,
104 entries: &[
105 wgpu::BindGroupEntry {
106 binding: 0,
107 resource: wgpu::BindingResource::TextureView(&view),
108 },
109 wgpu::BindGroupEntry {
110 binding: 1,
111 resource: wgpu::BindingResource::Sampler(&sampler),
112 },
113 ],
114 label: Some("Image Bind Group"),
115 });
116
117 Ok((bind_group, w, h))
118}
119
120pub fn create_texture_bind_group_thumbnail(
121 device: &wgpu::Device,
122 queue: &wgpu::Queue,
123 bgl: &wgpu::BindGroupLayout,
124 bytes: &[u8],
125 max_dim: u32,
126) -> Result<(wgpu::BindGroup, u32, u32), image::ImageError> {
127 let (rgba, w, h) = load_image_or_svg(bytes, Some(max_dim))?;
128 let size = wgpu::Extent3d {
129 width: w,
130 height: h,
131 depth_or_array_layers: 1,
132 };
133
134 let texture = device.create_texture(&wgpu::TextureDescriptor {
135 label: Some("Thumbnail Texture"),
136 size,
137 mip_level_count: 1,
138 sample_count: 1,
139 dimension: wgpu::TextureDimension::D2,
140 format: wgpu::TextureFormat::Rgba8UnormSrgb,
141 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
142 view_formats: &[],
143 });
144
145 queue.write_texture(
146 wgpu::TexelCopyTextureInfo {
147 texture: &texture,
148 mip_level: 0,
149 origin: wgpu::Origin3d::ZERO,
150 aspect: wgpu::TextureAspect::All,
151 },
152 &rgba,
153 wgpu::TexelCopyBufferLayout {
154 offset: 0,
155 bytes_per_row: Some(4 * w),
156 rows_per_image: Some(h),
157 },
158 size,
159 );
160
161 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
162 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
163 address_mode_u: wgpu::AddressMode::ClampToEdge,
164 address_mode_v: wgpu::AddressMode::ClampToEdge,
165 address_mode_w: wgpu::AddressMode::ClampToEdge,
166 mag_filter: wgpu::FilterMode::Linear,
167 min_filter: wgpu::FilterMode::Linear,
168 mipmap_filter: wgpu::MipmapFilterMode::Linear,
169 ..Default::default()
170 });
171
172 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
173 layout: bgl,
174 entries: &[
175 wgpu::BindGroupEntry {
176 binding: 0,
177 resource: wgpu::BindingResource::TextureView(&view),
178 },
179 wgpu::BindGroupEntry {
180 binding: 1,
181 resource: wgpu::BindingResource::Sampler(&sampler),
182 },
183 ],
184 label: Some("Thumbnail Bind Group"),
185 });
186
187 Ok((bind_group, w, h))
188}