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}