aboutsummaryrefslogtreecommitdiff
path: root/vgcodec/src/export.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vgcodec/src/export.rs')
-rw-r--r--vgcodec/src/export.rs80
1 files changed, 80 insertions, 0 deletions
diff --git a/vgcodec/src/export.rs b/vgcodec/src/export.rs
new file mode 100644
index 0000000..7b02085
--- /dev/null
+++ b/vgcodec/src/export.rs
@@ -0,0 +1,80 @@
+use crate::app::App;
+use image::RgbaImage;
+use std::{num::NonZeroU32, sync::Arc};
+use wgpu::{
+ Buffer, Extent3d, ImageCopyBuffer, ImageCopyTexture, ImageDataLayout, Origin3d, Texture,
+};
+
+pub struct Exporter {
+ app: Arc<App>,
+ size: Extent3d,
+
+ padded_bytes_per_row: u32,
+ export_buffer: Buffer,
+}
+
+impl Exporter {
+ pub fn new(app: &Arc<App>, size: Extent3d) -> Self {
+ let App { device, .. } = app.as_ref();
+
+ let bytes_per_pixel = std::mem::size_of::<u32>() as u32;
+ let unpadded_bytes_per_row = size.width * bytes_per_pixel;
+ let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
+ let padded_bytes_per_row_padding = (align - unpadded_bytes_per_row % align) % align;
+ let padded_bytes_per_row = unpadded_bytes_per_row + padded_bytes_per_row_padding;
+
+ let export_buffer_size = (padded_bytes_per_row * size.height) as u64;
+ let export_buffer = device.create_buffer(&wgpu::BufferDescriptor {
+ label: None,
+ size: export_buffer_size,
+ usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
+ mapped_at_creation: false,
+ });
+
+ Self {
+ padded_bytes_per_row,
+ app: app.clone(),
+ size,
+ export_buffer,
+ }
+ }
+ pub async fn run(&self, texture: &Texture, save_path: &str) {
+ let App { device, queue, .. } = self.app.as_ref();
+
+ let mut encoder =
+ device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
+
+ encoder.copy_texture_to_buffer(
+ ImageCopyTexture {
+ texture,
+ aspect: wgpu::TextureAspect::All,
+ mip_level: 0,
+ origin: Origin3d::ZERO,
+ },
+ ImageCopyBuffer {
+ buffer: &self.export_buffer,
+ layout: ImageDataLayout {
+ offset: 0,
+ bytes_per_row: Some(NonZeroU32::new(self.padded_bytes_per_row).unwrap()),
+ rows_per_image: None,
+ },
+ },
+ self.size,
+ );
+
+ queue.submit(Some(encoder.finish()));
+
+ let buffer_slice = self.export_buffer.slice(..);
+ let (sender, receiver) = futures_intrusive::channel::shared::oneshot_channel();
+ buffer_slice.map_async(wgpu::MapMode::Read, move |v| {
+ sender.send(v.unwrap()).unwrap()
+ });
+ device.poll(wgpu::Maintain::Wait);
+ receiver.receive().await;
+
+ let data = buffer_slice.get_mapped_range();
+ let result: Vec<u8> = bytemuck::cast_slice(&data).to_vec();
+ let image = RgbaImage::from_raw(self.size.width, self.size.height, result).unwrap();
+ image.save(save_path).unwrap();
+ }
+}