1use crate as wgpu;
7use std::ops::Deref;
8use std::path::Path;
9
10pub trait Pixel: image::Pixel {
17 const TEXTURE_FORMAT: wgpu::TextureFormat;
19}
20
21pub struct ImageReadMapping<'buffer> {
25 buffer: &'buffer wgpu::RowPaddedBuffer,
26 view: wgpu::BufferView<'buffer>,
27}
28
29pub struct ImageHolder<'b, P: Pixel>(image::ImageBuffer<P, &'b [P::Subpixel]>);
31impl<'b, P: Pixel> Deref for ImageHolder<'b, P> {
32 type Target = image::ImageBuffer<P, &'b [P::Subpixel]>;
33 fn deref(&self) -> &Self::Target {
34 &self.0
35 }
36}
37
38impl wgpu::TextureBuilder {
39 pub const REQUIRED_IMAGE_TEXTURE_USAGE: wgpu::TextureUsages = wgpu::TextureUsages::COPY_DST;
41
42 pub fn from_image_view<T>(image_view: &T) -> Self
53 where
54 T: image::GenericImageView,
55 T::Pixel: Pixel,
56 {
57 builder_from_image_view(image_view)
58 }
59
60 pub fn default_image_texture_usage() -> wgpu::TextureUsages {
62 wgpu::TextureUsages::COPY_SRC
63 | wgpu::TextureUsages::COPY_DST
64 | wgpu::TextureUsages::TEXTURE_BINDING
65 | wgpu::TextureUsages::RENDER_ATTACHMENT
66 }
67}
68
69pub trait WithDeviceQueuePair {
75 fn with_device_queue_pair<F, O>(self, f: F) -> O
76 where
77 F: FnOnce(&wgpu::Device, &wgpu::Queue) -> O;
78}
79
80impl wgpu::Texture {
81 pub fn from_path<T, P>(src: T, path: P) -> image::ImageResult<Self>
97 where
98 T: WithDeviceQueuePair,
99 P: AsRef<Path>,
100 {
101 let path = path.as_ref();
102 let usage = wgpu::TextureBuilder::default_image_texture_usage();
103 src.with_device_queue_pair(|device, queue| {
104 wgpu::Texture::load_from_path(device, queue, usage, path)
105 })
106 }
107
108 pub fn from_image<T>(src: T, image: &image::DynamicImage) -> Self
127 where
128 T: WithDeviceQueuePair,
129 {
130 let usage = wgpu::TextureBuilder::default_image_texture_usage();
131 src.with_device_queue_pair(|device, queue| {
132 wgpu::Texture::load_from_image(device, queue, usage, image)
133 })
134 }
135
136 pub fn load_from_path<P>(
140 device: &wgpu::Device,
141 queue: &wgpu::Queue,
142 usage: wgpu::TextureUsages,
143 path: P,
144 ) -> image::ImageResult<Self>
145 where
146 P: AsRef<Path>,
147 {
148 let path = path.as_ref();
149 let image = image::open(path)?;
150 Ok(Self::load_from_image(device, queue, usage, &image))
151 }
152
153 pub fn load_from_image(
162 device: &wgpu::Device,
163 queue: &wgpu::Queue,
164 usage: wgpu::TextureUsages,
165 image: &image::DynamicImage,
166 ) -> Self {
167 load_texture_from_image(device, queue, usage, image)
168 }
169
170 pub fn load_from_image_buffer<P, Container>(
177 device: &wgpu::Device,
178 queue: &wgpu::Queue,
179 usage: wgpu::TextureUsages,
180 buffer: &image::ImageBuffer<P, Container>,
181 ) -> Self
182 where
183 P: 'static + Pixel,
184 Container: std::ops::Deref<Target = [P::Subpixel]>,
185 {
186 load_texture_from_image_buffer(device, queue, usage, buffer)
187 }
188
189 pub fn load_array_from_image_buffers<'a, I, P, Container>(
198 device: &wgpu::Device,
199 queue: &wgpu::Queue,
200 usage: wgpu::TextureUsages,
201 buffers: I,
202 ) -> Option<Self>
203 where
204 I: IntoIterator<Item = &'a image::ImageBuffer<P, Container>>,
205 I::IntoIter: ExactSizeIterator,
206 P: 'static + Pixel,
207 Container: 'a + std::ops::Deref<Target = [P::Subpixel]>,
208 {
209 load_texture_array_from_image_buffers(device, queue, usage, buffers)
210 }
211
212 pub fn encode_load_from_image(
224 device: &wgpu::Device,
225 encoder: &mut wgpu::CommandEncoder,
226 usage: wgpu::TextureUsages,
227 image: &image::DynamicImage,
228 ) -> Self {
229 encode_load_texture_from_image(device, encoder, usage, image)
230 }
231
232 pub fn encode_load_from_image_buffer<P, Container>(
242 device: &wgpu::Device,
243 encoder: &mut wgpu::CommandEncoder,
244 usage: wgpu::TextureUsages,
245 buffer: &image::ImageBuffer<P, Container>,
246 ) -> Self
247 where
248 P: 'static + Pixel,
249 Container: std::ops::Deref<Target = [P::Subpixel]>,
250 {
251 encode_load_texture_from_image_buffer(device, encoder, usage, buffer)
252 }
253
254 pub fn encode_load_3d_from_image_buffers<'a, I, P, Container>(
269 device: &wgpu::Device,
270 encoder: &mut wgpu::CommandEncoder,
271 usage: wgpu::TextureUsages,
272 buffers: I,
273 ) -> Option<Self>
274 where
275 I: IntoIterator<Item = &'a image::ImageBuffer<P, Container>>,
276 I::IntoIter: ExactSizeIterator,
277 P: 'static + Pixel,
278 Container: 'a + std::ops::Deref<Target = [P::Subpixel]>,
279 {
280 encode_load_texture_array_from_image_buffers(device, encoder, usage, buffers)
281 }
282}
283
284impl wgpu::RowPaddedBuffer {
285 pub fn from_image_buffer<P, Container>(
287 device: &wgpu::Device,
288 image_buffer: &image::ImageBuffer<P, Container>,
289 ) -> Self
290 where
291 P: 'static + Pixel,
292 Container: std::ops::Deref<Target = [P::Subpixel]>,
293 {
294 let result = Self::new(
295 device,
296 image_buffer.width() * P::COLOR_TYPE.bytes_per_pixel() as u32,
297 image_buffer.height(),
298 wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC,
299 );
300 result.write(unsafe { wgpu::bytes::from_slice(&*image_buffer) });
305 result
306 }
307
308 pub async fn read<'b>(&'b self) -> Result<ImageReadMapping<'b>, wgpu::BufferAsyncError> {
313 let slice = self.buffer.slice(..);
314 let (tx, rx) = futures::channel::oneshot::channel();
315
316 slice.map_async(wgpu::MapMode::Read, |res| {
317 tx.send(res).expect("Failed to send map_async result");
318 });
319
320 rx.await.expect("Failed to receive map_async result")?;
321
322 Ok(wgpu::ImageReadMapping {
323 buffer: self,
324 view: slice.get_mapped_range(),
328 })
329 }
330}
331
332impl<'buffer> ImageReadMapping<'buffer> {
333 pub unsafe fn as_image<P>(&self) -> image::SubImage<ImageHolder<P>>
338 where
339 P: Pixel + 'static,
340 {
341 let subpixel_size = std::mem::size_of::<P::Subpixel>() as u32;
342 let pixel_size = subpixel_size * P::CHANNEL_COUNT as u32;
343 assert_eq!(pixel_size, P::COLOR_TYPE.bytes_per_pixel() as u32);
344
345 assert_eq!(
346 self.buffer.padded_width() % pixel_size,
347 0,
348 "buffer padded width not an even multiple of primitive size"
349 );
350 assert_eq!(
351 self.buffer.width() % pixel_size,
352 0,
353 "buffer row width not an even multiple of primitive size"
354 );
355
356 let width_pixels = self.buffer.width() / pixel_size;
357 let padded_width_pixels = self.buffer.padded_width() / pixel_size;
358
359 let container = wgpu::bytes::to_slice::<P::Subpixel>(&self.view[..]);
366
367 let full_image =
368 image::ImageBuffer::from_raw(padded_width_pixels, self.buffer.height(), container)
369 .expect("nannou internal error: incorrect buffer size");
370 image::SubImage::new(
371 ImageHolder(full_image),
372 0,
373 0,
374 width_pixels,
375 self.buffer.height(),
376 )
377 }
378}
379
380impl Pixel for image::Bgra<u8> {
381 const TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Bgra8UnormSrgb;
382}
383
384impl Pixel for image::Luma<u8> {
385 const TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::R8Unorm;
386}
387
388impl Pixel for image::Luma<i8> {
389 const TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::R8Snorm;
390}
391
392impl Pixel for image::Luma<u16> {
393 const TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::R16Uint;
394}
395
396impl Pixel for image::Luma<i16> {
397 const TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::R16Sint;
398}
399
400impl Pixel for image::LumaA<u8> {
401 const TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rg8Unorm;
402}
403
404impl Pixel for image::LumaA<i8> {
405 const TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rg8Snorm;
406}
407
408impl Pixel for image::LumaA<u16> {
409 const TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rg16Uint;
410}
411
412impl Pixel for image::LumaA<i16> {
413 const TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rg16Sint;
414}
415
416impl Pixel for image::Rgba<u8> {
417 const TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8UnormSrgb;
418}
419
420impl Pixel for image::Rgba<i8> {
421 const TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8Snorm;
422}
423
424impl Pixel for image::Rgba<u16> {
425 const TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba16Uint;
426}
427
428impl Pixel for image::Rgba<i16> {
429 const TEXTURE_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba16Sint;
430}
431
432impl<'a> WithDeviceQueuePair for (&'a wgpu::Device, &'a wgpu::Queue) {
433 fn with_device_queue_pair<F, O>(self, f: F) -> O
434 where
435 F: FnOnce(&wgpu::Device, &wgpu::Queue) -> O,
436 {
437 let (device, queue) = self;
438 f(device, queue)
439 }
440}
441
442impl<'a> WithDeviceQueuePair for &'a wgpu::DeviceQueuePair {
443 fn with_device_queue_pair<F, O>(self, f: F) -> O
444 where
445 F: FnOnce(&wgpu::Device, &wgpu::Queue) -> O,
446 {
447 let device = self.device();
448 let queue = self.queue();
449 f(&*device, &*queue)
450 }
451}
452
453impl<'a, 'b, T> wgpu::WithDeviceQueuePair for &'a std::cell::Ref<'b, T>
454where
455 &'a T: wgpu::WithDeviceQueuePair,
456{
457 fn with_device_queue_pair<F, O>(self, f: F) -> O
458 where
459 F: FnOnce(&wgpu::Device, &wgpu::Queue) -> O,
460 {
461 (**self).with_device_queue_pair(f)
462 }
463}
464
465pub fn format_from_image_color_type(color_type: image::ColorType) -> Option<wgpu::TextureFormat> {
472 let format = match color_type {
473 image::ColorType::L8 => wgpu::TextureFormat::R8Unorm,
474 image::ColorType::La8 => wgpu::TextureFormat::Rg8Unorm,
475 image::ColorType::Rgba8 => wgpu::TextureFormat::Rgba8UnormSrgb,
476 image::ColorType::L16 => wgpu::TextureFormat::R16Uint,
477 image::ColorType::La16 => wgpu::TextureFormat::Rg16Uint,
478 image::ColorType::Rgba16 => wgpu::TextureFormat::Rgba16Uint,
479 image::ColorType::Bgra8 => wgpu::TextureFormat::Bgra8UnormSrgb,
480 _ => return None,
481 };
482 Some(format)
483}
484
485pub fn builder_from_image_view<T>(image: &T) -> wgpu::TextureBuilder
494where
495 T: image::GenericImageView,
496 T::Pixel: Pixel,
497{
498 let (width, height) = image.dimensions();
499 let format = <T::Pixel as Pixel>::TEXTURE_FORMAT;
500 wgpu::TextureBuilder::new()
501 .size([width, height])
502 .format(format)
503 .usage(wgpu::TextureBuilder::default_image_texture_usage())
504}
505
506pub fn load_texture_from_image(
519 device: &wgpu::Device,
520 queue: &wgpu::Queue,
521 usage: wgpu::TextureUsages,
522 image: &image::DynamicImage,
523) -> wgpu::Texture {
524 use image::DynamicImage::*;
525 match image {
526 ImageLuma8(img) => load_texture_from_image_buffer(device, queue, usage, img),
527 ImageLumaA8(img) => load_texture_from_image_buffer(device, queue, usage, img),
528 ImageRgba8(img) => load_texture_from_image_buffer(device, queue, usage, img),
529 ImageBgra8(img) => load_texture_from_image_buffer(device, queue, usage, img),
530 ImageLuma16(img) => load_texture_from_image_buffer(device, queue, usage, img),
531 ImageLumaA16(img) => load_texture_from_image_buffer(device, queue, usage, img),
532 ImageRgba16(img) => load_texture_from_image_buffer(device, queue, usage, img),
533 ImageRgb8(_img) => {
534 let img = image.to_rgba8();
535 load_texture_from_image_buffer(device, queue, usage, &img)
536 }
537 ImageBgr8(_img) => {
538 let img = image.to_bgra8();
539 load_texture_from_image_buffer(device, queue, usage, &img)
540 }
541 ImageRgb16(_img) => {
542 let img = image.to_rgba16();
543 load_texture_from_image_buffer(device, queue, usage, &img)
544 }
545 }
546}
547
548pub fn load_texture_from_image_buffer<P, Container>(
559 device: &wgpu::Device,
560 queue: &wgpu::Queue,
561 usage: wgpu::TextureUsages,
562 buffer: &image::ImageBuffer<P, Container>,
563) -> wgpu::Texture
564where
565 P: 'static + Pixel,
566 Container: std::ops::Deref<Target = [P::Subpixel]>,
567{
568 let texture = wgpu::TextureBuilder::from_image_view(buffer)
570 .usage(wgpu::TextureBuilder::REQUIRED_IMAGE_TEXTURE_USAGE | usage)
571 .build(device);
572
573 let extent = texture.extent();
575 let format = texture.format();
576 let block_size = format
577 .block_copy_size(None)
578 .expect("Expected the format to have a block size");
579 let bytes_per_row = extent.width * block_size as u32;
580 let image_data_layout = wgpu::ImageDataLayout {
581 offset: 0,
582 bytes_per_row: Some(bytes_per_row),
583 rows_per_image: None,
584 };
585
586 let image_copy_texture = texture.as_image_copy();
588
589 let data = unsafe { wgpu::bytes::from_slice(&*buffer) };
594
595 queue.write_texture(image_copy_texture, data, image_data_layout, extent);
596 texture
597}
598
599pub fn load_texture_array_from_image_buffers<'a, I, P, Container>(
608 device: &wgpu::Device,
609 queue: &wgpu::Queue,
610 usage: wgpu::TextureUsages,
611 buffers: I,
612) -> Option<wgpu::Texture>
613where
614 I: IntoIterator<Item = &'a image::ImageBuffer<P, Container>>,
615 I::IntoIter: ExactSizeIterator,
616 P: 'static + Pixel,
617 Container: 'a + std::ops::Deref<Target = [P::Subpixel]>,
618{
619 let mut buffers = buffers.into_iter();
620 let array_layers = buffers.len() as u32;
621 let first_buffer = buffers.next()?;
622
623 let (width, height) = first_buffer.dimensions();
625 let extent = wgpu::Extent3d {
626 width,
627 height,
628 depth_or_array_layers: array_layers,
629 };
630 let texture = wgpu::TextureBuilder::from_image_view(first_buffer)
631 .extent(extent)
632 .dimension(wgpu::TextureDimension::D2) .usage(wgpu::TextureBuilder::REQUIRED_IMAGE_TEXTURE_USAGE | usage)
634 .build(device);
635
636 let format = texture.format();
638 let block_size = format
639 .block_copy_size(None)
640 .expect("Expected the format to have a block size");
641 let bytes_per_row = extent.width * block_size as u32;
642 let image_data_layout = wgpu::ImageDataLayout {
643 offset: 0,
644 bytes_per_row: Some(bytes_per_row),
645 rows_per_image: Some(height),
646 };
647
648 let capacity = bytes_per_row as usize * height as usize * array_layers as usize;
663 let mut data: Vec<u8> = Vec::with_capacity(capacity);
664 for buffer in Some(first_buffer).into_iter().chain(buffers) {
665 let layer_data = unsafe { wgpu::bytes::from_slice(&*buffer) };
666 data.extend_from_slice(layer_data);
667 }
668
669 let image_copy_texture = texture.as_image_copy();
671
672 queue.write_texture(image_copy_texture, &data, image_data_layout, extent);
673
674 Some(texture)
675}
676
677pub fn encode_load_texture_from_image(
689 device: &wgpu::Device,
690 encoder: &mut wgpu::CommandEncoder,
691 usage: wgpu::TextureUsages,
692 image: &image::DynamicImage,
693) -> wgpu::Texture {
694 use image::DynamicImage::*;
695 match image {
696 ImageLuma8(img) => encode_load_texture_from_image_buffer(device, encoder, usage, img),
697 ImageLumaA8(img) => encode_load_texture_from_image_buffer(device, encoder, usage, img),
698 ImageRgba8(img) => encode_load_texture_from_image_buffer(device, encoder, usage, img),
699 ImageBgra8(img) => encode_load_texture_from_image_buffer(device, encoder, usage, img),
700 ImageLuma16(img) => encode_load_texture_from_image_buffer(device, encoder, usage, img),
701 ImageLumaA16(img) => encode_load_texture_from_image_buffer(device, encoder, usage, img),
702 ImageRgba16(img) => encode_load_texture_from_image_buffer(device, encoder, usage, img),
703 ImageRgb8(_img) => {
704 let img = image.to_rgba8();
705 encode_load_texture_from_image_buffer(device, encoder, usage, &img)
706 }
707 ImageBgr8(_img) => {
708 let img = image.to_bgra8();
709 encode_load_texture_from_image_buffer(device, encoder, usage, &img)
710 }
711 ImageRgb16(_img) => {
712 let img = image.to_rgba16();
713 encode_load_texture_from_image_buffer(device, encoder, usage, &img)
714 }
715 }
716}
717
718pub fn encode_load_texture_from_image_buffer<P, Container>(
728 device: &wgpu::Device,
729 encoder: &mut wgpu::CommandEncoder,
730 usage: wgpu::TextureUsages,
731 buffer: &image::ImageBuffer<P, Container>,
732) -> wgpu::Texture
733where
734 P: 'static + Pixel,
735 Container: std::ops::Deref<Target = [P::Subpixel]>,
736{
737 let texture = wgpu::TextureBuilder::from_image_view(buffer)
739 .usage(wgpu::TextureBuilder::REQUIRED_IMAGE_TEXTURE_USAGE | usage)
740 .build(device);
741
742 let buffer_image = wgpu::RowPaddedBuffer::from_image_buffer(device, buffer);
743 buffer_image.encode_copy_into(encoder, &texture);
744
745 texture
746}
747
748pub fn encode_load_texture_array_from_image_buffers<'a, I, P, Container>(
761 device: &wgpu::Device,
762 encoder: &mut wgpu::CommandEncoder,
763 usage: wgpu::TextureUsages,
764 buffers: I,
765) -> Option<wgpu::Texture>
766where
767 I: IntoIterator<Item = &'a image::ImageBuffer<P, Container>>,
768 I::IntoIter: ExactSizeIterator,
769 P: 'static + Pixel,
770 Container: 'a + std::ops::Deref<Target = [P::Subpixel]>,
771{
772 let mut buffers = buffers.into_iter();
773 let array_layers = buffers.len() as u32;
774 let first_buffer = buffers.next()?;
775
776 let (width, height) = first_buffer.dimensions();
777
778 let texture = wgpu::TextureBuilder::from_image_view(first_buffer)
780 .extent(wgpu::Extent3d {
781 width,
782 height,
783 depth_or_array_layers: array_layers,
784 })
785 .dimension(wgpu::TextureDimension::D2) .usage(wgpu::TextureBuilder::REQUIRED_IMAGE_TEXTURE_USAGE | usage)
787 .build(device);
788
789 for (layer, buffer) in Some(first_buffer).into_iter().chain(buffers).enumerate() {
791 let buffer = wgpu::RowPaddedBuffer::from_image_buffer(device, &buffer);
793 buffer.encode_copy_into_at(encoder, &texture, layer as u32);
794 }
795
796 Some(texture)
797}