pix_engine/
draw.rs

1//! Drawing methods.
2//!
3//! Provides a [Draw] trait as well standard draw methods.
4//!
5//! Provided [`PixState`] methods:
6//!
7//! - [`PixState::clear`]: Clear the render target to the current background [Color].
8//! - [`PixState::save_canvas`]: Save the current render target out to a [png] file.
9//!
10//! # Example
11//!
12//! ```
13//! # use pix_engine::prelude::*;
14//! # struct App;
15//! # impl PixEngine for App {
16//! fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
17//!     s.background(Color::ALICE_BLUE);
18//!     s.clear();
19//!     let rect = rect![0, 0, 100, 100];
20//!     s.fill(Color::RED);
21//!     s.stroke(Color::BLACK);
22//!     s.rect(rect)?;
23//!     Ok(())
24//! }
25//! # }
26//! ```
27
28use anyhow::Context;
29
30use crate::{prelude::*, renderer::Rendering};
31use log::info;
32use std::{fs::File, io::BufWriter, path::Path};
33
34/// Trait for objects that can be drawn to the screen.
35pub trait Draw {
36    /// Draw object to the current [`PixState`] canvas.
37    ///
38    /// # Errors
39    ///
40    /// If the renderer fails to draw to the current render target, then an error is returned.
41    ///
42    /// # Example
43    ///
44    /// ```
45    /// # use pix_engine::prelude::*;
46    /// # struct App;
47    /// # impl PixEngine for App {
48    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
49    ///     let rect = rect![0, 0, 100, 100];
50    ///     // The following two lines are equivalent.
51    ///     s.rect(rect)?;
52    ///     rect.draw(s)?;
53    ///     Ok(())
54    /// }
55    /// # }
56    /// ```
57    fn draw(&self, s: &mut PixState) -> PixResult<()>;
58}
59
60impl PixState {
61    /// Clears the render target to the current background [Color] set by [`PixState::background`].
62    ///
63    /// # Errors
64    ///
65    /// If the current render target is closed or dropped, then an error is returned.
66    ///
67    /// # Example
68    ///
69    /// ```
70    /// # use pix_engine::prelude::*;
71    /// # struct App;
72    /// # impl PixEngine for App {
73    /// fn on_update(&mut self, s: &mut PixState) -> PixResult<()> {
74    ///     s.background(Color::CADET_BLUE);
75    ///     s.clear();
76    ///     Ok(())
77    /// }
78    /// # }
79    /// ```
80    #[inline]
81    pub fn clear(&mut self) -> PixResult<()> {
82        self.renderer.set_draw_color(self.settings.background)?;
83        self.renderer.clear()
84    }
85
86    /// Save a portion `src` of the currently rendered target to a [png] file. Passing `None` for
87    /// `src` saves the entire target.
88    ///
89    /// # Errors
90    ///
91    /// Returns an error for any of the following:
92    ///     - The current render target is closed or dropped.
93    ///     - The renderer fails to read pixels from the current window target.
94    ///     - An [`io::Error`] occurs attempting to create the [png] file.
95    ///     - A [`png::EncodingError`] occurs attempting to write image bytes.
96    ///
97    /// [`io::Error`]: std::io::Error
98    ///
99    /// # Example
100    ///
101    /// ```
102    /// # use pix_engine::prelude::*;
103    /// # struct App;
104    /// # impl PixEngine for App {
105    /// # fn on_update(&mut self, s: &mut PixState) -> PixResult<()> { Ok(()) }
106    /// fn on_key_pressed(&mut self, s: &mut PixState, event: KeyEvent) -> PixResult<bool> {
107    ///     if let Key::S = event.key {
108    ///         s.save_canvas(None, "test_image.png")?;
109    ///     }
110    ///     Ok(false)
111    /// }
112    /// # }
113    /// ```
114    pub fn save_canvas<P, R>(&mut self, src: R, path: P) -> PixResult<()>
115    where
116        P: AsRef<Path>,
117        R: Into<Option<Rect<i32>>>,
118    {
119        info!("Saving canvas to {}", path.as_ref().display());
120        if let Some(src) = src.into() {
121            // Copy current texture target to a texture
122            let bytes = self.renderer.to_bytes()?;
123            let render_texture = self.create_texture(self.width()?, self.height()?, None)?;
124            self.update_texture(render_texture, None, bytes, self.width()? as usize * 4)?;
125            // Render the `src` rect from texture onto another texture, and save it
126            let src_texture = self.create_texture(src.width() as u32, src.height() as u32, None)?;
127            self.set_texture_target(src_texture)?;
128            self.texture(render_texture, src, None)?;
129            self.save_canvas(None, path)?;
130            self.clear_texture_target();
131            self.delete_texture(render_texture)?;
132            self.delete_texture(src_texture)?;
133            Ok(())
134        } else {
135            let path = path.as_ref();
136            let png_file = BufWriter::new(File::create(path)?);
137            let mut png = png::Encoder::new(png_file, self.width()?, self.height()?);
138            png.set_color(PixelFormat::Rgba.into());
139            png.set_depth(png::BitDepth::Eight);
140            let mut writer = png
141                .write_header()
142                .with_context(|| format!("failed to write png header: {path:?}"))?;
143
144            writer
145                .write_image_data(&self.renderer.to_bytes()?)
146                .with_context(|| format!("failed to write png data: {path:?}"))
147        }
148    }
149}