1use std::sync::Arc;
6use wgpu::util::DeviceExt;
7
8pub struct GpuImage {
10 texture: wgpu::Texture,
12 view: wgpu::TextureView,
14 width: u32,
16 height: u32,
18}
19
20impl GpuImage {
21 pub fn from_rgba(
23 device: &wgpu::Device,
24 queue: &wgpu::Queue,
25 pixels: &[u8],
26 width: u32,
27 height: u32,
28 label: Option<&str>,
29 ) -> Self {
30 let texture = device.create_texture_with_data(
31 queue,
32 &wgpu::TextureDescriptor {
33 label,
34 size: wgpu::Extent3d {
35 width,
36 height,
37 depth_or_array_layers: 1,
38 },
39 mip_level_count: 1,
40 sample_count: 1,
41 dimension: wgpu::TextureDimension::D2,
42 format: wgpu::TextureFormat::Rgba8Unorm,
43 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
44 view_formats: &[],
45 },
46 wgpu::util::TextureDataOrder::LayerMajor,
47 pixels,
48 );
49
50 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
51
52 Self {
53 texture,
54 view,
55 width,
56 height,
57 }
58 }
59
60 pub fn view(&self) -> &wgpu::TextureView {
62 &self.view
63 }
64
65 pub fn dimensions(&self) -> (u32, u32) {
67 (self.width, self.height)
68 }
69
70 pub fn width(&self) -> u32 {
72 self.width
73 }
74
75 pub fn height(&self) -> u32 {
77 self.height
78 }
79
80 pub fn texture(&self) -> &wgpu::Texture {
82 &self.texture
83 }
84}
85
86#[repr(C)]
103#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
104pub struct GpuImageInstance {
105 pub dst_rect: [f32; 4],
107 pub src_uv: [f32; 4],
109 pub tint: [f32; 4],
111 pub params: [f32; 4],
113 pub clip_bounds: [f32; 4],
115 pub clip_radius: [f32; 4],
117 pub filter_a: [f32; 4],
119 pub filter_b: [f32; 4],
121 pub transform: [f32; 4],
124 pub clip2_bounds: [f32; 4],
128 pub mask_params: [f32; 4],
130 pub mask_info: [f32; 4],
132}
133
134impl Default for GpuImageInstance {
135 fn default() -> Self {
136 Self {
137 dst_rect: [0.0, 0.0, 100.0, 100.0],
138 src_uv: [0.0, 0.0, 1.0, 1.0],
139 tint: [1.0, 1.0, 1.0, 1.0],
140 params: [0.0, 1.0, 0.0, 0.0], clip_bounds: [-10000.0, -10000.0, 100000.0, 100000.0],
143 clip_radius: [0.0; 4],
144 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],
151 mask_params: [0.0; 4],
153 mask_info: [0.0; 4],
154 }
155 }
156}
157
158impl GpuImageInstance {
159 pub fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
161 Self {
162 dst_rect: [x, y, width, height],
163 ..Default::default()
164 }
165 }
166
167 pub fn with_src_uv(mut self, u_min: f32, v_min: f32, u_max: f32, v_max: f32) -> Self {
169 self.src_uv = [u_min, v_min, u_max, v_max];
170 self
171 }
172
173 pub fn with_tint(mut self, r: f32, g: f32, b: f32, a: f32) -> Self {
175 self.tint = [r, g, b, a];
176 self
177 }
178
179 pub fn with_border_radius(mut self, radius: f32) -> Self {
181 self.params[0] = radius;
182 self
183 }
184
185 pub fn with_opacity(mut self, opacity: f32) -> Self {
187 self.params[1] = opacity;
188 self
189 }
190
191 pub fn with_image_border(mut self, width: f32, r: f32, g: f32, b: f32, a: f32) -> Self {
194 self.params[2] = width;
195 let ru = (r.clamp(0.0, 1.0) * 255.0).round() as u32;
196 let gu = (g.clamp(0.0, 1.0) * 255.0).round() as u32;
197 let bu = (b.clamp(0.0, 1.0) * 255.0).round() as u32;
198 let au = (a.clamp(0.0, 1.0) * 255.0).round() as u32;
199 self.params[3] = f32::from_bits((ru << 24) | (gu << 16) | (bu << 8) | au);
200 self
201 }
202
203 pub fn with_transform(mut self, a: f32, b: f32, c: f32, d: f32) -> Self {
206 self.transform = [a, b, c, d];
207 self
208 }
209
210 pub fn with_clip_rect(mut self, x: f32, y: f32, width: f32, height: f32) -> Self {
212 self.clip_bounds = [x, y, width, height];
213 self.clip_radius = [0.0; 4];
214 self
215 }
216
217 pub fn with_clip_rounded_rect(
219 mut self,
220 x: f32,
221 y: f32,
222 width: f32,
223 height: f32,
224 radius: f32,
225 ) -> Self {
226 self.clip_bounds = [x, y, width, height];
227 self.clip_radius = [radius; 4];
228 self
229 }
230
231 #[allow(clippy::too_many_arguments)]
233 pub fn with_clip_rounded_rect_corners(
234 mut self,
235 x: f32,
236 y: f32,
237 width: f32,
238 height: f32,
239 tl: f32,
240 tr: f32,
241 br: f32,
242 bl: f32,
243 ) -> Self {
244 self.clip_bounds = [x, y, width, height];
245 self.clip_radius = [tl, tr, br, bl];
246 self
247 }
248
249 pub fn with_no_clip(mut self) -> Self {
251 self.clip_bounds = [-10000.0, -10000.0, 100000.0, 100000.0];
252 self.clip_radius = [0.0; 4];
253 self
254 }
255
256 pub fn with_clip2_rect(mut self, x: f32, y: f32, width: f32, height: f32) -> Self {
258 self.clip2_bounds = [x, y, width, height];
259 self
260 }
261
262 pub fn with_filter(mut self, filter_a: [f32; 4], filter_b: [f32; 4]) -> Self {
266 self.filter_a = filter_a;
267 self.filter_b = filter_b;
268 self
269 }
270
271 pub fn border_radius(&self) -> f32 {
273 self.params[0]
274 }
275
276 pub fn opacity(&self) -> f32 {
278 self.params[1]
279 }
280}
281
282pub struct ImageRenderingContext {
284 device: Arc<wgpu::Device>,
286 queue: Arc<wgpu::Queue>,
288 sampler_linear: wgpu::Sampler,
290 sampler_nearest: wgpu::Sampler,
292}
293
294impl ImageRenderingContext {
295 pub fn new(device: Arc<wgpu::Device>, queue: Arc<wgpu::Queue>) -> Self {
297 let sampler_linear = device.create_sampler(&wgpu::SamplerDescriptor {
298 label: Some("Image Sampler (Linear)"),
299 address_mode_u: wgpu::AddressMode::ClampToEdge,
300 address_mode_v: wgpu::AddressMode::ClampToEdge,
301 address_mode_w: wgpu::AddressMode::ClampToEdge,
302 mag_filter: wgpu::FilterMode::Linear,
303 min_filter: wgpu::FilterMode::Linear,
304 mipmap_filter: wgpu::FilterMode::Linear,
305 ..Default::default()
306 });
307
308 let sampler_nearest = device.create_sampler(&wgpu::SamplerDescriptor {
309 label: Some("Image Sampler (Nearest)"),
310 address_mode_u: wgpu::AddressMode::ClampToEdge,
311 address_mode_v: wgpu::AddressMode::ClampToEdge,
312 address_mode_w: wgpu::AddressMode::ClampToEdge,
313 mag_filter: wgpu::FilterMode::Nearest,
314 min_filter: wgpu::FilterMode::Nearest,
315 mipmap_filter: wgpu::FilterMode::Nearest,
316 ..Default::default()
317 });
318
319 Self {
320 device,
321 queue,
322 sampler_linear,
323 sampler_nearest,
324 }
325 }
326
327 pub fn create_image(&self, pixels: &[u8], width: u32, height: u32) -> GpuImage {
329 GpuImage::from_rgba(&self.device, &self.queue, pixels, width, height, None)
330 }
331
332 pub fn create_image_labeled(
334 &self,
335 pixels: &[u8],
336 width: u32,
337 height: u32,
338 label: &str,
339 ) -> GpuImage {
340 GpuImage::from_rgba(
341 &self.device,
342 &self.queue,
343 pixels,
344 width,
345 height,
346 Some(label),
347 )
348 }
349
350 pub fn sampler_linear(&self) -> &wgpu::Sampler {
352 &self.sampler_linear
353 }
354
355 pub fn sampler_nearest(&self) -> &wgpu::Sampler {
357 &self.sampler_nearest
358 }
359
360 pub fn device(&self) -> &wgpu::Device {
362 &self.device
363 }
364
365 pub fn queue(&self) -> &wgpu::Queue {
367 &self.queue
368 }
369}