diff --git a/README.md b/README.md index 1094a4f3..0bb6c261 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/gb/Cargo.toml b/gb/Cargo.toml index bb578edb..0133c5b8 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -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"] diff --git a/gb/src/joypad_menu/joypad_gfx_menu.rs b/gb/src/joypad_menu/joypad_gfx_menu.rs index 4611674b..5022b2d1 100644 --- a/gb/src/joypad_menu/joypad_gfx_menu.rs +++ b/gb/src/joypad_menu/joypad_gfx_menu.rs @@ -43,7 +43,7 @@ impl<'a, GFX: GfxDevice, T, S:AsRef> MenuRenderer for GfxDeviceMenuRe } impl<'a, GFX:GfxDevice> GfxDeviceMenuRenderer<'a, GFX>{ - fn render_string>(prompt: S, frame_buffer: &mut [u32; 23040], frame_buffer_height_index: usize, color:Color, bg:Color) { + fn render_string>(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]; diff --git a/gb/src/main.rs b/gb/src/main.rs index 2b0d6083..20bc140a 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -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; diff --git a/gb/src/rpi_gpio/ili9341_controller.rs b/gb/src/rpi_gpio/ili9341_controller.rs index 9bcbc7ff..a2245793 100644 --- a/gb/src/rpi_gpio/ili9341_controller.rs +++ b/gb/src/rpi_gpio/ili9341_controller.rs @@ -100,7 +100,7 @@ impl Ili9341Contoller{ 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]); @@ -142,7 +142,7 @@ impl Ili9341Contoller{ 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::(buffer.as_ptr(), scaled_buffer.as_mut_ptr())}; + unsafe{image_inter::scale_nearest::(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, &[ diff --git a/gb/src/sdl/sdl_gfx_device.rs b/gb/src/sdl/sdl_gfx_device.rs index 5e3f1bf3..61cd731d 100644 --- a/gb/src/sdl/sdl_gfx_device.rs +++ b/gb/src/sdl/sdl_gfx_device.rs @@ -9,15 +9,12 @@ 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{ @@ -25,23 +22,19 @@ impl SdlGfxDevice{ 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{ @@ -49,28 +42,16 @@ impl SdlGfxDevice{ } 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) }; @@ -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{ - 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{ @@ -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); } diff --git a/image_inter/benches/inter_bench.rs b/image_inter/benches/inter_bench.rs index 857ebe16..5a822e65 100644 --- a/image_inter/benches/inter_bench.rs +++ b/image_inter/benches/inter_bench.rs @@ -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())}; })); } diff --git a/image_inter/src/lib.rs b/image_inter/src/lib.rs index 2eb177b5..c8c7498b 100644 --- a/image_inter/src/lib.rs +++ b/image_inter/src/lib.rs @@ -57,12 +57,14 @@ pub unsafe fn scale_bilinear(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(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;