Skip to content

Commit

Permalink
Merge pull request #29 from ian-h-chamberlain/example/graphics-bitmap
Browse files Browse the repository at this point in the history
  • Loading branch information
Meziu authored Jan 30, 2022
2 parents e98b0a9 + 9aac807 commit 8ba1bb2
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 16 deletions.
Binary file added ctru-rs/examples/assets/ferris.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ctru-rs/examples/assets/ferris.rgb
Binary file not shown.
59 changes: 59 additions & 0 deletions ctru-rs/examples/graphics-bitmap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use ctru::console::Console;
use ctru::gfx::Screen as _;
use ctru::services::hid::KeyPad;
use ctru::services::{Apt, Hid};
use ctru::Gfx;

/// Ferris image taken from <https://rustacean.net> and scaled down to 320x240px.
/// To regenerate the data, you will need to install `imagemagick` and run this
/// command from the `examples` directory:
///
/// ```sh
/// magick assets/ferris.png -channel-fx "red<=>blue" -rotate 90 assets/ferris.rgb
/// ```
///
/// This creates an image appropriate for the default frame buffer format of
/// [`Bgr8`](ctru::services::gspgpu::FramebufferFormat::Bgr8)
/// and rotates the image 90° to account for the portrait mode screen.
static IMAGE: &[u8] = include_bytes!("assets/ferris.rgb");

fn main() {
ctru::init();
let gfx = Gfx::default();
let hid = Hid::init().expect("Couldn't obtain HID controller");
let apt = Apt::init().expect("Couldn't obtain APT controller");
let _console = Console::init(gfx.top_screen.borrow_mut());

println!("\x1b[21;16HPress Start to exit.");

let mut bottom_screen = gfx.bottom_screen.borrow_mut();

// We don't need double buffering in this example.
// In this way we can draw our image only once on screen.
bottom_screen.set_double_buffering(false);

// We assume the image is the correct size already, so we drop width + height.
let frame_buffer = bottom_screen.get_raw_framebuffer();

// Copy the image into the frame buffer
unsafe {
frame_buffer.ptr.copy_from(IMAGE.as_ptr(), IMAGE.len());
}

// Main loop
while apt.main_loop() {
//Scan all the inputs. This should be done once for each frame
hid.scan_input();

if hid.keys_down().contains(KeyPad::KEY_START) {
break;
}

// Flush and swap framebuffers
gfx.flush_buffers();
gfx.swap_buffers();

//Wait for VBlank
gfx.wait_for_vblank();
}
}
67 changes: 51 additions & 16 deletions ctru-rs/src/gfx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use std::cell::RefCell;
use std::default::Default;
use std::marker::PhantomData;
use std::ops::Drop;

use crate::services::gspgpu::{self, FramebufferFormat};
Expand All @@ -28,29 +29,29 @@ pub trait Screen {
fn set_framebuffer_format(&mut self, fmt: FramebufferFormat) {
unsafe { ctru_sys::gfxSetScreenFormat(self.as_raw(), fmt.into()) }
}

/// Returns a tuple containing a pointer to the specifified framebuffer (as determined by the
/// calling screen and `Side`), the width of the framebuffer in pixels, and the height of
/// the framebuffer in pixels
///
/// Note that the pointer returned by this function can change after each call to this function
/// if double buffering is enabled
fn get_raw_framebuffer(&self, side: Side) -> (*mut u8, u16, u16) {
let mut width: u16 = 0;
let mut height: u16 = 0;
unsafe {
let buf: *mut u8 =
ctru_sys::gfxGetFramebuffer(self.as_raw(), side.into(), &mut width, &mut height);
(buf, width, height)
}
}
}

#[non_exhaustive]
pub struct TopScreen;

#[non_exhaustive]
pub struct BottomScreen;

/// Representation of a framebuffer for one [`Side`] of the top screen, or the
/// entire bottom screen. The inner pointer is only valid for one frame if double
/// buffering is enabled. Data written to `ptr` will be rendered to the screen.
#[derive(Debug)]
pub struct RawFrameBuffer<'screen> {
/// Pointer to graphics data to be rendered.
pub ptr: *mut u8,
/// The width of the framebuffer in pixels.
pub width: u16,
/// The height of the framebuffer in pixels.
pub height: u16,
/// Keep a mutable reference to the Screen for which this framebuffer is tied.
screen: PhantomData<&'screen mut dyn Screen>,
}

#[derive(Copy, Clone, Debug)]
/// Side of top screen framebuffer
///
Expand Down Expand Up @@ -138,6 +139,40 @@ impl TopScreen {
pub fn get_wide_mode(&self) -> bool {
unsafe { ctru_sys::gfxIsWide() }
}

/// Returns a [`RawFrameBuffer`] for the given [`Side`] of the top screen.
///
/// Note that the pointer of the framebuffer returned by this function can
/// change after each call to this function if double buffering is enabled.
pub fn get_raw_framebuffer(&mut self, side: Side) -> RawFrameBuffer {
RawFrameBuffer::for_screen_side(self, side)
}
}

impl BottomScreen {
/// Returns a [`RawFrameBuffer`] for the bottom screen.
///
/// Note that the pointer of the framebuffer returned by this function can
/// change after each call to this function if double buffering is enabled.
pub fn get_raw_framebuffer(&mut self) -> RawFrameBuffer {
RawFrameBuffer::for_screen_side(self, Side::Left)
}
}

impl<'screen> RawFrameBuffer<'screen> {
fn for_screen_side(screen: &'screen mut dyn Screen, side: Side) -> Self {
let mut width = 0;
let mut height = 0;
let ptr = unsafe {
ctru_sys::gfxGetFramebuffer(screen.as_raw(), side.into(), &mut width, &mut height)
};
Self {
ptr,
width,
height,
screen: PhantomData,
}
}
}

impl Screen for TopScreen {
Expand Down

0 comments on commit 8ba1bb2

Please sign in to comment.