Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/color rpi #117

Merged
merged 2 commits into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ cargo build --release --features [optional_features]
On by default
* `sdl-resample` - Use the audio resampler from sdl2 library and a manual one I wrote
* `push-audio` - Use a push methododlogy instead of pull for the delivery of the sound samples to sdl2
* `static-scale` - Will use a fixed scale values for the renderer instead of addapting to the screen size
* `u16pixel` - pixels are represented by 16 bits and not 32 bits - neccessary for interfacing the ili9341 spi lcd
* `apu` - Turn on the apu (On by default)
* `rpi` - Input is from the RPI GPIO pins and output is to an ili9341 spi lcd connected to the RPI GPIO pins, activates the `u16pixel` feature.
Expand Down
1 change: 0 additions & 1 deletion gb/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ sdl = ["sdl2"]
sdl-resample = ["apu"]
push-audio = ["apu"]
static-sdl = ["sdl", "sdl2/bundled", "sdl2/static-link"]
static-scale = ["sdl"]
u16pixel = ["lib_gb/u16pixel"]
apu = ["lib_gb/apu", "sdl", "wav"]
rpi = ["rppal", "u16pixel", "image_inter", "nix/signal"]
Expand Down
2 changes: 1 addition & 1 deletion gb/src/joypad_menu/joypad_gfx_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl<'a, GFX: GfxDevice, T, S:AsRef<str>> MenuRenderer<T, S> for GfxDeviceMenuRe
}

