Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

eframe: capture a screenshot using Frame::request_screenshot #2676

Merged
merged 51 commits into from
Mar 29, 2023
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
c533c46
first stab at frame capture
amfaber Feb 4, 2023
318b1d5
Merge branch 'master' of https://github.com/emilk/egui
amfaber Feb 4, 2023
dd6f898
capture with indirection
amfaber Feb 4, 2023
447b68f
A bit of cleanup
amfaber Feb 4, 2023
1f1e58f
Merge branch 'master' of https://github.com/emilk/egui into emilk-master
amfaber Feb 4, 2023
6b7bf39
Merge branch 'emilk-master'
amfaber Feb 4, 2023
7e2232e
some cleaning
amfaber Feb 4, 2023
fb55318
trying to fix a git mess
amfaber Feb 4, 2023
387d81a
rustfmt
amfaber Feb 4, 2023
78650a2
rebase in a moment anyways
amfaber Feb 4, 2023
2dc00e7
Fix import of cell for single use
amfaber Feb 5, 2023
e1aa669
Add notes in changelog
amfaber Feb 7, 2023
ae5d24e
Merge https://github.com/emilk/egui
amfaber Feb 7, 2023
a3b253b
Merge branch 'emilk:master' into master
amfaber Feb 8, 2023
94f3a84
refactoring of screenshot example WIP
amfaber Feb 8, 2023
9cf3b52
Explicit width and height for region method of colorimage
amfaber Feb 8, 2023
094f284
Some documentation and cargo fmt
amfaber Feb 8, 2023
bf6e33a
Final touches
amfaber Feb 8, 2023
52f886a
Guard new feature behind #[cfg(not(target_arch = "wasm32"))].
amfaber Feb 8, 2023
ff32cd8
A bit more docs cleanup
amfaber Feb 8, 2023
06e6dc0
Merge https://github.com/emilk/egui
amfaber Feb 8, 2023
570e941
cargo.lock troubles
amfaber Feb 8, 2023
204a450
rename pixel_data to screenshot
amfaber Feb 8, 2023
214146e
Update crates/egui-wgpu/src/winit.rs
amfaber Feb 8, 2023
391629a
Remove into_raw unsafe method
amfaber Feb 8, 2023
b333b56
Merge https://github.com/emilk/egui
amfaber Feb 8, 2023
73ea872
Merge branch 'emilk:master' into master
amfaber Feb 8, 2023
886fb7f
cargo fmt and resolve cargo-lock merge
amfaber Feb 28, 2023
3245832
Merge branch 'emilk:master' into master
amfaber Feb 28, 2023
ecd4d96
Update crates/eframe/src/epi.rs
amfaber Mar 1, 2023
554834c
Update crates/eframe/src/native/run.rs
amfaber Mar 1, 2023
632e0e5
Merge branch 'emilk:master' into master
amfaber Mar 1, 2023
3f34373
Updates based on PR feedback
amfaber Mar 1, 2023
64a8452
Merge branch 'master' of https://github.com/amfaber/egui
amfaber Mar 1, 2023
c0b526a
cargo-fmt
amfaber Mar 1, 2023
23016b3
Fix two failing tests
amfaber Mar 1, 2023
5fa9ced
another cargo fmt ._.
amfaber Mar 1, 2023
f239e7a
Delete and regenerate Cargo.lock for cargo deny
amfaber Mar 1, 2023
4a1c38d
egui -> emath in docs
amfaber Mar 1, 2023
c154774
Allow two versions of base64 since plink updated their dependency
amfaber Mar 1, 2023
f437c12
Fixing some cranky warnings
amfaber Mar 1, 2023
00c63d4
Merge pull request #2 from amfaber/WIP
amfaber Mar 1, 2023
78de848
Revert changes to Cargo.lock and deny.toml
amfaber Mar 2, 2023
516f8c6
Labels for capture texture and buffer, wgpu::util::align_to for aligning
amfaber Mar 2, 2023
4d638b1
Git actions cargo fmt is stricter than my cargo fmt
amfaber Mar 2, 2023
c62aa2b
Merge pull request #3 from amfaber/WIP
amfaber Mar 2, 2023
e3d937e
storage for wasm too
emilk Mar 29, 2023
bf08887
Move changes to unreleased
amfaber Mar 29, 2023
08ea093
? operator for getting (render|surface)state
amfaber Mar 29, 2023
10e072c
Merge branch 'emilk:master' into WIP
amfaber Mar 29, 2023
19f7577
Merge pull request #4 from amfaber/WIP
amfaber Mar 29, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ NOTE: [`epaint`](crates/epaint/CHANGELOG.md), [`eframe`](crates/eframe/CHANGELOG
* You can turn off the vertical line left of indented regions with `Visuals::indent_has_left_vline` ([#2636](https://github.com/emilk/egui/pull/2636)).
* Add `Response.highlight` to highlight a widget ([#2632](https://github.com/emilk/egui/pull/2632)).
* Add `Separator::grow` and `Separator::shrink` ([#2665](https://github.com/emilk/egui/pull/2665)).
* Add `Frame::request_screenshot` and `Frame::screenshot` to communicate to the backend that a screenshot of the current frame should be exposed by `Frame` during `App::post_rendering`
* Add `read_screan_rgba` to the egui-wgpu `Painter`, to allow for capturing the current frame when using wgpu. Used in conjuction with `Frame::request_screenshot`
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these should go into the eframe and egui-wgpu changelogs, and include al ink to this PR


### Changed 🔧
* Improved plot grid appearance ([#2412](https://github.com/emilk/egui/pull/2412)).
Expand Down
120 changes: 31 additions & 89 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 65 additions & 0 deletions crates/eframe/src/epi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,9 @@ pub struct Frame {
/// Can be used to manage GPU resources for custom rendering with WGPU using [`egui::PaintCallback`]s.
#[cfg(feature = "wgpu")]
pub(crate) wgpu_render_state: Option<egui_wgpu::RenderState>,

#[cfg(not(target_arch = "wasm32"))]
pub(crate) screenshot: std::cell::Cell<Option<egui::ColorImage>>,
Wumpf marked this conversation as resolved.
Show resolved Hide resolved
}

impl Frame {
Expand All @@ -668,10 +671,69 @@ impl Frame {
}

/// A place where you can store custom data in a way that persists when you restart the app.
#[cfg(not(target_arch = "wasm32"))]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have storage on web (local storage)

emilk marked this conversation as resolved.
Show resolved Hide resolved
pub fn storage(&self) -> Option<&dyn Storage> {
self.storage.as_deref()
}

/// Request the current frame's pixel data. Needs to be retrieved by calling [`Frame::screenshot`]
/// during [`App::post_rendering`].
#[cfg(not(target_arch = "wasm32"))]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the limitation for implementing this on web?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly my inexperience with wasm and any kind of web-dev. I took a stab at trying to port the screenshot example to wasm with the eframe-template though, and it seems like App::post_rendering is never called on the web?
From the backend perspectiv, 3 things are required;

  1. A flag needs to be set during App::update.
  2. The flag needs to be read during the backend's paint call, such that the data can be pulled from the GPU
  3. The data needs to be delivered back to the app during App::post_rendering

I chose to use the Frame struct for delivering the flag and image data between the app and the backend, which was easy on native since everything could be handled inside the WinitApp::paint function. I didn't look too closely at the architecture of the webpainter yet, but as App::post_rendering is never called, there would be no way to deliver the image data back to the app.

If this is solved, I believe it would be possible to more or less copy-paste the actual screen capturing logic for wgpu and glow.

pub fn request_screenshot(&mut self) {
self.output.screenshot_requested = true;
}

/// Cancel a request made with [`Frame::request_screenshot`].
#[cfg(not(target_arch = "wasm32"))]
pub fn cancel_screenshot_request(&mut self) {
self.output.screenshot_requested = false;
}

/// During [`App::post_rendering`], use this to retrieve the pixel data that was requested during
/// [`App::update`] via [`Frame::request_screenshot`].
///
/// Returns None if:
/// * Called in [`App::update`]
/// * [`Frame::request_screenshot`] wasn't called on this frame during [`App::update`]
/// * The rendering backend doesn't support this feature (yet). Currently implemented for wgpu and glow, but not with wasm as target.
/// * Retrieving the data was unsuccessful in some way.
///
/// See also [`egui::ColorImage::region`]
///
/// ## Example generating a capture of everything within a square of 100 pixels located at the top left of the app and saving it with the [`image`](crates.io/crates/image) crate:
/// ```
/// impl eframe::App for MyApp{
/// fn update(ctx: &egui::Context, frame: &mut eframe::Frame){
/// ... // In real code the app would render something here
/// frame.request_screenshot()
/// ... // Things that are added to the frame after the call to
/// // request_screenshot() will still be included.
/// }
///
/// fn post_rendering(&mut self, _window_size: [u32; 2], frame: &eframe::Frame){
/// if let Some(screenshot) = frame.screenshot(){
/// let pixels_per_point = frame.info().native_pixels_per_point;
/// let region = egui::Rect::from_two_pos(
/// egui::Pos2::ZERO,
/// egui::Pos2{ x: 100., y: 100. },
/// );
/// let top_left_corner = screenshot.region(&region, pixels_per_point);
/// image::save_buffer(
/// "top_left.png",
/// top_left_corner.as_raw(),
/// top_left_corner.width() as u32,
/// top_left_corner.height() as u32,
/// image::ColorType::Rgba8,
/// ).unwrap();
/// }
/// }
/// }
amfaber marked this conversation as resolved.
Show resolved Hide resolved
/// ```
#[cfg(not(target_arch = "wasm32"))]
pub fn screenshot(&self) -> Option<egui::ColorImage> {
self.screenshot.take()
}

/// A place where you can store custom data in a way that persists when you restart the app.
pub fn storage_mut(&mut self) -> Option<&mut (dyn Storage + 'static)> {
self.storage.as_deref_mut()
Expand Down Expand Up @@ -1037,5 +1099,8 @@ pub(crate) mod backend {
/// Set to some bool to maximize or unmaximize window.
#[cfg(not(target_arch = "wasm32"))]
pub maximized: Option<bool>,

#[cfg(not(target_arch = "wasm32"))]
pub screenshot_requested: bool,
}
}
Loading