Added offscreen rendering support for wgpu & tiny-skia exposed with the window::screenshot command.

This commit is contained in:
Bingus 2023-03-25 10:45:39 -07:00 committed by Héctor Ramón Jiménez
parent c15f1b5f65
commit 233196eb14
No known key found for this signature in database
GPG key ID: 140CC052C94F138E
16 changed files with 893 additions and 24 deletions

View file

@ -60,6 +60,7 @@ mod debug;
#[cfg(not(feature = "debug"))]
#[path = "debug/null.rs"]
mod debug;
mod screenshot;
pub use iced_core as core;
pub use iced_futures as futures;
@ -68,4 +69,5 @@ pub use command::Command;
pub use debug::Debug;
pub use font::Font;
pub use program::Program;
pub use screenshot::{CropError, Screenshot};
pub use user_interface::UserInterface;

80
runtime/src/screenshot.rs Normal file
View file

@ -0,0 +1,80 @@
use iced_core::{Rectangle, Size};
use std::fmt::{Debug, Formatter};
/// Data of a screenshot, captured with `window::screenshot()`.
///
/// The `bytes` of this screenshot will always be ordered as `RGBA` in the sRGB color space.
#[derive(Clone)]
pub struct Screenshot {
/// The bytes of the [`Screenshot`].
pub bytes: Vec<u8>,
/// The size of the [`Screenshot`].
pub size: Size<u32>,
}
impl Debug for Screenshot {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Screenshot: {{ \n bytes: {}\n size: {:?} }}",
self.bytes.len(),
self.size
)
}
}
impl Screenshot {
/// Creates a new [`Screenshot`].
pub fn new(bytes: Vec<u8>, size: Size<u32>) -> Self {
Self { bytes, size }
}
/// Crops a [`Screenshot`] to the provided `region`. This will always be relative to the
/// top-left corner of the [`Screenshot`].
pub fn crop(&self, region: Rectangle<u32>) -> Result<Self, CropError> {
if region.width == 0 || region.height == 0 {
return Err(CropError::Zero);
}
if region.x + region.width > self.size.width
|| region.y + region.height > self.size.height
{
return Err(CropError::OutOfBounds);
}
// Image is always RGBA8 = 4 bytes per pixel
const PIXEL_SIZE: usize = 4;
let bytes_per_row = self.size.width as usize * PIXEL_SIZE;
let row_range = region.y as usize..(region.y + region.height) as usize;
let column_range = region.x as usize * PIXEL_SIZE
..(region.x + region.width) as usize * PIXEL_SIZE;
let chopped = self.bytes.chunks(bytes_per_row).enumerate().fold(
vec![],
|mut acc, (row, bytes)| {
if row_range.contains(&row) {
acc.extend(&bytes[column_range.clone()]);
}
acc
},
);
Ok(Self {
bytes: chopped,
size: Size::new(region.width, region.height),
})
}
}
#[derive(Debug, thiserror::Error)]
/// Errors that can occur when cropping a [`Screenshot`].
pub enum CropError {
#[error("The cropped region is out of bounds.")]
/// The cropped region's size is out of bounds.
OutOfBounds,
#[error("The cropped region is not visible.")]
/// The cropped region's size is zero.
Zero,
}

View file

@ -7,6 +7,7 @@ use crate::command::{self, Command};
use crate::core::time::Instant;
use crate::core::window::{Event, Icon, Level, Mode, UserAttention};
use crate::futures::subscription::{self, Subscription};
use crate::screenshot::Screenshot;
/// Subscribes to the frames of the window of the running application.
///
@ -115,3 +116,10 @@ pub fn fetch_id<Message>(
pub fn change_icon<Message>(icon: Icon) -> Command<Message> {
Command::single(command::Action::Window(Action::ChangeIcon(icon)))
}
/// Captures a [`Screenshot`] from the window.
pub fn screenshot<Message>(
f: impl FnOnce(Screenshot) -> Message + Send + 'static,
) -> Command<Message> {
Command::single(command::Action::Window(Action::Screenshot(Box::new(f))))
}

View file

@ -1,6 +1,7 @@
use crate::core::window::{Icon, Level, Mode, UserAttention};
use crate::futures::MaybeSend;
use crate::screenshot::Screenshot;
use std::fmt;
/// An operation to be performed on some window.
@ -89,6 +90,8 @@ pub enum Action<T> {
/// - **X11:** Has no universal guidelines for icon sizes, so you're at the whims of the WM. That
/// said, it's usually in the same ballpark as on Windows.
ChangeIcon(Icon),
/// Screenshot the viewport of the window.
Screenshot(Box<dyn FnOnce(Screenshot) -> T + 'static>),
}
impl<T> Action<T> {
@ -118,6 +121,11 @@ impl<T> Action<T> {
Self::ChangeLevel(level) => Action::ChangeLevel(level),
Self::FetchId(o) => Action::FetchId(Box::new(move |s| f(o(s)))),
Self::ChangeIcon(icon) => Action::ChangeIcon(icon),
Self::Screenshot(tag) => {
Action::Screenshot(Box::new(move |screenshot| {
f(tag(screenshot))
}))
}
}
}
}
@ -155,6 +163,7 @@ impl<T> fmt::Debug for Action<T> {
Self::ChangeIcon(_icon) => {
write!(f, "Action::ChangeIcon(icon)")
}
Self::Screenshot(_) => write!(f, "Action::Screenshot"),
}
}
}