impl<'a, GFX:GfxDevice> GfxDeviceMenuRenderer<'a, GFX>{
fn render_string<S:AsRef<str>>(prompt: S, frame_buffer: &mut [u32; 23040], frame_buffer_height_index: usize, color:Color, bg:Color) {
fn render_string<S:AsRef<str>>(prompt: S, frame_buffer: &mut [Pixel; SCREEN_HEIGHT * SCREEN_WIDTH], frame_buffer_height_index: usize, color:Color, bg:Color) {
let mut width_index = 0;
for char in prompt.as_ref().as_bytes(){
let glyph = FONT_LUT[(char - FONT_ASCII_START_INDEX) as usize];
Expand Down
1 change: 0 additions & 1 deletion gb/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ mod audio{
#[cfg(feature = "sdl")]
mod sdl{
pub mod utils;
#[cfg(not(feature = "u16pixel"))]
pub mod sdl_gfx_device;
#[cfg(feature = "sdl-resample")]
pub mod sdl_audio_resampler;
Expand Down
4 changes: 2 additions & 2 deletions gb/src/rpi_gpio/ili9341_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ impl<SC:SpiController> Ili9341Contoller<SC>{
spi.write(Ili9341Commands::VcomControl2, &[0x86]);

// Configuring the screen
spi.write(Ili9341Commands::MemoryAccessControl, &[0x20]); // This command tlit the screen 90 degree
spi.write(Ili9341Commands::MemoryAccessControl, &[0x28]); // This command tlit the screen 90 degree and set pixel BGR order
spi.write(Ili9341Commands::PixelFormatSet, &[0x55]); // set pixel format to 16 bit per pixel;
spi.write(Ili9341Commands::FrameRateControl, &[0x0, 0x10 /*According to the docs this is 119 hrz, setting this option in order to avoid screen tearing on rpi zero2 */]);
spi.write(Ili9341Commands::DisplayFunctionControl, &[0x8, 0x82, 0x27]);
Expand Down Expand Up @@ -142,7 +142,7 @@ impl<SC:SpiController> Ili9341Contoller<SC>{

pub fn write_frame_buffer(&mut self, buffer:&[u16;SCREEN_HEIGHT*SCREEN_WIDTH]){
let mut scaled_buffer: [u8;TARGET_SCREEN_HEIGHT * TARGET_SCREEN_WIDTH * 2] = [0;TARGET_SCREEN_HEIGHT * TARGET_SCREEN_WIDTH * 2];
unsafe{image_inter::scale_biliniear_c::<SCREEN_WIDTH, SCREEN_HEIGHT, TARGET_SCREEN_WIDTH, TARGET_SCREEN_HEIGHT>(buffer.as_ptr(), scaled_buffer.as_mut_ptr())};
unsafe{image_inter::scale_nearest::<SCREEN_WIDTH, SCREEN_HEIGHT, TARGET_SCREEN_WIDTH, TARGET_SCREEN_HEIGHT>(buffer.as_ptr(), scaled_buffer.as_mut_ptr())};

let end_x_index = TARGET_SCREEN_WIDTH + FRAME_BUFFER_X_OFFSET - 1;
self.spi.write(Ili9341Commands::ColumnAddressSet, &[
Expand Down
72 changes: 16 additions & 56 deletions gb/src/sdl/sdl_gfx_device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,68 +9,49 @@ pub struct SdlGfxDevice{
texture: *mut SDL_Texture,
discard:u8,
turbo_mul:u8,
#[cfg(feature = "static-scale")]
screen_scale:usize,
}

const SDL_PIXEL_FORMAT:u32 = if cfg!(feature = "u16pixel"){SDL_PixelFormatEnum::SDL_PIXELFORMAT_RGB565 as u32}else{SDL_PixelFormatEnum::SDL_PIXELFORMAT_RGB888 as u32};

impl SdlGfxDevice{
pub fn new(window_name:&str, screen_scale: usize, turbo_mul:u8, disable_vsync:bool, full_screen:bool)->Self{
#[cfg(feature = "u16pixel")]
std::compile_error("Sdl gfx device must have Pixel type = u32");

let cs_wnd_name = CString::new(window_name).unwrap();

let (_window, renderer, texture): (*mut SDL_Window, *mut SDL_Renderer, *mut SDL_Texture) = unsafe{
if SDL_Init(SDL_INIT_VIDEO) != 0{
std::panic!("Init error: {}", get_sdl_error_message());
}

let window_flags = if full_screen{
#[cfg(feature = "static-scale")]
log::warn!("Please notice that this binary have been compiled with the static-scale feature and you are running with the full screen option.\nThe rendering window might be in wrong scale.");

let window_flags = if full_screen{
// Hide cursor
SDL_ShowCursor(0);
SDL_WindowFlags::SDL_WINDOW_FULLSCREEN_DESKTOP as u32
}
else{
0
SDL_WindowFlags::SDL_WINDOW_RESIZABLE as u32
};

let wind:*mut SDL_Window = SDL_CreateWindow(
cs_wnd_name.as_ptr(),
SDL_WINDOWPOS_UNDEFINED_MASK as i32, SDL_WINDOWPOS_UNDEFINED_MASK as i32,
SCREEN_WIDTH as i32 * screen_scale as i32, SCREEN_HEIGHT as i32 * screen_scale as i32,
window_flags);
SCREEN_WIDTH as i32 * screen_scale as i32, SCREEN_HEIGHT as i32 * screen_scale as i32, window_flags);

let mut render_flags = SDL_RendererFlags::SDL_RENDERER_ACCELERATED as u32;
if !disable_vsync{
render_flags |= SDL_RendererFlags::SDL_RENDERER_PRESENTVSYNC as u32;
}

let rend: *mut SDL_Renderer = SDL_CreateRenderer(wind, -1, render_flags);

let texture_width:i32;
let texture_height:i32;

cfg_if::cfg_if!{
if #[cfg(feature = "static-scale")]{
texture_height = SCREEN_HEIGHT as i32 * screen_scale as i32;
texture_width = SCREEN_WIDTH as i32 * screen_scale as i32;
}
else{
if SDL_RenderSetLogicalSize(rend, (SCREEN_WIDTH as u32) as i32, (SCREEN_HEIGHT as u32) as i32) != 0{
std::panic!("Error while setting logical rendering\nError:{}", get_sdl_error_message());
}
texture_height = SCREEN_HEIGHT as i32;
texture_width = SCREEN_WIDTH as i32;
}
if SDL_RenderSetLogicalSize(rend, (SCREEN_WIDTH as u32) as i32, (SCREEN_HEIGHT as u32) as i32) != 0{
std::panic!("Error while setting logical rendering\nError:{}", get_sdl_error_message());
}

let tex: *mut SDL_Texture = SDL_CreateTexture(rend,
SDL_PixelFormatEnum::SDL_PIXELFORMAT_RGB888 as u32, SDL_TextureAccess::SDL_TEXTUREACCESS_STREAMING as i32,
texture_width, texture_height);

let tex: *mut SDL_Texture = SDL_CreateTexture(rend, SDL_PIXEL_FORMAT,
SDL_TextureAccess::SDL_TEXTUREACCESS_STREAMING as i32, SCREEN_WIDTH as i32, SCREEN_HEIGHT as i32);

SDL_SetWindowMinimumSize(wind, SCREEN_WIDTH as i32, SCREEN_HEIGHT as i32);

(wind, rend, tex)
};

Expand All @@ -79,28 +60,9 @@ impl SdlGfxDevice{
renderer,
texture,
discard:0,
turbo_mul,
#[cfg(feature = "static-scale")]
screen_scale
turbo_mul
}
}

#[cfg(feature = "static-scale")]
fn extend_vec(vec:&[u32], scale:usize, w:usize, h:usize)->Vec<u32>{
let mut new_vec = vec![0;vec.len()*scale*scale];
for y in 0..h{
let sy = y*scale;
for x in 0..w{
let sx = x*scale;
for i in 0..scale{
for j in 0..scale{
new_vec[(sy+i)*(w*scale)+sx+j] = vec[y*w+x];
}
}
}
}
return new_vec;
}
}

impl GfxDevice for SdlGfxDevice{
Expand All @@ -110,17 +72,15 @@ impl GfxDevice for SdlGfxDevice{
return;
}

#[cfg(feature = "static-scale")]
let buffer = Self::extend_vec(buffer, self.screen_scale, SCREEN_WIDTH, SCREEN_HEIGHT);

unsafe{
let mut pixels: *mut c_void = std::ptr::null_mut();
let mut length: std::os::raw::c_int = 0;
SDL_LockTexture(self.texture, std::ptr::null(), &mut pixels, &mut length);
std::ptr::copy_nonoverlapping(buffer.as_ptr(),pixels as *mut u32, buffer.len());
std::ptr::copy_nonoverlapping(buffer.as_ptr(),pixels as *mut Pixel, buffer.len());
SDL_UnlockTexture(self.texture);

//There is no need to call SDL_RenderClear since im replacing the whole buffer
// Clear renderer cause the window can be resized
SDL_RenderClear(self.renderer);
SDL_RenderCopy(self.renderer, self.texture, std::ptr::null(), std::ptr::null());
SDL_RenderPresent(self.renderer);
}
Expand Down
2 changes: 1 addition & 1 deletion image_inter/benches/inter_bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub fn neighbor_rust_inter(c: &mut Criterion){
let input_buffer = [0_u16; 160*144];
let mut output_buffer = [0_u8; 240*266*2];
c.bench_function("bench rust neighbor", |b|b.iter(||{
unsafe{scale_nearest::<160, 144, 266, 240>(input_buffer.as_ptr(), output_buffer.as_mut_ptr(), 5.0/3.0)};
unsafe{scale_nearest::<160, 144, 266, 240>(input_buffer.as_ptr(), output_buffer.as_mut_ptr())};
}));
}

Expand Down
10 changes: 6 additions & 4 deletions image_inter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,14 @@ pub unsafe fn scale_bilinear<const INPUT_WIDTH:usize,const INPUT_HEIGHT:usize, c
}
}

// implemented based on this article - https://kwojcicki.github.io/blog/NEAREST-NEIGHBOUR
pub unsafe fn scale_nearest<const INPUT_WIDTH:usize,const INPUT_HEIGHT:usize, const OUTPUT_WIDTH:usize, const OUTPUT_HEIGHT:usize>(input_buffer: *const u16, output_buffer: *mut u8, scale:f32){
// implemented based on this article - https://towardsdatascience.com/image-processing-image-scaling-algorithms-ae29aaa6b36c
pub unsafe fn scale_nearest<const INPUT_WIDTH:usize,const INPUT_HEIGHT:usize, const OUTPUT_WIDTH:usize, const OUTPUT_HEIGHT:usize>(input_buffer: *const u16, output_buffer: *mut u8){
let scale_x = OUTPUT_WIDTH as f32 / INPUT_WIDTH as f32;
let scale_y = OUTPUT_HEIGHT as f32 / INPUT_HEIGHT as f32;
for y in 0..OUTPUT_HEIGHT{
for x in 0..OUTPUT_WIDTH{
let proj_x = ((1.0 / scale) * x as f32) as usize;
let proj_y = ((1.0 / scale) * y as f32) as usize;
let proj_x = (x as f32 / scale_x).round() as usize;
let proj_y = (y as f32 / scale_y).round() as usize;
let pixel = *input_buffer.add((proj_y * INPUT_WIDTH) + proj_x);
let output_index = (y * OUTPUT_WIDTH) + x;
*output_buffer.add(output_index * 2) = (pixel >> 8) as u8;
Expand Down