Added offscreen rendering support for wgpu & tiny-skia exposed with the window::screenshot command.
This commit is contained in:
parent
c15f1b5f65
commit
233196eb14
16 changed files with 893 additions and 24 deletions
|
|
@ -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
80
runtime/src/screenshot.rs
Normal 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,
|
||||
}
|
||||
|
|
@ -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))))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue