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