Browse Source

Merge pull request #29 from ian-h-chamberlain/example/graphics-bitmap

pull/37/head
Meziu 3 years ago committed by GitHub
parent
commit
8ba1bb28fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. BIN
      ctru-rs/examples/assets/ferris.png
  2. BIN
      ctru-rs/examples/assets/ferris.rgb
  3. 59
      ctru-rs/examples/graphics-bitmap.rs
  4. 67
      ctru-rs/src/gfx.rs

BIN
ctru-rs/examples/assets/ferris.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
ctru-rs/examples/assets/ferris.rgb

Binary file not shown.

59
ctru-rs/examples/graphics-bitmap.rs

@ -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
ctru-rs/src/gfx.rs

@ -2,6 +2,7 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::default::Default; use std::default::Default;
use std::marker::PhantomData;
use std::ops::Drop; use std::ops::Drop;
use crate::services::gspgpu::{self, FramebufferFormat}; use crate::services::gspgpu::{self, FramebufferFormat};
@ -28,29 +29,29 @@ pub trait Screen {
fn set_framebuffer_format(&mut self, fmt: FramebufferFormat) { fn set_framebuffer_format(&mut self, fmt: FramebufferFormat) {
unsafe { ctru_sys::gfxSetScreenFormat(self.as_raw(), fmt.into()) } 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] #[non_exhaustive]
pub struct TopScreen; pub struct TopScreen;
#[non_exhaustive] #[non_exhaustive]
pub struct BottomScreen; 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)] #[derive(Copy, Clone, Debug)]
/// Side of top screen framebuffer /// Side of top screen framebuffer
/// ///
@ -138,6 +139,40 @@ impl TopScreen {
pub fn get_wide_mode(&self) -> bool { pub fn get_wide_mode(&self) -> bool {
unsafe { ctru_sys::gfxIsWide() } 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 { impl Screen for TopScreen {

Loading…
Cancel
Save